blob: b062f85d89ece54000177b02947fe4ff7c0657c6 [file] [log] [blame]
#include "arch_cpu.h"
#include "dlog.h" /* TODO: Remove? */
#include "msr.h"
void arch_vptable_init(struct arch_page_table *table)
{
uint64_t i;
uint64_t v;
dlog("ID_AA64MMFR0_EL1=0x%x\n", read_msr(ID_AA64MMFR0_EL1));
/* TODO: Check each bit. */
for (i = 0; i < 512; i++) {
table->entry0[i] = 1 |
(i << 30) | /* Address */
(1 << 10) | /* Access flag. */
(0 << 8) | /* sh: non-shareable. this preserves EL1. */
(3 << 6) | /* rw */
(0xf << 2); /* normal mem; preserves EL0/1. */
table->entry1[i] = 1 |
((i+512) << 30) | /* Address */
(1 << 10) | /* Access flag. */
(0 << 8) | /* sh: non-shareable. this preserves EL1. */
(3 << 6) | /* rw */
(0xf << 2); /* normal mem; preserves EL0/1. */
table->first[i] = 0;
}
table->first[0] = (uint64_t)&table->entry0[0] | 3;
table->first[1] = (uint64_t)&table->entry1[0] | 3;
/* TODO: Where should this go? */
v =
(1u << 31) | /* RES1. */
(4 << 16) | /* PS: 44 bits. */
(0 << 14) | /* TG0: 4 KB granule. */
(3 << 12) | /* SH0: inner shareable. */
(1 << 10) | /* ORGN0: normal, cacheable ... */
(1 << 8) | /* IRGN0: normal, cacheable ... */
(2 << 6) | /* SL0: Start at level 0. */
(20 << 0); /* T0SZ: 44-bit input address size. */
write_msr(vtcr_el2, v);
}
#if 0
#include "arch.h"
#include <stdint.h>
#include "alloc.h"
#include "log.h"
#include "msr.h"
#define PAGE_BITS 12
#define PAGE_SIZE (1 << PAGE_BITS)
#define ENTRIES_PER_LEVEL (PAGE_SIZE / sizeof(uint64_t))
#define INITIAL_LEVEL 1
extern char text_begin[];
extern char text_end[];
extern char rodata_begin[];
extern char rodata_end[];
extern char data_begin[];
extern char data_end[];
extern char bin_end[];
static uint64_t *ttbr;
static inline size_t mm_entry_size(int level)
{
return 1ull << (PAGE_BITS + (3 - level) * (PAGE_BITS - 3));
}
static inline size_t mm_level_end(size_t va, int level)
{
size_t offset = (PAGE_BITS + (4 - level) * (PAGE_BITS - 3));
return ((va >> offset) + 1) << offset;
}
static inline size_t mm_index(size_t va, int level)
{
size_t v = va >> (PAGE_BITS + (3 - level) * (PAGE_BITS - 3));
return v & ((1 << (PAGE_BITS - 3)) - 1);
}
static inline uint64_t mm_clear_attrs(uint64_t v)
{
/* Clean bottom bits. */
v &= ~((1 << PAGE_BITS) - 1);
/* Clean top bits. */
v &= ((1ull << 59) - 1);
return v;
}
static inline uint64_t *mm_table_ptr(uint64_t pa)
{
return (uint64_t *)mm_clear_attrs(pa);
}
static inline uint64_t mm_mode_to_attrs(uint64_t mode)
{
uint64_t attrs =
(1 << 10) | /* Access flag. */
(2 << 8); /* sh -> outer shareable. */
/* TODO: This is different in s2. */
if (!(mode & MM_X)) {
attrs |= (1ull << 54); /* XN or UXN, [user] execute never. */
/* TODO: This is only ok in EL1, it is RES0 in EL2. */
attrs |= (1ull << 53); /* PXN, privileged execute never. */
}
/* TODO: This is different in s2. */
if (mode & MM_W)
attrs |= (0 << 6); /* rw, no EL0 access. */
else
attrs |= (2 << 6); /* read-only, no EL0 access. */
if (mode & MM_D)
attrs |= (0 << 2); /* device memory in MAIR_ELx. */
else
attrs |= (1 << 2); /* normal memory in MAIR_ELx. */
return attrs;
}
static uint64_t *mm_populate_table(uint64_t *table, uint64_t index)
{
uint64_t *ntable;
uint64_t v = table[index];
uint64_t i;
/* Check if table entry already exists. */
if (v & 1) {
/* Fail if it's a block one. */
if (!(v & 2))
return NULL;
return mm_table_ptr(v);
}
/* Allocate a new table entry and initialize it. */
ntable = halloc_aligned(PAGE_SIZE, PAGE_SIZE);
if (!ntable)
return NULL;
for (i = 0; i < ENTRIES_PER_LEVEL; i++)
ntable[i] = 0;
/* Fill in the new entry. */
table[index] = (size_t)ntable | 0x3;
return ntable;
}
static bool mm_map_level(size_t va, size_t va_end, size_t pa,
uint64_t attrs, uint64_t *table, int level)
{
size_t i = mm_index(va, level);
size_t va_level_end = mm_level_end(va, level);
size_t entry_size = mm_entry_size(level);
/* Cap va_end so that we don't go over of the current level max. */
if (va_end > va_level_end)
va_end = va_level_end;
/* Fill each entry in the table. */
while (va < va_end) {
if (level == 3) {
table[i] = pa | 0x3 | attrs;
} else {
uint64_t *nt = mm_populate_table(table, i);
if (!nt) {
/* TODO: Undo all the work so far? */
return false;
}
if (!mm_map_level(va, va_end, pa, attrs, nt, level+1)) {
/* TODO: Undo all the work so far? */
return false;
}
}
va += entry_size;
pa += entry_size;
i++;
}
return true;
}
bool mm_map_range(size_t va, size_t size, uint64_t pa, uint64_t mode)
{
uint64_t attrs = mm_mode_to_attrs(mode);
uint64_t end = mm_clear_attrs(va + size + PAGE_SIZE - 1);
va = mm_clear_attrs(va);
pa = mm_clear_attrs(pa);
return mm_map_level(va, end, pa, attrs, ttbr, INITIAL_LEVEL);
}
bool mm_map_page(size_t va, size_t pa, uint64_t mode)
{
size_t i;
uint64_t attrs = mm_mode_to_attrs(mode);
uint64_t *table = ttbr;
va = mm_clear_attrs(va);
pa = mm_clear_attrs(pa);
for (i = INITIAL_LEVEL; i < 3; i++) {
table = mm_populate_table(table, mm_index(va, i));
if (!table)
return false;
}
/* We reached level 3. */
i = mm_index(va, 3);
table[i] = pa | 0x3 | attrs;
return true;
}
bool arch_init_mm(void)
{
#if 0
size_t i;
/* Allocate the first level, then zero it out. */
ttbr = halloc_aligned(PAGE_SIZE, PAGE_SIZE);
if (!ttbr)
return false;
for (i = 0; i < ENTRIES_PER_LEVEL; i++)
ttbr[i] = 0;
/* Map page for uart. */
mm_map_page(PL011_BASE, PL011_BASE, MM_R | MM_W | MM_D);
/* Map page for gic. */
mm_map_page(GICD_BASE, GICD_BASE, MM_R | MM_W | MM_D);
mm_map_page(GICC_BASE, GICC_BASE, MM_R | MM_W | MM_D);
/* Map each section. */
mm_map_range((size_t)text_begin, text_end - text_begin,
(size_t)text_begin, MM_X);
mm_map_range((size_t)rodata_begin, rodata_end - rodata_begin,
(size_t)rodata_begin, MM_R);
mm_map_range((size_t)data_begin, data_end - data_begin,
(size_t)data_begin, MM_R | MM_W);
mm_map_range((size_t)bin_end, 20 * 1024 * 1024, (size_t)bin_end,
MM_R | MM_W);
#endif
log(INFO, "About to enable mmu.\n");
enable_mmu(ttbr);
log(INFO, "mmu is on.\n");
return true;
}
static void arch_mm_dump_table(uint64_t *table, int level)
{
uint64_t i, j;
for (i = 0; i < ENTRIES_PER_LEVEL; i++) {
if ((table[i] & 1) == 0)
continue;
for (j = 1 * (level - INITIAL_LEVEL + 1); j; j--)
log(INFO, "\t");
log(INFO, "%x: %x\n", i, table[i]);
if (level >= 3)
continue;
if ((table[i] & 3) == 3)
arch_mm_dump_table(mm_table_ptr(table[i]), level + 1);
}
}
void enable_mmu(uint64_t *table)
{
//uint32_t v;
enable_s2();
#if 0
/*
* 0 -> Device-nGnRnE memory
* 1 -> Normal memory, Inner/Outer Write-Back Non-transient,
* Write-Alloc, Read-Alloc.
*/
write_msr(mair_el2, 0xff00);
write_msr(ttbr0_el2, table);
/*
* Configure tcr_el2.
*/
v =
(1 << 20) | /* TBI, top byte ignored. */
(2 << 16) | /* PS, Physical Address Size, 40 bits, 1TB. */
(0 << 14) | /* TG0, granule size, 4KB. */
(3 << 12) | /* SH0, inner shareable. */
(1 << 10) | /* ORGN0, normal mem, WB RA WA Cacheable. */
(1 << 8) | /* IRGN0, normal mem, WB RA WA Cacheable. */
(25 << 0) | /* T0SZ, input address is 2^39 bytes. */
0;
write_msr(tcr_el2, v);
v =
(1 << 0) | /* M, enable stage 1 EL2 MMU. */
(1 << 1) | /* A, enable alignment check faults. */
// TODO: Enable this.
// (1 << 2) | /* C, data cache enable. */
(1 << 3) | /* SA, enable stack alignment check. */
(3 << 4) | /* RES1 bits. */
(1 << 11) | /* RES1 bit. */
(1 << 12) | /* I, instruction cache enable. */
(1 << 16) | /* RES1 bit. */
(1 << 18) | /* RES1 bit. */
(1 << 19) | /* WXN bit, writable execute never . */
(3 << 22) | /* RES1 bits. */
(3 << 28) | /* RES1 bits. */
0;
__asm volatile("dsb sy");
__asm volatile("isb");
write_msr(sctlr_el2, v);
__asm volatile("isb");
#endif
}
#endif