aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLucas Teske <[email protected]>2020-05-09 15:39:19 -0300
committerAyke <[email protected]>2020-07-31 00:58:09 +0200
commit3650c2c7397b5f21f61c1a4362b28596968c912f (patch)
tree7c331676822cb4109fb11967cea73e826f2bd138
parentd4e04e4e493aa4a99659361e28b22ecd51cceee3 (diff)
downloadtinygo-3650c2c7397b5f21f61c1a4362b28596968c912f.tar.gz
tinygo-3650c2c7397b5f21f61c1a4362b28596968c912f.zip
nintendoswitch: Add experimental Nintendo Switch support without CRT
Bare minimal nintendo switch support using LLD
-rw-r--r--Makefile2
-rw-r--r--compileopts/config.go10
-rw-r--r--compileopts/target.go5
-rw-r--r--compiler/compiler.go12
-rw-r--r--loader/goroot.go2
-rw-r--r--src/runtime/runtime_nintendoswitch.go73
-rw-r--r--src/runtime/runtime_nintendoswitch.s45
-rw-r--r--src/runtime/runtime_nintendoswitch_heap.go31
-rw-r--r--src/runtime/runtime_nintendoswitch_noheap.go7
-rw-r--r--src/runtime/runtime_nintendoswitch_svc.go22
-rw-r--r--src/runtime/runtime_unix.go1
-rw-r--r--src/runtime/runtime_unix_heap.go1
-rw-r--r--src/runtime/runtime_unix_noheap.go2
-rw-r--r--src/syscall/syscall_libc.go2
-rw-r--r--src/syscall/syscall_nintendoswitch.go48
-rw-r--r--src/syscall/tables_baremetal.go2
-rw-r--r--targets/nintendoswitch.json28
-rw-r--r--targets/nintendoswitch.ld61
-rw-r--r--targets/nintendoswitch.s51
19 files changed, 401 insertions, 4 deletions
diff --git a/Makefile b/Makefile
index e428030e4..4691d1213 100644
--- a/Makefile
+++ b/Makefile
@@ -342,6 +342,8 @@ endif
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=1 examples/blinky1
@$(MD5SUM) test.hex
+ $(TINYGO) build -o test.elf -target=nintendoswitch examples/serial
+ @$(MD5SUM) test.elf
wasmtest:
$(GO) test ./tests/wasm
diff --git a/compileopts/config.go b/compileopts/config.go
index 4c30d6b52..fdb26f01b 100644
--- a/compileopts/config.go
+++ b/compileopts/config.go
@@ -284,6 +284,16 @@ func (c *Config) CodeModel() string {
return "default"
}
+// RelocationModel returns the relocation model in use on this platform. Valid
+// values are "static", "pic", "dynamicnopic".
+func (c *Config) RelocationModel() string {
+ if c.Target.RelocationModel != "" {
+ return c.Target.RelocationModel
+ }
+
+ return "static"
+}
+
type TestConfig struct {
CompileTestBinary bool
// TODO: Filter the test functions to run, include verbose flag, etc
diff --git a/compileopts/target.go b/compileopts/target.go
index 5d5e2f1b3..256e70751 100644
--- a/compileopts/target.go
+++ b/compileopts/target.go
@@ -50,6 +50,7 @@ type TargetSpec struct {
OpenOCDTransport string `json:"openocd-transport"`
JLinkDevice string `json:"jlink-device"`
CodeModel string `json:"code-model"`
+ RelocationModel string `json:"relocation-model"`
}
// copyProperties copies all properties that are set in spec2 into itself.
@@ -134,6 +135,10 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
if spec2.CodeModel != "" {
spec.CodeModel = spec2.CodeModel
}
+
+ if spec2.RelocationModel != "" {
+ spec.RelocationModel = spec2.RelocationModel
+ }
}
// load reads a target specification from the JSON in the given io.Reader. It
diff --git a/compiler/compiler.go b/compiler/compiler.go
index 7b52cb66b..cd5b15eed 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -93,6 +93,7 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
features := strings.Join(config.Features(), ",")
var codeModel llvm.CodeModel
+ var relocationModel llvm.RelocMode
switch config.CodeModel() {
case "default":
@@ -109,7 +110,16 @@ func NewTargetMachine(config *compileopts.Config) (llvm.TargetMachine, error) {
codeModel = llvm.CodeModelLarge
}
- machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, llvm.RelocStatic, codeModel)
+ switch config.RelocationModel() {
+ case "static":
+ relocationModel = llvm.RelocStatic
+ case "pic":
+ relocationModel = llvm.RelocPIC
+ case "dynamicnopic":
+ relocationModel = llvm.RelocDynamicNoPic
+ }
+
+ machine := target.CreateTargetMachine(config.Triple(), config.CPU(), features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
return machine, nil
}
diff --git a/loader/goroot.go b/loader/goroot.go
index 7947d15e2..d9cf6ad7e 100644
--- a/loader/goroot.go
+++ b/loader/goroot.go
@@ -165,7 +165,7 @@ func mergeDirectory(goroot, tinygoroot, tmpgoroot, importPath string, overrides
// with the TinyGo version. This is the case on some targets.
func needsSyscallPackage(buildTags []string) bool {
for _, tag := range buildTags {
- if tag == "baremetal" || tag == "darwin" {
+ if tag == "baremetal" || tag == "darwin" || tag == "nintendoswitch" {
return true
}
}
diff --git a/src/runtime/runtime_nintendoswitch.go b/src/runtime/runtime_nintendoswitch.go
new file mode 100644
index 000000000..690f559e1
--- /dev/null
+++ b/src/runtime/runtime_nintendoswitch.go
@@ -0,0 +1,73 @@
+// +build nintendoswitch
+
+package runtime
+
+type timeUnit int64
+
+const asyncScheduler = false
+
+func postinit() {}
+
+// Entry point for Go. Initialize all packages and call main.main().
+//export main
+func main() int {
+ preinit()
+ run()
+
+ // Call exit to correctly finish the program
+ // Without this, the application crashes at start, not sure why
+ return exit(0)
+}
+
+// sleepTicks sleeps for the specified system ticks
+func sleepTicks(d timeUnit) {
+ sleepThread(uint64(ticksToNanoseconds(d)))
+}
+
+// armTicksToNs converts cpu ticks to nanoseconds
+// Nintendo Switch CPU ticks has a fixed rate at 19200000
+// It is basically 52 ns per tick
+// The formula 625 / 12 is equivalent to 1e9 / 19200000
+func ticksToNanoseconds(tick timeUnit) int64 {
+ return int64(tick * 625 / 12)
+}
+
+func nanosecondsToTicks(ns int64) timeUnit {
+ return timeUnit(12 * ns / 625)
+}
+
+func ticks() timeUnit {
+ return timeUnit(ticksToNanoseconds(timeUnit(getArmSystemTick())))
+}
+
+var stdoutBuffer = make([]byte, 0, 120)
+
+func putchar(c byte) {
+ if c == '\n' || len(stdoutBuffer)+1 >= 120 {
+ NxOutputString(string(stdoutBuffer))
+ stdoutBuffer = stdoutBuffer[:0]
+ return
+ }
+
+ stdoutBuffer = append(stdoutBuffer, c)
+}
+
+func usleep(usec uint) int {
+ sleepThread(uint64(usec) * 1000)
+ return 0
+}
+
+func abort() {
+ for {
+ exit(1)
+ }
+}
+
+//export sleepThread
+func sleepThread(nanos uint64)
+
+//export exit
+func exit(code int) int
+
+//export armGetSystemTick
+func getArmSystemTick() int64
diff --git a/src/runtime/runtime_nintendoswitch.s b/src/runtime/runtime_nintendoswitch.s
new file mode 100644
index 000000000..58ffe273f
--- /dev/null
+++ b/src/runtime/runtime_nintendoswitch.s
@@ -0,0 +1,45 @@
+.section .text.armGetSystemTick, "ax", %progbits
+.global armGetSystemTick
+.type armGetSystemTick, %function
+.align 2
+armGetSystemTick:
+ mrs x0, cntpct_el0
+ ret
+
+.section .text.nxOutputString, "ax", %progbits
+.global nxOutputString
+.type nxOutputString, %function
+.align 2
+.cfi_startproc
+nxOutputString:
+ svc 0x27
+ ret
+.cfi_endproc
+
+.section .text.exit, "ax", %progbits
+.global exit
+.type exit, %function
+.align 2
+exit:
+ svc 0x7
+ ret
+
+.section .text.setHeapSize, "ax", %progbits
+.global setHeapSize
+.type setHeapSize, %function
+.align 2
+setHeapSize:
+ str x0, [sp, #-16]!
+ svc 0x1
+ ldr x2, [sp], #16
+ str x1, [x2]
+ ret
+
+
+.section .text.sleepThread, "ax", %progbits
+.global sleepThread
+.type sleepThread, %function
+.align 2
+sleepThread:
+ svc 0xB
+ ret
diff --git a/src/runtime/runtime_nintendoswitch_heap.go b/src/runtime/runtime_nintendoswitch_heap.go
new file mode 100644
index 000000000..5518825e3
--- /dev/null
+++ b/src/runtime/runtime_nintendoswitch_heap.go
@@ -0,0 +1,31 @@
+// +build nintendoswitch
+
+// +build gc.conservative gc.leaking
+
+package runtime
+
+import "unsafe"
+
+const heapSize = 0x2000000 * 16 // Default by libnx
+
+//go:extern _stack_top
+var stackTopSymbol [0]byte
+
+var (
+ heapStart = uintptr(0)
+ heapEnd = uintptr(0)
+ stackTop = uintptr(unsafe.Pointer(&stackTopSymbol))
+)
+
+//export setHeapSize
+func setHeapSize(addr *uintptr, length uint64) uint64
+
+func preinit() {
+ setHeapSize(&heapStart, heapSize)
+
+ if heapStart == 0 {
+ panic("failed to allocate heap")
+ }
+
+ heapEnd = heapStart + heapSize
+}
diff --git a/src/runtime/runtime_nintendoswitch_noheap.go b/src/runtime/runtime_nintendoswitch_noheap.go
new file mode 100644
index 000000000..41e0b3683
--- /dev/null
+++ b/src/runtime/runtime_nintendoswitch_noheap.go
@@ -0,0 +1,7 @@
+// +build nintendoswitch
+
+// +build gc.none gc.extalloc
+
+package runtime
+
+func preinit() {}
diff --git a/src/runtime/runtime_nintendoswitch_svc.go b/src/runtime/runtime_nintendoswitch_svc.go
new file mode 100644
index 000000000..2bc2003f9
--- /dev/null
+++ b/src/runtime/runtime_nintendoswitch_svc.go
@@ -0,0 +1,22 @@
+// +build nintendoswitch
+
+package runtime
+
+import (
+ "unsafe"
+)
+
+// Result nxOutputString(const char *str, u64 size)
+//export nxOutputString
+func nxOutputString(str *uint8, size uint64) uint64
+
+func NxOutputString(str string) uint64 {
+ strData := (*_string)(unsafe.Pointer(&str))
+ return nxOutputString((*uint8)(unsafe.Pointer(strData.ptr)), uint64(strData.length))
+}
+
+//export malloc
+func extalloc(size uintptr) unsafe.Pointer
+
+//export free
+func extfree(ptr unsafe.Pointer)
diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go
index acb830946..a3c2b4471 100644
--- a/src/runtime/runtime_unix.go
+++ b/src/runtime/runtime_unix.go
@@ -1,4 +1,5 @@
// +build darwin linux,!baremetal freebsd,!baremetal
+// +build !nintendoswitch
package runtime
diff --git a/src/runtime/runtime_unix_heap.go b/src/runtime/runtime_unix_heap.go
index c9b04f1d8..14168f438 100644
--- a/src/runtime/runtime_unix_heap.go
+++ b/src/runtime/runtime_unix_heap.go
@@ -1,4 +1,5 @@
// +build darwin linux,!baremetal freebsd,!baremetal
+// +build !nintendoswitch
// +build gc.conservative gc.leaking
diff --git a/src/runtime/runtime_unix_noheap.go b/src/runtime/runtime_unix_noheap.go
index 4802fdabf..bf7f67598 100644
--- a/src/runtime/runtime_unix_noheap.go
+++ b/src/runtime/runtime_unix_noheap.go
@@ -1,5 +1,7 @@
// +build darwin linux,!baremetal freebsd,!baremetal
+// +build !nintendoswitch
+
// +build gc.none gc.extalloc
package runtime
diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go
index 3f24ddb70..337a2f011 100644
--- a/src/syscall/syscall_libc.go
+++ b/src/syscall/syscall_libc.go
@@ -1,4 +1,4 @@
-// +build darwin
+// +build darwin nintendoswitch
package syscall
diff --git a/src/syscall/syscall_nintendoswitch.go b/src/syscall/syscall_nintendoswitch.go
new file mode 100644
index 000000000..8a81de1e6
--- /dev/null
+++ b/src/syscall/syscall_nintendoswitch.go
@@ -0,0 +1,48 @@
+// +build nintendoswitch
+
+package syscall
+
+import "errors"
+
+// A Signal is a number describing a process signal.
+// It implements the os.Signal interface.
+type Signal int
+
+const (
+ _ Signal = iota
+ SIGCHLD
+ SIGINT
+ SIGKILL
+ SIGTRAP
+ SIGQUIT
+ SIGTERM
+)
+
+// File system
+
+const (
+ Stdin = 0
+ Stdout = 1
+ Stderr = 2
+)
+
+const (
+ O_RDONLY = 0
+ O_WRONLY = 1
+ O_RDWR = 2
+
+ O_CREAT = 0100
+ O_CREATE = O_CREAT
+ O_TRUNC = 01000
+ O_APPEND = 02000
+ O_EXCL = 0200
+ O_SYNC = 010000
+
+ O_CLOEXEC = 0
+)
+
+var dummyError = errors.New("unknown syscall error")
+
+func getErrno() error {
+ return dummyError
+}
diff --git a/src/syscall/tables_baremetal.go b/src/syscall/tables_baremetal.go
index c081feace..47a536bff 100644
--- a/src/syscall/tables_baremetal.go
+++ b/src/syscall/tables_baremetal.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build baremetal
+// +build baremetal nintendoswitch
package syscall
diff --git a/targets/nintendoswitch.json b/targets/nintendoswitch.json
new file mode 100644
index 000000000..583d68f06
--- /dev/null
+++ b/targets/nintendoswitch.json
@@ -0,0 +1,28 @@
+{
+ "llvm-target": "aarch64",
+ "build-tags": ["nintendoswitch", "arm64"],
+ "goos": "linux",
+ "goarch": "arm64",
+ "compiler": "clang",
+ "linker": "ld.lld",
+ "rtlib": "compiler-rt",
+ "libc": "picolibc",
+ "gc": "conservative",
+ "relocation-model": "pic",
+ "cflags": [
+ "-target", "aarch64-none-linux-gnu",
+ "-mtune=cortex-a57",
+ "-fPIE",
+ "-Werror",
+ "-Qunused-arguments",
+ "-fshort-enums",
+ "-fomit-frame-pointer",
+ "-fno-exceptions", "-fno-unwind-tables",
+ "-ffunction-sections", "-fdata-sections"
+ ],
+ "linkerscript": "targets/nintendoswitch.ld",
+ "extra-files": [
+ "targets/nintendoswitch.s",
+ "src/runtime/runtime_nintendoswitch.s"
+ ]
+}
diff --git a/targets/nintendoswitch.ld b/targets/nintendoswitch.ld
new file mode 100644
index 000000000..e61391c83
--- /dev/null
+++ b/targets/nintendoswitch.ld
@@ -0,0 +1,61 @@
+OUTPUT_FORMAT(elf64-littleaarch64)
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+
+PHDRS
+{
+ text PT_LOAD FLAGS(5);
+ rodata PT_LOAD FLAGS(4);
+ data PT_LOAD FLAGS(6);
+ bss PT_LOAD FLAGS(6);
+ dynamic PT_DYNAMIC;
+}
+
+SECTIONS
+{
+ . = 0;
+
+ .text : ALIGN(0x1000) {
+ HIDDEN(__text_start = .);
+ KEEP(*(.text.jmp))
+
+ . = 0x80;
+
+ *(.text .text.*)
+ }
+
+ /* Read-only sections */
+
+ . = ALIGN(0x1000);
+
+ .rodata : { *(.rodata .rodata.*) } :rodata
+ .mod0 : {
+ KEEP(crt0.nso.o(.data.mod0))
+ KEEP(crt0.nro.o(.data.mod0))
+ KEEP(crt0.lib.nro.o(.data.mod0))
+ }
+
+ /* Read-write sections */
+ . = ALIGN(0x1000);
+
+ .data : {
+ *(.data .data.*)
+ } :data
+
+ .dynamic : {
+ HIDDEN(__dynamic_start = .);
+ *(.dynamic)
+ }
+
+ /* BSS section */
+
+ . = ALIGN(0x1000);
+
+ .bss : {
+ HIDDEN(__bss_start = .);
+ *(.bss .bss.*)
+ *(COMMON)
+ . = ALIGN(8);
+ HIDDEN(__bss_end = .);
+ } :bss
+}
diff --git a/targets/nintendoswitch.s b/targets/nintendoswitch.s
new file mode 100644
index 000000000..8e36fe7a6
--- /dev/null
+++ b/targets/nintendoswitch.s
@@ -0,0 +1,51 @@
+.section .text.jmp, "x"
+.global _start
+_start:
+ b start
+ .word _mod_header - _start
+
+.section .data.mod0
+ .word 0, 8
+
+.global _mod_header
+_mod_header:
+ .ascii "MOD0"
+ .word __dynamic_start - _mod_header
+ .word __bss_start - _mod_header
+ .word __bss_end - _mod_header
+ .word 0, 0 // eh_frame_hdr start/end
+ .word 0 // runtime-generated module object offset
+
+.section .text, "x"
+.global start
+start:
+
+ // save lr
+ mov x7, x30
+
+ // get aslr base
+ bl +4
+ sub x6, x30, #0x88
+
+ // context ptr and main thread handle
+ mov x5, x0
+ mov x4, x1
+
+ // clear .bss
+ adrp x5, __bss_start
+ add x5, x5, #:lo12:__bss_start
+ adrp x6, __bss_end
+ add x6, x6, #:lo12:__bss_end
+
+bssloop:
+ cmp x5, x6
+ b.eq run
+ str xzr, [x5]
+ add x5, x5, 8
+ b bssloop
+
+run:
+ // call entrypoint
+ adrp x30, exit
+ add x30, x30, #:lo12:exit
+ b main