first functional block read
This commit is contained in:
parent
78f2f4c75a
commit
085d3f4538
6 changed files with 120 additions and 44 deletions
1
Makefile
1
Makefile
|
|
@ -64,6 +64,7 @@ KERNEL_SOURCES := \
|
|||
src/karlos-logo.c \
|
||||
src/tlsf.c \
|
||||
src/virtio-common.c \
|
||||
src/virtio-block.c \
|
||||
$(KERNEL_SOURCES_$(ARCH)) \
|
||||
# end of kernel sources list
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,12 @@ struct virtio_blk_req {
|
|||
#define VIRTIO_BLK_T_WRITE_ZEROES 13
|
||||
#define VIRTIO_BLK_T_SECURE_ERASE 14
|
||||
|
||||
void virtio_blk_req_init_in(struct virtio_blk_req *req, uint8_t *data, uint64_t sector);
|
||||
void virtio_blk_req_init_in(struct virtio_blk_req *req, uint64_t sector);
|
||||
void virtio_blk_req_init_out(struct virtio_blk_req *req, uint64_t sector);
|
||||
void virtio_blk_req_init_flush(struct virtio_blk_req *req);
|
||||
|
||||
#define VIRTIO_BLK_S_OK 0
|
||||
#define VIRTIO_BLK_S_IOERR 1
|
||||
#define VIRTIO_BLK_S_UNSUPP 2
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -133,6 +133,16 @@ struct virtio_pci_cap {
|
|||
/* Vendor-specific data */
|
||||
#define VIRTIO_PCI_CAP_VENDOR_CFG 9
|
||||
|
||||
struct virtio_cap {
|
||||
unsigned type;
|
||||
uint8_t pci_offset;
|
||||
uint8_t barno;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
bool virtio_find_cap_of_type(uint16_t bdf, unsigned type, struct virtio_cap *cap);
|
||||
|
||||
struct virtio_pci_common_cfg {
|
||||
le32 device_feature_select;
|
||||
le32 device_feature;
|
||||
|
|
@ -155,4 +165,9 @@ struct virtio_pci_common_cfg {
|
|||
le16 queue_reset; // only for VIRTIO_F_RING_-RESET
|
||||
};
|
||||
|
||||
struct virtio_pci_notify_cap {
|
||||
struct virtio_pci_cap cap;
|
||||
le32 notify_off_multiplier; /* Multiplier for queue_notify_off. */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
106
src/kernel.c
106
src/kernel.c
|
|
@ -94,48 +94,15 @@ void main(void) {
|
|||
uint8_t cap_ptr = pci_config_read_u8(bdf, PCI_HEADER_CAP_PTR);
|
||||
// pci spec p. 234
|
||||
// virtio spec p. 49
|
||||
uint8_t common_bar = 0xff;
|
||||
uint32_t common_offset = 0xff;
|
||||
uint32_t common_length = 0xff;
|
||||
uint8_t device_bar = 0xff;
|
||||
uint32_t device_offset = 0xff;
|
||||
uint32_t device_length = 0xff;
|
||||
do {
|
||||
uint16_t as16 = pci_config_read_u16(bdf, cap_ptr + 0);
|
||||
uint8_t cap_id = (as16) & 0xff;
|
||||
uint8_t cap_next = (as16 >> 8) & 0xff;
|
||||
if (cap_id != 9) {
|
||||
cap_ptr = cap_next;
|
||||
continue; // ignore non-virtio
|
||||
}
|
||||
as16 = pci_config_read_u16(bdf, cap_ptr + 2);
|
||||
uint8_t cap_len = (as16) & 0xff;
|
||||
uint8_t cfg_type = (as16 >> 8) & 0xff;
|
||||
printlinef("cap_len %u cfg_type %u", cap_len, cfg_type);
|
||||
uint32_t as32 = pci_config_read_u32(bdf, cap_ptr + 4);
|
||||
uint8_t bar = (as32) & 0xff;
|
||||
uint32_t offset = pci_config_read_u32(bdf, cap_ptr + 8);
|
||||
uint32_t length = pci_config_read_u32(bdf, cap_ptr + 12);
|
||||
printlinef("bar %u offset %u length %u", bar, offset, length);
|
||||
if (cfg_type == 1) {
|
||||
ASSERT(common_bar = 0xff); // only 1 (I know there may be several)
|
||||
common_bar = bar;
|
||||
common_offset = offset;
|
||||
common_length = length;
|
||||
} else if (cfg_type == 4) {
|
||||
ASSERT(device_bar == 0xff);
|
||||
device_bar = bar;
|
||||
device_offset = offset;
|
||||
device_length = length;
|
||||
}
|
||||
cap_ptr = cap_next;
|
||||
} while (cap_ptr != 0);
|
||||
printlinef("common: bar %u offset %u length %u", common_bar, common_offset, common_length);
|
||||
struct pci_bar bar = pci_config_get_bar(bdf, common_bar);
|
||||
struct pa pa = pa_from_value(bar.base_address + common_offset);
|
||||
struct virtio_cap cap_common, cap_notify, cap_device;
|
||||
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_COMMON_CFG, &cap_common));
|
||||
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_NOTIFY_CFG, &cap_notify));
|
||||
ASSERT(virtio_find_cap_of_type(bdf, VIRTIO_PCI_CAP_DEVICE_CFG, &cap_device));
|
||||
struct pci_bar bar = pci_config_get_bar(bdf, cap_common.barno);
|
||||
struct pa pa = pa_from_value(bar.base_address + cap_common.offset);
|
||||
volatile struct virtio_pci_common_cfg *cfg = pa_to_pointer(pa);
|
||||
bar = pci_config_get_bar(bdf, device_bar);
|
||||
pa = pa_from_value(bar.base_address + device_offset);
|
||||
bar = pci_config_get_bar(bdf, cap_device.barno);
|
||||
pa = pa_from_value(bar.base_address + cap_device.offset);
|
||||
volatile struct virtio_blk_config *dev_cfg = pa_to_pointer(pa);
|
||||
|
||||
// TODO read configuration correctly (make sure `generation` doesn't change; p.24)
|
||||
|
|
@ -216,6 +183,13 @@ void main(void) {
|
|||
// set up virtqueue 0
|
||||
LE16_WRITE(cfg->queue_select, 0);
|
||||
while (LE16_READ(cfg->queue_select) != 0) ; // wait.
|
||||
// notification stuff
|
||||
// NOTE: this read depends on queue_select, so it may be different for every queue.
|
||||
uint32_t queue_notify_off = LE32_READ(cfg->queue_notify_off);
|
||||
uint32_t notify_off_multiplier = pci_config_read_u32(bdf, cap_notify.pci_offset + 16);
|
||||
bar = pci_config_get_bar(bdf, cap_notify.barno);
|
||||
pa = pa_from_value(bar.base_address + cap_notify.offset + queue_notify_off * notify_off_multiplier);
|
||||
volatile le16 *queue0_notify_location = pa_to_pointer(pa);
|
||||
|
||||
uint16_t queue_size = LE16_READ(cfg->queue_size);
|
||||
printlinef("queue_size %u", queue_size);
|
||||
|
|
@ -245,6 +219,56 @@ void main(void) {
|
|||
while (cfg->device_status != wanted_status) ; // wait.
|
||||
printline("virtio block device initialized.");
|
||||
|
||||
// want to read a single sector.
|
||||
struct virtq_desc *queue_desc = ppn_to_pointer(queue_desc_ppn);
|
||||
// descriptor 0: header (device-readable)
|
||||
struct virtq_desc *desc0 = queue_desc;
|
||||
struct ppn buf0_ppn;
|
||||
ASSERT(ram_alloc_frame_zeroed(&buf0_ppn, RAM_PAGE_NORMAL));
|
||||
uint64_t buf0_addr = ppn_to_value(buf0_ppn);
|
||||
LE64_WRITE(desc0->addr, buf0_addr);
|
||||
LE32_WRITE(desc0->len, 16);
|
||||
LE16_WRITE(desc0->flags, VIRTQ_DESC_F_NEXT);
|
||||
LE16_WRITE(desc0->next, 1);
|
||||
// descriptor 1: data + status (device-writable)
|
||||
struct virtq_desc *desc1 = queue_desc + 1;
|
||||
struct ppn buf1_ppn;
|
||||
ASSERT(ram_alloc_frame_zeroed(&buf1_ppn, RAM_PAGE_NORMAL));
|
||||
uint64_t buf1_addr = ppn_to_value(buf1_ppn);
|
||||
LE64_WRITE(desc1->addr, buf1_addr);
|
||||
LE32_WRITE(desc1->len, 513); // TROUBLESHOOT: maybe 512? but then where is status?
|
||||
LE16_WRITE(desc1->flags, VIRTQ_DESC_F_WRITE);
|
||||
LE16_WRITE(desc1->next, 0); // probably not necessary. 0 is also valid here, unlike NULL.
|
||||
|
||||
// fill request
|
||||
struct virtio_blk_req *req = ppn_to_pointer(buf0_ppn);
|
||||
virtio_blk_req_init_in(req, 1);
|
||||
|
||||
struct virtq_avail *avail = ppn_to_pointer(queue_driver_ppn);
|
||||
LE16_WRITE(avail->ring[LE16_READ(avail->idx)], 0); // our chain starts with 0
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
LE16_WRITE(avail->idx, LE16_READ(avail->idx) + 1);
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
// TODO here, check if notifications are disabled
|
||||
|
||||
// TODO: send avail buffer notification for read
|
||||
// queue is 0
|
||||
LE16_WRITE(*queue0_notify_location, 0);
|
||||
|
||||
// we hope for the best and check if it works even without notifications
|
||||
struct virtq_used *used = ppn_to_pointer(queue_device_ppn);
|
||||
while (LE16_READ(used->idx) < 1) ; // wait.
|
||||
|
||||
struct virtq_used_elem *used_elem = used->ring + 0;
|
||||
uint32_t used_id = LE32_READ(used_elem->id);
|
||||
uint32_t used_len = LE32_READ(used_elem->len);
|
||||
printlinef("used id %u len %u", used_id, used_len);
|
||||
// read status
|
||||
uint8_t *datap = (uint8_t*)ppn_to_pointer(buf1_ppn);
|
||||
uint8_t status = datap[512];
|
||||
ASSERT(status == VIRTIO_BLK_S_OK);
|
||||
printlinef("%X8 %X8 %X8 %X8", datap[0], datap[1], datap[2], datap[3]);
|
||||
|
||||
while (1) {
|
||||
fb_refresh();
|
||||
__asm__ ("hlt");
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
#include "virtio-block.h"
|
||||
#include "std.h"
|
||||
|
||||
void virtio_blk_req_init_in(struct virtio_blk_req *req, uint8_t *data, uint64_t sector) {
|
||||
void virtio_blk_req_init_in(struct virtio_blk_req *req, uint64_t sector) {
|
||||
LE32_WRITE(req->type, VIRTIO_BLK_T_IN);
|
||||
LE32_WRITE(req->reserved, 0);
|
||||
LE64_WRITE(req->sector, sector);
|
||||
memcpy(req->data, data, 512);
|
||||
}
|
||||
|
||||
void virtio_blk_req_init_out(struct virtio_blk_req *req, uint64_t sector) {
|
||||
|
|
|
|||
|
|
@ -37,3 +37,34 @@ bool virtio_dev_iter_next(struct virtio_dev_iter *it, uint16_t *bdf_out) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#define CAP_VNDR_VIRTIO 0x09
|
||||
|
||||
bool virtio_find_cap_of_type(uint16_t bdf, unsigned type, struct virtio_cap *cap) {
|
||||
uint8_t cap_ptr = pci_config_read_u8(bdf, PCI_HEADER_CAP_PTR);
|
||||
do {
|
||||
// for some reason you need to read this as 16-bit; 2 8-bit reads don't work
|
||||
uint16_t as16 = pci_config_read_u16(bdf, cap_ptr + 0);
|
||||
uint8_t cap_id = (as16) & 0xff;
|
||||
uint8_t cap_next = (as16 >> 8) & 0xff;
|
||||
if (cap_id != CAP_VNDR_VIRTIO) {
|
||||
cap_ptr = cap_next;
|
||||
continue;
|
||||
}
|
||||
as16 = pci_config_read_u16(bdf, cap_ptr + 2);
|
||||
uint8_t cap_len = (as16) & 0xff; // TODO there is 64-bit extension; how to handle?
|
||||
uint8_t cfg_type = (as16 >> 8) & 0xff;
|
||||
if (cfg_type != type) {
|
||||
cap_ptr = cap_next;
|
||||
continue;
|
||||
}
|
||||
cap->type = type;
|
||||
cap->pci_offset = cap_ptr;
|
||||
uint32_t as32 = pci_config_read_u32(bdf, cap_ptr + 4);
|
||||
cap->barno = (as32) & 0xff;
|
||||
cap->offset = pci_config_read_u32(bdf, cap_ptr + 8);
|
||||
cap->length = pci_config_read_u32(bdf, cap_ptr + 12);
|
||||
return true;
|
||||
} while (cap_ptr != 0);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue