From: Michalis Pappas Date: Thu, 20 Jul 2023 04:31:51 +0000 (+0200) Subject: support/scripts: Add script to generate arm64 linux boot header X-Git-Tag: RELEASE-0.14.0~36 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=bdeef8585d6a0fcac0075c5619baf2e2bd0fa2f0;p=unikraft%2Funikraft.git support/scripts: Add script to generate arm64 linux boot header The Linux arm64 boot protocol [1] prepends the image with a modified MZ header: u32 code0; /* Executable code */ u32 code1; /* Executable code */ u64 text_offset; /* Image load offset, little endian */ u64 image_size; /* Effective Image size, little endian */ u64 flags; /* kernel flags, little endian */ u64 res2 = 0; /* reserved */ u64 res3 = 0; /* reserved */ u64 res4 = 0; /* reserved */ u32 magic = 0x644d5241; /* Magic number, little endian,"ARM\x64" */ u32 res5; /* reserved (used for PE COFF offset) */ Introduce mklinux.py that generates the arm64 linux header when CONFIG_LXBOOT is enabled on arm64. [1] https://www.kernel.org/doc/Documentation/arm64/booting.txt Signed-off-by: Michalis Pappas Reviewed-by: Sergiu Moga Approved-by: Razvan Deaconescu Tested-by: Unikraft CI GitHub-Closes: #988 --- diff --git a/support/scripts/mklinux.py b/support/scripts/mklinux.py new file mode 100755 index 000000000..d84ce14b0 --- /dev/null +++ b/support/scripts/mklinux.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import subprocess +import sys + +# Linux arm64 boot header +# https://www.kernel.org/doc/Documentation/arm64/booting.txt +LINUX_ARM64_HDR = { + "CODE0" : (0x0000000091005a4d, 0x04), # Offset 0 + "CODE1" : [0x0000000000000000, 0x04], # Offset 4 + "LOAD_OFFS" : [0x0000000000000000, 0x08], # Offset 8 + "IMAGE_SIZE" : [0x0000000000000000, 0x08], # Offset 16 + "KERNEL_FLAGS" : [0x0000000000000000, 0x08], # Offset 24 + "RES2" : (0x0000000000000000, 0x08), # Offset 32 + "RES3" : (0x0000000000000000, 0x08), # Offset 40 + "RES4" : (0x0000000000000000, 0x08), # Offset 48 + "MAGIC" : (0x00000000644d5241, 0x04), # Offset 56 + "RES5" : (0x0000000000000040, 0x04), # Offset 60 +} +LINUX_ARM64_HDR_SIZE = 64 + +# surely there's a pythonic way for that +def align_up(v, a): + return (((v) + (a)-1) & ~((a)-1)) + +def autoint(x): + return int(x, 0) + +# Get the absolute value of symbol, as seen through `nm` +def get_sym_val(elf, sym): + exp = r'^\s*' + r'([a-f0-9]+)' + r'\s+[A-Za-z]\s+' + sym + r'$' + out = subprocess.check_output(['nm', elf]) + + re_out = re.findall(exp, out.decode('ASCII'), re.MULTILINE) + if len(re_out) != 1: + raise Exception('Found no ' + sym + ' symbol.') + + return int(re_out[0], 16) + +def main(): + parser = argparse.ArgumentParser() + description='Prepends image with arm64 linux boot header.' + parser.add_argument('bin', help='Raw binary to prepend header with.') + parser.add_argument('elf', help='The ELF image the raw binary was derived from. ' + 'Required for resolving symbols used in the header.') + opt = parser.parse_args() + + ram_base = get_sym_val(opt.elf, r'_start_ram_addr') + img_base = get_sym_val(opt.elf, r'_base_addr') + entry = get_sym_val(opt.elf, r'_libkvmplat_entry') + + # code1 + # + # This contains an unconditional branch to the entry point. + # The encoding of the branch instruction according to the + # Arm ARM (DDI0487I.a) is: + # + # 31 25 0 + # ┌───────────┬───────────────────────────────┐ + # │0 0 0 1 0 1│ imm26 │ + # └───────────┴───────────────────────────────┘ + # + # b + # + # where offs is encoded as "imm26" times 4, relatively + # to the branch instruction + # + pc = img_base - LINUX_ARM64_HDR_SIZE + 4 + offs = entry - pc + + # offset must be <= +128M. We don't accept negative offsets + # here as the header always prepends the image. + assert((offs) <= ((1 << 26) / 2 - 1)) + + LINUX_ARM64_HDR["CODE1"][0] = ((0b101 << 26) | (int(offs / 4))) + + # load_offset + # + # This includes the header, so we subtract the header size + LINUX_ARM64_HDR["LOAD_OFFS"][0] = (img_base - ram_base) - LINUX_ARM64_HDR_SIZE + + # kernel_flags + # + # For now assume little-endian, 4KiB pages, image anywhere in memory + LINUX_ARM64_HDR["KERNEL_FLAGS"][0] = 0xa + + # image_size + # + # We arbitrarily set this to header size + image size aligned up to 2MiB + total_size = os.path.getsize(opt.bin) + LINUX_ARM64_HDR_SIZE + LINUX_ARM64_HDR["IMAGE_SIZE"][0] = align_up(total_size, 2 ** 21) + + # Create final image + with open(opt.bin, 'r+b') as f: + img = f.read() + f.seek(0) + for field in [k for k in LINUX_ARM64_HDR.keys()]: + f.write(LINUX_ARM64_HDR[field][0].to_bytes(LINUX_ARM64_HDR[field][1], 'little')) + f.write(img) + +if __name__ == '__main__': + main()