From raymondmaoca at gmail.com Fri May 1 11:33:43 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Fri, 1 May 2026 14:33:43 -0400 Subject: [RFC PATCH 0/3] Add QEMU virt WorldGuard support on top of HWISO Message-ID: <20260501183346.1596027-1-raymondmaoca@gmail.com> From: Raymond Mao This series adds an WorldGuard implementation for OpenSBI on top of previous hardware-isolation framework (HWISO) RFC [1]. The goal is to let OpenSBI program platform WorldGuard checker state at boot and reprogram WorldGuard hart state during domain transitions. The current RFC targets the QEMU virt WorldGuard model on top of the proposed generic HWISO hooks. This series does the following: 1. Add the WorldGuard CSR definitions and hart extension flags needed to detect support for MLWID, MWIDDELEG, and SLWID. 2. Document the HWISO/WorldGuard DT bindings and add a QEMU virt overlay example for domain WID/WID list assignment and checker permissions. 3. Add a QEMU virt WorldGuard HWISO mechanism that: - parses checker topology and protected resource permissions from DT - programs wgChecker MMIO state at boot - parses per-hart default WorldGuard execution state - parses per-domain WorldGuard metadata - reprograms MLWID, MWIDDELEG, and SLWID on domain transitions [1] [RFC PATCH] sbi: add hardware isolation abstraction framework https://lore.kernel.org/opensbi/20260317201849.903071-1-raymondmaoca at gmail.com/ Raymond Mao (3): hart: add WorldGuard CSR IDs and hart extension flags docs: document hwiso WorldGuard DT bindings and add QEMU overlay example platform: virt: add QEMU virt WorldGuard hwiso mechanism docs/domain_support.md | 159 +++ include/sbi/riscv_encoding.h | 3 + include/sbi/sbi_hart.h | 4 + lib/sbi/sbi_hart.c | 2 + platform/generic/include/qemu_virt_wg.h | 60 + platform/generic/objects.mk | 1 + platform/generic/platform.c | 11 + .../generic/virt/qemu-virt-hwiso-overlay.dts | 120 ++ platform/generic/virt/qemu_virt_wgchecker.c | 1050 +++++++++++++++++ 9 files changed, 1410 insertions(+) create mode 100644 platform/generic/include/qemu_virt_wg.h create mode 100644 platform/generic/virt/qemu-virt-hwiso-overlay.dts create mode 100644 platform/generic/virt/qemu_virt_wgchecker.c -- 2.25.1 From raymondmaoca at gmail.com Fri May 1 11:33:44 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Fri, 1 May 2026 14:33:44 -0400 Subject: [RFC PATCH 1/3] hart: add WorldGuard CSR IDs and hart extension flags In-Reply-To: <20260501183346.1596027-1-raymondmaoca@gmail.com> References: <20260501183346.1596027-1-raymondmaoca@gmail.com> Message-ID: <20260501183346.1596027-2-raymondmaoca@gmail.com> From: Raymond Mao Define the WorldGuard-related CSR numbers and claim smwg / sswg hart extensions so platform code can probe support before programming MLWID, MWIDDELEG and SLWID. Signed-off-by: Raymond Mao --- include/sbi/riscv_encoding.h | 3 +++ include/sbi/sbi_hart.h | 4 ++++ lib/sbi/sbi_hart.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h index 46bbeed0..48304132 100644 --- a/include/sbi/riscv_encoding.h +++ b/include/sbi/riscv_encoding.h @@ -331,6 +331,7 @@ /* Supervisor Protection and Translation */ #define CSR_SATP 0x180 +#define CSR_SLWID 0x190 /* Supervisor Indirect Register Alias */ #define CSR_SISELECT 0x150 @@ -454,6 +455,7 @@ /* Machine Configuration */ #define CSR_MENVCFG 0x30a #define CSR_MENVCFGH 0x31a +#define CSR_MLWID 0x390 /* Machine Trap Handling */ #define CSR_MSCRATCH 0x340 @@ -680,6 +682,7 @@ /* Machine Security Configuration CSR (mseccfg) */ #define CSR_MSECCFG 0x747 #define CSR_MSECCFGH 0x757 +#define CSR_MWIDDELEG 0x748 #define MSECCFG_MML_SHIFT (0) #define MSECCFG_MML (_UL(1) << MSECCFG_MML_SHIFT) diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index cc78eec6..2c725ae2 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -63,6 +63,10 @@ enum sbi_hart_extensions { SBI_HART_EXT_SSCSRIND, /** Hart has Ssccfg extension */ SBI_HART_EXT_SSCCFG, + /** Hart has Smwg extension */ + SBI_HART_EXT_SMWG, + /** Hart has Sswg extension */ + SBI_HART_EXT_SSWG, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 3d136944..6fc03a59 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -666,6 +666,8 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(sdtrig, SBI_HART_EXT_SDTRIG), __SBI_HART_EXT_DATA(smcsrind, SBI_HART_EXT_SMCSRIND), __SBI_HART_EXT_DATA(smcdeleg, SBI_HART_EXT_SMCDELEG), + __SBI_HART_EXT_DATA(smwg, SBI_HART_EXT_SMWG), + __SBI_HART_EXT_DATA(sswg, SBI_HART_EXT_SSWG), }; /** -- 2.25.1 From raymondmaoca at gmail.com Fri May 1 11:33:45 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Fri, 1 May 2026 14:33:45 -0400 Subject: [RFC PATCH 2/3] docs: document hwiso WorldGuard DT bindings and add QEMU overlay example In-Reply-To: <20260501183346.1596027-1-raymondmaoca@gmail.com> References: <20260501183346.1596027-1-raymondmaoca@gmail.com> Message-ID: <20260501183346.1596027-3-raymondmaoca@gmail.com> From: Raymond Mao Document the hw-isolation and worldguard_cfg device-tree metadata used by the HWISO framework, and provide a QEMU virt overlay example showing domain WID/WID list assignment and checker permission policy. Signed-off-by: Raymond Mao --- docs/domain_support.md | 159 ++++++++++++++++++ .../generic/virt/qemu-virt-hwiso-overlay.dts | 120 +++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 platform/generic/virt/qemu-virt-hwiso-overlay.dts diff --git a/docs/domain_support.md b/docs/domain_support.md index b34e43aa..d81f1bc6 100644 --- a/docs/domain_support.md +++ b/docs/domain_support.md @@ -201,6 +201,165 @@ The DT properties of a domain instance DT node are as follows: whether the domain instance is allowed to do system reset. * **system-suspend-allowed** (Optional) - A boolean flag representing whether the domain instance is allowed to do system suspend. +* **hw-isolation** (Optional) - A child node acting as a container for + system-level hardware isolation mechanisms. Each child node represents a + single mechanism configured via its compatible string and properties. + +Hardware Isolation Hooks +------------------------ + +OpenSBI provides a system-level hardware isolation framework that dispatches +all registered mechanisms in the following phases: + +* **init** - Runs at boot to configure system-level isolation features. +* **domain_init** - Parses per-domain isolation configuration. +* **domain_exit** - Runs before switching out of a domain. +* **domain_enter** - Runs after switching into a domain. + +Hardware Isolation Device Tree Binding +-------------------------------------- + +The hardware isolation configuration is specified as an optional child node +named **hw-isolation** under a domain instance node. The **hw-isolation** +node is a container for one or more mechanism nodes. + +The DT properties of a hardware isolation container node are as follows: + +* **#address-cells** / **#size-cells** (Optional) - Standard container node + properties. They are not interpreted by OpenSBI. + +Each hardware isolation mechanism has its own properties and compatible +string. A mechanism can either use per-domain properties below the domain +instance node, or parse system-level DT nodes describing isolation hardware. + +For the WorldGuard support on QEMU virt, OpenSBI parses the +following WG-style system nodes: + +* **sifive,wgchecker2** - WorldGuard checker node. +* **reg** - Checker MMIO base/size. +* **sifive,slot-count** - Number of hardware checker slots. +* **sifive,subordinates** - List of protected resource phandles owned by the + checker. +* **worldguard_cfg** - Child node of a protected memory or device node + describing WorldGuard policy for that resource. +* **perms** - 64-bit permission bitmap values encoded as **** cell + pairs, with either one value for the whole resource or one value per + protected range. +* **reg** - Optional protected address ranges inside a **worldguard_cfg** + child. If omitted, the resource node's own **reg** is used. A single + subordinate with one **perms** entry and no explicit **worldguard_cfg/reg** + is treated as a full-checker rule. +* **worldguard** - Optional CPU child node compatible with **riscv,wgcpu** + providing default WG execution state. +* **mwid** - Default machine world ID for a hart. +* **mwidlist** - Valid/delegable world IDs for that hart. + +Domain nodes can optionally provide WG execution metadata under the +**hw-isolation** container: + +* **worldguard,wid** - Machine world ID selected when entering the domain. +* **worldguard,widlist** - World IDs delegated to the domain. + +At runtime the WorldGuard implementation uses the hooks as follows: + +* **init** - Parses all WG checker nodes, validates the protected ranges, and + programs checker MMIO slots at boot when platform checker nodes are + present. Runtime WID/WID list support is enabled only when per-CPU WG + runtime nodes are present; checker-only DTs do not force runtime + switching on. +* **domain_init** - Parses per-domain **worldguard,wid** and + **worldguard,widlist** metadata. +* **domain_exit** - Quiesces the current hart back to its per-hart default + machine WID and clears **MWIDDELEG** before the handoff. +* **domain_enter** - Reprograms **MLWID**, **MWIDDELEG**, and, when + delegation is active, **SLWID** for the destination domain when the hart + supports **smwg** / **sswg**. + +The CPU **worldguard** defaults are parsed per hart from **/cpus/**, so +platforms may provide different default **mwid** / **mwidlist** values on +different harts. + +Hardware Isolation Examples +--------------------------- + +Domain instance with WG execution metadata: + +```text + chosen { + opensbi-domains { + compatible = "opensbi,domain,config"; + + example_domain: domain at 1 { + compatible = "opensbi,domain,instance"; + possible-harts = <&cpu2>; + regions = <&mem0 0x3f>; + boot-hart = <&cpu2>; + next-addr = <0x00000000 0x80200000>; + next-mode = <0x1>; + + hw-isolation { + worldguard { + compatible = "sifive,wgchecker2"; + worldguard,wid = <1>; + worldguard,widlist = <1 3>; + }; + }; + }; + }; + }; +``` + +WG checker, CPU default state, and protected resource example. These nodes +remain in the normal system DT topology because they describe isolation +hardware and protected resources, not OpenSBI domain instances: + +```text + cpu0: cpu at 0 { + worldguard { + compatible = "riscv,wgcpu"; + mwid = <0>; + mwidlist = <0 1 3>; + }; + }; + + flash0: flash at 20000000 { + reg = <0x0 0x20000000 0x0 0x2000000>; + worldguard_cfg { + perms = <0x0 0xc3>; + }; + }; + + uart0: serial at 10000000 { + reg = <0x0 0x10000000 0x0 0x100>; + worldguard_cfg { + perms = <0x0 0xc0>; + }; + }; + + memory0: memory at 80000000 { + reg = <0x0 0x80000000 0x0 0x80000000>; + worldguard_cfg { + reg = <0x0 0x80000000 0x0 0x40000000 + 0x0 0xc0000000 0x0 0x01000000 + 0x0 0xc1000000 0x0 0x3f000000>; + perms = <0x0 0xcf 0x0 0xcc 0x0 0xcf>; + }; + }; + + wgchecker0: wgchecker at 10100000 { + compatible = "sifive,wgchecker2"; + reg = <0x0 0x10100000 0x0 0x1000>; + sifive,slot-count = <8>; + sifive,subordinates = <&memory0 &flash0 &uart0>; + }; +``` + +The test overlay used in this tree is at: + +* **platform/generic/virt/qemu-virt-hwiso-overlay.dts** + +That overlay only adds per-domain and per-resource metadata. The base DTB +must still provide the WG checker nodes and per-CPU **worldguard** nodes. ### Assigning HART To Domain Instance diff --git a/platform/generic/virt/qemu-virt-hwiso-overlay.dts b/platform/generic/virt/qemu-virt-hwiso-overlay.dts new file mode 100644 index 00000000..63676abb --- /dev/null +++ b/platform/generic/virt/qemu-virt-hwiso-overlay.dts @@ -0,0 +1,120 @@ +/dts-v1/; +/plugin/; + +/* + * Test-only overlay for exercising HWISO with WorldGuard metadata. + * + * This overlay only adds OpenSBI domain metadata and worldguard_cfg resource + * policy. The base DTB is expected to already provide the WG checker nodes + * and per-CPU worldguard child nodes. + * + * Usage: + * Domain hart phandles are filled in after merge because fdtoverlay does not + * reliably resolve CPU-node references against QEMU dumpdtb output here. + * See below steps for filling the domain hart phandles (assume the dumped dtb + * and merged dtb are represented by 'qemu.dtb' and 'qemu-merged.dtb' + * respectively): + * cpu0_phandle=$(fdtget -t x qemu.dtb /cpus/cpu at 0 phandle) + * cpu1_phandle=$(fdtget -t x qemu.dtb /cpus/cpu at 1 phandle) + * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain at 0 \ + * possible-harts "$cpu0_phandle" "$cpu1_phandle" + * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain at 0 \ + * boot-hart "$cpu0_phandle" + * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain at 1 \ + * possible-harts "$cpu1_phandle" + * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain at 1 \ + * boot-hart "$cpu1_phandle" + */ +/ { + fragment at 0 { + target-path = "/chosen"; + __overlay__ { + opensbi-domains { + compatible = "opensbi,domain,config"; + #address-cells = <1>; + #size-cells = <0>; + + memregion0: memregion at 0 { + compatible = "opensbi,domain,memregion"; + base = <0x00000000 0x80000000>; + order = <0x1f>; + }; + + guest0: domain at 0 { + compatible = "opensbi,domain,instance"; + regions = <&memregion0 0x3f>; + next-addr = <0x00000000 0x80200000>; + next-arg1 = <0x00000000 0x82200000>; + next-mode = <0x1>; + + hw-isolation { + worldguard { + compatible = "sifive,wgchecker2"; + worldguard,wid = <0>; + worldguard,widlist = <0 1 3>; + }; + }; + }; + + guest1: domain at 1 { + compatible = "opensbi,domain,instance"; + regions = <&memregion0 0x3f>; + next-addr = <0x00000000 0x80200000>; + next-mode = <0x1>; + + hw-isolation { + worldguard { + compatible = "sifive,wgchecker2"; + worldguard,wid = <1>; + worldguard,widlist = <1 3>; + }; + }; + }; + }; + }; + }; + + fragment at 1 { + target-path = "/cpus/cpu at 0"; + __overlay__ { + opensbi-domain = <&guest0>; + }; + }; + + fragment at 2 { + target-path = "/cpus/cpu at 1"; + __overlay__ { + opensbi-domain = <&guest0>; + }; + }; + + fragment at 3 { + target-path = "/memory at 80000000"; + __overlay__ { + worldguard_cfg { + reg = <0x00000000 0x80000000 0x00000000 0x40000000 + 0x00000000 0xc0000000 0x00000000 0x01000000 + 0x00000000 0xc1000000 0x00000000 0x3f000000>; + perms = <0x0 0xcf 0x0 0xcc 0x0 0xcf>; + }; + }; + }; + + fragment at 4 { + target-path = "/flash at 20000000"; + __overlay__ { + worldguard_cfg { + perms = <0x0 0xc3>; + }; + }; + }; + + fragment at 5 { + target-path = "/soc/serial at 10000000"; + __overlay__ { + worldguard_cfg { + perms = <0x0 0xc0>; + }; + }; + }; +}; -- 2.25.1 From raymondmaoca at gmail.com Fri May 1 11:33:46 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Fri, 1 May 2026 14:33:46 -0400 Subject: [RFC PATCH 3/3] platform: virt: add QEMU virt WorldGuard hwiso mechanism In-Reply-To: <20260501183346.1596027-1-raymondmaoca@gmail.com> References: <20260501183346.1596027-1-raymondmaoca@gmail.com> Message-ID: <20260501183346.1596027-4-raymondmaoca@gmail.com> From: Raymond Mao Implement the QEMU virt WorldGuard HWISO mechanism. Parse checker subordinates and resource permissions from the FDT, program wgChecker MMIO state at boot, parse per-hart CPU defaults plus per-domain WorldGuard metadata, and switch MLWID, MWIDDELEG and SLWID on domain transitions. Signed-off-by: Raymond Mao --- platform/generic/include/qemu_virt_wg.h | 60 ++ platform/generic/objects.mk | 1 + platform/generic/platform.c | 11 + platform/generic/virt/qemu_virt_wgchecker.c | 1050 +++++++++++++++++++ 4 files changed, 1122 insertions(+) create mode 100644 platform/generic/include/qemu_virt_wg.h create mode 100644 platform/generic/virt/qemu_virt_wgchecker.c diff --git a/platform/generic/include/qemu_virt_wg.h b/platform/generic/include/qemu_virt_wg.h new file mode 100644 index 00000000..c1685c0c --- /dev/null +++ b/platform/generic/include/qemu_virt_wg.h @@ -0,0 +1,60 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#ifndef __QEMU_VIRT_WG_H__ +#define __QEMU_VIRT_WG_H__ + +#include + +/* + * QEMU virt WorldGuard model definitions + */ +#define QEMU_VIRT_WG_COMPAT "sifive,wgchecker2" +#define QEMU_VIRT_WG_CPU_COMPAT "riscv,wgcpu" +#define QEMU_VIRT_WG_CPU_NODE "worldguard" +#define QEMU_VIRT_WG_CFG_NODE "worldguard_cfg" + +#define QEMU_VIRT_WG_PROP_SLOT_COUNT "sifive,slot-count" +#define QEMU_VIRT_WG_PROP_SUBORDINATES "sifive,subordinates" +#define QEMU_VIRT_WG_PROP_WID "worldguard,wid" +#define QEMU_VIRT_WG_PROP_WIDLIST "worldguard,widlist" +#define QEMU_VIRT_WG_PROP_MWID "mwid" +#define QEMU_VIRT_WG_PROP_MWIDLIST "mwidlist" +#define QEMU_VIRT_WG_PROP_PERMS "perms" + +/* + * The current QEMU wgChecker model uses a 64-bit permission register with + * 2 bits per world, so the current software model tracks at most 32 WIDs. + */ +#define QEMU_VIRT_WG_MAX_WIDS 32 + +/* The current QEMU wgChecker model requires 4 KiB slot alignment. */ +#define QEMU_VIRT_WG_MIN_ALIGN 0x1000ULL + +/* Current QEMU wgChecker MMIO register layout. */ +#define QEMU_VIRT_WG_MMIO_NSLOTS 0x008 +#define QEMU_VIRT_WG_MMIO_ERRCAUSE 0x010 +#define QEMU_VIRT_WG_MMIO_ERRADDR 0x018 +#define QEMU_VIRT_WG_MMIO_SLOT_BASE 0x020 +#define QEMU_VIRT_WG_MMIO_SLOT_STRIDE 0x020 +#define QEMU_VIRT_WG_MMIO_SLOT_ADDR 0x000 +#define QEMU_VIRT_WG_MMIO_SLOT_PERM 0x008 +#define QEMU_VIRT_WG_MMIO_SLOT_CFG 0x010 + +/* Current QEMU wgChecker slot cfg.A[1:0] encoding. */ +#define QEMU_VIRT_WG_SLOT_CFG_A_MASK 0x3 +#define QEMU_VIRT_WG_SLOT_CFG_A_OFF 0x0 +#define QEMU_VIRT_WG_SLOT_CFG_A_TOR 0x1 + +struct qemu_virt_wg_range { + u64 base; + u64 size; + u64 perm; +}; + +#endif diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk index 85aa723a..ebca6940 100644 --- a/platform/generic/objects.mk +++ b/platform/generic/objects.mk @@ -20,6 +20,7 @@ platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \ # Objects to build platform-objs-y += platform.o platform-objs-y += platform_override_modules.o +platform-objs-y += virt/qemu_virt_wgchecker.o # Blobs to build FW_TEXT_START=0x80000000 diff --git a/platform/generic/platform.c b/platform/generic/platform.c index b76c2a2f..5f2bc1e4 100644 --- a/platform/generic/platform.c +++ b/platform/generic/platform.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,8 @@ #include #include +extern int qemu_virt_hwiso_register(void *fdt); + /* List of platform override modules generated at compile time */ extern const struct platform_override *platform_override_modules[]; extern unsigned long platform_override_modules_size; @@ -222,9 +225,17 @@ static int generic_nascent_init(void) static int generic_early_init(bool cold_boot) { + int rc; + if (cold_boot) fdt_reset_init(); + if (cold_boot) { + rc = qemu_virt_hwiso_register(fdt_get_address()); + if (rc && rc != SBI_EALREADY) + return rc; + } + if (!generic_plat || !generic_plat->early_init) return 0; diff --git a/platform/generic/virt/qemu_virt_wgchecker.c b/platform/generic/virt/qemu_virt_wgchecker.c new file mode 100644 index 00000000..063fcecb --- /dev/null +++ b/platform/generic/virt/qemu_virt_wgchecker.c @@ -0,0 +1,1050 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * QEMU virt WorldGuard hardware isolation support + * + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct wg_checker { + char name[32]; + u64 mmio_base; + u64 mmio_size; + u32 slot_count; + u32 subordinate_count; + bool full_checker_rule; + u64 full_checker_perm; + u32 range_count; + struct qemu_virt_wg_range *ranges; +}; + +struct wg_cpu_defaults { + u32 trusted_wid; + u32 nworlds; + u32 valid_wid_mask; +}; + +struct wg_platform_ctx { + u32 checker_count; + u32 hart_count; + bool checker_enabled; + bool runtime_enabled; + struct wg_checker *checkers; + struct wg_cpu_defaults *hart_defaults; +}; + +struct wg_domain_ctx { + bool has_wid; + u32 wid; + u32 widlist_count; + u32 widlist_mask; + u32 widlist[QEMU_VIRT_WG_MAX_WIDS]; +}; + +static struct wg_platform_ctx *wg_platform; + +static void wg_free_platform_ctx(struct wg_platform_ctx *platform) +{ + u32 i; + + if (!platform) + return; + + for (i = 0; i < platform->checker_count; i++) + sbi_free(platform->checkers[i].ranges); + + sbi_free(platform->checkers); + sbi_free(platform->hart_defaults); + sbi_free(platform); +} + +static bool wg_runtime_enabled(void) +{ + return wg_platform && wg_platform->runtime_enabled; +} + +static u64 wg_read_cells(const fdt32_t *cells, int count) +{ + u64 val = 0; + int i; + + for (i = 0; i < count; i++) + val = (val << 32) | fdt32_to_cpu(cells[i]); + + return val; +} + +static void wg_write64(u64 addr, u64 val) +{ +#if __riscv_xlen != 32 + writeq(val, (void *)(unsigned long)addr); +#else + writel((u32)val, (void *)(unsigned long)addr); + writel((u32)(val >> 32), (void *)(unsigned long)(addr + 4)); +#endif +} + +static void wg_write32(u64 addr, u32 val) +{ + writel(val, (void *)(unsigned long)addr); +} + +static u64 wg_slot_addr_encode(u64 addr) +{ + return addr >> 2; +} + +static u64 wg_wid_mask(u32 wid) +{ + return (wid < 32) ? (1ULL << wid) : 0; +} + +static bool wg_range_is_aligned(u64 base, u64 size) +{ + if (!size) + return false; + + if (base & (QEMU_VIRT_WG_MIN_ALIGN - 1)) + return false; + if (size & (QEMU_VIRT_WG_MIN_ALIGN - 1)) + return false; + + return true; +} + +static void wg_sort_ranges(struct wg_checker *checker) +{ + struct qemu_virt_wg_range tmp; + u32 i, j; + + for (i = 1; i < checker->range_count; i++) { + tmp = checker->ranges[i]; + j = i; + while (j > 0 && checker->ranges[j - 1].base > tmp.base) { + checker->ranges[j] = checker->ranges[j - 1]; + j--; + } + checker->ranges[j] = tmp; + } +} + +static int wg_compact_ranges(struct wg_checker *checker) +{ + struct qemu_virt_wg_range *prev, *cur; + u64 prev_end, cur_end; + u32 i, out = 0; + + if (!checker->range_count) + return 0; + + wg_sort_ranges(checker); + + for (i = 0; i < checker->range_count; i++) { + cur = &checker->ranges[i]; + cur_end = cur->base + cur->size; + if (cur_end <= cur->base) + return SBI_EINVAL; + + if (!out) { + checker->ranges[out++] = *cur; + continue; + } + + prev = &checker->ranges[out - 1]; + prev_end = prev->base + prev->size; + if (cur->base < prev_end) + return SBI_EINVAL; + + if (cur->base == prev_end && cur->perm == prev->perm) { + prev->size += cur->size; + continue; + } + + checker->ranges[out++] = *cur; + } + + checker->range_count = out; + return 0; +} + +static int wg_get_reg_cells(void *fdt, int resource_node, + int *addr_cells, int *size_cells) +{ + int parent; + + parent = fdt_parent_offset(fdt, resource_node); + if (parent < 0) + return SBI_EINVAL; + + *addr_cells = fdt_address_cells(fdt, parent); + *size_cells = fdt_size_cells(fdt, parent); + if (*addr_cells <= 0 || *addr_cells > 2 || *size_cells <= 0 || + *size_cells > 2) + return SBI_EINVAL; + + return 0; +} + +static int wg_count_reg_entries(void *fdt, int resource_node, int reg_node) +{ + const fdt32_t *reg; + int addr_cells, size_cells, entry_cells, len, rc; + + rc = wg_get_reg_cells(fdt, resource_node, &addr_cells, &size_cells); + if (rc) + return rc; + + reg = fdt_getprop(fdt, reg_node, "reg", &len); + if (!reg || len <= 0) + return 0; + + entry_cells = addr_cells + size_cells; + if (len % (entry_cells * (int)sizeof(fdt32_t))) + return SBI_EINVAL; + + return len / (entry_cells * (int)sizeof(fdt32_t)); +} + +static int wg_parse_perms(void *fdt, int cfg_node, u64 **out_perms, + u32 *out_count) +{ + const fdt32_t *perms; + u64 *vals; + int len, i, count; + + *out_perms = NULL; + *out_count = 0; + + perms = fdt_getprop(fdt, cfg_node, QEMU_VIRT_WG_PROP_PERMS, &len); + if (!perms || len <= 0) + return 0; + + /* QEMU virt WG permissions are always encoded as 64-bit cells. */ + if (len % (2 * (int)sizeof(fdt32_t))) + return SBI_EINVAL; + + count = len / (2 * (int)sizeof(fdt32_t)); + vals = sbi_calloc(sizeof(*vals), count); + if (!vals) + return SBI_ENOMEM; + + for (i = 0; i < count; i++, perms += 2) + vals[i] = wg_read_cells(perms, 2); + + *out_perms = vals; + *out_count = count; + return 0; +} + +static int wg_fill_ranges(void *fdt, int resource_node, int reg_node, + const u64 *perms, u32 perm_count, + struct qemu_virt_wg_range *ranges, u32 range_count) +{ + const fdt32_t *reg; + u64 base, size; + int addr_cells, size_cells, entry_cells, len, i, rc; + + rc = wg_get_reg_cells(fdt, resource_node, &addr_cells, &size_cells); + if (rc) + return rc; + + reg = fdt_getprop(fdt, reg_node, "reg", &len); + if (!reg || len <= 0) + return SBI_EINVAL; + + entry_cells = addr_cells + size_cells; + for (i = 0; i < (int)range_count; i++, reg += entry_cells) { + base = wg_read_cells(reg, addr_cells); + size = wg_read_cells(reg + addr_cells, size_cells); + if (!wg_range_is_aligned(base, size)) + return SBI_EINVAL; + + ranges[i].base = base; + ranges[i].size = size; + ranges[i].perm = perms[(perm_count == 1) ? 0 : i]; + } + + return 0; +} + +static int wg_parse_checker_rules(void *fdt, int checker_node, + struct wg_checker *checker) +{ + const fdt32_t *subs; + u64 *perms = NULL; + int cfg_node, len, i, rc = 0, reg_count; + u32 perm_count = 0; + int child; + + subs = fdt_getprop(fdt, checker_node, + QEMU_VIRT_WG_PROP_SUBORDINATES, &len); + if (!subs || len <= 0) + return 0; + if (len % (int)sizeof(fdt32_t)) + goto err; + + checker->subordinate_count = len / sizeof(fdt32_t); + if (!checker->slot_count) + goto err; + + checker->ranges = sbi_calloc(sizeof(*checker->ranges), + checker->slot_count); + if (!checker->ranges) + return SBI_ENOMEM; + + for (i = 0; i < checker->subordinate_count; i++) { + child = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(subs[i])); + if (child < 0) { + sbi_printf("[WG] checker %s has invalid subordinate" + " phandle[%d]=0x%x err=%d\n", + checker->name, i, fdt32_to_cpu(subs[i]), + child); + rc = child; + goto err; + } + + cfg_node = fdt_subnode_offset(fdt, child, + QEMU_VIRT_WG_CFG_NODE); + if (cfg_node < 0) + continue; + + rc = wg_parse_perms(fdt, cfg_node, &perms, &perm_count); + if (rc) + goto err; + if (!perm_count) + continue; + + reg_count = wg_count_reg_entries(fdt, child, cfg_node); + if (reg_count < 0) + goto err; + + if (!reg_count && checker->subordinate_count == 1 && + perm_count == 1) { + if (checker->range_count) + goto err; + checker->full_checker_rule = true; + checker->full_checker_perm = perms[0]; + sbi_free(perms); + perms = NULL; + continue; + } + + if (!reg_count) + reg_count = wg_count_reg_entries(fdt, child, child); + if (reg_count <= 0) + goto err; + + if (perm_count != 1 && perm_count != (u32)reg_count) + goto err; + if (checker->full_checker_rule) + goto err; + if (checker->range_count + reg_count > checker->slot_count) + goto err; + + rc = wg_fill_ranges(fdt, child, + (fdt_getprop(fdt, cfg_node, "reg", NULL) ? + cfg_node : child), + perms, perm_count, + &checker->ranges[checker->range_count], + reg_count); + sbi_free(perms); + perms = NULL; + if (rc) + goto err; + + checker->range_count += reg_count; + } + + if (checker->full_checker_rule) + return 0; + + return wg_compact_ranges(checker); + +err: + sbi_free(perms); + return rc ? rc : SBI_EINVAL; +} + +static int wg_parse_checker(void *fdt, int checker_node, + struct wg_checker *checker) +{ + const fdt32_t *val; + u64 base = 0, size = 0; + int len, rc; + + rc = fdt_get_node_addr_size(fdt, checker_node, 0, &base, &size); + if (rc) + return rc; + + val = fdt_getprop(fdt, checker_node, + QEMU_VIRT_WG_PROP_SLOT_COUNT, &len); + if (!val || len < (int)sizeof(fdt32_t)) + return SBI_EINVAL; + + checker->mmio_base = base; + checker->mmio_size = size; + checker->slot_count = fdt32_to_cpu(val[0]); + sbi_snprintf(checker->name, sizeof(checker->name), "%s", + fdt_get_name(fdt, checker_node, NULL)); + + return wg_parse_checker_rules(fdt, checker_node, checker); +} + +static void wg_program_clear_slots(const struct wg_checker *checker) +{ + u32 slot; + + for (slot = 1; slot < checker->slot_count; slot++) { + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_ADDR, 0); + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_PERM, 0); + wg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_CFG, 0); + } + +} + +static void wg_program_clear_last_slot(const struct wg_checker *checker) +{ + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + checker->slot_count * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_PERM, 0); + wg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + checker->slot_count * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_CFG, 0); +} + +static void wg_program_clear_slots_from(const struct wg_checker *checker, + u32 first_slot) +{ + u32 slot; + + if (first_slot >= checker->slot_count) + return; + + for (slot = first_slot; slot < checker->slot_count; slot++) { + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_ADDR, 0); + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_PERM, 0); + wg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_CFG, 0); + } +} + +static int wg_program_checker(const struct wg_checker *checker) +{ + u64 prev_end = 0; + u32 required_slots = 0, slot = 1, i; + + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_ERRCAUSE, 0); + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_ERRADDR, 0); + + if (checker->full_checker_rule) { + wg_program_clear_slots(checker); + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + checker->slot_count * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_PERM, + checker->full_checker_perm); + wg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + checker->slot_count * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_CFG, + QEMU_VIRT_WG_SLOT_CFG_A_TOR); + return 0; + } + + for (i = 0; i < checker->range_count; i++) { + if (!i || checker->ranges[i].base != prev_end) + required_slots++; + required_slots++; + prev_end = checker->ranges[i].base + checker->ranges[i].size; + } + + if (required_slots > checker->slot_count - 1) + return SBI_EINVAL; + + prev_end = 0; + for (i = 0; i < checker->range_count; i++) { + const struct qemu_virt_wg_range *range = &checker->ranges[i]; + u64 end = range->base + range->size; + + if (!i || range->base != prev_end) { + wg_write64(checker->mmio_base + + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_ADDR, + wg_slot_addr_encode(range->base)); + wg_write64(checker->mmio_base + + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_PERM, 0); + wg_write32(checker->mmio_base + + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_CFG, + QEMU_VIRT_WG_SLOT_CFG_A_OFF); + slot++; + } + + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_ADDR, + wg_slot_addr_encode(end)); + wg_write64(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_PERM, + range->perm); + wg_write32(checker->mmio_base + QEMU_VIRT_WG_MMIO_SLOT_BASE + + slot * QEMU_VIRT_WG_MMIO_SLOT_STRIDE + + QEMU_VIRT_WG_MMIO_SLOT_CFG, + QEMU_VIRT_WG_SLOT_CFG_A_TOR); + prev_end = end; + slot++; + } + + /* + * Keep the reset-time trusted-WID bypass slot alive until the new + * rule set is fully programmed, otherwise the DRAM checker can deny + * OpenSBI's own RAM accesses mid-update. + */ + wg_program_clear_slots_from(checker, slot); + wg_program_clear_last_slot(checker); + + return 0; +} + +static void wg_free_platform(void) +{ + if (!wg_platform) + return; + + wg_free_platform_ctx(wg_platform); + wg_platform = NULL; +} + +static void wg_init_cpu_defaults(struct wg_platform_ctx *platform) +{ + u32 i; + + if (!platform || !platform->hart_defaults) + return; + + for (i = 0; i < platform->hart_count; i++) { + platform->hart_defaults[i].trusted_wid = 0; + platform->hart_defaults[i].nworlds = 1; + platform->hart_defaults[i].valid_wid_mask = 0x1; + } +} + +static int wg_parse_wid_prop(void *fdt, int node, const char *prop_name, + u32 *out_wid) +{ + const fdt32_t *prop; + int len; + + if (!out_wid) + return SBI_EINVAL; + + prop = fdt_getprop(fdt, node, prop_name, &len); + if (!prop) + return SBI_ENOENT; + if (len != (int)sizeof(fdt32_t)) + return SBI_EINVAL; + + *out_wid = fdt32_to_cpu(prop[0]); + if (*out_wid >= QEMU_VIRT_WG_MAX_WIDS) + return SBI_EINVAL; + + return 0; +} + +static int wg_parse_widlist(void *fdt, int node, const char *prop_name, + u32 *out_mask, u32 *out_wids, u32 *out_count) +{ + const fdt32_t *prop; + u32 mask = 0, count = 0, wid; + int len, i; + + if (!out_mask || !out_count) + return SBI_EINVAL; + + *out_mask = 0; + *out_count = 0; + + prop = fdt_getprop(fdt, node, prop_name, &len); + if (!prop) + return 0; + if (len < 0 || (len % (int)sizeof(fdt32_t))) + return SBI_EINVAL; + + count = len / sizeof(fdt32_t); + if (count > QEMU_VIRT_WG_MAX_WIDS) + return SBI_EINVAL; + + for (i = 0; i < (int)count; i++) { + wid = fdt32_to_cpu(prop[i]); + if (wid >= QEMU_VIRT_WG_MAX_WIDS) + return SBI_EINVAL; + if (mask & wg_wid_mask(wid)) + return SBI_EINVAL; + + mask |= wg_wid_mask(wid); + if (out_wids) + out_wids[i] = wid; + } + + *out_mask = mask; + *out_count = count; + return 0; +} + +static int wg_parse_cpu_defaults(void *fdt, struct wg_platform_ctx *platform) +{ + struct wg_cpu_defaults *cpu_defaults; + u32 hartid, hartindex, max_wid, widlist_count; + int cpus_offset, cpu_offset, wgcpu, rc; + + if (!fdt || !platform || !platform->hart_defaults) + return 0; + + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return 0; + + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + if (fdt_parse_hart_id(fdt, cpu_offset, &hartid)) + continue; + + hartindex = sbi_hartid_to_hartindex(hartid); + if (!sbi_hartindex_valid(hartindex) || + hartindex >= platform->hart_count) + continue; + + wgcpu = fdt_subnode_offset(fdt, cpu_offset, + QEMU_VIRT_WG_CPU_NODE); + if (wgcpu < 0 || fdt_node_check_compatible( + fdt, wgcpu, QEMU_VIRT_WG_CPU_COMPAT)) + continue; + + cpu_defaults = &platform->hart_defaults[hartindex]; + rc = wg_parse_wid_prop(fdt, wgcpu, QEMU_VIRT_WG_PROP_MWID, + &cpu_defaults->trusted_wid); + if (rc) + return rc; + + max_wid = cpu_defaults->trusted_wid; + rc = wg_parse_widlist(fdt, wgcpu, QEMU_VIRT_WG_PROP_MWIDLIST, + &cpu_defaults->valid_wid_mask, NULL, + &widlist_count); + if (rc) + return rc; + + cpu_defaults->valid_wid_mask |= + wg_wid_mask(cpu_defaults->trusted_wid); + if (cpu_defaults->valid_wid_mask) { + u32 wid; + + for (wid = 0; wid < QEMU_VIRT_WG_MAX_WIDS; wid++) { + if (cpu_defaults->valid_wid_mask & (1U << wid)) + max_wid = wid; + } + } + + cpu_defaults->nworlds = max_wid + 1; + } + + return 0; +} + +static bool wg_has_cpu_runtime(void *fdt) +{ + u32 hartid; + int cpus_offset, cpu_offset, wgcpu; + + if (!fdt) + return false; + + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return false; + + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + if (fdt_parse_hart_id(fdt, cpu_offset, &hartid)) + continue; + + wgcpu = fdt_subnode_offset(fdt, cpu_offset, + QEMU_VIRT_WG_CPU_NODE); + if (wgcpu < 0) + continue; + if (fdt_node_check_compatible(fdt, wgcpu, + QEMU_VIRT_WG_CPU_COMPAT)) + continue; + + return true; + } + + return false; +} + +static u32 wg_count_platform_checkers(void *fdt) +{ + int checker_node; + u32 count = 0; + + if (!fdt) + return 0; + + checker_node = -1; + while (true) { + checker_node = fdt_node_offset_by_compatible( + fdt, checker_node, QEMU_VIRT_WG_COMPAT); + if (checker_node < 0) + break; + if (fdt_getprop(fdt, checker_node, + QEMU_VIRT_WG_PROP_SUBORDINATES, NULL)) + count++; + } + + return count; +} + +static int wg_validate_domain_ctx(const struct sbi_domain *dom, + const struct wg_domain_ctx *ctx) +{ + const struct wg_cpu_defaults *cpu_defaults; + u32 hartindex; + + if (!wg_platform || !dom || !ctx || dom == &root || !dom->possible_harts) + return 0; + + for (hartindex = 0; hartindex < wg_platform->hart_count; hartindex++) { + if (!sbi_hartmask_test_hartindex(hartindex, dom->possible_harts)) + continue; + + cpu_defaults = &wg_platform->hart_defaults[hartindex]; + if (!(cpu_defaults->valid_wid_mask & wg_wid_mask(ctx->wid))) + return SBI_EINVAL; + if (ctx->widlist_mask & ~cpu_defaults->valid_wid_mask) + return SBI_EINVAL; + } + + return 0; +} + +static const struct wg_cpu_defaults *wg_current_cpu_defaults(void) +{ + u32 hartindex; + + if (!wg_platform || !wg_platform->hart_defaults) + return NULL; + + hartindex = sbi_hartid_to_hartindex(current_hartid()); + if (!sbi_hartindex_valid(hartindex) || + hartindex >= wg_platform->hart_count) + return NULL; + + return &wg_platform->hart_defaults[hartindex]; +} + +static void wg_program_wid_state(u32 mlwid, u32 mwiddeleg, u32 slwid) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMWG)) + return; + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSWG)) { + csr_write(CSR_MLWID, mlwid); + return; + } + + csr_write(CSR_MWIDDELEG, 0); + csr_write(CSR_MLWID, mlwid); + if (mwiddeleg) { + csr_write(CSR_MWIDDELEG, mwiddeleg); + csr_write(CSR_SLWID, slwid); + } +} + +static int wg_init(void *fdt) +{ + struct wg_platform_ctx *platform; + int checker_node, rc; + u32 count, idx = 0; + bool has_runtime; + + wg_free_platform(); + + if (!fdt) + return 0; + + count = wg_count_platform_checkers(fdt); + has_runtime = wg_has_cpu_runtime(fdt); + if (!count && !has_runtime) + return 0; + + platform = sbi_zalloc(sizeof(*platform)); + if (!platform) + return SBI_ENOMEM; + + platform->hart_count = sbi_scratch_last_hartindex() + 1; + platform->checker_count = count; + platform->checker_enabled = !!count; + platform->runtime_enabled = has_runtime; + platform->checkers = sbi_calloc(sizeof(*platform->checkers), count); + if (count && !platform->checkers) { + sbi_free(platform); + return SBI_ENOMEM; + } + + platform->hart_defaults = sbi_calloc(sizeof(*platform->hart_defaults), + platform->hart_count); + if (!platform->hart_defaults) { + wg_free_platform_ctx(platform); + return SBI_ENOMEM; + } + + wg_init_cpu_defaults(platform); + rc = wg_parse_cpu_defaults(fdt, platform); + if (rc) { + wg_free_platform_ctx(platform); + return rc; + } + + if (platform->checker_enabled) { + checker_node = -1; + while (true) { + checker_node = fdt_node_offset_by_compatible( + fdt, checker_node, QEMU_VIRT_WG_COMPAT); + if (checker_node < 0) + break; + if (!fdt_getprop(fdt, checker_node, + QEMU_VIRT_WG_PROP_SUBORDINATES, NULL)) + continue; + + rc = wg_parse_checker(fdt, checker_node, + &platform->checkers[idx]); + if (rc) { + sbi_printf("[WG] failed to parse checker %s err=%d\n", + fdt_get_name(fdt, checker_node, NULL), + rc); + wg_free_platform_ctx(platform); + return rc; + } + + rc = wg_program_checker(&platform->checkers[idx]); + if (rc) { + sbi_printf("[WG] failed to program checker %s err=%d\n", + platform->checkers[idx].name, rc); + wg_free_platform_ctx(platform); + return rc; + } + + sbi_printf("[WG] checker %s base=0x%llx slots=%u rules=%u%s\n", + platform->checkers[idx].name, + (unsigned long long)platform->checkers[idx].mmio_base, + platform->checkers[idx].slot_count, + platform->checkers[idx].range_count, + platform->checkers[idx].full_checker_rule ? + " full-checker" : ""); + idx++; + } + } + + wg_platform = platform; + return 0; +} + +static int wg_domain_init(void *fdt, int domain_offset, + struct sbi_domain *dom, void **out_ctx) +{ + struct wg_domain_ctx *ctx; + int hoff, child, rc; + bool found = false; + + if (!out_ctx) + return SBI_EINVAL; + + *out_ctx = NULL; + if (!wg_runtime_enabled()) + return 0; + if (!fdt || domain_offset < 0) + return 0; + + hoff = fdt_subnode_offset(fdt, domain_offset, "hw-isolation"); + if (hoff < 0) + return (dom == &root) ? 0 : SBI_EINVAL; + + fdt_for_each_subnode(child, fdt, hoff) { + if (fdt_node_check_compatible( + fdt, child, QEMU_VIRT_WG_COMPAT)) + continue; + found = true; + break; + } + + if (!found) + return (dom == &root) ? 0 : SBI_EINVAL; + + ctx = sbi_zalloc(sizeof(*ctx)); + if (!ctx) + return SBI_ENOMEM; + + rc = wg_parse_wid_prop(fdt, child, QEMU_VIRT_WG_PROP_WID, &ctx->wid); + if (rc) + goto err_free_ctx; + ctx->has_wid = true; + + rc = wg_parse_widlist(fdt, child, QEMU_VIRT_WG_PROP_WIDLIST, + &ctx->widlist_mask, ctx->widlist, + &ctx->widlist_count); + if (rc) + goto err_free_ctx; + + rc = wg_validate_domain_ctx(dom, ctx); + if (rc) + goto err_free_ctx; + + *out_ctx = ctx; + return 0; + +err_free_ctx: + sbi_free(ctx); + return rc; +} + +static u32 wg_fallback_wid(void) +{ + const struct wg_cpu_defaults *cpu_defaults = wg_current_cpu_defaults(); + + return cpu_defaults ? cpu_defaults->trusted_wid : 0; +} + +static u32 wg_valid_wid_mask(void) +{ + const struct wg_cpu_defaults *cpu_defaults = wg_current_cpu_defaults(); + + return cpu_defaults ? cpu_defaults->valid_wid_mask : + (u32)wg_wid_mask(wg_fallback_wid()); +} + +static u32 wg_select_slwid(u32 widlist_mask, bool has_wid, u32 wid, u32 fallback) +{ + u32 i; + + if (!widlist_mask) + return fallback; + + if (has_wid && (wg_wid_mask(wid) & widlist_mask)) + return wid; + + for (i = 0; i < 32; i++) { + if (widlist_mask & (1U << i)) + return i; + } + + return fallback; +} + +static void wg_domain_exit(const struct sbi_domain *src, + const struct sbi_domain *dst, void *ctx) +{ + u32 mlwid = wg_fallback_wid(); + + (void)ctx; + if (!wg_runtime_enabled()) + return; + + wg_program_wid_state(mlwid, 0, mlwid); + + sbi_printf("[WG] domain_exit src=%s dst=%s mlwid=%u mwiddeleg=0x0\n", + src ? src->name : "", + dst ? dst->name : "", mlwid); +} + +static void wg_domain_enter(const struct sbi_domain *dst, + const struct sbi_domain *src, void *ctx) +{ + struct wg_domain_ctx *dctx = ctx; + u32 valid_mask = wg_valid_wid_mask(); + u32 mlwid = wg_fallback_wid(); + u32 mwiddeleg = 0; + u32 slwid = mlwid; + + (void)src; + if (!wg_runtime_enabled()) + return; + + if (dctx && dctx->has_wid && (wg_wid_mask(dctx->wid) & valid_mask)) + mlwid = dctx->wid; + + if (dctx) + mwiddeleg = dctx->widlist_mask & valid_mask; + slwid = wg_select_slwid(mwiddeleg, dctx && dctx->has_wid, + dctx ? dctx->wid : 0, mlwid); + + wg_program_wid_state(mlwid, mwiddeleg, slwid); + + sbi_printf("[WG] domain_enter dst=%s mlwid=%u mwiddeleg=0x%x", + dst ? dst->name : "", mlwid, mwiddeleg); + sbi_printf(" slwid=%u\n", slwid); +} + +static void wg_domain_cleanup(struct sbi_domain *dom, void *ctx) +{ + (void)dom; + sbi_free(ctx); +} + +static const struct sbi_hwiso_ops wg_ops = { + .name = QEMU_VIRT_WG_COMPAT, + .init = wg_init, + .domain_init = wg_domain_init, + .domain_exit = wg_domain_exit, + .domain_enter = wg_domain_enter, + .domain_cleanup = wg_domain_cleanup, +}; + +int qemu_virt_hwiso_register(void *fdt) +{ + int rc; + + if (!fdt) + return 0; + + if (fdt_node_check_compatible(fdt, 0, "riscv-virtio") && + fdt_node_check_compatible(fdt, 0, "qemu,virt")) + return 0; + + rc = sbi_hwiso_register(&wg_ops); + if (rc) + return rc; + + return 0; +} + -- 2.25.1 From evvoevod at tenstorrent.com Fri May 1 14:16:22 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Fri, 1 May 2026 21:16:22 +0000 Subject: [PATCH v2 0/5] Add RISC-V Smrnmi extension support Message-ID: <20260501211627.3293126-1-evvoevod@tenstorrent.com> This is v2 of the Smrnmi series. Rebased on top of upstream/master at commit 2257e9957103 ("lib: sbi_bitmap_test: add tests for bitmap_empty()"). v1 thread: https://lore.kernel.org/opensbi/20260310183155.2186463-1-evvoevod at oss.tenstorrent.com/ Changes vs v1: - Dropped patches 6/7 and 7/7 (Tenstorrent platform support). They will go through the Atlantis platform series instead. - Dropped _rnme_handler assembly entry point, ops->rnme_handler callback and sbi_rnme_handler() C function. RNME path now reuses the existing _trap_handler since RNME is taken as a regular M-mode trap with NMIE=0. ops->smrnmi_handlers_init() now passes _trap_handler in place of _rnme_handler. - Renamed _rnmi_handler to _trap_rnmi_handler. - Renamed C handler to sbi_trap_rnmi_handler(). - Kept ops->rnmi_handler as the temporary RNMI dispatch mechanism, per the v1 discussion. Extending sbi_irqchip to support NMIs is left for a separate follow-up series. Between v1 and v2, upstream introduced ecdb6c90 ("firmware: Initialize stack guard via Zkr"). The Zkr seed-CSR access can trap if Zkr is not implemented. On Smrnmi platforms NMIE=0 by default after reset, which routes that trap to NMEVEC rather than MTVEC. Until Smrnmi handlers are installed and NMIE=1, no NMEVEC handler is in place, so the early-boot Zkr probe in _start would crash. To handle this, a new patch (4/5) refactors the inline seed loop in fw_base.S into a callable __stack_chk_guard_init function and invokes it from init_coldboot() after sbi_hart_init() has installed Smrnmi handlers and set NMIE=1. Testing: Verified on Whisper SW system simulator and on HW emulator. Evgeny Voevodin (5): include: sbi_scratch: Add tmp1 scratch space for RNMI context saving lib: sbi: Add Smrnmi extension macros for registers and bits firmware: Add RNMI handler infrastructure lib: sbi: Create a spot to place Smrnmi detection before traps and after DT is ready lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection firmware/fw_base.S | 188 ++++++++++++++++++++++++++++++----- include/sbi/riscv_encoding.h | 10 ++ include/sbi/sbi_hart.h | 2 + include/sbi/sbi_platform.h | 8 ++ include/sbi/sbi_scratch.h | 11 +- include/sbi/sbi_trap.h | 2 + lib/sbi/sbi_hart.c | 37 +++++-- lib/sbi/sbi_init.c | 10 ++ lib/sbi/sbi_trap.c | 39 ++++++++ 9 files changed, 273 insertions(+), 34 deletions(-) base-commit: 2257e9957103aac7df8089a59b9d4bdda7c592ce -- 2.43.0 From evvoevod at tenstorrent.com Fri May 1 14:16:23 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Fri, 1 May 2026 21:16:23 +0000 Subject: [PATCH v2 1/5] include: sbi_scratch: Add tmp1 scratch space for RNMI context saving In-Reply-To: <20260501211627.3293126-1-evvoevod@tenstorrent.com> References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> Message-ID: <20260501211627.3293126-2-evvoevod@tenstorrent.com> RNMI handlers use MNSCRATCH instead of MSCRATCH and need separate scratch space from regular trap handling. Add tmp1 for RNMI context while tmp0 remains for regular traps. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- firmware/fw_base.S | 3 ++- include/sbi/sbi_scratch.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 63bb4473..cdb15429 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -258,9 +258,10 @@ _scratch_init: /* Store hartid-to-scratch function address in scratch space */ lla a4, _hartid_to_scratch REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) - /* Clear trap_context and tmp0 in scratch space */ + /* Clear trap_context, tmp0 and tmp1 in scratch space */ REG_S zero, SBI_SCRATCH_TRAP_CONTEXT_OFFSET(tp) REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp) + REG_S zero, SBI_SCRATCH_TMP1_OFFSET(tp) /* Store firmware options in scratch space */ MOV_3R s0, a0, s1, a1, s2, a2 #ifdef FW_OPTIONS diff --git a/include/sbi/sbi_scratch.h b/include/sbi/sbi_scratch.h index 58d54628..a6edeb2d 100644 --- a/include/sbi/sbi_scratch.h +++ b/include/sbi/sbi_scratch.h @@ -40,12 +40,14 @@ #define SBI_SCRATCH_TRAP_CONTEXT_OFFSET (11 * __SIZEOF_POINTER__) /** Offset of tmp0 member in sbi_scratch */ #define SBI_SCRATCH_TMP0_OFFSET (12 * __SIZEOF_POINTER__) +/** Offset of tmp1 member in sbi_scratch */ +#define SBI_SCRATCH_TMP1_OFFSET (13 * __SIZEOF_POINTER__) /** Offset of options member in sbi_scratch */ -#define SBI_SCRATCH_OPTIONS_OFFSET (13 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_OPTIONS_OFFSET (14 * __SIZEOF_POINTER__) /** Offset of hartindex member in sbi_scratch */ -#define SBI_SCRATCH_HARTINDEX_OFFSET (14 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_HARTINDEX_OFFSET (15 * __SIZEOF_POINTER__) /** Offset of extra space in sbi_scratch */ -#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (15 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (16 * __SIZEOF_POINTER__) /** Maximum size of sbi_scratch (4KB) */ #define SBI_SCRATCH_SIZE (0x1000) @@ -83,6 +85,8 @@ struct sbi_scratch { unsigned long trap_context; /** Temporary storage */ unsigned long tmp0; + /** Temporary storage */ + unsigned long tmp1; /** Options for OpenSBI library */ unsigned long options; /** Index of the hart */ @@ -106,6 +110,7 @@ assert_member_offset(struct sbi_scratch, platform_addr, SBI_SCRATCH_PLATFORM_ADD assert_member_offset(struct sbi_scratch, hartid_to_scratch, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET); assert_member_offset(struct sbi_scratch, trap_context, SBI_SCRATCH_TRAP_CONTEXT_OFFSET); assert_member_offset(struct sbi_scratch, tmp0, SBI_SCRATCH_TMP0_OFFSET); +assert_member_offset(struct sbi_scratch, tmp1, SBI_SCRATCH_TMP1_OFFSET); assert_member_offset(struct sbi_scratch, options, SBI_SCRATCH_OPTIONS_OFFSET); assert_member_offset(struct sbi_scratch, hartindex, SBI_SCRATCH_HARTINDEX_OFFSET); -- 2.43.0 From evvoevod at tenstorrent.com Fri May 1 14:16:24 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Fri, 1 May 2026 21:16:24 +0000 Subject: [PATCH v2 2/5] lib: sbi: Add Smrnmi extension macros for registers and bits In-Reply-To: <20260501211627.3293126-1-evvoevod@tenstorrent.com> References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> Message-ID: <20260501211627.3293126-3-evvoevod@tenstorrent.com> Add CSR definitions (MNSCRATCH, MNSTATUS, MNEPC, MNCAUSE) and bit definitions (MNSTATUS_NMIE, MNSTATUS_MNPV, MNSTATUS_MNPP). Also add SBI_HART_EXT_SMRNMI to the hart extension enumeration. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- include/sbi/riscv_encoding.h | 10 ++++++++++ include/sbi/sbi_hart.h | 2 ++ lib/sbi/sbi_hart.c | 1 + 3 files changed, 13 insertions(+) diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h index 3c1d5256..18f7b4a7 100644 --- a/include/sbi/riscv_encoding.h +++ b/include/sbi/riscv_encoding.h @@ -215,6 +215,10 @@ #endif +#define MNSTATUS_NMIE (_UL(0x8)) +#define MNSTATUS_MNPV (_UL(0x80)) +#define MNSTATUS_MNPP (_UL(0x1800)) + #define MHPMEVENT_SSCOF_MASK _ULL(0xFF00000000000000) #define ENVCFG_STCE (_ULL(1) << 63) @@ -830,6 +834,12 @@ #define CSR_CUSTOM10_M_RO_BASE 0xFC0 #define CSR_CUSTOM10_M_RO_COUNT 0x040 +/* Smrnmi extension registers */ +#define CSR_MNSCRATCH 0x740 +#define CSR_MNEPC 0x741 +#define CSR_MNCAUSE 0x742 +#define CSR_MNSTATUS 0x744 + /* ===== Trap/Exception Causes ===== */ #define CAUSE_MISALIGNED_FETCH 0x0 diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index a788b34c..937cdf29 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -87,6 +87,8 @@ enum sbi_hart_extensions { SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, /** Hart has Xsfcease extension */ SBI_HART_EXT_XSIFIVE_CEASE, + /** Hart has Smrnmi extension */ + SBI_HART_EXT_SMRNMI, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 99e13990..4aefb759 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -396,6 +396,7 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), + __SBI_HART_EXT_DATA(smrnmi, SBI_HART_EXT_SMRNMI), }; _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), -- 2.43.0 From evvoevod at tenstorrent.com Fri May 1 14:16:25 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Fri, 1 May 2026 21:16:25 +0000 Subject: [PATCH v2 3/5] firmware: Add RNMI handler infrastructure In-Reply-To: <20260501211627.3293126-1-evvoevod@tenstorrent.com> References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> Message-ID: <20260501211627.3293126-4-evvoevod@tenstorrent.com> Implement basic Resumable NMI (RNMI) handler support for the RISC-V Smrnmi extension. The new _trap_rnmi_handler assembly entry point saves context using the Smrnmi MN* CSRs (MNSCRATCH, MNEPC, MNSTATUS, MNCAUSE) and returns via mnret. It dispatches to sbi_trap_rnmi_handler(), which optionally calls a platform-specific ops->rnmi_handler callback for actual NMI processing. If no platform handler is registered or it fails, the event is reported as an unhandled NMI. The RNMI handler reuses the generic trap context structure but stores MN* CSR values (MNEPC, MNSTATUS, MNCAUSE) into the corresponding generic fields (mepc, mstatus, cause) for compatibility with existing trap infrastructure. Signed-off-by: Evgeny Voevodin --- firmware/fw_base.S | 121 +++++++++++++++++++++++++++++++++++++ include/sbi/sbi_platform.h | 4 ++ include/sbi/sbi_trap.h | 2 + lib/sbi/sbi_trap.c | 39 ++++++++++++ 4 files changed, 166 insertions(+) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index cdb15429..043de7d7 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -529,6 +529,45 @@ memcmp: csrrw tp, CSR_MSCRATCH, tp .endm +.macro TRAP_SAVE_AND_SETUP_SP_T0_NMI + /* Swap TP and MNSCRATCH (for RNMI) */ + csrrw tp, CSR_MNSCRATCH, tp + + /* Save T0 in scratch space */ + REG_S t0, SBI_SCRATCH_TMP1_OFFSET(tp) + + /* + * Set T0 to appropriate exception stack + * + * Came_From_M_Mode = ((MNSTATUS.MNPP < PRV_M) ? 1 : 0) - 1; + * Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP)) + */ + csrr t0, CSR_MNSTATUS + srl t0, t0, 11 /* MNPP is at bits 11-12 */ + and t0, t0, PRV_M + slti t0, t0, PRV_M + add t0, t0, -1 + xor sp, sp, tp + and t0, t0, sp + xor sp, sp, tp + xor t0, tp, t0 + + /* Save original SP on exception stack */ + REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_CONTEXT_SIZE)(t0) + + /* Set SP to exception stack and make room for trap context */ + add sp, t0, -(SBI_TRAP_CONTEXT_SIZE) + + /* Restore T0 from scratch space */ + REG_L t0, SBI_SCRATCH_TMP1_OFFSET(tp) + + /* Save T0 on stack */ + REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) + + /* Swap TP and MNSCRATCH */ + csrrw tp, CSR_MNSCRATCH, tp +.endm + .macro TRAP_SAVE_MEPC_MSTATUS have_mstatush /* Save MEPC and MSTATUS CSRs */ csrr t0, CSR_MEPC @@ -543,6 +582,20 @@ memcmp: .endif .endm +.macro TRAP_SAVE_MNEPC_MNSTATUS have_mstatush + /* + * Save MNEPC and MNSTATUS CSRs (for RNMI) + * Note: Trap context structure has generic field names (mepc, mstatus), + * we store MN* CSR values into these same structure fields. + */ + csrr t0, CSR_MNEPC + REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) + csrr t0, CSR_MNSTATUS + REG_S t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp) + /* MNSTATUSH doesn't exist in SMRNMI spec */ + REG_S zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) +.endm + .macro TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 /* Save all general regisers except SP and T0 */ REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp) @@ -606,12 +659,36 @@ memcmp: CLEAR_MDT t0 .endm +.macro TRAP_SAVE_NMI_INFO + /* + * Save NMI trap info (MNCAUSE, no MNTVAL in spec) + * Note: Trap info structure has generic field names (cause, tval, etc.), + * we store MN* CSR values into these same structure fields. + */ + csrr t0, CSR_MNCAUSE + REG_S t0, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(cause))(sp) + /* MNTVAL doesn't exist in SMRNMI spec */ + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tval))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tval2))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tinst))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(gva))(sp) + + /* We are ready to take another trap, clear MDT */ + CLEAR_MDT t0 +.endm + .macro TRAP_CALL_C_ROUTINE /* Call C routine */ add a0, sp, zero call sbi_trap_handler .endm +.macro TRAP_CALL_C_RNMI_ROUTINE + /* Call C routine */ + add a0, sp, zero + call sbi_trap_rnmi_handler +.endm + .macro TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 /* Restore all general regisers except A0 and T0 */ REG_L ra, SBI_TRAP_REGS_OFFSET(ra)(a0) @@ -660,6 +737,19 @@ memcmp: csrw CSR_MEPC, t0 .endm +.macro TRAP_RESTORE_MNEPC_MNSTATUS + /* + * Restore MNSTATUS and MNEPC CSRs (for RNMI) + * Note: Load from generic structure fields (mstatus, mepc) and + * restore to NMI-specific CSRs (MNSTATUS, MNEPC). + * No MNSTATUSH in SMRNMI spec. + */ + REG_L t0, SBI_TRAP_REGS_OFFSET(mstatus)(a0) + csrw CSR_MNSTATUS, t0 + REG_L t0, SBI_TRAP_REGS_OFFSET(mepc)(a0) + csrw CSR_MNEPC, t0 +.endm + .macro TRAP_RESTORE_A0_T0 /* Restore T0 */ REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(a0) @@ -724,6 +814,37 @@ _trap_handler_hyp: mret + .section .entry, "ax", %progbits + .align 3 + .globl _trap_rnmi_handler +_trap_rnmi_handler: + /* + * NMI interrupt handler using MN* CSRs + * + * Context detection via MNPP (previous privilege mode): + * - If MNPP < M-mode: use exception stack (TP) + * - If MNPP == M-mode: use current stack (SP) + * This handles nested interrupt cases. + */ + TRAP_SAVE_AND_SETUP_SP_T0_NMI + + TRAP_SAVE_MNEPC_MNSTATUS 0 + + TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_SAVE_NMI_INFO + + TRAP_CALL_C_RNMI_ROUTINE + + TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 + + TRAP_RESTORE_MNEPC_MNSTATUS + + TRAP_RESTORE_A0_T0 + + /* mnret - return from NMI (SMRNMI extension) */ + .word 0x70200073 + .section .entry, "ax", %progbits .align 3 .globl _reset_regs diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index e65d9877..715df499 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -149,6 +149,10 @@ struct sbi_platform_operations { unsigned long log2len); /** platform specific pmp disable on current HART */ void (*pmp_disable)(unsigned int n); + + /** platform specific Smrnmi NMI handler. + * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ + int (*rnmi_handler)(struct sbi_trap_context *tcntx); }; /** Platform default per-HART stack size for exception/interrupt handling */ diff --git a/include/sbi/sbi_trap.h b/include/sbi/sbi_trap.h index 731a0c98..091a2446 100644 --- a/include/sbi/sbi_trap.h +++ b/include/sbi/sbi_trap.h @@ -289,6 +289,8 @@ static inline void sbi_trap_set_context(struct sbi_scratch *scratch, struct sbi_trap_context *sbi_trap_handler(struct sbi_trap_context *tcntx); +struct sbi_trap_context *sbi_trap_rnmi_handler(struct sbi_trap_context *tcntx); + #endif #endif diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c index f41db4d1..1e55b885 100644 --- a/lib/sbi/sbi_trap.c +++ b/lib/sbi/sbi_trap.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -375,3 +376,41 @@ trap_done: sbi_trap_set_context(scratch, tcntx->prev_context); return tcntx; } + +/** + * Default Resumable NMI (RNMI) handler + * + * This function is called from the _trap_rnmi_handler assembly code. + * It provides a simple wrapper that calls the platform-specific + * NMI handler if registered. If no handler is registered, it prints + * diagnostic information and hangs, similar to unhandled traps. + * + * Note: The trap context stores NMI CSR values (MNCAUSE, MNEPC, MNSTATUS) + * in the generic trap context fields (cause, mepc, mstatus). + * + * @param tcntx Pointer to trap context (saved on stack) + * @return Same trap context pointer (needed for restore macros) + */ +struct sbi_trap_context *sbi_trap_rnmi_handler(struct sbi_trap_context *tcntx) +{ + int rc; + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); + + /* Call platform-specific NMI handler if registered */ + if (ops && ops->rnmi_handler) { + rc = ops->rnmi_handler(tcntx); + if (rc) { + /* Platform handler failed to handle NMI */ + sbi_trap_error("platform NMI handler failed", rc, tcntx); + } + return tcntx; + } + + /* No platform handler - treat as unhandled NMI */ + sbi_trap_error("unhandled NMI (no platform rnmi_handler)", + SBI_ENOTSUPP, tcntx); + + /* Never returns */ + return tcntx; +} -- 2.43.0 From evvoevod at tenstorrent.com Fri May 1 14:16:26 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Fri, 1 May 2026 21:16:26 +0000 Subject: [PATCH v2 4/5] lib: sbi: Create a spot to place Smrnmi detection before traps and after DT is ready In-Reply-To: <20260501211627.3293126-1-evvoevod@tenstorrent.com> References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> Message-ID: <20260501211627.3293126-5-evvoevod@tenstorrent.com> Since Smrnmi is detected from the device tree, move sbi_platform_extensions_init() before trap-based feature detection so the extension is known when the next commit acts on it. The Zkr seed-CSR access can trap if Zkr is not implemented. On Smrnmi platforms NMIE=0 by default after reset, which routes that trap to NMEVEC rather than MTVEC. Until Smrnmi handlers are installed and NMIE=1, no NMEVEC handler is in place, so an early-boot trap there would crash. Refactor the inline seed loop in fw_base.S into a callable __stack_chk_guard_init function and invoke it from init_coldboot() after sbi_hart_init() returns; the next commit makes sbi_hart_init() install Smrnmi handlers and set NMIE=1, so by the time __stack_chk_guard_init runs the trap path is safe. Signed-off-by: Evgeny Voevodin --- firmware/fw_base.S | 64 +++++++++++++++++++++++++++++----------------- lib/sbi/sbi_hart.c | 16 +++++++----- lib/sbi/sbi_init.c | 10 ++++++++ 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 043de7d7..ebecfecc 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -107,30 +107,6 @@ _bss_zero: add s4, s4, __SIZEOF_POINTER__ blt s4, s5, _bss_zero - /* Trying to initialize the stack guard via the Zkr extension */ - lla t0, __stack_chk_guard_done - csrw CSR_MTVEC, t0 - li t0, 0 - li t3, SEED_OPTS_ES16 - li t4, SEED_ENTROPY_MASK - li t5, __SIZEOF_POINTER__ -__stack_chk_guard_loop: - csrrw t1, CSR_SEED, x0 - li t2, SEED_OPTS_MASK - and t2, t2, t1 - bgtu t2, t3, __stack_chk_guard_done - bltu t2, t3, __stack_chk_guard_loop - and t1, t1, t4 - slli t0, t0, 16 - or t0, t0, t1 - addi t5, t5, -2 - bgtz t5, __stack_chk_guard_loop - lla t1, __stack_chk_guard - REG_S t0, 0(t1) - j __stack_chk_guard_done - .align 3 -__stack_chk_guard_done: - /* Setup temporary trap handler */ lla s4, _start_hang csrw CSR_MTVEC, s4 @@ -895,6 +871,46 @@ __stack_chk_fail: la a0, .Lstack_corrupt_msg call sbi_panic + /* + * Initialize __stack_chk_guard from the Zkr seed CSR. Called from C + * after Smrnmi has been enabled (NMIE=1), so that a missing-Zkr + * illegal-instruction trap is delivered to MTVEC normally and is + * caught by our temporary MTVEC redirect below. + */ + .align 3 + .globl __stack_chk_guard_init + .type __stack_chk_guard_init, %function +__stack_chk_guard_init: + /* Save current MTVEC so we can restore it on return */ + csrr t6, CSR_MTVEC + + /* Redirect MTVEC to local skip target for Zkr-not-supported case */ + lla t0, 1f + csrw CSR_MTVEC, t0 + + li t0, 0 + li t3, SEED_OPTS_ES16 + li t4, SEED_ENTROPY_MASK + li t5, __SIZEOF_POINTER__ +2: + csrrw t1, CSR_SEED, x0 + li t2, SEED_OPTS_MASK + and t2, t2, t1 + bgtu t2, t3, 1f + bltu t2, t3, 2b + and t1, t1, t4 + slli t0, t0, 16 + or t0, t0, t1 + addi t5, t5, -2 + bgtz t5, 2b + lla t1, __stack_chk_guard + REG_S t0, 0(t1) + j 1f + .align 3 +1: + csrw CSR_MTVEC, t6 + ret + /* Initial value of the stack guard variable */ .section .data .align 3 diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 4aefb759..781161e5 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -522,6 +522,16 @@ static int hart_detect_features(struct sbi_scratch *scratch) hfeatures->mhpm_mask = 0; hfeatures->priv_version = SBI_HART_PRIV_VER_UNKNOWN; + /* + * Parse device tree extensions early, before any trap-based checks. + * Needed to detect Smrnmi and install NMI handlers before CSR probes + * that may trigger traps. + */ + rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), + hfeatures); + if (rc) + return rc; + #define __check_hpm_csr(__csr, __mask) \ oldval = csr_read_allowed(__csr, &trap); \ if (!trap.cause) { \ @@ -676,12 +686,6 @@ __pmp_skip: #undef __check_csr_existence - /* Let platform populate extensions */ - rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), - hfeatures); - if (rc) - return rc; - /* Zicntr should only be detected using traps */ __sbi_hart_update_extension(hfeatures, SBI_HART_EXT_ZICNTR, sbi_hart_has_csr(scratch, SBI_HART_CSR_CYCLE) && diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c index aae035e1..644f62cb 100644 --- a/lib/sbi/sbi_init.c +++ b/lib/sbi/sbi_init.c @@ -36,6 +36,8 @@ #include #include +extern void __stack_chk_guard_init(void); + #define BANNER \ " ____ _____ ____ _____\n" \ " / __ \\ / ____| _ \\_ _|\n" \ @@ -269,6 +271,14 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) if (rc) sbi_hart_hang(); + /* + * Initialize stack-guard canary now that hart_detect_features() + * has enabled Smrnmi (NMIE=1). Done from a __noreturn function so + * the canary mutation doesn't trip our own exit check. The asm + * helper saves and restores MTVEC around its operation. + */ + __stack_chk_guard_init(); + rc = sbi_timer_init(scratch, true); if (rc) sbi_hart_hang(); -- 2.43.0 From evvoevod at tenstorrent.com Fri May 1 14:16:27 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Fri, 1 May 2026 21:16:27 +0000 Subject: [PATCH v2 5/5] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection In-Reply-To: <20260501211627.3293126-1-evvoevod@tenstorrent.com> References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> Message-ID: <20260501211627.3293126-6-evvoevod@tenstorrent.com> The location of the RNMI/E trap vectors in the Smrnmi extension is implementation-defined, so platforms with vendor-specific NMI vector mechanisms must install the firmware's NMI entry points themselves. Add an smrnmi_handlers_init() callback to sbi_platform_operations that receives the firmware entry points and lets platform code install them at the hardware-specific vector locations. Two pointers are passed: - _trap_rnmi_handler: the dedicated RNMI entry point that saves context using the Smrnmi MN* CSRs and returns via mnret. - _trap_handler: the regular M-mode trap entry since RNME is taken as a regular M-mode trap with NMIE=0. When Smrnmi is present, install the platform's NMI vectors via the new callback, initialize MNSCRATCH with the per-hart scratch pointer, and set MNSTATUS.NMIE. Smrnmi-enabled platforms must register smrnmi_handlers_init; if the extension is detected but no callback is registered, sbi_panic() is called since enabling NMIs without handlers in place would route subsequent traps into nowhere. Signed-off-by: Evgeny Voevodin --- include/sbi/sbi_platform.h | 4 ++++ lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index 715df499..fe382b56 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -150,6 +150,10 @@ struct sbi_platform_operations { /** platform specific pmp disable on current HART */ void (*pmp_disable)(unsigned int n); + /** platform specific Smrnmi handlers init on current HART */ + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), + void (*rnme_handler)(void)); + /** platform specific Smrnmi NMI handler. * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ int (*rnmi_handler)(struct sbi_trap_context *tcntx); diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 781161e5..92c602aa 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -532,6 +532,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) if (rc) return rc; + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); + extern void _trap_rnmi_handler(void); + extern void _trap_handler(void); + + if (!ops || !ops->smrnmi_handlers_init) + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); + + /* Reuse _trap_handler for the RNME slot since RNME is taken + * as a regular M-mode trap with NMIE=0. */ + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); + + /* Initialize MNSCRATCH for the RNMI handler */ + csr_write(CSR_MNSCRATCH, scratch); + + /* Enable NMIs */ + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); + } + #define __check_hpm_csr(__csr, __mask) \ oldval = csr_read_allowed(__csr, &trap); \ if (!trap.cause) { \ -- 2.43.0 From anup at brainfault.org Mon May 4 01:42:42 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 4 May 2026 14:12:42 +0530 Subject: [PATCH v2 3/5] firmware: Add RNMI handler infrastructure In-Reply-To: <20260501211627.3293126-4-evvoevod@tenstorrent.com> References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> <20260501211627.3293126-4-evvoevod@tenstorrent.com> Message-ID: On Sat, May 2, 2026 at 2:46?AM Evgeny Voevodin wrote: > > Implement basic Resumable NMI (RNMI) handler support for the RISC-V > Smrnmi extension. > > The new _trap_rnmi_handler assembly entry point saves context using the > Smrnmi MN* CSRs (MNSCRATCH, MNEPC, MNSTATUS, MNCAUSE) and returns via > mnret. It dispatches to sbi_trap_rnmi_handler(), which optionally calls > a platform-specific ops->rnmi_handler callback for actual NMI > processing. If no platform handler is registered or it fails, the > event is reported as an unhandled NMI. > > The RNMI handler reuses the generic trap context structure but stores MN* > CSR values (MNEPC, MNSTATUS, MNCAUSE) into the corresponding generic > fields (mepc, mstatus, cause) for compatibility with existing trap > infrastructure. > > Signed-off-by: Evgeny Voevodin LGTM. Reviewed-by: Anup Patel Regards, Anup > --- > firmware/fw_base.S | 121 +++++++++++++++++++++++++++++++++++++ > include/sbi/sbi_platform.h | 4 ++ > include/sbi/sbi_trap.h | 2 + > lib/sbi/sbi_trap.c | 39 ++++++++++++ > 4 files changed, 166 insertions(+) > > diff --git a/firmware/fw_base.S b/firmware/fw_base.S > index cdb15429..043de7d7 100644 > --- a/firmware/fw_base.S > +++ b/firmware/fw_base.S > @@ -529,6 +529,45 @@ memcmp: > csrrw tp, CSR_MSCRATCH, tp > .endm > > +.macro TRAP_SAVE_AND_SETUP_SP_T0_NMI > + /* Swap TP and MNSCRATCH (for RNMI) */ > + csrrw tp, CSR_MNSCRATCH, tp > + > + /* Save T0 in scratch space */ > + REG_S t0, SBI_SCRATCH_TMP1_OFFSET(tp) > + > + /* > + * Set T0 to appropriate exception stack > + * > + * Came_From_M_Mode = ((MNSTATUS.MNPP < PRV_M) ? 1 : 0) - 1; > + * Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP)) > + */ > + csrr t0, CSR_MNSTATUS > + srl t0, t0, 11 /* MNPP is at bits 11-12 */ > + and t0, t0, PRV_M > + slti t0, t0, PRV_M > + add t0, t0, -1 > + xor sp, sp, tp > + and t0, t0, sp > + xor sp, sp, tp > + xor t0, tp, t0 > + > + /* Save original SP on exception stack */ > + REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_CONTEXT_SIZE)(t0) > + > + /* Set SP to exception stack and make room for trap context */ > + add sp, t0, -(SBI_TRAP_CONTEXT_SIZE) > + > + /* Restore T0 from scratch space */ > + REG_L t0, SBI_SCRATCH_TMP1_OFFSET(tp) > + > + /* Save T0 on stack */ > + REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) > + > + /* Swap TP and MNSCRATCH */ > + csrrw tp, CSR_MNSCRATCH, tp > +.endm > + > .macro TRAP_SAVE_MEPC_MSTATUS have_mstatush > /* Save MEPC and MSTATUS CSRs */ > csrr t0, CSR_MEPC > @@ -543,6 +582,20 @@ memcmp: > .endif > .endm > > +.macro TRAP_SAVE_MNEPC_MNSTATUS have_mstatush > + /* > + * Save MNEPC and MNSTATUS CSRs (for RNMI) > + * Note: Trap context structure has generic field names (mepc, mstatus), > + * we store MN* CSR values into these same structure fields. > + */ > + csrr t0, CSR_MNEPC > + REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) > + csrr t0, CSR_MNSTATUS > + REG_S t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp) > + /* MNSTATUSH doesn't exist in SMRNMI spec */ > + REG_S zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) > +.endm > + > .macro TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 > /* Save all general regisers except SP and T0 */ > REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp) > @@ -606,12 +659,36 @@ memcmp: > CLEAR_MDT t0 > .endm > > +.macro TRAP_SAVE_NMI_INFO > + /* > + * Save NMI trap info (MNCAUSE, no MNTVAL in spec) > + * Note: Trap info structure has generic field names (cause, tval, etc.), > + * we store MN* CSR values into these same structure fields. > + */ > + csrr t0, CSR_MNCAUSE > + REG_S t0, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(cause))(sp) > + /* MNTVAL doesn't exist in SMRNMI spec */ > + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tval))(sp) > + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tval2))(sp) > + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tinst))(sp) > + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(gva))(sp) > + > + /* We are ready to take another trap, clear MDT */ > + CLEAR_MDT t0 > +.endm > + > .macro TRAP_CALL_C_ROUTINE > /* Call C routine */ > add a0, sp, zero > call sbi_trap_handler > .endm > > +.macro TRAP_CALL_C_RNMI_ROUTINE > + /* Call C routine */ > + add a0, sp, zero > + call sbi_trap_rnmi_handler > +.endm > + > .macro TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 > /* Restore all general regisers except A0 and T0 */ > REG_L ra, SBI_TRAP_REGS_OFFSET(ra)(a0) > @@ -660,6 +737,19 @@ memcmp: > csrw CSR_MEPC, t0 > .endm > > +.macro TRAP_RESTORE_MNEPC_MNSTATUS > + /* > + * Restore MNSTATUS and MNEPC CSRs (for RNMI) > + * Note: Load from generic structure fields (mstatus, mepc) and > + * restore to NMI-specific CSRs (MNSTATUS, MNEPC). > + * No MNSTATUSH in SMRNMI spec. > + */ > + REG_L t0, SBI_TRAP_REGS_OFFSET(mstatus)(a0) > + csrw CSR_MNSTATUS, t0 > + REG_L t0, SBI_TRAP_REGS_OFFSET(mepc)(a0) > + csrw CSR_MNEPC, t0 > +.endm > + > .macro TRAP_RESTORE_A0_T0 > /* Restore T0 */ > REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(a0) > @@ -724,6 +814,37 @@ _trap_handler_hyp: > > mret > > + .section .entry, "ax", %progbits > + .align 3 > + .globl _trap_rnmi_handler > +_trap_rnmi_handler: > + /* > + * NMI interrupt handler using MN* CSRs > + * > + * Context detection via MNPP (previous privilege mode): > + * - If MNPP < M-mode: use exception stack (TP) > + * - If MNPP == M-mode: use current stack (SP) > + * This handles nested interrupt cases. > + */ > + TRAP_SAVE_AND_SETUP_SP_T0_NMI > + > + TRAP_SAVE_MNEPC_MNSTATUS 0 > + > + TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 > + > + TRAP_SAVE_NMI_INFO > + > + TRAP_CALL_C_RNMI_ROUTINE > + > + TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 > + > + TRAP_RESTORE_MNEPC_MNSTATUS > + > + TRAP_RESTORE_A0_T0 > + > + /* mnret - return from NMI (SMRNMI extension) */ > + .word 0x70200073 > + > .section .entry, "ax", %progbits > .align 3 > .globl _reset_regs > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > index e65d9877..715df499 100644 > --- a/include/sbi/sbi_platform.h > +++ b/include/sbi/sbi_platform.h > @@ -149,6 +149,10 @@ struct sbi_platform_operations { > unsigned long log2len); > /** platform specific pmp disable on current HART */ > void (*pmp_disable)(unsigned int n); > + > + /** platform specific Smrnmi NMI handler. > + * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ > + int (*rnmi_handler)(struct sbi_trap_context *tcntx); > }; > > /** Platform default per-HART stack size for exception/interrupt handling */ > diff --git a/include/sbi/sbi_trap.h b/include/sbi/sbi_trap.h > index 731a0c98..091a2446 100644 > --- a/include/sbi/sbi_trap.h > +++ b/include/sbi/sbi_trap.h > @@ -289,6 +289,8 @@ static inline void sbi_trap_set_context(struct sbi_scratch *scratch, > > struct sbi_trap_context *sbi_trap_handler(struct sbi_trap_context *tcntx); > > +struct sbi_trap_context *sbi_trap_rnmi_handler(struct sbi_trap_context *tcntx); > + > #endif > > #endif > diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c > index f41db4d1..1e55b885 100644 > --- a/lib/sbi/sbi_trap.c > +++ b/lib/sbi/sbi_trap.c > @@ -20,6 +20,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -375,3 +376,41 @@ trap_done: > sbi_trap_set_context(scratch, tcntx->prev_context); > return tcntx; > } > + > +/** > + * Default Resumable NMI (RNMI) handler > + * > + * This function is called from the _trap_rnmi_handler assembly code. > + * It provides a simple wrapper that calls the platform-specific > + * NMI handler if registered. If no handler is registered, it prints > + * diagnostic information and hangs, similar to unhandled traps. > + * > + * Note: The trap context stores NMI CSR values (MNCAUSE, MNEPC, MNSTATUS) > + * in the generic trap context fields (cause, mepc, mstatus). > + * > + * @param tcntx Pointer to trap context (saved on stack) > + * @return Same trap context pointer (needed for restore macros) > + */ > +struct sbi_trap_context *sbi_trap_rnmi_handler(struct sbi_trap_context *tcntx) > +{ > + int rc; > + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); > + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); > + > + /* Call platform-specific NMI handler if registered */ > + if (ops && ops->rnmi_handler) { > + rc = ops->rnmi_handler(tcntx); > + if (rc) { > + /* Platform handler failed to handle NMI */ > + sbi_trap_error("platform NMI handler failed", rc, tcntx); > + } > + return tcntx; > + } > + > + /* No platform handler - treat as unhandled NMI */ > + sbi_trap_error("unhandled NMI (no platform rnmi_handler)", > + SBI_ENOTSUPP, tcntx); > + > + /* Never returns */ > + return tcntx; > +} > -- > 2.43.0 > From anup at brainfault.org Mon May 4 02:00:53 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 4 May 2026 14:30:53 +0530 Subject: [PATCH v2 4/5] lib: sbi: Create a spot to place Smrnmi detection before traps and after DT is ready In-Reply-To: <20260501211627.3293126-5-evvoevod@tenstorrent.com> References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> <20260501211627.3293126-5-evvoevod@tenstorrent.com> Message-ID: On Sat, May 2, 2026 at 2:46?AM Evgeny Voevodin wrote: > > Since Smrnmi is detected from the device tree, move > sbi_platform_extensions_init() before trap-based feature detection so > the extension is known when the next commit acts on it. > > The Zkr seed-CSR access can trap if Zkr is not implemented. On Smrnmi > platforms NMIE=0 by default after reset, which routes that trap to > NMEVEC rather than MTVEC. Until Smrnmi handlers are installed and > NMIE=1, no NMEVEC handler is in place, so an early-boot trap there > would crash. Refactor the inline seed loop in fw_base.S into a > callable __stack_chk_guard_init function and invoke it from > init_coldboot() after sbi_hart_init() returns; the next commit makes > sbi_hart_init() install Smrnmi handlers and set NMIE=1, so by the time > __stack_chk_guard_init runs the trap path is safe. > > Signed-off-by: Evgeny Voevodin > --- > firmware/fw_base.S | 64 +++++++++++++++++++++++++++++----------------- > lib/sbi/sbi_hart.c | 16 +++++++----- > lib/sbi/sbi_init.c | 10 ++++++++ > 3 files changed, 60 insertions(+), 30 deletions(-) > > diff --git a/firmware/fw_base.S b/firmware/fw_base.S > index 043de7d7..ebecfecc 100644 > --- a/firmware/fw_base.S > +++ b/firmware/fw_base.S > @@ -107,30 +107,6 @@ _bss_zero: > add s4, s4, __SIZEOF_POINTER__ > blt s4, s5, _bss_zero > > - /* Trying to initialize the stack guard via the Zkr extension */ > - lla t0, __stack_chk_guard_done > - csrw CSR_MTVEC, t0 > - li t0, 0 > - li t3, SEED_OPTS_ES16 > - li t4, SEED_ENTROPY_MASK > - li t5, __SIZEOF_POINTER__ > -__stack_chk_guard_loop: > - csrrw t1, CSR_SEED, x0 > - li t2, SEED_OPTS_MASK > - and t2, t2, t1 > - bgtu t2, t3, __stack_chk_guard_done > - bltu t2, t3, __stack_chk_guard_loop > - and t1, t1, t4 > - slli t0, t0, 16 > - or t0, t0, t1 > - addi t5, t5, -2 > - bgtz t5, __stack_chk_guard_loop > - lla t1, __stack_chk_guard > - REG_S t0, 0(t1) > - j __stack_chk_guard_done > - .align 3 > -__stack_chk_guard_done: > - > /* Setup temporary trap handler */ > lla s4, _start_hang > csrw CSR_MTVEC, s4 > @@ -895,6 +871,46 @@ __stack_chk_fail: > la a0, .Lstack_corrupt_msg > call sbi_panic > > + /* > + * Initialize __stack_chk_guard from the Zkr seed CSR. Called from C > + * after Smrnmi has been enabled (NMIE=1), so that a missing-Zkr > + * illegal-instruction trap is delivered to MTVEC normally and is > + * caught by our temporary MTVEC redirect below. > + */ > + .align 3 > + .globl __stack_chk_guard_init > + .type __stack_chk_guard_init, %function > +__stack_chk_guard_init: > + /* Save current MTVEC so we can restore it on return */ > + csrr t6, CSR_MTVEC > + > + /* Redirect MTVEC to local skip target for Zkr-not-supported case */ > + lla t0, 1f > + csrw CSR_MTVEC, t0 > + > + li t0, 0 > + li t3, SEED_OPTS_ES16 > + li t4, SEED_ENTROPY_MASK > + li t5, __SIZEOF_POINTER__ > +2: > + csrrw t1, CSR_SEED, x0 > + li t2, SEED_OPTS_MASK > + and t2, t2, t1 > + bgtu t2, t3, 1f > + bltu t2, t3, 2b > + and t1, t1, t4 > + slli t0, t0, 16 > + or t0, t0, t1 > + addi t5, t5, -2 > + bgtz t5, 2b > + lla t1, __stack_chk_guard > + REG_S t0, 0(t1) > + j 1f > + .align 3 > +1: > + csrw CSR_MTVEC, t6 > + ret > + Initializing __stack_chk_guard from C code may break stack canary already pushed on stack hence it is better to initialize __stack_chk_guard in low-level code before entering C code. Instead of the approach taken by this patch, my suggestion is to set mnstatus.NMIE early on so that mtvec based trap behave normally and setup NMI trap handler later in the fw_platform_init() or nascent_init(). Something like below, can help ... diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 043de7d7..21d59e71 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -39,6 +39,16 @@ li \tmp, MSTATUS_MDT csrc CSR_MSTATUS, \tmp #endif +.endm + +.macro PARTIALLY_ENABLE_SMRNMI tmp + lla \tmp, 9999f + csrw CSR_MTVEC, \tmp + li \tmp, MNSTATUS_NMIE + csrs CSR_MNSTATUS, MNSTATUS_NMIE + j 9999f + .align 3 +9999: .endm .section .entry, "ax", %progbits @@ -107,6 +117,12 @@ _bss_zero: add s4, s4, __SIZEOF_POINTER__ blt s4, s5, _bss_zero + /* + * Partially enable Smrnmi early for boot HART so that + * normal traps get routed to mtvec trap handler. + */ + PARTIALLY_ENABLE_SMRNMI t0 + /* Trying to initialize the stack guard via the Zkr extension */ lla t0, __stack_chk_guard_done csrw CSR_MTVEC, t0 @@ -339,6 +355,12 @@ _wait_for_boot_hart: bne t0, t1, _wait_for_boot_hart _start_warm: + /* + * Partially enable Smrnmi early for current HART so that + * normal traps get routed to mtvec trap handler. + */ + PARTIALLY_ENABLE_SMRNMI t0 + /* Reset all registers except ra, a0, a1, a2, a3 and a4 for non-boot HART */ li ra, 0 call _reset_regs Regards, Anup From himanshu.chauhan at oss.qualcomm.com Mon May 4 08:00:33 2026 From: himanshu.chauhan at oss.qualcomm.com (Himanshu Chauhan) Date: Mon, 4 May 2026 20:30:33 +0530 Subject: [PATCH 02/18] dbtr: Trigger update should set sbiret.value on failure In-Reply-To: <20260313051948.4017134-3-npiggin@gmail.com> References: <20260313051948.4017134-1-npiggin@gmail.com> <20260313051948.4017134-3-npiggin@gmail.com> Message-ID: On Fri, Mar 13, 2026 at 03:19:31PM +1000, Nicholas Piggin wrote: > According to RISC-V SBI specification, trigger updates should return the > value of the problem trigger index on failure. > > Signed-off-by: Nicholas Piggin > --- > include/sbi/sbi_dbtr.h | 2 +- > lib/sbi/sbi_dbtr.c | 4 +++- > lib/sbi/sbi_ecall_dbtr.c | 2 +- > 3 files changed, 5 insertions(+), 3 deletions(-) > > diff --git a/include/sbi/sbi_dbtr.h b/include/sbi/sbi_dbtr.h > index 5e0bf84e..b8b5c9ab 100644 > --- a/include/sbi/sbi_dbtr.h > +++ b/include/sbi/sbi_dbtr.h > @@ -115,7 +115,7 @@ int sbi_dbtr_uninstall_trig(unsigned long trig_idx_base, > int sbi_dbtr_enable_trig(unsigned long trig_idx_base, > unsigned long trig_idx_mask); > int sbi_dbtr_update_trig(unsigned long smode, > - unsigned long trig_count); > + unsigned long trig_count, unsigned long *out); > int sbi_dbtr_disable_trig(unsigned long trig_idx_base, > unsigned long trig_idx_mask); > > diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c > index b0160163..8f59e407 100644 > --- a/lib/sbi/sbi_dbtr.c > +++ b/lib/sbi/sbi_dbtr.c > @@ -714,7 +714,7 @@ int sbi_dbtr_enable_trig(unsigned long trig_idx_base, > } > > int sbi_dbtr_update_trig(unsigned long smode, > - unsigned long trig_count) > + unsigned long trig_count, unsigned long *out) > { > unsigned long trig_idx; > struct sbi_dbtr_trigger *trig; > @@ -740,6 +740,7 @@ int sbi_dbtr_update_trig(unsigned long smode, > > if (trig_idx >= hs->total_trigs) { > sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > + *out = _idx; > return SBI_ERR_INVALID_PARAM; > } > > @@ -747,6 +748,7 @@ int sbi_dbtr_update_trig(unsigned long smode, > > if (!(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED))) { > sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > + *out = _idx; > return SBI_ERR_FAILED; > } > > diff --git a/lib/sbi/sbi_ecall_dbtr.c b/lib/sbi/sbi_ecall_dbtr.c > index 40a437ee..f3196feb 100644 > --- a/lib/sbi/sbi_ecall_dbtr.c > +++ b/lib/sbi/sbi_ecall_dbtr.c > @@ -43,7 +43,7 @@ static int sbi_ecall_dbtr_handler(unsigned long extid, unsigned long funcid, > ret = sbi_dbtr_enable_trig(regs->a0, regs->a1); > break; > case SBI_EXT_DBTR_TRIGGER_UPDATE: > - ret = sbi_dbtr_update_trig(smode, regs->a0); > + ret = sbi_dbtr_update_trig(smode, regs->a0, &out->value); > break; > case SBI_EXT_DBTR_TRIGGER_DISABLE: > ret = sbi_dbtr_disable_trig(regs->a0, regs->a1); Looks good. Reviewed-by: Himanshu Chauhan T/R Himanshu > -- > 2.51.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From himanshu.chauhan at oss.qualcomm.com Mon May 4 08:03:35 2026 From: himanshu.chauhan at oss.qualcomm.com (Himanshu Chauhan) Date: Mon, 4 May 2026 20:33:35 +0530 Subject: [PATCH 03/18] dbtr: Fix endian conversion in trigger install handler In-Reply-To: <20260313051948.4017134-4-npiggin@gmail.com> References: <20260313051948.4017134-1-npiggin@gmail.com> <20260313051948.4017134-4-npiggin@gmail.com> Message-ID: On Fri, Mar 13, 2026 at 03:19:32PM +1000, Nicholas Piggin wrote: > An endian conversion was missed loading tdata1 from shm. Add the > conversion and change the variable name from ctrl to tdata1 while here. > > Signed-off-by: Nicholas Piggin > --- > lib/sbi/sbi_dbtr.c | 9 +++++---- > 1 file changed, 5 insertions(+), 4 deletions(-) > > diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c > index 8f59e407..10ed6b3e 100644 > --- a/lib/sbi/sbi_dbtr.c > +++ b/lib/sbi/sbi_dbtr.c > @@ -589,7 +589,6 @@ int sbi_dbtr_install_trig(unsigned long smode, > union sbi_dbtr_shmem_entry *entry; > struct sbi_dbtr_data_msg *recv; > struct sbi_dbtr_id_msg *xmit; > - unsigned long ctrl; > struct sbi_dbtr_trigger *trig; > struct sbi_dbtr_hart_triggers_state *hs = NULL; > > @@ -609,17 +608,19 @@ int sbi_dbtr_install_trig(unsigned long smode, > > /* Check requested triggers configuration */ > for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { > + unsigned long tdata1; > + > recv = (struct sbi_dbtr_data_msg *)(&entry->data); > - ctrl = recv->tdata1; > + tdata1 = lle_to_cpu(recv->tdata1); > > - if (!dbtr_trigger_supported(TDATA1_GET_TYPE(ctrl))) { > + if (!dbtr_trigger_supported(TDATA1_GET_TYPE(tdata1))) { > *out = _idx; > sbi_hart_protection_unmap_range((unsigned long)shmem_base, > trig_count * sizeof(*entry)); > return SBI_ERR_FAILED; > } > > - if (!dbtr_trigger_valid(TDATA1_GET_TYPE(ctrl), ctrl)) { > + if (!dbtr_trigger_valid(TDATA1_GET_TYPE(tdata1), tdata1)) { > *out = _idx; > sbi_hart_protection_unmap_range((unsigned long)shmem_base, > trig_count * sizeof(*entry)); Looks good. Reviewed-by: Himanshu Chauhan Thanks Himanshu > -- > 2.51.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From himanshu.chauhan at oss.qualcomm.com Mon May 4 08:10:14 2026 From: himanshu.chauhan at oss.qualcomm.com (Himanshu Chauhan) Date: Mon, 4 May 2026 20:40:14 +0530 Subject: [PATCH 04/18] dbtr: Return correct error on install not supported In-Reply-To: <20260313051948.4017134-5-npiggin@gmail.com> References: <20260313051948.4017134-1-npiggin@gmail.com> <20260313051948.4017134-5-npiggin@gmail.com> Message-ID: On Fri, Mar 13, 2026 at 03:19:33PM +1000, Nicholas Piggin wrote: > Return SBI_ERR_NOT_SUPPORTED if a trigger is valid but not > supported. > > Signed-off-by: Nicholas Piggin > --- > lib/sbi/sbi_dbtr.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c > index 10ed6b3e..65c3b2df 100644 > --- a/lib/sbi/sbi_dbtr.c > +++ b/lib/sbi/sbi_dbtr.c > @@ -617,7 +617,7 @@ int sbi_dbtr_install_trig(unsigned long smode, > *out = _idx; > sbi_hart_protection_unmap_range((unsigned long)shmem_base, > trig_count * sizeof(*entry)); > - return SBI_ERR_FAILED; > + return SBI_ERR_NOT_SUPPORTED; > } > Looks good. Reviewed-by: Himanshu Chauhan Thanks Himanshu > if (!dbtr_trigger_valid(TDATA1_GET_TYPE(tdata1), tdata1)) { > -- > 2.51.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From himanshu.chauhan at oss.qualcomm.com Mon May 4 08:15:14 2026 From: himanshu.chauhan at oss.qualcomm.com (Himanshu Chauhan) Date: Mon, 4 May 2026 20:45:14 +0530 Subject: [PATCH 05/18] dbtr: Do not support chain bit In-Reply-To: <20260313051948.4017134-6-npiggin@gmail.com> References: <20260313051948.4017134-1-npiggin@gmail.com> <20260313051948.4017134-6-npiggin@gmail.com> Message-ID: On Fri, Mar 13, 2026 at 03:19:34PM +1000, Nicholas Piggin wrote: > There is no chain bit validation in in SBI, so do not support it. > > Signed-off-by: Nicholas Piggin > --- > lib/sbi/sbi_dbtr.c | 14 ++++++++++++-- > 1 file changed, 12 insertions(+), 2 deletions(-) > > diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c > index 65c3b2df..224f2350 100644 > --- a/lib/sbi/sbi_dbtr.c > +++ b/lib/sbi/sbi_dbtr.c > @@ -466,11 +466,21 @@ static void dbtr_trigger_clear(struct sbi_dbtr_trigger *trig) > csr_write(CSR_TDATA2, 0x0); > } > > -static int dbtr_trigger_supported(unsigned long type) > +static int dbtr_trigger_supported(unsigned long type, unsigned long tdata) > { > switch (type) { > case RISCV_DBTR_TRIG_MCONTROL: > + /* > + * SBI currently does not validate chain bit in install/update > + * so we do not support it. > + */ > + if (tdata & RV_DBTR_BIT_MASK(MC, CHAIN)) Just a minor suggestion. Since it may be supported in future, I would suggest that we print a warning message that a trigger with chain support was asked for. > + return 0; > + return 1; > case RISCV_DBTR_TRIG_MCONTROL6: > + if (tdata & RV_DBTR_BIT_MASK(MC6, CHAIN)) ditto Otherwise looks good. Reviewed-by: Himanshu Chauhan Thanks Regards Himanshu > + return 0; > + return 1; > case RISCV_DBTR_TRIG_ICOUNT: > return 1; > default: > @@ -613,7 +623,7 @@ int sbi_dbtr_install_trig(unsigned long smode, > recv = (struct sbi_dbtr_data_msg *)(&entry->data); > tdata1 = lle_to_cpu(recv->tdata1); > > - if (!dbtr_trigger_supported(TDATA1_GET_TYPE(tdata1))) { > + if (!dbtr_trigger_supported(TDATA1_GET_TYPE(tdata1), tdata1)) { > *out = _idx; > sbi_hart_protection_unmap_range((unsigned long)shmem_base, > trig_count * sizeof(*entry)); > -- > 2.51.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From himanshu.chauhan at oss.qualcomm.com Mon May 4 08:23:18 2026 From: himanshu.chauhan at oss.qualcomm.com (Himanshu Chauhan) Date: Mon, 4 May 2026 20:53:18 +0530 Subject: [PATCH 06/18] dbtr: Improve trigger update error checking In-Reply-To: <20260313051948.4017134-7-npiggin@gmail.com> References: <20260313051948.4017134-1-npiggin@gmail.com> <20260313051948.4017134-7-npiggin@gmail.com> Message-ID: On Fri, Mar 13, 2026 at 03:19:35PM +1000, Nicholas Piggin wrote: > Trigger updates should ensure all triggers can be upated without failure > before making any changes. Updates that change the trigger type must > also be disallowed according to SBI specification. > > Change the style of shmem access and checking to match the trigger > install code and perform all checks first. Add the missing check to > prevent type change. > > Signed-off-by: Nicholas Piggin > --- > lib/sbi/sbi_dbtr.c | 33 ++++++++++++++++++++++++++------- > 1 file changed, 26 insertions(+), 7 deletions(-) > > diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c > index 224f2350..d2845fec 100644 > --- a/lib/sbi/sbi_dbtr.c > +++ b/lib/sbi/sbi_dbtr.c > @@ -727,9 +727,9 @@ int sbi_dbtr_enable_trig(unsigned long trig_idx_base, > int sbi_dbtr_update_trig(unsigned long smode, > unsigned long trig_count, unsigned long *out) > { > - unsigned long trig_idx; > struct sbi_dbtr_trigger *trig; > union sbi_dbtr_shmem_entry *entry; > + struct sbi_dbtr_data_msg *recv; > void *shmem_base = NULL; > struct sbi_dbtr_hart_triggers_state *hs = NULL; > > @@ -744,30 +744,49 @@ int sbi_dbtr_update_trig(unsigned long smode, > return SBI_ERR_NO_SHMEM; > > shmem_base = hart_shmem_base(hs); > + sbi_hart_protection_map_range((unsigned long)shmem_base, > + trig_count * sizeof(*entry)); Hi Nicholas, I would suggest to verify the value of trig_count before making a large map entry. It would also help in the two loops below. > > + /* Check requested triggers configuration */ > for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { > - sbi_hart_protection_map_range((unsigned long)entry, sizeof(*entry)); > - trig_idx = entry->id.idx; > + unsigned long trig_idx, tdata1; > > + trig_idx = entry->id.idx; > if (trig_idx >= hs->total_trigs) { > - sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > *out = _idx; > + sbi_hart_protection_unmap_range((unsigned long)shmem_base, > + trig_count * sizeof(*entry)); A forward goto to bailout in success/error condition would be better. Thanks Regards Himanshu > return SBI_ERR_INVALID_PARAM; > } > > trig = INDEX_TO_TRIGGER(trig_idx); > - > if (!(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED))) { > - sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > *out = _idx; > + sbi_hart_protection_unmap_range((unsigned long)shmem_base, > + trig_count * sizeof(*entry)); > return SBI_ERR_FAILED; > } > > + recv = (struct sbi_dbtr_data_msg *)(&entry->data); > + tdata1 = lle_to_cpu(recv->tdata1); > + if (TDATA1_GET_TYPE(tdata1) != TDATA1_GET_TYPE(trig->tdata1)) { > + *out = _idx; > + sbi_hart_protection_unmap_range((unsigned long)shmem_base, > + trig_count * sizeof(*entry)); > + return SBI_ERR_INVALID_PARAM; > + } > + } > + > + /* Update triggers */ > + for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { > + trig = INDEX_TO_TRIGGER(entry->id.idx); > dbtr_trigger_setup(trig, &entry->data); > - sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > dbtr_trigger_enable(trig); > } > > + sbi_hart_protection_unmap_range((unsigned long)shmem_base, > + trig_count * sizeof(*entry)); > + > return SBI_SUCCESS; > } > > -- > 2.51.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From evvoevod at tenstorrent.com Mon May 4 09:40:15 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Mon, 4 May 2026 16:40:15 +0000 Subject: [PATCH v2 4/5] lib: sbi: Create a spot to place Smrnmi detection before traps and after DT is ready In-Reply-To: References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> <20260501211627.3293126-5-evvoevod@tenstorrent.com> Message-ID: <20260504164015.3775558-1-evvoevod@tenstorrent.com> Hi Anup, Two concerns about the suggested approach. 1) Enablement of NMIs before the platform has set up its NMI handler introduces a window of possible hang. While NMIE=1, the trap handler is not defined, so if an NMI arrives, the trap jumps to nowhere. The v2 series keeps NMIE=0 until smrnmi_handlers_init() has installed both NMIVEC and the regular trap entry, which avoids this hole. More generally: on Smrnmi-enabled platforms, there is really no way to use any trap-based mechanism until the platform has set its NMI handlers. Holding off them until after a call to the platform's smrnmi_handlers_init() is mandatory to prevent possible hangs. 2) About the stack canary concern with the C-side __stack_chk_guard_init(). The danger pattern "canary already pushed on stack" only triggers if a function call which returns is currently on stack, which means __stack_chk_guard variable will be checked against its value on prologue. Neither C nor asm functions currently on stack return or use epilogue check of __stack_chk_guard. The full call chain to __stack_chk_guard_init() consists of: asm _start -> sbi_init() __noreturn -> init_coldboot() __noreturn -> __stack_chk_guard_init() asm, no canary Even if asm side falls back to `return`, it is still `j _start_hang` which also doesn't check epilogue. Could you reconsider patch 4/5 with this context? Thanks, Evgeny From raymondmaoca at gmail.com Mon May 4 10:13:40 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Mon, 4 May 2026 13:13:40 -0400 Subject: [PATCH v2 1/3] lib: utils: irqchip: implement APLIC hwirq operation hooks Message-ID: <20260504171342.1655882-1-raymondmaoca@gmail.com> From: Raymond Mao Implement the APLIC hardware interrupt hooks used by the generic irqchip framework for M-mode direct-mode wired interrupts. Program a minimal APLIC direct-mode configuration during hwirq setup and claim external interrupts via IDC.CLAIMI. Add the helper logic needed to derive the source ID from CLAIMI, map hart index to IDC index, skip delegated interrupts, and register process_hwirqs() only for the M-mode direct-mode provider. Signed-off-by: Raymond Mao --- lib/utils/irqchip/aplic.c | 227 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c index ec69c82b..3f4991b5 100644 --- a/lib/utils/irqchip/aplic.c +++ b/lib/utils/irqchip/aplic.c @@ -245,6 +245,220 @@ static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg) return 0; } +static inline void *aplic_idc_base(unsigned long aplic_addr, u32 idc_index) +{ + return (void *)(aplic_addr + APLIC_IDC_BASE + + (unsigned long)idc_index * APLIC_IDC_SIZE); +} + +static inline struct aplic_data *aplic_irqchip_to_data(struct sbi_irqchip_device *chip) +{ + return container_of(chip, struct aplic_data, irqchip); +} + +static bool aplic_hwirq_delegated(const struct aplic_data *aplic, u32 hwirq) +{ + u32 i; + + for (i = 0; i < APLIC_MAX_DELEGATE; i++) { + const struct aplic_delegate_data *deleg = &aplic->delegate[i]; + + if (!deleg->first_irq || !deleg->last_irq) + continue; + if (deleg->first_irq <= hwirq && hwirq <= deleg->last_irq) + return true; + } + + return false; +} + +static bool aplic_mmode_direct(const struct aplic_data *aplic) +{ + return aplic->targets_mmode && aplic->num_idc; +} + +static int aplic_hartindex_to_idc_index(const struct aplic_data *aplic, + u32 hartindex) +{ + u32 i; + + if (!aplic->num_idc) + return SBI_ENODEV; + + if (aplic->idc_map) { + for (i = 0; i < aplic->num_idc; i++) { + if (aplic->idc_map[i] == hartindex) + return i; + } + + return SBI_ENODEV; + } + + if (hartindex < aplic->num_idc) + return hartindex; + + return SBI_ENODEV; +} + +static int aplic_hwirq_target_idc_index(struct sbi_irqchip_device *chip) +{ + u32 hartindex = current_hartindex(); + + if (!sbi_hartmask_test_hartindex(hartindex, &chip->target_harts)) { + sbi_hartmask_for_each_hartindex(hartindex, &chip->target_harts) + break; + if (hartindex >= SBI_HARTMASK_MAX_BITS) + return SBI_ENODEV; + } + + return aplic_hartindex_to_idc_index(aplic_irqchip_to_data(chip), + hartindex); +} + +static u32 aplic_domaincfg_value(void) +{ + u32 val = APLIC_DOMAINCFG_IE; + +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + val |= APLIC_DOMAINCFG_BE; +#endif + + return val; +} + +static void aplic_hwirq_mask(struct sbi_irqchip_device *chip, u32 hwirq) +{ + struct aplic_data *aplic = aplic_irqchip_to_data(chip); + + if (!hwirq || aplic_hwirq_delegated(aplic, hwirq)) + return; + + if (!aplic->addr || hwirq > aplic->num_source) + return; + + /* Disable source */ + writel(hwirq, (void *)(aplic->addr + APLIC_CLRIENUM)); +} + +static void aplic_hwirq_unmask(struct sbi_irqchip_device *chip, u32 hwirq) +{ + struct aplic_data *aplic = aplic_irqchip_to_data(chip); + + if (!hwirq || aplic_hwirq_delegated(aplic, hwirq)) + return; + + if (!aplic->addr || hwirq > aplic->num_source) + return; + + /* Enable source */ + writel(hwirq, (void *)(aplic->addr + APLIC_SETIENUM)); +} + +static int aplic_hwirq_claim(struct sbi_irqchip_device *chip, u32 *hwirq) +{ + struct aplic_data *aplic = aplic_irqchip_to_data(chip); + int idc_index; + void *idc; + u32 v, id; + + if (!hwirq) + return SBI_EINVAL; + + idc_index = aplic_hartindex_to_idc_index(aplic, current_hartindex()); + if (!aplic->addr || idc_index < 0) + return SBI_ENODEV; + + idc = aplic_idc_base(aplic->addr, idc_index); + + /* + * Read CLAIMI: returns TOPI value. + * ID==0 means spurious interrupt (spec-defined). + */ + v = readl(idc + APLIC_IDC_CLAIMI); /* dequeue */ + + id = (v >> APLIC_IDC_TOPI_ID_SHIFT) & APLIC_IDC_TOPI_ID_MASK; + + /* ID==0 means spurious / no pending wired interrupt */ + if (!id) + return SBI_ENOENT; + + /* Bound check against DT-discovered num_src */ + if (id > aplic->num_source) + return SBI_EINVAL; + + *hwirq = id; + + return SBI_OK; +} + +static int aplic_hwirq_setup(struct sbi_irqchip_device *chip, u32 hwirq) +{ + struct aplic_data *aplic = aplic_irqchip_to_data(chip); + unsigned long idc; + int idc_index; + + if (!hwirq || hwirq > aplic->num_source) + return SBI_EINVAL; + if (!aplic_mmode_direct(aplic)) + return SBI_ENOTSUPP; + if (aplic_hwirq_delegated(aplic, hwirq)) + return SBI_ENOTSUPP; + + idc_index = aplic_hwirq_target_idc_index(chip); + if (idc_index < 0) + return idc_index; + + idc = aplic->addr + APLIC_IDC_BASE + idc_index * APLIC_IDC_SIZE; + + /* APLIC: sourcecfg/target/enable */ + writel(APLIC_SOURCECFG_SM_LEVEL_HIGH, + (void *)(aplic->addr + APLIC_SOURCECFG_BASE + (hwirq - 1) * 4)); + + writel(((u32)idc_index << APLIC_TARGET_HART_IDX_SHIFT) | + APLIC_DEFAULT_PRIORITY, + (void *)(aplic->addr + APLIC_TARGET_BASE + (hwirq - 1) * 4)); + + writel(hwirq, (void *)(aplic->addr + APLIC_SETIENUM)); + + /* Direct mode for aia=aplic: DM=0 => don't set DM bit */ + writel(aplic_domaincfg_value(), (void *)(aplic->addr + APLIC_DOMAINCFG)); + + /* IDC delivery */ + writel(APLIC_ENABLE_IDELIVERY, (void *)(idc + APLIC_IDC_IDELIVERY)); + writel(APLIC_ENABLE_ITHRESHOLD, (void *)(idc + APLIC_IDC_ITHRESHOLD)); + + return SBI_OK; +} + +static int aplic_process_hwirqs(struct sbi_irqchip_device *chip) +{ + if (!chip) + return SBI_ENODEV; + + for (;;) { + u32 hwirq = 0; + int rc = aplic_hwirq_claim(chip, &hwirq); + + if (rc == SBI_ENOENT) + break; + if (rc) + return rc; + + if (!hwirq) + break; + + if (hwirq > chip->num_hwirq) { + return SBI_EINVAL; + } + + rc = sbi_irqchip_process_hwirq(chip, hwirq); + if (rc) + return rc; + } + + return SBI_OK; +} + int aplic_cold_irqchip_init(struct aplic_data *aplic) { int rc; @@ -308,6 +522,19 @@ int aplic_cold_irqchip_init(struct aplic_data *aplic) /* Register irqchip device */ aplic->irqchip.id = aplic->unique_id; aplic->irqchip.num_hwirq = aplic->num_source + 1; + aplic->irqchip.hwirq_mask = aplic_hwirq_mask; + aplic->irqchip.hwirq_unmask = aplic_hwirq_unmask; + /* + * Only the domain that directly injects interrupts into M-mode external + * interrupt line should provide process_hwirqs(). + * + * The other domain (e.g. S-mode) may still be registered so that its + * other ops (mask/unmask/config/etc.) can be used, but it must not + * claim to be the external interrupt line provider. + */ + if (aplic_mmode_direct(aplic)) + aplic->irqchip.process_hwirqs = aplic_process_hwirqs; + aplic->irqchip.hwirq_setup = aplic_hwirq_setup; rc = sbi_irqchip_add_device(&aplic->irqchip); if (rc) return rc; -- 2.25.1 From raymondmaoca at gmail.com Mon May 4 10:13:41 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Mon, 4 May 2026 13:13:41 -0400 Subject: [PATCH v2 2/3] [NOT-FOR-UPSTREAM] lib: utils: irqchip: add QEMU virt test for APLIC wired IRQs In-Reply-To: <20260504171342.1655882-1-raymondmaoca@gmail.com> References: <20260504171342.1655882-1-raymondmaoca@gmail.com> Message-ID: <20260504171342.1655882-2-raymondmaoca@gmail.com> From: Raymond Mao Add a QEMU virt specific test hook for validating APLIC wired external interrupt handling. When APLIC_QEMU_WIRED_TEST is enabled, keep the test path limited to hwirq 10 and only register it when the current APLIC instance is the M-mode direct-mode provider and that interrupt is not delegated to a child domain. This keeps the test path compatible with the generic APLIC changes while still allowing a DT overlay to expose an M-mode wired IRQ on QEMU virt. Signed-off-by: Raymond Mao --- Makefile | 3 ++ lib/utils/irqchip/aplic.c | 62 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/Makefile b/Makefile index 46541063..418ee1aa 100644 --- a/Makefile +++ b/Makefile @@ -399,6 +399,9 @@ CFLAGS += $(GENFLAGS) CFLAGS += $(platform-cflags-y) CFLAGS += -fPIE -pie CFLAGS += $(firmware-cflags-y) +ifeq ($(APLIC_QEMU_WIRED_TEST),y) +CFLAGS += -DAPLIC_QEMU_WIRED_TEST +endif CPPFLAGS += $(GENFLAGS) CPPFLAGS += $(platform-cppflags-y) diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c index 3f4991b5..82efdb71 100644 --- a/lib/utils/irqchip/aplic.c +++ b/lib/utils/irqchip/aplic.c @@ -112,6 +112,7 @@ #define APLIC_DEFAULT_PRIORITY 1 #define APLIC_DISABLE_IDELIVERY 0 #define APLIC_ENABLE_IDELIVERY 1 +#define APLIC_QEMU_TEST_HWIRQ 10 #define APLIC_DISABLE_ITHRESHOLD 1 #define APLIC_ENABLE_ITHRESHOLD 0 @@ -119,6 +120,41 @@ static SBI_LIST_HEAD(aplic_list); static void aplic_writel_msicfg(struct aplic_msicfg_data *msicfg, void *msicfgaddr, void *msicfgaddrH); +#ifdef APLIC_QEMU_WIRED_TEST + +#define UART_QEMU_MMIO 0x10000000UL + +static void aplic_test_uart_handler(void) +{ + volatile u8 *uart = (volatile u8 *)UART_QEMU_MMIO; + + /* Drain RX FIFO to clear the interrupt source */ + while (uart[0x05] & 0x01) { /* LSR.DR */ + u8 ch = uart[0x00]; /* RBR */ + + sbi_printf("[APLIC TEST] UART got '%c'(0x%02x)\n", + (ch >= 32 && ch < 127) ? ch : '.', ch); + } + + /* (Optional) read IIR to acknowledge on some models */ + (void)uart[0x02]; /* IIR is at offset 2 when DLAB=0; */ +} + +static void aplic_hwirq_test_run(unsigned long aplic_addr) +{ + volatile u8 *uart = (volatile u8 *)UART_QEMU_MMIO; + + /* UART: enable RX interrupt */ + uart[0x02] = 0x07; /* FCR enable+clear */ + uart[0x04] |= (1 << 3); /* MCR.OUT2 */ + uart[0x01] |= 0x01; /* IER.ERBFI */ + while (uart[0x05] & 0x01) /* drain */ + (void)uart[0x00]; + + sbi_printf("[APLIC TEST] Setup done. Type keys now.\n"); +} +#endif + static void aplic_init(struct aplic_data *aplic) { struct aplic_delegate_data *deleg; @@ -245,6 +281,20 @@ static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg) return 0; } +#ifdef APLIC_QEMU_WIRED_TEST +static int aplic_hwirq_handler(u32 hwirq, void *opaque) +{ + (void)opaque; + + sbi_printf("[APLIC] Enter registered hwirq %u raw handler callback\n", + hwirq); + + if (hwirq == 10) + aplic_test_uart_handler(); + + return SBI_OK; +} +#endif static inline void *aplic_idc_base(unsigned long aplic_addr, u32 idc_index) { return (void *)(aplic_addr + APLIC_IDC_BASE + @@ -541,6 +591,18 @@ int aplic_cold_irqchip_init(struct aplic_data *aplic) /* Attach to the aplic list */ sbi_list_add_tail(&aplic->node, &aplic_list); +#ifdef APLIC_QEMU_WIRED_TEST + if (aplic_mmode_direct(aplic) && + !aplic_hwirq_delegated(aplic, APLIC_QEMU_TEST_HWIRQ)) { + rc = sbi_irqchip_register_handler(&aplic->irqchip, + APLIC_QEMU_TEST_HWIRQ, 1, + aplic_hwirq_handler, NULL); + if (rc) + return rc; + /* Enable test in M-mode before jumping to any payload */ + aplic_hwirq_test_run(aplic->addr); + } +#endif return 0; } -- 2.25.1 From raymondmaoca at gmail.com Mon May 4 10:13:42 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Mon, 4 May 2026 13:13:42 -0400 Subject: [PATCH v2 3/3] [NOT-FOR-UPSTREAM] platform: generic: virt: add APLIC M-mode IRQ test overlay In-Reply-To: <20260504171342.1655882-1-raymondmaoca@gmail.com> References: <20260504171342.1655882-1-raymondmaoca@gmail.com> Message-ID: <20260504171342.1655882-3-raymondmaoca@gmail.com> From: Raymond Mao Stock QEMU virt,aia=aplic delegates all wired IRQs from the M-mode APLIC to its child S-mode APLIC, which prevents the local APLIC_QEMU_WIRED_TEST path from exercising M-mode wired IRQ delivery. Add a DT overlay that changes the M-mode APLIC delegation ranges from 1..96 to 1..9 and 11..96, leaving hwirq 10 local to the M-mode APLIC. This matches the local test path, which uses UART RX on hwirq 10. The overlay is intended to be applied on top of a base DTB dumped from the same QEMU virt,aia=aplic command line, so the embedded child phandle matches that dumped base DTB. Usage: - dump the base DTB from the target QEMU command line - compile this file as a DT overlay and apply it to the dumped base DTB - boot QEMU with the merged DTB and APLIC_QEMU_WIRED_TEST=y to verify M-mode wired IRQ delivery Signed-off-by: Raymond Mao --- .../virt/aplic_mmode_wired_irq_test.dts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 platform/generic/virt/aplic_mmode_wired_irq_test.dts diff --git a/platform/generic/virt/aplic_mmode_wired_irq_test.dts b/platform/generic/virt/aplic_mmode_wired_irq_test.dts new file mode 100644 index 00000000..8fb976b5 --- /dev/null +++ b/platform/generic/virt/aplic_mmode_wired_irq_test.dts @@ -0,0 +1,28 @@ +/dts-v1/; +/plugin/; + +/ { + fragment at 0 { + target-path = "/soc/interrupt-controller at c000000"; + __overlay__ { + /* + * Stock QEMU virt,aia=aplic delegates all wired IRQs from + * the M-mode APLIC to its child S-mode APLIC: + * + * + * + * Keep hwirq 10 local to the M-mode APLIC by delegating + * only 1..9 and 11..96 to the child APLIC. This matches + * the local OpenSBI APLIC_QEMU_WIRED_TEST path, which + * exercises wired IRQ delivery via hwirq 10 (UART RX). + * + * This overlay is intended to be applied on top of a base + * DTB dumped from the same QEMU virt,aia=aplic command + * line. The child phandle value below therefore matches + * that dumped base DTB. + */ + riscv,delegate = <0x04 0x01 0x09 0x04 0x0b 0x60>; + riscv,delegation = <0x04 0x01 0x09 0x04 0x0b 0x60>; + }; + }; +}; -- 2.25.1 From raymondmaoca at gmail.com Mon May 4 10:39:47 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Mon, 4 May 2026 13:39:47 -0400 Subject: [PATCH 1/2] sbi: add hardware isolation abstraction framework Message-ID: <20260504173948.1663823-1-raymondmaoca@gmail.com> From: Raymond Mao Introduce a generic hardware-isolation registration and dispatch framework for OpenSBI domains. Add boot-time init, per-domain init, domain exit, domain enter and cleanup callbacks, and store per-domain mechanism contexts in struct sbi_domain. This establishes an abstraction that allows multiple hardware isolation mechanisms to be composed while keeping core domain model and data structures independent from any single platform implementation. Signed-off-by: Raymond Mao --- include/sbi/sbi_domain.h | 4 + include/sbi/sbi_hwiso.h | 56 +++++++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_hwiso.c | 175 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 include/sbi/sbi_hwiso.h create mode 100644 lib/sbi/sbi_hwiso.c diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h index 02765777..fc7330a6 100644 --- a/include/sbi/sbi_domain.h +++ b/include/sbi/sbi_domain.h @@ -14,6 +14,7 @@ #include #include #include +#include #include struct sbi_scratch; @@ -199,6 +200,9 @@ struct sbi_domain { bool system_reset_allowed; /** Is domain allowed to suspend the system */ bool system_suspend_allowed; + /** Hardware isolation contexts for registered mechanisms */ + struct sbi_hwiso_domain_ctx *hwiso_ctxs; + u32 hwiso_ctx_count; /** Identifies whether to include the firmware region */ bool fw_region_inited; }; diff --git a/include/sbi/sbi_hwiso.h b/include/sbi/sbi_hwiso.h new file mode 100644 index 00000000..1efdb8c1 --- /dev/null +++ b/include/sbi/sbi_hwiso.h @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * System-level hardware isolation framework + * + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#ifndef __SBI_HWISO_H__ +#define __SBI_HWISO_H__ + +#include +#include + +struct sbi_hwiso_ops { + const char *name; + + /* Boot-time init */ + int (*init)(void *fdt); + + /* Per-domain init (domain_offset refers to domain instance node) */ + int (*domain_init)(void *fdt, int domain_offset, + struct sbi_domain *dom, void **ctx); + + /* Before switching away from a domain */ + void (*domain_exit)(const struct sbi_domain *src, + const struct sbi_domain *dst, void *ctx); + + /* After switching into a domain */ + void (*domain_enter)(const struct sbi_domain *dst, + const struct sbi_domain *src, void *ctx); + + /* Optional cleanup */ + void (*domain_cleanup)(struct sbi_domain *dom, void *ctx); +}; + +struct sbi_hwiso_domain_ctx { + const struct sbi_hwiso_ops *ops; + void *ctx; +}; + +int sbi_hwiso_register(const struct sbi_hwiso_ops *ops); + +int sbi_hwiso_init(void *fdt); +int sbi_hwiso_domain_init(void *fdt, int domain_offset, + struct sbi_domain *dom); + +void sbi_hwiso_domain_exit(const struct sbi_domain *src, + const struct sbi_domain *dst); +void sbi_hwiso_domain_enter(const struct sbi_domain *dst, + const struct sbi_domain *src); +void sbi_hwiso_domain_cleanup(struct sbi_domain *dom); + +#endif /* __SBI_HWISO_H__ */ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index ca312ee2..6091499a 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -72,6 +72,7 @@ libsbi-objs-y += sbi_domain.o libsbi-objs-y += sbi_emulate_csr.o libsbi-objs-y += sbi_fifo.o libsbi-objs-y += sbi_hart.o +libsbi-objs-y += sbi_hwiso.o libsbi-objs-y += sbi_heap.o libsbi-objs-y += sbi_math.o libsbi-objs-y += sbi_hfence.o diff --git a/lib/sbi/sbi_hwiso.c b/lib/sbi/sbi_hwiso.c new file mode 100644 index 00000000..6c96dc3e --- /dev/null +++ b/lib/sbi/sbi_hwiso.c @@ -0,0 +1,175 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * System-level hardware isolation framework + * + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#include +#include +#include +#include +#include + +struct sbi_hwiso_node { + const struct sbi_hwiso_ops *ops; + struct sbi_dlist node; +}; + +static SBI_LIST_HEAD(hwiso_ops_list); +static u32 hwiso_ops_count; + +static bool hwiso_ops_registered(const struct sbi_hwiso_ops *ops) +{ + struct sbi_hwiso_node *entry; + + sbi_list_for_each_entry(entry, &hwiso_ops_list, node) { + if (entry->ops == ops) + return true; + } + + return false; +} + +int sbi_hwiso_register(const struct sbi_hwiso_ops *ops) +{ + struct sbi_hwiso_node *node; + + if (!ops || !ops->name) + return SBI_EINVAL; + + if (hwiso_ops_registered(ops)) + return SBI_EALREADY; + + node = sbi_zalloc(sizeof(*node)); + if (!node) + return SBI_ENOMEM; + + node->ops = ops; + SBI_INIT_LIST_HEAD(&node->node); + sbi_list_add_tail(&node->node, &hwiso_ops_list); + hwiso_ops_count++; + + return 0; +} + +int sbi_hwiso_init(void *fdt) +{ + struct sbi_hwiso_node *entry; + int rc; + + sbi_list_for_each_entry(entry, &hwiso_ops_list, node) { + if (!entry->ops->init) + continue; + + rc = entry->ops->init(fdt); + if (rc) + return rc; + } + + return 0; +} + +int sbi_hwiso_domain_init(void *fdt, int domain_offset, + struct sbi_domain *dom) +{ + struct sbi_hwiso_node *entry; + struct sbi_hwiso_domain_ctx *ctxs; + void *ctx; + u32 idx = 0; + int rc; + + if (!dom) + return 0; + + if (!hwiso_ops_count) + return 0; + + ctxs = sbi_calloc(sizeof(*ctxs), hwiso_ops_count); + if (!ctxs) + return SBI_ENOMEM; + + dom->hwiso_ctxs = ctxs; + dom->hwiso_ctx_count = hwiso_ops_count; + + sbi_list_for_each_entry(entry, &hwiso_ops_list, node) { + ctxs[idx].ops = entry->ops; + ctxs[idx].ctx = NULL; + ctx = NULL; + + if (entry->ops->domain_init) { + rc = entry->ops->domain_init(fdt, domain_offset, + dom, &ctx); + ctxs[idx].ctx = ctx; + if (rc) { + sbi_hwiso_domain_cleanup(dom); + return rc; + } + } + + ctxs[idx].ctx = ctx; + idx++; + } + + return 0; +} + +void sbi_hwiso_domain_exit(const struct sbi_domain *src, + const struct sbi_domain *dst) +{ + u32 i; + + if (!src || !src->hwiso_ctxs) + return; + + for (i = 0; i < src->hwiso_ctx_count; i++) { + if (!src->hwiso_ctxs[i].ops || + !src->hwiso_ctxs[i].ops->domain_exit) + continue; + + src->hwiso_ctxs[i].ops->domain_exit( + src, dst, src->hwiso_ctxs[i].ctx); + } +} + +void sbi_hwiso_domain_enter(const struct sbi_domain *dst, + const struct sbi_domain *src) +{ + u32 i; + + if (!dst || !dst->hwiso_ctxs) + return; + + for (i = 0; i < dst->hwiso_ctx_count; i++) { + if (!dst->hwiso_ctxs[i].ops || + !dst->hwiso_ctxs[i].ops->domain_enter) + continue; + + dst->hwiso_ctxs[i].ops->domain_enter( + dst, src, dst->hwiso_ctxs[i].ctx); + } +} + +void sbi_hwiso_domain_cleanup(struct sbi_domain *dom) +{ + u32 i; + + if (!dom || !dom->hwiso_ctxs) + return; + + for (i = 0; i < dom->hwiso_ctx_count; i++) { + if (!dom->hwiso_ctxs[i].ops || + !dom->hwiso_ctxs[i].ops->domain_cleanup) + continue; + + dom->hwiso_ctxs[i].ops->domain_cleanup( + dom, dom->hwiso_ctxs[i].ctx); + } + + sbi_free(dom->hwiso_ctxs); + dom->hwiso_ctxs = NULL; + dom->hwiso_ctx_count = 0; +} -- 2.25.1 From raymondmaoca at gmail.com Mon May 4 10:39:48 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Mon, 4 May 2026 13:39:48 -0400 Subject: [PATCH 2/2] sbi: route domain lifecycle transitions through hwiso hooks In-Reply-To: <20260504173948.1663823-1-raymondmaoca@gmail.com> References: <20260504173948.1663823-1-raymondmaoca@gmail.com> Message-ID: <20260504173948.1663823-2-raymondmaoca@gmail.com> From: Raymond Mao Invoke HWISO boot/domain initialization during domain finalization and FDT domain population, and call mechanism exit/enter hooks around domain context switches and secondary hart bring-up. This makes hardware-isolation state follow the same lifecycle as PMP and domain assignment. Signed-off-by: Raymond Mao --- lib/sbi/sbi_domain.c | 19 +++++++++++++++++++ lib/sbi/sbi_domain_context.c | 10 ++++++++++ lib/sbi/sbi_hsm.c | 5 +++++ lib/utils/fdt/fdt_domain.c | 7 +++++++ 4 files changed, 41 insertions(+) diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c index 374ac36b..ef308925 100644 --- a/lib/sbi/sbi_domain.c +++ b/lib/sbi/sbi_domain.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include /* * We allocate an extra element because sbi_domain_for_each() expects @@ -695,6 +697,15 @@ int sbi_domain_finalize(struct sbi_scratch *scratch, u32 cold_hartid) u32 i, dhart; struct sbi_domain *dom; const struct sbi_platform *plat = sbi_platform_ptr(scratch); + void *fdt = fdt_get_address(); + + /* Configure system-level hardware isolation mechanisms at boot */ + rc = sbi_hwiso_init(fdt); + if (rc) { + sbi_printf("%s: hw isolation init failed (error %d)\n", + __func__, rc); + return rc; + } /* Initialize and populate domains for the platform */ rc = sbi_platform_domains_init(plat); @@ -704,6 +715,14 @@ int sbi_domain_finalize(struct sbi_scratch *scratch, u32 cold_hartid) return rc; } + /* Prepare root domain hwiso contexts even without DT parsing */ + rc = sbi_hwiso_domain_init(fdt, -1, &root); + if (rc) { + sbi_printf("%s: root hw isolation init failed (error %d)\n", + __func__, rc); + return rc; + } + /* Startup boot HART of domains */ sbi_domain_for_each(i, dom) { /* Domain boot HART index */ diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index a3d3988d..aa7eb01b 100755 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,9 @@ static void switch_to_next_domain_context(struct sbi_context *ctx, struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); unsigned int pmp_count = sbi_hart_pmp_count(scratch); + /* Exit first so mechanisms can tear down current domain state. */ + sbi_hwiso_domain_exit(current_dom, target_dom); + /* Assign current hart to target domain */ spin_lock(¤t_dom->assigned_harts_lock); sbi_hartmask_clear_hartindex(hartindex, ¤t_dom->assigned_harts); @@ -50,6 +54,12 @@ static void switch_to_next_domain_context(struct sbi_context *ctx, } sbi_hart_pmp_configure(scratch); + /* + * Enter after PMP reconfiguration so mechanisms can rely on the + * target domain's base isolation being in effect. + */ + sbi_hwiso_domain_enter(target_dom, current_dom); + /* Save current CSR context and restore target domain's CSR context */ ctx->sstatus = csr_swap(CSR_SSTATUS, dom_ctx->sstatus); ctx->sie = csr_swap(CSR_SIE, dom_ctx->sie); diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c index 35c84de2..69d70304 100644 --- a/lib/sbi/sbi_hsm.c +++ b/lib/sbi/sbi_hsm.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -145,6 +146,7 @@ void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch, unsigned long next_arg1; unsigned long next_addr; unsigned long next_mode; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch, hart_data_offset); @@ -157,6 +159,9 @@ void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch, next_mode = scratch->next_mode; hsm_start_ticket_release(hdata); + if (dom) + sbi_hwiso_domain_enter(dom, NULL); + sbi_hart_switch_mode(hartid, next_arg1, next_addr, next_mode, false); } diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c index fa1c3575..ac0074ed 100644 --- a/lib/utils/fdt/fdt_domain.c +++ b/lib/utils/fdt/fdt_domain.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -439,6 +440,11 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) else dom->system_suspend_allowed = false; + /* Parse per-domain hardware isolation configuration */ + err = sbi_hwiso_domain_init(fdt, domain_offset, dom); + if (err) + goto fail_free_all; + /* Find /cpus DT node */ cpus_offset = fdt_path_offset(fdt, "/cpus"); if (cpus_offset < 0) { @@ -483,6 +489,7 @@ static int __fdt_parse_domain(void *fdt, int domain_offset, void *opaque) return 0; fail_free_all: + sbi_hwiso_domain_cleanup(dom); sbi_free(mask); fail_free_regions: sbi_free(dom->regions); -- 2.25.1 From anup at brainfault.org Wed May 6 07:38:11 2026 From: anup at brainfault.org (Anup Patel) Date: Wed, 6 May 2026 20:08:11 +0530 Subject: [PATCH v2 4/5] lib: sbi: Create a spot to place Smrnmi detection before traps and after DT is ready In-Reply-To: <20260504164015.3775558-1-evvoevod@tenstorrent.com> References: <20260501211627.3293126-1-evvoevod@tenstorrent.com> <20260501211627.3293126-5-evvoevod@tenstorrent.com> <20260504164015.3775558-1-evvoevod@tenstorrent.com> Message-ID: On Mon, May 4, 2026 at 10:10?PM Evgeny Voevodin wrote: > > Hi Anup, > > Two concerns about the suggested approach. > > 1) Enablement of NMIs before the platform has set up its NMI handler > introduces a window of possible hang. While NMIE=1, the trap handler is > not defined, so if an NMI arrives, the trap jumps to nowhere. > > The v2 series keeps NMIE=0 until smrnmi_handlers_init() has installed > both NMIVEC and the regular trap entry, which avoids this hole. > > More generally: on Smrnmi-enabled platforms, there is really no way to > use any trap-based mechanism until the platform has set its NMI handlers. > Holding off them until after a call to the platform's > smrnmi_handlers_init() is mandatory to prevent possible hangs. > > 2) About the stack canary concern with the C-side __stack_chk_guard_init(). > > The danger pattern "canary already pushed on stack" only triggers if a > function call which returns is currently on stack, which means > __stack_chk_guard variable will be checked against its value on prologue. > Neither C nor asm functions currently on stack return or use epilogue > check of __stack_chk_guard. The full call chain to __stack_chk_guard_init() > consists of: > > asm _start > -> sbi_init() __noreturn > -> init_coldboot() __noreturn > -> __stack_chk_guard_init() asm, no canary > > Even if asm side falls back to `return`, it is still `j _start_hang` > which also doesn't check epilogue. > > Could you reconsider patch 4/5 with this context? > Sounds good but I still require some changes. This patch needs to be split into two patches: 1) The first patch should only move sbi_platform_extensions_init() before trap-based feature detection 2) The second patch should move Zkr based stack canary initialization into C code. No need for preserving the existing __stack_chk_guard_init() intead we can initialize __stack_chk_guard variable directly from init_coldboot() Regards, Anup From evvoevod at tenstorrent.com Thu May 7 11:08:01 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Thu, 7 May 2026 18:08:01 +0000 Subject: [PATCH v3 0/6] Add RNMI handler infrastructure for Smrnmi extension Message-ID: This is v3 of the Smrnmi series. Based on upstream/master at commit 2257e9957103 ("lib: sbi_bitmap_test: add tests for bitmap_empty()"). v1 thread: https://lore.kernel.org/opensbi/20260310183155.2186463-1-evvoevod at oss.tenstorrent.com/ v2 thread: https://lore.kernel.org/opensbi/20260501211627.3293126-1-evvoevod at tenstorrent.com/ Changes vs v2: - Split former patch 4 ("Create a spot ...") into two patches per Anup's review: * Patch 4 only moves sbi_platform_extensions_init() to the beginning of hart_detect_features(). * Patch 5 moves Zkr stack-guard initialization out of fw_base.S into init_coldboot() in C, drops the asm __stack_chk_guard_init() helper. Testing: Verified on Whisper SW system simulator and on HW emulator. Evgeny Voevodin (6): include: sbi_scratch: Add tmp1 scratch space for RNMI context saving lib: sbi: Add Smrnmi extension macros for registers and bits firmware: Add RNMI handler infrastructure lib: sbi: hart: Move device tree features detection before trap-based checks lib: sbi: Move Zkr entropy initialization from fw_base.S to init_coldboot lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection firmware/fw_base.S | 156 +++++++++++++++++++++++++++-------- include/sbi/riscv_encoding.h | 10 +++ include/sbi/sbi_hart.h | 2 + include/sbi/sbi_platform.h | 8 ++ include/sbi/sbi_scratch.h | 11 ++- include/sbi/sbi_trap.h | 2 + lib/sbi/sbi_hart.c | 37 +++++++-- lib/sbi/sbi_init.c | 31 +++++++ lib/sbi/sbi_trap.c | 39 +++++++++ 9 files changed, 254 insertions(+), 42 deletions(-) -- 2.43.0 From evvoevod at tenstorrent.com Thu May 7 11:08:03 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Thu, 7 May 2026 18:08:03 +0000 Subject: [PATCH v3 2/6] lib: sbi: Add Smrnmi extension macros for registers and bits In-Reply-To: References: Message-ID: <1c6feb6d359b9827b3c2ad8f4f0e0a4dfd1de911.1778176768.git.evvoevod@tenstorrent.com> Add CSR definitions (MNSCRATCH, MNSTATUS, MNEPC, MNCAUSE) and bit definitions (MNSTATUS_NMIE, MNSTATUS_MNPV, MNSTATUS_MNPP). Also add SBI_HART_EXT_SMRNMI to the hart extension enumeration. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- include/sbi/riscv_encoding.h | 10 ++++++++++ include/sbi/sbi_hart.h | 2 ++ lib/sbi/sbi_hart.c | 1 + 3 files changed, 13 insertions(+) diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h index 3c1d5256..18f7b4a7 100644 --- a/include/sbi/riscv_encoding.h +++ b/include/sbi/riscv_encoding.h @@ -215,6 +215,10 @@ #endif +#define MNSTATUS_NMIE (_UL(0x8)) +#define MNSTATUS_MNPV (_UL(0x80)) +#define MNSTATUS_MNPP (_UL(0x1800)) + #define MHPMEVENT_SSCOF_MASK _ULL(0xFF00000000000000) #define ENVCFG_STCE (_ULL(1) << 63) @@ -830,6 +834,12 @@ #define CSR_CUSTOM10_M_RO_BASE 0xFC0 #define CSR_CUSTOM10_M_RO_COUNT 0x040 +/* Smrnmi extension registers */ +#define CSR_MNSCRATCH 0x740 +#define CSR_MNEPC 0x741 +#define CSR_MNCAUSE 0x742 +#define CSR_MNSTATUS 0x744 + /* ===== Trap/Exception Causes ===== */ #define CAUSE_MISALIGNED_FETCH 0x0 diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index a788b34c..937cdf29 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -87,6 +87,8 @@ enum sbi_hart_extensions { SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, /** Hart has Xsfcease extension */ SBI_HART_EXT_XSIFIVE_CEASE, + /** Hart has Smrnmi extension */ + SBI_HART_EXT_SMRNMI, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 99e13990..4aefb759 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -396,6 +396,7 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), + __SBI_HART_EXT_DATA(smrnmi, SBI_HART_EXT_SMRNMI), }; _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), -- 2.43.0 From evvoevod at tenstorrent.com Thu May 7 11:08:02 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Thu, 7 May 2026 18:08:02 +0000 Subject: [PATCH v3 1/6] include: sbi_scratch: Add tmp1 scratch space for RNMI context saving In-Reply-To: References: Message-ID: <0a5d241fa1db03e71a3f56be24708cbbc8037e28.1778176768.git.evvoevod@tenstorrent.com> RNMI handlers use MNSCRATCH instead of MSCRATCH and need separate scratch space from regular trap handling. Add tmp1 for RNMI context while tmp0 remains for regular traps. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- firmware/fw_base.S | 3 ++- include/sbi/sbi_scratch.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 63bb4473..cdb15429 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -258,9 +258,10 @@ _scratch_init: /* Store hartid-to-scratch function address in scratch space */ lla a4, _hartid_to_scratch REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) - /* Clear trap_context and tmp0 in scratch space */ + /* Clear trap_context, tmp0 and tmp1 in scratch space */ REG_S zero, SBI_SCRATCH_TRAP_CONTEXT_OFFSET(tp) REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp) + REG_S zero, SBI_SCRATCH_TMP1_OFFSET(tp) /* Store firmware options in scratch space */ MOV_3R s0, a0, s1, a1, s2, a2 #ifdef FW_OPTIONS diff --git a/include/sbi/sbi_scratch.h b/include/sbi/sbi_scratch.h index 58d54628..a6edeb2d 100644 --- a/include/sbi/sbi_scratch.h +++ b/include/sbi/sbi_scratch.h @@ -40,12 +40,14 @@ #define SBI_SCRATCH_TRAP_CONTEXT_OFFSET (11 * __SIZEOF_POINTER__) /** Offset of tmp0 member in sbi_scratch */ #define SBI_SCRATCH_TMP0_OFFSET (12 * __SIZEOF_POINTER__) +/** Offset of tmp1 member in sbi_scratch */ +#define SBI_SCRATCH_TMP1_OFFSET (13 * __SIZEOF_POINTER__) /** Offset of options member in sbi_scratch */ -#define SBI_SCRATCH_OPTIONS_OFFSET (13 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_OPTIONS_OFFSET (14 * __SIZEOF_POINTER__) /** Offset of hartindex member in sbi_scratch */ -#define SBI_SCRATCH_HARTINDEX_OFFSET (14 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_HARTINDEX_OFFSET (15 * __SIZEOF_POINTER__) /** Offset of extra space in sbi_scratch */ -#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (15 * __SIZEOF_POINTER__) +#define SBI_SCRATCH_EXTRA_SPACE_OFFSET (16 * __SIZEOF_POINTER__) /** Maximum size of sbi_scratch (4KB) */ #define SBI_SCRATCH_SIZE (0x1000) @@ -83,6 +85,8 @@ struct sbi_scratch { unsigned long trap_context; /** Temporary storage */ unsigned long tmp0; + /** Temporary storage */ + unsigned long tmp1; /** Options for OpenSBI library */ unsigned long options; /** Index of the hart */ @@ -106,6 +110,7 @@ assert_member_offset(struct sbi_scratch, platform_addr, SBI_SCRATCH_PLATFORM_ADD assert_member_offset(struct sbi_scratch, hartid_to_scratch, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET); assert_member_offset(struct sbi_scratch, trap_context, SBI_SCRATCH_TRAP_CONTEXT_OFFSET); assert_member_offset(struct sbi_scratch, tmp0, SBI_SCRATCH_TMP0_OFFSET); +assert_member_offset(struct sbi_scratch, tmp1, SBI_SCRATCH_TMP1_OFFSET); assert_member_offset(struct sbi_scratch, options, SBI_SCRATCH_OPTIONS_OFFSET); assert_member_offset(struct sbi_scratch, hartindex, SBI_SCRATCH_HARTINDEX_OFFSET); -- 2.43.0 From evvoevod at tenstorrent.com Thu May 7 11:08:05 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Thu, 7 May 2026 18:08:05 +0000 Subject: [PATCH v3 4/6] lib: sbi: hart: Move device tree features detection before trap-based checks In-Reply-To: References: Message-ID: Smrnmi detection and enablement in the following commits will happen before any trap-based mechanism. As it relies on device tree, move sbi_platform_extensions_init() to the beginning of hart_detect_features(). Signed-off-by: Evgeny Voevodin --- lib/sbi/sbi_hart.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 4aefb759..781161e5 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -522,6 +522,16 @@ static int hart_detect_features(struct sbi_scratch *scratch) hfeatures->mhpm_mask = 0; hfeatures->priv_version = SBI_HART_PRIV_VER_UNKNOWN; + /* + * Parse device tree extensions early, before any trap-based checks. + * Needed to detect Smrnmi and install NMI handlers before CSR probes + * that may trigger traps. + */ + rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), + hfeatures); + if (rc) + return rc; + #define __check_hpm_csr(__csr, __mask) \ oldval = csr_read_allowed(__csr, &trap); \ if (!trap.cause) { \ @@ -676,12 +686,6 @@ __pmp_skip: #undef __check_csr_existence - /* Let platform populate extensions */ - rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), - hfeatures); - if (rc) - return rc; - /* Zicntr should only be detected using traps */ __sbi_hart_update_extension(hfeatures, SBI_HART_EXT_ZICNTR, sbi_hart_has_csr(scratch, SBI_HART_CSR_CYCLE) && -- 2.43.0 From evvoevod at tenstorrent.com Thu May 7 11:08:04 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Thu, 7 May 2026 18:08:04 +0000 Subject: [PATCH v3 3/6] firmware: Add RNMI handler infrastructure In-Reply-To: References: Message-ID: <050ae6d2762ba8d5b9dfb3cc1960a23aa3d6c549.1778176768.git.evvoevod@tenstorrent.com> Implement basic Resumable NMI (RNMI) handler support for the RISC-V Smrnmi extension. The new _trap_rnmi_handler assembly entry point saves context using the Smrnmi MN* CSRs (MNSCRATCH, MNEPC, MNSTATUS, MNCAUSE) and returns via mnret. It dispatches to sbi_trap_rnmi_handler(), which optionally calls a platform-specific ops->rnmi_handler callback for actual NMI processing. If no platform handler is registered or it fails, the event is reported as an unhandled NMI. The RNMI handler reuses the generic trap context structure but stores MN* CSR values (MNEPC, MNSTATUS, MNCAUSE) into the corresponding generic fields (mepc, mstatus, cause) for compatibility with existing trap infrastructure. Signed-off-by: Evgeny Voevodin Reviewed-by: Anup Patel --- firmware/fw_base.S | 121 +++++++++++++++++++++++++++++++++++++ include/sbi/sbi_platform.h | 4 ++ include/sbi/sbi_trap.h | 2 + lib/sbi/sbi_trap.c | 39 ++++++++++++ 4 files changed, 166 insertions(+) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index cdb15429..043de7d7 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -529,6 +529,45 @@ memcmp: csrrw tp, CSR_MSCRATCH, tp .endm +.macro TRAP_SAVE_AND_SETUP_SP_T0_NMI + /* Swap TP and MNSCRATCH (for RNMI) */ + csrrw tp, CSR_MNSCRATCH, tp + + /* Save T0 in scratch space */ + REG_S t0, SBI_SCRATCH_TMP1_OFFSET(tp) + + /* + * Set T0 to appropriate exception stack + * + * Came_From_M_Mode = ((MNSTATUS.MNPP < PRV_M) ? 1 : 0) - 1; + * Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP)) + */ + csrr t0, CSR_MNSTATUS + srl t0, t0, 11 /* MNPP is at bits 11-12 */ + and t0, t0, PRV_M + slti t0, t0, PRV_M + add t0, t0, -1 + xor sp, sp, tp + and t0, t0, sp + xor sp, sp, tp + xor t0, tp, t0 + + /* Save original SP on exception stack */ + REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_CONTEXT_SIZE)(t0) + + /* Set SP to exception stack and make room for trap context */ + add sp, t0, -(SBI_TRAP_CONTEXT_SIZE) + + /* Restore T0 from scratch space */ + REG_L t0, SBI_SCRATCH_TMP1_OFFSET(tp) + + /* Save T0 on stack */ + REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) + + /* Swap TP and MNSCRATCH */ + csrrw tp, CSR_MNSCRATCH, tp +.endm + .macro TRAP_SAVE_MEPC_MSTATUS have_mstatush /* Save MEPC and MSTATUS CSRs */ csrr t0, CSR_MEPC @@ -543,6 +582,20 @@ memcmp: .endif .endm +.macro TRAP_SAVE_MNEPC_MNSTATUS have_mstatush + /* + * Save MNEPC and MNSTATUS CSRs (for RNMI) + * Note: Trap context structure has generic field names (mepc, mstatus), + * we store MN* CSR values into these same structure fields. + */ + csrr t0, CSR_MNEPC + REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) + csrr t0, CSR_MNSTATUS + REG_S t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp) + /* MNSTATUSH doesn't exist in SMRNMI spec */ + REG_S zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) +.endm + .macro TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 /* Save all general regisers except SP and T0 */ REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp) @@ -606,12 +659,36 @@ memcmp: CLEAR_MDT t0 .endm +.macro TRAP_SAVE_NMI_INFO + /* + * Save NMI trap info (MNCAUSE, no MNTVAL in spec) + * Note: Trap info structure has generic field names (cause, tval, etc.), + * we store MN* CSR values into these same structure fields. + */ + csrr t0, CSR_MNCAUSE + REG_S t0, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(cause))(sp) + /* MNTVAL doesn't exist in SMRNMI spec */ + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tval))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tval2))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(tinst))(sp) + REG_S zero, (SBI_TRAP_REGS_SIZE + SBI_TRAP_INFO_OFFSET(gva))(sp) + + /* We are ready to take another trap, clear MDT */ + CLEAR_MDT t0 +.endm + .macro TRAP_CALL_C_ROUTINE /* Call C routine */ add a0, sp, zero call sbi_trap_handler .endm +.macro TRAP_CALL_C_RNMI_ROUTINE + /* Call C routine */ + add a0, sp, zero + call sbi_trap_rnmi_handler +.endm + .macro TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 /* Restore all general regisers except A0 and T0 */ REG_L ra, SBI_TRAP_REGS_OFFSET(ra)(a0) @@ -660,6 +737,19 @@ memcmp: csrw CSR_MEPC, t0 .endm +.macro TRAP_RESTORE_MNEPC_MNSTATUS + /* + * Restore MNSTATUS and MNEPC CSRs (for RNMI) + * Note: Load from generic structure fields (mstatus, mepc) and + * restore to NMI-specific CSRs (MNSTATUS, MNEPC). + * No MNSTATUSH in SMRNMI spec. + */ + REG_L t0, SBI_TRAP_REGS_OFFSET(mstatus)(a0) + csrw CSR_MNSTATUS, t0 + REG_L t0, SBI_TRAP_REGS_OFFSET(mepc)(a0) + csrw CSR_MNEPC, t0 +.endm + .macro TRAP_RESTORE_A0_T0 /* Restore T0 */ REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(a0) @@ -724,6 +814,37 @@ _trap_handler_hyp: mret + .section .entry, "ax", %progbits + .align 3 + .globl _trap_rnmi_handler +_trap_rnmi_handler: + /* + * NMI interrupt handler using MN* CSRs + * + * Context detection via MNPP (previous privilege mode): + * - If MNPP < M-mode: use exception stack (TP) + * - If MNPP == M-mode: use current stack (SP) + * This handles nested interrupt cases. + */ + TRAP_SAVE_AND_SETUP_SP_T0_NMI + + TRAP_SAVE_MNEPC_MNSTATUS 0 + + TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_SAVE_NMI_INFO + + TRAP_CALL_C_RNMI_ROUTINE + + TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 + + TRAP_RESTORE_MNEPC_MNSTATUS + + TRAP_RESTORE_A0_T0 + + /* mnret - return from NMI (SMRNMI extension) */ + .word 0x70200073 + .section .entry, "ax", %progbits .align 3 .globl _reset_regs diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index e65d9877..715df499 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -149,6 +149,10 @@ struct sbi_platform_operations { unsigned long log2len); /** platform specific pmp disable on current HART */ void (*pmp_disable)(unsigned int n); + + /** platform specific Smrnmi NMI handler. + * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ + int (*rnmi_handler)(struct sbi_trap_context *tcntx); }; /** Platform default per-HART stack size for exception/interrupt handling */ diff --git a/include/sbi/sbi_trap.h b/include/sbi/sbi_trap.h index 731a0c98..091a2446 100644 --- a/include/sbi/sbi_trap.h +++ b/include/sbi/sbi_trap.h @@ -289,6 +289,8 @@ static inline void sbi_trap_set_context(struct sbi_scratch *scratch, struct sbi_trap_context *sbi_trap_handler(struct sbi_trap_context *tcntx); +struct sbi_trap_context *sbi_trap_rnmi_handler(struct sbi_trap_context *tcntx); + #endif #endif diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c index f41db4d1..1e55b885 100644 --- a/lib/sbi/sbi_trap.c +++ b/lib/sbi/sbi_trap.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -375,3 +376,41 @@ trap_done: sbi_trap_set_context(scratch, tcntx->prev_context); return tcntx; } + +/** + * Default Resumable NMI (RNMI) handler + * + * This function is called from the _trap_rnmi_handler assembly code. + * It provides a simple wrapper that calls the platform-specific + * NMI handler if registered. If no handler is registered, it prints + * diagnostic information and hangs, similar to unhandled traps. + * + * Note: The trap context stores NMI CSR values (MNCAUSE, MNEPC, MNSTATUS) + * in the generic trap context fields (cause, mepc, mstatus). + * + * @param tcntx Pointer to trap context (saved on stack) + * @return Same trap context pointer (needed for restore macros) + */ +struct sbi_trap_context *sbi_trap_rnmi_handler(struct sbi_trap_context *tcntx) +{ + int rc; + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); + + /* Call platform-specific NMI handler if registered */ + if (ops && ops->rnmi_handler) { + rc = ops->rnmi_handler(tcntx); + if (rc) { + /* Platform handler failed to handle NMI */ + sbi_trap_error("platform NMI handler failed", rc, tcntx); + } + return tcntx; + } + + /* No platform handler - treat as unhandled NMI */ + sbi_trap_error("unhandled NMI (no platform rnmi_handler)", + SBI_ENOTSUPP, tcntx); + + /* Never returns */ + return tcntx; +} -- 2.43.0 From evvoevod at tenstorrent.com Thu May 7 11:08:07 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Thu, 7 May 2026 18:08:07 +0000 Subject: [PATCH v3 6/6] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection In-Reply-To: References: Message-ID: <88b1470e1e3348d454b4b995a11a85c01914f7af.1778176768.git.evvoevod@tenstorrent.com> The location of the RNMI/E trap vectors in the Smrnmi extension is implementation-defined, so platforms with vendor-specific NMI vector mechanisms must install the firmware's NMI entry points themselves. Add an smrnmi_handlers_init() callback to sbi_platform_operations that receives the firmware entry points and lets platform code install them at the hardware-specific vector locations. Two pointers are passed: - _trap_rnmi_handler: the dedicated RNMI entry point that saves context using the Smrnmi MN* CSRs and returns via mnret. - _trap_handler: the regular M-mode trap entry since RNME is taken as a regular M-mode trap with NMIE=0. When Smrnmi is present, install the platform's NMI vectors via the new callback, initialize MNSCRATCH with the per-hart scratch pointer, and set MNSTATUS.NMIE. Smrnmi-enabled platforms must register smrnmi_handlers_init; if the extension is detected but no callback is registered, sbi_panic() is called since enabling NMIs without handlers in place would route subsequent traps into nowhere. Signed-off-by: Evgeny Voevodin --- include/sbi/sbi_platform.h | 4 ++++ lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index 715df499..fe382b56 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -150,6 +150,10 @@ struct sbi_platform_operations { /** platform specific pmp disable on current HART */ void (*pmp_disable)(unsigned int n); + /** platform specific Smrnmi handlers init on current HART */ + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), + void (*rnme_handler)(void)); + /** platform specific Smrnmi NMI handler. * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ int (*rnmi_handler)(struct sbi_trap_context *tcntx); diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 781161e5..92c602aa 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -532,6 +532,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) if (rc) return rc; + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); + extern void _trap_rnmi_handler(void); + extern void _trap_handler(void); + + if (!ops || !ops->smrnmi_handlers_init) + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); + + /* Reuse _trap_handler for the RNME slot since RNME is taken + * as a regular M-mode trap with NMIE=0. */ + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); + + /* Initialize MNSCRATCH for the RNMI handler */ + csr_write(CSR_MNSCRATCH, scratch); + + /* Enable NMIs */ + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); + } + #define __check_hpm_csr(__csr, __mask) \ oldval = csr_read_allowed(__csr, &trap); \ if (!trap.cause) { \ -- 2.43.0 From evvoevod at tenstorrent.com Thu May 7 11:08:06 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Thu, 7 May 2026 18:08:06 +0000 Subject: [PATCH v3 5/6] lib: sbi: Move Zkr entropy initialization from fw_base.S to init_coldboot In-Reply-To: References: Message-ID: Current placement of entropy initialization via Zkr extension requires a trap-based mechanism to handle absent Zkr extension case. In presence of Smrnmi extension no trap-based mechanisms should be used before Smrnmi is detected and enabled otherwise trap will jump to undefined location. Move stack guard initialization into init_coldboot function body after device tree has been parsed so we know if Zkr extension is implemented by the platform which helps to avoid trap-based discovery. init_coldboot() is a safe place to initialize entropy because it doesn't return so no check of __stack_chk_guard against value on entry will be done. Signed-off-by: Evgeny Voevodin --- firmware/fw_base.S | 32 -------------------------------- lib/sbi/sbi_init.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/firmware/fw_base.S b/firmware/fw_base.S index 043de7d7..5d6540d7 100644 --- a/firmware/fw_base.S +++ b/firmware/fw_base.S @@ -107,30 +107,6 @@ _bss_zero: add s4, s4, __SIZEOF_POINTER__ blt s4, s5, _bss_zero - /* Trying to initialize the stack guard via the Zkr extension */ - lla t0, __stack_chk_guard_done - csrw CSR_MTVEC, t0 - li t0, 0 - li t3, SEED_OPTS_ES16 - li t4, SEED_ENTROPY_MASK - li t5, __SIZEOF_POINTER__ -__stack_chk_guard_loop: - csrrw t1, CSR_SEED, x0 - li t2, SEED_OPTS_MASK - and t2, t2, t1 - bgtu t2, t3, __stack_chk_guard_done - bltu t2, t3, __stack_chk_guard_loop - and t1, t1, t4 - slli t0, t0, 16 - or t0, t0, t1 - addi t5, t5, -2 - bgtz t5, __stack_chk_guard_loop - lla t1, __stack_chk_guard - REG_S t0, 0(t1) - j __stack_chk_guard_done - .align 3 -__stack_chk_guard_done: - /* Setup temporary trap handler */ lla s4, _start_hang csrw CSR_MTVEC, s4 @@ -895,14 +871,6 @@ __stack_chk_fail: la a0, .Lstack_corrupt_msg call sbi_panic - /* Initial value of the stack guard variable */ - .section .data - .align 3 - .globl __stack_chk_guard - .type __stack_chk_guard, %object -__stack_chk_guard: - RISCV_PTR 0x95B5FF5A - #ifdef FW_FDT_PATH .section .rodata .align 4 diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c index aae035e1..b248e73f 100644 --- a/lib/sbi/sbi_init.c +++ b/lib/sbi/sbi_init.c @@ -218,6 +218,8 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch) __smp_store_release(&coldboot_done, 1); } +unsigned long __stack_chk_guard = 0x95B5FF5A; + static unsigned long entry_count_offset; static unsigned long init_count_offset; @@ -269,6 +271,35 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) if (rc) sbi_hart_hang(); + /* + * Initialize stack guard via Zkr entropy source if Zkr is + * implemented according to device tree. Writing new seed value + * to __stack_chk_guard is safe here because function doesn't + * return and no check against value on entry will be done. + */ + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_ZKR)) { + unsigned long guard_val = 0; + int chunks = sizeof(unsigned long) / sizeof(uint16_t); + bool res = true; + + while (chunks) { + unsigned long seed = csr_swap(CSR_SEED, 0); + unsigned long opst = seed & SEED_OPTS_MASK; + + if (opst == SEED_OPTS_DEAD) { + res = false; + break; + } + if (opst == SEED_OPTS_ES16) { + guard_val = (guard_val << 16) | (seed & SEED_ENTROPY_MASK); + chunks--; + } + continue; + } + if (res) + __stack_chk_guard = guard_val; + } + rc = sbi_timer_init(scratch, true); if (rc) sbi_hart_hang(); -- 2.43.0 From anup at brainfault.org Thu May 7 21:53:17 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 8 May 2026 10:23:17 +0530 Subject: [PATCH v3 4/6] lib: sbi: hart: Move device tree features detection before trap-based checks In-Reply-To: References: Message-ID: On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin wrote: > > Smrnmi detection and enablement in the following commits will happen > before any trap-based mechanism. As it relies on device tree, move > sbi_platform_extensions_init() to the beginning of hart_detect_features(). > > Signed-off-by: Evgeny Voevodin LGTM. Reviewed-by: Anup Patel Regards, Anup > --- > lib/sbi/sbi_hart.c | 16 ++++++++++------ > 1 file changed, 10 insertions(+), 6 deletions(-) > > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > index 4aefb759..781161e5 100644 > --- a/lib/sbi/sbi_hart.c > +++ b/lib/sbi/sbi_hart.c > @@ -522,6 +522,16 @@ static int hart_detect_features(struct sbi_scratch *scratch) > hfeatures->mhpm_mask = 0; > hfeatures->priv_version = SBI_HART_PRIV_VER_UNKNOWN; > > + /* > + * Parse device tree extensions early, before any trap-based checks. > + * Needed to detect Smrnmi and install NMI handlers before CSR probes > + * that may trigger traps. > + */ > + rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), > + hfeatures); > + if (rc) > + return rc; > + > #define __check_hpm_csr(__csr, __mask) \ > oldval = csr_read_allowed(__csr, &trap); \ > if (!trap.cause) { \ > @@ -676,12 +686,6 @@ __pmp_skip: > > #undef __check_csr_existence > > - /* Let platform populate extensions */ > - rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), > - hfeatures); > - if (rc) > - return rc; > - > /* Zicntr should only be detected using traps */ > __sbi_hart_update_extension(hfeatures, SBI_HART_EXT_ZICNTR, > sbi_hart_has_csr(scratch, SBI_HART_CSR_CYCLE) && > -- > 2.43.0 > From anup at brainfault.org Thu May 7 22:02:17 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 8 May 2026 10:32:17 +0530 Subject: [PATCH v3 5/6] lib: sbi: Move Zkr entropy initialization from fw_base.S to init_coldboot In-Reply-To: References: Message-ID: On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin wrote: > > Current placement of entropy initialization via Zkr extension requires a > trap-based mechanism to handle absent Zkr extension case. In presence of > Smrnmi extension no trap-based mechanisms should be used before Smrnmi is > detected and enabled otherwise trap will jump to undefined location. > Move stack guard initialization into init_coldboot function body after > device tree has been parsed so we know if Zkr extension is implemented by > the platform which helps to avoid trap-based discovery. > init_coldboot() is a safe place to initialize entropy because it doesn't > return so no check of __stack_chk_guard against value on entry > will be done. > > Signed-off-by: Evgeny Voevodin LGTM. Reviewed-by: Anup Patel Regards, Anup > --- > firmware/fw_base.S | 32 -------------------------------- > lib/sbi/sbi_init.c | 31 +++++++++++++++++++++++++++++++ > 2 files changed, 31 insertions(+), 32 deletions(-) > > diff --git a/firmware/fw_base.S b/firmware/fw_base.S > index 043de7d7..5d6540d7 100644 > --- a/firmware/fw_base.S > +++ b/firmware/fw_base.S > @@ -107,30 +107,6 @@ _bss_zero: > add s4, s4, __SIZEOF_POINTER__ > blt s4, s5, _bss_zero > > - /* Trying to initialize the stack guard via the Zkr extension */ > - lla t0, __stack_chk_guard_done > - csrw CSR_MTVEC, t0 > - li t0, 0 > - li t3, SEED_OPTS_ES16 > - li t4, SEED_ENTROPY_MASK > - li t5, __SIZEOF_POINTER__ > -__stack_chk_guard_loop: > - csrrw t1, CSR_SEED, x0 > - li t2, SEED_OPTS_MASK > - and t2, t2, t1 > - bgtu t2, t3, __stack_chk_guard_done > - bltu t2, t3, __stack_chk_guard_loop > - and t1, t1, t4 > - slli t0, t0, 16 > - or t0, t0, t1 > - addi t5, t5, -2 > - bgtz t5, __stack_chk_guard_loop > - lla t1, __stack_chk_guard > - REG_S t0, 0(t1) > - j __stack_chk_guard_done > - .align 3 > -__stack_chk_guard_done: > - > /* Setup temporary trap handler */ > lla s4, _start_hang > csrw CSR_MTVEC, s4 > @@ -895,14 +871,6 @@ __stack_chk_fail: > la a0, .Lstack_corrupt_msg > call sbi_panic > > - /* Initial value of the stack guard variable */ > - .section .data > - .align 3 > - .globl __stack_chk_guard > - .type __stack_chk_guard, %object > -__stack_chk_guard: > - RISCV_PTR 0x95B5FF5A > - > #ifdef FW_FDT_PATH > .section .rodata > .align 4 > diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c > index aae035e1..b248e73f 100644 > --- a/lib/sbi/sbi_init.c > +++ b/lib/sbi/sbi_init.c > @@ -218,6 +218,8 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch) > __smp_store_release(&coldboot_done, 1); > } > > +unsigned long __stack_chk_guard = 0x95B5FF5A; > + > static unsigned long entry_count_offset; > static unsigned long init_count_offset; > > @@ -269,6 +271,35 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) > if (rc) > sbi_hart_hang(); > > + /* > + * Initialize stack guard via Zkr entropy source if Zkr is > + * implemented according to device tree. Writing new seed value > + * to __stack_chk_guard is safe here because function doesn't > + * return and no check against value on entry will be done. > + */ > + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_ZKR)) { > + unsigned long guard_val = 0; > + int chunks = sizeof(unsigned long) / sizeof(uint16_t); > + bool res = true; > + > + while (chunks) { > + unsigned long seed = csr_swap(CSR_SEED, 0); > + unsigned long opst = seed & SEED_OPTS_MASK; > + > + if (opst == SEED_OPTS_DEAD) { > + res = false; > + break; > + } > + if (opst == SEED_OPTS_ES16) { > + guard_val = (guard_val << 16) | (seed & SEED_ENTROPY_MASK); > + chunks--; > + } > + continue; > + } > + if (res) > + __stack_chk_guard = guard_val; > + } > + > rc = sbi_timer_init(scratch, true); > if (rc) > sbi_hart_hang(); > -- > 2.43.0 > From asrinivasan at oss.tenstorrent.com Fri May 8 09:32:30 2026 From: asrinivasan at oss.tenstorrent.com (Anirudh Srinivasan) Date: Fri, 08 May 2026 11:32:30 -0500 Subject: [PATCH] platform: Fix payload alignment when FW_TEXT_START isn't 2M/4M aligned Message-ID: <20260508-payload_alignment-v1-1-6628b4ec1ed3@oss.tenstorrent.com> The payload for FW_PAYLOAD needs to be placed at a 2M/4M aligned address (for 64/32 bit systems) and the current makefile uses FW_PAYLOAD_OFFSET to achieve this. This only works if FW_TEXT_START is already 2M/4M aligned. Most existing physical/virtual platforms have used a FW_TEXT_START of 0x0 or 0x80000000, so this hasn't been an issue so far. If, for example, FW_TEXT_START is 0x80000, the payload would end up placed at 0x280000 on a 64 bit system, which isn't a 2M aligned address. Update the makefile to use FW_PAYLOAD_ALIGN instead. This will ensure that the address picked for the payload is 2M/4M aligned irrespective of where FW_TEXT_START is. Signed-off-by: Anirudh Srinivasan --- To: opensbi at lists.infradead.org --- platform/generic/objects.mk | 4 ++-- platform/template/objects.mk | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk index c4a8fee2..ca7fb8b7 100644 --- a/platform/generic/objects.mk +++ b/platform/generic/objects.mk @@ -35,9 +35,9 @@ FW_JUMP_FDT_OFFSET=0x2200000 FW_PAYLOAD=y ifeq ($(PLATFORM_RISCV_XLEN), 32) # This needs to be 4MB aligned for 32-bit system - FW_PAYLOAD_OFFSET=0x400000 + FW_PAYLOAD_ALIGN=0x400000 else # This needs to be 2MB aligned for 64-bit system - FW_PAYLOAD_OFFSET=0x200000 + FW_PAYLOAD_ALIGN=0x200000 endif FW_PAYLOAD_FDT_OFFSET=$(FW_JUMP_FDT_OFFSET) diff --git a/platform/template/objects.mk b/platform/template/objects.mk index f240a557..9b4bc928 100644 --- a/platform/template/objects.mk +++ b/platform/template/objects.mk @@ -85,11 +85,11 @@ FW_PAYLOAD= # This needs to be 4MB aligned for 32-bit support # This needs to be 2MB aligned for 64-bit support ifeq ($(PLATFORM_RISCV_XLEN), 32) -FW_PAYLOAD_OFFSET=0x400000 +FW_PAYLOAD_ALIGN=0x400000 else -FW_PAYLOAD_OFFSET=0x200000 +FW_PAYLOAD_ALIGN=0x200000 endif -# FW_PAYLOAD_ALIGN=0x1000 +# FW_PAYLOAD_OFFSET=0x400000 # FW_PAYLOAD_PATH="path to next boot stage binary image file" # FW_PAYLOAD_FDT_OFFSET=0x2200000 # --- base-commit: 2257e9957103aac7df8089a59b9d4bdda7c592ce change-id: 20260508-payload_alignment-5585a86ec7e4 Best regards, -- Anirudh Srinivasan From anup at brainfault.org Fri May 8 23:50:48 2026 From: anup at brainfault.org (Anup Patel) Date: Sat, 9 May 2026 12:20:48 +0530 Subject: [PATCH v3 6/6] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection In-Reply-To: <88b1470e1e3348d454b4b995a11a85c01914f7af.1778176768.git.evvoevod@tenstorrent.com> References: <88b1470e1e3348d454b4b995a11a85c01914f7af.1778176768.git.evvoevod@tenstorrent.com> Message-ID: On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin wrote: > > The location of the RNMI/E trap vectors in the Smrnmi extension is > implementation-defined, so platforms with vendor-specific NMI vector > mechanisms must install the firmware's NMI entry points themselves. > > Add an smrnmi_handlers_init() callback to sbi_platform_operations that > receives the firmware entry points and lets platform code install them > at the hardware-specific vector locations. Two pointers are passed: > > - _trap_rnmi_handler: the dedicated RNMI entry point that saves > context using the Smrnmi MN* CSRs and returns via mnret. > - _trap_handler: the regular M-mode trap entry since RNME is taken > as a regular M-mode trap with NMIE=0. > > When Smrnmi is present, install the platform's NMI vectors via the new > callback, initialize MNSCRATCH with the per-hart scratch pointer, and > set MNSTATUS.NMIE. > > Smrnmi-enabled platforms must register smrnmi_handlers_init; if the > extension is detected but no callback is registered, sbi_panic() is > called since enabling NMIs without handlers in place would route > subsequent traps into nowhere. > > Signed-off-by: Evgeny Voevodin LGTM. Reviewed-by: Anup Patel Regards, Anup > --- > include/sbi/sbi_platform.h | 4 ++++ > lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ > 2 files changed, 24 insertions(+) > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > index 715df499..fe382b56 100644 > --- a/include/sbi/sbi_platform.h > +++ b/include/sbi/sbi_platform.h > @@ -150,6 +150,10 @@ struct sbi_platform_operations { > /** platform specific pmp disable on current HART */ > void (*pmp_disable)(unsigned int n); > > + /** platform specific Smrnmi handlers init on current HART */ > + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), > + void (*rnme_handler)(void)); > + > /** platform specific Smrnmi NMI handler. > * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ > int (*rnmi_handler)(struct sbi_trap_context *tcntx); > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > index 781161e5..92c602aa 100644 > --- a/lib/sbi/sbi_hart.c > +++ b/lib/sbi/sbi_hart.c > @@ -532,6 +532,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) > if (rc) > return rc; > > + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { > + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); > + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); > + extern void _trap_rnmi_handler(void); > + extern void _trap_handler(void); > + > + if (!ops || !ops->smrnmi_handlers_init) > + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); > + > + /* Reuse _trap_handler for the RNME slot since RNME is taken > + * as a regular M-mode trap with NMIE=0. */ > + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); > + > + /* Initialize MNSCRATCH for the RNMI handler */ > + csr_write(CSR_MNSCRATCH, scratch); > + > + /* Enable NMIs */ > + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > + } > + > #define __check_hpm_csr(__csr, __mask) \ > oldval = csr_read_allowed(__csr, &trap); \ > if (!trap.cause) { \ > -- > 2.43.0 > From anup at brainfault.org Sat May 9 00:34:59 2026 From: anup at brainfault.org (Anup Patel) Date: Sat, 9 May 2026 13:04:59 +0530 Subject: [PATCH v3 0/6] Add RNMI handler infrastructure for Smrnmi extension In-Reply-To: References: Message-ID: On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin wrote: > > This is v3 of the Smrnmi series. Based on upstream/master at commit > 2257e9957103 ("lib: sbi_bitmap_test: add tests for bitmap_empty()"). > > v1 thread: > https://lore.kernel.org/opensbi/20260310183155.2186463-1-evvoevod at oss.tenstorrent.com/ > > v2 thread: > https://lore.kernel.org/opensbi/20260501211627.3293126-1-evvoevod at tenstorrent.com/ > > Changes vs v2: > > - Split former patch 4 ("Create a spot ...") into two patches per > Anup's review: > * Patch 4 only moves sbi_platform_extensions_init() to the > beginning of hart_detect_features(). > * Patch 5 moves Zkr stack-guard initialization out of fw_base.S > into init_coldboot() in C, drops the asm > __stack_chk_guard_init() helper. > > Testing: Verified on Whisper SW system simulator and on HW emulator. > > Evgeny Voevodin (6): > include: sbi_scratch: Add tmp1 scratch space for RNMI context saving > lib: sbi: Add Smrnmi extension macros for registers and bits > firmware: Add RNMI handler infrastructure > lib: sbi: hart: Move device tree features detection before trap-based > checks > lib: sbi: Move Zkr entropy initialization from fw_base.S to > init_coldboot > lib: sbi: hart: Detect and enable Smrnmi before trap-based feature > detection > > firmware/fw_base.S | 156 +++++++++++++++++++++++++++-------- > include/sbi/riscv_encoding.h | 10 +++ > include/sbi/sbi_hart.h | 2 + > include/sbi/sbi_platform.h | 8 ++ > include/sbi/sbi_scratch.h | 11 ++- > include/sbi/sbi_trap.h | 2 + > lib/sbi/sbi_hart.c | 37 +++++++-- > lib/sbi/sbi_init.c | 31 +++++++ > lib/sbi/sbi_trap.c | 39 +++++++++ > 9 files changed, 254 insertions(+), 42 deletions(-) > > -- > 2.43.0 > Applied this series to the riscv/opensbi repo. Thanks, Anup From anup at brainfault.org Sat May 9 00:37:26 2026 From: anup at brainfault.org (Anup Patel) Date: Sat, 9 May 2026 13:07:26 +0530 Subject: [PATCH] platform: Remove kendryte/k210 platform In-Reply-To: <20260409045310.2045739-1-anup.patel@oss.qualcomm.com> References: <20260409045310.2045739-1-anup.patel@oss.qualcomm.com> Message-ID: On Thu, Apr 9, 2026 at 10:23?AM Anup Patel wrote: > > The kendryte/k210 platform does not have MMU support in S-mode hence > only NOMMU kernel which runs in M-mode can be used on this platform. > > As of now, there is no clear use-case of supporting OpenSBI for > kendryte/k210 platform. > > Signed-off-by: Anup Patel Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > README.md | 1 - > docs/platform/platform.md | 3 - > platform/kendryte/k210/Kconfig | 10 -- > platform/kendryte/k210/configs/defconfig | 0 > platform/kendryte/k210/k210.dts | 70 --------- > platform/kendryte/k210/objects.mk | 25 ---- > platform/kendryte/k210/platform.c | 176 ----------------------- > platform/kendryte/k210/platform.h | 50 ------- > scripts/create-binary-archive.sh | 1 - > 9 files changed, 336 deletions(-) > delete mode 100644 platform/kendryte/k210/Kconfig > delete mode 100644 platform/kendryte/k210/configs/defconfig > delete mode 100644 platform/kendryte/k210/k210.dts > delete mode 100644 platform/kendryte/k210/objects.mk > delete mode 100644 platform/kendryte/k210/platform.c > delete mode 100644 platform/kendryte/k210/platform.h > > diff --git a/README.md b/README.md > index fd40be0b..c19bfdde 100644 > --- a/README.md > +++ b/README.md > @@ -402,6 +402,5 @@ make I= install_docs > [Firmware Documentation]: docs/firmware/fw.md > [Domain Support]: docs/domain_support.md > [Doxygen manual]: http://www.doxygen.nl/manual/index.html > -[Kendryte standalone SDK]: https://github.com/kendryte/kendryte-standalone-sdk > [third party notices]: ThirdPartyNotices.md > [reproducible builds]: https://reproducible-builds.org > diff --git a/docs/platform/platform.md b/docs/platform/platform.md > index b77112ff..9e78dac2 100644 > --- a/docs/platform/platform.md > +++ b/docs/platform/platform.md > @@ -18,9 +18,6 @@ OpenSBI currently supports the following virtual and hardware platforms: > machine. More details on this platform can be found in the file > *[sifive_fu540.md]*. > > -* **Kendryte K210 SoC**: Platform support for the Kendryte K210 SoC used on > - boards such as the Kendryte KD233 or the Sipeed MAIX Dock. > - > * **Andes AE350 SoC**: Platform support for the Andes's SoC (AE350). More > details on this platform can be found in the file *[andes-ae350.md]*. > > diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig > deleted file mode 100644 > index 5bf59731..00000000 > --- a/platform/kendryte/k210/Kconfig > +++ /dev/null > @@ -1,10 +0,0 @@ > -# SPDX-License-Identifier: BSD-2-Clause > - > -config PLATFORM_KENDRYTE_K210 > - bool > - select FDT > - select IPI_MSWI > - select IRQCHIP_PLIC > - select SERIAL_SIFIVE > - select TIMER_MTIMER > - default y > diff --git a/platform/kendryte/k210/configs/defconfig b/platform/kendryte/k210/configs/defconfig > deleted file mode 100644 > index e69de29b..00000000 > diff --git a/platform/kendryte/k210/k210.dts b/platform/kendryte/k210/k210.dts > deleted file mode 100644 > index bcd075bf..00000000 > --- a/platform/kendryte/k210/k210.dts > +++ /dev/null > @@ -1,70 +0,0 @@ > -/* > - * SPDX-License-Identifier: BSD-2-Clause > - * > - * Copyright (c) 2019 Western Digital Corporation or its affiliates. > - * > - * Authors: > - * Damien Le Moal > - */ > - > -/dts-v1/; > -/ { > - #address-cells = <2>; > - #size-cells = <2>; > - compatible = "kendryte,k210"; > - > - chosen { > - bootargs = "console=hvc0 earlycon=sbi"; > - }; > - > - cpus { > - #address-cells = <1>; > - #size-cells = <0>; > - cpu0: cpu at 0 { > - device_type = "cpu"; > - clock-frequency = <390000000>; > - i-cache-size = <32768>; > - d-cache-size = <32768>; > - mmu-type = "none"; > - reg = <0>; > - riscv,isa = "rv64imafdc"; > - status = "okay"; > - cpu0_intc: interrupt-controller { > - #interrupt-cells = <1>; > - compatible = "riscv,cpu-intc"; > - interrupt-controller; > - }; > - }; > - cpu1: cpu at 1 { > - device_type = "cpu"; > - clock-frequency = <390000000>; > - d-cache-size = <32768>; > - i-cache-size = <32768>; > - mmu-type = "none"; > - reg = <1>; > - riscv,isa = "rv64imafdc"; > - status = "okay"; > - cpu1_intc: interrupt-controller { > - #interrupt-cells = <1>; > - compatible = "riscv,cpu-intc"; > - interrupt-controller; > - }; > - }; > - }; > - > - memory at 80000000 { > - /* Bank 0: 4 MB, Bank 1: 2 MB, AI chip SRAM: 2MB */ > - device_type = "memory"; > - reg = <0x00000000 0x80000000 0x00000000 0x00800000>; > - }; > - > - plic0: interrupt-controller at C000000 { > - #interrupt-cells = <1>; > - compatible = "riscv,plic0"; > - interrupt-controller; > - interrupts-extended = > - <&cpu0_intc 11 &cpu0_intc 9 > - &cpu1_intc 11 &cpu1_intc 9>; > - reg = <0x0 0xc000000 0x0 0x4000000>; > - }; > -}; > diff --git a/platform/kendryte/k210/objects.mk b/platform/kendryte/k210/objects.mk > deleted file mode 100644 > index efac3d2f..00000000 > --- a/platform/kendryte/k210/objects.mk > +++ /dev/null > @@ -1,25 +0,0 @@ > -# > -# SPDX-License-Identifier: BSD-2-Clause > -# > -# Copyright (c) 2019 Western Digital Corporation or its affiliates. > -# > -# Authors: > -# Damien Le Moal > -# > - > -# Compiler flags > -platform-cppflags-y = > -platform-cflags-y = > -platform-asflags-y = > -platform-ldflags-y = > - > -# Objects to build > -platform-objs-y += platform.o > - > -platform-objs-y += k210.o > -platform-varprefix-k210.o = dt_k210 > -platform-padding-k210.o = 2048 > - > -# Blobs to build > -FW_PAYLOAD=y > -FW_PAYLOAD_ALIGN=0x1000 > diff --git a/platform/kendryte/k210/platform.c b/platform/kendryte/k210/platform.c > deleted file mode 100644 > index 0b76104c..00000000 > --- a/platform/kendryte/k210/platform.c > +++ /dev/null > @@ -1,176 +0,0 @@ > -/* > - * SPDX-License-Identifier: BSD-2-Clause > - * > - * Copyright (c) 2019 Western Digital Corporation or its affiliates. > - * > - * Authors: > - * Damien Le Moal > - */ > - > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include > -#include "platform.h" > - > -extern const char dt_k210_start[]; > - > -unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1, > - unsigned long arg2, unsigned long arg3, > - unsigned long arg4) > -{ > - return (unsigned long)&dt_k210_start[0]; > -} > - > -static struct plic_data plic = { > - .unique_id = 0, > - .addr = K210_PLIC_BASE_ADDR, > - .size = K210_PLIC_BASE_SIZE, > - .num_src = K210_PLIC_NUM_SOURCES, > - .context_map = { > - [0] = { 0, 1 }, > - [1] = { 2, 3 }, > - }, > -}; > - > -static struct aclint_mswi_data mswi = { > - .addr = K210_ACLINT_MSWI_ADDR, > - .size = ACLINT_MSWI_SIZE, > - .first_hartid = 0, > - .hart_count = K210_HART_COUNT, > -}; > - > -static struct aclint_mtimer_data mtimer = { > - .mtime_freq = K210_ACLINT_MTIMER_FREQ, > - .mtime_addr = K210_ACLINT_MTIMER_ADDR + > - ACLINT_DEFAULT_MTIME_OFFSET, > - .mtime_size = ACLINT_DEFAULT_MTIME_SIZE, > - .mtimecmp_addr = K210_ACLINT_MTIMER_ADDR + > - ACLINT_DEFAULT_MTIMECMP_OFFSET, > - .mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE, > - .first_hartid = 0, > - .hart_count = K210_HART_COUNT, > - .has_64bit_mmio = true, > -}; > - > -static u32 k210_get_clk_freq(void) > -{ > - u32 clksel0, pll0; > - u64 pll0_freq, clkr0, clkf0, clkod0, div; > - > - /* > - * If the clock selector is not set, use the base frequency. > - * Otherwise, use PLL0 frequency with a frequency divisor. > - */ > - clksel0 = k210_read_sysreg(K210_CLKSEL0); > - if (!(clksel0 & 0x1)) > - return K210_CLK0_FREQ; > - > - /* > - * Get PLL0 frequency: > - * freq = base frequency * clkf0 / (clkr0 * clkod0) > - */ > - pll0 = k210_read_sysreg(K210_PLL0); > - clkr0 = 1 + (pll0 & 0x0000000f); > - clkf0 = 1 + ((pll0 & 0x000003f0) >> 4); > - clkod0 = 1 + ((pll0 & 0x00003c00) >> 10); > - pll0_freq = clkf0 * K210_CLK0_FREQ / (clkr0 * clkod0); > - > - /* Get the frequency divisor from the clock selector */ > - div = 2ULL << ((clksel0 & 0x00000006) >> 1); > - > - return pll0_freq / div; > -} > - > -static int k210_system_reset_check(u32 type, u32 reason) > -{ > - return 1; > -} > - > -static void k210_system_reset(u32 type, u32 reason) > -{ > - u32 val; > - > - val = k210_read_sysreg(K210_RESET); > - val |= K210_RESET_MASK; > - k210_write_sysreg(val, K210_RESET); > - > - while (1); > -} > - > -static struct sbi_system_reset_device k210_reset = { > - .name = "kendryte_k210_reset", > - .system_reset_check = k210_system_reset_check, > - .system_reset = k210_system_reset > -}; > - > -static int k210_early_init(bool cold_boot) > -{ > - int rc; > - > - if (!cold_boot) > - return 0; > - > - sbi_system_reset_add_device(&k210_reset); > - > - rc = sifive_uart_init(K210_UART_BASE_ADDR, k210_get_clk_freq(), > - K210_UART_BAUDRATE); > - if (rc) > - return rc; > - > - return aclint_mswi_cold_init(&mswi); > -} > - > -static int k210_final_init(bool cold_boot) > -{ > - void *fdt; > - > - if (!cold_boot) > - return 0; > - > - fdt = fdt_get_address_rw(); > - > - fdt_cpu_fixup(fdt); > - fdt_fixups(fdt); > - > - return 0; > -} > - > -static int k210_irqchip_init(void) > -{ > - return plic_cold_irqchip_init(&plic); > -} > - > -static int k210_timer_init(void) > -{ > - return aclint_mtimer_cold_init(&mtimer, NULL); > -} > - > -const struct sbi_platform_operations platform_ops = { > - .early_init = k210_early_init, > - > - .final_init = k210_final_init, > - > - .irqchip_init = k210_irqchip_init, > - > - .timer_init = k210_timer_init, > -}; > - > -const struct sbi_platform platform = { > - .opensbi_version = OPENSBI_VERSION, > - .platform_version = SBI_PLATFORM_VERSION(0x0, 0x01), > - .name = "Kendryte K210", > - .features = 0, > - .hart_count = K210_HART_COUNT, > - .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE, > - .heap_size = > - SBI_PLATFORM_DEFAULT_HEAP_SIZE(K210_HART_COUNT), > - .platform_ops_addr = (unsigned long)&platform_ops > -}; > diff --git a/platform/kendryte/k210/platform.h b/platform/kendryte/k210/platform.h > deleted file mode 100644 > index 9417403d..00000000 > --- a/platform/kendryte/k210/platform.h > +++ /dev/null > @@ -1,50 +0,0 @@ > -/* > - * SPDX-License-Identifier: BSD-2-Clause > - * > - * Copyright (c) 2019 Western Digital Corporation or its affiliates. > - * > - * Authors: > - * Damien Le Moal > - */ > -#ifndef _K210_PLATFORM_H_ > -#define _K210_PLATFORM_H_ > - > -#include > - > -#define K210_HART_COUNT 2 > - > -#define K210_UART_BAUDRATE 115200 > -#define K210_ACLINT_MTIMER_FREQ 7800000 > -#define K210_CLK0_FREQ 26000000UL > -#define K210_PLIC_NUM_SOURCES 65 > - > -/* Registers base address */ > -#define K210_SYSCTL_BASE_ADDR 0x50440000ULL > -#define K210_UART_BASE_ADDR 0x38000000ULL > -#define K210_CLINT_BASE_ADDR 0x02000000ULL > -#define K210_ACLINT_MSWI_ADDR \ > - (K210_CLINT_BASE_ADDR + CLINT_MSWI_OFFSET) > -#define K210_ACLINT_MTIMER_ADDR \ > - (K210_CLINT_BASE_ADDR + CLINT_MTIMER_OFFSET) > -#define K210_PLIC_BASE_ADDR 0x0C000000ULL > -#define K210_PLIC_BASE_SIZE (0x200000ULL + (K210_HART_COUNT * 0x1000)) > - > -/* Registers */ > -#define K210_PLL0 0x08 > -#define K210_CLKSEL0 0x20 > -#define K210_RESET 0x30 > - > -/* Register bit masks */ > -#define K210_RESET_MASK 0x01 > - > -static inline u32 k210_read_sysreg(u32 reg) > -{ > - return readl((volatile void *)(K210_SYSCTL_BASE_ADDR + reg)); > -} > - > -static inline void k210_write_sysreg(u32 val, u32 reg) > -{ > - writel(val, (volatile void *)(K210_SYSCTL_BASE_ADDR + reg)); > -} > - > -#endif /* _K210_PLATFORM_H_ */ > diff --git a/scripts/create-binary-archive.sh b/scripts/create-binary-archive.sh > index 6ea4c9c5..7f211788 100755 > --- a/scripts/create-binary-archive.sh > +++ b/scripts/create-binary-archive.sh > @@ -100,7 +100,6 @@ build_opensbi() { > 64) > # Setup 64-bit platform list > BUILD_PLATFORM_SUBDIR+=("nuclei/ux600") > - BUILD_PLATFORM_SUBDIR+=("kendryte/k210") > BUILD_PLATFORM_SUBDIR+=("generic") > ;; > *) > -- > 2.43.0 > From anup at brainfault.org Sat May 9 00:46:42 2026 From: anup at brainfault.org (Anup Patel) Date: Sat, 9 May 2026 13:16:42 +0530 Subject: [PATCH v2 0/4] Timer events for OpenSBI In-Reply-To: <20260425104048.2335262-1-anup.patel@oss.qualcomm.com> References: <20260425104048.2335262-1-anup.patel@oss.qualcomm.com> Message-ID: On Sat, Apr 25, 2026 at 4:10?PM Anup Patel wrote: > > This series extends the sbi_timer framework to support > timer events usable from any part of OpenSBI. The platform > drivers in OpenSBI can use timer events for timeouts or > periodic checks. > > These patches can also be found in sbi_timer_imp_v2 branch > at: https://github.com/avpatel/opensbi.git > > Changes since v1: > - New PATCH4 adding sbi_timer_compute_delta() and friends > - Fix typo on cleanup() comments of struct sbi_timer_event > - Remove the spin_unlock()/lock() dance from sbi_timer_event_start() > and sbi_timer_exit() > - Update timer device in sbi_timer_event_stop() only when > required (i.e. ev->hart_index != current_hartindex()) > - Break the loop in sbi_timer_process() when > ev->time_stamp > sbi_timer_value()) > - Allow callback() function in struct sbi_timer_event to > optionally provide event re-start details > > Anup Patel (4): > include: sbi: Add sbi_scratch_hartindex() macro > lib: sbi_timer: Introduce per-HART timer state > lib: sbi_timer: Add support for timer events > lib: sbi_timer: Introduce sbi_timer_compute_delta() and friends > > include/sbi/sbi_scratch.h | 6 +- > include/sbi/sbi_timer.h | 92 ++++++++++++- > lib/sbi/sbi_ecall_legacy.c | 4 +- > lib/sbi/sbi_ecall_time.c | 4 +- > lib/sbi/sbi_timer.c | 256 +++++++++++++++++++++++++++++++------ > 5 files changed, 314 insertions(+), 48 deletions(-) > > -- > 2.43.0 > Applied this series to the riscv/opensbi repo. Thanks, Anup From apatel at ventanamicro.com Sat May 9 01:33:15 2026 From: apatel at ventanamicro.com (Anup Patel) Date: Sat, 9 May 2026 14:03:15 +0530 Subject: [PATCH 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260408072123.7543-2-dave.patel@riscstar.com> References: <20260408072123.7543-1-dave.patel@riscstar.com> <20260408072123.7543-2-dave.patel@riscstar.com> Message-ID: On Wed, Apr 8, 2026 at 12:52?PM wrote: > > From: Dave Patel > > Eager context switch: Add support for saving and restoring RISC-V vector > extension state in OpenSBI. This introduces a per-hart vector context > structure and helper routines to perform full context save and restore. > > The vector context includes vcsr CSRs along with storage for all 32 vector > registers. The register state is saved and restored using byte-wise vector > load/store instructions (vs8r/vl8r). > > The implementation follows an eager context switching model where the entire > vector state is saved and restored on every context switch. This provides a > simple and deterministic mechanism without requiring lazy trap-based > management. > > Notes: > - The SBI_MAX_VLENB is configured using CONFIG_SBI_MAX_VLENB. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_vector.h | 30 ++++++++ > lib/sbi/Kconfig | 4 + > lib/sbi/objects.mk | 1 + > lib/sbi/sbi_vector.c | 155 +++++++++++++++++++++++++++++++++++++++ > 4 files changed, 190 insertions(+) > create mode 100644 include/sbi/sbi_vector.h > create mode 100644 lib/sbi/sbi_vector.c > > diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h > new file mode 100644 > index 00000000..3b63b02c > --- /dev/null > +++ b/include/sbi/sbi_vector.h > @@ -0,0 +1,30 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#ifndef __SBI_VECTOR_H__ > +#define __SBI_VECTOR_H__ > + > +#include > + > +#define SBI_MAX_VLENB CONFIG_SBI_MAX_VLENB > + > +struct sbi_vector_context { > + unsigned long vcsr; > + unsigned long vstart; > + > + /* size depends on VLEN */ > + uint8_t vregs[32 * SBI_MAX_VLENB]; We have same OpenSBI firmware running of multiple platforms so please discover max_vlenb as a hart feature in sbi_hart.c The vregs over here should be "uint8_t vregs[]" and "struct sbi_vector_context" must be dynamically allocated. > +}; > + > +void sbi_vector_save(struct sbi_vector_context *dst); > +void sbi_vector_restore(const struct sbi_vector_context *src); > +int sbi_vector_domain_init(void); > + > +#endif //__SBI_VECTOR_H__ > + > diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig > index 8479f861..b2432150 100644 > --- a/lib/sbi/Kconfig > +++ b/lib/sbi/Kconfig > @@ -74,4 +74,8 @@ config SBI_ECALL_VIRQ > bool "VIRQ extension" > default y > > +config SBI_MAX_VLENB > + int "Vector VLENB size" > + default 256 > + > endmenu > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index 68fc2036..ecb2b54e 100644 > --- a/lib/sbi/objects.mk > +++ b/lib/sbi/objects.mk > @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o > libsbi-objs-y += sbi_unpriv.o > libsbi-objs-y += sbi_expected_trap.o > libsbi-objs-y += sbi_cppc.o > +libsbi-objs-y += sbi_vector.o > diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c > new file mode 100644 > index 00000000..29434e2e > --- /dev/null > +++ b/lib/sbi/sbi_vector.c > @@ -0,0 +1,155 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#ifdef OPENSBI_CC_SUPPORT_VECTOR > + > +static inline unsigned long vector_vlenb(void) > +{ > + unsigned long vlenb = 0; > + > + asm volatile ( > + ".option push\n\t" > + ".option arch, +v\n\t" > + "csrr %0, vlenb\n\t" > + ".option pop\n\t" > + : "=r"(vlenb) > + : > + : "memory"); > + > + return vlenb; > +} > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > + if (!dst) > + return; > + > +#define READ_CSR(dst, csr) \ > + ({ \ > + asm volatile ( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " csrr %0, " #csr "\n\t" \ > + " .option pop\n\t" \ > + : "=r"(dst) \ > + : \ > + : "memory"); \ > + }) \ > + > + /* Step 1: Save CSRs */ > + READ_CSR(dst->vcsr, vcsr); > + READ_CSR(dst->vstart, vstart); use csr_read() instead of READ_CSR(). > + > +#undef READ_CSR > + > + ulong vlenb = vector_vlenb(); > + uint8_t *base = dst->vregs; > + > + /* Step 3: Save vector registers */ > +#define SAVE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vs8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + SAVE_VREG(0); > + SAVE_VREG(8); > + SAVE_VREG(16); > + SAVE_VREG(24); > + > +#undef SAVE_VREG > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > + if (!src) > + return; > + > + const uint8_t *base = src->vregs; > + ulong vlenb = vector_vlenb(); > + > + /* Step 2: Restore vector registers */ > +#define RESTORE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vl8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + RESTORE_VREG(0); > + RESTORE_VREG(8); > + RESTORE_VREG(16); > + RESTORE_VREG(24); > +#undef RESTORE_VREG > + > + /* Step 3: Restore CSR's last */ > +#define WRITE_CSR(csr, val) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " csrw " #csr ", %0\n\t" \ > + " .option pop\n\t" \ > + : \ > + : "r"(val) \ > + : "memory"); \ > + }) \ > + > + /* Restore CSRs first */ > + WRITE_CSR(vcsr, src->vcsr); > + WRITE_CSR(vstart, src->vstart); > +#undef WRITE_CSR Use csr_write() instead of WRITE_CSR(). > +} > + > +int sbi_vector_domain_init(void) > +{ > + csr_set(CSR_MSTATUS, MSTATUS_VS); > + ulong vlenb = vector_vlenb(); > + > + if (vlenb > SBI_MAX_VLENB) { > + sbi_printf("[Vector ERR:] vlenb range error\n"); > + return SBI_ERR_BAD_RANGE; > + } > + > + csr_clear(CSR_MSTATUS, MSTATUS_VS); Why this ? The mstatus_init() in sbi_hart.c sets MSTATUS_VS. Regards, Anup From anup at brainfault.org Sat May 9 08:57:18 2026 From: anup at brainfault.org (Anup Patel) Date: Sat, 9 May 2026 21:27:18 +0530 Subject: [PATCH v3] platform: generic: Tenstorrent Atlantis support In-Reply-To: <20260424062520.238403-1-npiggin@gmail.com> References: <20260424062520.238403-1-npiggin@gmail.com> Message-ID: On Fri, Apr 24, 2026 at 11:55?AM Nicholas Piggin wrote: > > Add the Tenstorrent Atlantis as a generic-platform. This initial support > enables the single_fw_region option, and verifies and prints HART PMA > CSR configuration. > > Signed-off-by: Nicholas Piggin > --- > Since v2: > - Fix silly 32-bit build issue. > > Since v1: > - Drop the IOMMU since it depends on core PMP changes. I will work > on those in parallel. > > Note the Atlantis hardware is not yet released, and QEMU models are in > the process of being upstreamed, but at the moment not complete. We > will continue to expand support and improve documentation for QEMU/HW > as we get more pieces in place. LGTM. Reviewed-by: Anup Patel Applied this patch to the riscv/opensbi repo. Thanks, Anup > > Thanks, > Nick > --- > docs/platform/generic.md | 2 + > docs/platform/tt-atlantis.md | 35 +++++ > platform/generic/Kconfig | 6 + > platform/generic/configs/defconfig | 1 + > .../generic/include/tenstorrent/ascalon.h | 12 ++ > platform/generic/include/tenstorrent/pma.h | 18 +++ > platform/generic/tenstorrent/Kconfig | 5 + > platform/generic/tenstorrent/ascalon.c | 53 +++++++ > platform/generic/tenstorrent/atlantis.c | 50 +++++++ > platform/generic/tenstorrent/objects.mk | 12 ++ > platform/generic/tenstorrent/pma.c | 138 ++++++++++++++++++ > 11 files changed, 332 insertions(+) > create mode 100644 docs/platform/tt-atlantis.md > create mode 100644 platform/generic/include/tenstorrent/ascalon.h > create mode 100644 platform/generic/include/tenstorrent/pma.h > create mode 100644 platform/generic/tenstorrent/Kconfig > create mode 100644 platform/generic/tenstorrent/ascalon.c > create mode 100644 platform/generic/tenstorrent/atlantis.c > create mode 100644 platform/generic/tenstorrent/objects.mk > create mode 100644 platform/generic/tenstorrent/pma.c > > diff --git a/docs/platform/generic.md b/docs/platform/generic.md > index c48d6a9a..0b896ede 100644 > --- a/docs/platform/generic.md > +++ b/docs/platform/generic.md > @@ -47,6 +47,7 @@ RISC-V Platforms Using Generic Platform > * **SiFive HiFive Unleashed** (*[sifive_fu540.md]*) > * **Spike** (*[spike.md]*) > * **T-HEAD C9xx series Processors** (*[thead-c9xx.md]*) > +* **Tenstorrent Atlantis Platform** (*[tt-atlantis.md]*) > * **OpenPiton FPGA SoC** (*[fpga-openpiton.md]*) > * **Ariane FPGA SoC** (*[fpga-ariane.md]*) > > @@ -57,5 +58,6 @@ RISC-V Platforms Using Generic Platform > [sifive_fu540.md]: sifive_fu540.md > [spike.md]: spike.md > [thead-c9xx.md]: thead-c9xx.md > +[tt-atlantis.md]: tt-atlantis.md > [fpga-openpiton.md]: fpga-openpiton.md > [fpga-ariane.md]: fpga-ariane.md > diff --git a/docs/platform/tt-atlantis.md b/docs/platform/tt-atlantis.md > new file mode 100644 > index 00000000..b9bdd238 > --- /dev/null > +++ b/docs/platform/tt-atlantis.md > @@ -0,0 +1,35 @@ > +Tenstorrent Atlantis Platform > +============================= > + > +The Tenstorrent Atlantis is an SoC and development board from > +Tenstorrent in partnership with CoreLab Technology. It contains 8 RISC-V > +RVA23 compliant Tenstorrent Ascalon cores with RISC-V AIA, RISC-V IOMMU, > +and a range of devices and IO connectivity. > + > +To build the platform-specific library and firmware images, provide the > +*PLATFORM=generic* parameter to the top level `make` command. > + > +Platform Options > +---------------- > + > +The *Tenstorrent Atlantis* platform does not have any platform-specific > +options. > + > +Building Tenstorrent Atlantis Platform > +-------------------------------------- > + > +The Atlantis Platform is still under development. This section will be > +expanded as firmware and support become available. > + > +QEMU support is currently being developed and initial support has been > +proposed for upstream. To run QEMU that is patched with 'tt-atlantis' > +machine support, run: > + > +``` > +qemu-system-riscv64 -M tt-atlantis -nographic \ > + -bios build/platform/generic/firmware/fw_payload.bin \ > + -kernel /Image > +``` > + > +Recent (6.18) Linux/riscv 64-bit defconfig kernels should run the QEMU > +tt-atlantis machine. > diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig > index 600273d1..d594b140 100644 > --- a/platform/generic/Kconfig > +++ b/platform/generic/Kconfig > @@ -89,6 +89,11 @@ config PLATFORM_STARFIVE_JH7110 > bool "StarFive JH7110 support" > default n > > +config PLATFORM_TENSTORRENT_ATLANTIS > + bool "Tenstorrent Atlantis support" > + select CPU_TENSTORRENT_ASCALON > + default n > + > config PLATFORM_THEAD > bool "THEAD C9xx support" > select THEAD_C9XX_ERRATA > @@ -114,6 +119,7 @@ config PLATFORM_MIPS_P8700_BOSTON > > source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig" > source "$(OPENSBI_SRC_DIR)/platform/generic/eswin/Kconfig" > +source "$(OPENSBI_SRC_DIR)/platform/generic/tenstorrent/Kconfig" > source "$(OPENSBI_SRC_DIR)/platform/generic/thead/Kconfig" > > endif > diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig > index 1d3431e7..800f3f14 100644 > --- a/platform/generic/configs/defconfig > +++ b/platform/generic/configs/defconfig > @@ -10,6 +10,7 @@ CONFIG_PLATFORM_SIFIVE_FU540=y > CONFIG_PLATFORM_SIFIVE_FU740=y > CONFIG_PLATFORM_SOPHGO_SG2042=y > CONFIG_PLATFORM_STARFIVE_JH7110=y > +CONFIG_PLATFORM_TENSTORRENT_ATLANTIS=y > CONFIG_PLATFORM_THEAD=y > CONFIG_PLATFORM_MIPS_P8700_EYEQ7H=y > CONFIG_PLATFORM_MIPS_P8700_BOSTON=y > diff --git a/platform/generic/include/tenstorrent/ascalon.h b/platform/generic/include/tenstorrent/ascalon.h > new file mode 100644 > index 00000000..5d7b7635 > --- /dev/null > +++ b/platform/generic/include/tenstorrent/ascalon.h > @@ -0,0 +1,12 @@ > +/* > + * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc. > + * SPDX-License-Identifier: BSD-2-Clause > + */ > + > +#ifndef __TENSTORRENT_ASCALON_H__ > +#define __TENSTORRENT_ASCALON_H__ > + > +void tt_ascalon_discover_pmas_from_boot_hart(void); > +void tt_ascalon_verify_pmas_nonboot_hart(void); > + > +#endif > diff --git a/platform/generic/include/tenstorrent/pma.h b/platform/generic/include/tenstorrent/pma.h > new file mode 100644 > index 00000000..051764ee > --- /dev/null > +++ b/platform/generic/include/tenstorrent/pma.h > @@ -0,0 +1,18 @@ > +/* > + * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc. > + * SPDX-License-Identifier: BSD-2-Clause > + */ > + > +#ifndef __TENSTORRENT_PMA_H__ > +#define __TENSTORRENT_PMA_H__ > + > +/* Max number of PMAs for devices (CPU, IOMMU) for Tenstorrent platforms. */ > +#define TT_MAX_PMAS 32 > + > +u64 tt_pma_get(unsigned int n); > +void tt_pma_set(unsigned int n, u64 pma); > +bool tt_pma_validate(unsigned int i, u64 pma); > +void tt_pma_print(unsigned int i, u64 pma); > + > +#endif > + > diff --git a/platform/generic/tenstorrent/Kconfig b/platform/generic/tenstorrent/Kconfig > new file mode 100644 > index 00000000..76c7fb32 > --- /dev/null > +++ b/platform/generic/tenstorrent/Kconfig > @@ -0,0 +1,5 @@ > +# SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc. > +# SPDX-License-Identifier: BSD-2-Clause > + > +config CPU_TENSTORRENT_ASCALON > + bool > diff --git a/platform/generic/tenstorrent/ascalon.c b/platform/generic/tenstorrent/ascalon.c > new file mode 100644 > index 00000000..485144cd > --- /dev/null > +++ b/platform/generic/tenstorrent/ascalon.c > @@ -0,0 +1,53 @@ > +/* > + * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc. > + * SPDX-License-Identifier: BSD-2-Clause > + */ > + > +#include > +#include > +#include > + > +#include > +#include > + > +#define CSR_PMACFG0 0x7e0 > + > +void tt_ascalon_discover_pmas_from_boot_hart(void) > +{ > + struct sbi_trap_info trap = {0}; > + > + /* Whisper virtual platform does not implement PMA */ > + csr_read_allowed(CSR_PMACFG0, &trap); > + if (trap.cause) > + return; > + > + for (unsigned int i = 0; i < TT_MAX_PMAS; i++) { > + u64 pma = csr_read_num(CSR_PMACFG0 + i); > + if (!tt_pma_validate(i, pma)) { > + sbi_printf("HART%d: Bad boot PMA%02d 0x%016lx\n", > + current_hartid(), i, pma); > + } > + tt_pma_set(i, pma); > + > + if (pma) > + tt_pma_print(i, pma); > + } > +} > + > +void tt_ascalon_verify_pmas_nonboot_hart(void) > +{ > + struct sbi_trap_info trap = {0}; > + > + /* Whisper virtual platform does not implement PMA */ > + csr_read_allowed(CSR_PMACFG0, &trap); > + if (trap.cause) > + return; > + > + for (unsigned int i = 0; i < TT_MAX_PMAS; i++) { > + u64 pma = csr_read_num(CSR_PMACFG0 + i); > + if (pma != tt_pma_get(i)) { > + sbi_printf("HART%d: Bad boot PMA%02d 0x%016lx does not match boot HART\n", > + current_hartid(), i, pma); > + } > + } > +} > diff --git a/platform/generic/tenstorrent/atlantis.c b/platform/generic/tenstorrent/atlantis.c > new file mode 100644 > index 00000000..4c312f7e > --- /dev/null > +++ b/platform/generic/tenstorrent/atlantis.c > @@ -0,0 +1,50 @@ > +/* > + * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc. > + * SPDX-License-Identifier: BSD-2-Clause > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +static int tt_atlantis_final_init(bool cold_boot) > +{ > + if (cold_boot) { > + /* Boot firmware sets HART PMAs. Read and verify them. */ > + tt_ascalon_discover_pmas_from_boot_hart(); > + } else { > + /* Verify nonboot HARTs have PMAs matching boot HART */ > + tt_ascalon_verify_pmas_nonboot_hart(); > + } > + > + return generic_final_init(cold_boot); > +} > + > +static bool tt_atlantis_single_fw_region(void) > +{ > + return true; > +} > + > +static int tt_atlantis_platform_init(const void *fdt, int nodeoff, const struct fdt_match *match) > +{ > + generic_platform_ops.final_init = tt_atlantis_final_init; > + generic_platform_ops.single_fw_region = tt_atlantis_single_fw_region; > + > + return 0; > +} > + > +static const struct fdt_match tt_atlantis_match[] = { > + { .compatible = "tenstorrent,atlantis" }, > + { }, > +}; > + > +const struct fdt_driver tenstorrent_atlantis = { > + .match_table = tt_atlantis_match, > + .init = tt_atlantis_platform_init, > +}; > diff --git a/platform/generic/tenstorrent/objects.mk b/platform/generic/tenstorrent/objects.mk > new file mode 100644 > index 00000000..17da2c5e > --- /dev/null > +++ b/platform/generic/tenstorrent/objects.mk > @@ -0,0 +1,12 @@ > +# > +# SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc. > +# SPDX-License-Identifier: BSD-2-Clause > +# > + > +ifeq ($(PLATFORM_RISCV_XLEN), 64) > +platform-objs-y += tenstorrent/pma.o > +platform-objs-$(CONFIG_CPU_TENSTORRENT_ASCALON) += tenstorrent/ascalon.o > + > +carray-platform_override_modules-$(CONFIG_PLATFORM_TENSTORRENT_ATLANTIS) += tenstorrent_atlantis > +platform-objs-$(CONFIG_PLATFORM_TENSTORRENT_ATLANTIS) += tenstorrent/atlantis.o > +endif > diff --git a/platform/generic/tenstorrent/pma.c b/platform/generic/tenstorrent/pma.c > new file mode 100644 > index 00000000..daf60192 > --- /dev/null > +++ b/platform/generic/tenstorrent/pma.c > @@ -0,0 +1,138 @@ > +/* > + * SPDX-FileCopyrightText: (c) 2025-2026 Tenstorrent USA, Inc. > + * SPDX-License-Identifier: BSD-2-Clause > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +/* > + * All PMAs in the system should be the same (after boot). The init code > + * must have set PMAs for all HARTs. > + */ > + > +/* > + * Ascalon CPU and IOMMU PMA layout: > + * Field > + * [2:0] Permission [0] Read, [1] Write, [2] Execute > + * [4:3] Memory type 00: Main memory, 01: IO memory relaxed, > + * 10: IO memory channel 0, 11: IO memory channel 1 > + * [6:5] AMO type 00: AMONone, 01: AMOSwap, > + * 10: AMOLogical, 11: AMOArithmetic > + * [7] Cacheability (main memory type) > + * 1: Cacheable, 0: Non-cacheable > + * Combining Capability (IO memory type) > + * 1: Combining allowed, 0: Combining disallowed > + * [8] Routing (coherency) > + * 1: Coherent network, 0: Non-coherent network > + * [11:9] Reserved > + * [51:12] Physical address [51:12] base > + * [63:58] Size log 2 (number of address LSB to ignore when matching) > + * 0 = invalid entry (no match) > + */ > + > +#define PMA_PERMISSION_R 0x1 > +#define PMA_PERMISSION_W 0x2 > +#define PMA_PERMISSION_X 0x4 > +#define PMA_PERMISSION_MASK 0x7 > + > +#define PMA_TYPE_MAIN_MEMORY 0x0 > +#define PMA_TYPE_IO_RELAXED 0x8 > +#define PMA_TYPE_IO_ORDERED_0 0x10 > +#define PMA_TYPE_IO_ORDERED_1 0x18 > +#define PMA_TYPE_MASK 0x18 > + > +#define PMA_AMO_NONE 0x0 > +#define PMA_AMO_SWAP 0x20 > +#define PMA_AMO_LOGICAL 0x40 > +#define PMA_AMO_ARITHMETIC 0x60 > +#define PMA_AMO_MASK 0x60 > + > +#define PMA_MEMORY_CACHEABLE 0x80 > +#define PMA_IO_COMBINING 0x80 > +#define PMA_ROUTING_COHERENT 0x100 > + > +#define PMA_FLAGS_MASK 0x00000000000001ffULL > +#define PMA_ADDRESS_MASK 0x000ffffffffff000ULL > +#define PMA_SIZE_MASK 0xfc00000000000000ULL > +#define PMA_RESERVED_MASK 0x0300000000000e00ULL > + > +#define PMA_SIZE_SHIFT 58 > + > +static u64 tt_pma_size(u64 pma) > +{ > + if ((pma & PMA_SIZE_MASK) == 0) > + return 0; > + > + return 1ULL << ((pma & PMA_SIZE_MASK) >> PMA_SIZE_SHIFT); > +} > + > +static u64 tt_pma_address(u64 pma) > +{ > + return (pma & PMA_ADDRESS_MASK) & ~((tt_pma_size(pma) - 1)); > +} > + > +bool tt_pma_validate(unsigned int i, u64 pma) > +{ > + if (!pma) > + return true; > + > + if (pma & PMA_RESERVED_MASK) { > + sbi_printf("PMA%02u 0x%016lx contains reserved bits\n", i, pma); > + return false; > + } > + > + if (tt_pma_size(pma) < 4096) { > + sbi_printf("PMA%02u 0x%016lx size < 4KB\n", i, pma); > + return false; > + } > + > + if (tt_pma_address(pma) != (pma & PMA_ADDRESS_MASK)) { > + sbi_printf("PMA%02u 0x%016lx address is not aligned to size\n", i, pma); > + return false; > + } > + > + return true; > +} > + > +void tt_pma_print(unsigned int i, u64 pma) > +{ > + sbi_printf("PMA%02d : 0x%016lx-0x%016lx perm:%s%s%s type:%s %s %s amo:%s\n", i, > + tt_pma_address(pma), tt_pma_address(pma) + tt_pma_size(pma) - 1, > + pma & PMA_PERMISSION_R ? "R" : " ", > + pma & PMA_PERMISSION_W ? "W" : " ", > + pma & PMA_PERMISSION_X ? "X" : " ", > + (pma & PMA_TYPE_MASK) == PMA_TYPE_MAIN_MEMORY ? "main-memory" : > + ((pma & PMA_TYPE_MASK) == PMA_TYPE_IO_RELAXED ? "io-relaxed" : > + ((pma & PMA_TYPE_MASK) == PMA_TYPE_IO_ORDERED_0 ? "io-ordered-0" : "io-ordered-1")), > + (pma & PMA_TYPE_MASK) == PMA_TYPE_MAIN_MEMORY ? > + (pma & PMA_MEMORY_CACHEABLE ? "cacheable" : "non-cacheable") : > + (pma & PMA_IO_COMBINING ? "combining" : "non-combining"), > + pma & PMA_ROUTING_COHERENT ? "coherent" : "non-coherent", > + (pma & PMA_AMO_MASK) == PMA_AMO_NONE ? "none" : > + ((pma & PMA_AMO_MASK) == PMA_AMO_SWAP ? "swap" : > + ((pma & PMA_AMO_MASK) == PMA_AMO_LOGICAL ? "logical" : "arithmetic"))); > +} > + > +static u64 pmas[TT_MAX_PMAS]; > + > +void tt_pma_set(unsigned int n, u64 pma) > +{ > + if (n >= TT_MAX_PMAS) > + sbi_panic("PMA exceeded TT_MAX_PMAS"); > + > + pmas[n] = pma; > +} > + > +u64 tt_pma_get(unsigned int n) > +{ > + if (n >= TT_MAX_PMAS) > + sbi_panic("PMA exceeded TT_MAX_PMAS"); > + > + return pmas[n]; > +} > -- > 2.53.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup.patel at oss.qualcomm.com Sat May 9 09:18:49 2026 From: anup.patel at oss.qualcomm.com (Anup Patel) Date: Sat, 9 May 2026 21:48:49 +0530 Subject: [PATCH] lib: utils: Fix LLVM compile error in MPXY client driver for RPMI MM Message-ID: <20260509161849.2935816-1-anup.patel@oss.qualcomm.com> The following compile error is seen with LLVM compiler: CC platform/generic/lib/utils/mpxy/fdt_mpxy_rpmi_mm.o lib/utils/mpxy/fdt_mpxy_rpmi_mm.c:17:6: error: use of GNU 'missing =' extension in designator [-Werror,-Wgnu-designator] 17 | [0] { | ^ | = lib/utils/mpxy/fdt_mpxy_rpmi_mm.c:24:6: error: use of GNU 'missing =' extension in designator [-Werror,-Wgnu-designator] 24 | [1] { | ^ | = 2 errors generated. Add missing "=" in mm_srvcdata[] array initialization to address the above issue. Fixes: 0b041e58c078 ("lib: utils: Add MPXY client driver for RPMI MM service group") Signed-off-by: Anup Patel --- lib/utils/mpxy/fdt_mpxy_rpmi_mm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c index 0163d0d2..d7176aca 100644 --- a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c +++ b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c @@ -14,14 +14,14 @@ static struct rpmi_mm_get_attributes_rsp rsp; static struct mpxy_rpmi_service_data mm_srvcdata[] = { - [0] { + [0] = { .id = RPMI_MM_SRV_GET_ATTRIBUTES, .min_tx_len = 0, .max_tx_len = 0, .min_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp), .max_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp), }, - [1] { + [1] = { .id = RPMI_MM_SRV_COMMUNICATE, .min_tx_len = sizeof(struct rpmi_mm_communicate_req), .max_tx_len = sizeof(struct rpmi_mm_communicate_req), -- 2.43.0 From linux.amoon at gmail.com Sat May 9 12:23:59 2026 From: linux.amoon at gmail.com (Anand Moon) Date: Sun, 10 May 2026 00:53:59 +0530 Subject: [PATCH 1/2] lib: utils/i2c: add minimal SpacemiT I2C driver In-Reply-To: <20260419150857.2705843-2-aurelien@aurel32.net> References: <20260419150857.2705843-1-aurelien@aurel32.net> <20260419150857.2705843-2-aurelien@aurel32.net> Message-ID: Hi Aurelien, On Sun, 19 Apr 2026 at 20:40, Aurelien Jarno wrote: > > Add a simple SpacemiT I2C driver for basic byte transfers over the I2C > bus, prioritizing simplicity over performance. The driver operates in > PIO mode and does not use interrupts, FIFO, or DMA. > > The controller is reset at the start of each transaction to ensure a > known initial state, regardless of prior configuration by the kernel. > This also avoids the need for additional error recovery code. > > This will be used for communication with onboard PMIC to reset and > power-off the board. > > Signed-off-by: Aurelien Jarno Tested-by: Anand Moon Thanks -Anand > --- > lib/utils/i2c/Kconfig | 4 + > lib/utils/i2c/fdt_i2c_spacemit.c | 220 +++++++++++++++++++++++++++++ > lib/utils/i2c/objects.mk | 3 + > platform/generic/configs/defconfig | 1 + > 4 files changed, 228 insertions(+) > create mode 100644 lib/utils/i2c/fdt_i2c_spacemit.c > > diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig > index 7fa32fcf..bdaaff62 100644 > --- a/lib/utils/i2c/Kconfig > +++ b/lib/utils/i2c/Kconfig > @@ -14,6 +14,10 @@ config FDT_I2C_SIFIVE > bool "SiFive I2C FDT driver" > default n > > +config FDT_I2C_SPACEMIT > + bool "SpacemiT I2C FDT driver" > + default n > + > config FDT_I2C_DW > bool "Synopsys Designware I2C FDT driver" > select I2C_DW > diff --git a/lib/utils/i2c/fdt_i2c_spacemit.c b/lib/utils/i2c/fdt_i2c_spacemit.c > new file mode 100644 > index 00000000..9009b6b6 > --- /dev/null > +++ b/lib/utils/i2c/fdt_i2c_spacemit.c > @@ -0,0 +1,220 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 Aurelien Jarno > + * > + * Authors: > + * Aurelien Jarno > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* Controller registers */ > +#define ICR_OFFSET 0x00 /* I2C control register */ > +#define IDBR_OFFSET 0x0c /* I2C data buffer register */ > + > +/* Control register bits */ > +#define ICR_START BIT(0) /* start */ > +#define ICR_STOP BIT(1) /* stop */ > +#define ICR_ACKNAK BIT(2) /* ACK(0) or NAK(1) */ > +#define ICR_TB BIT(3) /* transfer byte */ > +#define ICR_UR BIT(10) /* unit reset */ > +#define ICR_SCLE BIT(13) /* SCL enable */ > +#define ICR_IUE BIT(14) /* unit enable */ > + > +/* Timing */ > +#define I2C_RESET_US 10 > +#define I2C_TIMEOUT_US 1000 > + > +struct spacemit_i2c_adapter { > + unsigned long base; > + struct i2c_adapter adapter; > +}; > + > +static inline void spacemit_i2c_set_reg(struct spacemit_i2c_adapter *adap, > + uint8_t reg, uint32_t val) > +{ > + writel(val, (void *)adap->base + reg); > +} > + > +static inline uint32_t spacemit_i2c_get_reg(struct spacemit_i2c_adapter *adap, > + uint32_t reg) > +{ > + return readl((void *)adap->base + reg); > +} > + > +static void spacemit_i2c_reset(struct spacemit_i2c_adapter *adap) > +{ > + /* disable unit */ > + spacemit_i2c_set_reg(adap, ICR_OFFSET, 0); > + sbi_timer_udelay(I2C_RESET_US); > + > + /* reset unit */ > + spacemit_i2c_set_reg(adap, ICR_OFFSET, ICR_UR); > + sbi_timer_udelay(I2C_RESET_US); > + > + /* clear reset and enable unit and SCL */ > + spacemit_i2c_set_reg(adap, ICR_OFFSET, ICR_IUE | ICR_SCLE); > +} > + > +static int spacemit_i2c_wait_xfer_done(struct spacemit_i2c_adapter *adap) > +{ > + for (int i = 0; i < I2C_TIMEOUT_US; i++) { > + uint32_t val = spacemit_i2c_get_reg(adap, ICR_OFFSET); > + > + if (!(val & ICR_TB)) > + return 0; > + > + sbi_timer_udelay(1); > + }; > + > + return SBI_ETIMEDOUT; > +} > + > +static void spacemit_i2c_start_xfer(struct spacemit_i2c_adapter *adap, > + uint32_t ctrl) > +{ > + const uint32_t ctrl_mask = ICR_START | ICR_STOP | ICR_ACKNAK; > + uint32_t val; > + > + val = spacemit_i2c_get_reg(adap, ICR_OFFSET); > + val &= ~ctrl_mask; > + val |= (ctrl & ctrl_mask); > + val |= ICR_TB; > + > + spacemit_i2c_set_reg(adap, ICR_OFFSET, val); > +} > + > +static int spacemit_i2c_xfer_write(struct spacemit_i2c_adapter *adap, > + uint8_t byte, uint32_t ctrl) > +{ > + spacemit_i2c_set_reg(adap, IDBR_OFFSET, byte); > + spacemit_i2c_start_xfer(adap, ctrl); > + > + return spacemit_i2c_wait_xfer_done(adap); > +} > + > +static int spacemit_i2c_xfer_read(struct spacemit_i2c_adapter *adap, > + uint8_t *byte, uint32_t ctrl) > +{ > + int rc; > + > + spacemit_i2c_start_xfer(adap, ctrl); > + > + rc = spacemit_i2c_wait_xfer_done(adap); > + if (rc) > + return rc; > + > + *byte = spacemit_i2c_get_reg(adap, IDBR_OFFSET); > + return 0; > +} > + > +static int spacemit_i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, > + uint8_t reg, uint8_t *buffer, int len) > +{ > + struct spacemit_i2c_adapter *adap = > + container_of(ia, struct spacemit_i2c_adapter, adapter); > + int rc; > + > + /* reset controller to a known state */ > + spacemit_i2c_reset(adap); > + > + /* send device address (in write mode) */ > + rc = spacemit_i2c_xfer_write(adap, addr << 1, ICR_START); > + if (rc) > + return rc; > + > + /* send register + data bytes */ > + for (int i = 0; i < len + 1; i++) { > + uint32_t ctrl = (i == len) ? ICR_STOP : 0; > + uint8_t byte = (i == 0) ? reg : buffer[i - 1]; > + > + rc = spacemit_i2c_xfer_write(adap, byte, ctrl); > + if (rc) > + return rc; > + } > + > + return 0; > +} > + > +static int spacemit_i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, > + uint8_t reg, uint8_t *buffer, int len) > +{ > + struct spacemit_i2c_adapter *adap = > + container_of(ia, struct spacemit_i2c_adapter, adapter); > + int rc; > + > + /* reset controller to a known state */ > + spacemit_i2c_reset(adap); > + > + /* send device address (in write mode) */ > + rc = spacemit_i2c_xfer_write(adap, addr << 1, ICR_START); > + if (rc) > + return rc; > + > + /* send register */ > + rc = spacemit_i2c_xfer_write(adap, reg, 0); > + if (rc) > + return rc; > + > + /* repeated start and send device address (in read mode) */ > + rc = spacemit_i2c_xfer_write(adap, (addr << 1) | 1, ICR_START); > + if (rc) > + return rc; > + > + /* read data bytes */ > + for (int i = 0; i < len; i++) { > + uint32_t ctrl = (i == len - 1) ? (ICR_ACKNAK | ICR_STOP) : 0; > + > + rc = spacemit_i2c_xfer_read(adap, &buffer[i], ctrl); > + if (rc) > + return rc; > + } > + > + return 0; > +} > + > +static int spacemit_i2c_init(const void *fdt, int nodeoff, > + const struct fdt_match *match) > +{ > + struct spacemit_i2c_adapter *adapter; > + uint64_t base; > + int rc; > + > + adapter = sbi_zalloc(sizeof(*adapter)); > + if (!adapter) > + return SBI_ENOMEM; > + > + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &base, NULL); > + if (rc) { > + sbi_free(adapter); > + return rc; > + } > + > + adapter->base = base; > + adapter->adapter.id = nodeoff; > + adapter->adapter.write = spacemit_i2c_adapter_write; > + adapter->adapter.read = spacemit_i2c_adapter_read; > + rc = i2c_adapter_add(&adapter->adapter); > + if (rc) { > + sbi_free(adapter); > + return rc; > + } > + > + return 0; > +} > + > +static const struct fdt_match spacemit_i2c_match[] = { > + { .compatible = "spacemit,k1-i2c" }, > + { }, > +}; > + > +const struct fdt_driver fdt_i2c_adapter_spacemit = { > + .match_table = spacemit_i2c_match, > + .init = spacemit_i2c_init, > +}; > diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk > index d34d6648..91ac17ec 100644 > --- a/lib/utils/i2c/objects.mk > +++ b/lib/utils/i2c/objects.mk > @@ -15,6 +15,9 @@ libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.carray.o > carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive > libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o > > +carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SPACEMIT) += fdt_i2c_adapter_spacemit > +libsbiutils-objs-$(CONFIG_FDT_I2C_SPACEMIT) += i2c/fdt_i2c_spacemit.o > + > carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_DW) += fdt_i2c_adapter_dw > libsbiutils-objs-$(CONFIG_FDT_I2C_DW) += i2c/fdt_i2c_dw.o > > diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig > index 1d3431e7..a9cb0f06 100644 > --- a/platform/generic/configs/defconfig > +++ b/platform/generic/configs/defconfig > @@ -31,6 +31,7 @@ CONFIG_FDT_HSM_RPMI=y > CONFIG_FDT_HSM_SIFIVE_TMC0=y > CONFIG_FDT_I2C=y > CONFIG_FDT_I2C_SIFIVE=y > +CONFIG_FDT_I2C_SPACEMIT=y > CONFIG_FDT_I2C_DW=y > CONFIG_FDT_IPI=y > CONFIG_FDT_IPI_MSWI=y > -- > 2.53.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From linux.amoon at gmail.com Sat May 9 12:24:21 2026 From: linux.amoon at gmail.com (Anand Moon) Date: Sun, 10 May 2026 00:54:21 +0530 Subject: [PATCH 2/2] lib: utils/reset: add SpacemiT P1 PMIC support In-Reply-To: <20260419150857.2705843-3-aurelien@aurel32.net> References: <20260419150857.2705843-1-aurelien@aurel32.net> <20260419150857.2705843-3-aurelien@aurel32.net> Message-ID: Hi Aurelien, On Sun, 19 Apr 2026 at 20:40, Aurelien Jarno wrote: > > The SpacemiT P1 is a PMIC commonly found with SpacemiT CPU like K1. Add > a reset driver for it. > > Signed-off-by: Aurelien Jarno Tested-by: Anand Moon Thanks -Anand > --- > lib/utils/reset/Kconfig | 4 + > lib/utils/reset/fdt_reset_spacemit_p1.c | 112 ++++++++++++++++++++++++ > lib/utils/reset/objects.mk | 3 + > platform/generic/configs/defconfig | 1 + > 4 files changed, 120 insertions(+) > create mode 100644 lib/utils/reset/fdt_reset_spacemit_p1.c > > diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig > index 4835921f..f98926e5 100644 > --- a/lib/utils/reset/Kconfig > +++ b/lib/utils/reset/Kconfig > @@ -33,6 +33,10 @@ config FDT_RESET_SG2042_HWMON_MCU > bool "Sophgo SG2042 hwmon MCU FDT reset driver" > default n > > +config FDT_RESET_SPACEMIT_P1 > + bool "SpacemiT P1 reset driver" > + default n > + > config FDT_RESET_SUNXI_WDT > bool "Sunxi WDT FDT reset driver" > default n > diff --git a/lib/utils/reset/fdt_reset_spacemit_p1.c b/lib/utils/reset/fdt_reset_spacemit_p1.c > new file mode 100644 > index 00000000..5312e741 > --- /dev/null > +++ b/lib/utils/reset/fdt_reset_spacemit_p1.c > @@ -0,0 +1,112 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 Aurelien Jarno > + * Authors: > + * Aurelien Jarno > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* SpacemiT P1 Power Control Register 2 */ > +#define PWR_CTRL2 0x7e > +#define PWR_CTRL2_SHUTDOWN BIT(2) /* Shutdown request */ > +#define PWR_CTRL2_RST BIT(1) /* Reset request */ > + > +static struct i2c_adapter *p1_adapter = NULL; > +static uint32_t p1_reg = 0; > + > +static int p1_system_reset_check(uint32_t type, uint32_t reason) > +{ > + switch (type) { > + case SBI_SRST_RESET_TYPE_SHUTDOWN: > + return 1; > + case SBI_SRST_RESET_TYPE_COLD_REBOOT: > + case SBI_SRST_RESET_TYPE_WARM_REBOOT: > + return 255; > + } > + > + return 0; > +} > + > +static void p1_ops(uint32_t type) > +{ > + uint8_t byte; > + int rc; > + > + rc = i2c_adapter_reg_read(p1_adapter, p1_reg, PWR_CTRL2, &byte); > + if (rc) { > + sbi_printf("%s: cannot read P1 Power Control Register 2\n", __func__); > + return; > + } > + > + if (type == SBI_SRST_RESET_TYPE_SHUTDOWN) > + byte |= PWR_CTRL2_SHUTDOWN; > + else > + byte |= PWR_CTRL2_RST; > + > + rc = i2c_adapter_reg_write(p1_adapter, p1_reg, PWR_CTRL2, byte); > + if (rc) > + sbi_printf("%s: cannot write P1 Power Control Register 2\n", __func__); > +} > + > +static void p1_system_reset(uint32_t type, uint32_t reason) > +{ > + switch (type) { > + case SBI_SRST_RESET_TYPE_SHUTDOWN: > + case SBI_SRST_RESET_TYPE_COLD_REBOOT: > + case SBI_SRST_RESET_TYPE_WARM_REBOOT: > + p1_ops(type); > + break; > + } > +} > + > +static struct sbi_system_reset_device p1_reset = { > + .name = "spacemit-p1-reset", > + .system_reset_check = p1_system_reset_check, > + .system_reset = p1_system_reset > +}; > + > +static int p1_reset_init(const void *fdt, int nodeoff, > + const struct fdt_match *match) > +{ > + int rc, i2c_bus; > + uint64_t addr; > + > + /* we are spacemit,p1 node */ > + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL); > + if (rc) > + return rc; > + > + p1_reg = addr; > + > + /* find i2c bus parent node */ > + i2c_bus = fdt_parent_offset(fdt, nodeoff); > + if (i2c_bus < 0) > + return i2c_bus; > + > + /* i2c adapter get */ > + rc = fdt_i2c_adapter_get(fdt, i2c_bus, &p1_adapter); > + if (rc) > + return rc; > + > + sbi_system_reset_add_device(&p1_reset); > + > + return 0; > +} > + > +static const struct fdt_match p1_reset_match[] = { > + { .compatible = "spacemit,p1", .data = (void *)true }, > + { }, > +}; > + > +const struct fdt_driver fdt_reset_spacemit_p1 = { > + .match_table = p1_reset_match, > + .init = p1_reset_init, > +}; > diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk > index ac38b49d..3c681c27 100644 > --- a/lib/utils/reset/objects.mk > +++ b/lib/utils/reset/objects.mk > @@ -20,6 +20,9 @@ libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o > carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += fdt_reset_sg2042_mcu > libsbiutils-objs-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += reset/fdt_reset_sg2042_hwmon_mcu.o > > +carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SPACEMIT_P1) += fdt_reset_spacemit_p1 > +libsbiutils-objs-$(CONFIG_FDT_RESET_SPACEMIT_P1) += reset/fdt_reset_spacemit_p1.o > + > carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SUNXI_WDT) += fdt_reset_sunxi_wdt > libsbiutils-objs-$(CONFIG_FDT_RESET_SUNXI_WDT) += reset/fdt_reset_sunxi_wdt.o > > diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig > index a9cb0f06..ae278856 100644 > --- a/platform/generic/configs/defconfig > +++ b/platform/generic/configs/defconfig > @@ -51,6 +51,7 @@ CONFIG_FDT_RESET_GPIO=y > CONFIG_FDT_RESET_HTIF=y > CONFIG_FDT_RESET_RPMI=y > CONFIG_FDT_RESET_SG2042_HWMON_MCU=y > +CONFIG_FDT_RESET_SPACEMIT_P1=y > CONFIG_FDT_RESET_SUNXI_WDT=y > CONFIG_FDT_RESET_SYSCON=y > CONFIG_FDT_SERIAL=y > -- > 2.53.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup at brainfault.org Sat May 9 23:04:49 2026 From: anup at brainfault.org (Anup Patel) Date: Sun, 10 May 2026 11:34:49 +0530 Subject: [PATCH] lib: utils: fdt_domain: add support for root domain region inheritance In-Reply-To: <20260327043357.3452008-1-peter.lin@sifive.com> References: <20260327043357.3452008-1-peter.lin@sifive.com> Message-ID: On Fri, Mar 27, 2026 at 10:04?AM Yu-Chien Peter Lin wrote: > > Add the "root-regions" property in domain device-tree nodes to > allow domains to inherit all regions from the root domain. This > simplifies configuration for domains that need access to most > root domain regions with only minor exclusions or additions. Overall, this is a good approach but I suggest renaming this property to "root-regions-inheritance" which takes the following possible values: 1) "all" : Inherit all regions from the root domain 2) "su-only": Inherit regions accessible to S-mode or U-mode from the root domain 3) "none": Inherit no regions from the root domain >From the above, if "root-regions-inheritance" is not set then it is assumed to be "su-only". Regards, Anup > > Signed-off-by: Yu-Chien Peter Lin > --- > docs/domain_support.md | 4 ++++ > lib/utils/fdt/fdt_domain.c | 46 +++++++++++++++++++++++--------------- > 2 files changed, 32 insertions(+), 18 deletions(-) > > diff --git a/docs/domain_support.md b/docs/domain_support.md > index 93186c4a..a88f1cfb 100644 > --- a/docs/domain_support.md > +++ b/docs/domain_support.md > @@ -159,6 +159,10 @@ The DT properties of a domain instance DT node are as follows: > * **possible-harts** (Optional) - The list of CPU DT node phandles for the > the domain instance. This list represents the possible HARTs of the > domain instance. > +* **root-regions** (Optional) - A boolean flag indicating whether this domain > + inherits ALL memory regions from the root domain. If this property is present, > + the domain will include with all root domain regions and then overlay with > + regions specified in the **regions** property for additional restrictions. > * **regions** (Optional) - The list of domain memory region DT node phandle > and access permissions for the domain instance. Each list entry is a pair > of DT node phandle and access permissions. The access permissions are > diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c > index b2fa8633..45407c22 100644 > --- a/lib/utils/fdt/fdt_domain.c > +++ b/lib/utils/fdt/fdt_domain.c > @@ -373,25 +373,35 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) > if (err) > goto fail_free_all; > > - /* > - * Copy over root domain memregions which don't allow > - * read, write and execute from lower privilege modes. > - * > - * These root domain memregions without read, write, > - * and execute permissions include: > - * 1) firmware region protecting the firmware memory > - * 2) mmio regions protecting M-mode only mmio devices > - */ > - sbi_domain_for_each_memregion(&root, reg) { > - if ((reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) || > - (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) || > - (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)) > - continue; > - if (preg.max_regions <= preg.region_count) { > - err = SBI_EINVAL; > - goto fail_free_all; > + if (fdt_get_property(fdt, domain_offset, "root-regions", NULL)) { > + sbi_domain_for_each_memregion(&root, reg) { > + if (preg.max_regions <= preg.region_count) { > + err = SBI_EINVAL; > + goto fail_free_all; > + } > + memcpy(&dom->regions[preg.region_count++], reg, sizeof(*reg)); > + } > + } else { > + /* > + * Copy over root domain memregions which don't allow > + * read, write and execute from lower privilege modes. > + * > + * These root domain memregions without read, write, > + * and execute permissions include: > + * 1) firmware region protecting the firmware memory > + * 2) mmio regions protecting M-mode only mmio devices > + */ > + sbi_domain_for_each_memregion(&root, reg) { > + if ((reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) || > + (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) || > + (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)) > + continue; > + if (preg.max_regions <= preg.region_count) { > + err = SBI_EINVAL; > + goto fail_free_all; > + } > + memcpy(&dom->regions[preg.region_count++], reg, sizeof(*reg)); > } > - memcpy(&dom->regions[preg.region_count++], reg, sizeof(*reg)); > } > dom->fw_region_inited = root.fw_region_inited; > > -- > 2.48.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup at brainfault.org Sat May 9 23:10:16 2026 From: anup at brainfault.org (Anup Patel) Date: Sun, 10 May 2026 11:40:16 +0530 Subject: [PATCH] lib: fdt_domain: Default boot-hart to coldboot HART for multi-domain boot In-Reply-To: <20260327054936.3462935-1-peter.lin@sifive.com> References: <20260327054936.3462935-1-peter.lin@sifive.com> Message-ID: On Fri, Mar 27, 2026 at 11:19?AM Yu-Chien Peter Lin wrote: > > When "boot-hart" is not specified, dom->boot_hartid was left as -1U, > causing domain context switching to fail. Default it to the coldboot > HART to enable SMP boot in multi-domain setups, consistent with how > next-arg1 is handled. > > Also update its description in domain_support.md. > > Signed-off-by: Yu-Chien Peter Lin LGTM. Reviewed-by: Anup Patel Regards, Anup > --- > docs/domain_support.md | 6 +++--- > lib/utils/fdt/fdt_domain.c | 5 +---- > 2 files changed, 4 insertions(+), 7 deletions(-) > > diff --git a/docs/domain_support.md b/docs/domain_support.md > index 93186c4a..c01e445f 100644 > --- a/docs/domain_support.md > +++ b/docs/domain_support.md > @@ -173,9 +173,9 @@ The DT properties of a domain instance DT node are as follows: > Any region of a domain defined in DT node cannot have only M-bits set > in access permissions i.e. it cannot be an m-mode only accessible region. > * **boot-hart** (Optional) - The DT node phandle of the HART booting the > - domain instance. If coldboot HART is assigned to the domain instance then > - this DT property is ignored and the coldboot HART is assumed to be the > - boot HART of the domain instance. > + domain instance. If not specified, defaults to the coldboot HART. Note that > + if the coldboot HART is assigned to this domain, it will be forced as > + the boot HART regardless of this property. > * **next-arg1** (Optional) - The 64 bit next booting stage arg1 for the > domain instance. If this DT property is not available and coldboot HART > is not assigned to the domain instance then **next booting stage arg1 of coldboot HART** > diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c > index b2fa8633..1b039533 100644 > --- a/lib/utils/fdt/fdt_domain.c > +++ b/lib/utils/fdt/fdt_domain.c > @@ -396,16 +396,13 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) > dom->fw_region_inited = root.fw_region_inited; > > /* Read "boot-hart" DT property */ > - val32 = -1U; > + val32 = current_hartid(); > val = fdt_getprop(fdt, domain_offset, "boot-hart", &len); > if (val && len >= 4) { > cpu_offset = fdt_node_offset_by_phandle(fdt, > fdt32_to_cpu(*val)); > if (cpu_offset >= 0 && fdt_node_is_enabled(fdt, cpu_offset)) > fdt_parse_hart_id(fdt, cpu_offset, &val32); > - } else { > - if (domain_offset == *cold_domain_offset) > - val32 = current_hartid(); > } > dom->boot_hartid = val32; > > -- > 2.48.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From nylon.chen at sifive.com Sun May 10 20:23:51 2026 From: nylon.chen at sifive.com (Nylon Chen) Date: Mon, 11 May 2026 11:23:51 +0800 Subject: [PATCH v3 2/6] lib: sbi: Add Smrnmi extension macros for registers and bits In-Reply-To: <1c6feb6d359b9827b3c2ad8f4f0e0a4dfd1de911.1778176768.git.evvoevod@tenstorrent.com> References: <1c6feb6d359b9827b3c2ad8f4f0e0a4dfd1de911.1778176768.git.evvoevod@tenstorrent.com> Message-ID: Evgeny Voevodin ? 2026?5?8??? ??2:08??? > > Add CSR definitions (MNSCRATCH, MNSTATUS, MNEPC, MNCAUSE) and bit definitions > (MNSTATUS_NMIE, MNSTATUS_MNPV, MNSTATUS_MNPP). Also add SBI_HART_EXT_SMRNMI to > the hart extension enumeration. > > Signed-off-by: Evgeny Voevodin > Reviewed-by: Anup Patel > --- > include/sbi/riscv_encoding.h | 10 ++++++++++ > include/sbi/sbi_hart.h | 2 ++ > lib/sbi/sbi_hart.c | 1 + > 3 files changed, 13 insertions(+) > > diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h > index 3c1d5256..18f7b4a7 100644 > --- a/include/sbi/riscv_encoding.h > +++ b/include/sbi/riscv_encoding.h > @@ -215,6 +215,10 @@ > > #endif > > +#define MNSTATUS_NMIE (_UL(0x8)) > +#define MNSTATUS_MNPV (_UL(0x80)) > +#define MNSTATUS_MNPP (_UL(0x1800)) Please add define fo mnstatus.MNPELP bit as well. > + > #define MHPMEVENT_SSCOF_MASK _ULL(0xFF00000000000000) > > #define ENVCFG_STCE (_ULL(1) << 63) > @@ -830,6 +834,12 @@ > #define CSR_CUSTOM10_M_RO_BASE 0xFC0 > #define CSR_CUSTOM10_M_RO_COUNT 0x040 > > +/* Smrnmi extension registers */ > +#define CSR_MNSCRATCH 0x740 > +#define CSR_MNEPC 0x741 > +#define CSR_MNCAUSE 0x742 > +#define CSR_MNSTATUS 0x744 > + > /* ===== Trap/Exception Causes ===== */ > > #define CAUSE_MISALIGNED_FETCH 0x0 > diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h > index a788b34c..937cdf29 100644 > --- a/include/sbi/sbi_hart.h > +++ b/include/sbi/sbi_hart.h > @@ -87,6 +87,8 @@ enum sbi_hart_extensions { > SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, > /** Hart has Xsfcease extension */ > SBI_HART_EXT_XSIFIVE_CEASE, > + /** Hart has Smrnmi extension */ > + SBI_HART_EXT_SMRNMI, > > /** Maximum index of Hart extension */ > SBI_HART_EXT_MAX, > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > index 99e13990..4aefb759 100644 > --- a/lib/sbi/sbi_hart.c > +++ b/lib/sbi/sbi_hart.c > @@ -396,6 +396,7 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { > __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), > __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), > __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), > + __SBI_HART_EXT_DATA(smrnmi, SBI_HART_EXT_SMRNMI), > }; > > _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), > -- > 2.43.0 > Otherwise, LGTM Reviewed-by: Nylon Chen Regards, Nylon From ranbir.singh at oss.qualcomm.com Sun May 10 21:37:18 2026 From: ranbir.singh at oss.qualcomm.com (Ranbir Singh) Date: Mon, 11 May 2026 10:07:18 +0530 Subject: [PATCH] lib: utils: Fix LLVM compile error in MPXY client driver for RPMI MM In-Reply-To: <20260509161849.2935816-1-anup.patel@oss.qualcomm.com> References: <20260509161849.2935816-1-anup.patel@oss.qualcomm.com> Message-ID: On Sat, May 9, 2026 at 9:55?PM Anup Patel wrote: > > The following compile error is seen with LLVM compiler: > > CC platform/generic/lib/utils/mpxy/fdt_mpxy_rpmi_mm.o > lib/utils/mpxy/fdt_mpxy_rpmi_mm.c:17:6: error: use of GNU 'missing =' extension in > designator [-Werror,-Wgnu-designator] > 17 | [0] { > | ^ > | = > lib/utils/mpxy/fdt_mpxy_rpmi_mm.c:24:6: error: use of GNU 'missing =' extension in > designator [-Werror,-Wgnu-designator] > 24 | [1] { > | ^ > | = > 2 errors generated. > > Add missing "=" in mm_srvcdata[] array initialization to address > the above issue. > > Fixes: 0b041e58c078 ("lib: utils: Add MPXY client driver for RPMI MM service group") > Signed-off-by: Anup Patel LGTM Reviewed-by: Ranbir Singh > --- > lib/utils/mpxy/fdt_mpxy_rpmi_mm.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c > index 0163d0d2..d7176aca 100644 > --- a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c > +++ b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c > @@ -14,14 +14,14 @@ > static struct rpmi_mm_get_attributes_rsp rsp; > > static struct mpxy_rpmi_service_data mm_srvcdata[] = { > - [0] { > + [0] = { > .id = RPMI_MM_SRV_GET_ATTRIBUTES, > .min_tx_len = 0, > .max_tx_len = 0, > .min_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp), > .max_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp), > }, > - [1] { > + [1] = { > .id = RPMI_MM_SRV_COMMUNICATE, > .min_tx_len = sizeof(struct rpmi_mm_communicate_req), > .max_tx_len = sizeof(struct rpmi_mm_communicate_req), > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup at brainfault.org Sun May 10 21:51:51 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 11 May 2026 10:21:51 +0530 Subject: [PATCH] lib: fdt_domain: Default boot-hart to coldboot HART for multi-domain boot In-Reply-To: <20260327054936.3462935-1-peter.lin@sifive.com> References: <20260327054936.3462935-1-peter.lin@sifive.com> Message-ID: On Fri, Mar 27, 2026 at 11:19?AM Yu-Chien Peter Lin wrote: > > When "boot-hart" is not specified, dom->boot_hartid was left as -1U, > causing domain context switching to fail. Default it to the coldboot > HART to enable SMP boot in multi-domain setups, consistent with how > next-arg1 is handled. > > Also update its description in domain_support.md. > > Signed-off-by: Yu-Chien Peter Lin Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > docs/domain_support.md | 6 +++--- > lib/utils/fdt/fdt_domain.c | 5 +---- > 2 files changed, 4 insertions(+), 7 deletions(-) > > diff --git a/docs/domain_support.md b/docs/domain_support.md > index 93186c4a..c01e445f 100644 > --- a/docs/domain_support.md > +++ b/docs/domain_support.md > @@ -173,9 +173,9 @@ The DT properties of a domain instance DT node are as follows: > Any region of a domain defined in DT node cannot have only M-bits set > in access permissions i.e. it cannot be an m-mode only accessible region. > * **boot-hart** (Optional) - The DT node phandle of the HART booting the > - domain instance. If coldboot HART is assigned to the domain instance then > - this DT property is ignored and the coldboot HART is assumed to be the > - boot HART of the domain instance. > + domain instance. If not specified, defaults to the coldboot HART. Note that > + if the coldboot HART is assigned to this domain, it will be forced as > + the boot HART regardless of this property. > * **next-arg1** (Optional) - The 64 bit next booting stage arg1 for the > domain instance. If this DT property is not available and coldboot HART > is not assigned to the domain instance then **next booting stage arg1 of coldboot HART** > diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c > index b2fa8633..1b039533 100644 > --- a/lib/utils/fdt/fdt_domain.c > +++ b/lib/utils/fdt/fdt_domain.c > @@ -396,16 +396,13 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) > dom->fw_region_inited = root.fw_region_inited; > > /* Read "boot-hart" DT property */ > - val32 = -1U; > + val32 = current_hartid(); > val = fdt_getprop(fdt, domain_offset, "boot-hart", &len); > if (val && len >= 4) { > cpu_offset = fdt_node_offset_by_phandle(fdt, > fdt32_to_cpu(*val)); > if (cpu_offset >= 0 && fdt_node_is_enabled(fdt, cpu_offset)) > fdt_parse_hart_id(fdt, cpu_offset, &val32); > - } else { > - if (domain_offset == *cold_domain_offset) > - val32 = current_hartid(); > } > dom->boot_hartid = val32; > > -- > 2.48.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From apatel at ventanamicro.com Sun May 10 21:53:10 2026 From: apatel at ventanamicro.com (Anup Patel) Date: Mon, 11 May 2026 10:23:10 +0530 Subject: [PATCH] lib: utils: Fix LLVM compile error in MPXY client driver for RPMI MM In-Reply-To: <20260509161849.2935816-1-anup.patel@oss.qualcomm.com> References: <20260509161849.2935816-1-anup.patel@oss.qualcomm.com> Message-ID: On Sat, May 9, 2026 at 9:58?PM Anup Patel wrote: > > The following compile error is seen with LLVM compiler: > > CC platform/generic/lib/utils/mpxy/fdt_mpxy_rpmi_mm.o > lib/utils/mpxy/fdt_mpxy_rpmi_mm.c:17:6: error: use of GNU 'missing =' extension in > designator [-Werror,-Wgnu-designator] > 17 | [0] { > | ^ > | = > lib/utils/mpxy/fdt_mpxy_rpmi_mm.c:24:6: error: use of GNU 'missing =' extension in > designator [-Werror,-Wgnu-designator] > 24 | [1] { > | ^ > | = > 2 errors generated. > > Add missing "=" in mm_srvcdata[] array initialization to address > the above issue. > > Fixes: 0b041e58c078 ("lib: utils: Add MPXY client driver for RPMI MM service group") > Signed-off-by: Anup Patel Since this is critical fix, applying it right away. Applied this patch to the riscv/opensbi repo. Regards, Anup > --- > lib/utils/mpxy/fdt_mpxy_rpmi_mm.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c > index 0163d0d2..d7176aca 100644 > --- a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c > +++ b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c > @@ -14,14 +14,14 @@ > static struct rpmi_mm_get_attributes_rsp rsp; > > static struct mpxy_rpmi_service_data mm_srvcdata[] = { > - [0] { > + [0] = { > .id = RPMI_MM_SRV_GET_ATTRIBUTES, > .min_tx_len = 0, > .max_tx_len = 0, > .min_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp), > .max_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp), > }, > - [1] { > + [1] = { > .id = RPMI_MM_SRV_COMMUNICATE, > .min_tx_len = sizeof(struct rpmi_mm_communicate_req), > .max_tx_len = sizeof(struct rpmi_mm_communicate_req), > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup at brainfault.org Sun May 10 21:57:58 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 11 May 2026 10:27:58 +0530 Subject: [PATCH] docs: platform: update qemu_virt.md In-Reply-To: <20260319125641.480161-1-runmin.zhang@ingenic.com> References: <20260319125641.480161-1-runmin.zhang@ingenic.com> Message-ID: On Thu, Mar 19, 2026 at 6:28?PM wrote: > > From: Zhang RunMin > > When using GDB for debugging, if FW_TEXT_START is not set to 0x80000000 > during compilation, the following error occurs: > > Reading symbols from build/platform/generic/firmware/fw_payload.elf... > Remote debugging using localhost:1234 > mips_warm_boot () > at /home/zrmin/opensbi/platform/generic/mips/mips_warm_boot.S:11 > 11 j _start_warm > (gdb) b _start > Breakpoint 1 at 0x0: file /home/zrmin/opensbi/firmware/fw_base.S, line 50. > (gdb) c > Continuing. > Remote connection closed > (gdb) > > With FW_TEXT_START=0x80000000, debugging works correctly: > Reading symbols from build/platform/generic/firmware/fw_payload.elf... > Remote debugging using localhost:1234 > 0x0000000000001000 in ?? () > (gdb) b _start > Breakpoint 1 at 0x80000000: file /home/zrmin/opensbi/firmware/fw_base.S, line 50. > (gdb) c > Continuing. > > Breakpoint 1, _start () at /home/zrmin/opensbi/firmware/fw_base.S:50 > 50 MOV_3R s0, a0, s1, a1, s2, a2 > (gdb) > > This is because QEMU loads OpenSBI at address 0x80000000. When > FW_TEXT_START does not match this address, the debug symbols are > incorrectly offset, causing GDB to fail to set breakpoints properly. > > Signed-off-by: Zhang RunMin LGTM. Reviewed-by: Anup Patel Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > docs/platform/qemu_virt.md | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/docs/platform/qemu_virt.md b/docs/platform/qemu_virt.md > index fd87ee5a..4513df8b 100644 > --- a/docs/platform/qemu_virt.md > +++ b/docs/platform/qemu_virt.md > @@ -158,6 +158,9 @@ qemu-system-riscv32 -M virt -m 256M -nographic \ > Debugging with GDB > ------------------ > > +Note: the command line examples here assume that OpenSBI was compiled using > +the `DEBUG=1 FW_TEXT_START=0x80000000` configuration. > + > In a first console start OpenSBI with QEMU: > > ``` > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From rahul at summations.net Sun May 10 22:24:19 2026 From: rahul at summations.net (Rahul Pathak) Date: Mon, 11 May 2026 10:54:19 +0530 Subject: [PATCH] lib: sbi_domain: reject overflowing address range in check_addr_range() In-Reply-To: <20260327034822.39371-1-takumihara1226@gmail.com> References: <20260319132232.51572-1-takumihara1226@gmail.com> <20260327034822.39371-1-takumihara1226@gmail.com> Message-ID: Hi Takumi, I believe from the semantics of this function if the size passed is 0 which makes its a invalid range in which case this function should return false rather than true. Caller of this function if proceeds with the address without any checks happening inside the function just because it passed the size == 0 will be incorrect use, even though will be trapped later. Will let you and the maintainer decide on this behaviour. Since this behaviour is existing and the fix you have provided is still applicable. Reviewed-by: Rahul Pathak Thanks Rahul On Fri, Mar 27, 2026 at 9:18?AM Takumi Hara wrote: > > Thanks for the review, Rahul. > > When size == 0, max == addr, so the overflow guard is skipped > (it's not an overflow), and the while(addr < max) loop is also > skipped since addr == max. The function returns true. > > This is the existing behavior and is intentional -- a zero-length > range has no bytes to validate, so returning true is correct. > > This patch only targets the overflow case where a non-zero size > wraps max around to <= addr, which silently skips all permission > checks on a range that actually covers memory. > > Best regards, > Takumi Hara From apatel at ventanamicro.com Mon May 11 00:52:48 2026 From: apatel at ventanamicro.com (Anup Patel) Date: Mon, 11 May 2026 13:22:48 +0530 Subject: [PATCH] lib: utils/regmap: Fix reg_stride calculation in syscon regmap In-Reply-To: <20260403202903.3407945-1-david.garcia@aheadcomputing.com> References: <20260403202903.3407945-1-david.garcia@aheadcomputing.com> Message-ID: On Sat, Apr 4, 2026 at 1:59?AM David E. Garcia Porras wrote: > > The reg_stride field represents the address stride in bytes between > consecutive registers. The Linux kernel regmap framework validates > register accesses using IS_ALIGNED(reg, map->reg_stride) as an address > alignment check (drivers/base/regmap/regmap.c). The Linux kernel syscon > driver (drivers/mfd/syscon.c) sets reg_stride directly to reg_io_width: > > syscon_config.reg_stride = reg_io_width; > > The current OpenSBI code incorrectly multiplies reg_io_width by 8, > converting a byte value to bits. Fix this by using reg_io_width directly > as the stride value, consistent with the Linux kernel. > > Signed-off-by: David E. Garcia Porras Fixes tag is missing but I will add this at the time of merging. Reviewed-by: Anup Patel Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > lib/utils/regmap/fdt_regmap_syscon.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/lib/utils/regmap/fdt_regmap_syscon.c b/lib/utils/regmap/fdt_regmap_syscon.c > index 1309a55c..59cd5ee5 100644 > --- a/lib/utils/regmap/fdt_regmap_syscon.c > +++ b/lib/utils/regmap/fdt_regmap_syscon.c > @@ -190,7 +190,7 @@ static int regmap_syscon_init(const void *fdt, int nodeoff, > > srm->rmap.id = nodeoff; > srm->rmap.reg_shift = 0; > - srm->rmap.reg_stride = srm->reg_io_width * 8; > + srm->rmap.reg_stride = srm->reg_io_width; > srm->rmap.reg_base = 0; > srm->rmap.reg_max = size / srm->reg_io_width; > switch (srm->reg_io_width) { > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup at brainfault.org Mon May 11 00:52:52 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 11 May 2026 13:22:52 +0530 Subject: [PATCH] lib/sbi_pmu: Don't fallback to fixed counters when sscofpmf && !smcntrpmf In-Reply-To: <20260324-mcycle-fix-v1-1-1444e9fe5c32@kernel.org> References: <20260324-mcycle-fix-v1-1-1444e9fe5c32@kernel.org> Message-ID: On Tue, Mar 24, 2026 at 5:59?PM Michael Ellerman wrote: > > Currently when searching for a hardware counter for an event, if no > programmable counter is available, the code falls back to using a fixed > counter (mcycle/minstret) if one matches the event. > > However the fallback is incorrect when sscofpmf is present but > smcntrpmf is not. That's because with sscofpmf, programmable counters > support mode filtering, but the fixed counters do not (without > smcntrpmf). Even if the caller didn't configure mode filtering, by > default programmable counters don't count M mode when sscofpmf is > present, whereas mcycle/minstret do. > > Fix the logic to not fallback to a fixed counter if sscofpmf is present > but smcntrpmf is not. > > Signed-off-by: Michael Ellerman Fixes tag is missing but I will add this at the time of merging. Reviewed-by: Anup Patel Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > Hi folks, > > I noticed this while testing Linux perf with more events than > programmable counters. > > Sometimes cycles gets counted using a mhpmcounter, and sometimes with > mcycle, and the cycle count differs between the two. > > The count differs because mcycle doesn't support filtering when > smcntrpmf is not present. > > It can be reproduced by running perf stat and specifying `cycles:u` N+1 > times where N is the number of mhpmcounters, eg: > > $ perf stat -e cycles:u,cycles:u,... > ... > 392860 cycles:u > 61101120 cycles:u > > The first cycles got an mhpmcounter and only counted userspace cycles. > The second used mcycle and counted everything. > > cheers > --- > lib/sbi/sbi_pmu.c | 17 ++++++++++++----- > 1 file changed, 12 insertions(+), 5 deletions(-) > > diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c > index ae00ad5844b1fe92a8eebfb618902a5eef218a9e..8a9021e2e5bac2d71a0f07d3f81600ff370ee2fc 100644 > --- a/lib/sbi/sbi_pmu.c > +++ b/lib/sbi/sbi_pmu.c > @@ -830,13 +830,20 @@ static int pmu_ctr_find_hw(struct sbi_pmu_hart_state *phs, > > if (ctr_idx == SBI_ENOTSUPP) { > /** > - * We can't find any programmable counters for cycle/instret. > - * Return the fixed counter as they are mandatory anyways. > + * We can't find a programmable counter, see if we can use a > + * fixed counter instead if one was found for this event. > + * > + * If sscofpmf is present but smcntrpmf is not, we can't > + * fallback to a fixed counter, because the fixed counter > + * doesn't support filtering whereas a programmable counter > + * would. > */ > - if (fixed_ctr >= 0) > - return pmu_fixed_ctr_update_inhibit_bits(fixed_ctr, flags); > - else > + if (fixed_ctr < 0 || > + ((sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF) && > + !sbi_hart_has_extension(scratch, SBI_HART_EXT_SMCNTRPMF)))) > return SBI_EFAIL; > + > + return pmu_fixed_ctr_update_inhibit_bits(fixed_ctr, flags); > } > ret = pmu_update_hw_mhpmevent(temp, ctr_idx, flags, event_idx, data); > > > --- > base-commit: 4813a2042096b7860655761aad973723293a552e > change-id: 20260324-mcycle-fix-3fc5682eee9c > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From apatel at ventanamicro.com Mon May 11 06:48:17 2026 From: apatel at ventanamicro.com (Anup Patel) Date: Mon, 11 May 2026 19:18:17 +0530 Subject: [PATCH] lib: sbi: Fix hw a/d updating defaults In-Reply-To: <20260401220845.190680-1-andrew.jones@oss.qualcomm.com> References: <20260401220845.190680-1-andrew.jones@oss.qualcomm.com> Message-ID: On Thu, Apr 2, 2026 at 3:41?AM Andrew Jones wrote: > > The Svade dt-binding description states that Svadu should only > be enabled at boot time when only Svadu is present in the DT. > Ensure that's the case. Also, when only Svadu is supported, > disable FWFT.PTE_AD_HW_UPDATING, as we need both to support > toggling. > > Signed-off-by: Andrew Jones Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > lib/sbi/sbi_fwft.c | 10 +++++++++- > lib/sbi/sbi_hart.c | 18 ++++++++++-------- > 2 files changed, 19 insertions(+), 9 deletions(-) > > diff --git a/lib/sbi/sbi_fwft.c b/lib/sbi/sbi_fwft.c > index 373140b746bd..574b875e408d 100644 > --- a/lib/sbi/sbi_fwft.c > +++ b/lib/sbi/sbi_fwft.c > @@ -160,8 +160,16 @@ static int fwft_get_double_trap(struct fwft_config *conf, unsigned long *value) > > static int fwft_adue_supported(struct fwft_config *conf) > { > + /* > + * FWFT.PTE_AD_HW_UPDATING is only supported when both Svade and Svadu > + * are supported. We need both in order to support toggling and to > + * ensure the reset value of zero is valid (it wouldn't be when only > + * Svadu is supported). > + */ > if (!sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > - SBI_HART_EXT_SVADU)) > + SBI_HART_EXT_SVADU) || > + !sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_SVADE)) > return SBI_ENOTSUPP; > > return SBI_OK; > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > index 99e13990aa60..0df11d0d1029 100644 > --- a/lib/sbi/sbi_hart.c > +++ b/lib/sbi/sbi_hart.c > @@ -137,6 +137,9 @@ static void mstatus_init(struct sbi_scratch *scratch) > if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_12) { > menvcfg_val = csr_read64(CSR_MENVCFG); > > + /* Disable HW A/D updating by default */ > + menvcfg_val &= ~ENVCFG_ADUE; > + > /* Disable double trap by default */ > menvcfg_val &= ~ENVCFG_DTE; > > @@ -158,18 +161,17 @@ static void mstatus_init(struct sbi_scratch *scratch) > #endif > __set_menvcfg_ext(SBI_HART_EXT_SSTC, ENVCFG_STCE) > __set_menvcfg_ext(SBI_HART_EXT_SMCDELEG, ENVCFG_CDE); > - __set_menvcfg_ext(SBI_HART_EXT_SVADU, ENVCFG_ADUE); > - > -#undef __set_menvcfg_ext > > /* > - * When both Svade and Svadu are present in DT, the default scheme for managing > - * the PTE A/D bits should use Svade. Check Svadu before Svade extension to ensure > - * that the ADUE bit is cleared when the Svade support are specified. > + * Assume only Svadu is supported when it is the only extension > + * present in the ISA string. Svade is assumed when neither are > + * present. When both are present we must default to Svade (see > + * the zero reset value of FWFT.PTE_AD_HW_UPDATING). > */ > + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SVADE)) > + __set_menvcfg_ext(SBI_HART_EXT_SVADU, ENVCFG_ADUE); > > - if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SVADE)) > - menvcfg_val &= ~ENVCFG_ADUE; > +#undef __set_menvcfg_ext > > csr_write64(CSR_MENVCFG, menvcfg_val); > > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup at brainfault.org Mon May 11 06:55:59 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 11 May 2026 19:25:59 +0530 Subject: [PATCH 1/2] lib: utils/i2c: add minimal SpacemiT I2C driver In-Reply-To: <20260419150857.2705843-2-aurelien@aurel32.net> References: <20260419150857.2705843-1-aurelien@aurel32.net> <20260419150857.2705843-2-aurelien@aurel32.net> Message-ID: On Sun, Apr 19, 2026 at 8:39?PM Aurelien Jarno wrote: > > Add a simple SpacemiT I2C driver for basic byte transfers over the I2C > bus, prioritizing simplicity over performance. The driver operates in > PIO mode and does not use interrupts, FIFO, or DMA. > > The controller is reset at the start of each transaction to ensure a > known initial state, regardless of prior configuration by the kernel. > This also avoids the need for additional error recovery code. > > This will be used for communication with onboard PMIC to reset and > power-off the board. > > Signed-off-by: Aurelien Jarno Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > lib/utils/i2c/Kconfig | 4 + > lib/utils/i2c/fdt_i2c_spacemit.c | 220 +++++++++++++++++++++++++++++ > lib/utils/i2c/objects.mk | 3 + > platform/generic/configs/defconfig | 1 + > 4 files changed, 228 insertions(+) > create mode 100644 lib/utils/i2c/fdt_i2c_spacemit.c > > diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig > index 7fa32fcf..bdaaff62 100644 > --- a/lib/utils/i2c/Kconfig > +++ b/lib/utils/i2c/Kconfig > @@ -14,6 +14,10 @@ config FDT_I2C_SIFIVE > bool "SiFive I2C FDT driver" > default n > > +config FDT_I2C_SPACEMIT > + bool "SpacemiT I2C FDT driver" > + default n > + > config FDT_I2C_DW > bool "Synopsys Designware I2C FDT driver" > select I2C_DW > diff --git a/lib/utils/i2c/fdt_i2c_spacemit.c b/lib/utils/i2c/fdt_i2c_spacemit.c > new file mode 100644 > index 00000000..9009b6b6 > --- /dev/null > +++ b/lib/utils/i2c/fdt_i2c_spacemit.c > @@ -0,0 +1,220 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 Aurelien Jarno > + * > + * Authors: > + * Aurelien Jarno > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* Controller registers */ > +#define ICR_OFFSET 0x00 /* I2C control register */ > +#define IDBR_OFFSET 0x0c /* I2C data buffer register */ > + > +/* Control register bits */ > +#define ICR_START BIT(0) /* start */ > +#define ICR_STOP BIT(1) /* stop */ > +#define ICR_ACKNAK BIT(2) /* ACK(0) or NAK(1) */ > +#define ICR_TB BIT(3) /* transfer byte */ > +#define ICR_UR BIT(10) /* unit reset */ > +#define ICR_SCLE BIT(13) /* SCL enable */ > +#define ICR_IUE BIT(14) /* unit enable */ > + > +/* Timing */ > +#define I2C_RESET_US 10 > +#define I2C_TIMEOUT_US 1000 > + > +struct spacemit_i2c_adapter { > + unsigned long base; > + struct i2c_adapter adapter; > +}; > + > +static inline void spacemit_i2c_set_reg(struct spacemit_i2c_adapter *adap, > + uint8_t reg, uint32_t val) > +{ > + writel(val, (void *)adap->base + reg); > +} > + > +static inline uint32_t spacemit_i2c_get_reg(struct spacemit_i2c_adapter *adap, > + uint32_t reg) > +{ > + return readl((void *)adap->base + reg); > +} > + > +static void spacemit_i2c_reset(struct spacemit_i2c_adapter *adap) > +{ > + /* disable unit */ > + spacemit_i2c_set_reg(adap, ICR_OFFSET, 0); > + sbi_timer_udelay(I2C_RESET_US); > + > + /* reset unit */ > + spacemit_i2c_set_reg(adap, ICR_OFFSET, ICR_UR); > + sbi_timer_udelay(I2C_RESET_US); > + > + /* clear reset and enable unit and SCL */ > + spacemit_i2c_set_reg(adap, ICR_OFFSET, ICR_IUE | ICR_SCLE); > +} > + > +static int spacemit_i2c_wait_xfer_done(struct spacemit_i2c_adapter *adap) > +{ > + for (int i = 0; i < I2C_TIMEOUT_US; i++) { > + uint32_t val = spacemit_i2c_get_reg(adap, ICR_OFFSET); > + > + if (!(val & ICR_TB)) > + return 0; > + > + sbi_timer_udelay(1); > + }; > + > + return SBI_ETIMEDOUT; > +} > + > +static void spacemit_i2c_start_xfer(struct spacemit_i2c_adapter *adap, > + uint32_t ctrl) > +{ > + const uint32_t ctrl_mask = ICR_START | ICR_STOP | ICR_ACKNAK; > + uint32_t val; > + > + val = spacemit_i2c_get_reg(adap, ICR_OFFSET); > + val &= ~ctrl_mask; > + val |= (ctrl & ctrl_mask); > + val |= ICR_TB; > + > + spacemit_i2c_set_reg(adap, ICR_OFFSET, val); > +} > + > +static int spacemit_i2c_xfer_write(struct spacemit_i2c_adapter *adap, > + uint8_t byte, uint32_t ctrl) > +{ > + spacemit_i2c_set_reg(adap, IDBR_OFFSET, byte); > + spacemit_i2c_start_xfer(adap, ctrl); > + > + return spacemit_i2c_wait_xfer_done(adap); > +} > + > +static int spacemit_i2c_xfer_read(struct spacemit_i2c_adapter *adap, > + uint8_t *byte, uint32_t ctrl) > +{ > + int rc; > + > + spacemit_i2c_start_xfer(adap, ctrl); > + > + rc = spacemit_i2c_wait_xfer_done(adap); > + if (rc) > + return rc; > + > + *byte = spacemit_i2c_get_reg(adap, IDBR_OFFSET); > + return 0; > +} > + > +static int spacemit_i2c_adapter_write(struct i2c_adapter *ia, uint8_t addr, > + uint8_t reg, uint8_t *buffer, int len) > +{ > + struct spacemit_i2c_adapter *adap = > + container_of(ia, struct spacemit_i2c_adapter, adapter); > + int rc; > + > + /* reset controller to a known state */ > + spacemit_i2c_reset(adap); > + > + /* send device address (in write mode) */ > + rc = spacemit_i2c_xfer_write(adap, addr << 1, ICR_START); > + if (rc) > + return rc; > + > + /* send register + data bytes */ > + for (int i = 0; i < len + 1; i++) { > + uint32_t ctrl = (i == len) ? ICR_STOP : 0; > + uint8_t byte = (i == 0) ? reg : buffer[i - 1]; > + > + rc = spacemit_i2c_xfer_write(adap, byte, ctrl); > + if (rc) > + return rc; > + } > + > + return 0; > +} > + > +static int spacemit_i2c_adapter_read(struct i2c_adapter *ia, uint8_t addr, > + uint8_t reg, uint8_t *buffer, int len) > +{ > + struct spacemit_i2c_adapter *adap = > + container_of(ia, struct spacemit_i2c_adapter, adapter); > + int rc; > + > + /* reset controller to a known state */ > + spacemit_i2c_reset(adap); > + > + /* send device address (in write mode) */ > + rc = spacemit_i2c_xfer_write(adap, addr << 1, ICR_START); > + if (rc) > + return rc; > + > + /* send register */ > + rc = spacemit_i2c_xfer_write(adap, reg, 0); > + if (rc) > + return rc; > + > + /* repeated start and send device address (in read mode) */ > + rc = spacemit_i2c_xfer_write(adap, (addr << 1) | 1, ICR_START); > + if (rc) > + return rc; > + > + /* read data bytes */ > + for (int i = 0; i < len; i++) { > + uint32_t ctrl = (i == len - 1) ? (ICR_ACKNAK | ICR_STOP) : 0; > + > + rc = spacemit_i2c_xfer_read(adap, &buffer[i], ctrl); > + if (rc) > + return rc; > + } > + > + return 0; > +} > + > +static int spacemit_i2c_init(const void *fdt, int nodeoff, > + const struct fdt_match *match) > +{ > + struct spacemit_i2c_adapter *adapter; > + uint64_t base; > + int rc; > + > + adapter = sbi_zalloc(sizeof(*adapter)); > + if (!adapter) > + return SBI_ENOMEM; > + > + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &base, NULL); > + if (rc) { > + sbi_free(adapter); > + return rc; > + } > + > + adapter->base = base; > + adapter->adapter.id = nodeoff; > + adapter->adapter.write = spacemit_i2c_adapter_write; > + adapter->adapter.read = spacemit_i2c_adapter_read; > + rc = i2c_adapter_add(&adapter->adapter); > + if (rc) { > + sbi_free(adapter); > + return rc; > + } > + > + return 0; > +} > + > +static const struct fdt_match spacemit_i2c_match[] = { > + { .compatible = "spacemit,k1-i2c" }, > + { }, > +}; > + > +const struct fdt_driver fdt_i2c_adapter_spacemit = { > + .match_table = spacemit_i2c_match, > + .init = spacemit_i2c_init, > +}; > diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk > index d34d6648..91ac17ec 100644 > --- a/lib/utils/i2c/objects.mk > +++ b/lib/utils/i2c/objects.mk > @@ -15,6 +15,9 @@ libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.carray.o > carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive > libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o > > +carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SPACEMIT) += fdt_i2c_adapter_spacemit > +libsbiutils-objs-$(CONFIG_FDT_I2C_SPACEMIT) += i2c/fdt_i2c_spacemit.o > + > carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_DW) += fdt_i2c_adapter_dw > libsbiutils-objs-$(CONFIG_FDT_I2C_DW) += i2c/fdt_i2c_dw.o > > diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig > index 1d3431e7..a9cb0f06 100644 > --- a/platform/generic/configs/defconfig > +++ b/platform/generic/configs/defconfig > @@ -31,6 +31,7 @@ CONFIG_FDT_HSM_RPMI=y > CONFIG_FDT_HSM_SIFIVE_TMC0=y > CONFIG_FDT_I2C=y > CONFIG_FDT_I2C_SIFIVE=y > +CONFIG_FDT_I2C_SPACEMIT=y > CONFIG_FDT_I2C_DW=y > CONFIG_FDT_IPI=y > CONFIG_FDT_IPI_MSWI=y > -- > 2.53.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup at brainfault.org Mon May 11 06:56:22 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 11 May 2026 19:26:22 +0530 Subject: [PATCH 2/2] lib: utils/reset: add SpacemiT P1 PMIC support In-Reply-To: <20260419150857.2705843-3-aurelien@aurel32.net> References: <20260419150857.2705843-1-aurelien@aurel32.net> <20260419150857.2705843-3-aurelien@aurel32.net> Message-ID: On Sun, Apr 19, 2026 at 8:39?PM Aurelien Jarno wrote: > > The SpacemiT P1 is a PMIC commonly found with SpacemiT CPU like K1. Add > a reset driver for it. > > Signed-off-by: Aurelien Jarno Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > lib/utils/reset/Kconfig | 4 + > lib/utils/reset/fdt_reset_spacemit_p1.c | 112 ++++++++++++++++++++++++ > lib/utils/reset/objects.mk | 3 + > platform/generic/configs/defconfig | 1 + > 4 files changed, 120 insertions(+) > create mode 100644 lib/utils/reset/fdt_reset_spacemit_p1.c > > diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig > index 4835921f..f98926e5 100644 > --- a/lib/utils/reset/Kconfig > +++ b/lib/utils/reset/Kconfig > @@ -33,6 +33,10 @@ config FDT_RESET_SG2042_HWMON_MCU > bool "Sophgo SG2042 hwmon MCU FDT reset driver" > default n > > +config FDT_RESET_SPACEMIT_P1 > + bool "SpacemiT P1 reset driver" > + default n > + > config FDT_RESET_SUNXI_WDT > bool "Sunxi WDT FDT reset driver" > default n > diff --git a/lib/utils/reset/fdt_reset_spacemit_p1.c b/lib/utils/reset/fdt_reset_spacemit_p1.c > new file mode 100644 > index 00000000..5312e741 > --- /dev/null > +++ b/lib/utils/reset/fdt_reset_spacemit_p1.c > @@ -0,0 +1,112 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 Aurelien Jarno > + * Authors: > + * Aurelien Jarno > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* SpacemiT P1 Power Control Register 2 */ > +#define PWR_CTRL2 0x7e > +#define PWR_CTRL2_SHUTDOWN BIT(2) /* Shutdown request */ > +#define PWR_CTRL2_RST BIT(1) /* Reset request */ > + > +static struct i2c_adapter *p1_adapter = NULL; > +static uint32_t p1_reg = 0; > + > +static int p1_system_reset_check(uint32_t type, uint32_t reason) > +{ > + switch (type) { > + case SBI_SRST_RESET_TYPE_SHUTDOWN: > + return 1; > + case SBI_SRST_RESET_TYPE_COLD_REBOOT: > + case SBI_SRST_RESET_TYPE_WARM_REBOOT: > + return 255; > + } > + > + return 0; > +} > + > +static void p1_ops(uint32_t type) > +{ > + uint8_t byte; > + int rc; > + > + rc = i2c_adapter_reg_read(p1_adapter, p1_reg, PWR_CTRL2, &byte); > + if (rc) { > + sbi_printf("%s: cannot read P1 Power Control Register 2\n", __func__); > + return; > + } > + > + if (type == SBI_SRST_RESET_TYPE_SHUTDOWN) > + byte |= PWR_CTRL2_SHUTDOWN; > + else > + byte |= PWR_CTRL2_RST; > + > + rc = i2c_adapter_reg_write(p1_adapter, p1_reg, PWR_CTRL2, byte); > + if (rc) > + sbi_printf("%s: cannot write P1 Power Control Register 2\n", __func__); > +} > + > +static void p1_system_reset(uint32_t type, uint32_t reason) > +{ > + switch (type) { > + case SBI_SRST_RESET_TYPE_SHUTDOWN: > + case SBI_SRST_RESET_TYPE_COLD_REBOOT: > + case SBI_SRST_RESET_TYPE_WARM_REBOOT: > + p1_ops(type); > + break; > + } > +} > + > +static struct sbi_system_reset_device p1_reset = { > + .name = "spacemit-p1-reset", > + .system_reset_check = p1_system_reset_check, > + .system_reset = p1_system_reset > +}; > + > +static int p1_reset_init(const void *fdt, int nodeoff, > + const struct fdt_match *match) > +{ > + int rc, i2c_bus; > + uint64_t addr; > + > + /* we are spacemit,p1 node */ > + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL); > + if (rc) > + return rc; > + > + p1_reg = addr; > + > + /* find i2c bus parent node */ > + i2c_bus = fdt_parent_offset(fdt, nodeoff); > + if (i2c_bus < 0) > + return i2c_bus; > + > + /* i2c adapter get */ > + rc = fdt_i2c_adapter_get(fdt, i2c_bus, &p1_adapter); > + if (rc) > + return rc; > + > + sbi_system_reset_add_device(&p1_reset); > + > + return 0; > +} > + > +static const struct fdt_match p1_reset_match[] = { > + { .compatible = "spacemit,p1", .data = (void *)true }, > + { }, > +}; > + > +const struct fdt_driver fdt_reset_spacemit_p1 = { > + .match_table = p1_reset_match, > + .init = p1_reset_init, > +}; > diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk > index ac38b49d..3c681c27 100644 > --- a/lib/utils/reset/objects.mk > +++ b/lib/utils/reset/objects.mk > @@ -20,6 +20,9 @@ libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o > carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += fdt_reset_sg2042_mcu > libsbiutils-objs-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += reset/fdt_reset_sg2042_hwmon_mcu.o > > +carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SPACEMIT_P1) += fdt_reset_spacemit_p1 > +libsbiutils-objs-$(CONFIG_FDT_RESET_SPACEMIT_P1) += reset/fdt_reset_spacemit_p1.o > + > carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SUNXI_WDT) += fdt_reset_sunxi_wdt > libsbiutils-objs-$(CONFIG_FDT_RESET_SUNXI_WDT) += reset/fdt_reset_sunxi_wdt.o > > diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig > index a9cb0f06..ae278856 100644 > --- a/platform/generic/configs/defconfig > +++ b/platform/generic/configs/defconfig > @@ -51,6 +51,7 @@ CONFIG_FDT_RESET_GPIO=y > CONFIG_FDT_RESET_HTIF=y > CONFIG_FDT_RESET_RPMI=y > CONFIG_FDT_RESET_SG2042_HWMON_MCU=y > +CONFIG_FDT_RESET_SPACEMIT_P1=y > CONFIG_FDT_RESET_SUNXI_WDT=y > CONFIG_FDT_RESET_SYSCON=y > CONFIG_FDT_SERIAL=y > -- > 2.53.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From anup at brainfault.org Mon May 11 07:57:37 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 11 May 2026 20:27:37 +0530 Subject: [PATCH] lib: sbi_domain: reject zero-size and overflowing ranges in sbi_domain_check_addr_range In-Reply-To: <20260422151750.3794520-1-liutong@iscas.ac.cn> References: <20260422151750.3794520-1-liutong@iscas.ac.cn> Message-ID: On Wed, Apr 22, 2026 at 8:48?PM liutong wrote: > > `max = addr + size` is computed without overflow detection, and a > size of zero is not rejected. In both cases the `while (addr < max)` > loop executes zero times and the function falls through to > `return true` without actually checking any region against the > domain configuration. > > Reject size == 0 and detect unsigned overflow of addr + size before > entering the loop. > > Fixes: eab48c33a12d ("lib: sbi: Add sbi_domain_check_addr_range() function") > Signed-off-by: liutong The problem of overflowing ranges is already taken care by the patch "lib: sbi_domain: reject overflowing address range in check_addr_range()" so you might want to focus only on "reject zero-size". Regards, Anup > --- > Apologies for the earlier non-standard submission; resending as a > proper patch per docs/contributing.md. > > lib/sbi/sbi_domain.c | 7 ++++++- > 1 file changed, 6 insertions(+), 1 deletion(-) > > diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c > index 7030848d..74a02057 100644 > --- a/lib/sbi/sbi_domain.c > +++ b/lib/sbi/sbi_domain.c > @@ -499,12 +499,17 @@ bool sbi_domain_check_addr_range(const struct sbi_domain *dom, > unsigned long mode, > unsigned long access_flags) > { > - unsigned long max = addr + size; > + unsigned long max; > const struct sbi_domain_memregion *reg, *sreg; > > if (!dom) > return false; > > + if (!size || (addr + size) < addr) > + return false; > + > + max = addr + size; > + > while (addr < max) { > reg = find_region(dom, addr); > if (!reg) > -- > 2.34.1 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From dave.patel at riscstar.com Mon May 11 12:28:40 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Mon, 11 May 2026 20:28:40 +0100 Subject: [PATCH 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: References: <20260408072123.7543-1-dave.patel@riscstar.com> <20260408072123.7543-2-dave.patel@riscstar.com> Message-ID: On 5/9/26 09:33, Anup Patel wrote: > On Wed, Apr 8, 2026 at 12:52?PM wrote: >> >> From: Dave Patel >> >> Eager context switch: Add support for saving and restoring RISC-V vector >> extension state in OpenSBI. This introduces a per-hart vector context >> structure and helper routines to perform full context save and restore. >> >> The vector context includes vcsr CSRs along with storage for all 32 vector >> registers. The register state is saved and restored using byte-wise vector >> load/store instructions (vs8r/vl8r). >> >> The implementation follows an eager context switching model where the entire >> vector state is saved and restored on every context switch. This provides a >> simple and deterministic mechanism without requiring lazy trap-based >> management. >> >> Notes: >> - The SBI_MAX_VLENB is configured using CONFIG_SBI_MAX_VLENB. >> >> Signed-off-by: Dave Patel >> --- >> include/sbi/sbi_vector.h | 30 ++++++++ >> lib/sbi/Kconfig | 4 + >> lib/sbi/objects.mk | 1 + >> lib/sbi/sbi_vector.c | 155 +++++++++++++++++++++++++++++++++++++++ >> 4 files changed, 190 insertions(+) >> create mode 100644 include/sbi/sbi_vector.h >> create mode 100644 lib/sbi/sbi_vector.c >> >> diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h >> new file mode 100644 >> index 00000000..3b63b02c >> --- /dev/null >> +++ b/include/sbi/sbi_vector.h >> @@ -0,0 +1,30 @@ >> +/* >> + * SPDX-License-Identifier: BSD-2-Clause >> + * >> + * Copyright (c) 2026 RISCstar Solutions. >> + * >> + * Authors: >> + * Dave Patel >> + */ >> + >> +#ifndef __SBI_VECTOR_H__ >> +#define __SBI_VECTOR_H__ >> + >> +#include >> + >> +#define SBI_MAX_VLENB CONFIG_SBI_MAX_VLENB >> + >> +struct sbi_vector_context { >> + unsigned long vcsr; >> + unsigned long vstart; >> + >> + /* size depends on VLEN */ >> + uint8_t vregs[32 * SBI_MAX_VLENB]; > > We have same OpenSBI firmware running of multiple platforms > so please discover max_vlenb as a hart feature in sbi_hart.c > > The vregs over here should be "uint8_t vregs[]" and > "struct sbi_vector_context" must be dynamically allocated. I will allocate this dynamically, however the original changes were setting it dynamically and moved to to static due to review comment. Am I ok in understanding that the struct sbi_vector_context vec_ctx; should be struct sbi_vector_context * > >> +}; >> + >> +void sbi_vector_save(struct sbi_vector_context *dst); >> +void sbi_vector_restore(const struct sbi_vector_context *src); >> +int sbi_vector_domain_init(void); >> + >> +#endif //__SBI_VECTOR_H__ >> + >> diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig >> index 8479f861..b2432150 100644 >> --- a/lib/sbi/Kconfig >> +++ b/lib/sbi/Kconfig >> @@ -74,4 +74,8 @@ config SBI_ECALL_VIRQ >> bool "VIRQ extension" >> default y >> >> +config SBI_MAX_VLENB >> + int "Vector VLENB size" >> + default 256 >> + >> endmenu >> diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk >> index 68fc2036..ecb2b54e 100644 >> --- a/lib/sbi/objects.mk >> +++ b/lib/sbi/objects.mk >> @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o >> libsbi-objs-y += sbi_unpriv.o >> libsbi-objs-y += sbi_expected_trap.o >> libsbi-objs-y += sbi_cppc.o >> +libsbi-objs-y += sbi_vector.o >> diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c >> new file mode 100644 >> index 00000000..29434e2e >> --- /dev/null >> +++ b/lib/sbi/sbi_vector.c >> @@ -0,0 +1,155 @@ >> +/* >> + * SPDX-License-Identifier: BSD-2-Clause >> + * >> + * Copyright (c) 2026 RISCstar Solutions. >> + * >> + * Authors: >> + * Dave Patel >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#ifdef OPENSBI_CC_SUPPORT_VECTOR >> + >> +static inline unsigned long vector_vlenb(void) >> +{ >> + unsigned long vlenb = 0; >> + >> + asm volatile ( >> + ".option push\n\t" >> + ".option arch, +v\n\t" >> + "csrr %0, vlenb\n\t" >> + ".option pop\n\t" >> + : "=r"(vlenb) >> + : >> + : "memory"); >> + >> + return vlenb; >> +} >> + >> +void sbi_vector_save(struct sbi_vector_context *dst) >> +{ >> + if (!dst) >> + return; >> + >> +#define READ_CSR(dst, csr) \ >> + ({ \ >> + asm volatile ( \ >> + " .option push\n\t" \ >> + " .option arch, +v\n\t" \ >> + " csrr %0, " #csr "\n\t" \ >> + " .option pop\n\t" \ >> + : "=r"(dst) \ >> + : \ >> + : "memory"); \ >> + }) \ >> + >> + /* Step 1: Save CSRs */ >> + READ_CSR(dst->vcsr, vcsr); >> + READ_CSR(dst->vstart, vstart); > > use csr_read() instead of READ_CSR(). ok thanks, will do this. > >> + >> +#undef READ_CSR >> + >> + ulong vlenb = vector_vlenb(); >> + uint8_t *base = dst->vregs; >> + >> + /* Step 3: Save vector registers */ >> +#define SAVE_VREG(i) \ >> + ({ \ >> + asm volatile( \ >> + " .option push\n\t" \ >> + " .option arch, +v\n\t" \ >> + " vs8r.v v" #i ", (%0)\n\t" \ >> + " .option pop\n\t" \ >> + :: "r"(base + (i) * vlenb) : "memory"); \ >> + }) \ >> + >> + SAVE_VREG(0); >> + SAVE_VREG(8); >> + SAVE_VREG(16); >> + SAVE_VREG(24); >> + >> +#undef SAVE_VREG >> +} >> + >> +void sbi_vector_restore(const struct sbi_vector_context *src) >> +{ >> + if (!src) >> + return; >> + >> + const uint8_t *base = src->vregs; >> + ulong vlenb = vector_vlenb(); >> + >> + /* Step 2: Restore vector registers */ >> +#define RESTORE_VREG(i) \ >> + ({ \ >> + asm volatile( \ >> + " .option push\n\t" \ >> + " .option arch, +v\n\t" \ >> + " vl8r.v v" #i ", (%0)\n\t" \ >> + " .option pop\n\t" \ >> + :: "r"(base + (i) * vlenb) : "memory"); \ >> + }) \ >> + >> + RESTORE_VREG(0); >> + RESTORE_VREG(8); >> + RESTORE_VREG(16); >> + RESTORE_VREG(24); >> +#undef RESTORE_VREG >> + >> + /* Step 3: Restore CSR's last */ >> +#define WRITE_CSR(csr, val) \ >> + ({ \ >> + asm volatile( \ >> + " .option push\n\t" \ >> + " .option arch, +v\n\t" \ >> + " csrw " #csr ", %0\n\t" \ >> + " .option pop\n\t" \ >> + : \ >> + : "r"(val) \ >> + : "memory"); \ >> + }) \ >> + >> + /* Restore CSRs first */ >> + WRITE_CSR(vcsr, src->vcsr); >> + WRITE_CSR(vstart, src->vstart); >> +#undef WRITE_CSR > > Use csr_write() instead of WRITE_CSR(). > >> +} >> + >> +int sbi_vector_domain_init(void) >> +{ >> + csr_set(CSR_MSTATUS, MSTATUS_VS); >> + ulong vlenb = vector_vlenb(); >> + >> + if (vlenb > SBI_MAX_VLENB) { >> + sbi_printf("[Vector ERR:] vlenb range error\n"); >> + return SBI_ERR_BAD_RANGE; >> + } >> + >> + csr_clear(CSR_MSTATUS, MSTATUS_VS); > > Why this ? > > The mstatus_init() in sbi_hart.c sets MSTATUS_VS. This is done once during init at the start to check the vlenb, which can only be fetched if VS is enabled, hence the change. I am clearing it back to it original state. > > Regards, > Anup Hi Anup, Thanks for the comments. Please see inline comments above and clarify it so that I can push changes. Thanks Dave From anup at brainfault.org Mon May 11 20:27:23 2026 From: anup at brainfault.org (Anup Patel) Date: Tue, 12 May 2026 08:57:23 +0530 Subject: [PATCH 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: References: <20260408072123.7543-1-dave.patel@riscstar.com> <20260408072123.7543-2-dave.patel@riscstar.com> Message-ID: On Tue, May 12, 2026 at 12:58?AM Dave Patel wrote: > > On 5/9/26 09:33, Anup Patel wrote: > > On Wed, Apr 8, 2026 at 12:52?PM wrote: > >> > >> From: Dave Patel > >> > >> Eager context switch: Add support for saving and restoring RISC-V vector > >> extension state in OpenSBI. This introduces a per-hart vector context > >> structure and helper routines to perform full context save and restore. > >> > >> The vector context includes vcsr CSRs along with storage for all 32 vector > >> registers. The register state is saved and restored using byte-wise vector > >> load/store instructions (vs8r/vl8r). > >> > >> The implementation follows an eager context switching model where the entire > >> vector state is saved and restored on every context switch. This provides a > >> simple and deterministic mechanism without requiring lazy trap-based > >> management. > >> > >> Notes: > >> - The SBI_MAX_VLENB is configured using CONFIG_SBI_MAX_VLENB. > >> > >> Signed-off-by: Dave Patel > >> --- > >> include/sbi/sbi_vector.h | 30 ++++++++ > >> lib/sbi/Kconfig | 4 + > >> lib/sbi/objects.mk | 1 + > >> lib/sbi/sbi_vector.c | 155 +++++++++++++++++++++++++++++++++++++++ > >> 4 files changed, 190 insertions(+) > >> create mode 100644 include/sbi/sbi_vector.h > >> create mode 100644 lib/sbi/sbi_vector.c > >> > >> diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h > >> new file mode 100644 > >> index 00000000..3b63b02c > >> --- /dev/null > >> +++ b/include/sbi/sbi_vector.h > >> @@ -0,0 +1,30 @@ > >> +/* > >> + * SPDX-License-Identifier: BSD-2-Clause > >> + * > >> + * Copyright (c) 2026 RISCstar Solutions. > >> + * > >> + * Authors: > >> + * Dave Patel > >> + */ > >> + > >> +#ifndef __SBI_VECTOR_H__ > >> +#define __SBI_VECTOR_H__ > >> + > >> +#include > >> + > >> +#define SBI_MAX_VLENB CONFIG_SBI_MAX_VLENB > >> + > >> +struct sbi_vector_context { > >> + unsigned long vcsr; > >> + unsigned long vstart; > >> + > >> + /* size depends on VLEN */ > >> + uint8_t vregs[32 * SBI_MAX_VLENB]; > > > > We have same OpenSBI firmware running of multiple platforms > > so please discover max_vlenb as a hart feature in sbi_hart.c > > > > The vregs over here should be "uint8_t vregs[]" and > > "struct sbi_vector_context" must be dynamically allocated. > > I will allocate this dynamically, however the original changes were > setting it dynamically and moved to to static due to review comment. > > Am I ok in understanding that the > struct sbi_vector_context vec_ctx; should be struct sbi_vector_context * Drop the CONFIG_SBI_MAX_VLENB and sbi_vector_domain_init(). The "struct sbi_vector_context vec_ctx" in "struct hart_context" must be "struct sbi_vector_context *vec_ctx". Extend hart_context_init() to compute size and allocate "struct sbi_vector_context". The size of "struct sbi_vector_context" must be derived using CSR_VLENB. > > > > >> +}; > >> + > >> +void sbi_vector_save(struct sbi_vector_context *dst); > >> +void sbi_vector_restore(const struct sbi_vector_context *src); > >> +int sbi_vector_domain_init(void); > >> + > >> +#endif //__SBI_VECTOR_H__ > >> + > >> diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig > >> index 8479f861..b2432150 100644 > >> --- a/lib/sbi/Kconfig > >> +++ b/lib/sbi/Kconfig > >> @@ -74,4 +74,8 @@ config SBI_ECALL_VIRQ > >> bool "VIRQ extension" > >> default y > >> > >> +config SBI_MAX_VLENB > >> + int "Vector VLENB size" > >> + default 256 > >> + > >> endmenu > >> diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > >> index 68fc2036..ecb2b54e 100644 > >> --- a/lib/sbi/objects.mk > >> +++ b/lib/sbi/objects.mk > >> @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o > >> libsbi-objs-y += sbi_unpriv.o > >> libsbi-objs-y += sbi_expected_trap.o > >> libsbi-objs-y += sbi_cppc.o > >> +libsbi-objs-y += sbi_vector.o > >> diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c > >> new file mode 100644 > >> index 00000000..29434e2e > >> --- /dev/null > >> +++ b/lib/sbi/sbi_vector.c > >> @@ -0,0 +1,155 @@ > >> +/* > >> + * SPDX-License-Identifier: BSD-2-Clause > >> + * > >> + * Copyright (c) 2026 RISCstar Solutions. > >> + * > >> + * Authors: > >> + * Dave Patel > >> + */ > >> + > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> + > >> +#ifdef OPENSBI_CC_SUPPORT_VECTOR > >> + > >> +static inline unsigned long vector_vlenb(void) > >> +{ > >> + unsigned long vlenb = 0; > >> + > >> + asm volatile ( > >> + ".option push\n\t" > >> + ".option arch, +v\n\t" > >> + "csrr %0, vlenb\n\t" > >> + ".option pop\n\t" > >> + : "=r"(vlenb) > >> + : > >> + : "memory"); > >> + > >> + return vlenb; > >> +} > >> + > >> +void sbi_vector_save(struct sbi_vector_context *dst) > >> +{ > >> + if (!dst) > >> + return; > >> + > >> +#define READ_CSR(dst, csr) \ > >> + ({ \ > >> + asm volatile ( \ > >> + " .option push\n\t" \ > >> + " .option arch, +v\n\t" \ > >> + " csrr %0, " #csr "\n\t" \ > >> + " .option pop\n\t" \ > >> + : "=r"(dst) \ > >> + : \ > >> + : "memory"); \ > >> + }) \ > >> + > >> + /* Step 1: Save CSRs */ > >> + READ_CSR(dst->vcsr, vcsr); > >> + READ_CSR(dst->vstart, vstart); > > > > use csr_read() instead of READ_CSR(). > > ok thanks, will do this. > > > > >> + > >> +#undef READ_CSR > >> + > >> + ulong vlenb = vector_vlenb(); > >> + uint8_t *base = dst->vregs; > >> + > >> + /* Step 3: Save vector registers */ > >> +#define SAVE_VREG(i) \ > >> + ({ \ > >> + asm volatile( \ > >> + " .option push\n\t" \ > >> + " .option arch, +v\n\t" \ > >> + " vs8r.v v" #i ", (%0)\n\t" \ > >> + " .option pop\n\t" \ > >> + :: "r"(base + (i) * vlenb) : "memory"); \ > >> + }) \ > >> + > >> + SAVE_VREG(0); > >> + SAVE_VREG(8); > >> + SAVE_VREG(16); > >> + SAVE_VREG(24); > >> + > >> +#undef SAVE_VREG > >> +} > >> + > >> +void sbi_vector_restore(const struct sbi_vector_context *src) > >> +{ > >> + if (!src) > >> + return; > >> + > >> + const uint8_t *base = src->vregs; > >> + ulong vlenb = vector_vlenb(); > >> + > >> + /* Step 2: Restore vector registers */ > >> +#define RESTORE_VREG(i) \ > >> + ({ \ > >> + asm volatile( \ > >> + " .option push\n\t" \ > >> + " .option arch, +v\n\t" \ > >> + " vl8r.v v" #i ", (%0)\n\t" \ > >> + " .option pop\n\t" \ > >> + :: "r"(base + (i) * vlenb) : "memory"); \ > >> + }) \ > >> + > >> + RESTORE_VREG(0); > >> + RESTORE_VREG(8); > >> + RESTORE_VREG(16); > >> + RESTORE_VREG(24); > >> +#undef RESTORE_VREG > >> + > >> + /* Step 3: Restore CSR's last */ > >> +#define WRITE_CSR(csr, val) \ > >> + ({ \ > >> + asm volatile( \ > >> + " .option push\n\t" \ > >> + " .option arch, +v\n\t" \ > >> + " csrw " #csr ", %0\n\t" \ > >> + " .option pop\n\t" \ > >> + : \ > >> + : "r"(val) \ > >> + : "memory"); \ > >> + }) \ > >> + > >> + /* Restore CSRs first */ > >> + WRITE_CSR(vcsr, src->vcsr); > >> + WRITE_CSR(vstart, src->vstart); > >> +#undef WRITE_CSR > > > > Use csr_write() instead of WRITE_CSR(). > > > >> +} > >> + > >> +int sbi_vector_domain_init(void) > >> +{ > >> + csr_set(CSR_MSTATUS, MSTATUS_VS); > >> + ulong vlenb = vector_vlenb(); > >> + > >> + if (vlenb > SBI_MAX_VLENB) { > >> + sbi_printf("[Vector ERR:] vlenb range error\n"); > >> + return SBI_ERR_BAD_RANGE; > >> + } > >> + > >> + csr_clear(CSR_MSTATUS, MSTATUS_VS); > > > > Why this ? > > > > The mstatus_init() in sbi_hart.c sets MSTATUS_VS. > > This is done once during init at the start to check the vlenb, which can > only be fetched if VS is enabled, hence the change. I am clearing it > back to it original state. This entire function is not needed. See above comment. Regards, Anup From anup at brainfault.org Mon May 11 21:28:41 2026 From: anup at brainfault.org (Anup Patel) Date: Tue, 12 May 2026 09:58:41 +0530 Subject: [PATCH 0/6] Extend irqchip framework for MSIs and line sensing In-Reply-To: <20260423052339.356900-1-anup.patel@oss.qualcomm.com> References: <20260423052339.356900-1-anup.patel@oss.qualcomm.com> Message-ID: On Thu, Apr 23, 2026 at 10:53?AM Anup Patel wrote: > > This series further extends the OpenSBI irqchip framework to: > 1) Allow interrupt clients specify line sensing type > 2) Support MSI controller (aka IMSIC) as irqchip device > > These patches can also be found the irqchip_imp2_v1 branch > at: https://github.com/avpatel/opensbi.git > > Anup Patel (6): > lib: sbi_irqchip: Check full range for existing handlers in > sbi_irqchip_register_handler() > lib: sbi_irqchip: Keep the handler list in sorted order for irqchip > lib: sbi_irqchip: Allow interrupt client to specify line sensing > lib: sbi_irqchip: Allow marking hardware interrupts as reserved > lib: sbi_irqchip: Allow setting hardware interrupt affinity > lib: sbi_irqchip: Add support for registering MSI handlers > > include/sbi/sbi_irqchip.h | 49 +++++++- > lib/sbi/sbi_irqchip.c | 258 +++++++++++++++++++++++++++++++++++--- > lib/utils/irqchip/imsic.c | 9 +- > 3 files changed, 292 insertions(+), 24 deletions(-) > > -- > 2.43.0 > This series is a critical dependency for APLIC and IMSIC drivers in OpenSBI hence merging it sooner. Applied this series to the riscv/opensbi repo. Thanks, Anup From nylon.chen at sifive.com Mon May 11 23:44:35 2026 From: nylon.chen at sifive.com (Nylon Chen) Date: Tue, 12 May 2026 14:44:35 +0800 Subject: [PATCH v3 6/6] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection In-Reply-To: References: <88b1470e1e3348d454b4b995a11a85c01914f7af.1778176768.git.evvoevod@tenstorrent.com> Message-ID: Anup Patel ? 2026?5?9??? ??2:51??? > > On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin > wrote: > > > > The location of the RNMI/E trap vectors in the Smrnmi extension is > > implementation-defined, so platforms with vendor-specific NMI vector > > mechanisms must install the firmware's NMI entry points themselves. > > > > Add an smrnmi_handlers_init() callback to sbi_platform_operations that > > receives the firmware entry points and lets platform code install them > > at the hardware-specific vector locations. Two pointers are passed: > > > > - _trap_rnmi_handler: the dedicated RNMI entry point that saves > > context using the Smrnmi MN* CSRs and returns via mnret. > > - _trap_handler: the regular M-mode trap entry since RNME is taken > > as a regular M-mode trap with NMIE=0. > > > > When Smrnmi is present, install the platform's NMI vectors via the new > > callback, initialize MNSCRATCH with the per-hart scratch pointer, and > > set MNSTATUS.NMIE. > > > > Smrnmi-enabled platforms must register smrnmi_handlers_init; if the > > extension is detected but no callback is registered, sbi_panic() is > > called since enabling NMIs without handlers in place would route > > subsequent traps into nowhere. > > > > Signed-off-by: Evgeny Voevodin > > LGTM. > > Reviewed-by: Anup Patel > > Regards, > Anup > > > --- > > include/sbi/sbi_platform.h | 4 ++++ > > lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ > > 2 files changed, 24 insertions(+) > > > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > > index 715df499..fe382b56 100644 > > --- a/include/sbi/sbi_platform.h > > +++ b/include/sbi/sbi_platform.h > > @@ -150,6 +150,10 @@ struct sbi_platform_operations { > > /** platform specific pmp disable on current HART */ > > void (*pmp_disable)(unsigned int n); > > > > + /** platform specific Smrnmi handlers init on current HART */ > > + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), > > + void (*rnme_handler)(void)); > > + > > /** platform specific Smrnmi NMI handler. > > * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ > > int (*rnmi_handler)(struct sbi_trap_context *tcntx); > > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > > index 781161e5..92c602aa 100644 > > --- a/lib/sbi/sbi_hart.c > > +++ b/lib/sbi/sbi_hart.c > > @@ -532,6 +532,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) > > if (rc) > > return rc; > > > > + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { > > + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); > > + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); > > + extern void _trap_rnmi_handler(void); > > + extern void _trap_handler(void); > > + > > + if (!ops || !ops->smrnmi_handlers_init) > > + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); > > + > > + /* Reuse _trap_handler for the RNME slot since RNME is taken > > + * as a regular M-mode trap with NMIE=0. */ > > + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); > > + > > + /* Initialize MNSCRATCH for the RNMI handler */ > > + csr_write(CSR_MNSCRATCH, scratch); > > + > > + /* Enable NMIs */ > > + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); Two concerns about this block First, sbi_panic() when smrnmi_handlers_init is NULL is too strict. Not all platforms with Smrnmi need to program a vendor-specific NMI vector register. Some implementations have a fixed NMI vector address or route NMIs through mtvec by default; for these, setting MNSCRATCH and NMIE is sufficient and no platform callback is needed. This can be reproduced with QEMU virt: qemu-system-riscv64 -M virt -cpu rv64,smrnmi=on -m 256m \ -bios fw_jump.bin -nographic -no-reboot The firmware hangs with zero output. The panic fires before sbi_platform_early_init() so the console is not yet initialized and the message is silently dropped. Adding a no-op stub to the generic platform confirms the panic is the sole cause -- boot proceeds normally with the stub in place. Second, the callback returns void, so there is no way to detect whether the platform succeeded in programming the NMI vector. csr_set(MNSTATUS, NMIE) then runs unconditionally. If the callback fails silently, NMIs are enabled but the hardware vector register is left at its reset value. The first NMI would jump to an undefined address. Suggested fix: /* allow NULL: platforms with fixed or mtvec-based NMI vectors * do not need to program any vendor register */ if (ops && ops->smrnmi_handlers_init) { int ret = ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); if (ret) return ret; } csr_write(CSR_MNSCRATCH, scratch); csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); This requires changing the callback signature from void to int. Platforms that have nothing to program return 0, platforms that fail return an error, and platforms with no callback at all are handled gracefully without a panic. > > + } > > + > > #define __check_hpm_csr(__csr, __mask) \ > > oldval = csr_read_allowed(__csr, &trap); \ > > if (!trap.cause) { \ > > -- > > 2.43.0 > > From anup at brainfault.org Tue May 12 00:58:59 2026 From: anup at brainfault.org (Anup Patel) Date: Tue, 12 May 2026 13:28:59 +0530 Subject: [PATCH v3 6/6] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection In-Reply-To: References: <88b1470e1e3348d454b4b995a11a85c01914f7af.1778176768.git.evvoevod@tenstorrent.com> Message-ID: On Tue, May 12, 2026 at 12:14?PM Nylon Chen wrote: > > Anup Patel ? 2026?5?9??? ??2:51??? > > > > > On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin > > wrote: > > > > > > The location of the RNMI/E trap vectors in the Smrnmi extension is > > > implementation-defined, so platforms with vendor-specific NMI vector > > > mechanisms must install the firmware's NMI entry points themselves. > > > > > > Add an smrnmi_handlers_init() callback to sbi_platform_operations that > > > receives the firmware entry points and lets platform code install them > > > at the hardware-specific vector locations. Two pointers are passed: > > > > > > - _trap_rnmi_handler: the dedicated RNMI entry point that saves > > > context using the Smrnmi MN* CSRs and returns via mnret. > > > - _trap_handler: the regular M-mode trap entry since RNME is taken > > > as a regular M-mode trap with NMIE=0. > > > > > > When Smrnmi is present, install the platform's NMI vectors via the new > > > callback, initialize MNSCRATCH with the per-hart scratch pointer, and > > > set MNSTATUS.NMIE. > > > > > > Smrnmi-enabled platforms must register smrnmi_handlers_init; if the > > > extension is detected but no callback is registered, sbi_panic() is > > > called since enabling NMIs without handlers in place would route > > > subsequent traps into nowhere. > > > > > > Signed-off-by: Evgeny Voevodin > > > > LGTM. > > > > Reviewed-by: Anup Patel > > > > Regards, > > Anup > > > > > --- > > > include/sbi/sbi_platform.h | 4 ++++ > > > lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ > > > 2 files changed, 24 insertions(+) > > > > > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > > > index 715df499..fe382b56 100644 > > > --- a/include/sbi/sbi_platform.h > > > +++ b/include/sbi/sbi_platform.h > > > @@ -150,6 +150,10 @@ struct sbi_platform_operations { > > > /** platform specific pmp disable on current HART */ > > > void (*pmp_disable)(unsigned int n); > > > > > > + /** platform specific Smrnmi handlers init on current HART */ > > > + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), > > > + void (*rnme_handler)(void)); > > > + > > > /** platform specific Smrnmi NMI handler. > > > * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ > > > int (*rnmi_handler)(struct sbi_trap_context *tcntx); > > > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > > > index 781161e5..92c602aa 100644 > > > --- a/lib/sbi/sbi_hart.c > > > +++ b/lib/sbi/sbi_hart.c > > > @@ -532,6 +532,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) > > > if (rc) > > > return rc; > > > > > > + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { > > > + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); > > > + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); > > > + extern void _trap_rnmi_handler(void); > > > + extern void _trap_handler(void); > > > + > > > + if (!ops || !ops->smrnmi_handlers_init) > > > + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); > > > + > > > + /* Reuse _trap_handler for the RNME slot since RNME is taken > > > + * as a regular M-mode trap with NMIE=0. */ > > > + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); > > > + > > > + /* Initialize MNSCRATCH for the RNMI handler */ > > > + csr_write(CSR_MNSCRATCH, scratch); > > > + > > > + /* Enable NMIs */ > > > + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > Two concerns about this block > > First, sbi_panic() when smrnmi_handlers_init is NULL is too strict. > Not all platforms with Smrnmi need to program a vendor-specific NMI > vector register. > Some implementations have a fixed NMI vector address or route NMIs > through mtvec by default; for these, setting MNSCRATCH and NMIE is > sufficient and no platform callback is needed. > > This can be reproduced with QEMU virt: > > qemu-system-riscv64 -M virt -cpu rv64,smrnmi=on -m 256m \ > -bios fw_jump.bin -nographic -no-reboot > > The firmware hangs with zero output. The panic fires before > sbi_platform_early_init() so the console is not yet initialized and > the message is silently dropped. > > Adding a no-op stub to the generic platform confirms the panic is the > sole cause -- boot proceeds normally with the stub in place. > > Second, the callback returns void, so there is no way to detect > whether the platform succeeded in programming the NMI vector. > csr_set(MNSTATUS, NMIE) then runs unconditionally. > If the callback fails silently, NMIs are enabled but the hardware > vector register is left at its reset value. The first NMI would jump > to an undefined address. > > Suggested fix: > > /* allow NULL: platforms with fixed or mtvec-based NMI vectors > * do not need to program any vendor register */ > if (ops && ops->smrnmi_handlers_init) { > int ret = ops->smrnmi_handlers_init(_trap_rnmi_handler, > _trap_handler); > if (ret) > return ret; > } > > csr_write(CSR_MNSCRATCH, scratch); > csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > > This requires changing the callback signature from void to int. > > Platforms that have nothing to program return 0, platforms that fail > return an error, and platforms with no callback at all are handled > gracefully without a panic. > Please send a patch with appropriate Fixes tag. Regards, Anup From peter.lin at sifive.com Tue May 12 01:23:50 2026 From: peter.lin at sifive.com (Yu-Chien Peter Lin) Date: Tue, 12 May 2026 16:23:50 +0800 Subject: [PATCH] lib: utils: fdt_domain: add support for root domain region inheritance In-Reply-To: References: <20260327043357.3452008-1-peter.lin@sifive.com> Message-ID: Hi Anup, Thanks for suggetion, I'd like to double-check before sending v2 patch. On Sun, May 10, 2026 at 11:34:49AM +0530, Anup Patel wrote: > On Fri, Mar 27, 2026 at 10:04?AM Yu-Chien Peter Lin > wrote: > > > > Add the "root-regions" property in domain device-tree nodes to > > allow domains to inherit all regions from the root domain. This > > simplifies configuration for domains that need access to most > > root domain regions with only minor exclusions or additions. > > Overall, this is a good approach but I suggest renaming this property > to "root-regions-inheritance" which takes the following possible values: > > 1) "all" : Inherit all regions from the root domain > 2) "su-only": Inherit regions accessible to S-mode or U-mode from the > root domain 1. Do you mean: a) any root regions that have any SU permission bits set regardless of the M?mode permission bits, or b) only regions that satisfy SBI_DOMAIN_MEMREGION_SU_ONLY_ACCESS(flags)? 2. For firmware regions marked with SBI_DOMAIN_MEMREGION_FW, should those always be inherited, even when "root-regions-inheritance" is "none" or "su-only"? Thanks, Peter Lin > 3) "none": Inherit no regions from the root domain > > From the above, if "root-regions-inheritance" is not set then it is > assumed to be "su-only". > > Regards, > Anup From nylon.chen at sifive.com Tue May 12 01:25:08 2026 From: nylon.chen at sifive.com (Nylon Chen) Date: Tue, 12 May 2026 16:25:08 +0800 Subject: [PATCH v3 6/6] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection In-Reply-To: References: <88b1470e1e3348d454b4b995a11a85c01914f7af.1778176768.git.evvoevod@tenstorrent.com> Message-ID: Anup Patel ? 2026?5?12??? ??3:59??? > > On Tue, May 12, 2026 at 12:14?PM Nylon Chen wrote: > > > > Anup Patel ? 2026?5?9??? ??2:51??? > > > > > > > > On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin > > > wrote: > > > > > > > > The location of the RNMI/E trap vectors in the Smrnmi extension is > > > > implementation-defined, so platforms with vendor-specific NMI vector > > > > mechanisms must install the firmware's NMI entry points themselves. > > > > > > > > Add an smrnmi_handlers_init() callback to sbi_platform_operations that > > > > receives the firmware entry points and lets platform code install them > > > > at the hardware-specific vector locations. Two pointers are passed: > > > > > > > > - _trap_rnmi_handler: the dedicated RNMI entry point that saves > > > > context using the Smrnmi MN* CSRs and returns via mnret. > > > > - _trap_handler: the regular M-mode trap entry since RNME is taken > > > > as a regular M-mode trap with NMIE=0. > > > > > > > > When Smrnmi is present, install the platform's NMI vectors via the new > > > > callback, initialize MNSCRATCH with the per-hart scratch pointer, and > > > > set MNSTATUS.NMIE. > > > > > > > > Smrnmi-enabled platforms must register smrnmi_handlers_init; if the > > > > extension is detected but no callback is registered, sbi_panic() is > > > > called since enabling NMIs without handlers in place would route > > > > subsequent traps into nowhere. > > > > > > > > Signed-off-by: Evgeny Voevodin > > > > > > LGTM. > > > > > > Reviewed-by: Anup Patel > > > > > > Regards, > > > Anup > > > > > > > --- > > > > include/sbi/sbi_platform.h | 4 ++++ > > > > lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ > > > > 2 files changed, 24 insertions(+) > > > > > > > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > > > > index 715df499..fe382b56 100644 > > > > --- a/include/sbi/sbi_platform.h > > > > +++ b/include/sbi/sbi_platform.h > > > > @@ -150,6 +150,10 @@ struct sbi_platform_operations { > > > > /** platform specific pmp disable on current HART */ > > > > void (*pmp_disable)(unsigned int n); > > > > > > > > + /** platform specific Smrnmi handlers init on current HART */ > > > > + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), > > > > + void (*rnme_handler)(void)); > > > > + > > > > /** platform specific Smrnmi NMI handler. > > > > * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ > > > > int (*rnmi_handler)(struct sbi_trap_context *tcntx); > > > > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > > > > index 781161e5..92c602aa 100644 > > > > --- a/lib/sbi/sbi_hart.c > > > > +++ b/lib/sbi/sbi_hart.c > > > > @@ -532,6 +532,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) > > > > if (rc) > > > > return rc; > > > > > > > > + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { > > > > + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); > > > > + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); > > > > + extern void _trap_rnmi_handler(void); > > > > + extern void _trap_handler(void); > > > > + > > > > + if (!ops || !ops->smrnmi_handlers_init) > > > > + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); > > > > + > > > > + /* Reuse _trap_handler for the RNME slot since RNME is taken > > > > + * as a regular M-mode trap with NMIE=0. */ > > > > + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); > > > > + > > > > + /* Initialize MNSCRATCH for the RNMI handler */ > > > > + csr_write(CSR_MNSCRATCH, scratch); > > > > + > > > > + /* Enable NMIs */ > > > > + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > > Two concerns about this block > > > > First, sbi_panic() when smrnmi_handlers_init is NULL is too strict. > > Not all platforms with Smrnmi need to program a vendor-specific NMI > > vector register. > > Some implementations have a fixed NMI vector address or route NMIs > > through mtvec by default; for these, setting MNSCRATCH and NMIE is > > sufficient and no platform callback is needed. > > > > This can be reproduced with QEMU virt: > > > > qemu-system-riscv64 -M virt -cpu rv64,smrnmi=on -m 256m \ > > -bios fw_jump.bin -nographic -no-reboot > > > > The firmware hangs with zero output. The panic fires before > > sbi_platform_early_init() so the console is not yet initialized and > > the message is silently dropped. > > > > Adding a no-op stub to the generic platform confirms the panic is the > > sole cause -- boot proceeds normally with the stub in place. > > > > Second, the callback returns void, so there is no way to detect > > whether the platform succeeded in programming the NMI vector. > > csr_set(MNSTATUS, NMIE) then runs unconditionally. > > If the callback fails silently, NMIs are enabled but the hardware > > vector register is left at its reset value. The first NMI would jump > > to an undefined address. > > > > Suggested fix: > > > > /* allow NULL: platforms with fixed or mtvec-based NMI vectors > > * do not need to program any vendor register */ > > if (ops && ops->smrnmi_handlers_init) { > > int ret = ops->smrnmi_handlers_init(_trap_rnmi_handler, > > _trap_handler); > > if (ret) > > return ret; > > } > > > > csr_write(CSR_MNSCRATCH, scratch); > > csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > > > > This requires changing the callback signature from void to int. > > > > Platforms that have nothing to program return 0, platforms that fail > > return an error, and platforms with no callback at all are handled > > gracefully without a panic. > > > > Please send a patch with appropriate Fixes tag. Will do. While preparing the patch, I also noticed the series is missing non-retentive suspend/resume handling for CSR_MNSCRATCH and MNSTATUS.NMIE. After non-retentive suspend, the hart is fully powered off and all CSR state is reset. On resume, sbi_hsm_hart_resume_finish() calls __sbi_hsm_suspend_non_ret_restore() and jumps directly to the kernel without going through sbi_hart_init(), so the Smrnmi CSRs set up in hart_detect_features() are never re-initialized. I will send both fixes together as a two-patch series after testing. > > Regards, > Anup From apatel at ventanamicro.com Tue May 12 08:40:42 2026 From: apatel at ventanamicro.com (Anup Patel) Date: Tue, 12 May 2026 21:10:42 +0530 Subject: [PATCH v3 6/6] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection In-Reply-To: References: <88b1470e1e3348d454b4b995a11a85c01914f7af.1778176768.git.evvoevod@tenstorrent.com> Message-ID: On Tue, May 12, 2026 at 2:00?PM Nylon Chen wrote: > > Anup Patel ? 2026?5?12??? ??3:59??? > > > > On Tue, May 12, 2026 at 12:14?PM Nylon Chen wrote: > > > > > > Anup Patel ? 2026?5?9??? ??2:51??? > > > > > > > > > > > On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin > > > > wrote: > > > > > > > > > > The location of the RNMI/E trap vectors in the Smrnmi extension is > > > > > implementation-defined, so platforms with vendor-specific NMI vector > > > > > mechanisms must install the firmware's NMI entry points themselves. > > > > > > > > > > Add an smrnmi_handlers_init() callback to sbi_platform_operations that > > > > > receives the firmware entry points and lets platform code install them > > > > > at the hardware-specific vector locations. Two pointers are passed: > > > > > > > > > > - _trap_rnmi_handler: the dedicated RNMI entry point that saves > > > > > context using the Smrnmi MN* CSRs and returns via mnret. > > > > > - _trap_handler: the regular M-mode trap entry since RNME is taken > > > > > as a regular M-mode trap with NMIE=0. > > > > > > > > > > When Smrnmi is present, install the platform's NMI vectors via the new > > > > > callback, initialize MNSCRATCH with the per-hart scratch pointer, and > > > > > set MNSTATUS.NMIE. > > > > > > > > > > Smrnmi-enabled platforms must register smrnmi_handlers_init; if the > > > > > extension is detected but no callback is registered, sbi_panic() is > > > > > called since enabling NMIs without handlers in place would route > > > > > subsequent traps into nowhere. > > > > > > > > > > Signed-off-by: Evgeny Voevodin > > > > > > > > LGTM. > > > > > > > > Reviewed-by: Anup Patel > > > > > > > > Regards, > > > > Anup > > > > > > > > > --- > > > > > include/sbi/sbi_platform.h | 4 ++++ > > > > > lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ > > > > > 2 files changed, 24 insertions(+) > > > > > > > > > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > > > > > index 715df499..fe382b56 100644 > > > > > --- a/include/sbi/sbi_platform.h > > > > > +++ b/include/sbi/sbi_platform.h > > > > > @@ -150,6 +150,10 @@ struct sbi_platform_operations { > > > > > /** platform specific pmp disable on current HART */ > > > > > void (*pmp_disable)(unsigned int n); > > > > > > > > > > + /** platform specific Smrnmi handlers init on current HART */ > > > > > + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), > > > > > + void (*rnme_handler)(void)); > > > > > + > > > > > /** platform specific Smrnmi NMI handler. > > > > > * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ > > > > > int (*rnmi_handler)(struct sbi_trap_context *tcntx); > > > > > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > > > > > index 781161e5..92c602aa 100644 > > > > > --- a/lib/sbi/sbi_hart.c > > > > > +++ b/lib/sbi/sbi_hart.c > > > > > @@ -532,6 +532,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) > > > > > if (rc) > > > > > return rc; > > > > > > > > > > + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { > > > > > + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); > > > > > + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); > > > > > + extern void _trap_rnmi_handler(void); > > > > > + extern void _trap_handler(void); > > > > > + > > > > > + if (!ops || !ops->smrnmi_handlers_init) > > > > > + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); > > > > > + > > > > > + /* Reuse _trap_handler for the RNME slot since RNME is taken > > > > > + * as a regular M-mode trap with NMIE=0. */ > > > > > + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); > > > > > + > > > > > + /* Initialize MNSCRATCH for the RNMI handler */ > > > > > + csr_write(CSR_MNSCRATCH, scratch); > > > > > + > > > > > + /* Enable NMIs */ > > > > > + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > > > Two concerns about this block > > > > > > First, sbi_panic() when smrnmi_handlers_init is NULL is too strict. > > > Not all platforms with Smrnmi need to program a vendor-specific NMI > > > vector register. > > > Some implementations have a fixed NMI vector address or route NMIs > > > through mtvec by default; for these, setting MNSCRATCH and NMIE is > > > sufficient and no platform callback is needed. > > > > > > This can be reproduced with QEMU virt: > > > > > > qemu-system-riscv64 -M virt -cpu rv64,smrnmi=on -m 256m \ > > > -bios fw_jump.bin -nographic -no-reboot > > > > > > The firmware hangs with zero output. The panic fires before > > > sbi_platform_early_init() so the console is not yet initialized and > > > the message is silently dropped. > > > > > > Adding a no-op stub to the generic platform confirms the panic is the > > > sole cause -- boot proceeds normally with the stub in place. > > > > > > Second, the callback returns void, so there is no way to detect > > > whether the platform succeeded in programming the NMI vector. > > > csr_set(MNSTATUS, NMIE) then runs unconditionally. > > > If the callback fails silently, NMIs are enabled but the hardware > > > vector register is left at its reset value. The first NMI would jump > > > to an undefined address. > > > > > > Suggested fix: > > > > > > /* allow NULL: platforms with fixed or mtvec-based NMI vectors > > > * do not need to program any vendor register */ > > > if (ops && ops->smrnmi_handlers_init) { > > > int ret = ops->smrnmi_handlers_init(_trap_rnmi_handler, > > > _trap_handler); > > > if (ret) > > > return ret; > > > } > > > > > > csr_write(CSR_MNSCRATCH, scratch); > > > csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > > > > > > This requires changing the callback signature from void to int. > > > > > > Platforms that have nothing to program return 0, platforms that fail > > > return an error, and platforms with no callback at all are handled > > > gracefully without a panic. > > > > > > > Please send a patch with appropriate Fixes tag. > Will do. > > While preparing the patch, I also noticed the series is missing > non-retentive suspend/resume handling for CSR_MNSCRATCH and > MNSTATUS.NMIE. > > After non-retentive suspend, the hart is fully powered off and all CSR > state is reset. > > On resume, sbi_hsm_hart_resume_finish() calls > __sbi_hsm_suspend_non_ret_restore() and jumps directly to the kernel > without going through sbi_hart_init(), so the Smrnmi CSRs set up in > hart_detect_features() are never re-initialized. I think Smrnmi in hart_detect_features() should be moved to a separate function which should be called from two places: 1) At the current location from hart_detect_features() before we use trap-based extension detection 2) In sbi_hart_init() for both cold boot and warm boot path Regards, Anup From anup at brainfault.org Tue May 12 08:50:40 2026 From: anup at brainfault.org (Anup Patel) Date: Tue, 12 May 2026 21:20:40 +0530 Subject: [PATCH] lib: utils: fdt_domain: add support for root domain region inheritance In-Reply-To: References: <20260327043357.3452008-1-peter.lin@sifive.com> Message-ID: On Tue, May 12, 2026 at 1:53?PM Yu-Chien Peter Lin wrote: > > Hi Anup, > > Thanks for suggetion, I'd like to double-check before > sending v2 patch. > > On Sun, May 10, 2026 at 11:34:49AM +0530, Anup Patel wrote: > > On Fri, Mar 27, 2026 at 10:04?AM Yu-Chien Peter Lin > > wrote: > > > > > > Add the "root-regions" property in domain device-tree nodes to > > > allow domains to inherit all regions from the root domain. This > > > simplifies configuration for domains that need access to most > > > root domain regions with only minor exclusions or additions. > > > > Overall, this is a good approach but I suggest renaming this property > > to "root-regions-inheritance" which takes the following possible values: > > > > 1) "all" : Inherit all regions from the root domain > > 2) "su-only": Inherit regions accessible to S-mode or U-mode from the > > root domain > > 1. Do you mean: > a) any root regions that have any SU permission bits set regardless of the > M?mode permission bits, or > b) only regions that satisfy SBI_DOMAIN_MEMREGION_SU_ONLY_ACCESS(flags)? Actually, none of these. Apologies for the confusion. I meant the current policy of copying select regions from root domain to non-root domain which is only copy regions which have no SU permissions set. This means the following possible values: 1) "all" : Inherit all regions from the root domain 2) "m-only": Inherit regions of root domain not having any SU permissions set (current policy) which also covers firmware regions. 3) "none": Inherit no regions from the root domain > 2. For firmware regions marked with SBI_DOMAIN_MEMREGION_FW, should those always > be inherited, even when "root-regions-inheritance" is "none" or "su-only"? Yes, FW regions should always be inherited because these are for OpenSBI itself and OpenSBI is always resident in background. Regards, Anup From anup at brainfault.org Tue May 12 09:04:55 2026 From: anup at brainfault.org (Anup Patel) Date: Tue, 12 May 2026 21:34:55 +0530 Subject: [PATCH] lib: utils: fdt_domain: add support for root domain region inheritance In-Reply-To: References: <20260327043357.3452008-1-peter.lin@sifive.com> Message-ID: On Tue, May 12, 2026 at 9:20?PM Anup Patel wrote: > > On Tue, May 12, 2026 at 1:53?PM Yu-Chien Peter Lin wrote: > > > > Hi Anup, > > > > Thanks for suggetion, I'd like to double-check before > > sending v2 patch. > > > > On Sun, May 10, 2026 at 11:34:49AM +0530, Anup Patel wrote: > > > On Fri, Mar 27, 2026 at 10:04?AM Yu-Chien Peter Lin > > > wrote: > > > > > > > > Add the "root-regions" property in domain device-tree nodes to > > > > allow domains to inherit all regions from the root domain. This > > > > simplifies configuration for domains that need access to most > > > > root domain regions with only minor exclusions or additions. > > > > > > Overall, this is a good approach but I suggest renaming this property > > > to "root-regions-inheritance" which takes the following possible values: > > > > > > 1) "all" : Inherit all regions from the root domain > > > 2) "su-only": Inherit regions accessible to S-mode or U-mode from the > > > root domain > > > > 1. Do you mean: > > a) any root regions that have any SU permission bits set regardless of the > > M?mode permission bits, or > > b) only regions that satisfy SBI_DOMAIN_MEMREGION_SU_ONLY_ACCESS(flags)? > > Actually, none of these. Apologies for the confusion. > > I meant the current policy of copying select regions from > root domain to non-root domain which is only copy regions > which have no SU permissions set. > > This means the following possible values: > > 1) "all" : Inherit all regions from the root domain > 2) "m-only": Inherit regions of root domain not having > any SU permissions set (current policy) which also > covers firmware regions. > 3) "none": Inherit no regions from the root domain > > > 2. For firmware regions marked with SBI_DOMAIN_MEMREGION_FW, should those always > > be inherited, even when "root-regions-inheritance" is "none" or "su-only"? > > Yes, FW regions should always be inherited because these are for > OpenSBI itself and OpenSBI is always resident in background. > Thinking about this more, the "none" option should not be there because m-only regions (including fw regions) are always required for proper functioning of OpenSBI. Regards, Anup From dave.patel at riscstar.com Wed May 13 00:14:15 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Wed, 13 May 2026 08:14:15 +0100 Subject: [PATCH v4 0/3] Add eager FP and RISC-V vector context switching support Message-ID: <20260513071418.50957-1-dave.patel@riscstar.com> Hi Anup, Thank you for taking out time and reviewing the patches. Following your review comments, I have made following changes. Also I have tested the changes. The changes include: - Dropping CONFIG_SBI_MAX_VLENB and sbi_vector_domain_init() - struct sbi_vector_context vec_ctx is now struct * in 'struct hart_context' - changing READ_CSR and WRITE_CSR to vcsr_read and vcsr_write I have covered all your comments, please can you have a look and let me know. Thanks and Regards, Dave Signed-off-by: Dave Patel From dave.patel at riscstar.com Wed May 13 00:14:16 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Wed, 13 May 2026 08:14:16 +0100 Subject: [PATCH v4 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260513071418.50957-1-dave.patel@riscstar.com> References: <20260513071418.50957-1-dave.patel@riscstar.com> Message-ID: <20260513071418.50957-2-dave.patel@riscstar.com> From: Dave Patel Eager context switch: Add support for saving and restoring RISC-V vector extension state in OpenSBI. This introduces a per-hart vector context structure and helper routines to perform full context save and restore. The vector context includes vcsr CSRs along with storage for all 32 vector registers. The register state is saved and restored using byte-wise vector load/store instructions (vs8r/vl8r). The implementation follows an eager context switching model where the entire vector state is saved and restored on every context switch. This provides a simple and deterministic mechanism without requiring lazy trap-based management. Signed-off-by: Dave Patel --- include/sbi/sbi_vector.h | 28 ++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_vector.c | 136 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_vector.c diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h new file mode 100644 index 00000000..bbd857c3 --- /dev/null +++ b/include/sbi/sbi_vector.h @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#ifndef __SBI_VECTOR_H__ +#define __SBI_VECTOR_H__ + +#include + +struct sbi_vector_context { + unsigned long vcsr; + unsigned long vstart; + + /* size depends on VLEN */ + uint8_t vregs[]; +}; + +void sbi_vector_save(struct sbi_vector_context *dst); +void sbi_vector_restore(const struct sbi_vector_context *src); +unsigned long vector_vlenb(void); + +#endif //__SBI_VECTOR_H__ + diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 97cc4521..ddb2e7ac 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o +libsbi-objs-y += sbi_vector.o diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c new file mode 100644 index 00000000..0085aa02 --- /dev/null +++ b/lib/sbi/sbi_vector.c @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENSBI_CC_SUPPORT_VECTOR + +unsigned long vector_vlenb(void) +{ + unsigned long vlenb = 0; + + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "csrr %0, vlenb\n\t" + ".option pop\n\t" + : "=r"(vlenb) + : + : "memory"); + + return vlenb; +} + +void sbi_vector_save(struct sbi_vector_context *dst) +{ + if (!dst) + return; + +#define vcsr_read(dst, csr) \ + ({ \ + asm volatile ( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " csrr %0, " #csr "\n\t" \ + " .option pop\n\t" \ + : "=r"(dst) \ + : \ + : "memory"); \ + }) \ + + /* Step 1: Save CSRs */ + vcsr_read(dst->vcsr, vcsr); + vcsr_read(dst->vstart, vstart); + +#undef vcsr_read + + ulong vlenb = vector_vlenb(); + uint8_t *base = dst->vregs; + + /* Step 3: Save vector registers */ +#define SAVE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vs8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + SAVE_VREG(0); + SAVE_VREG(8); + SAVE_VREG(16); + SAVE_VREG(24); + +#undef SAVE_VREG +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ + if (!src) + return; + + const uint8_t *base = src->vregs; + ulong vlenb = vector_vlenb(); + + /* Step 2: Restore vector registers */ +#define RESTORE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vl8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + RESTORE_VREG(0); + RESTORE_VREG(8); + RESTORE_VREG(16); + RESTORE_VREG(24); +#undef RESTORE_VREG + + /* Step 3: Restore CSR's last */ +#define vcsr_write(csr, val) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " csrw " #csr ", %0\n\t" \ + " .option pop\n\t" \ + : \ + : "r"(val) \ + : "memory"); \ + }) \ + + /* Restore CSRs first */ + vcsr_write(vcsr, src->vcsr); + vcsr_write(vstart, src->vstart); +#undef vcsr_write +} + +#else + +void sbi_vector_save(struct sbi_vector_context *dst) +{ +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ +} + +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ -- 2.43.0 From dave.patel at riscstar.com Wed May 13 00:14:17 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Wed, 13 May 2026 08:14:17 +0100 Subject: [PATCH v4 2/3] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260513071418.50957-1-dave.patel@riscstar.com> References: <20260513071418.50957-1-dave.patel@riscstar.com> Message-ID: <20260513071418.50957-3-dave.patel@riscstar.com> From: Dave Patel Add support for saving and restoring RISC-V floating-point (F/D) extension state in OpenSBI. This introduces a floating-point context structure and helper routines to perform full context save and restore. The floating-point context includes storage for all 32 FPi registers (f0?f31) along with the fcsr control and status register. The register state is saved and restored using double-precision load/store instructions (fsd/fld), and single-precision load/store instructions (fsw/flw) on an RV64 system with F and D-extension support. The implementation follows an eager context switching model where the entire FP state is saved and restored on every context switch. This avoids the need for trap-based lazy management and keeps the design simple and deterministic. Signed-off-by: Dave Patel " --- include/sbi/sbi_fp.h | 26 ++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 lib/sbi/sbi_fp.c diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h new file mode 100644 index 00000000..d4eb7694 --- /dev/null +++ b/include/sbi/sbi_fp.h @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ +#ifndef __SBI_FP_H__ +#define __SBI_FP_H__ + +#include + +struct sbi_fp_context { +#if __riscv_d + uint64_t f[32]; +#else + uint32_t f[32]; +#endif + uint32_t fcsr; +}; + +void sbi_fp_save(struct sbi_fp_context *dst); +void sbi_fp_restore(const struct sbi_fp_context *src); + +#endif //__SBI_VECTOR_H__ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index ddb2e7ac..d8182383 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -110,3 +110,4 @@ libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o libsbi-objs-y += sbi_vector.o +libsbi-objs-y += sbi_fp.o diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c new file mode 100644 index 00000000..887bca4d --- /dev/null +++ b/lib/sbi/sbi_fp.c @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include + +#if defined(__riscv_f) || defined(__riscv_d) + +void sbi_fp_save(struct sbi_fp_context *dst) +{ + if (!dst) + return; + +#if defined(__riscv_d) + asm volatile( + "fsd f0, 0(%0)\n" + "fsd f1, 8(%0)\n" + "fsd f2, 16(%0)\n" + "fsd f3, 24(%0)\n" + "fsd f4, 32(%0)\n" + "fsd f5, 40(%0)\n" + "fsd f6, 48(%0)\n" + "fsd f7, 56(%0)\n" + "fsd f8, 64(%0)\n" + "fsd f9, 72(%0)\n" + "fsd f10, 80(%0)\n" + "fsd f11, 88(%0)\n" + "fsd f12, 96(%0)\n" + "fsd f13, 104(%0)\n" + "fsd f14, 112(%0)\n" + "fsd f15, 120(%0)\n" + "fsd f16, 128(%0)\n" + "fsd f17, 136(%0)\n" + "fsd f18, 144(%0)\n" + "fsd f19, 152(%0)\n" + "fsd f20, 160(%0)\n" + "fsd f21, 168(%0)\n" + "fsd f22, 176(%0)\n" + "fsd f23, 184(%0)\n" + "fsd f24, 192(%0)\n" + "fsd f25, 200(%0)\n" + "fsd f26, 208(%0)\n" + "fsd f27, 216(%0)\n" + "fsd f28, 224(%0)\n" + "fsd f29, 232(%0)\n" + "fsd f30, 240(%0)\n" + "fsd f31, 248(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#else + asm volatile( + "fsw f0, 0(%0)\n" + "fsw f1, 4(%0)\n" + "fsw f2, 8(%0)\n" + "fsw f3, 12(%0)\n" + "fsw f4, 16(%0)\n" + "fsw f5, 20(%0)\n" + "fsw f6, 24(%0)\n" + "fsw f7, 28(%0)\n" + "fsw f8, 32(%0)\n" + "fsw f9, 36(%0)\n" + "fsw f10, 40(%0)\n" + "fsw f11, 44(%0)\n" + "fsw f12, 48(%0)\n" + "fsw f13, 52(%0)\n" + "fsw f14, 56(%0)\n" + "fsw f15, 60(%0)\n" + "fsw f16, 64(%0)\n" + "fsw f17, 68(%0)\n" + "fsw f18, 72(%0)\n" + "fsw f19, 76(%0)\n" + "fsw f20, 80(%0)\n" + "fsw f21, 84(%0)\n" + "fsw f22, 88(%0)\n" + "fsw f23, 92(%0)\n" + "fsw f24, 96(%0)\n" + "fsw f25, 100(%0)\n" + "fsw f26, 104(%0)\n" + "fsw f27, 108(%0)\n" + "fsw f28, 112(%0)\n" + "fsw f29, 116(%0)\n" + "fsw f30, 120(%0)\n" + "fsw f31, 124(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#endif //__riscv_d + + dst->fcsr = csr_read(CSR_FCSR); +} + +void sbi_fp_restore(const struct sbi_fp_context *src) +{ + if (!src) + return; + +#if defined(__riscv_d) + asm volatile( + "fld f0, 0(%0)\n" + "fld f1, 8(%0)\n" + "fld f2, 16(%0)\n" + "fld f3, 24(%0)\n" + "fld f4, 32(%0)\n" + "fld f5, 40(%0)\n" + "fld f6, 48(%0)\n" + "fld f7, 56(%0)\n" + "fld f8, 64(%0)\n" + "fld f9, 72(%0)\n" + "fld f10, 80(%0)\n" + "fld f11, 88(%0)\n" + "fld f12, 96(%0)\n" + "fld f13, 104(%0)\n" + "fld f14, 112(%0)\n" + "fld f15, 120(%0)\n" + "fld f16, 128(%0)\n" + "fld f17, 136(%0)\n" + "fld f18, 144(%0)\n" + "fld f19, 152(%0)\n" + "fld f20, 160(%0)\n" + "fld f21, 168(%0)\n" + "fld f22, 176(%0)\n" + "fld f23, 184(%0)\n" + "fld f24, 192(%0)\n" + "fld f25, 200(%0)\n" + "fld f26, 208(%0)\n" + "fld f27, 216(%0)\n" + "fld f28, 224(%0)\n" + "fld f29, 232(%0)\n" + "fld f30, 240(%0)\n" + "fld f31, 248(%0)\n" + : + : "r"(src->f) + : "memory" + ); +#else + + asm volatile( + "flw f0, 0(%0)\n" + "flw f1, 4(%0)\n" + "flw f2, 8(%0)\n" + "flw f3, 12(%0)\n" + "flw f4, 16(%0)\n" + "flw f5, 20(%0)\n" + "flw f6, 24(%0)\n" + "flw f7, 28(%0)\n" + "flw f8, 32(%0)\n" + "flw f9, 36(%0)\n" + "flw f10, 40(%0)\n" + "flw f11, 44(%0)\n" + "flw f12, 48(%0)\n" + "flw f13, 52(%0)\n" + "flw f14, 56(%0)\n" + "flw f15, 60(%0)\n" + "flw f16, 64(%0)\n" + "flw f17, 68(%0)\n" + "flw f18, 72(%0)\n" + "flw f19, 76(%0)\n" + "flw f20, 80(%0)\n" + "flw f21, 84(%0)\n" + "flw f22, 88(%0)\n" + "flw f23, 92(%0)\n" + "flw f24, 96(%0)\n" + "flw f25, 100(%0)\n" + "flw f26, 104(%0)\n" + "flw f27, 108(%0)\n" + "flw f28, 112(%0)\n" + "flw f29, 116(%0)\n" + "flw f30, 120(%0)\n" + "flw f31, 124(%0)\n" + : + : "r"(src->f) + : "memory" + ); + +#endif + + csr_write(CSR_FCSR, src->fcsr); +} +#else +void sbi_fp_save(struct sbi_fp_context *dst) {} +void sbi_fp_restore(const struct sbi_fp_context *src) {} +#endif // FP present -- 2.43.0 From dave.patel at riscstar.com Wed May 13 00:14:18 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Wed, 13 May 2026 08:14:18 +0100 Subject: [PATCH v4 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260513071418.50957-1-dave.patel@riscstar.com> References: <20260513071418.50957-1-dave.patel@riscstar.com> Message-ID: <20260513071418.50957-4-dave.patel@riscstar.com> From: Dave Patel This patch adds proper support for per-domain floating-point (FP) and vector (V) contexts in the domain context switch logic. Each domain now maintains its own FP and vector state, which is saved and restored during domain switches. Changes include: - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' to allocate and free per-domain FP and vector context. - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. - Updated `switch_to_next_domain_context()` to save/restore FP and vector contexts safely: - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. - Added runtime checks for FP and vector extensions where needed. This improves support for multi-domain systems with FP and Vector extensions, and prevents corruption of FP/Vector state during domain switches. Signed-off-by: Dave Patel --- include/sbi/sbi_domain.h | 2 ++ lib/sbi/sbi_domain_context.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h index 882b62c2..05a5d86c 100644 --- a/include/sbi/sbi_domain.h +++ b/include/sbi/sbi_domain.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include struct sbi_scratch; diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index 158f4990..5e180698 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -18,6 +18,9 @@ #include #include #include +#include +#include + /** Context representation for a hart within a domain */ struct hart_context { @@ -55,6 +58,11 @@ struct hart_context { struct hart_context *prev_ctx; /** Is context initialized and runnable */ bool initialized; + + /** float context state */ + struct sbi_fp_context fp_ctx; + /** vector context state */ + struct sbi_vector_context *vec_ctx; }; static struct sbi_domain_data dcpriv; @@ -143,6 +151,15 @@ static int switch_to_next_domain_context(struct hart_context *ctx, if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); + /* Make sure FS and VS is on before context switch */ + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); + + /* Eager context switch F and V */ + sbi_fp_save(&ctx->fp_ctx); + sbi_fp_restore(&dom_ctx->fp_ctx); + sbi_vector_save(ctx->vec_ctx); + sbi_vector_restore(dom_ctx->vec_ctx); + /* Save current trap state and restore target domain's trap state */ trap_ctx = sbi_trap_get_context(scratch); sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); @@ -170,6 +187,10 @@ static int hart_context_init(u32 hartindex) { struct hart_context *ctx; struct sbi_domain *dom; + unsigned long vlenb = vector_vlenb(); + + /* Calculate size: base struct + 32 registers of vlenb size */ + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); sbi_domain_for_each(dom) { if (!sbi_hartmask_test_hartindex(hartindex, @@ -180,6 +201,13 @@ static int hart_context_init(u32 hartindex) if (!ctx) return SBI_ENOMEM; + /* Allocate the vector context pointer */ + ctx->vec_ctx = sbi_zalloc(vec_size); + if (!ctx->vec_ctx) { + sbi_free(ctx); + return SBI_ENOMEM; + } + /* Bind context and domain */ ctx->dom = dom; hart_context_set(dom, hartindex, ctx); -- 2.43.0 From anup at brainfault.org Wed May 13 00:42:52 2026 From: anup at brainfault.org (Anup Patel) Date: Wed, 13 May 2026 13:12:52 +0530 Subject: [PATCH v4 0/3] Add eager FP and RISC-V vector context switching support In-Reply-To: <20260513071418.50957-1-dave.patel@riscstar.com> References: <20260513071418.50957-1-dave.patel@riscstar.com> Message-ID: On Wed, May 13, 2026 at 12:44?PM wrote: > > Hi Anup, > Thank you for taking out time and reviewing the patches. > Following your review comments, I have made following changes. Also I have > tested the changes. > > The changes include: > - Dropping CONFIG_SBI_MAX_VLENB and sbi_vector_domain_init() > - struct sbi_vector_context vec_ctx is now struct * in > 'struct hart_context' > - changing READ_CSR and WRITE_CSR to vcsr_read and vcsr_write I think you misunderstood my previous comment. We already have csr_read() and csr_write() defined in riscv_asm.h so use these macros instead of introducing vcsr_read() and vcsr_write(). > > I have covered all your comments, please can you have a look and let me know. > Regards, Anup From anup at brainfault.org Wed May 13 00:54:15 2026 From: anup at brainfault.org (Anup Patel) Date: Wed, 13 May 2026 13:24:15 +0530 Subject: [PATCH v4 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260513071418.50957-4-dave.patel@riscstar.com> References: <20260513071418.50957-1-dave.patel@riscstar.com> <20260513071418.50957-4-dave.patel@riscstar.com> Message-ID: On Wed, May 13, 2026 at 12:44?PM wrote: > > From: Dave Patel > > This patch adds proper support for per-domain floating-point (FP) and > vector (V) contexts in the domain context switch logic. Each domain > now maintains its own FP and vector state, which is saved and restored > during domain switches. > > Changes include: > > - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. > - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' > to allocate and free per-domain FP and vector context. > - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. > - Updated `switch_to_next_domain_context()` to save/restore FP and vector > contexts safely: > - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. > - Added runtime checks for FP and vector extensions where needed. > > This improves support for multi-domain systems with FP and Vector > extensions, and prevents corruption of FP/Vector state during domain > switches. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_domain.h | 2 ++ > lib/sbi/sbi_domain_context.c | 28 ++++++++++++++++++++++++++++ > 2 files changed, 30 insertions(+) > > diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h > index 882b62c2..05a5d86c 100644 > --- a/include/sbi/sbi_domain.h > +++ b/include/sbi/sbi_domain.h > @@ -16,6 +16,8 @@ > #include > #include > #include > +#include > +#include > > struct sbi_scratch; > > diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c > index 158f4990..5e180698 100644 > --- a/lib/sbi/sbi_domain_context.c > +++ b/lib/sbi/sbi_domain_context.c > @@ -18,6 +18,9 @@ > #include > #include > #include > +#include > +#include > + > > /** Context representation for a hart within a domain */ > struct hart_context { > @@ -55,6 +58,11 @@ struct hart_context { > struct hart_context *prev_ctx; > /** Is context initialized and runnable */ > bool initialized; > + > + /** float context state */ > + struct sbi_fp_context fp_ctx; > + /** vector context state */ > + struct sbi_vector_context *vec_ctx; > }; > > static struct sbi_domain_data dcpriv; > @@ -143,6 +151,15 @@ static int switch_to_next_domain_context(struct hart_context *ctx, > if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) > ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); > > + /* Make sure FS and VS is on before context switch */ > + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); > + > + /* Eager context switch F and V */ > + sbi_fp_save(&ctx->fp_ctx); > + sbi_fp_restore(&dom_ctx->fp_ctx); > + sbi_vector_save(ctx->vec_ctx); > + sbi_vector_restore(dom_ctx->vec_ctx); Unconditional save/restore of FP and Vector is not going to fly because the generic platform firmwares are expected to work on multiple platforms (both with or without vector). To address this, do FP save/restore only if underlying hart has F/D hart extension. Similarly, do Vector save/restore only if underlying hart has Vector extension. As a separate patch, introduce SBI_HART_EXT_F, SBI_HART_EXT_D, and SBI_HART_EXT_V in enum sbi_hart_extensions and sbi_hart_ext[]. Once, this is done you can check for SBI_HART_EXT_F, SBI_HART_EXT_D, and SBI_HART_EXT_V using sbi_hart_has_extension(). > + > /* Save current trap state and restore target domain's trap state */ > trap_ctx = sbi_trap_get_context(scratch); > sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); > @@ -170,6 +187,10 @@ static int hart_context_init(u32 hartindex) > { > struct hart_context *ctx; > struct sbi_domain *dom; > + unsigned long vlenb = vector_vlenb(); > + > + /* Calculate size: base struct + 32 registers of vlenb size */ > + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); > > sbi_domain_for_each(dom) { > if (!sbi_hartmask_test_hartindex(hartindex, > @@ -180,6 +201,13 @@ static int hart_context_init(u32 hartindex) > if (!ctx) > return SBI_ENOMEM; > > + /* Allocate the vector context pointer */ > + ctx->vec_ctx = sbi_zalloc(vec_size); > + if (!ctx->vec_ctx) { > + sbi_free(ctx); > + return SBI_ENOMEM; > + } > + > /* Bind context and domain */ > ctx->dom = dom; > hart_context_set(dom, hartindex, ctx); > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Wed May 13 00:57:15 2026 From: anup at brainfault.org (Anup Patel) Date: Wed, 13 May 2026 13:27:15 +0530 Subject: [PATCH v4 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260513071418.50957-4-dave.patel@riscstar.com> References: <20260513071418.50957-1-dave.patel@riscstar.com> <20260513071418.50957-4-dave.patel@riscstar.com> Message-ID: On Wed, May 13, 2026 at 12:44?PM wrote: > > From: Dave Patel > > This patch adds proper support for per-domain floating-point (FP) and > vector (V) contexts in the domain context switch logic. Each domain > now maintains its own FP and vector state, which is saved and restored > during domain switches. > > Changes include: > > - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. > - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' > to allocate and free per-domain FP and vector context. > - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. > - Updated `switch_to_next_domain_context()` to save/restore FP and vector > contexts safely: > - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. > - Added runtime checks for FP and vector extensions where needed. > > This improves support for multi-domain systems with FP and Vector > extensions, and prevents corruption of FP/Vector state during domain > switches. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_domain.h | 2 ++ > lib/sbi/sbi_domain_context.c | 28 ++++++++++++++++++++++++++++ > 2 files changed, 30 insertions(+) > > diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h > index 882b62c2..05a5d86c 100644 > --- a/include/sbi/sbi_domain.h > +++ b/include/sbi/sbi_domain.h > @@ -16,6 +16,8 @@ > #include > #include > #include > +#include > +#include > > struct sbi_scratch; > > diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c > index 158f4990..5e180698 100644 > --- a/lib/sbi/sbi_domain_context.c > +++ b/lib/sbi/sbi_domain_context.c > @@ -18,6 +18,9 @@ > #include > #include > #include > +#include > +#include > + > > /** Context representation for a hart within a domain */ > struct hart_context { > @@ -55,6 +58,11 @@ struct hart_context { > struct hart_context *prev_ctx; > /** Is context initialized and runnable */ > bool initialized; > + > + /** float context state */ > + struct sbi_fp_context fp_ctx; > + /** vector context state */ > + struct sbi_vector_context *vec_ctx; > }; > > static struct sbi_domain_data dcpriv; > @@ -143,6 +151,15 @@ static int switch_to_next_domain_context(struct hart_context *ctx, > if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) > ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); > > + /* Make sure FS and VS is on before context switch */ > + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); > + > + /* Eager context switch F and V */ > + sbi_fp_save(&ctx->fp_ctx); > + sbi_fp_restore(&dom_ctx->fp_ctx); > + sbi_vector_save(ctx->vec_ctx); > + sbi_vector_restore(dom_ctx->vec_ctx); > + > /* Save current trap state and restore target domain's trap state */ > trap_ctx = sbi_trap_get_context(scratch); > sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); > @@ -170,6 +187,10 @@ static int hart_context_init(u32 hartindex) > { > struct hart_context *ctx; > struct sbi_domain *dom; > + unsigned long vlenb = vector_vlenb(); > + > + /* Calculate size: base struct + 32 registers of vlenb size */ > + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); Call vector_vlenb() and allocate vec_ctx only when underlying HART has SBI_HART_EXT_V. > > sbi_domain_for_each(dom) { > if (!sbi_hartmask_test_hartindex(hartindex, > @@ -180,6 +201,13 @@ static int hart_context_init(u32 hartindex) > if (!ctx) > return SBI_ENOMEM; > > + /* Allocate the vector context pointer */ > + ctx->vec_ctx = sbi_zalloc(vec_size); > + if (!ctx->vec_ctx) { > + sbi_free(ctx); > + return SBI_ENOMEM; > + } > + > /* Bind context and domain */ > ctx->dom = dom; > hart_context_set(dom, hartindex, ctx); > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From dave.patel at riscstar.com Wed May 13 00:58:27 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Wed, 13 May 2026 08:58:27 +0100 Subject: [PATCH v4 0/3] Add eager FP and RISC-V vector context switching support In-Reply-To: References: <20260513071418.50957-1-dave.patel@riscstar.com> Message-ID: <478b4c36-4c98-4b21-bb08-24deeed077da@riscstar.com> On 5/13/26 08:42, Anup Patel wrote: > On Wed, May 13, 2026 at 12:44?PM wrote: >> >> Hi Anup, >> Thank you for taking out time and reviewing the patches. >> Following your review comments, I have made following changes. Also I have >> tested the changes. >> >> The changes include: >> - Dropping CONFIG_SBI_MAX_VLENB and sbi_vector_domain_init() >> - struct sbi_vector_context vec_ctx is now struct * in >> 'struct hart_context' >> - changing READ_CSR and WRITE_CSR to vcsr_read and vcsr_write > > I think you misunderstood my previous comment. We already > have csr_read() and csr_write() defined in riscv_asm.h so use > these macros instead of introducing vcsr_read() and vcsr_write(). > >> >> I have covered all your comments, please can you have a look and let me know. >> > > Regards, > Anup Hi Anup, Thanks, There is subtle difference between csr_read/write and vcsr_read/write in the context that this explicitly enables and isolates the RISC-V Vector (V) extension during compilation. This allows the program to read vector-specific Control and Status Registers (such as vlenb, vl, or vtype) without requiring the entire project to be compiled with global vector support. Vector Architecture Override: The directive .option arch, +v dynamically tells the assembler that the Vector extension is supported for this specific instruction block. A standard csr_read lacks this, causing compilation to fail if vector registers are accessed without global -march vector flags. Context Protection: The .option push and .option pop directives safeguard the build environment. They save the original compiler options, temporarily inject the vector extension, and immediately restore the original settings so subsequent code is unaffected. Memory Serialization: The inclusion of the "memory" clobber acts as a optimization barrier. It prevents the compiler from reordering memory reads or writes around the CSR operation, which is critical for vector state updates. So should I update it to normal csr_read/write instead? Thanks Dave From dave.patel at riscstar.com Wed May 13 01:02:46 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Wed, 13 May 2026 09:02:46 +0100 Subject: [PATCH v4 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: References: <20260513071418.50957-1-dave.patel@riscstar.com> <20260513071418.50957-4-dave.patel@riscstar.com> Message-ID: On 5/13/26 08:57, Anup Patel wrote: > On Wed, May 13, 2026 at 12:44?PM wrote: >> >> From: Dave Patel >> >> This patch adds proper support for per-domain floating-point (FP) and >> vector (V) contexts in the domain context switch logic. Each domain >> now maintains its own FP and vector state, which is saved and restored >> during domain switches. >> >> Changes include: >> >> - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. >> - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' >> to allocate and free per-domain FP and vector context. >> - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. >> - Updated `switch_to_next_domain_context()` to save/restore FP and vector >> contexts safely: >> - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. >> - Added runtime checks for FP and vector extensions where needed. >> >> This improves support for multi-domain systems with FP and Vector >> extensions, and prevents corruption of FP/Vector state during domain >> switches. >> >> Signed-off-by: Dave Patel >> --- >> include/sbi/sbi_domain.h | 2 ++ >> lib/sbi/sbi_domain_context.c | 28 ++++++++++++++++++++++++++++ >> 2 files changed, 30 insertions(+) >> >> diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h >> index 882b62c2..05a5d86c 100644 >> --- a/include/sbi/sbi_domain.h >> +++ b/include/sbi/sbi_domain.h >> @@ -16,6 +16,8 @@ >> #include >> #include >> #include >> +#include >> +#include >> >> struct sbi_scratch; >> >> diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c >> index 158f4990..5e180698 100644 >> --- a/lib/sbi/sbi_domain_context.c >> +++ b/lib/sbi/sbi_domain_context.c >> @@ -18,6 +18,9 @@ >> #include >> #include >> #include >> +#include >> +#include >> + >> >> /** Context representation for a hart within a domain */ >> struct hart_context { >> @@ -55,6 +58,11 @@ struct hart_context { >> struct hart_context *prev_ctx; >> /** Is context initialized and runnable */ >> bool initialized; >> + >> + /** float context state */ >> + struct sbi_fp_context fp_ctx; >> + /** vector context state */ >> + struct sbi_vector_context *vec_ctx; >> }; >> >> static struct sbi_domain_data dcpriv; >> @@ -143,6 +151,15 @@ static int switch_to_next_domain_context(struct hart_context *ctx, >> if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) >> ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); >> >> + /* Make sure FS and VS is on before context switch */ >> + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); >> + >> + /* Eager context switch F and V */ >> + sbi_fp_save(&ctx->fp_ctx); >> + sbi_fp_restore(&dom_ctx->fp_ctx); >> + sbi_vector_save(ctx->vec_ctx); >> + sbi_vector_restore(dom_ctx->vec_ctx); >> + >> /* Save current trap state and restore target domain's trap state */ >> trap_ctx = sbi_trap_get_context(scratch); >> sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); >> @@ -170,6 +187,10 @@ static int hart_context_init(u32 hartindex) >> { >> struct hart_context *ctx; >> struct sbi_domain *dom; >> + unsigned long vlenb = vector_vlenb(); >> + >> + /* Calculate size: base struct + 32 registers of vlenb size */ >> + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); > > Call vector_vlenb() and allocate vec_ctx only when > underlying HART has SBI_HART_EXT_V. ok shall add this change. > >> >> sbi_domain_for_each(dom) { >> if (!sbi_hartmask_test_hartindex(hartindex, >> @@ -180,6 +201,13 @@ static int hart_context_init(u32 hartindex) >> if (!ctx) >> return SBI_ENOMEM; >> >> + /* Allocate the vector context pointer */ >> + ctx->vec_ctx = sbi_zalloc(vec_size); >> + if (!ctx->vec_ctx) { >> + sbi_free(ctx); >> + return SBI_ENOMEM; >> + } >> + >> /* Bind context and domain */ >> ctx->dom = dom; >> hart_context_set(dom, hartindex, ctx); >> -- >> 2.43.0 >> >> >> -- >> opensbi mailing list >> opensbi at lists.infradead.org >> http://lists.infradead.org/mailman/listinfo/opensbi > > Regards, > Anup Thanks Anup, will make this change From anup at brainfault.org Wed May 13 01:04:48 2026 From: anup at brainfault.org (Anup Patel) Date: Wed, 13 May 2026 13:34:48 +0530 Subject: [PATCH v4 0/3] Add eager FP and RISC-V vector context switching support In-Reply-To: <478b4c36-4c98-4b21-bb08-24deeed077da@riscstar.com> References: <20260513071418.50957-1-dave.patel@riscstar.com> <478b4c36-4c98-4b21-bb08-24deeed077da@riscstar.com> Message-ID: On Wed, May 13, 2026 at 1:28?PM Dave Patel wrote: > > On 5/13/26 08:42, Anup Patel wrote: > > On Wed, May 13, 2026 at 12:44?PM wrote: > >> > >> Hi Anup, > >> Thank you for taking out time and reviewing the patches. > >> Following your review comments, I have made following changes. Also I have > >> tested the changes. > >> > >> The changes include: > >> - Dropping CONFIG_SBI_MAX_VLENB and sbi_vector_domain_init() > >> - struct sbi_vector_context vec_ctx is now struct * in > >> 'struct hart_context' > >> - changing READ_CSR and WRITE_CSR to vcsr_read and vcsr_write > > > > I think you misunderstood my previous comment. We already > > have csr_read() and csr_write() defined in riscv_asm.h so use > > these macros instead of introducing vcsr_read() and vcsr_write(). > > > >> > >> I have covered all your comments, please can you have a look and let me know. > >> > > > > Regards, > > Anup > > Hi Anup, > Thanks, > > There is subtle difference between csr_read/write and vcsr_read/write in > the context that this explicitly enables and isolates the RISC-V Vector > (V) extension during compilation. This allows the program to read > vector-specific Control and Status Registers (such as vlenb, vl, or > vtype) without requiring the entire project to be compiled with global > vector support. I totally disagree. Your vcsr_read() is exactly same as csr_read() except that it relies on compiler nemomics instead of CSR number. > > Vector Architecture Override: The directive .option arch, +v dynamically > tells the assembler that the Vector extension is supported for this > specific instruction block. A standard csr_read lacks this, causing > compilation to fail if vector registers are accessed without global > -march vector flags. > Context Protection: The .option push and .option pop directives > safeguard the build environment. They save the original compiler > options, temporarily inject the vector extension, and immediately > restore the original settings so subsequent code is unaffected. > Memory Serialization: The inclusion of the "memory" clobber acts as a > optimization barrier. It prevents the compiler from reordering memory > reads or writes around the CSR operation, which is critical for vector > state updates. > > So should I update it to normal csr_read/write instead? Older toolchains dont understand vector nemonics that is why I insist on using common csr_read/write(). Regards, Anup From dave.patel at riscstar.com Wed May 13 01:09:40 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Wed, 13 May 2026 09:09:40 +0100 Subject: [PATCH v4 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: References: <20260513071418.50957-1-dave.patel@riscstar.com> <20260513071418.50957-4-dave.patel@riscstar.com> Message-ID: On 5/13/26 08:54, Anup Patel wrote: > On Wed, May 13, 2026 at 12:44?PM wrote: >> >> From: Dave Patel >> >> This patch adds proper support for per-domain floating-point (FP) and >> vector (V) contexts in the domain context switch logic. Each domain >> now maintains its own FP and vector state, which is saved and restored >> during domain switches. >> >> Changes include: >> >> - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. >> - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' >> to allocate and free per-domain FP and vector context. >> - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. >> - Updated `switch_to_next_domain_context()` to save/restore FP and vector >> contexts safely: >> - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. >> - Added runtime checks for FP and vector extensions where needed. >> >> This improves support for multi-domain systems with FP and Vector >> extensions, and prevents corruption of FP/Vector state during domain >> switches. >> >> Signed-off-by: Dave Patel >> --- >> include/sbi/sbi_domain.h | 2 ++ >> lib/sbi/sbi_domain_context.c | 28 ++++++++++++++++++++++++++++ >> 2 files changed, 30 insertions(+) >> >> diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h >> index 882b62c2..05a5d86c 100644 >> --- a/include/sbi/sbi_domain.h >> +++ b/include/sbi/sbi_domain.h >> @@ -16,6 +16,8 @@ >> #include >> #include >> #include >> +#include >> +#include >> >> struct sbi_scratch; >> >> diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c >> index 158f4990..5e180698 100644 >> --- a/lib/sbi/sbi_domain_context.c >> +++ b/lib/sbi/sbi_domain_context.c >> @@ -18,6 +18,9 @@ >> #include >> #include >> #include >> +#include >> +#include >> + >> >> /** Context representation for a hart within a domain */ >> struct hart_context { >> @@ -55,6 +58,11 @@ struct hart_context { >> struct hart_context *prev_ctx; >> /** Is context initialized and runnable */ >> bool initialized; >> + >> + /** float context state */ >> + struct sbi_fp_context fp_ctx; >> + /** vector context state */ >> + struct sbi_vector_context *vec_ctx; >> }; >> >> static struct sbi_domain_data dcpriv; >> @@ -143,6 +151,15 @@ static int switch_to_next_domain_context(struct hart_context *ctx, >> if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) >> ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); >> >> + /* Make sure FS and VS is on before context switch */ >> + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); >> + >> + /* Eager context switch F and V */ >> + sbi_fp_save(&ctx->fp_ctx); >> + sbi_fp_restore(&dom_ctx->fp_ctx); >> + sbi_vector_save(ctx->vec_ctx); >> + sbi_vector_restore(dom_ctx->vec_ctx); > > Unconditional save/restore of FP and Vector is not going to fly > because the generic platform firmwares are expected to work > on multiple platforms (both with or without vector). > > To address this, do FP save/restore only if underlying hart > has F/D hart extension. Similarly, do Vector save/restore > only if underlying hart has Vector extension. > > As a separate patch, introduce SBI_HART_EXT_F, > SBI_HART_EXT_D, and SBI_HART_EXT_V in > enum sbi_hart_extensions and sbi_hart_ext[]. Once, > this is done you can check for SBI_HART_EXT_F, > SBI_HART_EXT_D, and SBI_HART_EXT_V using > sbi_hart_has_extension(). > Ok will extend the enum sbi_hart_extensions and shall do check before save/restore of the context >> + >> /* Save current trap state and restore target domain's trap state */ >> trap_ctx = sbi_trap_get_context(scratch); >> sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); >> @@ -170,6 +187,10 @@ static int hart_context_init(u32 hartindex) >> { >> struct hart_context *ctx; >> struct sbi_domain *dom; >> + unsigned long vlenb = vector_vlenb(); >> + >> + /* Calculate size: base struct + 32 registers of vlenb size */ >> + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); >> >> sbi_domain_for_each(dom) { >> if (!sbi_hartmask_test_hartindex(hartindex, >> @@ -180,6 +201,13 @@ static int hart_context_init(u32 hartindex) >> if (!ctx) >> return SBI_ENOMEM; >> >> + /* Allocate the vector context pointer */ >> + ctx->vec_ctx = sbi_zalloc(vec_size); >> + if (!ctx->vec_ctx) { >> + sbi_free(ctx); >> + return SBI_ENOMEM; >> + } >> + >> /* Bind context and domain */ >> ctx->dom = dom; >> hart_context_set(dom, hartindex, ctx); >> -- >> 2.43.0 >> >> >> -- >> opensbi mailing list >> opensbi at lists.infradead.org >> http://lists.infradead.org/mailman/listinfo/opensbi > > Regards, > Anup Hi Anup, Thanks, Ok understood I will extend the enum sbi_hart_extensions and will add another patch on top of this 3rd patch to add and check the extension. Thanks Dave From dave.patel at riscstar.com Wed May 13 01:12:07 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Wed, 13 May 2026 09:12:07 +0100 Subject: [PATCH v4 0/3] Add eager FP and RISC-V vector context switching support In-Reply-To: References: <20260513071418.50957-1-dave.patel@riscstar.com> <478b4c36-4c98-4b21-bb08-24deeed077da@riscstar.com> Message-ID: <0654a207-8bb0-436f-a29c-be8d09fc1862@riscstar.com> On 5/13/26 09:04, Anup Patel wrote: > On Wed, May 13, 2026 at 1:28?PM Dave Patel wrote: >> >> On 5/13/26 08:42, Anup Patel wrote: >>> On Wed, May 13, 2026 at 12:44?PM wrote: >>>> >>>> Hi Anup, >>>> Thank you for taking out time and reviewing the patches. >>>> Following your review comments, I have made following changes. Also I have >>>> tested the changes. >>>> >>>> The changes include: >>>> - Dropping CONFIG_SBI_MAX_VLENB and sbi_vector_domain_init() >>>> - struct sbi_vector_context vec_ctx is now struct * in >>>> 'struct hart_context' >>>> - changing READ_CSR and WRITE_CSR to vcsr_read and vcsr_write >>> >>> I think you misunderstood my previous comment. We already >>> have csr_read() and csr_write() defined in riscv_asm.h so use >>> these macros instead of introducing vcsr_read() and vcsr_write(). >>> >>>> >>>> I have covered all your comments, please can you have a look and let me know. >>>> >>> >>> Regards, >>> Anup >> >> Hi Anup, >> Thanks, >> >> There is subtle difference between csr_read/write and vcsr_read/write in >> the context that this explicitly enables and isolates the RISC-V Vector >> (V) extension during compilation. This allows the program to read >> vector-specific Control and Status Registers (such as vlenb, vl, or >> vtype) without requiring the entire project to be compiled with global >> vector support. > > I totally disagree. Your vcsr_read() is exactly same as csr_read() > except that it relies on compiler nemomics instead of CSR number. > >> >> Vector Architecture Override: The directive .option arch, +v dynamically >> tells the assembler that the Vector extension is supported for this >> specific instruction block. A standard csr_read lacks this, causing >> compilation to fail if vector registers are accessed without global >> -march vector flags. >> Context Protection: The .option push and .option pop directives >> safeguard the build environment. They save the original compiler >> options, temporarily inject the vector extension, and immediately >> restore the original settings so subsequent code is unaffected. >> Memory Serialization: The inclusion of the "memory" clobber acts as a >> optimization barrier. It prevents the compiler from reordering memory >> reads or writes around the CSR operation, which is critical for vector >> state updates. >> >> So should I update it to normal csr_read/write instead? > > Older toolchains dont understand vector nemonics that > is why I insist on using common csr_read/write(). > > Regards, > Anup Thanks Anup, understood and shall amend it as per your suggestion above. Thanks Dave From samuel.holland at sifive.com Wed May 13 15:44:12 2026 From: samuel.holland at sifive.com (Samuel Holland) Date: Wed, 13 May 2026 15:44:12 -0700 Subject: [PATCH] lib: utils/reset: Remove unused match data Message-ID: <20260513224414.1078791-1-samuel.holland@sifive.com> Some drivers inherited FDT match data from the GPIO/syscon reset drivers, but do not use it for anything. Remove it to avoid confusion. Signed-off-by: Samuel Holland --- lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c | 2 +- lib/utils/reset/fdt_reset_spacemit_p1.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c b/lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c index 94ac4162..bb180268 100644 --- a/lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c +++ b/lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c @@ -104,7 +104,7 @@ static int sg2042_mcu_reset_init(const void *fdt, int nodeoff, } static const struct fdt_match sg2042_mcu_reset_match[] = { - { .compatible = "sophgo,sg2042-hwmon-mcu", .data = (void *)true}, + { .compatible = "sophgo,sg2042-hwmon-mcu" }, { }, }; diff --git a/lib/utils/reset/fdt_reset_spacemit_p1.c b/lib/utils/reset/fdt_reset_spacemit_p1.c index 5312e741..d153f68b 100644 --- a/lib/utils/reset/fdt_reset_spacemit_p1.c +++ b/lib/utils/reset/fdt_reset_spacemit_p1.c @@ -102,7 +102,7 @@ static int p1_reset_init(const void *fdt, int nodeoff, } static const struct fdt_match p1_reset_match[] = { - { .compatible = "spacemit,p1", .data = (void *)true }, + { .compatible = "spacemit,p1" }, { }, }; -- 2.52.0 base-commit: 79e63bc8348df12835b5c264144e50d45abe72c8 branch: up/cleanup-reset From evvoevod at tenstorrent.com Thu May 14 13:27:03 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Thu, 14 May 2026 20:27:03 +0000 Subject: [PATCH v3 6/6] lib: sbi: hart: Detect and enable Smrnmi before trap-based feature detection In-Reply-To: References: <88b1470e1e3348d454b4b995a11a85c01914f7af.1778176768.git.evvoevod@tenstorrent.com> Message-ID: On Tue May 12, 2026 at 3:40 PM UTC, Anup Patel wrote: > On Tue, May 12, 2026 at 2:00?PM Nylon Chen wrote: > > > > Anup Patel ? 2026?5?12??? ??3:59??? > > > > > > On Tue, May 12, 2026 at 12:14?PM Nylon Chen wrote: > > > > > > > > Anup Patel ? 2026?5?9??? ??2:51??? > > > > > > > > > > > > > > On Thu, May 7, 2026 at 11:38?PM Evgeny Voevodin > > > > > wrote: > > > > > > > > > > > > The location of the RNMI/E trap vectors in the Smrnmi extension is > > > > > > implementation-defined, so platforms with vendor-specific NMI vector > > > > > > mechanisms must install the firmware's NMI entry points themselves. > > > > > > > > > > > > Add an smrnmi_handlers_init() callback to sbi_platform_operations that > > > > > > receives the firmware entry points and lets platform code install them > > > > > > at the hardware-specific vector locations. Two pointers are passed: > > > > > > > > > > > > - _trap_rnmi_handler: the dedicated RNMI entry point that saves > > > > > > context using the Smrnmi MN* CSRs and returns via mnret. > > > > > > - _trap_handler: the regular M-mode trap entry since RNME is taken > > > > > > as a regular M-mode trap with NMIE=0. > > > > > > > > > > > > When Smrnmi is present, install the platform's NMI vectors via the new > > > > > > callback, initialize MNSCRATCH with the per-hart scratch pointer, and > > > > > > set MNSTATUS.NMIE. > > > > > > > > > > > > Smrnmi-enabled platforms must register smrnmi_handlers_init; if the > > > > > > extension is detected but no callback is registered, sbi_panic() is > > > > > > called since enabling NMIs without handlers in place would route > > > > > > subsequent traps into nowhere. > > > > > > > > > > > > Signed-off-by: Evgeny Voevodin > > > > > > > > > > LGTM. > > > > > > > > > > Reviewed-by: Anup Patel > > > > > > > > > > Regards, > > > > > Anup > > > > > > > > > > > --- > > > > > > include/sbi/sbi_platform.h | 4 ++++ > > > > > > lib/sbi/sbi_hart.c | 20 ++++++++++++++++++++ > > > > > > 2 files changed, 24 insertions(+) > > > > > > > > > > > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > > > > > > index 715df499..fe382b56 100644 > > > > > > --- a/include/sbi/sbi_platform.h > > > > > > +++ b/include/sbi/sbi_platform.h > > > > > > @@ -150,6 +150,10 @@ struct sbi_platform_operations { > > > > > > /** platform specific pmp disable on current HART */ > > > > > > void (*pmp_disable)(unsigned int n); > > > > > > > > > > > > + /** platform specific Smrnmi handlers init on current HART */ > > > > > > + void (*smrnmi_handlers_init)(void (*rnmi_handler)(void), > > > > > > + void (*rnme_handler)(void)); > > > > > > + > > > > > > /** platform specific Smrnmi NMI handler. > > > > > > * Returns SBI_SUCCESS on success, error code if NMI cannot be handled. */ > > > > > > int (*rnmi_handler)(struct sbi_trap_context *tcntx); > > > > > > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > > > > > > index 781161e5..92c602aa 100644 > > > > > > --- a/lib/sbi/sbi_hart.c > > > > > > +++ b/lib/sbi/sbi_hart.c > > > > > > @@ -532,6 +532,26 @@ static int hart_detect_features(struct sbi_scratch *scratch) > > > > > > if (rc) > > > > > > return rc; > > > > > > > > > > > > + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { > > > > > > + const struct sbi_platform *plat = sbi_platform_thishart_ptr(); > > > > > > + const struct sbi_platform_operations *ops = sbi_platform_ops(plat); > > > > > > + extern void _trap_rnmi_handler(void); > > > > > > + extern void _trap_handler(void); > > > > > > + > > > > > > + if (!ops || !ops->smrnmi_handlers_init) > > > > > > + sbi_panic("Smrnmi detected, but platform lacks smrnmi_handlers_init callback\n"); > > > > > > + > > > > > > + /* Reuse _trap_handler for the RNME slot since RNME is taken > > > > > > + * as a regular M-mode trap with NMIE=0. */ > > > > > > + ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); > > > > > > + > > > > > > + /* Initialize MNSCRATCH for the RNMI handler */ > > > > > > + csr_write(CSR_MNSCRATCH, scratch); > > > > > > + > > > > > > + /* Enable NMIs */ > > > > > > + csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > > > > Two concerns about this block > > > > > > > > First, sbi_panic() when smrnmi_handlers_init is NULL is too strict. > > > > Not all platforms with Smrnmi need to program a vendor-specific NMI > > > > vector register. > > > > Some implementations have a fixed NMI vector address or route NMIs > > > > through mtvec by default; for these, setting MNSCRATCH and NMIE is > > > > sufficient and no platform callback is needed. > > > > > > > > This can be reproduced with QEMU virt: > > > > > > > > qemu-system-riscv64 -M virt -cpu rv64,smrnmi=on -m 256m \ > > > > -bios fw_jump.bin -nographic -no-reboot > > > > > > > > The firmware hangs with zero output. The panic fires before > > > > sbi_platform_early_init() so the console is not yet initialized and > > > > the message is silently dropped. Agree, silent hang regresses platforms which don't need to setup Rnmi/e handlers explicitly and just use default vectors. > > > > > > > > Adding a no-op stub to the generic platform confirms the panic is the > > > > sole cause -- boot proceeds normally with the stub in place. > > > > > > > > Second, the callback returns void, so there is no way to detect > > > > whether the platform succeeded in programming the NMI vector. > > > > csr_set(MNSTATUS, NMIE) then runs unconditionally. > > > > If the callback fails silently, NMIs are enabled but the hardware > > > > vector register is left at its reset value. The first NMI would jump > > > > to an undefined address. > > > > > > > > Suggested fix: > > > > > > > > /* allow NULL: platforms with fixed or mtvec-based NMI vectors > > > > * do not need to program any vendor register */ > > > > if (ops && ops->smrnmi_handlers_init) { > > > > int ret = ops->smrnmi_handlers_init(_trap_rnmi_handler, > > > > _trap_handler); > > > > if (ret) > > > > return ret; > > > > } > > > > > > > > csr_write(CSR_MNSCRATCH, scratch); > > > > csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); > > > > > > > > This requires changing the callback signature from void to int. > > > > > > > > Platforms that have nothing to program return 0, platforms that fail > > > > return an error, and platforms with no callback at all are handled > > > > gracefully without a panic. What I would suggest here: Instead of a stub handler provided by a platform which doesn't require handlers setup, let's introduce a flag that will simply indicate if platform provided handlers setup callback or not. Later, when console becomes available, just print the informational line in the boot banner: Smrnmi subsystem : Default <-- or "Platform" This will help to not regress existing platforms and also provide some diagnostics for those platforms which require handlers initialization but missed that. Sketch implementation could be: enum { SMRNMI_INIT_NONE, SMRNMI_INIT_DEFAULT, SMRNMI_INIT_PLATFORM, }; static int smrnmi_init_mode; if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SMRNMI)) { if (ops && ops->smrnmi_handlers_init) { int ret = ops->smrnmi_handlers_init(_trap_rnmi_handler, _trap_handler); if (ret) return ret; smrnmi_init_mode = SMRNMI_INIT_PLATFORM; } else { smrnmi_init_mode = SMRNMI_INIT_DEFAULT; } csr_write(CSR_MNSCRATCH, scratch); csr_set(CSR_MNSTATUS, MNSTATUS_NMIE); } And in the boot info printer (sbi_boot_print_general_info() or wherever the banner lives): if (smrnmi_init_mode != SMRNMI_INIT_NONE) sbi_printf("Smrnmi subsystem : %s\n", smrnmi_init_mode == SMRNMI_INIT_PLATFORM ? "Platform" : "Default"); > > > Please send a patch with appropriate Fixes tag. > > Will do. > > > > While preparing the patch, I also noticed the series is missing > > non-retentive suspend/resume handling for CSR_MNSCRATCH and > > MNSTATUS.NMIE. > > > > After non-retentive suspend, the hart is fully powered off and all CSR > > state is reset. > > > > On resume, sbi_hsm_hart_resume_finish() calls > > __sbi_hsm_suspend_non_ret_restore() and jumps directly to the kernel > > without going through sbi_hart_init(), so the Smrnmi CSRs set up in > > hart_detect_features() are never re-initialized. > > I think Smrnmi in hart_detect_features() should be moved to a > separate function which should be called from two places: > 1) At the current location from hart_detect_features() before > we use trap-based extension detection > 2) In sbi_hart_init() for both cold boot and warm boot path > > Regards, > Anup I think __sbi_hsm_suspend_non_ret_save/restore is the right place to preserve and restore MNSCRATCH and MNSTATUS CSRs as recovery from non-retentive state bypasses sbi_hart_init(). Also, these functions are placeholders for other CSRs save/restore which don't survive during non-retentive entry. Thanks, Evgeny From raymondmaoca at gmail.com Thu May 14 15:57:46 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:46 -0400 Subject: [PATCH 00/10] Introduce Virtual IRQ (VIRQ) framework Message-ID: <20260514225756.2255758-1-raymondmaoca@gmail.com> From: Raymond Mao This series introduces a Virtual IRQ (VIRQ) framework for OpenSBI to support domain-aware wired interrupt couriering. The main use case is to let OpenSBI receive a host HWIRQ in M-mode, map it into a per-channel VIRQ number space, route it to a target domain, notify the target S-mode payload, and let that payload pop/complete the pending VIRQ through an SBI ecall interface. The implementation is organized as follows: - add irqchip helpers for S-mode pending notification - extend domain context switching to support VIRQ-driven cross-domain couriering and return-to-previous-domain flow - add the core VIRQ mapping, routing, and per-(domain,hart) pending queue framework - add a VIRQ vendor ecall extension for POP and COMPLETE - parse sysirq routing rules from DT under /chosen/opensbi-domains - derive APLIC target hart routing from sysirq nodes - update the irqchip/APLIC path to support deferred completion and per-HWIRQ APLIC targets - document the DT binding and routing rules The DT routing model is based on "opensbi,mpxy-sysirq" nodes. Each interrupts-extended entry contributes one routed physical source, its entry index becomes the VIRQ number within the selected opensbi,mpxy-channel-id, and opensbi,domain selects the destination OpenSBI domain. This series depends on a previous patch set: [PATCH v2 1/3] lib: utils: irqchip: implement APLIC hwirq operation hooks https://lore.kernel.org/opensbi/20260504171342.1655882-1-raymondmaoca at gmail.com/#t Raymond Mao (10): lib: irqchip: add S-mode notification helpers lib: sbi: domain: adaptation for supporting VIRQ couriering domain context switch lib: sbi: Add Virtual IRQ (VIRQ) subsystem lib: sbi: Add VIRQ ecall extension lib: sbi: domain: add domain lookup by name lib: utils: fdt: parse sysirq routing from DT lib: utils: irqchip: derive APLIC targets from sysirq nodes lib: irqchip: support deferred completion and per-HWIRQ APLIC targets lib: sbi: domain: ensure boot_hartid is assigned docs: domain: document sysirq VIRQ mapping and routing rules docs/domain_support.md | 63 ++ include/sbi/sbi_domain.h | 7 + include/sbi/sbi_domain_context.h | 24 + include/sbi/sbi_ecall_interface.h | 26 + include/sbi/sbi_irqchip.h | 24 + include/sbi/sbi_virq.h | 492 +++++++++++ include/sbi_utils/fdt/fdt_helper.h | 17 + include/sbi_utils/irqchip/aplic.h | 1 + lib/sbi/Kconfig | 10 + lib/sbi/objects.mk | 4 + lib/sbi/sbi_domain.c | 49 ++ lib/sbi/sbi_domain_context.c | 152 +++- lib/sbi/sbi_ecall_virq.c | 56 ++ lib/sbi/sbi_irqchip.c | 39 +- lib/sbi/sbi_trap.c | 16 + lib/sbi/sbi_virq.c | 1136 +++++++++++++++++++++++++ lib/utils/fdt/fdt_domain.c | 119 ++- lib/utils/fdt/fdt_helper.c | 49 ++ lib/utils/irqchip/aplic.c | 60 +- lib/utils/irqchip/fdt_irqchip_aplic.c | 103 +++ 20 files changed, 2429 insertions(+), 18 deletions(-) create mode 100644 include/sbi/sbi_virq.h create mode 100644 lib/sbi/sbi_ecall_virq.c create mode 100644 lib/sbi/sbi_virq.c -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:47 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:47 -0400 Subject: [PATCH 01/10] lib: irqchip: add S-mode notification helpers In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-2-raymondmaoca@gmail.com> From: Raymond Mao Add irqchip helpers to set/clear S-mode notification (SEIP-based), In addition to set/clear, expose a get API to read the current notification state so upper layers can do edge-triggered notification. Signed-off-by: Raymond Mao --- include/sbi/sbi_irqchip.h | 24 ++++++++++++++++++++++++ lib/sbi/sbi_irqchip.c | 18 +++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/include/sbi/sbi_irqchip.h b/include/sbi/sbi_irqchip.h index 77b54110..7f23615a 100644 --- a/include/sbi/sbi_irqchip.h +++ b/include/sbi/sbi_irqchip.h @@ -110,4 +110,28 @@ int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot); /** Exit interrupt controllers */ void sbi_irqchip_exit(struct sbi_scratch *scratch); +/** + * Notify S-mode for a pending virtual interrupt on this hart. + * + * The irqchip layer abstracts the notification mechanism; on platforms that + * use SEIP, this sets mip.SEIP. + */ +int sbi_irqchip_notify_smode_set(void); + +/** + * Clear S-mode notification for virtual interrupts on this hart. + * + * The irqchip layer abstracts the notification mechanism; on platforms that + * use SEIP, this clears mip.SEIP. + */ +void sbi_irqchip_notify_smode_clear(void); + +/** + * Read S-mode notification state for virtual interrupts on this hart. + * + * The irqchip layer abstracts the notification mechanism; on platforms that + * use SEIP, this reads mip.SEIP. + */ +bool sbi_irqchip_notify_smode_get(void); + #endif diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c index f8599fa6..e022d534 100644 --- a/lib/sbi/sbi_irqchip.c +++ b/lib/sbi/sbi_irqchip.c @@ -122,7 +122,7 @@ int sbi_irqchip_raw_handler_default(struct sbi_irqchip_device *chip, u32 hwirq) sbi_printf("[IRQCHIP] Calling hwirq %u raw handler callback\n", hwirq); rc = h->callback(hwirq, h->priv); - if (chip->hwirq_eoi) { + if (chip->hwirq_eoi && rc != SBI_EALREADY) { sbi_printf("[IRQCHIP] Calling EOI of hwirq %u\n", hwirq); chip->hwirq_eoi(chip, hwirq); } @@ -320,3 +320,19 @@ void sbi_irqchip_exit(struct sbi_scratch *scratch) if (hd && hd->chip && hd->chip->process_hwirqs) csr_clear(CSR_MIE, MIP_MEIP); } + +int sbi_irqchip_notify_smode_set(void) +{ + csr_set(CSR_MIP, MIP_SEIP); + return 0; +} + +void sbi_irqchip_notify_smode_clear(void) +{ + csr_clear(CSR_MIP, MIP_SEIP); +} + +bool sbi_irqchip_notify_smode_get(void) +{ + return !!(csr_read(CSR_MIP) & MIP_SEIP); +} -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:48 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:48 -0400 Subject: [PATCH 02/10] lib: sbi: domain: adaptation for supporting VIRQ couriering domain context switch In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-3-raymondmaoca@gmail.com> From: Raymond Mao Prerequisite adaptations for introducing VIRQ couriering domain context switch. MIDELEG: - Save/restore MIDELEG in domain contexts and initialize per-domain defaults. SEIP notification: - Add virq_seip_notify flag, and use it to enable SEIP delegation for SEIP-notify domains. - Add smode_notify_pending and helper function to store the S-mode notification status, and use it as a flag to fire the pending notification after domain context switch. Return domain context switch: - Add sbi_domain_context_exit_to_prev() to return to the previous domain without scanning for another candidate. - Introduce per-hart deferred return flags and APIs to request/consume them. - Perform the actual return-to-prev at the end of sbi_trap_handler() to avoid corrupting mepc during ecall handling. Additionally, fix two return domain-switch protential issues: - When a domain switch occurs inside sbi_trap_handler(), return the switched-to trap context from scratch instead of the original trap entry context. This prevents the trap restore path from resuming with stale state from the previous domain. - When returning to an S-mode target domain, copy the restored CSR_SSTATUS SIE/SPIE/SPP bits into trap_ctx->regs.mstatus. The final trap exit writes CSR_MSTATUS from the trap context, so stale mstatus bits can otherwise clear S-mode interrupt enable and leave a pending SEIP undelivered. Signed-off-by: Raymond Mao --- include/sbi/sbi_domain.h | 2 + include/sbi/sbi_domain_context.h | 24 +++++ lib/sbi/sbi_domain_context.c | 152 ++++++++++++++++++++++++++++++- lib/sbi/sbi_trap.c | 16 ++++ 4 files changed, 192 insertions(+), 2 deletions(-) diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h index 16edd4ce..c507023c 100644 --- a/include/sbi/sbi_domain.h +++ b/include/sbi/sbi_domain.h @@ -217,6 +217,8 @@ struct sbi_domain { bool system_suspend_allowed; /** Identifies whether to include the firmware region */ bool fw_region_inited; + /** Whether to notify S-mode for VIRQ couriering */ + bool virq_seip_notify; }; /** The root domain instance */ diff --git a/include/sbi/sbi_domain_context.h b/include/sbi/sbi_domain_context.h index 31a3a7f8..88450fcb 100644 --- a/include/sbi/sbi_domain_context.h +++ b/include/sbi/sbi_domain_context.h @@ -28,6 +28,30 @@ int sbi_domain_context_enter(struct sbi_domain *dom); */ int sbi_domain_context_exit(void); +/** + * Exit the current domain context and return to the previous context + * if one exists. This will not attempt to start other domains. + * + * @return 0 on success and negative error code on failure + */ +int sbi_domain_context_exit_to_prev(void); + +void sbi_domain_context_request_return_to_prev(void); +bool sbi_domain_context_need_return_to_prev(void); +void sbi_domain_context_mark_switched(void); +bool sbi_domain_context_consume_switched(void); + +/** + * Mark a pending S-mode notification for a target domain context. + * + * @param dom pointer to domain + * @param hartindex hart index + * + * @return true if notification was already pending, false otherwise + */ +bool sbi_domain_context_pending_notify_smode(struct sbi_domain *dom, + u32 hartindex); + /** * Initialize domain context support * diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index 158f4990..ee84b2f1 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,8 @@ struct hart_context { unsigned long sip; /** Supervisor address translation and protection register */ unsigned long satp; + /** Machine interrupt delegation register */ + unsigned long mideleg; /** Counter-enable register */ unsigned long scounteren; /** Supervisor environment configuration register */ @@ -55,9 +58,13 @@ struct hart_context { struct hart_context *prev_ctx; /** Is context initialized and runnable */ bool initialized; + /** Pending S-mode notification to deliver after switch */ + bool smode_notify_pending; }; static struct sbi_domain_data dcpriv; +static unsigned long sbi_domain_defer_return_mask; +static unsigned long sbi_domain_switched_mask; static inline struct hart_context *hart_context_get(struct sbi_domain *dom, u32 hartindex) @@ -126,16 +133,32 @@ static int switch_to_next_domain_context(struct hart_context *ctx, sbi_hart_protection_unconfigure(scratch); sbi_hart_protection_configure(scratch); - /* Save current CSR context and restore target domain's CSR context */ + /* + * Save current CSR context and restore target domain's CSR context. + * + * If the trap came from S-mode (MPP=S), MEPC holds the S-mode return + * point. In that case, save MEPC as the SEPC for the current domain + * so returning resumes correctly after a VIRQ-driven domain switch. + */ ctx->sstatus = csr_swap(CSR_SSTATUS, dom_ctx->sstatus); ctx->sie = csr_swap(CSR_SIE, dom_ctx->sie); ctx->stvec = csr_swap(CSR_STVEC, dom_ctx->stvec); ctx->sscratch = csr_swap(CSR_SSCRATCH, dom_ctx->sscratch); - ctx->sepc = csr_swap(CSR_SEPC, dom_ctx->sepc); + { + unsigned long cur_sepc = csr_read(CSR_SEPC); + + if (((csr_read(CSR_MSTATUS) & MSTATUS_MPP) >> + MSTATUS_MPP_SHIFT) == PRV_S) + cur_sepc = csr_read(CSR_MEPC); + ctx->sepc = cur_sepc; + csr_write(CSR_SEPC, dom_ctx->sepc); + } ctx->scause = csr_swap(CSR_SCAUSE, dom_ctx->scause); ctx->stval = csr_swap(CSR_STVAL, dom_ctx->stval); ctx->sip = csr_swap(CSR_SIP, dom_ctx->sip); ctx->satp = csr_swap(CSR_SATP, dom_ctx->satp); + if (misa_extension('S')) + ctx->mideleg = csr_swap(CSR_MIDELEG, dom_ctx->mideleg); if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10) ctx->scounteren = csr_swap(CSR_SCOUNTEREN, dom_ctx->scounteren); if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_12) @@ -146,7 +169,38 @@ static int switch_to_next_domain_context(struct hart_context *ctx, /* Save current trap state and restore target domain's trap state */ trap_ctx = sbi_trap_get_context(scratch); sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); + if (((csr_read(CSR_MSTATUS) & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT) == + PRV_S) { + /* Preserve S-mode return PC in the saved trap context */ + ctx->trap_ctx.regs.mepc = ctx->sepc; + } + /* Ensure M-mode trap context fields are refreshed */ + ctx->trap_ctx.regs.mepc = csr_read(CSR_MEPC); + ctx->trap_ctx.regs.mstatus = csr_read(CSR_MSTATUS); sbi_memcpy(trap_ctx, &dom_ctx->trap_ctx, sizeof(*trap_ctx)); + if (((csr_read(CSR_MSTATUS) & MSTATUS_MPP) >> MSTATUS_MPP_SHIFT) == + PRV_S) { + /* Ensure target trap context returns to its S-mode PC */ + trap_ctx->regs.mepc = dom_ctx->sepc; + } + if (target_dom->next_mode == PRV_S) { + trap_ctx->regs.mstatus &= ~MSTATUS_MPP; + trap_ctx->regs.mstatus |= (PRV_S << MSTATUS_MPP_SHIFT); + trap_ctx->regs.mstatus &= ~(SSTATUS_SIE | SSTATUS_SPIE | + SSTATUS_SPP); + trap_ctx->regs.mstatus |= csr_read(CSR_SSTATUS) & + (SSTATUS_SIE | SSTATUS_SPIE | + SSTATUS_SPP); + } + /* Keep CSR_MEPC aligned with the active trap context */ + csr_write(CSR_MEPC, trap_ctx->regs.mepc); + + /* Deliver pending S-mode notification after switching context */ + if (dom_ctx->smode_notify_pending) { + if (!sbi_irqchip_notify_smode_get()) + sbi_irqchip_notify_smode_set(); + dom_ctx->smode_notify_pending = false; + } /* Mark current context structure initialized because context saved */ ctx->initialized = true; @@ -163,6 +217,7 @@ static int switch_to_next_domain_context(struct hart_context *ctx, else sbi_hsm_hart_stop(scratch, true); } + sbi_domain_context_mark_switched(); return 0; } @@ -182,6 +237,19 @@ static int hart_context_init(u32 hartindex) /* Bind context and domain */ ctx->dom = dom; + /* + * Default MIDELEG policy: root domain keeps SEI delegated; + * non-root domains keep SEI delegated only when VIRQ uses + * mip.SEIP for notification. + */ + if (misa_extension('S')) { + unsigned long mideleg = csr_read(CSR_MIDELEG); + + if (dom == &root || dom->virq_seip_notify) + ctx->mideleg = mideleg | MIP_SEIP; + else + ctx->mideleg = mideleg & ~MIP_SEIP; + } hart_context_set(dom, hartindex, ctx); } @@ -271,6 +339,86 @@ int sbi_domain_context_exit(void) return switch_to_next_domain_context(ctx, dom_ctx); } +int sbi_domain_context_exit_to_prev(void) +{ + struct hart_context *ctx = hart_context_thishart_get(); + struct hart_context *dom_ctx; + + if (!ctx) + return SBI_EINVAL; + + dom_ctx = ctx->prev_ctx; + if (!dom_ctx) + return SBI_ENOENT; + + /* + * Returning to a previous domain implies it has already executed, + * so its context is runnable even if not marked initialized. + */ + dom_ctx->initialized = true; + + /* Clear prev context to avoid unintended re-entry */ + ctx->prev_ctx = NULL; + + return switch_to_next_domain_context(ctx, dom_ctx); +} + +void sbi_domain_context_request_return_to_prev(void) +{ + sbi_domain_defer_return_mask |= (1UL << current_hartindex()); +} + +bool sbi_domain_context_need_return_to_prev(void) +{ + u32 hartindex = current_hartindex(); + bool need = !!(sbi_domain_defer_return_mask & (1UL << hartindex)); + + if (need) + sbi_domain_defer_return_mask &= ~(1UL << hartindex); + + return need; +} + +void sbi_domain_context_mark_switched(void) +{ + sbi_domain_switched_mask |= (1UL << current_hartindex()); +} + +bool sbi_domain_context_consume_switched(void) +{ + u32 hartindex = current_hartindex(); + bool switched = !!(sbi_domain_switched_mask & (1UL << hartindex)); + + if (switched) + sbi_domain_switched_mask &= ~(1UL << hartindex); + + return switched; +} + +bool sbi_domain_context_pending_notify_smode(struct sbi_domain *dom, + u32 hartindex) +{ + struct hart_context *ctx; + bool already; + + if (!dom) + return false; + + ctx = hart_context_get(dom, hartindex); + if (!ctx) { + if (hart_context_init(hartindex)) + return false; + ctx = hart_context_get(dom, hartindex); + if (!ctx) + return false; + } + + already = ctx->smode_notify_pending; + ctx->smode_notify_pending = true; + + return already; +} + int sbi_domain_context_init(void) { /** diff --git a/lib/sbi/sbi_trap.c b/lib/sbi/sbi_trap.c index f41db4d1..79d9ca5a 100644 --- a/lib/sbi/sbi_trap.c +++ b/lib/sbi/sbi_trap.c @@ -24,6 +24,7 @@ #include #include #include +#include static void sbi_trap_error_one(const struct sbi_trap_context *tcntx, const char *prefix, u32 hartid, u32 depth) @@ -372,6 +373,21 @@ trap_done: if (sbi_mstatus_prev_mode(regs->mstatus) != PRV_M) sbi_sse_process_pending_events(regs); + if (sbi_domain_context_need_return_to_prev()) { + int rc = sbi_domain_context_exit_to_prev(); + + if (rc && rc != SBI_ENOENT) + sbi_printf("return_to_prev failed, rc=%d\n", + rc); + } + + if (sbi_domain_context_consume_switched()) { + struct sbi_trap_context *newctx = sbi_trap_get_context(scratch); + + sbi_trap_set_context(scratch, newctx->prev_context); + return newctx; + } + sbi_trap_set_context(scratch, tcntx->prev_context); return tcntx; } -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:49 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:49 -0400 Subject: [PATCH 03/10] lib: sbi: Add Virtual IRQ (VIRQ) subsystem In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-4-raymondmaoca@gmail.com> From: Raymond Mao VIRQ is an abstraction framework providing per-MPXY-channel HWIRQ<->VIRQ mapping, per-(domain,hart) VIRQ routing and couriering. It notifies S-mode payload via irqchip SEIP helper when a VIRQ is mapped/routed/enqueued and provides ecall extension for a S-mode payload to pop/complete a pending VIRQ. VIRQ layer is composed of three major parts: 1. VIRQ mapping and allocation - Provides a stable per-MPXY-channel mapping between a host physical interrupt endpoint (chip_uid, hwirq) and a VIRQ number. - VIRQ number allocation uses a scalable bitmap. 2. HWIRQ->(Domain,hart) routing rules - Routing rules are derived from sysirq nodes by interrupts-extended property, for example: interrupts-extended = <&aplic HWIRQx IRQ_TYPE>, // virq 0 <&aplic HWIRQy IRQ_TYPE>, // virq 1 ...; - VIRQ numbers are allocated from zero, implicit from the order of the entries in the interrupts-extended property. - Each entry is cached as a routing rule. - Default behavior: if an asserted HWIRQ does not match any routing rule, it will be routed to the root domain (channel 0) as a fallback. 3. Per-(domain,hart) pending queue couriering - Each domain maintains a per-hart ring buffer queue of pending VIRQs. A courier handler enqueues VIRQs on HWIRQ assertion. - The couriering is domain-aware. It switches to the target domain when it is not the same as the current one, and sends request for returning to the previous domain after SEIP completion. - S-mode notification is edge-triggered based on the irqchip notify state, and is cleared only when the queue becomes empty. Signed-off-by: Raymond Mao --- include/sbi/sbi_domain.h | 2 + include/sbi/sbi_virq.h | 492 +++++++++++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_domain.c | 10 + lib/sbi/sbi_virq.c | 1136 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1641 insertions(+) create mode 100644 include/sbi/sbi_virq.h create mode 100644 lib/sbi/sbi_virq.c diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h index c507023c..7e288cd8 100644 --- a/include/sbi/sbi_domain.h +++ b/include/sbi/sbi_domain.h @@ -219,6 +219,8 @@ struct sbi_domain { bool fw_region_inited; /** Whether to notify S-mode for VIRQ couriering */ bool virq_seip_notify; + /** per-domain wired-IRQ courier state */ + void *virq_priv; }; /** The root domain instance */ diff --git a/include/sbi/sbi_virq.h b/include/sbi/sbi_virq.h new file mode 100644 index 00000000..566ae827 --- /dev/null +++ b/include/sbi/sbi_virq.h @@ -0,0 +1,492 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Virtual IRQ (VIRQ) courier/routing layer for OpenSBI. + * + * This header defines: + * 1) VIRQ number allocation and (chip_uid,hwirq) <-> VIRQ mapping + * 2) HWIRQ -> Domain routing rules (from DeviceTree "opensbi,mpxy-sysirq") + * 3) Per-(domain,hart) pending queue (push in M-mode, pop/complete in S-mode) + * + * High-level design intent: + * - All physical host IRQs are handled in M-mode by host irqchip drivers. + * - For each incoming HWIRQ, OpenSBI determines the destination domain using + * DT-defined routing rules and enqueues a VIRQ into the per-(domain,hart) + * pending queue. + * - S-mode payload consumes pending VIRQs via pop(), and completes them via + * complete(), which unmasks the corresponding host HWIRQ line. + * - M-mode notifies S-mode via the irqchip notification mechanism. + * + * Notes: + * - "opensbi,mpxy-sysirq" routing is derived from the sysirq node's + * "interrupts-extended" entries. It does not encode privilege level + * delivery. Hardware delivery (MEI vs SEI) is determined by platform IRQ + * topology and interrupt-parent. + * + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#ifndef __SBI_VIRQ_H__ +#define __SBI_VIRQ_H__ + +#include +#include +#include +#include + +/* + * Current implementation behavior when queue overflows: + * - Drop the incoming VIRQ + * - Return SBI_ENOMEM + */ +#define VIRQ_QSIZE 64 + +/* + * Reverse mapping table is chunked to avoid a single large static array. + * VIRQ is used as an index into a chunk; chunks are allocated on demand. + */ +#define VIRQ_CHUNK_SHIFT 6U +#define VIRQ_CHUNK_SIZE (1U << VIRQ_CHUNK_SHIFT) +#define VIRQ_CHUNK_MASK (VIRQ_CHUNK_SIZE - 1U) + +/* Minimum growth step for forward mapping vector and related metadata. */ +#define VEC_GROW_MIN 16U + +/* Returned by pop when no pending VIRQ is available. */ +#define VIRQ_INVALID 0xffffffffU + +/* + * VIRQ allocator and (chip_uid,hwirq) <-> VIRQ mapping + */ + +/* + * VIRQ mapping model: + * - Forward mapping: (chip_uid,hwirq) -> VIRQ + * Implementation: dynamic vector of entries (linear search). + * + * - Reverse mapping: VIRQ -> (chip_uid,hwirq) + * Implementation: chunked table allocated on demand, O(1) lookup. + * + * - VIRQ number allocation: + * Implementation: growable bitmap; capacity expands as needed. + * + * Memory usage scales with the number of installed mappings. + */ + +/* Entry of reverse mapping table: represents (chip_uid,hwirq) endpoint */ +struct virq_entry { + u32 chip_uid; + u32 hwirq; +}; + +/* Chunked reverse mapping table: VIRQ -> (chip_uid,hwirq) */ +struct virq_chunk { + struct virq_entry e[VIRQ_CHUNK_SIZE]; +}; + +/* + * HWIRQ -> Domain routing rules + */ + +/* + * A routing rule maps a single HWIRQ to a domain. + * + * Rules are populated once during cold boot while parsing the DT + * opensbi-domains configuration (sysirq node "opensbi,mpxy-sysirq"). + * + * DT encodes mapping via "interrupts-extended"; the index within this array + * becomes the VIRQ number for the given MPXY channel. + * + * Policy notes: + * - Duplicate HWIRQ entries are rejected and return SBI_EALREADY. + * - If no rule matches, routing falls back to the root domain (&root). + */ +struct sbi_virq_route_rule { + u32 hwirq; + struct sbi_domain *dom; /* owner domain */ + u32 channel_id; /* VIRQ space/channel */ +}; + +/* + * Courier context passed as 'opaque' to sbi_virq_courier_handler(), created + * per host irqchip. + * + * The courier handler needs to: + * - map (chip_uid,hwirq) -> VIRQ + * - mask/unmask HWIRQ using the correct irqchip device + * Therefore the irqchip device pointer is carried here. + */ +struct sbi_virq_courier_ctx { + struct sbi_irqchip_device *chip; +}; + +/* + * Per-(domain,hart) pending VIRQ state and queue management + */ + +/* + * Per-(domain,hart) VIRQ state. + * + * Locking: + * - lock protects head/tail and q[]. + * + * Queue semantics: + * - q[] stores VIRQs pending handling for this (domain,hart). + * - enqueue is performed by M-mode (courier handler) according to route rule + * populated from DT. + * - pop/complete is performed by S-mode payload running in the destination + * domain on the current hart. + * - chip caches the irqchip device for unmasking on complete(). + */ +struct sbi_domain_virq_state { + spinlock_t lock; + u32 head; + u32 tail; + + /* Pending VIRQ ring buffer. */ + struct { + u32 virq; + u32 channel_id; + struct sbi_irqchip_device *chip; + } q[VIRQ_QSIZE]; + + /* Last popped entry for completion. */ + u32 last_pop_virq; + u32 last_pop_channel_id; + struct sbi_irqchip_device *last_pop_chip; + + /* Return to previous domain after VIRQ completion. */ + bool return_to_prev; +}; + +/* + * Per-domain private VIRQ context. + * + * Attached to struct sbi_domain and contains per-hart states. + */ +struct sbi_domain_virq_priv { + /* number of platform harts */ + u32 nharts; + + /* number of allocated per-hart states */ + u32 st_count; + + /* per-hart VIRQ state pointer array (indexed by hart index) */ + struct sbi_domain_virq_state *st_by_hart[]; +}; + +/* Courier binding used when enqueuing a VIRQ. */ +struct sbi_virq_courier_binding { + /* destination domain */ + struct sbi_domain *dom; + + /* irqchip device that asserted the HWIRQ */ + struct sbi_irqchip_device *chip; + + /* VIRQ space/channel ID */ + u32 channel_id; + + /* VIRQ number to enqueue */ + u32 virq; +}; + +/* + * Public APIs + */ + +/* + * Initialize a per-channel VIRQ map. + * + * @channel_id: + * VIRQ space/channel ID (0 is the default channel). + * + * @init_virq_cap: + * Initial capacity in VIRQ bits (e.g., 256). Implementation may grow beyond. + * + * Return: + * SBI_OK on success + * SBI_ENOMEM on allocation failure + */ +int sbi_virq_map_init(u32 channel_id, u32 init_virq_cap); + +/* + * Create or get a stable mapping for (channel_id, chip_uid, hwirq) -> VIRQ. + * + * @channel_id: + * Paravirt channel ID; VIRQ numbering is local to each channel. + * + * @chip_uid: + * Unique 32-bit ID of the host irqchip device. + * + * @hwirq: + * Host HWIRQ number as produced by the irqchip driver (e.g. APLIC claim ID). + * + * @allow_identity: + * If true, allocator may attempt VIRQ == hwirq for small ranges. + * + * @identity_limit: + * Upper bound (exclusive) for identity mapping trial: hwirq < identity_limit. + * + * @out_virq: + * Output pointer receiving the mapped/allocated VIRQ (0 is valid). + * + * Return: + * SBI_OK on success + * SBI_ENOMEM on allocation failure + * SBI_ENOSPC if allocator cannot allocate + * SBI_EINVAL on invalid parameters + */ +int sbi_virq_map_one(u32 channel_id, u32 chip_uid, u32 hwirq, + bool allow_identity, u32 identity_limit, u32 *out_virq); + +/* + * Force a mapping for (channel_id, chip_uid, hwirq) -> VIRQ. + * + * @channel_id: + * Paravirt channel ID; VIRQ numbering is local to each channel. + * + * @chip_uid: + * Unique 32-bit ID of the host irqchip device. + * + * @hwirq: + * Host HWIRQ number as produced by the irqchip driver. + * + * @virq: + * VIRQ number to assign (0 is valid). + * + * Return: + * SBI_OK on success + * SBI_ENOMEM on allocation failure + * SBI_EINVAL on invalid parameters + * SBI_EALREADY if a different mapping already exists + */ +int sbi_virq_map_set(u32 channel_id, u32 chip_uid, u32 hwirq, u32 virq); + +/* + * Ensure VIRQ map capacity for a given channel. + * + * @channel_id: + * Paravirt channel ID. + * + * @min_virq_cap: + * Minimum VIRQ bitmap capacity in bits (will be rounded up). + * + * Return: + * SBI_OK on success + * SBI_EINVAL if the map is not initialized (channel 0) + * SBI_ENOMEM on allocation failure + */ +int sbi_virq_map_ensure_cap(u32 channel_id, u32 min_virq_cap); + +/* + * Lookup existing mapping: (channel_id, chip_uid, hwirq) -> VIRQ. + * + * @channel_id: + * Paravirt channel ID; VIRQ numbering is local to each channel. + * + * @chip_uid: + * Irqchip unique id. + * + * @hwirq: + * Host hwirq number. + * + * @out_virq: + * Output VIRQ (0 is valid). + * + * Return: + * SBI_OK if found + * SBI_ENOENT if not mapped + * SBI_EINVAL on invalid input + */ +int sbi_virq_hwirq2virq(u32 channel_id, u32 chip_uid, u32 hwirq, + u32 *out_virq); + +/* + * Reverse lookup: (channel_id, VIRQ) -> (chip_uid, hwirq). + * + * @channel_id: + * Paravirt channel ID; VIRQ numbering is local to each channel. + * + * @virq: + * VIRQ number to look up. + * + * @out_chip_uid: + * Output pointer receiving irqchip unique id. + * + * @out_hwirq: + * Output pointer receiving host hwirq number. + * + * Return: + * SBI_OK on success + * SBI_EINVAL if virq is VIRQ_INVALID, out of range, not allocated, or + * reverse entry missing + */ +int sbi_virq_virq2hwirq(u32 channel_id, u32 virq, + u32 *out_chip_uid, u32 *out_hwirq); + +/* + * Unmap a single VIRQ mapping and free the VIRQ number. + * + * @virq: + * VIRQ number to unmap. + * + * Return: + * SBI_OK on success + * SBI_EINVAL if virq is invalid or state is inconsistent + */ +int sbi_virq_unmap_one(u32 virq); + +/* + * Uninitialize the VIRQ mapping allocator and free all resources. + * + * Notes: + * - This frees bitmap, forward vector, and reverse chunks. + */ +void sbi_virq_map_uninit(void); + +/* + * Reset all HWIRQ->Domain routing rules (frees the rule array). + * + * Typical usage: + * - Called once at cold boot during init before parsing DT domains. + */ +void sbi_virq_route_reset(void); + +/* + * Add a routing rule: hwirq -> dom with channel_id. + * + * @dom: + * Target domain that should receive HWIRQs in this range. + * + * @hwirq: + * HWIRQ number to route. + * + * @channel_id: + * Paravirt channel ID for VIRQ mapping (MPXY channel). + * + * Return: + * SBI_OK on success + * SBI_EINVAL on invalid parameters + * SBI_ENOMEM on allocation failure + * SBI_EALREADY if the HWIRQ already has a rule + */ +int sbi_virq_route_add(struct sbi_domain *dom, u32 hwirq, u32 channel_id); + +/* + * Lookup destination domain for a given HWIRQ. + * + * @hwirq: + * Incoming host HWIRQ number. + * + * @out_dom: + * Output pointer receiving destination domain. If no rule matches, &root + * is returned. + * + * @out_channel_id: + * Output pointer receiving channel id if non-NULL. + * + * Return: + * SBI_OK on success + * SBI_EINVAL on invalid parameters + */ +int sbi_virq_route_lookup(u32 hwirq, struct sbi_domain **out_dom, + u32 *out_channel_id); + +/* + * Enqueue a VIRQ for the destination domain on the current hart. + * + * @c: + * Courier binding containing: + * - c->dom : destination domain + * - c->chip : irqchip device pointer + * - c->virq : VIRQ number + * + * Return: + * SBI_OK on success + * SBI_EINVAL on invalid parameters + * SBI_ENODEV if per-(domain,hart) state is not available + * SBI_ENOMEM if queue is full + */ +int sbi_virq_enqueue(struct sbi_virq_courier_binding *c); + +/* + * Pop the next pending VIRQ for the current domain on the current hart. + * + * Return: + * VIRQ_INVALID if none pending or state not available + * otherwise a VIRQ number (zero is legal) + */ +u32 sbi_virq_pop_thishart(void); + +/* + * Complete a previously couriered VIRQ for the current domain/hart. + * + * @virq: + * VIRQ to complete. + */ +void sbi_virq_complete_thishart(u32 virq); + +/* Return to previous domain if a VIRQ-driven switch is pending. */ +void sbi_virq_return_to_prev_if_needed(void); + + +/* + * Courier handler intended to be registered by host irqchip driver. + * + * @hwirq: + * Incoming host HWIRQ number asserted on the irqchip. + * + * @opaque: + * Point to a valid struct sbi_virq_courier_ctx, which provides the + * irqchip device pointer used for mapping and mask/unmask. + * + * Return: + * SBI_OK on success + * SBI_EINVAL on invalid parameters + * Other SBI_E* propagated from mapping or enqueue + */ +int sbi_virq_courier_handler(u32 hwirq, void *opaque); + +/* + * Initialize per-domain VIRQ state. + * + * @dom: + * Domain to initialize. + * + * Return: + * SBI_OK on success + * SBI_EINVAL on invalid parameters + * SBI_ENOMEM on allocation failure + */ +int sbi_virq_domain_init(struct sbi_domain *dom); + +/* + * Free per-domain VIRQ state. + * + * @dom: + * Free the per-domain VIRQ state. + */ +void sbi_virq_domain_exit(struct sbi_domain *dom); + +/* + * Initialize VIRQ subsystem (mapping allocator + route rules). + * Must be called once before parsing sysirq DT nodes. + * + * @init_virq_cap: + * Initial VIRQ bitmap capacity in bits + * + * Return: + * SBI_OK on success + * SBI_EALREADY if called more than once + * SBI_ENOMEM on allocation failure + * Other SBI_E* error codes propagated from mapping init + */ +int sbi_virq_init(u32 init_virq_cap); + +/* + * Query whether the VIRQ subsystem is initialized. + */ +bool sbi_virq_is_inited(void); + +#endif diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 07d13229..184bf173 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -86,6 +86,7 @@ libsbi-objs-y += sbi_illegal_insn.o libsbi-objs-y += sbi_init.o libsbi-objs-y += sbi_ipi.o libsbi-objs-y += sbi_irqchip.o +libsbi-objs-y += sbi_virq.o libsbi-objs-y += sbi_platform.o libsbi-objs-y += sbi_pmu.o libsbi-objs-y += sbi_dbtr.o diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c index 7030848d..2a846eea 100644 --- a/lib/sbi/sbi_domain.c +++ b/lib/sbi/sbi_domain.c @@ -18,6 +18,7 @@ #include #include #include +#include SBI_LIST_HEAD(domain_list); @@ -693,6 +694,15 @@ int sbi_domain_register(struct sbi_domain *dom, return rc; } + /* Init per-domain wired-IRQ courier state */ + rc = sbi_virq_domain_init(dom); + if (rc) { + sbi_printf("%s: virq init failed for %s (error %d)\n", + __func__, dom->name, rc); + sbi_list_del(&dom->node); + return rc; + } + return 0; } diff --git a/lib/sbi/sbi_virq.c b/lib/sbi/sbi_virq.c new file mode 100644 index 00000000..fcd83369 --- /dev/null +++ b/lib/sbi/sbi_virq.c @@ -0,0 +1,1136 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026 RISCstar Solutions. + * + * Author: Raymond Mao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct map_node { + u32 chip_uid; + u32 hwirq; + u32 virq; +}; + +struct sbi_virq_map { + spinlock_t lock; + + /* allocator bitmap */ + unsigned long *bmap; + u32 bmap_nbits; /* virq range: [0..nbits-1] */ + + /* reverse table: virq -> endpoint */ + struct virq_chunk **chunks; + u32 chunks_cap; /* number of chunk pointers */ + + /* forward table: vector of mappings, linear search */ + struct map_node *nodes; + u32 nodes_cnt; + u32 nodes_cap; +}; + +struct sbi_virq_map_list { + u32 channel_id; + struct sbi_virq_map map; +}; + +/* + * HWIRQ -> Domain routing rules + */ + +struct sbi_virq_router { + spinlock_t lock; + struct sbi_virq_route_rule *rules; + u32 cnt; + u32 cap; +}; + +static struct sbi_virq_map g_virq_map; /* channel 0 */ +static struct sbi_virq_map_list *g_virq_maps; +static u32 g_virq_maps_cnt; +static u32 g_virq_maps_cap; +static spinlock_t g_virq_maps_lock; +static struct sbi_virq_router g_router; +static bool g_virq_inited; + +void sbi_virq_route_reset(void) +{ + spin_lock(&g_router.lock); + if (g_router.rules) { + sbi_free(g_router.rules); + g_router.rules = NULL; + } + g_router.cnt = 0; + g_router.cap = 0; + spin_unlock(&g_router.lock); +} + +static int router_ensure_cap(u32 need) +{ + struct sbi_virq_route_rule *newp; + u32 newcap; + + if (g_router.cap >= need) + return 0; + + newcap = g_router.cap ? (g_router.cap << 1) : 8; + while (newcap < need) + newcap <<= 1; + + newp = sbi_zalloc((size_t)newcap * sizeof(*newp)); + if (!newp) + return SBI_ENOMEM; + + if (g_router.rules) { + sbi_memcpy(newp, g_router.rules, + (size_t)g_router.cnt * sizeof(*newp)); + sbi_free(g_router.rules); + } + + g_router.rules = newp; + g_router.cap = newcap; + + return SBI_OK; +} + +int sbi_virq_route_add(struct sbi_domain *dom, u32 hwirq, u32 channel_id) +{ + int rc; + + if (!dom) + return SBI_EINVAL; + + spin_lock(&g_router.lock); + + /* Reject duplicates to keep routing unambiguous */ + for (u32 i = 0; i < g_router.cnt; i++) { + if (g_router.rules[i].hwirq == hwirq) { + spin_unlock(&g_router.lock); + return SBI_EALREADY; + } + } + + rc = router_ensure_cap(g_router.cnt + 1); + if (rc) { + spin_unlock(&g_router.lock); + return rc; + } + + g_router.rules[g_router.cnt].hwirq = hwirq; + g_router.rules[g_router.cnt].dom = dom; + g_router.rules[g_router.cnt].channel_id = channel_id; + g_router.cnt++; + + spin_unlock(&g_router.lock); + + return SBI_OK; +} + +int sbi_virq_route_lookup(u32 hwirq, struct sbi_domain **out_dom, + u32 *out_channel_id) +{ + /* Fast path: no rules */ + if (!g_router.cnt) { + if (out_dom) + *out_dom = &root; + if (out_channel_id) + *out_channel_id = 0; + return SBI_OK; + } + + spin_lock(&g_router.lock); + for (u32 i = 0; i < g_router.cnt; i++) { + if (hwirq == g_router.rules[i].hwirq) { + struct sbi_domain *d = g_router.rules[i].dom; + u32 cid = g_router.rules[i].channel_id; + + spin_unlock(&g_router.lock); + if (out_dom) + *out_dom = d ? d : &root; + if (out_channel_id) + *out_channel_id = cid; + return SBI_OK; + } + } + spin_unlock(&g_router.lock); + + if (out_dom) + *out_dom = &root; + if (out_channel_id) + *out_channel_id = 0; + return SBI_OK; +} + +static inline void virq_state_init(struct sbi_domain_virq_state *st) +{ + SPIN_LOCK_INIT(st->lock); + st->head = 0; + st->tail = 0; + st->return_to_prev = false; +} + +static inline +struct sbi_domain_virq_state *domain_virq_thishart(struct sbi_domain *dom) +{ + unsigned long hartidx = sbi_hartid_to_hartindex(current_hartid()); + struct sbi_domain_virq_priv *p; + + p = (struct sbi_domain_virq_priv *)dom->virq_priv; + if (!p || hartidx >= p->nharts) + return NULL; + + return p->st_by_hart[hartidx]; +} + +static inline bool q_full(struct sbi_domain_virq_state *st) +{ + return ((st->tail + 1) % VIRQ_QSIZE) == st->head; +} + +static inline bool q_empty(struct sbi_domain_virq_state *st) +{ + return st->head == st->tail; +} + +static inline void virq_set_domain_return_flag(struct sbi_domain *dom, + bool return_to_prev) +{ + struct sbi_domain_virq_state *st = domain_virq_thishart(dom); + + if (!st) + return; + + spin_lock(&st->lock); + st->return_to_prev = return_to_prev; + spin_unlock(&st->lock); +} + +static u32 sbi_virq_platform_hart_count(void) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + + return sbi_platform_hart_count(plat); +} + +static int bmap_alloc_one(struct sbi_virq_map *m, u32 *out_virq) +{ + u32 v; + + for (v = 0; v < m->bmap_nbits; v++) { + if (!bitmap_test(m->bmap, (int)v)) { + bitmap_set(m->bmap, (int)v, 1); + *out_virq = v; + return 0; + } + } + + return SBI_ENOSPC; +} + +static int bmap_alloc_specific(struct sbi_virq_map *m, u32 virq) +{ + if (virq >= m->bmap_nbits) + return SBI_EINVAL; + if (bitmap_test(m->bmap, (int)virq)) + return SBI_EALREADY; + bitmap_set(m->bmap, (int)virq, 1); + + return 0; +} + +static void bmap_free_one(struct sbi_virq_map *m, u32 virq) +{ + if (virq < m->bmap_nbits) + bitmap_clear(m->bmap, (int)virq, 1); +} + +static int chunks_ensure_cap(struct sbi_virq_map *m, u32 new_bmap_nbits) +{ + u32 new_chunks_cap = + (new_bmap_nbits + VIRQ_CHUNK_SIZE - 1U) >> VIRQ_CHUNK_SHIFT; + struct virq_chunk **newp; + + if (new_chunks_cap <= m->chunks_cap) + return 0; + + newp = sbi_zalloc((size_t)new_chunks_cap * sizeof(*newp)); + if (!newp) + return SBI_ENOMEM; + + if (m->chunks) { + sbi_memcpy(newp, m->chunks, + (size_t)m->chunks_cap * sizeof(*newp)); + sbi_free(m->chunks); + } + + m->chunks = newp; + m->chunks_cap = new_chunks_cap; + + return 0; +} + +static int bmap_grow(struct sbi_virq_map *m, u32 new_nbits) +{ + unsigned long *newmap; + + if (new_nbits <= m->bmap_nbits) + return 0; + + newmap = sbi_zalloc(bitmap_estimate_size((int)new_nbits)); + if (!newmap) + return SBI_ENOMEM; + + bitmap_zero(newmap, (int)new_nbits); + bitmap_copy(newmap, m->bmap, (int)m->bmap_nbits); + + sbi_free(m->bmap); + m->bmap = newmap; + m->bmap_nbits = new_nbits; + + return chunks_ensure_cap(m, new_nbits); +} + +static struct virq_entry *rev_get_or_alloc(struct sbi_virq_map *m, u32 virq) +{ + u32 ci = virq >> VIRQ_CHUNK_SHIFT; + u32 off = virq & VIRQ_CHUNK_MASK; + + if (ci >= m->chunks_cap) + return NULL; + + if (!m->chunks[ci]) { + m->chunks[ci] = sbi_zalloc(sizeof(struct virq_chunk)); + if (!m->chunks[ci]) + return NULL; + } + return &m->chunks[ci]->e[off]; +} + +static struct virq_entry *rev_get_existing(struct sbi_virq_map *m, u32 virq) +{ + u32 ci = virq >> VIRQ_CHUNK_SHIFT; + u32 off = virq & VIRQ_CHUNK_MASK; + + if (ci >= m->chunks_cap || !m->chunks[ci]) + return NULL; + return &m->chunks[ci]->e[off]; +} + +static void rev_clear(struct sbi_virq_map *m, u32 virq) +{ + struct virq_entry *e = rev_get_existing(m, virq); + + if (e) { + e->chip_uid = 0; + e->hwirq = 0; + } +} + +static int vec_ensure_cap(struct sbi_virq_map *m, u32 need_cnt) +{ + struct map_node *newp; + u32 newcap; + + if (m->nodes_cap >= need_cnt) + return 0; + + newcap = m->nodes_cap ? (m->nodes_cap << 1) : + VEC_GROW_MIN; + while (newcap < need_cnt) + newcap <<= 1; + + newp = sbi_zalloc((size_t)newcap * sizeof(*newp)); + if (!newp) + return SBI_ENOMEM; + + if (m->nodes) { + sbi_memcpy(newp, m->nodes, + (size_t)m->nodes_cnt * sizeof(*newp)); + sbi_free(m->nodes); + } + + m->nodes = newp; + m->nodes_cap = newcap; + + return 0; +} + +static int forward_find_idx(struct sbi_virq_map *m, + u32 chip_uid, u32 hwirq, u32 *out_idx) +{ + u32 i; + + for (i = 0; i < m->nodes_cnt; i++) { + if (m->nodes[i].chip_uid == chip_uid && + m->nodes[i].hwirq == hwirq) { + *out_idx = i; + return 0; + } + } + + return SBI_ENOENT; +} + +static int virq_map_init_one(struct sbi_virq_map *m, u32 init_virq_cap) +{ + int rc; + + sbi_memset(m, 0, sizeof(*m)); + SPIN_LOCK_INIT(m->lock); + + if (init_virq_cap < 8U) + init_virq_cap = 8U; + + m->bmap_nbits = init_virq_cap; + m->bmap = + sbi_zalloc(bitmap_estimate_size((int)m->bmap_nbits)); + if (!m->bmap) + return SBI_ENOMEM; + + bitmap_zero(m->bmap, (int)m->bmap_nbits); + + rc = chunks_ensure_cap(m, m->bmap_nbits); + if (rc) + return rc; + + return SBI_OK; +} + +static struct sbi_virq_map *virq_map_get(u32 channel_id, bool create, + u32 init_virq_cap) +{ + u32 i; + struct sbi_virq_map_list *newp; + + if (channel_id == 0) + return &g_virq_map; + + spin_lock(&g_virq_maps_lock); + for (i = 0; i < g_virq_maps_cnt; i++) { + if (g_virq_maps[i].channel_id == channel_id) { + spin_unlock(&g_virq_maps_lock); + return &g_virq_maps[i].map; + } + } + if (!create) { + spin_unlock(&g_virq_maps_lock); + return NULL; + } + + if (g_virq_maps_cnt == g_virq_maps_cap) { + u32 newcap = g_virq_maps_cap ? (g_virq_maps_cap << 1) : 4; + + newp = sbi_zalloc((size_t)newcap * sizeof(*newp)); + if (!newp) { + spin_unlock(&g_virq_maps_lock); + return NULL; + } + if (g_virq_maps) { + sbi_memcpy(newp, g_virq_maps, + (size_t)g_virq_maps_cnt * sizeof(*newp)); + sbi_free(g_virq_maps); + } + g_virq_maps = newp; + g_virq_maps_cap = newcap; + } + + g_virq_maps[g_virq_maps_cnt].channel_id = channel_id; + if (virq_map_init_one(&g_virq_maps[g_virq_maps_cnt].map, + init_virq_cap)) { + spin_unlock(&g_virq_maps_lock); + return NULL; + } + g_virq_maps_cnt++; + spin_unlock(&g_virq_maps_lock); + + return &g_virq_maps[g_virq_maps_cnt - 1].map; +} + +int sbi_virq_map_init(u32 channel_id, u32 init_virq_cap) +{ + if (channel_id == 0) + return virq_map_init_one(&g_virq_map, init_virq_cap); + SPIN_LOCK_INIT(g_virq_maps_lock); + return virq_map_get(channel_id, true, init_virq_cap) ? + SBI_OK : SBI_ENOMEM; +} + +int sbi_virq_map_one(u32 channel_id, u32 chip_uid, u32 hwirq, + bool allow_identity, u32 identity_limit, + u32 *out_virq) +{ + u32 idx, virq = 0; + int rc; + struct sbi_virq_map *m; + + m = virq_map_get(channel_id, true, 0); + if (!m) + return SBI_ENOMEM; + + spin_lock(&m->lock); + /* already mapped? */ + rc = forward_find_idx(m, chip_uid, hwirq, &idx); + if (!rc) { + *out_virq = m->nodes[idx].virq; + spin_unlock(&m->lock); + return 0; + } + + /* ensure vector capacity for new node */ + rc = vec_ensure_cap(m, m->nodes_cnt + 1U); + if (rc) { + spin_unlock(&m->lock); + return rc; + } + + /* optional identity */ + if (allow_identity && hwirq < identity_limit) { + /* ensure bitmap covers this virq */ + if (hwirq >= m->bmap_nbits) { + u32 new_nbits = m->bmap_nbits; + + while (new_nbits <= hwirq) + new_nbits <<= 1; + rc = bmap_grow(m, new_nbits); + if (rc) { + spin_unlock(&m->lock); + return rc; + } + } + + rc = bmap_alloc_specific(m, hwirq); + if (!rc) + virq = hwirq; + else if (rc != SBI_EALREADY) { + spin_unlock(&m->lock); + return rc; + } + } + + /* allocate new virq if identity not taken */ + if (!virq) { + rc = bmap_alloc_one(m, &virq); + if (rc == SBI_ENOSPC) { + rc = bmap_grow(m, m->bmap_nbits << 1); + if (rc) { + spin_unlock(&m->lock); + return rc; + } + rc = bmap_alloc_one(m, &virq); + } + if (rc) { + spin_unlock(&m->lock); + return rc; + } + } + + /* install reverse mapping */ + { + struct virq_entry *e = rev_get_or_alloc(m, virq); + + if (!e) { + bmap_free_one(m, virq); + spin_unlock(&m->lock); + return SBI_ENOMEM; + } + e->chip_uid = chip_uid; + e->hwirq = hwirq; + } + + /* append forward node */ + m->nodes[m->nodes_cnt].chip_uid = chip_uid; + m->nodes[m->nodes_cnt].hwirq = hwirq; + m->nodes[m->nodes_cnt].virq = virq; + m->nodes_cnt++; + + *out_virq = virq; + spin_unlock(&m->lock); + + return SBI_OK; +} + +int sbi_virq_map_set(u32 channel_id, u32 chip_uid, u32 hwirq, u32 virq) +{ + struct sbi_virq_map *m; + u32 idx; + int rc; + + m = virq_map_get(channel_id, true, virq + 1U); + if (!m) + return SBI_ENOMEM; + + spin_lock(&m->lock); + rc = forward_find_idx(m, chip_uid, hwirq, &idx); + if (!rc) { + spin_unlock(&m->lock); + return (m->nodes[idx].virq == virq) ? SBI_OK : SBI_EALREADY; + } + + if (virq >= m->bmap_nbits) { + u32 new_nbits = m->bmap_nbits; + + while (new_nbits <= virq) + new_nbits <<= 1; + rc = bmap_grow(m, new_nbits); + if (rc) { + spin_unlock(&m->lock); + return rc; + } + } + + rc = bmap_alloc_specific(m, virq); + if (rc == SBI_EALREADY) { + struct virq_entry *e = rev_get_existing(m, virq); + + if (!e || e->chip_uid != chip_uid || e->hwirq != hwirq) { + spin_unlock(&m->lock); + return SBI_EALREADY; + } + + spin_unlock(&m->lock); + return SBI_OK; + } else if (rc) { + spin_unlock(&m->lock); + return rc; + } + + rc = vec_ensure_cap(m, m->nodes_cnt + 1U); + if (rc) { + spin_unlock(&m->lock); + return rc; + } + + { + struct virq_entry *e = rev_get_or_alloc(m, virq); + + if (!e) { + bmap_free_one(m, virq); + spin_unlock(&m->lock); + return SBI_ENOMEM; + } + e->chip_uid = chip_uid; + e->hwirq = hwirq; + } + + m->nodes[m->nodes_cnt].chip_uid = chip_uid; + m->nodes[m->nodes_cnt].hwirq = hwirq; + m->nodes[m->nodes_cnt].virq = virq; + m->nodes_cnt++; + spin_unlock(&m->lock); + + return SBI_OK; +} + +int sbi_virq_map_ensure_cap(u32 channel_id, u32 min_virq_cap) +{ + struct sbi_virq_map *m; + u32 new_nbits; + int rc = SBI_OK; + + if (min_virq_cap < 8U) + min_virq_cap = 8U; + + if (channel_id == 0) { + m = &g_virq_map; + if (!m->bmap) + return SBI_EINVAL; + } else { + m = virq_map_get(channel_id, true, min_virq_cap); + if (!m) + return SBI_ENOMEM; + } + + if (m->bmap_nbits >= min_virq_cap) + return SBI_OK; + + spin_lock(&m->lock); + new_nbits = m->bmap_nbits ? m->bmap_nbits : 8U; + while (new_nbits < min_virq_cap) + new_nbits <<= 1; + rc = bmap_grow(m, new_nbits); + spin_unlock(&m->lock); + + return rc; +} + +int sbi_virq_hwirq2virq(u32 channel_id, u32 chip_uid, u32 hwirq, + u32 *out_virq) +{ + u32 idx; + int rc; + struct sbi_virq_map *m; + + m = virq_map_get(channel_id, false, 0); + if (!m) + return SBI_ENOENT; + + spin_lock(&m->lock); + rc = forward_find_idx(m, chip_uid, hwirq, &idx); + if (!rc) + *out_virq = m->nodes[idx].virq; + spin_unlock(&m->lock); + + return rc; +} + +int sbi_virq_virq2hwirq(u32 channel_id, u32 virq, + u32 *out_chip_uid, u32 *out_hwirq) +{ + struct virq_entry *e; + struct sbi_virq_map *m; + + m = virq_map_get(channel_id, false, 0); + if (!m) + return SBI_EINVAL; + + spin_lock(&m->lock); + + if (virq >= m->bmap_nbits || + !bitmap_test(m->bmap, (int)virq)) { + spin_unlock(&m->lock); + return SBI_EINVAL; + } + + e = rev_get_existing(m, virq); + if (!e) { + spin_unlock(&m->lock); + return SBI_EINVAL; + } + + *out_chip_uid = e->chip_uid; + *out_hwirq = e->hwirq; + + spin_unlock(&m->lock); + + return SBI_OK; +} + +int sbi_virq_unmap_one(u32 virq) +{ + struct virq_entry *e; + u32 idx, last; + int rc; + struct sbi_virq_map *m = &g_virq_map; + + spin_lock(&m->lock); + + if (virq >= m->bmap_nbits || + !bitmap_test(m->bmap, (int)virq)) { + spin_unlock(&m->lock); + return SBI_EINVAL; + } + + e = rev_get_existing(m, virq); + if (!e) { + spin_unlock(&m->lock); + return SBI_EINVAL; + } + + /* find forward node corresponding to this virq (linear) */ + rc = SBI_ENOENT; + for (idx = 0; idx < m->nodes_cnt; idx++) { + if (m->nodes[idx].virq == virq) { + /* optionally also check endpoint matches e */ + rc = 0; + break; + } + } + if (rc) { + /* inconsistent state */ + spin_unlock(&m->lock); + return SBI_EINVAL; + } + + /* remove node: swap with last */ + last = m->nodes_cnt - 1U; + if (idx != last) + m->nodes[idx] = m->nodes[last]; + m->nodes_cnt--; + + /* clear reverse + free virq id */ + rev_clear(m, virq); + bmap_free_one(m, virq); + + spin_unlock(&m->lock); + + return SBI_OK; +} + +static void virq_map_uninit_one(struct sbi_virq_map *m) +{ + u32 i; + + spin_lock(&m->lock); + + /* free reverse chunks */ + if (m->chunks) { + for (i = 0; i < m->chunks_cap; i++) { + if (m->chunks[i]) + sbi_free(m->chunks[i]); + } + sbi_free(m->chunks); + m->chunks = NULL; + m->chunks_cap = 0; + } + + /* free forward vector */ + if (m->nodes) { + sbi_free(m->nodes); + m->nodes = NULL; + m->nodes_cnt = 0; + m->nodes_cap = 0; + } + + /* free bitmap */ + if (m->bmap) { + sbi_free(m->bmap); + m->bmap = NULL; + m->bmap_nbits = 0; + } + + spin_unlock(&m->lock); +} + +void sbi_virq_map_uninit(void) +{ + u32 i; + + virq_map_uninit_one(&g_virq_map); + + spin_lock(&g_virq_maps_lock); + for (i = 0; i < g_virq_maps_cnt; i++) + virq_map_uninit_one(&g_virq_maps[i].map); + if (g_virq_maps) { + sbi_free(g_virq_maps); + g_virq_maps = NULL; + g_virq_maps_cnt = 0; + g_virq_maps_cap = 0; + } + spin_unlock(&g_virq_maps_lock); +} + +int sbi_virq_enqueue(struct sbi_virq_courier_binding *c) +{ + struct sbi_domain_virq_state *st; + + if (!c->dom || c->virq == VIRQ_INVALID) + return SBI_EINVAL; + + st = domain_virq_thishart(c->dom); + if (!st) + return SBI_ENODEV; + + spin_lock(&st->lock); + if (q_full(st)) { + spin_unlock(&st->lock); + return SBI_ENOSPC; + } + + st->q[st->tail].virq = c->virq; + st->q[st->tail].channel_id = c->channel_id; + st->q[st->tail].chip = c->chip; + st->tail = (st->tail + 1) % VIRQ_QSIZE; + spin_unlock(&st->lock); + + return SBI_OK; +} + +u32 sbi_virq_pop_thishart(void) +{ + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + struct sbi_domain_virq_state *st; + u32 virq = VIRQ_INVALID; + + if (!dom) + return VIRQ_INVALID; + + st = domain_virq_thishart(dom); + if (!st) + return VIRQ_INVALID; + + spin_lock(&st->lock); + if (!q_empty(st)) { + virq = st->q[st->head].virq; + st->last_pop_virq = virq; + st->last_pop_channel_id = st->q[st->head].channel_id; + st->last_pop_chip = st->q[st->head].chip; + st->head = (st->head + 1) % VIRQ_QSIZE; + } else + virq = VIRQ_INVALID; + spin_unlock(&st->lock); + + if (virq == VIRQ_INVALID) { + if (sbi_irqchip_notify_smode_get()) + sbi_irqchip_notify_smode_clear(); + } + + return virq; +} + +void sbi_virq_complete_thishart(u32 virq) +{ + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + struct sbi_domain_virq_state *st; + u32 hwirq; + u32 chip_uid; + u32 channel_id; + struct sbi_irqchip_device *chip; + bool drained = false; + + if (virq == VIRQ_INVALID) + return; + + if (!dom) + return; + + st = domain_virq_thishart(dom); + if (!st) + return; + + spin_lock(&st->lock); + channel_id = st->last_pop_channel_id; + chip = st->last_pop_chip; + if (st->last_pop_virq == virq) { + st->last_pop_virq = 0; + st->last_pop_channel_id = 0; + st->last_pop_chip = NULL; + } + drained = q_empty(st); + spin_unlock(&st->lock); + + if (!chip) + return; + + sbi_virq_virq2hwirq(channel_id, virq, &chip_uid, &hwirq); + (void)chip_uid; + if (chip->hwirq_eoi) + chip->hwirq_eoi(chip, hwirq); + sbi_irqchip_unmask_hwirq(chip, hwirq); + + if (drained) { + if (sbi_irqchip_notify_smode_get()) + sbi_irqchip_notify_smode_clear(); + sbi_virq_return_to_prev_if_needed(); + } +} + +void sbi_virq_return_to_prev_if_needed(void) +{ + struct sbi_domain *dom = sbi_domain_thishart_ptr(); + struct sbi_domain_virq_state *st; + bool do_return = false; + + if (!dom) + return; + + st = domain_virq_thishart(dom); + if (!st) + return; + + spin_lock(&st->lock); + if (st->return_to_prev && q_empty(st)) { + st->return_to_prev = false; + do_return = true; + } + spin_unlock(&st->lock); + + if (!do_return) + return; + sbi_domain_context_request_return_to_prev(); +} + +int sbi_virq_courier_handler(u32 hwirq, void *opaque) +{ + struct sbi_virq_courier_ctx *ctx = + (struct sbi_virq_courier_ctx *)opaque; + struct sbi_domain *dom; + struct sbi_virq_courier_binding courier; + u32 channel_id = 0; + u32 virq = 0; + int rc; + struct sbi_domain *curr_dom; + + if (!ctx || !ctx->chip) + return SBI_EINVAL; + + /* Route purely by HWIRQ -> Domain/channel rules (from FDT). */ + rc = sbi_virq_route_lookup(hwirq, &dom, &channel_id); + if (rc || !dom) + return SBI_EINVAL; + + curr_dom = sbi_domain_thishart_ptr(); + + /* Allocate/Get a stable VIRQ for (chip_uid, hwirq). */ + rc = sbi_virq_map_one(channel_id, ctx->chip->id, hwirq, + false, 0, &virq); + if (rc) + return rc; + + /* + * Mask to avoid level-trigger storm before S-mode clears device source. + * S-mode will call sbi_virq_complete_thishart(virq) to unmask. + */ + sbi_irqchip_mask_hwirq(ctx->chip, hwirq); + + courier.dom = dom; + courier.chip = ctx->chip; + courier.channel_id = channel_id; + courier.virq = virq; + + rc = sbi_virq_enqueue(&courier); + if (rc) { + /* enqueue failed; re-enable to avoid deadlock */ + sbi_irqchip_unmask_hwirq(ctx->chip, hwirq); + return rc; + } + + /* + * Notify S-mode on notification rising edge. + * + * If the target is the current domain, operate on the live CSR. + * Otherwise, set the pending bit in the target domain context + * before switching (covers first-entry). After switching, set the + * live CSR only if needed (covers already-initialized targets). + */ + if (dom != curr_dom) { + (void)sbi_domain_context_pending_notify_smode( + dom, current_hartindex()); + + /* Mark return_to_prev for VIRQ-driven domain switch. */ + virq_set_domain_return_flag(dom, true); + rc = sbi_domain_context_enter(dom); + if (rc) { + /* Switch failed; do not defer EOI */ + sbi_irqchip_unmask_hwirq(ctx->chip, hwirq); + if (ctx->chip->hwirq_eoi) + ctx->chip->hwirq_eoi(ctx->chip, hwirq); + return SBI_OK; + } + + /* + * If the domain was already initialized, + * sbi_domain_context_enter() returns and CSR_SIP reflect + * dom_ctx->sip. For robustness, set the live notify bit if it + * is still clear. + */ + if (!sbi_irqchip_notify_smode_get()) { + rc = sbi_irqchip_notify_smode_set(); + if (rc) { + /* + * notification failed; re-enable to avoid + * deadlock + */ + sbi_irqchip_unmask_hwirq(ctx->chip, hwirq); + return rc; + } + } + } else if (!sbi_irqchip_notify_smode_get()) { + rc = sbi_irqchip_notify_smode_set(); + if (rc) { + /* notification failed; re-enable to avoid deadlock */ + sbi_irqchip_unmask_hwirq(ctx->chip, hwirq); + return rc; + } + } + + /* + * Return SBI_EALREADY to defer EOI until VIRQ COMPLETE so S-mode + * notification can be delivered to the target domain. + */ + return SBI_EALREADY; +} + +int sbi_virq_domain_init(struct sbi_domain *dom) +{ + struct sbi_domain_virq_priv *p; + u32 i, k, nharts, st_count; + struct sbi_domain_virq_state *st_base; + size_t alloc_size; + + if (!dom) + return SBI_EINVAL; + + if (dom->virq_priv) + return SBI_OK; + + nharts = sbi_virq_platform_hart_count(); + st_count = dom->possible_harts ? + (u32)sbi_hartmask_weight(dom->possible_harts) : nharts; + + alloc_size = sizeof(*p) + + nharts * sizeof(p->st_by_hart[0]) + + st_count * sizeof(struct sbi_domain_virq_state); + p = sbi_zalloc(alloc_size); + if (!p) + return SBI_ENOMEM; + + p->nharts = nharts; + p->st_count = st_count; + st_base = (struct sbi_domain_virq_state *)(p->st_by_hart + nharts); + + if (!dom->possible_harts) { + for (i = 0; i < nharts; i++) { + p->st_by_hart[i] = &st_base[i]; + virq_state_init(p->st_by_hart[i]); + } + } else { + for (i = 0; i < nharts; i++) + p->st_by_hart[i] = NULL; + k = 0; + sbi_hartmask_for_each_hartindex(i, dom->possible_harts) { + if (k >= st_count) + break; + p->st_by_hart[i] = &st_base[k++]; + virq_state_init(p->st_by_hart[i]); + } + } + dom->virq_priv = p; + + return SBI_OK; +} + +void sbi_virq_domain_exit(struct sbi_domain *dom) +{ + if (!dom || !dom->virq_priv) + return; + + sbi_free(dom->virq_priv); + dom->virq_priv = NULL; +} + +int sbi_virq_init(u32 init_virq_cap) +{ + int rc = SBI_OK; + + if (g_virq_inited) + return SBI_EALREADY; + + rc = sbi_virq_map_init(0, init_virq_cap); + if (rc) + return rc; + + SPIN_LOCK_INIT(g_virq_maps_lock); + SPIN_LOCK_INIT(g_router.lock); + sbi_virq_route_reset(); + g_virq_inited = true; + return rc; +} + +bool sbi_virq_is_inited(void) +{ + return g_virq_inited; +} -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:50 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:50 -0400 Subject: [PATCH 04/10] lib: sbi: Add VIRQ ecall extension In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-5-raymondmaoca@gmail.com> From: Raymond Mao Add vendor SBI extension ecall for VIRQ. This allows S-mode payload to pop/complete the next pending VIRQ has couried into the current domain. Signed-off-by: Raymond Mao --- include/sbi/sbi_ecall_interface.h | 26 ++++++++++++++ lib/sbi/Kconfig | 10 ++++++ lib/sbi/objects.mk | 3 ++ lib/sbi/sbi_ecall_virq.c | 56 +++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 lib/sbi/sbi_ecall_virq.c diff --git a/include/sbi/sbi_ecall_interface.h b/include/sbi/sbi_ecall_interface.h index 9a776f79..37937a0c 100644 --- a/include/sbi/sbi_ecall_interface.h +++ b/include/sbi/sbi_ecall_interface.h @@ -126,6 +126,32 @@ #define SBI_EXT_FWFT_SET 0x0 #define SBI_EXT_FWFT_GET 0x1 +#ifdef CONFIG_SBI_ECALL_VIRQ + +/* Vendor extension base range is defined by the SBI spec. Choose a private ID. */ +#define SBI_EXT_VIRQ 0x0900524d + +/* Function IDs for SBI_EXT_VIRQ */ +#define SBI_EXT_VIRQ_POP 0 +#define SBI_EXT_VIRQ_COMPLETE 1 + +/* + * SBI_EXT_VIRQ_POP + * Returns: + * a0: SBI error code (0 for success) + * a1: next pending VIRQ (VIRQ_INVALID if none pending) + */ + +/* + * SBI_EXT_VIRQ_COMPLETE + * Input: + * a0: VIRQ to complete + * Returns: + * a0: SBI error code (0 for success) + */ + +#endif + enum sbi_fwft_feature_t { SBI_FWFT_MISALIGNED_EXC_DELEG = 0x0, SBI_FWFT_LANDING_PAD = 0x1, diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig index c6cc04bc..cbb74640 100644 --- a/lib/sbi/Kconfig +++ b/lib/sbi/Kconfig @@ -69,4 +69,14 @@ config SBI_ECALL_SSE config SBI_ECALL_MPXY bool "MPXY extension" default y + +config SBI_ECALL_VIRQ + bool "VIRQ extension" + default y + help + Enable the OpenSBI VIRQ ecall extension. + This extension allows an S-mode payload to pop a pending + virtual interrupt and complete its deferred host interrupt + handling after the payload has consumed the event. + endmenu diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 184bf173..ea816e92 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -64,6 +64,9 @@ libsbi-objs-$(CONFIG_SBI_ECALL_SSE) += sbi_ecall_sse.o carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_MPXY) += ecall_mpxy libsbi-objs-$(CONFIG_SBI_ECALL_MPXY) += sbi_ecall_mpxy.o +carray-sbi_ecall_exts-$(CONFIG_SBI_ECALL_VIRQ) += ecall_virq +libsbi-objs-$(CONFIG_SBI_ECALL_VIRQ) += sbi_ecall_virq.o + libsbi-objs-y += sbi_bitmap.o libsbi-objs-y += sbi_bitops.o libsbi-objs-y += sbi_console.o diff --git a/lib/sbi/sbi_ecall_virq.c b/lib/sbi/sbi_ecall_virq.c new file mode 100644 index 00000000..a84a83d7 --- /dev/null +++ b/lib/sbi/sbi_ecall_virq.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026 RISCstar Solutions. + * + * Author: Raymond Mao + */ + +#include +#include +#include +#include +#include +#include + +static int sbi_ecall_virq_handler(unsigned long extid, + unsigned long funcid, + struct sbi_trap_regs *regs, + struct sbi_ecall_return *out) +{ + (void)extid; + + sbi_printf("[ECALL VIRQ] VIRQ ecall handler, funcid: %ld\n", funcid); + + switch (funcid) { + case SBI_EXT_VIRQ_POP: + out->value = (unsigned long)sbi_virq_pop_thishart(); + return SBI_OK; + case SBI_EXT_VIRQ_COMPLETE: + u32 virq = (u32)regs->a0; + + sbi_virq_complete_thishart(virq); + regs->a0 = 0; + return SBI_OK; + default: + return SBI_ENOTSUPP; + } +} + +struct sbi_ecall_extension ecall_virq; + +static int sbi_ecall_virq_register_extensions(void) +{ + int ret; + + ret = sbi_ecall_register_extension(&ecall_virq); + sbi_printf("[ECALL VIRQ] register VIRQ ecall extensions, ret=%d\n", ret); + return ret; +} + +struct sbi_ecall_extension ecall_virq = { + .name = "virq", + .extid_start = SBI_EXT_VIRQ, + .extid_end = SBI_EXT_VIRQ, + .register_extensions = sbi_ecall_virq_register_extensions, + .handle = sbi_ecall_virq_handler, +}; -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:51 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:51 -0400 Subject: [PATCH 05/10] lib: sbi: domain: add domain lookup by name In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-6-raymondmaoca@gmail.com> From: Raymond Mao Provide a helper to resolve a domain by its DT node name, used by sysirq DT parsing. Signed-off-by: Raymond Mao --- include/sbi/sbi_domain.h | 3 +++ lib/sbi/sbi_domain.c | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/sbi/sbi_domain.h b/include/sbi/sbi_domain.h index 7e288cd8..b7267da8 100644 --- a/include/sbi/sbi_domain.h +++ b/include/sbi/sbi_domain.h @@ -232,6 +232,9 @@ struct sbi_domain *sbi_hartindex_to_domain(u32 hartindex); /** Update HART local pointer to point to specified domain */ void sbi_update_hartindex_to_domain(u32 hartindex, struct sbi_domain *dom); +/** Find domain by DT node name (domain name) */ +struct sbi_domain *sbi_domain_find_by_name(const char *name); + /** Get pointer to sbi_domain for current HART */ #define sbi_domain_thishart_ptr() \ sbi_hartindex_to_domain(current_hartindex()) diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c index 2a846eea..c33f2b3c 100644 --- a/lib/sbi/sbi_domain.c +++ b/lib/sbi/sbi_domain.c @@ -60,6 +60,21 @@ void sbi_update_hartindex_to_domain(u32 hartindex, struct sbi_domain *dom) sbi_scratch_write_type(scratch, void *, domain_hart_ptr_offset, dom); } +struct sbi_domain *sbi_domain_find_by_name(const char *name) +{ + struct sbi_domain *dom; + + if (!name) + return NULL; + + sbi_domain_for_each(dom) { + if (!sbi_strncmp(dom->name, name, sizeof(dom->name))) + return dom; + } + + return NULL; +} + bool sbi_domain_is_assigned_hart(const struct sbi_domain *dom, u32 hartindex) { bool ret; -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:52 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:52 -0400 Subject: [PATCH 06/10] lib: utils: fdt: parse sysirq routing from DT In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-7-raymondmaoca@gmail.com> From: Raymond Mao Init VIRQ and parse mpxy-sysirq nodes under /chosen/opensbi-domains, pre-size per-channel VIRQ maps, and add HWIRQ routes from interrupts-extended. Mark sysirq domains to allow SEIP-based VIRQ notification. Signed-off-by: Raymond Mao --- include/sbi_utils/fdt/fdt_helper.h | 17 +++++ lib/utils/fdt/fdt_domain.c | 119 ++++++++++++++++++++++++++++- lib/utils/fdt/fdt_helper.c | 49 ++++++++++++ 3 files changed, 183 insertions(+), 2 deletions(-) diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h index 04c850cc..e49a5bca 100644 --- a/include/sbi_utils/fdt/fdt_helper.h +++ b/include/sbi_utils/fdt/fdt_helper.h @@ -12,6 +12,7 @@ #include #include +#include struct fdt_match { const char *compatible; @@ -38,6 +39,22 @@ int fdt_parse_phandle_with_args(const void *fdt, int nodeoff, const char *prop, const char *cells_prop, int index, struct fdt_phandle_args *out_args); +/* + * Parse one entry from "interrupts-extended" and return irqchip device, + * hwirq, and optional flags. + * + * @index: entry index within interrupts-extended + * @out_flags_count: in/out, on input capacity of out_flags array; + * on output number of flags returned (or total flags + * if out_flags is NULL). + */ +int fdt_parse_interrupts_extended_entry(const void *fdt, int nodeoff, + int index, + struct sbi_irqchip_device **out_chip, + u32 *out_hwirq, + u32 *out_flags, + u32 *out_flags_count); + int fdt_get_node_addr_size(const void *fdt, int node, int index, uint64_t *addr, uint64_t *size); diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c index b2fa8633..4a75f25a 100644 --- a/lib/utils/fdt/fdt_domain.c +++ b/lib/utils/fdt/fdt_domain.c @@ -10,11 +10,14 @@ #include #include +#include #include #include #include #include +#include #include +#include #include #include @@ -304,6 +307,7 @@ static int __fdt_parse_region(const void *fdt, int domain_offset, return 0; } + static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) { u32 val32; @@ -511,6 +515,113 @@ fail_free_domain: return err; } +static int __fdt_parse_mpxy_sysirq_node(const void *fdt, int nodeoff) +{ + const fdt32_t *val; + int len, rc, doff; + u32 channel_id; + u32 index; + struct sbi_domain *dom; + + if (!fdt || nodeoff < 0) + return SBI_EINVAL; + + val = fdt_getprop(fdt, nodeoff, "opensbi,mpxy-channel-id", &len); + if (!val || len < (int)sizeof(fdt32_t)) { + sbi_printf("[SYSIRQ] missing opensbi,mpxy-channel-id\n"); + return SBI_EINVAL; + } + channel_id = fdt32_to_cpu(*val); + + val = fdt_getprop(fdt, nodeoff, "opensbi,domain", &len); + if (!val || len < (int)sizeof(fdt32_t)) { + sbi_printf("[SYSIRQ] missing opensbi,domain\n"); + return SBI_EINVAL; + } + + doff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val)); + if (doff < 0) + return doff; + + dom = sbi_domain_find_by_name(fdt_get_name(fdt, doff, NULL)); + if (!dom) { + sbi_printf("[SYSIRQ] domain not found for node %s\n", + fdt_get_name(fdt, doff, NULL)); + return SBI_ENOENT; + } + dom->virq_seip_notify = true; + + /* Pre-allocate VIRQ map based on interrupts-extended count */ + for (index = 0; ; index++) { + rc = fdt_parse_interrupts_extended_entry(fdt, nodeoff, index, + NULL, NULL, + NULL, NULL); + if (rc == SBI_ENOENT) + break; + if (rc) + return rc; + } + + if (!sbi_virq_is_inited()) + rc = sbi_virq_init(index); + else + rc = sbi_virq_map_ensure_cap(channel_id, index); + if (rc) + return rc; + + for (index = 0; ; index++) { + struct sbi_irqchip_device *chip = NULL; + u32 hwirq = 0; + + rc = fdt_parse_interrupts_extended_entry(fdt, nodeoff, index, + &chip, &hwirq, + NULL, NULL); + if (rc == SBI_ENOENT) + break; + if (rc) + return rc; + if (!chip) + return SBI_ENODEV; + + rc = sbi_virq_map_set(channel_id, chip->id, hwirq, index); + if (rc) + return rc; + + rc = sbi_virq_route_add(dom, hwirq, channel_id); + if (rc) + return rc; + } + + return SBI_OK; +} + +static int __fdt_parse_mpxy_sysirq_nodes(const void *fdt) +{ + int poffset, noff, rc; + + if (!fdt) + return SBI_EINVAL; + + poffset = fdt_path_offset(fdt, "/chosen"); + if (poffset < 0) + return 0; + poffset = fdt_node_offset_by_compatible(fdt, poffset, + "opensbi,domain,config"); + if (poffset < 0) + return 0; + + fdt_for_each_subnode(noff, fdt, poffset) { + if (fdt_node_check_compatible(fdt, noff, + "opensbi,mpxy-sysirq")) + continue; + rc = __fdt_parse_mpxy_sysirq_node(fdt, noff); + if (rc) + return rc; + } + + return 0; +} + int fdt_domains_populate(const void *fdt) { const u32 *val; @@ -550,6 +661,10 @@ int fdt_domains_populate(const void *fdt) } /* Iterate over each domain in FDT and populate details */ - return fdt_iterate_each_domain_ro(fdt, &cold_domain_offset, - __fdt_parse_domain); + err = fdt_iterate_each_domain_ro(fdt, &cold_domain_offset, + __fdt_parse_domain); + if (err) + return err; + + return __fdt_parse_mpxy_sysirq_nodes(fdt); } diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c index b57eae1a..3d7c4eec 100644 --- a/lib/utils/fdt/fdt_helper.c +++ b/lib/utils/fdt/fdt_helper.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,54 @@ int fdt_parse_phandle_with_args(const void *fdt, int nodeoff, return SBI_ENOENT; } +int fdt_parse_interrupts_extended_entry(const void *fdt, int nodeoff, + int index, + struct sbi_irqchip_device **out_chip, + u32 *out_hwirq, + u32 *out_flags, + u32 *out_flags_count) +{ + struct fdt_phandle_args args; + struct sbi_irqchip_device *chip; + u32 flags_cap = 0, flags_cnt = 0; + int rc, i; + + if (!fdt || nodeoff < 0) + return SBI_EINVAL; + + rc = fdt_parse_phandle_with_args(fdt, nodeoff, "interrupts-extended", + "#interrupt-cells", index, &args); + if (rc) + return rc; + + if (args.args_count < 1) + return SBI_EINVAL; + + if (out_hwirq) + *out_hwirq = args.args[0]; + + if (out_flags_count) { + flags_cap = *out_flags_count; + flags_cnt = (args.args_count > 1) ? (args.args_count - 1) : 0; + if (out_flags && flags_cap < flags_cnt) + flags_cnt = flags_cap; + if (out_flags) { + for (i = 0; i < (int)flags_cnt; i++) + out_flags[i] = args.args[i + 1]; + } + *out_flags_count = flags_cnt; + } + + if (out_chip) { + chip = sbi_irqchip_find_device((u32)args.node_offset); + if (!chip) + return SBI_ENODEV; + *out_chip = chip; + } + + return 0; +} + static int fdt_translate_address(const void *fdt, uint64_t reg, int parent, uint64_t *addr) { -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:53 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:53 -0400 Subject: [PATCH 07/10] lib: utils: irqchip: derive APLIC targets from sysirq nodes In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-8-raymondmaoca@gmail.com> From: Raymond Mao Derive a per-HWIRQ target hartindex map from each sysirq node's target domain boot-hart, and store the result in aplic_data. This is used for setting IDC target per HWIRQ under irqchip driver instead of hardcoding to a specific target. Signed-off-by: Raymond Mao --- include/sbi_utils/irqchip/aplic.h | 1 + lib/utils/irqchip/fdt_irqchip_aplic.c | 103 ++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/include/sbi_utils/irqchip/aplic.h b/include/sbi_utils/irqchip/aplic.h index 3461d1c7..d6e088f8 100644 --- a/include/sbi_utils/irqchip/aplic.h +++ b/include/sbi_utils/irqchip/aplic.h @@ -47,6 +47,7 @@ struct aplic_data { struct aplic_msicfg_data msicfg_smode; struct aplic_delegate_data delegate[APLIC_MAX_DELEGATE]; u32 *idc_map; + u32 *hwirq_target_hartindex; }; int aplic_cold_irqchip_init(struct aplic_data *aplic); diff --git a/lib/utils/irqchip/fdt_irqchip_aplic.c b/lib/utils/irqchip/fdt_irqchip_aplic.c index f9b567f5..cbbdf8d8 100644 --- a/lib/utils/irqchip/fdt_irqchip_aplic.c +++ b/lib/utils/irqchip/fdt_irqchip_aplic.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,88 @@ static int irqchip_aplic_update_idc_map(const void *fdt, int nodeoff, return 0; } +static u32 irqchip_aplic_domain_boot_hartindex(void *fdt, int domain_offset) +{ + int len, cpu_offset; + const fdt32_t *val; + + val = fdt_getprop(fdt, domain_offset, "boot-hart", &len); + if (val && len >= 4) { + cpu_offset = fdt_node_offset_by_phandle(fdt, + fdt32_to_cpu(*val)); + if (cpu_offset >= 0) { + u32 hartid; + + if (!fdt_parse_hart_id(fdt, cpu_offset, &hartid)) { + u32 hidx = sbi_hartid_to_hartindex(hartid); + + if (sbi_hartindex_valid(hidx)) + return hidx; + } + } + } + + return current_hartindex(); +} + +static void irqchip_aplic_fill_hwirq_targets_from_sysirq(const void *fdt, + int aplic_nodeoff, + struct aplic_data *pd) +{ + int chosen_off, nodeoff; + int len, rc, index; + const fdt32_t *val; + u32 boot_hartindex; + + if (!fdt || aplic_nodeoff < 0 || !pd || !pd->hwirq_target_hartindex) + return; + + chosen_off = fdt_path_offset(fdt, "/chosen/opensbi-domains"); + if (chosen_off < 0) + return; + + fdt_for_each_subnode(nodeoff, fdt, chosen_off) { + if (fdt_node_check_compatible(fdt, nodeoff, + "opensbi,mpxy-sysirq")) + continue; + + val = fdt_getprop(fdt, nodeoff, "opensbi,domain", &len); + if (!val || len < 4) + continue; + + rc = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val)); + if (rc < 0) + continue; + + boot_hartindex = irqchip_aplic_domain_boot_hartindex((void *)fdt, + rc); + + for (index = 0; ; index++) { + struct fdt_phandle_args args; + + rc = fdt_parse_phandle_with_args(fdt, nodeoff, + "interrupts-extended", + "#interrupt-cells", + index, &args); + if (rc) + break; + if (args.args_count < 1) + continue; + if (args.node_offset != aplic_nodeoff) + continue; + + u32 hwirq = args.args[0]; + + if (!hwirq || hwirq > pd->num_source) + continue; + + if (pd->hwirq_target_hartindex[hwirq] == -1U) + pd->hwirq_target_hartindex[hwirq] = + boot_hartindex; + } + } +} + static int irqchip_aplic_cold_init(const void *fdt, int nodeoff, const struct fdt_match *match) { @@ -85,6 +168,24 @@ static int irqchip_aplic_cold_init(const void *fdt, int nodeoff, goto fail_free_idc_map; } + /* Precompute target hartindex per HWIRQ from DT. */ + if (pd->targets_mmode) { + u32 i; + + pd->hwirq_target_hartindex = + sbi_zalloc(sizeof(*pd->hwirq_target_hartindex) * + (pd->num_source + 1)); + if (!pd->hwirq_target_hartindex) { + rc = SBI_ENOMEM; + goto fail_free_idc_map; + } + + for (i = 0; i <= pd->num_source; i++) + pd->hwirq_target_hartindex[i] = -1U; + + irqchip_aplic_fill_hwirq_targets_from_sysirq(fdt, nodeoff, pd); + } + rc = aplic_cold_irqchip_init(pd); if (rc) goto fail_free_idc_map; @@ -93,6 +194,8 @@ static int irqchip_aplic_cold_init(const void *fdt, int nodeoff, return 0; fail_free_idc_map: + if (pd->hwirq_target_hartindex) + sbi_free(pd->hwirq_target_hartindex); if (pd->num_idc) sbi_free(pd->idc_map); fail_free_data: -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:54 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:54 -0400 Subject: [PATCH 08/10] lib: irqchip: support deferred completion and per-HWIRQ APLIC targets In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-9-raymondmaoca@gmail.com> From: Raymond Mao Lazily resolve the active irqchip provider for each hart when the scratch-local provider is not yet populated. Program APLIC target IDCs from the precomputed per-HWIRQ hart map, and treat SBI_EALREADY as deferred completion so the normal process path does not complete an interrupt that will be finished later by the VIRQ flow. Signed-off-by: Raymond Mao --- lib/sbi/sbi_irqchip.c | 21 ++++++++++++++ lib/utils/irqchip/aplic.c | 60 ++++++++++++++++++++++++++++++--------- 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c index e022d534..45b2992f 100644 --- a/lib/sbi/sbi_irqchip.c +++ b/lib/sbi/sbi_irqchip.c @@ -45,11 +45,28 @@ struct sbi_irqchip_hart_data { static unsigned long irqchip_hart_data_off; static SBI_LIST_HEAD(irqchip_list); +static struct sbi_irqchip_device *sbi_irqchip_find_hart_device(u32 hartindex) +{ + struct sbi_irqchip_device *chip; + + sbi_list_for_each_entry(chip, &irqchip_list, node) { + if (!chip->process_hwirqs) + continue; + if (!sbi_hartmask_test_hartindex(hartindex, &chip->target_harts)) + continue; + return chip; + } + + return NULL; +} + int sbi_irqchip_process(void) { struct sbi_irqchip_hart_data *hd; hd = sbi_scratch_thishart_offset_ptr(irqchip_hart_data_off); + if (hd && !hd->chip) + hd->chip = sbi_irqchip_find_hart_device(current_hartindex()); if (!hd || !hd->chip || !hd->chip->process_hwirqs) return SBI_ENODEV; @@ -306,6 +323,8 @@ int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot) } hd = sbi_scratch_thishart_offset_ptr(irqchip_hart_data_off); + if (hd && !hd->chip) + hd->chip = sbi_irqchip_find_hart_device(current_hartindex()); if (hd && hd->chip && hd->chip->process_hwirqs) csr_set(CSR_MIE, MIP_MEIP); @@ -317,6 +336,8 @@ void sbi_irqchip_exit(struct sbi_scratch *scratch) struct sbi_irqchip_hart_data *hd; hd = sbi_scratch_thishart_offset_ptr(irqchip_hart_data_off); + if (hd && !hd->chip) + hd->chip = sbi_irqchip_find_hart_device(current_hartindex()); if (hd && hd->chip && hd->chip->process_hwirqs) csr_clear(CSR_MIE, MIP_MEIP); } diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c index 82efdb71..77743685 100644 --- a/lib/utils/irqchip/aplic.c +++ b/lib/utils/irqchip/aplic.c @@ -306,6 +306,8 @@ static inline struct aplic_data *aplic_irqchip_to_data(struct sbi_irqchip_device return container_of(chip, struct aplic_data, irqchip); } +static bool aplic_mmode_direct(const struct aplic_data *aplic); + static bool aplic_hwirq_delegated(const struct aplic_data *aplic, u32 hwirq) { u32 i; @@ -441,10 +443,49 @@ static int aplic_hwirq_claim(struct sbi_irqchip_device *chip, u32 *hwirq) return SBI_OK; } +static void aplic_set_target(struct aplic_data *aplic, u32 hwirq, u32 idc_index) +{ + unsigned long idc; + + if (!aplic->addr || !hwirq || hwirq > aplic->num_source) + return; + if (aplic->num_idc <= idc_index) + return; + + idc = aplic->addr + APLIC_IDC_BASE + + (unsigned long)idc_index * APLIC_IDC_SIZE; + + writel((idc_index << APLIC_TARGET_HART_IDX_SHIFT) | + APLIC_DEFAULT_PRIORITY, + (void *)(aplic->addr + APLIC_TARGET_BASE + (hwirq - 1) * 4)); + + /* IDC delivery */ + writel(APLIC_ENABLE_IDELIVERY, (void *)(idc + APLIC_IDC_IDELIVERY)); + writel(APLIC_ENABLE_ITHRESHOLD, (void *)(idc + APLIC_IDC_ITHRESHOLD)); +} + +static int aplic_hwirq_setup_target_idc_index(struct aplic_data *aplic, + struct sbi_irqchip_device *chip, + u32 hwirq) +{ + u32 hartindex; + int idc_index; + + if (aplic->hwirq_target_hartindex) { + hartindex = aplic->hwirq_target_hartindex[hwirq]; + if (hartindex != -1U) { + idc_index = aplic_hartindex_to_idc_index(aplic, hartindex); + if (idc_index >= 0) + return idc_index; + } + } + + return aplic_hwirq_target_idc_index(chip); +} + static int aplic_hwirq_setup(struct sbi_irqchip_device *chip, u32 hwirq) { struct aplic_data *aplic = aplic_irqchip_to_data(chip); - unsigned long idc; int idc_index; if (!hwirq || hwirq > aplic->num_source) @@ -454,29 +495,19 @@ static int aplic_hwirq_setup(struct sbi_irqchip_device *chip, u32 hwirq) if (aplic_hwirq_delegated(aplic, hwirq)) return SBI_ENOTSUPP; - idc_index = aplic_hwirq_target_idc_index(chip); + idc_index = aplic_hwirq_setup_target_idc_index(aplic, chip, hwirq); if (idc_index < 0) return idc_index; - idc = aplic->addr + APLIC_IDC_BASE + idc_index * APLIC_IDC_SIZE; - /* APLIC: sourcecfg/target/enable */ writel(APLIC_SOURCECFG_SM_LEVEL_HIGH, (void *)(aplic->addr + APLIC_SOURCECFG_BASE + (hwirq - 1) * 4)); - - writel(((u32)idc_index << APLIC_TARGET_HART_IDX_SHIFT) | - APLIC_DEFAULT_PRIORITY, - (void *)(aplic->addr + APLIC_TARGET_BASE + (hwirq - 1) * 4)); - + aplic_set_target(aplic, hwirq, (u32)idc_index); writel(hwirq, (void *)(aplic->addr + APLIC_SETIENUM)); /* Direct mode for aia=aplic: DM=0 => don't set DM bit */ writel(aplic_domaincfg_value(), (void *)(aplic->addr + APLIC_DOMAINCFG)); - /* IDC delivery */ - writel(APLIC_ENABLE_IDELIVERY, (void *)(idc + APLIC_IDC_IDELIVERY)); - writel(APLIC_ENABLE_ITHRESHOLD, (void *)(idc + APLIC_IDC_ITHRESHOLD)); - return SBI_OK; } @@ -502,6 +533,9 @@ static int aplic_process_hwirqs(struct sbi_irqchip_device *chip) } rc = sbi_irqchip_process_hwirq(chip, hwirq); + /* Deferred completion paths consume the IRQ without EOI here. */ + if (rc == SBI_EALREADY) + return SBI_OK; if (rc) return rc; } -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:55 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:55 -0400 Subject: [PATCH 09/10] lib: sbi: domain: ensure boot_hartid is assigned In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-10-raymondmaoca@gmail.com> From: Raymond Mao When boot_hartid points to a hart that is not in the domain's assigned hartmask (e.g. due to cold boot hart differences), the domain startup can skip starting the intended boot hart, leading to intermittent Linux boot failures. Scan the assigned hartmask and pick the first available hartid to guarantee a valid boot target. Signed-off-by: Raymond Mao --- lib/sbi/sbi_domain.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c index c33f2b3c..ac563615 100644 --- a/lib/sbi/sbi_domain.c +++ b/lib/sbi/sbi_domain.c @@ -819,6 +819,30 @@ int sbi_domain_startup(struct sbi_scratch *scratch, u32 cold_hartid) /* Startup boot HART of domains */ sbi_domain_for_each(dom) { + u32 boot_hartindex = sbi_hartid_to_hartindex(dom->boot_hartid); + bool boot_assigned = false; + + if (sbi_hartindex_valid(boot_hartindex)) { + spin_lock(&dom->assigned_harts_lock); + boot_assigned = sbi_hartmask_test_hartindex( + boot_hartindex, &dom->assigned_harts); + spin_unlock(&dom->assigned_harts_lock); + } + + if (!boot_assigned) { + u32 new_hartid = -1U; + + spin_lock(&dom->assigned_harts_lock); + sbi_hartmask_for_each_hartindex(dhart, &dom->assigned_harts) { + new_hartid = sbi_hartindex_to_hartid(dhart); + break; + } + spin_unlock(&dom->assigned_harts_lock); + + if (new_hartid != -1U) + dom->boot_hartid = new_hartid; + } + /* Domain boot HART index */ dhart = sbi_hartid_to_hartindex(dom->boot_hartid); -- 2.25.1 From raymondmaoca at gmail.com Thu May 14 15:57:56 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Thu, 14 May 2026 18:57:56 -0400 Subject: [PATCH 10/10] docs: domain: document sysirq VIRQ mapping and routing rules In-Reply-To: <20260514225756.2255758-1-raymondmaoca@gmail.com> References: <20260514225756.2255758-1-raymondmaoca@gmail.com> Message-ID: <20260514225756.2255758-11-raymondmaoca@gmail.com> From: Raymond Mao Document the DT binding semantics for opensbi,mpxy-sysirq nodes, including how interrupts-extended entry order determines per-channel VIRQ numbers and how opensbi,domain selects the destination OpenSBI domain. Add a rpmi_sysirq_intc example under /chosen/opensbi-domains. Signed-off-by: Raymond Mao --- docs/domain_support.md | 63 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/docs/domain_support.md b/docs/domain_support.md index 93186c4a..8d474e50 100644 --- a/docs/domain_support.md +++ b/docs/domain_support.md @@ -198,6 +198,48 @@ The DT properties of a domain instance DT node are as follows: * **system-suspend-allowed** (Optional) - A boolean flag representing whether the domain instance is allowed to do system suspend. +### Domain SysIRQ / VIRQ Routing Node + +The domain configuration DT node can also contain sysirq routing nodes for +describing how a physical interrupt source should be mapped to a VIRQ and +routed to a target domain. In local DTS overlays this node is often labeled +`rpmi_sysirq_intc`, but OpenSBI matches it by compatible string rather than +by node name or label. + +The DT properties of a domain sysirq routing DT node are as follows: + +* **compatible** (Mandatory) - The compatible string of the sysirq routing + node. This DT property should have value *"opensbi,mpxy-sysirq"* +* **interrupt-controller** (Mandatory) - Marks the node as an interrupt + controller for child references. +* **#interrupt-cells** (Mandatory) - Number of cells used by this interrupt + controller. Current examples use value **<1>**. +* **interrupts-extended** (Mandatory) - The list of routed physical interrupt + sources. Each entry is `<&irqchip hwirq flags>`. OpenSBI interprets the + entry order as the VIRQ number within the selected MPXY channel, starting + from zero: + VIRQ 0 maps to entry 0, VIRQ 1 maps to entry 1, and so on. +* **opensbi,mpxy-channel-id** (Mandatory) - The MPXY channel identifier used + as the VIRQ number space for this sysirq node. +* **opensbi,domain** (Mandatory) - Phandle to the target domain instance DT + node. All physical interrupts listed in **interrupts-extended** are routed + to this domain after being mapped to VIRQs. + +The resulting VIRQ rule for a sysirq node is: + +* **mapping** - `(irqchip phandle, hwirq, entry index)` from + **interrupts-extended** becomes `(channel-id, virq)` +* **routing** - `opensbi,domain` selects the destination OpenSBI domain for + all VIRQs created from that sysirq node + +In other words, a node labeled `rpmi_sysirq_intc` typically means: + +* the physical interrupt source is described by one **interrupts-extended** + entry +* the VIRQ number is the position of that entry in the list +* the VIRQ namespace is selected by **opensbi,mpxy-channel-id** +* the destination domain is selected by **opensbi,domain** + ### Assigning HART To Domain Instance By default, all HARTs are assigned to **the ROOT domain**. The OpenSBI @@ -270,6 +312,27 @@ be done: possible-harts = <&cpu1 &cpu2 &cpu3 &cpu4>; regions = <&tmem 0x0>, <&tuart 0x0>, <&allmem 0x3f>; }; + + rpmi_sysirq_intc: interrupt-controller { + compatible = "opensbi,mpxy-sysirq"; + interrupt-controller; + #interrupt-cells = <1>; + + /* + * VIRQ numbers are assigned from zero in + * interrupts-extended order. + */ + interrupts-extended = + <&aplic_m 10 4>, /* VIRQ 0 */ + <&aplic_m 20 4>, /* VIRQ 1 */ + <&aplic_m 21 4>; /* VIRQ 2 */ + + /* Select the VIRQ namespace / MPXY channel. */ + opensbi,mpxy-channel-id = <4>; + + /* Route all VIRQs from this node to udomain. */ + opensbi,domain = <&udomain>; + }; }; }; -- 2.25.1 From dave.patel at riscstar.com Thu May 14 22:42:04 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Fri, 15 May 2026 06:42:04 +0100 Subject: [PATCH v5 0/3] Add eager FP and RISC-V vector context switching support Message-ID: <20260515054208.151045-1-dave.patel@riscstar.com> Hi Anup, Thank you for taking out time and reviewing the patches. I have added new patch to add Extension V, F and D and their checks before context swtich The changes include: - Alter sbi_vector.c to use csr_read and csr_write - Added another patch for Extension check. I have covered all your comments, please can you have a look and let me know. Thanks and Regards, Dave Signed-off-by: Dave Patel From dave.patel at riscstar.com Thu May 14 22:42:05 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Fri, 15 May 2026 06:42:05 +0100 Subject: [PATCH v5 1/4] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260515054208.151045-1-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> Message-ID: <20260515054208.151045-2-dave.patel@riscstar.com> From: Dave Patel Eager context switch: Add support for saving and restoring RISC-V vector extension state in OpenSBI. This introduces a per-hart vector context structure and helper routines to perform full context save and restore. The vector context includes vcsr CSRs along with storage for all 32 vector registers. The register state is saved and restored using byte-wise vector load/store instructions (vs8r/vl8r). The implementation follows an eager context switching model where the entire vector state is saved and restored on every context switch. This provides a simple and deterministic mechanism without requiring lazy trap-based management. Signed-off-by: Dave Patel --- include/sbi/sbi_vector.h | 28 ++++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_vector.c | 109 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_vector.c diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h new file mode 100644 index 00000000..bbd857c3 --- /dev/null +++ b/include/sbi/sbi_vector.h @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#ifndef __SBI_VECTOR_H__ +#define __SBI_VECTOR_H__ + +#include + +struct sbi_vector_context { + unsigned long vcsr; + unsigned long vstart; + + /* size depends on VLEN */ + uint8_t vregs[]; +}; + +void sbi_vector_save(struct sbi_vector_context *dst); +void sbi_vector_restore(const struct sbi_vector_context *src); +unsigned long vector_vlenb(void); + +#endif //__SBI_VECTOR_H__ + diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 97cc4521..ddb2e7ac 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o +libsbi-objs-y += sbi_vector.o diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c new file mode 100644 index 00000000..1d2ac944 --- /dev/null +++ b/lib/sbi/sbi_vector.c @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENSBI_CC_SUPPORT_VECTOR + +unsigned long vector_vlenb(void) +{ + unsigned long vlenb = 0; + + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "csrr %0, vlenb\n\t" + ".option pop\n\t" + : "=r"(vlenb) + : + : "memory"); + + return vlenb; +} + +void sbi_vector_save(struct sbi_vector_context *dst) +{ + if (!dst) + return; + + /* Step 1: Save CSRs */ + dst->vcsr = csr_read(vcsr); + dst->vstart = csr_read(vstart); + + ulong vlenb = vector_vlenb(); + uint8_t *base = dst->vregs; + + /* Step 3: Save vector registers */ +#define SAVE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vs8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + SAVE_VREG(0); + SAVE_VREG(8); + SAVE_VREG(16); + SAVE_VREG(24); + +#undef SAVE_VREG +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ + if (!src) + return; + + const uint8_t *base = src->vregs; + ulong vlenb = vector_vlenb(); + + /* Step 2: Restore vector registers */ +#define RESTORE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vl8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + RESTORE_VREG(0); + RESTORE_VREG(8); + RESTORE_VREG(16); + RESTORE_VREG(24); +#undef RESTORE_VREG + + /* Step 3: Restore CSR's last */ + /* Restore CSRs first */ + csr_write(vcsr, src->vcsr); + csr_write(vstart, src->vstart); +} + +#else + +void sbi_vector_save(struct sbi_vector_context *dst) +{ +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ +} + +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ -- 2.43.0 From dave.patel at riscstar.com Thu May 14 22:42:06 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Fri, 15 May 2026 06:42:06 +0100 Subject: [PATCH v5 2/4] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260515054208.151045-1-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> Message-ID: <20260515054208.151045-3-dave.patel@riscstar.com> From: Dave Patel Add support for saving and restoring RISC-V floating-point (F/D) extension state in OpenSBI. This introduces a floating-point context structure and helper routines to perform full context save and restore. The floating-point context includes storage for all 32 FPi registers (f0?f31) along with the fcsr control and status register. The register state is saved and restored using double-precision load/store instructions (fsd/fld), and single-precision load/store instructions (fsw/flw) on an RV64 system with F and D-extension support. The implementation follows an eager context switching model where the entire FP state is saved and restored on every context switch. This avoids the need for trap-based lazy management and keeps the design simple and deterministic. Signed-off-by: Dave Patel " --- include/sbi/sbi_fp.h | 26 ++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 lib/sbi/sbi_fp.c diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h new file mode 100644 index 00000000..d4eb7694 --- /dev/null +++ b/include/sbi/sbi_fp.h @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ +#ifndef __SBI_FP_H__ +#define __SBI_FP_H__ + +#include + +struct sbi_fp_context { +#if __riscv_d + uint64_t f[32]; +#else + uint32_t f[32]; +#endif + uint32_t fcsr; +}; + +void sbi_fp_save(struct sbi_fp_context *dst); +void sbi_fp_restore(const struct sbi_fp_context *src); + +#endif //__SBI_VECTOR_H__ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index ddb2e7ac..d8182383 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -110,3 +110,4 @@ libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o libsbi-objs-y += sbi_vector.o +libsbi-objs-y += sbi_fp.o diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c new file mode 100644 index 00000000..887bca4d --- /dev/null +++ b/lib/sbi/sbi_fp.c @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include + +#if defined(__riscv_f) || defined(__riscv_d) + +void sbi_fp_save(struct sbi_fp_context *dst) +{ + if (!dst) + return; + +#if defined(__riscv_d) + asm volatile( + "fsd f0, 0(%0)\n" + "fsd f1, 8(%0)\n" + "fsd f2, 16(%0)\n" + "fsd f3, 24(%0)\n" + "fsd f4, 32(%0)\n" + "fsd f5, 40(%0)\n" + "fsd f6, 48(%0)\n" + "fsd f7, 56(%0)\n" + "fsd f8, 64(%0)\n" + "fsd f9, 72(%0)\n" + "fsd f10, 80(%0)\n" + "fsd f11, 88(%0)\n" + "fsd f12, 96(%0)\n" + "fsd f13, 104(%0)\n" + "fsd f14, 112(%0)\n" + "fsd f15, 120(%0)\n" + "fsd f16, 128(%0)\n" + "fsd f17, 136(%0)\n" + "fsd f18, 144(%0)\n" + "fsd f19, 152(%0)\n" + "fsd f20, 160(%0)\n" + "fsd f21, 168(%0)\n" + "fsd f22, 176(%0)\n" + "fsd f23, 184(%0)\n" + "fsd f24, 192(%0)\n" + "fsd f25, 200(%0)\n" + "fsd f26, 208(%0)\n" + "fsd f27, 216(%0)\n" + "fsd f28, 224(%0)\n" + "fsd f29, 232(%0)\n" + "fsd f30, 240(%0)\n" + "fsd f31, 248(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#else + asm volatile( + "fsw f0, 0(%0)\n" + "fsw f1, 4(%0)\n" + "fsw f2, 8(%0)\n" + "fsw f3, 12(%0)\n" + "fsw f4, 16(%0)\n" + "fsw f5, 20(%0)\n" + "fsw f6, 24(%0)\n" + "fsw f7, 28(%0)\n" + "fsw f8, 32(%0)\n" + "fsw f9, 36(%0)\n" + "fsw f10, 40(%0)\n" + "fsw f11, 44(%0)\n" + "fsw f12, 48(%0)\n" + "fsw f13, 52(%0)\n" + "fsw f14, 56(%0)\n" + "fsw f15, 60(%0)\n" + "fsw f16, 64(%0)\n" + "fsw f17, 68(%0)\n" + "fsw f18, 72(%0)\n" + "fsw f19, 76(%0)\n" + "fsw f20, 80(%0)\n" + "fsw f21, 84(%0)\n" + "fsw f22, 88(%0)\n" + "fsw f23, 92(%0)\n" + "fsw f24, 96(%0)\n" + "fsw f25, 100(%0)\n" + "fsw f26, 104(%0)\n" + "fsw f27, 108(%0)\n" + "fsw f28, 112(%0)\n" + "fsw f29, 116(%0)\n" + "fsw f30, 120(%0)\n" + "fsw f31, 124(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#endif //__riscv_d + + dst->fcsr = csr_read(CSR_FCSR); +} + +void sbi_fp_restore(const struct sbi_fp_context *src) +{ + if (!src) + return; + +#if defined(__riscv_d) + asm volatile( + "fld f0, 0(%0)\n" + "fld f1, 8(%0)\n" + "fld f2, 16(%0)\n" + "fld f3, 24(%0)\n" + "fld f4, 32(%0)\n" + "fld f5, 40(%0)\n" + "fld f6, 48(%0)\n" + "fld f7, 56(%0)\n" + "fld f8, 64(%0)\n" + "fld f9, 72(%0)\n" + "fld f10, 80(%0)\n" + "fld f11, 88(%0)\n" + "fld f12, 96(%0)\n" + "fld f13, 104(%0)\n" + "fld f14, 112(%0)\n" + "fld f15, 120(%0)\n" + "fld f16, 128(%0)\n" + "fld f17, 136(%0)\n" + "fld f18, 144(%0)\n" + "fld f19, 152(%0)\n" + "fld f20, 160(%0)\n" + "fld f21, 168(%0)\n" + "fld f22, 176(%0)\n" + "fld f23, 184(%0)\n" + "fld f24, 192(%0)\n" + "fld f25, 200(%0)\n" + "fld f26, 208(%0)\n" + "fld f27, 216(%0)\n" + "fld f28, 224(%0)\n" + "fld f29, 232(%0)\n" + "fld f30, 240(%0)\n" + "fld f31, 248(%0)\n" + : + : "r"(src->f) + : "memory" + ); +#else + + asm volatile( + "flw f0, 0(%0)\n" + "flw f1, 4(%0)\n" + "flw f2, 8(%0)\n" + "flw f3, 12(%0)\n" + "flw f4, 16(%0)\n" + "flw f5, 20(%0)\n" + "flw f6, 24(%0)\n" + "flw f7, 28(%0)\n" + "flw f8, 32(%0)\n" + "flw f9, 36(%0)\n" + "flw f10, 40(%0)\n" + "flw f11, 44(%0)\n" + "flw f12, 48(%0)\n" + "flw f13, 52(%0)\n" + "flw f14, 56(%0)\n" + "flw f15, 60(%0)\n" + "flw f16, 64(%0)\n" + "flw f17, 68(%0)\n" + "flw f18, 72(%0)\n" + "flw f19, 76(%0)\n" + "flw f20, 80(%0)\n" + "flw f21, 84(%0)\n" + "flw f22, 88(%0)\n" + "flw f23, 92(%0)\n" + "flw f24, 96(%0)\n" + "flw f25, 100(%0)\n" + "flw f26, 104(%0)\n" + "flw f27, 108(%0)\n" + "flw f28, 112(%0)\n" + "flw f29, 116(%0)\n" + "flw f30, 120(%0)\n" + "flw f31, 124(%0)\n" + : + : "r"(src->f) + : "memory" + ); + +#endif + + csr_write(CSR_FCSR, src->fcsr); +} +#else +void sbi_fp_save(struct sbi_fp_context *dst) {} +void sbi_fp_restore(const struct sbi_fp_context *src) {} +#endif // FP present -- 2.43.0 From dave.patel at riscstar.com Thu May 14 22:42:07 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Fri, 15 May 2026 06:42:07 +0100 Subject: [PATCH v5 3/4] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260515054208.151045-1-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> Message-ID: <20260515054208.151045-4-dave.patel@riscstar.com> From: Dave Patel This patch adds proper support for per-domain floating-point (FP) and vector (V) contexts in the domain context switch logic. Each domain now maintains its own FP and vector state, which is saved and restored during domain switches. Changes include: - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' to allocate and free per-domain FP and vector context. - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. - Updated `switch_to_next_domain_context()` to save/restore FP and vector contexts safely: - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. - Added runtime checks for FP and vector extensions where needed. This improves support for multi-domain systems with FP and Vector extensions, and prevents corruption of FP/Vector state during domain switches. Signed-off-by: Dave Patel --- lib/sbi/sbi_domain_context.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index 158f4990..5e180698 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -18,6 +18,9 @@ #include #include #include +#include +#include + /** Context representation for a hart within a domain */ struct hart_context { @@ -55,6 +58,11 @@ struct hart_context { struct hart_context *prev_ctx; /** Is context initialized and runnable */ bool initialized; + + /** float context state */ + struct sbi_fp_context fp_ctx; + /** vector context state */ + struct sbi_vector_context *vec_ctx; }; static struct sbi_domain_data dcpriv; @@ -143,6 +151,15 @@ static int switch_to_next_domain_context(struct hart_context *ctx, if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); + /* Make sure FS and VS is on before context switch */ + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); + + /* Eager context switch F and V */ + sbi_fp_save(&ctx->fp_ctx); + sbi_fp_restore(&dom_ctx->fp_ctx); + sbi_vector_save(ctx->vec_ctx); + sbi_vector_restore(dom_ctx->vec_ctx); + /* Save current trap state and restore target domain's trap state */ trap_ctx = sbi_trap_get_context(scratch); sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); @@ -170,6 +187,10 @@ static int hart_context_init(u32 hartindex) { struct hart_context *ctx; struct sbi_domain *dom; + unsigned long vlenb = vector_vlenb(); + + /* Calculate size: base struct + 32 registers of vlenb size */ + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); sbi_domain_for_each(dom) { if (!sbi_hartmask_test_hartindex(hartindex, @@ -180,6 +201,13 @@ static int hart_context_init(u32 hartindex) if (!ctx) return SBI_ENOMEM; + /* Allocate the vector context pointer */ + ctx->vec_ctx = sbi_zalloc(vec_size); + if (!ctx->vec_ctx) { + sbi_free(ctx); + return SBI_ENOMEM; + } + /* Bind context and domain */ ctx->dom = dom; hart_context_set(dom, hartindex, ctx); -- 2.43.0 From dave.patel at riscstar.com Thu May 14 22:42:08 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Fri, 15 May 2026 06:42:08 +0100 Subject: [PATCH v5 4/4] lib: sbi: Conditionalize FP and Vector save/restore based on extensions In-Reply-To: <20260515054208.151045-1-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> Message-ID: <20260515054208.151045-5-dave.patel@riscstar.com> From: Dave Patel Unconditional save and restore of floating-point (FP) and Vector registers fails on generic platform firmware. This firmware must run on multiple platforms that may lack these extensions. Address this by conditionally executing FP save/restore only if the underlying hart supports the F or D extensions. Similarly, perform Vector save/restore only if the hart supports the Vector extension. Depend on a separate patch that introduces SBI_HART_EXT_F, SBI_HART_EXT_D, and SBI_HART_EXT_V to enum sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() to check for these capabilities before performing the context switches. Signed-off-by: Dave Patel --- include/sbi/sbi_hart.h | 6 ++++++ lib/sbi/sbi_domain_context.c | 31 ++++++++++++++++++++++--------- lib/sbi/sbi_hart.c | 4 ++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index a788b34c..68a01b97 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -87,6 +87,12 @@ enum sbi_hart_extensions { SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, /** Hart has Xsfcease extension */ SBI_HART_EXT_XSIFIVE_CEASE, + /** Hart has V extension */ + SBI_HART_EXT_V, + /** Hart has F extension */ + SBI_HART_EXT_F, + /** Hart has D extension */ + SBI_HART_EXT_D, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index 5e180698..1e3ca46d 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -155,10 +155,20 @@ static int switch_to_next_domain_context(struct hart_context *ctx, csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); /* Eager context switch F and V */ - sbi_fp_save(&ctx->fp_ctx); - sbi_fp_restore(&dom_ctx->fp_ctx); - sbi_vector_save(ctx->vec_ctx); - sbi_vector_restore(dom_ctx->vec_ctx); + + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_F) || + sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_D)) { + sbi_fp_save(&ctx->fp_ctx); + sbi_fp_restore(&dom_ctx->fp_ctx); + } + + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V)) { + sbi_vector_save(ctx->vec_ctx); + sbi_vector_restore(dom_ctx->vec_ctx); + } /* Save current trap state and restore target domain's trap state */ trap_ctx = sbi_trap_get_context(scratch); @@ -201,11 +211,14 @@ static int hart_context_init(u32 hartindex) if (!ctx) return SBI_ENOMEM; - /* Allocate the vector context pointer */ - ctx->vec_ctx = sbi_zalloc(vec_size); - if (!ctx->vec_ctx) { - sbi_free(ctx); - return SBI_ENOMEM; + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V )) { + /* Allocate the vector context pointer */ + ctx->vec_ctx = sbi_zalloc(vec_size); + if (!ctx->vec_ctx) { + sbi_free(ctx); + return SBI_ENOMEM; + } } /* Bind context and domain */ diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 60e95bca..e7581368 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -396,6 +396,10 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), + __SBI_HART_EXT_DATA(v, SBI_HART_EXT_V), + __SBI_HART_EXT_DATA(f, SBI_HART_EXT_F), + __SBI_HART_EXT_DATA(d, SBI_HART_EXT_D), + }; _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), -- 2.43.0 From anup at brainfault.org Thu May 14 23:56:01 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 15 May 2026 12:26:01 +0530 Subject: [PATCH v5 1/4] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260515054208.151045-2-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> <20260515054208.151045-2-dave.patel@riscstar.com> Message-ID: On Fri, May 15, 2026 at 11:12?AM wrote: > > From: Dave Patel > > Eager context switch: Add support for saving and restoring RISC-V vector > extension state in OpenSBI. This introduces a per-hart vector context > structure and helper routines to perform full context save and restore. > > The vector context includes vcsr CSRs along with storage for all 32 vector > registers. The register state is saved and restored using byte-wise vector > load/store instructions (vs8r/vl8r). > > The implementation follows an eager context switching model where the entire > vector state is saved and restored on every context switch. This provides a > simple and deterministic mechanism without requiring lazy trap-based > management. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_vector.h | 28 ++++++++++ > lib/sbi/objects.mk | 1 + > lib/sbi/sbi_vector.c | 109 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 138 insertions(+) > create mode 100644 include/sbi/sbi_vector.h > create mode 100644 lib/sbi/sbi_vector.c > > diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h > new file mode 100644 > index 00000000..bbd857c3 > --- /dev/null > +++ b/include/sbi/sbi_vector.h > @@ -0,0 +1,28 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#ifndef __SBI_VECTOR_H__ > +#define __SBI_VECTOR_H__ > + > +#include > + > +struct sbi_vector_context { > + unsigned long vcsr; > + unsigned long vstart; > + > + /* size depends on VLEN */ > + uint8_t vregs[]; > +}; > + > +void sbi_vector_save(struct sbi_vector_context *dst); > +void sbi_vector_restore(const struct sbi_vector_context *src); > +unsigned long vector_vlenb(void); > + > +#endif //__SBI_VECTOR_H__ > + Redundant newline here. > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index 97cc4521..ddb2e7ac 100644 > --- a/lib/sbi/objects.mk > +++ b/lib/sbi/objects.mk > @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o > libsbi-objs-y += sbi_unpriv.o > libsbi-objs-y += sbi_expected_trap.o > libsbi-objs-y += sbi_cppc.o > +libsbi-objs-y += sbi_vector.o > diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c > new file mode 100644 > index 00000000..1d2ac944 > --- /dev/null > +++ b/lib/sbi/sbi_vector.c > @@ -0,0 +1,109 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#ifdef OPENSBI_CC_SUPPORT_VECTOR > + > +unsigned long vector_vlenb(void) > +{ > + unsigned long vlenb = 0; > + > + asm volatile ( > + ".option push\n\t" > + ".option arch, +v\n\t" > + "csrr %0, vlenb\n\t" > + ".option pop\n\t" > + : "=r"(vlenb) > + : > + : "memory"); > + > + return vlenb; > +} > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > + if (!dst) > + return; > + > + /* Step 1: Save CSRs */ > + dst->vcsr = csr_read(vcsr); > + dst->vstart = csr_read(vstart); > + > + ulong vlenb = vector_vlenb(); > + uint8_t *base = dst->vregs; > + > + /* Step 3: Save vector registers */ > +#define SAVE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vs8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + SAVE_VREG(0); > + SAVE_VREG(8); > + SAVE_VREG(16); > + SAVE_VREG(24); > + > +#undef SAVE_VREG > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > + if (!src) > + return; > + > + const uint8_t *base = src->vregs; > + ulong vlenb = vector_vlenb(); > + > + /* Step 2: Restore vector registers */ > +#define RESTORE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vl8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + RESTORE_VREG(0); > + RESTORE_VREG(8); > + RESTORE_VREG(16); > + RESTORE_VREG(24); > +#undef RESTORE_VREG > + > + /* Step 3: Restore CSR's last */ > + /* Restore CSRs first */ > + csr_write(vcsr, src->vcsr); > + csr_write(vstart, src->vstart); > +} > + > +#else > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > +} > + > +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Otherwise, it looks good to me. I will take care of above issue at the time of merging. Reviewed-by: Anup Patel Thanks, Anup From anup at brainfault.org Fri May 15 00:10:05 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 15 May 2026 12:40:05 +0530 Subject: [PATCH v5 2/4] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260515054208.151045-3-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> <20260515054208.151045-3-dave.patel@riscstar.com> Message-ID: On Fri, May 15, 2026 at 11:12?AM wrote: > > From: Dave Patel > > Add support for saving and restoring RISC-V floating-point (F/D) extension > state in OpenSBI. This introduces a floating-point context structure and > helper routines to perform full context save and restore. > > The floating-point context includes storage for all 32 FPi registers (f0?f31) > along with the fcsr control and status register. The register state is saved > and restored using double-precision load/store instructions (fsd/fld), and > single-precision load/store instructions (fsw/flw) on an RV64 system with > F and D-extension support. > > The implementation follows an eager context switching model where the entire > FP state is saved and restored on every context switch. This avoids the need > for trap-based lazy management and keeps the design simple and deterministic. > > Signed-off-by: Dave Patel " > --- > include/sbi/sbi_fp.h | 26 ++++++ > lib/sbi/objects.mk | 1 + > lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 219 insertions(+) > create mode 100644 include/sbi/sbi_fp.h > create mode 100644 lib/sbi/sbi_fp.c > > diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h > new file mode 100644 > index 00000000..d4eb7694 > --- /dev/null > +++ b/include/sbi/sbi_fp.h > @@ -0,0 +1,26 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > +#ifndef __SBI_FP_H__ > +#define __SBI_FP_H__ > + > +#include > + > +struct sbi_fp_context { > +#if __riscv_d > + uint64_t f[32]; > +#else > + uint32_t f[32]; > +#endif > + uint32_t fcsr; All CSRs are machine wide hence fcsr must be unsigned long. > +}; > + > +void sbi_fp_save(struct sbi_fp_context *dst); > +void sbi_fp_restore(const struct sbi_fp_context *src); > + > +#endif //__SBI_VECTOR_H__ > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index ddb2e7ac..d8182383 100644 > --- a/lib/sbi/objects.mk > +++ b/lib/sbi/objects.mk > @@ -110,3 +110,4 @@ libsbi-objs-y += sbi_unpriv.o > libsbi-objs-y += sbi_expected_trap.o > libsbi-objs-y += sbi_cppc.o > libsbi-objs-y += sbi_vector.o > +libsbi-objs-y += sbi_fp.o > diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c > new file mode 100644 > index 00000000..887bca4d > --- /dev/null > +++ b/lib/sbi/sbi_fp.c > @@ -0,0 +1,192 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#include > +#include > +#include > + > +#if defined(__riscv_f) || defined(__riscv_d) > + > +void sbi_fp_save(struct sbi_fp_context *dst) > +{ > + if (!dst) > + return; > + > +#if defined(__riscv_d) > + asm volatile( > + "fsd f0, 0(%0)\n" > + "fsd f1, 8(%0)\n" > + "fsd f2, 16(%0)\n" > + "fsd f3, 24(%0)\n" > + "fsd f4, 32(%0)\n" > + "fsd f5, 40(%0)\n" > + "fsd f6, 48(%0)\n" > + "fsd f7, 56(%0)\n" > + "fsd f8, 64(%0)\n" > + "fsd f9, 72(%0)\n" > + "fsd f10, 80(%0)\n" > + "fsd f11, 88(%0)\n" > + "fsd f12, 96(%0)\n" > + "fsd f13, 104(%0)\n" > + "fsd f14, 112(%0)\n" > + "fsd f15, 120(%0)\n" > + "fsd f16, 128(%0)\n" > + "fsd f17, 136(%0)\n" > + "fsd f18, 144(%0)\n" > + "fsd f19, 152(%0)\n" > + "fsd f20, 160(%0)\n" > + "fsd f21, 168(%0)\n" > + "fsd f22, 176(%0)\n" > + "fsd f23, 184(%0)\n" > + "fsd f24, 192(%0)\n" > + "fsd f25, 200(%0)\n" > + "fsd f26, 208(%0)\n" > + "fsd f27, 216(%0)\n" > + "fsd f28, 224(%0)\n" > + "fsd f29, 232(%0)\n" > + "fsd f30, 240(%0)\n" > + "fsd f31, 248(%0)\n" > + : > + : "r"(dst->f) > + : "memory" > + ); > +#else > + asm volatile( > + "fsw f0, 0(%0)\n" > + "fsw f1, 4(%0)\n" > + "fsw f2, 8(%0)\n" > + "fsw f3, 12(%0)\n" > + "fsw f4, 16(%0)\n" > + "fsw f5, 20(%0)\n" > + "fsw f6, 24(%0)\n" > + "fsw f7, 28(%0)\n" > + "fsw f8, 32(%0)\n" > + "fsw f9, 36(%0)\n" > + "fsw f10, 40(%0)\n" > + "fsw f11, 44(%0)\n" > + "fsw f12, 48(%0)\n" > + "fsw f13, 52(%0)\n" > + "fsw f14, 56(%0)\n" > + "fsw f15, 60(%0)\n" > + "fsw f16, 64(%0)\n" > + "fsw f17, 68(%0)\n" > + "fsw f18, 72(%0)\n" > + "fsw f19, 76(%0)\n" > + "fsw f20, 80(%0)\n" > + "fsw f21, 84(%0)\n" > + "fsw f22, 88(%0)\n" > + "fsw f23, 92(%0)\n" > + "fsw f24, 96(%0)\n" > + "fsw f25, 100(%0)\n" > + "fsw f26, 104(%0)\n" > + "fsw f27, 108(%0)\n" > + "fsw f28, 112(%0)\n" > + "fsw f29, 116(%0)\n" > + "fsw f30, 120(%0)\n" > + "fsw f31, 124(%0)\n" This will trap and crash if FP is disabled in mstatus CSR. The correct approach to save/restore is set mstatus.FS to 0x3 before accessing floating point registers and restore the orginial value of mstatus.FS after acesssing floating point registers. Refer, __kvm_riscv_fp_f_save()/restore() at /arch/riscv/kvm/vcpu_switch.S > + : > + : "r"(dst->f) > + : "memory" > + ); > +#endif //__riscv_d > + > + dst->fcsr = csr_read(CSR_FCSR); > +} > + > +void sbi_fp_restore(const struct sbi_fp_context *src) > +{ > + if (!src) > + return; > + > +#if defined(__riscv_d) > + asm volatile( > + "fld f0, 0(%0)\n" > + "fld f1, 8(%0)\n" > + "fld f2, 16(%0)\n" > + "fld f3, 24(%0)\n" > + "fld f4, 32(%0)\n" > + "fld f5, 40(%0)\n" > + "fld f6, 48(%0)\n" > + "fld f7, 56(%0)\n" > + "fld f8, 64(%0)\n" > + "fld f9, 72(%0)\n" > + "fld f10, 80(%0)\n" > + "fld f11, 88(%0)\n" > + "fld f12, 96(%0)\n" > + "fld f13, 104(%0)\n" > + "fld f14, 112(%0)\n" > + "fld f15, 120(%0)\n" > + "fld f16, 128(%0)\n" > + "fld f17, 136(%0)\n" > + "fld f18, 144(%0)\n" > + "fld f19, 152(%0)\n" > + "fld f20, 160(%0)\n" > + "fld f21, 168(%0)\n" > + "fld f22, 176(%0)\n" > + "fld f23, 184(%0)\n" > + "fld f24, 192(%0)\n" > + "fld f25, 200(%0)\n" > + "fld f26, 208(%0)\n" > + "fld f27, 216(%0)\n" > + "fld f28, 224(%0)\n" > + "fld f29, 232(%0)\n" > + "fld f30, 240(%0)\n" > + "fld f31, 248(%0)\n" > + : > + : "r"(src->f) > + : "memory" > + ); > +#else > + > + asm volatile( > + "flw f0, 0(%0)\n" > + "flw f1, 4(%0)\n" > + "flw f2, 8(%0)\n" > + "flw f3, 12(%0)\n" > + "flw f4, 16(%0)\n" > + "flw f5, 20(%0)\n" > + "flw f6, 24(%0)\n" > + "flw f7, 28(%0)\n" > + "flw f8, 32(%0)\n" > + "flw f9, 36(%0)\n" > + "flw f10, 40(%0)\n" > + "flw f11, 44(%0)\n" > + "flw f12, 48(%0)\n" > + "flw f13, 52(%0)\n" > + "flw f14, 56(%0)\n" > + "flw f15, 60(%0)\n" > + "flw f16, 64(%0)\n" > + "flw f17, 68(%0)\n" > + "flw f18, 72(%0)\n" > + "flw f19, 76(%0)\n" > + "flw f20, 80(%0)\n" > + "flw f21, 84(%0)\n" > + "flw f22, 88(%0)\n" > + "flw f23, 92(%0)\n" > + "flw f24, 96(%0)\n" > + "flw f25, 100(%0)\n" > + "flw f26, 104(%0)\n" > + "flw f27, 108(%0)\n" > + "flw f28, 112(%0)\n" > + "flw f29, 116(%0)\n" > + "flw f30, 120(%0)\n" > + "flw f31, 124(%0)\n" > + : > + : "r"(src->f) > + : "memory" > + ); > + > +#endif > + > + csr_write(CSR_FCSR, src->fcsr); > +} > +#else > +void sbi_fp_save(struct sbi_fp_context *dst) {} > +void sbi_fp_restore(const struct sbi_fp_context *src) {} > +#endif // FP present > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Fri May 15 00:41:27 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 15 May 2026 13:11:27 +0530 Subject: [PATCH v5 1/4] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260515054208.151045-2-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> <20260515054208.151045-2-dave.patel@riscstar.com> Message-ID: On Fri, May 15, 2026 at 11:12?AM wrote: > > From: Dave Patel > > Eager context switch: Add support for saving and restoring RISC-V vector > extension state in OpenSBI. This introduces a per-hart vector context > structure and helper routines to perform full context save and restore. > > The vector context includes vcsr CSRs along with storage for all 32 vector > registers. The register state is saved and restored using byte-wise vector > load/store instructions (vs8r/vl8r). > > The implementation follows an eager context switching model where the entire > vector state is saved and restored on every context switch. This provides a > simple and deterministic mechanism without requiring lazy trap-based > management. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_vector.h | 28 ++++++++++ > lib/sbi/objects.mk | 1 + > lib/sbi/sbi_vector.c | 109 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 138 insertions(+) > create mode 100644 include/sbi/sbi_vector.h > create mode 100644 lib/sbi/sbi_vector.c > > diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h > new file mode 100644 > index 00000000..bbd857c3 > --- /dev/null > +++ b/include/sbi/sbi_vector.h > @@ -0,0 +1,28 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#ifndef __SBI_VECTOR_H__ > +#define __SBI_VECTOR_H__ > + > +#include > + > +struct sbi_vector_context { > + unsigned long vcsr; > + unsigned long vstart; > + > + /* size depends on VLEN */ > + uint8_t vregs[]; > +}; > + > +void sbi_vector_save(struct sbi_vector_context *dst); > +void sbi_vector_restore(const struct sbi_vector_context *src); > +unsigned long vector_vlenb(void); > + > +#endif //__SBI_VECTOR_H__ > + > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index 97cc4521..ddb2e7ac 100644 > --- a/lib/sbi/objects.mk > +++ b/lib/sbi/objects.mk > @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o > libsbi-objs-y += sbi_unpriv.o > libsbi-objs-y += sbi_expected_trap.o > libsbi-objs-y += sbi_cppc.o > +libsbi-objs-y += sbi_vector.o > diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c > new file mode 100644 > index 00000000..1d2ac944 > --- /dev/null > +++ b/lib/sbi/sbi_vector.c > @@ -0,0 +1,109 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#ifdef OPENSBI_CC_SUPPORT_VECTOR > + > +unsigned long vector_vlenb(void) > +{ > + unsigned long vlenb = 0; > + > + asm volatile ( > + ".option push\n\t" > + ".option arch, +v\n\t" > + "csrr %0, vlenb\n\t" > + ".option pop\n\t" > + : "=r"(vlenb) > + : > + : "memory"); > + > + return vlenb; > +} > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > + if (!dst) > + return; > + > + /* Step 1: Save CSRs */ > + dst->vcsr = csr_read(vcsr); > + dst->vstart = csr_read(vstart); > + > + ulong vlenb = vector_vlenb(); > + uint8_t *base = dst->vregs; > + > + /* Step 3: Save vector registers */ > +#define SAVE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vs8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + SAVE_VREG(0); > + SAVE_VREG(8); > + SAVE_VREG(16); > + SAVE_VREG(24); Same issue as FP save/restore, this will trap and crash if Vector is disabled in mstatus.VS. For correct implementation, refer __riscv_v_vstate_save()/restore() from /arch/riscv/include/asm/vector.h > + > +#undef SAVE_VREG > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > + if (!src) > + return; > + > + const uint8_t *base = src->vregs; > + ulong vlenb = vector_vlenb(); > + > + /* Step 2: Restore vector registers */ > +#define RESTORE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vl8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + RESTORE_VREG(0); > + RESTORE_VREG(8); > + RESTORE_VREG(16); > + RESTORE_VREG(24); > +#undef RESTORE_VREG > + > + /* Step 3: Restore CSR's last */ > + /* Restore CSRs first */ > + csr_write(vcsr, src->vcsr); > + csr_write(vstart, src->vstart); > +} > + > +#else > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > +} > + > +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Fri May 15 00:43:40 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 15 May 2026 13:13:40 +0530 Subject: [PATCH v5 3/4] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260515054208.151045-4-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> <20260515054208.151045-4-dave.patel@riscstar.com> Message-ID: On Fri, May 15, 2026 at 11:12?AM wrote: > > From: Dave Patel > > This patch adds proper support for per-domain floating-point (FP) and > vector (V) contexts in the domain context switch logic. Each domain > now maintains its own FP and vector state, which is saved and restored > during domain switches. > > Changes include: > > - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. > - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' > to allocate and free per-domain FP and vector context. > - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. > - Updated `switch_to_next_domain_context()` to save/restore FP and vector > contexts safely: > - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. > - Added runtime checks for FP and vector extensions where needed. > > This improves support for multi-domain systems with FP and Vector > extensions, and prevents corruption of FP/Vector state during domain > switches. > > Signed-off-by: Dave Patel > --- > lib/sbi/sbi_domain_context.c | 28 ++++++++++++++++++++++++++++ > 1 file changed, 28 insertions(+) > > diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c > index 158f4990..5e180698 100644 > --- a/lib/sbi/sbi_domain_context.c > +++ b/lib/sbi/sbi_domain_context.c > @@ -18,6 +18,9 @@ > #include > #include > #include > +#include > +#include > + Redundant newline here. > > /** Context representation for a hart within a domain */ > struct hart_context { > @@ -55,6 +58,11 @@ struct hart_context { > struct hart_context *prev_ctx; > /** Is context initialized and runnable */ > bool initialized; > + > + /** float context state */ > + struct sbi_fp_context fp_ctx; > + /** vector context state */ > + struct sbi_vector_context *vec_ctx; > }; > > static struct sbi_domain_data dcpriv; > @@ -143,6 +151,15 @@ static int switch_to_next_domain_context(struct hart_context *ctx, > if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) > ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); > > + /* Make sure FS and VS is on before context switch */ > + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); > + > + /* Eager context switch F and V */ > + sbi_fp_save(&ctx->fp_ctx); > + sbi_fp_restore(&dom_ctx->fp_ctx); > + sbi_vector_save(ctx->vec_ctx); > + sbi_vector_restore(dom_ctx->vec_ctx); > + > /* Save current trap state and restore target domain's trap state */ > trap_ctx = sbi_trap_get_context(scratch); > sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); > @@ -170,6 +187,10 @@ static int hart_context_init(u32 hartindex) > { > struct hart_context *ctx; > struct sbi_domain *dom; > + unsigned long vlenb = vector_vlenb(); This will trap if Vector extension is not available. > + > + /* Calculate size: base struct + 32 registers of vlenb size */ > + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); > > sbi_domain_for_each(dom) { > if (!sbi_hartmask_test_hartindex(hartindex, > @@ -180,6 +201,13 @@ static int hart_context_init(u32 hartindex) > if (!ctx) > return SBI_ENOMEM; > > + /* Allocate the vector context pointer */ > + ctx->vec_ctx = sbi_zalloc(vec_size); > + if (!ctx->vec_ctx) { > + sbi_free(ctx); > + return SBI_ENOMEM; > + } > + > /* Bind context and domain */ > ctx->dom = dom; > hart_context_set(dom, hartindex, ctx); > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Fri May 15 00:44:50 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 15 May 2026 13:14:50 +0530 Subject: [PATCH v5 4/4] lib: sbi: Conditionalize FP and Vector save/restore based on extensions In-Reply-To: <20260515054208.151045-5-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> <20260515054208.151045-5-dave.patel@riscstar.com> Message-ID: On Fri, May 15, 2026 at 11:12?AM wrote: > > From: Dave Patel > > Unconditional save and restore of floating-point (FP) and Vector > registers fails on generic platform firmware. This firmware must run > on multiple platforms that may lack these extensions. > > Address this by conditionally executing FP save/restore only if the > underlying hart supports the F or D extensions. Similarly, perform > Vector save/restore only if the hart supports the Vector extension. > > Depend on a separate patch that introduces SBI_HART_EXT_F, > SBI_HART_EXT_D, and SBI_HART_EXT_V to enum sbi_hart_extensions and > the sbi_hart_ext[] array. Use sbi_hart_has_extension() to check for > these capabilities before performing the context switches. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_hart.h | 6 ++++++ > lib/sbi/sbi_domain_context.c | 31 ++++++++++++++++++++++--------- > lib/sbi/sbi_hart.c | 4 ++++ This patch should be before PATCH3 and the sbi_domain_context.c changes in this patch must be part of PATCH3. > 3 files changed, 32 insertions(+), 9 deletions(-) > > diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h > index a788b34c..68a01b97 100644 > --- a/include/sbi/sbi_hart.h > +++ b/include/sbi/sbi_hart.h > @@ -87,6 +87,12 @@ enum sbi_hart_extensions { > SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, > /** Hart has Xsfcease extension */ > SBI_HART_EXT_XSIFIVE_CEASE, > + /** Hart has V extension */ > + SBI_HART_EXT_V, > + /** Hart has F extension */ > + SBI_HART_EXT_F, > + /** Hart has D extension */ > + SBI_HART_EXT_D, > > /** Maximum index of Hart extension */ > SBI_HART_EXT_MAX, > diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c > index 5e180698..1e3ca46d 100644 > --- a/lib/sbi/sbi_domain_context.c > +++ b/lib/sbi/sbi_domain_context.c > @@ -155,10 +155,20 @@ static int switch_to_next_domain_context(struct hart_context *ctx, > csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); > > /* Eager context switch F and V */ > - sbi_fp_save(&ctx->fp_ctx); > - sbi_fp_restore(&dom_ctx->fp_ctx); > - sbi_vector_save(ctx->vec_ctx); > - sbi_vector_restore(dom_ctx->vec_ctx); > + > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_F) || > + sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_D)) { > + sbi_fp_save(&ctx->fp_ctx); > + sbi_fp_restore(&dom_ctx->fp_ctx); > + } > + > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_V)) { > + sbi_vector_save(ctx->vec_ctx); > + sbi_vector_restore(dom_ctx->vec_ctx); > + } > > /* Save current trap state and restore target domain's trap state */ > trap_ctx = sbi_trap_get_context(scratch); > @@ -201,11 +211,14 @@ static int hart_context_init(u32 hartindex) > if (!ctx) > return SBI_ENOMEM; > > - /* Allocate the vector context pointer */ > - ctx->vec_ctx = sbi_zalloc(vec_size); > - if (!ctx->vec_ctx) { > - sbi_free(ctx); > - return SBI_ENOMEM; > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_V )) { > + /* Allocate the vector context pointer */ > + ctx->vec_ctx = sbi_zalloc(vec_size); > + if (!ctx->vec_ctx) { > + sbi_free(ctx); > + return SBI_ENOMEM; > + } > } > > /* Bind context and domain */ > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > index 60e95bca..e7581368 100644 > --- a/lib/sbi/sbi_hart.c > +++ b/lib/sbi/sbi_hart.c > @@ -396,6 +396,10 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { > __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), > __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), > __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), > + __SBI_HART_EXT_DATA(v, SBI_HART_EXT_V), > + __SBI_HART_EXT_DATA(f, SBI_HART_EXT_F), > + __SBI_HART_EXT_DATA(d, SBI_HART_EXT_D), > + > }; > > _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Fri May 15 00:57:06 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 15 May 2026 13:27:06 +0530 Subject: [PATCH v5 0/3] Add eager FP and RISC-V vector context switching support In-Reply-To: <20260515054208.151045-1-dave.patel@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> Message-ID: On Fri, May 15, 2026 at 11:12?AM wrote: > > Hi Anup, > Thank you for taking out time and reviewing the patches. > I have added new patch to add Extension V, F and D and their checks before > context swtich > > The changes include: > - Alter sbi_vector.c to use csr_read and csr_write > - Added another patch for Extension check. > > I have covered all your comments, please can you have a look and let me know. > > Thanks and Regards, > Dave > > Signed-off-by: Dave Patel > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi You cover-letter is prefixed "[PATCH v5 0/3]" but you have 4 patches in your series. Please use "git format-patch" to general cover-letter along with the patches like below: git format-patch -M -C --cover-letter --subject-prefix="PATCH v5" ... Regards, Anup From peter.lin at sifive.com Fri May 15 04:12:21 2026 From: peter.lin at sifive.com (Yu-Chien Peter Lin) Date: Fri, 15 May 2026 19:12:21 +0800 Subject: [PATCH v2] lib: utils: fdt_domain: add root-regions-inheritance policy Message-ID: <20260515111221.1235342-1-peter.lin@sifive.com> Introduce root-regions-inheritance DT property to control copying of root domain memregions. Support 'all' and 'm-only' modes, always inheriting firmware and M-only regions; behavior matches existing policy when property is absent. Signed-off-by: Yu-Chien Peter Lin --- Changes v1->v2: - Addressed feedback from Anup: https://lists.infradead.org/pipermail/opensbi/2026-May/009850.html --- docs/domain_support.md | 7 ++++++ lib/utils/fdt/fdt_domain.c | 49 ++++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/docs/domain_support.md b/docs/domain_support.md index c01e445f..e267a9f7 100644 --- a/docs/domain_support.md +++ b/docs/domain_support.md @@ -159,6 +159,13 @@ The DT properties of a domain instance DT node are as follows: * **possible-harts** (Optional) - The list of CPU DT node phandles for the the domain instance. This list represents the possible HARTs of the domain instance. +* **root-regions-inheritance** (Optional) - A string property controlling + how memory regions are inherited from **the ROOT domain**, which are then + overlaid with regions specified in the **regions** property for additional + restrictions. The allowed values are: + * "all" - inherit all memory regions from **the ROOT domain** + * "m-only" - inherit M-mode only memory regions from **the ROOT domain** + If this DT property is absent, behavior is the same as "m-only". * **regions** (Optional) - The list of domain memory region DT node phandle and access permissions for the domain instance. Each list entry is a pair of DT node phandle and access permissions. The access permissions are diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c index 1b039533..2357346b 100644 --- a/lib/utils/fdt/fdt_domain.c +++ b/lib/utils/fdt/fdt_domain.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -237,7 +238,9 @@ skip_device_disable: fdt_nop_node(fdt, poffset); } -#define FDT_DOMAIN_REGION_MAX_COUNT 16 +#define FDT_DOMAIN_REGION_MAX_COUNT 16 +#define FDT_ROOT_REGION_INHERIT_ALL 0 +#define FDT_ROOT_REGION_INHERIT_M_ONLY 1 struct parse_region_data { struct sbi_domain *dom; @@ -309,12 +312,14 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) u32 val32; u64 val64; const u32 *val; + const char *inherit; struct sbi_domain *dom; struct sbi_hartmask *mask; struct sbi_hartmask assign_mask; struct parse_region_data preg; int *cold_domain_offset = opaque; struct sbi_domain_memregion *reg; + int inheritance_mode = FDT_ROOT_REGION_INHERIT_M_ONLY; int i, err = 0, len, cpus_offset, cpu_offset, doffset; dom = sbi_zalloc(sizeof(*dom)); @@ -373,20 +378,38 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) if (err) goto fail_free_all; - /* - * Copy over root domain memregions which don't allow - * read, write and execute from lower privilege modes. - * - * These root domain memregions without read, write, - * and execute permissions include: - * 1) firmware region protecting the firmware memory - * 2) mmio regions protecting M-mode only mmio devices - */ + /* Determine root domain regions inheritance behavior. */ + inherit = fdt_getprop(fdt, domain_offset, + "root-regions-inheritance", &len); + if (inherit && len > 0) { + if (!strcmp(inherit, "all")) + inheritance_mode = FDT_ROOT_REGION_INHERIT_ALL; + else { + sbi_printf("%s: domain \"%s\" has unsupported " + "root-regions-inheritance=\"%s\", using \"m-only\"\n", + __func__, dom->name, inherit); + } + } + + /* Copy over root domain memregions according to inheritance_mode. */ sbi_domain_for_each_memregion(&root, reg) { - if ((reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) || - (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) || - (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)) + int copy = 0; + + switch (inheritance_mode) { + case FDT_ROOT_REGION_INHERIT_ALL: + copy = 1; + break; + case FDT_ROOT_REGION_INHERIT_M_ONLY: + if (SBI_DOMAIN_MEMREGION_IS_FIRMWARE(reg->flags) || + SBI_DOMAIN_MEMREGION_M_ONLY_ACCESS(reg->flags)) { + copy = 1; + } + break; + } + + if (!copy) continue; + if (preg.max_regions <= preg.region_count) { err = SBI_EINVAL; goto fail_free_all; -- 2.34.1 From marcos.oduardo at gmail.com Fri May 15 09:33:21 2026 From: marcos.oduardo at gmail.com (Marcos Oduardo) Date: Fri, 15 May 2026 18:33:21 +0200 Subject: [PATCH v4] lib: sbi: add UBSan support Message-ID: <20260515163321.2038366-1-marcos.oduardo@gmail.com> UBSan (Undefined Behavior Sanitizer) is a tool implemented using compiler instrumentation at runtime that allows checking for statements whose output is not deterministic or defined by the C standard. Compiling and running OpenSBI with UBSan instrumentation will print a message in the console if any sentence performs such an action. Support involves two main components: 1. The UBSan implementation hooks (derived from NetBSD), used by the compiler to handle the check output. 2. A test suite integrated with the SBI unit test framework to verify correct operation at runtime. Usage: make UBSAN=y PLATFORM=generic ... The test suite is built when both UBSAN=y and CONFIG_SBIUNIT=y are enabled. When UBSan is enabled, FW_PAYLOAD_OFFSET may need to be increased due to the size increase added by the instrumentation. A value of 0x400000 has been tested. UBSan adds runtime overhead and is intended for development builds only, not for production. Note: This patch marks __stack_chk_guard in sbi_init.c as a weak symbol to prevent multiple definition errors at compile time with UBSan instrumentation enabled. This resolves the conflict between the .globl definitions in sbi_init.c and test_head.S. Signed-off-by: Marcos Oduardo --- Changes added to this patch: - Fixed formatting - Integrated the previous tests in the sbi unit test - Renamed main flag to UBSAN=y - Regarding the license header in sbi_ubsan.c: this patch is BSD2-Clause compatible, but I am not sure whether the upstream NetBSD header can be reduced to SPDX-only given COPYING.BSD, or whether it should be kept in full as done for other imported code in the tree (lib/utils/libquad/). In this patch I've trimmed it, but I will follow whichever you prefer if a v5 is needed. Makefile | 17 + include/sbi/sbi_ubsan.h | 14 + lib/sbi/objects.mk | 3 + lib/sbi/sbi_init.c | 2 +- lib/sbi/sbi_ubsan.c | 924 +++++++++++++++++++++++++++++++++ lib/sbi/tests/objects.mk | 5 + lib/sbi/tests/sbi_ubsan_test.c | 114 ++++ platform/generic/objects.mk | 5 + 8 files changed, 1083 insertions(+), 1 deletion(-) create mode 100644 include/sbi/sbi_ubsan.h create mode 100644 lib/sbi/sbi_ubsan.c create mode 100644 lib/sbi/tests/sbi_ubsan_test.c diff --git a/Makefile b/Makefile index 46541063..54eb15bc 100644 --- a/Makefile +++ b/Makefile @@ -455,6 +455,23 @@ else CFLAGS += -O2 endif +ifeq ($(UBSAN),y) +UBSAN_CC_FLAGS := -fsanitize=undefined +UBSAN_CC_FLAGS += -DUBSAN_ENABLED +UBSAN_CC_FLAGS += -fno-sanitize=vptr +UBSAN_CC_FLAGS += -fno-sanitize=float-cast-overflow +UBSAN_CC_FLAGS += -fno-sanitize=float-divide-by-zero +UBSAN_CC_FLAGS += -fsanitize-recover=undefined +UBSAN_CC_FLAGS += -fsanitize=pointer-overflow +UBSAN_CC_FLAGS += -fsanitize=alignment +UBSAN_CC_FLAGS += -fno-sanitize-recover=alignment +UBSAN_CC_FLAGS += -fno-stack-protector +ifeq ($(LLVM), y) +UBSAN_CC_FLAGS += -fno-sanitize-link-runtime +endif +CFLAGS += $(UBSAN_CC_FLAGS) +endif + ifeq ($(V), 1) ELFFLAGS += -Wl,--print-gc-sections endif diff --git a/include/sbi/sbi_ubsan.h b/include/sbi/sbi_ubsan.h new file mode 100644 index 00000000..f93215c4 --- /dev/null +++ b/include/sbi/sbi_ubsan.h @@ -0,0 +1,14 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Author: Marcos Oduardo + */ + +#ifndef __SBI_UBSAN_H__ +#define __SBI_UBSAN_H__ + +#include + +extern volatile unsigned long sbi_ubsan_report_count; + +#endif /* __SBI_UBSAN_H__ */ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 07d13229..f1f5cc31 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -99,6 +99,9 @@ libsbi-objs-y += sbi_tlb.o libsbi-objs-y += sbi_trap.o libsbi-objs-y += sbi_trap_ldst.o libsbi-objs-y += sbi_trap_v_ldst.o +ifeq ($(UBSAN), y) +libsbi-objs-y += sbi_ubsan.o +endif libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c index b248e73f..4e34bbc5 100644 --- a/lib/sbi/sbi_init.c +++ b/lib/sbi/sbi_init.c @@ -218,7 +218,7 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch) __smp_store_release(&coldboot_done, 1); } -unsigned long __stack_chk_guard = 0x95B5FF5A; +unsigned long __attribute__((weak)) __stack_chk_guard = 0x95B5FF5A; static unsigned long entry_count_offset; static unsigned long init_count_offset; diff --git a/lib/sbi/sbi_ubsan.c b/lib/sbi/sbi_ubsan.c new file mode 100644 index 00000000..271be7b0 --- /dev/null +++ b/lib/sbi/sbi_ubsan.c @@ -0,0 +1,924 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 The NetBSD Foundation, Inc. + * + * Author: Marcos Oduardo + */ + +#ifdef UBSAN_ENABLED + +#include +#include + +/* Undefined Behavior specific defines and structures defined by the compiler ABI */ + +#define KIND_INTEGER 0 +#define KIND_FLOAT 1 +#define KIND_UNKNOWN UINT16_MAX + +volatile unsigned long sbi_ubsan_report_count; + +struct CSourceLocation { + char *mFilename; + uint32_t mLine; + uint32_t mColumn; +}; + +struct CTypeDescriptor { + uint16_t mTypeKind; + uint16_t mTypeInfo; + uint8_t mTypeName[1]; +}; + +struct COverflowData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CUnreachableData { + struct CSourceLocation mLocation; +}; + +struct CCFICheckFailData { + uint8_t mCheckKind; + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CDynamicTypeCacheMissData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + void *mTypeInfo; + uint8_t mTypeCheckKind; +}; + +struct CFunctionTypeMismatchData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CInvalidBuiltinData { + struct CSourceLocation mLocation; + uint8_t mKind; +}; + +struct CInvalidValueData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CNonNullArgData { + struct CSourceLocation mLocation; + struct CSourceLocation mAttributeLocation; + int mArgIndex; +}; + +struct CNonNullReturnData { + struct CSourceLocation mAttributeLocation; +}; + +struct COutOfBoundsData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mArrayType; + struct CTypeDescriptor *mIndexType; +}; + +struct CPointerOverflowData { + struct CSourceLocation mLocation; +}; + +struct CShiftOutOfBoundsData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mLHSType; + struct CTypeDescriptor *mRHSType; +}; + +struct CTypeMismatchData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + unsigned long mLogAlignment; + uint8_t mTypeCheckKind; +}; + +struct CTypeMismatchData_v1 { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; + uint8_t mLogAlignment; + uint8_t mTypeCheckKind; +}; + +struct CVLABoundData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mType; +}; + +struct CFloatCastOverflowData { + struct CSourceLocation + mLocation; /* This field exists in this struct since 2015 August 11th */ + struct CTypeDescriptor *mFromType; + struct CTypeDescriptor *mToType; +}; + +struct CImplicitConversionData { + struct CSourceLocation mLocation; + struct CTypeDescriptor *mFromType; + struct CTypeDescriptor *mToType; + uint8_t mKind; +}; + +struct CAlignmentAssumptionData { + struct CSourceLocation mLocation; + struct CSourceLocation mAssumptionLocation; + struct CTypeDescriptor *mType; +}; + +/* Public symbols used in the instrumentation of the code generation part */ +void __ubsan_handle_add_overflow(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, + unsigned long ulLHS, + unsigned long ulRHS); +void __ubsan_handle_alignment_assumption(struct CAlignmentAssumptionData *pData, + unsigned long ulPointer, + unsigned long ulAlignment, + unsigned long ulOffset); +void __ubsan_handle_alignment_assumption_abort( + struct CAlignmentAssumptionData *pData, unsigned long ulPointer, + unsigned long ulAlignment, unsigned long ulOffset); +void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData); +void __ubsan_handle_divrem_overflow(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, + unsigned long ulLHS, + unsigned long ulRHS); +void __ubsan_handle_function_type_mismatch( + struct CFunctionTypeMismatchData *pData, unsigned long ulFunction); +void __ubsan_handle_function_type_mismatch_abort( + struct CFunctionTypeMismatchData *pData, unsigned long ulFunction); +void __ubsan_handle_function_type_mismatch_v1( + struct CFunctionTypeMismatchData *pData, unsigned long ulFunction, + unsigned long ulCalleeRTTI, unsigned long ulFnRTTI); +void __ubsan_handle_function_type_mismatch_v1_abort( + struct CFunctionTypeMismatchData *pData, unsigned long ulFunction, + unsigned long ulCalleeRTTI, unsigned long ulFnRTTI); +void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData); +void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData); +void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, + unsigned long ulVal); +void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, + unsigned long ulVal); +void __ubsan_handle_missing_return(struct CUnreachableData *pData); +void __ubsan_handle_mul_overflow(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, + unsigned long ulLHS, + unsigned long ulRHS); +void __ubsan_handle_negate_overflow(struct COverflowData *pData, + unsigned long ulOldVal); +void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, + unsigned long ulOldVal); +void __ubsan_handle_nullability_arg(struct CNonNullArgData *pData); +void __ubsan_handle_nullability_arg_abort(struct CNonNullArgData *pData); +void __ubsan_handle_nullability_return_v1( + struct CNonNullReturnData *pData, + struct CSourceLocation *pLocationPointer); +void __ubsan_handle_nullability_return_v1_abort( + struct CNonNullReturnData *pData, + struct CSourceLocation *pLocationPointer); +void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, + unsigned long ulIndex); +void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, + unsigned long ulIndex); +void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, + unsigned long ulBase, + unsigned long ulResult); +void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, + unsigned long ulBase, + unsigned long ulResult); +void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, + unsigned long ulLHS, + unsigned long ulRHS); +void __ubsan_handle_shift_out_of_bounds_abort( + struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, + unsigned long ulRHS); +void __ubsan_handle_sub_overflow(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS); +void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, + unsigned long ulLHS, + unsigned long ulRHS); +void __ubsan_handle_type_mismatch(struct CTypeMismatchData *pData, + unsigned long ulPointer); +void __ubsan_handle_type_mismatch_abort(struct CTypeMismatchData *pData, + unsigned long ulPointer); +void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, + unsigned long ulPointer); +void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, + unsigned long ulPointer); +void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, + unsigned long ulBound); +void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, + unsigned long ulBound); +void __ubsan_get_current_report_data(const char **ppOutIssueKind, + const char **ppOutMessage, + const char **ppOutFilename, + uint32_t *pOutLine, uint32_t *pOutCol, + char **ppOutMemoryAddr); +static void HandleOverflow(bool isFatal, struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS, + const char *szOperation); +static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, + unsigned long ulOldValue); +static void HandleBuiltinUnreachable(bool isFatal, + struct CUnreachableData *pData); +static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, + struct CTypeDescriptor *mType, + unsigned long mLogAlignment, + uint8_t mTypeCheckKind, unsigned long ulPointer); +static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, + unsigned long ulBound); +static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, + unsigned long ulIndex); +static void HandleShiftOutOfBounds(bool isFatal, + struct CShiftOutOfBoundsData *pData, + unsigned long ulLHS, unsigned long ulRHS); +static void HandleLoadInvalidValue(bool isFatal, + struct CInvalidValueData *pData, + unsigned long ulValue); +static void HandleInvalidBuiltin(bool isFatal, + struct CInvalidBuiltinData *pData); +static void HandleFunctionTypeMismatch(bool isFatal, + struct CFunctionTypeMismatchData *pData, + unsigned long ulFunction); +static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData); +static void HandlePointerOverflow(bool isFatal, + struct CPointerOverflowData *pData, + unsigned long ulBase, unsigned long ulResult); +static void HandleAlignmentAssumption(bool isFatal, + struct CAlignmentAssumptionData *pData, + unsigned long ulPointer, + unsigned long ulAlignment, + unsigned long ulOffset); + +#define NUMBER_SIGNED_BIT 1 +#define NUMBER_MAXLEN 128 +#define __arraycount(__a) (sizeof(__a) / sizeof(__a[0])) +#define __BIT(__n) (1UL << (__n)) +#define SEPARATOR sbi_printf("===========================================\n") +#define ACK_REPORTED (1U << 31) + +static bool isAlreadyReported(struct CSourceLocation *pLocation) +{ + uint32_t siOldValue; + volatile uint32_t *pLine; + + if (!pLocation) + return false; + + pLine = &pLocation->mLine; + + do { + siOldValue = *pLine; + } while (__sync_val_compare_and_swap(pLine, siOldValue, + siOldValue | ACK_REPORTED) != + siOldValue); + + if (!(siOldValue & ACK_REPORTED)) { + + sbi_ubsan_report_count++; + + return false; + } + + return true; +} + +static void HandleOverflow(bool isFatal, struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS, + const char *szOperation) +{ + if (!pData) { + return; + } + + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT; + + sbi_printf("UBSan: %s integer overflow: ", + is_signed ? "signed" : "unsigned"); + + if (is_signed) { + sbi_printf("%ld %s %ld ", (long)ulLHS, szOperation, + (long)ulRHS); + } else { + sbi_printf("%lu %s %lu ", ulLHS, szOperation, ulRHS); + } + + sbi_printf("cannot be represented in type %s\n", + pData->mType->mTypeName); + + SEPARATOR; +} + +static void HandleNegateOverflow(bool isFatal, struct COverflowData *pData, + unsigned long ulOldValue) +{ + if (!pData) { + return; + } + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT; + + sbi_printf("UBSan: Negation of "); + + if (is_signed) { + sbi_printf("%ld", (long)ulOldValue); + } else { + sbi_printf("%lu", ulOldValue); + } + + sbi_printf("cannot be represented in type %s\n", + pData->mType->mTypeName); + + SEPARATOR; +} + +static void HandleBuiltinUnreachable(bool isFatal, + struct CUnreachableData *pData) +{ + if (!pData) { + return; + } + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + SEPARATOR; +} + +const char *rgczTypeCheckKinds[] = { "load of", + "store to", + "reference binding to", + "member access within", + "member call on", + "constructor call on", + "downcast of", + "downcast of", + "upcast of", + "cast to virtual base of", + "_Nonnull binding to", + "dynamic operation on" }; + +static void HandleTypeMismatch(bool isFatal, struct CSourceLocation *mLocation, + struct CTypeDescriptor *mType, + unsigned long mLogAlignment, + uint8_t mTypeCheckKind, unsigned long ulPointer) +{ + + if ((!mLocation) || (!mType)) { + return; + } + + if (isAlreadyReported(mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + mLocation->mFilename, mLocation->mLine & ~ACK_REPORTED, + mLocation->mColumn); + + const char *kind = (mTypeCheckKind < __arraycount(rgczTypeCheckKinds)) + ? rgczTypeCheckKinds[mTypeCheckKind] + : "access to"; + + if (ulPointer == 0) { + sbi_printf("%s null pointer of type %s\n", kind, + mType->mTypeName); + } else if ( + (mLogAlignment - 1) & + ulPointer) { //mLogAlignment is converted on the wrapper function call + sbi_printf( + "%s misaligned address %p for type %s which requires %ld byte alignment\n", + kind, (void *)ulPointer, mType->mTypeName, + mLogAlignment); + } else { + sbi_printf( + "%s address %p with insufficient space for an object of type %s\n", + kind, (void *)ulPointer, mType->mTypeName); + } + SEPARATOR; +} + +static void HandleVlaBoundNotPositive(bool isFatal, struct CVLABoundData *pData, + unsigned long ulBound) +{ + if (!pData) { + return; + } + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + sbi_printf("variable length array bound value "); + + bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT; + + if (is_signed) { + sbi_printf("%ld", (long)ulBound); + } else { + sbi_printf("%lu", ulBound); + } + + sbi_printf(" <= 0\n"); + + SEPARATOR; +} + +static void HandleOutOfBounds(bool isFatal, struct COutOfBoundsData *pData, + unsigned long ulIndex) +{ + if (!pData) { + return; + } + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + bool is_signed = pData->mIndexType->mTypeInfo & NUMBER_SIGNED_BIT; + + if (is_signed) { + sbi_printf("index %ld", (long)ulIndex); + } else { + sbi_printf("index %lu", ulIndex); + } + + sbi_printf(" is out of range for type %s\n", + pData->mArrayType->mTypeName); + + SEPARATOR; +} + +static bool isNegativeNumber(struct CTypeDescriptor *pType, unsigned long ulVal) +{ + if (!(pType->mTypeInfo & NUMBER_SIGNED_BIT)) { + return false; + } + + return (long)ulVal < 0; +} + +static size_t type_width(struct CTypeDescriptor *pType) +{ + return 1UL << (pType->mTypeInfo >> 1); +} + +static void HandleShiftOutOfBounds(bool isFatal, + struct CShiftOutOfBoundsData *pData, + unsigned long ulLHS, unsigned long ulRHS) +{ + if (!pData) { + return; + } + + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + if (isNegativeNumber(pData->mRHSType, ulRHS)) { + sbi_printf("shift exponent %ld is negative\n", (long)ulRHS); + } else if (ulRHS >= type_width(pData->mLHSType)) { + sbi_printf( + "shift exponent %lu is too large for %lu-bit type %s\n", + ulRHS, (unsigned long)type_width(pData->mLHSType), + pData->mLHSType->mTypeName); + + } else if (isNegativeNumber(pData->mLHSType, ulLHS)) { + sbi_printf("left shift of negative value %ld\n", (long)ulLHS); + } else { + sbi_printf( + "left shift of %lu by %lu places cannot be represented in type %s\n", + ulLHS, ulRHS, pData->mLHSType->mTypeName); + } + + SEPARATOR; +} + +static void HandleLoadInvalidValue(bool isFatal, + struct CInvalidValueData *pData, + unsigned long ulValue) +{ + if (!pData) { + return; + } + + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + bool is_signed = pData->mType->mTypeInfo & NUMBER_SIGNED_BIT; + + sbi_printf("load of value "); + + if (is_signed) { + sbi_printf("%ld ", (long)ulValue); + } else { + sbi_printf("%lu ", ulValue); + } + + sbi_printf("is not a valid value for type %s\n", + pData->mType->mTypeName); + + SEPARATOR; +} + +const char *rgczBuiltinCheckKinds[] = { "ctz()", "clz()" }; + +static void HandleInvalidBuiltin(bool isFatal, + struct CInvalidBuiltinData *pData) +{ + if (!pData) { + return; + } + + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + const char *builtin = + (pData->mKind < __arraycount(rgczBuiltinCheckKinds)) + ? rgczBuiltinCheckKinds[pData->mKind] + : "unknown builtin"; + + sbi_printf("passing zero to %s, which is not a valid argument\n", + builtin); + + SEPARATOR; +} + +static void HandleFunctionTypeMismatch(bool isFatal, + struct CFunctionTypeMismatchData *pData, + unsigned long ulFunction) +{ + if (!pData) { + return; + } + + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + sbi_printf( + "call to function %#lx through pointer to incorrect function type %s\n", + ulFunction, pData->mType->mTypeName); + + SEPARATOR; +} + +static void HandleMissingReturn(bool isFatal, struct CUnreachableData *pData) +{ + if (!pData) { + return; + } + + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + sbi_printf( + "execution reached the end of a value-returning function without returning a value\n"); + + SEPARATOR; +} + +static void HandlePointerOverflow(bool isFatal, + struct CPointerOverflowData *pData, + unsigned long ulBase, unsigned long ulResult) +{ + if (!pData) { + return; + } + + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + sbi_printf("pointer expression with base %#lx overflowed to %#lx\n", + ulBase, ulResult); + + SEPARATOR; +} + +static void HandleAlignmentAssumption(bool isFatal, + struct CAlignmentAssumptionData *pData, + unsigned long ulPointer, + unsigned long ulAlignment, + unsigned long ulOffset) +{ + + if (!pData) { + return; + } + + if (isAlreadyReported(&pData->mLocation)) { + return; + } + + SEPARATOR; + + sbi_printf("UBSan: Undefined Behavior in %s:%u:%u\n", + pData->mLocation.mFilename, + pData->mLocation.mLine & ~ACK_REPORTED, + pData->mLocation.mColumn); + + unsigned long ulRealPointer = ulPointer - ulOffset; + sbi_printf("alignment assumption of %lu for pointer %p (offset %p)", + ulAlignment, (void *)ulRealPointer, (void *)ulOffset); + + if (pData->mAssumptionLocation.mFilename != NULL) { + sbi_printf(", assumption made in %s:%u:%u", + pData->mAssumptionLocation.mFilename, + pData->mAssumptionLocation.mLine, + pData->mAssumptionLocation.mColumn); + } + + sbi_printf("\n"); + + SEPARATOR; +} + +/* Definions of public symbols emitted by the instrumentation code */ +void __ubsan_handle_add_overflow(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS) +{ + HandleOverflow(false, pData, ulLHS, ulRHS, "+"); +} + +void __ubsan_handle_add_overflow_abort(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS) +{ + HandleOverflow(true, pData, ulLHS, ulRHS, "+"); +} + +void __ubsan_handle_sub_overflow(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS) +{ + HandleOverflow(false, pData, ulLHS, ulRHS, "-"); +} + +void __ubsan_handle_sub_overflow_abort(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS) +{ + HandleOverflow(true, pData, ulLHS, ulRHS, "-"); +} + +void __ubsan_handle_mul_overflow(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS) +{ + HandleOverflow(false, pData, ulLHS, ulRHS, "*"); +} + +void __ubsan_handle_mul_overflow_abort(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS) +{ + HandleOverflow(true, pData, ulLHS, ulRHS, "*"); +} + +void __ubsan_handle_negate_overflow(struct COverflowData *pData, + unsigned long ulOldValue) +{ + HandleNegateOverflow(false, pData, ulOldValue); +} + +void __ubsan_handle_negate_overflow_abort(struct COverflowData *pData, + unsigned long ulOldValue) +{ + HandleNegateOverflow(true, pData, ulOldValue); +} + +void __ubsan_handle_divrem_overflow(struct COverflowData *pData, + unsigned long ulLHS, unsigned long ulRHS) +{ + HandleOverflow(false, pData, ulLHS, ulRHS, "divrem"); +} + +void __ubsan_handle_divrem_overflow_abort(struct COverflowData *pData, + unsigned long ulLHS, + unsigned long ulRHS) +{ + HandleOverflow(true, pData, ulLHS, ulRHS, "divrem"); +} + +void __ubsan_handle_type_mismatch_v1(struct CTypeMismatchData_v1 *pData, + unsigned long ulPointer) +{ + HandleTypeMismatch(false, &pData->mLocation, pData->mType, + __BIT(pData->mLogAlignment), pData->mTypeCheckKind, + ulPointer); +} + +void __ubsan_handle_type_mismatch_v1_abort(struct CTypeMismatchData_v1 *pData, + unsigned long ulPointer) +{ + HandleTypeMismatch(true, &pData->mLocation, pData->mType, + __BIT(pData->mLogAlignment), pData->mTypeCheckKind, + ulPointer); +} + +void __ubsan_handle_out_of_bounds(struct COutOfBoundsData *pData, + unsigned long ulIndex) +{ + HandleOutOfBounds(false, pData, ulIndex); +} + +void __ubsan_handle_out_of_bounds_abort(struct COutOfBoundsData *pData, + unsigned long ulIndex) +{ + HandleOutOfBounds(true, pData, ulIndex); +} + +void __ubsan_handle_shift_out_of_bounds(struct CShiftOutOfBoundsData *pData, + unsigned long ulLHS, + unsigned long ulRHS) +{ + HandleShiftOutOfBounds(false, pData, ulLHS, ulRHS); +} + +void __ubsan_handle_shift_out_of_bounds_abort( + struct CShiftOutOfBoundsData *pData, unsigned long ulLHS, + unsigned long ulRHS) +{ + HandleShiftOutOfBounds(true, pData, ulLHS, ulRHS); +} + +void __ubsan_handle_pointer_overflow(struct CPointerOverflowData *pData, + unsigned long ulBase, + unsigned long ulResult) +{ + HandlePointerOverflow(false, pData, ulBase, ulResult); +} + +void __ubsan_handle_pointer_overflow_abort(struct CPointerOverflowData *pData, + unsigned long ulBase, + unsigned long ulResult) +{ + HandlePointerOverflow(true, pData, ulBase, ulResult); +} + +void __ubsan_handle_alignment_assumption(struct CAlignmentAssumptionData *pData, + unsigned long ulPointer, + unsigned long ulAlignment, + unsigned long ulOffset) +{ + HandleAlignmentAssumption(false, pData, ulPointer, ulAlignment, + ulOffset); +} + +void __ubsan_handle_alignment_assumption_abort( + struct CAlignmentAssumptionData *pData, unsigned long ulPointer, + unsigned long ulAlignment, unsigned long ulOffset) +{ + HandleAlignmentAssumption(true, pData, ulPointer, ulAlignment, + ulOffset); +} + +void __ubsan_handle_builtin_unreachable(struct CUnreachableData *pData) +{ + HandleBuiltinUnreachable(true, pData); +} + +void __ubsan_handle_invalid_builtin(struct CInvalidBuiltinData *pData) +{ + HandleInvalidBuiltin(true, pData); +} + +void __ubsan_handle_invalid_builtin_abort(struct CInvalidBuiltinData *pData) +{ + HandleInvalidBuiltin(true, pData); +} + +void __ubsan_handle_load_invalid_value(struct CInvalidValueData *pData, + unsigned long ulValue) +{ + HandleLoadInvalidValue(false, pData, ulValue); +} + +void __ubsan_handle_load_invalid_value_abort(struct CInvalidValueData *pData, + unsigned long ulValue) +{ + HandleLoadInvalidValue(true, pData, ulValue); +} + +void __ubsan_handle_missing_return(struct CUnreachableData *pData) +{ + HandleMissingReturn(true, pData); +} + +void __ubsan_handle_vla_bound_not_positive(struct CVLABoundData *pData, + unsigned long ulBound) +{ + HandleVlaBoundNotPositive(false, pData, ulBound); +} + +void __ubsan_handle_vla_bound_not_positive_abort(struct CVLABoundData *pData, + unsigned long ulBound) +{ + HandleVlaBoundNotPositive(true, pData, ulBound); +} + +void __ubsan_handle_function_type_mismatch( + struct CFunctionTypeMismatchData *pData, unsigned long ulFunction) +{ + HandleFunctionTypeMismatch(false, pData, ulFunction); +} + +void __ubsan_handle_function_type_mismatch_abort( + struct CFunctionTypeMismatchData *pData, unsigned long ulFunction) +{ + HandleFunctionTypeMismatch(true, pData, ulFunction); +} +#endif diff --git a/lib/sbi/tests/objects.mk b/lib/sbi/tests/objects.mk index 3ee1c635..40c441e1 100644 --- a/lib/sbi/tests/objects.mk +++ b/lib/sbi/tests/objects.mk @@ -24,3 +24,8 @@ libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_bitops_test.o carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += string_test_suite libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_string_test.o + +ifeq ($(UBSAN),y) +carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += ubsan_test_suite +libsbi-objs-$(CONFIG_SBIUNIT) += tests/sbi_ubsan_test.o +endif diff --git a/lib/sbi/tests/sbi_ubsan_test.c b/lib/sbi/tests/sbi_ubsan_test.c new file mode 100644 index 00000000..23409cb1 --- /dev/null +++ b/lib/sbi/tests/sbi_ubsan_test.c @@ -0,0 +1,114 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Author: Marcos Oduardo + */ + +#include +#include +#include +#include + +#define UBSAN_EXPECT_FIRES(test, stmt) \ + do { \ + unsigned long _before = sbi_ubsan_report_count; \ + stmt; \ + SBIUNIT_EXPECT_NE(test, sbi_ubsan_report_count, _before); \ + } while (0) + +static void test_ubsan_add_overflow(struct sbiunit_test_case *test) +{ + volatile int a = 0x7FFFFFFF; //INT_MAx + volatile int b = 1; + volatile int c; + UBSAN_EXPECT_FIRES(test, c = a + b); + (void)c; +} + +static void test_ubsan_sub_overflow(struct sbiunit_test_case *test) +{ + volatile int a = 0x80000000; //INT_MIN + volatile int b = 1; + volatile int c; + UBSAN_EXPECT_FIRES(test, c = a - b); + (void)c; +} + +static void test_ubsan_mul_overflow(struct sbiunit_test_case *test) +{ + volatile int a = 0x7FFFFFFF; + volatile int b = 2; + volatile int c; + UBSAN_EXPECT_FIRES(test, c = a * b); + (void)c; +} + +static void test_ubsan_divrem(struct sbiunit_test_case *test) +{ + volatile int a = 10; + volatile int b = 0; + volatile int c; + UBSAN_EXPECT_FIRES(test, c = a / b); + (void)c; +} + +static void test_ubsan_oob(struct sbiunit_test_case *test) +{ + volatile int idx = 5; + int arr[3] = { 1, 2, 3 }; + volatile int val; + UBSAN_EXPECT_FIRES(test, val = arr[idx]); + (void)val; +} + +static void test_ubsan_shift_too_large(struct sbiunit_test_case *test) +{ + volatile unsigned long val = 1; + volatile int shift = 64; + volatile unsigned long res; + UBSAN_EXPECT_FIRES(test, res = val << shift); + (void)res; +} + +static void test_ubsan_shift_negative(struct sbiunit_test_case *test) +{ + volatile int val = 1; + volatile int shift = -1; + volatile int res; + + UBSAN_EXPECT_FIRES(test, res = val << shift); + (void)res; +} + +static void test_ubsan_load_invalid_bool(struct sbiunit_test_case *test) +{ + volatile char bool_val = 5; + volatile bool *b_ptr = (bool *)&bool_val; + volatile int taken = 0; + UBSAN_EXPECT_FIRES(test, if (*b_ptr) taken = 1); + (void)taken; +} + +static void test_ubsan_pointer_overflow(struct sbiunit_test_case *test) +{ + volatile uintptr_t base = 0xFFFFFFFFFFFFFFFEUL; + volatile char *ptr = (char *)base; + volatile char *res; + UBSAN_EXPECT_FIRES(test, res = ptr + 5); + (void)res; +} + +static struct sbiunit_test_case ubsan_tests[] = { + SBIUNIT_TEST_CASE(test_ubsan_add_overflow), + SBIUNIT_TEST_CASE(test_ubsan_sub_overflow), + SBIUNIT_TEST_CASE(test_ubsan_mul_overflow), + SBIUNIT_TEST_CASE(test_ubsan_divrem), + SBIUNIT_TEST_CASE(test_ubsan_oob), + SBIUNIT_TEST_CASE(test_ubsan_shift_too_large), + SBIUNIT_TEST_CASE(test_ubsan_shift_negative), + SBIUNIT_TEST_CASE(test_ubsan_load_invalid_bool), + SBIUNIT_TEST_CASE(test_ubsan_pointer_overflow), + SBIUNIT_END_CASE, +}; + +SBIUNIT_TEST_SUITE(ubsan_test_suite, ubsan_tests); diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk index c4a8fee2..3f190625 100644 --- a/platform/generic/objects.mk +++ b/platform/generic/objects.mk @@ -39,5 +39,10 @@ ifeq ($(PLATFORM_RISCV_XLEN), 32) else # This needs to be 2MB aligned for 64-bit system FW_PAYLOAD_OFFSET=0x200000 + ifeq ($(UBSAN), y) + # FW payload size overlaps data for 2MB + FW_PAYLOAD_OFFSET=0x400000 + endif + endif FW_PAYLOAD_FDT_OFFSET=$(FW_JUMP_FDT_OFFSET) -- 2.53.0 From peter.lin at sifive.com Sat May 16 00:29:06 2026 From: peter.lin at sifive.com (Yu-Chien Peter Lin) Date: Sat, 16 May 2026 15:29:06 +0800 Subject: [PATCH v3] lib: utils: fdt_domain: add root-regions-inheritance policy Message-ID: <20260516072906.1427203-1-peter.lin@sifive.com> Introduce root-regions-inheritance DT property to control copying of root domain memregions. Support 'all' and 'm-only' modes, always inheriting firmware and M-only regions; behavior matches m-only policy when property is absent. Signed-off-by: Yu-Chien Peter Lin --- Changes v1->v2: - Addressed feedback from Anup: https://lists.infradead.org/pipermail/opensbi/2026-May/009850.html Changes v2->v3: - Fail early when unsupported value is found --- docs/domain_support.md | 7 +++++ lib/utils/fdt/fdt_domain.c | 53 ++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/docs/domain_support.md b/docs/domain_support.md index 93186c4a..77122330 100644 --- a/docs/domain_support.md +++ b/docs/domain_support.md @@ -159,6 +159,13 @@ The DT properties of a domain instance DT node are as follows: * **possible-harts** (Optional) - The list of CPU DT node phandles for the the domain instance. This list represents the possible HARTs of the domain instance. +* **root-regions-inheritance** (Optional) - A string property controlling + how memory regions are inherited from **the ROOT domain**, which are then + overlaid with regions specified in the **regions** property for additional + restrictions. The allowed values are: + * "all" - inherit all memory regions from **the ROOT domain** + * "m-only" - inherit M-mode only memory regions from **the ROOT domain** + If this DT property is absent, behavior is the same as "m-only". * **regions** (Optional) - The list of domain memory region DT node phandle and access permissions for the domain instance. Each list entry is a pair of DT node phandle and access permissions. The access permissions are diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c index b2fa8633..b31254af 100644 --- a/lib/utils/fdt/fdt_domain.c +++ b/lib/utils/fdt/fdt_domain.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -237,7 +238,9 @@ skip_device_disable: fdt_nop_node(fdt, poffset); } -#define FDT_DOMAIN_REGION_MAX_COUNT 16 +#define FDT_DOMAIN_REGION_MAX_COUNT 16 +#define FDT_ROOT_REGION_INHERIT_M_ONLY 0 +#define FDT_ROOT_REGION_INHERIT_ALL 1 struct parse_region_data { struct sbi_domain *dom; @@ -309,12 +312,14 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) u32 val32; u64 val64; const u32 *val; + const char *inherit; struct sbi_domain *dom; struct sbi_hartmask *mask; struct sbi_hartmask assign_mask; struct parse_region_data preg; int *cold_domain_offset = opaque; struct sbi_domain_memregion *reg; + int inheritance_mode = FDT_ROOT_REGION_INHERIT_M_ONLY; int i, err = 0, len, cpus_offset, cpu_offset, doffset; dom = sbi_zalloc(sizeof(*dom)); @@ -373,20 +378,42 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) if (err) goto fail_free_all; - /* - * Copy over root domain memregions which don't allow - * read, write and execute from lower privilege modes. - * - * These root domain memregions without read, write, - * and execute permissions include: - * 1) firmware region protecting the firmware memory - * 2) mmio regions protecting M-mode only mmio devices - */ + /* Determine root domain regions inheritance behavior. */ + inherit = fdt_getprop(fdt, domain_offset, + "root-regions-inheritance", &len); + if (inherit && len > 0) { + if (!strcmp(inherit, "all")) + inheritance_mode = FDT_ROOT_REGION_INHERIT_ALL; + else if (!strcmp(inherit, "m-only")) + inheritance_mode = FDT_ROOT_REGION_INHERIT_M_ONLY; + else { + sbi_printf("%s: domain \"%s\" has unsupported " + "root-regions-inheritance=\"%s\"\n", + __func__, dom->name, inherit); + err = SBI_EINVAL; + goto fail_free_all; + } + } + + /* Copy over root domain memregions according to inheritance_mode. */ sbi_domain_for_each_memregion(&root, reg) { - if ((reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) || - (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) || - (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)) + bool copy = false; + + switch (inheritance_mode) { + case FDT_ROOT_REGION_INHERIT_ALL: + copy = true; + break; + case FDT_ROOT_REGION_INHERIT_M_ONLY: + if (SBI_DOMAIN_MEMREGION_IS_FIRMWARE(reg->flags) || + SBI_DOMAIN_MEMREGION_M_ONLY_ACCESS(reg->flags)) { + copy = true; + } + break; + } + + if (!copy) continue; + if (preg.max_regions <= preg.region_count) { err = SBI_EINVAL; goto fail_free_all; -- 2.43.7 From dave.patel at riscstar.com Sat May 16 03:07:44 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Sat, 16 May 2026 11:07:44 +0100 Subject: [PATCH v5 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260516100746.13502-1-dave.patel@riscstar.com> References: <20260516100746.13502-1-dave.patel@riscstar.com> Message-ID: <20260516100746.13502-2-dave.patel@riscstar.com> From: Dave Patel Eager context switch: Add support for saving and restoring RISC-V vector extension state in OpenSBI. This introduces a per-hart vector context structure and helper routines to perform full context save and restore. The vector context includes vcsr CSRs along with storage for all 32 vector registers. The register state is saved and restored using byte-wise vector load/store instructions (vs8r/vl8r). The implementation follows an eager context switching model where the entire vector state is saved and restored on every context switch. This provides a simple and deterministic mechanism without requiring lazy trap-based management. Signed-off-by: Dave Patel --- include/sbi/sbi_vector.h | 28 ++++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_vector.c | 109 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+) create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_vector.c diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h new file mode 100644 index 00000000..bbd857c3 --- /dev/null +++ b/include/sbi/sbi_vector.h @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#ifndef __SBI_VECTOR_H__ +#define __SBI_VECTOR_H__ + +#include + +struct sbi_vector_context { + unsigned long vcsr; + unsigned long vstart; + + /* size depends on VLEN */ + uint8_t vregs[]; +}; + +void sbi_vector_save(struct sbi_vector_context *dst); +void sbi_vector_restore(const struct sbi_vector_context *src); +unsigned long vector_vlenb(void); + +#endif //__SBI_VECTOR_H__ + diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 97cc4521..ddb2e7ac 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o +libsbi-objs-y += sbi_vector.o diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c new file mode 100644 index 00000000..1d2ac944 --- /dev/null +++ b/lib/sbi/sbi_vector.c @@ -0,0 +1,109 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENSBI_CC_SUPPORT_VECTOR + +unsigned long vector_vlenb(void) +{ + unsigned long vlenb = 0; + + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "csrr %0, vlenb\n\t" + ".option pop\n\t" + : "=r"(vlenb) + : + : "memory"); + + return vlenb; +} + +void sbi_vector_save(struct sbi_vector_context *dst) +{ + if (!dst) + return; + + /* Step 1: Save CSRs */ + dst->vcsr = csr_read(vcsr); + dst->vstart = csr_read(vstart); + + ulong vlenb = vector_vlenb(); + uint8_t *base = dst->vregs; + + /* Step 3: Save vector registers */ +#define SAVE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vs8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + SAVE_VREG(0); + SAVE_VREG(8); + SAVE_VREG(16); + SAVE_VREG(24); + +#undef SAVE_VREG +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ + if (!src) + return; + + const uint8_t *base = src->vregs; + ulong vlenb = vector_vlenb(); + + /* Step 2: Restore vector registers */ +#define RESTORE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vl8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + RESTORE_VREG(0); + RESTORE_VREG(8); + RESTORE_VREG(16); + RESTORE_VREG(24); +#undef RESTORE_VREG + + /* Step 3: Restore CSR's last */ + /* Restore CSRs first */ + csr_write(vcsr, src->vcsr); + csr_write(vstart, src->vstart); +} + +#else + +void sbi_vector_save(struct sbi_vector_context *dst) +{ +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ +} + +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ -- 2.43.0 From dave.patel at riscstar.com Sat May 16 03:07:43 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Sat, 16 May 2026 11:07:43 +0100 Subject: [PATCH v5 0/3] Add eager FP and RISC-V vector context switching support Message-ID: <20260516100746.13502-1-dave.patel@riscstar.com> From: Dave Patel 1) fcsr to unsigned long. 2) removed redundant line 3) merging path 3 and 4 into patch 3. Dave Patel (3): lib: sbi: Add RISC-V vector context save/restore support lib: sbi: Add floating-point context save/restore support. lib: sbi: domain FP/Vector context support for context switch include/sbi/sbi_fp.h | 26 +++++ include/sbi/sbi_hart.h | 6 ++ include/sbi/sbi_vector.h | 28 +++++ lib/sbi/objects.mk | 2 + lib/sbi/sbi_domain_context.c | 40 ++++++++ lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++ lib/sbi/sbi_hart.c | 3 + lib/sbi/sbi_vector.c | 109 ++++++++++++++++++++ 8 files changed, 406 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_fp.c create mode 100644 lib/sbi/sbi_vector.c -- 2.43.0 From dave.patel at riscstar.com Sat May 16 03:07:46 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Sat, 16 May 2026 11:07:46 +0100 Subject: [PATCH v5 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260516100746.13502-1-dave.patel@riscstar.com> References: <20260516100746.13502-1-dave.patel@riscstar.com> Message-ID: <20260516100746.13502-4-dave.patel@riscstar.com> From: Dave Patel This patch adds proper support for per-domain floating-point (FP) and vector (V) contexts in the domain context switch logic. Each domain now maintains its own FP and vector state, which is saved and restored during domain switches. Conditionalize FP and Vector save/restore based on extensions, unconditional save and restore of floating-point (FP) and Vector registers fails on generic platform firmware. This firmware must run on multiple platforms that may lack these extensions. Address this by conditionally executing FP save/restore only if the underlying hart supports the F or D extensions. Similarly, perform Vector save/restore only if the hart supports the Vector extension. Changes include: - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' to allocate and free per-domain FP and vector context. - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. - Updated `switch_to_next_domain_context()` to save/restore FP and vector contexts safely: - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. - Added runtime checks for FP and vector extensions where needed. - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() to check for these capabilities before performing the context switches This improves support for multi-domain systems with FP and Vector extensions, and prevents corruption of FP/Vector state during domain switches. Signed-off-by: Dave Patel --- include/sbi/sbi_hart.h | 6 ++++++ lib/sbi/sbi_domain_context.c | 40 ++++++++++++++++++++++++++++++++++++ lib/sbi/sbi_hart.c | 3 +++ 3 files changed, 49 insertions(+) diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index a788b34c..68a01b97 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -87,6 +87,12 @@ enum sbi_hart_extensions { SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, /** Hart has Xsfcease extension */ SBI_HART_EXT_XSIFIVE_CEASE, + /** Hart has V extension */ + SBI_HART_EXT_V, + /** Hart has F extension */ + SBI_HART_EXT_F, + /** Hart has D extension */ + SBI_HART_EXT_D, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index 158f4990..46485728 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include /** Context representation for a hart within a domain */ struct hart_context { @@ -55,6 +57,11 @@ struct hart_context { struct hart_context *prev_ctx; /** Is context initialized and runnable */ bool initialized; + + /** float context state */ + struct sbi_fp_context fp_ctx; + /** vector context state */ + struct sbi_vector_context *vec_ctx; }; static struct sbi_domain_data dcpriv; @@ -143,6 +150,25 @@ static int switch_to_next_domain_context(struct hart_context *ctx, if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); + /* Make sure FS and VS is on before context switch */ + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); + + /* Eager context switch F and V */ + + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_F) || + sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_D)) { + sbi_fp_save(&ctx->fp_ctx); + sbi_fp_restore(&dom_ctx->fp_ctx); + } + + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V)) { + sbi_vector_save(ctx->vec_ctx); + sbi_vector_restore(dom_ctx->vec_ctx); + } + /* Save current trap state and restore target domain's trap state */ trap_ctx = sbi_trap_get_context(scratch); sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); @@ -180,6 +206,20 @@ static int hart_context_init(u32 hartindex) if (!ctx) return SBI_ENOMEM; + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V)) { + unsigned long vlenb = vector_vlenb(); + /* Calculate size: base struct + 32 registers of vlenb size */ + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); + + /* Allocate the vector context pointer */ + ctx->vec_ctx = sbi_zalloc(vec_size); + if (!ctx->vec_ctx) { + sbi_free(ctx); + return SBI_ENOMEM; + } + } + /* Bind context and domain */ ctx->dom = dom; hart_context_set(dom, hartindex, ctx); diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 60e95bca..b5e0ee10 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -396,6 +396,9 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), + __SBI_HART_EXT_DATA(v, SBI_HART_EXT_V), + __SBI_HART_EXT_DATA(f, SBI_HART_EXT_F), + __SBI_HART_EXT_DATA(d, SBI_HART_EXT_D), }; _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), -- 2.43.0 From dave.patel at riscstar.com Sat May 16 03:07:45 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Sat, 16 May 2026 11:07:45 +0100 Subject: [PATCH v5 2/3] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260516100746.13502-1-dave.patel@riscstar.com> References: <20260516100746.13502-1-dave.patel@riscstar.com> Message-ID: <20260516100746.13502-3-dave.patel@riscstar.com> From: Dave Patel Add support for saving and restoring RISC-V floating-point (F/D) extension state in OpenSBI. This introduces a floating-point context structure and helper routines to perform full context save and restore. The floating-point context includes storage for all 32 FPi registers (f0?f31) along with the fcsr control and status register. The register state is saved and restored using double-precision load/store instructions (fsd/fld), and single-precision load/store instructions (fsw/flw) on an RV64 system with F and D-extension support. The implementation follows an eager context switching model where the entire FP state is saved and restored on every context switch. This avoids the need for trap-based lazy management and keeps the design simple and deterministic. Signed-off-by: Dave Patel " --- include/sbi/sbi_fp.h | 26 ++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 lib/sbi/sbi_fp.c diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h new file mode 100644 index 00000000..a00756fc --- /dev/null +++ b/include/sbi/sbi_fp.h @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ +#ifndef __SBI_FP_H__ +#define __SBI_FP_H__ + +#include + +struct sbi_fp_context { +#if __riscv_d + uint64_t f[32]; +#else + uint32_t f[32]; +#endif + unsigned long fcsr; +}; + +void sbi_fp_save(struct sbi_fp_context *dst); +void sbi_fp_restore(const struct sbi_fp_context *src); + +#endif //__SBI_VECTOR_H__ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index ddb2e7ac..d8182383 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -110,3 +110,4 @@ libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o libsbi-objs-y += sbi_vector.o +libsbi-objs-y += sbi_fp.o diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c new file mode 100644 index 00000000..887bca4d --- /dev/null +++ b/lib/sbi/sbi_fp.c @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include + +#if defined(__riscv_f) || defined(__riscv_d) + +void sbi_fp_save(struct sbi_fp_context *dst) +{ + if (!dst) + return; + +#if defined(__riscv_d) + asm volatile( + "fsd f0, 0(%0)\n" + "fsd f1, 8(%0)\n" + "fsd f2, 16(%0)\n" + "fsd f3, 24(%0)\n" + "fsd f4, 32(%0)\n" + "fsd f5, 40(%0)\n" + "fsd f6, 48(%0)\n" + "fsd f7, 56(%0)\n" + "fsd f8, 64(%0)\n" + "fsd f9, 72(%0)\n" + "fsd f10, 80(%0)\n" + "fsd f11, 88(%0)\n" + "fsd f12, 96(%0)\n" + "fsd f13, 104(%0)\n" + "fsd f14, 112(%0)\n" + "fsd f15, 120(%0)\n" + "fsd f16, 128(%0)\n" + "fsd f17, 136(%0)\n" + "fsd f18, 144(%0)\n" + "fsd f19, 152(%0)\n" + "fsd f20, 160(%0)\n" + "fsd f21, 168(%0)\n" + "fsd f22, 176(%0)\n" + "fsd f23, 184(%0)\n" + "fsd f24, 192(%0)\n" + "fsd f25, 200(%0)\n" + "fsd f26, 208(%0)\n" + "fsd f27, 216(%0)\n" + "fsd f28, 224(%0)\n" + "fsd f29, 232(%0)\n" + "fsd f30, 240(%0)\n" + "fsd f31, 248(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#else + asm volatile( + "fsw f0, 0(%0)\n" + "fsw f1, 4(%0)\n" + "fsw f2, 8(%0)\n" + "fsw f3, 12(%0)\n" + "fsw f4, 16(%0)\n" + "fsw f5, 20(%0)\n" + "fsw f6, 24(%0)\n" + "fsw f7, 28(%0)\n" + "fsw f8, 32(%0)\n" + "fsw f9, 36(%0)\n" + "fsw f10, 40(%0)\n" + "fsw f11, 44(%0)\n" + "fsw f12, 48(%0)\n" + "fsw f13, 52(%0)\n" + "fsw f14, 56(%0)\n" + "fsw f15, 60(%0)\n" + "fsw f16, 64(%0)\n" + "fsw f17, 68(%0)\n" + "fsw f18, 72(%0)\n" + "fsw f19, 76(%0)\n" + "fsw f20, 80(%0)\n" + "fsw f21, 84(%0)\n" + "fsw f22, 88(%0)\n" + "fsw f23, 92(%0)\n" + "fsw f24, 96(%0)\n" + "fsw f25, 100(%0)\n" + "fsw f26, 104(%0)\n" + "fsw f27, 108(%0)\n" + "fsw f28, 112(%0)\n" + "fsw f29, 116(%0)\n" + "fsw f30, 120(%0)\n" + "fsw f31, 124(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#endif //__riscv_d + + dst->fcsr = csr_read(CSR_FCSR); +} + +void sbi_fp_restore(const struct sbi_fp_context *src) +{ + if (!src) + return; + +#if defined(__riscv_d) + asm volatile( + "fld f0, 0(%0)\n" + "fld f1, 8(%0)\n" + "fld f2, 16(%0)\n" + "fld f3, 24(%0)\n" + "fld f4, 32(%0)\n" + "fld f5, 40(%0)\n" + "fld f6, 48(%0)\n" + "fld f7, 56(%0)\n" + "fld f8, 64(%0)\n" + "fld f9, 72(%0)\n" + "fld f10, 80(%0)\n" + "fld f11, 88(%0)\n" + "fld f12, 96(%0)\n" + "fld f13, 104(%0)\n" + "fld f14, 112(%0)\n" + "fld f15, 120(%0)\n" + "fld f16, 128(%0)\n" + "fld f17, 136(%0)\n" + "fld f18, 144(%0)\n" + "fld f19, 152(%0)\n" + "fld f20, 160(%0)\n" + "fld f21, 168(%0)\n" + "fld f22, 176(%0)\n" + "fld f23, 184(%0)\n" + "fld f24, 192(%0)\n" + "fld f25, 200(%0)\n" + "fld f26, 208(%0)\n" + "fld f27, 216(%0)\n" + "fld f28, 224(%0)\n" + "fld f29, 232(%0)\n" + "fld f30, 240(%0)\n" + "fld f31, 248(%0)\n" + : + : "r"(src->f) + : "memory" + ); +#else + + asm volatile( + "flw f0, 0(%0)\n" + "flw f1, 4(%0)\n" + "flw f2, 8(%0)\n" + "flw f3, 12(%0)\n" + "flw f4, 16(%0)\n" + "flw f5, 20(%0)\n" + "flw f6, 24(%0)\n" + "flw f7, 28(%0)\n" + "flw f8, 32(%0)\n" + "flw f9, 36(%0)\n" + "flw f10, 40(%0)\n" + "flw f11, 44(%0)\n" + "flw f12, 48(%0)\n" + "flw f13, 52(%0)\n" + "flw f14, 56(%0)\n" + "flw f15, 60(%0)\n" + "flw f16, 64(%0)\n" + "flw f17, 68(%0)\n" + "flw f18, 72(%0)\n" + "flw f19, 76(%0)\n" + "flw f20, 80(%0)\n" + "flw f21, 84(%0)\n" + "flw f22, 88(%0)\n" + "flw f23, 92(%0)\n" + "flw f24, 96(%0)\n" + "flw f25, 100(%0)\n" + "flw f26, 104(%0)\n" + "flw f27, 108(%0)\n" + "flw f28, 112(%0)\n" + "flw f29, 116(%0)\n" + "flw f30, 120(%0)\n" + "flw f31, 124(%0)\n" + : + : "r"(src->f) + : "memory" + ); + +#endif + + csr_write(CSR_FCSR, src->fcsr); +} +#else +void sbi_fp_save(struct sbi_fp_context *dst) {} +void sbi_fp_restore(const struct sbi_fp_context *src) {} +#endif // FP present -- 2.43.0 From dave.patel at riscstar.com Sat May 16 03:23:09 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Sat, 16 May 2026 11:23:09 +0100 Subject: [PATCH v5 1/4] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: References: <20260515054208.151045-1-dave.patel@riscstar.com> <20260515054208.151045-2-dave.patel@riscstar.com> Message-ID: <7c06e1b8-8c42-4dcc-ac68-066a5bc6d567@riscstar.com> On 5/15/26 08:41, Anup Patel wrote: > On Fri, May 15, 2026 at 11:12?AM wrote: >> >> From: Dave Patel >> >> Eager context switch: Add support for saving and restoring RISC-V vector >> extension state in OpenSBI. This introduces a per-hart vector context >> structure and helper routines to perform full context save and restore. >> >> The vector context includes vcsr CSRs along with storage for all 32 vector >> registers. The register state is saved and restored using byte-wise vector >> load/store instructions (vs8r/vl8r). >> >> The implementation follows an eager context switching model where the entire >> vector state is saved and restored on every context switch. This provides a >> simple and deterministic mechanism without requiring lazy trap-based >> management. >> >> Signed-off-by: Dave Patel >> --- >> include/sbi/sbi_vector.h | 28 ++++++++++ >> lib/sbi/objects.mk | 1 + >> lib/sbi/sbi_vector.c | 109 +++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 138 insertions(+) >> create mode 100644 include/sbi/sbi_vector.h >> create mode 100644 lib/sbi/sbi_vector.c >> >> diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h >> new file mode 100644 >> index 00000000..bbd857c3 >> --- /dev/null >> +++ b/include/sbi/sbi_vector.h >> @@ -0,0 +1,28 @@ >> +/* >> + * SPDX-License-Identifier: BSD-2-Clause >> + * >> + * Copyright (c) 2026 RISCstar Solutions. >> + * >> + * Authors: >> + * Dave Patel >> + */ >> + >> +#ifndef __SBI_VECTOR_H__ >> +#define __SBI_VECTOR_H__ >> + >> +#include >> + >> +struct sbi_vector_context { >> + unsigned long vcsr; >> + unsigned long vstart; >> + >> + /* size depends on VLEN */ >> + uint8_t vregs[]; >> +}; >> + >> +void sbi_vector_save(struct sbi_vector_context *dst); >> +void sbi_vector_restore(const struct sbi_vector_context *src); >> +unsigned long vector_vlenb(void); >> + >> +#endif //__SBI_VECTOR_H__ >> + >> diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk >> index 97cc4521..ddb2e7ac 100644 >> --- a/lib/sbi/objects.mk >> +++ b/lib/sbi/objects.mk >> @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o >> libsbi-objs-y += sbi_unpriv.o >> libsbi-objs-y += sbi_expected_trap.o >> libsbi-objs-y += sbi_cppc.o >> +libsbi-objs-y += sbi_vector.o >> diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c >> new file mode 100644 >> index 00000000..1d2ac944 >> --- /dev/null >> +++ b/lib/sbi/sbi_vector.c >> @@ -0,0 +1,109 @@ >> +/* >> + * SPDX-License-Identifier: BSD-2-Clause >> + * >> + * Copyright (c) 2026 RISCstar Solutions. >> + * >> + * Authors: >> + * Dave Patel >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#ifdef OPENSBI_CC_SUPPORT_VECTOR >> + >> +unsigned long vector_vlenb(void) >> +{ >> + unsigned long vlenb = 0; >> + >> + asm volatile ( >> + ".option push\n\t" >> + ".option arch, +v\n\t" >> + "csrr %0, vlenb\n\t" >> + ".option pop\n\t" >> + : "=r"(vlenb) >> + : >> + : "memory"); >> + >> + return vlenb; >> +} >> + >> +void sbi_vector_save(struct sbi_vector_context *dst) >> +{ >> + if (!dst) >> + return; >> + >> + /* Step 1: Save CSRs */ >> + dst->vcsr = csr_read(vcsr); >> + dst->vstart = csr_read(vstart); >> + >> + ulong vlenb = vector_vlenb(); >> + uint8_t *base = dst->vregs; >> + >> + /* Step 3: Save vector registers */ >> +#define SAVE_VREG(i) \ >> + ({ \ >> + asm volatile( \ >> + " .option push\n\t" \ >> + " .option arch, +v\n\t" \ >> + " vs8r.v v" #i ", (%0)\n\t" \ >> + " .option pop\n\t" \ >> + :: "r"(base + (i) * vlenb) : "memory"); \ >> + }) \ >> + >> + SAVE_VREG(0); >> + SAVE_VREG(8); >> + SAVE_VREG(16); >> + SAVE_VREG(24); > > Same issue as FP save/restore, this will trap and crash > if Vector is disabled in mstatus.VS. > > For correct implementation, refer __riscv_v_vstate_save()/restore() > from /arch/riscv/include/asm/vector.h > >> + >> +#undef SAVE_VREG >> +} >> + >> +void sbi_vector_restore(const struct sbi_vector_context *src) >> +{ >> + if (!src) >> + return; >> + >> + const uint8_t *base = src->vregs; >> + ulong vlenb = vector_vlenb(); >> + >> + /* Step 2: Restore vector registers */ >> +#define RESTORE_VREG(i) \ >> + ({ \ >> + asm volatile( \ >> + " .option push\n\t" \ >> + " .option arch, +v\n\t" \ >> + " vl8r.v v" #i ", (%0)\n\t" \ >> + " .option pop\n\t" \ >> + :: "r"(base + (i) * vlenb) : "memory"); \ >> + }) \ >> + >> + RESTORE_VREG(0); >> + RESTORE_VREG(8); >> + RESTORE_VREG(16); >> + RESTORE_VREG(24); >> +#undef RESTORE_VREG >> + >> + /* Step 3: Restore CSR's last */ >> + /* Restore CSRs first */ >> + csr_write(vcsr, src->vcsr); >> + csr_write(vstart, src->vstart); >> +} >> + >> +#else >> + >> +void sbi_vector_save(struct sbi_vector_context *dst) >> +{ >> +} >> + >> +void sbi_vector_restore(const struct sbi_vector_context *src) >> +{ >> +} >> + >> +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ >> -- >> 2.43.0 >> >> >> -- >> opensbi mailing list >> opensbi at lists.infradead.org >> http://lists.infradead.org/mailman/listinfo/opensbi > > Regards, > Anup Hi Anup, Firstly thanks for all your review comments, I have endeavor to cover all of them, so please confirm. Rgarding this comment of mstatus.VS and FS (I think which includes 3 comments) I have already have code inplace in patch 3, please see below line from patch 3 + /* Make sure FS and VS is on before context switch */ + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); Since the context of Floating and Vector is more granular in Opensbi I thought of handle in context switch. So there is no issue of trap and crash, however there is still a question regarding if you are fine with this or you want it more fine grained inside its individual functions? Please let me know. Thanks Dave From anup at brainfault.org Sat May 16 09:00:10 2026 From: anup at brainfault.org (Anup Patel) Date: Sat, 16 May 2026 21:30:10 +0530 Subject: [PATCH v5 1/4] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <7c06e1b8-8c42-4dcc-ac68-066a5bc6d567@riscstar.com> References: <20260515054208.151045-1-dave.patel@riscstar.com> <20260515054208.151045-2-dave.patel@riscstar.com> <7c06e1b8-8c42-4dcc-ac68-066a5bc6d567@riscstar.com> Message-ID: On Sat, May 16, 2026 at 3:53?PM Dave Patel wrote: > > On 5/15/26 08:41, Anup Patel wrote: > > On Fri, May 15, 2026 at 11:12?AM wrote: > >> > >> From: Dave Patel > >> > >> Eager context switch: Add support for saving and restoring RISC-V vector > >> extension state in OpenSBI. This introduces a per-hart vector context > >> structure and helper routines to perform full context save and restore. > >> > >> The vector context includes vcsr CSRs along with storage for all 32 vector > >> registers. The register state is saved and restored using byte-wise vector > >> load/store instructions (vs8r/vl8r). > >> > >> The implementation follows an eager context switching model where the entire > >> vector state is saved and restored on every context switch. This provides a > >> simple and deterministic mechanism without requiring lazy trap-based > >> management. > >> > >> Signed-off-by: Dave Patel > >> --- > >> include/sbi/sbi_vector.h | 28 ++++++++++ > >> lib/sbi/objects.mk | 1 + > >> lib/sbi/sbi_vector.c | 109 +++++++++++++++++++++++++++++++++++++++ > >> 3 files changed, 138 insertions(+) > >> create mode 100644 include/sbi/sbi_vector.h > >> create mode 100644 lib/sbi/sbi_vector.c > >> > >> diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h > >> new file mode 100644 > >> index 00000000..bbd857c3 > >> --- /dev/null > >> +++ b/include/sbi/sbi_vector.h > >> @@ -0,0 +1,28 @@ > >> +/* > >> + * SPDX-License-Identifier: BSD-2-Clause > >> + * > >> + * Copyright (c) 2026 RISCstar Solutions. > >> + * > >> + * Authors: > >> + * Dave Patel > >> + */ > >> + > >> +#ifndef __SBI_VECTOR_H__ > >> +#define __SBI_VECTOR_H__ > >> + > >> +#include > >> + > >> +struct sbi_vector_context { > >> + unsigned long vcsr; > >> + unsigned long vstart; > >> + > >> + /* size depends on VLEN */ > >> + uint8_t vregs[]; > >> +}; > >> + > >> +void sbi_vector_save(struct sbi_vector_context *dst); > >> +void sbi_vector_restore(const struct sbi_vector_context *src); > >> +unsigned long vector_vlenb(void); > >> + > >> +#endif //__SBI_VECTOR_H__ > >> + > >> diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > >> index 97cc4521..ddb2e7ac 100644 > >> --- a/lib/sbi/objects.mk > >> +++ b/lib/sbi/objects.mk > >> @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o > >> libsbi-objs-y += sbi_unpriv.o > >> libsbi-objs-y += sbi_expected_trap.o > >> libsbi-objs-y += sbi_cppc.o > >> +libsbi-objs-y += sbi_vector.o > >> diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c > >> new file mode 100644 > >> index 00000000..1d2ac944 > >> --- /dev/null > >> +++ b/lib/sbi/sbi_vector.c > >> @@ -0,0 +1,109 @@ > >> +/* > >> + * SPDX-License-Identifier: BSD-2-Clause > >> + * > >> + * Copyright (c) 2026 RISCstar Solutions. > >> + * > >> + * Authors: > >> + * Dave Patel > >> + */ > >> + > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> +#include > >> + > >> +#ifdef OPENSBI_CC_SUPPORT_VECTOR > >> + > >> +unsigned long vector_vlenb(void) > >> +{ > >> + unsigned long vlenb = 0; > >> + > >> + asm volatile ( > >> + ".option push\n\t" > >> + ".option arch, +v\n\t" > >> + "csrr %0, vlenb\n\t" > >> + ".option pop\n\t" > >> + : "=r"(vlenb) > >> + : > >> + : "memory"); > >> + > >> + return vlenb; > >> +} > >> + > >> +void sbi_vector_save(struct sbi_vector_context *dst) > >> +{ > >> + if (!dst) > >> + return; > >> + > >> + /* Step 1: Save CSRs */ > >> + dst->vcsr = csr_read(vcsr); > >> + dst->vstart = csr_read(vstart); > >> + > >> + ulong vlenb = vector_vlenb(); > >> + uint8_t *base = dst->vregs; > >> + > >> + /* Step 3: Save vector registers */ > >> +#define SAVE_VREG(i) \ > >> + ({ \ > >> + asm volatile( \ > >> + " .option push\n\t" \ > >> + " .option arch, +v\n\t" \ > >> + " vs8r.v v" #i ", (%0)\n\t" \ > >> + " .option pop\n\t" \ > >> + :: "r"(base + (i) * vlenb) : "memory"); \ > >> + }) \ > >> + > >> + SAVE_VREG(0); > >> + SAVE_VREG(8); > >> + SAVE_VREG(16); > >> + SAVE_VREG(24); > > > > Same issue as FP save/restore, this will trap and crash > > if Vector is disabled in mstatus.VS. > > > > For correct implementation, refer __riscv_v_vstate_save()/restore() > > from /arch/riscv/include/asm/vector.h > > > >> + > >> +#undef SAVE_VREG > >> +} > >> + > >> +void sbi_vector_restore(const struct sbi_vector_context *src) > >> +{ > >> + if (!src) > >> + return; > >> + > >> + const uint8_t *base = src->vregs; > >> + ulong vlenb = vector_vlenb(); > >> + > >> + /* Step 2: Restore vector registers */ > >> +#define RESTORE_VREG(i) \ > >> + ({ \ > >> + asm volatile( \ > >> + " .option push\n\t" \ > >> + " .option arch, +v\n\t" \ > >> + " vl8r.v v" #i ", (%0)\n\t" \ > >> + " .option pop\n\t" \ > >> + :: "r"(base + (i) * vlenb) : "memory"); \ > >> + }) \ > >> + > >> + RESTORE_VREG(0); > >> + RESTORE_VREG(8); > >> + RESTORE_VREG(16); > >> + RESTORE_VREG(24); > >> +#undef RESTORE_VREG > >> + > >> + /* Step 3: Restore CSR's last */ > >> + /* Restore CSRs first */ > >> + csr_write(vcsr, src->vcsr); > >> + csr_write(vstart, src->vstart); > >> +} > >> + > >> +#else > >> + > >> +void sbi_vector_save(struct sbi_vector_context *dst) > >> +{ > >> +} > >> + > >> +void sbi_vector_restore(const struct sbi_vector_context *src) > >> +{ > >> +} > >> + > >> +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ > >> -- > >> 2.43.0 > >> > >> > >> -- > >> opensbi mailing list > >> opensbi at lists.infradead.org > >> http://lists.infradead.org/mailman/listinfo/opensbi > > > > Regards, > > Anup > > Hi Anup, > Firstly thanks for all your review comments, I have endeavor to > cover all of them, so please confirm. > > Rgarding this comment of mstatus.VS and FS (I think which includes 3 > comments) > > I have already have code inplace in patch 3, please see below line from > patch 3 > > + /* Make sure FS and VS is on before context switch */ > > + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); Setting mstatus.FS and mstatus.VS in switch_to_next_domain_context() means you floating and vectore save/restore is not self contained and expecting the calling function to setup mstatus.FS and mstatus.VS. > > Since the context of Floating and Vector is more granular in Opensbi I > thought of handle in context switch. > > So there is no issue of trap and crash, however there is still a > question regarding if you are fine with this or you want it more fine > grained inside its individual functions? > I would insist to keep the float and vector save/restore code aligned with Linux sources. Regards, Anup From dave.patel at riscstar.com Sat May 16 10:17:15 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Sat, 16 May 2026 18:17:15 +0100 Subject: [PATCH v5 1/4] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: References: <20260515054208.151045-1-dave.patel@riscstar.com> <20260515054208.151045-2-dave.patel@riscstar.com> <7c06e1b8-8c42-4dcc-ac68-066a5bc6d567@riscstar.com> Message-ID: <14f4791c-692f-44ab-946a-61bebc9a4d01@riscstar.com> On 5/16/26 17:00, Anup Patel wrote: > On Sat, May 16, 2026 at 3:53?PM Dave Patel wrote: >> >> On 5/15/26 08:41, Anup Patel wrote: >>> On Fri, May 15, 2026 at 11:12?AM wrote: >>>> >>>> From: Dave Patel >>>> >>>> Eager context switch: Add support for saving and restoring RISC-V vector >>>> extension state in OpenSBI. This introduces a per-hart vector context >>>> structure and helper routines to perform full context save and restore. >>>> >>>> The vector context includes vcsr CSRs along with storage for all 32 vector >>>> registers. The register state is saved and restored using byte-wise vector >>>> load/store instructions (vs8r/vl8r). >>>> >>>> The implementation follows an eager context switching model where the entire >>>> vector state is saved and restored on every context switch. This provides a >>>> simple and deterministic mechanism without requiring lazy trap-based >>>> management. >>>> >>>> Signed-off-by: Dave Patel >>>> --- >>>> include/sbi/sbi_vector.h | 28 ++++++++++ >>>> lib/sbi/objects.mk | 1 + >>>> lib/sbi/sbi_vector.c | 109 +++++++++++++++++++++++++++++++++++++++ >>>> 3 files changed, 138 insertions(+) >>>> create mode 100644 include/sbi/sbi_vector.h >>>> create mode 100644 lib/sbi/sbi_vector.c >>>> >>>> diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h >>>> new file mode 100644 >>>> index 00000000..bbd857c3 >>>> --- /dev/null >>>> +++ b/include/sbi/sbi_vector.h >>>> @@ -0,0 +1,28 @@ >>>> +/* >>>> + * SPDX-License-Identifier: BSD-2-Clause >>>> + * >>>> + * Copyright (c) 2026 RISCstar Solutions. >>>> + * >>>> + * Authors: >>>> + * Dave Patel >>>> + */ >>>> + >>>> +#ifndef __SBI_VECTOR_H__ >>>> +#define __SBI_VECTOR_H__ >>>> + >>>> +#include >>>> + >>>> +struct sbi_vector_context { >>>> + unsigned long vcsr; >>>> + unsigned long vstart; >>>> + >>>> + /* size depends on VLEN */ >>>> + uint8_t vregs[]; >>>> +}; >>>> + >>>> +void sbi_vector_save(struct sbi_vector_context *dst); >>>> +void sbi_vector_restore(const struct sbi_vector_context *src); >>>> +unsigned long vector_vlenb(void); >>>> + >>>> +#endif //__SBI_VECTOR_H__ >>>> + >>>> diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk >>>> index 97cc4521..ddb2e7ac 100644 >>>> --- a/lib/sbi/objects.mk >>>> +++ b/lib/sbi/objects.mk >>>> @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o >>>> libsbi-objs-y += sbi_unpriv.o >>>> libsbi-objs-y += sbi_expected_trap.o >>>> libsbi-objs-y += sbi_cppc.o >>>> +libsbi-objs-y += sbi_vector.o >>>> diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c >>>> new file mode 100644 >>>> index 00000000..1d2ac944 >>>> --- /dev/null >>>> +++ b/lib/sbi/sbi_vector.c >>>> @@ -0,0 +1,109 @@ >>>> +/* >>>> + * SPDX-License-Identifier: BSD-2-Clause >>>> + * >>>> + * Copyright (c) 2026 RISCstar Solutions. >>>> + * >>>> + * Authors: >>>> + * Dave Patel >>>> + */ >>>> + >>>> +#include >>>> +#include >>>> +#include >>>> +#include >>>> +#include >>>> +#include >>>> +#include >>>> +#include >>>> + >>>> +#ifdef OPENSBI_CC_SUPPORT_VECTOR >>>> + >>>> +unsigned long vector_vlenb(void) >>>> +{ >>>> + unsigned long vlenb = 0; >>>> + >>>> + asm volatile ( >>>> + ".option push\n\t" >>>> + ".option arch, +v\n\t" >>>> + "csrr %0, vlenb\n\t" >>>> + ".option pop\n\t" >>>> + : "=r"(vlenb) >>>> + : >>>> + : "memory"); >>>> + >>>> + return vlenb; >>>> +} >>>> + >>>> +void sbi_vector_save(struct sbi_vector_context *dst) >>>> +{ >>>> + if (!dst) >>>> + return; >>>> + >>>> + /* Step 1: Save CSRs */ >>>> + dst->vcsr = csr_read(vcsr); >>>> + dst->vstart = csr_read(vstart); >>>> + >>>> + ulong vlenb = vector_vlenb(); >>>> + uint8_t *base = dst->vregs; >>>> + >>>> + /* Step 3: Save vector registers */ >>>> +#define SAVE_VREG(i) \ >>>> + ({ \ >>>> + asm volatile( \ >>>> + " .option push\n\t" \ >>>> + " .option arch, +v\n\t" \ >>>> + " vs8r.v v" #i ", (%0)\n\t" \ >>>> + " .option pop\n\t" \ >>>> + :: "r"(base + (i) * vlenb) : "memory"); \ >>>> + }) \ >>>> + >>>> + SAVE_VREG(0); >>>> + SAVE_VREG(8); >>>> + SAVE_VREG(16); >>>> + SAVE_VREG(24); >>> >>> Same issue as FP save/restore, this will trap and crash >>> if Vector is disabled in mstatus.VS. >>> >>> For correct implementation, refer __riscv_v_vstate_save()/restore() >>> from /arch/riscv/include/asm/vector.h >>> >>>> + >>>> +#undef SAVE_VREG >>>> +} >>>> + >>>> +void sbi_vector_restore(const struct sbi_vector_context *src) >>>> +{ >>>> + if (!src) >>>> + return; >>>> + >>>> + const uint8_t *base = src->vregs; >>>> + ulong vlenb = vector_vlenb(); >>>> + >>>> + /* Step 2: Restore vector registers */ >>>> +#define RESTORE_VREG(i) \ >>>> + ({ \ >>>> + asm volatile( \ >>>> + " .option push\n\t" \ >>>> + " .option arch, +v\n\t" \ >>>> + " vl8r.v v" #i ", (%0)\n\t" \ >>>> + " .option pop\n\t" \ >>>> + :: "r"(base + (i) * vlenb) : "memory"); \ >>>> + }) \ >>>> + >>>> + RESTORE_VREG(0); >>>> + RESTORE_VREG(8); >>>> + RESTORE_VREG(16); >>>> + RESTORE_VREG(24); >>>> +#undef RESTORE_VREG >>>> + >>>> + /* Step 3: Restore CSR's last */ >>>> + /* Restore CSRs first */ >>>> + csr_write(vcsr, src->vcsr); >>>> + csr_write(vstart, src->vstart); >>>> +} >>>> + >>>> +#else >>>> + >>>> +void sbi_vector_save(struct sbi_vector_context *dst) >>>> +{ >>>> +} >>>> + >>>> +void sbi_vector_restore(const struct sbi_vector_context *src) >>>> +{ >>>> +} >>>> + >>>> +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ >>>> -- >>>> 2.43.0 >>>> >>>> >>>> -- >>>> opensbi mailing list >>>> opensbi at lists.infradead.org >>>> http://lists.infradead.org/mailman/listinfo/opensbi >>> >>> Regards, >>> Anup >> >> Hi Anup, >> Firstly thanks for all your review comments, I have endeavor to >> cover all of them, so please confirm. >> >> Rgarding this comment of mstatus.VS and FS (I think which includes 3 >> comments) >> >> I have already have code inplace in patch 3, please see below line from >> patch 3 >> >> + /* Make sure FS and VS is on before context switch */ >> >> + csr_set(CSR_MSTATUS, MSTATUS_FS | MSTATUS_VS); > > Setting mstatus.FS and mstatus.VS in switch_to_next_domain_context() > means you floating and vectore save/restore is not self contained and > expecting the calling function to setup mstatus.FS and mstatus.VS. > > >> >> Since the context of Floating and Vector is more granular in Opensbi I >> thought of handle in context switch. >> >> So there is no issue of trap and crash, however there is still a >> question regarding if you are fine with this or you want it more fine >> grained inside its individual functions? >> > > I would insist to keep the float and vector save/restore code > aligned with Linux sources. > > Regards, > Anup Hi Anup, Thanks, ok I understand you want Opensbi to tightly aligned with Linux. I will do the modification and shall publish new patches. Thanks Dave From dave.patel at riscstar.com Sat May 16 15:21:40 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Sat, 16 May 2026 23:21:40 +0100 Subject: [PATCH v6 0/3]i Add eager FP and RISC-V vector context switching support Message-ID: <20260516222143.23619-1-dave.patel@riscstar.com> From: Dave Patel 1) Moving FS and VS check for MSTATUS inside save and restore of each functions. Dave Patel (3): lib: sbi: Add RISC-V vector context save/restore support lib: sbi: Add floating-point context save/restore support. lib: sbi: domain FP/Vector context support for context switch include/sbi/sbi_fp.h | 26 +++++ include/sbi/sbi_hart.h | 6 + include/sbi/sbi_vector.h | 28 +++++ lib/sbi/objects.mk | 2 + lib/sbi/sbi_domain_context.c | 37 ++++++ lib/sbi/sbi_fp.c | 215 +++++++++++++++++++++++++++++++++++ lib/sbi/sbi_hart.c | 3 + lib/sbi/sbi_vector.c | 138 ++++++++++++++++++++++ 8 files changed, 455 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_fp.c create mode 100644 lib/sbi/sbi_vector.c -- 2.43.0 From dave.patel at riscstar.com Sat May 16 15:21:41 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Sat, 16 May 2026 23:21:41 +0100 Subject: [PATCH v6 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260516222143.23619-1-dave.patel@riscstar.com> References: <20260516222143.23619-1-dave.patel@riscstar.com> Message-ID: <20260516222143.23619-2-dave.patel@riscstar.com> From: Dave Patel Eager context switch: Add support for saving and restoring RISC-V vector extension state in OpenSBI. This introduces a per-hart vector context structure and helper routines to perform full context save and restore. The vector context includes vcsr CSRs along with storage for all 32 vector registers. The register state is saved and restored using byte-wise vector load/store instructions (vs8r/vl8r). The implementation follows an eager context switching model where the entire vector state is saved and restored on every context switch. This provides a simple and deterministic mechanism without requiring lazy trap-based management. Signed-off-by: Dave Patel --- include/sbi/sbi_vector.h | 28 ++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_vector.c | 138 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_vector.c diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h new file mode 100644 index 00000000..bbd857c3 --- /dev/null +++ b/include/sbi/sbi_vector.h @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#ifndef __SBI_VECTOR_H__ +#define __SBI_VECTOR_H__ + +#include + +struct sbi_vector_context { + unsigned long vcsr; + unsigned long vstart; + + /* size depends on VLEN */ + uint8_t vregs[]; +}; + +void sbi_vector_save(struct sbi_vector_context *dst); +void sbi_vector_restore(const struct sbi_vector_context *src); +unsigned long vector_vlenb(void); + +#endif //__SBI_VECTOR_H__ + diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 97cc4521..ddb2e7ac 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o +libsbi-objs-y += sbi_vector.o diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c new file mode 100644 index 00000000..c596ec7a --- /dev/null +++ b/lib/sbi/sbi_vector.c @@ -0,0 +1,138 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENSBI_CC_SUPPORT_VECTOR + +unsigned long vector_vlenb(void) +{ + unsigned long vlenb = 0; + + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "csrr %0, vlenb\n\t" + ".option pop\n\t" + : "=r"(vlenb) + : + : "memory"); + + return vlenb; +} + +static inline void set_vs(void) +{ + csr_set(CSR_MSTATUS, MSTATUS_VS); +} + +void sbi_vector_save(struct sbi_vector_context *dst) +{ + if (!dst) + return; + + unsigned long mstatus_orig; + /* Step 1. Save original mstatus */ + asm volatile("csrr %0, mstatus" : "=r"(mstatus_orig)); + + /* Step 2. Enable VS */ + set_vs(); + + /* Step 3: Save CSRs */ + dst->vcsr = csr_read(vcsr); + dst->vstart = csr_read(vstart); + + ulong vlenb = vector_vlenb(); + uint8_t *base = dst->vregs; + + /* Step 4: Save vector registers */ +#define SAVE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vs8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + SAVE_VREG(0); + SAVE_VREG(8); + SAVE_VREG(16); + SAVE_VREG(24); + +#undef SAVE_VREG + + /* Step 5. Restore original mstatus LAST */ + asm volatile("csrw mstatus, %0" :: "r"(mstatus_orig)); +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ + if (!src) + return; + + unsigned long mstatus_orig; + /* Step 1. Save original mstatus */ + asm volatile("csrr %0, mstatus" : "=r"(mstatus_orig)); + + /* Step 2. Enable VS */ + set_vs(); + + const uint8_t *base = src->vregs; + ulong vlenb = vector_vlenb(); + + /* Step 3: Restore vector registers */ +#define RESTORE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vl8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + RESTORE_VREG(0); + RESTORE_VREG(8); + RESTORE_VREG(16); + RESTORE_VREG(24); +#undef RESTORE_VREG + + /* Step 4: Restore CSR's last */ + /* Restore CSRs first */ + csr_write(vcsr, src->vcsr); + csr_write(vstart, src->vstart); + + /* Step 5. Restore original mstatus LAST */ + asm volatile("csrw mstatus, %0" :: "r"(mstatus_orig)); +} + +#else + +void sbi_vector_save(struct sbi_vector_context *dst) +{ +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ +} + +unsigned long vector_vlenb(void) +{ + return 0; +} +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ -- 2.43.0 From dave.patel at riscstar.com Sat May 16 15:21:42 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Sat, 16 May 2026 23:21:42 +0100 Subject: [PATCH v6 2/3] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260516222143.23619-1-dave.patel@riscstar.com> References: <20260516222143.23619-1-dave.patel@riscstar.com> Message-ID: <20260516222143.23619-3-dave.patel@riscstar.com> From: Dave Patel Add support for saving and restoring RISC-V floating-point (F/D) extension state in OpenSBI. This introduces a floating-point context structure and helper routines to perform full context save and restore. The floating-point context includes storage for all 32 FPi registers (f0?f31) along with the fcsr control and status register. The register state is saved and restored using double-precision load/store instructions (fsd/fld), and single-precision load/store instructions (fsw/flw) on an RV64 system with F and D-extension support. The implementation follows an eager context switching model where the entire FP state is saved and restored on every context switch. This avoids the need for trap-based lazy management and keeps the design simple and deterministic. Signed-off-by: Dave Patel " --- include/sbi/sbi_fp.h | 26 ++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_fp.c | 215 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 lib/sbi/sbi_fp.c diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h new file mode 100644 index 00000000..a00756fc --- /dev/null +++ b/include/sbi/sbi_fp.h @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ +#ifndef __SBI_FP_H__ +#define __SBI_FP_H__ + +#include + +struct sbi_fp_context { +#if __riscv_d + uint64_t f[32]; +#else + uint32_t f[32]; +#endif + unsigned long fcsr; +}; + +void sbi_fp_save(struct sbi_fp_context *dst); +void sbi_fp_restore(const struct sbi_fp_context *src); + +#endif //__SBI_VECTOR_H__ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index ddb2e7ac..d8182383 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -110,3 +110,4 @@ libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o libsbi-objs-y += sbi_vector.o +libsbi-objs-y += sbi_fp.o diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c new file mode 100644 index 00000000..96769bbc --- /dev/null +++ b/lib/sbi/sbi_fp.c @@ -0,0 +1,215 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include + +#if defined(__riscv_f) || defined(__riscv_d) + +static inline void set_fs(void) +{ + csr_set(CSR_MSTATUS, MSTATUS_FS); +} + +void sbi_fp_save(struct sbi_fp_context *dst) +{ + if (!dst) + return; + + unsigned long mstatus_orig; + /* Save original mstatus */ + asm volatile("csrr %0, mstatus" : "=r"(mstatus_orig)); + + set_fs(); + +#if defined(__riscv_d) + asm volatile( + "fsd f0, 0(%0)\n" + "fsd f1, 8(%0)\n" + "fsd f2, 16(%0)\n" + "fsd f3, 24(%0)\n" + "fsd f4, 32(%0)\n" + "fsd f5, 40(%0)\n" + "fsd f6, 48(%0)\n" + "fsd f7, 56(%0)\n" + "fsd f8, 64(%0)\n" + "fsd f9, 72(%0)\n" + "fsd f10, 80(%0)\n" + "fsd f11, 88(%0)\n" + "fsd f12, 96(%0)\n" + "fsd f13, 104(%0)\n" + "fsd f14, 112(%0)\n" + "fsd f15, 120(%0)\n" + "fsd f16, 128(%0)\n" + "fsd f17, 136(%0)\n" + "fsd f18, 144(%0)\n" + "fsd f19, 152(%0)\n" + "fsd f20, 160(%0)\n" + "fsd f21, 168(%0)\n" + "fsd f22, 176(%0)\n" + "fsd f23, 184(%0)\n" + "fsd f24, 192(%0)\n" + "fsd f25, 200(%0)\n" + "fsd f26, 208(%0)\n" + "fsd f27, 216(%0)\n" + "fsd f28, 224(%0)\n" + "fsd f29, 232(%0)\n" + "fsd f30, 240(%0)\n" + "fsd f31, 248(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#else + asm volatile( + "fsw f0, 0(%0)\n" + "fsw f1, 4(%0)\n" + "fsw f2, 8(%0)\n" + "fsw f3, 12(%0)\n" + "fsw f4, 16(%0)\n" + "fsw f5, 20(%0)\n" + "fsw f6, 24(%0)\n" + "fsw f7, 28(%0)\n" + "fsw f8, 32(%0)\n" + "fsw f9, 36(%0)\n" + "fsw f10, 40(%0)\n" + "fsw f11, 44(%0)\n" + "fsw f12, 48(%0)\n" + "fsw f13, 52(%0)\n" + "fsw f14, 56(%0)\n" + "fsw f15, 60(%0)\n" + "fsw f16, 64(%0)\n" + "fsw f17, 68(%0)\n" + "fsw f18, 72(%0)\n" + "fsw f19, 76(%0)\n" + "fsw f20, 80(%0)\n" + "fsw f21, 84(%0)\n" + "fsw f22, 88(%0)\n" + "fsw f23, 92(%0)\n" + "fsw f24, 96(%0)\n" + "fsw f25, 100(%0)\n" + "fsw f26, 104(%0)\n" + "fsw f27, 108(%0)\n" + "fsw f28, 112(%0)\n" + "fsw f29, 116(%0)\n" + "fsw f30, 120(%0)\n" + "fsw f31, 124(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#endif //__riscv_d + + dst->fcsr = csr_read(CSR_FCSR); + + /* Restore original mstatus LAST */ + asm volatile("csrw mstatus, %0" :: "r"(mstatus_orig)); +} + +void sbi_fp_restore(const struct sbi_fp_context *src) +{ + if (!src) + return; + + unsigned long mstatus_orig; + /* Save original mstatus */ + asm volatile("csrr %0, mstatus" : "=r"(mstatus_orig)); + + set_fs(); + +#if defined(__riscv_d) + asm volatile( + "fld f0, 0(%0)\n" + "fld f1, 8(%0)\n" + "fld f2, 16(%0)\n" + "fld f3, 24(%0)\n" + "fld f4, 32(%0)\n" + "fld f5, 40(%0)\n" + "fld f6, 48(%0)\n" + "fld f7, 56(%0)\n" + "fld f8, 64(%0)\n" + "fld f9, 72(%0)\n" + "fld f10, 80(%0)\n" + "fld f11, 88(%0)\n" + "fld f12, 96(%0)\n" + "fld f13, 104(%0)\n" + "fld f14, 112(%0)\n" + "fld f15, 120(%0)\n" + "fld f16, 128(%0)\n" + "fld f17, 136(%0)\n" + "fld f18, 144(%0)\n" + "fld f19, 152(%0)\n" + "fld f20, 160(%0)\n" + "fld f21, 168(%0)\n" + "fld f22, 176(%0)\n" + "fld f23, 184(%0)\n" + "fld f24, 192(%0)\n" + "fld f25, 200(%0)\n" + "fld f26, 208(%0)\n" + "fld f27, 216(%0)\n" + "fld f28, 224(%0)\n" + "fld f29, 232(%0)\n" + "fld f30, 240(%0)\n" + "fld f31, 248(%0)\n" + : + : "r"(src->f) + : "memory" + ); +#else + + asm volatile( + "flw f0, 0(%0)\n" + "flw f1, 4(%0)\n" + "flw f2, 8(%0)\n" + "flw f3, 12(%0)\n" + "flw f4, 16(%0)\n" + "flw f5, 20(%0)\n" + "flw f6, 24(%0)\n" + "flw f7, 28(%0)\n" + "flw f8, 32(%0)\n" + "flw f9, 36(%0)\n" + "flw f10, 40(%0)\n" + "flw f11, 44(%0)\n" + "flw f12, 48(%0)\n" + "flw f13, 52(%0)\n" + "flw f14, 56(%0)\n" + "flw f15, 60(%0)\n" + "flw f16, 64(%0)\n" + "flw f17, 68(%0)\n" + "flw f18, 72(%0)\n" + "flw f19, 76(%0)\n" + "flw f20, 80(%0)\n" + "flw f21, 84(%0)\n" + "flw f22, 88(%0)\n" + "flw f23, 92(%0)\n" + "flw f24, 96(%0)\n" + "flw f25, 100(%0)\n" + "flw f26, 104(%0)\n" + "flw f27, 108(%0)\n" + "flw f28, 112(%0)\n" + "flw f29, 116(%0)\n" + "flw f30, 120(%0)\n" + "flw f31, 124(%0)\n" + : + : "r"(src->f) + : "memory" + ); + +#endif + + csr_write(CSR_FCSR, src->fcsr); + + /* Restore original mstatus LAST */ + asm volatile("csrw mstatus, %0" :: "r"(mstatus_orig)); +} +#else +void sbi_fp_save(struct sbi_fp_context *dst) {} +void sbi_fp_restore(const struct sbi_fp_context *src) {} +#endif // FP present -- 2.43.0 From dave.patel at riscstar.com Sat May 16 15:21:43 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Sat, 16 May 2026 23:21:43 +0100 Subject: [PATCH v6 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260516222143.23619-1-dave.patel@riscstar.com> References: <20260516222143.23619-1-dave.patel@riscstar.com> Message-ID: <20260516222143.23619-4-dave.patel@riscstar.com> From: Dave Patel This patch adds proper support for per-domain floating-point (FP) and vector (V) contexts in the domain context switch logic. Each domain now maintains its own FP and vector state, which is saved and restored during domain switches. Conditionalize FP and Vector save/restore based on extensions, unconditional save and restore of floating-point (FP) and Vector registers fails on generic platform firmware. This firmware must run on multiple platforms that may lack these extensions. Address this by conditionally executing FP save/restore only if the underlying hart supports the F or D extensions. Similarly, perform Vector save/restore only if the hart supports the Vector extension. Changes include: - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' to allocate and free per-domain FP and vector context. - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. - Updated `switch_to_next_domain_context()` to save/restore FP and vector contexts safely: - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. - Added runtime checks for FP and vector extensions where needed. - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() to check for these capabilities before performing the context switches This improves support for multi-domain systems with FP and Vector extensions, and prevents corruption of FP/Vector state during domain switches. Signed-off-by: Dave Patel --- include/sbi/sbi_hart.h | 6 ++++++ lib/sbi/sbi_domain_context.c | 37 ++++++++++++++++++++++++++++++++++++ lib/sbi/sbi_hart.c | 3 +++ 3 files changed, 46 insertions(+) diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index a788b34c..68a01b97 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -87,6 +87,12 @@ enum sbi_hart_extensions { SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, /** Hart has Xsfcease extension */ SBI_HART_EXT_XSIFIVE_CEASE, + /** Hart has V extension */ + SBI_HART_EXT_V, + /** Hart has F extension */ + SBI_HART_EXT_F, + /** Hart has D extension */ + SBI_HART_EXT_D, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index 158f4990..a3f69e7f 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include /** Context representation for a hart within a domain */ struct hart_context { @@ -55,6 +57,11 @@ struct hart_context { struct hart_context *prev_ctx; /** Is context initialized and runnable */ bool initialized; + + /** float context state */ + struct sbi_fp_context fp_ctx; + /** vector context state */ + struct sbi_vector_context *vec_ctx; }; static struct sbi_domain_data dcpriv; @@ -143,6 +150,22 @@ static int switch_to_next_domain_context(struct hart_context *ctx, if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); + /* Eager context switch F */ + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_F) || + sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_D)) { + sbi_fp_save(&ctx->fp_ctx); + sbi_fp_restore(&dom_ctx->fp_ctx); + } + + /* Eager context switch V */ + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V)) { + sbi_vector_save(ctx->vec_ctx); + sbi_vector_restore(dom_ctx->vec_ctx); + } + /* Save current trap state and restore target domain's trap state */ trap_ctx = sbi_trap_get_context(scratch); sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); @@ -180,6 +203,20 @@ static int hart_context_init(u32 hartindex) if (!ctx) return SBI_ENOMEM; + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V)) { + unsigned long vlenb = vector_vlenb(); + /* Calculate size: base struct + 32 registers of vlenb size */ + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); + + /* Allocate the vector context pointer */ + ctx->vec_ctx = sbi_zalloc(vec_size); + if (!ctx->vec_ctx) { + sbi_free(ctx); + return SBI_ENOMEM; + } + } + /* Bind context and domain */ ctx->dom = dom; hart_context_set(dom, hartindex, ctx); diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 60e95bca..b5e0ee10 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -396,6 +396,9 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), + __SBI_HART_EXT_DATA(v, SBI_HART_EXT_V), + __SBI_HART_EXT_DATA(f, SBI_HART_EXT_F), + __SBI_HART_EXT_DATA(d, SBI_HART_EXT_D), }; _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), -- 2.43.0 From anup at brainfault.org Sun May 17 08:37:34 2026 From: anup at brainfault.org (Anup Patel) Date: Sun, 17 May 2026 21:07:34 +0530 Subject: [PATCH v6 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260516222143.23619-2-dave.patel@riscstar.com> References: <20260516222143.23619-1-dave.patel@riscstar.com> <20260516222143.23619-2-dave.patel@riscstar.com> Message-ID: On Sun, May 17, 2026 at 3:51?AM wrote: > > From: Dave Patel > > Eager context switch: Add support for saving and restoring RISC-V vector > extension state in OpenSBI. This introduces a per-hart vector context > structure and helper routines to perform full context save and restore. > > The vector context includes vcsr CSRs along with storage for all 32 vector > registers. The register state is saved and restored using byte-wise vector > load/store instructions (vs8r/vl8r). > > The implementation follows an eager context switching model where the entire > vector state is saved and restored on every context switch. This provides a > simple and deterministic mechanism without requiring lazy trap-based > management. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_vector.h | 28 ++++++++ > lib/sbi/objects.mk | 1 + > lib/sbi/sbi_vector.c | 138 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 167 insertions(+) > create mode 100644 include/sbi/sbi_vector.h > create mode 100644 lib/sbi/sbi_vector.c > > diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h > new file mode 100644 > index 00000000..bbd857c3 > --- /dev/null > +++ b/include/sbi/sbi_vector.h > @@ -0,0 +1,28 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#ifndef __SBI_VECTOR_H__ > +#define __SBI_VECTOR_H__ > + > +#include > + > +struct sbi_vector_context { > + unsigned long vcsr; > + unsigned long vstart; > + > + /* size depends on VLEN */ > + uint8_t vregs[]; > +}; > + > +void sbi_vector_save(struct sbi_vector_context *dst); > +void sbi_vector_restore(const struct sbi_vector_context *src); > +unsigned long vector_vlenb(void); > + > +#endif //__SBI_VECTOR_H__ > + > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index 97cc4521..ddb2e7ac 100644 > --- a/lib/sbi/objects.mk > +++ b/lib/sbi/objects.mk > @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o > libsbi-objs-y += sbi_unpriv.o > libsbi-objs-y += sbi_expected_trap.o > libsbi-objs-y += sbi_cppc.o > +libsbi-objs-y += sbi_vector.o > diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c > new file mode 100644 > index 00000000..c596ec7a > --- /dev/null > +++ b/lib/sbi/sbi_vector.c > @@ -0,0 +1,138 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#ifdef OPENSBI_CC_SUPPORT_VECTOR > + > +unsigned long vector_vlenb(void) > +{ > + unsigned long vlenb = 0; > + > + asm volatile ( > + ".option push\n\t" > + ".option arch, +v\n\t" > + "csrr %0, vlenb\n\t" > + ".option pop\n\t" > + : "=r"(vlenb) > + : > + : "memory"); > + > + return vlenb; This whole function can be a single statement "csr_read(CSR_VLENB);" Introduce new function sbi_vector_context_size() over here which estimates vector context size over here so that all vector context related code is in one place. size_t sbi_vector_context_size(void) { return sizeof(struct sbi_vector_context) + (32 * csr_read(CSR_VLENB)); } > +} > + > +static inline void set_vs(void) > +{ > + csr_set(CSR_MSTATUS, MSTATUS_VS); > +} > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > + if (!dst) > + return; > + > + unsigned long mstatus_orig; Move all variable declarations at the start of function. > + /* Step 1. Save original mstatus */ > + asm volatile("csrr %0, mstatus" : "=r"(mstatus_orig)); > + > + /* Step 2. Enable VS */ > + set_vs(); These two lines can be just one once CSR operation: mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); > + > + /* Step 3: Save CSRs */ > + dst->vcsr = csr_read(vcsr); > + dst->vstart = csr_read(vstart); > + > + ulong vlenb = vector_vlenb(); > + uint8_t *base = dst->vregs; > + > + /* Step 4: Save vector registers */ > +#define SAVE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vs8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + SAVE_VREG(0); > + SAVE_VREG(8); > + SAVE_VREG(16); > + SAVE_VREG(24); > + > +#undef SAVE_VREG > + > + /* Step 5. Restore original mstatus LAST */ > + asm volatile("csrw mstatus, %0" :: "r"(mstatus_orig)); csr_write(CSR_MSTATUS, mstatus_orig); > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > + if (!src) > + return; > + > + unsigned long mstatus_orig; Move all variable declarations at the start of function. > + /* Step 1. Save original mstatus */ > + asm volatile("csrr %0, mstatus" : "=r"(mstatus_orig)); > + > + /* Step 2. Enable VS */ > + set_vs(); Same comment as above. > + > + const uint8_t *base = src->vregs; > + ulong vlenb = vector_vlenb(); > + > + /* Step 3: Restore vector registers */ > +#define RESTORE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vl8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + RESTORE_VREG(0); > + RESTORE_VREG(8); > + RESTORE_VREG(16); > + RESTORE_VREG(24); > +#undef RESTORE_VREG > + > + /* Step 4: Restore CSR's last */ > + /* Restore CSRs first */ > + csr_write(vcsr, src->vcsr); > + csr_write(vstart, src->vstart); > + > + /* Step 5. Restore original mstatus LAST */ > + asm volatile("csrw mstatus, %0" :: "r"(mstatus_orig)); Same comment as above. > +} > + > +#else > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > +} > + > +unsigned long vector_vlenb(void) > +{ > + return 0; > +} size_t sbi_vector_context_size(void) { return 0; } > +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Sun May 17 08:37:37 2026 From: anup at brainfault.org (Anup Patel) Date: Sun, 17 May 2026 21:07:37 +0530 Subject: [PATCH v6 2/3] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260516222143.23619-3-dave.patel@riscstar.com> References: <20260516222143.23619-1-dave.patel@riscstar.com> <20260516222143.23619-3-dave.patel@riscstar.com> Message-ID: On Sun, May 17, 2026 at 3:51?AM wrote: > > From: Dave Patel > > Add support for saving and restoring RISC-V floating-point (F/D) extension > state in OpenSBI. This introduces a floating-point context structure and > helper routines to perform full context save and restore. > > The floating-point context includes storage for all 32 FPi registers (f0?f31) > along with the fcsr control and status register. The register state is saved > and restored using double-precision load/store instructions (fsd/fld), and > single-precision load/store instructions (fsw/flw) on an RV64 system with > F and D-extension support. > > The implementation follows an eager context switching model where the entire > FP state is saved and restored on every context switch. This avoids the need > for trap-based lazy management and keeps the design simple and deterministic. > > Signed-off-by: Dave Patel " > --- > include/sbi/sbi_fp.h | 26 ++++++ > lib/sbi/objects.mk | 1 + > lib/sbi/sbi_fp.c | 215 +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 242 insertions(+) > create mode 100644 include/sbi/sbi_fp.h > create mode 100644 lib/sbi/sbi_fp.c > > diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h > new file mode 100644 > index 00000000..a00756fc > --- /dev/null > +++ b/include/sbi/sbi_fp.h > @@ -0,0 +1,26 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > +#ifndef __SBI_FP_H__ > +#define __SBI_FP_H__ > + > +#include > + > +struct sbi_fp_context { > +#if __riscv_d > + uint64_t f[32]; > +#else > + uint32_t f[32]; > +#endif > + unsigned long fcsr; > +}; > + > +void sbi_fp_save(struct sbi_fp_context *dst); > +void sbi_fp_restore(const struct sbi_fp_context *src); > + > +#endif //__SBI_VECTOR_H__ > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index ddb2e7ac..d8182383 100644 > --- a/lib/sbi/objects.mk > +++ b/lib/sbi/objects.mk > @@ -110,3 +110,4 @@ libsbi-objs-y += sbi_unpriv.o > libsbi-objs-y += sbi_expected_trap.o > libsbi-objs-y += sbi_cppc.o > libsbi-objs-y += sbi_vector.o > +libsbi-objs-y += sbi_fp.o > diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c > new file mode 100644 > index 00000000..96769bbc > --- /dev/null > +++ b/lib/sbi/sbi_fp.c > @@ -0,0 +1,215 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#include > +#include > +#include > + > +#if defined(__riscv_f) || defined(__riscv_d) > + > +static inline void set_fs(void) > +{ > + csr_set(CSR_MSTATUS, MSTATUS_FS); > +} > + > +void sbi_fp_save(struct sbi_fp_context *dst) > +{ > + if (!dst) > + return; > + > + unsigned long mstatus_orig; Move all variable declarations at the start of function. > + /* Save original mstatus */ > + asm volatile("csrr %0, mstatus" : "=r"(mstatus_orig)); > + > + set_fs(); These two lines can be just one once CSR operation: mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_FS); > + > +#if defined(__riscv_d) > + asm volatile( > + "fsd f0, 0(%0)\n" > + "fsd f1, 8(%0)\n" > + "fsd f2, 16(%0)\n" > + "fsd f3, 24(%0)\n" > + "fsd f4, 32(%0)\n" > + "fsd f5, 40(%0)\n" > + "fsd f6, 48(%0)\n" > + "fsd f7, 56(%0)\n" > + "fsd f8, 64(%0)\n" > + "fsd f9, 72(%0)\n" > + "fsd f10, 80(%0)\n" > + "fsd f11, 88(%0)\n" > + "fsd f12, 96(%0)\n" > + "fsd f13, 104(%0)\n" > + "fsd f14, 112(%0)\n" > + "fsd f15, 120(%0)\n" > + "fsd f16, 128(%0)\n" > + "fsd f17, 136(%0)\n" > + "fsd f18, 144(%0)\n" > + "fsd f19, 152(%0)\n" > + "fsd f20, 160(%0)\n" > + "fsd f21, 168(%0)\n" > + "fsd f22, 176(%0)\n" > + "fsd f23, 184(%0)\n" > + "fsd f24, 192(%0)\n" > + "fsd f25, 200(%0)\n" > + "fsd f26, 208(%0)\n" > + "fsd f27, 216(%0)\n" > + "fsd f28, 224(%0)\n" > + "fsd f29, 232(%0)\n" > + "fsd f30, 240(%0)\n" > + "fsd f31, 248(%0)\n" > + : > + : "r"(dst->f) > + : "memory" > + ); > +#else > + asm volatile( > + "fsw f0, 0(%0)\n" > + "fsw f1, 4(%0)\n" > + "fsw f2, 8(%0)\n" > + "fsw f3, 12(%0)\n" > + "fsw f4, 16(%0)\n" > + "fsw f5, 20(%0)\n" > + "fsw f6, 24(%0)\n" > + "fsw f7, 28(%0)\n" > + "fsw f8, 32(%0)\n" > + "fsw f9, 36(%0)\n" > + "fsw f10, 40(%0)\n" > + "fsw f11, 44(%0)\n" > + "fsw f12, 48(%0)\n" > + "fsw f13, 52(%0)\n" > + "fsw f14, 56(%0)\n" > + "fsw f15, 60(%0)\n" > + "fsw f16, 64(%0)\n" > + "fsw f17, 68(%0)\n" > + "fsw f18, 72(%0)\n" > + "fsw f19, 76(%0)\n" > + "fsw f20, 80(%0)\n" > + "fsw f21, 84(%0)\n" > + "fsw f22, 88(%0)\n" > + "fsw f23, 92(%0)\n" > + "fsw f24, 96(%0)\n" > + "fsw f25, 100(%0)\n" > + "fsw f26, 104(%0)\n" > + "fsw f27, 108(%0)\n" > + "fsw f28, 112(%0)\n" > + "fsw f29, 116(%0)\n" > + "fsw f30, 120(%0)\n" > + "fsw f31, 124(%0)\n" > + : > + : "r"(dst->f) > + : "memory" > + ); > +#endif //__riscv_d > + > + dst->fcsr = csr_read(CSR_FCSR); > + > + /* Restore original mstatus LAST */ > + asm volatile("csrw mstatus, %0" :: "r"(mstatus_orig)); csr_write(CSR_MSTATUS, mstatus_orig); > +} > + > +void sbi_fp_restore(const struct sbi_fp_context *src) > +{ > + if (!src) > + return; > + > + unsigned long mstatus_orig; Move all variable declarations at the start of function. > + /* Save original mstatus */ > + asm volatile("csrr %0, mstatus" : "=r"(mstatus_orig)); > + > + set_fs(); Same comment as above. > + > +#if defined(__riscv_d) > + asm volatile( > + "fld f0, 0(%0)\n" > + "fld f1, 8(%0)\n" > + "fld f2, 16(%0)\n" > + "fld f3, 24(%0)\n" > + "fld f4, 32(%0)\n" > + "fld f5, 40(%0)\n" > + "fld f6, 48(%0)\n" > + "fld f7, 56(%0)\n" > + "fld f8, 64(%0)\n" > + "fld f9, 72(%0)\n" > + "fld f10, 80(%0)\n" > + "fld f11, 88(%0)\n" > + "fld f12, 96(%0)\n" > + "fld f13, 104(%0)\n" > + "fld f14, 112(%0)\n" > + "fld f15, 120(%0)\n" > + "fld f16, 128(%0)\n" > + "fld f17, 136(%0)\n" > + "fld f18, 144(%0)\n" > + "fld f19, 152(%0)\n" > + "fld f20, 160(%0)\n" > + "fld f21, 168(%0)\n" > + "fld f22, 176(%0)\n" > + "fld f23, 184(%0)\n" > + "fld f24, 192(%0)\n" > + "fld f25, 200(%0)\n" > + "fld f26, 208(%0)\n" > + "fld f27, 216(%0)\n" > + "fld f28, 224(%0)\n" > + "fld f29, 232(%0)\n" > + "fld f30, 240(%0)\n" > + "fld f31, 248(%0)\n" > + : > + : "r"(src->f) > + : "memory" > + ); > +#else > + > + asm volatile( > + "flw f0, 0(%0)\n" > + "flw f1, 4(%0)\n" > + "flw f2, 8(%0)\n" > + "flw f3, 12(%0)\n" > + "flw f4, 16(%0)\n" > + "flw f5, 20(%0)\n" > + "flw f6, 24(%0)\n" > + "flw f7, 28(%0)\n" > + "flw f8, 32(%0)\n" > + "flw f9, 36(%0)\n" > + "flw f10, 40(%0)\n" > + "flw f11, 44(%0)\n" > + "flw f12, 48(%0)\n" > + "flw f13, 52(%0)\n" > + "flw f14, 56(%0)\n" > + "flw f15, 60(%0)\n" > + "flw f16, 64(%0)\n" > + "flw f17, 68(%0)\n" > + "flw f18, 72(%0)\n" > + "flw f19, 76(%0)\n" > + "flw f20, 80(%0)\n" > + "flw f21, 84(%0)\n" > + "flw f22, 88(%0)\n" > + "flw f23, 92(%0)\n" > + "flw f24, 96(%0)\n" > + "flw f25, 100(%0)\n" > + "flw f26, 104(%0)\n" > + "flw f27, 108(%0)\n" > + "flw f28, 112(%0)\n" > + "flw f29, 116(%0)\n" > + "flw f30, 120(%0)\n" > + "flw f31, 124(%0)\n" > + : > + : "r"(src->f) > + : "memory" > + ); > + > +#endif > + > + csr_write(CSR_FCSR, src->fcsr); > + > + /* Restore original mstatus LAST */ > + asm volatile("csrw mstatus, %0" :: "r"(mstatus_orig)); Same comment as above. > +} > +#else > +void sbi_fp_save(struct sbi_fp_context *dst) {} > +void sbi_fp_restore(const struct sbi_fp_context *src) {} > +#endif // FP present > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Sun May 17 08:37:57 2026 From: anup at brainfault.org (Anup Patel) Date: Sun, 17 May 2026 21:07:57 +0530 Subject: [PATCH v6 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260516222143.23619-4-dave.patel@riscstar.com> References: <20260516222143.23619-1-dave.patel@riscstar.com> <20260516222143.23619-4-dave.patel@riscstar.com> Message-ID: On Sun, May 17, 2026 at 3:51?AM wrote: > > From: Dave Patel > > This patch adds proper support for per-domain floating-point (FP) and > vector (V) contexts in the domain context switch logic. Each domain > now maintains its own FP and vector state, which is saved and restored > during domain switches. > > Conditionalize FP and Vector save/restore based on extensions, unconditional > save and restore of floating-point (FP) and Vector registers fails on > generic platform firmware. This firmware must run on multiple platforms > that may lack these extensions. > > Address this by conditionally executing FP save/restore only if the underlying > hart supports the F or D extensions. Similarly, perform Vector save/restore > only if the hart supports the Vector extension. > > Changes include: > > - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. > - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' > to allocate and free per-domain FP and vector context. > - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. > - Updated `switch_to_next_domain_context()` to save/restore FP and vector > contexts safely: > - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. > - Added runtime checks for FP and vector extensions where needed. > - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum > sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() > to check for these capabilities before performing the context switches This changelog should be part of cover-letter with every series revision. > > This improves support for multi-domain systems with FP and Vector > extensions, and prevents corruption of FP/Vector state during domain > switches. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_hart.h | 6 ++++++ > lib/sbi/sbi_domain_context.c | 37 ++++++++++++++++++++++++++++++++++++ > lib/sbi/sbi_hart.c | 3 +++ > 3 files changed, 46 insertions(+) > > diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h > index a788b34c..68a01b97 100644 > --- a/include/sbi/sbi_hart.h > +++ b/include/sbi/sbi_hart.h > @@ -87,6 +87,12 @@ enum sbi_hart_extensions { > SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, > /** Hart has Xsfcease extension */ > SBI_HART_EXT_XSIFIVE_CEASE, > + /** Hart has V extension */ > + SBI_HART_EXT_V, > + /** Hart has F extension */ > + SBI_HART_EXT_F, > + /** Hart has D extension */ > + SBI_HART_EXT_D, > > /** Maximum index of Hart extension */ > SBI_HART_EXT_MAX, > diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c > index 158f4990..a3f69e7f 100644 > --- a/lib/sbi/sbi_domain_context.c > +++ b/lib/sbi/sbi_domain_context.c > @@ -18,6 +18,8 @@ > #include > #include > #include > +#include > +#include > > /** Context representation for a hart within a domain */ > struct hart_context { > @@ -55,6 +57,11 @@ struct hart_context { > struct hart_context *prev_ctx; > /** Is context initialized and runnable */ > bool initialized; > + > + /** float context state */ > + struct sbi_fp_context fp_ctx; > + /** vector context state */ > + struct sbi_vector_context *vec_ctx; > }; > > static struct sbi_domain_data dcpriv; > @@ -143,6 +150,22 @@ static int switch_to_next_domain_context(struct hart_context *ctx, > if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) > ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); > > + /* Eager context switch F */ > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_F) || > + sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_D)) { > + sbi_fp_save(&ctx->fp_ctx); > + sbi_fp_restore(&dom_ctx->fp_ctx); > + } > + > + /* Eager context switch V */ > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_V)) { > + sbi_vector_save(ctx->vec_ctx); > + sbi_vector_restore(dom_ctx->vec_ctx); > + } > + > /* Save current trap state and restore target domain's trap state */ > trap_ctx = sbi_trap_get_context(scratch); > sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); > @@ -180,6 +203,20 @@ static int hart_context_init(u32 hartindex) > if (!ctx) > return SBI_ENOMEM; > > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_V)) { > + unsigned long vlenb = vector_vlenb(); > + /* Calculate size: base struct + 32 registers of vlenb size */ > + size_t vec_size = sizeof(struct sbi_vector_context) + (32 * vlenb); Use sbi_vector_context_size() over here as suggested in PATCH1 > + > + /* Allocate the vector context pointer */ > + ctx->vec_ctx = sbi_zalloc(vec_size); > + if (!ctx->vec_ctx) { > + sbi_free(ctx); > + return SBI_ENOMEM; > + } > + } > + > /* Bind context and domain */ > ctx->dom = dom; > hart_context_set(dom, hartindex, ctx); > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > index 60e95bca..b5e0ee10 100644 > --- a/lib/sbi/sbi_hart.c > +++ b/lib/sbi/sbi_hart.c > @@ -396,6 +396,9 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { > __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), > __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), > __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), > + __SBI_HART_EXT_DATA(v, SBI_HART_EXT_V), > + __SBI_HART_EXT_DATA(f, SBI_HART_EXT_F), > + __SBI_HART_EXT_DATA(d, SBI_HART_EXT_D), > }; > > _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From mpe at kernel.org Sun May 17 18:13:44 2026 From: mpe at kernel.org (Michael Ellerman) Date: Mon, 18 May 2026 11:13:44 +1000 Subject: [PATCH] lib/sbi_pmu: Don't fallback to fixed counters when sscofpmf && !smcntrpmf In-Reply-To: References: <20260324-mcycle-fix-v1-1-1444e9fe5c32@kernel.org> Message-ID: On 11/5/2026 17:52, Anup Patel wrote: > On Tue, Mar 24, 2026 at 5:59?PM Michael Ellerman wrote: >> >> Currently when searching for a hardware counter for an event, if no >> programmable counter is available, the code falls back to using a fixed >> counter (mcycle/minstret) if one matches the event. >> >> However the fallback is incorrect when sscofpmf is present but >> smcntrpmf is not. That's because with sscofpmf, programmable counters >> support mode filtering, but the fixed counters do not (without >> smcntrpmf). Even if the caller didn't configure mode filtering, by >> default programmable counters don't count M mode when sscofpmf is >> present, whereas mcycle/minstret do. >> >> Fix the logic to not fallback to a fixed counter if sscofpmf is present >> but smcntrpmf is not. >> >> Signed-off-by: Michael Ellerman > > Fixes tag is missing but I will add this at the time of merging. Thanks. cheers From dave.patel at riscstar.com Sun May 17 22:42:17 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Mon, 18 May 2026 06:42:17 +0100 Subject: [PATCH v7 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260518054219.58708-1-dave.patel@riscstar.com> References: <20260518054219.58708-1-dave.patel@riscstar.com> Message-ID: <20260518054219.58708-2-dave.patel@riscstar.com> From: Dave Patel Eager context switch: Add support for saving and restoring RISC-V vector extension state in OpenSBI. This introduces a per-hart vector context structure and helper routines to perform full context save and restore. The vector context includes vcsr CSRs along with storage for all 32 vector registers. The register state is saved and restored using byte-wise vector load/store instructions (vs8r/vl8r). The implementation follows an eager context switching model where the entire vector state is saved and restored on every context switch. This provides a simple and deterministic mechanism without requiring lazy trap-based management. Signed-off-by: Dave Patel --- include/sbi/sbi_vector.h | 28 ++++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_vector.c | 118 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_vector.c diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h new file mode 100644 index 00000000..d92fc03d --- /dev/null +++ b/include/sbi/sbi_vector.h @@ -0,0 +1,28 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#ifndef __SBI_VECTOR_H__ +#define __SBI_VECTOR_H__ + +#include + +struct sbi_vector_context { + unsigned long vcsr; + unsigned long vstart; + + /* size depends on VLEN */ + uint8_t vregs[]; +}; + +void sbi_vector_save(struct sbi_vector_context *dst); +void sbi_vector_restore(const struct sbi_vector_context *src); +size_t sbi_vector_context_size(void); + +#endif //__SBI_VECTOR_H__ + diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 97cc4521..ddb2e7ac 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o +libsbi-objs-y += sbi_vector.o diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c new file mode 100644 index 00000000..e4a1f515 --- /dev/null +++ b/lib/sbi/sbi_vector.c @@ -0,0 +1,118 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENSBI_CC_SUPPORT_VECTOR + +size_t sbi_vector_context_size(void) +{ + return sizeof(struct sbi_vector_context) + (32 * csr_read(CSR_VLENB)); +} + +void sbi_vector_save(struct sbi_vector_context *dst) +{ + unsigned long mstatus_orig; + + if (!dst) + return; + + /* Step 1. Save original mstatus and Enable VS */ + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); + + /* Step 2: Save CSRs */ + dst->vcsr = csr_read(vcsr); + dst->vstart = csr_read(vstart); + + ulong vlenb = csr_read(CSR_VLENB); + uint8_t *base = dst->vregs; + + /* Step 3: Save vector registers */ +#define SAVE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vs8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + SAVE_VREG(0); + SAVE_VREG(8); + SAVE_VREG(16); + SAVE_VREG(24); + +#undef SAVE_VREG + + /* Step 4. Restore original mstatus LAST */ + csr_write(CSR_MSTATUS, mstatus_orig); +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ + unsigned long mstatus_orig; + + if (!src) + return; + + /* Step 1. Save original mstatus and Enable VS */ + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); + + const uint8_t *base = src->vregs; + ulong vlenb = csr_read(CSR_VLENB); + + /* Step 2: Restore vector registers */ +#define RESTORE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vl8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(base + (i) * vlenb) : "memory"); \ + }) \ + + RESTORE_VREG(0); + RESTORE_VREG(8); + RESTORE_VREG(16); + RESTORE_VREG(24); +#undef RESTORE_VREG + + /* Step 3: Restore CSR's last */ + /* Restore CSRs first */ + csr_write(vcsr, src->vcsr); + csr_write(vstart, src->vstart); + + /* Step 4. Restore original mstatus LAST */ + csr_write(CSR_MSTATUS, mstatus_orig); +} + +#else + +void sbi_vector_save(struct sbi_vector_context *dst) +{ +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ +} + +size_t sbi_vector_context_size(void) +{ + return 0; +} +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ -- 2.43.0 From dave.patel at riscstar.com Sun May 17 22:42:16 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Mon, 18 May 2026 06:42:16 +0100 Subject: [PATCH v7 0/3] Add eager FP and RISC-V vector context switching support Message-ID: <20260518054219.58708-1-dave.patel@riscstar.com> From: Dave Patel This patch series adds proper support for per-domain floating-point (FP) and vector (V) contexts in the domain context switch logic. Each domain now maintains its own FP and vector state, which is saved and restored during domain switches. Conditionalize FP and Vector save/restore based on extensions, unconditional save and restore of floating-point (FP) and Vector registers fails on generic platform firmware. This firmware must run on multiple platforms that may lack these extensions. Address this by conditionally executing FP save/restore only if the underlying hart supports the F or D extensions. Similarly, perform Vector save/restore only if the hart supports the Vector extension. Changes include: - Add Vector save and restore functionality - Add Floating Point save and restore functioanlity - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' to allocate and free per-domain FP and vector context. - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. - Updated `switch_to_next_domain_context()` to save/restore FP and vector contexts safely: - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. - Added runtime checks for FP and vector extensions where needed. - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() to check for these capabilities before performing the context switches This improves support for multi-domain systems with FP and Vector extensions, and prevents corruption of FP/Vector state during domain switches. Dave Patel (3): lib: sbi: Add RISC-V vector context save/restore support lib: sbi: Add floating-point context save/restore support. lib: sbi: domain FP/Vector context support for context switch include/sbi/sbi_fp.h | 26 +++++ include/sbi/sbi_hart.h | 6 + include/sbi/sbi_vector.h | 28 +++++ lib/sbi/objects.mk | 2 + lib/sbi/sbi_domain_context.c | 36 ++++++ lib/sbi/sbi_fp.c | 207 +++++++++++++++++++++++++++++++++++ lib/sbi/sbi_hart.c | 3 + lib/sbi/sbi_vector.c | 118 ++++++++++++++++++++ 8 files changed, 426 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_fp.c create mode 100644 lib/sbi/sbi_vector.c -- 2.43.0 From dave.patel at riscstar.com Sun May 17 22:42:18 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Mon, 18 May 2026 06:42:18 +0100 Subject: [PATCH v7 2/3] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260518054219.58708-1-dave.patel@riscstar.com> References: <20260518054219.58708-1-dave.patel@riscstar.com> Message-ID: <20260518054219.58708-3-dave.patel@riscstar.com> From: Dave Patel Add support for saving and restoring RISC-V floating-point (F/D) extension state in OpenSBI. This introduces a floating-point context structure and helper routines to perform full context save and restore. The floating-point context includes storage for all 32 FPi registers (f0?f31) along with the fcsr control and status register. The register state is saved and restored using double-precision load/store instructions (fsd/fld), and single-precision load/store instructions (fsw/flw) on an RV64 system with F and D-extension support. The implementation follows an eager context switching model where the entire FP state is saved and restored on every context switch. This avoids the need for trap-based lazy management and keeps the design simple and deterministic. Signed-off-by: Dave Patel " --- include/sbi/sbi_fp.h | 26 ++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_fp.c | 207 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 lib/sbi/sbi_fp.c diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h new file mode 100644 index 00000000..a00756fc --- /dev/null +++ b/include/sbi/sbi_fp.h @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ +#ifndef __SBI_FP_H__ +#define __SBI_FP_H__ + +#include + +struct sbi_fp_context { +#if __riscv_d + uint64_t f[32]; +#else + uint32_t f[32]; +#endif + unsigned long fcsr; +}; + +void sbi_fp_save(struct sbi_fp_context *dst); +void sbi_fp_restore(const struct sbi_fp_context *src); + +#endif //__SBI_VECTOR_H__ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index ddb2e7ac..d8182383 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -110,3 +110,4 @@ libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o libsbi-objs-y += sbi_vector.o +libsbi-objs-y += sbi_fp.o diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c new file mode 100644 index 00000000..78d13a9c --- /dev/null +++ b/lib/sbi/sbi_fp.c @@ -0,0 +1,207 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include + +#if defined(__riscv_f) || defined(__riscv_d) + +void sbi_fp_save(struct sbi_fp_context *dst) +{ + unsigned long mstatus_orig; + + if (!dst) + return; + + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); + +#if defined(__riscv_d) + asm volatile( + "fsd f0, 0(%0)\n" + "fsd f1, 8(%0)\n" + "fsd f2, 16(%0)\n" + "fsd f3, 24(%0)\n" + "fsd f4, 32(%0)\n" + "fsd f5, 40(%0)\n" + "fsd f6, 48(%0)\n" + "fsd f7, 56(%0)\n" + "fsd f8, 64(%0)\n" + "fsd f9, 72(%0)\n" + "fsd f10, 80(%0)\n" + "fsd f11, 88(%0)\n" + "fsd f12, 96(%0)\n" + "fsd f13, 104(%0)\n" + "fsd f14, 112(%0)\n" + "fsd f15, 120(%0)\n" + "fsd f16, 128(%0)\n" + "fsd f17, 136(%0)\n" + "fsd f18, 144(%0)\n" + "fsd f19, 152(%0)\n" + "fsd f20, 160(%0)\n" + "fsd f21, 168(%0)\n" + "fsd f22, 176(%0)\n" + "fsd f23, 184(%0)\n" + "fsd f24, 192(%0)\n" + "fsd f25, 200(%0)\n" + "fsd f26, 208(%0)\n" + "fsd f27, 216(%0)\n" + "fsd f28, 224(%0)\n" + "fsd f29, 232(%0)\n" + "fsd f30, 240(%0)\n" + "fsd f31, 248(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#else + asm volatile( + "fsw f0, 0(%0)\n" + "fsw f1, 4(%0)\n" + "fsw f2, 8(%0)\n" + "fsw f3, 12(%0)\n" + "fsw f4, 16(%0)\n" + "fsw f5, 20(%0)\n" + "fsw f6, 24(%0)\n" + "fsw f7, 28(%0)\n" + "fsw f8, 32(%0)\n" + "fsw f9, 36(%0)\n" + "fsw f10, 40(%0)\n" + "fsw f11, 44(%0)\n" + "fsw f12, 48(%0)\n" + "fsw f13, 52(%0)\n" + "fsw f14, 56(%0)\n" + "fsw f15, 60(%0)\n" + "fsw f16, 64(%0)\n" + "fsw f17, 68(%0)\n" + "fsw f18, 72(%0)\n" + "fsw f19, 76(%0)\n" + "fsw f20, 80(%0)\n" + "fsw f21, 84(%0)\n" + "fsw f22, 88(%0)\n" + "fsw f23, 92(%0)\n" + "fsw f24, 96(%0)\n" + "fsw f25, 100(%0)\n" + "fsw f26, 104(%0)\n" + "fsw f27, 108(%0)\n" + "fsw f28, 112(%0)\n" + "fsw f29, 116(%0)\n" + "fsw f30, 120(%0)\n" + "fsw f31, 124(%0)\n" + : + : "r"(dst->f) + : "memory" + ); +#endif //__riscv_d + + dst->fcsr = csr_read(CSR_FCSR); + + /* Restore original mstatus LAST */ + csr_write(CSR_MSTATUS, mstatus_orig); +} + +void sbi_fp_restore(const struct sbi_fp_context *src) +{ + unsigned long mstatus_orig; + + if (!src) + return; + + /* Save original mstatus */ + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); + +#if defined(__riscv_d) + asm volatile( + "fld f0, 0(%0)\n" + "fld f1, 8(%0)\n" + "fld f2, 16(%0)\n" + "fld f3, 24(%0)\n" + "fld f4, 32(%0)\n" + "fld f5, 40(%0)\n" + "fld f6, 48(%0)\n" + "fld f7, 56(%0)\n" + "fld f8, 64(%0)\n" + "fld f9, 72(%0)\n" + "fld f10, 80(%0)\n" + "fld f11, 88(%0)\n" + "fld f12, 96(%0)\n" + "fld f13, 104(%0)\n" + "fld f14, 112(%0)\n" + "fld f15, 120(%0)\n" + "fld f16, 128(%0)\n" + "fld f17, 136(%0)\n" + "fld f18, 144(%0)\n" + "fld f19, 152(%0)\n" + "fld f20, 160(%0)\n" + "fld f21, 168(%0)\n" + "fld f22, 176(%0)\n" + "fld f23, 184(%0)\n" + "fld f24, 192(%0)\n" + "fld f25, 200(%0)\n" + "fld f26, 208(%0)\n" + "fld f27, 216(%0)\n" + "fld f28, 224(%0)\n" + "fld f29, 232(%0)\n" + "fld f30, 240(%0)\n" + "fld f31, 248(%0)\n" + : + : "r"(src->f) + : "memory" + ); +#else + + asm volatile( + "flw f0, 0(%0)\n" + "flw f1, 4(%0)\n" + "flw f2, 8(%0)\n" + "flw f3, 12(%0)\n" + "flw f4, 16(%0)\n" + "flw f5, 20(%0)\n" + "flw f6, 24(%0)\n" + "flw f7, 28(%0)\n" + "flw f8, 32(%0)\n" + "flw f9, 36(%0)\n" + "flw f10, 40(%0)\n" + "flw f11, 44(%0)\n" + "flw f12, 48(%0)\n" + "flw f13, 52(%0)\n" + "flw f14, 56(%0)\n" + "flw f15, 60(%0)\n" + "flw f16, 64(%0)\n" + "flw f17, 68(%0)\n" + "flw f18, 72(%0)\n" + "flw f19, 76(%0)\n" + "flw f20, 80(%0)\n" + "flw f21, 84(%0)\n" + "flw f22, 88(%0)\n" + "flw f23, 92(%0)\n" + "flw f24, 96(%0)\n" + "flw f25, 100(%0)\n" + "flw f26, 104(%0)\n" + "flw f27, 108(%0)\n" + "flw f28, 112(%0)\n" + "flw f29, 116(%0)\n" + "flw f30, 120(%0)\n" + "flw f31, 124(%0)\n" + : + : "r"(src->f) + : "memory" + ); + +#endif + + csr_write(CSR_FCSR, src->fcsr); + + /* Restore original mstatus LAST */ + csr_write(CSR_MSTATUS, mstatus_orig); +} +#else +void sbi_fp_save(struct sbi_fp_context *dst) {} +void sbi_fp_restore(const struct sbi_fp_context *src) {} +#endif // FP present -- 2.43.0 From dave.patel at riscstar.com Sun May 17 22:42:19 2026 From: dave.patel at riscstar.com (dave.patel at riscstar.com) Date: Mon, 18 May 2026 06:42:19 +0100 Subject: [PATCH v7 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260518054219.58708-1-dave.patel@riscstar.com> References: <20260518054219.58708-1-dave.patel@riscstar.com> Message-ID: <20260518054219.58708-4-dave.patel@riscstar.com> From: Dave Patel This patch adds proper support for per-domain floating-point (FP) and vector (V) contexts in the domain context switch logic. Each domain now maintains its own FP and vector state, which is saved and restored during domain switches. Conditionalize FP and Vector save/restore based on extensions, unconditional save and restore of floating-point (FP) and Vector registers fails on generic platform firmware. This firmware must run on multiple platforms that may lack these extensions. Address this by conditionally executing FP save/restore only if the underlying hart supports the F or D extensions. Similarly, perform Vector save/restore only if the hart supports the Vector extension. Changes include: - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' to allocate and free per-domain FP and vector context. - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. - Updated `switch_to_next_domain_context()` to save/restore FP and vector contexts safely: - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. - Added runtime checks for FP and vector extensions where needed. - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() to check for these capabilities before performing the context switches This improves support for multi-domain systems with FP and Vector extensions, and prevents corruption of FP/Vector state during domain switches. Signed-off-by: Dave Patel --- include/sbi/sbi_hart.h | 6 ++++++ lib/sbi/sbi_domain_context.c | 36 ++++++++++++++++++++++++++++++++++++ lib/sbi/sbi_hart.c | 3 +++ 3 files changed, 45 insertions(+) diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index a788b34c..68a01b97 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -87,6 +87,12 @@ enum sbi_hart_extensions { SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, /** Hart has Xsfcease extension */ SBI_HART_EXT_XSIFIVE_CEASE, + /** Hart has V extension */ + SBI_HART_EXT_V, + /** Hart has F extension */ + SBI_HART_EXT_F, + /** Hart has D extension */ + SBI_HART_EXT_D, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index 158f4990..e06103c3 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include /** Context representation for a hart within a domain */ struct hart_context { @@ -55,6 +57,11 @@ struct hart_context { struct hart_context *prev_ctx; /** Is context initialized and runnable */ bool initialized; + + /** float context state */ + struct sbi_fp_context fp_ctx; + /** vector context state */ + struct sbi_vector_context *vec_ctx; }; static struct sbi_domain_data dcpriv; @@ -143,6 +150,22 @@ static int switch_to_next_domain_context(struct hart_context *ctx, if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); + /* Eager context switch F */ + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_F) || + sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_D)) { + sbi_fp_save(&ctx->fp_ctx); + sbi_fp_restore(&dom_ctx->fp_ctx); + } + + /* Eager context switch V */ + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V)) { + sbi_vector_save(ctx->vec_ctx); + sbi_vector_restore(dom_ctx->vec_ctx); + } + /* Save current trap state and restore target domain's trap state */ trap_ctx = sbi_trap_get_context(scratch); sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); @@ -168,6 +191,7 @@ static int switch_to_next_domain_context(struct hart_context *ctx, static int hart_context_init(u32 hartindex) { + size_t vec_size; struct hart_context *ctx; struct sbi_domain *dom; @@ -180,6 +204,18 @@ static int hart_context_init(u32 hartindex) if (!ctx) return SBI_ENOMEM; + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V)) { + vec_size = sbi_vector_context_size(); + + /* Allocate the vector context pointer */ + ctx->vec_ctx = sbi_zalloc(vec_size); + if (!ctx->vec_ctx) { + sbi_free(ctx); + return SBI_ENOMEM; + } + } + /* Bind context and domain */ ctx->dom = dom; hart_context_set(dom, hartindex, ctx); diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 60e95bca..b5e0ee10 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -396,6 +396,9 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), + __SBI_HART_EXT_DATA(v, SBI_HART_EXT_V), + __SBI_HART_EXT_DATA(f, SBI_HART_EXT_F), + __SBI_HART_EXT_DATA(d, SBI_HART_EXT_D), }; _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), -- 2.43.0 From anup at brainfault.org Mon May 18 01:25:51 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 18 May 2026 13:55:51 +0530 Subject: [PATCH v7 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260518054219.58708-2-dave.patel@riscstar.com> References: <20260518054219.58708-1-dave.patel@riscstar.com> <20260518054219.58708-2-dave.patel@riscstar.com> Message-ID: On Mon, May 18, 2026 at 11:12?AM wrote: > > From: Dave Patel > > Eager context switch: Add support for saving and restoring RISC-V vector > extension state in OpenSBI. This introduces a per-hart vector context > structure and helper routines to perform full context save and restore. > > The vector context includes vcsr CSRs along with storage for all 32 vector > registers. The register state is saved and restored using byte-wise vector > load/store instructions (vs8r/vl8r). > > The implementation follows an eager context switching model where the entire > vector state is saved and restored on every context switch. This provides a > simple and deterministic mechanism without requiring lazy trap-based > management. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_vector.h | 28 ++++++++++ > lib/sbi/objects.mk | 1 + > lib/sbi/sbi_vector.c | 118 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 147 insertions(+) > create mode 100644 include/sbi/sbi_vector.h > create mode 100644 lib/sbi/sbi_vector.c > > diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h > new file mode 100644 > index 00000000..d92fc03d > --- /dev/null > +++ b/include/sbi/sbi_vector.h > @@ -0,0 +1,28 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#ifndef __SBI_VECTOR_H__ > +#define __SBI_VECTOR_H__ > + > +#include > + > +struct sbi_vector_context { > + unsigned long vcsr; > + unsigned long vstart; > + > + /* size depends on VLEN */ > + uint8_t vregs[]; > +}; > + > +void sbi_vector_save(struct sbi_vector_context *dst); > +void sbi_vector_restore(const struct sbi_vector_context *src); > +size_t sbi_vector_context_size(void); > + > +#endif //__SBI_VECTOR_H__ > + I had commented on previous series that this newline is redundant but I still see it here. > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index 97cc4521..ddb2e7ac 100644 > --- a/lib/sbi/objects.mk > +++ b/lib/sbi/objects.mk > @@ -109,3 +109,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o > libsbi-objs-y += sbi_unpriv.o > libsbi-objs-y += sbi_expected_trap.o > libsbi-objs-y += sbi_cppc.o > +libsbi-objs-y += sbi_vector.o > diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c > new file mode 100644 > index 00000000..e4a1f515 > --- /dev/null > +++ b/lib/sbi/sbi_vector.c > @@ -0,0 +1,118 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#ifdef OPENSBI_CC_SUPPORT_VECTOR > + > +size_t sbi_vector_context_size(void) > +{ > + return sizeof(struct sbi_vector_context) + (32 * csr_read(CSR_VLENB)); > +} > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > + unsigned long mstatus_orig; > + > + if (!dst) > + return; > + > + /* Step 1. Save original mstatus and Enable VS */ > + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); > + > + /* Step 2: Save CSRs */ > + dst->vcsr = csr_read(vcsr); > + dst->vstart = csr_read(vstart); > + > + ulong vlenb = csr_read(CSR_VLENB); > + uint8_t *base = dst->vregs; > + > + /* Step 3: Save vector registers */ > +#define SAVE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vs8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + SAVE_VREG(0); > + SAVE_VREG(8); > + SAVE_VREG(16); > + SAVE_VREG(24); > + > +#undef SAVE_VREG > + > + /* Step 4. Restore original mstatus LAST */ > + csr_write(CSR_MSTATUS, mstatus_orig); > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > + unsigned long mstatus_orig; > + > + if (!src) > + return; > + > + /* Step 1. Save original mstatus and Enable VS */ > + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); > + > + const uint8_t *base = src->vregs; > + ulong vlenb = csr_read(CSR_VLENB); > + > + /* Step 2: Restore vector registers */ > +#define RESTORE_VREG(i) \ > + ({ \ > + asm volatile( \ > + " .option push\n\t" \ > + " .option arch, +v\n\t" \ > + " vl8r.v v" #i ", (%0)\n\t" \ > + " .option pop\n\t" \ > + :: "r"(base + (i) * vlenb) : "memory"); \ > + }) \ > + > + RESTORE_VREG(0); > + RESTORE_VREG(8); > + RESTORE_VREG(16); > + RESTORE_VREG(24); > +#undef RESTORE_VREG > + > + /* Step 3: Restore CSR's last */ > + /* Restore CSRs first */ > + csr_write(vcsr, src->vcsr); > + csr_write(vstart, src->vstart); > + > + /* Step 4. Restore original mstatus LAST */ > + csr_write(CSR_MSTATUS, mstatus_orig); > +} > + > +#else > + > +void sbi_vector_save(struct sbi_vector_context *dst) > +{ > +} > + > +void sbi_vector_restore(const struct sbi_vector_context *src) > +{ > +} > + > +size_t sbi_vector_context_size(void) > +{ > + return 0; Use tabs for alignment instead of spaces. > +} > +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Mon May 18 01:25:55 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 18 May 2026 13:55:55 +0530 Subject: [PATCH v7 2/3] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260518054219.58708-3-dave.patel@riscstar.com> References: <20260518054219.58708-1-dave.patel@riscstar.com> <20260518054219.58708-3-dave.patel@riscstar.com> Message-ID: On Mon, May 18, 2026 at 11:12?AM wrote: > > From: Dave Patel > > Add support for saving and restoring RISC-V floating-point (F/D) extension > state in OpenSBI. This introduces a floating-point context structure and > helper routines to perform full context save and restore. > > The floating-point context includes storage for all 32 FPi registers (f0?f31) > along with the fcsr control and status register. The register state is saved > and restored using double-precision load/store instructions (fsd/fld), and > single-precision load/store instructions (fsw/flw) on an RV64 system with > F and D-extension support. > > The implementation follows an eager context switching model where the entire > FP state is saved and restored on every context switch. This avoids the need > for trap-based lazy management and keeps the design simple and deterministic. > > Signed-off-by: Dave Patel " Extra quote at the end-of-line which causes problem when applying. > --- > include/sbi/sbi_fp.h | 26 ++++++ > lib/sbi/objects.mk | 1 + > lib/sbi/sbi_fp.c | 207 +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 234 insertions(+) > create mode 100644 include/sbi/sbi_fp.h > create mode 100644 lib/sbi/sbi_fp.c > > diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h > new file mode 100644 > index 00000000..a00756fc > --- /dev/null > +++ b/include/sbi/sbi_fp.h > @@ -0,0 +1,26 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > +#ifndef __SBI_FP_H__ > +#define __SBI_FP_H__ > + > +#include > + > +struct sbi_fp_context { > +#if __riscv_d > + uint64_t f[32]; > +#else > + uint32_t f[32]; > +#endif > + unsigned long fcsr; > +}; > + > +void sbi_fp_save(struct sbi_fp_context *dst); > +void sbi_fp_restore(const struct sbi_fp_context *src); > + > +#endif //__SBI_VECTOR_H__ > diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk > index ddb2e7ac..d8182383 100644 > --- a/lib/sbi/objects.mk > +++ b/lib/sbi/objects.mk > @@ -110,3 +110,4 @@ libsbi-objs-y += sbi_unpriv.o > libsbi-objs-y += sbi_expected_trap.o > libsbi-objs-y += sbi_cppc.o > libsbi-objs-y += sbi_vector.o > +libsbi-objs-y += sbi_fp.o > diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c > new file mode 100644 > index 00000000..78d13a9c > --- /dev/null > +++ b/lib/sbi/sbi_fp.c > @@ -0,0 +1,207 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 RISCstar Solutions. > + * > + * Authors: > + * Dave Patel > + */ > + > +#include > +#include > +#include > + > +#if defined(__riscv_f) || defined(__riscv_d) > + > +void sbi_fp_save(struct sbi_fp_context *dst) > +{ > + unsigned long mstatus_orig; > + > + if (!dst) > + return; > + > + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); s/MSTATUS_VS/MSTATUS_FS/ This makes me wonder if you are testing every revision of this series before sending. > + > +#if defined(__riscv_d) > + asm volatile( > + "fsd f0, 0(%0)\n" > + "fsd f1, 8(%0)\n" > + "fsd f2, 16(%0)\n" > + "fsd f3, 24(%0)\n" > + "fsd f4, 32(%0)\n" > + "fsd f5, 40(%0)\n" > + "fsd f6, 48(%0)\n" > + "fsd f7, 56(%0)\n" > + "fsd f8, 64(%0)\n" > + "fsd f9, 72(%0)\n" > + "fsd f10, 80(%0)\n" > + "fsd f11, 88(%0)\n" > + "fsd f12, 96(%0)\n" > + "fsd f13, 104(%0)\n" > + "fsd f14, 112(%0)\n" > + "fsd f15, 120(%0)\n" > + "fsd f16, 128(%0)\n" > + "fsd f17, 136(%0)\n" > + "fsd f18, 144(%0)\n" > + "fsd f19, 152(%0)\n" > + "fsd f20, 160(%0)\n" > + "fsd f21, 168(%0)\n" > + "fsd f22, 176(%0)\n" > + "fsd f23, 184(%0)\n" > + "fsd f24, 192(%0)\n" > + "fsd f25, 200(%0)\n" > + "fsd f26, 208(%0)\n" > + "fsd f27, 216(%0)\n" > + "fsd f28, 224(%0)\n" > + "fsd f29, 232(%0)\n" > + "fsd f30, 240(%0)\n" > + "fsd f31, 248(%0)\n" > + : > + : "r"(dst->f) > + : "memory" > + ); > +#else > + asm volatile( > + "fsw f0, 0(%0)\n" > + "fsw f1, 4(%0)\n" > + "fsw f2, 8(%0)\n" > + "fsw f3, 12(%0)\n" > + "fsw f4, 16(%0)\n" > + "fsw f5, 20(%0)\n" > + "fsw f6, 24(%0)\n" > + "fsw f7, 28(%0)\n" > + "fsw f8, 32(%0)\n" > + "fsw f9, 36(%0)\n" > + "fsw f10, 40(%0)\n" > + "fsw f11, 44(%0)\n" > + "fsw f12, 48(%0)\n" > + "fsw f13, 52(%0)\n" > + "fsw f14, 56(%0)\n" > + "fsw f15, 60(%0)\n" > + "fsw f16, 64(%0)\n" > + "fsw f17, 68(%0)\n" > + "fsw f18, 72(%0)\n" > + "fsw f19, 76(%0)\n" > + "fsw f20, 80(%0)\n" > + "fsw f21, 84(%0)\n" > + "fsw f22, 88(%0)\n" > + "fsw f23, 92(%0)\n" > + "fsw f24, 96(%0)\n" > + "fsw f25, 100(%0)\n" > + "fsw f26, 104(%0)\n" > + "fsw f27, 108(%0)\n" > + "fsw f28, 112(%0)\n" > + "fsw f29, 116(%0)\n" > + "fsw f30, 120(%0)\n" > + "fsw f31, 124(%0)\n" > + : > + : "r"(dst->f) > + : "memory" > + ); > +#endif //__riscv_d > + > + dst->fcsr = csr_read(CSR_FCSR); > + > + /* Restore original mstatus LAST */ > + csr_write(CSR_MSTATUS, mstatus_orig); > +} > + > +void sbi_fp_restore(const struct sbi_fp_context *src) > +{ > + unsigned long mstatus_orig; > + > + if (!src) > + return; > + > + /* Save original mstatus */ > + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); > + > +#if defined(__riscv_d) > + asm volatile( > + "fld f0, 0(%0)\n" > + "fld f1, 8(%0)\n" > + "fld f2, 16(%0)\n" > + "fld f3, 24(%0)\n" > + "fld f4, 32(%0)\n" > + "fld f5, 40(%0)\n" > + "fld f6, 48(%0)\n" > + "fld f7, 56(%0)\n" > + "fld f8, 64(%0)\n" > + "fld f9, 72(%0)\n" > + "fld f10, 80(%0)\n" > + "fld f11, 88(%0)\n" > + "fld f12, 96(%0)\n" > + "fld f13, 104(%0)\n" > + "fld f14, 112(%0)\n" > + "fld f15, 120(%0)\n" > + "fld f16, 128(%0)\n" > + "fld f17, 136(%0)\n" > + "fld f18, 144(%0)\n" > + "fld f19, 152(%0)\n" > + "fld f20, 160(%0)\n" > + "fld f21, 168(%0)\n" > + "fld f22, 176(%0)\n" > + "fld f23, 184(%0)\n" > + "fld f24, 192(%0)\n" > + "fld f25, 200(%0)\n" > + "fld f26, 208(%0)\n" > + "fld f27, 216(%0)\n" > + "fld f28, 224(%0)\n" > + "fld f29, 232(%0)\n" > + "fld f30, 240(%0)\n" > + "fld f31, 248(%0)\n" > + : > + : "r"(src->f) > + : "memory" > + ); > +#else > + > + asm volatile( > + "flw f0, 0(%0)\n" > + "flw f1, 4(%0)\n" > + "flw f2, 8(%0)\n" > + "flw f3, 12(%0)\n" > + "flw f4, 16(%0)\n" > + "flw f5, 20(%0)\n" > + "flw f6, 24(%0)\n" > + "flw f7, 28(%0)\n" > + "flw f8, 32(%0)\n" > + "flw f9, 36(%0)\n" > + "flw f10, 40(%0)\n" > + "flw f11, 44(%0)\n" > + "flw f12, 48(%0)\n" > + "flw f13, 52(%0)\n" > + "flw f14, 56(%0)\n" > + "flw f15, 60(%0)\n" > + "flw f16, 64(%0)\n" > + "flw f17, 68(%0)\n" > + "flw f18, 72(%0)\n" > + "flw f19, 76(%0)\n" > + "flw f20, 80(%0)\n" > + "flw f21, 84(%0)\n" > + "flw f22, 88(%0)\n" > + "flw f23, 92(%0)\n" > + "flw f24, 96(%0)\n" > + "flw f25, 100(%0)\n" > + "flw f26, 104(%0)\n" > + "flw f27, 108(%0)\n" > + "flw f28, 112(%0)\n" > + "flw f29, 116(%0)\n" > + "flw f30, 120(%0)\n" > + "flw f31, 124(%0)\n" > + : > + : "r"(src->f) > + : "memory" > + ); > + > +#endif > + > + csr_write(CSR_FCSR, src->fcsr); > + > + /* Restore original mstatus LAST */ > + csr_write(CSR_MSTATUS, mstatus_orig); > +} > +#else > +void sbi_fp_save(struct sbi_fp_context *dst) {} > +void sbi_fp_restore(const struct sbi_fp_context *src) {} > +#endif // FP present > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup at brainfault.org Mon May 18 01:25:59 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 18 May 2026 13:55:59 +0530 Subject: [PATCH v7 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260518054219.58708-4-dave.patel@riscstar.com> References: <20260518054219.58708-1-dave.patel@riscstar.com> <20260518054219.58708-4-dave.patel@riscstar.com> Message-ID: On Mon, May 18, 2026 at 11:12?AM wrote: > > From: Dave Patel > > This patch adds proper support for per-domain floating-point (FP) and > vector (V) contexts in the domain context switch logic. Each domain > now maintains its own FP and vector state, which is saved and restored > during domain switches. > > Conditionalize FP and Vector save/restore based on extensions, unconditional > save and restore of floating-point (FP) and Vector registers fails on > generic platform firmware. This firmware must run on multiple platforms > that may lack these extensions. > > Address this by conditionally executing FP save/restore only if the underlying > hart supports the F or D extensions. Similarly, perform Vector save/restore > only if the hart supports the Vector extension. > > Changes include: > > - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. > - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' > to allocate and free per-domain FP and vector context. > - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. > - Updated `switch_to_next_domain_context()` to save/restore FP and vector > contexts safely: > - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. > - Added runtime checks for FP and vector extensions where needed. > - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum > sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() > to check for these capabilities before performing the context switches I clearly mentioned last time that changelog needs to be in cover-letter. It's very frustrating as reviewer when only select comments are being addressed. > > This improves support for multi-domain systems with FP and Vector > extensions, and prevents corruption of FP/Vector state during domain > switches. > > Signed-off-by: Dave Patel > --- > include/sbi/sbi_hart.h | 6 ++++++ > lib/sbi/sbi_domain_context.c | 36 ++++++++++++++++++++++++++++++++++++ > lib/sbi/sbi_hart.c | 3 +++ > 3 files changed, 45 insertions(+) This does not apply on latest OpenSBI master which means you are not rebasing your series upon latest OpenSBI before sending it. > > diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h > index a788b34c..68a01b97 100644 > --- a/include/sbi/sbi_hart.h > +++ b/include/sbi/sbi_hart.h > @@ -87,6 +87,12 @@ enum sbi_hart_extensions { > SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1, > /** Hart has Xsfcease extension */ > SBI_HART_EXT_XSIFIVE_CEASE, > + /** Hart has V extension */ > + SBI_HART_EXT_V, > + /** Hart has F extension */ > + SBI_HART_EXT_F, > + /** Hart has D extension */ > + SBI_HART_EXT_D, > > /** Maximum index of Hart extension */ > SBI_HART_EXT_MAX, > diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c > index 158f4990..e06103c3 100644 > --- a/lib/sbi/sbi_domain_context.c > +++ b/lib/sbi/sbi_domain_context.c > @@ -18,6 +18,8 @@ > #include > #include > #include > +#include > +#include > > /** Context representation for a hart within a domain */ > struct hart_context { > @@ -55,6 +57,11 @@ struct hart_context { > struct hart_context *prev_ctx; > /** Is context initialized and runnable */ > bool initialized; > + > + /** float context state */ > + struct sbi_fp_context fp_ctx; > + /** vector context state */ > + struct sbi_vector_context *vec_ctx; > }; > > static struct sbi_domain_data dcpriv; > @@ -143,6 +150,22 @@ static int switch_to_next_domain_context(struct hart_context *ctx, > if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) > ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); > > + /* Eager context switch F */ > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_F) || > + sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_D)) { The "scratch" local variable already points to sbi_scratch_thishart_ptr() > + sbi_fp_save(&ctx->fp_ctx); > + sbi_fp_restore(&dom_ctx->fp_ctx); > + } > + > + /* Eager context switch V */ > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_V)) { Same as above comment. > + sbi_vector_save(ctx->vec_ctx); > + sbi_vector_restore(dom_ctx->vec_ctx); > + } > + > /* Save current trap state and restore target domain's trap state */ > trap_ctx = sbi_trap_get_context(scratch); > sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); > @@ -168,6 +191,7 @@ static int switch_to_next_domain_context(struct hart_context *ctx, > > static int hart_context_init(u32 hartindex) > { > + size_t vec_size; > struct hart_context *ctx; > struct sbi_domain *dom; > > @@ -180,6 +204,18 @@ static int hart_context_init(u32 hartindex) > if (!ctx) > return SBI_ENOMEM; > > + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), > + SBI_HART_EXT_V)) { > + vec_size = sbi_vector_context_size(); > + > + /* Allocate the vector context pointer */ > + ctx->vec_ctx = sbi_zalloc(vec_size); > + if (!ctx->vec_ctx) { > + sbi_free(ctx); > + return SBI_ENOMEM; > + } > + } > + > /* Bind context and domain */ > ctx->dom = dom; > hart_context_set(dom, hartindex, ctx); > diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c > index 60e95bca..b5e0ee10 100644 > --- a/lib/sbi/sbi_hart.c > +++ b/lib/sbi/sbi_hart.c > @@ -396,6 +396,9 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { > __SBI_HART_EXT_DATA(ssstateen, SBI_HART_EXT_SSSTATEEN), > __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), > __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), > + __SBI_HART_EXT_DATA(v, SBI_HART_EXT_V), > + __SBI_HART_EXT_DATA(f, SBI_HART_EXT_F), > + __SBI_HART_EXT_DATA(d, SBI_HART_EXT_D), > }; > > _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi Regards, Anup From anup.patel at oss.qualcomm.com Mon May 18 01:30:20 2026 From: anup.patel at oss.qualcomm.com (Anup Patel) Date: Mon, 18 May 2026 14:00:20 +0530 Subject: [PATCH v8 0/3] Add eager FP and Vector context switching support Message-ID: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> From: Dave Patel This patch series adds proper support for per-domain floating-point (FP) and vector (V) contexts in the domain context switch logic. Each domain now maintains its own FP and vector state, which is saved and restored during domain switches. Conditionalize FP and Vector save/restore based on extensions, unconditional save and restore of floating-point (FP) and Vector registers fails on generic platform firmware. This firmware must run on multiple platforms that may lack these extensions. Address this by conditionally executing FP save/restore only if the underlying hart supports the F or D extensions. Similarly, perform Vector save/restore only if the hart supports the Vector extension. Changes since v7: - Rebased on latest OpenSBI master - Addressed all my comments on v7 series Changes include: - Add Vector save and restore functionality - Add Floating Point save and restore functioanlity - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' to allocate and free per-domain FP and vector context. - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. - Updated `switch_to_next_domain_context()` to save/restore FP and vector contexts safely: - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. - Added runtime checks for FP and vector extensions where needed. - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() to check for these capabilities before performing the context switches This improves support for multi-domain systems with FP and Vector extensions, and prevents corruption of FP/Vector state during domain switches. Dave Patel (3): lib: sbi: Add RISC-V vector context save/restore support lib: sbi: Add floating-point context save/restore support. lib: sbi: domain FP/Vector context support for context switch include/sbi/sbi_fp.h | 35 +++++++ include/sbi/sbi_hart.h | 6 ++ include/sbi/sbi_vector.h | 40 ++++++++ lib/sbi/objects.mk | 2 + lib/sbi/sbi_domain_context.c | 33 ++++++ lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++ lib/sbi/sbi_hart.c | 3 + lib/sbi/sbi_vector.c | 94 +++++++++++++++++ 8 files changed, 405 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_fp.c create mode 100644 lib/sbi/sbi_vector.c -- 2.43.0 From anup.patel at oss.qualcomm.com Mon May 18 01:30:21 2026 From: anup.patel at oss.qualcomm.com (Anup Patel) Date: Mon, 18 May 2026 14:00:21 +0530 Subject: [PATCH v8 1/3] lib: sbi: Add RISC-V vector context save/restore support In-Reply-To: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> References: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> Message-ID: <20260518083023.997323-2-anup.patel@oss.qualcomm.com> From: Dave Patel Eager context switch: Add support for saving and restoring RISC-V vector extension state in OpenSBI. This introduces a per-hart vector context structure and helper routines to perform full context save and restore. The vector context includes vcsr CSRs along with storage for all 32 vector registers. The register state is saved and restored using byte-wise vector load/store instructions (vs8r/vl8r). The implementation follows an eager context switching model where the entire vector state is saved and restored on every context switch. This provides a simple and deterministic mechanism without requiring lazy trap-based management. Signed-off-by: Dave Patel Signed-off-by: Anup Patel --- include/sbi/sbi_vector.h | 40 +++++++++++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_vector.c | 94 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 include/sbi/sbi_vector.h create mode 100644 lib/sbi/sbi_vector.c diff --git a/include/sbi/sbi_vector.h b/include/sbi/sbi_vector.h new file mode 100644 index 00000000..f00184f0 --- /dev/null +++ b/include/sbi/sbi_vector.h @@ -0,0 +1,40 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#ifndef __SBI_VECTOR_H__ +#define __SBI_VECTOR_H__ + +#include + +struct sbi_vector_context { + unsigned long vcsr; + unsigned long vstart; + + /* size depends on VLEN */ + uint8_t vregs[]; +}; + +#ifdef OPENSBI_CC_SUPPORT_VECTOR +void sbi_vector_save(struct sbi_vector_context *dst); +void sbi_vector_restore(const struct sbi_vector_context *src); +size_t sbi_vector_context_size(void); +#else +static inline void sbi_vector_save(struct sbi_vector_context *dst) +{ +} +static inline void sbi_vector_restore(const struct sbi_vector_context *src) +{ +} +static inline size_t sbi_vector_context_size(void) +{ + return 0; +} +#endif /* OPENSBI_CC_SUPPORT_VECTOR */ + +#endif /* __SBI_VECTOR_H__ */ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 07d13229..c965e86d 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -102,3 +102,4 @@ libsbi-objs-y += sbi_trap_v_ldst.o libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o +libsbi-objs-$(CC_SUPPORT_VECTOR) += sbi_vector.o diff --git a/lib/sbi/sbi_vector.c b/lib/sbi/sbi_vector.c new file mode 100644 index 00000000..1f9e01cf --- /dev/null +++ b/lib/sbi/sbi_vector.c @@ -0,0 +1,94 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include +#include +#include +#include + +size_t sbi_vector_context_size(void) +{ + return sizeof(struct sbi_vector_context) + (32 * csr_read(CSR_VLENB)); +} + +void sbi_vector_save(struct sbi_vector_context *dst) +{ + unsigned long vlenb, mstatus_orig; + + if (!dst) + return; + + /* Step 1. Save original mstatus and Enable VS */ + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); + vlenb = csr_read(CSR_VLENB); + + /* Step 2: Save CSRs */ + dst->vcsr = csr_read(vcsr); + dst->vstart = csr_read(vstart); + + /* Step 3: Save vector registers */ +#define SAVE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vs8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(dst->vregs + (i) * vlenb) : "memory"); \ + }) \ + + SAVE_VREG(0); + SAVE_VREG(8); + SAVE_VREG(16); + SAVE_VREG(24); + +#undef SAVE_VREG + + /* Step 4. Restore original mstatus LAST */ + csr_write(CSR_MSTATUS, mstatus_orig); +} + +void sbi_vector_restore(const struct sbi_vector_context *src) +{ + unsigned long vlenb, mstatus_orig; + + if (!src) + return; + + /* Step 1. Save original mstatus and Enable VS */ + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); + vlenb = csr_read(CSR_VLENB); + + /* Step 2: Restore vector registers */ +#define RESTORE_VREG(i) \ + ({ \ + asm volatile( \ + " .option push\n\t" \ + " .option arch, +v\n\t" \ + " vl8r.v v" #i ", (%0)\n\t" \ + " .option pop\n\t" \ + :: "r"(src->vregs + (i) * vlenb) : "memory"); \ + }) \ + + RESTORE_VREG(0); + RESTORE_VREG(8); + RESTORE_VREG(16); + RESTORE_VREG(24); +#undef RESTORE_VREG + + /* Step 3: Restore CSR's last */ + /* Restore CSRs first */ + csr_write(vcsr, src->vcsr); + csr_write(vstart, src->vstart); + + /* Step 4. Restore original mstatus LAST */ + csr_write(CSR_MSTATUS, mstatus_orig); +} -- 2.43.0 From anup.patel at oss.qualcomm.com Mon May 18 01:30:23 2026 From: anup.patel at oss.qualcomm.com (Anup Patel) Date: Mon, 18 May 2026 14:00:23 +0530 Subject: [PATCH v8 3/3] lib: sbi: domain FP/Vector context support for context switch In-Reply-To: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> References: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> Message-ID: <20260518083023.997323-4-anup.patel@oss.qualcomm.com> From: Dave Patel This patch adds proper support for per-domain floating-point (FP) and vector (V) contexts in the domain context switch logic. Each domain now maintains its own FP and vector state, which is saved and restored during domain switches. Conditionalize FP and Vector save/restore based on extensions, unconditional save and restore of floating-point (FP) and Vector registers fails on generic platform firmware. This firmware must run on multiple platforms that may lack these extensions. Address this by conditionally executing FP save/restore only if the underlying hart supports the F or D extensions. Similarly, perform Vector save/restore only if the hart supports the Vector extension. This improves support for multi-domain systems with FP and Vector extensions, and prevents corruption of FP/Vector state during domain switches. Signed-off-by: Dave Patel Signed-off-by: Anup Patel --- include/sbi/sbi_hart.h | 6 ++++++ lib/sbi/sbi_domain_context.c | 33 +++++++++++++++++++++++++++++++++ lib/sbi/sbi_hart.c | 3 +++ 3 files changed, 42 insertions(+) diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index 937cdf29..543393bb 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -89,6 +89,12 @@ enum sbi_hart_extensions { SBI_HART_EXT_XSIFIVE_CEASE, /** Hart has Smrnmi extension */ SBI_HART_EXT_SMRNMI, + /** Hart has V extension */ + SBI_HART_EXT_V, + /** Hart has F extension */ + SBI_HART_EXT_F, + /** Hart has D extension */ + SBI_HART_EXT_D, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index 158f4990..fad402a8 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include /** Context representation for a hart within a domain */ struct hart_context { @@ -49,6 +51,11 @@ struct hart_context { /** Supervisor resource management configuration register */ unsigned long srmcfg; + /** Float context state */ + struct sbi_fp_context fp_ctx; + /** Vector context state */ + struct sbi_vector_context *vec_ctx; + /** Reference to the owning domain */ struct sbi_domain *dom; /** Previous context (caller) to jump to during context exits */ @@ -143,6 +150,19 @@ static int switch_to_next_domain_context(struct hart_context *ctx, if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSQOSID)) ctx->srmcfg = csr_swap(CSR_SRMCFG, dom_ctx->srmcfg); + /* Eager context switch for float */ + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_F) || + sbi_hart_has_extension(scratch, SBI_HART_EXT_D)) { + sbi_fp_save(&ctx->fp_ctx); + sbi_fp_restore(&dom_ctx->fp_ctx); + } + + /* Eager context switch for vector */ + if (sbi_hart_has_extension(scratch, SBI_HART_EXT_V)) { + sbi_vector_save(ctx->vec_ctx); + sbi_vector_restore(dom_ctx->vec_ctx); + } + /* Save current trap state and restore target domain's trap state */ trap_ctx = sbi_trap_get_context(scratch); sbi_memcpy(&ctx->trap_ctx, trap_ctx, sizeof(*trap_ctx)); @@ -168,6 +188,7 @@ static int switch_to_next_domain_context(struct hart_context *ctx, static int hart_context_init(u32 hartindex) { + size_t vec_size; struct hart_context *ctx; struct sbi_domain *dom; @@ -180,6 +201,18 @@ static int hart_context_init(u32 hartindex) if (!ctx) return SBI_ENOMEM; + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), + SBI_HART_EXT_V)) { + vec_size = sbi_vector_context_size(); + + /* Allocate the vector context pointer */ + ctx->vec_ctx = sbi_zalloc(vec_size); + if (!ctx->vec_ctx) { + sbi_free(ctx); + return SBI_ENOMEM; + } + } + /* Bind context and domain */ ctx->dom = dom; hart_context_set(dom, hartindex, ctx); diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 43197721..0f690f53 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -399,6 +399,9 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(xsfcflushdlone, SBI_HART_EXT_XSIFIVE_CFLUSH_D_L1), __SBI_HART_EXT_DATA(xsfcease, SBI_HART_EXT_XSIFIVE_CEASE), __SBI_HART_EXT_DATA(smrnmi, SBI_HART_EXT_SMRNMI), + __SBI_HART_EXT_DATA(v, SBI_HART_EXT_V), + __SBI_HART_EXT_DATA(f, SBI_HART_EXT_F), + __SBI_HART_EXT_DATA(d, SBI_HART_EXT_D), }; _Static_assert(SBI_HART_EXT_MAX == array_size(sbi_hart_ext), -- 2.43.0 From anup.patel at oss.qualcomm.com Mon May 18 01:30:22 2026 From: anup.patel at oss.qualcomm.com (Anup Patel) Date: Mon, 18 May 2026 14:00:22 +0530 Subject: [PATCH v8 2/3] lib: sbi: Add floating-point context save/restore support. In-Reply-To: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> References: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> Message-ID: <20260518083023.997323-3-anup.patel@oss.qualcomm.com> From: Dave Patel Add support for saving and restoring RISC-V floating-point (F/D) extension state in OpenSBI. This introduces a floating-point context structure and helper routines to perform full context save and restore. The floating-point context includes storage for all 32 FPi registers (f0?f31) along with the fcsr control and status register. The register state is saved and restored using double-precision load/store instructions (fsd/fld), and single-precision load/store instructions (fsw/flw) on an RV64 system with F and D-extension support. The implementation follows an eager context switching model where the entire FP state is saved and restored on every context switch. This avoids the need for trap-based lazy management and keeps the design simple and deterministic. Signed-off-by: Dave Patel Signed-off-by: Anup Patel --- include/sbi/sbi_fp.h | 35 ++++++++ lib/sbi/objects.mk | 1 + lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 228 insertions(+) create mode 100644 include/sbi/sbi_fp.h create mode 100644 lib/sbi/sbi_fp.c diff --git a/include/sbi/sbi_fp.h b/include/sbi/sbi_fp.h new file mode 100644 index 00000000..81998215 --- /dev/null +++ b/include/sbi/sbi_fp.h @@ -0,0 +1,35 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ +#ifndef __SBI_FP_H__ +#define __SBI_FP_H__ + +#include + +struct sbi_fp_context { +#if __riscv_d + uint64_t f[32]; +#else + uint32_t f[32]; +#endif + unsigned long fcsr; +}; + +#if defined(__riscv_f) || defined(__riscv_d) +void sbi_fp_save(struct sbi_fp_context *dst); +void sbi_fp_restore(const struct sbi_fp_context *src); +#else +static inline void sbi_fp_save(struct sbi_fp_context *dst) +{ +} +static inline void sbi_fp_restore(const struct sbi_fp_context *src) +{ +} +#endif /* __riscv_f || __riscv_d */ + +#endif /*__SBI_FP_H__ */ diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index c965e86d..a01a5ec3 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -103,3 +103,4 @@ libsbi-objs-y += sbi_unpriv.o libsbi-objs-y += sbi_expected_trap.o libsbi-objs-y += sbi_cppc.o libsbi-objs-$(CC_SUPPORT_VECTOR) += sbi_vector.o +libsbi-objs-y += sbi_fp.o diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c new file mode 100644 index 00000000..c8941252 --- /dev/null +++ b/lib/sbi/sbi_fp.c @@ -0,0 +1,192 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 RISCstar Solutions. + * + * Authors: + * Dave Patel + */ + +#include +#include +#include + +#if defined(__riscv_f) || defined(__riscv_d) + +void sbi_fp_save(struct sbi_fp_context *dst) +{ + unsigned long mstatus_orig; + + if (!dst) + return; + + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS); + + asm volatile( +#if defined(__riscv_d) + "fsd f0, 0(%0)\n" + "fsd f1, 8(%0)\n" + "fsd f2, 16(%0)\n" + "fsd f3, 24(%0)\n" + "fsd f4, 32(%0)\n" + "fsd f5, 40(%0)\n" + "fsd f6, 48(%0)\n" + "fsd f7, 56(%0)\n" + "fsd f8, 64(%0)\n" + "fsd f9, 72(%0)\n" + "fsd f10, 80(%0)\n" + "fsd f11, 88(%0)\n" + "fsd f12, 96(%0)\n" + "fsd f13, 104(%0)\n" + "fsd f14, 112(%0)\n" + "fsd f15, 120(%0)\n" + "fsd f16, 128(%0)\n" + "fsd f17, 136(%0)\n" + "fsd f18, 144(%0)\n" + "fsd f19, 152(%0)\n" + "fsd f20, 160(%0)\n" + "fsd f21, 168(%0)\n" + "fsd f22, 176(%0)\n" + "fsd f23, 184(%0)\n" + "fsd f24, 192(%0)\n" + "fsd f25, 200(%0)\n" + "fsd f26, 208(%0)\n" + "fsd f27, 216(%0)\n" + "fsd f28, 224(%0)\n" + "fsd f29, 232(%0)\n" + "fsd f30, 240(%0)\n" + "fsd f31, 248(%0)\n" +#else + "fsw f0, 0(%0)\n" + "fsw f1, 4(%0)\n" + "fsw f2, 8(%0)\n" + "fsw f3, 12(%0)\n" + "fsw f4, 16(%0)\n" + "fsw f5, 20(%0)\n" + "fsw f6, 24(%0)\n" + "fsw f7, 28(%0)\n" + "fsw f8, 32(%0)\n" + "fsw f9, 36(%0)\n" + "fsw f10, 40(%0)\n" + "fsw f11, 44(%0)\n" + "fsw f12, 48(%0)\n" + "fsw f13, 52(%0)\n" + "fsw f14, 56(%0)\n" + "fsw f15, 60(%0)\n" + "fsw f16, 64(%0)\n" + "fsw f17, 68(%0)\n" + "fsw f18, 72(%0)\n" + "fsw f19, 76(%0)\n" + "fsw f20, 80(%0)\n" + "fsw f21, 84(%0)\n" + "fsw f22, 88(%0)\n" + "fsw f23, 92(%0)\n" + "fsw f24, 96(%0)\n" + "fsw f25, 100(%0)\n" + "fsw f26, 104(%0)\n" + "fsw f27, 108(%0)\n" + "fsw f28, 112(%0)\n" + "fsw f29, 116(%0)\n" + "fsw f30, 120(%0)\n" + "fsw f31, 124(%0)\n" +#endif /* __riscv_d */ + : + : "r"(dst->f) + : "memory" + ); + + dst->fcsr = csr_read(CSR_FCSR); + + /* Restore original mstatus LAST */ + csr_write(CSR_MSTATUS, mstatus_orig); +} + +void sbi_fp_restore(const struct sbi_fp_context *src) +{ + unsigned long mstatus_orig; + + if (!src) + return; + + /* Save original mstatus */ + mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_FS); + + asm volatile( +#if defined(__riscv_d) + "fld f0, 0(%0)\n" + "fld f1, 8(%0)\n" + "fld f2, 16(%0)\n" + "fld f3, 24(%0)\n" + "fld f4, 32(%0)\n" + "fld f5, 40(%0)\n" + "fld f6, 48(%0)\n" + "fld f7, 56(%0)\n" + "fld f8, 64(%0)\n" + "fld f9, 72(%0)\n" + "fld f10, 80(%0)\n" + "fld f11, 88(%0)\n" + "fld f12, 96(%0)\n" + "fld f13, 104(%0)\n" + "fld f14, 112(%0)\n" + "fld f15, 120(%0)\n" + "fld f16, 128(%0)\n" + "fld f17, 136(%0)\n" + "fld f18, 144(%0)\n" + "fld f19, 152(%0)\n" + "fld f20, 160(%0)\n" + "fld f21, 168(%0)\n" + "fld f22, 176(%0)\n" + "fld f23, 184(%0)\n" + "fld f24, 192(%0)\n" + "fld f25, 200(%0)\n" + "fld f26, 208(%0)\n" + "fld f27, 216(%0)\n" + "fld f28, 224(%0)\n" + "fld f29, 232(%0)\n" + "fld f30, 240(%0)\n" + "fld f31, 248(%0)\n" +#else + "flw f0, 0(%0)\n" + "flw f1, 4(%0)\n" + "flw f2, 8(%0)\n" + "flw f3, 12(%0)\n" + "flw f4, 16(%0)\n" + "flw f5, 20(%0)\n" + "flw f6, 24(%0)\n" + "flw f7, 28(%0)\n" + "flw f8, 32(%0)\n" + "flw f9, 36(%0)\n" + "flw f10, 40(%0)\n" + "flw f11, 44(%0)\n" + "flw f12, 48(%0)\n" + "flw f13, 52(%0)\n" + "flw f14, 56(%0)\n" + "flw f15, 60(%0)\n" + "flw f16, 64(%0)\n" + "flw f17, 68(%0)\n" + "flw f18, 72(%0)\n" + "flw f19, 76(%0)\n" + "flw f20, 80(%0)\n" + "flw f21, 84(%0)\n" + "flw f22, 88(%0)\n" + "flw f23, 92(%0)\n" + "flw f24, 96(%0)\n" + "flw f25, 100(%0)\n" + "flw f26, 104(%0)\n" + "flw f27, 108(%0)\n" + "flw f28, 112(%0)\n" + "flw f29, 116(%0)\n" + "flw f30, 120(%0)\n" + "flw f31, 124(%0)\n" +#endif /* __riscv_d */ + : + : "r"(src->f) + : "memory" + ); + + csr_write(CSR_FCSR, src->fcsr); + + /* Restore original mstatus LAST */ + csr_write(CSR_MSTATUS, mstatus_orig); +} +#endif /* __riscv_f || __riscv_d */ -- 2.43.0 From anup at brainfault.org Mon May 18 01:31:19 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 18 May 2026 14:01:19 +0530 Subject: [PATCH v7 0/3] Add eager FP and RISC-V vector context switching support In-Reply-To: <20260518054219.58708-1-dave.patel@riscstar.com> References: <20260518054219.58708-1-dave.patel@riscstar.com> Message-ID: On Mon, May 18, 2026 at 11:12?AM wrote: > > From: Dave Patel > > This patch series adds proper support for per-domain floating-point (FP) and > vector (V) contexts in the domain context switch logic. Each domain > now maintains its own FP and vector state, which is saved and restored > during domain switches. > > Conditionalize FP and Vector save/restore based on extensions, unconditional > save and restore of floating-point (FP) and Vector registers fails on > generic platform firmware. This firmware must run on multiple platforms > that may lack these extensions. > > Address this by conditionally executing FP save/restore only if the underlying > hart supports the F or D extensions. Similarly, perform Vector save/restore > only if the hart supports the Vector extension. > > Changes include: > > - Add Vector save and restore functionality > - Add Floating Point save and restore functioanlity > - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. > - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' > to allocate and free per-domain FP and vector context. > - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. > - Updated `switch_to_next_domain_context()` to save/restore FP and vector > contexts safely: > - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. > - Added runtime checks for FP and vector extensions where needed. > - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum > sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() > to check for these capabilities before performing the context switches > > This improves support for multi-domain systems with FP and Vector > extensions, and prevents corruption of FP/Vector state during domain > switches. > > Dave Patel (3): > lib: sbi: Add RISC-V vector context save/restore support > lib: sbi: Add floating-point context save/restore support. > lib: sbi: domain FP/Vector context support for context switch > > include/sbi/sbi_fp.h | 26 +++++ > include/sbi/sbi_hart.h | 6 + > include/sbi/sbi_vector.h | 28 +++++ > lib/sbi/objects.mk | 2 + > lib/sbi/sbi_domain_context.c | 36 ++++++ > lib/sbi/sbi_fp.c | 207 +++++++++++++++++++++++++++++++++++ > lib/sbi/sbi_hart.c | 3 + > lib/sbi/sbi_vector.c | 118 ++++++++++++++++++++ > 8 files changed, 426 insertions(+) > create mode 100644 include/sbi/sbi_fp.h > create mode 100644 include/sbi/sbi_vector.h > create mode 100644 lib/sbi/sbi_fp.c > create mode 100644 lib/sbi/sbi_vector.c > > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi I have send-out v8 to address my own comments. Regards, Anup From anup at brainfault.org Mon May 18 01:33:59 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 18 May 2026 14:03:59 +0530 Subject: [PATCH v8 0/3] Add eager FP and Vector context switching support In-Reply-To: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> References: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> Message-ID: On Mon, May 18, 2026 at 2:00?PM Anup Patel wrote: > > From: Dave Patel > > This patch series adds proper support for per-domain floating-point (FP) and > vector (V) contexts in the domain context switch logic. Each domain > now maintains its own FP and vector state, which is saved and restored > during domain switches. > > Conditionalize FP and Vector save/restore based on extensions, unconditional > save and restore of floating-point (FP) and Vector registers fails on > generic platform firmware. This firmware must run on multiple platforms > that may lack these extensions. > > Address this by conditionally executing FP save/restore only if the underlying > hart supports the F or D extensions. Similarly, perform Vector save/restore > only if the hart supports the Vector extension. > > Changes since v7: > - Rebased on latest OpenSBI master > - Addressed all my comments on v7 series > > Changes include: > > - Add Vector save and restore functionality > - Add Floating Point save and restore functioanlity > - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. > - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' > to allocate and free per-domain FP and vector context. > - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. > - Updated `switch_to_next_domain_context()` to save/restore FP and vector > contexts safely: > - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. > - Added runtime checks for FP and vector extensions where needed. > - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum > sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() > to check for these capabilities before performing the context switches > > This improves support for multi-domain systems with FP and Vector > extensions, and prevents corruption of FP/Vector state during domain > switches. > > Dave Patel (3): > lib: sbi: Add RISC-V vector context save/restore support > lib: sbi: Add floating-point context save/restore support. > lib: sbi: domain FP/Vector context support for context switch > > include/sbi/sbi_fp.h | 35 +++++++ > include/sbi/sbi_hart.h | 6 ++ > include/sbi/sbi_vector.h | 40 ++++++++ > lib/sbi/objects.mk | 2 + > lib/sbi/sbi_domain_context.c | 33 ++++++ > lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++ > lib/sbi/sbi_hart.c | 3 + > lib/sbi/sbi_vector.c | 94 +++++++++++++++++ > 8 files changed, 405 insertions(+) > create mode 100644 include/sbi/sbi_fp.h > create mode 100644 include/sbi/sbi_vector.h > create mode 100644 lib/sbi/sbi_fp.c > create mode 100644 lib/sbi/sbi_vector.c > > -- > 2.43.0 > Applied this series to the riscv/opensbi repo. Regards, Anup From dave.patel at riscstar.com Mon May 18 01:35:43 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Mon, 18 May 2026 09:35:43 +0100 Subject: [PATCH v7 0/3] Add eager FP and RISC-V vector context switching support In-Reply-To: References: <20260518054219.58708-1-dave.patel@riscstar.com> Message-ID: On 5/18/26 09:31, Anup Patel wrote: > On Mon, May 18, 2026 at 11:12?AM wrote: >> >> From: Dave Patel >> >> This patch series adds proper support for per-domain floating-point (FP) and >> vector (V) contexts in the domain context switch logic. Each domain >> now maintains its own FP and vector state, which is saved and restored >> during domain switches. >> >> Conditionalize FP and Vector save/restore based on extensions, unconditional >> save and restore of floating-point (FP) and Vector registers fails on >> generic platform firmware. This firmware must run on multiple platforms >> that may lack these extensions. >> >> Address this by conditionally executing FP save/restore only if the underlying >> hart supports the F or D extensions. Similarly, perform Vector save/restore >> only if the hart supports the Vector extension. >> >> Changes include: >> >> - Add Vector save and restore functionality >> - Add Floating Point save and restore functioanlity >> - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. >> - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' >> to allocate and free per-domain FP and vector context. >> - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. >> - Updated `switch_to_next_domain_context()` to save/restore FP and vector >> contexts safely: >> - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. >> - Added runtime checks for FP and vector extensions where needed. >> - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum >> sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() >> to check for these capabilities before performing the context switches >> >> This improves support for multi-domain systems with FP and Vector >> extensions, and prevents corruption of FP/Vector state during domain >> switches. >> >> Dave Patel (3): >> lib: sbi: Add RISC-V vector context save/restore support >> lib: sbi: Add floating-point context save/restore support. >> lib: sbi: domain FP/Vector context support for context switch >> >> include/sbi/sbi_fp.h | 26 +++++ >> include/sbi/sbi_hart.h | 6 + >> include/sbi/sbi_vector.h | 28 +++++ >> lib/sbi/objects.mk | 2 + >> lib/sbi/sbi_domain_context.c | 36 ++++++ >> lib/sbi/sbi_fp.c | 207 +++++++++++++++++++++++++++++++++++ >> lib/sbi/sbi_hart.c | 3 + >> lib/sbi/sbi_vector.c | 118 ++++++++++++++++++++ >> 8 files changed, 426 insertions(+) >> create mode 100644 include/sbi/sbi_fp.h >> create mode 100644 include/sbi/sbi_vector.h >> create mode 100644 lib/sbi/sbi_fp.c >> create mode 100644 lib/sbi/sbi_vector.c >> >> -- >> 2.43.0 >> >> >> -- >> opensbi mailing list >> opensbi at lists.infradead.org >> http://lists.infradead.org/mailman/listinfo/opensbi > > I have send-out v8 to address my own comments. > > Regards, > Anup Thanks Anup, I need to check my environment as spaces should have been tabs now. Thanks for V8 very much appreciated. Thanks and Kind Regards Dave From dave.patel at riscstar.com Mon May 18 01:40:08 2026 From: dave.patel at riscstar.com (Dave Patel) Date: Mon, 18 May 2026 09:40:08 +0100 Subject: [PATCH v8 0/3] Add eager FP and Vector context switching support In-Reply-To: References: <20260518083023.997323-1-anup.patel@oss.qualcomm.com> Message-ID: <1f06c227-e8e1-4913-bc54-891a269e8938@riscstar.com> On 5/18/26 09:33, Anup Patel wrote: > On Mon, May 18, 2026 at 2:00?PM Anup Patel wrote: >> >> From: Dave Patel >> >> This patch series adds proper support for per-domain floating-point (FP) and >> vector (V) contexts in the domain context switch logic. Each domain >> now maintains its own FP and vector state, which is saved and restored >> during domain switches. >> >> Conditionalize FP and Vector save/restore based on extensions, unconditional >> save and restore of floating-point (FP) and Vector registers fails on >> generic platform firmware. This firmware must run on multiple platforms >> that may lack these extensions. >> >> Address this by conditionally executing FP save/restore only if the underlying >> hart supports the F or D extensions. Similarly, perform Vector save/restore >> only if the hart supports the Vector extension. >> >> Changes since v7: >> - Rebased on latest OpenSBI master >> - Addressed all my comments on v7 series >> >> Changes include: >> >> - Add Vector save and restore functionality >> - Add Floating Point save and restore functioanlity >> - Added `fp_ctx` and `vec_ctx` members to `struct hart_context`. >> - Introduced dynamic vector struct allocation for vlenb in 'struct hart_context' >> to allocate and free per-domain FP and vector context. >> - Modified `sbi_domain_register()` to initialize FP/Vector context per domain. >> - Updated `switch_to_next_domain_context()` to save/restore FP and vector >> contexts safely: >> - Ensures FS/VS fields in `mstatus` are enabled (set to Initial) only if Off. >> - Added runtime checks for FP and vector extensions where needed. >> - Added SBI_HART_EXT_F, SBI_HART_EXT_D, SBI_HART_EXT_V to enum >> sbi_hart_extensions and the sbi_hart_ext[] array. Use sbi_hart_has_extension() >> to check for these capabilities before performing the context switches >> >> This improves support for multi-domain systems with FP and Vector >> extensions, and prevents corruption of FP/Vector state during domain >> switches. >> >> Dave Patel (3): >> lib: sbi: Add RISC-V vector context save/restore support >> lib: sbi: Add floating-point context save/restore support. >> lib: sbi: domain FP/Vector context support for context switch >> >> include/sbi/sbi_fp.h | 35 +++++++ >> include/sbi/sbi_hart.h | 6 ++ >> include/sbi/sbi_vector.h | 40 ++++++++ >> lib/sbi/objects.mk | 2 + >> lib/sbi/sbi_domain_context.c | 33 ++++++ >> lib/sbi/sbi_fp.c | 192 +++++++++++++++++++++++++++++++++++ >> lib/sbi/sbi_hart.c | 3 + >> lib/sbi/sbi_vector.c | 94 +++++++++++++++++ >> 8 files changed, 405 insertions(+) >> create mode 100644 include/sbi/sbi_fp.h >> create mode 100644 include/sbi/sbi_vector.h >> create mode 100644 lib/sbi/sbi_fp.c >> create mode 100644 lib/sbi/sbi_vector.c >> >> -- >> 2.43.0 >> > > Applied this series to the riscv/opensbi repo. > > Regards, > Anup Many thanks Anup! Kind Regards, Dave From anup at brainfault.org Mon May 18 05:55:04 2026 From: anup at brainfault.org (Anup Patel) Date: Mon, 18 May 2026 18:25:04 +0530 Subject: [PATCH v3] lib: utils: fdt_domain: add root-regions-inheritance policy In-Reply-To: <20260516072906.1427203-1-peter.lin@sifive.com> References: <20260516072906.1427203-1-peter.lin@sifive.com> Message-ID: On Sat, May 16, 2026 at 12:59?PM Yu-Chien Peter Lin wrote: > > Introduce root-regions-inheritance DT property to control > copying of root domain memregions. Support 'all' and 'm-only' > modes, always inheriting firmware and M-only regions; behavior > matches m-only policy when property is absent. > > Signed-off-by: Yu-Chien Peter Lin LGTM. Reviewed-by: Anup Patel Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > Changes v1->v2: > - Addressed feedback from Anup: https://lists.infradead.org/pipermail/opensbi/2026-May/009850.html > Changes v2->v3: > - Fail early when unsupported value is found > --- > docs/domain_support.md | 7 +++++ > lib/utils/fdt/fdt_domain.c | 53 ++++++++++++++++++++++++++++---------- > 2 files changed, 47 insertions(+), 13 deletions(-) > > diff --git a/docs/domain_support.md b/docs/domain_support.md > index 93186c4a..77122330 100644 > --- a/docs/domain_support.md > +++ b/docs/domain_support.md > @@ -159,6 +159,13 @@ The DT properties of a domain instance DT node are as follows: > * **possible-harts** (Optional) - The list of CPU DT node phandles for the > the domain instance. This list represents the possible HARTs of the > domain instance. > +* **root-regions-inheritance** (Optional) - A string property controlling > + how memory regions are inherited from **the ROOT domain**, which are then > + overlaid with regions specified in the **regions** property for additional > + restrictions. The allowed values are: > + * "all" - inherit all memory regions from **the ROOT domain** > + * "m-only" - inherit M-mode only memory regions from **the ROOT domain** > + If this DT property is absent, behavior is the same as "m-only". > * **regions** (Optional) - The list of domain memory region DT node phandle > and access permissions for the domain instance. Each list entry is a pair > of DT node phandle and access permissions. The access permissions are > diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c > index b2fa8633..b31254af 100644 > --- a/lib/utils/fdt/fdt_domain.c > +++ b/lib/utils/fdt/fdt_domain.c > @@ -10,6 +10,7 @@ > > #include > #include > +#include > #include > #include > #include > @@ -237,7 +238,9 @@ skip_device_disable: > fdt_nop_node(fdt, poffset); > } > > -#define FDT_DOMAIN_REGION_MAX_COUNT 16 > +#define FDT_DOMAIN_REGION_MAX_COUNT 16 > +#define FDT_ROOT_REGION_INHERIT_M_ONLY 0 > +#define FDT_ROOT_REGION_INHERIT_ALL 1 > > struct parse_region_data { > struct sbi_domain *dom; > @@ -309,12 +312,14 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) > u32 val32; > u64 val64; > const u32 *val; > + const char *inherit; > struct sbi_domain *dom; > struct sbi_hartmask *mask; > struct sbi_hartmask assign_mask; > struct parse_region_data preg; > int *cold_domain_offset = opaque; > struct sbi_domain_memregion *reg; > + int inheritance_mode = FDT_ROOT_REGION_INHERIT_M_ONLY; > int i, err = 0, len, cpus_offset, cpu_offset, doffset; > > dom = sbi_zalloc(sizeof(*dom)); > @@ -373,20 +378,42 @@ static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) > if (err) > goto fail_free_all; > > - /* > - * Copy over root domain memregions which don't allow > - * read, write and execute from lower privilege modes. > - * > - * These root domain memregions without read, write, > - * and execute permissions include: > - * 1) firmware region protecting the firmware memory > - * 2) mmio regions protecting M-mode only mmio devices > - */ > + /* Determine root domain regions inheritance behavior. */ > + inherit = fdt_getprop(fdt, domain_offset, > + "root-regions-inheritance", &len); > + if (inherit && len > 0) { > + if (!strcmp(inherit, "all")) > + inheritance_mode = FDT_ROOT_REGION_INHERIT_ALL; > + else if (!strcmp(inherit, "m-only")) > + inheritance_mode = FDT_ROOT_REGION_INHERIT_M_ONLY; > + else { > + sbi_printf("%s: domain \"%s\" has unsupported " > + "root-regions-inheritance=\"%s\"\n", > + __func__, dom->name, inherit); > + err = SBI_EINVAL; > + goto fail_free_all; > + } > + } > + > + /* Copy over root domain memregions according to inheritance_mode. */ > sbi_domain_for_each_memregion(&root, reg) { > - if ((reg->flags & SBI_DOMAIN_MEMREGION_SU_READABLE) || > - (reg->flags & SBI_DOMAIN_MEMREGION_SU_WRITABLE) || > - (reg->flags & SBI_DOMAIN_MEMREGION_SU_EXECUTABLE)) > + bool copy = false; > + > + switch (inheritance_mode) { > + case FDT_ROOT_REGION_INHERIT_ALL: > + copy = true; > + break; > + case FDT_ROOT_REGION_INHERIT_M_ONLY: > + if (SBI_DOMAIN_MEMREGION_IS_FIRMWARE(reg->flags) || > + SBI_DOMAIN_MEMREGION_M_ONLY_ACCESS(reg->flags)) { > + copy = true; > + } > + break; > + } > + > + if (!copy) > continue; > + > if (preg.max_regions <= preg.region_count) { > err = SBI_EINVAL; > goto fail_free_all; > -- > 2.43.7 > From olof at lixom.net Mon May 18 17:33:17 2026 From: olof at lixom.net (Olof Johansson) Date: Mon, 18 May 2026 17:33:17 -0700 Subject: [PATCH v3 2/3] lib: sbi: ISA extension emulation. In-Reply-To: <20251227121802.15703-3-b.freisen@gmx.net> References: <20251227121802.15703-1-b.freisen@gmx.net> <20251227121802.15703-3-b.freisen@gmx.net> Message-ID: Hi, This is really useful, even if it's not very fast, for obvious reasons. Fairly late feedback on this, but I found two bugs and a nit when working to get Ubuntu 26.04 working on X280 (Tenstorrent Blackhole). It boots with these fixes applied. In particular, without the cbo.zero fix Python crashes on init. For those curious, booting is quite slow due to the massive amount of emulation needed. Implemented stats, boot to login prompt takes: L2CPU 0 ISA-emulation counters (version 5, hart 0): ID Extension count ---- ------------- --------------- 1 ANY 128201838 (sum of all below) 2 Zcb 103287492 5 Zbs 12818741 6 Zicond 12090563 9 Zfa 4517 17 Zvbb 69 18 VS-off-bounce 455 -Olof On Sat, Dec 27, 2025 at 01:18:01PM +0100, Benedikt Freisen wrote: > Add trap-based emulation code or compatibility stubs for the following ISA extensions: > Zicbom, Zicboz, Zicond, Zimop, Zawrs, Zfa, Zfhmin, Zcb, Zcmop, Zba, Zbb, Zbc, Zbs, Supm > > Signed-off-by: Benedikt Freisen > --- [...] > diff --git a/lib/sbi/sbi_insn_emu.c b/lib/sbi/sbi_insn_emu.c > new file mode 100644 > index 00000000..ffc3bade > --- /dev/null > +++ b/lib/sbi/sbi_insn_emu.c [...] > +#if defined(CONFIG_EMU_ZBA) || defined(CONFIG_EMU_ZBB) || \ > + defined(CONFIG_EMU_ZBC) || defined(CONFIG_EMU_ZBS) || \ > + defined(CONFIG_EMU_ZICOND) > +int sbi_insn_emu_op(ulong insn, struct sbi_trap_regs *regs) > +{ [...] > +#ifdef CONFIG_EMU_ZBC > + /* Emulate Zbc instructions */ > + case INSN_MATCH_CLMUL: > + rd_val = 0; > + for (int i = 0; i < __riscv_xlen; i++) { > + if ((rs2_val >> i) & 1) > + rd_val ^= rs1_val << i; > + } > + break; > + case INSN_MATCH_CLMULH: > + rd_val = 0; > + for (int i = 1; i <= __riscv_xlen; i++) { > + if ((rs2_val >> i) & 1) > + rd_val ^= rs1_val >> (__riscv_xlen - i); > + } > + break; The above needs to be < __riscv_xlen. Per the spec: for (int i = 1; i < XLEN; i++) if ((rs2 >> i) & 1) x ^= rs1 >> (XLEN - i); > + case INSN_MATCH_CLMULR: > + rd_val = 0; > + for (int i = 0; i < __riscv_xlen; i++) { > + if ((rs2_val >> i) & 1) > + rd_val ^= rs1_val >> (__riscv_xlen - i - 1); > + } > + break; > +#endif [...] > +int sbi_insn_emu_zicbom_zicboz(ulong insn, struct sbi_trap_regs *regs) > +{ > + /* NOTE: Errata workarounds for fence instructions are handled in > + * misc_mem_opcode_insn. */ > + > + /* Emulate Zicbom and Zicboz */ > + switch (insn & INSN_MASK_CBO) { > + case INSN_MATCH_CBO_ZERO: { > + /* Check whether the instruction was even allowed */ > + ulong prev_mode = sbi_mstatus_prev_mode(regs->mstatus); > + if ((prev_mode == PRV_U && > + !(read_senvcfg_or_emu() & ENVCFG_CBZE)) || > + (prev_mode == PRV_S && > + !(read_menvcfg_or_emu() & ENVCFG_CBZE))) > + return truly_illegal_insn(insn, regs); > + > + u32 *addr = > + (u32 *)(GET_RS1S(insn, regs) & 0xffffffffffffffc0ull); This needs to be GET_RS1(), or you end up picking up the wrong register in some cases (gpr[8]). [...] > +int sbi_insn_emu_store_fp(ulong insn, struct sbi_trap_regs *regs) > +{ > + struct sbi_trap_context *tcntx = > + container_of(regs, struct sbi_trap_context, regs); > + > + /* If floating point is available and insn is FSH, > + * simply use the misaligned store handler */ > + if ((regs->mstatus & MSTATUS_FS) != 0 && > + (sbi_mstatus_prev_mode(regs->mstatus) != PRV_U || > + (csr_read(CSR_SSTATUS) & SSTATUS_FS) != 0) && > + (insn & INSN_MASK_FSH) == INSN_MATCH_FSH) { > + tcntx->trap.cause = CAUSE_MISALIGNED_LOAD; Nit: This should be CAUSE_MISALIGNED_STORE > + tcntx->trap.tval = GET_RS1(insn, regs) + IMM_S(insn); > + return sbi_misaligned_store_handler(tcntx); > + } > + > + return truly_illegal_insn(insn, regs); > +} -Olof From oriol.catalan at openchip.com Tue May 19 01:58:45 2026 From: oriol.catalan at openchip.com (Oriol Catalan | OPENCHIP) Date: Tue, 19 May 2026 08:58:45 +0000 Subject: lib: sbi: Make per-HART stack size configurable via Kconfig Message-ID: lib: sbi: Make per-HART stack size configurable via Kconfig The per-HART stack size for exception/interrupt handling is currently hardcoded to 8192 bytes in SBI_PLATFORM_DEFAULT_HART_STACK_SIZE. This may not be sufficient for platforms with deeper call stacks (e.g., those enabling additional SBI extensions) or may be wasteful for minimal platforms. Introduce a HART_STACK_SIZE Kconfig option in lib/sbi/Kconfig with a valid range of 8192 to 1048576 bytes and a default of 8192 bytes to preserve existing behavior. The SBI_PLATFORM_DEFAULT_HART_STACK_SIZE macro now resolves to CONFIG_HART_STACK_SIZE, allowing all platforms to benefit from a single configuration knob without any source changes. Signed-off-by: Oriol Catalan diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index fe382b56..7ea9b961 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -160,7 +160,7 @@ struct sbi_platform_operations { }; /** Platform default per-HART stack size for exception/interrupt handling */ -#define SBI_PLATFORM_DEFAULT_HART_STACK_SIZE 8192 +#define SBI_PLATFORM_DEFAULT_HART_STACK_SIZE CONFIG_HART_STACK_SIZE /** Platform default heap size */ #define SBI_PLATFORM_DEFAULT_HEAP_SIZE(__num_hart) \ diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig index c6cc04bc..2f6fa3a4 100644 --- a/lib/sbi/Kconfig +++ b/lib/sbi/Kconfig @@ -2,6 +2,11 @@ menu "Generic SBI Support" +config HART_STACK_SIZE + int "Per-HART stack size (bytes)" + range 8192 1048576 + default 8192 + config CONSOLE_EARLY_BUFFER_SIZE int "Early console buffer size (bytes)" default 256 The content, data, and any attached documents to this email are addressed exclusively to the addressee and are confidential and/or may be subject to a non-disclosure agreement. Any use, forwarding, disclosure, and/or copying, in whole or in part, without authorization is prohibited. If you have received this email in error, we apologize and, please notify the sender or Openchip immediately, and delete it from your system. El contenido, los datos y cualquier documento adjunto a este correo electr?nico est?n dirigidos exclusivamente al destinatario y son confidenciales y/o pueden estar sujetas a un acuerdo de no revelaci?n. Est? prohibido cualquier uso, reenv?o, divulgaci?n o copia, total o parcial, sin autorizaci?n. Si has recibido este correo por error, te pedimos disculpas y agradecemos que lo notifiques de inmediato al remitente o a Openchip, y lo elimines de tu sistema. El contingut, les dades i qualsevol document adjunt a aquest correu electr?nic estan dirigits exclusivament al destinatari i s?n confidencials i/o poden estar subjectes a un acord de no revelaci?. Est? prohibit qualsevol ?s, reenviament, divulgaci? o c?pia, total o parcial, sense autoritzaci?. Si has rebut aquest correu per error, et demanem disculpes i agra?m que ho notifiquis d'immediat al remitent o a Openchip, i l'eliminis del teu sistema From raymondmaoca at gmail.com Tue May 19 13:33:24 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Tue, 19 May 2026 16:33:24 -0400 Subject: [PATCH 0/7] Add WorldGuard hwiso support Message-ID: <20260519203331.2773185-1-raymondmaoca@gmail.com> From: Raymond Mao This series adds WorldGuard implementation for OpenSBI on top of the generic hardware isolation (HWISO) hooks. The goal is to let OpenSBI: - program platform WorldGuard checker state at boot - parse per-domain WorldGuard metadata from the device tree - reprogram hart WorldGuard runtime state during domain transitions The current implementation targets the WorldGuard model of `sifive,wgchecker2` checker plus per-hart and per-domain WorldGuard metadata from DT. The series is organized as follows: 1. add the WorldGuard CSR definitions and hart extension flags needed to detect support for MLWID, MWIDDELEG, and SLWID 2. document the HWISO / WorldGuard device-tree metadata used by the current implementation 3. add a QEMU virt DT overlay that describes domain WID/WID list assignment and checker permission policy for the current test flow 4. add generic-platform WorldGuard runtime support together with `wgchecker2` checker parsing and boot-time MMIO programming 5. add generic HWISO SBIUNIT coverage 6. add QEMU virt WorldGuard mechanism-specific SBIUNIT checks for boot-time checker programming and runtime CSR state 7. add a QEMU virt failure-mode SBIUNIT test that intentionally triggers a denied WorldGuard access and verifies the expected trap Notes: - The implementation has been verified with QEMU virt that supports `wg=on`[1]. - This series depends on previous patches ([2] and [3]) for introducing HWISO framework. [1] https://github.com/cwshu/qemu/tree/riscv-wg-dts [2] [PATCH 1/2] sbi: add hardware isolation abstraction framework https://lore.kernel.org/opensbi/20260504173948.1663823-1-raymondmaoca at gmail.com/ [3] [PATCH 2/2] sbi: route domain lifecycle transitions through hwiso hooks https://lore.kernel.org/opensbi/20260504173948.1663823-2-raymondmaoca at gmail.com/ Raymond Mao (7): hart: add WorldGuard CSR IDs and hart extension flags docs: document hwiso WorldGuard DT bindings [NOT-FOR-UPSTREAM] platform: virt: add QEMU WorldGuard hwiso overlay platform: generic: add WorldGuard hwiso support with wgchecker2 test: add generic hwiso SBI unit coverage platform: virt: add QEMU virt WorldGuard hwiso tests platform: virt: add WorldGuard HWISO failure-mode SBIUNIT test docs/domain_support.md | 159 +++++ include/sbi/riscv_encoding.h | 3 + include/sbi/sbi_hart.h | 4 + include/sbi/sbi_hwiso_test.h | 35 ++ lib/sbi/objects.mk | 3 + lib/sbi/sbi_hart.c | 2 + lib/sbi/sbi_hwiso_test.c | 173 ++++++ lib/sbi/sbi_hwiso_testlib.c | 119 ++++ platform/generic/include/wgchecker2.h | 55 ++ platform/generic/include/worldguard.h | 45 ++ platform/generic/objects.mk | 4 + platform/generic/platform.c | 11 + .../generic/virt/qemu-virt-hwiso-overlay.dts | 121 ++++ .../generic/virt/qemu_virt_wgchecker_test.c | 356 +++++++++++ platform/generic/virt/qemu_virt_worldguard.c | 42 ++ platform/generic/wgchecker2.c | 585 ++++++++++++++++++ platform/generic/worldguard.c | 522 ++++++++++++++++ 17 files changed, 2239 insertions(+) create mode 100644 include/sbi/sbi_hwiso_test.h create mode 100644 lib/sbi/sbi_hwiso_test.c create mode 100644 lib/sbi/sbi_hwiso_testlib.c create mode 100644 platform/generic/include/wgchecker2.h create mode 100644 platform/generic/include/worldguard.h create mode 100644 platform/generic/virt/qemu-virt-hwiso-overlay.dts create mode 100644 platform/generic/virt/qemu_virt_wgchecker_test.c create mode 100644 platform/generic/virt/qemu_virt_worldguard.c create mode 100644 platform/generic/wgchecker2.c create mode 100644 platform/generic/worldguard.c -- 2.25.1 From raymondmaoca at gmail.com Tue May 19 13:33:25 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Tue, 19 May 2026 16:33:25 -0400 Subject: [PATCH 1/7] hart: add WorldGuard CSR IDs and hart extension flags In-Reply-To: <20260519203331.2773185-1-raymondmaoca@gmail.com> References: <20260519203331.2773185-1-raymondmaoca@gmail.com> Message-ID: <20260519203331.2773185-2-raymondmaoca@gmail.com> From: Raymond Mao Define the WorldGuard-related CSR numbers and claim smwg / sswg hart extensions so platform code can probe support before programming MLWID, MWIDDELEG and SLWID. Signed-off-by: Raymond Mao --- include/sbi/riscv_encoding.h | 3 +++ include/sbi/sbi_hart.h | 4 ++++ lib/sbi/sbi_hart.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h index 46bbeed0..48304132 100644 --- a/include/sbi/riscv_encoding.h +++ b/include/sbi/riscv_encoding.h @@ -331,6 +331,7 @@ /* Supervisor Protection and Translation */ #define CSR_SATP 0x180 +#define CSR_SLWID 0x190 /* Supervisor Indirect Register Alias */ #define CSR_SISELECT 0x150 @@ -454,6 +455,7 @@ /* Machine Configuration */ #define CSR_MENVCFG 0x30a #define CSR_MENVCFGH 0x31a +#define CSR_MLWID 0x390 /* Machine Trap Handling */ #define CSR_MSCRATCH 0x340 @@ -680,6 +682,7 @@ /* Machine Security Configuration CSR (mseccfg) */ #define CSR_MSECCFG 0x747 #define CSR_MSECCFGH 0x757 +#define CSR_MWIDDELEG 0x748 #define MSECCFG_MML_SHIFT (0) #define MSECCFG_MML (_UL(1) << MSECCFG_MML_SHIFT) diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h index cc78eec6..2c725ae2 100644 --- a/include/sbi/sbi_hart.h +++ b/include/sbi/sbi_hart.h @@ -63,6 +63,10 @@ enum sbi_hart_extensions { SBI_HART_EXT_SSCSRIND, /** Hart has Ssccfg extension */ SBI_HART_EXT_SSCCFG, + /** Hart has Smwg extension */ + SBI_HART_EXT_SMWG, + /** Hart has Sswg extension */ + SBI_HART_EXT_SSWG, /** Maximum index of Hart extension */ SBI_HART_EXT_MAX, diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 3d136944..6fc03a59 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -666,6 +666,8 @@ const struct sbi_hart_ext_data sbi_hart_ext[] = { __SBI_HART_EXT_DATA(sdtrig, SBI_HART_EXT_SDTRIG), __SBI_HART_EXT_DATA(smcsrind, SBI_HART_EXT_SMCSRIND), __SBI_HART_EXT_DATA(smcdeleg, SBI_HART_EXT_SMCDELEG), + __SBI_HART_EXT_DATA(smwg, SBI_HART_EXT_SMWG), + __SBI_HART_EXT_DATA(sswg, SBI_HART_EXT_SSWG), }; /** -- 2.25.1 From raymondmaoca at gmail.com Tue May 19 13:33:26 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Tue, 19 May 2026 16:33:26 -0400 Subject: [PATCH 2/7] docs: document hwiso WorldGuard DT bindings In-Reply-To: <20260519203331.2773185-1-raymondmaoca@gmail.com> References: <20260519203331.2773185-1-raymondmaoca@gmail.com> Message-ID: <20260519203331.2773185-3-raymondmaoca@gmail.com> From: Raymond Mao Document the hw-isolation and worldguard_cfg device-tree metadata used by the HWISO framework for WorldGuard-enabled platforms. Signed-off-by: Raymond Mao --- docs/domain_support.md | 159 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/docs/domain_support.md b/docs/domain_support.md index b34e43aa..d81f1bc6 100644 --- a/docs/domain_support.md +++ b/docs/domain_support.md @@ -201,6 +201,165 @@ The DT properties of a domain instance DT node are as follows: whether the domain instance is allowed to do system reset. * **system-suspend-allowed** (Optional) - A boolean flag representing whether the domain instance is allowed to do system suspend. +* **hw-isolation** (Optional) - A child node acting as a container for + system-level hardware isolation mechanisms. Each child node represents a + single mechanism configured via its compatible string and properties. + +Hardware Isolation Hooks +------------------------ + +OpenSBI provides a system-level hardware isolation framework that dispatches +all registered mechanisms in the following phases: + +* **init** - Runs at boot to configure system-level isolation features. +* **domain_init** - Parses per-domain isolation configuration. +* **domain_exit** - Runs before switching out of a domain. +* **domain_enter** - Runs after switching into a domain. + +Hardware Isolation Device Tree Binding +-------------------------------------- + +The hardware isolation configuration is specified as an optional child node +named **hw-isolation** under a domain instance node. The **hw-isolation** +node is a container for one or more mechanism nodes. + +The DT properties of a hardware isolation container node are as follows: + +* **#address-cells** / **#size-cells** (Optional) - Standard container node + properties. They are not interpreted by OpenSBI. + +Each hardware isolation mechanism has its own properties and compatible +string. A mechanism can either use per-domain properties below the domain +instance node, or parse system-level DT nodes describing isolation hardware. + +For the WorldGuard support on QEMU virt, OpenSBI parses the +following WG-style system nodes: + +* **sifive,wgchecker2** - WorldGuard checker node. +* **reg** - Checker MMIO base/size. +* **sifive,slot-count** - Number of hardware checker slots. +* **sifive,subordinates** - List of protected resource phandles owned by the + checker. +* **worldguard_cfg** - Child node of a protected memory or device node + describing WorldGuard policy for that resource. +* **perms** - 64-bit permission bitmap values encoded as **** cell + pairs, with either one value for the whole resource or one value per + protected range. +* **reg** - Optional protected address ranges inside a **worldguard_cfg** + child. If omitted, the resource node's own **reg** is used. A single + subordinate with one **perms** entry and no explicit **worldguard_cfg/reg** + is treated as a full-checker rule. +* **worldguard** - Optional CPU child node compatible with **riscv,wgcpu** + providing default WG execution state. +* **mwid** - Default machine world ID for a hart. +* **mwidlist** - Valid/delegable world IDs for that hart. + +Domain nodes can optionally provide WG execution metadata under the +**hw-isolation** container: + +* **worldguard,wid** - Machine world ID selected when entering the domain. +* **worldguard,widlist** - World IDs delegated to the domain. + +At runtime the WorldGuard implementation uses the hooks as follows: + +* **init** - Parses all WG checker nodes, validates the protected ranges, and + programs checker MMIO slots at boot when platform checker nodes are + present. Runtime WID/WID list support is enabled only when per-CPU WG + runtime nodes are present; checker-only DTs do not force runtime + switching on. +* **domain_init** - Parses per-domain **worldguard,wid** and + **worldguard,widlist** metadata. +* **domain_exit** - Quiesces the current hart back to its per-hart default + machine WID and clears **MWIDDELEG** before the handoff. +* **domain_enter** - Reprograms **MLWID**, **MWIDDELEG**, and, when + delegation is active, **SLWID** for the destination domain when the hart + supports **smwg** / **sswg**. + +The CPU **worldguard** defaults are parsed per hart from **/cpus/**, so +platforms may provide different default **mwid** / **mwidlist** values on +different harts. + +Hardware Isolation Examples +--------------------------- + +Domain instance with WG execution metadata: + +```text + chosen { + opensbi-domains { + compatible = "opensbi,domain,config"; + + example_domain: domain at 1 { + compatible = "opensbi,domain,instance"; + possible-harts = <&cpu2>; + regions = <&mem0 0x3f>; + boot-hart = <&cpu2>; + next-addr = <0x00000000 0x80200000>; + next-mode = <0x1>; + + hw-isolation { + worldguard { + compatible = "sifive,wgchecker2"; + worldguard,wid = <1>; + worldguard,widlist = <1 3>; + }; + }; + }; + }; + }; +``` + +WG checker, CPU default state, and protected resource example. These nodes +remain in the normal system DT topology because they describe isolation +hardware and protected resources, not OpenSBI domain instances: + +```text + cpu0: cpu at 0 { + worldguard { + compatible = "riscv,wgcpu"; + mwid = <0>; + mwidlist = <0 1 3>; + }; + }; + + flash0: flash at 20000000 { + reg = <0x0 0x20000000 0x0 0x2000000>; + worldguard_cfg { + perms = <0x0 0xc3>; + }; + }; + + uart0: serial at 10000000 { + reg = <0x0 0x10000000 0x0 0x100>; + worldguard_cfg { + perms = <0x0 0xc0>; + }; + }; + + memory0: memory at 80000000 { + reg = <0x0 0x80000000 0x0 0x80000000>; + worldguard_cfg { + reg = <0x0 0x80000000 0x0 0x40000000 + 0x0 0xc0000000 0x0 0x01000000 + 0x0 0xc1000000 0x0 0x3f000000>; + perms = <0x0 0xcf 0x0 0xcc 0x0 0xcf>; + }; + }; + + wgchecker0: wgchecker at 10100000 { + compatible = "sifive,wgchecker2"; + reg = <0x0 0x10100000 0x0 0x1000>; + sifive,slot-count = <8>; + sifive,subordinates = <&memory0 &flash0 &uart0>; + }; +``` + +The test overlay used in this tree is at: + +* **platform/generic/virt/qemu-virt-hwiso-overlay.dts** + +That overlay only adds per-domain and per-resource metadata. The base DTB +must still provide the WG checker nodes and per-CPU **worldguard** nodes. ### Assigning HART To Domain Instance -- 2.25.1 From raymondmaoca at gmail.com Tue May 19 13:33:27 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Tue, 19 May 2026 16:33:27 -0400 Subject: [PATCH 3/7] [NOT-FOR-UPSTREAM] platform: virt: add QEMU WorldGuard hwiso overlay In-Reply-To: <20260519203331.2773185-1-raymondmaoca@gmail.com> References: <20260519203331.2773185-1-raymondmaoca@gmail.com> Message-ID: <20260519203331.2773185-4-raymondmaoca@gmail.com> From: Raymond Mao Add a QEMU virt device-tree overlay that describes OpenSBI domain WorldGuard metadata and checker permission policy for the current HWISO test and demo flow. Signed-off-by: Raymond Mao --- .../generic/virt/qemu-virt-hwiso-overlay.dts | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 platform/generic/virt/qemu-virt-hwiso-overlay.dts diff --git a/platform/generic/virt/qemu-virt-hwiso-overlay.dts b/platform/generic/virt/qemu-virt-hwiso-overlay.dts new file mode 100644 index 00000000..e44d85b7 --- /dev/null +++ b/platform/generic/virt/qemu-virt-hwiso-overlay.dts @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: BSD-2-Clause +/dts-v1/; +/plugin/; + +/* + * Test-only overlay for exercising HWISO with WorldGuard metadata. + * + * This overlay only adds OpenSBI domain metadata and worldguard_cfg resource + * policy. The base DTB is expected to already provide the WG checker nodes + * and per-CPU worldguard child nodes. + * + * Usage: + * Domain hart phandles are filled in after merge because fdtoverlay does not + * reliably resolve CPU-node references against QEMU dumpdtb output here. + * See below steps for filling the domain hart phandles (assume the dumped dtb + * and merged dtb are represented by 'qemu.dtb' and 'qemu-merged.dtb' + * respectively): + * cpu0_phandle=$(fdtget -t x qemu.dtb /cpus/cpu at 0 phandle) + * cpu1_phandle=$(fdtget -t x qemu.dtb /cpus/cpu at 1 phandle) + * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain at 0 \ + * possible-harts "$cpu0_phandle" "$cpu1_phandle" + * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain at 0 \ + * boot-hart "$cpu0_phandle" + * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain at 1 \ + * possible-harts "$cpu1_phandle" + * fdtput -t x qemu-merged.dtb /chosen/opensbi-domains/domain at 1 \ + * boot-hart "$cpu1_phandle" + */ +/ { + fragment at 0 { + target-path = "/chosen"; + __overlay__ { + opensbi-domains { + compatible = "opensbi,domain,config"; + #address-cells = <1>; + #size-cells = <0>; + + memregion0: memregion at 0 { + compatible = "opensbi,domain,memregion"; + base = <0x00000000 0x80000000>; + order = <0x1f>; + }; + + guest0: domain at 0 { + compatible = "opensbi,domain,instance"; + regions = <&memregion0 0x3f>; + next-addr = <0x00000000 0x80200000>; + next-arg1 = <0x00000000 0x82200000>; + next-mode = <0x1>; + + hw-isolation { + worldguard { + compatible = "sifive,wgchecker2"; + worldguard,wid = <0>; + worldguard,widlist = <0 1 3>; + }; + }; + }; + + guest1: domain at 1 { + compatible = "opensbi,domain,instance"; + regions = <&memregion0 0x3f>; + next-addr = <0x00000000 0x80200000>; + next-mode = <0x1>; + + hw-isolation { + worldguard { + compatible = "sifive,wgchecker2"; + worldguard,wid = <1>; + worldguard,widlist = <1 3>; + }; + }; + }; + }; + }; + }; + + fragment at 1 { + target-path = "/cpus/cpu at 0"; + __overlay__ { + opensbi-domain = <&guest0>; + }; + }; + + fragment at 2 { + target-path = "/cpus/cpu at 1"; + __overlay__ { + opensbi-domain = <&guest0>; + }; + }; + + fragment at 3 { + target-path = "/memory at 80000000"; + __overlay__ { + worldguard_cfg { + reg = <0x00000000 0x80000000 0x00000000 0x40000000 + 0x00000000 0xc0000000 0x00000000 0x01000000 + 0x00000000 0xc1000000 0x00000000 0x3f000000>; + perms = <0x0 0xcf 0x0 0xcc 0x0 0xcf>; + }; + }; + }; + + fragment at 4 { + target-path = "/flash at 20000000"; + __overlay__ { + worldguard_cfg { + perms = <0x0 0xc3>; + }; + }; + }; + + fragment at 5 { + target-path = "/soc/serial at 10000000"; + __overlay__ { + worldguard_cfg { + perms = <0x0 0xc0>; + }; + }; + }; +}; -- 2.25.1 From raymondmaoca at gmail.com Tue May 19 13:33:28 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Tue, 19 May 2026 16:33:28 -0400 Subject: [PATCH 4/7] platform: generic: add WorldGuard hwiso support with wgchecker2 In-Reply-To: <20260519203331.2773185-1-raymondmaoca@gmail.com> References: <20260519203331.2773185-1-raymondmaoca@gmail.com> Message-ID: <20260519203331.2773185-5-raymondmaoca@gmail.com> From: Raymond Mao Add WorldGuard support for the hardware isolation framework on the generic platform. Implement boot-time parsing of sifive,wgchecker2 checker instances and their subordinate resource permissions from the FDT, then program the checker MMIO state according to the worldguard_cfg rules described for each protected resource. Add WorldGuard runtime support to parse per-hart CPU defaults and per-domain WorldGuard metadata, and use that state to reprogram MLWID, MWIDDELEG, and SLWID during domain transitions. Keep the QEMU virt platform code as the integration layer that registers the WorldGuard hardware isolation mechanism for the virt platform. Signed-off-by: Raymond Mao --- platform/generic/include/wgchecker2.h | 55 ++ platform/generic/include/worldguard.h | 45 ++ platform/generic/objects.mk | 3 + platform/generic/platform.c | 11 + platform/generic/virt/qemu_virt_worldguard.c | 42 ++ platform/generic/wgchecker2.c | 585 +++++++++++++++++++ platform/generic/worldguard.c | 522 +++++++++++++++++ 7 files changed, 1263 insertions(+) create mode 100644 platform/generic/include/wgchecker2.h create mode 100644 platform/generic/include/worldguard.h create mode 100644 platform/generic/virt/qemu_virt_worldguard.c create mode 100644 platform/generic/wgchecker2.c create mode 100644 platform/generic/worldguard.c diff --git a/platform/generic/include/wgchecker2.h b/platform/generic/include/wgchecker2.h new file mode 100644 index 00000000..37b4bfe0 --- /dev/null +++ b/platform/generic/include/wgchecker2.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#ifndef __PLATFORM_GENERIC_WGCHECKER2_H__ +#define __PLATFORM_GENERIC_WGCHECKER2_H__ + +#include + +#define WGCHECKER2_COMPAT "sifive,wgchecker2" +#define WGCHECKER2_CFG_NODE "worldguard_cfg" + +#define WGCHECKER2_PROP_SLOT_COUNT "sifive,slot-count" +#define WGCHECKER2_PROP_SUBORDINATES "sifive,subordinates" +#define WGCHECKER2_PROP_PERMS "perms" + +/* + * The current wgchecker2 model uses a 64-bit permission register with + * 2 bits per world, so the current checker model tracks at most 32 WIDs. + */ +#define WGCHECKER2_MAX_WIDS 32 + +/* The current wgchecker2 model requires 4 KiB slot alignment. */ +#define WGCHECKER2_MIN_ALIGN 0x1000ULL + +/* Current wgchecker2 MMIO register layout. */ +#define WGCHECKER2_MMIO_NSLOTS 0x008 +#define WGCHECKER2_MMIO_ERRCAUSE 0x010 +#define WGCHECKER2_MMIO_ERRADDR 0x018 +#define WGCHECKER2_MMIO_SLOT_BASE 0x020 +#define WGCHECKER2_MMIO_SLOT_STRIDE 0x020 +#define WGCHECKER2_MMIO_SLOT_ADDR 0x000 +#define WGCHECKER2_MMIO_SLOT_PERM 0x008 +#define WGCHECKER2_MMIO_SLOT_CFG 0x010 + +/* Current wgchecker2 slot cfg.A[1:0] encoding. */ +#define WGCHECKER2_SLOT_CFG_A_MASK 0x3 +#define WGCHECKER2_SLOT_CFG_A_OFF 0x0 +#define WGCHECKER2_SLOT_CFG_A_TOR 0x1 + +struct wgchecker2_range { + u64 base; + u64 size; + u64 perm; +}; + +u32 wgchecker2_count_platform_checkers(void *fdt); +int wgchecker2_init(void *fdt); +void wgchecker2_cleanup(void); +u32 wgchecker2_checker_count(void); + +#endif diff --git a/platform/generic/include/worldguard.h b/platform/generic/include/worldguard.h new file mode 100644 index 00000000..ed1e70b8 --- /dev/null +++ b/platform/generic/include/worldguard.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#ifndef __PLATFORM_GENERIC_WORLDGUARD_H__ +#define __PLATFORM_GENERIC_WORLDGUARD_H__ + +#include +#include +#include + +#define WORLDGUARD_CPU_COMPAT "riscv,wgcpu" +#define WORLDGUARD_CPU_NODE "worldguard" + +#define WORLDGUARD_PROP_WID "worldguard,wid" +#define WORLDGUARD_PROP_WIDLIST "worldguard,widlist" +#define WORLDGUARD_PROP_MWID "mwid" +#define WORLDGUARD_PROP_MWIDLIST "mwidlist" + +struct wg_cpu_defaults { + u32 trusted_wid; + u32 nworlds; + u32 valid_wid_mask; +}; + +struct worldguard_domain_ctx { + bool has_wid; + u32 wid; + u32 widlist_mask; +}; + +int worldguard_register(void); +const struct sbi_hwiso_ops *worldguard_ops_get(void); + +#ifdef CONFIG_SBIUNIT +int worldguard_test_check_runtime_state(bool runtime_enabled); +int worldguard_test_check_domain_state(const struct sbi_domain *dom, + bool expect_ctx, u32 wid, + u32 widlist_mask); +#endif + +#endif diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk index 85aa723a..80bd65ea 100644 --- a/platform/generic/objects.mk +++ b/platform/generic/objects.mk @@ -20,6 +20,9 @@ platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \ # Objects to build platform-objs-y += platform.o platform-objs-y += platform_override_modules.o +platform-objs-y += worldguard.o +platform-objs-y += wgchecker2.o +platform-objs-y += virt/qemu_virt_worldguard.o # Blobs to build FW_TEXT_START=0x80000000 diff --git a/platform/generic/platform.c b/platform/generic/platform.c index b76c2a2f..7326b709 100644 --- a/platform/generic/platform.c +++ b/platform/generic/platform.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,8 @@ #include #include +extern int qemu_virt_worldguard_register(void *fdt); + /* List of platform override modules generated at compile time */ extern const struct platform_override *platform_override_modules[]; extern unsigned long platform_override_modules_size; @@ -222,9 +225,17 @@ static int generic_nascent_init(void) static int generic_early_init(bool cold_boot) { + int rc; + if (cold_boot) fdt_reset_init(); + if (cold_boot) { + rc = qemu_virt_worldguard_register(fdt_get_address()); + if (rc && rc != SBI_EALREADY) + return rc; + } + if (!generic_plat || !generic_plat->early_init) return 0; diff --git a/platform/generic/virt/qemu_virt_worldguard.c b/platform/generic/virt/qemu_virt_worldguard.c new file mode 100644 index 00000000..b5cdab81 --- /dev/null +++ b/platform/generic/virt/qemu_virt_worldguard.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * QEMU virt WorldGuard registration shim + * + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#include +#include +#include +#include + +#ifdef CONFIG_SBIUNIT +extern const struct sbi_hwiso_test_ops qemu_virt_worldguard_test_ops; +#endif + +int qemu_virt_worldguard_register(void *fdt) +{ + int rc; + + if (!fdt) + return 0; + + if (fdt_node_check_compatible(fdt, 0, "riscv-virtio") && + fdt_node_check_compatible(fdt, 0, "qemu,virt")) + return 0; + + rc = worldguard_register(); + if (rc) + return rc; + +#ifdef CONFIG_SBIUNIT + rc = sbi_hwiso_test_register(worldguard_ops_get(), + &qemu_virt_worldguard_test_ops); + if (rc && rc != SBI_EALREADY) + return rc; +#endif + + return 0; +} diff --git a/platform/generic/wgchecker2.c b/platform/generic/wgchecker2.c new file mode 100644 index 00000000..bd3fab15 --- /dev/null +++ b/platform/generic/wgchecker2.c @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * wgchecker2 model support + * + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#include +#include +#include +#include +#include +#include +#include + +struct wgchecker2_checker { + char name[32]; + u64 mmio_base; + u64 mmio_size; + u32 slot_count; + u32 subordinate_count; + bool full_checker_rule; + u64 full_checker_perm; + u32 range_count; + struct wgchecker2_range *ranges; +}; + +struct wgchecker2_platform_ctx { + u32 checker_count; + struct wgchecker2_checker *checkers; +}; + +static struct wgchecker2_platform_ctx *wgchecker2_platform; + +static void wgchecker2_free_platform_ctx(struct wgchecker2_platform_ctx *platform) +{ + u32 i; + + if (!platform) + return; + + for (i = 0; i < platform->checker_count; i++) + sbi_free(platform->checkers[i].ranges); + + sbi_free(platform->checkers); + sbi_free(platform); +} + +static u64 wgchecker2_read_cells(const fdt32_t *cells, int count) +{ + u64 val = 0; + int i; + + for (i = 0; i < count; i++) + val = (val << 32) | fdt32_to_cpu(cells[i]); + + return val; +} + +static void wgchecker2_write64(u64 addr, u64 val) +{ +#if __riscv_xlen != 32 + writeq(val, (void *)(unsigned long)addr); +#else + writel((u32)val, (void *)(unsigned long)addr); + writel((u32)(val >> 32), (void *)(unsigned long)(addr + 4)); +#endif +} + +static void wgchecker2_write32(u64 addr, u32 val) +{ + writel(val, (void *)(unsigned long)addr); +} + +static u64 wgchecker2_slot_addr_encode(u64 addr) +{ + return addr >> 2; +} + +static bool wgchecker2_range_is_aligned(u64 base, u64 size) +{ + if (!size) + return false; + + if (base & (WGCHECKER2_MIN_ALIGN - 1)) + return false; + if (size & (WGCHECKER2_MIN_ALIGN - 1)) + return false; + + return true; +} + +static void wgchecker2_sort_ranges(struct wgchecker2_checker *checker) +{ + struct wgchecker2_range tmp; + u32 i, j; + + for (i = 1; i < checker->range_count; i++) { + tmp = checker->ranges[i]; + j = i; + while (j > 0 && checker->ranges[j - 1].base > tmp.base) { + checker->ranges[j] = checker->ranges[j - 1]; + j--; + } + checker->ranges[j] = tmp; + } +} + +static int wgchecker2_compact_ranges(struct wgchecker2_checker *checker) +{ + struct wgchecker2_range *prev, *cur; + u64 prev_end, cur_end; + u32 i, out = 0; + + if (!checker->range_count) + return 0; + + wgchecker2_sort_ranges(checker); + + for (i = 0; i < checker->range_count; i++) { + cur = &checker->ranges[i]; + cur_end = cur->base + cur->size; + if (cur_end <= cur->base) + return SBI_EINVAL; + + if (!out) { + checker->ranges[out++] = *cur; + continue; + } + + prev = &checker->ranges[out - 1]; + prev_end = prev->base + prev->size; + if (cur->base < prev_end) + return SBI_EINVAL; + + if (cur->base == prev_end && cur->perm == prev->perm) { + prev->size += cur->size; + continue; + } + + checker->ranges[out++] = *cur; + } + + checker->range_count = out; + return 0; +} + +static int wgchecker2_get_reg_cells(void *fdt, int resource_node, + int *addr_cells, int *size_cells) +{ + int parent; + + parent = fdt_parent_offset(fdt, resource_node); + if (parent < 0) + return SBI_EINVAL; + + *addr_cells = fdt_address_cells(fdt, parent); + *size_cells = fdt_size_cells(fdt, parent); + if (*addr_cells <= 0 || *addr_cells > 2 || *size_cells <= 0 || + *size_cells > 2) + return SBI_EINVAL; + + return 0; +} + +static int wgchecker2_count_reg_entries(void *fdt, int resource_node, + int reg_node) +{ + const fdt32_t *reg; + int addr_cells, size_cells, entry_cells, len, rc; + + rc = wgchecker2_get_reg_cells(fdt, resource_node, &addr_cells, + &size_cells); + if (rc) + return rc; + + reg = fdt_getprop(fdt, reg_node, "reg", &len); + if (!reg || len <= 0) + return 0; + + entry_cells = addr_cells + size_cells; + if (len % (entry_cells * (int)sizeof(fdt32_t))) + return SBI_EINVAL; + + return len / (entry_cells * (int)sizeof(fdt32_t)); +} + +static int wgchecker2_parse_perms(void *fdt, int cfg_node, u64 **out_perms, + u32 *out_count) +{ + const fdt32_t *perms; + u64 *vals; + int len, i, count; + + *out_perms = NULL; + *out_count = 0; + + perms = fdt_getprop(fdt, cfg_node, WGCHECKER2_PROP_PERMS, &len); + if (!perms || len <= 0) + return 0; + + if (len % (2 * (int)sizeof(fdt32_t))) + return SBI_EINVAL; + + count = len / (2 * (int)sizeof(fdt32_t)); + vals = sbi_calloc(sizeof(*vals), count); + if (!vals) + return SBI_ENOMEM; + + for (i = 0; i < count; i++, perms += 2) + vals[i] = wgchecker2_read_cells(perms, 2); + + *out_perms = vals; + *out_count = count; + return 0; +} + +static int wgchecker2_fill_ranges(void *fdt, int resource_node, int reg_node, + const u64 *perms, u32 perm_count, + struct wgchecker2_range *ranges, + u32 range_count) +{ + const fdt32_t *reg; + u64 base, size; + int addr_cells, size_cells, entry_cells, len, i, rc; + + rc = wgchecker2_get_reg_cells(fdt, resource_node, &addr_cells, + &size_cells); + if (rc) + return rc; + + reg = fdt_getprop(fdt, reg_node, "reg", &len); + if (!reg || len <= 0) + return SBI_EINVAL; + + entry_cells = addr_cells + size_cells; + for (i = 0; i < (int)range_count; i++, reg += entry_cells) { + base = wgchecker2_read_cells(reg, addr_cells); + size = wgchecker2_read_cells(reg + addr_cells, size_cells); + if (!wgchecker2_range_is_aligned(base, size)) + return SBI_EINVAL; + + ranges[i].base = base; + ranges[i].size = size; + ranges[i].perm = perms[(perm_count == 1) ? 0 : i]; + } + + return 0; +} + +static int wgchecker2_parse_checker_rules(void *fdt, int checker_node, + struct wgchecker2_checker *checker) +{ + const fdt32_t *subs; + u64 *perms = NULL; + int cfg_node, len, i, rc = 0, reg_count; + u32 perm_count = 0; + int child; + + subs = fdt_getprop(fdt, checker_node, WGCHECKER2_PROP_SUBORDINATES, &len); + if (!subs || len <= 0) + return 0; + if (len % (int)sizeof(fdt32_t)) + goto err; + + checker->subordinate_count = len / sizeof(fdt32_t); + if (!checker->slot_count) + goto err; + + checker->ranges = sbi_calloc(sizeof(*checker->ranges), + checker->slot_count); + if (!checker->ranges) + return SBI_ENOMEM; + + for (i = 0; i < checker->subordinate_count; i++) { + child = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(subs[i])); + if (child < 0) { + rc = child; + goto err; + } + + cfg_node = fdt_subnode_offset(fdt, child, WGCHECKER2_CFG_NODE); + if (cfg_node < 0) + continue; + + rc = wgchecker2_parse_perms(fdt, cfg_node, &perms, &perm_count); + if (rc) + goto err; + if (!perm_count) + continue; + + reg_count = wgchecker2_count_reg_entries(fdt, child, cfg_node); + if (reg_count < 0) + goto err; + + if (!reg_count && checker->subordinate_count == 1 && + perm_count == 1) { + if (checker->range_count) + goto err; + checker->full_checker_rule = true; + checker->full_checker_perm = perms[0]; + sbi_free(perms); + perms = NULL; + continue; + } + + if (!reg_count) + reg_count = wgchecker2_count_reg_entries(fdt, child, child); + if (reg_count <= 0) + goto err; + + if (perm_count != 1 && perm_count != (u32)reg_count) + goto err; + if (checker->full_checker_rule) + goto err; + if (checker->range_count + reg_count > checker->slot_count) + goto err; + + rc = wgchecker2_fill_ranges( + fdt, child, + (fdt_getprop(fdt, cfg_node, "reg", NULL) ? cfg_node : child), + perms, perm_count, + &checker->ranges[checker->range_count], reg_count); + sbi_free(perms); + perms = NULL; + if (rc) + goto err; + + checker->range_count += reg_count; + } + + if (checker->full_checker_rule) + return 0; + + return wgchecker2_compact_ranges(checker); + +err: + sbi_free(perms); + return rc ? rc : SBI_EINVAL; +} + +static int wgchecker2_parse_checker(void *fdt, int checker_node, + struct wgchecker2_checker *checker) +{ + const fdt32_t *val; + u64 base = 0, size = 0; + int len, rc; + + rc = fdt_get_node_addr_size(fdt, checker_node, 0, &base, &size); + if (rc) + return rc; + + val = fdt_getprop(fdt, checker_node, WGCHECKER2_PROP_SLOT_COUNT, &len); + if (!val || len < (int)sizeof(fdt32_t)) + return SBI_EINVAL; + + checker->mmio_base = base; + checker->mmio_size = size; + checker->slot_count = fdt32_to_cpu(val[0]); + sbi_snprintf(checker->name, sizeof(checker->name), "%s", + fdt_get_name(fdt, checker_node, NULL)); + + return wgchecker2_parse_checker_rules(fdt, checker_node, checker); +} + +static void wgchecker2_program_clear_slots(const struct wgchecker2_checker *checker) +{ + u32 slot; + + for (slot = 1; slot < checker->slot_count; slot++) { + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_ADDR, 0); + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_PERM, 0); + wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_CFG, 0); + } +} + +static void wgchecker2_program_clear_last_slot(const struct wgchecker2_checker *checker) +{ + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + checker->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_PERM, 0); + wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + checker->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_CFG, 0); +} + +static void wgchecker2_program_clear_slots_from(const struct wgchecker2_checker *checker, + u32 first_slot) +{ + u32 slot; + + if (first_slot >= checker->slot_count) + return; + + for (slot = first_slot; slot < checker->slot_count; slot++) { + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_ADDR, 0); + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_PERM, 0); + wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_CFG, 0); + } +} + +static int wgchecker2_program_checker(const struct wgchecker2_checker *checker) +{ + u64 prev_end = 0; + u32 required_slots = 0, slot = 1, i; + + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_ERRCAUSE, 0); + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_ERRADDR, 0); + + if (checker->full_checker_rule) { + wgchecker2_program_clear_slots(checker); + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + checker->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_PERM, + checker->full_checker_perm); + wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + checker->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_CFG, + WGCHECKER2_SLOT_CFG_A_TOR); + return 0; + } + + for (i = 0; i < checker->range_count; i++) { + if (!i || checker->ranges[i].base != prev_end) + required_slots++; + required_slots++; + prev_end = checker->ranges[i].base + checker->ranges[i].size; + } + + if (required_slots > checker->slot_count - 1) + return SBI_EINVAL; + + prev_end = 0; + for (i = 0; i < checker->range_count; i++) { + const struct wgchecker2_range *range = &checker->ranges[i]; + u64 end = range->base + range->size; + + if (!i || range->base != prev_end) { + wgchecker2_write64(checker->mmio_base + + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_ADDR, + wgchecker2_slot_addr_encode(range->base)); + wgchecker2_write64(checker->mmio_base + + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_PERM, 0); + wgchecker2_write32(checker->mmio_base + + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_CFG, + WGCHECKER2_SLOT_CFG_A_OFF); + slot++; + } + + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_ADDR, + wgchecker2_slot_addr_encode(end)); + wgchecker2_write64(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_PERM, range->perm); + wgchecker2_write32(checker->mmio_base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_CFG, + WGCHECKER2_SLOT_CFG_A_TOR); + prev_end = end; + slot++; + } + + /* + * Keep the reset-time trusted-WID bypass slot alive until the new + * rule set is fully programmed, otherwise the DRAM checker can deny + * OpenSBI's own RAM accesses mid-update. + */ + wgchecker2_program_clear_slots_from(checker, slot); + wgchecker2_program_clear_last_slot(checker); + + return 0; +} + +u32 wgchecker2_count_platform_checkers(void *fdt) +{ + int checker_node; + u32 count = 0; + + if (!fdt) + return 0; + + checker_node = -1; + while (true) { + checker_node = fdt_node_offset_by_compatible(fdt, checker_node, + WGCHECKER2_COMPAT); + if (checker_node < 0) + break; + if (fdt_getprop(fdt, checker_node, WGCHECKER2_PROP_SUBORDINATES, + NULL)) + count++; + } + + return count; +} + +int wgchecker2_init(void *fdt) +{ + struct wgchecker2_platform_ctx *platform; + int checker_node, rc; + u32 count, idx = 0; + + wgchecker2_cleanup(); + + if (!fdt) + return 0; + + count = wgchecker2_count_platform_checkers(fdt); + if (!count) + return 0; + + platform = sbi_zalloc(sizeof(*platform)); + if (!platform) + return SBI_ENOMEM; + + platform->checker_count = count; + platform->checkers = sbi_calloc(sizeof(*platform->checkers), count); + if (!platform->checkers) { + sbi_free(platform); + return SBI_ENOMEM; + } + + checker_node = -1; + while (true) { + checker_node = fdt_node_offset_by_compatible(fdt, checker_node, + WGCHECKER2_COMPAT); + if (checker_node < 0) + break; + if (!fdt_getprop(fdt, checker_node, WGCHECKER2_PROP_SUBORDINATES, + NULL)) + continue; + + rc = wgchecker2_parse_checker(fdt, checker_node, + &platform->checkers[idx]); + if (rc) { + wgchecker2_free_platform_ctx(platform); + return rc; + } + + rc = wgchecker2_program_checker(&platform->checkers[idx]); + if (rc) { + wgchecker2_free_platform_ctx(platform); + return rc; + } + idx++; + } + + wgchecker2_platform = platform; + return 0; +} + +void wgchecker2_cleanup(void) +{ + if (!wgchecker2_platform) + return; + + wgchecker2_free_platform_ctx(wgchecker2_platform); + wgchecker2_platform = NULL; +} + +u32 wgchecker2_checker_count(void) +{ + return wgchecker2_platform ? wgchecker2_platform->checker_count : 0; +} diff --git a/platform/generic/worldguard.c b/platform/generic/worldguard.c new file mode 100644 index 00000000..a951459b --- /dev/null +++ b/platform/generic/worldguard.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Generic WorldGuard runtime support + * + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct worldguard_platform_ctx { + u32 hart_count; + bool runtime_enabled; + struct wg_cpu_defaults *hart_defaults; +}; + +static struct worldguard_platform_ctx *worldguard_platform; + +static u32 worldguard_wid_mask(u32 wid) +{ + return (wid < WGCHECKER2_MAX_WIDS) ? (1U << wid) : 0; +} + +static void worldguard_free_platform(void) +{ + if (!worldguard_platform) + return; + + sbi_free(worldguard_platform->hart_defaults); + sbi_free(worldguard_platform); + worldguard_platform = NULL; +} + +static bool worldguard_runtime_enabled(void) +{ + return worldguard_platform && worldguard_platform->runtime_enabled; +} + +static void worldguard_init_cpu_defaults(struct worldguard_platform_ctx *platform) +{ + u32 i; + + if (!platform || !platform->hart_defaults) + return; + + for (i = 0; i < platform->hart_count; i++) { + platform->hart_defaults[i].trusted_wid = 0; + platform->hart_defaults[i].nworlds = 1; + platform->hart_defaults[i].valid_wid_mask = 0x1; + } +} + +static int worldguard_parse_wid_prop(void *fdt, int node, const char *prop_name, + u32 *out_wid) +{ + const fdt32_t *prop; + int len; + + if (!out_wid) + return SBI_EINVAL; + + prop = fdt_getprop(fdt, node, prop_name, &len); + if (!prop) + return SBI_ENOENT; + if (len != (int)sizeof(fdt32_t)) + return SBI_EINVAL; + + *out_wid = fdt32_to_cpu(prop[0]); + if (*out_wid >= WGCHECKER2_MAX_WIDS) + return SBI_EINVAL; + + return 0; +} + +static int worldguard_parse_widlist(void *fdt, int node, const char *prop_name, + u32 *out_mask) +{ + const fdt32_t *prop; + u32 mask = 0, count, wid; + int len, i; + + if (!out_mask) + return SBI_EINVAL; + + *out_mask = 0; + + prop = fdt_getprop(fdt, node, prop_name, &len); + if (!prop) + return 0; + if (len < 0 || (len % (int)sizeof(fdt32_t))) + return SBI_EINVAL; + + count = len / sizeof(fdt32_t); + if (count > WGCHECKER2_MAX_WIDS) + return SBI_EINVAL; + + for (i = 0; i < (int)count; i++) { + wid = fdt32_to_cpu(prop[i]); + if (wid >= WGCHECKER2_MAX_WIDS) + return SBI_EINVAL; + if (mask & worldguard_wid_mask(wid)) + return SBI_EINVAL; + + mask |= worldguard_wid_mask(wid); + } + + *out_mask = mask; + return 0; +} + +static int worldguard_parse_cpu_defaults(void *fdt, + struct worldguard_platform_ctx *platform) +{ + struct wg_cpu_defaults *cpu_defaults; + u32 hartid, hartindex, max_wid; + int cpus_offset, cpu_offset, wgcpu, rc; + + if (!fdt || !platform || !platform->hart_defaults) + return 0; + + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return 0; + + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + if (fdt_parse_hart_id(fdt, cpu_offset, &hartid)) + continue; + + hartindex = sbi_hartid_to_hartindex(hartid); + if (!sbi_hartindex_valid(hartindex) || + hartindex >= platform->hart_count) + continue; + + wgcpu = fdt_subnode_offset(fdt, cpu_offset, WORLDGUARD_CPU_NODE); + if (wgcpu < 0 || fdt_node_check_compatible( + fdt, wgcpu, WORLDGUARD_CPU_COMPAT)) + continue; + + cpu_defaults = &platform->hart_defaults[hartindex]; + rc = worldguard_parse_wid_prop(fdt, wgcpu, WORLDGUARD_PROP_MWID, + &cpu_defaults->trusted_wid); + if (rc) + return rc; + + max_wid = cpu_defaults->trusted_wid; + rc = worldguard_parse_widlist(fdt, wgcpu, + WORLDGUARD_PROP_MWIDLIST, + &cpu_defaults->valid_wid_mask); + if (rc) + return rc; + + cpu_defaults->valid_wid_mask |= + worldguard_wid_mask(cpu_defaults->trusted_wid); + if (cpu_defaults->valid_wid_mask) { + u32 wid; + + for (wid = 0; wid < WGCHECKER2_MAX_WIDS; wid++) { + if (cpu_defaults->valid_wid_mask & (1U << wid)) + max_wid = wid; + } + } + + cpu_defaults->nworlds = max_wid + 1; + } + + return 0; +} + +static bool worldguard_has_cpu_runtime(void *fdt) +{ + u32 hartid; + int cpus_offset, cpu_offset, wgcpu; + + if (!fdt) + return false; + + cpus_offset = fdt_path_offset(fdt, "/cpus"); + if (cpus_offset < 0) + return false; + + fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) { + if (fdt_parse_hart_id(fdt, cpu_offset, &hartid)) + continue; + + wgcpu = fdt_subnode_offset(fdt, cpu_offset, WORLDGUARD_CPU_NODE); + if (wgcpu < 0) + continue; + if (fdt_node_check_compatible(fdt, wgcpu, WORLDGUARD_CPU_COMPAT)) + continue; + + return true; + } + + return false; +} + +static int worldguard_validate_domain_ctx(const struct sbi_domain *dom, + const struct worldguard_domain_ctx *ctx) +{ + const struct wg_cpu_defaults *cpu_defaults; + u32 hartindex; + + if (!worldguard_platform || !dom || !ctx || dom == &root || + !dom->possible_harts) + return 0; + + for (hartindex = 0; hartindex < worldguard_platform->hart_count; + hartindex++) { + if (!sbi_hartmask_test_hartindex(hartindex, dom->possible_harts)) + continue; + + cpu_defaults = &worldguard_platform->hart_defaults[hartindex]; + if (!(cpu_defaults->valid_wid_mask & worldguard_wid_mask(ctx->wid))) + return SBI_EINVAL; + if (ctx->widlist_mask & ~cpu_defaults->valid_wid_mask) + return SBI_EINVAL; + } + + return 0; +} + +static const struct wg_cpu_defaults *worldguard_current_cpu_defaults(void) +{ + u32 hartindex; + + if (!worldguard_platform || !worldguard_platform->hart_defaults) + return NULL; + + hartindex = sbi_hartid_to_hartindex(current_hartid()); + if (!sbi_hartindex_valid(hartindex) || + hartindex >= worldguard_platform->hart_count) + return NULL; + + return &worldguard_platform->hart_defaults[hartindex]; +} + +static u32 worldguard_fallback_wid(void) +{ + const struct wg_cpu_defaults *cpu_defaults = + worldguard_current_cpu_defaults(); + + return cpu_defaults ? cpu_defaults->trusted_wid : 0; +} + +static u32 worldguard_valid_wid_mask(void) +{ + const struct wg_cpu_defaults *cpu_defaults = + worldguard_current_cpu_defaults(); + + return cpu_defaults ? cpu_defaults->valid_wid_mask : + worldguard_wid_mask(worldguard_fallback_wid()); +} + +static u32 worldguard_select_slwid(u32 widlist_mask, bool has_wid, u32 wid, + u32 fallback) +{ + u32 i; + + if (!widlist_mask) + return fallback; + + if (has_wid && (worldguard_wid_mask(wid) & widlist_mask)) + return wid; + + for (i = 0; i < WGCHECKER2_MAX_WIDS; i++) { + if (widlist_mask & (1U << i)) + return i; + } + + return fallback; +} + +static void worldguard_program_wid_state(u32 mlwid, u32 mwiddeleg, u32 slwid) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMWG)) + return; + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSWG)) { + csr_write(CSR_MLWID, mlwid); + return; + } + + csr_write(CSR_MWIDDELEG, 0); + csr_write(CSR_MLWID, mlwid); + if (mwiddeleg) { + csr_write(CSR_MWIDDELEG, mwiddeleg); + csr_write(CSR_SLWID, slwid); + } +} + +static int worldguard_init(void *fdt) +{ + struct worldguard_platform_ctx *platform; + u32 checker_count; + bool has_runtime; + int rc; + + wgchecker2_cleanup(); + worldguard_free_platform(); + + if (!fdt) + return 0; + + checker_count = wgchecker2_count_platform_checkers(fdt); + has_runtime = worldguard_has_cpu_runtime(fdt); + if (!checker_count && !has_runtime) + return 0; + + platform = sbi_zalloc(sizeof(*platform)); + if (!platform) + return SBI_ENOMEM; + + platform->hart_count = sbi_scratch_last_hartindex() + 1; + platform->runtime_enabled = has_runtime; + platform->hart_defaults = sbi_calloc(sizeof(*platform->hart_defaults), + platform->hart_count); + if (!platform->hart_defaults) { + sbi_free(platform); + return SBI_ENOMEM; + } + + worldguard_init_cpu_defaults(platform); + rc = worldguard_parse_cpu_defaults(fdt, platform); + if (rc) { + sbi_free(platform->hart_defaults); + sbi_free(platform); + return rc; + } + + worldguard_platform = platform; + + if (checker_count) { + rc = wgchecker2_init(fdt); + if (rc) { + worldguard_free_platform(); + wgchecker2_cleanup(); + return rc; + } + } + + return 0; +} + +static int worldguard_domain_init(void *fdt, int domain_offset, + struct sbi_domain *dom, void **out_ctx) +{ + struct worldguard_domain_ctx *ctx; + int hoff, child, rc; + bool found = false; + + if (!out_ctx) + return SBI_EINVAL; + + *out_ctx = NULL; + if (!worldguard_runtime_enabled()) + return 0; + if (!fdt || domain_offset < 0) + return 0; + + hoff = fdt_subnode_offset(fdt, domain_offset, "hw-isolation"); + if (hoff < 0) + return (dom == &root) ? 0 : SBI_EINVAL; + + fdt_for_each_subnode(child, fdt, hoff) { + if (fdt_node_check_compatible(fdt, child, WGCHECKER2_COMPAT)) + continue; + found = true; + break; + } + + if (!found) + return (dom == &root) ? 0 : SBI_EINVAL; + + ctx = sbi_zalloc(sizeof(*ctx)); + if (!ctx) + return SBI_ENOMEM; + + rc = worldguard_parse_wid_prop(fdt, child, WORLDGUARD_PROP_WID, + &ctx->wid); + if (rc) + goto err_free_ctx; + ctx->has_wid = true; + + rc = worldguard_parse_widlist(fdt, child, WORLDGUARD_PROP_WIDLIST, + &ctx->widlist_mask); + if (rc) + goto err_free_ctx; + + rc = worldguard_validate_domain_ctx(dom, ctx); + if (rc) + goto err_free_ctx; + + *out_ctx = ctx; + return 0; + +err_free_ctx: + sbi_free(ctx); + return rc; +} + +static void worldguard_domain_exit(const struct sbi_domain *src, + const struct sbi_domain *dst, void *ctx) +{ + u32 mlwid = worldguard_fallback_wid(); + + (void)ctx; + if (!worldguard_runtime_enabled()) + return; + + worldguard_program_wid_state(mlwid, 0, mlwid); + +} + +static void worldguard_domain_enter(const struct sbi_domain *dst, + const struct sbi_domain *src, void *ctx) +{ + struct worldguard_domain_ctx *dctx = ctx; + u32 valid_mask = worldguard_valid_wid_mask(); + u32 mlwid = worldguard_fallback_wid(); + u32 mwiddeleg = 0; + u32 slwid = mlwid; + + (void)src; + if (!worldguard_runtime_enabled()) + return; + + if (dctx && dctx->has_wid && (worldguard_wid_mask(dctx->wid) & valid_mask)) + mlwid = dctx->wid; + + if (dctx) + mwiddeleg = dctx->widlist_mask & valid_mask; + slwid = worldguard_select_slwid(mwiddeleg, dctx && dctx->has_wid, + dctx ? dctx->wid : 0, mlwid); + + worldguard_program_wid_state(mlwid, mwiddeleg, slwid); + +} + +static void worldguard_domain_cleanup(struct sbi_domain *dom, void *ctx) +{ + (void)dom; + sbi_free(ctx); +} + +static const struct sbi_hwiso_ops worldguard_ops = { + .name = WGCHECKER2_COMPAT, + .init = worldguard_init, + .domain_init = worldguard_domain_init, + .domain_exit = worldguard_domain_exit, + .domain_enter = worldguard_domain_enter, + .domain_cleanup = worldguard_domain_cleanup, +}; + +int worldguard_register(void) +{ + return sbi_hwiso_register(&worldguard_ops); +} + +const struct sbi_hwiso_ops *worldguard_ops_get(void) +{ + return &worldguard_ops; +} + +#ifdef CONFIG_SBIUNIT +static struct worldguard_domain_ctx * +worldguard_test_find_domain_ctx(const struct sbi_domain *dom) +{ + u32 i; + + if (!dom || !dom->hwiso_ctxs) + return NULL; + + for (i = 0; i < dom->hwiso_ctx_count; i++) { + if (dom->hwiso_ctxs[i].ops != &worldguard_ops) + continue; + + return dom->hwiso_ctxs[i].ctx; + } + + return NULL; +} + +int worldguard_test_check_runtime_state(bool runtime_enabled) +{ + if (!worldguard_platform) + return runtime_enabled ? SBI_ENOENT : 0; + if (worldguard_platform->runtime_enabled != runtime_enabled) + return SBI_EINVAL; + + return 0; +} + +int worldguard_test_check_domain_state(const struct sbi_domain *dom, + bool expect_ctx, u32 wid, + u32 widlist_mask) +{ + struct worldguard_domain_ctx *ctx = worldguard_test_find_domain_ctx(dom); + + if (!expect_ctx) + return ctx ? SBI_EINVAL : 0; + if (!ctx || !ctx->has_wid) + return SBI_ENOENT; + if (ctx->wid != wid || ctx->widlist_mask != widlist_mask) + return SBI_EINVAL; + + return 0; +} +#endif -- 2.25.1 From raymondmaoca at gmail.com Tue May 19 13:33:29 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Tue, 19 May 2026 16:33:29 -0400 Subject: [PATCH 5/7] test: add generic hwiso SBI unit coverage In-Reply-To: <20260519203331.2773185-1-raymondmaoca@gmail.com> References: <20260519203331.2773185-1-raymondmaoca@gmail.com> Message-ID: <20260519203331.2773185-6-raymondmaoca@gmail.com> From: Raymond Mao Add a generic HWISO SBI unit suite that exercises boot-time initialization and domain-switch hook sequencing without embedding one platform mechanism's policy directly into the core test driver. Provide a small test registration layer so each HWISO mechanism can supply its own state and quiesce assertions. Signed-off-by: Raymond Mao --- include/sbi/sbi_hwiso_test.h | 33 +++++++ lib/sbi/objects.mk | 3 + lib/sbi/sbi_hwiso_test.c | 167 +++++++++++++++++++++++++++++++++++ lib/sbi/sbi_hwiso_testlib.c | 108 ++++++++++++++++++++++ 4 files changed, 311 insertions(+) create mode 100644 include/sbi/sbi_hwiso_test.h create mode 100644 lib/sbi/sbi_hwiso_test.c create mode 100644 lib/sbi/sbi_hwiso_testlib.c diff --git a/include/sbi/sbi_hwiso_test.h b/include/sbi/sbi_hwiso_test.h new file mode 100644 index 00000000..3970df5a --- /dev/null +++ b/include/sbi/sbi_hwiso_test.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#ifndef __SBI_HWISO_TEST_H__ +#define __SBI_HWISO_TEST_H__ + +#include +#include +#include + +#ifdef CONFIG_SBIUNIT +struct sbi_hwiso_test_ops { + void (*boot_test)(struct sbiunit_test_case *test); + void (*domain_state_test)(struct sbiunit_test_case *test, + const struct sbi_domain *dom, void *ctx); + void (*domain_quiesce_test)(struct sbiunit_test_case *test, + const struct sbi_domain *dom, void *ctx); +}; + +int sbi_hwiso_test_register(const struct sbi_hwiso_ops *ops, + const struct sbi_hwiso_test_ops *test_ops); +void sbi_hwiso_test_boot(struct sbiunit_test_case *test); +void sbi_hwiso_test_domain_state(struct sbiunit_test_case *test, + const struct sbi_domain *dom); +void sbi_hwiso_test_domain_quiesced(struct sbiunit_test_case *test, + const struct sbi_domain *dom); +#endif + +#endif diff --git a/lib/sbi/objects.mk b/lib/sbi/objects.mk index 6091499a..8839d13c 100644 --- a/lib/sbi/objects.mk +++ b/lib/sbi/objects.mk @@ -13,9 +13,12 @@ libsbi-objs-y += riscv_hardfp.o libsbi-objs-y += riscv_locks.o libsbi-objs-$(CONFIG_SBIUNIT) += sbi_unit_test.o libsbi-objs-$(CONFIG_SBIUNIT) += sbi_unit_tests.o +libsbi-objs-$(CONFIG_SBIUNIT) += sbi_hwiso_testlib.o libsbi-objs-$(CONFIG_SBIUNIT) += sbi_bitmap_test.o +libsbi-objs-$(CONFIG_SBIUNIT) += sbi_hwiso_test.o carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += bitmap_test_suite +carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += hwiso_test_suite carray-sbi_unit_tests-$(CONFIG_SBIUNIT) += console_test_suite libsbi-objs-y += sbi_ecall.o diff --git a/lib/sbi/sbi_hwiso_test.c b/lib/sbi/sbi_hwiso_test.c new file mode 100644 index 00000000..716596e2 --- /dev/null +++ b/lib/sbi/sbi_hwiso_test.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u32 hwiso_collect_switch_domains(struct sbi_domain *current, + struct sbi_domain **targets, + u32 max_targets) +{ + u32 i; + u32 count = 0; + struct sbi_domain *dom; + + if (!targets || !max_targets) + return 0; + + sbi_domain_for_each(i, dom) { + if (dom == current || dom == &root) + continue; + + targets[count++] = dom; + if (count == max_targets) + return count; + } + + if (current != &root && count < max_targets) + targets[count++] = &root; + + return count; +} + +static void hwiso_snapshot_context(struct sbi_context *ctx) +{ + struct sbi_trap_regs *trap_regs; + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + trap_regs = (struct sbi_trap_regs *)(csr_read(CSR_MSCRATCH) - + SBI_TRAP_REGS_SIZE); + ctx->regs = *trap_regs; + ctx->sstatus = csr_read(CSR_SSTATUS); + ctx->sie = csr_read(CSR_SIE); + ctx->stvec = csr_read(CSR_STVEC); + ctx->sscratch = csr_read(CSR_SSCRATCH); + ctx->sepc = csr_read(CSR_SEPC); + ctx->scause = csr_read(CSR_SCAUSE); + ctx->stval = csr_read(CSR_STVAL); + ctx->sip = csr_read(CSR_SIP); + ctx->satp = csr_read(CSR_SATP); + ctx->scounteren = 0; + ctx->senvcfg = 0; + if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10) + ctx->scounteren = csr_read(CSR_SCOUNTEREN); + if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_12) + ctx->senvcfg = csr_read(CSR_SENVCFG); + ctx->initialized = true; +} + +static struct sbi_context *hwiso_ensure_context(struct sbiunit_test_case *test, + struct sbi_domain *dom, + struct sbi_context *tmpl, + u32 hartindex) +{ + struct sbi_context *dom_ctx; + + dom_ctx = sbi_hartindex_to_domain_context(hartindex, dom); + if (!dom_ctx) { + dom_ctx = sbi_zalloc(sizeof(*dom_ctx)); + SBIUNIT_ASSERT_NE(test, dom_ctx, NULL); + dom_ctx->dom = dom; + dom->hartindex_to_context_table[hartindex] = dom_ctx; + } + + if (tmpl) { + *dom_ctx = *tmpl; + dom_ctx->dom = dom; + dom->hartindex_to_context_table[hartindex] = dom_ctx; + } + + return dom_ctx; +} + +static void hwiso_boot_test(struct sbiunit_test_case *test) +{ + sbi_hwiso_test_boot(test); +} + +static void hwiso_domain_switch_test(struct sbiunit_test_case *test) +{ + struct sbi_domain *cur_dom = sbi_domain_thishart_ptr(); + struct sbi_context *ctx = sbi_domain_context_thishart_ptr(); + struct sbi_domain *switch_doms[2]; + struct sbi_context *cur_ctx; + struct sbi_context tmpl_ctx; + u32 switch_count; + u32 i; + u32 hartindex = sbi_hartid_to_hartindex(current_hartid()); + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + bool debug_on = false; + + SBIUNIT_ASSERT_NE(test, cur_dom, NULL); + + if (scratch->options & SBI_SCRATCH_DEBUG_PRINTS) + debug_on = true; + else + scratch->options |= SBI_SCRATCH_DEBUG_PRINTS; + + if (!ctx) { + cur_ctx = sbi_zalloc(sizeof(*cur_ctx)); + SBIUNIT_ASSERT_NE(test, cur_ctx, NULL); + cur_ctx->dom = cur_dom; + cur_dom->hartindex_to_context_table[hartindex] = cur_ctx; + ctx = cur_ctx; + } + + hwiso_snapshot_context(ctx); + tmpl_ctx = *ctx; + switch_count = hwiso_collect_switch_domains(cur_dom, switch_doms, 2); + + (void)hwiso_ensure_context(test, cur_dom, NULL, hartindex); + for (i = 0; i < switch_count; i++) + (void)hwiso_ensure_context(test, switch_doms[i], &tmpl_ctx, + hartindex); + + if (!switch_count) + goto out; + + sbi_hwiso_domain_exit(cur_dom, switch_doms[0]); + sbi_hwiso_test_domain_quiesced(test, cur_dom); + sbi_hwiso_domain_enter(switch_doms[0], cur_dom); + sbi_hwiso_test_domain_state(test, switch_doms[0]); + sbi_hwiso_domain_exit(switch_doms[0], cur_dom); + sbi_hwiso_test_domain_quiesced(test, switch_doms[0]); + sbi_hwiso_domain_enter(cur_dom, switch_doms[0]); + sbi_hwiso_test_domain_state(test, cur_dom); + + for (i = 0; i < switch_count; i++) { + SBIUNIT_ASSERT_EQ(test, sbi_domain_context_enter(switch_doms[i]), 0); + sbi_hwiso_test_domain_state(test, switch_doms[i]); + } + + SBIUNIT_ASSERT_EQ(test, sbi_domain_context_enter(cur_dom), 0); + sbi_hwiso_test_domain_state(test, cur_dom); + +out: + if (!debug_on) + scratch->options &= ~SBI_SCRATCH_DEBUG_PRINTS; +} + +static struct sbiunit_test_case hwiso_test_cases[] = { + SBIUNIT_TEST_CASE(hwiso_boot_test), + SBIUNIT_TEST_CASE(hwiso_domain_switch_test), + SBIUNIT_END_CASE, +}; + +SBIUNIT_TEST_SUITE(hwiso_test_suite, hwiso_test_cases); diff --git a/lib/sbi/sbi_hwiso_testlib.c b/lib/sbi/sbi_hwiso_testlib.c new file mode 100644 index 00000000..2a988088 --- /dev/null +++ b/lib/sbi/sbi_hwiso_testlib.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ + +#include +#include +#include +#include + +struct sbi_hwiso_test_node { + const struct sbi_hwiso_ops *ops; + const struct sbi_hwiso_test_ops *test_ops; + struct sbi_dlist node; +}; + +static SBI_LIST_HEAD(hwiso_test_ops_list); + +static const struct sbi_hwiso_test_ops * +sbi_hwiso_find_test_ops(const struct sbi_hwiso_ops *ops) +{ + struct sbi_hwiso_test_node *entry; + + sbi_list_for_each_entry(entry, &hwiso_test_ops_list, node) { + if (entry->ops == ops) + return entry->test_ops; + } + + return NULL; +} + +int sbi_hwiso_test_register(const struct sbi_hwiso_ops *ops, + const struct sbi_hwiso_test_ops *test_ops) +{ + struct sbi_hwiso_test_node *node; + + if (!ops || !test_ops) + return SBI_EINVAL; + if (sbi_hwiso_find_test_ops(ops)) + return SBI_EALREADY; + + node = sbi_zalloc(sizeof(*node)); + if (!node) + return SBI_ENOMEM; + + node->ops = ops; + node->test_ops = test_ops; + SBI_INIT_LIST_HEAD(&node->node); + sbi_list_add_tail(&node->node, &hwiso_test_ops_list); + + return 0; +} + +void sbi_hwiso_test_boot(struct sbiunit_test_case *test) +{ + struct sbi_hwiso_test_node *entry; + + sbi_list_for_each_entry(entry, &hwiso_test_ops_list, node) { + if (!entry->test_ops->boot_test) + continue; + entry->test_ops->boot_test(test); + } +} + +void sbi_hwiso_test_domain_state(struct sbiunit_test_case *test, + const struct sbi_domain *dom) +{ + const struct sbi_hwiso_test_ops *test_ops; + u32 i; + + if (!dom || !dom->hwiso_ctxs) + return; + + for (i = 0; i < dom->hwiso_ctx_count; i++) { + if (!dom->hwiso_ctxs[i].ops) + continue; + + test_ops = sbi_hwiso_find_test_ops(dom->hwiso_ctxs[i].ops); + if (!test_ops || !test_ops->domain_state_test) + continue; + + test_ops->domain_state_test(test, dom, dom->hwiso_ctxs[i].ctx); + } +} + +void sbi_hwiso_test_domain_quiesced(struct sbiunit_test_case *test, + const struct sbi_domain *dom) +{ + const struct sbi_hwiso_test_ops *test_ops; + u32 i; + + if (!dom || !dom->hwiso_ctxs) + return; + + for (i = 0; i < dom->hwiso_ctx_count; i++) { + if (!dom->hwiso_ctxs[i].ops) + continue; + + test_ops = sbi_hwiso_find_test_ops(dom->hwiso_ctxs[i].ops); + if (!test_ops || !test_ops->domain_quiesce_test) + continue; + + test_ops->domain_quiesce_test(test, dom, + dom->hwiso_ctxs[i].ctx); + } +} -- 2.25.1 From raymondmaoca at gmail.com Tue May 19 13:33:30 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Tue, 19 May 2026 16:33:30 -0400 Subject: [PATCH 6/7] platform: virt: add QEMU virt WorldGuard hwiso tests In-Reply-To: <20260519203331.2773185-1-raymondmaoca@gmail.com> References: <20260519203331.2773185-1-raymondmaoca@gmail.com> Message-ID: <20260519203331.2773185-7-raymondmaoca@gmail.com> From: Raymond Mao Add QEMU virt WorldGuard mechanism-specific tests for boot-time checker programming and runtime CSR state, and verify the expected root / domain at 0 / domain at 1 behavior through the HWISO test callback API. Signed-off-by: Raymond Mao --- platform/generic/objects.mk | 1 + .../generic/virt/qemu_virt_wgchecker_test.c | 321 ++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 platform/generic/virt/qemu_virt_wgchecker_test.c diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk index 80bd65ea..be58873a 100644 --- a/platform/generic/objects.mk +++ b/platform/generic/objects.mk @@ -23,6 +23,7 @@ platform-objs-y += platform_override_modules.o platform-objs-y += worldguard.o platform-objs-y += wgchecker2.o platform-objs-y += virt/qemu_virt_worldguard.o +platform-objs-$(CONFIG_SBIUNIT) += virt/qemu_virt_wgchecker_test.o # Blobs to build FW_TEXT_START=0x80000000 diff --git a/platform/generic/virt/qemu_virt_wgchecker_test.c b/platform/generic/virt/qemu_virt_wgchecker_test.c new file mode 100644 index 00000000..0eeaa3a0 --- /dev/null +++ b/platform/generic/virt/qemu_virt_wgchecker_test.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026 RISCstar Solutions Corporation. + * + * Author: Raymond Mao + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct qemu_virt_wg_slot_expect { + bool check_addr; + u64 addr; + u64 perm; + u32 cfg; +}; + +struct qemu_virt_wg_checker_expect { + const char *path; + u32 slot_count; + struct qemu_virt_wg_slot_expect *slots; + struct qemu_virt_wg_slot_expect last_slot; +}; + +struct qemu_virt_wg_expect { + u32 wid; + u32 widlist_mask; + bool check_slwid; + u32 slwid; +}; + +static u64 qemu_virt_wg_mmio_read64(unsigned long addr) +{ +#if __riscv_xlen != 32 + return readq((void *)addr); +#else + return readl((void *)addr) | ((u64)readl((void *)(addr + 4)) << 32); +#endif +} + +static struct qemu_virt_wg_slot_expect qemu_virt_wg_dram_slots[] = { + { + .check_addr = true, + .addr = 0x20000000ULL, + .perm = 0, + .cfg = WGCHECKER2_SLOT_CFG_A_OFF, + }, + { + .check_addr = true, + .addr = 0x30000000ULL, + .perm = 0xcf, + .cfg = WGCHECKER2_SLOT_CFG_A_TOR, + }, + { + .check_addr = true, + .addr = 0x30400000ULL, + .perm = 0xcc, + .cfg = WGCHECKER2_SLOT_CFG_A_TOR, + }, + { + .check_addr = true, + .addr = 0x40000000ULL, + .perm = 0xcf, + .cfg = WGCHECKER2_SLOT_CFG_A_TOR, + }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, +}; + +static struct qemu_virt_wg_slot_expect qemu_virt_wg_zero_slots[] = { + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, + { .check_addr = true, .addr = 0, .perm = 0, .cfg = 0 }, +}; + +static const struct qemu_virt_wg_checker_expect qemu_virt_wg_checker_expects[] = { + { + .path = "/soc/wgchecker at 6000000", + .slot_count = 16, + .slots = qemu_virt_wg_dram_slots, + .last_slot = { .perm = 0, .cfg = 0 }, + }, + { + .path = "/soc/wgchecker at 6001000", + .slot_count = 16, + .slots = qemu_virt_wg_zero_slots, + .last_slot = { .perm = 0xc3, .cfg = WGCHECKER2_SLOT_CFG_A_TOR }, + }, + { + .path = "/soc/wgchecker at 6002000", + .slot_count = 1, + .slots = NULL, + .last_slot = { .perm = 0xc0, .cfg = WGCHECKER2_SLOT_CFG_A_TOR }, + }, +}; + +static const struct qemu_virt_wg_expect qemu_virt_wg_root_expect = { + .wid = 3, + .widlist_mask = 0, + .check_slwid = false, +}; + +static const struct qemu_virt_wg_expect qemu_virt_wg_domain0_expect = { + .wid = 0, + .widlist_mask = 0xb, + .check_slwid = true, + .slwid = 0, +}; + +static const struct qemu_virt_wg_expect qemu_virt_wg_domain1_expect = { + .wid = 1, + .widlist_mask = 0xa, + .check_slwid = true, + .slwid = 1, +}; + +static struct sbi_domain *qemu_virt_wg_find_domain(const char *name) +{ + u32 i; + struct sbi_domain *dom; + + sbi_domain_for_each(i, dom) { + if (!sbi_strcmp(dom->name, name)) + return dom; + } + + return NULL; +} + +static const struct qemu_virt_wg_expect * +qemu_virt_wg_domain_expect(const struct sbi_domain *dom) +{ + if (dom == &root) + return &qemu_virt_wg_root_expect; + if (dom && !sbi_strcmp(dom->name, "root")) + return &qemu_virt_wg_root_expect; + if (!dom) + return NULL; + if (!sbi_strcmp(dom->name, "domain at 0")) + return &qemu_virt_wg_domain0_expect; + if (!sbi_strcmp(dom->name, "domain at 1")) + return &qemu_virt_wg_domain1_expect; + + return NULL; +} + +static void qemu_virt_wg_assert_checker(struct sbiunit_test_case *test, + const struct qemu_virt_wg_checker_expect *expect) +{ + void *fdt = fdt_get_address(); + u64 base, size, addr, perm; + u32 cfg, slot; + int node, rc; + + node = fdt_path_offset(fdt, expect->path); + SBIUNIT_ASSERT(test, node >= 0); + + rc = fdt_get_node_addr_size(fdt, node, 0, &base, &size); + SBIUNIT_ASSERT_EQ(test, rc, 0); + (void)size; + + SBIUNIT_ASSERT_EQ(test, + readl((void *)(unsigned long) + (base + WGCHECKER2_MMIO_NSLOTS)), + expect->slot_count); + SBIUNIT_ASSERT_EQ(test, + qemu_virt_wg_mmio_read64(base + WGCHECKER2_MMIO_ERRCAUSE), + 0UL); + SBIUNIT_ASSERT_EQ(test, + qemu_virt_wg_mmio_read64(base + WGCHECKER2_MMIO_ERRADDR), + 0UL); + + for (slot = 1; slot < expect->slot_count; slot++) { + addr = qemu_virt_wg_mmio_read64( + base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_ADDR); + perm = qemu_virt_wg_mmio_read64( + base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_PERM); + cfg = readl((void *)(unsigned long) + (base + WGCHECKER2_MMIO_SLOT_BASE + + slot * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_CFG)) & + WGCHECKER2_SLOT_CFG_A_MASK; + + if (expect->slots[slot - 1].check_addr) + SBIUNIT_ASSERT_EQ(test, addr, expect->slots[slot - 1].addr); + SBIUNIT_ASSERT_EQ(test, perm, expect->slots[slot - 1].perm); + SBIUNIT_ASSERT_EQ(test, cfg, expect->slots[slot - 1].cfg); + } + + perm = qemu_virt_wg_mmio_read64( + base + WGCHECKER2_MMIO_SLOT_BASE + + expect->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_PERM); + cfg = readl((void *)(unsigned long) + (base + WGCHECKER2_MMIO_SLOT_BASE + + expect->slot_count * WGCHECKER2_MMIO_SLOT_STRIDE + + WGCHECKER2_MMIO_SLOT_CFG)) & + WGCHECKER2_SLOT_CFG_A_MASK; + SBIUNIT_ASSERT_EQ(test, perm, expect->last_slot.perm); + SBIUNIT_ASSERT_EQ(test, cfg, expect->last_slot.cfg); +} + +static void qemu_virt_wg_assert_state(struct sbiunit_test_case *test, + const struct sbi_domain *dom, void *ctx) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + const struct qemu_virt_wg_expect *expect; + + (void)ctx; + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMWG)) + return; + expect = qemu_virt_wg_domain_expect(dom); + if (!expect) + return; + + SBIUNIT_ASSERT_EQ(test, csr_read(CSR_MLWID), expect->wid); + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSWG)) + return; + + SBIUNIT_ASSERT_EQ(test, csr_read(CSR_MWIDDELEG), expect->widlist_mask); + if (!expect->check_slwid) + return; + + SBIUNIT_ASSERT_EQ(test, csr_read(CSR_SLWID), expect->slwid); +} + +static void qemu_virt_wg_assert_quiesced(struct sbiunit_test_case *test, + const struct sbi_domain *dom, void *ctx) +{ + struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); + + (void)dom; + (void)ctx; + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SMWG)) + return; + + SBIUNIT_ASSERT_EQ(test, csr_read(CSR_MLWID), qemu_virt_wg_root_expect.wid); + + if (!sbi_hart_has_extension(scratch, SBI_HART_EXT_SSWG)) + return; + + SBIUNIT_ASSERT_EQ(test, csr_read(CSR_MWIDDELEG), 0UL); +} + +static void qemu_virt_wg_boot_test(struct sbiunit_test_case *test) +{ + struct sbi_domain *dom0, *dom1; + u32 i; + + for (i = 0; + i < (sizeof(qemu_virt_wg_checker_expects) / + sizeof(qemu_virt_wg_checker_expects[0])); + i++) + qemu_virt_wg_assert_checker(test, &qemu_virt_wg_checker_expects[i]); + + SBIUNIT_ASSERT_EQ(test, worldguard_test_check_runtime_state(true), 0); + SBIUNIT_ASSERT_EQ(test, wgchecker2_checker_count(), 3); + + dom0 = qemu_virt_wg_find_domain("domain at 0"); + dom1 = qemu_virt_wg_find_domain("domain at 1"); + SBIUNIT_ASSERT_NE(test, dom0, NULL); + SBIUNIT_ASSERT_NE(test, dom1, NULL); + SBIUNIT_ASSERT_EQ(test, + worldguard_test_check_domain_state( + dom0, true, qemu_virt_wg_domain0_expect.wid, + qemu_virt_wg_domain0_expect.widlist_mask), + 0); + SBIUNIT_ASSERT_EQ(test, + worldguard_test_check_domain_state( + dom1, true, qemu_virt_wg_domain1_expect.wid, + qemu_virt_wg_domain1_expect.widlist_mask), + 0); + SBIUNIT_ASSERT_EQ(test, + worldguard_test_check_domain_state(&root, false, 0, 0), + 0); +} + +const struct sbi_hwiso_test_ops qemu_virt_worldguard_test_ops = { + .boot_test = qemu_virt_wg_boot_test, + .domain_state_test = qemu_virt_wg_assert_state, + .domain_quiesce_test = qemu_virt_wg_assert_quiesced, +}; -- 2.25.1 From raymondmaoca at gmail.com Tue May 19 13:33:31 2026 From: raymondmaoca at gmail.com (Raymond Mao) Date: Tue, 19 May 2026 16:33:31 -0400 Subject: [PATCH 7/7] platform: virt: add WorldGuard HWISO failure-mode SBIUNIT test In-Reply-To: <20260519203331.2773185-1-raymondmaoca@gmail.com> References: <20260519203331.2773185-1-raymondmaoca@gmail.com> Message-ID: <20260519203331.2773185-8-raymondmaoca@gmail.com> From: Raymond Mao Extend the generic HWISO SBIUNIT test hook interface with a failure-test callback and invoke it from a new hwiso_failure_mode_test case. Implement the QEMU virt WorldGuard failure-mode test by switching into domain at 0's WorldGuard state, issuing an intentional denied store to a protected DRAM address, and verifying that the access raises the expected store access fault with the faulting address recorded in tval. Also print the captured trap cause and trap value so the negative-path result is visible in the runtime log. Signed-off-by: Raymond Mao --- include/sbi/sbi_hwiso_test.h | 2 ++ lib/sbi/sbi_hwiso_test.c | 6 ++++ lib/sbi/sbi_hwiso_testlib.c | 11 ++++++ .../generic/virt/qemu_virt_wgchecker_test.c | 35 +++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/include/sbi/sbi_hwiso_test.h b/include/sbi/sbi_hwiso_test.h index 3970df5a..354e8d17 100644 --- a/include/sbi/sbi_hwiso_test.h +++ b/include/sbi/sbi_hwiso_test.h @@ -15,6 +15,7 @@ #ifdef CONFIG_SBIUNIT struct sbi_hwiso_test_ops { void (*boot_test)(struct sbiunit_test_case *test); + void (*failure_test)(struct sbiunit_test_case *test); void (*domain_state_test)(struct sbiunit_test_case *test, const struct sbi_domain *dom, void *ctx); void (*domain_quiesce_test)(struct sbiunit_test_case *test, @@ -24,6 +25,7 @@ struct sbi_hwiso_test_ops { int sbi_hwiso_test_register(const struct sbi_hwiso_ops *ops, const struct sbi_hwiso_test_ops *test_ops); void sbi_hwiso_test_boot(struct sbiunit_test_case *test); +void sbi_hwiso_test_failure(struct sbiunit_test_case *test); void sbi_hwiso_test_domain_state(struct sbiunit_test_case *test, const struct sbi_domain *dom); void sbi_hwiso_test_domain_quiesced(struct sbiunit_test_case *test, diff --git a/lib/sbi/sbi_hwiso_test.c b/lib/sbi/sbi_hwiso_test.c index 716596e2..6b8384d4 100644 --- a/lib/sbi/sbi_hwiso_test.c +++ b/lib/sbi/sbi_hwiso_test.c @@ -96,6 +96,11 @@ static void hwiso_boot_test(struct sbiunit_test_case *test) sbi_hwiso_test_boot(test); } +static void hwiso_failure_mode_test(struct sbiunit_test_case *test) +{ + sbi_hwiso_test_failure(test); +} + static void hwiso_domain_switch_test(struct sbiunit_test_case *test) { struct sbi_domain *cur_dom = sbi_domain_thishart_ptr(); @@ -160,6 +165,7 @@ out: static struct sbiunit_test_case hwiso_test_cases[] = { SBIUNIT_TEST_CASE(hwiso_boot_test), + SBIUNIT_TEST_CASE(hwiso_failure_mode_test), SBIUNIT_TEST_CASE(hwiso_domain_switch_test), SBIUNIT_END_CASE, }; diff --git a/lib/sbi/sbi_hwiso_testlib.c b/lib/sbi/sbi_hwiso_testlib.c index 2a988088..2dfb8646 100644 --- a/lib/sbi/sbi_hwiso_testlib.c +++ b/lib/sbi/sbi_hwiso_testlib.c @@ -64,6 +64,17 @@ void sbi_hwiso_test_boot(struct sbiunit_test_case *test) } } +void sbi_hwiso_test_failure(struct sbiunit_test_case *test) +{ + struct sbi_hwiso_test_node *entry; + + sbi_list_for_each_entry(entry, &hwiso_test_ops_list, node) { + if (!entry->test_ops->failure_test) + continue; + entry->test_ops->failure_test(test); + } +} + void sbi_hwiso_test_domain_state(struct sbiunit_test_case *test, const struct sbi_domain *dom) { diff --git a/platform/generic/virt/qemu_virt_wgchecker_test.c b/platform/generic/virt/qemu_virt_wgchecker_test.c index 0eeaa3a0..eaaa39f7 100644 --- a/platform/generic/virt/qemu_virt_wgchecker_test.c +++ b/platform/generic/virt/qemu_virt_wgchecker_test.c @@ -7,14 +7,17 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#include #include #include #include @@ -40,6 +43,8 @@ struct qemu_virt_wg_expect { u32 slwid; }; +#define QEMU_VIRT_WG_DENIED_STORE_ADDR 0xc0001000UL + static u64 qemu_virt_wg_mmio_read64(unsigned long addr) { #if __riscv_xlen != 32 @@ -314,8 +319,38 @@ static void qemu_virt_wg_boot_test(struct sbiunit_test_case *test) 0); } +static void qemu_virt_wg_failure_test(struct sbiunit_test_case *test) +{ + struct sbi_domain *cur_dom = sbi_domain_thishart_ptr(); + struct sbi_domain *dom0 = qemu_virt_wg_find_domain("domain at 0"); + struct sbi_trap_info trap = { 0 }; + + SBIUNIT_ASSERT_NE(test, cur_dom, NULL); + SBIUNIT_ASSERT_NE(test, dom0, NULL); + + if (cur_dom != dom0) { + sbi_hwiso_domain_exit(cur_dom, dom0); + sbi_hwiso_domain_enter(dom0, cur_dom); + } + + sbi_store_u32((u32 *)QEMU_VIRT_WG_DENIED_STORE_ADDR, 0x5a5aa5a5U, + &trap); + + if (cur_dom != dom0) { + sbi_hwiso_domain_exit(dom0, cur_dom); + sbi_hwiso_domain_enter(cur_dom, dom0); + } + + sbi_printf("[WG TEST] failure trap cause=0x%lx tval=0x%lx\n", + trap.cause, trap.tval); + + SBIUNIT_ASSERT_EQ(test, trap.cause, CAUSE_STORE_ACCESS); + SBIUNIT_ASSERT_EQ(test, trap.tval, QEMU_VIRT_WG_DENIED_STORE_ADDR); +} + const struct sbi_hwiso_test_ops qemu_virt_worldguard_test_ops = { .boot_test = qemu_virt_wg_boot_test, + .failure_test = qemu_virt_wg_failure_test, .domain_state_test = qemu_virt_wg_assert_state, .domain_quiesce_test = qemu_virt_wg_assert_quiesced, }; -- 2.25.1 From evvoevod at tenstorrent.com Tue May 19 15:50:14 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Tue, 19 May 2026 22:50:14 +0000 Subject: [PATCH] lib: sbi: Apply budget restriction when polling Zkr CSR state transition Message-ID: <20260519225014.244672-1-evvoevod@tenstorrent.com> Zkr architecture doesn't define a time limit on state transitions which results in hanging on unresponsive or event-driven platforms. To prevent this, we need to limit polling iterations and fall back in case the budget is over, and stack guard keeps its initial value. The budget is configurable with CONFIG_SBI_INIT_ZKR_POLL_BUDGET, defaulting to 1000 iterations. Successful reads do not consume a try. Signed-off-by: Evgeny Voevodin --- lib/sbi/Kconfig | 12 ++++++++++++ lib/sbi/sbi_init.c | 12 +++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig index c6cc04bc..a11f788c 100644 --- a/lib/sbi/Kconfig +++ b/lib/sbi/Kconfig @@ -6,6 +6,18 @@ config CONSOLE_EARLY_BUFFER_SIZE int "Early console buffer size (bytes)" default 256 +config SBI_INIT_ZKR_POLL_BUDGET + int "Zkr seed polling budget (iterations)" + default 1000 + help + Maximum number of iterations to poll CSR_SEED when initializing + the stack guard variable. The Zkr specification doesn't define + a time limit on transitioning to ES16 between polls, which + makes it impossible to tell whether entropy is being + accumulated slowly or the entropy source is not functioning. + This also limits the wait time on systems with an event-driven + entropy source. A successful read doesn't consume a try. + config SBI_ECALL_TIME bool "Timer extension" default y diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c index b248e73f..7a0c4f74 100644 --- a/lib/sbi/sbi_init.c +++ b/lib/sbi/sbi_init.c @@ -280,20 +280,26 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) if (sbi_hart_has_extension(scratch, SBI_HART_EXT_ZKR)) { unsigned long guard_val = 0; int chunks = sizeof(unsigned long) / sizeof(uint16_t); - bool res = true; + unsigned int tries = CONFIG_SBI_INIT_ZKR_POLL_BUDGET; + bool res = false; - while (chunks) { + while (chunks && tries) { unsigned long seed = csr_swap(CSR_SEED, 0); unsigned long opst = seed & SEED_OPTS_MASK; + res = false; if (opst == SEED_OPTS_DEAD) { - res = false; break; } if (opst == SEED_OPTS_ES16) { guard_val = (guard_val << 16) | (seed & SEED_ENTROPY_MASK); chunks--; + res = true; + /* Successful read doesn't consume a try */ + tries++; } + + tries--; continue; } if (res) -- 2.43.0 From himanshu.chauhan at oss.qualcomm.com Wed May 20 22:28:38 2026 From: himanshu.chauhan at oss.qualcomm.com (Himanshu Chauhan) Date: Thu, 21 May 2026 10:58:38 +0530 Subject: [PATCH] sbi: mpxy: define INVALID_ADDR using unsigned long width Message-ID: <20260521052838.2174588-1-himanshu.chauhan@oss.qualcomm.com> INVALID_ADDR is used as an all-ones physical address sentinel. Using -1U only guarantees 32-bit width, so on platforms where unsigned long is wider it may not expand to all ones after assignment. Use -1ULL so the conversion to unsigned long preserves an all-ones bit pattern across supported widths. Fixes: 7939bf1329eb ("lib: sbi: Add SBI Message Proxy (MPXY) framework") Signed-off-by: Himanshu Chauhan --- lib/sbi/sbi_mpxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sbi/sbi_mpxy.c b/lib/sbi/sbi_mpxy.c index a83cf16c..79851097 100644 --- a/lib/sbi/sbi_mpxy.c +++ b/lib/sbi/sbi_mpxy.c @@ -28,7 +28,7 @@ static unsigned long mpxy_shmem_size = PAGE_SIZE; static SBI_LIST_HEAD(mpxy_channel_list); /** Invalid Physical Address(all bits 1) */ -#define INVALID_ADDR (-1U) +#define INVALID_ADDR (-1ULL) /** MPXY Attribute size in bytes */ #define ATTR_SIZE (4) -- 2.43.0 From anup.patel at oss.qualcomm.com Thu May 21 01:26:24 2026 From: anup.patel at oss.qualcomm.com (Anup Patel) Date: Thu, 21 May 2026 13:56:24 +0530 Subject: [PATCH 1/2] lib: sbi_hart: No need to clear features in hart_detect_features() In-Reply-To: <20260521082625.1520870-1-anup.patel@oss.qualcomm.com> References: <20260521082625.1520870-1-anup.patel@oss.qualcomm.com> Message-ID: <20260521082625.1520870-2-anup.patel@oss.qualcomm.com> The per-hart features are already zeroed by sbi_scratch_alloc_offset() for all harts so hart_detect_features() should not explicitly clear features later. Signed-off-by: Anup Patel --- lib/sbi/sbi_hart.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 0f690f53..e7360712 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -520,13 +520,6 @@ static int hart_detect_features(struct sbi_scratch *scratch) if (hfeatures->detected) return 0; - /* Clear hart features */ - sbi_memset(hfeatures->extensions, 0, sizeof(hfeatures->extensions)); - sbi_memset(hfeatures->csrs, 0, sizeof(hfeatures->csrs)); - hfeatures->pmp_count = 0; - hfeatures->mhpm_mask = 0; - hfeatures->priv_version = SBI_HART_PRIV_VER_UNKNOWN; - /* * Parse device tree extensions early, before any trap-based checks. * Needed to detect Smrnmi and install NMI handlers before CSR probes -- 2.43.0 From anup.patel at oss.qualcomm.com Thu May 21 01:26:25 2026 From: anup.patel at oss.qualcomm.com (Anup Patel) Date: Thu, 21 May 2026 13:56:25 +0530 Subject: [PATCH 2/2] platform: generic: Optimize extensions_init() to parse ISA extensions once In-Reply-To: <20260521082625.1520870-1-anup.patel@oss.qualcomm.com> References: <20260521082625.1520870-1-anup.patel@oss.qualcomm.com> Message-ID: <20260521082625.1520870-3-anup.patel@oss.qualcomm.com> Instead of parsing ISA extensions separately for each hart in the generic_extensions_init() function, it is better to parse ISA extensions for all available harts in the cold boot path. Also, this allows us to remove fdt_isa_bitmap from scratch space and directly initialize "extensions" in struct sbi_hart_features for each hart. Signed-off-by: Anup Patel --- include/sbi/sbi_platform.h | 7 ++- include/sbi_utils/fdt/fdt_helper.h | 3 +- lib/sbi/sbi_hart.c | 7 ++- lib/utils/fdt/fdt_helper.c | 47 ++++---------------- platform/generic/allwinner/sun20i-d1.c | 7 ++- platform/generic/andes/andes_pmu.c | 5 ++- platform/generic/eswin/eic770x.c | 1 + platform/generic/include/andes/andes_pmu.h | 4 +- platform/generic/include/platform_override.h | 3 +- platform/generic/platform.c | 8 ++-- platform/generic/sophgo/sg2042.c | 4 +- platform/generic/starfive/jh7110.c | 1 + platform/generic/thead/thead-generic.c | 4 +- 13 files changed, 37 insertions(+), 64 deletions(-) diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index fe382b56..7a4f9242 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -55,7 +55,6 @@ struct sbi_domain_memregion; struct sbi_ecall_return; struct sbi_trap_regs; -struct sbi_hart_features; union sbi_ldst_data; /** Possible feature flags of a platform */ @@ -105,7 +104,7 @@ struct sbi_platform_operations { int (*misa_get_xlen)(void); /** Initialize (or populate) HART extensions for the platform */ - int (*extensions_init)(struct sbi_hart_features *hfeatures); + int (*extensions_init)(bool cold_boot); /** Initialize (or populate) domains for the platform */ int (*domains_init)(void); @@ -486,10 +485,10 @@ static inline int sbi_platform_misa_xlen(const struct sbi_platform *plat) */ static inline int sbi_platform_extensions_init( const struct sbi_platform *plat, - struct sbi_hart_features *hfeatures) + bool cold_boot) { if (plat && sbi_platform_ops(plat)->extensions_init) - return sbi_platform_ops(plat)->extensions_init(hfeatures); + return sbi_platform_ops(plat)->extensions_init(cold_boot); return 0; } diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h index 04c850cc..75a564d1 100644 --- a/include/sbi_utils/fdt/fdt_helper.h +++ b/include/sbi_utils/fdt/fdt_helper.h @@ -54,8 +54,7 @@ int fdt_parse_cbom_block_size(const void *fdt, int cpu_offset, unsigned long *c int fdt_parse_timebase_frequency(const void *fdt, unsigned long *freq); -int fdt_parse_isa_extensions(const void *fdt, unsigned int hartid, - unsigned long *extensions); +int fdt_parse_isa_extensions_all_harts(const void *fdt); int fdt_parse_gaisler_uart_node(const void *fdt, int nodeoffset, struct platform_uart_data *uart); diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index e7360712..21713816 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -508,7 +508,7 @@ static int hart_mhpm_get_allowed_bits(void) return num_bits; } -static int hart_detect_features(struct sbi_scratch *scratch) +static int hart_detect_features(struct sbi_scratch *scratch, bool cold_boot) { struct sbi_trap_info trap = {0}; struct sbi_hart_features *hfeatures = @@ -525,8 +525,7 @@ static int hart_detect_features(struct sbi_scratch *scratch) * Needed to detect Smrnmi and install NMI handlers before CSR probes * that may trigger traps. */ - rc = sbi_platform_extensions_init(sbi_platform_thishart_ptr(), - hfeatures); + rc = sbi_platform_extensions_init(sbi_platform_ptr(scratch), cold_boot); if (rc) return rc; @@ -764,7 +763,7 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot) return SBI_ENOMEM; } - rc = hart_detect_features(scratch); + rc = hart_detect_features(scratch, cold_boot); if (rc) return rc; diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c index b57eae1a..ad4efaaf 100644 --- a/lib/utils/fdt/fdt_helper.c +++ b/lib/utils/fdt/fdt_helper.c @@ -328,8 +328,6 @@ int fdt_parse_timebase_frequency(const void *fdt, unsigned long *freq) #define RISCV_ISA_EXT_NAME_LEN_MAX 32 -static unsigned long fdt_isa_bitmap_offset; - static int fdt_parse_isa_one_hart(const char *isa, unsigned long *extensions) { size_t i, j, isa_len; @@ -409,15 +407,15 @@ static void fdt_parse_isa_extensions_one_hart(const char *isa, } } -static int fdt_parse_isa_all_harts(const void *fdt) +int fdt_parse_isa_extensions_all_harts(const void *fdt) { u32 hartid; const fdt32_t *val; - unsigned long *hart_exts; struct sbi_scratch *scratch; + struct sbi_hart_features *hfeatures; int err, cpu_offset, cpus_offset, len; - if (!fdt || !fdt_isa_bitmap_offset) + if (!fdt) return SBI_EINVAL; cpus_offset = fdt_path_offset(fdt, "/cpus"); @@ -436,13 +434,14 @@ static int fdt_parse_isa_all_harts(const void *fdt) if (!scratch) return SBI_ENOENT; - hart_exts = sbi_scratch_offset_ptr(scratch, - fdt_isa_bitmap_offset); + hfeatures = sbi_hart_features_ptr(scratch); + if (!hfeatures) + return SBI_ENOENT; val = fdt_getprop(fdt, cpu_offset, "riscv,isa-extensions", &len); if (val && len > 0) { fdt_parse_isa_extensions_one_hart((const char *)val, - hart_exts, len); + hfeatures->extensions, len); continue; } @@ -450,7 +449,7 @@ static int fdt_parse_isa_all_harts(const void *fdt) if (!val || len <= 0) return SBI_ENOENT; - err = fdt_parse_isa_one_hart((const char *)val, hart_exts); + err = fdt_parse_isa_one_hart((const char *)val, hfeatures->extensions); if (err) return err; } @@ -458,36 +457,6 @@ static int fdt_parse_isa_all_harts(const void *fdt) return 0; } -int fdt_parse_isa_extensions(const void *fdt, unsigned int hartid, - unsigned long *extensions) -{ - int rc, i; - unsigned long *hart_exts; - struct sbi_scratch *scratch; - - if (!fdt_isa_bitmap_offset) { - fdt_isa_bitmap_offset = sbi_scratch_alloc_offset( - sizeof(*hart_exts) * - BITS_TO_LONGS(SBI_HART_EXT_MAX)); - if (!fdt_isa_bitmap_offset) - return SBI_ENOMEM; - - rc = fdt_parse_isa_all_harts(fdt); - if (rc) - return rc; - } - - scratch = sbi_hartid_to_scratch(hartid); - if (!scratch) - return SBI_ENOENT; - - hart_exts = sbi_scratch_offset_ptr(scratch, fdt_isa_bitmap_offset); - - for (i = 0; i < BITS_TO_LONGS(SBI_HART_EXT_MAX); i++) - extensions[i] |= hart_exts[i]; - return 0; -} - static int fdt_parse_uart_node_common(const void *fdt, int nodeoffset, struct platform_uart_data *uart, unsigned long default_freq, diff --git a/platform/generic/allwinner/sun20i-d1.c b/platform/generic/allwinner/sun20i-d1.c index 666473c1..4ea90158 100644 --- a/platform/generic/allwinner/sun20i-d1.c +++ b/platform/generic/allwinner/sun20i-d1.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -197,11 +198,12 @@ static int sun20i_d1_final_init(bool cold_boot) return generic_final_init(cold_boot); } -static int sun20i_d1_extensions_init(struct sbi_hart_features *hfeatures) +static int sun20i_d1_extensions_init(bool cold_boot) { + struct sbi_hart_features *hfeatures; int rc; - rc = generic_extensions_init(hfeatures); + rc = generic_extensions_init(cold_boot); if (rc) return rc; @@ -209,6 +211,7 @@ static int sun20i_d1_extensions_init(struct sbi_hart_features *hfeatures) /* auto-detection doesn't work on t-head c9xx cores */ /* D1 has 29 mhpmevent csrs, but only 3-9,13-17 have valid value */ + hfeatures = sbi_hart_features_ptr(sbi_scratch_thishart_ptr()); hfeatures->mhpm_mask = 0x0003e3f8; hfeatures->mhpm_bits = 64; diff --git a/platform/generic/andes/andes_pmu.c b/platform/generic/andes/andes_pmu.c index 9eee4edc..b3e49e8d 100644 --- a/platform/generic/andes/andes_pmu.c +++ b/platform/generic/andes/andes_pmu.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -58,12 +59,12 @@ static struct sbi_pmu_device andes_pmu = { .hw_counter_filter_mode = andes_hw_counter_filter_mode }; -int andes_pmu_extensions_init(struct sbi_hart_features *hfeatures) +int andes_pmu_extensions_init(bool cold_boot) { struct sbi_scratch *scratch = sbi_scratch_thishart_ptr(); int rc; - rc = generic_extensions_init(hfeatures); + rc = generic_extensions_init(cold_boot); if (rc) return rc; diff --git a/platform/generic/eswin/eic770x.c b/platform/generic/eswin/eic770x.c index 7330df9f..60d28c51 100644 --- a/platform/generic/eswin/eic770x.c +++ b/platform/generic/eswin/eic770x.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/platform/generic/include/andes/andes_pmu.h b/platform/generic/include/andes/andes_pmu.h index ad4564a6..e4446364 100644 --- a/platform/generic/include/andes/andes_pmu.h +++ b/platform/generic/include/andes/andes_pmu.h @@ -7,9 +7,9 @@ #ifndef _RISCV_ANDES_PMU_H #define _RISCV_ANDES_PMU_H -#include +#include int andes_pmu_init(void); -int andes_pmu_extensions_init(struct sbi_hart_features *hfeatures); +int andes_pmu_extensions_init(bool cold_boot); #endif /* _RISCV_ANDES_PMU_H */ diff --git a/platform/generic/include/platform_override.h b/platform/generic/include/platform_override.h index 3e6e5044..1b8127cb 100644 --- a/platform/generic/include/platform_override.h +++ b/platform/generic/include/platform_override.h @@ -10,7 +10,6 @@ #ifndef __PLATFORM_OVERRIDE_H__ #define __PLATFORM_OVERRIDE_H__ -#include #include #include #include @@ -19,7 +18,7 @@ bool generic_cold_boot_allowed(u32 hartid); int generic_nascent_init(void); int generic_early_init(bool cold_boot); int generic_final_init(bool cold_boot); -int generic_extensions_init(struct sbi_hart_features *hfeatures); +int generic_extensions_init(bool cold_boot); int generic_domains_init(void); int generic_pmu_init(void); uint64_t generic_pmu_xlate_to_mhpmevent(uint32_t event_idx, uint64_t data); diff --git a/platform/generic/platform.c b/platform/generic/platform.c index 01fa03e0..1df0280d 100644 --- a/platform/generic/platform.c +++ b/platform/generic/platform.c @@ -253,11 +253,13 @@ int generic_final_init(bool cold_boot) return 0; } -int generic_extensions_init(struct sbi_hart_features *hfeatures) +int generic_extensions_init(bool cold_boot) { + if (!cold_boot) + return 0; + /* Parse the ISA string from FDT and enable the listed extensions */ - return fdt_parse_isa_extensions(fdt_get_address(), current_hartid(), - hfeatures->extensions); + return fdt_parse_isa_extensions_all_harts(fdt_get_address()); } int generic_domains_init(void) diff --git a/platform/generic/sophgo/sg2042.c b/platform/generic/sophgo/sg2042.c index c2447c3c..ac8840e8 100644 --- a/platform/generic/sophgo/sg2042.c +++ b/platform/generic/sophgo/sg2042.c @@ -49,11 +49,11 @@ static int sophgo_sg2042_early_init(bool cold_boot) return 0; } -static int sophgo_sg2042_extensions_init(struct sbi_hart_features *hfeatures) +static int sophgo_sg2042_extensions_init(bool cold_boot) { int rc; - rc = generic_extensions_init(hfeatures); + rc = generic_extensions_init(cold_boot); if (rc) return rc; diff --git a/platform/generic/starfive/jh7110.c b/platform/generic/starfive/jh7110.c index c1328437..7008386a 100644 --- a/platform/generic/starfive/jh7110.c +++ b/platform/generic/starfive/jh7110.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/platform/generic/thead/thead-generic.c b/platform/generic/thead/thead-generic.c index ddb4f0bf..fef0871b 100644 --- a/platform/generic/thead/thead-generic.c +++ b/platform/generic/thead/thead-generic.c @@ -26,11 +26,11 @@ static int thead_tlb_flush_early_init(bool cold_boot) return generic_early_init(cold_boot); } -static int thead_pmu_extensions_init(struct sbi_hart_features *hfeatures) +static int thead_pmu_extensions_init(bool cold_boot) { int rc; - rc = generic_extensions_init(hfeatures); + rc = generic_extensions_init(cold_boot); if (rc) return rc; -- 2.43.0 From anup.patel at oss.qualcomm.com Thu May 21 01:26:23 2026 From: anup.patel at oss.qualcomm.com (Anup Patel) Date: Thu, 21 May 2026 13:56:23 +0530 Subject: [PATCH 0/2] Minor improvement in FDT extension parsing Message-ID: <20260521082625.1520870-1-anup.patel@oss.qualcomm.com> The FDT extension parsing can be improved by parsing ISA extensions of all harts in the cold boot path. This also saves some part of scratch space which was used by the fdt_isa_bitmap. These patches can also be found in platform_extensions_init_v1 branch at: https://github.com/avpatel/opensbi.git Anup Patel (2): lib: sbi_hart: No need to clear features in hart_detect_features() platform: generic: Optimize extensions_init() to parse ISA extensions once include/sbi/sbi_platform.h | 7 ++- include/sbi_utils/fdt/fdt_helper.h | 3 +- lib/sbi/sbi_hart.c | 14 ++---- lib/utils/fdt/fdt_helper.c | 47 ++++---------------- platform/generic/allwinner/sun20i-d1.c | 7 ++- platform/generic/andes/andes_pmu.c | 5 ++- platform/generic/eswin/eic770x.c | 1 + platform/generic/include/andes/andes_pmu.h | 4 +- platform/generic/include/platform_override.h | 3 +- platform/generic/platform.c | 8 ++-- platform/generic/sophgo/sg2042.c | 4 +- platform/generic/starfive/jh7110.c | 1 + platform/generic/thead/thead-generic.c | 4 +- 13 files changed, 37 insertions(+), 71 deletions(-) -- 2.43.0 From wangruikang at iscas.ac.cn Thu May 21 05:18:29 2026 From: wangruikang at iscas.ac.cn (Vivian Wang) Date: Thu, 21 May 2026 20:18:29 +0800 Subject: [PATCH v2] riscv: misaligned: Make enabling delegation depend on NONPORTABLE In-Reply-To: References: <20260401-riscv-misaligned-dont-delegate-v2-1-5014a288c097@iscas.ac.cn> Message-ID: <825c93ad-bb68-443a-8c0a-9c5e62e08ac0@iscas.ac.cn> [ Anup, Bo, OpenSBI list: It's the already reported issue where SBI_PLATFORM_DEFAULT_HART_STACK_SIZE is not enough for sbi_misaligned_v_ld_emulator. How should we approach this? ] On 5/21/26 19:49, Michael Ellerman wrote: > On 21/5/2026 10:27, Vivian Wang wrote: >> >> On 5/20/26 23:47, Anirudh Srinivasan wrote: >>> Hi Vivian, Paul >>> >>> On Wed, Apr 01, 2026 at 09:53:17AM +0800, Vivian Wang wrote: >>>> The unaligned access emulation code in Linux has various deficiencies. >>>> For example, it doesn't emulate vector instructions [1] [2], and >>>> doesn't >>>> emulate KVM guest accesses. Therefore, requesting misaligned exception >>>> delegation with SBI FWFT actually regresses vector instructions' >>>> and KVM >>>> guests' behavior. >>>> >>>> Until Linux can handle it properly, guard these sbi_fwft_set() calls >>>> behind RISCV_SBI_FWFT_DELEGATE_MISALIGNED, which in turn depends on >>>> NONPORTABLE. Those who are sure that this wouldn't be a problem can >>>> enable this option, perhaps getting better performance. >>>> >>>> The rest of the existing code proceeds as before, except as if >>>> SBI_FWFT_MISALIGNED_EXC_DELEG is not available, to handle any >>>> remaining >>>> address misaligned exceptions on a best-effort basis. The KVM SBI FWFT >>>> implementation is also not touched, but it is disabled if the firmware >>>> emulates unaligned accesses. >>> On a Tenstorrent Blackhole with SiFive x280 cores, with OpenSBI 1.7 and >>> defconfig kernel, I'm seeing a bunch of hangs/opensbi prints at boot >>> time. >>> Without this patch, the boot prints this and continues on. >>> >>> [??? 0.226339] SBI misaligned access exception delegation ok >> >> Your OpenSBI looks very broken (more on what I mean later), and in a way >> that might only manifest if it's trying to emulate vector misaligned >> instructions? An interesting thing I can think of is maybe your SiFive >> x280 has a very long VLEN (512? 1024? I forgot) which may have exposed >> some stuff... > > It's 512. > >> I have two ideas: >> >> Firstly, try bumping this in include/sbi/sbi_platform.h up to 65536 or >> something like that. If that works you can also start trying to lower it >> to 16384 or something similar. >> >> #define SBI_PLATFORM_DEFAULT_HART_STACK_SIZE? ? 8192 > > Yep, bumping that to 16384 fixes it for me. > Thanks for confirming. I've already told Greg to drop the stable backport [2]. I still think that's the right thing to do - users on older stable kernels don't need to get surprised by this. > The culprit is in lib/sbi/sbi_trap_v_ldst.c: > > #define VLEN_MAX 65536 > ... > > int sbi_misaligned_v_ld_emulator(int rlen, union sbi_ldst_data *out_val, > ???????????????????????????????? struct sbi_trap_context *tcntx) > { > ??????? const struct sbi_trap_info *orig_trap = &tcntx->trap; > ... > ??????? uint8_t mask[VLEN_MAX / 8]; > > > ie. 8KB on stack buffer. Yes, thanks. That's the one I had in mind but forgot the exact location of. > > Shrinking VLEN_MAX to 4096 also gets it booting. But I guess that's > not viable because in theory someone might build a chip with VLEN > 65536 one day? > > Would a heap allocation at boot be better? > Allocation on boot would probably be better. Or maybe there's some way to refactor it to not use so much stack. > Or just force the stack to be bigger when vector is enabled, eg: > > [...] Possibly. I'm not sure. "CC_SUPPORTS" sounds wrong. I haven't dealt with OpenSBI for a good while so I don't have much of an idea about what should be done here. Bo Gan's thread on the OpenSBI list [1] is probably a better place to talk about this. Or maybe we should send a patch to bump the stack size first and try to deal with it later.? Vivian "dramforever" Wang [1]: https://lore.kernel.org/opensbi/20260210094044.72591-1-ganboing at gmail.com/ [2]: https://lore.kernel.org/stable/99c8c715-b37f-4f2a-8100-5ea4970ff34d at iscas.ac.cn/ From anup at brainfault.org Thu May 21 21:59:10 2026 From: anup at brainfault.org (Anup Patel) Date: Fri, 22 May 2026 10:29:10 +0530 Subject: [PATCH] lib: utils/reset: Remove unused match data In-Reply-To: <20260513224414.1078791-1-samuel.holland@sifive.com> References: <20260513224414.1078791-1-samuel.holland@sifive.com> Message-ID: On Thu, May 14, 2026 at 4:14?AM Samuel Holland wrote: > > Some drivers inherited FDT match data from the GPIO/syscon reset > drivers, but do not use it for anything. Remove it to avoid confusion. > > Signed-off-by: Samuel Holland LGTM. Reviewed-by: Anup Patel Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > > lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c | 2 +- > lib/utils/reset/fdt_reset_spacemit_p1.c | 2 +- > 2 files changed, 2 insertions(+), 2 deletions(-) > > diff --git a/lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c b/lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c > index 94ac4162..bb180268 100644 > --- a/lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c > +++ b/lib/utils/reset/fdt_reset_sg2042_hwmon_mcu.c > @@ -104,7 +104,7 @@ static int sg2042_mcu_reset_init(const void *fdt, int nodeoff, > } > > static const struct fdt_match sg2042_mcu_reset_match[] = { > - { .compatible = "sophgo,sg2042-hwmon-mcu", .data = (void *)true}, > + { .compatible = "sophgo,sg2042-hwmon-mcu" }, > { }, > }; > > diff --git a/lib/utils/reset/fdt_reset_spacemit_p1.c b/lib/utils/reset/fdt_reset_spacemit_p1.c > index 5312e741..d153f68b 100644 > --- a/lib/utils/reset/fdt_reset_spacemit_p1.c > +++ b/lib/utils/reset/fdt_reset_spacemit_p1.c > @@ -102,7 +102,7 @@ static int p1_reset_init(const void *fdt, int nodeoff, > } > > static const struct fdt_match p1_reset_match[] = { > - { .compatible = "spacemit,p1", .data = (void *)true }, > + { .compatible = "spacemit,p1" }, > { }, > }; > > -- > 2.52.0 > > base-commit: 79e63bc8348df12835b5c264144e50d45abe72c8 > branch: up/cleanup-reset > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From apatel at ventanamicro.com Thu May 21 21:59:59 2026 From: apatel at ventanamicro.com (Anup Patel) Date: Fri, 22 May 2026 10:29:59 +0530 Subject: [PATCH] sbi: mpxy: define INVALID_ADDR using unsigned long width In-Reply-To: <20260521052838.2174588-1-himanshu.chauhan@oss.qualcomm.com> References: <20260521052838.2174588-1-himanshu.chauhan@oss.qualcomm.com> Message-ID: On Thu, May 21, 2026 at 10:59?AM Himanshu Chauhan wrote: > > INVALID_ADDR is used as an all-ones physical address sentinel. > Using -1U only guarantees 32-bit width, so on platforms where > unsigned long is wider it may not expand to all ones after assignment. > > Use -1ULL so the conversion to unsigned long preserves an all-ones > bit pattern across supported widths. > > Fixes: 7939bf1329eb ("lib: sbi: Add SBI Message Proxy (MPXY) framework") > Signed-off-by: Himanshu Chauhan This is critical fix hence merging it quickly. Reviewed-by: Anup Patel Applied this patch to the riscv/opensbi repo. Thanks, Anup > --- > lib/sbi/sbi_mpxy.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/lib/sbi/sbi_mpxy.c b/lib/sbi/sbi_mpxy.c > index a83cf16c..79851097 100644 > --- a/lib/sbi/sbi_mpxy.c > +++ b/lib/sbi/sbi_mpxy.c > @@ -28,7 +28,7 @@ static unsigned long mpxy_shmem_size = PAGE_SIZE; > static SBI_LIST_HEAD(mpxy_channel_list); > > /** Invalid Physical Address(all bits 1) */ > -#define INVALID_ADDR (-1U) > +#define INVALID_ADDR (-1ULL) > > /** MPXY Attribute size in bytes */ > #define ATTR_SIZE (4) > -- > 2.43.0 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From david.garcia at aheadcomputing.com Fri May 22 07:46:07 2026 From: david.garcia at aheadcomputing.com (David E. Garcia Porras) Date: Fri, 22 May 2026 08:46:07 -0600 Subject: [PATCH] lib: sbi_pmu: Honor CLEAR_VALUE/AUTO_START for all hardware event types Message-ID: <20260522144608.3433470-1-david.garcia@aheadcomputing.com> sbi_pmu_ctr_cfg_match() only acts on SBI_PMU_CFG_FLAG_CLEAR_VALUE and SBI_PMU_CFG_FLAG_AUTO_START when the event type is SBI_PMU_EVENT_TYPE_HW. However, pmu_ctr_find_hw() allocates a hardware counter from the same hw_event_map for SBI_PMU_EVENT_TYPE_HW_CACHE, SBI_PMU_EVENT_TYPE_HW_RAW, and SBI_PMU_EVENT_TYPE_HW_RAW_V2 as well, and the start/clear helpers (pmu_ctr_start_hw, pmu_ctr_write_hw) operate on the counter index alone and are agnostic to the event type. As a result, when a supervisor configures a HW_CACHE/HW_RAW/HW_RAW_V2 event with these flags, the counter is programmed and recorded in active_events[] but is never cleared or started, requiring an extra SBI call to make it count. Extend the check to cover all hardware-counter event types so that the configuration flags take effect for HW_CACHE and raw events too. Deliberately avoiding using "not FW" logic to be explicit about HW-backed events only. Fixes: 13d40f21 ("lib: sbi: Add PMU support") Signed-off-by: David E. Garcia Porras --- lib/sbi/sbi_pmu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c index 8a9021e2..480a9723 100644 --- a/lib/sbi/sbi_pmu.c +++ b/lib/sbi/sbi_pmu.c @@ -946,7 +946,10 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask, phs->active_events[ctr_idx] = event_idx; skip_match: - if (event_type == SBI_PMU_EVENT_TYPE_HW) { + if (event_type == SBI_PMU_EVENT_TYPE_HW || + event_type == SBI_PMU_EVENT_TYPE_HW_CACHE || + event_type == SBI_PMU_EVENT_TYPE_HW_RAW || + event_type == SBI_PMU_EVENT_TYPE_HW_RAW_V2) { if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE) pmu_ctr_write_hw(ctr_idx, 0); if (flags & SBI_PMU_CFG_FLAG_AUTO_START) -- 2.43.0 From asrinivasan at oss.tenstorrent.com Fri May 22 15:54:17 2026 From: asrinivasan at oss.tenstorrent.com (Anirudh Srinivasan) Date: Fri, 22 May 2026 17:54:17 -0500 Subject: [PATCH] lib: sbi: Drop fw_rw_offset alignment requirement for single fw region Message-ID: <20260522-fw_rw_start_alignment-v1-1-362c17331541@oss.tenstorrent.com> From: Nicholas Piggin In a single fw region scheme, there is no separate PMP created for RW memory. The checks that opensbi does for the alignment between fw_start and fw_rw_start (using fw_rw_offset) and the power of 2 check for fw_rw_offset are no longer necessary. Update sbi_domain_init so that these checks are only done in the non single fw region scheme. Signed-off-by: Nicholas Piggin Co-developed-by: Anirudh Srinivasan Signed-off-by: Anirudh Srinivasan --- lib/sbi/sbi_domain.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/sbi/sbi_domain.c b/lib/sbi/sbi_domain.c index 4f458ac0..296ee81b 100644 --- a/lib/sbi/sbi_domain.c +++ b/lib/sbi/sbi_domain.c @@ -871,20 +871,23 @@ int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid) struct sbi_hartmask *root_hmask; struct sbi_domain_memregion *root_memregs; int root_memregs_count = 0; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + bool fw_single_region = sbi_platform_single_fw_region(plat); SBI_INIT_LIST_HEAD(&domain_list); - if (scratch->fw_rw_offset == 0 || - (scratch->fw_rw_offset & (scratch->fw_rw_offset - 1)) != 0) { - sbi_printf("%s: fw_rw_offset is not a power of 2 (0x%lx)\n", - __func__, scratch->fw_rw_offset); - return SBI_EINVAL; - } - - if ((scratch->fw_start & (scratch->fw_rw_offset - 1)) != 0) { - sbi_printf("%s: fw_start and fw_rw_offset not aligned\n", - __func__); - return SBI_EINVAL; + if (!fw_single_region) { + if (scratch->fw_rw_offset == 0 || + (scratch->fw_rw_offset & (scratch->fw_rw_offset - 1)) != 0) { + sbi_printf("%s: fw_rw_offset is not a power of 2 (0x%lx)\n", + __func__, scratch->fw_rw_offset); + return SBI_EINVAL; + } + if ((scratch->fw_start & (scratch->fw_rw_offset - 1)) != 0) { + sbi_printf("%s: fw_start and fw_rw_offset not aligned\n", + __func__); + return SBI_EINVAL; + } } domain_hart_ptr_offset = sbi_scratch_alloc_type_offset(void *); @@ -913,7 +916,7 @@ int sbi_domain_init(struct sbi_scratch *scratch, u32 cold_hartid) root.possible_harts = root_hmask; /* Root domain firmware memory region */ - if (sbi_platform_single_fw_region(sbi_platform_ptr(scratch))) { + if (fw_single_region) { sbi_domain_memregion_init(scratch->fw_start, scratch->fw_size, (SBI_DOMAIN_MEMREGION_M_READABLE | SBI_DOMAIN_MEMREGION_M_WRITABLE | --- base-commit: e7fa66c2160ec139de1853a00f669c09320a9256 change-id: 20260522-fw_rw_start_alignment-44eaac884823 Best regards, -- Anirudh Srinivasan From wangruikang at iscas.ac.cn Sat May 23 22:53:32 2026 From: wangruikang at iscas.ac.cn (Vivian Wang) Date: Sun, 24 May 2026 13:53:32 +0800 Subject: [PATCH] include: sbi_platform: Increase default stack size Message-ID: sbi_misaligned_v_ld_emulator() has a local buffer (namely mask) which is 8192 bytes in size, so the default stack size is not enough. Double it to 16384. This stack overflow can be observed by any S-mode software and leads to M-mode crash or unexpected behavior, as long as the S-mode software does not enable FWFT misaligned delegation [1]. This may be considered a temporary fix until further fixes are made to misaligned handling [2]. Link: https://lore.kernel.org/linux-riscv/nrvt74qnojaubiwjo37ums4lnclu466hovwrhmtbag6f5uhrql at q6msoe2oto4b # [1] Link: https://lore.kernel.org/opensbi/20260210094044.72591-1-ganboing at gmail.com # [2] Signed-off-by: Vivian Wang --- include/sbi/sbi_platform.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index fe382b56..ec1e0f73 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -160,7 +160,7 @@ struct sbi_platform_operations { }; /** Platform default per-HART stack size for exception/interrupt handling */ -#define SBI_PLATFORM_DEFAULT_HART_STACK_SIZE 8192 +#define SBI_PLATFORM_DEFAULT_HART_STACK_SIZE 16384 /** Platform default heap size */ #define SBI_PLATFORM_DEFAULT_HEAP_SIZE(__num_hart) \ -- 2.54.0 From asrinivasan at oss.tenstorrent.com Mon May 25 06:23:57 2026 From: asrinivasan at oss.tenstorrent.com (Anirudh Srinivasan) Date: Mon, 25 May 2026 08:23:57 -0500 Subject: [PATCH] include: sbi_platform: Increase default stack size In-Reply-To: References: Message-ID: Hi Vivian, On Sun, May 24, 2026 at 12:53?AM Vivian Wang wrote: > > sbi_misaligned_v_ld_emulator() has a local buffer (namely mask) which is > 8192 bytes in size, so the default stack size is not enough. Double it > to 16384. > > This stack overflow can be observed by any S-mode software and leads to > M-mode crash or unexpected behavior, as long as the S-mode software does > not enable FWFT misaligned delegation [1]. This may be considered a > temporary fix until further fixes are made to misaligned handling [2]. > > Link: https://lore.kernel.org/linux-riscv/nrvt74qnojaubiwjo37ums4lnclu466hovwrhmtbag6f5uhrql at q6msoe2oto4b # [1] > Link: https://lore.kernel.org/opensbi/20260210094044.72591-1-ganboing at gmail.com # [2] > Signed-off-by: Vivian Wang There was a similar patch [1] from few days ago that makes this a Kconfig option. Either option would work to fix the bug I encountered. Wondering what the maintainers think about how to proceed with this. [1] https://lore.kernel.org/opensbi/VI0P192MB3062735A6194BB6DA72083499E002 at VI0P192MB3062.EURP192.PROD.OUTLOOK.COM/T/#u Regards Anirudh Srinivasan From david.garcia at aheadcomputing.com Mon May 25 11:50:33 2026 From: david.garcia at aheadcomputing.com (David E. Garcia Porras) Date: Mon, 25 May 2026 12:50:33 -0600 Subject: [PATCH] lib: sbi: dbtr: do not unconditionally access tdata2/tdata3 CSRs Message-ID: <20260525185033.4165210-1-david.garcia@aheadcomputing.com> The current SBI DBTR extension implementation accesses tdata2 and tdata3 without first checking whether either register is implemented on the underlying hart. This produces an illegal instruction exception on otherwise spec-compliant cores that legitimately omit one or both registers. Per the RISC-V Debug Specification, Chapter 5 (Sdtrig ISA Extension) and Section 5.7 (Trigger Module Registers): Section 5 (Sdtrig introduction): "If Sdtrig is implemented, the Trigger Module must support at least one trigger. Accessing trigger CSRs that are not used by any of the implemented triggers must result in an illegal instruction exception. M-Mode and Debug Mode accesses to trigger CSRs that are used by any of the implemented triggers must succeed, regardless of the current type of the currently selected trigger." Section 5.7 (Trigger Module Registers): "Attempts to access an unimplemented Trigger Module Register raise an illegal instruction exception." Per-register optionality is also explicit: Section 5.7.3 (Trigger Data 2, at 0x7a2): "Trigger-specific data. It is optional if no implemented triggers use it." Section 5.7.4 (Trigger Data 3, at 0x7a3): "Trigger-specific data. It is optional if no implemented triggers use it." Section 5.7.17 (Trigger Extra (RV32), at 0x7a3), which also applies via textra64 on RV64: "All functionality in this register is optional. Any number of upper bits of mhvalue and svalue may be tied to 0. mhselect and sselect may only support 0 (ignore)." Not probing for tdata2/tdata3 support at boot and unconditionally accessing them in the install/update/read/uninstall paths causes SBI calls to fail with an illegal instruction exception on hardware that does not implement one or both CSRs, even if the supervisor-supplied trigger configuration does not require the missing CSR(s). This patch: 1. Probes tdata2 and tdata3 implementation at boot, per hart, by attempting a guarded read with the M-mode illegal-instruction trap handler temporarily set to record-and-skip. The presence of each register is cached in the per-hart DBTR state. 2. Gates every read/write of tdata2 and tdata3 in the install, update, read, and uninstall paths on the corresponding presence flag. 3. When a trigger configuration in shared memory carries a non-zero trig_tdata2 or trig_tdata3 that the hart cannot honor because the CSR is not implemented, the offending entry is rejected with SBI_ERR_NOT_SUPPORTED, matching the spec wording in ?19.4/?19.5 of the SBI v3.0 specification: "One of the trigger configuration can't be programmed due to unimplemented optional bits in tdata1, tdata2, or tdata3 CSRs." 4. When the supervisor-supplied trig_tdata2 / trig_tdata3 are zero and the CSR is unimplemented, the corresponding CSR write is skipped (it would be a no-op anyway) and the operation proceeds. On the uninstall path, clearing of an unimplemented tdata2/tdata3 is likewise skipped. 5. sbi_debug_read_triggers fills the corresponding shared-memory word with zero for any unimplemented register, which is the correct "would-read-as" value for a tied-off WARL register. 6. Enable tdata3 configuration in the debug trigger install path. References: - RISC-V Debug Specification, Chapter 5 (Sdtrig), ?5, ?5.7, ?5.7.2, ?5.7.3, ?5.7.4, ?5.7.17. - RISC-V SBI Specification v3.0, Chapter 19 (Debug Triggers Extension), ?19.4, ?19.5. Fixes: 97f234f15c96 ("lib: sbi: Introduce the SBI debug triggers extension support") Signed-off-by: David E. Garcia Porras --- include/sbi/sbi_dbtr.h | 2 ++ lib/sbi/sbi_dbtr.c | 60 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/include/sbi/sbi_dbtr.h b/include/sbi/sbi_dbtr.h index 5e0bf84e..723d30c8 100644 --- a/include/sbi/sbi_dbtr.h +++ b/include/sbi/sbi_dbtr.h @@ -75,6 +75,8 @@ struct sbi_dbtr_hart_triggers_state { u32 available_trigs; u32 hartid; u32 probed; + bool tdata2_supported; + bool tdata3_supported; }; #define TDATA1_GET_TYPE(_t1) \ diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c index 8bcb4312..e17f697e 100644 --- a/lib/sbi/sbi_dbtr.c +++ b/lib/sbi/sbi_dbtr.c @@ -211,6 +211,17 @@ int sbi_dbtr_init(struct sbi_scratch *scratch, bool coldboot) } } + /* + * Probe tdata2/tdata3 support. These CSRs are optional in the + * RISC-V Sdtrig extension: accessing them on hardware that does + * not implement them raises an illegal instruction exception. + */ + csr_read_allowed(CSR_TDATA2, &trap); + hart_state->tdata2_supported = !trap.cause; + + csr_read_allowed(CSR_TDATA3, &trap); + hart_state->tdata3_supported = !trap.cause; + hart_state->probed = 1; _probed: @@ -367,12 +378,17 @@ static inline void update_bit(unsigned long new, int nr, volatile unsigned long static void dbtr_trigger_enable(struct sbi_dbtr_trigger *trig) { + struct sbi_dbtr_hart_triggers_state *hs; unsigned long state; unsigned long tdata1; if (!trig || !(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED))) return; + hs = dbtr_thishart_state_ptr(); + if (!hs) + return; + state = trig->state; tdata1 = trig->tdata1; @@ -418,7 +434,10 @@ static void dbtr_trigger_enable(struct sbi_dbtr_trigger *trig) */ csr_write(CSR_TSELECT, trig->index); csr_write(CSR_TDATA1, 0x0); - csr_write(CSR_TDATA2, trig->tdata2); + if (hs->tdata2_supported) + csr_write(CSR_TDATA2, trig->tdata2); + if (hs->tdata3_supported) + csr_write(CSR_TDATA3, trig->tdata3); csr_write(CSR_TDATA1, trig->tdata1); } @@ -458,12 +477,21 @@ static void dbtr_trigger_disable(struct sbi_dbtr_trigger *trig) static void dbtr_trigger_clear(struct sbi_dbtr_trigger *trig) { + struct sbi_dbtr_hart_triggers_state *hs; + if (!trig || !(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED))) return; + hs = dbtr_thishart_state_ptr(); + if (!hs) + return; + csr_write(CSR_TSELECT, trig->index); csr_write(CSR_TDATA1, 0x0); - csr_write(CSR_TDATA2, 0x0); + if (hs->tdata2_supported) + csr_write(CSR_TDATA2, 0x0); + if (hs->tdata3_supported) + csr_write(CSR_TDATA3, 0x0); } static int dbtr_trigger_supported(unsigned long type) @@ -566,8 +594,8 @@ int sbi_dbtr_read_trig(unsigned long smode, trig = INDEX_TO_TRIGGER((_idx + trig_idx_base)); csr_write(CSR_TSELECT, trig->index); trig->tdata1 = csr_read(CSR_TDATA1); - trig->tdata2 = csr_read(CSR_TDATA2); - trig->tdata3 = csr_read(CSR_TDATA3); + trig->tdata2 = hs->tdata2_supported ? csr_read(CSR_TDATA2) : 0; + trig->tdata3 = hs->tdata3_supported ? csr_read(CSR_TDATA3) : 0; xmit->tstate = cpu_to_lle(trig->state); xmit->tdata1 = cpu_to_lle(trig->tdata1); xmit->tdata2 = cpu_to_lle(trig->tdata2); @@ -619,6 +647,20 @@ int sbi_dbtr_install_trig(unsigned long smode, trig_count * sizeof(*entry)); return SBI_ERR_FAILED; } + + if (recv->tdata2 && !hs->tdata2_supported) { + *out = _idx; + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } + + if (recv->tdata3 && !hs->tdata3_supported) { + *out = _idx; + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } } if (hs->available_trigs < trig_count) { @@ -734,6 +776,16 @@ int sbi_dbtr_update_trig(unsigned long smode, return SBI_ERR_FAILED; } + if (entry->data.tdata2 && !hs->tdata2_supported) { + sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } + + if (entry->data.tdata3 && !hs->tdata3_supported) { + sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } + dbtr_trigger_setup(trig, &entry->data); sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); dbtr_trigger_enable(trig); -- 2.43.0 From npiggin at gmail.com Mon May 25 20:02:22 2026 From: npiggin at gmail.com (Nicholas Piggin) Date: Mon, 25 May 2026 20:02:22 -0700 Subject: [PATCH] lib: sbi: dbtr: do not unconditionally access tdata2/tdata3 CSRs In-Reply-To: <20260525185033.4165210-1-david.garcia@aheadcomputing.com> References: <20260525185033.4165210-1-david.garcia@aheadcomputing.com> Message-ID: On Mon, May 25, 2026 at 12:50:33PM -0600, David E. Garcia Porras wrote: > The current SBI DBTR extension implementation accesses tdata2 and tdata3 > without first checking whether either register is implemented on the underlying hart. > This produces an illegal instruction exception on otherwise > spec-compliant cores that legitimately omit one or both registers. Hey, I attempted to fix this to work around a crash I saw with QEMU. https://lists.infradead.org/pipermail/opensbi/2026-March/009600.html I didn't actually dig far enough into the spec or see the CSR write side of the issue because it was following the recipe from the spec, and QEMU doesn't crash in that case. AFAIKS you're right though, tdata2/3 must always be readable if they are implemented for any trigger. so QEMU's implementation seems contrary to the Section 5 introduction (I have a QEMU Sdtrig fix series, I'll add a fix to it). Unfortunately your fix here I think won't help the existing QEMU bug because trap depends on the selected trigger type (see tdata_available()). Arguably we could just ignore QEMU... but using csr_read_allowed/csr_write_allowed would solve both cases. What do you think? [snip] > @@ -619,6 +647,20 @@ int sbi_dbtr_install_trig(unsigned long smode, > trig_count * sizeof(*entry)); > return SBI_ERR_FAILED; > } > + > + if (recv->tdata2 && !hs->tdata2_supported) { > + *out = _idx; > + sbi_hart_protection_unmap_range((unsigned long)shmem_base, > + trig_count * sizeof(*entry)); > + return SBI_ERR_NOT_SUPPORTED; > + } > + > + if (recv->tdata3 && !hs->tdata3_supported) { > + *out = _idx; > + sbi_hart_protection_unmap_range((unsigned long)shmem_base, > + trig_count * sizeof(*entry)); > + return SBI_ERR_NOT_SUPPORTED; > + } > } These checks seem consistent with the SBI spec, but even with them we miss classes of these errors AFAIKS. To be comprehensive we might need to program the trigger then read it back and compare. Which might require some complicated unwinding. In any case I don't dislike adding the checks, but perhaps they can be addressed separately and we could decide whether to cover other cases too. Thanks, Nick > > if (hs->available_trigs < trig_count) { > @@ -734,6 +776,16 @@ int sbi_dbtr_update_trig(unsigned long smode, > return SBI_ERR_FAILED; > } > > + if (entry->data.tdata2 && !hs->tdata2_supported) { > + sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > + return SBI_ERR_NOT_SUPPORTED; > + } > + > + if (entry->data.tdata3 && !hs->tdata3_supported) { > + sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > + return SBI_ERR_NOT_SUPPORTED; > + } > + > dbtr_trigger_setup(trig, &entry->data); > sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > dbtr_trigger_enable(trig); > -- From wangruikang at iscas.ac.cn Mon May 25 20:26:27 2026 From: wangruikang at iscas.ac.cn (Vivian Wang) Date: Tue, 26 May 2026 11:26:27 +0800 Subject: [PATCH] include: sbi_platform: Increase default stack size In-Reply-To: References: Message-ID: <1b2d686f-9be6-4e28-9ed2-f89dfd72a148@iscas.ac.cn> On 5/25/26 21:23, Anirudh Srinivasan wrote: > Hi Vivian, > > On Sun, May 24, 2026 at 12:53?AM Vivian Wang wrote: >> sbi_misaligned_v_ld_emulator() has a local buffer (namely mask) which is >> 8192 bytes in size, so the default stack size is not enough. Double it >> to 16384. >> >> This stack overflow can be observed by any S-mode software and leads to >> M-mode crash or unexpected behavior, as long as the S-mode software does >> not enable FWFT misaligned delegation [1]. This may be considered a >> temporary fix until further fixes are made to misaligned handling [2]. >> >> Link: https://lore.kernel.org/linux-riscv/nrvt74qnojaubiwjo37ums4lnclu466hovwrhmtbag6f5uhrql at q6msoe2oto4b # [1] >> Link: https://lore.kernel.org/opensbi/20260210094044.72591-1-ganboing at gmail.com # [2] >> Signed-off-by: Vivian Wang > There was a similar patch [1] from few days ago that makes this a > Kconfig option. Either option would work to fix the bug I encountered. Ah, oops, I definitely missed that. Thanks for the pointer. > Wondering what the maintainers think about how to proceed with this. > > [1] https://lore.kernel.org/opensbi/VI0P192MB3062735A6194BB6DA72083499E002 at VI0P192MB3062.EURP192.PROD.OUTLOOK.COM/T/#u I would appreciate guidance from the maintainers on this as well. Thanks, Vivian "dramforever" Wang From npiggin at gmail.com Mon May 25 20:36:25 2026 From: npiggin at gmail.com (Nicholas Piggin) Date: Mon, 25 May 2026 20:36:25 -0700 Subject: [PATCH] lib: sbi: Apply budget restriction when polling Zkr CSR state transition In-Reply-To: <20260519225014.244672-1-evvoevod@tenstorrent.com> References: <20260519225014.244672-1-evvoevod@tenstorrent.com> Message-ID: On Tue, May 19, 2026 at 10:50:14PM +0000, Evgeny Voevodin wrote: > Zkr architecture doesn't define a time limit on state transitions > which results in hanging on unresponsive or event-driven platforms. > To prevent this, we need to limit polling iterations and fall back > in case the budget is over, and stack guard keeps its initial value. > The budget is configurable with CONFIG_SBI_INIT_ZKR_POLL_BUDGET, > defaulting to 1000 iterations. > Successful reads do not consume a try. > > Signed-off-by: Evgeny Voevodin > --- > lib/sbi/Kconfig | 12 ++++++++++++ > lib/sbi/sbi_init.c | 12 +++++++++--- > 2 files changed, 21 insertions(+), 3 deletions(-) > > diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig > index c6cc04bc..a11f788c 100644 > --- a/lib/sbi/Kconfig > +++ b/lib/sbi/Kconfig > @@ -6,6 +6,18 @@ config CONSOLE_EARLY_BUFFER_SIZE > int "Early console buffer size (bytes)" > default 256 > > +config SBI_INIT_ZKR_POLL_BUDGET > + int "Zkr seed polling budget (iterations)" > + default 1000 > + help > + Maximum number of iterations to poll CSR_SEED when initializing > + the stack guard variable. The Zkr specification doesn't define > + a time limit on transitioning to ES16 between polls, which > + makes it impossible to tell whether entropy is being > + accumulated slowly or the entropy source is not functioning. If entropy source is not functioning it should return DEAD, surely. Specifications always require a "reasonable" performance, whether that is explicit or not. That's open to interpretation and application, but if a word of entropy at boot causes a responsiveness problem, I don't know if that's a reasonable expectation in the spec that needs to be accounted for. So I think a bit more information would be good. If if this is a workaround for a particular platform that might be okay but I think it should be framed as such. Thanks, Nick From npiggin at gmail.com Mon May 25 21:35:23 2026 From: npiggin at gmail.com (Nicholas Piggin) Date: Mon, 25 May 2026 21:35:23 -0700 Subject: [PATCH 05/18] dbtr: Do not support chain bit In-Reply-To: References: <20260313051948.4017134-1-npiggin@gmail.com> <20260313051948.4017134-6-npiggin@gmail.com> Message-ID: On Mon, May 04, 2026 at 08:45:14PM +0530, Himanshu Chauhan wrote: > On Fri, Mar 13, 2026 at 03:19:34PM +1000, Nicholas Piggin wrote: > > There is no chain bit validation in in SBI, so do not support it. > > > > Signed-off-by: Nicholas Piggin > > --- > > lib/sbi/sbi_dbtr.c | 14 ++++++++++++-- > > 1 file changed, 12 insertions(+), 2 deletions(-) > > > > diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c > > index 65c3b2df..224f2350 100644 > > --- a/lib/sbi/sbi_dbtr.c > > +++ b/lib/sbi/sbi_dbtr.c > > @@ -466,11 +466,21 @@ static void dbtr_trigger_clear(struct sbi_dbtr_trigger *trig) > > csr_write(CSR_TDATA2, 0x0); > > } > > > > -static int dbtr_trigger_supported(unsigned long type) > > +static int dbtr_trigger_supported(unsigned long type, unsigned long tdata) > > { > > switch (type) { > > case RISCV_DBTR_TRIG_MCONTROL: > > + /* > > + * SBI currently does not validate chain bit in install/update > > + * so we do not support it. > > + */ > > + if (tdata & RV_DBTR_BIT_MASK(MC, CHAIN)) > > Just a minor suggestion. Since it may be supported in future, I would suggest that > we print a warning message that a trigger with chain support was asked for. I'm not sure that is an appropriate way to report this. There are already, in theory, certain configurations that may be supported by the underlying hardware, but are today rejected due to opensbi not having support for it (e.g., the other trigger types). The idea of logging / warning is interesting, maybe under a "verbose" boot/build option? And the issue of missing hardware vs opensbi support for a feature is certainly something a user might be interested in. But I think it's for another patch series. Thanks, Nick > > > + return 0; > > + return 1; > > case RISCV_DBTR_TRIG_MCONTROL6: > > + if (tdata & RV_DBTR_BIT_MASK(MC6, CHAIN)) > ditto > > Otherwise looks good. > > Reviewed-by: Himanshu Chauhan > > Thanks > Regards > Himanshu > > > + return 0; > > + return 1; > > case RISCV_DBTR_TRIG_ICOUNT: > > return 1; > > default: > > @@ -613,7 +623,7 @@ int sbi_dbtr_install_trig(unsigned long smode, > > recv = (struct sbi_dbtr_data_msg *)(&entry->data); > > tdata1 = lle_to_cpu(recv->tdata1); > > > > - if (!dbtr_trigger_supported(TDATA1_GET_TYPE(tdata1))) { > > + if (!dbtr_trigger_supported(TDATA1_GET_TYPE(tdata1), tdata1)) { > > *out = _idx; > > sbi_hart_protection_unmap_range((unsigned long)shmem_base, > > trig_count * sizeof(*entry)); > > -- > > 2.51.0 > > > > > > -- > > opensbi mailing list > > opensbi at lists.infradead.org > > http://lists.infradead.org/mailman/listinfo/opensbi > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From npiggin at gmail.com Mon May 25 21:38:28 2026 From: npiggin at gmail.com (Nicholas Piggin) Date: Mon, 25 May 2026 21:38:28 -0700 Subject: [PATCH 06/18] dbtr: Improve trigger update error checking In-Reply-To: References: <20260313051948.4017134-1-npiggin@gmail.com> <20260313051948.4017134-7-npiggin@gmail.com> Message-ID: On Mon, May 04, 2026 at 08:53:18PM +0530, Himanshu Chauhan wrote: > On Fri, Mar 13, 2026 at 03:19:35PM +1000, Nicholas Piggin wrote: > > Trigger updates should ensure all triggers can be upated without failure > > before making any changes. Updates that change the trigger type must > > also be disallowed according to SBI specification. > > > > Change the style of shmem access and checking to match the trigger > > install code and perform all checks first. Add the missing check to > > prevent type change. > > > > Signed-off-by: Nicholas Piggin > > --- > > lib/sbi/sbi_dbtr.c | 33 ++++++++++++++++++++++++++------- > > 1 file changed, 26 insertions(+), 7 deletions(-) > > > > diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c > > index 224f2350..d2845fec 100644 > > --- a/lib/sbi/sbi_dbtr.c > > +++ b/lib/sbi/sbi_dbtr.c > > @@ -727,9 +727,9 @@ int sbi_dbtr_enable_trig(unsigned long trig_idx_base, > > int sbi_dbtr_update_trig(unsigned long smode, > > unsigned long trig_count, unsigned long *out) > > { > > - unsigned long trig_idx; > > struct sbi_dbtr_trigger *trig; > > union sbi_dbtr_shmem_entry *entry; > > + struct sbi_dbtr_data_msg *recv; > > void *shmem_base = NULL; > > struct sbi_dbtr_hart_triggers_state *hs = NULL; > > > > @@ -744,30 +744,49 @@ int sbi_dbtr_update_trig(unsigned long smode, > > return SBI_ERR_NO_SHMEM; > > > > shmem_base = hart_shmem_base(hs); > > + sbi_hart_protection_map_range((unsigned long)shmem_base, > > + trig_count * sizeof(*entry)); > Hi Nicholas, > > I would suggest to verify the value of trig_count before making a large map entry. > It would also help in the two loops below. > > > > > + /* Check requested triggers configuration */ > > for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { > > - sbi_hart_protection_map_range((unsigned long)entry, sizeof(*entry)); > > - trig_idx = entry->id.idx; > > + unsigned long trig_idx, tdata1; > > > > + trig_idx = entry->id.idx; > > if (trig_idx >= hs->total_trigs) { > > - sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); > > *out = _idx; > > + sbi_hart_protection_unmap_range((unsigned long)shmem_base, > > + trig_count * sizeof(*entry)); > > A forward goto to bailout in success/error condition would be better. Hey Himanshu, Thanks for the reviews, I'll take a look at these suggestions and see how they look. I should repost this series soon, I will do so after hearing from David about the tdata2/tdata3 issue. Thanks, Nick From david.garcia at aheadcomputing.com Tue May 26 10:24:51 2026 From: david.garcia at aheadcomputing.com (David E. Garcia Porras) Date: Tue, 26 May 2026 11:24:51 -0600 Subject: [PATCH] lib: sbi: dbtr: do not unconditionally access tdata2/tdata3 CSRs In-Reply-To: References: <20260525185033.4165210-1-david.garcia@aheadcomputing.com> Message-ID: <20260526172452.280191-1-david.garcia@aheadcomputing.com> Hi Nick, Thanks for the review and for the QEMU context, I missed that work from your patch series. If QEMU behaves as stated (tying trap directly to specific triggers), then yes my checks won't suffice. I'll update my patch and submit a V2, lmk if we should maybe merge it into your patch series. > Unfortunately your fix here I think won't help the existing QEMU bug > because trap depends on the selected trigger type (see > tdata_available()). Arguably we could just ignore QEMU... but using > csr_read_allowed/csr_write_allowed would solve both cases. What do > you think? Agreed. v2 will drop the boot-time probe and instead wrap every tdata2/tdata3 read and write with csr_read_allowed/csr_write_allowed, checking trap.cause locally, this should cover all cases. For read path, I used your exact same code. > These checks seem consistent with the SBI spec, but even with them we > miss classes of these errors AFAIKS. To be comprehensive we might > need to program the trigger then read it back and compare. Which > might require some complicated unwinding. > > In any case I don't dislike adding the checks, but perhaps they can > be addressed separately and we could decide whether to cover other > cases too. I'd like to keep the SBI_ERR_NOT_SUPPORTED checks in v2 to satisfy the SBI spec wording in section 19.4 / 19.5: "One of the trigger configuration can't be programmed due to unimplemented optional bits in tdata1, tdata2, or tdata3 CSRs." I'll implement them via csr_read_allowed so there's a single consistent code path, and I'll add an explicit comment noting that this only catches the "whole CSR unimplemented" case -- WARL tied-off bits within a CSR are not caught and would require the program-then-read-back approach you described. We can address that as a separate follow-up patch. v2 incoming. Thanks, David From david.garcia at aheadcomputing.com Tue May 26 10:26:51 2026 From: david.garcia at aheadcomputing.com (David E. Garcia Porras) Date: Tue, 26 May 2026 11:26:51 -0600 Subject: [PATCH v2] lib: sbi: dbtr: do not unconditionally access tdata2/tdata3 CSRs In-Reply-To: <20260525185033.4165210-1-david.garcia@aheadcomputing.com> References: <20260525185033.4165210-1-david.garcia@aheadcomputing.com> Message-ID: <20260526172651.280722-1-david.garcia@aheadcomputing.com> The current SBI DBTR extension implementation accesses tdata2 and tdata3 without first checking whether either register is implemented on the underlying hart. This produces an illegal instruction exception on otherwise spec-compliant cores that legitimately omit one or both registers. Per the RISC-V Debug Specification, Chapter 5 (Sdtrig ISA Extension) and Section 5.7 (Trigger Module Registers): Section 5 (Sdtrig introduction): "If Sdtrig is implemented, the Trigger Module must support at least one trigger. Accessing trigger CSRs that are not used by any of the implemented triggers must result in an illegal instruction exception. M-Mode and Debug Mode accesses to trigger CSRs that are used by any of the implemented triggers must succeed, regardless of the current type of the currently selected trigger." Section 5.7 (Trigger Module Registers): "Attempts to access an unimplemented Trigger Module Register raise an illegal instruction exception." Per-register optionality is also explicit: Section 5.7.3 (Trigger Data 2, at 0x7a2): "Trigger-specific data. It is optional if no implemented triggers use it." Section 5.7.4 (Trigger Data 3, at 0x7a3): "Trigger-specific data. It is optional if no implemented triggers use it." Section 5.7.17 (Trigger Extra (RV32), at 0x7a3), which also applies via textra64 on RV64: "All functionality in this register is optional. Any number of upper bits of mhvalue and svalue may be tied to 0. mhselect and sselect may only support 0 (ignore)." Unconditionally accessing tdata2/tdata3 in the install/update/read/ uninstall paths causes SBI calls to fail with an illegal instruction exception on hardware that does not implement one or both CSRs, even if the supervisor-supplied trigger configuration does not require the missing CSR(s). This patch: 1. Wraps every tdata2/tdata3 read and write in csr_read_allowed / csr_write_allowed so that an illegal-instruction trap raised by an unimplemented CSR is caught locally rather than propagated. On the read path, a trapped read yields zero, on the write path, the trap is silently absorbed (writes to an unimplemented CSR are no-ops by definition). 2. On the install and update paths, rejects requests that program a non-zero trig_tdata2 or trig_tdata3 into an unimplemented CSR with SBI_ERR_NOT_SUPPORTED, matching the SBI spec wording in sections 19.4 / 19.5: "One of the trigger configuration can't be programmed due to unimplemented optional bits in tdata1, tdata2, or tdata3 CSRs." Implementation status is probed once per call via csr_read_allowed. This only catches the "whole CSR unimplemented" case; tied-off WARL bits inside an otherwise- implemented CSR are not caught here and would require programming the trigger and reading the value back for comparison, which can be addressed separately. 3. Enable tdata3 configuration in the debug trigger install path. References: - RISC-V Debug Specification, Chapter 5 (Sdtrig), sections 5, 5.7, 5.7.3, 5.7.4, 5.7.17. - RISC-V SBI Specification v3.0, Chapter 19 (Debug Triggers Extension), sections 19.4, 19.5. Fixes: 97f234f15c96 ("lib: sbi: Introduce the SBI debug triggers extension support") Suggested-by: Nicholas Piggin Signed-off-by: David E. Garcia Porras --- Changes since v1: - Replaced the boot-time probe and the cached per-hart tdata2_supported / tdata3_supported flags with per-access csr_read_allowed / csr_write_allowed at every tdata2/tdata3 site, per Nicholas Piggin's review. This covers both spec-compliant cores and implementations whose trap behavior depends on the currently selected trigger type (e.g. QEMU's tdata_available()). - Dropped the additions to struct sbi_dbtr_hart_triggers_state. - Install / update SBI_ERR_NOT_SUPPORTED checks now probe via csr_read_allowed once per call. Added a comment noting that this only catches the "whole CSR unimplemented" case; tied-off WARL bits inside an implemented CSR are not detected and can be addressed in a follow-up. lib/sbi/sbi_dbtr.c | 76 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c index 8bcb4312..aeb92f58 100644 --- a/lib/sbi/sbi_dbtr.c +++ b/lib/sbi/sbi_dbtr.c @@ -367,6 +367,7 @@ static inline void update_bit(unsigned long new, int nr, volatile unsigned long static void dbtr_trigger_enable(struct sbi_dbtr_trigger *trig) { + struct sbi_trap_info trap = {0}; unsigned long state; unsigned long tdata1; @@ -418,7 +419,15 @@ static void dbtr_trigger_enable(struct sbi_dbtr_trigger *trig) */ csr_write(CSR_TSELECT, trig->index); csr_write(CSR_TDATA1, 0x0); - csr_write(CSR_TDATA2, trig->tdata2); + /* + * tdata2 and tdata3 are optional in the RISC-V Sdtrig extension. + * Use csr_write_allowed so that writing to an unimplemented CSR + * traps locally and is silently absorbed; install/update have + * already rejected non-zero requested values for unimplemented + * CSRs. + */ + csr_write_allowed(CSR_TDATA2, &trap, trig->tdata2); + csr_write_allowed(CSR_TDATA3, &trap, trig->tdata3); csr_write(CSR_TDATA1, trig->tdata1); } @@ -458,12 +467,16 @@ static void dbtr_trigger_disable(struct sbi_dbtr_trigger *trig) static void dbtr_trigger_clear(struct sbi_dbtr_trigger *trig) { + struct sbi_trap_info trap = {0}; + if (!trig || !(trig->state & RV_DBTR_BIT_MASK(TS, MAPPED))) return; csr_write(CSR_TSELECT, trig->index); csr_write(CSR_TDATA1, 0x0); - csr_write(CSR_TDATA2, 0x0); + /* Clearing an unimplemented tdata2/tdata3 is a no-op; absorb the trap. */ + csr_write_allowed(CSR_TDATA2, &trap, 0x0); + csr_write_allowed(CSR_TDATA3, &trap, 0x0); } static int dbtr_trigger_supported(unsigned long type) @@ -562,12 +575,14 @@ int sbi_dbtr_read_trig(unsigned long smode, sbi_hart_protection_map_range((unsigned long)shmem_base, trig_count * sizeof(*entry)); for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { + struct sbi_trap_info trap = {0}; + xmit = &entry->data; trig = INDEX_TO_TRIGGER((_idx + trig_idx_base)); csr_write(CSR_TSELECT, trig->index); trig->tdata1 = csr_read(CSR_TDATA1); - trig->tdata2 = csr_read(CSR_TDATA2); - trig->tdata3 = csr_read(CSR_TDATA3); + trig->tdata2 = csr_read_allowed(CSR_TDATA2, &trap); + trig->tdata3 = csr_read_allowed(CSR_TDATA3, &trap); xmit->tstate = cpu_to_lle(trig->state); xmit->tdata1 = cpu_to_lle(trig->tdata1); xmit->tdata2 = cpu_to_lle(trig->tdata2); @@ -589,6 +604,8 @@ int sbi_dbtr_install_trig(unsigned long smode, unsigned long ctrl; struct sbi_dbtr_trigger *trig; struct sbi_dbtr_hart_triggers_state *hs = NULL; + struct sbi_trap_info trap = {0}; + bool tdata2_impl, tdata3_impl; hs = dbtr_thishart_state_ptr(); if (!hs) @@ -601,6 +618,19 @@ int sbi_dbtr_install_trig(unsigned long smode, sbi_hart_protection_map_range((unsigned long)shmem_base, trig_count * sizeof(*entry)); + /* + * Probe tdata2/tdata3 implementation status for the + * SBI v3.0 sec 19.4/19.5 mandatory SBI_ERR_NOT_SUPPORTED check + * below. This only catches the "whole CSR unimplemented" case; + * WARL bits tied off inside an otherwise-implemented CSR are + * not caught here. + */ + csr_read_allowed(CSR_TDATA2, &trap); + tdata2_impl = !trap.cause; + trap.cause = 0; + csr_read_allowed(CSR_TDATA3, &trap); + tdata3_impl = !trap.cause; + /* Check requested triggers configuration */ for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { recv = (struct sbi_dbtr_data_msg *)(&entry->data); @@ -619,6 +649,20 @@ int sbi_dbtr_install_trig(unsigned long smode, trig_count * sizeof(*entry)); return SBI_ERR_FAILED; } + + if (recv->tdata2 && !tdata2_impl) { + *out = _idx; + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } + + if (recv->tdata3 && !tdata3_impl) { + *out = _idx; + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } } if (hs->available_trigs < trig_count) { @@ -705,6 +749,8 @@ int sbi_dbtr_update_trig(unsigned long smode, union sbi_dbtr_shmem_entry *entry; void *shmem_base = NULL; struct sbi_dbtr_hart_triggers_state *hs = NULL; + struct sbi_trap_info trap = {0}; + bool tdata2_impl, tdata3_impl; hs = dbtr_thishart_state_ptr(); if (!hs) @@ -718,6 +764,18 @@ int sbi_dbtr_update_trig(unsigned long smode, if (trig_count >= hs->total_trigs) return SBI_ERR_BAD_RANGE; + /* + * Probe tdata2/tdata3 implementation status for the + * SBI v3.0 sec 19.4/19.5 mandatory SBI_ERR_NOT_SUPPORTED check + * below. Only the "whole CSR unimplemented" case is caught + * here; tied-off WARL bits inside an implemented CSR are not. + */ + csr_read_allowed(CSR_TDATA2, &trap); + tdata2_impl = !trap.cause; + trap.cause = 0; + csr_read_allowed(CSR_TDATA3, &trap); + tdata3_impl = !trap.cause; + for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { sbi_hart_protection_map_range((unsigned long)entry, sizeof(*entry)); trig_idx = entry->id.idx; @@ -734,6 +792,16 @@ int sbi_dbtr_update_trig(unsigned long smode, return SBI_ERR_FAILED; } + if (entry->data.tdata2 && !tdata2_impl) { + sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } + + if (entry->data.tdata3 && !tdata3_impl) { + sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } + dbtr_trigger_setup(trig, &entry->data); sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); dbtr_trigger_enable(trig); -- 2.43.0 From evvoevod at tenstorrent.com Wed May 27 10:19:14 2026 From: evvoevod at tenstorrent.com (Evgeny Voevodin) Date: Wed, 27 May 2026 17:19:14 +0000 Subject: [PATCH] lib: sbi: Apply budget restriction when polling Zkr CSR state transition In-Reply-To: References: <20260519225014.244672-1-evvoevod@tenstorrent.com> Message-ID: <20260527171915.416189-1-evvoevod@tenstorrent.com> Yes, DEAD is the architectural way to signal a non-functioning source, and the spec implicitly assumes reasonable timing. The actual scenario this patch fixes is more specific: This patch fixes a hang on platforms where either internal entropy source is event driven and requires corresponding interrupt line to be enabled or external entropy FIFO refill source is not wired up (as on SW and HW emulation of Tenstorrent platform). Without the cap, init_coldboot() spins forever - no console output, no diagnostic, just a silent boot-time hang. With it, __stack_chk_guard keeps its compile-time default and the firmware boots. Given this explanation, is it OK to keep this patch as is or is it better to send v2 with updated commit message and Kconfig help text? Thanks, Evgeny From wangxiang at iscas.ac.cn Fri May 29 00:35:10 2026 From: wangxiang at iscas.ac.cn (Xiang W) Date: Fri, 29 May 2026 15:35:10 +0800 Subject: [PATCH] platform: Allow platforms to customize TLB operations Message-ID: <20260529073510.97299-1-wangxiang@iscas.ac.cn> Some T-Head based processors, for example the Sophgo SG2044, have a JTLB errata that requires special handling of certain TLB maintenance instructions (sfence.vma). Enable the JTLB workaround for affected boards. Signed-off-by: Xiang W Signed-off-by: Han Gao --- include/sbi/sbi_platform.h | 162 +++++++++++++++++++ lib/sbi/sbi_tlb.c | 28 ++++ platform/generic/include/thead/c9xx_errata.h | 1 + platform/generic/thead/thead-generic.c | 26 ++- 4 files changed, 216 insertions(+), 1 deletion(-) diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index fe382b56..911b37bf 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -56,6 +56,7 @@ struct sbi_domain_memregion; struct sbi_ecall_return; struct sbi_trap_regs; struct sbi_hart_features; +struct sbi_tlb_info; union sbi_ldst_data; /** Possible feature flags of a platform */ @@ -125,6 +126,27 @@ struct sbi_platform_operations { /** Get tlb fifo num entries*/ u32 (*get_tlb_num_entries)(void); + /** Platform hook for custom fence.i */ + void (*local_fence_i)(struct sbi_tlb_info *tinfo); + + /** Platform hook for custom supervisor-mode TLB flush */ + void (*local_sfence_vma)(struct sbi_tlb_info *tinfo); + + /** Platform hook for custom supervisor-mode TLB flush with ASID */ + void (*local_sfence_vma_asid)(struct sbi_tlb_info *tinfo); + + /** Platform hook for custom G-stage TLB flush for a specific VMID */ + void (*local_hfence_gvma_vmid)(struct sbi_tlb_info *tinfo); + + /** Platform hook for custom G-stage TLB flush */ + void (*local_hfence_gvma)(struct sbi_tlb_info *tinfo); + + /** Platform hook for custom VS-stage TLB flush with ASID */ + void (*local_hfence_vvma_asid)(struct sbi_tlb_info *tinfo); + + /** Platform hook for custom VS-stage TLB flush */ + void (*local_hfence_vvma)(struct sbi_tlb_info *tinfo); + /** Initialize platform timer during cold boot */ int (*timer_init)(void); @@ -313,6 +335,146 @@ static inline u32 sbi_platform_tlb_fifo_num_entries(const struct sbi_platform *p return sbi_hart_count(); } +/** + * Platform hook for custom fence.i + * + * @param plat pointer to struct sbi_platform + * @paeam tinfo pointer to struct sbi_tlb_info + * + * @return 0 indicates that the platform supports the operation and the + * operation was successful; otherwise, a normal operation needs to be performed. + */ +static inline u32 sbi_platform_local_fence_i( + const struct sbi_platform *plat, + struct sbi_tlb_info *tinfo) +{ + if (plat && sbi_platform_ops(plat)->local_fence_i) { + sbi_platform_ops(plat)->local_fence_i(tinfo); + return 0; + } + return SBI_ENOTSUPP; +} + +/** + * Platform hook for custom supervisor-mode TLB flush + * + * @param plat pointer to struct sbi_platform + * @paeam tinfo pointer to struct sbi_tlb_info + * + * @return 0 indicates that the platform supports the operation and the + * operation was successful; otherwise, a normal operation needs to be performed. + */ +static inline u32 sbi_platform_local_sfence_vma( + const struct sbi_platform *plat, + struct sbi_tlb_info *tinfo) +{ + if (plat && sbi_platform_ops(plat)->local_sfence_vma) { + sbi_platform_ops(plat)->local_sfence_vma(tinfo); + return 0; + } + return SBI_ENOTSUPP; +} + +/** + * Platform hook for custom supervisor-mode TLB flush with ASID + * + * @param plat pointer to struct sbi_platform + * @paeam tinfo pointer to struct sbi_tlb_info + * + * @return 0 indicates that the platform supports the operation and the + * operation was successful; otherwise, a normal operation needs to be performed. + */ +static inline u32 sbi_platform_local_sfence_vma_asid( + const struct sbi_platform *plat, + struct sbi_tlb_info *tinfo) +{ + if (plat && sbi_platform_ops(plat)->local_sfence_vma_asid) { + sbi_platform_ops(plat)->local_sfence_vma_asid(tinfo); + return 0; + } + return SBI_ENOTSUPP; +} + +/** + * Platform hook for custom G-stage TLB flush for a specific VMID + * + * @param plat pointer to struct sbi_platform + * @paeam tinfo pointer to struct sbi_tlb_info + * + * @return 0 indicates that the platform supports the operation and the + * operation was successful; otherwise, a normal operation needs to be performed. + */ +static inline u32 sbi_platform_local_hfence_gvma_vmid( + const struct sbi_platform *plat, + struct sbi_tlb_info *tinfo) +{ + if (plat && sbi_platform_ops(plat)->local_hfence_gvma_vmid) { + sbi_platform_ops(plat)->local_hfence_gvma_vmid(tinfo); + return 0; + } + return SBI_ENOTSUPP; +} + +/** + * Platform hook for custom G-stage TLB flush + * + * @param plat pointer to struct sbi_platform + * @paeam tinfo pointer to struct sbi_tlb_info + * + * @return 0 indicates that the platform supports the operation and the + * operation was successful; otherwise, a normal operation needs to be performed. + */ +static inline u32 sbi_platform_local_hfence_gvma( + const struct sbi_platform *plat, + struct sbi_tlb_info *tinfo) +{ + if (plat && sbi_platform_ops(plat)->local_hfence_gvma) { + sbi_platform_ops(plat)->local_hfence_gvma(tinfo); + return 0; + } + return SBI_ENOTSUPP; +} + +/** + * Platform hook for custom VS-stage TLB flush with ASID + * + * @param plat pointer to struct sbi_platform + * @paeam tinfo pointer to struct sbi_tlb_info + * + * @return 0 indicates that the platform supports the operation and the + * operation was successful; otherwise, a normal operation needs to be performed. + */ +static inline u32 sbi_platform_local_hfence_vvma_asid( + const struct sbi_platform *plat, + struct sbi_tlb_info *tinfo) +{ + if (plat && sbi_platform_ops(plat)->local_hfence_vvma_asid) { + sbi_platform_ops(plat)->local_hfence_vvma_asid(tinfo); + return 0; + } + return SBI_ENOTSUPP; +} + +/** + * Platform hook for custom VS-stage TLB flush + * + * @param plat pointer to struct sbi_platform + * @paeam tinfo pointer to struct sbi_tlb_info + * + * @return 0 indicates that the platform supports the operation and the + * operation was successful; otherwise, a normal operation needs to be performed. + */ +static inline u32 sbi_platform_local_hfence_vvma( + const struct sbi_platform *plat, + struct sbi_tlb_info *tinfo) +{ + if (plat && sbi_platform_ops(plat)->local_hfence_vvma) { + sbi_platform_ops(plat)->local_hfence_vvma(tinfo); + return 0; + } + return SBI_ENOTSUPP; +} + /** * Get total number of HARTs supported by the platform * diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c index ada60c32..69b8fb48 100644 --- a/lib/sbi/sbi_tlb.c +++ b/lib/sbi/sbi_tlb.c @@ -46,6 +46,10 @@ static void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo) hgatp = csr_swap(CSR_HGATP, (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); + if (sbi_platform_local_hfence_gvma( + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) + gito done; + if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { __sbi_hfence_vvma_all(); goto done; @@ -67,6 +71,10 @@ static void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo) sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_RCVD); + if (sbi_platform_local_hfence_gvma( + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) + return; + if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { __sbi_hfence_gvma_all(); return; @@ -85,6 +93,10 @@ static void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo) sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_RCVD); + if (sbi_platform_local_sfence_vma( + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) + return; + if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { __sbi_sfence_vma_all(); return; @@ -111,6 +123,10 @@ static void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo) hgatp = csr_swap(CSR_HGATP, (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); + if (sbi_platform_local_hfence_vvma_asid( + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) + goto done; + if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { __sbi_hfence_vvma_asid(asid); goto done; @@ -133,6 +149,10 @@ static void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo) sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD); + if (sbi_platform_local_hfence_gvma_vmid( + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) + return; + if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { __sbi_hfence_gvma_vmid(vmid); return; @@ -152,6 +172,10 @@ static void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo) sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_RCVD); + if (sbi_platform_local_sfence_vma_asid( + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) + return; + /* Flush entire MM context for a given ASID */ if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { __asm__ __volatile__("sfence.vma x0, %0" @@ -173,6 +197,10 @@ static void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo) { sbi_pmu_ctr_incr_fw(SBI_PMU_FW_FENCE_I_RECVD); + if (sbi_platform_local_fence_i( + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) + return; + __asm__ __volatile("fence.i"); } diff --git a/platform/generic/include/thead/c9xx_errata.h b/platform/generic/include/thead/c9xx_errata.h index 40c1587b..7a419e41 100644 --- a/platform/generic/include/thead/c9xx_errata.h +++ b/platform/generic/include/thead/c9xx_errata.h @@ -8,6 +8,7 @@ */ #define THEAD_QUIRK_ERRATA_TLB_FLUSH BIT(0) #define THEAD_QUIRK_ERRATA_THEAD_PMU BIT(1) +#define THEAD_QUIRK_ERRATA_JTLB BIT(2) void thead_register_tlb_flush_trap_handler(void); diff --git a/platform/generic/thead/thead-generic.c b/platform/generic/thead/thead-generic.c index ddb4f0bf..86793ab1 100644 --- a/platform/generic/thead/thead-generic.c +++ b/platform/generic/thead/thead-generic.c @@ -13,6 +13,7 @@ #include #include #include +#include #include struct thead_generic_quirks { @@ -39,6 +40,20 @@ static int thead_pmu_extensions_init(struct sbi_hart_features *hfeatures) return 0; } +static void thead_jtlb_local_sfence_vma(struct sbi_tlb_info *tinfo) +{ + __asm__ __volatile__("sfence.vma"); +} + +static void thead_jtlb_local_sfence_vvma_asid(struct sbi_tlb_info *tinfo) +{ + /* Flush entire MM context for a given ASID */ + __asm__ __volatile__("sfence.vma x0, %0" + : + : "r"(tinfo->asid) + : "memory"); +} + static int thead_generic_platform_init(const void *fdt, int nodeoff, const struct fdt_match *match) { @@ -49,6 +64,11 @@ static int thead_generic_platform_init(const void *fdt, int nodeoff, if (quirks->errata & THEAD_QUIRK_ERRATA_THEAD_PMU) generic_platform_ops.extensions_init = thead_pmu_extensions_init; + if (quirks->errata &THEAD_QUIRK_ERRATA_JTLB) { + generic_platform_ops.local_sfence_vma = thead_jtlb_local_sfence_vma; + generic_platform_ops.local_sfence_vma_asid = thead_jtlb_local_sfence_vvma_asid; + } + return 0; } @@ -60,13 +80,17 @@ static const struct thead_generic_quirks thead_pmu_quirks = { .errata = THEAD_QUIRK_ERRATA_THEAD_PMU, }; +static const struct thead_generic_quirks thead_pmu_jtlb_quirks = { + .errata = THEAD_QUIRK_ERRATA_THEAD_PMU | THEAD_QUIRK_ERRATA_JTLB, +}; + static const struct fdt_match thead_generic_match[] = { { .compatible = "canaan,kendryte-k230", .data = &thead_pmu_quirks }, { .compatible = "sophgo,cv1800b", .data = &thead_pmu_quirks }, { .compatible = "sophgo,cv1812h", .data = &thead_pmu_quirks }, { .compatible = "sophgo,sg2000", .data = &thead_pmu_quirks }, { .compatible = "sophgo,sg2002", .data = &thead_pmu_quirks }, - { .compatible = "sophgo,sg2044", .data = &thead_pmu_quirks }, + { .compatible = "sophgo,sg2044", .data = &thead_pmu_jtlb_quirks }, { .compatible = "thead,th1520", .data = &thead_th1520_quirks }, { }, }; -- 2.47.3 From inochiama at gmail.com Fri May 29 01:52:32 2026 From: inochiama at gmail.com (Inochi Amaoto) Date: Fri, 29 May 2026 16:52:32 +0800 Subject: [PATCH] lib: utils/reset: Add litex SoC reset driver Message-ID: <20260529085234.1682842-1-inochiama@gmail.com> Litex SoC controller supports reboot function by toggling the first bit of the ctrl register. Add a reset driver so other software can use it. Signed-off-by: Inochi Amaoto --- lib/utils/reset/Kconfig | 5 +++ lib/utils/reset/fdt_reset_litex.c | 66 ++++++++++++++++++++++++++++++ lib/utils/reset/objects.mk | 3 ++ platform/generic/configs/defconfig | 1 + 4 files changed, 75 insertions(+) create mode 100644 lib/utils/reset/fdt_reset_litex.c diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig index f98926e5..b2ac120e 100644 --- a/lib/utils/reset/Kconfig +++ b/lib/utils/reset/Kconfig @@ -24,6 +24,11 @@ config FDT_RESET_HTIF select SYS_HTIF default n +config FDT_RESET_LITEX + bool "LITEX SoC reset driver" + depends on FDT_GPIO + default n + config FDT_RESET_RPMI bool "RPMI FDT reset driver" depends on FDT_MAILBOX && RPMI_MAILBOX diff --git a/lib/utils/reset/fdt_reset_litex.c b/lib/utils/reset/fdt_reset_litex.c new file mode 100644 index 00000000..287745f0 --- /dev/null +++ b/lib/utils/reset/fdt_reset_litex.c @@ -0,0 +1,66 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Inochi Amaoto + */ + +#include +#include +#include +#include +#include +#include + +#define RESET_CTRL 0x0 + +static volatile u32 *litex_soc_base; + +static int litex_reset_check(u32 type, u32 reason) +{ + switch (type) { + case SBI_SRST_RESET_TYPE_COLD_REBOOT: + case SBI_SRST_RESET_TYPE_WARM_REBOOT: + return 255; + } + + return 0; +} + +static void litex_do_reset(u32 type, u32 reason) +{ + writel_relaxed(0x1, litex_soc_base + RESET_CTRL); +} + +static struct sbi_system_reset_device litex_reset = { + .name = "litex-reset", + .system_reset_check = litex_reset_check, + .system_reset = litex_do_reset +}; + +static int litex_reset_init(const void *fdt, int nodeoff, + const struct fdt_match *match) +{ + uint64_t reg_addr; + int rc; + + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, ®_addr, NULL); + if (rc < 0 || !reg_addr) + return SBI_ENODEV; + + + litex_soc_base = (volatile u32 *)(unsigned long)reg_addr; + + sbi_system_reset_add_device(&litex_reset); + + return 0; +} + +static const struct fdt_match litex_reset_match[] = { + { .compatible = "litex,soc-controller" }, + { }, +}; + +const struct fdt_driver fdt_reset_litex = { + .match_table = litex_reset_match, + .init = litex_reset_init, +}; diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk index 3c681c27..38b4e306 100644 --- a/lib/utils/reset/objects.mk +++ b/lib/utils/reset/objects.mk @@ -17,6 +17,9 @@ libsbiutils-objs-$(CONFIG_FDT_RESET_GPIO) += reset/fdt_reset_gpio.o carray-fdt_early_drivers-$(CONFIG_FDT_RESET_HTIF) += fdt_reset_htif libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o +carray-fdt_early_drivers-$(CONFIG_FDT_RESET_LITEX) += fdt_reset_litex +libsbiutils-objs-$(CONFIG_FDT_RESET_LITEX) += reset/fdt_reset_litex.o + carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += fdt_reset_sg2042_mcu libsbiutils-objs-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += reset/fdt_reset_sg2042_hwmon_mcu.o diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig index 818b71f4..969639e3 100644 --- a/platform/generic/configs/defconfig +++ b/platform/generic/configs/defconfig @@ -49,6 +49,7 @@ CONFIG_FDT_REGMAP_SYSCON=y CONFIG_FDT_RESET=y CONFIG_FDT_RESET_ATCWDT200=y CONFIG_FDT_RESET_GPIO=y +CONFIG_FDT_RESET_LITEX=y CONFIG_FDT_RESET_HTIF=y CONFIG_FDT_RESET_RPMI=y CONFIG_FDT_RESET_SG2042_HWMON_MCU=y -- 2.54.0 From inochiama at gmail.com Fri May 29 01:56:42 2026 From: inochiama at gmail.com (Inochi Amaoto) Date: Fri, 29 May 2026 16:56:42 +0800 Subject: [PATCH] platform: Allow platforms to customize TLB operations In-Reply-To: <20260529073510.97299-1-wangxiang@iscas.ac.cn> References: <20260529073510.97299-1-wangxiang@iscas.ac.cn> Message-ID: On Fri, May 29, 2026 at 03:35:10PM +0800, Xiang W wrote: > Some T-Head based processors, for example the Sophgo SG2044, have a > JTLB errata that requires special handling of certain TLB maintenance > instructions (sfence.vma). > > Enable the JTLB workaround for affected boards. > > Signed-off-by: Xiang W > Signed-off-by: Han Gao > --- > include/sbi/sbi_platform.h | 162 +++++++++++++++++++ > lib/sbi/sbi_tlb.c | 28 ++++ > platform/generic/include/thead/c9xx_errata.h | 1 + > platform/generic/thead/thead-generic.c | 26 ++- > 4 files changed, 216 insertions(+), 1 deletion(-) > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > index fe382b56..911b37bf 100644 > --- a/include/sbi/sbi_platform.h > +++ b/include/sbi/sbi_platform.h > @@ -56,6 +56,7 @@ struct sbi_domain_memregion; > struct sbi_ecall_return; > struct sbi_trap_regs; > struct sbi_hart_features; > +struct sbi_tlb_info; > union sbi_ldst_data; > > /** Possible feature flags of a platform */ > @@ -125,6 +126,27 @@ struct sbi_platform_operations { > /** Get tlb fifo num entries*/ > u32 (*get_tlb_num_entries)(void); > > + /** Platform hook for custom fence.i */ > + void (*local_fence_i)(struct sbi_tlb_info *tinfo); > + > + /** Platform hook for custom supervisor-mode TLB flush */ > + void (*local_sfence_vma)(struct sbi_tlb_info *tinfo); > + > + /** Platform hook for custom supervisor-mode TLB flush with ASID */ > + void (*local_sfence_vma_asid)(struct sbi_tlb_info *tinfo); > + > + /** Platform hook for custom G-stage TLB flush for a specific VMID */ > + void (*local_hfence_gvma_vmid)(struct sbi_tlb_info *tinfo); > + > + /** Platform hook for custom G-stage TLB flush */ > + void (*local_hfence_gvma)(struct sbi_tlb_info *tinfo); > + > + /** Platform hook for custom VS-stage TLB flush with ASID */ > + void (*local_hfence_vvma_asid)(struct sbi_tlb_info *tinfo); > + > + /** Platform hook for custom VS-stage TLB flush */ > + void (*local_hfence_vvma)(struct sbi_tlb_info *tinfo); > + > /** Initialize platform timer during cold boot */ > int (*timer_init)(void); > > @@ -313,6 +335,146 @@ static inline u32 sbi_platform_tlb_fifo_num_entries(const struct sbi_platform *p > return sbi_hart_count(); > } > > +/** > + * Platform hook for custom fence.i > + * > + * @param plat pointer to struct sbi_platform > + * @paeam tinfo pointer to struct sbi_tlb_info > + * > + * @return 0 indicates that the platform supports the operation and the > + * operation was successful; otherwise, a normal operation needs to be performed. > + */ > +static inline u32 sbi_platform_local_fence_i( > + const struct sbi_platform *plat, > + struct sbi_tlb_info *tinfo) > +{ > + if (plat && sbi_platform_ops(plat)->local_fence_i) { > + sbi_platform_ops(plat)->local_fence_i(tinfo); > + return 0; > + } > + return SBI_ENOTSUPP; > +} > + > +/** > + * Platform hook for custom supervisor-mode TLB flush > + * > + * @param plat pointer to struct sbi_platform > + * @paeam tinfo pointer to struct sbi_tlb_info > + * > + * @return 0 indicates that the platform supports the operation and the > + * operation was successful; otherwise, a normal operation needs to be performed. > + */ > +static inline u32 sbi_platform_local_sfence_vma( > + const struct sbi_platform *plat, > + struct sbi_tlb_info *tinfo) > +{ > + if (plat && sbi_platform_ops(plat)->local_sfence_vma) { > + sbi_platform_ops(plat)->local_sfence_vma(tinfo); > + return 0; > + } > + return SBI_ENOTSUPP; > +} > + > +/** > + * Platform hook for custom supervisor-mode TLB flush with ASID > + * > + * @param plat pointer to struct sbi_platform > + * @paeam tinfo pointer to struct sbi_tlb_info > + * > + * @return 0 indicates that the platform supports the operation and the > + * operation was successful; otherwise, a normal operation needs to be performed. > + */ > +static inline u32 sbi_platform_local_sfence_vma_asid( > + const struct sbi_platform *plat, > + struct sbi_tlb_info *tinfo) > +{ > + if (plat && sbi_platform_ops(plat)->local_sfence_vma_asid) { > + sbi_platform_ops(plat)->local_sfence_vma_asid(tinfo); > + return 0; > + } > + return SBI_ENOTSUPP; > +} > + > +/** > + * Platform hook for custom G-stage TLB flush for a specific VMID > + * > + * @param plat pointer to struct sbi_platform > + * @paeam tinfo pointer to struct sbi_tlb_info > + * > + * @return 0 indicates that the platform supports the operation and the > + * operation was successful; otherwise, a normal operation needs to be performed. > + */ > +static inline u32 sbi_platform_local_hfence_gvma_vmid( > + const struct sbi_platform *plat, > + struct sbi_tlb_info *tinfo) > +{ > + if (plat && sbi_platform_ops(plat)->local_hfence_gvma_vmid) { > + sbi_platform_ops(plat)->local_hfence_gvma_vmid(tinfo); > + return 0; > + } > + return SBI_ENOTSUPP; > +} > + > +/** > + * Platform hook for custom G-stage TLB flush > + * > + * @param plat pointer to struct sbi_platform > + * @paeam tinfo pointer to struct sbi_tlb_info > + * > + * @return 0 indicates that the platform supports the operation and the > + * operation was successful; otherwise, a normal operation needs to be performed. > + */ > +static inline u32 sbi_platform_local_hfence_gvma( > + const struct sbi_platform *plat, > + struct sbi_tlb_info *tinfo) > +{ > + if (plat && sbi_platform_ops(plat)->local_hfence_gvma) { > + sbi_platform_ops(plat)->local_hfence_gvma(tinfo); > + return 0; > + } > + return SBI_ENOTSUPP; > +} > + > +/** > + * Platform hook for custom VS-stage TLB flush with ASID > + * > + * @param plat pointer to struct sbi_platform > + * @paeam tinfo pointer to struct sbi_tlb_info > + * > + * @return 0 indicates that the platform supports the operation and the > + * operation was successful; otherwise, a normal operation needs to be performed. > + */ > +static inline u32 sbi_platform_local_hfence_vvma_asid( > + const struct sbi_platform *plat, > + struct sbi_tlb_info *tinfo) > +{ > + if (plat && sbi_platform_ops(plat)->local_hfence_vvma_asid) { > + sbi_platform_ops(plat)->local_hfence_vvma_asid(tinfo); > + return 0; > + } > + return SBI_ENOTSUPP; > +} > + > +/** > + * Platform hook for custom VS-stage TLB flush > + * > + * @param plat pointer to struct sbi_platform > + * @paeam tinfo pointer to struct sbi_tlb_info > + * > + * @return 0 indicates that the platform supports the operation and the > + * operation was successful; otherwise, a normal operation needs to be performed. > + */ > +static inline u32 sbi_platform_local_hfence_vvma( > + const struct sbi_platform *plat, > + struct sbi_tlb_info *tinfo) > +{ > + if (plat && sbi_platform_ops(plat)->local_hfence_vvma) { > + sbi_platform_ops(plat)->local_hfence_vvma(tinfo); > + return 0; > + } > + return SBI_ENOTSUPP; > +} > + > /** > * Get total number of HARTs supported by the platform > * > diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c > index ada60c32..69b8fb48 100644 > --- a/lib/sbi/sbi_tlb.c > +++ b/lib/sbi/sbi_tlb.c > @@ -46,6 +46,10 @@ static void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo) > hgatp = csr_swap(CSR_HGATP, > (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); > > + if (sbi_platform_local_hfence_gvma( > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > + gito done; > + > if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > __sbi_hfence_vvma_all(); > goto done; > @@ -67,6 +71,10 @@ static void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo) > > sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_RCVD); > > + if (sbi_platform_local_hfence_gvma( > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > + return; > + > if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > __sbi_hfence_gvma_all(); > return; > @@ -85,6 +93,10 @@ static void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo) > > sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_RCVD); > > + if (sbi_platform_local_sfence_vma( > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > + return; > + > if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > __sbi_sfence_vma_all(); > return; > @@ -111,6 +123,10 @@ static void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo) > hgatp = csr_swap(CSR_HGATP, > (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); > > + if (sbi_platform_local_hfence_vvma_asid( > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > + goto done; > + > if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > __sbi_hfence_vvma_asid(asid); > goto done; > @@ -133,6 +149,10 @@ static void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo) > > sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD); > > + if (sbi_platform_local_hfence_gvma_vmid( > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > + return; > + > if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > __sbi_hfence_gvma_vmid(vmid); > return; > @@ -152,6 +172,10 @@ static void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo) > > sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_RCVD); > > + if (sbi_platform_local_sfence_vma_asid( > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > + return; > + > /* Flush entire MM context for a given ASID */ > if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > __asm__ __volatile__("sfence.vma x0, %0" > @@ -173,6 +197,10 @@ static void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo) > { > sbi_pmu_ctr_incr_fw(SBI_PMU_FW_FENCE_I_RECVD); > > + if (sbi_platform_local_fence_i( > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > + return; > + > __asm__ __volatile("fence.i"); > } > I suggest split the following as a separate patch. > diff --git a/platform/generic/include/thead/c9xx_errata.h b/platform/generic/include/thead/c9xx_errata.h > index 40c1587b..7a419e41 100644 > --- a/platform/generic/include/thead/c9xx_errata.h > +++ b/platform/generic/include/thead/c9xx_errata.h > @@ -8,6 +8,7 @@ > */ > #define THEAD_QUIRK_ERRATA_TLB_FLUSH BIT(0) > #define THEAD_QUIRK_ERRATA_THEAD_PMU BIT(1) > +#define THEAD_QUIRK_ERRATA_JTLB BIT(2) > > void thead_register_tlb_flush_trap_handler(void); > > diff --git a/platform/generic/thead/thead-generic.c b/platform/generic/thead/thead-generic.c > index ddb4f0bf..86793ab1 100644 > --- a/platform/generic/thead/thead-generic.c > +++ b/platform/generic/thead/thead-generic.c > @@ -13,6 +13,7 @@ > #include > #include > #include > +#include > #include > > struct thead_generic_quirks { > @@ -39,6 +40,20 @@ static int thead_pmu_extensions_init(struct sbi_hart_features *hfeatures) > return 0; > } > > +static void thead_jtlb_local_sfence_vma(struct sbi_tlb_info *tinfo) > +{ > + __asm__ __volatile__("sfence.vma"); > +} > + > +static void thead_jtlb_local_sfence_vvma_asid(struct sbi_tlb_info *tinfo) > +{ > + /* Flush entire MM context for a given ASID */ > + __asm__ __volatile__("sfence.vma x0, %0" > + : > + : "r"(tinfo->asid) > + : "memory"); > +} > + > static int thead_generic_platform_init(const void *fdt, int nodeoff, > const struct fdt_match *match) > { > @@ -49,6 +64,11 @@ static int thead_generic_platform_init(const void *fdt, int nodeoff, > if (quirks->errata & THEAD_QUIRK_ERRATA_THEAD_PMU) > generic_platform_ops.extensions_init = thead_pmu_extensions_init; > > + if (quirks->errata &THEAD_QUIRK_ERRATA_JTLB) { > + generic_platform_ops.local_sfence_vma = thead_jtlb_local_sfence_vma; > + generic_platform_ops.local_sfence_vma_asid = thead_jtlb_local_sfence_vvma_asid; > + } > + > return 0; > } > > @@ -60,13 +80,17 @@ static const struct thead_generic_quirks thead_pmu_quirks = { > .errata = THEAD_QUIRK_ERRATA_THEAD_PMU, > }; > > +static const struct thead_generic_quirks thead_pmu_jtlb_quirks = { > + .errata = THEAD_QUIRK_ERRATA_THEAD_PMU | THEAD_QUIRK_ERRATA_JTLB, > +}; > + IIRC, it is better to not setting THEAD_QUIRK_ERRATA_THEAD_PMU anymore for sg2044. The T-HEAD specific PMU feature is bad for the cpu version of the sg2044. > static const struct fdt_match thead_generic_match[] = { > { .compatible = "canaan,kendryte-k230", .data = &thead_pmu_quirks }, > { .compatible = "sophgo,cv1800b", .data = &thead_pmu_quirks }, > { .compatible = "sophgo,cv1812h", .data = &thead_pmu_quirks }, > { .compatible = "sophgo,sg2000", .data = &thead_pmu_quirks }, > { .compatible = "sophgo,sg2002", .data = &thead_pmu_quirks }, > - { .compatible = "sophgo,sg2044", .data = &thead_pmu_quirks }, > + { .compatible = "sophgo,sg2044", .data = &thead_pmu_jtlb_quirks }, > { .compatible = "thead,th1520", .data = &thead_th1520_quirks }, > { }, > }; > -- > 2.47.3 > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From wangxiang at iscas.ac.cn Fri May 29 02:24:17 2026 From: wangxiang at iscas.ac.cn (Xiang W) Date: Fri, 29 May 2026 17:24:17 +0800 Subject: [PATCH] platform: Allow platforms to customize TLB operations In-Reply-To: References: <20260529073510.97299-1-wangxiang@iscas.ac.cn> Message-ID: <3518a37ebaaf7c8f4eb3afdcbf8c888bbae09047.camel@iscas.ac.cn> ? 2026-05-29?? 16:56 +0800?Inochi Amaoto??? > On Fri, May 29, 2026 at 03:35:10PM +0800, Xiang W wrote: > > Some T-Head based processors, for example the Sophgo SG2044, have a > > JTLB errata that requires special handling of certain TLB maintenance > > instructions (sfence.vma). > > > > Enable the JTLB workaround for affected boards. > > > > Signed-off-by: Xiang W > > Signed-off-by: Han Gao > > --- > > ?include/sbi/sbi_platform.h?????????????????? | 162 +++++++++++++++++++ > > ?lib/sbi/sbi_tlb.c??????????????????????????? |? 28 ++++ > > ?platform/generic/include/thead/c9xx_errata.h |?? 1 + > > ?platform/generic/thead/thead-generic.c?????? |? 26 ++- > > ?4 files changed, 216 insertions(+), 1 deletion(-) > > > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > > index fe382b56..911b37bf 100644 > > --- a/include/sbi/sbi_platform.h > > +++ b/include/sbi/sbi_platform.h > > @@ -56,6 +56,7 @@ struct sbi_domain_memregion; > > ?struct sbi_ecall_return; > > ?struct sbi_trap_regs; > > ?struct sbi_hart_features; > > +struct sbi_tlb_info; > > ?union sbi_ldst_data; > > ? > > ?/** Possible feature flags of a platform */ > > @@ -125,6 +126,27 @@ struct sbi_platform_operations { > > ? /** Get tlb fifo num entries*/ > > ? u32 (*get_tlb_num_entries)(void); > > ? > > + /** Platform hook for custom fence.i */ > > + void (*local_fence_i)(struct sbi_tlb_info *tinfo); > > + > > + /** Platform hook for custom supervisor-mode TLB flush */ > > + void (*local_sfence_vma)(struct sbi_tlb_info *tinfo); > > + > > + /** Platform hook for custom supervisor-mode TLB flush with ASID */ > > + void (*local_sfence_vma_asid)(struct sbi_tlb_info *tinfo); > > + > > + /** Platform hook for custom G-stage TLB flush for a specific VMID */ > > + void (*local_hfence_gvma_vmid)(struct sbi_tlb_info *tinfo); > > + > > + /** Platform hook for custom G-stage TLB flush */ > > + void (*local_hfence_gvma)(struct sbi_tlb_info *tinfo); > > + > > + /** Platform hook for custom VS-stage TLB flush with ASID */ > > + void (*local_hfence_vvma_asid)(struct sbi_tlb_info *tinfo); > > + > > + /** Platform hook for custom VS-stage TLB flush */ > > + void (*local_hfence_vvma)(struct sbi_tlb_info *tinfo); > > + > > ? /** Initialize platform timer during cold boot */ > > ? int (*timer_init)(void); > > ? > > @@ -313,6 +335,146 @@ static inline u32 sbi_platform_tlb_fifo_num_entries(const struct sbi_platform *p > > ? return sbi_hart_count(); > > ?} > > ? > > +/** > > + * Platform hook for custom fence.i > > + * > > + * @param plat pointer to struct sbi_platform > > + * @paeam tinfo pointer to struct sbi_tlb_info > > + * > > + * @return 0 indicates that the platform supports the operation and the > > + * operation was successful; otherwise, a normal operation needs to be performed. > > + */ > > +static inline u32 sbi_platform_local_fence_i( > > + const struct sbi_platform *plat, > > + struct sbi_tlb_info *tinfo) > > +{ > > + if (plat && sbi_platform_ops(plat)->local_fence_i) { > > + sbi_platform_ops(plat)->local_fence_i(tinfo); > > + return 0; > > + } > > + return SBI_ENOTSUPP; > > +} > > + > > +/** > > + * Platform hook for custom supervisor-mode TLB flush > > + * > > + * @param plat pointer to struct sbi_platform > > + * @paeam tinfo pointer to struct sbi_tlb_info > > + * > > + * @return 0 indicates that the platform supports the operation and the > > + * operation was successful; otherwise, a normal operation needs to be performed. > > + */ > > +static inline u32 sbi_platform_local_sfence_vma( > > + const struct sbi_platform *plat, > > + struct sbi_tlb_info *tinfo) > > +{ > > + if (plat && sbi_platform_ops(plat)->local_sfence_vma) { > > + sbi_platform_ops(plat)->local_sfence_vma(tinfo); > > + return 0; > > + } > > + return SBI_ENOTSUPP; > > +} > > + > > +/** > > + * Platform hook for custom supervisor-mode TLB flush with ASID > > + * > > + * @param plat pointer to struct sbi_platform > > + * @paeam tinfo pointer to struct sbi_tlb_info > > + * > > + * @return 0 indicates that the platform supports the operation and the > > + * operation was successful; otherwise, a normal operation needs to be performed. > > + */ > > +static inline u32 sbi_platform_local_sfence_vma_asid( > > + const struct sbi_platform *plat, > > + struct sbi_tlb_info *tinfo) > > +{ > > + if (plat && sbi_platform_ops(plat)->local_sfence_vma_asid) { > > + sbi_platform_ops(plat)->local_sfence_vma_asid(tinfo); > > + return 0; > > + } > > + return SBI_ENOTSUPP; > > +} > > + > > +/** > > + * Platform hook for custom G-stage TLB flush for a specific VMID > > + * > > + * @param plat pointer to struct sbi_platform > > + * @paeam tinfo pointer to struct sbi_tlb_info > > + * > > + * @return 0 indicates that the platform supports the operation and the > > + * operation was successful; otherwise, a normal operation needs to be performed. > > + */ > > +static inline u32 sbi_platform_local_hfence_gvma_vmid( > > + const struct sbi_platform *plat, > > + struct sbi_tlb_info *tinfo) > > +{ > > + if (plat && sbi_platform_ops(plat)->local_hfence_gvma_vmid) { > > + sbi_platform_ops(plat)->local_hfence_gvma_vmid(tinfo); > > + return 0; > > + } > > + return SBI_ENOTSUPP; > > +} > > + > > +/** > > + * Platform hook for custom G-stage TLB flush > > + * > > + * @param plat pointer to struct sbi_platform > > + * @paeam tinfo pointer to struct sbi_tlb_info > > + * > > + * @return 0 indicates that the platform supports the operation and the > > + * operation was successful; otherwise, a normal operation needs to be performed. > > + */ > > +static inline u32 sbi_platform_local_hfence_gvma( > > + const struct sbi_platform *plat, > > + struct sbi_tlb_info *tinfo) > > +{ > > + if (plat && sbi_platform_ops(plat)->local_hfence_gvma) { > > + sbi_platform_ops(plat)->local_hfence_gvma(tinfo); > > + return 0; > > + } > > + return SBI_ENOTSUPP; > > +} > > + > > +/** > > + * Platform hook for custom VS-stage TLB flush with ASID > > + * > > + * @param plat pointer to struct sbi_platform > > + * @paeam tinfo pointer to struct sbi_tlb_info > > + * > > + * @return 0 indicates that the platform supports the operation and the > > + * operation was successful; otherwise, a normal operation needs to be performed. > > + */ > > +static inline u32 sbi_platform_local_hfence_vvma_asid( > > + const struct sbi_platform *plat, > > + struct sbi_tlb_info *tinfo) > > +{ > > + if (plat && sbi_platform_ops(plat)->local_hfence_vvma_asid) { > > + sbi_platform_ops(plat)->local_hfence_vvma_asid(tinfo); > > + return 0; > > + } > > + return SBI_ENOTSUPP; > > +} > > + > > +/** > > + * Platform hook for custom VS-stage TLB flush > > + * > > + * @param plat pointer to struct sbi_platform > > + * @paeam tinfo pointer to struct sbi_tlb_info > > + * > > + * @return 0 indicates that the platform supports the operation and the > > + * operation was successful; otherwise, a normal operation needs to be performed. > > + */ > > +static inline u32 sbi_platform_local_hfence_vvma( > > + const struct sbi_platform *plat, > > + struct sbi_tlb_info *tinfo) > > +{ > > + if (plat && sbi_platform_ops(plat)->local_hfence_vvma) { > > + sbi_platform_ops(plat)->local_hfence_vvma(tinfo); > > + return 0; > > + } > > + return SBI_ENOTSUPP; > > +} > > + > > ?/** > > ? * Get total number of HARTs supported by the platform > > ? * > > diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c > > index ada60c32..69b8fb48 100644 > > --- a/lib/sbi/sbi_tlb.c > > +++ b/lib/sbi/sbi_tlb.c > > @@ -46,6 +46,10 @@ static void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo) > > ? hgatp = csr_swap(CSR_HGATP, > > ? (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); > > ? > > + if (sbi_platform_local_hfence_gvma( > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > + gito done; > > + > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > ? __sbi_hfence_vvma_all(); > > ? goto done; > > @@ -67,6 +71,10 @@ static void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo) > > ? > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_RCVD); > > ? > > + if (sbi_platform_local_hfence_gvma( > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > + return; > > + > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > ? __sbi_hfence_gvma_all(); > > ? return; > > @@ -85,6 +93,10 @@ static void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo) > > ? > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_RCVD); > > ? > > + if (sbi_platform_local_sfence_vma( > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > + return; > > + > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > ? __sbi_sfence_vma_all(); > > ? return; > > @@ -111,6 +123,10 @@ static void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo) > > ? hgatp = csr_swap(CSR_HGATP, > > ? (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); > > ? > > + if (sbi_platform_local_hfence_vvma_asid( > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > + goto done; > > + > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > ? __sbi_hfence_vvma_asid(asid); > > ? goto done; > > @@ -133,6 +149,10 @@ static void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo) > > ? > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD); > > ? > > + if (sbi_platform_local_hfence_gvma_vmid( > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > + return; > > + > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > ? __sbi_hfence_gvma_vmid(vmid); > > ? return; > > @@ -152,6 +172,10 @@ static void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo) > > ? > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_RCVD); > > ? > > + if (sbi_platform_local_sfence_vma_asid( > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > + return; > > + > > ? /* Flush entire MM context for a given ASID */ > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > ? __asm__ __volatile__("sfence.vma x0, %0" > > @@ -173,6 +197,10 @@ static void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo) > > ?{ > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_FENCE_I_RECVD); > > ? > > + if (sbi_platform_local_fence_i( > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > + return; > > + > > ? __asm__ __volatile("fence.i"); > > ?} > > ? > > I suggest split the following as a separate patch. > > > diff --git a/platform/generic/include/thead/c9xx_errata.h b/platform/generic/include/thead/c9xx_errata.h > > index 40c1587b..7a419e41 100644 > > --- a/platform/generic/include/thead/c9xx_errata.h > > +++ b/platform/generic/include/thead/c9xx_errata.h > > @@ -8,6 +8,7 @@ > > ? */ > > ?#define THEAD_QUIRK_ERRATA_TLB_FLUSH BIT(0) > > ?#define THEAD_QUIRK_ERRATA_THEAD_PMU BIT(1) > > +#define THEAD_QUIRK_ERRATA_JTLB BIT(2) > > ? > > ?void thead_register_tlb_flush_trap_handler(void); > > ? > > diff --git a/platform/generic/thead/thead-generic.c b/platform/generic/thead/thead-generic.c > > index ddb4f0bf..86793ab1 100644 > > --- a/platform/generic/thead/thead-generic.c > > +++ b/platform/generic/thead/thead-generic.c > > @@ -13,6 +13,7 @@ > > ?#include > > ?#include > > ?#include > > +#include > > ?#include > > ? > > ?struct thead_generic_quirks { > > @@ -39,6 +40,20 @@ static int thead_pmu_extensions_init(struct sbi_hart_features *hfeatures) > > ? return 0; > > ?} > > ? > > +static void thead_jtlb_local_sfence_vma(struct sbi_tlb_info *tinfo) > > +{ > > + __asm__ __volatile__("sfence.vma"); > > +} > > + > > +static void thead_jtlb_local_sfence_vvma_asid(struct sbi_tlb_info *tinfo) > > +{ > > + /* Flush entire MM context for a given ASID */ > > + __asm__ __volatile__("sfence.vma x0, %0" > > + ???? : > > + ???? : "r"(tinfo->asid) > > + ???? : "memory"); > > +} > > + > > ?static int thead_generic_platform_init(const void *fdt, int nodeoff, > > ? ?????? const struct fdt_match *match) > > ?{ > > @@ -49,6 +64,11 @@ static int thead_generic_platform_init(const void *fdt, int nodeoff, > > ? if (quirks->errata & THEAD_QUIRK_ERRATA_THEAD_PMU) > > ? generic_platform_ops.extensions_init = thead_pmu_extensions_init; > > ? > > + if (quirks->errata &THEAD_QUIRK_ERRATA_JTLB) { > > + generic_platform_ops.local_sfence_vma = thead_jtlb_local_sfence_vma; > > + generic_platform_ops.local_sfence_vma_asid = thead_jtlb_local_sfence_vvma_asid; > > + } > > + > > ? return 0; > > ?} > > ? > > @@ -60,13 +80,17 @@ static const struct thead_generic_quirks thead_pmu_quirks = { > > ? .errata = THEAD_QUIRK_ERRATA_THEAD_PMU, > > ?}; > > ? > > +static const struct thead_generic_quirks thead_pmu_jtlb_quirks = { > > + .errata = THEAD_QUIRK_ERRATA_THEAD_PMU | THEAD_QUIRK_ERRATA_JTLB, > > +}; > > + > > IIRC, it is better to not setting THEAD_QUIRK_ERRATA_THEAD_PMU anymore > for sg2044. The T-HEAD specific PMU feature is bad for the cpu version of > the sg2044. Thanks for the feedback. IIRC, you're right that the T-HEAD specific PMU feature may not be suitable? for the current SG2044 CPU version. However, since this patch is focused on TLB customization for the JTLB errata,? I think it's better to keep it independent. Could you please send the PMU quirk? change as a separate patch? That would make both changes cleaner and easier to? review. Best regards, Xiang W > > > ?static const struct fdt_match thead_generic_match[] = { > > ? { .compatible = "canaan,kendryte-k230", .data = &thead_pmu_quirks }, > > ? { .compatible = "sophgo,cv1800b", .data = &thead_pmu_quirks }, > > ? { .compatible = "sophgo,cv1812h", .data = &thead_pmu_quirks }, > > ? { .compatible = "sophgo,sg2000", .data = &thead_pmu_quirks }, > > ? { .compatible = "sophgo,sg2002", .data = &thead_pmu_quirks }, > > - { .compatible = "sophgo,sg2044", .data = &thead_pmu_quirks }, > > + { .compatible = "sophgo,sg2044", .data = &thead_pmu_jtlb_quirks }, > > ? { .compatible = "thead,th1520", .data = &thead_th1520_quirks }, > > ? { }, > > ?}; > > -- > > 2.47.3 > > > > > > -- > > opensbi mailing list > > opensbi at lists.infradead.org > > http://lists.infradead.org/mailman/listinfo/opensbi From inochiama at gmail.com Fri May 29 02:25:49 2026 From: inochiama at gmail.com (Inochi Amaoto) Date: Fri, 29 May 2026 17:25:49 +0800 Subject: [PATCH] platform: Allow platforms to customize TLB operations In-Reply-To: <3518a37ebaaf7c8f4eb3afdcbf8c888bbae09047.camel@iscas.ac.cn> References: <20260529073510.97299-1-wangxiang@iscas.ac.cn> <3518a37ebaaf7c8f4eb3afdcbf8c888bbae09047.camel@iscas.ac.cn> Message-ID: On Fri, May 29, 2026 at 05:24:17PM +0800, Xiang W wrote: > ? 2026-05-29?? 16:56 +0800?Inochi Amaoto??? > > On Fri, May 29, 2026 at 03:35:10PM +0800, Xiang W wrote: > > > Some T-Head based processors, for example the Sophgo SG2044, have a > > > JTLB errata that requires special handling of certain TLB maintenance > > > instructions (sfence.vma). > > > > > > Enable the JTLB workaround for affected boards. > > > > > > Signed-off-by: Xiang W > > > Signed-off-by: Han Gao > > > --- > > > ?include/sbi/sbi_platform.h?????????????????? | 162 +++++++++++++++++++ > > > ?lib/sbi/sbi_tlb.c??????????????????????????? |? 28 ++++ > > > ?platform/generic/include/thead/c9xx_errata.h |?? 1 + > > > ?platform/generic/thead/thead-generic.c?????? |? 26 ++- > > > ?4 files changed, 216 insertions(+), 1 deletion(-) > > > > > > diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h > > > index fe382b56..911b37bf 100644 > > > --- a/include/sbi/sbi_platform.h > > > +++ b/include/sbi/sbi_platform.h > > > @@ -56,6 +56,7 @@ struct sbi_domain_memregion; > > > ?struct sbi_ecall_return; > > > ?struct sbi_trap_regs; > > > ?struct sbi_hart_features; > > > +struct sbi_tlb_info; > > > ?union sbi_ldst_data; > > > ? > > > ?/** Possible feature flags of a platform */ > > > @@ -125,6 +126,27 @@ struct sbi_platform_operations { > > > ? /** Get tlb fifo num entries*/ > > > ? u32 (*get_tlb_num_entries)(void); > > > ? > > > + /** Platform hook for custom fence.i */ > > > + void (*local_fence_i)(struct sbi_tlb_info *tinfo); > > > + > > > + /** Platform hook for custom supervisor-mode TLB flush */ > > > + void (*local_sfence_vma)(struct sbi_tlb_info *tinfo); > > > + > > > + /** Platform hook for custom supervisor-mode TLB flush with ASID */ > > > + void (*local_sfence_vma_asid)(struct sbi_tlb_info *tinfo); > > > + > > > + /** Platform hook for custom G-stage TLB flush for a specific VMID */ > > > + void (*local_hfence_gvma_vmid)(struct sbi_tlb_info *tinfo); > > > + > > > + /** Platform hook for custom G-stage TLB flush */ > > > + void (*local_hfence_gvma)(struct sbi_tlb_info *tinfo); > > > + > > > + /** Platform hook for custom VS-stage TLB flush with ASID */ > > > + void (*local_hfence_vvma_asid)(struct sbi_tlb_info *tinfo); > > > + > > > + /** Platform hook for custom VS-stage TLB flush */ > > > + void (*local_hfence_vvma)(struct sbi_tlb_info *tinfo); > > > + > > > ? /** Initialize platform timer during cold boot */ > > > ? int (*timer_init)(void); > > > ? > > > @@ -313,6 +335,146 @@ static inline u32 sbi_platform_tlb_fifo_num_entries(const struct sbi_platform *p > > > ? return sbi_hart_count(); > > > ?} > > > ? > > > +/** > > > + * Platform hook for custom fence.i > > > + * > > > + * @param plat pointer to struct sbi_platform > > > + * @paeam tinfo pointer to struct sbi_tlb_info > > > + * > > > + * @return 0 indicates that the platform supports the operation and the > > > + * operation was successful; otherwise, a normal operation needs to be performed. > > > + */ > > > +static inline u32 sbi_platform_local_fence_i( > > > + const struct sbi_platform *plat, > > > + struct sbi_tlb_info *tinfo) > > > +{ > > > + if (plat && sbi_platform_ops(plat)->local_fence_i) { > > > + sbi_platform_ops(plat)->local_fence_i(tinfo); > > > + return 0; > > > + } > > > + return SBI_ENOTSUPP; > > > +} > > > + > > > +/** > > > + * Platform hook for custom supervisor-mode TLB flush > > > + * > > > + * @param plat pointer to struct sbi_platform > > > + * @paeam tinfo pointer to struct sbi_tlb_info > > > + * > > > + * @return 0 indicates that the platform supports the operation and the > > > + * operation was successful; otherwise, a normal operation needs to be performed. > > > + */ > > > +static inline u32 sbi_platform_local_sfence_vma( > > > + const struct sbi_platform *plat, > > > + struct sbi_tlb_info *tinfo) > > > +{ > > > + if (plat && sbi_platform_ops(plat)->local_sfence_vma) { > > > + sbi_platform_ops(plat)->local_sfence_vma(tinfo); > > > + return 0; > > > + } > > > + return SBI_ENOTSUPP; > > > +} > > > + > > > +/** > > > + * Platform hook for custom supervisor-mode TLB flush with ASID > > > + * > > > + * @param plat pointer to struct sbi_platform > > > + * @paeam tinfo pointer to struct sbi_tlb_info > > > + * > > > + * @return 0 indicates that the platform supports the operation and the > > > + * operation was successful; otherwise, a normal operation needs to be performed. > > > + */ > > > +static inline u32 sbi_platform_local_sfence_vma_asid( > > > + const struct sbi_platform *plat, > > > + struct sbi_tlb_info *tinfo) > > > +{ > > > + if (plat && sbi_platform_ops(plat)->local_sfence_vma_asid) { > > > + sbi_platform_ops(plat)->local_sfence_vma_asid(tinfo); > > > + return 0; > > > + } > > > + return SBI_ENOTSUPP; > > > +} > > > + > > > +/** > > > + * Platform hook for custom G-stage TLB flush for a specific VMID > > > + * > > > + * @param plat pointer to struct sbi_platform > > > + * @paeam tinfo pointer to struct sbi_tlb_info > > > + * > > > + * @return 0 indicates that the platform supports the operation and the > > > + * operation was successful; otherwise, a normal operation needs to be performed. > > > + */ > > > +static inline u32 sbi_platform_local_hfence_gvma_vmid( > > > + const struct sbi_platform *plat, > > > + struct sbi_tlb_info *tinfo) > > > +{ > > > + if (plat && sbi_platform_ops(plat)->local_hfence_gvma_vmid) { > > > + sbi_platform_ops(plat)->local_hfence_gvma_vmid(tinfo); > > > + return 0; > > > + } > > > + return SBI_ENOTSUPP; > > > +} > > > + > > > +/** > > > + * Platform hook for custom G-stage TLB flush > > > + * > > > + * @param plat pointer to struct sbi_platform > > > + * @paeam tinfo pointer to struct sbi_tlb_info > > > + * > > > + * @return 0 indicates that the platform supports the operation and the > > > + * operation was successful; otherwise, a normal operation needs to be performed. > > > + */ > > > +static inline u32 sbi_platform_local_hfence_gvma( > > > + const struct sbi_platform *plat, > > > + struct sbi_tlb_info *tinfo) > > > +{ > > > + if (plat && sbi_platform_ops(plat)->local_hfence_gvma) { > > > + sbi_platform_ops(plat)->local_hfence_gvma(tinfo); > > > + return 0; > > > + } > > > + return SBI_ENOTSUPP; > > > +} > > > + > > > +/** > > > + * Platform hook for custom VS-stage TLB flush with ASID > > > + * > > > + * @param plat pointer to struct sbi_platform > > > + * @paeam tinfo pointer to struct sbi_tlb_info > > > + * > > > + * @return 0 indicates that the platform supports the operation and the > > > + * operation was successful; otherwise, a normal operation needs to be performed. > > > + */ > > > +static inline u32 sbi_platform_local_hfence_vvma_asid( > > > + const struct sbi_platform *plat, > > > + struct sbi_tlb_info *tinfo) > > > +{ > > > + if (plat && sbi_platform_ops(plat)->local_hfence_vvma_asid) { > > > + sbi_platform_ops(plat)->local_hfence_vvma_asid(tinfo); > > > + return 0; > > > + } > > > + return SBI_ENOTSUPP; > > > +} > > > + > > > +/** > > > + * Platform hook for custom VS-stage TLB flush > > > + * > > > + * @param plat pointer to struct sbi_platform > > > + * @paeam tinfo pointer to struct sbi_tlb_info > > > + * > > > + * @return 0 indicates that the platform supports the operation and the > > > + * operation was successful; otherwise, a normal operation needs to be performed. > > > + */ > > > +static inline u32 sbi_platform_local_hfence_vvma( > > > + const struct sbi_platform *plat, > > > + struct sbi_tlb_info *tinfo) > > > +{ > > > + if (plat && sbi_platform_ops(plat)->local_hfence_vvma) { > > > + sbi_platform_ops(plat)->local_hfence_vvma(tinfo); > > > + return 0; > > > + } > > > + return SBI_ENOTSUPP; > > > +} > > > + > > > ?/** > > > ? * Get total number of HARTs supported by the platform > > > ? * > > > diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c > > > index ada60c32..69b8fb48 100644 > > > --- a/lib/sbi/sbi_tlb.c > > > +++ b/lib/sbi/sbi_tlb.c > > > @@ -46,6 +46,10 @@ static void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo) > > > ? hgatp = csr_swap(CSR_HGATP, > > > ? (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); > > > ? > > > + if (sbi_platform_local_hfence_gvma( > > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > > + gito done; > > > + > > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > > ? __sbi_hfence_vvma_all(); > > > ? goto done; > > > @@ -67,6 +71,10 @@ static void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo) > > > ? > > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_RCVD); > > > ? > > > + if (sbi_platform_local_hfence_gvma( > > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > > + return; > > > + > > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > > ? __sbi_hfence_gvma_all(); > > > ? return; > > > @@ -85,6 +93,10 @@ static void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo) > > > ? > > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_RCVD); > > > ? > > > + if (sbi_platform_local_sfence_vma( > > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > > + return; > > > + > > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > > ? __sbi_sfence_vma_all(); > > > ? return; > > > @@ -111,6 +123,10 @@ static void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo) > > > ? hgatp = csr_swap(CSR_HGATP, > > > ? (vmid << HGATP_VMID_SHIFT) & HGATP_VMID_MASK); > > > ? > > > + if (sbi_platform_local_hfence_vvma_asid( > > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > > + goto done; > > > + > > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > > ? __sbi_hfence_vvma_asid(asid); > > > ? goto done; > > > @@ -133,6 +149,10 @@ static void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo) > > > ? > > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD); > > > ? > > > + if (sbi_platform_local_hfence_gvma_vmid( > > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > > + return; > > > + > > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > > ? __sbi_hfence_gvma_vmid(vmid); > > > ? return; > > > @@ -152,6 +172,10 @@ static void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo) > > > ? > > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_RCVD); > > > ? > > > + if (sbi_platform_local_sfence_vma_asid( > > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > > + return; > > > + > > > ? /* Flush entire MM context for a given ASID */ > > > ? if ((start == 0 && size == 0) || (size == SBI_TLB_FLUSH_ALL)) { > > > ? __asm__ __volatile__("sfence.vma x0, %0" > > > @@ -173,6 +197,10 @@ static void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo) > > > ?{ > > > ? sbi_pmu_ctr_incr_fw(SBI_PMU_FW_FENCE_I_RECVD); > > > ? > > > + if (sbi_platform_local_fence_i( > > > + sbi_platform_thishart_ptr(), tinfo) == SBI_OK) > > > + return; > > > + > > > ? __asm__ __volatile("fence.i"); > > > ?} > > > ? > > > > I suggest split the following as a separate patch. > > > > > diff --git a/platform/generic/include/thead/c9xx_errata.h b/platform/generic/include/thead/c9xx_errata.h > > > index 40c1587b..7a419e41 100644 > > > --- a/platform/generic/include/thead/c9xx_errata.h > > > +++ b/platform/generic/include/thead/c9xx_errata.h > > > @@ -8,6 +8,7 @@ > > > ? */ > > > ?#define THEAD_QUIRK_ERRATA_TLB_FLUSH BIT(0) > > > ?#define THEAD_QUIRK_ERRATA_THEAD_PMU BIT(1) > > > +#define THEAD_QUIRK_ERRATA_JTLB BIT(2) > > > ? > > > ?void thead_register_tlb_flush_trap_handler(void); > > > ? > > > diff --git a/platform/generic/thead/thead-generic.c b/platform/generic/thead/thead-generic.c > > > index ddb4f0bf..86793ab1 100644 > > > --- a/platform/generic/thead/thead-generic.c > > > +++ b/platform/generic/thead/thead-generic.c > > > @@ -13,6 +13,7 @@ > > > ?#include > > > ?#include > > > ?#include > > > +#include > > > ?#include > > > ? > > > ?struct thead_generic_quirks { > > > @@ -39,6 +40,20 @@ static int thead_pmu_extensions_init(struct sbi_hart_features *hfeatures) > > > ? return 0; > > > ?} > > > ? > > > +static void thead_jtlb_local_sfence_vma(struct sbi_tlb_info *tinfo) > > > +{ > > > + __asm__ __volatile__("sfence.vma"); > > > +} > > > + > > > +static void thead_jtlb_local_sfence_vvma_asid(struct sbi_tlb_info *tinfo) > > > +{ > > > + /* Flush entire MM context for a given ASID */ > > > + __asm__ __volatile__("sfence.vma x0, %0" > > > + ???? : > > > + ???? : "r"(tinfo->asid) > > > + ???? : "memory"); > > > +} > > > + > > > ?static int thead_generic_platform_init(const void *fdt, int nodeoff, > > > ? ?????? const struct fdt_match *match) > > > ?{ > > > @@ -49,6 +64,11 @@ static int thead_generic_platform_init(const void *fdt, int nodeoff, > > > ? if (quirks->errata & THEAD_QUIRK_ERRATA_THEAD_PMU) > > > ? generic_platform_ops.extensions_init = thead_pmu_extensions_init; > > > ? > > > + if (quirks->errata &THEAD_QUIRK_ERRATA_JTLB) { > > > + generic_platform_ops.local_sfence_vma = thead_jtlb_local_sfence_vma; > > > + generic_platform_ops.local_sfence_vma_asid = thead_jtlb_local_sfence_vvma_asid; > > > + } > > > + > > > ? return 0; > > > ?} > > > ? > > > @@ -60,13 +80,17 @@ static const struct thead_generic_quirks thead_pmu_quirks = { > > > ? .errata = THEAD_QUIRK_ERRATA_THEAD_PMU, > > > ?}; > > > ? > > > +static const struct thead_generic_quirks thead_pmu_jtlb_quirks = { > > > + .errata = THEAD_QUIRK_ERRATA_THEAD_PMU | THEAD_QUIRK_ERRATA_JTLB, > > > +}; > > > + > > > > IIRC, it is better to not setting THEAD_QUIRK_ERRATA_THEAD_PMU anymore > > for sg2044. The T-HEAD specific PMU feature is bad for the cpu version of > > the sg2044. > Thanks for the feedback. > > IIRC, you're right that the T-HEAD specific PMU feature may not be suitable? > for the current SG2044 CPU version. > > However, since this patch is focused on TLB customization for the JTLB errata,? > I think it's better to keep it independent. Could you please send the PMU quirk? > change as a separate patch? That would make both changes cleaner and easier to? > review. > > Best regards, > Xiang W It is fine for me to fix it. I will send the fix patch after this is merged. Regards, Inochi > > > > > ?static const struct fdt_match thead_generic_match[] = { > > > ? { .compatible = "canaan,kendryte-k230", .data = &thead_pmu_quirks }, > > > ? { .compatible = "sophgo,cv1800b", .data = &thead_pmu_quirks }, > > > ? { .compatible = "sophgo,cv1812h", .data = &thead_pmu_quirks }, > > > ? { .compatible = "sophgo,sg2000", .data = &thead_pmu_quirks }, > > > ? { .compatible = "sophgo,sg2002", .data = &thead_pmu_quirks }, > > > - { .compatible = "sophgo,sg2044", .data = &thead_pmu_quirks }, > > > + { .compatible = "sophgo,sg2044", .data = &thead_pmu_jtlb_quirks }, > > > ? { .compatible = "thead,th1520", .data = &thead_th1520_quirks }, > > > ? { }, > > > ?}; > > > -- > > > 2.47.3 > > > > > > > > > -- > > > opensbi mailing list > > > opensbi at lists.infradead.org > > > http://lists.infradead.org/mailman/listinfo/opensbi > > > -- > opensbi mailing list > opensbi at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/opensbi From mpe at kernel.org Fri May 29 06:02:42 2026 From: mpe at kernel.org (Michael Ellerman) Date: Fri, 29 May 2026 23:02:42 +1000 Subject: [PATCH] include: sbi_platform: Increase default stack size In-Reply-To: References: Message-ID: On 25/5/26 11:23 pm, Anirudh Srinivasan wrote: > Hi Vivian, > > On Sun, May 24, 2026 at 12:53?AM Vivian Wang wrote: >> >> sbi_misaligned_v_ld_emulator() has a local buffer (namely mask) which is >> 8192 bytes in size, so the default stack size is not enough. Double it >> to 16384. >> >> This stack overflow can be observed by any S-mode software and leads to >> M-mode crash or unexpected behavior, as long as the S-mode software does >> not enable FWFT misaligned delegation [1]. This may be considered a >> temporary fix until further fixes are made to misaligned handling [2]. >> >> Link: https://lore.kernel.org/linux-riscv/nrvt74qnojaubiwjo37ums4lnclu466hovwrhmtbag6f5uhrql at q6msoe2oto4b # [1] >> Link: https://lore.kernel.org/opensbi/20260210094044.72591-1-ganboing at gmail.com # [2] >> Signed-off-by: Vivian Wang > > There was a similar patch [1] from few days ago that makes this a > Kconfig option. Either option would work to fix the bug I encountered. Making the stack size configurable is nice, but it's not really a fix for this bug if the user has to know to increase the size. So some combination of the two patches would be best IMHO. What I'm not clear on is whether just bumping to a 16KB minimum always is acceptable. Are there folks running on memory constrained systems that don't want stack that big? If you're not building the vector emulation code in then 8KB is presumably fine. cheers From mpe at kernel.org Fri May 29 07:42:18 2026 From: mpe at kernel.org (Michael Ellerman) Date: Sat, 30 May 2026 00:42:18 +1000 Subject: [PATCH] lib: sbi_trap_v_ldst: Redirect unhandled traps Message-ID: <20260530-trap-redirect-v1-1-45d4d333d8c9@kernel.org> When SBI is built with a compiler that doesn't support vector, the misaligned vector load/store emulation is not built in, the handlers are just stubs. Currently the stubs just return 0, causing sbi_trap_emulate_load() to return without incrementing mepc, meaning the instruction will just fault again, an infinite loop. Fix the stubs to use sbi_trap_redirect(), which forwards the trap to the previous mode, allowing it to be handled there. Fixes: c2acc5e5 ("lib: sbi_misaligned_ldst: Add handling of vector load/store") Signed-off-by: Michael Ellerman --- lib/sbi/sbi_trap_v_ldst.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/sbi/sbi_trap_v_ldst.c b/lib/sbi/sbi_trap_v_ldst.c index f4d469dcf28884d0eecb4fdec61eee25895d55a0..1e4336f9db23e7b972d3430cdc52addb0fcdb817 100644 --- a/lib/sbi/sbi_trap_v_ldst.c +++ b/lib/sbi/sbi_trap_v_ldst.c @@ -334,11 +334,13 @@ int sbi_misaligned_v_st_emulator(int wlen, union sbi_ldst_data in_val, int sbi_misaligned_v_ld_emulator(int rlen, union sbi_ldst_data *out_val, struct sbi_trap_context *tcntx) { - return 0; + /* Unable to emulate, send trap to previous mode. */ + return sbi_trap_redirect(&tcntx->regs, &tcntx->trap); } int sbi_misaligned_v_st_emulator(int wlen, union sbi_ldst_data in_val, struct sbi_trap_context *tcntx) { - return 0; + /* Unable to emulate, send trap to previous mode. */ + return sbi_trap_redirect(&tcntx->regs, &tcntx->trap); } #endif /* OPENSBI_CC_SUPPORT_VECTOR */ --- base-commit: 547a5bbda7c3ec0096a6c87809851f8c2df047d1 change-id: 20260530-trap-redirect-4a66fae8aaff