Bug #623
closedOn T480/S, missing P-state data from ACPI tables prevents Xen from performing frequency scaling
0%
Description
Hello
Currently, P-state data is missing from ACPI tables on T480/S. On my T480S running libreboot, this was confirmed by using the iasl command to decompile the tables at /sys/firmware/acpi/tables/; grepping for _PSS, _PCT and _PPC didn't return any results. Another user has confirmed that this is also the case for the similar T480 device, with latest release build of libreboot (26.01 RC1).
Meanwhile, in the vendor T480S BIOS, this data is present (contributed by @henk on #coreboot IRC channel):
# grep -r -e _PCT -e _PPC -e _PSS *.dsl
DSDT.dsl: If (CondRefOf (\_PR.PR00._PPC))
DSDT.dsl: If ((CondRefOf (\_PR.PR00._PSS) && CondRefOf (\_PR.PR00._PPC)))
SSDT1.dsl: External (_PR_.PR00._PSS, MethodObj) // 0 Arguments
SSDT1.dsl: Name (_PPC, Zero) // _PPC: Performance Present Capabilities
SSDT1.dsl: If (CondRefOf (\_PR.PR00._PSS))
SSDT1.dsl: Method (_PSS, 0, NotSerialized) // _PSS: Performance Supported States
SSDT1.dsl: If (CondRefOf (\_PR.PR00._PSS))
SSDT1.dsl: Return (\_PR.PR00._PSS ())
SSDT1.dsl: If (CondRefOf (\_PR.PR00._PSS))
SSDT8.dsl: If (CondRefOf (\_PR.PR00._PSS))
The Linux kernel doesn't rely on this data to perform frequency scaling, because native support is provided by the intel_pstate driver. However, the Xen kernel lacks this native support. When the data is missing, as in my case, the Xen-based QubesOS runs underclocked and is only barely usable.
HH Updated by herme herme 9 months ago
- Description updated (diff)
IB Updated by Ivi Ballou 9 months ago · Edited
herme herme wrote:
Hello
Currently, P-state data is missing from ACPI tables on T480/S. On my T480S running libreboot, this was confirmed by using the iasl command to decompile the tables at
/sys/firmware/acpi/tables/; grepping for_PSS,_PCTand_PPCdidn't return any results. Another user has confirmed that this is also the case for the similar T480 device, with latest release build of libreboot (26.01 RC1).Meanwhile, in the vendor T480S BIOS, this data is present (contributed by @henk on #coreboot IRC channel):
# grep -r -e _PCT -e _PPC -e _PSS *.dsl DSDT.dsl: If (CondRefOf (\_PR.PR00._PPC)) DSDT.dsl: If ((CondRefOf (\_PR.PR00._PSS) && CondRefOf (\_PR.PR00._PPC))) SSDT1.dsl: External (_PR_.PR00._PSS, MethodObj) // 0 Arguments SSDT1.dsl: Name (_PPC, Zero) // _PPC: Performance Present Capabilities SSDT1.dsl: If (CondRefOf (\_PR.PR00._PSS)) SSDT1.dsl: Method (_PSS, 0, NotSerialized) // _PSS: Performance Supported States SSDT1.dsl: If (CondRefOf (\_PR.PR00._PSS)) SSDT1.dsl: Return (\_PR.PR00._PSS ()) SSDT1.dsl: If (CondRefOf (\_PR.PR00._PSS)) SSDT8.dsl: If (CondRefOf (\_PR.PR00._PSS))The Linux kernel doesn't rely on this data to perform frequency scaling, because native support is provided by the intel_pstate driver. However, the Xen kernel lacks this native support. When the data is missing, as in my case, the Xen-based QubesOS runs underclocked and is only barely usable.
Hi!
Could you include the entire .dsl files? I might be able to add the missing entries, but I'd need to know what the vendor BIOS does. Thanks :)
PR Updated by Patrick Rudolph 9 months ago
Set register "eist_enable" = "true" in devicetree.cb?
HH Updated by herme herme 8 months ago · Edited
Hi Ivi, many thanks for taking an interest in this issue! I was able to obtain the following:
- A dump using the method described in the first post, for a T480S motherboard; Bios Version: N22ET62W (1.39), CPU: i5-8350U. Courtesy of cepawiel at the Libreboot issue tracker. The files are here: https://codeberg.org/attachments/f669fca0-90d2-48c9-9602-c9aa20102440
- One dump for T480 (Model: 20L5000UUS) and two dumps for T480S (Models: 20L7S00600 and 20L8S7PP06). These dumps are part of the repo for the https://linux-hardware.org/ project, which provides their own tool for dumping the ACPI tables. The actual data is uploaded by anonymous volunteers.
There is some variation across T480/S models (e.g several different CPU models and dozens of device models). I didn't know if this makes a difference, which is why I tried to include more than one source (but my guess is if the motherboard is the same, then it shouldn't matter). Please let me know in case you think additional dumps might be useful and I'll try to find more volunteers.
PR Updated by Patrick Rudolph 8 months ago
Should be fixed by https://review.coreboot.org/c/coreboot/+/91173
FS Updated by felix sanchez 18 days ago
Machine: ThinkPad T480 (i7-8650U), currently running Libreboot/coreboot
1d8755f2425 (2026-09-28). Before flashing I took a full 16 MiB dump of the
Lenovo factory firmware (BIOS version N24RE03WL), so I could compare the vendor
ACPI tables against what coreboot emits.
== How the vendor tables were extracted (reproducible) ==
The ACPI tables are not stored in clear in the Lenovo image, so plain grep/iasl
on the dump finds nothing:
- UEFIExtract on the 16 MiB dump parses the firmware volumes but yields no
ACPI tables and no ACPI table GUIDs (EB9D2D30-... for DSDT, etc.). - The DXE volume is stored as an LZMA-compressed blob. Decompressing it
(FORMAT_ALONE) yields ~14.9 MiB containing the real tables. - Locating tables by signature + valid header length + valid checksum finds
1 DSDT (140807 bytes, OEM ID "INTEL", table ID "SKL") and 34 SSDTs. - iasl -d on those gives readable ASL.
== What the vendor firmware actually contains ==
The DSDT only declares the processors and defers to an SSDT:
External (_PR_.PR00, DeviceObj)
External (_PR_.PR00.LPSS, PkgObj)
External (_PR_.PR00.TPSS, PkgObj)
If (CondRefOf (\_PR.PR00._PPC))
If ((CondRefOf (\_PR.PR00._PSS) && CondRefOf (\_PR.PR00._PPC)))
SSDT "Cpu0Ist" (OEM ID "PmRef") implements the interface on PR00:
Method (_PPC, 0) { Return (\_PR.CPPC) }
Name (_PCT, Package (0x02)
{
ResourceTemplate () { Register (FFixedHW, 0x00, 0x00, 0x0000000000000000, ,) },
ResourceTemplate () { Register (FFixedHW, 0x00, 0x00, 0x0000000000000000, ,) }
})
Method (_PSS, 0)
{
If ((\_SB.OSCP & 0x0400)) // OS supports CPPC
{
Return (TPSS)
}
Else
{
Return (LPSS)
}
}
SSDT "ApIst" covers the remaining CPUs by delegating to PR00:
Scope (\_PR.PR01)
{
Method (_PPC) { Return (\_PR.PR00._PPC ()) }
Method (_PCT) { Return (\_PR.PR00._PCT ()) }
Method (_PSS) { Return (\_PR.PR00._PSS ()) }
}
... repeated up to PR15.
== The important part ==
Every entry in both packages is the placeholder 0x80000000:
Name (LPSS, Package (0x10) // 16 states
{
Package (0x06) { 0x80000000, 0x80000000, 0x80000000,
0x80000000, 0x80000000, 0x80000000 },
...
})
Name (TPSS, Package (0x28) // 40 states
{
Package (0x06) { 0x80000000, 0x80000000, 0x80000000,
0x80000000, 0x80000000, 0x80000000 },
...
})
Unique values found in LPSS: 0x80000000. Unique values in TPSS: 0x80000000.
No real frequency (800/1900/4200 MHz) appears anywhere in the 34 SSDTs.
Likewise _PCT is a pair of FFixedHW registers with Address 0x0, Bit Width 0x00
and Bit Offset 0x00, i.e. also a placeholder.
This is consistent with the SSDT name ("Cpu0Ist" = CPU0 Initialization State
Table): the vendor firmware fills these tables in at runtime, presumably from
the CPU's own MSRs, and the ROM only carries the template.
== Why this matters for the fix ==
The reference .dsl files requested earlier in this ticket have been provided,
but they cannot be used to simply copy values into coreboot: there are no
values in them to copy. Implementing this in coreboot means generating
_PSS/_PCT/_PPC (and _PSD) at runtime, computing the state list from the CPU
(MSR_PLATFORM_INFO for the max non-turbo ratio, MSR_TURBO_RATIO_LIMIT for the
turbo bins), not backporting a static table.
Current state of coreboot, for reference (checked on 1d8755f2425): searching
src/mainboard/lenovo/sklkbl_thinkpad/ and src/soc/intel/sklake/ for _PCT, _PPC,
_PSS and Processor returns 0 matches. The only related symbol left in
src/arch/x86/acpi/statdef.asl is the unused #define NOTIFY_CPU_PPCCHG. So there
is no existing infrastructure to enable - it would be new code.
== Relation to Xen ==
Worth noting for anyone following this: Qubes' Xen already has HWP support
(QubesOS/qubes-vmm-xen#158, merged Feb 2023, which closed
QubesOS/qubes-issues#4604). But the HWP driver still needs the ACPI P-state
data to initialise its policy, so with no _PSS/_PPC/_PCT present, cpufreq=xen:hwp
has nothing to work with and the CPU stays pinned. Hence the firmware side is
the blocker, and it is the one that has not moved.
HH Updated by herme herme 17 days ago · Edited
Unfortunately my device died and I can't run tests on it anymore. In the meantime, a QubesOS user reported that the PR by Patrick is working: https://forum.qubes-os.org/t/cpufreq-drivers-not-working-on-t480s/37965/25
PR Updated by Patrick Rudolph 16 days ago
- Status changed from New to Resolved