Skip to content

WIP: CHERI Standard support #419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 18 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c1a4b05
[zcheri] Initial support for the RISC-V CHERI standard
arichardson Apr 22, 2025
648dda8
[zcheri] Honor riscv_cheri_isa configuration flag for CheriBSD builds.
qwattash May 26, 2025
214485f
[zcheri] Add Cheri Alliance OpenSBI target with Zcheri support.
qwattash Aug 8, 2025
9cb5bf0
[zcheri] Default OpenSBI start to 0x80000000
qwattash Aug 8, 2025
56fa427
[zcheri] Added gfe build for cheri alliance opensbi
francislaus May 29, 2025
674df00
[zcheri] Add CHERI Alliance SDK (QEMU and LLVM)
heshamelmatary Jun 23, 2025
4e18945
[zcheri] Use CHERI Alliance SDK for CheriBSD targets.
qwattash Jun 27, 2025
0262d09
[zcheri] Add GDB target off CHERI Alliance
heshamelmatary Jun 26, 2025
83a52b5
[zcheri] Obey --riscv-cheri-isa in run-riscv64-xxx.
qwattash Jun 30, 2025
af5f307
[zcheri] Honor --riscv-cheri-isa for baremetal targets.
qwattash Jul 1, 2025
fae3ba9
[zcheri] Add Cheri Alliance gdb for purecap rootfs target.
qwattash Jul 3, 2025
4fa1f28
[zcheri] Disable -mrelax for Zcheri ISA.
qwattash Jul 3, 2025
df3777e
[zcheri] Fix sysroot path to alliance sdk
heshamelmatary Jul 25, 2025
31abb57
combine-files: Make sure opensbi.py precedes other projects
heshamelmatary Jul 25, 2025
639be27
[zcheri] Fix llvm_test_suite llvm_project dependency resolution.
qwattash Jul 28, 2025
ee6c8fe
[zcheri] Drop unused `unaligned` option from BuildCheriAllianceQEMU.
qwattash Jul 30, 2025
953e90e
[qemu] Bump tracked branch in cheri-alliance-qemu.
qwattash Aug 1, 2025
5932aa2
[WIP] DO NOT MERGE Point the cheri-alliance-llvm target to codasip-re…
qwattash Aug 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions combine-files.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ def check_all_files_used(directory: Path):
add_filtered_file(script_dir / "projects/cross/crosscompileproject.py")
add_filtered_file(script_dir / "projects/cross/benchmark_mixin.py")
add_filtered_file(script_dir / "projects/cross/llvm_test_suite.py")
add_filtered_file(script_dir / "projects/cross/opensbi.py")
# disk-image, sdk and run_qemu must come after cheribsd as they use CheriBSD.rootfs_dir
add_filtered_file(script_dir / "projects/disk_image.py")
add_filtered_file(script_dir / "projects/run_qemu.py")
Expand Down
10 changes: 8 additions & 2 deletions pycheribuild/boot_cheribsd/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from typing import Callable, Optional, Union

