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