diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-31 02:55:10 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-31 02:55:10 +0200 |
| commit | 1963a0a6d7398a426d0701f90a5bc796e32899ae (patch) | |
| tree | 8294a30acd2c8bb0d0ff98ebaee14f9667cbcc6d /pad_for_optiplex.py | |
| download | rsboot-1963a0a6d7398a426d0701f90a5bc796e32899ae.tar.gz rsboot-1963a0a6d7398a426d0701f90a5bc796e32899ae.zip | |
feat(rsboot): initial commit — UEFI bootloader extracted from RNG
Diffstat (limited to 'pad_for_optiplex.py')
| -rw-r--r-- | pad_for_optiplex.py | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/pad_for_optiplex.py b/pad_for_optiplex.py new file mode 100644 index 0000000..2d48552 --- /dev/null +++ b/pad_for_optiplex.py | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | #!/usr/bin/env python3 | ||
| 2 | """pad_for_optiplex.py — pad a UEFI bootloader PE for Optiplex 7010 | ||
| 3 | |||
| 4 | The Optiplex 7010 AMI Aptio firmware rejects PEs smaller than ~160KB | ||
| 5 | ("no bootable devices"). This pads the PE to match systemd-boot's size | ||
| 6 | and sets SizeOfImage accordingly. | ||
| 7 | """ | ||
| 8 | |||
| 9 | import struct, sys, os | ||
| 10 | |||
| 11 | def pad(path): | ||
| 12 | try: | ||
| 13 | with open(path, 'rb') as f: | ||
| 14 | d = bytearray(f.read()) | ||
| 15 | except IOError as e: | ||
| 16 | print(f" ERROR: cannot read {path}: {e}") | ||
| 17 | return False | ||
| 18 | |||
| 19 | po = struct.unpack('<I', d[0x3C:0x40])[0] | ||
| 20 | if d[po:po+4] != b'PE\x00\x00': | ||
| 21 | print(f" Not a PE file") | ||
| 22 | return False | ||
| 23 | |||
| 24 | opt = po + 24 | ||
| 25 | |||
| 26 | # Target size: 160256 bytes (matching systemd-boot) | ||
| 27 | target = 160256 | ||
| 28 | if len(d) >= target: | ||
| 29 | print(f" Already >= {target} bytes ({len(d)}), nothing to do") | ||
| 30 | return True | ||
| 31 | |||
| 32 | # Compute SizeOfImage: round current SizeOfImage to match file growth | ||
| 33 | cur_si = struct.unpack('<I', d[opt+56:opt+60])[0] | ||
| 34 | cur_file_size = len(d) | ||
| 35 | |||
| 36 | # The file is padded after all sections. SizeOfImage is the highest | ||
| 37 | # RVA + virtual size, rounded up to SectionAlignment. After padding | ||
| 38 | # the file, we need to increase SizeOfImage so the firmware sees a | ||
| 39 | # consistent image. | ||
| 40 | # | ||
| 41 | # Simple approach: set SizeOfImage = target - file_delta + old_si | ||
| 42 | # Actually: SizeOfImage already covers our sections at up to ~0x4000. | ||
| 43 | # We just need to make it large enough for the padded region. | ||
| 44 | # Use systemd-boot's SizeOfImage = 0x2d000 as reference. | ||
| 45 | file_growth = target - len(d) | ||
| 46 | new_si = ((cur_si + file_growth + 0xfff) // 0x1000) * 0x1000 | ||
| 47 | |||
| 48 | # Pad the file | ||
| 49 | d += b'\x00' * file_growth | ||
| 50 | |||
| 51 | # Update SizeOfImage in optional header | ||
| 52 | struct.pack_into('<I', d, opt+56, new_si) | ||
| 53 | |||
| 54 | try: | ||
| 55 | with open(path, 'wb') as f: | ||
| 56 | f.write(d) | ||
| 57 | except IOError as e: | ||
| 58 | print(f" ERROR: cannot write {path}: {e}") | ||
| 59 | return False | ||
| 60 | |||
| 61 | print(f" Padded: {len(d)} bytes (was {cur_file_size}), SizeOfImage = {new_si:#x}") | ||
| 62 | return True | ||
| 63 | |||
| 64 | if __name__ == '__main__': | ||
| 65 | if len(sys.argv) < 2: | ||
| 66 | print(f"Usage: {sys.argv[0]} <pe-path>") | ||
| 67 | sys.exit(1) | ||
| 68 | if not pad(sys.argv[1]): | ||
| 69 | sys.exit(1) | ||
