/* * 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 #include #include #include #include #include #include #include #include #include #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, };