Compare commits
33 Commits
lab1
...
c3e2a92b08
| Author | SHA1 | Date | |
|---|---|---|---|
| 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)
|
GDBPORT := $(shell expr `id -u` % 5000 + 25000)
|
||||||
|
|
||||||
CC := $(GCCPREFIX)gcc -pipe
|
CC := $(GCCPREFIX)gcc -pipe
|
||||||
|
GDB := $(GCCPREFIX)gdb
|
||||||
AS := $(GCCPREFIX)as
|
AS := $(GCCPREFIX)as
|
||||||
AR := $(GCCPREFIX)ar
|
AR := $(GCCPREFIX)ar
|
||||||
LD := $(GCCPREFIX)ld
|
LD := $(GCCPREFIX)ld
|
||||||
@@ -87,6 +88,7 @@ CFLAGS := $(CFLAGS) $(DEFS) $(LABDEFS) -O1 -fno-builtin -I$(TOP) -MD
|
|||||||
CFLAGS += -fno-omit-frame-pointer
|
CFLAGS += -fno-omit-frame-pointer
|
||||||
CFLAGS += -std=gnu99
|
CFLAGS += -std=gnu99
|
||||||
CFLAGS += -static
|
CFLAGS += -static
|
||||||
|
CFLAGS += -fno-pie
|
||||||
CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32
|
CFLAGS += -Wall -Wno-format -Wno-unused -Werror -gstabs -m32
|
||||||
# -fno-tree-ch prevented gcc from sometimes reordering read_ebp() before
|
# -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
|
# 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 Makefrags for subdirectories
|
||||||
include boot/Makefrag
|
include boot/Makefrag
|
||||||
include kern/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 = -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)
|
QEMUOPTS += $(shell if $(QEMU) -nographic -help | grep -q '^-D '; then echo '-D qemu.log'; fi)
|
||||||
IMAGES = $(OBJDIR)/kern/kernel.img
|
IMAGES = $(OBJDIR)/kern/kernel.img
|
||||||
|
QEMUOPTS += -smp $(CPUS)
|
||||||
QEMUOPTS += $(QEMUEXTRA)
|
QEMUOPTS += $(QEMUEXTRA)
|
||||||
|
|
||||||
.gdbinit: .gdbinit.tmpl
|
.gdbinit: .gdbinit.tmpl
|
||||||
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
|
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
|
||||||
|
|
||||||
gdb:
|
gdb:
|
||||||
gdb -n -x .gdbinit
|
$(GDB) -n -x .gdbinit
|
||||||
|
|
||||||
pre-qemu: .gdbinit
|
pre-qemu: .gdbinit
|
||||||
|
|
||||||
@@ -297,6 +304,22 @@ myapi.key:
|
|||||||
#handin-prep:
|
#handin-prep:
|
||||||
# @./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
|
# This magic automatically generates makefile dependencies
|
||||||
# for header files included from C source files we compile,
|
# for header files included from C source files we compile,
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ void
|
|||||||
bootmain(void)
|
bootmain(void)
|
||||||
{
|
{
|
||||||
struct Proghdr *ph, *eph;
|
struct Proghdr *ph, *eph;
|
||||||
|
int i;
|
||||||
|
|
||||||
// read 1st page off disk
|
// read 1st page off disk
|
||||||
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
|
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
|
||||||
@@ -50,10 +51,14 @@ bootmain(void)
|
|||||||
// load each program segment (ignores ph flags)
|
// load each program segment (ignores ph flags)
|
||||||
ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
|
ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
|
||||||
eph = ph + ELFHDR->e_phnum;
|
eph = ph + ELFHDR->e_phnum;
|
||||||
for (; ph < eph; ph++)
|
for (; ph < eph; ph++) {
|
||||||
// p_pa is the load address of this segment (as well
|
// p_pa is the load address of this segment (as well
|
||||||
// as the physical address)
|
// as the physical address)
|
||||||
readseg(ph->p_pa, ph->p_memsz, ph->p_offset);
|
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
|
// call the entry point from the ELF header
|
||||||
// note: does not return!
|
// note: does not return!
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
LAB=1
|
LAB=4
|
||||||
PACKAGEDATE=Thu Aug 30 15:16:04 EDT 2018
|
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
|
// the maximum allowed
|
||||||
E_FAULT , // Memory fault
|
E_FAULT , // Memory fault
|
||||||
|
|
||||||
|
E_IPC_NOT_RECV , // Attempt to send to env that is not recving
|
||||||
|
E_EOF , // Unexpected end of file
|
||||||
|
|
||||||
MAXERROR
|
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
|
// The location of the user-level STABS data structure
|
||||||
#define USTABDATA (PTSIZE / 2)
|
#define USTABDATA (PTSIZE / 2)
|
||||||
|
|
||||||
|
// Physical address of startup code for non-boot CPUs (APs)
|
||||||
|
#define MPENTRY_PADDR 0x7000
|
||||||
|
|
||||||
#ifndef __ASSEMBLER__
|
#ifndef __ASSEMBLER__
|
||||||
|
|
||||||
typedef uint32_t pte_t;
|
typedef uint32_t pte_t;
|
||||||
typedef uint32_t pde_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 /* !__ASSEMBLER__ */
|
||||||
#endif /* !JOS_INC_MEMLAYOUT_H */
|
#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 */
|
||||||
@@ -33,12 +33,50 @@ KERN_SRCFILES := kern/entry.S \
|
|||||||
lib/readline.c \
|
lib/readline.c \
|
||||||
lib/string.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.
|
# Only build files if they exist.
|
||||||
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
|
KERN_SRCFILES := $(wildcard $(KERN_SRCFILES))
|
||||||
|
|
||||||
# Binary program images to embed within the kernel.
|
# 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
|
||||||
|
|
||||||
|
# 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 %.c, $(OBJDIR)/%.o, $(KERN_SRCFILES))
|
||||||
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
|
KERN_OBJFILES := $(patsubst %.S, $(OBJDIR)/%.o, $(KERN_OBJFILES))
|
||||||
KERN_OBJFILES := $(patsubst $(OBJDIR)/lib/%, $(OBJDIR)/kern/%, $(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>
|
#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 {
|
struct AttrState {
|
||||||
uint8_t cattrs;
|
uint8_t cattrs;
|
||||||
uint8_t attrs;
|
uint8_t attrs;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <inc/assert.h>
|
#include <inc/assert.h>
|
||||||
|
|
||||||
#include <kern/console.h>
|
#include <kern/console.h>
|
||||||
|
#include <kern/trap.h>
|
||||||
|
#include <kern/picirq.h>
|
||||||
#include <kern/ansi.h>
|
#include <kern/ansi.h>
|
||||||
|
|
||||||
static void cons_intr(int (*proc)(void));
|
static void cons_intr(int (*proc)(void));
|
||||||
@@ -384,6 +386,9 @@ kbd_intr(void)
|
|||||||
static void
|
static void
|
||||||
kbd_init(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/mmu.h>
|
||||||
#include <inc/memlayout.h>
|
#include <inc/memlayout.h>
|
||||||
|
#include <inc/trap.h>
|
||||||
|
|
||||||
# Shift Right Logical
|
# Shift Right Logical
|
||||||
#define SRL(val, shamt) (((val) >> (shamt)) & ~(-1 << (32 - (shamt))))
|
#define SRL(val, shamt) (((val) >> (shamt)) & ~(-1 << (32 - (shamt))))
|
||||||
|
|||||||
533
kern/env.c
Normal file
533
kern/env.c
Normal file
@@ -0,0 +1,533 @@
|
|||||||
|
/* 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.
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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);
|
||||||
|
lcr3(PADDR(e->env_pgdir));
|
||||||
|
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));
|
||||||
|
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
|
||||||
122
kern/init.c
122
kern/init.c
@@ -6,39 +6,26 @@
|
|||||||
|
|
||||||
#include <kern/monitor.h>
|
#include <kern/monitor.h>
|
||||||
#include <kern/console.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>
|
||||||
|
|
||||||
|
static void boot_aps(void);
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
i386_init(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.
|
// Initialize the console.
|
||||||
// Can't call cprintf until after we do this!
|
// Can't call cprintf until after we do this!
|
||||||
cons_init();
|
cons_init();
|
||||||
|
|
||||||
cprintf("444544 decimal is %o octal!\n", 444544);
|
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"
|
cprintf("\33[31m" "C"
|
||||||
"\33[33m" "o"
|
"\33[33m" "o"
|
||||||
"\33[32m" "l"
|
"\33[32m" "l"
|
||||||
@@ -46,11 +33,92 @@ i386_init(void)
|
|||||||
"\33[34m" "r"
|
"\33[34m" "r"
|
||||||
"\33[0m" " Works!" "\n");
|
"\33[0m" " Works!" "\n");
|
||||||
|
|
||||||
// Drop into the kernel monitor.
|
// Lab 2 memory management initialization functions
|
||||||
while (1)
|
mem_init();
|
||||||
monitor(NULL);
|
|
||||||
|
// 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:
|
||||||
|
|
||||||
|
// 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_primes, 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:
|
||||||
|
|
||||||
|
// Remove this after you finish Exercise 6
|
||||||
|
for (;;);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Variable panicstr contains argument to first call to panic; used as flag
|
* Variable panicstr contains argument to first call to panic; used as flag
|
||||||
@@ -75,7 +143,7 @@ _panic(const char *file, int line, const char *fmt,...)
|
|||||||
asm volatile("cli; cld");
|
asm volatile("cli; cld");
|
||||||
|
|
||||||
va_start(ap, fmt);
|
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);
|
vcprintf(fmt, ap);
|
||||||
cprintf("\n");
|
cprintf("\n");
|
||||||
va_end(ap);
|
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 <inc/assert.h>
|
||||||
|
|
||||||
#include <kern/kdebug.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_BEGIN__[]; // Beginning of stabs table
|
||||||
extern const struct Stab __STAB_END__[]; // End 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_BEGIN__[]; // Beginning of string table
|
||||||
extern const char __STABSTR_END__[]; // End 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)
|
// 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 = __STABSTR_BEGIN__;
|
||||||
stabstr_end = __STABSTR_END__;
|
stabstr_end = __STABSTR_END__;
|
||||||
} else {
|
} else {
|
||||||
// Can't search for user-level addresses yet!
|
// The user-application linker script, user/user.ld,
|
||||||
panic("User address");
|
// 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
|
// String table validity checks
|
||||||
|
|||||||
@@ -44,18 +44,19 @@ SECTIONS
|
|||||||
|
|
||||||
/* The data segment */
|
/* The data segment */
|
||||||
.data : {
|
.data : {
|
||||||
*(.data)
|
*(.data .data.*)
|
||||||
}
|
}
|
||||||
|
|
||||||
.bss : {
|
.bss : {
|
||||||
PROVIDE(edata = .);
|
PROVIDE(edata = .);
|
||||||
*(.bss)
|
*(.dynbss)
|
||||||
|
*(.bss .bss.*)
|
||||||
|
*(COMMON)
|
||||||
PROVIDE(end = .);
|
PROVIDE(end = .);
|
||||||
BYTE(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/DISCARD/ : {
|
/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)
|
||||||
|
;
|
||||||
|
}
|
||||||
110
kern/monitor.c
110
kern/monitor.c
@@ -7,9 +7,13 @@
|
|||||||
#include <inc/assert.h>
|
#include <inc/assert.h>
|
||||||
#include <inc/x86.h>
|
#include <inc/x86.h>
|
||||||
|
|
||||||
|
#include <kern/ansi.h>
|
||||||
#include <kern/console.h>
|
#include <kern/console.h>
|
||||||
#include <kern/monitor.h>
|
#include <kern/monitor.h>
|
||||||
#include <kern/kdebug.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
|
#define CMDBUF_SIZE 80 // enough for one VGA text line
|
||||||
|
|
||||||
@@ -25,7 +29,10 @@ struct Command {
|
|||||||
static struct Command commands[] = {
|
static struct Command commands[] = {
|
||||||
{ "help", "Display this list of commands", mon_help },
|
{ "help", "Display this list of commands", mon_help },
|
||||||
{ "kerninfo", "Display information about the kernel", mon_kerninfo },
|
{ "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 }
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/***** Implementations of basic kernel monitor commands *****/
|
/***** Implementations of basic kernel monitor commands *****/
|
||||||
@@ -46,7 +53,7 @@ mon_kerninfo(int argc, char **argv, struct Trapframe *tf)
|
|||||||
extern char _start[], entry[], etext[], edata[], end[];
|
extern char _start[], entry[], etext[], edata[], end[];
|
||||||
|
|
||||||
cprintf("Special kernel symbols:\n");
|
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(" entry %08x (virt) %08x (phys)\n", entry, entry - KERNBASE);
|
||||||
cprintf(" etext %08x (virt) %08x (phys)\n", etext, etext - KERNBASE);
|
cprintf(" etext %08x (virt) %08x (phys)\n", etext, etext - KERNBASE);
|
||||||
cprintf(" edata %08x (virt) %08x (phys)\n", edata, edata - KERNBASE);
|
cprintf(" edata %08x (virt) %08x (phys)\n", edata, edata - KERNBASE);
|
||||||
@@ -75,7 +82,7 @@ mon_backtrace(int argc, char **argv, struct Trapframe *tf)
|
|||||||
ebp = (uint32_t*) ebp[0];
|
ebp = (uint32_t*) ebp[0];
|
||||||
|
|
||||||
debuginfo_eip(eip, &info);
|
debuginfo_eip(eip, &info);
|
||||||
cprintf(" %s:%d: %.*s+%d\n",
|
cprintf(" %s:%d: %.*s+%d\n",
|
||||||
info.eip_file, info.eip_line,
|
info.eip_file, info.eip_line,
|
||||||
info.eip_fn_namelen, info.eip_fn_name,
|
info.eip_fn_namelen, info.eip_fn_name,
|
||||||
eip - info.eip_fn_addr);
|
eip - info.eip_fn_addr);
|
||||||
@@ -84,7 +91,102 @@ mon_backtrace(int argc, char **argv, struct Trapframe *tf)
|
|||||||
return 0;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/***** Kernel monitor command interpreter *****/
|
/***** Kernel monitor command interpreter *****/
|
||||||
|
|
||||||
@@ -138,6 +240,8 @@ monitor(struct Trapframe *tf)
|
|||||||
cprintf("Welcome to the JOS kernel monitor!\n");
|
cprintf("Welcome to the JOS kernel monitor!\n");
|
||||||
cprintf("Type 'help' for a list of commands.\n");
|
cprintf("Type 'help' for a list of commands.\n");
|
||||||
|
|
||||||
|
if (tf != NULL)
|
||||||
|
print_trapframe(tf);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
buf = readline("K> ");
|
buf = readline("K> ");
|
||||||
|
|||||||
@@ -15,5 +15,7 @@ void monitor(struct Trapframe *tf);
|
|||||||
int mon_help(int argc, char **argv, 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_kerninfo(int argc, char **argv, struct Trapframe *tf);
|
||||||
int mon_backtrace(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);
|
||||||
|
|
||||||
#endif // !JOS_KERN_MONITOR_H
|
#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
|
||||||
1119
kern/pmap.c
Normal file
1119
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 */
|
||||||
85
kern/sched.c
Normal file
85
kern/sched.c
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#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.
|
||||||
|
|
||||||
|
// 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
|
||||||
286
kern/syscall.c
Normal file
286
kern/syscall.c
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
/* 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.
|
||||||
|
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("sys_exofork not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("sys_env_set_status not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("sys_env_set_pgfault_upcall not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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!
|
||||||
|
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("sys_page_alloc not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("sys_page_map not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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().
|
||||||
|
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("sys_page_unmap not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("sys_ipc_try_send not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("sys_ipc_recv not implemented");
|
||||||
|
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);
|
||||||
|
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 */
|
||||||
383
kern/trap.c
Normal file
383
kern/trap.c
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
#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
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
ts.ts_esp0 = KSTACKTOP;
|
||||||
|
ts.ts_ss0 = GD_KD;
|
||||||
|
ts.ts_iomb = sizeof(struct Taskstate);
|
||||||
|
|
||||||
|
// Initialize the TSS slot of the gdt.
|
||||||
|
gdt[GD_TSS0 >> 3] = SEG16(STS_T32A, (uint32_t) (&ts),
|
||||||
|
sizeof(struct Taskstate) - 1, 0);
|
||||||
|
gdt[GD_TSS0 >> 3].sd_s = 0;
|
||||||
|
|
||||||
|
// Load the TSS selector (like other segment selectors, the
|
||||||
|
// bottom three bits are special; we leave them 0)
|
||||||
|
ltr(GD_TSS0);
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
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.
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
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 */
|
||||||
93
kern/trapentry.S
Normal file
93
kern/trapentry.S
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
90
lib/fork.c
Normal file
90
lib/fork.c
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// 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>).
|
||||||
|
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
|
||||||
|
panic("pgfault not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("duppage not implemented");
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("fork not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Challenge!
|
||||||
|
int
|
||||||
|
sfork(void)
|
||||||
|
{
|
||||||
|
panic("sfork not implemented");
|
||||||
|
return -E_INVAL;
|
||||||
|
}
|
||||||
56
lib/ipc.c
Normal file
56
lib/ipc.c
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("ipc_recv not implemented");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("ipc_send not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
82
lib/pfentry.S
Normal file
82
lib/pfentry.S
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#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.
|
||||||
|
|
||||||
|
// Restore the trap-time registers. After you do this, you
|
||||||
|
// can no longer modify any general-purpose registers.
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Switch back to the adjusted trap-time stack.
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
|
||||||
|
// Return to re-execute the instruction that faulted.
|
||||||
|
// LAB 4: Your code here.
|
||||||
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) {
|
||||||
|
// First time through!
|
||||||
|
// LAB 4: Your code here.
|
||||||
|
panic("set_pgfault_handler not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_MEM] = "out of memory",
|
||||||
[E_NO_FREE_ENV] = "out of environments",
|
[E_NO_FREE_ENV] = "out of environments",
|
||||||
[E_FAULT] = "segmentation fault",
|
[E_FAULT] = "segmentation fault",
|
||||||
|
[E_IPC_NOT_RECV]= "env is not recving",
|
||||||
|
[E_EOF] = "unexpected end of file",
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
113
lib/syscall.c
Normal file
113
lib/syscall.c
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// System call stubs.
|
||||||
|
|
||||||
|
#include <inc/syscall.h>
|
||||||
|
#include <inc/lib.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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/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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user