Skip to content

Commit 6661ab1

Browse files
povikmarcan
authored andcommitted
hv: Add 9P virtio peripheral
Add some minimal implementation of virtio peripherals. At the level of on-target hypervisor code we implement the MMIO layout and maintain virtqueues. Once a buffer is available, we break into the host proxyclient to deal with it. Specific device-classes of the virtio spec ought to be implemented in the proxyclient. Here the one device implemented is 9P transport, exporting the m1n1 source directory. Signed-off-by: Martin Povišer <[email protected]>
1 parent 586157b commit 6661ab1

9 files changed

Lines changed: 511 additions & 1 deletion

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ OBJECTS := \
9999
firmware.o \
100100
gxf.o gxf_asm.o \
101101
heapblock.o \
102-
hv.o hv_vm.o hv_exc.o hv_vuart.o hv_wdt.o hv_asm.o hv_aic.o \
102+
hv.o hv_vm.o hv_exc.o hv_vuart.o hv_wdt.o hv_asm.o hv_aic.o hv_virtio.o \
103103
i2c.o \
104104
iodev.o \
105105
iova.o \

proxyclient/m1n1/hv/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from .gdbserver import *
1515
from .types import *
16+
from .virtio import *
1617

1718
__all__ = ["HV"]
1819

@@ -111,6 +112,7 @@ def __init__(self, iface, proxy, utils):
111112
self.hvcall_handlers = {}
112113
self.switching_context = False
113114
self.show_timestamps = False
115+
self.virtio_devs = {}
114116

115117
def _reloadme(self):
116118
super()._reloadme()
@@ -999,6 +1001,52 @@ def handle_bark(self, reason, code, info):
9991001

10001002
self.p.exit(0)
10011003

1004+
def attach_virtio(self, dev, base=None, irq=None, verbose=False):
1005+
assert base is not None and irq is not None
1006+
# TODO: ^-- allocate those if not chosen by caller
1007+
1008+
data = dev.config_data
1009+
data_base = self.u.heap.malloc(len(data))
1010+
self.iface.writemem(data_base, data)
1011+
1012+
config = VirtioConfig.build({
1013+
"irq": irq,
1014+
"devid": dev.devid,
1015+
"feats": dev.feats,
1016+
"num_qus": dev.num_qus,
1017+
"data": data_base,
1018+
"data_len": len(data),
1019+
"verbose": verbose,
1020+
})
1021+
1022+
config_base = self.u.heap.malloc(len(config))
1023+
self.iface.writemem(config_base, config)
1024+
1025+
self.p.hv_map_virtio(base, config_base)
1026+
self.add_tracer(irange(base, 0x1000), "VIRTIO", TraceMode.RESERVED)
1027+
1028+
dev.base = base
1029+
dev.hv = self
1030+
self.virtio_devs[base] = dev
1031+
1032+
def handle_virtio(self, reason, code, info):
1033+
ctx = self.iface.readstruct(info, ExcInfo)
1034+
self.virtio_ctx = info = self.iface.readstruct(ctx.data, VirtioExcInfo)
1035+
1036+
try:
1037+
handled = self.virtio_devs[info.devbase].handle_exc(info)
1038+
except:
1039+
self.log(f"Python exception from within virtio handler")
1040+
traceback.print_exc()
1041+
handled = False
1042+
1043+
if not handled:
1044+
signal.signal(signal.SIGINT, self.default_sigint)
1045+
self.run_shell("Entering hypervisor shell", "Returning")
1046+
signal.signal(signal.SIGINT, self._handle_sigint)
1047+
1048+
self.p.exit(EXC_RET.HANDLED)
1049+
10021050
def skip(self):
10031051
self.ctx.elr += 4
10041052
self.cont()
@@ -1299,6 +1347,7 @@ def init(self):
12991347
self.iface.set_handler(START.HV, HV_EVENT.VTIMER, self.handle_exception)
13001348
self.iface.set_handler(START.HV, HV_EVENT.WDT_BARK, self.handle_bark)
13011349
self.iface.set_handler(START.HV, HV_EVENT.CPU_SWITCH, self.handle_exception)
1350+
self.iface.set_handler(START.HV, HV_EVENT.VIRTIO, self.handle_virtio)
13021351
self.iface.set_event_handler(EVENT.MMIOTRACE, self.handle_mmiotrace)
13031352
self.iface.set_event_handler(EVENT.IRQTRACE, self.handle_irqtrace)
13041353

proxyclient/m1n1/hv/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class HV_EVENT(IntEnum):
3737
USER_INTERRUPT = 3
3838
WDT_BARK = 4
3939
CPU_SWITCH = 5
40+
VIRTIO = 6
4041

