Skip to content

Commit b653a41

Browse files
committed
Implement virtio-gpu device
1 parent 36fc1b2 commit b653a41

File tree

10 files changed

+1874
-2
lines changed

10 files changed

+1874
-2
lines changed

Makefile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ include mk/common.mk
33
CC ?= gcc
44
CFLAGS := -O2 -g -Wall -Wextra
55
CFLAGS += -include common.h
6+
LDFLAGS :=
67

78
# clock frequency
89
CLOCK_FREQ ?= 65000000
@@ -13,6 +14,8 @@ OBJS_EXTRA :=
1314
# command line option
1415
OPTS :=
1516

17+
LDFLAGS += -lpthread
18+
1619
# virtio-blk
1720
ENABLE_VIRTIOBLK ?= 1
1821
$(call set-feature, VIRTIOBLK)
@@ -43,6 +46,33 @@ ifeq ($(call has, VIRTIONET), 1)
4346
OBJS_EXTRA += netdev.o
4447
endif
4548

49+
# virtio-gpu
50+
ENABLE_VIRTIOGPU ?= 1
51+
ifneq ($(UNAME_S),Linux)
52+
ENABLE_VIRTIOGPU := 0
53+
endif
54+
55+
# SDL2
56+
ENABLE_SDL ?= 1
57+
ifeq (, $(shell which sdl2-config))
58+
$(warning No sdl2-config in $$PATH. Check SDL2 installation in advance)
59+
override ENABLE_SDL := 0
60+
endif
61+
62+
ifeq ($(ENABLE_SDL),1)
63+
CFLAGS += $(shell sdl2-config --cflags)
64+
LDFLAGS += $(shell sdl2-config --libs)
65+
else
66+
override ENABLE_VIRTIOGPU := 0
67+
endif
68+
69+
ifeq ($(ENABLE_VIRTIOGPU),1)
70+
OBJS_EXTRA += window.o
71+
OBJS_EXTRA += virtio-gpu.o
72+
endif
73+
74+
$(call set-feature, VIRTIOGPU)
75+
4676
BIN = semu
4777
all: $(BIN) minimal.dtb
4878

device.h

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
#define RAM_SIZE (512 * 1024 * 1024)
1010
#define DTB_SIZE (1 * 1024 * 1024)
11-
#define INITRD_SIZE (8 * 1024 * 1024)
11+
#define INITRD_SIZE (65 * 1024 * 1024)
1212

