yeet another post
This commit is contained in:
parent
7e0b3a05e1
commit
26d2e57e42
1 changed files with 220 additions and 0 deletions
220
content/posts/pulling-apart-dell-uefi.md
Normal file
220
content/posts/pulling-apart-dell-uefi.md
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
+++
|
||||||
|
date = "2023-10-19"
|
||||||
|
draft = false
|
||||||
|
path = "/blog/pulling-apart-dell-uefi"
|
||||||
|
tags = ["uefi"]
|
||||||
|
title = "Pulling apart Dell UEFI images and messing with ACPI"
|
||||||
|
+++
|
||||||
|
|
||||||
|
This is a quick post to write down the method that worked for me to acquire and
|
||||||
|
pull apart a Dell UEFI image to get the executables out, and how to poke at
|
||||||
|
ACPI.
|
||||||
|
|
||||||
|
The specific issue I was curious about and trying to find more information
|
||||||
|
about is the [`rtsx` card reader NVMe regression][rtsx-explanation] on the Dell
|
||||||
|
XPS 15 9560, which is the laptop I use.
|
||||||
|
|
||||||
|
[rtsx-explanation]: https://lore.kernel.org/regressions/c7bdd821686e496eb31e4298050dfb72@realtek.com/
|
||||||
|
|
||||||
|
The regression was caused by the card reader driver no longer setting a
|
||||||
|
register that forces the PCIe `CLKREQ#` pin to be always pulled low, which was,
|
||||||
|
in effect, making the kernel no longer ask the chipset for PCIe clock all the
|
||||||
|
time.
|
||||||
|
|
||||||
|
However, this is even more confusing, because this is essentially a
|
||||||
|
force-disabling bit for attempting to do PCIe ASPM, which was never enabled in
|
||||||
|
the first place on the machine!
|
||||||
|
|
||||||
|
For *some reason*, the *card reader* ceasing to ask the chipset for clock all
|
||||||
|
the time (which, given that ASPM is purportedly disabled in the ACPI tables,
|
||||||
|
should be a no-op!) caused the *NVMe SSD*, which has nothing to do with the
|
||||||
|
card reader, to lose its PCIe link after the card reader driver loads. Further,
|
||||||
|
this happened to every kind of SSD, so it must be some kind of platform bug.
|
||||||
|
|
||||||
|
If you have any ideas of why this is happening, send me an email at <bug at jade
|
||||||
|
dot fyi>, I am very curious.
|
||||||
|
|
||||||
|
# Extracting the firwmare
|
||||||
|
|
||||||
|
I acquired a copy of the firmware [from fwupd here][fwupd]. This is a
|
||||||
|
Microsoft cabinet file. I then used [BIOSUtilities] and p7zip to extract it:
|
||||||
|
|
||||||
|
[fwupd]: https://fwupd.org/lvfs/devices/com.dell.uefi34578c72.firmware
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir bios
|
||||||
|
mv ../4d77eabfe13e3d153dfe9f19b570de40cc90260ef7229b2ca070e06b5c840040-Dell_XPS_15_9560_Precision_5520_System_BIOS_Ver.1.24.0.cab bios
|
||||||
|
(cd bios; 7z x *.cab)
|
||||||
|
python Dell_PFS_Extract.py -i bios -o bios_ex
|
||||||
|
```
|
||||||
|
|
||||||
|
This then produces some files:
|
||||||
|
|
||||||
|
```
|
||||||
|
BIOSUtilities » ls bios_ex/firmware.bin_extracted/Firmware/1\ firmware\ --\ *
|
||||||
|
'bios_ex/firmware.bin_extracted/Firmware/1 firmware -- 1 System BIOS with BIOS Guard v1.24.0.bin'
|
||||||
|
'bios_ex/firmware.bin_extracted/Firmware/1 firmware -- 2 Embedded Controller v1.0.29.bin'
|
||||||
|
'bios_ex/firmware.bin_extracted/Firmware/1 firmware -- 3 Intel Management Engine (VPro) Update v11.8.86.3877.bin'
|
||||||
|
'bios_ex/firmware.bin_extracted/Firmware/1 firmware -- 4 Main System TI Port Controller 0 v7.0.0.31.bin'
|
||||||
|
'bios_ex/firmware.bin_extracted/Firmware/1 firmware -- 5 System Board Map v1.0.1.bin'
|
||||||
|
'bios_ex/firmware.bin_extracted/Firmware/1 firmware -- 6 PCR0 XML v0.0.0.1.xml'
|
||||||
|
'bios_ex/firmware.bin_extracted/Firmware/1 firmware -- 7 Model Information v1.0.0.0.txt'
|
||||||
|
```
|
||||||
|
|
||||||
|
[BIOSUtilities]: https://github.com/platomav/BIOSUtilities
|
||||||
|
|
||||||
|
From here, you can use UEFITool on these files:
|
||||||
|
|
||||||
|
```
|
||||||
|
UEFITool firmware.bin_extracted/Firmware/1\ firmware\ --\ 1\ System\ BIOS\ with\ BIOS\ Guard\ v1.24.0.bin
|
||||||
|
```
|
||||||
|
|
||||||
|
From here you can go hunting through the data in there or try `UEFIExtract`.
|
||||||
|
|
||||||
|
You can find a PE32 image called Setup somewhere in the UEFI image, and extract
|
||||||
|
it. With such an image, you can use [`ifrextractor`][ifrextractor] to pull out
|
||||||
|
the BIOS options. Acquire a copy like so:
|
||||||
|
|
||||||
|
[ifrextractor]: https://github.com/LongSoft/IFRExtractor-RS
|
||||||
|
|
||||||
|
`ifrextractor.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ rustPlatform, fetchFromGitHub }:
|
||||||
|
rustPlatform.buildRustPackage {
|
||||||
|
pname = "ifrextractor";
|
||||||
|
version = "0.0.1";
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "longsoft";
|
||||||
|
repo = "ifrextractor-rs";
|
||||||
|
rev = "f40b9be0da561ede62f3988072100550a73d5386";
|
||||||
|
sha256 = "sha256-No0H91iMcOQlE0Hcc+w02w5CP3M+Ixct+92+XsreIik=";
|
||||||
|
};
|
||||||
|
cargoSha256 = "sha256-smVHEBhjUcy4ApyJzhV31sMTB87Cdq59fxkSJsS1/cw=";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
nix-build -E 'with import <nixpkgs> {}; callPackage ./ifrextractor.nix'
|
||||||
|
```
|
||||||
|
|
||||||
|
Then extract the setup stuff with the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
ifrextractor firmware.bin_extracted/Section_PE32_image_Setup_body.efi
|
||||||
|
```
|
||||||
|
|
||||||
|
Then read the extremely large file named like:
|
||||||
|
`Section_PE32_image_Setup_body.efi.0.0.en-US.ifr.txt`.
|
||||||
|
|
||||||
|
If you *did* want to tamper with hidden settings, you could potentially have
|
||||||
|
such bad ideas with similar methods to the following (please take a backup of
|
||||||
|
the NVRAM first to not brick your computer!):
|
||||||
|
|
||||||
|
* <https://ristovski.github.io/posts/inside-insydeh2o/#peeking-inside-the-bios-image>
|
||||||
|
* <https://hansdegoede.livejournal.com/25413.html>
|
||||||
|
|
||||||
|
If you want to stick the thing in Ghidra, there does [exist an extension,
|
||||||
|
efiSeek][efiSeek], but I couldn't really tell if it was providing any useful
|
||||||
|
analysis results.
|
||||||
|
|
||||||
|
[efiSeek]: https://github.com/DSecurity/efiSeek
|
||||||
|
|
||||||
|
# ACPI
|
||||||
|
|
||||||
|
In investigating the power management issue I was curious about, I wanted to
|
||||||
|
understand what was in the ACPI tables and what was going on. These tables are
|
||||||
|
probably somewhere in the UEFI image as well, but I am not sure where.
|
||||||
|
|
||||||
|
You can dump the ACPI tables from a running system using `acpidump` from `acpica-tools`:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir acpi && cd acpi
|
||||||
|
sudo acpidump -b
|
||||||
|
iasl -d *.dat
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to get values of ACPI variables, you can use the [ACPI
|
||||||
|
debugger][acpidbg] from the Linux kernel distribution:
|
||||||
|
|
||||||
|
[acpidbg]: https://docs.kernel.org/firmware-guide/acpi/aml-debugger.html
|
||||||
|
|
||||||
|
First, enable the right Kconfig options:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{ lib, config, ... }: {
|
||||||
|
boot.kernelPackages = pkgs.linuxPackages.extend (self: super: {
|
||||||
|
kernel = super.kernel.override (old: {
|
||||||
|
kernelPatches = old.kernelPatches ++ [
|
||||||
|
{
|
||||||
|
name = "acpi_nonsense";
|
||||||
|
patch = null;
|
||||||
|
extraStructuredConfig = with lib.kernel; {
|
||||||
|
ACPI_DEBUGGER = yes;
|
||||||
|
ACPI_DEBUGGER_USER = module;
|
||||||
|
DEVMEM = yes;
|
||||||
|
STRICT_DEVMEM = no;
|
||||||
|
IO_STRICT_DEVMEM = option no;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd some-linux-sources
|
||||||
|
make acpi
|
||||||
|
sudo ./power/acpi/acpidbg
|
||||||
|
```
|
||||||
|
|
||||||
|
Inside the debugger you can print out things:
|
||||||
|
|
||||||
|
```
|
||||||
|
- Evaluate UCSI
|
||||||
|
Evaluating \UCSI
|
||||||
|
Evaluation of \UCSI returned object 00000000a57a6a95, external buffer length 18
|
||||||
|
[Integer] = 0000000000000000
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use `Dump` to get information about a variable, such as its
|
||||||
|
offset, if you need to peek/poke the memory.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
If you want to tamper with the ACPI tables for investigating a potential fix,
|
||||||
|
there are a couple of ways to do it.
|
||||||
|
|
||||||
|
At runtime, you can [load an overlay][overlay]:
|
||||||
|
|
||||||
|
```
|
||||||
|
modprobe acpi_configfs
|
||||||
|
cd /sys/kernel/config/acpi/table/
|
||||||
|
mkdir my-ssdt ; cat ~youruser/somewhere/my-ssdt.aml > my-ssdt/aml
|
||||||
|
|
||||||
|
# unload with:
|
||||||
|
rmdir my-ssdt
|
||||||
|
```
|
||||||
|
|
||||||
|
[overlay]: https://www.kernel.org/doc/html/latest/admin-guide/acpi/ssdt-overlays.html
|
||||||
|
|
||||||
|
It's also possible to replace the ACPI tables [with the initrd][initrd-acpi]:
|
||||||
|
Create a cpio archive with `kernel/firmware/acpi/*.dat`, then prepend it to the
|
||||||
|
actual initrd.
|
||||||
|
|
||||||
|
[initrd-acpi]: https://docs.kernel.org/admin-guide/acpi/initrd_table_override.html
|
||||||
|
|
||||||
|
# Conclusion
|
||||||
|
|
||||||
|
This whole affair did not achieve much, but it was very interesting, and I have
|
||||||
|
more of an idea how firmware works on x86.
|
||||||
|
|
||||||
|
For further information:
|
||||||
|
- [ACPI Spec][https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf]
|
||||||
|
|
||||||
|
There may also exist schematics of the machine online, but I cannot confirm
|
||||||
|
this.
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue