Browse Source

migrate MMU initialization and exception handling from tamago-go to linked application

pull/26/head
Andrea Barisani 2 months ago
parent
commit
046dc6d13c
  1. 4
      arm/arm.go
  2. 18
      arm/cache.go
  3. 141
      arm/exception.go
  4. 86
      arm/exception.s
  5. 109
      arm/mmu.go
  6. 49
      arm/mmu.s
  7. 1
      board/f-secure/usbarmory/mark-two/usbarmory.go
  8. 2
      go.mod
  9. 14
      soc/bcm2835/bcm2835.go
  10. 9
      soc/imx6/imx6.go

4
arm/arm.go

@ -56,10 +56,10 @@ type CPU struct {
func read_cpsr() uint32
func read_scr() uint32
// Init performs ARM processor instance initialization by detecting its
// available features.
// Init performs initialization of an ARM core instance.
func (cpu *CPU) Init() {
cpu.initFeatures()
cpu.initVectorTable()
}
// Mode returns the processor mode.

18
arm/cache.go

@ -28,26 +28,26 @@ func cache_flush_instruction()
// performed (p115, Cortex™-A7 MPCore® Technical Reference Manual r0p5).
func (cpu *CPU) EnableSMP() {
aux := read_actlr()
aux |= (1 << ACTLR_SMP)
aux |= 1 << ACTLR_SMP
write_actlr(aux)
}
// CacheEnable activates the ARM instruction and data caches.
func (cpu *CPU) CacheEnable() {
// EnableCache activates the ARM instruction and data caches.
func (cpu *CPU) EnableCache() {
cache_enable()
}
// CacheDisable disables the ARM instruction and data caches.
func (cpu *CPU) CacheDisable() {
// DisableCache disables the ARM instruction and data caches.
func (cpu *CPU) DisableCache() {
cache_disable()
}
// CacheFlushData flushes the ARM data cache.
func (cpu *CPU) CacheFlushData() {
// FlushDataCache flushes the ARM data cache.
func (cpu *CPU) FlushDataCache() {
cache_flush_data()
}
// CacheFlushInstruction flushes the ARM instruction cache.
func (cpu *CPU) CacheFlushInstruction() {
// FlushInstructionCache flushes the ARM instruction cache.
func (cpu *CPU) FlushInstructionCache() {
cache_flush_instruction()
}

141
arm/exception.go

@ -10,14 +10,16 @@
package arm
import (
"fmt"
_ "unsafe"
"runtime"
"unsafe"
"github.com/f-secure-foundry/tamago/internal/reg"
)
// ARM exception vector offsets
// Table 11-1 ARM® Cortex™ -A Series Programmer’s Guide
const (
RESET = 0x0
RESET = 0x00
UNDEFINED = 0x04
SUPERVISOR = 0x08
PREFETCH_ABORT = 0x0c
@ -26,24 +28,69 @@ const (
FIQ = 0x1c
)
var exceptionHandlerFn = defaultExceptionHandler
const (
vecTableJump uint32 = 0xe59ff018 // ldr pc, [pc, #24]
//go:linkname exceptionHandler runtime.exceptionHandler
func exceptionHandler(off int) {
exceptionHandlerFn(off)
}
vecTableOffset = 0
vecTableSize = 0x4000 // 16 kB
func defaultExceptionHandler(off int) {
mode := int(read_cpsr() & 0x1f)
msg := fmt.Sprintf("unhandled exception, vector %#x (%s), mode %#x (%s)", off, VectorName(off), mode, ModeName(mode))
panic(msg)
}
excStackOffset = 0x8000 // 32 kB
excStackSize = 0x4000 // 16 kB
)
// ExceptionHandler overrides the default exception handler, the passed
// function receives the exception vector offset as argument.
func ExceptionHandler(fn func(int)) {
exceptionHandlerFn = fn
}
var (
// ExceptionHandler defines the global exception handler, the function
// is executed at any exception, in system mode on the Go runtime stack
// within goroutine g0.
ExceptionHandler = DefaultExceptionHandler
// ResetHandler defines the reset exception handler, executed within
// supervisor mode and its stack. SetExceptionHandlers() must be
// invoked to update the vector table when changed.
ResetHandler = resetHandler
// UndefinedHandler defines the undefined exception handler, executed
// within undefined mode and its stack. SetExceptionHandlers() must be
// invoked to update the vector table when changed.
UndefinedHandler = undefinedHandler
// SupervisorHandler defines the supervisor call exception handler,
// executed within supervisor mode and its stack.
// SetExceptionHandlers() must be invoked to update the vector table
// when changed.
SupervisorHandler = svcHandler
// PrefetchAbortHandler defines the prefetch abort exception handler,
// executed within abort mode and its stack. SetExceptionHandlers()
// must be invoked to update the vector table when changed.
PrefetchAbortHandler = prefetchAbortHandler
// DataAbortHandler defines the data abort exception handler, executed
// within abort mode and its stack. SetExceptionHandlers() must be
// invoked to update the vector table when changed.
DataAbortHandler = dataAbortHandler
// IRQHandler defines the IRQ interrupt exception handler, executed
// within IRQ mode and its stack. SetExceptionHandlers() must be
// invoked to update the vector table when changed.
IRQHandler = irqHandler
// FIQHandler defines the FIQ interrupt exception handler, executed
// within FIQ mode and its stack. SetExceptionHandlers() must be
// invoked to update the vector table when changed.
FIQHandler = fiqHandler
)
// defined in exception.s
func set_exc_stack(addr uint32)
func set_vbar(addr uint32)
func resetHandler()
func undefinedHandler()
func svcHandler()
func prefetchAbortHandler()
func dataAbortHandler()
func irqHandler()
func fiqHandler()
// VectorName returns the exception vector offset name.
func VectorName(off int) string {
@ -66,3 +113,61 @@ func VectorName(off int) string {
return "Unknown"
}
func DefaultExceptionHandler(off int) {
print("exception: vector ", off, " mode ", int(read_cpsr()&0x1f), "\n")
panic("unhandled exception")
}
func systemException(off int) {
ExceptionHandler(off)
}
func fnAddress(fn func()) uint32 {
return **((**uint32)(unsafe.Pointer(&fn)))
}
// SetExceptionHandlers updates the exception handling vector table with the
// functions defined in the related global variables. It must be invoked
// whenever handling functions are changed.
func SetExceptionHandlers() {
ramStart, _ := runtime.MemRegion()
vecTableStart := ramStart + vecTableOffset
// end of jump entries
off := vecTableStart + 8*4
// set handler pointers
// Table 11-1 ARM® Cortex™ -A Series Programmer’s Guide
reg.Write(off+0*4, fnAddress(ResetHandler))
reg.Write(off+1*4, fnAddress(UndefinedHandler))
reg.Write(off+2*4, fnAddress(SupervisorHandler))
reg.Write(off+3*4, fnAddress(PrefetchAbortHandler))
reg.Write(off+4*4, fnAddress(DataAbortHandler))
reg.Write(off+5*4, fnAddress(IRQHandler))
reg.Write(off+6*4, fnAddress(FIQHandler))
}
//go:nosplit
func (cpu *CPU) initVectorTable() {
ramStart, _ := runtime.MemRegion()
vecTableStart := ramStart + vecTableOffset
// initialize jump table
// Table 11-1 ARM® Cortex™ -A Series Programmer’s Guide
for i := uint32(0); i < 8; i++ {
reg.Write(vecTableStart+4*i, vecTableJump)
}
// set exception handlers
SetExceptionHandlers()
// set vector base address register
set_vbar(vecTableStart)
// Set the stack pointer for exception modes to provide a stack when
// summoned by exception vectors.
excStackStart := ramStart + excStackOffset
set_exc_stack(excStackStart + excStackSize)
}

86
arm/exception.s

@ -0,0 +1,86 @@
// ARM processor support
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
#include "textflag.h"
// func set_exc_stack(addr uint32)
TEXT ·set_exc_stack(SB),NOSPLIT,$0-4
MOVW addr+0(FP), R0
// Set IRQ mode SP
WORD $0xe321f0d2 // msr CPSR_c, 0xd2
MOVW R0, R13
// Set Abort mode SP
WORD $0xe321f0d7 // msr CPSR_c, 0xd7
MOVW R0, R13
// Set Undefined mode SP
WORD $0xe321f0db // msr CPSR_c, 0xdb
MOVW R0, R13
// Set Supervisor mode SP
WORD $0xe321f0d3 // msr CPSR_c, 0xd3
MOVW R0, R13
// Return to System mode
WORD $0xe321f0df // msr CPSR_c, 0xdf
RET
// func set_vbar(addr uint32)
TEXT ·set_vbar(SB),NOSPLIT,$0-4
MOVW addr+0(FP), R0
MCR 15, 0, R0, C12, C0, 0
RET
#define EXCEPTION(OFFSET, FN, LROFFSET, RN, SAVE_SIZE) \
/* restore stack pointer */ \
WORD $0xe105d200 /* mrs sp, SP_usr */ \
\
/* remove exception specific LR offset */ \
SUB $LROFFSET, R14, R14 \
\
/* save caller registers */ \
MOVM.DB.W [R0-RN, R14], (R13) /* push {r0-rN, r14} */ \
\
/* call exception handler on g0 */ \
MOVW $OFFSET, R0 \
MOVW $FN(SB), R1 \
MOVW $SAVE_SIZE, R2 \
MOVW R14, R3 \
CALL runtime·CallOnG0(SB) \
\
/* restore registers */ \
MOVM.IA.W (R13), [R0-RN, R14] /* pop {r0-rN, r14} */ \
\
/* restore PC from LR and mode */ \
ADD $LROFFSET, R14, R14 \
MOVW.S R14, R15
TEXT ·resetHandler(SB),NOSPLIT|NOFRAME,$0
EXCEPTION(0x0, ·systemException, 0, R12, 56)
TEXT ·undefinedHandler(SB),NOSPLIT|NOFRAME,$0
EXCEPTION(0x4, ·systemException, 4, R12, 56)
TEXT ·svcHandler(SB),NOSPLIT|NOFRAME,$0
EXCEPTION(0x8, ·systemException, 0, R12, 56)
TEXT ·prefetchAbortHandler(SB),NOSPLIT|NOFRAME,$0
EXCEPTION(0xc, ·systemException, 4, R12, 56)
TEXT ·dataAbortHandler(SB),NOSPLIT|NOFRAME,$0
EXCEPTION(0x10, ·systemException, 8, R12, 56)
TEXT ·irqHandler(SB),NOSPLIT|NOFRAME,$0
EXCEPTION(0x18, ·systemException, 4, R12, 56)
TEXT ·fiqHandler(SB),NOSPLIT|NOFRAME,$0
EXCEPTION(0x1c, ·systemException, 4, R7, 36)

109
arm/mmu.go

@ -0,0 +1,109 @@
// ARM processor support
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
package arm
import (
"runtime"
"github.com/f-secure-foundry/tamago/internal/reg"
)
const (
l1pageTableOffset = 0x4000 // 16 kB
l1pageTableSize = 0x4000 // 16 kB
)
// Memory region attributes
// Table B3-10 ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition
const (
TTE_SECTION_1MB uint32 = 0x2
TTE_SECTION_16MB uint32 = 0x40002
TTE_EXECUTE_NEVER uint32 = 0x10
TTE_CACHEABLE uint32 = 0x8
TTE_BUFFERABLE uint32 = 0x4
)
// MMU access permissions
// Table B3-8 ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition
const (
// PL1: no access PL0: no access
TTE_AP_000 uint32 = 0b000000 << 10
// PL1: read/write PL0: no access
TTE_AP_001 uint32 = 0b000001 << 10
// PL1: read/write PL0: read only
TTE_AP_010 uint32 = 0b000010 << 10
// PL1: read/write PL0: read/write
TTE_AP_011 uint32 = 0b000011 << 10
// Reserved
TTE_AP_100 uint32 = 0b100000 << 10
// PL1: read only PL0: no access
TTE_AP_101 uint32 = 0b100001 << 10
// PL1: read only PL0: read only
TTE_AP_110 uint32 = 0b100010 << 10
// PL1: read only PL0: read only
TTE_AP_111 uint32 = 0b100011 << 10
)
// defined in mmu.s
func set_ttbr0(addr uint32)
// ConfigureMMU (re)configures the first-level translation tables for the
// provided memory range with the passed attribute flags.
func (cpu *CPU) ConfigureMMU(start uint32, end uint32, flags uint32) {
ramStart, _ := runtime.MemRegion()
l1pageTableStart := ramStart + l1pageTableOffset
start = start >> 20
end = end >> 20
for i := uint32(1); i < l1pageTableSize/4; i++ {
page := l1pageTableStart + 4*i
pa := i << 20
if i < start {
continue
}
if i >= end {
break
}
reg.Write(page, pa|flags)
}
set_ttbr0(l1pageTableStart)
}
// InitMMU initializes the first-level translation tables for all available
// memory with a flat mapping and privileged attribute flags.
func (cpu *CPU) InitMMU() {
start, end := runtime.MemRegion()
l1pageTableStart := start + l1pageTableOffset
memAttr := uint32(TTE_AP_001 | TTE_CACHEABLE | TTE_BUFFERABLE | TTE_SECTION_1MB)
devAttr := uint32(TTE_AP_001 | TTE_SECTION_1MB)
start = start >> 20
end = end >> 20
// skip page zero to trap null pointers
for i := uint32(1); i < l1pageTableSize/4; i++ {
page := l1pageTableStart + 4*i
pa := i << 20
if i >= start && i < end {
reg.Write(page, pa|memAttr)
} else {
reg.Write(page, pa|devAttr)
}
}
set_ttbr0(l1pageTableStart)
}

49
arm/mmu.s

@ -0,0 +1,49 @@
// ARM processor support
// https://github.com/f-secure-foundry/tamago
//
// Copyright (c) F-Secure Corporation
// https://foundry.f-secure.com
//
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
#include "textflag.h"
// func set_ttbr0(addr uint32)
TEXT ·set_ttbr0(SB),NOSPLIT,$0-4
// Data Memory Barrier
MOVW $0, R0
MCR 15, 0, R0, C7, C10, 5
// Invalidate Instruction Cache + DSB
MOVW $0, R1
MCR 15, 0, R1, C7, C5, 0
MCR 15, 0, R1, C7, C10, 4
MOVW addr+0(FP), R0
// Invalidate unified TLB
MCR 15, 0, R0, C8, C7, 0 // TLBIALL
// Set TTBR0
MCR 15, 0, R0, C2, C0, 0
// Use TTBR0 for translation table walks
MOVW $0x0, R0
MCR 15, 0, R0, C2, C0, 2
// Set Domain Access
MOVW $0x1, R0
MCR 15, 0, R0, C3, C0, 0
// Invalidate Instruction Cache + DSB
MOVW $0, R0
MCR 15, 0, R0, C7, C5, 0
MCR 15, 0, R0, C7, C10, 4
// Enable MMU
MRC 15, 0, R0, C1, C0, 0
ORR $0x1, R0
MCR 15, 0, R0, C1, C0, 0
RET

1
board/f-secure/usbarmory/mark-two/usbarmory.go

@ -34,6 +34,7 @@ func Model() (model string) {
//
//go:linkname Init runtime.hwinit
func Init() {
// initialize SoC
imx6.Init()
// initialize serial console

2
go.mod

@ -1,3 +1,3 @@
module github.com/f-secure-foundry/tamago
go 1.15
go 1.16

14
soc/bcm2835/bcm2835.go

@ -20,13 +20,8 @@ import (
"github.com/f-secure-foundry/tamago/arm"
)
const (
// nanos - should be same value as arm/timer.go refFreq
refFreq int64 = 1000000000
)
//go:linkname ramStackOffset runtime.ramStackOffset
var ramStackOffset uint32 = 0x100000 // 1 MB
// nanos - should be same value as arm/timer.go refFreq
const refFreq int64 = 1000000000
// DRAM_FLAG_NOCACHE disables caching by setting to high bits
const DRAM_FLAG_NOCACHE = 0xC0000000
@ -39,6 +34,9 @@ var peripheralBase uint32
// ARM processor instance
var ARM = &arm.CPU{}
//go:linkname ramStackOffset runtime.ramStackOffset
var ramStackOffset uint32 = 0x100000 // 1 MB
//go:linkname nanotime1 runtime.nanotime1
func nanotime1() int64 {
return int64(read_systimer() * ARM.TimerMultiplier)
@ -55,6 +53,8 @@ func Init(base uint32) {
// required when booting in SDP mode
ARM.EnableSMP()
// MMU initialization is required to take advantage of data cache
ARM.InitMMU()
ARM.CacheEnable()
ARM.TimerMultiplier = refFreq / SysTimerFreq

9
soc/imx6/imx6.go

@ -77,13 +77,20 @@ func nanotime1() int64 {
// Init takes care of the lower level SoC initialization triggered early in
// runtime setup.
func Init() {
if ARM.Mode() != arm.SYS_MODE {
// initialization required only when in PL1
return
}
ARM.Init()
ARM.EnableVFP()
// required when booting in SDP mode
ARM.EnableSMP()
ARM.CacheEnable()
// MMU initialization is required to take advantage of data cache
ARM.InitMMU()
ARM.EnableCache()
_, fam, revMajor, revMinor := SiliconVersion()
Family = fam

Loading…
Cancel
Save