rust-raspberrypi-OS-tutorials/10_virtualmemory/mmu.c
2018-01-06 16:49:26 +01:00

178 lines
7.2 KiB
C

/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* 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 "gpio.h" // get MMIO_BASE
#include "uart.h"
#define PAGESIZE 4096
// granularity
#define PT_PAGE 0b11 // 4k granule
#define PT_BLOCK 0b01 // 2M granule
// accessibility
#define PT_KERNEL (0<<6) // privileged, supervisor EL1 access only
#define PT_USER (1<<6) // unprivileged, EL0 access allowed
#define PT_RW (0<<7) // read-write
#define PT_RO (1<<7) // read-only
#define PT_AF (1<<10) // accessed flag
#define PT_NX (1UL<<54) // no execute
// shareability
#define PT_OSH (2<<8) // outter shareable
#define PT_ISH (3<<8) // inner shareable
// defined in MAIR register
#define PT_MEM (0<<2) // normal memory
#define PT_DEV (1<<2) // device MMIO
#define PT_NC (2<<2) // non-cachable
#define TTBR_ENABLE 1
// get addresses from linker
extern volatile unsigned char _data;
extern volatile unsigned char _end;
/**
* Set up page translation tables and enable virtual memory
*/
void mmu_init()
{
unsigned long r, b, *paging=(unsigned long*)&_end;
/* create MMU translation tables at _end */
// TTBR0, identity L1
paging[0]=(unsigned long)((unsigned char*)&_end+2*PAGESIZE) | // physical address
PT_PAGE | // it has the "Present" flag, which must be set, and we have area in it mapped by pages
PT_AF | // accessed flag. Without this we're going to have a Data Abort exception
PT_USER | // non-privileged
PT_ISH | // inner shareable
PT_MEM; // normal memory
// identity L2, first 2M block
paging[2*512]=(unsigned long)((unsigned char*)&_end+3*PAGESIZE) | // physical address
PT_PAGE | // we have area in it mapped by pages
PT_AF | // accessed flag
PT_USER | // non-privileged
PT_ISH | // inner shareable
PT_MEM; // normal memory
// identity L2 2M blocks
b=MMIO_BASE>>21;
// skip 0th, as we're about to map it by L3
for(r=1;r<512;r++)
paging[2*512+r]=(unsigned long)((r<<21)) | // physical address
PT_BLOCK | // map 2M block
PT_AF | // accessed flag
PT_NX | // no execute
PT_USER | // non-privileged
(r>=b? PT_OSH|PT_DEV : PT_ISH|PT_MEM); // different attributes for device memory
// identity L3
for(r=0;r<512;r++)
paging[3*512+r]=(unsigned long)(r*PAGESIZE) | // physical address
PT_PAGE | // map 4k
PT_AF | // accessed flag
PT_USER | // non-privileged
PT_ISH | // inner shareable
((r<0x80||r>(unsigned long)&_data/PAGESIZE)? PT_RW|PT_NX : PT_RO); // different for code and data
// TTBR1, kernel L1
paging[512+511]=(unsigned long)((unsigned char*)&_end+4*PAGESIZE) | // physical address
PT_PAGE | // we have area in it mapped by pages
PT_AF | // accessed flag
PT_KERNEL | // privileged
PT_ISH | // inner shareable
PT_MEM; // normal memory
// kernel L2
paging[4*512+511]=(unsigned long)((unsigned char*)&_end+5*PAGESIZE) | // physical address
PT_PAGE | // we have area in it mapped by pages
PT_AF | // accessed flag
PT_KERNEL | // privileged
PT_ISH | // inner shareable
PT_MEM; // normal memory
// kernel L3
paging[5*512]=(unsigned long)(MMIO_BASE+0x00201000) | // physical address
PT_PAGE | // map 4k
PT_AF | // accessed flag
PT_NX | // no execute
PT_KERNEL | // privileged
PT_OSH | // outter shareable
PT_DEV; // device memory
/* okay, now we have to set system registers to enable MMU */
// check for 4k granule and at least 36 bits physical address bus */
asm volatile ("mrs %0, id_aa64mmfr0_el1" : "=r" (r));
b=r&0xF;
if(r&(0xF<<28)/*4k*/ || b<1/*36 bits*/) {
uart_puts("ERROR: 4k granule or 36 bit address space not supported\n");
return;
}
// first, set Memory Attributes array, indexed by PT_MEM, PT_DEV, PT_NC in our example
r= (0xFF << 0) | // AttrIdx=0: normal, IWBWA, OWBWA, NTR
(0x04 << 8) | // AttrIdx=1: device, nGnRE (must be OSH too)
(0x44 <<16); // AttrIdx=2: non cacheable
asm volatile ("msr mair_el1, %0" : : "r" (r));
// next, specify mapping characteristics in translate control register
r= (0b00LL << 37) | // TBI=0, no tagging
(b << 32) | // IPS=autodetected
(0b10LL << 30) | // TG1=4k
(0b11LL << 28) | // SH1=3 inner
(0b01LL << 26) | // ORGN1=1 write back
(0b01LL << 24) | // IRGN1=1 write back
(0b0LL << 23) | // EPD1 enable higher half
(25LL << 16) | // T1SZ=25, 3 levels (512G)
(0b00LL << 14) | // TG0=4k
(0b11LL << 12) | // SH0=3 inner
(0b01LL << 10) | // ORGN0=1 write back
(0b01LL << 8) | // IRGN0=1 write back
(0b0LL << 7) | // EPD0 enable lower half
(25LL << 0); // T0SZ=25, 3 levels (512G)
asm volatile ("msr tcr_el1, %0; isb" : : "r" (r));
// tell the MMU where our translation tables are. TTBR_ENABLE bit not documented, but required
// lower half, user space
asm volatile ("msr ttbr0_el1, %0" : : "r" ((unsigned long)&_end + TTBR_ENABLE));
// upper half, kernel space
asm volatile ("msr ttbr1_el1, %0" : : "r" ((unsigned long)&_end + TTBR_ENABLE + PAGESIZE));
// finally, toggle some bits in system control register to enable page translation
asm volatile ("dsb ish; isb; mrs %0, sctlr_el1" : "=r" (r));
r|=0xC00800; // set mandatory reserved bits
r&=~((1<<25) | // clear EE, little endian translation tables
(1<<24) | // clear E0E
(1<<19) | // clear WXN
(1<<12) | // clear I, no instruction cache
(1<<4) | // clear SA0
(1<<3) | // clear SA
(1<<2) | // clear C, no cache at all
(1<<1)); // clear A, no aligment check
r|= (1<<0); // set M, enable MMU
asm volatile ("msr sctlr_el1, %0; isb" : : "r" (r));
}