diff options
| author | Mitchell Riedstra <mitch@riedstra.dev> | 2025-12-24 19:49:57 -0500 |
|---|---|---|
| committer | Mitchell Riedstra <mitch@riedstra.dev> | 2025-12-24 19:49:57 -0500 |
| commit | 939ac4319cb047a37ba46f84eff81948063f6954 (patch) | |
| tree | 5112cf8aad73125a13f5b52c0290a7f26f948b52 /jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c | |
| parent | 3a1b5ba15b89c907f9bf66a0761ffdd73b32208b (diff) | |
| download | unixv4-939ac4319cb047a37ba46f84eff81948063f6954.tar.gz unixv4-939ac4319cb047a37ba46f84eff81948063f6954.tar.xz | |
Add working webpage for unix v4
Diffstat (limited to 'jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c')
| -rw-r--r-- | jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c | 1053 |
1 files changed, 1053 insertions, 0 deletions
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c new file mode 100644 index 0000000..a7149fd --- /dev/null +++ b/jslinux-2019-12-21/tinyemu-2019-12-21/riscv_machine.c @@ -0,0 +1,1053 @@ +/* + * RISCV machine + * + * Copyright (c) 2016-2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <inttypes.h> +#include <assert.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> + +#include "cutils.h" +#include "iomem.h" +#include "riscv_cpu.h" +#include "virtio.h" +#include "machine.h" + +/* RISCV machine */ + +typedef struct RISCVMachine { + VirtMachine common; + PhysMemoryMap *mem_map; + int max_xlen; + RISCVCPUState *cpu_state; + uint64_t ram_size; + /* RTC */ + BOOL rtc_real_time; + uint64_t rtc_start_time; + uint64_t timecmp; + /* PLIC */ + uint32_t plic_pending_irq, plic_served_irq; + IRQSignal plic_irq[32]; /* IRQ 0 is not used */ + /* HTIF */ + uint64_t htif_tohost, htif_fromhost; + + VIRTIODevice *keyboard_dev; + VIRTIODevice *mouse_dev; + + int virtio_count; +} RISCVMachine; + +#define LOW_RAM_SIZE 0x00010000 /* 64KB */ +#define RAM_BASE_ADDR 0x80000000 +#define CLINT_BASE_ADDR 0x02000000 +#define CLINT_SIZE 0x000c0000 +#define HTIF_BASE_ADDR 0x40008000 +#define IDE_BASE_ADDR 0x40009000 +#define VIRTIO_BASE_ADDR 0x40010000 +#define VIRTIO_SIZE 0x1000 +#define VIRTIO_IRQ 1 +#define PLIC_BASE_ADDR 0x40100000 +#define PLIC_SIZE 0x00400000 +#define FRAMEBUFFER_BASE_ADDR 0x41000000 + +#define RTC_FREQ 10000000 +#define RTC_FREQ_DIV 16 /* arbitrary, relative to CPU freq to have a + 10 MHz frequency */ + +static uint64_t rtc_get_real_time(RISCVMachine *s) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * RTC_FREQ + + (ts.tv_nsec / (1000000000 / RTC_FREQ)); +} + +static uint64_t rtc_get_time(RISCVMachine *m) +{ + uint64_t val; + if (m->rtc_real_time) { + val = rtc_get_real_time(m) - m->rtc_start_time; + } else { + val = riscv_cpu_get_cycles(m->cpu_state) / RTC_FREQ_DIV; + } + // printf("rtc_time=%" PRId64 "\n", val); + return val; +} + +static uint32_t htif_read(void *opaque, uint32_t offset, + int size_log2) +{ + RISCVMachine *s = opaque; + uint32_t val; + + assert(size_log2 == 2); + switch(offset) { + case 0: + val = s->htif_tohost; + break; + case 4: + val = s->htif_tohost >> 32; + break; + case 8: + val = s->htif_fromhost; + break; + case 12: + val = s->htif_fromhost >> 32; + break; + default: + val = 0; + break; + } + return val; +} + +static void htif_handle_cmd(RISCVMachine *s) +{ + uint32_t device, cmd; + + device = s->htif_tohost >> 56; + cmd = (s->htif_tohost >> 48) & 0xff; + if (s->htif_tohost == 1) { + /* shuthost */ + printf("\nPower off.\n"); + exit(0); + } else if (device == 1 && cmd == 1) { + uint8_t buf[1]; + buf[0] = s->htif_tohost & 0xff; + s->common.console->write_data(s->common.console->opaque, buf, 1); + s->htif_tohost = 0; + s->htif_fromhost = ((uint64_t)device << 56) | ((uint64_t)cmd << 48); + } else if (device == 1 && cmd == 0) { + /* request keyboard interrupt */ + s->htif_tohost = 0; + } else { + printf("HTIF: unsupported tohost=0x%016" PRIx64 "\n", s->htif_tohost); + } +} + +static void htif_write(void *opaque, uint32_t offset, uint32_t val, + int size_log2) +{ + RISCVMachine *s = opaque; + + assert(size_log2 == 2); + switch(offset) { + case 0: + s->htif_tohost = (s->htif_tohost & ~0xffffffff) | val; + break; + case 4: + s->htif_tohost = (s->htif_tohost & 0xffffffff) | ((uint64_t)val << 32); + htif_handle_cmd(s); + break; + case 8: + s->htif_fromhost = (s->htif_fromhost & ~0xffffffff) | val; + break; + case 12: + s->htif_fromhost = (s->htif_fromhost & 0xffffffff) | + (uint64_t)val << 32; + break; + default: + break; + } +} + +#if 0 +static void htif_poll(RISCVMachine *s) +{ + uint8_t buf[1]; + int ret; + + if (s->htif_fromhost == 0) { + ret = s->console->read_data(s->console->opaque, buf, 1); + if (ret == 1) { + s->htif_fromhost = ((uint64_t)1 << 56) | ((uint64_t)0 << 48) | + buf[0]; + } + } +} +#endif + +static uint32_t clint_read(void *opaque, uint32_t offset, int size_log2) +{ + RISCVMachine *m = opaque; + uint32_t val; + + assert(size_log2 == 2); + switch(offset) { + case 0xbff8: + val = rtc_get_time(m); + break; + case 0xbffc: + val = rtc_get_time(m) >> 32; + break; + case 0x4000: + val = m->timecmp; + break; + case 0x4004: + val = m->timecmp >> 32; + break; + default: + val = 0; + break; + } + return val; +} + +static void clint_write(void *opaque, uint32_t offset, uint32_t val, + int size_log2) +{ + RISCVMachine *m = opaque; + + assert(size_log2 == 2); + switch(offset) { + case 0x4000: + m->timecmp = (m->timecmp & ~0xffffffff) | val; + riscv_cpu_reset_mip(m->cpu_state, MIP_MTIP); + break; + case 0x4004: + m->timecmp = (m->timecmp & 0xffffffff) | ((uint64_t)val << 32); + riscv_cpu_reset_mip(m->cpu_state, MIP_MTIP); + break; + default: + break; + } +} + +static void plic_update_mip(RISCVMachine *s) +{ + RISCVCPUState *cpu = s->cpu_state; + uint32_t mask; + mask = s->plic_pending_irq & ~s->plic_served_irq; + if (mask) { + riscv_cpu_set_mip(cpu, MIP_MEIP | MIP_SEIP); + } else { + riscv_cpu_reset_mip(cpu, MIP_MEIP | MIP_SEIP); + } +} + +#define PLIC_HART_BASE 0x200000 +#define PLIC_HART_SIZE 0x1000 + +static uint32_t plic_read(void *opaque, uint32_t offset, int size_log2) +{ + RISCVMachine *s = opaque; + uint32_t val, mask; + int i; + assert(size_log2 == 2); + switch(offset) { + case PLIC_HART_BASE: + val = 0; + break; + case PLIC_HART_BASE + 4: + mask = s->plic_pending_irq & ~s->plic_served_irq; + if (mask != 0) { + i = ctz32(mask); + s->plic_served_irq |= 1 << i; + plic_update_mip(s); + val = i + 1; + } else { + val = 0; + } + break; + default: + val = 0; + break; + } + return val; +} + +static void plic_write(void *opaque, uint32_t offset, uint32_t val, + int size_log2) +{ + RISCVMachine *s = opaque; + + assert(size_log2 == 2); + switch(offset) { + case PLIC_HART_BASE + 4: + val--; + if (val < 32) { + s->plic_served_irq &= ~(1 << val); + plic_update_mip(s); + } + break; + default: + break; + } +} + +static void plic_set_irq(void *opaque, int irq_num, int state) +{ + RISCVMachine *s = opaque; + uint32_t mask; + + mask = 1 << (irq_num - 1); + if (state) + s->plic_pending_irq |= mask; + else + s->plic_pending_irq &= ~mask; + plic_update_mip(s); +} + +static uint8_t *get_ram_ptr(RISCVMachine *s, uint64_t paddr, BOOL is_rw) +{ + return phys_mem_get_ram_ptr(s->mem_map, paddr, is_rw); +} + +/* FDT machine description */ + +#define FDT_MAGIC 0xd00dfeed +#define FDT_VERSION 17 + +struct fdt_header { + uint32_t magic; + uint32_t totalsize; + uint32_t off_dt_struct; + uint32_t off_dt_strings; + uint32_t off_mem_rsvmap; + uint32_t version; + uint32_t last_comp_version; /* <= 17 */ + uint32_t boot_cpuid_phys; + uint32_t size_dt_strings; + uint32_t size_dt_struct; +}; + +struct fdt_reserve_entry { + uint64_t address; + uint64_t size; +}; + +#define FDT_BEGIN_NODE 1 +#define FDT_END_NODE 2 +#define FDT_PROP 3 +#define FDT_NOP 4 +#define FDT_END 9 + +typedef struct { + uint32_t *tab; + int tab_len; + int tab_size; + int open_node_count; + + char *string_table; + int string_table_len; + int string_table_size; +} FDTState; + +static FDTState *fdt_init(void) +{ + FDTState *s; + s = mallocz(sizeof(*s)); + return s; +} + +static void fdt_alloc_len(FDTState *s, int len) +{ + int new_size; + if (unlikely(len > s->tab_size)) { + new_size = max_int(len, s->tab_size * 3 / 2); + s->tab = realloc(s->tab, new_size * sizeof(uint32_t)); + s->tab_size = new_size; + } +} + +static void fdt_put32(FDTState *s, int v) +{ + fdt_alloc_len(s, s->tab_len + 1); + s->tab[s->tab_len++] = cpu_to_be32(v); +} + +/* the data is zero padded */ +static void fdt_put_data(FDTState *s, const uint8_t *data, int len) +{ + int len1; + + len1 = (len + 3) / 4; + fdt_alloc_len(s, s->tab_len + len1); + memcpy(s->tab + s->tab_len, data, len); + memset((uint8_t *)(s->tab + s->tab_len) + len, 0, -len & 3); + s->tab_len += len1; +} + +static void fdt_begin_node(FDTState *s, const char *name) +{ + fdt_put32(s, FDT_BEGIN_NODE); + fdt_put_data(s, (uint8_t *)name, strlen(name) + 1); + s->open_node_count++; +} + +static void fdt_begin_node_num(FDTState *s, const char *name, uint64_t n) +{ + char buf[256]; + snprintf(buf, sizeof(buf), "%s@%" PRIx64, name, n); + fdt_begin_node(s, buf); +} + +static void fdt_end_node(FDTState *s) +{ + fdt_put32(s, FDT_END_NODE); + s->open_node_count--; +} + +static int fdt_get_string_offset(FDTState *s, const char *name) +{ + int pos, new_size, name_size, new_len; + + pos = 0; + while (pos < s->string_table_len) { + if (!strcmp(s->string_table + pos, name)) + return pos; + pos += strlen(s->string_table + pos) + 1; + } + /* add a new string */ + name_size = strlen(name) + 1; + new_len = s->string_table_len + name_size; + if (new_len > s->string_table_size) { + new_size = max_int(new_len, s->string_table_size * 3 / 2); + s->string_table = realloc(s->string_table, new_size); + s->string_table_size = new_size; + } + pos = s->string_table_len; + memcpy(s->string_table + pos, name, name_size); + s->string_table_len = new_len; + return pos; +} + +static void fdt_prop(FDTState *s, const char *prop_name, + const void *data, int data_len) +{ + fdt_put32(s, FDT_PROP); + fdt_put32(s, data_len); + fdt_put32(s, fdt_get_string_offset(s, prop_name)); + fdt_put_data(s, data, data_len); +} + +static void fdt_prop_tab_u32(FDTState *s, const char *prop_name, + uint32_t *tab, int tab_len) +{ + int i; + fdt_put32(s, FDT_PROP); + fdt_put32(s, tab_len * sizeof(uint32_t)); + fdt_put32(s, fdt_get_string_offset(s, prop_name)); + for(i = 0; i < tab_len; i++) + fdt_put32(s, tab[i]); +} + +static void fdt_prop_u32(FDTState *s, const char *prop_name, uint32_t val) +{ + fdt_prop_tab_u32(s, prop_name, &val, 1); +} + +static void fdt_prop_tab_u64(FDTState *s, const char *prop_name, + uint64_t v0) +{ + uint32_t tab[2]; + tab[0] = v0 >> 32; + tab[1] = v0; + fdt_prop_tab_u32(s, prop_name, tab, 2); +} + +static void fdt_prop_tab_u64_2(FDTState *s, const char *prop_name, + uint64_t v0, uint64_t v1) +{ + uint32_t tab[4]; + tab[0] = v0 >> 32; + tab[1] = v0; + tab[2] = v1 >> 32; + tab[3] = v1; + fdt_prop_tab_u32(s, prop_name, tab, 4); +} + +static void fdt_prop_str(FDTState *s, const char *prop_name, + const char *str) +{ + fdt_prop(s, prop_name, str, strlen(str) + 1); +} + +/* NULL terminated string list */ +static void fdt_prop_tab_str(FDTState *s, const char *prop_name, + ...) +{ + va_list ap; + int size, str_size; + char *ptr, *tab; + + va_start(ap, prop_name); + size = 0; + for(;;) { + ptr = va_arg(ap, char *); + if (!ptr) + break; + str_size = strlen(ptr) + 1; + size += str_size; + } + va_end(ap); + + tab = malloc(size); + va_start(ap, prop_name); + size = 0; + for(;;) { + ptr = va_arg(ap, char *); + if (!ptr) + break; + str_size = strlen(ptr) + 1; + memcpy(tab + size, ptr, str_size); + size += str_size; + } + va_end(ap); + + fdt_prop(s, prop_name, tab, size); + free(tab); +} + +/* write the FDT to 'dst1'. return the FDT size in bytes */ +int fdt_output(FDTState *s, uint8_t *dst) +{ + struct fdt_header *h; + struct fdt_reserve_entry *re; + int dt_struct_size; + int dt_strings_size; + int pos; + + assert(s->open_node_count == 0); + + fdt_put32(s, FDT_END); + + dt_struct_size = s->tab_len * sizeof(uint32_t); + dt_strings_size = s->string_table_len; + + h = (struct fdt_header *)dst; + h->magic = cpu_to_be32(FDT_MAGIC); + h->version = cpu_to_be32(FDT_VERSION); + h->last_comp_version = cpu_to_be32(16); + h->boot_cpuid_phys = cpu_to_be32(0); + h->size_dt_strings = cpu_to_be32(dt_strings_size); + h->size_dt_struct = cpu_to_be32(dt_struct_size); + + pos = sizeof(struct fdt_header); + + h->off_dt_struct = cpu_to_be32(pos); + memcpy(dst + pos, s->tab, dt_struct_size); + pos += dt_struct_size; + + /* align to 8 */ + while ((pos & 7) != 0) { + dst[pos++] = 0; + } + h->off_mem_rsvmap = cpu_to_be32(pos); + re = (struct fdt_reserve_entry *)(dst + pos); + re->address = 0; /* no reserved entry */ + re->size = 0; + pos += sizeof(struct fdt_reserve_entry); + + h->off_dt_strings = cpu_to_be32(pos); + memcpy(dst + pos, s->string_table, dt_strings_size); + pos += dt_strings_size; + + /* align to 8, just in case */ + while ((pos & 7) != 0) { + dst[pos++] = 0; + } + + h->totalsize = cpu_to_be32(pos); + return pos; +} + +void fdt_end(FDTState *s) +{ + free(s->tab); + free(s->string_table); + free(s); +} + +static int riscv_build_fdt(RISCVMachine *m, uint8_t *dst, + uint64_t kernel_start, uint64_t kernel_size, + uint64_t initrd_start, uint64_t initrd_size, + const char *cmd_line) +{ + FDTState *s; + int size, max_xlen, i, cur_phandle, intc_phandle, plic_phandle; + char isa_string[128], *q; + uint32_t misa; + uint32_t tab[4]; + FBDevice *fb_dev; + + s = fdt_init(); + + cur_phandle = 1; + + fdt_begin_node(s, ""); + fdt_prop_u32(s, "#address-cells", 2); + fdt_prop_u32(s, "#size-cells", 2); + fdt_prop_str(s, "compatible", "ucbbar,riscvemu-bar_dev"); + fdt_prop_str(s, "model", "ucbbar,riscvemu-bare"); + + /* CPU list */ + fdt_begin_node(s, "cpus"); + fdt_prop_u32(s, "#address-cells", 1); + fdt_prop_u32(s, "#size-cells", 0); + fdt_prop_u32(s, "timebase-frequency", RTC_FREQ); + + /* cpu */ + fdt_begin_node_num(s, "cpu", 0); + fdt_prop_str(s, "device_type", "cpu"); + fdt_prop_u32(s, "reg", 0); + fdt_prop_str(s, "status", "okay"); + fdt_prop_str(s, "compatible", "riscv"); + + max_xlen = m->max_xlen; + misa = riscv_cpu_get_misa(m->cpu_state); + q = isa_string; + q += snprintf(isa_string, sizeof(isa_string), "rv%d", max_xlen); + for(i = 0; i < 26; i++) { + if (misa & (1 << i)) + *q++ = 'a' + i; + } + *q = '\0'; + fdt_prop_str(s, "riscv,isa", isa_string); + + fdt_prop_str(s, "mmu-type", max_xlen <= 32 ? "riscv,sv32" : "riscv,sv48"); + fdt_prop_u32(s, "clock-frequency", 2000000000); + + fdt_begin_node(s, "interrupt-controller"); + fdt_prop_u32(s, "#interrupt-cells", 1); + fdt_prop(s, "interrupt-controller", NULL, 0); + fdt_prop_str(s, "compatible", "riscv,cpu-intc"); + intc_phandle = cur_phandle++; + fdt_prop_u32(s, "phandle", intc_phandle); + fdt_end_node(s); /* interrupt-controller */ + + fdt_end_node(s); /* cpu */ + + fdt_end_node(s); /* cpus */ + + fdt_begin_node_num(s, "memory", RAM_BASE_ADDR); + fdt_prop_str(s, "device_type", "memory"); + tab[0] = (uint64_t)RAM_BASE_ADDR >> 32; + tab[1] = RAM_BASE_ADDR; + tab[2] = m->ram_size >> 32; + tab[3] = m->ram_size; + fdt_prop_tab_u32(s, "reg", tab, 4); + + fdt_end_node(s); /* memory */ + + fdt_begin_node(s, "htif"); + fdt_prop_str(s, "compatible", "ucb,htif0"); + fdt_end_node(s); /* htif */ + + fdt_begin_node(s, "soc"); + fdt_prop_u32(s, "#address-cells", 2); + fdt_prop_u32(s, "#size-cells", 2); + fdt_prop_tab_str(s, "compatible", + "ucbbar,riscvemu-bar-soc", "simple-bus", NULL); + fdt_prop(s, "ranges", NULL, 0); + + fdt_begin_node_num(s, "clint", CLINT_BASE_ADDR); + fdt_prop_str(s, "compatible", "riscv,clint0"); + + tab[0] = intc_phandle; + tab[1] = 3; /* M IPI irq */ + tab[2] = intc_phandle; + tab[3] = 7; /* M timer irq */ + fdt_prop_tab_u32(s, "interrupts-extended", tab, 4); + + fdt_prop_tab_u64_2(s, "reg", CLINT_BASE_ADDR, CLINT_SIZE); + + fdt_end_node(s); /* clint */ + + fdt_begin_node_num(s, "plic", PLIC_BASE_ADDR); + fdt_prop_u32(s, "#interrupt-cells", 1); + fdt_prop(s, "interrupt-controller", NULL, 0); + fdt_prop_str(s, "compatible", "riscv,plic0"); + fdt_prop_u32(s, "riscv,ndev", 31); + fdt_prop_tab_u64_2(s, "reg", PLIC_BASE_ADDR, PLIC_SIZE); + + tab[0] = intc_phandle; + tab[1] = 9; /* S ext irq */ + tab[2] = intc_phandle; + tab[3] = 11; /* M ext irq */ + fdt_prop_tab_u32(s, "interrupts-extended", tab, 4); + + plic_phandle = cur_phandle++; + fdt_prop_u32(s, "phandle", plic_phandle); + + fdt_end_node(s); /* plic */ + + for(i = 0; i < m->virtio_count; i++) { + fdt_begin_node_num(s, "virtio", VIRTIO_BASE_ADDR + i * VIRTIO_SIZE); + fdt_prop_str(s, "compatible", "virtio,mmio"); + fdt_prop_tab_u64_2(s, "reg", VIRTIO_BASE_ADDR + i * VIRTIO_SIZE, + VIRTIO_SIZE); + tab[0] = plic_phandle; + tab[1] = VIRTIO_IRQ + i; + fdt_prop_tab_u32(s, "interrupts-extended", tab, 2); + fdt_end_node(s); /* virtio */ + } + + fb_dev = m->common.fb_dev; + if (fb_dev) { + fdt_begin_node_num(s, "framebuffer", FRAMEBUFFER_BASE_ADDR); + fdt_prop_str(s, "compatible", "simple-framebuffer"); + fdt_prop_tab_u64_2(s, "reg", FRAMEBUFFER_BASE_ADDR, fb_dev->fb_size); + fdt_prop_u32(s, "width", fb_dev->width); + fdt_prop_u32(s, "height", fb_dev->height); + fdt_prop_u32(s, "stride", fb_dev->stride); + fdt_prop_str(s, "format", "a8r8g8b8"); + fdt_end_node(s); /* framebuffer */ + } + + fdt_end_node(s); /* soc */ + + fdt_begin_node(s, "chosen"); + fdt_prop_str(s, "bootargs", cmd_line ? cmd_line : ""); + if (kernel_size > 0) { + fdt_prop_tab_u64(s, "riscv,kernel-start", kernel_start); + fdt_prop_tab_u64(s, "riscv,kernel-end", kernel_start + kernel_size); + } + if (initrd_size > 0) { + fdt_prop_tab_u64(s, "linux,initrd-start", initrd_start); + fdt_prop_tab_u64(s, "linux,initrd-end", initrd_start + initrd_size); + } + + + fdt_end_node(s); /* chosen */ + + fdt_end_node(s); /* / */ + + size = fdt_output(s, dst); +#if 0 + { + FILE *f; + f = fopen("/tmp/riscvemu.dtb", "wb"); + fwrite(dst, 1, size, f); + fclose(f); + } +#endif + fdt_end(s); + return size; +} + +static void copy_bios(RISCVMachine *s, const uint8_t *buf, int buf_len, + const uint8_t *kernel_buf, int kernel_buf_len, + const uint8_t *initrd_buf, int initrd_buf_len, + const char *cmd_line) +{ + uint32_t fdt_addr, align, kernel_base, initrd_base; + uint8_t *ram_ptr; + uint32_t *q; + + if (buf_len > s->ram_size) { + vm_error("BIOS too big\n"); + exit(1); + } + + ram_ptr = get_ram_ptr(s, RAM_BASE_ADDR, TRUE); + memcpy(ram_ptr, buf, buf_len); + + kernel_base = 0; + if (kernel_buf_len > 0) { + /* copy the kernel if present */ + if (s->max_xlen == 32) + align = 4 << 20; /* 4 MB page align */ + else + align = 2 << 20; /* 2 MB page align */ + kernel_base = (buf_len + align - 1) & ~(align - 1); + memcpy(ram_ptr + kernel_base, kernel_buf, kernel_buf_len); + if (kernel_buf_len + kernel_base > s->ram_size) { + vm_error("kernel too big"); + exit(1); + } + } + + initrd_base = 0; + if (initrd_buf_len > 0) { + /* same allocation as QEMU */ + initrd_base = s->ram_size / 2; + if (initrd_base > (128 << 20)) + initrd_base = 128 << 20; + memcpy(ram_ptr + initrd_base, initrd_buf, initrd_buf_len); + if (initrd_buf_len + initrd_base > s->ram_size) { + vm_error("initrd too big"); + exit(1); + } + } + + ram_ptr = get_ram_ptr(s, 0, TRUE); + + fdt_addr = 0x1000 + 8 * 8; + + riscv_build_fdt(s, ram_ptr + fdt_addr, + RAM_BASE_ADDR + kernel_base, kernel_buf_len, + RAM_BASE_ADDR + initrd_base, initrd_buf_len, + cmd_line); + + /* jump_addr = 0x80000000 */ + + q = (uint32_t *)(ram_ptr + 0x1000); + q[0] = 0x297 + 0x80000000 - 0x1000; /* auipc t0, jump_addr */ + q[1] = 0x597; /* auipc a1, dtb */ + q[2] = 0x58593 + ((fdt_addr - 4) << 20); /* addi a1, a1, dtb */ + q[3] = 0xf1402573; /* csrr a0, mhartid */ + q[4] = 0x00028067; /* jalr zero, t0, jump_addr */ +} + +static void riscv_flush_tlb_write_range(void *opaque, uint8_t *ram_addr, + size_t ram_size) +{ + RISCVMachine *s = opaque; + riscv_cpu_flush_tlb_write_range_ram(s->cpu_state, ram_addr, ram_size); +} + +static void riscv_machine_set_defaults(VirtMachineParams *p) +{ +} + +static VirtMachine *riscv_machine_init(const VirtMachineParams *p) +{ + RISCVMachine *s; + VIRTIODevice *blk_dev; + int irq_num, i, max_xlen, ram_flags; + VIRTIOBusDef vbus_s, *vbus = &vbus_s; + + + if (!strcmp(p->machine_name, "riscv32")) { + max_xlen = 32; + } else if (!strcmp(p->machine_name, "riscv64")) { + max_xlen = 64; + } else if (!strcmp(p->machine_name, "riscv128")) { + max_xlen = 128; + } else { + vm_error("unsupported machine: %s\n", p->machine_name); + return NULL; + } + + s = mallocz(sizeof(*s)); + s->common.vmc = p->vmc; + s->ram_size = p->ram_size; + s->max_xlen = max_xlen; + s->mem_map = phys_mem_map_init(); + /* needed to handle the RAM dirty bits */ + s->mem_map->opaque = s; + s->mem_map->flush_tlb_write_range = riscv_flush_tlb_write_range; + + s->cpu_state = riscv_cpu_init(s->mem_map, max_xlen); + if (!s->cpu_state) { + vm_error("unsupported max_xlen=%d\n", max_xlen); + /* XXX: should free resources */ + return NULL; + } + /* RAM */ + ram_flags = 0; + cpu_register_ram(s->mem_map, RAM_BASE_ADDR, p->ram_size, ram_flags); + cpu_register_ram(s->mem_map, 0x00000000, LOW_RAM_SIZE, 0); + s->rtc_real_time = p->rtc_real_time; + if (p->rtc_real_time) { + s->rtc_start_time = rtc_get_real_time(s); + } + + cpu_register_device(s->mem_map, CLINT_BASE_ADDR, CLINT_SIZE, s, + clint_read, clint_write, DEVIO_SIZE32); + cpu_register_device(s->mem_map, PLIC_BASE_ADDR, PLIC_SIZE, s, + plic_read, plic_write, DEVIO_SIZE32); + for(i = 1; i < 32; i++) { + irq_init(&s->plic_irq[i], plic_set_irq, s, i); + } + + cpu_register_device(s->mem_map, HTIF_BASE_ADDR, 16, + s, htif_read, htif_write, DEVIO_SIZE32); + s->common.console = p->console; + + memset(vbus, 0, sizeof(*vbus)); + vbus->mem_map = s->mem_map; + vbus->addr = VIRTIO_BASE_ADDR; + irq_num = VIRTIO_IRQ; + + /* virtio console */ + if (p->console) { + vbus->irq = &s->plic_irq[irq_num]; + s->common.console_dev = virtio_console_init(vbus, p->console); + vbus->addr += VIRTIO_SIZE; + irq_num++; + s->virtio_count++; + } + + /* virtio net device */ + for(i = 0; i < p->eth_count; i++) { + vbus->irq = &s->plic_irq[irq_num]; + virtio_net_init(vbus, p->tab_eth[i].net); + s->common.net = p->tab_eth[i].net; + vbus->addr += VIRTIO_SIZE; + irq_num++; + s->virtio_count++; + } + + /* virtio block device */ + for(i = 0; i < p->drive_count; i++) { + vbus->irq = &s->plic_irq[irq_num]; + blk_dev = virtio_block_init(vbus, p->tab_drive[i].block_dev); + (void)blk_dev; + vbus->addr += VIRTIO_SIZE; + irq_num++; + s->virtio_count++; + } + + /* virtio filesystem */ + for(i = 0; i < p->fs_count; i++) { + VIRTIODevice *fs_dev; + vbus->irq = &s->plic_irq[irq_num]; + fs_dev = virtio_9p_init(vbus, p->tab_fs[i].fs_dev, + p->tab_fs[i].tag); + (void)fs_dev; + // virtio_set_debug(fs_dev, VIRTIO_DEBUG_9P); + vbus->addr += VIRTIO_SIZE; + irq_num++; + s->virtio_count++; + } + + if (p->display_device) { + FBDevice *fb_dev; + fb_dev = mallocz(sizeof(*fb_dev)); + s->common.fb_dev = fb_dev; + if (!strcmp(p->display_device, "simplefb")) { + simplefb_init(s->mem_map, + FRAMEBUFFER_BASE_ADDR, + fb_dev, + p->width, p->height); + + } else { + vm_error("unsupported display device: %s\n", p->display_device); + exit(1); + } + } + + if (p->input_device) { + if (!strcmp(p->input_device, "virtio")) { + vbus->irq = &s->plic_irq[irq_num]; + s->keyboard_dev = virtio_input_init(vbus, + VIRTIO_INPUT_TYPE_KEYBOARD); + vbus->addr += VIRTIO_SIZE; + irq_num++; + s->virtio_count++; + + vbus->irq = &s->plic_irq[irq_num]; + s->mouse_dev = virtio_input_init(vbus, + VIRTIO_INPUT_TYPE_TABLET); + vbus->addr += VIRTIO_SIZE; + irq_num++; + s->virtio_count++; + } else { + vm_error("unsupported input device: %s\n", p->input_device); + exit(1); + } + } + + if (!p->files[VM_FILE_BIOS].buf) { + vm_error("No bios found"); + } + + copy_bios(s, p->files[VM_FILE_BIOS].buf, p->files[VM_FILE_BIOS].len, + p->files[VM_FILE_KERNEL].buf, p->files[VM_FILE_KERNEL].len, + p->files[VM_FILE_INITRD].buf, p->files[VM_FILE_INITRD].len, + p->cmdline); + + return (VirtMachine *)s; +} + +static void riscv_machine_end(VirtMachine *s1) +{ + RISCVMachine *s = (RISCVMachine *)s1; + /* XXX: stop all */ + riscv_cpu_end(s->cpu_state); + phys_mem_map_end(s->mem_map); + free(s); +} + +/* in ms */ +static int riscv_machine_get_sleep_duration(VirtMachine *s1, int delay) +{ + RISCVMachine *m = (RISCVMachine *)s1; + RISCVCPUState *s = m->cpu_state; + int64_t delay1; + + /* wait for an event: the only asynchronous event is the RTC timer */ + if (!(riscv_cpu_get_mip(s) & MIP_MTIP)) { + delay1 = m->timecmp - rtc_get_time(m); + if (delay1 <= 0) { + riscv_cpu_set_mip(s, MIP_MTIP); + delay = 0; + } else { + /* convert delay to ms */ + delay1 = delay1 / (RTC_FREQ / 1000); + if (delay1 < delay) + delay = delay1; + } + } + if (!riscv_cpu_get_power_down(s)) + delay = 0; + return delay; +} + +static void riscv_machine_interp(VirtMachine *s1, int max_exec_cycle) +{ + RISCVMachine *s = (RISCVMachine *)s1; + riscv_cpu_interp(s->cpu_state, max_exec_cycle); +} + +static void riscv_vm_send_key_event(VirtMachine *s1, BOOL is_down, + uint16_t key_code) +{ + RISCVMachine *s = (RISCVMachine *)s1; + if (s->keyboard_dev) { + virtio_input_send_key_event(s->keyboard_dev, is_down, key_code); + } +} + +static BOOL riscv_vm_mouse_is_absolute(VirtMachine *s) +{ + return TRUE; +} + +static void riscv_vm_send_mouse_event(VirtMachine *s1, int dx, int dy, int dz, + unsigned int buttons) +{ + RISCVMachine *s = (RISCVMachine *)s1; + if (s->mouse_dev) { + virtio_input_send_mouse_event(s->mouse_dev, dx, dy, dz, buttons); + } +} + +const VirtMachineClass riscv_machine_class = { + "riscv32,riscv64,riscv128", + riscv_machine_set_defaults, + riscv_machine_init, + riscv_machine_end, + riscv_machine_get_sleep_duration, + riscv_machine_interp, + riscv_vm_mouse_is_absolute, + riscv_vm_send_mouse_event, + riscv_vm_send_key_event, +}; |
