diff --git a/Makefile b/Makefile index dab81c6..e983e67 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,8 @@ QEMUOPTS = -machine virt -bios none -kernel $(TARGET) -m 128M -smp $(CPUS) -nogr QEMUOPTS += -global virtio-mmio.force-legacy=false QEMUOPTS += -drive file=./fs.img,if=none,format=raw,id=x0 QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 +QEMUOPTS += -netdev user,id=net0 +QEMUOPTS += -device virtio-net-device,netdev=net0,bus=virtio-mmio-bus.1 ifndef CPUS CPUS := 1 endif diff --git a/arch/riscv/include/mem_layout.h b/arch/riscv/include/mem_layout.h index 7656f27..386fe0a 100644 --- a/arch/riscv/include/mem_layout.h +++ b/arch/riscv/include/mem_layout.h @@ -35,6 +35,8 @@ /* virtio mmio interface */ #define VIRTIO0 0x10001000 #define VIRTIO0_IRQ 1 +#define VIRTIO1 0x10002000 +#define VIRTIO1_IRQ 2 /* core local interruptor (CLINT), which contains the timer. */ #define CLINT 0x2000000L diff --git a/arch/riscv/trap.c b/arch/riscv/trap.c index a8db128..4d7c46b 100644 --- a/arch/riscv/trap.c +++ b/arch/riscv/trap.c @@ -6,6 +6,7 @@ #include #include #include +#include #include extern void kernel_vec(void); @@ -42,6 +43,8 @@ static int dev_intr(uint64 scause) uart_intr(); } else if(irq == VIRTIO0_IRQ) { virtio_disk_intr(); + } else if(irq == VIRTIO1_IRQ) { + virtio_net_intr(); /* * irq 0 is reserved to mean “no interrupt”. * see here: https://five-embeddev.com/riscv-priv-isa-manual/Priv-v1.12/plic.html#interrupt-identifiers-ids @@ -179,4 +182,4 @@ void trap_init_lock(void) void trap_init(void) { stvec_w((uint64)kernel_vec); -} \ No newline at end of file +} diff --git a/kernel/Makefile b/kernel/Makefile index dad7b32..03e00af 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -6,4 +6,4 @@ CFLAGS_file.o := obj-y += string.o palloc.o spinlock.o thread.o \ sleeplock.o switchto.o trampoline.o process.o\ plic.o console.o printf.o scheduler.o \ -exec.o sysfile.o syscall.o sysproc.o driver.o main.o \ No newline at end of file +exec.o sysfile.o syscall.o sysproc.o driver.o virtio_net.o main.o diff --git a/kernel/fs/virtio_disk.c b/kernel/fs/virtio_disk.c index 60a5753..fe33a6e 100644 --- a/kernel/fs/virtio_disk.c +++ b/kernel/fs/virtio_disk.c @@ -67,7 +67,7 @@ void virtio_disk_init(void) if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 || *R(VIRTIO_MMIO_VERSION) != 2 || - *R(VIRTIO_MMIO_DEVICE_ID) != 2 || + *R(VIRTIO_MMIO_DEVICE_ID) != VIRTIO_DEVICE_BLOCK || *R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551) { panic("could not find virtio disk"); } diff --git a/kernel/include/virtio.h b/kernel/include/virtio.h new file mode 100644 index 0000000..f638940 --- /dev/null +++ b/kernel/include/virtio.h @@ -0,0 +1,70 @@ +#ifndef __CAFFEINIX_KERNEL_VIRTIO_H +#define __CAFFEINIX_KERNEL_VIRTIO_H + +#include + +#define VIRTIO_MMIO_MAGIC_VALUE 0x000 +#define VIRTIO_MMIO_VERSION 0x004 +#define VIRTIO_MMIO_DEVICE_ID 0x008 +#define VIRTIO_MMIO_VENDOR_ID 0x00c +#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 +#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 +#define VIRTIO_MMIO_QUEUE_SEL 0x030 +#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 +#define VIRTIO_MMIO_QUEUE_NUM 0x038 +#define VIRTIO_MMIO_QUEUE_READY 0x044 +#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 +#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 +#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 +#define VIRTIO_MMIO_STATUS 0x070 +#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 +#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 +#define VIRTIO_MMIO_DRIVER_DESC_LOW 0x090 +#define VIRTIO_MMIO_DRIVER_DESC_HIGH 0x094 +#define VIRTIO_MMIO_DEVICE_DESC_LOW 0x0a0 +#define VIRTIO_MMIO_DEVICE_DESC_HIGH 0x0a4 +#define VIRTIO_MMIO_CONFIG 0x100 + +#define VIRTIO_DEVICE_NET 1 +#define VIRTIO_DEVICE_BLOCK 2 + +#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 +#define VIRTIO_CONFIG_S_DRIVER 2 +#define VIRTIO_CONFIG_S_DRIVER_OK 4 +#define VIRTIO_CONFIG_S_FEATURES_OK 8 + +#define VIRTIO_F_ANY_LAYOUT 27 +#define VIRTIO_RING_F_INDIRECT_DESC 28 +#define VIRTIO_RING_F_EVENT_IDX 29 + +#define VIRTIO_DES_NUM 8 + +struct virtq_desc { + uint64 addr; + uint32 len; + uint16 flags; + uint16 next; +}; + +#define VRING_DESC_F_NEXT 1 +#define VRING_DESC_F_WRITE 2 + +struct virtq_avail { + uint16 flags; + uint16 idx; + uint16 ring[VIRTIO_DES_NUM]; + uint16 unused; +}; + +struct virtq_used_elem { + uint32 id; + uint32 len; +}; + +struct virtq_used { + uint16 flags; + uint16 idx; + struct virtq_used_elem ring[VIRTIO_DES_NUM]; +}; + +#endif diff --git a/kernel/include/virtio_disk.h b/kernel/include/virtio_disk.h index d54f1fe..9fcaa17 100644 --- a/kernel/include/virtio_disk.h +++ b/kernel/include/virtio_disk.h @@ -3,78 +3,16 @@ #include #include +#include /* Block size */ #define BSIZE 1024 -#define VIRTIO_MMIO_MAGIC_VALUE 0x000 // 0x74726976 -#define VIRTIO_MMIO_VERSION 0x004 // version; should be 2 -#define VIRTIO_MMIO_DEVICE_ID 0x008 // device type; 1 is net, 2 is disk -#define VIRTIO_MMIO_VENDOR_ID 0x00c // 0x554d4551 -#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 -#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 -#define VIRTIO_MMIO_QUEUE_SEL 0x030 // select queue, write-only -#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 // max size of current queue, read-only -#define VIRTIO_MMIO_QUEUE_NUM 0x038 // size of current queue, write-only -#define VIRTIO_MMIO_QUEUE_READY 0x044 // ready bit -#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 // write-only -#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 // read-only -#define VIRTIO_MMIO_INTERRUPT_ACK 0x064 // write-only -#define VIRTIO_MMIO_STATUS 0x070 // read/write -#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080 // physical address for descriptor table, write-only -#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084 -#define VIRTIO_MMIO_DRIVER_DESC_LOW 0x090 // physical address for available ring, write-only -#define VIRTIO_MMIO_DRIVER_DESC_HIGH 0x094 -#define VIRTIO_MMIO_DEVICE_DESC_LOW 0x0a0 // physical address for used ring, write-only -#define VIRTIO_MMIO_DEVICE_DESC_HIGH 0x0a4 - -/* status register bits, from qemu virtio_config.h */ -#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 -#define VIRTIO_CONFIG_S_DRIVER 2 -#define VIRTIO_CONFIG_S_DRIVER_OK 4 -#define VIRTIO_CONFIG_S_FEATURES_OK 8 - /* device feature bits */ #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ #define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ #define VIRTIO_BLK_F_MQ 12 /* support more than one vq */ -#define VIRTIO_F_ANY_LAYOUT 27 -#define VIRTIO_RING_F_INDIRECT_DESC 28 -#define VIRTIO_RING_F_EVENT_IDX 29 - -/* this many virtio descriptors.must be a power of two. */ -#define VIRTIO_DES_NUM 8 - -struct virtq_desc { - uint64 addr; - uint32 len; - uint16 flags; - uint16 next; -}; -#define VRING_DESC_F_NEXT 1 // chained with another descriptor -#define VRING_DESC_F_WRITE 2 // device writes (vs read) - -// the (entire) avail ring, from the spec. -struct virtq_avail { - uint16 flags; // always zero - uint16 idx; // driver will write ring[idx] next - uint16 ring[VIRTIO_DES_NUM]; // descriptor numbers of chain heads - uint16 unused; -}; - -// one entry in the "used" ring, with which the -// device tells the driver about completed requests. -struct virtq_used_elem { - uint32 id; // index of start of completed descriptor chain - uint32 len; -}; - -struct virtq_used { - uint16 flags; // always zero - uint16 idx; // device increments when it adds a ring[] entry - struct virtq_used_elem ring[VIRTIO_DES_NUM]; -}; // these are specific to virtio block devices, e.g. disks, // described in Section 5.2 of the spec. @@ -97,4 +35,4 @@ void virtio_disk_rw(struct bio *b, int write); void virtio_disk_init(void); void virtio_disk_intr(void); -#endif \ No newline at end of file +#endif diff --git a/kernel/include/virtio_net.h b/kernel/include/virtio_net.h new file mode 100644 index 0000000..7e0ede8 --- /dev/null +++ b/kernel/include/virtio_net.h @@ -0,0 +1,32 @@ +#ifndef __CAFFEINIX_KERNEL_VIRTIO_NET_H +#define __CAFFEINIX_KERNEL_VIRTIO_NET_H + +#include + +#define VIRTIO_NET_F_CSUM 0 +#define VIRTIO_NET_F_GUEST_CSUM 1 +#define VIRTIO_NET_F_MAC 5 +#define VIRTIO_NET_F_GUEST_TSO4 7 +#define VIRTIO_NET_F_GUEST_TSO6 8 +#define VIRTIO_NET_F_GUEST_ECN 9 +#define VIRTIO_NET_F_GUEST_UFO 10 +#define VIRTIO_NET_F_HOST_TSO4 11 +#define VIRTIO_NET_F_HOST_TSO6 12 +#define VIRTIO_NET_F_HOST_ECN 13 +#define VIRTIO_NET_F_HOST_UFO 14 +#define VIRTIO_NET_F_MRG_RXBUF 15 +#define VIRTIO_NET_F_STATUS 16 +#define VIRTIO_NET_F_CTRL_VQ 17 +#define VIRTIO_NET_F_CTRL_RX 18 +#define VIRTIO_NET_F_CTRL_VLAN 19 +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 +#define VIRTIO_NET_F_MQ 22 +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 + +#define VIRTIO_NET_RX_QUEUE 0 +#define VIRTIO_NET_TX_QUEUE 1 + +void virtio_net_init(void); +void virtio_net_intr(void); + +#endif diff --git a/kernel/main.c b/kernel/main.c index cf84ce3..9a8ee54 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -24,6 +24,7 @@ #include #include #include +#include volatile static uint8 start = 0; extern char end[]; @@ -48,6 +49,7 @@ void main(void) iinit(); file_init(); virtio_disk_init(); + virtio_net_init(); printf("Hello! Caffeinix\n"); // thread_test(); @@ -68,4 +70,4 @@ void main(void) scheduler(); while(1); -} \ No newline at end of file +} diff --git a/kernel/plic.c b/kernel/plic.c index 60a8bbe..5764c14 100644 --- a/kernel/plic.c +++ b/kernel/plic.c @@ -5,14 +5,15 @@ void plic_init(void) { *(uint32*)(PLIC + UART0_IRQ*4) = 1; *(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1; + *(uint32*)(PLIC + VIRTIO1_IRQ*4) = 1; } void plic_init_hart(void) { int hart = cpuid(); - /* set enable bits for this hart's S-mode for the uart and virtio disk. */ - *(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ); + /* set enable bits for this hart's S-mode external devices. */ + *(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ) | (1 << VIRTIO1_IRQ); /* set this hart's S-mode priority threshold to 0. */ *(uint32*)PLIC_SPRIORITY(hart) = 0; diff --git a/kernel/virtio_net.c b/kernel/virtio_net.c new file mode 100644 index 0000000..a7f8b5d --- /dev/null +++ b/kernel/virtio_net.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include +#include +#include + +#define NUM VIRTIO_DES_NUM +#define R(r) ((volatile uint32 *)(VIRTIO1 + (r))) +#define C(r) ((volatile uint8 *)(VIRTIO1 + VIRTIO_MMIO_CONFIG + (r))) + +static struct net { + struct virtq_desc *desc[2]; + struct virtq_avail *avail[2]; + struct virtq_used *used[2]; + uint16 used_idx[2]; + uint8 ready; + uint8 mac[6]; + struct spinlock vnet_lock; +} net; + +static void virtio_net_queue_init(int q) +{ + *R(VIRTIO_MMIO_QUEUE_SEL) = q; + + if(*R(VIRTIO_MMIO_QUEUE_READY)) + PANIC("virtio net queue already ready"); + + uint32 max = *R(VIRTIO_MMIO_QUEUE_NUM_MAX); + if(max == 0) + PANIC("virtio net queue missing"); + if(max < NUM) + PANIC("virtio net queue too short"); + + net.desc[q] = palloc(); + net.avail[q] = palloc(); + net.used[q] = palloc(); + if(!net.desc[q] || !net.avail[q] || !net.used[q]) + PANIC("virtio net kalloc"); + + memset(net.desc[q], 0, PGSIZE); + memset(net.avail[q], 0, PGSIZE); + memset(net.used[q], 0, PGSIZE); + + *R(VIRTIO_MMIO_QUEUE_NUM) = NUM; + *R(VIRTIO_MMIO_QUEUE_DESC_LOW) = (uint64)net.desc[q]; + *R(VIRTIO_MMIO_QUEUE_DESC_HIGH) = (uint64)net.desc[q] >> 32; + *R(VIRTIO_MMIO_DRIVER_DESC_LOW) = (uint64)net.avail[q]; + *R(VIRTIO_MMIO_DRIVER_DESC_HIGH) = (uint64)net.avail[q] >> 32; + *R(VIRTIO_MMIO_DEVICE_DESC_LOW) = (uint64)net.used[q]; + *R(VIRTIO_MMIO_DEVICE_DESC_HIGH) = (uint64)net.used[q] >> 32; + *R(VIRTIO_MMIO_QUEUE_READY) = 1; + + net.used_idx[q] = 0; +} + +void virtio_net_init(void) +{ + uint32 status = 0; + + spinlock_init(&net.vnet_lock, "virtio_net"); + + if(*R(VIRTIO_MMIO_MAGIC_VALUE) != 0x74726976 || + *R(VIRTIO_MMIO_VERSION) != 2 || + *R(VIRTIO_MMIO_VENDOR_ID) != 0x554d4551) { + printf("virtio net: device not present\n"); + return; + } + + if(*R(VIRTIO_MMIO_DEVICE_ID) != VIRTIO_DEVICE_NET) { + printf("virtio net: unexpected device id %d\n", *R(VIRTIO_MMIO_DEVICE_ID)); + return; + } + + *R(VIRTIO_MMIO_STATUS) = status; + + status |= VIRTIO_CONFIG_S_ACKNOWLEDGE; + *R(VIRTIO_MMIO_STATUS) = status; + + status |= VIRTIO_CONFIG_S_DRIVER; + *R(VIRTIO_MMIO_STATUS) = status; + + uint64 features = *R(VIRTIO_MMIO_DEVICE_FEATURES); + uint64 negotiated = 0; + if(features & (1 << VIRTIO_NET_F_MAC)) + negotiated |= (1 << VIRTIO_NET_F_MAC); + *R(VIRTIO_MMIO_DRIVER_FEATURES) = negotiated; + + status |= VIRTIO_CONFIG_S_FEATURES_OK; + *R(VIRTIO_MMIO_STATUS) = status; + + status = *R(VIRTIO_MMIO_STATUS); + if(!(status & VIRTIO_CONFIG_S_FEATURES_OK)) + PANIC("virtio net FEATURES_OK unset"); + + if(negotiated & (1 << VIRTIO_NET_F_MAC)) { + for(int i = 0; i < 6; i++) + net.mac[i] = *C(i); + } + + virtio_net_queue_init(VIRTIO_NET_RX_QUEUE); + virtio_net_queue_init(VIRTIO_NET_TX_QUEUE); + + status |= VIRTIO_CONFIG_S_DRIVER_OK; + *R(VIRTIO_MMIO_STATUS) = status; + + net.ready = 1; + printf("virtio net: initialized"); + if(negotiated & (1 << VIRTIO_NET_F_MAC)) { + printf(" mac=%x:%x:%x:%x:%x:%x", + net.mac[0], net.mac[1], net.mac[2], + net.mac[3], net.mac[4], net.mac[5]); + } + printf("\n"); +} + +void virtio_net_intr(void) +{ + if(!net.ready) + return; + + uint32 status = *R(VIRTIO_MMIO_INTERRUPT_STATUS); + if(status) + *R(VIRTIO_MMIO_INTERRUPT_ACK) = status; +}