Skip to content

Conversation

@wxjstz
Copy link

@wxjstz wxjstz commented Dec 31, 2025

Summary by Sourcery

Add TH1520/light SoC-specific support for power management, hotplug, PMU, PMP, and mailbox-based AON coordination on the T-Head generic platform, integrate XTHEADSSTC timer handling and vendor SBI extensions, and wire the new platform pieces into generic init, trap, timer, and build/CI flows.

New Features:

  • Introduce light_c910 platform support implementing CPU hotplug, system suspend, and low-power entry/exit sequences for TH1520 SoCs.
  • Add TH1520 AON mailbox driver and RPC interface to coordinate CPU power state changes and system suspend with the always-on subsystem.
  • Expose vendor SBI extensions for T-Head PMU and PMP control, including programmable event mapping and DSP TCM/reserved region PMP setup.
  • Support the XTHEADSSTC timer extension in the generic T-Head platform and ACLINT mTimer handling.
  • Add a GitHub Actions workflow to build OpenSBI for the generic platform with both Xuantie and mainline RISC-V toolchains.

Enhancements:

  • Refactor T-Head generic platform hooks to centralize errata handling, vendor extension registration, and cold-boot policy, and to integrate light-specific HSM and suspend devices.
  • Extend trap handling to better deal with T-Head-specific DCACHE call interrupts and add an sfence in the base trap entry path.
  • Extend T-Head C9xx CSR encodings and errata flags to cover MHINT4 and the XTHEADSSTC and light PPU quirks.
  • Provide a generic FDT matching helper and use it to detect T-Head light-compatible platforms.
  • Update generic T-Head build objects to include new light-specific and AON/mbox sources.

CI:

  • Add a CI workflow to build the generic OpenSBI firmware image using both Xuantie and mainline RISC-V toolchains and publish the resulting binary as an artifact.

RevySR and others added 17 commits December 31, 2025 15:09
Signed-off-by: Han Gao <rabenda.cn@gmail.com>
add light_ppu for thead,light

refer from: revyos/thead-opensbi@f880f3e

Signed-off-by: Xiang W <wxjstz@126.com>
refer from: revyos/thead-opensbi@f880f3e

Signed-off-by: Xiang W <wxjstz@126.com>
Signed-off-by: Han Gao <gaohan@iscas.ac.cn>
Signed-off-by: Han Gao <gaohan@iscas.ac.cn>
There is a lot of code in light_ext.c that is duplicated in
thead-generic.c, so delete it.

Signed-off-by: Xiang W <wxjstz@126.com>
Signed-off-by: David Li <davidli.li@linux.alibaba.com>
Signed-off-by: David Li <davidli.li@linux.alibaba.com>
Signed-off-by: David Li <davidli.li@linux.alibaba.com>
Signed-off-by: Esther Z <Esther.Z@linux.aliabab.com>
Signed-off-by: Han Gao <gaohan@iscas.ac.cn>
Signed-off-by: Han Gao <gaohan@iscas.ac.cn>
Signed-off-by: Han Gao <rabenda.cn@gmail.com>
Signed-off-by: Xiang W <wxjstz@126.com>
Signed-off-by: Xiang W <wxjstz@126.com>
Signed-off-by: Xiang W <wxjstz@126.com>
The mailbox binding for it isn't properly defined.

Hardcode it instead of requireing a non-standard DT node, and remove the
non-upstream "mbox" code from Xuantie (which is incompatible with later
upstream mailbox code).

The proper AON dt compatible string "thead,th1520-aon" is also added,
and a bogus ipc.h header file is cleaned up.

Signed-off-by: Icenowy Zheng <uwu@icenowy.me>
@sourcery-ai
Copy link

sourcery-ai bot commented Dec 31, 2025

Reviewer's Guide

Adds TH1520 v1.8 platform support for T-HEAD Light/TH1520 SoCs, including low-power / suspend and CPU hotplug via AON mailbox, vendor PMU/PMP SBI extensions, XThead SSTC timer handling, and CI build workflow, while refactoring generic T-HEAD platform hooks to use errata flags and new light-specific implementations.

Sequence diagram for TH1520 AON CPU hotplug via mailbox

