diff options
author | David S. Miller <davem@davemloft.net> | 2013-09-21 00:50:41 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-11-12 18:22:34 -0500 |
commit | b2d438348024b75a1ee8b66b85d77f569a5dfed8 (patch) | |
tree | 057c725d9d058d326533d0947aedd226adb57540 /arch/sparc | |
parent | f998c9c0d663b013e3aa3ba78908396c8c497218 (diff) |
sparc64: Make PAGE_OFFSET variable.
Choose PAGE_OFFSET dynamically based upon cpu type.
Original UltraSPARC-I (spitfire) chips only supported a 44-bit
virtual address space.
Newer chips (T4 and later) support 52-bit virtual addresses
and up to 47-bits of physical memory space.
Therefore we have to adjust PAGE_SIZE dynamically based upon
the capabilities of the chip.
Note that this change alone does not allow us to support > 43-bit
physical memory, to do that we need to re-arrange our page table
support. The current encodings of the pmd_t and pgd_t pointers
restricts us to "32 + 11" == 43 bits.
This change can waste quite a bit of memory for the various tables.
In particular, a future change should work to size and allocate
kern_linear_bitmap[] and sparc64_valid_addr_bitmap[] dynamically.
This isn't easy as we really cannot take a TLB miss when accessing
kern_linear_bitmap[]. We'd have to lock it into the TLB or similar.
Signed-off-by: David S. Miller <davem@davemloft.net>
Acked-by: Bob Picco <bob.picco@oracle.com>
Diffstat (limited to 'arch/sparc')
-rw-r--r-- | arch/sparc/include/asm/page_64.h | 20 | ||||
-rw-r--r-- | arch/sparc/kernel/ktlb.S | 30 | ||||
-rw-r--r-- | arch/sparc/kernel/vmlinux.lds.S | 5 | ||||
-rw-r--r-- | arch/sparc/lib/clear_page.S | 4 | ||||
-rw-r--r-- | arch/sparc/lib/copy_page.S | 4 | ||||
-rw-r--r-- | arch/sparc/mm/init_64.c | 92 | ||||
-rw-r--r-- | arch/sparc/mm/ultra.S | 12 |
7 files changed, 138 insertions, 29 deletions
diff --git a/arch/sparc/include/asm/page_64.h b/arch/sparc/include/asm/page_64.h index 978ea6d022e9..89e07fd0ac88 100644 --- a/arch/sparc/include/asm/page_64.h +++ b/arch/sparc/include/asm/page_64.h | |||
@@ -112,24 +112,16 @@ typedef pte_t *pgtable_t; | |||
112 | 112 | ||
113 | #include <asm-generic/memory_model.h> | 113 | #include <asm-generic/memory_model.h> |
114 | 114 | ||
115 | #endif /* !(__ASSEMBLY__) */ | ||
116 | |||
117 | /* We used to stick this into a hard-coded global register (%g4) | ||
118 | * but that does not make sense anymore. | ||
119 | */ | ||
120 | #define MAX_SUPPORTED_PA_BITS 43 | ||
121 | #define PAGE_OFFSET_BY_BITS(X) (-(_AC(1,UL) << (X))) | 115 | #define PAGE_OFFSET_BY_BITS(X) (-(_AC(1,UL) << (X))) |
122 | #define PAGE_OFFSET PAGE_OFFSET_BY_BITS(MAX_SUPPORTED_PA_BITS) | 116 | extern unsigned long PAGE_OFFSET; |
123 | 117 | ||
124 | /* The "virtual" portion of PAGE_OFFSET, used to clip off the non-physical | 118 | #endif /* !(__ASSEMBLY__) */ |
125 | * bits of a linear kernel address. | ||
126 | */ | ||
127 | #define PAGE_OFFSET_VA_BITS (64 - MAX_SUPPORTED_PA_BITS) | ||
128 | 119 | ||
129 | /* The actual number of physical memory address bits we support, this is | 120 | /* The maximum number of physical memory address bits we support, this |
130 | * used to size various tables used to manage kernel TLB misses. | 121 | * is used to size various tables used to manage kernel TLB misses and |
122 | * also the sparsemem code. | ||
131 | */ | 123 | */ |
132 | #define MAX_PHYS_ADDRESS_BITS 41 | 124 | #define MAX_PHYS_ADDRESS_BITS 47 |
133 | 125 | ||
134 | /* These two shift counts are used when indexing sparc64_valid_addr_bitmap | 126 | /* These two shift counts are used when indexing sparc64_valid_addr_bitmap |
135 | * and kpte_linear_bitmap. | 127 | * and kpte_linear_bitmap. |
diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S index 7ad46bc0c698..542e96ac4d39 100644 --- a/arch/sparc/kernel/ktlb.S +++ b/arch/sparc/kernel/ktlb.S | |||
@@ -153,12 +153,19 @@ kvmap_dtlb_tsb4m_miss: | |||
153 | /* Clear the PAGE_OFFSET top virtual bits, shift | 153 | /* Clear the PAGE_OFFSET top virtual bits, shift |
154 | * down to get PFN, and make sure PFN is in range. | 154 | * down to get PFN, and make sure PFN is in range. |
155 | */ | 155 | */ |
156 | sllx %g4, PAGE_OFFSET_VA_BITS, %g5 | 156 | 661: sllx %g4, 0, %g5 |
157 | .section .page_offset_shift_patch, "ax" | ||
158 | .word 661b | ||
159 | .previous | ||
157 | 160 | ||
158 | /* Check to see if we know about valid memory at the 4MB | 161 | /* Check to see if we know about valid memory at the 4MB |
159 | * chunk this physical address will reside within. | 162 | * chunk this physical address will reside within. |
160 | */ | 163 | */ |
161 | srlx %g5, PAGE_OFFSET_VA_BITS + MAX_PHYS_ADDRESS_BITS, %g2 | 164 | 661: srlx %g5, MAX_PHYS_ADDRESS_BITS, %g2 |
165 | .section .page_offset_shift_patch, "ax" | ||
166 | .word 661b | ||
167 | .previous | ||
168 | |||
162 | brnz,pn %g2, kvmap_dtlb_longpath | 169 | brnz,pn %g2, kvmap_dtlb_longpath |
163 | nop | 170 | nop |
164 | 171 | ||
@@ -176,7 +183,11 @@ valid_addr_bitmap_patch: | |||
176 | or %g7, %lo(sparc64_valid_addr_bitmap), %g7 | 183 | or %g7, %lo(sparc64_valid_addr_bitmap), %g7 |
177 | .previous | 184 | .previous |
178 | 185 | ||
179 | srlx %g5, PAGE_OFFSET_VA_BITS + ILOG2_4MB, %g2 | 186 | 661: srlx %g5, ILOG2_4MB, %g2 |
187 | .section .page_offset_shift_patch, "ax" | ||
188 | .word 661b | ||
189 | .previous | ||
190 | |||
180 | srlx %g2, 6, %g5 | 191 | srlx %g2, 6, %g5 |
181 | and %g2, 63, %g2 | 192 | and %g2, 63, %g2 |
182 | sllx %g5, 3, %g5 | 193 | sllx %g5, 3, %g5 |
@@ -189,9 +200,18 @@ valid_addr_bitmap_patch: | |||
189 | 2: sethi %hi(kpte_linear_bitmap), %g2 | 200 | 2: sethi %hi(kpte_linear_bitmap), %g2 |
190 | 201 | ||
191 | /* Get the 256MB physical address index. */ | 202 | /* Get the 256MB physical address index. */ |
192 | sllx %g4, PAGE_OFFSET_VA_BITS, %g5 | 203 | 661: sllx %g4, 0, %g5 |
204 | .section .page_offset_shift_patch, "ax" | ||
205 | .word 661b | ||
206 | .previous | ||
207 | |||
193 | or %g2, %lo(kpte_linear_bitmap), %g2 | 208 | or %g2, %lo(kpte_linear_bitmap), %g2 |
194 | srlx %g5, PAGE_OFFSET_VA_BITS + ILOG2_256MB, %g5 | 209 | |
210 | 661: srlx %g5, ILOG2_256MB, %g5 | ||
211 | .section .page_offset_shift_patch, "ax" | ||
212 | .word 661b | ||
213 | .previous | ||
214 | |||
195 | and %g5, (32 - 1), %g7 | 215 | and %g5, (32 - 1), %g7 |
196 | 216 | ||
197 | /* Divide by 32 to get the offset into the bitmask. */ | 217 | /* Divide by 32 to get the offset into the bitmask. */ |
diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S index 0bacceb19150..932ff90fd760 100644 --- a/arch/sparc/kernel/vmlinux.lds.S +++ b/arch/sparc/kernel/vmlinux.lds.S | |||
@@ -122,6 +122,11 @@ SECTIONS | |||
122 | *(.swapper_4m_tsb_phys_patch) | 122 | *(.swapper_4m_tsb_phys_patch) |
123 | __swapper_4m_tsb_phys_patch_end = .; | 123 | __swapper_4m_tsb_phys_patch_end = .; |
124 | } | 124 | } |
125 | .page_offset_shift_patch : { | ||
126 | __page_offset_shift_patch = .; | ||
127 | *(.page_offset_shift_patch) | ||
128 | __page_offset_shift_patch_end = .; | ||
129 | } | ||
125 | .popc_3insn_patch : { | 130 | .popc_3insn_patch : { |
126 | __popc_3insn_patch = .; | 131 | __popc_3insn_patch = .; |
127 | *(.popc_3insn_patch) | 132 | *(.popc_3insn_patch) |
diff --git a/arch/sparc/lib/clear_page.S b/arch/sparc/lib/clear_page.S index 77e531f6c2a7..46272dfc26e8 100644 --- a/arch/sparc/lib/clear_page.S +++ b/arch/sparc/lib/clear_page.S | |||
@@ -37,10 +37,10 @@ _clear_page: /* %o0=dest */ | |||
37 | .globl clear_user_page | 37 | .globl clear_user_page |
38 | clear_user_page: /* %o0=dest, %o1=vaddr */ | 38 | clear_user_page: /* %o0=dest, %o1=vaddr */ |
39 | lduw [%g6 + TI_PRE_COUNT], %o2 | 39 | lduw [%g6 + TI_PRE_COUNT], %o2 |
40 | sethi %uhi(PAGE_OFFSET), %g2 | 40 | sethi %hi(PAGE_OFFSET), %g2 |
41 | sethi %hi(PAGE_SIZE), %o4 | 41 | sethi %hi(PAGE_SIZE), %o4 |
42 | 42 | ||
43 | sllx %g2, 32, %g2 | 43 | ldx [%g2 + %lo(PAGE_OFFSET)], %g2 |
44 | sethi %hi(PAGE_KERNEL_LOCKED), %g3 | 44 | sethi %hi(PAGE_KERNEL_LOCKED), %g3 |
45 | 45 | ||
46 | ldx [%g3 + %lo(PAGE_KERNEL_LOCKED)], %g3 | 46 | ldx [%g3 + %lo(PAGE_KERNEL_LOCKED)], %g3 |
diff --git a/arch/sparc/lib/copy_page.S b/arch/sparc/lib/copy_page.S index 4d2df328e514..dd16c61f3263 100644 --- a/arch/sparc/lib/copy_page.S +++ b/arch/sparc/lib/copy_page.S | |||
@@ -46,10 +46,10 @@ | |||
46 | .type copy_user_page,#function | 46 | .type copy_user_page,#function |
47 | copy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */ | 47 | copy_user_page: /* %o0=dest, %o1=src, %o2=vaddr */ |
48 | lduw [%g6 + TI_PRE_COUNT], %o4 | 48 | lduw [%g6 + TI_PRE_COUNT], %o4 |
49 | sethi %uhi(PAGE_OFFSET), %g2 | 49 | sethi %hi(PAGE_OFFSET), %g2 |
50 | sethi %hi(PAGE_SIZE), %o3 | 50 | sethi %hi(PAGE_SIZE), %o3 |
51 | 51 | ||
52 | sllx %g2, 32, %g2 | 52 | ldx [%g2 + %lo(PAGE_OFFSET)], %g2 |
53 | sethi %hi(PAGE_KERNEL_LOCKED), %g3 | 53 | sethi %hi(PAGE_KERNEL_LOCKED), %g3 |
54 | 54 | ||
55 | ldx [%g3 + %lo(PAGE_KERNEL_LOCKED)], %g3 | 55 | ldx [%g3 + %lo(PAGE_KERNEL_LOCKED)], %g3 |
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c index e295a5b30ac9..7a97b5a28b4b 100644 --- a/arch/sparc/mm/init_64.c +++ b/arch/sparc/mm/init_64.c | |||
@@ -1557,6 +1557,96 @@ unsigned long __init find_ecache_flush_span(unsigned long size) | |||
1557 | return ~0UL; | 1557 | return ~0UL; |
1558 | } | 1558 | } |
1559 | 1559 | ||
1560 | unsigned long PAGE_OFFSET; | ||
1561 | EXPORT_SYMBOL(PAGE_OFFSET); | ||
1562 | |||
1563 | static void __init page_offset_shift_patch_one(unsigned int *insn, unsigned long phys_bits) | ||
1564 | { | ||
1565 | unsigned long final_shift; | ||
1566 | unsigned int val = *insn; | ||
1567 | unsigned int cnt; | ||
1568 | |||
1569 | /* We are patching in ilog2(max_supported_phys_address), and | ||
1570 | * we are doing so in a manner similar to a relocation addend. | ||
1571 | * That is, we are adding the shift value to whatever value | ||
1572 | * is in the shift instruction count field already. | ||
1573 | */ | ||
1574 | cnt = (val & 0x3f); | ||
1575 | val &= ~0x3f; | ||
1576 | |||
1577 | /* If we are trying to shift >= 64 bits, clear the destination | ||
1578 | * register. This can happen when phys_bits ends up being equal | ||
1579 | * to MAX_PHYS_ADDRESS_BITS. | ||
1580 | */ | ||
1581 | final_shift = (cnt + (64 - phys_bits)); | ||
1582 | if (final_shift >= 64) { | ||
1583 | unsigned int rd = (val >> 25) & 0x1f; | ||
1584 | |||
1585 | val = 0x80100000 | (rd << 25); | ||
1586 | } else { | ||
1587 | val |= final_shift; | ||
1588 | } | ||
1589 | *insn = val; | ||
1590 | |||
1591 | __asm__ __volatile__("flush %0" | ||
1592 | : /* no outputs */ | ||
1593 | : "r" (insn)); | ||
1594 | } | ||
1595 | |||
1596 | static void __init page_offset_shift_patch(unsigned long phys_bits) | ||
1597 | { | ||
1598 | extern unsigned int __page_offset_shift_patch; | ||
1599 | extern unsigned int __page_offset_shift_patch_end; | ||
1600 | unsigned int *p; | ||
1601 | |||
1602 | p = &__page_offset_shift_patch; | ||
1603 | while (p < &__page_offset_shift_patch_end) { | ||
1604 | unsigned int *insn = (unsigned int *)(unsigned long)*p; | ||
1605 | |||
1606 | page_offset_shift_patch_one(insn, phys_bits); | ||
1607 | |||
1608 | p++; | ||
1609 | } | ||
1610 | } | ||
1611 | |||
1612 | static void __init setup_page_offset(void) | ||
1613 | { | ||
1614 | unsigned long max_phys_bits = 40; | ||
1615 | |||
1616 | if (tlb_type == cheetah || tlb_type == cheetah_plus) { | ||
1617 | max_phys_bits = 42; | ||
1618 | } else if (tlb_type == hypervisor) { | ||
1619 | switch (sun4v_chip_type) { | ||
1620 | case SUN4V_CHIP_NIAGARA1: | ||
1621 | case SUN4V_CHIP_NIAGARA2: | ||
1622 | max_phys_bits = 39; | ||
1623 | break; | ||
1624 | case SUN4V_CHIP_NIAGARA3: | ||
1625 | max_phys_bits = 43; | ||
1626 | break; | ||
1627 | case SUN4V_CHIP_NIAGARA4: | ||
1628 | case SUN4V_CHIP_NIAGARA5: | ||
1629 | case SUN4V_CHIP_SPARC64X: | ||
1630 | default: | ||
1631 | max_phys_bits = 47; | ||
1632 | break; | ||
1633 | } | ||
1634 | } | ||
1635 | |||
1636 | if (max_phys_bits > MAX_PHYS_ADDRESS_BITS) { | ||
1637 | prom_printf("MAX_PHYS_ADDRESS_BITS is too small, need %lu\n", | ||
1638 | max_phys_bits); | ||
1639 | prom_halt(); | ||
1640 | } | ||
1641 | |||
1642 | PAGE_OFFSET = PAGE_OFFSET_BY_BITS(max_phys_bits); | ||
1643 | |||
1644 | pr_info("PAGE_OFFSET is 0x%016lx (max_phys_bits == %lu)\n", | ||
1645 | PAGE_OFFSET, max_phys_bits); | ||
1646 | |||
1647 | page_offset_shift_patch(max_phys_bits); | ||
1648 | } | ||
1649 | |||
1560 | static void __init tsb_phys_patch(void) | 1650 | static void __init tsb_phys_patch(void) |
1561 | { | 1651 | { |
1562 | struct tsb_ldquad_phys_patch_entry *pquad; | 1652 | struct tsb_ldquad_phys_patch_entry *pquad; |
@@ -1763,6 +1853,8 @@ void __init paging_init(void) | |||
1763 | unsigned long real_end, i; | 1853 | unsigned long real_end, i; |
1764 | int node; | 1854 | int node; |
1765 | 1855 | ||
1856 | setup_page_offset(); | ||
1857 | |||
1766 | /* These build time checkes make sure that the dcache_dirty_cpu() | 1858 | /* These build time checkes make sure that the dcache_dirty_cpu() |
1767 | * page->flags usage will work. | 1859 | * page->flags usage will work. |
1768 | * | 1860 | * |
diff --git a/arch/sparc/mm/ultra.S b/arch/sparc/mm/ultra.S index 432aa0cb1b38..b4f4733abc6e 100644 --- a/arch/sparc/mm/ultra.S +++ b/arch/sparc/mm/ultra.S | |||
@@ -153,10 +153,10 @@ __spitfire_flush_tlb_mm_slow: | |||
153 | .globl __flush_icache_page | 153 | .globl __flush_icache_page |
154 | __flush_icache_page: /* %o0 = phys_page */ | 154 | __flush_icache_page: /* %o0 = phys_page */ |
155 | srlx %o0, PAGE_SHIFT, %o0 | 155 | srlx %o0, PAGE_SHIFT, %o0 |
156 | sethi %uhi(PAGE_OFFSET), %g1 | 156 | sethi %hi(PAGE_OFFSET), %g1 |
157 | sllx %o0, PAGE_SHIFT, %o0 | 157 | sllx %o0, PAGE_SHIFT, %o0 |
158 | sethi %hi(PAGE_SIZE), %g2 | 158 | sethi %hi(PAGE_SIZE), %g2 |
159 | sllx %g1, 32, %g1 | 159 | ldx [%g1 + %lo(PAGE_OFFSET)], %g1 |
160 | add %o0, %g1, %o0 | 160 | add %o0, %g1, %o0 |
161 | 1: subcc %g2, 32, %g2 | 161 | 1: subcc %g2, 32, %g2 |
162 | bne,pt %icc, 1b | 162 | bne,pt %icc, 1b |
@@ -178,8 +178,8 @@ __flush_icache_page: /* %o0 = phys_page */ | |||
178 | .align 64 | 178 | .align 64 |
179 | .globl __flush_dcache_page | 179 | .globl __flush_dcache_page |
180 | __flush_dcache_page: /* %o0=kaddr, %o1=flush_icache */ | 180 | __flush_dcache_page: /* %o0=kaddr, %o1=flush_icache */ |
181 | sethi %uhi(PAGE_OFFSET), %g1 | 181 | sethi %hi(PAGE_OFFSET), %g1 |
182 | sllx %g1, 32, %g1 | 182 | ldx [%g1 + %lo(PAGE_OFFSET)], %g1 |
183 | sub %o0, %g1, %o0 ! physical address | 183 | sub %o0, %g1, %o0 ! physical address |
184 | srlx %o0, 11, %o0 ! make D-cache TAG | 184 | srlx %o0, 11, %o0 ! make D-cache TAG |
185 | sethi %hi(1 << 14), %o2 ! D-cache size | 185 | sethi %hi(1 << 14), %o2 ! D-cache size |
@@ -287,8 +287,8 @@ __cheetah_flush_tlb_pending: /* 27 insns */ | |||
287 | 287 | ||
288 | #ifdef DCACHE_ALIASING_POSSIBLE | 288 | #ifdef DCACHE_ALIASING_POSSIBLE |
289 | __cheetah_flush_dcache_page: /* 11 insns */ | 289 | __cheetah_flush_dcache_page: /* 11 insns */ |
290 | sethi %uhi(PAGE_OFFSET), %g1 | 290 | sethi %hi(PAGE_OFFSET), %g1 |
291 | sllx %g1, 32, %g1 | 291 | ldx [%g1 + %lo(PAGE_OFFSET)], %g1 |
292 | sub %o0, %g1, %o0 | 292 | sub %o0, %g1, %o0 |
293 | sethi %hi(PAGE_SIZE), %o4 | 293 | sethi %hi(PAGE_SIZE), %o4 |
294 | 1: subcc %o4, (1 << 5), %o4 | 294 | 1: subcc %o4, (1 << 5), %o4 |