-
Notifications
You must be signed in to change notification settings - Fork 60
Internals
-
Go applications built with TamaGo run on bare metal, without any underlying OS. All required support is provided by the Go runtime and driver packages, also written in Go.
-
When hardware initialization is provided (like in the imx6ul package), the compiled binaries can be directly executed by the processor, without any need for a 3rd party bootloader. This completely eliminates any non-Go dependency.
-
Due to its design TamaGo also allows user space execution.
The GOOS=tamago
target maintains full language and standard library support,
see Compatibility.
Board support packages or applications are required to define specific functions to support the runtime, additional functions/variables can be optionally set for more fine grained control over the runtime context.
The API is described in package tamago/doc.
- TamaGo modifications to the original Go distribution are minimal to ease maintainability and potential upstream acceptance.
TamaGo support is implemented with a clean separation from other OSes, all
modifications are conditional to compilation option GOOS=tamago
and do not
affect other architectures.
In addition to the runtime API, the following changes are highlighted:
-
The memory allocation is identical to plan9 with the exception of the brk call which is not required.
-
The only supported, and required, syscall is write which allows only stdout and stderr file descriptors.
-
The crypto/rand support is delegated to the supporting SoC driver, for example with the imx6ul packages support for the RNGB True Random Number Generator or amd64 use of RDRAND.
-
The file system support is identical to NaCl and provides a volatile in-memory emulated disk, the file descriptor support is therefore also identical to NaCl to support it.
-
Network I/O through Go own net package requires the application to set an external Socket function, see imx-enet or virtio-net for examples.
-
The testing environment for AMD64, ARM and RISCV64 architectures runs under Linux natively or using qemu-user-static via binfmt_misc.
+----------------------------------+ 0x00000000 (zero page start)
| |
| INACCESSIBLE |
| |
+----------------------------------+ 0x00009000
| |
| PAGE TABLES (16 kB) |
| |
+----------------------------------+ 0x0000d000
| |
| INACCESSIBLE |
| |
+----------------------------------+ 0x00200000 (zero page end)
| |
| UNUSED |
| |
+----------------------------------+ runtime.ramStart + 0x10000 (64 kB)
| |
| .text |
| |
| .noptrdata |
| Go application |
| .data |
| |
| .bss |
| |
| .noptrbss |
| |
+----------------------------------+
| |
| HEAP |
| |
+----------------------------------+ runtime.g0.stack.lo (runtime.go.stack.hi - 0x10000)
| |
| STACK |
| |
+----------------------------------+ runtime.go.stack.hi (runtime.ramStart + runtime.ramSize - runtime.ramStackOffset)
| |
| UNUSED |
| |
+----------------------------------+ runtime.ramStart + runtime.ramSize
| |
| UNUSED |
| |
+----------------------------------+ 0xc0000000
| |
| UNCACHEABLE |
| |
+----------------------------------+ 0x100000000
+----------------------------------+ 0x00000000 (zero page start)
| |
| INACCESSIBLE |
| |
+----------------------------------+ 0x00001000 (zero page end)
| |
| UNUSED |
| |
+----------------------------------+ runtime.ramStart (default for runtime.vecTableStart)
| |
| EXCEPTION VECTOR TABLE (16 kB) |
| |
+----------------------------------+ runtime.ramStart + 0x4000 (16 kB)
| |
| L1 PAGE TABLE (16 kB) |
| |
+----------------------------------+ runtime.ramStart + 0x8000 (32 kB)
| |
| EXCEPTION STACK (16 kB) |
| |
+----------------------------------+ runtime.ramStart + 0xC000 (48 kB)
| |
| L2 PAGE TABLE (16 kB) |
| |
+----------------------------------+ runtime.ramStart + 0x10000 (64 kB)
| |
| .text |
| |
| .noptrdata |
| Go application |
| .data |
| |
| .bss |
| |
| .noptrbss |
| |
+----------------------------------+
| |
| HEAP |
| |
+----------------------------------+ runtime.g0.stack.lo (runtime.go.stack.hi - 0x10000)
| |
| STACK |
| |
+----------------------------------+ runtime.go.stack.hi (runtime.ramStart + runtime.ramSize - runtime.ramStackOffset)
| |
| UNUSED |
| |
+----------------------------------+ runtime.ramStart + runtime.ramSize
| |
| |
+----------------------------------+ 0x100000000
The dma provides primitives for direct memory allocation.
Such DMA areas can be defined outside Go runtime, to prevent any GC operation on it and allow its use for transfers with hardware peripherals.
+----------------------------------+ mem.dmaStart
| |
| DMA BUFFERS |
| |
+----------------------------------+ mem.dmaStart + mem.dmaSize
As an example the imx6ul package uses the 128KB On-Chip RAM (also known as iRAM) for DMA transfers.
Board packages and applications are free re-initialize DMA buffers if more, or alternate, space is required.
The handling of interrupts is
achieved by GOOS=tamago
specific runtime.WakeG
which implements a pure Go
assembly procedure to wake a sleeping goroutine.
In combination with runtime.GetG
, which obtains the current goroutine
pointer, this allows to park an arbitrary goroutine and asynchronously wake it
from an execution context outside the Go runtime, such as an interrupt service
routine (ISR) vector.
This enables the following pattern to implement ISRs:
func ServiceInterrupts(isr func(id int)) {
irqHandlerG, _ = runtime.GetG()
// irqHandlerG gets registered to be woken up by runtime.WakeG with
// architecture specific code, see tamago/{amd64|arm}/irq.{go|s}
for {
// re-enable interrupts after we get parked
go irq_enable()
// sleep indefinitely until woken up by runtime.WakeG
time.Sleep(math.MaxInt64)
// handle interrupts
isr(id)
}
}
To reduce power consumption when the CPU is idle, tamago
amd64
and arm
packages use the optional runtime.Idle
function to halt the processor until
an interrupt is received when no runtime activity is scheduled in the future.
On AMD/Intel 64-bit architecture interrupts are supported with the following helpers:
-
Packages lapic and ioapic provides a driver for Intel Local (LAPIC) and I/O (IOAPIC) Advanced Programmable Interrupt Controllers.
-
Function ServiceInterrupts allows registration of a goroutine as IRQ handler.
The following projects provide an example use of such helpers:
- VirtIO networking interrupt handling in tamago-example.
On the ARM architecture interrupts are supported with the following helpers:
-
Package gic, provides a driver for the ARM Generic Interrupt Controller.
-
Function ServiceInterrupts, allows registration of a goroutine as IRQ handler.
-
Function SetAlarm, allows registration of a physical countdown timer to raise an interrupt at a specific time.
-
Peripheral drivers functions to enable specific controller IRQs as needed, such as NXP ENET Ethernet driver.
The following projects provide an example use of such helpers:
-
Ethernet and USB interrupt handling in tamago-example.
-
TrustZone Watchdog enabling, and servicing in GoTEE-example.
-
The tamago-example interrupt handler for ARM halts the processor at any given idle period.
- Cloud Hypervisor: cloud_hypervisor/vm
- QEMU microvm: qemu/microvm
- Firecracker microvm: firecracker/microvm
- UEFI: uefi/x64
- USB armory: usbarmory
- MCIMX6ULL-EVK: mx6ullevk
- Raspberry Pi: pi
- SiFive Unleashed: sifive_u