evidence · 31.1 KiB

second-core-feasibility.md

Committed at docs/research/second-core-feasibility.md. This is what grounds the requirements that cite it.

Open raw source

K230 second-core feasibility

This source and boot-record audit includes a bounded physical CPU0 identity measurement recorded below. It does not claim that the second core can be enabled safely. The diagnostic firmware was restored; the normal kernel, device tree, OpenSBI, and stage 1 are unchanged.

Silicon and vendor architecture

Canaan’s primary documents describe two heterogeneous Xuantie C908 CPUs:

These documents establish hardware capability and the vendor SDK’s AMP design. They do not establish which physical core the current Xuantie Linux image uses, or that Linux can safely take the other core.

What the pinned image exposes

The pinned Linux source is nix/kernel-src.nix, from ruyisdk/linux-xuantie-kernel revision 7d4e1f444f461dbe3833bd99a4640e7b6c2cd529. The evaluated source path was /nix/store/hn11x8zd5linl193d8q0k9k99b46zqbm-linux-xuantie-k230-src. arch/riscv/boot/dts/canaan/k230.dtsi:28-69 declares only cpu@0, with reg = <0>, thead,c908, Sv39, and one cache description. There is no cpu@1. Its PLIC at :210-218 references only cpu0_intc, and its CLINT at :255-259 references only CPU0 software/timer interrupt lines.

The project image is the K230 Linux SDK/Xuantie Linux path, not a complete Linux-plus-RT-Smart image. nix/stage1.nix:140-142 carries OpenSBI, its U-Boot header, and the kernel-side boot files; it does not carry an RT-Smart payload. nix/kernel.nix uses the vendor k230 kernel defconfig and sets SMP support, but the DT presents one hart.

The boot-path comparison and hardware userspace evidence record nproc = 1 and only processor 0. Fresh physical evidence in docs/evidence/cpu-readiness.txt records CONFIG_SMP=y, NR_CPUS=64, RISCV_SBI=y, only CPU0 present/possible, live DT only cpu@0, and a Linux hart reporting thead,c908, vector support, and a 256 KiB L2. That is consistent with the datasheet’s big-core description, so it is unsafe to label logical hart 0 as physical CPU0 or CPU1 without a register/firmware observation.

The current boot transcript says:

Platform HART Count       : 1
Platform IPI Device       : aclint-mswi
Platform Timer Device     : aclint-mtimer @ 27000000Hz
Platform HSM Device       : ---
Domain0 HARTs             : 0*
[    0.015...] smp: Brought up 1 node, 1 CPU

See docs/evidence/boot-from-source-cold.txt:104-125,230-231. The source and logs establish one enumerated HART, not the physical-core identity and not that RT-Smart is running on the other core in this image.

OpenSBI and SMP barriers

The official OpenSBI README and official SBI HSM specification describe HSM hart start/stop/status calls required for normal Linux multi-hart boot. In the pinned OpenSBI source /nix/store/k6kih6q1vh5nashsgaz96ch012742s8z-source, lib/sbi/sbi_ecall_hsm.c:19-67 implements the HSM SBI calls and lib/sbi/sbi_init.c:98-100 prints Platform HSM Device. That line reports a platform-specific HSM device pointer; --- is not proof that the SBI HSM extension is absent. The current log proves only that one HART was enumerated and assigned to the root domain.

Adding a cpu@1 node alone is therefore insufficient:

  1. Physical mapping and ownership are unknown. The current carried image has no RT-Smart payload. The SDK’s CPU0/Linux, CPU1/RT-Smart mapping is a reference architecture, not proof for this image. The live hart’s RVV and L2 size make big-core execution plausible, but no source or current log maps logical hart 0 to a physical core. Display, multimedia, KPU, mailbox, and power-domain ownership must be established separately.
  2. HART discovery and start: OpenSBI must enumerate a second unique hart, assign it to Linux, and provide a valid hart-start path. The current /chosen/opensbi-domains node (k230.dtsi:21-26) does not describe a second-HART domain or ownership contract.
  3. Interrupts and timers: the K230 DTS PLIC and CLINT target CPU0 only. CPU1 needs verified local interrupt, software/timer interrupt, and K230 timer/IPI wiring. The datasheet mailbox proves an inter-core mechanism, but not a Linux SBI IPI implementation.
  4. ISA and MMU: CPU1 has RVV 1.0 and a different cache topology. Linux needs an accurate CPU1 ISA/MMU/cache description and a common ISA baseline for SMP code.
  5. Coherency and shared memory: the repository has no proof of the cache coherency contract needed for Linux shared page tables and SMP atomics. This must be established from the K230 programming/reference material or a controlled firmware experiment before any Linux shared-memory test.

