Hiidb access from userspace not working
Hi all,
I'm working on accessing and modifying BIOS/UEFI settings (such as toggling hardware ports) from Linux userspace on an ARM-based system using the uefisettings tool. The goal is to interact with the HII (Human Interface Infrastructure) database that stores BIOS configuration menus. Current Setup:
I'm using EDK2, and have customized the HiiDatabaseDxe driver to manually create an EFI variable named HiiDb-1b838190-4625-4ead-abc9-cd5e6af18fe0, following the format expected by uefisettings.
This variable contains:
UEFI variable flags for boot/runtime access.
The size of the HII database.
The physical memory address where the HII database is located in /dev/mem.
Example:
$ hexdump -C /sys/firmware/efi/efivars/HiiDb-1b838190-4625-4ead-abc9-cd5e6af18fe0 00000000 06 00 00 00 18 0d 00 00 00 00 c9 ed |............|
I verified that /dev/mem at 0xEDC90000 (as pointed to in the variable) contains valid-looking IFR data and string content.
I also see other EFI variable files under /sys/firmware/efi/efivars/ for specific formsets, holding BIOS option values.
Problems I'm Facing:
uefisettings fails to parse the HII database, and commands like show-ifr, list-questions, list-strings, or even hii extract-db fail with:
Can't parse more package lists
I added debug logging inside uefisettings, and noticed:
The seek operation on /dev/mem works correctly.
But the subsequent read_exact fails to copy data into the expected buffer (it seems like a zero-length or incomplete read).
I'm not familiar with Rust, and I suspect this might be a memory allocation issue—possibly stack vs heap or incorrect buffer sizing.
What Works:
The HII database is clearly published and accessible in memory.
The format of the manually created HiiDb-* variable matches expectations.
I can read specific efivars corresponding to BIOS option values for particular formsets.
What I Need Help With:
Why is uefisettings unable to read the data correctly from /dev/mem despite a valid memory region and variable?
Could this be due to Rust’s read_exact requiring specific buffer alignment or size?
How can I debug or modify this behavior?
Are there any known limitations or required conditions (e.g., memory page alignment, permissions, architecture-specific quirks) for uefisettings ?
Is there a way to feed the extracted HII binary data into uefisettings directly, bypassing the need for /dev/mem access?
Any Rust-side fix or workaround suggestions to make the tool read the HII data correctly?
Any documentation or working examples where others have successfully parsed and modified BIOS settings via uefisettings?
I wanted to understand what I need to do exactly to make uefisettings work to modify options of Bios from userspace. Any documentation or guidence can really help me . I am breaking my head from last month to figure out why it fails .
Any guidance or suggestions would be deeply appreciated—especially on resolving the read_exact issue and getting uefisettings to recognize and work with the HII database correctly.
Thanks in advance!
However, when I try to run commands like show-ifr, list-questions, or list-strings, I get the following error: Can't parse more package lists: To investigate further, I manually created an EFI variable under /sys/firmware/efi/efivars/ that mimics the format expected by uefisettings.
Could you please copy&paste the log the terminal? (with the exact command you execute and exact output)
Also could you run it with strace -f and show the result there as well?
Why is uefisettings unable to read the data correctly from /dev/mem despite a valid memory region and variable?
Hard to say without a repro that I could run on my side :(
Could this be due to Rust’s read_exact requiring specific buffer alignment or size?
read_exact would fail, if it wasn't able to read "size" bytes, where size is roughly speaking provided by the firmware.
How can I debug or modify this behavior?
My intuition says that the issues is related to:
- either the fact this is an ARM platform (since
uefisettingswas mostly tested/used onx86_64/amd64platforms); - or to some bug in the firmware.
Until I have a repro I have no ideas how to debug this.
P.S.: It likely won't help to debug, but you may want to try running uefisettings with environment variable RUST_LOG=trace.
Are there any known limitations or required conditions (e.g., memory page alignment, permissions, architecture-specific quirks) for uefisettings ?
We wrote this tool for our use cases with OCP and HPE servers. It works in both cases. I'm aware only of these limitations:
- https://github.com/linuxboot/uefisettings/issues/2 --
uefisettingsdoes not implement access to password-protected variables. - There are a lot of firmwares that just implement this interface (HII) incorrectly.
- On one of the ARM-specific proprietary firmwares it does not work because of one more reason, but due to the proprietary nature of that firmware I'm not allowed to share unfortunately (might be NDA) :(
- The tool does not understand grayed-out/disabled options and displays everything, that sometimes makes more difficult to access the enabled variable (definitely not the problem you are having, though).
If the firmware implements the interface correctly, the tool should work, regardless of alignment, or anything else.
Permissions are required, yes. I assume you run under actual root, you don't have any LSMs blocking uefisettings, and firmware does give permission to access the variables -- if these assumptions are correct, then I'd expect there should not be problems with permissions.
Is there a way to feed the extracted HII binary data into uefisettings directly, bypassing the need for /dev/mem access?
One can pass HiiDB via option --filename.
Any Rust-side fix or workaround suggestions to make the tool read the HII data correctly?
Likely the tool already reads HII data correctly. I did like 20 investigations with a claim that it does not read it correctly, and essentially every single time the issue was on the firmware side.
Any documentation or working examples where others have successfully parsed and modified BIOS settings via uefisettings?
I did it a lot of times, what exactly would you like to know? :) One example is provided in https://github.com/linuxboot/uefisettings?tab=readme-ov-file#usage-examples
I am breaking my head from last month to figure out why it fails . Any guidance or suggestions would be deeply appreciated—especially on resolving the read_exact issue and getting uefisettings to recognize and work with the HII database correctly.
The best would be to start with actual log of uefisettings run and strace log. Then depending on what we would see in strace it might be useful to either try perf trace or save the inputs we see in strace (and send them to this task, so that I can reproduce on my side).
@xaionaro Thanks for your quick response — I genuinely appreciate it. After struggling with this issue for almost a month, it feels like I finally have someone to talk to who understands the space.
cp /sys/firmware/efi/efivars/HiiDb-de629656-a0a4-4604-8b38-ae25bbb0e4a5 test.bin
hexdump -C test.bin
00000000 06 00 00 00 c4 31 00 00 00 00 d3 ee |.....1......|
0000000c
strace ./uefisettings hii extract-db out.bin
execve("./uefisettings", ["./uefisettings", "hii", "extract-db", "out.bin"], 0xffffc35f6a18 /* 10 vars */) = 0
brk(NULL) = 0xaaab1a14c000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffff8c81e000
faccessat(AT_FDCWD, "/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib64/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=133328, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 262992, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffff8c7a5000
mmap(0xffff8c7b0000, 197456, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffff8c7b0000
munmap(0xffff8c7a5000, 45056) = 0
munmap(0xffff8c7e1000, 17232) = 0
mprotect(0xffff8c7c8000, 94208, PROT_NONE) = 0
mmap(0xffff8c7df000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0xffff8c7df000
close(3) = 0
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0P\264\2\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=1650960, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 1826928, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffff8c5f1000
mmap(0xffff8c600000, 1761392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffff8c600000
munmap(0xffff8c5f1000, 61440) = 0
munmap(0xffff8c7af000, 112) = 0
mprotect(0xffff8c783000, 106496, PROT_NONE) = 0
mmap(0xffff8c79d000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18d000) = 0xffff8c79d000
mmap(0xffff8c7a2000, 49264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffff8c7a2000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffff8c81c000
set_tid_address(0xffff8c81c0f0) = 319;
set_robust_list(0xffff8c81c100, 24) = 0
rseq(0xffff8c81c740, 0x20, 0, 0xd428bc00) = 0
mprotect(0xffff8c79d000, 12288, PROT_READ) = 0
mprotect(0xffff8c7df000, 4096, PROT_READ) = 0
mprotect(0xaaaada1fa000, 286720, PROT_READ) = 0
mprotect(0xffff8c824000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
ppoll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, {tv_sec=0, tv_nsec=0}, NULL, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTART}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
getrandom("\xf0\x16\x11\xc3\x1a\xe3\x83\xa2", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0xaaab1a14c000
brk(0xaaab1a16d000) = 0xaaab1a16d000
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
newfstatat(3, "", {st_mode=S_IFREG|0444, st_size=0, ...}, AT_EMPTY_PATH) = 0
read(3, "aaaad9f10000-aaaada1e9000 r-xp 0"..., 1024) = 1024
read(3, "c_s.so.1\nffff8c7e0000-ffff8c7e10"..., 1024) = 691
close(3) = 0
sched_getaffinity(319, 32, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27]) = 8
rt_sigaction(SIGSEGV, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xffff8c817000
mprotect(0xffff8c817000, 4096, PROT_NONE) = 0
sigaltstack({ss_sp=0xffff8c818000, ss_flags=0, ss_size=16384}, NULL) = 0
rt_sigaction(SIGSEGV, {sa_handler=0xaaaada106e50, sa_mask=[], sa_flags=SA_ONSTACK|SA_SIGINFO}, NULL, 8) = 0
rt_sigaction(SIGBUS, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0xaaaada106e50, sa_mask=[], sa_flags=SA_ONSTACK|SA_SIGINFO}, NULL, 8) = 0
ioctl(2, TIOCGWINSZ, {ws_row=0, ws_col=0, ws_xpixel=0, ws_ypixel=0}) = 0
getrandom("\x05\x0b\xf7\x84\xd8\xb9\xdd\x8b\xcc\xce\x9b\x8f\x5c\x83\x95\x62", 16, GRND_INSECURE) = 16
openat(AT_FDCWD, "out.bin", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
write(1, "Starting HiiDb extraction from '"..., 48Starting HiiDb extraction from '/root/test.bin'
) = 48
openat(AT_FDCWD, "/root/test.bin", O_RDONLY|O_CLOEXEC) = 4
write(1, "Successfully opened efivar file."..., 33Successfully opened efivar file.
) = 33
statx(4, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=12, ...}) = 0
lseek(4, 0, SEEK_CUR) = 0
read(4, "\6\0\0\0\3041\0\0\0\0\323\356", 12) = 12
read(4, "", 32) = 0
write(1, "Read 12 bytes from efivar file.\n", 32Read 12 bytes from efivar file.
) = 32
write(1, "Parsed HiiDBEFIVar: flags=0x0000"..., 76Parsed HiiDBEFIVar: flags=0x00000006, length=0x000031c4, address=0xeed30000
) = 76
openat(AT_FDCWD, "/dev/mem", O_RDONLY|O_CLOEXEC) = 5
write(1, "Opened /dev/mem\n", 16Opened /dev/mem
) = 16
write(1, "Seeking to physical address 0xee"..., 54Seeking to physical address 0xeed30000 in /dev/mem...
) = 54
lseek(5, 4006805504, SEEK_SET) = 4006805504
write(1, "Allocated buffer of 12740 bytes\n", 32Allocated buffer of 12740 bytes
) = 32
read(5, 0xaaab1a150150, 12740) = -1 EFAULT (Bad address)
close(5) = 0
close(4) = 0
close(3) = 0
write(1, "{\"error_message\":\"Failed to read"..., 102{"error_message":"Failed to read bytes of specified length from /dev/mem: Bad address (os error 14)"}
) = 102
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=16384}, NULL) = 0
munmap(0xffff8c817000, 20480) = 0
exit_group(1) = ?
+++ exited with 1 +++
So it failed here:
write(1, "Seeking to physical address 0xee"..., 54Seeking to physical address 0xeed30000 in /dev/mem...
) = 54
lseek(5, 4006805504, SEEK_SET) = 4006805504
write(1, "Allocated buffer of 12740 bytes\n", 32Allocated buffer of 12740 bytes
) = 32
read(5, 0xaaab1a150150, 12740) = -1 EFAULT (Bad address)
Likely it was provided with an address that leads to 0xeed30000 and size 12740 ultimately from the firmware. And then an attempt to read this via /dev/mem failed. You can try reading this area yourself using dd from /dev/mem to reproduce the issue:
sudo dd if=/dev/mem of=/dev/stdout bs=1 skip=$((16#eed30000)) count=12740 | hexdump -C
So likely the question is where this address comes from.
Here you can see that address and size come from HiiDB. In your case it seems that you feed HiiDB content from file /root/test.bin (see below).
The strace log looks weird: it looks like it was edited (or the source code was edited). For example the first line says:
execve("./uefisettings", ["./uefisettings", "hii", "extract-db", "out.bin"], 0xffffc35f6a18 /* 10 vars */) = 0
But then we can see:
write(1, "Starting HiiDb extraction from '"..., 48Starting HiiDb extraction from '/root/test.bin'
) = 48
openat(AT_FDCWD, "/root/test.bin", O_RDONLY|O_CLOEXEC) = 4
~~Where this path /root/test.bin gets from?~~
~~And also in any case, could you share the file, please?~~
Never mind, I assume you've just modified the code; in any case see the comment below.
Here we see the content of /root/test.bin:
openat(AT_FDCWD, "/root/test.bin", O_RDONLY|O_CLOEXEC) = 4
write(1, "Successfully opened efivar file."..., 33Successfully opened efivar file.
) = 33
statx(4, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=12, ...}) = 0
lseek(4, 0, SEEK_CUR) = 0
read(4, "\6\0\0\0\3041\0\0\0\0\323\356", 12) = 12
read(4, "", 32)
xaionaro@void:~$ printf '\6\0\0\0\3041\0\0\0\0\323\356' | hexdump -C
00000000 06 00 00 00 c4 31 00 00 00 00 d3 ee |.....1......|
0000000c
(which seem to match the hexdump provided by you above, so I assume /root/test.bin is the same as your test.bin)
And yes, one can see: 00 00 d3 ee, which translates into address 0xeed30000 (4006805504), and one can see c4 31 00 00 which translates into size 0x31c4 (12740). Then one can see the tool obeys, and goes to /dev/mem to read exactly that and gets an error:
openat(AT_FDCWD, "/dev/mem", O_RDONLY|O_CLOEXEC) = 5
...
lseek(5, 4006805504, SEEK_SET) = 4006805504
...
read(5, 0xaaab1a150150, 12740) = -1 EFAULT (Bad address)
So the tool seem to work fine, and this is a HiiDB-file or/and firmware problem :)
test.bin I see this is going to be the output dump file of the hiidb content from extract-db ,I placed tool in /root and trying to give dump path in the same folder hence /root/test.bin ( after I login to the system I see i am in /root folder by default).
Also tried adding some logs in https://github.com/linuxboot/uefisettings/blob/main/src/lib/hii/extract.rs file ,diff as follows:
diff --git a/src/lib/hii/extract.rs b/src/lib/hii/extract.rs
index e963cbd..2c6f290 100644
--- a/src/lib/hii/extract.rs
+++ b/src/lib/hii/extract.rs
@@ -53,10 +53,15 @@ pub fn extract_db() -> Result<Vec<u8>> {
// Now that we have offset and size from the HiiDB efivar, use it to read DB from memory.
+ // open /dev/mem and seek
let mut mem_file = File::open("/dev/mem").context("Failed to open /dev/mem")?;
- mem_file.seek(SeekFrom::Start(db_info.address as u64))?;
+ println!("Opened /dev/mem, attempting to read HiiDB at address 0x{:x} with length {}",db_info.address, db_info.length);
+ let seek_target = db_info.address as u64;
+ let pos = mem_file.seek(SeekFrom::Start(seek_target)).context("Failed to seek /dev/mem")?;
+ println!("Requested seek to 0x{:x}, kernel reported position 0x{:x}",seek_target, pos);
let mut buf = vec![0u8; db_info.length.try_into()?];
+ println!("Reading {} bytes from /dev/mem at position {:p}", buf.len(), buf.as_ptr());
mem_file
.read_exact(&mut buf)
.context("Failed to read bytes of specified length from /dev/mem")?;
I tried running uefisettings after adding above changes (binary name changed for my convinence uefisetting_orig_logs);
# stracte -f ./uefisetting_orig_logs hii extract-db test.dump
execve("./uefisetting_orig_logs", ["./uefisetting_orig_logs", "hii", "extract-db", "test.dump"], 0xffffcb667c10 /* 10 vars */) = 0
brk(NULL) = 0xaaaae7cd1000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffba018000
faccessat(AT_FDCWD, "/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib64/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=133328, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 262992, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffffb9f9e000
mmap(0xffffb9fa0000, 197456, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffffb9fa0000
munmap(0xffffb9f9e000, 8192) = 0
munmap(0xffffb9fd1000, 54096) = 0
mprotect(0xffffb9fb8000, 94208, PROT_NONE) = 0
mmap(0xffffb9fcf000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0xffffb9fcf000
close(3) = 0
openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0P\264\2\0\0\0\0\0"..., 832) = 832
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=1650960, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 1826928, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffffb9de1000
mmap(0xffffb9df0000, 1761392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffffb9df0000
munmap(0xffffb9de1000, 61440) = 0
munmap(0xffffb9f9f000, 112) = 0
mprotect(0xffffb9f73000, 106496, PROT_NONE) = 0
mmap(0xffffb9f8d000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18d000) = 0xffffb9f8d000
mmap(0xffffb9f92000, 49264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffffb9f92000
close(3) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffba016000
set_tid_address(0xffffba0160f0) = 324
set_robust_list(0xffffba016100, 24) = 0
rseq(0xffffba016740, 0x20, 0, 0xd428bc00) = 0
mprotect(0xffffb9f8d000, 12288, PROT_READ) = 0
mprotect(0xffffb9fcf000, 4096, PROT_READ) = 0
mprotect(0xaaaab552a000, 286720, PROT_READ) = 0
mprotect(0xffffba01d000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
ppoll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, {tv_sec=0, tv_nsec=0}, NULL, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTART}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
getrandom("\x72\xbb\xd7\x1d\x85\x77\xa7\x6a", 8, GRND_NONBLOCK) = 8
brk(NULL) = 0xaaaae7cd1000
brk(0xaaaae7cf2000) = 0xaaaae7cf2000
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
newfstatat(3, "", {st_mode=S_IFREG|0444, st_size=0, ...}, AT_EMPTY_PATH) = 0
read(3, "aaaab5240000-aaaab5518000 r-xp 0"..., 1024) = 1024
read(3, " /lib/libgcc_s.s"..., 1024) = 718
close(3) = 0
sched_getaffinity(324, 32, [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27]) = 8
rt_sigaction(SIGSEGV, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0xffffba011000
mprotect(0xffffba011000, 4096, PROT_NONE) = 0
sigaltstack({ss_sp=0xffffba012000, ss_flags=0, ss_size=16384}, NULL) = 0
rt_sigaction(SIGSEGV, {sa_handler=0xaaaab5435d28, sa_mask=[], sa_flags=SA_ONSTACK|SA_SIGINFO}, NULL, 8) = 0
rt_sigaction(SIGBUS, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0xaaaab5435d28, sa_mask=[], sa_flags=SA_ONSTACK|SA_SIGINFO}, NULL, 8) = 0
ioctl(2, TIOCGWINSZ, {ws_row=0, ws_col=0, ws_xpixel=0, ws_ypixel=0}) = 0
getrandom("\xe5\x00\x45\x5b\xaa\x87\x85\xf9\x50\x42\xa7\xb3\x0c\x94\x28\xbc", 16, GRND_INSECURE) = 16
openat(AT_FDCWD, "test.dump", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
openat(AT_FDCWD, "/sys/firmware/efi/efivars/HiiDB-1b838190-4625-4ead-abc9-cd5e6af18fe0", O_RDONLY|O_CLOEXEC) = 4
statx(4, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_BASIC_STATS|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0644, stx_size=12, ...}) = 0
lseek(4, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
read(4, "\7\0\0\0005>\0\0\30\1\232\362", 32) = 12
read(4, "", 32) = 0
openat(AT_FDCWD, "/dev/mem", O_RDONLY|O_CLOEXEC) = 5
write(1, "Opened /dev/mem, attempting to r"..., 82Opened /dev/mem, attempting to read HiiDB at address 0xf29a0118 with length 15925
) = 82
lseek(5, 4070179096, SEEK_SET) = 4070179096
write(1, "Requested seek to 0xf29a0118, ke"..., 66Requested seek to 0xf29a0118, kernel reported position 0xf29a0118
) = 66
write(1, "Reading 15925 bytes from /dev/me"..., 61Reading 15925 bytes from /dev/mem at position 0xaaaae7cd5150
) = 61
read(5, 0xaaaae7cd5150, 15925) = -1 EFAULT (Bad address)
close(5) = 0
close(4) = 0
close(3) = 0
write(1, "{\"error_message\":\"Failed to read"..., 102{"error_message":"Failed to read bytes of specified length from /dev/mem: Bad address (os error 14)"}
) = 102
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=16384}, NULL) = 0
munmap(0xffffba011000, 20480) = 0
exit_group(1) = ?
+++ exited with 1 +++
#
# ./uefisetting_orig_logs hii extract-db test.dump
Opened /dev/mem, attempting to read HiiDB at address 0xf29a0118 with length 15925
Requested seek to 0xf29a0118, kernel reported position 0xf29a0118
Reading 15925 bytes from /dev/mem at position 0xaaaadf05c150
{"error_message":"Failed to read bytes of specified length from /dev/mem: Bad address (os error 14)"}
Also I tried dumping the hiidb content using a sample c code which used mmap since dd command fails , I have made sure CONFIG_STRICT_DEVMEM is disabled as well.
zcat /proc/config.gz | grep CONFIG_STRICT_DEVMEM
CONFIG_STRICT_DEVMEM is not set
./mmap_test 0xf29a0118 16384 hiidump.hex
Read 16384 bytes from physical 0xf29a0118 -> hiidump.hex
Also dd commadn strace is as follows :
strace -f dd if=/dev/mem of=/dev/stdout bs=1 skip=$((16#f29a0118)) count=16384 | hexdump -C
execve("/bin/dd", ["dd", "if=/dev/mem", "of=/dev/stdout", "bs=1", "skip=4070179096", "count=16384"], 0xffffcd01b090 /* 10 vars /) = 0 brk(NULL) = 0xaaaad7ecb000 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffb019e000 faccessat(AT_FDCWD, "/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/lib64/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832 newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=68080, ...}, AT_EMPTY_PATH) = 0 mmap(NULL, 207352, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffffb0132000 mmap(0xffffb0140000, 141816, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffffb0140000 munmap(0xffffb0132000, 57344) = 0 munmap(0xffffb0163000, 6648) = 0 mprotect(0xffffb014d000, 73728, PROT_NONE) = 0 mmap(0xffffb015f000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xf000) = 0xffffb015f000 mmap(0xffffb0161000, 6648, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffffb0161000 close(3) = 0 openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0P\264\2\0\0\0\0\0"..., 832) = 832 newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=1650960, ...}, AT_EMPTY_PATH) = 0 mmap(NULL, 1826928, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_DENYWRITE, -1, 0) = 0xffffaff81000 mmap(0xffffaff90000, 1761392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0xffffaff90000 munmap(0xffffaff81000, 61440) = 0 munmap(0xffffb013f000, 112) = 0 mprotect(0xffffb0113000, 106496, PROT_NONE) = 0 mmap(0xffffb012d000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18d000) = 0xffffb012d000 mmap(0xffffb0132000, 49264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xffffb0132000 close(3) = 0 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xffffb019c000 set_tid_address(0xffffb019c0f0) = 342 set_robust_list(0xffffb019c100, 24) = 0 rseq(0xffffb019c740, 0x20, 0, 0xd428bc00) = 0 mprotect(0xffffb012d000, 12288, PROT_READ) = 0 mprotect(0xffffb015f000, 4096, PROT_READ) = 0 mprotect(0xaaaab7b96000, 12288, PROT_READ) = 0 mprotect(0xffffb01a3000, 8192, PROT_READ) = 0 prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=81921024, rlim_max=RLIM64_INFINITY}) = 0 getrandom("\xd5\xeb\xb5\xb2\x8c\x12\xa9\xd0", 8, GRND_NONBLOCK) = 8 getuid() = 0 brk(NULL) = 0xaaaad7ecb000 brk(0xaaaad7eec000) = 0xaaaad7eec000 rt_sigaction(SIGUSR1, {sa_handler=0xaaaab7b3fe84, sa_mask=[], sa_flags=SA_RESTART}, NULL, 8) = 0 openat(AT_FDCWD, "/dev/mem", O_RDONLY) = 3 dup3(3, 0, 0) = 0 close(3) = 0 openat(AT_FDCWD, "/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 dup3(3, 1, 0) = 1 close(3) = 0 lseek(0, 4070179096, SEEK_CUR) = 4070179096 read(0, 0xaaaad7ecb2a0, 1) = -1 EFAULT (Bad address) write(2, "dd: /dev/mem: Bad address\n", 26dd: /dev/mem: Bad address ) = 26 exit_group(1) = ? +++ exited with 1 +++
My sample C code which I use for dumping /dev/mem content is attached as mmap_dump.txt
My first Strace log is bit old one , I have ran again and added in my before message where the address is 0xf29a0118 and size 16384( actual size of Hiidb is 15925 but just to align 4k page size I made it 16384, as my mmap also fails if not page aligned )
hexdump -C /sys/firmware/efi/efivars/HiiDB-1b838190-4625-4ead-abc9-cd5e6af18fe0
00000000 07 00 00 00 35 3e 00 00 18 01 9a f2 |....5>......| 0000000c
I forgot to mention about the hiidump.txt , since I was not able to copy the dumped file from my environment I copied the data from the uart output to a text file .
@xaionaro Here’s a concise recap of what I did to get your UEFISetting tool’s extract-db working on the HiiDB firmware blob:
- Identified the /dev/mem read path Discovered that read_exact() in your tool calls the generic /dev/mem driver’s read_mem() routine in drivers/char/mem.c. By default read_mem() only allows reads from “real RAM” (it rejects any physical address ≥ __pa(high_memory)) and also enforces page_is_allowed(), which blocks MMIO/firmware regions.
- Bypassed the RAM-only check Commented out the valid_phys_addr_range() guard so the EFAULT from “not real RAM” would no longer fire.
- Neutralized the page_is_allowed() barrier Patched out the page_is_allowed(pfn) test (or forced it to always return true) so that strictly-protected pages (MMIO/UEFI vars) would be considered readable.
- Replaced unsafe kernel copy with I/O-safe mapping The original driver path still used xlate_dev_mem_ptr() + copy_from_kernel_nofault() and blew past that on firmware pages. We inserted an explicit ioremap(page_pa, PAGE_SIZE)/iounmap() around each page and used memcpy_fromio() to safely read from the resulting I/O mapping into our bounce buffer.
- Added debug logging Sprinkled pr_info() calls at key points in read_mem() to trace entry, loop iterations, pointer mappings, and error conditions. Verified via dmesg --follow that each physical‐page copy was happening without faults.
Now I am able to get the Hiidump.bin file , I would like to understand is my Hii base in secure region here? I am trying hard to figure out now .
Also next challenge I am facing is while running show-ifr :
./uefisetting_orig_logs hii show-ifr test.bin
Opened /dev/mem, attempting to read HiiDB at address 0xf29a0118 with length 15925 Requested seek to 0xf29a0118, kernel reported position 0xf29a0118 Reading 15925 bytes from /dev/mem at position 0xaaab04bfb150
[1970-01-01T00:03:00Z ERROR uefisettings::hii::package] Can't parse more package lists: ╺━━━━━━━━━━━━━━━━━━━━┅ Backtrace ┅━━━━━━━━━━━━━━━━━━━━╸
0: Error: not enough bytes in reader
While parsing field 'data' in PackageList
at src/lib/hii/package.rs:39
╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸
{"error_message":"\n ╺━━━━━━━━━━━━━━━━━━━━┅ Backtrace ┅━━━━━━━━━━━━━━━━━━━━╸\n\n 0: \u001b[1m\u001b[1mError: not enough bytes in reader\u001b[22m\n \u001b[1mWhile parsing field 'data' in PackageList\u001b[22m\u001b[22m\n at src/lib/hii/package.rs:39\n\n ╺━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸\n\n"}
@xaionaro can you provide me details on how the data should look like when I run show-ifr, list-questions & list-strings please , I am trying to analyse my hiidump for the failue I receive when I run the commands . Please need your inputs for me to solve this issue .
Hey. Sorry, I maintain uefisettings not as part of my work, so I can respond with substance only on weekends/holidays :(
@xaionaro ,
I’ve got extract-db working and now need to understand how show-ifr, list-questions, list-strings (and get/set) operate. Would you be able to share a small sample HII binary (or IFR blob) that you know works with those commands? I’d like to run it locally to see the exact format and compare it against my hiidump.
Thanks!
@xaionaro I’ve opened a pull request to fix a bug related to HII database parsing when trailing padding bytes (0x00 or 0xFF) are present after the last valid package list " [https://github.com/linuxboot/uefisettings/pull/9 ] " . Can you please review the changes ,they might be useful in future for any other teams looking to use uefisettings tool.
Issue Summary: While parsing a full HII database dump, the tool attempts to read additional package lists even after the actual data ends. If padding or leftover bytes follow, it tries to interpret them as a new package list header, leading to a subtraction overflow and a runtime panic .
Fix provided: I added a guard in get_package_lists() that:
Stops parsing if fewer than 20 bytes remain (not enough for a valid package list header)
Stops if the next 20 bytes are all 0x00 or 0xFF (interpreted as padding)
This prevents the panic and ensures graceful handling of valid and padded dumps.