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

     1  // ARM Generic Interrupt Controller (GIC) driver
     2  // https://github.com/usbarmory/tamago
     3  //
     4  // IP: ARM Generic Interrupt Controller version 2.0
     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 gic implements a driver for the ARM Generic Interrupt Controller.
    13  //
    14  // The driver is based on the following reference specifications:
    15  //   - ARM IHI 0048B.b - ARM Generic Interrupt Controller - Architecture version 2.0
    16  //
    17  // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as
    18  // supported by the TamaGo framework for bare metal Go on ARM SoCs, see
    19  // https://github.com/usbarmory/tamago.
    20  package gic
    21  
    22  import (
    23  	"github.com/usbarmory/tamago/internal/reg"
    24  )
    25  
    26  const (
    27  	// GIC offsets in Cortex-A7
    28  	// (p178, Table 8-1, Cortex-A7 MPCore Technical Reference Manual).
    29  	GICD_OFF = 0x1000
    30  	GICC_OFF = 0x2000
    31  
    32  	// Distributor register map
    33  	// (p75, Table 4-1, ARM Generic Interrupt Controller Architecture Specification).
    34  	GICD_CTLR            = 0x000
    35  	GICD_CTLR_ENABLEGRP1 = 1
    36  	GICD_CTLR_ENABLEGRP0 = 0
    37  
    38  	GICD_TYPER         = 0x004
    39  	GICD_TYPER_ITLINES = 0
    40  
    41  	GICD_IGROUPR   = 0x080
    42  	GICD_ISENABLER = 0x100
    43  	GICD_ICENABLER = 0x180
    44  	GICD_ICPENDR   = 0x280
    45  
    46  	// CPU interface register map
    47  	// (p76, Table 4-2, ARM Generic Interrupt Controller Architecture Specification).
    48  	GICC_CTLR            = 0x0000
    49  	GICC_CTLR_FIQEN      = 3
    50  	GICC_CTLR_ENABLEGRP1 = 1
    51  	GICC_CTLR_ENABLEGRP0 = 0
    52  
    53  	GICC_PMR          = 0x0004
    54  	GICC_PMR_PRIORITY = 0
    55  
    56  	GICC_IAR    = 0x000c
    57  	GICC_IAR_ID = 0
    58  
    59  	GICC_EOIR    = 0x0010
    60  	GICC_EOIR_ID = 0
    61  
    62  	GICC_AIAR    = 0x0020
    63  	GICC_AIAR_ID = 0
    64  
    65  	GICC_AEOIR    = 0x0024
    66  	GICC_AEOIR_ID = 0
    67  )
    68  
    69  // GIC represents the Generic Interrupt Controller instance.
    70  type GIC struct {
    71  	// Base register
    72  	Base uint32
    73  
    74  	// control registers
    75  	gicd uint32
    76  	gicc uint32
    77  }
    78  
    79  // InitGIC initializes the ARM Generic Interrupt Controller (GIC).
    80  func (hw *GIC) Init(secure bool, fiqen bool) {
    81  	if hw.Base == 0 {
    82  		panic("invalid GIC instance")
    83  	}
    84  
    85  	hw.gicd = hw.Base + GICD_OFF
    86  	hw.gicc = hw.Base + GICC_OFF
    87  
    88  	// Get the maximum number of external interrupt lines
    89  	itLinesNum := reg.Get(hw.gicd+GICD_TYPER, GICD_TYPER_ITLINES, 0x1f)
    90  
    91  	// Add a line for the 32 internal interrupts
    92  	itLinesNum += 1
    93  
    94  	for n := uint32(0); n < itLinesNum; n++ {
    95  		// Disable interrupts
    96  		addr := hw.gicd + GICD_ICENABLER + 4*n
    97  		reg.Write(addr, 0xffffffff)
    98  
    99  		// Clear pending interrupts
   100  		addr = hw.gicd + GICD_ICPENDR + 4*n
   101  		reg.Write(addr, 0xffffffff)
   102  
   103  		if !secure {
   104  			addr = hw.gicd + GICD_IGROUPR + 4*n
   105  			reg.Write(addr, 0xffffffff)
   106  		}
   107  	}
   108  
   109  	// Set priority mask to allow Non-Secure world to use the lower half
   110  	// of the priority range.
   111  	reg.Write(hw.gicc+GICC_PMR, 0x80)
   112  
   113  	reg.SetTo(hw.gicc+GICC_CTLR, GICC_CTLR_FIQEN, fiqen)
   114  
   115  	reg.Set(hw.gicc+GICC_CTLR, GICC_CTLR_ENABLEGRP1)
   116  	reg.Set(hw.gicc+GICC_CTLR, GICC_CTLR_ENABLEGRP0)
   117  
   118  	reg.Set(hw.gicd+GICD_CTLR, GICD_CTLR_ENABLEGRP1)
   119  	reg.Set(hw.gicd+GICD_CTLR, GICD_CTLR_ENABLEGRP0)
   120  }
   121  
   122  // FIQEn controls whether Group 0 (Secure) interrupts should be signalled as
   123  // IRQ or FIQ requests.
   124  func (hw *GIC) FIQEn(fiq bool) {
   125  	if hw.gicc == 0 {
   126  		return
   127  	}
   128  
   129  	reg.SetTo(hw.gicc+GICC_CTLR, GICC_CTLR_FIQEN, fiq)
   130  }
   131  
   132  func irq(gicd uint32, m int, secure bool, enable bool) {
   133  	if gicd == 0 {
   134  		return
   135  	}
   136  
   137  	var addr uint32
   138  
   139  	n := uint32(m / 32)
   140  	i := m % 32
   141  
   142  	if enable {
   143  		reg.SetTo(gicd + GICD_IGROUPR + 4*n, i, !secure)
   144  		addr = gicd + GICD_ISENABLER + 4*n
   145  	} else {
   146  		addr = gicd + GICD_ICENABLER + 4*n
   147  	}
   148  
   149  	reg.SetTo(addr, i, true)
   150  }
   151  
   152  // EnableInterrupt enables forwarding of the corresponding interrupt to the CPU
   153  // and configures its group status (Secure: Group 0, Non-Secure: Group 1).
   154  func (hw *GIC) EnableInterrupt(id int, secure bool) {
   155  	irq(hw.gicd, id, secure, true)
   156  }
   157  
   158  // DisableInterrupt disables forwarding of the corresponding interrupt to the
   159  // CPU.
   160  func (hw *GIC) DisableInterrupt(id int) {
   161  	irq(hw.gicd, id, false, false)
   162  }
   163  
   164  // GetInterrupt obtains and acknowledges a signaled interrupt, the end of its
   165  // handling must be signaled by closing the returned channel.
   166  func (hw *GIC) GetInterrupt(secure bool) (id int, end chan struct{}) {
   167  	if hw.gicc == 0 {
   168  		return
   169  	}
   170  
   171  	var m uint32
   172  
   173  	if secure {
   174  		m = reg.Get(hw.gicc+GICC_IAR, GICC_IAR_ID, 0x3ff)
   175  	} else {
   176  		m = reg.Get(hw.gicc+GICC_AIAR, GICC_AIAR_ID, 0x3ff)
   177  	}
   178  
   179  	if m < 1020 {
   180  		end = make(chan struct{})
   181  
   182  		go func() {
   183  			<-end
   184  
   185  			if secure {
   186  				reg.SetN(hw.gicc+GICC_EOIR, GICC_EOIR_ID, 0x3ff, m)
   187  			} else {
   188  				reg.SetN(hw.gicc+GICC_AEOIR, GICC_AEOIR_ID, 0x3ff, m)
   189  			}
   190  		}()
   191  	}
   192  
   193  	id = int(m)
   194  
   195  	return
   196  }