Options

The lowest-risk option is to leave this single-hart Linux image unchanged. The vendor SDK’s AMP split is a separate reference architecture; this image contains no RT-Smart payload, and its physical-core mapping and peripheral ownership remain unknown.

A Linux SMP option needs a coordinated patch family, not one DTS edit:

An AMP Linux-plus-RT-Smart option would require adding and validating the RT-Smart payload and its ownership contract. It is not evidence about this image, and no ready Linux-side API for it is present here.

Minimal recoverable experiment

Do not experiment on the only working card. Preserve the current image and use a second card or reversible UMS image. Before attempting Linux SMP or shared memory:

  1. Establish the physical-to-hart map and ownership from the K230 reference manual and a controlled firmware/register observation. Confirm unique hart IDs, reset release, interrupt/timer routes, cache hierarchy, and the coherency/atomic-sharing contract.
  2. Only then prepare an experimental DT with CPU1 and those verified descriptions; do not infer routes from the CPU count.
  3. Generic SBI HSM is already compiled and detected in this image (docs/evidence/boot-from-source-cold.txt:173). Investigate whether the verified second hart can use OpenSBI’s existing raw-IPI start path or needs a platform HSM start device, then build a variant that enumerates it. Retain the Canaan MAEE quirk and a rollback image. Treat Platform HSM Device: --- as informational, not as the acceptance test.
  4. Boot with early serial logging and require HART count 2, Linux smp: Brought up 1 node, 2 CPUs, and successful CPU status checks. Stop on any hang, missing HART, or interrupt/timer failure. Only after that test shared-memory/cache behavior and peripheral ownership.
  5. Restore the known-good stage-1/kernel image over the proven UMS path after any failed experiment.

This would prove only that Linux can start a uniquely identified second hart after the coherency and ownership prerequisites are established. It would not prove RT-Smart AMP operation, display ownership, or the full vendor SDK. No board experiment was run for this audit.

2026-09 evidence update and decision

The primary K230 Technical Reference Manual v0.3.1 (TRM) resolves part of the physical mapping which the earlier audit deliberately left open. Section 1.3.2 identifies CPU0 as the 800 MHz/128 KiB-L2 small core and CPU1 as the 1.6 GHz/256 KiB-L2 RVV big core. Section 1.4.1 says BootROM starts CPU0 after reset deassertion and CPU0 controls CPU1 reset deassertion. That establishes reset ownership, but it does not assign Linux hart IDs or describe a Linux secondary-hart release protocol.

The current Linux hart reports RVV and a 256 KiB L2 while the TRM assigns both features to CPU1. This makes CPU1 execution a strong inference for the current Linux handoff. It remains an inference, not a basis to add a guessed cpu@1: the physical-core-to-hart mapping is not in the captured handoff.

What the vendor register map proves—and does not

The TRM provides real CPU1 controls:

Those facts rule out a speculative register poke: a write would be an actual reset or power transition, and reads of the W1C-containing reset register must not be turned into read-modify-write operations. The TRM does not publish a CPU1 reset vector, physical hart ID, PLIC context mapping, ACLINT wiring, or an SBI HSM implementation for this device.

It also does not establish the condition Linux SMP actually needs: coherent shared memory for page tables, spinlocks, and secondary-start data. The existing DT’s dma-noncoherent property concerns DMA; it neither proves nor disproves CPU-to-CPU cache coherence. The TRM describes per-C908 cache control and an instruction-cache/data-cache snoop control, but contains no CPU0/CPU1 coherency or shared-atomic contract. This is a blocking absence of evidence, not a demonstrated hardware impossibility.

Linux SMP is a different project from AMP

The pinned Linux code makes the integration requirements concrete. Its cpu_ops_sbi.c starts a secondary with SBI_EXT_HSM_HART_START; its cpu.c requires a valid ISA description for each DT CPU; and the K230 DTS supplies only one CPU interrupt controller to its PLIC and CLINT. Therefore Linux SMP needs a verified second hart, HSM start path, timer/IPI routes, CPU ISA/MMU and cache description, and coherency contract before a DT change is even useful.

