Dix Watts

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.

The Jumper EZbook, a thin silver 13 inch netbook, seen from the front and from the side
The Jumper EZbook. (Product photos.)

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.

OpenArena running on the EZbook, watching a bot match: 'You fragged Ayumi, tied for 1st place'
OpenArena at 111 fps instead of 69.
glmark2's refract scene, a glass rabbit, rendered by the Intel HD Graphics 500
glmark2: 205 points at 6 W, 310 at 10 W.

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.

Chart: before and after the MMIO write, package power 5.97 to 10.12 W, all-core clock 1.5 to 2.09 GHz, temperature 62 to 76 °C
Before and after the MMIO write, CPU only.

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.

Chart: package power over time under CPU load, a flat 10 W until the GPU briefly wakes, then a drop to 6 W that never comes back
The GPU wakes up for a second, and the 10 W are gone.

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.

Chart: performance versus power limit from 6 to 15 W; most curves stay flat after 10 to 12 W and the mixed encode drops at 15 W
Performance versus power limit. 10 W is the best compromise.

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.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *