virtio stuff, discovery of virtio devices over PCI
This commit is contained in:
parent
b52e397303
commit
e48ef201d5
9 changed files with 216 additions and 3 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,6 +2,7 @@ config.mk
|
|||
/tags
|
||||
build/
|
||||
drive.img
|
||||
drive-virtio.img
|
||||
/compile_commands.json
|
||||
/opt
|
||||
/.cache
|
||||
|
|
|
|||
8
Makefile
8
Makefile
|
|
@ -63,6 +63,7 @@ KERNEL_SOURCES := \
|
|||
src/font.c \
|
||||
src/karlos-logo.c \
|
||||
src/tlsf.c \
|
||||
src/virtio-common.c \
|
||||
$(KERNEL_SOURCES_$(ARCH)) \
|
||||
# end of kernel sources list
|
||||
|
||||
|
|
@ -74,10 +75,13 @@ KERNEL_TARGET := build/boot/sys/core
|
|||
|
||||
.PHONY: all clean
|
||||
|
||||
all: build/disk.img drive.img
|
||||
all: build/disk.img drive.img drive-virtio.img
|
||||
|
||||
drive.img:
|
||||
truncate -s 16M drive.img
|
||||
truncate -s 16M $@
|
||||
|
||||
drive-virtio.img:
|
||||
truncate -s 16M $@
|
||||
|
||||
clean:
|
||||
rm -rf build/*
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ QEMU_OPTS="$QEMU_OPTS -drive if=ide,format=raw,file=build/disk.img"
|
|||
# SATA drive
|
||||
QEMU_OPTS="$QEMU_OPTS -drive id=disk,format=raw,file=drive.img,if=none -device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0,serial=DEADBEEF"
|
||||
|
||||
# VIRTIO drive
|
||||
QEMU_OPTS="$QEMU_OPTS -drive file=drive-virtio.img,format=raw,if=virtio"
|
||||
|
||||
# Multicore
|
||||
QEMU_OPTS="$QEMU_OPTS -smp sockets=1,cores=2,threads=1"
|
||||
|
||||
|
|
|
|||
54
include/virtio-block.h
Normal file
54
include/virtio-block.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef KARLOS_VIRTIO_BLOCK_H
|
||||
#define KARLOS_VIRTIO_BLOCK_H
|
||||
|
||||
#include "virtio-common.h"
|
||||
|
||||
#define VIRTIO_BLK_F_SIZE_MAX 1
|
||||
#define VIRTIO_BLK_F_SEG_MAX 2
|
||||
#define VIRTIO_BLK_F_GEOMETRY 4
|
||||
|
||||
#define VIRTIO_BLK_F_BLK_SIZE 6
|
||||
#define VIRTIO_BLK_F_FLUSH 9
|
||||
#define VIRTIO_BLK_F_TOPOLOGY 10
|
||||
#define VIRTIO_BLK_F_WRITE_ZEROES 14
|
||||
|
||||
struct virtio_blk_config {
|
||||
le64 capacity;
|
||||
le64 size_max;
|
||||
le32 seg_max;
|
||||
struct virtio_blk_geometry {
|
||||
le16 cylinders;
|
||||
uint8_t heads;
|
||||
uint8_t sectors;
|
||||
} geometry;
|
||||
le32 blk_size;
|
||||
struct virtio_blk_topology {
|
||||
uint8_t physical_block_exp;
|
||||
uint8_t alignment_offset;
|
||||
le16 min_io_size;
|
||||
le32 opt_io_size;
|
||||
} topology;
|
||||
uint8_t writeback;
|
||||
uint8_t unused0;
|
||||
uint16_t num_queues; // TODO uint16_t? Shouldn't this be le16?
|
||||
le32 max_discard_sectors;
|
||||
le32 max_discard_seg;
|
||||
le32 discard_sector_alignment;
|
||||
le32 max_write_zeroes_sectors;
|
||||
le32 max_write_zeroes_seg;
|
||||
uint8_t write_zeroes_may_unmap;
|
||||
uint8_t unused1[3];
|
||||
le32 max_secure_erase_sectors;
|
||||
le32 max_secure_erase_seg;
|
||||
le32 secure_erase_sector_alignment;
|
||||
};
|
||||
|
||||
struct virtio_blk_req {
|
||||
le32 type;
|
||||
le32 reserved;
|
||||
le64 sector;
|
||||
uint8_t data[];
|
||||
// uint8_t status;
|
||||
};
|
||||
|
||||
#endif
|
||||
100
include/virtio-common.h
Normal file
100
include/virtio-common.h
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
#ifndef KARLOS_VIRTIO_COMMON_H
|
||||
#define KARLOS_VIRTIO_COMMON_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "pci.h"
|
||||
|
||||
// struct so you don't accidentally access the value; use following macros to read/write
|
||||
// TODO these are aligned, we have no way to specify unaligned ints currently
|
||||
typedef struct le16 {
|
||||
uint16_t value;
|
||||
} le16;
|
||||
typedef struct le32 {
|
||||
uint32_t value;
|
||||
} le32;
|
||||
typedef struct le64 {
|
||||
uint64_t value;
|
||||
} le64;
|
||||
|
||||
#if !defined(__BYTE_ORDER__) || (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
#define LE16_READ(v) TODO()
|
||||
#define LE16_WRITE(v, new_value) TODO()
|
||||
#define LE32_READ(v) TODO()
|
||||
#define LE32_WRITE(v, new_value) TODO()
|
||||
#define LE64_READ(v) TODO()
|
||||
#define LE64_WRITE(v, new_value) TODO()
|
||||
#else
|
||||
#define LE16_READ(v) ((v).value)
|
||||
#define LE16_WRITE(v, new_value) do { (v).value = (new_value); } while (0)
|
||||
#define LE32_READ(v) ((v).value)
|
||||
#define LE32_WRITE(v, new_value) do { (v).value = (new_value); } while (0)
|
||||
#define LE64_READ(v) ((v).value)
|
||||
#define LE64_WRITE(v, new_value) do { (v).value = (new_value); } while (0)
|
||||
#endif
|
||||
|
||||
// OS acknowledged the device
|
||||
#define VIRTIO_DEV_STATUS_ACKNOWLEDGE 1
|
||||
// OS knows how to drive the device
|
||||
#define VIRTIO_DEV_STATUS_DRIVER 2
|
||||
// guest has given up on the device
|
||||
#define VIRTIO_DEV_STATUS_FAILED 128
|
||||
// feature negotiation complete
|
||||
#define VIRTIO_DEV_STATUS_FEATURES_OK 8
|
||||
// driver is set up
|
||||
#define VIRTIO_DEV_STATUS_DRIVER_OK 4
|
||||
// unrecoverable error
|
||||
#define VIRTIO_DEV_STATUS_DEVICE_NEEDS_RESET 64
|
||||
|
||||
// buffer continues via `next` field
|
||||
#define VIRTQ_DESC_F_NEXT 1
|
||||
// buffer is device write-only (not set: device read-only)
|
||||
#define VIRTQ_DESC_F_WRITE 2
|
||||
// buffer contains list of buffer descriptors (ONLY AVAILABLE BY VIRTIO_F_INDIRECT_DESC; DO NOT SET THIS AND F_NEXT)
|
||||
#define VIRTQ_DESC_F_INDIRECT 4
|
||||
struct virtq_desc {
|
||||
le64 addr;
|
||||
le32 len;
|
||||
le16 flags; // see macros above
|
||||
le16 next;
|
||||
};
|
||||
|
||||
#define VIRTQ_AVAIL_F_NO_INTERRUPT 1
|
||||
struct virtq_avail {
|
||||
le16 flags;
|
||||
// where driver would put next descriptor entry, modulo queue size
|
||||
le16 idx;
|
||||
le16 ring[];
|
||||
// le16 used_event; // only when VIRTIO_F_EVENT_IDX
|
||||
};
|
||||
|
||||
struct virtq_used_elem {
|
||||
// index of start of used descriptor chain
|
||||
le32 id;
|
||||
// number of bytes written into device writable portion
|
||||
// useful to avoid data leaks (e.g. passing buffer directly to userspace)
|
||||
le32 len;
|
||||
};
|
||||
|
||||
#define VIRTQ_USED_F_NO_NOTIFY 1
|
||||
struct virtq_used {
|
||||
le16 flags;
|
||||
le16 idx;
|
||||
struct virtq_used_elem ring [];
|
||||
// le16 avail_event; // only when VIRTIO_F_EVENT_IDX
|
||||
};
|
||||
|
||||
// searching for devices
|
||||
#define VIRTIO_VENDOR_ID 0x1af4
|
||||
enum virtio_dev_kind {
|
||||
VIRTIO_DEV_KIND_BLOCK,
|
||||
// TODO
|
||||
};
|
||||
struct virtio_dev_iter {
|
||||
struct pci_iter iter;
|
||||
enum virtio_dev_kind kind;
|
||||
};
|
||||
void virtio_dev_iter_init(struct virtio_dev_iter *it, enum virtio_dev_kind kind);
|
||||
bool virtio_dev_iter_next(struct virtio_dev_iter *it, uint16_t *bdf);
|
||||
|
||||
#endif
|
||||
|
|
@ -8,6 +8,9 @@ QEMU_OPTS="$QEMU_OPTS -drive if=ide,format=raw,file=build/disk.img"
|
|||
# SATA drive
|
||||
QEMU_OPTS="$QEMU_OPTS -drive id=disk,format=raw,file=drive.img,if=none -device ahci,id=ahci -device ide-hd,drive=disk,bus=ahci.0,serial=DEADBEEF"
|
||||
|
||||
# VIRTIO drive
|
||||
QEMU_OPTS="$QEMU_OPTS -drive file=drive-virtio.img,format=raw,if=virtio"
|
||||
|
||||
# Multicore
|
||||
QEMU_OPTS="$QEMU_OPTS -smp sockets=1,cores=2,threads=1"
|
||||
|
||||
|
|
|
|||
10
src/kernel.c
10
src/kernel.c
|
|
@ -5,6 +5,7 @@
|
|||
#include "app.h"
|
||||
#include "bootboot.h"
|
||||
#include "keyboard.h"
|
||||
#include "pci.h"
|
||||
#include "std.h"
|
||||
#include "cpu.h"
|
||||
#include "texture.h"
|
||||
|
|
@ -16,6 +17,7 @@
|
|||
#include "bootparam.h"
|
||||
|
||||
#include "framebuffer.h"
|
||||
#include "virtio-common.h"
|
||||
#include "x86_64/interrupt.h"
|
||||
|
||||
/* imported virtual addresses, see linker script */
|
||||
|
|
@ -75,6 +77,14 @@ void main(void) {
|
|||
// app_push(&snake_template, NULL, &RECT_XYWH(10, 10, 500, 500));
|
||||
|
||||
__asm__ ("sti");
|
||||
struct virtio_dev_iter iter;
|
||||
virtio_dev_iter_init(&iter, VIRTIO_DEV_KIND_BLOCK);
|
||||
uint16_t bdf;
|
||||
ASSERT(virtio_dev_iter_next(&iter, &bdf));
|
||||
// search for exactly one device
|
||||
ASSERT(!virtio_dev_iter_next(&iter, &bdf));
|
||||
printlinef("found virtio block device with bdf %X8", bdf);
|
||||
|
||||
while (1) {
|
||||
fb_refresh();
|
||||
__asm__ ("hlt");
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ void pci_print_listing() {
|
|||
uint8_t prog_if = (rev_class >> 8) & 0xFF;
|
||||
uint8_t sub_class = (rev_class >> 16) & 0xFF;
|
||||
uint8_t base_class = (rev_class >> 24) & 0xFF;
|
||||
printlinef("PCI %x %x:%x %x:%x:%x\n", bdf,
|
||||
printlinef("PCI %x %x:%x %x:%x:%x", bdf,
|
||||
vendor_id, device_id, base_class, sub_class, prog_if);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,39 @@
|
|||
#include "virtio-common.h"
|
||||
#include "std.h"
|
||||
|
||||
static unsigned virtio_dev_kind_transitional_id(enum virtio_dev_kind kind) {
|
||||
switch (kind) {
|
||||
case VIRTIO_DEV_KIND_BLOCK: return 0x1001;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
static unsigned virtio_dev_kind_device_id(enum virtio_dev_kind kind) {
|
||||
switch (kind) {
|
||||
case VIRTIO_DEV_KIND_BLOCK: return 0x1040 + 2;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void virtio_dev_iter_init(struct virtio_dev_iter *it, enum virtio_dev_kind kind) {
|
||||
pci_iter_reset(&it->iter);
|
||||
it->kind = kind;
|
||||
}
|
||||
|
||||
bool virtio_dev_iter_next(struct virtio_dev_iter *it, uint16_t *bdf_out) {
|
||||
while (pci_iter_next(&it->iter)) {
|
||||
uint16_t bdf = pci_iter_get_bdf(&it->iter);
|
||||
uint16_t vendor_id = pci_config_read_u16(bdf, PCI_HEADER_VENDOR_ID);
|
||||
if (vendor_id != VIRTIO_VENDOR_ID) {
|
||||
continue;
|
||||
}
|
||||
uint16_t device_id = pci_config_read_u16(bdf, PCI_HEADER_DEVICE_ID);
|
||||
if (device_id == virtio_dev_kind_device_id(it->kind)
|
||||
|| device_id == virtio_dev_kind_transitional_id(it->kind))
|
||||
{
|
||||
*bdf_out = bdf;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue