2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
|
|
* License. See the file "COPYING" in the main directory of this archive
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003 Ralf Baechle
|
|
|
|
*/
|
|
|
|
#ifndef _ASM_PGTABLE_H
|
|
|
|
#define _ASM_PGTABLE_H
|
|
|
|
|
2005-09-04 06:56:16 +08:00
|
|
|
#ifdef CONFIG_32BIT
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/pgtable-32.h>
|
|
|
|
#endif
|
2005-09-04 06:56:16 +08:00
|
|
|
#ifdef CONFIG_64BIT
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/pgtable-64.h>
|
|
|
|
#endif
|
|
|
|
|
2005-07-14 08:17:05 +08:00
|
|
|
#include <asm/io.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/pgtable-bits.h>
|
|
|
|
|
2005-11-07 16:59:43 +08:00
|
|
|
struct mm_struct;
|
|
|
|
struct vm_area_struct;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define PAGE_NONE __pgprot(_PAGE_PRESENT | _CACHE_CACHABLE_NONCOHERENT)
|
2010-02-11 07:12:47 +08:00
|
|
|
#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_WRITE | (kernel_uses_smartmips_rixi ? 0 : _PAGE_READ) | \
|
2007-09-19 07:58:24 +08:00
|
|
|
_page_cachable_default)
|
2010-02-11 07:12:47 +08:00
|
|
|
#define PAGE_COPY __pgprot(_PAGE_PRESENT | (kernel_uses_smartmips_rixi ? 0 : _PAGE_READ) | \
|
|
|
|
(kernel_uses_smartmips_rixi ? _PAGE_NO_EXEC : 0) | _page_cachable_default)
|
|
|
|
#define PAGE_READONLY __pgprot(_PAGE_PRESENT | (kernel_uses_smartmips_rixi ? 0 : _PAGE_READ) | \
|
2007-09-19 07:58:24 +08:00
|
|
|
_page_cachable_default)
|
2005-04-17 06:20:36 +08:00
|
|
|
#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \
|
2007-09-19 07:58:24 +08:00
|
|
|
_PAGE_GLOBAL | _page_cachable_default)
|
2010-02-11 07:12:47 +08:00
|
|
|
#define PAGE_USERIO __pgprot(_PAGE_PRESENT | (kernel_uses_smartmips_rixi ? 0 : _PAGE_READ) | _PAGE_WRITE | \
|
2007-09-19 07:58:24 +08:00
|
|
|
_page_cachable_default)
|
2005-04-17 06:20:36 +08:00
|
|
|
#define PAGE_KERNEL_UNCACHED __pgprot(_PAGE_PRESENT | __READABLE | \
|
|
|
|
__WRITEABLE | _PAGE_GLOBAL | _CACHE_UNCACHED)
|
|
|
|
|
|
|
|
/*
|
2010-02-11 07:12:47 +08:00
|
|
|
* If _PAGE_NO_EXEC is not defined, we can't do page protection for
|
|
|
|
* execute, and consider it to be the same as read. Also, write
|
|
|
|
* permissions imply read permissions. This is the closest we can get
|
|
|
|
* by reasonable means..
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
2007-09-19 07:58:24 +08:00
|
|
|
/*
|
|
|
|
* Dummy values to fill the table in mmap.c
|
|
|
|
* The real values will be generated at runtime
|
|
|
|
*/
|
|
|
|
#define __P000 __pgprot(0)
|
|
|
|
#define __P001 __pgprot(0)
|
|
|
|
#define __P010 __pgprot(0)
|
|
|
|
#define __P011 __pgprot(0)
|
|
|
|
#define __P100 __pgprot(0)
|
|
|
|
#define __P101 __pgprot(0)
|
|
|
|
#define __P110 __pgprot(0)
|
|
|
|
#define __P111 __pgprot(0)
|
|
|
|
|
|
|
|
#define __S000 __pgprot(0)
|
|
|
|
#define __S001 __pgprot(0)
|
|
|
|
#define __S010 __pgprot(0)
|
|
|
|
#define __S011 __pgprot(0)
|
|
|
|
#define __S100 __pgprot(0)
|
|
|
|
#define __S101 __pgprot(0)
|
|
|
|
#define __S110 __pgprot(0)
|
|
|
|
#define __S111 __pgprot(0)
|
|
|
|
|
|
|
|
extern unsigned long _page_cachable_default;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ZERO_PAGE is a global shared page that is always zero; used
|
|
|
|
* for zero-mapped memory areas etc..
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern unsigned long empty_zero_page;
|
|
|
|
extern unsigned long zero_page_mask;
|
|
|
|
|
|
|
|
#define ZERO_PAGE(vaddr) \
|
2006-10-19 19:19:59 +08:00
|
|
|
(virt_to_page((void *)(empty_zero_page + (((unsigned long)(vaddr)) & zero_page_mask))))
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-09-22 08:03:34 +08:00
|
|
|
#define is_zero_pfn is_zero_pfn
|
|
|
|
static inline int is_zero_pfn(unsigned long pfn)
|
|
|
|
{
|
|
|
|
extern unsigned long zero_pfn;
|
|
|
|
unsigned long offset_from_zero_pfn = pfn - zero_pfn;
|
|
|
|
return offset_from_zero_pfn <= (zero_page_mask >> PAGE_SHIFT);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define my_zero_pfn(addr) page_to_pfn(ZERO_PAGE(addr))
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
extern void paging_init(void);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Conversion functions: convert a page and protection to a page entry,
|
|
|
|
* and a page entry and page directory to the page they refer to.
|
|
|
|
*/
|
2007-03-20 00:36:42 +08:00
|
|
|
#define pmd_phys(pmd) virt_to_phys((void *)pmd_val(pmd))
|
2005-04-17 06:20:36 +08:00
|
|
|
#define pmd_page(pmd) (pfn_to_page(pmd_phys(pmd) >> PAGE_SHIFT))
|
2006-09-26 14:31:48 +08:00
|
|
|
#define pmd_page_vaddr(pmd) pmd_val(pmd)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-09-19 07:46:32 +08:00
|
|
|
#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32)
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
|
|
|
|
#define pte_none(pte) (!(((pte).pte_low | (pte).pte_high) & ~_PAGE_GLOBAL))
|
|
|
|
#define pte_present(pte) ((pte).pte_low & _PAGE_PRESENT)
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline void set_pte(pte_t *ptep, pte_t pte)
|
|
|
|
{
|
|
|
|
ptep->pte_high = pte.pte_high;
|
|
|
|
smp_wmb();
|
|
|
|
ptep->pte_low = pte.pte_low;
|
|
|
|
//printk("pte_high %x pte_low %x\n", ptep->pte_high, ptep->pte_low);
|
|
|
|
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
if (pte.pte_low & _PAGE_GLOBAL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
pte_t *buddy = ptep_buddy(ptep);
|
|
|
|
/*
|
|
|
|
* Make sure the buddy is global too (if it's !none,
|
|
|
|
* it better already be global)
|
|
|
|
*/
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
if (pte_none(*buddy)) {
|
|
|
|
buddy->pte_low |= _PAGE_GLOBAL;
|
|
|
|
buddy->pte_high |= _PAGE_GLOBAL;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2007-10-12 06:46:15 +08:00
|
|
|
#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
|
|
|
|
{
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
pte_t null = __pte(0);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Preserve global status for the pair */
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
if (ptep_buddy(ptep)->pte_low & _PAGE_GLOBAL)
|
|
|
|
null.pte_low = null.pte_high = _PAGE_GLOBAL;
|
|
|
|
|
|
|
|
set_pte_at(mm, addr, ptep, null);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
#else
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
|
|
|
|
#define pte_none(pte) (!(pte_val(pte) & ~_PAGE_GLOBAL))
|
|
|
|
#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT)
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Certain architectures need to do special things when pte's
|
|
|
|
* within a page table are directly modified. Thus, the following
|
|
|
|
* hook is made available.
|
|
|
|
*/
|
|
|
|
static inline void set_pte(pte_t *ptep, pte_t pteval)
|
|
|
|
{
|
|
|
|
*ptep = pteval;
|
|
|
|
#if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX)
|
|
|
|
if (pte_val(pteval) & _PAGE_GLOBAL) {
|
|
|
|
pte_t *buddy = ptep_buddy(ptep);
|
|
|
|
/*
|
|
|
|
* Make sure the buddy is global too (if it's !none,
|
|
|
|
* it better already be global)
|
|
|
|
*/
|
|
|
|
if (pte_none(*buddy))
|
|
|
|
pte_val(*buddy) = pte_val(*buddy) | _PAGE_GLOBAL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2007-10-12 06:46:15 +08:00
|
|
|
#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
|
|
|
|
{
|
|
|
|
#if !defined(CONFIG_CPU_R3000) && !defined(CONFIG_CPU_TX39XX)
|
|
|
|
/* Preserve global status for the pair */
|
|
|
|
if (pte_val(*ptep_buddy(ptep)) & _PAGE_GLOBAL)
|
|
|
|
set_pte_at(mm, addr, ptep, __pte(_PAGE_GLOBAL));
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
set_pte_at(mm, addr, ptep, __pte(0));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
2005-02-10 20:19:59 +08:00
|
|
|
* (pmds are folded into puds so this doesn't get actually called,
|
2005-04-17 06:20:36 +08:00
|
|
|
* but the define is needed for a generic inline function.)
|
|
|
|
*/
|
|
|
|
#define set_pmd(pmdptr, pmdval) do { *(pmdptr) = (pmdval); } while(0)
|
2005-02-10 20:19:59 +08:00
|
|
|
|
2009-12-05 05:52:36 +08:00
|
|
|
#ifndef __PAGETABLE_PMD_FOLDED
|
2005-02-10 20:19:59 +08:00
|
|
|
/*
|
|
|
|
* (puds are folded into pgds so this doesn't get actually called,
|
|
|
|
* but the define is needed for a generic inline function.)
|
|
|
|
*/
|
|
|
|
#define set_pud(pudptr, pudval) do { *(pudptr) = (pudval); } while(0)
|
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-01 22:25:28 +08:00
|
|
|
#define PGD_T_LOG2 (__builtin_ffs(sizeof(pgd_t)) - 1)
|
|
|
|
#define PMD_T_LOG2 (__builtin_ffs(sizeof(pmd_t)) - 1)
|
|
|
|
#define PTE_T_LOG2 (__builtin_ffs(sizeof(pte_t)) - 1)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-13 19:44:41 +08:00
|
|
|
/*
|
|
|
|
* We used to declare this array with size but gcc 3.3 and older are not able
|
|
|
|
* to find that this expression is a constant, so the size is dropped.
|
|
|
|
*/
|
|
|
|
extern pgd_t swapper_pg_dir[];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The following only work if pte_present() is true.
|
|
|
|
* Undefined behaviour if not..
|
|
|
|
*/
|
2007-09-19 07:46:32 +08:00
|
|
|
#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32)
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
static inline int pte_write(pte_t pte) { return pte.pte_low & _PAGE_WRITE; }
|
|
|
|
static inline int pte_dirty(pte_t pte) { return pte.pte_low & _PAGE_MODIFIED; }
|
|
|
|
static inline int pte_young(pte_t pte) { return pte.pte_low & _PAGE_ACCESSED; }
|
|
|
|
static inline int pte_file(pte_t pte) { return pte.pte_low & _PAGE_FILE; }
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline pte_t pte_wrprotect(pte_t pte)
|
|
|
|
{
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
pte.pte_low &= ~(_PAGE_WRITE | _PAGE_SILENT_WRITE);
|
|
|
|
pte.pte_high &= ~_PAGE_SILENT_WRITE;
|
2005-04-17 06:20:36 +08:00
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkclean(pte_t pte)
|
|
|
|
{
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
pte.pte_low &= ~(_PAGE_MODIFIED | _PAGE_SILENT_WRITE);
|
|
|
|
pte.pte_high &= ~_PAGE_SILENT_WRITE;
|
2005-04-17 06:20:36 +08:00
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkold(pte_t pte)
|
|
|
|
{
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
pte.pte_low &= ~(_PAGE_ACCESSED | _PAGE_SILENT_READ);
|
|
|
|
pte.pte_high &= ~_PAGE_SILENT_READ;
|
2005-04-17 06:20:36 +08:00
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkwrite(pte_t pte)
|
|
|
|
{
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
pte.pte_low |= _PAGE_WRITE;
|
|
|
|
if (pte.pte_low & _PAGE_MODIFIED) {
|
|
|
|
pte.pte_low |= _PAGE_SILENT_WRITE;
|
|
|
|
pte.pte_high |= _PAGE_SILENT_WRITE;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkdirty(pte_t pte)
|
|
|
|
{
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
pte.pte_low |= _PAGE_MODIFIED;
|
|
|
|
if (pte.pte_low & _PAGE_WRITE) {
|
|
|
|
pte.pte_low |= _PAGE_SILENT_WRITE;
|
|
|
|
pte.pte_high |= _PAGE_SILENT_WRITE;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkyoung(pte_t pte)
|
|
|
|
{
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
pte.pte_low |= _PAGE_ACCESSED;
|
2008-05-02 19:08:20 +08:00
|
|
|
if (pte.pte_low & _PAGE_READ) {
|
[MIPS] Fix marking buddy of pte global for MIPS32 w/36-bit physical address
In case of CONFIG_64BIT_PHYS_ADDR, set_pte() and pte_clear() functions
only set _PAGE_GLOBAL bit in the pte_low field of the buddy PTEs,
forgetting to propagate ito to pte_high. Thus, the both pages might not
really be made global for the CPU (since it AND's the G-bit of the
odd / even PTEs together to decide whether they're global or not). Thus,
if only a single page is allocated via vmalloc() or ioremap(), it's not
really global for CPU (and it must be, since this is kernel mapping),
and thus its ASID is compared against the current process' one -- so,
we'll get into trouble sooner or later... Also, pte_none() will fail
on global pages because _PAGE_GLOBAL bit is set in both pte_low and
pte_high, and pte_val() will return u64 value consisting of those fields
concateneted.
Signed-off-by: Sergei Shtylyov <sshtylyov@ru.mvista.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
2006-04-17 03:27:21 +08:00
|
|
|
pte.pte_low |= _PAGE_SILENT_READ;
|
|
|
|
pte.pte_high |= _PAGE_SILENT_READ;
|
2008-05-02 19:08:20 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; }
|
|
|
|
static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_MODIFIED; }
|
|
|
|
static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; }
|
|
|
|
static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE; }
|
|
|
|
|
|
|
|
static inline pte_t pte_wrprotect(pte_t pte)
|
|
|
|
{
|
|
|
|
pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_SILENT_WRITE);
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkclean(pte_t pte)
|
|
|
|
{
|
|
|
|
pte_val(pte) &= ~(_PAGE_MODIFIED|_PAGE_SILENT_WRITE);
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkold(pte_t pte)
|
|
|
|
{
|
|
|
|
pte_val(pte) &= ~(_PAGE_ACCESSED|_PAGE_SILENT_READ);
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkwrite(pte_t pte)
|
|
|
|
{
|
|
|
|
pte_val(pte) |= _PAGE_WRITE;
|
|
|
|
if (pte_val(pte) & _PAGE_MODIFIED)
|
|
|
|
pte_val(pte) |= _PAGE_SILENT_WRITE;
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkdirty(pte_t pte)
|
|
|
|
{
|
|
|
|
pte_val(pte) |= _PAGE_MODIFIED;
|
|
|
|
if (pte_val(pte) & _PAGE_WRITE)
|
|
|
|
pte_val(pte) |= _PAGE_SILENT_WRITE;
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline pte_t pte_mkyoung(pte_t pte)
|
|
|
|
{
|
|
|
|
pte_val(pte) |= _PAGE_ACCESSED;
|
2010-02-11 07:12:47 +08:00
|
|
|
if (kernel_uses_smartmips_rixi) {
|
|
|
|
if (!(pte_val(pte) & _PAGE_NO_READ))
|
|
|
|
pte_val(pte) |= _PAGE_SILENT_READ;
|
|
|
|
} else {
|
|
|
|
if (pte_val(pte) & _PAGE_READ)
|
|
|
|
pte_val(pte) |= _PAGE_SILENT_READ;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
return pte;
|
|
|
|
}
|
2009-05-28 08:47:43 +08:00
|
|
|
|
|
|
|
#ifdef _PAGE_HUGE
|
|
|
|
static inline int pte_huge(pte_t pte) { return pte_val(pte) & _PAGE_HUGE; }
|
|
|
|
|
|
|
|
static inline pte_t pte_mkhuge(pte_t pte)
|
|
|
|
{
|
|
|
|
pte_val(pte) |= _PAGE_HUGE;
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
#endif /* _PAGE_HUGE */
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
mm: introduce pte_special pte bit
s390 for one, cannot implement VM_MIXEDMAP with pfn_valid, due to their memory
model (which is more dynamic than most). Instead, they had proposed to
implement it with an additional path through vm_normal_page(), using a bit in
the pte to determine whether or not the page should be refcounted:
vm_normal_page()
{
...
if (unlikely(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP))) {
if (vma->vm_flags & VM_MIXEDMAP) {
#ifdef s390
if (!mixedmap_refcount_pte(pte))
return NULL;
#else
if (!pfn_valid(pfn))
return NULL;
#endif
goto out;
}
...
}
This is fine, however if we are allowed to use a bit in the pte to determine
refcountedness, we can use that to _completely_ replace all the vma based
schemes. So instead of adding more cases to the already complex vma-based
scheme, we can have a clearly seperate and simple pte-based scheme (and get
slightly better code generation in the process):
vm_normal_page()
{
#ifdef s390
if (!mixedmap_refcount_pte(pte))
return NULL;
return pte_page(pte);
#else
...
#endif
}
And finally, we may rather make this concept usable by any architecture rather
than making it s390 only, so implement a new type of pte state for this.
Unfortunately the old vma based code must stay, because some architectures may
not be able to spare pte bits. This makes vm_normal_page a little bit more
ugly than we would like, but the 2 cases are clearly seperate.
So introduce a pte_special pte state, and use it in mm/memory.c. It is
currently a noop for all architectures, so this doesn't actually result in any
compiled code changes to mm/memory.o.
BTW:
I haven't put vm_normal_page() into arch code as-per an earlier suggestion.
The reason is that, regardless of where vm_normal_page is actually
implemented, the *abstraction* is still exactly the same. Also, while it
depends on whether the architecture has pte_special or not, that is the
only two possible cases, and it really isn't an arch specific function --
the role of the arch code should be to provide primitive functions and
accessors with which to build the core code; pte_special does that. We do
not want architectures to know or care about vm_normal_page itself, and
we definitely don't want them being able to invent something new there
out of sight of mm/ code. If we made vm_normal_page an arch function, then
we have to make vm_insert_mixed (next patch) an arch function too. So I
don't think moving it to arch code fundamentally improves any abstractions,
while it does practically make the code more difficult to follow, for both
mm and arch developers, and easier to misuse.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Nick Piggin <npiggin@suse.de>
Acked-by: Carsten Otte <cotte@de.ibm.com>
Cc: Jared Hulbert <jaredeh@gmail.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-04-28 17:13:00 +08:00
|
|
|
static inline int pte_special(pte_t pte) { return 0; }
|
|
|
|
static inline pte_t pte_mkspecial(pte_t pte) { return pte; }
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Macro to make mark a page protection value as "uncacheable". Note
|
|
|
|
* that "protection" is really a misnomer here as the protection value
|
|
|
|
* contains the memory attribute bits, dirty bits, and various other
|
|
|
|
* bits as well.
|
|
|
|
*/
|
|
|
|
#define pgprot_noncached pgprot_noncached
|
|
|
|
|
|
|
|
static inline pgprot_t pgprot_noncached(pgprot_t _prot)
|
|
|
|
{
|
|
|
|
unsigned long prot = pgprot_val(_prot);
|
|
|
|
|
|
|
|
prot = (prot & ~_CACHE_MASK) | _CACHE_UNCACHED;
|
|
|
|
|
|
|
|
return __pgprot(prot);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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, pgprot) pfn_pte(page_to_pfn(page), (pgprot))
|
|
|
|
|
2007-09-19 07:46:32 +08:00
|
|
|
#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32)
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
|
|
|
{
|
2006-05-04 02:56:43 +08:00
|
|
|
pte.pte_low &= _PAGE_CHG_MASK;
|
|
|
|
pte.pte_high &= ~0x3f;
|
|
|
|
pte.pte_low |= pgprot_val(newprot);
|
2005-04-17 06:20:36 +08:00
|
|
|
pte.pte_high |= pgprot_val(newprot) & 0x3f;
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
|
|
|
{
|
|
|
|
return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
extern void __update_tlb(struct vm_area_struct *vma, unsigned long address,
|
|
|
|
pte_t pte);
|
|
|
|
extern void __update_cache(struct vm_area_struct *vma, unsigned long address,
|
|
|
|
pte_t pte);
|
|
|
|
|
|
|
|
static inline void update_mmu_cache(struct vm_area_struct *vma,
|
MM: Pass a PTE pointer to update_mmu_cache() rather than the PTE itself
On VIVT ARM, when we have multiple shared mappings of the same file
in the same MM, we need to ensure that we have coherency across all
copies. We do this via make_coherent() by making the pages
uncacheable.
This used to work fine, until we allowed highmem with highpte - we
now have a page table which is mapped as required, and is not available
for modification via update_mmu_cache().
Ralf Beache suggested getting rid of the PTE value passed to
update_mmu_cache():
On MIPS update_mmu_cache() calls __update_tlb() which walks pagetables
to construct a pointer to the pte again. Passing a pte_t * is much
more elegant. Maybe we might even replace the pte argument with the
pte_t?
Ben Herrenschmidt would also like the pte pointer for PowerPC:
Passing the ptep in there is exactly what I want. I want that
-instead- of the PTE value, because I have issue on some ppc cases,
for I$/D$ coherency, where set_pte_at() may decide to mask out the
_PAGE_EXEC.
So, pass in the mapped page table pointer into update_mmu_cache(), and
remove the PTE value, updating all implementations and call sites to
suit.
Includes a fix from Stephen Rothwell:
sparc: fix fallout from update_mmu_cache API change
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2009-12-19 00:40:18 +08:00
|
|
|
unsigned long address, pte_t *ptep)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
MM: Pass a PTE pointer to update_mmu_cache() rather than the PTE itself
On VIVT ARM, when we have multiple shared mappings of the same file
in the same MM, we need to ensure that we have coherency across all
copies. We do this via make_coherent() by making the pages
uncacheable.
This used to work fine, until we allowed highmem with highpte - we
now have a page table which is mapped as required, and is not available
for modification via update_mmu_cache().
Ralf Beache suggested getting rid of the PTE value passed to
update_mmu_cache():
On MIPS update_mmu_cache() calls __update_tlb() which walks pagetables
to construct a pointer to the pte again. Passing a pte_t * is much
more elegant. Maybe we might even replace the pte argument with the
pte_t?
Ben Herrenschmidt would also like the pte pointer for PowerPC:
Passing the ptep in there is exactly what I want. I want that
-instead- of the PTE value, because I have issue on some ppc cases,
for I$/D$ coherency, where set_pte_at() may decide to mask out the
_PAGE_EXEC.
So, pass in the mapped page table pointer into update_mmu_cache(), and
remove the PTE value, updating all implementations and call sites to
suit.
Includes a fix from Stephen Rothwell:
sparc: fix fallout from update_mmu_cache API change
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2009-12-19 00:40:18 +08:00
|
|
|
pte_t pte = *ptep;
|
2005-04-17 06:20:36 +08:00
|
|
|
__update_tlb(vma, address, pte);
|
|
|
|
__update_cache(vma, address, pte);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define kern_addr_valid(addr) (1)
|
|
|
|
|
|
|
|
#ifdef CONFIG_64BIT_PHYS_ADDR
|
|
|
|
extern int remap_pfn_range(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot);
|
|
|
|
|
|
|
|
static inline int io_remap_pfn_range(struct vm_area_struct *vma,
|
|
|
|
unsigned long vaddr,
|
|
|
|
unsigned long pfn,
|
|
|
|
unsigned long size,
|
|
|
|
pgprot_t prot)
|
|
|
|
{
|
|
|
|
phys_t phys_addr_high = fixup_bigphys_addr(pfn << PAGE_SHIFT, size);
|
2005-04-11 20:24:16 +08:00
|
|
|
return remap_pfn_range(vma, vaddr, phys_addr_high >> PAGE_SHIFT, size, prot);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define io_remap_pfn_range(vma, vaddr, pfn, size, prot) \
|
|
|
|
remap_pfn_range(vma, vaddr, pfn, size, prot)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <asm-generic/pgtable.h>
|
|
|
|
|
2009-11-11 13:59:23 +08:00
|
|
|
/*
|
|
|
|
* uncached accelerated TLB map for video memory access
|
|
|
|
*/
|
|
|
|
#ifdef CONFIG_CPU_SUPPORTS_UNCACHED_ACCELERATED
|
|
|
|
#define __HAVE_PHYS_MEM_ACCESS_PROT
|
|
|
|
|
|
|
|
struct file;
|
|
|
|
pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
|
|
|
|
unsigned long size, pgprot_t vma_prot);
|
|
|
|
int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
|
|
|
|
unsigned long size, pgprot_t *vma_prot);
|
|
|
|
#endif
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* We provide our own get_unmapped area to cope with the virtual aliasing
|
|
|
|
* constraints placed on us by the cache architecture.
|
|
|
|
*/
|
|
|
|
#define HAVE_ARCH_UNMAPPED_AREA
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No page table caches to initialise
|
|
|
|
*/
|
|
|
|
#define pgtable_cache_init() do { } while (0)
|
|
|
|
|
|
|
|
#endif /* _ASM_PGTABLE_H */
|