sequenceDiagram
    actor S_Mode_OS
    participant SBI_HSM as SBI_HSM
    participant light_ppu as light_ppu
    participant thead_aon as thead_aon
    participant th1520_mbox as th1520_mbox
    participant AON_Firmware as AON_Firmware

    S_Mode_OS->>SBI_HSM: request hart_stop(hartid)
    SBI_HSM->>light_ppu: hart_stop(hartid)
    alt hart_is_core0
        light_ppu-->>SBI_HSM: log not_available_for_hotplug
        SBI_HSM-->>S_Mode_OS: error
    else auxiliary_core
        light_ppu->>thead_aon: thead_aon_cpuhp(hartid, OFFLINE)
        thead_aon->>th1520_mbox: th1520_mbox_write(AON_CHANNEL, cpu_off_msg)
        th1520_mbox->>AON_Firmware: send_cpu_off_request
        AON_Firmware-->>th1520_mbox: TH1520_MBOX_ACK_MAGIC
        th1520_mbox-->>thead_aon: success
        thead_aon-->>light_ppu: success
        light_ppu->>light_ppu: thead_aon_cpuhp(current_hartid, OFFLINE)
        light_ppu->>light_ppu: light_auxcore_save()
        light_ppu->>light_ppu: wfi()
        light_ppu->>SBI_HSM: sbi_hart_hang()
    end

    S_Mode_OS->>SBI_HSM: request hart_start(hartid)
    SBI_HSM->>light_ppu: hart_start(hartid, saddr)
    alt core0
        light_ppu->>SBI_HSM: sbi_ipi_raw_send(hartindex, true)
        SBI_HSM-->>S_Mode_OS: success
    else auxiliary_core
        light_ppu->>light_ppu: light_auxcore_restore(hartid)
        light_ppu->>thead_aon: thead_aon_cpuhp(hartid, ONLINE)
        thead_aon->>th1520_mbox: th1520_mbox_write(AON_CHANNEL, cpu_on_msg)
        th1520_mbox->>AON_Firmware: send_cpu_on_request
        AON_Firmware-->>th1520_mbox: TH1520_MBOX_ACK_MAGIC
        th1520_mbox-->>thead_aon: success
        thead_aon-->>light_ppu: success
        light_ppu-->>SBI_HSM: success
        SBI_HSM-->>S_Mode_OS: success
    end
Loading

Sequence diagram for vendor PMU/PMP SBI ecall light handling

sequenceDiagram
    actor S_Mode_OS
    participant SBI_ECALL as SBI_ECALL
    participant ecall_light as ecall_light
    participant PMU_Handler as sbi_thead_pmu_set
    participant PMP_Handler as sbi_thead_pmp_set
    participant CSR_Hardware as CSR_HW
    participant PMP_HW as PMP_HW

    S_Mode_OS->>SBI_ECALL: ecall(extid=SBI_EXT_VENDOR_PMU, a0=type, a1=idx, a2=event_id)
    SBI_ECALL->>ecall_light: handle(extid, funcid, regs, out)
    alt extid_is_SBI_EXT_VENDOR_PMU
        ecall_light->>PMU_Handler: sbi_thead_pmu_set(type, idx, event_id)
        alt type_equals_2
            PMU_Handler->>PMU_Handler: sbi_thead_pmu_map(idx, event_id)
            PMU_Handler->>CSR_Hardware: csr_write(MHPMEVENT[idx], event_id)
        else other_type
            PMU_Handler->>PMU_Handler: sbi_thead_pmu_init()
            PMU_Handler->>CSR_Hardware: configure_MIDELEG_MCOUNTERWEN_MHPMEVENT3_28
        end
        PMU_Handler-->>ecall_light: success
    else extid_is_SBI_EXT_VENDOR_PMP
        ecall_light->>PMP_Handler: sbi_thead_pmp_set(funcid, regs->a0)
        PMP_Handler->>PMP_HW: ensure_reserved_region_pmp_entry28
        alt funcid_equals_0
            PMP_Handler->>PMP_HW: configure_TCM0_PMP_entry26(auth)
        else funcid_equals_1
            PMP_Handler->>PMP_HW: configure_TCM1_PMP_entry27(auth)
        end
        PMP_Handler-->>ecall_light: success
    end
    ecall_light-->>SBI_ECALL: out->value=0
    SBI_ECALL-->>S_Mode_OS: return_success
Loading

Class diagram for TH1520 AON, mailbox, and Light platform integration

