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

     1  // ARM TrustZone Address Space Controller TZC-380 driver
     2  // https://github.com/usbarmory/tamago
     3  //
     4  // IP: ARM CoreLinkā„¢ TrustZone Address Space Controller TZC-380
     5  //
     6  // Copyright (c) WithSecure Corporation
     7  // https://foundry.withsecure.com
     8  //
     9  // Use of this source code is governed by the license
    10  // that can be found in the LICENSE file.
    11  
    12  // Package tzc380 implements a driver for the ARM TrustZone Address Space
    13  // Controller TZC-380.
    14  //
    15  // Note that the TZASC must be initialized early in the boot process, see
    16  // TZASC.Bypass for information.
    17  //
    18  // The driver is based on the following reference specifications:
    19  //   - TZC-380 TRM - CoreLinkā„¢ TrustZone Address Space Controller TZC-380 - Revision: r0p1
    20  //
    21  // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as
    22  // supported by the TamaGo framework for bare metal Go on ARM SoCs, see
    23  // https://github.com/usbarmory/tamago.
    24  package tzc380
    25  
    26  import (
    27  	"errors"
    28  
    29  	"github.com/usbarmory/tamago/bits"
    30  	"github.com/usbarmory/tamago/internal/reg"
    31  )
    32  
    33  // TZASC registers
    34  // (p37, Table 3-1 Register summary, TZC-380 TRM).
    35  const (
    36  	TZASC_CONF   = 0x000
    37  	CONF_REGIONS = 0
    38  
    39  	TZASC_LOCKDOWN_RANGE  = 0x008
    40  	TZASC_LOCKDOWN_SELECT = 0x00c
    41  	TZASC_SEC_INV_EN      = 0x034
    42  
    43  	TZASC_REGION_SETUP_LOW_0  = 0x100
    44  	TZASC_REGION_SETUP_HIGH_0 = 0x104
    45  
    46  	TZASC_REGION_ATTRS_0 = 0x108
    47  	REGION_ATTRS_SP      = 28
    48  	REGION_ATTRS_SIZE    = 1
    49  	REGION_ATTRS_EN      = 0
    50  
    51  	SIZE_MIN = 0b001110
    52  	SIZE_MAX = 0b111111
    53  )
    54  
    55  // TZASC security permissions,
    56  // (p28, Table 2-4, TZC-380 TRM).
    57  const (
    58  	// Secure Read Access bit
    59  	SP_SW_RD = 3
    60  	// Secure Write Access bit
    61  	SP_SW_WR = 2
    62  	// NonSecure Read Access bit
    63  	SP_NW_RD = 1
    64  	// NonSecure Write Access bit
    65  	SP_NW_WR = 0
    66  )
    67  
    68  // TZASC represents the TrustZone Address Space Controller instance.
    69  type TZASC struct {
    70  	// Base register
    71  	Base uint32
    72  
    73  	// The bypass register controls the TZASC monitoring of DDR
    74  	// transactions.
    75  	//
    76  	// To use the TZASC, the bypass must be disabled early in the boot
    77  	// process, before DDR use. This is a one time operation, until the
    78  	// next power-up cycle.
    79  	//
    80  	// To do so the register can be written in the board DCD file (e.g.
    81  	// imximage.cfg in usbarmory package):
    82  	// 	`DATA 4 0x020e4024 0x00000001`
    83  	//
    84  	// The register must be specified in the TZASC instance for
    85  	// verification purposes.
    86  	Bypass uint32
    87  
    88  	// Secure Boot Lock signal (see 2.2.8 TZC-380 TRM)
    89  	SecureBootLockReg uint32
    90  	SecureBootLockPos int
    91  
    92  	// control registers
    93  	conf                uint32
    94  	lockdown_range      uint32
    95  	lockdown_select     uint32
    96  	sec_inv_en          uint32
    97  	region_setup_low_0  uint32
    98  	region_setup_high_0 uint32
    99  	region_attrs_0      uint32
   100  }
   101  
   102  // Init initializes the TrustZone Address Space Controller (TZASC).
   103  func (hw *TZASC) Init() {
   104  	if hw.Base == 0 || hw.Bypass == 0 || hw.SecureBootLockReg == 0 {
   105  		panic("invalid TZASC instance")
   106  	}
   107  
   108  	hw.conf = hw.Base + TZASC_CONF
   109  	hw.lockdown_range = hw.Base + TZASC_LOCKDOWN_RANGE
   110  	hw.lockdown_select = hw.Base + TZASC_LOCKDOWN_SELECT
   111  	hw.sec_inv_en = hw.Base + TZASC_SEC_INV_EN
   112  	hw.region_setup_low_0 = hw.Base + TZASC_REGION_SETUP_LOW_0
   113  	hw.region_setup_high_0 = hw.Base + TZASC_REGION_SETUP_HIGH_0
   114  	hw.region_attrs_0 = hw.Base + TZASC_REGION_ATTRS_0
   115  }
   116  
   117  // Regions returns the number of regions that the TZASC provides.
   118  func (hw *TZASC) Regions() int {
   119  	return int(reg.Get(hw.conf, CONF_REGIONS, 0xf)) + 1
   120  }
   121  
   122  // EnableSecurityInversion allows configuration of arbitrary security
   123  // permissions, disabling automatic enabling of secure access on non-secure
   124  // only permissions
   125  // (p49, 3.2.12 Security Inversion Enable Register, TZC-380 TRM).
   126  func (hw *TZASC) EnableSecurityInversion() {
   127  	reg.Set(hw.sec_inv_en, 0)
   128  }
   129  
   130  // EnableRegion configures a TZASC region with the argument start address, size
   131  // and security permissions, for region 0 only security permissions are
   132  // relevant.
   133  func (hw *TZASC) EnableRegion(n int, start uint32, size uint32, sp int) (err error) {
   134  	var attrs uint32
   135  	var s uint32
   136  
   137  	if n < 0 || n+1 > hw.Regions() {
   138  		return errors.New("invalid region index")
   139  	}
   140  
   141  	if reg.Read(hw.Bypass) != 1 {
   142  		return errors.New("TZASC inactive (bypass detected)")
   143  	}
   144  
   145  	if n == 0 {
   146  		reg.SetN(hw.region_attrs_0, REGION_ATTRS_SP, 0b1111, uint32(sp))
   147  		return
   148  	}
   149  
   150  	if start%(1<<15) != 0 {
   151  		return errors.New("incompatible start address")
   152  	}
   153  
   154  	if start != 0 && (start%size) != 0 {
   155  		return errors.New("start address must be a multiple of its region size")
   156  	}
   157  
   158  	if sp > 0b1111 {
   159  		return errors.New("invalid security permissions")
   160  	}
   161  
   162  	// size = 2^(s+1)
   163  	for i := uint32(SIZE_MIN); i <= SIZE_MAX; i++ {
   164  		if size == (1 << (i + 1)) {
   165  			s = i
   166  			break
   167  		}
   168  	}
   169  
   170  	if s == 0 {
   171  		return errors.New("incompatible region size")
   172  	}
   173  
   174  	bits.SetN(&attrs, REGION_ATTRS_SP, 0b1111, uint32(sp))
   175  	bits.SetN(&attrs, REGION_ATTRS_SIZE, 0b111111, s)
   176  	bits.Set(&attrs, REGION_ATTRS_EN)
   177  
   178  	off := uint32(0x10 * n)
   179  
   180  	reg.Write(hw.region_setup_low_0+off, start&0xffff8000)
   181  	reg.Write(hw.region_setup_high_0+off, 0)
   182  	reg.Write(hw.region_attrs_0+off, attrs)
   183  
   184  	return
   185  }
   186  
   187  // DisableRegion disables a TZASC region.
   188  func (hw *TZASC) DisableRegion(n int) (err error) {
   189  	if n < 0 || n+1 > hw.Regions() {
   190  		return errors.New("invalid region index")
   191  	}
   192  
   193  	if reg.Read(hw.Bypass) != 1 {
   194  		return errors.New("TZASC inactive (bypass detected)")
   195  	}
   196  
   197  	reg.Clear(hw.region_attrs_0+uint32(0x10*n), REGION_ATTRS_EN)
   198  
   199  	return
   200  }
   201  
   202  // Lock enables TZASC secure boot lock register writing restrictions
   203  // (p30, 2.2.8 Preventing writes to registers and using secure_boot_lock, TZC-380 TRM).
   204  func (hw *TZASC) Lock() {
   205  	reg.Write(hw.lockdown_range, 0xffffffff)
   206  	reg.Write(hw.lockdown_select, 0xffffffff)
   207  	reg.Set(hw.SecureBootLockReg, hw.SecureBootLockPos)
   208  }