summaryrefslogtreecommitdiff
path: root/arch/parisc/mm/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/parisc/mm/init.c')
-rw-r--r--arch/parisc/mm/init.c272
1 files changed, 172 insertions, 100 deletions
diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c
index ddca8287d43b..f876af56e13f 100644
--- a/arch/parisc/mm/init.c
+++ b/arch/parisc/mm/init.c
@@ -26,28 +26,24 @@
#include <linux/compat.h>
#include <asm/pgalloc.h>
-#include <asm/pgtable.h>
#include <asm/tlb.h>
#include <asm/pdc_chassis.h>
#include <asm/mmzone.h>
#include <asm/sections.h>
#include <asm/msgbuf.h>
#include <asm/sparsemem.h>
+#include <asm/asm-offsets.h>
+#include <asm/shmbuf.h>
extern int data_start;
extern void parisc_kernel_start(void); /* Kernel entry point in head.S */
#if CONFIG_PGTABLE_LEVELS == 3
-/* NOTE: This layout exactly conforms to the hybrid L2/L3 page table layout
- * with the first pmd adjacent to the pgd and below it. gcc doesn't actually
- * guarantee that global objects will be laid out in memory in the same order
- * as the order of declaration, so put these in different sections and use
- * the linker script to order them. */
-pmd_t pmd0[PTRS_PER_PMD] __attribute__ ((__section__ (".data..vm0.pmd"), aligned(PAGE_SIZE)));
+pmd_t pmd0[PTRS_PER_PMD] __section(".data..vm0.pmd") __attribute__ ((aligned(PAGE_SIZE)));
#endif
-pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__ ((__section__ (".data..vm0.pgd"), aligned(PAGE_SIZE)));
-pte_t pg0[PT_INITIAL * PTRS_PER_PTE] __attribute__ ((__section__ (".data..vm0.pte"), aligned(PAGE_SIZE)));
+pgd_t swapper_pg_dir[PTRS_PER_PGD] __section(".data..vm0.pgd") __attribute__ ((aligned(PAGE_SIZE)));
+pte_t pg0[PT_INITIAL * PTRS_PER_PTE] __section(".data..vm0.pte") __attribute__ ((aligned(PAGE_SIZE)));
static struct resource data_resource = {
.name = "Kernel data",
@@ -133,16 +129,12 @@ static void __init setup_bootmem(void)
int j;
for (j = i; j > 0; j--) {
- physmem_range_t tmp;
-
if (pmem_ranges[j-1].start_pfn <
pmem_ranges[j].start_pfn) {
break;
}
- tmp = pmem_ranges[j-1];
- pmem_ranges[j-1] = pmem_ranges[j];
- pmem_ranges[j] = tmp;
+ swap(pmem_ranges[j-1], pmem_ranges[j]);
}
}
@@ -347,11 +339,10 @@ static void __init setup_bootmem(void)
static bool kernel_set_to_readonly;
-static void __init map_pages(unsigned long start_vaddr,
- unsigned long start_paddr, unsigned long size,
- pgprot_t pgprot, int force)
+static void __ref map_pages(unsigned long start_vaddr,
+ unsigned long start_paddr, unsigned long size,
+ pgprot_t pgprot, int force)
{
- pgd_t *pg_dir;
pmd_t *pmd;
pte_t *pg_table;
unsigned long end_paddr;
@@ -372,62 +363,37 @@ static void __init map_pages(unsigned long start_vaddr,
end_paddr = start_paddr + size;
- pg_dir = pgd_offset_k(start_vaddr);
-
-#if PTRS_PER_PMD == 1
- start_pmd = 0;
-#else
+ /* for 2-level configuration PTRS_PER_PMD is 0 so start_pmd will be 0 */
start_pmd = ((start_vaddr >> PMD_SHIFT) & (PTRS_PER_PMD - 1));
-#endif
start_pte = ((start_vaddr >> PAGE_SHIFT) & (PTRS_PER_PTE - 1));
address = start_paddr;
vaddr = start_vaddr;
while (address < end_paddr) {
-#if PTRS_PER_PMD == 1
- pmd = (pmd_t *)__pa(pg_dir);
-#else
- pmd = (pmd_t *)pgd_address(*pg_dir);
-
- /*
- * pmd is physical at this point
- */
+ pgd_t *pgd = pgd_offset_k(vaddr);
+ p4d_t *p4d = p4d_offset(pgd, vaddr);
+ pud_t *pud = pud_offset(p4d, vaddr);
- if (!pmd) {
- pmd = memblock_alloc(PAGE_SIZE << PMD_ORDER,
- PAGE_SIZE << PMD_ORDER);
+#if CONFIG_PGTABLE_LEVELS == 3
+ if (pud_none(*pud)) {
+ pmd = memblock_alloc(PAGE_SIZE << PMD_TABLE_ORDER,
+ PAGE_SIZE << PMD_TABLE_ORDER);
if (!pmd)
panic("pmd allocation failed.\n");
- pmd = (pmd_t *) __pa(pmd);
+ pud_populate(NULL, pud, pmd);
}
-
- pgd_populate(NULL, pg_dir, __va(pmd));
#endif
- pg_dir++;
- /* now change pmd to kernel virtual addresses */
-
- pmd = (pmd_t *)__va(pmd) + start_pmd;
+ pmd = pmd_offset(pud, vaddr);
for (tmp1 = start_pmd; tmp1 < PTRS_PER_PMD; tmp1++, pmd++) {
-
- /*
- * pg_table is physical at this point
- */
-
- pg_table = (pte_t *)pmd_address(*pmd);
- if (!pg_table) {
- pg_table = memblock_alloc(PAGE_SIZE,
- PAGE_SIZE);
+ if (pmd_none(*pmd)) {
+ pg_table = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
if (!pg_table)
panic("page table allocation failed\n");
- pg_table = (pte_t *) __pa(pg_table);
+ pmd_populate_kernel(NULL, pmd, pg_table);
}
- pmd_populate_kernel(NULL, pmd, __va(pg_table));
-
- /* now change pg_table to kernel virtual addresses */
-
- pg_table = (pte_t *) __va(pg_table) + start_pte;
+ pg_table = pte_offset_kernel(pmd, vaddr);
for (tmp2 = start_pte; tmp2 < PTRS_PER_PTE; tmp2++, pg_table++) {
pte_t pte;
pgprot_t prot;
@@ -485,7 +451,7 @@ void __init set_kernel_text_rw(int enable_read_write)
flush_tlb_all();
}
-void __ref free_initmem(void)
+void free_initmem(void)
{
unsigned long init_begin = (unsigned long)__init_begin;
unsigned long init_end = (unsigned long)__init_end;
@@ -499,7 +465,6 @@ void __ref free_initmem(void)
/* The init text pages are marked R-X. We have to
* flush the icache and mark them RW-
*
- * This is tricky, because map_pages is in the init section.
* Do a dummy remap of the data section first (the data
* section is already PAGE_KERNEL) to pull in the TLB entries
* for map_kernel */
@@ -560,10 +525,6 @@ void mark_rodata_ro(void)
void *parisc_vmalloc_start __ro_after_init;
EXPORT_SYMBOL(parisc_vmalloc_start);
-#ifdef CONFIG_PA11
-unsigned long pcxl_dma_start __ro_after_init;
-#endif
-
void __init mem_init(void)
{
/* Do sanity checks on IPC (compat) structures */
@@ -586,9 +547,20 @@ void __init mem_init(void)
BUILD_BUG_ON(PGD_ENTRY_SIZE != sizeof(pgd_t));
BUILD_BUG_ON(PAGE_SHIFT + BITS_PER_PTE + BITS_PER_PMD + BITS_PER_PGD
> BITS_PER_LONG);
+#if CONFIG_PGTABLE_LEVELS == 3
+ BUILD_BUG_ON(PT_INITIAL > PTRS_PER_PMD);
+#else
+ BUILD_BUG_ON(PT_INITIAL > PTRS_PER_PGD);
+#endif
+
+#ifdef CONFIG_64BIT
+ /* avoid ldil_%L() asm statements to sign-extend into upper 32-bits */
+ BUILD_BUG_ON(__PAGE_OFFSET >= 0x80000000);
+ BUILD_BUG_ON(TMPALIAS_MAP_START >= 0x80000000);
+#endif
high_memory = __va((max_pfn << PAGE_SHIFT));
- set_max_mapnr(page_to_pfn(virt_to_page(high_memory - 1)) + 1);
+ set_max_mapnr(max_low_pfn);
memblock_free_all();
#ifdef CONFIG_PA11
@@ -600,8 +572,6 @@ void __init mem_init(void)
#endif
parisc_vmalloc_start = SET_MAP_OFFSET(MAP_START);
- mem_init_print_info(NULL);
-
#if 0
/*
* Do not expose the virtual kernel memory layout to userspace.
@@ -654,12 +624,10 @@ static void __init pagetable_init(void)
for (range = 0; range < npmem_ranges; range++) {
unsigned long start_paddr;
- unsigned long end_paddr;
unsigned long size;
start_paddr = pmem_ranges[range].start_pfn << PAGE_SHIFT;
size = pmem_ranges[range].pages << PAGE_SHIFT;
- end_paddr = start_paddr + size;
map_pages((unsigned long)__va(start_paddr), start_paddr,
size, PAGE_KERNEL, 0);
@@ -699,29 +667,46 @@ static void __init gateway_init(void)
PAGE_SIZE, PAGE_GATEWAY, 1);
}
-static void __init parisc_bootmem_free(void)
+static void __init fixmap_init(void)
{
- unsigned long zones_size[MAX_NR_ZONES] = { 0, };
- unsigned long holes_size[MAX_NR_ZONES] = { 0, };
- unsigned long mem_start_pfn = ~0UL, mem_end_pfn = 0, mem_size_pfn = 0;
- int i;
+ unsigned long addr = FIXMAP_START;
+ unsigned long end = FIXMAP_START + FIXMAP_SIZE;
+ pgd_t *pgd = pgd_offset_k(addr);
+ p4d_t *p4d = p4d_offset(pgd, addr);
+ pud_t *pud = pud_offset(p4d, addr);
+ pmd_t *pmd;
- for (i = 0; i < npmem_ranges; i++) {
- unsigned long start = pmem_ranges[i].start_pfn;
- unsigned long size = pmem_ranges[i].pages;
- unsigned long end = start + size;
-
- if (mem_start_pfn > start)
- mem_start_pfn = start;
- if (mem_end_pfn < end)
- mem_end_pfn = end;
- mem_size_pfn += size;
+ BUILD_BUG_ON(FIXMAP_SIZE > PMD_SIZE);
+
+#if CONFIG_PGTABLE_LEVELS == 3
+ if (pud_none(*pud)) {
+ pmd = memblock_alloc(PAGE_SIZE << PMD_TABLE_ORDER,
+ PAGE_SIZE << PMD_TABLE_ORDER);
+ if (!pmd)
+ panic("fixmap: pmd allocation failed.\n");
+ pud_populate(NULL, pud, pmd);
}
+#endif
+
+ pmd = pmd_offset(pud, addr);
+ do {
+ pte_t *pte = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+ if (!pte)
+ panic("fixmap: pte allocation failed.\n");
+
+ pmd_populate_kernel(&init_mm, pmd, pte);
+
+ addr += PAGE_SIZE;
+ } while (addr < end);
+}
+
+static void __init parisc_bootmem_free(void)
+{
+ unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0, };
- zones_size[0] = mem_end_pfn - mem_start_pfn;
- holes_size[0] = zones_size[0] - mem_size_pfn;
+ max_zone_pfn[0] = memblock_end_of_DRAM();
- free_area_init_node(0, zones_size, mem_start_pfn, holes_size);
+ free_area_init(max_zone_pfn);
}
void __init paging_init(void)
@@ -729,18 +714,85 @@ void __init paging_init(void)
setup_bootmem();
pagetable_init();
gateway_init();
+ fixmap_init();
flush_cache_all_local(); /* start with known state */
flush_tlb_all_local(NULL);
- /*
- * Mark all memblocks as present for sparsemem using
- * memory_present() and then initialize sparsemem.
- */
- memblocks_present();
sparse_init();
parisc_bootmem_free();
}
+static void alloc_btlb(unsigned long start, unsigned long end, int *slot,
+ unsigned long entry_info)
+{
+ const int slot_max = btlb_info.fixed_range_info.num_comb;
+ int min_num_pages = btlb_info.min_size;
+ unsigned long size;
+
+ /* map at minimum 4 pages */
+ if (min_num_pages < 4)
+ min_num_pages = 4;
+
+ size = HUGEPAGE_SIZE;
+ while (start < end && *slot < slot_max && size >= PAGE_SIZE) {
+ /* starting address must have same alignment as size! */
+ /* if correctly aligned and fits in double size, increase */
+ if (((start & (2 * size - 1)) == 0) &&
+ (end - start) >= (2 * size)) {
+ size <<= 1;
+ continue;
+ }
+ /* if current size alignment is too big, try smaller size */
+ if ((start & (size - 1)) != 0) {
+ size >>= 1;
+ continue;
+ }
+ if ((end - start) >= size) {
+ if ((size >> PAGE_SHIFT) >= min_num_pages)
+ pdc_btlb_insert(start >> PAGE_SHIFT, __pa(start) >> PAGE_SHIFT,
+ size >> PAGE_SHIFT, entry_info, *slot);
+ (*slot)++;
+ start += size;
+ continue;
+ }
+ size /= 2;
+ continue;
+ }
+}
+
+void btlb_init_per_cpu(void)
+{
+ unsigned long s, t, e;
+ int slot;
+
+ /* BTLBs are not available on 64-bit CPUs */
+ if (IS_ENABLED(CONFIG_PA20))
+ return;
+ else if (pdc_btlb_info(&btlb_info) < 0) {
+ memset(&btlb_info, 0, sizeof btlb_info);
+ }
+
+ /* insert BLTLBs for code and data segments */
+ s = (uintptr_t) dereference_function_descriptor(&_stext);
+ e = (uintptr_t) dereference_function_descriptor(&_etext);
+ t = (uintptr_t) dereference_function_descriptor(&_sdata);
+ BUG_ON(t != e);
+
+ /* code segments */
+ slot = 0;
+ alloc_btlb(s, e, &slot, 0x13800000);
+
+ /* sanity check */
+ t = (uintptr_t) dereference_function_descriptor(&_edata);
+ e = (uintptr_t) dereference_function_descriptor(&__bss_start);
+ BUG_ON(t != e);
+
+ /* data segments */
+ s = (uintptr_t) dereference_function_descriptor(&_sdata);
+ e = (uintptr_t) dereference_function_descriptor(&__bss_stop);
+ alloc_btlb(s, e, &slot, 0x11800000);
+}
+
#ifdef CONFIG_PA20
/*
@@ -771,7 +823,7 @@ static unsigned long space_id[SID_ARRAY_SIZE] = { 1 }; /* disallow space 0 */
static unsigned long dirty_space_id[SID_ARRAY_SIZE];
static unsigned long space_id_index;
static unsigned long free_space_ids = NR_SPACE_IDS - 1;
-static unsigned long dirty_space_ids = 0;
+static unsigned long dirty_space_ids;
static DEFINE_SPINLOCK(sid_lock);
@@ -793,7 +845,7 @@ unsigned long alloc_sid(void)
free_space_ids--;
index = find_next_zero_bit(space_id, NR_SPACE_IDS, space_id_index);
- space_id[index >> SHIFT_PER_LONG] |= (1L << (index & (BITS_PER_LONG - 1)));
+ space_id[BIT_WORD(index)] |= BIT_MASK(index);
space_id_index = index;
spin_unlock(&sid_lock);
@@ -804,16 +856,16 @@ unsigned long alloc_sid(void)
void free_sid(unsigned long spaceid)
{
unsigned long index = spaceid >> SPACEID_SHIFT;
- unsigned long *dirty_space_offset;
+ unsigned long *dirty_space_offset, mask;
- dirty_space_offset = dirty_space_id + (index >> SHIFT_PER_LONG);
- index &= (BITS_PER_LONG - 1);
+ dirty_space_offset = &dirty_space_id[BIT_WORD(index)];
+ mask = BIT_MASK(index);
spin_lock(&sid_lock);
- BUG_ON(*dirty_space_offset & (1L << index)); /* attempt to free space id twice */
+ BUG_ON(*dirty_space_offset & mask); /* attempt to free space id twice */
- *dirty_space_offset |= (1L << index);
+ *dirty_space_offset |= mask;
dirty_space_ids++;
spin_unlock(&sid_lock);
@@ -892,9 +944,9 @@ void flush_tlb_all(void)
{
int do_recycle;
- __inc_irq_stat(irq_tlb_count);
do_recycle = 0;
spin_lock(&sid_lock);
+ __inc_irq_stat(irq_tlb_count);
if (dirty_space_ids > RECYCLE_THRESHOLD) {
BUG_ON(recycle_inuse); /* FIXME: Use a semaphore/wait queue here */
get_dirty_sids(&recycle_ndirty,recycle_dirty_array);
@@ -913,10 +965,30 @@ void flush_tlb_all(void)
#else
void flush_tlb_all(void)
{
- __inc_irq_stat(irq_tlb_count);
spin_lock(&sid_lock);
+ __inc_irq_stat(irq_tlb_count);
flush_tlb_all_local(NULL);
recycle_sids();
spin_unlock(&sid_lock);
}
#endif
+
+static const pgprot_t protection_map[16] = {
+ [VM_NONE] = PAGE_NONE,
+ [VM_READ] = PAGE_READONLY,
+ [VM_WRITE] = PAGE_NONE,
+ [VM_WRITE | VM_READ] = PAGE_READONLY,
+ [VM_EXEC] = PAGE_EXECREAD,
+ [VM_EXEC | VM_READ] = PAGE_EXECREAD,
+ [VM_EXEC | VM_WRITE] = PAGE_EXECREAD,
+ [VM_EXEC | VM_WRITE | VM_READ] = PAGE_EXECREAD,
+ [VM_SHARED] = PAGE_NONE,
+ [VM_SHARED | VM_READ] = PAGE_READONLY,
+ [VM_SHARED | VM_WRITE] = PAGE_WRITEONLY,
+ [VM_SHARED | VM_WRITE | VM_READ] = PAGE_SHARED,
+ [VM_SHARED | VM_EXEC] = PAGE_EXECREAD,
+ [VM_SHARED | VM_EXEC | VM_READ] = PAGE_EXECREAD,
+ [VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_RWX,
+ [VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_RWX
+};
+DECLARE_VM_GET_PAGE_PROT