erythros/src/net/devices/virtio.jakt

143 lines
No EOL
4.6 KiB
Text

import relative parent::os::os { OS }
import relative parent::os::pci { PCI, PCIDevice }
enum VirtIOConfig: u8 {
acknowledge = 1
driver = 2
driver_ok = 4
}
enum VirtIOReg: u16 {
host_features = 0
guest_features = 4
queue_page_frame_number = 8
queue_size = 12
queue_select = 14
queue_notify = 16
status = 18
isr = 19
config = 20
}
class VirtIO {
public pci_device: PCIDevice
public rq_index: i64
public rq_size: u16
public rq: u32
public tq_size: u16
public tq: u32
public fn rx_frame(mut this) throws -> [u8] {
mut frame: [u8] = []
mut queue_notify: bool = false
unsafe {
cpp {
"
#include <../../src/net/devices/virtio.h>
virtio_queue *rq = (virtio_queue*)this->rq;
i64 i = this->rq_index;
i64 used_index = rq->used.index;
if (used_index < i)
used_index += 0x10000;
if (used_index && i != used_index) {
virtio_used_item* item = rq->used.ring;
u8* buffer = (u8*)rq->buffers[item[i % 256].index + 1].address;
i64 length = item[i % 256].length - 10;
for (i64 j = 0; j < length; j++)
frame.push(buffer[j]);
this->rq_index = used_index % 0x10000;
rq->available.index++;
queue_notify = true;
}
"
}
}
if queue_notify {
.pci_device.io_write_u16(offset: VirtIOReg::queue_notify as! u16, value: 0)
}
return frame
}
public fn tx_frame(mut this, anon mut data: [u8]) throws {
mut size = data.size()
unsafe {
cpp {
"
#include <../../src/net/devices/virtio.h>
virtio_queue *tq = (virtio_queue*)this->tq;
int tq_idx = tq->available.index % 256;
int tq_idx2 = tq_idx % 128;
memset((u8*)tq->buffers[tq_idx2 * 2].address, 0, 10);
u8 *buffer = (u8*)tq->buffers[(tq_idx2 * 2) + 1].address;
for (int i = 0; i < size; i++)
buffer[i] = data[i];
tq->buffers[tq_idx2 * 2].length = 10;
tq->buffers[tq_idx2 * 2].flags = 1;
tq->buffers[tq_idx2 * 2].next = (tq_idx2 * 2) + 1;
tq->buffers[(tq_idx2 * 2) + 1].length = size;
tq->buffers[(tq_idx2 * 2) + 1].flags = 0;
tq->buffers[(tq_idx2 * 2) + 1].next = 0;
tq->available.ring[tq_idx] = tq_idx2 * 2;
tq->available.index++;
"
}
}
.pci_device.io_write_u16(offset: VirtIOReg::queue_notify as! u16, value: 1)
}
fn reset_device(this) {
.pci_device.io_write_u8(offset: VirtIOReg::status as! u16, value: 0)
}
fn found_driver(this) throws {
.pci_device.io_write_u8(offset: VirtIOReg::status as! u16,
value: .pci_device.io_read_u8(VirtIOReg::status as! u16) | VirtIOConfig::acknowledge as! u8 | VirtIOConfig::driver as! u8)
}
fn setup_rx_queue(mut this) throws {
.pci_device.io_write_u16(offset: VirtIOReg::queue_select as! u16, value: 0)
.rq_size = .pci_device.io_read_u16(VirtIOReg::queue_size as! u16)
.rq = OS::device_calloc(16384)
.pci_device.io_write_u32(offset: VirtIOReg::queue_page_frame_number as! u16, value: .rq / 4096)
}
fn setup_tx_queue(mut this) throws {
.pci_device.io_write_u16(offset: VirtIOReg::queue_select as! u16, value: 1)
.tq_size = .pci_device.io_read_u16(VirtIOReg::queue_size as! u16)
.tq = OS::device_calloc(16384)
.pci_device.io_write_u32(offset: VirtIOReg::queue_page_frame_number as! u16, value: .tq / 4096)
}
fn init_queue_buffers(this) {
unsafe {
cpp {
"
#include <../../src/net/devices/virtio.h>
virtio_queue *rq = (virtio_queue*)this->rq;
virtio_queue *tq = (virtio_queue*)this->tq;
for (int i = 0; i < 128; i++) {
rq->buffers[i * 2].address = (u64)calloc(1, 16);
rq->buffers[i * 2].length = 10;
rq->buffers[i * 2].flags = 3;
rq->buffers[i * 2].next = (i * 2) + 1;
rq->buffers[(i * 2) + 1].address = (u64)calloc(1, 2048);
rq->buffers[(i * 2) + 1].length = 2048;
rq->buffers[(i * 2) + 1].flags = 2;
rq->buffers[(i * 2) + 1].next = 0;
rq->available.ring[i] = i * 2;
rq->available.ring[i + 128] = i * 2;
tq->buffers[i * 2].address = (u64)calloc(1, 16);
tq->buffers[(i * 2) + 1].address = (u64)calloc(1, 2048);
}
rq->available.index = 1;
"
}
}
}
fn init_ok(this) throws {
.pci_device.io_write_u8(offset: VirtIOReg::status as! u16,
value: .pci_device.io_read_u8(VirtIOReg::status as! u16) | VirtIOConfig::driver_ok as! u8)
.pci_device.io_write_u16(offset: VirtIOReg::queue_notify as! u16, value: 0)
}
public fn init(mut this) throws {
.reset_device()
.found_driver()
.setup_rx_queue()
.setup_tx_queue()
.init_queue_buffers()
.init_ok()
}
}