live-bootstrap/rootfs.py

245 lines
9.5 KiB
Python
Raw Normal View History

2021-04-06 00:12:02 +01:00
#!/usr/bin/env python3
"""
A helper application used to start bootstrapping process.
It has a few modes of operation, you can create initramfs with
binary seeds and sources that you can boot into or alternatively
you can run bootstap inside chroot.
"""
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2022 Dor Askayo <dor.askayo@gmail.com>
2021-04-06 00:12:02 +01:00
# SPDX-FileCopyrightText: 2021 Andrius Štikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2021 Bastian Bittorf <bb@npl.de>
# SPDX-FileCopyrightText: 2021 Melg Eight <public.melg8@gmail.com>
# SPDX-FileCopyrightText: 2021-22 fosslinux <fosslinux@aussies.space>
2021-04-06 00:12:02 +01:00
import argparse
import os
import shutil
2021-04-06 00:12:02 +01:00
from sysa import SysA
from sysb import SysB
from sysc import SysC
from lib.utils import run
2021-10-20 21:11:48 +01:00
from lib.sysgeneral import stage0_arch_map
def create_configuration_file(args):
"""
Creates bootstrap.cfg file which would contain options used to
customize bootstrap.
"""
config_path = os.path.join('sysa', 'bootstrap.cfg')
with open(config_path, "w", encoding="utf_8") as config:
config.write("FORCE_TIMESTAMPS=" + str(args.force_timestamps) + "\n")
config.write("CHROOT=" + str(args.chroot or args.bwrap) + "\n")
2022-04-07 00:09:45 +01:00
config.write("UPDATE_CHECKSUMS=" + str(args.update_checksums) + "\n")
if args.external_sources:
config.write("DISK=sda1\n")
else:
config.write("DISK=sda\n")
2021-04-06 00:12:02 +01:00
# pylint: disable=too-many-statements
2021-04-06 00:12:02 +01:00
def main():
"""
A few command line arguments to customize bootstrap.
This function also creates SysA object which prepares directory
structure with bootstrap seeds and all sources.
"""
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--arch", help="Bootstrap architecture",
default="x86")
parser.add_argument("-c", "--chroot", help="Run inside chroot",
action="store_true")
parser.add_argument("-bw", "--bwrap", help="Run inside a bwrap sandbox",
action="store_true")
parser.add_argument("-p", "--preserve", help="Do not remove temporary dir",
2021-04-06 00:12:02 +01:00
action="store_true")
parser.add_argument("-t", "--tmpdir", help="Temporary directory")
2022-04-07 00:09:45 +01:00
parser.add_argument("--force-timestamps",
help="Force all files timestamps to be 0 unix time",
action="store_true")
2022-04-07 00:09:45 +01:00
parser.add_argument("--update-checksums",
help="Update checksum files.",
action="store_true")
parser.add_argument("--external-sources",
help="Download sources externally from live-bootstrap.",
action="store_true")
parser.add_argument("--no-create-config",
help="Do not automatically create config file",
action="store_true")
parser.add_argument("-r", "--repo",
help="Path to prebuilt binary packages.", nargs=None)
2021-04-06 00:12:02 +01:00
# QEMU arguments
parser.add_argument("-q", "--qemu", help="Use QEMU",
action="store_true")
parser.add_argument("-qc", "--qemu-cmd", help="QEMU command to run",
2021-04-06 00:12:02 +01:00
default="qemu-system-x86_64")
parser.add_argument("-qr", "--qemu-ram", help="Memory (in megabytes) allocated to QEMU VM",
default=4096)
parser.add_argument("-qk", "--kernel", help="Kernel to use (default is ./kernel)",
2021-04-06 00:12:02 +01:00
default="kernel")
parser.add_argument("-m", "--minikernel", help="Use minikernel",
action="store_true")
parser.add_argument("-b", "--bare-metal", help="Build images for bare metal",
action="store_true")
2021-04-06 00:12:02 +01:00
args = parser.parse_args()
def check_types():
count = 0
if args.qemu:
count += 1
if args.chroot:
count += 1
if args.bwrap:
count += 1
if args.minikernel:
count += 1
if args.bare_metal:
count += 1
return count
if check_types() > 1:
raise ValueError("No more than one of qemu, chroot, minikernel, bare metal may be used.")
if check_types() == 0:
raise ValueError("One of qemu, chroot, minikernel or bare metal must be selected.")
if args.bare_metal:
args.no_create_config = True
2021-04-06 00:12:02 +01:00
if args.arch != "x86":
raise ValueError("Only x86 is supported at the moment.")
try:
os.remove(os.path.join('sysa', 'bootstrap.cfg'))
except FileNotFoundError:
pass
if not args.no_create_config:
create_configuration_file(args)
else:
with open(os.path.join('sysa', 'bootstrap.cfg'), 'a', encoding='UTF-8'):
pass
2021-04-06 00:12:02 +01:00
system_c = SysC(arch=args.arch, preserve_tmp=args.preserve,
tmpdir=args.tmpdir, external_sources=args.external_sources)
system_b = SysB(arch=args.arch, preserve_tmp=args.preserve)
system_a = SysA(arch=args.arch, preserve_tmp=args.preserve,
tmpdir=args.tmpdir, external_sources=args.external_sources,
sysb_dir=system_b.sys_dir, sysc_dir=system_c.sys_dir)
2021-04-06 00:12:02 +01:00
bootstrap(args, system_a, system_b, system_c)
2021-04-06 00:12:02 +01:00
def bootstrap(args, system_a, system_b, system_c):
2021-04-06 00:12:02 +01:00
"""Kick off bootstrap process."""
print(f"Bootstrapping {args.arch} -- SysA")
2021-04-06 00:12:02 +01:00
if args.chroot:
find_chroot = """
import shutil
print(shutil.which('chroot'))
"""
chroot_binary = run('sudo', 'python3', '-c', find_chroot,
capture_output=True).stdout.decode().strip()
2021-10-20 21:11:48 +01:00
system_c.prepare(mount_tmpfs=True,
create_disk_image=False)
system_a.prepare(mount_tmpfs=True,
create_initramfs=False,
repo_path=args.repo)
# sysa
2021-10-20 21:11:48 +01:00
arch = stage0_arch_map.get(args.arch, args.arch)
init = os.path.join(os.sep, 'bootstrap-seeds', 'POSIX', arch, 'kaem-optional-seed')
run('sudo', 'env', '-i', 'PATH=/bin', chroot_binary, system_a.tmp_dir, init)
elif args.bwrap:
system_c.prepare(mount_tmpfs=False,
create_disk_image=False)
system_a.prepare(mount_tmpfs=False,
create_initramfs=False,
repo_path=args.repo)
# sysa
arch = stage0_arch_map.get(args.arch, args.arch)
init = os.path.join(os.sep, 'bootstrap-seeds', 'POSIX', arch, 'kaem-optional-seed')
run('bwrap', '--unshare-user',
'--uid', '0',
'--gid', '0',
'--cap-add', 'CAP_SYS_CHROOT', # Required for chroot from sysa to sysc
'--clearenv',
'--setenv', 'PATH', '/usr/bin',
'--bind', system_a.tmp_dir, '/',
'--dir', '/dev',
'--dev-bind', '/dev/null', '/dev/null',
'--dev-bind', '/dev/zero', '/dev/zero',
'--dev-bind', '/dev/random', '/dev/random',
'--dev-bind', '/dev/urandom', '/dev/urandom',
2022-06-13 07:17:05 +01:00
'--dir', '/sysc_image/dev',
'--dev-bind', '/dev/null', '/sysc_image/dev/null',
'--dev-bind', '/dev/zero', '/sysc_image/dev/zero',
'--dev-bind', '/dev/random', '/sysc_image/dev/random',
'--dev-bind', '/dev/urandom', '/sysc_image/dev/urandom',
'--proc', '/sysc_image/proc',
'--bind', '/sys', '/sysc_image/sys',
'--tmpfs', '/sysc_image/tmp',
init)
elif args.minikernel:
if os.path.isdir('kritis-linux'):
shutil.rmtree('kritis-linux')
system_c.prepare(mount_tmpfs=True,
create_disk_image=True)
system_a.prepare(mount_tmpfs=True,
2022-06-05 20:59:35 +01:00
create_initramfs=True,
repo_path=args.repo)
run('git', 'clone',
'--depth', '1', '--branch', 'v0.7',
2021-04-06 00:12:02 +01:00
'https://github.com/bittorf/kritis-linux.git')
run('kritis-linux/ci_helper.sh',
'--private',
'--multi', '1',
'--repeat', '1',
'--arch', args.arch,
'--qemucpu', '486',
'--kernel', '3.18.140',
'--features', 'kflock,highrestimers',
# Hack to add -hda /dev/blah
'--ramsize', str(args.qemu_ram) + 'M -hda ' + system_b.dev_name,
'--initrd', system_a.initramfs_path,
'--log', '/tmp/bootstrap.log')
elif args.bare_metal:
system_c.prepare(mount_tmpfs=True,
create_disk_image=True)
system_a.prepare(mount_tmpfs=True,
2022-06-05 20:59:35 +01:00
create_initramfs=True,
repo_path=args.repo)
print("Please:")
print(" 1. Take sysa/tmp/initramfs and your kernel, boot using this.")
print(" 2. Take sysc/tmp/disk.img and put this on a writable storage medium.")
else:
system_c.prepare(mount_tmpfs=True,
create_disk_image=True)
system_a.prepare(mount_tmpfs=True,
2022-06-05 20:59:35 +01:00
create_initramfs=True,
repo_path=args.repo)
run(args.qemu_cmd,
'-enable-kvm',
'-m', str(args.qemu_ram) + 'M',
'-no-reboot',
'-hda', system_c.dev_name,
'-nic', 'user,ipv6=off,model=e1000',
'-kernel', args.kernel,
'-initrd', system_a.initramfs_path,
Update the linux kernel for sysb/c to 4.9.10. - We do not use latest 4.9.x because it relies on a new version of binutils, while older versions do not. (Note: we should be able to go a bit newer but I didn't bother testing >50 versions to figure this out). - We do not use newer kernel versions because they require one or more of (new perl, new binutils, new make, new gcc, new bison, new tar). - sysb and sysc are updated to use the SATA (libata) subsystem (aka sda) instead of IDE-emulating SATA subsystem (aka hda) which is now available to us. - While theoretically according to docs 4.9 should work OOTB with our version of binutils this is not the case, so we have to do a bit of (interesting) patching. But this does not break anything. - Thankfully serial support in 4.9 is not screwed over like it is in 2.6 so we can revert to that. - 4.9 has the linux-libre project at our disposal, instead of gNewSense. So we use this. Unfortunatley that takes forever because we have to use sed because our version of gawk is too old/buggy. :( I plan to introduce very shortly 1. parallelism 2. 'sysc snapshot' which will start from sysc to avoid this. I do not want to use linux-libre tarballs because they make modificiations directly from this script (aka not easily verifiable, use the source!) and this script allows for much greater flexibility. - We compile the initramfs ahead-of-build using the in-tree cpio generator instead of also building cpio to use less packages. We do NOT build the initramfs into the kernel like 2.6 (unsupported). - Oh and fix a kexec-tools checksum.
2021-08-04 03:56:07 +01:00
'-nographic',
'-append', 'console=ttyS0')
2021-04-06 00:12:02 +01:00
if __name__ == "__main__":
2021-04-06 00:12:02 +01:00
main()