Skip to content

Commit

Permalink
Implement virtio-input device
Browse files Browse the repository at this point in the history
  • Loading branch information
shengwen-tw committed Aug 17, 2024
1 parent dd94f21 commit 1ae6101
Show file tree
Hide file tree
Showing 8 changed files with 891 additions and 5 deletions.
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ ifeq ($(call has, VIRTIONET), 1)
OBJS_EXTRA += virtio-net.o
endif

# virtio-input
ENABLE_VIRTIOINPUT ?= 1
ifneq ($(UNAME_S),Linux)
ENABLE_VIRTIOINPUT := 0
endif
$(call set-feature, VIRTIOINPUT)
ifeq ($(call has, VIRTIOINPUT), 1)
OBJS_EXTRA += virtio-input.o
endif

# virtio-gpu
ENABLE_VIRTIOGPU ?= 1
ifneq ($(UNAME_S),Linux)
Expand Down
67 changes: 67 additions & 0 deletions bitops.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#ifndef __BITOPS_H__
#define __BITOPS_H__

#include <sys/param.h>

#define BITS_PER_CHAR 8
#define BITS_PER_LONG (BITS_PER_CHAR * sizeof(long))

/* Find last bit set:
* the index of the last bit that's set, or 0 if value is zero.
*/
static inline unsigned long _flsl(unsigned long word)
{
return word ? sizeof(long) * BITS_PER_CHAR - __builtin_clz(word) : 0;
}

static inline void clear_bit(unsigned long bit, unsigned long *word)
{
*word &= ~(1 << bit);
}

static inline void set_bit(unsigned long bit, unsigned long *word)
{
*word |= (1 << bit);
}

static inline void bitmap_clear_bit(unsigned long *map, unsigned long bit)
{
clear_bit(bit % BITS_PER_LONG, &map[bit / BITS_PER_LONG]);
}

static inline void bitmap_set_bit(unsigned long *map, unsigned long bit)
{
set_bit(bit % BITS_PER_LONG, &map[bit / BITS_PER_LONG]);
}

static inline unsigned long bitmap_get_bit(unsigned long *map,
unsigned long bit)
{
return map[bit / BITS_PER_LONG] >> (bit % BITS_PER_LONG) & 1;
}

static inline unsigned long find_first_bit(const unsigned long *addr,
unsigned long size)
{
for (unsigned long i = 0; i * BITS_PER_LONG < size; i++) {
if (addr[i]) {
return MIN(i * BITS_PER_LONG + __builtin_ffsl(addr[i]) - 1, size);
}
}

return size;
}

static inline unsigned long find_first_zero_bit(const unsigned long *addr,
unsigned long size)
{
for (unsigned long i = 0; i * BITS_PER_LONG < size; i++) {
if (addr[i] != ~0ul) {
return MIN(i * BITS_PER_LONG + __builtin_ffsl(~addr[i]) - 1, size);
}
}

return size;
}

#endif
60 changes: 60 additions & 0 deletions device.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,62 @@ void virtio_gpu_add_scanout(virtio_gpu_state_t *vgpu,
uint32_t height);
#endif /* SEMU_HAS(VIRTIOGPU) */

/* VirtIO Input */

#if SEMU_HAS(VIRTIOINPUT)

#define IRQ_VINPUT_KEYBOARD 5
#define IRQ_VINPUT_KEYBOARD_BIT (1 << IRQ_VINPUT_KEYBOARD)

#define IRQ_VINPUT_MOUSE 6
#define IRQ_VINPUT_MOUSE_BIT (1 << IRQ_VINPUT_MOUSE)

typedef struct {
uint32_t QueueNum;
uint32_t QueueDesc;
uint32_t QueueAvail;
uint32_t QueueUsed;
uint16_t last_avail;
bool ready;
} virtio_input_queue_t;

typedef struct {
/* feature negotiation */
uint32_t DeviceFeaturesSel;
uint32_t DriverFeatures;
uint32_t DriverFeaturesSel;
/* queue config */
uint32_t QueueSel;
virtio_input_queue_t queues[2];
/* status */
uint32_t Status;
uint32_t InterruptStatus;
/* supplied by environment */
uint32_t *ram;
/* implementation-specific */
int id; // FIXME
void *priv;
} virtio_input_state_t;

void virtio_input_read(hart_t *vm,
virtio_input_state_t *vinput,
uint32_t addr,
uint8_t width,
uint32_t *value);

void virtio_input_write(hart_t *vm,
virtio_input_state_t *vinput,
uint32_t addr,
uint8_t width,
uint32_t value);

void virtio_input_init(virtio_input_state_t *vinput);

void virtio_input_update_cursor(uint32_t x, uint32_t y);

void virtio_input_update_key(uint32_t key, uint32_t state);
#endif /* SEMU_HAS(VIRTIOINPUT) */

/* clint */
typedef struct {
uint32_t msip[4096];
Expand Down Expand Up @@ -257,6 +313,10 @@ typedef struct {
#endif
#if SEMU_HAS(VIRTIOGPU)
virtio_gpu_state_t vgpu;
#endif
#if SEMU_HAS(VIRTIOINPUT)
virtio_input_state_t vkeyboard;
virtio_input_state_t vmouse;
#endif
clint_state_t clint;
} emu_state_t;
5 changes: 5 additions & 0 deletions feature.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@
#define SEMU_FEATUREVIRTIOGPU 1
#endif

/* virtio-input */
#ifndef SEMU_FEATUREVIRTIOINPUT
#define SEMU_FEATUREVIRTIOINPUT 1
#endif

/* Feature test macro */
#define SEMU_HAS(x) SEMU_FEATURE_##x
61 changes: 61 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,28 @@ static void emu_update_vgpu_interrupts(vm_t *vm)
}
#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 emu_update_timer_interrupt(hart_t *hart)
{
emu_state_t *data = PRIV(hart);
Expand Down Expand Up @@ -139,6 +161,18 @@ static void mem_load(hart_t *hart,
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
}
}
Expand Down Expand Up @@ -190,6 +224,18 @@ static void mem_store(hart_t *hart,
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
}
}
Expand Down Expand Up @@ -606,6 +652,13 @@ static int semu_start(int argc, char **argv)
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)
emu.vgpu.ram = emu.ram;
virtio_gpu_init(&(emu.vgpu));
Expand Down Expand Up @@ -639,6 +692,14 @@ static int semu_start(int argc, char **argv)
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]);
Expand Down
14 changes: 14 additions & 0 deletions minimal.dts
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,19 @@
interrupts = <4>;
};
#endif

#if SEMU_FEATURE_VIRTIOINPUT
keyboard0: virtio@4500000 {
compatible = "virtio,mmio";
reg = <0x4500000 0x200>;
interrupts = <5>;
};

mouse0: virtio@4600000 {
compatible = "virtio,mmio";
reg = <0x4600000 0x200>;
interrupts = <6>;
};
#endif
};
};
Loading

0 comments on commit 1ae6101

Please sign in to comment.