diff --git a/README.md b/README.md index 24eb4749..0915a179 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,9 @@ Current release level [![GitHub release](https://img.shields.io/github/v/release/usbarmory/tamago-go)](https://github.com/usbarmory/tamago-go/tree/latest) [![Build Status](https://github.com/usbarmory/tamago-go/workflows/Build%20Go%20compiler/badge.svg)](https://github.com/usbarmory/tamago-go/actions) The current release for the [TamaGo modified Go distribution](https://github.com/usbarmory/tamago-go) is -[tamago1.23.4](https://github.com/usbarmory/tamago-go/tree/tamago1.23.4), -which [adds](https://github.com/golang/go/compare/go1.23.4...usbarmory:tamago1.23.4) -`GOOS=tamago` support to go1.23.4. +[tamago1.23.5](https://github.com/usbarmory/tamago-go/tree/tamago1.23.5), +which [adds](https://github.com/golang/go/compare/go1.23.5...usbarmory:tamago1.23.5) +`GOOS=tamago` support to go1.23.5. Binary releases for amd64 and armv7l Linux hosts [are available](https://github.com/usbarmory/tamago-go/releases/latest). @@ -90,10 +90,9 @@ The following table summarizes currently supported RISC-V SoCs and boards Supported AMD64 targets ======================= -The support for an actual target is work-in-progress, the -[testing environment](https://github.com/usbarmory/tamago-go/blob/tamago1.23.4/src/testing/testing_tamago.go) -is supported and all Go standard library packages are supported and -[tested using original distribution tests](https://github.com/usbarmory/tamago/wiki/Compatibility). +| CPU | Board | CPU package | Board package | +|------------------|----------------------------------------------------------------------|----------------------------------------------------------------|------------------------------------------------------------------------------------| +| AMD/Intel 64-bit | [microvm](https://www.qemu.org/docs/master/system/i386/microvm.html) | [amd64](https://github.com/usbarmory/tamago/tree/master/amd64) | [qemu/microvm](https://github.com/usbarmory/tamago/tree/master/board/qemu/microvm) | Userspace targets ================= @@ -125,7 +124,7 @@ Compiling ========= Go applications are simply required to import, the relevant board package to -ensure that hardware initialization and runtime support takes place: +ensure that hardware initialization and runtime support take place: ```golang import ( @@ -154,6 +153,9 @@ GOOS=tamago GOARM=7 GOARCH=arm ${TAMAGO} build -ldflags "-T 0x80010000 -R 0x1000 # Example for QEMU RISC-V sifive_u GOOS=tamago GOARCH=riscv64 ${TAMAGO} build -ldflags "-T 0x80010000 -R 0x1000" main.go +# Example for QEMU AMD64 sifive_u +GOOS=tamago GOARCH=amd64 ${TAMAGO} build -ldflags "-T 0x10010000 -R 0x1000" main.go + # Example for Linux userspace GOOS=tamago ${TAMAGO} build main.go ``` diff --git a/amd64/amd64.go b/amd64/amd64.go new file mode 100644 index 00000000..2b452a61 --- /dev/null +++ b/amd64/amd64.go @@ -0,0 +1,60 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +// Package amd64 provides support for AMD64 architecture specific operations. +// +// The following architectures/cores are supported/tested: +// - AMD64 (single-core) +// +// This package is only meant to be used with `GOOS=tamago GOARCH=amd64` as +// supported by the TamaGo framework for bare metal Go, see +// https://github.com/usbarmory/tamago. +package amd64 + +import ( + "runtime" + _ "unsafe" +) + +//go:linkname ramStackOffset runtime.ramStackOffset +var ramStackOffset uint64 = 0x100000 // 1 MB + +// CPU instance +type CPU struct { + // features + invariant bool + kvm bool + kvmclock uint32 + + // Timer multiplier + TimerMultiplier float64 + // Timer offset in nanoseconds + TimerOffset int64 + // Timer function + TimerFn func() uint64 +} + +// defined in amd64.s +func halt(int32) + +// Fault generates a triple fault. +func Fault() + +// Init performs initialization of an AMD64 core instance. +func (cpu *CPU) Init() { + runtime.Exit = halt + + cpu.initFeatures() + cpu.initTimers() +} + +// Name returns the CPU identifier. +func (cpu *CPU) Name() string { + return runtime.CPU() +} diff --git a/amd64/amd64.s b/amd64/amd64.s new file mode 100644 index 00000000..ac6ae473 --- /dev/null +++ b/amd64/amd64.s @@ -0,0 +1,22 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +// func Fault() +TEXT ·Fault(SB),$0 + CLI + XORL AX, AX + LGDT (AX) + HLT + +// func halt(int32) +TEXT ·halt(SB),$0-8 + CLI +halt: + HLT + JMP halt diff --git a/amd64/features.go b/amd64/features.go new file mode 100644 index 00000000..a89624cd --- /dev/null +++ b/amd64/features.go @@ -0,0 +1,92 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +package amd64 + +import ( + "github.com/usbarmory/tamago/bits" +) + +// CPUID function numbers +// +// (Intel® Architecture Instruction Set Extensions +// and Future Features Programming Reference +// 1.5 CPUID INSTRUCTION). +const ( + CPUID_TSC_CCC = 0x15 + + CPUID_APM = 0x80000007 + APM_TSC_INVARIANT = 8 +) + +// KVM CPUID function numbers +// +// (https://docs.kernel.org/virt/kvm/x86/cpuid.html) +const ( + KVM_CPUID_SIGNATURE = 0x40000000 + KVM_SIGNATURE = 0x4b4d564b // "KVMK" + + KVM_CPUID_FEATURES = 0x40000001 + FEATURES_CLOCKSOURCE = 0 + FEATURES_CLOCKSOURCE2 = 3 + + KVM_CPUID_TSC_KHZ = 0x40000010 +) + +// kvmclock MSRs +const ( + MSR_KVM_SYSTEM_TIME = 0x12 + MSR_KVM_SYSTEM_TIME_NEW = 0x4b564d01 +) + +// defined in features.s +func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) + +func (cpu *CPU) initFeatures() { + _, _, _, apmFeatures := cpuid(CPUID_APM, 0x00) + cpu.invariant = bits.IsSet(&apmFeatures, APM_TSC_INVARIANT) + + _, kvmk, _, _ := cpuid(KVM_CPUID_SIGNATURE, 0x00) + cpu.kvm = kvmk == KVM_SIGNATURE + + if !cpu.kvm { + return + } + + kvmFeatures, _, _, _ := cpuid(KVM_CPUID_FEATURES, 0x00) + + if bits.IsSet(&kvmFeatures, FEATURES_CLOCKSOURCE) { + cpu.kvmclock = 0x12 + } + + if bits.IsSet(&kvmFeatures, FEATURES_CLOCKSOURCE2) { + cpu.kvmclock = 0x4b564d01 + } +} + +// Features represents the processor capabilities detected through the CPUID +// instruction. +type Features struct { + // InvariantTSC indicates whether the Time Stamp Counter is guaranteed + // to be at constant rate. + InvariantTSC bool + // KVM indicates whether a Kernel-base Virtual Machine is detected. + KVM bool + // KVMClockMSR returns the kvmclock Model Specific Register. + KVMClockMSR uint32 +} + +// Features returns the processor capabilities. +func (cpu *CPU) Features() *Features { + return &Features{ + InvariantTSC: cpu.invariant, + KVM: cpu.kvm, + KVMClockMSR: cpu.kvmclock, + } +} diff --git a/amd64/features.s b/amd64/features.s new file mode 100644 index 00000000..f57b6740 --- /dev/null +++ b/amd64/features.s @@ -0,0 +1,21 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +#include "textflag.h" + +// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32) +TEXT ·cpuid(SB),NOSPLIT,$0-24 + MOVL eaxArg+0(FP), AX + MOVL ecxArg+4(FP), CX + CPUID + MOVL AX, eax+8(FP) + MOVL BX, ebx+12(FP) + MOVL CX, ecx+16(FP) + MOVL DX, edx+20(FP) + RET diff --git a/amd64/init.s b/amd64/init.s new file mode 100644 index 00000000..ba3e314d --- /dev/null +++ b/amd64/init.s @@ -0,0 +1,113 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +#include "textflag.h" + +#define MSR_EFER 0xc0000080 + +#define PML4T 0x1000 // Page Map Level 4 Table (512GB entries) +#define PDPT 0x2000 // Page Directory Pointer Table (1GB entries) +#define PDT 0x3000 // Page Directory Table (2MB entries) +#define PT 0x4000 // Page Table (4kB entries) + +// Global Descriptor Table +DATA gdt<>+0x00(SB)/8, $0x0000000000000000 // null descriptor +DATA gdt<>+0x08(SB)/8, $0x00209a0000000000 // code descriptor (x/r) +DATA gdt<>+0x10(SB)/8, $0x0000920000000000 // data descriptor (r/w) +GLOBL gdt<>(SB),8,$24 + +DATA gdtptr<>+0x00(SB)/2, $0x1800 // GDT Limit +DATA gdtptr<>+0x02(SB)/8, $gdt<>(SB) // GDT Base Address +GLOBL gdtptr<>(SB),8,$10 + +TEXT cpuinit(SB),NOSPLIT|NOFRAME,$0 + // Disable interrupts + CLI + + // Set up paging + // + // AMD64 Architecture Programmer’s Manual + // Volume 2 - 5.3 Long-Mode Page Translation + // + // Intel® 64 and IA-32 Architectures Software Developer’s Manual + // Volume 3A - 4.5 IA-32E PAGING + + // Clear tables + XORL AX, AX // value + MOVL $PML4T, DI // to + MOVL $0x5000, CX // n + MOVL DI, CR3 + REP; STOSB + + // PML4T[0] = PDPT + MOVL $PML4T, DI + MOVL $(PDPT | 1<<1 | 1<<0), (DI) // set R/W, P + + // Configure Long-Mode Page Translation as follows: + // 0x00000000 - 0x3ffffffff (1GB) cacheable physical page + // 0x40000000 - 0x7ffffffff (1GB) cacheable physical page + // 0x80000000 - 0xbffffffff (1GB) cacheable physical page + // 0xc0000000 - 0xfffffffff (1GB) uncacheable physical page + MOVL $PDPT, DI + MOVQ $(0 << 30 | 1<<7 | 1<<1 | 1<<0), (DI) // set PS, R/W, P + ADDL $8, DI + MOVQ $(1 << 30 | 1<<7 | 1<<1 | 1<<0), (DI) // set PS, R/W, P + ADDL $8, DI + MOVQ $(2 << 30 | 1<<7 | 1<<1 | 1<<0), (DI) // set PS, R/W, P + ADDL $8, DI + MOVQ $(3 << 30 | 1<<7 | 1<<4 | 1<<1 | 1<<0), (DI) // set PS, PCD, R/W, P + + // Enter long mode + + MOVL $(1<<7 | 1<<5), AX // set CR4.(PGE|PAE) + MOVL AX, CR4 + + MOVL $MSR_EFER, CX + RDMSR + ORL $(1<<8), AX // set MSR_EFER.LME + WRMSR + + MOVL CR0, AX + ORL $(1<<31 | 1<<0), AX // set CR0.(PG|PE) + MOVL AX, CR0 + + // Set Global Descriptor Table + + CALL ·getPC<>(SB) + MOVL $gdtptr<>(SB), BX // 32-bit mode: only PC offset is copied + ADDL $6, AX + ADDL BX, AX + LGDT (AX) + + CALL ·getPC<>(SB) + MOVL $·start<>(SB), BX // 32-bit mode: only PC offset is copied + ADDL $6, AX + ADDL BX, AX + + PUSHQ $0x08 + PUSHQ AX + RETFQ + +TEXT ·start<>(SB),NOSPLIT|NOFRAME,$0 + MOVL CR0, AX + MOVL CR4, BX + + // Enable SSE + ANDL $~(1<<2), AX // clear CR0.EM + ORL $(1<<1), AX // set CR0.MP + ORL $(1<<10 | 1<<9), BX // set CR4.(OSXMMEXCPT|OSFXSR) + + MOVL AX, CR0 + MOVL BX, CR4 + + JMP _rt0_tamago_start(SB) + +TEXT ·getPC<>(SB),NOSPLIT|NOFRAME,$0 + POPQ AX + CALL AX diff --git a/amd64/mem.go b/amd64/mem.go new file mode 100644 index 00000000..fd1783b5 --- /dev/null +++ b/amd64/mem.go @@ -0,0 +1,19 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +//go:build !linkramstart + +package amd64 + +import ( + _ "unsafe" +) + +//go:linkname ramStart runtime.ramStart +var ramStart uint64 = 0x10000000 diff --git a/amd64/rng.go b/amd64/rng.go new file mode 100644 index 00000000..418a8497 --- /dev/null +++ b/amd64/rng.go @@ -0,0 +1,34 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +package amd64 + +import ( + _ "unsafe" + + "github.com/usbarmory/tamago/internal/rng" +) + +// defined in rng.s +func rdrand() uint32 + +// GetRandomData returns len(b) random bytes gathered from the RDRAND instruction. +func GetRandomData(b []byte) { + read := 0 + need := len(b) + + for read < need { + read = rng.Fill(b, read, rdrand()) + } +} + +//go:linkname initRNG runtime.initRNG +func initRNG() { + rng.GetRandomDataFn = GetRandomData +} diff --git a/amd64/rng.s b/amd64/rng.s new file mode 100644 index 00000000..a8bebbbe --- /dev/null +++ b/amd64/rng.s @@ -0,0 +1,17 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +// func rdrand() uint32 +TEXT ·rdrand(SB),$0-4 + // rdrand eax + BYTE $0x0f + BYTE $0xc7 + BYTE $0xf0 + MOVL AX, ret+0(FP) + RET diff --git a/amd64/timer.go b/amd64/timer.go new file mode 100644 index 00000000..09c9abc8 --- /dev/null +++ b/amd64/timer.go @@ -0,0 +1,48 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +package amd64 + +const ( + // nanoseconds + refFreq uint32 = 1e9 +) + +// defined in timer.s +func read_tsc() uint64 + +func (cpu *CPU) initTimers() { + var timerFreq uint32 + + if denominator, numerator, nominalFreq, _ := cpuid(CPUID_TSC_CCC, 0x00); nominalFreq != 0 { + timerFreq = (numerator * nominalFreq) / denominator + } + + if cpu.kvm { + if khz, _, _, _ := cpuid(KVM_CPUID_TSC_KHZ, 0x00); khz != 0 { + timerFreq = khz * 1000 + } + } + + if timerFreq == 0 { + panic("TSC frequency is unavailable") + } + + cpu.TimerMultiplier = float64(refFreq) / float64(timerFreq) + cpu.TimerFn = read_tsc +} + +// SetTimer sets the timer to the argument nanoseconds value. +func (cpu *CPU) SetTimer(t int64) { + if cpu.TimerFn == nil || cpu.TimerMultiplier == 0 { + return + } + + cpu.TimerOffset = t - int64(float64(cpu.TimerFn())*cpu.TimerMultiplier) +} diff --git a/amd64/timer.s b/amd64/timer.s new file mode 100644 index 00000000..ba01afb3 --- /dev/null +++ b/amd64/timer.s @@ -0,0 +1,15 @@ +// x86-64 processor support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +// func read_tsc() uint64 +TEXT ·read_tsc(SB),$0-8 + RDTSC + MOVL AX, ret+0(FP) + MOVL DX, ret+4(FP) + RET diff --git a/arm/arm.go b/arm/arm.go index 22e46f9c..a9584f57 100644 --- a/arm/arm.go +++ b/arm/arm.go @@ -13,7 +13,7 @@ // - ARMv7-A / Cortex-A7 (single-core) // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package arm @@ -41,18 +41,18 @@ type CPU struct { jazelle bool thumbee bool - // extensions + // features programmersModel bool security bool mProfileModel bool virtualization bool genericTimer bool - // timer multiplier + // Timer multiplier TimerMultiplier int64 - // timer offset in nanoseconds + // Timer offset in nanoseconds TimerOffset int64 - // timer function + // Timer function TimerFn func() int64 // GIC Distributor base address diff --git a/arm/gic/gic.go b/arm/gic/gic.go index 0db26e3e..9e2c6eb9 100644 --- a/arm/gic/gic.go +++ b/arm/gic/gic.go @@ -15,7 +15,7 @@ // - ARM IHI 0048B.b - ARM Generic Interrupt Controller - Architecture version 2.0 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package gic diff --git a/arm/timer.go b/arm/timer.go index 7cdebc36..337e357c 100644 --- a/arm/timer.go +++ b/arm/timer.go @@ -35,10 +35,10 @@ const ( CNTKCTL_PL0PCTEN = 0 // nanoseconds - refFreq int64 = 1000000000 + refFreq int64 = 1e9 ) -// defined in timer_arm.s +// defined in timer.s func read_gtc() int64 func read_cntfrq() int32 func write_cntfrq(freq int32) @@ -91,7 +91,7 @@ func (cpu *CPU) SetTimer(t int64) { return } - cpu.TimerOffset = t - int64(cpu.TimerFn()*cpu.TimerMultiplier) + cpu.TimerOffset = t - cpu.TimerFn()*cpu.TimerMultiplier } // SetDownCounter sets a physical countdown timer. diff --git a/arm/tzc380/tzc380.go b/arm/tzc380/tzc380.go index 8e5d65e2..a017c026 100644 --- a/arm/tzc380/tzc380.go +++ b/arm/tzc380/tzc380.go @@ -19,7 +19,7 @@ // - TZC-380 TRM - CoreLink™ TrustZone Address Space Controller TZC-380 - Revision: r0p1 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package tzc380 diff --git a/bits/bits32.go b/bits/bits32.go index 6077c422..98e01dc9 100644 --- a/bits/bits32.go +++ b/bits/bits32.go @@ -10,6 +10,11 @@ // registers. package bits +// IsSet returns whether a specific bit position is set at the pointed value. +func IsSet(addr *uint32, pos int) bool { + return (int(*addr) >> pos) & 1 == 1 +} + // Get returns the pointed value at a specific bit position and with a bitmask // applied. func Get(addr *uint32, pos int, mask int) uint32 { diff --git a/bits/bits64.go b/bits/bits64.go index dc2739f4..7e4b3737 100644 --- a/bits/bits64.go +++ b/bits/bits64.go @@ -8,6 +8,11 @@ package bits +// IsSet64 returns whether a specific bit position is set at the pointed value. +func IsSet64(addr *uint64, pos int) bool { + return (int(*addr) >> pos) & 1 == 1 +} + // Get64 returns the pointed value at a specific bit position and with a // bitmask applied. func Get64(addr *uint64, pos int, mask int) uint64 { diff --git a/board/nxp/mx6ullevk/README.md b/board/nxp/mx6ullevk/README.md index 3710b38b..6b55d69e 100644 --- a/board/nxp/mx6ullevk/README.md +++ b/board/nxp/mx6ullevk/README.md @@ -21,7 +21,7 @@ Introduction ============ TamaGo is a framework that enables compilation and execution of unencumbered Go -applications on bare metal ARM/RISC-V System-on-Chip (SoC) components. +applications on bare metal AMD64/ARM/RISC-V processors. The [mx6ullevk](https://github.com/usbarmory/tamago/tree/master/board/nxp/mx6ullevk) package provides support for the [MCIMX6ULL-EVK](https://www.nxp.com/design/development-boards/i-mx-evaluation-and-development-boards/evaluation-kit-for-the-i-mx-6ull-and-6ulz-applications-processor:MCIMX6ULL-EVK) development board. @@ -50,7 +50,7 @@ Compiling ========= Go applications are simply required to import, the relevant board package to -ensure that hardware initialization and runtime support takes place: +ensure that hardware initialization and runtime support take place: ```golang import ( @@ -164,7 +164,7 @@ The target can be executed under emulation as follows: ``` qemu-system-arm \ -machine mcimx6ul-evk -cpu cortex-a7 -m 512M \ - -nographic -monitor none -serial null -serial stdio -net none \ + -nographic -monitor none -serial null -serial stdio \ -kernel example -semihosting ``` diff --git a/board/nxp/mx6ullevk/console.go b/board/nxp/mx6ullevk/console.go index 58fb316a..8649a4de 100644 --- a/board/nxp/mx6ullevk/console.go +++ b/board/nxp/mx6ullevk/console.go @@ -8,7 +8,6 @@ // that can be found in the LICENSE file. //go:build !linkprintk -// +build !linkprintk package mx6ullevk diff --git a/board/nxp/mx6ullevk/mem.go b/board/nxp/mx6ullevk/mem.go index b7fcca69..4974664c 100644 --- a/board/nxp/mx6ullevk/mem.go +++ b/board/nxp/mx6ullevk/mem.go @@ -8,7 +8,6 @@ // that can be found in the LICENSE file. //go:build !linkramsize -// +build !linkramsize package mx6ullevk @@ -19,9 +18,9 @@ import ( // Applications can override ramSize with the `linkramsize` build tag. // // This is useful when large DMA descriptors are required to re-initialize -// tamago `mem` package in external RAM. +// tamago `dma` package in external RAM. // The MCIMX6ULL-EVK features a single 512MB DDR3 RAM module. //go:linkname ramSize runtime.ramSize -var ramSize uint32 = 0x20000000 // 512 MB +var ramSize uint32 = 0x20000000 // 512MB diff --git a/board/nxp/mx6ullevk/mx6ullevk.go b/board/nxp/mx6ullevk/mx6ullevk.go index 65e4b563..07a3c932 100644 --- a/board/nxp/mx6ullevk/mx6ullevk.go +++ b/board/nxp/mx6ullevk/mx6ullevk.go @@ -11,7 +11,7 @@ // for the NXP MCIMX6ULL-EVK evaluation board. // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package mx6ullevk @@ -40,8 +40,8 @@ var ( USDHC2 = imx6ul.USDHC2 ) -// Init takes care of the lower level SoC initialization triggered early in -// runtime setup. +// Init takes care of the lower level initialization triggered early in runtime +// setup. // //go:linkname Init runtime.hwinit func Init() { diff --git a/board/qemu/microvm/README.md b/board/qemu/microvm/README.md new file mode 100644 index 00000000..fa1fe6ca --- /dev/null +++ b/board/qemu/microvm/README.md @@ -0,0 +1,130 @@ +TamaGo - bare metal Go for AMD64 CPUs - microVM support +======================================================= + +tamago | https://github.com/usbarmory/tamago + +Copyright (c) WithSecure Corporation +https://foundry.withsecure.com + +![TamaGo gopher](https://github.com/usbarmory/tamago/wiki/images/tamago.svg?sanitize=true) + +Authors +======= + +Andrea Barisani +andrea.barisani@withsecure.com | andrea@inversepath.com + +Andrej Rosano +andrej.rosano@withsecure.com | andrej@inversepath.com + +Introduction +============ + +TamaGo is a framework that enables compilation and execution of unencumbered Go +applications on bare metal AMD64/ARM/RISC-V processors. + +The [microvm](https://github.com/usbarmory/tamago/tree/master/board/qemu/microvm) +package provides support for the [microvm](https://www.qemu.org/docs/master/system/i386/microvm.html) +paravirtualized Kernel-based Virtual Machine (KVM) configured with a single +AMD64 core. + +Documentation +============= + +For more information about TamaGo see its +[repository](https://github.com/usbarmory/tamago) and +[project wiki](https://github.com/usbarmory/tamago/wiki). + +For the underlying driver support for this board see package +[amd64](https://github.com/usbarmory/tamago/tree/master/amd64) and +[microvm](https://github.com/usbarmory/tamago/tree/master/board/qemu/microvm). + +The package API documentation can be found on +[pkg.go.dev](https://pkg.go.dev/github.com/usbarmory/tamago). + +Supported hardware +================== + +| CPU | Board | CPU package | Board package | +|------------------|----------------------------------------------------------------------|----------------------------------------------------------------|------------------------------------------------------------------------------------| +| AMD/Intel 64-bit | [microvm](https://www.qemu.org/docs/master/system/i386/microvm.html) | [amd64](https://github.com/usbarmory/tamago/tree/master/amd64) | [qemu/microvm](https://github.com/usbarmory/tamago/tree/master/board/qemu/microvm) | + +Compiling +========= + +Go applications are simply required to import, the relevant board package to +ensure that hardware initialization and runtime support take place: + +```golang +import ( + _ "github.com/usbarmory/tamago/board/qemu/microvm" +) +``` + +Build the [TamaGo compiler](https://github.com/usbarmory/tamago-go) +(or use the [latest binary release](https://github.com/usbarmory/tamago-go/releases/latest)): + +``` +wget https://github.com/usbarmory/tamago-go/archive/refs/tags/latest.zip +unzip latest.zip +cd tamago-go-latest/src && ./all.bash +cd ../bin && export TAMAGO=`pwd`/go +``` + +Go applications can be compiled as usual, using the compiler built in the +previous step, but with the addition of the following flags/variables and +ensuring that the required SoC and board packages are available in `GOPATH`: + +``` +GOOS=tamago GOARCH=amd64 ${TAMAGO} build -ldflags "-T 0x10010000 -R 0x1000" main.go +``` + +An example application, targeting the QEMU microvm platform, +is [available](https://github.com/usbarmory/tamago-example). + +Executing and debugging +======================= + +The [example application](https://github.com/usbarmory/tamago-example) provides +reference usage and a Makefile target for automatic creation of an ELF image as +well as paravirtualized execution. + +QEMU +---- + +``` +qemu-system-x86_64 \ + -machine microvm,x-option-roms=on,pit=off,pic=off,rtc=on \ + -global virtio-mmio.force-legacy=false \ + -enable-kvm -cpu host,invtsc=on,kvmclock=on -no-reboot \ + -m 4G -nographic -monitor none -serial stdio \ + -kernel example +``` + +The paravirtualized target can be debugged with GDB by adding the `-S -s` flags +to the previous execution command, this will make qemu waiting for a GDB +connection that can be launched as follows: + +``` +gdb -ex "target remote 127.0.0.1:1234" example +``` + +Breakpoints can be set in the usual way: + +``` +b ecdsa.Verify +continue +``` + +License +======= + +tamago | https://github.com/usbarmory/tamago +Copyright (c) WithSecure Corporation + +These source files are distributed under the BSD-style license found in the +[LICENSE](https://github.com/usbarmory/tamago/blob/master/LICENSE) file. + +The TamaGo logo is adapted from the Go gopher designed by Renee French and +licensed under the Creative Commons 3.0 Attributions license. Go Gopher vector +illustration by Hugo Arganda. diff --git a/board/qemu/microvm/console.go b/board/qemu/microvm/console.go new file mode 100644 index 00000000..eb2c7e1c --- /dev/null +++ b/board/qemu/microvm/console.go @@ -0,0 +1,21 @@ +// microvm support for tamago/amd64 +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +//go:build !linkprintk + +package microvm + +import ( + _ "unsafe" +) + +//go:linkname printk runtime.printk +func printk(c byte) { + UART0.Tx(c) +} diff --git a/board/qemu/microvm/mem.go b/board/qemu/microvm/mem.go new file mode 100644 index 00000000..e4575c7a --- /dev/null +++ b/board/qemu/microvm/mem.go @@ -0,0 +1,24 @@ +// microvm support for tamago/amd64 +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +//go:build !linkramsize + +package microvm + +import ( + _ "unsafe" +) + +// Applications can override ramSize with the `linkramsize` build tag. +// +// This is useful when large DMA descriptors are required to re-initialize +// tamago `dma` package in external RAM. + +//go:linkname ramSize runtime.ramSize +var ramSize uint64 = 0x40000000 // 1GB diff --git a/board/qemu/microvm/microvm.go b/board/qemu/microvm/microvm.go new file mode 100644 index 00000000..4b1b967f --- /dev/null +++ b/board/qemu/microvm/microvm.go @@ -0,0 +1,83 @@ +// microvm support for tamago/amd64 +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +// Package microvm provides hardware initialization, automatically on import, +// for the QEMU microvm machine configured with a single x86_64 core. +// +// This package is only meant to be used with `GOOS=tamago GOARCH=amd64` as +// supported by the TamaGo framework for bare metal Go, see +// https://github.com/usbarmory/tamago. +package microvm + +import ( + "runtime" + _ "unsafe" + + "github.com/usbarmory/tamago/amd64" + "github.com/usbarmory/tamago/dma" + "github.com/usbarmory/tamago/kvm/clock" +) + +const ( + dmaStart = 0x50000000 + dmaSize = 0x10000000 // 256MB +) + +// Peripheral registers +const ( + COM1 = 0x3f8 + + VIRTIO_MMIO_BASE = 0xfeb00000 + VIRTIO_NET_BASE = VIRTIO_MMIO_BASE + 0x2e00 +) + +// Peripheral instances +var ( + // AMD64 core + AMD64 = &amd64.CPU{} + + // Real-Time Clock + RTC0 = &RTC{} + + // Serial port + UART0 = &UART{ + Index: 1, + Base: COM1, + } +) + +//go:linkname nanotime1 runtime.nanotime1 +func nanotime1() int64 { + return int64(float64(AMD64.TimerFn())*AMD64.TimerMultiplier) + AMD64.TimerOffset +} + +func init() { + dma.Init(dmaStart, dmaSize) + + // initialize KVM clock as needed + kvmclock.Init(AMD64) +} + +// Init takes care of the lower level initialization triggered early in runtime +// setup. +// +//go:linkname Init runtime.hwinit +func Init() { + // initialize CPU + AMD64.Init() + + // initialize serial console + UART0.Init() + + runtime.Exit = func(_ int32) { + // On microvm the recommended way to trigger a guest-initiated + // shut down is by generating a triple-fault. + amd64.Fault() + } +} diff --git a/board/qemu/microvm/rtc.go b/board/qemu/microvm/rtc.go new file mode 100644 index 00000000..5ac7b7e7 --- /dev/null +++ b/board/qemu/microvm/rtc.go @@ -0,0 +1,80 @@ +// MC146818A RTC driver +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +package microvm + +import ( + "errors" + "time" + + "github.com/usbarmory/tamago/internal/reg" +) + +// RTC registers +// +// (IBM PC AT Technical Reference - March 1984) +const ( + CMOS_RTC_OUT = 0x70 + CMOS_RTC_IN = 0x71 + + SECONDS = 0x00 + MINUTES = 0x02 + HOURS = 0x04 + DOW = 0x07 + MONTH = 0x08 + YEAR = 0x09 + CENTURY = 0x32 + + STATUSA = 0x0a + STATUSA_UIP = 7 +) + +// RTC represents a Real-Time Clock instance. +type RTC struct { + // Time zone + Location *time.Location +} + +func (rtc *RTC) read(addr int) int { + reg.Out8(CMOS_RTC_OUT, uint8(addr)) + return int(reg.In8(CMOS_RTC_IN)) +} + +func bcdToBin(val int) int { + return (val & 0x0f) + ((val / 16) * 10) +} + +// Now() returns the real-time clock information. +func (rtc *RTC) Now() (t time.Time, err error) { + if rtc.Location == nil { + if rtc.Location, err = time.LoadLocation(""); err != nil { + return + } + } + + if a := rtc.read(STATUSA); (a>>STATUSA_UIP)&1 == 1 { + err = errors.New("update in progress") + return + } + + // We assume that the RTC remains in its initialized state with Data + // Mode set to BCD and 24-hour mode. + + ss := bcdToBin(rtc.read(SECONDS)) + mm := bcdToBin(rtc.read(MINUTES)) + dd := bcdToBin(rtc.read(DOW)) + MM := bcdToBin(rtc.read(MONTH)) + yy := bcdToBin(rtc.read(YEAR)) + cc := bcdToBin(rtc.read(CENTURY)) + + hh := rtc.read(HOURS) + hh = ((hh & 0x0f) + (((hh & 0x70) / 16) * 10)) | (hh & 0x80) + + return time.Date(cc*100+yy, time.Month(MM), dd, hh, mm, ss, 0, rtc.Location), nil +} diff --git a/board/qemu/microvm/uart.go b/board/qemu/microvm/uart.go new file mode 100644 index 00000000..2d271451 --- /dev/null +++ b/board/qemu/microvm/uart.go @@ -0,0 +1,86 @@ +// 16550A UART driver +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +package microvm + +import ( + "github.com/usbarmory/tamago/internal/reg" +) + +// UART registers +const ( + DEFAULT_BAUDRATE = 115200 + + RBR = 0x00 + THR = 0x00 + IER = 0x01 + FCR = 0x02 + MCR = 0x04 + + LSR = 0x05 + LSR_DR = 0 + LSR_THRE = 5 +) + +// UART represents a serial port instance. +type UART struct { + // Controller index + Index int + // Base register + Base uint16 +} + +// Init initializes and enables the UART. +func (hw *UART) Init() { + if hw.Base == 0 { + panic("invalid UART controller instance") + } +} + +// Tx transmits a single character to the serial port. +func (hw *UART) Tx(c byte) { + for reg.In8(hw.Base+LSR)&(1<>= -timeInfo.Shift + } else { + delta <<= timeInfo.Shift + } + + d := big.NewInt(int64(delta)) + m := big.NewInt(int64(timeInfo.Multiplier)) + r := big.NewInt(0) + + r.Mul(d, m) + r.Rsh(r, 32) + + return int64(r.Uint64() + timeInfo.SystemTime) +} + +func kvmClockSync(cpu *amd64.CPU) { + version := uint32(0) + timeInfo := &pvClockTimeInfo{} + + for { + time.Sleep(TimeInfoUpdate) + + binary.Decode(timeInfoBuffer, binary.LittleEndian, timeInfo) + + if timeInfo.Version == version || timeInfo.Version%2 == 1 { + continue + } + + version = timeInfo.Version + cpu.SetTimer(kvmClock(cpu, timeInfo)) + } +} + +func Init(cpu *amd64.CPU) { + features := cpu.Features() + + switch { + case features.InvariantTSC && !features.KVM: + // no action required as TSC is reliable + case features.InvariantTSC && features.KVM && features.KVMClockMSR > 0: + // no action required as TSC is reliable but we + // opportunistically adjust once with kvmclock + initTimeInfo(features.KVMClockMSR) + cpu.SetTimer(kvmClock(cpu, nil)) + case features.KVM && features.KVMClockMSR > 0: + // TSC must be adjusted as it is not reliable through state + // changes. + // + // As nanotime1() cannot malloc we cannot override it, rather + // we adjust asynchronously with kvmclock every TimeInfoUpdate + // interval. + // + // If ever required kvmClockSync() can be moved to Go assembly. + initTimeInfo(features.KVMClockMSR) + go kvmClockSync(cpu) + default: + panic("could not set system timer") + } +} diff --git a/kvm/clock/kvm_clock.s b/kvm/clock/kvm_clock.s new file mode 100644 index 00000000..3b0ba430 --- /dev/null +++ b/kvm/clock/kvm_clock.s @@ -0,0 +1,26 @@ +// KVM clock driver +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +#include "go_asm.h" + +// holder for struct pvclock_vcpu_time_info +DATA pvclock<>+0x00(SB)/8, $0x0000000000000000 +DATA pvclock<>+0x08(SB)/8, $0x0000000000000000 +DATA pvclock<>+0x10(SB)/8, $0x0000000000000000 +GLOBL pvclock<>(SB),8,$32 + +// func pvclock(msr uint32) uint32 +TEXT ·pvclock(SB),$8 + MOVL msr+0(FP), CX + MOVL $pvclock<>(SB), AX + MOVL $0, DX + MOVL AX, ret+8(FP) + ORL $1, AX + WRMSR + RET diff --git a/kvm/virtio/descriptor.go b/kvm/virtio/descriptor.go new file mode 100644 index 00000000..f1eda97b --- /dev/null +++ b/kvm/virtio/descriptor.go @@ -0,0 +1,346 @@ +// VirtIO Virtual Queue support +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +package virtio + +import ( + "bytes" + "encoding/binary" + "fmt" + "sync" + + "github.com/usbarmory/tamago/dma" +) + +// Descriptor Flags +const ( + Next = 1 + Write = 2 + Indirect = 3 +) + +// Descriptor represents a VirtIO virtual queue descriptor. +// +// All exported fields are used one-time at initialization, fields requiring +// DMA are accessible through functions. +type Descriptor struct { + Address uint64 + length uint32 + Flags uint16 + Next uint16 + + // DMA buffer + buf []byte +} + +// Bytes converts the descriptor structure to byte array format. +func (d *Descriptor) Bytes() []byte { + buf := new(bytes.Buffer) + + binary.Write(buf, binary.LittleEndian, d.Address) + binary.Write(buf, binary.LittleEndian, d.length) + binary.Write(buf, binary.LittleEndian, d.Flags) + binary.Write(buf, binary.LittleEndian, d.Next) + + return buf.Bytes() +} + +// SetLength updates the descriptor length field. +func (d *Descriptor) SetLength(length uint32) { + off := 8 + binary.LittleEndian.PutUint32(d.buf[off:], length) + + d.length = length +} + +// Init initializes a virtual queue descriptor the given buffer length. +func (d *Descriptor) Init(length int, flags uint16) { + addr, buf := dma.Reserve(length, 0) + + d.Address = uint64(addr) + d.length = uint32(length) + d.Flags = flags + + d.buf = buf +} + +// Destroy removes a virtual queue descriptor from physical memory. +func (d *Descriptor) Destroy() { + dma.Release(uint(d.Address)) +} + +// Available represents a VirtIO virtual queue Available ring buffer. +// +// All exported fields are used one-time at initialization, fields requiring +// DMA are accessible through functions. +type Available struct { + Flags uint16 + index uint16 + ring []uint16 + EventIndex uint16 + + // DMA buffer + buf []byte +} + +// Bytes converts the descriptor structure to byte array format. +func (d *Available) Bytes() []byte { + buf := new(bytes.Buffer) + + binary.Write(buf, binary.LittleEndian, d.Flags) + binary.Write(buf, binary.LittleEndian, d.index) + + for _, ring := range d.ring { + binary.Write(buf, binary.LittleEndian, ring) + } + + binary.Write(buf, binary.LittleEndian, d.EventIndex) + + return buf.Bytes() +} + +// SetIndex updates the descriptor index field. +func (d *Available) SetIndex(index uint16) { + off := 2 + binary.LittleEndian.PutUint16(d.buf[off:], index) + + d.index = index +} + +// Ring returns a ring buffer at the given position. +func (d *Available) Ring(n uint16) uint16 { + off := 4 + n*2 + d.ring[n] = binary.LittleEndian.Uint16(d.buf[off:]) + + return d.ring[n] +} + +// SetRingIndex updates the index value of a ring buffer. +func (d *Available) SetRingIndex(n uint16, index uint16) { + off := 4 + n*2 + binary.LittleEndian.PutUint16(d.buf[off:], index) + + d.ring[n] = index +} + +// Ring represents a VirtIO virtual queue buffer index +type Ring struct { + Index uint32 + Length uint32 +} + +// Bytes converts the descriptor structure to byte array format. +func (d *Ring) Bytes() []byte { + buf := new(bytes.Buffer) + binary.Write(buf, binary.LittleEndian, d) + return buf.Bytes() +} + +// Used represents a VirtIO virtual queue Used ring buffer. +// +// All exported fields are used one-time at initialization, fields requiring +// DMA are accessible through functions. +type Used struct { + Flags uint16 + index uint16 + ring []*Ring + AvailEvent uint16 + + // DMA buffer + buf []byte + + last uint16 +} + +// Bytes converts the descriptor structure to byte array format. +func (d *Used) Bytes() []byte { + buf := new(bytes.Buffer) + + binary.Write(buf, binary.LittleEndian, d.Flags) + binary.Write(buf, binary.LittleEndian, d.index) + + for _, ring := range d.ring { + buf.Write(ring.Bytes()) + } + + binary.Write(buf, binary.LittleEndian, d.AvailEvent) + + return buf.Bytes() +} + +// Index returns the descriptor index field. +func (d *Used) Index() uint16 { + off := 2 + d.index = binary.LittleEndian.Uint16(d.buf[off:]) + + return d.index +} + +// Ring returns a ring buffer at the given position. +func (d *Used) Ring(n uint16) Ring { + off := 4 + n*8 + binary.Decode(d.buf[off:], binary.LittleEndian, d.ring[n]) + + return *d.ring[n] +} + +// VirtualQueue represents a VirtIO split virtual queue Descriptor +type VirtualQueue struct { + sync.Mutex + + Descriptors []*Descriptor + Available Available + Used Used + + // DMA buffer + buf []byte + desc uint // physical address for QueueDesc + driver uint // phusical address for QueueDriver + device uint // physical address for QueueDevice + + size uint16 +} + +// Bytes converts the descriptor structure to byte array format, the device +// area and driver area location offsets are also returned. +func (d *VirtualQueue) Bytes() ([]byte, int, int) { + buf := new(bytes.Buffer) + + for _, desc := range d.Descriptors { + buf.Write(desc.Bytes()) + } + + driver := buf.Len() + buf.Write(d.Available.Bytes()) + + device := buf.Len() + buf.Write(d.Used.Bytes()) + + return buf.Bytes(), driver, device +} + +// Init initializes a split virtual queue for the given size. +func (d *VirtualQueue) Init(size int, length int, flags uint16) { + d.Lock() + defer d.Unlock() + + for i := 0; i < size; i++ { + desc := &Descriptor{} + desc.Init(length, flags) + + ring := &Ring{} + + d.Descriptors = append(d.Descriptors, desc) + d.Available.ring = append(d.Available.ring, uint16(i)) + d.Used.ring = append(d.Used.ring, ring) + } + + if flags == Write { + // make all buffers immediately available + d.Available.index = uint16(size) + } + + // allocate DMA buffer + buf, driver, device := d.Bytes() + d.desc, d.buf = dma.Reserve(len(buf), 16) + copy(d.buf, buf) + + // calculate area pointers + d.driver = d.desc + uint(driver) + d.device = d.desc + uint(device) + d.size = uint16(size) + + // assign DMA slices + d.Available.buf = d.buf[driver:device] + d.Used.buf = d.buf[device:] +} + +// Destroy removes a split virtual queue from physical memory. +func (d *VirtualQueue) Destroy() { + for _, d := range d.Descriptors { + d.Destroy() + } + + d.Available.buf = nil + d.Used.buf = nil + + dma.Release(d.desc) +} + +// Address returns the virtual queue physical address. +func (d *VirtualQueue) Address() (desc uint, driver uint, device uint) { + return d.desc, d.driver, d.device +} + +// Pop receives a single used buffer from the virtual queue, +func (d *VirtualQueue) Pop() (buf []byte) { + d.Lock() + defer d.Unlock() + + if d.Used.Index() == d.Used.last { + return + } + + avail := d.Used.Ring(d.Used.last % d.size) + + buf = make([]byte, avail.Length) + copy(buf, d.Descriptors[avail.Index].buf) + + d.Available.index += 1 + d.Available.SetRingIndex(d.Available.index%d.size, uint16(avail.Index)) + + d.Available.SetIndex(d.Available.index) + d.Used.last += 1 + + return +} + +// Push supplies a single available buffer to the virtual queue. +func (d *VirtualQueue) Push(buf []byte) { + d.Lock() + defer d.Unlock() + + length := len(buf) + index := d.Available.Ring(d.Available.index % d.size) + + d.Descriptors[index].SetLength(uint32(length)) + copy(d.Descriptors[index].buf, buf) + + d.Available.SetIndex(d.Available.index + 1) + + for used := d.Used.Index() - d.Used.last; used > 0; used-- { + index = used - 1 + avail := d.Used.Ring(used) + + d.Available.SetRingIndex(d.Available.index%d.size, uint16(avail.Index)) + d.Used.last += 1 + } + + return +} + +func (d *VirtualQueue) Debug() { + fmt.Printf("\n%+v\n", d) + + for _, desc := range d.Descriptors { + fmt.Printf("%x\n", desc) + } + + for _, ring := range d.Used.ring { + fmt.Printf("%x\n", ring) + } + + descSize := len((&Descriptor{}).Bytes()) * len(d.Descriptors) + availSize := len(d.Available.Bytes()) + + driver := uint(descSize) + device := driver + uint(availSize) + + fmt.Printf("%x", d.buf[device:]) +} diff --git a/kvm/virtio/virtio.go b/kvm/virtio/virtio.go new file mode 100644 index 00000000..89f3e1d9 --- /dev/null +++ b/kvm/virtio/virtio.go @@ -0,0 +1,239 @@ +// VirtIO driver +// https://github.com/usbarmory/tamago +// +// Copyright (c) WithSecure Corporation +// https://foundry.withsecure.com +// +// Use of this source code is governed by the license +// that can be found in the LICENSE file. + +// Package virtio implements a driver for Virtual I/O devices (VirtIO) +// following reference specifications: +// - Virtual I/O Device (VIRTIO) - Version 1.2 +// +// This package is only meant to be used with `GOOS=tamago` as +// supported by the TamaGo framework for bare metal Go, see +// https://github.com/usbarmory/tamago. +package virtio + +import ( + "errors" + + "github.com/usbarmory/tamago/bits" + "github.com/usbarmory/tamago/dma" + "github.com/usbarmory/tamago/internal/reg" +) + +// VirtIO MMIO Device Registers +const ( + Magic = 0x000 + Version = 0x004 + DeviceID = 0x008 + VendorID = 0x00c + DeviceFeatures = 0x010 + DeviceFeaturesSel = 0x014 + DriverFeatures = 0x020 + DriverFeaturesSel = 0x024 + QueueSel = 0x030 + QueueNumMax = 0x034 + QueueNum = 0x038 + QueueReady = 0x044 + QueueNotify = 0x050 + InterruptStatus = 0x060 + InterruptACK = 0x064 + Status = 0x070 + QueueDesc = 0x080 + QueueDriver = 0x090 + QueueDevice = 0x0a0 + ConfigGeneration = 0x0fc + Config = 0x100 +) + +// Reserved Feature bits +const ( + Packed = 34 + NotificationData = 38 +) + +// Device Status bits +const ( + Acknowledge = 0 + Driver = 1 + DriverOk = 2 + FeaturesOk = 3 + DeviceneedsReset = 6 + Failed = 7 +) + +const ( + MAGIC = 0x74726976 // "virt" + VERSION = 0x02 + + // bits 0 to 23, and 50 to 63 + deviceSpecificFeatureMask = 0xfffc000000ffffff + // bits 24 to 49 + deviceReservedFeatureMask = 0x0003ffffff000000 +) + +// VirtIO represents a VirtIO device. +type VirtIO struct { + // MMIO base address + Base uint32 + // ConfigSize is the device configuration size + ConfigSize int + // Config is a reserved DMA buffer for device configuration access and + // modification. + Config []byte + + features uint64 +} + +func (io *VirtIO) negotiate(features uint64) (err error) { + // get offered features + io.features = io.DeviceFeatures() + + // clear unsupported features + bits.Clear64(&io.features, Packed) + bits.Clear64(&io.features, NotificationData) + + // keep all remaining reserved features, clear device type ones + io.features &= deviceReservedFeatureMask + + // apply device type features from the driver + io.features &= features + + // negotiate features + io.SetDriverFeatures(io.features) + reg.Set(io.Base+Status, FeaturesOk) + + if !reg.IsSet(io.Base+Status, FeaturesOk) { + return errors.New("could not set features") + } + + return +} + +// Init initializes a VirtIO over MMIO device instance. +func (io *VirtIO) Init(features uint64) (err error) { + if io.Base == 0 || reg.Read(io.Base+Magic) != MAGIC { + return errors.New("invalid VirtIO instance") + } + + if reg.Read(io.Base+Version) != VERSION { + return errors.New("unsupported VirtIO interface") + } + + // reset + reg.Write(io.Base+Status, 0x0) + + // initialize driver + reg.Set(io.Base+Status, Driver|Acknowledge) + + if err = io.negotiate(features); err != nil { + return + } + + // finalize driver + reg.Set(io.Base+Status, DriverOk) + + // initialize Config DMA buffers + r, err := dma.NewRegion(uint(io.Base+Config), io.ConfigSize, false) + + if err != nil { + return + } + + _, io.Config = r.Reserve(io.ConfigSize, 0) + + return +} + +// DeviceID returns the VirtIO subsystem device ID +func (io *VirtIO) DeviceID() uint32 { + return reg.Read(io.Base + DeviceID) +} + +// DeviceFeatures returns the device feature bits. +func (io *VirtIO) DeviceFeatures() (features uint64) { + for i := uint32(0); i <= 1; i++ { + reg.Write(io.Base+DeviceFeaturesSel, i) + features |= uint64(reg.Read(io.Base+DeviceFeatures)) << (i * 32) + } + + return +} + +// DriverFeatures returns the driver feature bits. +func (io *VirtIO) DriverFeatures() (features uint64) { + for i := uint32(0); i <= 1; i++ { + reg.Write(io.Base+DriverFeaturesSel, i) + features |= uint64(reg.Read(io.Base+DriverFeatures)) << (i * 32) + } + + return +} + +// SetDriverFeatures sets the driver feature bits. +func (io *VirtIO) SetDriverFeatures(features uint64) { + for i := uint32(0); i <= 1; i++ { + reg.Write(io.Base+DriverFeaturesSel, i) + reg.Write(io.Base+DriverFeatures, uint32(features>>(i*32))) + } + + return +} + +// QueueReady returns whether a queue is ready for use. +func (io *VirtIO) QueueReady(index int) (ready bool) { + reg.Write(io.Base+QueueSel, uint32(index)) + ready = reg.Read(io.Base+QueueReady) != 0 + return +} + +// MaxQueueSize returns the maximum virtual queue size. +func (io *VirtIO) MaxQueueSize(index int) int { + reg.Write(io.Base+QueueSel, uint32(index)) + return int(reg.Read(io.Base + QueueNumMax)) +} + +// SetQueueSize sets the virtual queue size. +func (io *VirtIO) SetQueueSize(index int, n int) { + reg.Write(io.Base+QueueSel, uint32(index)) + reg.Write(io.Base+QueueNum, uint32(n)) +} + +// InterruptStatus returns the interrupt status and reason. +func (io *VirtIO) InterruptStatus() (buffer bool, config bool) { + s := reg.Read(io.Base + InterruptStatus) + + buffer = bits.IsSet(&s, 0) + config = bits.IsSet(&s, 1) + + return +} + +// Status returns the device status. +func (io *VirtIO) Status() uint32 { + return reg.Read(io.Base + Status) +} + +// SetQueue registers the indexed virtual queue for device access. +func (io *VirtIO) SetQueue(index int, queue *VirtualQueue) { + desc, driver, device := queue.Address() + + reg.Write(io.Base+QueueSel, uint32(index)) + reg.Write(io.Base+QueueDesc, uint32(desc)) + reg.Write(io.Base+QueueDriver, uint32(driver)) + reg.Write(io.Base+QueueDevice, uint32(device)) + reg.Write(io.Base+QueueReady, 1) +} + +// QueueNotify notifies the device that a queue can be processed. +func (io *VirtIO) QueueNotify(index int) { + reg.Write(io.Base+QueueNotify, uint32(index)) +} + +// ConfigVersion returns the device configuration (see Config field) version. +func (io *VirtIO) ConfigVersion() uint32 { + return reg.Read(io.Base + ConfigGeneration) +} diff --git a/riscv/csr.h b/riscv64/csr.h similarity index 100% rename from riscv/csr.h rename to riscv64/csr.h diff --git a/riscv/exception.go b/riscv64/exception.go similarity index 99% rename from riscv/exception.go rename to riscv64/exception.go index fbbb6a0b..9aafb1ea 100644 --- a/riscv/exception.go +++ b/riscv64/exception.go @@ -7,7 +7,7 @@ // Use of this source code is governed by the license // that can be found in the LICENSE file. -package riscv +package riscv64 import ( "unsafe" diff --git a/riscv/exception.s b/riscv64/exception.s similarity index 100% rename from riscv/exception.s rename to riscv64/exception.s diff --git a/riscv/init.s b/riscv64/init.s similarity index 100% rename from riscv/init.s rename to riscv64/init.s diff --git a/riscv/pmp.go b/riscv64/pmp.go similarity index 99% rename from riscv/pmp.go rename to riscv64/pmp.go index 30f4b7c2..082275f0 100644 --- a/riscv/pmp.go +++ b/riscv64/pmp.go @@ -7,7 +7,7 @@ // Use of this source code is governed by the license // that can be found in the LICENSE file. -package riscv +package riscv64 import ( "errors" diff --git a/riscv/pmp.s b/riscv64/pmp.s similarity index 100% rename from riscv/pmp.s rename to riscv64/pmp.s diff --git a/riscv/riscv.go b/riscv64/riscv64.go similarity index 83% rename from riscv/riscv.go rename to riscv64/riscv64.go index 5dc3bce2..df536b8b 100644 --- a/riscv/riscv.go +++ b/riscv64/riscv64.go @@ -7,15 +7,16 @@ // Use of this source code is governed by the license // that can be found in the LICENSE file. -// Package riscv provides support for RISC-V architecture specific operations. +// Package riscv64 provides support for RISC-V architecture specific +// operations. // // The following architectures/cores are supported/tested: // - RV64 (single-core) // // This package is only meant to be used with `GOOS=tamago GOARCH=riscv64` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. -package riscv +package riscv64 import "runtime" @@ -25,7 +26,7 @@ const XLEN = 64 // CPU instance type CPU struct{} -// defined in riscv.s +// defined in riscv64.s func halt(int32) // Init performs initialization of an RV64 core instance in machine mode. diff --git a/riscv/riscv.s b/riscv64/riscv64.s similarity index 100% rename from riscv/riscv.s rename to riscv64/riscv64.s diff --git a/soc/bcm2835/README.md b/soc/bcm2835/README.md index fcaf0dfe..523b002b 100644 --- a/soc/bcm2835/README.md +++ b/soc/bcm2835/README.md @@ -14,7 +14,7 @@ Introduction ============ TamaGo is a framework that enables compilation and execution of unencumbered Go -applications on bare metal ARM/RISC-V System-on-Chip (SoC) components. +applications on bare metal AMD64/ARM/RISC-V processors. The [bcm2835](https://github.com/usbarmory/tamago/tree/master/soc/bcm2835) package provides support for the Broadcom BCM2835 series of SoC. diff --git a/soc/bcm2835/bcm2835.go b/soc/bcm2835/bcm2835.go index f0ffbc33..f276940c 100644 --- a/soc/bcm2835/bcm2835.go +++ b/soc/bcm2835/bcm2835.go @@ -10,7 +10,7 @@ // the TamaGo framework on BCM2835/BCM2836 SoCs. // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package bcm2835 @@ -21,7 +21,7 @@ import ( ) // nanos - should be same value as arm/timer.go refFreq -const refFreq int64 = 1000000000 +const refFreq int64 = 1e9 // DRAM_FLAG_NOCACHE disables caching by setting to high bits const DRAM_FLAG_NOCACHE = 0xC0000000 @@ -42,8 +42,8 @@ func nanotime1() int64 { return read_systimer()*ARM.TimerMultiplier + ARM.TimerOffset } -// Init takes care of the lower level SoC initialization triggered early in -// runtime setup (e.g. runtime.hwinit). +// Init takes care of the lower level initialization triggered early in runtime +// setup (e.g. runtime.hwinit). func Init(base uint32) { peripheralBase = base diff --git a/soc/bcm2835/mem.go b/soc/bcm2835/mem.go index da384a68..2b4120ff 100644 --- a/soc/bcm2835/mem.go +++ b/soc/bcm2835/mem.go @@ -7,7 +7,6 @@ // that can be found in the LICENSE file. //go:build !linkramstart -// +build !linkramstart package bcm2835 diff --git a/soc/nxp/bee/bee.go b/soc/nxp/bee/bee.go index 0458de8e..d8a8ed61 100644 --- a/soc/nxp/bee/bee.go +++ b/soc/nxp/bee/bee.go @@ -12,7 +12,7 @@ // - IMX6ULSRM - i.MX6UL Security Reference Manual - Rev 0 04/2016 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package bee diff --git a/soc/nxp/caam/caam.go b/soc/nxp/caam/caam.go index 45abca8a..62fa4032 100644 --- a/soc/nxp/caam/caam.go +++ b/soc/nxp/caam/caam.go @@ -13,7 +13,7 @@ // - IMX7DSSRM - i.MX7DS Security Reference Manual - Rev 0 03/2017 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package caam diff --git a/soc/nxp/csu/csu.go b/soc/nxp/csu/csu.go index 937f9b03..95002a47 100644 --- a/soc/nxp/csu/csu.go +++ b/soc/nxp/csu/csu.go @@ -13,7 +13,7 @@ // - IMX6ULLSRM - i.MX 6ULL Applications Processor Security Reference Manual - Rev 0 2016/09 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package csu diff --git a/soc/nxp/dcp/dcp.go b/soc/nxp/dcp/dcp.go index 4ff9cdc3..f67cbcd5 100644 --- a/soc/nxp/dcp/dcp.go +++ b/soc/nxp/dcp/dcp.go @@ -12,7 +12,7 @@ // - MCIMX28RM - i.MX28 Applications Processor Reference Manual - Rev 2 2013/08 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package dcp diff --git a/soc/nxp/enet/enet.go b/soc/nxp/enet/enet.go index f0afa2ea..22749036 100644 --- a/soc/nxp/enet/enet.go +++ b/soc/nxp/enet/enet.go @@ -12,7 +12,7 @@ // - IMX6ULLRM - i.MX 6ULL Applications Processor Reference Manual - Rev 1 2017/11 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package enet @@ -190,7 +190,7 @@ func (hw *ENET) Init() { hw.MAC[0] &= 0xfe hw.MAC[0] |= 0x02 } else if len(hw.MAC) != 6 { - panic("invalid ENET hardware address") + panic("invalid MAC") } if hw.RingSize == 0 { @@ -291,6 +291,8 @@ func (hw *ENET) SetMAC(mac net.HardwareAddr) { // function waits and handles received packets (see Rx()) through RxHandler() // (when set), it should never return. func (hw *ENET) Start(rx bool) { + var buf []byte + // set receive and transmit descriptors reg.Write(hw.rdsr, hw.rx.init(true, hw.RingSize, &hw.Stats)) reg.Write(hw.tdsr, hw.tx.init(false, hw.RingSize, &hw.Stats)) @@ -301,8 +303,6 @@ func (hw *ENET) Start(rx bool) { return } - var buf []byte - for { runtime.Gosched() diff --git a/soc/nxp/gpio/gpio.go b/soc/nxp/gpio/gpio.go index 7c636c71..4fa81934 100644 --- a/soc/nxp/gpio/gpio.go +++ b/soc/nxp/gpio/gpio.go @@ -10,7 +10,7 @@ // Package gpio implements helpers for GPIO configuration on NXP SoCs. // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package gpio diff --git a/soc/nxp/i2c/i2c.go b/soc/nxp/i2c/i2c.go index 6b0ab77f..7ab81814 100644 --- a/soc/nxp/i2c/i2c.go +++ b/soc/nxp/i2c/i2c.go @@ -13,7 +13,7 @@ // - IMX6FG - i.MX 6 Series Firmware Guide - Rev 0 2012/11 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package i2c diff --git a/soc/nxp/imx6ul/README.md b/soc/nxp/imx6ul/README.md index 2ac3f98b..f6789730 100644 --- a/soc/nxp/imx6ul/README.md +++ b/soc/nxp/imx6ul/README.md @@ -21,7 +21,7 @@ Introduction ============ TamaGo is a framework that enables compilation and execution of unencumbered Go -applications on bare metal ARM/RISC-V System-on-Chip (SoC) components. +applications on bare metal AMD64/ARM/RISC-V processors. The [imx6ul](https://github.com/usbarmory/tamago/tree/master/soc/nxp/imx6ul) package provides support for the NXP i.MX 6UL series of System-on-Chip (SoCs) diff --git a/soc/nxp/imx6ul/imx6ul.go b/soc/nxp/imx6ul/imx6ul.go index 284ebe97..7077e26f 100644 --- a/soc/nxp/imx6ul/imx6ul.go +++ b/soc/nxp/imx6ul/imx6ul.go @@ -22,7 +22,7 @@ // - IMX6ULZRM - i.MX 6ULZ Applications Processor Reference Manual - Rev 0 2018/10 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package imx6ul diff --git a/soc/nxp/imx6ul/init.go b/soc/nxp/imx6ul/init.go index 6b7fb79e..c230544d 100644 --- a/soc/nxp/imx6ul/init.go +++ b/soc/nxp/imx6ul/init.go @@ -45,8 +45,8 @@ var ( SDP bool ) -// Init takes care of the lower level SoC initialization triggered early in -// runtime setup (e.g. runtime.hwinit). +// Init takes care of the lower level initialization triggered early in runtime +// setup (e.g. runtime.hwinit). func Init() { if ARM.Mode() != arm.SYS_MODE { // initialization required only when in PL1 diff --git a/soc/nxp/imx6ul/mem.go b/soc/nxp/imx6ul/mem.go index fd51012d..2e9c7143 100644 --- a/soc/nxp/imx6ul/mem.go +++ b/soc/nxp/imx6ul/mem.go @@ -8,7 +8,6 @@ // that can be found in the LICENSE file. //go:build !linkramstart -// +build !linkramstart package imx6ul diff --git a/soc/nxp/imx6ul/timer.go b/soc/nxp/imx6ul/timer.go index d31b3a8a..3605c04e 100644 --- a/soc/nxp/imx6ul/timer.go +++ b/soc/nxp/imx6ul/timer.go @@ -33,5 +33,5 @@ func initTimers() { //go:linkname nanotime1 runtime.nanotime1 func nanotime1() int64 { - return int64(ARM.TimerFn()*ARM.TimerMultiplier + ARM.TimerOffset) + return ARM.TimerFn()*ARM.TimerMultiplier + ARM.TimerOffset } diff --git a/soc/nxp/iomuxc/iomuxc.go b/soc/nxp/iomuxc/iomuxc.go index a3e0b55e..6e266c20 100644 --- a/soc/nxp/iomuxc/iomuxc.go +++ b/soc/nxp/iomuxc/iomuxc.go @@ -10,7 +10,7 @@ // Package iomuxc implements helpers for IOMUX configuration on NXP SoCs. // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package iomuxc diff --git a/soc/nxp/ocotp/ocotp.go b/soc/nxp/ocotp/ocotp.go index 9773fe83..19a2f8da 100644 --- a/soc/nxp/ocotp/ocotp.go +++ b/soc/nxp/ocotp/ocotp.go @@ -20,7 +20,7 @@ // The use of this package is therefore **at your own risk**. // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package ocotp diff --git a/soc/nxp/rngb/rngb.go b/soc/nxp/rngb/rngb.go index d1603605..1df82adb 100644 --- a/soc/nxp/rngb/rngb.go +++ b/soc/nxp/rngb/rngb.go @@ -12,7 +12,7 @@ // - IMX6ULLRM - i.MX 6ULL Applications Processor Reference Manual - Rev 1 2017/11 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package rngb diff --git a/soc/nxp/snvs/snvs.go b/soc/nxp/snvs/snvs.go index f2195fb8..66fdc487 100644 --- a/soc/nxp/snvs/snvs.go +++ b/soc/nxp/snvs/snvs.go @@ -13,7 +13,7 @@ // - IMX6ULLSRM - i.MX 6ULL Applications Processor Security Reference Manual - Rev 0 2016/09 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package snvs diff --git a/soc/nxp/tempmon/tempmon.go b/soc/nxp/tempmon/tempmon.go index 127e0249..93214617 100644 --- a/soc/nxp/tempmon/tempmon.go +++ b/soc/nxp/tempmon/tempmon.go @@ -12,7 +12,7 @@ // - IMX6ULLRM - i.MX 6ULL Applications Processor Reference Manual - Rev 1 2017/11 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package tempmon diff --git a/soc/nxp/uart/uart.go b/soc/nxp/uart/uart.go index 73cce88b..d99ea2c4 100644 --- a/soc/nxp/uart/uart.go +++ b/soc/nxp/uart/uart.go @@ -12,7 +12,7 @@ // - IMX6ULLRM - i.MX 6ULL Applications Processor Reference Manual - Rev 1 2017/11 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package uart @@ -178,14 +178,6 @@ func (hw *UART) Init() { hw.setup() } -func (hw *UART) txFull() bool { - return reg.Get(hw.uts, UTS_TXFULL, 1) == 1 -} - -func (hw *UART) rxReady() bool { - return reg.Get(hw.usr2, USR2_RDR, 1) == 1 -} - func (hw *UART) setup() { // disable UART reg.Write(hw.ucr1, 0) @@ -289,15 +281,16 @@ func (hw *UART) Disable() { // Tx transmits a single character to the serial port. func (hw *UART) Tx(c byte) { - for hw.txFull() { + for reg.Get(hw.uts, UTS_TXFULL, 1) == 1 { // wait for TX FIFO to have room for a character } + reg.Write(hw.utxd, uint32(c)) } // Rx receives a single character from the serial port. func (hw *UART) Rx() (c byte, valid bool) { - if !hw.rxReady() { + if reg.Get(hw.usr2, USR2_RDR, 1) != 1 { return } diff --git a/soc/nxp/usb/bus.go b/soc/nxp/usb/bus.go index 725560c6..9148d5df 100644 --- a/soc/nxp/usb/bus.go +++ b/soc/nxp/usb/bus.go @@ -14,7 +14,7 @@ // - USB2.0 - USB Specification Revision 2.0 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package usb diff --git a/soc/nxp/usdhc/usdhc.go b/soc/nxp/usdhc/usdhc.go index b53a638a..76096080 100644 --- a/soc/nxp/usdhc/usdhc.go +++ b/soc/nxp/usdhc/usdhc.go @@ -50,7 +50,7 @@ // - SD DDR50: 45MB/s - 45MHz (instead of 50MB/s - 50MHz), unsupported // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package usdhc diff --git a/soc/nxp/wdog/wdog.go b/soc/nxp/wdog/wdog.go index 84c48a21..f621dffd 100644 --- a/soc/nxp/wdog/wdog.go +++ b/soc/nxp/wdog/wdog.go @@ -12,7 +12,7 @@ // - IMX6ULLRM - i.MX 6ULL Applications Processor Reference Manual - Rev 1 2017/11 // // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as -// supported by the TamaGo framework for bare metal Go on ARM SoCs, see +// supported by the TamaGo framework for bare metal Go, see // https://github.com/usbarmory/tamago. package wdog diff --git a/soc/sifive/fu540/README.md b/soc/sifive/fu540/README.md index 28d8d5e9..7905b20c 100644 --- a/soc/sifive/fu540/README.md +++ b/soc/sifive/fu540/README.md @@ -21,7 +21,7 @@ Introduction ============ TamaGo is a framework that enables compilation and execution of unencumbered Go -applications on bare metal ARM/RISC-V System-on-Chip (SoC) components. +applications on bare metal AMD64/ARM/RISC-V processors. The [fu540](https://github.com/usbarmory/tamago/tree/master/soc/sifive/fu540) package provides support for the SiFive FU540 System-on-Chip (SoCs). diff --git a/soc/sifive/fu540/fu540.go b/soc/sifive/fu540/fu540.go index 59d5a535..fb20f909 100644 --- a/soc/sifive/fu540/fu540.go +++ b/soc/sifive/fu540/fu540.go @@ -23,7 +23,7 @@ package fu540 import ( _ "unsafe" - "github.com/usbarmory/tamago/riscv" + "github.com/usbarmory/tamago/riscv64" "github.com/usbarmory/tamago/soc/sifive/clint" "github.com/usbarmory/tamago/soc/sifive/uart" ) @@ -41,7 +41,7 @@ const ( // Peripheral instances var ( // RISC-V core - RV64 = &riscv.CPU{} + RV64 = &riscv64.CPU{} // Core-Local Interruptor CLINT = &clint.CLINT{ diff --git a/soc/sifive/fu540/init.go b/soc/sifive/fu540/init.go index f2765d5b..b626f6e7 100644 --- a/soc/sifive/fu540/init.go +++ b/soc/sifive/fu540/init.go @@ -16,8 +16,8 @@ import ( //go:linkname ramStackOffset runtime.ramStackOffset var ramStackOffset uint64 = 0x100 -// Init takes care of the lower level SoC initialization triggered early in -// runtime setup (e.g. runtime.hwinit). +// Init takes care of the lower level initialization triggered early in runtime +// setup (e.g. runtime.hwinit). func Init() { RV64.Init() } diff --git a/soc/sifive/fu540/mem.go b/soc/sifive/fu540/mem.go index 03a8366f..48677edf 100644 --- a/soc/sifive/fu540/mem.go +++ b/soc/sifive/fu540/mem.go @@ -8,7 +8,6 @@ // that can be found in the LICENSE file. //go:build !linkramstart -// +build !linkramstart package fu540 diff --git a/soc/sifive/uart/uart.go b/soc/sifive/uart/uart.go index 27a712e8..31614fbe 100644 --- a/soc/sifive/uart/uart.go +++ b/soc/sifive/uart/uart.go @@ -66,15 +66,12 @@ func (hw *UART) Init() { hw.rxctrl = hw.Base + UARTx_RXCTRL } -func (hw *UART) txFull() bool { - return reg.Get(hw.txdata, TXDATA_FULL, 1) == 1 -} - // Tx transmits a single character to the serial port. func (hw *UART) Tx(c byte) { - for hw.txFull() { + for reg.Get(hw.txdata, TXDATA_FULL, 1) == 1 { // wait for TX FIFO to have room for a character } + reg.Write(hw.txdata, uint32(c)) }