All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/8] sparc64: add Fujitsu M3000 support
@ 2026-10-12 18:48 Magnus Lindholm
  2026-10-12 18:48 ` [PATCH v3 1/8] sparc64: return from the generic clear_page implementation Magnus Lindholm
                   ` (7 more replies)
  0 siblings, 8 replies; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 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.
For this, Codex proved very helpful. 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. It is based on v7.3-rc1.

Patches 1-5 are generic sparc64 fixes: the clear_page return, secondary
startup spinlock layout, sun4u kernel PUD mappings, IRQ allocation
failure handling and Fire firmware register-property validation.
Patches 6-8 add the M3000 support. The generic fixes are kept in this
series so that the architecture changes can be applied and tested
together.

The on-board BCM5718 also needs the separate netdev patch:

[PATCH net-next v3] tg3: normalize inherited BCM5718 register byte order
Link: https://lore.kernel.org/sparclinux/20261011194228.981141-1-linmag7@gmail.com/

That patch applies independently and is not included in this series.
It was included in the M3000 test kernel described below.

Changes since v2
---------------
- Add separate generic fixes for unchecked IRQ allocation failure and
  missing or short Fire reg properties, following the Sashiko review.
  IRQ setup now stops before publishing an invalid descriptor; register
  properties are validated before their entries are accessed.
- Mark fill_in_one_cpu() as __init to match its initialization-only
  caller and vii_cpu_topology(), fixing the section mismatch reported
  by the kernel test robot.
- Address Jiri Slaby's HVC review: reorder the console state to reduce
  padding, use guard()/scoped_guard() for locking, test the input
  pointer directly, and name the firmware argument counts and error
  return values. Keep the polling spinlock released during synchronous
  timer deletion, with the mutex held to serialize close and reopen.
- Retain the raw buffer lock for atomic console calls and the existing
  firmware polling policy. Zero still means zero bytes transferred;
  firmware argument-array positions remain the fixed interface layout.
- Remove the leftover experimental Oberon boot message.
- Fold the CPU and HVC corrections into their respective support
  patches. The two new generic fixes bring the SPARC series to eight
  patches; the tg3 fix remains separate.

Testing
-------
The candidate source tree, including the separate tg3 patch, was built
with W=1 using GCC 13.3 and Clang 23.1.3. Neither build reports the
CPU topology section mismatch or warnings in the changed HVC code.
Other warnings remain in existing code. This does not reproduce the
robot's exact Clang 24 development compiler and defconfig combination.

The GCC kernel booted from disk into OpenRC and SSH on an M3000 with
all eight CPUs and 16 GiB of RAM. Bounded fork/COW, CPU migration,
file write/read verification and network transfer checks passed.
HVC checks covered output, paced input after idle, concurrent open and
close, and hangup/reopen. The kernel remained untainted, with no new
WARNING, BUG or Oops reports during these checks.

The final export plus tg3 differs from that boot-tested source tree only
by removal of the experimental Oberon boot message. It applies cleanly
to v7.3-rc1 and has been rebuilt with GCC and W=1; the final export has
not been rebooted. The Clang image was not booted. This revision has not
been retested on the Ultra45 or in UP mode, and these bounded tests do
not establish long-term stability or exercise hardware failure paths.

Current limitations
-------------------
SPARC64 VII support remains a Kconfig option because this path currently
excludes HugeTLB and THP. Removing that restriction requires further
large-page MMU work and testing. CPU offlining is not supported.

Oberon uses legacy interrupts; MSI and native bridge error recovery are
not implemented. CPU error handling is limited to the supported
restrainable ECC path; other unhandled error traps remain fatal.

The HVC backend relies on firmware calls. A firmware call that does not
return remains unbounded, and fast pasted input can exceed the firmware
FIFO. The console tests above do not establish panic-path reliability.

Magnus Lindholm (8):
  sparc64: return from the generic clear_page implementation
  sparc64: honor queued spinlock layout in secondary startup
  sparc64: avoid huge kernel PUD mappings on sun4u
  sparc64: stop IRQ setup when descriptor allocation fails
  sparc64: validate Fire register properties before initialization
  sparc64: add SPARC64 VII CPU, MMU and SMP support
  sparc64: add M3000 Oberon PCIe support
  hvc: add an M3000 firmware console backend

 MAINTAINERS                         |   6 +
 arch/sparc/Kconfig                  |  20 +++
 arch/sparc/include/asm/head_64.h    |  15 ++
 arch/sparc/include/asm/iommu_64.h   |   1 +
 arch/sparc/include/asm/irq_64.h     |   1 +
 arch/sparc/include/asm/setup.h      |   5 +
 arch/sparc/include/asm/smp_64.h     |   4 +
 arch/sparc/include/asm/spitfire.h   |   1 +
 arch/sparc/include/asm/trap_block.h |   6 +
 arch/sparc/kernel/cpu.c             |   3 +
 arch/sparc/kernel/entry.h           |   2 +
 arch/sparc/kernel/head_64.S         |  18 +++
 arch/sparc/kernel/helpers.S         |  41 +++++
 arch/sparc/kernel/iommu.c           |  44 ++++++
 arch/sparc/kernel/irq_64.c          |  54 +++++--
 arch/sparc/kernel/pci_fire.c        |  37 ++++-
 arch/sparc/kernel/pci_impl.h        |   1 +
 arch/sparc/kernel/prom_64.c         |  29 +++-
 arch/sparc/kernel/prom_irqtrans.c   |  15 +-
 arch/sparc/kernel/ptrace_64.c       |   7 +
 arch/sparc/kernel/setup_64.c        |  55 +++++++
 arch/sparc/kernel/smp_64.c          |  89 ++++++++++-
 arch/sparc/kernel/trampoline_64.S   |  53 ++++++-
 arch/sparc/kernel/traps_64.c        |  27 ++++
 arch/sparc/lib/GENpage.S            |   3 +
 arch/sparc/mm/init_64.c             | 110 ++++++++++++-
 arch/sparc/mm/tsb.c                 |  19 ++-
 arch/sparc/mm/ultra.S               | 236 ++++++++++++++++++++++++++++
 drivers/tty/hvc/Kconfig             |  10 ++
 drivers/tty/hvc/Makefile            |   1 +
 drivers/tty/hvc/hvc_m3000.c         | 202 ++++++++++++++++++++++++
 31 files changed, 1077 insertions(+), 38 deletions(-)
 create mode 100644 drivers/tty/hvc/hvc_m3000.c


base-commit: cee9395acd8043be0644b25c34bfa86623f2b935

^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v3 1/8] sparc64: return from the generic clear_page implementation
  2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
@ 2026-10-12 18:48 ` Magnus Lindholm
  2026-10-12 18:55   ` sashiko-bot
  2026-10-12 18:48 ` [PATCH v3 2/8] sparc64: honor queued spinlock layout in secondary startup Magnus Lindholm
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 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.
Return to the caller 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)	\

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 2/8] sparc64: honor queued spinlock layout in secondary startup
  2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
  2026-10-12 18:48 ` [PATCH v3 1/8] sparc64: return from the generic clear_page implementation Magnus Lindholm
@ 2026-10-12 18:48 ` Magnus Lindholm
  2026-10-12 18:55   ` sashiko-bot
  2026-10-12 18:48 ` [PATCH v3 3/8] sparc64: avoid huge kernel PUD mappings on sun4u Magnus Lindholm
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 UTC (permalink / raw)
  To: davem, andreas, sparclinux
  Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
	oleg, linmag7, stable

The trampoline's ldstub modifies the queue-tail byte of prom_entry_lock,
not its locked byte, because SPARC64 uses big-endian queued spinlocks.
Acquire the word with CASA, release byte three and poll while contended.
Retain the predicted-branch barriers for Spitfire erratum 51.

See qspinlock_types.h for the layout 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 | 34 +++++++++++++++++++++++++++----
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/arch/sparc/kernel/trampoline_64.S b/arch/sparc/kernel/trampoline_64.S
index 51bf1eb92a36..62f404b3079a 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,10 @@ startup_continue:
 	 nop
 
 	sethi		%hi(prom_entry_lock), %g2
-	stb		%g0, [%g2 + %lo(prom_entry_lock)]
+	/* Match membar_safe() for 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 +339,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 +412,10 @@ after_lock_tlb:
 	 add		%sp, (2047 + 128), %o0
 
 3:	sethi		%hi(prom_entry_lock), %g2
-	stb		%g0, [%g2 + %lo(prom_entry_lock)]
+	/* Match membar_safe() for 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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 3/8] sparc64: avoid huge kernel PUD mappings on sun4u
  2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
  2026-10-12 18:48 ` [PATCH v3 1/8] sparc64: return from the generic clear_page implementation Magnus Lindholm
  2026-10-12 18:48 ` [PATCH v3 2/8] sparc64: honor queued spinlock layout in secondary startup Magnus Lindholm
@ 2026-10-12 18:48 ` Magnus Lindholm
  2026-10-12 18:55   ` sashiko-bot
  2026-10-12 18:48 ` [PATCH v3 4/8] sparc64: stop IRQ setup when descriptor allocation fails Magnus Lindholm
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 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 a hardware page size of at least 256MB. On sun4u,
kern_linear_pte_xor[2] and [3] instead select the 4MB fallback TTE.
Consequently address bits 27:22 are lost when refilling a huge PUD.

Keep the kernel linear mapping at PMD granularity on sun4u. Its refill
path preserves bit 22, as required by the 4MB TTE. Leave the sun4v
mapping selection unchanged.

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..0ae97e616435 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. Sun4u uses 4MB pages here: retain PMD
+	 * mappings so bits 27:22 are not lost during TLB refill.
+	 */
+	if (tlb_type != hypervisor)
+		return false;
+
 	if (guard && !(vstart & ~PUD_MASK) && (vend - vstart) >= PUD_SIZE)
 		return true;
 

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 4/8] sparc64: stop IRQ setup when descriptor allocation fails
  2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
                   ` (2 preceding siblings ...)
  2026-10-12 18:48 ` [PATCH v3 3/8] sparc64: avoid huge kernel PUD mappings on sun4u Magnus Lindholm
@ 2026-10-12 18:48 ` Magnus Lindholm
  2026-10-12 19:03   ` sashiko-bot
  2026-10-12 18:48 ` [PATCH v3 5/8] sparc64: validate Fire register properties before initialization Magnus Lindholm
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 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.

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");

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 5/8] sparc64: validate Fire register properties before initialization
  2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
                   ` (3 preceding siblings ...)
  2026-10-12 18:48 ` [PATCH v3 4/8] sparc64: stop IRQ setup when descriptor allocation fails Magnus Lindholm
@ 2026-10-12 18:48 ` Magnus Lindholm
  2026-10-12 18:55   ` sashiko-bot
  2026-10-12 18:48 ` [PATCH v3 6/8] sparc64: add SPARC64 VII CPU, MMU and SMP support Magnus Lindholm
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 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.

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;

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 6/8] sparc64: add SPARC64 VII CPU, MMU and SMP support
  2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
                   ` (4 preceding siblings ...)
  2026-10-12 18:48 ` [PATCH v3 5/8] sparc64: validate Fire register properties before initialization Magnus Lindholm
@ 2026-10-12 18:48 ` Magnus Lindholm
  2026-10-12 19:03   ` sashiko-bot
  2026-10-12 18:48 ` [PATCH v3 7/8] sparc64: add M3000 Oberon PCIe support Magnus Lindholm
  2026-10-12 18:48 ` [PATCH v3 8/8] hvc: add an M3000 firmware console backend Magnus Lindholm
  7 siblings, 1 reply; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 UTC (permalink / raw)
  To: davem, andreas, sparclinux
  Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
	oleg, linmag7, kernel test robot

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.
CPU offlining, HugeTLB and THP remain unsupported on this path.
Keep VII demap routines separate: similar Cheetah assembly does not
establish the same hardware synchronization requirement on Cheetah.

Hardware references (printed sections/pages):
- SPARC64 VII Extensions v1.0: 8.4.7 pp.42-43 (coherence), A.30 pp.64-65
  (physical loads), F.10 pp.109-110 (MCNTL), F.10.4 (indexed TLBs),
  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 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).

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202610121549.5ejZQ08o-lkp@intel.com/
Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
 arch/sparc/Kconfig                  |  10 ++
 arch/sparc/include/asm/head_64.h    |  15 ++
 arch/sparc/include/asm/setup.h      |   5 +
 arch/sparc/include/asm/smp_64.h     |   4 +
 arch/sparc/include/asm/spitfire.h   |   1 +
 arch/sparc/include/asm/trap_block.h |   6 +
 arch/sparc/kernel/cpu.c             |   3 +
 arch/sparc/kernel/entry.h           |   2 +
 arch/sparc/kernel/head_64.S         |  18 +++
 arch/sparc/kernel/helpers.S         |  41 +++++
 arch/sparc/kernel/prom_64.c         |  29 +++-
 arch/sparc/kernel/ptrace_64.c       |   7 +
 arch/sparc/kernel/setup_64.c        |  55 +++++++
 arch/sparc/kernel/smp_64.c          |  89 ++++++++++-
 arch/sparc/kernel/trampoline_64.S   |  19 +++
 arch/sparc/kernel/traps_64.c        |  27 ++++
 arch/sparc/mm/init_64.c             | 102 +++++++++++-
 arch/sparc/mm/tsb.c                 |  19 ++-
 arch/sparc/mm/ultra.S               | 236 ++++++++++++++++++++++++++++
 19 files changed, 670 insertions(+), 18 deletions(-)

diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index ab77d3f2536e..1263150a5702 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -250,6 +250,16 @@ config US3_MC
 
 	  If in doubt, say Y, as this information can be very useful.
 
+config SPARC64_VII
+	bool "Fujitsu SPARC64 VII and VII+ support"
+	depends on SPARC64
+	depends on !HUGETLB_PAGE && !TRANSPARENT_HUGEPAGE
+	help
+	  Support SPARC64 VII and VII+ processors in the Fujitsu M3000.
+	  Firmware must leave context hashing and multiple-page-size
+	  context mode disabled. HugeTLB and transparent huge pages are
+	  not supported by this CPU path.
+
 # Global things across all Sun machines.
 config GENERIC_LOCKBREAK
 	bool
diff --git a/arch/sparc/include/asm/head_64.h b/arch/sparc/include/asm/head_64.h
index 69a2062d992c..62ea826f174b 100644
--- a/arch/sparc/include/asm/head_64.h
+++ b/arch/sparc/include/asm/head_64.h
@@ -20,6 +20,21 @@
 #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
+
+#ifdef CONFIG_SPARC64_VII
+#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
+#else
+#define BRANCH_IF_SPARC64_VII(tmp1, tmp2, label)
+#endif
+
 #define __CHEETAH_ID	0x003e0014
 #define __JALAPENO_ID	0x003e0016
 #define __SERRANO_ID	0x003e0022
diff --git a/arch/sparc/include/asm/setup.h b/arch/sparc/include/asm/setup.h
index 21bed5514028..ef6ff1169ec0 100644
--- a/arch/sparc/include/asm/setup.h
+++ b/arch/sparc/include/asm/setup.h
@@ -49,6 +49,11 @@ 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_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..bbf171c9d1e9 100644
--- a/arch/sparc/include/asm/smp_64.h
+++ b/arch/sparc/include/asm/smp_64.h
@@ -34,6 +34,10 @@
 DECLARE_PER_CPU(cpumask_t, cpu_sibling_map);
 extern cpumask_t cpu_core_map[NR_CPUS];
 
+#ifdef CONFIG_SPARC64_VII
+extern unsigned long vii_startup_mcntl;
+#endif
+
 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..015bf4e5ca10 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,7 @@ 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"),
+		CPU(7, "Fujitsu SPARC64 VII / VII+"),
 		CPU(-1, NULL)
 	},
 	.fpu_info = {
@@ -139,6 +141,7 @@ static const struct manufacturer_info __initconst manufacturer_info[] = {
 		FPU(0, "SuperSparc on-chip FPU"),
 		/* SparcClassic */
 		FPU(4, "TI MicroSparc on chip FPU"),
+		FPU(7, "Fujitsu SPARC64 VII integrated FPU"),
 		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..8f879abc07f0 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,20 @@ cheetah_tlb_fixup:
 
 	ba,a,pt	%xcc, tlb_fixup_done
 
+sparc64_vii_tlb_fixup:
+	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 +858,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..da5467db2de3 100644
--- a/arch/sparc/kernel/helpers.S
+++ b/arch/sparc/kernel/helpers.S
@@ -64,3 +64,44 @@ 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]
+	sethi %hi(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
diff --git a/arch/sparc/kernel/prom_64.c b/arch/sparc/kernel/prom_64.c
index aa4799cbb9c1..ce56ed917f07 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;
@@ -564,6 +587,7 @@ static void *fill_in_one_cpu(struct device_node *dp, int cpuid, int arg)
 
 		cpu_data(cpuid).core_id = portid + 1;
 		cpu_data(cpuid).proc_id = portid;
+
 	} else {
 		cpu_data(cpuid).dcache_size =
 			of_getintprop_default(dp, "dcache-size", 16 * 1024);
@@ -585,6 +609,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..0f1187fc775f 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,40 @@ 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_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 +395,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));
+		/*
+		 * No context hashing, forced uncached instruction caching or
+		 * multiple-page-size context mode is supported by this path.
+		 * 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..a1344bbd8554 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,11 @@ extern unsigned long sparc64_cpu_startup;
  */
 static struct thread_info *cpu_new_thread = NULL;
 
+#ifdef CONFIG_SPARC64_VII
+/* Written before the secondary validates its inherited MMU control. */
+unsigned long vii_startup_mcntl;
+#endif
+
 static int smp_boot_one_cpu(unsigned int cpu, struct task_struct *idle)
 {
 	unsigned long entry =
@@ -354,6 +358,10 @@ static int smp_boot_one_cpu(unsigned int cpu, struct task_struct *idle)
 	int timeout, ret;
 
 	callin_flag = 0;
+#ifdef CONFIG_SPARC64_VII
+	if (tlb_type == sparc64_vii)
+		WRITE_ONCE(vii_startup_mcntl, ~0UL);
+#endif
 	cpu_new_thread = task_thread_info(idle);
 
 	if (tlb_type == hypervisor) {
@@ -381,6 +389,11 @@ static int smp_boot_one_cpu(unsigned int cpu, struct task_struct *idle)
 		ret = 0;
 	} else {
 		printk("Processor %d is stuck.\n", cpu);
+#ifdef CONFIG_SPARC64_VII
+		if (tlb_type == sparc64_vii)
+			pr_err("VII secondary CPU %u MCNTL=%lx (all ones: not reached)\n",
+			       cpu, READ_ONCE(vii_startup_mcntl));
+#endif
 		ret = -ENODEV;
 	}
 	cpu_new_thread = NULL;
@@ -390,6 +403,55 @@ static int smp_boot_one_cpu(unsigned int cpu, struct task_struct *idle)
 	return ret;
 }
 
+/*
+ * VII manual Appendix N: dispatch using BUSY/NACK pair zero, one target
+ * at a time. No Spitfire UDB erratum access and no hypervisor operation.
+ */
+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 +1267,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 +1305,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 +1325,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 +1401,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 62f404b3079a..73838a021be6 100644
--- a/arch/sparc/kernel/trampoline_64.S
+++ b/arch/sparc/kernel/trampoline_64.S
@@ -40,6 +40,7 @@ tramp_stack:
 	.align		8
 	.globl		sparc64_cpu_startup, sparc64_cpu_startup_end
 sparc64_cpu_startup:
+	BRANCH_IF_SPARC64_VII(g1, g5, vii_startup)
 	BRANCH_IF_SUN4V(g1, niagara_startup)
 	BRANCH_IF_CHEETAH_BASE(g1, g5, cheetah_startup)
 	BRANCH_IF_CHEETAH_PLUS_OR_FOLLOWON(g1, g5, cheetah_plus_startup)
@@ -47,6 +48,23 @@ sparc64_cpu_startup:
 	ba,pt	%xcc, spitfire_startup
 	 nop
 
+#ifdef CONFIG_SPARC64_VII
+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
+	set		0x101c0, %g1
+	andcc		%g5, %g1, %g0
+1:	bne,pn		%xcc, 1b
+	 nop
+	ba,pt		%xcc, niagara_startup
+	 nop
+
+#endif /* CONFIG_SPARC64_VII */
+
 cheetah_plus_startup:
 	/* Preserve OBP chosen DCU and DCR register settings.  */
 	ba,pt	%xcc, cheetah_generic_startup
@@ -144,6 +162,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
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 0ae97e616435..4918afdcc923 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);
@@ -495,6 +496,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 +537,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 +675,50 @@ static void __init hypervisor_tlb_lock(unsigned long vaddr,
 
 static unsigned long kern_large_tte(unsigned long paddr);
 
+/*
+ * Reserve two 4MB kernel mappings and an adjacent slot 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 = 15 - 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)
+{
+	unsigned long mask = _PAGE_VALID | _PAGE_SZ4MB_4U |
+		_PAGE_PADDR_4U | _PAGE_L_4U;
+	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 || it != va) {
+		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 +730,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 +743,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;
 		}
@@ -724,6 +794,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 +2422,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 +2445,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 +2487,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 +2898,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..c4a9647d6eb0 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
+
+	/* VII uses 32-entry fTLBs: do not use the Spitfire 64-slot sweep. */
+	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

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 7/8] sparc64: add M3000 Oberon PCIe support
  2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
                   ` (5 preceding siblings ...)
  2026-10-12 18:48 ` [PATCH v3 6/8] sparc64: add SPARC64 VII CPU, MMU and SMP support Magnus Lindholm
@ 2026-10-12 18:48 ` Magnus Lindholm
  2026-10-12 19:01   ` sashiko-bot
  2026-10-12 18:48 ` [PATCH v3 8/8] hvc: add an M3000 firmware console backend Magnus Lindholm
  7 siblings, 1 reply; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 UTC (permalink / raw)
  To: davem, andreas, sparclinux
  Cc: linux-kernel, gregkh, jirislaby, linux-serial, linuxppc-dev, krzk,
	oleg, linmag7

Recognize Oberon while 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/Kconfig                | 10 +++++++
 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      | 28 +++++++++++++++--
 arch/sparc/kernel/pci_impl.h      |  1 +
 arch/sparc/kernel/prom_irqtrans.c |  7 +++++
 8 files changed, 129 insertions(+), 13 deletions(-)

diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 1263150a5702..96aa140815fe 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -387,6 +387,16 @@ endif
 endmenu
 
 menu "Bus options (PCI etc.)"
+
+config SPARC64_OBERON
+	bool "Fujitsu M3000 Oberon PCIe bridge"
+	depends on SPARC64_VII && PCI
+	help
+	  Support Oberon enumeration, translated DMA and legacy interrupts.
+	  Firmware link configuration is preserved. MSI and native bridge
+	  error interrupt handling are not supported on Oberon.
+	  Enable this option for the M3000 on-board network and disk devices.
+
 config SBUS
 	bool
 	default y
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 7613ab0ffb89..9132f0535ce4 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..00992fa11a60 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,9 @@ static const struct of_device_id fire_match[] = {
 		.name = "pci",
 		.compatible = "pciex108e,80f0",
 	},
+#ifdef CONFIG_SPARC64_OBERON
+	{ .name = "pci", .compatible = "pciex108e,80f8" },
+#endif
 	{},
 };
 
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..73051bd2105d 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,9 @@ 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 },
+#ifdef CONFIG_SPARC64_OBERON
+	{ "pciex108e,80f8", fire_irq_trans_init },
+#endif
 };
 #endif
 

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v3 8/8] hvc: add an M3000 firmware console backend
  2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
                   ` (6 preceding siblings ...)
  2026-10-12 18:48 ` [PATCH v3 7/8] sparc64: add M3000 Oberon PCIe support Magnus Lindholm
@ 2026-10-12 18:48 ` Magnus Lindholm
  2026-10-12 18:57   ` sashiko-bot
  2026-10-13 13:04   ` Greg KH
  7 siblings, 2 replies; 18+ messages in thread
From: Magnus Lindholm @ 2026-10-12 18:48 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>
---
 MAINTAINERS                 |   6 ++
 drivers/tty/hvc/Kconfig     |  10 ++
 drivers/tty/hvc/Makefile    |   1 +
 drivers/tty/hvc/hvc_m3000.c | 202 ++++++++++++++++++++++++++++++++++++
 4 files changed, 219 insertions(+)
 create mode 100644 drivers/tty/hvc/hvc_m3000.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..5bc9b1cabd0a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10728,6 +10728,12 @@ 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
+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..0feb7ec98f4f 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -134,3 +134,13 @@ 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_VII
+	select HVC_DRIVER
+	help
+	  Provide hvc0 using Open Firmware stdin/stdout on IKKAKU only.
+	  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);

^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 3/8] sparc64: avoid huge kernel PUD mappings on sun4u
  2026-10-12 18:48 ` [PATCH v3 3/8] sparc64: avoid huge kernel PUD mappings on sun4u Magnus Lindholm
@ 2026-10-12 18:55   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-12 18:55 UTC (permalink / raw)
  To: Magnus Lindholm; +Cc: linux-serial

> The kernel PUD refill path merges virtual address bits 32:28 into a
> TTE, requiring a hardware page size of at least 256MB. On sun4u,
> kern_linear_pte_xor[2] and [3] instead select the 4MB fallback TTE.
> Consequently address bits 27:22 are lost when refilling a huge PUD.
> 
> Keep the kernel linear mapping at PMD granularity on sun4u. Its refill
> path preserves bit 22, as required by the 4MB TTE. Leave the sun4v
> mapping selection unchanged.
> 
> 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. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261012184826.986050-1-linmag7@gmail.com?part=3


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 1/8] sparc64: return from the generic clear_page implementation
  2026-10-12 18:48 ` [PATCH v3 1/8] sparc64: return from the generic clear_page implementation Magnus Lindholm
@ 2026-10-12 18:55   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-12 18:55 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.
> Return to the caller 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. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261012184826.986050-1-linmag7@gmail.com?part=1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 2/8] sparc64: honor queued spinlock layout in secondary startup
  2026-10-12 18:48 ` [PATCH v3 2/8] sparc64: honor queued spinlock layout in secondary startup Magnus Lindholm
@ 2026-10-12 18:55   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-12 18:55 UTC (permalink / raw)
  To: Magnus Lindholm; +Cc: linux-serial

> The trampoline's ldstub modifies the queue-tail byte of prom_entry_lock,
> not its locked byte, because SPARC64 uses big-endian queued spinlocks.
> Acquire the word with CASA, release byte three and poll while contended.
> Retain the predicted-branch barriers for Spitfire erratum 51.
> 
> See qspinlock_types.h for the layout 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>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261012184826.986050-1-linmag7@gmail.com?part=2


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 5/8] sparc64: validate Fire register properties before initialization
  2026-10-12 18:48 ` [PATCH v3 5/8] sparc64: validate Fire register properties before initialization Magnus Lindholm
@ 2026-10-12 18:55   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-12 18:55 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.
> 
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261012184826.986050-1-linmag7@gmail.com?part=5


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 8/8] hvc: add an M3000 firmware console backend
  2026-10-12 18:48 ` [PATCH v3 8/8] hvc: add an M3000 firmware console backend Magnus Lindholm
@ 2026-10-12 18:57   ` sashiko-bot
  2026-10-13 13:04   ` Greg KH
  1 sibling, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-12 18:57 UTC (permalink / raw)
  To: Magnus Lindholm; +Cc: linux-serial

> 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>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261012184826.986050-1-linmag7@gmail.com?part=8


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 7/8] sparc64: add M3000 Oberon PCIe support
  2026-10-12 18:48 ` [PATCH v3 7/8] sparc64: add M3000 Oberon PCIe support Magnus Lindholm
@ 2026-10-12 19:01   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-12 19:01 UTC (permalink / raw)
  To: Magnus Lindholm; +Cc: linux-serial

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] iommu: missing IOMMU TSB clearing in dma_4u_free_coherent for non-Oberon
- [Medium] sparc64: IOMMU context leak in dma_4u_map_sg error path
--

commit 4df317d4338c546c969a0d6073e2c04be876d4a5
Author: Magnus Lindholm <linmag7@gmail.com>

sparc64: add M3000 Oberon PCIe support

This patch adds support for the Fujitsu M3000 Oberon PCIe bridge. It handles
Oberon enumeration, flushes DVMA translations, and routes legacy interrupts
using the bridge's ten-bit target ID. MSI queues are left uninitialized so
the existing architecture path uses legacy interrupts.

> diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c
> index 7613ab0ffb89d..9132f0535ce43 100644
> --- a/arch/sparc/kernel/iommu.c
> +++ b/arch/sparc/kernel/iommu.c
[ ... ]
> @@ -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);
> +	}

[Severity: High]
Does this code inadvertently leave valid DMA translations in the hardware page
table for non-Oberon platforms?

When freeing a coherent DMA mapping here, the physical pages are subsequently
released back to the system via free_pages(). By explicitly wrapping the loop
that clears the IOMMU TSB entries with iopte_make_dummy() inside the
if (iommu->iommu_cache_flush) check, non-Oberon hardware skips this step.

This could allow a rogue or delayed DMA from the device to silently corrupt
newly allocated memory. Other unmap paths, like dma_4u_unmap_phys(), appear to
clear TSB entries for all platforms without this restriction.

>  	iommu_tbl_range_free(&iommu->tbl, dvma, npages, IOMMU_ERROR_CODE);
>  
>  	order = get_order(size);
[ ... ]
> @@ -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);
>  			s->dma_length = 0;
>  		}
>  		if (s == outs)
>  			break;
>  	}
>  	spin_unlock_irqrestore(&iommu->lock, flags);
>  
>  	return -EINVAL;
>  }

[Severity: Medium]
Does this error cleanup path leak the IOMMU context?

Earlier in dma_4u_map_sg(), an IOMMU context is allocated via
iommu_alloc_ctx(). If iommu_tbl_range_alloc() fails for any segment,
execution jumps to the iommu_map_failed label.

The cleanup logic unmaps the previously mapped ranges and releases the
spinlock, but it does not seem to call iommu_free_ctx() to release the
context ID. This could potentially cause a resource leak, leading to IOMMU
mapping failures once all contexts are exhausted.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261012184826.986050-1-linmag7@gmail.com?part=7

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 4/8] sparc64: stop IRQ setup when descriptor allocation fails
  2026-10-12 18:48 ` [PATCH v3 4/8] sparc64: stop IRQ setup when descriptor allocation fails Magnus Lindholm
@ 2026-10-12 19:03   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-12 19:03 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.
> 
> Signed-off-by: Magnus Lindholm <linmag7@gmail.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261012184826.986050-1-linmag7@gmail.com?part=4


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 6/8] sparc64: add SPARC64 VII CPU, MMU and SMP support
  2026-10-12 18:48 ` [PATCH v3 6/8] sparc64: add SPARC64 VII CPU, MMU and SMP support Magnus Lindholm
