6 watts. That is all the firmware gives to the Celeron N3450 in my Jumper EZbook. As soon as it works a bit, the CPU drops to 1.5 GHz and stays there.
Two register writes later, it runs at 10 W, and OpenArena goes from 69 to 111 fps. Finding those two registers took a lot longer than writing them.

Results
6 W stock → 10 W unlocked:
- OpenArena: 69 → 111 fps (+61 %)
- glmark2: 205 → 310 (+51 %)
- 7-zip: 4858 → 6383 MIPS (+31 %)
- CPU + GPU together: 134 → 257 fps (+91 %)
Max temperature at 10 W: 84 °C, for a 105 °C limit. Heat was never the problem, it is just a setting.


The fix
Two register writes at boot:
MMIO MCHBAR(0xFED10000)+0x70A8 := 0x00008f0000dd8a00 # PL1 10 W / PL2 15 W
MSR 0x610 := 0x80008f0000dd8a00 # same, + bit 63 = LOCK
The MSR part needs nothing special, msr-tools is enough:
sudo modprobe msr
sudo wrmsr -a 0x610 0x80008f0000dd8a00
The MMIO part is a 64-bit write at 0xFED170A8 through /dev/mem (I do it with a small Python script that uses mmap).
Both are lost at reboot, so a small systemd unit writes them again at every boot. These addresses are for the N3450 only: a wrong /dev/mem address freezes the machine.
How I found it
1. The documented MSR first, MSR_PKG_POWER_LIMIT (0x610). The factory value:
0x610 = 0x00008f0000dd8600
PL1 (sustained) = 0x600 * 1/256 W = 6.00 W
PL2 (burst) = 0xF00 * 1/256 W = 15.00 W
lock bit 63 = 0 (not locked, so I can write it)
I wrote PL1 = 10 W (0x...8a00). It reads back fine, powercap says 10 W. The CPU ignores it: 5.97 W, 1.5 GHz, and the power limit flag on in THERM_STATUS (0x19C). 2. Intel Core CPUs have a copy of the limit in MMIO, at MCHBAR + 0x59A0 (MCHBAR is at 0xFED10000 here, from PCI 0:0.0 offset 0x48). Here it reads 0x0000000000000000. « No copy on this chip », I said, very sure of myself. Wrong. 3. The config-TDP MSRs (0x648 to 0x64B) all give a #GP fault: not on this chip. And the BIOS setup has a « TDP Limit » setting (NVAR offset 0xEC, « TDP Limit in Watts »). I set it to 10 in both setup stores. Still 5.97 W: it is hidden behind a « Turbo-XE supported » flag that is 0 on this model, so the firmware never uses it. 4. The AI agent I was working with wrote it down: « 6 W is locked, no lever left ». It gave up, very professionally. I was quite sure we could do it, and asked it to look again. Real MMIO is never all zeros, so I scanned the whole 32 KB MCHBAR window (764 non-zero values) for the PL2 value 0x8F00:
+0x70A8 = 0x00008F0000DD8600 PL1=6.00W PL2=15.00W <-- here
On Apollo Lake the register is at 0x70A8, not 0x59A0. I wrote 10 W there: 10.12 W, 2.09 GHz. At last.

5. Then the GPU wakes up, and the power goes back to 6 W. And stays there. Retour à la case départ. One trace per second, CPU load only:
t GPU MHz CPU MHz package W
1-9 100 2089 ~10.0 GPU asleep: 10 W, full turbo
10 300 1392 6.65 the GPU wakes up for one second
16-30 ~100 ~1450 ~6.0 GPU asleep again, but still 6 W
Any window refresh or cursor blink wakes the GPU. So in real life (a browser open), always 6 W.

The answer was in a coreboot commit message for Apollo Lake: « This RAPL MMIO register is a physically separate instance from RAPL MSR register. The Punit algorithm constrains performance to whichever power limit is lower between both registers. »
So the real limit is min(MMIO, MSR). I checked the MSR during a GPU load: back to 0x...8600, 6 W. The firmware puts the MSR back to 6 W each time the GPU wakes up. So I write the MSR too, with its lock bit. Now it stays at 10 W, with or without the GPU.
Why not 15 W?
I tested it. Above 10 W the CPU is already at its max clock (2.09 GHz all cores), glmark2 gains only 7 % at 12 W, the game is already at its max, and mixed loads get worse: the GPU takes all the power and the CPU encode is slower than at 10 W. And 87 °C. Hotter and slower.

All the details, including the dead ends: github.com/fabien0008/jumper-ezbook-power-unlock
How AI was used
The register hunting was done with an AI coding agent (Claude Code) on the EZbook itself: it read and wrote the MSRs and MMIO, scanned MCHBAR, read the coreboot source, and ran the benchmarks. It also gave up twice: after the 0x59A0 zeros, and after the GPU latch. Both times I did not believe it, and both times there was a way. My job: insist, and check that the numbers made sense.
Note: I don’t have much time for the blog these days, so this post was written with the help of an AI, from my own notes, then read and corrected by me. Better to share the content this way than not at all.