summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/e1000.c129
-rw-r--r--kernel/e1000_dev.h125
-rw-r--r--kernel/net.c374
-rw-r--r--kernel/net.h173
-rw-r--r--kernel/pci.c61
-rw-r--r--kernel/sysnet.c185
6 files changed, 1047 insertions, 0 deletions
diff --git a/kernel/e1000.c b/kernel/e1000.c
new file mode 100644
index 0000000..70a2adf
--- /dev/null
+++ b/kernel/e1000.c
@@ -0,0 +1,129 @@
+#include "types.h"
+#include "param.h"
+#include "memlayout.h"
+#include "riscv.h"
+#include "spinlock.h"
+#include "proc.h"
+#include "defs.h"
+#include "e1000_dev.h"
+#include "net.h"
+
+#define TX_RING_SIZE 16
+static struct tx_desc tx_ring[TX_RING_SIZE] __attribute__((aligned(16)));
+static struct mbuf *tx_mbufs[TX_RING_SIZE];
+
+#define RX_RING_SIZE 16
+static struct rx_desc rx_ring[RX_RING_SIZE] __attribute__((aligned(16)));
+static struct mbuf *rx_mbufs[RX_RING_SIZE];
+
+// remember where the e1000's registers live.
+static volatile uint32 *regs;
+
+struct spinlock e1000_lock;
+
+// called by pci_init().
+// xregs is the memory address at which the
+// e1000's registers are mapped.
+void
+e1000_init(uint32 *xregs)
+{
+ int i;
+
+ initlock(&e1000_lock, "e1000");
+
+ regs = xregs;
+
+ // Reset the device
+ regs[E1000_IMS] = 0; // disable interrupts
+ regs[E1000_CTL] |= E1000_CTL_RST;
+ regs[E1000_IMS] = 0; // redisable interrupts
+ __sync_synchronize();
+
+ // [E1000 14.5] Transmit initialization
+ memset(tx_ring, 0, sizeof(tx_ring));
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tx_ring[i].status = E1000_TXD_STAT_DD;
+ tx_mbufs[i] = 0;
+ }
+ regs[E1000_TDBAL] = (uint64) tx_ring;
+ if(sizeof(tx_ring) % 128 != 0)
+ panic("e1000");
+ regs[E1000_TDLEN] = sizeof(tx_ring);
+ regs[E1000_TDH] = regs[E1000_TDT] = 0;
+
+ // [E1000 14.4] Receive initialization
+ memset(rx_ring, 0, sizeof(rx_ring));
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ rx_mbufs[i] = mbufalloc(0);
+ if (!rx_mbufs[i])
+ panic("e1000");
+ rx_ring[i].addr = (uint64) rx_mbufs[i]->head;
+ }
+ regs[E1000_RDBAL] = (uint64) rx_ring;
+ if(sizeof(rx_ring) % 128 != 0)
+ panic("e1000");
+ regs[E1000_RDH] = 0;
+ regs[E1000_RDT] = RX_RING_SIZE - 1;
+ regs[E1000_RDLEN] = sizeof(rx_ring);
+
+ // filter by qemu's MAC address, 52:54:00:12:34:56
+ regs[E1000_RA] = 0x12005452;
+ regs[E1000_RA+1] = 0x5634 | (1<<31);
+ // multicast table
+ for (int i = 0; i < 4096/32; i++)
+ regs[E1000_MTA + i] = 0;
+
+ // transmitter control bits.
+ regs[E1000_TCTL] = E1000_TCTL_EN | // enable
+ E1000_TCTL_PSP | // pad short packets
+ (0x10 << E1000_TCTL_CT_SHIFT) | // collision stuff
+ (0x40 << E1000_TCTL_COLD_SHIFT);
+ regs[E1000_TIPG] = 10 | (8<<10) | (6<<20); // inter-pkt gap
+
+ // receiver control bits.
+ regs[E1000_RCTL] = E1000_RCTL_EN | // enable receiver
+ E1000_RCTL_BAM | // enable broadcast
+ E1000_RCTL_SZ_2048 | // 2048-byte rx buffers
+ E1000_RCTL_SECRC; // strip CRC
+
+ // ask e1000 for receive interrupts.
+ regs[E1000_RDTR] = 0; // interrupt after every received packet (no timer)
+ regs[E1000_RADV] = 0; // interrupt after every packet (no timer)
+ regs[E1000_IMS] = (1 << 7); // RXDW -- Receiver Descriptor Write Back
+}
+
+int
+e1000_transmit(struct mbuf *m)
+{
+ //
+ // Your code here.
+ //
+ // the mbuf contains an ethernet frame; program it into
+ // the TX descriptor ring so that the e1000 sends it. Stash
+ // a pointer so that it can be freed after sending.
+ //
+
+ return 0;
+}
+
+static void
+e1000_recv(void)
+{
+ //
+ // Your code here.
+ //
+ // Check for packets that have arrived from the e1000
+ // Create and deliver an mbuf for each packet (using net_rx()).
+ //
+}
+
+void
+e1000_intr(void)
+{
+ // tell the e1000 we've seen this interrupt;
+ // without this the e1000 won't raise any
+ // further interrupts.
+ regs[E1000_ICR] = 0xffffffff;
+
+ e1000_recv();
+}
diff --git a/kernel/e1000_dev.h b/kernel/e1000_dev.h
new file mode 100644
index 0000000..9b462df
--- /dev/null
+++ b/kernel/e1000_dev.h
@@ -0,0 +1,125 @@
+//
+// E1000 hardware definitions: registers and DMA ring format.
+// from the Intel 82540EP/EM &c manual.
+//
+
+/* Registers */
+#define E1000_CTL (0x00000/4) /* Device Control Register - RW */
+#define E1000_ICR (0x000C0/4) /* Interrupt Cause Read - R */
+#define E1000_IMS (0x000D0/4) /* Interrupt Mask Set - RW */
+#define E1000_RCTL (0x00100/4) /* RX Control - RW */
+#define E1000_TCTL (0x00400/4) /* TX Control - RW */
+#define E1000_TIPG (0x00410/4) /* TX Inter-packet gap -RW */
+#define E1000_RDBAL (0x02800/4) /* RX Descriptor Base Address Low - RW */
+#define E1000_RDTR (0x02820/4) /* RX Delay Timer */
+#define E1000_RADV (0x0282C/4) /* RX Interrupt Absolute Delay Timer */
+#define E1000_RDH (0x02810/4) /* RX Descriptor Head - RW */
+#define E1000_RDT (0x02818/4) /* RX Descriptor Tail - RW */
+#define E1000_RDLEN (0x02808/4) /* RX Descriptor Length - RW */
+#define E1000_RSRPD (0x02C00/4) /* RX Small Packet Detect Interrupt */
+#define E1000_TDBAL (0x03800/4) /* TX Descriptor Base Address Low - RW */
+#define E1000_TDLEN (0x03808/4) /* TX Descriptor Length - RW */
+#define E1000_TDH (0x03810/4) /* TX Descriptor Head - RW */
+#define E1000_TDT (0x03818/4) /* TX Descripotr Tail - RW */
+#define E1000_MTA (0x05200/4) /* Multicast Table Array - RW Array */
+#define E1000_RA (0x05400/4) /* Receive Address - RW Array */
+
+/* Device Control */
+#define E1000_CTL_SLU 0x00000040 /* set link up */
+#define E1000_CTL_FRCSPD 0x00000800 /* force speed */
+#define E1000_CTL_FRCDPLX 0x00001000 /* force duplex */
+#define E1000_CTL_RST 0x00400000 /* full reset */
+
+/* Transmit Control */
+#define E1000_TCTL_RST 0x00000001 /* software reset */
+#define E1000_TCTL_EN 0x00000002 /* enable tx */
+#define E1000_TCTL_BCE 0x00000004 /* busy check enable */
+#define E1000_TCTL_PSP 0x00000008 /* pad short packets */
+#define E1000_TCTL_CT 0x00000ff0 /* collision threshold */
+#define E1000_TCTL_CT_SHIFT 4
+#define E1000_TCTL_COLD 0x003ff000 /* collision distance */
+#define E1000_TCTL_COLD_SHIFT 12
+#define E1000_TCTL_SWXOFF 0x00400000 /* SW Xoff transmission */
+#define E1000_TCTL_PBE 0x00800000 /* Packet Burst Enable */
+#define E1000_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
+#define E1000_TCTL_NRTU 0x02000000 /* No Re-transmit on underrun */
+#define E1000_TCTL_MULR 0x10000000 /* Multiple request support */
+
+/* Receive Control */
+#define E1000_RCTL_RST 0x00000001 /* Software reset */
+#define E1000_RCTL_EN 0x00000002 /* enable */
+#define E1000_RCTL_SBP 0x00000004 /* store bad packet */
+#define E1000_RCTL_UPE 0x00000008 /* unicast promiscuous enable */
+#define E1000_RCTL_MPE 0x00000010 /* multicast promiscuous enab */
+#define E1000_RCTL_LPE 0x00000020 /* long packet enable */
+#define E1000_RCTL_LBM_NO 0x00000000 /* no loopback mode */
+#define E1000_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
+#define E1000_RCTL_LBM_SLP 0x00000080 /* serial link loopback mode */
+#define E1000_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */
+#define E1000_RCTL_DTYP_MASK 0x00000C00 /* Descriptor type mask */
+#define E1000_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */
+#define E1000_RCTL_RDMTS_HALF 0x00000000 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_QUAT 0x00000100 /* rx desc min threshold size */
+#define E1000_RCTL_RDMTS_EIGTH 0x00000200 /* rx desc min threshold size */
+#define E1000_RCTL_MO_SHIFT 12 /* multicast offset shift */
+#define E1000_RCTL_MO_0 0x00000000 /* multicast offset 11:0 */
+#define E1000_RCTL_MO_1 0x00001000 /* multicast offset 12:1 */
+#define E1000_RCTL_MO_2 0x00002000 /* multicast offset 13:2 */
+#define E1000_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */
+#define E1000_RCTL_MDR 0x00004000 /* multicast desc ring 0 */
+#define E1000_RCTL_BAM 0x00008000 /* broadcast enable */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 0 */
+#define E1000_RCTL_SZ_2048 0x00000000 /* rx buffer size 2048 */
+#define E1000_RCTL_SZ_1024 0x00010000 /* rx buffer size 1024 */
+#define E1000_RCTL_SZ_512 0x00020000 /* rx buffer size 512 */
+#define E1000_RCTL_SZ_256 0x00030000 /* rx buffer size 256 */
+/* these buffer sizes are valid if E1000_RCTL_BSEX is 1 */
+#define E1000_RCTL_SZ_16384 0x00010000 /* rx buffer size 16384 */
+#define E1000_RCTL_SZ_8192 0x00020000 /* rx buffer size 8192 */
+#define E1000_RCTL_SZ_4096 0x00030000 /* rx buffer size 4096 */
+#define E1000_RCTL_VFE 0x00040000 /* vlan filter enable */
+#define E1000_RCTL_CFIEN 0x00080000 /* canonical form enable */
+#define E1000_RCTL_CFI 0x00100000 /* canonical form indicator */
+#define E1000_RCTL_DPF 0x00400000 /* discard pause frames */
+#define E1000_RCTL_PMCF 0x00800000 /* pass MAC control frames */
+#define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */
+#define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */
+#define E1000_RCTL_FLXBUF_MASK 0x78000000 /* Flexible buffer size */
+#define E1000_RCTL_FLXBUF_SHIFT 27 /* Flexible buffer shift */
+
+#define DATA_MAX 1518
+
+/* Transmit Descriptor command definitions [E1000 3.3.3.1] */
+#define E1000_TXD_CMD_EOP 0x01 /* End of Packet */
+#define E1000_TXD_CMD_RS 0x08 /* Report Status */
+
+/* Transmit Descriptor status definitions [E1000 3.3.3.2] */
+#define E1000_TXD_STAT_DD 0x00000001 /* Descriptor Done */
+
+// [E1000 3.3.3]
+struct tx_desc
+{
+ uint64 addr;
+ uint16 length;
+ uint8 cso;
+ uint8 cmd;
+ uint8 status;
+ uint8 css;
+ uint16 special;
+};
+
+/* Receive Descriptor bit definitions [E1000 3.2.3.1] */
+#define E1000_RXD_STAT_DD 0x01 /* Descriptor Done */
+#define E1000_RXD_STAT_EOP 0x02 /* End of Packet */
+
+// [E1000 3.2.3]
+struct rx_desc
+{
+ uint64 addr; /* Address of the descriptor's data buffer */
+ uint16 length; /* Length of data DMAed into data buffer */
+ uint16 csum; /* Packet checksum */
+ uint8 status; /* Descriptor status */
+ uint8 errors; /* Descriptor Errors */
+ uint16 special;
+};
+
diff --git a/kernel/net.c b/kernel/net.c
new file mode 100644
index 0000000..137ea2b
--- /dev/null
+++ b/kernel/net.c
@@ -0,0 +1,374 @@
+//
+// networking protocol support (IP, UDP, ARP, etc.).
+//
+
+#include "types.h"
+#include "param.h"
+#include "memlayout.h"
+#include "riscv.h"
+#include "spinlock.h"
+#include "proc.h"
+#include "net.h"
+#include "defs.h"
+
+static uint32 local_ip = MAKE_IP_ADDR(10, 0, 2, 15); // qemu's idea of the guest IP
+static uint8 local_mac[ETHADDR_LEN] = { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 };
+static uint8 broadcast_mac[ETHADDR_LEN] = { 0xFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF };
+
+// Strips data from the start of the buffer and returns a pointer to it.
+// Returns 0 if less than the full requested length is available.
+char *
+mbufpull(struct mbuf *m, unsigned int len)
+{
+ char *tmp = m->head;
+ if (m->len < len)
+ return 0;
+ m->len -= len;
+ m->head += len;
+ return tmp;
+}
+
+// Prepends data to the beginning of the buffer and returns a pointer to it.
+char *
+mbufpush(struct mbuf *m, unsigned int len)
+{
+ m->head -= len;
+ if (m->head < m->buf)
+ panic("mbufpush");
+ m->len += len;
+ return m->head;
+}
+
+// Appends data to the end of the buffer and returns a pointer to it.
+char *
+mbufput(struct mbuf *m, unsigned int len)
+{
+ char *tmp = m->head + m->len;
+ m->len += len;
+ if (m->len > MBUF_SIZE)
+ panic("mbufput");
+ return tmp;
+}
+
+// Strips data from the end of the buffer and returns a pointer to it.
+// Returns 0 if less than the full requested length is available.
+char *
+mbuftrim(struct mbuf *m, unsigned int len)
+{
+ if (len > m->len)
+ return 0;
+ m->len -= len;
+ return m->head + m->len;
+}
+
+// Allocates a packet buffer.
+struct mbuf *
+mbufalloc(unsigned int headroom)
+{
+ struct mbuf *m;
+
+ if (headroom > MBUF_SIZE)
+ return 0;
+ m = kalloc();
+ if (m == 0)
+ return 0;
+ m->next = 0;
+ m->head = (char *)m->buf + headroom;
+ m->len = 0;
+ memset(m->buf, 0, sizeof(m->buf));
+ return m;
+}
+
+// Frees a packet buffer.
+void
+mbuffree(struct mbuf *m)
+{
+ kfree(m);
+}
+
+// Pushes an mbuf to the end of the queue.
+void
+mbufq_pushtail(struct mbufq *q, struct mbuf *m)
+{
+ m->next = 0;
+ if (!q->head){
+ q->head = q->tail = m;
+ return;
+ }
+ q->tail->next = m;
+ q->tail = m;
+}
+
+// Pops an mbuf from the start of the queue.
+struct mbuf *
+mbufq_pophead(struct mbufq *q)
+{
+ struct mbuf *head = q->head;
+ if (!head)
+ return 0;
+ q->head = head->next;
+ return head;
+}
+
+// Returns one (nonzero) if the queue is empty.
+int
+mbufq_empty(struct mbufq *q)
+{
+ return q->head == 0;
+}
+
+// Intializes a queue of mbufs.
+void
+mbufq_init(struct mbufq *q)
+{
+ q->head = 0;
+}
+
+// This code is lifted from FreeBSD's ping.c, and is copyright by the Regents
+// of the University of California.
+static unsigned short
+in_cksum(const unsigned char *addr, int len)
+{
+ int nleft = len;
+ const unsigned short *w = (const unsigned short *)addr;
+ unsigned int sum = 0;
+ unsigned short answer = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+ * sequential 16 bit words to it, and at the end, fold back all the
+ * carry bits from the top 16 bits into the lower 16 bits.
+ */
+ while (nleft > 1) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if (nleft == 1) {
+ *(unsigned char *)(&answer) = *(const unsigned char *)w;
+ sum += answer;
+ }
+
+ /* add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum += (sum >> 16);
+ /* guaranteed now that the lower 16 bits of sum are correct */
+
+ answer = ~sum; /* truncate to 16 bits */
+ return answer;
+}
+
+// sends an ethernet packet
+static void
+net_tx_eth(struct mbuf *m, uint16 ethtype)
+{
+ struct eth *ethhdr;
+
+ ethhdr = mbufpushhdr(m, *ethhdr);
+ memmove(ethhdr->shost, local_mac, ETHADDR_LEN);
+ // In a real networking stack, dhost would be set to the address discovered
+ // through ARP. Because we don't support enough of the ARP protocol, set it
+ // to broadcast instead.
+ memmove(ethhdr->dhost, broadcast_mac, ETHADDR_LEN);
+ ethhdr->type = htons(ethtype);
+ if (e1000_transmit(m)) {
+ mbuffree(m);
+ }
+}
+
+// sends an IP packet
+static void
+net_tx_ip(struct mbuf *m, uint8 proto, uint32 dip)
+{
+ struct ip *iphdr;
+
+ // push the IP header
+ iphdr = mbufpushhdr(m, *iphdr);
+ memset(iphdr, 0, sizeof(*iphdr));
+ iphdr->ip_vhl = (4 << 4) | (20 >> 2);
+ iphdr->ip_p = proto;
+ iphdr->ip_src = htonl(local_ip);
+ iphdr->ip_dst = htonl(dip);
+ iphdr->ip_len = htons(m->len);
+ iphdr->ip_ttl = 100;
+ iphdr->ip_sum = in_cksum((unsigned char *)iphdr, sizeof(*iphdr));
+
+ // now on to the ethernet layer
+ net_tx_eth(m, ETHTYPE_IP);
+}
+
+// sends a UDP packet
+void
+net_tx_udp(struct mbuf *m, uint32 dip,
+ uint16 sport, uint16 dport)
+{
+ struct udp *udphdr;
+
+ // put the UDP header
+ udphdr = mbufpushhdr(m, *udphdr);
+ udphdr->sport = htons(sport);
+ udphdr->dport = htons(dport);
+ udphdr->ulen = htons(m->len);
+ udphdr->sum = 0; // zero means no checksum is provided
+
+ // now on to the IP layer
+ net_tx_ip(m, IPPROTO_UDP, dip);
+}
+
+// sends an ARP packet
+static int
+net_tx_arp(uint16 op, uint8 dmac[ETHADDR_LEN], uint32 dip)
+{
+ struct mbuf *m;
+ struct arp *arphdr;
+
+ m = mbufalloc(MBUF_DEFAULT_HEADROOM);
+ if (!m)
+ return -1;
+
+ // generic part of ARP header
+ arphdr = mbufputhdr(m, *arphdr);
+ arphdr->hrd = htons(ARP_HRD_ETHER);
+ arphdr->pro = htons(ETHTYPE_IP);
+ arphdr->hln = ETHADDR_LEN;
+ arphdr->pln = sizeof(uint32);
+ arphdr->op = htons(op);
+
+ // ethernet + IP part of ARP header
+ memmove(arphdr->sha, local_mac, ETHADDR_LEN);
+ arphdr->sip = htonl(local_ip);
+ memmove(arphdr->tha, dmac, ETHADDR_LEN);
+ arphdr->tip = htonl(dip);
+
+ // header is ready, send the packet
+ net_tx_eth(m, ETHTYPE_ARP);
+ return 0;
+}
+
+// receives an ARP packet
+static void
+net_rx_arp(struct mbuf *m)
+{
+ struct arp *arphdr;
+ uint8 smac[ETHADDR_LEN];
+ uint32 sip, tip;
+
+ arphdr = mbufpullhdr(m, *arphdr);
+ if (!arphdr)
+ goto done;
+
+ // validate the ARP header
+ if (ntohs(arphdr->hrd) != ARP_HRD_ETHER ||
+ ntohs(arphdr->pro) != ETHTYPE_IP ||
+ arphdr->hln != ETHADDR_LEN ||
+ arphdr->pln != sizeof(uint32)) {
+ goto done;
+ }
+
+ // only requests are supported so far
+ // check if our IP was solicited
+ tip = ntohl(arphdr->tip); // target IP address
+ if (ntohs(arphdr->op) != ARP_OP_REQUEST || tip != local_ip)
+ goto done;
+
+ // handle the ARP request
+ memmove(smac, arphdr->sha, ETHADDR_LEN); // sender's ethernet address
+ sip = ntohl(arphdr->sip); // sender's IP address (qemu's slirp)
+ net_tx_arp(ARP_OP_REPLY, smac, sip);
+
+done:
+ mbuffree(m);
+}
+
+// receives a UDP packet
+static void
+net_rx_udp(struct mbuf *m, uint16 len, struct ip *iphdr)
+{
+ struct udp *udphdr;
+ uint32 sip;
+ uint16 sport, dport;
+
+
+ udphdr = mbufpullhdr(m, *udphdr);
+ if (!udphdr)
+ goto fail;
+
+ // TODO: validate UDP checksum
+
+ // validate lengths reported in headers
+ if (ntohs(udphdr->ulen) != len)
+ goto fail;
+ len -= sizeof(*udphdr);
+ if (len > m->len)
+ goto fail;
+ // minimum packet size could be larger than the payload
+ mbuftrim(m, m->len - len);
+
+ // parse the necessary fields
+ sip = ntohl(iphdr->ip_src);
+ sport = ntohs(udphdr->sport);
+ dport = ntohs(udphdr->dport);
+ sockrecvudp(m, sip, dport, sport);
+ return;
+
+fail:
+ mbuffree(m);
+}
+
+// receives an IP packet
+static void
+net_rx_ip(struct mbuf *m)
+{
+ struct ip *iphdr;
+ uint16 len;
+
+ iphdr = mbufpullhdr(m, *iphdr);
+ if (!iphdr)
+ goto fail;
+
+ // check IP version and header len
+ if (iphdr->ip_vhl != ((4 << 4) | (20 >> 2)))
+ goto fail;
+ // validate IP checksum
+ if (in_cksum((unsigned char *)iphdr, sizeof(*iphdr)))
+ goto fail;
+ // can't support fragmented IP packets
+ if (htons(iphdr->ip_off) != 0)
+ goto fail;
+ // is the packet addressed to us?
+ if (htonl(iphdr->ip_dst) != local_ip)
+ goto fail;
+ // can only support UDP
+ if (iphdr->ip_p != IPPROTO_UDP)
+ goto fail;
+
+ len = ntohs(iphdr->ip_len) - sizeof(*iphdr);
+ net_rx_udp(m, len, iphdr);
+ return;
+
+fail:
+ mbuffree(m);
+}
+
+// called by e1000 driver's interrupt handler to deliver a packet to the
+// networking stack
+void net_rx(struct mbuf *m)
+{
+ struct eth *ethhdr;
+ uint16 type;
+
+ ethhdr = mbufpullhdr(m, *ethhdr);
+ if (!ethhdr) {
+ mbuffree(m);
+ return;
+ }
+
+ type = ntohs(ethhdr->type);
+ if (type == ETHTYPE_IP)
+ net_rx_ip(m);
+ else if (type == ETHTYPE_ARP)
+ net_rx_arp(m);
+ else
+ mbuffree(m);
+}
diff --git a/kernel/net.h b/kernel/net.h
new file mode 100644
index 0000000..9e6fefe
--- /dev/null
+++ b/kernel/net.h
@@ -0,0 +1,173 @@
+//
+// packet buffer management
+//
+
+#define MBUF_SIZE 2048
+#define MBUF_DEFAULT_HEADROOM 128
+
+struct mbuf {
+ struct mbuf *next; // the next mbuf in the chain
+ char *head; // the current start position of the buffer
+ unsigned int len; // the length of the buffer
+ char buf[MBUF_SIZE]; // the backing store
+};
+
+char *mbufpull(struct mbuf *m, unsigned int len);
+char *mbufpush(struct mbuf *m, unsigned int len);
+char *mbufput(struct mbuf *m, unsigned int len);
+char *mbuftrim(struct mbuf *m, unsigned int len);
+
+// The above functions manipulate the size and position of the buffer:
+// <- push <- trim
+// -> pull -> put
+// [-headroom-][------buffer------][-tailroom-]
+// |----------------MBUF_SIZE-----------------|
+//
+// These marcos automatically typecast and determine the size of header structs.
+// In most situations you should use these instead of the raw ops above.
+#define mbufpullhdr(mbuf, hdr) (typeof(hdr)*)mbufpull(mbuf, sizeof(hdr))
+#define mbufpushhdr(mbuf, hdr) (typeof(hdr)*)mbufpush(mbuf, sizeof(hdr))
+#define mbufputhdr(mbuf, hdr) (typeof(hdr)*)mbufput(mbuf, sizeof(hdr))
+#define mbuftrimhdr(mbuf, hdr) (typeof(hdr)*)mbuftrim(mbuf, sizeof(hdr))
+
+struct mbuf *mbufalloc(unsigned int headroom);
+void mbuffree(struct mbuf *m);
+
+struct mbufq {
+ struct mbuf *head; // the first element in the queue
+ struct mbuf *tail; // the last element in the queue
+};
+
+void mbufq_pushtail(struct mbufq *q, struct mbuf *m);
+struct mbuf *mbufq_pophead(struct mbufq *q);
+int mbufq_empty(struct mbufq *q);
+void mbufq_init(struct mbufq *q);
+
+
+//
+// endianness support
+//
+
+static inline uint16 bswaps(uint16 val)
+{
+ return (((val & 0x00ffU) << 8) |
+ ((val & 0xff00U) >> 8));
+}
+
+static inline uint32 bswapl(uint32 val)
+{
+ return (((val & 0x000000ffUL) << 24) |
+ ((val & 0x0000ff00UL) << 8) |
+ ((val & 0x00ff0000UL) >> 8) |
+ ((val & 0xff000000UL) >> 24));
+}
+
+// Use these macros to convert network bytes to the native byte order.
+// Note that Risc-V uses little endian while network order is big endian.
+#define ntohs bswaps
+#define ntohl bswapl
+#define htons bswaps
+#define htonl bswapl
+
+
+//
+// useful networking headers
+//
+
+#define ETHADDR_LEN 6
+
+// an Ethernet packet header (start of the packet).
+struct eth {
+ uint8 dhost[ETHADDR_LEN];
+ uint8 shost[ETHADDR_LEN];
+ uint16 type;
+} __attribute__((packed));
+
+#define ETHTYPE_IP 0x0800 // Internet protocol
+#define ETHTYPE_ARP 0x0806 // Address resolution protocol
+
+// an IP packet header (comes after an Ethernet header).
+struct ip {
+ uint8 ip_vhl; // version << 4 | header length >> 2
+ uint8 ip_tos; // type of service
+ uint16 ip_len; // total length
+ uint16 ip_id; // identification
+ uint16 ip_off; // fragment offset field
+ uint8 ip_ttl; // time to live
+ uint8 ip_p; // protocol
+ uint16 ip_sum; // checksum
+ uint32 ip_src, ip_dst;
+};
+
+#define IPPROTO_ICMP 1 // Control message protocol
+#define IPPROTO_TCP 6 // Transmission control protocol
+#define IPPROTO_UDP 17 // User datagram protocol
+
+#define MAKE_IP_ADDR(a, b, c, d) \
+ (((uint32)a << 24) | ((uint32)b << 16) | \
+ ((uint32)c << 8) | (uint32)d)
+
+// a UDP packet header (comes after an IP header).
+struct udp {
+ uint16 sport; // source port
+ uint16 dport; // destination port
+ uint16 ulen; // length, including udp header, not including IP header
+ uint16 sum; // checksum
+};
+
+// an ARP packet (comes after an Ethernet header).
+struct arp {
+ uint16 hrd; // format of hardware address
+ uint16 pro; // format of protocol address
+ uint8 hln; // length of hardware address
+ uint8 pln; // length of protocol address
+ uint16 op; // operation
+
+ char sha[ETHADDR_LEN]; // sender hardware address
+ uint32 sip; // sender IP address
+ char tha[ETHADDR_LEN]; // target hardware address
+ uint32 tip; // target IP address
+} __attribute__((packed));
+
+#define ARP_HRD_ETHER 1 // Ethernet
+
+enum {
+ ARP_OP_REQUEST = 1, // requests hw addr given protocol addr
+ ARP_OP_REPLY = 2, // replies a hw addr given protocol addr
+};
+
+// an DNS packet (comes after an UDP header).
+struct dns {
+ uint16 id; // request ID
+
+ uint8 rd: 1; // recursion desired
+ uint8 tc: 1; // truncated
+ uint8 aa: 1; // authoritive
+ uint8 opcode: 4;
+ uint8 qr: 1; // query/response
+ uint8 rcode: 4; // response code
+ uint8 cd: 1; // checking disabled
+ uint8 ad: 1; // authenticated data
+ uint8 z: 1;
+ uint8 ra: 1; // recursion available
+
+ uint16 qdcount; // number of question entries
+ uint16 ancount; // number of resource records in answer section
+ uint16 nscount; // number of NS resource records in authority section
+ uint16 arcount; // number of resource records in additional records
+} __attribute__((packed));
+
+struct dns_question {
+ uint16 qtype;
+ uint16 qclass;
+} __attribute__((packed));
+
+#define ARECORD (0x0001)
+#define QCLASS (0x0001)
+
+struct dns_data {
+ uint16 type;
+ uint16 class;
+ uint32 ttl;
+ uint16 len;
+} __attribute__((packed));
diff --git a/kernel/pci.c b/kernel/pci.c
new file mode 100644
index 0000000..5cd2102
--- /dev/null
+++ b/kernel/pci.c
@@ -0,0 +1,61 @@
+//
+// simple PCI-Express initialization, only
+// works for qemu and its e1000 card.
+//
+
+#include "types.h"
+#include "param.h"
+#include "memlayout.h"
+#include "riscv.h"
+#include "spinlock.h"
+#include "proc.h"
+#include "defs.h"
+
+void
+pci_init()
+{
+ // we'll place the e1000 registers at this address.
+ // vm.c maps this range.
+ uint64 e1000_regs = 0x40000000L;
+
+ // qemu -machine virt puts PCIe config space here.
+ // vm.c maps this range.
+ uint32 *ecam = (uint32 *) 0x30000000L;
+
+ // look at each possible PCI device on bus 0.
+ for(int dev = 0; dev < 32; dev++){
+ int bus = 0;
+ int func = 0;
+ int offset = 0;
+ uint32 off = (bus << 16) | (dev << 11) | (func << 8) | (offset);
+ volatile uint32 *base = ecam + off;
+ uint32 id = base[0];
+
+ // 100e:8086 is an e1000
+ if(id == 0x100e8086){
+ // command and status register.
+ // bit 0 : I/O access enable
+ // bit 1 : memory access enable
+ // bit 2 : enable mastering
+ base[1] = 7;
+ __sync_synchronize();
+
+ for(int i = 0; i < 6; i++){
+ uint32 old = base[4+i];
+
+ // writing all 1's to the BAR causes it to be
+ // replaced with its size.
+ base[4+i] = 0xffffffff;
+ __sync_synchronize();
+
+ base[4+i] = old;
+ }
+
+ // tell the e1000 to reveal its registers at
+ // physical address 0x40000000.
+ base[4+0] = e1000_regs;
+
+ e1000_init((uint32*)e1000_regs);
+ }
+ }
+}
diff --git a/kernel/sysnet.c b/kernel/sysnet.c
new file mode 100644
index 0000000..1c48cb3
--- /dev/null
+++ b/kernel/sysnet.c
@@ -0,0 +1,185 @@
+//
+// network system calls.
+//
+
+#include "types.h"
+#include "param.h"
+#include "memlayout.h"
+#include "riscv.h"
+#include "spinlock.h"
+#include "proc.h"
+#include "defs.h"
+#include "fs.h"
+#include "sleeplock.h"
+#include "file.h"
+#include "net.h"
+
+struct sock {
+ struct sock *next; // the next socket in the list
+ uint32 raddr; // the remote IPv4 address
+ uint16 lport; // the local UDP port number
+ uint16 rport; // the remote UDP port number
+ struct spinlock lock; // protects the rxq
+ struct mbufq rxq; // a queue of packets waiting to be received
+};
+
+static struct spinlock lock;
+static struct sock *sockets;
+
+void
+sockinit(void)
+{
+ initlock(&lock, "socktbl");
+}
+
+int
+sockalloc(struct file **f, uint32 raddr, uint16 lport, uint16 rport)
+{
+ struct sock *si, *pos;
+
+ si = 0;
+ *f = 0;
+ if ((*f = filealloc()) == 0)
+ goto bad;
+ if ((si = (struct sock*)kalloc()) == 0)
+ goto bad;
+
+ // initialize objects
+ si->raddr = raddr;
+ si->lport = lport;
+ si->rport = rport;
+ initlock(&si->lock, "sock");
+ mbufq_init(&si->rxq);
+ (*f)->type = FD_SOCK;
+ (*f)->readable = 1;
+ (*f)->writable = 1;
+ (*f)->sock = si;
+
+ // add to list of sockets
+ acquire(&lock);
+ pos = sockets;
+ while (pos) {
+ if (pos->raddr == raddr &&
+ pos->lport == lport &&
+ pos->rport == rport) {
+ release(&lock);
+ goto bad;
+ }
+ pos = pos->next;
+ }
+ si->next = sockets;
+ sockets = si;
+ release(&lock);
+ return 0;
+
+bad:
+ if (si)
+ kfree((char*)si);
+ if (*f)
+ fileclose(*f);
+ return -1;
+}
+
+void
+sockclose(struct sock *si)
+{
+ struct sock **pos;
+ struct mbuf *m;
+
+ // remove from list of sockets
+ acquire(&lock);
+ pos = &sockets;
+ while (*pos) {
+ if (*pos == si){
+ *pos = si->next;
+ break;
+ }
+ pos = &(*pos)->next;
+ }
+ release(&lock);
+
+ // free any pending mbufs
+ while (!mbufq_empty(&si->rxq)) {
+ m = mbufq_pophead(&si->rxq);
+ mbuffree(m);
+ }
+
+ kfree((char*)si);
+}
+
+int
+sockread(struct sock *si, uint64 addr, int n)
+{
+ struct proc *pr = myproc();
+ struct mbuf *m;
+ int len;
+
+ acquire(&si->lock);
+ while (mbufq_empty(&si->rxq) && !pr->killed) {
+ sleep(&si->rxq, &si->lock);
+ }
+ if (pr->killed) {
+ release(&si->lock);
+ return -1;
+ }
+ m = mbufq_pophead(&si->rxq);
+ release(&si->lock);
+
+ len = m->len;
+ if (len > n)
+ len = n;
+ if (copyout(pr->pagetable, addr, m->head, len) == -1) {
+ mbuffree(m);
+ return -1;
+ }
+ mbuffree(m);
+ return len;
+}
+
+int
+sockwrite(struct sock *si, uint64 addr, int n)
+{
+ struct proc *pr = myproc();
+ struct mbuf *m;
+
+ m = mbufalloc(MBUF_DEFAULT_HEADROOM);
+ if (!m)
+ return -1;
+
+ if (copyin(pr->pagetable, mbufput(m, n), addr, n) == -1) {
+ mbuffree(m);
+ return -1;
+ }
+ net_tx_udp(m, si->raddr, si->lport, si->rport);
+ return n;
+}
+
+// called by protocol handler layer to deliver UDP packets
+void
+sockrecvudp(struct mbuf *m, uint32 raddr, uint16 lport, uint16 rport)
+{
+ //
+ // Find the socket that handles this mbuf and deliver it, waking
+ // any sleeping reader. Free the mbuf if there are no sockets
+ // registered to handle it.
+ //
+ struct sock *si;
+
+ acquire(&lock);
+ si = sockets;
+ while (si) {
+ if (si->raddr == raddr && si->lport == lport && si->rport == rport)
+ goto found;
+ si = si->next;
+ }
+ release(&lock);
+ mbuffree(m);
+ return;
+
+found:
+ acquire(&si->lock);
+ mbufq_pushtail(&si->rxq, m);
+ wakeup(&si->rxq);
+ release(&si->lock);
+ release(&lock);
+}