classDiagram
    class thead_generic_quirks {
        u64 errata
    }

    class generic_platform_ops {
        +early_init(cold_boot)
        +extensions_init(hfeatures)
        +final_init(cold_boot)
        +final_exit()
        +vendor_ext_provider(funcid, regs, out)
        +cold_boot_allowed(hartid)
    }

    class thead_generic_platform_init {
        +thead_generic_platform_init(fdt, nodeoff, match)
    }

    thead_generic_quirks <.. thead_generic_platform_init : uses
    thead_generic_platform_init ..> generic_platform_ops : configures

    class th1520_mbox {
        uint32_t *cur_cpu_ch_base
        enum th1520_mbox_icu_cpu_id cur_icu_cpu_id
        uint32_t *comm_local_base[TH1520_MBOX_CHANS]
        uint32_t *comm_remote_base[TH1520_MBOX_CHANS]
        +th1520_mbox_write(ia, remote_channel, buffer, len)
        +th1520_mbox_read(ia, src_channel, buffer, len, timeout_us)
    }

    class thead_aon {
        th1520_mbox *mbox
        +thead_aon_init()
        +thead_aon_cpuhp(cpu, status)
        +thead_aon_system_suspend()
    }

    class th1520_aon_rpc_msg_hdr {
        uint8_t ver
        uint8_t size
        uint8_t svc
        uint8_t func
    }

    class rpc_msg_cpu_info {
        u16 cpu_id
        u16 status
        u32 reserved[5]
    }

    class th1520_aon_rpc_lpm_msg_hdr {
        th1520_aon_rpc_msg_hdr hdr
        rpc_func_t rpc
    }

    class th1520_aon_rpc_ack_common {
        th1520_aon_rpc_msg_hdr hdr
        u8 err_code
    }

    class sbi_hsm_device_light_ppu {
        +const char *name
        +hart_start(hartid, saddr)
        +hart_stop()
    }

    class sbi_system_suspend_device_th1520_susp {
        +const char *name
        +system_suspend_check(sleep_type)
        +system_suspend(suspend_type, resume_addr)
    }

    class light_c910_platform {
        +hotplug_flag
        +cpu_performance_disable()
        +light_mastercore_save()
        +light_auxcore_save()
        +light_auxcore_restore(hartid)
        +light_hart_start(hartid, saddr)
        +light_hart_stop()
        +th1520_system_suspend(suspend_type, resume_addr)
        +th1520_system_suspend_check(sleep_type)
    }

    th1520_mbox <.. thead_aon : contains
    thead_aon <.. light_c910_platform : uses
    th1520_aon_rpc_msg_hdr <|-- th1520_aon_rpc_lpm_msg_hdr
    th1520_aon_rpc_msg_hdr <|-- th1520_aon_rpc_ack_common
    rpc_msg_cpu_info <.. th1520_aon_rpc_lpm_msg_hdr : field
    light_c910_platform <.. sbi_hsm_device_light_ppu : provides_callbacks
    light_c910_platform <.. sbi_system_suspend_device_th1520_susp : provides_callbacks

    class ecall_light_extension {
        +const char *name
        +u64 extid_start
        +u64 extid_end
        +register_extensions()
        +handle(extid, funcid, regs, out)
    }

    class sbi_thead_pmu_pmp_handlers {
        +sbi_thead_pmu_init()
        +sbi_thead_pmu_set(type, idx, event_id)
        +sbi_thead_pmp_set(idx, auth)
    }

    ecall_light_extension ..> sbi_thead_pmu_pmp_handlers : calls
Loading

Flow diagram for ACLINT MTIMER handling with XThead SSTC extension

flowchart TD
    A[start_mtimer_event] --> B[load_sbi_scratch]
    B --> C[check sbi_hart_has_extension scratch SBI_HART_EXT_XTHEADSSTC]
    C -->|true| D[time_cmp_addr = mtimer.mtimecmp_addr + 0x9000]
    C -->|false| E[time_cmp_addr = mtimer.mtimecmp_addr]
    D --> F[compute target_index = target_hart - first_hartid]
    E --> F
    F --> G[write compare value via mtimer.time_wr]
    G --> H[mtimer_event_complete]
Loading

File-Level Changes

Change Details Files
Refactor T-HEAD generic platform init to support TH1520/Light-specific errata, vendor SBI extensions, and power-management callbacks.
  • Replace thead_tlb_flush_early_init and thead_pmu_extensions_init with unified thead_generic_early_init and thead_generic_extensions_init that apply errata flags, save/restore CPU performance CSRs, and configure XTHEADSSTC vs SSTC hart extensions.
  • Add vendor SBI extension IDs for SMC/PMU/PMP and register a new ecall_light extension that routes PMU/PMP requests into platform hooks, with optional enablement based on DT compatible string.
  • Wire new platform ops into generic_platform_ops: early_init, extensions_init, final_init, vendor_ext_provider, cold_boot_allowed, and final_exit, and change th1520 quirks to enable LOGHT_PPU and XTHEADSSTC errata, including support for "thead,light" DT compatibles.
