github.com/usbarmory/GoTEE@v0.0.0-20240405084336-c52770d9fcdb/monitor/exec_riscv64.go (about) 1 // Copyright (c) WithSecure Corporation 2 // https://foundry.withsecure.com 3 // 4 // Use of this source code is governed by the license 5 // that can be found in the LICENSE file. 6 7 // Package monitor provides supervisor support for TamaGo unikernels to allow 8 // scheduling of isolated supervisor executables. 9 // 10 // This package is only meant to be used with `GOOS=tamago GOARCH=riscv64` as 11 // supported by the TamaGo framework for bare metal Go on ARM SoCs, see 12 // https://github.com/usbarmory/tamago. 13 package monitor 14 15 import ( 16 "fmt" 17 "log" 18 "net/rpc" 19 "runtime" 20 "sync" 21 22 "github.com/usbarmory/tamago/dma" 23 "github.com/usbarmory/tamago/riscv" 24 "github.com/usbarmory/tamago/soc/sifive/fu540" 25 ) 26 27 // RISC-V privilege levels 28 const ( 29 User = 0b00 // U 30 Supervisor = 0b01 // S 31 Machine = 0b11 // M 32 ) 33 34 var mux sync.Mutex 35 36 // defined in exec_riscv64.s 37 func monitor() 38 39 func init() { 40 if err := fu540.RV64.WritePMP(0, (1<<64)-1, true, true, true, riscv.PMP_A_TOR, false); err != nil { 41 panic("could not set PMP default entry") 42 } 43 } 44 45 // Exec allows execution of an executable in supervisor mode. The execution is 46 // isolated from the invoking Go runtime, yielding back to it is supported 47 // through exceptions (e.g. syscalls through ECALL). 48 // 49 // The execution context pointer allows task initialization and it is updated 50 // with the program state at return, it can therefore be passed again to resume 51 // the task. 52 func Exec(ctx *ExecCtx) 53 54 // ExecCtx represents a executable initialization or returning state. 55 type ExecCtx struct { 56 X1 uint64 // RA 57 X2 uint64 // SP 58 X3 uint64 // GP 59 X4 uint64 // TP 60 X5 uint64 // T0 61 X6 uint64 // T1 62 X7 uint64 // T2 63 X8 uint64 // S0/FP 64 X9 uint64 // S1 65 X10 uint64 // A0 66 X11 uint64 // A1 67 X12 uint64 // A2 68 X13 uint64 // A3 69 X14 uint64 // A4 70 X15 uint64 // A5 71 X16 uint64 // A6 72 X17 uint64 // A7 73 X18 uint64 // S2 74 X19 uint64 // S3 75 X20 uint64 // S4 76 X21 uint64 // S5 77 X22 uint64 // S6 78 X23 uint64 // S7 79 X24 uint64 // S8 80 X25 uint64 // S9 81 X26 uint64 // S10 82 X27 uint64 // S11 (g) 83 X28 uint64 // T3 84 X29 uint64 // T4 85 X30 uint64 // T5 86 X31 uint64 // T6 87 88 // Program Counter 89 PC uint64 90 // Machine Exception Program Counter 91 MEPC uint64 92 // Machine Cause 93 MCAUSE uint64 94 95 // floating-point registers 96 F [32]uint64 // F0-F31 97 // Floating-point Control and Status Register 98 FCSR uint64 99 100 // Memory is the executable allocated RAM 101 Memory *dma.Region 102 103 // PMP, if not nil, handles physical memory protection for the 104 // environment invoking the execution context. 105 // 106 // The integer argument is passed as first available PMP entry, the PMP 107 // function must not modify previous entries as they are used at 108 // scheduling to grant execution context access to its own memory. 109 PMP func(ctx *ExecCtx, pmpEntry int) error 110 111 // Handler, if not nil, handles syscalls 112 Handler func(ctx *ExecCtx) error 113 114 // Server, if not nil, serves RPC calls over syscalls 115 Server *rpc.Server 116 117 // Debug controls activation of debug logs 118 Debug bool 119 120 // execution state 121 run bool 122 // stopped will be closed once the context has stopped running. 123 stopped chan struct{} 124 // trusted applet flag 125 secure bool 126 // executing g stack pointer 127 g_sp uint64 128 129 // Read() buffer 130 in []byte 131 // Write() buffer 132 out []byte 133 } 134 135 // Print logs the execution context registers. 136 func (ctx *ExecCtx) Print() { 137 code, _ := ctx.Cause() 138 139 log.Printf(" ra:%.16x sp:%.16x gp:%.16x tp:%.16x", ctx.X1, ctx.X2, ctx.X3, ctx.X4) 140 log.Printf(" t0:%.16x t1:%.16x t2:%.16x s0:%.16x", ctx.X5, ctx.X6, ctx.X7, ctx.X8) 141 log.Printf(" s1:%.16x a0:%.16x a1:%.16x a2:%.16x", ctx.X9, ctx.X10, ctx.X11, ctx.X12) 142 log.Printf(" a3:%.16x a4:%.16x a5:%.16x a6:%.16x", ctx.X13, ctx.X14, ctx.X15, ctx.X16) 143 log.Printf(" a7:%.16x s2:%.16x s3:%.16x s4:%.16x", ctx.X17, ctx.X18, ctx.X19, ctx.X20) 144 log.Printf(" s5:%.16x s6:%.16x s7:%.16x s8:%.16x", ctx.X21, ctx.X22, ctx.X23, ctx.X24) 145 log.Printf(" s9:%.16x s10:%.16x s11:%.16x t3:%.16x", ctx.X25, ctx.X26, ctx.X27, ctx.X28) 146 log.Printf(" t4:%.16x t5:%.16x t6:%.16x pc:%.16x err:%d", ctx.X29, ctx.X30, ctx.X31, ctx.PC, code) 147 } 148 149 // Secure returns whether the execution context is loaded as trusted applet. 150 func (ctx *ExecCtx) Secure() bool { 151 return ctx.secure 152 } 153 154 // Cause returns the trap event. 155 func (ctx *ExecCtx) Cause() (code uint64, irq bool) { 156 code = (ctx.MCAUSE &^ (1 << 63)) 157 irq = (ctx.MCAUSE >> 63) == 1 158 return 159 } 160 161 func (ctx *ExecCtx) schedule() (err error) { 162 var pmpEntry int 163 164 mux.Lock() 165 defer mux.Unlock() 166 167 // set monitor handlers 168 fu540.RV64.SetExceptionHandler(monitor) 169 170 // grant execution context access to its own memory 171 if pmpEntry, err = ctx.pmp(); err != nil { 172 return 173 } 174 175 if ctx.PMP != nil { 176 // set up application physical memory protection 177 if err = ctx.PMP(ctx, pmpEntry); err != nil { 178 return 179 } 180 } 181 182 // execute applet 183 Exec(ctx) 184 185 // restore default handlers 186 fu540.RV64.SetExceptionHandler(riscv.DefaultExceptionHandler) 187 188 code, irq := ctx.Cause() 189 190 if code != riscv.EnvironmentCallFromS || irq { 191 if ctx.Debug { 192 ctx.Print() 193 } 194 195 return fmt.Errorf("%x", code) 196 } 197 198 return 199 } 200 201 // Run starts the execution context and handles system or monitor calls. The 202 // execution yields back to the invoking Go runtime only when exceptions are 203 // caught. 204 // 205 // The function invokes the context Handler() and returns when an unhandled 206 // exception, or any other error, is raised. 207 func (ctx *ExecCtx) Run() (err error) { 208 ctx.run = true 209 ctx.stopped = make(chan struct{}) 210 defer close(ctx.stopped) 211 212 for ctx.run { 213 if err = ctx.schedule(); err != nil { 214 break 215 } 216 217 if ctx.Handler != nil { 218 if err = ctx.Handler(ctx); err != nil { 219 break 220 } 221 } 222 223 runtime.Gosched() 224 } 225 226 return 227 } 228 229 // Stop stops the execution context. 230 func (ctx *ExecCtx) Stop() { 231 mux.Lock() 232 defer mux.Unlock() 233 234 ctx.run = false 235 } 236 237 // Done returns a channel which will be closed once execution context has stopped. 238 func (ctx *ExecCtx) Done() chan struct{} { 239 return ctx.stopped 240 } 241 242 // Load returns an execution context initialized for the argument entry point 243 // and memory region. 244 // 245 // Any additional peripheral restrictions are up to the caller. 246 func Load(entry uint, mem *dma.Region, secure bool) (ctx *ExecCtx, err error) { 247 ctx = &ExecCtx{ 248 PC: uint64(entry), 249 Memory: mem, 250 Server: rpc.NewServer(), 251 secure: secure, 252 } 253 254 if secure { 255 ctx.Handler = SecureHandler 256 } else { 257 ctx.Handler = NonSecureHandler 258 } 259 260 return 261 } 262 263 // pmp grants context access to its own memory. 264 func (ctx *ExecCtx) pmp() (pmpEntry int, err error) { 265 if err = fu540.RV64.WritePMP(pmpEntry, uint64(ctx.Memory.Start()), false, false, false, riscv.PMP_A_OFF, false); err != nil { 266 return 267 } 268 pmpEntry += 1 269 270 if err = fu540.RV64.WritePMP(pmpEntry, uint64(ctx.Memory.End()), true, true, true, riscv.PMP_A_TOR, false); err != nil { 271 return 272 } 273 pmpEntry += 1 274 275 return 276 }