[sbi] Add support for running as a RISC-V SBI payload
Add basic support for running directly on top of SBI, with no UEFI
firmware present. Build as e.g.:
make CROSS=riscv64-linux-gnu- bin-riscv64/ipxe.sbi
The resulting binary can be tested in QEMU using e.g.:
qemu-system-riscv64 -M virt -cpu max -serial stdio \
-kernel bin-riscv64/ipxe.sbi
No drivers or executable binary formats are supported yet, but the
unit test suite may be run successfully.
Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping
index ba61e21..2b4356d 100644
--- a/src/Makefile.housekeeping
+++ b/src/Makefile.housekeeping
@@ -352,6 +352,8 @@
# Determine build platform
DEFAULT_PLATFORM_i386 := pcbios
DEFAULT_PLATFORM_x86_64 := pcbios
+DEFAULT_PLATFORM_riscv32 := sbi
+DEFAULT_PLATFORM_riscv64 := sbi
DEFAULT_PLATFORM := $(DEFAULT_PLATFORM_$(ARCH))
PLATFORM := $(firstword $(BIN_PLATFORM) $(DEFAULT_PLATFORM) none)
CFLAGS += -DPLATFORM=$(PLATFORM) -DPLATFORM_$(PLATFORM)
diff --git a/src/arch/riscv/Makefile b/src/arch/riscv/Makefile
index d3ae4e8..324e140 100644
--- a/src/arch/riscv/Makefile
+++ b/src/arch/riscv/Makefile
@@ -11,6 +11,7 @@
#
SRCDIRS += arch/riscv/core
SRCDIRS += arch/riscv/interface/sbi
+SRCDIRS += arch/riscv/prefix
# RISCV-specific flags
#
diff --git a/src/arch/riscv/Makefile.sbi b/src/arch/riscv/Makefile.sbi
new file mode 100644
index 0000000..dee1b6e
--- /dev/null
+++ b/src/arch/riscv/Makefile.sbi
@@ -0,0 +1,16 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Build a position-independent executable, with relocations required
+# only for data values. Runtime relocations are applied by the
+# prefix code.
+#
+CFLAGS += -mcmodel=medany -fpie
+LDFLAGS += -pie --no-dynamic-linker
+
+# Linker script
+#
+LDSCRIPT = arch/riscv/scripts/sbi.lds
+
+# Media types
+#
+MEDIA += sbi
diff --git a/src/arch/riscv/core/stack.S b/src/arch/riscv/core/stack.S
new file mode 100644
index 0000000..1cd1da7
--- /dev/null
+++ b/src/arch/riscv/core/stack.S
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * Internal stack
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+#define STACK_ALIGN 16
+
+#define STACK_SIZE 8192
+
+ .section ".stack", "aw", @nobits
+ .balign STACK_ALIGN
+ .globl _stack
+_stack:
+ .space STACK_SIZE
+ .globl _estack
+_estack:
diff --git a/src/arch/riscv/include/bits/umalloc.h b/src/arch/riscv/include/bits/umalloc.h
new file mode 100644
index 0000000..a7171ca
--- /dev/null
+++ b/src/arch/riscv/include/bits/umalloc.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_UMALLOC_H
+#define _BITS_UMALLOC_H
+
+/** @file
+ *
+ * RISCV-specific user memory allocation API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/sbi_umalloc.h>
+
+#endif /* _BITS_UMALLOC_H */
diff --git a/src/arch/riscv/include/ipxe/errno/sbi.h b/src/arch/riscv/include/ipxe/errno/sbi.h
new file mode 100644
index 0000000..2428183
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/errno/sbi.h
@@ -0,0 +1,19 @@
+#ifndef _IPXE_ERRNO_SBI_H
+#define _IPXE_ERRNO_SBI_H
+
+/**
+ * @file
+ *
+ * RISC-V SBI platform error codes
+ *
+ * We never need to return SBI error codes ourselves, so we
+ * arbitrarily choose to use the Linux error codes as platform error
+ * codes.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/errno/linux.h>
+
+#endif /* _IPXE_ERRNO_SBI_H */
diff --git a/src/arch/riscv/include/ipxe/sbi_umalloc.h b/src/arch/riscv/include/ipxe/sbi_umalloc.h
new file mode 100644
index 0000000..5763239
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/sbi_umalloc.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_SBI_UMALLOC_H
+#define _IPXE_SBI_UMALLOC_H
+
+/** @file
+ *
+ * External memory allocation
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef UMALLOC_SBI
+#define UMALLOC_PREFIX_sbi
+#else
+#define UMALLOC_PREFIX_sbi __sbi_
+#endif
+
+#endif /* _IPXE_SBI_UMALLOC_H */
diff --git a/src/arch/riscv/interface/sbi/sbi_umalloc.c b/src/arch/riscv/interface/sbi/sbi_umalloc.c
new file mode 100644
index 0000000..2f9935a
--- /dev/null
+++ b/src/arch/riscv/interface/sbi/sbi_umalloc.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdlib.h>
+#include <ipxe/umalloc.h>
+
+/** @file
+ *
+ * iPXE user memory allocation API for SBI
+ *
+ */
+
+/** Equivalent of NOWHERE for user pointers */
+#define UNOWHERE ( ~UNULL )
+
+/**
+ * Reallocate external memory
+ *
+ * @v old_ptr Memory previously allocated by umalloc(), or UNULL
+ * @v new_size Requested size
+ * @ret new_ptr Allocated memory, or UNULL
+ *
+ * Calling realloc() with a new size of zero is a valid way to free a
+ * memory block.
+ */
+static userptr_t sbi_urealloc ( userptr_t old_ptr, size_t new_size ) {
+
+ /* External allocation not yet implemented: allocate from heap */
+ return ( ( userptr_t ) realloc ( ( ( void * ) old_ptr ), new_size ) );
+}
+
+PROVIDE_UMALLOC ( sbi, urealloc, sbi_urealloc );
diff --git a/src/arch/riscv/prefix/sbiprefix.S b/src/arch/riscv/prefix/sbiprefix.S
new file mode 100644
index 0000000..0de0019
--- /dev/null
+++ b/src/arch/riscv/prefix/sbiprefix.S
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
+
+/** @file
+ *
+ * SBI position-independent executable prefix
+ *
+ */
+
+ .section ".note.GNU-stack", "", @progbits
+ .text
+
+/* SBI debug console extension */
+#define SBI_DBCN ( ( 'D' << 24 ) | ( 'B' << 16 ) | ( 'C' << 8 ) | 'N' )
+#define SBI_DBCN_WRITE 0x00
+
+/* SBI system reset extension */
+#define SBI_SRST ( ( 'S' << 24 ) | ( 'R' << 16 ) | ( 'S' << 8 ) | 'T' )
+#define SBI_SRST_SYSTEM_RESET 0x00
+#define SBI_RESET_COLD 0x00000001
+
+/* Relative relocation type */
+#define R_RISCV_RELATIVE 3
+
+ /* Layout of a relocation record */
+ .struct 0
+rela_offset: .space ( __riscv_xlen / 8 )
+rela_type: .space ( __riscv_xlen / 8 )
+rela_addend: .space ( __riscv_xlen / 8 )
+rela_len:
+ .previous
+
+ /*
+ * Display progress message via debug console
+ */
+ .macro progress message
+#ifndef NDEBUG
+ .section ".prefix.data", "aw", @progbits
+progress_\@:
+ .ascii "\message"
+ .equ progress_\@_len, . - progress_\@
+ .size progress_\@, . - progress_\@
+ .previous
+ li a7, SBI_DBCN
+ li a6, SBI_DBCN_WRITE
+ li a0, progress_\@_len
+ la a1, progress_\@
+ mv a2, zero
+ ecall
+#endif
+ .endm
+
+ /*
+ * SBI entry point
+ */
+ .section ".prefix", "ax", @progbits
+ .org 0
+ .globl _sbi_start
+_sbi_start:
+ /* Preserve arguments */
+ mv s0, a0
+ mv s1, a1
+ progress "\nSBI->iPXE"
+
+ /* Apply dynamic relocations */
+ la t0, _reloc
+ la t1, _ereloc
+ la t2, _sbi_start
+1: /* Read relocation record */
+ LOADN t3, rela_offset(t0)
+ LOADN t4, rela_type(t0)
+ LOADN t5, rela_addend(t0)
+ /* Check relocation type */
+ addi t4, t4, -R_RISCV_RELATIVE
+ bnez t4, 2f
+ /* Apply relocation */
+ add t3, t3, t2
+ add t5, t5, t2
+ STOREN t5, (t3)
+2: /* Loop */
+ addi t0, t0, rela_len
+ blt t0, t1, 1b
+ progress " .reloc"
+
+ /* Zero the bss */
+ la t0, _bss
+ la t1, _ebss
+1: STOREN zero, (t0)
+ addi t0, t0, ( __riscv_xlen / 8 )
+ blt t0, t1, 1b
+ progress " .bss"
+
+ /* Set up stack */
+ la sp, _estack
+ progress " .stack"
+
+ /* Store boot hart */
+ la t0, boot_hart
+ STOREN s0, (t0)
+
+ /* Register device tree */
+ mv a0, s1
+ call register_fdt
+
+ /* Call main program */
+ progress "\n\n"
+ call main
+
+ /* We have no return path, since the M-mode SBI implementation
+ * will have jumped to us by setting our start address in MEPC
+ * and issuing an MRET instruction.
+ *
+ * Attempt a system reset, since there is nothing else we can
+ * viably do at this point.
+ */
+ progress "\niPXE->SBI reset\n"
+ li a7, SBI_SRST
+ li a6, SBI_SRST_SYSTEM_RESET
+ li a0, SBI_RESET_COLD
+ mv a1, zero
+ ecall
+
+ /* If reset failed, lock the system */
+ progress "(reset failed)\n"
+1: wfi
+ j 1b
+ .size _sbi_start, . - _sbi_start
+
+ /* File split information for the compressor */
+ .section ".zinfo", "a", @progbits
+ .ascii "COPY"
+ .word 0
+ .word _sbi_filesz
+ .word 1
diff --git a/src/arch/riscv/scripts/sbi.lds b/src/arch/riscv/scripts/sbi.lds
new file mode 100644
index 0000000..a65b271
--- /dev/null
+++ b/src/arch/riscv/scripts/sbi.lds
@@ -0,0 +1,119 @@
+/*
+ * Linker script for RISC-V SBI images
+ *
+ */
+
+SECTIONS {
+
+ /* Start at virtual address zero */
+ . = 0;
+
+ /* Weak symbols that need zero values if not otherwise defined */
+ .weak 0x0 : {
+ _weak = .;
+ *(.weak)
+ *(.weak.*)
+ _eweak = .;
+ }
+ _assert = ASSERT ( ( _weak == _eweak ), ".weak is non-zero length" );
+
+ /* Prefix code */
+ .prefix : {
+ _prefix = .;
+ *(.prefix)
+ *(.prefix.*)
+ _eprefix = .;
+ }
+
+ /* Program code */
+ .text : {
+ _text = .;
+ *(.text)
+ *(.text.*)
+ _etext = .;
+ }
+
+ /* Align to page size to allow linker to generate W^X segments */
+ . = ALIGN ( 4096 );
+
+ /* Read-only data */
+ .rodata : {
+ _rodata = .;
+ *(.rodata)
+ *(.rodata.*)
+ _erodata = .;
+ }
+
+ /* Writable data */
+ .data : {
+ _data = .;
+ *(.data)
+ *(.data.*)
+ KEEP(*(SORT(.tbl.*))) /* Various tables. See include/tables.h */
+ KEEP(*(.provided))
+ KEEP(*(.provided.*))
+ _edata = .;
+ }
+
+ /* Uninitialised and discardable data */
+ OVERLAY : {
+
+ /* Runtime relocations (discarded after use) */
+ .rela.dyn {
+ _reloc = .;
+ *(.rela)
+ *(.rela.dyn)
+ }
+
+ /* Compressor information block */
+ .zinfo {
+ _zinfo = .;
+ KEEP(*(.zinfo))
+ KEEP(*(.zinfo.*))
+ _ezinfo = .;
+ }
+
+ /* Uninitialised data */
+ .bss {
+ _bss = .;
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ *(.stack)
+ *(.stack.*)
+ /* Align to allow for easy zeroing by prefix code */
+ . = ALIGN ( 16 );
+ _ebss = .;
+ }
+ }
+
+ /* Calculate end of relocations
+ *
+ * This cannot be done by placing "_ereloc = .;" inside the
+ * .rela.dyn section, since the dynamic relocations are not
+ * present in the input sections but are instead generated during
+ * linking.
+ */
+ _ereloc = ( _reloc + __load_stop_reladyn - __load_start_reladyn );
+
+ /* Length of initialised data */
+ _sbi_filesz = ABSOLUTE ( _ereloc );
+
+ /* Unwanted sections */
+ /DISCARD/ : {
+ *(.comment)
+ *(.comment.*)
+ *(.note)
+ *(.note.*)
+ *(.eh_frame)
+ *(.eh_frame.*)
+ *(.dynamic)
+ *(.dynsym)
+ *(.dynstr)
+ *(.einfo)
+ *(.einfo.*)
+ *(.discard)
+ *(.discard.*)
+ *(.pci_devlist.*)
+ }
+}
diff --git a/src/arch/riscv32/Makefile.sbi b/src/arch/riscv32/Makefile.sbi
new file mode 100644
index 0000000..d622877
--- /dev/null
+++ b/src/arch/riscv32/Makefile.sbi
@@ -0,0 +1,6 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Include generic SBI Makefile
+#
+MAKEDEPS += arch/riscv/Makefile.sbi
+include arch/riscv/Makefile.sbi
diff --git a/src/arch/riscv32/include/ipxe/sbi/dhcparch.h b/src/arch/riscv32/include/ipxe/sbi/dhcparch.h
new file mode 100644
index 0000000..713d4cf
--- /dev/null
+++ b/src/arch/riscv32/include/ipxe/sbi/dhcparch.h
@@ -0,0 +1,20 @@
+#ifndef _IPXE_SBI_DHCPARCH_H
+#define _IPXE_SBI_DHCPARCH_H
+
+/** @file
+ *
+ * DHCP client architecture definitions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/dhcp.h>
+
+/** DHCP client architecture */
+#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_RISCV32
+
+/** DHCP client network device interface */
+#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */
+
+#endif /* _IPXE_SBI_DHCPARCH_H */
diff --git a/src/arch/riscv64/Makefile.sbi b/src/arch/riscv64/Makefile.sbi
new file mode 100644
index 0000000..d622877
--- /dev/null
+++ b/src/arch/riscv64/Makefile.sbi
@@ -0,0 +1,6 @@
+# -*- makefile -*- : Force emacs to use Makefile mode
+
+# Include generic SBI Makefile
+#
+MAKEDEPS += arch/riscv/Makefile.sbi
+include arch/riscv/Makefile.sbi
diff --git a/src/arch/riscv64/include/ipxe/sbi/dhcparch.h b/src/arch/riscv64/include/ipxe/sbi/dhcparch.h
new file mode 100644
index 0000000..e172f06
--- /dev/null
+++ b/src/arch/riscv64/include/ipxe/sbi/dhcparch.h
@@ -0,0 +1,20 @@
+#ifndef _IPXE_SBI_DHCPARCH_H
+#define _IPXE_SBI_DHCPARCH_H
+
+/** @file
+ *
+ * DHCP client architecture definitions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/dhcp.h>
+
+/** DHCP client architecture */
+#define DHCP_ARCH_CLIENT_ARCHITECTURE DHCP_CLIENT_ARCHITECTURE_RISCV64
+
+/** DHCP client network device interface */
+#define DHCP_ARCH_CLIENT_NDI 1 /* UNDI */ , 3, 10 /* v3.10 */
+
+#endif /* _IPXE_SBI_DHCPARCH_H */
diff --git a/src/config/config_sbi.c b/src/config/config_sbi.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/config/config_sbi.c
diff --git a/src/config/defaults/sbi.h b/src/config/defaults/sbi.h
new file mode 100644
index 0000000..42fb515
--- /dev/null
+++ b/src/config/defaults/sbi.h
@@ -0,0 +1,36 @@
+#ifndef CONFIG_DEFAULTS_SBI_H
+#define CONFIG_DEFAULTS_SBI_H
+
+/** @file
+ *
+ * Configuration defaults for RISC-V SBI
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#define IOAPI_RISCV
+#define IOMAP_VIRT
+#define DMAAPI_FLAT
+#define UACCESS_FLAT
+#define TIMER_ZICNTR
+#define ENTROPY_ZKR
+
+#define CONSOLE_SBI
+#define REBOOT_SBI
+#define UMALLOC_SBI
+
+#define ACPI_NULL
+#define MPAPI_NULL
+#define NAP_NULL
+#define PCIAPI_NULL
+#define SANBOOT_NULL
+#define SMBIOS_NULL
+#define TIME_NULL
+
+#define IMAGE_SCRIPT
+
+#define REBOOT_CMD
+#define POWEROFF_CMD
+
+#endif /* CONFIG_DEFAULTS_SBI_H */