Skip to content

Commit

Permalink
[S390] 1K/2K page table pages.
Browse files Browse the repository at this point in the history
This patch implements 1K/2K page table pages for s390.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Martin Schwidefsky committed Feb 9, 2008
1 parent 0c1f1dc commit 146e4b3
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 183 deletions.
2 changes: 1 addition & 1 deletion arch/s390/mm/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ void kernel_map_pages(struct page *page, int numpages, int enable)
pmd = pmd_offset(pud, address);
pte = pte_offset_kernel(pmd, address);
if (!enable) {
ptep_invalidate(address, pte);
ptep_invalidate(&init_mm, address, pte);
continue;
}
*pte = mk_pte_phys(address, __pgprot(_PAGE_TYPE_RW));
Expand Down
102 changes: 81 additions & 21 deletions arch/s390/mm/pgtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@

#ifndef CONFIG_64BIT
#define ALLOC_ORDER 1
#define TABLES_PER_PAGE 4
#define FRAG_MASK 15UL
#define SECOND_HALVES 10UL
#else
#define ALLOC_ORDER 2
#define TABLES_PER_PAGE 2
#define FRAG_MASK 3UL
#define SECOND_HALVES 2UL
#endif

unsigned long *crst_table_alloc(struct mm_struct *mm, int noexec)
Expand All @@ -45,13 +51,20 @@ unsigned long *crst_table_alloc(struct mm_struct *mm, int noexec)
}
page->index = page_to_phys(shadow);
}
spin_lock(&mm->page_table_lock);
list_add(&page->lru, &mm->context.crst_list);
spin_unlock(&mm->page_table_lock);
return (unsigned long *) page_to_phys(page);
}

void crst_table_free(unsigned long *table)
void crst_table_free(struct mm_struct *mm, unsigned long *table)
{
unsigned long *shadow = get_shadow_table(table);
struct page *page = virt_to_page(table);

spin_lock(&mm->page_table_lock);
list_del(&page->lru);
spin_unlock(&mm->page_table_lock);
if (shadow)
free_pages((unsigned long) shadow, ALLOC_ORDER);
free_pages((unsigned long) table, ALLOC_ORDER);
Expand All @@ -60,37 +73,84 @@ void crst_table_free(unsigned long *table)
/*
* page table entry allocation/free routines.
*/
unsigned long *page_table_alloc(int noexec)
unsigned long *page_table_alloc(struct mm_struct *mm)
{
struct page *page = alloc_page(GFP_KERNEL);
struct page *page;
unsigned long *table;
unsigned long bits;

if (!page)
return NULL;
page->index = 0;
if (noexec) {
struct page *shadow = alloc_page(GFP_KERNEL);
if (!shadow) {
__free_page(page);
bits = mm->context.noexec ? 3UL : 1UL;
spin_lock(&mm->page_table_lock);
page = NULL;
if (!list_empty(&mm->context.pgtable_list)) {
page = list_first_entry(&mm->context.pgtable_list,
struct page, lru);
if ((page->flags & FRAG_MASK) == ((1UL << TABLES_PER_PAGE) - 1))
page = NULL;
}
if (!page) {
spin_unlock(&mm->page_table_lock);
page = alloc_page(GFP_KERNEL|__GFP_REPEAT);
if (!page)
return NULL;
}
table = (unsigned long *) page_to_phys(shadow);
pgtable_page_ctor(page);
page->flags &= ~FRAG_MASK;
table = (unsigned long *) page_to_phys(page);
clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
page->index = (addr_t) table;
spin_lock(&mm->page_table_lock);
list_add(&page->lru, &mm->context.pgtable_list);
}
pgtable_page_ctor(page);
table = (unsigned long *) page_to_phys(page);
clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
while (page->flags & bits) {
table += 256;
bits <<= 1;
}
page->flags |= bits;
if ((page->flags & FRAG_MASK) == ((1UL << TABLES_PER_PAGE) - 1))
list_move_tail(&page->lru, &mm->context.pgtable_list);
spin_unlock(&mm->page_table_lock);
return table;
}

void page_table_free(unsigned long *table)
void page_table_free(struct mm_struct *mm, unsigned long *table)
{
unsigned long *shadow = get_shadow_pte(table);
struct page *page;
unsigned long bits;

pgtable_page_dtor(virt_to_page(table));
if (shadow)
free_page((unsigned long) shadow);
free_page((unsigned long) table);
bits = mm->context.noexec ? 3UL : 1UL;
bits <<= (__pa(table) & (PAGE_SIZE - 1)) / 256 / sizeof(unsigned long);
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
spin_lock(&mm->page_table_lock);
page->flags ^= bits;
if (page->flags & FRAG_MASK) {
/* Page now has some free pgtable fragments. */
list_move(&page->lru, &mm->context.pgtable_list);
page = NULL;
} else
/* All fragments of the 4K page have been freed. */
list_del(&page->lru);
spin_unlock(&mm->page_table_lock);
if (page) {
pgtable_page_dtor(page);
__free_page(page);
}
}

void disable_noexec(struct mm_struct *mm, struct task_struct *tsk)
{
struct page *page;

spin_lock(&mm->page_table_lock);
/* Free shadow region and segment tables. */
list_for_each_entry(page, &mm->context.crst_list, lru)
if (page->index) {
free_pages((unsigned long) page->index, ALLOC_ORDER);
page->index = 0;
}
/* "Free" second halves of page tables. */
list_for_each_entry(page, &mm->context.pgtable_list, lru)
page->flags &= ~SECOND_HALVES;
spin_unlock(&mm->page_table_lock);
mm->context.noexec = 0;
update_mm(mm, tsk);
}
14 changes: 11 additions & 3 deletions arch/s390/mm/vmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,18 @@ static inline pmd_t *vmem_pmd_alloc(void)
return pmd;
}

static inline pte_t *vmem_pte_alloc(void)
static pte_t __init_refok *vmem_pte_alloc(void)
{
pte_t *pte = vmem_alloc_pages(0);
pte_t *pte;

if (slab_is_available())
pte = (pte_t *) page_table_alloc(&init_mm);
else
pte = alloc_bootmem(PTRS_PER_PTE * sizeof(pte_t));
if (!pte)
return NULL;
clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY, PAGE_SIZE);
clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY,
PTRS_PER_PTE * sizeof(pte_t));
return pte;
}