AMP has a different boundary: separate firmware images, mailbox notifications, explicitly managed shared buffers, and exclusive peripheral ownership. Canaan documents separate Linux little-core and RT-Smart big-core SDK images, which is evidence for an AMP model, not for Linux SMP. This image has neither the RT-Smart payload nor a Linux-side video-offload protocol, so AMP cannot yet improve the running video workload. A second core can help video only after a measured offload design exists; two cores alone do not improve a single-thread renderer or prove shared-memory safety.

Proposed safe next step: passive handoff audit

No boot configuration, device tree, CPU online state, or MMIO register is changed by this proposal. The integration operator should collect the following from a known-good board when it is free; this investigation does not take the serial device.

  1. Preserve a cold-boot console capture containing OpenSBI platform HART count, domain HART list, HSM/IPI/timer device lines, and Linux’s smp: result. This separates a firmware one-hart declaration from a Linux secondary-start failure.
  2. Capture the live cpus DT subtree, PLIC/CLINT interrupts-extended, /sys/devices/system/cpu/{possible,present,online}, and per-CPU ISA/cache reports. These no-write facts identify the handoff Linux actually received.
  3. Only with the operator’s established read-only MMIO capture method, record values at 0x9110100c, 0x91103018, and 0x9110301c. Do not use a tool mode that writes, performs read-modify-write, clears W1C status, or probes adjacent registers. These values may show reset/power state; they cannot establish hart identity or coherency.
  4. Obtain a Canaan statement or shipped stage-1 source proving the CPU1 release sequence and vector, physical hart IDs, PLIC/ACLINT contexts, and CPU-to-CPU coherency contract. An undocumented release sequence is a stop condition.

Only after step 4 should a separate rollback-card SMP proposal exist. It must first make OpenSBI enumerate and HSM-start both verified harts, then add the complete DT routes, then enable Linux SMP. Its acceptance criteria begin with two OpenSBI harts, smp: Brought up 1 node, 2 CPUs, timer/IPI stress, and a shared-memory atomic test. Video benchmarking comes after correctness, not before it. No board experiment was performed for this update.

Vendor AMP source establishes a release contract, not SMP support

A further source review found the vendor’s concrete Linux-to-RT-Smart AMP path in k230-amp.c and its amp_test client. This is primary vendor source, but it is not part of this NixOS image.

The client opens /dev/k230-amp, maps a device-tree-selected RT-Smart memory region, loads a firmware image there, then invokes AMP_CMD_BOOT. The driver writes that region back from the running Linux CPU’s cache, programs the CPU1 reset-vector register, and performs the CPU1 reset-control writes. Mapping the region first holds the large core in reset. The companion README calls this “small-core Linux controls big-core restart”. That is direct evidence that the vendor AMP boundary requires an explicit firmware payload, memory reservation, cache maintenance, reset vector, and reset protocol.

It does not establish Linux SMP. The driver hardcodes vendor addresses and does not create a Linux CPU, an OpenSBI domain, a PLIC/ACLINT route, or shared Linux page-table coherency. Its actual reset writes are why this project will not use it as a probe or copy it into the image.

The vendor’s generic OpenSBI platform independently counts harts by iterating /cpus in platform/generic/platform.c. The pinned handoff supplies only cpu@0, so the observed OpenSBI platform count of one follows from the handoff description. OpenSBI’s generic HSM ecall handler being linked only means it can dispatch HART_START for an assigned, known hart; it does not manufacture CPU1 release support.

The publicly documented DRAM RAW/WAR coherency guarantees apply to the memory controller interfaces and are conditional on configuration for AXI. They are not a documented CPU0/CPU1 cache-coherency or atomic-sharing contract. This remains a blocker for Linux SMP, while the AMP source shows cache maintenance must be part of any separately designed shared-buffer protocol.

2026-09-26 pinned stage-1 audit: Linux runs on physical CPU1

The exact stage-1 source pinned by nix/k230-sdk-src.nix is Canaan’s k230_linux_sdk revision 1104236db4d1e47873bd68924f912747b820228c. nix/uboot-k230.nix copies its U-Boot overlay into the built source and uses k230_canmv_v3_defconfig. In that overlay, board/canaan/common/sdk_autoconf.h:16 defines CONFIG_LINUX_RUN_CORE_ID 1; board/canaan/common/k230_img.c:53 includes the header. Its k230_boot_uboot_uimage() branch at lines 276-284 calls de_reset_big_core(image_get_load(pUh)) and parks the current core in wfi. The release function at lines 149-170 writes CPU1’s reset-vector register 0x91102104, then performs the documented CPU1_RST_CTL sequence at 0x9110100c. board/canaan/common/k230_spl.c:164-166 selects the U-Boot on-big-core path for the same configuration. This is shipped stage-1 source for a physical CPU1 release, and the live hart’s RVV/256 KiB-L2 observation corroborates it. The extra core we want is physical CPU0, the 800 MHz non-RVV core.

