Compare commits

..

4 Commits

Author SHA1 Message Date
whatever
0d0a728f73 net done 2024-11-23 13:35:55 +08:00
whatever
079dcbaa31 personal commit 2024-11-20 23:00:16 +08:00
Ivy Wu
0b94a6a45f update lab net 2024-10-10 22:49:32 -04:00
Frans Kaashoek
10584ccdde net lab 2024-10-07 15:05:55 -04:00
38 changed files with 2187 additions and 1281 deletions

View File

@ -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

View File

@ -1 +1 @@
LAB=lock
LAB=net

BIN
fs.img.bk

Binary file not shown.

View File

@ -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
View 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()

View File

@ -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):

View File

@ -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
View 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
View 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;
};

View File

@ -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);
}

View File

@ -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)
{
}

View File

@ -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
View 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
View 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
View 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);
}
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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)

View File

@ -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
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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)
{

View File

@ -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

View File

@ -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);
}

View File

@ -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.

View File

@ -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
View 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
time.txt Normal file
View File

@ -0,0 +1 @@
5

View File

@ -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");
}
}

View File

@ -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

View File

@ -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
View 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
View 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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -36,3 +36,9 @@ entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
entry("bind");
entry("unbind");
entry("send");
entry("recv");
entry("pgpte");
entry("kpgtbl");

View File

@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}