* [PATCH v4 00/10] sparc64: add Fujitsu M3000 support
@ 2026-10-18 13:45 Magnus Lindholm
2026-10-18 13:45 ` [PATCH v4 01/10] sparc64: return from the generic clear_page implementation Magnus Lindholm
` (9 more replies)
0 siblings, 10 replies; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7
Several years ago I salvaged an M3000 SPARC Enterprise box being
decommissioned at work. The box has been sitting ever since, as it did
not run Linux. It did not seem impossible to get Linux running, but
my earlier attempts failed. I recently found some documentation:
"SPARC JPS2: Common Specification" and the "Fujitsu SPARC64 VII
Extensions", which gave me some of the missing pieces.
Getting this up and running required extensive testing, some
kernel instrumentation, debug printouts and many reboot iterations.
Codex assisted with debugging, instrumentation, code drafting, review
and commit-message preparation. Along the way, I stumbled upon some
generic sparc and driver bugs that this series addresses as well.
This series adds CPU, MMU, SMP, Oberon PCIe and firmware console support
for the Fujitsu SPARC Enterprise M3000, based on v7.3-rc1. It also applies
cleanly to the SPARC for-next snapshot a9e88d617a25.
Patches 1-7 fix generic sparc64 code used by the port: page clearing,
secondary startup locking, kernel PUD mappings, IRQ allocation,
firmware-property validation, DMA segment boundaries and IOMMU table
initialization. Patches 8-10 add VII, Oberon and HVC support.
VII CPUs and Oberon bridges are detected at runtime; no platform-specific
CPU or bridge Kconfig selection is required. HVC remains selectable.
Four independent fixes are in a separate follow-up series: IOMMU context
cleanup, PROM short writes, cookie IRQ allocation and interrupt-vector
bounds. They are useful independently and are not prerequisites for
applying or building this series. The PROM short-write fix also affects
the M3000 early console if firmware returns short writes.
The on-board BCM5718 also needs the separately submitted netdev patch:
[PATCH net-next v3] tg3: normalize inherited BCM5718 register byte order
It is independent of this SPARC series and was included in hardware
validation kernels.
Changes since v3
----------------
- Add generic fixes for DMA segment-boundary checks on sun4u/sun4v and
initialization of IOMMU page-table entries. Keep the four unrelated
fixes listed above in their own series.
- Base the huge-PUD guard on the selected hardware page encoding,
including the sun4v small-page fallback.
- Check for sun4v before reading %ver during secondary startup; restrict
VII CPU/FPU names to sparc64 builds.
- Reserve only required locked TLB slots. Accept arbitrary 4MB offset
bits on VII readback while checking all size and address bits.
- Preserve VII PA[46:43] in pte_pfn() and pte_modify(), leaving other
processors' masks unchanged.
- Require disabled shared-context and TSB-prefetch valid bits at CPU
entry and firmware handoffs. Record unsupported state and halt.
- Capture secondary MCNTL before the MMU-state check and identify an
uncaptured value accurately. Explain the silent-halt limitation and
print an error before halting on an out-of-range trap branch.
- Correct the fatal-trap marker, clarify manual references, queued-lock
ordering and IPI wording, and complete the generic fixes' Fixes tags.
- Carry Greg Kroah-Hartman's Reviewed-by on the unchanged HVC C code,
clarify its Kconfig help and add sparclinux maintainer routing.
- Remove the VII and Oberon Kconfig options, retaining runtime CPU and
firmware-compatible checks and sun4v startup bypasses. Reject unsupported
VII HugeTLB/THP mappings at runtime before page-size setup side effects;
preserve huge-page support on other processors.
- Fold VII corrections into CPU enablement. This is ten patches versus
eight in v3; tg3 remains separate.
Testing
-------
The final code plus the separately submitted tg3 fix booted on
M3000 with eight CPUs and 16 GiB. The four generic follow-up fixes and
the experimental Oberon error/MSI series were absent. It passed 32
fork/COW rounds over 64 pages, migration across all CPUs, 1,000 concurrent
remap/protection rounds, 32 MiB direct-disk verification, network image
checksum verification and per-CPU HVC open/write/close checks. Taint stayed
zero, with no kernel warning, Oops or panic in the collected log.
With HugeTLB and THP compiled in, VII rejected huge-page boot reservations,
MAP_HUGETLB, hugetlbfs and forced huge tmpfs mounts, and MADV_COLLAPSE.
Huge-page pool controls were absent; base-page data and protection checks
passed. Since that boot, only commit text and a stray Kconfig blank line
were changed; executable source is unchanged. The tg3 fix is accounted
for separately.
The same runtime-detection changes were also tested in the preceding
hardware follow-up tree: M3000 SMP and true UP; Ultra45 / UltraSPARC IIIi
UP; and a T5120 sun4v LDOM with eight threads on one physical T2 core.
The identical SMP image booted on M3000 and the T5120 guest. Ultra45 used
its own UP configuration. COW and disk checks passed on each. Ultra45 and
T5120 faulted and verified real HugeTLB mappings; THP collapse produced
16 MiB of anonymous huge pages. These other-platform boots include the
experimental follow-up code and are not exact final-v4 runtime tests.
Ultra45 retains its existing ATAPI DMA advisory messages.
GCC 13.3/binutils 2.42 W=1 vmlinux builds passed in UP and SMP at each of
the revised commits 8-10. Unchanged commits 1-7 retain their earlier
per-commit build results. Existing FusionMPT warnings remain. The runtime
detection tree also passed builds with PCI disabled, HVC disabled, huge
pages disabled, and sparc64 defconfig.
Earlier targeted harnesses reproduce the generic bugs and VII PTE/readback
issues. A read-only M3000 diagnostic found shared-context and prefetch
state disabled on all CPUs at startup and across thousands of PROM calls;
it is absent from this series. No unsupported MMU state was injected on
hardware. These are bounded regression tests, not a long-term stability
or performance claim.
Current limitations
-------------------
VII HugeTLB and THP need further MMU work and testing and are refused at
runtime. Compiling them in remains supported for other processors in the
same kernel. VII CPU offlining is not supported.
Runtime CPU/bridge matching can also select these paths on other VII/VII+
systems, including M4000-M9000. Only the M3000 has been tested; mixed
VI/VII domains are unsupported, and hvc0 binds only on the M3000.
Shared contexts and hardware TSB prefetch must remain disabled. If the
state check fails while PROM is locked, the firmware console cannot
report it and other CPUs can stall behind that lock. The four-word
sparc64_vii_mmu_fault record is identified in System.map; recovering it
after a reset has not been validated. Supporting enabled firmware modes
requires a preservation/restoration protocol.
Oberon uses legacy interrupts; MSI and native bridge error recovery are
not implemented. A boot with PCI_MSI=y and a larger NR_CPUS remains useful
validation. CPU error handling supports restrainable ECC; other patched
error traps remain fatal, including PCI config-access errors. IPI retry
policy and broader error recovery remain separate work.
HVC calls firmware; a call that never returns remains unbounded. Fast
pasted input can exceed the firmware FIFO, and console testing does not
establish panic-path reliability.
Magnus Lindholm (10):
sparc64: return from the generic clear_page implementation
sparc64: honor queued spinlock layout in secondary startup
sparc64: avoid huge kernel PUD mappings with small hardware pages
sparc64: stop IRQ setup when descriptor allocation fails
sparc64: validate Fire register properties before initialization
sparc64: retain the DMA segment start across SG entries
sparc64: zero newly allocated IOMMU page tables
sparc64: add SPARC64 VII CPU, MMU and SMP support
sparc64: add M3000 Oberon PCIe support
hvc: add an M3000 firmware console backend
MAINTAINERS | 7 +
arch/sparc/include/asm/head_64.h | 11 ++
arch/sparc/include/asm/hugetlb.h | 3 +
arch/sparc/include/asm/iommu_64.h | 1 +
arch/sparc/include/asm/irq_64.h | 1 +
arch/sparc/include/asm/pgtable_64.h | 13 +-
arch/sparc/include/asm/setup.h | 7 +
arch/sparc/include/asm/smp_64.h | 2 +
arch/sparc/include/asm/spitfire.h | 1 +
arch/sparc/include/asm/trap_block.h | 6 +
arch/sparc/kernel/cpu.c | 7 +
arch/sparc/kernel/entry.h | 2 +
arch/sparc/kernel/head_64.S | 20 +++
arch/sparc/kernel/helpers.S | 126 +++++++++++++++
arch/sparc/kernel/iommu.c | 50 +++++-
arch/sparc/kernel/irq_64.c | 54 +++++--
arch/sparc/kernel/pci_fire.c | 35 ++++-
arch/sparc/kernel/pci_impl.h | 1 +
arch/sparc/kernel/pci_sun4v.c | 4 +-
arch/sparc/kernel/prom_64.c | 28 +++-
arch/sparc/kernel/prom_irqtrans.c | 13 +-
arch/sparc/kernel/ptrace_64.c | 7 +
arch/sparc/kernel/setup_64.c | 57 +++++++
arch/sparc/kernel/smp_64.c | 83 +++++++++-
arch/sparc/kernel/trampoline_64.S | 74 ++++++++-
arch/sparc/kernel/traps_64.c | 27 ++++
arch/sparc/lib/GENpage.S | 3 +
arch/sparc/mm/init_64.c | 121 +++++++++++++-
arch/sparc/mm/tsb.c | 19 ++-
arch/sparc/mm/ultra.S | 236 ++++++++++++++++++++++++++++
drivers/tty/hvc/Kconfig | 11 ++
drivers/tty/hvc/Makefile | 1 +
drivers/tty/hvc/hvc_m3000.c | 202 ++++++++++++++++++++++++
33 files changed, 1189 insertions(+), 44 deletions(-)
create mode 100644 drivers/tty/hvc/hvc_m3000.c
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.43.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v4 01/10] sparc64: return from the generic clear_page implementation
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 02/10] sparc64: honor queued spinlock layout in secondary startup Magnus Lindholm
` (8 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7, stable
GENclear_page falls through into generic_patch_pageops after clearing
its page. The patcher then rewrites kernel text on each invocation.
This executes 45 extra instructions, including six stores and three
FLUSH instructions, before returning.
Return once the clear loop completes, as GENcopy_page does. This affects
processors using the generic page-operation fallback.
Fixes: 6c70b6fc7b6f ("[SPARC64]: Do not assume sun4v chips have load-twin/store-init support.")
Cc: stable@vger.kernel.org
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/lib/GENpage.S | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/sparc/lib/GENpage.S b/arch/sparc/lib/GENpage.S
index c143c4d1de3f..6ce11108e143 100644
--- a/arch/sparc/lib/GENpage.S
+++ b/arch/sparc/lib/GENpage.S
@@ -48,6 +48,9 @@ GENclear_user_page:
bne,pt %xcc, 1b
add %o0, 64, %o0
+ retl
+ nop
+
#define BRANCH_ALWAYS 0x10680000
#define NOP 0x01000000
#define GEN_DO_PATCH(OLD, NEW) \
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 02/10] sparc64: honor queued spinlock layout in secondary startup
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
2026-10-18 13:45 ` [PATCH v4 01/10] sparc64: return from the generic clear_page implementation Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 14:01 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 03/10] sparc64: avoid huge kernel PUD mappings with small hardware pages Magnus Lindholm
` (7 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7, stable
The trampoline uses ldstub on byte zero of prom_entry_lock, which is a
queue-tail byte in the big-endian queued-spinlock layout. This writes
0xff000000; with NR_CPUS < 16384, a C locker can decode that tail as
CPU 16319 and access beyond the per-CPU table. Testing this byte also
fails to exclude a C holder of the locked byte at offset three.
Acquire the word with CASA, release byte three and poll while contended.
Retain explicit assembly ordering and the predicted branch for Spitfire
erratum 51; C unlock relies on TSO and compiler ordering.
See qspinlock_types.h and JPS2 A.10/A.35 for CASA/MEMBAR.
Fixes: 145d97858597 ("arch/sparc: Enable queued spinlock support for SPARC")
Cc: stable@vger.kernel.org
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/kernel/trampoline_64.S | 40 +++++++++++++++++++++++++++----
1 file changed, 36 insertions(+), 4 deletions(-)
diff --git a/arch/sparc/kernel/trampoline_64.S b/arch/sparc/kernel/trampoline_64.S
index 51bf1eb92a36..447334730122 100644
--- a/arch/sparc/kernel/trampoline_64.S
+++ b/arch/sparc/kernel/trampoline_64.S
@@ -106,9 +106,19 @@ startup_continue:
* We lock 'num_kernel_image_mappings' consequetive entries.
*/
sethi %hi(prom_entry_lock), %g2
-1: ldstub [%g2 + %lo(prom_entry_lock)], %g1
+ /* Big-endian qspinlock: acquire the whole 32-bit value, not byte 0. */
+ or %g2, %lo(prom_entry_lock), %g2
+1: lduw [%g2], %g1
brnz,pn %g1, 1b
nop
+ mov 1, %g1
+ casa [%g2] ASI_P, %g0, %g1
+ brnz,pn %g1, 1b
+ nop
+ /* Spitfire erratum 51: barrier in a predicted-taken delay slot. */
+ ba,pt %xcc, 2f
+ membar #Sync
+2:
/* Get onto temporary stack which will be in the locked
* kernel image.
@@ -212,7 +222,13 @@ startup_continue:
nop
sethi %hi(prom_entry_lock), %g2
- stb %g0, [%g2 + %lo(prom_entry_lock)]
+ /*
+ * Retain explicit assembly release ordering; C unlock relies on TSO.
+ * The predicted branch protects Spitfire erratum 51.
+ */
+ ba,pt %xcc, 2f
+ membar #StoreStore | #LoadStore
+2: stb %g0, [%g2 + %lo(prom_entry_lock) + 3]
ba,pt %xcc, after_lock_tlb
nop
@@ -326,9 +342,19 @@ after_lock_tlb:
wrpr %g0, 0, %wstate
sethi %hi(prom_entry_lock), %g2
-1: ldstub [%g2 + %lo(prom_entry_lock)], %g1
+ /* Big-endian qspinlock: acquire the whole 32-bit value, not byte 0. */
+ or %g2, %lo(prom_entry_lock), %g2
+1: lduw [%g2], %g1
brnz,pn %g1, 1b
nop
+ mov 1, %g1
+ casa [%g2] ASI_P, %g0, %g1
+ brnz,pn %g1, 1b
+ nop
+ /* Spitfire erratum 51: barrier in a predicted-taken delay slot. */
+ ba,pt %xcc, 2f
+ membar #Sync
+2:
/* As a hack, put &init_thread_union into %g6.
* prom_world() loads from here to restore the %asi
@@ -389,7 +415,13 @@ after_lock_tlb:
add %sp, (2047 + 128), %o0
3: sethi %hi(prom_entry_lock), %g2
- stb %g0, [%g2 + %lo(prom_entry_lock)]
+ /*
+ * Retain explicit assembly release ordering; C unlock relies on TSO.
+ * The predicted branch protects Spitfire erratum 51.
+ */
+ ba,pt %xcc, 2f
+ membar #StoreStore | #LoadStore
+2: stb %g0, [%g2 + %lo(prom_entry_lock) + 3]
ldx [%l0], %g6
ldx [%g6 + TI_TASK], %g4
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 03/10] sparc64: avoid huge kernel PUD mappings with small hardware pages
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
2026-10-18 13:45 ` [PATCH v4 01/10] sparc64: return from the generic clear_page implementation Magnus Lindholm
2026-10-18 13:45 ` [PATCH v4 02/10] sparc64: honor queued spinlock layout in secondary startup Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 13:53 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 04/10] sparc64: stop IRQ setup when descriptor allocation fails Magnus Lindholm
` (6 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7, stable
The kernel PUD refill path merges virtual address bits 32:28 into a
TTE, requiring hardware pages of at least 256MB. Sun4u instead selects
4MB pages, losing bits 27:22 when refilling a huge PUD. Sun4v also falls
back to 4MB pages when neither 256MB nor 2GB pages are selected.
Keep PMD mappings when the selected PUD encoding matches the small-page
entry. The PMD refill path preserves the bits needed by the 4MB TTE;
sun4v selections of 256MB or larger remain eligible for huge PUDs.
Fixes: 0dd5b7b09e13 ("sparc64: Fix physical memory management regressions with large max_phys_bits.")
Cc: stable@vger.kernel.org
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/mm/init_64.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 103db4683b16..f3f7eba08469 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -1696,6 +1696,14 @@ static unsigned long __ref kernel_map_hugepud(unsigned long vstart,
static bool kernel_can_map_hugepud(unsigned long vstart, unsigned long vend,
bool guard)
{
+ /*
+ * The PUD miss path propagates VA bits 32:28, assuming at least
+ * 256MB hardware pages. Retain PMD mappings when the selected PUD
+ * encoding falls back to the small-page entry.
+ */
+ if (kern_linear_pte_xor[2] == kern_linear_pte_xor[0])
+ return false;
+
if (guard && !(vstart & ~PUD_MASK) && (vend - vstart) >= PUD_SIZE)
return true;
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 04/10] sparc64: stop IRQ setup when descriptor allocation fails
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
` (2 preceding siblings ...)
2026-10-18 13:45 ` [PATCH v4 03/10] sparc64: avoid huge kernel PUD mappings with small hardware pages Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 05/10] sparc64: validate Fire register properties before initialization Magnus Lindholm
` (5 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7
irq_alloc() returns zero on failure, but build_irq() then configures
IRQ 0, which is reserved for the timer, as a device interrupt.
Halt before publishing the IRQ or changing its descriptor, matching the
existing fatal handler-data allocation path. Returning zero would let
callers continue treating the timer IRQ as a valid device interrupt.
Fixes: 8047e247c899 ("[SPARC64]: Virtualize IRQ numbers.")
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/kernel/irq_64.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c
index 3f55c69d5f3b..4cceeba726ab 100644
--- a/arch/sparc/kernel/irq_64.c
+++ b/arch/sparc/kernel/irq_64.c
@@ -619,6 +619,10 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
irq = bucket_get_irq(__pa(bucket));
if (!irq) {
irq = irq_alloc(0, ino);
+ if (!irq) {
+ prom_printf("IRQ: cannot allocate interrupt descriptor.\n");
+ prom_halt();
+ }
bucket_set_irq(__pa(bucket), irq);
irq_set_chip_and_handler_name(irq, &sun4u_irq,
handle_fasteoi_irq, "IVEC");
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 05/10] sparc64: validate Fire register properties before initialization
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
` (3 preceding siblings ...)
2026-10-18 13:45 ` [PATCH v4 04/10] sparc64: stop IRQ setup when descriptor allocation fails Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 06/10] sparc64: retain the DMA segment start across SG entries Magnus Lindholm
` (4 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7
The Fire interrupt translator reads one reg entry, and PCI PBM setup
reads two, without checking that the firmware supplied enough data.
Check the property before allocating the translator or changing PBM
state. Halt early translation on invalid firmware data rather than leave
raw interrupt numbers untranslated. Return an error from PBM setup so
probe can release its allocations.
Fixes: 861fe90656b8 ("[SPARC64]: SUN4U PCI-E controller support.")
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/kernel/pci_fire.c | 9 +++++++--
arch/sparc/kernel/prom_irqtrans.c | 8 +++++++-
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/arch/sparc/kernel/pci_fire.c b/arch/sparc/kernel/pci_fire.c
index c35a8e3c1eae..db160eeb0ffe 100644
--- a/arch/sparc/kernel/pci_fire.c
+++ b/arch/sparc/kernel/pci_fire.c
@@ -416,7 +416,13 @@ static int pci_fire_pbm_init(struct pci_pbm_info *pbm,
{
const struct linux_prom64_registers *regs;
struct device_node *dp = op->dev.of_node;
- int err;
+ int err, len;
+
+ regs = of_get_property(dp, "reg", &len);
+ if (!regs || len < 2 * sizeof(*regs)) {
+ dev_err(&op->dev, "missing or short reg property\n");
+ return -EINVAL;
+ }
pbm->numa_node = NUMA_NO_NODE;
@@ -429,7 +435,6 @@ static int pci_fire_pbm_init(struct pci_pbm_info *pbm,
pbm->op = op;
pbm->name = dp->full_name;
- regs = of_get_property(dp, "reg", NULL);
pbm->pbm_regs = regs[0].phys_addr;
pbm->controller_regs = regs[1].phys_addr - 0x410000UL;
diff --git a/arch/sparc/kernel/prom_irqtrans.c b/arch/sparc/kernel/prom_irqtrans.c
index 5752bfd73ac0..558ed5114664 100644
--- a/arch/sparc/kernel/prom_irqtrans.c
+++ b/arch/sparc/kernel/prom_irqtrans.c
@@ -544,13 +544,19 @@ static void __init fire_irq_trans_init(struct device_node *dp)
{
const struct linux_prom64_registers *regs;
struct fire_irq_data *irq_data;
+ int len;
+
+ regs = of_get_property(dp, "reg", &len);
+ if (!regs || len < sizeof(*regs)) {
+ prom_printf("%s: missing or short reg property.\n", dp->full_name);
+ prom_halt();
+ }
dp->irq_trans = prom_early_alloc(sizeof(struct of_irq_controller));
dp->irq_trans->irq_build = fire_irq_build;
irq_data = prom_early_alloc(sizeof(struct fire_irq_data));
- regs = of_get_property(dp, "reg", NULL);
dp->irq_trans->data = irq_data;
irq_data->pbm_regs = regs[0].phys_addr;
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 06/10] sparc64: retain the DMA segment start across SG entries
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
` (4 preceding siblings ...)
2026-10-18 13:45 ` [PATCH v4 05/10] sparc64: validate Fire register properties before initialization Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 07/10] sparc64: zero newly allocated IOMMU page tables Magnus Lindholm
` (3 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7
The SG mapping loops reset out_entry to zero for each input entry.
When extending an output segment, the boundary check therefore tests
entry zero instead of the segment's actual starting IOMMU entry.
Adjacent allocations can be merged across a device segment boundary.
Keep out_entry for the lifetime of the mapping loop and update it only
when starting an output segment, in both sun4u and sun4v.
Fixes: f08802572965 ("sparc64: add the segment boundary checking to IOMMUs while merging SG entries")
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/kernel/iommu.c | 4 ++--
arch/sparc/kernel/pci_sun4v.c | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c
index 7613ab0ffb89..a23105b64fb9 100644
--- a/arch/sparc/kernel/iommu.c
+++ b/arch/sparc/kernel/iommu.c
@@ -451,7 +451,7 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist,
int outcount, incount, i;
struct strbuf *strbuf;
struct iommu *iommu;
- unsigned long base_shift;
+ unsigned long base_shift, out_entry = 0;
BUG_ON(direction == DMA_NONE);
@@ -485,7 +485,7 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist,
seg_boundary_size = dma_get_seg_boundary_nr_pages(dev, IO_PAGE_SHIFT);
base_shift = iommu->tbl.table_map_base >> IO_PAGE_SHIFT;
for_each_sg(sglist, s, nelems, i) {
- unsigned long paddr, npages, entry, out_entry = 0, slen;
+ unsigned long paddr, npages, entry, slen;
iopte_t *base;
slen = s->length;
diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c
index 61f14b4c8f90..22584c4de973 100644
--- a/arch/sparc/kernel/pci_sun4v.c
+++ b/arch/sparc/kernel/pci_sun4v.c
@@ -489,7 +489,7 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
struct atu *atu;
struct iommu_map_table *tbl;
u64 mask;
- unsigned long base_shift;
+ unsigned long base_shift, out_entry = 0;
long err;
BUG_ON(direction == DMA_NONE);
@@ -530,7 +530,7 @@ static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
base_shift = tbl->table_map_base >> IO_PAGE_SHIFT;
for_each_sg(sglist, s, nelems, i) {
- unsigned long paddr, npages, entry, out_entry = 0, slen;
+ unsigned long paddr, npages, entry, slen;
slen = s->length;
/* Sanity check */
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 07/10] sparc64: zero newly allocated IOMMU page tables
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
` (5 preceding siblings ...)
2026-10-18 13:45 ` [PATCH v4 06/10] sparc64: retain the DMA segment start across SG entries Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 08/10] sparc64: add SPARC64 VII CPU, MMU and SMP support Magnus Lindholm
` (2 subsequent siblings)
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7
iopte_make_dummy() replaces only the physical page field, preserving
all attributes. Applying it to an uninitialized table leaves validity,
permissions, context and reserved bits dependent on prior page contents.
Zero the table allocation before installing dummy page addresses. Keep
iopte_make_dummy() unchanged for unmap callers that preserve attributes.
This initialization pattern is already present in the initial Git
import; the Fixes tag identifies its earliest recorded version.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/kernel/iommu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c
index a23105b64fb9..66aaafbf1621 100644
--- a/arch/sparc/kernel/iommu.c
+++ b/arch/sparc/kernel/iommu.c
@@ -130,7 +130,7 @@ int iommu_table_init(struct iommu *iommu, int tsbsize,
/* Now allocate and setup the IOMMU page table itself. */
order = get_order(tsbsize);
- page = alloc_pages_node(numa_node, GFP_KERNEL, order);
+ page = alloc_pages_node(numa_node, GFP_KERNEL | __GFP_ZERO, order);
if (!page) {
printk(KERN_ERR "IOMMU: Error, gfp(tsb) failed.\n");
goto out_free_dummy_page;
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 08/10] sparc64: add SPARC64 VII CPU, MMU and SMP support
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
` (6 preceding siblings ...)
2026-10-18 13:45 ` [PATCH v4 07/10] sparc64: zero newly allocated IOMMU page tables Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 14:22 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 09/10] sparc64: add M3000 Oberon PCIe support Magnus Lindholm
2026-10-18 13:45 ` [PATCH v4 10/10] hvc: add an M3000 firmware console backend Magnus Lindholm
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7
Recognize implementation 7 and add the M3000 CPU/MMU/SMP path, including
topology, serialized interrupt dispatch and restrainable ECC handling.
Derive core and package identities from compatible core nodes and their
firmware ancestry instead of the root model or adjacent CPU numbers.
Use physical TSB accesses to avoid sibling aliases in the shared TLB;
synchronize context writes and instruction demaps while preserving
supported firmware modes and the existing non-VII demap routines.
Preserve all 47 physical-address bits in PTE helpers and check every
page-size bit in locked-mapping readback. Keep the PTE masks specific to
VII rather than treating reserved bits as address bits on every sun4u
CPU; this adds a runtime check to the PTE helpers.
Require disabled shared-context and TSB-prefetch valid bits at CPU entry
and firmware handoffs, including callbacks. Unsupported state is recorded
and halted without reentering a potentially locked PROM path.
Select VII at runtime from %ver without a CPU Kconfig option. Reject
unsupported HugeTLB and THP mappings at runtime, before page-size setup
side effects, while retaining huge pages on other CPUs. CPU offlining
remains unsupported on VII.
JPS1 F.10.1 p.458 states the same MMU ordering rule as JPS2. Keep the
new demap routines scoped to VII while Cheetah implementation guarantees
and affected paths receive a separate audit and regression test.
Hardware references (printed sections/pages):
- SPARC64 VII Extensions v1.0: 5.2.9 Table 5-1 p.18 (CPU identification),
8.4.7 pp.42-43 (coherence), A.30 pp.64-65
(physical loads), F.2 pp.103-104 (TTEs), F.10 pp.109-110 (MCNTL),
F.10.2 p.114 (shared context), F.10.4 (indexed TLBs),
F.10.13 pp.127-128 (TSB prefetch),
F.12 p.129 (sibling sharing).
- VII N.1 pp.155-156, N.4.2/N.4.3 p.158, R.3 (IPIs/target IDs);
P.3.1 p.187 and P.7.1/P.7.4 pp.198-200 (error registers).
- JPS2 Common r1.0 F.10.1 p.488, F.10.11 pp.500-502 (MMU ordering).
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/include/asm/head_64.h | 11 ++
arch/sparc/include/asm/hugetlb.h | 3 +
arch/sparc/include/asm/pgtable_64.h | 13 +-
arch/sparc/include/asm/setup.h | 7 +
arch/sparc/include/asm/smp_64.h | 2 +
arch/sparc/include/asm/spitfire.h | 1 +
arch/sparc/include/asm/trap_block.h | 6 +
arch/sparc/kernel/cpu.c | 7 +
arch/sparc/kernel/entry.h | 2 +
arch/sparc/kernel/head_64.S | 20 +++
arch/sparc/kernel/helpers.S | 126 +++++++++++++++
arch/sparc/kernel/prom_64.c | 28 +++-
arch/sparc/kernel/ptrace_64.c | 7 +
arch/sparc/kernel/setup_64.c | 57 +++++++
arch/sparc/kernel/smp_64.c | 83 +++++++++-
arch/sparc/kernel/trampoline_64.S | 34 ++++
arch/sparc/kernel/traps_64.c | 27 ++++
arch/sparc/mm/init_64.c | 113 ++++++++++++-
arch/sparc/mm/tsb.c | 19 ++-
arch/sparc/mm/ultra.S | 236 ++++++++++++++++++++++++++++
20 files changed, 783 insertions(+), 19 deletions(-)
diff --git a/arch/sparc/include/asm/head_64.h b/arch/sparc/include/asm/head_64.h
index 69a2062d992c..a977db986ad8 100644
--- a/arch/sparc/include/asm/head_64.h
+++ b/arch/sparc/include/asm/head_64.h
@@ -20,6 +20,17 @@
#define RTRAP_PSTATE_IRQOFF (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV)
#define RTRAP_PSTATE_AG_IRQOFF (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV|PSTATE_AG)
+#define __SPARC64_VII_ID 0x00040007
+
+#define BRANCH_IF_SPARC64_VII(tmp1, tmp2, label) \
+ rdpr %ver, %tmp1; \
+ srlx %tmp1, 32, %tmp1; \
+ sethi %hi(__SPARC64_VII_ID), %tmp2; \
+ or %tmp2, %lo(__SPARC64_VII_ID), %tmp2; \
+ cmp %tmp1, %tmp2; \
+ be,pn %icc, label; \
+ nop
+
#define __CHEETAH_ID 0x003e0014
#define __JALAPENO_ID 0x003e0016
#define __SERRANO_ID 0x003e0022
diff --git a/arch/sparc/include/asm/hugetlb.h b/arch/sparc/include/asm/hugetlb.h
index d3bc16fbcbbd..53b087562851 100644
--- a/arch/sparc/include/asm/hugetlb.h
+++ b/arch/sparc/include/asm/hugetlb.h
@@ -3,8 +3,11 @@
#define _ASM_SPARC64_HUGETLB_H
#include <asm/page.h>
+#include <asm/spitfire.h>
#ifdef CONFIG_HUGETLB_PAGE
+#define hugepages_supported() (tlb_type != sparc64_vii)
+
struct pud_huge_patch_entry {
unsigned int addr;
unsigned int insn;
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index 0837ebbc5dce..b6dda7b731da 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -146,6 +146,9 @@ bool kern_addr_valid(unsigned long addr);
#define _PAGE_P_4U _AC(0x0000000000000004,UL) /* Privileged Page */
#define _PAGE_W_4U _AC(0x0000000000000002,UL) /* Writable */
+/* SPARC64 VII Extensions F.2: physical address bits 46:13. */
+#define _PAGE_PADDR_VII _AC(0x00007FFFFFFFE000, UL) /* pa[46:13] */
+
/* SUN4V pte bits... */
#define _PAGE_NFO_4V _AC(0x4000000000000000,UL) /* No Fault Only */
#define _PAGE_SOFT2_4V _AC(0x3F00000000000000,UL) /* Software bits, set 2 */
@@ -224,6 +227,8 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot)
}
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+#define has_transparent_hugepage() (tlb_type != sparc64_vii)
+
static inline pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot)
{
pte_t pte = pfn_pte(page_nr, pgprot);
@@ -232,11 +237,14 @@ static inline pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot)
}
#endif
-/* This one can be done with two shifts. */
+/* Extract the physical page number using the processor's TTE format. */
static inline unsigned long pte_pfn(pte_t pte)
{
unsigned long ret;
+ if (tlb_type == sparc64_vii)
+ return (pte_val(pte) & _PAGE_PADDR_VII) >> PAGE_SHIFT;
+
__asm__ __volatile__(
"\n661: sllx %1, %2, %0\n"
" srlx %0, %3, %0\n"
@@ -309,6 +317,9 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t prot)
_PAGE_CP_4V | _PAGE_E_4V |
_PAGE_SPECIAL | _PAGE_PMD_HUGE | _PAGE_SZALL_4V));
+ if (tlb_type == sparc64_vii)
+ mask |= _PAGE_PADDR_VII;
+
return __pte((pte_val(pte) & mask) | (pgprot_val(prot) & ~mask));
}
diff --git a/arch/sparc/include/asm/setup.h b/arch/sparc/include/asm/setup.h
index 21bed5514028..507776ee3b4d 100644
--- a/arch/sparc/include/asm/setup.h
+++ b/arch/sparc/include/asm/setup.h
@@ -49,6 +49,13 @@ unsigned long safe_compute_effective_address(struct pt_regs *, unsigned int);
#ifdef CONFIG_SPARC64
void __init start_early_boot(void);
+extern unsigned int sparc64_ttable_tl0[], sparc64_ttable_tl1[];
+void sparc64_vii_fatal_trap(void);
+void sparc64_vii_check_mmu_state(void);
+extern unsigned long sparc64_vii_mmu_fault[4];
+void sparc64_vii_ecc_trap(void);
+void sparc64_vii_ecc_trap_tl1(void);
+
/* unaligned_64.c */
int handle_ldf_stq(u32 insn, struct pt_regs *regs);
void handle_ld_nf(u32 insn, struct pt_regs *regs);
diff --git a/arch/sparc/include/asm/smp_64.h b/arch/sparc/include/asm/smp_64.h
index 759fb4a9530e..21aa4c209895 100644
--- a/arch/sparc/include/asm/smp_64.h
+++ b/arch/sparc/include/asm/smp_64.h
@@ -34,6 +34,8 @@
DECLARE_PER_CPU(cpumask_t, cpu_sibling_map);
extern cpumask_t cpu_core_map[NR_CPUS];
+extern unsigned long vii_startup_mcntl;
+
void smp_init_cpu_poke(void);
void scheduler_poke(void);
diff --git a/arch/sparc/include/asm/spitfire.h b/arch/sparc/include/asm/spitfire.h
index 79b9dd5e9ac6..9bfa9348dd5a 100644
--- a/arch/sparc/include/asm/spitfire.h
+++ b/arch/sparc/include/asm/spitfire.h
@@ -75,6 +75,7 @@ enum ultra_tlb_layout {
cheetah = 1,
cheetah_plus = 2,
hypervisor = 3,
+ sparc64_vii = 4,
};
extern enum ultra_tlb_layout tlb_type;
diff --git a/arch/sparc/include/asm/trap_block.h b/arch/sparc/include/asm/trap_block.h
index 6cf2a60a0156..04f3d5826e16 100644
--- a/arch/sparc/include/asm/trap_block.h
+++ b/arch/sparc/include/asm/trap_block.h
@@ -67,6 +67,7 @@ struct cpuid_patch_entry {
unsigned int cheetah_jbus[4];
unsigned int starfire[4];
unsigned int sun4v[4];
+ unsigned int vii[4];
};
extern struct cpuid_patch_entry __cpuid_patch, __cpuid_patch_end;
@@ -146,6 +147,11 @@ extern struct sun4v_2insn_patch_entry __sun_m7_2insn_patch,
ldxa [REG] ASI_SCRATCHPAD, REG; \
nop; \
nop; \
+ /* SPARC64 VII: Jupiter ITID is bits 9:0. */ \
+ ldxa [%g0] ASI_UPA_CONFIG, REG; \
+ and REG, 0x3ff, REG; \
+ nop; \
+ nop; \
.previous;
#ifdef CONFIG_SMP
diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c
index 79cd6ccfeac0..19cf910ac41d 100644
--- a/arch/sparc/kernel/cpu.c
+++ b/arch/sparc/kernel/cpu.c
@@ -123,6 +123,7 @@ static const struct manufacturer_info __initconst manufacturer_info[] = {
FPU(-1, NULL)
}
},{
+ /* SPARC64 Fujitsu %ver manufacturer 4 shares the sparc32 TI value. */
PSR_IMPL_TI,
.cpu_info = {
CPU(0, "Texas Instruments, Inc. - SuperSparc-(II)"),
@@ -132,6 +133,9 @@ static const struct manufacturer_info __initconst manufacturer_info[] = {
CPU(3, "Texas Instruments, Inc. - SuperSparc 51"),
CPU(4, "Texas Instruments, Inc. - SuperSparc 61"),
CPU(5, "Texas Instruments, Inc. - unknown"),
+#ifdef CONFIG_SPARC64
+ CPU(7, "Fujitsu SPARC64 VII / VII+"),
+#endif
CPU(-1, NULL)
},
.fpu_info = {
@@ -139,6 +143,9 @@ static const struct manufacturer_info __initconst manufacturer_info[] = {
FPU(0, "SuperSparc on-chip FPU"),
/* SparcClassic */
FPU(4, "TI MicroSparc on chip FPU"),
+#ifdef CONFIG_SPARC64
+ FPU(7, "Fujitsu SPARC64 VII integrated FPU"),
+#endif
FPU(-1, NULL)
}
},{
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h
index c746c0fd5d6b..517eb1a054f2 100644
--- a/arch/sparc/kernel/entry.h
+++ b/arch/sparc/kernel/entry.h
@@ -249,5 +249,7 @@ extern unsigned long ivector_table_pa;
void init_irqwork_curcpu(void);
void sun4v_register_mondo_queues(int this_cpu);
+void sparc64_vii_ecc_error(struct pt_regs *regs);
+
#endif /* CONFIG_SPARC32 */
#endif /* _ENTRY_H */
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
index cf0549134234..365ca8198ea0 100644
--- a/arch/sparc/kernel/head_64.S
+++ b/arch/sparc/kernel/head_64.S
@@ -485,6 +485,8 @@ EXPORT_SYMBOL(sun4v_chip_type)
80:
BRANCH_IF_SUN4V(g1, jump_to_sun4u_init)
+ /* Preserve Fujitsu firmware cache/MMU control, not Spitfire LSU. */
+ BRANCH_IF_SPARC64_VII(g1,g7,jump_to_sun4u_init)
BRANCH_IF_CHEETAH_BASE(g1,g7,cheetah_boot)
BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1,g7,cheetah_plus_boot)
ba,pt %xcc, spitfire_boot
@@ -574,6 +576,7 @@ sun4v_init:
ba,a,pt %xcc, niagara_tlb_fixup
sun4u_continue:
+ BRANCH_IF_SPARC64_VII(g1,g7,sparc64_vii_tlb_fixup)
BRANCH_IF_ANY_CHEETAH(g1, g7, cheetah_tlb_fixup)
ba,a,pt %xcc, spitfire_tlb_fixup
@@ -692,6 +695,22 @@ cheetah_tlb_fixup:
ba,a,pt %xcc, tlb_fixup_done
+sparc64_vii_tlb_fixup:
+ call sparc64_vii_check_mmu_state
+ nop
+ mov 4, %g2
+ sethi %hi(tlb_type), %g1
+ stw %g2, [%g1 + %lo(tlb_type)]
+ call generic_patch_copyops
+ nop
+ call generic_patch_bzero
+ nop
+ call generic_patch_pageops
+ nop
+ call sparc64_vii_patch_cachetlbops
+ nop
+ ba,a,pt %xcc, tlb_fixup_done
+
spitfire_tlb_fixup:
/* Set TLB type to spitfire. */
mov 0, %g2
@@ -841,6 +860,7 @@ setup_trap_table:
sllx %o2, 32, %o2
wr %o2, 0, %tick_cmpr
+ BRANCH_IF_SPARC64_VII(o2, o3, 1f)
BRANCH_IF_ANY_CHEETAH(o2, o3, 1f)
ba,a,pt %xcc, 2f
diff --git a/arch/sparc/kernel/helpers.S b/arch/sparc/kernel/helpers.S
index 9b3f74706cfb..b4ea0ca95a37 100644
--- a/arch/sparc/kernel/helpers.S
+++ b/arch/sparc/kernel/helpers.S
@@ -64,3 +64,129 @@ real_hard_smp_processor_id:
#endif
.size real_hard_smp_processor_id,.-real_hard_smp_processor_id
EXPORT_SYMBOL_GPL(real_hard_smp_processor_id)
+
+ .align 32
+ .globl sparc64_vii_ecc_trap
+sparc64_vii_ecc_trap:
+ TRAP(sparc64_vii_ecc_error)
+ .globl sparc64_vii_ecc_trap_tl1
+sparc64_vii_ecc_trap_tl1:
+ TRAPTL1(sparc64_vii_ecc_error)
+
+ .align 32
+ .globl sparc64_vii_fatal_trap
+sparc64_vii_fatal_trap:
+ wrpr %g0, 15, %pil
+ sethi %hi(sparc64_vii_fault_record), %g1
+ or %g1, %lo(sparc64_vii_fault_record), %g1
+ rdpr %tpc, %g2
+ stx %g2, [%g1 + 8]
+ rdpr %tnpc, %g2
+ stx %g2, [%g1 + 16]
+ rdpr %tstate, %g2
+ stx %g2, [%g1 + 24]
+ rdpr %tl, %g2
+ stx %g2, [%g1 + 32]
+ rdpr %tt, %g2
+ stx %g2, [%g1 + 40]
+ /* VII Appendix P: state-change error information, read-only here. */
+ mov 0x18, %g2
+ ldxa [%g2] 0x4c, %g2
+ stx %g2, [%g1 + 48]
+ set 0x56494900, %g2
+ stx %g2, [%g1]
+ membar #Sync
+1: ba,pt %xcc, 1b
+ nop
+
+ .data
+ .align 64
+ .globl sparc64_vii_fault_record
+sparc64_vii_fault_record:
+ .xword 0, 0, 0, 0, 0, 0, 0, 0
+ .previous
+
+/*
+ * Require disabled shared-context/prefetch valid bits (VII F.10.2/F.10.13).
+ * Failures populate sparc64_vii_mmu_fault; a locked PROM cannot report them.
+ */
+ .align 32
+ .globl sparc64_vii_check_mmu_state
+ .type sparc64_vii_check_mmu_state,#function
+sparc64_vii_check_mmu_state:
+ /* Preserve %o0, which carries the secondary startup cookie. */
+ mov 0x68, %o1
+ ldxa [%o1] 0x58, %o2
+ sethi %hi(0x8000), %o3
+ sllx %o3, 32, %o4
+ or %o3, %o4, %o3
+ andcc %o2, %o3, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 1, %o1
+ mov 0x0, %o1
+ ldxa [%o1] 0x61, %o2
+ andcc %o2, 0x100, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 2, %o1
+ mov 0x8, %o1
+ ldxa [%o1] 0x61, %o2
+ andcc %o2, 0x100, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 3, %o1
+ mov 0x40, %o1
+ ldxa [%o1] 0x61, %o2
+ andcc %o2, 0x100, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 4, %o1
+ mov 0x48, %o1
+ ldxa [%o1] 0x61, %o2
+ andcc %o2, 0x100, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 5, %o1
+ mov 0x0, %o1
+ ldxa [%o1] 0x62, %o2
+ andcc %o2, 0x100, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 6, %o1
+ mov 0x8, %o1
+ ldxa [%o1] 0x62, %o2
+ andcc %o2, 0x100, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 7, %o1
+ mov 0x40, %o1
+ ldxa [%o1] 0x62, %o2
+ andcc %o2, 0x100, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 8, %o1
+ mov 0x48, %o1
+ ldxa [%o1] 0x62, %o2
+ andcc %o2, 0x100, %g0
+ bne,pn %xcc, .Lvii_mmu_bad
+ mov 9, %o1
+ retl
+ nop
+.Lvii_mmu_bad:
+ /* PROM may already be locked; record the failure without reentering it. */
+ wrpr %g0, 15, %pil
+ rdpr %pstate, %o3
+ andn %o3, PSTATE_IE, %o3
+ wrpr %o3, 0, %pstate
+ sethi %hi(sparc64_vii_mmu_fault), %o3
+ or %o3, %lo(sparc64_vii_mmu_fault), %o3
+ stx %o1, [%o3 + 8]
+ stx %o2, [%o3 + 16]
+ stx %o7, [%o3 + 24]
+ set 0x56494d4d, %o2
+ stx %o2, [%o3]
+ membar #Sync
+1: ba,pt %xcc, 1b
+ nop
+ .size sparc64_vii_check_mmu_state,.-sparc64_vii_check_mmu_state
+
+ .data
+ .align 32
+/* Fault record: magic, register (1 shared, 2-5 I, 6-9 D), value, caller. */
+ .globl sparc64_vii_mmu_fault
+sparc64_vii_mmu_fault:
+ .xword 0, 0, 0, 0
+ .previous
diff --git a/arch/sparc/kernel/prom_64.c b/arch/sparc/kernel/prom_64.c
index aa4799cbb9c1..c4448ec6e919 100644
--- a/arch/sparc/kernel/prom_64.c
+++ b/arch/sparc/kernel/prom_64.c
@@ -501,7 +501,30 @@ void __init of_populate_present_mask(void)
of_iterate_over_cpus(record_one_cpu, 0);
}
-static void *fill_in_one_cpu(struct device_node *dp, int cpuid, int arg)
+static void __init vii_cpu_topology(struct device_node *dp, int cpuid)
+{
+ struct device_node *core = dp->parent;
+ struct device_node *package;
+
+ cpu_data(cpuid).core_id = 0;
+ cpu_data(cpuid).proc_id = -1;
+ cpu_data(cpuid).sock_id = cpuid;
+ if (!core || !of_node_is_type(core, "core") ||
+ !of_device_is_compatible(core, "FJSV,SPARC64-VII"))
+ return;
+ package = core->parent;
+ if (!package || !of_node_name_eq(package, "cmp"))
+ return;
+ if (core->unique_id >= USHRT_MAX || package->unique_id > SHRT_MAX)
+ return;
+
+ /* Node IDs distinguish equal core addresses in different packages. */
+ cpu_data(cpuid).core_id = core->unique_id + 1;
+ cpu_data(cpuid).proc_id = package->unique_id;
+ cpu_data(cpuid).sock_id = package->unique_id;
+}
+
+static void * __init fill_in_one_cpu(struct device_node *dp, int cpuid, int arg)
{
struct device_node *portid_parent = NULL;
int portid = -1;
@@ -585,6 +608,9 @@ static void *fill_in_one_cpu(struct device_node *dp, int cpuid, int arg)
cpu_data(cpuid).proc_id = -1;
}
+ if (tlb_type == sparc64_vii)
+ vii_cpu_topology(dp, cpuid);
+
return NULL;
}
diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c
index 825ddf55fece..0d537ae7bef3 100644
--- a/arch/sparc/kernel/ptrace_64.c
+++ b/arch/sparc/kernel/ptrace_64.c
@@ -109,6 +109,13 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
{
BUG_ON(len > PAGE_SIZE);
+ if (tlb_type == sparc64_vii) {
+ /* VII caches are coherent; ASI_DCACHE_INVALIDATE is not valid. */
+ flush_icache_range((unsigned long)kaddr,
+ (unsigned long)kaddr + len);
+ return;
+ }
+
if (tlb_type == hypervisor)
return;
diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c
index 63615f5c99b4..ee8a79565ff5 100644
--- a/arch/sparc/kernel/setup_64.c
+++ b/arch/sparc/kernel/setup_64.c
@@ -184,6 +184,9 @@ static void __init per_cpu_patch(void)
else
insns = &p->cheetah_safari[0];
break;
+ case sparc64_vii:
+ insns = &p->vii[0];
+ break;
case hypervisor:
insns = &p->sun4v[0];
break;
@@ -349,6 +352,42 @@ static void __init pause_patch(void)
}
}
+/*
+ * Route restrainable ECC (0x63) to the VII C handler.
+ * Other patched errors record state and halt without touching Spitfire
+ * error/cache registers, including at TL>1.
+ */
+static void __init vii_patch_error_traps(void)
+{
+ static const unsigned int traps[] = { 0x0a, 0x32, 0x40, 0x63 };
+ unsigned int *tables[] = { sparc64_ttable_tl0, sparc64_ttable_tl1 };
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(tables); i++) {
+ for (j = 0; j < ARRAY_SIZE(traps); j++) {
+ unsigned int *slot = tables[i] + traps[j] * 8;
+ void (*handler)(void) = sparc64_vii_fatal_trap;
+ long delta;
+
+ if (traps[j] == 0x63)
+ handler = i ? sparc64_vii_ecc_trap_tl1 :
+ sparc64_vii_ecc_trap;
+ delta = (long)handler - (long)slot;
+
+ /* ba (disp22), followed by nop. */
+ if (delta < -(1L << 23) || delta >= (1L << 23)) {
+ prom_printf("VII: error-trap handler is out of branch range.\n");
+ prom_halt();
+ }
+ slot[1] = 0x01000000;
+ slot[0] = 0x10800000 | ((delta >> 2) & 0x3fffff);
+ /* Publish the trap instructions before synchronizing fetch. */
+ wmb();
+ __asm__ __volatile__("flush %0" : : "r" (slot));
+ }
+ }
+}
+
void __init start_early_boot(void)
{
int cpu;
@@ -358,6 +397,24 @@ void __init start_early_boot(void)
sun4v_patch();
smp_init_cpu_poke();
+ if (tlb_type == sparc64_vii) {
+ unsigned long mcntl;
+
+ vii_patch_error_traps();
+
+ __asm__ __volatile__("ldxa [%1] 0x45, %0"
+ : "=r" (mcntl) : "r" (8UL));
+ /*
+ * Reject context hashing, forced instruction caching of noncacheable
+ * addresses and multiple-page-size contexts (VII Table F-4).
+ * Preserve firmware state; stop if a mode transition is required.
+ */
+ if (mcntl & 0x101c0UL) {
+ prom_printf("VII-BOOT: incompatible inherited MCNTL; halted\n");
+ prom_halt();
+ }
+ }
+
cpu = hard_smp_processor_id();
if (cpu >= NR_CPUS) {
prom_printf("Serious problem, boot cpu id (%d) >= NR_CPUS (%d)\n",
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index 371460e34484..ab010c2f77bc 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -288,7 +288,6 @@ static void smp_synchronize_one_tick(int cpu)
static void ldom_startcpu_cpuid(unsigned int cpu, unsigned long thread_reg,
void **descrp)
{
- extern unsigned long sparc64_ttable_tl0;
extern unsigned long kern_locked_tte_data;
struct hvtramp_descr *hdesc;
unsigned long trampoline_ra;
@@ -344,6 +343,9 @@ extern unsigned long sparc64_cpu_startup;
*/
static struct thread_info *cpu_new_thread = NULL;
+/* Written before the secondary validates its inherited MMU control. */
+unsigned long vii_startup_mcntl;
+
static int smp_boot_one_cpu(unsigned int cpu, struct task_struct *idle)
{
unsigned long entry =
@@ -354,6 +356,8 @@ static int smp_boot_one_cpu(unsigned int cpu, struct task_struct *idle)
int timeout, ret;
callin_flag = 0;
+ if (tlb_type == sparc64_vii)
+ WRITE_ONCE(vii_startup_mcntl, ~0UL);
cpu_new_thread = task_thread_info(idle);
if (tlb_type == hypervisor) {
@@ -381,6 +385,9 @@ static int smp_boot_one_cpu(unsigned int cpu, struct task_struct *idle)
ret = 0;
} else {
printk("Processor %d is stuck.\n", cpu);
+ if (tlb_type == sparc64_vii)
+ pr_err("VII secondary CPU %u MCNTL=%lx (all ones: MCNTL not captured)\n",
+ cpu, READ_ONCE(vii_startup_mcntl));
ret = -ENODEV;
}
cpu_new_thread = NULL;
@@ -390,6 +397,55 @@ static int smp_boot_one_cpu(unsigned int cpu, struct task_struct *idle)
return ret;
}
+/*
+ * Serialize targets through BUSY/NACK pair zero for simple retry handling.
+ * VII Extensions N.4.3 permits pipelining through multiple pairs.
+ */
+static void vii_xcall_deliver(struct trap_per_cpu *tb, int cnt)
+{
+ u64 *data = __va(tb->cpu_mondo_block_pa);
+ u16 *cpus = __va(tb->cpu_list_pa);
+ unsigned long pstate, disabled;
+ int i;
+
+ __asm__ __volatile__("rdpr %%pstate, %0" : "=r" (pstate));
+ disabled = pstate & ~PSTATE_IE;
+ for (i = 0; i < cnt; i++) {
+ unsigned long target = ((unsigned long)cpus[i] << 14) | 0x70;
+ unsigned long status = 0;
+ int retry;
+
+ for (retry = 0; retry < 10000; retry++) {
+ int polls = 1000000;
+
+ __asm__ __volatile__("wrpr %0, 0, %%pstate\n\t"
+ "stxa %1, [%4] %7\n\t"
+ "stxa %2, [%5] %7\n\t"
+ "stxa %3, [%6] %7\n\t"
+ "membar #Sync\n\t"
+ "stxa %%g0, [%8] %7\n\t"
+ "membar #Sync"
+ : : "r" (disabled), "r" (data[0]), "r" (data[1]),
+ "r" (data[2]), "r" (0x40UL), "r" (0x50UL),
+ "r" (0x60UL), "i" (ASI_INTR_W), "r" (target)
+ : "memory");
+ do {
+ __asm__ __volatile__("ldxa [%%g0] %1, %0"
+ : "=r" (status) : "i" (ASI_INTR_DISPATCH_STAT));
+ } while ((status & 1) && --polls);
+ __asm__ __volatile__("wrpr %0, 0, %%pstate"
+ : : "r" (pstate) : "memory");
+ if (!polls)
+ panic("VII IPI busy timeout target=%u status=%lx", cpus[i], status);
+ if (!(status & 2))
+ break;
+ udelay(2);
+ }
+ if (retry == 10000)
+ panic("VII IPI NACK timeout target=%u status=%lx", cpus[i], status);
+ }
+}
+
static void spitfire_xcall_helper(u64 data0, u64 data1, u64 data2, u64 pstate, unsigned long cpu)
{
u64 result, target;
@@ -1205,7 +1261,9 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
void __init smp_setup_processor_id(void)
{
- if (tlb_type == spitfire)
+ if (tlb_type == sparc64_vii)
+ xcall_deliver_impl = vii_xcall_deliver;
+ else if (tlb_type == spitfire)
xcall_deliver_impl = spitfire_xcall_deliver;
else if (tlb_type == cheetah || tlb_type == cheetah_plus)
xcall_deliver_impl = cheetah_xcall_deliver;
@@ -1241,8 +1299,13 @@ void smp_fill_in_sib_core_maps(void)
cpu_data(j).max_cache_id)
cpumask_set_cpu(j, &cpu_core_sib_cache_map[i]);
- if (cpu_data(i).sock_id == cpu_data(j).sock_id)
+ if (tlb_type == sparc64_vii) {
+ if (i == j || (cpu_data(i).proc_id >= 0 &&
+ cpu_data(i).proc_id == cpu_data(j).proc_id))
+ cpumask_set_cpu(j, &cpu_core_sib_map[i]);
+ } else if (cpu_data(i).sock_id == cpu_data(j).sock_id) {
cpumask_set_cpu(j, &cpu_core_sib_map[i]);
+ }
}
}
@@ -1256,8 +1319,14 @@ void smp_fill_in_sib_core_maps(void)
}
for_each_present_cpu(j) {
- if (cpu_data(i).proc_id ==
- cpu_data(j).proc_id)
+ bool sibling;
+
+ /* VII proc_id identifies the package, not the core. */
+ if (tlb_type == sparc64_vii)
+ sibling = cpu_data(i).core_id == cpu_data(j).core_id;
+ else
+ sibling = cpu_data(i).proc_id == cpu_data(j).proc_id;
+ if (sibling)
cpumask_set_cpu(j, &per_cpu(cpu_sibling_map, i));
}
}
@@ -1326,6 +1395,10 @@ int __cpu_disable(void)
cpuinfo_sparc *c;
int i;
+ /* VII secondary startup does not implement offline/restart. */
+ if (tlb_type == sparc64_vii)
+ return -EOPNOTSUPP;
+
for_each_cpu(i, &cpu_core_map[cpu])
cpumask_clear_cpu(cpu, &cpu_core_map[i]);
cpumask_clear(&cpu_core_map[cpu]);
diff --git a/arch/sparc/kernel/trampoline_64.S b/arch/sparc/kernel/trampoline_64.S
index 447334730122..664d69b84b83 100644
--- a/arch/sparc/kernel/trampoline_64.S
+++ b/arch/sparc/kernel/trampoline_64.S
@@ -21,6 +21,18 @@
#include <asm/hypervisor.h>
#include <asm/cpudata.h>
+/* Validate each direct firmware return without reading %ver on sun4v. */
+ .macro VII_CHECK_MMU_STATE
+ BRANCH_IF_SUN4V(g1, .Lvii_mmu_done\@)
+ BRANCH_IF_SPARC64_VII(g1, g5, .Lvii_mmu_check\@)
+ ba,pt %xcc, .Lvii_mmu_done\@
+ nop
+.Lvii_mmu_check\@:
+ call sparc64_vii_check_mmu_state
+ nop
+.Lvii_mmu_done\@:
+ .endm
+
.data
.align 8
call_method:
@@ -41,12 +53,29 @@ tramp_stack:
.globl sparc64_cpu_startup, sparc64_cpu_startup_end
sparc64_cpu_startup:
BRANCH_IF_SUN4V(g1, niagara_startup)
+ BRANCH_IF_SPARC64_VII(g1, g5, vii_startup)
BRANCH_IF_CHEETAH_BASE(g1, g5, cheetah_startup)
BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1, g5, cheetah_plus_startup)
ba,pt %xcc, spitfire_startup
nop
+vii_startup:
+ /* Preserve firmware cache/MMU control, as on the boot CPU. */
+ mov 8, %g1
+ ldxa [%g1] 0x45, %g5
+ sethi %hi(vii_startup_mcntl), %g1
+ stx %g5, [%g1 + %lo(vii_startup_mcntl)]
+ membar #Sync
+ call sparc64_vii_check_mmu_state
+ nop
+ set 0x101c0, %g1
+ andcc %g5, %g1, %g0
+1: bne,pn %xcc, 1b
+ nop
+ ba,pt %xcc, niagara_startup
+ nop
+
cheetah_plus_startup:
/* Preserve OBP chosen DCU and DCR register settings. */
ba,pt %xcc, cheetah_generic_startup
@@ -144,6 +173,7 @@ startup_continue:
lduw [%l6 + %lo(num_kernel_image_mappings)], %l6
mov 15, %l7
+ BRANCH_IF_SPARC64_VII(g1,g5,2f)
BRANCH_IF_ANY_CHEETAH(g1,g5,2f)
mov 63, %l7
@@ -182,6 +212,7 @@ startup_continue:
ldx [%g2 + 0x08], %o1
call %o1
add %sp, (2047 + 128), %o0
+ VII_CHECK_MMU_STATE
/* Lock into D-MMU */
sethi %hi(call_method), %g2
@@ -215,6 +246,7 @@ startup_continue:
ldx [%g2 + 0x08], %o1
call %o1
add %sp, (2047 + 128), %o0
+ VII_CHECK_MMU_STATE
add %l5, 1, %l5
cmp %l5, %l6
@@ -396,6 +428,7 @@ after_lock_tlb:
ldx [%g2 + 0x08], %o1
call %o1
add %sp, (2047 + 128), %o0
+ VII_CHECK_MMU_STATE
ba,pt %xcc, 3f
nop
@@ -413,6 +446,7 @@ after_lock_tlb:
ldx [%g2 + 0x08], %o1
call %o1
add %sp, (2047 + 128), %o0
+ VII_CHECK_MMU_STATE
3: sethi %hi(prom_entry_lock), %g2
/*
diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c
index 28cb0d66ab40..3c693b7bd32b 100644
--- a/arch/sparc/kernel/traps_64.c
+++ b/arch/sparc/kernel/traps_64.c
@@ -2716,6 +2716,33 @@ void do_privact(struct pt_regs *regs)
do_privop(regs);
}
+/* SPARC64 VII Extensions, Appendix P.7.1 and P.7.4. */
+#define VII_AFSR_DEGRADATION ((1UL << 12) | (1UL << 11) | (1UL << 10))
+
+void sparc64_vii_ecc_error(struct pt_regs *regs)
+{
+ enum ctx_state prev_state = exception_enter();
+ unsigned long afsr;
+
+ __asm__ __volatile__("ldxa [%%g0] 0x4c, %0" : "=r" (afsr));
+ /* An ECC trap without a sticky error indication is explicitly allowed. */
+ if (!afsr)
+ goto out;
+
+ /* No data recovery is implemented for raw UE or store bus errors. */
+ if (afsr & ~VII_AFSR_DEGRADATION)
+ panic("SPARC64 VII: unhandled ECC error, CPU %u AFSR=%lx TPC=%lx",
+ smp_processor_id(), afsr, regs->tpc);
+
+ /* Hardware has already reduced the affected cache/TLB associativity. */
+ __asm__ __volatile__("stxa %0, [%%g0] 0x4c; membar #Sync"
+ : : "r" (afsr) : "memory");
+ pr_warn_ratelimited("SPARC64 VII: CPU %u cache/TLB degradation, AFSR=%lx\n",
+ smp_processor_id(), afsr);
+out:
+ exception_exit(prev_state);
+}
+
/* Trap level 1 stuff or other traps we should never see... */
void do_cee(struct pt_regs *regs)
{
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index f3f7eba08469..f4f3e3d1e9b0 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -271,7 +271,8 @@ static inline void tsb_insert(struct tsb *ent, unsigned long tag, unsigned long
{
unsigned long tsb_addr = (unsigned long) ent;
- if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor ||
+ tlb_type == sparc64_vii)
tsb_addr = __pa(tsb_addr);
__tsb_insert(tsb_addr, tag, pte);
@@ -329,6 +330,9 @@ static void __update_mmu_tsb_insert(struct mm_struct *mm, unsigned long tsb_inde
#ifdef CONFIG_HUGETLB_PAGE
static int __init hugetlbpage_init(void)
{
+ if (!hugepages_supported())
+ return 0;
+
hugetlb_add_hstate(HPAGE_64K_SHIFT - PAGE_SHIFT);
hugetlb_add_hstate(HPAGE_SHIFT - PAGE_SHIFT);
hugetlb_add_hstate(HPAGE_256MB_SHIFT - PAGE_SHIFT);
@@ -356,6 +360,9 @@ bool __init arch_hugetlb_valid_size(unsigned long size)
unsigned int hugepage_shift = ilog2(size);
unsigned int hv_pgsz_mask;
+ if (!hugepages_supported())
+ return false;
+
switch (hugepage_shift) {
case HPAGE_16GB_SHIFT:
hv_pgsz_mask = HV_PGSZ_MASK_16GB;
@@ -495,6 +502,12 @@ EXPORT_SYMBOL(flush_dcache_folio);
void __kprobes flush_icache_range(unsigned long start, unsigned long end)
{
+ if (tlb_type == sparc64_vii) {
+ /* Coherent caches still require instruction-stream synchronization. */
+ __asm__ __volatile__("membar #Sync\n\tflush %0"
+ : : "r" (start) : "memory");
+ return;
+ }
/* Cheetah and Hypervisor platform cpus have coherent I-cache. */
if (tlb_type == spitfire) {
unsigned long kaddr;
@@ -530,6 +543,8 @@ void mmu_info(struct seq_file *m)
seq_printf(m, "MMU Type\t: Cheetah\n");
else if (tlb_type == cheetah_plus)
seq_printf(m, "MMU Type\t: Cheetah+\n");
+ else if (tlb_type == sparc64_vii)
+ seq_puts(m, "MMU Type\t: Fujitsu SPARC64 VII\n");
else if (tlb_type == spitfire)
seq_printf(m, "MMU Type\t: Spitfire\n");
else if (tlb_type == hypervisor)
@@ -666,6 +681,52 @@ static void __init hypervisor_tlb_lock(unsigned long vaddr,
static unsigned long kern_large_tte(unsigned long paddr);
+/*
+ * Reserve the kernel image mappings in the lower half of the fully
+ * associative bank. Check existing reservations, firmware
+ * status and physical slot readback before using the mappings.
+ */
+static void __init vii_check_slots(void)
+{
+ int i;
+
+ if (num_kernel_image_mappings > 2) {
+ prom_printf("VII-BOOT: kernel exceeds initial 8MB mapping budget\n");
+ prom_halt();
+ }
+ for (i = 16 - num_kernel_image_mappings; i <= 15; i++) {
+ unsigned long d = cheetah_get_ldtlb_data(i);
+ unsigned long t = cheetah_get_litlb_data(i);
+
+ if (((d & (_PAGE_VALID | _PAGE_L_4U)) ==
+ (_PAGE_VALID | _PAGE_L_4U)) ||
+ ((t & (_PAGE_VALID | _PAGE_L_4U)) ==
+ (_PAGE_VALID | _PAGE_L_4U))) {
+ prom_printf("VII-BOOT: reserved slot %d occupied; halted\n", i);
+ prom_halt();
+ }
+ }
+}
+
+static void __init vii_check_mapping(int slot, unsigned long va,
+ unsigned long tte)
+{
+ /* VII F.2 #238 leaves PA/VA bits 21:13 arbitrary on 4MB TLB reads. */
+ unsigned long offset_mask = GENMASK(21, 13);
+ unsigned long mask = (_PAGE_VALID | _PAGE_SZALL_4U |
+ _PAGE_PADDR_VII | _PAGE_L_4U) & ~offset_mask;
+ unsigned long d = cheetah_get_ldtlb_data(slot);
+ unsigned long t = cheetah_get_litlb_data(slot);
+ unsigned long dt = cheetah_get_ldtlb_tag(slot);
+ unsigned long it = cheetah_get_litlb_tag(slot);
+
+ if ((d & mask) != (tte & mask) || (t & mask) != (tte & mask) ||
+ ((dt ^ va) & ~offset_mask) || ((it ^ va) & ~offset_mask)) {
+ prom_printf("VII-BOOT: mapping readback mismatch; halted\n");
+ prom_halt();
+ }
+}
+
static void __init remap_kernel(void)
{
unsigned long phys_page, tte_vaddr, tte_data;
@@ -677,6 +738,9 @@ static void __init remap_kernel(void)
kern_locked_tte_data = tte_data;
+ if (tlb_type == sparc64_vii)
+ vii_check_slots();
+
/* Now lock us into the TLBs via Hypervisor or OBP. */
if (tlb_type == hypervisor) {
for (i = 0; i < num_kernel_image_mappings; i++) {
@@ -687,8 +751,22 @@ static void __init remap_kernel(void)
}
} else {
for (i = 0; i < num_kernel_image_mappings; i++) {
- prom_dtlb_load(tlb_ent - i, tte_data, tte_vaddr);
- prom_itlb_load(tlb_ent - i, tte_data, tte_vaddr);
+ long ret;
+
+ ret = prom_dtlb_load(tlb_ent - i, tte_data, tte_vaddr);
+ if (ret && tlb_type == sparc64_vii) {
+ prom_printf("SUNW,dtlb-load failed: idx=%d va=%lx tte=%lx rc=%lx\n",
+ tlb_ent - i, tte_vaddr, tte_data, ret);
+ prom_halt();
+ }
+ ret = prom_itlb_load(tlb_ent - i, tte_data, tte_vaddr);
+ if (ret && tlb_type == sparc64_vii) {
+ prom_printf("SUNW,itlb-load failed: idx=%d va=%lx tte=%lx rc=%lx\n",
+ tlb_ent - i, tte_vaddr, tte_data, ret);
+ prom_halt();
+ }
+ if (tlb_type == sparc64_vii)
+ vii_check_mapping(tlb_ent - i, tte_vaddr, tte_data);
tte_vaddr += 0x400000;
tte_data += 0x400000;
}
@@ -713,6 +791,9 @@ static void __init inherit_prom_mappings(void)
void prom_world(int enter)
{
+ if (tlb_type == sparc64_vii)
+ sparc64_vii_check_mmu_state();
+
/*
* No need to change the address space any more, just flush
* the register windows
@@ -724,6 +805,11 @@ void __flush_dcache_range(unsigned long start, unsigned long end)
{
unsigned long va;
+ if (tlb_type == sparc64_vii) {
+ __asm__ __volatile__("membar #Sync" : : : "memory");
+ return;
+ }
+
if (tlb_type == spitfire) {
int n = 0;
@@ -2347,7 +2433,8 @@ void __init paging_init(void)
else
sun4u_pgprot_init();
- if (tlb_type == cheetah_plus ||
+ /* VII supports ASI_QUAD_LDD_PHYS (0x34); avoid shared-TLB TSB aliases. */
+ if (tlb_type == cheetah_plus || tlb_type == sparc64_vii ||
tlb_type == hypervisor) {
tsb_phys_patch();
ktsb_phys_patch();
@@ -2369,6 +2456,15 @@ void __init paging_init(void)
read_obp_memory("available", &pavail[0], &pavail_ents);
read_obp_memory("available", &pavail[0], &pavail_ents);
+ if (tlb_type == sparc64_vii) {
+ for (i = 0; i < pall_ents; i++) {
+ if (pall[i].phys_addr >= (1UL << 40) ||
+ pall[i].reg_size > (1UL << 40) - pall[i].phys_addr) {
+ prom_printf("VII-BOOT: RAM exceeds initial PA layout\n");
+ prom_halt();
+ }
+ }
+ }
phys_base = 0xffffffffffffffffUL;
for (i = 0; i < pavail_ents; i++) {
phys_base = min(phys_base, pavail[i].phys_addr);
@@ -2402,7 +2498,7 @@ void __init paging_init(void)
memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir));
inherit_prom_mappings();
-
+
/* Ok, we can use our TLB miss and window trap handlers safely. */
setup_tba();
@@ -2813,9 +2909,14 @@ void __flush_tlb_all(void)
spitfire_put_itlb_data(i, 0x0UL);
}
}
- } else if (tlb_type == cheetah || tlb_type == cheetah_plus) {
+ } else if (tlb_type == cheetah || tlb_type == cheetah_plus ||
+ tlb_type == sparc64_vii) {
cheetah_flush_dtlb_all();
cheetah_flush_itlb_all();
+ /* JPS2 F.10.1 requires instruction visibility after demap-all. */
+ if (tlb_type == sparc64_vii)
+ __asm__ __volatile__("flush %0"
+ : : "r" (KERNBASE) : "memory");
}
__asm__ __volatile__("wrpr %0, 0, %%pstate"
: : "r" (pstate));
diff --git a/arch/sparc/mm/tsb.c b/arch/sparc/mm/tsb.c
index 5fe52a64c7e7..ba5dec3cb0f7 100644
--- a/arch/sparc/mm/tsb.c
+++ b/arch/sparc/mm/tsb.c
@@ -126,7 +126,8 @@ void flush_tsb_user(struct tlb_batch *tb)
if (tb->hugepage_shift < REAL_HPAGE_SHIFT) {
base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
- if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor ||
+ tlb_type == sparc64_vii)
base = __pa(base);
if (tb->hugepage_shift == PAGE_SHIFT)
__flush_tsb_one(tb, PAGE_SHIFT, base, nentries);
@@ -140,7 +141,8 @@ void flush_tsb_user(struct tlb_batch *tb)
else if (mm->context.tsb_block[MM_TSB_HUGE].tsb) {
base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
- if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor ||
+ tlb_type == sparc64_vii)
base = __pa(base);
__flush_huge_tsb_one(tb, REAL_HPAGE_SHIFT, base, nentries,
tb->hugepage_shift);
@@ -159,7 +161,8 @@ void flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr,
if (hugepage_shift < REAL_HPAGE_SHIFT) {
base = (unsigned long) mm->context.tsb_block[MM_TSB_BASE].tsb;
nentries = mm->context.tsb_block[MM_TSB_BASE].tsb_nentries;
- if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor ||
+ tlb_type == sparc64_vii)
base = __pa(base);
if (hugepage_shift == PAGE_SHIFT)
__flush_tsb_one_entry(base, vaddr, PAGE_SHIFT,
@@ -174,7 +177,8 @@ void flush_tsb_user_page(struct mm_struct *mm, unsigned long vaddr,
else if (mm->context.tsb_block[MM_TSB_HUGE].tsb) {
base = (unsigned long) mm->context.tsb_block[MM_TSB_HUGE].tsb;
nentries = mm->context.tsb_block[MM_TSB_HUGE].tsb_nentries;
- if (tlb_type == cheetah_plus || tlb_type == hypervisor)
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor ||
+ tlb_type == sparc64_vii)
base = __pa(base);
__flush_huge_tsb_one_entry(base, vaddr, REAL_HPAGE_SHIFT,
nentries, hugepage_shift);
@@ -270,7 +274,9 @@ static void setup_tsb_params(struct mm_struct *mm, unsigned long tsb_idx, unsign
}
tte |= pte_sz_bits(page_sz);
- if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
+ /* VII siblings share TLBs: per-mm TSBs cannot reuse a fixed VA/slot. */
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor ||
+ tlb_type == sparc64_vii) {
/* Physical mapping, no locked TLB entry for TSB. */
tsb_reg |= tsb_paddr;
@@ -502,7 +508,8 @@ void tsb_grow(struct mm_struct *mm, unsigned long tsb_index, unsigned long rss)
unsigned long old_tsb_base = (unsigned long) old_tsb;
unsigned long new_tsb_base = (unsigned long) new_tsb;
- if (tlb_type == cheetah_plus || tlb_type == hypervisor) {
+ if (tlb_type == cheetah_plus || tlb_type == hypervisor ||
+ tlb_type == sparc64_vii) {
old_tsb_base = __pa(old_tsb_base);
new_tsb_base = __pa(new_tsb_base);
}
diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S
index 70e658d107e0..4942b793badb 100644
--- a/arch/sparc/mm/ultra.S
+++ b/arch/sparc/mm/ultra.S
@@ -981,6 +981,242 @@ xcall_kgdb_capture:
#endif /* CONFIG_SMP */
+/*
+ * Synchronize VII MMU stores before noninternal memory access or the end
+ * of a control-transfer delay slot (JPS2 F.10.1, p. 488).
+ * Instruction visibility also requires FLUSH or trap return (F.10.11,
+ * pp. 500-502); MEMBAR alone is insufficient.
+ */
+/*
+ * Absolute jumps preserve patch-slot sizes and caller return addresses
+ * without branch relocation. Local entries may clobber %g1; the xcall
+ * stub uses %g4 to preserve its address/context arguments in %g1/%g5.
+ */
+ .type __vii_flush_tlb_page_patch, #function
+__vii_flush_tlb_page_patch:
+ sethi %hi(__vii_flush_tlb_page), %g1
+ jmpl %g1 + %lo(__vii_flush_tlb_page), %g0
+ nop
+ .size __vii_flush_tlb_page_patch, .-__vii_flush_tlb_page_patch
+ .if (. - __vii_flush_tlb_page_patch) - 12
+ .error "VII TLB jump template must contain three instructions"
+ .endif
+
+ .type __vii_flush_tlb_page, #function
+__vii_flush_tlb_page:
+ /* %o0 = context, %o1 = vaddr */
+ rdpr %pstate, %g7
+ andn %g7, PSTATE_IE, %g2
+ wrpr %g2, 0x0, %pstate
+ wrpr %g0, 1, %tl
+ mov PRIMARY_CONTEXT, %o4
+ ldxa [%o4] ASI_DMMU, %g2
+ srlx %g2, CTX_PGSZ1_NUC_SHIFT, %o3
+ sllx %o3, CTX_PGSZ1_NUC_SHIFT, %o3
+ or %o0, %o3, %o0 /* Preserve nucleus page size fields */
+ stxa %o0, [%o4] ASI_DMMU
+ membar #Sync
+ andcc %o1, 1, %g0
+ be,pn %icc, 1f
+ andn %o1, 1, %o3
+ stxa %g0, [%o3] ASI_IMMU_DEMAP
+1: stxa %g0, [%o3] ASI_DMMU_DEMAP
+ membar #Sync
+ stxa %g2, [%o4] ASI_DMMU
+ sethi %hi(KERNBASE), %o4
+ flush %o4
+ wrpr %g0, 0, %tl
+ retl
+ wrpr %g7, 0x0, %pstate
+ .size __vii_flush_tlb_page, .-__vii_flush_tlb_page
+
+ .type __vii_flush_tlb_pending_patch, #function
+__vii_flush_tlb_pending_patch:
+ sethi %hi(__vii_flush_tlb_pending), %g1
+ jmpl %g1 + %lo(__vii_flush_tlb_pending), %g0
+ nop
+ .size __vii_flush_tlb_pending_patch, .-__vii_flush_tlb_pending_patch
+ .if (. - __vii_flush_tlb_pending_patch) - 12
+ .error "VII TLB jump template must contain three instructions"
+ .endif
+
+ .type __vii_flush_tlb_pending, #function
+__vii_flush_tlb_pending:
+ /* %o0 = context, %o1 = nr, %o2 = vaddrs[] */
+ rdpr %pstate, %g7
+ sllx %o1, 3, %o1
+ andn %g7, PSTATE_IE, %g2
+ wrpr %g2, 0x0, %pstate
+ wrpr %g0, 1, %tl
+ mov PRIMARY_CONTEXT, %o4
+ ldxa [%o4] ASI_DMMU, %g2
+ srlx %g2, CTX_PGSZ1_NUC_SHIFT, %o3
+ sllx %o3, CTX_PGSZ1_NUC_SHIFT, %o3
+ or %o0, %o3, %o0 /* Preserve nucleus page size fields */
+ stxa %o0, [%o4] ASI_DMMU
+ membar #Sync
+1: sub %o1, (1 << 3), %o1
+ ldx [%o2 + %o1], %o3
+ andcc %o3, 1, %g0
+ be,pn %icc, 2f
+ andn %o3, 1, %o3
+ stxa %g0, [%o3] ASI_IMMU_DEMAP
+2: stxa %g0, [%o3] ASI_DMMU_DEMAP
+ membar #Sync
+ brnz,pt %o1, 1b
+ nop
+ stxa %g2, [%o4] ASI_DMMU
+ sethi %hi(KERNBASE), %o4
+ flush %o4
+ wrpr %g0, 0, %tl
+ retl
+ wrpr %g7, 0x0, %pstate
+ .size __vii_flush_tlb_pending, .-__vii_flush_tlb_pending
+
+ .type __vii_flush_tlb_kernel_range_patch, #function
+__vii_flush_tlb_kernel_range_patch:
+ sethi %hi(__vii_flush_tlb_kernel_range), %g1
+ jmpl %g1 + %lo(__vii_flush_tlb_kernel_range), %g0
+ nop
+ .size __vii_flush_tlb_kernel_range_patch, .-__vii_flush_tlb_kernel_range_patch
+ .if (. - __vii_flush_tlb_kernel_range_patch) - 12
+ .error "VII TLB jump template must contain three instructions"
+ .endif
+
+ .type __vii_flush_tlb_kernel_range, #function
+__vii_flush_tlb_kernel_range:
+ /* %o0=start, %o1=end */
+ cmp %o0, %o1
+ be,pn %xcc, 2f
+ sub %o1, %o0, %o3
+ srlx %o3, 18, %o4
+ brnz,pn %o4, 3f
+ sethi %hi(PAGE_SIZE), %o4
+ sub %o3, %o4, %o3
+ or %o0, 0x20, %o0 ! Nucleus
+1: stxa %g0, [%o0 + %o3] ASI_DMMU_DEMAP
+ stxa %g0, [%o0 + %o3] ASI_IMMU_DEMAP
+ membar #Sync
+ brnz,pt %o3, 1b
+ sub %o3, %o4, %o3
+2: sethi %hi(KERNBASE), %o3
+ flush %o3
+ retl
+ nop
+3: mov 0x80, %o4
+ stxa %g0, [%o4] ASI_DMMU_DEMAP
+ membar #Sync
+ stxa %g0, [%o4] ASI_IMMU_DEMAP
+ membar #Sync
+ sethi %hi(KERNBASE), %o3
+ flush %o3
+ retl
+ nop
+ .size __vii_flush_tlb_kernel_range, .-__vii_flush_tlb_kernel_range
+
+#ifdef CONFIG_SMP
+ .type __vii_xcall_flush_tlb_page_patch, #function
+__vii_xcall_flush_tlb_page_patch:
+ sethi %hi(__vii_xcall_flush_tlb_page), %g4
+ jmpl %g4 + %lo(__vii_xcall_flush_tlb_page), %g0
+ nop
+ .size __vii_xcall_flush_tlb_page_patch, .-__vii_xcall_flush_tlb_page_patch
+ .if (. - __vii_xcall_flush_tlb_page_patch) - 12
+ .error "VII TLB jump template must contain three instructions"
+ .endif
+
+ .type __vii_xcall_flush_tlb_page, #function
+__vii_xcall_flush_tlb_page:
+ /* %g5=context, %g1=vaddr */
+ mov PRIMARY_CONTEXT, %g4
+ ldxa [%g4] ASI_DMMU, %g2
+ srlx %g2, CTX_PGSZ1_NUC_SHIFT, %g4
+ sllx %g4, CTX_PGSZ1_NUC_SHIFT, %g4
+ or %g5, %g4, %g5
+ mov PRIMARY_CONTEXT, %g4
+ stxa %g5, [%g4] ASI_DMMU
+ membar #Sync
+ andcc %g1, 0x1, %g0
+ be,pn %icc, 2f
+ andn %g1, 0x1, %g5
+ stxa %g0, [%g5] ASI_IMMU_DEMAP
+2: stxa %g0, [%g5] ASI_DMMU_DEMAP
+ membar #Sync
+ stxa %g2, [%g4] ASI_DMMU
+ retry
+ .size __vii_xcall_flush_tlb_page, .-__vii_xcall_flush_tlb_page
+
+#endif /* CONFIG_SMP */
+
+ .globl sparc64_vii_patch_cachetlbops
+sparc64_vii_patch_cachetlbops:
+ save %sp, -128, %sp
+
+ sethi %hi(__flush_tlb_mm), %o0
+ or %o0, %lo(__flush_tlb_mm), %o0
+ sethi %hi(__cheetah_flush_tlb_mm), %o1
+ or %o1, %lo(__cheetah_flush_tlb_mm), %o1
+ call tlb_patch_one
+ mov 19, %o2
+
+ sethi %hi(__flush_tlb_page), %o0
+ or %o0, %lo(__flush_tlb_page), %o0
+ sethi %hi(__vii_flush_tlb_page_patch), %o1
+ or %o1, %lo(__vii_flush_tlb_page_patch), %o1
+ call tlb_patch_one
+ mov 3, %o2
+
+ sethi %hi(__flush_tlb_pending), %o0
+ or %o0, %lo(__flush_tlb_pending), %o0
+ sethi %hi(__vii_flush_tlb_pending_patch), %o1
+ or %o1, %lo(__vii_flush_tlb_pending_patch), %o1
+ call tlb_patch_one
+ mov 3, %o2
+
+ sethi %hi(__flush_tlb_kernel_range), %o0
+ or %o0, %lo(__flush_tlb_kernel_range), %o0
+ sethi %hi(__vii_flush_tlb_kernel_range_patch), %o1
+ or %o1, %lo(__vii_flush_tlb_kernel_range_patch), %o1
+ call tlb_patch_one
+ mov 3, %o2
+
+#ifdef CONFIG_SMP
+ sethi %hi(xcall_flush_tlb_page), %o0
+ or %o0, %lo(xcall_flush_tlb_page), %o0
+ sethi %hi(__vii_xcall_flush_tlb_page_patch), %o1
+ or %o1, %lo(__vii_xcall_flush_tlb_page_patch), %o1
+ call tlb_patch_one
+ mov 3, %o2
+
+ /* The Spitfire sweep misses VII sTLBs (VII Extensions F.10.4). */
+ sethi %hi(xcall_flush_tlb_kernel_range), %o0
+ or %o0, %lo(xcall_flush_tlb_kernel_range), %o0
+ sethi %hi(__cheetah_xcall_flush_tlb_kernel_range), %o1
+ or %o1, %lo(__cheetah_xcall_flush_tlb_kernel_range), %o1
+ call tlb_patch_one
+ mov 44, %o2
+#endif
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ sethi %hi(__flush_dcache_page), %o0
+ or %o0, %lo(__flush_dcache_page), %o0
+ sethi %hi(__vii_flush_dcache_page), %o1
+ or %o1, %lo(__vii_flush_dcache_page), %o1
+ call tlb_patch_one
+ mov 3, %o2
+#endif /* DCACHE_ALIASING_POSSIBLE */
+
+ ret
+ restore
+
+/* VII caches are strongly coherent (VII manual section 8.4.7). */
+#ifdef DCACHE_ALIASING_POSSIBLE
+__vii_flush_dcache_page:
+ membar #Sync
+ retl
+ nop
+#endif
+
.globl cheetah_patch_cachetlbops
cheetah_patch_cachetlbops:
save %sp, -128, %sp
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 09/10] sparc64: add M3000 Oberon PCIe support
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
` (7 preceding siblings ...)
2026-10-18 13:45 ` [PATCH v4 08/10] sparc64: add SPARC64 VII CPU, MMU and SMP support Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 13:57 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 10/10] hvc: add an M3000 firmware console backend Magnus Lindholm
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7
Recognize Oberon from its firmware compatible property without a platform
Kconfig option, preserving firmware link setup. Flush DVMA translations
and changed noncoherent IOTSB cache lines, and route legacy interrupts
using the bridge's ten-bit target ID.
Cache the bridge type and share target encoding. Leave MSI queues
uninitialized so the existing architecture path uses legacy interrupts
even with CONFIG_PCI_MSI=y; native bridge error handling is not provided.
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
arch/sparc/include/asm/iommu_64.h | 1 +
arch/sparc/include/asm/irq_64.h | 1 +
arch/sparc/kernel/iommu.c | 44 +++++++++++++++++++++++++++
arch/sparc/kernel/irq_64.c | 50 ++++++++++++++++++++++++-------
arch/sparc/kernel/pci_fire.c | 26 ++++++++++++++--
arch/sparc/kernel/pci_impl.h | 1 +
arch/sparc/kernel/prom_irqtrans.c | 5 ++++
7 files changed, 115 insertions(+), 13 deletions(-)
diff --git a/arch/sparc/include/asm/iommu_64.h b/arch/sparc/include/asm/iommu_64.h
index 0ef6dedf747e..abd7bb0fa615 100644
--- a/arch/sparc/include/asm/iommu_64.h
+++ b/arch/sparc/include/asm/iommu_64.h
@@ -62,6 +62,7 @@ struct iommu {
unsigned long iommu_tsbbase;
unsigned long iommu_flush;
unsigned long iommu_flushinv;
+ unsigned long iommu_cache_flush; /* Oberon IOTSB cache */
unsigned long iommu_tags;
unsigned long iommu_ctxflush;
unsigned long write_complete_reg;
diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h
index 8c4c0c87f998..c3e88f2f8e80 100644
--- a/arch/sparc/include/asm/irq_64.h
+++ b/arch/sparc/include/asm/irq_64.h
@@ -45,6 +45,7 @@ void irq_install_pre_handler(int irq,
void *arg1, void *arg2);
#define irq_canonicalize(irq) (irq)
unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap);
+unsigned int oberon_build_irq(int inofixup, unsigned long iclr, unsigned long imap);
unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino);
unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino);
unsigned int sun4v_build_msi(u32 devhandle, unsigned int *irq_p,
diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c
index 66aaafbf1621..27d269a8b52c 100644
--- a/arch/sparc/kernel/iommu.c
+++ b/arch/sparc/kernel/iommu.c
@@ -52,6 +52,8 @@ static void iommu_flushall(struct iommu_map_table *iommu_map_table)
struct iommu *iommu = container_of(iommu_map_table, struct iommu, tbl);
if (iommu->iommu_flushinv) {
iommu_write(iommu->iommu_flushinv, ~(u64)0);
+ if (iommu->iommu_cache_flush)
+ (void)iommu_read(iommu->write_complete_reg);
} else {
unsigned long tag;
int entry;
@@ -67,6 +69,30 @@ static void iommu_flushall(struct iommu_map_table *iommu_map_table)
}
}
+/*
+ * Oberon's noncoherent IOTSB needs each changed 64-byte cache line flushed
+ * along with its DVMA translations. Call only for device-inactive mappings.
+ */
+static void iommu_sync_mapping(struct iommu *iommu, iopte_t *base,
+ unsigned long npages)
+{
+ unsigned long slot, i;
+
+ if (!iommu->iommu_cache_flush || !npages)
+ return;
+ slot = base - iommu->page_table;
+ /* Publish IOPTE stores before invalidating the hardware caches. */
+ wmb();
+ for (i = 0; i < npages; i++, slot++) {
+ iommu_write(iommu->iommu_flush,
+ iommu->tbl.table_map_base + (slot << IO_PAGE_SHIFT));
+ if ((slot & 7) == 7 || i == npages - 1)
+ iommu_write(iommu->iommu_cache_flush,
+ __pa(iommu->page_table + slot));
+ }
+ (void)iommu_read(iommu->write_complete_reg);
+}
+
#define IOPTE_CONSISTENT(CTX) \
(IOPTE_VALID | IOPTE_CACHE | \
(((CTX) << 47) & IOPTE_CONTEXT))
@@ -240,6 +266,8 @@ static void *dma_4u_alloc_coherent(struct device *dev, size_t size,
first_page += IO_PAGE_SIZE;
}
+ iommu_sync_mapping(iommu, iopte - (size >> IO_PAGE_SHIFT),
+ size >> IO_PAGE_SHIFT);
return ret;
}
@@ -253,6 +281,15 @@ static void dma_4u_free_coherent(struct device *dev, size_t size,
npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT;
iommu = dev->archdata.iommu;
+ if (iommu->iommu_cache_flush) {
+ iopte_t *base = iommu->page_table +
+ ((dvma - iommu->tbl.table_map_base) >> IO_PAGE_SHIFT);
+ unsigned long i;
+
+ for (i = 0; i < npages; i++)
+ iopte_make_dummy(iommu, base + i);
+ iommu_sync_mapping(iommu, base, npages);
+ }
iommu_tbl_range_free(&iommu->tbl, dvma, npages, IOMMU_ERROR_CODE);
order = get_order(size);
@@ -316,6 +353,7 @@ static dma_addr_t dma_4u_map_phys(struct device *dev, phys_addr_t phys,
for (i = 0; i < npages; i++, base++, phys += IO_PAGE_SIZE)
iopte_val(*base) = iopte_protection | phys;
+ iommu_sync_mapping(iommu, base - npages, npages);
return ret;
@@ -432,6 +470,7 @@ static void dma_4u_unmap_phys(struct device *dev, dma_addr_t bus_addr,
/* Step 2: Clear out TSB entries. */
for (i = 0; i < npages; i++)
iopte_make_dummy(iommu, base + i);
+ iommu_sync_mapping(iommu, base, npages);
iommu_free_ctx(iommu, ctx);
spin_unlock_irqrestore(&iommu->lock, flags);
@@ -523,6 +562,9 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist,
paddr += IO_PAGE_SIZE;
}
+ iommu_sync_mapping(iommu, iommu->page_table + entry,
+ base - (iommu->page_table + entry));
+
/* If we are in an open segment, try merging */
if (segstart != s) {
/* We cannot merge if:
@@ -577,6 +619,7 @@ static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist,
for (j = 0; j < npages; j++)
iopte_make_dummy(iommu, base + j);
+ iommu_sync_mapping(iommu, base, npages);
iommu_tbl_range_free(&iommu->tbl, vaddr, npages,
IOMMU_ERROR_CODE);
@@ -653,6 +696,7 @@ static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist,
for (i = 0; i < npages; i++)
iopte_make_dummy(iommu, base + i);
+ iommu_sync_mapping(iommu, base, npages);
iommu_tbl_range_free(&iommu->tbl, dma_handle, npages,
IOMMU_ERROR_CODE);
diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c
index 4cceeba726ab..338f58246feb 100644
--- a/arch/sparc/kernel/irq_64.c
+++ b/arch/sparc/kernel/irq_64.c
@@ -206,6 +206,7 @@ struct irq_handler_data {
struct ino_bucket bucket;
unsigned long iclr;
unsigned long imap;
+ bool oberon;
};
static inline unsigned int irq_data_to_handle(struct irq_data *data)
@@ -364,6 +365,16 @@ static int irq_choose_cpu(unsigned int irq, const struct cpumask *affinity)
real_hard_smp_processor_id()
#endif
+/* Oberon IMAP destination is a ten-bit interrupt target ID. */
+#define OBERON_IMAP_TID_SHIFT 21
+#define OBERON_IMAP_TID_MASK (0x3ffUL << OBERON_IMAP_TID_SHIFT)
+
+static unsigned long oberon_imap_target(unsigned long val, unsigned int cpuid)
+{
+ return (val & ~OBERON_IMAP_TID_MASK) |
+ ((cpuid << OBERON_IMAP_TID_SHIFT) & OBERON_IMAP_TID_MASK);
+}
+
static void sun4u_irq_enable(struct irq_data *data)
{
struct irq_handler_data *handler_data;
@@ -377,12 +388,16 @@ static void sun4u_irq_enable(struct irq_data *data)
irq_data_get_affinity_mask(data));
imap = handler_data->imap;
- tid = sun4u_compute_tid(imap, cpuid);
-
val = upa_readq(imap);
- val &= ~(IMAP_TID_UPA | IMAP_TID_JBUS |
- IMAP_AID_SAFARI | IMAP_NID_SAFARI);
- val |= tid | IMAP_VALID;
+ if (handler_data->oberon) {
+ val = oberon_imap_target(val, cpuid);
+ } else {
+ tid = sun4u_compute_tid(imap, cpuid);
+ val &= ~(IMAP_TID_UPA | IMAP_TID_JBUS |
+ IMAP_AID_SAFARI | IMAP_NID_SAFARI);
+ val |= tid;
+ }
+ val |= IMAP_VALID;
upa_writeq(val, imap);
upa_writeq(ICLR_IDLE, handler_data->iclr);
}
@@ -401,12 +416,16 @@ static int sun4u_set_affinity(struct irq_data *data,
cpuid = irq_choose_cpu(data->irq, mask);
imap = handler_data->imap;
- tid = sun4u_compute_tid(imap, cpuid);
-
val = upa_readq(imap);
- val &= ~(IMAP_TID_UPA | IMAP_TID_JBUS |
- IMAP_AID_SAFARI | IMAP_NID_SAFARI);
- val |= tid | IMAP_VALID;
+ if (handler_data->oberon) {
+ val = oberon_imap_target(val, cpuid);
+ } else {
+ tid = sun4u_compute_tid(imap, cpuid);
+ val &= ~(IMAP_TID_UPA | IMAP_TID_JBUS |
+ IMAP_AID_SAFARI | IMAP_NID_SAFARI);
+ val |= tid;
+ }
+ val |= IMAP_VALID;
upa_writeq(val, imap);
upa_writeq(ICLR_IDLE, handler_data->iclr);
}
@@ -646,6 +665,17 @@ unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap)
return irq;
}
+unsigned int oberon_build_irq(int inofixup, unsigned long iclr,
+ unsigned long imap)
+{
+ unsigned int irq = build_irq(inofixup, iclr, imap);
+ struct irq_handler_data *data = irq_get_handler_data(irq);
+
+ if (data)
+ data->oberon = true;
+ return irq;
+}
+
static unsigned int sun4v_build_common(u32 devhandle, unsigned int devino,
void (*handler_data_init)(struct irq_handler_data *data,
u32 devhandle, unsigned int devino),
diff --git a/arch/sparc/kernel/pci_fire.c b/arch/sparc/kernel/pci_fire.c
index db160eeb0ffe..f2776144d1d4 100644
--- a/arch/sparc/kernel/pci_fire.c
+++ b/arch/sparc/kernel/pci_fire.c
@@ -46,11 +46,21 @@ static int pci_fire_pbm_iommu_init(struct pci_pbm_info *pbm)
iommu->iommu_tsbbase = pbm->pbm_regs + FIRE_IOMMU_TSBBASE;
iommu->iommu_flush = pbm->pbm_regs + FIRE_IOMMU_FLUSH;
iommu->iommu_flushinv = pbm->pbm_regs + FIRE_IOMMU_FLUSHINV;
+ if (pbm->is_oberon) {
+ /*
+ * 0x40100 flushes an IOTSB cache line by physical address;
+ * 0x40010 flushes a translated DVMA page.
+ */
+ iommu->iommu_flush = pbm->pbm_regs + 0x40010UL;
+ iommu->iommu_cache_flush = pbm->pbm_regs + 0x40100UL;
+ iommu->write_complete_reg = iommu->iommu_control;
+ }
/* We use the main control/status register of FIRE as the write
* completion register.
*/
- iommu->write_complete_reg = pbm->controller_regs + 0x410000UL;
+ if (!iommu->iommu_cache_flush)
+ iommu->write_complete_reg = pbm->controller_regs + 0x410000UL;
/*
* Invalidate TLB Entries.
@@ -62,6 +72,8 @@ static int pci_fire_pbm_iommu_init(struct pci_pbm_info *pbm)
if (err)
return err;
+ /* Make initialized IOPTEs visible before publishing the table base. */
+ wmb();
upa_writeq(__pa(iommu->page_table) | 0x7UL, iommu->iommu_tsbbase);
control = upa_readq(iommu->iommu_control);
@@ -70,6 +82,10 @@ static int pci_fire_pbm_iommu_init(struct pci_pbm_info *pbm)
0x00000002 /* Bypass enable */ |
0x00000001 /* Translation enable */);
upa_writeq(control, iommu->iommu_control);
+ if (iommu->iommu_cache_flush) {
+ upa_writeq(~(u64)0, iommu->iommu_flushinv);
+ (void)upa_readq(iommu->iommu_control);
+ }
return 0;
}
@@ -425,6 +441,7 @@ static int pci_fire_pbm_init(struct pci_pbm_info *pbm,
}
pbm->numa_node = NUMA_NO_NODE;
+ pbm->is_oberon = of_device_is_compatible(dp, "pciex108e,80f8");
pbm->pci_ops = &sun4u_pci_ops;
pbm->config_space_reg_bits = 12;
@@ -444,13 +461,15 @@ static int pci_fire_pbm_init(struct pci_pbm_info *pbm,
pci_get_pbm_props(pbm);
- pci_fire_hw_init(pbm);
+ if (!pbm->is_oberon)
+ pci_fire_hw_init(pbm);
err = pci_fire_pbm_iommu_init(pbm);
if (err)
return err;
- pci_fire_msi_init(pbm);
+ if (!pbm->is_oberon)
+ pci_fire_msi_init(pbm);
pbm->pci_bus = pci_scan_one_pbm(pbm, &op->dev);
@@ -510,6 +529,7 @@ static const struct of_device_id fire_match[] = {
.name = "pci",
.compatible = "pciex108e,80f0",
},
+ { .name = "pci", .compatible = "pciex108e,80f8" },
{},
};
diff --git a/arch/sparc/kernel/pci_impl.h b/arch/sparc/kernel/pci_impl.h
index 83718876f1d4..92505fcc3cf3 100644
--- a/arch/sparc/kernel/pci_impl.h
+++ b/arch/sparc/kernel/pci_impl.h
@@ -117,6 +117,7 @@ struct pci_pbm_info {
/* State of 66MHz capabilities on this PBM. */
int is_66mhz_capable;
int all_devs_66mhz;
+ bool is_oberon;
#ifdef CONFIG_PCI_MSI
/* MSI info. */
diff --git a/arch/sparc/kernel/prom_irqtrans.c b/arch/sparc/kernel/prom_irqtrans.c
index 558ed5114664..58c5b4565f41 100644
--- a/arch/sparc/kernel/prom_irqtrans.c
+++ b/arch/sparc/kernel/prom_irqtrans.c
@@ -481,6 +481,7 @@ static void __init pci_sun4v_irq_trans_init(struct device_node *dp)
struct fire_irq_data {
unsigned long pbm_regs;
u32 portid;
+ bool oberon;
};
#define FIRE_IMAP_BASE 0x001000
@@ -537,6 +538,8 @@ static unsigned int fire_irq_build(struct device_node *dp,
*/
ino |= (irq_data->portid << 6);
ino -= int_ctrlr;
+ if (irq_data->oberon)
+ return oberon_build_irq(ino, iclr, imap);
return build_irq(ino, iclr, imap);
}
@@ -559,6 +562,7 @@ static void __init fire_irq_trans_init(struct device_node *dp)
dp->irq_trans->data = irq_data;
+ irq_data->oberon = of_device_is_compatible(dp, "pciex108e,80f8");
irq_data->pbm_regs = regs[0].phys_addr;
irq_data->portid = of_getintprop_default(dp, "portid", 0);
}
@@ -784,6 +788,7 @@ static struct irq_trans __initdata pci_irq_trans_table[] = {
{ "pci108e,a801", tomatillo_irq_trans_init },
{ "SUNW,sun4v-pci", pci_sun4v_irq_trans_init },
{ "pciex108e,80f0", fire_irq_trans_init },
+ { "pciex108e,80f8", fire_irq_trans_init },
};
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v4 10/10] hvc: add an M3000 firmware console backend
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
` (8 preceding siblings ...)
2026-10-18 13:45 ` [PATCH v4 09/10] sparc64: add M3000 Oberon PCIe support Magnus Lindholm
@ 2026-10-18 13:45 ` Magnus Lindholm
2026-10-18 14:09 ` sashiko-bot
9 siblings, 1 reply; 21+ messages in thread
From: Magnus Lindholm @ 2026-10-18 13:45 UTC (permalink / raw)
To: davem, andreas, sparclinux
Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
oleg, linmag7
Provide hvc0 through the M3000 Open Firmware pseudo-console, validating
the firmware model and console endpoints before registering it. Keep
firmware buffers in locked kernel mappings and serialize their use in
atomic console contexts through the existing SPARC p1275 interface.
Poll input while the TTY is open and synchronize close with timer
rearming. Return zero on no progress so HVC retries buffered TTY output
but drops stalled console chunks instead of spinning indefinitely.
A firmware call that does not return remains unbounded.
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
MAINTAINERS | 7 ++
drivers/tty/hvc/Kconfig | 11 ++
drivers/tty/hvc/Makefile | 1 +
drivers/tty/hvc/hvc_m3000.c | 202 ++++++++++++++++++++++++++++++++++++
4 files changed, 221 insertions(+)
create mode 100644 drivers/tty/hvc/hvc_m3000.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..21e4f4e558bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10728,6 +10728,13 @@ L: platform-driver-x86@vger.kernel.org
S: Maintained
F: drivers/platform/x86/fujitsu-laptop.c
+FUJITSU M3000 FIRMWARE CONSOLE
+M: Magnus Lindholm <linmag7@gmail.com>
+L: linux-serial@vger.kernel.org
+L: sparclinux@vger.kernel.org
+S: Maintained
+F: drivers/tty/hvc/hvc_m3000.c
+
FUJITSU TABLET EXTRAS
M: Robert Gerlach <khnz@gmx.de>
L: platform-driver-x86@vger.kernel.org
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 5866195de26a..d940fa0d68e0 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -134,3 +134,14 @@ config HVCS
will depend on arch specific APIs exported from hvcserver.ko
which will also be compiled when this driver is built as a
module.
+
+config HVC_M3000
+ bool "M3000 firmware pseudo-console"
+ depends on SPARC64
+ select HVC_DRIVER
+ help
+ Provide hvc0 using Open Firmware stdin/stdout on the Fujitsu M3000
+ (IKKAKU platform).
+ Input is polled; no native UART registers are accessed.
+ Firmware calls are serialized and use a locked-image bounce buffer.
+ Say Y to use the M3000 firmware pseudo-console as hvc0.
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile
index 98880e357941..38c8cf349053 100644
--- a/drivers/tty/hvc/Makefile
+++ b/drivers/tty/hvc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_HVC_IUCV) += hvc_iucv.o
obj-$(CONFIG_HVC_UDBG) += hvc_udbg.o
obj-$(CONFIG_HVC_RISCV_SBI) += hvc_riscv_sbi.o
obj-$(CONFIG_HVCS) += hvcs.o
+obj-$(CONFIG_HVC_M3000) += hvc_m3000.o
diff --git a/drivers/tty/hvc/hvc_m3000.c b/drivers/tty/hvc/hvc_m3000.c
new file mode 100644
index 000000000000..e2150d479308
--- /dev/null
+++ b/drivers/tty/hvc/hvc_m3000.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * M3000 Open Firmware pseudo-console backend.
+ * Copyright (C) 2026 Magnus Lindholm
+ */
+#include <linux/cleanup.h>
+#include <linux/console.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/irqflags.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <asm/oplib.h>
+#include <asm/spitfire.h>
+#include "hvc_console.h"
+
+#define M3000_PROM_IO_ARGS 3
+#define M3000_PROM_IO_RETURNS 1
+#define M3000_PROM_ERROR (-1)
+#define M3000_PROM_NO_DATA (-2)
+
+struct m3000_console {
+ struct timer_list poll_timer;
+ struct mutex poll_mutex; /* Serialize open against synchronous close. */
+ raw_spinlock_t buffer_lock;
+ spinlock_t poll_lock; /* Protect active state and timer rearming. */
+ int stdin;
+ u8 buffer[256];
+ bool ready;
+ bool poll_active;
+};
+
+/* Keep the firmware buffer within the locked kernel image mappings. */
+static struct m3000_console m3000_consoles[] = {
+ {
+ .buffer_lock = __RAW_SPIN_LOCK_UNLOCKED(m3000_consoles[0].buffer_lock),
+ .poll_mutex = __MUTEX_INITIALIZER(m3000_consoles[0].poll_mutex),
+ .poll_lock = __SPIN_LOCK_UNLOCKED(m3000_consoles[0].poll_lock),
+ },
+};
+
+static struct m3000_console *m3000_console(u32 termno)
+{
+ if (termno >= ARRAY_SIZE(m3000_consoles))
+ return NULL;
+ return &m3000_consoles[termno];
+}
+
+/*
+ * HVC's idle backoff can exceed the firmware input FIFO's capacity.
+ * Wake its worker each tick while open; keep PROM calls out of the timer.
+ */
+static void m3000_poll_tick(struct timer_list *timer)
+{
+ struct m3000_console *con = timer_container_of(con, timer, poll_timer);
+
+ guard(spinlock_irqsave)(&con->poll_lock);
+ if (con->poll_active) {
+ hvc_kick();
+ mod_timer(&con->poll_timer, jiffies + 1);
+ }
+}
+
+static int m3000_open(struct hvc_struct *hp, int data)
+{
+ struct m3000_console *con = m3000_console(hp->vtermno);
+
+ if (!con)
+ return -ENODEV;
+ guard(mutex)(&con->poll_mutex);
+ guard(spinlock_irqsave)(&con->poll_lock);
+ con->poll_active = true;
+ mod_timer(&con->poll_timer, jiffies + 1);
+ return 0;
+}
+
+static void m3000_close(struct hvc_struct *hp, int data)
+{
+ struct m3000_console *con = m3000_console(hp->vtermno);
+
+ if (!con)
+ return;
+ guard(mutex)(&con->poll_mutex);
+ scoped_guard(spinlock_irqsave, &con->poll_lock)
+ con->poll_active = false;
+ /* Block reopen until the callback has finished and cannot rearm. */
+ timer_delete_sync(&con->poll_timer);
+}
+
+static ssize_t m3000_io(struct m3000_console *con, u8 *in,
+ const u8 *out, size_t count)
+{
+ unsigned long args[3 + M3000_PROM_IO_ARGS + M3000_PROM_IO_RETURNS];
+ int ret;
+
+ if (!count)
+ return 0;
+ count = min(count, in ? 1 : sizeof(con->buffer));
+ /* Console writes can run with interrupts disabled; this lock cannot sleep. */
+ scoped_guard(raw_spinlock_irqsave, &con->buffer_lock) {
+ if (!in)
+ memcpy(con->buffer, out, count);
+ args[0] = (unsigned long)(in ? "read" : "write");
+ args[1] = M3000_PROM_IO_ARGS;
+ args[2] = M3000_PROM_IO_RETURNS;
+ args[3] = (unsigned int)(in ? con->stdin : prom_stdout);
+ args[4] = (unsigned long)con->buffer;
+ args[5] = count;
+ args[6] = (unsigned long)M3000_PROM_ERROR;
+ p1275_cmd_direct(args);
+ ret = (int)args[6];
+ if (ret > 0 && ret <= count && in)
+ memcpy(in, con->buffer, ret);
+ }
+ if (ret == M3000_PROM_NO_DATA || ret == 0)
+ return 0;
+ if (ret < 0 || ret > count)
+ return -EIO;
+ return ret;
+}
+
+static ssize_t m3000_get(u32 termno, u8 *buf, size_t count)
+{
+ struct m3000_console *con = m3000_console(termno);
+
+ return con ? m3000_io(con, buf, NULL, count) : -ENODEV;
+}
+
+static ssize_t m3000_put(u32 termno, const u8 *buf, size_t count)
+{
+ struct m3000_console *con = m3000_console(termno);
+
+ /* HVC retries zero for TTY writes but drops stalled console output. */
+ return con ? m3000_io(con, NULL, buf, count) : -ENODEV;
+}
+
+static const struct hv_ops m3000_ops = {
+ .get_chars = m3000_get,
+ .put_chars = m3000_put,
+ .notifier_add = m3000_open,
+ .notifier_del = m3000_close,
+ .notifier_hangup = m3000_close,
+};
+
+static bool __init m3000_property_matches(phandle node, const char *prop,
+ const char *expected)
+{
+ char value[64];
+ int len;
+
+ len = prom_getproperty(node, prop, value, sizeof(value) - 1);
+ if (len <= 0)
+ return false;
+ value[len] = '\0';
+ return !strcmp(value, expected);
+}
+
+static int __init m3000_console_init(void)
+{
+ struct m3000_console *con = &m3000_consoles[0];
+ phandle node;
+ int ret;
+
+ if (tlb_type != sparc64_vii)
+ return -ENODEV;
+ if (!m3000_property_matches(prom_finddevice("/"), "model", "IKKAKU"))
+ return -ENODEV;
+ con->stdin = prom_getint(prom_chosen_node, "stdin");
+ if (!con->stdin || con->stdin == -1 || !prom_stdout || prom_stdout == -1)
+ return -ENODEV;
+ node = prom_inst2pkg(prom_stdout);
+ if (!m3000_property_matches(node, "name", "pseudo-console"))
+ return -ENODEV;
+ node = prom_inst2pkg(con->stdin);
+ if (!m3000_property_matches(node, "name", "pseudo-console"))
+ return -ENODEV;
+ ret = hvc_instantiate(0, 0, &m3000_ops);
+ if (ret < 0)
+ return ret;
+ con->ready = true;
+ return 0;
+}
+console_initcall(m3000_console_init);
+
+static int __init m3000_tty_init(void)
+{
+ struct m3000_console *con = &m3000_consoles[0];
+ struct hvc_struct *hp;
+
+ if (!con->ready)
+ return -ENODEV;
+ timer_setup(&con->poll_timer, m3000_poll_tick, 0);
+ hp = hvc_alloc(0, 0, &m3000_ops, sizeof(con->buffer));
+ if (IS_ERR(hp))
+ return PTR_ERR(hp);
+ hp->ws.ws_row = 24;
+ hp->ws.ws_col = 80;
+ return 0;
+}
+device_initcall(m3000_tty_init);
--
2.43.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v4 01/10] sparc64: return from the generic clear_page implementation
2026-10-18 13:45 ` [PATCH v4 01/10] sparc64: return from the generic clear_page implementation Magnus Lindholm
@ 2026-10-18 13:52 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 13:52 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
> GENclear_page falls through into generic_patch_pageops after clearing
> its page. The patcher then rewrites kernel text on each invocation.
> This executes 45 extra instructions, including six stores and three
> FLUSH instructions, before returning.
>
> Return once the clear loop completes, as GENcopy_page does. This affects
> processors using the generic page-operation fallback.
>
> Fixes: 6c70b6fc7b6f ("[SPARC64]: Do not assume sun4v chips have load-twin/store-init support.")
> Cc: stable@vger.kernel.org
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=1
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 07/10] sparc64: zero newly allocated IOMMU page tables
2026-10-18 13:45 ` [PATCH v4 07/10] sparc64: zero newly allocated IOMMU page tables Magnus Lindholm
@ 2026-10-18 13:52 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 13:52 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
> iopte_make_dummy() replaces only the physical page field, preserving
> all attributes. Applying it to an uninitialized table leaves validity,
> permissions, context and reserved bits dependent on prior page contents.
>
> Zero the table allocation before installing dummy page addresses. Keep
> iopte_make_dummy() unchanged for unmap callers that preserve attributes.
>
> This initialization pattern is already present in the initial Git
> import; the Fixes tag identifies its earliest recorded version.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=7
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 04/10] sparc64: stop IRQ setup when descriptor allocation fails
2026-10-18 13:45 ` [PATCH v4 04/10] sparc64: stop IRQ setup when descriptor allocation fails Magnus Lindholm
@ 2026-10-18 13:52 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 13:52 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
> irq_alloc() returns zero on failure, but build_irq() then configures
> IRQ 0, which is reserved for the timer, as a device interrupt.
>
> Halt before publishing the IRQ or changing its descriptor, matching the
> existing fatal handler-data allocation path. Returning zero would let
> callers continue treating the timer IRQ as a valid device interrupt.
>
> Fixes: 8047e247c899 ("[SPARC64]: Virtualize IRQ numbers.")
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=4
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 06/10] sparc64: retain the DMA segment start across SG entries
2026-10-18 13:45 ` [PATCH v4 06/10] sparc64: retain the DMA segment start across SG entries Magnus Lindholm
@ 2026-10-18 13:52 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 13:52 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
> The SG mapping loops reset out_entry to zero for each input entry.
> When extending an output segment, the boundary check therefore tests
> entry zero instead of the segment's actual starting IOMMU entry.
> Adjacent allocations can be merged across a device segment boundary.
>
> Keep out_entry for the lifetime of the mapping loop and update it only
> when starting an output segment, in both sun4u and sun4v.
>
> Fixes: f08802572965 ("sparc64: add the segment boundary checking to IOMMUs while merging SG entries")
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=6
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 05/10] sparc64: validate Fire register properties before initialization
2026-10-18 13:45 ` [PATCH v4 05/10] sparc64: validate Fire register properties before initialization Magnus Lindholm
@ 2026-10-18 13:52 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 13:52 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
> The Fire interrupt translator reads one reg entry, and PCI PBM setup
> reads two, without checking that the firmware supplied enough data.
>
> Check the property before allocating the translator or changing PBM
> state. Halt early translation on invalid firmware data rather than leave
> raw interrupt numbers untranslated. Return an error from PBM setup so
> probe can release its allocations.
>
> Fixes: 861fe90656b8 ("[SPARC64]: SUN4U PCI-E controller support.")
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=5
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 03/10] sparc64: avoid huge kernel PUD mappings with small hardware pages
2026-10-18 13:45 ` [PATCH v4 03/10] sparc64: avoid huge kernel PUD mappings with small hardware pages Magnus Lindholm
@ 2026-10-18 13:53 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 13:53 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
> The kernel PUD refill path merges virtual address bits 32:28 into a
> TTE, requiring hardware pages of at least 256MB. Sun4u instead selects
> 4MB pages, losing bits 27:22 when refilling a huge PUD. Sun4v also falls
> back to 4MB pages when neither 256MB nor 2GB pages are selected.
>
> Keep PMD mappings when the selected PUD encoding matches the small-page
> entry. The PMD refill path preserves the bits needed by the 4MB TTE;
> sun4v selections of 256MB or larger remain eligible for huge PUDs.
>
> Fixes: 0dd5b7b09e13 ("sparc64: Fix physical memory management regressions with large max_phys_bits.")
> Cc: stable@vger.kernel.org
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=3
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 09/10] sparc64: add M3000 Oberon PCIe support
2026-10-18 13:45 ` [PATCH v4 09/10] sparc64: add M3000 Oberon PCIe support Magnus Lindholm
@ 2026-10-18 13:57 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 13:57 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
> Recognize Oberon from its firmware compatible property without a platform
> Kconfig option, preserving firmware link setup. Flush DVMA translations
> and changed noncoherent IOTSB cache lines, and route legacy interrupts
> using the bridge's ten-bit target ID.
>
> Cache the bridge type and share target encoding. Leave MSI queues
> uninitialized so the existing architecture path uses legacy interrupts
> even with CONFIG_PCI_MSI=y; native bridge error handling is not provided.
>
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=9
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 02/10] sparc64: honor queued spinlock layout in secondary startup
2026-10-18 13:45 ` [PATCH v4 02/10] sparc64: honor queued spinlock layout in secondary startup Magnus Lindholm
@ 2026-10-18 14:01 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 14:01 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] sparc64: invalid [%g2 + %lo(prom_entry_lock) + 3] operand in trampoline_64.S
--
commit 3122e42ebc7be814860c71cd7ad49ac4a1876063
Author: Magnus Lindholm <linmag7@gmail.com>
sparc64: honor queued spinlock layout in secondary startup
This commit updates secondary CPU startup in arch/sparc/kernel/trampoline_64.S
to acquire prom_entry_lock as a 32-bit word with CASA and release byte offset 3
with STB to match the big-endian queued spinlock layout.
> diff --git a/arch/sparc/kernel/trampoline_64.S b/arch/sparc/kernel/trampoline_64.S
> index 51bf1eb92a36e..4473347301229 100644
> --- a/arch/sparc/kernel/trampoline_64.S
> +++ b/arch/sparc/kernel/trampoline_64.S
[ ... ]
> @@ -212,7 +222,13 @@ startup_continue:
> nop
>
> sethi %hi(prom_entry_lock), %g2
> - stb %g0, [%g2 + %lo(prom_entry_lock)]
> + /*
> + * Retain explicit assembly release ordering; C unlock relies on TSO.
> + * The predicted branch protects Spitfire erratum 51.
> + */
> + ba,pt %xcc, 2f
> + membar #StoreStore | #LoadStore
> +2: stb %g0, [%g2 + %lo(prom_entry_lock) + 3]
>
> ba,pt %xcc, after_lock_tlb
> nop
[ ... ]
> @@ -389,7 +415,13 @@ after_lock_tlb:
> add %sp, (2047 + 128), %o0
>
> 3: sethi %hi(prom_entry_lock), %g2
> - stb %g0, [%g2 + %lo(prom_entry_lock)]
> + /*
> + * Retain explicit assembly release ordering; C unlock relies on TSO.
> + * The predicted branch protects Spitfire erratum 51.
> + */
> + ba,pt %xcc, 2f
> + membar #StoreStore | #LoadStore
> +2: stb %g0, [%g2 + %lo(prom_entry_lock) + 3]
[Severity: Low]
Does this assemble on SPARC64 SMP builds (CONFIG_SPARC64=y && CONFIG_SMP=y)?
In both startup_continue and after_lock_tlb in
arch/sparc/kernel/trampoline_64.S, the unlock sequences use:
stb %g0, [%g2 + %lo(prom_entry_lock) + 3]
In SPARC assembly syntax (both GNU as and LLVM's SparcAsmParser), %lo(...) is
parsed as the single immediate operand in [rs1 + simm13] and does not accept a
trailing "+ 3" after the closing parenthesis, which causes the assembler to
reject the instruction with "Error: Illegal operands".
Should the byte offset be placed inside the relocation modifier as
[%g2 + %lo(prom_entry_lock + 3)], or should %g2 first be formed with
"or %g2, %lo(prom_entry_lock), %g2" followed by "stb %g0, [%g2 + 3]"?
>
> ldx [%l0], %g6
> ldx [%g6 + TI_TASK], %g4
титься
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=2
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 10/10] hvc: add an M3000 firmware console backend
2026-10-18 13:45 ` [PATCH v4 10/10] hvc: add an M3000 firmware console backend Magnus Lindholm
@ 2026-10-18 14:09 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 14:09 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] hvc: hvc_m3000 fails to register console when CONFIG_VT_CONSOLE=y
- [Low] tty: hvc_m3000: ineffective window size initialization in m3000_tty_init()
--
commit 7a0bedd3c2b41c2e4d2aba3f69c932366fc243a8
Author: Magnus Lindholm <linmag7@gmail.com>
hvc: add an M3000 firmware console backend
This commit adds an HVC console backend for the Fujitsu M3000 (IKKAKU
platform) using the Open Firmware pseudo-console via the SPARC p1275
interface. It registers hvc0 after validating the firmware model and console
endpoints, polling input with a timer while the TTY is open.
> diff --git a/drivers/tty/hvc/hvc_m3000.c b/drivers/tty/hvc/hvc_m3000.c
> new file mode 100644
> index 0000000000000..e2150d4793080
> --- /dev/null
> +++ b/drivers/tty/hvc/hvc_m3000.c
> @@ -0,0 +1,202 @@
[ ... ]
> +static int __init m3000_console_init(void)
> +{
> + struct m3000_console *con = &m3000_consoles[0];
> + phandle node;
> + int ret;
> +
> + if (tlb_type != sparc64_vii)
> + return -ENODEV;
> + if (!m3000_property_matches(prom_finddevice("/"), "model", "IKKAKU"))
> + return -ENODEV;
> + con->stdin = prom_getint(prom_chosen_node, "stdin");
> + if (!con->stdin || con->stdin == -1 || !prom_stdout || prom_stdout == -1)
> + return -ENODEV;
> + node = prom_inst2pkg(prom_stdout);
> + if (!m3000_property_matches(node, "name", "pseudo-console"))
> + return -ENODEV;
> + node = prom_inst2pkg(con->stdin);
> + if (!m3000_property_matches(node, "name", "pseudo-console"))
> + return -ENODEV;
> + ret = hvc_instantiate(0, 0, &m3000_ops);
> + if (ret < 0)
> + return ret;
> + con->ready = true;
> + return 0;
> +}
> +console_initcall(m3000_console_init);
[Severity: Medium]
When booting with CONFIG_VT_CONSOLE=y (which is enabled in sparc64_defconfig)
and without an explicit console=hvc0 command-line parameter, will hvc0 fail
to register as the kernel console here?
During console_init(), con_init() in drivers/tty/vt/vt.c runs before the hvc
console initcalls because vt/ precedes hvc/ in drivers/tty/Makefile. With no
preferred console selected yet, register_console(&vt_console_driver) enables
tty0 (backed by dummy_con) via try_enable_default_console() and unregisters
prom_early_console.
Next, hvc_console_init() in drivers/tty/hvc/hvc_console.c runs (linked before
hvc_m3000.o in drivers/tty/hvc/Makefile) and calls
register_console(&hvc_console). In kernel/printk/printk.c:register_console():
if (preferred_console < 0) {
if (hlist_empty(&console_list) || !console_first()->device ||
console_first()->flags & CON_BOOT) {
try_enable_default_console(newcon);
}
}
Because console_first() is now vt_console_driver (which has a non-NULL
.device and does not have CON_BOOT set), try_enable_default_console() is
skipped and hvc_console.index remains -1.
When m3000_console_init() then runs and calls
hvc_instantiate(0, 0, &m3000_ops) -> hvc_check_console(0):
if (index == hvc_console.index)
register_console(&hvc_console);
Because hvc_console.index is -1 rather than 0, and add_preferred_console()
was never called, register_console(&hvc_console) is not invoked, leaving the
system without kernel console output after prom_early_console is unregistered.
Should m3000_console_init() call add_preferred_console("hvc", 0, NULL) before
hvc_instantiate() (or before hvc_console_init() runs) when Open Firmware's
stdout/stdin are verified to be pseudo-console?
> +
> +static int __init m3000_tty_init(void)
> +{
> + struct m3000_console *con = &m3000_consoles[0];
> + struct hvc_struct *hp;
> +
> + if (!con->ready)
> + return -ENODEV;
> + timer_setup(&con->poll_timer, m3000_poll_tick, 0);
> + hp = hvc_alloc(0, 0, &m3000_ops, sizeof(con->buffer));
> + if (IS_ERR(hp))
> + return PTR_ERR(hp);
> + hp->ws.ws_row = 24;
> + hp->ws.ws_col = 80;
> + return 0;
> +}
> +device_initcall(m3000_tty_init);
[Severity: Low]
Does setting hp->ws.ws_row and hp->ws.ws_col directly in m3000_tty_init()
actually propagate the 80x24 window size to the TTY when /dev/hvc0 is opened?
In drivers/tty/hvc/hvc_console.c, neither hvc_install() nor hvc_open() reads
hp->ws. The only consumer of hp->ws is the workqueue callback
hvc_set_winsz():
tty = tty_port_tty_get(&hp->port);
if (!tty)
return;
spin_lock_irqsave(&hp->lock, hvc_flags);
ws = hp->ws;
spin_unlock_irqrestore(&hp->lock, hvc_flags);
tty_do_resize(tty, &ws);
hvc_set_winsz() only runs when hp->tty_resize is scheduled via hvc_resize()
or __hvc_resize(), and requires a tty_struct to already be attached to
hp->port (which happens in hvc_open() via tty_port_tty_set() right before
hp->ops->notifier_add is called).
Should this use hvc_resize(hp, ws) inside m3000_open() instead so that
hvc_set_winsz() is scheduled once the TTY is attached?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=10
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v4 08/10] sparc64: add SPARC64 VII CPU, MMU and SMP support
2026-10-18 13:45 ` [PATCH v4 08/10] sparc64: add SPARC64 VII CPU, MMU and SMP support Magnus Lindholm
@ 2026-10-18 14:22 ` sashiko-bot
0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-10-18 14:22 UTC (permalink / raw)
To: Magnus Lindholm; +Cc: linux-serial
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] sparc64: broken TL0/TL1 trap handling for sparc64_vii_ecc_error()
- [Medium] sparc64: missing sparc64_vii_check_mmu_state() call in setup_trap_table()
- [Medium] sparc64: inverted core_id and proc_id hierarchy in vii_cpu_topology()
--
commit 0bb62b166c246b99a1e65e42c594b6bc9e54632a
Author: Magnus Lindholm <linmag7@gmail.com>
sparc64: add SPARC64 VII CPU, MMU and SMP support
This commit adds runtime support for Fujitsu SPARC64 VII (implementation 7)
processors, including CPU identification, topology discovery, physical TSB
accesses, MMU demap and context synchronization, 47-bit physical address PTE
handling, SMP IPI delivery, and restrainable ECC trap handling.
> diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
> index cf0549134234e..365ca8198ea0d 100644
> --- a/arch/sparc/kernel/head_64.S
> +++ b/arch/sparc/kernel/head_64.S
[ ... ]
> @@ -841,6 +860,7 @@ setup_trap_table:
> sllx %o2, 32, %o2
> wr %o2, 0, %tick_cmpr
>
> + BRANCH_IF_SPARC64_VII(o2, o3, 1f)
> BRANCH_IF_ANY_CHEETAH(o2, o3, 1f)
>
> ba,a,pt %xcc, 2f
[Severity: Medium]
The commit message states that disabled shared-context and TSB-prefetch valid
bits are required at CPU entry and firmware handoffs, and after_lock_tlb in
arch/sparc/kernel/trampoline_64.S runs VII_CHECK_MMU_STATE right after its
direct p1275buf CIF call to SUNW,set-trap-table.
However, in setup_trap_table() in arch/sparc/kernel/head_64.S (reached via
paging_init() -> setup_tba()), the boot CPU makes the same direct p1275buf
CIF call to SUNW,set-trap-table (bypassing p1275_cmd_direct() and
prom_world()):
arch/sparc/kernel/head_64.S:setup_trap_table:
sethi %hi(p1275buf), %g2
or %g2, %lo(p1275buf), %g2
ldx [%g2 + 0x08], %o1
call %o1
add %sp, (2047 + 128), %o0
/* Start using proper page size encodings in ctx register. */
2: sethi %hi(sparc64_kern_pri_context), %g3
Should setup_trap_table() also call sparc64_vii_check_mmu_state() upon return
from this firmware call before programming PRIMARY_CONTEXT and re-enabling
interrupts?
> diff --git a/arch/sparc/kernel/helpers.S b/arch/sparc/kernel/helpers.S
> index 9b3f74706cfbc..b4ea0ca95a37e 100644
> --- a/arch/sparc/kernel/helpers.S
> +++ b/arch/sparc/kernel/helpers.S
> @@ -64,3 +64,129 @@ real_hard_smp_processor_id:
> #endif
> .size real_hard_smp_processor_id,.-real_hard_smp_processor_id
> EXPORT_SYMBOL_GPL(real_hard_smp_processor_id)
> +
> + .align 32
> + .globl sparc64_vii_ecc_trap
> +sparc64_vii_ecc_trap:
> + TRAP(sparc64_vii_ecc_error)
> + .globl sparc64_vii_ecc_trap_tl1
> +sparc64_vii_ecc_trap_tl1:
> + TRAPTL1(sparc64_vii_ecc_error)
[Severity: High]
In vii_patch_error_traps(), trap 0x63 is routed to sparc64_vii_ecc_trap at
TL=0 (using TRAP / etrap) and sparc64_vii_ecc_trap_tl1 at TL>0 (using
TRAPTL1 / etraptl1), and sparc64_vii_ecc_error() returns normally when AFSR
is zero or indicates cache/TLB degradation.
Can a recoverable trap 0x63 at TL>0 safely use TRAPTL1(sparc64_vii_ecc_error)
and return through rtrap? Looking at etraptl1 in arch/sparc/kernel/etrap_64.S,
it is structured for fatal traps that never return:
arch/sparc/kernel/etrap_64.S:etraptl1:
TRAP_LOAD_THREAD_REG(%g6, %g1)
sub %sp, ((4 * 8) * 4) + 8, %g2
rdpr %tl, %g1
...
finish_tl1_capture:
wrpr %g0, 1, %tl
...
rdpr %tstate, %g1
sub %g2, STACKFRAME_SZ + TRACEREG_SZ - STACK_BIAS, %g2
ba,pt %xcc, 1b
andcc %g1, TSTATE_PRIV, %g0
First, etraptl1 computes %g2 directly from %sp, writes the traplog to
[%g2 + STACK_BIAS + ...], and branches to label 1b, bypassing the user-to-
kernel stack switch in etrap. If trap 0x63 occurs at TL=1 while %sp is still
a userspace stack pointer (for example, during a user TLB miss in tl0_iamiss
or tl0_damiss, a window spill/fill trap, or in etrap/rtrap before switching
stacks), won't etraptl1 write the traplog and pt_regs onto the user stack and
execute sparc64_vii_ecc_error() on the user stack?
Second, finish_tl1_capture forces %tl to 1, captures TL=1's %tstate, %tpc,
and %tnpc into pt_regs instead of TL=2's trap state, overwrites TL=1's trap
registers, and drops to TL=0 via done. When sparc64_vii_ecc_error() returns
to rtrap, won't rtrap restore TL=1's %tstate/%tpc/%tnpc and retry to TL=0
instead of resuming the interrupted TL=1 handler at TL=2's %tpc (aborting the
TL=1 handler mid-execution, or jumping to stale %tpc[1]/%tstate[1] values if
the trap occurred while __vii_flush_tlb_page or __vii_flush_tlb_pending had
manually set %tl to 1)?
[ ... ]
> diff --git a/arch/sparc/kernel/prom_64.c b/arch/sparc/kernel/prom_64.c
> index aa4799cbb9c1d..c4448ec6e9190 100644
> --- a/arch/sparc/kernel/prom_64.c
> +++ b/arch/sparc/kernel/prom_64.c
> @@ -501,7 +501,30 @@ void __init of_populate_present_mask(void)
> of_iterate_over_cpus(record_one_cpu, 0);
> }
>
> -static void *fill_in_one_cpu(struct device_node *dp, int cpuid, int arg)
> +static void __init vii_cpu_topology(struct device_node *dp, int cpuid)
> +{
> + struct device_node *core = dp->parent;
> + struct device_node *package;
> +
> + cpu_data(cpuid).core_id = 0;
> + cpu_data(cpuid).proc_id = -1;
> + cpu_data(cpuid).sock_id = cpuid;
> + if (!core || !of_node_is_type(core, "core") ||
> + !of_device_is_compatible(core, "FJSV,SPARC64-VII"))
> + return;
> + package = core->parent;
> + if (!package || !of_node_name_eq(package, "cmp"))
> + return;
> + if (core->unique_id >= USHRT_MAX || package->unique_id > SHRT_MAX)
> + return;
> +
> + /* Node IDs distinguish equal core addresses in different packages. */
> + cpu_data(cpuid).core_id = core->unique_id + 1;
> + cpu_data(cpuid).proc_id = package->unique_id;
> + cpu_data(cpuid).sock_id = package->unique_id;
> +}
[Severity: Medium]
Does assigning the per-core ID to core_id and the per-package ID to proc_id
break the cpuinfo_tree hierarchy built by _cpu_map_rebuild() in
arch/sparc/kernel/cpumap.c?
When sun4u_irq_enable() or sun4u_set_affinity() calls irq_choose_cpu() ->
map_to_cpu() -> _cpu_map_rebuild(), enumerate_cpuinfo_nodes() and
build_cpuinfo_tree() construct a 4-level tree ordered as ROOT -> NODE ->
CORE (core_id) -> PROC (proc_id):
arch/sparc/kernel/cpumap.c:build_cpuinfo_tree() {
...
if ((id != prev_id[level]) || (cpu == last_cpu)) {
prev_id[level] = id;
node = &new_tree->nodes[level_rover[level]];
...
if (level == CPUINFO_LVL_PROC) {
node->child_end =
(cpu == last_cpu) ? cpu : prev_cpu;
} else {
node->child_end =
level_rover[level + 1] - 1;
}
...
}
Because vii_cpu_topology() sets core_id per core and proc_id per cmp package,
all cores within a package share the same proc_id. When build_cpuinfo_tree()
advances from one core to the next within the same package, core_id changes
while proc_id does not, so level_rover[CPUINFO_LVL_PROC] has not advanced.
As a result, the previous CORE node gets child_end < child_start, and the new
CORE node points its child_start and rover to the same package-wide PROC
node. When iterate_cpu() walks this tree to populate cpu_distribution_map[],
single-package systems distribute IRQs sequentially to sibling threads on the
same core before moving to the next core, while multi-package systems repeat
the first package's CPUs once per core in that package and omit CPUs on other
packages.
[ ... ]
> diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c
> index 28cb0d66ab40c..3c693b7bd32b6 100644
> --- a/arch/sparc/kernel/traps_64.c
> +++ b/arch/sparc/kernel/traps_64.c
> @@ -2716,6 +2716,33 @@ void do_privact(struct pt_regs *regs)
> do_privop(regs);
> }
>
> +/* SPARC64 VII Extensions, Appendix P.7.1 and P.7.4. */
> +#define VII_AFSR_DEGRADATION ((1UL << 12) | (1UL << 11) | (1UL << 10))
> +
> +void sparc64_vii_ecc_error(struct pt_regs *regs)
> +{
> + enum ctx_state prev_state = exception_enter();
> + unsigned long afsr;
> +
> + __asm__ __volatile__("ldxa [%%g0] 0x4c, %0" : "=r" (afsr));
> + /* An ECC trap without a sticky error indication is explicitly allowed. */
> + if (!afsr)
> + goto out;
> +
> + /* No data recovery is implemented for raw UE or store bus errors. */
> + if (afsr & ~VII_AFSR_DEGRADATION)
> + panic("SPARC64 VII: unhandled ECC error, CPU %u AFSR=%lx TPC=%lx",
> + smp_processor_id(), afsr, regs->tpc);
> +
> + /* Hardware has already reduced the affected cache/TLB associativity. */
> + __asm__ __volatile__("stxa %0, [%%g0] 0x4c; membar #Sync"
> + : : "r" (afsr) : "memory");
> + pr_warn_ratelimited("SPARC64 VII: CPU %u cache/TLB degradation, AFSR=%lx\n",
> + smp_processor_id(), afsr);
> +out:
> + exception_exit(prev_state);
> +}
[Severity: High]
Because sparc64_vii_ecc_trap uses TRAP(sparc64_vii_ecc_error) rather than
TRAP_IRQ (or latching and clearing AFSR at TL=1 before re-enabling PSTATE_IE,
as c_cee in arch/sparc/kernel/cherrs.S and __spitfire_access_error in
arch/sparc/kernel/spiterrs.S do), etrap drops to TL=0 with TSTATE_IE set and
%pil unchanged (which is 0 when trapping from user mode or normal kernel
context).
Doesn't this run sparc64_vii_ecc_error() with hardware interrupts and
preemption enabled before the per-CPU AFSR register (ASI 0x4c) is read and
cleared, which can trigger a preemptible smp_processor_id() warning under
CONFIG_DEBUG_PREEMPT, allow the task to migrate to another CPU between the
ldxa and stxa on ASI 0x4c, or re-trigger trap 0x63 while sticky AFSR bits
remain set?
> +
> /* Trap level 1 stuff or other traps we should never see... */
> void do_cee(struct pt_regs *regs)
> {
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261018134738.1016106-1-linmag7@gmail.com?part=8
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-10-18 14:23 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-18 13:45 [PATCH v4 00/10] sparc64: add Fujitsu M3000 support Magnus Lindholm
2026-10-18 13:45 ` [PATCH v4 01/10] sparc64: return from the generic clear_page implementation Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 02/10] sparc64: honor queued spinlock layout in secondary startup Magnus Lindholm
2026-10-18 14:01 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 03/10] sparc64: avoid huge kernel PUD mappings with small hardware pages Magnus Lindholm
2026-10-18 13:53 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 04/10] sparc64: stop IRQ setup when descriptor allocation fails Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 05/10] sparc64: validate Fire register properties before initialization Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 06/10] sparc64: retain the DMA segment start across SG entries Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 07/10] sparc64: zero newly allocated IOMMU page tables Magnus Lindholm
2026-10-18 13:52 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 08/10] sparc64: add SPARC64 VII CPU, MMU and SMP support Magnus Lindholm
2026-10-18 14:22 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 09/10] sparc64: add M3000 Oberon PCIe support Magnus Lindholm
2026-10-18 13:57 ` sashiko-bot
2026-10-18 13:45 ` [PATCH v4 10/10] hvc: add an M3000 firmware console backend Magnus Lindholm
2026-10-18 14:09 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.