You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
unicore32 core architecture: mm related: fault handling
This patch implements fault handling of memory management. Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn> Reviewed-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* linux/arch/unicore32/include/asm/mmu.h
|
||||
*
|
||||
* Code specific to PKUnity SoC and UniCore ISA
|
||||
*
|
||||
* Copyright (C) 2001-2010 GUAN Xue-tao
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __UNICORE_MMU_H__
|
||||
#define __UNICORE_MMU_H__
|
||||
|
||||
typedef unsigned long mm_context_t;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* linux/arch/unicore32/include/asm/mmu_context.h
|
||||
*
|
||||
* Code specific to PKUnity SoC and UniCore ISA
|
||||
*
|
||||
* Copyright (C) 2001-2010 GUAN Xue-tao
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __UNICORE_MMU_CONTEXT_H__
|
||||
#define __UNICORE_MMU_CONTEXT_H__
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpu-single.h>
|
||||
|
||||
#define init_new_context(tsk, mm) 0
|
||||
|
||||
#define destroy_context(mm) do { } while (0)
|
||||
|
||||
/*
|
||||
* This is called when "tsk" is about to enter lazy TLB mode.
|
||||
*
|
||||
* mm: describes the currently active mm context
|
||||
* tsk: task which is entering lazy tlb
|
||||
* cpu: cpu number which is entering lazy tlb
|
||||
*
|
||||
* tsk->mm will be NULL
|
||||
*/
|
||||
static inline void
|
||||
enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the actual mm switch as far as the scheduler
|
||||
* is concerned. No registers are touched. We avoid
|
||||
* calling the CPU specific function when the mm hasn't
|
||||
* actually changed.
|
||||
*/
|
||||
static inline void
|
||||
switch_mm(struct mm_struct *prev, struct mm_struct *next,
|
||||
struct task_struct *tsk)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
|
||||
if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next)
|
||||
cpu_switch_mm(next->pgd, next);
|
||||
}
|
||||
|
||||
#define deactivate_mm(tsk, mm) do { } while (0)
|
||||
#define activate_mm(prev, next) switch_mm(prev, next, NULL)
|
||||
|
||||
/*
|
||||
* We are inserting a "fake" vma for the user-accessible vector page so
|
||||
* gdb and friends can get to it through ptrace and /proc/<pid>/mem.
|
||||
* But we also want to remove it before the generic code gets to see it
|
||||
* during process exit or the unmapping of it would cause total havoc.
|
||||
* (the macro is used as remove_vma() is static to mm/mmap.c)
|
||||
*/
|
||||
#define arch_exit_mmap(mm) \
|
||||
do { \
|
||||
struct vm_area_struct *high_vma = find_vma(mm, 0xffff0000); \
|
||||
if (high_vma) { \
|
||||
BUG_ON(high_vma->vm_next); /* it should be last */ \
|
||||
if (high_vma->vm_prev) \
|
||||
high_vma->vm_prev->vm_next = NULL; \
|
||||
else \
|
||||
mm->mmap = NULL; \
|
||||
rb_erase(&high_vma->vm_rb, &mm->mm_rb); \
|
||||
mm->mmap_cache = NULL; \
|
||||
mm->map_count--; \
|
||||
remove_vma(high_vma); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static inline void arch_dup_mmap(struct mm_struct *oldmm,
|
||||
struct mm_struct *mm)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* linux/arch/unicore32/include/asm/pgalloc.h
|
||||
*
|
||||
* Code specific to PKUnity SoC and UniCore ISA
|
||||
*
|
||||
* Copyright (C) 2001-2010 GUAN Xue-tao
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __UNICORE_PGALLOC_H__
|
||||
#define __UNICORE_PGALLOC_H__
|
||||
|
||||
#include <asm/pgtable-hwdef.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
#define check_pgt_cache() do { } while (0)
|
||||
|
||||
#define _PAGE_USER_TABLE (PMD_TYPE_TABLE | PMD_PRESENT)
|
||||
#define _PAGE_KERNEL_TABLE (PMD_TYPE_TABLE | PMD_PRESENT)
|
||||
|
||||
extern pgd_t *get_pgd_slow(struct mm_struct *mm);
|
||||
extern void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd);
|
||||
|
||||
#define pgd_alloc(mm) get_pgd_slow(mm)
|
||||
#define pgd_free(mm, pgd) free_pgd_slow(mm, pgd)
|
||||
|
||||
#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO)
|
||||
|
||||
/*
|
||||
* Allocate one PTE table.
|
||||
*/
|
||||
static inline pte_t *
|
||||
pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
|
||||
{
|
||||
pte_t *pte;
|
||||
|
||||
pte = (pte_t *)__get_free_page(PGALLOC_GFP);
|
||||
if (pte)
|
||||
clean_dcache_area(pte, PTRS_PER_PTE * sizeof(pte_t));
|
||||
|
||||
return pte;
|
||||
}
|
||||
|
||||
static inline pgtable_t
|
||||
pte_alloc_one(struct mm_struct *mm, unsigned long addr)
|
||||
{
|
||||
struct page *pte;
|
||||
|
||||
pte = alloc_pages(PGALLOC_GFP, 0);
|
||||
if (pte) {
|
||||
if (!PageHighMem(pte)) {
|
||||
void *page = page_address(pte);
|
||||
clean_dcache_area(page, PTRS_PER_PTE * sizeof(pte_t));
|
||||
}
|
||||
pgtable_page_ctor(pte);
|
||||
}
|
||||
|
||||
return pte;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free one PTE table.
|
||||
*/
|
||||
static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
|
||||
{
|
||||
if (pte)
|
||||
free_page((unsigned long)pte);
|
||||
}
|
||||
|
||||
static inline void pte_free(struct mm_struct *mm, pgtable_t pte)
|
||||
{
|
||||
pgtable_page_dtor(pte);
|
||||
__free_page(pte);
|
||||
}
|
||||
|
||||
static inline void __pmd_populate(pmd_t *pmdp, unsigned long pmdval)
|
||||
{
|
||||
set_pmd(pmdp, __pmd(pmdval));
|
||||
flush_pmd_entry(pmdp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Populate the pmdp entry with a pointer to the pte. This pmd is part
|
||||
* of the mm address space.
|
||||
*/
|
||||
static inline void
|
||||
pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep)
|
||||
{
|
||||
unsigned long pte_ptr = (unsigned long)ptep;
|
||||
|
||||
/*
|
||||
* The pmd must be loaded with the physical
|
||||
* address of the PTE table
|
||||
*/
|
||||
__pmd_populate(pmdp, __pa(pte_ptr) | _PAGE_KERNEL_TABLE);
|
||||
}
|
||||
|
||||
static inline void
|
||||
pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep)
|
||||
{
|
||||
__pmd_populate(pmdp,
|
||||
page_to_pfn(ptep) << PAGE_SHIFT | _PAGE_USER_TABLE);
|
||||
}
|
||||
#define pmd_pgtable(pmd) pmd_page(pmd)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* linux/arch/unicore32/include/asm/pgtable-hwdef.h
|
||||
*
|
||||
* Code specific to PKUnity SoC and UniCore ISA
|
||||
*
|
||||
* Copyright (C) 2001-2010 GUAN Xue-tao
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __UNICORE_PGTABLE_HWDEF_H__
|
||||
#define __UNICORE_PGTABLE_HWDEF_H__
|
||||
|
||||
/*
|
||||
* Hardware page table definitions.
|
||||
*
|
||||
* + Level 1 descriptor (PMD)
|
||||
* - common
|
||||
*/
|
||||
#define PMD_TYPE_MASK (3 << 0)
|
||||
#define PMD_TYPE_TABLE (0 << 0)
|
||||
/*#define PMD_TYPE_LARGE (1 << 0) */
|
||||
#define PMD_TYPE_INVALID (2 << 0)
|
||||
#define PMD_TYPE_SECT (3 << 0)
|
||||
|
||||
#define PMD_PRESENT (1 << 2)
|
||||
#define PMD_YOUNG (1 << 3)
|
||||
|
||||
/*#define PMD_SECT_DIRTY (1 << 4) */
|
||||
#define PMD_SECT_CACHEABLE (1 << 5)
|
||||
#define PMD_SECT_EXEC (1 << 6)
|
||||
#define PMD_SECT_WRITE (1 << 7)
|
||||
#define PMD_SECT_READ (1 << 8)
|
||||
|
||||
/*
|
||||
* + Level 2 descriptor (PTE)
|
||||
* - common
|
||||
*/
|
||||
#define PTE_TYPE_MASK (3 << 0)
|
||||
#define PTE_TYPE_SMALL (0 << 0)
|
||||
#define PTE_TYPE_MIDDLE (1 << 0)
|
||||
#define PTE_TYPE_LARGE (2 << 0)
|
||||
#define PTE_TYPE_INVALID (3 << 0)
|
||||
|
||||
#define PTE_PRESENT (1 << 2)
|
||||
#define PTE_FILE (1 << 3) /* only when !PRESENT */
|
||||
#define PTE_YOUNG (1 << 3)
|
||||
#define PTE_DIRTY (1 << 4)
|
||||
#define PTE_CACHEABLE (1 << 5)
|
||||
#define PTE_EXEC (1 << 6)
|
||||
#define PTE_WRITE (1 << 7)
|
||||
#define PTE_READ (1 << 8)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* linux/arch/unicore32/include/asm/pgtable.h
|
||||
*
|
||||
* Code specific to PKUnity SoC and UniCore ISA
|
||||
*
|
||||
* Copyright (C) 2001-2010 GUAN Xue-tao
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __UNICORE_PGTABLE_H__
|
||||
#define __UNICORE_PGTABLE_H__
|
||||
|
||||
#include <asm-generic/pgtable-nopmd.h>
|
||||
#include <asm/cpu-single.h>
|
||||
|
||||
#include <asm/memory.h>
|
||||
#include <asm/pgtable-hwdef.h>
|
||||
|
||||
/*
|
||||
* Just any arbitrary offset to the start of the vmalloc VM area: the
|
||||
* current 8MB value just means that there will be a 8MB "hole" after the
|
||||
* physical memory until the kernel virtual memory starts. That means that
|
||||
* any out-of-bounds memory accesses will hopefully be caught.
|
||||
* The vmalloc() routines leaves a hole of 4kB between each vmalloced
|
||||
* area for the same reason. ;)
|
||||
*
|
||||
* Note that platforms may override VMALLOC_START, but they must provide
|
||||
* VMALLOC_END. VMALLOC_END defines the (exclusive) limit of this space,
|
||||
* which may not overlap IO space.
|
||||
*/
|
||||
#ifndef VMALLOC_START
|
||||
#define VMALLOC_OFFSET SZ_8M
|
||||
#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) \
|
||||
& ~(VMALLOC_OFFSET-1))
|
||||
#define VMALLOC_END (0xff000000UL)
|
||||
#endif
|
||||
|
||||
#define PTRS_PER_PTE 1024
|
||||
#define PTRS_PER_PGD 1024
|
||||
|
||||
/*
|
||||
* PGDIR_SHIFT determines what a third-level page table entry can map
|
||||
*/
|
||||
#define PGDIR_SHIFT 22
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
extern void __pte_error(const char *file, int line, unsigned long val);
|
||||
extern void __pgd_error(const char *file, int line, unsigned long val);
|
||||
|
||||
#define pte_ERROR(pte) __pte_error(__FILE__, __LINE__, pte_val(pte))
|
||||
#define pgd_ERROR(pgd) __pgd_error(__FILE__, __LINE__, pgd_val(pgd))
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
|
||||
#define PGDIR_MASK (~(PGDIR_SIZE-1))
|
||||
|
||||
/*
|
||||
* This is the lowest virtual address we can permit any user space
|
||||
* mapping to be mapped at. This is particularly important for
|
||||
* non-high vector CPUs.
|
||||
*/
|
||||
#define FIRST_USER_ADDRESS PAGE_SIZE
|
||||
|
||||
#define FIRST_USER_PGD_NR 1
|
||||
#define USER_PTRS_PER_PGD ((TASK_SIZE/PGDIR_SIZE) - FIRST_USER_PGD_NR)
|
||||
|
||||
/*
|
||||
* section address mask and size definitions.
|
||||
*/
|
||||
#define SECTION_SHIFT 22
|
||||
#define SECTION_SIZE (1UL << SECTION_SHIFT)
|
||||
#define SECTION_MASK (~(SECTION_SIZE-1))
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
/*
|
||||
* The pgprot_* and protection_map entries will be fixed up in runtime
|
||||
* to include the cachable bits based on memory policy, as well as any
|
||||
* architecture dependent bits.
|
||||
*/
|
||||
#define _PTE_DEFAULT (PTE_PRESENT | PTE_YOUNG | PTE_CACHEABLE)
|
||||
|
||||
extern pgprot_t pgprot_user;
|
||||
extern pgprot_t pgprot_kernel;
|
||||
|
||||
#define PAGE_NONE pgprot_user
|
||||
#define PAGE_SHARED __pgprot(pgprot_val(pgprot_user | PTE_READ \
|
||||
| PTE_WRITE)
|
||||
#define PAGE_SHARED_EXEC __pgprot(pgprot_val(pgprot_user | PTE_READ \
|
||||
| PTE_WRITE \
|
||||
| PTE_EXEC)
|
||||
#define PAGE_COPY __pgprot(pgprot_val(pgprot_user | PTE_READ)
|
||||
#define PAGE_COPY_EXEC __pgprot(pgprot_val(pgprot_user | PTE_READ \
|
||||
| PTE_EXEC)
|
||||
#define PAGE_READONLY __pgprot(pgprot_val(pgprot_user | PTE_READ)
|
||||
#define PAGE_READONLY_EXEC __pgprot(pgprot_val(pgprot_user | PTE_READ \
|
||||
| PTE_EXEC)
|
||||
#define PAGE_KERNEL pgprot_kernel
|
||||
#define PAGE_KERNEL_EXEC __pgprot(pgprot_val(pgprot_kernel | PTE_EXEC))
|
||||
|
||||
#define __PAGE_NONE __pgprot(_PTE_DEFAULT)
|
||||
#define __PAGE_SHARED __pgprot(_PTE_DEFAULT | PTE_READ \
|
||||
| PTE_WRITE)
|
||||
#define __PAGE_SHARED_EXEC __pgprot(_PTE_DEFAULT | PTE_READ \
|
||||
| PTE_WRITE \
|
||||
| PTE_EXEC)
|
||||
#define __PAGE_COPY __pgprot(_PTE_DEFAULT | PTE_READ)
|
||||
#define __PAGE_COPY_EXEC __pgprot(_PTE_DEFAULT | PTE_READ \
|
||||
| PTE_EXEC)
|
||||
#define __PAGE_READONLY __pgprot(_PTE_DEFAULT | PTE_READ)
|
||||
#define __PAGE_READONLY_EXEC __pgprot(_PTE_DEFAULT | PTE_READ \
|
||||
| PTE_EXEC)
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
/*
|
||||
* The table below defines the page protection levels that we insert into our
|
||||
* Linux page table version. These get translated into the best that the
|
||||
* architecture can perform. Note that on UniCore hardware:
|
||||
* 1) We cannot do execute protection
|
||||
* 2) If we could do execute protection, then read is implied
|
||||
* 3) write implies read permissions
|
||||
*/
|
||||
#define __P000 __PAGE_NONE
|
||||
#define __P001 __PAGE_READONLY
|
||||
#define __P010 __PAGE_COPY
|
||||
#define __P011 __PAGE_COPY
|
||||
#define __P100 __PAGE_READONLY_EXEC
|
||||
#define __P101 __PAGE_READONLY_EXEC
|
||||
#define __P110 __PAGE_COPY_EXEC
|
||||
#define __P111 __PAGE_COPY_EXEC
|
||||
|
||||
#define __S000 __PAGE_NONE
|
||||
#define __S001 __PAGE_READONLY
|
||||
#define __S010 __PAGE_SHARED
|
||||
#define __S011 __PAGE_SHARED
|
||||
#define __S100 __PAGE_READONLY_EXEC
|
||||
#define __S101 __PAGE_READONLY_EXEC
|
||||
#define __S110 __PAGE_SHARED_EXEC
|
||||
#define __S111 __PAGE_SHARED_EXEC
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
/*
|
||||
* ZERO_PAGE is a global shared page that is always zero: used
|
||||
* for zero-mapped memory areas etc..
|
||||
*/
|
||||
extern struct page *empty_zero_page;
|
||||
#define ZERO_PAGE(vaddr) (empty_zero_page)
|
||||
|
||||
#define pte_pfn(pte) (pte_val(pte) >> PAGE_SHIFT)
|
||||
#define pfn_pte(pfn, prot) (__pte(((pfn) << PAGE_SHIFT) \
|
||||
| pgprot_val(prot)))
|
||||
|
||||
#define pte_none(pte) (!pte_val(pte))
|
||||
#define pte_clear(mm, addr, ptep) set_pte(ptep, __pte(0))
|
||||
#define pte_page(pte) (pfn_to_page(pte_pfn(pte)))
|
||||
#define pte_offset_kernel(dir, addr) (pmd_page_vaddr(*(dir)) \
|
||||
+ __pte_index(addr))
|
||||
|
||||
#define pte_offset_map(dir, addr) (pmd_page_vaddr(*(dir)) \
|
||||
+ __pte_index(addr))
|
||||
#define pte_unmap(pte) do { } while (0)
|
||||
|
||||
#define set_pte(ptep, pte) cpu_set_pte(ptep, pte)
|
||||
|
||||
#define set_pte_at(mm, addr, ptep, pteval) \
|
||||
do { \
|
||||
set_pte(ptep, pteval); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* The following only work if pte_present() is true.
|
||||
* Undefined behaviour if not..
|
||||
*/
|
||||
#define pte_present(pte) (pte_val(pte) & PTE_PRESENT)
|
||||
#define pte_write(pte) (pte_val(pte) & PTE_WRITE)
|
||||
#define pte_dirty(pte) (pte_val(pte) & PTE_DIRTY)
|
||||
#define pte_young(pte) (pte_val(pte) & PTE_YOUNG)
|
||||
#define pte_exec(pte) (pte_val(pte) & PTE_EXEC)
|
||||
#define pte_special(pte) (0)
|
||||
|
||||
#define PTE_BIT_FUNC(fn, op) \
|
||||
static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
|
||||
|
||||
PTE_BIT_FUNC(wrprotect, &= ~PTE_WRITE);
|
||||
PTE_BIT_FUNC(mkwrite, |= PTE_WRITE);
|
||||
PTE_BIT_FUNC(mkclean, &= ~PTE_DIRTY);
|
||||
PTE_BIT_FUNC(mkdirty, |= PTE_DIRTY);
|
||||
PTE_BIT_FUNC(mkold, &= ~PTE_YOUNG);
|
||||
PTE_BIT_FUNC(mkyoung, |= PTE_YOUNG);
|
||||
|
||||
static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
|
||||
|
||||
/*
|
||||
* Mark the prot value as uncacheable.
|
||||
*/
|
||||
#define pgprot_noncached(prot) \
|
||||
__pgprot(pgprot_val(prot) & ~PTE_CACHEABLE)
|
||||
#define pgprot_writecombine(prot) \
|
||||
__pgprot(pgprot_val(prot) & ~PTE_CACHEABLE)
|
||||
#define pgprot_dmacoherent(prot) \
|
||||
__pgprot(pgprot_val(prot) & ~PTE_CACHEABLE)
|
||||
|
||||
#define pmd_none(pmd) (!pmd_val(pmd))
|
||||
#define pmd_present(pmd) (pmd_val(pmd) & PMD_PRESENT)
|
||||
#define pmd_bad(pmd) (((pmd_val(pmd) & \
|
||||
(PMD_PRESENT | PMD_TYPE_MASK)) \
|
||||
!= (PMD_PRESENT | PMD_TYPE_TABLE)))
|
||||
|
||||
#define set_pmd(pmdpd, pmdval) \
|
||||
do { \
|
||||
*(pmdpd) = pmdval; \
|
||||
} while (0)
|
||||
|
||||
#define pmd_clear(pmdp) \
|
||||
do { \
|
||||
set_pmd(pmdp, __pmd(0));\
|
||||
clean_pmd_entry(pmdp); \
|
||||
} while (0)
|
||||
|
||||
#define pmd_page_vaddr(pmd) ((pte_t *)__va(pmd_val(pmd) & PAGE_MASK))
|
||||
#define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd)))
|
||||
|
||||
/*
|
||||
* Conversion functions: convert a page and protection to a page entry,
|
||||
* and a page entry and page directory to the page they refer to.
|
||||
*/
|
||||
#define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot)
|
||||
|
||||
/* to find an entry in a page-table-directory */
|
||||
#define pgd_index(addr) ((addr) >> PGDIR_SHIFT)
|
||||
|
||||
#define pgd_offset(mm, addr) ((mm)->pgd+pgd_index(addr))
|
||||
|
||||
/* to find an entry in a kernel page-table-directory */
|
||||
#define pgd_offset_k(addr) pgd_offset(&init_mm, addr)
|
||||
|
||||
/* Find an entry in the third-level page table.. */
|
||||
#define __pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
|
||||
|
||||
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
||||
{
|
||||
const unsigned long mask = PTE_EXEC | PTE_WRITE | PTE_READ;
|
||||
pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask);
|
||||
return pte;
|
||||
}
|
||||
|
||||
extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
|
||||
|
||||
/*
|
||||
* Encode and decode a swap entry. Swap entries are stored in the Linux
|
||||
* page tables as follows:
|
||||
*
|
||||
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
||||
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
* <--------------- offset --------------> <--- type --> 0 0 0 0 0
|
||||
*
|
||||
* This gives us up to 127 swap files and 32GB per swap file. Note that
|
||||
* the offset field is always non-zero.
|
||||
*/
|
||||
#define __SWP_TYPE_SHIFT 5
|
||||
#define __SWP_TYPE_BITS 7
|
||||
#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1)
|
||||
#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
|
||||
|
||||
#define __swp_type(x) (((x).val >> __SWP_TYPE_SHIFT) \
|
||||
& __SWP_TYPE_MASK)
|
||||
#define __swp_offset(x) ((x).val >> __SWP_OFFSET_SHIFT)
|
||||
#define __swp_entry(type, offset) ((swp_entry_t) { \
|
||||
((type) << __SWP_TYPE_SHIFT) | \
|
||||
((offset) << __SWP_OFFSET_SHIFT) })
|
||||
|
||||
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
|
||||
#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val })
|
||||
|
||||
/*
|
||||
* It is an error for the kernel to have more swap files than we can
|
||||
* encode in the PTEs. This ensures that we know when MAX_SWAPFILES
|
||||
* is increased beyond what we presently support.
|
||||
*/
|
||||
#define MAX_SWAPFILES_CHECK() \
|
||||
BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS)
|
||||
|
||||
/*
|
||||
* Encode and decode a file entry. File entries are stored in the Linux
|
||||
* page tables as follows:
|
||||
*
|
||||
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
|
||||
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
* <----------------------- offset ----------------------> 1 0 0 0
|
||||
*/
|
||||
#define pte_file(pte) (pte_val(pte) & PTE_FILE)
|
||||
#define pte_to_pgoff(x) (pte_val(x) >> 4)
|
||||
#define pgoff_to_pte(x) __pte(((x) << 4) | PTE_FILE)
|
||||
|
||||
#define PTE_FILE_MAX_BITS 28
|
||||
|
||||
/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
|
||||
/* FIXME: this is not correct */
|
||||
#define kern_addr_valid(addr) (1)
|
||||
|
||||
#include <asm-generic/pgtable.h>
|
||||
|
||||
/*
|
||||
* remap a physical page `pfn' of size `size' with page protection `prot'
|
||||
* into virtual address `from'
|
||||
*/
|
||||
#define io_remap_pfn_range(vma, from, pfn, size, prot) \
|
||||
remap_pfn_range(vma, from, pfn, size, prot)
|
||||
|
||||
#define pgtable_cache_init() do { } while (0)
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#endif /* __UNICORE_PGTABLE_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* linux/arch/unicore32/mm/extable.c
|
||||
*
|
||||
* Code specific to PKUnity SoC and UniCore ISA
|
||||
*
|
||||
* Copyright (C) 2001-2010 GUAN Xue-tao
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
int fixup_exception(struct pt_regs *regs)
|
||||
{
|
||||
const struct exception_table_entry *fixup;
|
||||
|
||||
fixup = search_exception_tables(instruction_pointer(regs));
|
||||
if (fixup)
|
||||
regs->UCreg_pc = fixup->fixup;
|
||||
|
||||
return fixup != NULL;
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* linux/arch/unicore32/mm/fault.c
|
||||
*
|
||||
* Code specific to PKUnity SoC and UniCore ISA
|
||||
*
|
||||
* Copyright (C) 2001-2010 GUAN Xue-tao
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/page-flags.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
/*
|
||||
* Fault status register encodings. We steal bit 31 for our own purposes.
|
||||
*/
|
||||
#define FSR_LNX_PF (1 << 31)
|
||||
|
||||
static inline int fsr_fs(unsigned int fsr)
|
||||
{
|
||||
/* xyabcde will be abcde+xy */
|
||||
return (fsr & 31) + ((fsr & (3 << 5)) >> 5);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is useful to dump out the page tables associated with
|
||||
* 'addr' in mm 'mm'.
|
||||
*/
|
||||
void show_pte(struct mm_struct *mm, unsigned long addr)
|
||||
{
|
||||
pgd_t *pgd;
|
||||
|
||||
if (!mm)
|
||||
mm = &init_mm;
|
||||
|
||||
printk(KERN_ALERT "pgd = %p\n", mm->pgd);
|
||||
pgd = pgd_offset(mm, addr);
|
||||
printk(KERN_ALERT "[%08lx] *pgd=%08lx", addr, pgd_val(*pgd));
|
||||
|
||||
do {
|
||||
pmd_t *pmd;
|
||||
pte_t *pte;
|
||||
|
||||
if (pgd_none(*pgd))
|
||||
break;
|
||||
|
||||
if (pgd_bad(*pgd)) {
|
||||
printk("(bad)");
|
||||
break;
|
||||
}
|
||||
|
||||
pmd = pmd_offset((pud_t *) pgd, addr);
|
||||
if (PTRS_PER_PMD != 1)
|
||||
printk(", *pmd=%08lx", pmd_val(*pmd));
|
||||
|
||||
if (pmd_none(*pmd))
|
||||
break;
|
||||
|
||||
if (pmd_bad(*pmd)) {
|
||||
printk("(bad)");
|
||||
break;
|
||||
}
|
||||
|
||||
/* We must not map this if we have highmem enabled */
|
||||
if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT)))
|
||||
break;
|
||||
|
||||
pte = pte_offset_map(pmd, addr);
|
||||
printk(", *pte=%08lx", pte_val(*pte));
|
||||
pte_unmap(pte);
|
||||
} while (0);
|
||||
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Oops. The kernel tried to access some page that wasn't present.
|
||||
*/
|
||||
static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr,
|
||||
unsigned int fsr, struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* Are we prepared to handle this kernel fault?
|
||||
*/
|
||||
if (fixup_exception(regs))
|
||||
return;
|
||||
|
||||
/*
|
||||
* No handler, we'll have to terminate things with extreme prejudice.
|
||||
*/
|
||||
bust_spinlocks(1);
|
||||
printk(KERN_ALERT
|
||||
"Unable to handle kernel %s at virtual address %08lx\n",
|
||||
(addr < PAGE_SIZE) ? "NULL pointer dereference" :
|
||||
"paging request", addr);
|
||||
|
||||
show_pte(mm, addr);
|
||||
die("Oops", regs, fsr);
|
||||
bust_spinlocks(0);
|
||||
do_exit(SIGKILL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Something tried to access memory that isn't in our memory map..
|
||||
* User mode accesses just cause a SIGSEGV
|
||||
*/
|
||||
static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
|
||||
unsigned int fsr, unsigned int sig, int code,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct siginfo si;
|
||||
|
||||
tsk->thread.address = addr;
|
||||
tsk->thread.error_code = fsr;
|
||||
tsk->thread.trap_no = 14;
|
||||
si.si_signo = sig;
|
||||
si.si_errno = 0;
|
||||
si.si_code = code;
|
||||
si.si_addr = (void __user *)addr;
|
||||
force_sig_info(sig, &si, tsk);
|
||||
}
|
||||
|
||||
void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct mm_struct *mm = tsk->active_mm;
|
||||
|
||||
/*
|
||||
* If we are in kernel mode at this point, we
|
||||
* have no context to handle this fault with.
|
||||
*/
|
||||
if (user_mode(regs))
|
||||
__do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);
|
||||
else
|
||||
__do_kernel_fault(mm, addr, fsr, regs);
|
||||
}
|
||||
|
||||
#define VM_FAULT_BADMAP 0x010000
|
||||
#define VM_FAULT_BADACCESS 0x020000
|
||||
|
||||
/*
|
||||
* Check that the permissions on the VMA allow for the fault which occurred.
|
||||
* If we encountered a write fault, we must have write permission, otherwise
|
||||
* we allow any permission.
|
||||
*/
|
||||
static inline bool access_error(unsigned int fsr, struct vm_area_struct *vma)
|
||||
{
|
||||
unsigned int mask = VM_READ | VM_WRITE | VM_EXEC;
|
||||
|
||||
if (!(fsr ^ 0x12)) /* write? */
|
||||
mask = VM_WRITE;
|
||||
if (fsr & FSR_LNX_PF)
|
||||
mask = VM_EXEC;
|
||||
|
||||
return vma->vm_flags & mask ? false : true;
|
||||
}
|
||||
|
||||
static int __do_pf(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
|
||||
struct task_struct *tsk)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
int fault;
|
||||
|
||||
vma = find_vma(mm, addr);
|
||||
fault = VM_FAULT_BADMAP;
|
||||
if (unlikely(!vma))
|
||||
goto out;
|
||||
if (unlikely(vma->vm_start > addr))
|
||||
goto check_stack;
|
||||
|
||||
/*
|
||||
* Ok, we have a good vm_area for this
|
||||
* memory access, so we can handle it.
|
||||
*/
|
||||
good_area:
|
||||
if (access_error(fsr, vma)) {
|
||||
fault = VM_FAULT_BADACCESS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If for any reason at all we couldn't handle the fault, make
|
||||
* sure we exit gracefully rather than endlessly redo the fault.
|
||||
*/
|
||||
fault = handle_mm_fault(mm, vma, addr & PAGE_MASK,
|
||||
(!(fsr ^ 0x12)) ? FAULT_FLAG_WRITE : 0);
|
||||
if (unlikely(fault & VM_FAULT_ERROR))
|
||||
return fault;
|
||||
if (fault & VM_FAULT_MAJOR)
|
||||
tsk->maj_flt++;
|
||||
else
|
||||
tsk->min_flt++;
|
||||
return fault;
|
||||
|
||||
check_stack:
|
||||
if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
|
||||
goto good_area;
|
||||
out:
|
||||
return fault;
|
||||
}
|
||||
|
||||
static int do_pf(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
struct mm_struct *mm;
|
||||
int fault, sig, code;
|
||||
|
||||
tsk = current;
|
||||
mm = tsk->mm;
|
||||
|
||||
/*
|
||||
* If we're in an interrupt or have no user
|
||||
* context, we must not take the fault..
|
||||
*/
|
||||
if (in_atomic() || !mm)
|
||||
goto no_context;
|
||||
|
||||
/*
|
||||
* As per x86, we may deadlock here. However, since the kernel only
|
||||
* validly references user space from well defined areas of the code,
|
||||
* we can bug out early if this is from code which shouldn't.
|
||||
*/
|
||||
if (!down_read_trylock(&mm->mmap_sem)) {
|
||||
if (!user_mode(regs)
|
||||
&& !search_exception_tables(regs->UCreg_pc))
|
||||
goto no_context;
|
||||
down_read(&mm->mmap_sem);
|
||||
} else {
|
||||
/*
|
||||
* The above down_read_trylock() might have succeeded in
|
||||
* which case, we'll have missed the might_sleep() from
|
||||
* down_read()
|
||||
*/
|
||||
might_sleep();
|
||||
#ifdef CONFIG_DEBUG_VM
|
||||
if (!user_mode(regs) &&
|
||||
!search_exception_tables(regs->UCreg_pc))
|
||||
goto no_context;
|
||||
#endif
|
||||
}
|
||||
|
||||
fault = __do_pf(mm, addr, fsr, tsk);
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
/*
|
||||
* Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR
|
||||
*/
|
||||
if (likely(!(fault &
|
||||
(VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS))))
|
||||
return 0;
|
||||
|
||||
if (fault & VM_FAULT_OOM) {
|
||||
/*
|
||||
* We ran out of memory, call the OOM killer, and return to
|
||||
* userspace (which will retry the fault, or kill us if we
|
||||
* got oom-killed)
|
||||
*/
|
||||
pagefault_out_of_memory();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are in kernel mode at this point, we
|
||||
* have no context to handle this fault with.
|
||||
*/
|
||||
if (!user_mode(regs))
|
||||
goto no_context;
|
||||
|
||||
if (fault & VM_FAULT_SIGBUS) {
|
||||
/*
|
||||
* We had some memory, but were unable to
|
||||
* successfully fix up this page fault.
|
||||
*/
|
||||
sig = SIGBUS;
|
||||
code = BUS_ADRERR;
|
||||
} else {
|
||||
/*
|
||||
* Something tried to access memory that
|
||||
* isn't in our memory map..
|
||||
*/
|
||||
sig = SIGSEGV;
|
||||
code = fault == VM_FAULT_BADACCESS ? SEGV_ACCERR : SEGV_MAPERR;
|
||||
}
|
||||
|
||||
__do_user_fault(tsk, addr, fsr, sig, code, regs);
|
||||
return 0;
|
||||
|
||||
no_context:
|
||||
__do_kernel_fault(mm, addr, fsr, regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* First Level Translation Fault Handler
|
||||
*
|
||||
* We enter here because the first level page table doesn't contain
|
||||
* a valid entry for the address.
|
||||
*
|
||||
* If the address is in kernel space (>= TASK_SIZE), then we are
|
||||
* probably faulting in the vmalloc() area.
|
||||
*
|
||||
* If the init_task's first level page tables contains the relevant
|
||||
* entry, we copy the it to this task. If not, we send the process
|
||||
* a signal, fixup the exception, or oops the kernel.
|
||||
*
|
||||
* NOTE! We MUST NOT take any locks for this case. We may be in an
|
||||
* interrupt or a critical region, and should only copy the information
|
||||
* from the master page table, nothing more.
|
||||
*/
|
||||
static int do_ifault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
||||
{
|
||||
unsigned int index;
|
||||
pgd_t *pgd, *pgd_k;
|
||||
pmd_t *pmd, *pmd_k;
|
||||
|
||||
if (addr < TASK_SIZE)
|
||||
return do_pf(addr, fsr, regs);
|
||||
|
||||
if (user_mode(regs))
|
||||
goto bad_area;
|
||||
|
||||
index = pgd_index(addr);
|
||||
|
||||
pgd = cpu_get_pgd() + index;
|
||||
pgd_k = init_mm.pgd + index;
|
||||
|
||||
if (pgd_none(*pgd_k))
|
||||
goto bad_area;
|
||||
|
||||
pmd_k = pmd_offset((pud_t *) pgd_k, addr);
|
||||
pmd = pmd_offset((pud_t *) pgd, addr);
|
||||
|
||||
if (pmd_none(*pmd_k))
|
||||
goto bad_area;
|
||||
|
||||
set_pmd(pmd, *pmd_k);
|
||||
flush_pmd_entry(pmd);
|
||||
return 0;
|
||||
|
||||
bad_area:
|
||||
do_bad_area(addr, fsr, regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This abort handler always returns "fault".
|
||||
*/
|
||||
static int do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_good(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
|
||||
{
|
||||
unsigned int res1, res2;
|
||||
|
||||
printk("dabt exception but no error!\n");
|
||||
|
||||
__asm__ __volatile__(
|
||||
"mff %0,f0\n"
|
||||
"mff %1,f1\n"
|
||||
: "=r"(res1), "=r"(res2)
|
||||
:
|
||||
: "memory");
|
||||
|
||||
printk(KERN_EMERG "r0 :%08x r1 :%08x\n", res1, res2);
|
||||
panic("shut up\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fsr_info {
|
||||
int (*fn) (unsigned long addr, unsigned int fsr, struct pt_regs *regs);
|
||||
int sig;
|
||||
int code;
|
||||
const char *name;
|
||||
} fsr_info[] = {
|
||||
/*
|
||||
* The following are the standard Unicore-I and UniCore-II aborts.
|
||||
*/
|
||||
{ do_good, SIGBUS, 0, "no error" },
|
||||
{ do_bad, SIGBUS, BUS_ADRALN, "alignment exception" },
|
||||
{ do_bad, SIGBUS, BUS_OBJERR, "external exception" },
|
||||
{ do_bad, SIGBUS, 0, "burst operation" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 00100" },
|
||||
{ do_ifault, SIGSEGV, SEGV_MAPERR, "2nd level pt non-exist"},
|
||||
{ do_bad, SIGBUS, 0, "2nd lvl large pt non-exist" },
|
||||
{ do_bad, SIGBUS, 0, "invalid pte" },
|
||||
{ do_pf, SIGSEGV, SEGV_MAPERR, "page miss" },
|
||||
{ do_bad, SIGBUS, 0, "middle page miss" },
|
||||
{ do_bad, SIGBUS, 0, "large page miss" },
|
||||
{ do_pf, SIGSEGV, SEGV_MAPERR, "super page (section) miss" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 01100" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 01101" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 01110" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 01111" },
|
||||
{ do_bad, SIGBUS, 0, "addr: up 3G or IO" },
|
||||
{ do_pf, SIGSEGV, SEGV_ACCERR, "read unreadable addr" },
|
||||
{ do_pf, SIGSEGV, SEGV_ACCERR, "write unwriteable addr"},
|
||||
{ do_pf, SIGSEGV, SEGV_ACCERR, "exec unexecutable addr"},
|
||||
{ do_bad, SIGBUS, 0, "unknown 10100" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 10101" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 10110" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 10111" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 11000" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 11001" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 11010" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 11011" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 11100" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 11101" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 11110" },
|
||||
{ do_bad, SIGBUS, 0, "unknown 11111" }
|
||||
};
|
||||
|
||||
void __init hook_fault_code(int nr,
|
||||
int (*fn) (unsigned long, unsigned int, struct pt_regs *),
|
||||
int sig, int code, const char *name)
|
||||
{
|
||||
if (nr < 0 || nr >= ARRAY_SIZE(fsr_info))
|
||||
BUG();
|
||||
|
||||
fsr_info[nr].fn = fn;
|
||||
fsr_info[nr].sig = sig;
|
||||
fsr_info[nr].code = code;
|
||||
fsr_info[nr].name = name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispatch a data abort to the relevant handler.
|
||||
*/
|
||||
asmlinkage void do_DataAbort(unsigned long addr, unsigned int fsr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
const struct fsr_info *inf = fsr_info + fsr_fs(fsr);
|
||||
struct siginfo info;
|
||||
|
||||
if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs))
|
||||
return;
|
||||
|
||||
printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n",
|
||||
inf->name, fsr, addr);
|
||||
|
||||
info.si_signo = inf->sig;
|
||||
info.si_errno = 0;
|
||||
info.si_code = inf->code;
|
||||
info.si_addr = (void __user *)addr;
|
||||
uc32_notify_die("", regs, &info, fsr, 0);
|
||||
}
|
||||
|
||||
asmlinkage void do_PrefetchAbort(unsigned long addr,
|
||||
unsigned int ifsr, struct pt_regs *regs)
|
||||
{
|
||||
const struct fsr_info *inf = fsr_info + fsr_fs(ifsr);
|
||||
struct siginfo info;
|
||||
|
||||
if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
|
||||
return;
|
||||
|
||||
printk(KERN_ALERT "Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n",
|
||||
inf->name, ifsr, addr);
|
||||
|
||||
info.si_signo = inf->sig;
|
||||
info.si_errno = 0;
|
||||
info.si_code = inf->code;
|
||||
info.si_addr = (void __user *)addr;
|
||||
uc32_notify_die("", regs, &info, ifsr, 0);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* linux/arch/unicore32/mm/pgd.c
|
||||
*
|
||||
* Code specific to PKUnity SoC and UniCore ISA
|
||||
*
|
||||
* Copyright (C) 2001-2010 GUAN Xue-tao
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/mm.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/highmem.h>
|
||||
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
#include "mm.h"
|
||||
|
||||
#define FIRST_KERNEL_PGD_NR (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD)
|
||||
|
||||
/*
|
||||
* need to get a 4k page for level 1
|
||||
*/
|
||||
pgd_t *get_pgd_slow(struct mm_struct *mm)
|
||||
{
|
||||
pgd_t *new_pgd, *init_pgd;
|
||||
pmd_t *new_pmd, *init_pmd;
|
||||
pte_t *new_pte, *init_pte;
|
||||
|
||||
new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 0);
|
||||
if (!new_pgd)
|
||||
goto no_pgd;
|
||||
|
||||
memset(new_pgd, 0, FIRST_KERNEL_PGD_NR * sizeof(pgd_t));
|
||||
|
||||
/*
|
||||
* Copy over the kernel and IO PGD entries
|
||||
*/
|
||||
init_pgd = pgd_offset_k(0);
|
||||
memcpy(new_pgd + FIRST_KERNEL_PGD_NR, init_pgd + FIRST_KERNEL_PGD_NR,
|
||||
(PTRS_PER_PGD - FIRST_KERNEL_PGD_NR) * sizeof(pgd_t));
|
||||
|
||||
clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t));
|
||||
|
||||
if (!vectors_high()) {
|
||||
/*
|
||||
* On UniCore, first page must always be allocated since it
|
||||
* contains the machine vectors.
|
||||
*/
|
||||
new_pmd = pmd_alloc(mm, (pud_t *)new_pgd, 0);
|
||||
if (!new_pmd)
|
||||
goto no_pmd;
|
||||
|
||||
new_pte = pte_alloc_map(mm, new_pmd, 0);
|
||||
if (!new_pte)
|
||||
goto no_pte;
|
||||
|
||||
init_pmd = pmd_offset((pud_t *)init_pgd, 0);
|
||||
init_pte = pte_offset_map(init_pmd, 0);
|
||||
set_pte(new_pte, *init_pte);
|
||||
pte_unmap(init_pte);
|
||||
pte_unmap(new_pte);
|
||||
}
|
||||
|
||||
return new_pgd;
|
||||
|
||||
no_pte:
|
||||
pmd_free(mm, new_pmd);
|
||||
no_pmd:
|
||||
free_pages((unsigned long)new_pgd, 0);
|
||||
no_pgd:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd)
|
||||
{
|
||||
pmd_t *pmd;
|
||||
pgtable_t pte;
|
||||
|
||||
if (!pgd)
|
||||
return;
|
||||
|
||||
/* pgd is always present and good */
|
||||
pmd = pmd_off(pgd, 0);
|
||||
if (pmd_none(*pmd))
|
||||
goto free;
|
||||
if (pmd_bad(*pmd)) {
|
||||
pmd_ERROR(*pmd);
|
||||
pmd_clear(pmd);
|
||||
goto free;
|
||||
}
|
||||
|
||||
pte = pmd_pgtable(*pmd);
|
||||
pmd_clear(pmd);
|
||||
pte_free(mm, pte);
|
||||
pmd_free(mm, pmd);
|
||||
free:
|
||||
free_pages((unsigned long) pgd, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user