1313
void ram_read(hart_t *core,
1414
uint32_t *mem,
@@ -172,6 +172,57 @@ void virtio_blk_write(hart_t *vm,
172172
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
173173
#endif /* SEMU_HAS(VIRTIOBLK) */
174174

175+
/* VirtIO-GPU */
176+
177+
#if SEMU_HAS(VIRTIOGPU)
178+
179+
#define IRQ_VGPU 4
180+
#define IRQ_VGPU_BIT (1 << IRQ_VGPU)
181+
182+
typedef struct {
183+
uint32_t QueueNum;
184+
uint32_t QueueDesc;
185+
uint32_t QueueAvail;
186+
uint32_t QueueUsed;
187+
uint16_t last_avail;
188+
bool ready;
189+
} virtio_gpu_queue_t;
190+
191+
typedef struct {
192+
/* feature negotiation */
193+
uint32_t DeviceFeaturesSel;
194+
uint32_t DriverFeatures;
195+
uint32_t DriverFeaturesSel;
196+
/* queue config */
197+
uint32_t QueueSel;
198+
virtio_gpu_queue_t queues[2];
199+
/* status */
200+
uint32_t Status;
201+
uint32_t InterruptStatus;
202+
/* supplied by environment */
203+
uint32_t *ram;
204+
/* implementation-specific */
205+
void *priv;
206+
} virtio_gpu_state_t;
207+
208+
void virtio_gpu_read(hart_t *vm,
209+
virtio_gpu_state_t *vgpu,
210+
uint32_t addr,
211+
uint8_t width,
212+
uint32_t *value);
213+
214+
void virtio_gpu_write(hart_t *vm,
215+
virtio_gpu_state_t *vgpu,
216+
uint32_t addr,
217+
uint8_t width,
218+
uint32_t value);
219+
220+
void virtio_gpu_init(virtio_gpu_state_t *vgpu);
221+
void virtio_gpu_add_scanout(virtio_gpu_state_t *vgpu,
222+
uint32_t width,
223+
uint32_t height);
224+
#endif /* SEMU_HAS(VIRTIOGPU) */
225+
175226
/* ACLINT MTIMER */
176227
typedef struct {
177228
/* A MTIMER device has two separate base addresses: one for the MTIME
@@ -272,6 +323,9 @@ typedef struct {
272323
#endif
273324
#if SEMU_HAS(VIRTIOBLK)
274325
virtio_blk_state_t vblk;
326+
#endif
327+
#if SEMU_HAS(VIRTIOGPU)
328+
virtio_gpu_state_t vgpu;
275329
#endif
276330
/* ACLINT */
277331
mtimer_state_t mtimer;

feature.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,10 @@
1212
#define SEMU_FEATURE_VIRTIONET 1
1313
#endif
1414

15+
/* virtio-gpu */
16+
#ifndef SEMU_FEATURE_VIRTIOGPU
17+
#define SEMU_FEATURE_VIRTIOGPU 1
18+
#endif
19+
1520
/* Feature test macro */
1621
#define SEMU_HAS(x) SEMU_FEATURE_##x

list.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#pragma once
2+
3+
#include <stddef.h>
4+
5+
#define container_of(ptr, type, member) \
6+
((type *) ((void *) ptr - offsetof(type, member)))
7+
8+
#define list_entry(ptr, type, member) container_of(ptr, type, member)
9+
10+
#define list_first_entry(ptr, type, member) \
11+
list_entry((ptr)->next, type, member)
12+
13+
#define list_prev_entry(pos, member) \
14+
list_entry((pos)->member.prev, typeof(*(pos)), member)
15+
16+
#define list_next_entry(pos, member) \
17+
list_entry((pos)->member.next, typeof(*(pos)), member)
18+
19+
#define list_entry_is_head(pos, head, member) (&pos->member == (head))
20+
21+
#define list_for_each(pos, head) \
22+
for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next)
23+
24+
#define list_for_each_safe(pos, _next, head) \
25+
for (pos = (head)->next, _next = (pos)->next; (pos) != (head); \
26+
(pos) = _next, _next = (pos)->next)
27+
28+
#define list_for_each_entry(pos, head, member) \
29+
for (pos = list_first_entry(head, __typeof__(*pos), member); \
30+
&pos->member != (head); pos = list_next_entry(pos, member))
31+
32+
#define LIST_HEAD_INIT(name) \
33+
{ \
34+
.prev = (&name), .next = (&name) \
35+
}
36+
37+
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
38+
39+
struct list_head {
40+
struct list_head *next, *prev;
41+
};
42+
43+
static inline void INIT_LIST_HEAD(struct list_head *list)
44+
{
45+
list->prev = list;
46+
list->next = list;
47+
}
48+
49+
static inline int list_empty(const struct list_head *head)
50+
{
51+
return head->next == head;
52+
}
53+
54+
static int list_is_last(const struct list_head *list,
55+
const struct list_head *head)
56+
{
57+
return list->next == head;
58+
}
59+
60+
static inline void list_add(struct list_head *new, struct list_head *list)
61+
{
62+
new->prev = list->prev;
63+
new->next = list;
64+
list->prev->next = new;
65+
list->prev = new;
66+
}
67+
68+
static inline void list_del(struct list_head *list)
69+
{
70+
list->next->prev = list->prev;
71+
list->prev->next = list->next;
72+
}
73+
74+
static void list_del_init(struct list_head *entry)
75+
{
76+
list_del(entry);
77+
INIT_LIST_HEAD(entry);
78+
}
79+
80+
static inline void list_move(struct list_head *list, struct list_head *new_head)
81+
{
82+
list_del(list);
83+
list_add(new_head, list);
84+
}

main.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "device.h"
1212
#include "riscv.h"
1313
#include "riscv_private.h"
14+
#include "window.h"
1415

1516
#define PRIV(x) ((emu_state_t *) x->priv)
1617

@@ -72,6 +73,18 @@ static void emu_update_vblk_interrupts(vm_t *vm)
7273
}
7374
#endif
7475

76+
#if SEMU_HAS(VIRTIOGPU)
77+
static void emu_update_vgpu_interrupts(vm_t *vm)
78+
{
79+
emu_state_t *data = PRIV(vm->hart[0]);
80+
if (data->vgpu.InterruptStatus)
81+
data->plic.active |= IRQ_VGPU_BIT;
82+
else
83+
data->plic.active &= ~IRQ_VGPU_BIT;
84+
plic_update_interrupts(vm, &data->plic);
85+
}
86+
#endif
87+
7588
static void emu_update_timer_interrupt(hart_t *hart)
7689
{
7790
emu_state_t *data = PRIV(hart);
@@ -137,6 +150,12 @@ static void mem_load(hart_t *hart,
137150
aclint_sswi_read(hart, &data->sswi, addr & 0xFFFFF, width, value);
138151
aclint_sswi_update_interrupts(hart, &data->sswi);
139152
return;
153+
#if SEMU_HAS(VIRTIOGPU)
154+
case 0x46: /* virtio-gpu */
155+
virtio_gpu_read(hart, &data->vgpu, addr & 0xFFFFF, width, value);
156+
emu_update_vgpu_interrupts(hart->vm);
157+
return;
158+
#endif
140159
}
141160
}
142161
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);
@@ -191,6 +210,12 @@ static void mem_store(hart_t *hart,
191210
aclint_sswi_write(hart, &data->sswi, addr & 0xFFFFF, width, value);
192211
aclint_sswi_update_interrupts(hart, &data->sswi);
193212
return;
213+
#if SEMU_HAS(VIRTIOGPU)
214+
case 0x46: /* virtio-gpu */
215+
virtio_gpu_write(hart, &data->vgpu, addr & 0xFFFFF, width, value);
216+
emu_update_vgpu_interrupts(hart->vm);
217+
return;
218+
#endif
194219
}
195220
}
196221
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
@@ -623,6 +648,12 @@ static int semu_start(int argc, char **argv)
623648
emu.mtimer.mtimecmp = calloc(vm.n_hart, sizeof(uint64_t));
624649
emu.mswi.msip = calloc(vm.n_hart, sizeof(uint32_t));
625650
emu.sswi.ssip = calloc(vm.n_hart, sizeof(uint32_t));
651+
#if SEMU_HAS(VIRTIOGPU)
652+
emu.vgpu.ram = emu.ram;
653+
virtio_gpu_init(&(emu.vgpu));
654+
virtio_gpu_add_scanout(&(emu.vgpu), 1024, 768);
655+
window_init();
656+
#endif
626657