4142
VMProxyHookData = Struct(
4243
"flags" / RegAdapter(MMIOTraceFlags),

proxyclient/m1n1/hv/virtio.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# SPDX-License-Identifier: MIT
2+
from construct import Struct, Int8ul, Int16ul, Int32sl, Int32ul, Int64ul
3+
from subprocess import Popen, PIPE
4+
import pathlib
5+
import struct
6+
import os
7+
import sys
8+
9+
from ..utils import *
10+
11+
VirtioConfig = Struct(
12+
"irq" / Int32sl,
13+
"devid" / Int32ul,
14+
"feats" / Int64ul,
15+
"num_qus" / Int32ul,
16+
"data" / Int64ul,
17+
"data_len" / Int64ul,
18+
"verbose" / Int8ul,
19+
)
20+
21+
class VirtioDescFlags(Register16):
22+
WRITE = 1
23+
NEXT = 0
24+
25+
VirtioDesc = Struct(
26+
"addr" / Int64ul,
27+
"len" / Int32ul,
28+
"flags" / RegAdapter(VirtioDescFlags),
29+
"next" / Int16ul,
30+
)
31+
32+
VirtioExcInfo = Struct(
33+
"devbase" / Int64ul,
34+
"qu" / Int16ul,
35+
"idx" / Int16ul,
36+
"pad" / Int32ul,
37+
"descbase" / Int64ul,
38+
)
39+
40+
class VirtioDev:
41+
def __init__(self):
42+
self.base, self.hv = None, None # assigned by HV object
43+
44+
def read_buf(self, desc):
45+
return self.hv.iface.readmem(desc.addr, desc.len)
46+
47+
def read_desc(self, ctx, idx):
48+
off = VirtioDesc.sizeof() * idx
49+
return self.hv.iface.readstruct(ctx.descbase + off, VirtioDesc)
50+
51+
@property
52+
def config_data(self):
53+
return b""
54+
55+
@property
56+
def devid(self):
57+
return 0
58+
59+
@property
60+
def num_qus(self):
61+
return 1
62+
63+
@property
64+
def feats(self):
65+
return 0
66+
67+
class Virtio9PTransport(VirtioDev):
68+
def __init__(self, tag="m1n1", root=None):
69+
p_stdin, self.fin = os.pipe()
70+
self.fout, p_stdout = os.pipe()
71+
if root is None:
72+
root = str(pathlib.Path(__file__).resolve().parents[3])
73+
if type(tag) is str:
74+
self.tag = tag.encode("ascii")
75+
else:
76+
self.tag = tag
77+
self.p = Popen([
78+
"u9fs",
79+
"-a", "none", # no auth
80+
"-n", # not a network conn
81+
"-u", os.getlogin(), # single user
82+
root,
83+
], stdin=p_stdin, stdout=p_stdout, stderr=sys.stderr)
84+
85+
@property
86+
def config_data(self):
87+
return struct.pack("=H", len(self.tag)) + self.tag
88+
89+
@property
90+
def devid(self):
91+
return 9
92+
93+
@property
94+
def num_qus(self):
95+
return 1
96+
97+
@property
98+
def feats(self):
99+
return 1
100+
101+
def call(self, req):
102+
os.write(self.fin, req)
103+
resp = os.read(self.fout, 4)
104+
length = int.from_bytes(resp, byteorder="little")
105+
resp += os.read(self.fout, length - 4)
106+
return resp
107+
108+
def handle_exc(self, ctx):
109+
head = self.read_desc(ctx, ctx.idx)
110+
assert not head.flags.WRITE
111+
112+
req = bytearray()
113+
114+
while not head.flags.WRITE:
115+
req += self.read_buf(head)
116+
117+
if not head.flags.NEXT:
118+
break
119+
head = self.read_desc(ctx, head.next)
120+
121+
resp = self.call(bytes(req))
122+
resplen = len(resp)
123+
124+
while len(resp):
125+
self.hv.iface.writemem(head.addr, resp[:head.len])
126+
resp = resp[head.len:]
127+
if not head.flags.NEXT:
128+
break
129+
head = self.read_desc(ctx, head.next)
130+
131+
self.hv.p.virtio_put_buffer(ctx.devbase, ctx.qu, ctx.idx, resplen)
132+
133+
return True

proxyclient/m1n1/proxy.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,8 @@ class M1N1Proxy(Reloadable):
594594
P_HV_SET_TIME_STEALING = 0xc0a
595595
P_HV_PIN_CPU = 0xc0b
596596
P_HV_WRITE_HCR = 0xc0c
597+
P_HV_MAP_VIRTIO = 0xc0d
598+
P_VIRTIO_PUT_BUFFER = 0xc0e
597599

598600
P_FB_INIT = 0xd00
599601
P_FB_SHUTDOWN = 0xd01
@@ -1025,6 +1027,10 @@ def hv_pin_cpu(self, cpu):
10251027
return self.request(self.P_HV_PIN_CPU, cpu)
10261028
def hv_write_hcr(self, hcr):
10271029
return self.request(self.P_HV_WRITE_HCR, hcr)
1030+
def hv_map_virtio(self, base, config):
1031+
return self.request(self.P_HV_MAP_VIRTIO, base, config)
1032+
def virtio_put_buffer(self, base, qu, idx, length):
1033+
return self.request(self.P_VIRTIO_PUT_BUFFER, base, qu, idx, length)
10281034

10291035
def fb_init(self):
10301036
return self.request(self.P_FB_INIT)

src/hv.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ typedef enum _hv_entry_type {
4747
HV_USER_INTERRUPT,
4848
HV_WDT_BARK,
4949
HV_CPU_SWITCH,
50+
HV_VIRTIO,
5051
} hv_entry_type;
5152

5253
/* VM */
@@ -69,6 +70,9 @@ bool hv_trace_irq(u32 type, u32 num, u32 count, u32 flags);
6970
/* Virtual peripherals */
7071
void hv_vuart_poll(void);
7172
void hv_map_vuart(u64 base, int irq, iodev_id_t iodev);
73+
struct virtio_conf;
74+
void hv_map_virtio(u64 base, struct virtio_conf *conf);
75+
void virtio_put_buffer(u64 base, int qu, u32 id, u32 len);
7276

7377
/* Exceptions */
7478
void hv_exc_proxy(struct exc_info *ctx, uartproxy_boot_reason_t reason, u32 type, void *extra);

0 commit comments

Comments
 (0)