platform/generic/thead/thead-generic.c
platform/generic/include/thead/c9xx_errata.h
platform/generic/include/thead/c9xx_encoding.h
Implement TH1520 Light C910 CPU hotplug and system suspend using AON mailbox and low-power flows.
  • Introduce light_c910.c implementing CSR save/restore and performance disable, master/aux core low-power entry helpers, and sbi_hsm_device callbacks (hart_start/hart_stop) plus sbi_system_suspend_device for suspend-to-RAM on TH1520.
  • Add AON RPC abstraction (thead_aon.[ch]) and th1520_mbox.[ch] mailbox driver to communicate CPU hotplug and system suspend state to the AON subsystem over hardware mailboxes, including basic ACK handling and FDT-based init.
  • Provide Light-specific register definitions for AON, APSYS, APCLK and APRST blocks to control low-power modes and reset/boot vectors of C910 cores.
platform/generic/thead/light_c910.c
platform/generic/thead/thead_aon.c
platform/generic/thead/th1520_mbox.c
platform/generic/include/thead/thead_aon.h
platform/generic/include/thead/light/aonsys_reg_define.h
platform/generic/include/thead/light/apsys_reg_define.h
platform/generic/include/thead/light/apclk_reg_define.h
platform/generic/include/thead/light/aprst_reg_define.h
platform/generic/include/thead/light/asm.h
platform/generic/thead/th1520_mbox.h
platform/generic/thead/objects.mk
Extend timer and trap handling for XThead SSTC and DCACHE.CALL instruction on local interrupts.
  • Add SBI_HART_EXT_XTHEADSSTC enum and registration data, and in generic extensions init, replace standard SSTC with XTHEADSSTC when errata flag is set.
  • Update ACLINT mtimer event_start/event_stop to offset the mtimecmp base address when XTHEADSSTC is present, using sbi_hart_has_extension checks.
  • In sbi_trap_handler, on non-AIA local interrupts, detect the DCACHE.CALL pattern from mepc and emit the corresponding custom instruction before calling the non-AIA IRQ handler.
include/sbi/sbi_hart.h
lib/sbi/sbi_hart.c
lib/utils/timer/aclint_mtimer.c
lib/sbi/sbi_trap.c
platform/generic/include/thead/light/asm.h
platform/generic/thead/thead-generic.c
Add generic FDT helper and integrate TH1520/Light compatibles.
  • Introduce fdt_match_node() helper to search a node against a match table and expose it in the FDT helper header for reuse.
  • Extend the T-HEAD generic platform match table to include "thead,light" mapped to the TH1520 quirks, and plumb the errata value into thead_generic_platform_init for later use.
lib/utils/fdt/fdt_helper.c
include/sbi_utils/fdt/fdt_helper.h
platform/generic/thead/thead-generic.c
Misc platform and CI changes: PMP management, SFENCE in trap entry, and GitHub Actions build workflow.
  • Add PMP programming helpers to configure reserved memory and DSP TCM0/TCM1 regions based on vendor SBI PMP extension calls, using memory-mapped PMP controller registers and hart state checks.
  • Insert an sfence.vma zero,t0 at the start of the assembly trap handler to ensure TLB coherency on traps, and include new T-HEAD objects (light_c910, thead_aon, th1520_mbox) in the platform build.
  • Add xuantie-mainline-opensbi GitHub Actions workflow that installs RISC-V toolchains (Xuantie and mainline GCC), builds generic platform firmware, and uploads fw_dynamic.bin artifacts. Remove the old repo-lockdown workflow.