Expand Down Expand Up @@ -360,6 +365,9 @@ void __init vmem_map_init(void)
{
int i;

INIT_LIST_HEAD(&init_mm.context.crst_list);
INIT_LIST_HEAD(&init_mm.context.pgtable_list);
init_mm.context.noexec = 0;
NODE_DATA(0)->node_mem_map = VMEM_MAP;
for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++)
vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size);
Expand Down
13 changes: 13 additions & 0 deletions include/asm-s390/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ typedef s390_regs elf_gregset_t;

#include <linux/sched.h> /* for task_struct */
#include <asm/system.h> /* for save_access_regs */
#include <asm/mmu_context.h>

/*
* This is used to ensure we don't load something for the wrong architecture.
Expand Down Expand Up @@ -214,4 +215,16 @@ do { \
} while (0)
#endif /* __s390x__ */

/*
* An executable for which elf_read_implies_exec() returns TRUE will
* have the READ_IMPLIES_EXEC personality flag set automatically.
*/
#define elf_read_implies_exec(ex, executable_stack) \
({ \
if (current->mm->context.noexec && \
executable_stack != EXSTACK_DISABLE_X) \
disable_noexec(current->mm, current); \
current->mm->context.noexec == 0; \
})

#endif
8 changes: 6 additions & 2 deletions include/asm-s390/mmu.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#ifndef __MMU_H
#define __MMU_H

/* Default "unsigned long" context */
typedef unsigned long mm_context_t;
typedef struct {
struct list_head crst_list;
struct list_head pgtable_list;
unsigned long asce_bits;
int noexec;
} mm_context_t;

#endif
14 changes: 9 additions & 5 deletions include/asm-s390/mmu_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@
#define __S390_MMU_CONTEXT_H

#include <asm/pgalloc.h>
#include <asm/uaccess.h>
#include <asm-generic/mm_hooks.h>

static inline int init_new_context(struct task_struct *tsk,
struct mm_struct *mm)
{
mm->context = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
mm->context.asce_bits = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
#ifdef CONFIG_64BIT
mm->context |= _ASCE_TYPE_REGION3;
mm->context.asce_bits |= _ASCE_TYPE_REGION3;
#endif
mm->context.noexec = s390_noexec;
return 0;
}

Expand All @@ -32,11 +34,13 @@ static inline int init_new_context(struct task_struct *tsk,

static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
{
S390_lowcore.user_asce = mm->context | __pa(mm->pgd);
pgd_t *pgd = mm->pgd;

S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd);
if (switch_amode) {
/* Load primary space page table origin. */
pgd_t *shadow_pgd = get_shadow_table(mm->pgd) ? : mm->pgd;
S390_lowcore.user_exec_asce = mm->context | __pa(shadow_pgd);
pgd = mm->context.noexec ? get_shadow_table(pgd) : pgd;
S390_lowcore.user_exec_asce = mm->context.asce_bits | __pa(pgd);
asm volatile(LCTL_OPCODE" 1,1,%0\n"
: : "m" (S390_lowcore.user_exec_asce) );
} else
Expand Down
36 changes: 5 additions & 31 deletions include/asm-s390/page.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,43 +74,17 @@ static inline void copy_page(void *to, void *from)

typedef struct { unsigned long pgprot; } pgprot_t;
typedef struct { unsigned long pte; } pte_t;

#define pte_val(x) ((x).pte)
#define pgprot_val(x) ((x).pgprot)

#ifndef __s390x__

typedef struct { unsigned long pmd; } pmd_t;
typedef struct { unsigned long pud; } pud_t;
typedef struct {
unsigned long pgd0;
unsigned long pgd1;
unsigned long pgd2;
unsigned long pgd3;
} pgd_t;

#define pmd_val(x) ((x).pmd)
#define pud_val(x) ((x).pud)
#define pgd_val(x) ((x).pgd0)

#else /* __s390x__ */

typedef struct {
unsigned long pmd0;
unsigned long pmd1;
} pmd_t;
typedef struct { unsigned long pud; } pud_t;
typedef struct { unsigned long pgd; } pgd_t;
typedef pte_t *pgtable_t;

#define pmd_val(x) ((x).pmd0)
#define pmd_val1(x) ((x).pmd1)
#define pgprot_val(x) ((x).pgprot)
#define pte_val(x) ((x).pte)
#define pmd_val(x) ((x).pmd)
#define pud_val(x) ((x).pud)
#define pgd_val(x) ((x).pgd)

#endif /* __s390x__ */

typedef struct page *pgtable_t;

#define __pte(x) ((pte_t) { (x) } )
#define __pmd(x) ((pmd_t) { (x) } )
#define __pgd(x) ((pgd_t) { (x) } )
Expand Down Expand Up @@ -167,7 +141,7 @@ static inline int pfn_valid(unsigned long pfn)
#define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT)
#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)

#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \
#define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | \
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)

#include <asm-generic/memory_model.h>
Expand Down
Loading

0 comments on commit 146e4b3

Please sign in to comment.