Open ZLangJIT opened 1 month ago
note however virtio is not hooked up to rvvm itself yet
also im not sure what the rvvm version of these would be as some exist but others dont
enum {
RV_MEM_LB = 0b000,
RV_MEM_LH = 0b001,
RV_MEM_LW = 0b010,
RV_MEM_LBU = 0b100,
RV_MEM_LHU = 0b101,
RV_MEM_SB = 0b000,
RV_MEM_SH = 0b001,
RV_MEM_SW = 0b010,
};
Load/store memory instructions specifics should be ignored from device perspective.
RVVM MMIO API can be seen in src/rvvmlib.h
, and is well documented.
for such specifics we have
void virtio_blk_read(rvvm_hart_t *vm,
virtio_blk_state_t *vblk,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
switch (width) {
case RV_MEM_LW:
if (!virtio_blk_reg_read(vblk, addr >> 2, value))
riscv_trap(vm, TRAP_LOAD_FAULT, vm->registers[REGISTER_PC]);
break;
case RV_MEM_LBU:
case RV_MEM_LB:
case RV_MEM_LHU:
case RV_MEM_LH:
riscv_trap(vm, TRAP_LOAD_MISALIGN, vm->registers[REGISTER_PC]);
return;
default:
riscv_trap(vm, TRAP_ILL_INSTR, 0);
return;
}
}
void virtio_blk_write(rvvm_hart_t *vm,
virtio_blk_state_t *vblk,
uint32_t addr,
uint8_t width,
uint32_t value)
{
switch (width) {
case RV_MEM_SW:
if (!virtio_blk_reg_write(vblk, addr >> 2, value))
riscv_trap(vm, TRAP_STORE_FAULT, vm->registers[REGISTER_PC]);
break;
case RV_MEM_SB:
case RV_MEM_SH:
riscv_trap(vm, TRAP_STORE_MISALIGN, vm->registers[REGISTER_PC]);
return;
default:
riscv_trap(vm, TRAP_ILL_INSTR, 0);
return;
}
}
void virtio_gpu_read(rvvm_hart_t *vm,
virtio_gpu_state_t *vgpu,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
switch (width) {
case RV_MEM_LW:
if (!virtio_gpu_reg_read(vgpu, addr >> 2, value))
riscv_trap(vm, TRAP_LOAD_FAULT, vm->registers[REGISTER_PC]);
break;
case RV_MEM_LBU:
case RV_MEM_LB:
case RV_MEM_LHU:
case RV_MEM_LH:
riscv_trap(vm, TRAP_LOAD_MISALIGN, vm->registers[REGISTER_PC]);
return;
default:
riscv_trap(vm, TRAP_ILL_INSTR, 0);
return;
}
}
void virtio_gpu_write(rvvm_hart_t *vm,
virtio_gpu_state_t *vgpu,
uint32_t addr,
uint8_t width,
uint32_t value)
{
switch (width) {
case RV_MEM_SW:
if (!virtio_gpu_reg_write(vgpu, addr >> 2, value))
riscv_trap(vm, TRAP_STORE_FAULT, vm->registers[REGISTER_PC]);
break;
case RV_MEM_SB:
case RV_MEM_SH:
riscv_trap(vm, TRAP_STORE_MISALIGN, vm->registers[REGISTER_PC]);
return;
default:
riscv_trap(vm, TRAP_ILL_INSTR, 0);
return;
}
}
void virtio_input_read(rvvm_hart_t *vm,
virtio_input_state_t *vinput,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
pthread_mutex_lock(&virtio_input_mutex);
/* XXX: 4-byte alignment (i.e., addr >> 2) is removed due to the per
byte accessing */
switch (width) {
case RV_MEM_LW:
if (!virtio_input_reg_read(vinput, addr, value, 4))
riscv_trap(vm, TRAP_LOAD_FAULT, vm->registers[REGISTER_PC]);
break;
case RV_MEM_LBU:
case RV_MEM_LB:
case RV_MEM_LHU:
case RV_MEM_LH:
/*FIXME: virtio-input driver need to access device config register per
* byte. the following code that derived from other virtio devices'
* implementation will cause kernel panic */
// riscv_trap(vm, TRAP_LOAD_MISALIGN, vm->registers[REGISTER_PC]);
#if 1
// printf("read addr: 0x%x, width: %d\n", addr, width);
if (!virtio_input_reg_read(vinput, addr, value, 1))
riscv_trap(vm, TRAP_LOAD_FAULT, vm->registers[REGISTER_PC]);
#endif
break;
default:
riscv_trap(vm, TRAP_ILL_INSTR, 0);
break;
}
pthread_mutex_unlock(&virtio_input_mutex);
}
void virtio_input_write(rvvm_hart_t *vm,
virtio_input_state_t *vinput,
uint32_t addr,
uint8_t width,
uint32_t value)
{
pthread_mutex_lock(&virtio_input_mutex);
/* XXX: 4-byte alignment (i.e., addr >> 2) is removed due to the per
byte accessing */
switch (width) {
case RV_MEM_SW:
if (!virtio_input_reg_write(vinput, addr, value))
riscv_trap(vm, TRAP_STORE_FAULT, vm->registers[REGISTER_PC]);
break;
case RV_MEM_SB:
case RV_MEM_SH:
/* FIXME: virtio-input driver need to access device config register per
* byte. the following code that derived from other virtio devices'
* implementation will cause kernel panic */
// riscv_trap(vm, TRAP_STORE_MISALIGN, vm->registers[REGISTER_PC]);
#if 1
// printf("read addr: 0x%x, width: %d\n", addr, width);
if (!virtio_input_reg_write(vinput, addr, value))
riscv_trap(vm, TRAP_STORE_FAULT, vm->registers[REGISTER_PC]);
#endif
break;
default:
riscv_trap(vm, TRAP_ILL_INSTR, 0);
break;
}
pthread_mutex_unlock(&virtio_input_mutex);
}
void virtio_net_read(rvvm_hart_t *vm,
virtio_net_state_t *vnet,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
switch (width) {
case RV_MEM_LW:
if (!virtio_net_reg_read(vnet, addr >> 2, value))
riscv_trap(vm, TRAP_LOAD_FAULT, vm->registers[REGISTER_PC]);
break;
case RV_MEM_LBU:
case RV_MEM_LB:
case RV_MEM_LHU:
case RV_MEM_LH:
riscv_trap(vm, TRAP_LOAD_MISALIGN, vm->registers[REGISTER_PC]);
return;
default:
riscv_trap(vm, TRAP_ILL_INSTR, 0);
return;
}
}
void virtio_net_write(rvvm_hart_t *vm,
virtio_net_state_t *vnet,
uint32_t addr,
uint8_t width,
uint32_t value)
{
switch (width) {
case RV_MEM_SW:
if (!virtio_net_reg_write(vnet, addr >> 2, value))
riscv_trap(vm, TRAP_STORE_FAULT, vm->registers[REGISTER_PC]);
break;
case RV_MEM_SB:
case RV_MEM_SH:
riscv_trap(vm, TRAP_STORE_MISALIGN, vm->registers[REGISTER_PC]);
return;
default:
riscv_trap(vm, TRAP_ILL_INSTR, 0);
return;
}
}
Please see the RVVM API and only then try to adapt anything.
Devices should not touch CPU specifics directly, like load/store instruction opcodes, registers. Devices should not trap the CPU manually either, it is done by the MMU subsystem.
If semu project did it that way it's silly and should not be replicated here.
Please see the RVVM API and only then try to adapt anything.
Devices should not touch CPU specifics directly, like load/store instruction opcodes, registers. Devices should not trap the CPU manually either, it is done by the MMU subsystem.
If semu project did it that way it's silly and should not be replicated here.
yea, but i will need to trace exactly how such api's get called so we can then decide how best to adapt each one
in main.c of semu/bb98469f47b09324561970e91c49a58ab038a176
we currently have these references of virtio
and virgl
#if SEMU_HAS(VIRTIONET)
static void emu_update_vnet_interrupts(vm_t *vm)
{
emu_state_t *data = PRIV(vm->hart[0]);
if (data->vnet.InterruptStatus)
data->plic.active |= IRQ_VNET_BIT;
else
data->plic.active &= ~IRQ_VNET_BIT;
plic_update_interrupts(vm, &data->plic);
}
#endif
#if SEMU_HAS(VIRTIOBLK)
static void emu_update_vblk_interrupts(vm_t *vm)
{
emu_state_t *data = PRIV(vm->hart[0]);
if (data->vblk.InterruptStatus)
data->plic.active |= IRQ_VBLK_BIT;
else
data->plic.active &= ~IRQ_VBLK_BIT;
plic_update_interrupts(vm, &data->plic);
}
#endif
#if SEMU_HAS(VIRTIOGPU)
static void emu_update_vgpu_interrupts(vm_t *vm)
{
emu_state_t *data = PRIV(vm->hart[0]);
if (data->vgpu.InterruptStatus)
data->plic.active |= IRQ_VGPU_BIT;
else
data->plic.active &= ~IRQ_VGPU_BIT;
plic_update_interrupts(vm, &data->plic);
}
#endif
#if SEMU_HAS(VIRTIOINPUT)
static void emu_update_vinput_keyboard_interrupts(vm_t *vm)
{
emu_state_t *data = PRIV(vm->hart[0]);
if (data->vkeyboard.InterruptStatus)
data->plic.active |= IRQ_VINPUT_KEYBOARD_BIT;
else
data->plic.active &= ~IRQ_VINPUT_KEYBOARD_BIT;
plic_update_interrupts(vm, &data->plic);
}
static void emu_update_vinput_mouse_interrupts(vm_t *vm)
{
emu_state_t *data = PRIV(vm->hart[0]);
if (data->vmouse.InterruptStatus)
data->plic.active |= IRQ_VINPUT_MOUSE_BIT;
else
data->plic.active &= ~IRQ_VINPUT_MOUSE_BIT;
plic_update_interrupts(vm, &data->plic);
}
#endif
// ...
static void mem_load(hart_t *hart,
uint32_t addr,
uint8_t width,
uint32_t *value)
{
emu_state_t *data = PRIV(hart);
/* RAM at 0x00000000 + RAM_SIZE */
if (addr < RAM_SIZE) {
ram_read(hart, data->ram, addr, width, value);
return;
}
if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */
/* 256 regions of 1MiB */
switch ((addr >> 20) & MASK(8)) {
case 0x0:
case 0x2: /* PLIC (0 - 0x3F) */
plic_read(hart, &data->plic, addr & 0x3FFFFFF, width, value);
plic_update_interrupts(hart->vm, &data->plic);
return;
case 0x40: /* UART */
u8250_read(hart, &data->uart, addr & 0xFFFFF, width, value);
emu_update_uart_interrupts(hart->vm);
return;
#if SEMU_HAS(VIRTIONET)
case 0x41: /* virtio-net */
virtio_net_read(hart, &data->vnet, addr & 0xFFFFF, width, value);
emu_update_vnet_interrupts(hart->vm);
return;
#endif
#if SEMU_HAS(VIRTIOBLK)
case 0x42: /* virtio-blk */
virtio_blk_read(hart, &data->vblk, addr & 0xFFFFF, width, value);
emu_update_vblk_interrupts(hart->vm);
return;
#endif
case 0x43: /* clint */
clint_read(hart, &data->clint, addr & 0xFFFFF, width, value);
clint_update_interrupts(hart, &data->clint);
return;
#if SEMU_HAS(VIRTIOGPU)
case 0x44: /* virtio-gpu */
virtio_gpu_read(hart, &data->vgpu, addr & 0xFFFFF, width, value);
emu_update_vgpu_interrupts(hart->vm);
return;
#endif
#if SEMU_HAS(VIRTIOINPUT)
case 0x45: /* virtio-input keyboard */
virtio_input_read(hart, &data->vkeyboard, addr & 0xFFFFF, width,
value);
emu_update_vinput_keyboard_interrupts(hart->vm);
return;
case 0x46: /* virtio-input mouse */
virtio_input_read(hart, &data->vmouse, addr & 0xFFFFF, width,
value);
emu_update_vinput_mouse_interrupts(hart->vm);
return;
#endif
}
}
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);
}
static void mem_store(hart_t *hart,
uint32_t addr,
uint8_t width,
uint32_t value)
{
emu_state_t *data = PRIV(hart);
/* RAM at 0x00000000 + RAM_SIZE */
if (addr < RAM_SIZE) {
ram_write(hart, data->ram, addr, width, value);
return;
}
if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */
/* 256 regions of 1MiB */
switch ((addr >> 20) & MASK(8)) {
case 0x0:
case 0x2: /* PLIC (0 - 0x3F) */
plic_write(hart, &data->plic, addr & 0x3FFFFFF, width, value);
plic_update_interrupts(hart->vm, &data->plic);
return;
case 0x40: /* UART */
u8250_write(hart, &data->uart, addr & 0xFFFFF, width, value);
emu_update_uart_interrupts(hart->vm);
return;
#if SEMU_HAS(VIRTIONET)
case 0x41: /* virtio-net */
virtio_net_write(hart, &data->vnet, addr & 0xFFFFF, width, value);
emu_update_vnet_interrupts(hart->vm);
return;
#endif
#if SEMU_HAS(VIRTIOBLK)
case 0x42: /* virtio-blk */
virtio_blk_write(hart, &data->vblk, addr & 0xFFFFF, width, value);
emu_update_vblk_interrupts(hart->vm);
return;
#endif
case 0x43: /* clint */
clint_write(hart, &data->clint, addr & 0xFFFFF, width, value);
clint_update_interrupts(hart, &data->clint);
return;
#if SEMU_HAS(VIRTIOGPU)
case 0x44: /* virtio-gpu */
virtio_gpu_write(hart, &data->vgpu, addr & 0xFFFFF, width, value);
emu_update_vgpu_interrupts(hart->vm);
return;
#endif
#if SEMU_HAS(VIRTIOINPUT)
case 0x45: /* virtio-input */
virtio_input_write(hart, &data->vkeyboard, addr & 0xFFFFF, width,
value);
emu_update_vinput_keyboard_interrupts(hart->vm);
return;
case 0x46: /* virtio-input mouse */
virtio_input_write(hart, &data->vmouse, addr & 0xFFFFF, width,
value);
emu_update_vinput_mouse_interrupts(hart->vm);
return;
#endif
}
}
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
}
// ...
static int semu_start(int argc, char **argv)
{
char *kernel_file;
char *dtb_file;
char *initrd_file;
char *disk_file;
int hart_count = 1;
handle_options(argc, argv, &kernel_file, &dtb_file, &initrd_file,
&disk_file, &hart_count);
/* Initialize the emulator */
emu_state_t emu;
memset(&emu, 0, sizeof(emu));
semu_timer_init(&emu.clint.mtime, CLOCK_FREQ);
/* Set up RAM */
emu.ram = mmap(NULL, RAM_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (emu.ram == MAP_FAILED) {
fprintf(stderr, "Could not map RAM\n");
return 2;
}
assert(!(((uintptr_t) emu.ram) & 0b11));
/* *-----------------------------------------*
* | Memory layout |
* *----------------*----------------*-------*
* | kernel image | initrd image | dtb |
* *----------------*----------------*-------*
*/
char *ram_loc = (char *) emu.ram;
/* Load Linux kernel image */
map_file(&ram_loc, kernel_file);
/* Load at last 1 MiB to prevent kernel from overwriting it */
uint32_t dtb_addr = RAM_SIZE - DTB_SIZE; /* Device tree */
ram_loc = ((char *) emu.ram) + dtb_addr;
map_file(&ram_loc, dtb_file);
/* Load optional initrd image at last 8 MiB before the dtb region to
* prevent kernel from overwritting it
*/
if (initrd_file) {
uint32_t initrd_addr = dtb_addr - INITRD_SIZE; /* Init RAM disk */
ram_loc = ((char *) emu.ram) + initrd_addr;
map_file(&ram_loc, initrd_file);
}
/* Hook for unmapping files */
atexit(unmap_files);
/* Set up RISC-V harts */
vm_t vm = {
.n_hart = hart_count,
.hart = malloc(sizeof(hart_t *) * vm.n_hart),
};
for (uint32_t i = 0; i < vm.n_hart; i++) {
hart_t *newhart = malloc(sizeof(hart_t));
INIT_HART(newhart, &emu, i);
newhart->x_regs[RV_R_A0] = i;
newhart->x_regs[RV_R_A1] = dtb_addr;
if (i == 0)
newhart->hsm_status = SBI_HSM_STATE_STARTED;
newhart->vm = &vm;
vm.hart[i] = newhart;
}
/* Set up peripherals */
emu.uart.in_fd = 0, emu.uart.out_fd = 1;
capture_keyboard_input(); /* set up uart */
#if SEMU_HAS(VIRTIONET)
if (!virtio_net_init(&(emu.vnet)))
fprintf(stderr, "No virtio-net functioned\n");
emu.vnet.ram = emu.ram;
#endif
#if SEMU_HAS(VIRTIOBLK)
emu.vblk.ram = emu.ram;
emu.disk = virtio_blk_init(&(emu.vblk), disk_file);
#endif
#if SEMU_HAS(VIRTIOINPUT)
emu.vkeyboard.ram = emu.ram;
virtio_input_init(&(emu.vkeyboard));
emu.vmouse.ram = emu.ram;
virtio_input_init(&(emu.vmouse));
#endif
#if SEMU_HAS(VIRTIOGPU)
semu_virgl_init();
emu.vgpu.ram = emu.ram;
virtio_gpu_init(&(emu.vgpu));
virtio_gpu_add_scanout(&(emu.vgpu), 1024, 768);
window_init();
#endif
/* Emulate */
uint32_t peripheral_update_ctr = 0;
while (!emu.stopped) {
for (uint32_t i = 0; i < vm.n_hart; i++) {
if (peripheral_update_ctr-- == 0) {
peripheral_update_ctr = 64;
u8250_check_ready(&emu.uart);
if (emu.uart.in_ready)
emu_update_uart_interrupts(&vm);
#if SEMU_HAS(VIRTIONET)
virtio_net_refresh_queue(&emu.vnet);
if (emu.vnet.InterruptStatus)
emu_update_vnet_interrupts(&vm);
#endif
#if SEMU_HAS(VIRTIOBLK)
if (emu.vblk.InterruptStatus)
emu_update_vblk_interrupts(&vm);
#endif
#if SEMU_HAS(VIRTIOGPU)
if (emu.vgpu.InterruptStatus)
emu_update_vgpu_interrupts(&vm);
#endif
#if SEMU_HAS(VIRTIOINPUT)
if (emu.vkeyboard.InterruptStatus)
emu_update_vinput_keyboard_interrupts(&vm);
if (emu.vmouse.InterruptStatus)
emu_update_vinput_mouse_interrupts(&vm);
#endif
}
emu_update_timer_interrupt(vm.hart[i]);
vm_step(vm.hart[i]);
if (likely(!vm.hart[i]->error))
continue;
if (vm.hart[i]->error == ERR_EXCEPTION &&
vm.hart[i]->exc_cause == RV_EXC_ECALL_S) {
handle_sbi_ecall(vm.hart[i]);
continue;
}
if (vm.hart[i]->error == ERR_EXCEPTION) {
hart_trap(vm.hart[i]);
continue;
}
vm_error_report(vm.hart[i]);
return 2;
}
}
/* unreachable */
return 0;
}
int main(int argc, char **argv)
{
return semu_start(argc, argv);
}
https://github.com/sysprog21/semu/issues/32 may help shed some light on the inner workings of how this is meant to work from semu's / Linux Kernel's perspective
additionally https://github.com/sysprog21/semu/pull/53 appears to be a PR for virtio sound
additionally https://github.com/sysprog21/semu/pull/23 and https://github.com/sysprog21/semu/pull/21
ok i just finished getting RVVM and its deps building for android (again) in the correct order
previously when building i got format nor supported
when linking rvvm and friends
so i had to rely on my build_root
utility (also add meson support to it and fix some bugs in it) to get it all to correctly compile and install
additionally i had to add install steps and find*.cmake scripts to rvvm in order to support finding rvvm deps (one can set RVVM__ROOTFS
variable when building to change the location at which rvvm looks for deps if they are not installed to /
) and installation of rvvm via cmake --install path/to/rvvm/build_dir
additionally i added a remote shell (tmate
) to my build.yaml (currently commented out) for attempting to debug gradle cmake build failures
https://github.com/onnokort/semu-c64/commit/21dc47de085fb4dbf55d78de42f9e7927122a8f5 seems to be a very simple example of how to add a virtio-blk device (implementation) to semu
Originally posted by @ZLangJIT in https://github.com/LekKit/RVVM/issues/144#issuecomment-2393013854
virtio
itself covers much more than justvirtio-gpu
attached is a patch that introduces initial support for virtio ported from https://github.com/shengwen-tw/semu/tree/master (from https://github.com/sysprog21/semu/pull/34) to rvvm
currently compiles with minimal warnings