Skip to content

Commit

Permalink
MIPS: Add support for XPA.
Browse files Browse the repository at this point in the history
Add support for extended physical addressing (XPA) so that
32-bit platforms can access equal to or greater than 40 bits
of physical addresses.

NOTE:
      1) XPA and EVA are not the same and cannot be used
         simultaneously.
      2) If you configure your kernel for XPA, the PTEs
         and all address sizes become 64-bit.
      3) Your platform MUST have working HIGHMEM support.

Signed-off-by: Steven J. Hill <Steven.Hill@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9355/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
  • Loading branch information
Steven J. Hill authored and ralfbaechle committed Mar 19, 2015
1 parent be0c37c commit c5b3678
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 44 deletions.
35 changes: 35 additions & 0 deletions arch/mips/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ config MIPS_MALTA
select SYS_HAS_CPU_MIPS32_R1
select SYS_HAS_CPU_MIPS32_R2
select SYS_HAS_CPU_MIPS32_R3_5
select SYS_HAS_CPU_MIPS32_R5
select SYS_HAS_CPU_MIPS32_R6
select SYS_HAS_CPU_MIPS64_R1
select SYS_HAS_CPU_MIPS64_R2
Expand All @@ -386,6 +387,7 @@ config MIPS_MALTA
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_64BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
select SYS_SUPPORTS_HIGHMEM
select SYS_SUPPORTS_LITTLE_ENDIAN
select SYS_SUPPORTS_MICROMIPS
select SYS_SUPPORTS_MIPS_CMP
Expand Down Expand Up @@ -1596,6 +1598,33 @@ config CPU_MIPS32_3_5_EVA
One of its primary benefits is an increase in the maximum size
of lowmem (up to 3GB). If unsure, say 'N' here.

config CPU_MIPS32_R5_FEATURES
bool "MIPS32 Release 5 Features"
depends on SYS_HAS_CPU_MIPS32_R5
depends on CPU_MIPS32_R2
help
Choose this option to build a kernel for release 2 or later of the
MIPS32 architecture including features from release 5 such as
support for Extended Physical Addressing (XPA).

config CPU_MIPS32_R5_XPA
bool "Extended Physical Addressing (XPA)"
depends on CPU_MIPS32_R5_FEATURES
depends on !EVA
depends on !PAGE_SIZE_4KB
depends on SYS_SUPPORTS_HIGHMEM
select XPA
select HIGHMEM
select ARCH_PHYS_ADDR_T_64BIT
default n
help
Choose this option if you want to enable the Extended Physical
Addressing (XPA) on your MIPS32 core (such as P5600 series). The
benefit is to increase physical addressing equal to or greater
than 40 bits. Note that this has the side effect of turning on
64-bit addressing which in turn makes the PTEs 64-bit in size.
If unsure, say 'N' here.

if CPU_LOONGSON2F
config CPU_NOP_WORKAROUNDS
bool
Expand Down Expand Up @@ -1699,6 +1728,9 @@ config SYS_HAS_CPU_MIPS32_R2
config SYS_HAS_CPU_MIPS32_R3_5
bool

config SYS_HAS_CPU_MIPS32_R5
bool

config SYS_HAS_CPU_MIPS32_R6
bool

Expand Down Expand Up @@ -1836,6 +1868,9 @@ config CPU_MIPSR6
config EVA
bool

config XPA
bool

config SYS_SUPPORTS_32BIT_KERNEL
bool
config SYS_SUPPORTS_64BIT_KERNEL
Expand Down
3 changes: 3 additions & 0 deletions arch/mips/include/asm/cpu-features.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@
# endif
#endif

#ifndef cpu_has_xpa
#define cpu_has_xpa (cpu_data[0].options & MIPS_CPU_XPA)
#endif
#ifndef cpu_has_vtag_icache
#define cpu_has_vtag_icache (cpu_data[0].icache.flags & MIPS_CACHE_VTAG)
#endif
Expand Down
1 change: 1 addition & 0 deletions arch/mips/include/asm/cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ enum cpu_type_enum {
#define MIPS_CPU_MAAR 0x400000000ull /* MAAR(I) registers are present */
#define MIPS_CPU_FRE 0x800000000ull /* FRE & UFE bits implemented */
#define MIPS_CPU_RW_LLB 0x1000000000ull /* LLADDR/LLB writes are allowed */
#define MIPS_CPU_XPA 0x2000000000ull /* CPU supports Extended Physical Addressing */

/*
* CPU ASE encodings
Expand Down
15 changes: 9 additions & 6 deletions arch/mips/include/asm/pgtable-32.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,16 @@ static inline void pmd_clear(pmd_t *pmdp)

#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
#define pte_page(x) pfn_to_page(pte_pfn(x))
#define pte_pfn(x) ((unsigned long)((x).pte_high >> 6))
#define pte_pfn(x) (((unsigned long)((x).pte_high >> _PFN_SHIFT)) | (unsigned long)((x).pte_low << _PAGE_PRESENT_SHIFT))
static inline pte_t
pfn_pte(unsigned long pfn, pgprot_t prot)
{
pte_t pte;
pte.pte_high = (pfn << 6) | (pgprot_val(prot) & 0x3f);
pte.pte_low = pgprot_val(prot);

pte.pte_low = (pfn >> _PAGE_PRESENT_SHIFT) |
(pgprot_val(prot) & ~_PFNX_MASK);
pte.pte_high = (pfn << _PFN_SHIFT) |
(pgprot_val(prot) & ~_PFN_MASK);
return pte;
}

Expand Down Expand Up @@ -166,9 +169,9 @@ pfn_pte(unsigned long pfn, pgprot_t prot)
#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)

/* Swap entries must have VALID and GLOBAL bits cleared. */
#define __swp_type(x) (((x).val >> 2) & 0x1f)
#define __swp_offset(x) ((x).val >> 7)
#define __swp_entry(type,offset) ((swp_entry_t) { ((type) << 2) | ((offset) << 7) })
#define __swp_type(x) (((x).val >> 4) & 0x1f)
#define __swp_offset(x) ((x).val >> 9)
#define __swp_entry(type,offset) ((swp_entry_t) { ((type) << 4) | ((offset) << 9) })
#define __pte_to_swp_entry(pte) ((swp_entry_t) { (pte).pte_high })
#define __swp_entry_to_pte(x) ((pte_t) { 0, (x).val })

Expand Down
13 changes: 11 additions & 2 deletions arch/mips/include/asm/pgtable-bits.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@
/*
* The following bits are implemented by the TLB hardware
*/
#define _PAGE_GLOBAL_SHIFT 0
#define _PAGE_NO_EXEC_SHIFT 0
#define _PAGE_NO_EXEC (1 << _PAGE_NO_EXEC_SHIFT)
#define _PAGE_NO_READ_SHIFT (_PAGE_NO_EXEC_SHIFT + 1)
#define _PAGE_NO_READ (1 << _PAGE_NO_READ_SHIFT)
#define _PAGE_GLOBAL_SHIFT (_PAGE_NO_READ_SHIFT + 1)
#define _PAGE_GLOBAL (1 << _PAGE_GLOBAL_SHIFT)
#define _PAGE_VALID_SHIFT (_PAGE_GLOBAL_SHIFT + 1)
#define _PAGE_VALID (1 << _PAGE_VALID_SHIFT)
Expand All @@ -49,7 +53,7 @@
/*
* The following bits are implemented in software
*/
#define _PAGE_PRESENT_SHIFT (_CACHE_SHIFT + 3)
#define _PAGE_PRESENT_SHIFT (24)
#define _PAGE_PRESENT (1 << _PAGE_PRESENT_SHIFT)
#define _PAGE_READ_SHIFT (_PAGE_PRESENT_SHIFT + 1)
#define _PAGE_READ (1 << _PAGE_READ_SHIFT)
Expand All @@ -62,6 +66,11 @@

#define _PFN_SHIFT (PAGE_SHIFT - 12 + _CACHE_SHIFT + 3)

/*
* Bits for extended EntryLo0/EntryLo1 registers
*/
#define _PFNX_MASK 0xffffff

#elif defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)

/*
Expand Down
36 changes: 14 additions & 22 deletions arch/mips/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ extern void set_pte_at(struct mm_struct *mm, unsigned long addr, pte_t *ptep,

#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)

#define pte_none(pte) (!(((pte).pte_low | (pte).pte_high) & ~_PAGE_GLOBAL))
#define pte_none(pte) (!(((pte).pte_high) & ~_PAGE_GLOBAL))
#define pte_present(pte) ((pte).pte_low & _PAGE_PRESENT)

static inline void set_pte(pte_t *ptep, pte_t pte)
Expand All @@ -142,16 +142,14 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
smp_wmb();
ptep->pte_low = pte.pte_low;

if (pte.pte_low & _PAGE_GLOBAL) {
if (pte.pte_high & _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)) {
buddy->pte_low |= _PAGE_GLOBAL;
if (pte_none(*buddy))
buddy->pte_high |= _PAGE_GLOBAL;
}
}
}

Expand All @@ -161,8 +159,8 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt

htw_stop();
/* Preserve global status for the pair */
if (ptep_buddy(ptep)->pte_low & _PAGE_GLOBAL)
null.pte_low = null.pte_high = _PAGE_GLOBAL;
if (ptep_buddy(ptep)->pte_high & _PAGE_GLOBAL)
null.pte_high = _PAGE_GLOBAL;

set_pte_at(mm, addr, ptep, null);
htw_start();
Expand Down Expand Up @@ -242,52 +240,46 @@ static inline int pte_young(pte_t pte) { return pte.pte_low & _PAGE_ACCESSED; }

static inline pte_t pte_wrprotect(pte_t pte)
{
pte.pte_low &= ~(_PAGE_WRITE | _PAGE_SILENT_WRITE);
pte.pte_low &= ~_PAGE_WRITE;
pte.pte_high &= ~_PAGE_SILENT_WRITE;
return pte;
}

static inline pte_t pte_mkclean(pte_t pte)
{
pte.pte_low &= ~(_PAGE_MODIFIED | _PAGE_SILENT_WRITE);
pte.pte_low &= ~_PAGE_MODIFIED;
pte.pte_high &= ~_PAGE_SILENT_WRITE;
return pte;
}

static inline pte_t pte_mkold(pte_t pte)
{
pte.pte_low &= ~(_PAGE_ACCESSED | _PAGE_SILENT_READ);
pte.pte_low &= ~_PAGE_ACCESSED;
pte.pte_high &= ~_PAGE_SILENT_READ;
return pte;
}

static inline pte_t pte_mkwrite(pte_t pte)
{
pte.pte_low |= _PAGE_WRITE;
if (pte.pte_low & _PAGE_MODIFIED) {
pte.pte_low |= _PAGE_SILENT_WRITE;
if (pte.pte_low & _PAGE_MODIFIED)
pte.pte_high |= _PAGE_SILENT_WRITE;
}
return pte;
}

static inline pte_t pte_mkdirty(pte_t pte)
{
pte.pte_low |= _PAGE_MODIFIED;
if (pte.pte_low & _PAGE_WRITE) {
pte.pte_low |= _PAGE_SILENT_WRITE;
if (pte.pte_low & _PAGE_WRITE)
pte.pte_high |= _PAGE_SILENT_WRITE;
}
return pte;
}

static inline pte_t pte_mkyoung(pte_t pte)
{
pte.pte_low |= _PAGE_ACCESSED;
if (pte.pte_low & _PAGE_READ) {
pte.pte_low |= _PAGE_SILENT_READ;
if (pte.pte_low & _PAGE_READ)
pte.pte_high |= _PAGE_SILENT_READ;
}
return pte;
}
#else
Expand Down Expand Up @@ -391,10 +383,10 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
{
pte.pte_low &= _PAGE_CHG_MASK;
pte.pte_low &= (_PAGE_MODIFIED | _PAGE_ACCESSED | _PFNX_MASK);
pte.pte_high &= (_PFN_MASK | _CACHE_MASK);
pte.pte_low |= pgprot_val(newprot);
pte.pte_high |= pgprot_val(newprot) & ~(_PFN_MASK | _CACHE_MASK);
pte.pte_low |= pgprot_val(newprot) & ~_PFNX_MASK;
pte.pte_high |= pgprot_val(newprot) & ~_PFN_MASK;
return pte;
}
#else
Expand Down
4 changes: 4 additions & 0 deletions arch/mips/kernel/cpu-probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,10 @@ static inline unsigned int decode_config5(struct cpuinfo_mips *c)
c->options |= MIPS_CPU_MAAR;
if (config5 & MIPS_CONF5_LLB)
c->options |= MIPS_CPU_RW_LLB;
#ifdef CONFIG_XPA
if (config5 & MIPS_CONF5_MVH)
c->options |= MIPS_CPU_XPA;
#endif

return config5 & MIPS_CONF_M;
}
Expand Down
1 change: 1 addition & 0 deletions arch/mips/kernel/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
if (cpu_has_msa) seq_printf(m, "%s", " msa");
if (cpu_has_eva) seq_printf(m, "%s", " eva");
if (cpu_has_htw) seq_printf(m, "%s", " htw");
if (cpu_has_xpa) seq_printf(m, "%s", " xpa");
seq_printf(m, "\n");

