github.com/usbarmory/GoTEE@v0.0.0-20240405084336-c52770d9fcdb/monitor/exec_arm.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 Secure user mode or NonSecure system mode executables.
     9  //
    10  // This package is only meant to be used with `GOOS=tamago GOARCH=arm` 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  	"errors"
    17  	"log"
    18  	"net/rpc"
    19  	"runtime"
    20  	"sync"
    21  
    22  	"github.com/usbarmory/tamago/arm"
    23  	"github.com/usbarmory/tamago/arm/tzc380"
    24  	"github.com/usbarmory/tamago/dma"
    25  	"github.com/usbarmory/tamago/soc/nxp/imx6ul"
    26  )
    27  
    28  var (
    29  	systemVectorTable = arm.SystemVectorTable()
    30  
    31  	monitorVectorTable = arm.VectorTable{
    32  		Reset:         resetMonitor,
    33  		Undefined:     undefinedMonitor,
    34  		Supervisor:    supervisorMonitor,
    35  		PrefetchAbort: prefetchAbortMonitor,
    36  		DataAbort:     dataAbortMonitor,
    37  		IRQ:           irqMonitor,
    38  		FIQ:           fiqMonitor,
    39  	}
    40  )
    41  
    42  var mux sync.Mutex
    43  
    44  // defined in exec_arm.s
    45  func resetMonitor()
    46  func undefinedMonitor()
    47  func supervisorMonitor()
    48  func prefetchAbortMonitor()
    49  func dataAbortMonitor()
    50  func irqMonitor()
    51  func fiqMonitor()
    52  
    53  func init() {
    54  	imx6ul.CSU.Init()
    55  	imx6ul.TZASC.Init()
    56  
    57  	if !imx6ul.Native {
    58  		return
    59  	}
    60  
    61  	tzcAttr := (1 << tzc380.SP_SW_RD) | (1 << tzc380.SP_SW_WR)
    62  
    63  	// redundant enforcement of Region 0 (entire memory space) defaults
    64  	if err := imx6ul.TZASC.EnableRegion(0, 0, 0, tzcAttr); err != nil {
    65  		panic("could not set TZASC defaults")
    66  	}
    67  }
    68  
    69  // Exec allows execution of an executable in Secure user mode or NonSecure
    70  // system mode. The execution is isolated from the invoking Go runtime,
    71  // yielding back to it is supported through exceptions (e.g. syscalls through
    72  // SVC).
    73  //
    74  // The execution context pointer allows task initialization and it is updated
    75  // with the program state at return, it can therefore be passed again to resume
    76  // the task.
    77  func Exec(ctx *ExecCtx)
    78  
    79  // ExecCtx represents a executable initialization or returning state.
    80  type ExecCtx struct {
    81  	R0  uint32
    82  	R1  uint32
    83  	R2  uint32
    84  	R3  uint32
    85  	R4  uint32
    86  	R5  uint32
    87  	R6  uint32
    88  	R7  uint32
    89  	R8  uint32
    90  	R9  uint32
    91  	R10 uint32
    92  	R11 uint32
    93  	R12 uint32
    94  	R13 uint32 // SP
    95  	R14 uint32 // LR
    96  	R15 uint32 // PC
    97  
    98  	// CPSR is the Current Program Status Register of the handler which
    99  	// caught the exception raised by the execution context.
   100  	CPSR uint32
   101  
   102  	// SPSR (Saved Program Status Register) is the CPSR of the execution
   103  	// context as it raised the exception.
   104  	SPSR uint32
   105  
   106  	ExceptionVector int
   107  
   108  	VFP   []uint64 // d0-d31
   109  	FPSCR uint32
   110  	FPEXC uint32
   111  
   112  	// Memory is the executable allocated RAM
   113  	Memory *dma.Region
   114  	// Domain represents the domain ID (0-15) assigned to the executable
   115  	// Memory. The value must be overridden with distinct values if memory
   116  	// isolation is required for parallel execution of different
   117  	// user/secure execution contexts.
   118  	Domain uint32
   119  
   120  	// Handler, if not nil, handles user syscalls
   121  	Handler func(ctx *ExecCtx) error
   122  
   123  	// Server, if not nil, serves RPC calls over syscalls
   124  	Server *rpc.Server
   125  
   126  	// Debug controls activation of debug logs
   127  	Debug bool
   128  
   129  	// execution state
   130  	run bool
   131  	// stopped will be closed once the context has stopped running.
   132  	stopped chan struct{}
   133  	// TrustZone configuration
   134  	ns bool
   135  	// executing g stack pointer
   136  	g_sp uint32
   137  
   138  	// Read() buffer
   139  	in []byte
   140  	// Write() buffer
   141  	out []byte
   142  }
   143  
   144  // Print logs the execution context registers.
   145  func (ctx *ExecCtx) Print() {
   146  	cpsr, spsr := ctx.Mode()
   147  
   148  	log.Printf("   r0:%.8x  r1:%.8x  r2:%.8x  r3:%.8x", ctx.R0, ctx.R1, ctx.R2, ctx.R3)
   149  	log.Printf("   r4:%.8x  r5:%.8x  r6:%.8x  r7:%.8x", ctx.R4, ctx.R5, ctx.R6, ctx.R7)
   150  	log.Printf("   r8:%.8x  r9:%.8x r10:%.8x r11:%.8x cpsr:%.8x (%s)", ctx.R8, ctx.R9, ctx.R10, ctx.R11, ctx.CPSR, arm.ModeName(cpsr))
   151  	log.Printf("  r12:%.8x  sp:%.8x  lr:%.8x  pc:%.8x spsr:%.8x (%s)", ctx.R12, ctx.R13, ctx.R14, ctx.R15, ctx.SPSR, arm.ModeName(spsr))
   152  }
   153  
   154  // NonSecure returns whether the execution context is loaded as non-secure.
   155  func (ctx *ExecCtx) NonSecure() bool {
   156  	return ctx.ns
   157  }
   158  
   159  // Mode returns the processor mode.
   160  func (ctx *ExecCtx) Mode() (current int, saved int) {
   161  	current = int(ctx.CPSR & 0x1f)
   162  	saved = int(ctx.SPSR & 0x1f)
   163  	return
   164  }
   165  
   166  func (ctx *ExecCtx) schedule() (err error) {
   167  	mux.Lock()
   168  	defer mux.Unlock()
   169  
   170  	// set monitor handlers
   171  	arm.SetVectorTable(monitorVectorTable)
   172  
   173  	// execute context
   174  	Exec(ctx)
   175  
   176  	// restore default handlers
   177  	arm.SetVectorTable(systemVectorTable)
   178  
   179  	mode, _ := ctx.Mode()
   180  
   181  	switch mode {
   182  	case arm.IRQ_MODE, arm.FIQ_MODE, arm.SVC_MODE, arm.MON_MODE:
   183  		return
   184  	default:
   185  		if ctx.Debug {
   186  			ctx.Print()
   187  		}
   188  
   189  		return errors.New(arm.ModeName(mode))
   190  	}
   191  
   192  	return
   193  }
   194  
   195  // Run starts the execution context and handles system or monitor calls. The
   196  // execution yields back to the invoking Go runtime only when exceptions are
   197  // caught.
   198  //
   199  // The function invokes the context Handler() and returns when an unhandled
   200  // exception, or any other error, is raised.
   201  func (ctx *ExecCtx) Run() (err error) {
   202  	ctx.run = true
   203  	ctx.stopped = make(chan struct{})
   204  	defer close(ctx.stopped)
   205  
   206  	ap := arm.TTE_AP_001
   207  
   208  	if !ctx.ns {
   209  		ap = arm.TTE_AP_011
   210  	}
   211  
   212  	// Set privilege level and domain access
   213  	imx6ul.ARM.SetAccessPermissions(
   214  		uint32(ctx.Memory.Start()), uint32(ctx.Memory.End()),
   215  		ap, ctx.Domain,
   216  	)
   217  
   218  	for ctx.run {
   219  		if err = ctx.schedule(); err != nil {
   220  			break
   221  		}
   222  
   223  		if ctx.Handler != nil {
   224  			if err = ctx.Handler(ctx); err != nil {
   225  				break
   226  			}
   227  		}
   228  
   229  		// Return to next instruction when handling interrupts
   230  		// (Table 11-3, ARM® Cortex™ -A Series Programmer’s Guide).
   231  		switch ctx.ExceptionVector {
   232  		case arm.IRQ, arm.FIQ:
   233  			ctx.R15 -= 4
   234  		}
   235  
   236  		runtime.Gosched()
   237  	}
   238  
   239  	return
   240  }
   241  
   242  // Stop stops the execution context.
   243  func (ctx *ExecCtx) Stop() {
   244  	mux.Lock()
   245  	defer mux.Unlock()
   246  
   247  	ctx.run = false
   248  }
   249  
   250  // Done returns a channel which will be closed once execution context has stopped.
   251  func (ctx *ExecCtx) Done() chan struct{} {
   252  	return ctx.stopped
   253  }
   254  
   255  // Load returns an execution context initialized for the argument entry point
   256  // and memory region, the secure flag controls whether the context belongs to a
   257  // secure partition (e.g. TrustZone Secure World) or a non-secure one (e.g.
   258  // TrustZone Normal World).
   259  //
   260  // In case of a non-secure execution context, the memory is configured as
   261  // NonSecure by means of MMU NS bit and memory controller region configuration.
   262  //
   263  // The caller is responsible for any other required MMU configuration (see
   264  // arm.ConfigureMMU()) or additional peripheral restrictions (e.g. TrustZone).
   265  func Load(entry uint, mem *dma.Region, secure bool) (ctx *ExecCtx, err error) {
   266  	ctx = &ExecCtx{
   267  		R15:    uint32(entry),
   268  		VFP:    make([]uint64, 32),
   269  		Memory: mem,
   270  		Server: rpc.NewServer(),
   271  		ns:     !secure,
   272  	}
   273  
   274  	if secure {
   275  		// enable VFP
   276  		ctx.FPEXC = 1 << arm.FPEXC_EN
   277  	}
   278  
   279  	if secure {
   280  		ctx.Handler = SecureHandler
   281  	} else {
   282  		ctx.Handler = NonSecureHandler
   283  	}
   284  
   285  	tzcAttr := (1 << tzc380.SP_NW_RD) | (1 << tzc380.SP_NW_WR)
   286  
   287  	if ctx.ns && imx6ul.Native {
   288  		// allow NonSecure World R/W access to its own memory
   289  		if err = imx6ul.TZASC.EnableRegion(1, uint32(mem.Start()), uint32(mem.Size()), tzcAttr); err != nil {
   290  			return
   291  		}
   292  	}
   293  
   294  	// set all mask bits
   295  	ctx.SPSR = (0b111 << 6)
   296  	flags := arm.MemoryRegion
   297  
   298  	if ctx.ns {
   299  		// The NS bit is required to ensure that cache lines are kept
   300  		// separate.
   301  		ctx.SPSR |= arm.SYS_MODE
   302  		flags |= arm.TTE_NS
   303  	} else {
   304  		ctx.SPSR |= arm.USR_MODE
   305  	}
   306  
   307  	imx6ul.ARM.SetAttributes(uint32(mem.Start()), uint32(mem.End()), flags)
   308  
   309  	return
   310  }