platform/generic/thead/thead-generic.c
firmware/fw_base.S
platform/generic/thead/objects.mk
.github/workflows/build.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 8 issues, and left some high level feedback:

  • In thead_generic_cold_boot_allowed(), selected_hartid is a u32 initialized to 0 but compared to -1, so the selected_hartid != -1 branch is always taken; clarify the sentinel scheme (or change the type/value) so the cold-boot restriction logic actually reflects intent.
  • In thead_aon_init() the mailbox base addresses are hardcoded into a local regs[] array; consider deriving these from the DT node (e.g. reg properties) so the implementation follows the hardware description instead of embedding magic addresses.
  • The new TH1520 mailbox code reintroduces simple MMIO helpers (iowrite32/ioread32) while other parts of the patch use writel/readl; it would be cleaner and more consistent to use the existing riscv_io accessors throughout the mailbox implementation.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `thead_generic_cold_boot_allowed()`, `selected_hartid` is a `u32` initialized to 0 but compared to `-1`, so the `selected_hartid != -1` branch is always taken; clarify the sentinel scheme (or change the type/value) so the cold-boot restriction logic actually reflects intent.
- In `thead_aon_init()` the mailbox base addresses are hardcoded into a local `regs[]` array; consider deriving these from the DT node (e.g. `reg` properties) so the implementation follows the hardware description instead of embedding magic addresses.
- The new TH1520 mailbox code reintroduces simple MMIO helpers (`iowrite32`/`ioread32`) while other parts of the patch use `writel`/`readl`; it would be cleaner and more consistent to use the existing `riscv_io` accessors throughout the mailbox implementation.

## Individual Comments

### Comment 1
<location> `platform/generic/thead/thead-generic.c:442-444` </location>
<code_context>
+	return generic_final_init(cold_boot);
+}
+
+static bool thead_generic_cold_boot_allowed(u32 hartid)
+{
+	if (selected_hartid != -1)
+		return (selected_hartid == hartid);
+
</code_context>

<issue_to_address>
**issue (bug_risk):** Comparing unsigned `selected_hartid` against `-1` makes the check always true and effectively pins cold boot to hart 0.

`selected_hartid` is a `u32` initialized to `0`, but `thead_generic_cold_boot_allowed` checks `if (selected_hartid != -1)`. Since `-1` promotes to `UINT_MAX` for the comparison, this condition is always true and only `hartid == selected_hartid` (currently 0) is allowed to cold boot. If the intent is “any hart unless explicitly selected,” use a sentinel that’s representable (e.g., make `selected_hartid` signed and init to `-1`, or track selection with a separate boolean).
</issue_to_address>

### Comment 2
<location> `platform/generic/thead/thead-generic.c:405-410` </location>
<code_context>
+	return sbi_ecall_register_extension(&ecall_light);
+}
+
+struct sbi_ecall_extension ecall_light = {
+	.name			= "light",
+	.extid_start		= SBI_EXT_VENDOR_PMU,
+	.extid_end		= SBI_EXT_VENDOR_PMP,
+	.register_extensions	= sbi_ecall_light_register_extensions,
+	.handle			= sbi_ecall_light_handler,
+};
+
</code_context>

<issue_to_address>
**issue (bug_risk):** The `ecall_light` extension calls `sbi_ecall_register_extension(&ecall_light)` from its own `register_extensions` hook, which is likely recursive.

Here `ecall_light.register_extensions` points to `sbi_ecall_light_register_extensions()`, which in turn calls `sbi_ecall_register_extension(&ecall_light)`. Since `register_extensions` is invoked by the core during extension registration, this will re-register the same extension and can cause recursion or double registration. If there are no sub-extensions, either set `.register_extensions = NULL` and only register `ecall_light` from `thead_generic_final_init()`, or keep the hook and avoid explicitly registering it again in `final_init`.
</issue_to_address>