from ..colour import AnsiColour, coloured
from ..config.chericonfig import RiscvCheriISA
from ..config.compilation_targets import CompilationTargets, CrossCompileTarget
from ..processutils import commandline_to_str, keep_terminal_sane, run_and_kill_children_on_exit
from ..qemu_utils import QemuOptions, riscv_bios_arguments
Expand Down Expand Up @@ -916,7 +917,7 @@ def boot_cheribsd(
if bios_path is not None:
bios_args = ["-bios", str(bios_path)]
elif qemu_options.xtarget.is_riscv(include_purecap=True):
bios_args = riscv_bios_arguments(qemu_options.xtarget, None)
bios_args = riscv_bios_arguments(qemu_options.xtarget, qemu_options.riscv_cheri_isa)
else:
bios_args = []
qemu_args = qemu_options.get_commandline(
Expand Down Expand Up @@ -1516,7 +1517,12 @@ def _main(
if argparse_adjust_args_callback:
argparse_adjust_args_callback(args)

qemu_options = QemuOptions(xtarget)
riscv_cheri_isa = None
# Slightly ugly hack to avoid having to pass yet another argument for something
# that is a temporary workaround until ISAv9 is gone.
if args.qemu_cmd is not None and str(args.qemu_cmd).endswith("cheristd"):
riscv_cheri_isa = RiscvCheriISA.STD
qemu_options = QemuOptions(xtarget, riscv_cheri_isa=riscv_cheri_isa)
if args.qemu_cmd is not None:
if not Path(args.qemu_cmd).exists():
failure("ERROR: Cannot find QEMU binary ", args.qemu_cmd, " doesn't exist", exit=True)
Expand Down
25 changes: 25 additions & 0 deletions pycheribuild/config/chericonfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ def clang_march_flag(self) -> str:
return self.value[1]


class RiscvCheriISA(Enum):
V9 = "v9"
STD = "std"


class RiscvFloatAbi(Enum):
SOFT = "soft"
HARD = "hard"
Expand Down Expand Up @@ -319,6 +324,13 @@ def __init__(self, loader, action_class: "type[CheribuildActionEnum]") -> None:
self.skip_configure: "Optional[bool] " = None
self.force_configure: "Optional[bool] " = None
self.force_update: "Optional[bool] " = None
self.riscv_cheri_isa = loader.add_option(
"riscv-cheri-isa",
default=RiscvCheriISA.V9,
type=RiscvCheriISA,
group=loader.cross_compile_options_group,
help="The CHERI ISA to target for RISC-V code",
)
self.mips_float_abi = loader.add_option(
"mips-float-abi",
default=MipsFloatAbi.SOFT,
Expand Down Expand Up @@ -455,6 +467,7 @@ def __init__(self, loader, action_class: "type[CheribuildActionEnum]") -> None:
self.cheribsd_image_root: Optional[Path] = None
self.cheri_sdk_dir: Optional[Path] = None
self.morello_sdk_dir: Optional[Path] = None
self.cheri_alliance_sdk_dir: Optional[Path] = None
self.other_tools_dir: Optional[Path] = None
self.sysroot_output_root: Optional[Path] = None
self.docker = loader.add_bool_option(
Expand Down Expand Up @@ -737,6 +750,10 @@ def default_cheri_sdk_directory_name(self) -> str:
def default_morello_sdk_directory_name(self) -> str:
return "morello-sdk"

@property
def default_cheri_alliance_sdk_directory_name(self) -> str:
return "cheri-alliance-sdk"

@property
def cheri_sdk_bindir(self):
return self.cheri_sdk_dir / "bin"
Expand All @@ -745,6 +762,14 @@ def cheri_sdk_bindir(self):
def morello_sdk_bindir(self):
return self.morello_sdk_dir / "bin"

@property
def cheri_alliance_sdk_bindir(self):
return self.cheri_alliance_sdk_dir / "bin"

@property
def cheri_alliance_qemu_bindir(self):
return self.cheri_alliance_sdk_bindir

@property
def qemu_bindir(self):
return self.cheri_sdk_bindir
Expand Down
67 changes: 44 additions & 23 deletions pycheribuild/config/compilation_targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
from pathlib import Path
from typing import Optional

from .chericonfig import CheriConfig
from .chericonfig import CheriConfig, RiscvCheriISA
from .config_loader_base import ConfigLoaderBase, ConfigOptionHandle
from .target_info import (
AArch64FloatSimdOptions,
Expand Down Expand Up @@ -84,8 +84,12 @@ def get_qemu_mfs_root_kernel(self, use_benchmark_kernel: bool) -> Path:

@functools.lru_cache(maxsize=20)
def _linker_supports_riscv_relaxations(linker: Path, config: CheriConfig, xtarget: "CrossCompileTarget") -> bool:
if xtarget.is_hybrid_or_purecap_cheri():
# XXX-AM: Hack: codasip lld does not seem to play nice with -mrelax and cheri
if config.riscv_cheri_isa == RiscvCheriISA.STD:
return False
elif xtarget.is_hybrid_or_purecap_cheri():
return False

try:
linker_version = get_version_output(linker, config=config)
except subprocess.CalledProcessError as e:
Expand Down Expand Up @@ -118,15 +122,17 @@ def sdk_root_dir(self) -> Path:
return self._sdk_root_dir

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
raise NotImplementedError()

def _get_sdk_root_dir_lazy(self) -> Path:
return self._get_compiler_project().get_native_install_path(self.config)
return self._get_compiler_project(self.config).get_native_install_path(self.config)

@classmethod
def toolchain_targets(cls, target: "CrossCompileTarget", config: "CheriConfig") -> "list[str]":
return [cls._get_compiler_project().get_class_for_target(BasicCompilationTargets.NATIVE_NON_PURECAP).target]
return [
cls._get_compiler_project(config).get_class_for_target(BasicCompilationTargets.NATIVE_NON_PURECAP).target
]

def _rootfs_path(self) -> Path:
xtarget = self.target.get_rootfs_target()
Expand Down Expand Up @@ -277,7 +283,7 @@ def essential_compiler_and_linker_flags_impl(
result.append("-mcpu=beri")
elif xtarget.is_riscv(include_purecap=True):
# Use the insane RISC-V arch string to enable CHERI
result.append("-march=" + cls.get_riscv_arch_string(xtarget, softfloat=softfloat))
result.append("-march=" + cls.get_riscv_arch_string(xtarget, config, softfloat=softfloat))
result.append("-mabi=" + cls.get_riscv_abi(xtarget, softfloat=softfloat))
result.append(
"-mrelax" if _linker_supports_riscv_relaxations(instance.linker, config, xtarget) else "-mno-relax"
Expand Down Expand Up @@ -313,15 +319,19 @@ def essential_compiler_and_linker_flags_impl(
return result

@classmethod
def get_riscv_arch_string(cls, xtarget: CrossCompileTarget, softfloat: bool) -> str:
def get_riscv_arch_string(cls, xtarget: CrossCompileTarget, config: CheriConfig, softfloat: bool) -> str:
assert xtarget.is_riscv(include_purecap=True)
# Use the insane RISC-V arch string to enable CHERI
arch_string = "rv" + str(xtarget.cpu_architecture.word_bits()) + "ima"
if not softfloat:
arch_string += "fd"
arch_string += "c"
if xtarget.is_hybrid_or_purecap_cheri():
arch_string += "xcheri"
if config.riscv_cheri_isa == RiscvCheriISA.V9:
arch_string += "xcheri"
else:
assert config.riscv_cheri_isa == RiscvCheriISA.STD
arch_string += "zcherihybrid"
return arch_string

@classmethod
Expand Down Expand Up @@ -366,7 +376,7 @@ def _get_sdk_root_dir_lazy(self) -> Path:
# If we couldn't find a working system compiler, default to cheribuild-compiled upstream LLVM.
assert fbsd.build_toolchain == CompilerType.DEFAULT_COMPILER
# noinspection PyUnresolvedReferences
return self._get_compiler_project().get_native_install_path(self.config)
return self._get_compiler_project(self.config).get_native_install_path(self.config)
return configured_path

@property
Expand Down Expand Up @@ -464,7 +474,7 @@ def localbase(self) -> Path:
return Path("usr/local")

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("upstream-llvm", None))

def _get_rootfs_class(self, xtarget: "CrossCompileTarget") -> "type[SimpleProject]":
Expand Down Expand Up @@ -502,7 +512,7 @@ def has_test_extra_arg_override(arg: str):
rootfs_xtarget = xtarget.get_rootfs_target()
from ..qemu_utils import QemuOptions

qemu_options = QemuOptions(rootfs_xtarget)
qemu_options = QemuOptions(rootfs_xtarget, riscv_cheri_isa=self.config.riscv_cheri_isa)
run_instance: LaunchFreeBSDInterface = self._get_run_project(rootfs_xtarget, self.project)
if rootfs_xtarget.cpu_architecture not in (
CPUArchitecture.MIPS64,
Expand Down Expand Up @@ -635,8 +645,12 @@ class CheriBSDTargetInfo(FreeBSDTargetInfo):
FREEBSD_VERSION: int = 13

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("llvm", None))
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
if config.riscv_cheri_isa == RiscvCheriISA.STD:
llvm_target = SimpleProject.get_class_for_target_name("cheri-alliance-llvm", None)
else:
llvm_target = SimpleProject.get_class_for_target_name("llvm", None)
return typing.cast("type[BuildLLVMInterface]", llvm_target)

def _get_run_project(self, xtarget: "CrossCompileTarget", caller: SimpleProject) -> LaunchFreeBSDInterface:
result = SimpleProject.get_instance_for_target_name("run", xtarget, caller.config, caller)
Expand Down Expand Up @@ -703,7 +717,7 @@ class CheriBSDMorelloTargetInfo(CheriBSDTargetInfo):
uses_morello_llvm: bool = True

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("morello-llvm", None))

@classmethod
Expand Down Expand Up @@ -739,10 +753,10 @@ def _get_rootfs_class(self, xtarget: "CrossCompileTarget") -> "type[SimpleProjec
raise LookupError("Should not be called")

def _get_sdk_root_dir_lazy(self) -> Path:
return self._get_compiler_project().get_native_install_path(self.config)
return self._get_compiler_project(self.config).get_native_install_path(self.config)

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("cherios-llvm", None))

@property
Expand Down Expand Up @@ -809,7 +823,7 @@ def sysroot_install_prefix_relative(self) -> Path:
return Path(self.target_triple)

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("llvm", None))

@property
Expand Down Expand Up @@ -861,7 +875,7 @@ def sysroot_dir(self) -> Path:
return sysroot_dir / "baremetal" / suffix

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("llvm", None))

@classmethod
Expand Down Expand Up @@ -947,7 +961,7 @@ def sysroot_dir(self) -> Path:
return result

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("llvm", None))

@property
Expand Down Expand Up @@ -1021,8 +1035,12 @@ class BaremetalFreestandingTargetInfo(BaremetalClangTargetInfo):
os_prefix: str = "baremetal-"

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("llvm", None))
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
if config.riscv_cheri_isa == RiscvCheriISA.STD:
llvm_target = SimpleProject.get_class_for_target_name("cheri-alliance-llvm", None)
else:
llvm_target = SimpleProject.get_class_for_target_name("llvm", None)
return typing.cast("type[BuildLLVMInterface]", llvm_target)

@classmethod
def base_sysroot_targets(cls, target: "CrossCompileTarget", config: "CheriConfig") -> "list[str]":
Expand All @@ -1036,7 +1054,10 @@ def triple_for_target(cls, target: "CrossCompileTarget", config: "CheriConfig",

@property
def sysroot_dir(self) -> Path:
sysroot_dir = self.config.sysroot_output_root / self.config.default_cheri_sdk_directory_name
if self.config.riscv_cheri_isa == RiscvCheriISA.STD:
sysroot_dir = self.config.sysroot_output_root / self.config.default_cheri_alliance_sdk_directory_name
else:
sysroot_dir = self.config.sysroot_output_root / self.config.default_cheri_sdk_directory_name
return sysroot_dir / "baremetal" / self.target.get_rootfs_target().generic_arch_suffix


Expand All @@ -1046,7 +1067,7 @@ class MorelloBaremetalTargetInfo(BaremetalFreestandingTargetInfo):
uses_morello_llvm: bool = True

@classmethod
def _get_compiler_project(cls) -> "type[BuildLLVMInterface]":
def _get_compiler_project(cls, config: CheriConfig) -> "type[BuildLLVMInterface]":
return typing.cast("type[BuildLLVMInterface]", SimpleProject.get_class_for_target_name("morello-llvm", None))

@property
Expand Down
10 changes: 10 additions & 0 deletions pycheribuild/config/defaultconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,16 @@ def __init__(self, loader: ConfigLoaderBase, available_targets: "list[str]") ->
group=loader.path_group,
help="The directory to find/install the Morello SDK",
)
default_cheri_alliance_sdk = ComputedDefaultValue(
function=lambda p, cls: (p.tools_root / p.default_cheri_alliance_sdk_directory_name),
as_string="'<TOOLS_ROOT>/cheri-alliance-sdk'",
)
self.cheri_alliance_sdk_dir = loader.add_path_option(
"cheri-alliance-sdk-root",
default=default_cheri_alliance_sdk,
group=loader.path_group,
help="The directory to find/install the CHERI Alliance SDK",
)
self.sysroot_output_root = loader.add_path_option(
"sysroot-install-root",
shortname="-sysroot-install-dir",
Expand Down
13 changes: 13 additions & 0 deletions pycheribuild/config/jenkinsconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ def __init__(self, loader: ConfigLoaderBase, available_targets: "list[str]") ->
type=Path,
help="Override the path to the CHERI SDK (default is $WORKSPACE/cherisdk)",
)
self._cheri_alliance_sdk_dir_override = loader.add_commandline_only_option(
"cheri-alliance-sdk-path",
default=None,
type=Path,
help="Override the path to the CHERI Alliance SDK (default is $WORKSPACE/cheri-alliance-sdk)",
)
self._morello_sdk_dir_override = loader.add_commandline_only_option(
"morello-sdk-path",
default=None,
Expand Down Expand Up @@ -293,6 +299,13 @@ def load(self) -> None:
else:
self.cheri_sdk_dir = self.workspace / self.default_cheri_sdk_directory_name

if self._cheri_alliance_sdk_dir_override is not None:
self.cheri_alliance_sdk_dir = self._cheri_alliance_sdk_dir_override
elif Path("/cheri-alliance-sdk/bin/clang").exists(): # check for docker image
self.cheri_alliance_sdk_dir = Path("/cheri-alliance-sdk")
else:
self.cheri_alliance_sdk_dir = self.workspace / self.default_cheri_alliance_sdk_directory_name

if self._morello_sdk_dir_override is not None:
self.morello_sdk_dir = self._morello_sdk_dir_override
elif Path("/morello-sdk/bin/clang").exists(): # check for docker image
Expand Down
4 changes: 4 additions & 0 deletions pycheribuild/config/target_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class CompilerType(Enum):
DEFAULT_COMPILER = "default-compiler" # Default system compiler (i.e. the argument passed to cheribuild)
CHERI_LLVM = "cheri-llvm" # Compile with CHERI LLVM built by cheribuild
MORELLO_LLVM = "morello-llvm" # Compile with Morello LLVM built by cheribuild
CHERI_ALLIANCE_LLVM = "cheri-alliance-llvm" # Compile with CHERI Alliance LLVM built by cheribuild
UPSTREAM_LLVM = "upstream-llvm" # Compile with upstream LLVM built by cheribuild
SYSTEM_LLVM = "system-llvm" # Compile with system installation of LLVM/Clang
BOOTSTRAPPED = "bootstrap" # Compiler is included with the project
Expand Down Expand Up @@ -149,6 +150,7 @@ class DefaultInstallDir(Enum):
KDE_PREFIX = "The sysroot for this target (<rootfs>/opt/<arch>/kde by default)"
CHERI_SDK = "The CHERI SDK directory"
MORELLO_SDK = "The Morello SDK directory"
CHERI_ALLIANCE_SDK = "The CHERI Alliance SDK directory"
BOOTSTRAP_TOOLS = "The bootstap tools directory"
CUSTOM_INSTALL_DIR = "Custom install directory"
SYSROOT_FOR_BAREMETAL_ROOTFS_OTHERWISE = "Sysroot for baremetal projects, rootfs otherwise"
Expand Down Expand Up @@ -599,6 +601,8 @@ def default_install_dir(self, install_dir: DefaultInstallDir) -> Path:
return config.cheri_sdk_dir
elif install_dir == DefaultInstallDir.MORELLO_SDK:
return config.morello_sdk_dir
elif install_dir == DefaultInstallDir.CHERI_ALLIANCE_SDK:
return config.cheri_alliance_sdk_dir
elif install_dir == DefaultInstallDir.BOOTSTRAP_TOOLS:
return config.other_tools_dir
return super().default_install_dir(install_dir)
Expand Down
Loading
Loading