if (cpu_has_mmips) {
Expand Down
7 changes: 6 additions & 1 deletion arch/mips/mm/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ static void *__kmap_pgprot(struct page *page, unsigned long addr, pgprot_t prot)
vaddr = __fix_to_virt(FIX_CMAP_END - idx);
pte = mk_pte(page, prot);
#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
entrylo = pte.pte_high;
entrylo = pte_to_entrylo(pte.pte_high);
#else
entrylo = pte_to_entrylo(pte_val(pte));
#endif
Expand All @@ -106,6 +106,11 @@ static void *__kmap_pgprot(struct page *page, unsigned long addr, pgprot_t prot)
write_c0_entryhi(vaddr & (PAGE_MASK << 1));
write_c0_entrylo0(entrylo);
write_c0_entrylo1(entrylo);
#ifdef CONFIG_XPA
entrylo = (pte.pte_low & _PFNX_MASK);
writex_c0_entrylo0(entrylo);
writex_c0_entrylo1(entrylo);
#endif
tlbidx = read_c0_wired();
write_c0_wired(tlbidx + 1);
write_c0_index(tlbidx);
Expand Down
12 changes: 12 additions & 0 deletions arch/mips/mm/tlb-r4k.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,17 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
ptep = pte_offset_map(pmdp, address);

#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
#ifdef CONFIG_XPA
write_c0_entrylo0(pte_to_entrylo(ptep->pte_high));
writex_c0_entrylo0(ptep->pte_low & _PFNX_MASK);
ptep++;
write_c0_entrylo1(pte_to_entrylo(ptep->pte_high));
writex_c0_entrylo1(ptep->pte_low & _PFNX_MASK);
#else
write_c0_entrylo0(ptep->pte_high);
ptep++;
write_c0_entrylo1(ptep->pte_high);
#endif
#else
write_c0_entrylo0(pte_to_entrylo(pte_val(*ptep++)));
write_c0_entrylo1(pte_to_entrylo(pte_val(*ptep)));
Expand All @@ -355,6 +363,9 @@ void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
unsigned long entryhi, unsigned long pagemask)
{
#ifdef CONFIG_XPA
panic("Broken for XPA kernels");
#else
unsigned long flags;
unsigned long wired;
unsigned long old_pagemask;
Expand Down Expand Up @@ -383,6 +394,7 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
write_c0_pagemask(old_pagemask);
local_flush_tlb_all();
local_irq_restore(flags);
#endif
}

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
Expand Down

0 comments on commit c5b3678

Please sign in to comment.