new file mode 100644
@@ -0,0 +1,28 @@
+/*
+ * Nios2 semihosting interface.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef SEMICALL_H
+#define SEMICALL_H
+
+#define HOSTED_EXIT 0
+#define HOSTED_INIT_SIM 1
+#define HOSTED_OPEN 2
+#define HOSTED_CLOSE 3
+#define HOSTED_READ 4
+#define HOSTED_WRITE 5
+#define HOSTED_LSEEK 6
+#define HOSTED_RENAME 7
+#define HOSTED_UNLINK 8
+#define HOSTED_STAT 9
+#define HOSTED_FSTAT 10
+#define HOSTED_GETTIMEOFDAY 11
+#define HOSTED_ISATTY 12
+#define HOSTED_SYSTEM 13
+
+#define semihosting_call break 1
+
+#endif /* SEMICALL_H */
new file mode 100644
@@ -0,0 +1,66 @@
+/*
+ * Link script for the Nios2 10m50-ghrd board.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+MEMORY
+{
+ tpf (rx) : ORIGIN = 0xc0000000, LENGTH = 1K
+ ram (rwx) : ORIGIN = 0xc8000000, LENGTH = 128M
+}
+
+PHDRS
+{
+ RAM PT_LOAD;
+}
+
+ENTRY(_start)
+EXTERN(_start)
+EXTERN(_interrupt)
+EXTERN(_fast_tlb_miss)
+
+SECTIONS
+{
+ /* Begin at the (hardcoded) _interrupt entry point. */
+ .text 0xc8000120 : {
+ *(.text.intr)
+ *(.text .text.* .gnu.linkonce.t.*)
+ } >ram :RAM
+
+ .rodata : ALIGN(4) {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ } > ram :RAM
+
+ .eh_frame_hdr : ALIGN (4) {
+ KEEP (*(.eh_frame_hdr))
+ *(.eh_frame_entry .eh_frame_entry.*)
+ } >ram :RAM
+ .eh_frame : ALIGN (4) {
+ KEEP (*(.eh_frame)) *(.eh_frame.*)
+ } >ram :RAM
+
+ .data : ALIGN(4) {
+ *(.shdata)
+ *(.data .data.* .gnu.linkonce.d.*)
+ . = ALIGN(4);
+ _gp = ABSOLUTE(. + 0x8000);
+ *(.got.plt) *(.got)
+ *(.lit8)
+ *(.lit4)
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ } >ram :RAM
+
+ .bss : ALIGN(4) {
+ __bss_start = ABSOLUTE(.);
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end = ABSOLUTE(.);
+ } >ram :RAM
+
+ __stack = ORIGIN(ram) + LENGTH(ram);
+}
new file mode 100644
@@ -0,0 +1,32 @@
+#
+# Nios2 system tests
+#
+# Copyright Linaro Ltd 2022
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+
+NIOS2_SYSTEM_SRC = $(SRC_PATH)/tests/tcg/nios2
+VPATH += $(NIOS2_SYSTEM_SRC)
+
+# These objects provide the basic boot code and helper functions for all tests
+CRT_OBJS = boot.o intr.o $(MINILIB_OBJS)
+LINK_SCRIPT = $(NIOS2_SYSTEM_SRC)/10m50-ghrd.ld
+
+CFLAGS += -nostdlib -g -O0 $(MINILIB_INC)
+LDFLAGS += -Wl,-T$(LINK_SCRIPT) -static -nostdlib $(CRT_OBJS) -lgcc
+
+%.o: %.S
+ $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -x assembler-with-cpp -c $< -o $@, AS, $@)
+
+%.o: %.c
+ $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@, CC, $@)
+
+# Build and link the tests
+%: %.o $(LINK_SCRIPT) $(CRT_OBJS)
+ $(call quiet-command, $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS), LD, $@)
+
+# FIXME: nios2 semihosting writes to stdout, not a chardev
+QEMU_OPTS = -M 10m50-ghrd,vic=on -semihosting >$@.out -kernel
+
+memory: CFLAGS+=-DCHECK_UNALIGNED=0
+TESTS += $(MULTIARCH_TESTS)
new file mode 100644
@@ -0,0 +1,218 @@
+/*
+ * Minimal Nios2 system boot code.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "semicall.h"
+
+ .text
+ .set noat
+
+_start:
+ /* Linker script defines stack at end of ram. */
+ movia sp, __stack
+
+ /* Install trampoline to _fast_tlb_miss at hardcoded vector. */
+ movia r4, 0xc0000100
+ movia r5, _ftm_tramp
+ movi r6, .L__ftm_end - _ftm_tramp
+ call memcpy
+
+ /* Zero the bss to satisfy C. */
+ movia r4, __bss_start
+ movia r6, __bss_end
+ sub r6, r6, r4
+ movi r5, 0
+ call memset
+
+ /* Test! */
+ call main
+
+ /* Exit with main's return value. */
+ movi r4, HOSTED_EXIT
+ mov r5, r2
+ semihosting_call
+
+ .globl _start
+ .type _start, @function
+ .size _start, . - _start
+
+_ftm_tramp:
+ movia et, _fast_tlb_miss
+ jmp et
+.L__ftm_end:
+
+ .type _ftm_tramp, @function
+ .size _ftm_tramp, . - _ftm_tramp
+
+#define dst r4
+#define src r5
+#define len r6
+
+memcpy:
+ /* Store return value right away, per API */
+ mov r2, dst
+
+ /* Check for both dst and src aligned. */
+ or at, dst, src
+ andi at, at, 3
+ bne at, zero, .L_mc_test1
+
+ /* Copy blocks of 8. */
+
+ movi at, 8
+ bltu len, at, .L_mc_test4
+
+.L_mc_loop8:
+ ldw r8, 0(src)
+ ldw r9, 4(src)
+ addi src, src, 8
+ addi dst, dst, 8
+ subi len, len, 8
+ stw r8, -8(dst)
+ stw r9, -4(dst)
+ bgeu len, at, .L_mc_loop8
+
+ /* Copy final aligned block of 4. */
+
+.L_mc_test4:
+ movi at, 4
+ bltu len, at, .L_mc_test1
+
+ ldw r8, 0(src)
+ addi src, src, 4
+ addi dst, dst, 4
+ subi len, len, 4
+ stw r8, -4(dst)
+
+ /* Copy single bytes to finish. */
+
+.L_mc_test1:
+ beq len, zero, .L_mc_done
+
+.L_mc_loop1:
+ ldb r8, 0(src)
+ addi src, src, 1
+ addi dst, dst, 1
+ subi len, len, 1
+ stb r8, -1(dst)
+ bne len, zero, .L_mc_loop1
+
+.L_mc_done:
+ ret
+
+#undef dst
+#undef src
+#undef len
+
+ .global memcpy
+ .type memcpy, @function
+ .size memcpy, . - memcpy
+
+#define dst r4
+#define val r5
+#define len r6
+
+memset:
+ /* Store return value right away, per API */
+ mov r2, dst
+
+ /* Check for small blocks; fall back to bytewise. */
+ movi r3, 8
+ bltu len, r3, .L_ms_test1
+
+ /* Replicate the byte across the word. */
+ andi val, val, 0xff
+ slli at, val, 8
+ or val, val, at
+ slli at, val, 16
+ or val, val, at
+
+ /* Check for destination alignment; realign if needed. */
+ andi at, dst, 3
+ bne at, zero, .L_ms_align
+
+ /* Set blocks of 8. */
+
+.L_ms_loop8:
+ stw val, 0(dst)
+ stw val, 4(dst)
+ addi dst, dst, 8
+ subi len, len, 8
+ bgeu len, r3, .L_ms_loop8
+
+ /* Set final aligned block of 4. */
+
+.L_ms_test4:
+ movi at, 4
+ bltu len, at, .L_ms_test1
+
+ stw r8, 0(dst)
+ addi dst, dst, 4
+ subi len, len, 4
+ stw r8, -4(dst)
+
+ /* Set single bytes to finish. */
+
+.L_ms_test1:
+ beq len, zero, .L_ms_done
+
+.L_ms_loop1:
+ stb r8, 0(dst)
+ addi dst, dst, 1
+ subi len, len, 1
+ bne len, zero, .L_ms_loop1
+
+.L_ms_done:
+ ret
+
+ /* Realign for a large block, len >= 8. */
+.L_ms_align:
+ andi at, dst, 1
+ beq at, zero, 2f
+
+ stb val, 0(dst)
+ addi dst, dst, 1
+ subi len, len, 1
+
+2: andi at, dst, 2
+ beq at, zero, 4f
+
+ sth val, 0(dst)
+ addi dst, dst, 2
+ subi len, len, 2
+
+4: bgeu len, r3, .L_ms_loop8
+ br .L_ms_test4
+
+#undef dst
+#undef val
+#undef len
+
+ .global memset
+ .type memset, @function
+ .size memset, . - memset
+
+/*
+ * void __sys_outc(char c);
+ */
+__sys_outc:
+ subi sp, sp, 16
+ stb r4, 0(sp) /* buffer[0] = c */
+ movi at, 1
+ stw at, 4(sp) /* STDOUT_FILENO */
+ stw sp, 8(sp) /* buffer */
+ stw at, 12(sp) /* len */
+
+ movi r4, HOSTED_WRITE
+ addi r5, sp, 4
+ semihosting_call
+
+ addi sp, sp, 16
+ ret
+
+ .global __sys_outc
+ .type __sys_outc, @function
+ .size __sys_outc, . - __sys_outc
new file mode 100644
@@ -0,0 +1,31 @@
+/*
+ * Minimal Nios2 system boot code -- exit on interrupt.
+ *
+ * Copyright Linaro Ltd 2022
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "semicall.h"
+
+ .section .text.intr, "ax"
+ .global _interrupt
+ .type _interrupt, @function
+
+_interrupt:
+ rdctl r5, exception /* extract exception.CAUSE */
+ srli r5, r5, 2
+ movi r4, HOSTED_EXIT
+ semihosting_call
+
+ .size _interrupt, . - _interrupt
+
+ .text
+ .global _fast_tlb_miss
+ .type _fast_tlb_miss, @function
+
+_fast_tlb_miss:
+ movi r5, 32
+ movi r4, HOSTED_EXIT
+ semihosting_call
+
+ .size _fast_tlb_miss, . - _fast_tlb_miss