mirror of
https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials.git
synced 2024-11-11 07:10:59 +00:00
178 lines
7.2 KiB
C
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));
|
||
|
}
|