-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
virtio support #149
Comments
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 |
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. |
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 #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);
} |
|
ok i just finished getting RVVM and its deps building for android (again) in the correct order previously when building i got so i had to rely on my additionally i had to add install steps and find*.cmake scripts to rvvm in order to support finding rvvm deps (one can set additionally i added a remote shell ( |
onnokort/semu-c64@21dc47d seems to be a very simple example of how to add a virtio-blk device (implementation) to semu |
Originally posted by @ZLangJIT in #144 (comment)
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 sysprog21/semu#34) to rvvm
currently compiles with minimal warnings
The text was updated successfully, but these errors were encountered: