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  }