github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/arm/mmu.go (about)

     1  // ARM processor support
     2  // https://github.com/usbarmory/tamago
     3  //
     4  // Copyright (c) WithSecure Corporation
     5  // https://foundry.withsecure.com
     6  //
     7  // Use of this source code is governed by the license
     8  // that can be found in the LICENSE file.
     9  
    10  package arm
    11  
    12  import (
    13  	"runtime"
    14  
    15  	"github.com/usbarmory/tamago/internal/reg"
    16  )
    17  
    18  const (
    19  	l1pageTableOffset = 0x4000
    20  	l1pageTableSize   = 4096
    21  
    22  	l2pageTableOffset = 0xc000
    23  	l2pageTableSize   = 256
    24  )
    25  
    26  // Memory region attributes
    27  // (Table B3-10, ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition).
    28  const (
    29  	TTE_PAGE_TABLE    uint32 = (1 << 0)
    30  	TTE_SECTION       uint32 = (1 << 1)
    31  	TTE_BUFFERABLE    uint32 = (1 << 2)
    32  	TTE_CACHEABLE     uint32 = (1 << 3)
    33  	TTE_EXECUTE_NEVER uint32 = (1 << 4)
    34  	TTE_SUPERSECTION  uint32 = (1 << 18) | (1 << 1)
    35  	TTE_NS            uint32 = (1 << 19)
    36  )
    37  
    38  // MMU access permissions
    39  // (Table B3-8, ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition).
    40  const (
    41  	// PL1: no access   PL0: no access
    42  	TTE_AP_000 uint32 = 0b00
    43  	// PL1: read/write  PL0: no access
    44  	TTE_AP_001 uint32 = 0b01
    45  	// PL1: read/write  PL0: read only
    46  	TTE_AP_010 uint32 = 0b10
    47  	// PL1: read/write  PL0: read/write
    48  	TTE_AP_011 uint32 = 0b11
    49  )
    50  
    51  const (
    52  	MemoryRegion = TTE_AP_001<<10 | TTE_CACHEABLE | TTE_BUFFERABLE | TTE_SECTION
    53  	DeviceRegion = TTE_AP_001<<10 | TTE_SECTION
    54  )
    55  
    56  // defined in mmu.s
    57  func flush_tlb()
    58  func set_ttbr0(addr uint32)
    59  
    60  // First level address translation
    61  // 9.4, ARM® Cortex™ -A Series Programmer’s Guide
    62  func (cpu *CPU) initL1Table(entry int, ttbr uint32, section uint32) {
    63  	ramStart, ramEnd := runtime.MemRegion()
    64  	_, textEnd := runtime.TextRegion()
    65  
    66  	for i := uint32(entry); i < l1pageTableSize; i++ {
    67  		page := ttbr + 4*i
    68  		addr := section + (i << 20)
    69  
    70  		switch {
    71  		case addr < textEnd && (addr+(1<<20)) > textEnd:
    72  			// skip first L2 table, reserved to trap null pointers
    73  			l2pageTableStart := cpu.vbar + l2pageTableOffset
    74  			base := l2pageTableStart + l2pageTableSize*4
    75  
    76  			// use L2 table to end non-executable boundary
    77  			// precisely at textStart
    78  			reg.Write(page, base|TTE_PAGE_TABLE)
    79  			cpu.initL2Table(0, base, addr)
    80  		case addr >= ramStart && addr < textEnd:
    81  			reg.Write(page, addr|MemoryRegion)
    82  		case addr >= ramStart && addr < ramEnd:
    83  			reg.Write(page, addr|MemoryRegion|TTE_EXECUTE_NEVER)
    84  		default:
    85  			reg.Write(page, addr|DeviceRegion|TTE_EXECUTE_NEVER)
    86  		}
    87  	}
    88  }
    89  
    90  // Level 2 translation tables
    91  // 9.5, ARM® Cortex™ -A Series Programmer’s Guide
    92  func (cpu *CPU) initL2Table(entry int, base uint32, section uint32) {
    93  	ramStart, ramEnd := runtime.MemRegion()
    94  	_, textEnd := runtime.TextRegion()
    95  
    96  	memoryRegion := TTE_AP_001<<4 | TTE_CACHEABLE | TTE_BUFFERABLE | TTE_SECTION
    97  	deviceRegion := TTE_AP_001<<4 | TTE_SECTION
    98  
    99  	for i := uint32(entry); i < l2pageTableSize; i++ {
   100  		page := base + 4*i
   101  		addr := section + (i << 12)
   102  
   103  		switch {
   104  		case addr >= ramStart && addr < textEnd:
   105  			reg.Write(page, addr|memoryRegion)
   106  		case addr >= ramStart && addr < ramEnd:
   107  			reg.Write(page, addr|memoryRegion|TTE_EXECUTE_NEVER)
   108  		default:
   109  			reg.Write(page, addr|deviceRegion|TTE_EXECUTE_NEVER)
   110  		}
   111  	}
   112  }
   113  
   114  // InitMMU initializes the first-level translation tables for all available
   115  // memory with a flat mapping and privileged attribute flags.
   116  //
   117  // The first 4096 bytes (0x00000000 - 0x00001000) are flagged as invalid to
   118  // trap null pointers, applications that need to make use of this memory space
   119  // must use ConfigureMMU to reconfigure as required.
   120  //
   121  // All available memory is marked as non-executable except for the range
   122  // returned by runtime.TextRegion().
   123  func (cpu *CPU) InitMMU() {
   124  	l1pageTableStart := cpu.vbar + l1pageTableOffset
   125  	l2pageTableStart := cpu.vbar + l2pageTableOffset
   126  
   127  	// Map the first L1 entry to an L2 table to trap null pointers within
   128  	// the smallest possible section (4KB starting from 0x00000000).
   129  	firstSection := l2pageTableStart | TTE_PAGE_TABLE
   130  	reg.Write(l1pageTableStart, firstSection)
   131  
   132  	// set first L2 entry as invalid
   133  	reg.Write(l2pageTableStart, 0)
   134  
   135  	// set remaining entries with flat mapping
   136  	cpu.initL1Table(1, l1pageTableStart, 0)
   137  	cpu.initL2Table(1, l2pageTableStart, 0)
   138  
   139  	set_ttbr0(l1pageTableStart)
   140  }
   141  
   142  // ConfigureMMU (re)configures the first-level translation tables for the
   143  // provided memory range with the argument attribute flags. An alias argument
   144  // greater than zero specifies the physical address corresponding to the start
   145  // argument in case virtual memory is required, otherwise a flat 1:1 mapping is
   146  // set.
   147  func (cpu *CPU) ConfigureMMU(start, end, alias, flags uint32) {
   148  	l1pageTableStart := cpu.vbar + l1pageTableOffset
   149  
   150  	start = start >> 20
   151  	end = end >> 20
   152  	alias = alias >> 20
   153  
   154  	var pa uint32
   155  
   156  	for i := start; i < l1pageTableSize; i++ {
   157  		if i >= end {
   158  			break
   159  		}
   160  
   161  		page := l1pageTableStart + 4*i
   162  
   163  		if alias > 0 {
   164  			pa = (alias + i - start) << 20
   165  		} else {
   166  			pa = i << 20
   167  		}
   168  
   169  		reg.Write(page, pa|flags)
   170  	}
   171  
   172  	cpu.FlushDataCache()
   173  	cpu.FlushTLBs()
   174  }
   175  
   176  func (cpu *CPU) updateMMU(start uint32, end uint32, pos int, mask int, val uint32) {
   177  	l1pageTableStart := cpu.vbar + l1pageTableOffset
   178  
   179  	start = start >> 20
   180  	end = end >> 20
   181  
   182  	for i := start; i < l1pageTableSize; i++ {
   183  		if i >= end {
   184  			break
   185  		}
   186  
   187  		page := l1pageTableStart + 4*i
   188  		reg.SetN(page, pos, mask, val)
   189  	}
   190  
   191  	cpu.FlushDataCache()
   192  	cpu.FlushTLBs()
   193  }
   194  
   195  // SetAccessPermissions (re)configures the first-level translation tables for
   196  // the provided memory range with the argument domain and access permissions.
   197  func (cpu *CPU) SetAccessPermissions(start, end, ap, domain uint32) {
   198  	cpu.updateMMU(start, end, 5, 0b1101111, (ap<<5)|(domain&0xf))
   199  }
   200  
   201  // SetFlags (re)configures the first-level translation tables for the provided
   202  // memory range with the argument attribute flags.
   203  func (cpu *CPU) SetAttributes(start, end, flags uint32) {
   204  	mask := TTE_NS | TTE_SUPERSECTION | TTE_EXECUTE_NEVER | TTE_CACHEABLE |
   205  		TTE_BUFFERABLE | TTE_SECTION | TTE_PAGE_TABLE
   206  
   207  	cpu.updateMMU(start, end, 0, int(mask), flags)
   208  }