1 <!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 3.2//EN">
4 <title>Lab
6: Network Driver
</title>
5 <link rel=
"stylesheet" href=
"../labs.css" type=
"text/css" />
6 <script type=
"text/javascript" src=
"../labs.js"></script>
7 <script type=
"text/javascript">
8 var references
= [["E1000 manual", "../../readings/hardware/8254x_GBe_SDM.pdf"],
9 ["e1000_hw.h", "e1000_hw.h"]];
15 <h1>6.828 Fall
2011 Lab
6: Network Driver
</h1>
19 Handed out Thursday, November
17,
2011<br />
20 Part A due Wednesday, November
23,
2011<br />
21 Part B due Thursday, December
1,
2011<br />
28 Now that you have a file system, no self respecting OS should go
29 without a network stack. In this the lab you are going to write a
30 driver for a network interface card. The card will be based on the
31 Intel
82540EM chip, also known as the E1000.
34 <h3>Getting Started
</h3>
37 Use Git to commit your Lab
5 source (if you haven't already), fetch
38 the latest version of the course
39 repository, and then create a local branch called
<tt>lab6
</tt> based on our
40 lab6 branch,
<tt>origin/lab6
</tt>:
43 athena%
<kbd>cd ~/
6.828/lab
</kbd>
44 athena%
<kbd>add git
</kbd>
45 athena%
<kbd>git commit -am 'my solution to lab5'
</kbd>
46 nothing to commit (working directory clean)
47 athena%
<kbd>git pull
</kbd>
49 athena%
<kbd>git checkout -b lab6 origin/lab6
</kbd>
50 Branch lab6 set up to track remote branch refs/remotes/origin/lab6.
51 Switched to a new branch
"lab6"
52 athena%
<kbd>git merge lab5
</kbd>
53 Merge made by recursive.
54 fs/fs.c |
42 +++++++++++++++++++
55 1 files changed,
42 insertions(+),
0 deletions(-)
60 The network card driver, however, will not be enough to get your OS hooked up to the
61 Internet. In the new lab6 code, we have provided you with a network stack
62 and a network server. As in previous labs, use git to grab the code for
63 this lab, merge in your own code, and explore the contents of the new
64 <tt>net/
</tt> directory, as well as the new files in
<tt>kern/
</tt>.
68 In addition to writing the driver, you will need to create a system call
69 interface to give access to your driver. You will implement missing network
70 server code to transfer packets between the network stack and your driver.
71 You will also tie everything together by finishing a web server. With the new
72 web server you will be able to serve files from your file system.
76 Much of the kernel device driver code you will have to write yourself from
77 scratch. This lab provides much less guidance than previous labs:
78 there are no skeleton files, no system call interfaces written in
79 stone, and many design decisions are left up to you. For
80 this reason, we recommend that you read the entire assignment write up
81 before starting any individual exercises.
82 Many students find this lab more difficult than previous labs, so
83 please plan your time accordingly.
86 <h3>Lab Requirements
</h3>
89 As before, you will need
90 to do all of the regular exercises described in the lab and
<i>at
91 least one
</i> challenge problem. Write up brief answers to the
92 questions posed in the lab and a description of your challenge
93 exercise in
<tt>answers-lab6.txt
</tt>.
96 <h2>QEMU's virtual network
</h2>
99 We will be using QEMU's user mode network stack since it requires no administrative
100 privileges to run. QEMU's documentation has more about user-net
101 <a href=
"http://wiki.qemu.org/download/qemu-doc.html#Using-the-user-mode-network-stack">here
</a>.
102 We've updated the makefile to enable QEMU's user-mode network stack
103 and the virtual E1000 network card.
107 By default, QEMU provides a virtual router running on IP
10.0.2.2 and
108 will assign JOS the IP address
10.0.2.15. To keep things simple, we
109 hard-code these defaults into the network server in
<tt>net/ns.h
</tt>.
113 While QEMU's virtual network allows JOS to make arbitrary connections
114 out to the Internet, JOS's
10.0.2.15 address has no meaning outside
115 the virtual network running inside QEMU (that is, QEMU acts as a NAT),
116 so we can't connect directly to servers running inside JOS, even from
117 the host running QEMU. To address this, we configure QEMU to run a
118 server on some port on the
<i>host
</i> machine that simply connects
119 through to some port in JOS and shuttles data back and forth between
120 your real host and the virtual network.
124 You will run JOS servers on ports
7 (echo) and
80 (http). To avoid
125 collisions on shared Athena machines, the makefile generates
126 forwarding ports for these based on your user ID. To find out what
127 ports QEMU is forwarding to on your development host, run
<kbd>make
128 which-ports
</kbd>. For convenience, the makefile also provides
129 <kbd>make nc-
7</kbd> and
<kbd>make nc-
80</kbd>, which allow you to
130 interact directly with servers running on these ports in your
131 terminal. (These targets only connect to a running QEMU instance; you
132 must start QEMU itself separately.)
135 <h3>Packet Inspection
</h3>
138 The makefile also configures QEMU's network stack to record all
139 incoming and outgoing packets to
<tt>qemu.pcap
</tt> in your lab
144 To get a hex/ASCII dump of captured packets use
<tt>tcpdump
</tt> like this:
148 <kbd>tcpdump -XXnr qemu.pcap
</kbd>
152 Alternatively, you can use
<a
153 href=
"http://www.wireshark.org/">Wireshark
</a> to graphically inspect
154 the pcap file. Wireshark also knows how to decode and inspect
155 hundreds of network protocols. If you're on Athena, you'll have to
156 use Wireshark's predecessor, ethereal, which is in the sipbnet locker.
159 <h3>Debugging the E1000
</h3>
162 We are very lucky to be using emulated hardware. Since the E1000 is running in
163 software, the emulated E1000 can report to us, in a user readable format, its
164 internal state and any problems it encounters. Normally, such a luxury would
165 not be available to a driver developer writing with bare metal.
169 The E1000 can produce a lot of debug output, so you have to enable
170 specific logging channels. Some channels you might find useful are:
173 <table align=
"center">
174 <tr><th align=
"left">Flag
</th><th align=
"left">Meaning
</th></tr>
175 <tr><td>tx
</td><td>Log packet transmit operations
</td></tr>
176 <tr><td>txerr
</td><td>Log transmit ring errors
</td></tr>
177 <tr><td>rx
</td><td>Log changes to RCTL
</td></tr>
178 <tr><td>rxfilter
</td><td>Log filtering of incoming packets
</td></tr>
179 <tr><td>rxerr
</td><td>Log receive ring errors
</td></tr>
180 <tr><td>unknown
</td><td>Log reads and writes of unknown registers
</td></tr>
181 <tr><td>eeprom
</td><td>Log reads from the EEPROM
</td></tr>
182 <tr><td>interrupt
</td><td>Log interrupts and changes to interrupt
186 <p>To enable
"tx" and
"txerr" logging, for example, use
187 <kbd>make E1000_DEBUG=tx,txerr ...
</kbd>.
</p>
190 Note that
<tt>E1000_DEBUG
</tt> only works in the
6.828 version of QEMU.
194 You can take debugging using software emulated hardware one step
195 further. If you are ever stuck and do not understand why the E1000 is
196 not responding the way you would expect, you can look at QEMU's E1000
197 implementation in
<tt>hw/e1000.c
</tt>.
200 <h2>The Network Server
</h2>
203 Writing a network stack from scratch is hard work. Instead, we will be using
204 lwIP, an open source lightweight TCP/IP protocol suite that among many things
205 includes a network stack. You can find more information on lwIP
206 <a href=
"http://www.sics.se/~adam/lwip/">here
</a>. In this assignment, as far
207 as we are concerned, lwIP is a black box that implements a BSD socket interface
208 and has a packet input port and packet output port.
212 The network server is actually a combination of four environments:
216 <li> core network server environment (includes socket call dispatcher and lwIP)
</li>
217 <li> input environment
</li>
218 <li> output environment
</li>
219 <li> timer environment
</li>
223 The following diagram shows the different environments and their relationships.
224 The diagram shows the entire system including the device driver, which will be
225 covered later. In this lab, you will implement the parts highlighted
229 <center><img src=
"ns.png" alt=
"Network server architecture" /></center>
231 <h3>The Core Network Server Environment
</h3>
234 The core network server environment is composed of the socket call dispatcher
235 and lwIP itself. The socket call dispatcher works exactly like the file server.
236 User environments use stubs (found in
<tt>lib/nsipc.c
</tt>) to send IPC
237 messages to the core network environment. If you look at
238 <tt>lib/nsipc.c
</tt> you will see that we find the core network server
239 the same way we found the file server:
<code>i386_init
</code> created
240 the NS environment with NS_TYPE_NS, so we scan
<code>envs
</code>,
241 looking for this special environment type.
242 For each user environment IPC, the dispatcher in the network server
243 calls the appropriate BSD socket
244 interface function provided by lwIP on behalf of the user.
248 Regular user environments do not use the
<code>nsipc_*
</code> calls
249 directly. Instead, they use the functions in
<tt>lib/sockets.c
</tt>,
250 which provides a file descriptor-based sockets API. Thus, user
251 environments refer to sockets via file descriptors, just
252 like how they referred to on-disk files. A number of operations
253 (
<code>connect
</code>,
<code>accept
</code>, etc.) are specific to
254 sockets, but
<code>read
</code>,
<code>write
</code>, and
255 <code>close
</code> go through the normal file descriptor
256 device-dispatch code in
<tt>lib/fd.c
</tt>. Much like how the file
257 server maintained internal unique ID's for all open files, lwIP also
258 generates unique ID's for all open sockets. In both the file server
259 and the network server, we
260 use information stored in
<code>struct Fd
</code> to map
261 per-environment file descriptors to these unique ID spaces.
265 Even though it may seem that the IPC dispatchers of the file server and network
266 server act the same, there is a key difference. BSD socket calls like
267 <code>accept
</code> and
<code>recv
</code> can block indefinitely. If the
268 dispatcher were to let lwIP execute one of these blocking calls, the dispatcher
269 would also block and there could only be one outstanding network call
270 at a time for the whole system. Since this is unacceptable, the network
271 server uses user-level threading to avoid blocking the entire server
272 environment. For every incoming IPC message, the dispatcher creates a
273 thread and processes the request in the newly created thread. If the thread
274 blocks, then only that thread is put to sleep while other threads continue
279 In addition to the core network environment there are three helper
280 environments. Besides accepting messages from user applications, the
281 core network environment's dispatcher also accepts messages from the
282 input and timer environments.
285 <h3>The Output Environment
</h3>
288 When servicing user environment socket calls, lwIP will generate packets for
289 the network card to transmit. LwIP will send each packet to be transmitted to
290 the output helper environment using the
<code>NSREQ_OUTPUT
</code> IPC message
291 with the packet attached in the page argument of the IPC message. The output
292 environment is responsible for accepting
293 these messages and forwarding the packet on to the device driver via the
294 system call interface that you will soon create.
297 <h3>The Input Environment
</h3>
300 Packets received by the network card
301 need to be injected into lwIP. For every packet received by the device driver,
302 the input environment pulls the packet out of kernel space (using kernel
303 system calls that you will implement) and sends the packet to the core
304 server environment using the
<code>NSREQ_INPUT
</code> IPC message.
308 The packet input functionality is separated from the core network environment
309 because JOS makes it hard to simultaneous accept IPC messages and poll or wait
310 for a packet from the device driver. We do not have a
<code>select
</code>
311 system call in JOS that would allow environments to monitor multiple input
312 sources to identify which input is ready to be processed.
316 If you take a look at
<tt>net/input.c
</tt> and
<tt>net/output.c
</tt> you
317 will see that both need to be implemented. This is mainly because the
318 implementation depends on your system call interface. You will write the code
319 for the two helper environments after you implement the driver and system call
323 <h3>The Timer Environment
</h3>
327 environment periodically sends messages of type
<code>NSREQ_TIMER
</code> to the
328 core network server notifying it that a timer has expired. The timer messages
329 from this thread are used by lwIP to implement various network timeouts.
332 <h1>Part A: Initialization and transmitting packets
</h1>
335 Your kernel does not have a notion of time, so we need to add
337 currently a clock interrupt that is generated by the hardware every
10ms. On
338 every clock interrupt we can increment a variable to indicate that time has
339 advanced by
10ms. This is implemented in
<tt>kern/time.c
</tt>, but is not
340 yet fully integrated into your kernel.
343 <div class=
"required">
344 <p><span class=
"header">Exercise
1.
</span>
345 Add a call to
<code>time_tick
</code> for every clock interrupt in
346 <tt>kern/trap.c
</tt>. Implement
<code>sys_time_msec
</code> and add it
347 to
<code>syscall
</code> in
<tt>kern/syscall.c
</tt> so that user space has
352 Use
<kbd>make INIT_CFLAGS=-DTEST_NO_NS run-testtime
</kbd> to test your time
354 should see the environment count down from
5 in
1 second intervals.
355 The
"-DTEST_NO_NS" disables starting the network server environment
356 because it will panic at this point in the lab.
359 <h2>The Network Interface Card
</h2>
362 Writing a driver requires knowing in depth the hardware and the interface
363 presented to the software. The lab text will provide a high-level
364 overview of how to interface with the E1000, but you'll need to make
365 extensive use of Intel's manual while writing your driver.
368 <div class=
"required">
369 <p><span class=
"header">Exercise
2.
</span> Browse Intel's
<a
370 href=
"../../readings/hardware/8254x_GBe_SDM.pdf">Software Developer's
371 Manual
</a> for the E1000. This manual covers several closely related
372 Ethernet controllers. QEMU emulates the
82540EM.
</p>
374 <p>You should skim over chapter
2 now to get a feel for the device.
375 To write your driver, you'll need to be familiar with chapters
3 and
376 14, as well as
4.1 (though not
4.1's subsections). You'll also need
377 to use chapter
13 as reference. The other chapters mostly cover
378 components of the E1000 that your driver won't have to interact
379 with. Don't worry about the details right now; just get a feel for
380 how the document is structured so you can find things later.
</p>
382 <p>While reading the manual, keep in mind that the E1000 is a
383 sophisticated device with many advanced features. A working E1000
384 driver only needs a fraction of the features and interfaces that the
385 NIC provides. Think carefully about the easiest way to interface
386 with the card. We strongly recommend that you get a basic driver
387 working before taking advantage of the advanced features.
</p>
390 <h3>PCI Interface
</h3>
393 The E1000 is a PCI device, which means it plugs into the PCI bus on
394 the motherboard. The PCI bus has address, data, and interrupt lines,
395 and allows the CPU to communicate with PCI devices and PCI devices
396 to read and write memory.
397 A PCI device needs to be discovered and
398 initialized before it can be used. Discovery is the process of walking the PCI
399 bus looking for attached devices. Initialization is the process of
400 allocating I/O and memory space as well as negotiating the IRQ line for the
405 We have provided you with PCI code in
<tt>kern/pci.c
</tt>.
406 To perform PCI initialization during boot, the PCI code walks the PCI
407 bus looking for devices. When it finds
408 a device, it reads its vendor ID and device ID and uses these two values as a key
409 to search the
<code>pci_attach_vendor
</code> array. The array is composed of
410 <code>struct pci_driver
</code> entries like this:
416 int (*attachfn) (struct pci_func *pcif);
421 If the discovered device's vendor ID and device ID match an entry in
422 the array, the PCI code calls that entry's
<tt>attachfn
</tt> to
423 perform device initialization.
424 (Devices can also be identified by class, which is
425 what the other driver table in
<tt>kern/pci.c
</tt> is for.)
429 The attach function is passed a
<i>PCI function
</i> to initialize. A
430 PCI card can expose multiple functions, though the E1000 exposes only
431 one. Here is how we represent a PCI function in JOS:
444 uint32_t reg_base[
6];
445 uint32_t reg_size[
6];
451 The above structure reflects some of the entries found in Table
452 4-
1 of Section
4.1 of the
453 developer manual. The last three entries of
454 <code>struct pci_func
</code> are of particular interest to us, as they
455 record the negotiated memory, I/O, and interrupt resources for the
457 The
<code>reg_base
</code> and
<code>reg_size
</code> arrays contain
458 information for up to six Base Address Registers or BARs.
459 <code>reg_base
</code> stores the base memory addresses for
460 memory-mapped I/O regions (or base I/O ports for I/O port resources),
461 <code>reg_size
</code> contains the size in bytes or number of I/O
462 ports for the corresponding base values from
<code>reg_base
</code>,
463 and
<code>irq_line
</code> contains the IRQ line
464 assigned to the device for interrupts. The specific meanings of the
465 E1000 BARs are given in the second half of table
4-
2.
469 When the attach function of a device is called, the
470 device has been found but not yet
<i>enabled
</i>. This means that the PCI code has
471 not yet determined the resources allocated to the device, such as address space and
472 an IRQ line, and, thus, the last three elements of the
<code>struct
473 pci_func
</code> structure are not yet filled in. The attach function should call
474 <code>pci_func_enable
</code>, which will enable the device,
475 negotiate these resources, and fill in the
<code>struct
479 <div class=
"required">
480 <p><span class=
"header">Exercise
3.
</span>
481 Implement an attach function to initialize the E1000. Add an entry to the
482 <code>pci_attach_vendor
</code> array in
<tt>kern/pci.c
</tt> to trigger
483 your function if a matching PCI device is found (be sure to put it
484 before the
<code>{
0,
0,
0}
</code> entry that mark the end of the
485 table). You can find the
486 vendor ID and device ID of the
82540EM that QEMU emulates in section
488 should also see these listed when JOS scans the PCI bus while booting.
491 For now, just enable the E1000 device via
492 <code>pci_func_enable
</code>. We'll add more initialization
496 We have provided the
<tt>kern/e1000.c
</tt> and
497 <tt>kern/e1000.h
</tt> files for you so that you do not need to mess with
498 the build system. You may still need to include the
<tt>e1000.h
</tt> file in
499 other places in the kernel.
502 When you boot your kernel, you should see it print that the PCI
503 function of the E1000 card was enabled. Your code should now pass the
504 <tt>pci attach
</tt> test of
<kbd>make grade
</kbd>.
508 <h3>Memory-mapped I/O
</h3>
511 Software communicates with the E1000 via
<i>memory-mapped I/O
</i>
512 (MMIO). You've seen this twice before in JOS: both the CGA console
513 and the LAPIC are devices that you control and query by writing to
514 and reading from
"memory". But these reads and writes don't go to
515 DRAM; they go directly to these devices.
519 <code>pci_func_enable
</code> negotiates an MMIO region with the
520 E1000 and stores its base and size in BAR
0 (that is,
521 <code>reg_base[
0]
</code> and
<code>reg_size[
0]
</code>). This is a
522 range of
<i>physical memory addresses
</i> assigned to the device,
523 which means you'll have to do something to access it via virtual
524 addresses. Since MMIO regions are assigned very high physical
525 addresses (typically above
3GB), you can't use
<code>KADDR
</code> to
526 access it because of JOS's
256MB limit. Thus, you'll have to create
527 a new memory mapping. It's up to you where to put this mapping.
528 BAR
0 is smaller than
4MB, so you could use the gap between
529 KSTACKTOP and KERNBASE; or you could map it well above KERNBASE (but
530 don't overwrite the mapping used by the LAPIC). Since PCI device
531 initialization happens before JOS creates user environments, you can
532 create the mapping in
<code>kern_pgdir
</code> and it will always be
536 <div class=
"required">
537 <p><span class=
"header">Exercise
4.
</span> In your attach function,
538 create a virtual memory mapping for the E1000's BAR
0. Since this
539 is device memory and not regular DRAM, you'll have to tell the CPU
540 that it isn't safe to cache access to this memory. Luckily, the
541 page tables provide bits for this purpose; simply create the mapping
542 with PTE_PCD|PTE_PWT (cache-disable and write-through). (If you're
543 interested in more details on this, see section
10.5 of IA32 volume
546 <p>You'll want to record where you made this mapping in a variable
547 so you can later access the registers you just mapped. Take a look
548 at the
<code>lapic
</code> variable in
<tt>kern/lapic.c
</tt> for an
549 example of one way to do this. If you do use a pointer to the
550 device register mapping, be sure to declare it
551 <code>volatile
</code>; otherwise, the compiler is allowed to cache
552 values and reorder accesses to this memory.
</p>
554 <p>To test your mapping, try printing out the device status register
555 (section
13.4.2). This is a
4 byte register that starts at byte
8
556 of the register space. You should get
<tt>0x80080783</tt>, which
557 indicates a full duplex link is up at
1000 MB/s, among other
562 Hint: You'll need a lot of constants, like the locations of
563 registers and values of bit masks. Trying to copy these out of the
564 developer's manual is error-prone and mistakes can lead to painful
565 debugging sessions. We recommend instead using QEMU's
<a
566 href=
"e1000_hw.h"><tt>e1000_hw.h
</tt></a> header as a guideline. We
567 don't recommend copying it in verbatim, because it defines far more
568 than you actually need and may not define things in the way you
569 need, but it's a good starting point.
575 You could imagine transmitting and receiving packets by writing and
576 reading from the E1000's registers, but this would be slow and would
577 require the E1000 to buffer packet data internally. Instead, the
578 E1000 uses
<i>Direct Memory Access
</i> or DMA to read and write
579 packet data directly from memory without involving the CPU. The
580 driver is responsible for allocating memory for the transmit and
581 receive queues, setting up DMA descriptors, and configuring the
582 E1000 with the location of these queues, but everything after that
583 is asynchronous. To transmit a packet, the driver copies it into
584 the next DMA descriptor in the transmit queue and informs the E1000
585 that another packet is available; the E1000 will copy the data out
586 of the descriptor when there is time to send the packet. Likewise,
587 when the E1000 receives a packet, it copies it into the next DMA
588 descriptor in the receive queue, which the driver can read from at
589 its next opportunity.
593 The receive and transmit queues are very similar at a high level.
594 Both consist of a sequence of
<i>descriptors
</i>. While the exact
595 structure of these descriptors varies, each descriptor contains some
596 flags and the physical address of a buffer containing packet data
597 (either packet data for the card to send, or a buffer allocated by
598 the OS for the card to write a received packet to).
601 <!-- XXX Figure of a descriptor? -->
604 The queues are implemented as circular arrays, meaning that when the
605 card or the driver reach the end of the array, it wraps back around
606 to the beginning. Both have a
<i>head pointer
</i> and a
<i>tail
607 pointer
</i> and the contents of the queue are the descriptors
608 between these two pointers. The hardware always consumes
609 descriptors from the head and moves the head pointer, while the
610 driver always add descriptors to the tail and moves the tail
611 pointer. The descriptors in the transmit queue represent packets
612 waiting to be sent (hence, in the steady state, the transmit queue
613 is empty). For the receive queue, the descriptors in the queue are
614 free descriptors that the card can receive packets into (hence, in
615 the steady state, the receive queue consists of all available
616 receive descriptors).
619 <!-- XXX Figure? Cite sections 3.2.6 and section 3.4? -->
622 The pointers to these arrays as well as the addresses of the packet
623 buffers in the descriptors must all be
<i>physical addresses
</i>
624 because hardware performs DMA directly to and from physical RAM
625 without going through the MMU.
628 <h2>Transmitting Packets
</h2>
631 The transmit and receive functions of the E1000 are basically
632 independent of each other, so we can work on one at a time. We'll
633 attack transmitting packets first simply because we can't test
634 receive without transmitting an
"I'm here!" packet first.
638 First, you'll have to initialize the card to transmit, following the
639 steps described in section
14.5 (you don't have to worry about the
640 subsections). The first step of transmit initialization is setting
641 up the transmit queue. The precise structure of the queue is
642 described in section
3.4 and the structure of the descriptors is
643 described in section
3.3.3. We won't be using the TCP offload
644 features of the E1000, so you can focus on the
"legacy transmit
645 descriptor format." You should read those sections now and
646 familiarize yourself with these structures.
649 <h3>C Structures
</h3>
652 You'll find it convenient to use C
<code>struct
</code>s to describe
653 the E1000's structures. As you've seen with structures like the
654 <code>struct Trapframe
</code>, C
<code>struct
</code>s let you
655 precisely layout data in memory. C can insert padding between
656 fields, but the E1000's structures are laid out such that this
657 shouldn't be a problem. If you do encounter field alignment
658 problems, look into GCC's
"packed" attribute.
662 As an example, consider the legacy transmit descriptor given in
663 table
3-
8 of the manual and reproduced here:
666 <pre style=
"text-align: center">
667 63 48 47 40 39 32 31 24 23 16 15 0
668 +---------------------------------------------------------------+
670 +---------------+-------+-------+-------+-------+---------------+
671 | Special | CSS | Status| Cmd | CSO | Length |
672 +---------------+-------+-------+-------+-------+---------------+
676 The first byte of the structure starts at the top right, so to
677 convert this into a C struct, read from right to left, top to
678 bottom. If you squint at it right, you'll see that all of the
679 fields even fit nicely into a standard-size types:
696 Your driver will have to reserve memory for the transmit descriptor
697 array and the packet buffers pointed to by the transmit descriptors.
698 There are several ways to do this, ranging from dynamically
699 allocating pages to simply declaring them in global variables.
700 Whatever you choose, keep in mind that the E1000 accesses physical
701 memory directly, which means any buffer it accesses must be
702 contiguous in physical memory.
706 There are also multiple ways to handle the packet buffers. The
707 simplest, which we recommend starting with, is to reserve space for
708 a packet buffer for each descriptor during driver initialization and
709 simply copy packet data into and out of these pre-allocated buffers.
710 The maximum size of an Ethernet packet is
1518 bytes, which bounds
711 how big these buffers need to be. More sophisticated drivers could
712 dynamically allocate packet buffers (e.g., to reduce memory overhead
713 when network usage is low) or even pass buffers directly provided by
714 user space (a technique known as
"zero copy"), but it's good to
718 <div class=
"required">
719 <p><span class=
"header">Exercise
5.
</span> Perform the
720 initialization steps described in section
14.5 (but not its
721 subsections). Use section
13 as a reference for the registers the
722 initialization process refers to and sections
3.3.3 and
3.4 for
723 reference to the transmit descriptors and transmit descriptor
726 <p>Be mindful of the alignment requirements on the transmit
727 descriptor array and the restrictions on length of this array.
728 Since TDLEN must be
128-byte aligned and each transmit descriptor is
729 16 bytes, your transmit descriptor array will need some multiple of
730 8 transmit descriptors. However, don't use more than
64 descriptors
731 or our tests won't be able to test transmit ring overflow.
</p>
733 <p>For the TCTL.COLD, you can assume full-duplex operation.
734 For TIPG, refer to the default values described in table
13-
77 of
735 section
13.4.34 for the IEEE
802.3 standard IPG (don't use the
736 values in the table in section
14.5).
</p>
740 Try running
<kbd>make E1000_DEBUG=TXERR,TX qemu
</kbd>. You should
741 see an
"e1000: tx disabled" message when you set the TDT register
742 (since this happens before you set TCTL.EN) and no further
"e1000"
747 Now that transmit is initialized, you'll have to write the code to
748 transmit a packet and make it accessible to user space via a system
749 call. To transmit a packet, you have to add it to the tail of the
750 transmit queue, which means copying the packet data into the next
751 packet buffer and then updating the TDT (transmit descriptor tail)
752 register to inform the card that there's another packet in the
753 transmit queue. (Note that TDT is an
<i>index
</i> into the transmit
754 descriptor array, not a byte offset; the documentation isn't very
759 However, the transmit queue is only so big. What happens if the
760 card has fallen behind transmitting packets and the transmit queue
761 is full? In order to detect this condition, you'll need some
762 feedback from the E1000. Unfortunately, you can't just use the TDH
763 (transmit descriptor head) register; the documentation explicitly
764 states that reading this register from software is unreliable.
765 However, if you set the RS bit in the command field of a transmit
766 descriptor, then, when the card has transmitted the packet in that
767 descriptor, the card will set the DD bit in the status field of the
768 descriptor. If a descriptor's DD bit is set, you know it's safe to
769 recycle that descriptor and use it to transmit another packet.
773 What if the user calls your transmit system call, but the DD bit of
774 the next descriptor isn't set, indicating that the transmit queue is
775 full? You'll have to decide what to do in this situation. You
776 could simply drop the packet. Network protocols are resilient to
777 this, but if you drop a large burst of packets, the protocol may not
778 recover. You could tell the user environment that it has to retry,
779 much like you did for
<code>sys_ipc_try_send
</code>. This has the
780 advantage of pushing back on the environment generating the data.
781 You could spin in the driver until a transmit descriptor frees up,
782 but this may introduce serious performance problems since the JOS
783 kernel isn't designed to block. Finally, you could put the
784 transmitting environment to sleep and request that the card send an
785 interrupt when transmit descriptors free up. We recommend that you
786 start with whatever you think is simplest.
789 <div class=
"required">
790 <p><span class=
"header">Exercise
6.
</span> Write a function to
791 transmit a packet by checking that the next descriptor is free,
792 copying the packet data into the next descriptor, and updating TDT.
793 Make sure you handle the transmit queue being full.
</p>
797 Now would be a good time to test your packet transmit code. Try
798 transmitting just a few packets by directly calling your transmit
799 function from the kernel. You don't have to create packets that
800 conform to any particular network protocol in order to test this.
801 Run
<kbd>make E1000_DEBUG=TXERR,TX qemu
</kbd> to run your test. You
802 should see something like
805 e1000: index
0:
0x271f00 :
9000002a
0
809 as you transmit packets. Each line gives the index in the transmit
810 array, the buffer address of that transmit descriptor, the
811 cmd/CSO/length fields, and the special/CSS/status fields.
812 If QEMU doesn't print the values you expected from your transmit
813 descriptor, check that you're filling in the right descriptor and
814 that you configured TDBAL and TDBAH correctly.
815 If you get
"e1000: TDH wraparound @0, TDT
816 x, TDLEN y" messages, that means the E1000 ran all the way through
817 the transmit queue without stopping (if QEMU didn't check this, it
818 would enter an infinite loop), which probably means you aren't
819 manipulating TDT correctly. If you get lots of
"e1000: tx disabled"
820 messages, then you didn't set the transmit control register right.
824 Once QEMU runs, you can then run
<kbd>tcpdump -XXnr qemu.pcap
</kbd>
825 to see the packet data that you transmitted. If you saw the
826 expected
"e1000: index" messages from QEMU, but your packet capture
827 is empty, double check that you filled in every necessary field and
828 bit in your transmit descriptors (the E1000 probably went through
829 your transmit descriptors, but didn't think it had to send
833 <div class=
"required">
834 <p><span class=
"header">Exercise
7.
</span> Add a system call that
835 lets you transmit packets from user space. The exact interface is
836 up to you. Don't forget to check any pointers passed to the kernel
840 <h2>Transmitting Packets: Network Server
</h2>
843 Now that you have a system call interface to the transmit side of your device
844 driver, it's time to send packets. The output helper environment's goal is to
845 accept
<code>NSREQ_OUTPUT
</code> IPC messages from the core network server and
846 send the packets accompanying these IPC message to the network device driver
847 using the system call you added above. The
<code>NSREQ_OUTPUT
</code>
848 IPC's are sent by the
<code>low_level_output
</code> function in
849 <tt>net/lwip/jos/jif/jif.c
</tt>, which glues the lwIP stack to JOS's
850 network system. Each IPC will include a page consisting of a
851 <code>union Nsipc
</code> with the packet in its
852 <code>struct jif_pkt pkt
</code> field (see
<tt>inc/ns.h
</tt>).
853 <code>struct jif_pkt
</code> looks like
863 <code>jp_len
</code> represents the length of the packet. All
864 subsequent bytes on the IPC page are dedicated to the packet contents.
865 Using a zero-length array like
<code>jp_data
</code> at the end of a
866 struct is a common C trick (some would say abomination) for
867 representing buffers without pre-determined lengths. Since C doesn't
868 do array bounds checking, as long as you ensure there's enough unused
869 memory following the struct, you can use
<code>jp_data
</code> as if it
870 were an array of any size.
874 Be aware of the interaction between the device driver, the output environment
875 and the core network server when there is no more space in the device driver's
876 transmit queue. The core network server sends packets to the output environment
877 using IPC. If the output environment is suspended due to a send packet system
878 call because the driver has no more buffer space for new packets, the core
879 network server will block waiting for the output server to accept the IPC call.
882 <div class=
"required">
883 <p><span class=
"header">Exercise
8.
</span>
884 Implement
<tt>net/output.c
</tt>.
888 You can use
<tt>net/testoutput.c
</tt> to test your output code
889 without involving the whole network server. Try running
890 <kbd>make E1000_DEBUG=TXERR,TX run-net_testoutput
</kbd>. You should
894 Transmitting packet
0
895 e1000: index
0:
0x271f00 :
9000009 0
896 Transmitting packet
1
897 e1000: index
1:
0x2724ee :
9000009 0
901 and
<kbd>tcpdump -XXnr qemu.pcap
</kbd> should output
904 reading from file qemu.pcap, link-type EN10MB (Ethernet)
905 -
5:
00:
00.600186 [|ether]
906 0x0000:
5061 636b
6574 2030 30 Packet
.00
907 -
5:
00:
00.610080 [|ether]
908 0x0000:
5061 636b
6574 2030 31 Packet
.01
912 To test with a larger packet count, try
913 <kbd>make E1000_DEBUG=TXERR,TX
914 NET_CFLAGS=-DTESTOUTPUT_COUNT=
100 run-net_testoutput
</kbd>. If this
915 overflows your transmit ring, double check that you're handling the
916 DD status bit correctly and that you've told the hardware to set the
917 DD status bit (using the RS command bit).
921 Your code should pass the
<tt>testoutput
</tt> tests of
<kbd>make
925 <div class=
"question">
926 <p><span class=
"header">Question
</span></p>
929 How did you structure your transmit implementation? In
930 particular, what do you do if the transmit ring is full?
935 <h1>Part B: Receiving packets and the web server
</h1>
937 <h2>Receiving Packets
</h2>
940 Just like you did for transmitting packets, you'll have to configure
941 the E1000 to receive packets and provide a receive descriptor queue
942 and receive descriptors. Section
3.2 describes how packet reception
943 works, including the receive queue structure and receive
944 descriptors, and the initialization process is detailed in section
948 <div class=
"required">
949 <p><span class=
"header">Exercise
9.
</span> Read section
3.2. You
950 can ignore anything about interrupts and checksum offloading (you
951 can return to these sections if you decide to use these features
952 later), and you don't have to be concerned with the details of
953 thresholds and how the card's internal caches work.
</p>
957 The receive queue is very similar to the transmit queue, except that
958 it consists of empty packet buffers waiting to be filled with
959 incoming packets. Hence, when the network is idle, the transmit
960 queue is empty (because all packets have been sent), but the receive
961 queue is full (of empty packet buffers).
965 When the E1000 receives a packet, it first checks if it matches the
966 card's configured filters (for example, to see if the packet is
967 addressed to this E1000's MAC address) and ignores the packet if it
968 doesn't match any filters. Otherwise, the E1000 tries to retrieve
969 the next receive descriptor from the head of the receive queue. If
970 the head (RDH) has caught up with the tail (RDT), then the receive
971 queue is out of free descriptors, so the card drops the packet. If
972 there is a free receive descriptor, it copies the packet data into
973 the buffer pointed to by the descriptor, sets the descriptor's DD
974 (Descriptor Done) and EOP (End of Packet) status bits, and
979 If the E1000 receives a packet that is larger than the packet buffer
980 in one receive descriptor, it will retrieve as many descriptors as
981 necessary from the receive queue to store the entire contents of the
982 packet. To indicate that this has happened, it will set the DD
983 status bit on all of these descriptors, but only set the EOP status
984 bit on the last of these descriptors. You can either deal with this
985 possibility in your driver, or simply configure the card to not
986 accept
"long packets" (also known as
<i>jumbo frames
</i>) and make
987 sure your receive buffers are large enough to store the largest
988 possible standard Ethernet packet (
1518 bytes).
991 <div class=
"required">
992 <p><span class=
"header">Exercise
10.
</span> Set up the receive queue
993 and configure the E1000 by following the process in section
14.4.
994 You don't have to support
"long packets" or multicast. For now,
995 don't configure the card to use interrupts; you can change that
996 later if you decide to use receive interrupts. Also, configure the
997 E1000 to strip the Ethernet CRC, since the grade script expects it
1000 <p>By default, the card will filter out
<i>all
</i> packets. You
1001 have to configure the Receive Address Registers (RAL and RAH) with
1002 the card's own MAC address in order to accept packets addressed to
1003 that card. You can simply hard-code QEMU's default MAC address of
1004 52:
54:
00:
12:
34:
56 (we already hard-code this in lwIP, so doing it
1005 here too doesn't make things any worse). Be very careful with the
1006 byte order; MAC addresses are written from lowest-order byte to
1007 highest-order byte, so
52:
54:
00:
12 are the low-order
32 bits of the
1008 MAC address and
34:
56 are the high-order
16 bits.
</p>
1010 <p>The E1000 only supports a specific set of receive buffer sizes
1011 (given in the description of RCTL.BSIZE in
13.4.22). If you make
1012 your receive packet buffers large enough and disable long packets,
1013 you won't have to worry about packets spanning multiple receive
1014 buffers. Also, remember that, just like for transmit, the receive
1015 queue and the packet buffers must be contiguous in physical
1020 You can do a basic test of receive functionality now, even without
1021 writing the code to receive packets. Run
1022 <kbd>make E1000_DEBUG=TX,TXERR,RX,RXERR,RXFILTER run-net_testinput
</kbd>.
1023 <tt>testinput
</tt> will transmit an ARP (Address Resolution
1024 Protocol) announcement packet, which QEMU will automatically reply
1025 to. Even though your driver can't receive this reply yet, you
1026 should see a
"e1000: unicast match[0]: 52:54:00:12:34:56" message,
1027 indicating that a packet was received by the E1000 and matched the
1028 configured receive filter. If you see a
"e1000: unicast mismatch:
1029 52:54:00:12:34:56" message instead, the E1000 filtered out the
1030 packet, which means you probably didn't configure RAL and RAH
1031 correctly. Make sure you got the byte ordering right and didn't
1032 forget to set the
"Address Valid" bit in RAH. If you don't get any
1033 "e1000" messages, you probably didn't enable receive correctly.
1037 Now you're ready to implement receiving packets. To receive a
1038 packet, your driver will have to keep track of which descriptor it
1039 expects to hold the next received packet (hint: depending on your
1040 design, there's probably already be a register in the E1000 keeping
1041 track of this). Similar to transmit, the documentation states that
1042 the RDH register cannot be reliably read from software, so in order
1043 to determine if a packet has been delivered to this descriptor's
1044 packet buffer, you'll have to read the DD status bit in the
1045 descriptor. If the DD bit is set, you can copy the packet data out
1046 of that descriptor's packet buffer and then tell the card that the
1047 descriptor is free by updating the queue's tail index, RDT.
1051 If the DD bit isn't set, then no packet has been received. This is
1052 the receive-side equivalent of when the transmit queue was full, and
1053 there are several things you can do in this situation. You can
1054 simply return a
"try again" error and require the caller to retry.
1055 While this approach works well for full transmit queues because
1056 that's a transient condition, it is less justifiable for empty
1057 receive queues because the receive queue may remain empty for long
1058 stretches of time. A second approach is to suspend the calling
1059 environment until there are packets in the receive queue to process.
1060 This tactic is very similar to
<code>sys_ipc_recv
</code>. Just like
1061 in the IPC case, since we have only one kernel stack per CPU, as
1062 soon as we leave the kernel the state on the stack will be lost. We
1063 need to set a flag indicating that an environment has been suspended
1064 by receive queue underflow and record the system call arguments.
1065 The drawback of this approach is complexity: the E1000 must be
1066 instructed to generate receive interrupts and the driver must handle
1067 them in order to resume the environment blocked waiting for a
1071 <div class=
"required">
1072 <p><span class=
"header">Exercise
11.
</span> Write a function to
1073 receive a packet from the E1000 and expose it to user space by
1074 adding a system call. Make sure you handle the receive queue being
1077 <!-- XXX Check this for the E1000 and the LAPIC -->
1078 <p>If you decide to use interrupts to detect when packets are
1079 received, you'll need to write code to handle these interrupts. If
1080 you do use interrupts, note that, once an interrupt is asserted, it
1081 will remain asserted until the driver clears the interrupt. In your
1082 interrupt handler make sure to clear the interrupt handled as soon
1083 as you handle it. If you don't, after returning from your interrupt
1084 handler, the CPU will jump back into it again. In addition to
1085 clearing the interrupts on the E1000 card, interrupts also need to
1086 be cleared on the LAPIC. Use
<code>lapic_eoi
</code> to do so.
</p>
1089 <h2>Receiving Packets: Network Server
</h2>
1092 In the network server input environment, you will need to use your new
1093 receive system call to receive packets and pass them to the core
1094 network server environment using the
<code>NSREQ_INPUT
</code> IPC
1095 message. These IPC input message should have a page
1096 attached with a
<code>union Nsipc
</code> with its
<code>struct jif_pkt
1097 pkt
</code> field filled in with the packet received from the network.
1100 <div class=
"required">
1101 <p><span class=
"header">Exercise
12.
</span>
1102 Implement
<tt>net/input.c
</tt>.
1106 Run
<tt>testinput
</tt> again with
<kbd>make E1000_DEBUG=TX,TXERR,RX,RXERR,RXFILTER
1107 run-net_testinput
</kbd>. You should see
1110 Sending ARP announcement...
1111 Waiting for packets...
1112 e1000: index
0:
0x26dea0 :
900002a
0
1113 e1000: unicast match[
0]:
52:
54:
00:
12:
34:
56
1114 input:
0000 5254 0012 3456 5255 0a00
0202 0806 0001
1115 input:
0010 0800 0604 0002 5255 0a00
0202 0a00
0202
1116 input:
0020 5254 0012 3456 0a00
020f
0000 0000 0000
1117 input:
0030 0000 0000 0000 0000 0000 0000 0000 0000
1120 The lines beginning with
"input:" are a hexdump of QEMU's ARP reply.
1124 Your code should pass the
<tt>testinput
</tt> tests of
<kbd>make
1125 grade
</kbd>. Note that there's no way to test packet receiving
1126 without sending at least one ARP packet to inform QEMU of JOS' IP
1127 address, so bugs in your transmitting code can cause this test to
1132 To more thoroughly test your networking code, we have provided a daemon called
1133 <tt>echosrv
</tt> that sets up an echo server running on port
7
1134 that will echo back anything sent over a TCP connection. Use
1135 <kbd>make E1000_DEBUG=TX,TXERR,RX,RXERR,RXFILTER run-echosrv
</kbd> to
1136 start the echo server in one terminal and
<kbd>make nc-
7</kbd> in
1137 another to connect to it. Every line you type should be echoed back
1139 Every time the emulated E1000 receives a packet, QEMU should print
1140 something like the following to the console:
1143 e1000: unicast match[
0]:
52:
54:
00:
12:
34:
56
1144 e1000: index
2:
0x26ea7c :
9000036 0
1145 e1000: index
3:
0x26f06a :
9000039 0
1146 e1000: unicast match[
0]:
52:
54:
00:
12:
34:
56
1150 At this point, you should also be able to pass the
<tt>echosrv
</tt>
1154 <div class=
"question">
1155 <p><span class=
"header">Question
</span></p>
1158 How did you structure your receive implementation? In particular,
1159 what do you do if the receive queue is empty and a user environment
1160 requests the next incoming packet?
1165 <!-- (amdragon: QEMU only has minimal offload support)
1166 <div class="challenge">
1167 <p><span class="header">Challenge!</span> Take advantage of the
1168 E1000's TCP offload features. This lets the E1000 take care of some
1169 of the more computationally-intensive parts of TCP processing like
1170 computing checksums before transmitting and checking checksums of
1171 received packets. You'll have to configure the card to enable TCP
1172 offload and modify lwIP so it doesn't do this work internally.</p>
1176 <div class=
"challenge">
1177 <p><span class=
"header">Challenge!
</span>
1178 Read about the EEPROM in the developer's manual and write the code to
1179 load the E1000's MAC address out of the EEPROM. Currently,
1180 QEMU's default MAC address is hard-coded into both your
1181 receive initialization and lwIP. Fix your initialization to
1182 use the MAC address you read from the EEPROM, add a
1183 system call to pass the MAC address to lwIP, and modify lwIP to
1184 the MAC address read from the card. Test your change by
1185 configuring QEMU to use a different MAC address.
1188 <div class=
"challenge">
1189 <p><span class=
"header">Challenge!
</span> Modify your E1000 driver
1190 to be
"zero copy." Currently, packet data has to be copied from
1191 user-space buffers to transmit packet buffers and from receive
1192 packet buffers back to user-space buffers. A zero copy driver
1193 avoids this by having user space and the E1000 share packet buffer
1194 memory directly. There are many different approaches to this,
1195 including mapping the kernel-allocated structures into user space or
1196 passing user-provided buffers directly to the E1000. Regardless of
1197 your approach, be careful how you reuse buffers so that you don't
1198 introduce races between user-space code and the E1000.
</p>
1201 <div class=
"challenge">
1202 <p><span class=
"header">Challenge!
</span>
1203 Take the zero copy concept all the way into lwIP.
1207 A typical packet is composed of many headers. The user sends data to be
1208 transmitted to lwIP in one buffer. The TCP layer wants to add a TCP
1209 header, the IP layer an IP header and the MAC layer an Ethernet header.
1210 Even though there are many parts to a packet, right now the parts need
1211 to be joined together so that the device driver can send the final
1216 The E1000's transmit descriptor design is well-suited to
1217 collecting pieces of a packet scattered throughout memory,
1218 like the packet fragments created inside lwIP. If you enqueue
1219 multiple transmit descriptors, but only set the EOP command
1220 bit on the last one, then the E1000 will internally
1221 concatenate the packet buffers from these descriptors and only
1222 transmit the concatenated buffer when it reaches the
1223 EOP-marked descriptor. As a result, the individual packet
1224 pieces never need to be joined together in memory.
1228 Change your driver to be able to send packets composed of many
1229 buffers without copying and modify lwIP to avoid merging the
1230 packet pieces as it does right now.
1234 <div class=
"challenge">
1235 <p><span class=
"header">Challenge!
</span>
1236 Augment your system call interface to service more than one user
1237 environment. This will prove useful if there are multiple network
1238 stacks (and multiple network servers) each with their own IP address
1239 running in user mode. The receive system call will need to decide to
1240 which environment it needs to forward each incoming packet.
1244 Note that the current interface cannot tell the difference between two
1245 packets and if multiple environments call the packet receive system
1246 call, each respective environment will get a subset of the incoming
1247 packets and that subset may include packets that are not destined to
1248 the calling environment.
1252 Sections
2.2 and
3 in
1253 <a href=
"http://pdos.csail.mit.edu/papers/exo:tocs.pdf">this
</a>
1254 Exokernel paper have an in-depth explanation of the problem
1255 and a method of addressing it in a kernel like JOS. Use the paper to
1256 help you get a grip on the problem, chances are you do not need a
1257 solution as complex as presented in the paper.
1261 <h2>The Web Server
</h2>
1264 A web server in its simplest form sends the contents of a file to the
1265 requesting client. We have provided skeleton code for a very simple web server
1266 in
<tt>user/httpd.c
</tt>. The skeleton code deals with incoming connections
1267 and parses the headers.
1270 <div class=
"required">
1271 <p><span class=
"header">Exercise
13.
</span>
1272 The web server is missing the code that deals with sending the
1273 contents of a file back to the client. Finish the web server by
1274 implementing
<code>send_file
</code> and
<code>send_data
</code>.
1278 Once you've finished the web server, point your favorite browser at
1279 http://
<i>host
</i>:
<i>port
</i>/index.html, where
<i>host
</i> is the
1280 name of the computer running QEMU (
<tt>linerva.mit.edu
</tt> if you're
1281 running QEMU on Linerva, or
<tt>localhost
</tt> if you're running the
1282 web browser and QEMU on the same computer) and
<i>port
</i> is the port
1283 number reported for the web server by
<kbd>make which-ports
</kbd>.
1284 You should see a web page served by the HTTP server running inside
1289 At this point, you should score
105/
105 on
<kbd>make grade
</kbd>.
1292 <div class=
"challenge">
1293 <p><span class=
"header">Challenge!
</span>
1295 Add a simple chat server to JOS, where multiple people can
1296 connect to the server and anything that any user types is
1297 transmitted to the other users. To do this, you will have to
1298 find a way to communicate with multiple sockets at once
1299 <i>and
</i> to send and receive on the same socket at the same
1300 time. There are multiple ways to go about this. lwIP
1301 provides a MSG_DONTWAIT flag for recv (see
1302 <tt>lwip_recvfrom
</tt> in
<tt>net/lwip/api/sockets.c
</tt>), so
1303 you could constantly loop through all open sockets, polling
1304 them for data. Note that, while
<tt>recv
</tt> flags are
1305 supported by the network server IPC, they aren't accessible
1306 via the regular
<tt>read
</tt> function, so you'll need a way
1307 to pass the flags. A more efficient approach is to start one
1308 or more environments for each connection
1309 and to use IPC to coordinate them. Conveniently, the lwIP
1310 socket ID found in the struct Fd for a socket is global (not
1311 per-environment), so, for example, the child of a
1312 <tt>fork
</tt> inherits its parents sockets. Or, an
1313 environment can even send on another environment's socket simply by
1314 constructing an Fd containing the right socket ID.
1319 <div class=
"question">
1320 <p><span class=
"header">Question
</span></p>
1322 <li>What does the web page served by JOS's web server say?
</li>
1324 How long approximately did it take you to do this lab?
1330 <b>This completes the lab.
</b>
1331 As usual, don't forget to run
<kbd>make grade
</kbd> and to write up
1332 your answers and a description of your challenge exercise solution.
1333 Before handing in, use
<kbd>git status
</kbd> and
<kbd>git diff
</kbd>
1334 to examine your changes and don't forget to
<kbd>git add
1335 answers-lab6.txt
</kbd>. When you're ready, commit your changes with
1336 <kbd>git commit -am 'my solutions to lab
6'
</kbd>, then
<kbd>make
1337 handin
</kbd> and follow the directions.
1342 <!-- LocalWords: IPC exokernel lib inc pingpong forktree Bochs inode inodes
1344 <!-- LocalWords: granularities Superblocks superblocks superblock IDE PIO rm
1346 <!-- LocalWords: IOPL EFLAGS obj gmake AMD's Athlon VM fd alloc FDTABLE MAXFD
1348 <!-- LocalWords: FILEBASE unmap bss libos foo umain int argc argv cputs ptr
1350 <!-- LocalWords: USTACKTOP esp arg init TMPPAGE TMPPAGETOP libmain env's