Problem
On the Lenovo LOQ Essential 15IRX11 (83SC) with RTX 5050 (GB207M, 10DE:2D98), Dynamic Boost never activates. The GPU stays at the VBIOS default 35W instead of boosting to 65W (50W TGP + 15W PPAB).
The root cause is that the stock BIOS NPCF SSDT returns zeroed power budget fields in all NVPCF DSM subfunctions. The driver constructs the PMO successfully (func 0x00 bit 0 IS set), but all downstream budget data is zeroed — so Dynamic Boost offsets are computed as 0W and the GPU never leaves VBIOS defaults.
This was confirmed on Windows 11 with stock BIOS as well — same 45W flatline. The bug is in the ACPI tables, not the driver.
System Specifications
| Component |
Detail |
| Machine |
Lenovo LOQ Essential 15IRX11, type 83SC |
| BIOS |
SECN22WW (Insyde H2O, 2026-05-21) |
| GPU |
NVIDIA GeForce RTX 5050 Laptop GPU, GB207M die |
| GPU PCI ID |
10DE:2D98 (SubSystem 0x8FFE17AA) |
| GPU Architecture |
Blackwell |
| GPU Memory |
8151 MiB GDDR7 |
| VBIOS Version |
98.07.27.00.43 |
| VBIOS Power Limits |
TGP 50W / CTGP 35W / PPAB 15W / Max 65W |
| GPU Max Clocks |
Graphics 3090 MHz / Memory 12001 MHz |
| PCIe |
Gen5 x16 max, Gen1 x8 current |
| CPU |
Intel Core i5-13450HX (Raptor Lake-HX, 10C/16T) |
| CPU power limits |
PL1 configurable 25–60W, PL2 configurable 40–85W (Lenovo Vantage) |
| Kernel |
7.2.3-1-cachyos (x86_64, CONFIG_ACPI_TABLE_UPGRADE=y) |
| NVIDIA driver |
610.57.04 (open, nvidia-open) |
| GSP Firmware |
610.57.04 |
| nvidia-powerd |
v2.0 (build 1) |
| Inforom |
G005.0000.98.01 |
| Bootloader |
Limine (UEFI) |
ACPI Table Inventory
The firmware exposes 38 CB-01 tables via XSDT. Key tables:
| XSDT # |
Type |
Size |
OEM Table ID |
Contents |
| 1 |
SSDT |
86,813B |
CB-01 |
Large peripheral SSDT |
| 2 |
SSDT |
262B |
CB-01 |
Small auxiliary |
| 3 |
SSDT |
5,709B |
CB-01 |
Peripheral config |
| 4 |
SSDT |
7,946B |
CB-01 |
NPCF + GPS + PEGP — the buggy table |
| 12 |
DSDT |
660,250B |
CB-01 |
Main DSDT (has External(NPCF) + Notify(NPCF)) |
| 5–22 |
SSDT |
varies |
CB-01 |
Remaining CB-01 tables |
All 22 CB-01 SSDTs share the identity tuple (SSDT, LENOVO, CB-01) with OEM Revision 0x01. The NPCF-carrying table is SSDT #4 in XSDT order (0-indexed: #3).
NPCF Table Structure (7,946 bytes)
The NPCF SSDT contains:
- PEGP OperationRegions — VBOR, NOPR (0x7387C018, 0x2027 bytes), HGOP
- PEG1/PEGP scope — RPCX config space, PEGP methods including GPS
- Scope(_SB) Device(NPCF) — NVDA0820, _DSM with UUID 36b49710-2483-11e7-9598-0800200c9a66
- PEGP _DOD/MXMX/MXDS/MXM — display output methods
The GPS method (lines 406–536) is inside PEGP scope. The NPCF device (lines 844–1186) is at \_SB scope.
Root Cause Analysis
NPCF _DSM Structure
The NPCF device (\_SB.NPCF) implements \_DSM(UUID=36b49710-2483-11e7-9598-0800200c9a66, rev=0x200) with these subfunctions:
| Subfunc |
Name |
Purpose |
| 0x00 |
GET_SUPPORTED |
Returns bitmask of supported subfunctions |
| 0x01 |
GET_STATIC_CONFIG_TABLES |
Static configuration (checksum-validated) |
| 0x02 |
GET_DYNAMIC_PARAMS |
Dynamic power budget (TGPA/TPPA/MAGA offsets) |
| 0x08 |
GET_DC_SYSTEM_POWER_LIMITS |
DC battery power limits (JPAC) |
What the Driver Does With Each Subfunction
func 0x00 (client_resource.c:2942-3002):
- Returns
Buffer(4) — a bitmask of supported subfunctions
- The driver tests only bit 0 (
FLD_TEST_DRF(..._FUNC_GET_SUPPORTED_IS_SUPPORTED, _NO, ...))
- If bit 0 = 1 → PMO constructs, driver proceeds to func 0x02
- The stock BIOS returns
Buffer(4){0xBF,0x06,0x00,0x00} — bit 0 IS set
- So the driver enters the dynamic boost path, but all data is zeroed
func 0x02 (client_resource.c:3100-3173):
- Returns a 49-byte buffer with this memory layout:
Offset Size Field Description
------ ---- ------------ -----------
0x00 1 headerSize Must be 0x05
0x01 1 commonSize Must be 0x10
0x02 1 entrySize Must be 0x1C
0x03 1 version Must match driver's requested version
0x04 1 entryCount Number of controller entries (1 on LOQ)
--- common section (0x10 bytes) ---
0x05 2 TGPA CTGP AC offset in 125mW units
Driver: pParams->ctgpOffsetmW = 125 * TGPA
0x07 2 TGPD CTGP DC offset
--- entry section (0x1C bytes per entry) ---
0x19 2 TPPA Target PP AC offset
0x1B 2 TPPD Target PP DC offset
0x1D 2 MAGA Max AC GPU offset
0x21 2 MIGA Max idle GPU offset
0x25 2 DROP Delta response offset
0x29 4 LTBC Long-term budget cap
0x2D 4 STBC Short-term budget cap
The driver validates at client_resource.c:3121-3135:
if ((headerOut.headerSize != 0x05) ||
(headerOut.commonSize != 0x10) ||
(headerOut.entrySize != 0x1C))
{
status = NV_ERR_INVALID_DATA;
}
Stock BIOS returns all-zeroed fields — TGPA=0x0000, TPPA=0x0000, MAGA=0x0000. The driver computes:
ctgpOffsetmW = 125 * 0 = 0W — no CTGP boost
targetTppOffsetmW = 125 * 0 = 0W — no PP offset
maxOutputOffsetmW = 125 * 0 = 0W — no max output boost
Result: Dynamic Boost is "supported" (bit 0 set) but all offsets are 0W. The GPU stays at VBIOS default 35W.
func 0x01 — 14-byte static config with two's complement checksum:
- Sum all bytes except the last
- Negate:
(~sum) + 1 (one-byte two's complement)
- Must equal the last byte
- Stock BIOS: checksum 0xAB, data correct
func 0x08 — 22-byte DC system power limits (only used on battery, not relevant to AC dynamic boost)
GPS Pipeline Issues (Secondary)
The GPS (GPU Power Steering) method in the same SSDT has three additional problems:
-
GPSS guard blocks GPS calls: PEGP's _DSM checks If ((GPSS != Zero)) before dispatching to GPS(). GPSS is a field in an OperationRegion (NOPR at 0x7387C018) that may not be mapped when _INI runs. The nvidia driver probes GPS at boot before NPCF's _INI sets GPSS=1.
-
Missing GETPPL (func 0x24): The GPS switch statement has no Case(0x24), so it falls through to Default → Return(0x80000002). The driver's platform_request_handler_ctrl.c:629-641 expects a 12-byte buffer {Version=0x00010000, PL1, PL2}. Without it, NV_ERR_INVALID_DATA fires at line 2174.
-
GPSP buffer too small: The SSDT allocates Buffer(0x28) (40 bytes), but the driver's PFM_REQ_HNDLR_PSHAREDATA struct is 44 bytes (0x2C). The ppmd field at offset 0x28 is outside the buffer. The PPMD sensor is never populated, causing NV_ERR_INVALID_DATA at line 2117.
Evidence
Stock BIOS func 0x02 return (49 bytes):
TGPA (offset 0x05): 0x0000 = 0W ← should be 65W (0x0208)
TGPD (offset 0x07): 0x0000 = 0W
TPPA (offset 0x19): 0x0000 = 0W ← should be 135W (0x0438)
MAGA (offset 0x1D): 0x0000 = 0W ← should be 120W (0x03C0)
func 0x00 return: Buffer(4){0xBF,0x06,0x00,0x00} — bit 0 IS set, so PMO constructs. But all downstream func 0x02/0x01 fields are zeroed.
nvidia-smi after boot (stock):
Current Power Limit: 35.00W (VBIOS default — never boosts)
Default Power Limit: 35.00W
Max Power Limit: 65.00W (hardware maximum, never reached)
Requested Power Limit: N/A (nvidia-powerd never requests boost)
nvidia-powerd: Runs, DBus connected, zero activity logs. Falls back to "SBIOS support not found for NVPCF GET_SUPPORTED" → default TPP.
dmesg:
NVRM: GPU0 PlatformRequestHandler failed to get target temp from SBIOS @ platform_request_handler_ctrl.c:2174
NVRM: GPU0 PlatformRequestHandler failed to get platform power mode from SBIOS @ platform_request_handler_ctrl.c:2117
GPS func 0x24 (GETPPL): Returns 0x80000002 (not implemented — Case(0x24) missing from switch)
GPS func 0x2A (PSHAREPARAMS): Returns 40-byte buffer but RETN=0x00000000 — no sensors declared, PPMD never populated
Fix
ACPI SSDT override via CONFIG_ACPI_TABLE_UPGRADE initrd mechanism. Two changes:
1. NPCF Device Embedded in DSDT
Embed Device(NPCF) at Scope(\_SB) in the DSDT. The nvidia-acpi driver hardcodes pathname "\\_SB.NPCF._DSM" (nv-acpi.c:785-793) — NPCF must be at \_SB scope.
Complete NPCF device with corrected budgets:
Scope (\_SB)
{
Device (NPCF)
{
Name (_HID, "NVDA0820")
Name (_UID, "NPCF")
Name (_STA, 0x0F)
Method (_INI, 0, NotSerialized)
{
If (CondRefOf (\_SB.PC00.PEG1.PEGP.GPSS))
^^PC00.PEG1.PEGP.GPSS = One
If (CondRefOf (\_SB.PC00.PEG1.PEGP.NPCS))
^^PC00.PEG1.PEGP.NPCS = One
}
Method (_DSM, 4, Serialized)
{
If ((Arg0 == ToUUID ("36b49710-2483-11e7-9598-0800200c9a66")))
{
Return (NPCF (Arg0, Arg1, Arg2, Arg3))
}
Return (Buffer (One) { 0x00 })
}
Method (NPCF, 4, NotSerialized)
{
Switch (ToInteger (Arg2))
{
Case (Zero)
{
Return (Buffer (4)
{
0x07, 0x01, 0x00, 0x00 // bit0=PMO, bit8=DC support
})
}
Case (One)
{
// 14-byte static config — checksum 0xAB
Return (Buffer (0x0E)
{
0x20, 0x03, 0x01, 0x00, 0x25, 0x04, 0x05,
0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0xAB
})
}
Case (0x02)
{
// 49-byte dynamic params
Name (PBD2, Buffer (0x31)
{
0x25, 0x05, 0x10, 0x1C, 0x01 // header: ver=0x25, hdr=5, com=16, ent=28, count=1
})
CreateWordField (PBD2, 0x05, TGPA) // CTGP AC offset
CreateWordField (PBD2, 0x07, TGPD) // CTGP DC offset
CreateWordField (PBD2, 0x19, TPPA) // Target PP AC
CreateWordField (PBD2, 0x1B, TPPD) // Target PP DC
CreateWordField (PBD2, 0x1D, MAGA) // Max AC GPU
CreateWordField (PBD2, 0x21, MIGA) // Max Idle GPU
TGPA = 0x0208 // 65W (520 * 125mW)
TPPA = 0x0438 // 135W (1080 * 125mW)
MAGA = 0x03C0 // 120W (960 * 125mW)
Return (PBD2)
}
Case (0x08)
{
// 22-byte DC system power limits
Return (Buffer (0x16)
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00
})
}
}
Return (Buffer (One) { 0x00 })
}
}
}
Budget values explained:
TGPA = 0x0208 → 520 × 125mW = 65,000mW = 65W (TGP + CTGP + PPAB)
TPPA = 0x0438 → 1080 × 125mW = 135,000mW = 135W (total platform power budget)
MAGA = 0x03C0 → 960 × 125mW = 120,000mW = 120W (max AC GPU allocation)
The driver extracts these at client_resource.c:3160-3173:
pParams->ctgpOffsetmW = (NvS32)125 * (NvS16)DRF_VAL(..._CMD0_CTGP_AC_OFFSET, commonOut.param0);
pParams->targetTppOffsetmW = (NvS32)125 * (NvS16)DRF_VAL(..._CMD0_SIGNED0, entriesOut.param1);
pParams->maxOutputOffsetmW = (NvS32)125 * (NvS16)DRF_VAL(..._CMD0_SIGNED0, entriesOut.param2);
2. GPS Pipeline Fix (SSDT#4)
The GPS method in the NPCF SSDT needs three patches:
a) Remove GPSS guard from _DSM dispatcher (line 232):
// BEFORE:
If ((\_SB.PC00.PEG1.PEGP.GPSS != Zero))
{ Return (\_SB.PC00.PEG1.PEGP.GPS (Arg0, Arg1, Arg2, Arg3)) }
// AFTER: (guard removed)
Return (\_SB.PC00.PEG1.PEGP.GPS (Arg0, Arg1, Arg2, Arg3))
b) Add Case(0x24) for GETPPL:
Case (0x24) // GPS_FUNC_GETPPL
{
Return (Buffer (0x0C)
{
0x00, 0x00, 0x01, 0x00, // Version: Major=1 (0x00010000)
0xC8, 0xAF, 0x00, 0x00, // PL1: 45000 mW (45W)
0xE8, 0xFD, 0x00, 0x00 // PL2: 65000 mW (65W)
})
}
The driver validates at platform_request_handler_ctrl.c:629-641:
if (FLD_TEST_DRF(0000_CTRL_PFM_REQ_HNDLR, _PPL_ARG0_VERSION, _MAJOR, _V1,
acpiParamsEx.output[0])) // output[0] bits 31:16 must == 1
c) Enlarge GPSP buffer from 0x28 to 0x2C and set PPMD status bit:
// BEFORE: Buffer(0x28) with 9 DWordFields (missing CTGP, PPMD)
// AFTER: Buffer(0x2C) with 11 DWordFields
Name (GPSP, Buffer (0x2C) {}) // was 0x28 (40 bytes)
CreateDWordField (GPSP, 0x00, RETN)
CreateDWordField (GPSP, 0x04, VRV1)
CreateDWordField (GPSP, 0x08, TGPU)
CreateDWordField (GPSP, 0x0C, PDTS)
CreateDWordField (GPSP, 0x10, SFAN)
CreateDWordField (GPSP, 0x14, SKNT)
CreateDWordField (GPSP, 0x18, CPUE)
CreateDWordField (GPSP, 0x1C, TMP1)
CreateDWordField (GPSP, 0x20, TMP2)
CreateDWordField (GPSP, 0x24, CTGP) // NEW: maps to driver ctgp at offset 0x24
CreateDWordField (GPSP, 0x28, PPMD) // NEW: maps to driver ppmd at offset 0x28
// In Case(Zero) — SUPPORTED_FIELDS query:
RETN = 0x00010100 // bit 8 = TGPU, bit 16 = PPMD
// was 0x00000000 — no sensors declared
The driver reads PPMD at platform_request_handler_ctrl.c:1208-1219:
if (0 != DRF_VAL(..._PSHARE_PARAMS_STATUS, _PPMD, pPShareParams->status))
{
_pfmreqhndlrUpdatePpmdLimit(pPlatformRequestHandler, pGpu, NV_TRUE);
}
3. OEM Revision Bump (All 22 CB-01 SSDTs)
Every CB-01 SSDT must have OEM Revision ≥ 0x02. The kernel's acpi_table_initrd_override() silently skips if firmware_rev >= initrd_rev. Factory SSDTs have rev 0x01. Hex-patch byte offset 24-27 from 01 00 00 00 to 02 00 00 00 and recalculate the ACPI checksum.
After Fix
Current Power Limit: 65.00W (Dynamic Boost active)
Synthetic load: 65W sustained (cuda-matmul 30s)
Genshin Impact avg: 53.1W (with CPU -75mV undervolt on shared heatsink)
AE_ALREADY_EXISTS: 0 (was 242)
Touchpad: working (preserved)
GPS GETPPL: {V1, PL1=45W, PL2=65W}
GPS PSHAREPARAMS: 44-byte buffer with PPMD=7 sensor
Key Driver Paths
nv-acpi.c:785-793 — hardcodes pathname "\\_SB.NPCF._DSM" for all NVPCF DSM calls
nv-acpi.c:361-365 — namespace walk discovers NPCF, overwrites globals unconditionally (last-wins)
client_resource.c:2942-3002 — cliresCtrlCmdSystemNVPCFGetPowerModeInfo_IMPL dispatches via osCallACPI_DSM
client_resource.c:2992-2994 — GET_SUPPORTED checks only bit 0 of supportedFuncs
client_resource.c:3100-3173 — func 0x02 validates header (0x05/0x10/0x1C), extracts TGPA/TPPA/MAGA
client_resource.c:2898-2940 — func 0x01 _validateConfigStaticTable_2x two's complement checksum
dynamic-power.c:1202-1223 — os_get_dynamic_boost_support probes via func 0x00
platform_request_handler_ctrl.c:601-643 — GETPPL (func 0x24) validates Major version = 1
platform_request_handler_ctrl.c:1060-1088 — pfmreqhndlrCallACPI_EX copies input→output, calls osCallACPI_DSM
platform_request_handler_ctrl.c:2174 — TGPU sensor update fails when PSHAREPARAMS incomplete
platform_request_handler_ctrl.c:2117 — PPMD sensor update fails when ppmd field missing
os.c:2934-3048 — osCallACPI_DSM maps function IDs to GUIDs and revisions
Question for Maintainers
-
Driver-side fallback: When func 0x02 returns all-zero budget fields, the driver silently falls back to VBIOS defaults with no warning. A pr_warn when ctgpOffsetmW == 0 && maxOutputOffsetmW == 0 (after func 0x00 reports support) would help users diagnose this class of firmware bug. Is this intentional?
-
Should the driver validate func 0x02 budget values? Currently client_resource.c validates the header structure (sizes, version) but not whether the extracted TGPA/TPPA/MAGA are non-zero when bit 0 of func 0x00 indicates support. Adding a check like if (pParams->ctgpOffsetmW == 0 && pParams->maxOutputOffsetmW == 0) { pr_warn(NV, "NPCF: zeroed power budget detected"); } would surface Lenovo's firmware bug at boot.
-
PPMD sensor initialization: The PlatformRequestHandler NV_ERR_INVALID_DATA errors at lines 2174/2117 fire when PSHAREPARAMS doesn't populate the PPMD field. Is this expected for laptops without platform power mode support, or should the driver handle the missing PPMD gracefully?
Full patched SSDT source, scripts, and power measurement data available at loq-npcf.
Problem
On the Lenovo LOQ Essential 15IRX11 (83SC) with RTX 5050 (GB207M, 10DE:2D98), Dynamic Boost never activates. The GPU stays at the VBIOS default 35W instead of boosting to 65W (50W TGP + 15W PPAB).
The root cause is that the stock BIOS NPCF SSDT returns zeroed power budget fields in all NVPCF DSM subfunctions. The driver constructs the PMO successfully (func 0x00 bit 0 IS set), but all downstream budget data is zeroed — so Dynamic Boost offsets are computed as 0W and the GPU never leaves VBIOS defaults.
This was confirmed on Windows 11 with stock BIOS as well — same 45W flatline. The bug is in the ACPI tables, not the driver.
System Specifications
ACPI Table Inventory
The firmware exposes 38 CB-01 tables via XSDT. Key tables:
External(NPCF)+Notify(NPCF))All 22 CB-01 SSDTs share the identity tuple
(SSDT, LENOVO, CB-01)with OEM Revision 0x01. The NPCF-carrying table is SSDT #4 in XSDT order (0-indexed: #3).NPCF Table Structure (7,946 bytes)
The NPCF SSDT contains:
The GPS method (lines 406–536) is inside PEGP scope. The NPCF device (lines 844–1186) is at
\_SBscope.Root Cause Analysis
NPCF _DSM Structure
The NPCF device (
\_SB.NPCF) implements\_DSM(UUID=36b49710-2483-11e7-9598-0800200c9a66, rev=0x200)with these subfunctions:What the Driver Does With Each Subfunction
func 0x00 (
client_resource.c:2942-3002):Buffer(4)— a bitmask of supported subfunctionsFLD_TEST_DRF(..._FUNC_GET_SUPPORTED_IS_SUPPORTED, _NO, ...))Buffer(4){0xBF,0x06,0x00,0x00}— bit 0 IS setfunc 0x02 (
client_resource.c:3100-3173):The driver validates at
client_resource.c:3121-3135:Stock BIOS returns all-zeroed fields — TGPA=0x0000, TPPA=0x0000, MAGA=0x0000. The driver computes:
ctgpOffsetmW = 125 * 0 = 0W— no CTGP boosttargetTppOffsetmW = 125 * 0 = 0W— no PP offsetmaxOutputOffsetmW = 125 * 0 = 0W— no max output boostResult: Dynamic Boost is "supported" (bit 0 set) but all offsets are 0W. The GPU stays at VBIOS default 35W.
func 0x01 — 14-byte static config with two's complement checksum:
(~sum) + 1(one-byte two's complement)func 0x08 — 22-byte DC system power limits (only used on battery, not relevant to AC dynamic boost)
GPS Pipeline Issues (Secondary)
The GPS (GPU Power Steering) method in the same SSDT has three additional problems:
GPSS guard blocks GPS calls: PEGP's
_DSMchecksIf ((GPSS != Zero))before dispatching toGPS(). GPSS is a field in an OperationRegion (NOPR at 0x7387C018) that may not be mapped when_INIruns. The nvidia driver probes GPS at boot before NPCF's_INIsets GPSS=1.Missing GETPPL (func 0x24): The GPS switch statement has no
Case(0x24), so it falls through toDefault → Return(0x80000002). The driver'splatform_request_handler_ctrl.c:629-641expects a 12-byte buffer{Version=0x00010000, PL1, PL2}. Without it,NV_ERR_INVALID_DATAfires at line 2174.GPSP buffer too small: The SSDT allocates
Buffer(0x28)(40 bytes), but the driver'sPFM_REQ_HNDLR_PSHAREDATAstruct is 44 bytes (0x2C). Theppmdfield at offset 0x28 is outside the buffer. The PPMD sensor is never populated, causingNV_ERR_INVALID_DATAat line 2117.Evidence
Stock BIOS func 0x02 return (49 bytes):
func 0x00 return:
Buffer(4){0xBF,0x06,0x00,0x00}— bit 0 IS set, so PMO constructs. But all downstream func 0x02/0x01 fields are zeroed.nvidia-smi after boot (stock):
nvidia-powerd: Runs, DBus connected, zero activity logs. Falls back to "SBIOS support not found for NVPCF GET_SUPPORTED" → default TPP.
dmesg:
GPS func 0x24 (GETPPL): Returns
0x80000002(not implemented — Case(0x24) missing from switch)GPS func 0x2A (PSHAREPARAMS): Returns 40-byte buffer but
RETN=0x00000000— no sensors declared, PPMD never populatedFix
ACPI SSDT override via
CONFIG_ACPI_TABLE_UPGRADEinitrd mechanism. Two changes:1. NPCF Device Embedded in DSDT
Embed
Device(NPCF)atScope(\_SB)in the DSDT. The nvidia-acpi driver hardcodespathname "\\_SB.NPCF._DSM"(nv-acpi.c:785-793) — NPCF must be at\_SBscope.Complete NPCF device with corrected budgets:
Budget values explained:
TGPA = 0x0208→ 520 × 125mW = 65,000mW = 65W (TGP + CTGP + PPAB)TPPA = 0x0438→ 1080 × 125mW = 135,000mW = 135W (total platform power budget)MAGA = 0x03C0→ 960 × 125mW = 120,000mW = 120W (max AC GPU allocation)The driver extracts these at
client_resource.c:3160-3173:2. GPS Pipeline Fix (SSDT#4)
The GPS method in the NPCF SSDT needs three patches:
a) Remove GPSS guard from _DSM dispatcher (line 232):
b) Add Case(0x24) for GETPPL:
The driver validates at
platform_request_handler_ctrl.c:629-641:c) Enlarge GPSP buffer from 0x28 to 0x2C and set PPMD status bit:
The driver reads PPMD at
platform_request_handler_ctrl.c:1208-1219:3. OEM Revision Bump (All 22 CB-01 SSDTs)
Every CB-01 SSDT must have OEM Revision ≥ 0x02. The kernel's
acpi_table_initrd_override()silently skips iffirmware_rev >= initrd_rev. Factory SSDTs have rev 0x01. Hex-patch byte offset 24-27 from01 00 00 00to02 00 00 00and recalculate the ACPI checksum.After Fix
Key Driver Paths
nv-acpi.c:785-793— hardcodespathname "\\_SB.NPCF._DSM"for all NVPCF DSM callsnv-acpi.c:361-365— namespace walk discovers NPCF, overwrites globals unconditionally (last-wins)client_resource.c:2942-3002—cliresCtrlCmdSystemNVPCFGetPowerModeInfo_IMPLdispatches viaosCallACPI_DSMclient_resource.c:2992-2994— GET_SUPPORTED checks only bit 0 ofsupportedFuncsclient_resource.c:3100-3173— func 0x02 validates header (0x05/0x10/0x1C), extracts TGPA/TPPA/MAGAclient_resource.c:2898-2940— func 0x01_validateConfigStaticTable_2xtwo's complement checksumdynamic-power.c:1202-1223—os_get_dynamic_boost_supportprobes via func 0x00platform_request_handler_ctrl.c:601-643— GETPPL (func 0x24) validates Major version = 1platform_request_handler_ctrl.c:1060-1088—pfmreqhndlrCallACPI_EXcopies input→output, callsosCallACPI_DSMplatform_request_handler_ctrl.c:2174— TGPU sensor update fails when PSHAREPARAMS incompleteplatform_request_handler_ctrl.c:2117— PPMD sensor update fails when ppmd field missingos.c:2934-3048—osCallACPI_DSMmaps function IDs to GUIDs and revisionsQuestion for Maintainers
Driver-side fallback: When func 0x02 returns all-zero budget fields, the driver silently falls back to VBIOS defaults with no warning. A
pr_warnwhenctgpOffsetmW == 0 && maxOutputOffsetmW == 0(after func 0x00 reports support) would help users diagnose this class of firmware bug. Is this intentional?Should the driver validate func 0x02 budget values? Currently
client_resource.cvalidates the header structure (sizes, version) but not whether the extracted TGPA/TPPA/MAGA are non-zero when bit 0 of func 0x00 indicates support. Adding a check likeif (pParams->ctgpOffsetmW == 0 && pParams->maxOutputOffsetmW == 0) { pr_warn(NV, "NPCF: zeroed power budget detected"); }would surface Lenovo's firmware bug at boot.PPMD sensor initialization: The
PlatformRequestHandlerNV_ERR_INVALID_DATA errors at lines 2174/2117 fire when PSHAREPARAMS doesn't populate the PPMD field. Is this expected for laptops without platform power mode support, or should the driver handle the missing PPMD gracefully?Full patched SSDT source, scripts, and power measurement data available at loq-npcf.