This does not prove the physical CPU0 hart ID. The same U-Boot overlay’s arch/riscv/dts/k230.dtsi:46-57 calls the single described, vector-capable hart cpu@0, gives it reg = <0>, and lists 800 MHz. That contradicts the physical CPU1 profile and shows the device-tree label/clock cannot be used as a physical-core map. The physical mhartid values of both CPUs require a direct, controlled firmware observation or a Canaan statement. U-Boot’s arch/riscv/cpu/k230/cpu.c:125-160 also has a boot_baremetal path whose CPU0 branch writes 0x91102100 and 0x91101004; it demonstrates a physical CPU0 reset vector/release address but does not set up OpenSBI, interrupts, timers, cache sharing, or Linux SMP.

There is stronger, adverse evidence about the hart map: in the Linux K230 DTS review, the patch author reports that the physical big core’s CSR.MHARTID is 0, despite its physical CPU1 name. A QEMU K230 board author reporting the physical topology states that both physical cores have mhartid 0. These are first-hand developer reports, not a measurement on this board, but they directly undermine the assumed unique-hart-ID prerequisite of conventional SBI HSM and Linux SMP. They must be tested on a disposable board experiment before any design assigns the extra core reg = <1>. If the collision is confirmed, the existing SMP release option cannot proceed as written; AMP with a separate CPU0 payload and explicit shared-memory protocol is the plausible way to execute a workload on the extra physical core.

The pinned Linux tree at revision 7d4e1f444f461dbe3833bd99a4640e7b6c2cd529 has an internal SBI HSM hart-status call in arch/riscv/kernel/cpu_ops_sbi.c:52-63, but its only caller is the CPU hotplug cpu_is_stopped path at lines 107-119. The read-only source search rg -n 'sbi_hsm|hart_status|debugfs' <pinned-src>/arch/riscv found no Linux userspace/debugfs passthrough that queries an unenumerated hart’s HSM status without CPU up/down. That says the proposed Linux-side query is unavailable in this pinned tree; it says nothing about whether firmware can answer the ecall.

The pinned K230 Linux DTS still names only one PLIC/ACLINT target. Neither the pinned stage-1 overlay nor its OpenSBI generic-platform overlay defines a CPU0 PLIC context and ACLINT timer/IPI route for a joint Linux handoff. Thus task 3.1 is partly closed (physical CPU0 vector and release source) but remains blocked on the physical hart ID and interrupt topology. No reset, power, or vector write was performed in this audit.

2026-09-26 Linux SMP path audit (physical CPU0 is the target)

The requested outcome is one Linux kernel scheduling ordinary processes on both physical cores. A separate CPU0 heartbeat or AMP payload cannot satisfy it. the-small-core-runs-a-recoverable-heartbeat is suspended after that scope correction. Pinned stage 1 (k230_img.c:276-285) already starts physical CPU1 and leaves CPU0 in WFI; Linux is on CPU1. The remaining physical core to bring into the same kernel is CPU0.

The pinned OpenSBI 1.4 generic platform is not ready for two physical cores that report the same mhartid:

The K230 QEMU author reports that both physical cores read mhartid=0. The Linux DTS author reports that physical CPU1 reads 0 and says inter-core cache coherence is unknown. These are firsthand reports, not measurements on this board. A fake cpu@1 node would be unsafe even if a two-CPU DT compiled. A K230-specific virtual ID would need an early physical-core discriminator and consistent use in OpenSBI warm entry, scratch, domain, HSM, IPI, timer, and Linux handoff. No such implementation exists in the pinned vendor overlay: platform/generic/thead/thead-generic.c only applies C908 errata and PMU setup. A grep for another physical core-ID CSR in the pinned U-Boot and OpenSBI overlays found no defined candidate. This is an audited absence within those overlays, not proof that the silicon has no such CSR.

The narrowest next physical identity measurement is source-reachable at an already executing point. The optional, default-off patch nix/patches/second-core/spl-cpu0-identity.patch reads mhartid and misa in CPU0 SPL immediately before its existing CPU1 release, prints them, and leaves the release/WFI behavior untouched. CPU1’s existing OpenSBI boot banner reports its logical ID; an equivalent M-mode CPU1 readout would settle both physical CSR values. This diagnostic is not a second program or a Linux feature. It still requires a separately reviewed experimental stage-1 build and the board operator’s recovery/serial session; it does not justify a reset write or SMP boot by itself.

