Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
9acc7c80f7 | |||
721a113c93 | |||
86c4aa03ed | |||
f1980d32ca | |||
46e7d21fbf | |||
d943a2be37 | |||
1e5c0639ee | |||
34e9433d15 | |||
9bec3d83bd | |||
0289ec3b3e | |||
46cc5c9478 | |||
4e4de7b836 | |||
03b296f7e1 | |||
74b1c2c69d | |||
3449b8c566 | |||
7d76204053 | |||
c3e2a92b08 | |||
0fe97e52c3 | |||
8cd1daf8dd | |||
113093f919 | |||
94b52c5730 | |||
9231075799 | |||
f4e3196494 | |||
f0e2ab8abd | |||
eae0475758 | |||
bbdb8bd811 | |||
6ab267bb9b | |||
667f2df4cc | |||
8120dfde65 | |||
73dad8b484 | |||
c07415cffc | |||
e1d239139a | |||
e484704ce8 | |||
60ee3619af | |||
ca51596893 | |||
d7a96eb60f | |||
|
7d3fde5f77 | ||
|
023f974e4c | ||
|
0b89b560f3 | ||
|
187496eb19 | ||
|
f1840b8b11 | ||
|
a37b5511d0 | ||
|
c56b8ebcbd | ||
|
2dc0ccbbdc | ||
|
0ce94c7ca2 | ||
|
815502c0cc | ||
|
da1f8392b1 | ||
|
a9d7717cc4 | ||
|
2d1187aa3c |
25
GNUmakefile
25
GNUmakefile
|
@ -67,6 +67,7 @@ endif
|
|||
GDBPORT := $(shell expr `id -u` % 5000 + 25000)
|
||||
|
||||
CC := $(GCCPREFIX)gcc -pipe
|
||||
GDB := $(GCCPREFIX)gdb
|
||||
AS := $(GCCPREFIX)as
|
||||
AR := $(GCCPREFIX)ar
|
||||
LD := $(GCCPREFIX)ld
|
||||
|
@ -87,6 +88,7 @@ CFLAGS := $(CFLAGS) $(DEFS) $(LABDEFS) -O1 -fno-builtin -I$(TOP) -MD
|
|||
CFLAGS += -fno-omit-frame-pointer
|
||||
CFLAGS += -std=gnu99
|
||||
CFLAGS += -static
|
||||
CFLAGS += -fno-pie
|
||||
CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32
|
||||
# -fno-tree-ch prevented gcc from sometimes reordering read_ebp() before
|
||||
# mon_backtrace()'s function prologue on gcc version: (Debian 4.7.2-5) 4.7.2
|
||||
|
@ -137,18 +139,23 @@ $(OBJDIR)/.vars.%: FORCE
|
|||
# Include Makefrags for subdirectories
|
||||
include boot/Makefrag
|
||||
include kern/Makefrag
|
||||
include lib/Makefrag
|
||||
include user/Makefrag
|
||||
|
||||
|
||||
CPUS ?= 1
|
||||
|
||||
QEMUOPTS = -drive file=$(OBJDIR)/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdb tcp::$(GDBPORT)
|
||||
QEMUOPTS += $(shell if $(QEMU) -nographic -help | grep -q '^-D '; then echo '-D qemu.log'; fi)
|
||||
IMAGES = $(OBJDIR)/kern/kernel.img
|
||||
QEMUOPTS += -smp $(CPUS)
|
||||
QEMUOPTS += $(QEMUEXTRA)
|
||||
|
||||
.gdbinit: .gdbinit.tmpl
|
||||
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
|
||||
|
||||
gdb:
|
||||
gdb -n -x .gdbinit
|
||||
$(GDB) -n -x .gdbinit
|
||||
|
||||
pre-qemu: .gdbinit
|
||||
|
||||
|
@ -297,6 +304,22 @@ myapi.key:
|
|||
#handin-prep:
|
||||
# @./handin-prep
|
||||
|
||||
# For test runs
|
||||
|
||||
prep-%:
|
||||
$(V)$(MAKE) "INIT_CFLAGS=${INIT_CFLAGS} -DTEST=`case $* in *_*) echo $*;; *) echo user_$*;; esac`" $(IMAGES)
|
||||
|
||||
run-%-nox-gdb: prep-% pre-qemu
|
||||
$(QEMU) -nographic $(QEMUOPTS) -S
|
||||
|
||||
run-%-gdb: prep-% pre-qemu
|
||||
$(QEMU) $(QEMUOPTS) -S
|
||||
|
||||
run-%-nox: prep-% pre-qemu
|
||||
$(QEMU) -nographic $(QEMUOPTS)
|
||||
|
||||
run-%: prep-% pre-qemu
|
||||
$(QEMU) $(QEMUOPTS)
|
||||
|
||||
# This magic automatically generates makefile dependencies
|
||||
# for header files included from C source files we compile,
|
||||
|
|
22
answers-lab2.txt
Normal file
22
answers-lab2.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
1. The entry given by PDX(UVPT) is mapped to the page directory itself,
|
||||
to allow programs to read existing memory mappings. The entry at PDX(UPAGES)
|
||||
is mapped to the pages variable in memory so that the kernel (and potentially other ring 0 programs) can access it. The entry pointed to by PDX(KSTACKTOP-KSTACKSIZE) is mapped
|
||||
to the bootstack location. Finally, both the memory pointed to by 0 and PDX(KERNBASE) are mapped to kernel memory. However, the mappings at VA 0 are read-only, so user programs can't touch them, while the mappings at PDX(KERNBASE) are kernel-private and RW.
|
||||
|
||||
A table could be as follows:
|
||||
|
||||
400 - Kernel memory
|
||||
... - Kernel memory
|
||||
3c0 - Kernel memory
|
||||
3bf - Kernel Stack
|
||||
3bd - UVPT
|
||||
3bc - UPAGES
|
||||
|
||||
|
||||
2. The kernel memory is mapped as kernel read write. This means there are several flags set in the kernel page directory and page table entries, which indicate that this is restricted memory. The lower 3 bits of the CS register will be checked when an access to this memory is made, and, if they are not 0 or 1 (indicating kernel code), the CPU generates a fault, and the user program gives up control back to the OS. Thus, unless a program is started in ring 0, it will not be able to read or write kernel memory, as it should.
|
||||
|
||||
3. The absolute maximum is 4GB, since we use 32-bit integers for referencing memory. Some of this memory is used for the kernel itself, as well as for the page directory and tables.
|
||||
|
||||
4. The page directory and the corresponding pages are all 4kb. The page directory can have 1024 entries, and each of these point to a page table. Thus, we use approximatey 4MB (slightly more than that, actually, due to the size of the page directory itself) of memory. Additionally, the "pages" structs (which are used to keep track of available physical pages), will require ~1000000 entries, each of which is between 6 and 8 bytes (depending on whether GCC aligns struct sizes). This means another 8MB is used to keep track of free pages, to a total of around 12MB.
|
||||
|
||||
5. We switch to high EIP when we jump to "relocated". Relocated is a label, and a symbol that's inserted by the linker. Since the linker is configured to link the kernel high, relocated points to the upper portion of memory, where KERNBASE is. However, the entry page directory, just like our full page directory later on, sets up two mappings, one starting at 0 (creating a one to one mapping between some of the virtual addresses and their physical counterparts), and one starting at KERNBASE. Thus, we can continue to run at a low EIP. The only reason I can think of as to why we NEED to make the switch, besides the elementary "the kernel links high", is that we need to be able to write to various symbols, also linked above KERNBASE.
|
|
@ -39,6 +39,7 @@ void
|
|||
bootmain(void)
|
||||
{
|
||||
struct Proghdr *ph, *eph;
|
||||
int i;
|
||||
|
||||
// read 1st page off disk
|
||||
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
|
||||
|
@ -50,10 +51,14 @@ bootmain(void)
|
|||
// load each program segment (ignores ph flags)
|
||||
ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
|
||||
eph = ph + ELFHDR->e_phnum;
|
||||
for (; ph < eph; ph++)
|
||||
for (; ph < eph; ph++) {
|
||||
// p_pa is the load address of this segment (as well
|
||||
// as the physical address)
|
||||
readseg(ph->p_pa, ph->p_memsz, ph->p_offset);
|
||||
for (i = 0; i < ph->p_memsz - ph->p_filesz; i++) {
|
||||
*((char *) ph->p_pa + ph->p_filesz + i) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// call the entry point from the ELF header
|
||||
// note: does not return!
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
LAB=1
|
||||
PACKAGEDATE=Thu Aug 30 15:16:04 EDT 2018
|
||||
LAB=4
|
||||
PACKAGEDATE=Mon Oct 8 21:31:51 PDT 2018
|
||||
|
|
28
grade-lab2
Executable file
28
grade-lab2
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("jos.out"),
|
||||
stop_breakpoint("readline"))
|
||||
|
||||
@test(0, "running JOS")
|
||||
def test_jos():
|
||||
r.run_qemu()
|
||||
|
||||
@test(20, "Physical page allocator", parent=test_jos)
|
||||
def test_check_page_alloc():
|
||||
r.match(r"check_page_alloc\(\) succeeded!")
|
||||
|
||||
@test(20, "Page management", parent=test_jos)
|
||||
def test_check_page():
|
||||
r.match(r"check_page\(\) succeeded!")
|
||||
|
||||
@test(20, "Kernel page directory", parent=test_jos)
|
||||
def test_check_kern_pgdir():
|
||||
r.match(r"check_kern_pgdir\(\) succeeded!")
|
||||
|
||||
@test(10, "Page management 2", parent=test_jos)
|
||||
def test_check_page_installed_pgdir():
|
||||
r.match(r"check_page_installed_pgdir\(\) succeeded!")
|
||||
|
||||
run_tests()
|
135
grade-lab3
Executable file
135
grade-lab3
Executable file
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("jos.out"),
|
||||
stop_breakpoint("readline"))
|
||||
|
||||
@test(10)
|
||||
def test_divzero():
|
||||
r.user_test("divzero")
|
||||
r.match('Incoming TRAP frame at 0xefffff..',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x00000000 Divide error',
|
||||
' eip 0x008.....',
|
||||
' ss 0x----0023',
|
||||
'.00001000. free env 00001000',
|
||||
no=['1/0 is ........!'])
|
||||
|
||||
@test(10)
|
||||
def test_softint():
|
||||
r.user_test("softint")
|
||||
r.match('Welcome to the JOS kernel monitor!',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000d General Protection',
|
||||
' eip 0x008.....',
|
||||
' ss 0x----0023',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
@test(10)
|
||||
def test_badsegment():
|
||||
r.user_test("badsegment")
|
||||
r.match('Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000d General Protection',
|
||||
' err 0x00000028',
|
||||
' eip 0x008.....',
|
||||
' ss 0x----0023',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
end_part("A")
|
||||
|
||||
@test(5)
|
||||
def test_faultread():
|
||||
r.user_test("faultread")
|
||||
r.match('.00001000. user fault va 00000000 ip 008.....',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000e Page Fault',
|
||||
' err 0x00000004.*',
|
||||
'.00001000. free env 0000100',
|
||||
no=['I read ........ from location 0!'])
|
||||
|
||||
@test(5)
|
||||
def test_faultreadkernel():
|
||||
r.user_test("faultreadkernel")
|
||||
r.match('.00001000. user fault va f0100000 ip 008.....',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000e Page Fault',
|
||||
' err 0x00000005.*',
|
||||
'.00001000. free env 00001000',
|
||||
no=['I read ........ from location 0xf0100000!'])
|
||||
|
||||
@test(5)
|
||||
def test_faultwrite():
|
||||
r.user_test("faultwrite")
|
||||
r.match('.00001000. user fault va 00000000 ip 008.....',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000e Page Fault',
|
||||
' err 0x00000006.*',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
@test(5)
|
||||
def test_faultwritekernel():
|
||||
r.user_test("faultwritekernel")
|
||||
r.match('.00001000. user fault va f0100000 ip 008.....',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x0000000e Page Fault',
|
||||
' err 0x00000007.*',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
@test(5)
|
||||
def test_breakpoint():
|
||||
r.user_test("breakpoint")
|
||||
r.match('Welcome to the JOS kernel monitor!',
|
||||
'Incoming TRAP frame at 0xefffffbc',
|
||||
'TRAP frame at 0xf.......',
|
||||
' trap 0x00000003 Breakpoint',
|
||||
' eip 0x008.....',
|
||||
' ss 0x----0023',
|
||||
no=['.00001000. free env 00001000'])
|
||||
|
||||
@test(5)
|
||||
def test_testbss():
|
||||
r.user_test("testbss")
|
||||
r.match('Making sure bss works right...',
|
||||
'Yes, good. Now doing a wild write off the end...',
|
||||
'.00001000. user fault va 00c..... ip 008.....',
|
||||
'.00001000. free env 0000100')
|
||||
|
||||
@test(5)
|
||||
def test_hello():
|
||||
r.user_test("hello")
|
||||
r.match('.00000000. new env 00001000',
|
||||
'hello, world',
|
||||
'i am environment 00001000',
|
||||
'.00001000. exiting gracefully',
|
||||
'.00001000. free env 00001000',
|
||||
'Destroyed the only environment - nothing more to do!')
|
||||
|
||||
@test(5)
|
||||
def test_buggyhello():
|
||||
r.user_test("buggyhello")
|
||||
r.match('.00001000. user_mem_check assertion failure for va 00000001',
|
||||
'.00001000. free env 00001000')
|
||||
|
||||
@test(5)
|
||||
def test_buggyhello2():
|
||||
r.user_test("buggyhello2")
|
||||
r.match('.00001000. user_mem_check assertion failure for va 0....000',
|
||||
'.00001000. free env 00001000',
|
||||
no=['hello, world'])
|
||||
|
||||
@test(5)
|
||||
def test_evilhello():
|
||||
r.user_test("evilhello")
|
||||
r.match('.00001000. user_mem_check assertion failure for va f0100...',
|
||||
'.00001000. free env 00001000')
|
||||
|
||||
end_part("B")
|
||||
|
||||
run_tests()
|
190
grade-lab4
Executable file
190
grade-lab4
Executable file
|
@ -0,0 +1,190 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
from gradelib import *
|
||||
|
||||
r = Runner(save("jos.out"),
|
||||
stop_breakpoint("readline"))
|
||||
|
||||
def E(s, trim=False):
|
||||
"""Expand $En in s to the environment ID of the n'th user
|
||||
environment."""
|
||||
|
||||
tmpl = "%x" if trim else "%08x"
|
||||
return re.sub(r"\$E([0-9]+)",
|
||||
lambda m: tmpl % (0x1000 + int(m.group(1))-1), s)
|
||||
|
||||
@test(5)
|
||||
def test_dumbfork():
|
||||
r.user_test("dumbfork")
|
||||
r.match(E(".00000000. new env $E1"),
|
||||
E(".$E1. new env $E2"),
|
||||
"0: I am the parent.",
|
||||
"9: I am the parent.",
|
||||
"0: I am the child.",
|
||||
"9: I am the child.",
|
||||
"19: I am the child.",
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"),
|
||||
E(".$E2. exiting gracefully"),
|
||||
E(".$E2. free env $E2"))
|
||||
|
||||
end_part("A")
|
||||
|
||||
@test(5)
|
||||
def test_faultread():
|
||||
r.user_test("faultread")
|
||||
r.match(E(".$E1. user fault va 00000000 ip 008....."),
|
||||
"TRAP frame at 0xf....... from CPU .",
|
||||
" trap 0x0000000e Page Fault",
|
||||
" err 0x00000004.*",
|
||||
E(".$E1. free env $E1"),
|
||||
no=["I read ........ from location 0."])
|
||||
|
||||
@test(5)
|
||||
def test_faultwrite():
|
||||
r.user_test("faultwrite")
|
||||
r.match(E(".$E1. user fault va 00000000 ip 008....."),
|
||||
"TRAP frame at 0xf....... from CPU .",
|
||||
" trap 0x0000000e Page Fault",
|
||||
" err 0x00000006.*",
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultdie():
|
||||
r.user_test("faultdie")
|
||||
r.match("i faulted at va deadbeef, err 6",
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultregs():
|
||||
r.user_test("faultregs")
|
||||
r.match("Registers in UTrapframe OK",
|
||||
"Registers after page-fault OK",
|
||||
no=["Registers in UTrapframe MISMATCH",
|
||||
"Registers after page-fault MISMATCH"])
|
||||
|
||||
@test(5)
|
||||
def test_faultalloc():
|
||||
r.user_test("faultalloc")
|
||||
r.match("fault deadbeef",
|
||||
"this string was faulted in at deadbeef",
|
||||
"fault cafebffe",
|
||||
"fault cafec000",
|
||||
"this string was faulted in at cafebffe",
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultallocbad():
|
||||
r.user_test("faultallocbad")
|
||||
r.match(E(".$E1. user_mem_check assertion failure for va deadbeef"),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultnostack():
|
||||
r.user_test("faultnostack")
|
||||
r.match(E(".$E1. user_mem_check assertion failure for va eebfff.."),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultbadhandler():
|
||||
r.user_test("faultbadhandler")
|
||||
r.match(E(".$E1. user_mem_check assertion failure for va (deadb|eebfe)..."),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_faultevilhandler():
|
||||
r.user_test("faultevilhandler")
|
||||
r.match(E(".$E1. user_mem_check assertion failure for va (f0100|eebfe)..."),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_forktree():
|
||||
r.user_test("forktree")
|
||||
r.match("....: I am .0.",
|
||||
"....: I am .1.",
|
||||
"....: I am .000.",
|
||||
"....: I am .100.",
|
||||
"....: I am .110.",
|
||||
"....: I am .111.",
|
||||
"....: I am .011.",
|
||||
"....: I am .001.",
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E2. exiting gracefully"),
|
||||
".0000200.. exiting gracefully",
|
||||
".0000200.. free env 0000200.")
|
||||
|
||||
end_part("B")
|
||||
|
||||
@test(5)
|
||||
def test_spin():
|
||||
r.user_test("spin")
|
||||
r.match(E(".00000000. new env $E1"),
|
||||
"I am the parent. Forking the child...",
|
||||
E(".$E1. new env $E2"),
|
||||
"I am the parent. Running the child...",
|
||||
"I am the child. Spinning...",
|
||||
"I am the parent. Killing the child...",
|
||||
E(".$E1. destroying $E2"),
|
||||
E(".$E1. free env $E2"),
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"))
|
||||
|
||||
@test(5)
|
||||
def test_stresssched():
|
||||
r.user_test("stresssched", make_args=["CPUS=4"])
|
||||
r.match(".000010... stresssched on CPU 0",
|
||||
".000010... stresssched on CPU 1",
|
||||
".000010... stresssched on CPU 2",
|
||||
".000010... stresssched on CPU 3",
|
||||
no=[".*ran on two CPUs at once"])
|
||||
|
||||
@test(5)
|
||||
def test_sendpage():
|
||||
r.user_test("sendpage", make_args=["CPUS=2"])
|
||||
r.match(".00000000. new env 00001000",
|
||||
E(".00000000. new env $E1"),
|
||||
E(".$E1. new env $E2"),
|
||||
E("$E1 got message: hello child environment! how are you?", trim=True),
|
||||
E("child received correct message", trim=True),
|
||||
E("$E2 got message: hello parent environment! I'm good", trim=True),
|
||||
E("parent received correct message", trim=True),
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"),
|
||||
E(".$E2. exiting gracefully"),
|
||||
E(".$E2. free env $E2"))
|
||||
|
||||
@test(5)
|
||||
def test_pingpong():
|
||||
r.user_test("pingpong", make_args=["CPUS=4"])
|
||||
r.match(E(".00000000. new env $E1"),
|
||||
E(".$E1. new env $E2"),
|
||||
E("send 0 from $E1 to $E2", trim=True),
|
||||
E("$E2 got 0 from $E1", trim=True),
|
||||
E("$E1 got 1 from $E2", trim=True),
|
||||
E("$E2 got 8 from $E1", trim=True),
|
||||
E("$E1 got 9 from $E2", trim=True),
|
||||
E("$E2 got 10 from $E1", trim=True),
|
||||
E(".$E1. exiting gracefully"),
|
||||
E(".$E1. free env $E1"),
|
||||
E(".$E2. exiting gracefully"),
|
||||
E(".$E2. free env $E2"))
|
||||
|
||||
@test(5)
|
||||
def test_primes():
|
||||
r.user_test("primes", stop_on_line("CPU .: 1877"), stop_on_line(".*panic"),
|
||||
make_args=["CPUS=4"], timeout=60)
|
||||
r.match(E(".00000000. new env $E1"),
|
||||
E(".$E1. new env $E2"),
|
||||
E("CPU .: 2 .$E2. new env $E3"),
|
||||
E("CPU .: 3 .$E3. new env $E4"),
|
||||
E("CPU .: 5 .$E4. new env $E5"),
|
||||
E("CPU .: 7 .$E5. new env $E6"),
|
||||
E("CPU .: 11 .$E6. new env $E7"),
|
||||
E("CPU .: 1877 .$E289. new env $E290"))
|
||||
|
||||
end_part("C")
|
||||
|
||||
run_tests()
|
70
inc/env.h
Normal file
70
inc/env.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_INC_ENV_H
|
||||
#define JOS_INC_ENV_H
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/trap.h>
|
||||
#include <inc/memlayout.h>
|
||||
|
||||
typedef int32_t envid_t;
|
||||
|
||||
// An environment ID 'envid_t' has three parts:
|
||||
//
|
||||
// +1+---------------21-----------------+--------10--------+
|
||||
// |0| Uniqueifier | Environment |
|
||||
// | | | Index |
|
||||
// +------------------------------------+------------------+
|
||||
// \--- ENVX(eid) --/
|
||||
//
|
||||
// The environment index ENVX(eid) equals the environment's index in the
|
||||
// 'envs[]' array. The uniqueifier distinguishes environments that were
|
||||
// created at different times, but share the same environment index.
|
||||
//
|
||||
// All real environments are greater than 0 (so the sign bit is zero).
|
||||
// envid_ts less than 0 signify errors. The envid_t == 0 is special, and
|
||||
// stands for the current environment.
|
||||
|
||||
#define LOG2NENV 10
|
||||
#define NENV (1 << LOG2NENV)
|
||||
#define ENVX(envid) ((envid) & (NENV - 1))
|
||||
|
||||
// Values of env_status in struct Env
|
||||
enum {
|
||||
ENV_FREE = 0,
|
||||
ENV_DYING,
|
||||
ENV_RUNNABLE,
|
||||
ENV_RUNNING,
|
||||
ENV_NOT_RUNNABLE
|
||||
};
|
||||
|
||||
// Special environment types
|
||||
enum EnvType {
|
||||
ENV_TYPE_USER = 0,
|
||||
};
|
||||
|
||||
struct Env {
|
||||
struct Trapframe env_tf; // Saved registers
|
||||
struct Env *env_link; // Next free Env
|
||||
envid_t env_id; // Unique environment identifier
|
||||
envid_t env_parent_id; // env_id of this env's parent
|
||||
enum EnvType env_type; // Indicates special system environments
|
||||
unsigned env_status; // Status of the environment
|
||||
uint32_t env_runs; // Number of times environment has run
|
||||
int env_cpunum; // The CPU that the env is running on
|
||||
|
||||
// Address space
|
||||
pde_t *env_pgdir; // Kernel virtual address of page dir
|
||||
|
||||
// Exception handling
|
||||
void *env_pgfault_upcall; // Page fault upcall entry point
|
||||
|
||||
// Lab 4 IPC
|
||||
bool env_ipc_recving; // Env is blocked receiving
|
||||
void *env_ipc_dstva; // VA at which to map received page
|
||||
uint32_t env_ipc_value; // Data value sent to us
|
||||
envid_t env_ipc_from; // envid of the sender
|
||||
int env_ipc_perm; // Perm of page mapping received
|
||||
};
|
||||
|
||||
#endif // !JOS_INC_ENV_H
|
|
@ -14,6 +14,9 @@ enum {
|
|||
// the maximum allowed
|
||||
E_FAULT , // Memory fault
|
||||
|
||||
E_IPC_NOT_RECV , // Attempt to send to env that is not recving
|
||||
E_EOF , // Unexpected end of file
|
||||
|
||||
MAXERROR
|
||||
};
|
||||
|
||||
|
|
91
inc/lib.h
Normal file
91
inc/lib.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Main public header file for our user-land support library,
|
||||
// whose code lives in the lib directory.
|
||||
// This library is roughly our OS's version of a standard C library,
|
||||
// and is intended to be linked into all user-mode applications
|
||||
// (NOT the kernel or boot loader).
|
||||
|
||||
#ifndef JOS_INC_LIB_H
|
||||
#define JOS_INC_LIB_H 1
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/stdarg.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/env.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/syscall.h>
|
||||
#include <inc/trap.h>
|
||||
|
||||
#define USED(x) (void)(x)
|
||||
|
||||
// main user program
|
||||
void umain(int argc, char **argv);
|
||||
|
||||
// libmain.c or entry.S
|
||||
extern const char *binaryname;
|
||||
extern const volatile struct Env *thisenv;
|
||||
extern const volatile struct Env envs[NENV];
|
||||
extern const volatile struct PageInfo pages[];
|
||||
|
||||
// exit.c
|
||||
void exit(void);
|
||||
|
||||
// pgfault.c
|
||||
void set_pgfault_handler(void (*handler)(struct UTrapframe *utf));
|
||||
|
||||
// readline.c
|
||||
char* readline(const char *buf);
|
||||
|
||||
// syscall.c
|
||||
void sys_cputs(const char *string, size_t len);
|
||||
int sys_cgetc(void);
|
||||
envid_t sys_getenvid(void);
|
||||
int sys_env_destroy(envid_t);
|
||||
void sys_yield(void);
|
||||
static envid_t sys_exofork(void);
|
||||
int sys_env_set_status(envid_t env, int status);
|
||||
int sys_env_set_pgfault_upcall(envid_t env, void *upcall);
|
||||
int sys_page_alloc(envid_t env, void *pg, int perm);
|
||||
int sys_page_map(envid_t src_env, void *src_pg,
|
||||
envid_t dst_env, void *dst_pg, int perm);
|
||||
int sys_page_unmap(envid_t env, void *pg);
|
||||
int sys_ipc_try_send(envid_t to_env, uint32_t value, void *pg, int perm);
|
||||
int sys_ipc_recv(void *rcv_pg);
|
||||
|
||||
// This must be inlined. Exercise for reader: why?
|
||||
static inline envid_t __attribute__((always_inline))
|
||||
sys_exofork(void)
|
||||
{
|
||||
envid_t ret;
|
||||
asm volatile("int %2"
|
||||
: "=a" (ret)
|
||||
: "a" (SYS_exofork), "i" (T_SYSCALL));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ipc.c
|
||||
void ipc_send(envid_t to_env, uint32_t value, void *pg, int perm);
|
||||
int32_t ipc_recv(envid_t *from_env_store, void *pg, int *perm_store);
|
||||
envid_t ipc_find_env(enum EnvType type);
|
||||
|
||||
// fork.c
|
||||
#define PTE_SHARE 0x400
|
||||
envid_t fork(void);
|
||||
envid_t sfork(void); // Challenge!
|
||||
|
||||
|
||||
|
||||
/* File open modes */
|
||||
#define O_RDONLY 0x0000 /* open for reading only */
|
||||
#define O_WRONLY 0x0001 /* open for writing only */
|
||||
#define O_RDWR 0x0002 /* open for reading and writing */
|
||||
#define O_ACCMODE 0x0003 /* mask for above modes */
|
||||
|
||||
#define O_CREAT 0x0100 /* create if nonexistent */
|
||||
#define O_TRUNC 0x0200 /* truncate to zero length */
|
||||
#define O_EXCL 0x0400 /* error if already exists */
|
||||
#define O_MKDIR 0x0800 /* create directory, not regular file */
|
||||
|
||||
#endif // !JOS_INC_LIB_H
|
|
@ -138,10 +138,54 @@
|
|||
// The location of the user-level STABS data structure
|
||||
#define USTABDATA (PTSIZE / 2)
|
||||
|
||||
// Physical address of startup code for non-boot CPUs (APs)
|
||||
#define MPENTRY_PADDR 0x7000
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
typedef uint32_t pte_t;
|
||||
typedef uint32_t pde_t;
|
||||
|
||||
#if JOS_USER
|
||||
/*
|
||||
* The page directory entry corresponding to the virtual address range
|
||||
* [UVPT, UVPT + PTSIZE) points to the page directory itself. Thus, the page
|
||||
* directory is treated as a page table as well as a page directory.
|
||||
*
|
||||
* One result of treating the page directory as a page table is that all PTEs
|
||||
* can be accessed through a "virtual page table" at virtual address UVPT (to
|
||||
* which uvpt is set in lib/entry.S). The PTE for page number N is stored in
|
||||
* uvpt[N]. (It's worth drawing a diagram of this!)
|
||||
*
|
||||
* A second consequence is that the contents of the current page directory
|
||||
* will always be available at virtual address (UVPT + (UVPT >> PGSHIFT)), to
|
||||
* which uvpd is set in lib/entry.S.
|
||||
*/
|
||||
extern volatile pte_t uvpt[]; // VA of "virtual page table"
|
||||
extern volatile pde_t uvpd[]; // VA of current page directory
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Page descriptor structures, mapped at UPAGES.
|
||||
* Read/write to the kernel, read-only to user programs.
|
||||
*
|
||||
* Each struct PageInfo stores metadata for one physical page.
|
||||
* Is it NOT the physical page itself, but there is a one-to-one
|
||||
* correspondence between physical pages and struct PageInfo's.
|
||||
* You can map a struct PageInfo * to the corresponding physical address
|
||||
* with page2pa() in kern/pmap.h.
|
||||
*/
|
||||
struct PageInfo {
|
||||
// Next page on the free list.
|
||||
struct PageInfo *pp_link;
|
||||
|
||||
// pp_ref is the count of pointers (usually in page table entries)
|
||||
// to this page, for pages allocated using page_alloc.
|
||||
// Pages allocated at boot time using pmap.c's
|
||||
// boot_alloc do not have valid reference count fields.
|
||||
|
||||
uint16_t pp_ref;
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
#endif /* !JOS_INC_MEMLAYOUT_H */
|
||||
|
|
22
inc/syscall.h
Normal file
22
inc/syscall.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef JOS_INC_SYSCALL_H
|
||||
#define JOS_INC_SYSCALL_H
|
||||
|
||||
/* system call numbers */
|
||||
enum {
|
||||
SYS_cputs = 0,
|
||||
SYS_cgetc,
|
||||
SYS_getenvid,
|
||||
SYS_env_destroy,
|
||||
SYS_page_alloc,
|
||||
SYS_page_map,
|
||||
SYS_page_unmap,
|
||||
SYS_exofork,
|
||||
SYS_env_set_status,
|
||||
SYS_env_set_pgfault_upcall,
|
||||
SYS_yield,
|
||||
SYS_ipc_try_send,
|
||||
SYS_ipc_recv,
|
||||
NSYSCALLS
|
||||
};
|
||||
|
||||
#endif /* !JOS_INC_SYSCALL_H */
|
91
inc/trap.h
Normal file
91
inc/trap.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
#ifndef JOS_INC_TRAP_H
|
||||
#define JOS_INC_TRAP_H
|
||||
|
||||
// Trap numbers
|
||||
// These are processor defined:
|
||||
#define T_DIVIDE 0 // divide error
|
||||
#define T_DEBUG 1 // debug exception
|
||||
#define T_NMI 2 // non-maskable interrupt
|
||||
#define T_BRKPT 3 // breakpoint
|
||||
#define T_OFLOW 4 // overflow
|
||||
#define T_BOUND 5 // bounds check
|
||||
#define T_ILLOP 6 // illegal opcode
|
||||
#define T_DEVICE 7 // device not available
|
||||
#define T_DBLFLT 8 // double fault
|
||||
/* #define T_COPROC 9 */ // reserved (not generated by recent processors)
|
||||
#define T_TSS 10 // invalid task switch segment
|
||||
#define T_SEGNP 11 // segment not present
|
||||
#define T_STACK 12 // stack exception
|
||||
#define T_GPFLT 13 // general protection fault
|
||||
#define T_PGFLT 14 // page fault
|
||||
/* #define T_RES 15 */ // reserved
|
||||
#define T_FPERR 16 // floating point error
|
||||
#define T_ALIGN 17 // aligment check
|
||||
#define T_MCHK 18 // machine check
|
||||
#define T_SIMDERR 19 // SIMD floating point error
|
||||
|
||||
// These are arbitrarily chosen, but with care not to overlap
|
||||
// processor defined exceptions or interrupt vectors.
|
||||
#define T_SYSCALL 48 // system call
|
||||
#define T_DEFAULT 500 // catchall
|
||||
|
||||
#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET
|
||||
|
||||
// Hardware IRQ numbers. We receive these as (IRQ_OFFSET+IRQ_WHATEVER)
|
||||
#define IRQ_TIMER 0
|
||||
#define IRQ_KBD 1
|
||||
#define IRQ_SERIAL 4
|
||||
#define IRQ_SPURIOUS 7
|
||||
#define IRQ_IDE 14
|
||||
#define IRQ_ERROR 19
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
struct PushRegs {
|
||||
/* registers as pushed by pusha */
|
||||
uint32_t reg_edi;
|
||||
uint32_t reg_esi;
|
||||
uint32_t reg_ebp;
|
||||
uint32_t reg_oesp; /* Useless */
|
||||
uint32_t reg_ebx;
|
||||
uint32_t reg_edx;
|
||||
uint32_t reg_ecx;
|
||||
uint32_t reg_eax;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Trapframe {
|
||||
struct PushRegs tf_regs;
|
||||
uint16_t tf_es;
|
||||
uint16_t tf_padding1;
|
||||
uint16_t tf_ds;
|
||||
uint16_t tf_padding2;
|
||||
uint32_t tf_trapno;
|
||||
/* below here defined by x86 hardware */
|
||||
uint32_t tf_err;
|
||||
uintptr_t tf_eip;
|
||||
uint16_t tf_cs;
|
||||
uint16_t tf_padding3;
|
||||
uint32_t tf_eflags;
|
||||
/* below here only when crossing rings, such as from user to kernel */
|
||||
uintptr_t tf_esp;
|
||||
uint16_t tf_ss;
|
||||
uint16_t tf_padding4;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct UTrapframe {
|
||||
/* information about the fault */
|
||||
uint32_t utf_fault_va; /* va for T_PGFLT, 0 otherwise */
|
||||
uint32_t utf_err;
|
||||
/* trap-time return state */
|
||||
struct PushRegs utf_regs;
|
||||
uintptr_t utf_eip;
|
||||
uint32_t utf_eflags;
|
||||
/* the trap-time stack to return to */
|
||||
uintptr_t utf_esp;
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
#endif /* !JOS_INC_TRAP_H */
|
19
inc/x86.h
19
inc/x86.h
|
@ -3,6 +3,10 @@
|
|||
|
||||
#include <inc/types.h>
|
||||
|
||||
#define MSR_IA32_SYSENTER_CS 0x174
|
||||
#define MSR_IA32_SYSENTER_EIP 0x176
|
||||
#define MSR_IA32_SYSENTER_ESP 0x175
|
||||
|
||||
static inline void
|
||||
breakpoint(void)
|
||||
{
|
||||
|
@ -261,4 +265,19 @@ xchg(volatile uint32_t *addr, uint32_t newval)
|
|||
return result;
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_msr(uint32_t reg, uint32_t low, uint32_t high) {
|
||||
asm volatile("wrmsr\n\t"
|
||||
:: "c" (reg), "a" (low), "d" (high));
|
||||
}
|
||||
|
||||
static inline void
|
||||
read_msr(uint32_t reg, uint32_t* low, uint32_t* high) {
|
||||
uint32_t eax, edx;
|
||||
asm volatile("rdmsr\n\t"
|
||||
: "=a" (eax), "=d" (edx) : "c" (reg));
|
||||
*low = eax;
|
||||
*high = edx;
|
||||
}
|
||||
|
||||
#endif /* !JOS_INC_X86_H */
|
||||
|
|
|
@ -33,12 +33,51 @@ KERN_SRCFILES := kern/entry.S \
|
|||
lib/readline.c \
|
||||
lib/string.c
|
||||
|
||||
# Source files for LAB4
|
||||
KERN_SRCFILES += kern/mpentry.S \
|
||||
kern/mpconfig.c \
|
||||
kern/lapic.c \
|
||||
kern/spinlock.c
|
||||
|
||||
# Only build files if they exist.
|
||||
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
|
||||
|
||||
# Binary program images to embed within the kernel.
|
||||
KERN_BINFILES :=
|
||||
# Binary files for LAB3
|
||||
KERN_BINFILES := user/hello \
|
||||
user/buggyhello \
|
||||
user/buggyhello2 \
|
||||
user/evilhello \
|
||||
user/testbss \
|
||||
user/divzero \
|
||||
user/breakpoint \
|
||||
user/softint \
|
||||
user/badsegment \
|
||||
user/faultread \
|
||||
user/faultreadkernel \
|
||||
user/faultwrite \
|
||||
user/faultwritekernel \
|
||||
user/getc
|
||||
|
||||
# Binary files for LAB4
|
||||
KERN_BINFILES += user/idle \
|
||||
user/yield \
|
||||
user/dumbfork \
|
||||
user/stresssched \
|
||||
user/faultdie \
|
||||
user/faultregs \
|
||||
user/faultalloc \
|
||||
user/faultallocbad \
|
||||
user/faultnostack \
|
||||
user/faultbadhandler \
|
||||
user/faultevilhandler \
|
||||
user/forktree \
|
||||
user/sendpage \
|
||||
user/spin \
|
||||
user/fairness \
|
||||
user/pingpong \
|
||||
user/pingpongs \
|
||||
user/primes
|
||||
KERN_OBJFILES := $(patsubst %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
|
||||
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
|
||||
KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(KERN_OBJFILES))
|
||||
|
|
16
kern/ansi.h
16
kern/ansi.h
|
@ -3,6 +3,22 @@
|
|||
|
||||
#include <inc/types.h>
|
||||
|
||||
#define ACOL_WRAP(s) "\33[" s "m"
|
||||
#define ACOL_BLACK ACOL_WRAP("30")
|
||||
#define ACOL_RED ACOL_WRAP("31")
|
||||
#define ACOL_GREEN ACOL_WRAP("32")
|
||||
#define ACOL_YELLOW ACOL_WRAP("33")
|
||||
#define ACOL_BLUE ACOL_WRAP("34")
|
||||
#define ACOL_MAGENTA ACOL_WRAP("35")
|
||||
#define ACOL_CYAN ACOL_WRAP("36")
|
||||
#define ACOL_WHITE ACOL_WRAP("37")
|
||||
#define ACOL_CLEAR ACOL_WRAP("0")
|
||||
|
||||
#define ACOL_TAG(c, s) "[" c s ACOL_CLEAR "] "
|
||||
#define ACOL_NOTE(s) ACOL_TAG(ACOL_WHITE, " Note ") s
|
||||
#define ACOL_WARN(s) ACOL_TAG(ACOL_YELLOW, "Warning") s
|
||||
#define ACOL_ERR(s) ACOL_TAG(ACOL_RED, " Error ") s
|
||||
|
||||
struct AttrState {
|
||||
uint8_t cattrs;
|
||||
uint8_t attrs;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/console.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/ansi.h>
|
||||
|
||||
static void cons_intr(int (*proc)(void));
|
||||
|
@ -384,6 +386,9 @@ kbd_intr(void)
|
|||
static void
|
||||
kbd_init(void)
|
||||
{
|
||||
// Drain the kbd buffer so that QEMU generates interrupts.
|
||||
kbd_intr();
|
||||
irq_setmask_8259A(irq_mask_8259A & ~(1<<IRQ_KBD));
|
||||
}
|
||||
|
||||
|
||||
|
|
46
kern/cpu.h
Normal file
46
kern/cpu.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
#ifndef JOS_INC_CPU_H
|
||||
#define JOS_INC_CPU_H
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/env.h>
|
||||
|
||||
// Maximum number of CPUs
|
||||
#define NCPU 8
|
||||
|
||||
// Values of status in struct Cpu
|
||||
enum {
|
||||
CPU_UNUSED = 0,
|
||||
CPU_STARTED,
|
||||
CPU_HALTED,
|
||||
};
|
||||
|
||||
// Per-CPU state
|
||||
struct CpuInfo {
|
||||
uint8_t cpu_id; // Local APIC ID; index into cpus[] below
|
||||
volatile unsigned cpu_status; // The status of the CPU
|
||||
struct Env *cpu_env; // The currently-running environment.
|
||||
struct Taskstate cpu_ts; // Used by x86 to find stack for interrupt
|
||||
};
|
||||
|
||||
// Initialized in mpconfig.c
|
||||
extern struct CpuInfo cpus[NCPU];
|
||||
extern int ncpu; // Total number of CPUs in the system
|
||||
extern struct CpuInfo *bootcpu; // The boot-strap processor (BSP)
|
||||
extern physaddr_t lapicaddr; // Physical MMIO address of the local APIC
|
||||
|
||||
// Per-CPU kernel stacks
|
||||
extern unsigned char percpu_kstacks[NCPU][KSTKSIZE];
|
||||
|
||||
int cpunum(void);
|
||||
#define thiscpu (&cpus[cpunum()])
|
||||
|
||||
void mp_init(void);
|
||||
void lapic_init(void);
|
||||
void lapic_startap(uint8_t apicid, uint32_t addr);
|
||||
void lapic_eoi(void);
|
||||
void lapic_ipi(int vector);
|
||||
|
||||
#endif
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/trap.h>
|
||||
|
||||
# Shift Right Logical
|
||||
#define SRL(val, shamt) (((val) >> (shamt)) & ~(-1 << (32 - (shamt))))
|
||||
|
|
535
kern/env.c
Normal file
535
kern/env.c
Normal file
|
@ -0,0 +1,535 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/elf.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
struct Env *envs = NULL; // All environments
|
||||
static struct Env *env_free_list; // Free environment list
|
||||
// (linked by Env->env_link)
|
||||
|
||||
#define ENVGENSHIFT 12 // >= LOGNENV
|
||||
|
||||
// Global descriptor table.
|
||||
//
|
||||
// Set up global descriptor table (GDT) with separate segments for
|
||||
// kernel mode and user mode. Segments serve many purposes on the x86.
|
||||
// We don't use any of their memory-mapping capabilities, but we need
|
||||
// them to switch privilege levels.
|
||||
//
|
||||
// The kernel and user segments are identical except for the DPL.
|
||||
// To load the SS register, the CPL must equal the DPL. Thus,
|
||||
// we must duplicate the segments for the user and the kernel.
|
||||
//
|
||||
// In particular, the last argument to the SEG macro used in the
|
||||
// definition of gdt specifies the Descriptor Privilege Level (DPL)
|
||||
// of that descriptor: 0 for kernel and 3 for user.
|
||||
//
|
||||
struct Segdesc gdt[NCPU + 5] =
|
||||
{
|
||||
// 0x0 - unused (always faults -- for trapping NULL far pointers)
|
||||
SEG_NULL,
|
||||
|
||||
// 0x8 - kernel code segment
|
||||
[GD_KT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 0),
|
||||
|
||||
// 0x10 - kernel data segment
|
||||
[GD_KD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 0),
|
||||
|
||||
// 0x18 - user code segment
|
||||
[GD_UT >> 3] = SEG(STA_X | STA_R, 0x0, 0xffffffff, 3),
|
||||
|
||||
// 0x20 - user data segment
|
||||
[GD_UD >> 3] = SEG(STA_W, 0x0, 0xffffffff, 3),
|
||||
|
||||
// Per-CPU TSS descriptors (starting from GD_TSS0) are initialized
|
||||
// in trap_init_percpu()
|
||||
[GD_TSS0 >> 3] = SEG_NULL
|
||||
};
|
||||
|
||||
struct Pseudodesc gdt_pd = {
|
||||
sizeof(gdt) - 1, (unsigned long) gdt
|
||||
};
|
||||
|
||||
//
|
||||
// Converts an envid to an env pointer.
|
||||
// If checkperm is set, the specified environment must be either the
|
||||
// current environment or an immediate child of the current environment.
|
||||
//
|
||||
// RETURNS
|
||||
// 0 on success, -E_BAD_ENV on error.
|
||||
// On success, sets *env_store to the environment.
|
||||
// On error, sets *env_store to NULL.
|
||||
//
|
||||
int
|
||||
envid2env(envid_t envid, struct Env **env_store, bool checkperm)
|
||||
{
|
||||
struct Env *e;
|
||||
|
||||
// If envid is zero, return the current environment.
|
||||
if (envid == 0) {
|
||||
*env_store = curenv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Look up the Env structure via the index part of the envid,
|
||||
// then check the env_id field in that struct Env
|
||||
// to ensure that the envid is not stale
|
||||
// (i.e., does not refer to a _previous_ environment
|
||||
// that used the same slot in the envs[] array).
|
||||
e = &envs[ENVX(envid)];
|
||||
if (e->env_status == ENV_FREE || e->env_id != envid) {
|
||||
*env_store = 0;
|
||||
return -E_BAD_ENV;
|
||||
}
|
||||
|
||||
// Check that the calling environment has legitimate permission
|
||||
// to manipulate the specified environment.
|
||||
// If checkperm is set, the specified environment
|
||||
// must be either the current environment
|
||||
// or an immediate child of the current environment.
|
||||
if (checkperm && e != curenv && e->env_parent_id != curenv->env_id) {
|
||||
*env_store = 0;
|
||||
return -E_BAD_ENV;
|
||||
}
|
||||
|
||||
*env_store = e;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Mark all environments in 'envs' as free, set their env_ids to 0,
|
||||
// and insert them into the env_free_list.
|
||||
// Make sure the environments are in the free list in the same order
|
||||
// they are in the envs array (i.e., so that the first call to
|
||||
// env_alloc() returns envs[0]).
|
||||
//
|
||||
void
|
||||
env_init(void)
|
||||
{
|
||||
// Set up envs array
|
||||
// LAB 3: Your code here.
|
||||
size_t i = NENV;
|
||||
while(true) {
|
||||
i--;
|
||||
envs[i].env_link = env_free_list;
|
||||
env_free_list = &envs[i];
|
||||
if(i == 0) break;
|
||||
}
|
||||
|
||||
// Per-CPU part of the initialization
|
||||
env_init_percpu();
|
||||
}
|
||||
|
||||
// Load GDT and segment descriptors.
|
||||
void
|
||||
env_init_percpu(void)
|
||||
{
|
||||
lgdt(&gdt_pd);
|
||||
// The kernel never uses GS or FS, so we leave those set to
|
||||
// the user data segment.
|
||||
asm volatile("movw %%ax,%%gs" : : "a" (GD_UD|3));
|
||||
asm volatile("movw %%ax,%%fs" : : "a" (GD_UD|3));
|
||||
// The kernel does use ES, DS, and SS. We'll change between
|
||||
// the kernel and user data segments as needed.
|
||||
asm volatile("movw %%ax,%%es" : : "a" (GD_KD));
|
||||
asm volatile("movw %%ax,%%ds" : : "a" (GD_KD));
|
||||
asm volatile("movw %%ax,%%ss" : : "a" (GD_KD));
|
||||
// Load the kernel text segment into CS.
|
||||
asm volatile("ljmp %0,$1f\n 1:\n" : : "i" (GD_KT));
|
||||
// For good measure, clear the local descriptor table (LDT),
|
||||
// since we don't use it.
|
||||
lldt(0);
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize the kernel virtual memory layout for environment e.
|
||||
// Allocate a page directory, set e->env_pgdir accordingly,
|
||||
// and initialize the kernel portion of the new environment's address space.
|
||||
// Do NOT (yet) map anything into the user portion
|
||||
// of the environment's virtual address space.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors include:
|
||||
// -E_NO_MEM if page directory or table could not be allocated.
|
||||
//
|
||||
static int
|
||||
env_setup_vm(struct Env *e)
|
||||
{
|
||||
int i;
|
||||
struct PageInfo *p = NULL;
|
||||
|
||||
// Allocate a page for the page directory
|
||||
if (!(p = page_alloc(ALLOC_ZERO)))
|
||||
return -E_NO_MEM;
|
||||
|
||||
// Now, set e->env_pgdir and initialize the page directory.
|
||||
//
|
||||
// Hint:
|
||||
// - The VA space of all envs is identical above UTOP
|
||||
// (except at UVPT, which we've set below).
|
||||
// See inc/memlayout.h for permissions and layout.
|
||||
// Can you use kern_pgdir as a template? Hint: Yes.
|
||||
// (Make sure you got the permissions right in Lab 2.)
|
||||
// - The initial VA below UTOP is empty.
|
||||
// - You do not need to make any more calls to page_alloc.
|
||||
// - Note: In general, pp_ref is not maintained for
|
||||
// physical pages mapped only above UTOP, but env_pgdir
|
||||
// is an exception -- you need to increment env_pgdir's
|
||||
// pp_ref for env_free to work correctly.
|
||||
// - The functions in kern/pmap.h are handy.
|
||||
p->pp_ref++;
|
||||
e->env_pgdir = page2kva(p);
|
||||
memcpy(e->env_pgdir + PDX(UTOP), kern_pgdir + PDX(UTOP), sizeof(pde_t) * (NPDENTRIES - PDX(UTOP)));
|
||||
|
||||
// UVPT maps the env's own page table read-only.
|
||||
// Permissions: kernel R, user R
|
||||
e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocates and initializes a new environment.
|
||||
// On success, the new environment is stored in *newenv_store.
|
||||
//
|
||||
// Returns 0 on success, < 0 on failure. Errors include:
|
||||
// -E_NO_FREE_ENV if all NENV environments are allocated
|
||||
// -E_NO_MEM on memory exhaustion
|
||||
//
|
||||
int
|
||||
env_alloc(struct Env **newenv_store, envid_t parent_id)
|
||||
{
|
||||
int32_t generation;
|
||||
int r;
|
||||
struct Env *e;
|
||||
|
||||
if (!(e = env_free_list))
|
||||
return -E_NO_FREE_ENV;
|
||||
|
||||
// Allocate and set up the page directory for this environment.
|
||||
if ((r = env_setup_vm(e)) < 0)
|
||||
return r;
|
||||
|
||||
// Generate an env_id for this environment.
|
||||
generation = (e->env_id + (1 << ENVGENSHIFT)) & ~(NENV - 1);
|
||||
if (generation <= 0) // Don't create a negative env_id.
|
||||
generation = 1 << ENVGENSHIFT;
|
||||
e->env_id = generation | (e - envs);
|
||||
|
||||
// Set the basic status variables.
|
||||
e->env_parent_id = parent_id;
|
||||
e->env_type = ENV_TYPE_USER;
|
||||
e->env_status = ENV_RUNNABLE;
|
||||
e->env_runs = 0;
|
||||
|
||||
// Clear out all the saved register state,
|
||||
// to prevent the register values
|
||||
// of a prior environment inhabiting this Env structure
|
||||
// from "leaking" into our new environment.
|
||||
memset(&e->env_tf, 0, sizeof(e->env_tf));
|
||||
|
||||
// Set up appropriate initial values for the segment registers.
|
||||
// GD_UD is the user data segment selector in the GDT, and
|
||||
// GD_UT is the user text segment selector (see inc/memlayout.h).
|
||||
// The low 2 bits of each segment register contains the
|
||||
// Requestor Privilege Level (RPL); 3 means user mode. When
|
||||
// we switch privilege levels, the hardware does various
|
||||
// checks involving the RPL and the Descriptor Privilege Level
|
||||
// (DPL) stored in the descriptors themselves.
|
||||
e->env_tf.tf_ds = GD_UD | 3;
|
||||
e->env_tf.tf_es = GD_UD | 3;
|
||||
e->env_tf.tf_ss = GD_UD | 3;
|
||||
e->env_tf.tf_esp = USTACKTOP;
|
||||
e->env_tf.tf_cs = GD_UT | 3;
|
||||
// You will set e->env_tf.tf_eip later.
|
||||
|
||||
// Enable interrupts while in user mode.
|
||||
// LAB 4: Your code here.
|
||||
e->env_tf.tf_eflags |= FL_IF;
|
||||
|
||||
// Clear the page fault handler until user installs one.
|
||||
e->env_pgfault_upcall = 0;
|
||||
|
||||
// Also clear the IPC receiving flag.
|
||||
e->env_ipc_recving = 0;
|
||||
|
||||
// commit the allocation
|
||||
env_free_list = e->env_link;
|
||||
*newenv_store = e;
|
||||
|
||||
cprintf("[%08x] new env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate len bytes of physical memory for environment env,
|
||||
// and map it at virtual address va in the environment's address space.
|
||||
// Does not zero or otherwise initialize the mapped pages in any way.
|
||||
// Pages should be writable by user and kernel.
|
||||
// Panic if any allocation attempt fails.
|
||||
//
|
||||
static void
|
||||
region_alloc(struct Env *e, void *va, size_t len)
|
||||
{
|
||||
// LAB 3: Your code here.
|
||||
// (But only if you need it for load_icode.)
|
||||
//
|
||||
// Hint: It is easier to use region_alloc if the caller can pass
|
||||
// 'va' and 'len' values that are not page-aligned.
|
||||
// You should round va down, and round (va + len) up.
|
||||
// (Watch out for corner-cases!)
|
||||
va = ROUNDDOWN(va, PGSIZE);
|
||||
size_t count = ROUNDUP(len, PGSIZE) / PGSIZE;
|
||||
struct PageInfo* p;
|
||||
|
||||
while(count--) {
|
||||
if(!(p = page_alloc(0)))
|
||||
panic("Failed to allocate memory");
|
||||
if(page_insert(e->env_pgdir, p, va, PTE_U | PTE_W))
|
||||
panic("Failed to allocate memory for page table");
|
||||
va += PGSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Set up the initial program binary, stack, and processor flags
|
||||
// for a user process.
|
||||
// This function is ONLY called during kernel initialization,
|
||||
// before running the first user-mode environment.
|
||||
//
|
||||
// This function loads all loadable segments from the ELF binary image
|
||||
// into the environment's user memory, starting at the appropriate
|
||||
// virtual addresses indicated in the ELF program header.
|
||||
// At the same time it clears to zero any portions of these segments
|
||||
// that are marked in the program header as being mapped
|
||||
// but not actually present in the ELF file - i.e., the program's bss section.
|
||||
//
|
||||
// All this is very similar to what our boot loader does, except the boot
|
||||
// loader also needs to read the code from disk. Take a look at
|
||||
// boot/main.c to get ideas.
|
||||
//
|
||||
// Finally, this function maps one page for the program's initial stack.
|
||||
//
|
||||
// load_icode panics if it encounters problems.
|
||||
// - How might load_icode fail? What might be wrong with the given input?
|
||||
//
|
||||
static void
|
||||
load_icode(struct Env *e, uint8_t *binary)
|
||||
{
|
||||
// Hints:
|
||||
// Load each program segment into virtual memory
|
||||
// at the address specified in the ELF segment header.
|
||||
// You should only load segments with ph->p_type == ELF_PROG_LOAD.
|
||||
// Each segment's virtual address can be found in ph->p_va
|
||||
// and its size in memory can be found in ph->p_memsz.
|
||||
// The ph->p_filesz bytes from the ELF binary, starting at
|
||||
// 'binary + ph->p_offset', should be copied to virtual address
|
||||
// ph->p_va. Any remaining memory bytes should be cleared to zero.
|
||||
// (The ELF header should have ph->p_filesz <= ph->p_memsz.)
|
||||
// Use functions from the previous lab to allocate and map pages.
|
||||
//
|
||||
// All page protection bits should be user read/write for now.
|
||||
// ELF segments are not necessarily page-aligned, but you can
|
||||
// assume for this function that no two segments will touch
|
||||
// the same virtual page.
|
||||
//
|
||||
// You may find a function like region_alloc useful.
|
||||
//
|
||||
// Loading the segments is much simpler if you can move data
|
||||
// directly into the virtual addresses stored in the ELF binary.
|
||||
// So which page directory should be in force during
|
||||
// this function?
|
||||
//
|
||||
// You must also do something with the program's entry point,
|
||||
// to make sure that the environment starts executing there.
|
||||
// What? (See env_run() and env_pop_tf() below.)
|
||||
|
||||
// LAB 3: Your code here.
|
||||
|
||||
// TODO validate the headers
|
||||
lcr3(PADDR(e->env_pgdir));
|
||||
struct Elf* elf = (struct Elf*) binary;
|
||||
struct Proghdr* ph = (struct Proghdr*) (binary + elf->e_phoff);
|
||||
struct Proghdr* phend = ph + elf->e_phnum;
|
||||
for(; ph < phend; ph++) {
|
||||
if(ph->p_type != ELF_PROG_LOAD) continue;
|
||||
|
||||
region_alloc(e, (void*) ph->p_va, ph->p_memsz);
|
||||
memcpy((void*) ph->p_va, binary + ph->p_offset, ph->p_filesz);
|
||||
memset((void*) ph->p_va + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
|
||||
}
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
e->env_tf.tf_eip = elf->e_entry;
|
||||
|
||||
// Now map one page for the program's initial stack
|
||||
// at virtual address USTACKTOP - PGSIZE.
|
||||
region_alloc(e, (void*) USTACKTOP - PGSIZE, PGSIZE);
|
||||
}
|
||||
|
||||
//
|
||||
// Allocates a new env with env_alloc, loads the named elf
|
||||
// binary into it with load_icode, and sets its env_type.
|
||||
// This function is ONLY called during kernel initialization,
|
||||
// before running the first user-mode environment.
|
||||
// The new env's parent ID is set to 0.
|
||||
//
|
||||
void
|
||||
env_create(uint8_t *binary, enum EnvType type)
|
||||
{
|
||||
// LAB 3: Your code here.
|
||||
struct Env* new_env;
|
||||
if(env_alloc(&new_env, 0) < 0)
|
||||
panic("Failed to allocate environment");
|
||||
new_env->env_type = type;
|
||||
load_icode(new_env, binary);
|
||||
}
|
||||
|
||||
//
|
||||
// Frees env e and all memory it uses.
|
||||
//
|
||||
void
|
||||
env_free(struct Env *e)
|
||||
{
|
||||
pte_t *pt;
|
||||
uint32_t pdeno, pteno;
|
||||
physaddr_t pa;
|
||||
|
||||
// If freeing the current environment, switch to kern_pgdir
|
||||
// before freeing the page directory, just in case the page
|
||||
// gets reused.
|
||||
if (e == curenv)
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
|
||||
// Note the environment's demise.
|
||||
cprintf("[%08x] free env %08x\n", curenv ? curenv->env_id : 0, e->env_id);
|
||||
|
||||
// Flush all mapped pages in the user portion of the address space
|
||||
static_assert(UTOP % PTSIZE == 0);
|
||||
for (pdeno = 0; pdeno < PDX(UTOP); pdeno++) {
|
||||
|
||||
// only look at mapped page tables
|
||||
if (!(e->env_pgdir[pdeno] & PTE_P))
|
||||
continue;
|
||||
|
||||
// find the pa and va of the page table
|
||||
pa = PTE_ADDR(e->env_pgdir[pdeno]);
|
||||
pt = (pte_t*) KADDR(pa);
|
||||
|
||||
// unmap all PTEs in this page table
|
||||
for (pteno = 0; pteno <= PTX(~0); pteno++) {
|
||||
if (pt[pteno] & PTE_P)
|
||||
page_remove(e->env_pgdir, PGADDR(pdeno, pteno, 0));
|
||||
}
|
||||
|
||||
// free the page table itself
|
||||
e->env_pgdir[pdeno] = 0;
|
||||
page_decref(pa2page(pa));
|
||||
}
|
||||
|
||||
// free the page directory
|
||||
pa = PADDR(e->env_pgdir);
|
||||
e->env_pgdir = 0;
|
||||
page_decref(pa2page(pa));
|
||||
|
||||
// return the environment to the free list
|
||||
e->env_status = ENV_FREE;
|
||||
e->env_link = env_free_list;
|
||||
env_free_list = e;
|
||||
}
|
||||
|
||||
//
|
||||
// Frees environment e.
|
||||
// If e was the current env, then runs a new environment (and does not return
|
||||
// to the caller).
|
||||
//
|
||||
void
|
||||
env_destroy(struct Env *e)
|
||||
{
|
||||
// If e is currently running on other CPUs, we change its state to
|
||||
// ENV_DYING. A zombie environment will be freed the next time
|
||||
// it traps to the kernel.
|
||||
if (e->env_status == ENV_RUNNING && curenv != e) {
|
||||
e->env_status = ENV_DYING;
|
||||
return;
|
||||
}
|
||||
|
||||
env_free(e);
|
||||
|
||||
if (curenv == e) {
|
||||
curenv = NULL;
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Restores the register values in the Trapframe with the 'iret' instruction.
|
||||
// This exits the kernel and starts executing some environment's code.
|
||||
//
|
||||
// This function does not return.
|
||||
//
|
||||
void
|
||||
env_pop_tf(struct Trapframe *tf)
|
||||
{
|
||||
// Record the CPU we are running on for user-space debugging
|
||||
curenv->env_cpunum = cpunum();
|
||||
|
||||
asm volatile(
|
||||
"\tmovl %0,%%esp\n"
|
||||
"\tpopal\n"
|
||||
"\tpopl %%es\n"
|
||||
"\tpopl %%ds\n"
|
||||
"\taddl $0x8,%%esp\n" /* skip tf_trapno and tf_errcode */
|
||||
"\tiret\n"
|
||||
: : "g" (tf) : "memory");
|
||||
panic("iret failed"); /* mostly to placate the compiler */
|
||||
}
|
||||
|
||||
//
|
||||
// Context switch from curenv to env e.
|
||||
// Note: if this is the first call to env_run, curenv is NULL.
|
||||
//
|
||||
// This function does not return.
|
||||
//
|
||||
void
|
||||
env_run(struct Env *e)
|
||||
{
|
||||
// Step 1: If this is a context switch (a new environment is running):
|
||||
// 1. Set the current environment (if any) back to
|
||||
// ENV_RUNNABLE if it is ENV_RUNNING (think about
|
||||
// what other states it can be in),
|
||||
// 2. Set 'curenv' to the new environment,
|
||||
// 3. Set its status to ENV_RUNNING,
|
||||
// 4. Update its 'env_runs' counter,
|
||||
// 5. Use lcr3() to switch to its address space.
|
||||
// Step 2: Use env_pop_tf() to restore the environment's
|
||||
// registers and drop into user mode in the
|
||||
// environment.
|
||||
|
||||
// Hint: This function loads the new environment's state from
|
||||
// e->env_tf. Go back through the code you wrote above
|
||||
// and make sure you have set the relevant parts of
|
||||
// e->env_tf to sensible values.
|
||||
|
||||
// LAB 3: Your code here.
|
||||
if(curenv && curenv->env_status == ENV_RUNNING) {
|
||||
curenv->env_status = ENV_RUNNABLE;
|
||||
}
|
||||
curenv = e;
|
||||
e->env_status = ENV_RUNNING;
|
||||
e->env_runs++;
|
||||
lcr3(PADDR(e->env_pgdir));
|
||||
unlock_kernel();
|
||||
env_pop_tf(&e->env_tf);
|
||||
}
|
||||
|
36
kern/env.h
Normal file
36
kern/env.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_ENV_H
|
||||
#define JOS_KERN_ENV_H
|
||||
|
||||
#include <inc/env.h>
|
||||
#include <kern/cpu.h>
|
||||
|
||||
extern struct Env *envs; // All environments
|
||||
#define curenv (thiscpu->cpu_env) // Current environment
|
||||
extern struct Segdesc gdt[];
|
||||
|
||||
void env_init(void);
|
||||
void env_init_percpu(void);
|
||||
int env_alloc(struct Env **e, envid_t parent_id);
|
||||
void env_free(struct Env *e);
|
||||
void env_create(uint8_t *binary, enum EnvType type);
|
||||
void env_destroy(struct Env *e); // Does not return if e == curenv
|
||||
|
||||
int envid2env(envid_t envid, struct Env **env_store, bool checkperm);
|
||||
// The following two functions do not return
|
||||
void env_run(struct Env *e) __attribute__((noreturn));
|
||||
void env_pop_tf(struct Trapframe *tf) __attribute__((noreturn));
|
||||
|
||||
// Without this extra macro, we couldn't pass macros like TEST to
|
||||
// ENV_CREATE because of the C pre-processor's argument prescan rule.
|
||||
#define ENV_PASTE3(x, y, z) x ## y ## z
|
||||
|
||||
#define ENV_CREATE(x, type) \
|
||||
do { \
|
||||
extern uint8_t ENV_PASTE3(_binary_obj_, x, _start)[]; \
|
||||
env_create(ENV_PASTE3(_binary_obj_, x, _start), \
|
||||
type); \
|
||||
} while (0)
|
||||
|
||||
#endif // !JOS_KERN_ENV_H
|
134
kern/init.c
134
kern/init.c
|
@ -3,42 +3,31 @@
|
|||
#include <inc/stdio.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/x86.h>
|
||||
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
// Test the stack backtrace function (lab 1 only)
|
||||
void
|
||||
test_backtrace(int x)
|
||||
{
|
||||
cprintf("entering test_backtrace %d\n", x);
|
||||
if (x > 0)
|
||||
test_backtrace(x-1);
|
||||
else
|
||||
mon_backtrace(0, 0, 0);
|
||||
cprintf("leaving test_backtrace %d\n", x);
|
||||
}
|
||||
static void boot_aps(void);
|
||||
|
||||
void sysenter_handler();
|
||||
|
||||
void
|
||||
i386_init(void)
|
||||
{
|
||||
extern char edata[], end[];
|
||||
|
||||
// Before doing anything else, complete the ELF loading process.
|
||||
// Clear the uninitialized global data (BSS) section of our program.
|
||||
// This ensures that all static/global variables start out zero.
|
||||
memset(edata, 0, end - edata);
|
||||
|
||||
// Initialize the console.
|
||||
// Can't call cprintf until after we do this!
|
||||
cons_init();
|
||||
|
||||
cprintf("444544 decimal is %o octal!\n", 444544);
|
||||
|
||||
// Test the stack backtrace function (lab 1 only)
|
||||
test_backtrace(5);
|
||||
|
||||
// Test ANSI color escape codes
|
||||
cprintf("\33[31m" "C"
|
||||
"\33[33m" "o"
|
||||
"\33[32m" "l"
|
||||
|
@ -46,11 +35,102 @@ i386_init(void)
|
|||
"\33[34m" "r"
|
||||
"\33[0m" " Works!" "\n");
|
||||
|
||||
// Drop into the kernel monitor.
|
||||
while (1)
|
||||
monitor(NULL);
|
||||
write_msr(MSR_IA32_SYSENTER_EIP, (uint32_t) sysenter_handler, 0);
|
||||
write_msr(MSR_IA32_SYSENTER_ESP, KSTACKTOP, 0);
|
||||
write_msr(MSR_IA32_SYSENTER_CS, GD_KT, 0);
|
||||
|
||||
// Lab 2 memory management initialization functions
|
||||
mem_init();
|
||||
|
||||
// Lab 3 user environment initialization functions
|
||||
env_init();
|
||||
trap_init();
|
||||
|
||||
// Lab 4 multiprocessor initialization functions
|
||||
mp_init();
|
||||
lapic_init();
|
||||
|
||||
// Lab 4 multitasking initialization functions
|
||||
pic_init();
|
||||
|
||||
// Acquire the big kernel lock before waking up APs
|
||||
// Your code here:
|
||||
lock_kernel();
|
||||
|
||||
// Starting non-boot CPUs
|
||||
boot_aps();
|
||||
|
||||
#if defined(TEST)
|
||||
// Don't touch -- used by grading script!
|
||||
ENV_CREATE(TEST, ENV_TYPE_USER);
|
||||
#else
|
||||
// Touch all you want.
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
ENV_CREATE(user_yield, ENV_TYPE_USER);
|
||||
#endif // TEST*
|
||||
|
||||
// Schedule and run the first user environment!
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
// While boot_aps is booting a given CPU, it communicates the per-core
|
||||
// stack pointer that should be loaded by mpentry.S to that CPU in
|
||||
// this variable.
|
||||
void *mpentry_kstack;
|
||||
|
||||
// Start the non-boot (AP) processors.
|
||||
static void
|
||||
boot_aps(void)
|
||||
{
|
||||
extern unsigned char mpentry_start[], mpentry_end[];
|
||||
void *code;
|
||||
struct CpuInfo *c;
|
||||
|
||||
// Write entry code to unused memory at MPENTRY_PADDR
|
||||
code = KADDR(MPENTRY_PADDR);
|
||||
memmove(code, mpentry_start, mpentry_end - mpentry_start);
|
||||
|
||||
// Boot each AP one at a time
|
||||
for (c = cpus; c < cpus + ncpu; c++) {
|
||||
if (c == cpus + cpunum()) // We've started already.
|
||||
continue;
|
||||
|
||||
// Tell mpentry.S what stack to use
|
||||
mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE;
|
||||
// Start the CPU at mpentry_start
|
||||
lapic_startap(c->cpu_id, PADDR(code));
|
||||
// Wait for the CPU to finish some basic setup in mp_main()
|
||||
while(c->cpu_status != CPU_STARTED)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup code for APs
|
||||
void
|
||||
mp_main(void)
|
||||
{
|
||||
// We are in high EIP now, safe to switch to kern_pgdir
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
cprintf("SMP: CPU %d starting\n", cpunum());
|
||||
|
||||
lapic_init();
|
||||
env_init_percpu();
|
||||
trap_init_percpu();
|
||||
xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up
|
||||
|
||||
// Now that we have finished some basic setup, call sched_yield()
|
||||
// to start running processes on this CPU. But make sure that
|
||||
// only one CPU can enter the scheduler at a time!
|
||||
//
|
||||
// Your code here:
|
||||
lock_kernel();
|
||||
sched_yield();
|
||||
|
||||
// Remove this after you finish Exercise 6
|
||||
for (;;);
|
||||
}
|
||||
|
||||
/*
|
||||
* Variable panicstr contains argument to first call to panic; used as flag
|
||||
|
@ -75,7 +155,7 @@ _panic(const char *file, int line, const char *fmt,...)
|
|||
asm volatile("cli; cld");
|
||||
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel panic at %s:%d: ", file, line);
|
||||
cprintf("kernel panic on CPU %d at %s:%d: ", cpunum(), file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
|
|
22
kern/kclock.c
Normal file
22
kern/kclock.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
/* Support for reading the NVRAM from the real-time clock. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
|
||||
#include <kern/kclock.h>
|
||||
|
||||
|
||||
unsigned
|
||||
mc146818_read(unsigned reg)
|
||||
{
|
||||
outb(IO_RTC, reg);
|
||||
return inb(IO_RTC+1);
|
||||
}
|
||||
|
||||
void
|
||||
mc146818_write(unsigned reg, unsigned datum)
|
||||
{
|
||||
outb(IO_RTC, reg);
|
||||
outb(IO_RTC+1, datum);
|
||||
}
|
29
kern/kclock.h
Normal file
29
kern/kclock.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_KCLOCK_H
|
||||
#define JOS_KERN_KCLOCK_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#define IO_RTC 0x070 /* RTC port */
|
||||
|
||||
#define MC_NVRAM_START 0xe /* start of NVRAM: offset 14 */
|
||||
#define MC_NVRAM_SIZE 50 /* 50 bytes of NVRAM */
|
||||
|
||||
/* NVRAM bytes 7 & 8: base memory size */
|
||||
#define NVRAM_BASELO (MC_NVRAM_START + 7) /* low byte; RTC off. 0x15 */
|
||||
#define NVRAM_BASEHI (MC_NVRAM_START + 8) /* high byte; RTC off. 0x16 */
|
||||
|
||||
/* NVRAM bytes 9 & 10: extended memory size (between 1MB and 16MB) */
|
||||
#define NVRAM_EXTLO (MC_NVRAM_START + 9) /* low byte; RTC off. 0x17 */
|
||||
#define NVRAM_EXTHI (MC_NVRAM_START + 10) /* high byte; RTC off. 0x18 */
|
||||
|
||||
/* NVRAM bytes 38 and 39: extended memory size (between 16MB and 4G) */
|
||||
#define NVRAM_EXT16LO (MC_NVRAM_START + 38) /* low byte; RTC off. 0x34 */
|
||||
#define NVRAM_EXT16HI (MC_NVRAM_START + 39) /* high byte; RTC off. 0x35 */
|
||||
|
||||
unsigned mc146818_read(unsigned reg);
|
||||
void mc146818_write(unsigned reg, unsigned datum);
|
||||
|
||||
#endif // !JOS_KERN_KCLOCK_H
|
|
@ -4,12 +4,21 @@
|
|||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/kdebug.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/env.h>
|
||||
|
||||
extern const struct Stab __STAB_BEGIN__[]; // Beginning of stabs table
|
||||
extern const struct Stab __STAB_END__[]; // End of stabs table
|
||||
extern const char __STABSTR_BEGIN__[]; // Beginning of string table
|
||||
extern const char __STABSTR_END__[]; // End of string table
|
||||
|
||||
struct UserStabData {
|
||||
const struct Stab *stabs;
|
||||
const struct Stab *stab_end;
|
||||
const char *stabstr;
|
||||
const char *stabstr_end;
|
||||
};
|
||||
|
||||
|
||||
// stab_binsearch(stabs, region_left, region_right, type, addr)
|
||||
//
|
||||
|
@ -123,8 +132,24 @@ debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)
|
|||
stabstr = __STABSTR_BEGIN__;
|
||||
stabstr_end = __STABSTR_END__;
|
||||
} else {
|
||||
// Can't search for user-level addresses yet!
|
||||
panic("User address");
|
||||
// The user-application linker script, user/user.ld,
|
||||
// puts information about the application's stabs (equivalent
|
||||
// to __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__, and
|
||||
// __STABSTR_END__) in a structure located at virtual address
|
||||
// USTABDATA.
|
||||
const struct UserStabData *usd = (const struct UserStabData *) USTABDATA;
|
||||
|
||||
// Make sure this memory is valid.
|
||||
// Return -1 if it is not. Hint: Call user_mem_check.
|
||||
// LAB 3: Your code here.
|
||||
|
||||
stabs = usd->stabs;
|
||||
stab_end = usd->stab_end;
|
||||
stabstr = usd->stabstr;
|
||||
stabstr_end = usd->stabstr_end;
|
||||
|
||||
// Make sure the STABS and string table memory is valid.
|
||||
// LAB 3: Your code here.
|
||||
}
|
||||
|
||||
// String table validity checks
|
||||
|
|
|
@ -44,18 +44,19 @@ SECTIONS
|
|||
|
||||
/* The data segment */
|
||||
.data : {
|
||||
*(.data)
|
||||
*(.data .data.*)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
PROVIDE(edata = .);
|
||||
*(.bss)
|
||||
*(.dynbss)
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
PROVIDE(end = .);
|
||||
BYTE(0)
|
||||
}
|
||||
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame .note.GNU-stack)
|
||||
*(.eh_frame .note.GNU-stack .comment .note)
|
||||
}
|
||||
}
|
||||
|
|
182
kern/lapic.c
Normal file
182
kern/lapic.c
Normal file
|
@ -0,0 +1,182 @@
|
|||
// The local APIC manages internal (non-I/O) interrupts.
|
||||
// See Chapter 8 & Appendix C of Intel processor manual volume 3.
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/trap.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/x86.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/cpu.h>
|
||||
|
||||
// Local APIC registers, divided by 4 for use as uint32_t[] indices.
|
||||
#define ID (0x0020/4) // ID
|
||||
#define VER (0x0030/4) // Version
|
||||
#define TPR (0x0080/4) // Task Priority
|
||||
#define EOI (0x00B0/4) // EOI
|
||||
#define SVR (0x00F0/4) // Spurious Interrupt Vector
|
||||
#define ENABLE 0x00000100 // Unit Enable
|
||||
#define ESR (0x0280/4) // Error Status
|
||||
#define ICRLO (0x0300/4) // Interrupt Command
|
||||
#define INIT 0x00000500 // INIT/RESET
|
||||
#define STARTUP 0x00000600 // Startup IPI
|
||||
#define DELIVS 0x00001000 // Delivery status
|
||||
#define ASSERT 0x00004000 // Assert interrupt (vs deassert)
|
||||
#define DEASSERT 0x00000000
|
||||
#define LEVEL 0x00008000 // Level triggered
|
||||
#define BCAST 0x00080000 // Send to all APICs, including self.
|
||||
#define OTHERS 0x000C0000 // Send to all APICs, excluding self.
|
||||
#define BUSY 0x00001000
|
||||
#define FIXED 0x00000000
|
||||
#define ICRHI (0x0310/4) // Interrupt Command [63:32]
|
||||
#define TIMER (0x0320/4) // Local Vector Table 0 (TIMER)
|
||||
#define X1 0x0000000B // divide counts by 1
|
||||
#define PERIODIC 0x00020000 // Periodic
|
||||
#define PCINT (0x0340/4) // Performance Counter LVT
|
||||
#define LINT0 (0x0350/4) // Local Vector Table 1 (LINT0)
|
||||
#define LINT1 (0x0360/4) // Local Vector Table 2 (LINT1)
|
||||
#define ERROR (0x0370/4) // Local Vector Table 3 (ERROR)
|
||||
#define MASKED 0x00010000 // Interrupt masked
|
||||
#define TICR (0x0380/4) // Timer Initial Count
|
||||
#define TCCR (0x0390/4) // Timer Current Count
|
||||
#define TDCR (0x03E0/4) // Timer Divide Configuration
|
||||
|
||||
physaddr_t lapicaddr; // Initialized in mpconfig.c
|
||||
volatile uint32_t *lapic;
|
||||
|
||||
static void
|
||||
lapicw(int index, int value)
|
||||
{
|
||||
lapic[index] = value;
|
||||
lapic[ID]; // wait for write to finish, by reading
|
||||
}
|
||||
|
||||
void
|
||||
lapic_init(void)
|
||||
{
|
||||
if (!lapicaddr)
|
||||
return;
|
||||
|
||||
// lapicaddr is the physical address of the LAPIC's 4K MMIO
|
||||
// region. Map it in to virtual memory so we can access it.
|
||||
lapic = mmio_map_region(lapicaddr, 4096);
|
||||
|
||||
// Enable local APIC; set spurious interrupt vector.
|
||||
lapicw(SVR, ENABLE | (IRQ_OFFSET + IRQ_SPURIOUS));
|
||||
|
||||
// The timer repeatedly counts down at bus frequency
|
||||
// from lapic[TICR] and then issues an interrupt.
|
||||
// If we cared more about precise timekeeping,
|
||||
// TICR would be calibrated using an external time source.
|
||||
lapicw(TDCR, X1);
|
||||
lapicw(TIMER, PERIODIC | (IRQ_OFFSET + IRQ_TIMER));
|
||||
lapicw(TICR, 10000000);
|
||||
|
||||
// Leave LINT0 of the BSP enabled so that it can get
|
||||
// interrupts from the 8259A chip.
|
||||
//
|
||||
// According to Intel MP Specification, the BIOS should initialize
|
||||
// BSP's local APIC in Virtual Wire Mode, in which 8259A's
|
||||
// INTR is virtually connected to BSP's LINTIN0. In this mode,
|
||||
// we do not need to program the IOAPIC.
|
||||
if (thiscpu != bootcpu)
|
||||
lapicw(LINT0, MASKED);
|
||||
|
||||
// Disable NMI (LINT1) on all CPUs
|
||||
lapicw(LINT1, MASKED);
|
||||
|
||||
// Disable performance counter overflow interrupts
|
||||
// on machines that provide that interrupt entry.
|
||||
if (((lapic[VER]>>16) & 0xFF) >= 4)
|
||||
lapicw(PCINT, MASKED);
|
||||
|
||||
// Map error interrupt to IRQ_ERROR.
|
||||
lapicw(ERROR, IRQ_OFFSET + IRQ_ERROR);
|
||||
|
||||
// Clear error status register (requires back-to-back writes).
|
||||
lapicw(ESR, 0);
|
||||
lapicw(ESR, 0);
|
||||
|
||||
// Ack any outstanding interrupts.
|
||||
lapicw(EOI, 0);
|
||||
|
||||
// Send an Init Level De-Assert to synchronize arbitration ID's.
|
||||
lapicw(ICRHI, 0);
|
||||
lapicw(ICRLO, BCAST | INIT | LEVEL);
|
||||
while(lapic[ICRLO] & DELIVS)
|
||||
;
|
||||
|
||||
// Enable interrupts on the APIC (but not on the processor).
|
||||
lapicw(TPR, 0);
|
||||
}
|
||||
|
||||
int
|
||||
cpunum(void)
|
||||
{
|
||||
if (lapic)
|
||||
return lapic[ID] >> 24;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Acknowledge interrupt.
|
||||
void
|
||||
lapic_eoi(void)
|
||||
{
|
||||
if (lapic)
|
||||
lapicw(EOI, 0);
|
||||
}
|
||||
|
||||
// Spin for a given number of microseconds.
|
||||
// On real hardware would want to tune this dynamically.
|
||||
static void
|
||||
microdelay(int us)
|
||||
{
|
||||
}
|
||||
|
||||
#define IO_RTC 0x70
|
||||
|
||||
// Start additional processor running entry code at addr.
|
||||
// See Appendix B of MultiProcessor Specification.
|
||||
void
|
||||
lapic_startap(uint8_t apicid, uint32_t addr)
|
||||
{
|
||||
int i;
|
||||
uint16_t *wrv;
|
||||
|
||||
// "The BSP must initialize CMOS shutdown code to 0AH
|
||||
// and the warm reset vector (DWORD based at 40:67) to point at
|
||||
// the AP startup code prior to the [universal startup algorithm]."
|
||||
outb(IO_RTC, 0xF); // offset 0xF is shutdown code
|
||||
outb(IO_RTC+1, 0x0A);
|
||||
wrv = (uint16_t *)KADDR((0x40 << 4 | 0x67)); // Warm reset vector
|
||||
wrv[0] = 0;
|
||||
wrv[1] = addr >> 4;
|
||||
|
||||
// "Universal startup algorithm."
|
||||
// Send INIT (level-triggered) interrupt to reset other CPU.
|
||||
lapicw(ICRHI, apicid << 24);
|
||||
lapicw(ICRLO, INIT | LEVEL | ASSERT);
|
||||
microdelay(200);
|
||||
lapicw(ICRLO, INIT | LEVEL);
|
||||
microdelay(100); // should be 10ms, but too slow in Bochs!
|
||||
|
||||
// Send startup IPI (twice!) to enter code.
|
||||
// Regular hardware is supposed to only accept a STARTUP
|
||||
// when it is in the halted state due to an INIT. So the second
|
||||
// should be ignored, but it is part of the official Intel algorithm.
|
||||
// Bochs complains about the second one. Too bad for Bochs.
|
||||
for (i = 0; i < 2; i++) {
|
||||
lapicw(ICRHI, apicid << 24);
|
||||
lapicw(ICRLO, STARTUP | (addr >> 12));
|
||||
microdelay(200);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lapic_ipi(int vector)
|
||||
{
|
||||
lapicw(ICRLO, OTHERS | FIXED | vector);
|
||||
while (lapic[ICRLO] & DELIVS)
|
||||
;
|
||||
}
|
150
kern/monitor.c
150
kern/monitor.c
|
@ -7,9 +7,14 @@
|
|||
#include <inc/assert.h>
|
||||
#include <inc/x86.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/ansi.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/kdebug.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <inc/string.h>
|
||||
|
||||
#define CMDBUF_SIZE 80 // enough for one VGA text line
|
||||
|
||||
|
@ -25,7 +30,12 @@ struct Command {
|
|||
static struct Command commands[] = {
|
||||
{ "help", "Display this list of commands", mon_help },
|
||||
{ "kerninfo", "Display information about the kernel", mon_kerninfo },
|
||||
{ "backtrace", "Display current backtrace", mon_backtrace }
|
||||
{ "backtrace", "Display current backtrace", mon_backtrace },
|
||||
{ "showmappings", "Display the physical mappings for range", mon_showmappings },
|
||||
{ "mperms", "Change the permissions of a memory range", mon_mperms },
|
||||
{ "resume", "Resume from a breakpoint", mon_resume },
|
||||
{ "step", "Step to next instruction", mon_step }
|
||||
|
||||
};
|
||||
|
||||
/***** Implementations of basic kernel monitor commands *****/
|
||||
|
@ -46,7 +56,7 @@ mon_kerninfo(int argc, char **argv, struct Trapframe *tf)
|
|||
extern char _start[], entry[], etext[], edata[], end[];
|
||||
|
||||
cprintf("Special kernel symbols:\n");
|
||||
cprintf(" _start %08x (phys)\n", _start);
|
||||
cprintf(" _start %08x (phys)\n", _start);
|
||||
cprintf(" entry %08x (virt) %08x (phys)\n", entry, entry - KERNBASE);
|
||||
cprintf(" etext %08x (virt) %08x (phys)\n", etext, etext - KERNBASE);
|
||||
cprintf(" edata %08x (virt) %08x (phys)\n", edata, edata - KERNBASE);
|
||||
|
@ -75,7 +85,7 @@ mon_backtrace(int argc, char **argv, struct Trapframe *tf)
|
|||
ebp = (uint32_t*) ebp[0];
|
||||
|
||||
debuginfo_eip(eip, &info);
|
||||
cprintf(" %s:%d: %.*s+%d\n",
|
||||
cprintf(" %s:%d: %.*s+%d\n",
|
||||
info.eip_file, info.eip_line,
|
||||
info.eip_fn_namelen, info.eip_fn_name,
|
||||
eip - info.eip_fn_addr);
|
||||
|
@ -84,7 +94,139 @@ mon_backtrace(int argc, char **argv, struct Trapframe *tf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define EXPECT_ARGS(n, ac) if(ac - 1 != n) { \
|
||||
cprintf(ACOL_ERR("Expected %d arguments, " \
|
||||
"got %d\n"), n, ac - 1); \
|
||||
return 1; }
|
||||
|
||||
#define VA_TXT ACOL_CYAN "VA" ACOL_CLEAR
|
||||
#define PA_TXT ACOL_GREEN "PA" ACOL_CLEAR
|
||||
|
||||
char*
|
||||
decode_pte_perms(pte_t pte, char* buffer) {
|
||||
buffer[0] = (pte & PTE_W) ? 'w' : 'r';
|
||||
buffer[1] = (pte & PTE_U) ? 'u' : 'k';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void
|
||||
get_pagebounds(char* one, char* two, uintptr_t* pone, uintptr_t* ptwo) {
|
||||
long from = strtol(one, NULL, 0);
|
||||
long to = strtol(two, NULL, 0);
|
||||
*pone = ROUNDDOWN(from, PGSIZE);
|
||||
*ptwo = ROUNDUP(to, PGSIZE);
|
||||
if(*pone != from) cprintf(ACOL_WARN("Aligning start address %p down to %p\n"),
|
||||
from, *pone);
|
||||
if(*ptwo != to) cprintf(ACOL_WARN("Aligning end address %p up to %p\n"),
|
||||
to, *ptwo);
|
||||
}
|
||||
|
||||
int
|
||||
mon_showmappings(int argc, char** argv, struct Trapframe* tf) {
|
||||
EXPECT_ARGS(2, argc);
|
||||
uintptr_t va_start, va_end;
|
||||
char buffer[3] = {0};
|
||||
|
||||
get_pagebounds(argv[1], argv[2], &va_start, &va_end);
|
||||
|
||||
if(va_start == va_end) va_end += PGSIZE;
|
||||
while(va_start < va_end) {
|
||||
pte_t* pte = pgdir_walk(kern_pgdir, (const void*) va_start, 0);
|
||||
cprintf(VA_TXT " 0x%08p -> ", va_start);
|
||||
|
||||
if(pte && (*pte & PTE_P)) {
|
||||
cprintf(PA_TXT " 0x%08p (%s)\n", PTE_ADDR(*pte),
|
||||
decode_pte_perms(*pte, buffer));
|
||||
} else {
|
||||
cprintf(PA_TXT " ------------\n");
|
||||
}
|
||||
|
||||
va_start += PGSIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mon_mperms(int argc, char** argv, struct Trapframe* tf) {
|
||||
EXPECT_ARGS(3, argc);
|
||||
pte_t perms = 0;
|
||||
enum {
|
||||
PERM_ADD,
|
||||
PERM_REMOVE,
|
||||
PERM_SET
|
||||
} pmode;
|
||||
|
||||
const char* str = argv[1];
|
||||
if(str[0] == '+') { pmode = PERM_ADD; str++; }
|
||||
else if(str[0] == '-') { pmode = PERM_REMOVE; str++; }
|
||||
else pmode = PERM_SET;
|
||||
|
||||
while(*str) {
|
||||
if(*str == 'w') perms |= PTE_W;
|
||||
else if(*str == 'u') perms |= PTE_U;
|
||||
else {
|
||||
cprintf(ACOL_ERR("Unknown permission character %c\n"), *str);
|
||||
return 1;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
uintptr_t va_start, va_end;
|
||||
get_pagebounds(argv[2], argv[3], &va_start, &va_end);
|
||||
if(va_start == va_end) va_end += PGSIZE;
|
||||
while(va_start < va_end) {
|
||||
pte_t* pte = pgdir_walk(kern_pgdir, (void*) va_start, 0);
|
||||
if(!pte || !(*pte & PTE_P)) { va_start += PGSIZE; continue; }
|
||||
|
||||
if(pmode == PERM_ADD) {
|
||||
*pte |= perms;
|
||||
} else if(pmode == PERM_REMOVE) {
|
||||
*pte &= ~perms;
|
||||
} else if(pmode == PERM_SET) {
|
||||
*pte = PTE_ADDR(*pte) | perms | PTE_P;
|
||||
}
|
||||
va_start += PGSIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This is a nice thought and all...
|
||||
// But we should modify the trap frame.
|
||||
// I feel stupid.
|
||||
static inline void
|
||||
set_eflag(uint16_t flagno, int value) {
|
||||
uint32_t temp;
|
||||
uint32_t mask = ~(1 << flagno);
|
||||
uint32_t regv = value << flagno;
|
||||
asm volatile("pushfl\n\t"
|
||||
"pop %w0\n\t"
|
||||
"and %w1, %w0\n\t"
|
||||
"or %w2, %w0\n\t"
|
||||
"push %w0\n\t"
|
||||
"popfl\n\t"
|
||||
: "=r" (temp)
|
||||
: "r" (mask), "r" (regv));
|
||||
}
|
||||
|
||||
#define EXPECT_BRKPT if(!(tf->tf_trapno == T_BRKPT || tf->tf_trapno == T_DEBUG)) { \
|
||||
cprintf(ACOL_ERR("I don't think I should resume from this.\n")); \
|
||||
return 0; }
|
||||
#define EFLAGS_TF 0x8
|
||||
|
||||
int mon_resume(int argc, char** argv, struct Trapframe* tf) {
|
||||
EXPECT_BRKPT;
|
||||
tf->tf_eflags &= ~(1 << EFLAGS_TF);
|
||||
env_pop_tf(tf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mon_step(int argc, char** argv, struct Trapframe* tf) {
|
||||
EXPECT_BRKPT;
|
||||
tf->tf_eflags |= 1 << EFLAGS_TF;
|
||||
env_pop_tf(tf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***** Kernel monitor command interpreter *****/
|
||||
|
||||
|
@ -138,6 +280,8 @@ monitor(struct Trapframe *tf)
|
|||
cprintf("Welcome to the JOS kernel monitor!\n");
|
||||
cprintf("Type 'help' for a list of commands.\n");
|
||||
|
||||
if (tf != NULL)
|
||||
print_trapframe(tf);
|
||||
|
||||
while (1) {
|
||||
buf = readline("K> ");
|
||||
|
|
|
@ -15,5 +15,9 @@ void monitor(struct Trapframe *tf);
|
|||
int mon_help(int argc, char **argv, struct Trapframe *tf);
|
||||
int mon_kerninfo(int argc, char **argv, struct Trapframe *tf);
|
||||
int mon_backtrace(int argc, char **argv, struct Trapframe *tf);
|
||||
int mon_showmappings(int argc, char **argv, struct Trapframe *tf);
|
||||
int mon_mperms(int argc, char** argv, struct Trapframe* tf);
|
||||
int mon_resume(int argc, char** argv, struct Trapframe* tf);
|
||||
int mon_step(int argc, char** argv, struct Trapframe* tf);
|
||||
|
||||
#endif // !JOS_KERN_MONITOR_H
|
||||
|
|
225
kern/mpconfig.c
Normal file
225
kern/mpconfig.c
Normal file
|
@ -0,0 +1,225 @@
|
|||
// Search for and parse the multiprocessor configuration table
|
||||
// See http://developer.intel.com/design/pentium/datashts/24201606.pdf
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/env.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/pmap.h>
|
||||
|
||||
struct CpuInfo cpus[NCPU];
|
||||
struct CpuInfo *bootcpu;
|
||||
int ismp;
|
||||
int ncpu;
|
||||
|
||||
// Per-CPU kernel stacks
|
||||
unsigned char percpu_kstacks[NCPU][KSTKSIZE]
|
||||
__attribute__ ((aligned(PGSIZE)));
|
||||
|
||||
|
||||
// See MultiProcessor Specification Version 1.[14]
|
||||
|
||||
struct mp { // floating pointer [MP 4.1]
|
||||
uint8_t signature[4]; // "_MP_"
|
||||
physaddr_t physaddr; // phys addr of MP config table
|
||||
uint8_t length; // 1
|
||||
uint8_t specrev; // [14]
|
||||
uint8_t checksum; // all bytes must add up to 0
|
||||
uint8_t type; // MP system config type
|
||||
uint8_t imcrp;
|
||||
uint8_t reserved[3];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct mpconf { // configuration table header [MP 4.2]
|
||||
uint8_t signature[4]; // "PCMP"
|
||||
uint16_t length; // total table length
|
||||
uint8_t version; // [14]
|
||||
uint8_t checksum; // all bytes must add up to 0
|
||||
uint8_t product[20]; // product id
|
||||
physaddr_t oemtable; // OEM table pointer
|
||||
uint16_t oemlength; // OEM table length
|
||||
uint16_t entry; // entry count
|
||||
physaddr_t lapicaddr; // address of local APIC
|
||||
uint16_t xlength; // extended table length
|
||||
uint8_t xchecksum; // extended table checksum
|
||||
uint8_t reserved;
|
||||
uint8_t entries[0]; // table entries
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct mpproc { // processor table entry [MP 4.3.1]
|
||||
uint8_t type; // entry type (0)
|
||||
uint8_t apicid; // local APIC id
|
||||
uint8_t version; // local APIC version
|
||||
uint8_t flags; // CPU flags
|
||||
uint8_t signature[4]; // CPU signature
|
||||
uint32_t feature; // feature flags from CPUID instruction
|
||||
uint8_t reserved[8];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
// mpproc flags
|
||||
#define MPPROC_BOOT 0x02 // This mpproc is the bootstrap processor
|
||||
|
||||
// Table entry types
|
||||
#define MPPROC 0x00 // One per processor
|
||||
#define MPBUS 0x01 // One per bus
|
||||
#define MPIOAPIC 0x02 // One per I/O APIC
|
||||
#define MPIOINTR 0x03 // One per bus interrupt source
|
||||
#define MPLINTR 0x04 // One per system interrupt source
|
||||
|
||||
static uint8_t
|
||||
sum(void *addr, int len)
|
||||
{
|
||||
int i, sum;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
sum += ((uint8_t *)addr)[i];
|
||||
return sum;
|
||||
}
|
||||
|
||||
// Look for an MP structure in the len bytes at physical address addr.
|
||||
static struct mp *
|
||||
mpsearch1(physaddr_t a, int len)
|
||||
{
|
||||
struct mp *mp = KADDR(a), *end = KADDR(a + len);
|
||||
|
||||
for (; mp < end; mp++)
|
||||
if (memcmp(mp->signature, "_MP_", 4) == 0 &&
|
||||
sum(mp, sizeof(*mp)) == 0)
|
||||
return mp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Search for the MP Floating Pointer Structure, which according to
|
||||
// [MP 4] is in one of the following three locations:
|
||||
// 1) in the first KB of the EBDA;
|
||||
// 2) if there is no EBDA, in the last KB of system base memory;
|
||||
// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
|
||||
static struct mp *
|
||||
mpsearch(void)
|
||||
{
|
||||
uint8_t *bda;
|
||||
uint32_t p;
|
||||
struct mp *mp;
|
||||
|
||||
static_assert(sizeof(*mp) == 16);
|
||||
|
||||
// The BIOS data area lives in 16-bit segment 0x40.
|
||||
bda = (uint8_t *) KADDR(0x40 << 4);
|
||||
|
||||
// [MP 4] The 16-bit segment of the EBDA is in the two bytes
|
||||
// starting at byte 0x0E of the BDA. 0 if not present.
|
||||
if ((p = *(uint16_t *) (bda + 0x0E))) {
|
||||
p <<= 4; // Translate from segment to PA
|
||||
if ((mp = mpsearch1(p, 1024)))
|
||||
return mp;
|
||||
} else {
|
||||
// The size of base memory, in KB is in the two bytes
|
||||
// starting at 0x13 of the BDA.
|
||||
p = *(uint16_t *) (bda + 0x13) * 1024;
|
||||
if ((mp = mpsearch1(p - 1024, 1024)))
|
||||
return mp;
|
||||
}
|
||||
return mpsearch1(0xF0000, 0x10000);
|
||||
}
|
||||
|
||||
// Search for an MP configuration table. For now, don't accept the
|
||||
// default configurations (physaddr == 0).
|
||||
// Check for the correct signature, checksum, and version.
|
||||
static struct mpconf *
|
||||
mpconfig(struct mp **pmp)
|
||||
{
|
||||
struct mpconf *conf;
|
||||
struct mp *mp;
|
||||
|
||||
if ((mp = mpsearch()) == 0)
|
||||
return NULL;
|
||||
if (mp->physaddr == 0 || mp->type != 0) {
|
||||
cprintf("SMP: Default configurations not implemented\n");
|
||||
return NULL;
|
||||
}
|
||||
conf = (struct mpconf *) KADDR(mp->physaddr);
|
||||
if (memcmp(conf, "PCMP", 4) != 0) {
|
||||
cprintf("SMP: Incorrect MP configuration table signature\n");
|
||||
return NULL;
|
||||
}
|
||||
if (sum(conf, conf->length) != 0) {
|
||||
cprintf("SMP: Bad MP configuration checksum\n");
|
||||
return NULL;
|
||||
}
|
||||
if (conf->version != 1 && conf->version != 4) {
|
||||
cprintf("SMP: Unsupported MP version %d\n", conf->version);
|
||||
return NULL;
|
||||
}
|
||||
if ((sum((uint8_t *)conf + conf->length, conf->xlength) + conf->xchecksum) & 0xff) {
|
||||
cprintf("SMP: Bad MP configuration extended checksum\n");
|
||||
return NULL;
|
||||
}
|
||||
*pmp = mp;
|
||||
return conf;
|
||||
}
|
||||
|
||||
void
|
||||
mp_init(void)
|
||||
{
|
||||
struct mp *mp;
|
||||
struct mpconf *conf;
|
||||
struct mpproc *proc;
|
||||
uint8_t *p;
|
||||
unsigned int i;
|
||||
|
||||
bootcpu = &cpus[0];
|
||||
if ((conf = mpconfig(&mp)) == 0)
|
||||
return;
|
||||
ismp = 1;
|
||||
lapicaddr = conf->lapicaddr;
|
||||
|
||||
for (p = conf->entries, i = 0; i < conf->entry; i++) {
|
||||
switch (*p) {
|
||||
case MPPROC:
|
||||
proc = (struct mpproc *)p;
|
||||
if (proc->flags & MPPROC_BOOT)
|
||||
bootcpu = &cpus[ncpu];
|
||||
if (ncpu < NCPU) {
|
||||
cpus[ncpu].cpu_id = ncpu;
|
||||
ncpu++;
|
||||
} else {
|
||||
cprintf("SMP: too many CPUs, CPU %d disabled\n",
|
||||
proc->apicid);
|
||||
}
|
||||
p += sizeof(struct mpproc);
|
||||
continue;
|
||||
case MPBUS:
|
||||
case MPIOAPIC:
|
||||
case MPIOINTR:
|
||||
case MPLINTR:
|
||||
p += 8;
|
||||
continue;
|
||||
default:
|
||||
cprintf("mpinit: unknown config type %x\n", *p);
|
||||
ismp = 0;
|
||||
i = conf->entry;
|
||||
}
|
||||
}
|
||||
|
||||
bootcpu->cpu_status = CPU_STARTED;
|
||||
if (!ismp) {
|
||||
// Didn't like what we found; fall back to no MP.
|
||||
ncpu = 1;
|
||||
lapicaddr = 0;
|
||||
cprintf("SMP: configuration not found, SMP disabled\n");
|
||||
return;
|
||||
}
|
||||
cprintf("SMP: CPU %d found %d CPU(s)\n", bootcpu->cpu_id, ncpu);
|
||||
|
||||
if (mp->imcrp) {
|
||||
// [MP 3.2.6.1] If the hardware implements PIC mode,
|
||||
// switch to getting interrupts from the LAPIC.
|
||||
cprintf("SMP: Setting IMCR to switch from PIC mode to symmetric I/O mode\n");
|
||||
outb(0x22, 0x70); // Select IMCR
|
||||
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
|
||||
}
|
||||
}
|
97
kern/mpentry.S
Normal file
97
kern/mpentry.S
Normal file
|
@ -0,0 +1,97 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
|
||||
###################################################################
|
||||
# entry point for APs
|
||||
###################################################################
|
||||
|
||||
# Each non-boot CPU ("AP") is started up in response to a STARTUP
|
||||
# IPI from the boot CPU. Section B.4.2 of the Multi-Processor
|
||||
# Specification says that the AP will start in real mode with CS:IP
|
||||
# set to XY00:0000, where XY is an 8-bit value sent with the
|
||||
# STARTUP. Thus this code must start at a 4096-byte boundary.
|
||||
#
|
||||
# Because this code sets DS to zero, it must run from an address in
|
||||
# the low 2^16 bytes of physical memory.
|
||||
#
|
||||
# boot_aps() (in init.c) copies this code to MPENTRY_PADDR (which
|
||||
# satisfies the above restrictions). Then, for each AP, it stores the
|
||||
# address of the pre-allocated per-core stack in mpentry_kstack, sends
|
||||
# the STARTUP IPI, and waits for this code to acknowledge that it has
|
||||
# started (which happens in mp_main in init.c).
|
||||
#
|
||||
# This code is similar to boot/boot.S except that
|
||||
# - it does not need to enable A20
|
||||
# - it uses MPBOOTPHYS to calculate absolute addresses of its
|
||||
# symbols, rather than relying on the linker to fill them
|
||||
|
||||
#define RELOC(x) ((x) - KERNBASE)
|
||||
#define MPBOOTPHYS(s) ((s) - mpentry_start + MPENTRY_PADDR)
|
||||
|
||||
.set PROT_MODE_CSEG, 0x8 # kernel code segment selector
|
||||
.set PROT_MODE_DSEG, 0x10 # kernel data segment selector
|
||||
|
||||
.code16
|
||||
.globl mpentry_start
|
||||
mpentry_start:
|
||||
cli
|
||||
|
||||
xorw %ax, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
|
||||
lgdt MPBOOTPHYS(gdtdesc)
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
ljmpl $(PROT_MODE_CSEG), $(MPBOOTPHYS(start32))
|
||||
|
||||
.code32
|
||||
start32:
|
||||
movw $(PROT_MODE_DSEG), %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
movw $0, %ax
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
|
||||
# Set up initial page table. We cannot use kern_pgdir yet because
|
||||
# we are still running at a low EIP.
|
||||
movl $(RELOC(entry_pgdir)), %eax
|
||||
movl %eax, %cr3
|
||||
# Turn on paging.
|
||||
movl %cr0, %eax
|
||||
orl $(CR0_PE|CR0_PG|CR0_WP), %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Switch to the per-cpu stack allocated in boot_aps()
|
||||
movl mpentry_kstack, %esp
|
||||
movl $0x0, %ebp # nuke frame pointer
|
||||
|
||||
# Call mp_main(). (Exercise for the reader: why the indirect call?)
|
||||
movl $mp_main, %eax
|
||||
call *%eax
|
||||
|
||||
# If mp_main returns (it shouldn't), loop.
|
||||
spin:
|
||||
jmp spin
|
||||
|
||||
# Bootstrap GDT
|
||||
.p2align 2 # force 4 byte alignment
|
||||
gdt:
|
||||
SEG_NULL # null seg
|
||||
SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg
|
||||
SEG(STA_W, 0x0, 0xffffffff) # data seg
|
||||
|
||||
gdtdesc:
|
||||
.word 0x17 # sizeof(gdt) - 1
|
||||
.long MPBOOTPHYS(gdt) # address gdt
|
||||
|
||||
.globl mpentry_end
|
||||
mpentry_end:
|
||||
nop
|
86
kern/picirq.c
Normal file
86
kern/picirq.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/assert.h>
|
||||
#include <inc/trap.h>
|
||||
|
||||
#include <kern/picirq.h>
|
||||
|
||||
|
||||
// Current IRQ mask.
|
||||
// Initial IRQ mask has interrupt 2 enabled (for slave 8259A).
|
||||
uint16_t irq_mask_8259A = 0xFFFF & ~(1<<IRQ_SLAVE);
|
||||
static bool didinit;
|
||||
|
||||
/* Initialize the 8259A interrupt controllers. */
|
||||
void
|
||||
pic_init(void)
|
||||
{
|
||||
didinit = 1;
|
||||
|
||||
// mask all interrupts
|
||||
outb(IO_PIC1+1, 0xFF);
|
||||
outb(IO_PIC2+1, 0xFF);
|
||||
|
||||
// Set up master (8259A-1)
|
||||
|
||||
// ICW1: 0001g0hi
|
||||
// g: 0 = edge triggering, 1 = level triggering
|
||||
// h: 0 = cascaded PICs, 1 = master only
|
||||
// i: 0 = no ICW4, 1 = ICW4 required
|
||||
outb(IO_PIC1, 0x11);
|
||||
|
||||
// ICW2: Vector offset
|
||||
outb(IO_PIC1+1, IRQ_OFFSET);
|
||||
|
||||
// ICW3: bit mask of IR lines connected to slave PICs (master PIC),
|
||||
// 3-bit No of IR line at which slave connects to master(slave PIC).
|
||||
outb(IO_PIC1+1, 1<<IRQ_SLAVE);
|
||||
|
||||
// ICW4: 000nbmap
|
||||
// n: 1 = special fully nested mode
|
||||
// b: 1 = buffered mode
|
||||
// m: 0 = slave PIC, 1 = master PIC
|
||||
// (ignored when b is 0, as the master/slave role
|
||||
// can be hardwired).
|
||||
// a: 1 = Automatic EOI mode
|
||||
// p: 0 = MCS-80/85 mode, 1 = intel x86 mode
|
||||
outb(IO_PIC1+1, 0x3);
|
||||
|
||||
// Set up slave (8259A-2)
|
||||
outb(IO_PIC2, 0x11); // ICW1
|
||||
outb(IO_PIC2+1, IRQ_OFFSET + 8); // ICW2
|
||||
outb(IO_PIC2+1, IRQ_SLAVE); // ICW3
|
||||
// NB Automatic EOI mode doesn't tend to work on the slave.
|
||||
// Linux source code says it's "to be investigated".
|
||||
outb(IO_PIC2+1, 0x01); // ICW4
|
||||
|
||||
// OCW3: 0ef01prs
|
||||
// ef: 0x = NOP, 10 = clear specific mask, 11 = set specific mask
|
||||
// p: 0 = no polling, 1 = polling mode
|
||||
// rs: 0x = NOP, 10 = read IRR, 11 = read ISR
|
||||
outb(IO_PIC1, 0x68); /* clear specific mask */
|
||||
outb(IO_PIC1, 0x0a); /* read IRR by default */
|
||||
|
||||
outb(IO_PIC2, 0x68); /* OCW3 */
|
||||
outb(IO_PIC2, 0x0a); /* OCW3 */
|
||||
|
||||
if (irq_mask_8259A != 0xFFFF)
|
||||
irq_setmask_8259A(irq_mask_8259A);
|
||||
}
|
||||
|
||||
void
|
||||
irq_setmask_8259A(uint16_t mask)
|
||||
{
|
||||
int i;
|
||||
irq_mask_8259A = mask;
|
||||
if (!didinit)
|
||||
return;
|
||||
outb(IO_PIC1+1, (char)mask);
|
||||
outb(IO_PIC2+1, (char)(mask >> 8));
|
||||
cprintf("enabled interrupts:");
|
||||
for (i = 0; i < 16; i++)
|
||||
if (~mask & (1<<i))
|
||||
cprintf(" %d", i);
|
||||
cprintf("\n");
|
||||
}
|
||||
|
28
kern/picirq.h
Normal file
28
kern/picirq.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_PICIRQ_H
|
||||
#define JOS_KERN_PICIRQ_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#define MAX_IRQS 16 // Number of IRQs
|
||||
|
||||
// I/O Addresses of the two 8259A programmable interrupt controllers
|
||||
#define IO_PIC1 0x20 // Master (IRQs 0-7)
|
||||
#define IO_PIC2 0xA0 // Slave (IRQs 8-15)
|
||||
|
||||
#define IRQ_SLAVE 2 // IRQ at which slave connects to master
|
||||
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/x86.h>
|
||||
|
||||
extern uint16_t irq_mask_8259A;
|
||||
void pic_init(void);
|
||||
void irq_setmask_8259A(uint16_t mask);
|
||||
#endif // !__ASSEMBLER__
|
||||
|
||||
#endif // !JOS_KERN_PICIRQ_H
|
1125
kern/pmap.c
Normal file
1125
kern/pmap.c
Normal file
File diff suppressed because it is too large
Load Diff
93
kern/pmap.h
Normal file
93
kern/pmap.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_PMAP_H
|
||||
#define JOS_KERN_PMAP_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/assert.h>
|
||||
struct Env;
|
||||
|
||||
extern char bootstacktop[], bootstack[];
|
||||
|
||||
extern struct PageInfo *pages;
|
||||
extern size_t npages;
|
||||
|
||||
extern pde_t *kern_pgdir;
|
||||
|
||||
|
||||
/* This macro takes a kernel virtual address -- an address that points above
|
||||
* KERNBASE, where the machine's maximum 256MB of physical memory is mapped --
|
||||
* and returns the corresponding physical address. It panics if you pass it a
|
||||
* non-kernel virtual address.
|
||||
*/
|
||||
#define PADDR(kva) _paddr(__FILE__, __LINE__, kva)
|
||||
|
||||
static inline physaddr_t
|
||||
_paddr(const char *file, int line, void *kva)
|
||||
{
|
||||
if ((uint32_t)kva < KERNBASE)
|
||||
_panic(file, line, "PADDR called with invalid kva %08lx", kva);
|
||||
return (physaddr_t)kva - KERNBASE;
|
||||
}
|
||||
|
||||
/* This macro takes a physical address and returns the corresponding kernel
|
||||
* virtual address. It panics if you pass an invalid physical address. */
|
||||
#define KADDR(pa) _kaddr(__FILE__, __LINE__, pa)
|
||||
|
||||
static inline void*
|
||||
_kaddr(const char *file, int line, physaddr_t pa)
|
||||
{
|
||||
if (PGNUM(pa) >= npages)
|
||||
_panic(file, line, "KADDR called with invalid pa %08lx", pa);
|
||||
return (void *)(pa + KERNBASE);
|
||||
}
|
||||
|
||||
|
||||
enum {
|
||||
// For page_alloc, zero the returned physical page.
|
||||
ALLOC_ZERO = 1<<0,
|
||||
};
|
||||
|
||||
void mem_init(void);
|
||||
|
||||
void page_init(void);
|
||||
struct PageInfo *page_alloc(int alloc_flags);
|
||||
void page_free(struct PageInfo *pp);
|
||||
int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm);
|
||||
void page_remove(pde_t *pgdir, void *va);
|
||||
struct PageInfo *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store);
|
||||
void page_decref(struct PageInfo *pp);
|
||||
|
||||
void tlb_invalidate(pde_t *pgdir, void *va);
|
||||
|
||||
void * mmio_map_region(physaddr_t pa, size_t size);
|
||||
|
||||
int user_mem_check(struct Env *env, const void *va, size_t len, int perm);
|
||||
void user_mem_assert(struct Env *env, const void *va, size_t len, int perm);
|
||||
|
||||
static inline physaddr_t
|
||||
page2pa(struct PageInfo *pp)
|
||||
{
|
||||
return (pp - pages) << PGSHIFT;
|
||||
}
|
||||
|
||||
static inline struct PageInfo*
|
||||
pa2page(physaddr_t pa)
|
||||
{
|
||||
if (PGNUM(pa) >= npages)
|
||||
panic("pa2page called with invalid pa");
|
||||
return &pages[PGNUM(pa)];
|
||||
}
|
||||
|
||||
static inline void*
|
||||
page2kva(struct PageInfo *pp)
|
||||
{
|
||||
return KADDR(page2pa(pp));
|
||||
}
|
||||
|
||||
pte_t *pgdir_walk(pde_t *pgdir, const void *va, int create);
|
||||
|
||||
#endif /* !JOS_KERN_PMAP_H */
|
102
kern/sched.c
Normal file
102
kern/sched.c
Normal file
|
@ -0,0 +1,102 @@
|
|||
#include <inc/assert.h>
|
||||
#include <inc/x86.h>
|
||||
#include <kern/spinlock.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/monitor.h>
|
||||
|
||||
void sched_halt(void);
|
||||
|
||||
// Choose a user environment to run and run it.
|
||||
void
|
||||
sched_yield(void)
|
||||
{
|
||||
struct Env *idle;
|
||||
|
||||
// Implement simple round-robin scheduling.
|
||||
//
|
||||
// Search through 'envs' for an ENV_RUNNABLE environment in
|
||||
// circular fashion starting just after the env this CPU was
|
||||
// last running. Switch to the first such environment found.
|
||||
//
|
||||
// If no envs are runnable, but the environment previously
|
||||
// running on this CPU is still ENV_RUNNING, it's okay to
|
||||
// choose that environment.
|
||||
//
|
||||
// Never choose an environment that's currently running on
|
||||
// another CPU (env_status == ENV_RUNNING). If there are
|
||||
// no runnable environments, simply drop through to the code
|
||||
// below to halt the cpu.
|
||||
|
||||
// LAB 4: Your code here.
|
||||
struct Env* next_env = curenv ? curenv + 1 : envs;
|
||||
struct Env* end_env = envs + NENV;
|
||||
struct Env* to_run = NULL;
|
||||
|
||||
for(int i = 0; i < NENV; i++, next_env++) {
|
||||
if(next_env == end_env) next_env = envs;
|
||||
if(next_env->env_status == ENV_RUNNABLE) {
|
||||
to_run = next_env;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!to_run && curenv && curenv->env_status == ENV_RUNNING) {
|
||||
to_run = curenv;
|
||||
}
|
||||
|
||||
if(to_run) env_run(to_run);
|
||||
|
||||
// sched_halt never returns
|
||||
sched_halt();
|
||||
}
|
||||
|
||||
// Halt this CPU when there is nothing to do. Wait until the
|
||||
// timer interrupt wakes it up. This function never returns.
|
||||
//
|
||||
void
|
||||
sched_halt(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
// For debugging and testing purposes, if there are no runnable
|
||||
// environments in the system, then drop into the kernel monitor.
|
||||
for (i = 0; i < NENV; i++) {
|
||||
if ((envs[i].env_status == ENV_RUNNABLE ||
|
||||
envs[i].env_status == ENV_RUNNING ||
|
||||
envs[i].env_status == ENV_DYING))
|
||||
break;
|
||||
}
|
||||
if (i == NENV) {
|
||||
cprintf("No runnable environments in the system!\n");
|
||||
while (1)
|
||||
monitor(NULL);
|
||||
}
|
||||
|
||||
// Mark that no environment is running on this CPU
|
||||
curenv = NULL;
|
||||
lcr3(PADDR(kern_pgdir));
|
||||
|
||||
// Mark that this CPU is in the HALT state, so that when
|
||||
// timer interupts come in, we know we should re-acquire the
|
||||
// big kernel lock
|
||||
xchg(&thiscpu->cpu_status, CPU_HALTED);
|
||||
|
||||
// Release the big kernel lock as if we were "leaving" the kernel
|
||||
unlock_kernel();
|
||||
|
||||
// Reset stack pointer, enable interrupts and then halt.
|
||||
asm volatile (
|
||||
"movl $0, %%ebp\n"
|
||||
"movl %0, %%esp\n"
|
||||
"pushl $0\n"
|
||||
"pushl $0\n"
|
||||
// LAB 4:
|
||||
// Uncomment the following line after completing exercise 13
|
||||
"sti\n"
|
||||
"1:\n"
|
||||
"hlt\n"
|
||||
"jmp 1b\n"
|
||||
: : "a" (thiscpu->cpu_ts.ts_esp0));
|
||||
}
|
||||
|
12
kern/sched.h
Normal file
12
kern/sched.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_SCHED_H
|
||||
#define JOS_KERN_SCHED_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
// This function does not return.
|
||||
void sched_yield(void) __attribute__((noreturn));
|
||||
|
||||
#endif // !JOS_KERN_SCHED_H
|
116
kern/spinlock.c
Normal file
116
kern/spinlock.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Mutual exclusion spin locks.
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/assert.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/string.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
#include <kern/kdebug.h>
|
||||
|
||||
// The big kernel lock
|
||||
struct spinlock kernel_lock = {
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
.name = "kernel_lock"
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
// Record the current call stack in pcs[] by following the %ebp chain.
|
||||
static void
|
||||
get_caller_pcs(uint32_t pcs[])
|
||||
{
|
||||
uint32_t *ebp;
|
||||
int i;
|
||||
|
||||
ebp = (uint32_t *)read_ebp();
|
||||
for (i = 0; i < 10; i++){
|
||||
if (ebp == 0 || ebp < (uint32_t *)ULIM)
|
||||
break;
|
||||
pcs[i] = ebp[1]; // saved %eip
|
||||
ebp = (uint32_t *)ebp[0]; // saved %ebp
|
||||
}
|
||||
for (; i < 10; i++)
|
||||
pcs[i] = 0;
|
||||
}
|
||||
|
||||
// Check whether this CPU is holding the lock.
|
||||
static int
|
||||
holding(struct spinlock *lock)
|
||||
{
|
||||
return lock->locked && lock->cpu == thiscpu;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
__spin_initlock(struct spinlock *lk, char *name)
|
||||
{
|
||||
lk->locked = 0;
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
lk->name = name;
|
||||
lk->cpu = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Acquire the lock.
|
||||
// Loops (spins) until the lock is acquired.
|
||||
// Holding a lock for a long time may cause
|
||||
// other CPUs to waste time spinning to acquire it.
|
||||
void
|
||||
spin_lock(struct spinlock *lk)
|
||||
{
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
if (holding(lk))
|
||||
panic("CPU %d cannot acquire %s: already holding", cpunum(), lk->name);
|
||||
#endif
|
||||
|
||||
// The xchg is atomic.
|
||||
// It also serializes, so that reads after acquire are not
|
||||
// reordered before it.
|
||||
while (xchg(&lk->locked, 1) != 0)
|
||||
asm volatile ("pause");
|
||||
|
||||
// Record info about lock acquisition for debugging.
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
lk->cpu = thiscpu;
|
||||
get_caller_pcs(lk->pcs);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Release the lock.
|
||||
void
|
||||
spin_unlock(struct spinlock *lk)
|
||||
{
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
if (!holding(lk)) {
|
||||
int i;
|
||||
uint32_t pcs[10];
|
||||
// Nab the acquiring EIP chain before it gets released
|
||||
memmove(pcs, lk->pcs, sizeof pcs);
|
||||
cprintf("CPU %d cannot release %s: held by CPU %d\nAcquired at:",
|
||||
cpunum(), lk->name, lk->cpu->cpu_id);
|
||||
for (i = 0; i < 10 && pcs[i]; i++) {
|
||||
struct Eipdebuginfo info;
|
||||
if (debuginfo_eip(pcs[i], &info) >= 0)
|
||||
cprintf(" %08x %s:%d: %.*s+%x\n", pcs[i],
|
||||
info.eip_file, info.eip_line,
|
||||
info.eip_fn_namelen, info.eip_fn_name,
|
||||
pcs[i] - info.eip_fn_addr);
|
||||
else
|
||||
cprintf(" %08x\n", pcs[i]);
|
||||
}
|
||||
panic("spin_unlock");
|
||||
}
|
||||
|
||||
lk->pcs[0] = 0;
|
||||
lk->cpu = 0;
|
||||
#endif
|
||||
|
||||
// The xchg instruction is atomic (i.e. uses the "lock" prefix) with
|
||||
// respect to any other instruction which references the same memory.
|
||||
// x86 CPUs will not reorder loads/stores across locked instructions
|
||||
// (vol 3, 8.2.2). Because xchg() is implemented using asm volatile,
|
||||
// gcc will not reorder C statements across the xchg.
|
||||
xchg(&lk->locked, 0);
|
||||
}
|
48
kern/spinlock.h
Normal file
48
kern/spinlock.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#ifndef JOS_INC_SPINLOCK_H
|
||||
#define JOS_INC_SPINLOCK_H
|
||||
|
||||
#include <inc/types.h>
|
||||
|
||||
// Comment this to disable spinlock debugging
|
||||
#define DEBUG_SPINLOCK
|
||||
|
||||
// Mutual exclusion lock.
|
||||
struct spinlock {
|
||||
unsigned locked; // Is the lock held?
|
||||
|
||||
#ifdef DEBUG_SPINLOCK
|
||||
// For debugging:
|
||||
char *name; // Name of lock.
|
||||
struct CpuInfo *cpu; // The CPU holding the lock.
|
||||
uintptr_t pcs[10]; // The call stack (an array of program counters)
|
||||
// that locked the lock.
|
||||
#endif
|
||||
};
|
||||
|
||||
void __spin_initlock(struct spinlock *lk, char *name);
|
||||
void spin_lock(struct spinlock *lk);
|
||||
void spin_unlock(struct spinlock *lk);
|
||||
|
||||
#define spin_initlock(lock) __spin_initlock(lock, #lock)
|
||||
|
||||
extern struct spinlock kernel_lock;
|
||||
|
||||
static inline void
|
||||
lock_kernel(void)
|
||||
{
|
||||
spin_lock(&kernel_lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
unlock_kernel(void)
|
||||
{
|
||||
spin_unlock(&kernel_lock);
|
||||
|
||||
// Normally we wouldn't need to do this, but QEMU only runs
|
||||
// one CPU at a time and has a long time-slice. Without the
|
||||
// pause, this CPU is likely to reacquire the lock before
|
||||
// another CPU has even been given a chance to acquire it.
|
||||
asm volatile("pause");
|
||||
}
|
||||
|
||||
#endif
|
403
kern/syscall.c
Normal file
403
kern/syscall.c
Normal file
|
@ -0,0 +1,403 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/error.h>
|
||||
#include <inc/string.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/env.h>
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/sched.h>
|
||||
|
||||
// Print a string to the system console.
|
||||
// The string is exactly 'len' characters long.
|
||||
// Destroys the environment on memory errors.
|
||||
static void
|
||||
sys_cputs(const char *s, size_t len)
|
||||
{
|
||||
// Check that the user has permission to read memory [s, s+len).
|
||||
// Destroy the environment if not.
|
||||
user_mem_assert(curenv, s, len, 0);
|
||||
// Print the string supplied by the user.
|
||||
cprintf("%.*s", len, s);
|
||||
}
|
||||
|
||||
// Read a character from the system console without blocking.
|
||||
// Returns the character, or 0 if there is no input waiting.
|
||||
static int
|
||||
sys_cgetc(void)
|
||||
{
|
||||
return cons_getc();
|
||||
}
|
||||
|
||||
// Returns the current environment's envid.
|
||||
static envid_t
|
||||
sys_getenvid(void)
|
||||
{
|
||||
return curenv->env_id;
|
||||
}
|
||||
|
||||
// Destroy a given environment (possibly the currently running environment).
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
static int
|
||||
sys_env_destroy(envid_t envid)
|
||||
{
|
||||
int r;
|
||||
struct Env *e;
|
||||
|
||||
if ((r = envid2env(envid, &e, 1)) < 0)
|
||||
return r;
|
||||
if (e == curenv)
|
||||
cprintf("[%08x] exiting gracefully\n", curenv->env_id);
|
||||
else
|
||||
cprintf("[%08x] destroying %08x\n", curenv->env_id, e->env_id);
|
||||
env_destroy(e);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Deschedule current environment and pick a different one to run.
|
||||
static void
|
||||
sys_yield(void)
|
||||
{
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
// Allocate a new environment.
|
||||
// Returns envid of new environment, or < 0 on error. Errors are:
|
||||
// -E_NO_FREE_ENV if no free environment is available.
|
||||
// -E_NO_MEM on memory exhaustion.
|
||||
static envid_t
|
||||
sys_exofork(void)
|
||||
{
|
||||
// Create the new environment with env_alloc(), from kern/env.c.
|
||||
// It should be left as env_alloc created it, except that
|
||||
// status is set to ENV_NOT_RUNNABLE, and the register set is copied
|
||||
// from the current environment -- but tweaked so sys_exofork
|
||||
// will appear to return 0.
|
||||
struct Env* new_env;
|
||||
int error_code;
|
||||
|
||||
error_code = env_alloc(&new_env, curenv->env_id);
|
||||
if(error_code < 0) return error_code;
|
||||
|
||||
new_env->env_tf = curenv->env_tf;
|
||||
new_env->env_tf.tf_regs.reg_eax = 0;
|
||||
new_env->env_status = ENV_NOT_RUNNABLE;
|
||||
return new_env->env_id;
|
||||
}
|
||||
|
||||
// Set envid's env_status to status, which must be ENV_RUNNABLE
|
||||
// or ENV_NOT_RUNNABLE.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
// -E_INVAL if status is not a valid status for an environment.
|
||||
static int
|
||||
sys_env_set_status(envid_t envid, int status)
|
||||
{
|
||||
// Hint: Use the 'envid2env' function from kern/env.c to translate an
|
||||
// envid to a struct Env.
|
||||
// You should set envid2env's third argument to 1, which will
|
||||
// check whether the current environment has permission to set
|
||||
// envid's status.
|
||||
struct Env* env;
|
||||
int error_code;
|
||||
|
||||
error_code = envid2env(envid, &env, 1);
|
||||
if(error_code < 0) return error_code;
|
||||
|
||||
if(status != ENV_RUNNABLE && status != ENV_NOT_RUNNABLE)
|
||||
return -E_INVAL;
|
||||
|
||||
env->env_status = status;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the page fault upcall for 'envid' by modifying the corresponding struct
|
||||
// Env's 'env_pgfault_upcall' field. When 'envid' causes a page fault, the
|
||||
// kernel will push a fault record onto the exception stack, then branch to
|
||||
// 'func'.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
static int
|
||||
sys_env_set_pgfault_upcall(envid_t envid, void *func)
|
||||
{
|
||||
struct Env* env;
|
||||
int return_code;
|
||||
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
|
||||
env->env_pgfault_upcall = func;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SYS_CHECKPERMS(perm) \
|
||||
((((perm) & (PTE_P | PTE_U)) == (PTE_P | PTE_U)) && \
|
||||
(((perm) & ~(PTE_P | PTE_U | PTE_W | PTE_AVAIL)) == 0))
|
||||
#define SYS_CHECKADDR(addr) (((uintptr_t) (addr) < UTOP) && ((uintptr_t) (addr) % PGSIZE == 0))
|
||||
|
||||
// Allocate a page of memory and map it at 'va' with permission
|
||||
// 'perm' in the address space of 'envid'.
|
||||
// The page's contents are set to 0.
|
||||
// If a page is already mapped at 'va', that page is unmapped as a
|
||||
// side effect.
|
||||
//
|
||||
// perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set,
|
||||
// but no other bits may be set. See PTE_SYSCALL in inc/mmu.h.
|
||||
//
|
||||
// Return 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
// -E_INVAL if va >= UTOP, or va is not page-aligned.
|
||||
// -E_INVAL if perm is inappropriate (see above).
|
||||
// -E_NO_MEM if there's no memory to allocate the new page,
|
||||
// or to allocate any necessary page tables.
|
||||
static int
|
||||
sys_page_alloc(envid_t envid, void *va, int perm)
|
||||
{
|
||||
// Hint: This function is a wrapper around page_alloc() and
|
||||
// page_insert() from kern/pmap.c.
|
||||
// Most of the new code you write should be to check the
|
||||
// parameters for correctness.
|
||||
// If page_insert() fails, remember to free the page you
|
||||
// allocated!
|
||||
struct Env* env;
|
||||
int return_code;
|
||||
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
|
||||
|
||||
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
|
||||
if(!SYS_CHECKADDR(va)) return -E_INVAL;
|
||||
|
||||
struct PageInfo* page = page_alloc(1);
|
||||
if(!page) return -E_NO_MEM;
|
||||
|
||||
if((return_code = page_insert(env->env_pgdir, page, va, perm)) < 0) {
|
||||
page_free(page);
|
||||
return return_code;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Map the page of memory at 'srcva' in srcenvid's address space
|
||||
// at 'dstva' in dstenvid's address space with permission 'perm'.
|
||||
// Perm has the same restrictions as in sys_page_alloc, except
|
||||
// that it also must not grant write access to a read-only
|
||||
// page.
|
||||
//
|
||||
// Return 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change one of them.
|
||||
// -E_INVAL if srcva >= UTOP or srcva is not page-aligned,
|
||||
// or dstva >= UTOP or dstva is not page-aligned.
|
||||
// -E_INVAL is srcva is not mapped in srcenvid's address space.
|
||||
// -E_INVAL if perm is inappropriate (see sys_page_alloc).
|
||||
// -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's
|
||||
// address space.
|
||||
// -E_NO_MEM if there's no memory to allocate any necessary page tables.
|
||||
static int
|
||||
sys_page_map(envid_t srcenvid, void *srcva,
|
||||
envid_t dstenvid, void *dstva, int perm)
|
||||
{
|
||||
// Hint: This function is a wrapper around page_lookup() and
|
||||
// page_insert() from kern/pmap.c.
|
||||
// Again, most of the new code you write should be to check the
|
||||
// parameters for correctness.
|
||||
// Use the third argument to page_lookup() to
|
||||
// check the current permissions on the page.
|
||||
struct Env *srcenv, *dstenv;
|
||||
pte_t* srcpte;
|
||||
int return_code;
|
||||
|
||||
if((return_code = envid2env(srcenvid, &srcenv, 1)) < 0) return return_code;
|
||||
if((return_code = envid2env(dstenvid, &dstenv, 1)) < 0) return return_code;
|
||||
|
||||
if(!SYS_CHECKADDR(srcva)) return -E_INVAL;
|
||||
if(!SYS_CHECKADDR(dstva)) return -E_INVAL;
|
||||
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
|
||||
|
||||
struct PageInfo* page = page_lookup(srcenv->env_pgdir, srcva, &srcpte);
|
||||
if(page == NULL) return -E_INVAL;
|
||||
if(perm & PTE_W && !(*srcpte & PTE_W)) return -E_INVAL;
|
||||
|
||||
if((return_code = page_insert(dstenv->env_pgdir, page, dstva, perm)) < 0)
|
||||
return return_code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Unmap the page of memory at 'va' in the address space of 'envid'.
|
||||
// If no page is mapped, the function silently succeeds.
|
||||
//
|
||||
// Return 0 on success, < 0 on error. Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist,
|
||||
// or the caller doesn't have permission to change envid.
|
||||
// -E_INVAL if va >= UTOP, or va is not page-aligned.
|
||||
static int
|
||||
sys_page_unmap(envid_t envid, void *va)
|
||||
{
|
||||
// Hint: This function is a wrapper around page_remove().
|
||||
struct Env* env;
|
||||
int return_code;
|
||||
|
||||
if((return_code = envid2env(envid, &env, 1)) < 0) return return_code;
|
||||
if(!SYS_CHECKADDR(va)) return -E_INVAL;
|
||||
|
||||
page_remove(env->env_pgdir, va);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try to send 'value' to the target env 'envid'.
|
||||
// If srcva < UTOP, then also send page currently mapped at 'srcva',
|
||||
// so that receiver gets a duplicate mapping of the same page.
|
||||
//
|
||||
// The send fails with a return value of -E_IPC_NOT_RECV if the
|
||||
// target is not blocked, waiting for an IPC.
|
||||
//
|
||||
// The send also can fail for the other reasons listed below.
|
||||
//
|
||||
// Otherwise, the send succeeds, and the target's ipc fields are
|
||||
// updated as follows:
|
||||
// env_ipc_recving is set to 0 to block future sends;
|
||||
// env_ipc_from is set to the sending envid;
|
||||
// env_ipc_value is set to the 'value' parameter;
|
||||
// env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise.
|
||||
// The target environment is marked runnable again, returning 0
|
||||
// from the paused sys_ipc_recv system call. (Hint: does the
|
||||
// sys_ipc_recv function ever actually return?)
|
||||
//
|
||||
// If the sender wants to send a page but the receiver isn't asking for one,
|
||||
// then no page mapping is transferred, but no error occurs.
|
||||
// The ipc only happens when no errors occur.
|
||||
//
|
||||
// Returns 0 on success, < 0 on error.
|
||||
// Errors are:
|
||||
// -E_BAD_ENV if environment envid doesn't currently exist.
|
||||
// (No need to check permissions.)
|
||||
// -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv,
|
||||
// or another environment managed to send first.
|
||||
// -E_INVAL if srcva < UTOP but srcva is not page-aligned.
|
||||
// -E_INVAL if srcva < UTOP and perm is inappropriate
|
||||
// (see sys_page_alloc).
|
||||
// -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's
|
||||
// address space.
|
||||
// -E_INVAL if (perm & PTE_W), but srcva is read-only in the
|
||||
// current environment's address space.
|
||||
// -E_NO_MEM if there's not enough memory to map srcva in envid's
|
||||
// address space.
|
||||
static int
|
||||
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
|
||||
{
|
||||
struct Env* dest_env;
|
||||
struct Env* src_env;
|
||||
int return_code;
|
||||
|
||||
if((return_code = envid2env(0, &src_env, 0)) < 0)
|
||||
return return_code;
|
||||
if((return_code = envid2env(envid, &dest_env, 0)) < 0)
|
||||
return return_code;
|
||||
|
||||
if(!dest_env->env_ipc_recving)
|
||||
return -E_IPC_NOT_RECV;
|
||||
|
||||
if((uintptr_t) srcva < UTOP && dest_env->env_ipc_dstva) {
|
||||
if(!SYS_CHECKADDR(srcva)) return -E_INVAL;
|
||||
if(!SYS_CHECKPERMS(perm)) return -E_INVAL;
|
||||
|
||||
pte_t* srcpte;
|
||||
struct PageInfo* page = page_lookup(src_env->env_pgdir, srcva, &srcpte);
|
||||
if(page == NULL) return -E_INVAL;
|
||||
if(perm & PTE_W && !(*srcpte & PTE_W)) return -E_INVAL;
|
||||
|
||||
page_insert(dest_env->env_pgdir, page, dest_env->env_ipc_dstva, perm);
|
||||
dest_env->env_ipc_perm = perm;
|
||||
}
|
||||
|
||||
dest_env->env_ipc_from = src_env->env_id;
|
||||
dest_env->env_ipc_value = value;
|
||||
dest_env->env_ipc_recving = false;
|
||||
if(dest_env->env_status == ENV_NOT_RUNNABLE)
|
||||
dest_env->env_status = ENV_RUNNABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Block until a value is ready. Record that you want to receive
|
||||
// using the env_ipc_recving and env_ipc_dstva fields of struct Env,
|
||||
// mark yourself not runnable, and then give up the CPU.
|
||||
//
|
||||
// If 'dstva' is < UTOP, then you are willing to receive a page of data.
|
||||
// 'dstva' is the virtual address at which the sent page should be mapped.
|
||||
//
|
||||
// This function only returns on error, but the system call will eventually
|
||||
// return 0 on success.
|
||||
// Return < 0 on error. Errors are:
|
||||
// -E_INVAL if dstva < UTOP but dstva is not page-aligned.
|
||||
static int
|
||||
sys_ipc_recv(void *dstva)
|
||||
{
|
||||
struct Env* env;
|
||||
int return_code;
|
||||
|
||||
if((return_code = envid2env(0, &env, 1)) < 0)
|
||||
return return_code;
|
||||
|
||||
// LAB 4: Your code here.
|
||||
if((uintptr_t) dstva < UTOP) {
|
||||
if(!SYS_CHECKADDR(dstva)) return -E_INVAL;
|
||||
env->env_ipc_dstva = dstva;
|
||||
}
|
||||
env->env_ipc_recving = true;
|
||||
env->env_status = ENV_NOT_RUNNABLE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Dispatches to the correct kernel function, passing the arguments.
|
||||
int32_t
|
||||
syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
|
||||
{
|
||||
// Call the function corresponding to the 'syscallno' parameter.
|
||||
// Return any appropriate return value.
|
||||
// LAB 3: Your code here.
|
||||
|
||||
switch (syscallno) {
|
||||
case SYS_cputs:
|
||||
sys_cputs((const char*) a1, a2);
|
||||
return 0;
|
||||
case SYS_cgetc:
|
||||
return sys_cgetc();
|
||||
case SYS_getenvid:
|
||||
return sys_getenvid();
|
||||
case SYS_env_destroy:
|
||||
return sys_env_destroy(a1);
|
||||
case SYS_yield:
|
||||
sys_yield();
|
||||
return 0;
|
||||
case SYS_exofork:
|
||||
return sys_exofork();
|
||||
case SYS_env_set_status:
|
||||
return sys_env_set_status(a1, a2);
|
||||
case SYS_env_set_pgfault_upcall:
|
||||
return sys_env_set_pgfault_upcall(a1, (void*) a2);
|
||||
case SYS_page_alloc:
|
||||
return sys_page_alloc(a1, (void*) a2, a3);
|
||||
case SYS_page_map:
|
||||
return sys_page_map(a1, (void*) a2, a3, (void*) a4, a5);
|
||||
case SYS_page_unmap:
|
||||
return sys_page_unmap(a1, (void*) a2);
|
||||
case SYS_ipc_try_send:
|
||||
return sys_ipc_try_send(a1, a2, (void*) a3, a4);
|
||||
case SYS_ipc_recv:
|
||||
return sys_ipc_recv((void*) a1);
|
||||
default:
|
||||
return -E_INVAL;
|
||||
}
|
||||
}
|
||||
|
11
kern/syscall.h
Normal file
11
kern/syscall.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef JOS_KERN_SYSCALL_H
|
||||
#define JOS_KERN_SYSCALL_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#include <inc/syscall.h>
|
||||
|
||||
int32_t syscall(uint32_t num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5);
|
||||
|
||||
#endif /* !JOS_KERN_SYSCALL_H */
|
429
kern/trap.c
Normal file
429
kern/trap.c
Normal file
|
@ -0,0 +1,429 @@
|
|||
#include <inc/mmu.h>
|
||||
#include <inc/x86.h>
|
||||
#include <inc/assert.h>
|
||||
|
||||
#include <kern/pmap.h>
|
||||
#include <kern/trap.h>
|
||||
#include <kern/console.h>
|
||||
#include <kern/monitor.h>
|
||||
#include <kern/env.h>
|
||||
#include <kern/syscall.h>
|
||||
#include <kern/sched.h>
|
||||
#include <kern/kclock.h>
|
||||
#include <kern/picirq.h>
|
||||
#include <kern/cpu.h>
|
||||
#include <kern/spinlock.h>
|
||||
|
||||
static struct Taskstate ts;
|
||||
|
||||
/* For debugging, so print_trapframe can distinguish between printing
|
||||
* a saved trapframe and printing the current trapframe and print some
|
||||
* additional information in the latter case.
|
||||
*/
|
||||
static struct Trapframe *last_tf;
|
||||
|
||||
/* Interrupt descriptor table. (Must be built at run time because
|
||||
* shifted function addresses can't be represented in relocation records.)
|
||||
*/
|
||||
struct Gatedesc idt[256] = { { 0 } };
|
||||
struct Pseudodesc idt_pd = {
|
||||
sizeof(idt) - 1, (uint32_t) idt
|
||||
};
|
||||
|
||||
|
||||
static const char *trapname(int trapno)
|
||||
{
|
||||
static const char * const excnames[] = {
|
||||
"Divide error",
|
||||
"Debug",
|
||||
"Non-Maskable Interrupt",
|
||||
"Breakpoint",
|
||||
"Overflow",
|
||||
"BOUND Range Exceeded",
|
||||
"Invalid Opcode",
|
||||
"Device Not Available",
|
||||
"Double Fault",
|
||||
"Coprocessor Segment Overrun",
|
||||
"Invalid TSS",
|
||||
"Segment Not Present",
|
||||
"Stack Fault",
|
||||
"General Protection",
|
||||
"Page Fault",
|
||||
"(unknown trap)",
|
||||
"x87 FPU Floating-Point Error",
|
||||
"Alignment Check",
|
||||
"Machine-Check",
|
||||
"SIMD Floating-Point Exception"
|
||||
};
|
||||
|
||||
if (trapno < ARRAY_SIZE(excnames))
|
||||
return excnames[trapno];
|
||||
if (trapno == T_SYSCALL)
|
||||
return "System call";
|
||||
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16)
|
||||
return "Hardware Interrupt";
|
||||
return "(unknown trap)";
|
||||
}
|
||||
|
||||
|
||||
// XYZ: write a function declaration here...
|
||||
// e.g., void t_divide();
|
||||
void t_divide();
|
||||
void t_debug();
|
||||
void t_nmi();
|
||||
void t_brkpt();
|
||||
void t_oflow();
|
||||
void t_bound();
|
||||
void t_illop();
|
||||
void t_device();
|
||||
void t_dblflt();
|
||||
void t_tss();
|
||||
void t_segnp();
|
||||
void t_stack();
|
||||
void t_gpflt();
|
||||
void t_pgflt();
|
||||
void t_fperr();
|
||||
void t_align();
|
||||
void t_mchk();
|
||||
void t_simderr();
|
||||
void t_syscall();
|
||||
void t_default();
|
||||
|
||||
void irq_timer();
|
||||
void irq_kbd();
|
||||
void irq_serial();
|
||||
void irq_spurious();
|
||||
void irq_ide();
|
||||
void irq_error();
|
||||
|
||||
void
|
||||
trap_init(void)
|
||||
{
|
||||
extern struct Segdesc gdt[];
|
||||
|
||||
/*
|
||||
*
|
||||
* HINT
|
||||
* Do something like this: SETGATE(idt[T_DIVIDE], 0, GD_KT, t_divide, 0);
|
||||
* if your trap handler's name for divide by zero is t_device.
|
||||
* Additionally, you should declare trap handler as a function
|
||||
* to refer that in C code... (see the comment XYZ above)
|
||||
*
|
||||
*/
|
||||
// LAB 3: Your code here.
|
||||
SETGATE(idt[T_DIVIDE], 0, GD_KT, t_divide, 0);
|
||||
SETGATE(idt[T_DEBUG], 0, GD_KT, t_debug, 0);
|
||||
SETGATE(idt[T_NMI], 0, GD_KT, t_nmi, 0);
|
||||
SETGATE(idt[T_BRKPT], 0, GD_KT, t_brkpt, 3);
|
||||
SETGATE(idt[T_OFLOW], 0, GD_KT, t_oflow, 0);
|
||||
SETGATE(idt[T_BOUND], 0, GD_KT, t_bound, 0);
|
||||
SETGATE(idt[T_ILLOP], 0, GD_KT, t_illop, 0);
|
||||
SETGATE(idt[T_DEVICE], 0, GD_KT, t_device, 0);
|
||||
SETGATE(idt[T_DBLFLT], 0, GD_KT, t_dblflt, 0);
|
||||
SETGATE(idt[T_TSS], 0, GD_KT, t_tss, 0);
|
||||
SETGATE(idt[T_SEGNP], 0, GD_KT, t_segnp, 0);
|
||||
SETGATE(idt[T_STACK], 0, GD_KT, t_stack, 0);
|
||||
SETGATE(idt[T_GPFLT], 0, GD_KT, t_gpflt, 0);
|
||||
SETGATE(idt[T_PGFLT], 0, GD_KT, t_pgflt, 0);
|
||||
SETGATE(idt[T_FPERR], 0, GD_KT, t_fperr, 0);
|
||||
SETGATE(idt[T_ALIGN], 0, GD_KT, t_align, 0);
|
||||
SETGATE(idt[T_MCHK], 0, GD_KT, t_mchk, 0);
|
||||
SETGATE(idt[T_SIMDERR], 0, GD_KT, t_simderr, 0);
|
||||
SETGATE(idt[T_SYSCALL], 0, GD_KT, t_syscall, 3);
|
||||
SETGATE(idt[T_DEFAULT], 0, GD_KT, t_default, 0);
|
||||
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_TIMER], 0, GD_KT, irq_timer, 0);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_KBD], 0, GD_KT, irq_kbd, 0);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SERIAL], 0, GD_KT, irq_serial, 0);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_SPURIOUS], 0, GD_KT, irq_spurious, 0);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_IDE], 0, GD_KT, irq_ide, 0);
|
||||
SETGATE(idt[IRQ_OFFSET + IRQ_ERROR], 0, GD_KT, irq_error, 0);
|
||||
|
||||
// Per-CPU setup
|
||||
trap_init_percpu();
|
||||
}
|
||||
|
||||
// Initialize and load the per-CPU TSS and IDT
|
||||
void
|
||||
trap_init_percpu(void)
|
||||
{
|
||||
// The example code here sets up the Task State Segment (TSS) and
|
||||
// the TSS descriptor for CPU 0. But it is incorrect if we are
|
||||
// running on other CPUs because each CPU has its own kernel stack.
|
||||
// Fix the code so that it works for all CPUs.
|
||||
//
|
||||
// Hints:
|
||||
// - The macro "thiscpu" always refers to the current CPU's
|
||||
// struct CpuInfo;
|
||||
// - The ID of the current CPU is given by cpunum() or
|
||||
// thiscpu->cpu_id;
|
||||
// - Use "thiscpu->cpu_ts" as the TSS for the current CPU,
|
||||
// rather than the global "ts" variable;
|
||||
// - Use gdt[(GD_TSS0 >> 3) + i] for CPU i's TSS descriptor;
|
||||
// - You mapped the per-CPU kernel stacks in mem_init_mp()
|
||||
// - Initialize cpu_ts.ts_iomb to prevent unauthorized environments
|
||||
// from doing IO (0 is not the correct value!)
|
||||
//
|
||||
// ltr sets a 'busy' flag in the TSS selector, so if you
|
||||
// accidentally load the same TSS on more than one CPU, you'll
|
||||
// get a triple fault. If you set up an individual CPU's TSS
|
||||
// wrong, you may not get a fault until you try to return from
|
||||
// user space on that CPU.
|
||||
//
|
||||
// LAB 4: Your code here:
|
||||
|
||||
// Setup a TSS so that we get the right stack
|
||||
// when we trap to the kernel.
|
||||
thiscpu->cpu_ts.ts_esp0 = KSTACKTOP - thiscpu->cpu_id * (KSTKSIZE + KSTKGAP);
|
||||
thiscpu->cpu_ts.ts_ss0 = GD_KD;
|
||||
thiscpu->cpu_ts.ts_iomb = sizeof(struct Taskstate);
|
||||
|
||||
// Initialize the TSS slot of the gdt.
|
||||
gdt[(GD_TSS0 >> 3) + cpunum()] = SEG16(STS_T32A, (uint32_t) (&thiscpu->cpu_ts),
|
||||
sizeof(struct Taskstate) - 1, 0);
|
||||
gdt[(GD_TSS0 >> 3) + cpunum()].sd_s = 0;
|
||||
|
||||
// Load the TSS selector (like other segment selectors, the
|
||||
// bottom three bits are special; we leave them 0)
|
||||
ltr(GD_TSS0 + (cpunum() << 3));
|
||||
|
||||
// Load the IDT
|
||||
lidt(&idt_pd);
|
||||
}
|
||||
|
||||
void
|
||||
print_trapframe(struct Trapframe *tf)
|
||||
{
|
||||
cprintf("TRAP frame at %p from CPU %d\n", tf, cpunum());
|
||||
print_regs(&tf->tf_regs);
|
||||
cprintf(" es 0x----%04x\n", tf->tf_es);
|
||||
cprintf(" ds 0x----%04x\n", tf->tf_ds);
|
||||
cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
|
||||
// If this trap was a page fault that just happened
|
||||
// (so %cr2 is meaningful), print the faulting linear address.
|
||||
if (tf == last_tf && tf->tf_trapno == T_PGFLT)
|
||||
cprintf(" cr2 0x%08x\n", rcr2());
|
||||
cprintf(" err 0x%08x", tf->tf_err);
|
||||
// For page faults, print decoded fault error code:
|
||||
// U/K=fault occurred in user/kernel mode
|
||||
// W/R=a write/read caused the fault
|
||||
// PR=a protection violation caused the fault (NP=page not present).
|
||||
if (tf->tf_trapno == T_PGFLT)
|
||||
cprintf(" [%s, %s, %s]\n",
|
||||
tf->tf_err & 4 ? "user" : "kernel",
|
||||
tf->tf_err & 2 ? "write" : "read",
|
||||
tf->tf_err & 1 ? "protection" : "not-present");
|
||||
else
|
||||
cprintf("\n");
|
||||
cprintf(" eip 0x%08x\n", tf->tf_eip);
|
||||
cprintf(" cs 0x----%04x\n", tf->tf_cs);
|
||||
cprintf(" flag 0x%08x\n", tf->tf_eflags);
|
||||
if ((tf->tf_cs & 3) != 0) {
|
||||
cprintf(" esp 0x%08x\n", tf->tf_esp);
|
||||
cprintf(" ss 0x----%04x\n", tf->tf_ss);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_regs(struct PushRegs *regs)
|
||||
{
|
||||
cprintf(" edi 0x%08x\n", regs->reg_edi);
|
||||
cprintf(" esi 0x%08x\n", regs->reg_esi);
|
||||
cprintf(" ebp 0x%08x\n", regs->reg_ebp);
|
||||
cprintf(" oesp 0x%08x\n", regs->reg_oesp);
|
||||
cprintf(" ebx 0x%08x\n", regs->reg_ebx);
|
||||
cprintf(" edx 0x%08x\n", regs->reg_edx);
|
||||
cprintf(" ecx 0x%08x\n", regs->reg_ecx);
|
||||
cprintf(" eax 0x%08x\n", regs->reg_eax);
|
||||
}
|
||||
|
||||
static void
|
||||
trap_dispatch(struct Trapframe *tf)
|
||||
{
|
||||
// Handle processor exceptions.
|
||||
// LAB 3: Your code here.
|
||||
if (tf->tf_trapno == T_PGFLT) {
|
||||
page_fault_handler(tf);
|
||||
return;
|
||||
} else if (tf->tf_trapno == T_BRKPT || tf->tf_trapno == T_DEBUG) {
|
||||
monitor(tf);
|
||||
return;
|
||||
} else if (tf->tf_trapno == T_SYSCALL) {
|
||||
int32_t returned = syscall(tf->tf_regs.reg_eax,
|
||||
tf->tf_regs.reg_edx,
|
||||
tf->tf_regs.reg_ecx,
|
||||
tf->tf_regs.reg_ebx,
|
||||
tf->tf_regs.reg_edi,
|
||||
tf->tf_regs.reg_esi);
|
||||
tf->tf_regs.reg_eax = returned;
|
||||
return;
|
||||
} else if (tf->tf_trapno == IRQ_OFFSET + IRQ_TIMER) {
|
||||
lapic_eoi();
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
// Handle spurious interrupts
|
||||
// The hardware sometimes raises these because of noise on the
|
||||
// IRQ line or other reasons. We don't care.
|
||||
if (tf->tf_trapno == IRQ_OFFSET + IRQ_SPURIOUS) {
|
||||
cprintf("Spurious interrupt on irq 7\n");
|
||||
print_trapframe(tf);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle clock interrupts. Don't forget to acknowledge the
|
||||
// interrupt using lapic_eoi() before calling the scheduler!
|
||||
// LAB 4: Your code here.
|
||||
|
||||
// Unexpected trap: The user process or the kernel has a bug.
|
||||
print_trapframe(tf);
|
||||
if (tf->tf_cs == GD_KT)
|
||||
panic("unhandled trap in kernel");
|
||||
else {
|
||||
env_destroy(curenv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
trap(struct Trapframe *tf)
|
||||
{
|
||||
// The environment may have set DF and some versions
|
||||
// of GCC rely on DF being clear
|
||||
asm volatile("cld" ::: "cc");
|
||||
|
||||
// Halt the CPU if some other CPU has called panic()
|
||||
extern char *panicstr;
|
||||
if (panicstr)
|
||||
asm volatile("hlt");
|
||||
|
||||
// Re-acqurie the big kernel lock if we were halted in
|
||||
// sched_yield()
|
||||
if (xchg(&thiscpu->cpu_status, CPU_STARTED) == CPU_HALTED)
|
||||
lock_kernel();
|
||||
// Check that interrupts are disabled. If this assertion
|
||||
// fails, DO NOT be tempted to fix it by inserting a "cli" in
|
||||
// the interrupt path.
|
||||
assert(!(read_eflags() & FL_IF));
|
||||
|
||||
if ((tf->tf_cs & 3) == 3) {
|
||||
// Trapped from user mode.
|
||||
// Acquire the big kernel lock before doing any
|
||||
// serious kernel work.
|
||||
// LAB 4: Your code here.
|
||||
lock_kernel();
|
||||
assert(curenv);
|
||||
|
||||
// Garbage collect if current enviroment is a zombie
|
||||
if (curenv->env_status == ENV_DYING) {
|
||||
env_free(curenv);
|
||||
curenv = NULL;
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
// Copy trap frame (which is currently on the stack)
|
||||
// into 'curenv->env_tf', so that running the environment
|
||||
// will restart at the trap point.
|
||||
curenv->env_tf = *tf;
|
||||
// The trapframe on the stack should be ignored from here on.
|
||||
tf = &curenv->env_tf;
|
||||
}
|
||||
|
||||
// Record that tf is the last real trapframe so
|
||||
// print_trapframe can print some additional information.
|
||||
last_tf = tf;
|
||||
|
||||
// Dispatch based on what type of trap occurred
|
||||
trap_dispatch(tf);
|
||||
|
||||
// If we made it to this point, then no other environment was
|
||||
// scheduled, so we should return to the current environment
|
||||
// if doing so makes sense.
|
||||
if (curenv && curenv->env_status == ENV_RUNNING)
|
||||
env_run(curenv);
|
||||
else
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
page_fault_handler(struct Trapframe *tf)
|
||||
{
|
||||
uint32_t fault_va;
|
||||
|
||||
// Read processor's CR2 register to find the faulting address
|
||||
fault_va = rcr2();
|
||||
|
||||
// Handle kernel-mode page faults.
|
||||
|
||||
// LAB 3: Your code here.
|
||||
|
||||
// We've already handled kernel-mode exceptions, so if we get here,
|
||||
// the page fault happened in user mode.
|
||||
|
||||
// Call the environment's page fault upcall, if one exists. Set up a
|
||||
// page fault stack frame on the user exception stack (below
|
||||
// UXSTACKTOP), then branch to curenv->env_pgfault_upcall.
|
||||
//
|
||||
// The page fault upcall might cause another page fault, in which case
|
||||
// we branch to the page fault upcall recursively, pushing another
|
||||
// page fault stack frame on top of the user exception stack.
|
||||
//
|
||||
// It is convenient for our code which returns from a page fault
|
||||
// (lib/pfentry.S) to have one word of scratch space at the top of the
|
||||
// trap-time stack; it allows us to more easily restore the eip/esp. In
|
||||
// the non-recursive case, we don't have to worry about this because
|
||||
// the top of the regular user stack is free. In the recursive case,
|
||||
// this means we have to leave an extra word between the current top of
|
||||
// the exception stack and the new stack frame because the exception
|
||||
// stack _is_ the trap-time stack.
|
||||
//
|
||||
// If there's no page fault upcall, the environment didn't allocate a
|
||||
// page for its exception stack or can't write to it, or the exception
|
||||
// stack overflows, then destroy the environment that caused the fault.
|
||||
// Note that the grade script assumes you will first check for the page
|
||||
// fault upcall and print the "user fault va" message below if there is
|
||||
// none. The remaining three checks can be combined into a single test.
|
||||
//
|
||||
// Hints:
|
||||
// user_mem_assert() and env_run() are useful here.
|
||||
// To change what the user environment runs, modify 'curenv->env_tf'
|
||||
// (the 'tf' variable points at 'curenv->env_tf').
|
||||
|
||||
// LAB 4: Your code here.
|
||||
if(!curenv->env_pgfault_upcall) {
|
||||
// Destroy the environment that caused the fault.
|
||||
cprintf("[%08x] user fault va %08x ip %08x\n",
|
||||
curenv->env_id, fault_va, tf->tf_eip);
|
||||
print_trapframe(tf);
|
||||
env_destroy(curenv);
|
||||
}
|
||||
user_mem_assert(curenv, curenv->env_pgfault_upcall, 1, PTE_U | PTE_P);
|
||||
user_mem_assert(curenv, (void*) UXSTACKTOP - 1, 1, PTE_U | PTE_P | PTE_W);
|
||||
|
||||
uintptr_t top_addr = UXSTACKTOP;
|
||||
if(tf->tf_esp <= UXSTACKTOP && tf->tf_esp >= (UXSTACKTOP - PGSIZE)) {
|
||||
top_addr = tf->tf_esp - 4;
|
||||
}
|
||||
|
||||
struct UTrapframe utf;
|
||||
utf.utf_eflags = tf->tf_eflags;
|
||||
utf.utf_eip = tf->tf_eip;
|
||||
utf.utf_err = tf->tf_err;
|
||||
utf.utf_esp = tf->tf_esp;
|
||||
utf.utf_fault_va = fault_va;
|
||||
utf.utf_regs = tf->tf_regs;
|
||||
|
||||
struct UTrapframe* push_to = (struct UTrapframe*) top_addr - 1;
|
||||
if((uintptr_t) push_to < USTACKTOP - PGSIZE) {
|
||||
cprintf("[%08x] stack overflow in page fault handler\n",
|
||||
curenv->env_id);
|
||||
env_destroy(curenv);
|
||||
}
|
||||
|
||||
*push_to = utf;
|
||||
curenv->env_tf.tf_eip = (uintptr_t) curenv->env_pgfault_upcall;
|
||||
curenv->env_tf.tf_esp = (uintptr_t) push_to;
|
||||
env_run(curenv);
|
||||
}
|
||||
|
23
kern/trap.h
Normal file
23
kern/trap.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#ifndef JOS_KERN_TRAP_H
|
||||
#define JOS_KERN_TRAP_H
|
||||
#ifndef JOS_KERNEL
|
||||
# error "This is a JOS kernel header; user programs should not #include it"
|
||||
#endif
|
||||
|
||||
#include <inc/trap.h>
|
||||
#include <inc/mmu.h>
|
||||
|
||||
/* The kernel's interrupt descriptor table */
|
||||
extern struct Gatedesc idt[];
|
||||
extern struct Pseudodesc idt_pd;
|
||||
|
||||
void trap_init(void);
|
||||
void trap_init_percpu(void);
|
||||
void print_regs(struct PushRegs *regs);
|
||||
void print_trapframe(struct Trapframe *tf);
|
||||
void page_fault_handler(struct Trapframe *);
|
||||
void backtrace(struct Trapframe *);
|
||||
|
||||
#endif /* JOS_KERN_TRAP_H */
|
114
kern/trapentry.S
Normal file
114
kern/trapentry.S
Normal file
|
@ -0,0 +1,114 @@
|
|||
/* See COPYRIGHT for copyright information. */
|
||||
|
||||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
#include <inc/trap.h>
|
||||
|
||||
#include <kern/picirq.h>
|
||||
|
||||
|
||||
###################################################################
|
||||
# exceptions/interrupts
|
||||
###################################################################
|
||||
|
||||
/* TRAPHANDLER defines a globally-visible function for handling a trap.
|
||||
* It pushes a trap number onto the stack, then jumps to _alltraps.
|
||||
* Use TRAPHANDLER for traps where the CPU automatically pushes an error code.
|
||||
*
|
||||
* You shouldn't call a TRAPHANDLER function from C, but you may
|
||||
* need to _declare_ one in C (for instance, to get a function pointer
|
||||
* during IDT setup). You can declare the function with
|
||||
* void NAME();
|
||||
* where NAME is the argument passed to TRAPHANDLER.
|
||||
*/
|
||||
#define TRAPHANDLER(name, num) \
|
||||
.globl name; /* define global symbol for 'name' */ \
|
||||
.type name, @function; /* symbol type is function */ \
|
||||
.align 2; /* align function definition */ \
|
||||
name: /* function starts here */ \
|
||||
pushl $(num); \
|
||||
jmp _alltraps
|
||||
|
||||
/* Use TRAPHANDLER_NOEC for traps where the CPU doesn't push an error code.
|
||||
* It pushes a 0 in place of the error code, so the trap frame has the same
|
||||
* format in either case.
|
||||
*/
|
||||
#define TRAPHANDLER_NOEC(name, num) \
|
||||
.globl name; \
|
||||
.type name, @function; \
|
||||
.align 2; \
|
||||
name: \
|
||||
pushl $0; \
|
||||
pushl $(num); \
|
||||
jmp _alltraps
|
||||
|
||||
.text
|
||||
|
||||
.globl sysenter_handler
|
||||
sysenter_handler:
|
||||
push %ebp // holds env's stack pointer
|
||||
push %esi // holds the env's return addr
|
||||
push %edi
|
||||
push %ebx
|
||||
push %ecx
|
||||
push %edx
|
||||
push %eax
|
||||
call syscall
|
||||
add $0x14, %esp
|
||||
pop %edx
|
||||
pop %ecx
|
||||
sysexit
|
||||
|
||||
/*
|
||||
* Lab 3: Your code here for generating entry points for the different traps.
|
||||
*/
|
||||
TRAPHANDLER_NOEC(t_divide, T_DIVIDE);
|
||||
TRAPHANDLER_NOEC(t_debug, T_DEBUG);
|
||||
TRAPHANDLER_NOEC(t_nmi, T_NMI);
|
||||
TRAPHANDLER_NOEC(t_brkpt, T_BRKPT);
|
||||
TRAPHANDLER_NOEC(t_oflow, T_OFLOW);
|
||||
TRAPHANDLER_NOEC(t_bound, T_OFLOW);
|
||||
TRAPHANDLER_NOEC(t_illop, T_OFLOW);
|
||||
TRAPHANDLER_NOEC(t_device, T_OFLOW);
|
||||
TRAPHANDLER(t_dblflt, T_OFLOW);
|
||||
TRAPHANDLER(t_tss, T_TSS);
|
||||
TRAPHANDLER(t_segnp, T_SEGNP);
|
||||
TRAPHANDLER(t_stack, T_STACK);
|
||||
TRAPHANDLER(t_gpflt, T_GPFLT);
|
||||
TRAPHANDLER(t_pgflt, T_PGFLT);
|
||||
TRAPHANDLER_NOEC(t_fperr, T_FPERR);
|
||||
TRAPHANDLER(t_align, T_ALIGN);
|
||||
TRAPHANDLER_NOEC(t_mchk, T_MCHK);
|
||||
TRAPHANDLER_NOEC(t_simderr, T_SIMDERR);
|
||||
TRAPHANDLER_NOEC(t_syscall, T_SYSCALL);
|
||||
TRAPHANDLER(t_default, T_DEFAULT);
|
||||
TRAPHANDLER_NOEC(irq_timer, IRQ_OFFSET + IRQ_TIMER);
|
||||
TRAPHANDLER_NOEC(irq_kbd, IRQ_OFFSET + IRQ_KBD);
|
||||
TRAPHANDLER_NOEC(irq_serial, IRQ_OFFSET + IRQ_SERIAL);
|
||||
TRAPHANDLER_NOEC(irq_spurious, IRQ_OFFSET + IRQ_SPURIOUS);
|
||||
TRAPHANDLER_NOEC(irq_ide, IRQ_OFFSET + IRQ_IDE);
|
||||
TRAPHANDLER_NOEC(irq_error, IRQ_OFFSET + IRQ_ERROR);
|
||||
|
||||
// HINT 1 : TRAPHANDLER_NOEC(t_divide, T_DIVIDE);
|
||||
// Do something like this if there is no error code for the trap
|
||||
// HINT 2 : TRAPHANDLER(t_dblflt, T_DBLFLT);
|
||||
// Do something like this if the trap includes an error code..
|
||||
// HINT 3 : READ Intel's manual to check if the trap includes an error code
|
||||
// or not...
|
||||
|
||||
/*
|
||||
* Lab 3: Your code here for _alltraps
|
||||
*/
|
||||
|
||||
.globl _alltraps
|
||||
_alltraps:
|
||||
sub $0x2, %esp
|
||||
pushw %ds
|
||||
sub $0x2, %esp
|
||||
pushw %es
|
||||
pushal
|
||||
mov $(GD_KD), %eax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
pushl %esp
|
||||
call trap
|
36
lib/Makefrag
Normal file
36
lib/Makefrag
Normal file
|
@ -0,0 +1,36 @@
|
|||
OBJDIRS += lib
|
||||
|
||||
LIB_SRCFILES := lib/console.c \
|
||||
lib/libmain.c \
|
||||
lib/exit.c \
|
||||
lib/panic.c \
|
||||
lib/printf.c \
|
||||
lib/printfmt.c \
|
||||
lib/readline.c \
|
||||
lib/string.c \
|
||||
lib/syscall.c
|
||||
|
||||
LIB_SRCFILES := $(LIB_SRCFILES) \
|
||||
lib/pgfault.c \
|
||||
lib/pfentry.S \
|
||||
lib/fork.c \
|
||||
lib/ipc.c
|
||||
|
||||
|
||||
|
||||
LIB_OBJFILES := $(patsubst lib/%.c, $(OBJDIR)/lib/%.o, $(LIB_SRCFILES))
|
||||
LIB_OBJFILES := $(patsubst lib/%.S, $(OBJDIR)/lib/%.o, $(LIB_OBJFILES))
|
||||
|
||||
$(OBJDIR)/lib/%.o: lib/%.c $(OBJDIR)/.vars.USER_CFLAGS
|
||||
@echo + cc[USER] $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/lib/%.o: lib/%.S $(OBJDIR)/.vars.USER_CFLAGS
|
||||
@echo + as[USER] $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/lib/libjos.a: $(LIB_OBJFILES)
|
||||
@echo + ar $@
|
||||
$(V)$(AR) r $@ $(LIB_OBJFILES)
|
25
lib/console.c
Normal file
25
lib/console.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
cputchar(int ch)
|
||||
{
|
||||
char c = ch;
|
||||
|
||||
// Unlike standard Unix's putchar,
|
||||
// the cputchar function _always_ outputs to the system console.
|
||||
sys_cputs(&c, 1);
|
||||
}
|
||||
|
||||
int
|
||||
getchar(void)
|
||||
{
|
||||
int r;
|
||||
// sys_cgetc does not block, but getchar should.
|
||||
while ((r = sys_cgetc()) == 0)
|
||||
sys_yield();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
35
lib/entry.S
Normal file
35
lib/entry.S
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
|
||||
.data
|
||||
// Define the global symbols 'envs', 'pages', 'uvpt', and 'uvpd'
|
||||
// so that they can be used in C as if they were ordinary global arrays.
|
||||
.globl envs
|
||||
.set envs, UENVS
|
||||
.globl pages
|
||||
.set pages, UPAGES
|
||||
.globl uvpt
|
||||
.set uvpt, UVPT
|
||||
.globl uvpd
|
||||
.set uvpd, (UVPT+(UVPT>>12)*4)
|
||||
|
||||
|
||||
// Entrypoint - this is where the kernel (or our parent environment)
|
||||
// starts us running when we are initially loaded into a new environment.
|
||||
.text
|
||||
.globl _start
|
||||
_start:
|
||||
// See if we were started with arguments on the stack
|
||||
cmpl $USTACKTOP, %esp
|
||||
jne args_exist
|
||||
|
||||
// If not, push dummy argc/argv arguments.
|
||||
// This happens when we are loaded by the kernel,
|
||||
// because the kernel does not know about passing arguments.
|
||||
pushl $0
|
||||
pushl $0
|
||||
|
||||
args_exist:
|
||||
call libmain
|
||||
1: jmp 1b
|
||||
|
9
lib/exit.c
Normal file
9
lib/exit.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
exit(void)
|
||||
{
|
||||
sys_env_destroy(0);
|
||||
}
|
||||
|
147
lib/fork.c
Normal file
147
lib/fork.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
// implement fork from user space
|
||||
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
// PTE_COW marks copy-on-write page table entries.
|
||||
// It is one of the bits explicitly allocated to user processes (PTE_AVAIL).
|
||||
#define PTE_COW 0x800
|
||||
|
||||
//
|
||||
// Custom page fault handler - if faulting page is copy-on-write,
|
||||
// map in our own private writable copy.
|
||||
//
|
||||
static void
|
||||
pgfault(struct UTrapframe *utf)
|
||||
{
|
||||
void *addr = (void *) utf->utf_fault_va;
|
||||
uint32_t err = utf->utf_err;
|
||||
int r;
|
||||
|
||||
// Check that the faulting access was (1) a write, and (2) to a
|
||||
// copy-on-write page. If not, panic.
|
||||
// Hint:
|
||||
// Use the read-only page table mappings at uvpt
|
||||
// (see <inc/memlayout.h>).
|
||||
if(!((err & FEC_WR) && (uvpt[(uintptr_t) addr >> PGSHIFT] & PTE_COW)))
|
||||
panic("page fault (addr %p)! %c", addr, (err & FEC_WR) ? 'w' : 'r');
|
||||
|
||||
// Allocate a new page, map it at a temporary location (PFTEMP),
|
||||
// copy the data from the old page to the new page, then move the new
|
||||
// page to the old page's address.
|
||||
// Hint:
|
||||
// You should make three system calls.
|
||||
void* temp_addr = (void*) PFTEMP;
|
||||
void* fault_addr = ROUNDDOWN(addr, PGSIZE);
|
||||
if(sys_page_alloc(0, temp_addr, PTE_P | PTE_W | PTE_U) < 0)
|
||||
panic("failed to allocate new page");
|
||||
|
||||
memcpy(temp_addr, fault_addr, PGSIZE);
|
||||
sys_page_map(0, temp_addr, 0, fault_addr, PTE_P | PTE_U | PTE_W);
|
||||
sys_page_unmap(0, temp_addr);
|
||||
}
|
||||
|
||||
//
|
||||
// Map our virtual page pn (address pn*PGSIZE) into the target envid
|
||||
// at the same virtual address. If the page is writable or copy-on-write,
|
||||
// the new mapping must be created copy-on-write, and then our mapping must be
|
||||
// marked copy-on-write as well. (Exercise: Why do we need to mark ours
|
||||
// copy-on-write again if it was already copy-on-write at the beginning of
|
||||
// this function?)
|
||||
//
|
||||
// Returns: 0 on success, < 0 on error.
|
||||
// It is also OK to panic on error.
|
||||
//
|
||||
static int
|
||||
duppage(envid_t envid, unsigned pn)
|
||||
{
|
||||
int r;
|
||||
bool change_own = false;
|
||||
pte_t new_pte = uvpt[pn];
|
||||
pte_t perms = new_pte & (PTE_P | PTE_U | PTE_W | PTE_AVAIL);
|
||||
void* addr = (void*) (pn * PGSIZE);
|
||||
|
||||
// If we're writable, remove write permission
|
||||
if((new_pte & PTE_W) || (new_pte & PTE_COW)) {
|
||||
perms = (perms & ~PTE_W) | PTE_COW;
|
||||
change_own = true;
|
||||
}
|
||||
|
||||
// Map either with the same permissions or with COW.
|
||||
if((r = sys_page_map(0, addr, envid, addr, perms)) < 0)
|
||||
return r;
|
||||
|
||||
// Update our own permissions if necessary
|
||||
if(change_own) {
|
||||
if((r = sys_page_map(0, addr, 0, addr, perms)) < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// User-level fork with copy-on-write.
|
||||
// Set up our page fault handler appropriately.
|
||||
// Create a child.
|
||||
// Copy our address space and page fault handler setup to the child.
|
||||
// Then mark the child as runnable and return.
|
||||
//
|
||||
// Returns: child's envid to the parent, 0 to the child, < 0 on error.
|
||||
// It is also OK to panic on error.
|
||||
//
|
||||
// Hint:
|
||||
// Use uvpd, uvpt, and duppage.
|
||||
// Remember to fix "thisenv" in the child process.
|
||||
// Neither user exception stack should ever be marked copy-on-write,
|
||||
// so you must allocate a new page for the child's user exception stack.
|
||||
//
|
||||
envid_t
|
||||
fork(void)
|
||||
{
|
||||
set_pgfault_handler(pgfault);
|
||||
|
||||
int return_code;
|
||||
envid_t forked;
|
||||
|
||||
forked = sys_exofork();
|
||||
if(forked < 0) return forked;
|
||||
if(forked == 0) { thisenv = &envs[ENVX(sys_getenvid())]; return 0; }
|
||||
|
||||
// Map all accessible page directory entries
|
||||
for(int pde_i = 0; pde_i < PDX(UTOP); pde_i++) {
|
||||
pde_t pde = uvpd[pde_i];
|
||||
if(!(pde & PTE_P)) continue;
|
||||
|
||||
// For each PDE, map all the underlying PTEs
|
||||
for(int pte_i = 0; pte_i < NPTENTRIES; pte_i++) {
|
||||
int pn = pde_i * NPTENTRIES + pte_i;
|
||||
pte_t pte = uvpt[pn];
|
||||
if(!(pte & PTE_P)) continue;
|
||||
|
||||
// Do not map user exception stack, though
|
||||
if(pn == ((UXSTACKTOP - PGSIZE) >> PGSHIFT)) continue;
|
||||
|
||||
if((return_code = duppage(forked, pn)) < 0) return return_code;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate new page for the exception stack
|
||||
return_code = sys_page_alloc(forked, (void*) UXSTACKTOP - PGSIZE,
|
||||
PTE_P | PTE_U | PTE_W);
|
||||
if(return_code < 0) return return_code;
|
||||
|
||||
// Set the upcall entry point
|
||||
sys_env_set_pgfault_upcall(forked, thisenv->env_pgfault_upcall);
|
||||
sys_env_set_status(forked, ENV_RUNNABLE);
|
||||
|
||||
return forked;
|
||||
}
|
||||
|
||||
// Challenge!
|
||||
int
|
||||
sfork(void)
|
||||
{
|
||||
panic("sfork not implemented");
|
||||
return -E_INVAL;
|
||||
}
|
65
lib/ipc.c
Normal file
65
lib/ipc.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
// User-level IPC library routines
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
// Receive a value via IPC and return it.
|
||||
// If 'pg' is nonnull, then any page sent by the sender will be mapped at
|
||||
// that address.
|
||||
// If 'from_env_store' is nonnull, then store the IPC sender's envid in
|
||||
// *from_env_store.
|
||||
// If 'perm_store' is nonnull, then store the IPC sender's page permission
|
||||
// in *perm_store (this is nonzero iff a page was successfully
|
||||
// transferred to 'pg').
|
||||
// If the system call fails, then store 0 in *fromenv and *perm (if
|
||||
// they're nonnull) and return the error.
|
||||
// Otherwise, return the value sent by the sender
|
||||
//
|
||||
// Hint:
|
||||
// Use 'thisenv' to discover the value and who sent it.
|
||||
// If 'pg' is null, pass sys_ipc_recv a value that it will understand
|
||||
// as meaning "no page". (Zero is not the right value, since that's
|
||||
// a perfectly valid place to map a page.)
|
||||
int32_t
|
||||
ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
|
||||
{
|
||||
int return_code;
|
||||
if((return_code = sys_ipc_recv(pg ? pg : (void*) ~0)) < 0)
|
||||
return return_code;
|
||||
|
||||
if(from_env_store) *from_env_store = thisenv->env_ipc_from;
|
||||
if(perm_store) *perm_store = thisenv->env_ipc_perm;
|
||||
|
||||
return thisenv->env_ipc_value;
|
||||
}
|
||||
|
||||
// Send 'val' (and 'pg' with 'perm', if 'pg' is nonnull) to 'toenv'.
|
||||
// This function keeps trying until it succeeds.
|
||||
// It should panic() on any error other than -E_IPC_NOT_RECV.
|
||||
//
|
||||
// Hint:
|
||||
// Use sys_yield() to be CPU-friendly.
|
||||
// If 'pg' is null, pass sys_ipc_try_send a value that it will understand
|
||||
// as meaning "no page". (Zero is not the right value.)
|
||||
void
|
||||
ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
|
||||
{
|
||||
int return_code = -E_IPC_NOT_RECV;
|
||||
while(return_code == -E_IPC_NOT_RECV) {
|
||||
return_code = sys_ipc_try_send(to_env, val, pg ? pg : (void*) ~0, perm);
|
||||
sys_yield();
|
||||
}
|
||||
if(return_code != 0) panic("failed to send\n");
|
||||
}
|
||||
|
||||
// Find the first environment of the given type. We'll use this to
|
||||
// find special environments.
|
||||
// Returns 0 if no such environment exists.
|
||||
envid_t
|
||||
ipc_find_env(enum EnvType type)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < NENV; i++)
|
||||
if (envs[i].env_type == type)
|
||||
return envs[i].env_id;
|
||||
return 0;
|
||||
}
|
29
lib/libmain.c
Normal file
29
lib/libmain.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Called from entry.S to get us going.
|
||||
// entry.S already took care of defining envs, pages, uvpd, and uvpt.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
extern void umain(int argc, char **argv);
|
||||
|
||||
const volatile struct Env *thisenv;
|
||||
const char *binaryname = "<unknown>";
|
||||
|
||||
void
|
||||
libmain(int argc, char **argv)
|
||||
{
|
||||
// set thisenv to point at our Env structure in envs[].
|
||||
// LAB 3: Your code here.
|
||||
envid_t id = sys_getenvid();
|
||||
thisenv = &envs[ENVX(id)];
|
||||
|
||||
// save the name of the program so that panic() can use it
|
||||
if (argc > 0)
|
||||
binaryname = argv[0];
|
||||
|
||||
// call user main routine
|
||||
umain(argc, argv);
|
||||
|
||||
// exit gracefully
|
||||
exit();
|
||||
}
|
||||
|
26
lib/panic.c
Normal file
26
lib/panic.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
#include <inc/lib.h>
|
||||
|
||||
/*
|
||||
* Panic is called on unresolvable fatal errors.
|
||||
* It prints "panic: <message>", then causes a breakpoint exception,
|
||||
* which causes JOS to enter the JOS kernel monitor.
|
||||
*/
|
||||
void
|
||||
_panic(const char *file, int line, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
|
||||
// Print the panic message
|
||||
cprintf("[%08x] user panic in %s at %s:%d: ",
|
||||
sys_getenvid(), binaryname, file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
|
||||
// Cause a breakpoint exception
|
||||
while (1)
|
||||
asm volatile("int3");
|
||||
}
|
||||
|
93
lib/pfentry.S
Normal file
93
lib/pfentry.S
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include <inc/mmu.h>
|
||||
#include <inc/memlayout.h>
|
||||
|
||||
// Page fault upcall entrypoint.
|
||||
|
||||
// This is where we ask the kernel to redirect us to whenever we cause
|
||||
// a page fault in user space (see the call to sys_set_pgfault_handler
|
||||
// in pgfault.c).
|
||||
//
|
||||
// When a page fault actually occurs, the kernel switches our ESP to
|
||||
// point to the user exception stack if we're not already on the user
|
||||
// exception stack, and then it pushes a UTrapframe onto our user
|
||||
// exception stack:
|
||||
//
|
||||
// trap-time esp
|
||||
// trap-time eflags
|
||||
// trap-time eip
|
||||
// utf_regs.reg_eax
|
||||
// ...
|
||||
// utf_regs.reg_esi
|
||||
// utf_regs.reg_edi
|
||||
// utf_err (error code)
|
||||
// utf_fault_va <-- %esp
|
||||
//
|
||||
// If this is a recursive fault, the kernel will reserve for us a
|
||||
// blank word above the trap-time esp for scratch work when we unwind
|
||||
// the recursive call.
|
||||
//
|
||||
// We then have call up to the appropriate page fault handler in C
|
||||
// code, pointed to by the global variable '_pgfault_handler'.
|
||||
|
||||
.text
|
||||
.globl _pgfault_upcall
|
||||
_pgfault_upcall:
|
||||
// Call the C page fault handler.
|
||||
pushl %esp // function argument: pointer to UTF
|
||||
movl _pgfault_handler, %eax
|
||||
call *%eax
|
||||
addl $4, %esp // pop function argument
|
||||
|
||||
// Now the C page fault handler has returned and you must return
|
||||
// to the trap time state.
|
||||
// Push trap-time %eip onto the trap-time stack.
|
||||
//
|
||||
// Explanation:
|
||||
// We must prepare the trap-time stack for our eventual return to
|
||||
// re-execute the instruction that faulted.
|
||||
// Unfortunately, we can't return directly from the exception stack:
|
||||
// We can't call 'jmp', since that requires that we load the address
|
||||
// into a register, and all registers must have their trap-time
|
||||
// values after the return.
|
||||
// We can't call 'ret' from the exception stack either, since if we
|
||||
// did, %esp would have the wrong value.
|
||||
// So instead, we push the trap-time %eip onto the *trap-time* stack!
|
||||
// Below we'll switch to that stack and call 'ret', which will
|
||||
// restore %eip to its pre-fault value.
|
||||
//
|
||||
// In the case of a recursive fault on the exception stack,
|
||||
// note that the word we're pushing now will fit in the
|
||||
// blank word that the kernel reserved for us.
|
||||
//
|
||||
// Throughout the remaining code, think carefully about what
|
||||
// registers are available for intermediate calculations. You
|
||||
// may find that you have to rearrange your code in non-obvious
|
||||
// ways as registers become unavailable as scratch space.
|
||||
//
|
||||
// LAB 4: Your code here.
|
||||
mov 40(%esp), %eax // Take the EIP from memory
|
||||
mov 48(%esp), %ebp // Take the ESP from memory
|
||||
sub $4, %ebp // Push onto trap-time ESP
|
||||
mov %eax, (%ebp)
|
||||
mov %ebp, 48(%esp) // Put ESP back
|
||||
|
||||
// Restore the trap-time registers. After you do this, you
|
||||
// can no longer modify any general-purpose registers.
|
||||
// LAB 4: Your code here.
|
||||
add $0x8, %esp
|
||||
popal
|
||||
|
||||
// Restore eflags from the stack. After you do this, you can
|
||||
// no longer use arithmetic operations or anything else that
|
||||
// modifies eflags.
|
||||
// LAB 4: Your code here.
|
||||
add $0x4, %esp
|
||||
popfl
|
||||
|
||||
// Switch back to the adjusted trap-time stack.
|
||||
// LAB 4: Your code here.
|
||||
pop %esp
|
||||
|
||||
// Return to re-execute the instruction that faulted.
|
||||
// LAB 4: Your code here.
|
||||
ret
|
37
lib/pgfault.c
Normal file
37
lib/pgfault.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
// User-level page fault handler support.
|
||||
// Rather than register the C page fault handler directly with the
|
||||
// kernel as the page fault handler, we register the assembly language
|
||||
// wrapper in pfentry.S, which in turns calls the registered C
|
||||
// function.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
|
||||
// Assembly language pgfault entrypoint defined in lib/pfentry.S.
|
||||
extern void _pgfault_upcall(void);
|
||||
|
||||
// Pointer to currently installed C-language pgfault handler.
|
||||
void (*_pgfault_handler)(struct UTrapframe *utf);
|
||||
|
||||
//
|
||||
// Set the page fault handler function.
|
||||
// If there isn't one yet, _pgfault_handler will be 0.
|
||||
// The first time we register a handler, we need to
|
||||
// allocate an exception stack (one page of memory with its top
|
||||
// at UXSTACKTOP), and tell the kernel to call the assembly-language
|
||||
// _pgfault_upcall routine when a page fault occurs.
|
||||
//
|
||||
void
|
||||
set_pgfault_handler(void (*handler)(struct UTrapframe *utf))
|
||||
{
|
||||
int r;
|
||||
|
||||
if (_pgfault_handler == 0) {
|
||||
if(sys_page_alloc(0, (void*) UXSTACKTOP - PGSIZE, PTE_U | PTE_P | PTE_W) < 0)
|
||||
panic("set_pgfault_handler");
|
||||
sys_env_set_pgfault_upcall(0, _pgfault_upcall);
|
||||
}
|
||||
|
||||
// Save handler pointer for assembly to call.
|
||||
_pgfault_handler = handler;
|
||||
}
|
62
lib/printf.c
Normal file
62
lib/printf.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Implementation of cprintf console output for user environments,
|
||||
// based on printfmt() and the sys_cputs() system call.
|
||||
//
|
||||
// cprintf is a debugging statement, not a generic output statement.
|
||||
// It is very important that it always go to the console, especially when
|
||||
// debugging file descriptor code!
|
||||
|
||||
#include <inc/types.h>
|
||||
#include <inc/stdio.h>
|
||||
#include <inc/stdarg.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
|
||||
// Collect up to 256 characters into a buffer
|
||||
// and perform ONE system call to print all of them,
|
||||
// in order to make the lines output to the console atomic
|
||||
// and prevent interrupts from causing context switches
|
||||
// in the middle of a console output line and such.
|
||||
struct printbuf {
|
||||
int idx; // current buffer index
|
||||
int cnt; // total bytes printed so far
|
||||
char buf[256];
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
putch(int ch, struct printbuf *b)
|
||||
{
|
||||
b->buf[b->idx++] = ch;
|
||||
if (b->idx == 256-1) {
|
||||
sys_cputs(b->buf, b->idx);
|
||||
b->idx = 0;
|
||||
}
|
||||
b->cnt++;
|
||||
}
|
||||
|
||||
int
|
||||
vcprintf(const char *fmt, va_list ap)
|
||||
{
|
||||
struct printbuf b;
|
||||
|
||||
b.idx = 0;
|
||||
b.cnt = 0;
|
||||
vprintfmt((void*)putch, &b, fmt, ap);
|
||||
sys_cputs(b.buf, b.idx);
|
||||
|
||||
return b.cnt;
|
||||
}
|
||||
|
||||
int
|
||||
cprintf(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int cnt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
cnt = vcprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
|
@ -26,6 +26,8 @@ static const char * const error_string[MAXERROR] =
|
|||
[E_NO_MEM] = "out of memory",
|
||||
[E_NO_FREE_ENV] = "out of environments",
|
||||
[E_FAULT] = "segmentation fault",
|
||||
[E_IPC_NOT_RECV]= "env is not recving",
|
||||
[E_EOF] = "unexpected end of file",
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
128
lib/syscall.c
Normal file
128
lib/syscall.c
Normal file
|
@ -0,0 +1,128 @@
|
|||
// System call stubs.
|
||||
|
||||
#include <inc/syscall.h>
|
||||
#include <inc/lib.h>
|
||||
#include <inc/x86.h>
|
||||
|
||||
static inline int32_t
|
||||
syscall(int num, int check, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5)
|
||||
{
|
||||
int32_t ret;
|
||||
|
||||
// Generic system call: pass system call number in AX,
|
||||
// up to five parameters in DX, CX, BX, DI, SI.
|
||||
// Interrupt kernel with T_SYSCALL.
|
||||
//
|
||||
// The "volatile" tells the assembler not to optimize
|
||||
// this instruction away just because we don't use the
|
||||
// return value.
|
||||
//
|
||||
// The last clause tells the assembler that this can
|
||||
// potentially change the condition codes and arbitrary
|
||||
// memory locations.
|
||||
|
||||
asm volatile("int %1\n"
|
||||
: "=a" (ret)
|
||||
: "i" (T_SYSCALL),
|
||||
"a" (num),
|
||||
"d" (a1),
|
||||
"c" (a2),
|
||||
"b" (a3),
|
||||
"D" (a4),
|
||||
"S" (a5)
|
||||
: "cc", "memory");
|
||||
|
||||
if(check && ret > 0)
|
||||
panic("syscall %d returned %d (> 0)", num, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t
|
||||
fast_syscall(int num, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4) {
|
||||
asm volatile(
|
||||
"push %%ebp\n\t"
|
||||
"mov %%esp, %%ebp\n\t"
|
||||
"lea syscall_ret_%=, %%esi\n\t"
|
||||
"sysenter\n\t"
|
||||
"syscall_ret_%=: pop %%ebp\n\t"
|
||||
: "+a" (num)
|
||||
: "d" (a1), "c" (a2), "b" (a3), "D" (a4)
|
||||
: "esi");
|
||||
return num;
|
||||
}
|
||||
|
||||
void
|
||||
sys_cputs(const char *s, size_t len)
|
||||
{
|
||||
syscall(SYS_cputs, 0, (uint32_t)s, len, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_cgetc(void)
|
||||
{
|
||||
return syscall(SYS_cgetc, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_env_destroy(envid_t envid)
|
||||
{
|
||||
return syscall(SYS_env_destroy, 1, envid, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
envid_t
|
||||
sys_getenvid(void)
|
||||
{
|
||||
return syscall(SYS_getenvid, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
sys_yield(void)
|
||||
{
|
||||
syscall(SYS_yield, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_page_alloc(envid_t envid, void *va, int perm)
|
||||
{
|
||||
return syscall(SYS_page_alloc, 1, envid, (uint32_t) va, perm, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_page_map(envid_t srcenv, void *srcva, envid_t dstenv, void *dstva, int perm)
|
||||
{
|
||||
return syscall(SYS_page_map, 1, srcenv, (uint32_t) srcva, dstenv, (uint32_t) dstva, perm);
|
||||
}
|
||||
|
||||
int
|
||||
sys_page_unmap(envid_t envid, void *va)
|
||||
{
|
||||
return syscall(SYS_page_unmap, 1, envid, (uint32_t) va, 0, 0, 0);
|
||||
}
|
||||
|
||||
// sys_exofork is inlined in lib.h
|
||||
|
||||
int
|
||||
sys_env_set_status(envid_t envid, int status)
|
||||
{
|
||||
return syscall(SYS_env_set_status, 1, envid, status, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_env_set_pgfault_upcall(envid_t envid, void *upcall)
|
||||
{
|
||||
return syscall(SYS_env_set_pgfault_upcall, 1, envid, (uint32_t) upcall, 0, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, int perm)
|
||||
{
|
||||
return syscall(SYS_ipc_try_send, 0, envid, value, (uint32_t) srcva, perm, 0);
|
||||
}
|
||||
|
||||
int
|
||||
sys_ipc_recv(void *dstva)
|
||||
{
|
||||
return syscall(SYS_ipc_recv, 1, (uint32_t)dstva, 0, 0, 0, 0);
|
||||
}
|
||||
|
16
user/Makefrag
Normal file
16
user/Makefrag
Normal file
|
@ -0,0 +1,16 @@
|
|||
OBJDIRS += user
|
||||
|
||||
|
||||
USERLIBS += jos
|
||||
|
||||
$(OBJDIR)/user/%.o: user/%.c $(OBJDIR)/.vars.USER_CFLAGS
|
||||
@echo + cc[USER] $<
|
||||
@mkdir -p $(@D)
|
||||
$(V)$(CC) -nostdinc $(USER_CFLAGS) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/user/%: $(OBJDIR)/user/%.o $(OBJDIR)/lib/entry.o $(USERLIBS:%=$(OBJDIR)/lib/lib%.a) user/user.ld
|
||||
@echo + ld $@
|
||||
$(V)$(LD) -o $@ $(ULDFLAGS) $(LDFLAGS) -nostdlib $(OBJDIR)/lib/entry.o $@.o -L$(OBJDIR)/lib $(USERLIBS:%=-l%) $(GCC_LIB)
|
||||
$(V)$(OBJDUMP) -S $@ > $@.asm
|
||||
$(V)$(NM) -n $@ > $@.sym
|
||||
|
11
user/badsegment.c
Normal file
11
user/badsegment.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
// program to cause a general protection exception
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
// Try to load the kernel's TSS selector into the DS register.
|
||||
asm volatile("movw $0x28,%ax; movw %ax,%ds");
|
||||
}
|
||||
|
10
user/breakpoint.c
Normal file
10
user/breakpoint.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
// program to cause a breakpoint trap
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
asm volatile("int $3");
|
||||
}
|
||||
|
11
user/buggyhello.c
Normal file
11
user/buggyhello.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
// buggy hello world -- unmapped pointer passed to kernel
|
||||
// kernel should destroy user environment in response
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_cputs((char*)1, 1);
|
||||
}
|
||||
|
13
user/buggyhello2.c
Normal file
13
user/buggyhello2.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
// buggy hello world 2 -- pointed-to region extends into unmapped memory
|
||||
// kernel should destroy user environment in response
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
const char *hello = "hello, world\n";
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_cputs(hello, 1024*1024);
|
||||
}
|
||||
|
13
user/divzero.c
Normal file
13
user/divzero.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
// buggy program - causes a divide by zero exception
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
int zero;
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
zero = 0;
|
||||
cprintf("1/0 is %08x!\n", 1/zero);
|
||||
}
|
||||
|
80
user/dumbfork.c
Normal file
80
user/dumbfork.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Ping-pong a counter between two processes.
|
||||
// Only need to start one of these -- splits into two, crudely.
|
||||
|
||||
#include <inc/string.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
envid_t dumbfork(void);
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who;
|
||||
int i;
|
||||
|
||||
// fork a child process
|
||||
who = dumbfork();
|
||||
|
||||
// print a message and yield to the other a few times
|
||||
for (i = 0; i < (who ? 10 : 20); i++) {
|
||||
cprintf("%d: I am the %s!\n", i, who ? "parent" : "child");
|
||||
sys_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
duppage(envid_t dstenv, void *addr)
|
||||
{
|
||||
int r;
|
||||
|
||||
// This is NOT what you should do in your fork.
|
||||
if ((r = sys_page_alloc(dstenv, addr, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
if ((r = sys_page_map(dstenv, addr, 0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("sys_page_map: %e", r);
|
||||
memmove(UTEMP, addr, PGSIZE);
|
||||
if ((r = sys_page_unmap(0, UTEMP)) < 0)
|
||||
panic("sys_page_unmap: %e", r);
|
||||
}
|
||||
|
||||
envid_t
|
||||
dumbfork(void)
|
||||
{
|
||||
envid_t envid;
|
||||
uint8_t *addr;
|
||||
int r;
|
||||
extern unsigned char end[];
|
||||
|
||||
// Allocate a new child environment.
|
||||
// The kernel will initialize it with a copy of our register state,
|
||||
// so that the child will appear to have called sys_exofork() too -
|
||||
// except that in the child, this "fake" call to sys_exofork()
|
||||
// will return 0 instead of the envid of the child.
|
||||
envid = sys_exofork();
|
||||
if (envid < 0)
|
||||
panic("sys_exofork: %e", envid);
|
||||
if (envid == 0) {
|
||||
// We're the child.
|
||||
// The copied value of the global variable 'thisenv'
|
||||
// is no longer valid (it refers to the parent!).
|
||||
// Fix it and return 0.
|
||||
thisenv = &envs[ENVX(sys_getenvid())];
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We're the parent.
|
||||
// Eagerly copy our entire address space into the child.
|
||||
// This is NOT what you should do in your fork implementation.
|
||||
for (addr = (uint8_t*) UTEXT; addr < end; addr += PGSIZE)
|
||||
duppage(envid, addr);
|
||||
|
||||
// Also copy the stack we are currently running on.
|
||||
duppage(envid, ROUNDDOWN(&addr, PGSIZE));
|
||||
|
||||
// Start the child environment running
|
||||
if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0)
|
||||
panic("sys_env_set_status: %e", r);
|
||||
|
||||
return envid;
|
||||
}
|
||||
|
12
user/evilhello.c
Normal file
12
user/evilhello.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
// evil hello world -- kernel pointer passed to kernel
|
||||
// kernel should destroy user environment in response
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
// try to print the kernel entry point as a string! mua ha ha!
|
||||
sys_cputs((char*)0xf010000c, 100);
|
||||
}
|
||||
|
25
user/fairness.c
Normal file
25
user/fairness.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Demonstrate lack of fairness in IPC.
|
||||
// Start three instances of this program as envs 1, 2, and 3.
|
||||
// (user/idle is env 0).
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who, id;
|
||||
|
||||
id = sys_getenvid();
|
||||
|
||||
if (thisenv == &envs[1]) {
|
||||
while (1) {
|
||||
ipc_recv(&who, 0, 0);
|
||||
cprintf("%x recv from %x\n", id, who);
|
||||
}
|
||||
} else {
|
||||
cprintf("%x loop sending to %x\n", id, envs[1].env_id);
|
||||
while (1)
|
||||
ipc_send(envs[1].env_id, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
24
user/faultalloc.c
Normal file
24
user/faultalloc.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
// test user-level fault handler -- alloc pages to fix faults
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
|
||||
cprintf("fault %x\n", addr);
|
||||
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
|
||||
PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("allocating at %x in page fault handler: %e", addr, r);
|
||||
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
cprintf("%s\n", (char*)0xDeadBeef);
|
||||
cprintf("%s\n", (char*)0xCafeBffe);
|
||||
}
|
24
user/faultallocbad.c
Normal file
24
user/faultallocbad.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
// test user-level fault handler -- alloc pages to fix faults
|
||||
// doesn't work because we sys_cputs instead of cprintf (exercise: why?)
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
|
||||
cprintf("fault %x\n", addr);
|
||||
if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE),
|
||||
PTE_P|PTE_U|PTE_W)) < 0)
|
||||
panic("allocating at %x in page fault handler: %e", addr, r);
|
||||
snprintf((char*) addr, 100, "this string was faulted in at %x", addr);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
sys_cputs((char*)0xDEADBEEF, 4);
|
||||
}
|
14
user/faultbadhandler.c
Normal file
14
user/faultbadhandler.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
// test bad pointer for user-level fault handler
|
||||
// this is going to fault in the fault handler accessing eip (always!)
|
||||
// so eventually the kernel kills it (PFM_KILL) because
|
||||
// we outrun the stack with invocations of the user-level handler
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
|
||||
sys_env_set_pgfault_upcall(0, (void*) 0xDeadBeef);
|
||||
*(int*)0 = 0;
|
||||
}
|
19
user/faultdie.c
Normal file
19
user/faultdie.c
Normal file
|
@ -0,0 +1,19 @@
|
|||
// test user-level fault handler -- just exit when we fault
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
handler(struct UTrapframe *utf)
|
||||
{
|
||||
void *addr = (void*)utf->utf_fault_va;
|
||||
uint32_t err = utf->utf_err;
|
||||
cprintf("i faulted at va %x, err %x\n", addr, err & 7);
|
||||
sys_env_destroy(sys_getenvid());
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(handler);
|
||||
*(int*)0xDeadBeef = 0;
|
||||
}
|
11
user/faultevilhandler.c
Normal file
11
user/faultevilhandler.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
// test evil pointer for user-level fault handler
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W);
|
||||
sys_env_set_pgfault_upcall(0, (void*) 0xF0100020);
|
||||
*(int*)0 = 0;
|
||||
}
|
12
user/faultnostack.c
Normal file
12
user/faultnostack.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
// test user fault handler being called with no exception stack mapped
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void _pgfault_upcall();
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
sys_env_set_pgfault_upcall(0, (void*) _pgfault_upcall);
|
||||
*(int*)0 = 0;
|
||||
}
|
10
user/faultread.c
Normal file
10
user/faultread.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
// buggy program - faults with a read from location zero
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
cprintf("I read %08x from location 0!\n", *(unsigned*)0);
|
||||
}
|
||||
|
10
user/faultreadkernel.c
Normal file
10
user/faultreadkernel.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
// buggy program - faults with a read from kernel space
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
cprintf("I read %08x from location 0xf0100000!\n", *(unsigned*)0xf0100000);
|
||||
}
|
||||
|
146
user/faultregs.c
Normal file
146
user/faultregs.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
// test register restore on user-level page fault return
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
struct regs
|
||||
{
|
||||
struct PushRegs regs;
|
||||
uintptr_t eip;
|
||||
uint32_t eflags;
|
||||
uintptr_t esp;
|
||||
};
|
||||
|
||||
#define SAVE_REGS(base) \
|
||||
"\tmovl %%edi, "base"+0x00\n" \
|
||||
"\tmovl %%esi, "base"+0x04\n" \
|
||||
"\tmovl %%ebp, "base"+0x08\n" \
|
||||
"\tmovl %%ebx, "base"+0x10\n" \
|
||||
"\tmovl %%edx, "base"+0x14\n" \
|
||||
"\tmovl %%ecx, "base"+0x18\n" \
|
||||
"\tmovl %%eax, "base"+0x1c\n" \
|
||||
"\tmovl %%esp, "base"+0x28\n"
|
||||
|
||||
#define LOAD_REGS(base) \
|
||||
"\tmovl "base"+0x00, %%edi\n" \
|
||||
"\tmovl "base"+0x04, %%esi\n" \
|
||||
"\tmovl "base"+0x08, %%ebp\n" \
|
||||
"\tmovl "base"+0x10, %%ebx\n" \
|
||||
"\tmovl "base"+0x14, %%edx\n" \
|
||||
"\tmovl "base"+0x18, %%ecx\n" \
|
||||
"\tmovl "base"+0x1c, %%eax\n" \
|
||||
"\tmovl "base"+0x28, %%esp\n"
|
||||
|
||||
static struct regs before, during, after;
|
||||
|
||||
static void
|
||||
check_regs(struct regs* a, const char *an, struct regs* b, const char *bn,
|
||||
const char *testname)
|
||||
{
|
||||
int mismatch = 0;
|
||||
|
||||
cprintf("%-6s %-8s %-8s\n", "", an, bn);
|
||||
|
||||
#define CHECK(name, field) \
|
||||
do { \
|
||||
cprintf("%-6s %08x %08x ", #name, a->field, b->field); \
|
||||
if (a->field == b->field) \
|
||||
cprintf("OK\n"); \
|
||||
else { \
|
||||
cprintf("MISMATCH\n"); \
|
||||
mismatch = 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
CHECK(edi, regs.reg_edi);
|
||||
CHECK(esi, regs.reg_esi);
|
||||
CHECK(ebp, regs.reg_ebp);
|
||||
CHECK(ebx, regs.reg_ebx);
|
||||
CHECK(edx, regs.reg_edx);
|
||||
CHECK(ecx, regs.reg_ecx);
|
||||
CHECK(eax, regs.reg_eax);
|
||||
CHECK(eip, eip);
|
||||
CHECK(eflags, eflags);
|
||||
CHECK(esp, esp);
|
||||
|
||||
#undef CHECK
|
||||
|
||||
cprintf("Registers %s ", testname);
|
||||
if (!mismatch)
|
||||
cprintf("OK\n");
|
||||
else
|
||||
cprintf("MISMATCH\n");
|
||||
}
|
||||
|
||||
static void
|
||||
pgfault(struct UTrapframe *utf)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (utf->utf_fault_va != (uint32_t)UTEMP)
|
||||
panic("pgfault expected at UTEMP, got 0x%08x (eip %08x)",
|
||||
utf->utf_fault_va, utf->utf_eip);
|
||||
|
||||
// Check registers in UTrapframe
|
||||
during.regs = utf->utf_regs;
|
||||
during.eip = utf->utf_eip;
|
||||
during.eflags = utf->utf_eflags & ~FL_RF;
|
||||
during.esp = utf->utf_esp;
|
||||
check_regs(&before, "before", &during, "during", "in UTrapframe");
|
||||
|
||||
// Map UTEMP so the write succeeds
|
||||
if ((r = sys_page_alloc(0, UTEMP, PTE_U|PTE_P|PTE_W)) < 0)
|
||||
panic("sys_page_alloc: %e", r);
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
set_pgfault_handler(pgfault);
|
||||
|
||||
asm volatile(
|
||||
// Light up eflags to catch more errors
|
||||
"\tpushl %%eax\n"
|
||||
"\tpushfl\n"
|
||||
"\tpopl %%eax\n"
|
||||
"\torl $0x8d5, %%eax\n"
|
||||
"\tpushl %%eax\n"
|
||||
"\tpopfl\n"
|
||||
|
||||
// Save before registers
|
||||
// eflags
|
||||
"\tmov %%eax, %0+0x24\n"
|
||||
// eip
|
||||
"\tleal 0f, %%eax\n"
|
||||
"\tmovl %%eax, %0+0x20\n"
|
||||
"\tpopl %%eax\n"
|
||||
// others
|
||||
SAVE_REGS("%0")
|
||||
|
||||
// Fault at UTEMP
|
||||
"\t0: movl $42, 0x400000\n"
|
||||
|
||||
// Save after registers (except eip and eflags)
|
||||
SAVE_REGS("%1")
|
||||
// Restore registers (except eip and eflags). This
|
||||
// way, the test will run even if EIP is the *only*
|
||||
// thing restored correctly.
|
||||
LOAD_REGS("%0")
|
||||
// Save after eflags (now that stack is back); note
|
||||
// that we were very careful not to modify eflags in
|
||||
// since we saved it
|
||||
"\tpushl %%eax\n"
|
||||
"\tpushfl\n"
|
||||
"\tpopl %%eax\n"
|
||||
"\tmov %%eax, %1+0x24\n"
|
||||
"\tpopl %%eax\n"
|
||||
: : "m" (before), "m" (after) : "memory", "cc");
|
||||
|
||||
// Check UTEMP to roughly determine that EIP was restored
|
||||
// correctly (of course, we probably wouldn't get this far if
|
||||
// it weren't)
|
||||
if (*(int*)UTEMP != 42)
|
||||
cprintf("EIP after page-fault MISMATCH\n");
|
||||
after.eip = before.eip;
|
||||
|
||||
check_regs(&before, "before", &after, "after", "after page-fault");
|
||||
}
|
10
user/faultwrite.c
Normal file
10
user/faultwrite.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
// buggy program - faults with a write to location zero
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
*(unsigned*)0 = 0;
|
||||
}
|
||||
|
10
user/faultwritekernel.c
Normal file
10
user/faultwritekernel.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
// buggy program - faults with a write to a kernel location
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
*(unsigned*)0xf0100000 = 0;
|
||||
}
|
||||
|
38
user/forktree.c
Normal file
38
user/forktree.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Fork a binary tree of processes and display their structure.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define DEPTH 3
|
||||
|
||||
void forktree(const char *cur);
|
||||
|
||||
void
|
||||
forkchild(const char *cur, char branch)
|
||||
{
|
||||
char nxt[DEPTH+1];
|
||||
|
||||
if (strlen(cur) >= DEPTH)
|
||||
return;
|
||||
|
||||
snprintf(nxt, DEPTH+1, "%s%c", cur, branch);
|
||||
if (fork() == 0) {
|
||||
forktree(nxt);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
forktree(const char *cur)
|
||||
{
|
||||
cprintf("%04x: I am '%s'\n", sys_getenvid(), cur);
|
||||
|
||||
forkchild(cur, '0');
|
||||
forkchild(cur, '1');
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
forktree("");
|
||||
}
|
||||
|
9
user/getc.c
Normal file
9
user/getc.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
char c;
|
||||
while(!(c = sys_cgetc()));
|
||||
cprintf("got character %c\n", c);
|
||||
}
|
9
user/hello.c
Normal file
9
user/hello.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
// hello, world
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
cprintf("hello, world\n");
|
||||
cprintf("i am environment %08x\n", thisenv->env_id);
|
||||
}
|
20
user/idle.c
Normal file
20
user/idle.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
// idle loop
|
||||
|
||||
#include <inc/x86.h>
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
binaryname = "idle";
|
||||
|
||||
// Loop forever, simply trying to yield to a different environment.
|
||||
// Instead of busy-waiting like this,
|
||||
// a better way would be to use the processor's HLT instruction
|
||||
// to cause the processor to stop executing until the next interrupt -
|
||||
// doing so allows the processor to conserve power more effectively.
|
||||
while (1) {
|
||||
sys_yield();
|
||||
}
|
||||
}
|
||||
|
29
user/pingpong.c
Normal file
29
user/pingpong.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Ping-pong a counter between two processes.
|
||||
// Only need to start one of these -- splits into two with fork.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who;
|
||||
|
||||
if ((who = fork()) != 0) {
|
||||
// get the ball rolling
|
||||
cprintf("send 0 from %x to %x\n", sys_getenvid(), who);
|
||||
ipc_send(who, 0, 0, 0);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
uint32_t i = ipc_recv(&who, 0, 0);
|
||||
cprintf("%x got %d from %x\n", sys_getenvid(), i, who);
|
||||
if (i == 10)
|
||||
return;
|
||||
i++;
|
||||
ipc_send(who, i, 0, 0);
|
||||
if (i == 10)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
33
user/pingpongs.c
Normal file
33
user/pingpongs.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Ping-pong a counter between two shared-memory processes.
|
||||
// Only need to start one of these -- splits into two with sfork.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
uint32_t val;
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t who;
|
||||
uint32_t i;
|
||||
|
||||
i = 0;
|
||||
if ((who = sfork()) != 0) {
|
||||
cprintf("i am %08x; thisenv is %p\n", sys_getenvid(), thisenv);
|
||||
// get the ball rolling
|
||||
cprintf("send 0 from %x to %x\n", sys_getenvid(), who);
|
||||
ipc_send(who, 0, 0, 0);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ipc_recv(&who, 0, 0);
|
||||
cprintf("%x got %d from %x (thisenv is %p %x)\n", sys_getenvid(), val, who, thisenv, thisenv->env_id);
|
||||
if (val == 10)
|
||||
return;
|
||||
++val;
|
||||
ipc_send(who, 0, 0, 0);
|
||||
if (val == 10)
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
53
user/primes.c
Normal file
53
user/primes.c
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Concurrent version of prime sieve of Eratosthenes.
|
||||
// Invented by Doug McIlroy, inventor of Unix pipes.
|
||||
// See http://swtch.com/~rsc/thread/.
|
||||
// The picture halfway down the page and the text surrounding it
|
||||
// explain what's going on here.
|
||||
//
|
||||
// Since NENV is 1024, we can print 1022 primes before running out.
|
||||
// The remaining two environments are the integer generator at the bottom
|
||||
// of main and user/idle.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
unsigned
|
||||
primeproc(void)
|
||||
{
|
||||
int i, id, p;
|
||||
envid_t envid;
|
||||
|
||||
// fetch a prime from our left neighbor
|
||||
top:
|
||||
p = ipc_recv(&envid, 0, 0);
|
||||
cprintf("CPU %d: %d ", thisenv->env_cpunum, p);
|
||||
|
||||
// fork a right neighbor to continue the chain
|
||||
if ((id = fork()) < 0)
|
||||
panic("fork: %e", id);
|
||||
if (id == 0)
|
||||
goto top;
|
||||
|
||||
// filter out multiples of our prime
|
||||
while (1) {
|
||||
i = ipc_recv(&envid, 0, 0);
|
||||
if (i % p)
|
||||
ipc_send(id, i, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, id;
|
||||
|
||||
// fork the first prime process in the chain
|
||||
if ((id = fork()) < 0)
|
||||
panic("fork: %e", id);
|
||||
if (id == 0)
|
||||
primeproc();
|
||||
|
||||
// feed all the integers through
|
||||
for (i = 2; ; i++)
|
||||
ipc_send(id, i, 0, 0);
|
||||
}
|
||||
|
10
user/softint.c
Normal file
10
user/softint.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
// buggy program - causes an illegal software interrupt
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
asm volatile("int $14"); // page fault
|
||||
}
|
||||
|
31
user/spin.c
Normal file
31
user/spin.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Test preemption by forking off a child process that just spins forever.
|
||||
// Let it run for a couple time slices, then kill it.
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
envid_t env;
|
||||
|
||||
cprintf("I am the parent. Forking the child...\n");
|
||||
if ((env = fork()) == 0) {
|
||||
cprintf("I am the child. Spinning...\n");
|
||||
while (1)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
cprintf("I am the parent. Running the child...\n");
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
sys_yield();
|
||||
|
||||
cprintf("I am the parent. Killing the child...\n");
|
||||
sys_env_destroy(env);
|
||||
}
|
||||
|
39
user/stresssched.c
Normal file
39
user/stresssched.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <inc/lib.h>
|
||||
|
||||
volatile int counter;
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i, j;
|
||||
int seen;
|
||||
envid_t parent = sys_getenvid();
|
||||
|
||||
// Fork several environments
|
||||
for (i = 0; i < 20; i++)
|
||||
if (fork() == 0)
|
||||
break;
|
||||
if (i == 20) {
|
||||
sys_yield();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the parent to finish forking
|
||||
while (envs[ENVX(parent)].env_status != ENV_FREE)
|
||||
asm volatile("pause");
|
||||
|
||||
// Check that one environment doesn't run on two CPUs at once
|
||||
for (i = 0; i < 10; i++) {
|
||||
sys_yield();
|
||||
for (j = 0; j < 10000; j++)
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (counter != 10*10000)
|
||||
panic("ran on two CPUs at once (counter is %d)", counter);
|
||||
|
||||
// Check that we see environments running on different CPUs
|
||||
cprintf("[%08x] stresssched on CPU %d\n", thisenv->env_id, thisenv->env_cpunum);
|
||||
|
||||
}
|
||||
|
27
user/testbss.c
Normal file
27
user/testbss.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
// test reads and writes to a large bss
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define ARRAYSIZE (1024*1024)
|
||||
|
||||
uint32_t bigarray[ARRAYSIZE];
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
cprintf("Making sure bss works right...\n");
|
||||
for (i = 0; i < ARRAYSIZE; i++)
|
||||
if (bigarray[i] != 0)
|
||||
panic("bigarray[%d] isn't cleared!\n", i);
|
||||
for (i = 0; i < ARRAYSIZE; i++)
|
||||
bigarray[i] = i;
|
||||
for (i = 0; i < ARRAYSIZE; i++)
|
||||
if (bigarray[i] != i)
|
||||
panic("bigarray[%d] didn't hold its value!\n", i);
|
||||
|
||||
cprintf("Yes, good. Now doing a wild write off the end...\n");
|
||||
bigarray[ARRAYSIZE+1024] = 0;
|
||||
panic("SHOULD HAVE TRAPPED!!!");
|
||||
}
|
72
user/user.ld
Normal file
72
user/user.ld
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* Simple linker script for JOS user-level programs.
|
||||
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
|
||||
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* Load programs at this address: "." means the current address */
|
||||
. = 0x800020;
|
||||
|
||||
.text : {
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
}
|
||||
|
||||
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
}
|
||||
|
||||
/* Adjust the address for the data segment to the next page */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
PROVIDE(edata = .);
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
PROVIDE(end = .);
|
||||
|
||||
|
||||
/* Place debugging symbols so that they can be found by
|
||||
* the kernel debugger.
|
||||
* Specifically, the four words at 0x200000 mark the beginning of
|
||||
* the stabs, the end of the stabs, the beginning of the stabs
|
||||
* string table, and the end of the stabs string table, respectively.
|
||||
*/
|
||||
|
||||
.stab_info 0x200000 : {
|
||||
LONG(__STAB_BEGIN__);
|
||||
LONG(__STAB_END__);
|
||||
LONG(__STABSTR_BEGIN__);
|
||||
LONG(__STABSTR_END__);
|
||||
}
|
||||
|
||||
.stab : {
|
||||
__STAB_BEGIN__ = DEFINED(__STAB_BEGIN__) ? __STAB_BEGIN__ : .;
|
||||
*(.stab);
|
||||
__STAB_END__ = DEFINED(__STAB_END__) ? __STAB_END__ : .;
|
||||
BYTE(0) /* Force the linker to allocate space
|
||||
for this section */
|
||||
}
|
||||
|
||||
.stabstr : {
|
||||
__STABSTR_BEGIN__ = DEFINED(__STABSTR_BEGIN__) ? __STABSTR_BEGIN__ : .;
|
||||
*(.stabstr);
|
||||
__STABSTR_END__ = DEFINED(__STABSTR_END__) ? __STABSTR_END__ : .;
|
||||
BYTE(0) /* Force the linker to allocate space
|
||||
for this section */
|
||||
}
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame .note.GNU-stack .comment)
|
||||
}
|
||||
}
|
17
user/yield.c
Normal file
17
user/yield.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
// yield the processor to other environments
|
||||
|
||||
#include <inc/lib.h>
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
cprintf("Hello, I am environment %08x.\n", thisenv->env_id);
|
||||
for (i = 0; i < 5; i++) {
|
||||
sys_yield();
|
||||
cprintf("Back in environment %08x, iteration %d.\n",
|
||||
thisenv->env_id, i);
|
||||
}
|
||||
cprintf("All done in environment %08x.\n", thisenv->env_id);
|
||||
}
|
Loading…
Reference in New Issue
Block a user