|
@@ -75,7 +75,6 @@ unsigned long kern_linear_pte_xor[4] __read_mostly;
|
|
|
* 'cpu' properties, but we need to have this table setup before the
|
|
|
* MDESC is initialized.
|
|
|
*/
|
|
|
-unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
|
|
|
|
|
|
#ifndef CONFIG_DEBUG_PAGEALLOC
|
|
|
/* A special kernel TSB for 4MB, 256MB, 2GB and 16GB linear mappings.
|
|
@@ -84,10 +83,11 @@ unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)];
|
|
|
*/
|
|
|
extern struct tsb swapper_4m_tsb[KERNEL_TSB4M_NENTRIES];
|
|
|
#endif
|
|
|
+extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
|
|
|
|
|
|
static unsigned long cpu_pgsz_mask;
|
|
|
|
|
|
-#define MAX_BANKS 32
|
|
|
+#define MAX_BANKS 1024
|
|
|
|
|
|
static struct linux_prom64_registers pavail[MAX_BANKS];
|
|
|
static int pavail_ents;
|
|
@@ -165,10 +165,6 @@ static void __init read_obp_memory(const char *property,
|
|
|
cmp_p64, NULL);
|
|
|
}
|
|
|
|
|
|
-unsigned long sparc64_valid_addr_bitmap[VALID_ADDR_BITMAP_BYTES /
|
|
|
- sizeof(unsigned long)];
|
|
|
-EXPORT_SYMBOL(sparc64_valid_addr_bitmap);
|
|
|
-
|
|
|
/* Kernel physical address base and size in bytes. */
|
|
|
unsigned long kern_base __read_mostly;
|
|
|
unsigned long kern_size __read_mostly;
|
|
@@ -840,7 +836,10 @@ static int find_node(unsigned long addr)
|
|
|
if ((addr & p->mask) == p->val)
|
|
|
return i;
|
|
|
}
|
|
|
- return -1;
|
|
|
+ /* The following condition has been observed on LDOM guests.*/
|
|
|
+ WARN_ONCE(1, "find_node: A physical address doesn't match a NUMA node"
|
|
|
+ " rule. Some physical memory will be owned by node 0.");
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static u64 memblock_nid_range(u64 start, u64 end, int *nid)
|
|
@@ -1366,9 +1365,144 @@ static unsigned long __init bootmem_init(unsigned long phys_base)
|
|
|
static struct linux_prom64_registers pall[MAX_BANKS] __initdata;
|
|
|
static int pall_ents __initdata;
|
|
|
|
|
|
-#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
|
+static unsigned long max_phys_bits = 40;
|
|
|
+
|
|
|
+bool kern_addr_valid(unsigned long addr)
|
|
|
+{
|
|
|
+ pgd_t *pgd;
|
|
|
+ pud_t *pud;
|
|
|
+ pmd_t *pmd;
|
|
|
+ pte_t *pte;
|
|
|
+
|
|
|
+ if ((long)addr < 0L) {
|
|
|
+ unsigned long pa = __pa(addr);
|
|
|
+
|
|
|
+ if ((addr >> max_phys_bits) != 0UL)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return pfn_valid(pa >> PAGE_SHIFT);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (addr >= (unsigned long) KERNBASE &&
|
|
|
+ addr < (unsigned long)&_end)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ pgd = pgd_offset_k(addr);
|
|
|
+ if (pgd_none(*pgd))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ pud = pud_offset(pgd, addr);
|
|
|
+ if (pud_none(*pud))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (pud_large(*pud))
|
|
|
+ return pfn_valid(pud_pfn(*pud));
|
|
|
+
|
|
|
+ pmd = pmd_offset(pud, addr);
|
|
|
+ if (pmd_none(*pmd))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (pmd_large(*pmd))
|
|
|
+ return pfn_valid(pmd_pfn(*pmd));
|
|
|
+
|
|
|
+ pte = pte_offset_kernel(pmd, addr);
|
|
|
+ if (pte_none(*pte))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return pfn_valid(pte_pfn(*pte));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(kern_addr_valid);
|
|
|
+
|
|
|
+static unsigned long __ref kernel_map_hugepud(unsigned long vstart,
|
|
|
+ unsigned long vend,
|
|
|
+ pud_t *pud)
|
|
|
+{
|
|
|
+ const unsigned long mask16gb = (1UL << 34) - 1UL;
|
|
|
+ u64 pte_val = vstart;
|
|
|
+
|
|
|
+ /* Each PUD is 8GB */
|
|
|
+ if ((vstart & mask16gb) ||
|
|
|
+ (vend - vstart <= mask16gb)) {
|
|
|
+ pte_val ^= kern_linear_pte_xor[2];
|
|
|
+ pud_val(*pud) = pte_val | _PAGE_PUD_HUGE;
|
|
|
+
|
|
|
+ return vstart + PUD_SIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ pte_val ^= kern_linear_pte_xor[3];
|
|
|
+ pte_val |= _PAGE_PUD_HUGE;
|
|
|
+
|
|
|
+ vend = vstart + mask16gb + 1UL;
|
|
|
+ while (vstart < vend) {
|
|
|
+ pud_val(*pud) = pte_val;
|
|
|
+
|
|
|
+ pte_val += PUD_SIZE;
|
|
|
+ vstart += PUD_SIZE;
|
|
|
+ pud++;
|
|
|
+ }
|
|
|
+ return vstart;
|
|
|
+}
|
|
|
+
|
|
|
+static bool kernel_can_map_hugepud(unsigned long vstart, unsigned long vend,
|
|
|
+ bool guard)
|
|
|
+{
|
|
|
+ if (guard && !(vstart & ~PUD_MASK) && (vend - vstart) >= PUD_SIZE)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static unsigned long __ref kernel_map_hugepmd(unsigned long vstart,
|
|
|
+ unsigned long vend,
|
|
|
+ pmd_t *pmd)
|
|
|
+{
|
|
|
+ const unsigned long mask256mb = (1UL << 28) - 1UL;
|
|
|
+ const unsigned long mask2gb = (1UL << 31) - 1UL;
|
|
|
+ u64 pte_val = vstart;
|
|
|
+
|
|
|
+ /* Each PMD is 8MB */
|
|
|
+ if ((vstart & mask256mb) ||
|
|
|
+ (vend - vstart <= mask256mb)) {
|
|
|
+ pte_val ^= kern_linear_pte_xor[0];
|
|
|
+ pmd_val(*pmd) = pte_val | _PAGE_PMD_HUGE;
|
|
|
+
|
|
|
+ return vstart + PMD_SIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((vstart & mask2gb) ||
|
|
|
+ (vend - vstart <= mask2gb)) {
|
|
|
+ pte_val ^= kern_linear_pte_xor[1];
|
|
|
+ pte_val |= _PAGE_PMD_HUGE;
|
|
|
+ vend = vstart + mask256mb + 1UL;
|
|
|
+ } else {
|
|
|
+ pte_val ^= kern_linear_pte_xor[2];
|
|
|
+ pte_val |= _PAGE_PMD_HUGE;
|
|
|
+ vend = vstart + mask2gb + 1UL;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (vstart < vend) {
|
|
|
+ pmd_val(*pmd) = pte_val;
|
|
|
+
|
|
|
+ pte_val += PMD_SIZE;
|
|
|
+ vstart += PMD_SIZE;
|
|
|
+ pmd++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return vstart;
|
|
|
+}
|
|
|
+
|
|
|
+static bool kernel_can_map_hugepmd(unsigned long vstart, unsigned long vend,
|
|
|
+ bool guard)
|
|
|
+{
|
|
|
+ if (guard && !(vstart & ~PMD_MASK) && (vend - vstart) >= PMD_SIZE)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
static unsigned long __ref kernel_map_range(unsigned long pstart,
|
|
|
- unsigned long pend, pgprot_t prot)
|
|
|
+ unsigned long pend, pgprot_t prot,
|
|
|
+ bool use_huge)
|
|
|
{
|
|
|
unsigned long vstart = PAGE_OFFSET + pstart;
|
|
|
unsigned long vend = PAGE_OFFSET + pend;
|
|
@@ -1387,19 +1521,34 @@ static unsigned long __ref kernel_map_range(unsigned long pstart,
|
|
|
pmd_t *pmd;
|
|
|
pte_t *pte;
|
|
|
|
|
|
+ if (pgd_none(*pgd)) {
|
|
|
+ pud_t *new;
|
|
|
+
|
|
|
+ new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
|
|
|
+ alloc_bytes += PAGE_SIZE;
|
|
|
+ pgd_populate(&init_mm, pgd, new);
|
|
|
+ }
|
|
|
pud = pud_offset(pgd, vstart);
|
|
|
if (pud_none(*pud)) {
|
|
|
pmd_t *new;
|
|
|
|
|
|
+ if (kernel_can_map_hugepud(vstart, vend, use_huge)) {
|
|
|
+ vstart = kernel_map_hugepud(vstart, vend, pud);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
|
|
|
alloc_bytes += PAGE_SIZE;
|
|
|
pud_populate(&init_mm, pud, new);
|
|
|
}
|
|
|
|
|
|
pmd = pmd_offset(pud, vstart);
|
|
|
- if (!pmd_present(*pmd)) {
|
|
|
+ if (pmd_none(*pmd)) {
|
|
|
pte_t *new;
|
|
|
|
|
|
+ if (kernel_can_map_hugepmd(vstart, vend, use_huge)) {
|
|
|
+ vstart = kernel_map_hugepmd(vstart, vend, pmd);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
|
|
|
alloc_bytes += PAGE_SIZE;
|
|
|
pmd_populate_kernel(&init_mm, pmd, new);
|
|
@@ -1422,100 +1571,34 @@ static unsigned long __ref kernel_map_range(unsigned long pstart,
|
|
|
return alloc_bytes;
|
|
|
}
|
|
|
|
|
|
-extern unsigned int kvmap_linear_patch[1];
|
|
|
-#endif /* CONFIG_DEBUG_PAGEALLOC */
|
|
|
-
|
|
|
-static void __init kpte_set_val(unsigned long index, unsigned long val)
|
|
|
-{
|
|
|
- unsigned long *ptr = kpte_linear_bitmap;
|
|
|
-
|
|
|
- val <<= ((index % (BITS_PER_LONG / 2)) * 2);
|
|
|
- ptr += (index / (BITS_PER_LONG / 2));
|
|
|
-
|
|
|
- *ptr |= val;
|
|
|
-}
|
|
|
-
|
|
|
-static const unsigned long kpte_shift_min = 28; /* 256MB */
|
|
|
-static const unsigned long kpte_shift_max = 34; /* 16GB */
|
|
|
-static const unsigned long kpte_shift_incr = 3;
|
|
|
-
|
|
|
-static unsigned long kpte_mark_using_shift(unsigned long start, unsigned long end,
|
|
|
- unsigned long shift)
|
|
|
+static void __init flush_all_kernel_tsbs(void)
|
|
|
{
|
|
|
- unsigned long size = (1UL << shift);
|
|
|
- unsigned long mask = (size - 1UL);
|
|
|
- unsigned long remains = end - start;
|
|
|
- unsigned long val;
|
|
|
-
|
|
|
- if (remains < size || (start & mask))
|
|
|
- return start;
|
|
|
-
|
|
|
- /* VAL maps:
|
|
|
- *
|
|
|
- * shift 28 --> kern_linear_pte_xor index 1
|
|
|
- * shift 31 --> kern_linear_pte_xor index 2
|
|
|
- * shift 34 --> kern_linear_pte_xor index 3
|
|
|
- */
|
|
|
- val = ((shift - kpte_shift_min) / kpte_shift_incr) + 1;
|
|
|
-
|
|
|
- remains &= ~mask;
|
|
|
- if (shift != kpte_shift_max)
|
|
|
- remains = size;
|
|
|
-
|
|
|
- while (remains) {
|
|
|
- unsigned long index = start >> kpte_shift_min;
|
|
|
+ int i;
|
|
|
|
|
|
- kpte_set_val(index, val);
|
|
|
+ for (i = 0; i < KERNEL_TSB_NENTRIES; i++) {
|
|
|
+ struct tsb *ent = &swapper_tsb[i];
|
|
|
|
|
|
- start += 1UL << kpte_shift_min;
|
|
|
- remains -= 1UL << kpte_shift_min;
|
|
|
+ ent->tag = (1UL << TSB_TAG_INVALID_BIT);
|
|
|
}
|
|
|
+#ifndef CONFIG_DEBUG_PAGEALLOC
|
|
|
+ for (i = 0; i < KERNEL_TSB4M_NENTRIES; i++) {
|
|
|
+ struct tsb *ent = &swapper_4m_tsb[i];
|
|
|
|
|
|
- return start;
|
|
|
-}
|
|
|
-
|
|
|
-static void __init mark_kpte_bitmap(unsigned long start, unsigned long end)
|
|
|
-{
|
|
|
- unsigned long smallest_size, smallest_mask;
|
|
|
- unsigned long s;
|
|
|
-
|
|
|
- smallest_size = (1UL << kpte_shift_min);
|
|
|
- smallest_mask = (smallest_size - 1UL);
|
|
|
-
|
|
|
- while (start < end) {
|
|
|
- unsigned long orig_start = start;
|
|
|
-
|
|
|
- for (s = kpte_shift_max; s >= kpte_shift_min; s -= kpte_shift_incr) {
|
|
|
- start = kpte_mark_using_shift(start, end, s);
|
|
|
-
|
|
|
- if (start != orig_start)
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if (start == orig_start)
|
|
|
- start = (start + smallest_size) & ~smallest_mask;
|
|
|
+ ent->tag = (1UL << TSB_TAG_INVALID_BIT);
|
|
|
}
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
-static void __init init_kpte_bitmap(void)
|
|
|
-{
|
|
|
- unsigned long i;
|
|
|
-
|
|
|
- for (i = 0; i < pall_ents; i++) {
|
|
|
- unsigned long phys_start, phys_end;
|
|
|
-
|
|
|
- phys_start = pall[i].phys_addr;
|
|
|
- phys_end = phys_start + pall[i].reg_size;
|
|
|
-
|
|
|
- mark_kpte_bitmap(phys_start, phys_end);
|
|
|
- }
|
|
|
-}
|
|
|
+extern unsigned int kvmap_linear_patch[1];
|
|
|
|
|
|
static void __init kernel_physical_mapping_init(void)
|
|
|
{
|
|
|
-#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
|
unsigned long i, mem_alloced = 0UL;
|
|
|
+ bool use_huge = true;
|
|
|
|
|
|
+#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
|
+ use_huge = false;
|
|
|
+#endif
|
|
|
for (i = 0; i < pall_ents; i++) {
|
|
|
unsigned long phys_start, phys_end;
|
|
|
|
|
@@ -1523,7 +1606,7 @@ static void __init kernel_physical_mapping_init(void)
|
|
|
phys_end = phys_start + pall[i].reg_size;
|
|
|
|
|
|
mem_alloced += kernel_map_range(phys_start, phys_end,
|
|
|
- PAGE_KERNEL);
|
|
|
+ PAGE_KERNEL, use_huge);
|
|
|
}
|
|
|
|
|
|
printk("Allocated %ld bytes for kernel page tables.\n",
|
|
@@ -1532,8 +1615,9 @@ static void __init kernel_physical_mapping_init(void)
|
|
|
kvmap_linear_patch[0] = 0x01000000; /* nop */
|
|
|
flushi(&kvmap_linear_patch[0]);
|
|
|
|
|
|
+ flush_all_kernel_tsbs();
|
|
|
+
|
|
|
__flush_tlb_all();
|
|
|
-#endif
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
@@ -1543,7 +1627,7 @@ void kernel_map_pages(struct page *page, int numpages, int enable)
|
|
|
unsigned long phys_end = phys_start + (numpages * PAGE_SIZE);
|
|
|
|
|
|
kernel_map_range(phys_start, phys_end,
|
|
|
- (enable ? PAGE_KERNEL : __pgprot(0)));
|
|
|
+ (enable ? PAGE_KERNEL : __pgprot(0)), false);
|
|
|
|
|
|
flush_tsb_kernel_range(PAGE_OFFSET + phys_start,
|
|
|
PAGE_OFFSET + phys_end);
|
|
@@ -1571,76 +1655,56 @@ unsigned long __init find_ecache_flush_span(unsigned long size)
|
|
|
unsigned long PAGE_OFFSET;
|
|
|
EXPORT_SYMBOL(PAGE_OFFSET);
|
|
|
|
|
|
-static void __init page_offset_shift_patch_one(unsigned int *insn, unsigned long phys_bits)
|
|
|
-{
|
|
|
- unsigned long final_shift;
|
|
|
- unsigned int val = *insn;
|
|
|
- unsigned int cnt;
|
|
|
-
|
|
|
- /* We are patching in ilog2(max_supported_phys_address), and
|
|
|
- * we are doing so in a manner similar to a relocation addend.
|
|
|
- * That is, we are adding the shift value to whatever value
|
|
|
- * is in the shift instruction count field already.
|
|
|
- */
|
|
|
- cnt = (val & 0x3f);
|
|
|
- val &= ~0x3f;
|
|
|
-
|
|
|
- /* If we are trying to shift >= 64 bits, clear the destination
|
|
|
- * register. This can happen when phys_bits ends up being equal
|
|
|
- * to MAX_PHYS_ADDRESS_BITS.
|
|
|
- */
|
|
|
- final_shift = (cnt + (64 - phys_bits));
|
|
|
- if (final_shift >= 64) {
|
|
|
- unsigned int rd = (val >> 25) & 0x1f;
|
|
|
-
|
|
|
- val = 0x80100000 | (rd << 25);
|
|
|
- } else {
|
|
|
- val |= final_shift;
|
|
|
- }
|
|
|
- *insn = val;
|
|
|
-
|
|
|
- __asm__ __volatile__("flush %0"
|
|
|
- : /* no outputs */
|
|
|
- : "r" (insn));
|
|
|
-}
|
|
|
-
|
|
|
-static void __init page_offset_shift_patch(unsigned long phys_bits)
|
|
|
-{
|
|
|
- extern unsigned int __page_offset_shift_patch;
|
|
|
- extern unsigned int __page_offset_shift_patch_end;
|
|
|
- unsigned int *p;
|
|
|
-
|
|
|
- p = &__page_offset_shift_patch;
|
|
|
- while (p < &__page_offset_shift_patch_end) {
|
|
|
- unsigned int *insn = (unsigned int *)(unsigned long)*p;
|
|
|
+unsigned long VMALLOC_END = 0x0000010000000000UL;
|
|
|
+EXPORT_SYMBOL(VMALLOC_END);
|
|
|
|
|
|
- page_offset_shift_patch_one(insn, phys_bits);
|
|
|
-
|
|
|
- p++;
|
|
|
- }
|
|
|
-}
|
|
|
+unsigned long sparc64_va_hole_top = 0xfffff80000000000UL;
|
|
|
+unsigned long sparc64_va_hole_bottom = 0x0000080000000000UL;
|
|
|
|
|
|
static void __init setup_page_offset(void)
|
|
|
{
|
|
|
- unsigned long max_phys_bits = 40;
|
|
|
-
|
|
|
if (tlb_type == cheetah || tlb_type == cheetah_plus) {
|
|
|
+ /* Cheetah/Panther support a full 64-bit virtual
|
|
|
+ * address, so we can use all that our page tables
|
|
|
+ * support.
|
|
|
+ */
|
|
|
+ sparc64_va_hole_top = 0xfff0000000000000UL;
|
|
|
+ sparc64_va_hole_bottom = 0x0010000000000000UL;
|
|
|
+
|
|
|
max_phys_bits = 42;
|
|
|
} else if (tlb_type == hypervisor) {
|
|
|
switch (sun4v_chip_type) {
|
|
|
case SUN4V_CHIP_NIAGARA1:
|
|
|
case SUN4V_CHIP_NIAGARA2:
|
|
|
+ /* T1 and T2 support 48-bit virtual addresses. */
|
|
|
+ sparc64_va_hole_top = 0xffff800000000000UL;
|
|
|
+ sparc64_va_hole_bottom = 0x0000800000000000UL;
|
|
|
+
|
|
|
max_phys_bits = 39;
|
|
|
break;
|
|
|
case SUN4V_CHIP_NIAGARA3:
|
|
|
+ /* T3 supports 48-bit virtual addresses. */
|
|
|
+ sparc64_va_hole_top = 0xffff800000000000UL;
|
|
|
+ sparc64_va_hole_bottom = 0x0000800000000000UL;
|
|
|
+
|
|
|
max_phys_bits = 43;
|
|
|
break;
|
|
|
case SUN4V_CHIP_NIAGARA4:
|
|
|
case SUN4V_CHIP_NIAGARA5:
|
|
|
case SUN4V_CHIP_SPARC64X:
|
|
|
- default:
|
|
|
+ case SUN4V_CHIP_SPARC_M6:
|
|
|
+ /* T4 and later support 52-bit virtual addresses. */
|
|
|
+ sparc64_va_hole_top = 0xfff8000000000000UL;
|
|
|
+ sparc64_va_hole_bottom = 0x0008000000000000UL;
|
|
|
max_phys_bits = 47;
|
|
|
break;
|
|
|
+ case SUN4V_CHIP_SPARC_M7:
|
|
|
+ default:
|
|
|
+ /* M7 and later support 52-bit virtual addresses. */
|
|
|
+ sparc64_va_hole_top = 0xfff8000000000000UL;
|
|
|
+ sparc64_va_hole_bottom = 0x0008000000000000UL;
|
|
|
+ max_phys_bits = 49;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1650,12 +1714,16 @@ static void __init setup_page_offset(void)
|
|
|
prom_halt();
|
|
|
}
|
|
|
|
|
|
- PAGE_OFFSET = PAGE_OFFSET_BY_BITS(max_phys_bits);
|
|
|
+ PAGE_OFFSET = sparc64_va_hole_top;
|
|
|
+ VMALLOC_END = ((sparc64_va_hole_bottom >> 1) +
|
|
|
+ (sparc64_va_hole_bottom >> 2));
|
|
|
|
|
|
- pr_info("PAGE_OFFSET is 0x%016lx (max_phys_bits == %lu)\n",
|
|
|
+ pr_info("MM: PAGE_OFFSET is 0x%016lx (max_phys_bits == %lu)\n",
|
|
|
PAGE_OFFSET, max_phys_bits);
|
|
|
-
|
|
|
- page_offset_shift_patch(max_phys_bits);
|
|
|
+ pr_info("MM: VMALLOC [0x%016lx --> 0x%016lx]\n",
|
|
|
+ VMALLOC_START, VMALLOC_END);
|
|
|
+ pr_info("MM: VMEMMAP [0x%016lx --> 0x%016lx]\n",
|
|
|
+ VMEMMAP_BASE, VMEMMAP_BASE << 1);
|
|
|
}
|
|
|
|
|
|
static void __init tsb_phys_patch(void)
|
|
@@ -1700,21 +1768,42 @@ static void __init tsb_phys_patch(void)
|
|
|
#define NUM_KTSB_DESCR 1
|
|
|
#endif
|
|
|
static struct hv_tsb_descr ktsb_descr[NUM_KTSB_DESCR];
|
|
|
-extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
|
|
|
+
|
|
|
+/* The swapper TSBs are loaded with a base sequence of:
|
|
|
+ *
|
|
|
+ * sethi %uhi(SYMBOL), REG1
|
|
|
+ * sethi %hi(SYMBOL), REG2
|
|
|
+ * or REG1, %ulo(SYMBOL), REG1
|
|
|
+ * or REG2, %lo(SYMBOL), REG2
|
|
|
+ * sllx REG1, 32, REG1
|
|
|
+ * or REG1, REG2, REG1
|
|
|
+ *
|
|
|
+ * When we use physical addressing for the TSB accesses, we patch the
|
|
|
+ * first four instructions in the above sequence.
|
|
|
+ */
|
|
|
|
|
|
static void patch_one_ktsb_phys(unsigned int *start, unsigned int *end, unsigned long pa)
|
|
|
{
|
|
|
- pa >>= KTSB_PHYS_SHIFT;
|
|
|
+ unsigned long high_bits, low_bits;
|
|
|
+
|
|
|
+ high_bits = (pa >> 32) & 0xffffffff;
|
|
|
+ low_bits = (pa >> 0) & 0xffffffff;
|
|
|
|
|
|
while (start < end) {
|
|
|
unsigned int *ia = (unsigned int *)(unsigned long)*start;
|
|
|
|
|
|
- ia[0] = (ia[0] & ~0x3fffff) | (pa >> 10);
|
|
|
+ ia[0] = (ia[0] & ~0x3fffff) | (high_bits >> 10);
|
|
|
__asm__ __volatile__("flush %0" : : "r" (ia));
|
|
|
|
|
|
- ia[1] = (ia[1] & ~0x3ff) | (pa & 0x3ff);
|
|
|
+ ia[1] = (ia[1] & ~0x3fffff) | (low_bits >> 10);
|
|
|
__asm__ __volatile__("flush %0" : : "r" (ia + 1));
|
|
|
|
|
|
+ ia[2] = (ia[2] & ~0x1fff) | (high_bits & 0x3ff);
|
|
|
+ __asm__ __volatile__("flush %0" : : "r" (ia + 2));
|
|
|
+
|
|
|
+ ia[3] = (ia[3] & ~0x1fff) | (low_bits & 0x3ff);
|
|
|
+ __asm__ __volatile__("flush %0" : : "r" (ia + 3));
|
|
|
+
|
|
|
start++;
|
|
|
}
|
|
|
}
|
|
@@ -1853,11 +1942,56 @@ static void __init sun4v_linear_pte_xor_finalize(void)
|
|
|
/* paging_init() sets up the page tables */
|
|
|
|
|
|
static unsigned long last_valid_pfn;
|
|
|
-pgd_t swapper_pg_dir[PTRS_PER_PGD];
|
|
|
|
|
|
static void sun4u_pgprot_init(void);
|
|
|
static void sun4v_pgprot_init(void);
|
|
|
|
|
|
+static phys_addr_t __init available_memory(void)
|
|
|
+{
|
|
|
+ phys_addr_t available = 0ULL;
|
|
|
+ phys_addr_t pa_start, pa_end;
|
|
|
+ u64 i;
|
|
|
+
|
|
|
+ for_each_free_mem_range(i, NUMA_NO_NODE, &pa_start, &pa_end, NULL)
|
|
|
+ available = available + (pa_end - pa_start);
|
|
|
+
|
|
|
+ return available;
|
|
|
+}
|
|
|
+
|
|
|
+/* We need to exclude reserved regions. This exclusion will include
|
|
|
+ * vmlinux and initrd. To be more precise the initrd size could be used to
|
|
|
+ * compute a new lower limit because it is freed later during initialization.
|
|
|
+ */
|
|
|
+static void __init reduce_memory(phys_addr_t limit_ram)
|
|
|
+{
|
|
|
+ phys_addr_t avail_ram = available_memory();
|
|
|
+ phys_addr_t pa_start, pa_end;
|
|
|
+ u64 i;
|
|
|
+
|
|
|
+ if (limit_ram >= avail_ram)
|
|
|
+ return;
|
|
|
+
|
|
|
+ for_each_free_mem_range(i, NUMA_NO_NODE, &pa_start, &pa_end, NULL) {
|
|
|
+ phys_addr_t region_size = pa_end - pa_start;
|
|
|
+ phys_addr_t clip_start = pa_start;
|
|
|
+
|
|
|
+ avail_ram = avail_ram - region_size;
|
|
|
+ /* Are we consuming too much? */
|
|
|
+ if (avail_ram < limit_ram) {
|
|
|
+ phys_addr_t give_back = limit_ram - avail_ram;
|
|
|
+
|
|
|
+ region_size = region_size - give_back;
|
|
|
+ clip_start = clip_start + give_back;
|
|
|
+ }
|
|
|
+
|
|
|
+ memblock_remove(clip_start, region_size);
|
|
|
+
|
|
|
+ if (avail_ram <= limit_ram)
|
|
|
+ break;
|
|
|
+ i = 0UL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void __init paging_init(void)
|
|
|
{
|
|
|
unsigned long end_pfn, shift, phys_base;
|
|
@@ -1937,7 +2071,8 @@ void __init paging_init(void)
|
|
|
|
|
|
find_ramdisk(phys_base);
|
|
|
|
|
|
- memblock_enforce_memory_limit(cmdline_memory_size);
|
|
|
+ if (cmdline_memory_size)
|
|
|
+ reduce_memory(cmdline_memory_size);
|
|
|
|
|
|
memblock_allow_resize();
|
|
|
memblock_dump_all();
|
|
@@ -1956,16 +2091,10 @@ void __init paging_init(void)
|
|
|
*/
|
|
|
init_mm.pgd += ((shift) / (sizeof(pgd_t)));
|
|
|
|
|
|
- memset(swapper_low_pmd_dir, 0, sizeof(swapper_low_pmd_dir));
|
|
|
+ memset(swapper_pg_dir, 0, sizeof(swapper_pg_dir));
|
|
|
|
|
|
- /* Now can init the kernel/bad page tables. */
|
|
|
- pud_set(pud_offset(&swapper_pg_dir[0], 0),
|
|
|
- swapper_low_pmd_dir + (shift / sizeof(pgd_t)));
|
|
|
-
|
|
|
inherit_prom_mappings();
|
|
|
|
|
|
- init_kpte_bitmap();
|
|
|
-
|
|
|
/* Ok, we can use our TLB miss and window trap handlers safely. */
|
|
|
setup_tba();
|
|
|
|
|
@@ -2072,70 +2201,6 @@ int page_in_phys_avail(unsigned long paddr)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static struct linux_prom64_registers pavail_rescan[MAX_BANKS] __initdata;
|
|
|
-static int pavail_rescan_ents __initdata;
|
|
|
-
|
|
|
-/* Certain OBP calls, such as fetching "available" properties, can
|
|
|
- * claim physical memory. So, along with initializing the valid
|
|
|
- * address bitmap, what we do here is refetch the physical available
|
|
|
- * memory list again, and make sure it provides at least as much
|
|
|
- * memory as 'pavail' does.
|
|
|
- */
|
|
|
-static void __init setup_valid_addr_bitmap_from_pavail(unsigned long *bitmap)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- read_obp_memory("available", &pavail_rescan[0], &pavail_rescan_ents);
|
|
|
-
|
|
|
- for (i = 0; i < pavail_ents; i++) {
|
|
|
- unsigned long old_start, old_end;
|
|
|
-
|
|
|
- old_start = pavail[i].phys_addr;
|
|
|
- old_end = old_start + pavail[i].reg_size;
|
|
|
- while (old_start < old_end) {
|
|
|
- int n;
|
|
|
-
|
|
|
- for (n = 0; n < pavail_rescan_ents; n++) {
|
|
|
- unsigned long new_start, new_end;
|
|
|
-
|
|
|
- new_start = pavail_rescan[n].phys_addr;
|
|
|
- new_end = new_start +
|
|
|
- pavail_rescan[n].reg_size;
|
|
|
-
|
|
|
- if (new_start <= old_start &&
|
|
|
- new_end >= (old_start + PAGE_SIZE)) {
|
|
|
- set_bit(old_start >> ILOG2_4MB, bitmap);
|
|
|
- goto do_next_page;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- prom_printf("mem_init: Lost memory in pavail\n");
|
|
|
- prom_printf("mem_init: OLD start[%lx] size[%lx]\n",
|
|
|
- pavail[i].phys_addr,
|
|
|
- pavail[i].reg_size);
|
|
|
- prom_printf("mem_init: NEW start[%lx] size[%lx]\n",
|
|
|
- pavail_rescan[i].phys_addr,
|
|
|
- pavail_rescan[i].reg_size);
|
|
|
- prom_printf("mem_init: Cannot continue, aborting.\n");
|
|
|
- prom_halt();
|
|
|
-
|
|
|
- do_next_page:
|
|
|
- old_start += PAGE_SIZE;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static void __init patch_tlb_miss_handler_bitmap(void)
|
|
|
-{
|
|
|
- extern unsigned int valid_addr_bitmap_insn[];
|
|
|
- extern unsigned int valid_addr_bitmap_patch[];
|
|
|
-
|
|
|
- valid_addr_bitmap_insn[1] = valid_addr_bitmap_patch[1];
|
|
|
- mb();
|
|
|
- valid_addr_bitmap_insn[0] = valid_addr_bitmap_patch[0];
|
|
|
- flushi(&valid_addr_bitmap_insn[0]);
|
|
|
-}
|
|
|
-
|
|
|
static void __init register_page_bootmem_info(void)
|
|
|
{
|
|
|
#ifdef CONFIG_NEED_MULTIPLE_NODES
|
|
@@ -2148,18 +2213,6 @@ static void __init register_page_bootmem_info(void)
|
|
|
}
|
|
|
void __init mem_init(void)
|
|
|
{
|
|
|
- unsigned long addr, last;
|
|
|
-
|
|
|
- addr = PAGE_OFFSET + kern_base;
|
|
|
- last = PAGE_ALIGN(kern_size) + addr;
|
|
|
- while (addr < last) {
|
|
|
- set_bit(__pa(addr) >> ILOG2_4MB, sparc64_valid_addr_bitmap);
|
|
|
- addr += PAGE_SIZE;
|
|
|
- }
|
|
|
-
|
|
|
- setup_valid_addr_bitmap_from_pavail(sparc64_valid_addr_bitmap);
|
|
|
- patch_tlb_miss_handler_bitmap();
|
|
|
-
|
|
|
high_memory = __va(last_valid_pfn << PAGE_SHIFT);
|
|
|
|
|
|
register_page_bootmem_info();
|
|
@@ -2249,18 +2302,9 @@ unsigned long _PAGE_CACHE __read_mostly;
|
|
|
EXPORT_SYMBOL(_PAGE_CACHE);
|
|
|
|
|
|
#ifdef CONFIG_SPARSEMEM_VMEMMAP
|
|
|
-unsigned long vmemmap_table[VMEMMAP_SIZE];
|
|
|
-
|
|
|
-static long __meminitdata addr_start, addr_end;
|
|
|
-static int __meminitdata node_start;
|
|
|
-
|
|
|
int __meminit vmemmap_populate(unsigned long vstart, unsigned long vend,
|
|
|
int node)
|
|
|
{
|
|
|
- unsigned long phys_start = (vstart - VMEMMAP_BASE);
|
|
|
- unsigned long phys_end = (vend - VMEMMAP_BASE);
|
|
|
- unsigned long addr = phys_start & VMEMMAP_CHUNK_MASK;
|
|
|
- unsigned long end = VMEMMAP_ALIGN(phys_end);
|
|
|
unsigned long pte_base;
|
|
|
|
|
|
pte_base = (_PAGE_VALID | _PAGE_SZ4MB_4U |
|
|
@@ -2271,47 +2315,52 @@ int __meminit vmemmap_populate(unsigned long vstart, unsigned long vend,
|
|
|
_PAGE_CP_4V | _PAGE_CV_4V |
|
|
|
_PAGE_P_4V | _PAGE_W_4V);
|
|
|
|
|
|
- for (; addr < end; addr += VMEMMAP_CHUNK) {
|
|
|
- unsigned long *vmem_pp =
|
|
|
- vmemmap_table + (addr >> VMEMMAP_CHUNK_SHIFT);
|
|
|
- void *block;
|
|
|
+ pte_base |= _PAGE_PMD_HUGE;
|
|
|
|
|
|
- if (!(*vmem_pp & _PAGE_VALID)) {
|
|
|
- block = vmemmap_alloc_block(1UL << ILOG2_4MB, node);
|
|
|
- if (!block)
|
|
|
+ vstart = vstart & PMD_MASK;
|
|
|
+ vend = ALIGN(vend, PMD_SIZE);
|
|
|
+ for (; vstart < vend; vstart += PMD_SIZE) {
|
|
|
+ pgd_t *pgd = pgd_offset_k(vstart);
|
|
|
+ unsigned long pte;
|
|
|
+ pud_t *pud;
|
|
|
+ pmd_t *pmd;
|
|
|
+
|
|
|
+ if (pgd_none(*pgd)) {
|
|
|
+ pud_t *new = vmemmap_alloc_block(PAGE_SIZE, node);
|
|
|
+
|
|
|
+ if (!new)
|
|
|
return -ENOMEM;
|
|
|
+ pgd_populate(&init_mm, pgd, new);
|
|
|
+ }
|
|
|
|
|
|
- *vmem_pp = pte_base | __pa(block);
|
|
|
+ pud = pud_offset(pgd, vstart);
|
|
|
+ if (pud_none(*pud)) {
|
|
|
+ pmd_t *new = vmemmap_alloc_block(PAGE_SIZE, node);
|
|
|
|
|
|
- /* check to see if we have contiguous blocks */
|
|
|
- if (addr_end != addr || node_start != node) {
|
|
|
- if (addr_start)
|
|
|
- printk(KERN_DEBUG " [%lx-%lx] on node %d\n",
|
|
|
- addr_start, addr_end-1, node_start);
|
|
|
- addr_start = addr;
|
|
|
- node_start = node;
|
|
|
- }
|
|
|
- addr_end = addr + VMEMMAP_CHUNK;
|
|
|
+ if (!new)
|
|
|
+ return -ENOMEM;
|
|
|
+ pud_populate(&init_mm, pud, new);
|
|
|
}
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
|
|
|
-void __meminit vmemmap_populate_print_last(void)
|
|
|
-{
|
|
|
- if (addr_start) {
|
|
|
- printk(KERN_DEBUG " [%lx-%lx] on node %d\n",
|
|
|
- addr_start, addr_end-1, node_start);
|
|
|
- addr_start = 0;
|
|
|
- addr_end = 0;
|
|
|
- node_start = 0;
|
|
|
+ pmd = pmd_offset(pud, vstart);
|
|
|
+
|
|
|
+ pte = pmd_val(*pmd);
|
|
|
+ if (!(pte & _PAGE_VALID)) {
|
|
|
+ void *block = vmemmap_alloc_block(PMD_SIZE, node);
|
|
|
+
|
|
|
+ if (!block)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ pmd_val(*pmd) = pte_base | __pa(block);
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
void vmemmap_free(unsigned long start, unsigned long end)
|
|
|
{
|
|
|
}
|
|
|
-
|
|
|
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
|
|
|
|
|
|
static void prot_init_common(unsigned long page_none,
|
|
@@ -2787,8 +2836,8 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
|
|
do_flush_tlb_kernel_range(start, LOW_OBP_ADDRESS);
|
|
|
}
|
|
|
if (end > HI_OBP_ADDRESS) {
|
|
|
- flush_tsb_kernel_range(end, HI_OBP_ADDRESS);
|
|
|
- do_flush_tlb_kernel_range(end, HI_OBP_ADDRESS);
|
|
|
+ flush_tsb_kernel_range(HI_OBP_ADDRESS, end);
|
|
|
+ do_flush_tlb_kernel_range(HI_OBP_ADDRESS, end);
|
|
|
}
|
|
|
} else {
|
|
|
flush_tsb_kernel_range(start, end);
|