pack_boot.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #!/usr/bin/env python3
  2. """
  3. ArkOS Boot Image Packager
  4. -------------------------
  5. Packs bootloader, stage2, animation, kernel and initramfs into a single boot.img.
  6. All paths are derived from --base-dir (the arkos/ directory).
  7. Usage: python3 pack_boot.py --base-dir /path/to/arkos
  8. """
  9. import argparse
  10. import os
  11. import struct
  12. import sys
  13. def align_up(val, align):
  14. return (val + align - 1) & ~(align - 1)
  15. def main():
  16. parser = argparse.ArgumentParser(description="ArkOS boot image packager")
  17. parser.add_argument("--base-dir", required=True,
  18. help="Path to the arkos/ base directory")
  19. args = parser.parse_args()
  20. base = args.base_dir
  21. bootloader_path = os.path.join(base, "out_staging", "bootloader.bin")
  22. stage2_path = os.path.join(base, "out_staging", "stage2.bin")
  23. kernel_path = os.path.join(base, "kernel", "prebuilts", "bzImage")
  24. initramfs_path = os.path.join(base, "finished", "initramfs.img")
  25. animation_path = os.path.join(base, "out_staging", "animation.bin")
  26. out_path = os.path.join(base, "finished", "boot.img")
  27. if not os.path.exists(kernel_path):
  28. kernel_path = "/boot/vmlinuz-linux-zen" # fallback
  29. with open(bootloader_path, "rb") as f:
  30. bootloader = bytearray(f.read())
  31. with open(stage2_path, "rb") as f:
  32. stage2 = bytearray(f.read())
  33. with open(kernel_path, "rb") as f:
  34. kernel = f.read()
  35. with open(initramfs_path, "rb") as f:
  36. initramfs = f.read()
  37. if os.path.exists(animation_path):
  38. with open(animation_path, "rb") as f:
  39. animation = f.read()
  40. else:
  41. animation = b""
  42. # Calculate LBA sectors (each sector is 512 bytes)
  43. bootloader_sectors = 1
  44. # Stage 2 MUST be exactly 7 sectors (bootloader.asm reads 7 sectors)
  45. stage2_padded_size = 7 * 512
  46. stage2 += b"\0" * (stage2_padded_size - len(stage2))
  47. animation_lba = bootloader_sectors + 7
  48. animation_size_sectors = align_up(len(animation), 512) // 512
  49. animation_padded = animation + b"\0" * (
  50. animation_size_sectors * 512 - len(animation)
  51. )
  52. bzimage_lba = animation_lba + animation_size_sectors
  53. bzimage_size_sectors = align_up(len(kernel), 512) // 512
  54. kernel_padded = kernel + b"\0" * (bzimage_size_sectors * 512 - len(kernel))
  55. initramfs_lba = bzimage_lba + bzimage_size_sectors
  56. initramfs_size_sectors = align_up(len(initramfs), 512) // 512
  57. initramfs_padded = initramfs + b"\0" * (
  58. initramfs_size_sectors * 512 - len(initramfs)
  59. )
  60. initramfs_size_bytes = len(initramfs)
  61. # Patch stage2 variables (7 consecutive dwords starting with default 8,0,0,0,0,0,0)
  62. pattern = struct.pack("<IIIIIII", 8, 0, 0, 0, 0, 0, 0)
  63. idx = stage2.find(pattern)
  64. if idx == -1:
  65. print("ERROR: Could not find variables in stage2 to patch!")
  66. sys.exit(1)
  67. print(f" Patching stage2 at offset {idx}")
  68. patched_vars = struct.pack(
  69. "<IIIIIII",
  70. bzimage_lba,
  71. bzimage_size_sectors,
  72. initramfs_lba,
  73. initramfs_size_sectors,
  74. initramfs_size_bytes,
  75. animation_lba,
  76. animation_size_sectors,
  77. )
  78. stage2[idx : idx + 28] = patched_vars
  79. with open(out_path, "wb") as f:
  80. f.write(bootloader)
  81. f.write(stage2)
  82. f.write(animation_padded)
  83. f.write(kernel_padded)
  84. f.write(initramfs_padded)
  85. total = os.path.getsize(out_path)
  86. print(f" boot.img built successfully! ({total:,} bytes)")
  87. if __name__ == "__main__":
  88. main()