### Comment 3
<location> `platform/generic/include/thead/thead_aon.h:62-65` </location>
<code_context>
+		uint8_t *data = (uint8_t *)(MESG); \
+		data[OFFSET]  = (SET_DATA)&0xFF;   \
+	} while (0)
+#define RPC_GET_BE64(MESG, OFFSET, PTR)                                    \
+	do {                                                               \
+		uint8_t *data = (uint8_t *)(MESG);                         \
+		*(uint32_t *)(PTR) =                                       \
+			(data[OFFSET + 7] | data[OFFSET + 6] << 8 |        \
+			 data[OFFSET + 5] << 16 | data[OFFSET + 4] << 24 | \
</code_context>

<issue_to_address>
**issue (bug_risk):** `RPC_GET_BE64` writes a 64-bit value through a `uint32_t *`, which is a type and size mismatch.

This macro computes a 64-bit value but assigns it through a `uint32_t *`, causing truncation on 32-bit targets and violating strict-aliasing/size assumptions on 64-bit targets. The store should use a 64-bit type (e.g. `*(uint64_t *)(PTR)`), or the macro should return the 64-bit value instead, matching the behavior of `RPC_SET_BE64`.
</issue_to_address>

### Comment 4
<location> `platform/generic/include/thead/thead_aon.h:23-28` </location>
<code_context>
+#define RPC_GET_VER(MESG) ((MESG)->ver)
+#define RPC_SET_VER(MESG, VER) ((MESG)->ver = (VER))
+#define RPC_GET_SVC_ID(MESG) ((MESG)->svc & 0x3F)
+#define RPC_SET_SVC_ID(MESG, ID) ((MESG)->svc |= 0x3F & (ID))
+#define RPC_GET_SVC_FLAG_MSG_TYPE(MESG) (((MESG)->svc & 0x80) >> 7)
+#define RPC_SET_SVC_FLAG_MSG_TYPE(MESG, TYPE) ((MESG)->svc |= (TYPE) << 7)
</code_context>

<issue_to_address>
**suggestion (bug_risk):** `RPC_SET_SVC_ID` only ORs in bits and never clears previous service ID bits, which can accumulate stale state.

Since it uses `|=` without first clearing the lower 6 bits, calling it on a message with an existing `svc` value (or calling it multiple times with different IDs) can leave old ID bits set. A safer pattern would be: `((MESG)->svc = ((MESG)->svc & ~0x3F) | ((ID) & 0x3F))`. Consider applying a similar mask-and-set approach to the flag setters if messages may be reused.

```suggestion
#define RPC_GET_SVC_ID(MESG) ((MESG)->svc & 0x3F)
#define RPC_SET_SVC_ID(MESG, ID) \
	((MESG)->svc = ((MESG)->svc & ~0x3F) | ((ID) & 0x3F))

#define RPC_GET_SVC_FLAG_MSG_TYPE(MESG) (((MESG)->svc & 0x80) >> 7)
#define RPC_SET_SVC_FLAG_MSG_TYPE(MESG, TYPE) \
	((MESG)->svc = ((MESG)->svc & ~0x80) | (((TYPE) & 0x1) << 7))

#define RPC_GET_SVC_FLAG_ACK_TYPE(MESG) (((MESG)->svc & 0x40) >> 6)
#define RPC_SET_SVC_FLAG_ACK_TYPE(MESG, ACK) \
	((MESG)->svc = ((MESG)->svc & ~0x40) | (((ACK) & 0x1) << 6))
```
</issue_to_address>

### Comment 5
<location> `platform/generic/thead/th1520_mbox.c:140-149` </location>
<code_context>
+	return val;
+}
+
+int th1520_mbox_write(struct th1520_mbox *ia,
+				    uint8_t remote_channel, uint8_t *buffer,
+				    int len)
+{
+	uint32_t count = 0;
+	uint32_t ack   = 0;
+
+	/* set value */
+	wj_mbox_chan_wr_data(ia, remote_channel, buffer, true);
+	wj_mbox_chan_rmw(ia, remote_channel, TH1520_MBOX_GEN,
+			 TH1520_MBOX_GEN_TX_DATA, 0, true);
+	do {
+		ack = wj_mbox_chan_read(ia, remote_channel,
+					TH1520_MBOX_INFO7, false);
+		sbi_timer_udelay(1);
+		count++;
+	} while (ack != TH1520_MBOX_ACK_MAGIC &&
+		 count <= TH1520_ACK_MAGIC_TIMEOUT);
+
+	if (ack != TH1520_MBOX_ACK_MAGIC) {
+		sbi_printf("mbox send faild, no ack magic receive %d\n", count);
+		return SBI_ETIMEDOUT;
+	} else {
+		//clear ack
</code_context>

<issue_to_address>
**issue (bug_risk):** `th1520_mbox_write` ignores its `len` parameter and always sends a fixed-size payload.

The `len` parameter is unused; `th1520_mbox_write()` always sends `sizeof(struct th1520_aon_rpc_lpm_msg_hdr)` via `wj_mbox_chan_wr_data`, which assumes `TH1520_MBOX_INFO_NUM` words. Either honor `len` (with bounds checks) to control the number of bytes/words sent, or remove `len` and clearly document that callers must provide a fixed-size buffer matching the wire format.
</issue_to_address>

### Comment 6
<location> `platform/generic/thead/th1520_mbox.c:171-168` </location>
<code_context>
+	return 0;
+}
+
+int th1520_mbox_read(struct th1520_mbox *ia,
+				   uint8_t *src_channel, uint8_t *buffer,
+				   int *len, int timeout_us)
+{
+	return 0;
+}
+
</code_context>

<issue_to_address>
**issue (bug_risk):** `th1520_mbox_read` is currently a stub that always returns success without filling the buffer.

This stub ignores all parameters, performs no mailbox access, and still returns success. Any caller expecting data or timeout handling will get misleading results. Please either implement the read logic (mirroring the write path where appropriate) or return `SBI_ENOSYS` or another explicit error so callers can detect that reading is unsupported.
</issue_to_address>

### Comment 7
<location> `platform/generic/thead/thead-generic.c:299-301` </location>
<code_context>
+	sync_is();
+}
+
+static void sbi_thead_tcm0_pmp_set(unsigned long auth)
+{
+	sbi_printf("%s: auth:%lx \n", __func__, auth);
+	unsigned int num, reg_val;
+
</code_context>

<issue_to_address>
**nitpick (performance):** Verbose `sbi_printf` in `sbi_thead_tcm0_pmp_set` may be too noisy for a hot path.

This function logs on every call, including the `auth` value. If it’s invoked frequently (e.g. via SBI calls from the kernel), this can flood the console and degrade performance. Please gate this behind a debug option or remove it once bring-up/validation is done.
</issue_to_address>

### Comment 8
<location> `platform/generic/thead/light_c910.c:194-201` </location>
<code_context>
+	light_auxcore_entryboot_set();
+}
+
+static int light_hart_start(u32 hartid, ulong saddr)
+{
+	int ret;
+
+	/* send ipi to triger already plugout core which will be waiting in sbi_hsm_hart_wait
+	 * after reset.
+	 */
+	if (sbi_entry_count(hartid) == C910_CORE0)
+		return sbi_ipi_raw_send(sbi_hartid_to_hartindex(hartid), true);
+
</code_context>

<issue_to_address>
**question (bug_risk):** `light_hart_start` uses `sbi_entry_count(hartid)` to special-case core0, which is likely not what `sbi_entry_count` is intended for.

This condition is effectively using `sbi_entry_count(hartid)` as a hart identifier to decide when to send an IPI. However, `sbi_entry_count()` is meant to count how many times a hart has entered OpenSBI, not which hart it is, so its value will change over time. If you need to special‑case the boot hart (e.g. hart 0), please check `hartid` (or a platform-specific boot hart ID) directly instead of relying on the entry count.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +442 to +444
static bool thead_generic_cold_boot_allowed(u32 hartid)
{
if (selected_hartid != -1)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Comparing unsigned selected_hartid against -1 makes the check always true and effectively pins cold boot to hart 0.

selected_hartid is a u32 initialized to 0, but thead_generic_cold_boot_allowed checks if (selected_hartid != -1). Since -1 promotes to UINT_MAX for the comparison, this condition is always true and only hartid == selected_hartid (currently 0) is allowed to cold boot. If the intent is “any hart unless explicitly selected,” use a sentinel that’s representable (e.g., make selected_hartid signed and init to -1, or track selection with a separate boolean).

Comment on lines +405 to +410
struct sbi_ecall_extension ecall_light = {
.name = "light",
.extid_start = SBI_EXT_VENDOR_PMU,
.extid_end = SBI_EXT_VENDOR_PMP,
.register_extensions = sbi_ecall_light_register_extensions,
.handle = sbi_ecall_light_handler,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The ecall_light extension calls sbi_ecall_register_extension(&ecall_light) from its own register_extensions hook, which is likely recursive.

Here ecall_light.register_extensions points to sbi_ecall_light_register_extensions(), which in turn calls sbi_ecall_register_extension(&ecall_light). Since register_extensions is invoked by the core during extension registration, this will re-register the same extension and can cause recursion or double registration. If there are no sub-extensions, either set .register_extensions = NULL and only register ecall_light from thead_generic_final_init(), or keep the hook and avoid explicitly registering it again in final_init.

Comment on lines +62 to +65
#define RPC_GET_BE64(MESG, OFFSET, PTR) \
do { \
uint8_t *data = (uint8_t *)(MESG); \
*(uint32_t *)(PTR) = \
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): RPC_GET_BE64 writes a 64-bit value through a uint32_t *, which is a type and size mismatch.

This macro computes a 64-bit value but assigns it through a uint32_t *, causing truncation on 32-bit targets and violating strict-aliasing/size assumptions on 64-bit targets. The store should use a 64-bit type (e.g. *(uint64_t *)(PTR)), or the macro should return the 64-bit value instead, matching the behavior of RPC_SET_BE64.

Comment on lines +23 to +28
#define RPC_GET_SVC_ID(MESG) ((MESG)->svc & 0x3F)
#define RPC_SET_SVC_ID(MESG, ID) ((MESG)->svc |= 0x3F & (ID))
#define RPC_GET_SVC_FLAG_MSG_TYPE(MESG) (((MESG)->svc & 0x80) >> 7)
#define RPC_SET_SVC_FLAG_MSG_TYPE(MESG, TYPE) ((MESG)->svc |= (TYPE) << 7)
#define RPC_GET_SVC_FLAG_ACK_TYPE(MESG) (((MESG)->svc & 0x40) >> 6)
#define RPC_SET_SVC_FLAG_ACK_TYPE(MESG, ACK) ((MESG)->svc |= (ACK) << 6)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): RPC_SET_SVC_ID only ORs in bits and never clears previous service ID bits, which can accumulate stale state.

Since it uses |= without first clearing the lower 6 bits, calling it on a message with an existing svc value (or calling it multiple times with different IDs) can leave old ID bits set. A safer pattern would be: ((MESG)->svc = ((MESG)->svc & ~0x3F) | ((ID) & 0x3F)). Consider applying a similar mask-and-set approach to the flag setters if messages may be reused.

Suggested change
#define RPC_GET_SVC_ID(MESG) ((MESG)->svc & 0x3F)
#define RPC_SET_SVC_ID(MESG, ID) ((MESG)->svc |= 0x3F & (ID))
#define RPC_GET_SVC_FLAG_MSG_TYPE(MESG) (((MESG)->svc & 0x80) >> 7)
#define RPC_SET_SVC_FLAG_MSG_TYPE(MESG, TYPE) ((MESG)->svc |= (TYPE) << 7)
#define RPC_GET_SVC_FLAG_ACK_TYPE(MESG) (((MESG)->svc & 0x40) >> 6)
#define RPC_SET_SVC_FLAG_ACK_TYPE(MESG, ACK) ((MESG)->svc |= (ACK) << 6)
#define RPC_GET_SVC_ID(MESG) ((MESG)->svc & 0x3F)
#define RPC_SET_SVC_ID(MESG, ID) \
((MESG)->svc = ((MESG)->svc & ~0x3F) | ((ID) & 0x3F))
#define RPC_GET_SVC_FLAG_MSG_TYPE(MESG) (((MESG)->svc & 0x80) >> 7)
#define RPC_SET_SVC_FLAG_MSG_TYPE(MESG, TYPE) \
((MESG)->svc = ((MESG)->svc & ~0x80) | (((TYPE) & 0x1) << 7))
#define RPC_GET_SVC_FLAG_ACK_TYPE(MESG) (((MESG)->svc & 0x40) >> 6)
#define RPC_SET_SVC_FLAG_ACK_TYPE(MESG, ACK) \
((MESG)->svc = ((MESG)->svc & ~0x40) | (((ACK) & 0x1) << 6))

Comment on lines +299 to +301
static void sbi_thead_tcm0_pmp_set(unsigned long auth)
{
sbi_printf("%s: auth:%lx \n", __func__, auth);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (performance): Verbose sbi_printf in sbi_thead_tcm0_pmp_set may be too noisy for a hot path.

This function logs on every call, including the auth value. If it’s invoked frequently (e.g. via SBI calls from the kernel), this can flood the console and degrade performance. Please gate this behind a debug option or remove it once bring-up/validation is done.

Comment on lines +194 to +201
static int light_hart_start(u32 hartid, ulong saddr)
{
int ret;

/* send ipi to triger already plugout core which will be waiting in sbi_hsm_hart_wait
* after reset.
*/
if (sbi_entry_count(hartid) == C910_CORE0)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): light_hart_start uses sbi_entry_count(hartid) to special-case core0, which is likely not what sbi_entry_count is intended for.

This condition is effectively using sbi_entry_count(hartid) as a hart identifier to decide when to send an IPI. However, sbi_entry_count() is meant to count how many times a hart has entered OpenSBI, not which hart it is, so its value will change over time. If you need to special‑case the boot hart (e.g. hart 0), please check hartid (or a platform-specific boot hart ID) directly instead of relying on the entry count.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants