From: dpim <[email protected]>

sun4v_tte_to_sun4u() keeps only 2 bits of the incoming TTE page-size
field, while TTE_PGSIZE_UA2005() in the same header defines it as 3.
Sizes 32M (4) and 256M (5) therefore alias down to 64K (1) and 512K
(2): a guest asking for a large page gets a TLB entry covering a
fraction of it, and faults on the first access past that.

Keep all three bits, in the two pieces the hardware uses for them:

    bit 62:61   Size<1:0>
    bit 48      Size<2>

That is the sun4u encoding introduced when 32M and 256M pages were
added, so it is where the field belongs in this word rather than in an
arbitrary spare bit:

  Panther Implementation Supplement (SPARC V9 JPS1 Implementation
  Supplement: Sun UltraSPARC Panther -- UltraSPARC IV+), Preliminary
  Draft 17 Mar 2006, TABLE F-1-4 "TTE Data Field Description", p.337:
    Size<1:0>  "Bit <62:61> represent the least significant 2 bits of
                the page size" -- 000=8K 001=64K 010=512K 011=4M
                100=32M 101=256M
    Size<2>    "Bit 48 is the most significant bit of the page size and
                is concatenated with bits <62:61>"
  
https://www.oracle.com/technetwork/server-storage/sun-sparc-enterprise/documentation/sparc4plus-usersmanual-2516673.pdf

UltraSPARC T1 keeps bit 48 for the same purpose in its sun4u-format
TTE, where the halves are called szl and szh:

  UltraSPARC T1 Supplement to the UltraSPARC Architecture 2005,
  Draft D2.1, sec. 13.1.2, TABLE 13-1, p.182
  https://www.oracle.com/docs/tech/systems/t1-09-ust1-uasuppl-draft-hp-ext.pdf

TTE_PGSIZE() is shared with the sun4u path, which is fine in both
directions. Parts that predate the extension -- UltraSPARC III,
SPARC64 V -- have a 2-bit size field and bit 48 reserved, reading as
zero:

  JPS1 Commonality, Working Draft 1.0.5, TABLE F-1, p.441:
    Data<49:47>  "Reserved, read as 0" (bit 48 is part of this range)
  
https://www.oracle.com/technetwork/server-storage/sun-sparc-enterprise/documentation/joint-program-spec1-2516675.pdf

so they keep decoding the same four sizes as before; parts that do
implement Size<2> now get 32M and 256M decoded correctly.

Callers computing 8192ULL << 3*TTE_PGSIZE(tte) then get 32M and 256M
for free. Two related fixes come with it:

  - demap_tlb() built its mask from the raw bits ((tte>>61)&3) rather
    than the accessor, so large-page demaps truncated the same way.
    It now uses TTE_PGSIZE().

  - dump_mmu() gains 32M/256M labels.

A QEMU_BUILD_BUG_ON guards bit 48 against a future overlapping TTE_*
field.

Tested with rebuilt machine-description blobs -- 1up-md.bin and
1up-hv.bin, two of the six files hw/sparc64/niagara.c loads. These are
generated with mdgen, not the pair shipped in the OpenSPARC T1
archive, whose MD does not declare the property at all: the cpu node
here sets mmu-page-size-list = 0x2b, so the guest actually selects
TTE256M. kmem64_szc = 0x5 confirmed under Solaris kmdb guest at a
breakpoint on alloc_kmem64(), and "info tlb" -- whose 32M/256M labels
this same patch adds to dump_mmu() -- shows a live 256M entry at the
matching kmem64_base address, e.g.:

  [57] VA: 70000100000, PA: 150000000, 256M, priv, RW, unlocked, ie no, ctx 0 
local

confirming the kernel's kmem64 mapping is backed by an actual 256M
TLB entry, not a 512K one truncated from the same bits.

Solaris 10 3/05 HW2, 10u8, 10u10, 10u11, OpenSolaris snv_134, Solaris
11 Express 2010.11, Solaris 11 11/11 and Solaris 11.1 boot at 4GB and
8GB; the same configurations trapped or hung in early kernel VM setup
before the fix.

Link: https://unix0cc.github.io/md/
AI-used-for: code, research, and result analysis.
Signed-off-by: dpim <[email protected]>
---
 target/sparc/cpu.h         | 38 +++++++++++++++++++++++++++++++++++++-
 target/sparc/ldst_helper.c | 18 ++++++++++++++++--
 target/sparc/mmu_helper.c  | 12 ++++++++++++
 3 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
index 5f583ed9de..17d1308101 100644
--- a/target/sparc/cpu.h
+++ b/target/sparc/cpu.h
@@ -307,10 +307,46 @@ enum {
 #define TTE_SET_USED(tte)   ((tte) |= TTE_USED_BIT)
 #define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT)
 
-#define TTE_PGSIZE(tte)     (((tte) >> 61) & 3ULL)
 #define TTE_PGSIZE_UA2005(tte)     ((tte) & 7ULL)
 #define TTE_PA(tte)         ((tte) & 0x1ffffffe000ULL)
 
+/*
+ * Internal QEMU TLB-entry page-size field: Size<1:0> at bits 62-61 plus
+ * Size<2> at bit 48, encoding the six sizes 8K/64K/512K/4M/32M/256M.
+ *
+ * That split is the sun4u encoding introduced when 32M and 256M pages were
+ * added, and it is the layout this internal word models:
+ *
+ *   Panther Implementation Supplement (SPARC V9 JPS1 Implementation
+ *   Supplement: Sun UltraSPARC Panther -- UltraSPARC IV+), Preliminary
+ *   Draft 17 Mar 2006, TABLE F-1-4 "TTE Data Field Description", p.337:
+ *     Size<1:0>  "Bit <62:61> represent the least significant 2 bits of
+ *                 the page size" -- 000=8K 001=64K 010=512K 011=4M
+ *                 100=32M 101=256M
+ *     Size<2>    "Bit 48 is the most significant bit of the page size and
+ *                 is concatenated with bits <62:61>"
+ *
+ * UltraSPARC T1 keeps bit 48 for the same purpose in its sun4u-format TTE,
+ * where the halves are called szl and szh (UltraSPARC T1 Supplement to the
+ * UltraSPARC Architecture 2005, Draft D2.1, TABLE 13-1, p.182).
+ *
+ * Earlier JPS1 parts -- UltraSPARC III, SPARC64 V -- have a 2-bit size
+ * field with bit 48 reserved, reading as zero (JPS1 Commonality, Working
+ * Draft 1.0.5, TABLE F-1, p.441), so they keep decoding the same four
+ * sizes as before.
+ *
+ * Only the low two bits used to be kept here, which truncated 32M/256M.
+ *
+ * The QEMU_BUILD_BUG_ON below fails the build if any other TTE_* field is
+ * ever defined overlapping bit 48, so a future field can never silently
+ * corrupt page-size decoding at runtime.
+ */
+#define TTE_PGSIZE_HI_BIT   (1ULL << 48)
+QEMU_BUILD_BUG_ON((TTE_PGSIZE_HI_BIT & (TTE_VALID_BIT | TTE_NFO_BIT |
+                                        TTE_USED_BIT | TTE_PA(~0ULL))) != 0);
+#define TTE_PGSIZE(tte)     ((((tte) >> 61) & 3ULL) | \
+                             (((tte) & TTE_PGSIZE_HI_BIT) >> 46))
+
 /* UltraSPARC T1 specific */
 #define TLB_UST1_IS_REAL_BIT   (1ULL << 9)  /* Real translation entry */
 #define TLB_UST1_IS_SUN4V_BIT  (1ULL << 10) /* sun4u/sun4v TTE format switch */
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c
index 4ec8799d1f..43a7ab6b09 100644
--- a/target/sparc/ldst_helper.c
+++ b/target/sparc/ldst_helper.c
@@ -186,7 +186,7 @@ static void demap_tlb(SparcTLBEntry *tlb, target_ulong 
demap_addr,
                 /* demap page
                    will remove any entry matching VA */
                 mask = 0xffffffffffffe000ULL;
-                mask <<= 3 * ((tlb[i].tte >> 61) & 3);
+                mask <<= 3 * TTE_PGSIZE(tlb[i].tte);
 
                 if (!compare_masked(demap_addr, tlb[i].tag, mask)) {
                     continue;
@@ -217,7 +217,21 @@ static uint64_t sun4v_tte_to_sun4u(CPUSPARCState *env, 
uint64_t tag,
         return sun4v_tte;
     }
     sun4u_tte = TTE_PA(sun4v_tte) | (sun4v_tte & TTE_VALID_BIT);
-    sun4u_tte |= (sun4v_tte & 3ULL) << 61; /* TTE_PGSIZE */
+    /*
+     * sun4v/UA2005 page size is a 3-bit field (TTE_PGSIZE_UA2005, values
+     * 0-5 = 8K/64K/512K/4M/32M/256M). Storing only the low 2 bits here (as
+     * this historically did) silently aliased 32M(4)/256M(5) down to
+     * 64K(1)/512K(2), so a guest asking for a large page got a real TLB
+     * entry covering a much smaller region. Preserve all three bits: the
+     * low two stay at 61-62, the high one goes to TTE_PGSIZE_HI_BIT
+     * (bit 48), which is where real UltraSPARC T1 keeps szh -- see the
+     * comment on TTE_PGSIZE_HI_BIT in cpu.h.
+     */
+    {
+        uint64_t pgsz = TTE_PGSIZE_UA2005(sun4v_tte);
+        sun4u_tte |= (pgsz & 3ULL) << 61;                 /* low 2 bits: 61-62 
*/
+        sun4u_tte |= (pgsz & 4ULL) ? TTE_PGSIZE_HI_BIT : 0; /* high bit */
+    }
     sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_NFO_BIT_UA2005, TTE_NFO_BIT);
     sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_USED_BIT_UA2005, TTE_USED_BIT);
     sun4u_tte |= CONVERT_BIT(sun4v_tte, TTE_W_OK_BIT_UA2005, TTE_W_OK_BIT);
diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
index 07ba25dfce..8544de097d 100644
--- a/target/sparc/mmu_helper.c
+++ b/target/sparc/mmu_helper.c
@@ -830,6 +830,12 @@ void dump_mmu(CPUSPARCState *env)
             case 0x3:
                 mask = "  4M";
                 break;
+            case 0x4:
+                mask = " 32M";
+                break;
+            case 0x5:
+                mask = "256M";
+                break;
             }
             if (TTE_IS_VALID(env->dtlb[i].tte)) {
                 qemu_printf("[%02u] VA: %" PRIx64 ", PA: %llx"
@@ -869,6 +875,12 @@ void dump_mmu(CPUSPARCState *env)
             case 0x3:
                 mask = "  4M";
                 break;
+            case 0x4:
+                mask = " 32M";
+                break;
+            case 0x5:
+                mask = "256M";
+                break;
             }
             if (TTE_IS_VALID(env->itlb[i].tte)) {
                 qemu_printf("[%02u] VA: %" PRIx64 ", PA: %llx"
-- 
2.43.0


Reply via email to