Skip to content

Commit e06594d

Browse files
authored
Merge pull request #97 from nathanchance/guess-architecture
2 parents cb9c38a + 4e6963d commit e06594d

1 file changed

Lines changed: 75 additions & 8 deletions

File tree

boot-qemu.py

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -661,16 +661,79 @@ def run(self):
661661
super().run()
662662

663663

664+
def guess_arch(kernel_arg):
665+
# kernel_arg is either a path to the kernel build folder or a full kernel
666+
# location. If it is a file, we need to strip off the basename so that we
667+
# can easily navigate around with '..'.
668+
if (kernel_dir := kernel_arg).is_file():
669+
kernel_dir = kernel_dir.parent
670+
671+
# If kernel_location is the kernel build folder, vmlinux will be at
672+
# <kernel_dir>/vmlinux
673+
#
674+
# If kernel_location is a full kernel location, it could either be:
675+
# * <kernel_dir>/vmlinux (if the image is vmlinux)
676+
# * <kernel_dir>/../../../vmlinux (if the image is in arch/*/boot/)
677+
#
678+
# Note: 'required=False' just to provide our own exception.
679+
vmlinux_locations = ['vmlinux', '../../../vmlinux']
680+
if not (vmlinux := utils.find_first_file(
681+
kernel_dir, vmlinux_locations, required=False)):
682+
raise RuntimeError(
683+
'Architecture was not provided and vmlinux could not be found!')
684+
685+
if not (file := shutil.which('file')):
686+
raise RuntimeError(
687+
"Architecture was not provided and 'file' is not installed!")
688+
689+
# Get output of file
690+
file_out = subprocess.run([file, vmlinux],
691+
capture_output=True,
692+
check=True,
693+
text=True).stdout.strip()
694+
695+
# Unfortunately, 'file' is not terribly precise when it comes to
696+
# microarchitecture or architecture revisions. As such, there are certain
697+
# strings that are just ambiguous so we bail out and let the user tell us
698+
# exactly what architecture they were hoping to boot.
699+
file_rosetta = {
700+
'ELF 32-bit LSB executable, ARM, EABI5': 'ambiguous', # could be any arm32
701+
'ELF 64-bit LSB pie executable, ARM aarch64': 'arm64',
702+
'ELF 64-bit MSB pie executable, ARM aarch64': 'arm64be',
703+
'ELF 32-bit MSB executable, Motorola m68k, 68020': 'm68k',
704+
'ELF 32-bit MSB executable, MIPS, MIPS32': 'mips',
705+
'ELF 32-bit LSB executable, MIPS, MIPS32': 'mipsel',
706+
'ELF 32-bit MSB executable, PowerPC': 'ambiguous', # could be ppc32 or ppc32_mac
707+
'ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, Power ELF V1 ABI': 'ppc64',
708+
'ELF 64-bit LSB executable, 64-bit PowerPC or cisco 7500, OpenPOWER ELF V2 ABI': 'ppc64le',
709+
'ELF 64-bit LSB executable, UCB RISC-V': 'riscv',
710+
'ELF 64-bit MSB executable, IBM S/390': 's390',
711+
'ELF 32-bit LSB executable, Intel 80386': 'x86',
712+
'ELF 64-bit LSB executable, x86-64': 'x86_64',
713+
} # yapf: disable
714+
for string, value in file_rosetta.items():
715+
if string in file_out:
716+
if value == 'ambiguous':
717+
raise RuntimeError(
718+
f"'{string}' found in '{file_out}' but the architecture is ambiguous, please explicitly specify it via '-a'!"
719+
)
720+
return value
721+
722+
raise RuntimeError(
723+
f"Architecture could not be deduced from '{file_out}', please explicitly specify it via '-a' or add support for it to guess_arch()!"
724+
)
725+
726+
664727
def parse_arguments():
665728
parser = ArgumentParser(description='Boot a Linux kernel in QEMU')
666729

667730
parser.add_argument(
668731
'-a',
669732
'--architecture',
670733
choices=SUPPORTED_ARCHES,
671-
help='The architecture to boot. Possible values are: %(choices)s',
672-
metavar='ARCH',
673-
required=True)
734+
help=
735+
"The architecture to boot. If omitted, value will be guessed based on 'vmlinux' if available. Possible values are: %(choices)s",
736+
metavar='ARCH')
674737
parser.add_argument('--efi',
675738
action='store_true',
676739
help='Boot kernel via UEFI (x86_64 only)')
@@ -719,6 +782,13 @@ def parse_arguments():
719782
if __name__ == '__main__':
720783
args = parse_arguments()
721784

785+
if not (kernel_location := Path(args.kernel_location).resolve()).exists():
786+
raise FileNotFoundError(
787+
f"Supplied kernel location ('{kernel_location}') does not exist!")
788+
789+
if not (arch := args.architecture):
790+
arch = guess_arch(kernel_location)
791+
722792
arch_to_runner = {
723793
'arm': ARMV7QEMURunner,
724794
'arm32_v5': ARMV5QEMURunner,
@@ -738,11 +808,8 @@ def parse_arguments():
738808
'x86': X86QEMURunner,
739809
'x86_64': X8664QEMURunner,
740810
}
741-
runner = arch_to_runner[args.architecture]()
811+
runner = arch_to_runner[arch]()
742812

743-
if not (kernel_location := Path(args.kernel_location).resolve()).exists():
744-
raise FileNotFoundError(
745-
f"Supplied kernel location ('{kernel_location}') does not exist!")
746813
if kernel_location.is_file():
747814
if args.gdb and kernel_location.name != 'vmlinux':
748815
raise RuntimeError(
@@ -759,7 +826,7 @@ def parse_arguments():
759826
runner.efi = runner.supports_efi
760827
if not runner.efi:
761828
utils.yellow(
762-
f"EFI boot requested on unsupported architecture ('{args.architecture}'), ignoring..."
829+
f"EFI boot requested on unsupported architecture ('{arch}'), ignoring..."
763830
)
764831

765832
if args.gdb:

0 commit comments

Comments
 (0)