Compare commits
4 Commits
checkpoint
...
net
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d0a728f73 | ||
|
|
079dcbaa31 | ||
|
|
0b94a6a45f | ||
|
|
10584ccdde |
12
Makefile
12
Makefile
@ -276,9 +276,6 @@ endif
|
||||
fs.img: mkfs/mkfs README $(UEXTRA) $(UPROGS)
|
||||
mkfs/mkfs fs.img README $(UEXTRA) $(UPROGS)
|
||||
|
||||
newfs.img:
|
||||
-mv -f fs.img fs.img.bk
|
||||
|
||||
-include kernel/*.d user/*.d
|
||||
|
||||
clean:
|
||||
@ -286,7 +283,7 @@ clean:
|
||||
*/*.o */*.d */*.asm */*.sym \
|
||||
$U/initcode $U/initcode.out $U/usys.S $U/_* \
|
||||
$K/kernel \
|
||||
mkfs/mkfs fs.img fs.img.bk .gdbinit __pycache__ xv6.out* \
|
||||
mkfs/mkfs fs.img .gdbinit __pycache__ xv6.out* \
|
||||
ph barrier
|
||||
|
||||
# try to generate a unique GDB port
|
||||
@ -315,12 +312,7 @@ QEMUOPTS += -netdev user,id=net0,hostfwd=udp::$(FWDPORT1)-:2000,hostfwd=udp::$(F
|
||||
QEMUOPTS += -device e1000,netdev=net0,bus=pcie.0
|
||||
endif
|
||||
|
||||
# makes a new fs.img
|
||||
qemu: newfs.img $K/kernel fs.img
|
||||
$(QEMU) $(QEMUOPTS)
|
||||
|
||||
# runs with existing fs.img, if present
|
||||
qemu-fs: $K/kernel fs.img
|
||||
qemu: $K/kernel fs.img
|
||||
$(QEMU) $(QEMUOPTS)
|
||||
|
||||
.gdbinit: .gdbinit.tmpl-riscv
|
||||
|
||||
@ -1 +1 @@
|
||||
LAB=lock
|
||||
LAB=net
|
||||
|
||||
@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("xv6.out"))
|
||||
|
||||
@test(0, "running kalloctest")
|
||||
def test_kalloctest():
|
||||
r.run_qemu(shell_script([
|
||||
'kalloctest'
|
||||
]), timeout=300)
|
||||
|
||||
@test(10, "kalloctest: test1", parent=test_kalloctest)
|
||||
def test_kalloctest_test1():
|
||||
r.match('^test1 OK$')
|
||||
|
||||
@test(10, "kalloctest: test2", parent=test_kalloctest)
|
||||
def test_kalloctest_test2():
|
||||
r.match('^test2 OK$')
|
||||
|
||||
@test(10, "kalloctest: test3", parent=test_kalloctest)
|
||||
def test_kalloctest_test3():
|
||||
r.match('^test3 OK$')
|
||||
|
||||
@test(10, "kalloctest: sbrkmuch")
|
||||
def test_sbrkmuch():
|
||||
r.run_qemu(shell_script([
|
||||
'usertests sbrkmuch'
|
||||
]), timeout=90)
|
||||
r.match('^ALL TESTS PASSED$')
|
||||
|
||||
@test(0, "running bcachetest")
|
||||
def test_bcachetest():
|
||||
r.run_qemu(shell_script([
|
||||
'bcachetest'
|
||||
]), timeout=150)
|
||||
|
||||
@test(20, "bcachetest: test0", parent=test_bcachetest)
|
||||
def test_bcachetest_test0():
|
||||
r.match('^test0: OK$')
|
||||
|
||||
@test(10, "bcachetest: test1", parent=test_bcachetest)
|
||||
def test_bcachetest_test1():
|
||||
r.match('^test1 OK$')
|
||||
|
||||
@test(10, "bcachetest: test2", parent=test_bcachetest)
|
||||
def test_bcachetest_test2():
|
||||
r.match('^test2 OK$')
|
||||
|
||||
@test(10, "bcachetest: test3", parent=test_bcachetest)
|
||||
def test_bcachetest_test3():
|
||||
r.match('^test3 OK$')
|
||||
|
||||
@test(19, "usertests")
|
||||
def test_usertests():
|
||||
r.run_qemu(shell_script([
|
||||
'usertests -q'
|
||||
]), timeout=300)
|
||||
r.match('^ALL TESTS PASSED$')
|
||||
|
||||
@test(1, "time")
|
||||
def test_time():
|
||||
check_time()
|
||||
|
||||
run_tests()
|
||||
66
grade-lab-net
Executable file
66
grade-lab-net
Executable file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("xv6.out"))
|
||||
|
||||
serverout = None
|
||||
|
||||
@test(0, "running nettest")
|
||||
def test_nettest():
|
||||
global serverout
|
||||
server = subprocess.Popen(["python3", "./nettest.py", "grade"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
r.run_qemu(shell_script([
|
||||
'nettest grade'
|
||||
]), timeout=30)
|
||||
server.terminate()
|
||||
serverout = server.communicate()[0].decode()
|
||||
|
||||
@test(20, "nettest: txone", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
assert_lines_match(serverout, 'txone: OK')
|
||||
|
||||
@test(20, "nettest: arp_rx", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
r.match('arp_rx: received an ARP packet')
|
||||
|
||||
@test(20, "nettest: ip_rx", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
r.match('ip_rx: received an IP packet')
|
||||
|
||||
@test(20, "nettest: ping0", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
r.match('^ping0: OK$')
|
||||
|
||||
@test(20, "nettest: ping1", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
r.match('^ping1: OK$')
|
||||
|
||||
@test(20, "nettest: ping2", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
r.match('^ping2: OK$')
|
||||
|
||||
@test(30, "nettest: ping3", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
r.match('^ping3: OK$')
|
||||
|
||||
@test(10, "nettest: dns", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
r.match('^dns: OK$')
|
||||
|
||||
@test(10, "nettest: free", parent=test_nettest)
|
||||
def test_nettest_():
|
||||
r.match('^free: OK$')
|
||||
|
||||
#@test(10, "answers-net.txt")
|
||||
#def test_answers():
|
||||
# # just a simple sanity check, will be graded manually
|
||||
# check_answers("answers-net.txt")
|
||||
|
||||
@test(1, "time")
|
||||
def test_time():
|
||||
check_time()
|
||||
|
||||
run_tests()
|
||||
@ -238,7 +238,7 @@ def make(*target):
|
||||
post_make()
|
||||
|
||||
def show_command(cmd):
|
||||
from pipes import quote
|
||||
from shlex import quote
|
||||
print("\n$", " ".join(map(quote, cmd)))
|
||||
|
||||
def maybe_unlink(*paths):
|
||||
|
||||
@ -237,3 +237,4 @@ void netinit(void);
|
||||
void net_rx(char *buf, int len);
|
||||
|
||||
#endif
|
||||
#define DEBUG_INFO printf("File: %s, Line: %d\n", __FILE__, __LINE__)
|
||||
|
||||
153
kernel/e1000.c
Normal file
153
kernel/e1000.c
Normal file
@ -0,0 +1,153 @@
|
||||
#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"
|
||||
|
||||
#define TX_RING_SIZE 16
|
||||
static struct tx_desc tx_ring[TX_RING_SIZE] __attribute__((aligned(16)));
|
||||
static char *tx_bufs[TX_RING_SIZE];
|
||||
|
||||
#define RX_RING_SIZE 16
|
||||
static struct rx_desc rx_ring[RX_RING_SIZE] __attribute__((aligned(16)));
|
||||
static char *rx_bufs[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_bufs[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_bufs[i] = kalloc();
|
||||
if (!rx_bufs[i])
|
||||
panic("e1000");
|
||||
rx_ring[i].addr = (uint64) rx_bufs[i];
|
||||
}
|
||||
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(char *buf, int len)
|
||||
{
|
||||
//
|
||||
// Your code here.
|
||||
//
|
||||
// buf 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 send completes.
|
||||
//
|
||||
acquire(&e1000_lock);
|
||||
struct tx_desc *cur=&tx_ring[regs[E1000_TDT]];
|
||||
if(cur->status!=E1000_TXD_STAT_DD)
|
||||
goto err;
|
||||
if(cur->addr)
|
||||
kfree((void*)cur->addr);
|
||||
cur->length=len;
|
||||
cur->addr=(uint64)buf;
|
||||
cur->cmd=E1000_TXD_CMD_EOP|E1000_TXD_CMD_RS;
|
||||
cur->status=0;
|
||||
regs[E1000_TDT]=(regs[E1000_TDT]+1)%TX_RING_SIZE;
|
||||
release(&e1000_lock);
|
||||
return 0;
|
||||
err:
|
||||
release(&e1000_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
e1000_recv(void)
|
||||
{
|
||||
//
|
||||
// Your code here.
|
||||
//
|
||||
// Check for packets that have arrived from the e1000
|
||||
// Create and deliver a buf for each packet (using net_rx()).
|
||||
//
|
||||
while(1){
|
||||
const uint32 id=(regs[E1000_RDT]+1)%RX_RING_SIZE;
|
||||
struct rx_desc *cur=&rx_ring[id];
|
||||
if((cur->status&E1000_RXD_STAT_DD)==0)
|
||||
break;
|
||||
net_rx((char*)cur->addr,cur->length);
|
||||
cur->status=0;
|
||||
rx_bufs[id]=kalloc();
|
||||
cur->addr=(uint64)rx_bufs[id];
|
||||
regs[E1000_RDT]=id;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
125
kernel/e1000_dev.h
Normal file
125
kernel/e1000_dev.h
Normal file
@ -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 Descriptor 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 0x04000000 /* 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;
|
||||
};
|
||||
|
||||
@ -295,11 +295,11 @@ ilock(struct inode *ip)
|
||||
struct buf *bp;
|
||||
struct dinode *dip;
|
||||
|
||||
if(ip == 0 || atomic_read4(&ip->ref) < 1)
|
||||
if(ip == 0 || ip->ref < 1)
|
||||
panic("ilock");
|
||||
|
||||
acquiresleep(&ip->lock);
|
||||
|
||||
|
||||
if(ip->valid == 0){
|
||||
bp = bread(ip->dev, IBLOCK(ip->inum, sb));
|
||||
dip = (struct dinode*)bp->data + ip->inum%IPB;
|
||||
@ -320,7 +320,7 @@ ilock(struct inode *ip)
|
||||
void
|
||||
iunlock(struct inode *ip)
|
||||
{
|
||||
if(ip == 0 || !holdingsleep(&ip->lock) || atomic_read4(&ip->ref) < 1)
|
||||
if(ip == 0 || !holdingsleep(&ip->lock) || ip->ref < 1)
|
||||
panic("iunlock");
|
||||
|
||||
releasesleep(&ip->lock);
|
||||
@ -416,6 +416,7 @@ bmap(struct inode *ip, uint bn)
|
||||
brelse(bp);
|
||||
return addr;
|
||||
}
|
||||
|
||||
panic("bmap: out of range");
|
||||
}
|
||||
|
||||
@ -446,7 +447,7 @@ itrunc(struct inode *ip)
|
||||
bfree(ip->dev, ip->addrs[NDIRECT]);
|
||||
ip->addrs[NDIRECT] = 0;
|
||||
}
|
||||
|
||||
|
||||
ip->size = 0;
|
||||
iupdate(ip);
|
||||
}
|
||||
|
||||
323
kernel/kcsan.c
323
kernel/kcsan.c
@ -1,323 +0,0 @@
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "memlayout.h"
|
||||
#include "spinlock.h"
|
||||
#include "riscv.h"
|
||||
#include "proc.h"
|
||||
#include "defs.h"
|
||||
|
||||
//
|
||||
// Race detector using gcc's thread sanitizer. It delays all stores
|
||||
// and loads and monitors if any other CPU is using the same address.
|
||||
// If so, we have a race and print out the backtrace of the thread
|
||||
// that raced and the thread that set the watchpoint.
|
||||
//
|
||||
|
||||
//
|
||||
// To run with kcsan:
|
||||
// make clean
|
||||
// make KCSAN=1 qemu
|
||||
//
|
||||
|
||||
// The number of watch points.
|
||||
#define NWATCH (NCPU)
|
||||
|
||||
// The number of cycles to delay stores, whatever that means on qemu.
|
||||
//#define DELAY_CYCLES 20000
|
||||
#define DELAY_CYCLES 200000
|
||||
|
||||
#define MAXTRACE 20
|
||||
|
||||
int
|
||||
trace(uint64 *trace, int maxtrace)
|
||||
{
|
||||
uint64 i = 0;
|
||||
|
||||
push_off();
|
||||
|
||||
uint64 fp = r_fp();
|
||||
uint64 ra, low = PGROUNDDOWN(fp) + 16, high = PGROUNDUP(fp);
|
||||
|
||||
while(!(fp & 7) && fp >= low && fp < high){
|
||||
ra = *(uint64*)(fp - 8);
|
||||
fp = *(uint64*)(fp - 16);
|
||||
trace[i++] = ra;
|
||||
if(i >= maxtrace)
|
||||
break;
|
||||
}
|
||||
|
||||
pop_off();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
struct watch {
|
||||
uint64 addr;
|
||||
int write;
|
||||
int race;
|
||||
uint64 trace[MAXTRACE];
|
||||
int tracesz;
|
||||
};
|
||||
|
||||
struct {
|
||||
struct spinlock lock;
|
||||
struct watch points[NWATCH];
|
||||
int on;
|
||||
} tsan;
|
||||
|
||||
static struct watch*
|
||||
wp_lookup(uint64 addr)
|
||||
{
|
||||
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
|
||||
if(w->addr == addr) {
|
||||
return w;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
wp_install(uint64 addr, int write)
|
||||
{
|
||||
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
|
||||
if(w->addr == 0) {
|
||||
w->addr = addr;
|
||||
w->write = write;
|
||||
w->tracesz = trace(w->trace, MAXTRACE);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
panic("wp_install");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_remove(uint64 addr)
|
||||
{
|
||||
for(struct watch *w = &tsan.points[0]; w < &tsan.points[NWATCH]; w++) {
|
||||
if(w->addr == addr) {
|
||||
w->addr = 0;
|
||||
w->tracesz = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic("remove");
|
||||
}
|
||||
|
||||
static void
|
||||
printtrace(uint64 *t, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < n; i++) {
|
||||
printf("%p\n", (void*) t[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
race(char *s, struct watch *w) {
|
||||
uint64 t[MAXTRACE];
|
||||
int n;
|
||||
|
||||
n = trace(t, MAXTRACE);
|
||||
printf("== race detected ==\n");
|
||||
printf("backtrace for racing %s\n", s);
|
||||
printtrace(t, n);
|
||||
printf("backtrace for watchpoint:\n");
|
||||
printtrace(w->trace, w->tracesz);
|
||||
printf("==========\n");
|
||||
}
|
||||
|
||||
// cycle counter
|
||||
static inline uint64
|
||||
r_cycle()
|
||||
{
|
||||
uint64 x;
|
||||
asm volatile("rdcycle %0" : "=r" (x) );
|
||||
return x;
|
||||
}
|
||||
|
||||
static void delay(void) __attribute__((noinline));
|
||||
static void delay() {
|
||||
uint64 stop = r_cycle() + DELAY_CYCLES;
|
||||
uint64 c = r_cycle();
|
||||
while(c < stop) {
|
||||
c = r_cycle();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
kcsan_read(uint64 addr, int sz)
|
||||
{
|
||||
struct watch *w;
|
||||
|
||||
acquire(&tsan.lock);
|
||||
if((w = wp_lookup(addr)) != 0) {
|
||||
if(w->write) {
|
||||
race("load", w);
|
||||
}
|
||||
release(&tsan.lock);
|
||||
return;
|
||||
}
|
||||
release(&tsan.lock);
|
||||
}
|
||||
|
||||
static void
|
||||
kcsan_write(uint64 addr, int sz)
|
||||
{
|
||||
struct watch *w;
|
||||
|
||||
acquire(&tsan.lock);
|
||||
if((w = wp_lookup(addr)) != 0) {
|
||||
race("store", w);
|
||||
release(&tsan.lock);
|
||||
}
|
||||
|
||||
// no watchpoint; try to install one
|
||||
if(wp_install(addr, 1)) {
|
||||
|
||||
release(&tsan.lock);
|
||||
|
||||
// XXX maybe read value at addr before and after delay to catch
|
||||
// races of unknown origins (e.g., device).
|
||||
|
||||
delay();
|
||||
|
||||
acquire(&tsan.lock);
|
||||
|
||||
wp_remove(addr);
|
||||
}
|
||||
release(&tsan.lock);
|
||||
}
|
||||
|
||||
// tsan.on will only have effect with "make KCSAN=1"
|
||||
void
|
||||
kcsaninit(void)
|
||||
{
|
||||
initlock(&tsan.lock, "tsan");
|
||||
tsan.on = 1;
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
//
|
||||
// Calls inserted by compiler into kernel binary, except for this file.
|
||||
//
|
||||
|
||||
void
|
||||
__tsan_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_read1(uint64 addr)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
// kcsan_read(addr, 1);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_read2(uint64 addr)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
kcsan_read(addr, 2);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_read4(uint64 addr)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
kcsan_read(addr, 4);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_read8(uint64 addr)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
kcsan_read(addr, 8);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_read_range(uint64 addr, uint64 size)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
kcsan_read(addr, size);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_write1(uint64 addr)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
// kcsan_write(addr, 1);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_write2(uint64 addr)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
kcsan_write(addr, 2);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_write4(uint64 addr)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
kcsan_write(addr, 4);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_write8(uint64 addr)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
kcsan_write(addr, 8);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_write_range(uint64 addr, uint64 size)
|
||||
{
|
||||
if(!tsan.on)
|
||||
return;
|
||||
kcsan_write(addr, size);
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_atomic_thread_fence(int order)
|
||||
{
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
uint32
|
||||
__tsan_atomic32_load(uint *ptr, uint *val, int order)
|
||||
{
|
||||
uint t;
|
||||
__atomic_load(ptr, &t, __ATOMIC_SEQ_CST);
|
||||
return t;
|
||||
}
|
||||
|
||||
void
|
||||
__tsan_atomic32_store(uint *ptr, uint val, int order)
|
||||
{
|
||||
__atomic_store(ptr, &val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
// We don't use this
|
||||
void
|
||||
__tsan_func_entry(uint64 pc)
|
||||
{
|
||||
}
|
||||
|
||||
// We don't use this
|
||||
void
|
||||
__tsan_func_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@ -25,6 +25,10 @@
|
||||
#define VIRTIO0 0x10001000
|
||||
#define VIRTIO0_IRQ 1
|
||||
|
||||
#ifdef LAB_NET
|
||||
#define E1000_IRQ 33
|
||||
#endif
|
||||
|
||||
// qemu puts platform-level interrupt controller (PLIC) here.
|
||||
#define PLIC 0x0c000000L
|
||||
#define PLIC_PRIORITY (PLIC + 0x0)
|
||||
@ -45,7 +49,7 @@
|
||||
|
||||
// map kernel stacks beneath the trampoline,
|
||||
// each surrounded by invalid guard pages.
|
||||
#define KSTACK(p) (TRAMPOLINE - ((p)+1)* 2*PGSIZE)
|
||||
#define KSTACK(p) (TRAMPOLINE - (p)*2*PGSIZE - 3*PGSIZE)
|
||||
|
||||
// User memory layout.
|
||||
// Address zero first:
|
||||
@ -54,6 +58,14 @@
|
||||
// fixed-size stack
|
||||
// expandable heap
|
||||
// ...
|
||||
// USYSCALL (shared with kernel)
|
||||
// TRAPFRAME (p->trapframe, used by the trampoline)
|
||||
// TRAMPOLINE (the same page as in the kernel)
|
||||
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
|
||||
#ifdef LAB_PGTBL
|
||||
#define USYSCALL (TRAPFRAME - PGSIZE)
|
||||
|
||||
struct usyscall {
|
||||
int pid; // Process ID
|
||||
};
|
||||
#endif
|
||||
|
||||
324
kernel/net.c
Normal file
324
kernel/net.c
Normal file
@ -0,0 +1,324 @@
|
||||
#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"
|
||||
|
||||
// xv6's ethernet and IP addresses
|
||||
static uint8 local_mac[ETHADDR_LEN] = { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 };
|
||||
static uint32 local_ip = MAKE_IP_ADDR(10, 0, 2, 15);
|
||||
|
||||
// qemu host's ethernet address.
|
||||
static uint8 host_mac[ETHADDR_LEN] = { 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02 };
|
||||
|
||||
static struct spinlock netlock;
|
||||
|
||||
void
|
||||
netinit(void)
|
||||
{
|
||||
initlock(&netlock, "netlock");
|
||||
}
|
||||
#define MAXPORT 10000+1
|
||||
#define MAXCNT 16
|
||||
static void *udpq[MAXPORT][MAXCNT];
|
||||
static char udph[MAXPORT],udpt[MAXPORT],udpc[MAXPORT];
|
||||
static int pused[MAXPORT];
|
||||
|
||||
//
|
||||
// bind(int port)
|
||||
// prepare to receive UDP packets address to the port,
|
||||
// i.e. allocate any queues &c needed.
|
||||
//
|
||||
uint64
|
||||
sys_bind(void)
|
||||
{
|
||||
//
|
||||
// Your code here.
|
||||
//
|
||||
short port;
|
||||
argint(0, (int *)&port);
|
||||
pused[port] = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// unbind(int port)
|
||||
// release any resources previously created by bind(port);
|
||||
// from now on UDP packets addressed to port should be dropped.
|
||||
//
|
||||
uint64
|
||||
sys_unbind(void)
|
||||
{
|
||||
//
|
||||
// Optional: Your code here.
|
||||
//
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// recv(int dport, int *src, short *sport, char *buf, int maxlen)
|
||||
// if there's a received UDP packet already queued that was
|
||||
// addressed to dport, then return it.
|
||||
// otherwise wait for such a packet.
|
||||
//
|
||||
// sets *src to the IP source address.
|
||||
// sets *sport to the UDP source port.
|
||||
// copies up to maxlen bytes of UDP payload to buf.
|
||||
// returns the number of bytes copied,
|
||||
// and -1 if there was an error.
|
||||
//
|
||||
// dport, *src, and *sport are host byte order.
|
||||
// bind(dport) must previously have been called.
|
||||
//
|
||||
uint64
|
||||
sys_recv(void)
|
||||
{
|
||||
//
|
||||
// Your code here.
|
||||
//
|
||||
acquire(&netlock);
|
||||
struct proc *p = myproc();
|
||||
int dport;
|
||||
uint64 src,sport,buf;
|
||||
int maxlen;
|
||||
argint(0, (int*)&dport);
|
||||
argaddr(1, &src);
|
||||
argaddr(2, &sport);
|
||||
argaddr(3, &buf);
|
||||
argint(4, &maxlen);
|
||||
while(!udpc[dport]){
|
||||
if(p->killed)
|
||||
goto err;
|
||||
sleep(&udpc[dport], &netlock);
|
||||
if(p->killed)
|
||||
goto err;
|
||||
}
|
||||
struct eth *eth=(struct eth *)udpq[dport][(int)udph[dport]];
|
||||
struct ip *ip=(struct ip *)(eth+1);
|
||||
struct udp *udp=(struct udp *)(ip+1);
|
||||
const int srcbuf=ntohl(ip->ip_src);
|
||||
const short sportbuf=ntohs(udp->sport);
|
||||
if(copyout(p->pagetable,src,(char *)&srcbuf,sizeof(int))<0){
|
||||
goto err;
|
||||
}
|
||||
if(copyout(p->pagetable,sport,(char *)&sportbuf,sizeof(short))<0){
|
||||
goto err;
|
||||
}
|
||||
char *payload=(char *)(udp+1);
|
||||
const int len=ntohs(udp->ulen)-sizeof(struct udp);
|
||||
|
||||
if(len>maxlen){
|
||||
goto err;
|
||||
}
|
||||
if(copyout(p->pagetable,buf,payload,len)<0){
|
||||
goto err;
|
||||
}
|
||||
kfree(eth);
|
||||
--udpc[dport];
|
||||
udph[dport]=(udph[dport]+1)%MAXCNT;
|
||||
release(&netlock);
|
||||
return len;
|
||||
err:
|
||||
release(&netlock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
//
|
||||
// send(int sport, int dst, int dport, char *buf, int len)
|
||||
//
|
||||
|
||||
uint64
|
||||
sys_send(void)
|
||||
{
|
||||
struct proc *p = myproc();
|
||||
int sport;
|
||||
int dst;
|
||||
int dport;
|
||||
uint64 bufaddr;
|
||||
int len;
|
||||
|
||||
argint(0, &sport);
|
||||
argint(1, &dst);
|
||||
argint(2, &dport);
|
||||
argaddr(3, &bufaddr);
|
||||
argint(4, &len);
|
||||
int total = len + sizeof(struct eth) + sizeof(struct ip) + sizeof(struct udp);
|
||||
if(total > PGSIZE)
|
||||
return -1;
|
||||
char *buf = kalloc();
|
||||
if(buf == 0){
|
||||
printf("sys_send: kalloc failed\n");
|
||||
return -1;
|
||||
}
|
||||
memset(buf, 0, PGSIZE);
|
||||
|
||||
struct eth *eth = (struct eth *) buf;
|
||||
memmove(eth->dhost, host_mac, ETHADDR_LEN);
|
||||
memmove(eth->shost, local_mac, ETHADDR_LEN);
|
||||
eth->type = htons(ETHTYPE_IP);
|
||||
|
||||
struct ip *ip = (struct ip *)(eth + 1);
|
||||
ip->ip_vhl = 0x45; // version 4, header length 4*5
|
||||
ip->ip_tos = 0;
|
||||
ip->ip_len = htons(sizeof(struct ip) + sizeof(struct udp) + len);
|
||||
ip->ip_id = 0;
|
||||
ip->ip_off = 0;
|
||||
ip->ip_ttl = 100;
|
||||
ip->ip_p = IPPROTO_UDP;
|
||||
ip->ip_src = htonl(local_ip);
|
||||
ip->ip_dst = htonl(dst);
|
||||
ip->ip_sum = in_cksum((unsigned char *)ip, sizeof(*ip));
|
||||
|
||||
struct udp *udp = (struct udp *)(ip + 1);
|
||||
udp->sport = htons(sport);
|
||||
udp->dport = htons(dport);
|
||||
udp->ulen = htons(len + sizeof(struct udp));
|
||||
|
||||
char *payload = (char *)(udp + 1);
|
||||
if(copyin(p->pagetable, payload, bufaddr, len) < 0){
|
||||
kfree(buf);
|
||||
printf("send: copyin failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
e1000_transmit(buf, total);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
ip_rx(char *buf, int len)
|
||||
{
|
||||
// don't delete this printf; make grade depends on it.
|
||||
static int seen_ip = 0;
|
||||
if(seen_ip == 0)
|
||||
printf("ip_rx: received an IP packet\n");
|
||||
seen_ip = 1;
|
||||
//
|
||||
// Your code here.
|
||||
//
|
||||
if(len<sizeof(struct eth)+sizeof(struct ip)+sizeof(struct udp))
|
||||
return;
|
||||
struct eth *eth=(struct eth *)buf;
|
||||
struct ip *ip=(struct ip *)(eth+1);
|
||||
struct udp *udp=(struct udp *)(ip+1);
|
||||
const short dport=ntohs(udp->dport);
|
||||
if(!pused[dport])
|
||||
goto abandon;
|
||||
if(udpc[dport]==MAXCNT)
|
||||
goto abandon;
|
||||
udpq[dport][(int)udpt[dport]]=buf;//tail
|
||||
udpt[dport]=(udpt[dport]+1)%MAXCNT;
|
||||
++udpc[dport];
|
||||
wakeup(&udpc[dport]);
|
||||
return;
|
||||
abandon:
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
//
|
||||
// send an ARP reply packet to tell qemu to map
|
||||
// xv6's ip address to its ethernet address.
|
||||
// this is the bare minimum needed to persuade
|
||||
// qemu to send IP packets to xv6; the real ARP
|
||||
// protocol is more complex.
|
||||
//
|
||||
void
|
||||
arp_rx(char *inbuf)
|
||||
{
|
||||
static int seen_arp = 0;
|
||||
|
||||
if(seen_arp){
|
||||
kfree(inbuf);
|
||||
return;
|
||||
}
|
||||
printf("arp_rx: received an ARP packet\n");
|
||||
seen_arp = 1;
|
||||
|
||||
struct eth *ineth = (struct eth *) inbuf;
|
||||
struct arp *inarp = (struct arp *) (ineth + 1);
|
||||
|
||||
char *buf = kalloc();
|
||||
if(buf == 0)
|
||||
panic("send_arp_reply");
|
||||
|
||||
struct eth *eth = (struct eth *) buf;
|
||||
memmove(eth->dhost, ineth->shost, ETHADDR_LEN); // ethernet destination = query source
|
||||
memmove(eth->shost, local_mac, ETHADDR_LEN); // ethernet source = xv6's ethernet address
|
||||
eth->type = htons(ETHTYPE_ARP);
|
||||
|
||||
struct arp *arp = (struct arp *)(eth + 1);
|
||||
arp->hrd = htons(ARP_HRD_ETHER);
|
||||
arp->pro = htons(ETHTYPE_IP);
|
||||
arp->hln = ETHADDR_LEN;
|
||||
arp->pln = sizeof(uint32);
|
||||
arp->op = htons(ARP_OP_REPLY);
|
||||
|
||||
memmove(arp->sha, local_mac, ETHADDR_LEN);
|
||||
arp->sip = htonl(local_ip);
|
||||
memmove(arp->tha, ineth->shost, ETHADDR_LEN);
|
||||
arp->tip = inarp->sip;
|
||||
|
||||
e1000_transmit(buf, sizeof(*eth) + sizeof(*arp));
|
||||
|
||||
kfree(inbuf);
|
||||
}
|
||||
|
||||
void
|
||||
net_rx(char *buf, int len)
|
||||
{
|
||||
struct eth *eth = (struct eth *) buf;
|
||||
|
||||
if(len >= sizeof(struct eth) + sizeof(struct arp) &&
|
||||
ntohs(eth->type) == ETHTYPE_ARP){
|
||||
arp_rx(buf);
|
||||
} else if(len >= sizeof(struct eth) + sizeof(struct ip) &&
|
||||
ntohs(eth->type) == ETHTYPE_IP){
|
||||
ip_rx(buf, len);
|
||||
} else {
|
||||
kfree(buf);
|
||||
}
|
||||
}
|
||||
127
kernel/net.h
Normal file
127
kernel/net.h
Normal file
@ -0,0 +1,127 @@
|
||||
//
|
||||
// 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, including this IP header
|
||||
uint16 ip_id; // identification
|
||||
uint16 ip_off; // fragment offset field
|
||||
uint8 ip_ttl; // time to live
|
||||
uint8 ip_p; // protocol
|
||||
uint16 ip_sum; // checksum, covers just IP header
|
||||
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 with the hw addr of the 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));
|
||||
61
kernel/pci.c
Normal file
61
kernel/pci.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,9 +68,6 @@ pipeclose(struct pipe *pi, int writable)
|
||||
}
|
||||
if(pi->readopen == 0 && pi->writeopen == 0){
|
||||
release(&pi->lock);
|
||||
#ifdef LAB_LOCK
|
||||
freelock(&pi->lock);
|
||||
#endif
|
||||
kfree((char*)pi);
|
||||
} else
|
||||
release(&pi->lock);
|
||||
|
||||
@ -14,6 +14,13 @@ plicinit(void)
|
||||
// set desired IRQ priorities non-zero (otherwise disabled).
|
||||
*(uint32*)(PLIC + UART0_IRQ*4) = 1;
|
||||
*(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1;
|
||||
|
||||
#ifdef LAB_NET
|
||||
// PCIE IRQs are 32 to 35
|
||||
for(int irq = 1; irq < 0x35; irq++){
|
||||
*(uint32*)(PLIC + irq*4) = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -25,6 +32,11 @@ plicinithart(void)
|
||||
// for the uart and virtio disk.
|
||||
*(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ);
|
||||
|
||||
#ifdef LAB_NET
|
||||
// hack to get at next 32 IRQs for e1000
|
||||
*(uint32*)(PLIC_SENABLE(hart)+4) = 0xffffffff;
|
||||
#endif
|
||||
|
||||
// set this hart's S-mode priority threshold to 0.
|
||||
*(uint32*)PLIC_SPRIORITY(hart) = 0;
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ r_menvcfg()
|
||||
static inline void
|
||||
w_menvcfg(uint64 x)
|
||||
{
|
||||
//asm volatile("csrw menvcfg, %0" : : "r" (x));
|
||||
// asm volatile("csrw menvcfg, %0" : : "r" (x));
|
||||
asm volatile("csrw 0x30a, %0" : : "r" (x));
|
||||
}
|
||||
|
||||
@ -314,14 +314,6 @@ r_sp()
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline uint64
|
||||
r_fp()
|
||||
{
|
||||
uint64 x;
|
||||
asm volatile("mv %0, s0" : "=r" (x) );
|
||||
return x;
|
||||
}
|
||||
|
||||
// read and write tp, the thread pointer, which xv6 uses to hold
|
||||
// this core's hartid (core number), the index into cpus[].
|
||||
static inline uint64
|
||||
@ -362,11 +354,6 @@ typedef uint64 *pagetable_t; // 512 PTEs
|
||||
#define PGSIZE 4096 // bytes per page
|
||||
#define PGSHIFT 12 // bits of offset within a page
|
||||
|
||||
#ifdef LAB_PGTBL
|
||||
#define SUPERPGSIZE (2 * (1 << 20)) // bytes per page
|
||||
#define SUPERPGROUNDUP(sz) (((sz)+SUPERPGSIZE-1) & ~(SUPERPGSIZE-1))
|
||||
#endif
|
||||
|
||||
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
|
||||
#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1))
|
||||
|
||||
@ -376,12 +363,6 @@ typedef uint64 *pagetable_t; // 512 PTEs
|
||||
#define PTE_X (1L << 3)
|
||||
#define PTE_U (1L << 4) // user can access
|
||||
|
||||
|
||||
|
||||
#if defined(LAB_MMAP) || defined(LAB_PGTBL)
|
||||
#define PTE_LEAF(pte) (((pte) & PTE_R) | ((pte) & PTE_W) | ((pte) & PTE_X))
|
||||
#endif
|
||||
|
||||
// shift a physical address to the right place for a PTE.
|
||||
#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
|
||||
|
||||
|
||||
@ -5,9 +5,5 @@ struct spinlock {
|
||||
// For debugging:
|
||||
char *name; // Name of lock.
|
||||
struct cpu *cpu; // The cpu holding the lock.
|
||||
#ifdef LAB_LOCK
|
||||
int nts;
|
||||
int n;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@ -1,88 +0,0 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
|
||||
static char digits[] = "0123456789abcdef";
|
||||
|
||||
static int
|
||||
sputc(char *s, char c)
|
||||
{
|
||||
*s = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
sprintint(char *s, int xx, int base, int sign)
|
||||
{
|
||||
char buf[16];
|
||||
int i, n;
|
||||
uint x;
|
||||
|
||||
if(sign && (sign = xx < 0))
|
||||
x = -xx;
|
||||
else
|
||||
x = xx;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
buf[i++] = digits[x % base];
|
||||
} while((x /= base) != 0);
|
||||
|
||||
if(sign)
|
||||
buf[i++] = '-';
|
||||
|
||||
n = 0;
|
||||
while(--i >= 0)
|
||||
n += sputc(s+n, buf[i]);
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
snprintf(char *buf, unsigned long sz, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, c;
|
||||
int off = 0;
|
||||
char *s;
|
||||
|
||||
va_start(ap, fmt);
|
||||
for(i = 0; off < sz && (c = fmt[i] & 0xff) != 0; i++){
|
||||
if(c != '%'){
|
||||
off += sputc(buf+off, c);
|
||||
continue;
|
||||
}
|
||||
c = fmt[++i] & 0xff;
|
||||
if(c == 0)
|
||||
break;
|
||||
switch(c){
|
||||
case 'd':
|
||||
off += sprintint(buf+off, va_arg(ap, int), 10, 1);
|
||||
break;
|
||||
case 'x':
|
||||
off += sprintint(buf+off, va_arg(ap, int), 16, 1);
|
||||
break;
|
||||
case 's':
|
||||
if((s = va_arg(ap, char*)) == 0)
|
||||
s = "(null)";
|
||||
for(; *s && off < sz; s++)
|
||||
off += sputc(buf+off, *s);
|
||||
break;
|
||||
case '%':
|
||||
off += sputc(buf+off, '%');
|
||||
break;
|
||||
default:
|
||||
// Print unknown % sequence to draw attention.
|
||||
off += sputc(buf+off, '%');
|
||||
off += sputc(buf+off, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return off;
|
||||
}
|
||||
@ -32,11 +32,6 @@ start()
|
||||
w_mideleg(0xffff);
|
||||
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
|
||||
|
||||
#ifdef KCSAN
|
||||
// allow supervisor to read cycle counter register
|
||||
w_mcounteren(r_mcounteren()|0x3);
|
||||
#endif
|
||||
|
||||
// configure Physical Memory Protection to give supervisor mode
|
||||
// access to all of physical memory.
|
||||
w_pmpaddr0(0x3fffffffffffffull);
|
||||
|
||||
@ -1,69 +0,0 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "param.h"
|
||||
#include "spinlock.h"
|
||||
#include "sleeplock.h"
|
||||
#include "fs.h"
|
||||
#include "file.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
|
||||
#define BUFSZ 4096
|
||||
static struct {
|
||||
struct spinlock lock;
|
||||
char buf[BUFSZ];
|
||||
int sz;
|
||||
int off;
|
||||
} stats;
|
||||
|
||||
int statscopyin(char*, int);
|
||||
int statslock(char*, int);
|
||||
|
||||
int
|
||||
statswrite(int user_src, uint64 src, int n)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
statsread(int user_dst, uint64 dst, int n)
|
||||
{
|
||||
int m;
|
||||
|
||||
acquire(&stats.lock);
|
||||
|
||||
if(stats.sz == 0) {
|
||||
#ifdef LAB_PGTBL
|
||||
stats.sz = statscopyin(stats.buf, BUFSZ);
|
||||
#endif
|
||||
#ifdef LAB_LOCK
|
||||
stats.sz = statslock(stats.buf, BUFSZ);
|
||||
#endif
|
||||
}
|
||||
m = stats.sz - stats.off;
|
||||
|
||||
if (m > 0) {
|
||||
if(m > n)
|
||||
m = n;
|
||||
if(either_copyout(user_dst, dst, stats.buf+stats.off, m) != -1) {
|
||||
stats.off += m;
|
||||
}
|
||||
} else {
|
||||
m = -1;
|
||||
stats.sz = 0;
|
||||
stats.off = 0;
|
||||
}
|
||||
release(&stats.lock);
|
||||
return m;
|
||||
}
|
||||
|
||||
void
|
||||
statsinit(void)
|
||||
{
|
||||
initlock(&stats.lock, "stats");
|
||||
|
||||
devsw[STATS].read = statsread;
|
||||
devsw[STATS].write = statswrite;
|
||||
}
|
||||
|
||||
@ -102,6 +102,17 @@ extern uint64 sys_link(void);
|
||||
extern uint64 sys_mkdir(void);
|
||||
extern uint64 sys_close(void);
|
||||
|
||||
#ifdef LAB_NET
|
||||
extern uint64 sys_bind(void);
|
||||
extern uint64 sys_unbind(void);
|
||||
extern uint64 sys_send(void);
|
||||
extern uint64 sys_recv(void);
|
||||
#endif
|
||||
#ifdef LAB_PGTBL
|
||||
extern uint64 sys_pgpte(void);
|
||||
extern uint64 sys_kpgtbl(void);
|
||||
#endif
|
||||
|
||||
// An array mapping syscall numbers from syscall.h
|
||||
// to the function that handles the system call.
|
||||
static uint64 (*syscalls[])(void) = {
|
||||
@ -126,8 +137,20 @@ static uint64 (*syscalls[])(void) = {
|
||||
[SYS_link] sys_link,
|
||||
[SYS_mkdir] sys_mkdir,
|
||||
[SYS_close] sys_close,
|
||||
#ifdef LAB_NET
|
||||
[SYS_bind] sys_bind,
|
||||
[SYS_unbind] sys_unbind,
|
||||
[SYS_send] sys_send,
|
||||
[SYS_recv] sys_recv,
|
||||
#endif
|
||||
#ifdef LAB_PGTBL
|
||||
[SYS_pgpte] sys_pgpte,
|
||||
[SYS_kpgtbl] sys_kpgtbl,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
void
|
||||
syscall(void)
|
||||
{
|
||||
|
||||
@ -20,3 +20,18 @@
|
||||
#define SYS_link 19
|
||||
#define SYS_mkdir 20
|
||||
#define SYS_close 21
|
||||
|
||||
// System calls for labs
|
||||
#define SYS_trace 22
|
||||
#define SYS_sysinfo 23
|
||||
#define SYS_sigalarm 24
|
||||
#define SYS_sigreturn 25
|
||||
#define SYS_symlink 26
|
||||
#define SYS_mmap 27
|
||||
#define SYS_munmap 28
|
||||
#define SYS_bind 29
|
||||
#define SYS_unbind 30
|
||||
#define SYS_send 31
|
||||
#define SYS_recv 32
|
||||
#define SYS_pgpte 33
|
||||
#define SYS_kpgtbl 34
|
||||
|
||||
@ -68,6 +68,8 @@ usertrap(void)
|
||||
} else if((which_dev = devintr()) != 0){
|
||||
// ok
|
||||
} else {
|
||||
|
||||
|
||||
printf("usertrap(): unexpected scause 0x%lx pid=%d\n", r_scause(), p->pid);
|
||||
printf(" sepc=0x%lx stval=0x%lx\n", r_sepc(), r_stval());
|
||||
setkilled(p);
|
||||
@ -75,6 +77,7 @@ usertrap(void)
|
||||
|
||||
if(killed(p))
|
||||
exit(-1);
|
||||
|
||||
|
||||
// give up the CPU if this is a timer interrupt.
|
||||
if(which_dev == 2)
|
||||
@ -147,6 +150,7 @@ kerneltrap()
|
||||
if((which_dev = devintr()) == 0){
|
||||
// interrupt or trap from an unknown source
|
||||
printf("scause=0x%lx sepc=0x%lx stval=0x%lx\n", scause, r_sepc(), r_stval());
|
||||
printf("pid=%d\n",myproc()->pid);
|
||||
panic("kerneltrap");
|
||||
}
|
||||
|
||||
@ -196,7 +200,13 @@ devintr()
|
||||
uartintr();
|
||||
} else if(irq == VIRTIO0_IRQ){
|
||||
virtio_disk_intr();
|
||||
} else if(irq){
|
||||
}
|
||||
#ifdef LAB_NET
|
||||
else if(irq == E1000_IRQ){
|
||||
e1000_intr();
|
||||
}
|
||||
#endif
|
||||
else if(irq){
|
||||
printf("unexpected interrupt irq=%d\n", irq);
|
||||
}
|
||||
|
||||
|
||||
@ -212,28 +212,6 @@ alloc3_desc(int *idx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LAB_LOCK
|
||||
//
|
||||
// check that there are at most NBUF distinct
|
||||
// struct buf's, which the lock lab requires.
|
||||
//
|
||||
static struct buf *xbufs[NBUF];
|
||||
static void
|
||||
checkbuf(struct buf *b)
|
||||
{
|
||||
for(int i = 0; i < NBUF; i++){
|
||||
if(xbufs[i] == b){
|
||||
return;
|
||||
}
|
||||
if(xbufs[i] == 0){
|
||||
xbufs[i] = b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic("more than NBUF bufs");
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
virtio_disk_rw(struct buf *b, int write)
|
||||
{
|
||||
@ -241,10 +219,6 @@ virtio_disk_rw(struct buf *b, int write)
|
||||
|
||||
acquire(&disk.vdisk_lock);
|
||||
|
||||
#ifdef LAB_LOCK
|
||||
checkbuf(b);
|
||||
#endif
|
||||
|
||||
// the spec's Section 5.2 says that legacy block operations use
|
||||
// three descriptors: one for type/reserved/sector, one for the
|
||||
// data, one for a 1-byte status result.
|
||||
|
||||
77
kernel/vm.c
77
kernel/vm.c
@ -4,6 +4,8 @@
|
||||
#include "elf.h"
|
||||
#include "riscv.h"
|
||||
#include "defs.h"
|
||||
#include "spinlock.h"
|
||||
#include "proc.h"
|
||||
#include "fs.h"
|
||||
|
||||
/*
|
||||
@ -30,6 +32,14 @@ kvmmake(void)
|
||||
// virtio mmio disk interface
|
||||
kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
|
||||
|
||||
#ifdef LAB_NET
|
||||
// PCI-E ECAM (configuration space), for pci.c
|
||||
kvmmap(kpgtbl, 0x30000000L, 0x30000000L, 0x10000000, PTE_R | PTE_W);
|
||||
|
||||
// pci.c maps the e1000's registers here.
|
||||
kvmmap(kpgtbl, 0x40000000L, 0x40000000L, 0x20000, PTE_R | PTE_W);
|
||||
#endif
|
||||
|
||||
// PLIC
|
||||
kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W);
|
||||
|
||||
@ -92,6 +102,11 @@ walk(pagetable_t pagetable, uint64 va, int alloc)
|
||||
pte_t *pte = &pagetable[PX(level, va)];
|
||||
if(*pte & PTE_V) {
|
||||
pagetable = (pagetable_t)PTE2PA(*pte);
|
||||
#ifdef LAB_PGTBL
|
||||
if(PTE_LEAF(*pte)) {
|
||||
return pte;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
|
||||
return 0;
|
||||
@ -125,6 +140,7 @@ walkaddr(pagetable_t pagetable, uint64 va)
|
||||
return pa;
|
||||
}
|
||||
|
||||
|
||||
// add a mapping to the kernel page table.
|
||||
// only used when booting.
|
||||
// does not flush TLB or enable paging.
|
||||
@ -179,15 +195,19 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
|
||||
{
|
||||
uint64 a;
|
||||
pte_t *pte;
|
||||
int sz;
|
||||
|
||||
if((va % PGSIZE) != 0)
|
||||
panic("uvmunmap: not aligned");
|
||||
|
||||
for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
|
||||
for(a = va; a < va + npages*PGSIZE; a += sz){
|
||||
sz = PGSIZE;
|
||||
if((pte = walk(pagetable, a, 0)) == 0)
|
||||
panic("uvmunmap: walk");
|
||||
if((*pte & PTE_V) == 0)
|
||||
if((*pte & PTE_V) == 0) {
|
||||
printf("va=%ld pte=%ld\n", a, *pte);
|
||||
panic("uvmunmap: not mapped");
|
||||
}
|
||||
if(PTE_FLAGS(*pte) == PTE_V)
|
||||
panic("uvmunmap: not a leaf");
|
||||
if(do_free){
|
||||
@ -227,6 +247,7 @@ uvmfirst(pagetable_t pagetable, uchar *src, uint sz)
|
||||
memmove(mem, src, sz);
|
||||
}
|
||||
|
||||
|
||||
// Allocate PTEs and physical memory to grow process from oldsz to
|
||||
// newsz, which need not be page aligned. Returns new size or 0 on error.
|
||||
uint64
|
||||
@ -234,19 +255,23 @@ uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)
|
||||
{
|
||||
char *mem;
|
||||
uint64 a;
|
||||
int sz;
|
||||
|
||||
if(newsz < oldsz)
|
||||
return oldsz;
|
||||
|
||||
oldsz = PGROUNDUP(oldsz);
|
||||
for(a = oldsz; a < newsz; a += PGSIZE){
|
||||
for(a = oldsz; a < newsz; a += sz){
|
||||
sz = PGSIZE;
|
||||
mem = kalloc();
|
||||
if(mem == 0){
|
||||
uvmdealloc(pagetable, a, oldsz);
|
||||
return 0;
|
||||
}
|
||||
memset(mem, 0, PGSIZE);
|
||||
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
|
||||
#ifndef LAB_SYSCALL
|
||||
memset(mem, 0, sz);
|
||||
#endif
|
||||
if(mappages(pagetable, a, sz, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
|
||||
kfree(mem);
|
||||
uvmdealloc(pagetable, a, oldsz);
|
||||
return 0;
|
||||
@ -316,8 +341,11 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
|
||||
uint64 pa, i;
|
||||
uint flags;
|
||||
char *mem;
|
||||
int szinc;
|
||||
|
||||
for(i = 0; i < sz; i += PGSIZE){
|
||||
for(i = 0; i < sz; i += szinc){
|
||||
szinc = PGSIZE;
|
||||
szinc = PGSIZE;
|
||||
if((pte = walk(old, i, 0)) == 0)
|
||||
panic("uvmcopy: pte should exist");
|
||||
if((*pte & PTE_V) == 0)
|
||||
@ -363,13 +391,21 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
|
||||
|
||||
while(len > 0){
|
||||
va0 = PGROUNDDOWN(dstva);
|
||||
if(va0 >= MAXVA)
|
||||
if (va0 >= MAXVA)
|
||||
return -1;
|
||||
pte = walk(pagetable, va0, 0);
|
||||
if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 ||
|
||||
(*pte & PTE_W) == 0)
|
||||
if((pte = walk(pagetable, va0, 0)) == 0) {
|
||||
// printf("copyout: pte should exist 0x%x %d\n", dstva, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// forbid copyout over read-only user text pages.
|
||||
if((*pte & PTE_W) == 0)
|
||||
return -1;
|
||||
|
||||
pa0 = walkaddr(pagetable, va0);
|
||||
if(pa0 == 0)
|
||||
return -1;
|
||||
pa0 = PTE2PA(*pte);
|
||||
n = PGSIZE - (dstva - va0);
|
||||
if(n > len)
|
||||
n = len;
|
||||
@ -389,7 +425,7 @@ int
|
||||
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
|
||||
{
|
||||
uint64 n, va0, pa0;
|
||||
|
||||
|
||||
while(len > 0){
|
||||
va0 = PGROUNDDOWN(srcva);
|
||||
pa0 = walkaddr(pagetable, va0);
|
||||
@ -449,3 +485,20 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef LAB_PGTBL
|
||||
void
|
||||
vmprint(pagetable_t pagetable) {
|
||||
// your code here
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef LAB_PGTBL
|
||||
pte_t*
|
||||
pgpte(pagetable_t pagetable, uint64 va) {
|
||||
return walk(pagetable, va, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
169
nettest.py
Executable file
169
nettest.py
Executable file
@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
#
|
||||
# net tests
|
||||
# to be used with user/nettest.c
|
||||
#
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
|
||||
# qemu listens for packets sent to FWDPORT,
|
||||
# and re-writes them so they arrive in
|
||||
# xv6 with destination port 2000.
|
||||
FWDPORT1 = (os.getuid() % 5000) + 25999
|
||||
FWDPORT2 = (os.getuid() % 5000) + 30999
|
||||
|
||||
# xv6's nettest.c tx sends to SERVERPORT.
|
||||
SERVERPORT = (os.getuid() % 5000) + 25099
|
||||
|
||||
def usage():
|
||||
sys.stderr.write("Usage: nettest.py txone\n")
|
||||
sys.stderr.write(" nettest.py rxone\n")
|
||||
sys.stderr.write(" nettest.py rx\n")
|
||||
sys.stderr.write(" nettest.py rx2\n")
|
||||
sys.stderr.write(" nettest.py rxburst\n")
|
||||
sys.stderr.write(" nettest.py tx\n")
|
||||
sys.stderr.write(" nettest.py ping\n")
|
||||
sys.stderr.write(" nettest.py grade\n")
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
usage()
|
||||
|
||||
if sys.argv[1] == "txone":
|
||||
#
|
||||
# listen for a single UDP packet sent by xv6's nettest txone.
|
||||
# nettest.py must be started before xv6's nettest txone.
|
||||
#
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind(('127.0.0.1', SERVERPORT))
|
||||
print("tx: listening for a UDP packet")
|
||||
buf0, raddr0 = sock.recvfrom(4096)
|
||||
if buf0 == b'txone':
|
||||
print("txone: OK")
|
||||
else:
|
||||
print("txone: unexpected payload %s" % (buf0))
|
||||
elif sys.argv[1] == "rxone":
|
||||
#
|
||||
# send a single UDP packet to xv6 to test e1000_recv().
|
||||
# should result in arp_rx() printing
|
||||
# arp_rx: received an ARP packet
|
||||
# and ip_rx() printing
|
||||
# ip_rx: received an IP packet
|
||||
#
|
||||
print("txone: sending one UDP packet")
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.sendto(b'xyz', ("127.0.0.1", FWDPORT1))
|
||||
elif sys.argv[1] == "rx":
|
||||
#
|
||||
# test the xv6 receive path by sending a slow
|
||||
# stream of UDP packets, which should appear
|
||||
# on port 2000.
|
||||
#
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
i = 0
|
||||
while True:
|
||||
txt = "packet %d" % (i)
|
||||
sys.stderr.write("%s\n" % txt)
|
||||
buf = txt.encode('ascii', 'ignore')
|
||||
sock.sendto(buf, ("127.0.0.1", FWDPORT1))
|
||||
time.sleep(1)
|
||||
i += 1
|
||||
elif sys.argv[1] == "rx2":
|
||||
#
|
||||
# send to two different UDP ports, to see
|
||||
# if xv6 keeps them separate.
|
||||
#
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
i = 0
|
||||
while True:
|
||||
txt = "one %d" % (i)
|
||||
sys.stderr.write("%s\n" % txt)
|
||||
buf = txt.encode('ascii', 'ignore')
|
||||
sock.sendto(buf, ("127.0.0.1", FWDPORT1))
|
||||
|
||||
txt = "two %d" % (i)
|
||||
sys.stderr.write("%s\n" % txt)
|
||||
buf = txt.encode('ascii', 'ignore')
|
||||
sock.sendto(buf, ("127.0.0.1", FWDPORT2))
|
||||
|
||||
time.sleep(1)
|
||||
i += 1
|
||||
elif sys.argv[1] == "rxburst":
|
||||
#
|
||||
# send a big burst of packets to 2001, then
|
||||
# a packet to 2000.
|
||||
#
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
i = 0
|
||||
while True:
|
||||
|
||||
for ii in range(0, 32):
|
||||
txt = "packet %d" % (i)
|
||||
# sys.stderr.write("%s\n" % txt)
|
||||
buf = txt.encode('ascii', 'ignore')
|
||||
sock.sendto(buf, ("127.0.0.1", FWDPORT2))
|
||||
|
||||
txt = "packet %d" % (i)
|
||||
sys.stderr.write("%s\n" % txt)
|
||||
buf = txt.encode('ascii', 'ignore')
|
||||
sock.sendto(buf, ("127.0.0.1", FWDPORT1))
|
||||
|
||||
time.sleep(1)
|
||||
i += 1
|
||||
elif sys.argv[1] == "tx":
|
||||
#
|
||||
# listen for UDP packets sent by xv6's nettest tx.
|
||||
# nettest.py must be started before xv6's nettest tx.
|
||||
#
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind(('127.0.0.1', SERVERPORT))
|
||||
print("tx: listening for UDP packets")
|
||||
buf0, raddr0 = sock.recvfrom(4096)
|
||||
buf1, raddr1 = sock.recvfrom(4096)
|
||||
if buf0 == b't 0' and buf1 == b't 1':
|
||||
print("tx: OK")
|
||||
else:
|
||||
print("tx: unexpected packets %s and %s" % (buf0, buf1))
|
||||
elif sys.argv[1] == "ping":
|
||||
#
|
||||
# listen for UDP packets sent by xv6's nettest ping,
|
||||
# and send them back.
|
||||
# nettest.py must be started before xv6's nettest.
|
||||
#
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind(('127.0.0.1', SERVERPORT))
|
||||
print("ping: listening for UDP packets")
|
||||
while True:
|
||||
buf, raddr = sock.recvfrom(4096)
|
||||
sock.sendto(buf, raddr)
|
||||
elif sys.argv[1] == "grade":
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind(('127.0.0.1', SERVERPORT))
|
||||
|
||||
# first, listen for a single UDP packet sent by xv6,
|
||||
# in order to test only e1000_transmit(), in a situation
|
||||
# where perhaps e1000_recv() has not yet been implemented.
|
||||
buf, raddr = sock.recvfrom(4096)
|
||||
if buf == b'txone':
|
||||
print("txone: OK")
|
||||
else:
|
||||
print("txone: received incorrect payload %s" % (buf))
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
# second, send a single UDP packet, to test
|
||||
# e1000_recv() -- received by user/nettest.c's rxone().
|
||||
print("rxone: sending one UDP packet")
|
||||
sock1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock1.sendto(b'rxone', ("127.0.0.1", FWDPORT2))
|
||||
|
||||
# third, act as a ping reflector.
|
||||
while True:
|
||||
buf, raddr = sock.recvfrom(4096)
|
||||
sock.sendto(buf, raddr)
|
||||
else:
|
||||
usage()
|
||||
@ -1,400 +0,0 @@
|
||||
#include "kernel/fcntl.h"
|
||||
#include "kernel/param.h"
|
||||
#include "kernel/types.h"
|
||||
#include "kernel/stat.h"
|
||||
#include "kernel/riscv.h"
|
||||
#include "kernel/fs.h"
|
||||
#include "user/user.h"
|
||||
|
||||
void test0();
|
||||
void test1();
|
||||
void test2();
|
||||
void test3();
|
||||
|
||||
#define SZ 4096
|
||||
char buf[SZ];
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
test0();
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
createfile(char *file, int nblock)
|
||||
{
|
||||
int fd;
|
||||
char buf[BSIZE];
|
||||
int i;
|
||||
|
||||
fd = open(file, O_CREATE | O_RDWR);
|
||||
if(fd < 0){
|
||||
printf("createfile %s failed\n", file);
|
||||
exit(-1);
|
||||
}
|
||||
for(i = 0; i < nblock; i++) {
|
||||
if(write(fd, buf, sizeof(buf)) != sizeof(buf)) {
|
||||
printf("write %s failed\n", file);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void
|
||||
readfile(char *file, int nbytes, int inc)
|
||||
{
|
||||
char buf[BSIZE];
|
||||
int fd;
|
||||
int i;
|
||||
|
||||
if(inc > BSIZE) {
|
||||
printf("readfile: inc too large\n");
|
||||
exit(-1);
|
||||
}
|
||||
if ((fd = open(file, O_RDONLY)) < 0) {
|
||||
printf("readfile open %s failed\n", file);
|
||||
exit(-1);
|
||||
}
|
||||
for (i = 0; i < nbytes; i += inc) {
|
||||
if(read(fd, buf, inc) != inc) {
|
||||
printf("read %s failed for block %d (%d)\n", file, i, nbytes);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int ntas(int print)
|
||||
{
|
||||
int n;
|
||||
char *c;
|
||||
|
||||
if (statistics(buf, SZ) <= 0) {
|
||||
fprintf(2, "ntas: no stats\n");
|
||||
}
|
||||
c = strchr(buf, '=');
|
||||
n = atoi(c+2);
|
||||
if(print)
|
||||
printf("%s", buf);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Test reading small files concurrently
|
||||
void
|
||||
test0()
|
||||
{
|
||||
char file[2];
|
||||
char dir[2];
|
||||
enum { N = 10, NCHILD = 3 };
|
||||
int m, n;
|
||||
|
||||
dir[0] = '0';
|
||||
dir[1] = '\0';
|
||||
file[0] = 'F';
|
||||
file[1] = '\0';
|
||||
|
||||
printf("start test0\n");
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
dir[0] = '0' + i;
|
||||
mkdir(dir);
|
||||
if (chdir(dir) < 0) {
|
||||
printf("chdir failed\n");
|
||||
exit(1);
|
||||
}
|
||||
unlink(file);
|
||||
createfile(file, N);
|
||||
if (chdir("..") < 0) {
|
||||
printf("chdir failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
m = ntas(0);
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
dir[0] = '0' + i;
|
||||
int pid = fork();
|
||||
if(pid < 0){
|
||||
printf("fork failed");
|
||||
exit(-1);
|
||||
}
|
||||
if(pid == 0){
|
||||
if (chdir(dir) < 0) {
|
||||
printf("chdir failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
readfile(file, N*BSIZE, 1);
|
||||
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
wait(&status);
|
||||
if (status != 0) {
|
||||
printf("FAIL: a child failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("test0 results:\n");
|
||||
n = ntas(1);
|
||||
if (n-m < 500)
|
||||
printf("test0: OK\n");
|
||||
else
|
||||
printf("test0: FAIL\n");
|
||||
}
|
||||
|
||||
// Test bcache evictions by reading a large file concurrently
|
||||
void test1()
|
||||
{
|
||||
char file[3];
|
||||
enum { N = 200, BIG=100, NCHILD=2 };
|
||||
|
||||
printf("start test1\n");
|
||||
file[0] = 'B';
|
||||
file[2] = '\0';
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
file[1] = '0' + i;
|
||||
unlink(file);
|
||||
if (i == 0) {
|
||||
createfile(file, BIG);
|
||||
} else {
|
||||
createfile(file, 1);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
file[1] = '0' + i;
|
||||
int pid = fork();
|
||||
if(pid < 0){
|
||||
printf("fork failed");
|
||||
exit(-1);
|
||||
}
|
||||
if(pid == 0){
|
||||
if (i==0) {
|
||||
for (i = 0; i < N; i++) {
|
||||
readfile(file, BIG*BSIZE, BSIZE);
|
||||
}
|
||||
unlink(file);
|
||||
exit(0);
|
||||
} else {
|
||||
for (i = 0; i < N*20; i++) {
|
||||
readfile(file, 1, BSIZE);
|
||||
}
|
||||
unlink(file);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
wait(&status);
|
||||
if (status != 0) {
|
||||
printf("FAIL: a child failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\ntest1 OK\n");
|
||||
}
|
||||
|
||||
//
|
||||
// test concurrent creates.
|
||||
//
|
||||
void
|
||||
test2()
|
||||
{
|
||||
int nc = 4;
|
||||
char file[16];
|
||||
|
||||
printf("start test2\n");
|
||||
|
||||
mkdir("d2");
|
||||
|
||||
file[0] = 'd';
|
||||
file[1] = '2';
|
||||
file[2] = '/';
|
||||
|
||||
// remove any stale existing files.
|
||||
for(int i = 0; i < 50; i++){
|
||||
for(int ci = 0; ci < nc; ci++){
|
||||
file[3] = 'a' + ci;
|
||||
file[4] = '0' + i;
|
||||
file[5] = '\0';
|
||||
unlink(file);
|
||||
}
|
||||
}
|
||||
|
||||
int pids[nc];
|
||||
for(int ci = 0; ci < nc; ci++){
|
||||
pids[ci] = fork();
|
||||
if(pids[ci] < 0){
|
||||
printf("test2: fork failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if(pids[ci] == 0){
|
||||
char me = "abcdefghijklmnop"[ci];
|
||||
int pid = getpid();
|
||||
int nf = (ci == 0 ? 10 : 15);
|
||||
|
||||
// create nf files.
|
||||
for(int i = 0; i < nf; i++){
|
||||
file[3] = me;
|
||||
file[4] = '0' + i;
|
||||
file[5] = '\0';
|
||||
int fd = open(file, O_CREATE | O_RDWR);
|
||||
if(fd < 0){
|
||||
printf("test2: create %s failed\n", file);
|
||||
exit(1);
|
||||
}
|
||||
int xx = (pid << 16) | i;
|
||||
for(int nw = 0; nw < 2; nw++){
|
||||
// the sleep() increases the chance of simultaneous
|
||||
// calls to bget().
|
||||
sleep(1);
|
||||
if(write(fd, &xx, sizeof(xx)) <= 0){
|
||||
printf("test2: write %s failed\n", file);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// read back the nf files.
|
||||
for(int i = 0; i < nf; i++){
|
||||
file[3] = me;
|
||||
file[4] = '0' + i;
|
||||
file[5] = '\0';
|
||||
// printf("r %s\n", file);
|
||||
int fd = open(file, O_RDWR);
|
||||
if(fd < 0){
|
||||
printf("test2: open %s failed\n", file);
|
||||
exit(1);
|
||||
}
|
||||
int xx = (pid << 16) | i;
|
||||
for(int nr = 0; nr < 2; nr++){
|
||||
int z = 0;
|
||||
sleep(1);
|
||||
int n = read(fd, &z, sizeof(z));
|
||||
if(n != sizeof(z)){
|
||||
printf("test2: read %s returned %d, expected %ld\n", file, n, sizeof(z));
|
||||
exit(1);
|
||||
}
|
||||
if(z != xx){
|
||||
printf("test2: file %s contained %d, not %d\n", file, z, xx);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// delete the nf files.
|
||||
for(int i = 0; i < nf; i++){
|
||||
file[3] = me;
|
||||
file[4] = '0' + i;
|
||||
file[5] = '\0';
|
||||
//printf("u %s\n", file);
|
||||
if(unlink(file) != 0){
|
||||
printf("test2: unlink %s failed\n", file);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
int ok = 1;
|
||||
|
||||
for(int ci = 0; ci < nc; ci++){
|
||||
int st = 0;
|
||||
int ret = wait(&st);
|
||||
if(ret <= 0){
|
||||
printf("test2: wait() failed\n");
|
||||
ok = 0;
|
||||
}
|
||||
if(st != 0)
|
||||
ok = 0;
|
||||
}
|
||||
if(ok) {
|
||||
printf("\ntest2 OK\n");
|
||||
} else {
|
||||
printf("test2 failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// generate big log transactions to check that bget() can
|
||||
// make use of any of the NBUF buffers for any block number.
|
||||
//
|
||||
void
|
||||
test3()
|
||||
{
|
||||
int nc = 5;
|
||||
char file[16];
|
||||
|
||||
printf("start test3\n");
|
||||
|
||||
mkdir("d2");
|
||||
|
||||
file[0] = 'd';
|
||||
file[1] = '2';
|
||||
file[2] = '/';
|
||||
|
||||
int pids[nc];
|
||||
for(int ci = 0; ci < nc; ci++){
|
||||
pids[ci] = fork();
|
||||
if(pids[ci] < 0){
|
||||
printf("test3: fork failed\n");
|
||||
exit(1);
|
||||
}
|
||||
if(pids[ci] == 0){
|
||||
file[3] = 'a' + ci;
|
||||
file[4] = '\0';
|
||||
unlink(file);
|
||||
int fd = open(file, O_CREATE | O_RDWR);
|
||||
if(fd < 0){
|
||||
printf("test3: create %s failed\n", file);
|
||||
exit(1);
|
||||
}
|
||||
write(fd, "x", 1);
|
||||
static char junk[12*512];
|
||||
for(int i = 0; i < 12; i++){
|
||||
sleep(1);
|
||||
write(fd, junk, sizeof(junk));
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
int ok = 1;
|
||||
|
||||
for(int ci = 0; ci < nc; ci++){
|
||||
int st = 0;
|
||||
int ret = wait(&st);
|
||||
if(ret <= 0){
|
||||
printf("test3: wait() failed\n");
|
||||
ok = 0;
|
||||
}
|
||||
if(st != 0)
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
for(int ci = 0; ci < nc; ci++){
|
||||
file[3] = 'a' + ci;
|
||||
file[4] = '\0';
|
||||
unlink(file);
|
||||
}
|
||||
|
||||
if(ok) {
|
||||
printf("\ntest3 OK\n");
|
||||
} else {
|
||||
printf("test3 failed\n");
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,6 @@ main(void)
|
||||
|
||||
if(open("console", O_RDWR) < 0){
|
||||
mknod("console", CONSOLE, 0);
|
||||
mknod("statistics", STATS, 0);
|
||||
open("console", O_RDWR);
|
||||
}
|
||||
dup(0); // stdout
|
||||
|
||||
@ -1,198 +0,0 @@
|
||||
#include "kernel/param.h"
|
||||
#include "kernel/types.h"
|
||||
#include "kernel/stat.h"
|
||||
#include "kernel/riscv.h"
|
||||
#include "kernel/memlayout.h"
|
||||
#include "kernel/fcntl.h"
|
||||
#include "user/user.h"
|
||||
|
||||
#define NCHILD 2
|
||||
#define N 100000
|
||||
#define SZ 4096
|
||||
|
||||
void test1(void);
|
||||
void test2(void);
|
||||
void test3(void);
|
||||
char buf[SZ];
|
||||
|
||||
int countfree();
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int ntas(int print)
|
||||
{
|
||||
int n;
|
||||
char *c;
|
||||
|
||||
if (statistics(buf, SZ) <= 0) {
|
||||
fprintf(2, "ntas: no stats\n");
|
||||
}
|
||||
c = strchr(buf, '=');
|
||||
n = atoi(c+2);
|
||||
if(print)
|
||||
printf("%s", buf);
|
||||
return n;
|
||||
}
|
||||
|
||||
// Test concurrent kallocs and kfrees
|
||||
void test1(void)
|
||||
{
|
||||
void *a, *a1;
|
||||
int n, m;
|
||||
|
||||
printf("start test1\n");
|
||||
m = ntas(0);
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
int pid = fork();
|
||||
if(pid < 0){
|
||||
printf("fork failed");
|
||||
exit(-1);
|
||||
}
|
||||
if(pid == 0){
|
||||
for(i = 0; i < N; i++) {
|
||||
a = sbrk(4096);
|
||||
*(int *)(a+4) = 1;
|
||||
a1 = sbrk(-4096);
|
||||
if (a1 != a + 4096) {
|
||||
printf("test1: FAIL wrong sbrk\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
int status = 0;
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
wait(&status);
|
||||
if (status != 0) {
|
||||
printf("FAIL: a child failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("test1 results:\n");
|
||||
n = ntas(1);
|
||||
if(n-m < 10)
|
||||
printf("test1 OK\n");
|
||||
else
|
||||
printf("test1 FAIL\n");
|
||||
}
|
||||
|
||||
|
||||
// Test stealing
|
||||
void test2() {
|
||||
int free0 = countfree();
|
||||
int free1;
|
||||
int n = (PHYSTOP-KERNBASE)/PGSIZE;
|
||||
printf("start test2\n");
|
||||
printf("total free number of pages: %d (out of %d)\n", free0, n);
|
||||
if(n - free0 > 1000) {
|
||||
printf("test2 FAILED: cannot allocate enough memory");
|
||||
exit(1);
|
||||
}
|
||||
for (int i = 0; i < 50; i++) {
|
||||
free1 = countfree();
|
||||
if(i % 10 == 9)
|
||||
printf(".");
|
||||
if(free1 != free0) {
|
||||
printf("test2 FAIL: losing pages %d %d\n", free0, free1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
printf("\ntest2 OK\n");
|
||||
}
|
||||
|
||||
// Test concurrent kalloc/kfree and stealing
|
||||
void test3(void)
|
||||
{
|
||||
uint64 a, a1;
|
||||
int n, m;
|
||||
|
||||
m = ntas(0);
|
||||
printf("start test3\n");
|
||||
int pid;
|
||||
|
||||
for(int i = 0; i < NCHILD; i++){
|
||||
pid = fork();
|
||||
if(pid < 0){
|
||||
printf("fork failed");
|
||||
exit(-1);
|
||||
}
|
||||
if(pid == 0){
|
||||
if (i == 0) {
|
||||
for(i = 0; i < N; i++) {
|
||||
a = (uint64) sbrk(4096);
|
||||
if(a == 0xffffffffffffffff){
|
||||
// no freemem
|
||||
continue;
|
||||
}
|
||||
*(int *)(a+4) = 1;
|
||||
a1 = (uint64) sbrk(-4096);
|
||||
if (a1 != a + 4096) {
|
||||
printf("test3 FAIL: wrong sbrk\n");
|
||||
exit(1);
|
||||
}
|
||||
if ((i + 1) % 10000 == 0) {
|
||||
printf(".");
|
||||
}
|
||||
}
|
||||
printf("child done %d\n", i);
|
||||
exit(0);
|
||||
} else {
|
||||
while (1) {
|
||||
int free0 = countfree();
|
||||
int free1 = countfree();
|
||||
if(free0 - free1 > 1) {
|
||||
printf("test3 FAIL: losing pages %d %d\n", free0, free1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int status = 0;
|
||||
for(int i = 0; i < NCHILD-1; i++){
|
||||
wait(&status);
|
||||
if (status != 0) {
|
||||
printf("a child failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
kill(pid);
|
||||
|
||||
n = ntas(1);
|
||||
if(n-m < 4000)
|
||||
printf("\ntest3 OK\n");
|
||||
else
|
||||
printf("test3 FAIL m %d n %d\n", m, n);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// countfree() from usertests.c
|
||||
//
|
||||
int
|
||||
countfree()
|
||||
{
|
||||
uint64 sz0 = (uint64)sbrk(0);
|
||||
int n = 0;
|
||||
|
||||
while(1){
|
||||
uint64 a = (uint64) sbrk(4096);
|
||||
if(a == 0xffffffffffffffff){
|
||||
break;
|
||||
}
|
||||
// modify the memory to make sure it's really allocated.
|
||||
*(char *)(a + 4096 - 1) = 1;
|
||||
n += 1;
|
||||
}
|
||||
sbrk(-((uint64)sbrk(0) - sz0));
|
||||
return n;
|
||||
}
|
||||
945
user/nettest.c
Normal file
945
user/nettest.c
Normal file
@ -0,0 +1,945 @@
|
||||
//
|
||||
// network tests
|
||||
// to be used with nettest.py (run outside of qemu)
|
||||
//
|
||||
|
||||
#include "kernel/types.h"
|
||||
#include "kernel/net.h"
|
||||
#include "kernel/stat.h"
|
||||
#include "user/user.h"
|
||||
#define DEBUG_INFO printf("File: %s, Line: %d\n", __FILE__, __LINE__)
|
||||
|
||||
//
|
||||
// send a single UDP packet (but don't recv() the reply).
|
||||
// python3 nettest.py txone can be used to wait for
|
||||
// this packet, and you can also see what
|
||||
// happened with tcpdump -XXnr packets.pcap
|
||||
//
|
||||
void
|
||||
txone()
|
||||
{
|
||||
printf("txone: sending one packet\n");
|
||||
uint32 dst = 0x0A000202; // 10.0.2.2
|
||||
int dport = NET_TESTS_PORT;
|
||||
char buf[5];
|
||||
buf[0] = 't';
|
||||
buf[1] = 'x';
|
||||
buf[2] = 'o';
|
||||
buf[3] = 'n';
|
||||
buf[4] = 'e';
|
||||
if(send(2003, dst, dport, buf, 5) < 0){
|
||||
printf("txone: send() failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// test just receive.
|
||||
// outside of qemu, run
|
||||
// ./nettest.py rx
|
||||
//
|
||||
int
|
||||
rx(char *name)
|
||||
{
|
||||
bind(2000);
|
||||
|
||||
int lastseq = -1;
|
||||
int ok = 0;
|
||||
|
||||
while(ok < 4){
|
||||
char ibuf[128];
|
||||
uint32 src;
|
||||
uint16 sport;
|
||||
int cc = recv(2000, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
fprintf(2, "nettest %s: recv() failed\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(src != 0x0A000202){ // 10.0.2.2
|
||||
printf("wrong ip src %x\n", src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc < strlen("packet 1")){
|
||||
printf("len %d too short\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc > strlen("packet xxxxxx")){
|
||||
printf("len %d too long\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(memcmp(ibuf, "packet ", strlen("packet ")) != 0){
|
||||
printf("packet doesn't start with packet\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ibuf[cc] = '\0';
|
||||
#define isdigit(x) ((x) >= '0' && (x) <= '9')
|
||||
if(!isdigit(ibuf[7])){
|
||||
printf("packet doesn't contain a number\n");
|
||||
return 0;
|
||||
}
|
||||
for(int i = 7; i < cc; i++){
|
||||
if(!isdigit(ibuf[i])){
|
||||
printf("packet contains non-digits in the number\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int seq = ibuf[7] - '0';
|
||||
if(isdigit(ibuf[8])){
|
||||
seq *= 10;
|
||||
seq += ibuf[8] - '0';
|
||||
if(isdigit(ibuf[9])){
|
||||
seq *= 10;
|
||||
seq += ibuf[9] - '0';
|
||||
}
|
||||
}
|
||||
|
||||
if(lastseq != -1){
|
||||
if(seq != lastseq + 1){
|
||||
printf("got seq %d, expecting %d\n", seq, lastseq + 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
lastseq = seq;
|
||||
|
||||
ok += 1;
|
||||
}
|
||||
|
||||
printf("%s: OK\n", name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// test receive on two different ports, interleaved.
|
||||
// outside of qemu, run
|
||||
// ./nettest.py rx2
|
||||
//
|
||||
int
|
||||
rx2()
|
||||
{
|
||||
bind(2000);
|
||||
bind(2001);
|
||||
|
||||
for(int i = 0; i < 3; i++){
|
||||
char ibuf[128];
|
||||
uint32 src;
|
||||
uint16 sport;
|
||||
int cc = recv(2000, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
fprintf(2, "nettest rx2: recv() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(src != 0x0A000202){ // 10.0.2.2
|
||||
printf("wrong ip src %x\n", src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc < strlen("one 1")){
|
||||
printf("len %d too short\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc > strlen("one xxxxxx")){
|
||||
printf("len %d too long\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(memcmp(ibuf, "one ", strlen("one ")) != 0){
|
||||
printf("packet doesn't start with one\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < 3; i++){
|
||||
char ibuf[128];
|
||||
uint32 src;
|
||||
uint16 sport;
|
||||
int cc = recv(2001, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
fprintf(2, "nettest rx2: recv() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(src != 0x0A000202){ // 10.0.2.2
|
||||
printf("wrong ip src %x\n", src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc < strlen("one 1")){
|
||||
printf("len %d too short\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc > strlen("one xxxxxx")){
|
||||
printf("len %d too long\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(memcmp(ibuf, "two ", strlen("two ")) != 0){
|
||||
printf("packet doesn't start with two\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < 3; i++){
|
||||
char ibuf[128];
|
||||
uint32 src;
|
||||
uint16 sport;
|
||||
int cc = recv(2000, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
fprintf(2, "nettest rx2: recv() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(src != 0x0A000202){ // 10.0.2.2
|
||||
printf("wrong ip src %x\n", src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc < strlen("one 1")){
|
||||
printf("len %d too short\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc > strlen("one xxxxxx")){
|
||||
printf("len %d too long\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(memcmp(ibuf, "one ", strlen("one ")) != 0){
|
||||
printf("packet doesn't start with one\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("rx2: OK\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// send some UDP packets to nettest.py tx.
|
||||
//
|
||||
int
|
||||
tx()
|
||||
{
|
||||
for(int ii = 0; ii < 5; ii++){
|
||||
uint32 dst = 0x0A000202; // 10.0.2.2
|
||||
int dport = NET_TESTS_PORT;
|
||||
char buf[3];
|
||||
buf[0] = 't';
|
||||
buf[1] = ' ';
|
||||
buf[2] = '0' + ii;
|
||||
if(send(2000, dst, dport, buf, 3) < 0){
|
||||
printf("send() failed\n");
|
||||
return 0;
|
||||
}
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
// can't actually tell if the packets arrived.
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// send just one UDP packets to nettest.py ping,
|
||||
// expect a reply.
|
||||
// nettest.py ping must be started first.
|
||||
//
|
||||
int
|
||||
ping0()
|
||||
{
|
||||
printf("ping0: starting\n");
|
||||
|
||||
bind(2004);
|
||||
|
||||
uint32 dst = 0x0A000202; // 10.0.2.2
|
||||
int dport = NET_TESTS_PORT;
|
||||
char buf[5];
|
||||
memcpy(buf, "ping0", sizeof(buf));
|
||||
if(send(2004, dst, dport, buf, sizeof(buf)) < 0){
|
||||
printf("ping0: send() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char ibuf[128];
|
||||
uint32 src = 0;
|
||||
uint16 sport = 0;
|
||||
memset(ibuf, 0, sizeof(ibuf));
|
||||
int cc = recv(2004, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
fprintf(2, "ping0: recv() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(src != 0x0A000202){ // 10.0.2.2
|
||||
printf("ping0: wrong ip src %x, expecting %x\n", src, 0x0A000202);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(sport != NET_TESTS_PORT){
|
||||
printf("ping0: wrong sport %d, expecting %d\n", sport, NET_TESTS_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(memcmp(buf, ibuf, sizeof(buf)) != 0){
|
||||
printf("ping0: wrong content\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc != sizeof(buf)){
|
||||
printf("ping0: wrong length %d, expecting %ld\n", cc, sizeof(buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("ping0: OK\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// send many UDP packets to nettest.py ping,
|
||||
// expect a reply to each.
|
||||
// nettest.py ping must be started first.
|
||||
//
|
||||
int
|
||||
ping1()
|
||||
{
|
||||
printf("ping1: starting\n");
|
||||
|
||||
bind(2005);
|
||||
|
||||
for(int ii = 0; ii < 20; ii++){
|
||||
uint32 dst = 0x0A000202; // 10.0.2.2
|
||||
int dport = NET_TESTS_PORT;
|
||||
char buf[3];
|
||||
buf[0] = 'p';
|
||||
buf[1] = ' ';
|
||||
buf[2] = '0' + ii;
|
||||
if(send(2005, dst, dport, buf, 3) < 0){
|
||||
printf("ping1: send() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char ibuf[128];
|
||||
uint32 src = 0;
|
||||
uint16 sport = 0;
|
||||
memset(ibuf, 0, sizeof(ibuf));
|
||||
int cc = recv(2005, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
fprintf(2, "ping1: recv() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(src != 0x0A000202){ // 10.0.2.2
|
||||
printf("ping1: wrong ip src %x, expecting %x\n", src, 0x0A000202);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(sport != NET_TESTS_PORT){
|
||||
printf("ping1: wrong sport %d, expecting %d\n", sport, NET_TESTS_PORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(memcmp(buf, ibuf, 3) != 0){
|
||||
printf("ping1: wrong content\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc != 3){
|
||||
printf("ping1: wrong length %d, expecting 3\n", cc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("ping1: OK\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// send UDP packets from two different ports to nettest.py ping,
|
||||
// expect a reply to each to appear on the correct port.
|
||||
// nettest.py ping must be started first.
|
||||
//
|
||||
int
|
||||
ping2()
|
||||
{
|
||||
printf("ping2: starting\n");
|
||||
|
||||
bind(2006);
|
||||
bind(2007);
|
||||
|
||||
for(int ii = 0; ii < 5; ii++){
|
||||
for(int port = 2006; port <= 2007; port++){
|
||||
uint32 dst = 0x0A000202; // 10.0.2.2
|
||||
int dport = NET_TESTS_PORT;
|
||||
char buf[4];
|
||||
buf[0] = 'p';
|
||||
buf[1] = ' ';
|
||||
buf[2] = (port == 2006 ? 'a' : 'A') + ii;
|
||||
buf[3] = '!';
|
||||
if(send(port, dst, dport, buf, 4) < 0){
|
||||
printf("ping2: send() failed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int port = 2006; port <= 2007; port++){
|
||||
for(int ii = 0; ii < 5; ii++){
|
||||
char ibuf[128];
|
||||
uint32 src = 0;
|
||||
uint16 sport = 0;
|
||||
memset(ibuf, 0, sizeof(ibuf));
|
||||
int cc = recv(port, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
fprintf(2, "ping2: recv() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(src != 0x0A000202){ // 10.0.2.2
|
||||
printf("ping2: wrong ip src %x\n", src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(sport != NET_TESTS_PORT){
|
||||
printf("ping2: wrong sport %d\n", sport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc != 4){
|
||||
printf("ping2: wrong length %d\n", cc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// printf("port=%d ii=%d: %c%c%c\n", port, ii, ibuf[0], ibuf[1], ibuf[2]);
|
||||
|
||||
char buf[4];
|
||||
buf[0] = 'p';
|
||||
buf[1] = ' ';
|
||||
buf[2] = (port == 2006 ? 'a' : 'A') + ii;
|
||||
buf[3] = '!';
|
||||
|
||||
if(memcmp(buf, ibuf, 3) != 0){
|
||||
// possibly recv() sees packets out of order.
|
||||
printf("ping2: wrong content\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("ping2: OK\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// send a big burst of packets from ports 2008 and 2010,
|
||||
// causing drops,
|
||||
// bracketed by two packets from port 2009.
|
||||
// check that the two packets can be recv()'d on port 2009.
|
||||
// check that port 2008 had a finite queue length (dropped some).
|
||||
// nettest.py ping must be started first.
|
||||
//
|
||||
int
|
||||
ping3()
|
||||
{
|
||||
printf("ping3: starting\n");
|
||||
|
||||
bind(2008);
|
||||
bind(2009);
|
||||
|
||||
//
|
||||
// send one packet on 2009.
|
||||
//
|
||||
{
|
||||
uint32 dst = 0x0A000202; // 10.0.2.2
|
||||
int dport = NET_TESTS_PORT;
|
||||
char buf[4];
|
||||
buf[0] = 'p';
|
||||
buf[1] = ' ';
|
||||
buf[2] = 'A';
|
||||
buf[3] = '!';
|
||||
if(send(2009, dst, dport, buf, 4) < 0){
|
||||
printf("ping3: send() failed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
sleep(1);
|
||||
//
|
||||
// send so many packets from 2008 and 2010 that some of the
|
||||
// replies must be dropped due to the requirement
|
||||
// for finite maximum queueing.
|
||||
//
|
||||
for(int ii = 0; ii < 257; ii++){
|
||||
uint32 dst = 0x0A000202; // 10.0.2.2
|
||||
int dport = NET_TESTS_PORT;
|
||||
char buf[4];
|
||||
buf[0] = 'p';
|
||||
buf[1] = ' ';
|
||||
buf[2] = 'a' + ii;
|
||||
buf[3] = '!';
|
||||
int port = 2008 + (ii % 2) * 2;
|
||||
if(send(port, dst, dport, buf, 4) < 0){
|
||||
printf("ping3: send() failed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
sleep(1);
|
||||
//
|
||||
// send another packet from 2009.
|
||||
//
|
||||
{
|
||||
uint32 dst = 0x0A000202; // 10.0.2.2
|
||||
int dport = NET_TESTS_PORT;
|
||||
char buf[4];
|
||||
buf[0] = 'p';
|
||||
buf[1] = ' ';
|
||||
buf[2] = 'B';
|
||||
buf[3] = '!';
|
||||
if(send(2009, dst, dport, buf, 4) < 0){
|
||||
printf("ping3: send() failed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//
|
||||
// did both reply packets for 2009 arrive?
|
||||
//
|
||||
for(int ii = 0; ii < 2; ii++){
|
||||
char ibuf[128];
|
||||
uint32 src = 0;
|
||||
uint16 sport = 0;
|
||||
memset(ibuf, 0, sizeof(ibuf));
|
||||
int cc = recv(2009, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
fprintf(2, "ping3: recv() failed\n");
|
||||
return 0;
|
||||
}
|
||||
if(src != 0x0A000202){ // 10.0.2.2
|
||||
printf("ping3: wrong ip src %x\n", src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(sport != NET_TESTS_PORT){
|
||||
printf("ping3: wrong sport %d\n", sport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(cc != 4){
|
||||
printf("ping3: wrong length %d\n", cc);
|
||||
return 0;
|
||||
}
|
||||
// printf("port=%d ii=%d: %c%c%c\n", port, ii, ibuf[0], ibuf[1], ibuf[2]);
|
||||
|
||||
char buf[4];
|
||||
buf[0] = 'p';
|
||||
buf[1] = ' ';
|
||||
buf[2] = 'A' + ii;
|
||||
buf[3] = '!';
|
||||
|
||||
if(memcmp(buf, ibuf, 3) != 0){
|
||||
// possibly recv() sees packets out of order.
|
||||
// possibly the burst on 2008 caused 2009's
|
||||
// packets to be dropped.
|
||||
printf("ping3: wrong content\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// now count how many replies were queued for 2008.
|
||||
//
|
||||
int fds[2];
|
||||
pipe(fds);
|
||||
int pid = fork();
|
||||
if(pid == 0){
|
||||
close(fds[0]);
|
||||
write(fds[1], ":", 1); // ensure parent's read() doesn't block
|
||||
while(1){
|
||||
char ibuf[128];
|
||||
uint32 src = 0;
|
||||
uint16 sport = 0;
|
||||
memset(ibuf, 0, sizeof(ibuf));
|
||||
int cc = recv(2008, &src, &sport, ibuf, sizeof(ibuf)-1);
|
||||
if(cc < 0){
|
||||
printf("ping3: recv failed\n");
|
||||
break;
|
||||
}
|
||||
write(fds[1], "x", 1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
close(fds[1]);
|
||||
sleep(5);
|
||||
static char nbuf[512];
|
||||
int n = read(fds[0], nbuf, sizeof(nbuf));
|
||||
close(fds[0]);
|
||||
kill(pid);
|
||||
|
||||
n -= 1; // the ":"
|
||||
if(n > 16){
|
||||
printf("ping3: too many packets (%d) were queued on a UDP port\n", n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("ping3: OK\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Encode a DNS name
|
||||
void
|
||||
encode_qname(char *qn, char *host)
|
||||
{
|
||||
char *l = host;
|
||||
|
||||
for(char *c = host; c < host+strlen(host)+1; c++) {
|
||||
if(*c == '.') {
|
||||
*qn++ = (char) (c-l);
|
||||
for(char *d = l; d < c; d++) {
|
||||
*qn++ = *d;
|
||||
}
|
||||
l = c+1; // skip .
|
||||
}
|
||||
}
|
||||
*qn = '\0';
|
||||
}
|
||||
|
||||
// Decode a DNS name
|
||||
void
|
||||
decode_qname(char *qn, int max)
|
||||
{
|
||||
char *qnMax = qn + max;
|
||||
while(1){
|
||||
if(qn >= qnMax){
|
||||
printf("invalid DNS reply\n");
|
||||
exit(1);
|
||||
}
|
||||
int l = *qn;
|
||||
if(l == 0)
|
||||
break;
|
||||
for(int i = 0; i < l; i++) {
|
||||
*qn = *(qn+1);
|
||||
qn++;
|
||||
}
|
||||
*qn++ = '.';
|
||||
}
|
||||
}
|
||||
|
||||
// Make a DNS request
|
||||
int
|
||||
dns_req(uint8 *obuf)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
struct dns *hdr = (struct dns *) obuf;
|
||||
hdr->id = htons(6828);
|
||||
hdr->rd = 1;
|
||||
hdr->qdcount = htons(1);
|
||||
|
||||
len += sizeof(struct dns);
|
||||
|
||||
// qname part of question
|
||||
char *qname = (char *) (obuf + sizeof(struct dns));
|
||||
char *s = "pdos.csail.mit.edu.";
|
||||
encode_qname(qname, s);
|
||||
len += strlen(qname) + 1;
|
||||
|
||||
// constants part of question
|
||||
struct dns_question *h = (struct dns_question *) (qname+strlen(qname)+1);
|
||||
h->qtype = htons(0x1);
|
||||
h->qclass = htons(0x1);
|
||||
|
||||
len += sizeof(struct dns_question);
|
||||
return len;
|
||||
}
|
||||
|
||||
// Process DNS response
|
||||
int
|
||||
dns_rep(uint8 *ibuf, int cc)
|
||||
{
|
||||
struct dns *hdr = (struct dns *) ibuf;
|
||||
int len;
|
||||
char *qname = 0;
|
||||
int record = 0;
|
||||
|
||||
if(cc < sizeof(struct dns)){
|
||||
printf("DNS reply too short\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!hdr->qr) {
|
||||
printf("Not a DNS reply for %d\n", ntohs(hdr->id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(hdr->id != htons(6828)){
|
||||
printf("DNS wrong id: %d\n", ntohs(hdr->id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(hdr->rcode != 0) {
|
||||
printf("DNS rcode error: %x\n", hdr->rcode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//printf("qdcount: %x\n", ntohs(hdr->qdcount));
|
||||
//printf("ancount: %x\n", ntohs(hdr->ancount));
|
||||
//printf("nscount: %x\n", ntohs(hdr->nscount));
|
||||
//printf("arcount: %x\n", ntohs(hdr->arcount));
|
||||
|
||||
len = sizeof(struct dns);
|
||||
|
||||
for(int i =0; i < ntohs(hdr->qdcount); i++) {
|
||||
char *qn = (char *) (ibuf+len);
|
||||
qname = qn;
|
||||
decode_qname(qn, cc - len);
|
||||
len += strlen(qn)+1;
|
||||
len += sizeof(struct dns_question);
|
||||
}
|
||||
|
||||
for(int i = 0; i < ntohs(hdr->ancount); i++) {
|
||||
if(len >= cc){
|
||||
printf("dns: invalid DNS reply\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *qn = (char *) (ibuf+len);
|
||||
|
||||
if((int) qn[0] > 63) { // compression?
|
||||
qn = (char *)(ibuf+qn[1]);
|
||||
len += 2;
|
||||
} else {
|
||||
decode_qname(qn, cc - len);
|
||||
len += strlen(qn)+1;
|
||||
}
|
||||
|
||||
struct dns_data *d = (struct dns_data *) (ibuf+len);
|
||||
len += sizeof(struct dns_data);
|
||||
//printf("type %d ttl %d len %d\n", ntohs(d->type), ntohl(d->ttl), ntohs(d->len));
|
||||
if(ntohs(d->type) == ARECORD && ntohs(d->len) == 4) {
|
||||
record = 1;
|
||||
printf("DNS arecord for %s is ", qname ? qname : "" );
|
||||
uint8 *ip = (ibuf+len);
|
||||
printf("%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
|
||||
if(ip[0] != 128 || ip[1] != 52 || ip[2] != 129 || ip[3] != 126) {
|
||||
printf("dns: wrong ip address");
|
||||
return 0;
|
||||
}
|
||||
len += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// needed for DNS servers with EDNS support
|
||||
for(int i = 0; i < ntohs(hdr->arcount); i++) {
|
||||
char *qn = (char *) (ibuf+len);
|
||||
if(*qn != 0) {
|
||||
printf("dns: invalid name for EDNS\n");
|
||||
return 0;
|
||||
}
|
||||
len += 1;
|
||||
|
||||
struct dns_data *d = (struct dns_data *) (ibuf+len);
|
||||
len += sizeof(struct dns_data);
|
||||
if(ntohs(d->type) != 41) {
|
||||
printf("dns: invalid type for EDNS\n");
|
||||
return 0;
|
||||
}
|
||||
len += ntohs(d->len);
|
||||
}
|
||||
|
||||
if(len != cc) {
|
||||
printf("dns: processed %d data bytes but received %d\n", len, cc);
|
||||
return 0;
|
||||
}
|
||||
if(!record) {
|
||||
printf("dns: didn't receive an arecord\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
dns()
|
||||
{
|
||||
#define N 1000
|
||||
uint8 obuf[N];
|
||||
uint8 ibuf[N];
|
||||
uint32 dst;
|
||||
int len;
|
||||
|
||||
printf("dns: starting\n");
|
||||
|
||||
memset(obuf, 0, N);
|
||||
memset(ibuf, 0, N);
|
||||
|
||||
// 8.8.8.8: google's name server
|
||||
dst = (8 << 24) | (8 << 16) | (8 << 8) | (8 << 0);
|
||||
|
||||
len = dns_req(obuf);
|
||||
|
||||
bind(10000);
|
||||
|
||||
if(send(10000, dst, 53, (char*)obuf, len) < 0){
|
||||
fprintf(2, "dns: send() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 src;
|
||||
uint16 sport;
|
||||
int cc = recv(10000, &src, &sport, (char*)ibuf, sizeof(ibuf));
|
||||
if(cc < 0){
|
||||
fprintf(2, "dns: recv() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(dns_rep(ibuf, cc)){
|
||||
printf("dns: OK\n");
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
usage()
|
||||
{
|
||||
printf("Usage: nettest txone\n");
|
||||
printf(" nettest tx\n");
|
||||
printf(" nettest rx\n");
|
||||
printf(" nettest rx2\n");
|
||||
printf(" nettest rxburst\n");
|
||||
printf(" nettest ping1\n");
|
||||
printf(" nettest ping2\n");
|
||||
printf(" nettest ping3\n");
|
||||
printf(" nettest dns\n");
|
||||
printf(" nettest grade\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//
|
||||
// use sbrk() to count how many free physical memory pages there are.
|
||||
// touches the pages to force allocation.
|
||||
// because out of memory with lazy allocation results in the process
|
||||
// taking a fault and being killed, fork and report back.
|
||||
//
|
||||
int
|
||||
countfree()
|
||||
{
|
||||
int fds[2];
|
||||
|
||||
if(pipe(fds) < 0){
|
||||
printf("pipe() failed in countfree()\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int pid = fork();
|
||||
|
||||
if(pid < 0){
|
||||
printf("fork failed in countfree()\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if(pid == 0){
|
||||
close(fds[0]);
|
||||
|
||||
while(1){
|
||||
uint64 a = (uint64) sbrk(4096);
|
||||
if(a == 0xffffffffffffffff){
|
||||
break;
|
||||
}
|
||||
|
||||
// modify the memory to make sure it's really allocated.
|
||||
*(char *)(a + 4096 - 1) = 1;
|
||||
|
||||
// report back one more page.
|
||||
if(write(fds[1], "x", 1) != 1){
|
||||
printf("write() failed in countfree()\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
|
||||
int n = 0;
|
||||
while(1){
|
||||
char c;
|
||||
int cc = read(fds[0], &c, 1);
|
||||
if(cc < 0){
|
||||
printf("read() failed in countfree()\n");
|
||||
exit(1);
|
||||
}
|
||||
if(cc == 0)
|
||||
break;
|
||||
n += 1;
|
||||
}
|
||||
|
||||
close(fds[0]);
|
||||
wait((int*)0);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 2)
|
||||
usage();
|
||||
|
||||
if(strcmp(argv[1], "txone") == 0){
|
||||
txone();
|
||||
} else if(strcmp(argv[1], "rx") == 0 || strcmp(argv[1], "rxburst") == 0){
|
||||
rx(argv[1]);
|
||||
} else if(strcmp(argv[1], "rx2") == 0){
|
||||
rx2();
|
||||
} else if(strcmp(argv[1], "tx") == 0){
|
||||
tx();
|
||||
} else if(strcmp(argv[1], "ping0") == 0){
|
||||
ping0();
|
||||
} else if(strcmp(argv[1], "ping1") == 0){
|
||||
ping1();
|
||||
} else if(strcmp(argv[1], "ping2") == 0){
|
||||
ping2();
|
||||
} else if(strcmp(argv[1], "ping3") == 0){
|
||||
ping3();
|
||||
} else if(strcmp(argv[1], "grade") == 0){
|
||||
//
|
||||
// "python3 nettest.py grade" must already be running...
|
||||
//
|
||||
int free0 = countfree();
|
||||
int free1 = 0;
|
||||
txone();
|
||||
sleep(2);
|
||||
ping0();
|
||||
sleep(2);
|
||||
ping1();
|
||||
sleep(2);
|
||||
ping2();
|
||||
sleep(2);
|
||||
ping3();
|
||||
sleep(2);
|
||||
dns();
|
||||
sleep(2);
|
||||
if ((free1 = countfree()) + 32 < free0) {
|
||||
printf("free: FAILED -- lost too many free pages %d (out of %d)\n", free1, free0);
|
||||
} else {
|
||||
printf("free: OK\n");
|
||||
}
|
||||
} else if(strcmp(argv[1], "dns") == 0){
|
||||
dns();
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
52
user/pingpong.c
Normal file
52
user/pingpong.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include "kernel/types.h"
|
||||
#include "kernel/stat.h"
|
||||
#include "user/user.h"
|
||||
|
||||
#define N 5
|
||||
char buf[N];
|
||||
|
||||
void
|
||||
pong(int *parent_to_child, int *child_to_parent) {
|
||||
if (read(parent_to_child[0], buf, N) < 0) {
|
||||
printf("read failed\n");
|
||||
}
|
||||
printf("%d: received %s\n", getpid(), buf);
|
||||
if (write(child_to_parent[1], "pong", 4) != 4) {
|
||||
printf("write failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ping(int *parent_to_child, int *child_to_parent) {
|
||||
|
||||
if (write(parent_to_child[1], "ping", 4) != 4) {
|
||||
printf("write failed\n");
|
||||
}
|
||||
if (read(child_to_parent[0], buf, N) < 0) {
|
||||
printf("read failed\n");
|
||||
}
|
||||
printf("%d: received %s\n", getpid(), buf);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int parent_to_child[2];
|
||||
int child_to_parent[2];
|
||||
|
||||
int pid;
|
||||
|
||||
if (pipe(parent_to_child) < 0 || pipe(child_to_parent) < 0) {
|
||||
printf("pipe failed\n");
|
||||
}
|
||||
if ((pid = fork()) < 0) {
|
||||
printf("fork failed\n");
|
||||
}
|
||||
if (pid == 0) {
|
||||
pong(parent_to_child, child_to_parent);
|
||||
} else {
|
||||
ping(parent_to_child, child_to_parent);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
#include "kernel/types.h"
|
||||
#include "kernel/stat.h"
|
||||
#include "kernel/fcntl.h"
|
||||
#include "user/user.h"
|
||||
|
||||
int
|
||||
statistics(void *buf, int sz)
|
||||
{
|
||||
int fd, i, n;
|
||||
|
||||
fd = open("statistics", O_RDONLY);
|
||||
if(fd < 0) {
|
||||
fprintf(2, "stats: open failed\n");
|
||||
exit(1);
|
||||
}
|
||||
for (i = 0; i < sz; ) {
|
||||
if ((n = read(fd, buf+i, sz-i)) < 0) {
|
||||
break;
|
||||
}
|
||||
i += n;
|
||||
}
|
||||
close(fd);
|
||||
return i;
|
||||
}
|
||||
24
user/stats.c
24
user/stats.c
@ -1,24 +0,0 @@
|
||||
#include "kernel/types.h"
|
||||
#include "kernel/stat.h"
|
||||
#include "kernel/fcntl.h"
|
||||
#include "user/user.h"
|
||||
|
||||
#define SZ 4096
|
||||
char buf[SZ];
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
while (1) {
|
||||
n = statistics(buf, SZ);
|
||||
for (i = 0; i < n; i++) {
|
||||
write(1, buf+i, 1);
|
||||
}
|
||||
if (n != SZ)
|
||||
break;
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
@ -36,3 +36,9 @@ entry("getpid");
|
||||
entry("sbrk");
|
||||
entry("sleep");
|
||||
entry("uptime");
|
||||
entry("bind");
|
||||
entry("unbind");
|
||||
entry("send");
|
||||
entry("recv");
|
||||
entry("pgpte");
|
||||
entry("kpgtbl");
|
||||
|
||||
8
xv6-labs-2024.code-workspace
Normal file
8
xv6-labs-2024.code-workspace
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user