summaryrefslogtreecommitdiffstats
path: root/src/arch/riscv/include
diff options
context:
space:
mode:
Diffstat (limited to 'src/arch/riscv/include')
-rw-r--r--src/arch/riscv/include/bits/bigint.h381
-rw-r--r--src/arch/riscv/include/bits/bitops.h82
-rw-r--r--src/arch/riscv/include/bits/byteswap.h48
-rw-r--r--src/arch/riscv/include/bits/compiler.h40
-rw-r--r--src/arch/riscv/include/bits/dma.h14
-rw-r--r--src/arch/riscv/include/bits/endian.h8
-rw-r--r--src/arch/riscv/include/bits/errfile.h24
-rw-r--r--src/arch/riscv/include/bits/io.h17
-rw-r--r--src/arch/riscv/include/bits/iomap.h14
-rw-r--r--src/arch/riscv/include/bits/lkrn.h34
-rw-r--r--src/arch/riscv/include/bits/nap.h20
-rw-r--r--src/arch/riscv/include/bits/profile.h28
-rw-r--r--src/arch/riscv/include/bits/reboot.h14
-rw-r--r--src/arch/riscv/include/bits/setjmp.h16
-rw-r--r--src/arch/riscv/include/bits/stdint.h23
-rw-r--r--src/arch/riscv/include/bits/string.h82
-rw-r--r--src/arch/riscv/include/bits/strings.h91
-rw-r--r--src/arch/riscv/include/bits/tcpip.h15
-rw-r--r--src/arch/riscv/include/bits/virt_offset.h33
-rw-r--r--src/arch/riscv/include/ipxe/csr.h75
-rw-r--r--src/arch/riscv/include/ipxe/errno/sbi.h19
-rw-r--r--src/arch/riscv/include/ipxe/hart.h16
-rw-r--r--src/arch/riscv/include/ipxe/riscv_dma.h45
-rw-r--r--src/arch/riscv/include/ipxe/riscv_io.h141
-rw-r--r--src/arch/riscv/include/ipxe/sbi.h213
-rw-r--r--src/arch/riscv/include/ipxe/sbi_reboot.h18
-rw-r--r--src/arch/riscv/include/ipxe/svpage.h28
-rw-r--r--src/arch/riscv/include/ipxe/xthead.h21
-rw-r--r--src/arch/riscv/include/ipxe/zicbom.h17
29 files changed, 1577 insertions, 0 deletions
diff --git a/src/arch/riscv/include/bits/bigint.h b/src/arch/riscv/include/bits/bigint.h
new file mode 100644
index 000000000..7f87d9748
--- /dev/null
+++ b/src/arch/riscv/include/bits/bigint.h
@@ -0,0 +1,381 @@
+#ifndef _BITS_BIGINT_H
+#define _BITS_BIGINT_H
+
+/** @file
+ *
+ * Big integer support
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+
+/** Element of a big integer */
+typedef unsigned long bigint_element_t;
+
+/**
+ * Initialise big integer
+ *
+ * @v value0 Element 0 of big integer to initialise
+ * @v size Number of elements
+ * @v data Raw data
+ * @v len Length of raw data
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_init_raw ( unsigned long *value0, unsigned int size,
+ const void *data, size_t len ) {
+ size_t pad_len = ( sizeof ( bigint_t ( size ) ) - len );
+ uint8_t *value_byte = ( ( void * ) value0 );
+ const uint8_t *data_byte = ( data + len );
+
+ /* Copy raw data in reverse order, padding with zeros */
+ while ( len-- )
+ *(value_byte++) = *(--data_byte);
+ while ( pad_len-- )
+ *(value_byte++) = 0;
+}
+
+/**
+ * Add big integers
+ *
+ * @v addend0 Element 0 of big integer to add
+ * @v value0 Element 0 of big integer to be added to
+ * @v size Number of elements
+ * @ret carry Carry out
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_add_raw ( const unsigned long *addend0, unsigned long *value0,
+ unsigned int size ) {
+ bigint_t ( size ) __attribute__ (( may_alias )) *value =
+ ( ( void * ) value0 );
+ unsigned long *valueN = ( value0 + size );
+ unsigned long *discard_addend;
+ unsigned long *discard_value;
+ unsigned long discard_addend_i;
+ unsigned long discard_value_i;
+ unsigned long discard_temp;
+ unsigned long carry;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ /* Load addend[i] and value[i] */
+ LOADN " %2, (%0)\n\t"
+ LOADN " %3, (%1)\n\t"
+ /* Add carry flag and addend */
+ "add %3, %3, %5\n\t"
+ "sltu %4, %3, %5\n\t"
+ "add %3, %3, %2\n\t"
+ "sltu %5, %3, %2\n\t"
+ "or %5, %4, %5\n\t"
+ /* Store value[i] */
+ STOREN " %3, (%1)\n\t"
+ /* Loop */
+ "addi %0, %0, %8\n\t"
+ "addi %1, %1, %8\n\t"
+ "bne %1, %7, 1b\n\t"
+ : "=&r" ( discard_addend ),
+ "=&r" ( discard_value ),
+ "=&r" ( discard_addend_i ),
+ "=&r" ( discard_value_i ),
+ "=&r" ( discard_temp ),
+ "=&r" ( carry ),
+ "+m" ( *value )
+ : "r" ( valueN ),
+ "i" ( sizeof ( unsigned long ) ),
+ "0" ( addend0 ), "1" ( value0 ), "5" ( 0 ) );
+ return carry;
+}
+
+/**
+ * Subtract big integers
+ *
+ * @v subtrahend0 Element 0 of big integer to subtract
+ * @v value0 Element 0 of big integer to be subtracted from
+ * @v size Number of elements
+ * @ret borrow Borrow out
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_subtract_raw ( const unsigned long *subtrahend0, unsigned long *value0,
+ unsigned int size ) {
+ bigint_t ( size ) __attribute__ (( may_alias )) *value =
+ ( ( void * ) value0 );
+ unsigned long *valueN = ( value0 + size );
+ unsigned long *discard_subtrahend;
+ unsigned long *discard_value;
+ unsigned long discard_subtrahend_i;
+ unsigned long discard_value_i;
+ unsigned long discard_temp;
+ unsigned long borrow;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ /* Load subtrahend[i] and value[i] */
+ LOADN " %2, (%0)\n\t"
+ LOADN " %3, (%1)\n\t"
+ /* Subtract carry flag and subtrahend */
+ "sltu %4, %3, %5\n\t"
+ "sub %3, %3, %5\n\t"
+ "sltu %5, %3, %2\n\t"
+ "sub %3, %3, %2\n\t"
+ "or %5, %5, %4\n\t"
+ /* Store value[i] */
+ STOREN " %3, (%1)\n\t"
+ /* Loop */
+ "addi %0, %0, %8\n\t"
+ "addi %1, %1, %8\n\t"
+ "bne %1, %7, 1b\n\t"
+ : "=&r" ( discard_subtrahend ),
+ "=&r" ( discard_value ),
+ "=&r" ( discard_subtrahend_i ),
+ "=&r" ( discard_value_i ),
+ "=&r" ( discard_temp ),
+ "=&r" ( borrow ),
+ "+m" ( *value )
+ : "r" ( valueN ),
+ "i" ( sizeof ( unsigned long ) ),
+ "0" ( subtrahend0 ), "1" ( value0 ),
+ "5" ( 0 ) );
+ return borrow;
+}
+
+/**
+ * Shift big integer left
+ *
+ * @v value0 Element 0 of big integer
+ * @v size Number of elements
+ * @ret out Bit shifted out
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_shl_raw ( unsigned long *value0, unsigned int size ) {
+ bigint_t ( size ) __attribute__ (( may_alias )) *value =
+ ( ( void * ) value0 );
+ unsigned long *valueN = ( value0 + size );
+ unsigned long *discard_value;
+ unsigned long discard_value_i;
+ unsigned long discard_temp;
+ unsigned long carry;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ /* Load value[i] */
+ LOADN " %1, (%0)\n\t"
+ /* Shift left */
+ "slli %2, %1, 1\n\t"
+ "or %2, %2, %3\n\t"
+ "srli %3, %1, %7\n\t"
+ /* Store value[i] */
+ STOREN " %2, (%0)\n\t"
+ /* Loop */
+ "addi %0, %0, %6\n\t"
+ "bne %0, %5, 1b\n\t"
+ : "=&r" ( discard_value ),
+ "=&r" ( discard_value_i ),
+ "=&r" ( discard_temp ),
+ "=&r" ( carry ),
+ "+m" ( *value )
+ : "r" ( valueN ),
+ "i" ( sizeof ( unsigned long ) ),
+ "i" ( ( 8 * sizeof ( unsigned long ) - 1 ) ),
+ "0" ( value0 ), "3" ( 0 ) );
+ return carry;
+}
+
+/**
+ * Shift big integer right
+ *
+ * @v value0 Element 0 of big integer
+ * @v size Number of elements
+ * @ret out Bit shifted out
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_shr_raw ( unsigned long *value0, unsigned int size ) {
+ bigint_t ( size ) __attribute__ (( may_alias )) *value =
+ ( ( void * ) value0 );
+ unsigned long *valueN = ( value0 + size );
+ unsigned long *discard_value;
+ unsigned long discard_value_i;
+ unsigned long discard_temp;
+ unsigned long carry;
+
+ __asm__ __volatile__ ( "\n1:\n\t"
+ /* Load value[i] */
+ LOADN " %1, %6(%0)\n\t"
+ /* Shift right */
+ "srli %2, %1, 1\n\t"
+ "or %2, %2, %3\n\t"
+ "slli %3, %1, %7\n\t"
+ /* Store value[i] */
+ STOREN " %2, %6(%0)\n\t"
+ /* Loop */
+ "addi %0, %0, %6\n\t"
+ "bne %0, %5, 1b\n\t"
+ : "=&r" ( discard_value ),
+ "=&r" ( discard_value_i ),
+ "=&r" ( discard_temp ),
+ "=&r" ( carry ),
+ "+m" ( *value )
+ : "r" ( value0 ),
+ "i" ( -( sizeof ( unsigned long ) ) ),
+ "i" ( ( 8 * sizeof ( unsigned long ) - 1 ) ),
+ "0" ( valueN ), "3" ( 0 ) );
+ return ( !! carry );
+}
+
+/**
+ * Test if big integer is equal to zero
+ *
+ * @v value0 Element 0 of big integer
+ * @v size Number of elements
+ * @ret is_zero Big integer is equal to zero
+ */
+static inline __attribute__ (( always_inline, pure )) int
+bigint_is_zero_raw ( const unsigned long *value0, unsigned int size ) {
+ const unsigned long *value = value0;
+ unsigned long value_i;
+
+ do {
+ value_i = *(value++);
+ if ( value_i )
+ break;
+ } while ( --size );
+
+ return ( value_i == 0 );
+}
+
+/**
+ * Compare big integers
+ *
+ * @v value0 Element 0 of big integer
+ * @v reference0 Element 0 of reference big integer
+ * @v size Number of elements
+ * @ret geq Big integer is greater than or equal to the reference
+ */
+static inline __attribute__ (( always_inline, pure )) int
+bigint_is_geq_raw ( const unsigned long *value0,
+ const unsigned long *reference0, unsigned int size ) {
+ const unsigned long *value = ( value0 + size );
+ const unsigned long *reference = ( reference0 + size );
+ unsigned long value_i;
+ unsigned long reference_i;
+
+ do {
+ value_i = *(--value);
+ reference_i = *(--reference);
+ if ( value_i != reference_i )
+ break;
+ } while ( --size );
+
+ return ( value_i >= reference_i );
+}
+
+/**
+ * Find highest bit set in big integer
+ *
+ * @v value0 Element 0 of big integer
+ * @v size Number of elements
+ * @ret max_bit Highest bit set + 1 (or 0 if no bits set)
+ */
+static inline __attribute__ (( always_inline )) int
+bigint_max_set_bit_raw ( const unsigned long *value0, unsigned int size ) {
+ const unsigned long *value = ( value0 + size );
+ int max_bit = ( 8 * sizeof ( bigint_t ( size ) ) );
+ unsigned long value_i;
+
+ do {
+ value_i = *(--value);
+ max_bit -= ( ( 8 * sizeof ( *value0 ) ) - fls ( value_i ) );
+ if ( value_i )
+ break;
+ } while ( --size );
+
+ return max_bit;
+}
+
+/**
+ * Grow big integer
+ *
+ * @v source0 Element 0 of source big integer
+ * @v source_size Number of elements in source big integer
+ * @v dest0 Element 0 of destination big integer
+ * @v dest_size Number of elements in destination big integer
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_grow_raw ( const unsigned long *source0, unsigned int source_size,
+ unsigned long *dest0, unsigned int dest_size ) {
+ unsigned int pad_size = ( dest_size - source_size );
+
+ memcpy ( dest0, source0, sizeof ( bigint_t ( source_size ) ) );
+ memset ( ( dest0 + source_size ), 0, sizeof ( bigint_t ( pad_size ) ) );
+}
+
+/**
+ * Shrink big integer
+ *
+ * @v source0 Element 0 of source big integer
+ * @v source_size Number of elements in source big integer
+ * @v dest0 Element 0 of destination big integer
+ * @v dest_size Number of elements in destination big integer
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_shrink_raw ( const unsigned long *source0,
+ unsigned int source_size __unused,
+ unsigned long *dest0, unsigned int dest_size ) {
+
+ memcpy ( dest0, source0, sizeof ( bigint_t ( dest_size ) ) );
+}
+
+/**
+ * Finalise big integer
+ *
+ * @v value0 Element 0 of big integer to finalise
+ * @v size Number of elements
+ * @v out Output buffer
+ * @v len Length of output buffer
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_done_raw ( const unsigned long *value0, unsigned int size __unused,
+ void *out, size_t len ) {
+ const uint8_t *value_byte = ( ( const void * ) value0 );
+ uint8_t *out_byte = ( out + len );
+
+ /* Copy raw data in reverse order */
+ while ( len-- )
+ *(--out_byte) = *(value_byte++);
+}
+
+/**
+ * Multiply big integer elements
+ *
+ * @v multiplicand Multiplicand element
+ * @v multiplier Multiplier element
+ * @v result Result element
+ * @v carry Carry element
+ */
+static inline __attribute__ (( always_inline )) void
+bigint_multiply_one ( const unsigned long multiplicand,
+ const unsigned long multiplier,
+ unsigned long *result, unsigned long *carry ) {
+ unsigned long discard_low;
+ unsigned long discard_high;
+ unsigned long discard_carry;
+
+ __asm__ __volatile__ ( /* Perform multiplication */
+ "mulhu %1, %5, %6\n\t"
+ "mul %0, %5, %6\n\t"
+ /* Accumulate low half */
+ "add %3, %3, %0\n\t"
+ "sltu %2, %3, %0\n\t"
+ "add %1, %1, %2\n\t"
+ /* Accumulate carry (cannot overflow) */
+ "add %3, %3, %4\n\t"
+ "sltu %2, %3, %4\n\t"
+ "add %4, %1, %2\n\t"
+ : "=r" ( discard_low ),
+ "=&r" ( discard_high ),
+ "=r" ( discard_carry ),
+ "+r" ( *result ),
+ "+r" ( *carry )
+ : "r" ( multiplicand ),
+ "r" ( multiplier ) );
+}
+
+#endif /* _BITS_BIGINT_H */
diff --git a/src/arch/riscv/include/bits/bitops.h b/src/arch/riscv/include/bits/bitops.h
new file mode 100644
index 000000000..2019db99a
--- /dev/null
+++ b/src/arch/riscv/include/bits/bitops.h
@@ -0,0 +1,82 @@
+#ifndef _BITS_BITOPS_H
+#define _BITS_BITOPS_H
+
+/** @file
+ *
+ * RISC-V bit operations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/**
+ * Test and set bit atomically
+ *
+ * @v bit Bit to set
+ * @v bits Bit field
+ * @ret old Old value of bit (zero or non-zero)
+ */
+static inline __attribute__ (( always_inline )) int
+test_and_set_bit ( unsigned int bit, volatile void *bits ) {
+ unsigned int index = ( bit / 32 );
+ unsigned int offset = ( bit % 32 );
+ volatile uint32_t *word = ( ( ( volatile uint32_t * ) bits ) + index );
+ uint32_t mask = ( 1U << offset );
+ uint32_t old;
+
+ __asm__ __volatile__ ( "amoor.w %0, %2, %1"
+ : "=r" ( old ), "+A" ( *word )
+ : "r" ( mask ) );
+
+ return ( !! ( old & mask ) );
+}
+
+/**
+ * Test and clear bit atomically
+ *
+ * @v bit Bit to set
+ * @v bits Bit field
+ * @ret old Old value of bit (zero or non-zero)
+ */
+static inline __attribute__ (( always_inline )) int
+test_and_clear_bit ( unsigned int bit, volatile void *bits ) {
+ unsigned int index = ( bit / 32 );
+ unsigned int offset = ( bit % 32 );
+ volatile uint32_t *word = ( ( ( volatile uint32_t * ) bits ) + index );
+ uint32_t mask = ( 1U << offset );
+ uint32_t old;
+
+ __asm__ __volatile__ ( "amoand.w %0, %2, %1"
+ : "=r" ( old ), "+A" ( *word )
+ : "r" ( ~mask ) );
+
+ return ( !! ( old & mask ) );
+}
+
+/**
+ * Set bit atomically
+ *
+ * @v bit Bit to set
+ * @v bits Bit field
+ */
+static inline __attribute__ (( always_inline )) void
+set_bit ( unsigned int bit, volatile void *bits ) {
+
+ test_and_set_bit ( bit, bits );
+}
+
+/**
+ * Clear bit atomically
+ *
+ * @v bit Bit to set
+ * @v bits Bit field
+ */
+static inline __attribute__ (( always_inline )) void
+clear_bit ( unsigned int bit, volatile void *bits ) {
+
+ test_and_clear_bit ( bit, bits );
+}
+
+#endif /* _BITS_BITOPS_H */
diff --git a/src/arch/riscv/include/bits/byteswap.h b/src/arch/riscv/include/bits/byteswap.h
new file mode 100644
index 000000000..56d03f64e
--- /dev/null
+++ b/src/arch/riscv/include/bits/byteswap.h
@@ -0,0 +1,48 @@
+#ifndef _BITS_BYTESWAP_H
+#define _BITS_BYTESWAP_H
+
+/** @file
+ *
+ * Byte-order swapping functions
+ *
+ */
+
+#include <stdint.h>
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern __asmcall uint64_t riscv_swap_word ( uint64_t x );
+extern __asmcall unsigned long riscv_swap_half ( unsigned long x );
+extern __asmcall unsigned long riscv_swap_byte ( unsigned long x );
+
+static inline __attribute__ (( always_inline, const )) uint16_t
+__bswap_variable_16 ( uint16_t x ) {
+ return riscv_swap_byte ( x );
+}
+
+static inline __attribute__ (( always_inline )) void
+__bswap_16s ( uint16_t *x ) {
+ *x = riscv_swap_byte ( *x );
+}
+
+static inline __attribute__ (( always_inline, const )) uint32_t
+__bswap_variable_32 ( uint32_t x ) {
+ return riscv_swap_half ( x );
+}
+
+static inline __attribute__ (( always_inline )) void
+__bswap_32s ( uint32_t *x ) {
+ *x = riscv_swap_half ( *x );
+}
+
+static inline __attribute__ (( always_inline, const )) uint64_t
+__bswap_variable_64 ( uint64_t x ) {
+ return riscv_swap_word ( x );
+}
+
+static inline __attribute__ (( always_inline )) void
+__bswap_64s ( uint64_t *x ) {
+ *x = riscv_swap_word ( *x );
+}
+
+#endif /* _BITS_BYTESWAP_H */
diff --git a/src/arch/riscv/include/bits/compiler.h b/src/arch/riscv/include/bits/compiler.h
new file mode 100644
index 000000000..624a16108
--- /dev/null
+++ b/src/arch/riscv/include/bits/compiler.h
@@ -0,0 +1,40 @@
+#ifndef _BITS_COMPILER_H
+#define _BITS_COMPILER_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** Dummy relocation type */
+#define RELOC_TYPE_NONE R_RISCV_NONE
+
+/* Determine load/store instructions for natural bit width */
+#if __riscv_xlen == 128
+#define NATURAL_SUFFIX q
+#elif __riscv_xlen == 64
+#define NATURAL_SUFFIX d
+#elif __riscv_xlen == 32
+#define NATURAL_SUFFIX w
+#else
+#error "Unsupported bit width"
+#endif
+#ifdef ASSEMBLY
+#define LOADN _C2 ( L, NATURAL_SUFFIX )
+#define STOREN _C2 ( S, NATURAL_SUFFIX )
+#else
+#define LOADN "L" _S2 ( NATURAL_SUFFIX )
+#define STOREN "S" _S2 ( NATURAL_SUFFIX )
+#endif
+
+#ifndef ASSEMBLY
+
+/** Unprefixed constant operand modifier */
+#define ASM_NO_PREFIX ""
+
+/** Declare a function with standard calling conventions */
+#define __asmcall
+
+/** Declare a function with libgcc implicit linkage */
+#define __libgcc
+
+#endif /* ASSEMBLY */
+
+#endif /* _BITS_COMPILER_H */
diff --git a/src/arch/riscv/include/bits/dma.h b/src/arch/riscv/include/bits/dma.h
new file mode 100644
index 000000000..f7decd14c
--- /dev/null
+++ b/src/arch/riscv/include/bits/dma.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_DMA_H
+#define _BITS_DMA_H
+
+/** @file
+ *
+ * RISCV-specific DMA API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/riscv_dma.h>
+
+#endif /* _BITS_DMA_H */
diff --git a/src/arch/riscv/include/bits/endian.h b/src/arch/riscv/include/bits/endian.h
new file mode 100644
index 000000000..85718cfdd
--- /dev/null
+++ b/src/arch/riscv/include/bits/endian.h
@@ -0,0 +1,8 @@
+#ifndef _BITS_ENDIAN_H
+#define _BITS_ENDIAN_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#define __BYTE_ORDER __LITTLE_ENDIAN
+
+#endif /* _BITS_ENDIAN_H */
diff --git a/src/arch/riscv/include/bits/errfile.h b/src/arch/riscv/include/bits/errfile.h
new file mode 100644
index 000000000..bdd2927a4
--- /dev/null
+++ b/src/arch/riscv/include/bits/errfile.h
@@ -0,0 +1,24 @@
+#ifndef _BITS_ERRFILE_H
+#define _BITS_ERRFILE_H
+
+/** @file
+ *
+ * RISC-V error file identifiers
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @addtogroup errfile Error file identifiers
+ * @{
+ */
+
+#define ERRFILE_sbi_reboot ( ERRFILE_ARCH | ERRFILE_CORE | 0x00000000 )
+#define ERRFILE_hart ( ERRFILE_ARCH | ERRFILE_CORE | 0x00010000 )
+#define ERRFILE_zicntr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00020000 )
+#define ERRFILE_zkr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00030000 )
+
+/** @} */
+
+#endif /* _BITS_ERRFILE_H */
diff --git a/src/arch/riscv/include/bits/io.h b/src/arch/riscv/include/bits/io.h
new file mode 100644
index 000000000..4296e318a
--- /dev/null
+++ b/src/arch/riscv/include/bits/io.h
@@ -0,0 +1,17 @@
+#ifndef _BITS_IO_H
+#define _BITS_IO_H
+
+/** @file
+ *
+ * RISCV-specific I/O API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** Page shift */
+#define PAGE_SHIFT 12
+
+#include <ipxe/riscv_io.h>
+
+#endif /* _BITS_IO_H */
diff --git a/src/arch/riscv/include/bits/iomap.h b/src/arch/riscv/include/bits/iomap.h
new file mode 100644
index 000000000..fd8e37825
--- /dev/null
+++ b/src/arch/riscv/include/bits/iomap.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_IOMAP_H
+#define _BITS_IOMAP_H
+
+/** @file
+ *
+ * RISCV-specific I/O mapping API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/svpage.h>
+
+#endif /* _BITS_IOMAP_H */
diff --git a/src/arch/riscv/include/bits/lkrn.h b/src/arch/riscv/include/bits/lkrn.h
new file mode 100644
index 000000000..d26108647
--- /dev/null
+++ b/src/arch/riscv/include/bits/lkrn.h
@@ -0,0 +1,34 @@
+#ifndef _BITS_LKRN_H
+#define _BITS_LKRN_H
+
+/** @file
+ *
+ * Linux kernel image invocation
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/hart.h>
+
+/** Header magic value */
+#define LKRN_MAGIC_ARCH LKRN_MAGIC_RISCV
+
+/**
+ * Jump to kernel entry point
+ *
+ * @v entry Kernel entry point
+ * @v fdt Device tree
+ */
+static inline __attribute__ (( noreturn )) void
+lkrn_jump ( physaddr_t entry, physaddr_t fdt ) {
+ register unsigned long a0 asm ( "a0" ) = boot_hart;
+ register unsigned long a1 asm ( "a1" ) = fdt;
+
+ __asm__ __volatile__ ( "call disable_paging\n\t"
+ "jr %2\n\t"
+ : : "r" ( a0 ), "r" ( a1 ), "r" ( entry ) );
+ __builtin_unreachable();
+}
+
+#endif /* _BITS_LKRN_H */
diff --git a/src/arch/riscv/include/bits/nap.h b/src/arch/riscv/include/bits/nap.h
new file mode 100644
index 000000000..331399f46
--- /dev/null
+++ b/src/arch/riscv/include/bits/nap.h
@@ -0,0 +1,20 @@
+#ifndef _BITS_NAP_H
+#define _BITS_NAP_H
+
+/** @file
+ *
+ * RISCV-specific CPU sleeping API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * Sleep until next CPU interrupt
+ *
+ */
+static inline __attribute__ (( always_inline )) void cpu_halt ( void ) {
+ __asm__ __volatile__ ( "wfi" );
+}
+
+#endif /* _BITS_NAP_H */
diff --git a/src/arch/riscv/include/bits/profile.h b/src/arch/riscv/include/bits/profile.h
new file mode 100644
index 000000000..e9e003dab
--- /dev/null
+++ b/src/arch/riscv/include/bits/profile.h
@@ -0,0 +1,28 @@
+#ifndef _BITS_PROFILE_H
+#define _BITS_PROFILE_H
+
+/** @file
+ *
+ * Profiling
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/**
+ * Get profiling timestamp
+ *
+ * @ret timestamp Timestamp
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+profile_timestamp ( void ) {
+ unsigned long cycles;
+
+ /* Read timestamp counter */
+ __asm__ __volatile__ ( "rdcycle %0" : "=r" ( cycles ) );
+ return cycles;
+}
+
+#endif /* _BITS_PROFILE_H */
diff --git a/src/arch/riscv/include/bits/reboot.h b/src/arch/riscv/include/bits/reboot.h
new file mode 100644
index 000000000..01272483b
--- /dev/null
+++ b/src/arch/riscv/include/bits/reboot.h
@@ -0,0 +1,14 @@
+#ifndef _BITS_REBOOT_H
+#define _BITS_REBOOT_H
+
+/** @file
+ *
+ * RISCV-specific reboot API implementations
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/sbi_reboot.h>
+
+#endif /* _BITS_REBOOT_H */
diff --git a/src/arch/riscv/include/bits/setjmp.h b/src/arch/riscv/include/bits/setjmp.h
new file mode 100644
index 000000000..5186fadaf
--- /dev/null
+++ b/src/arch/riscv/include/bits/setjmp.h
@@ -0,0 +1,16 @@
+#ifndef _BITS_SETJMP_H
+#define _BITS_SETJMP_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** A jump buffer */
+typedef struct {
+ /** Return address (ra) */
+ unsigned long ra;
+ /** Stack pointer (sp) */
+ unsigned long sp;
+ /** Callee-saved registers (s0-s11) */
+ unsigned long s[12];
+} jmp_buf[1];
+
+#endif /* _BITS_SETJMP_H */
diff --git a/src/arch/riscv/include/bits/stdint.h b/src/arch/riscv/include/bits/stdint.h
new file mode 100644
index 000000000..fe1f9946a
--- /dev/null
+++ b/src/arch/riscv/include/bits/stdint.h
@@ -0,0 +1,23 @@
+#ifndef _BITS_STDINT_H
+#define _BITS_STDINT_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+typedef __SIZE_TYPE__ size_t;
+typedef signed long ssize_t;
+typedef signed long off_t;
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long uint64_t;
+
+typedef signed char int8_t;
+typedef signed short int16_t;
+typedef signed int int32_t;
+typedef signed long long int64_t;
+
+typedef unsigned long physaddr_t;
+typedef unsigned long intptr_t;
+
+#endif /* _BITS_STDINT_H */
diff --git a/src/arch/riscv/include/bits/string.h b/src/arch/riscv/include/bits/string.h
new file mode 100644
index 000000000..87834d91a
--- /dev/null
+++ b/src/arch/riscv/include/bits/string.h
@@ -0,0 +1,82 @@
+#ifndef _BITS_STRING_H
+#define _BITS_STRING_H
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * String functions
+ *
+ */
+
+extern void riscv_bzero ( void *dest, size_t len );
+extern void riscv_memset ( void *dest, size_t len, int character );
+extern void riscv_memcpy ( void *dest, const void *src, size_t len );
+extern void riscv_memmove ( void *dest, const void *src, size_t len );
+
+/**
+ * Fill memory region
+ *
+ * @v dest Destination region
+ * @v character Fill character
+ * @v len Length
+ * @ret dest Destination region
+ */
+static inline __attribute__ (( always_inline )) void *
+memset ( void *dest, int character, size_t len ) {
+
+ /* For zeroing larger or non-constant lengths, use the
+ * optimised variable-length zeroing code.
+ */
+ if ( __builtin_constant_p ( character ) && ( character == 0 ) ) {
+ riscv_bzero ( dest, len );
+ return dest;
+ }
+
+ /* Not necessarily zeroing: use basic variable-length code */
+ riscv_memset ( dest, len, character );
+ return dest;
+}
+
+/**
+ * Copy memory region
+ *
+ * @v dest Destination region
+ * @v src Source region
+ * @v len Length
+ * @ret dest Destination region
+ */
+static inline __attribute__ (( always_inline )) void *
+memcpy ( void *dest, const void *src, size_t len ) {
+
+ /* Otherwise, use variable-length code */
+ riscv_memcpy ( dest, src, len );
+ return dest;
+}
+
+/**
+ * Copy (possibly overlapping) memory region
+ *
+ * @v dest Destination region
+ * @v src Source region
+ * @v len Length
+ * @ret dest Destination region
+ */
+static inline __attribute__ (( always_inline )) void *
+memmove ( void *dest, const void *src, size_t len ) {
+ ssize_t offset = ( dest - src );
+
+ /* If direction of copy is known to be forwards at build time,
+ * then use variable-length memcpy().
+ */
+ if ( __builtin_constant_p ( offset ) && ( offset <= 0 ) ) {
+ riscv_memcpy ( dest, src, len );
+ return dest;
+ }
+
+ /* Otherwise, use ambidirectional copy */
+ riscv_memmove ( dest, src, len );
+ return dest;
+}
+
+#endif /* _BITS_STRING_H */
diff --git a/src/arch/riscv/include/bits/strings.h b/src/arch/riscv/include/bits/strings.h
new file mode 100644
index 000000000..dd6d458b2
--- /dev/null
+++ b/src/arch/riscv/include/bits/strings.h
@@ -0,0 +1,91 @@
+#ifndef _BITS_STRINGS_H
+#define _BITS_STRINGS_H
+
+/** @file
+ *
+ * String functions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern __asmcall unsigned long riscv_ffs ( unsigned long value );
+extern __asmcall unsigned long riscv_fls ( unsigned long value );
+
+/**
+ * Find first (i.e. least significant) set bit
+ *
+ * @v value Value
+ * @ret lsb Least significant bit set in value (LSB=1), or zero
+ */
+static inline __attribute__ (( always_inline )) int __ffsl ( long value ) {
+
+ return riscv_ffs ( value );
+}
+
+/**
+ * Find first (i.e. least significant) set bit
+ *
+ * @v value Value
+ * @ret lsb Least significant bit set in value (LSB=1), or zero
+ */
+static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){
+ unsigned long low = value;
+ unsigned long high;
+
+ /* Check machine word size */
+ if ( sizeof ( value ) > sizeof ( low ) ) {
+ /* 32-bit */
+ high = ( value >> 32 );
+ if ( low ) {
+ return ( __ffsl ( low ) );
+ } else if ( high ) {
+ return ( 32 + __ffsl ( high ) );
+ } else {
+ return 0;
+ }
+ } else {
+ /* 64-bit */
+ return ( __ffsl ( low ) );
+ }
+}
+
+/**
+ * Find last (i.e. most significant) set bit
+ *
+ * @v value Value
+ * @ret msb Most significant bit set in value (LSB=1), or zero
+ */
+static inline __attribute__ (( always_inline )) int __flsl ( long value ) {
+
+ return riscv_fls ( value );
+}
+
+/**
+ * Find last (i.e. most significant) set bit
+ *
+ * @v value Value
+ * @ret msb Most significant bit set in value (LSB=1), or zero
+ */
+static inline __attribute__ (( always_inline )) int __flsll ( long long value ){
+ unsigned long low = value;
+ unsigned long high;
+
+ /* Check machine word size */
+ if ( sizeof ( value ) > sizeof ( low ) ) {
+ /* 32-bit */
+ high = ( value >> 32 );
+ if ( high ) {
+ return ( 32 + __flsl ( high ) );
+ } else if ( low ) {
+ return ( __flsl ( low ) );
+ } else {
+ return 0;
+ }
+ } else {
+ /* 64-bit */
+ return ( __flsl ( low ) );
+ }
+}
+
+#endif /* _BITS_STRINGS_H */
diff --git a/src/arch/riscv/include/bits/tcpip.h b/src/arch/riscv/include/bits/tcpip.h
new file mode 100644
index 000000000..0ac55b1a0
--- /dev/null
+++ b/src/arch/riscv/include/bits/tcpip.h
@@ -0,0 +1,15 @@
+#ifndef _BITS_TCPIP_H
+#define _BITS_TCPIP_H
+
+/** @file
+ *
+ * Transport-network layer interface
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern uint16_t tcpip_continue_chksum ( uint16_t partial, const void *data,
+ size_t len );
+
+#endif /* _BITS_TCPIP_H */
diff --git a/src/arch/riscv/include/bits/virt_offset.h b/src/arch/riscv/include/bits/virt_offset.h
new file mode 100644
index 000000000..83ac17551
--- /dev/null
+++ b/src/arch/riscv/include/bits/virt_offset.h
@@ -0,0 +1,33 @@
+#ifndef _BITS_VIRT_OFFSET_H
+#define _BITS_VIRT_OFFSET_H
+
+/** @file
+ *
+ * RISCV-specific virtual address offset
+ *
+ * We use the thread pointer register (tp) to hold the virtual address
+ * offset, so that virtual-to-physical address translations work as
+ * expected even while we are executing directly from read-only memory
+ * (and so cannot store a value in a global virt_offset variable).
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * Read virtual address offset held in thread pointer register
+ *
+ * @ret virt_offset Virtual address offset
+ */
+static inline __attribute__ (( const, always_inline )) unsigned long
+tp_virt_offset ( void ) {
+ register unsigned long tp asm ( "tp" );
+
+ __asm__ ( "" : "=r" ( tp ) );
+ return tp;
+}
+
+/** Always read thread pointer register to get virtual address offset */
+#define virt_offset tp_virt_offset()
+
+#endif /* _BITS_VIRT_OFFSET_H */
diff --git a/src/arch/riscv/include/ipxe/csr.h b/src/arch/riscv/include/ipxe/csr.h
new file mode 100644
index 000000000..c14974472
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/csr.h
@@ -0,0 +1,75 @@
+#ifndef _IPXE_CSR_H
+#define _IPXE_CSR_H
+
+/** @file
+ *
+ * Control and status registers (CSRs)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * Check if CSR can be read
+ *
+ * @v name CSR name
+ * @v allowed CSR can be read
+ */
+#define csr_can_read( name ) ( { \
+ unsigned long stvec_orig; \
+ unsigned long stvec_temp; \
+ unsigned long csr; \
+ int allowed = 0; \
+ \
+ __asm__ __volatile__ ( /* Set temporary trap vector */ \
+ "la %3, 1f\n\t" \
+ "csrrw %2, stvec, %3\n\t" \
+ /* Try reading CSR */ \
+ "csrr %1, " name "\n\t" \
+ /* Mark as allowed if not trapped */ \
+ "addi %0, %0, 1\n\t" \
+ /* Temporary trap vector */ \
+ ".balign 4\n\t" \
+ "\n1:\n\t" \
+ /* Restore original trap vector */ \
+ "csrw stvec, %2\n\t" \
+ : "+r" ( allowed ), \
+ "=r" ( csr ), \
+ "=r" ( stvec_orig ), \
+ "=r" ( stvec_temp ) ); \
+ allowed; \
+ } )
+
+/**
+ * Check if CSR can be written
+ *
+ * @v name CSR name
+ * @v value Value to write
+ * @v allowed CSR can be written
+ */
+#define csr_can_write( name, value ) ( { \
+ unsigned long stvec_orig; \
+ unsigned long stvec_temp; \
+ unsigned long csr = (value); \
+ int allowed = 0; \
+ \
+ __asm__ __volatile__ ( /* Set temporary trap vector */ \
+ "la %3, 1f\n\t" \
+ "csrrw %2, stvec, %3\n\t" \
+ /* Try writing CSR */ \
+ "csrrw %1, " name ", %1\n\t" \
+ /* Mark as allowed if not trapped */ \
+ "addi %0, %0, 1\n\t" \
+ /* Temporary trap vector */ \
+ ".balign 4\n\t" \
+ "\n1:\n\t" \
+ /* Restore original trap vector */ \
+ "csrw stvec, %2\n\t" \
+ : "+r" ( allowed ), \
+ "+r" ( csr ), \
+ "=r" ( stvec_orig ), \
+ "=r" ( stvec_temp ) ); \
+ allowed; \
+ } )
+
+#endif /* _IPXE_CSR_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 000000000..2428183d4
--- /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/hart.h b/src/arch/riscv/include/ipxe/hart.h
new file mode 100644
index 000000000..c201b6c77
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/hart.h
@@ -0,0 +1,16 @@
+#ifndef _IPXE_HART_H
+#define _IPXE_HART_H
+
+/** @file
+ *
+ * Hardware threads (harts)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+extern unsigned long boot_hart;
+
+extern int hart_supported ( const char *ext );
+
+#endif /* _IPXE_HART_H */
diff --git a/src/arch/riscv/include/ipxe/riscv_dma.h b/src/arch/riscv/include/ipxe/riscv_dma.h
new file mode 100644
index 000000000..d35904d88
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/riscv_dma.h
@@ -0,0 +1,45 @@
+#ifndef _IPXE_RISCV_DMA_H
+#define _IPXE_RISCV_DMA_H
+
+/** @file
+ *
+ * iPXE DMA API for RISC-V
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef DMAAPI_RISCV
+#define DMAAPI_PREFIX_riscv
+#else
+#define DMAAPI_PREFIX_riscv __riscv_
+#endif
+
+/**
+ * Set addressable space mask
+ *
+ * @v dma DMA device
+ * @v mask Addressable space mask
+ */
+static inline __always_inline void
+DMAAPI_INLINE ( riscv, dma_set_mask ) ( struct dma_device *dma __unused,
+ physaddr_t mask __unused ) {
+
+ /* Nothing to do */
+}
+
+/**
+ * Get DMA address from virtual address
+ *
+ * @v map DMA mapping
+ * @v addr Address within the mapped region
+ * @ret addr Device-side DMA address
+ */
+static inline __always_inline physaddr_t
+DMAAPI_INLINE ( riscv, dma ) ( struct dma_mapping *map __unused, void *addr ) {
+
+ /* Use physical address as device address */
+ return virt_to_phys ( addr );
+}
+
+#endif /* _IPXE_RISCV_DMA_H */
diff --git a/src/arch/riscv/include/ipxe/riscv_io.h b/src/arch/riscv/include/ipxe/riscv_io.h
new file mode 100644
index 000000000..539dbd7ed
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/riscv_io.h
@@ -0,0 +1,141 @@
+#ifndef _IPXE_RISCV_IO_H
+#define _IPXE_RISCV_IO_H
+
+/** @file
+ *
+ * iPXE I/O API for RISC-V
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef IOAPI_RISCV
+#define IOAPI_PREFIX_riscv
+#else
+#define IOAPI_PREFIX_riscv __riscv_
+#endif
+
+#include <ipxe/dummy_pio.h>
+
+/*
+ * Memory space mappings
+ *
+ */
+
+/*
+ * Physical<->Bus address mappings
+ *
+ */
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( riscv, phys_to_bus ) ( unsigned long phys_addr ) {
+ return phys_addr;
+}
+
+static inline __always_inline unsigned long
+IOAPI_INLINE ( riscv, bus_to_phys ) ( unsigned long bus_addr ) {
+ return bus_addr;
+}
+
+/*
+ * MMIO reads and writes
+ *
+ */
+
+/* Single-register read */
+#define RISCV_READX( _suffix, _type, _insn_suffix ) \
+static inline __always_inline _type \
+IOAPI_INLINE ( riscv, read ## _suffix ) ( volatile _type *io_addr ) { \
+ unsigned long data; \
+ __asm__ __volatile__ ( "fence io, io\n\t" \
+ "l" _insn_suffix " %0, %1\n\t" \
+ : "=r" ( data ) : "m" ( *io_addr ) ); \
+ return data; \
+}
+
+/* Single-register write */
+#define RISCV_WRITEX( _suffix, _type, _insn_suffix) \
+static inline __always_inline void \
+IOAPI_INLINE ( riscv, write ## _suffix ) ( _type data, \
+ volatile _type *io_addr ) { \
+ __asm__ __volatile__ ( "fence io, io\n\t" \
+ "s" _insn_suffix " %0, %1\n\t" \
+ : : "r" ( data ), "m" ( *io_addr ) ); \
+}
+
+/* Double-register hopefully-fused read */
+#define RISCV_READX_FUSED( _suffix, _type, _insn_suffix ) \
+static inline __always_inline _type \
+IOAPI_INLINE ( riscv, read ## _suffix ) ( volatile _type *io_addr ) { \
+ union { \
+ unsigned long half[2]; \
+ _type data; \
+ } u; \
+ __asm__ __volatile__ ( "fence io, io\n\t" \
+ "l" _insn_suffix " %0, 0(%2)\n\t" \
+ "l" _insn_suffix " %1, %3(%2)\n\t" \
+ : "=&r" ( u.half[0] ), \
+ "=&r" ( u.half[1] ) \
+ : "r" ( io_addr ), \
+ "i" ( sizeof ( u.half[0] ) ) ); \
+ return u.data; \
+}
+
+/* Double-register hopefully-fused write */
+#define RISCV_WRITEX_FUSED( _suffix, _type, _insn_suffix ) \
+static inline __always_inline void \
+IOAPI_INLINE ( riscv, write ## _suffix ) ( _type data, \
+ volatile _type *io_addr ) { \
+ union { \
+ unsigned long half[2]; \
+ _type data; \
+ } u = { .data = data }; \
+ __asm__ __volatile__ ( "fence io, io\n\t" \
+ "s" _insn_suffix " %0, 0(%2)\n\t" \
+ "s" _insn_suffix " %1, %3(%2)\n\t" : \
+ : "r" ( u.half[0] ), \
+ "r" ( u.half[1] ), \
+ "r" ( io_addr ), \
+ "i" ( sizeof ( u.half[0] ) ) ); \
+}
+
+RISCV_READX ( b, uint8_t, "bu" );
+RISCV_WRITEX ( b, uint8_t, "b" );
+
+RISCV_READX ( w, uint16_t, "hu" );
+RISCV_WRITEX ( w, uint16_t, "h" );
+
+#if __riscv_xlen > 32
+ RISCV_READX ( l, uint32_t, "wu" );
+ RISCV_WRITEX ( l, uint32_t, "w" );
+#else
+ RISCV_READX ( l, uint32_t, "w" );
+ RISCV_WRITEX ( l, uint32_t, "w" );
+#endif
+
+#if __riscv_xlen >= 64
+ #if __riscv_xlen > 64
+ RISCV_READX ( q, uint64_t, "du" );
+ RISCV_WRITEX ( q, uint64_t, "d" );
+ #else
+ RISCV_READX ( q, uint64_t, "d" );
+ RISCV_WRITEX ( q, uint64_t, "d" );
+ #endif
+#else
+ RISCV_READX_FUSED ( q, uint64_t, "w" );
+ RISCV_WRITEX_FUSED ( q, uint64_t, "w" );
+#endif
+
+/*
+ * Memory barrier
+ *
+ */
+static inline __always_inline void
+IOAPI_INLINE ( riscv, mb ) ( void ) {
+ __asm__ __volatile__ ( "fence" : : : "memory" );
+}
+
+/* Dummy PIO */
+DUMMY_PIO ( riscv );
+
+#endif /* _IPXE_RISCV_IO_H */
diff --git a/src/arch/riscv/include/ipxe/sbi.h b/src/arch/riscv/include/ipxe/sbi.h
new file mode 100644
index 000000000..4364098b9
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/sbi.h
@@ -0,0 +1,213 @@
+#ifndef _IPXE_SBI_H
+#define _IPXE_SBI_H
+
+/** @file
+ *
+ * Supervisor Binary Interface (SBI)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+/** An SBI function return value */
+struct sbi_return {
+ /** Error status (returned in a0) */
+ long error;
+ /** Data value (returned in a1) */
+ long value;
+};
+
+/**
+ * @defgroup sbierrors SBI errors
+ *
+ * *{
+ */
+#define SBI_SUCCESS 0 /**< Completed successfully */
+#define SBI_ERR_FAILED -1 /**< Failed */
+#define SBI_ERR_NOT_SUPPORTED -2 /**< Not supported */
+#define SBI_ERR_INVALID_PARAM -3 /**< Invalid parameter(s) */
+#define SBI_ERR_DENIED -4 /**< Denied or not allowed */
+#define SBI_ERR_INVALID_ADDRESS -5 /**< Invalid address(es) */
+#define SBI_ERR_ALREADY_AVAILABLE -6 /**< Already available */
+#define SBI_ERR_ALREADY_STARTED -7 /**< Already started */
+#define SBI_ERR_ALREADY_STOPPED -8 /**< Already stopped */
+#define SBI_ERR_NO_SHMEM -9 /**< Shared memory not available */
+#define SBI_ERR_INVALID_STATE -10 /**< Invalid state */
+#define SBI_ERR_BAD_RANGE -11 /**< Bad (or invalid) range */
+#define SBI_ERR_TIMEOUT -12 /**< Failed due to timeout */
+#define SBI_ERR_IO -13 /**< Input/output error */
+/** @} */
+
+/** Construct SBI extension ID */
+#define SBI_EID( c1, c2, c3, c4 ) \
+ ( (int) ( ( (c1) << 24 ) | ( (c2) << 16 ) | ( (c3) << 8 ) | (c4) ) )
+
+/**
+ * Call supervisor with no parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_0 ( int eid, int fid ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" );
+ register unsigned long a1 asm ( "a1" );
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "=r" ( a0 ), "=r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with one parameter
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v p0 Parameter 0
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_1 ( int eid, int fid, unsigned long p0 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" );
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "=r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with two parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v p0 Parameter 0
+ * @v p1 Parameter 1
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_2 ( int eid, int fid, unsigned long p0, unsigned long p1 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" ) = p1;
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "+r" ( a1 )
+ : "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with three parameters
+ *
+ * @v eid Extension ID
+ * @v fid Function ID
+ * @v p0 Parameter 0
+ * @v p1 Parameter 1
+ * @v p2 Parameter 2
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) struct sbi_return
+sbi_ecall_3 ( int eid, int fid, unsigned long p0, unsigned long p1,
+ unsigned long p2 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) eid );
+ register unsigned long a6 asm ( "a6" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+ register unsigned long a1 asm ( "a1" ) = p1;
+ register unsigned long a2 asm ( "a2" ) = p2;
+ struct sbi_return ret;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 ), "+r" ( a1 )
+ : "r" ( a2 ), "r" ( a6 ), "r" ( a7 )
+ : "memory" );
+ ret.error = a0;
+ ret.value = a1;
+ return ret;
+}
+
+/**
+ * Call supervisor with no parameters
+ *
+ * @v fid Legacy function ID
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) long
+sbi_legacy_ecall_0 ( int fid ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" );
+
+ __asm__ __volatile__ ( "ecall"
+ : "=r" ( a0 )
+ : "r" ( a7 )
+ : "memory" );
+ return a0;
+}
+
+/**
+ * Call supervisor with one parameter
+ *
+ * @v fid Legacy function ID
+ * @v p0 Parameter 0
+ * @ret ret Return value
+ */
+static inline __attribute__ (( always_inline )) long
+sbi_legacy_ecall_1 ( int fid, unsigned long p0 ) {
+ register unsigned long a7 asm ( "a7" ) = ( ( long ) fid );
+ register unsigned long a0 asm ( "a0" ) = p0;
+
+ __asm__ __volatile__ ( "ecall"
+ : "+r" ( a0 )
+ : "r" ( a7 )
+ : "memory" );
+ return a0;
+}
+
+/** Convert an SBI error code to an iPXE status code */
+#define ESBI( error ) EPLATFORM ( EINFO_EPLATFORM, error )
+
+/** Legacy extensions */
+#define SBI_LEGACY_PUTCHAR 0x01 /**< Console Put Character */
+#define SBI_LEGACY_GETCHAR 0x02 /**< Console Get Character */
+#define SBI_LEGACY_SHUTDOWN 0x08 /**< System Shutdown */
+
+/** Base extension */
+#define SBI_BASE 0x10
+#define SBI_BASE_MVENDORID 0x04 /**< Get machine vendor ID */
+
+/** System reset extension */
+#define SBI_SRST SBI_EID ( 'S', 'R', 'S', 'T' )
+#define SBI_SRST_SYSTEM_RESET 0x00 /**< Reset system */
+#define SBI_RESET_SHUTDOWN 0x00000000 /**< Shutdown */
+#define SBI_RESET_COLD 0x00000001 /**< Cold reboot */
+#define SBI_RESET_WARM 0x00000002 /**< Warm reboot */
+
+/** Debug console extension */
+#define SBI_DBCN SBI_EID ( 'D', 'B', 'C', 'N' )
+#define SBI_DBCN_WRITE 0x00 /**< Console Write */
+#define SBI_DBCN_READ 0x01 /**< Console Read */
+#define SBI_DBCN_WRITE_BYTE 0x02 /**< Console Write Byte */
+
+#endif /* _IPXE_SBI_H */
diff --git a/src/arch/riscv/include/ipxe/sbi_reboot.h b/src/arch/riscv/include/ipxe/sbi_reboot.h
new file mode 100644
index 000000000..e8d6e82bf
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/sbi_reboot.h
@@ -0,0 +1,18 @@
+#ifndef _IPXE_BIOS_REBOOT_H
+#define _IPXE_BIOS_REBOOT_H
+
+/** @file
+ *
+ * Supervisor Binary Interface (SBI) reboot mechanism
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef REBOOT_SBI
+#define REBOOT_PREFIX_sbi
+#else
+#define REBOOT_PREFIX_sbi __sbi_
+#endif
+
+#endif /* _IPXE_BIOS_REBOOT_H */
diff --git a/src/arch/riscv/include/ipxe/svpage.h b/src/arch/riscv/include/ipxe/svpage.h
new file mode 100644
index 000000000..897a3379a
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/svpage.h
@@ -0,0 +1,28 @@
+#ifndef _IPXE_SVPAGE_H
+#define _IPXE_SVPAGE_H
+
+/** @file
+ *
+ * Supervisor page table management
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+#ifdef IOMAP_SVPAGE
+#define IOMAP_PREFIX_svpage
+#else
+#define IOMAP_PREFIX_svpage __svpage_
+#endif
+
+static inline __always_inline unsigned long
+IOMAP_INLINE ( svpage, io_to_bus ) ( volatile const void *io_addr ) {
+ /* Not easy to do; just return the CPU address for debugging purposes */
+ return ( ( intptr_t ) io_addr );
+}
+
+extern void * svpage_dma32 ( void );
+
+#endif /* _IPXE_SVPAGE_H */
diff --git a/src/arch/riscv/include/ipxe/xthead.h b/src/arch/riscv/include/ipxe/xthead.h
new file mode 100644
index 000000000..d0c9449ef
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/xthead.h
@@ -0,0 +1,21 @@
+#ifndef _IPXE_XTHEAD_H
+#define _IPXE_XTHEAD_H
+
+/** @file
+ *
+ * T-Head vendor extensions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** T-Head machine vendor ID */
+#define THEAD_MVENDORID 0x5b7
+
+/** T-Head SXSTATUS CSR */
+#define THEAD_SXSTATUS 0x5c0
+#define THEAD_SXSTATUS_THEADISAEE 0x00400000 /**< General ISA extensions */
+
+extern int xthead_supported ( unsigned long feature );
+
+#endif /* _IPXE_XTHEAD_H */
diff --git a/src/arch/riscv/include/ipxe/zicbom.h b/src/arch/riscv/include/ipxe/zicbom.h
new file mode 100644
index 000000000..4ba165f3c
--- /dev/null
+++ b/src/arch/riscv/include/ipxe/zicbom.h
@@ -0,0 +1,17 @@
+#ifndef _IPXE_ZICBOM_H
+#define _IPXE_ZICBOM_H
+
+/** @file
+ *
+ * Cache-block management operations (Zicbom)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+
+extern void cache_clean ( const void *start, size_t len );
+extern void cache_invalidate ( void *start, size_t len );
+
+#endif /* _IPXE_ZICBOM_H */