summaryrefslogtreecommitdiffstats
path: root/arch/s390/mm
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/mm')
-rw-r--r--arch/s390/mm/Makefile2
-rw-r--r--arch/s390/mm/init.c32
-rw-r--r--arch/s390/mm/pgtable.c94
-rw-r--r--arch/s390/mm/vmem.c53
4 files changed, 145 insertions, 36 deletions
diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile
index f95449b..6640193 100644
--- a/arch/s390/mm/Makefile
+++ b/arch/s390/mm/Makefile
@@ -2,6 +2,6 @@
# Makefile for the linux s390-specific parts of the memory manager.
#
-obj-y := init.o fault.o extmem.o mmap.o vmem.o
+obj-y := init.o fault.o extmem.o mmap.o vmem.o pgtable.o
obj-$(CONFIG_CMM) += cmm.o
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index 3a25bbf..b234bb4 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -81,6 +81,7 @@ void show_mem(void)
static void __init setup_ro_region(void)
{
pgd_t *pgd;
+ pud_t *pud;
pmd_t *pmd;
pte_t *pte;
pte_t new_pte;
@@ -91,7 +92,8 @@ static void __init setup_ro_region(void)
for (; address < end; address += PAGE_SIZE) {
pgd = pgd_offset_k(address);
- pmd = pmd_offset(pgd, address);
+ pud = pud_offset(pgd, address);
+ pmd = pmd_offset(pud, address);
pte = pte_offset_kernel(pmd, address);
new_pte = mk_pte_phys(address, __pgprot(_PAGE_RO));
*pte = new_pte;
@@ -103,32 +105,28 @@ static void __init setup_ro_region(void)
*/
void __init paging_init(void)
{
- pgd_t *pg_dir;
- int i;
- unsigned long pgdir_k;
static const int ssm_mask = 0x04000000L;
unsigned long max_zone_pfns[MAX_NR_ZONES];
+ unsigned long pgd_type;
- pg_dir = swapper_pg_dir;
-
+ init_mm.pgd = swapper_pg_dir;
+ S390_lowcore.kernel_asce = __pa(init_mm.pgd) & PAGE_MASK;
#ifdef CONFIG_64BIT
- pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERN_REGION_TABLE;
- for (i = 0; i < PTRS_PER_PGD; i++)
- pgd_clear_kernel(pg_dir + i);
+ S390_lowcore.kernel_asce |= _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH;
+ pgd_type = _REGION3_ENTRY_EMPTY;
#else
- pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE;
- for (i = 0; i < PTRS_PER_PGD; i++)
- pmd_clear_kernel((pmd_t *)(pg_dir + i));
+ S390_lowcore.kernel_asce |= _ASCE_TABLE_LENGTH;
+ pgd_type = _SEGMENT_ENTRY_EMPTY;
#endif
+ clear_table((unsigned long *) init_mm.pgd, pgd_type,
+ sizeof(unsigned long)*2048);
vmem_map_init();
setup_ro_region();
- S390_lowcore.kernel_asce = pgdir_k;
-
/* enable virtual mapping in kernel mode */
- __ctl_load(pgdir_k, 1, 1);
- __ctl_load(pgdir_k, 7, 7);
- __ctl_load(pgdir_k, 13, 13);
+ __ctl_load(S390_lowcore.kernel_asce, 1, 1);
+ __ctl_load(S390_lowcore.kernel_asce, 7, 7);
+ __ctl_load(S390_lowcore.kernel_asce, 13, 13);
__raw_local_irq_ssm(ssm_mask);
memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
new file mode 100644
index 0000000..e60e0ae
--- /dev/null
+++ b/arch/s390/mm/pgtable.c
@@ -0,0 +1,94 @@
+/*
+ * arch/s390/mm/pgtable.c
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/smp.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/quicklist.h>
+
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/tlb.h>
+#include <asm/tlbflush.h>
+
+#ifndef CONFIG_64BIT
+#define ALLOC_ORDER 1
+#else
+#define ALLOC_ORDER 2
+#endif
+
+unsigned long *crst_table_alloc(struct mm_struct *mm, int noexec)
+{
+ struct page *page = alloc_pages(GFP_KERNEL, ALLOC_ORDER);
+
+ if (!page)
+ return NULL;
+ page->index = 0;
+ if (noexec) {
+ struct page *shadow = alloc_pages(GFP_KERNEL, ALLOC_ORDER);
+ if (!shadow) {
+ __free_pages(page, ALLOC_ORDER);
+ return NULL;
+ }
+ page->index = page_to_phys(shadow);
+ }
+ return (unsigned long *) page_to_phys(page);
+}
+
+void crst_table_free(unsigned long *table)
+{
+ unsigned long *shadow = get_shadow_table(table);
+
+ if (shadow)
+ free_pages((unsigned long) shadow, ALLOC_ORDER);
+ free_pages((unsigned long) table, ALLOC_ORDER);
+}
+
+/*
+ * page table entry allocation/free routines.
+ */
+unsigned long *page_table_alloc(int noexec)
+{
+ struct page *page = alloc_page(GFP_KERNEL);
+ unsigned long *table;
+
+ if (!page)
+ return NULL;
+ page->index = 0;
+ if (noexec) {
+ struct page *shadow = alloc_page(GFP_KERNEL);
+ if (!shadow) {
+ __free_page(page);
+ return NULL;
+ }
+ table = (unsigned long *) page_to_phys(shadow);
+ clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
+ page->index = (addr_t) table;
+ }
+ table = (unsigned long *) page_to_phys(page);
+ clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
+ return table;
+}
+
+void page_table_free(unsigned long *table)
+{
+ unsigned long *shadow = get_shadow_pte(table);
+
+ if (shadow)
+ free_page((unsigned long) shadow);
+ free_page((unsigned long) table);
+
+}
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index fd594d5..fb9c5a8 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -73,31 +73,28 @@ static void __init_refok *vmem_alloc_pages(unsigned int order)
return alloc_bootmem_pages((1 << order) * PAGE_SIZE);
}
+#define vmem_pud_alloc() ({ BUG(); ((pud_t *) NULL); })
+
static inline pmd_t *vmem_pmd_alloc(void)
{
- pmd_t *pmd;
- int i;
+ pmd_t *pmd = NULL;
- pmd = vmem_alloc_pages(PMD_ALLOC_ORDER);
+#ifdef CONFIG_64BIT
+ pmd = vmem_alloc_pages(2);
if (!pmd)
return NULL;
- for (i = 0; i < PTRS_PER_PMD; i++)
- pmd_clear_kernel(pmd + i);
+ clear_table((unsigned long *) pmd, _SEGMENT_ENTRY_EMPTY, PAGE_SIZE*4);
+#endif
return pmd;
}
static inline pte_t *vmem_pte_alloc(void)
{
- pte_t *pte;
- pte_t empty_pte;
- int i;
+ pte_t *pte = vmem_alloc_pages(0);
- pte = vmem_alloc_pages(PTE_ALLOC_ORDER);
if (!pte)
return NULL;
- pte_val(empty_pte) = _PAGE_TYPE_EMPTY;
- for (i = 0; i < PTRS_PER_PTE; i++)
- pte[i] = empty_pte;
+ clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY, PAGE_SIZE);
return pte;
}
@@ -108,6 +105,7 @@ static int vmem_add_range(unsigned long start, unsigned long size)
{
unsigned long address;
pgd_t *pg_dir;
+ pud_t *pu_dir;
pmd_t *pm_dir;
pte_t *pt_dir;
pte_t pte;
@@ -116,13 +114,21 @@ static int vmem_add_range(unsigned long start, unsigned long size)
for (address = start; address < start + size; address += PAGE_SIZE) {
pg_dir = pgd_offset_k(address);
if (pgd_none(*pg_dir)) {
+ pu_dir = vmem_pud_alloc();
+ if (!pu_dir)
+ goto out;
+ pgd_populate_kernel(&init_mm, pg_dir, pu_dir);
+ }
+
+ pu_dir = pud_offset(pg_dir, address);
+ if (pud_none(*pu_dir)) {
pm_dir = vmem_pmd_alloc();
if (!pm_dir)
goto out;
- pgd_populate_kernel(&init_mm, pg_dir, pm_dir);
+ pud_populate_kernel(&init_mm, pu_dir, pm_dir);
}
- pm_dir = pmd_offset(pg_dir, address);
+ pm_dir = pmd_offset(pu_dir, address);
if (pmd_none(*pm_dir)) {
pt_dir = vmem_pte_alloc();
if (!pt_dir)
@@ -148,6 +154,7 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
{
unsigned long address;
pgd_t *pg_dir;
+ pud_t *pu_dir;
pmd_t *pm_dir;
pte_t *pt_dir;
pte_t pte;
@@ -155,9 +162,10 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
pte_val(pte) = _PAGE_TYPE_EMPTY;
for (address = start; address < start + size; address += PAGE_SIZE) {
pg_dir = pgd_offset_k(address);
- if (pgd_none(*pg_dir))
+ pu_dir = pud_offset(pg_dir, address);
+ if (pud_none(*pu_dir))
continue;
- pm_dir = pmd_offset(pg_dir, address);
+ pm_dir = pmd_offset(pu_dir, address);
if (pmd_none(*pm_dir))
continue;
pt_dir = pte_offset_kernel(pm_dir, address);
@@ -174,6 +182,7 @@ static int vmem_add_mem_map(unsigned long start, unsigned long size)
unsigned long address, start_addr, end_addr;
struct page *map_start, *map_end;
pgd_t *pg_dir;
+ pud_t *pu_dir;
pmd_t *pm_dir;
pte_t *pt_dir;
pte_t pte;
@@ -188,13 +197,21 @@ static int vmem_add_mem_map(unsigned long start, unsigned long size)
for (address = start_addr; address < end_addr; address += PAGE_SIZE) {
pg_dir = pgd_offset_k(address);
if (pgd_none(*pg_dir)) {
+ pu_dir = vmem_pud_alloc();
+ if (!pu_dir)
+ goto out;
+ pgd_populate_kernel(&init_mm, pg_dir, pu_dir);
+ }
+
+ pu_dir = pud_offset(pg_dir, address);
+ if (pud_none(*pu_dir)) {
pm_dir = vmem_pmd_alloc();
if (!pm_dir)
goto out;
- pgd_populate_kernel(&init_mm, pg_dir, pm_dir);
+ pud_populate_kernel(&init_mm, pu_dir, pm_dir);
}
- pm_dir = pmd_offset(pg_dir, address);
+ pm_dir = pmd_offset(pu_dir, address);
if (pmd_none(*pm_dir)) {
pt_dir = vmem_pte_alloc();
if (!pt_dir)
OpenPOWER on IntegriCloud