From 9597ad68b8bbdd0cfbdadea1496315d80167d879 Mon Sep 17 00:00:00 2001 From: rezado <1908819406@qq.com> Date: Mon, 31 Aug 2026 16:18:31 +0800 Subject: [PATCH] feat(linux): derive firmware addresses from selected DTS --- Makefile | 19 +++- README.md | 26 ++--- bootloader/LibCheckpointAlpha | 2 +- dts/README.md | 24 +++-- scripts/build-firmware-linux.sh | 123 ++++++++++-------------- scripts/build-gcpt.sh | 35 +------ scripts/build-sbi.sh | 18 +++- scripts/dts-config.sh | 67 +++++++++++++ scripts/export-linux-debug-artifacts.sh | 35 ++++--- workloads/linux/gapbs/rules.mk | 9 +- workloads/linux/geekbench-common.mk | 7 +- workloads/linux/spec2006/rules.mk | 6 +- workloads/linux/spec2017/rules.mk | 6 +- workloads/linux/spec2026/rules.mk | 6 +- 14 files changed, 226 insertions(+), 157 deletions(-) create mode 100755 scripts/dts-config.sh diff --git a/Makefile b/Makefile index efb45be..6619f20 100644 --- a/Makefile +++ b/Makefile @@ -52,15 +52,17 @@ GCPT_SOURCE_DIR := $(if $(filter 1,$(MULTIHART)),bootloader/LibCheckpoint,bootlo GCPT_BUILD_DIR := $(if $(filter 1,$(MULTIHART)),build/LibCheckpoint,build/LibCheckpointAlpha) GCPT_BIN := $(GCPT_BUILD_DIR)/build/gcpt.bin GCPT_DEFAULT_DTB ?= $(if $(DEFAULT_DTB),$(DEFAULT_DTB),xiangshan) +GCPT_DEFAULT_DTS := dts/$(GCPT_DEFAULT_DTB).dts.in GCPT_CONFIGURE_MODE := $(if $(filter 1,$(MULTIHART)),dual_core,normal) -GCPT_CONFIG_STAMP := $(if $(filter 1,$(MULTIHART)),build/LibCheckpoint-config/mode.$(GCPT_CONFIGURE_MODE),build/LibCheckpointAlpha-config/dtb.$(shell printf '%s\n' "$(GCPT_DEFAULT_DTB)" | sha256sum | cut -d ' ' -f 1)) +GCPT_DTB_CONFIG_HASH := $(shell printf '%s\n' "$(GCPT_DEFAULT_DTB)" | sha256sum | cut -d ' ' -f 1) +GCPT_CONFIG_STAMP := $(if $(filter 1,$(MULTIHART)),build/LibCheckpoint-config/mode.$(GCPT_CONFIGURE_MODE),build/LibCheckpointAlpha-config/dtb.$(GCPT_DTB_CONFIG_HASH)) GCPT_SOURCES := $(if $(filter 1,$(MULTIHART)),$(shell find $(GCPT_SOURCE_DIR) -path '*/.git' -prune -o -path '*/tests' -prune -o -type f -print 2>/dev/null),$(shell find $(GCPT_SOURCE_DIR) -path '*/.git' -prune -o -type f -print 2>/dev/null)) -GCPT_DTS_SOURCES := $(if $(filter 1,$(MULTIHART)),,$(shell find dts -type f 2>/dev/null)) +GCPT_DTS_SOURCES := $(if $(filter 1,$(MULTIHART)),,$(GCPT_DEFAULT_DTS)) $(GCPT_CONFIG_STAMP): mkdir -p "$(@D)" rm -f $(if $(filter 1,$(MULTIHART)),build/LibCheckpoint-config/mode.*,build/LibCheckpointAlpha-config/dtb.*) touch "$@" -$(GCPT_BIN): scripts/build-gcpt.sh $(TOOLCHAIN_WRAPPER) $(GCPT_SOURCES) $(GCPT_DTS_SOURCES) $(GCPT_CONFIG_STAMP) $(if $(filter 1,$(MULTIHART)),$(SBI_BIN),) +$(GCPT_BIN): scripts/build-gcpt.sh scripts/dts-config.sh $(TOOLCHAIN_WRAPPER) $(GCPT_SOURCES) $(GCPT_DTS_SOURCES) $(GCPT_CONFIG_STAMP) $(if $(filter 1,$(MULTIHART)),$(SBI_BIN),) CROSS_COMPILE="$(abspath $(BUILDROOT_DIR)/output/host/bin)/riscv64-linux-" \ GCPT_IMPLEMENTATION="$(GCPT_IMPLEMENTATION)" \ GCPT_CONFIGURE_MODE="$(GCPT_CONFIGURE_MODE)" \ @@ -70,9 +72,16 @@ $(GCPT_BIN): scripts/build-gcpt.sh $(TOOLCHAIN_WRAPPER) $(GCPT_SOURCES) $(GCPT_D bash scripts/build-gcpt.sh $(GCPT_SOURCE_DIR) $(GCPT_BUILD_DIR) # Build OpenSBI -$(SBI_BIN): scripts/build-sbi.sh bootloader/opensbi.config $(TOOLCHAIN_WRAPPER) +SBI_CONFIG_STAMP := $(SBI_BUILD_DIR)-config/$(if $(filter 1,$(MULTIHART)),multihart-fixed,dtb.$(GCPT_DTB_CONFIG_HASH)) +$(SBI_CONFIG_STAMP): + mkdir -p "$(@D)" + rm -f "$(@D)"/dtb.* "$(@D)"/multihart-fixed + touch "$@" +$(SBI_BIN): scripts/build-sbi.sh scripts/dts-config.sh bootloader/opensbi.config $(TOOLCHAIN_WRAPPER) $(if $(filter 1,$(MULTIHART)),,$(GCPT_DEFAULT_DTS)) $(SBI_CONFIG_STAMP) CROSS_COMPILE="$(abspath $(BUILDROOT_DIR)/output/host/bin)/riscv64-linux-" \ MULTIHART="$(MULTIHART)" \ + DEFAULT_DTB="$(GCPT_DEFAULT_DTB)" \ + DTS_TEMPLATE_DIR="$(abspath dts)" \ bash scripts/build-sbi.sh bootloader/opensbi $(SBI_BUILD_DIR) define add_workload_linux @@ -101,7 +110,7 @@ build/linux-workloads/$(1)/firmware-vars.$(LINUX_FIRMWARE_BUILD_VARS_HASH).stamp rm -f "$$(@D)"/firmware-vars.*.stamp touch "$$@" -build/linux-workloads/$(1)/fw_payload.bin: $$(shell find $$(abspath dts)) $(GCPT_BIN) dts/xiangshan.dts.in scripts/build-sbi.sh scripts/build-firmware-linux.sh build/linux-workloads/$(1)/rootfs.cpio $(LINUX_IMAGE) $(SBI_BIN) build/linux-workloads/$(1)/firmware-vars.$(LINUX_FIRMWARE_BUILD_VARS_HASH).stamp +build/linux-workloads/$(1)/fw_payload.bin: $$(shell find $$(abspath dts)) $(GCPT_BIN) dts/xiangshan.dts.in scripts/build-sbi.sh scripts/dts-config.sh scripts/build-firmware-linux.sh build/linux-workloads/$(1)/rootfs.cpio $(LINUX_IMAGE) $(SBI_BIN) build/linux-workloads/$(1)/firmware-vars.$(LINUX_FIRMWARE_BUILD_VARS_HASH).stamp CROSS_COMPILE="$$(abspath $(BUILDROOT_DIR)/output/host/bin)/riscv64-linux-" \ DTC="$$(abspath $(BUILDROOT_DIR)/output/host/bin)/dtc" \ DEFAULT_DTB="$(LINUX_DEFAULT_DTB)" \ diff --git a/README.md b/README.md index 36c26ac..b985a05 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ For Linux workloads: - `build/linux-workloads/workload_name/fw_payload.bin`: The all-in-one image that can be directly loaded by NEMU. - `build/linux-workloads/workload_name/rootfs.cpio`: The initramfs overlay of the workload. -- `build/linux-workloads/workload_name/dt/`: A directory containing device tree files. +- `build/linux-workloads/workload_name/dt/`: The selected device tree source and binary. For AM workloads: @@ -64,21 +64,23 @@ To create a compressed tarball containing all built workloads, run `make tarball - [x] Add workload `kvmtool`. - [ ] Add workload `Xvisor`. -- [x] Support for building multiple device trees for each Linux workload. +- [x] Support for selectable device tree templates. - [ ] Test Linux workloads with checkpoint functionalities of NEMU. ## Format of the Image ### Linux Workloads -For Linux workloads, the image assumes that execution begins at `0x80000000`, and the image is loaded into a continuous memory starting from that address. A single-core image contains: +For single-core Linux workloads, execution begins at the DRAM base from the +selected DTS, and the image is loaded into continuous memory starting there. +A single-core image contains: | Offset | Content | |---------|-------------------------------| | 0.0 MiB | LibCheckpointAlpha | | 1.0 MiB | OpenSBI | | 1.5 MiB | device tree | -| 2.0 MiB | Linux kernel | +| >=2.0 MiB | Linux kernel at the next 2 MiB-aligned address | | -- | initramfs containing workload | A multi-hart image uses the fixed QEMU checkpoint layout: @@ -96,15 +98,14 @@ For OpenSBI, the multi-hart image uses `FW_TEXT_START=0x80100000`, The canonical single-core and multi-hart DTS memory maps, including DRAM profiles and DTS selection examples, are in [dts/README.md](dts/README.md#single-core-physical-memory-map). -Multiple device trees are built for each workload, each corresponds to a specific NEMU configuration. The device tree files are placed under the `dt` directory in the build output directory of that workload. The "default" device tree built into the image is `dt/xiangshan.dtb`. To replace the device tree, the following command can be used: +For single-core images, the selected DTS is the source of truth for the DRAM +base used by GCPT, OpenSBI, the firmware packer, and the exported manifest, as +well as the CLINT address used by GCPT. Its generated DTS and DTB are placed in +the workload's `dt` directory and the DTB is embedded in the firmware image. -For DTS files used with gcpt, the beginning of RAM must be reserved with a `reserved-memory` node so Linux does not allocate or map the gcpt checkpoint buffer. The XiangShan FPGA DTS templates reserve 1 MiB at `0x80000000` for this purpose. +For DTS files used with gcpt, the beginning of RAM must be reserved with a `reserved-memory` node so Linux does not allocate or map the gcpt checkpoint buffer. The XiangShan FPGA DTS templates reserve the first 1 MiB of their declared DRAM for this purpose. -```shell -dd conv=notrunc bs=1024 seek=1536 if=dt/some_device.dtb of=fw_payload.bin -``` - -OpenSBI is patched (see `bootloader/opensbi.patch`) to load the device tree from a fixed location. The initramfs is placed after the Linux kernel and aligned to 1 MiB. +OpenSBI is patched (see `bootloader/opensbi.patch`) to load the device tree from a fixed offset relative to the DRAM base. The initramfs is placed after the Linux kernel and aligned to 1 MiB. ### AM Workloads @@ -205,7 +206,8 @@ Then run `make` to build your new workload. ## Adding a Device Tree -To add a device tree, simply create a template file `device_name.dts.in` in the `dts` directory. It will be automatically found by the build system. +To add a device tree, create a template file `device_name.dts.in` in the `dts` +directory and select it with `DEFAULT_DTB=device_name`. Currently, the memory location of the initramfs containing the workload is passed to the kernel by device tree. So for each workload, device tree files are generated from the template on the fly, because the size of the initramfs cannot be known in advance. You should use the parameters `INITRAMFS_BEGIN` and `INITRAMFS_END` in the device tree template. diff --git a/bootloader/LibCheckpointAlpha b/bootloader/LibCheckpointAlpha index b313732..eb6ca46 160000 --- a/bootloader/LibCheckpointAlpha +++ b/bootloader/LibCheckpointAlpha @@ -1 +1 @@ -Subproject commit b3137321622584d35ebb9f8c70fe1a7e2041f506 +Subproject commit eb6ca46bb485b64bc4864d48957f84f6706c308c diff --git a/dts/README.md b/dts/README.md index 648e13e..05339f2 100644 --- a/dts/README.md +++ b/dts/README.md @@ -1,6 +1,8 @@ # Device Tree Templates -This directory contains device tree templates for each device configuration. For each workload, device tree files are generated from the template on the fly, because some parameters cannot be known in advance. +This directory contains device tree templates for each device configuration. +For each workload, the selected device tree is generated from its template on +the fly because some parameters cannot be known in advance. ## Parameters @@ -29,25 +31,31 @@ the build fails if it does not. Single-core images use `MULTIHART=0` and LibCheckpointAlpha. They keep the original compact placement below; the multi-hart checkpoint-state reservation -and the `0x88600000` kernel address do not apply: +and the `0x88600000` kernel address do not apply. This table shows the default +`0x80000000` DRAM base: | Physical address / range | Size or offset | Assignment | |-------------------------|----------------|------------| | `0x80000000–0x800fffff` | 1 MiB | LibCheckpointAlpha checkpoint-recovery program; reserved as `no-map` in the DTS | | `0x80100000` | +1 MiB | OpenSBI firmware starts here | | `0x80180000` | +1.5 MiB | Device tree placed here by firmware assembly | -| `0x80200000` and above | +2 MiB | Linux kernel image, then the MiB-aligned initramfs | +| `0x80200000` and above | >=+2 MiB | Linux kernel at the first 2 MiB-aligned address, then the MiB-aligned initramfs | The single-core firmware packer uses `DTB_OFFSET_KB=1536`, -`SBI_OFFSET_KB=1024`, and `KERNEL_OFFSET_MB=2`. The initramfs address is +`SBI_OFFSET_KB=1024`, and a minimum kernel offset of 2 MiB. The kernel's +physical address is rounded up to a 2 MiB boundary. The initramfs address is computed from the actual kernel size and starts at the next MiB boundary. The -selected single-core DTS supplies the DRAM capacity; no fixed 8 GiB or 64 GiB -profile is imposed by this layout. +selected single-core DTS supplies the DRAM base and capacity; no fixed 8 GiB or +64 GiB profile is imposed by this layout. + +The `memory` node's first address cell pair is the image's DRAM base. The build +uses it for GCPT, OpenSBI, kernel, initramfs, and manifest placement, and uses +the single `riscv,clint0` node to configure GCPT. ## Multi-Hart Physical Memory Map -All `MULTIHART=1` images use the same physical placement, regardless of the -selected DRAM capacity or hart count. The image is loaded at `0x80000000`: +All `MULTIHART=1` images use the same physical placement, regardless of +the selected DRAM capacity or hart count. The image is loaded at `0x80000000`: | Physical range | Size | Assignment | |----------------|------|------------| diff --git a/scripts/build-firmware-linux.sh b/scripts/build-firmware-linux.sh index acf0a72..a562e96 100644 --- a/scripts/build-firmware-linux.sh +++ b/scripts/build-firmware-linux.sh @@ -7,6 +7,7 @@ DTS_TEMPLATE_DIR="$(realpath "$3")" KERNEL_IMAGE="$(realpath "$4")" WORKLOAD_BUILD_DIR="$(realpath "$5")" CPIO_ARCHIVE="$WORKLOAD_BUILD_DIR/rootfs.cpio" +source "$(dirname "${BASH_SOURCE[0]}")/dts-config.sh" DEFAULT_DTB="${DEFAULT_DTB:-}" DTB_MEMORY_PROFILE="${DTB_MEMORY_PROFILE:-}" DTB_MIN_MEMORY_BYTES="${DTB_MIN_MEMORY_BYTES:-}" @@ -14,11 +15,10 @@ HARTS="${HARTS:-2}" readonly MULTIHART_MAX_HARTS=128 readonly MULTIHART_KERNEL_OFFSET_MB=134 -MEM_BEGIN=$(( 0x80000000 )) DTB_OFFSET_KB=1536 DTB_MAX_SIZE_KB=512 SBI_OFFSET_KB=1024 -KERNEL_OFFSET_MB=2 +KERNEL_MIN_OFFSET_MB=2 if [ "${MULTIHART:-0}" = 1 ]; then if [ -z "$DEFAULT_DTB" ]; then @@ -34,18 +34,52 @@ if [ "${MULTIHART:-0}" = 1 ]; then fi DTB_OFFSET_KB=2048 DTB_MAX_SIZE_KB=1024 - KERNEL_OFFSET_MB="$MULTIHART_KERNEL_OFFSET_MB" + KERNEL_MIN_OFFSET_MB="$MULTIHART_KERNEL_OFFSET_MB" elif [ -z "$DEFAULT_DTB" ]; then DEFAULT_DTB=xiangshan fi +resolve_default_dtb_base() { + local default_dtb="$1" + local memory_profile="$2" + + if [ -z "$memory_profile" ]; then + printf '%s\n' "$default_dtb" + return + fi + if [[ "$default_dtb" == *-novec ]]; then + printf '%s\n' "${default_dtb%-novec}-mem${memory_profile}-novec" + return + fi + printf '%s\n' "${default_dtb}-mem${memory_profile}" +} + +DEFAULT_DTB_BASE="$(resolve_default_dtb_base "$DEFAULT_DTB" "$DTB_MEMORY_PROFILE")" +DEFAULT_DTB_TEMPLATE="$DTS_TEMPLATE_DIR/$DEFAULT_DTB_BASE.dts.in" +if ! [ -f "$DEFAULT_DTB_TEMPLATE" ]; then + echo "Default device tree template not found in dts directory: $DEFAULT_DTB_TEMPLATE" >&2 + exit 1 +fi +DTS_CONFIG="$(dts_extract_config "$DEFAULT_DTB_TEMPLATE")" +read -r MEM_BEGIN MEM_SIZE CLINT_MMIO <<< "$DTS_CONFIG" +if [ "${MULTIHART:-0}" = 1 ] && { + (( MEM_BEGIN != 0x80000000 )) || + (( CLINT_MMIO != 0x38000000 )); +}; then + printf 'Multi-hart DTS addresses are fixed: expected DRAM=0x80000000 and CLINT=0x38000000 in %s\n' \ + "$DEFAULT_DTB_TEMPLATE" >&2 + exit 1 +fi + KILOBYTE=1024 MEGABYTE=$(( 1024*1024 )) KERNEL_SIZE=$(stat -c%s "$KERNEL_IMAGE") -KERNEL_SIZE_MB=$(( (KERNEL_SIZE + MEGABYTE - 1) / MEGABYTE )) -INITRAMFS_OFFSET_MB=$(( KERNEL_OFFSET_MB + KERNEL_SIZE_MB )) +KERNEL_BEGIN_ADDR="$(dts_linux_kernel_address "$MEM_BEGIN" "$((KERNEL_MIN_OFFSET_MB * MEGABYTE))")" +KERNEL_END_ADDR=$(( KERNEL_BEGIN_ADDR + KERNEL_SIZE )) +KERNEL_OFFSET_KB=$(( (KERNEL_BEGIN_ADDR - MEM_BEGIN) / KILOBYTE )) +INITRAMFS_BEGIN_ADDR=$(( (KERNEL_END_ADDR + MEGABYTE - 1) / MEGABYTE * MEGABYTE )) +INITRAMFS_OFFSET_KB=$(( (INITRAMFS_BEGIN_ADDR - MEM_BEGIN) / KILOBYTE )) INITRAMFS_SIZE=$(stat -c%s "$CPIO_ARCHIVE") -INITRAMFS_BEGIN_ADDR=$(( MEM_BEGIN + INITRAMFS_OFFSET_MB*MEGABYTE )) INITRAMFS_END_ADDR=$(( INITRAMFS_BEGIN_ADDR + INITRAMFS_SIZE )) INITRAMFS_BEGIN_HEX=$(printf "0x%x" "$INITRAMFS_BEGIN_ADDR") INITRAMFS_END_HEX=$(printf "0x%x" "$INITRAMFS_END_ADDR") @@ -58,41 +92,11 @@ INITRAMFS_END_LO=$(printf "0x%x" $(( INITRAMFS_END_ADDR & 0xffffffff ))) DTC="${DTC:-dtc}" dtb_memory_range_bytes() { local dts_file="$1" - local cells - cells="$( - awk ' - /device_type[[:space:]]*=[[:space:]]*"memory"/ { in_memory = 1 } - in_memory && /reg[[:space:]]*=/ { - line = $0 - while (line !~ /;/ && (getline more) > 0) { - line = line " " more - } - gsub(/[<>;]/, " ", line) - n = split(line, fields, /[[:space:]]+/) - count = 0 - for (i = 1; i <= n; i++) { - if (fields[i] ~ /^(0x[0-9a-fA-F]+|[0-9]+)$/) { - values[++count] = fields[i] - } - } - if (count >= 4) { - print values[1], values[2], values[3], values[4] - exit - } - } - ' "$dts_file" - )" - if [ -z "$cells" ]; then - return 1 - fi - set -- $cells - local begin_high="$1" - local begin_low="$2" - local size_high="$3" - local size_low="$4" - printf '%s %s\n' \ - $(( begin_high * 4294967296 + begin_low )) \ - $(( size_high * 4294967296 + size_low )) + local memory_begin memory_bytes unused_clint + local dts_config + dts_config="$(dts_extract_config "$dts_file")" || return + read -r memory_begin memory_bytes unused_clint <<< "$dts_config" + printf '%s %s\n' "$((memory_begin))" "$((memory_bytes))" } check_dtb_memory_layout() { @@ -107,9 +111,9 @@ check_dtb_memory_layout() { exit 1 fi read -r memory_begin memory_bytes <<< "$memory_range" - if [ "$memory_begin" -ne "$MEM_BEGIN" ]; then - printf 'DTS memory must begin at 0x80000000: found 0x%x in %s\n' \ - "$memory_begin" "$dts_file" >&2 + if (( memory_begin != MEM_BEGIN )); then + printf 'DTS memory base differs from selected DTS: found 0x%x, expected 0x%x in %s\n' \ + "$memory_begin" "$MEM_BEGIN" "$dts_file" >&2 exit 1 fi if [ -n "$min_bytes" ] && [ "$memory_bytes" -lt "$min_bytes" ]; then @@ -242,32 +246,9 @@ build-dtb() { "$DTC" -I dts -O dtb -o "$dtb_file" "$dts_file" } -resolve_default_dtb_base() { - local default_dtb="$1" - local memory_profile="$2" - - if [ -z "$memory_profile" ]; then - printf '%s\n' "$default_dtb" - return - fi - if [[ "$default_dtb" == *-novec ]]; then - printf '%s\n' "${default_dtb%-novec}-mem${memory_profile}-novec" - return - fi - printf '%s\n' "${default_dtb}-mem${memory_profile}" -} - # Assemble the image using the selected DTB basename and optional memory profile. -DEFAULT_DTB_BASE="$(resolve_default_dtb_base "$DEFAULT_DTB" "$DTB_MEMORY_PROFILE")" -DEFAULT_DTB_TEMPLATE="$DTS_TEMPLATE_DIR/$DEFAULT_DTB_BASE.dts.in" -if ! [ -f "$DEFAULT_DTB_TEMPLATE" ]; then - echo "Default device tree template not found in dts directory: $DEFAULT_DTB_TEMPLATE" >&2 - exit 1 -fi - -for dts_template in "$DTS_TEMPLATE_DIR"/*.dts.in ; do - build-dtb "$dts_template" -done +rm -rf "$WORKLOAD_BUILD_DIR/dt" +build-dtb "$DEFAULT_DTB_TEMPLATE" DEFAULT_DTB_FILE="$WORKLOAD_BUILD_DIR/dt/$DEFAULT_DTB_BASE.dtb" DEFAULT_DTS_FILE="$WORKLOAD_BUILD_DIR/dt/$DEFAULT_DTB_BASE.dts" @@ -297,5 +278,5 @@ check_image_component_size DTB "$DEFAULT_DTB_FILE" $(( DTB_MAX_SIZE_KB * KILOBYT dd if="$STARTUP_FILE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$KILOBYTE" count="$SBI_OFFSET_KB" status=none dd if="$DEFAULT_DTB_FILE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$KILOBYTE" seek="$DTB_OFFSET_KB" conv=notrunc status=none dd if="$SBI_IMAGE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$KILOBYTE" seek="$SBI_OFFSET_KB" conv=notrunc status=none -dd if="$KERNEL_IMAGE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$MEGABYTE" seek="$KERNEL_OFFSET_MB" conv=notrunc status=none -dd if="$CPIO_ARCHIVE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$MEGABYTE" seek="$INITRAMFS_OFFSET_MB" conv=notrunc status=none +dd if="$KERNEL_IMAGE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$KILOBYTE" seek="$KERNEL_OFFSET_KB" conv=notrunc status=none +dd if="$CPIO_ARCHIVE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$KILOBYTE" seek="$INITRAMFS_OFFSET_KB" conv=notrunc status=none diff --git a/scripts/build-gcpt.sh b/scripts/build-gcpt.sh index 48515d7..b9cb65e 100644 --- a/scripts/build-gcpt.sh +++ b/scripts/build-gcpt.sh @@ -8,34 +8,7 @@ GCPT_IMPLEMENTATION="${GCPT_IMPLEMENTATION:-alpha}" GCPT_CONFIGURE_MODE="${GCPT_CONFIGURE_MODE:-normal}" GCPT_PAYLOAD_PATH="${GCPT_PAYLOAD_PATH:-${3:-}}" -extract_clint_mmio() { - local dts_template_dir="$1" - local default_dtb="$2" - local dts_template="$dts_template_dir/$default_dtb.dts.in" - - if ! [ -f "$dts_template" ]; then - echo "Default DTS template not found: $dts_template" >&2 - return 1 - fi - - perl -0777 -ne ' - while (/(?:[A-Za-z_][A-Za-z0-9_]*:\s*)?[A-Za-z0-9,_-]*clint@[^{]*\{(.*?)\};/sg) { - my $node = $1; - next unless $node =~ /compatible\s*=\s*[^;]*"riscv,clint0"/s; - next unless $node =~ /reg\s*=\s*<([^>]+)>/s; - my @cells = $1 =~ /(0x[0-9a-fA-F]+|\d+)/g; - next unless @cells >= 2; - my $addr = (hex_or_dec($cells[0]) << 32) + hex_or_dec($cells[1]); - printf "0x%x\n", $addr; - exit 0; - } - exit 1; - sub hex_or_dec { - my ($v) = @_; - return $v =~ /^0x/i ? hex($v) : int($v); - } - ' "$dts_template" -} +source "$(dirname "${BASH_SOURCE[0]}")/dts-config.sh" mkdir -p "$BUILD_DIR" rm -rf "$GCPT_BUILD_DIR" @@ -48,8 +21,10 @@ case "$GCPT_IMPLEMENTATION" in alpha) DTS_TEMPLATE_DIR="$(realpath "${DTS_TEMPLATE_DIR:?DTS_TEMPLATE_DIR is required for LibCheckpointAlpha}")" DEFAULT_DTB="${DEFAULT_DTB:-xiangshan}" - CLINT_MMIO="${CLINT_MMIO:-$(extract_clint_mmio "$DTS_TEMPLATE_DIR" "$DEFAULT_DTB")}" - export CFLAGS="${CFLAGS:-} -DCONFIG_CLINT_MMIO=$CLINT_MMIO" + DTS_TEMPLATE="$DTS_TEMPLATE_DIR/$DEFAULT_DTB.dts.in" + DTS_CONFIG="$(dts_extract_config "$DTS_TEMPLATE")" + read -r MEM_BEGIN MEM_SIZE CLINT_MMIO <<< "$DTS_CONFIG" + export CFLAGS="${CFLAGS:-} -DCONFIG_CLINT_MMIO=$CLINT_MMIO -DCONFIG_DRAM_BASE=$MEM_BEGIN" make -C "$GCPT_BUILD_DIR" ;; libcheckpoint) diff --git a/scripts/build-sbi.sh b/scripts/build-sbi.sh index e621e42..2c4e2b5 100644 --- a/scripts/build-sbi.sh +++ b/scripts/build-sbi.sh @@ -4,12 +4,23 @@ set -e SBI_SOURCE_DIR="$(realpath "$1")" SBI_BUILD_DIR="$(realpath "$2")" BUILD_DIR="$(dirname "$SBI_BUILD_DIR")" -FW_JUMP_ADDR=0x80200000 -FW_JUMP_FDT_ADDR=0x80180000 +source "$(dirname "${BASH_SOURCE[0]}")/dts-config.sh" +MEGABYTE=$((1024 * 1024)) if [ "${MULTIHART:-0}" = 1 ]; then + FW_TEXT_START=0x80100000 FW_JUMP_ADDR=0x88600000 FW_JUMP_FDT_ADDR=0x80200000 +else + DEFAULT_DTB="${DEFAULT_DTB:-xiangshan}" + DTS_TEMPLATE_DIR="${DTS_TEMPLATE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../dts}" + DTS_TEMPLATE_DIR="$(realpath "$DTS_TEMPLATE_DIR")" + DTS_TEMPLATE="$DTS_TEMPLATE_DIR/$DEFAULT_DTB.dts.in" + DTS_CONFIG="$(dts_extract_config "$DTS_TEMPLATE")" + read -r MEM_BEGIN MEM_SIZE CLINT_MMIO <<< "$DTS_CONFIG" + FW_TEXT_START=$((MEM_BEGIN + MEGABYTE)) + FW_JUMP_ADDR="$(dts_linux_kernel_address "$MEM_BEGIN" "$((2 * MEGABYTE))")" + FW_JUMP_FDT_ADDR=$((MEM_BEGIN + 1536 * 1024)) fi # prepare OpenSBI source @@ -21,4 +32,5 @@ cp "$SBI_SOURCE_DIR/../opensbi.config" "$SBI_BUILD_DIR/platform/generic/configs/ # Build OpenSBI cd "$SBI_BUILD_DIR" patch -p1 < "$SBI_SOURCE_DIR/../opensbi.patch" -make PLATFORM=generic FW_JUMP=y FW_TEXT_START=0x80100000 FW_JUMP_ADDR="$FW_JUMP_ADDR" FW_JUMP_FDT_ADDR="$FW_JUMP_FDT_ADDR" +# OpenSBI treats any nonempty DEBUG value as an unoptimized debug build. +make DEBUG= PLATFORM=generic FW_JUMP=y FW_TEXT_START="$(printf '0x%x' "$FW_TEXT_START")" FW_JUMP_ADDR="$(printf '0x%x' "$FW_JUMP_ADDR")" FW_JUMP_FDT_ADDR="$(printf '0x%x' "$FW_JUMP_FDT_ADDR")" diff --git a/scripts/dts-config.sh b/scripts/dts-config.sh new file mode 100755 index 0000000..2714382 --- /dev/null +++ b/scripts/dts-config.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +# Print the selected DTS address configuration as three shell words: +# memory base, memory size, and CLINT base. +dts_extract_config() { + local dts_file="$1" + + if ! [ -f "$dts_file" ]; then + echo "DTS file not found: $dts_file" >&2 + return 1 + fi + + perl -0777 -ne ' + sub value { + my ($v) = @_; + return $v =~ /^0x/i ? hex($v) : int($v); + } + sub cells { + my ($node) = @_; + return unless $node =~ /reg\s*=\s*<([^>]+)>/s; + my @cells = $1 =~ /(0x[0-9a-fA-F]+|\d+)/g; + return @cells; + } + sub address { + my @cells = @_; + return unless @cells >= 2; + return (value($cells[0]) << 32) + value($cells[1]); + } + + my (@memory, @clint); + while (/(?:[A-Za-z_][A-Za-z0-9_]*:\s*)?[A-Za-z0-9,_-]*memory(?:@[^{]*)?\s*\{(.*?)\};/sg) { + my $node = $1; + next unless $node =~ /device_type\s*=\s*"memory"/s; + my @cells = cells($node); + next unless @cells >= 4; + push @memory, [address(@cells[0, 1]), address(@cells[2, 3])]; + } + while (/(?:[A-Za-z_][A-Za-z0-9_]*:\s*)?[A-Za-z0-9,_-]*clint@[^{]*\s*\{(.*?)\};/sg) { + my $node = $1; + next unless $node =~ /compatible\s*=\s*[^;]*"riscv,clint0"/s; + my @cells = cells($node); + next unless @cells >= 2; + push @clint, address(@cells[0, 1]); + } + for my $entry (["memory", \@memory], ["CLINT", \@clint]) { + my ($name, $values) = @$entry; + if (@$values == 0) { + print STDERR "DTS does not define a recognized $name node\n"; + exit 1; + } + if (@$values > 1) { + print STDERR "DTS defines multiple recognized $name nodes\n"; + exit 1; + } + } + my ($memory_base, $memory_size) = @{$memory[0]}; + printf "0x%x 0x%x 0x%x\n", $memory_base, $memory_size, $clint[0]; + ' "$dts_file" +} + +dts_linux_kernel_address() { + local memory_base="$1" + local minimum_offset="$2" + local alignment=$((2 * 1024 * 1024)) + + printf '%s\n' $(( (memory_base + minimum_offset + alignment - 1) / alignment * alignment )) +} diff --git a/scripts/export-linux-debug-artifacts.sh b/scripts/export-linux-debug-artifacts.sh index b01b611..c2d9185 100755 --- a/scripts/export-linux-debug-artifacts.sh +++ b/scripts/export-linux-debug-artifacts.sh @@ -7,12 +7,14 @@ WORKLOAD_BUILD_DIR="$(realpath "$3")" IMAGE_DIR="$(realpath -m "$4")" ARTIFACT_NAME="$5" DTB_BASENAME="$6" +KERNEL_IMAGE="$(realpath "$7")" WORKLOAD_ELF="$(realpath "${WORKLOAD_ELF:?WORKLOAD_ELF must be set}")" BUILD_LOG="$(realpath "${BUILD_LOG:?BUILD_LOG must be set}")" RUN_COMMAND="$(realpath "${RUN_COMMAND:?RUN_COMMAND must be set}")" SPEC_CONFIG="$(realpath "${SPEC_CONFIG:?SPEC_CONFIG must be set}")" GCPT_ELF="$(realpath "${GCPT_ELF:?GCPT_ELF must be set}")" GCPT_BIN="$(realpath "${GCPT_BIN:?GCPT_BIN must be set}")" +source "$(dirname "${BASH_SOURCE[0]}")/dts-config.sh" mapfile -t vmlinux_files < <(find "$BUILDROOT_DIR/output/build" -path '*/vmlinux' -type f -print | sort) if [ "${#vmlinux_files[@]}" -ne 1 ]; then @@ -31,13 +33,16 @@ SBI_CONFIG="$SBI_BUILD_DIR/platform/generic/configs/defconfig" FIRMWARE_IMAGE="$WORKLOAD_BUILD_DIR/fw_payload.bin" ROOTFS="$WORKLOAD_BUILD_DIR/rootfs.cpio" -for file in "$SYSTEM_MAP" "$KERNEL_CONFIG" "$DTB_FILE" "$DTS_FILE" "$SBI_ELF" "$SBI_CONFIG" "$FIRMWARE_IMAGE" "$ROOTFS" "$WORKLOAD_ELF" "$BUILD_LOG" "$RUN_COMMAND" "$SPEC_CONFIG" "$GCPT_ELF" "$GCPT_BIN"; do +for file in "$SYSTEM_MAP" "$KERNEL_CONFIG" "$DTB_FILE" "$DTS_FILE" "$SBI_ELF" "$SBI_CONFIG" "$FIRMWARE_IMAGE" "$ROOTFS" "$KERNEL_IMAGE" "$WORKLOAD_ELF" "$BUILD_LOG" "$RUN_COMMAND" "$SPEC_CONFIG" "$GCPT_ELF" "$GCPT_BIN"; do if [ ! -f "$file" ]; then echo "Required debug artifact not found: $file" >&2 exit 1 fi done +dts_config="$(dts_extract_config "$DTS_FILE")" +read -r memory_base memory_size clint_mmio <<< "$dts_config" + KERNEL_DIR="$IMAGE_DIR/kernel" DT_DIR="$IMAGE_DIR/dt" SBI_DIR="$IMAGE_DIR/opensbi" @@ -69,22 +74,26 @@ cp "$DTS_FILE" "$DT_DIR/$ARTIFACT_NAME.dts" cp "$SBI_ELF" "$SBI_DIR/fw_jump.elf" cp "$SBI_CONFIG" "$SBI_DIR/defconfig" -kernel_offset_mb=2 -dtb_address=0x80180000 -opensbi_jump_address=0x80200000 +kernel_min_offset_mb=2 +dtb_offset=$((1536 * 1024)) multihart=false if [ "${MULTIHART:-0}" = 1 ]; then - kernel_offset_mb=134 - dtb_address=0x80200000 - opensbi_jump_address=0x88600000 + kernel_min_offset_mb=134 + dtb_offset=$((2 * 1024 * 1024)) multihart=true fi megabyte=$((1024 * 1024)) -kernel_size=$(stat -c%s "$VMLINUX") -initramfs_offset_mb=$((kernel_offset_mb + (kernel_size + megabyte - 1) / megabyte)) -kernel_address=$(printf '0x%x' $((0x80000000 + kernel_offset_mb * megabyte))) -initramfs_address=$(printf '0x%x' $((0x80000000 + initramfs_offset_mb * megabyte))) +kernel_address_value="$(dts_linux_kernel_address "$memory_base" "$((kernel_min_offset_mb * megabyte))")" +kernel_size=$(stat -c%s "$KERNEL_IMAGE") +kernel_end_address=$((kernel_address_value + kernel_size)) +initramfs_address_value=$(( (kernel_end_address + megabyte - 1) / megabyte * megabyte )) +kernel_address=$(printf '0x%x' "$kernel_address_value") +initramfs_address=$(printf '0x%x' "$initramfs_address_value") +dtb_address=$(printf '0x%x' $((memory_base + dtb_offset))) +opensbi_load_address=$(printf '0x%x' $((memory_base + megabyte))) +opensbi_jump_address="$kernel_address" +gcpt_load_address=$(printf '0x%x' "$memory_base") sha256() { sha256sum "$1" | cut -d ' ' -f 1 } @@ -103,14 +112,14 @@ cat > "$MANIFEST_DIR/$ARTIFACT_NAME.json" < "$$@.tmp" @if [ -f "$$@" ] && cmp -s "$$@.tmp" "$$@"; then rm "$$@.tmp"; else mv "$$@.tmp" "$$@"; fi -$(SPEC2017_BUILD_DIR)/$(1)/fw_payload.bin: $$(SPEC2017_DTS_SOURCES) $$(SPEC2017_GCPT_BIN) $$(SPEC2017_SCRIPTS_DIR)/build-firmware-linux.sh $(SPEC2017_BUILD_DIR)/$(1)/rootfs.cpio $$(SPEC2017_LINUX_IMAGE) $$(SPEC2017_SBI_BIN) $(SPEC2017_BUILD_DIR)/$(1)/firmware/dtb-$(call spec2017_case_dtb_tag,$(1)).stamp +$(SPEC2017_BUILD_DIR)/$(1)/fw_payload.bin: $$(SPEC2017_DTS_SOURCES) $$(SPEC2017_GCPT_BIN) $$(SPEC2017_SCRIPTS_DIR)/build-firmware-linux.sh $$(SPEC2017_SCRIPTS_DIR)/dts-config.sh $(SPEC2017_BUILD_DIR)/$(1)/rootfs.cpio $$(SPEC2017_LINUX_IMAGE) $$(SPEC2017_SBI_BIN) $(SPEC2017_BUILD_DIR)/$(1)/firmware/dtb-$(call spec2017_case_dtb_tag,$(1)).stamp @printf '$(SPEC2017_PROGRESS_PREFIX) Assembling firmware for $(1)\n' @CROSS_COMPILE="$$(SPEC2017_BUILDROOT_CROSS_COMPILE)" \ DTC="$$(SPEC2017_DTC)" \ @@ -202,7 +202,7 @@ linux/$(1): $(SPEC2017_BUILD_DIR)/$(1)/fw_payload.bin WORKLOAD_PHONY_TARGETS += linux/$(1) -$(SPEC2017_IMAGE_DIR)/stamps/$(1).images.stamp: $(SPEC2017_PREPARE_STAMP) $(SPEC2017_BUILD_DIR)/$(1)/cfg.$(call spec2017_case_cfg_hash,$(1)).stamp $$(SPEC2017_HELPER) $$(SPEC2017_WORKLOAD_DIR)/build.sh $(SPEC2017_BUILD_DIR)/$(1)/download/sentinel $(SPEC2017_BUILD_DIR)/$(1)/build-vars.$(SPEC2017_BUILD_VARS_HASH).stamp $$(SPEC2017_DTS_SOURCES) $$(SPEC2017_GCPT_BIN) $$(SPEC2017_GCPT_ELF) $$(SPEC2017_SCRIPTS_DIR)/build-firmware-linux.sh $$(SPEC2017_SCRIPTS_DIR)/export-linux-debug-artifacts.sh $$(SPEC2017_SCRIPTS_DIR)/package-multihart-rootfs.py $$(SPEC2017_LINUX_IMAGE) $$(SPEC2017_SBI_BIN) | spec2017-check-spec-config +$(SPEC2017_IMAGE_DIR)/stamps/$(1).images.stamp: $(SPEC2017_PREPARE_STAMP) $(SPEC2017_BUILD_DIR)/$(1)/cfg.$(call spec2017_case_cfg_hash,$(1)).stamp $$(SPEC2017_HELPER) $$(SPEC2017_WORKLOAD_DIR)/build.sh $(SPEC2017_BUILD_DIR)/$(1)/download/sentinel $(SPEC2017_BUILD_DIR)/$(1)/build-vars.$(SPEC2017_BUILD_VARS_HASH).stamp $$(SPEC2017_DTS_SOURCES) $$(SPEC2017_GCPT_BIN) $$(SPEC2017_GCPT_ELF) $$(SPEC2017_SCRIPTS_DIR)/build-firmware-linux.sh $$(SPEC2017_SCRIPTS_DIR)/export-linux-debug-artifacts.sh $$(SPEC2017_SCRIPTS_DIR)/dts-config.sh $$(SPEC2017_SCRIPTS_DIR)/package-multihart-rootfs.py $$(SPEC2017_LINUX_IMAGE) $$(SPEC2017_SBI_BIN) | spec2017-check-spec-config @printf '$(SPEC2017_PROGRESS_PREFIX) Packaging split run images for $(1)\n' @WORKLOAD_DIR="$$(abspath $$(SPEC2017_WORKLOAD_DIR))" \ WORKLOAD_BUILD_DIR="$$(abspath $(SPEC2017_BUILD_DIR)/$(1))" \ @@ -249,7 +249,7 @@ $(SPEC2017_IMAGE_DIR)/stamps/$(1).images.stamp: $(SPEC2017_PREPARE_STAMP) $(SPEC SPEC_CONFIG="$(abspath $(call spec2017_case_cfg,$(1)))" \ GCPT_ELF="$$(SPEC2017_GCPT_ELF)" \ GCPT_BIN="$$(SPEC2017_GCPT_BIN)" \ - bash "$$(SPEC2017_SCRIPTS_DIR)/export-linux-debug-artifacts.sh" "$$(SPEC2017_BUILDROOT_DIR)" "$$(SPEC2017_SBI_BUILD_DIR)" "$$$$build_dir" "$(SPEC2017_IMAGE_DIR)" "$$$$variant" "$(call spec2017_case_dtb_name,$(1))"; \ + bash "$$(SPEC2017_SCRIPTS_DIR)/export-linux-debug-artifacts.sh" "$$(SPEC2017_BUILDROOT_DIR)" "$$(SPEC2017_SBI_BUILD_DIR)" "$$$$build_dir" "$(SPEC2017_IMAGE_DIR)" "$$$$variant" "$(call spec2017_case_dtb_name,$(1))" "$$(SPEC2017_LINUX_IMAGE)"; \ done @touch "$$@" endef diff --git a/workloads/linux/spec2026/rules.mk b/workloads/linux/spec2026/rules.mk index bfcc698..3d958d6 100644 --- a/workloads/linux/spec2026/rules.mk +++ b/workloads/linux/spec2026/rules.mk @@ -148,7 +148,7 @@ $(SPEC2026_BUILD_DIR)/$(1)/rootfs.cpio: $(SPEC2026_PREPARE_STAMP) $(SPEC2026_BUI SPEC2026_JOBS="$$(SPEC2026_JOBS)" \ bash "$$(SPEC2026_SCRIPTS_DIR)/build-workload-linux.sh" "$$(SPEC2026_WORKLOAD_DIR)" "$(SPEC2026_BUILD_DIR)/$(1)" -$(SPEC2026_BUILD_DIR)/$(1)/fw_payload.bin: $$(SPEC2026_DTS_SOURCES) $(SPEC2026_BUILD_DIR)/$(1)/firmware/dtb-$(call spec2026_case_dtb_tag,$(1)).stamp $$(SPEC2026_GCPT_BIN) $$(SPEC2026_SCRIPTS_DIR)/build-firmware-linux.sh $(SPEC2026_BUILD_DIR)/$(1)/rootfs.cpio $$(SPEC2026_LINUX_IMAGE) $$(SPEC2026_SBI_BIN) +$(SPEC2026_BUILD_DIR)/$(1)/fw_payload.bin: $$(SPEC2026_DTS_SOURCES) $(SPEC2026_BUILD_DIR)/$(1)/firmware/dtb-$(call spec2026_case_dtb_tag,$(1)).stamp $$(SPEC2026_GCPT_BIN) $$(SPEC2026_SCRIPTS_DIR)/build-firmware-linux.sh $$(SPEC2026_SCRIPTS_DIR)/dts-config.sh $(SPEC2026_BUILD_DIR)/$(1)/rootfs.cpio $$(SPEC2026_LINUX_IMAGE) $$(SPEC2026_SBI_BIN) @printf '$(SPEC2026_PROGRESS_PREFIX) Assembling firmware for $(1)\n' @CROSS_COMPILE="$$(SPEC2026_BUILDROOT_CROSS_COMPILE)" \ DTC="$$(SPEC2026_DTC)" \ @@ -163,7 +163,7 @@ linux/$(1): $(SPEC2026_BUILD_DIR)/$(1)/fw_payload.bin WORKLOAD_PHONY_TARGETS += linux/$(1) -$(call spec2026_case_image_stamp,$(1)): $(SPEC2026_BUILD_DIR)/$(1)/fw_payload.bin $(SPEC2026_GCPT_ELF) $(SPEC2026_GCPT_BIN) $(SPEC2026_LINUX_IMAGE) $(SPEC2026_SBI_BIN) $$(SPEC2026_SCRIPTS_DIR)/export-linux-debug-artifacts.sh | spec2026-check-spec-iso +$(call spec2026_case_image_stamp,$(1)): $(SPEC2026_BUILD_DIR)/$(1)/fw_payload.bin $(SPEC2026_GCPT_ELF) $(SPEC2026_GCPT_BIN) $(SPEC2026_LINUX_IMAGE) $(SPEC2026_SBI_BIN) $$(SPEC2026_SCRIPTS_DIR)/export-linux-debug-artifacts.sh $$(SPEC2026_SCRIPTS_DIR)/dts-config.sh | spec2026-check-spec-iso @printf '$(SPEC2026_PROGRESS_PREFIX) Exporting $(1) artifacts to $(call spec2026_case_image_dir,$(1))\n' @MULTIHART="$(SPEC2026_MULTIHART)" \ WORKLOAD_ELF="$(SPEC2026_BUILD_DIR)/$(1)/elf/$(1).elf" \ @@ -172,7 +172,7 @@ $(call spec2026_case_image_stamp,$(1)): $(SPEC2026_BUILD_DIR)/$(1)/fw_payload.bi SPEC_CONFIG="$(SPEC2026_CFG)" \ GCPT_ELF="$(SPEC2026_GCPT_ELF)" \ GCPT_BIN="$(SPEC2026_GCPT_BIN)" \ - bash "$(SPEC2026_SCRIPTS_DIR)/export-linux-debug-artifacts.sh" "$(SPEC2026_BUILDROOT_DIR)" "$(SPEC2026_SBI_BUILD_DIR)" "$(SPEC2026_BUILD_DIR)/$(1)" "$(call spec2026_case_image_dir,$(1))" "$(1)" "$(call spec2026_case_dtb_name,$(1))" + bash "$(SPEC2026_SCRIPTS_DIR)/export-linux-debug-artifacts.sh" "$(SPEC2026_BUILDROOT_DIR)" "$(SPEC2026_SBI_BUILD_DIR)" "$(SPEC2026_BUILD_DIR)/$(1)" "$(call spec2026_case_image_dir,$(1))" "$(1)" "$(call spec2026_case_dtb_name,$(1))" "$(SPEC2026_LINUX_IMAGE)" @touch "$$@" endef