@@ -15,10 +15,12 @@
#if defined(__ASSEMBLY__) || defined(__ASSEMBLER__)
#define _AC(X,Y) X
#define _AT(T,X) X
+#define __ASM_STR(X) X
#else
#define __AC(X,Y) (X##Y)
#define _AC(X,Y) __AC(X,Y)
#define _AT(T,X) ((T)(X))
+#define __ASM_STR(X) #X
#endif
#define _BITUL(x) (_AC(1,UL) << (x))
@@ -10,4 +10,9 @@
#define rmb() RISCV_FENCE(ir,ir)
#define wmb() RISCV_FENCE(ow,ow)
+/* These barriers do not need to enforce ordering on devices, just memory. */
+#define smp_mb() RISCV_FENCE(rw,rw)
+#define smp_rmb() RISCV_FENCE(r,r)
+#define smp_wmb() RISCV_FENCE(w,w)
+
#endif /* _ASMRISCV_BARRIER_H_ */
new file mode 100644
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASMRISCV_BITOPS_H_
+#define _ASMRISCV_BITOPS_H_
+
+#ifndef _BITOPS_H_
+#error only <bitops.h> can be included directly
+#endif
+
+#ifdef CONFIG_64BIT
+#define BITS_PER_LONG 64
+#else
+#define BITS_PER_LONG 32
+#endif
+
+void set_bit(int nr, volatile unsigned long *addr);
+void clear_bit(int nr, volatile unsigned long *addr);
+int test_bit(int nr, const volatile unsigned long *addr);
+int test_and_set_bit(int nr, volatile unsigned long *addr);
+int test_and_clear_bit(int nr, volatile unsigned long *addr);
+
+#endif /* _ASMRISCV_BITOPS_H_ */
@@ -1,7 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ASMRISCV_CSR_H_
#define _ASMRISCV_CSR_H_
+#include <linux/const.h>
#define CSR_SSCRATCH 0x140
+#ifndef __ASSEMBLY__
+
+#define csr_swap(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrrw %0, " __ASM_STR(csr) ", %1"\
+ : "=r" (__v) : "rK" (__v) \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_read(csr) \
+({ \
+ register unsigned long __v; \
+ __asm__ __volatile__ ("csrr %0, " __ASM_STR(csr) \
+ : "=r" (__v) : \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_write(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrw " __ASM_STR(csr) ", %0" \
+ : : "rK" (__v) \
+ : "memory"); \
+})
+
+#define csr_read_set(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrrs %0, " __ASM_STR(csr) ", %1"\
+ : "=r" (__v) : "rK" (__v) \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_set(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrs " __ASM_STR(csr) ", %0" \
+ : : "rK" (__v) \
+ : "memory"); \
+})
+
+#define csr_read_clear(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrrc %0, " __ASM_STR(csr) ", %1"\
+ : "=r" (__v) : "rK" (__v) \
+ : "memory"); \
+ __v; \
+})
+
+#define csr_clear(csr, val) \
+({ \
+ unsigned long __v = (unsigned long)(val); \
+ __asm__ __volatile__ ("csrc " __ASM_STR(csr) ", %0" \
+ : : "rK" (__v) \
+ : "memory"); \
+})
+
+#endif /* !__ASSEMBLY__ */
#endif /* _ASMRISCV_CSR_H_ */
@@ -1,7 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ASMRISCV_SETUP_H_
#define _ASMRISCV_SETUP_H_
+#include <libcflat.h>
+#define NR_CPUS 16
+extern unsigned long cpus[NR_CPUS]; /* per-cpu IDs (hartids) */
+extern int nr_cpus;
+int hartid_to_cpu(unsigned long hartid);
+
+void io_init(void);
void setup(const void *fdt, phys_addr_t freemem_start);
#endif /* _ASMRISCV_SETUP_H_ */
new file mode 100644
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
+ */
+#include <bitops.h>
+
+void set_bit(int nr, volatile unsigned long *addr)
+{
+ volatile unsigned long *word = addr + BIT_WORD(nr);
+ unsigned long mask = BIT_MASK(nr);
+
+ __sync_or_and_fetch(word, mask);
+}
+
+void clear_bit(int nr, volatile unsigned long *addr)
+{
+ volatile unsigned long *word = addr + BIT_WORD(nr);
+ unsigned long mask = BIT_MASK(nr);
+
+ __sync_and_and_fetch(word, ~mask);
+}
+
+int test_bit(int nr, const volatile unsigned long *addr)
+{
+ const volatile unsigned long *word = addr + BIT_WORD(nr);
+ unsigned long mask = BIT_MASK(nr);
+
+ return (*word & mask) != 0;
+}
+
+int test_and_set_bit(int nr, volatile unsigned long *addr)
+{
+ volatile unsigned long *word = addr + BIT_WORD(nr);
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long old = __sync_fetch_and_or(word, mask);
+
+ return (old & mask) != 0;
+}
+
+int test_and_clear_bit(int nr, volatile unsigned long *addr)
+{
+ volatile unsigned long *word = addr + BIT_WORD(nr);
+ unsigned long mask = BIT_MASK(nr);
+ unsigned long old = __sync_fetch_and_and(word, ~mask);
+
+ return (old & mask) != 0;
+}
@@ -7,7 +7,9 @@
*/
#include <libcflat.h>
#include <config.h>
+#include <devicetree.h>
#include <asm/io.h>
+#include <asm/setup.h>
#include <asm/spinlock.h>
/*
@@ -21,6 +23,55 @@
static volatile u8 *uart0_base = UART_EARLY_BASE;
static struct spinlock uart_lock;
+static void uart0_init_fdt(void)
+{
+ const char *compatible[] = {"ns16550a"};
+ struct dt_pbus_reg base;
+ int i, ret;
+
+ ret = dt_get_default_console_node();
+ assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND);
+
+ if (ret == -FDT_ERR_NOTFOUND) {
+ for (i = 0; i < ARRAY_SIZE(compatible); i++) {
+ ret = dt_pbus_get_base_compatible(compatible[i], &base);
+ assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
+ if (ret == 0)
+ break;
+ }
+
+ if (ret) {
+ printf("%s: Compatible uart not found in the device tree, aborting...\n",
+ __func__);
+ abort();
+ }
+ } else {
+ ret = dt_pbus_translate_node(ret, 0, &base);
+ assert(ret == 0);
+ }
+
+ uart0_base = ioremap(base.addr, base.size);
+}
+
+static void uart0_init_acpi(void)
+{
+ assert_msg(false, "ACPI not available");
+}
+
+void io_init(void)
+{
+ if (dt_available())
+ uart0_init_fdt();
+ else
+ uart0_init_acpi();
+
+ if (uart0_base != UART_EARLY_BASE) {
+ printf("WARNING: early print support may not work. "
+ "Found uart at %p, but early base is %p.\n",
+ uart0_base, UART_EARLY_BASE);
+ }
+}
+
void puts(const char *s)
{
spin_lock(&uart_lock);
@@ -5,8 +5,117 @@
* Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
*/
#include <libcflat.h>
+#include <alloc.h>
+#include <alloc_phys.h>
+#include <argv.h>
+#include <cpumask.h>
+#include <devicetree.h>
+#include <asm/csr.h>
+#include <asm/page.h>
#include <asm/setup.h>
+char *initrd;
+u32 initrd_size;
+
+unsigned long cpus[NR_CPUS] = { [0 ... NR_CPUS - 1] = ~0UL };
+int nr_cpus;
+
+int hartid_to_cpu(unsigned long hartid)
+{
+ int cpu;
+
+ for_each_present_cpu(cpu)
+ if (cpus[cpu] == hartid)
+ return cpu;
+ return -1;
+}
+
+static void cpu_set_fdt(int fdtnode __unused, u64 regval, void *info __unused)
+{
+ int cpu = nr_cpus++;
+
+ assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
+
+ cpus[cpu] = regval;
+ set_cpu_present(cpu, true);
+}
+
+static void cpu_init_acpi(void)
+{
+ assert_msg(false, "ACPI not available");
+}
+
+static void cpu_init(void)
+{
+ int ret;
+
+ nr_cpus = 0;
+ if (dt_available()) {
+ ret = dt_for_each_cpu_node(cpu_set_fdt, NULL);
+ assert(ret == 0);
+ } else {
+ cpu_init_acpi();
+ }
+
+ set_cpu_online(hartid_to_cpu(csr_read(CSR_SSCRATCH)), true);
+}
+
+static void mem_init(phys_addr_t freemem_start)
+{
+ //TODO - for now just assume we've got some memory available
+ phys_alloc_init(freemem_start, 16 * SZ_1M);
+}
+
+static void banner(void)
+{
+ puts("\n");
+ puts("##########################################################################\n");
+ puts("# kvm-unit-tests\n");
+ puts("##########################################################################\n");
+ puts("\n");
+}
+
void setup(const void *fdt, phys_addr_t freemem_start)
{
+ void *freemem;
+ const char *bootargs, *tmp;
+ u32 fdt_size;
+ int ret;
+
+ assert(sizeof(long) == 8 || freemem_start < (3ul << 30));
+ freemem = (void *)(unsigned long)freemem_start;
+
+ /* Move the FDT to the base of free memory */
+ fdt_size = fdt_totalsize(fdt);
+ ret = fdt_move(fdt, freemem, fdt_size);
+ assert(ret == 0);
+ ret = dt_init(freemem);
+ assert(ret == 0);
+ freemem += fdt_size;
+
+ /* Move the initrd to the top of the FDT */
+ ret = dt_get_initrd(&tmp, &initrd_size);
+ assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
+ if (ret == 0) {
+ initrd = freemem;
+ memmove(initrd, tmp, initrd_size);
+ freemem += initrd_size;
+ }
+
+ mem_init(PAGE_ALIGN((unsigned long)freemem));
+ cpu_init();
+ io_init();
+
+ ret = dt_get_bootargs(&bootargs);
+ assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
+ setup_args_progname(bootargs);
+
+ if (initrd) {
+ /* environ is currently the only file in the initrd */
+ char *env = malloc(initrd_size);
+ memcpy(env, initrd, initrd_size);
+ setup_env(env, initrd_size);
+ }
+
+ banner();
}
new file mode 100644
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <cpumask.h>
+
+cpumask_t cpu_present_mask;
+cpumask_t cpu_online_mask;
+cpumask_t cpu_idle_mask;
@@ -20,8 +20,13 @@ $(TEST_DIR)/sieve.elf: AUXFLAGS = 0x1
cstart.o = $(TEST_DIR)/cstart.o
+cflatobjs += lib/alloc.o
+cflatobjs += lib/alloc_phys.o
+cflatobjs += lib/devicetree.o
+cflatobjs += lib/riscv/bitops.o
cflatobjs += lib/riscv/io.o
cflatobjs += lib/riscv/setup.o
+cflatobjs += lib/riscv/smp.o
########################################
@@ -5,9 +5,47 @@
* Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
*/
#include <libcflat.h>
+#include <cpumask.h>
+#include <asm/setup.h>
-int main(void)
+static void check_cpus(void)
{
- puts("Hello, world\n");
- return 0;
+ int cpu;
+
+ for_each_present_cpu(cpu)
+ report_info("CPU%3d: hartid=%08lx", cpu, cpus[cpu]);
+}
+
+int main(int argc, char **argv)
+{
+ bool r;
+
+ report_prefix_push("selftest");
+
+ report(!strncmp(argv[0], "selftest", 8), "program name");
+
+ if (argc > 1) {
+ r = !strcmp(argv[1], "foo");
+ if (argc > 2)
+ r &= !strcmp(argv[2], "bar");
+ if (argc > 3)
+ r &= !strcmp(argv[3], "baz");
+ report_info("matched %d command line parameters", argc - 1);
+ report(r, "command line parsing");
+ } else {
+ report_skip("command line parsing");
+ }
+
+ if (getenv("FOO")) {
+ r = !strcmp(getenv("FOO"), "foo");
+ r &= !strcmp(getenv("BAR"), "bar");
+ r &= !strcmp(getenv("BAZ"), "baz");
+ report(r, "environ parsing");
+ } else {
+ report_skip("environ parsing");
+ }
+
+ check_cpus();
+
+ return report_summary();
}