blob: f55f86687df89bb278faa3bb9a5c138a375fdab1 [file] [log] [blame]
/*
* Copyright (c) 2015 Stefan Kristiansson
* Based on arch/arm/arm/mmu.c
* Copyright (c) 2008-2014 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <trace.h>
#include <debug.h>
#include <err.h>
#include <string.h>
#include <arch/mmu.h>
#include <arch/or1k.h>
#include <arch/or1k/mmu.h>
#include <kernel/vm.h>
#define LOCAL_TRACE 0
#if WITH_KERNEL_VM
uint32_t or1k_kernel_translation_table[256] __ALIGNED(8192) __SECTION(".bss.prebss.translation_table");
/* Pessimistic tlb invalidation, which rather invalidate too much.
* TODO: make it more precise. */
void or1k_invalidate_tlb(vaddr_t vaddr, uint count)
{
uint32_t dmmucfgr = mfspr(OR1K_SPR_SYS_DMMUCFGR_ADDR);
uint32_t immucfgr = mfspr(OR1K_SPR_SYS_IMMUCFGR_ADDR);
uint32_t num_dtlb_ways = OR1K_SPR_SYS_DMMUCFGR_NTW_GET(dmmucfgr) + 1;
uint32_t num_dtlb_sets = 1 << OR1K_SPR_SYS_DMMUCFGR_NTS_GET(dmmucfgr);
uint32_t num_itlb_ways = OR1K_SPR_SYS_IMMUCFGR_NTW_GET(immucfgr) + 1;
uint32_t num_itlb_sets = 1 << OR1K_SPR_SYS_IMMUCFGR_NTS_GET(immucfgr);
uint32_t offs;
for (; count; count--) {
offs = (vaddr >> PAGE_SIZE_SHIFT) & (num_dtlb_sets-1);
switch (num_dtlb_ways) {
case 4:
mtspr_off(0, OR1K_SPR_DMMU_DTLBW_MR_ADDR(3, offs), 0);
case 3:
mtspr_off(0, OR1K_SPR_DMMU_DTLBW_MR_ADDR(2, offs), 0);
case 2:
mtspr_off(0, OR1K_SPR_DMMU_DTLBW_MR_ADDR(1, offs), 0);
case 1:
mtspr_off(0, OR1K_SPR_DMMU_DTLBW_MR_ADDR(0, offs), 0);
}
offs = (vaddr >> PAGE_SIZE_SHIFT) & (num_itlb_sets-1);
switch (num_itlb_ways) {
case 4:
mtspr_off(0, OR1K_SPR_IMMU_ITLBW_MR_ADDR(3, offs), 0);
case 3:
mtspr_off(0, OR1K_SPR_IMMU_ITLBW_MR_ADDR(2, offs), 0);
case 2:
mtspr_off(0, OR1K_SPR_IMMU_ITLBW_MR_ADDR(1, offs), 0);
case 1:
mtspr_off(0, OR1K_SPR_IMMU_ITLBW_MR_ADDR(0, offs), 0);
}
vaddr += PAGE_SIZE;
}
}
status_t arch_mmu_query(vaddr_t vaddr, paddr_t *paddr, uint *flags)
{
uint index = vaddr / SECTION_SIZE;
uint32_t pte = or1k_kernel_translation_table[index];
uint32_t vmask = SECTION_SIZE-1;
if (!(pte & OR1K_MMU_PG_PRESENT))
return ERR_NOT_FOUND;
/* not a l1 entry */
if (!(pte & OR1K_MMU_PG_L)) {
uint32_t *l2_table = paddr_to_kvaddr(pte & ~OR1K_MMU_PG_FLAGS_MASK);
index = (vaddr % SECTION_SIZE) / PAGE_SIZE;
pte = l2_table[index];
vmask = PAGE_SIZE-1;
}
if (paddr)
*paddr = (pte & ~OR1K_MMU_PG_FLAGS_MASK) | (vaddr & vmask);
if (flags) {
*flags = 0;
if (pte & OR1K_MMU_PG_U)
*flags |= ARCH_MMU_FLAG_PERM_USER;
if (!(pte & OR1K_MMU_PG_X))
*flags |= ARCH_MMU_FLAG_PERM_NO_EXECUTE;
if (!(pte & OR1K_MMU_PG_W))
*flags |= ARCH_MMU_FLAG_PERM_RO;
if (pte & OR1K_MMU_PG_CI)
*flags |= ARCH_MMU_FLAG_UNCACHED;
}
return NO_ERROR;
}
int arch_mmu_unmap(vaddr_t vaddr, uint count)
{
LTRACEF("vaddr = 0x%x, count = %d\n", vaddr, count);
if (!IS_PAGE_ALIGNED(vaddr))
return ERR_INVALID_ARGS;
uint unmapped = 0;
while (count) {
uint index = vaddr / SECTION_SIZE;
uint32_t pte = or1k_kernel_translation_table[index];
if (!(pte & OR1K_MMU_PG_PRESENT)) {
vaddr += PAGE_SIZE;
count--;
continue;
}
/* Unmapping of l2 tables is not implemented (yet) */
if (!(pte & OR1K_MMU_PG_L) || !IS_ALIGNED(vaddr, SECTION_SIZE) || count < SECTION_SIZE / PAGE_SIZE)
PANIC_UNIMPLEMENTED;
or1k_kernel_translation_table[index] = 0;
or1k_invalidate_tlb(vaddr, SECTION_SIZE / PAGE_SIZE);
vaddr += SECTION_SIZE;
count -= SECTION_SIZE / PAGE_SIZE;
unmapped += SECTION_SIZE / PAGE_SIZE;
}
return unmapped;
}
int arch_mmu_map(vaddr_t vaddr, paddr_t paddr, uint count, uint flags)
{
uint l1_index;
uint32_t pte;
uint32_t arch_flags = 0;
LTRACEF("vaddr = 0x%x, paddr = 0x%x, count = %d, flags = 0x%x\n", vaddr, paddr, count, flags);
if (!IS_PAGE_ALIGNED(vaddr) || !IS_PAGE_ALIGNED(paddr))
return ERR_INVALID_ARGS;
if (flags & ARCH_MMU_FLAG_PERM_USER)
arch_flags |= OR1K_MMU_PG_U;
if (!(flags & ARCH_MMU_FLAG_PERM_NO_EXECUTE))
arch_flags |= OR1K_MMU_PG_X;
if (flags & ARCH_MMU_FLAG_CACHE_MASK)
arch_flags |= OR1K_MMU_PG_CI;
if (!(flags & ARCH_MMU_FLAG_PERM_RO))
arch_flags |= OR1K_MMU_PG_W;
uint mapped = 0;
while (count) {
l1_index = vaddr / SECTION_SIZE;
if (IS_ALIGNED(vaddr, SECTION_SIZE) && IS_ALIGNED(paddr, SECTION_SIZE) && count >= SECTION_SIZE / PAGE_SIZE) {
or1k_kernel_translation_table[l1_index] = (paddr & ~(SECTION_SIZE-1)) | arch_flags | OR1K_MMU_PG_PRESENT | OR1K_MMU_PG_L;
count -= SECTION_SIZE / PAGE_SIZE;
mapped += SECTION_SIZE / PAGE_SIZE;
vaddr += SECTION_SIZE;
paddr += SECTION_SIZE;
continue;
}
uint32_t *l2_table;
pte = or1k_kernel_translation_table[l1_index];
/* FIXME: l1 already mapped as a section */
if (pte & OR1K_MMU_PG_PRESENT && pte & OR1K_MMU_PG_L)
PANIC_UNIMPLEMENTED;
if (pte & OR1K_MMU_PG_PRESENT) {
l2_table = paddr_to_kvaddr(pte & ~OR1K_MMU_PG_FLAGS_MASK);
LTRACEF("l2_table at %p\n", l2_table);
} else {
l2_table = pmm_alloc_kpage();
if (!l2_table) {
TRACEF("failed to allocate pagetable\n");
return mapped;
}
memset(l2_table, 0, PAGE_SIZE);
paddr_t l2_pa = kvaddr_to_paddr(l2_table);
LTRACEF("allocated pagetable at %p, pa 0x%lx\n", l2_table, l2_pa);
or1k_kernel_translation_table[l1_index] = l2_pa | arch_flags | OR1K_MMU_PG_PRESENT;
}
uint l2_index = (vaddr % SECTION_SIZE) / PAGE_SIZE;
LTRACEF("l2_index = 0x%x, vaddr = 0x%x, paddr = 0x%x\n", l2_index, vaddr, paddr);
l2_table[l2_index] = paddr | arch_flags | OR1K_MMU_PG_PRESENT | OR1K_MMU_PG_L;
count--;
mapped++;
vaddr += PAGE_SIZE;
paddr += PAGE_SIZE;
}
return mapped;
}
#endif /* WITH_KERNEL_VM */