@ 2026-10-12 19:03   ` sashiko-bot
  0 siblings, 0 replies; 18+ messages in thread
From: sashiko-bot @ 2026-10-12 19:03 UTC (permalink / raw)
  To: Magnus Lindholm; +Cc: linux-serial

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] sparc64: missing membar #Sync in VII __flush_tlb_mm and xcall_flush_tlb_mm
- [High] sparc64: off-by-one error in VII TLB slot verification loop causes false panic
--

commit 83e75cdd0104cea9f29069ae3640894a08ee2f5f
Author: Magnus Lindholm <linmag7@gmail.com>
Subject: sparc64: add SPARC64 VII CPU, MMU and SMP support

This commit adds support for SPARC64 VII CPU, MMU, and SMP hardware paths.
It handles topology, interrupt dispatch, restrained ECC handling, and
synchronizes context writes and instruction demaps for supported firmware
modes.

Link: https://lore.kernel.org/oe-kbuild-all/202610121549.5ejZQ08o-lkp@intel.com/

> diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
> --- a/arch/sparc/mm/init_64.c
> +++ b/arch/sparc/mm/init_64.c
[ ... ]
> @@ -675,6 +675,50 @@ static unsigned long kern_large_tte(unsigned long paddr);
>  
> +/*
> + * Reserve two 4MB kernel mappings and an adjacent slot 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 = 15 - num_kernel_image_mappings; i <= 15; i++) {
> +		unsigned long d = cheetah_get_ldtlb_data(i);

[Severity: High]
Does this loop bounds check evaluate an extra unallocated slot?

Since remap_kernel() exclusively populates slots tlb_ent - i, the kernel
only uses slots 14 and 15 when num_kernel_image_mappings is 2. However,
evaluating 15 - num_kernel_image_mappings results in iterating through
slots 13, 14, and 15.

If the firmware legitimately uses slot 13 for its own translations, will
this false conflict cause the kernel to incorrectly panic and halt on boot?

> diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S
> --- a/arch/sparc/mm/ultra.S
> +++ b/arch/sparc/mm/ultra.S
[ ... ]
> +	.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

[Severity: Critical]
Does patching __flush_tlb_mm with __cheetah_flush_tlb_mm violate the VII
hardware synchronization requirements?

The commit message notes that VII requires a membar #Sync to safely
synchronize context writes and instruction demaps, but __cheetah_flush_tlb_mm
lacks this barrier after restoring ASI_DMMU.

Could this omission cause the hardware to execute the demap against the
wrong MMU context, leading to silent TLB corruption during process address
space teardowns like exit() or large munmap() operations?

[ ... ]
> +	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
> +
> +	/* VII uses 32-entry fTLBs: do not use the Spitfire 64-slot sweep. */
> +	sethi		%hi(xcall_flush_tlb_kernel_range), %o0

[Severity: Critical]
Is xcall_flush_tlb_mm unintentionally omitted from being patched here?

Failing to patch xcall_flush_tlb_mm causes SMP cross-CPU MM flushes on VII
to fall back to the generic Spitfire default.

Since the Spitfire default also lacks the necessary membar #Sync
synchronization before its DEMAP instructions, this appears to leave a path
for TLB corruption on SMP.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261012184826.986050-1-linmag7@gmail.com?part=6

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v3 8/8] hvc: add an M3000 firmware console backend
  2026-10-12 18:48 ` [PATCH v3 8/8] hvc: add an M3000 firmware console backend Magnus Lindholm
  2026-10-12 18:57   ` sashiko-bot
@ 2026-10-13 13:04   ` Greg KH
  1 sibling, 0 replies; 18+ messages in thread
From: Greg KH @ 2026-10-13 13:04 UTC (permalink / raw)
  To: Magnus Lindholm
  Cc: davem, andreas, sparclinux, linux-kernel, jirislaby, linux-serial,
	linuxppc-dev, krzk, oleg

On Mon, Oct 12, 2026 at 08:48:09PM +0200, Magnus Lindholm wrote:
> 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>
> ---
>  MAINTAINERS                 |   6 ++
>  drivers/tty/hvc/Kconfig     |  10 ++
>  drivers/tty/hvc/Makefile    |   1 +
>  drivers/tty/hvc/hvc_m3000.c | 202 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 219 insertions(+)
>  create mode 100644 drivers/tty/hvc/hvc_m3000.c

I'm guessing this will go through the sparc tree, so:

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

thanks,

greg k-h


^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2026-10-13 13:04 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-12 18:48 [PATCH v3 0/8] sparc64: add Fujitsu M3000 support Magnus Lindholm
2026-10-12 18:48 ` [PATCH v3 1/8] sparc64: return from the generic clear_page implementation Magnus Lindholm
2026-10-12 18:55   ` sashiko-bot
2026-10-12 18:48 ` [PATCH v3 2/8] sparc64: honor queued spinlock layout in secondary startup Magnus Lindholm
2026-10-12 18:55   ` sashiko-bot
2026-10-12 18:48 ` [PATCH v3 3/8] sparc64: avoid huge kernel PUD mappings on sun4u Magnus Lindholm
2026-10-12 18:55   ` sashiko-bot
2026-10-12 18:48 ` [PATCH v3 4/8] sparc64: stop IRQ setup when descriptor allocation fails Magnus Lindholm
2026-10-12 19:03   ` sashiko-bot
2026-10-12 18:48 ` [PATCH v3 5/8] sparc64: validate Fire register properties before initialization Magnus Lindholm
2026-10-12 18:55   ` sashiko-bot
2026-10-12 18:48 ` [PATCH v3 6/8] sparc64: add SPARC64 VII CPU, MMU and SMP support Magnus Lindholm
2026-10-12 19:03   ` sashiko-bot
2026-10-12 18:48 ` [PATCH v3 7/8] sparc64: add M3000 Oberon PCIe support Magnus Lindholm
2026-10-12 19:01   ` sashiko-bot
2026-10-12 18:48 ` [PATCH v3 8/8] hvc: add an M3000 firmware console backend Magnus Lindholm
2026-10-12 18:57   ` sashiko-bot
2026-10-13 13:04   ` Greg KH

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.