627658
/* Emulate */
628659
uint32_t peripheral_update_ctr = 0;
@@ -648,6 +679,11 @@ static int semu_start(int argc, char **argv)
648679
if (emu.vblk.InterruptStatus)
649680
emu_update_vblk_interrupts(&vm);
650681
#endif
682+
683+
#if SEMU_HAS(VIRTIOGPU)
684+
if (emu.vgpu.InterruptStatus)
685+
emu_update_vgpu_interrupts(&vm);
686+
#endif
651687
}
652688

653689
emu_update_timer_interrupt(vm.hart[i]);

minimal.dts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
chosen {
1818
bootargs = "earlycon console=ttyS0";
1919
stdout-path = "serial0";
20-
linux,initrd-start = <0x1f700000>; /* @403 MiB (503 * 1024 * 1024) */
20+
/* Reserve 65MiB for initrd image */
21+
linux,initrd-start = <0x1be00000>; /* @406 MiB (446 * 1024 * 1024) */
2122
linux,initrd-end = <0x1fefffff>; /* @511 MiB (511 * 1024 * 1024 - 1) */
2223
};
2324

@@ -63,5 +64,13 @@
6364
interrupts = <3>;
6465
};
6566
#endif
67+
68+
#if SEMU_FEATURE_VIRTIOGPU
69+
gpu0: virtio@4600000 {
70+
compatible = "virtio,mmio";
71+
reg = <0x4600000 0x200>;
72+
interrupts = <4>;
73+
};
74+
#endif
6675
};
6776
};

0 commit comments

Comments
 (0)