mirror of https://github.com/seL4/microkit.git
794 lines
26 KiB
Python
794 lines
26 KiB
Python
# Copyright 2021, Breakaway Consulting Pty. Ltd.
|
|
# SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
"""The SDK build script.
|
|
|
|
# Why Python (and not make, or something else)?
|
|
|
|
We call out to Make, but having this top-level driver script
|
|
is useful.
|
|
|
|
There are just a lot of things that are much easier in Python
|
|
than in make.
|
|
|
|
"""
|
|
from argparse import ArgumentParser
|
|
from os import popen, system, environ
|
|
from shutil import copy
|
|
from pathlib import Path
|
|
from dataclasses import dataclass
|
|
from sys import executable
|
|
from tarfile import open as tar_open, TarInfo
|
|
import platform as host_platform
|
|
from enum import IntEnum
|
|
import json
|
|
|
|
from typing import Any, Dict, Union, List, Tuple, Optional
|
|
|
|
NAME = "microkit"
|
|
|
|
ENV_BIN_DIR = Path(executable).parent
|
|
|
|
MICROKIT_EPOCH = 1616367257
|
|
|
|
TRIPLE_AARCH64 = "aarch64-none-elf"
|
|
TRIPLE_RISCV = "riscv64-unknown-elf"
|
|
|
|
KERNEL_CONFIG_TYPE = Union[bool, str]
|
|
KERNEL_OPTIONS = Dict[str, Union[bool, str]]
|
|
|
|
|
|
class KernelArch(IntEnum):
|
|
AARCH64 = 1
|
|
RISCV64 = 2
|
|
|
|
def target_triple(self) -> str:
|
|
if self == KernelArch.AARCH64:
|
|
return TRIPLE_AARCH64
|
|
elif self == KernelArch.RISCV64:
|
|
return TRIPLE_RISCV
|
|
else:
|
|
raise Exception(f"Unsupported toolchain target triple '{self}'")
|
|
|
|
def is_riscv(self) -> bool:
|
|
return self == KernelArch.RISCV64
|
|
|
|
def is_arm(self) -> bool:
|
|
return self == KernelArch.AARCH64
|
|
|
|
def to_str(self) -> str:
|
|
if self == KernelArch.AARCH64:
|
|
return "aarch64"
|
|
elif self == KernelArch.RISCV64:
|
|
return "riscv64"
|
|
else:
|
|
raise Exception(f"Unsupported arch {self}")
|
|
|
|
def as_kernel_arch_config(self) -> tuple[str, str]:
|
|
return ("KernelSel4Arch", self.to_str())
|
|
|
|
|
|
@dataclass
|
|
class BoardInfo:
|
|
name: str
|
|
arch: KernelArch
|
|
gcc_cpu: Optional[str]
|
|
loader_link_address: int
|
|
kernel_options: KERNEL_OPTIONS
|
|
|
|
|
|
@dataclass
|
|
class ConfigInfo:
|
|
name: str
|
|
debug: bool
|
|
kernel_options: KERNEL_OPTIONS
|
|
|
|
|
|
SUPPORTED_BOARDS = (
|
|
BoardInfo(
|
|
name="tqma8xqp1gb",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a35",
|
|
loader_link_address=0x80280000,
|
|
kernel_options={
|
|
"KernelPlatform": "tqma8xqp1gb",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="zcu102",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x40000000,
|
|
kernel_options={
|
|
"KernelPlatform": "zynqmp",
|
|
"KernelARMPlatform": "zcu102",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="maaxboard",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x50000000,
|
|
kernel_options={
|
|
"KernelPlatform": "maaxboard",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="imx8mm_evk",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x41000000,
|
|
kernel_options={
|
|
"KernelPlatform": "imx8mm-evk",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="imx8mp_evk",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x41000000,
|
|
kernel_options={
|
|
"KernelPlatform": "imx8mp-evk",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="imx8mq_evk",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x41000000,
|
|
kernel_options={
|
|
"KernelPlatform": "imx8mq-evk",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="odroidc2",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x20000000,
|
|
kernel_options={
|
|
"KernelPlatform": "odroidc2",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="odroidc4",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a55",
|
|
loader_link_address=0x20000000,
|
|
kernel_options={
|
|
"KernelPlatform": "odroidc4",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="ultra96v2",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x40000000,
|
|
kernel_options={
|
|
"KernelPlatform": "zynqmp",
|
|
"KernelARMPlatform": "ultra96v2",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="qemu_virt_aarch64",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x70000000,
|
|
kernel_options={
|
|
"KernelPlatform": "qemu-arm-virt",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"QEMU_MEMORY": "2048",
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmExportPTMRUser": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="qemu_virt_riscv64",
|
|
arch=KernelArch.RISCV64,
|
|
gcc_cpu=None,
|
|
loader_link_address=0x90000000,
|
|
kernel_options={
|
|
"KernelPlatform": "qemu-riscv-virt",
|
|
"KernelIsMCS": True,
|
|
"QEMU_MEMORY": "2048",
|
|
"KernelRiscvExtD": True,
|
|
"KernelRiscvExtF": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="rpi4b_1gb",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a72",
|
|
loader_link_address=0x10000000,
|
|
kernel_options={
|
|
"KernelPlatform": "bcm2711",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"RPI4_MEMORY": 1024,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="rockpro64",
|
|
arch=KernelArch.AARCH64,
|
|
gcc_cpu="cortex-a53",
|
|
loader_link_address=0x30000000,
|
|
kernel_options={
|
|
"KernelPlatform": "rockpro64",
|
|
"KernelIsMCS": True,
|
|
"KernelArmExportPCNTUser": True,
|
|
"KernelArmHypervisorSupport": True,
|
|
"KernelArmVtimerUpdateVOffset": False,
|
|
"KernelAllowSMCCalls": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="hifive_p550",
|
|
arch=KernelArch.RISCV64,
|
|
gcc_cpu=None,
|
|
loader_link_address=0x90000000,
|
|
kernel_options={
|
|
"KernelIsMCS": True,
|
|
"KernelPlatform": "hifive-p550",
|
|
"KernelRiscvExtD": True,
|
|
"KernelRiscvExtF": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="star64",
|
|
arch=KernelArch.RISCV64,
|
|
gcc_cpu=None,
|
|
loader_link_address=0x60000000,
|
|
kernel_options={
|
|
"KernelIsMCS": True,
|
|
"KernelPlatform": "star64",
|
|
"KernelRiscvExtD": True,
|
|
"KernelRiscvExtF": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="ariane",
|
|
arch=KernelArch.RISCV64,
|
|
gcc_cpu=None,
|
|
loader_link_address=0x90000000,
|
|
kernel_options={
|
|
"KernelIsMCS": True,
|
|
"KernelPlatform": "ariane",
|
|
"KernelRiscvExtD": True,
|
|
"KernelRiscvExtF": True,
|
|
},
|
|
),
|
|
BoardInfo(
|
|
name="cheshire",
|
|
arch=KernelArch.RISCV64,
|
|
gcc_cpu=None,
|
|
loader_link_address=0x90000000,
|
|
kernel_options={
|
|
"KernelIsMCS": True,
|
|
"KernelPlatform": "cheshire",
|
|
"KernelRiscvExtD": True,
|
|
"KernelRiscvExtF": True,
|
|
},
|
|
),
|
|
)
|
|
|
|
SUPPORTED_CONFIGS = (
|
|
ConfigInfo(
|
|
name="release",
|
|
debug=False,
|
|
kernel_options={},
|
|
),
|
|
ConfigInfo(
|
|
name="debug",
|
|
debug=True,
|
|
kernel_options={
|
|
"KernelDebugBuild": True,
|
|
"KernelPrinting": True,
|
|
"KernelVerificationBuild": False
|
|
}
|
|
),
|
|
ConfigInfo(
|
|
name="benchmark",
|
|
debug=False,
|
|
kernel_options={
|
|
"KernelArmExportPMUUser": True,
|
|
"KernelDebugBuild": False,
|
|
"KernelVerificationBuild": False,
|
|
"KernelBenchmarks": "track_utilisation"
|
|
},
|
|
),
|
|
)
|
|
|
|
|
|
EXAMPLES = {
|
|
"hello": Path("example/hello"),
|
|
"ethernet": Path("example/ethernet"),
|
|
"passive_server": Path("example/passive_server"),
|
|
"hierarchy": Path("example/hierarchy"),
|
|
"timer": Path("example/timer"),
|
|
}
|
|
|
|
|
|
def tar_filter(tarinfo: TarInfo) -> TarInfo:
|
|
"""This is used to change the tarinfo when created the .tar.gz archive.
|
|
|
|
This ensures the tar file does not leak information from the build environment.
|
|
"""
|
|
# Force uid/gid
|
|
tarinfo.uid = tarinfo.gid = 0
|
|
tarinfo.uname = tarinfo.gname = "microkit"
|
|
# This is unlikely to be set, but force it anyway
|
|
tarinfo.pax_headers = {}
|
|
tarinfo.mtime = MICROKIT_EPOCH
|
|
assert tarinfo.isfile() or tarinfo.isdir()
|
|
# Set the permissions properly
|
|
if tarinfo.isdir():
|
|
tarinfo.mode = tarinfo.mode & ~0o777 | 0o744
|
|
if tarinfo.isfile():
|
|
if "/bin/" in tarinfo.name:
|
|
# Assume everything in bin should be executable.
|
|
tarinfo.mode = tarinfo.mode & ~0o777 | 0o755
|
|
else:
|
|
tarinfo.mode = tarinfo.mode & ~0o777 | 0o644
|
|
return tarinfo
|
|
|
|
|
|
def get_tool_target_triple() -> str:
|
|
host_system = host_platform.system()
|
|
if host_system == "Linux":
|
|
host_arch = host_platform.machine()
|
|
if host_arch == "x86_64":
|
|
return "x86_64-unknown-linux-musl"
|
|
elif host_arch == "aarch64":
|
|
return "aarch64-unknown-linux-musl"
|
|
else:
|
|
raise Exception(f"Unexpected Linux architecture: {host_arch}")
|
|
elif host_system == "Darwin":
|
|
host_arch = host_platform.machine()
|
|
if host_arch == "x86_64":
|
|
return "x86_64-apple-darwin"
|
|
elif host_arch == "arm64":
|
|
return "aarch64-apple-darwin"
|
|
else:
|
|
raise Exception(f"Unexpected Darwin architecture: {host_arch}")
|
|
else:
|
|
raise Exception(f"The platform \"{host_system}\" is not supported")
|
|
|
|
|
|
def test_tool() -> None:
|
|
r = system(
|
|
f"cd tool/microkit && cargo test"
|
|
)
|
|
assert r == 0
|
|
|
|
|
|
def build_tool(tool_target: Path, target_triple: str) -> None:
|
|
r = system(
|
|
f"cd tool/microkit && cargo build --release --target {target_triple}"
|
|
)
|
|
assert r == 0
|
|
|
|
tool_output = f"./tool/microkit/target/{target_triple}/release/microkit"
|
|
|
|
copy(tool_output, tool_target)
|
|
|
|
tool_target.chmod(0o755)
|
|
|
|
|
|
def build_sel4(
|
|
sel4_dir: Path,
|
|
root_dir: Path,
|
|
build_dir: Path,
|
|
board: BoardInfo,
|
|
config: ConfigInfo,
|
|
llvm: bool
|
|
) -> Dict[str, Any]:
|
|
"""Build seL4"""
|
|
build_dir = build_dir / board.name / config.name / "sel4"
|
|
build_dir.mkdir(exist_ok=True, parents=True)
|
|
|
|
sel4_install_dir = build_dir / "install"
|
|
sel4_build_dir = build_dir / "build"
|
|
|
|
sel4_install_dir.mkdir(exist_ok=True, parents=True)
|
|
sel4_build_dir.mkdir(exist_ok=True, parents=True)
|
|
|
|
print(f"Building sel4: {sel4_dir=} {root_dir=} {build_dir=} {board=} {config=}")
|
|
|
|
config_args = [
|
|
*board.kernel_options.items(),
|
|
*config.kernel_options.items(),
|
|
board.arch.as_kernel_arch_config(),
|
|
]
|
|
config_strs = []
|
|
for arg, val in sorted(config_args):
|
|
if isinstance(val, bool):
|
|
str_val = "ON" if val else "OFF"
|
|
else:
|
|
str_val = str(val)
|
|
s = f"-D{arg}={str_val}"
|
|
config_strs.append(s)
|
|
config_str = " ".join(config_strs)
|
|
|
|
target_triple = f"{board.arch.target_triple()}"
|
|
|
|
cmd = (
|
|
f"cmake -GNinja -DCMAKE_INSTALL_PREFIX={sel4_install_dir.absolute()} "
|
|
f" -DPYTHON3={executable} "
|
|
f" {config_str} "
|
|
f"-S {sel4_dir.absolute()} -B {sel4_build_dir.absolute()}")
|
|
|
|
if llvm:
|
|
cmd += f" -DTRIPLE={target_triple}"
|
|
else:
|
|
cmd += f" -DCROSS_COMPILER_PREFIX={target_triple}-"
|
|
|
|
r = system(cmd)
|
|
if r != 0:
|
|
raise Exception(f"Error configuring sel4: cmd={cmd}")
|
|
|
|
cmd = f"cmake --build {sel4_build_dir.absolute()}"
|
|
r = system(cmd)
|
|
if r != 0:
|
|
raise Exception(f"Error building sel4: cmd={cmd}")
|
|
|
|
cmd = f"cmake --install {sel4_build_dir.absolute()}"
|
|
r = system(cmd)
|
|
if r != 0:
|
|
raise Exception(f"Error installing sel4: cmd={cmd}")
|
|
|
|
elf = sel4_install_dir / "bin" / "kernel.elf"
|
|
dest = (
|
|
root_dir / "board" / board.name / config.name / "elf" / "sel4.elf"
|
|
)
|
|
dest.unlink(missing_ok=True)
|
|
copy(elf, dest)
|
|
# Make output read-only
|
|
dest.chmod(0o744)
|
|
|
|
invocations_all = sel4_build_dir / "generated" / "invocations_all.json"
|
|
dest = (root_dir / "board" / board.name / config.name / "invocations_all.json")
|
|
dest.unlink(missing_ok=True)
|
|
copy(invocations_all, dest)
|
|
dest.chmod(0o744)
|
|
|
|
include_dir = root_dir / "board" / board.name / config.name / "include"
|
|
for source in ("kernel_Config", "libsel4", "libsel4/sel4_Config", "libsel4/autoconf"):
|
|
source_dir = sel4_install_dir / source / "include"
|
|
for p in source_dir.rglob("*"):
|
|
if not p.is_file():
|
|
continue
|
|
rel = p.relative_to(source_dir)
|
|
dest = include_dir / rel
|
|
dest.parent.mkdir(exist_ok=True, parents=True)
|
|
dest.unlink(missing_ok=True)
|
|
copy(p, dest)
|
|
dest.chmod(0o744)
|
|
|
|
platform_gen = sel4_build_dir / "gen_headers" / "plat" / "machine" / "platform_gen.json"
|
|
dest = root_dir / "board" / board.name / config.name / "platform_gen.json"
|
|
dest.unlink(missing_ok=True)
|
|
copy(platform_gen, dest)
|
|
dest.chmod(0o744)
|
|
|
|
gen_config_path = sel4_install_dir / "libsel4/include/kernel/gen_config.json"
|
|
with open(gen_config_path, "r") as f:
|
|
gen_config = json.load(f)
|
|
return gen_config
|
|
|
|
|
|
def build_elf_component(
|
|
component_name: str,
|
|
root_dir: Path,
|
|
build_dir: Path,
|
|
board: BoardInfo,
|
|
config: ConfigInfo,
|
|
llvm: bool,
|
|
defines: List[Tuple[str, str]],
|
|
) -> None:
|
|
"""Build a specific ELF component.
|
|
|
|
Right now this is either the loader or the monitor
|
|
"""
|
|
sel4_dir = root_dir / "board" / board.name / config.name
|
|
build_dir = build_dir / board.name / config.name / component_name
|
|
build_dir.mkdir(exist_ok=True, parents=True)
|
|
target_triple = f"{board.arch.target_triple()}"
|
|
defines_str = " ".join(f"{k}={v}" for k, v in defines)
|
|
defines_str += f" ARCH={board.arch.to_str()} BOARD={board.name} BUILD_DIR={build_dir.absolute()} SEL4_SDK={sel4_dir.absolute()} TARGET_TRIPLE={target_triple} LLVM={llvm}"
|
|
|
|
if board.gcc_cpu is not None:
|
|
defines_str += f" GCC_CPU={board.gcc_cpu}"
|
|
|
|
r = system(
|
|
f"{defines_str} make -C {component_name}"
|
|
)
|
|
if r != 0:
|
|
raise Exception(
|
|
f"Error building: {component_name} for board: {board.name} config: {config.name}"
|
|
)
|
|
elf = build_dir / f"{component_name}.elf"
|
|
dest = (
|
|
root_dir / "board" / board.name / config.name / "elf" / f"{component_name}.elf"
|
|
)
|
|
dest.unlink(missing_ok=True)
|
|
copy(elf, dest)
|
|
# Make output read-only
|
|
dest.chmod(0o744)
|
|
|
|
|
|
def build_doc(root_dir: Path):
|
|
output = root_dir / "doc" / "microkit_user_manual.pdf"
|
|
|
|
environ["TEXINPUTS"] = "style:"
|
|
r = system(f'cd docs && pandoc manual.md -o ../{output}')
|
|
assert r == 0
|
|
|
|
|
|
def build_lib_component(
|
|
component_name: str,
|
|
root_dir: Path,
|
|
build_dir: Path,
|
|
board: BoardInfo,
|
|
config: ConfigInfo,
|
|
llvm: bool
|
|
) -> None:
|
|
"""Build a specific library component.
|
|
|
|
Right now this is just libmicrokit.a
|
|
"""
|
|
sel4_dir = root_dir / "board" / board.name / config.name
|
|
build_dir = build_dir / board.name / config.name / component_name
|
|
build_dir.mkdir(exist_ok=True, parents=True)
|
|
|
|
target_triple = f"{board.arch.target_triple()}"
|
|
defines_str = f" ARCH={board.arch.to_str()} BUILD_DIR={build_dir.absolute()} SEL4_SDK={sel4_dir.absolute()} TARGET_TRIPLE={target_triple} LLVM={llvm}"
|
|
|
|
if board.gcc_cpu is not None:
|
|
defines_str += f" GCC_CPU={board.gcc_cpu}"
|
|
|
|
r = system(
|
|
f"{defines_str} make -C {component_name}"
|
|
)
|
|
if r != 0:
|
|
raise Exception(
|
|
f"Error building: {component_name} for board: {board.name} config: {config.name}"
|
|
)
|
|
lib = build_dir / f"{component_name}.a"
|
|
lib_dir = root_dir / "board" / board.name / config.name / "lib"
|
|
dest = lib_dir / f"{component_name}.a"
|
|
dest.unlink(missing_ok=True)
|
|
copy(lib, dest)
|
|
# Make output read-only
|
|
dest.chmod(0o744)
|
|
|
|
link_script = Path(component_name) / "microkit.ld"
|
|
dest = lib_dir / "microkit.ld"
|
|
dest.unlink(missing_ok=True)
|
|
copy(link_script, dest)
|
|
# Make output read-only
|
|
dest.chmod(0o744)
|
|
|
|
include_dir = root_dir / "board" / board.name / config.name / "include"
|
|
source_dir = Path(component_name) / "include"
|
|
for p in source_dir.rglob("*"):
|
|
if not p.is_file():
|
|
continue
|
|
rel = p.relative_to(source_dir)
|
|
dest = include_dir / rel
|
|
dest.parent.mkdir(exist_ok=True, parents=True)
|
|
dest.unlink(missing_ok=True)
|
|
copy(p, dest)
|
|
dest.chmod(0o744)
|
|
|
|
|
|
def main() -> None:
|
|
parser = ArgumentParser()
|
|
parser.add_argument("--sel4", type=Path, required=True)
|
|
parser.add_argument("--tool-target-triple", default=get_tool_target_triple(), help="Compile the Microkit tool for this target triple")
|
|
parser.add_argument("--llvm", action="store_true", help="Cross-compile seL4 and Microkit's run-time targets with LLVM")
|
|
parser.add_argument("--boards", metavar="BOARDS", help="Comma-separated list of boards to support. When absent, all boards are supported.")
|
|
parser.add_argument("--configs", metavar="CONFIGS", help="Comma-separated list of configurations to support. When absent, all configurations are supported.")
|
|
parser.add_argument("--skip-tool", action="store_true", help="Tool will not be built")
|
|
parser.add_argument("--skip-sel4", action="store_true", help="seL4 will not be built")
|
|
parser.add_argument("--skip-docs", action="store_true", help="Docs will not be built")
|
|
parser.add_argument("--skip-tar", action="store_true", help="SDK and source tarballs will not be built")
|
|
# Read from the version file as unless someone has specified
|
|
# a version, that is the source of truth
|
|
with open("VERSION", "r") as f:
|
|
default_version = f.read().strip()
|
|
parser.add_argument("--version", default=default_version, help="SDK version")
|
|
for arch in KernelArch:
|
|
arch_str = arch.name.lower()
|
|
parser.add_argument(f"--gcc-toolchain-prefix-{arch_str}", default=arch.target_triple(), help=f"GCC toolchain prefix when compiling for {arch_str}, e.g {arch_str}-none-elf")
|
|
|
|
args = parser.parse_args()
|
|
|
|
global TRIPLE_AARCH64
|
|
global TRIPLE_RISCV
|
|
TRIPLE_AARCH64 = args.gcc_toolchain_prefix_aarch64
|
|
TRIPLE_RISCV = args.gcc_toolchain_prefix_riscv64
|
|
|
|
version = args.version
|
|
|
|
if args.boards is not None:
|
|
supported_board_names = frozenset(board.name for board in SUPPORTED_BOARDS)
|
|
selected_board_names = frozenset(args.boards.split(","))
|
|
for board_name in selected_board_names:
|
|
if board_name not in supported_board_names:
|
|
raise Exception(f"Trying to build a board: {board_name} that does not exist in supported list.")
|
|
selected_boards = [board for board in SUPPORTED_BOARDS if board.name in selected_board_names]
|
|
else:
|
|
selected_boards = SUPPORTED_BOARDS
|
|
|
|
if args.configs is not None:
|
|
supported_config_names = frozenset(config.name for config in SUPPORTED_CONFIGS)
|
|
selected_config_names = frozenset(args.configs.split(","))
|
|
for config_name in selected_config_names:
|
|
if config_name not in supported_config_names:
|
|
raise Exception(f"Trying to build a configuration: {config_name} that does not exist in supported list.")
|
|
selected_configs = [config for config in SUPPORTED_CONFIGS if config.name in selected_config_names]
|
|
else:
|
|
selected_configs = SUPPORTED_CONFIGS
|
|
|
|
sel4_dir = args.sel4.expanduser()
|
|
if not sel4_dir.exists():
|
|
raise Exception(f"sel4_dir: {sel4_dir} does not exist")
|
|
|
|
root_dir = Path("release") / f"{NAME}-sdk-{version}"
|
|
tar_file = Path("release") / f"{NAME}-sdk-{version}.tar.gz"
|
|
source_tar_file = Path("release") / f"{NAME}-source-{version}.tar.gz"
|
|
dir_structure = [
|
|
root_dir / "bin",
|
|
root_dir / "board",
|
|
]
|
|
if not args.skip_docs:
|
|
dir_structure.append(root_dir / "doc")
|
|
for board in selected_boards:
|
|
board_dir = root_dir / "board" / board.name
|
|
dir_structure.append(board_dir)
|
|
for config in selected_configs:
|
|
config_dir = board_dir / config.name
|
|
dir_structure.append(config_dir)
|
|
dir_structure += [
|
|
config_dir / "include",
|
|
config_dir / "lib",
|
|
config_dir / "elf",
|
|
]
|
|
|
|
for dr in dir_structure:
|
|
dr.mkdir(exist_ok=True, parents=True)
|
|
|
|
with open(root_dir / "VERSION", "w+") as f:
|
|
f.write(version + "\n")
|
|
|
|
copy(Path("LICENSE.md"), root_dir)
|
|
licenses_dir = Path("LICENSES")
|
|
licenses_dest_dir = root_dir / "LICENSES"
|
|
for p in licenses_dir.rglob("*"):
|
|
if not p.is_file():
|
|
continue
|
|
rel = p.relative_to(licenses_dir)
|
|
dest = licenses_dest_dir / rel
|
|
dest.parent.mkdir(exist_ok=True, parents=True)
|
|
dest.unlink(missing_ok=True)
|
|
copy(p, dest)
|
|
dest.chmod(0o744)
|
|
|
|
if not args.skip_tool:
|
|
tool_target = root_dir / "bin" / "microkit"
|
|
test_tool()
|
|
build_tool(tool_target, args.tool_target_triple)
|
|
|
|
if not args.skip_docs:
|
|
build_doc(root_dir)
|
|
|
|
build_dir = Path("build")
|
|
for board in selected_boards:
|
|
for config in selected_configs:
|
|
if not args.skip_sel4:
|
|
sel4_gen_config = build_sel4(sel4_dir, root_dir, build_dir, board, config, args.llvm)
|
|
loader_printing = 1 if config.name == "debug" else 0
|
|
loader_defines = [
|
|
("LINK_ADDRESS", hex(board.loader_link_address)),
|
|
("PRINTING", loader_printing)
|
|
]
|
|
# There are some architecture dependent configuration options that the loader
|
|
# needs to know about, so we figure that out here
|
|
if board.arch.is_riscv():
|
|
loader_defines.append(("FIRST_HART_ID", sel4_gen_config["FIRST_HART_ID"]))
|
|
if board.arch.is_arm():
|
|
if sel4_gen_config["ARM_PA_SIZE_BITS_40"]:
|
|
arm_pa_size_bits = 40
|
|
elif sel4_gen_config["ARM_PA_SIZE_BITS_44"]:
|
|
arm_pa_size_bits = 44
|
|
else:
|
|
raise Exception("Unexpected ARM physical address bits defines")
|
|
loader_defines.append(("PHYSICAL_ADDRESS_BITS", arm_pa_size_bits))
|
|
|
|
build_elf_component("loader", root_dir, build_dir, board, config, args.llvm, loader_defines)
|
|
build_elf_component("monitor", root_dir, build_dir, board, config, args.llvm, [])
|
|
build_lib_component("libmicrokit", root_dir, build_dir, board, config, args.llvm)
|
|
|
|
# Setup the examples
|
|
for example, example_path in EXAMPLES.items():
|
|
include_dir = root_dir / "example" / example
|
|
source_dir = example_path
|
|
for p in source_dir.rglob("*"):
|
|
if not p.is_file():
|
|
continue
|
|
rel = p.relative_to(source_dir)
|
|
dest = include_dir / rel
|
|
dest.parent.mkdir(exist_ok=True, parents=True)
|
|
dest.unlink(missing_ok=True)
|
|
copy(p, dest)
|
|
dest.chmod(0o744)
|
|
|
|
if not args.skip_tar:
|
|
# At this point we create a tar.gz file
|
|
with tar_open(tar_file, "w:gz") as tar:
|
|
tar.add(root_dir, arcname=root_dir.name, filter=tar_filter)
|
|
|
|
# Build the source tar
|
|
process = popen("git ls-files")
|
|
filenames = [Path(fn.strip()) for fn in process.readlines()]
|
|
process.close()
|
|
source_prefix = Path(f"{NAME}-source-{version}")
|
|
with tar_open(source_tar_file, "w:gz") as tar:
|
|
for filename in filenames:
|
|
tar.add(filename, arcname=source_prefix / filename, filter=tar_filter)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|