Skip to content

Commit 5eb7023

Browse files
authored
Refactor VirtIO MMIO register map handling (#23)
This commit addresses the following: 1. Re-organizes the source structures to improve code organization and maintainability. 2. Introduces the use of opaque pointer for information hiding, enhancing encapsulation and abstraction. 3. Enforces mnemonic enums instead of numbers, enhancing code readability and maintainability. By incorporating these changes, the codebase becomes more structured and easier to understand and maintain.
1 parent 3a9437c commit 5eb7023

File tree

5 files changed

+223
-78
lines changed

5 files changed

+223
-78
lines changed

common.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ static inline int ilog2(int x)
1616
{
1717
return 31 - __builtin_clz(x | 1);
1818
}
19+
20+
/* Range check
21+
* For any variable range checking:
22+
* if (x >= minx && x <= maxx) ...
23+
* it is faster to use bit operation:
24+
* if ((signed)((x - minx) | (maxx - x)) >= 0) ...
25+
*/
26+
#define RANGE_CHECK(x, minx, size) \
27+
((int32_t) ((x - minx) | (minx + size - 1 - x)) >= 0)

device.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include "riscv.h"
4+
#include "virtio.h"
45

56
/* RAM */
67

@@ -100,6 +101,8 @@ typedef struct {
100101
/* supplied by environment */
101102
int tap_fd;
102103
uint32_t *ram;
104+
/* implementation-specific */
105+
void *priv;
103106
} virtio_net_state_t;
104107

105108
void virtio_net_read(vm_t *core,
@@ -147,7 +150,8 @@ typedef struct {
147150
/* supplied by environment */
148151
uint32_t *ram;
149152
uint32_t *disk;
150-
uint64_t capacity;
153+
/* implementation-specific */
154+
void *priv;
151155
} virtio_blk_state_t;
152156

153157
void virtio_blk_read(vm_t *vm,

virtio-blk.c

Lines changed: 100 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,64 @@
99
#include <sys/stat.h>
1010
#include <unistd.h>
1111

12+
#include "common.h"
1213
#include "device.h"
1314
#include "riscv.h"
1415
#include "riscv_private.h"
1516
#include "virtio.h"
1617

1718
#define DISK_BLK_SIZE 512
1819

20+
#define VBLK_DEV_CNT_MAX 1
21+
1922
#define VBLK_FEATURES_0 0
2023
#define VBLK_FEATURES_1 1 /* VIRTIO_F_VERSION_1 */
2124
#define VBLK_QUEUE_NUM_MAX 1024
2225
#define VBLK_QUEUE (vblk->queues[vblk->QueueSel])
2326

27+
#define PRIV(x) ((struct virtio_blk_config *) x->priv)
28+
29+
struct virtio_blk_config {
30+
uint64_t capacity;
31+
uint32_t size_max;
32+
uint32_t seg_max;
33+
34+
struct virtio_blk_geometry {
35+
uint16_t cylinders;
36+
uint8_t heads;
37+
uint8_t sectors;
38+
} geometry;
39+
40+
uint32_t blk_size;
41+
42+
struct virtio_blk_topology {
43+
uint8_t physical_block_exp;
44+
uint8_t alignment_offset;
45+
uint16_t min_io_size;
46+
uint32_t opt_io_size;
47+
} topology;
48+
49+
uint8_t writeback;
50+
uint8_t unused0[3];
51+
uint32_t max_discard_sectors;
52+
uint32_t max_discard_seg;
53+
uint32_t discard_sector_alignment;
54+
uint32_t max_write_zeroes_sectors;
55+
uint32_t max_write_zeroes_seg;
56+
uint8_t write_zeroes_may_unmap;
57+
uint8_t unused1[3];
58+
} __attribute__((packed));
59+
2460
struct vblk_req_header {
2561
uint32_t type;
2662
uint32_t reserved;
2763
uint64_t sector;
2864
uint8_t status;
2965
} __attribute__((packed));
3066

67+
struct virtio_blk_config vblk_configs[VBLK_DEV_CNT_MAX];
68+
int vblk_dev_cnt = 0;
69+
3170
static void virtio_blk_set_fail(virtio_blk_state_t *vblk)
3271
{
3372
vblk->Status |= VIRTIO_STATUS__DEVICE_NEEDS_RESET;
@@ -52,11 +91,13 @@ static void virtio_blk_update_status(virtio_blk_state_t *vblk, uint32_t status)
5291
/* Reset */
5392
uint32_t *ram = vblk->ram;
5493
uint32_t *disk = vblk->disk;
55-
uint32_t capacity = vblk->capacity;
94+
void *priv = vblk->priv;
95+
uint32_t capacity = PRIV(vblk)->capacity;
5696
memset(vblk, 0, sizeof(*vblk));
5797
vblk->ram = ram;
5898
vblk->disk = disk;
59-
vblk->capacity = capacity;
99+
vblk->priv = priv;
100+
PRIV(vblk)->capacity = capacity;
60101
}
61102

62103
static void virtio_blk_write_handler(virtio_blk_state_t *vblk,
@@ -128,7 +169,7 @@ static int virtio_blk_desc_handler(virtio_blk_state_t *vblk,
128169
uint8_t *status = (uint8_t *) ((uintptr_t) vblk->ram + vq_desc[2].addr);
129170

130171
/* Check sector index is valid */
131-
if (sector > (vblk->capacity - 1)) {
172+
if (sector > (PRIV(vblk)->capacity - 1)) {
132173
*status = VIRTIO_BLK_S_IOERR;
133174
return -1;
134175
}
@@ -219,125 +260,129 @@ static bool virtio_blk_reg_read(virtio_blk_state_t *vblk,
219260
uint32_t addr,
220261
uint32_t *value)
221262
{
222-
/* TODO: replace the register address with enum.
223-
* For the address after the configuration space, it should be
224-
* handled by structure pointer depend on the device.
225-
*/
263+
#define _(reg) VIRTIO_##reg
226264
switch (addr) {
227-
case 0: /* MagicValue (R) */
265+
case _(MagicValue):
228266
*value = 0x74726976;
229267
return true;
230-
case 1: /* Version (R) */
268+
case _(Version):
231269
*value = 2;
232270
return true;
233-
case 2: /* DeviceID (R) */
271+
case _(DeviceID):
234272
*value = 2;
235273
return true;
236-
case 3: /* VendorID (R) */
274+
case _(VendorID):
237275
*value = VIRTIO_VENDOR_ID;
238276
return true;
239-
case 4: /* DeviceFeatures (R) */
277+
case _(DeviceFeatures):
240278
*value = vblk->DeviceFeaturesSel == 0
241279
? VBLK_FEATURES_0
242280
: (vblk->DeviceFeaturesSel == 1 ? VBLK_FEATURES_1 : 0);
243281
return true;
244-
case 13: /* QueueNumMax (R) */
282+
case _(QueueNumMax):
245283
*value = VBLK_QUEUE_NUM_MAX;
246284
return true;
247-
case 17: /* QueueReady (RW) */
285+
case _(QueueReady):
248286
*value = VBLK_QUEUE.ready ? 1 : 0;
249287
return true;
250-
case 24: /* InterruptStatus (R) */
288+
case _(InterruptStatus):
251289
*value = vblk->InterruptStatus;
252290
return true;
253-
case 28: /* Status (RW) */
291+
case _(Status):
254292
*value = vblk->Status;
255293
return true;
256-
case 63: /* ConfigGeneration (R) */
294+
case _(ConfigGeneration):
257295
*value = 0;
258296
return true;
259-
case 64: /* CapacityLow (R) */
260-
*value = 0x00000000ffffffff & vblk->capacity;
261-
return true;
262-
case 65: /* CapacityHigh (R) */
263-
*value = vblk->capacity >> 32;
264-
return true;
265297
default:
266-
return false;
298+
/* Invalid address which exceeded the range */
299+
if (!RANGE_CHECK(addr, _(Config), sizeof(struct virtio_blk_config)))
300+
return false;
301+
302+
/* Read configuration from the corresponding register */
303+
*value = ((uint32_t *) PRIV(vblk))[addr - _(Config)];
304+
305+
return true;
267306
}
307+
#undef _
268308
}
269309

270310
static bool virtio_blk_reg_write(virtio_blk_state_t *vblk,
271311
uint32_t addr,
272312
uint32_t value)
273313
{
274-
/* TODO: replace the register address with enum.
275-
* For the address after the configuration space, it should be
276-
* handled by structure pointer depend on the device.
277-
*/
314+
#define _(reg) VIRTIO_##reg
278315
switch (addr) {
279-
case 5: /* DeviceFeaturesSel (W) */
316+
case _(DeviceFeaturesSel):
280317
vblk->DeviceFeaturesSel = value;
281318
return true;
282-
case 8: /* DriverFeatures (W) */
319+
case _(DriverFeatures):
283320
vblk->DriverFeaturesSel == 0 ? (vblk->DriverFeatures = value) : 0;
284321
return true;
285-
case 9: /* DriverFeaturesSel (W) */
322+
case _(DriverFeaturesSel):
286323
vblk->DriverFeaturesSel = value;
287324
return true;
288-
case 12: /* QueueSel (W) */
325+
case _(QueueSel):
289326
if (value < ARRAY_SIZE(vblk->queues))
290327
vblk->QueueSel = value;
291328
else
292329
virtio_blk_set_fail(vblk);
293330
return true;
294-
case 14: /* QueueNum (W) */
331+
case _(QueueNum):
295332
if (value > 0 && value <= VBLK_QUEUE_NUM_MAX)
296333
VBLK_QUEUE.QueueNum = value;
297334
else
298335
virtio_blk_set_fail(vblk);
299336
return true;
300-
case 17: /* QueueReady (RW) */
337+
case _(QueueReady):
301338
VBLK_QUEUE.ready = value & 1;
302339
if (value & 1)
303340
VBLK_QUEUE.last_avail = vblk->ram[VBLK_QUEUE.QueueAvail] >> 16;
304341
return true;
305-
case 32: /* QueueDescLow (W) */
342+
case _(QueueDescLow):
306343
VBLK_QUEUE.QueueDesc = vblk_preprocess(vblk, value);
307344
return true;
308-
case 33: /* QueueDescHigh (W) */
345+
case _(QueueDescHigh):
309346
if (value)
310347
virtio_blk_set_fail(vblk);
311348
return true;
312-
case 36: /* QueueAvailLow (W) */
349+
case _(QueueDriverLow):
313350
VBLK_QUEUE.QueueAvail = vblk_preprocess(vblk, value);
314351
return true;
315-
case 37: /* QueueAvailHigh (W) */
352+
case _(QueueDriverHigh):
316353
if (value)
317354
virtio_blk_set_fail(vblk);
318355
return true;
319-
case 40: /* QueueUsedLow (W) */
356+
case _(QueueDeviceLow):
320357
VBLK_QUEUE.QueueUsed = vblk_preprocess(vblk, value);
321358
return true;
322-
case 41: /* QueueUsedHigh (W) */
359+
case _(QueueDeviceHigh):
323360
if (value)
324361
virtio_blk_set_fail(vblk);
325362
return true;
326-
case 20: /* QueueNotify (W) */
363+
case _(QueueNotify):
327364
if (value < ARRAY_SIZE(vblk->queues))
328365
virtio_queue_notify_handler(vblk, value);
329366
else
330367
virtio_blk_set_fail(vblk);
331368
return true;
332-
case 25: /* InterruptACK (W) */
369+
case _(InterruptACK):
333370
vblk->InterruptStatus &= ~value;
334371
return true;
335-
case 28: /* Status (RW) */
372+
case _(Status):
336373
virtio_blk_update_status(vblk, value);
337374
return true;
338375
default:
339-
return false;
376+
/* Invalid address which exceeded the range */
377+
if (!RANGE_CHECK(addr, _(Config), sizeof(struct virtio_blk_config)))
378+
return false;
379+
380+
/* Write configuration to the corresponding register */
381+
((uint32_t *) PRIV(vblk))[addr - _(Config)] = value;
382+
383+
return true;
340384
}
385+
#undef _
341386
}
342387

343388
void virtio_blk_read(vm_t *vm,
@@ -386,11 +431,20 @@ void virtio_blk_write(vm_t *vm,
386431

387432
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
388433
{
434+
if (vblk_dev_cnt >= VBLK_DEV_CNT_MAX) {
435+
fprintf(stderr,
436+
"Excedded the number of virtio-blk device can be allocated.\n");
437+
exit(2);
438+
}
439+
440+
/* Allocate memory for the private member */
441+
vblk->priv = &vblk_configs[vblk_dev_cnt++];
442+
389443
/* No disk image is provided */
390444
if (!disk_file) {
391445
/* By setting the block capacity to zero, the kernel will
392446
* then not to touch the device after booting */
393-
vblk->capacity = 0;
447+
PRIV(vblk)->capacity = 0;
394448
return NULL;
395449
}
396450

@@ -417,7 +471,7 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
417471
close(disk_fd);
418472

419473
vblk->disk = disk_mem;
420-
vblk->capacity = (disk_size - 1) / DISK_BLK_SIZE + 1;
474+
PRIV(vblk)->capacity = (disk_size - 1) / DISK_BLK_SIZE + 1;
421475

422476
return disk_mem;
423477
}

0 commit comments

Comments
 (0)