Skip to content
12 changes: 9 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# Run shellcheck and shfmt on all shell files and yapf on all Python files in this repository
# Run shellcheck and shfmt on all shell files and several Python linters on all Python files in this repository
name: Lint checks
on: [push, pull_request]
jobs:
python:
strategy:
fail-fast: false
matrix:
version: ['3.11', '3.10', '3.9', '3.8']
uses: ClangBuiltLinux/actions-workflows/.github/workflows/python_lint.yml@main
with:
python_version: ${{ matrix.version }}
shellcheck:
uses: ClangBuiltLinux/actions-workflows/.github/workflows/shellcheck.yml@main
shfmt:
uses: ClangBuiltLinux/actions-workflows/.github/workflows/shfmt.yml@main
yapf:
uses: ClangBuiltLinux/actions-workflows/.github/workflows/yapf.yml@main
68 changes: 35 additions & 33 deletions boot-qemu.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
# pylint: disable=invalid-name

import argparse
import os
Expand All @@ -7,6 +8,7 @@
import re
import shutil
import subprocess
import sys

import utils

Expand Down Expand Up @@ -54,28 +56,28 @@ def parse_arguments():
"--interactive",
"--shell",
action="store_true",
help=
help= # noqa: E251
"Instead of immediately shutting down the machine upon successful boot, pass 'rdinit=/bin/sh' on the kernel command line to allow interacting with the machine via a shell."
)
parser.add_argument(
"-k",
"--kernel-location",
required=True,
type=str,
help=
help= # noqa: E251
"Path to kernel image or kernel build folder to search for image in. Can be an absolute or relative path."
)
parser.add_argument(
"--no-kvm",
action="store_true",
help=
help= # noqa: E251
"Do not use KVM for acceleration even when supported (only recommended for debugging)."
)
parser.add_argument(
"-s",
"--smp",
type=int,
help=
help= # noqa: E251
"Number of processors for virtual machine. By default, only machines spawned with KVM will use multiple vCPUS."
)
parser.add_argument(
Expand Down Expand Up @@ -112,24 +114,25 @@ def can_use_kvm(can_test_for_kvm, guest_arch):
if can_test_for_kvm:
# /dev/kvm must exist to use KVM with QEMU
if Path("/dev/kvm").exists():
guest_arch = args.architecture
host_arch = platform.machine()

if host_arch == "aarch64":
# If /dev/kvm exists on aarch64, KVM is supported for aarch64 guests
if "arm64" in guest_arch:
return True
# 32-bit EL1 is not always supported, test for it first
if guest_arch == "arm" or guest_arch == "arm32_v7":
check_32_bit_el1_exec = base_folder.joinpath(
if guest_arch in ("arm", "arm32_v7"):
check_32_bit_el1 = base_folder.joinpath(
"utils", "aarch64_32_bit_el1_supported")
check_32_bit_el1 = subprocess.run([check_32_bit_el1_exec])
return check_32_bit_el1.returncode == 0
try:
subprocess.run([check_32_bit_el1], check=True)
except subprocess.CalledSubprocessError:
return False
return True

if host_arch == "x86_64" and "x86" in guest_arch:
# Check /proc/cpuinfo for whether or not the machine supports hardware virtualization
with open("/proc/cpuinfo") as f:
cpuinfo = f.read()
cpuinfo = Path("/proc/cpuinfo").read_text(encoding='utf-8')
# SVM is AMD, VMX is Intel
return cpuinfo.count("svm") > 0 or cpuinfo.count("vmx") > 0

Expand Down Expand Up @@ -186,8 +189,8 @@ def get_smp_value(args):
# CONFIG_NR_CPUS then get the actual value if possible.
config_nr_cpus = 8
if config_file:
with open(config_file) as f:
for line in f:
with open(config_file, encoding='utf-8') as file:
for line in file:
if "CONFIG_NR_CPUS=" in line:
config_nr_cpus = int(line.split("=", 1)[1])
break
Expand Down Expand Up @@ -400,7 +403,7 @@ def get_qemu_args(cfg):
qemu = "qemu-system-arm"
qemu_args += ["-machine", "romulus-bmc"]

elif arch == "arm" or arch == "arm32_v7":
elif arch in ("arm", "arm32_v7"):
append += " console=ttyAMA0 earlycon"
kernel_arch = "arm"
qemu_args += ["-machine", "virt"]
Expand All @@ -410,7 +413,7 @@ def get_qemu_args(cfg):
else:
qemu = "qemu-system-arm"

elif arch == "arm64" or arch == "arm64be":
elif arch in ("arm64", "arm64be"):
append += " console=ttyAMA0 earlycon"
kernel_arch = "arm64"
kernel_image = "Image.gz"
Expand Down Expand Up @@ -452,7 +455,7 @@ def get_qemu_args(cfg):
qemu_args += ["-cpu", "m68040"]
qemu_args += ["-M", "q800"]

elif arch == "mips" or arch == "mipsel":
elif arch in ("mips", "mipsel"):
kernel_arch = "mips"
kernel_image = "vmlinux"
qemu = f"qemu-system-{arch}"
Expand Down Expand Up @@ -658,23 +661,22 @@ def launch_qemu(cfg):
utils.check_cmd("lsof")
lsof = subprocess.run(["lsof", "-i:1234"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
stderr=subprocess.DEVNULL,
check=False)
if lsof.returncode == 0:
utils.die("Port 1234 is already in use, is QEMU running?")

utils.green("Starting QEMU with GDB connection on port 1234...")
qemu_process = subprocess.Popen(qemu_cmd + ["-s", "-S"])

utils.green("Starting GDB...")
utils.check_cmd(gdb_bin)
gdb_cmd = [gdb_bin]
gdb_cmd += [kernel_location.joinpath("vmlinux")]
gdb_cmd += ["-ex", "target remote :1234"]
subprocess.run(gdb_cmd)
with subprocess.Popen(qemu_cmd + ["-s", "-S"]) as qemu_process:
utils.green("Starting GDB...")
utils.check_cmd(gdb_bin)
gdb_cmd = [gdb_bin]
gdb_cmd += [kernel_location.joinpath("vmlinux")]
gdb_cmd += ["-ex", "target remote :1234"]
subprocess.run(gdb_cmd, check=False)
Comment thread
nathanchance marked this conversation as resolved.

utils.red("Killing QEMU...")
qemu_process.kill()
qemu_process.wait()
utils.red("Killing QEMU...")
qemu_process.kill()

answer = input("Re-run QEMU + gdb? [y/n] ")
if answer.lower() == "n":
Expand All @@ -695,14 +697,14 @@ def launch_qemu(cfg):
utils.red("ERROR: QEMU timed out!")
else:
utils.red("ERROR: QEMU did not exit cleanly!")
exit(ex.returncode)
sys.exit(ex.returncode)


if __name__ == '__main__':
args = parse_arguments()
arguments = parse_arguments()

# Build configuration from arguments and QEMU flags
cfg = setup_cfg(args)
cfg = get_qemu_args(cfg)
config = setup_cfg(arguments)
config = get_qemu_args(config)

launch_qemu(cfg)
launch_qemu(config)
14 changes: 7 additions & 7 deletions boot-uml.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
# pylint: disable=invalid-name

import argparse
from pathlib import Path
Expand All @@ -22,15 +23,15 @@ def parse_arguments():
"-i",
"--interactive",
action="store_true",
help=
help= # noqa: E251
"Instead of immediately shutting down upon successful boot, pass 'init=/bin/sh' to the UML executable to allow interacting with UML via a shell."
)
parser.add_argument(
"-k",
"--kernel-location",
required=True,
type=str,
help=
help= # noqa: E251
"Path to UML executable ('linux') or kernel build folder to search for executable in. Can be an absolute or relative path."
)

Expand All @@ -57,17 +58,17 @@ def decomp_rootfs():
return rootfs


def run_kernel(kernel, rootfs, interactive):
def run_kernel(kernel_image, rootfs, interactive):
"""
Run UML command with path to rootfs and additional arguments based on user
input.

Parameters:
* kernel (Path): kernel Path object containing full path to kernel.
* kernel_image (Path): kernel Path object containing full path to kernel.
* rootfs (Path): rootfs Path object containing full path to rootfs.
* interactive (bool): Whether or not to run UML interactively.
"""
uml_cmd = [kernel, f"ubd0={rootfs}"]
uml_cmd = [kernel_image, f"ubd0={rootfs}"]
if interactive:
uml_cmd += ["init=/bin/sh"]
print(f"$ {' '.join([str(element) for element in uml_cmd])}")
Expand All @@ -77,6 +78,5 @@ def run_kernel(kernel, rootfs, interactive):
if __name__ == '__main__':
args = parse_arguments()
kernel = utils.get_full_kernel_path(args.kernel_location, "linux")
rootfs = decomp_rootfs()

run_kernel(kernel, rootfs, args.interactive)
run_kernel(kernel, decomp_rootfs(), args.interactive)
5 changes: 3 additions & 2 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from pathlib import Path
import shutil
import sys


def check_cmd(cmd):
Expand All @@ -26,7 +27,7 @@ def die(string):
automatically.
"""
red(f"ERROR: {string}")
exit(1)
sys.exit(1)


def get_full_kernel_path(kernel_location, image, arch=None):
Expand All @@ -51,7 +52,7 @@ def get_full_kernel_path(kernel_location, image, arch=None):
else:
# If the image is an uncompressed vmlinux or a UML image, it is in the
# root of the build folder
if image == "vmlinux" or image == "linux":
if image in ("vmlinux", "linux"):
kernel = kernel_location.joinpath(image)
# Otherwise, it is in the architecture's boot directory
else:
Expand Down