The pinned Linux DT arch/riscv/boot/dts/canaan/k230.dtsi:210-218,255-259 supplies PLIC and CLINT routes only to its single CPU interrupt controller. The vendor OpenSBI overlay has no CPU0-specific PLIC/ACLINT mapping for a joint handoff. U-Boot’s CSR_SMPEN write in arch/riscv/cpu/k230/cpu.c:39-49 is not a CPU0/CPU1 cache-coherence contract: the pinned source does not define its semantics or show an inter-core cache test. The vendor AMP driver cache-flushes shared data, so initial Linux shared page tables and locks cannot safely be the first coherence experiment. A controlled pre-Linux atomic/cache and timer/IPI diagnostic or a Canaan contract must close those gates before any two-CPU Linux boot. Once closed, the first Linux SMP image should use a scalar common ISA across kernel and userspace; RVV can return only with a proven per-hart scheduling and userspace capability design.

Canaan’s public small-core Linux DT at src/little/linux/arch/riscv/boot/dts/kendryte/k230.dtsi:18-63 separately describes physical CPU0 as its sole logical cpu@0, with PLIC at 0xf00000000 and CLINT at 0xf04000000, each wired only to that one local interrupt controller. This public main source was read during the 2026-09-26 audit but is not this project’s pinned SDK revision and is not a combined two-core DT. The matching base addresses in the pinned big-core DT do not establish whether the hardware maps separate PLIC/ CLINT instances into per-core address spaces or exposes usable second contexts to one joint Linux image. It cannot justify inventing a second interrupts-extended entry.

2026-09-27 physical CPU0 identity measurement

The default-off CPU0 SPL diagnostic was run on the board and its two raw SPL slots were restored byte-for-byte afterward. The physical trial recorded CPU0_SPL_IDENTITY mhartid=0x0 misa=0x800000000094112f before the existing CPU1 release, followed by one-hart OpenSBI and one-CPU Linux. The restored image then booted normally. This turns the pinned source’s physical CPU0 identity sampling point into a first-hand mhartid=0 observation. Source tracing also resolves what the CPU1-side Boot HART ID : 0 means. The pinned OpenSBI 1.4 include/sbi/riscv_asm.h:166 defines current_hartid() as csr_read(CSR_MHARTID). Canaan’s pinned overlay lib/sbi/sbi_init.c:534 assigns that value to hartid, :394 passes it to sbi_boot_print_hart(), and :169 prints Boot HART ID from it. The pinned stage-1 k230_img.c:276-285 releases CPU1 into this boot path while CPU0 parks; the observed 256 KiB L2/RVV profile corroborates that physical assignment. Therefore the board’s CPU1-side banner is also a first-hand CSR read of zero, with its physical attribution grounded in the stage-1 source. No new CPU1-specific marker was added.

Both physical cores reporting hardware mhartid=0 rules out a DT-only second CPU node under this generic OpenSBI’s current scratch/HSM indexing. A safe virtual-ID scheme or other supported two-hart mapping, CPU0 interrupt/timer routing, and the shared cached-memory/atomic contract remain open before any Linux SMP attempt.

TRM mailbox register map (task 1.1, 2026-10-12)

Result: the TRM publishes a mailbox register map. It is in TRM section 2.9 “Mailbox”, not section 2.4. Section 2.4 is “Interrupt System” (PDF page 104); earlier text here and in openspec/changes/the-small-core-runs-as-a-coprocessor/ that attributed the mailbox to “section 2.4” meant only the interrupt table, which names the mailbox interrupt sources. The map below is read from the primary document; nothing is inferred from th1520-mailbox.c.

Source: K230 Technical Reference Manual v0.3.1 (2024-11-18), the URL linked above, downloaded 2026-10-12 (SHA-256 4a18fdbd1cd25c5a33ca4d54b78c95e0e338305bceb17397133370fa10c1e5f0) and text-extracted with pdftotext -layout. The PDF is not stored in the repo or the Nix store (the repo keeps no vendor documents); PDF page numbers below are physical page indexes of that file. Section 2.9 starts at PDF page 172; the register descriptions run through page 185.

What this does not establish (kept UNVERIFIED):

Cross-check, naming only: drivers/mailbox/th1520-mailbox.c also has per-direction status/set/clear groups, but the K230 counters (two bits per source, 16 sources) and the 128 interlock registers are K230’s own and are not borrowed from it.