Merge branch 'riscv/sbi' into 'master'
Mainly riscv stuff, but also limits.h and pseudo random numbers
See merge request kvm-unit-tests/kvm-unit-tests!65
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cae3e74..67a9a15 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -133,18 +133,44 @@
| tee results.txt
- if grep -q FAIL results.txt ; then exit 1 ; fi
-# build-riscv32:
-# Fedora doesn't package a riscv32 compiler for QEMU. Oh, well.
+build-riscv32:
+ extends: .outoftree_template
+ script:
+ - dnf install -y qemu-system-riscv gcc-riscv64-linux-gnu
+ - mkdir build
+ - cd build
+ - ../configure --arch=riscv32 --cross-prefix=riscv64-linux-gnu-
+ - make -j2
+ - printf "FOO=foo\nBAR=bar\nBAZ=baz\nMVENDORID=0\nMARCHID=0\nMIMPID=0\n" >test-env
+ - ACCEL=tcg KVM_UNIT_TESTS_ENV=test-env ./run_tests.sh
+ selftest
+ sbi
+ | tee results.txt
+ - grep -q PASS results.txt && ! grep -q FAIL results.txt
-# Select 'rv64' with PROCESSOR_OVERRIDE in case QEMU is too old to have 'max'
build-riscv64:
extends: .intree_template
script:
- dnf install -y qemu-system-riscv gcc-riscv64-linux-gnu
- ./configure --arch=riscv64 --cross-prefix=riscv64-linux-gnu-
- make -j2
- - printf "FOO=foo\nBAR=bar\nBAZ=baz\nMVENDORID=0\n" >test-env
- - PROCESSOR_OVERRIDE=rv64 ACCEL=tcg KVM_UNIT_TESTS_ENV=test-env ./run_tests.sh
+ - printf "FOO=foo\nBAR=bar\nBAZ=baz\nMVENDORID=0\nMARCHID=0\nMIMPID=0\n" >test-env
+ - ACCEL=tcg KVM_UNIT_TESTS_ENV=test-env ./run_tests.sh
+ selftest
+ sbi
+ | tee results.txt
+ - grep -q PASS results.txt && ! grep -q FAIL results.txt
+
+build-riscv64-efi:
+ extends: .intree_template
+ script:
+ - dnf install -y edk2-riscv64 qemu-system-riscv gcc-riscv64-linux-gnu
+ - cp /usr/share/edk2/riscv/RISCV_VIRT_CODE.fd .
+ - truncate -s 32M RISCV_VIRT_CODE.fd
+ - ./configure --arch=riscv64 --cross-prefix=riscv64-linux-gnu- --enable-efi
+ - make -j2
+ - printf "FOO=foo\nBAR=bar\nBAZ=baz\nMVENDORID=0\nMARCHID=0\nMIMPID=0\n" >test-env
+ - ACCEL=tcg KVM_UNIT_TESTS_ENV=test-env ./run_tests.sh
selftest
sbi
| tee results.txt
diff --git a/Makefile b/Makefile
index 5b7998b..3d51cb7 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,7 @@
lib/printf.o \
lib/string.o \
lib/abort.o \
+ lib/rand.o \
lib/report.o \
lib/stack.o
diff --git a/configure b/configure
index db15e85..27ae9cc 100755
--- a/configure
+++ b/configure
@@ -418,12 +418,16 @@
asm="asm-generic"
if [ -d "$srcdir/lib/$arch/asm" ]; then
asm="$srcdir/lib/$arch/asm"
+ mkdir -p "lib/$arch"
+elif [ -d "$srcdir/lib/$arch_libdir/asm" ]; then
+ asm="$srcdir/lib/$arch_libdir/asm"
+ mkdir -p "lib/$arch_libdir"
elif [ -d "$srcdir/lib/$testdir/asm" ]; then
asm="$srcdir/lib/$testdir/asm"
+ mkdir -p "lib/$testdir"
fi
-mkdir -p lib
ln -sf "$asm" lib/asm
-
+mkdir -p lib/generated lib/libfdt
# create the config
cat <<EOF > config.mak
diff --git a/lib/limits.h b/lib/limits.h
new file mode 100644
index 0000000..650085c
--- /dev/null
+++ b/lib/limits.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _LIMITS_H_
+#define _LIMITS_H_
+
+#if __CHAR_BIT__ == 8
+# if __CHAR_UNSIGNED__
+# define CHAR_MIN 0
+# define CHAR_MAX __UINT8_MAX__
+# else
+# define CHAR_MAX __INT8_MAX__
+# define CHAR_MIN (-CHAR_MAX - 1)
+# endif
+#endif
+
+#if __SHRT_WIDTH__ == 16
+# define SHRT_MAX __INT16_MAX__
+# define SHRT_MIN (-SHRT_MAX - 1)
+# define USHRT_MAX __UINT16_MAX__
+#endif
+
+#if __INT_WIDTH__ == 32
+# define INT_MAX __INT32_MAX__
+# define INT_MIN (-INT_MAX - 1)
+# define UINT_MAX __UINT32_MAX__
+#endif
+
+#if __LONG_WIDTH__ == 64
+# define LONG_MAX __INT64_MAX__
+# define LONG_MIN (-LONG_MAX - 1)
+# define ULONG_MAX __UINT64_MAX__
+#elif __LONG_WIDTH__ == 32
+# define LONG_MAX __INT32_MAX__
+# define LONG_MIN (-LONG_MAX - 1)
+# define ULONG_MAX __UINT32_MAX__
+#endif
+
+#if __LONG_LONG_WIDTH__ == 64
+# define LLONG_MAX __INT64_MAX__
+# define LLONG_MIN (-LLONG_MAX - 1)
+# define ULLONG_MAX __UINT64_MAX__
+#endif
+
+#endif /* _LIMITS_H_ */
diff --git a/lib/memregions.h b/lib/memregions.h
index 1600530..04027f6 100644
--- a/lib/memregions.h
+++ b/lib/memregions.h
@@ -10,6 +10,7 @@
#define MR_F_CODE BIT(1)
#define MR_F_RESERVED BIT(2)
#define MR_F_PERSISTENT BIT(3)
+#define MR_F_UNUSED BIT(4)
#define MR_F_UNKNOWN BIT(31)
struct mem_region {
diff --git a/lib/rand.c b/lib/rand.c
new file mode 100644
index 0000000..78032b8
--- /dev/null
+++ b/lib/rand.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * (pseudo) random functions
+ * Currently uses SHA-256 to scramble the PRNG state.
+ *
+ * Copyright IBM Corp. 2024
+ */
+
+#include "libcflat.h"
+#include "rand.h"
+#include <string.h>
+
+/* Begin SHA-256 related definitions */
+
+#define INITAL_HASH { \
+ 0x6a09e667, \
+ 0xbb67ae85, \
+ 0x3c6ef372, \
+ 0xa54ff53a, \
+ 0x510e527f, \
+ 0x9b05688c, \
+ 0x1f83d9ab, \
+ 0x5be0cd19, \
+}
+
+static const uint32_t K[] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
+};
+
+static inline uint32_t ch(uint32_t x, uint32_t y, uint32_t z)
+{
+ return (x & y) ^ ((~x) & z);
+}
+
+static inline uint32_t maj(uint32_t x, uint32_t y, uint32_t z)
+{
+ return (x & y) ^ (x & z) ^ (y & z);
+}
+
+static inline uint32_t rot(uint32_t value, unsigned int count)
+{
+ return value >> count | value << (32 - count);
+}
+
+static inline uint32_t upper_sig0(uint32_t x)
+{
+ return rot(x, 2) ^ rot(x, 13) ^ rot(x, 22);
+}
+
+static inline uint32_t upper_sig1(uint32_t x)
+{
+ return rot(x, 6) ^ rot(x, 11) ^ rot(x, 25);
+}
+
+static inline uint32_t lower_sig0(uint32_t x)
+{
+ return rot(x, 7) ^ rot(x, 18) ^ (x >> 3);
+}
+
+static inline uint32_t lower_sig1(uint32_t x)
+{
+ return rot(x, 17) ^ rot(x, 19) ^ (x >> 10);
+}
+
+enum alphabet { A, B, C, D, E, F, G, H, };
+
+static void sha256_chunk(const uint32_t (*chunk)[16], uint32_t (*hash)[8])
+{
+ uint32_t w[64];
+ uint32_t w_hash[8];
+
+ memcpy(w, chunk, sizeof(*chunk));
+
+ for (int i = 16; i < 64; i++)
+ w[i] = lower_sig1(w[i - 2]) + w[i - 7] + lower_sig0(w[i - 15]) + w[i - 16];
+
+ memcpy(w_hash, hash, sizeof(*hash));
+
+ for (int i = 0; i < 64; i++) {
+ uint32_t t1, t2;
+
+ t1 = w_hash[H] +
+ upper_sig1(w_hash[E]) +
+ ch(w_hash[E], w_hash[F], w_hash[G]) +
+ K[i] +
+ w[i];
+
+ t2 = upper_sig0(w_hash[A]) + maj(w_hash[A], w_hash[B], w_hash[C]);
+
+ w_hash[H] = w_hash[G];
+ w_hash[G] = w_hash[F];
+ w_hash[F] = w_hash[E];
+ w_hash[E] = w_hash[D] + t1;
+ w_hash[D] = w_hash[C];
+ w_hash[C] = w_hash[B];
+ w_hash[B] = w_hash[A];
+ w_hash[A] = t1 + t2;
+ }
+
+ for (int i = 0; i < 8; i++)
+ (*hash)[i] += w_hash[i];
+}
+
+/**
+ * sha256_hash - Calculate SHA-256 of input. Only a limited subset of inputs supported.
+ * @n: Number of words to hash, must be <= 13
+ * @input: Input data to hash
+ * @hash: Output hash as a word array, ordered such that the first word contains
+ * the first/leftmost bits of the 256 bit hash
+ *
+ * Calculate the SHA-256 hash of the input where the input must be a multiple of
+ * 4 bytes and at most 52 long. The input is used without any adjustment, so,
+ * should the caller want to hash bytes it needs to interpret the bytes in the
+ * ordering as defined by the specification, that is big endian.
+ * The same applies to interpreting the output array as bytes.
+ * The function computes the same as: printf "%08x" ${input[@]} | xxd -r -p | sha256sum .
+ */
+static void sha256_hash(unsigned int n, const uint32_t (*input)[n], uint32_t (*hash)[8])
+{
+ /*
+ * Pad according to SHA-2 specification.
+ * First set up length in bits.
+ */
+ uint32_t chunk[16] = {
+ [15] = sizeof(*input) * 8,
+ };
+
+ memcpy(chunk, input, sizeof(*input));
+ /* Then add separator */
+ chunk[n] = 1 << 31;
+ memcpy(hash, (uint32_t[])INITAL_HASH, sizeof(*hash));
+ sha256_chunk(&chunk, hash);
+}
+
+/* End SHA-256 related definitions */
+
+prng_state prng_init(uint64_t seed)
+{
+ prng_state state = { .next_word = 0 };
+ uint32_t seed_arr[2] = { seed >> 32, seed };
+
+ sha256_hash(ARRAY_SIZE(seed_arr), &seed_arr, &state.hash);
+ return state;
+}
+
+static void prng_scramble(prng_state *state)
+{
+ uint32_t input[8];
+
+ memcpy(input, state->hash, sizeof(state->hash));
+ sha256_hash(ARRAY_SIZE(input), &input, &state->hash);
+ state->next_word = 0;
+}
+
+uint32_t prng32(prng_state *state)
+{
+ if (state->next_word < ARRAY_SIZE(state->hash))
+ return state->hash[state->next_word++];
+
+ prng_scramble(state);
+ return prng32(state);
+}
+
+uint64_t prng64(prng_state *state)
+{
+ /* explicitly evaluate the high word first */
+ uint64_t high = prng32(state);
+
+ return high << 32 | prng32(state);
+}
diff --git a/lib/rand.h b/lib/rand.h
new file mode 100644
index 0000000..cdce8bd
--- /dev/null
+++ b/lib/rand.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * (pseudo) random functions
+ *
+ * Copyright IBM Corp. 2024
+ */
+#ifndef _RAND_H_
+#define _RAND_H_
+
+#include <stdint.h>
+
+/* Non cryptographically secure PRNG */
+typedef struct {
+ uint32_t hash[8];
+ uint8_t next_word;
+} prng_state;
+prng_state prng_init(uint64_t seed);
+uint32_t prng32(prng_state *state);
+uint64_t prng64(prng_state *state);
+
+#endif /* _RAND_H_ */
diff --git a/lib/riscv/asm/asm.h b/lib/riscv/asm/asm.h
new file mode 100644
index 0000000..763b28e
--- /dev/null
+++ b/lib/riscv/asm/asm.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASMRISCV_ASM_H_
+#define _ASMRISCV_ASM_H_
+
+#if __riscv_xlen == 64
+#define __REG_SEL(a, b) a
+#elif __riscv_xlen == 32
+#define __REG_SEL(a, b) b
+#else
+#error "Unexpected __riscv_xlen"
+#endif
+
+#define REG_L __REG_SEL(ld, lw)
+#define REG_S __REG_SEL(sd, sw)
+#define SZREG __REG_SEL(8, 4)
+
+#define FP_SIZE 16
+
+#endif /* _ASMRISCV_ASM_H_ */
diff --git a/lib/riscv/asm/io.h b/lib/riscv/asm/io.h
index 37a130e..a48a9aa 100644
--- a/lib/riscv/asm/io.h
+++ b/lib/riscv/asm/io.h
@@ -77,10 +77,10 @@
void __iomem *ioremap(phys_addr_t phys_addr, size_t size);
#define virt_to_phys virt_to_phys
-unsigned long virt_to_phys(volatile void *address);
+phys_addr_t virt_to_phys(volatile void *address);
#define phys_to_virt phys_to_virt
-void *phys_to_virt(unsigned long address);
+void *phys_to_virt(phys_addr_t address);
#include <asm-generic/io.h>
diff --git a/lib/riscv/asm/mmu.h b/lib/riscv/asm/mmu.h
index bb60f08..28c332f 100644
--- a/lib/riscv/asm/mmu.h
+++ b/lib/riscv/asm/mmu.h
@@ -6,6 +6,9 @@
#include <asm/page.h>
#include <asm/pgtable.h>
+#define PHYS_MASK ((phys_addr_t)SATP_PPN << PAGE_SHIFT | (PAGE_SIZE - 1))
+#define PHYS_PAGE_MASK (~((phys_addr_t)PAGE_SIZE - 1))
+
static inline pgd_t *current_pgtable(void)
{
return (pgd_t *)((csr_read(CSR_SATP) & SATP_PPN) << PAGE_SHIFT);
diff --git a/lib/riscv/asm/sbi.h b/lib/riscv/asm/sbi.h
index 73ab543..47e9102 100644
--- a/lib/riscv/asm/sbi.h
+++ b/lib/riscv/asm/sbi.h
@@ -19,6 +19,7 @@
SBI_EXT_TIME = 0x54494d45,
SBI_EXT_HSM = 0x48534d,
SBI_EXT_SRST = 0x53525354,
+ SBI_EXT_DBCN = 0x4442434E,
};
enum sbi_ext_base_fid {
@@ -42,6 +43,12 @@
SBI_EXT_TIME_SET_TIMER = 0,
};
+enum sbi_ext_dbcn_fid {
+ SBI_EXT_DBCN_CONSOLE_WRITE = 0,
+ SBI_EXT_DBCN_CONSOLE_READ,
+ SBI_EXT_DBCN_CONSOLE_WRITE_BYTE,
+};
+
struct sbiret {
long error;
long value;
diff --git a/lib/riscv/mmu.c b/lib/riscv/mmu.c
index 165a703..577c66a 100644
--- a/lib/riscv/mmu.c
+++ b/lib/riscv/mmu.c
@@ -18,9 +18,16 @@
return (vaddr >> (PGDIR_BITS * level + PAGE_SHIFT)) & PGDIR_MASK;
}
+static phys_addr_t pteval_to_phys_addr(pteval_t pteval)
+{
+ return (phys_addr_t)((pteval & PTE_PPN) >> PPN_SHIFT) << PAGE_SHIFT;
+}
+
static pte_t *pteval_to_ptep(pteval_t pteval)
{
- return (pte_t *)(((pteval & PTE_PPN) >> PPN_SHIFT) << PAGE_SHIFT);
+ phys_addr_t paddr = pteval_to_phys_addr(pteval);
+ assert(paddr == __pa(paddr));
+ return (pte_t *)__pa(paddr);
}
static pteval_t ptep_to_pteval(pte_t *ptep)
@@ -57,7 +64,8 @@
assert(!(ppn & ~PTE_PPN));
ptep = get_pte(pgtable, vaddr);
- *ptep = __pte(pte | pgprot_val(prot) | _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY);
+ pte |= pgprot_val(prot) | _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY;
+ WRITE_ONCE(*ptep, __pte(pte));
if (flush)
local_flush_tlb_page(vaddr);
@@ -67,9 +75,11 @@
pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *virt)
{
- phys_addr_t paddr = phys & PAGE_MASK;
+ phys_addr_t paddr = phys & PHYS_PAGE_MASK;
uintptr_t vaddr = (uintptr_t)virt & PAGE_MASK;
+ assert(phys == (phys & PHYS_MASK));
+
return __install_page(pgtable, paddr, vaddr,
__pgprot(_PAGE_READ | _PAGE_WRITE), true);
}
@@ -78,10 +88,12 @@
phys_addr_t phys_start, phys_addr_t phys_end,
pgprot_t prot, bool flush)
{
- phys_addr_t paddr = phys_start & PAGE_MASK;
+ phys_addr_t paddr = phys_start & PHYS_PAGE_MASK;
uintptr_t vaddr = virt_offset & PAGE_MASK;
uintptr_t virt_end = phys_end - paddr + vaddr;
+ assert(phys_start == (phys_start & PHYS_MASK));
+ assert(phys_end == (phys_end & PHYS_MASK));
assert(phys_start < phys_end);
for (; vaddr < virt_end; vaddr += PAGE_SIZE, paddr += PAGE_SIZE)
@@ -106,7 +118,7 @@
void mmu_enable(unsigned long mode, pgd_t *pgtable)
{
- unsigned long ppn = (unsigned long)pgtable >> PAGE_SHIFT;
+ unsigned long ppn = __pa(pgtable) >> PAGE_SHIFT;
unsigned long satp = mode | ppn;
assert(!(ppn & ~SATP_PPN));
@@ -118,6 +130,9 @@
struct mem_region *r;
pgd_t *pgtable;
+ /* The initial page table uses an identity mapping. */
+ assert(top == __pa(top));
+
if (!__initial_pgtable)
__initial_pgtable = alloc_page();
pgtable = __initial_pgtable;
@@ -141,12 +156,13 @@
void __iomem *ioremap(phys_addr_t phys_addr, size_t size)
{
- phys_addr_t start = phys_addr & PAGE_MASK;
+ phys_addr_t start = phys_addr & PHYS_PAGE_MASK;
phys_addr_t end = PAGE_ALIGN(phys_addr + size);
pgd_t *pgtable = current_pgtable();
bool flush = true;
- assert(sizeof(long) == 8 || !(phys_addr >> 32));
+ /* I/O is always identity mapped. */
+ assert(end == __pa(end));
if (!pgtable) {
if (!__initial_pgtable)
@@ -158,7 +174,7 @@
mmu_set_range_ptes(pgtable, start, start, end,
__pgprot(_PAGE_READ | _PAGE_WRITE), flush);
- return (void __iomem *)(unsigned long)phys_addr;
+ return (void __iomem *)__pa(phys_addr);
}
phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *virt)
@@ -179,27 +195,24 @@
if (!pte_val(*ptep))
return 0;
- return __pa(pteval_to_ptep(pte_val(*ptep)));
+ return pteval_to_phys_addr(pte_val(*ptep)) | offset_in_page(virt);
}
-unsigned long virt_to_phys(volatile void *address)
+phys_addr_t virt_to_phys(volatile void *address)
{
unsigned long satp = csr_read(CSR_SATP);
pgd_t *pgtable = (pgd_t *)((satp & SATP_PPN) << PAGE_SHIFT);
- phys_addr_t paddr;
if ((satp >> SATP_MODE_SHIFT) == 0)
return __pa(address);
- paddr = virt_to_pte_phys(pgtable, (void *)address);
- assert(sizeof(long) == 8 || !(paddr >> 32));
-
- return (unsigned long)paddr | offset_in_page(address);
+ return virt_to_pte_phys(pgtable, (void *)address);
}
-void *phys_to_virt(unsigned long address)
+void *phys_to_virt(phys_addr_t address)
{
/* @address must have an identity mapping for this to work. */
+ assert(address == __pa(address));
assert(virt_to_phys(__va(address)) == address);
return __va(address);
}
diff --git a/lib/riscv/setup.c b/lib/riscv/setup.c
index e0b5f6f..9a16f00 100644
--- a/lib/riscv/setup.c
+++ b/lib/riscv/setup.c
@@ -85,12 +85,13 @@
cpu0_calls_idle = true;
}
-static void mem_allocator_init(phys_addr_t freemem_start, phys_addr_t freemem_end)
+static void mem_allocator_init(struct mem_region *freemem, phys_addr_t freemem_start)
{
+ phys_addr_t freemem_end = freemem->end;
phys_addr_t base, top;
freemem_start = PAGE_ALIGN(freemem_start);
- freemem_end &= PAGE_MASK;
+ freemem_end &= PHYS_PAGE_MASK;
/*
* The assert below is mostly checking that the free memory doesn't
@@ -100,8 +101,14 @@
*
* TODO: Allow the VA range to shrink and move.
*/
- if (freemem_end > VA_BASE)
+ if (freemem_end > VA_BASE) {
+ struct mem_region *curr, *rest;
freemem_end = VA_BASE;
+ memregions_split(VA_BASE, &curr, &rest);
+ assert(curr == freemem);
+ if (rest)
+ rest->flags = MR_F_UNUSED;
+ }
assert(freemem_end - freemem_start >= SZ_1M * 16);
init_alloc_vpage(__va(VA_TOP));
@@ -135,7 +142,7 @@
freemem = memregions_find(freemem_start);
assert(freemem && !(freemem->flags & (MR_F_IO | MR_F_CODE)));
- mem_allocator_init(freemem_start, freemem->end);
+ mem_allocator_init(freemem, freemem_start);
}
static void freemem_push_fdt(void **freemem, const void *fdt)
@@ -193,7 +200,7 @@
const char *bootargs;
int ret;
- assert(sizeof(long) == 8 || freemem_start < VA_BASE);
+ assert(freemem_start < VA_BASE);
freemem = __va(freemem_start);
freemem_push_fdt(&freemem, fdt);
@@ -248,7 +255,7 @@
freemem_push_fdt(&freemem, efi_bootinfo->fdt);
mmu_disable();
- mem_allocator_init((unsigned long)freemem, freemem_mr->end);
+ mem_allocator_init(freemem_mr, (unsigned long)freemem);
return EFI_SUCCESS;
}
diff --git a/lib/riscv/smp.c b/lib/riscv/smp.c
index 7e4bb5b..4d373e0 100644
--- a/lib/riscv/smp.c
+++ b/lib/riscv/smp.c
@@ -8,6 +8,7 @@
#include <alloc_page.h>
#include <cpumask.h>
#include <asm/csr.h>
+#include <asm/io.h>
#include <asm/mmu.h>
#include <asm/page.h>
#include <asm/processor.h>
@@ -36,6 +37,7 @@
static void __smp_boot_secondary(int cpu, secondary_func_t func)
{
struct secondary_data *sp = alloc_pages(1) + SZ_8K - 16;
+ phys_addr_t sp_phys;
struct sbiret ret;
sp -= sizeof(struct secondary_data);
@@ -43,7 +45,10 @@
sp->stvec = csr_read(CSR_STVEC);
sp->func = func;
- ret = sbi_hart_start(cpus[cpu].hartid, (unsigned long)&secondary_entry, __pa(sp));
+ sp_phys = virt_to_phys(sp);
+ assert(sp_phys == __pa(sp_phys));
+
+ ret = sbi_hart_start(cpus[cpu].hartid, (unsigned long)&secondary_entry, __pa(sp_phys));
assert(ret.error == SBI_SUCCESS);
}
diff --git a/riscv/Makefile b/riscv/Makefile
index b0cd613..179a373 100644
--- a/riscv/Makefile
+++ b/riscv/Makefile
@@ -64,13 +64,15 @@
$(error $(1) has unsupported reloc types))
endef
-ISA_COMMON = mafdc_zicsr_zifencei_zihintpause
+ISA_COMMON = imac_zicsr_zifencei_zihintpause
ifeq ($(ARCH),riscv64)
-CFLAGS += -march=rv64i$(ISA_COMMON)
-CFLAGS += -DCONFIG_64BIT
+CFLAGS += -DCONFIG_64BIT
+CFLAGS += -mabi=lp64 -march=rv64$(ISA_COMMON)
+LDFLAGS += -melf64lriscv
else ifeq ($(ARCH),riscv32)
-CFLAGS += -march=rv32i$(ISA_COMMON)
+CFLAGS += -mabi=ilp32 -march=rv32$(ISA_COMMON)
+LDFLAGS += -melf32lriscv
endif
CFLAGS += -DCONFIG_RELOC
CFLAGS += -mcmodel=medany
@@ -78,7 +80,7 @@
CFLAGS += -std=gnu99
CFLAGS += -ffreestanding
CFLAGS += -O2
-CFLAGS += -I $(SRCDIR)/lib -I $(SRCDIR)/lib/libfdt
+CFLAGS += -I $(SRCDIR)/lib -I $(SRCDIR)/lib/libfdt -I lib
asm-offsets = lib/riscv/asm-offsets.h
include $(SRCDIR)/scripts/asm-offsets.mak
diff --git a/riscv/cstart.S b/riscv/cstart.S
index 10b5da5..d5d8ad2 100644
--- a/riscv/cstart.S
+++ b/riscv/cstart.S
@@ -4,22 +4,10 @@
*
* Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
*/
+#include <asm/asm.h>
#include <asm/asm-offsets.h>
#include <asm/csr.h>
-#if __riscv_xlen == 64
-#define __REG_SEL(a, b) a
-#elif __riscv_xlen == 32
-#define __REG_SEL(a, b) b
-#else
-#error "Unexpected __riscv_xlen"
-#endif
-
-#define REG_L __REG_SEL(ld, lw)
-#define REG_S __REG_SEL(sd, sw)
-#define SZREG __REG_SEL(8, 4)
-
-#define FP_SIZE 16
.macro push_fp, ra=ra
addi sp, sp, -FP_SIZE
diff --git a/riscv/sbi.c b/riscv/sbi.c
index 2438c49..36ddfd4 100644
--- a/riscv/sbi.c
+++ b/riscv/sbi.c
@@ -5,17 +5,25 @@
* Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
*/
#include <libcflat.h>
+#include <alloc_page.h>
#include <stdlib.h>
+#include <string.h>
#include <limits.h>
+#include <vmalloc.h>
+#include <memregions.h>
#include <asm/barrier.h>
#include <asm/csr.h>
#include <asm/delay.h>
+#include <asm/io.h>
#include <asm/isa.h>
+#include <asm/mmu.h>
#include <asm/processor.h>
#include <asm/sbi.h>
#include <asm/smp.h>
#include <asm/timer.h>
+#define HIGH_ADDR_BOUNDARY ((phys_addr_t)1 << 32)
+
static void help(void)
{
puts("Test SBI\n");
@@ -32,6 +40,38 @@
return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0);
}
+static struct sbiret __dbcn_sbi_ecall(int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2)
+{
+ return sbi_ecall(SBI_EXT_DBCN, fid, arg0, arg1, arg2, 0, 0, 0);
+}
+
+static void split_phys_addr(phys_addr_t paddr, unsigned long *hi, unsigned long *lo)
+{
+ *lo = (unsigned long)paddr;
+ *hi = 0;
+ if (__riscv_xlen == 32)
+ *hi = (unsigned long)(paddr >> 32);
+}
+
+static bool check_addr(phys_addr_t start, phys_addr_t size)
+{
+ struct mem_region *r = memregions_find(start);
+ return r && r->end - start >= size && r->flags == MR_F_UNUSED;
+}
+
+static phys_addr_t get_highest_addr(void)
+{
+ phys_addr_t highest_end = 0;
+ struct mem_region *r;
+
+ for (r = mem_regions; r->end; ++r) {
+ if (r->end > highest_end)
+ highest_end = r->end;
+ }
+
+ return highest_end - 1;
+}
+
static bool env_or_skip(const char *env)
{
if (!getenv(env)) {
@@ -70,30 +110,30 @@
}
report_prefix_push("spec_version");
- if (env_or_skip("SPEC_VERSION")) {
- expected = strtol(getenv("SPEC_VERSION"), NULL, 0);
+ if (env_or_skip("SBI_SPEC_VERSION")) {
+ expected = (long)strtoul(getenv("SBI_SPEC_VERSION"), NULL, 0);
gen_report(&ret, 0, expected);
}
report_prefix_pop();
report_prefix_push("impl_id");
- if (env_or_skip("IMPL_ID")) {
- expected = strtol(getenv("IMPL_ID"), NULL, 0);
+ if (env_or_skip("SBI_IMPL_ID")) {
+ expected = (long)strtoul(getenv("SBI_IMPL_ID"), NULL, 0);
ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_ID, 0);
gen_report(&ret, 0, expected);
}
report_prefix_pop();
report_prefix_push("impl_version");
- if (env_or_skip("IMPL_VERSION")) {
- expected = strtol(getenv("IMPL_VERSION"), NULL, 0);
+ if (env_or_skip("SBI_IMPL_VERSION")) {
+ expected = (long)strtoul(getenv("SBI_IMPL_VERSION"), NULL, 0);
ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_VERSION, 0);
gen_report(&ret, 0, expected);
}
report_prefix_pop();
report_prefix_push("probe_ext");
- expected = getenv("PROBE_EXT") ? strtol(getenv("PROBE_EXT"), NULL, 0) : 1;
+ expected = getenv("SBI_PROBE_EXT") ? (long)strtoul(getenv("SBI_PROBE_EXT"), NULL, 0) : 1;
ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE);
gen_report(&ret, 0, expected);
report_prefix_push("unavailable");
@@ -104,7 +144,8 @@
report_prefix_push("mvendorid");
if (env_or_skip("MVENDORID")) {
- expected = strtol(getenv("MVENDORID"), NULL, 0);
+ expected = (long)strtoul(getenv("MVENDORID"), NULL, 0);
+ assert(__riscv_xlen == 32 || !(expected >> 32));
ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MVENDORID, 0);
gen_report(&ret, 0, expected);
}
@@ -112,7 +153,7 @@
report_prefix_push("marchid");
if (env_or_skip("MARCHID")) {
- expected = strtol(getenv("MARCHID"), NULL, 0);
+ expected = (long)strtoul(getenv("MARCHID"), NULL, 0);
ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MARCHID, 0);
gen_report(&ret, 0, expected);
}
@@ -120,7 +161,7 @@
report_prefix_push("mimpid");
if (env_or_skip("MIMPID")) {
- expected = strtol(getenv("MIMPID"), NULL, 0);
+ expected = (long)strtoul(getenv("MIMPID"), NULL, 0);
ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MIMPID, 0);
gen_report(&ret, 0, expected);
}
@@ -168,8 +209,8 @@
struct sbiret ret;
unsigned long begin, end, duration;
const char *mask_test_str = mask_timer_irq ? " for mask irq test" : "";
- unsigned long d = getenv("TIMER_DELAY") ? strtol(getenv("TIMER_DELAY"), NULL, 0) : 200000;
- unsigned long margin = getenv("TIMER_MARGIN") ? strtol(getenv("TIMER_MARGIN"), NULL, 0) : 200000;
+ unsigned long d = getenv("SBI_TIMER_DELAY") ? strtol(getenv("SBI_TIMER_DELAY"), NULL, 0) : 200000;
+ unsigned long margin = getenv("SBI_TIMER_MARGIN") ? strtol(getenv("SBI_TIMER_MARGIN"), NULL, 0) : 200000;
d = usec_to_cycles(d);
margin = usec_to_cycles(margin);
@@ -248,9 +289,139 @@
report_prefix_pop();
}
+#define DBCN_WRITE_TEST_STRING "DBCN_WRITE_TEST_STRING\n"
+#define DBCN_WRITE_BYTE_TEST_BYTE ((u8)'a')
+
+static void dbcn_write_test(const char *s, unsigned long num_bytes)
+{
+ unsigned long base_addr_lo, base_addr_hi;
+ phys_addr_t paddr = virt_to_phys((void *)s);
+ int num_calls = 0;
+ struct sbiret ret;
+
+ split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
+
+ do {
+ ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, num_bytes, base_addr_lo, base_addr_hi);
+ num_bytes -= ret.value;
+ paddr += ret.value;
+ split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
+ num_calls++;
+ } while (num_bytes != 0 && ret.error == SBI_SUCCESS);
+
+ report(ret.error == SBI_SUCCESS, "write success (error=%ld)", ret.error);
+ report_info("%d sbi calls made", num_calls);
+}
+
+static void dbcn_high_write_test(const char *s, unsigned long num_bytes,
+ phys_addr_t page_addr, size_t page_offset)
+{
+ int nr_pages = page_offset ? 2 : 1;
+ void *vaddr;
+
+ if (page_addr != PAGE_ALIGN(page_addr) || page_addr + PAGE_SIZE < HIGH_ADDR_BOUNDARY ||
+ !check_addr(page_addr, nr_pages * PAGE_SIZE)) {
+ report_skip("Memory above 4G required");
+ return;
+ }
+
+ vaddr = alloc_vpages(nr_pages);
+
+ for (int i = 0; i < nr_pages; ++i)
+ install_page(current_pgtable(), page_addr + i * PAGE_SIZE, vaddr + i * PAGE_SIZE);
+ memcpy(vaddr + page_offset, DBCN_WRITE_TEST_STRING, num_bytes);
+ dbcn_write_test(vaddr + page_offset, num_bytes);
+}
+
+/*
+ * Only the write functionality is tested here. There's no easy way to
+ * non-interactively test the read functionality.
+ */
+static void check_dbcn(void)
+{
+ unsigned long num_bytes = strlen(DBCN_WRITE_TEST_STRING);
+ unsigned long base_addr_lo, base_addr_hi;
+ bool do_invalid_addr = false;
+ phys_addr_t paddr;
+ struct sbiret ret;
+ const char *tmp;
+ char *buf;
+
+ report_prefix_push("dbcn");
+
+ ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_DBCN);
+ if (!ret.value) {
+ report_skip("DBCN extension unavailable");
+ report_prefix_pop();
+ return;
+ }
+
+ report_prefix_push("write");
+
+ dbcn_write_test(DBCN_WRITE_TEST_STRING, num_bytes);
+
+ assert(num_bytes < PAGE_SIZE);
+
+ report_prefix_push("page boundary");
+ buf = alloc_pages(1);
+ memcpy(&buf[PAGE_SIZE - num_bytes / 2], DBCN_WRITE_TEST_STRING, num_bytes);
+ dbcn_write_test(&buf[PAGE_SIZE - num_bytes / 2], num_bytes);
+ report_prefix_pop();
+
+ report_prefix_push("high boundary");
+ tmp = getenv("SBI_DBCN_SKIP_HIGH_BOUNDARY");
+ if (!tmp || atol(tmp) == 0)
+ dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes,
+ HIGH_ADDR_BOUNDARY - PAGE_SIZE, PAGE_SIZE - num_bytes / 2);
+ else
+ report_skip("user disabled");
+ report_prefix_pop();
+
+ report_prefix_push("high page");
+ tmp = getenv("SBI_DBCN_SKIP_HIGH_PAGE");
+ if (!tmp || atol(tmp) == 0) {
+ paddr = HIGH_ADDR_BOUNDARY;
+ tmp = getenv("HIGH_PAGE");
+ if (tmp)
+ paddr = strtoull(tmp, NULL, 0);
+ dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes, paddr, 0);
+ } else {
+ report_skip("user disabled");
+ }
+ report_prefix_pop();
+
+ /* Bytes are read from memory and written to the console */
+ report_prefix_push("invalid parameter");
+ tmp = getenv("INVALID_ADDR_AUTO");
+ if (tmp && atol(tmp) == 1) {
+ paddr = get_highest_addr() + 1;
+ do_invalid_addr = true;
+ } else if (env_or_skip("INVALID_ADDR")) {
+ paddr = strtoull(getenv("INVALID_ADDR"), NULL, 0);
+ do_invalid_addr = true;
+ }
+
+ if (do_invalid_addr) {
+ split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
+ ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, 1, base_addr_lo, base_addr_hi);
+ report(ret.error == SBI_ERR_INVALID_PARAM, "address (error=%ld)", ret.error);
+ }
+ report_prefix_pop();
+
+ report_prefix_pop();
+ report_prefix_push("write_byte");
+
+ puts("DBCN_WRITE TEST CHAR: ");
+ ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, (u8)DBCN_WRITE_BYTE_TEST_BYTE, 0, 0);
+ puts("\n");
+ report(ret.error == SBI_SUCCESS, "write success (error=%ld)", ret.error);
+ report(ret.value == 0, "expected ret.value (%ld)", ret.value);
+
+ report_prefix_pop();
+}
+
int main(int argc, char **argv)
{
-
if (argc > 1 && !strcmp(argv[1], "-h")) {
help();
exit(0);
@@ -259,6 +430,7 @@
report_prefix_push("sbi");
check_base();
check_time();
+ check_dbcn();
return report_summary();
}