mmu.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * Copyright (C) 2018 bzt (bztsrc@github)
  3. *
  4. * Permission is hereby granted, free of charge, to any person
  5. * obtaining a copy of this software and associated documentation
  6. * files (the "Software"), to deal in the Software without
  7. * restriction, including without limitation the rights to use, copy,
  8. * modify, merge, publish, distribute, sublicense, and/or sell copies
  9. * of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be
  13. * included in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  18. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  19. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  20. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  22. * DEALINGS IN THE SOFTWARE.
  23. *
  24. */
  25. #include "gpio.h" // get MMIO_BASE
  26. #include "uart.h"
  27. #define PAGESIZE 4096
  28. // granularity
  29. #define PT_PAGE 0b11 // 4k granule
  30. #define PT_BLOCK 0b01 // 2M granule
  31. // accessibility
  32. #define PT_KERNEL (0<<6) // privileged, supervisor EL1 access only
  33. #define PT_USER (1<<6) // unprivileged, EL0 access allowed
  34. #define PT_RW (0<<7) // read-write
  35. #define PT_RO (1<<7) // read-only
  36. #define PT_AF (1<<10) // accessed flag
  37. #define PT_NX (1UL<<54) // no execute
  38. // shareability
  39. #define PT_OSH (2<<8) // outter shareable
  40. #define PT_ISH (3<<8) // inner shareable
  41. // defined in MAIR register
  42. #define PT_MEM (0<<2) // normal memory
  43. #define PT_DEV (1<<2) // device MMIO
  44. #define PT_NC (2<<2) // non-cachable
  45. #define TTBR_ENABLE 1
  46. // get addresses from linker
  47. extern volatile unsigned char _data;
  48. extern volatile unsigned char _end;
  49. /**
  50. * Set up page translation tables and enable virtual memory
  51. */
  52. void mmu_init()
  53. {
  54. unsigned long data_page = (unsigned long)&_data/PAGESIZE;
  55. unsigned long r, b, *paging=(unsigned long*)&_end;
  56. /* create MMU translation tables at _end */
  57. // TTBR0, identity L1
  58. paging[0]=(unsigned long)((unsigned char*)&_end+2*PAGESIZE) | // physical address
  59. PT_PAGE | // it has the "Present" flag, which must be set, and we have area in it mapped by pages
  60. PT_AF | // accessed flag. Without this we're going to have a Data Abort exception
  61. PT_USER | // non-privileged
  62. PT_ISH | // inner shareable
  63. PT_MEM; // normal memory
  64. // identity L2, first 2M block
  65. paging[2*512]=(unsigned long)((unsigned char*)&_end+3*PAGESIZE) | // physical address
  66. PT_PAGE | // we have area in it mapped by pages
  67. PT_AF | // accessed flag
  68. PT_USER | // non-privileged
  69. PT_ISH | // inner shareable
  70. PT_MEM; // normal memory
  71. // identity L2 2M blocks
  72. b=MMIO_BASE>>21;
  73. // skip 0th, as we're about to map it by L3
  74. for(r=1;r<512;r++)
  75. paging[2*512+r]=(unsigned long)((r<<21)) | // physical address
  76. PT_BLOCK | // map 2M block
  77. PT_AF | // accessed flag
  78. PT_NX | // no execute
  79. PT_USER | // non-privileged
  80. (r>=b? PT_OSH|PT_DEV : PT_ISH|PT_MEM); // different attributes for device memory
  81. // identity L3
  82. for(r=0;r<512;r++)
  83. paging[3*512+r]=(unsigned long)(r*PAGESIZE) | // physical address
  84. PT_PAGE | // map 4k
  85. PT_AF | // accessed flag
  86. PT_USER | // non-privileged
  87. PT_ISH | // inner shareable
  88. ((r<0x80||r>data_page)? PT_RW|PT_NX : PT_RO); // different for code and data
  89. // TTBR1, kernel L1
  90. paging[512+511]=(unsigned long)((unsigned char*)&_end+4*PAGESIZE) | // physical address
  91. PT_PAGE | // we have area in it mapped by pages
  92. PT_AF | // accessed flag
  93. PT_KERNEL | // privileged
  94. PT_ISH | // inner shareable
  95. PT_MEM; // normal memory
  96. // kernel L2
  97. paging[4*512+511]=(unsigned long)((unsigned char*)&_end+5*PAGESIZE) | // physical address
  98. PT_PAGE | // we have area in it mapped by pages
  99. PT_AF | // accessed flag
  100. PT_KERNEL | // privileged
  101. PT_ISH | // inner shareable
  102. PT_MEM; // normal memory
  103. // kernel L3
  104. paging[5*512]=(unsigned long)(MMIO_BASE+0x00201000) | // physical address
  105. PT_PAGE | // map 4k
  106. PT_AF | // accessed flag
  107. PT_NX | // no execute
  108. PT_KERNEL | // privileged
  109. PT_OSH | // outter shareable
  110. PT_DEV; // device memory
  111. /* okay, now we have to set system registers to enable MMU */
  112. // check for 4k granule and at least 36 bits physical address bus */
  113. asm volatile ("mrs %0, id_aa64mmfr0_el1" : "=r" (r));
  114. b=r&0xF;
  115. if(r&(0xF<<28)/*4k*/ || b<1/*36 bits*/) {
  116. uart_puts("ERROR: 4k granule or 36 bit address space not supported\n");
  117. return;
  118. }
  119. // first, set Memory Attributes array, indexed by PT_MEM, PT_DEV, PT_NC in our example
  120. r= (0xFF << 0) | // AttrIdx=0: normal, IWBWA, OWBWA, NTR
  121. (0x04 << 8) | // AttrIdx=1: device, nGnRE (must be OSH too)
  122. (0x44 <<16); // AttrIdx=2: non cacheable
  123. asm volatile ("msr mair_el1, %0" : : "r" (r));
  124. // next, specify mapping characteristics in translate control register
  125. r= (0b00LL << 37) | // TBI=0, no tagging
  126. (b << 32) | // IPS=autodetected
  127. (0b10LL << 30) | // TG1=4k
  128. (0b11LL << 28) | // SH1=3 inner
  129. (0b01LL << 26) | // ORGN1=1 write back
  130. (0b01LL << 24) | // IRGN1=1 write back
  131. (0b0LL << 23) | // EPD1 enable higher half
  132. (25LL << 16) | // T1SZ=25, 3 levels (512G)
  133. (0b00LL << 14) | // TG0=4k
  134. (0b11LL << 12) | // SH0=3 inner
  135. (0b01LL << 10) | // ORGN0=1 write back
  136. (0b01LL << 8) | // IRGN0=1 write back
  137. (0b0LL << 7) | // EPD0 enable lower half
  138. (25LL << 0); // T0SZ=25, 3 levels (512G)
  139. asm volatile ("msr tcr_el1, %0; isb" : : "r" (r));
  140. // tell the MMU where our translation tables are. TTBR_ENABLE bit not documented, but required
  141. // lower half, user space
  142. asm volatile ("msr ttbr0_el1, %0" : : "r" ((unsigned long)&_end + TTBR_ENABLE));
  143. // upper half, kernel space
  144. asm volatile ("msr ttbr1_el1, %0" : : "r" ((unsigned long)&_end + TTBR_ENABLE + PAGESIZE));
  145. // finally, toggle some bits in system control register to enable page translation
  146. asm volatile ("dsb ish; isb; mrs %0, sctlr_el1" : "=r" (r));
  147. r|=0xC00800; // set mandatory reserved bits
  148. r&=~((1<<25) | // clear EE, little endian translation tables
  149. (1<<24) | // clear E0E
  150. (1<<19) | // clear WXN
  151. (1<<12) | // clear I, no instruction cache
  152. (1<<4) | // clear SA0
  153. (1<<3) | // clear SA
  154. (1<<2) | // clear C, no cache at all
  155. (1<<1)); // clear A, no aligment check
  156. r|= (1<<0); // set M, enable MMU
  157. asm volatile ("msr sctlr_el1, %0; isb" : : "r" (r));
  158. }