mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-04 08:17:46 +00:00

On some PAE systems (e.g. TI Keystone), memory is above the 32-bit addressable limit, and the interconnect provides an aliased view of parts of physical memory in the 32-bit addressable space. This alias is strictly for boot time usage, and is not otherwise usable because of coherency limitations. On such systems, the idmap mechanism needs to take this aliased mapping into account. This patch introduces virt_to_idmap() and a arch function pointer which can be populated by platform which needs it. Also populate necessary idmap spots with now available virt_to_idmap(). Avoided #ifdef approach to be compatible with multi-platform builds. Most architecture won't touch it and in that case virt_to_idmap() fall-back to existing virt_to_phys() macro. Cc: Russell King <linux@arm.linux.org.uk> Acked-by: Nicolas Pitre <nico@linaro.org> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
125 lines
3 KiB
C
125 lines
3 KiB
C
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <asm/cputype.h>
|
|
#include <asm/idmap.h>
|
|
#include <asm/pgalloc.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/sections.h>
|
|
#include <asm/system_info.h>
|
|
|
|
pgd_t *idmap_pgd;
|
|
phys_addr_t (*arch_virt_to_idmap) (unsigned long x);
|
|
|
|
#ifdef CONFIG_ARM_LPAE
|
|
static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end,
|
|
unsigned long prot)
|
|
{
|
|
pmd_t *pmd;
|
|
unsigned long next;
|
|
|
|
if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) {
|
|
pmd = pmd_alloc_one(&init_mm, addr);
|
|
if (!pmd) {
|
|
pr_warning("Failed to allocate identity pmd.\n");
|
|
return;
|
|
}
|
|
pud_populate(&init_mm, pud, pmd);
|
|
pmd += pmd_index(addr);
|
|
} else
|
|
pmd = pmd_offset(pud, addr);
|
|
|
|
do {
|
|
next = pmd_addr_end(addr, end);
|
|
*pmd = __pmd((addr & PMD_MASK) | prot);
|
|
flush_pmd_entry(pmd);
|
|
} while (pmd++, addr = next, addr != end);
|
|
}
|
|
#else /* !CONFIG_ARM_LPAE */
|
|
static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end,
|
|
unsigned long prot)
|
|
{
|
|
pmd_t *pmd = pmd_offset(pud, addr);
|
|
|
|
addr = (addr & PMD_MASK) | prot;
|
|
pmd[0] = __pmd(addr);
|
|
addr += SECTION_SIZE;
|
|
pmd[1] = __pmd(addr);
|
|
flush_pmd_entry(pmd);
|
|
}
|
|
#endif /* CONFIG_ARM_LPAE */
|
|
|
|
static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
|
|
unsigned long prot)
|
|
{
|
|
pud_t *pud = pud_offset(pgd, addr);
|
|
unsigned long next;
|
|
|
|
do {
|
|
next = pud_addr_end(addr, end);
|
|
idmap_add_pmd(pud, addr, next, prot);
|
|
} while (pud++, addr = next, addr != end);
|
|
}
|
|
|
|
static void identity_mapping_add(pgd_t *pgd, const char *text_start,
|
|
const char *text_end, unsigned long prot)
|
|
{
|
|
unsigned long addr, end;
|
|
unsigned long next;
|
|
|
|
addr = virt_to_idmap(text_start);
|
|
end = virt_to_idmap(text_end);
|
|
|
|
prot |= PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
|
|
|
|
if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale())
|
|
prot |= PMD_BIT4;
|
|
|
|
pgd += pgd_index(addr);
|
|
do {
|
|
next = pgd_addr_end(addr, end);
|
|
idmap_add_pud(pgd, addr, next, prot);
|
|
} while (pgd++, addr = next, addr != end);
|
|
}
|
|
|
|
extern char __idmap_text_start[], __idmap_text_end[];
|
|
|
|
static int __init init_static_idmap(void)
|
|
{
|
|
idmap_pgd = pgd_alloc(&init_mm);
|
|
if (!idmap_pgd)
|
|
return -ENOMEM;
|
|
|
|
pr_info("Setting up static identity map for 0x%p - 0x%p\n",
|
|
__idmap_text_start, __idmap_text_end);
|
|
identity_mapping_add(idmap_pgd, __idmap_text_start,
|
|
__idmap_text_end, 0);
|
|
|
|
/* Flush L1 for the hardware to see this page table content */
|
|
flush_cache_louis();
|
|
|
|
return 0;
|
|
}
|
|
early_initcall(init_static_idmap);
|
|
|
|
/*
|
|
* In order to soft-boot, we need to switch to a 1:1 mapping for the
|
|
* cpu_reset functions. This will then ensure that we have predictable
|
|
* results when turning off the mmu.
|
|
*/
|
|
void setup_mm_for_reboot(void)
|
|
{
|
|
/* Switch to the identity mapping. */
|
|
cpu_switch_mm(idmap_pgd, &init_mm);
|
|
local_flush_bp_all();
|
|
|
|
#ifdef CONFIG_CPU_HAS_ASID
|
|
/*
|
|
* We don't have a clean ASID for the identity mapping, which
|
|
* may clash with virtual addresses of the previous page tables
|
|
* and therefore potentially in the TLB.
|
|
*/
|
|
local_flush_tlb_all();
|
|
#endif
|
|
}
|