add exercise of lab5 spoc discussion
This commit is contained in:
parent
331b8dff5a
commit
f39299c9a2
related_info/lab5/lab5-spoc-discuss
Makefile
boot
kern
debug
driver
fs
init
libs
mm
default_pmm.cdefault_pmm.hkmalloc.ckmalloc.hmemlayout.hmmu.hpmm.cpmm.hswap.cswap.hswap_fifo.cswap_fifo.hvmm.cvmm.h
process
schedule
sync
syscall
trap
libs
atomic.hdefs.helf.herror.hhash.clist.hprintfmt.crand.cstdarg.hstdio.hstdlib.hstring.cstring.hunistd.hx86.h
tools
user
328
related_info/lab5/lab5-spoc-discuss/Makefile
Normal file
328
related_info/lab5/lab5-spoc-discuss/Makefile
Normal file
@ -0,0 +1,328 @@
|
||||
PROJ := 5
|
||||
EMPTY :=
|
||||
SPACE := $(EMPTY) $(EMPTY)
|
||||
SLASH := /
|
||||
|
||||
V := @
|
||||
|
||||
# try to infer the correct GCCPREFX
|
||||
ifndef GCCPREFIX
|
||||
GCCPREFIX := $(shell if i386-ucore-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
|
||||
then echo 'i386-ucore-elf-'; \
|
||||
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
|
||||
then echo ''; \
|
||||
else echo "***" 1>&2; \
|
||||
echo "*** Error: Couldn't find an i386-ucore-elf version of GCC/binutils." 1>&2; \
|
||||
echo "*** Is the directory with i386-ucore-elf-gcc in your PATH?" 1>&2; \
|
||||
echo "*** If your i386-ucore-elf toolchain is installed with a command" 1>&2; \
|
||||
echo "*** prefix other than 'i386-ucore-elf-', set your GCCPREFIX" 1>&2; \
|
||||
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
|
||||
echo "*** To turn off this error, run 'gmake GCCPREFIX= ...'." 1>&2; \
|
||||
echo "***" 1>&2; exit 1; fi)
|
||||
endif
|
||||
|
||||
# try to infer the correct QEMU
|
||||
ifndef QEMU
|
||||
QEMU := $(shell if which qemu-system-i386 > /dev/null; \
|
||||
then echo 'qemu-system-i386'; exit; \
|
||||
elif which i386-ucore-elf-qemu > /dev/null; \
|
||||
then echo 'i386-ucore-elf-qemu'; exit; \
|
||||
else \
|
||||
echo "***" 1>&2; \
|
||||
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
|
||||
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
|
||||
echo "***" 1>&2; exit 1; fi)
|
||||
endif
|
||||
|
||||
# eliminate default suffix rules
|
||||
.SUFFIXES: .c .S .h
|
||||
|
||||
# delete target files if there is an error (or make is interrupted)
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
# define compiler and flags
|
||||
|
||||
HOSTCC := gcc
|
||||
HOSTCFLAGS := -g -Wall -O2
|
||||
|
||||
GDB := $(GCCPREFIX)gdb
|
||||
|
||||
CC ?= $(GCCPREFIX)gcc
|
||||
CFLAGS := -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc $(DEFS)
|
||||
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
|
||||
CTYPE := c S
|
||||
|
||||
LD := $(GCCPREFIX)ld
|
||||
LDFLAGS := -m $(shell $(LD) -V | grep elf_i386 2>/dev/null)
|
||||
LDFLAGS += -nostdlib
|
||||
|
||||
OBJCOPY := $(GCCPREFIX)objcopy
|
||||
OBJDUMP := $(GCCPREFIX)objdump
|
||||
|
||||
COPY := cp
|
||||
MKDIR := mkdir -p
|
||||
MV := mv
|
||||
RM := rm -f
|
||||
AWK := awk
|
||||
SED := sed
|
||||
SH := sh
|
||||
TR := tr
|
||||
TOUCH := touch -c
|
||||
|
||||
OBJDIR := obj
|
||||
BINDIR := bin
|
||||
|
||||
ALLOBJS :=
|
||||
ALLDEPS :=
|
||||
TARGETS :=
|
||||
|
||||
include tools/function.mk
|
||||
|
||||
listf_cc = $(call listf,$(1),$(CTYPE))
|
||||
|
||||
USER_PREFIX := __user_
|
||||
|
||||
# for cc
|
||||
add_files_cc = $(call add_files,$(1),$(CC),$(CFLAGS) $(3),$(2),$(4))
|
||||
create_target_cc = $(call create_target,$(1),$(2),$(3),$(CC),$(CFLAGS))
|
||||
|
||||
# for hostcc
|
||||
add_files_host = $(call add_files,$(1),$(HOSTCC),$(HOSTCFLAGS),$(2),$(3))
|
||||
create_target_host = $(call create_target,$(1),$(2),$(3),$(HOSTCC),$(HOSTCFLAGS))
|
||||
|
||||
cgtype = $(patsubst %.$(2),%.$(3),$(1))
|
||||
objfile = $(call toobj,$(1))
|
||||
asmfile = $(call cgtype,$(call toobj,$(1)),o,asm)
|
||||
outfile = $(call cgtype,$(call toobj,$(1)),o,out)
|
||||
symfile = $(call cgtype,$(call toobj,$(1)),o,sym)
|
||||
filename = $(basename $(notdir $(1)))
|
||||
ubinfile = $(call outfile,$(addprefix $(USER_PREFIX),$(call filename,$(1))))
|
||||
|
||||
# for match pattern
|
||||
match = $(shell echo $(2) | $(AWK) '{for(i=1;i<=NF;i++){if(match("$(1)","^"$$(i)"$$")){exit 1;}}}'; echo $$?)
|
||||
|
||||
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
# include kernel/user
|
||||
|
||||
INCLUDE += libs/
|
||||
|
||||
CFLAGS += $(addprefix -I,$(INCLUDE))
|
||||
|
||||
LIBDIR += libs
|
||||
|
||||
$(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# user programs
|
||||
|
||||
UINCLUDE += user/include/ \
|
||||
user/libs/
|
||||
|
||||
USRCDIR += user
|
||||
|
||||
ULIBDIR += user/libs
|
||||
|
||||
UCFLAGS += $(addprefix -I,$(UINCLUDE))
|
||||
USER_BINS :=
|
||||
|
||||
$(call add_files_cc,$(call listf_cc,$(ULIBDIR)),ulibs,$(UCFLAGS))
|
||||
$(call add_files_cc,$(call listf_cc,$(USRCDIR)),uprog,$(UCFLAGS))
|
||||
|
||||
UOBJS := $(call read_packet,ulibs libs)
|
||||
|
||||
define uprog_ld
|
||||
__user_bin__ := $$(call ubinfile,$(1))
|
||||
USER_BINS += $$(__user_bin__)
|
||||
$$(__user_bin__): tools/user.ld
|
||||
$$(__user_bin__): $$(UOBJS)
|
||||
$$(__user_bin__): $(1) | $$$$(dir $$$$@)
|
||||
$(V)$(LD) $(LDFLAGS) -T tools/user.ld -o $$@ $$(UOBJS) $(1)
|
||||
@$(OBJDUMP) -S $$@ > $$(call cgtype,$$<,o,asm)
|
||||
@$(OBJDUMP) -t $$@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$$$/d' > $$(call cgtype,$$<,o,sym)
|
||||
endef
|
||||
|
||||
$(foreach p,$(call read_packet,uprog),$(eval $(call uprog_ld,$(p))))
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# kernel
|
||||
|
||||
KINCLUDE += kern/debug/ \
|
||||
kern/driver/ \
|
||||
kern/trap/ \
|
||||
kern/mm/ \
|
||||
kern/libs/ \
|
||||
kern/sync/ \
|
||||
kern/fs/ \
|
||||
kern/process \
|
||||
kern/schedule \
|
||||
kern/syscall
|
||||
|
||||
KSRCDIR += kern/init \
|
||||
kern/libs \
|
||||
kern/debug \
|
||||
kern/driver \
|
||||
kern/trap \
|
||||
kern/mm \
|
||||
kern/sync \
|
||||
kern/fs \
|
||||
kern/process \
|
||||
kern/schedule \
|
||||
kern/syscall
|
||||
|
||||
KCFLAGS += $(addprefix -I,$(KINCLUDE))
|
||||
|
||||
$(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS))
|
||||
|
||||
KOBJS = $(call read_packet,kernel libs)
|
||||
|
||||
# create kernel target
|
||||
kernel = $(call totarget,kernel)
|
||||
|
||||
$(kernel): tools/kernel.ld
|
||||
|
||||
$(kernel): $(KOBJS) $(USER_BINS)
|
||||
@echo + ld $@
|
||||
$(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) -b binary $(USER_BINS)
|
||||
@$(OBJDUMP) -S $@ > $(call asmfile,kernel)
|
||||
@$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel)
|
||||
|
||||
$(call create_target,kernel)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# create bootblock
|
||||
bootfiles = $(call listf_cc,boot)
|
||||
$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc))
|
||||
|
||||
bootblock = $(call totarget,bootblock)
|
||||
|
||||
$(bootblock): $(call toobj,boot/bootasm.S) $(call toobj,$(bootfiles)) | $(call totarget,sign)
|
||||
@echo + ld $@
|
||||
$(V)$(LD) $(LDFLAGS) -N -T tools/boot.ld $^ -o $(call toobj,bootblock)
|
||||
@$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock)
|
||||
@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock)
|
||||
@$(call totarget,sign) $(call outfile,bootblock) $(bootblock)
|
||||
|
||||
$(call create_target,bootblock)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# create 'sign' tools
|
||||
$(call add_files_host,tools/sign.c,sign,sign)
|
||||
$(call create_target_host,sign,sign)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# create ucore.img
|
||||
UCOREIMG := $(call totarget,ucore.img)
|
||||
|
||||
$(UCOREIMG): $(kernel) $(bootblock)
|
||||
$(V)dd if=/dev/zero of=$@ count=10000
|
||||
$(V)dd if=$(bootblock) of=$@ conv=notrunc
|
||||
$(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc
|
||||
|
||||
$(call create_target,ucore.img)
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# create swap.img
|
||||
SWAPIMG := $(call totarget,swap.img)
|
||||
|
||||
$(SWAPIMG):
|
||||
$(V)dd if=/dev/zero of=$@ bs=1M count=128
|
||||
|
||||
$(call create_target,swap.img)
|
||||
|
||||
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
$(call finish_all)
|
||||
|
||||
IGNORE_ALLDEPS = clean \
|
||||
dist-clean \
|
||||
grade \
|
||||
touch \
|
||||
print-.+ \
|
||||
run-.+ \
|
||||
build-.+ \
|
||||
handin
|
||||
|
||||
ifeq ($(call match,$(MAKECMDGOALS),$(IGNORE_ALLDEPS)),0)
|
||||
-include $(ALLDEPS)
|
||||
endif
|
||||
|
||||
# files for grade script
|
||||
|
||||
TARGETS: $(TARGETS)
|
||||
|
||||
.DEFAULT_GOAL := TARGETS
|
||||
|
||||
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
|
||||
|
||||
.PHONY: qemu qemu-nox debug debug-nox
|
||||
qemu-mon: $(UCOREIMG) $(SWAPIMG)
|
||||
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
|
||||
qemu: $(UCOREIMG) $(SWAPIMG)
|
||||
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null
|
||||
|
||||
qemu-nox: $(UCOREIMG) $(SWAPIMG)
|
||||
$(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic
|
||||
|
||||
TERMINAL := gnome-terminal
|
||||
|
||||
debug: $(UCOREIMG) $(SWAPIMG)
|
||||
$(V)$(QEMU) -S -s -parallel stdio $(QEMUOPTS) -serial null &
|
||||
$(V)sleep 2
|
||||
$(V)$(TERMINAL) -e "$(GDB) -q -x tools/gdbinit"
|
||||
|
||||
debug-nox: $(UCOREIMG) $(SWAPIMG)
|
||||
$(V)$(QEMU) -S -s -serial mon:stdio $(QEMUOPTS) -nographic &
|
||||
$(V)sleep 2
|
||||
$(V)$(TERMINAL) -e "$(GDB) -q -x tools/gdbinit"
|
||||
|
||||
RUN_PREFIX := _binary_$(OBJDIR)_$(USER_PREFIX)
|
||||
MAKEOPTS := --quiet --no-print-directory
|
||||
|
||||
run-%: build-%
|
||||
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null
|
||||
|
||||
run-nox-%: build-%
|
||||
$(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic
|
||||
|
||||
build-%: touch
|
||||
$(V)$(MAKE) $(MAKEOPTS) "DEFS+=-DTEST=$* -DTESTSTART=$(RUN_PREFIX)$*_out_start -DTESTSIZE=$(RUN_PREFIX)$*_out_size"
|
||||
|
||||
.PHONY: grade touch
|
||||
|
||||
GRADE_GDB_IN := .gdb.in
|
||||
GRADE_QEMU_OUT := .qemu.out
|
||||
HANDIN := proj$(PROJ)-handin.tar.gz
|
||||
|
||||
TOUCH_FILES := kern/process/proc.c
|
||||
|
||||
MAKEOPTS := --quiet --no-print-directory
|
||||
|
||||
grade:
|
||||
$(V)$(MAKE) $(MAKEOPTS) clean
|
||||
$(V)$(SH) tools/grade.sh
|
||||
|
||||
touch:
|
||||
$(V)$(foreach f,$(TOUCH_FILES),$(TOUCH) $(f))
|
||||
|
||||
print-%:
|
||||
@echo $($(shell echo $(patsubst print-%,%,$@) | $(TR) [a-z] [A-Z]))
|
||||
|
||||
.PHONY: clean dist-clean handin packall
|
||||
clean:
|
||||
$(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT)
|
||||
-$(RM) -r $(OBJDIR) $(BINDIR)
|
||||
|
||||
dist-clean: clean
|
||||
-$(RM) $(HANDIN)
|
||||
|
||||
handin: packall
|
||||
@echo Please visit http://learn.tsinghua.edu.cn and upload $(HANDIN). Thanks!
|
||||
|
||||
packall: clean
|
||||
@$(RM) -f $(HANDIN)
|
||||
@tar -czf $(HANDIN) `find . -type f -o -type d | grep -v '^\.*$$' | grep -vF '$(HANDIN)'`
|
||||
|
26
related_info/lab5/lab5-spoc-discuss/boot/asm.h
Normal file
26
related_info/lab5/lab5-spoc-discuss/boot/asm.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef __BOOT_ASM_H__
|
||||
#define __BOOT_ASM_H__
|
||||
|
||||
/* Assembler macros to create x86 segments */
|
||||
|
||||
/* Normal segment */
|
||||
#define SEG_NULLASM \
|
||||
.word 0, 0; \
|
||||
.byte 0, 0, 0, 0
|
||||
|
||||
#define SEG_ASM(type,base,lim) \
|
||||
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
||||
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
|
||||
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
||||
|
||||
|
||||
/* Application segment type bits */
|
||||
#define STA_X 0x8 // Executable segment
|
||||
#define STA_E 0x4 // Expand down (non-executable segments)
|
||||
#define STA_C 0x4 // Conforming code segment (executable only)
|
||||
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||
#define STA_R 0x2 // Readable (executable segments)
|
||||
#define STA_A 0x1 // Accessed
|
||||
|
||||
#endif /* !__BOOT_ASM_H__ */
|
||||
|
107
related_info/lab5/lab5-spoc-discuss/boot/bootasm.S
Normal file
107
related_info/lab5/lab5-spoc-discuss/boot/bootasm.S
Normal file
@ -0,0 +1,107 @@
|
||||
#include <asm.h>
|
||||
|
||||
# Start the CPU: switch to 32-bit protected mode, jump into C.
|
||||
# The BIOS loads this code from the first sector of the hard disk into
|
||||
# memory at physical address 0x7c00 and starts executing in real mode
|
||||
# with %cs=0 %ip=7c00.
|
||||
|
||||
.set PROT_MODE_CSEG, 0x8 # kernel code segment selector
|
||||
.set PROT_MODE_DSEG, 0x10 # kernel data segment selector
|
||||
.set CR0_PE_ON, 0x1 # protected mode enable flag
|
||||
.set SMAP, 0x534d4150
|
||||
|
||||
# start address should be 0:7c00, in real mode, the beginning address of the running bootloader
|
||||
.globl start
|
||||
start:
|
||||
.code16 # Assemble for 16-bit mode
|
||||
cli # Disable interrupts
|
||||
cld # String operations increment
|
||||
|
||||
# Set up the important data segment registers (DS, ES, SS).
|
||||
xorw %ax, %ax # Segment number zero
|
||||
movw %ax, %ds # -> Data Segment
|
||||
movw %ax, %es # -> Extra Segment
|
||||
movw %ax, %ss # -> Stack Segment
|
||||
|
||||
# Enable A20:
|
||||
# For backwards compatibility with the earliest PCs, physical
|
||||
# address line 20 is tied low, so that addresses higher than
|
||||
# 1MB wrap around to zero by default. This code undoes this.
|
||||
seta20.1:
|
||||
inb $0x64, %al # Wait for not busy
|
||||
testb $0x2, %al
|
||||
jnz seta20.1
|
||||
|
||||
movb $0xd1, %al # 0xd1 -> port 0x64
|
||||
outb %al, $0x64
|
||||
|
||||
seta20.2:
|
||||
inb $0x64, %al # Wait for not busy
|
||||
testb $0x2, %al
|
||||
jnz seta20.2
|
||||
|
||||
movb $0xdf, %al # 0xdf -> port 0x60
|
||||
outb %al, $0x60
|
||||
|
||||
probe_memory:
|
||||
movl $0, 0x8000
|
||||
xorl %ebx, %ebx
|
||||
movw $0x8004, %di
|
||||
start_probe:
|
||||
movl $0xE820, %eax
|
||||
movl $20, %ecx
|
||||
movl $SMAP, %edx
|
||||
int $0x15
|
||||
jnc cont
|
||||
movw $12345, 0x8000
|
||||
jmp finish_probe
|
||||
cont:
|
||||
addw $20, %di
|
||||
incl 0x8000
|
||||
cmpl $0, %ebx
|
||||
jnz start_probe
|
||||
finish_probe:
|
||||
|
||||
# Switch from real to protected mode, using a bootstrap GDT
|
||||
# and segment translation that makes virtual addresses
|
||||
# identical to physical addresses, so that the
|
||||
# effective memory map does not change during the switch.
|
||||
lgdt gdtdesc
|
||||
movl %cr0, %eax
|
||||
orl $CR0_PE_ON, %eax
|
||||
movl %eax, %cr0
|
||||
|
||||
# Jump to next instruction, but in 32-bit code segment.
|
||||
# Switches processor into 32-bit mode.
|
||||
ljmp $PROT_MODE_CSEG, $protcseg
|
||||
|
||||
.code32 # Assemble for 32-bit mode
|
||||
protcseg:
|
||||
# Set up the protected-mode data segment registers
|
||||
movw $PROT_MODE_DSEG, %ax # Our data segment selector
|
||||
movw %ax, %ds # -> DS: Data Segment
|
||||
movw %ax, %es # -> ES: Extra Segment
|
||||
movw %ax, %fs # -> FS
|
||||
movw %ax, %gs # -> GS
|
||||
movw %ax, %ss # -> SS: Stack Segment
|
||||
|
||||
# Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)
|
||||
movl $0x0, %ebp
|
||||
movl $start, %esp
|
||||
call bootmain
|
||||
|
||||
# If bootmain returns (it shouldn't), loop.
|
||||
spin:
|
||||
jmp spin
|
||||
|
||||
.data
|
||||
# Bootstrap GDT
|
||||
.p2align 2 # force 4 byte alignment
|
||||
gdt:
|
||||
SEG_NULLASM # null seg
|
||||
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel
|
||||
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel
|
||||
|
||||
gdtdesc:
|
||||
.word 0x17 # sizeof(gdt) - 1
|
||||
.long gdt # address gdt
|
116
related_info/lab5/lab5-spoc-discuss/boot/bootmain.c
Normal file
116
related_info/lab5/lab5-spoc-discuss/boot/bootmain.c
Normal file
@ -0,0 +1,116 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <elf.h>
|
||||
|
||||
/* *********************************************************************
|
||||
* This a dirt simple boot loader, whose sole job is to boot
|
||||
* an ELF kernel image from the first IDE hard disk.
|
||||
*
|
||||
* DISK LAYOUT
|
||||
* * This program(bootasm.S and bootmain.c) is the bootloader.
|
||||
* It should be stored in the first sector of the disk.
|
||||
*
|
||||
* * The 2nd sector onward holds the kernel image.
|
||||
*
|
||||
* * The kernel image must be in ELF format.
|
||||
*
|
||||
* BOOT UP STEPS
|
||||
* * when the CPU boots it loads the BIOS into memory and executes it
|
||||
*
|
||||
* * the BIOS intializes devices, sets of the interrupt routines, and
|
||||
* reads the first sector of the boot device(e.g., hard-drive)
|
||||
* into memory and jumps to it.
|
||||
*
|
||||
* * Assuming this boot loader is stored in the first sector of the
|
||||
* hard-drive, this code takes over...
|
||||
*
|
||||
* * control starts in bootasm.S -- which sets up protected mode,
|
||||
* and a stack so C code then run, then calls bootmain()
|
||||
*
|
||||
* * bootmain() in this file takes over, reads in the kernel and jumps to it.
|
||||
* */
|
||||
|
||||
#define SECTSIZE 512
|
||||
#define ELFHDR ((struct elfhdr *)0x10000) // scratch space
|
||||
|
||||
/* waitdisk - wait for disk ready */
|
||||
static void
|
||||
waitdisk(void) {
|
||||
while ((inb(0x1F7) & 0xC0) != 0x40)
|
||||
/* do nothing */;
|
||||
}
|
||||
|
||||
/* readsect - read a single sector at @secno into @dst */
|
||||
static void
|
||||
readsect(void *dst, uint32_t secno) {
|
||||
// wait for disk to be ready
|
||||
waitdisk();
|
||||
|
||||
outb(0x1F2, 1); // count = 1
|
||||
outb(0x1F3, secno & 0xFF);
|
||||
outb(0x1F4, (secno >> 8) & 0xFF);
|
||||
outb(0x1F5, (secno >> 16) & 0xFF);
|
||||
outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);
|
||||
outb(0x1F7, 0x20); // cmd 0x20 - read sectors
|
||||
|
||||
// wait for disk to be ready
|
||||
waitdisk();
|
||||
|
||||
// read a sector
|
||||
insl(0x1F0, dst, SECTSIZE / 4);
|
||||
}
|
||||
|
||||
/* *
|
||||
* readseg - read @count bytes at @offset from kernel into virtual address @va,
|
||||
* might copy more than asked.
|
||||
* */
|
||||
static void
|
||||
readseg(uintptr_t va, uint32_t count, uint32_t offset) {
|
||||
uintptr_t end_va = va + count;
|
||||
|
||||
// round down to sector boundary
|
||||
va -= offset % SECTSIZE;
|
||||
|
||||
// translate from bytes to sectors; kernel starts at sector 1
|
||||
uint32_t secno = (offset / SECTSIZE) + 1;
|
||||
|
||||
// If this is too slow, we could read lots of sectors at a time.
|
||||
// We'd write more to memory than asked, but it doesn't matter --
|
||||
// we load in increasing order.
|
||||
for (; va < end_va; va += SECTSIZE, secno ++) {
|
||||
readsect((void *)va, secno);
|
||||
}
|
||||
}
|
||||
|
||||
/* bootmain - the entry of bootloader */
|
||||
void
|
||||
bootmain(void) {
|
||||
// read the 1st page off disk
|
||||
readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0);
|
||||
|
||||
// is this a valid ELF?
|
||||
if (ELFHDR->e_magic != ELF_MAGIC) {
|
||||
goto bad;
|
||||
}
|
||||
|
||||
struct proghdr *ph, *eph;
|
||||
|
||||
// load each program segment (ignores ph flags)
|
||||
ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff);
|
||||
eph = ph + ELFHDR->e_phnum;
|
||||
for (; ph < eph; ph ++) {
|
||||
readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
|
||||
}
|
||||
|
||||
// call the entry point from the ELF header
|
||||
// note: does not return
|
||||
((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();
|
||||
|
||||
bad:
|
||||
outw(0x8A00, 0x8A00);
|
||||
outw(0x8A00, 0x8E00);
|
||||
|
||||
/* do nothing */
|
||||
while (1);
|
||||
}
|
||||
|
27
related_info/lab5/lab5-spoc-discuss/kern/debug/assert.h
Normal file
27
related_info/lab5/lab5-spoc-discuss/kern/debug/assert.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __KERN_DEBUG_ASSERT_H__
|
||||
#define __KERN_DEBUG_ASSERT_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
void __warn(const char *file, int line, const char *fmt, ...);
|
||||
void __noreturn __panic(const char *file, int line, const char *fmt, ...);
|
||||
|
||||
#define warn(...) \
|
||||
__warn(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define panic(...) \
|
||||
__panic(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define assert(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
panic("assertion failed: %s", #x); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// static_assert(x) will generate a compile-time error if 'x' is false.
|
||||
#define static_assert(x) \
|
||||
switch (x) { case 0: case (x): ; }
|
||||
|
||||
#endif /* !__KERN_DEBUG_ASSERT_H__ */
|
||||
|
365
related_info/lab5/lab5-spoc-discuss/kern/debug/kdebug.c
Normal file
365
related_info/lab5/lab5-spoc-discuss/kern/debug/kdebug.c
Normal file
@ -0,0 +1,365 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <stab.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <memlayout.h>
|
||||
#include <sync.h>
|
||||
#include <vmm.h>
|
||||
#include <proc.h>
|
||||
#include <kdebug.h>
|
||||
#include <kmonitor.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define STACKFRAME_DEPTH 20
|
||||
|
||||
extern const struct stab __STAB_BEGIN__[]; // beginning of stabs table
|
||||
extern const struct stab __STAB_END__[]; // end of stabs table
|
||||
extern const char __STABSTR_BEGIN__[]; // beginning of string table
|
||||
extern const char __STABSTR_END__[]; // end of string table
|
||||
|
||||
/* debug information about a particular instruction pointer */
|
||||
struct eipdebuginfo {
|
||||
const char *eip_file; // source code filename for eip
|
||||
int eip_line; // source code line number for eip
|
||||
const char *eip_fn_name; // name of function containing eip
|
||||
int eip_fn_namelen; // length of function's name
|
||||
uintptr_t eip_fn_addr; // start address of function
|
||||
int eip_fn_narg; // number of function arguments
|
||||
};
|
||||
|
||||
/* user STABS data structure */
|
||||
struct userstabdata {
|
||||
const struct stab *stabs;
|
||||
const struct stab *stab_end;
|
||||
const char *stabstr;
|
||||
const char *stabstr_end;
|
||||
};
|
||||
|
||||
/* *
|
||||
* stab_binsearch - according to the input, the initial value of
|
||||
* range [*@region_left, *@region_right], find a single stab entry
|
||||
* that includes the address @addr and matches the type @type,
|
||||
* and then save its boundary to the locations that pointed
|
||||
* by @region_left and @region_right.
|
||||
*
|
||||
* Some stab types are arranged in increasing order by instruction address.
|
||||
* For example, N_FUN stabs (stab entries with n_type == N_FUN), which
|
||||
* mark functions, and N_SO stabs, which mark source files.
|
||||
*
|
||||
* Given an instruction address, this function finds the single stab entry
|
||||
* of type @type that contains that address.
|
||||
*
|
||||
* The search takes place within the range [*@region_left, *@region_right].
|
||||
* Thus, to search an entire set of N stabs, you might do:
|
||||
*
|
||||
* left = 0;
|
||||
* right = N - 1; (rightmost stab)
|
||||
* stab_binsearch(stabs, &left, &right, type, addr);
|
||||
*
|
||||
* The search modifies *region_left and *region_right to bracket the @addr.
|
||||
* *@region_left points to the matching stab that contains @addr,
|
||||
* and *@region_right points just before the next stab.
|
||||
* If *@region_left > *region_right, then @addr is not contained in any
|
||||
* matching stab.
|
||||
*
|
||||
* For example, given these N_SO stabs:
|
||||
* Index Type Address
|
||||
* 0 SO f0100000
|
||||
* 13 SO f0100040
|
||||
* 117 SO f0100176
|
||||
* 118 SO f0100178
|
||||
* 555 SO f0100652
|
||||
* 556 SO f0100654
|
||||
* 657 SO f0100849
|
||||
* this code:
|
||||
* left = 0, right = 657;
|
||||
* stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184);
|
||||
* will exit setting left = 118, right = 554.
|
||||
* */
|
||||
static void
|
||||
stab_binsearch(const struct stab *stabs, int *region_left, int *region_right,
|
||||
int type, uintptr_t addr) {
|
||||
int l = *region_left, r = *region_right, any_matches = 0;
|
||||
|
||||
while (l <= r) {
|
||||
int true_m = (l + r) / 2, m = true_m;
|
||||
|
||||
// search for earliest stab with right type
|
||||
while (m >= l && stabs[m].n_type != type) {
|
||||
m --;
|
||||
}
|
||||
if (m < l) { // no match in [l, m]
|
||||
l = true_m + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// actual binary search
|
||||
any_matches = 1;
|
||||
if (stabs[m].n_value < addr) {
|
||||
*region_left = m;
|
||||
l = true_m + 1;
|
||||
} else if (stabs[m].n_value > addr) {
|
||||
*region_right = m - 1;
|
||||
r = m - 1;
|
||||
} else {
|
||||
// exact match for 'addr', but continue loop to find
|
||||
// *region_right
|
||||
*region_left = m;
|
||||
l = m;
|
||||
addr ++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_matches) {
|
||||
*region_right = *region_left - 1;
|
||||
}
|
||||
else {
|
||||
// find rightmost region containing 'addr'
|
||||
l = *region_right;
|
||||
for (; l > *region_left && stabs[l].n_type != type; l --)
|
||||
/* do nothing */;
|
||||
*region_left = l;
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* debuginfo_eip - Fill in the @info structure with information about
|
||||
* the specified instruction address, @addr. Returns 0 if information
|
||||
* was found, and negative if not. But even if it returns negative it
|
||||
* has stored some information into '*info'.
|
||||
* */
|
||||
int
|
||||
debuginfo_eip(uintptr_t addr, struct eipdebuginfo *info) {
|
||||
const struct stab *stabs, *stab_end;
|
||||
const char *stabstr, *stabstr_end;
|
||||
|
||||
info->eip_file = "<unknown>";
|
||||
info->eip_line = 0;
|
||||
info->eip_fn_name = "<unknown>";
|
||||
info->eip_fn_namelen = 9;
|
||||
info->eip_fn_addr = addr;
|
||||
info->eip_fn_narg = 0;
|
||||
|
||||
// find the relevant set of stabs
|
||||
if (addr >= KERNBASE) {
|
||||
stabs = __STAB_BEGIN__;
|
||||
stab_end = __STAB_END__;
|
||||
stabstr = __STABSTR_BEGIN__;
|
||||
stabstr_end = __STABSTR_END__;
|
||||
}
|
||||
else {
|
||||
// user-program linker script, tools/user.ld puts the information about the
|
||||
// program's stabs (included __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__,
|
||||
// and __STABSTR_END__) in a structure located at virtual address USTAB.
|
||||
const struct userstabdata *usd = (struct userstabdata *)USTAB;
|
||||
|
||||
// make sure that debugger (current process) can access this memory
|
||||
struct mm_struct *mm;
|
||||
if (current == NULL || (mm = current->mm) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (!user_mem_check(mm, (uintptr_t)usd, sizeof(struct userstabdata), 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
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
|
||||
if (!user_mem_check(mm, (uintptr_t)stabs, (uintptr_t)stab_end - (uintptr_t)stabs, 0)) {
|
||||
return -1;
|
||||
}
|
||||
if (!user_mem_check(mm, (uintptr_t)stabstr, stabstr_end - stabstr, 0)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// String table validity checks
|
||||
if (stabstr_end <= stabstr || stabstr_end[-1] != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Now we find the right stabs that define the function containing
|
||||
// 'eip'. First, we find the basic source file containing 'eip'.
|
||||
// Then, we look in that source file for the function. Then we look
|
||||
// for the line number.
|
||||
|
||||
// Search the entire set of stabs for the source file (type N_SO).
|
||||
int lfile = 0, rfile = (stab_end - stabs) - 1;
|
||||
stab_binsearch(stabs, &lfile, &rfile, N_SO, addr);
|
||||
if (lfile == 0)
|
||||
return -1;
|
||||
|
||||
// Search within that file's stabs for the function definition
|
||||
// (N_FUN).
|
||||
int lfun = lfile, rfun = rfile;
|
||||
int lline, rline;
|
||||
stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr);
|
||||
|
||||
if (lfun <= rfun) {
|
||||
// stabs[lfun] points to the function name
|
||||
// in the string table, but check bounds just in case.
|
||||
if (stabs[lfun].n_strx < stabstr_end - stabstr) {
|
||||
info->eip_fn_name = stabstr + stabs[lfun].n_strx;
|
||||
}
|
||||
info->eip_fn_addr = stabs[lfun].n_value;
|
||||
addr -= info->eip_fn_addr;
|
||||
// Search within the function definition for the line number.
|
||||
lline = lfun;
|
||||
rline = rfun;
|
||||
} else {
|
||||
// Couldn't find function stab! Maybe we're in an assembly
|
||||
// file. Search the whole file for the line number.
|
||||
info->eip_fn_addr = addr;
|
||||
lline = lfile;
|
||||
rline = rfile;
|
||||
}
|
||||
info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name;
|
||||
|
||||
// Search within [lline, rline] for the line number stab.
|
||||
// If found, set info->eip_line to the right line number.
|
||||
// If not found, return -1.
|
||||
stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
|
||||
if (lline <= rline) {
|
||||
info->eip_line = stabs[rline].n_desc;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Search backwards from the line number for the relevant filename stab.
|
||||
// We can't just use the "lfile" stab because inlined functions
|
||||
// can interpolate code from a different file!
|
||||
// Such included source files use the N_SOL stab type.
|
||||
while (lline >= lfile
|
||||
&& stabs[lline].n_type != N_SOL
|
||||
&& (stabs[lline].n_type != N_SO || !stabs[lline].n_value)) {
|
||||
lline --;
|
||||
}
|
||||
if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr) {
|
||||
info->eip_file = stabstr + stabs[lline].n_strx;
|
||||
}
|
||||
|
||||
// Set eip_fn_narg to the number of arguments taken by the function,
|
||||
// or 0 if there was no containing function.
|
||||
if (lfun < rfun) {
|
||||
for (lline = lfun + 1;
|
||||
lline < rfun && stabs[lline].n_type == N_PSYM;
|
||||
lline ++) {
|
||||
info->eip_fn_narg ++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *
|
||||
* print_kerninfo - print the information about kernel, including the location
|
||||
* of kernel entry, the start addresses of data and text segements, the start
|
||||
* address of free memory and how many memory that kernel has used.
|
||||
* */
|
||||
void
|
||||
print_kerninfo(void) {
|
||||
extern char etext[], edata[], end[], kern_init[];
|
||||
cprintf("Special kernel symbols:\n");
|
||||
cprintf(" entry 0x%08x (phys)\n", kern_init);
|
||||
cprintf(" etext 0x%08x (phys)\n", etext);
|
||||
cprintf(" edata 0x%08x (phys)\n", edata);
|
||||
cprintf(" end 0x%08x (phys)\n", end);
|
||||
cprintf("Kernel executable memory footprint: %dKB\n", (end - kern_init + 1023)/1024);
|
||||
}
|
||||
|
||||
/* *
|
||||
* print_debuginfo - read and print the stat information for the address @eip,
|
||||
* and info.eip_fn_addr should be the first address of the related function.
|
||||
* */
|
||||
void
|
||||
print_debuginfo(uintptr_t eip) {
|
||||
struct eipdebuginfo info;
|
||||
if (debuginfo_eip(eip, &info) != 0) {
|
||||
cprintf(" <unknow>: -- 0x%08x --\n", eip);
|
||||
}
|
||||
else {
|
||||
char fnname[256];
|
||||
int j;
|
||||
for (j = 0; j < info.eip_fn_namelen; j ++) {
|
||||
fnname[j] = info.eip_fn_name[j];
|
||||
}
|
||||
fnname[j] = '\0';
|
||||
cprintf(" %s:%d: %s+%d\n", info.eip_file, info.eip_line,
|
||||
fnname, eip - info.eip_fn_addr);
|
||||
}
|
||||
}
|
||||
|
||||
static __noinline uint32_t
|
||||
read_eip(void) {
|
||||
uint32_t eip;
|
||||
asm volatile("movl 4(%%ebp), %0" : "=r" (eip));
|
||||
return eip;
|
||||
}
|
||||
|
||||
/* *
|
||||
* print_stackframe - print a list of the saved eip values from the nested 'call'
|
||||
* instructions that led to the current point of execution
|
||||
*
|
||||
* The x86 stack pointer, namely esp, points to the lowest location on the stack
|
||||
* that is currently in use. Everything below that location in stack is free. Pushing
|
||||
* a value onto the stack will invole decreasing the stack pointer and then writing
|
||||
* the value to the place that stack pointer pointes to. And popping a value do the
|
||||
* opposite.
|
||||
*
|
||||
* The ebp (base pointer) register, in contrast, is associated with the stack
|
||||
* primarily by software convention. On entry to a C function, the function's
|
||||
* prologue code normally saves the previous function's base pointer by pushing
|
||||
* it onto the stack, and then copies the current esp value into ebp for the duration
|
||||
* of the function. If all the functions in a program obey this convention,
|
||||
* then at any given point during the program's execution, it is possible to trace
|
||||
* back through the stack by following the chain of saved ebp pointers and determining
|
||||
* exactly what nested sequence of function calls caused this particular point in the
|
||||
* program to be reached. This capability can be particularly useful, for example,
|
||||
* when a particular function causes an assert failure or panic because bad arguments
|
||||
* were passed to it, but you aren't sure who passed the bad arguments. A stack
|
||||
* backtrace lets you find the offending function.
|
||||
*
|
||||
* The inline function read_ebp() can tell us the value of current ebp. And the
|
||||
* non-inline function read_eip() is useful, it can read the value of current eip,
|
||||
* since while calling this function, read_eip() can read the caller's eip from
|
||||
* stack easily.
|
||||
*
|
||||
* In print_debuginfo(), the function debuginfo_eip() can get enough information about
|
||||
* calling-chain. Finally print_stackframe() will trace and print them for debugging.
|
||||
*
|
||||
* Note that, the length of ebp-chain is limited. In boot/bootasm.S, before jumping
|
||||
* to the kernel entry, the value of ebp has been set to zero, that's the boundary.
|
||||
* */
|
||||
void
|
||||
print_stackframe(void) {
|
||||
/* LAB1 YOUR CODE : STEP 1 */
|
||||
/* (1) call read_ebp() to get the value of ebp. the type is (uint32_t);
|
||||
* (2) call read_eip() to get the value of eip. the type is (uint32_t);
|
||||
* (3) from 0 .. STACKFRAME_DEPTH
|
||||
* (3.1) printf value of ebp, eip
|
||||
* (3.2) (uint32_t)calling arguments [0..4] = the contents in address (unit32_t)ebp +2 [0..4]
|
||||
* (3.3) cprintf("\n");
|
||||
* (3.4) call print_debuginfo(eip-1) to print the C calling function name and line number, etc.
|
||||
* (3.5) popup a calling stackframe
|
||||
* NOTICE: the calling funciton's return addr eip = ss:[ebp+4]
|
||||
* the calling funciton's ebp = ss:[ebp]
|
||||
*/
|
||||
uint32_t ebp = read_ebp(), eip = read_eip();
|
||||
|
||||
int i, j;
|
||||
for (i = 0; ebp != 0 && i < STACKFRAME_DEPTH; i ++) {
|
||||
cprintf("ebp:0x%08x eip:0x%08x args:", ebp, eip);
|
||||
uint32_t *args = (uint32_t *)ebp + 2;
|
||||
for (j = 0; j < 4; j ++) {
|
||||
cprintf("0x%08x ", args[j]);
|
||||
}
|
||||
cprintf("\n");
|
||||
print_debuginfo(eip - 1);
|
||||
eip = ((uint32_t *)ebp)[1];
|
||||
ebp = ((uint32_t *)ebp)[0];
|
||||
}
|
||||
}
|
||||
|
12
related_info/lab5/lab5-spoc-discuss/kern/debug/kdebug.h
Normal file
12
related_info/lab5/lab5-spoc-discuss/kern/debug/kdebug.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __KERN_DEBUG_KDEBUG_H__
|
||||
#define __KERN_DEBUG_KDEBUG_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <trap.h>
|
||||
|
||||
void print_kerninfo(void);
|
||||
void print_stackframe(void);
|
||||
void print_debuginfo(uintptr_t eip);
|
||||
|
||||
#endif /* !__KERN_DEBUG_KDEBUG_H__ */
|
||||
|
132
related_info/lab5/lab5-spoc-discuss/kern/debug/kmonitor.c
Normal file
132
related_info/lab5/lab5-spoc-discuss/kern/debug/kmonitor.c
Normal file
@ -0,0 +1,132 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <mmu.h>
|
||||
#include <trap.h>
|
||||
#include <kmonitor.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
/* *
|
||||
* Simple command-line kernel monitor useful for controlling the
|
||||
* kernel and exploring the system interactively.
|
||||
* */
|
||||
|
||||
struct command {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
// return -1 to force monitor to exit
|
||||
int(*func)(int argc, char **argv, struct trapframe *tf);
|
||||
};
|
||||
|
||||
static struct command commands[] = {
|
||||
{"help", "Display this list of commands.", mon_help},
|
||||
{"kerninfo", "Display information about the kernel.", mon_kerninfo},
|
||||
{"backtrace", "Print backtrace of stack frame.", mon_backtrace},
|
||||
};
|
||||
|
||||
/* return if kernel is panic, in kern/debug/panic.c */
|
||||
bool is_kernel_panic(void);
|
||||
|
||||
#define NCOMMANDS (sizeof(commands)/sizeof(struct command))
|
||||
|
||||
/***** Kernel monitor command interpreter *****/
|
||||
|
||||
#define MAXARGS 16
|
||||
#define WHITESPACE " \t\n\r"
|
||||
|
||||
/* parse - parse the command buffer into whitespace-separated arguments */
|
||||
static int
|
||||
parse(char *buf, char **argv) {
|
||||
int argc = 0;
|
||||
while (1) {
|
||||
// find global whitespace
|
||||
while (*buf != '\0' && strchr(WHITESPACE, *buf) != NULL) {
|
||||
*buf ++ = '\0';
|
||||
}
|
||||
if (*buf == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
// save and scan past next arg
|
||||
if (argc == MAXARGS - 1) {
|
||||
cprintf("Too many arguments (max %d).\n", MAXARGS);
|
||||
}
|
||||
argv[argc ++] = buf;
|
||||
while (*buf != '\0' && strchr(WHITESPACE, *buf) == NULL) {
|
||||
buf ++;
|
||||
}
|
||||
}
|
||||
return argc;
|
||||
}
|
||||
|
||||
/* *
|
||||
* runcmd - parse the input string, split it into separated arguments
|
||||
* and then lookup and invoke some related commands/
|
||||
* */
|
||||
static int
|
||||
runcmd(char *buf, struct trapframe *tf) {
|
||||
char *argv[MAXARGS];
|
||||
int argc = parse(buf, argv);
|
||||
if (argc == 0) {
|
||||
return 0;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < NCOMMANDS; i ++) {
|
||||
if (strcmp(commands[i].name, argv[0]) == 0) {
|
||||
return commands[i].func(argc - 1, argv + 1, tf);
|
||||
}
|
||||
}
|
||||
cprintf("Unknown command '%s'\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***** Implementations of basic kernel monitor commands *****/
|
||||
|
||||
void
|
||||
kmonitor(struct trapframe *tf) {
|
||||
cprintf("Welcome to the kernel debug monitor!!\n");
|
||||
cprintf("Type 'help' for a list of commands.\n");
|
||||
|
||||
if (tf != NULL) {
|
||||
print_trapframe(tf);
|
||||
}
|
||||
|
||||
char *buf;
|
||||
while (1) {
|
||||
if ((buf = readline("K> ")) != NULL) {
|
||||
if (runcmd(buf, tf) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* mon_help - print the information about mon_* functions */
|
||||
int
|
||||
mon_help(int argc, char **argv, struct trapframe *tf) {
|
||||
int i;
|
||||
for (i = 0; i < NCOMMANDS; i ++) {
|
||||
cprintf("%s - %s\n", commands[i].name, commands[i].desc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *
|
||||
* mon_kerninfo - call print_kerninfo in kern/debug/kdebug.c to
|
||||
* print the memory occupancy in kernel.
|
||||
* */
|
||||
int
|
||||
mon_kerninfo(int argc, char **argv, struct trapframe *tf) {
|
||||
print_kerninfo();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* *
|
||||
* mon_backtrace - call print_stackframe in kern/debug/kdebug.c to
|
||||
* print a backtrace of the stack.
|
||||
* */
|
||||
int
|
||||
mon_backtrace(int argc, char **argv, struct trapframe *tf) {
|
||||
print_stackframe();
|
||||
return 0;
|
||||
}
|
||||
|
19
related_info/lab5/lab5-spoc-discuss/kern/debug/kmonitor.h
Normal file
19
related_info/lab5/lab5-spoc-discuss/kern/debug/kmonitor.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __KERN_DEBUG_MONITOR_H__
|
||||
#define __KERN_DEBUG_MONITOR_H__
|
||||
|
||||
#include <trap.h>
|
||||
|
||||
void kmonitor(struct trapframe *tf);
|
||||
|
||||
int mon_help(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_kerninfo(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_backtrace(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_continue(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_step(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_breakpoint(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_watchpoint(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_delete_dr(int argc, char **argv, struct trapframe *tf);
|
||||
int mon_list_dr(int argc, char **argv, struct trapframe *tf);
|
||||
|
||||
#endif /* !__KERN_DEBUG_MONITOR_H__ */
|
||||
|
49
related_info/lab5/lab5-spoc-discuss/kern/debug/panic.c
Normal file
49
related_info/lab5/lab5-spoc-discuss/kern/debug/panic.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <intr.h>
|
||||
#include <kmonitor.h>
|
||||
|
||||
static bool is_panic = 0;
|
||||
|
||||
/* *
|
||||
* __panic - __panic is called on unresolvable fatal errors. it prints
|
||||
* "panic: 'message'", and then enters the kernel monitor.
|
||||
* */
|
||||
void
|
||||
__panic(const char *file, int line, const char *fmt, ...) {
|
||||
if (is_panic) {
|
||||
goto panic_dead;
|
||||
}
|
||||
is_panic = 1;
|
||||
|
||||
// print the 'message'
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel panic at %s:%d:\n ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
|
||||
panic_dead:
|
||||
intr_disable();
|
||||
while (1) {
|
||||
kmonitor(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* __warn - like panic, but don't */
|
||||
void
|
||||
__warn(const char *file, int line, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
cprintf("kernel warning at %s:%d:\n ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
bool
|
||||
is_kernel_panic(void) {
|
||||
return is_panic;
|
||||
}
|
||||
|
54
related_info/lab5/lab5-spoc-discuss/kern/debug/stab.h
Normal file
54
related_info/lab5/lab5-spoc-discuss/kern/debug/stab.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef __KERN_DEBUG_STAB_H__
|
||||
#define __KERN_DEBUG_STAB_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
/* *
|
||||
* STABS debugging info
|
||||
*
|
||||
* The kernel debugger can understand some debugging information in
|
||||
* the STABS format. For more information on this format, see
|
||||
* http://sources.redhat.com/gdb/onlinedocs/stabs_toc.html
|
||||
*
|
||||
* The constants below define some symbol types used by various debuggers
|
||||
* and compilers. Kernel uses the N_SO, N_SOL, N_FUN, and N_SLINE types.
|
||||
* */
|
||||
|
||||
#define N_GSYM 0x20 // global symbol
|
||||
#define N_FNAME 0x22 // F77 function name
|
||||
#define N_FUN 0x24 // procedure name
|
||||
#define N_STSYM 0x26 // data segment variable
|
||||
#define N_LCSYM 0x28 // bss segment variable
|
||||
#define N_MAIN 0x2a // main function name
|
||||
#define N_PC 0x30 // global Pascal symbol
|
||||
#define N_RSYM 0x40 // register variable
|
||||
#define N_SLINE 0x44 // text segment line number
|
||||
#define N_DSLINE 0x46 // data segment line number
|
||||
#define N_BSLINE 0x48 // bss segment line number
|
||||
#define N_SSYM 0x60 // structure/union element
|
||||
#define N_SO 0x64 // main source file name
|
||||
#define N_LSYM 0x80 // stack variable
|
||||
#define N_BINCL 0x82 // include file beginning
|
||||
#define N_SOL 0x84 // included source file name
|
||||
#define N_PSYM 0xa0 // parameter variable
|
||||
#define N_EINCL 0xa2 // include file end
|
||||
#define N_ENTRY 0xa4 // alternate entry point
|
||||
#define N_LBRAC 0xc0 // left bracket
|
||||
#define N_EXCL 0xc2 // deleted include file
|
||||
#define N_RBRAC 0xe0 // right bracket
|
||||
#define N_BCOMM 0xe2 // begin common
|
||||
#define N_ECOMM 0xe4 // end common
|
||||
#define N_ECOML 0xe8 // end common (local name)
|
||||
#define N_LENG 0xfe // length of preceding entry
|
||||
|
||||
/* Entries in the STABS table are formatted as follows. */
|
||||
struct stab {
|
||||
uint32_t n_strx; // index into string table of name
|
||||
uint8_t n_type; // type of symbol
|
||||
uint8_t n_other; // misc info (usually empty)
|
||||
uint16_t n_desc; // description field
|
||||
uintptr_t n_value; // value of symbol
|
||||
};
|
||||
|
||||
#endif /* !__KERN_DEBUG_STAB_H__ */
|
||||
|
45
related_info/lab5/lab5-spoc-discuss/kern/driver/clock.c
Normal file
45
related_info/lab5/lab5-spoc-discuss/kern/driver/clock.c
Normal file
@ -0,0 +1,45 @@
|
||||
#include <x86.h>
|
||||
#include <trap.h>
|
||||
#include <stdio.h>
|
||||
#include <picirq.h>
|
||||
|
||||
/* *
|
||||
* Support for time-related hardware gadgets - the 8253 timer,
|
||||
* which generates interruptes on IRQ-0.
|
||||
* */
|
||||
|
||||
#define IO_TIMER1 0x040 // 8253 Timer #1
|
||||
|
||||
/* *
|
||||
* Frequency of all three count-down timers; (TIMER_FREQ/freq)
|
||||
* is the appropriate count to generate a frequency of freq Hz.
|
||||
* */
|
||||
|
||||
#define TIMER_FREQ 1193182
|
||||
#define TIMER_DIV(x) ((TIMER_FREQ + (x) / 2) / (x))
|
||||
|
||||
#define TIMER_MODE (IO_TIMER1 + 3) // timer mode port
|
||||
#define TIMER_SEL0 0x00 // select counter 0
|
||||
#define TIMER_RATEGEN 0x04 // mode 2, rate generator
|
||||
#define TIMER_16BIT 0x30 // r/w counter 16 bits, LSB first
|
||||
|
||||
volatile size_t ticks;
|
||||
|
||||
/* *
|
||||
* clock_init - initialize 8253 clock to interrupt 100 times per second,
|
||||
* and then enable IRQ_TIMER.
|
||||
* */
|
||||
void
|
||||
clock_init(void) {
|
||||
// set 8253 timer-chip
|
||||
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT);
|
||||
outb(IO_TIMER1, TIMER_DIV(100) % 256);
|
||||
outb(IO_TIMER1, TIMER_DIV(100) / 256);
|
||||
|
||||
// initialize time counter 'ticks' to zero
|
||||
ticks = 0;
|
||||
|
||||
cprintf("++ setup timer interrupts\n");
|
||||
pic_enable(IRQ_TIMER);
|
||||
}
|
||||
|
11
related_info/lab5/lab5-spoc-discuss/kern/driver/clock.h
Normal file
11
related_info/lab5/lab5-spoc-discuss/kern/driver/clock.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __KERN_DRIVER_CLOCK_H__
|
||||
#define __KERN_DRIVER_CLOCK_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
extern volatile size_t ticks;
|
||||
|
||||
void clock_init(void);
|
||||
|
||||
#endif /* !__KERN_DRIVER_CLOCK_H__ */
|
||||
|
465
related_info/lab5/lab5-spoc-discuss/kern/driver/console.c
Normal file
465
related_info/lab5/lab5-spoc-discuss/kern/driver/console.c
Normal file
@ -0,0 +1,465 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <kbdreg.h>
|
||||
#include <picirq.h>
|
||||
#include <trap.h>
|
||||
#include <memlayout.h>
|
||||
#include <sync.h>
|
||||
|
||||
/* stupid I/O delay routine necessitated by historical PC design flaws */
|
||||
static void
|
||||
delay(void) {
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
inb(0x84);
|
||||
}
|
||||
|
||||
/***** Serial I/O code *****/
|
||||
#define COM1 0x3F8
|
||||
|
||||
#define COM_RX 0 // In: Receive buffer (DLAB=0)
|
||||
#define COM_TX 0 // Out: Transmit buffer (DLAB=0)
|
||||
#define COM_DLL 0 // Out: Divisor Latch Low (DLAB=1)
|
||||
#define COM_DLM 1 // Out: Divisor Latch High (DLAB=1)
|
||||
#define COM_IER 1 // Out: Interrupt Enable Register
|
||||
#define COM_IER_RDI 0x01 // Enable receiver data interrupt
|
||||
#define COM_IIR 2 // In: Interrupt ID Register
|
||||
#define COM_FCR 2 // Out: FIFO Control Register
|
||||
#define COM_LCR 3 // Out: Line Control Register
|
||||
#define COM_LCR_DLAB 0x80 // Divisor latch access bit
|
||||
#define COM_LCR_WLEN8 0x03 // Wordlength: 8 bits
|
||||
#define COM_MCR 4 // Out: Modem Control Register
|
||||
#define COM_MCR_RTS 0x02 // RTS complement
|
||||
#define COM_MCR_DTR 0x01 // DTR complement
|
||||
#define COM_MCR_OUT2 0x08 // Out2 complement
|
||||
#define COM_LSR 5 // In: Line Status Register
|
||||
#define COM_LSR_DATA 0x01 // Data available
|
||||
#define COM_LSR_TXRDY 0x20 // Transmit buffer avail
|
||||
#define COM_LSR_TSRE 0x40 // Transmitter off
|
||||
|
||||
#define MONO_BASE 0x3B4
|
||||
#define MONO_BUF 0xB0000
|
||||
#define CGA_BASE 0x3D4
|
||||
#define CGA_BUF 0xB8000
|
||||
#define CRT_ROWS 25
|
||||
#define CRT_COLS 80
|
||||
#define CRT_SIZE (CRT_ROWS * CRT_COLS)
|
||||
|
||||
#define LPTPORT 0x378
|
||||
|
||||
static uint16_t *crt_buf;
|
||||
static uint16_t crt_pos;
|
||||
static uint16_t addr_6845;
|
||||
|
||||
/* TEXT-mode CGA/VGA display output */
|
||||
|
||||
static void
|
||||
cga_init(void) {
|
||||
volatile uint16_t *cp = (uint16_t *)(CGA_BUF + KERNBASE);
|
||||
uint16_t was = *cp;
|
||||
*cp = (uint16_t) 0xA55A;
|
||||
if (*cp != 0xA55A) {
|
||||
cp = (uint16_t*)(MONO_BUF + KERNBASE);
|
||||
addr_6845 = MONO_BASE;
|
||||
} else {
|
||||
*cp = was;
|
||||
addr_6845 = CGA_BASE;
|
||||
}
|
||||
|
||||
// Extract cursor location
|
||||
uint32_t pos;
|
||||
outb(addr_6845, 14);
|
||||
pos = inb(addr_6845 + 1) << 8;
|
||||
outb(addr_6845, 15);
|
||||
pos |= inb(addr_6845 + 1);
|
||||
|
||||
crt_buf = (uint16_t*) cp;
|
||||
crt_pos = pos;
|
||||
}
|
||||
|
||||
static bool serial_exists = 0;
|
||||
|
||||
static void
|
||||
serial_init(void) {
|
||||
// Turn off the FIFO
|
||||
outb(COM1 + COM_FCR, 0);
|
||||
|
||||
// Set speed; requires DLAB latch
|
||||
outb(COM1 + COM_LCR, COM_LCR_DLAB);
|
||||
outb(COM1 + COM_DLL, (uint8_t) (115200 / 9600));
|
||||
outb(COM1 + COM_DLM, 0);
|
||||
|
||||
// 8 data bits, 1 stop bit, parity off; turn off DLAB latch
|
||||
outb(COM1 + COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB);
|
||||
|
||||
// No modem controls
|
||||
outb(COM1 + COM_MCR, 0);
|
||||
// Enable rcv interrupts
|
||||
outb(COM1 + COM_IER, COM_IER_RDI);
|
||||
|
||||
// Clear any preexisting overrun indications and interrupts
|
||||
// Serial port doesn't exist if COM_LSR returns 0xFF
|
||||
serial_exists = (inb(COM1 + COM_LSR) != 0xFF);
|
||||
(void) inb(COM1+COM_IIR);
|
||||
(void) inb(COM1+COM_RX);
|
||||
|
||||
if (serial_exists) {
|
||||
pic_enable(IRQ_COM1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lpt_putc_sub(int c) {
|
||||
int i;
|
||||
for (i = 0; !(inb(LPTPORT + 1) & 0x80) && i < 12800; i ++) {
|
||||
delay();
|
||||
}
|
||||
outb(LPTPORT + 0, c);
|
||||
outb(LPTPORT + 2, 0x08 | 0x04 | 0x01);
|
||||
outb(LPTPORT + 2, 0x08);
|
||||
}
|
||||
|
||||
/* lpt_putc - copy console output to parallel port */
|
||||
static void
|
||||
lpt_putc(int c) {
|
||||
if (c != '\b') {
|
||||
lpt_putc_sub(c);
|
||||
}
|
||||
else {
|
||||
lpt_putc_sub('\b');
|
||||
lpt_putc_sub(' ');
|
||||
lpt_putc_sub('\b');
|
||||
}
|
||||
}
|
||||
|
||||
/* cga_putc - print character to console */
|
||||
static void
|
||||
cga_putc(int c) {
|
||||
// set black on white
|
||||
if (!(c & ~0xFF)) {
|
||||
c |= 0x0700;
|
||||
}
|
||||
|
||||
switch (c & 0xff) {
|
||||
case '\b':
|
||||
if (crt_pos > 0) {
|
||||
crt_pos --;
|
||||
crt_buf[crt_pos] = (c & ~0xff) | ' ';
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
crt_pos += CRT_COLS;
|
||||
case '\r':
|
||||
crt_pos -= (crt_pos % CRT_COLS);
|
||||
break;
|
||||
default:
|
||||
crt_buf[crt_pos ++] = c; // write the character
|
||||
break;
|
||||
}
|
||||
|
||||
// What is the purpose of this?
|
||||
if (crt_pos >= CRT_SIZE) {
|
||||
int i;
|
||||
memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
|
||||
for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i ++) {
|
||||
crt_buf[i] = 0x0700 | ' ';
|
||||
}
|
||||
crt_pos -= CRT_COLS;
|
||||
}
|
||||
|
||||
// move that little blinky thing
|
||||
outb(addr_6845, 14);
|
||||
outb(addr_6845 + 1, crt_pos >> 8);
|
||||
outb(addr_6845, 15);
|
||||
outb(addr_6845 + 1, crt_pos);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_putc_sub(int c) {
|
||||
int i;
|
||||
for (i = 0; !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; i ++) {
|
||||
delay();
|
||||
}
|
||||
outb(COM1 + COM_TX, c);
|
||||
}
|
||||
|
||||
/* serial_putc - print character to serial port */
|
||||
static void
|
||||
serial_putc(int c) {
|
||||
if (c != '\b') {
|
||||
serial_putc_sub(c);
|
||||
}
|
||||
else {
|
||||
serial_putc_sub('\b');
|
||||
serial_putc_sub(' ');
|
||||
serial_putc_sub('\b');
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* Here we manage the console input buffer, where we stash characters
|
||||
* received from the keyboard or serial port whenever the corresponding
|
||||
* interrupt occurs.
|
||||
* */
|
||||
|
||||
#define CONSBUFSIZE 512
|
||||
|
||||
static struct {
|
||||
uint8_t buf[CONSBUFSIZE];
|
||||
uint32_t rpos;
|
||||
uint32_t wpos;
|
||||
} cons;
|
||||
|
||||
/* *
|
||||
* cons_intr - called by device interrupt routines to feed input
|
||||
* characters into the circular console input buffer.
|
||||
* */
|
||||
static void
|
||||
cons_intr(int (*proc)(void)) {
|
||||
int c;
|
||||
while ((c = (*proc)()) != -1) {
|
||||
if (c != 0) {
|
||||
cons.buf[cons.wpos ++] = c;
|
||||
if (cons.wpos == CONSBUFSIZE) {
|
||||
cons.wpos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* serial_proc_data - get data from serial port */
|
||||
static int
|
||||
serial_proc_data(void) {
|
||||
if (!(inb(COM1 + COM_LSR) & COM_LSR_DATA)) {
|
||||
return -1;
|
||||
}
|
||||
int c = inb(COM1 + COM_RX);
|
||||
if (c == 127) {
|
||||
c = '\b';
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* serial_intr - try to feed input characters from serial port */
|
||||
void
|
||||
serial_intr(void) {
|
||||
if (serial_exists) {
|
||||
cons_intr(serial_proc_data);
|
||||
}
|
||||
}
|
||||
|
||||
/***** Keyboard input code *****/
|
||||
|
||||
#define NO 0
|
||||
|
||||
#define SHIFT (1<<0)
|
||||
#define CTL (1<<1)
|
||||
#define ALT (1<<2)
|
||||
|
||||
#define CAPSLOCK (1<<3)
|
||||
#define NUMLOCK (1<<4)
|
||||
#define SCROLLLOCK (1<<5)
|
||||
|
||||
#define E0ESC (1<<6)
|
||||
|
||||
static uint8_t shiftcode[256] = {
|
||||
[0x1D] CTL,
|
||||
[0x2A] SHIFT,
|
||||
[0x36] SHIFT,
|
||||
[0x38] ALT,
|
||||
[0x9D] CTL,
|
||||
[0xB8] ALT
|
||||
};
|
||||
|
||||
static uint8_t togglecode[256] = {
|
||||
[0x3A] CAPSLOCK,
|
||||
[0x45] NUMLOCK,
|
||||
[0x46] SCROLLLOCK
|
||||
};
|
||||
|
||||
static uint8_t normalmap[256] = {
|
||||
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
|
||||
'7', '8', '9', '0', '-', '=', '\b', '\t',
|
||||
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
|
||||
'o', 'p', '[', ']', '\n', NO, 'a', 's',
|
||||
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
|
||||
'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
|
||||
'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
|
||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||||
[0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/,
|
||||
[0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP,
|
||||
[0xC9] KEY_PGUP, [0xCB] KEY_LF,
|
||||
[0xCD] KEY_RT, [0xCF] KEY_END,
|
||||
[0xD0] KEY_DN, [0xD1] KEY_PGDN,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
static uint8_t shiftmap[256] = {
|
||||
NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
|
||||
'&', '*', '(', ')', '_', '+', '\b', '\t',
|
||||
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
|
||||
'O', 'P', '{', '}', '\n', NO, 'A', 'S',
|
||||
'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
|
||||
'"', '~', NO, '|', 'Z', 'X', 'C', 'V',
|
||||
'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
|
||||
NO, ' ', NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
|
||||
'8', '9', '-', '4', '5', '6', '+', '1',
|
||||
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
|
||||
[0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/,
|
||||
[0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP,
|
||||
[0xC9] KEY_PGUP, [0xCB] KEY_LF,
|
||||
[0xCD] KEY_RT, [0xCF] KEY_END,
|
||||
[0xD0] KEY_DN, [0xD1] KEY_PGDN,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
#define C(x) (x - '@')
|
||||
|
||||
static uint8_t ctlmap[256] = {
|
||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
||||
NO, NO, NO, NO, NO, NO, NO, NO,
|
||||
C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
|
||||
C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
|
||||
C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
|
||||
NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
|
||||
C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
|
||||
[0x97] KEY_HOME,
|
||||
[0xB5] C('/'), [0xC8] KEY_UP,
|
||||
[0xC9] KEY_PGUP, [0xCB] KEY_LF,
|
||||
[0xCD] KEY_RT, [0xCF] KEY_END,
|
||||
[0xD0] KEY_DN, [0xD1] KEY_PGDN,
|
||||
[0xD2] KEY_INS, [0xD3] KEY_DEL
|
||||
};
|
||||
|
||||
static uint8_t *charcode[4] = {
|
||||
normalmap,
|
||||
shiftmap,
|
||||
ctlmap,
|
||||
ctlmap
|
||||
};
|
||||
|
||||
/* *
|
||||
* kbd_proc_data - get data from keyboard
|
||||
*
|
||||
* The kbd_proc_data() function gets data from the keyboard.
|
||||
* If we finish a character, return it, else 0. And return -1 if no data.
|
||||
* */
|
||||
static int
|
||||
kbd_proc_data(void) {
|
||||
int c;
|
||||
uint8_t data;
|
||||
static uint32_t shift;
|
||||
|
||||
if ((inb(KBSTATP) & KBS_DIB) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = inb(KBDATAP);
|
||||
|
||||
if (data == 0xE0) {
|
||||
// E0 escape character
|
||||
shift |= E0ESC;
|
||||
return 0;
|
||||
} else if (data & 0x80) {
|
||||
// Key released
|
||||
data = (shift & E0ESC ? data : data & 0x7F);
|
||||
shift &= ~(shiftcode[data] | E0ESC);
|
||||
return 0;
|
||||
} else if (shift & E0ESC) {
|
||||
// Last character was an E0 escape; or with 0x80
|
||||
data |= 0x80;
|
||||
shift &= ~E0ESC;
|
||||
}
|
||||
|
||||
shift |= shiftcode[data];
|
||||
shift ^= togglecode[data];
|
||||
|
||||
c = charcode[shift & (CTL | SHIFT)][data];
|
||||
if (shift & CAPSLOCK) {
|
||||
if ('a' <= c && c <= 'z')
|
||||
c += 'A' - 'a';
|
||||
else if ('A' <= c && c <= 'Z')
|
||||
c += 'a' - 'A';
|
||||
}
|
||||
|
||||
// Process special keys
|
||||
// Ctrl-Alt-Del: reboot
|
||||
if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
|
||||
cprintf("Rebooting!\n");
|
||||
outb(0x92, 0x3); // courtesy of Chris Frost
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* kbd_intr - try to feed input characters from keyboard */
|
||||
static void
|
||||
kbd_intr(void) {
|
||||
cons_intr(kbd_proc_data);
|
||||
}
|
||||
|
||||
static void
|
||||
kbd_init(void) {
|
||||
// drain the kbd buffer
|
||||
kbd_intr();
|
||||
pic_enable(IRQ_KBD);
|
||||
}
|
||||
|
||||
/* cons_init - initializes the console devices */
|
||||
void
|
||||
cons_init(void) {
|
||||
cga_init();
|
||||
serial_init();
|
||||
kbd_init();
|
||||
if (!serial_exists) {
|
||||
cprintf("serial port does not exist!!\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* cons_putc - print a single character @c to console devices */
|
||||
void
|
||||
cons_putc(int c) {
|
||||
bool intr_flag;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
lpt_putc(c);
|
||||
cga_putc(c);
|
||||
serial_putc(c);
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
}
|
||||
|
||||
/* *
|
||||
* cons_getc - return the next input character from console,
|
||||
* or 0 if none waiting.
|
||||
* */
|
||||
int
|
||||
cons_getc(void) {
|
||||
int c = 0;
|
||||
bool intr_flag;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
// poll for any pending input characters,
|
||||
// so that this function works even when interrupts are disabled
|
||||
// (e.g., when called from the kernel monitor).
|
||||
serial_intr();
|
||||
kbd_intr();
|
||||
|
||||
// grab the next character from the input buffer.
|
||||
if (cons.rpos != cons.wpos) {
|
||||
c = cons.buf[cons.rpos ++];
|
||||
if (cons.rpos == CONSBUFSIZE) {
|
||||
cons.rpos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
return c;
|
||||
}
|
||||
|
11
related_info/lab5/lab5-spoc-discuss/kern/driver/console.h
Normal file
11
related_info/lab5/lab5-spoc-discuss/kern/driver/console.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __KERN_DRIVER_CONSOLE_H__
|
||||
#define __KERN_DRIVER_CONSOLE_H__
|
||||
|
||||
void cons_init(void);
|
||||
void cons_putc(int c);
|
||||
int cons_getc(void);
|
||||
void serial_intr(void);
|
||||
void kbd_intr(void);
|
||||
|
||||
#endif /* !__KERN_DRIVER_CONSOLE_H__ */
|
||||
|
214
related_info/lab5/lab5-spoc-discuss/kern/driver/ide.c
Normal file
214
related_info/lab5/lab5-spoc-discuss/kern/driver/ide.c
Normal file
@ -0,0 +1,214 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <trap.h>
|
||||
#include <picirq.h>
|
||||
#include <fs.h>
|
||||
#include <ide.h>
|
||||
#include <x86.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define ISA_DATA 0x00
|
||||
#define ISA_ERROR 0x01
|
||||
#define ISA_PRECOMP 0x01
|
||||
#define ISA_CTRL 0x02
|
||||
#define ISA_SECCNT 0x02
|
||||
#define ISA_SECTOR 0x03
|
||||
#define ISA_CYL_LO 0x04
|
||||
#define ISA_CYL_HI 0x05
|
||||
#define ISA_SDH 0x06
|
||||
#define ISA_COMMAND 0x07
|
||||
#define ISA_STATUS 0x07
|
||||
|
||||
#define IDE_BSY 0x80
|
||||
#define IDE_DRDY 0x40
|
||||
#define IDE_DF 0x20
|
||||
#define IDE_DRQ 0x08
|
||||
#define IDE_ERR 0x01
|
||||
|
||||
#define IDE_CMD_READ 0x20
|
||||
#define IDE_CMD_WRITE 0x30
|
||||
#define IDE_CMD_IDENTIFY 0xEC
|
||||
|
||||
#define IDE_IDENT_SECTORS 20
|
||||
#define IDE_IDENT_MODEL 54
|
||||
#define IDE_IDENT_CAPABILITIES 98
|
||||
#define IDE_IDENT_CMDSETS 164
|
||||
#define IDE_IDENT_MAX_LBA 120
|
||||
#define IDE_IDENT_MAX_LBA_EXT 200
|
||||
|
||||
#define IO_BASE0 0x1F0
|
||||
#define IO_BASE1 0x170
|
||||
#define IO_CTRL0 0x3F4
|
||||
#define IO_CTRL1 0x374
|
||||
|
||||
#define MAX_IDE 4
|
||||
#define MAX_NSECS 128
|
||||
#define MAX_DISK_NSECS 0x10000000U
|
||||
#define VALID_IDE(ideno) (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid))
|
||||
|
||||
static const struct {
|
||||
unsigned short base; // I/O Base
|
||||
unsigned short ctrl; // Control Base
|
||||
} channels[2] = {
|
||||
{IO_BASE0, IO_CTRL0},
|
||||
{IO_BASE1, IO_CTRL1},
|
||||
};
|
||||
|
||||
#define IO_BASE(ideno) (channels[(ideno) >> 1].base)
|
||||
#define IO_CTRL(ideno) (channels[(ideno) >> 1].ctrl)
|
||||
|
||||
static struct ide_device {
|
||||
unsigned char valid; // 0 or 1 (If Device Really Exists)
|
||||
unsigned int sets; // Commend Sets Supported
|
||||
unsigned int size; // Size in Sectors
|
||||
unsigned char model[41]; // Model in String
|
||||
} ide_devices[MAX_IDE];
|
||||
|
||||
static int
|
||||
ide_wait_ready(unsigned short iobase, bool check_error) {
|
||||
int r;
|
||||
while ((r = inb(iobase + ISA_STATUS)) & IDE_BSY)
|
||||
/* nothing */;
|
||||
if (check_error && (r & (IDE_DF | IDE_ERR)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ide_init(void) {
|
||||
static_assert((SECTSIZE % 4) == 0);
|
||||
unsigned short ideno, iobase;
|
||||
for (ideno = 0; ideno < MAX_IDE; ideno ++) {
|
||||
/* assume that no device here */
|
||||
ide_devices[ideno].valid = 0;
|
||||
|
||||
iobase = IO_BASE(ideno);
|
||||
|
||||
/* wait device ready */
|
||||
ide_wait_ready(iobase, 0);
|
||||
|
||||
/* step1: select drive */
|
||||
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4));
|
||||
ide_wait_ready(iobase, 0);
|
||||
|
||||
/* step2: send ATA identify command */
|
||||
outb(iobase + ISA_COMMAND, IDE_CMD_IDENTIFY);
|
||||
ide_wait_ready(iobase, 0);
|
||||
|
||||
/* step3: polling */
|
||||
if (inb(iobase + ISA_STATUS) == 0 || ide_wait_ready(iobase, 1) != 0) {
|
||||
continue ;
|
||||
}
|
||||
|
||||
/* device is ok */
|
||||
ide_devices[ideno].valid = 1;
|
||||
|
||||
/* read identification space of the device */
|
||||
unsigned int buffer[128];
|
||||
insl(iobase + ISA_DATA, buffer, sizeof(buffer) / sizeof(unsigned int));
|
||||
|
||||
unsigned char *ident = (unsigned char *)buffer;
|
||||
unsigned int sectors;
|
||||
unsigned int cmdsets = *(unsigned int *)(ident + IDE_IDENT_CMDSETS);
|
||||
/* device use 48-bits or 28-bits addressing */
|
||||
if (cmdsets & (1 << 26)) {
|
||||
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA_EXT);
|
||||
}
|
||||
else {
|
||||
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA);
|
||||
}
|
||||
ide_devices[ideno].sets = cmdsets;
|
||||
ide_devices[ideno].size = sectors;
|
||||
|
||||
/* check if supports LBA */
|
||||
assert((*(unsigned short *)(ident + IDE_IDENT_CAPABILITIES) & 0x200) != 0);
|
||||
|
||||
unsigned char *model = ide_devices[ideno].model, *data = ident + IDE_IDENT_MODEL;
|
||||
unsigned int i, length = 40;
|
||||
for (i = 0; i < length; i += 2) {
|
||||
model[i] = data[i + 1], model[i + 1] = data[i];
|
||||
}
|
||||
do {
|
||||
model[i] = '\0';
|
||||
} while (i -- > 0 && model[i] == ' ');
|
||||
|
||||
cprintf("ide %d: %10u(sectors), '%s'.\n", ideno, ide_devices[ideno].size, ide_devices[ideno].model);
|
||||
}
|
||||
|
||||
// enable ide interrupt
|
||||
pic_enable(IRQ_IDE1);
|
||||
pic_enable(IRQ_IDE2);
|
||||
}
|
||||
|
||||
bool
|
||||
ide_device_valid(unsigned short ideno) {
|
||||
return VALID_IDE(ideno);
|
||||
}
|
||||
|
||||
size_t
|
||||
ide_device_size(unsigned short ideno) {
|
||||
if (ide_device_valid(ideno)) {
|
||||
return ide_devices[ideno].size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs) {
|
||||
assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
|
||||
assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
|
||||
unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno);
|
||||
|
||||
ide_wait_ready(iobase, 0);
|
||||
|
||||
// generate interrupt
|
||||
outb(ioctrl + ISA_CTRL, 0);
|
||||
outb(iobase + ISA_SECCNT, nsecs);
|
||||
outb(iobase + ISA_SECTOR, secno & 0xFF);
|
||||
outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF);
|
||||
outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF);
|
||||
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF));
|
||||
outb(iobase + ISA_COMMAND, IDE_CMD_READ);
|
||||
|
||||
int ret = 0;
|
||||
for (; nsecs > 0; nsecs --, dst += SECTSIZE) {
|
||||
if ((ret = ide_wait_ready(iobase, 1)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
insl(iobase, dst, SECTSIZE / sizeof(uint32_t));
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs) {
|
||||
assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
|
||||
assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
|
||||
unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno);
|
||||
|
||||
ide_wait_ready(iobase, 0);
|
||||
|
||||
// generate interrupt
|
||||
outb(ioctrl + ISA_CTRL, 0);
|
||||
outb(iobase + ISA_SECCNT, nsecs);
|
||||
outb(iobase + ISA_SECTOR, secno & 0xFF);
|
||||
outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF);
|
||||
outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF);
|
||||
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF));
|
||||
outb(iobase + ISA_COMMAND, IDE_CMD_WRITE);
|
||||
|
||||
int ret = 0;
|
||||
for (; nsecs > 0; nsecs --, src += SECTSIZE) {
|
||||
if ((ret = ide_wait_ready(iobase, 1)) != 0) {
|
||||
goto out;
|
||||
}
|
||||
outsl(iobase, src, SECTSIZE / sizeof(uint32_t));
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
14
related_info/lab5/lab5-spoc-discuss/kern/driver/ide.h
Normal file
14
related_info/lab5/lab5-spoc-discuss/kern/driver/ide.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __KERN_DRIVER_IDE_H__
|
||||
#define __KERN_DRIVER_IDE_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
void ide_init(void);
|
||||
bool ide_device_valid(unsigned short ideno);
|
||||
size_t ide_device_size(unsigned short ideno);
|
||||
|
||||
int ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs);
|
||||
int ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs);
|
||||
|
||||
#endif /* !__KERN_DRIVER_IDE_H__ */
|
||||
|
15
related_info/lab5/lab5-spoc-discuss/kern/driver/intr.c
Normal file
15
related_info/lab5/lab5-spoc-discuss/kern/driver/intr.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <x86.h>
|
||||
#include <intr.h>
|
||||
|
||||
/* intr_enable - enable irq interrupt */
|
||||
void
|
||||
intr_enable(void) {
|
||||
sti();
|
||||
}
|
||||
|
||||
/* intr_disable - disable irq interrupt */
|
||||
void
|
||||
intr_disable(void) {
|
||||
cli();
|
||||
}
|
||||
|
8
related_info/lab5/lab5-spoc-discuss/kern/driver/intr.h
Normal file
8
related_info/lab5/lab5-spoc-discuss/kern/driver/intr.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef __KERN_DRIVER_INTR_H__
|
||||
#define __KERN_DRIVER_INTR_H__
|
||||
|
||||
void intr_enable(void);
|
||||
void intr_disable(void);
|
||||
|
||||
#endif /* !__KERN_DRIVER_INTR_H__ */
|
||||
|
84
related_info/lab5/lab5-spoc-discuss/kern/driver/kbdreg.h
Normal file
84
related_info/lab5/lab5-spoc-discuss/kern/driver/kbdreg.h
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef __KERN_DRIVER_KBDREG_H__
|
||||
#define __KERN_DRIVER_KBDREG_H__
|
||||
|
||||
// Special keycodes
|
||||
#define KEY_HOME 0xE0
|
||||
#define KEY_END 0xE1
|
||||
#define KEY_UP 0xE2
|
||||
#define KEY_DN 0xE3
|
||||
#define KEY_LF 0xE4
|
||||
#define KEY_RT 0xE5
|
||||
#define KEY_PGUP 0xE6
|
||||
#define KEY_PGDN 0xE7
|
||||
#define KEY_INS 0xE8
|
||||
#define KEY_DEL 0xE9
|
||||
|
||||
|
||||
/* This is i8042reg.h + kbdreg.h from NetBSD. */
|
||||
|
||||
#define KBSTATP 0x64 // kbd controller status port(I)
|
||||
#define KBS_DIB 0x01 // kbd data in buffer
|
||||
#define KBS_IBF 0x02 // kbd input buffer low
|
||||
#define KBS_WARM 0x04 // kbd input buffer low
|
||||
#define BS_OCMD 0x08 // kbd output buffer has command
|
||||
#define KBS_NOSEC 0x10 // kbd security lock not engaged
|
||||
#define KBS_TERR 0x20 // kbd transmission error
|
||||
#define KBS_RERR 0x40 // kbd receive error
|
||||
#define KBS_PERR 0x80 // kbd parity error
|
||||
|
||||
#define KBCMDP 0x64 // kbd controller port(O)
|
||||
#define KBC_RAMREAD 0x20 // read from RAM
|
||||
#define KBC_RAMWRITE 0x60 // write to RAM
|
||||
#define KBC_AUXDISABLE 0xa7 // disable auxiliary port
|
||||
#define KBC_AUXENABLE 0xa8 // enable auxiliary port
|
||||
#define KBC_AUXTEST 0xa9 // test auxiliary port
|
||||
#define KBC_KBDECHO 0xd2 // echo to keyboard port
|
||||
#define KBC_AUXECHO 0xd3 // echo to auxiliary port
|
||||
#define KBC_AUXWRITE 0xd4 // write to auxiliary port
|
||||
#define KBC_SELFTEST 0xaa // start self-test
|
||||
#define KBC_KBDTEST 0xab // test keyboard port
|
||||
#define KBC_KBDDISABLE 0xad // disable keyboard port
|
||||
#define KBC_KBDENABLE 0xae // enable keyboard port
|
||||
#define KBC_PULSE0 0xfe // pulse output bit 0
|
||||
#define KBC_PULSE1 0xfd // pulse output bit 1
|
||||
#define KBC_PULSE2 0xfb // pulse output bit 2
|
||||
#define KBC_PULSE3 0xf7 // pulse output bit 3
|
||||
|
||||
#define KBDATAP 0x60 // kbd data port(I)
|
||||
#define KBOUTP 0x60 // kbd data port(O)
|
||||
|
||||
#define K_RDCMDBYTE 0x20
|
||||
#define K_LDCMDBYTE 0x60
|
||||
|
||||
#define KC8_TRANS 0x40 // convert to old scan codes
|
||||
#define KC8_MDISABLE 0x20 // disable mouse
|
||||
#define KC8_KDISABLE 0x10 // disable keyboard
|
||||
#define KC8_IGNSEC 0x08 // ignore security lock
|
||||
#define KC8_CPU 0x04 // exit from protected mode reset
|
||||
#define KC8_MENABLE 0x02 // enable mouse interrupt
|
||||
#define KC8_KENABLE 0x01 // enable keyboard interrupt
|
||||
#define CMDBYTE (KC8_TRANS|KC8_CPU|KC8_MENABLE|KC8_KENABLE)
|
||||
|
||||
/* keyboard commands */
|
||||
#define KBC_RESET 0xFF // reset the keyboard
|
||||
#define KBC_RESEND 0xFE // request the keyboard resend the last byte
|
||||
#define KBC_SETDEFAULT 0xF6 // resets keyboard to its power-on defaults
|
||||
#define KBC_DISABLE 0xF5 // as per KBC_SETDEFAULT, but also disable key scanning
|
||||
#define KBC_ENABLE 0xF4 // enable key scanning
|
||||
#define KBC_TYPEMATIC 0xF3 // set typematic rate and delay
|
||||
#define KBC_SETTABLE 0xF0 // set scancode translation table
|
||||
#define KBC_MODEIND 0xED // set mode indicators(i.e. LEDs)
|
||||
#define KBC_ECHO 0xEE // request an echo from the keyboard
|
||||
|
||||
/* keyboard responses */
|
||||
#define KBR_EXTENDED 0xE0 // extended key sequence
|
||||
#define KBR_RESEND 0xFE // needs resend of command
|
||||
#define KBR_ACK 0xFA // received a valid command
|
||||
#define KBR_OVERRUN 0x00 // flooded
|
||||
#define KBR_FAILURE 0xFD // diagnosic failure
|
||||
#define KBR_BREAK 0xF0 // break code prefix - sent on key release
|
||||
#define KBR_RSTDONE 0xAA // reset complete
|
||||
#define KBR_ECHO 0xEE // echo response
|
||||
|
||||
#endif /* !__KERN_DRIVER_KBDREG_H__ */
|
||||
|
86
related_info/lab5/lab5-spoc-discuss/kern/driver/picirq.c
Normal file
86
related_info/lab5/lab5-spoc-discuss/kern/driver/picirq.c
Normal file
@ -0,0 +1,86 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <picirq.h>
|
||||
|
||||
// I/O Addresses of the two 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
|
||||
|
||||
// Current IRQ mask.
|
||||
// Initial IRQ mask has interrupt 2 enabled (for slave 8259A).
|
||||
static uint16_t irq_mask = 0xFFFF & ~(1 << IRQ_SLAVE);
|
||||
static bool did_init = 0;
|
||||
|
||||
static void
|
||||
pic_setmask(uint16_t mask) {
|
||||
irq_mask = mask;
|
||||
if (did_init) {
|
||||
outb(IO_PIC1 + 1, mask);
|
||||
outb(IO_PIC2 + 1, mask >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
pic_enable(unsigned int irq) {
|
||||
pic_setmask(irq_mask & ~(1 << irq));
|
||||
}
|
||||
|
||||
/* pic_init - initialize the 8259A interrupt controllers */
|
||||
void
|
||||
pic_init(void) {
|
||||
did_init = 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: (master PIC) bit mask of IR lines connected to slaves
|
||||
// (slave PIC) 3-bit # of slave's connection to master
|
||||
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, 0x3); // 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 != 0xFFFF) {
|
||||
pic_setmask(irq_mask);
|
||||
}
|
||||
}
|
||||
|
10
related_info/lab5/lab5-spoc-discuss/kern/driver/picirq.h
Normal file
10
related_info/lab5/lab5-spoc-discuss/kern/driver/picirq.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef __KERN_DRIVER_PICIRQ_H__
|
||||
#define __KERN_DRIVER_PICIRQ_H__
|
||||
|
||||
void pic_init(void);
|
||||
void pic_enable(unsigned int irq);
|
||||
|
||||
#define IRQ_OFFSET 32
|
||||
|
||||
#endif /* !__KERN_DRIVER_PICIRQ_H__ */
|
||||
|
12
related_info/lab5/lab5-spoc-discuss/kern/fs/fs.h
Normal file
12
related_info/lab5/lab5-spoc-discuss/kern/fs/fs.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __KERN_FS_FS_H__
|
||||
#define __KERN_FS_FS_H__
|
||||
|
||||
#include <mmu.h>
|
||||
|
||||
#define SECTSIZE 512
|
||||
#define PAGE_NSECT (PGSIZE / SECTSIZE)
|
||||
|
||||
#define SWAP_DEV_NO 1
|
||||
|
||||
#endif /* !__KERN_FS_FS_H__ */
|
||||
|
27
related_info/lab5/lab5-spoc-discuss/kern/fs/swapfs.c
Normal file
27
related_info/lab5/lab5-spoc-discuss/kern/fs/swapfs.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include <swap.h>
|
||||
#include <swapfs.h>
|
||||
#include <mmu.h>
|
||||
#include <fs.h>
|
||||
#include <ide.h>
|
||||
#include <pmm.h>
|
||||
#include <assert.h>
|
||||
|
||||
void
|
||||
swapfs_init(void) {
|
||||
static_assert((PGSIZE % SECTSIZE) == 0);
|
||||
if (!ide_device_valid(SWAP_DEV_NO)) {
|
||||
panic("swap fs isn't available.\n");
|
||||
}
|
||||
max_swap_offset = ide_device_size(SWAP_DEV_NO) / (PGSIZE / SECTSIZE);
|
||||
}
|
||||
|
||||
int
|
||||
swapfs_read(swap_entry_t entry, struct Page *page) {
|
||||
return ide_read_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT);
|
||||
}
|
||||
|
||||
int
|
||||
swapfs_write(swap_entry_t entry, struct Page *page) {
|
||||
return ide_write_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT);
|
||||
}
|
||||
|
12
related_info/lab5/lab5-spoc-discuss/kern/fs/swapfs.h
Normal file
12
related_info/lab5/lab5-spoc-discuss/kern/fs/swapfs.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __KERN_FS_SWAPFS_H__
|
||||
#define __KERN_FS_SWAPFS_H__
|
||||
|
||||
#include <memlayout.h>
|
||||
#include <swap.h>
|
||||
|
||||
void swapfs_init(void);
|
||||
int swapfs_read(swap_entry_t entry, struct Page *page);
|
||||
int swapfs_write(swap_entry_t entry, struct Page *page);
|
||||
|
||||
#endif /* !__KERN_FS_SWAPFS_H__ */
|
||||
|
49
related_info/lab5/lab5-spoc-discuss/kern/init/entry.S
Normal file
49
related_info/lab5/lab5-spoc-discuss/kern/init/entry.S
Normal file
@ -0,0 +1,49 @@
|
||||
#include <mmu.h>
|
||||
#include <memlayout.h>
|
||||
|
||||
#define REALLOC(x) (x - KERNBASE)
|
||||
|
||||
.text
|
||||
.globl kern_entry
|
||||
kern_entry:
|
||||
# reload temperate gdt (second time) to remap all physical memory
|
||||
# virtual_addr 0~4G=linear_addr&physical_addr -KERNBASE~4G-KERNBASE
|
||||
lgdt REALLOC(__gdtdesc)
|
||||
movl $KERNEL_DS, %eax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
|
||||
ljmp $KERNEL_CS, $relocated
|
||||
|
||||
relocated:
|
||||
|
||||
# set ebp, esp
|
||||
movl $0x0, %ebp
|
||||
# the kernel stack region is from bootstack -- bootstacktop,
|
||||
# the kernel stack size is KSTACKSIZE (8KB)defined in memlayout.h
|
||||
movl $bootstacktop, %esp
|
||||
# now kernel stack is ready , call the first C function
|
||||
call kern_init
|
||||
|
||||
# should never get here
|
||||
spin:
|
||||
jmp spin
|
||||
|
||||
.data
|
||||
.align PGSIZE
|
||||
.globl bootstack
|
||||
bootstack:
|
||||
.space KSTACKSIZE
|
||||
.globl bootstacktop
|
||||
bootstacktop:
|
||||
|
||||
.align 4
|
||||
__gdt:
|
||||
SEG_NULL
|
||||
SEG_ASM(STA_X | STA_R, - KERNBASE, 0xFFFFFFFF) # code segment
|
||||
SEG_ASM(STA_W, - KERNBASE, 0xFFFFFFFF) # data segment
|
||||
__gdtdesc:
|
||||
.word 0x17 # sizeof(__gdt) - 1
|
||||
.long REALLOC(__gdt)
|
||||
|
113
related_info/lab5/lab5-spoc-discuss/kern/init/init.c
Normal file
113
related_info/lab5/lab5-spoc-discuss/kern/init/init.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <console.h>
|
||||
#include <kdebug.h>
|
||||
#include <picirq.h>
|
||||
#include <trap.h>
|
||||
#include <clock.h>
|
||||
#include <intr.h>
|
||||
#include <pmm.h>
|
||||
#include <vmm.h>
|
||||
#include <ide.h>
|
||||
#include <swap.h>
|
||||
#include <proc.h>
|
||||
|
||||
int kern_init(void) __attribute__((noreturn));
|
||||
|
||||
static void lab1_switch_test(void);
|
||||
|
||||
int
|
||||
kern_init(void) {
|
||||
extern char edata[], end[];
|
||||
memset(edata, 0, end - edata);
|
||||
|
||||
cons_init(); // init the console
|
||||
|
||||
const char *message = "(THU.CST) os is loading ...";
|
||||
cprintf("%s\n\n", message);
|
||||
|
||||
print_kerninfo();
|
||||
|
||||
grade_backtrace();
|
||||
|
||||
pmm_init(); // init physical memory management
|
||||
|
||||
pic_init(); // init interrupt controller
|
||||
idt_init(); // init interrupt descriptor table
|
||||
|
||||
vmm_init(); // init virtual memory management
|
||||
proc_init(); // init process table
|
||||
|
||||
ide_init(); // init ide devices
|
||||
swap_init(); // init swap
|
||||
|
||||
clock_init(); // init clock interrupt
|
||||
intr_enable(); // enable irq interrupt
|
||||
|
||||
//LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test()
|
||||
// user/kernel mode switch test
|
||||
//lab1_switch_test();
|
||||
|
||||
cpu_idle(); // run idle process
|
||||
}
|
||||
|
||||
void __attribute__((noinline))
|
||||
grade_backtrace2(int arg0, int arg1, int arg2, int arg3) {
|
||||
mon_backtrace(0, NULL, NULL);
|
||||
}
|
||||
|
||||
void __attribute__((noinline))
|
||||
grade_backtrace1(int arg0, int arg1) {
|
||||
grade_backtrace2(arg0, (int)&arg0, arg1, (int)&arg1);
|
||||
}
|
||||
|
||||
void __attribute__((noinline))
|
||||
grade_backtrace0(int arg0, int arg1, int arg2) {
|
||||
grade_backtrace1(arg0, arg2);
|
||||
}
|
||||
|
||||
void
|
||||
grade_backtrace(void) {
|
||||
grade_backtrace0(0, (int)kern_init, 0xffff0000);
|
||||
}
|
||||
|
||||
static void
|
||||
lab1_print_cur_status(void) {
|
||||
static int round = 0;
|
||||
uint16_t reg1, reg2, reg3, reg4;
|
||||
asm volatile (
|
||||
"mov %%cs, %0;"
|
||||
"mov %%ds, %1;"
|
||||
"mov %%es, %2;"
|
||||
"mov %%ss, %3;"
|
||||
: "=m"(reg1), "=m"(reg2), "=m"(reg3), "=m"(reg4));
|
||||
cprintf("%d: @ring %d\n", round, reg1 & 3);
|
||||
cprintf("%d: cs = %x\n", round, reg1);
|
||||
cprintf("%d: ds = %x\n", round, reg2);
|
||||
cprintf("%d: es = %x\n", round, reg3);
|
||||
cprintf("%d: ss = %x\n", round, reg4);
|
||||
round ++;
|
||||
}
|
||||
|
||||
static void
|
||||
lab1_switch_to_user(void) {
|
||||
//LAB1 CHALLENGE 1 : TODO
|
||||
}
|
||||
|
||||
static void
|
||||
lab1_switch_to_kernel(void) {
|
||||
//LAB1 CHALLENGE 1 : TODO
|
||||
}
|
||||
|
||||
static void
|
||||
lab1_switch_test(void) {
|
||||
lab1_print_cur_status();
|
||||
cprintf("+++ switch to user mode +++\n");
|
||||
lab1_switch_to_user();
|
||||
lab1_print_cur_status();
|
||||
cprintf("+++ switch to kernel mode +++\n");
|
||||
lab1_switch_to_kernel();
|
||||
lab1_print_cur_status();
|
||||
}
|
||||
|
528
related_info/lab5/lab5-spoc-discuss/kern/libs/rb_tree.c
Normal file
528
related_info/lab5/lab5-spoc-discuss/kern/libs/rb_tree.c
Normal file
@ -0,0 +1,528 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <kmalloc.h>
|
||||
#include <rb_tree.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* rb_node_create - create a new rb_node */
|
||||
static inline rb_node *
|
||||
rb_node_create(void) {
|
||||
return kmalloc(sizeof(rb_node));
|
||||
}
|
||||
|
||||
/* rb_tree_empty - tests if tree is empty */
|
||||
static inline bool
|
||||
rb_tree_empty(rb_tree *tree) {
|
||||
rb_node *nil = tree->nil, *root = tree->root;
|
||||
return root->left == nil;
|
||||
}
|
||||
|
||||
/* *
|
||||
* rb_tree_create - creates a new red-black tree, the 'compare' function
|
||||
* is required and returns 'NULL' if failed.
|
||||
*
|
||||
* Note that, root->left should always point to the node that is the root
|
||||
* of the tree. And nil points to a 'NULL' node which should always be
|
||||
* black and may have arbitrary children and parent node.
|
||||
* */
|
||||
rb_tree *
|
||||
rb_tree_create(int (*compare)(rb_node *node1, rb_node *node2)) {
|
||||
assert(compare != NULL);
|
||||
|
||||
rb_tree *tree;
|
||||
rb_node *nil, *root;
|
||||
|
||||
if ((tree = kmalloc(sizeof(rb_tree))) == NULL) {
|
||||
goto bad_tree;
|
||||
}
|
||||
|
||||
tree->compare = compare;
|
||||
|
||||
if ((nil = rb_node_create()) == NULL) {
|
||||
goto bad_node_cleanup_tree;
|
||||
}
|
||||
|
||||
nil->parent = nil->left = nil->right = nil;
|
||||
nil->red = 0;
|
||||
tree->nil = nil;
|
||||
|
||||
if ((root = rb_node_create()) == NULL) {
|
||||
goto bad_node_cleanup_nil;
|
||||
}
|
||||
|
||||
root->parent = root->left = root->right = nil;
|
||||
root->red = 0;
|
||||
tree->root = root;
|
||||
return tree;
|
||||
|
||||
bad_node_cleanup_nil:
|
||||
kfree(nil);
|
||||
bad_node_cleanup_tree:
|
||||
kfree(tree);
|
||||
bad_tree:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* *
|
||||
* FUNC_ROTATE - rotates as described in "Introduction to Algorithm".
|
||||
*
|
||||
* For example, FUNC_ROTATE(rb_left_rotate, left, right) can be expaned to a
|
||||
* left-rotate function, which requires an red-black 'tree' and a node 'x'
|
||||
* to be rotated on. Basically, this function, named rb_left_rotate, makes the
|
||||
* parent of 'x' be the left child of 'x', 'x' the parent of its parent before
|
||||
* rotation and finally fixes other nodes accordingly.
|
||||
*
|
||||
* FUNC_ROTATE(xx, left, right) means left-rotate,
|
||||
* and FUNC_ROTATE(xx, right, left) means right-rotate.
|
||||
* */
|
||||
#define FUNC_ROTATE(func_name, _left, _right) \
|
||||
static void \
|
||||
func_name(rb_tree *tree, rb_node *x) { \
|
||||
rb_node *nil = tree->nil, *y = x->_right; \
|
||||
assert(x != tree->root && x != nil && y != nil); \
|
||||
x->_right = y->_left; \
|
||||
if (y->_left != nil) { \
|
||||
y->_left->parent = x; \
|
||||
} \
|
||||
y->parent = x->parent; \
|
||||
if (x == x->parent->_left) { \
|
||||
x->parent->_left = y; \
|
||||
} \
|
||||
else { \
|
||||
x->parent->_right = y; \
|
||||
} \
|
||||
y->_left = x; \
|
||||
x->parent = y; \
|
||||
assert(!(nil->red)); \
|
||||
}
|
||||
|
||||
FUNC_ROTATE(rb_left_rotate, left, right);
|
||||
FUNC_ROTATE(rb_right_rotate, right, left);
|
||||
|
||||
#undef FUNC_ROTATE
|
||||
|
||||
#define COMPARE(tree, node1, node2) \
|
||||
((tree))->compare((node1), (node2))
|
||||
|
||||
/* *
|
||||
* rb_insert_binary - insert @node to red-black @tree as if it were
|
||||
* a regular binary tree. This function is only intended to be called
|
||||
* by function rb_insert.
|
||||
* */
|
||||
static inline void
|
||||
rb_insert_binary(rb_tree *tree, rb_node *node) {
|
||||
rb_node *x, *y, *z = node, *nil = tree->nil, *root = tree->root;
|
||||
|
||||
z->left = z->right = nil;
|
||||
y = root, x = y->left;
|
||||
while (x != nil) {
|
||||
y = x;
|
||||
x = (COMPARE(tree, x, node) > 0) ? x->left : x->right;
|
||||
}
|
||||
z->parent = y;
|
||||
if (y == root || COMPARE(tree, y, z) > 0) {
|
||||
y->left = z;
|
||||
}
|
||||
else {
|
||||
y->right = z;
|
||||
}
|
||||
}
|
||||
|
||||
/* rb_insert - insert a node to red-black tree */
|
||||
void
|
||||
rb_insert(rb_tree *tree, rb_node *node) {
|
||||
rb_insert_binary(tree, node);
|
||||
node->red = 1;
|
||||
|
||||
rb_node *x = node, *y;
|
||||
|
||||
#define RB_INSERT_SUB(_left, _right) \
|
||||
do { \
|
||||
y = x->parent->parent->_right; \
|
||||
if (y->red) { \
|
||||
x->parent->red = 0; \
|
||||
y->red = 0; \
|
||||
x->parent->parent->red = 1; \
|
||||
x = x->parent->parent; \
|
||||
} \
|
||||
else { \
|
||||
if (x == x->parent->_right) { \
|
||||
x = x->parent; \
|
||||
rb_##_left##_rotate(tree, x); \
|
||||
} \
|
||||
x->parent->red = 0; \
|
||||
x->parent->parent->red = 1; \
|
||||
rb_##_right##_rotate(tree, x->parent->parent); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
while (x->parent->red) {
|
||||
if (x->parent == x->parent->parent->left) {
|
||||
RB_INSERT_SUB(left, right);
|
||||
}
|
||||
else {
|
||||
RB_INSERT_SUB(right, left);
|
||||
}
|
||||
}
|
||||
tree->root->left->red = 0;
|
||||
assert(!(tree->nil->red) && !(tree->root->red));
|
||||
|
||||
#undef RB_INSERT_SUB
|
||||
}
|
||||
|
||||
/* *
|
||||
* rb_tree_successor - returns the successor of @node, or nil
|
||||
* if no successor exists. Make sure that @node must belong to @tree,
|
||||
* and this function should only be called by rb_node_prev.
|
||||
* */
|
||||
static inline rb_node *
|
||||
rb_tree_successor(rb_tree *tree, rb_node *node) {
|
||||
rb_node *x = node, *y, *nil = tree->nil;
|
||||
|
||||
if ((y = x->right) != nil) {
|
||||
while (y->left != nil) {
|
||||
y = y->left;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
else {
|
||||
y = x->parent;
|
||||
while (x == y->right) {
|
||||
x = y, y = y->parent;
|
||||
}
|
||||
if (y == tree->root) {
|
||||
return nil;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* rb_tree_predecessor - returns the predecessor of @node, or nil
|
||||
* if no predecessor exists, likes rb_tree_successor.
|
||||
* */
|
||||
static inline rb_node *
|
||||
rb_tree_predecessor(rb_tree *tree, rb_node *node) {
|
||||
rb_node *x = node, *y, *nil = tree->nil;
|
||||
|
||||
if ((y = x->left) != nil) {
|
||||
while (y->right != nil) {
|
||||
y = y->right;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
else {
|
||||
y = x->parent;
|
||||
while (x == y->left) {
|
||||
if (y == tree->root) {
|
||||
return nil;
|
||||
}
|
||||
x = y, y = y->parent;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* rb_search - returns a node with value 'equal' to @key (according to
|
||||
* function @compare). If there're multiple nodes with value 'equal' to @key,
|
||||
* the functions returns the one highest in the tree.
|
||||
* */
|
||||
rb_node *
|
||||
rb_search(rb_tree *tree, int (*compare)(rb_node *node, void *key), void *key) {
|
||||
rb_node *nil = tree->nil, *node = tree->root->left;
|
||||
int r;
|
||||
while (node != nil && (r = compare(node, key)) != 0) {
|
||||
node = (r > 0) ? node->left : node->right;
|
||||
}
|
||||
return (node != nil) ? node : NULL;
|
||||
}
|
||||
|
||||
/* *
|
||||
* rb_delete_fixup - performs rotations and changes colors to restore
|
||||
* red-black properties after a node is deleted.
|
||||
* */
|
||||
static void
|
||||
rb_delete_fixup(rb_tree *tree, rb_node *node) {
|
||||
rb_node *x = node, *w, *root = tree->root->left;
|
||||
|
||||
#define RB_DELETE_FIXUP_SUB(_left, _right) \
|
||||
do { \
|
||||
w = x->parent->_right; \
|
||||
if (w->red) { \
|
||||
w->red = 0; \
|
||||
x->parent->red = 1; \
|
||||
rb_##_left##_rotate(tree, x->parent); \
|
||||
w = x->parent->_right; \
|
||||
} \
|
||||
if (!w->_left->red && !w->_right->red) { \
|
||||
w->red = 1; \
|
||||
x = x->parent; \
|
||||
} \
|
||||
else { \
|
||||
if (!w->_right->red) { \
|
||||
w->_left->red = 0; \
|
||||
w->red = 1; \
|
||||
rb_##_right##_rotate(tree, w); \
|
||||
w = x->parent->_right; \
|
||||
} \
|
||||
w->red = x->parent->red; \
|
||||
x->parent->red = 0; \
|
||||
w->_right->red = 0; \
|
||||
rb_##_left##_rotate(tree, x->parent); \
|
||||
x = root; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
while (x != root && !x->red) {
|
||||
if (x == x->parent->left) {
|
||||
RB_DELETE_FIXUP_SUB(left, right);
|
||||
}
|
||||
else {
|
||||
RB_DELETE_FIXUP_SUB(right, left);
|
||||
}
|
||||
}
|
||||
x->red = 0;
|
||||
|
||||
#undef RB_DELETE_FIXUP_SUB
|
||||
}
|
||||
|
||||
/* *
|
||||
* rb_delete - deletes @node from @tree, and calls rb_delete_fixup to
|
||||
* restore red-black properties.
|
||||
* */
|
||||
void
|
||||
rb_delete(rb_tree *tree, rb_node *node) {
|
||||
rb_node *x, *y, *z = node;
|
||||
rb_node *nil = tree->nil, *root = tree->root;
|
||||
|
||||
y = (z->left == nil || z->right == nil) ? z : rb_tree_successor(tree, z);
|
||||
x = (y->left != nil) ? y->left : y->right;
|
||||
|
||||
assert(y != root && y != nil);
|
||||
|
||||
x->parent = y->parent;
|
||||
if (y == y->parent->left) {
|
||||
y->parent->left = x;
|
||||
}
|
||||
else {
|
||||
y->parent->right = x;
|
||||
}
|
||||
|
||||
bool need_fixup = !(y->red);
|
||||
|
||||
if (y != z) {
|
||||
if (z == z->parent->left) {
|
||||
z->parent->left = y;
|
||||
}
|
||||
else {
|
||||
z->parent->right = y;
|
||||
}
|
||||
z->left->parent = z->right->parent = y;
|
||||
*y = *z;
|
||||
}
|
||||
if (need_fixup) {
|
||||
rb_delete_fixup(tree, x);
|
||||
}
|
||||
}
|
||||
|
||||
/* rb_tree_destroy - destroy a tree and free memory */
|
||||
void
|
||||
rb_tree_destroy(rb_tree *tree) {
|
||||
kfree(tree->root);
|
||||
kfree(tree->nil);
|
||||
kfree(tree);
|
||||
}
|
||||
|
||||
/* *
|
||||
* rb_node_prev - returns the predecessor node of @node in @tree,
|
||||
* or 'NULL' if no predecessor exists.
|
||||
* */
|
||||
rb_node *
|
||||
rb_node_prev(rb_tree *tree, rb_node *node) {
|
||||
rb_node *prev = rb_tree_predecessor(tree, node);
|
||||
return (prev != tree->nil) ? prev : NULL;
|
||||
}
|
||||
|
||||
/* *
|
||||
* rb_node_next - returns the successor node of @node in @tree,
|
||||
* or 'NULL' if no successor exists.
|
||||
* */
|
||||
rb_node *
|
||||
rb_node_next(rb_tree *tree, rb_node *node) {
|
||||
rb_node *next = rb_tree_successor(tree, node);
|
||||
return (next != tree->nil) ? next : NULL;
|
||||
}
|
||||
|
||||
/* rb_node_root - returns the root node of a @tree, or 'NULL' if tree is empty */
|
||||
rb_node *
|
||||
rb_node_root(rb_tree *tree) {
|
||||
rb_node *node = tree->root->left;
|
||||
return (node != tree->nil) ? node : NULL;
|
||||
}
|
||||
|
||||
/* rb_node_left - gets the left child of @node, or 'NULL' if no such node */
|
||||
rb_node *
|
||||
rb_node_left(rb_tree *tree, rb_node *node) {
|
||||
rb_node *left = node->left;
|
||||
return (left != tree->nil) ? left : NULL;
|
||||
}
|
||||
|
||||
/* rb_node_right - gets the right child of @node, or 'NULL' if no such node */
|
||||
rb_node *
|
||||
rb_node_right(rb_tree *tree, rb_node *node) {
|
||||
rb_node *right = node->right;
|
||||
return (right != tree->nil) ? right : NULL;
|
||||
}
|
||||
|
||||
int
|
||||
check_tree(rb_tree *tree, rb_node *node) {
|
||||
rb_node *nil = tree->nil;
|
||||
if (node == nil) {
|
||||
assert(!node->red);
|
||||
return 1;
|
||||
}
|
||||
if (node->left != nil) {
|
||||
assert(COMPARE(tree, node, node->left) >= 0);
|
||||
assert(node->left->parent == node);
|
||||
}
|
||||
if (node->right != nil) {
|
||||
assert(COMPARE(tree, node, node->right) <= 0);
|
||||
assert(node->right->parent == node);
|
||||
}
|
||||
if (node->red) {
|
||||
assert(!node->left->red && !node->right->red);
|
||||
}
|
||||
int hb_left = check_tree(tree, node->left);
|
||||
int hb_right = check_tree(tree, node->right);
|
||||
assert(hb_left == hb_right);
|
||||
int hb = hb_left;
|
||||
if (!node->red) {
|
||||
hb ++;
|
||||
}
|
||||
return hb;
|
||||
}
|
||||
|
||||
static void *
|
||||
check_safe_kmalloc(size_t size) {
|
||||
void *ret = kmalloc(size);
|
||||
assert(ret != NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct check_data {
|
||||
long data;
|
||||
rb_node rb_link;
|
||||
};
|
||||
|
||||
#define rbn2data(node) \
|
||||
(to_struct(node, struct check_data, rb_link))
|
||||
|
||||
static inline int
|
||||
check_compare1(rb_node *node1, rb_node *node2) {
|
||||
return rbn2data(node1)->data - rbn2data(node2)->data;
|
||||
}
|
||||
|
||||
static inline int
|
||||
check_compare2(rb_node *node, void *key) {
|
||||
return rbn2data(node)->data - (long)key;
|
||||
}
|
||||
|
||||
void
|
||||
check_rb_tree(void) {
|
||||
rb_tree *tree = rb_tree_create(check_compare1);
|
||||
assert(tree != NULL);
|
||||
|
||||
rb_node *nil = tree->nil, *root = tree->root;
|
||||
assert(!nil->red && root->left == nil);
|
||||
|
||||
int total = 1000;
|
||||
struct check_data **all = check_safe_kmalloc(sizeof(struct check_data *) * total);
|
||||
|
||||
long i;
|
||||
for (i = 0; i < total; i ++) {
|
||||
all[i] = check_safe_kmalloc(sizeof(struct check_data));
|
||||
all[i]->data = i;
|
||||
}
|
||||
|
||||
int *mark = check_safe_kmalloc(sizeof(int) * total);
|
||||
memset(mark, 0, sizeof(int) * total);
|
||||
|
||||
for (i = 0; i < total; i ++) {
|
||||
mark[all[i]->data] = 1;
|
||||
}
|
||||
for (i = 0; i < total; i ++) {
|
||||
assert(mark[i] == 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < total; i ++) {
|
||||
int j = (rand() % (total - i)) + i;
|
||||
struct check_data *z = all[i];
|
||||
all[i] = all[j];
|
||||
all[j] = z;
|
||||
}
|
||||
|
||||
memset(mark, 0, sizeof(int) * total);
|
||||
for (i = 0; i < total; i ++) {
|
||||
mark[all[i]->data] = 1;
|
||||
}
|
||||
for (i = 0; i < total; i ++) {
|
||||
assert(mark[i] == 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < total; i ++) {
|
||||
rb_insert(tree, &(all[i]->rb_link));
|
||||
check_tree(tree, root->left);
|
||||
}
|
||||
|
||||
rb_node *node;
|
||||
for (i = 0; i < total; i ++) {
|
||||
node = rb_search(tree, check_compare2, (void *)(all[i]->data));
|
||||
assert(node != NULL && node == &(all[i]->rb_link));
|
||||
}
|
||||
|
||||
for (i = 0; i < total; i ++) {
|
||||
node = rb_search(tree, check_compare2, (void *)i);
|
||||
assert(node != NULL && rbn2data(node)->data == i);
|
||||
rb_delete(tree, node);
|
||||
check_tree(tree, root->left);
|
||||
}
|
||||
|
||||
assert(!nil->red && root->left == nil);
|
||||
|
||||
long max = 32;
|
||||
if (max > total) {
|
||||
max = total;
|
||||
}
|
||||
|
||||
for (i = 0; i < max; i ++) {
|
||||
all[i]->data = max;
|
||||
rb_insert(tree, &(all[i]->rb_link));
|
||||
check_tree(tree, root->left);
|
||||
}
|
||||
|
||||
for (i = 0; i < max; i ++) {
|
||||
node = rb_search(tree, check_compare2, (void *)max);
|
||||
assert(node != NULL && rbn2data(node)->data == max);
|
||||
rb_delete(tree, node);
|
||||
check_tree(tree, root->left);
|
||||
}
|
||||
|
||||
assert(rb_tree_empty(tree));
|
||||
|
||||
for (i = 0; i < total; i ++) {
|
||||
rb_insert(tree, &(all[i]->rb_link));
|
||||
check_tree(tree, root->left);
|
||||
}
|
||||
|
||||
rb_tree_destroy(tree);
|
||||
|
||||
for (i = 0; i < total; i ++) {
|
||||
kfree(all[i]);
|
||||
}
|
||||
|
||||
kfree(mark);
|
||||
kfree(all);
|
||||
}
|
||||
|
32
related_info/lab5/lab5-spoc-discuss/kern/libs/rb_tree.h
Normal file
32
related_info/lab5/lab5-spoc-discuss/kern/libs/rb_tree.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef __KERN_LIBS_RB_TREE_H__
|
||||
#define __KERN_LIBS_RB_TREE_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
typedef struct rb_node {
|
||||
bool red; // if red = 0, it's a black node
|
||||
struct rb_node *parent;
|
||||
struct rb_node *left, *right;
|
||||
} rb_node;
|
||||
|
||||
typedef struct rb_tree {
|
||||
// compare function should return -1 if *node1 < *node2, 1 if *node1 > *node2, and 0 otherwise
|
||||
int (*compare)(rb_node *node1, rb_node *node2);
|
||||
struct rb_node *nil, *root;
|
||||
} rb_tree;
|
||||
|
||||
rb_tree *rb_tree_create(int (*compare)(rb_node *node1, rb_node *node2));
|
||||
void rb_tree_destroy(rb_tree *tree);
|
||||
void rb_insert(rb_tree *tree, rb_node *node);
|
||||
void rb_delete(rb_tree *tree, rb_node *node);
|
||||
rb_node *rb_search(rb_tree *tree, int (*compare)(rb_node *node, void *key), void *key);
|
||||
rb_node *rb_node_prev(rb_tree *tree, rb_node *node);
|
||||
rb_node *rb_node_next(rb_tree *tree, rb_node *node);
|
||||
rb_node *rb_node_root(rb_tree *tree);
|
||||
rb_node *rb_node_left(rb_tree *tree, rb_node *node);
|
||||
rb_node *rb_node_right(rb_tree *tree, rb_node *node);
|
||||
|
||||
void check_rb_tree(void);
|
||||
|
||||
#endif /* !__KERN_LIBS_RBTREE_H__ */
|
||||
|
50
related_info/lab5/lab5-spoc-discuss/kern/libs/readline.c
Normal file
50
related_info/lab5/lab5-spoc-discuss/kern/libs/readline.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define BUFSIZE 1024
|
||||
static char buf[BUFSIZE];
|
||||
|
||||
/* *
|
||||
* readline - get a line from stdin
|
||||
* @prompt: the string to be written to stdout
|
||||
*
|
||||
* The readline() function will write the input string @prompt to
|
||||
* stdout first. If the @prompt is NULL or the empty string,
|
||||
* no prompt is issued.
|
||||
*
|
||||
* This function will keep on reading characters and saving them to buffer
|
||||
* 'buf' until '\n' or '\r' is encountered.
|
||||
*
|
||||
* Note that, if the length of string that will be read is longer than
|
||||
* buffer size, the end of string will be discarded.
|
||||
*
|
||||
* The readline() function returns the text of the line read. If some errors
|
||||
* are happened, NULL is returned. The return value is a global variable,
|
||||
* thus it should be copied before it is used.
|
||||
* */
|
||||
char *
|
||||
readline(const char *prompt) {
|
||||
if (prompt != NULL) {
|
||||
cprintf("%s", prompt);
|
||||
}
|
||||
int i = 0, c;
|
||||
while (1) {
|
||||
c = getchar();
|
||||
if (c < 0) {
|
||||
return NULL;
|
||||
}
|
||||
else if (c >= ' ' && i < BUFSIZE - 1) {
|
||||
cputchar(c);
|
||||
buf[i ++] = c;
|
||||
}
|
||||
else if (c == '\b' && i > 0) {
|
||||
cputchar(c);
|
||||
i --;
|
||||
}
|
||||
else if (c == '\n' || c == '\r') {
|
||||
cputchar(c);
|
||||
buf[i] = '\0';
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
78
related_info/lab5/lab5-spoc-discuss/kern/libs/stdio.c
Normal file
78
related_info/lab5/lab5-spoc-discuss/kern/libs/stdio.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <console.h>
|
||||
|
||||
/* HIGH level console I/O */
|
||||
|
||||
/* *
|
||||
* cputch - writes a single character @c to stdout, and it will
|
||||
* increace the value of counter pointed by @cnt.
|
||||
* */
|
||||
static void
|
||||
cputch(int c, int *cnt) {
|
||||
cons_putc(c);
|
||||
(*cnt) ++;
|
||||
}
|
||||
|
||||
/* *
|
||||
* vcprintf - format a string and writes it to stdout
|
||||
*
|
||||
* The return value is the number of characters which would be
|
||||
* written to stdout.
|
||||
*
|
||||
* Call this function if you are already dealing with a va_list.
|
||||
* Or you probably want cprintf() instead.
|
||||
* */
|
||||
int
|
||||
vcprintf(const char *fmt, va_list ap) {
|
||||
int cnt = 0;
|
||||
vprintfmt((void*)cputch, &cnt, fmt, ap);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* *
|
||||
* cprintf - formats a string and writes it to stdout
|
||||
*
|
||||
* The return value is the number of characters which would be
|
||||
* written to stdout.
|
||||
* */
|
||||
int
|
||||
cprintf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
int cnt;
|
||||
va_start(ap, fmt);
|
||||
cnt = vcprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* cputchar - writes a single character to stdout */
|
||||
void
|
||||
cputchar(int c) {
|
||||
cons_putc(c);
|
||||
}
|
||||
|
||||
/* *
|
||||
* cputs- writes the string pointed by @str to stdout and
|
||||
* appends a newline character.
|
||||
* */
|
||||
int
|
||||
cputs(const char *str) {
|
||||
int cnt = 0;
|
||||
char c;
|
||||
while ((c = *str ++) != '\0') {
|
||||
cputch(c, &cnt);
|
||||
}
|
||||
cputch('\n', &cnt);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* getchar - reads a single non-zero character from stdin */
|
||||
int
|
||||
getchar(void) {
|
||||
int c;
|
||||
while ((c = cons_getc()) == 0)
|
||||
/* do nothing */;
|
||||
return c;
|
||||
}
|
||||
|
294
related_info/lab5/lab5-spoc-discuss/kern/mm/default_pmm.c
Normal file
294
related_info/lab5/lab5-spoc-discuss/kern/mm/default_pmm.c
Normal file
@ -0,0 +1,294 @@
|
||||
#include <pmm.h>
|
||||
#include <list.h>
|
||||
#include <string.h>
|
||||
#include <default_pmm.h>
|
||||
|
||||
/* In the first fit algorithm, the allocator keeps a list of free blocks (known as the free list) and,
|
||||
on receiving a request for memory, scans along the list for the first block that is large enough to
|
||||
satisfy the request. If the chosen block is significantly larger than that requested, then it is
|
||||
usually split, and the remainder added to the list as another free block.
|
||||
Please see Page 196~198, Section 8.2 of Yan Wei Ming's chinese book "Data Structure -- C programming language"
|
||||
*/
|
||||
// LAB2 EXERCISE 1: YOUR CODE
|
||||
// you should rewrite functions: default_init,default_init_memmap,default_alloc_pages, default_free_pages.
|
||||
/*
|
||||
* Details of FFMA
|
||||
* (1) Prepare: In order to implement the First-Fit Mem Alloc (FFMA), we should manage the free mem block use some list.
|
||||
* The struct free_area_t is used for the management of free mem blocks. At first you should
|
||||
* be familiar to the struct list in list.h. struct list is a simple doubly linked list implementation.
|
||||
* You should know howto USE: list_init, list_add(list_add_after), list_add_before, list_del, list_next, list_prev
|
||||
* Another tricky method is to transform a general list struct to a special struct (such as struct page):
|
||||
* you can find some MACRO: le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.)
|
||||
* (2) default_init: you can reuse the demo default_init fun to init the free_list and set nr_free to 0.
|
||||
* free_list is used to record the free mem blocks. nr_free is the total number for free mem blocks.
|
||||
* (3) default_init_memmap: CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap
|
||||
* This fun is used to init a free block (with parameter: addr_base, page_number).
|
||||
* First you should init each page (in memlayout.h) in this free block, include:
|
||||
* p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c),
|
||||
* the bit PG_reserved is setted in p->flags)
|
||||
* if this page is free and is not the first page of free block, p->property should be set to 0.
|
||||
* if this page is free and is the first page of free block, p->property should be set to total num of block.
|
||||
* p->ref should be 0, because now p is free and no reference.
|
||||
* We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); )
|
||||
* Finally, we should sum the number of free mem block: nr_free+=n
|
||||
* (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr
|
||||
* of malloced block.
|
||||
* (4.1) So you should search freelist like this:
|
||||
* list_entry_t le = &free_list;
|
||||
* while((le=list_next(le)) != &free_list) {
|
||||
* ....
|
||||
* (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n?
|
||||
* struct Page *p = le2page(le, page_link);
|
||||
* if(p->property >= n){ ...
|
||||
* (4.1.2) If we find this p, then it' means we find a free block(block size >=n), and the first n pages can be malloced.
|
||||
* Some flag bits of this page should be setted: PG_reserved =1, PG_property =0
|
||||
* unlink the pages from free_list
|
||||
* (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block,
|
||||
* (such as: le2page(le,page_link))->property = p->property - n;)
|
||||
* (4.1.3) re-caluclate nr_free (number of the the rest of all free block)
|
||||
* (4.1.4) return p
|
||||
* (4.2) If we can not find a free block (block size >=n), then return NULL
|
||||
* (5) default_free_pages: relink the pages into free list, maybe merge small free blocks into big free blocks.
|
||||
* (5.1) according the base addr of withdrawed blocks, search free list, find the correct position
|
||||
* (from low to high addr), and insert the pages. (may use list_next, le2page, list_add_before)
|
||||
* (5.2) reset the fields of pages, such as p->ref, p->flags (PageProperty)
|
||||
* (5.3) try to merge low addr or high addr blocks. Notice: should change some pages's p->property correctly.
|
||||
*/
|
||||
free_area_t free_area;
|
||||
|
||||
#define free_list (free_area.free_list)
|
||||
#define nr_free (free_area.nr_free)
|
||||
|
||||
static void
|
||||
default_init(void) {
|
||||
list_init(&free_list);
|
||||
nr_free = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
default_init_memmap(struct Page *base, size_t n) {
|
||||
assert(n > 0);
|
||||
struct Page *p = base;
|
||||
for (; p != base + n; p ++) {
|
||||
assert(PageReserved(p));
|
||||
p->flags = 0;
|
||||
SetPageProperty(p);
|
||||
p->property = 0;
|
||||
set_page_ref(p, 0);
|
||||
list_add_before(&free_list, &(p->page_link));
|
||||
}
|
||||
nr_free += n;
|
||||
//first block
|
||||
base->property = n;
|
||||
}
|
||||
|
||||
static struct Page *
|
||||
default_alloc_pages(size_t n) {
|
||||
assert(n > 0);
|
||||
if (n > nr_free) {
|
||||
return NULL;
|
||||
}
|
||||
list_entry_t *le, *len;
|
||||
le = &free_list;
|
||||
|
||||
while((le=list_next(le)) != &free_list) {
|
||||
struct Page *p = le2page(le, page_link);
|
||||
if(p->property >= n){
|
||||
int i;
|
||||
for(i=0;i<n;i++){
|
||||
len = list_next(le);
|
||||
struct Page *pp = le2page(le, page_link);
|
||||
SetPageReserved(pp);
|
||||
ClearPageProperty(pp);
|
||||
list_del(le);
|
||||
le = len;
|
||||
}
|
||||
if(p->property>n){
|
||||
(le2page(le,page_link))->property = p->property - n;
|
||||
}
|
||||
ClearPageProperty(p);
|
||||
SetPageReserved(p);
|
||||
nr_free -= n;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
default_free_pages(struct Page *base, size_t n) {
|
||||
assert(n > 0);
|
||||
assert(PageReserved(base));
|
||||
|
||||
list_entry_t *le = &free_list;
|
||||
struct Page * p;
|
||||
while((le=list_next(le)) != &free_list) {
|
||||
p = le2page(le, page_link);
|
||||
if(p>base){
|
||||
break;
|
||||
}
|
||||
}
|
||||
//list_add_before(le, base->page_link);
|
||||
for(p=base;p<base+n;p++){
|
||||
list_add_before(le, &(p->page_link));
|
||||
}
|
||||
base->flags = 0;
|
||||
set_page_ref(base, 0);
|
||||
ClearPageProperty(base);
|
||||
SetPageProperty(base);
|
||||
base->property = n;
|
||||
|
||||
p = le2page(le,page_link) ;
|
||||
if( base+n == p ){
|
||||
base->property += p->property;
|
||||
p->property = 0;
|
||||
}
|
||||
le = list_prev(&(base->page_link));
|
||||
p = le2page(le, page_link);
|
||||
if(le!=&free_list && p==base-1){
|
||||
while(le!=&free_list){
|
||||
if(p->property){
|
||||
p->property += base->property;
|
||||
base->property = 0;
|
||||
break;
|
||||
}
|
||||
le = list_prev(le);
|
||||
p = le2page(le,page_link);
|
||||
}
|
||||
}
|
||||
|
||||
nr_free += n;
|
||||
return ;
|
||||
}
|
||||
|
||||
static size_t
|
||||
default_nr_free_pages(void) {
|
||||
return nr_free;
|
||||
}
|
||||
|
||||
static void
|
||||
basic_check(void) {
|
||||
struct Page *p0, *p1, *p2;
|
||||
p0 = p1 = p2 = NULL;
|
||||
assert((p0 = alloc_page()) != NULL);
|
||||
assert((p1 = alloc_page()) != NULL);
|
||||
assert((p2 = alloc_page()) != NULL);
|
||||
|
||||
assert(p0 != p1 && p0 != p2 && p1 != p2);
|
||||
assert(page_ref(p0) == 0 && page_ref(p1) == 0 && page_ref(p2) == 0);
|
||||
|
||||
assert(page2pa(p0) < npage * PGSIZE);
|
||||
assert(page2pa(p1) < npage * PGSIZE);
|
||||
assert(page2pa(p2) < npage * PGSIZE);
|
||||
|
||||
list_entry_t free_list_store = free_list;
|
||||
list_init(&free_list);
|
||||
assert(list_empty(&free_list));
|
||||
|
||||
unsigned int nr_free_store = nr_free;
|
||||
nr_free = 0;
|
||||
|
||||
assert(alloc_page() == NULL);
|
||||
|
||||
free_page(p0);
|
||||
free_page(p1);
|
||||
free_page(p2);
|
||||
assert(nr_free == 3);
|
||||
|
||||
assert((p0 = alloc_page()) != NULL);
|
||||
assert((p1 = alloc_page()) != NULL);
|
||||
assert((p2 = alloc_page()) != NULL);
|
||||
|
||||
assert(alloc_page() == NULL);
|
||||
|
||||
free_page(p0);
|
||||
assert(!list_empty(&free_list));
|
||||
|
||||
struct Page *p;
|
||||
assert((p = alloc_page()) == p0);
|
||||
assert(alloc_page() == NULL);
|
||||
|
||||
assert(nr_free == 0);
|
||||
free_list = free_list_store;
|
||||
nr_free = nr_free_store;
|
||||
|
||||
free_page(p);
|
||||
free_page(p1);
|
||||
free_page(p2);
|
||||
}
|
||||
|
||||
// LAB2: below code is used to check the first fit allocation algorithm (your EXERCISE 1)
|
||||
// NOTICE: You SHOULD NOT CHANGE basic_check, default_check functions!
|
||||
static void
|
||||
default_check(void) {
|
||||
int count = 0, total = 0;
|
||||
list_entry_t *le = &free_list;
|
||||
while ((le = list_next(le)) != &free_list) {
|
||||
struct Page *p = le2page(le, page_link);
|
||||
assert(PageProperty(p));
|
||||
count ++, total += p->property;
|
||||
}
|
||||
assert(total == nr_free_pages());
|
||||
|
||||
basic_check();
|
||||
|
||||
struct Page *p0 = alloc_pages(5), *p1, *p2;
|
||||
assert(p0 != NULL);
|
||||
assert(!PageProperty(p0));
|
||||
|
||||
list_entry_t free_list_store = free_list;
|
||||
list_init(&free_list);
|
||||
assert(list_empty(&free_list));
|
||||
assert(alloc_page() == NULL);
|
||||
|
||||
unsigned int nr_free_store = nr_free;
|
||||
nr_free = 0;
|
||||
|
||||
free_pages(p0 + 2, 3);
|
||||
assert(alloc_pages(4) == NULL);
|
||||
assert(PageProperty(p0 + 2) && p0[2].property == 3);
|
||||
assert((p1 = alloc_pages(3)) != NULL);
|
||||
assert(alloc_page() == NULL);
|
||||
assert(p0 + 2 == p1);
|
||||
|
||||
p2 = p0 + 1;
|
||||
free_page(p0);
|
||||
free_pages(p1, 3);
|
||||
assert(PageProperty(p0) && p0->property == 1);
|
||||
assert(PageProperty(p1) && p1->property == 3);
|
||||
|
||||
assert((p0 = alloc_page()) == p2 - 1);
|
||||
free_page(p0);
|
||||
assert((p0 = alloc_pages(2)) == p2 + 1);
|
||||
|
||||
free_pages(p0, 2);
|
||||
free_page(p2);
|
||||
|
||||
assert((p0 = alloc_pages(5)) != NULL);
|
||||
assert(alloc_page() == NULL);
|
||||
|
||||
assert(nr_free == 0);
|
||||
nr_free = nr_free_store;
|
||||
|
||||
free_list = free_list_store;
|
||||
free_pages(p0, 5);
|
||||
|
||||
le = &free_list;
|
||||
while ((le = list_next(le)) != &free_list) {
|
||||
struct Page *p = le2page(le, page_link);
|
||||
count --, total -= p->property;
|
||||
}
|
||||
assert(count == 0);
|
||||
assert(total == 0);
|
||||
}
|
||||
|
||||
const struct pmm_manager default_pmm_manager = {
|
||||
.name = "default_pmm_manager",
|
||||
.init = default_init,
|
||||
.init_memmap = default_init_memmap,
|
||||
.alloc_pages = default_alloc_pages,
|
||||
.free_pages = default_free_pages,
|
||||
.nr_free_pages = default_nr_free_pages,
|
||||
.check = default_check,
|
||||
};
|
||||
|
@ -0,0 +1,9 @@
|
||||
#ifndef __KERN_MM_DEFAULT_PMM_H__
|
||||
#define __KERN_MM_DEFAULT_PMM_H__
|
||||
|
||||
#include <pmm.h>
|
||||
|
||||
extern const struct pmm_manager default_pmm_manager;
|
||||
extern free_area_t free_area;
|
||||
#endif /* ! __KERN_MM_DEFAULT_PMM_H__ */
|
||||
|
310
related_info/lab5/lab5-spoc-discuss/kern/mm/kmalloc.c
Normal file
310
related_info/lab5/lab5-spoc-discuss/kern/mm/kmalloc.c
Normal file
@ -0,0 +1,310 @@
|
||||
#include <defs.h>
|
||||
#include <list.h>
|
||||
#include <memlayout.h>
|
||||
#include <assert.h>
|
||||
#include <kmalloc.h>
|
||||
#include <sync.h>
|
||||
#include <pmm.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* SLOB Allocator: Simple List Of Blocks
|
||||
*
|
||||
* Matt Mackall <mpm@selenic.com> 12/30/03
|
||||
*
|
||||
* How SLOB works:
|
||||
*
|
||||
* The core of SLOB is a traditional K&R style heap allocator, with
|
||||
* support for returning aligned objects. The granularity of this
|
||||
* allocator is 8 bytes on x86, though it's perhaps possible to reduce
|
||||
* this to 4 if it's deemed worth the effort. The slob heap is a
|
||||
* singly-linked list of pages from __get_free_page, grown on demand
|
||||
* and allocation from the heap is currently first-fit.
|
||||
*
|
||||
* Above this is an implementation of kmalloc/kfree. Blocks returned
|
||||
* from kmalloc are 8-byte aligned and prepended with a 8-byte header.
|
||||
* If kmalloc is asked for objects of PAGE_SIZE or larger, it calls
|
||||
* __get_free_pages directly so that it can return page-aligned blocks
|
||||
* and keeps a linked list of such pages and their orders. These
|
||||
* objects are detected in kfree() by their page alignment.
|
||||
*
|
||||
* SLAB is emulated on top of SLOB by simply calling constructors and
|
||||
* destructors for every SLAB allocation. Objects are returned with
|
||||
* the 8-byte alignment unless the SLAB_MUST_HWCACHE_ALIGN flag is
|
||||
* set, in which case the low-level allocator will fragment blocks to
|
||||
* create the proper alignment. Again, objects of page-size or greater
|
||||
* are allocated by calling __get_free_pages. As SLAB objects know
|
||||
* their size, no separate size bookkeeping is necessary and there is
|
||||
* essentially no allocation space overhead.
|
||||
*/
|
||||
|
||||
|
||||
//some helper
|
||||
#define spin_lock_irqsave(l, f) local_intr_save(f)
|
||||
#define spin_unlock_irqrestore(l, f) local_intr_restore(f)
|
||||
typedef unsigned int gfp_t;
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE PGSIZE
|
||||
#endif
|
||||
|
||||
#ifndef L1_CACHE_BYTES
|
||||
#define L1_CACHE_BYTES 64
|
||||
#endif
|
||||
|
||||
#ifndef ALIGN
|
||||
#define ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1)))
|
||||
#endif
|
||||
|
||||
|
||||
struct slob_block {
|
||||
int units;
|
||||
struct slob_block *next;
|
||||
};
|
||||
typedef struct slob_block slob_t;
|
||||
|
||||
#define SLOB_UNIT sizeof(slob_t)
|
||||
#define SLOB_UNITS(size) (((size) + SLOB_UNIT - 1)/SLOB_UNIT)
|
||||
#define SLOB_ALIGN L1_CACHE_BYTES
|
||||
|
||||
struct bigblock {
|
||||
int order;
|
||||
void *pages;
|
||||
struct bigblock *next;
|
||||
};
|
||||
typedef struct bigblock bigblock_t;
|
||||
|
||||
static slob_t arena = { .next = &arena, .units = 1 };
|
||||
static slob_t *slobfree = &arena;
|
||||
static bigblock_t *bigblocks;
|
||||
|
||||
|
||||
static void* __slob_get_free_pages(gfp_t gfp, int order)
|
||||
{
|
||||
struct Page * page = alloc_pages(1 << order);
|
||||
if(!page)
|
||||
return NULL;
|
||||
return page2kva(page);
|
||||
}
|
||||
|
||||
#define __slob_get_free_page(gfp) __slob_get_free_pages(gfp, 0)
|
||||
|
||||
static inline void __slob_free_pages(unsigned long kva, int order)
|
||||
{
|
||||
free_pages(kva2page(kva), 1 << order);
|
||||
}
|
||||
|
||||
static void slob_free(void *b, int size);
|
||||
|
||||
static void *slob_alloc(size_t size, gfp_t gfp, int align)
|
||||
{
|
||||
assert( (size + SLOB_UNIT) < PAGE_SIZE );
|
||||
|
||||
slob_t *prev, *cur, *aligned = 0;
|
||||
int delta = 0, units = SLOB_UNITS(size);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&slob_lock, flags);
|
||||
prev = slobfree;
|
||||
for (cur = prev->next; ; prev = cur, cur = cur->next) {
|
||||
if (align) {
|
||||
aligned = (slob_t *)ALIGN((unsigned long)cur, align);
|
||||
delta = aligned - cur;
|
||||
}
|
||||
if (cur->units >= units + delta) { /* room enough? */
|
||||
if (delta) { /* need to fragment head to align? */
|
||||
aligned->units = cur->units - delta;
|
||||
aligned->next = cur->next;
|
||||
cur->next = aligned;
|
||||
cur->units = delta;
|
||||
prev = cur;
|
||||
cur = aligned;
|
||||
}
|
||||
|
||||
if (cur->units == units) /* exact fit? */
|
||||
prev->next = cur->next; /* unlink */
|
||||
else { /* fragment */
|
||||
prev->next = cur + units;
|
||||
prev->next->units = cur->units - units;
|
||||
prev->next->next = cur->next;
|
||||
cur->units = units;
|
||||
}
|
||||
|
||||
slobfree = prev;
|
||||
spin_unlock_irqrestore(&slob_lock, flags);
|
||||
return cur;
|
||||
}
|
||||
if (cur == slobfree) {
|
||||
spin_unlock_irqrestore(&slob_lock, flags);
|
||||
|
||||
if (size == PAGE_SIZE) /* trying to shrink arena? */
|
||||
return 0;
|
||||
|
||||
cur = (slob_t *)__slob_get_free_page(gfp);
|
||||
if (!cur)
|
||||
return 0;
|
||||
|
||||
slob_free(cur, PAGE_SIZE);
|
||||
spin_lock_irqsave(&slob_lock, flags);
|
||||
cur = slobfree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void slob_free(void *block, int size)
|
||||
{
|
||||
slob_t *cur, *b = (slob_t *)block;
|
||||
unsigned long flags;
|
||||
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
if (size)
|
||||
b->units = SLOB_UNITS(size);
|
||||
|
||||
/* Find reinsertion point */
|
||||
spin_lock_irqsave(&slob_lock, flags);
|
||||
for (cur = slobfree; !(b > cur && b < cur->next); cur = cur->next)
|
||||
if (cur >= cur->next && (b > cur || b < cur->next))
|
||||
break;
|
||||
|
||||
if (b + b->units == cur->next) {
|
||||
b->units += cur->next->units;
|
||||
b->next = cur->next->next;
|
||||
} else
|
||||
b->next = cur->next;
|
||||
|
||||
if (cur + cur->units == b) {
|
||||
cur->units += b->units;
|
||||
cur->next = b->next;
|
||||
} else
|
||||
cur->next = b;
|
||||
|
||||
slobfree = cur;
|
||||
|
||||
spin_unlock_irqrestore(&slob_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_slab(void) {
|
||||
cprintf("check_slab() success\n");
|
||||
}
|
||||
|
||||
void
|
||||
slab_init(void) {
|
||||
cprintf("use SLOB allocator\n");
|
||||
check_slab();
|
||||
}
|
||||
|
||||
inline void
|
||||
kmalloc_init(void) {
|
||||
slab_init();
|
||||
cprintf("kmalloc_init() succeeded!\n");
|
||||
}
|
||||
|
||||
size_t
|
||||
slab_allocated(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
kallocated(void) {
|
||||
return slab_allocated();
|
||||
}
|
||||
|
||||
static int find_order(int size)
|
||||
{
|
||||
int order = 0;
|
||||
for ( ; size > 4096 ; size >>=1)
|
||||
order++;
|
||||
return order;
|
||||
}
|
||||
|
||||
static void *__kmalloc(size_t size, gfp_t gfp)
|
||||
{
|
||||
slob_t *m;
|
||||
bigblock_t *bb;
|
||||
unsigned long flags;
|
||||
|
||||
if (size < PAGE_SIZE - SLOB_UNIT) {
|
||||
m = slob_alloc(size + SLOB_UNIT, gfp, 0);
|
||||
return m ? (void *)(m + 1) : 0;
|
||||
}
|
||||
|
||||
bb = slob_alloc(sizeof(bigblock_t), gfp, 0);
|
||||
if (!bb)
|
||||
return 0;
|
||||
|
||||
bb->order = find_order(size);
|
||||
bb->pages = (void *)__slob_get_free_pages(gfp, bb->order);
|
||||
|
||||
if (bb->pages) {
|
||||
spin_lock_irqsave(&block_lock, flags);
|
||||
bb->next = bigblocks;
|
||||
bigblocks = bb;
|
||||
spin_unlock_irqrestore(&block_lock, flags);
|
||||
return bb->pages;
|
||||
}
|
||||
|
||||
slob_free(bb, sizeof(bigblock_t));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *
|
||||
kmalloc(size_t size)
|
||||
{
|
||||
return __kmalloc(size, 0);
|
||||
}
|
||||
|
||||
|
||||
void kfree(void *block)
|
||||
{
|
||||
bigblock_t *bb, **last = &bigblocks;
|
||||
unsigned long flags;
|
||||
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
if (!((unsigned long)block & (PAGE_SIZE-1))) {
|
||||
/* might be on the big block list */
|
||||
spin_lock_irqsave(&block_lock, flags);
|
||||
for (bb = bigblocks; bb; last = &bb->next, bb = bb->next) {
|
||||
if (bb->pages == block) {
|
||||
*last = bb->next;
|
||||
spin_unlock_irqrestore(&block_lock, flags);
|
||||
__slob_free_pages((unsigned long)block, bb->order);
|
||||
slob_free(bb, sizeof(bigblock_t));
|
||||
return;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&block_lock, flags);
|
||||
}
|
||||
|
||||
slob_free((slob_t *)block - 1, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
unsigned int ksize(const void *block)
|
||||
{
|
||||
bigblock_t *bb;
|
||||
unsigned long flags;
|
||||
|
||||
if (!block)
|
||||
return 0;
|
||||
|
||||
if (!((unsigned long)block & (PAGE_SIZE-1))) {
|
||||
spin_lock_irqsave(&block_lock, flags);
|
||||
for (bb = bigblocks; bb; bb = bb->next)
|
||||
if (bb->pages == block) {
|
||||
spin_unlock_irqrestore(&slob_lock, flags);
|
||||
return PAGE_SIZE << bb->order;
|
||||
}
|
||||
spin_unlock_irqrestore(&block_lock, flags);
|
||||
}
|
||||
|
||||
return ((slob_t *)block - 1)->units * SLOB_UNIT;
|
||||
}
|
||||
|
||||
|
||||
|
16
related_info/lab5/lab5-spoc-discuss/kern/mm/kmalloc.h
Normal file
16
related_info/lab5/lab5-spoc-discuss/kern/mm/kmalloc.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __KERN_MM_SLAB_H__
|
||||
#define __KERN_MM_SLAB_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
#define KMALLOC_MAX_ORDER 10
|
||||
|
||||
void kmalloc_init(void);
|
||||
|
||||
void *kmalloc(size_t n);
|
||||
void kfree(void *objp);
|
||||
|
||||
size_t kallocated(void);
|
||||
|
||||
#endif /* !__KERN_MM_SLAB_H__ */
|
||||
|
169
related_info/lab5/lab5-spoc-discuss/kern/mm/memlayout.h
Normal file
169
related_info/lab5/lab5-spoc-discuss/kern/mm/memlayout.h
Normal file
@ -0,0 +1,169 @@
|
||||
#ifndef __KERN_MM_MEMLAYOUT_H__
|
||||
#define __KERN_MM_MEMLAYOUT_H__
|
||||
|
||||
/* This file contains the definitions for memory management in our OS. */
|
||||
|
||||
/* global segment number */
|
||||
#define SEG_KTEXT 1
|
||||
#define SEG_KDATA 2
|
||||
#define SEG_UTEXT 3
|
||||
#define SEG_UDATA 4
|
||||
#define SEG_TSS 5
|
||||
|
||||
/* global descrptor numbers */
|
||||
#define GD_KTEXT ((SEG_KTEXT) << 3) // kernel text
|
||||
#define GD_KDATA ((SEG_KDATA) << 3) // kernel data
|
||||
#define GD_UTEXT ((SEG_UTEXT) << 3) // user text
|
||||
#define GD_UDATA ((SEG_UDATA) << 3) // user data
|
||||
#define GD_TSS ((SEG_TSS) << 3) // task segment selector
|
||||
|
||||
#define DPL_KERNEL (0)
|
||||
#define DPL_USER (3)
|
||||
|
||||
#define KERNEL_CS ((GD_KTEXT) | DPL_KERNEL)
|
||||
#define KERNEL_DS ((GD_KDATA) | DPL_KERNEL)
|
||||
#define USER_CS ((GD_UTEXT) | DPL_USER)
|
||||
#define USER_DS ((GD_UDATA) | DPL_USER)
|
||||
|
||||
/* *
|
||||
* Virtual memory map: Permissions
|
||||
* kernel/user
|
||||
*
|
||||
* 4G ------------------> +---------------------------------+
|
||||
* | |
|
||||
* | Empty Memory (*) |
|
||||
* | |
|
||||
* +---------------------------------+ 0xFB000000
|
||||
* | Cur. Page Table (Kern, RW) | RW/-- PTSIZE
|
||||
* VPT -----------------> +---------------------------------+ 0xFAC00000
|
||||
* | Invalid Memory (*) | --/--
|
||||
* KERNTOP -------------> +---------------------------------+ 0xF8000000
|
||||
* | |
|
||||
* | Remapped Physical Memory | RW/-- KMEMSIZE
|
||||
* | |
|
||||
* KERNBASE ------------> +---------------------------------+ 0xC0000000
|
||||
* | Invalid Memory (*) | --/--
|
||||
* USERTOP -------------> +---------------------------------+ 0xB0000000
|
||||
* | User stack |
|
||||
* +---------------------------------+
|
||||
* | |
|
||||
* : :
|
||||
* | ~~~~~~~~~~~~~~~~ |
|
||||
* : :
|
||||
* | |
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* | User Program & Heap |
|
||||
* UTEXT ---------------> +---------------------------------+ 0x00800000
|
||||
* | Invalid Memory (*) | --/--
|
||||
* | - - - - - - - - - - - - - - - |
|
||||
* | User STAB Data (optional) |
|
||||
* USERBASE, USTAB------> +---------------------------------+ 0x00200000
|
||||
* | Invalid Memory (*) | --/--
|
||||
* 0 -------------------> +---------------------------------+ 0x00000000
|
||||
* (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped.
|
||||
* "Empty Memory" is normally unmapped, but user programs may map pages
|
||||
* there if desired.
|
||||
*
|
||||
* */
|
||||
|
||||
/* All physical memory mapped at this address */
|
||||
#define KERNBASE 0xC0000000
|
||||
#define KMEMSIZE 0x38000000 // the maximum amount of physical memory
|
||||
#define KERNTOP (KERNBASE + KMEMSIZE)
|
||||
|
||||
/* *
|
||||
* Virtual page table. Entry PDX[VPT] in the PD (Page Directory) contains
|
||||
* a pointer to the page directory itself, thereby turning the PD into a page
|
||||
* table, which maps all the PTEs (Page Table Entry) containing the page mappings
|
||||
* for the entire virtual address space into that 4 Meg region starting at VPT.
|
||||
* */
|
||||
#define VPT 0xFAC00000
|
||||
|
||||
#define KSTACKPAGE 2 // # of pages in kernel stack
|
||||
#define KSTACKSIZE (KSTACKPAGE * PGSIZE) // sizeof kernel stack
|
||||
|
||||
#define USERTOP 0xB0000000
|
||||
#define USTACKTOP USERTOP
|
||||
#define USTACKPAGE 256 // # of pages in user stack
|
||||
#define USTACKSIZE (USTACKPAGE * PGSIZE) // sizeof user stack
|
||||
|
||||
#define USERBASE 0x00200000
|
||||
#define UTEXT 0x00800000 // where user programs generally begin
|
||||
#define USTAB USERBASE // the location of the user STABS data structure
|
||||
|
||||
#define USER_ACCESS(start, end) \
|
||||
(USERBASE <= (start) && (start) < (end) && (end) <= USERTOP)
|
||||
|
||||
#define KERN_ACCESS(start, end) \
|
||||
(KERNBASE <= (start) && (start) < (end) && (end) <= KERNTOP)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <defs.h>
|
||||
#include <atomic.h>
|
||||
#include <list.h>
|
||||
|
||||
typedef uintptr_t pte_t;
|
||||
typedef uintptr_t pde_t;
|
||||
typedef pte_t swap_entry_t; //the pte can also be a swap entry
|
||||
|
||||
// some constants for bios interrupt 15h AX = 0xE820
|
||||
#define E820MAX 20 // number of entries in E820MAP
|
||||
#define E820_ARM 1 // address range memory
|
||||
#define E820_ARR 2 // address range reserved
|
||||
|
||||
struct e820map {
|
||||
int nr_map;
|
||||
struct {
|
||||
uint64_t addr;
|
||||
uint64_t size;
|
||||
uint32_t type;
|
||||
} __attribute__((packed)) map[E820MAX];
|
||||
};
|
||||
|
||||
/* *
|
||||
* struct Page - Page descriptor structures. Each Page describes one
|
||||
* physical page. In kern/mm/pmm.h, you can find lots of useful functions
|
||||
* that convert Page to other data types, such as phyical address.
|
||||
* */
|
||||
struct Page {
|
||||
int ref; // page frame's reference counter
|
||||
uint32_t flags; // array of flags that describe the status of the page frame
|
||||
unsigned int property; // used in buddy system, stores the order (the X in 2^X) of the continuous memory block
|
||||
int zone_num; // used in buddy system, the No. of zone which the page belongs to
|
||||
list_entry_t page_link; // free list link
|
||||
list_entry_t pra_page_link; // used for pra (page replace algorithm)
|
||||
uintptr_t pra_vaddr; // used for pra (page replace algorithm)
|
||||
};
|
||||
|
||||
/* Flags describing the status of a page frame */
|
||||
#define PG_reserved 0 // the page descriptor is reserved for kernel or unusable
|
||||
#define PG_property 1 // the member 'property' is valid
|
||||
|
||||
#define SetPageReserved(page) set_bit(PG_reserved, &((page)->flags))
|
||||
#define ClearPageReserved(page) clear_bit(PG_reserved, &((page)->flags))
|
||||
#define PageReserved(page) test_bit(PG_reserved, &((page)->flags))
|
||||
#define SetPageProperty(page) set_bit(PG_property, &((page)->flags))
|
||||
#define ClearPageProperty(page) clear_bit(PG_property, &((page)->flags))
|
||||
#define PageProperty(page) test_bit(PG_property, &((page)->flags))
|
||||
|
||||
// convert list entry to page
|
||||
#define le2page(le, member) \
|
||||
to_struct((le), struct Page, member)
|
||||
|
||||
/* free_area_t - maintains a doubly linked list to record free (unused) pages */
|
||||
typedef struct {
|
||||
list_entry_t free_list; // the list header
|
||||
unsigned int nr_free; // # of free pages in this free list
|
||||
} free_area_t;
|
||||
|
||||
/* for slab style kmalloc */
|
||||
#define PG_slab 2 // page frame is included in a slab
|
||||
#define SetPageSlab(page) set_bit(PG_slab, &((page)->flags))
|
||||
#define ClearPageSlab(page) clear_bit(PG_slab, &((page)->flags))
|
||||
#define PageSlab(page) test_bit(PG_slab, &((page)->flags))
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
#endif /* !__KERN_MM_MEMLAYOUT_H__ */
|
||||
|
272
related_info/lab5/lab5-spoc-discuss/kern/mm/mmu.h
Normal file
272
related_info/lab5/lab5-spoc-discuss/kern/mm/mmu.h
Normal file
@ -0,0 +1,272 @@
|
||||
#ifndef __KERN_MM_MMU_H__
|
||||
#define __KERN_MM_MMU_H__
|
||||
|
||||
/* Eflags register */
|
||||
#define FL_CF 0x00000001 // Carry Flag
|
||||
#define FL_PF 0x00000004 // Parity Flag
|
||||
#define FL_AF 0x00000010 // Auxiliary carry Flag
|
||||
#define FL_ZF 0x00000040 // Zero Flag
|
||||
#define FL_SF 0x00000080 // Sign Flag
|
||||
#define FL_TF 0x00000100 // Trap Flag
|
||||
#define FL_IF 0x00000200 // Interrupt Flag
|
||||
#define FL_DF 0x00000400 // Direction Flag
|
||||
#define FL_OF 0x00000800 // Overflow Flag
|
||||
#define FL_IOPL_MASK 0x00003000 // I/O Privilege Level bitmask
|
||||
#define FL_IOPL_0 0x00000000 // IOPL == 0
|
||||
#define FL_IOPL_1 0x00001000 // IOPL == 1
|
||||
#define FL_IOPL_2 0x00002000 // IOPL == 2
|
||||
#define FL_IOPL_3 0x00003000 // IOPL == 3
|
||||
#define FL_NT 0x00004000 // Nested Task
|
||||
#define FL_RF 0x00010000 // Resume Flag
|
||||
#define FL_VM 0x00020000 // Virtual 8086 mode
|
||||
#define FL_AC 0x00040000 // Alignment Check
|
||||
#define FL_VIF 0x00080000 // Virtual Interrupt Flag
|
||||
#define FL_VIP 0x00100000 // Virtual Interrupt Pending
|
||||
#define FL_ID 0x00200000 // ID flag
|
||||
|
||||
/* Application segment type bits */
|
||||
#define STA_X 0x8 // Executable segment
|
||||
#define STA_E 0x4 // Expand down (non-executable segments)
|
||||
#define STA_C 0x4 // Conforming code segment (executable only)
|
||||
#define STA_W 0x2 // Writeable (non-executable segments)
|
||||
#define STA_R 0x2 // Readable (executable segments)
|
||||
#define STA_A 0x1 // Accessed
|
||||
|
||||
/* System segment type bits */
|
||||
#define STS_T16A 0x1 // Available 16-bit TSS
|
||||
#define STS_LDT 0x2 // Local Descriptor Table
|
||||
#define STS_T16B 0x3 // Busy 16-bit TSS
|
||||
#define STS_CG16 0x4 // 16-bit Call Gate
|
||||
#define STS_TG 0x5 // Task Gate / Coum Transmitions
|
||||
#define STS_IG16 0x6 // 16-bit Interrupt Gate
|
||||
#define STS_TG16 0x7 // 16-bit Trap Gate
|
||||
#define STS_T32A 0x9 // Available 32-bit TSS
|
||||
#define STS_T32B 0xB // Busy 32-bit TSS
|
||||
#define STS_CG32 0xC // 32-bit Call Gate
|
||||
#define STS_IG32 0xE // 32-bit Interrupt Gate
|
||||
#define STS_TG32 0xF // 32-bit Trap Gate
|
||||
|
||||
#ifdef __ASSEMBLER__
|
||||
|
||||
#define SEG_NULL \
|
||||
.word 0, 0; \
|
||||
.byte 0, 0, 0, 0
|
||||
|
||||
#define SEG_ASM(type,base,lim) \
|
||||
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
|
||||
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
|
||||
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)
|
||||
|
||||
#else /* not __ASSEMBLER__ */
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
/* Gate descriptors for interrupts and traps */
|
||||
struct gatedesc {
|
||||
unsigned gd_off_15_0 : 16; // low 16 bits of offset in segment
|
||||
unsigned gd_ss : 16; // segment selector
|
||||
unsigned gd_args : 5; // # args, 0 for interrupt/trap gates
|
||||
unsigned gd_rsv1 : 3; // reserved(should be zero I guess)
|
||||
unsigned gd_type : 4; // type(STS_{TG,IG32,TG32})
|
||||
unsigned gd_s : 1; // must be 0 (system)
|
||||
unsigned gd_dpl : 2; // descriptor(meaning new) privilege level
|
||||
unsigned gd_p : 1; // Present
|
||||
unsigned gd_off_31_16 : 16; // high bits of offset in segment
|
||||
};
|
||||
|
||||
/* *
|
||||
* Set up a normal interrupt/trap gate descriptor
|
||||
* - istrap: 1 for a trap (= exception) gate, 0 for an interrupt gate
|
||||
* - sel: Code segment selector for interrupt/trap handler
|
||||
* - off: Offset in code segment for interrupt/trap handler
|
||||
* - dpl: Descriptor Privilege Level - the privilege level required
|
||||
* for software to invoke this interrupt/trap gate explicitly
|
||||
* using an int instruction.
|
||||
* */
|
||||
#define SETGATE(gate, istrap, sel, off, dpl) { \
|
||||
(gate).gd_off_15_0 = (uint32_t)(off) & 0xffff; \
|
||||
(gate).gd_ss = (sel); \
|
||||
(gate).gd_args = 0; \
|
||||
(gate).gd_rsv1 = 0; \
|
||||
(gate).gd_type = (istrap) ? STS_TG32 : STS_IG32; \
|
||||
(gate).gd_s = 0; \
|
||||
(gate).gd_dpl = (dpl); \
|
||||
(gate).gd_p = 1; \
|
||||
(gate).gd_off_31_16 = (uint32_t)(off) >> 16; \
|
||||
}
|
||||
|
||||
/* Set up a call gate descriptor */
|
||||
#define SETCALLGATE(gate, ss, off, dpl) { \
|
||||
(gate).gd_off_15_0 = (uint32_t)(off) & 0xffff; \
|
||||
(gate).gd_ss = (ss); \
|
||||
(gate).gd_args = 0; \
|
||||
(gate).gd_rsv1 = 0; \
|
||||
(gate).gd_type = STS_CG32; \
|
||||
(gate).gd_s = 0; \
|
||||
(gate).gd_dpl = (dpl); \
|
||||
(gate).gd_p = 1; \
|
||||
(gate).gd_off_31_16 = (uint32_t)(off) >> 16; \
|
||||
}
|
||||
|
||||
/* segment descriptors */
|
||||
struct segdesc {
|
||||
unsigned sd_lim_15_0 : 16; // low bits of segment limit
|
||||
unsigned sd_base_15_0 : 16; // low bits of segment base address
|
||||
unsigned sd_base_23_16 : 8; // middle bits of segment base address
|
||||
unsigned sd_type : 4; // segment type (see STS_ constants)
|
||||
unsigned sd_s : 1; // 0 = system, 1 = application
|
||||
unsigned sd_dpl : 2; // descriptor Privilege Level
|
||||
unsigned sd_p : 1; // present
|
||||
unsigned sd_lim_19_16 : 4; // high bits of segment limit
|
||||
unsigned sd_avl : 1; // unused (available for software use)
|
||||
unsigned sd_rsv1 : 1; // reserved
|
||||
unsigned sd_db : 1; // 0 = 16-bit segment, 1 = 32-bit segment
|
||||
unsigned sd_g : 1; // granularity: limit scaled by 4K when set
|
||||
unsigned sd_base_31_24 : 8; // high bits of segment base address
|
||||
};
|
||||
|
||||
#define SEG_NULL \
|
||||
(struct segdesc) {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
#define SEG(type, base, lim, dpl) \
|
||||
(struct segdesc) { \
|
||||
((lim) >> 12) & 0xffff, (base) & 0xffff, \
|
||||
((base) >> 16) & 0xff, type, 1, dpl, 1, \
|
||||
(unsigned)(lim) >> 28, 0, 0, 1, 1, \
|
||||
(unsigned) (base) >> 24 \
|
||||
}
|
||||
|
||||
#define SEGTSS(type, base, lim, dpl) \
|
||||
(struct segdesc) { \
|
||||
(lim) & 0xffff, (base) & 0xffff, \
|
||||
((base) >> 16) & 0xff, type, 0, dpl, 1, \
|
||||
(unsigned) (lim) >> 16, 0, 0, 1, 0, \
|
||||
(unsigned) (base) >> 24 \
|
||||
}
|
||||
|
||||
/* task state segment format (as described by the Pentium architecture book) */
|
||||
struct taskstate {
|
||||
uint32_t ts_link; // old ts selector
|
||||
uintptr_t ts_esp0; // stack pointers and segment selectors
|
||||
uint16_t ts_ss0; // after an increase in privilege level
|
||||
uint16_t ts_padding1;
|
||||
uintptr_t ts_esp1;
|
||||
uint16_t ts_ss1;
|
||||
uint16_t ts_padding2;
|
||||
uintptr_t ts_esp2;
|
||||
uint16_t ts_ss2;
|
||||
uint16_t ts_padding3;
|
||||
uintptr_t ts_cr3; // page directory base
|
||||
uintptr_t ts_eip; // saved state from last task switch
|
||||
uint32_t ts_eflags;
|
||||
uint32_t ts_eax; // more saved state (registers)
|
||||
uint32_t ts_ecx;
|
||||
uint32_t ts_edx;
|
||||
uint32_t ts_ebx;
|
||||
uintptr_t ts_esp;
|
||||
uintptr_t ts_ebp;
|
||||
uint32_t ts_esi;
|
||||
uint32_t ts_edi;
|
||||
uint16_t ts_es; // even more saved state (segment selectors)
|
||||
uint16_t ts_padding4;
|
||||
uint16_t ts_cs;
|
||||
uint16_t ts_padding5;
|
||||
uint16_t ts_ss;
|
||||
uint16_t ts_padding6;
|
||||
uint16_t ts_ds;
|
||||
uint16_t ts_padding7;
|
||||
uint16_t ts_fs;
|
||||
uint16_t ts_padding8;
|
||||
uint16_t ts_gs;
|
||||
uint16_t ts_padding9;
|
||||
uint16_t ts_ldt;
|
||||
uint16_t ts_padding10;
|
||||
uint16_t ts_t; // trap on task switch
|
||||
uint16_t ts_iomb; // i/o map base address
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
// A linear address 'la' has a three-part structure as follows:
|
||||
//
|
||||
// +--------10------+-------10-------+---------12----------+
|
||||
// | Page Directory | Page Table | Offset within Page |
|
||||
// | Index | Index | |
|
||||
// +----------------+----------------+---------------------+
|
||||
// \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/
|
||||
// \----------- PPN(la) -----------/
|
||||
//
|
||||
// The PDX, PTX, PGOFF, and PPN macros decompose linear addresses as shown.
|
||||
// To construct a linear address la from PDX(la), PTX(la), and PGOFF(la),
|
||||
// use PGADDR(PDX(la), PTX(la), PGOFF(la)).
|
||||
|
||||
// page directory index
|
||||
#define PDX(la) ((((uintptr_t)(la)) >> PDXSHIFT) & 0x3FF)
|
||||
|
||||
// page table index
|
||||
#define PTX(la) ((((uintptr_t)(la)) >> PTXSHIFT) & 0x3FF)
|
||||
|
||||
// page number field of address
|
||||
#define PPN(la) (((uintptr_t)(la)) >> PTXSHIFT)
|
||||
|
||||
// offset in page
|
||||
#define PGOFF(la) (((uintptr_t)(la)) & 0xFFF)
|
||||
|
||||
// construct linear address from indexes and offset
|
||||
#define PGADDR(d, t, o) ((uintptr_t)((d) << PDXSHIFT | (t) << PTXSHIFT | (o)))
|
||||
|
||||
// address in page table or page directory entry
|
||||
#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF)
|
||||
#define PDE_ADDR(pde) PTE_ADDR(pde)
|
||||
|
||||
/* page directory and page table constants */
|
||||
#define NPDEENTRY 1024 // page directory entries per page directory
|
||||
#define NPTEENTRY 1024 // page table entries per page table
|
||||
|
||||
#define PGSIZE 4096 // bytes mapped by a page
|
||||
#define PGSHIFT 12 // log2(PGSIZE)
|
||||
#define PTSIZE (PGSIZE * NPTEENTRY) // bytes mapped by a page directory entry
|
||||
#define PTSHIFT 22 // log2(PTSIZE)
|
||||
|
||||
#define PTXSHIFT 12 // offset of PTX in a linear address
|
||||
#define PDXSHIFT 22 // offset of PDX in a linear address
|
||||
|
||||
/* page table/directory entry flags */
|
||||
#define PTE_P 0x001 // Present
|
||||
#define PTE_W 0x002 // Writeable
|
||||
#define PTE_U 0x004 // User
|
||||
#define PTE_PWT 0x008 // Write-Through
|
||||
#define PTE_PCD 0x010 // Cache-Disable
|
||||
#define PTE_A 0x020 // Accessed
|
||||
#define PTE_D 0x040 // Dirty
|
||||
#define PTE_PS 0x080 // Page Size
|
||||
#define PTE_MBZ 0x180 // Bits must be zero
|
||||
#define PTE_AVAIL 0xE00 // Available for software use
|
||||
// The PTE_AVAIL bits aren't used by the kernel or interpreted by the
|
||||
// hardware, so user processes are allowed to set them arbitrarily.
|
||||
|
||||
#define PTE_USER (PTE_U | PTE_W | PTE_P)
|
||||
|
||||
/* Control Register flags */
|
||||
#define CR0_PE 0x00000001 // Protection Enable
|
||||
#define CR0_MP 0x00000002 // Monitor coProcessor
|
||||
#define CR0_EM 0x00000004 // Emulation
|
||||
#define CR0_TS 0x00000008 // Task Switched
|
||||
#define CR0_ET 0x00000010 // Extension Type
|
||||
#define CR0_NE 0x00000020 // Numeric Errror
|
||||
#define CR0_WP 0x00010000 // Write Protect
|
||||
#define CR0_AM 0x00040000 // Alignment Mask
|
||||
#define CR0_NW 0x20000000 // Not Writethrough
|
||||
#define CR0_CD 0x40000000 // Cache Disable
|
||||
#define CR0_PG 0x80000000 // Paging
|
||||
|
||||
#define CR4_PCE 0x00000100 // Performance counter enable
|
||||
#define CR4_MCE 0x00000040 // Machine Check Enable
|
||||
#define CR4_PSE 0x00000010 // Page Size Extensions
|
||||
#define CR4_DE 0x00000008 // Debugging Extensions
|
||||
#define CR4_TSD 0x00000004 // Time Stamp Disable
|
||||
#define CR4_PVI 0x00000002 // Protected-Mode Virtual Interrupts
|
||||
#define CR4_VME 0x00000001 // V86 Mode Extensions
|
||||
|
||||
#endif /* !__KERN_MM_MMU_H__ */
|
||||
|
785
related_info/lab5/lab5-spoc-discuss/kern/mm/pmm.c
Normal file
785
related_info/lab5/lab5-spoc-discuss/kern/mm/pmm.c
Normal file
@ -0,0 +1,785 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <mmu.h>
|
||||
#include <memlayout.h>
|
||||
#include <pmm.h>
|
||||
#include <default_pmm.h>
|
||||
#include <sync.h>
|
||||
#include <error.h>
|
||||
#include <swap.h>
|
||||
#include <vmm.h>
|
||||
#include <kmalloc.h>
|
||||
|
||||
/* *
|
||||
* Task State Segment:
|
||||
*
|
||||
* The TSS may reside anywhere in memory. A special segment register called
|
||||
* the Task Register (TR) holds a segment selector that points a valid TSS
|
||||
* segment descriptor which resides in the GDT. Therefore, to use a TSS
|
||||
* the following must be done in function gdt_init:
|
||||
* - create a TSS descriptor entry in GDT
|
||||
* - add enough information to the TSS in memory as needed
|
||||
* - load the TR register with a segment selector for that segment
|
||||
*
|
||||
* There are several fileds in TSS for specifying the new stack pointer when a
|
||||
* privilege level change happens. But only the fields SS0 and ESP0 are useful
|
||||
* in our os kernel.
|
||||
*
|
||||
* The field SS0 contains the stack segment selector for CPL = 0, and the ESP0
|
||||
* contains the new ESP value for CPL = 0. When an interrupt happens in protected
|
||||
* mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their value
|
||||
* into SS and ESP respectively.
|
||||
* */
|
||||
static struct taskstate ts = {0};
|
||||
|
||||
// virtual address of physicall page array
|
||||
struct Page *pages;
|
||||
// amount of physical memory (in pages)
|
||||
size_t npage = 0;
|
||||
|
||||
// virtual address of boot-time page directory
|
||||
pde_t *boot_pgdir = NULL;
|
||||
// physical address of boot-time page directory
|
||||
uintptr_t boot_cr3;
|
||||
|
||||
// physical memory management
|
||||
const struct pmm_manager *pmm_manager;
|
||||
|
||||
/* *
|
||||
* The page directory entry corresponding to the virtual address range
|
||||
* [VPT, VPT + 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 though a "virtual page table" at virtual address VPT. And the
|
||||
* PTE for number n is stored in vpt[n].
|
||||
*
|
||||
* A second consequence is that the contents of the current page directory will
|
||||
* always available at virtual address PGADDR(PDX(VPT), PDX(VPT), 0), to which
|
||||
* vpd is set bellow.
|
||||
* */
|
||||
pte_t * const vpt = (pte_t *)VPT;
|
||||
pde_t * const vpd = (pde_t *)PGADDR(PDX(VPT), PDX(VPT), 0);
|
||||
|
||||
/* *
|
||||
* Global Descriptor Table:
|
||||
*
|
||||
* 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. Defined as follows:
|
||||
* - 0x0 : unused (always faults -- for trapping NULL far pointers)
|
||||
* - 0x8 : kernel code segment
|
||||
* - 0x10: kernel data segment
|
||||
* - 0x18: user code segment
|
||||
* - 0x20: user data segment
|
||||
* - 0x28: defined for tss, initialized in gdt_init
|
||||
* */
|
||||
static struct segdesc gdt[] = {
|
||||
SEG_NULL,
|
||||
[SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL),
|
||||
[SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL),
|
||||
[SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER),
|
||||
[SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER),
|
||||
[SEG_TSS] = SEG_NULL,
|
||||
};
|
||||
|
||||
static struct pseudodesc gdt_pd = {
|
||||
sizeof(gdt) - 1, (uintptr_t)gdt
|
||||
};
|
||||
|
||||
static void check_alloc_page(void);
|
||||
static void check_pgdir(void);
|
||||
static void check_boot_pgdir(void);
|
||||
|
||||
/* *
|
||||
* lgdt - load the global descriptor table register and reset the
|
||||
* data/code segement registers for kernel.
|
||||
* */
|
||||
static inline void
|
||||
lgdt(struct pseudodesc *pd) {
|
||||
asm volatile ("lgdt (%0)" :: "r" (pd));
|
||||
asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS));
|
||||
asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));
|
||||
asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS));
|
||||
asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS));
|
||||
asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS));
|
||||
// reload cs
|
||||
asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS));
|
||||
}
|
||||
|
||||
/* *
|
||||
* load_esp0 - change the ESP0 in default task state segment,
|
||||
* so that we can use different kernel stack when we trap frame
|
||||
* user to kernel.
|
||||
* */
|
||||
void
|
||||
load_esp0(uintptr_t esp0) {
|
||||
ts.ts_esp0 = esp0;
|
||||
}
|
||||
|
||||
/* gdt_init - initialize the default GDT and TSS */
|
||||
static void
|
||||
gdt_init(void) {
|
||||
// set boot kernel stack and default SS0
|
||||
load_esp0((uintptr_t)bootstacktop);
|
||||
ts.ts_ss0 = KERNEL_DS;
|
||||
|
||||
// initialize the TSS filed of the gdt
|
||||
gdt[SEG_TSS] = SEGTSS(STS_T32A, (uintptr_t)&ts, sizeof(ts), DPL_KERNEL);
|
||||
|
||||
// reload all segment registers
|
||||
lgdt(&gdt_pd);
|
||||
|
||||
// load the TSS
|
||||
ltr(GD_TSS);
|
||||
}
|
||||
|
||||
//init_pmm_manager - initialize a pmm_manager instance
|
||||
static void
|
||||
init_pmm_manager(void) {
|
||||
pmm_manager = &default_pmm_manager;
|
||||
cprintf("memory management: %s\n", pmm_manager->name);
|
||||
pmm_manager->init();
|
||||
}
|
||||
|
||||
//init_memmap - call pmm->init_memmap to build Page struct for free memory
|
||||
static void
|
||||
init_memmap(struct Page *base, size_t n) {
|
||||
pmm_manager->init_memmap(base, n);
|
||||
}
|
||||
|
||||
//alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE memory
|
||||
struct Page *
|
||||
alloc_pages(size_t n) {
|
||||
struct Page *page=NULL;
|
||||
bool intr_flag;
|
||||
|
||||
while (1)
|
||||
{
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
page = pmm_manager->alloc_pages(n);
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
|
||||
if (page != NULL || n > 1 || swap_init_ok == 0) break;
|
||||
|
||||
extern struct mm_struct *check_mm_struct;
|
||||
//cprintf("page %x, call swap_out in alloc_pages %d\n",page, n);
|
||||
swap_out(check_mm_struct, n, 0);
|
||||
}
|
||||
//cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages));
|
||||
return page;
|
||||
}
|
||||
|
||||
//free_pages - call pmm->free_pages to free a continuous n*PAGESIZE memory
|
||||
void
|
||||
free_pages(struct Page *base, size_t n) {
|
||||
bool intr_flag;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
pmm_manager->free_pages(base, n);
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
}
|
||||
|
||||
//nr_free_pages - call pmm->nr_free_pages to get the size (nr*PAGESIZE)
|
||||
//of current free memory
|
||||
size_t
|
||||
nr_free_pages(void) {
|
||||
size_t ret;
|
||||
bool intr_flag;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
ret = pmm_manager->nr_free_pages();
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* pmm_init - initialize the physical memory management */
|
||||
static void
|
||||
page_init(void) {
|
||||
struct e820map *memmap = (struct e820map *)(0x8000 + KERNBASE);
|
||||
uint64_t maxpa = 0;
|
||||
|
||||
cprintf("e820map:\n");
|
||||
int i;
|
||||
for (i = 0; i < memmap->nr_map; i ++) {
|
||||
uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;
|
||||
cprintf(" memory: %08llx, [%08llx, %08llx], type = %d.\n",
|
||||
memmap->map[i].size, begin, end - 1, memmap->map[i].type);
|
||||
if (memmap->map[i].type == E820_ARM) {
|
||||
if (maxpa < end && begin < KMEMSIZE) {
|
||||
maxpa = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxpa > KMEMSIZE) {
|
||||
maxpa = KMEMSIZE;
|
||||
}
|
||||
|
||||
extern char end[];
|
||||
|
||||
npage = maxpa / PGSIZE;
|
||||
pages = (struct Page *)ROUNDUP((void *)end, PGSIZE);
|
||||
|
||||
for (i = 0; i < npage; i ++) {
|
||||
SetPageReserved(pages + i);
|
||||
}
|
||||
|
||||
uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * npage);
|
||||
|
||||
for (i = 0; i < memmap->nr_map; i ++) {
|
||||
uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;
|
||||
if (memmap->map[i].type == E820_ARM) {
|
||||
if (begin < freemem) {
|
||||
begin = freemem;
|
||||
}
|
||||
if (end > KMEMSIZE) {
|
||||
end = KMEMSIZE;
|
||||
}
|
||||
if (begin < end) {
|
||||
begin = ROUNDUP(begin, PGSIZE);
|
||||
end = ROUNDDOWN(end, PGSIZE);
|
||||
if (begin < end) {
|
||||
init_memmap(pa2page(begin), (end - begin) / PGSIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
enable_paging(void) {
|
||||
lcr3(boot_cr3);
|
||||
|
||||
// turn on paging
|
||||
uint32_t cr0 = rcr0();
|
||||
cr0 |= CR0_PE | CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_TS | CR0_EM | CR0_MP;
|
||||
cr0 &= ~(CR0_TS | CR0_EM);
|
||||
lcr0(cr0);
|
||||
}
|
||||
|
||||
//boot_map_segment - setup&enable the paging mechanism
|
||||
// parameters
|
||||
// la: linear address of this memory need to map (after x86 segment map)
|
||||
// size: memory size
|
||||
// pa: physical address of this memory
|
||||
// perm: permission of this memory
|
||||
static void
|
||||
boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, uintptr_t pa, uint32_t perm) {
|
||||
assert(PGOFF(la) == PGOFF(pa));
|
||||
size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE;
|
||||
la = ROUNDDOWN(la, PGSIZE);
|
||||
pa = ROUNDDOWN(pa, PGSIZE);
|
||||
for (; n > 0; n --, la += PGSIZE, pa += PGSIZE) {
|
||||
pte_t *ptep = get_pte(pgdir, la, 1);
|
||||
assert(ptep != NULL);
|
||||
*ptep = pa | PTE_P | perm;
|
||||
}
|
||||
}
|
||||
|
||||
//boot_alloc_page - allocate one page using pmm->alloc_pages(1)
|
||||
// return value: the kernel virtual address of this allocated page
|
||||
//note: this function is used to get the memory for PDT(Page Directory Table)&PT(Page Table)
|
||||
static void *
|
||||
boot_alloc_page(void) {
|
||||
struct Page *p = alloc_page();
|
||||
if (p == NULL) {
|
||||
panic("boot_alloc_page failed.\n");
|
||||
}
|
||||
return page2kva(p);
|
||||
}
|
||||
|
||||
//pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup paging mechanism
|
||||
// - check the correctness of pmm & paging mechanism, print PDT&PT
|
||||
void
|
||||
pmm_init(void) {
|
||||
//We need to alloc/free the physical memory (granularity is 4KB or other size).
|
||||
//So a framework of physical memory manager (struct pmm_manager)is defined in pmm.h
|
||||
//First we should init a physical memory manager(pmm) based on the framework.
|
||||
//Then pmm can alloc/free the physical memory.
|
||||
//Now the first_fit/best_fit/worst_fit/buddy_system pmm are available.
|
||||
init_pmm_manager();
|
||||
|
||||
// detect physical memory space, reserve already used memory,
|
||||
// then use pmm->init_memmap to create free page list
|
||||
page_init();
|
||||
|
||||
//use pmm->check to verify the correctness of the alloc/free function in a pmm
|
||||
check_alloc_page();
|
||||
|
||||
// create boot_pgdir, an initial page directory(Page Directory Table, PDT)
|
||||
boot_pgdir = boot_alloc_page();
|
||||
memset(boot_pgdir, 0, PGSIZE);
|
||||
boot_cr3 = PADDR(boot_pgdir);
|
||||
|
||||
check_pgdir();
|
||||
|
||||
static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0);
|
||||
|
||||
// recursively insert boot_pgdir in itself
|
||||
// to form a virtual page table at virtual address VPT
|
||||
boot_pgdir[PDX(VPT)] = PADDR(boot_pgdir) | PTE_P | PTE_W;
|
||||
|
||||
// map all physical memory to linear memory with base linear addr KERNBASE
|
||||
//linear_addr KERNBASE~KERNBASE+KMEMSIZE = phy_addr 0~KMEMSIZE
|
||||
//But shouldn't use this map until enable_paging() & gdt_init() finished.
|
||||
boot_map_segment(boot_pgdir, KERNBASE, KMEMSIZE, 0, PTE_W);
|
||||
|
||||
//temporary map:
|
||||
//virtual_addr 3G~3G+4M = linear_addr 0~4M = linear_addr 3G~3G+4M = phy_addr 0~4M
|
||||
boot_pgdir[0] = boot_pgdir[PDX(KERNBASE)];
|
||||
|
||||
enable_paging();
|
||||
|
||||
//reload gdt(third time,the last time) to map all physical memory
|
||||
//virtual_addr 0~4G=liear_addr 0~4G
|
||||
//then set kernel stack(ss:esp) in TSS, setup TSS in gdt, load TSS
|
||||
gdt_init();
|
||||
|
||||
//disable the map of virtual_addr 0~4M
|
||||
boot_pgdir[0] = 0;
|
||||
|
||||
//now the basic virtual memory map(see memalyout.h) is established.
|
||||
//check the correctness of the basic virtual memory map.
|
||||
check_boot_pgdir();
|
||||
|
||||
print_pgdir();
|
||||
|
||||
kmalloc_init();
|
||||
|
||||
}
|
||||
|
||||
//get_pte - get pte and return the kernel virtual address of this pte for la
|
||||
// - if the PT contians this pte didn't exist, alloc a page for PT
|
||||
// parameter:
|
||||
// pgdir: the kernel virtual base address of PDT
|
||||
// la: the linear address need to map
|
||||
// create: a logical value to decide if alloc a page for PT
|
||||
// return vaule: the kernel virtual address of this pte
|
||||
pte_t *
|
||||
get_pte(pde_t *pgdir, uintptr_t la, bool create) {
|
||||
/* LAB2 EXERCISE 2: YOUR CODE
|
||||
*
|
||||
* If you need to visit a physical address, please use KADDR()
|
||||
* please read pmm.h for useful macros
|
||||
*
|
||||
* Maybe you want help comment, BELOW comments can help you finish the code
|
||||
*
|
||||
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
|
||||
* MACROs or Functions:
|
||||
* PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la.
|
||||
* KADDR(pa) : takes a physical address and returns the corresponding kernel virtual address.
|
||||
* set_page_ref(page,1) : means the page be referenced by one time
|
||||
* page2pa(page): get the physical address of memory which this (struct Page *) page manages
|
||||
* struct Page * alloc_page() : allocation a page
|
||||
* memset(void *s, char c, size_t n) : sets the first n bytes of the memory area pointed by s
|
||||
* to the specified value c.
|
||||
* DEFINEs:
|
||||
* PTE_P 0x001 // page table/directory entry flags bit : Present
|
||||
* PTE_W 0x002 // page table/directory entry flags bit : Writeable
|
||||
* PTE_U 0x004 // page table/directory entry flags bit : User can access
|
||||
*/
|
||||
#if 0
|
||||
pde_t *pdep = NULL; // (1) find page directory entry
|
||||
if (0) { // (2) check if entry is not present
|
||||
// (3) check if creating is needed, then alloc page for page table
|
||||
// CAUTION: this page is used for page table, not for common data page
|
||||
// (4) set page reference
|
||||
uintptr_t pa = 0; // (5) get linear address of page
|
||||
// (6) clear page content using memset
|
||||
// (7) set page directory entry's permission
|
||||
}
|
||||
return NULL; // (8) return page table entry
|
||||
#endif
|
||||
pde_t *pdep = &pgdir[PDX(la)];
|
||||
if (!(*pdep & PTE_P)) {
|
||||
struct Page *page;
|
||||
if (!create || (page = alloc_page()) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
set_page_ref(page, 1);
|
||||
uintptr_t pa = page2pa(page);
|
||||
memset(KADDR(pa), 0, PGSIZE);
|
||||
*pdep = pa | PTE_U | PTE_W | PTE_P;
|
||||
}
|
||||
return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)];
|
||||
}
|
||||
|
||||
//get_page - get related Page struct for linear address la using PDT pgdir
|
||||
struct Page *
|
||||
get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store) {
|
||||
pte_t *ptep = get_pte(pgdir, la, 0);
|
||||
if (ptep_store != NULL) {
|
||||
*ptep_store = ptep;
|
||||
}
|
||||
if (ptep != NULL && *ptep & PTE_P) {
|
||||
return pa2page(*ptep);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//page_remove_pte - free an Page sturct which is related linear address la
|
||||
// - and clean(invalidate) pte which is related linear address la
|
||||
//note: PT is changed, so the TLB need to be invalidate
|
||||
static inline void
|
||||
page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) {
|
||||
/* LAB2 EXERCISE 3: YOUR CODE
|
||||
*
|
||||
* Please check if ptep is valid, and tlb must be manually updated if mapping is updated
|
||||
*
|
||||
* Maybe you want help comment, BELOW comments can help you finish the code
|
||||
*
|
||||
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
|
||||
* MACROs or Functions:
|
||||
* struct Page *page pte2page(*ptep): get the according page from the value of a ptep
|
||||
* free_page : free a page
|
||||
* page_ref_dec(page) : decrease page->ref. NOTICE: ff page->ref == 0 , then this page should be free.
|
||||
* tlb_invalidate(pde_t *pgdir, uintptr_t la) : Invalidate a TLB entry, but only if the page tables being
|
||||
* edited are the ones currently in use by the processor.
|
||||
* DEFINEs:
|
||||
* PTE_P 0x001 // page table/directory entry flags bit : Present
|
||||
*/
|
||||
#if 0
|
||||
if (0) { //(1) check if page directory is present
|
||||
struct Page *page = NULL; //(2) find corresponding page to pte
|
||||
//(3) decrease page reference
|
||||
//(4) and free this page when page reference reachs 0
|
||||
//(5) clear second page table entry
|
||||
//(6) flush tlb
|
||||
}
|
||||
#endif
|
||||
if (*ptep & PTE_P) {
|
||||
struct Page *page = pte2page(*ptep);
|
||||
if (page_ref_dec(page) == 0) {
|
||||
free_page(page);
|
||||
}
|
||||
*ptep = 0;
|
||||
tlb_invalidate(pgdir, la);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end) {
|
||||
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
|
||||
assert(USER_ACCESS(start, end));
|
||||
|
||||
do {
|
||||
pte_t *ptep = get_pte(pgdir, start, 0);
|
||||
if (ptep == NULL) {
|
||||
start = ROUNDDOWN(start + PTSIZE, PTSIZE);
|
||||
continue ;
|
||||
}
|
||||
if (*ptep != 0) {
|
||||
page_remove_pte(pgdir, start, ptep);
|
||||
}
|
||||
start += PGSIZE;
|
||||
} while (start != 0 && start < end);
|
||||
}
|
||||
|
||||
void
|
||||
exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end) {
|
||||
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
|
||||
assert(USER_ACCESS(start, end));
|
||||
|
||||
start = ROUNDDOWN(start, PTSIZE);
|
||||
do {
|
||||
int pde_idx = PDX(start);
|
||||
if (pgdir[pde_idx] & PTE_P) {
|
||||
free_page(pde2page(pgdir[pde_idx]));
|
||||
pgdir[pde_idx] = 0;
|
||||
}
|
||||
start += PTSIZE;
|
||||
} while (start != 0 && start < end);
|
||||
}
|
||||
/* copy_range - copy content of memory (start, end) of one process A to another process B
|
||||
* @to: the addr of process B's Page Directory
|
||||
* @from: the addr of process A's Page Directory
|
||||
* @share: flags to indicate to dup OR share. We just use dup method, so it didn't be used.
|
||||
*
|
||||
* CALL GRAPH: copy_mm-->dup_mmap-->copy_range
|
||||
*/
|
||||
int
|
||||
copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share) {
|
||||
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
|
||||
assert(USER_ACCESS(start, end));
|
||||
// copy content by page unit.
|
||||
do {
|
||||
//call get_pte to find process A's pte according to the addr start
|
||||
pte_t *ptep = get_pte(from, start, 0), *nptep;
|
||||
if (ptep == NULL) {
|
||||
start = ROUNDDOWN(start + PTSIZE, PTSIZE);
|
||||
continue ;
|
||||
}
|
||||
//call get_pte to find process B's pte according to the addr start. If pte is NULL, just alloc a PT
|
||||
if (*ptep & PTE_P) {
|
||||
if ((nptep = get_pte(to, start, 1)) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
uint32_t perm = (*ptep & PTE_USER);
|
||||
//get page from ptep
|
||||
struct Page *page = pte2page(*ptep);
|
||||
// alloc a page for process B
|
||||
struct Page *npage=alloc_page();
|
||||
assert(page!=NULL);
|
||||
assert(npage!=NULL);
|
||||
int ret=0;
|
||||
/* LAB5:EXERCISE2 YOUR CODE
|
||||
* replicate content of page to npage, build the map of phy addr of nage with the linear addr start
|
||||
*
|
||||
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
|
||||
* MACROs or Functions:
|
||||
* page2kva(struct Page *page): return the kernel vritual addr of memory which page managed (SEE pmm.h)
|
||||
* page_insert: build the map of phy addr of an Page with the linear addr la
|
||||
* memcpy: typical memory copy function
|
||||
*
|
||||
* (1) find src_kvaddr: the kernel virtual address of page
|
||||
* (2) find dst_kvaddr: the kernel virtual address of npage
|
||||
* (3) memory copy from src_kvaddr to dst_kvaddr, size is PGSIZE
|
||||
* (4) build the map of phy addr of nage with the linear addr start
|
||||
*/
|
||||
void * kva_src = page2kva(page);
|
||||
void * kva_dst = page2kva(npage);
|
||||
|
||||
memcpy(kva_dst, kva_src, PGSIZE);
|
||||
|
||||
ret = page_insert(to, npage, start, perm);
|
||||
assert(ret == 0);
|
||||
}
|
||||
start += PGSIZE;
|
||||
} while (start != 0 && start < end);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//page_remove - free an Page which is related linear address la and has an validated pte
|
||||
void
|
||||
page_remove(pde_t *pgdir, uintptr_t la) {
|
||||
pte_t *ptep = get_pte(pgdir, la, 0);
|
||||
if (ptep != NULL) {
|
||||
page_remove_pte(pgdir, la, ptep);
|
||||
}
|
||||
}
|
||||
|
||||
//page_insert - build the map of phy addr of an Page with the linear addr la
|
||||
// paramemters:
|
||||
// pgdir: the kernel virtual base address of PDT
|
||||
// page: the Page which need to map
|
||||
// la: the linear address need to map
|
||||
// perm: the permission of this Page which is setted in related pte
|
||||
// return value: always 0
|
||||
//note: PT is changed, so the TLB need to be invalidate
|
||||
int
|
||||
page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) {
|
||||
pte_t *ptep = get_pte(pgdir, la, 1);
|
||||
if (ptep == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
page_ref_inc(page);
|
||||
if (*ptep & PTE_P) {
|
||||
struct Page *p = pte2page(*ptep);
|
||||
if (p == page) {
|
||||
page_ref_dec(page);
|
||||
}
|
||||
else {
|
||||
page_remove_pte(pgdir, la, ptep);
|
||||
}
|
||||
}
|
||||
*ptep = page2pa(page) | PTE_P | perm;
|
||||
tlb_invalidate(pgdir, la);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// invalidate a TLB entry, but only if the page tables being
|
||||
// edited are the ones currently in use by the processor.
|
||||
void
|
||||
tlb_invalidate(pde_t *pgdir, uintptr_t la) {
|
||||
if (rcr3() == PADDR(pgdir)) {
|
||||
invlpg((void *)la);
|
||||
}
|
||||
}
|
||||
|
||||
// pgdir_alloc_page - call alloc_page & page_insert functions to
|
||||
// - allocate a page size memory & setup an addr map
|
||||
// - pa<->la with linear address la and the PDT pgdir
|
||||
struct Page *
|
||||
pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) {
|
||||
struct Page *page = alloc_page();
|
||||
if (page != NULL) {
|
||||
if (page_insert(pgdir, page, la, perm) != 0) {
|
||||
free_page(page);
|
||||
return NULL;
|
||||
}
|
||||
if (swap_init_ok){
|
||||
if(check_mm_struct!=NULL) {
|
||||
swap_map_swappable(check_mm_struct, la, page, 0);
|
||||
page->pra_vaddr=la;
|
||||
assert(page_ref(page) == 1);
|
||||
//cprintf("get No. %d page: pra_vaddr %x, pra_link.prev %x, pra_link_next %x in pgdir_alloc_page\n", (page-pages), page->pra_vaddr,page->pra_page_link.prev, page->pra_page_link.next);
|
||||
}
|
||||
else { //now current is existed, should fix it in the future
|
||||
//swap_map_swappable(current->mm, la, page, 0);
|
||||
//page->pra_vaddr=la;
|
||||
//assert(page_ref(page) == 1);
|
||||
//panic("pgdir_alloc_page: no pages. now current is existed, should fix it in the future\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static void
|
||||
check_alloc_page(void) {
|
||||
pmm_manager->check();
|
||||
cprintf("check_alloc_page() succeeded!\n");
|
||||
}
|
||||
|
||||
static void
|
||||
check_pgdir(void) {
|
||||
assert(npage <= KMEMSIZE / PGSIZE);
|
||||
assert(boot_pgdir != NULL && (uint32_t)PGOFF(boot_pgdir) == 0);
|
||||
assert(get_page(boot_pgdir, 0x0, NULL) == NULL);
|
||||
|
||||
struct Page *p1, *p2;
|
||||
p1 = alloc_page();
|
||||
assert(page_insert(boot_pgdir, p1, 0x0, 0) == 0);
|
||||
|
||||
pte_t *ptep;
|
||||
assert((ptep = get_pte(boot_pgdir, 0x0, 0)) != NULL);
|
||||
assert(pa2page(*ptep) == p1);
|
||||
assert(page_ref(p1) == 1);
|
||||
|
||||
ptep = &((pte_t *)KADDR(PDE_ADDR(boot_pgdir[0])))[1];
|
||||
assert(get_pte(boot_pgdir, PGSIZE, 0) == ptep);
|
||||
|
||||
p2 = alloc_page();
|
||||
assert(page_insert(boot_pgdir, p2, PGSIZE, PTE_U | PTE_W) == 0);
|
||||
assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL);
|
||||
assert(*ptep & PTE_U);
|
||||
assert(*ptep & PTE_W);
|
||||
assert(boot_pgdir[0] & PTE_U);
|
||||
assert(page_ref(p2) == 1);
|
||||
|
||||
assert(page_insert(boot_pgdir, p1, PGSIZE, 0) == 0);
|
||||
assert(page_ref(p1) == 2);
|
||||
assert(page_ref(p2) == 0);
|
||||
assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL);
|
||||
assert(pa2page(*ptep) == p1);
|
||||
assert((*ptep & PTE_U) == 0);
|
||||
|
||||
page_remove(boot_pgdir, 0x0);
|
||||
assert(page_ref(p1) == 1);
|
||||
assert(page_ref(p2) == 0);
|
||||
|
||||
page_remove(boot_pgdir, PGSIZE);
|
||||
assert(page_ref(p1) == 0);
|
||||
assert(page_ref(p2) == 0);
|
||||
|
||||
assert(page_ref(pa2page(boot_pgdir[0])) == 1);
|
||||
free_page(pa2page(boot_pgdir[0]));
|
||||
boot_pgdir[0] = 0;
|
||||
|
||||
cprintf("check_pgdir() succeeded!\n");
|
||||
}
|
||||
|
||||
static void
|
||||
check_boot_pgdir(void) {
|
||||
pte_t *ptep;
|
||||
int i;
|
||||
for (i = 0; i < npage; i += PGSIZE) {
|
||||
assert((ptep = get_pte(boot_pgdir, (uintptr_t)KADDR(i), 0)) != NULL);
|
||||
assert(PTE_ADDR(*ptep) == i);
|
||||
}
|
||||
|
||||
assert(PDE_ADDR(boot_pgdir[PDX(VPT)]) == PADDR(boot_pgdir));
|
||||
|
||||
assert(boot_pgdir[0] == 0);
|
||||
|
||||
struct Page *p;
|
||||
p = alloc_page();
|
||||
assert(page_insert(boot_pgdir, p, 0x100, PTE_W) == 0);
|
||||
assert(page_ref(p) == 1);
|
||||
assert(page_insert(boot_pgdir, p, 0x100 + PGSIZE, PTE_W) == 0);
|
||||
assert(page_ref(p) == 2);
|
||||
|
||||
const char *str = "ucore: Hello world!!";
|
||||
strcpy((void *)0x100, str);
|
||||
assert(strcmp((void *)0x100, (void *)(0x100 + PGSIZE)) == 0);
|
||||
|
||||
*(char *)(page2kva(p) + 0x100) = '\0';
|
||||
assert(strlen((const char *)0x100) == 0);
|
||||
|
||||
free_page(p);
|
||||
free_page(pa2page(PDE_ADDR(boot_pgdir[0])));
|
||||
boot_pgdir[0] = 0;
|
||||
|
||||
cprintf("check_boot_pgdir() succeeded!\n");
|
||||
}
|
||||
|
||||
//perm2str - use string 'u,r,w,-' to present the permission
|
||||
static const char *
|
||||
perm2str(int perm) {
|
||||
static char str[4];
|
||||
str[0] = (perm & PTE_U) ? 'u' : '-';
|
||||
str[1] = 'r';
|
||||
str[2] = (perm & PTE_W) ? 'w' : '-';
|
||||
str[3] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
//get_pgtable_items - In [left, right] range of PDT or PT, find a continuous linear addr space
|
||||
// - (left_store*X_SIZE~right_store*X_SIZE) for PDT or PT
|
||||
// - X_SIZE=PTSIZE=4M, if PDT; X_SIZE=PGSIZE=4K, if PT
|
||||
// paramemters:
|
||||
// left: no use ???
|
||||
// right: the high side of table's range
|
||||
// start: the low side of table's range
|
||||
// table: the beginning addr of table
|
||||
// left_store: the pointer of the high side of table's next range
|
||||
// right_store: the pointer of the low side of table's next range
|
||||
// return value: 0 - not a invalid item range, perm - a valid item range with perm permission
|
||||
static int
|
||||
get_pgtable_items(size_t left, size_t right, size_t start, uintptr_t *table, size_t *left_store, size_t *right_store) {
|
||||
if (start >= right) {
|
||||
return 0;
|
||||
}
|
||||
while (start < right && !(table[start] & PTE_P)) {
|
||||
start ++;
|
||||
}
|
||||
if (start < right) {
|
||||
if (left_store != NULL) {
|
||||
*left_store = start;
|
||||
}
|
||||
int perm = (table[start ++] & PTE_USER);
|
||||
while (start < right && (table[start] & PTE_USER) == perm) {
|
||||
start ++;
|
||||
}
|
||||
if (right_store != NULL) {
|
||||
*right_store = start;
|
||||
}
|
||||
return perm;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//print_pgdir - print the PDT&PT
|
||||
void
|
||||
print_pgdir(void) {
|
||||
cprintf("-------------------- BEGIN --------------------\n");
|
||||
size_t left, right = 0, perm;
|
||||
while ((perm = get_pgtable_items(0, NPDEENTRY, right, vpd, &left, &right)) != 0) {
|
||||
cprintf("PDE(%03x) %08x-%08x %08x %s\n", right - left,
|
||||
left * PTSIZE, right * PTSIZE, (right - left) * PTSIZE, perm2str(perm));
|
||||
size_t l, r = left * NPTEENTRY;
|
||||
while ((perm = get_pgtable_items(left * NPTEENTRY, right * NPTEENTRY, r, vpt, &l, &r)) != 0) {
|
||||
cprintf(" |-- PTE(%05x) %08x-%08x %08x %s\n", r - l,
|
||||
l * PGSIZE, r * PGSIZE, (r - l) * PGSIZE, perm2str(perm));
|
||||
}
|
||||
}
|
||||
cprintf("--------------------- END ---------------------\n");
|
||||
}
|
147
related_info/lab5/lab5-spoc-discuss/kern/mm/pmm.h
Normal file
147
related_info/lab5/lab5-spoc-discuss/kern/mm/pmm.h
Normal file
@ -0,0 +1,147 @@
|
||||
#ifndef __KERN_MM_PMM_H__
|
||||
#define __KERN_MM_PMM_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <mmu.h>
|
||||
#include <memlayout.h>
|
||||
#include <atomic.h>
|
||||
#include <assert.h>
|
||||
|
||||
// pmm_manager is a physical memory management class. A special pmm manager - XXX_pmm_manager
|
||||
// only needs to implement the methods in pmm_manager class, then XXX_pmm_manager can be used
|
||||
// by ucore to manage the total physical memory space.
|
||||
struct pmm_manager {
|
||||
const char *name; // XXX_pmm_manager's name
|
||||
void (*init)(void); // initialize internal description&management data structure
|
||||
// (free block list, number of free block) of XXX_pmm_manager
|
||||
void (*init_memmap)(struct Page *base, size_t n); // setup description&management data structcure according to
|
||||
// the initial free physical memory space
|
||||
struct Page *(*alloc_pages)(size_t n); // allocate >=n pages, depend on the allocation algorithm
|
||||
void (*free_pages)(struct Page *base, size_t n); // free >=n pages with "base" addr of Page descriptor structures(memlayout.h)
|
||||
size_t (*nr_free_pages)(void); // return the number of free pages
|
||||
void (*check)(void); // check the correctness of XXX_pmm_manager
|
||||
};
|
||||
|
||||
extern const struct pmm_manager *pmm_manager;
|
||||
extern pde_t *boot_pgdir;
|
||||
extern uintptr_t boot_cr3;
|
||||
|
||||
void pmm_init(void);
|
||||
|
||||
struct Page *alloc_pages(size_t n);
|
||||
void free_pages(struct Page *base, size_t n);
|
||||
size_t nr_free_pages(void);
|
||||
|
||||
#define alloc_page() alloc_pages(1)
|
||||
#define free_page(page) free_pages(page, 1)
|
||||
|
||||
pte_t *get_pte(pde_t *pgdir, uintptr_t la, bool create);
|
||||
struct Page *get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store);
|
||||
void page_remove(pde_t *pgdir, uintptr_t la);
|
||||
int page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm);
|
||||
|
||||
void load_esp0(uintptr_t esp0);
|
||||
void tlb_invalidate(pde_t *pgdir, uintptr_t la);
|
||||
struct Page *pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm);
|
||||
void unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end);
|
||||
void exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end);
|
||||
int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share);
|
||||
|
||||
void print_pgdir(void);
|
||||
|
||||
/* *
|
||||
* PADDR - 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) ({ \
|
||||
uintptr_t __m_kva = (uintptr_t)(kva); \
|
||||
if (__m_kva < KERNBASE) { \
|
||||
panic("PADDR called with invalid kva %08lx", __m_kva); \
|
||||
} \
|
||||
__m_kva - KERNBASE; \
|
||||
})
|
||||
|
||||
/* *
|
||||
* KADDR - takes a physical address and returns the corresponding kernel virtual
|
||||
* address. It panics if you pass an invalid physical address.
|
||||
* */
|
||||
#define KADDR(pa) ({ \
|
||||
uintptr_t __m_pa = (pa); \
|
||||
size_t __m_ppn = PPN(__m_pa); \
|
||||
if (__m_ppn >= npage) { \
|
||||
panic("KADDR called with invalid pa %08lx", __m_pa); \
|
||||
} \
|
||||
(void *) (__m_pa + KERNBASE); \
|
||||
})
|
||||
|
||||
extern struct Page *pages;
|
||||
extern size_t npage;
|
||||
|
||||
static inline ppn_t
|
||||
page2ppn(struct Page *page) {
|
||||
return page - pages;
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
page2pa(struct Page *page) {
|
||||
return page2ppn(page) << PGSHIFT;
|
||||
}
|
||||
|
||||
static inline struct Page *
|
||||
pa2page(uintptr_t pa) {
|
||||
if (PPN(pa) >= npage) {
|
||||
panic("pa2page called with invalid pa");
|
||||
}
|
||||
return &pages[PPN(pa)];
|
||||
}
|
||||
|
||||
static inline void *
|
||||
page2kva(struct Page *page) {
|
||||
return KADDR(page2pa(page));
|
||||
}
|
||||
|
||||
static inline struct Page *
|
||||
kva2page(void *kva) {
|
||||
return pa2page(PADDR(kva));
|
||||
}
|
||||
|
||||
static inline struct Page *
|
||||
pte2page(pte_t pte) {
|
||||
if (!(pte & PTE_P)) {
|
||||
panic("pte2page called with invalid pte");
|
||||
}
|
||||
return pa2page(PTE_ADDR(pte));
|
||||
}
|
||||
|
||||
static inline struct Page *
|
||||
pde2page(pde_t pde) {
|
||||
return pa2page(PDE_ADDR(pde));
|
||||
}
|
||||
|
||||
static inline int
|
||||
page_ref(struct Page *page) {
|
||||
return page->ref;
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_page_ref(struct Page *page, int val) {
|
||||
page->ref = val;
|
||||
}
|
||||
|
||||
static inline int
|
||||
page_ref_inc(struct Page *page) {
|
||||
page->ref += 1;
|
||||
return page->ref;
|
||||
}
|
||||
|
||||
static inline int
|
||||
page_ref_dec(struct Page *page) {
|
||||
page->ref -= 1;
|
||||
return page->ref;
|
||||
}
|
||||
|
||||
extern char bootstack[], bootstacktop[];
|
||||
|
||||
#endif /* !__KERN_MM_PMM_H__ */
|
||||
|
284
related_info/lab5/lab5-spoc-discuss/kern/mm/swap.c
Normal file
284
related_info/lab5/lab5-spoc-discuss/kern/mm/swap.c
Normal file
@ -0,0 +1,284 @@
|
||||
#include <swap.h>
|
||||
#include <swapfs.h>
|
||||
#include <swap_fifo.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <memlayout.h>
|
||||
#include <pmm.h>
|
||||
#include <mmu.h>
|
||||
#include <default_pmm.h>
|
||||
#include <kdebug.h>
|
||||
|
||||
// the valid vaddr for check is between 0~CHECK_VALID_VADDR-1
|
||||
#define CHECK_VALID_VIR_PAGE_NUM 5
|
||||
#define BEING_CHECK_VALID_VADDR 0X1000
|
||||
#define CHECK_VALID_VADDR (CHECK_VALID_VIR_PAGE_NUM+1)*0x1000
|
||||
// the max number of valid physical page for check
|
||||
#define CHECK_VALID_PHY_PAGE_NUM 4
|
||||
// the max access seq number
|
||||
#define MAX_SEQ_NO 10
|
||||
|
||||
static struct swap_manager *sm;
|
||||
size_t max_swap_offset;
|
||||
|
||||
volatile int swap_init_ok = 0;
|
||||
|
||||
unsigned int swap_page[CHECK_VALID_VIR_PAGE_NUM];
|
||||
|
||||
unsigned int swap_in_seq_no[MAX_SEQ_NO],swap_out_seq_no[MAX_SEQ_NO];
|
||||
|
||||
static void check_swap(void);
|
||||
|
||||
int
|
||||
swap_init(void)
|
||||
{
|
||||
swapfs_init();
|
||||
|
||||
if (!(1024 <= max_swap_offset && max_swap_offset < MAX_SWAP_OFFSET_LIMIT))
|
||||
{
|
||||
panic("bad max_swap_offset %08x.\n", max_swap_offset);
|
||||
}
|
||||
|
||||
|
||||
sm = &swap_manager_fifo;
|
||||
int r = sm->init();
|
||||
|
||||
if (r == 0)
|
||||
{
|
||||
swap_init_ok = 1;
|
||||
cprintf("SWAP: manager = %s\n", sm->name);
|
||||
check_swap();
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
swap_init_mm(struct mm_struct *mm)
|
||||
{
|
||||
return sm->init_mm(mm);
|
||||
}
|
||||
|
||||
int
|
||||
swap_tick_event(struct mm_struct *mm)
|
||||
{
|
||||
return sm->tick_event(mm);
|
||||
}
|
||||
|
||||
int
|
||||
swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
|
||||
{
|
||||
return sm->map_swappable(mm, addr, page, swap_in);
|
||||
}
|
||||
|
||||
int
|
||||
swap_set_unswappable(struct mm_struct *mm, uintptr_t addr)
|
||||
{
|
||||
return sm->set_unswappable(mm, addr);
|
||||
}
|
||||
|
||||
volatile unsigned int swap_out_num=0;
|
||||
|
||||
int
|
||||
swap_out(struct mm_struct *mm, int n, int in_tick)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i != n; ++ i)
|
||||
{
|
||||
uintptr_t v;
|
||||
//struct Page **ptr_page=NULL;
|
||||
struct Page *page;
|
||||
// cprintf("i %d, SWAP: call swap_out_victim\n",i);
|
||||
int r = sm->swap_out_victim(mm, &page, in_tick);
|
||||
if (r != 0) {
|
||||
cprintf("i %d, swap_out: call swap_out_victim failed\n",i);
|
||||
break;
|
||||
}
|
||||
//assert(!PageReserved(page));
|
||||
|
||||
//cprintf("SWAP: choose victim page 0x%08x\n", page);
|
||||
|
||||
v=page->pra_vaddr;
|
||||
pte_t *ptep = get_pte(mm->pgdir, v, 0);
|
||||
assert((*ptep & PTE_P) != 0);
|
||||
|
||||
if (swapfs_write( (page->pra_vaddr/PGSIZE+1)<<8, page) != 0) {
|
||||
cprintf("SWAP: failed to save\n");
|
||||
sm->map_swappable(mm, v, page, 0);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
cprintf("swap_out: i %d, store page in vaddr 0x%x to disk swap entry %d\n", i, v, page->pra_vaddr/PGSIZE+1);
|
||||
*ptep = (page->pra_vaddr/PGSIZE+1)<<8;
|
||||
free_page(page);
|
||||
}
|
||||
|
||||
tlb_invalidate(mm->pgdir, v);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result)
|
||||
{
|
||||
struct Page *result = alloc_page();
|
||||
assert(result!=NULL);
|
||||
|
||||
pte_t *ptep = get_pte(mm->pgdir, addr, 0);
|
||||
// cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages));
|
||||
|
||||
int r;
|
||||
if ((r = swapfs_read((*ptep), result)) != 0)
|
||||
{
|
||||
assert(r!=0);
|
||||
}
|
||||
cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr);
|
||||
*ptr_result=result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline void
|
||||
check_content_set(void)
|
||||
{
|
||||
*(unsigned char *)0x1000 = 0x0a;
|
||||
assert(pgfault_num==1);
|
||||
*(unsigned char *)0x1010 = 0x0a;
|
||||
assert(pgfault_num==1);
|
||||
*(unsigned char *)0x2000 = 0x0b;
|
||||
assert(pgfault_num==2);
|
||||
*(unsigned char *)0x2010 = 0x0b;
|
||||
assert(pgfault_num==2);
|
||||
*(unsigned char *)0x3000 = 0x0c;
|
||||
assert(pgfault_num==3);
|
||||
*(unsigned char *)0x3010 = 0x0c;
|
||||
assert(pgfault_num==3);
|
||||
*(unsigned char *)0x4000 = 0x0d;
|
||||
assert(pgfault_num==4);
|
||||
*(unsigned char *)0x4010 = 0x0d;
|
||||
assert(pgfault_num==4);
|
||||
}
|
||||
|
||||
static inline int
|
||||
check_content_access(void)
|
||||
{
|
||||
int ret = sm->check_swap();
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct Page * check_rp[CHECK_VALID_PHY_PAGE_NUM];
|
||||
pte_t * check_ptep[CHECK_VALID_PHY_PAGE_NUM];
|
||||
unsigned int check_swap_addr[CHECK_VALID_VIR_PAGE_NUM];
|
||||
|
||||
extern free_area_t free_area;
|
||||
|
||||
#define free_list (free_area.free_list)
|
||||
#define nr_free (free_area.nr_free)
|
||||
|
||||
static void
|
||||
check_swap(void)
|
||||
{
|
||||
//backup mem env
|
||||
int ret, count = 0, total = 0, i;
|
||||
list_entry_t *le = &free_list;
|
||||
while ((le = list_next(le)) != &free_list) {
|
||||
struct Page *p = le2page(le, page_link);
|
||||
assert(PageProperty(p));
|
||||
count ++, total += p->property;
|
||||
}
|
||||
assert(total == nr_free_pages());
|
||||
cprintf("BEGIN check_swap: count %d, total %d\n",count,total);
|
||||
|
||||
//now we set the phy pages env
|
||||
struct mm_struct *mm = mm_create();
|
||||
assert(mm != NULL);
|
||||
|
||||
extern struct mm_struct *check_mm_struct;
|
||||
assert(check_mm_struct == NULL);
|
||||
|
||||
check_mm_struct = mm;
|
||||
|
||||
pde_t *pgdir = mm->pgdir = boot_pgdir;
|
||||
assert(pgdir[0] == 0);
|
||||
|
||||
struct vma_struct *vma = vma_create(BEING_CHECK_VALID_VADDR, CHECK_VALID_VADDR, VM_WRITE | VM_READ);
|
||||
assert(vma != NULL);
|
||||
|
||||
insert_vma_struct(mm, vma);
|
||||
|
||||
//setup the temp Page Table vaddr 0~4MB
|
||||
cprintf("setup Page Table for vaddr 0X1000, so alloc a page\n");
|
||||
pte_t *temp_ptep=NULL;
|
||||
temp_ptep = get_pte(mm->pgdir, BEING_CHECK_VALID_VADDR, 1);
|
||||
assert(temp_ptep!= NULL);
|
||||
cprintf("setup Page Table vaddr 0~4MB OVER!\n");
|
||||
|
||||
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
|
||||
check_rp[i] = alloc_page();
|
||||
assert(check_rp[i] != NULL );
|
||||
assert(!PageProperty(check_rp[i]));
|
||||
}
|
||||
list_entry_t free_list_store = free_list;
|
||||
list_init(&free_list);
|
||||
assert(list_empty(&free_list));
|
||||
|
||||
//assert(alloc_page() == NULL);
|
||||
|
||||
unsigned int nr_free_store = nr_free;
|
||||
nr_free = 0;
|
||||
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
|
||||
free_pages(check_rp[i],1);
|
||||
}
|
||||
assert(nr_free==CHECK_VALID_PHY_PAGE_NUM);
|
||||
|
||||
cprintf("set up init env for check_swap begin!\n");
|
||||
//setup initial vir_page<->phy_page environment for page relpacement algorithm
|
||||
|
||||
|
||||
pgfault_num=0;
|
||||
|
||||
check_content_set();
|
||||
assert( nr_free == 0);
|
||||
for(i = 0; i<MAX_SEQ_NO ; i++)
|
||||
swap_out_seq_no[i]=swap_in_seq_no[i]=-1;
|
||||
|
||||
for (i= 0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
|
||||
check_ptep[i]=0;
|
||||
check_ptep[i] = get_pte(pgdir, (i+1)*0x1000, 0);
|
||||
//cprintf("i %d, check_ptep addr %x, value %x\n", i, check_ptep[i], *check_ptep[i]);
|
||||
assert(check_ptep[i] != NULL);
|
||||
assert(pte2page(*check_ptep[i]) == check_rp[i]);
|
||||
assert((*check_ptep[i] & PTE_P));
|
||||
}
|
||||
cprintf("set up init env for check_swap over!\n");
|
||||
// now access the virt pages to test page relpacement algorithm
|
||||
ret=check_content_access();
|
||||
assert(ret==0);
|
||||
|
||||
//restore kernel mem env
|
||||
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
|
||||
free_pages(check_rp[i],1);
|
||||
}
|
||||
|
||||
//free_page(pte2page(*temp_ptep));
|
||||
free_page(pa2page(pgdir[0]));
|
||||
pgdir[0] = 0;
|
||||
mm->pgdir = NULL;
|
||||
mm_destroy(mm);
|
||||
check_mm_struct = NULL;
|
||||
|
||||
nr_free = nr_free_store;
|
||||
free_list = free_list_store;
|
||||
|
||||
|
||||
le = &free_list;
|
||||
while ((le = list_next(le)) != &free_list) {
|
||||
struct Page *p = le2page(le, page_link);
|
||||
count --, total -= p->property;
|
||||
}
|
||||
cprintf("count is %d, total is %d\n",count,total);
|
||||
//assert(count == 0);
|
||||
|
||||
cprintf("check_swap() succeeded!\n");
|
||||
}
|
65
related_info/lab5/lab5-spoc-discuss/kern/mm/swap.h
Normal file
65
related_info/lab5/lab5-spoc-discuss/kern/mm/swap.h
Normal file
@ -0,0 +1,65 @@
|
||||
#ifndef __KERN_MM_SWAP_H__
|
||||
#define __KERN_MM_SWAP_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <memlayout.h>
|
||||
#include <pmm.h>
|
||||
#include <vmm.h>
|
||||
|
||||
/* *
|
||||
* swap_entry_t
|
||||
* --------------------------------------------
|
||||
* | offset | reserved | 0 |
|
||||
* --------------------------------------------
|
||||
* 24 bits 7 bits 1 bit
|
||||
* */
|
||||
|
||||
#define MAX_SWAP_OFFSET_LIMIT (1 << 24)
|
||||
|
||||
extern size_t max_swap_offset;
|
||||
|
||||
/* *
|
||||
* swap_offset - takes a swap_entry (saved in pte), and returns
|
||||
* the corresponding offset in swap mem_map.
|
||||
* */
|
||||
#define swap_offset(entry) ({ \
|
||||
size_t __offset = (entry >> 8); \
|
||||
if (!(__offset > 0 && __offset < max_swap_offset)) { \
|
||||
panic("invalid swap_entry_t = %08x.\n", entry); \
|
||||
} \
|
||||
__offset; \
|
||||
})
|
||||
|
||||
struct swap_manager
|
||||
{
|
||||
const char *name;
|
||||
/* Global initialization for the swap manager */
|
||||
int (*init) (void);
|
||||
/* Initialize the priv data inside mm_struct */
|
||||
int (*init_mm) (struct mm_struct *mm);
|
||||
/* Called when tick interrupt occured */
|
||||
int (*tick_event) (struct mm_struct *mm);
|
||||
/* Called when map a swappable page into the mm_struct */
|
||||
int (*map_swappable) (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
|
||||
/* When a page is marked as shared, this routine is called to
|
||||
* delete the addr entry from the swap manager */
|
||||
int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr);
|
||||
/* Try to swap out a page, return then victim */
|
||||
int (*swap_out_victim) (struct mm_struct *mm, struct Page **ptr_page, int in_tick);
|
||||
/* check the page relpacement algorithm */
|
||||
int (*check_swap)(void);
|
||||
};
|
||||
|
||||
extern volatile int swap_init_ok;
|
||||
int swap_init(void);
|
||||
int swap_init_mm(struct mm_struct *mm);
|
||||
int swap_tick_event(struct mm_struct *mm);
|
||||
int swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
|
||||
int swap_set_unswappable(struct mm_struct *mm, uintptr_t addr);
|
||||
int swap_out(struct mm_struct *mm, int n, int in_tick);
|
||||
int swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result);
|
||||
|
||||
//#define MEMBER_OFFSET(m,t) ((int)(&((t *)0)->m))
|
||||
//#define FROM_MEMBER(m,t,a) ((t *)((char *)(a) - MEMBER_OFFSET(m,t)))
|
||||
|
||||
#endif
|
144
related_info/lab5/lab5-spoc-discuss/kern/mm/swap_fifo.c
Normal file
144
related_info/lab5/lab5-spoc-discuss/kern/mm/swap_fifo.c
Normal file
@ -0,0 +1,144 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <swap.h>
|
||||
#include <swap_fifo.h>
|
||||
#include <list.h>
|
||||
|
||||
/* [wikipedia]The simplest Page Replacement Algorithm(PRA) is a FIFO algorithm. The first-in, first-out
|
||||
* page replacement algorithm is a low-overhead algorithm that requires little book-keeping on
|
||||
* the part of the operating system. The idea is obvious from the name - the operating system
|
||||
* keeps track of all the pages in memory in a queue, with the most recent arrival at the back,
|
||||
* and the earliest arrival in front. When a page needs to be replaced, the page at the front
|
||||
* of the queue (the oldest page) is selected. While FIFO is cheap and intuitive, it performs
|
||||
* poorly in practical application. Thus, it is rarely used in its unmodified form. This
|
||||
* algorithm experiences Belady's anomaly.
|
||||
*
|
||||
* Details of FIFO PRA
|
||||
* (1) Prepare: In order to implement FIFO PRA, we should manage all swappable pages, so we can
|
||||
* link these pages into pra_list_head according the time order. At first you should
|
||||
* be familiar to the struct list in list.h. struct list is a simple doubly linked list
|
||||
* implementation. You should know howto USE: list_init, list_add(list_add_after),
|
||||
* list_add_before, list_del, list_next, list_prev. Another tricky method is to transform
|
||||
* a general list struct to a special struct (such as struct page). You can find some MACRO:
|
||||
* le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.
|
||||
*/
|
||||
|
||||
list_entry_t pra_list_head;
|
||||
/*
|
||||
* (2) _fifo_init_mm: init pra_list_head and let mm->sm_priv point to the addr of pra_list_head.
|
||||
* Now, From the memory control struct mm_struct, we can access FIFO PRA
|
||||
*/
|
||||
static int
|
||||
_fifo_init_mm(struct mm_struct *mm)
|
||||
{
|
||||
list_init(&pra_list_head);
|
||||
mm->sm_priv = &pra_list_head;
|
||||
//cprintf(" mm->sm_priv %x in fifo_init_mm\n",mm->sm_priv);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue
|
||||
*/
|
||||
static int
|
||||
_fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
|
||||
{
|
||||
list_entry_t *head=(list_entry_t*) mm->sm_priv;
|
||||
list_entry_t *entry=&(page->pra_page_link);
|
||||
|
||||
assert(entry != NULL && head != NULL);
|
||||
//record the page access situlation
|
||||
/*LAB3 EXERCISE 2: YOUR CODE*/
|
||||
//(1)link the most recent arrival page at the back of the pra_list_head qeueue.
|
||||
list_add(head, entry);
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue,
|
||||
* then set the addr of addr of this page to ptr_page.
|
||||
*/
|
||||
static int
|
||||
_fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick)
|
||||
{
|
||||
list_entry_t *head=(list_entry_t*) mm->sm_priv;
|
||||
assert(head != NULL);
|
||||
assert(in_tick==0);
|
||||
/* Select the victim */
|
||||
/*LAB3 EXERCISE 2: YOUR CODE*/
|
||||
//(1) unlink the earliest arrival page in front of pra_list_head qeueue
|
||||
//(2) set the addr of addr of this page to ptr_page
|
||||
/* Select the tail */
|
||||
list_entry_t *le = head->prev;
|
||||
assert(head!=le);
|
||||
struct Page *p = le2page(le, pra_page_link);
|
||||
list_del(le);
|
||||
assert(p !=NULL);
|
||||
*ptr_page = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_fifo_check_swap(void) {
|
||||
cprintf("write Virt Page c in fifo_check_swap\n");
|
||||
*(unsigned char *)0x3000 = 0x0c;
|
||||
assert(pgfault_num==4);
|
||||
cprintf("write Virt Page a in fifo_check_swap\n");
|
||||
*(unsigned char *)0x1000 = 0x0a;
|
||||
assert(pgfault_num==4);
|
||||
cprintf("write Virt Page d in fifo_check_swap\n");
|
||||
*(unsigned char *)0x4000 = 0x0d;
|
||||
assert(pgfault_num==4);
|
||||
cprintf("write Virt Page b in fifo_check_swap\n");
|
||||
*(unsigned char *)0x2000 = 0x0b;
|
||||
assert(pgfault_num==4);
|
||||
cprintf("write Virt Page e in fifo_check_swap\n");
|
||||
*(unsigned char *)0x5000 = 0x0e;
|
||||
assert(pgfault_num==5);
|
||||
cprintf("write Virt Page b in fifo_check_swap\n");
|
||||
*(unsigned char *)0x2000 = 0x0b;
|
||||
assert(pgfault_num==5);
|
||||
cprintf("write Virt Page a in fifo_check_swap\n");
|
||||
*(unsigned char *)0x1000 = 0x0a;
|
||||
assert(pgfault_num==6);
|
||||
cprintf("write Virt Page b in fifo_check_swap\n");
|
||||
*(unsigned char *)0x2000 = 0x0b;
|
||||
assert(pgfault_num==7);
|
||||
cprintf("write Virt Page c in fifo_check_swap\n");
|
||||
*(unsigned char *)0x3000 = 0x0c;
|
||||
assert(pgfault_num==8);
|
||||
cprintf("write Virt Page d in fifo_check_swap\n");
|
||||
*(unsigned char *)0x4000 = 0x0d;
|
||||
assert(pgfault_num==9);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
_fifo_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_fifo_set_unswappable(struct mm_struct *mm, uintptr_t addr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_fifo_tick_event(struct mm_struct *mm)
|
||||
{ return 0; }
|
||||
|
||||
|
||||
struct swap_manager swap_manager_fifo =
|
||||
{
|
||||
.name = "fifo swap manager",
|
||||
.init = &_fifo_init,
|
||||
.init_mm = &_fifo_init_mm,
|
||||
.tick_event = &_fifo_tick_event,
|
||||
.map_swappable = &_fifo_map_swappable,
|
||||
.set_unswappable = &_fifo_set_unswappable,
|
||||
.swap_out_victim = &_fifo_swap_out_victim,
|
||||
.check_swap = &_fifo_check_swap,
|
||||
};
|
7
related_info/lab5/lab5-spoc-discuss/kern/mm/swap_fifo.h
Normal file
7
related_info/lab5/lab5-spoc-discuss/kern/mm/swap_fifo.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef __KERN_MM_SWAP_FIFO_H__
|
||||
#define __KERN_MM_SWAP_FIFO_H__
|
||||
|
||||
#include <swap.h>
|
||||
extern struct swap_manager swap_manager_fifo;
|
||||
|
||||
#endif
|
562
related_info/lab5/lab5-spoc-discuss/kern/mm/vmm.c
Normal file
562
related_info/lab5/lab5-spoc-discuss/kern/mm/vmm.c
Normal file
@ -0,0 +1,562 @@
|
||||
#include <vmm.h>
|
||||
#include <sync.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <error.h>
|
||||
#include <pmm.h>
|
||||
#include <x86.h>
|
||||
#include <swap.h>
|
||||
#include <kmalloc.h>
|
||||
|
||||
/*
|
||||
vmm design include two parts: mm_struct (mm) & vma_struct (vma)
|
||||
mm is the memory manager for the set of continuous virtual memory
|
||||
area which have the same PDT. vma is a continuous virtual memory area.
|
||||
There a linear link list for vma & a redblack link list for vma in mm.
|
||||
---------------
|
||||
mm related functions:
|
||||
golbal functions
|
||||
struct mm_struct * mm_create(void)
|
||||
void mm_destroy(struct mm_struct *mm)
|
||||
int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr)
|
||||
--------------
|
||||
vma related functions:
|
||||
global functions
|
||||
struct vma_struct * vma_create (uintptr_t vm_start, uintptr_t vm_end,...)
|
||||
void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma)
|
||||
struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr)
|
||||
local functions
|
||||
inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next)
|
||||
---------------
|
||||
check correctness functions
|
||||
void check_vmm(void);
|
||||
void check_vma_struct(void);
|
||||
void check_pgfault(void);
|
||||
*/
|
||||
|
||||
static void check_vmm(void);
|
||||
static void check_vma_struct(void);
|
||||
static void check_pgfault(void);
|
||||
|
||||
// mm_create - alloc a mm_struct & initialize it.
|
||||
struct mm_struct *
|
||||
mm_create(void) {
|
||||
struct mm_struct *mm = kmalloc(sizeof(struct mm_struct));
|
||||
|
||||
if (mm != NULL) {
|
||||
list_init(&(mm->mmap_list));
|
||||
mm->mmap_cache = NULL;
|
||||
mm->pgdir = NULL;
|
||||
mm->map_count = 0;
|
||||
|
||||
if (swap_init_ok) swap_init_mm(mm);
|
||||
else mm->sm_priv = NULL;
|
||||
|
||||
set_mm_count(mm, 0);
|
||||
lock_init(&(mm->mm_lock));
|
||||
}
|
||||
return mm;
|
||||
}
|
||||
|
||||
// vma_create - alloc a vma_struct & initialize it. (addr range: vm_start~vm_end)
|
||||
struct vma_struct *
|
||||
vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags) {
|
||||
struct vma_struct *vma = kmalloc(sizeof(struct vma_struct));
|
||||
|
||||
if (vma != NULL) {
|
||||
vma->vm_start = vm_start;
|
||||
vma->vm_end = vm_end;
|
||||
vma->vm_flags = vm_flags;
|
||||
}
|
||||
return vma;
|
||||
}
|
||||
|
||||
|
||||
// find_vma - find a vma (vma->vm_start <= addr <= vma_vm_end)
|
||||
struct vma_struct *
|
||||
find_vma(struct mm_struct *mm, uintptr_t addr) {
|
||||
struct vma_struct *vma = NULL;
|
||||
if (mm != NULL) {
|
||||
vma = mm->mmap_cache;
|
||||
if (!(vma != NULL && vma->vm_start <= addr && vma->vm_end > addr)) {
|
||||
bool found = 0;
|
||||
list_entry_t *list = &(mm->mmap_list), *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
vma = le2vma(le, list_link);
|
||||
if (vma->vm_start<=addr && addr < vma->vm_end) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
vma = NULL;
|
||||
}
|
||||
}
|
||||
if (vma != NULL) {
|
||||
mm->mmap_cache = vma;
|
||||
}
|
||||
}
|
||||
return vma;
|
||||
}
|
||||
|
||||
|
||||
// check_vma_overlap - check if vma1 overlaps vma2 ?
|
||||
static inline void
|
||||
check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) {
|
||||
assert(prev->vm_start < prev->vm_end);
|
||||
assert(prev->vm_end <= next->vm_start);
|
||||
assert(next->vm_start < next->vm_end);
|
||||
}
|
||||
|
||||
|
||||
// insert_vma_struct -insert vma in mm's list link
|
||||
void
|
||||
insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) {
|
||||
assert(vma->vm_start < vma->vm_end);
|
||||
list_entry_t *list = &(mm->mmap_list);
|
||||
list_entry_t *le_prev = list, *le_next;
|
||||
|
||||
list_entry_t *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
struct vma_struct *mmap_prev = le2vma(le, list_link);
|
||||
if (mmap_prev->vm_start > vma->vm_start) {
|
||||
break;
|
||||
}
|
||||
le_prev = le;
|
||||
}
|
||||
|
||||
le_next = list_next(le_prev);
|
||||
|
||||
/* check overlap */
|
||||
if (le_prev != list) {
|
||||
check_vma_overlap(le2vma(le_prev, list_link), vma);
|
||||
}
|
||||
if (le_next != list) {
|
||||
check_vma_overlap(vma, le2vma(le_next, list_link));
|
||||
}
|
||||
|
||||
vma->vm_mm = mm;
|
||||
list_add_after(le_prev, &(vma->list_link));
|
||||
|
||||
mm->map_count ++;
|
||||
}
|
||||
|
||||
// mm_destroy - free mm and mm internal fields
|
||||
void
|
||||
mm_destroy(struct mm_struct *mm) {
|
||||
assert(mm_count(mm) == 0);
|
||||
|
||||
list_entry_t *list = &(mm->mmap_list), *le;
|
||||
while ((le = list_next(list)) != list) {
|
||||
list_del(le);
|
||||
kfree(le2vma(le, list_link)); //kfree vma
|
||||
}
|
||||
kfree(mm); //kfree mm
|
||||
mm=NULL;
|
||||
}
|
||||
|
||||
int
|
||||
mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags,
|
||||
struct vma_struct **vma_store) {
|
||||
uintptr_t start = ROUNDDOWN(addr, PGSIZE), end = ROUNDUP(addr + len, PGSIZE);
|
||||
if (!USER_ACCESS(start, end)) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
assert(mm != NULL);
|
||||
|
||||
int ret = -E_INVAL;
|
||||
|
||||
struct vma_struct *vma;
|
||||
if ((vma = find_vma(mm, start)) != NULL && end > vma->vm_start) {
|
||||
goto out;
|
||||
}
|
||||
ret = -E_NO_MEM;
|
||||
|
||||
if ((vma = vma_create(start, end, vm_flags)) == NULL) {
|
||||
goto out;
|
||||
}
|
||||
insert_vma_struct(mm, vma);
|
||||
if (vma_store != NULL) {
|
||||
*vma_store = vma;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
dup_mmap(struct mm_struct *to, struct mm_struct *from) {
|
||||
assert(to != NULL && from != NULL);
|
||||
list_entry_t *list = &(from->mmap_list), *le = list;
|
||||
while ((le = list_prev(le)) != list) {
|
||||
struct vma_struct *vma, *nvma;
|
||||
vma = le2vma(le, list_link);
|
||||
nvma = vma_create(vma->vm_start, vma->vm_end, vma->vm_flags);
|
||||
if (nvma == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
insert_vma_struct(to, nvma);
|
||||
|
||||
bool share = 0;
|
||||
if (copy_range(to->pgdir, from->pgdir, vma->vm_start, vma->vm_end, share) != 0) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
exit_mmap(struct mm_struct *mm) {
|
||||
assert(mm != NULL && mm_count(mm) == 0);
|
||||
pde_t *pgdir = mm->pgdir;
|
||||
list_entry_t *list = &(mm->mmap_list), *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
struct vma_struct *vma = le2vma(le, list_link);
|
||||
unmap_range(pgdir, vma->vm_start, vma->vm_end);
|
||||
}
|
||||
while ((le = list_next(le)) != list) {
|
||||
struct vma_struct *vma = le2vma(le, list_link);
|
||||
exit_range(pgdir, vma->vm_start, vma->vm_end);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable) {
|
||||
if (!user_mem_check(mm, (uintptr_t)src, len, writable)) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(dst, src, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool
|
||||
copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len) {
|
||||
if (!user_mem_check(mm, (uintptr_t)dst, len, 1)) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(dst, src, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// vmm_init - initialize virtual memory management
|
||||
// - now just call check_vmm to check correctness of vmm
|
||||
void
|
||||
vmm_init(void) {
|
||||
check_vmm();
|
||||
}
|
||||
|
||||
// check_vmm - check correctness of vmm
|
||||
static void
|
||||
check_vmm(void) {
|
||||
size_t nr_free_pages_store = nr_free_pages();
|
||||
|
||||
check_vma_struct();
|
||||
check_pgfault();
|
||||
|
||||
// assert(nr_free_pages_store == nr_free_pages());
|
||||
|
||||
cprintf("check_vmm() succeeded.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
check_vma_struct(void) {
|
||||
size_t nr_free_pages_store = nr_free_pages();
|
||||
|
||||
struct mm_struct *mm = mm_create();
|
||||
assert(mm != NULL);
|
||||
|
||||
int step1 = 10, step2 = step1 * 10;
|
||||
|
||||
int i;
|
||||
for (i = step1; i >= 1; i --) {
|
||||
struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
|
||||
assert(vma != NULL);
|
||||
insert_vma_struct(mm, vma);
|
||||
}
|
||||
|
||||
for (i = step1 + 1; i <= step2; i ++) {
|
||||
struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
|
||||
assert(vma != NULL);
|
||||
insert_vma_struct(mm, vma);
|
||||
}
|
||||
|
||||
list_entry_t *le = list_next(&(mm->mmap_list));
|
||||
|
||||
for (i = 1; i <= step2; i ++) {
|
||||
assert(le != &(mm->mmap_list));
|
||||
struct vma_struct *mmap = le2vma(le, list_link);
|
||||
assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2);
|
||||
le = list_next(le);
|
||||
}
|
||||
|
||||
for (i = 5; i <= 5 * step2; i +=5) {
|
||||
struct vma_struct *vma1 = find_vma(mm, i);
|
||||
assert(vma1 != NULL);
|
||||
struct vma_struct *vma2 = find_vma(mm, i+1);
|
||||
assert(vma2 != NULL);
|
||||
struct vma_struct *vma3 = find_vma(mm, i+2);
|
||||
assert(vma3 == NULL);
|
||||
struct vma_struct *vma4 = find_vma(mm, i+3);
|
||||
assert(vma4 == NULL);
|
||||
struct vma_struct *vma5 = find_vma(mm, i+4);
|
||||
assert(vma5 == NULL);
|
||||
|
||||
assert(vma1->vm_start == i && vma1->vm_end == i + 2);
|
||||
assert(vma2->vm_start == i && vma2->vm_end == i + 2);
|
||||
}
|
||||
|
||||
for (i =4; i>=0; i--) {
|
||||
struct vma_struct *vma_below_5= find_vma(mm,i);
|
||||
if (vma_below_5 != NULL ) {
|
||||
cprintf("vma_below_5: i %x, start %x, end %x\n",i, vma_below_5->vm_start, vma_below_5->vm_end);
|
||||
}
|
||||
assert(vma_below_5 == NULL);
|
||||
}
|
||||
|
||||
mm_destroy(mm);
|
||||
|
||||
// assert(nr_free_pages_store == nr_free_pages());
|
||||
|
||||
cprintf("check_vma_struct() succeeded!\n");
|
||||
}
|
||||
|
||||
struct mm_struct *check_mm_struct;
|
||||
|
||||
// check_pgfault - check correctness of pgfault handler
|
||||
static void
|
||||
check_pgfault(void) {
|
||||
size_t nr_free_pages_store = nr_free_pages();
|
||||
|
||||
check_mm_struct = mm_create();
|
||||
assert(check_mm_struct != NULL);
|
||||
|
||||
struct mm_struct *mm = check_mm_struct;
|
||||
pde_t *pgdir = mm->pgdir = boot_pgdir;
|
||||
assert(pgdir[0] == 0);
|
||||
|
||||
struct vma_struct *vma = vma_create(0, PTSIZE, VM_WRITE);
|
||||
assert(vma != NULL);
|
||||
|
||||
insert_vma_struct(mm, vma);
|
||||
|
||||
uintptr_t addr = 0x100;
|
||||
assert(find_vma(mm, addr) == vma);
|
||||
|
||||
int i, sum = 0;
|
||||
for (i = 0; i < 100; i ++) {
|
||||
*(char *)(addr + i) = i;
|
||||
sum += i;
|
||||
}
|
||||
for (i = 0; i < 100; i ++) {
|
||||
sum -= *(char *)(addr + i);
|
||||
}
|
||||
assert(sum == 0);
|
||||
|
||||
page_remove(pgdir, ROUNDDOWN(addr, PGSIZE));
|
||||
free_page(pa2page(pgdir[0]));
|
||||
pgdir[0] = 0;
|
||||
|
||||
mm->pgdir = NULL;
|
||||
mm_destroy(mm);
|
||||
check_mm_struct = NULL;
|
||||
|
||||
assert(nr_free_pages_store == nr_free_pages());
|
||||
|
||||
cprintf("check_pgfault() succeeded!\n");
|
||||
}
|
||||
//page fault number
|
||||
volatile unsigned int pgfault_num=0;
|
||||
|
||||
/* do_pgfault - interrupt handler to process the page fault execption
|
||||
* @mm : the control struct for a set of vma using the same PDT
|
||||
* @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware
|
||||
* @addr : the addr which causes a memory access exception, (the contents of the CR2 register)
|
||||
*
|
||||
* CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault
|
||||
* The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing
|
||||
* the exception and recovering from it.
|
||||
* (1) The contents of the CR2 register. The processor loads the CR2 register with the
|
||||
* 32-bit linear address that generated the exception. The do_pgfault fun can
|
||||
* use this address to locate the corresponding page directory and page-table
|
||||
* entries.
|
||||
* (2) An error code on the kernel stack. The error code for a page fault has a format different from
|
||||
* that for other exceptions. The error code tells the exception handler three things:
|
||||
* -- The P flag (bit 0) indicates whether the exception was due to a not-present page (0)
|
||||
* or to either an access rights violation or the use of a reserved bit (1).
|
||||
* -- The W/R flag (bit 1) indicates whether the memory access that caused the exception
|
||||
* was a read (0) or write (1).
|
||||
* -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1)
|
||||
* or supervisor mode (0) at the time of the exception.
|
||||
*/
|
||||
int
|
||||
do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
|
||||
int ret = -E_INVAL;
|
||||
//try to find a vma which include addr
|
||||
struct vma_struct *vma = find_vma(mm, addr);
|
||||
|
||||
pgfault_num++;
|
||||
//If the addr is in the range of a mm's vma?
|
||||
if (vma == NULL || vma->vm_start > addr) {
|
||||
cprintf("not valid addr %x, and can not find it in vma\n", addr);
|
||||
goto failed;
|
||||
}
|
||||
//check the error_code
|
||||
switch (error_code & 3) {
|
||||
default:
|
||||
/* error code flag : default is 3 ( W/R=1, P=1): write, present */
|
||||
case 2: /* error code flag : (W/R=1, P=0): write, not present */
|
||||
if (!(vma->vm_flags & VM_WRITE)) {
|
||||
cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n");
|
||||
goto failed;
|
||||
}
|
||||
break;
|
||||
case 1: /* error code flag : (W/R=0, P=1): read, present */
|
||||
cprintf("do_pgfault failed: error code flag = read AND present\n");
|
||||
goto failed;
|
||||
case 0: /* error code flag : (W/R=0, P=0): read, not present */
|
||||
if (!(vma->vm_flags & (VM_READ | VM_EXEC))) {
|
||||
cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n");
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
/* IF (write an existed addr ) OR
|
||||
* (write an non_existed addr && addr is writable) OR
|
||||
* (read an non_existed addr && addr is readable)
|
||||
* THEN
|
||||
* continue process
|
||||
*/
|
||||
uint32_t perm = PTE_U;
|
||||
if (vma->vm_flags & VM_WRITE) {
|
||||
perm |= PTE_W;
|
||||
}
|
||||
addr = ROUNDDOWN(addr, PGSIZE);
|
||||
|
||||
ret = -E_NO_MEM;
|
||||
|
||||
pte_t *ptep=NULL;
|
||||
/*LAB3 EXERCISE 1: YOUR CODE
|
||||
* Maybe you want help comment, BELOW comments can help you finish the code
|
||||
*
|
||||
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
|
||||
* MACROs or Functions:
|
||||
* get_pte : get an pte and return the kernel virtual address of this pte for la
|
||||
* if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1')
|
||||
* pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup
|
||||
* an addr map pa<--->la with linear address la and the PDT pgdir
|
||||
* DEFINES:
|
||||
* VM_WRITE : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable
|
||||
* PTE_W 0x002 // page table/directory entry flags bit : Writeable
|
||||
* PTE_U 0x004 // page table/directory entry flags bit : User can access
|
||||
* VARIABLES:
|
||||
* mm->pgdir : the PDT of these vma
|
||||
*
|
||||
*/
|
||||
#if 0
|
||||
/*LAB3 EXERCISE 1: YOUR CODE*/
|
||||
ptep = ??? //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
|
||||
if (*ptep == 0) {
|
||||
//(2) if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
|
||||
|
||||
}
|
||||
else {
|
||||
/*LAB3 EXERCISE 2: YOUR CODE
|
||||
* Now we think this pte is a swap entry, we should load data from disk to a page with phy addr,
|
||||
* and map the phy addr with logical addr, trigger swap manager to record the access situation of this page.
|
||||
*
|
||||
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
|
||||
* MACROs or Functions:
|
||||
* swap_in(mm, addr, &page) : alloc a memory page, then according to the swap entry in PTE for addr,
|
||||
* find the addr of disk page, read the content of disk page into this memroy page
|
||||
* page_insert : build the map of phy addr of an Page with the linear addr la
|
||||
* swap_map_swappable : set the page swappable
|
||||
*/
|
||||
if(swap_init_ok) {
|
||||
struct Page *page=NULL;
|
||||
//(1)According to the mm AND addr, try to load the content of right disk page
|
||||
// into the memory which page managed.
|
||||
//(2) According to the mm, addr AND page, setup the map of phy addr <---> logical addr
|
||||
//(3) make the page swappable.
|
||||
//(4) [NOTICE]: you myabe need to update your lab3's implementation for LAB5's normal execution.
|
||||
}
|
||||
else {
|
||||
cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
|
||||
// (notice the 3th parameter '1')
|
||||
if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {
|
||||
cprintf("get_pte in do_pgfault failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
|
||||
if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {
|
||||
cprintf("pgdir_alloc_page in do_pgfault failed\n");
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
else {
|
||||
struct Page *page=NULL;
|
||||
cprintf("do pgfault: ptep %x, pte %x\n",ptep, *ptep);
|
||||
if (*ptep & PTE_P) {
|
||||
//if process write to this existed readonly page (PTE_P means existed), then should be here now.
|
||||
//we can implement the delayed memory space copy for fork child process (AKA copy on write, COW).
|
||||
//we didn't implement now, we will do it in future.
|
||||
panic("error write a non-writable pte");
|
||||
//page = pte2page(*ptep);
|
||||
} else{
|
||||
// if this pte is a swap entry, then load data from disk to a page with phy addr
|
||||
// and call page_insert to map the phy addr with logical addr
|
||||
if(swap_init_ok) {
|
||||
if ((ret = swap_in(mm, addr, &page)) != 0) {
|
||||
cprintf("swap_in in do_pgfault failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
page_insert(mm->pgdir, page, addr, perm);
|
||||
swap_map_swappable(mm, addr, page, 1);
|
||||
}
|
||||
ret = 0;
|
||||
failed:
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
user_mem_check(struct mm_struct *mm, uintptr_t addr, size_t len, bool write) {
|
||||
if (mm != NULL) {
|
||||
if (!USER_ACCESS(addr, addr + len)) {
|
||||
return 0;
|
||||
}
|
||||
struct vma_struct *vma;
|
||||
uintptr_t start = addr, end = addr + len;
|
||||
while (start < end) {
|
||||
if ((vma = find_vma(mm, start)) == NULL || start < vma->vm_start) {
|
||||
return 0;
|
||||
}
|
||||
if (!(vma->vm_flags & ((write) ? VM_WRITE : VM_READ))) {
|
||||
return 0;
|
||||
}
|
||||
if (write && (vma->vm_flags & VM_STACK)) {
|
||||
if (start < vma->vm_start + PGSIZE) { //check stack start & size
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
start = vma->vm_end;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return KERN_ACCESS(addr, addr + len);
|
||||
}
|
||||
|
102
related_info/lab5/lab5-spoc-discuss/kern/mm/vmm.h
Normal file
102
related_info/lab5/lab5-spoc-discuss/kern/mm/vmm.h
Normal file
@ -0,0 +1,102 @@
|
||||
#ifndef __KERN_MM_VMM_H__
|
||||
#define __KERN_MM_VMM_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <list.h>
|
||||
#include <memlayout.h>
|
||||
#include <sync.h>
|
||||
|
||||
//pre define
|
||||
struct mm_struct;
|
||||
|
||||
// the virtual continuous memory area(vma)
|
||||
struct vma_struct {
|
||||
struct mm_struct *vm_mm; // the set of vma using the same PDT
|
||||
uintptr_t vm_start; // start addr of vma
|
||||
uintptr_t vm_end; // end addr of vma
|
||||
uint32_t vm_flags; // flags of vma
|
||||
list_entry_t list_link; // linear list link which sorted by start addr of vma
|
||||
};
|
||||
|
||||
#define le2vma(le, member) \
|
||||
to_struct((le), struct vma_struct, member)
|
||||
|
||||
#define VM_READ 0x00000001
|
||||
#define VM_WRITE 0x00000002
|
||||
#define VM_EXEC 0x00000004
|
||||
#define VM_STACK 0x00000008
|
||||
|
||||
// the control struct for a set of vma using the same PDT
|
||||
struct mm_struct {
|
||||
list_entry_t mmap_list; // linear list link which sorted by start addr of vma
|
||||
struct vma_struct *mmap_cache; // current accessed vma, used for speed purpose
|
||||
pde_t *pgdir; // the PDT of these vma
|
||||
int map_count; // the count of these vma
|
||||
void *sm_priv; // the private data for swap manager
|
||||
int mm_count; // the number ofprocess which shared the mm
|
||||
lock_t mm_lock; // mutex for using dup_mmap fun to duplicat the mm
|
||||
};
|
||||
|
||||
struct vma_struct *find_vma(struct mm_struct *mm, uintptr_t addr);
|
||||
struct vma_struct *vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags);
|
||||
void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma);
|
||||
|
||||
struct mm_struct *mm_create(void);
|
||||
void mm_destroy(struct mm_struct *mm);
|
||||
|
||||
void vmm_init(void);
|
||||
int mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags,
|
||||
struct vma_struct **vma_store);
|
||||
int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr);
|
||||
|
||||
int mm_unmap(struct mm_struct *mm, uintptr_t addr, size_t len);
|
||||
int dup_mmap(struct mm_struct *to, struct mm_struct *from);
|
||||
void exit_mmap(struct mm_struct *mm);
|
||||
uintptr_t get_unmapped_area(struct mm_struct *mm, size_t len);
|
||||
int mm_brk(struct mm_struct *mm, uintptr_t addr, size_t len);
|
||||
|
||||
extern volatile unsigned int pgfault_num;
|
||||
extern struct mm_struct *check_mm_struct;
|
||||
|
||||
bool user_mem_check(struct mm_struct *mm, uintptr_t start, size_t len, bool write);
|
||||
bool copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable);
|
||||
bool copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len);
|
||||
|
||||
static inline int
|
||||
mm_count(struct mm_struct *mm) {
|
||||
return mm->mm_count;
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_mm_count(struct mm_struct *mm, int val) {
|
||||
mm->mm_count = val;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mm_count_inc(struct mm_struct *mm) {
|
||||
mm->mm_count += 1;
|
||||
return mm->mm_count;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mm_count_dec(struct mm_struct *mm) {
|
||||
mm->mm_count -= 1;
|
||||
return mm->mm_count;
|
||||
}
|
||||
|
||||
static inline void
|
||||
lock_mm(struct mm_struct *mm) {
|
||||
if (mm != NULL) {
|
||||
lock(&(mm->mm_lock));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
unlock_mm(struct mm_struct *mm) {
|
||||
if (mm != NULL) {
|
||||
unlock(&(mm->mm_lock));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* !__KERN_MM_VMM_H__ */
|
||||
|
10
related_info/lab5/lab5-spoc-discuss/kern/process/entry.S
Normal file
10
related_info/lab5/lab5-spoc-discuss/kern/process/entry.S
Normal file
@ -0,0 +1,10 @@
|
||||
.text
|
||||
.globl kernel_thread_entry
|
||||
kernel_thread_entry: # void kernel_thread(void)
|
||||
|
||||
pushl %edx # push arg
|
||||
call *%ebx # call fn
|
||||
|
||||
pushl %eax # save the return value of fn(arg)
|
||||
call do_exit # call do_exit to terminate current thread
|
||||
|
888
related_info/lab5/lab5-spoc-discuss/kern/process/proc.c
Normal file
888
related_info/lab5/lab5-spoc-discuss/kern/process/proc.c
Normal file
@ -0,0 +1,888 @@
|
||||
#include <proc.h>
|
||||
#include <kmalloc.h>
|
||||
#include <string.h>
|
||||
#include <sync.h>
|
||||
#include <pmm.h>
|
||||
#include <error.h>
|
||||
#include <sched.h>
|
||||
#include <elf.h>
|
||||
#include <vmm.h>
|
||||
#include <trap.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* ------------- process/thread mechanism design&implementation -------------
|
||||
(an simplified Linux process/thread mechanism )
|
||||
introduction:
|
||||
ucore implements a simple process/thread mechanism. process contains the independent memory sapce, at least one threads
|
||||
for execution, the kernel data(for management), processor state (for context switch), files(in lab6), etc. ucore needs to
|
||||
manage all these details efficiently. In ucore, a thread is just a special kind of process(share process's memory).
|
||||
------------------------------
|
||||
process state : meaning -- reason
|
||||
PROC_UNINIT : uninitialized -- alloc_proc
|
||||
PROC_SLEEPING : sleeping -- try_free_pages, do_wait, do_sleep
|
||||
PROC_RUNNABLE : runnable(maybe running) -- proc_init, wakeup_proc,
|
||||
PROC_ZOMBIE : almost dead -- do_exit
|
||||
|
||||
-----------------------------
|
||||
process state changing:
|
||||
|
||||
alloc_proc RUNNING
|
||||
+ +--<----<--+
|
||||
+ + proc_run +
|
||||
V +-->---->--+
|
||||
PROC_UNINIT -- proc_init/wakeup_proc --> PROC_RUNNABLE -- try_free_pages/do_wait/do_sleep --> PROC_SLEEPING --
|
||||
A + +
|
||||
| +--- do_exit --> PROC_ZOMBIE +
|
||||
+ +
|
||||
-----------------------wakeup_proc----------------------------------
|
||||
-----------------------------
|
||||
process relations
|
||||
parent: proc->parent (proc is children)
|
||||
children: proc->cptr (proc is parent)
|
||||
older sibling: proc->optr (proc is younger sibling)
|
||||
younger sibling: proc->yptr (proc is older sibling)
|
||||
-----------------------------
|
||||
related syscall for process:
|
||||
SYS_exit : process exit, -->do_exit
|
||||
SYS_fork : create child process, dup mm -->do_fork-->wakeup_proc
|
||||
SYS_wait : wait process -->do_wait
|
||||
SYS_exec : after fork, process execute a program -->load a program and refresh the mm
|
||||
SYS_clone : create child thread -->do_fork-->wakeup_proc
|
||||
SYS_yield : process flag itself need resecheduling, -- proc->need_sched=1, then scheduler will rescheule this process
|
||||
SYS_sleep : process sleep -->do_sleep
|
||||
SYS_kill : kill process -->do_kill-->proc->flags |= PF_EXITING
|
||||
-->wakeup_proc-->do_wait-->do_exit
|
||||
SYS_getpid : get the process's pid
|
||||
|
||||
*/
|
||||
|
||||
// the process set's list
|
||||
list_entry_t proc_list;
|
||||
|
||||
#define HASH_SHIFT 10
|
||||
#define HASH_LIST_SIZE (1 << HASH_SHIFT)
|
||||
#define pid_hashfn(x) (hash32(x, HASH_SHIFT))
|
||||
|
||||
// has list for process set based on pid
|
||||
static list_entry_t hash_list[HASH_LIST_SIZE];
|
||||
|
||||
// idle proc
|
||||
struct proc_struct *idleproc = NULL;
|
||||
// init proc
|
||||
struct proc_struct *initproc = NULL;
|
||||
// current proc
|
||||
struct proc_struct *current = NULL;
|
||||
|
||||
static int nr_process = 0;
|
||||
|
||||
void kernel_thread_entry(void);
|
||||
void forkrets(struct trapframe *tf);
|
||||
void switch_to(struct context *from, struct context *to);
|
||||
|
||||
// alloc_proc - alloc a proc_struct and init all fields of proc_struct
|
||||
static struct proc_struct *
|
||||
alloc_proc(void) {
|
||||
struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));
|
||||
if (proc != NULL) {
|
||||
//LAB4:EXERCISE1 YOUR CODE
|
||||
/*
|
||||
* below fields in proc_struct need to be initialized
|
||||
* enum proc_state state; // Process state
|
||||
* int pid; // Process ID
|
||||
* int runs; // the running times of Proces
|
||||
* uintptr_t kstack; // Process kernel stack
|
||||
* volatile bool need_resched; // bool value: need to be rescheduled to release CPU?
|
||||
* struct proc_struct *parent; // the parent process
|
||||
* struct mm_struct *mm; // Process's memory management field
|
||||
* struct context context; // Switch here to run process
|
||||
* struct trapframe *tf; // Trap frame for current interrupt
|
||||
* uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT)
|
||||
* uint32_t flags; // Process flag
|
||||
* char name[PROC_NAME_LEN + 1]; // Process name
|
||||
*/
|
||||
proc->state = PROC_UNINIT;
|
||||
proc->pid = -1;
|
||||
proc->runs = 0;
|
||||
proc->kstack = 0;
|
||||
proc->need_resched = 0;
|
||||
proc->parent = NULL;
|
||||
proc->mm = NULL;
|
||||
memset(&(proc->context), 0, sizeof(struct context));
|
||||
proc->tf = NULL;
|
||||
proc->cr3 = boot_cr3;
|
||||
proc->flags = 0;
|
||||
memset(proc->name, 0, PROC_NAME_LEN);
|
||||
proc->wait_state = 0;
|
||||
proc->cptr = proc->optr = proc->yptr = NULL;
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
|
||||
// set_proc_name - set the name of proc
|
||||
char *
|
||||
set_proc_name(struct proc_struct *proc, const char *name) {
|
||||
memset(proc->name, 0, sizeof(proc->name));
|
||||
return memcpy(proc->name, name, PROC_NAME_LEN);
|
||||
}
|
||||
|
||||
// get_proc_name - get the name of proc
|
||||
char *
|
||||
get_proc_name(struct proc_struct *proc) {
|
||||
static char name[PROC_NAME_LEN + 1];
|
||||
memset(name, 0, sizeof(name));
|
||||
return memcpy(name, proc->name, PROC_NAME_LEN);
|
||||
}
|
||||
|
||||
// set_links - set the relation links of process
|
||||
static void
|
||||
set_links(struct proc_struct *proc) {
|
||||
list_add(&proc_list, &(proc->list_link));
|
||||
proc->yptr = NULL;
|
||||
if ((proc->optr = proc->parent->cptr) != NULL) {
|
||||
proc->optr->yptr = proc;
|
||||
}
|
||||
proc->parent->cptr = proc;
|
||||
nr_process ++;
|
||||
}
|
||||
|
||||
// remove_links - clean the relation links of process
|
||||
static void
|
||||
remove_links(struct proc_struct *proc) {
|
||||
list_del(&(proc->list_link));
|
||||
if (proc->optr != NULL) {
|
||||
proc->optr->yptr = proc->yptr;
|
||||
}
|
||||
if (proc->yptr != NULL) {
|
||||
proc->yptr->optr = proc->optr;
|
||||
}
|
||||
else {
|
||||
proc->parent->cptr = proc->optr;
|
||||
}
|
||||
nr_process --;
|
||||
}
|
||||
|
||||
// get_pid - alloc a unique pid for process
|
||||
static int
|
||||
get_pid(void) {
|
||||
static_assert(MAX_PID > MAX_PROCESS);
|
||||
struct proc_struct *proc;
|
||||
list_entry_t *list = &proc_list, *le;
|
||||
static int next_safe = MAX_PID, last_pid = MAX_PID;
|
||||
if (++ last_pid >= MAX_PID) {
|
||||
last_pid = 1;
|
||||
goto inside;
|
||||
}
|
||||
if (last_pid >= next_safe) {
|
||||
inside:
|
||||
next_safe = MAX_PID;
|
||||
repeat:
|
||||
le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
proc = le2proc(le, list_link);
|
||||
if (proc->pid == last_pid) {
|
||||
if (++ last_pid >= next_safe) {
|
||||
if (last_pid >= MAX_PID) {
|
||||
last_pid = 1;
|
||||
}
|
||||
next_safe = MAX_PID;
|
||||
goto repeat;
|
||||
}
|
||||
}
|
||||
else if (proc->pid > last_pid && next_safe > proc->pid) {
|
||||
next_safe = proc->pid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return last_pid;
|
||||
}
|
||||
|
||||
// proc_run - make process "proc" running on cpu
|
||||
// NOTE: before call switch_to, should load base addr of "proc"'s new PDT
|
||||
void
|
||||
proc_run(struct proc_struct *proc) {
|
||||
if (proc != current) {
|
||||
bool intr_flag;
|
||||
struct proc_struct *prev = current, *next = proc;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
current = proc;
|
||||
load_esp0(next->kstack + KSTACKSIZE);
|
||||
lcr3(next->cr3);
|
||||
switch_to(&(prev->context), &(next->context));
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
}
|
||||
}
|
||||
|
||||
// forkret -- the first kernel entry point of a new thread/process
|
||||
// NOTE: the addr of forkret is setted in copy_thread function
|
||||
// after switch_to, the current proc will execute here.
|
||||
static void
|
||||
forkret(void) {
|
||||
forkrets(current->tf);
|
||||
}
|
||||
|
||||
// hash_proc - add proc into proc hash_list
|
||||
static void
|
||||
hash_proc(struct proc_struct *proc) {
|
||||
list_add(hash_list + pid_hashfn(proc->pid), &(proc->hash_link));
|
||||
}
|
||||
|
||||
// unhash_proc - delete proc from proc hash_list
|
||||
static void
|
||||
unhash_proc(struct proc_struct *proc) {
|
||||
list_del(&(proc->hash_link));
|
||||
}
|
||||
|
||||
// find_proc - find proc frome proc hash_list according to pid
|
||||
struct proc_struct *
|
||||
find_proc(int pid) {
|
||||
if (0 < pid && pid < MAX_PID) {
|
||||
list_entry_t *list = hash_list + pid_hashfn(pid), *le = list;
|
||||
while ((le = list_next(le)) != list) {
|
||||
struct proc_struct *proc = le2proc(le, hash_link);
|
||||
if (proc->pid == pid) {
|
||||
return proc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// kernel_thread - create a kernel thread using "fn" function
|
||||
// NOTE: the contents of temp trapframe tf will be copied to
|
||||
// proc->tf in do_fork-->copy_thread function
|
||||
int
|
||||
kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags) {
|
||||
struct trapframe tf;
|
||||
memset(&tf, 0, sizeof(struct trapframe));
|
||||
tf.tf_cs = KERNEL_CS;
|
||||
tf.tf_ds = tf.tf_es = tf.tf_ss = KERNEL_DS;
|
||||
tf.tf_regs.reg_ebx = (uint32_t)fn;
|
||||
tf.tf_regs.reg_edx = (uint32_t)arg;
|
||||
tf.tf_eip = (uint32_t)kernel_thread_entry;
|
||||
return do_fork(clone_flags | CLONE_VM, 0, &tf);
|
||||
}
|
||||
|
||||
// setup_kstack - alloc pages with size KSTACKPAGE as process kernel stack
|
||||
static int
|
||||
setup_kstack(struct proc_struct *proc) {
|
||||
struct Page *page = alloc_pages(KSTACKPAGE);
|
||||
if (page != NULL) {
|
||||
proc->kstack = (uintptr_t)page2kva(page);
|
||||
return 0;
|
||||
}
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
|
||||
// put_kstack - free the memory space of process kernel stack
|
||||
static void
|
||||
put_kstack(struct proc_struct *proc) {
|
||||
free_pages(kva2page((void *)(proc->kstack)), KSTACKPAGE);
|
||||
}
|
||||
|
||||
// setup_pgdir - alloc one page as PDT
|
||||
static int
|
||||
setup_pgdir(struct mm_struct *mm) {
|
||||
struct Page *page;
|
||||
if ((page = alloc_page()) == NULL) {
|
||||
return -E_NO_MEM;
|
||||
}
|
||||
pde_t *pgdir = page2kva(page);
|
||||
memcpy(pgdir, boot_pgdir, PGSIZE);
|
||||
pgdir[PDX(VPT)] = PADDR(pgdir) | PTE_P | PTE_W;
|
||||
mm->pgdir = pgdir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// put_pgdir - free the memory space of PDT
|
||||
static void
|
||||
put_pgdir(struct mm_struct *mm) {
|
||||
free_page(kva2page(mm->pgdir));
|
||||
}
|
||||
|
||||
// copy_mm - process "proc" duplicate OR share process "current"'s mm according clone_flags
|
||||
// - if clone_flags & CLONE_VM, then "share" ; else "duplicate"
|
||||
static int
|
||||
copy_mm(uint32_t clone_flags, struct proc_struct *proc) {
|
||||
struct mm_struct *mm, *oldmm = current->mm;
|
||||
|
||||
/* current is a kernel thread */
|
||||
if (oldmm == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (clone_flags & CLONE_VM) {
|
||||
mm = oldmm;
|
||||
goto good_mm;
|
||||
}
|
||||
|
||||
int ret = -E_NO_MEM;
|
||||
if ((mm = mm_create()) == NULL) {
|
||||
goto bad_mm;
|
||||
}
|
||||
if (setup_pgdir(mm) != 0) {
|
||||
goto bad_pgdir_cleanup_mm;
|
||||
}
|
||||
|
||||
lock_mm(oldmm);
|
||||
{
|
||||
ret = dup_mmap(mm, oldmm);
|
||||
}
|
||||
unlock_mm(oldmm);
|
||||
|
||||
if (ret != 0) {
|
||||
goto bad_dup_cleanup_mmap;
|
||||
}
|
||||
|
||||
good_mm:
|
||||
mm_count_inc(mm);
|
||||
proc->mm = mm;
|
||||
proc->cr3 = PADDR(mm->pgdir);
|
||||
return 0;
|
||||
bad_dup_cleanup_mmap:
|
||||
exit_mmap(mm);
|
||||
put_pgdir(mm);
|
||||
bad_pgdir_cleanup_mm:
|
||||
mm_destroy(mm);
|
||||
bad_mm:
|
||||
return ret;
|
||||
}
|
||||
|
||||
// copy_thread - setup the trapframe on the process's kernel stack top and
|
||||
// - setup the kernel entry point and stack of process
|
||||
static void
|
||||
copy_thread(struct proc_struct *proc, uintptr_t esp, struct trapframe *tf) {
|
||||
proc->tf = (struct trapframe *)(proc->kstack + KSTACKSIZE) - 1;
|
||||
*(proc->tf) = *tf;
|
||||
proc->tf->tf_regs.reg_eax = 0;
|
||||
proc->tf->tf_esp = esp;
|
||||
proc->tf->tf_eflags |= FL_IF;
|
||||
|
||||
proc->context.eip = (uintptr_t)forkret;
|
||||
proc->context.esp = (uintptr_t)(proc->tf);
|
||||
}
|
||||
|
||||
/* do_fork - parent process for a new child process
|
||||
* @clone_flags: used to guide how to clone the child process
|
||||
* @stack: the parent's user stack pointer. if stack==0, It means to fork a kernel thread.
|
||||
* @tf: the trapframe info, which will be copied to child process's proc->tf
|
||||
*/
|
||||
int
|
||||
do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf) {
|
||||
int ret = -E_NO_FREE_PROC;
|
||||
struct proc_struct *proc;
|
||||
if (nr_process >= MAX_PROCESS) {
|
||||
goto fork_out;
|
||||
}
|
||||
ret = -E_NO_MEM;
|
||||
//LAB4:EXERCISE2 YOUR CODE
|
||||
/*
|
||||
* Some Useful MACROs, Functions and DEFINEs, you can use them in below implementation.
|
||||
* MACROs or Functions:
|
||||
* alloc_proc: create a proc struct and init fields (lab4:exercise1)
|
||||
* setup_kstack: alloc pages with size KSTACKPAGE as process kernel stack
|
||||
* copy_mm: process "proc" duplicate OR share process "current"'s mm according clone_flags
|
||||
* if clone_flags & CLONE_VM, then "share" ; else "duplicate"
|
||||
* copy_thread: setup the trapframe on the process's kernel stack top and
|
||||
* setup the kernel entry point and stack of process
|
||||
* hash_proc: add proc into proc hash_list
|
||||
* get_pid: alloc a unique pid for process
|
||||
* wakup_proc: set proc->state = PROC_RUNNABLE
|
||||
* VARIABLES:
|
||||
* proc_list: the process set's list
|
||||
* nr_process: the number of process set
|
||||
*/
|
||||
|
||||
// 1. call alloc_proc to allocate a proc_struct
|
||||
// 2. call setup_kstack to allocate a kernel stack for child process
|
||||
// 3. call copy_mm to dup OR share mm according clone_flag
|
||||
// 4. call copy_thread to setup tf & context in proc_struct
|
||||
// 5. insert proc_struct into hash_list && proc_list
|
||||
// 6. call wakup_proc to make the new child process RUNNABLE
|
||||
// 7. set ret vaule using child proc's pid
|
||||
if ((proc = alloc_proc()) == NULL) {
|
||||
goto fork_out;
|
||||
}
|
||||
|
||||
proc->parent = current;
|
||||
assert(current->wait_state == 0);
|
||||
|
||||
if (setup_kstack(proc) != 0) {
|
||||
goto bad_fork_cleanup_proc;
|
||||
}
|
||||
if (copy_mm(clone_flags, proc) != 0) {
|
||||
goto bad_fork_cleanup_kstack;
|
||||
}
|
||||
copy_thread(proc, stack, tf);
|
||||
|
||||
bool intr_flag;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
proc->pid = get_pid();
|
||||
hash_proc(proc);
|
||||
set_links(proc);
|
||||
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
|
||||
wakeup_proc(proc);
|
||||
|
||||
ret = proc->pid;
|
||||
fork_out:
|
||||
return ret;
|
||||
|
||||
bad_fork_cleanup_kstack:
|
||||
put_kstack(proc);
|
||||
bad_fork_cleanup_proc:
|
||||
kfree(proc);
|
||||
goto fork_out;
|
||||
}
|
||||
|
||||
// do_exit - called by sys_exit
|
||||
// 1. call exit_mmap & put_pgdir & mm_destroy to free the almost all memory space of process
|
||||
// 2. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself.
|
||||
// 3. call scheduler to switch to other process
|
||||
int
|
||||
do_exit(int error_code) {
|
||||
if (current == idleproc) {
|
||||
panic("idleproc exit.\n");
|
||||
}
|
||||
if (current == initproc) {
|
||||
panic("initproc exit.\n");
|
||||
}
|
||||
|
||||
struct mm_struct *mm = current->mm;
|
||||
if (mm != NULL) {
|
||||
lcr3(boot_cr3);
|
||||
if (mm_count_dec(mm) == 0) {
|
||||
exit_mmap(mm);
|
||||
put_pgdir(mm);
|
||||
mm_destroy(mm);
|
||||
}
|
||||
current->mm = NULL;
|
||||
}
|
||||
current->state = PROC_ZOMBIE;
|
||||
current->exit_code = error_code;
|
||||
|
||||
bool intr_flag;
|
||||
struct proc_struct *proc;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
proc = current->parent;
|
||||
if (proc->wait_state == WT_CHILD) {
|
||||
wakeup_proc(proc);
|
||||
}
|
||||
while (current->cptr != NULL) {
|
||||
proc = current->cptr;
|
||||
current->cptr = proc->optr;
|
||||
|
||||
proc->yptr = NULL;
|
||||
if ((proc->optr = initproc->cptr) != NULL) {
|
||||
initproc->cptr->yptr = proc;
|
||||
}
|
||||
proc->parent = initproc;
|
||||
initproc->cptr = proc;
|
||||
if (proc->state == PROC_ZOMBIE) {
|
||||
if (initproc->wait_state == WT_CHILD) {
|
||||
wakeup_proc(initproc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
|
||||
schedule();
|
||||
panic("do_exit will not return!! %d.\n", current->pid);
|
||||
}
|
||||
|
||||
/* load_icode - load the content of binary program(ELF format) as the new content of current process
|
||||
* @binary: the memory addr of the content of binary program
|
||||
* @size: the size of the content of binary program
|
||||
*/
|
||||
static int
|
||||
load_icode(unsigned char *binary, size_t size) {
|
||||
if (current->mm != NULL) {
|
||||
panic("load_icode: current->mm must be empty.\n");
|
||||
}
|
||||
|
||||
int ret = -E_NO_MEM;
|
||||
struct mm_struct *mm;
|
||||
//(1) create a new mm for current process
|
||||
if ((mm = mm_create()) == NULL) {
|
||||
goto bad_mm;
|
||||
}
|
||||
//(2) create a new PDT, and mm->pgdir= kernel virtual addr of PDT
|
||||
if (setup_pgdir(mm) != 0) {
|
||||
goto bad_pgdir_cleanup_mm;
|
||||
}
|
||||
//(3) copy TEXT/DATA section, build BSS parts in binary to memory space of process
|
||||
struct Page *page;
|
||||
//(3.1) get the file header of the bianry program (ELF format)
|
||||
struct elfhdr *elf = (struct elfhdr *)binary;
|
||||
//(3.2) get the entry of the program section headers of the bianry program (ELF format)
|
||||
struct proghdr *ph = (struct proghdr *)(binary + elf->e_phoff);
|
||||
//(3.3) This program is valid?
|
||||
if (elf->e_magic != ELF_MAGIC) {
|
||||
ret = -E_INVAL_ELF;
|
||||
goto bad_elf_cleanup_pgdir;
|
||||
}
|
||||
|
||||
uint32_t vm_flags, perm;
|
||||
struct proghdr *ph_end = ph + elf->e_phnum;
|
||||
for (; ph < ph_end; ph ++) {
|
||||
//(3.4) find every program section headers
|
||||
if (ph->p_type != ELF_PT_LOAD) {
|
||||
continue ;
|
||||
}
|
||||
if (ph->p_filesz > ph->p_memsz) {
|
||||
ret = -E_INVAL_ELF;
|
||||
goto bad_cleanup_mmap;
|
||||
}
|
||||
if (ph->p_filesz == 0) {
|
||||
continue ;
|
||||
}
|
||||
//(3.5) call mm_map fun to setup the new vma ( ph->p_va, ph->p_memsz)
|
||||
vm_flags = 0, perm = PTE_U;
|
||||
if (ph->p_flags & ELF_PF_X) vm_flags |= VM_EXEC;
|
||||
if (ph->p_flags & ELF_PF_W) vm_flags |= VM_WRITE;
|
||||
if (ph->p_flags & ELF_PF_R) vm_flags |= VM_READ;
|
||||
if (vm_flags & VM_WRITE) perm |= PTE_W;
|
||||
if ((ret = mm_map(mm, ph->p_va, ph->p_memsz, vm_flags, NULL)) != 0) {
|
||||
goto bad_cleanup_mmap;
|
||||
}
|
||||
unsigned char *from = binary + ph->p_offset;
|
||||
size_t off, size;
|
||||
uintptr_t start = ph->p_va, end, la = ROUNDDOWN(start, PGSIZE);
|
||||
|
||||
ret = -E_NO_MEM;
|
||||
|
||||
//(3.6) alloc memory, and copy the contents of every program section (from, from+end) to process's memory (la, la+end)
|
||||
end = ph->p_va + ph->p_filesz;
|
||||
//(3.6.1) copy TEXT/DATA section of bianry program
|
||||
while (start < end) {
|
||||
if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
|
||||
goto bad_cleanup_mmap;
|
||||
}
|
||||
off = start - la, size = PGSIZE - off, la += PGSIZE;
|
||||
if (end < la) {
|
||||
size -= la - end;
|
||||
}
|
||||
memcpy(page2kva(page) + off, from, size);
|
||||
start += size, from += size;
|
||||
}
|
||||
|
||||
//(3.6.2) build BSS section of binary program
|
||||
end = ph->p_va + ph->p_memsz;
|
||||
if (start < la) {
|
||||
/* ph->p_memsz == ph->p_filesz */
|
||||
if (start == end) {
|
||||
continue ;
|
||||
}
|
||||
off = start + PGSIZE - la, size = PGSIZE - off;
|
||||
if (end < la) {
|
||||
size -= la - end;
|
||||
}
|
||||
memset(page2kva(page) + off, 0, size);
|
||||
start += size;
|
||||
assert((end < la && start == end) || (end >= la && start == la));
|
||||
}
|
||||
while (start < end) {
|
||||
if ((page = pgdir_alloc_page(mm->pgdir, la, perm)) == NULL) {
|
||||
goto bad_cleanup_mmap;
|
||||
}
|
||||
off = start - la, size = PGSIZE - off, la += PGSIZE;
|
||||
if (end < la) {
|
||||
size -= la - end;
|
||||
}
|
||||
memset(page2kva(page) + off, 0, size);
|
||||
start += size;
|
||||
}
|
||||
}
|
||||
//(4) build user stack memory
|
||||
vm_flags = VM_READ | VM_WRITE | VM_STACK;
|
||||
if ((ret = mm_map(mm, USTACKTOP - USTACKSIZE, USTACKSIZE, vm_flags, NULL)) != 0) {
|
||||
goto bad_cleanup_mmap;
|
||||
}
|
||||
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-PGSIZE , PTE_USER) != NULL);
|
||||
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-2*PGSIZE , PTE_USER) != NULL);
|
||||
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-3*PGSIZE , PTE_USER) != NULL);
|
||||
assert(pgdir_alloc_page(mm->pgdir, USTACKTOP-4*PGSIZE , PTE_USER) != NULL);
|
||||
|
||||
//(5) set current process's mm, sr3, and set CR3 reg = physical addr of Page Directory
|
||||
mm_count_inc(mm);
|
||||
current->mm = mm;
|
||||
current->cr3 = PADDR(mm->pgdir);
|
||||
lcr3(PADDR(mm->pgdir));
|
||||
|
||||
//(6) setup trapframe for user environment
|
||||
struct trapframe *tf = current->tf;
|
||||
memset(tf, 0, sizeof(struct trapframe));
|
||||
/* LAB5:EXERCISE1 YOUR CODE
|
||||
* should set tf_cs,tf_ds,tf_es,tf_ss,tf_esp,tf_eip,tf_eflags
|
||||
* NOTICE: If we set trapframe correctly, then the user level process can return to USER MODE from kernel. So
|
||||
* tf_cs should be USER_CS segment (see memlayout.h)
|
||||
* tf_ds=tf_es=tf_ss should be USER_DS segment
|
||||
* tf_esp should be the top addr of user stack (USTACKTOP)
|
||||
* tf_eip should be the entry point of this binary program (elf->e_entry)
|
||||
* tf_eflags should be set to enable computer to produce Interrupt
|
||||
*/
|
||||
tf->tf_cs = USER_CS;
|
||||
tf->tf_ds = tf->tf_es = tf->tf_ss = USER_DS;
|
||||
tf->tf_esp = USTACKTOP;
|
||||
tf->tf_eip = elf->e_entry;
|
||||
tf->tf_eflags = FL_IF;
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
bad_cleanup_mmap:
|
||||
exit_mmap(mm);
|
||||
bad_elf_cleanup_pgdir:
|
||||
put_pgdir(mm);
|
||||
bad_pgdir_cleanup_mm:
|
||||
mm_destroy(mm);
|
||||
bad_mm:
|
||||
goto out;
|
||||
}
|
||||
|
||||
// do_execve - call exit_mmap(mm)&pug_pgdir(mm) to reclaim memory space of current process
|
||||
// - call load_icode to setup new memory space accroding binary prog.
|
||||
int
|
||||
do_execve(const char *name, size_t len, unsigned char *binary, size_t size) {
|
||||
struct mm_struct *mm = current->mm;
|
||||
if (!user_mem_check(mm, (uintptr_t)name, len, 0)) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
if (len > PROC_NAME_LEN) {
|
||||
len = PROC_NAME_LEN;
|
||||
}
|
||||
|
||||
char local_name[PROC_NAME_LEN + 1];
|
||||
memset(local_name, 0, sizeof(local_name));
|
||||
memcpy(local_name, name, len);
|
||||
|
||||
if (mm != NULL) {
|
||||
lcr3(boot_cr3);
|
||||
if (mm_count_dec(mm) == 0) {
|
||||
exit_mmap(mm);
|
||||
put_pgdir(mm);
|
||||
mm_destroy(mm);
|
||||
}
|
||||
current->mm = NULL;
|
||||
}
|
||||
int ret;
|
||||
if ((ret = load_icode(binary, size)) != 0) {
|
||||
goto execve_exit;
|
||||
}
|
||||
set_proc_name(current, local_name);
|
||||
return 0;
|
||||
|
||||
execve_exit:
|
||||
do_exit(ret);
|
||||
panic("already exit: %e.\n", ret);
|
||||
}
|
||||
|
||||
// do_yield - ask the scheduler to reschedule
|
||||
int
|
||||
do_yield(void) {
|
||||
current->need_resched = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// do_wait - wait one OR any children with PROC_ZOMBIE state, and free memory space of kernel stack
|
||||
// - proc struct of this child.
|
||||
// NOTE: only after do_wait function, all resources of the child proces are free.
|
||||
int
|
||||
do_wait(int pid, int *code_store) {
|
||||
struct mm_struct *mm = current->mm;
|
||||
if (code_store != NULL) {
|
||||
if (!user_mem_check(mm, (uintptr_t)code_store, sizeof(int), 1)) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
struct proc_struct *proc;
|
||||
bool intr_flag, haskid;
|
||||
repeat:
|
||||
haskid = 0;
|
||||
if (pid != 0) {
|
||||
proc = find_proc(pid);
|
||||
if (proc != NULL && proc->parent == current) {
|
||||
haskid = 1;
|
||||
if (proc->state == PROC_ZOMBIE) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
proc = current->cptr;
|
||||
for (; proc != NULL; proc = proc->optr) {
|
||||
haskid = 1;
|
||||
if (proc->state == PROC_ZOMBIE) {
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (haskid) {
|
||||
current->state = PROC_SLEEPING;
|
||||
current->wait_state = WT_CHILD;
|
||||
schedule();
|
||||
if (current->flags & PF_EXITING) {
|
||||
do_exit(-E_KILLED);
|
||||
}
|
||||
goto repeat;
|
||||
}
|
||||
return -E_BAD_PROC;
|
||||
|
||||
found:
|
||||
if (proc == idleproc || proc == initproc) {
|
||||
panic("wait idleproc or initproc.\n");
|
||||
}
|
||||
if (code_store != NULL) {
|
||||
*code_store = proc->exit_code;
|
||||
}
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
unhash_proc(proc);
|
||||
remove_links(proc);
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
put_kstack(proc);
|
||||
kfree(proc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// do_kill - kill process with pid by set this process's flags with PF_EXITING
|
||||
int
|
||||
do_kill(int pid) {
|
||||
struct proc_struct *proc;
|
||||
if ((proc = find_proc(pid)) != NULL) {
|
||||
if (!(proc->flags & PF_EXITING)) {
|
||||
proc->flags |= PF_EXITING;
|
||||
if (proc->wait_state & WT_INTERRUPTED) {
|
||||
wakeup_proc(proc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -E_KILLED;
|
||||
}
|
||||
return -E_INVAL;
|
||||
}
|
||||
|
||||
// kernel_execve - do SYS_exec syscall to exec a user program called by user_main kernel_thread
|
||||
static int
|
||||
kernel_execve(const char *name, unsigned char *binary, size_t size) {
|
||||
int ret, len = strlen(name);
|
||||
asm volatile (
|
||||
"int %1;"
|
||||
: "=a" (ret)
|
||||
: "i" (T_SYSCALL), "0" (SYS_exec), "d" (name), "c" (len), "b" (binary), "D" (size)
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define __KERNEL_EXECVE(name, binary, size) ({ \
|
||||
cprintf("kernel_execve: pid = %d, name = \"%s\".\n", \
|
||||
current->pid, name); \
|
||||
kernel_execve(name, binary, (size_t)(size)); \
|
||||
})
|
||||
|
||||
#define KERNEL_EXECVE(x) ({ \
|
||||
extern unsigned char _binary_obj___user_##x##_out_start[], \
|
||||
_binary_obj___user_##x##_out_size[]; \
|
||||
__KERNEL_EXECVE(#x, _binary_obj___user_##x##_out_start, \
|
||||
_binary_obj___user_##x##_out_size); \
|
||||
})
|
||||
|
||||
#define __KERNEL_EXECVE2(x, xstart, xsize) ({ \
|
||||
extern unsigned char xstart[], xsize[]; \
|
||||
__KERNEL_EXECVE(#x, xstart, (size_t)xsize); \
|
||||
})
|
||||
|
||||
#define KERNEL_EXECVE2(x, xstart, xsize) __KERNEL_EXECVE2(x, xstart, xsize)
|
||||
|
||||
// user_main - kernel thread used to exec a user program
|
||||
static int
|
||||
user_main(void *arg) {
|
||||
#ifdef TEST
|
||||
KERNEL_EXECVE2(TEST, TESTSTART, TESTSIZE);
|
||||
#else
|
||||
KERNEL_EXECVE(exit);
|
||||
#endif
|
||||
panic("user_main execve failed.\n");
|
||||
}
|
||||
|
||||
// init_main - the second kernel thread used to create user_main kernel threads
|
||||
static int
|
||||
init_main(void *arg) {
|
||||
size_t nr_free_pages_store = nr_free_pages();
|
||||
size_t kernel_allocated_store = kallocated();
|
||||
|
||||
int pid = kernel_thread(user_main, NULL, 0);
|
||||
if (pid <= 0) {
|
||||
panic("create user_main failed.\n");
|
||||
}
|
||||
|
||||
while (do_wait(0, NULL) == 0) {
|
||||
schedule();
|
||||
}
|
||||
|
||||
cprintf("all user-mode processes have quit.\n");
|
||||
assert(initproc->cptr == NULL && initproc->yptr == NULL && initproc->optr == NULL);
|
||||
assert(nr_process == 2);
|
||||
assert(list_next(&proc_list) == &(initproc->list_link));
|
||||
assert(list_prev(&proc_list) == &(initproc->list_link));
|
||||
assert(nr_free_pages_store == nr_free_pages());
|
||||
assert(kernel_allocated_store == kallocated());
|
||||
cprintf("init check memory pass.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// proc_init - set up the first kernel thread idleproc "idle" by itself and
|
||||
// - create the second kernel thread init_main
|
||||
void
|
||||
proc_init(void) {
|
||||
int i;
|
||||
|
||||
list_init(&proc_list);
|
||||
for (i = 0; i < HASH_LIST_SIZE; i ++) {
|
||||
list_init(hash_list + i);
|
||||
}
|
||||
|
||||
if ((idleproc = alloc_proc()) == NULL) {
|
||||
panic("cannot alloc idleproc.\n");
|
||||
}
|
||||
|
||||
idleproc->pid = 0;
|
||||
idleproc->state = PROC_RUNNABLE;
|
||||
idleproc->kstack = (uintptr_t)bootstack;
|
||||
idleproc->need_resched = 1;
|
||||
set_proc_name(idleproc, "idle");
|
||||
nr_process ++;
|
||||
|
||||
current = idleproc;
|
||||
|
||||
int pid = kernel_thread(init_main, NULL, 0);
|
||||
if (pid <= 0) {
|
||||
panic("create init_main failed.\n");
|
||||
}
|
||||
|
||||
initproc = find_proc(pid);
|
||||
set_proc_name(initproc, "init");
|
||||
|
||||
assert(idleproc != NULL && idleproc->pid == 0);
|
||||
assert(initproc != NULL && initproc->pid == 1);
|
||||
}
|
||||
|
||||
// cpu_idle - at the end of kern_init, the first kernel thread idleproc will do below works
|
||||
void
|
||||
cpu_idle(void) {
|
||||
while (1) {
|
||||
if (current->need_resched) {
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
89
related_info/lab5/lab5-spoc-discuss/kern/process/proc.h
Normal file
89
related_info/lab5/lab5-spoc-discuss/kern/process/proc.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef __KERN_PROCESS_PROC_H__
|
||||
#define __KERN_PROCESS_PROC_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <list.h>
|
||||
#include <trap.h>
|
||||
#include <memlayout.h>
|
||||
|
||||
|
||||
// process's state in his life cycle
|
||||
enum proc_state {
|
||||
PROC_UNINIT = 0, // uninitialized
|
||||
PROC_SLEEPING, // sleeping
|
||||
PROC_RUNNABLE, // runnable(maybe running)
|
||||
PROC_ZOMBIE, // almost dead, and wait parent proc to reclaim his resource
|
||||
};
|
||||
|
||||
// Saved registers for kernel context switches.
|
||||
// Don't need to save all the %fs etc. segment registers,
|
||||
// because they are constant across kernel contexts.
|
||||
// Save all the regular registers so we don't need to care
|
||||
// which are caller save, but not the return register %eax.
|
||||
// (Not saving %eax just simplifies the switching code.)
|
||||
// The layout of context must match code in switch.S.
|
||||
struct context {
|
||||
uint32_t eip;
|
||||
uint32_t esp;
|
||||
uint32_t ebx;
|
||||
uint32_t ecx;
|
||||
uint32_t edx;
|
||||
uint32_t esi;
|
||||
uint32_t edi;
|
||||
uint32_t ebp;
|
||||
};
|
||||
|
||||
#define PROC_NAME_LEN 15
|
||||
#define MAX_PROCESS 4096
|
||||
#define MAX_PID (MAX_PROCESS * 2)
|
||||
|
||||
extern list_entry_t proc_list;
|
||||
|
||||
struct proc_struct {
|
||||
enum proc_state state; // Process state
|
||||
int pid; // Process ID
|
||||
int runs; // the running times of Proces
|
||||
uintptr_t kstack; // Process kernel stack
|
||||
volatile bool need_resched; // bool value: need to be rescheduled to release CPU?
|
||||
struct proc_struct *parent; // the parent process
|
||||
struct mm_struct *mm; // Process's memory management field
|
||||
struct context context; // Switch here to run process
|
||||
struct trapframe *tf; // Trap frame for current interrupt
|
||||
uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT)
|
||||
uint32_t flags; // Process flag
|
||||
char name[PROC_NAME_LEN + 1]; // Process name
|
||||
list_entry_t list_link; // Process link list
|
||||
list_entry_t hash_link; // Process hash list
|
||||
int exit_code; // exit code (be sent to parent proc)
|
||||
uint32_t wait_state; // waiting state
|
||||
struct proc_struct *cptr, *yptr, *optr; // relations between processes
|
||||
};
|
||||
|
||||
#define PF_EXITING 0x00000001 // getting shutdown
|
||||
|
||||
#define WT_CHILD (0x00000001 | WT_INTERRUPTED)
|
||||
#define WT_INTERRUPTED 0x80000000 // the wait state could be interrupted
|
||||
|
||||
|
||||
#define le2proc(le, member) \
|
||||
to_struct((le), struct proc_struct, member)
|
||||
|
||||
extern struct proc_struct *idleproc, *initproc, *current;
|
||||
|
||||
void proc_init(void);
|
||||
void proc_run(struct proc_struct *proc);
|
||||
int kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags);
|
||||
|
||||
char *set_proc_name(struct proc_struct *proc, const char *name);
|
||||
char *get_proc_name(struct proc_struct *proc);
|
||||
void cpu_idle(void) __attribute__((noreturn));
|
||||
|
||||
struct proc_struct *find_proc(int pid);
|
||||
int do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf);
|
||||
int do_exit(int error_code);
|
||||
int do_yield(void);
|
||||
int do_execve(const char *name, size_t len, unsigned char *binary, size_t size);
|
||||
int do_wait(int pid, int *code_store);
|
||||
int do_kill(int pid);
|
||||
#endif /* !__KERN_PROCESS_PROC_H__ */
|
||||
|
30
related_info/lab5/lab5-spoc-discuss/kern/process/switch.S
Normal file
30
related_info/lab5/lab5-spoc-discuss/kern/process/switch.S
Normal file
@ -0,0 +1,30 @@
|
||||
.text
|
||||
.globl switch_to
|
||||
switch_to: # switch_to(from, to)
|
||||
|
||||
# save from's registers
|
||||
movl 4(%esp), %eax # eax points to from
|
||||
popl 0(%eax) # save eip !popl
|
||||
movl %esp, 4(%eax)
|
||||
movl %ebx, 8(%eax)
|
||||
movl %ecx, 12(%eax)
|
||||
movl %edx, 16(%eax)
|
||||
movl %esi, 20(%eax)
|
||||
movl %edi, 24(%eax)
|
||||
movl %ebp, 28(%eax)
|
||||
|
||||
# restore to's registers
|
||||
movl 4(%esp), %eax # not 8(%esp): popped return address already
|
||||
# eax now points to to
|
||||
movl 28(%eax), %ebp
|
||||
movl 24(%eax), %edi
|
||||
movl 20(%eax), %esi
|
||||
movl 16(%eax), %edx
|
||||
movl 12(%eax), %ecx
|
||||
movl 8(%eax), %ebx
|
||||
movl 4(%eax), %esp
|
||||
|
||||
pushl 0(%eax) # push eip
|
||||
|
||||
ret
|
||||
|
52
related_info/lab5/lab5-spoc-discuss/kern/schedule/sched.c
Normal file
52
related_info/lab5/lab5-spoc-discuss/kern/schedule/sched.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include <list.h>
|
||||
#include <sync.h>
|
||||
#include <proc.h>
|
||||
#include <sched.h>
|
||||
#include <assert.h>
|
||||
|
||||
void
|
||||
wakeup_proc(struct proc_struct *proc) {
|
||||
assert(proc->state != PROC_ZOMBIE);
|
||||
bool intr_flag;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
if (proc->state != PROC_RUNNABLE) {
|
||||
proc->state = PROC_RUNNABLE;
|
||||
proc->wait_state = 0;
|
||||
}
|
||||
else {
|
||||
warn("wakeup runnable process.\n");
|
||||
}
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
}
|
||||
|
||||
void
|
||||
schedule(void) {
|
||||
bool intr_flag;
|
||||
list_entry_t *le, *last;
|
||||
struct proc_struct *next = NULL;
|
||||
local_intr_save(intr_flag);
|
||||
{
|
||||
current->need_resched = 0;
|
||||
last = (current == idleproc) ? &proc_list : &(current->list_link);
|
||||
le = last;
|
||||
do {
|
||||
if ((le = list_next(le)) != &proc_list) {
|
||||
next = le2proc(le, list_link);
|
||||
if (next->state == PROC_RUNNABLE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (le != last);
|
||||
if (next == NULL || next->state != PROC_RUNNABLE) {
|
||||
next = idleproc;
|
||||
}
|
||||
next->runs ++;
|
||||
if (next != current) {
|
||||
proc_run(next);
|
||||
}
|
||||
}
|
||||
local_intr_restore(intr_flag);
|
||||
}
|
||||
|
10
related_info/lab5/lab5-spoc-discuss/kern/schedule/sched.h
Normal file
10
related_info/lab5/lab5-spoc-discuss/kern/schedule/sched.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef __KERN_SCHEDULE_SCHED_H__
|
||||
#define __KERN_SCHEDULE_SCHED_H__
|
||||
|
||||
#include <proc.h>
|
||||
|
||||
void schedule(void);
|
||||
void wakeup_proc(struct proc_struct *proc);
|
||||
|
||||
#endif /* !__KERN_SCHEDULE_SCHED_H__ */
|
||||
|
57
related_info/lab5/lab5-spoc-discuss/kern/sync/sync.h
Normal file
57
related_info/lab5/lab5-spoc-discuss/kern/sync/sync.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef __KERN_SYNC_SYNC_H__
|
||||
#define __KERN_SYNC_SYNC_H__
|
||||
|
||||
#include <x86.h>
|
||||
#include <intr.h>
|
||||
#include <mmu.h>
|
||||
#include <assert.h>
|
||||
#include <atomic.h>
|
||||
#include <sched.h>
|
||||
|
||||
static inline bool
|
||||
__intr_save(void) {
|
||||
if (read_eflags() & FL_IF) {
|
||||
intr_disable();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
__intr_restore(bool flag) {
|
||||
if (flag) {
|
||||
intr_enable();
|
||||
}
|
||||
}
|
||||
|
||||
#define local_intr_save(x) do { x = __intr_save(); } while (0)
|
||||
#define local_intr_restore(x) __intr_restore(x);
|
||||
|
||||
typedef volatile bool lock_t;
|
||||
|
||||
static inline void
|
||||
lock_init(lock_t *lock) {
|
||||
*lock = 0;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
try_lock(lock_t *lock) {
|
||||
return !test_and_set_bit(0, lock);
|
||||
}
|
||||
|
||||
static inline void
|
||||
lock(lock_t *lock) {
|
||||
while (!try_lock(lock)) {
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
unlock(lock_t *lock) {
|
||||
if (!test_and_clear_bit(0, lock)) {
|
||||
panic("Unlock failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* !__KERN_SYNC_SYNC_H__ */
|
||||
|
101
related_info/lab5/lab5-spoc-discuss/kern/syscall/syscall.c
Normal file
101
related_info/lab5/lab5-spoc-discuss/kern/syscall/syscall.c
Normal file
@ -0,0 +1,101 @@
|
||||
#include <unistd.h>
|
||||
#include <proc.h>
|
||||
#include <syscall.h>
|
||||
#include <trap.h>
|
||||
#include <stdio.h>
|
||||
#include <pmm.h>
|
||||
#include <assert.h>
|
||||
|
||||
static int
|
||||
sys_exit(uint32_t arg[]) {
|
||||
int error_code = (int)arg[0];
|
||||
return do_exit(error_code);
|
||||
}
|
||||
|
||||
static int
|
||||
sys_fork(uint32_t arg[]) {
|
||||
struct trapframe *tf = current->tf;
|
||||
uintptr_t stack = tf->tf_esp;
|
||||
return do_fork(0, stack, tf);
|
||||
}
|
||||
|
||||
static int
|
||||
sys_wait(uint32_t arg[]) {
|
||||
int pid = (int)arg[0];
|
||||
int *store = (int *)arg[1];
|
||||
return do_wait(pid, store);
|
||||
}
|
||||
|
||||
static int
|
||||
sys_exec(uint32_t arg[]) {
|
||||
const char *name = (const char *)arg[0];
|
||||
size_t len = (size_t)arg[1];
|
||||
unsigned char *binary = (unsigned char *)arg[2];
|
||||
size_t size = (size_t)arg[3];
|
||||
return do_execve(name, len, binary, size);
|
||||
}
|
||||
|
||||
static int
|
||||
sys_yield(uint32_t arg[]) {
|
||||
return do_yield();
|
||||
}
|
||||
|
||||
static int
|
||||
sys_kill(uint32_t arg[]) {
|
||||
int pid = (int)arg[0];
|
||||
return do_kill(pid);
|
||||
}
|
||||
|
||||
static int
|
||||
sys_getpid(uint32_t arg[]) {
|
||||
return current->pid;
|
||||
}
|
||||
|
||||
static int
|
||||
sys_putc(uint32_t arg[]) {
|
||||
int c = (int)arg[0];
|
||||
cputchar(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sys_pgdir(uint32_t arg[]) {
|
||||
print_pgdir();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*syscalls[])(uint32_t arg[]) = {
|
||||
[SYS_exit] sys_exit,
|
||||
[SYS_fork] sys_fork,
|
||||
[SYS_wait] sys_wait,
|
||||
[SYS_exec] sys_exec,
|
||||
[SYS_yield] sys_yield,
|
||||
[SYS_kill] sys_kill,
|
||||
[SYS_getpid] sys_getpid,
|
||||
[SYS_putc] sys_putc,
|
||||
[SYS_pgdir] sys_pgdir,
|
||||
};
|
||||
|
||||
#define NUM_SYSCALLS ((sizeof(syscalls)) / (sizeof(syscalls[0])))
|
||||
|
||||
void
|
||||
syscall(void) {
|
||||
struct trapframe *tf = current->tf;
|
||||
uint32_t arg[5];
|
||||
int num = tf->tf_regs.reg_eax;
|
||||
if (num >= 0 && num < NUM_SYSCALLS) {
|
||||
if (syscalls[num] != NULL) {
|
||||
arg[0] = tf->tf_regs.reg_edx;
|
||||
arg[1] = tf->tf_regs.reg_ecx;
|
||||
arg[2] = tf->tf_regs.reg_ebx;
|
||||
arg[3] = tf->tf_regs.reg_edi;
|
||||
arg[4] = tf->tf_regs.reg_esi;
|
||||
tf->tf_regs.reg_eax = syscalls[num](arg);
|
||||
return ;
|
||||
}
|
||||
}
|
||||
print_trapframe(tf);
|
||||
panic("undefined syscall %d, pid = %d, name = %s.\n",
|
||||
num, current->pid, current->name);
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
#ifndef __KERN_SYSCALL_SYSCALL_H__
|
||||
#define __KERN_SYSCALL_SYSCALL_H__
|
||||
|
||||
void syscall(void);
|
||||
|
||||
#endif /* !__KERN_SYSCALL_SYSCALL_H__ */
|
||||
|
300
related_info/lab5/lab5-spoc-discuss/kern/trap/trap.c
Normal file
300
related_info/lab5/lab5-spoc-discuss/kern/trap/trap.c
Normal file
@ -0,0 +1,300 @@
|
||||
#include <defs.h>
|
||||
#include <mmu.h>
|
||||
#include <memlayout.h>
|
||||
#include <clock.h>
|
||||
#include <trap.h>
|
||||
#include <x86.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <console.h>
|
||||
#include <vmm.h>
|
||||
#include <swap.h>
|
||||
#include <kdebug.h>
|
||||
#include <unistd.h>
|
||||
#include <syscall.h>
|
||||
#include <error.h>
|
||||
#include <sched.h>
|
||||
#include <sync.h>
|
||||
|
||||
#define TICK_NUM 100
|
||||
|
||||
static void print_ticks() {
|
||||
cprintf("%d ticks\n",TICK_NUM);
|
||||
#ifdef DEBUG_GRADE
|
||||
cprintf("End of Test.\n");
|
||||
panic("EOT: kernel seems ok.");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* *
|
||||
* Interrupt descriptor table:
|
||||
*
|
||||
* Must be built at run time because shifted function addresses can't
|
||||
* be represented in relocation records.
|
||||
* */
|
||||
static struct gatedesc idt[256] = {{0}};
|
||||
|
||||
static struct pseudodesc idt_pd = {
|
||||
sizeof(idt) - 1, (uintptr_t)idt
|
||||
};
|
||||
|
||||
/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
|
||||
void
|
||||
idt_init(void) {
|
||||
/* LAB1 YOUR CODE : STEP 2 */
|
||||
/* (1) Where are the entry addrs of each Interrupt Service Routine (ISR)?
|
||||
* All ISR's entry addrs are stored in __vectors. where is uintptr_t __vectors[] ?
|
||||
* __vectors[] is in kern/trap/vector.S which is produced by tools/vector.c
|
||||
* (try "make" command in lab1, then you will find vector.S in kern/trap DIR)
|
||||
* You can use "extern uintptr_t __vectors[];" to define this extern variable which will be used later.
|
||||
* (2) Now you should setup the entries of ISR in Interrupt Description Table (IDT).
|
||||
* Can you see idt[256] in this file? Yes, it's IDT! you can use SETGATE macro to setup each item of IDT
|
||||
* (3) After setup the contents of IDT, you will let CPU know where is the IDT by using 'lidt' instruction.
|
||||
* You don't know the meaning of this instruction? just google it! and check the libs/x86.h to know more.
|
||||
* Notice: the argument of lidt is idt_pd. try to find it!
|
||||
*/
|
||||
/* LAB5 YOUR CODE */
|
||||
//you should update your lab1 code (just add ONE or TWO lines of code), let user app to use syscall to get the service of ucore
|
||||
//so you should setup the syscall interrupt gate in here
|
||||
extern uintptr_t __vectors[];
|
||||
int i;
|
||||
for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
|
||||
SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
|
||||
}
|
||||
SETGATE(idt[T_SYSCALL], 1, GD_KTEXT, __vectors[T_SYSCALL], DPL_USER);
|
||||
lidt(&idt_pd);
|
||||
}
|
||||
|
||||
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 < sizeof(excnames)/sizeof(const char * const)) {
|
||||
return excnames[trapno];
|
||||
}
|
||||
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) {
|
||||
return "Hardware Interrupt";
|
||||
}
|
||||
return "(unknown trap)";
|
||||
}
|
||||
|
||||
/* trap_in_kernel - test if trap happened in kernel */
|
||||
bool
|
||||
trap_in_kernel(struct trapframe *tf) {
|
||||
return (tf->tf_cs == (uint16_t)KERNEL_CS);
|
||||
}
|
||||
|
||||
static const char *IA32flags[] = {
|
||||
"CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF",
|
||||
"TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL,
|
||||
"RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL,
|
||||
};
|
||||
|
||||
void
|
||||
print_trapframe(struct trapframe *tf) {
|
||||
cprintf("trapframe at %p\n", tf);
|
||||
print_regs(&tf->tf_regs);
|
||||
cprintf(" ds 0x----%04x\n", tf->tf_ds);
|
||||
cprintf(" es 0x----%04x\n", tf->tf_es);
|
||||
cprintf(" fs 0x----%04x\n", tf->tf_fs);
|
||||
cprintf(" gs 0x----%04x\n", tf->tf_gs);
|
||||
cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
|
||||
cprintf(" err 0x%08x\n", tf->tf_err);
|
||||
cprintf(" eip 0x%08x\n", tf->tf_eip);
|
||||
cprintf(" cs 0x----%04x\n", tf->tf_cs);
|
||||
cprintf(" flag 0x%08x ", tf->tf_eflags);
|
||||
|
||||
int i, j;
|
||||
for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) {
|
||||
if ((tf->tf_eflags & j) && IA32flags[i] != NULL) {
|
||||
cprintf("%s,", IA32flags[i]);
|
||||
}
|
||||
}
|
||||
cprintf("IOPL=%d\n", (tf->tf_eflags & FL_IOPL_MASK) >> 12);
|
||||
|
||||
if (!trap_in_kernel(tf)) {
|
||||
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 inline void
|
||||
print_pgfault(struct trapframe *tf) {
|
||||
/* error_code:
|
||||
* bit 0 == 0 means no page found, 1 means protection fault
|
||||
* bit 1 == 0 means read, 1 means write
|
||||
* bit 2 == 0 means kernel, 1 means user
|
||||
* */
|
||||
cprintf("page fault at 0x%08x: %c/%c [%s].\n", rcr2(),
|
||||
(tf->tf_err & 4) ? 'U' : 'K',
|
||||
(tf->tf_err & 2) ? 'W' : 'R',
|
||||
(tf->tf_err & 1) ? "protection fault" : "no page found");
|
||||
}
|
||||
|
||||
static int
|
||||
pgfault_handler(struct trapframe *tf) {
|
||||
extern struct mm_struct *check_mm_struct;
|
||||
if(check_mm_struct !=NULL) { //used for test check_swap
|
||||
print_pgfault(tf);
|
||||
}
|
||||
struct mm_struct *mm;
|
||||
if (check_mm_struct != NULL) {
|
||||
assert(current == idleproc);
|
||||
mm = check_mm_struct;
|
||||
}
|
||||
else {
|
||||
if (current == NULL) {
|
||||
print_trapframe(tf);
|
||||
print_pgfault(tf);
|
||||
panic("unhandled page fault.\n");
|
||||
}
|
||||
mm = current->mm;
|
||||
}
|
||||
return do_pgfault(mm, tf->tf_err, rcr2());
|
||||
}
|
||||
|
||||
static volatile int in_swap_tick_event = 0;
|
||||
extern struct mm_struct *check_mm_struct;
|
||||
|
||||
static void
|
||||
trap_dispatch(struct trapframe *tf) {
|
||||
char c;
|
||||
|
||||
int ret=0;
|
||||
|
||||
switch (tf->tf_trapno) {
|
||||
case T_PGFLT: //page fault
|
||||
if ((ret = pgfault_handler(tf)) != 0) {
|
||||
print_trapframe(tf);
|
||||
if (current == NULL) {
|
||||
panic("handle pgfault failed. ret=%d\n", ret);
|
||||
}
|
||||
else {
|
||||
if (trap_in_kernel(tf)) {
|
||||
panic("handle pgfault failed in kernel mode. ret=%d\n", ret);
|
||||
}
|
||||
cprintf("killed by kernel.\n");
|
||||
panic("handle user mode pgfault failed. ret=%d\n", ret);
|
||||
do_exit(-E_KILLED);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case T_SYSCALL:
|
||||
syscall();
|
||||
break;
|
||||
case IRQ_OFFSET + IRQ_TIMER:
|
||||
#if 0
|
||||
LAB3 : If some page replacement algorithm(such as CLOCK PRA) need tick to change the priority of pages,
|
||||
then you can add code here.
|
||||
#endif
|
||||
/* LAB1 YOUR CODE : STEP 3 */
|
||||
/* handle the timer interrupt */
|
||||
/* (1) After a timer interrupt, you should record this event using a global variable (increase it), such as ticks in kern/driver/clock.c
|
||||
* (2) Every TICK_NUM cycle, you can print some info using a funciton, such as print_ticks().
|
||||
* (3) Too Simple? Yes, I think so!
|
||||
*/
|
||||
/* LAB5 YOUR CODE */
|
||||
/* you should upate you lab1 code (just add ONE or TWO lines of code):
|
||||
* Every TICK_NUM cycle, you should set current process's current->need_resched = 1
|
||||
*/
|
||||
ticks ++;
|
||||
if (ticks % TICK_NUM == 0) {
|
||||
assert(current != NULL);
|
||||
current->need_resched = 1;
|
||||
}
|
||||
break;
|
||||
case IRQ_OFFSET + IRQ_COM1:
|
||||
c = cons_getc();
|
||||
cprintf("serial [%03d] %c\n", c, c);
|
||||
break;
|
||||
case IRQ_OFFSET + IRQ_KBD:
|
||||
c = cons_getc();
|
||||
cprintf("kbd [%03d] %c\n", c, c);
|
||||
break;
|
||||
//LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes.
|
||||
case T_SWITCH_TOU:
|
||||
case T_SWITCH_TOK:
|
||||
panic("T_SWITCH_** ??\n");
|
||||
break;
|
||||
case IRQ_OFFSET + IRQ_IDE1:
|
||||
case IRQ_OFFSET + IRQ_IDE2:
|
||||
/* do nothing */
|
||||
break;
|
||||
default:
|
||||
print_trapframe(tf);
|
||||
if (current != NULL) {
|
||||
cprintf("unhandled trap.\n");
|
||||
do_exit(-E_KILLED);
|
||||
}
|
||||
// in kernel, it must be a mistake
|
||||
panic("unexpected trap in kernel.\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* trap - handles or dispatches an exception/interrupt. if and when trap() returns,
|
||||
* the code in kern/trap/trapentry.S restores the old CPU state saved in the
|
||||
* trapframe and then uses the iret instruction to return from the exception.
|
||||
* */
|
||||
void
|
||||
trap(struct trapframe *tf) {
|
||||
// dispatch based on what type of trap occurred
|
||||
// used for previous projects
|
||||
if (current == NULL) {
|
||||
trap_dispatch(tf);
|
||||
}
|
||||
else {
|
||||
// keep a trapframe chain in stack
|
||||
struct trapframe *otf = current->tf;
|
||||
current->tf = tf;
|
||||
|
||||
bool in_kernel = trap_in_kernel(tf);
|
||||
|
||||
trap_dispatch(tf);
|
||||
|
||||
current->tf = otf;
|
||||
if (!in_kernel) {
|
||||
if (current->flags & PF_EXITING) {
|
||||
do_exit(-E_KILLED);
|
||||
}
|
||||
if (current->need_resched) {
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
89
related_info/lab5/lab5-spoc-discuss/kern/trap/trap.h
Normal file
89
related_info/lab5/lab5-spoc-discuss/kern/trap/trap.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef __KERN_TRAP_TRAP_H__
|
||||
#define __KERN_TRAP_TRAP_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
/* Trap Numbers */
|
||||
|
||||
/* 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 used since 486)
|
||||
#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
|
||||
|
||||
/* Hardware IRQ numbers. We receive these as (IRQ_OFFSET + IRQ_xx) */
|
||||
#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET
|
||||
|
||||
#define IRQ_TIMER 0
|
||||
#define IRQ_KBD 1
|
||||
#define IRQ_COM1 4
|
||||
#define IRQ_IDE1 14
|
||||
#define IRQ_IDE2 15
|
||||
#define IRQ_ERROR 19
|
||||
#define IRQ_SPURIOUS 31
|
||||
|
||||
/* *
|
||||
* These are arbitrarily chosen, but with care not to overlap
|
||||
* processor defined exceptions or interrupt vectors.
|
||||
* */
|
||||
#define T_SWITCH_TOU 120 // user/kernel switch
|
||||
#define T_SWITCH_TOK 121 // user/kernel switch
|
||||
|
||||
/* registers as pushed by pushal */
|
||||
struct pushregs {
|
||||
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;
|
||||
};
|
||||
|
||||
struct trapframe {
|
||||
struct pushregs tf_regs;
|
||||
uint16_t tf_gs;
|
||||
uint16_t tf_padding0;
|
||||
uint16_t tf_fs;
|
||||
uint16_t tf_padding1;
|
||||
uint16_t tf_es;
|
||||
uint16_t tf_padding2;
|
||||
uint16_t tf_ds;
|
||||
uint16_t tf_padding3;
|
||||
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_padding4;
|
||||
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_padding5;
|
||||
} __attribute__((packed));
|
||||
|
||||
void idt_init(void);
|
||||
void print_trapframe(struct trapframe *tf);
|
||||
void print_regs(struct pushregs *regs);
|
||||
bool trap_in_kernel(struct trapframe *tf);
|
||||
|
||||
#endif /* !__KERN_TRAP_TRAP_H__ */
|
||||
|
49
related_info/lab5/lab5-spoc-discuss/kern/trap/trapentry.S
Normal file
49
related_info/lab5/lab5-spoc-discuss/kern/trap/trapentry.S
Normal file
@ -0,0 +1,49 @@
|
||||
#include <memlayout.h>
|
||||
|
||||
# vectors.S sends all traps here.
|
||||
.text
|
||||
.globl __alltraps
|
||||
__alltraps:
|
||||
# push registers to build a trap frame
|
||||
# therefore make the stack look like a struct trapframe
|
||||
pushl %ds
|
||||
pushl %es
|
||||
pushl %fs
|
||||
pushl %gs
|
||||
pushal
|
||||
|
||||
# load GD_KDATA into %ds and %es to set up data segments for kernel
|
||||
movl $GD_KDATA, %eax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
|
||||
# push %esp to pass a pointer to the trapframe as an argument to trap()
|
||||
pushl %esp
|
||||
|
||||
# call trap(tf), where tf=%esp
|
||||
call trap
|
||||
|
||||
# pop the pushed stack pointer
|
||||
popl %esp
|
||||
|
||||
# return falls through to trapret...
|
||||
.globl __trapret
|
||||
__trapret:
|
||||
# restore registers from stack
|
||||
popal
|
||||
|
||||
# restore %ds, %es, %fs and %gs
|
||||
popl %gs
|
||||
popl %fs
|
||||
popl %es
|
||||
popl %ds
|
||||
|
||||
# get rid of the trap number and error code
|
||||
addl $0x8, %esp
|
||||
iret
|
||||
|
||||
.globl forkrets
|
||||
forkrets:
|
||||
# set stack to this new process's trapframe
|
||||
movl 4(%esp), %esp
|
||||
jmp __trapret
|
1536
related_info/lab5/lab5-spoc-discuss/kern/trap/vectors.S
Normal file
1536
related_info/lab5/lab5-spoc-discuss/kern/trap/vectors.S
Normal file
File diff suppressed because it is too large
Load Diff
81
related_info/lab5/lab5-spoc-discuss/libs/atomic.h
Normal file
81
related_info/lab5/lab5-spoc-discuss/libs/atomic.h
Normal file
@ -0,0 +1,81 @@
|
||||
#ifndef __LIBS_ATOMIC_H__
|
||||
#define __LIBS_ATOMIC_H__
|
||||
|
||||
/* Atomic operations that C can't guarantee us. Useful for resource counting etc.. */
|
||||
|
||||
static inline void set_bit(int nr, volatile void *addr) __attribute__((always_inline));
|
||||
static inline void clear_bit(int nr, volatile void *addr) __attribute__((always_inline));
|
||||
static inline void change_bit(int nr, volatile void *addr) __attribute__((always_inline));
|
||||
static inline bool test_bit(int nr, volatile void *addr) __attribute__((always_inline));
|
||||
|
||||
/* *
|
||||
* set_bit - Atomically set a bit in memory
|
||||
* @nr: the bit to set
|
||||
* @addr: the address to start counting from
|
||||
*
|
||||
* Note that @nr may be almost arbitrarily large; this function is not
|
||||
* restricted to acting on a single-word quantity.
|
||||
* */
|
||||
static inline void
|
||||
set_bit(int nr, volatile void *addr) {
|
||||
asm volatile ("btsl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr));
|
||||
}
|
||||
|
||||
/* *
|
||||
* clear_bit - Atomically clears a bit in memory
|
||||
* @nr: the bit to clear
|
||||
* @addr: the address to start counting from
|
||||
* */
|
||||
static inline void
|
||||
clear_bit(int nr, volatile void *addr) {
|
||||
asm volatile ("btrl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr));
|
||||
}
|
||||
|
||||
/* *
|
||||
* change_bit - Atomically toggle a bit in memory
|
||||
* @nr: the bit to change
|
||||
* @addr: the address to start counting from
|
||||
* */
|
||||
static inline void
|
||||
change_bit(int nr, volatile void *addr) {
|
||||
asm volatile ("btcl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr));
|
||||
}
|
||||
|
||||
/* *
|
||||
* test_bit - Determine whether a bit is set
|
||||
* @nr: the bit to test
|
||||
* @addr: the address to count from
|
||||
* */
|
||||
static inline bool
|
||||
test_bit(int nr, volatile void *addr) {
|
||||
int oldbit;
|
||||
asm volatile ("btl %2, %1; sbbl %0,%0" : "=r" (oldbit) : "m" (*(volatile long *)addr), "Ir" (nr));
|
||||
return oldbit != 0;
|
||||
}
|
||||
|
||||
/* *
|
||||
* test_and_set_bit - Atomically set a bit and return its old value
|
||||
* @nr: the bit to set
|
||||
* @addr: the address to count from
|
||||
* */
|
||||
static inline bool
|
||||
test_and_set_bit(int nr, volatile void *addr) {
|
||||
int oldbit;
|
||||
asm volatile ("btsl %2, %1; sbbl %0, %0" : "=r" (oldbit), "=m" (*(volatile long *)addr) : "Ir" (nr) : "memory");
|
||||
return oldbit != 0;
|
||||
}
|
||||
|
||||
/* *
|
||||
* test_and_clear_bit - Atomically clear a bit and return its old value
|
||||
* @nr: the bit to clear
|
||||
* @addr: the address to count from
|
||||
* */
|
||||
static inline bool
|
||||
test_and_clear_bit(int nr, volatile void *addr) {
|
||||
int oldbit;
|
||||
asm volatile ("btrl %2, %1; sbbl %0, %0" : "=r" (oldbit), "=m" (*(volatile long *)addr) : "Ir" (nr) : "memory");
|
||||
return oldbit != 0;
|
||||
}
|
||||
|
||||
#endif /* !__LIBS_ATOMIC_H__ */
|
||||
|
68
related_info/lab5/lab5-spoc-discuss/libs/defs.h
Normal file
68
related_info/lab5/lab5-spoc-discuss/libs/defs.h
Normal file
@ -0,0 +1,68 @@
|
||||
#ifndef __LIBS_DEFS_H__
|
||||
#define __LIBS_DEFS_H__
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#define __always_inline inline __attribute__((always_inline))
|
||||
#define __noinline __attribute__((noinline))
|
||||
#define __noreturn __attribute__((noreturn))
|
||||
|
||||
/* Represents true-or-false values */
|
||||
typedef int bool;
|
||||
|
||||
/* Explicitly-sized versions of integer types */
|
||||
typedef char int8_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef short int16_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef int int32_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
/* *
|
||||
* Pointers and addresses are 32 bits long.
|
||||
* We use pointer types to represent addresses,
|
||||
* uintptr_t to represent the numerical values of addresses.
|
||||
* */
|
||||
typedef int32_t intptr_t;
|
||||
typedef uint32_t uintptr_t;
|
||||
|
||||
/* size_t is used for memory object sizes */
|
||||
typedef uintptr_t size_t;
|
||||
|
||||
/* used for page numbers */
|
||||
typedef size_t ppn_t;
|
||||
|
||||
/* *
|
||||
* Rounding operations (efficient when n is a power of 2)
|
||||
* Round down to the nearest multiple of n
|
||||
* */
|
||||
#define ROUNDDOWN(a, n) ({ \
|
||||
size_t __a = (size_t)(a); \
|
||||
(typeof(a))(__a - __a % (n)); \
|
||||
})
|
||||
|
||||
/* Round up to the nearest multiple of n */
|
||||
#define ROUNDUP(a, n) ({ \
|
||||
size_t __n = (size_t)(n); \
|
||||
(typeof(a))(ROUNDDOWN((size_t)(a) + __n - 1, __n)); \
|
||||
})
|
||||
|
||||
/* Return the offset of 'member' relative to the beginning of a struct type */
|
||||
#define offsetof(type, member) \
|
||||
((size_t)(&((type *)0)->member))
|
||||
|
||||
/* *
|
||||
* to_struct - get the struct from a ptr
|
||||
* @ptr: a struct pointer of member
|
||||
* @type: the type of the struct this is embedded in
|
||||
* @member: the name of the member within the struct
|
||||
* */
|
||||
#define to_struct(ptr, type, member) \
|
||||
((type *)((char *)(ptr) - offsetof(type, member)))
|
||||
|
||||
#endif /* !__LIBS_DEFS_H__ */
|
||||
|
48
related_info/lab5/lab5-spoc-discuss/libs/elf.h
Normal file
48
related_info/lab5/lab5-spoc-discuss/libs/elf.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef __LIBS_ELF_H__
|
||||
#define __LIBS_ELF_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian
|
||||
|
||||
/* file header */
|
||||
struct elfhdr {
|
||||
uint32_t e_magic; // must equal ELF_MAGIC
|
||||
uint8_t e_elf[12];
|
||||
uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core image
|
||||
uint16_t e_machine; // 3=x86, 4=68K, etc.
|
||||
uint32_t e_version; // file version, always 1
|
||||
uint32_t e_entry; // entry point if executable
|
||||
uint32_t e_phoff; // file position of program header or 0
|
||||
uint32_t e_shoff; // file position of section header or 0
|
||||
uint32_t e_flags; // architecture-specific flags, usually 0
|
||||
uint16_t e_ehsize; // size of this elf header
|
||||
uint16_t e_phentsize; // size of an entry in program header
|
||||
uint16_t e_phnum; // number of entries in program header or 0
|
||||
uint16_t e_shentsize; // size of an entry in section header
|
||||
uint16_t e_shnum; // number of entries in section header or 0
|
||||
uint16_t e_shstrndx; // section number that contains section name strings
|
||||
};
|
||||
|
||||
/* program section header */
|
||||
struct proghdr {
|
||||
uint32_t p_type; // loadable code or data, dynamic linking info,etc.
|
||||
uint32_t p_offset; // file offset of segment
|
||||
uint32_t p_va; // virtual address to map segment
|
||||
uint32_t p_pa; // physical address, not used
|
||||
uint32_t p_filesz; // size of segment in file
|
||||
uint32_t p_memsz; // size of segment in memory (bigger if contains bss)
|
||||
uint32_t p_flags; // read/write/execute bits
|
||||
uint32_t p_align; // required alignment, invariably hardware page size
|
||||
};
|
||||
|
||||
/* values for Proghdr::p_type */
|
||||
#define ELF_PT_LOAD 1
|
||||
|
||||
/* flag bits for Proghdr::p_flags */
|
||||
#define ELF_PF_X 1
|
||||
#define ELF_PF_W 2
|
||||
#define ELF_PF_R 4
|
||||
|
||||
#endif /* !__LIBS_ELF_H__ */
|
||||
|
33
related_info/lab5/lab5-spoc-discuss/libs/error.h
Normal file
33
related_info/lab5/lab5-spoc-discuss/libs/error.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __LIBS_ERROR_H__
|
||||
#define __LIBS_ERROR_H__
|
||||
|
||||
/* kernel error codes -- keep in sync with list in lib/printfmt.c */
|
||||
#define E_UNSPECIFIED 1 // Unspecified or unknown problem
|
||||
#define E_BAD_PROC 2 // Process doesn't exist or otherwise
|
||||
#define E_INVAL 3 // Invalid parameter
|
||||
#define E_NO_MEM 4 // Request failed due to memory shortage
|
||||
#define E_NO_FREE_PROC 5 // Attempt to create a new process beyond
|
||||
#define E_FAULT 6 // Memory fault
|
||||
#define E_SWAP_FAULT 7 // SWAP READ/WRITE fault
|
||||
#define E_INVAL_ELF 8 // Invalid elf file
|
||||
#define E_KILLED 9 // Process is killed
|
||||
#define E_PANIC 10 // Panic Failure
|
||||
#define E_TIMEOUT 11 // Timeout
|
||||
#define E_TOO_BIG 12 // Argument is Too Big
|
||||
#define E_NO_DEV 13 // No such Device
|
||||
#define E_NA_DEV 14 // Device Not Available
|
||||
#define E_BUSY 15 // Device/File is Busy
|
||||
#define E_NOENT 16 // No Such File or Directory
|
||||
#define E_ISDIR 17 // Is a Directory
|
||||
#define E_NOTDIR 18 // Not a Directory
|
||||
#define E_XDEV 19 // Cross Device-Link
|
||||
#define E_UNIMP 20 // Unimplemented Feature
|
||||
#define E_SEEK 21 // Illegal Seek
|
||||
#define E_MAX_OPEN 22 // Too Many Files are Open
|
||||
#define E_EXISTS 23 // File/Directory Already Exists
|
||||
#define E_NOTEMPTY 24 // Directory is Not Empty
|
||||
/* the maximum allowed */
|
||||
#define MAXERROR 24
|
||||
|
||||
#endif /* !__LIBS_ERROR_H__ */
|
||||
|
18
related_info/lab5/lab5-spoc-discuss/libs/hash.c
Normal file
18
related_info/lab5/lab5-spoc-discuss/libs/hash.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
/* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
|
||||
#define GOLDEN_RATIO_PRIME_32 0x9e370001UL
|
||||
|
||||
/* *
|
||||
* hash32 - generate a hash value in the range [0, 2^@bits - 1]
|
||||
* @val: the input value
|
||||
* @bits: the number of bits in a return value
|
||||
*
|
||||
* High bits are more random, so we use them.
|
||||
* */
|
||||
uint32_t
|
||||
hash32(uint32_t val, unsigned int bits) {
|
||||
uint32_t hash = val * GOLDEN_RATIO_PRIME_32;
|
||||
return (hash >> (32 - bits));
|
||||
}
|
||||
|
163
related_info/lab5/lab5-spoc-discuss/libs/list.h
Normal file
163
related_info/lab5/lab5-spoc-discuss/libs/list.h
Normal file
@ -0,0 +1,163 @@
|
||||
#ifndef __LIBS_LIST_H__
|
||||
#define __LIBS_LIST_H__
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
/* *
|
||||
* Simple doubly linked list implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when manipulating
|
||||
* whole lists rather than single entries, as sometimes we already know
|
||||
* the next/prev entries and we can generate better code by using them
|
||||
* directly rather than using the generic single-entry routines.
|
||||
* */
|
||||
|
||||
struct list_entry {
|
||||
struct list_entry *prev, *next;
|
||||
};
|
||||
|
||||
typedef struct list_entry list_entry_t;
|
||||
|
||||
static inline void list_init(list_entry_t *elm) __attribute__((always_inline));
|
||||
static inline void list_add(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline));
|
||||
static inline void list_add_before(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline));
|
||||
static inline void list_add_after(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline));
|
||||
static inline void list_del(list_entry_t *listelm) __attribute__((always_inline));
|
||||
static inline void list_del_init(list_entry_t *listelm) __attribute__((always_inline));
|
||||
static inline bool list_empty(list_entry_t *list) __attribute__((always_inline));
|
||||
static inline list_entry_t *list_next(list_entry_t *listelm) __attribute__((always_inline));
|
||||
static inline list_entry_t *list_prev(list_entry_t *listelm) __attribute__((always_inline));
|
||||
|
||||
static inline void __list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) __attribute__((always_inline));
|
||||
static inline void __list_del(list_entry_t *prev, list_entry_t *next) __attribute__((always_inline));
|
||||
|
||||
/* *
|
||||
* list_init - initialize a new entry
|
||||
* @elm: new entry to be initialized
|
||||
* */
|
||||
static inline void
|
||||
list_init(list_entry_t *elm) {
|
||||
elm->prev = elm->next = elm;
|
||||
}
|
||||
|
||||
/* *
|
||||
* list_add - add a new entry
|
||||
* @listelm: list head to add after
|
||||
* @elm: new entry to be added
|
||||
*
|
||||
* Insert the new element @elm *after* the element @listelm which
|
||||
* is already in the list.
|
||||
* */
|
||||
static inline void
|
||||
list_add(list_entry_t *listelm, list_entry_t *elm) {
|
||||
list_add_after(listelm, elm);
|
||||
}
|
||||
|
||||
/* *
|
||||
* list_add_before - add a new entry
|
||||
* @listelm: list head to add before
|
||||
* @elm: new entry to be added
|
||||
*
|
||||
* Insert the new element @elm *before* the element @listelm which
|
||||
* is already in the list.
|
||||
* */
|
||||
static inline void
|
||||
list_add_before(list_entry_t *listelm, list_entry_t *elm) {
|
||||
__list_add(elm, listelm->prev, listelm);
|
||||
}
|
||||
|
||||
/* *
|
||||
* list_add_after - add a new entry
|
||||
* @listelm: list head to add after
|
||||
* @elm: new entry to be added
|
||||
*
|
||||
* Insert the new element @elm *after* the element @listelm which
|
||||
* is already in the list.
|
||||
* */
|
||||
static inline void
|
||||
list_add_after(list_entry_t *listelm, list_entry_t *elm) {
|
||||
__list_add(elm, listelm, listelm->next);
|
||||
}
|
||||
|
||||
/* *
|
||||
* list_del - deletes entry from list
|
||||
* @listelm: the element to delete from the list
|
||||
*
|
||||
* Note: list_empty() on @listelm does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
* */
|
||||
static inline void
|
||||
list_del(list_entry_t *listelm) {
|
||||
__list_del(listelm->prev, listelm->next);
|
||||
}
|
||||
|
||||
/* *
|
||||
* list_del_init - deletes entry from list and reinitialize it.
|
||||
* @listelm: the element to delete from the list.
|
||||
*
|
||||
* Note: list_empty() on @listelm returns true after this.
|
||||
* */
|
||||
static inline void
|
||||
list_del_init(list_entry_t *listelm) {
|
||||
list_del(listelm);
|
||||
list_init(listelm);
|
||||
}
|
||||
|
||||
/* *
|
||||
* list_empty - tests whether a list is empty
|
||||
* @list: the list to test.
|
||||
* */
|
||||
static inline bool
|
||||
list_empty(list_entry_t *list) {
|
||||
return list->next == list;
|
||||
}
|
||||
|
||||
/* *
|
||||
* list_next - get the next entry
|
||||
* @listelm: the list head
|
||||
**/
|
||||
static inline list_entry_t *
|
||||
list_next(list_entry_t *listelm) {
|
||||
return listelm->next;
|
||||
}
|
||||
|
||||
/* *
|
||||
* list_prev - get the previous entry
|
||||
* @listelm: the list head
|
||||
**/
|
||||
static inline list_entry_t *
|
||||
list_prev(list_entry_t *listelm) {
|
||||
return listelm->prev;
|
||||
}
|
||||
|
||||
/* *
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
* */
|
||||
static inline void
|
||||
__list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) {
|
||||
prev->next = next->prev = elm;
|
||||
elm->next = next;
|
||||
elm->prev = prev;
|
||||
}
|
||||
|
||||
/* *
|
||||
* Delete a list entry by making the prev/next entries point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
* */
|
||||
static inline void
|
||||
__list_del(list_entry_t *prev, list_entry_t *next) {
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
}
|
||||
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
#endif /* !__LIBS_LIST_H__ */
|
||||
|
343
related_info/lab5/lab5-spoc-discuss/libs/printfmt.c
Normal file
343
related_info/lab5/lab5-spoc-discuss/libs/printfmt.c
Normal file
@ -0,0 +1,343 @@
|
||||
#include <defs.h>
|
||||
#include <x86.h>
|
||||
#include <error.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* *
|
||||
* Space or zero padding and a field width are supported for the numeric
|
||||
* formats only.
|
||||
*
|
||||
* The special format %e takes an integer error code
|
||||
* and prints a string describing the error.
|
||||
* The integer may be positive or negative,
|
||||
* so that -E_NO_MEM and E_NO_MEM are equivalent.
|
||||
* */
|
||||
|
||||
static const char * const error_string[MAXERROR + 1] = {
|
||||
[0] NULL,
|
||||
[E_UNSPECIFIED] "unspecified error",
|
||||
[E_BAD_PROC] "bad process",
|
||||
[E_INVAL] "invalid parameter",
|
||||
[E_NO_MEM] "out of memory",
|
||||
[E_NO_FREE_PROC] "out of processes",
|
||||
[E_FAULT] "segmentation fault",
|
||||
[E_INVAL_ELF] "invalid elf file",
|
||||
[E_KILLED] "process is killed",
|
||||
[E_PANIC] "panic failure",
|
||||
};
|
||||
|
||||
/* *
|
||||
* printnum - print a number (base <= 16) in reverse order
|
||||
* @putch: specified putch function, print a single character
|
||||
* @putdat: used by @putch function
|
||||
* @num: the number will be printed
|
||||
* @base: base for print, must be in [1, 16]
|
||||
* @width: maximum number of digits, if the actual width is less than @width, use @padc instead
|
||||
* @padc: character that padded on the left if the actual width is less than @width
|
||||
* */
|
||||
static void
|
||||
printnum(void (*putch)(int, void*), void *putdat,
|
||||
unsigned long long num, unsigned base, int width, int padc) {
|
||||
unsigned long long result = num;
|
||||
unsigned mod = do_div(result, base);
|
||||
|
||||
// first recursively print all preceding (more significant) digits
|
||||
if (num >= base) {
|
||||
printnum(putch, putdat, result, base, width - 1, padc);
|
||||
} else {
|
||||
// print any needed pad characters before first digit
|
||||
while (-- width > 0)
|
||||
putch(padc, putdat);
|
||||
}
|
||||
// then print this (the least significant) digit
|
||||
putch("0123456789abcdef"[mod], putdat);
|
||||
}
|
||||
|
||||
/* *
|
||||
* getuint - get an unsigned int of various possible sizes from a varargs list
|
||||
* @ap: a varargs list pointer
|
||||
* @lflag: determines the size of the vararg that @ap points to
|
||||
* */
|
||||
static unsigned long long
|
||||
getuint(va_list *ap, int lflag) {
|
||||
if (lflag >= 2) {
|
||||
return va_arg(*ap, unsigned long long);
|
||||
}
|
||||
else if (lflag) {
|
||||
return va_arg(*ap, unsigned long);
|
||||
}
|
||||
else {
|
||||
return va_arg(*ap, unsigned int);
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* getint - same as getuint but signed, we can't use getuint because of sign extension
|
||||
* @ap: a varargs list pointer
|
||||
* @lflag: determines the size of the vararg that @ap points to
|
||||
* */
|
||||
static long long
|
||||
getint(va_list *ap, int lflag) {
|
||||
if (lflag >= 2) {
|
||||
return va_arg(*ap, long long);
|
||||
}
|
||||
else if (lflag) {
|
||||
return va_arg(*ap, long);
|
||||
}
|
||||
else {
|
||||
return va_arg(*ap, int);
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* printfmt - format a string and print it by using putch
|
||||
* @putch: specified putch function, print a single character
|
||||
* @putdat: used by @putch function
|
||||
* @fmt: the format string to use
|
||||
* */
|
||||
void
|
||||
printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vprintfmt(putch, putdat, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* *
|
||||
* vprintfmt - format a string and print it by using putch, it's called with a va_list
|
||||
* instead of a variable number of arguments
|
||||
* @putch: specified putch function, print a single character
|
||||
* @putdat: used by @putch function
|
||||
* @fmt: the format string to use
|
||||
* @ap: arguments for the format string
|
||||
*
|
||||
* Call this function if you are already dealing with a va_list.
|
||||
* Or you probably want printfmt() instead.
|
||||
* */
|
||||
void
|
||||
vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) {
|
||||
register const char *p;
|
||||
register int ch, err;
|
||||
unsigned long long num;
|
||||
int base, width, precision, lflag, altflag;
|
||||
|
||||
while (1) {
|
||||
while ((ch = *(unsigned char *)fmt ++) != '%') {
|
||||
if (ch == '\0') {
|
||||
return;
|
||||
}
|
||||
putch(ch, putdat);
|
||||
}
|
||||
|
||||
// Process a %-escape sequence
|
||||
char padc = ' ';
|
||||
width = precision = -1;
|
||||
lflag = altflag = 0;
|
||||
|
||||
reswitch:
|
||||
switch (ch = *(unsigned char *)fmt ++) {
|
||||
|
||||
// flag to pad on the right
|
||||
case '-':
|
||||
padc = '-';
|
||||
goto reswitch;
|
||||
|
||||
// flag to pad with 0's instead of spaces
|
||||
case '0':
|
||||
padc = '0';
|
||||
goto reswitch;
|
||||
|
||||
// width field
|
||||
case '1' ... '9':
|
||||
for (precision = 0; ; ++ fmt) {
|
||||
precision = precision * 10 + ch - '0';
|
||||
ch = *fmt;
|
||||
if (ch < '0' || ch > '9') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
goto process_precision;
|
||||
|
||||
case '*':
|
||||
precision = va_arg(ap, int);
|
||||
goto process_precision;
|
||||
|
||||
case '.':
|
||||
if (width < 0)
|
||||
width = 0;
|
||||
goto reswitch;
|
||||
|
||||
case '#':
|
||||
altflag = 1;
|
||||
goto reswitch;
|
||||
|
||||
process_precision:
|
||||
if (width < 0)
|
||||
width = precision, precision = -1;
|
||||
goto reswitch;
|
||||
|
||||
// long flag (doubled for long long)
|
||||
case 'l':
|
||||
lflag ++;
|
||||
goto reswitch;
|
||||
|
||||
// character
|
||||
case 'c':
|
||||
putch(va_arg(ap, int), putdat);
|
||||
break;
|
||||
|
||||
// error message
|
||||
case 'e':
|
||||
err = va_arg(ap, int);
|
||||
if (err < 0) {
|
||||
err = -err;
|
||||
}
|
||||
if (err > MAXERROR || (p = error_string[err]) == NULL) {
|
||||
printfmt(putch, putdat, "error %d", err);
|
||||
}
|
||||
else {
|
||||
printfmt(putch, putdat, "%s", p);
|
||||
}
|
||||
break;
|
||||
|
||||
// string
|
||||
case 's':
|
||||
if ((p = va_arg(ap, char *)) == NULL) {
|
||||
p = "(null)";
|
||||
}
|
||||
if (width > 0 && padc != '-') {
|
||||
for (width -= strnlen(p, precision); width > 0; width --) {
|
||||
putch(padc, putdat);
|
||||
}
|
||||
}
|
||||
for (; (ch = *p ++) != '\0' && (precision < 0 || -- precision >= 0); width --) {
|
||||
if (altflag && (ch < ' ' || ch > '~')) {
|
||||
putch('?', putdat);
|
||||
}
|
||||
else {
|
||||
putch(ch, putdat);
|
||||
}
|
||||
}
|
||||
for (; width > 0; width --) {
|
||||
putch(' ', putdat);
|
||||
}
|
||||
break;
|
||||
|
||||
// (signed) decimal
|
||||
case 'd':
|
||||
num = getint(&ap, lflag);
|
||||
if ((long long)num < 0) {
|
||||
putch('-', putdat);
|
||||
num = -(long long)num;
|
||||
}
|
||||
base = 10;
|
||||
goto number;
|
||||
|
||||
// unsigned decimal
|
||||
case 'u':
|
||||
num = getuint(&ap, lflag);
|
||||
base = 10;
|
||||
goto number;
|
||||
|
||||
// (unsigned) octal
|
||||
case 'o':
|
||||
num = getuint(&ap, lflag);
|
||||
base = 8;
|
||||
goto number;
|
||||
|
||||
// pointer
|
||||
case 'p':
|
||||
putch('0', putdat);
|
||||
putch('x', putdat);
|
||||
num = (unsigned long long)(uintptr_t)va_arg(ap, void *);
|
||||
base = 16;
|
||||
goto number;
|
||||
|
||||
// (unsigned) hexadecimal
|
||||
case 'x':
|
||||
num = getuint(&ap, lflag);
|
||||
base = 16;
|
||||
number:
|
||||
printnum(putch, putdat, num, base, width, padc);
|
||||
break;
|
||||
|
||||
// escaped '%' character
|
||||
case '%':
|
||||
putch(ch, putdat);
|
||||
break;
|
||||
|
||||
// unrecognized escape sequence - just print it literally
|
||||
default:
|
||||
putch('%', putdat);
|
||||
for (fmt --; fmt[-1] != '%'; fmt --)
|
||||
/* do nothing */;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* sprintbuf is used to save enough information of a buffer */
|
||||
struct sprintbuf {
|
||||
char *buf; // address pointer points to the first unused memory
|
||||
char *ebuf; // points the end of the buffer
|
||||
int cnt; // the number of characters that have been placed in this buffer
|
||||
};
|
||||
|
||||
/* *
|
||||
* sprintputch - 'print' a single character in a buffer
|
||||
* @ch: the character will be printed
|
||||
* @b: the buffer to place the character @ch
|
||||
* */
|
||||
static void
|
||||
sprintputch(int ch, struct sprintbuf *b) {
|
||||
b->cnt ++;
|
||||
if (b->buf < b->ebuf) {
|
||||
*b->buf ++ = ch;
|
||||
}
|
||||
}
|
||||
|
||||
/* *
|
||||
* snprintf - format a string and place it in a buffer
|
||||
* @str: the buffer to place the result into
|
||||
* @size: the size of buffer, including the trailing null space
|
||||
* @fmt: the format string to use
|
||||
* */
|
||||
int
|
||||
snprintf(char *str, size_t size, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
int cnt;
|
||||
va_start(ap, fmt);
|
||||
cnt = vsnprintf(str, size, fmt, ap);
|
||||
va_end(ap);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* *
|
||||
* vsnprintf - format a string and place it in a buffer, it's called with a va_list
|
||||
* instead of a variable number of arguments
|
||||
* @str: the buffer to place the result into
|
||||
* @size: the size of buffer, including the trailing null space
|
||||
* @fmt: the format string to use
|
||||
* @ap: arguments for the format string
|
||||
*
|
||||
* The return value is the number of characters which would be generated for the
|
||||
* given input, excluding the trailing '\0'.
|
||||
*
|
||||
* Call this function if you are already dealing with a va_list.
|
||||
* Or you probably want snprintf() instead.
|
||||
* */
|
||||
int
|
||||
vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
|
||||
struct sprintbuf b = {str, str + size - 1, 0};
|
||||
if (str == NULL || b.buf > b.ebuf) {
|
||||
return -E_INVAL;
|
||||
}
|
||||
// print the string to the buffer
|
||||
vprintfmt((void*)sprintputch, &b, fmt, ap);
|
||||
// null terminate the buffer
|
||||
*b.buf = '\0';
|
||||
return b.cnt;
|
||||
}
|
||||
|
26
related_info/lab5/lab5-spoc-discuss/libs/rand.c
Normal file
26
related_info/lab5/lab5-spoc-discuss/libs/rand.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <x86.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static unsigned long long next = 1;
|
||||
|
||||
/* *
|
||||
* rand - returns a pseudo-random integer
|
||||
*
|
||||
* The rand() function return a value in the range [0, RAND_MAX].
|
||||
* */
|
||||
int
|
||||
rand(void) {
|
||||
next = (next * 0x5DEECE66DLL + 0xBLL) & ((1LL << 48) - 1);
|
||||
unsigned long long result = (next >> 12);
|
||||
return (int)do_div(result, RAND_MAX + 1);
|
||||
}
|
||||
|
||||
/* *
|
||||
* srand - seed the random number generator with the given number
|
||||
* @seed: the required seed number
|
||||
* */
|
||||
void
|
||||
srand(unsigned int seed) {
|
||||
next = seed;
|
||||
}
|
||||
|
12
related_info/lab5/lab5-spoc-discuss/libs/stdarg.h
Normal file
12
related_info/lab5/lab5-spoc-discuss/libs/stdarg.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef __LIBS_STDARG_H__
|
||||
#define __LIBS_STDARG_H__
|
||||
|
||||
/* compiler provides size of save area */
|
||||
typedef __builtin_va_list va_list;
|
||||
|
||||
#define va_start(ap, last) (__builtin_va_start(ap, last))
|
||||
#define va_arg(ap, type) (__builtin_va_arg(ap, type))
|
||||
#define va_end(ap) /*nothing*/
|
||||
|
||||
#endif /* !__LIBS_STDARG_H__ */
|
||||
|
24
related_info/lab5/lab5-spoc-discuss/libs/stdio.h
Normal file
24
related_info/lab5/lab5-spoc-discuss/libs/stdio.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef __LIBS_STDIO_H__
|
||||
#define __LIBS_STDIO_H__
|
||||
|
||||
#include <defs.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* kern/libs/stdio.c */
|
||||
int cprintf(const char *fmt, ...);
|
||||
int vcprintf(const char *fmt, va_list ap);
|
||||
void cputchar(int c);
|
||||
int cputs(const char *str);
|
||||
int getchar(void);
|
||||
|
||||
/* kern/libs/readline.c */
|
||||
char *readline(const char *prompt);
|
||||
|
||||
/* libs/printfmt.c */
|
||||
void printfmt(void (*putch)(int, void *), void *putdat, const char *fmt, ...);
|
||||
void vprintfmt(void (*putch)(int, void *), void *putdat, const char *fmt, va_list ap);
|
||||
int snprintf(char *str, size_t size, const char *fmt, ...);
|
||||
int vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
|
||||
|
||||
#endif /* !__LIBS_STDIO_H__ */
|
||||
|
17
related_info/lab5/lab5-spoc-discuss/libs/stdlib.h
Normal file
17
related_info/lab5/lab5-spoc-discuss/libs/stdlib.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef __LIBS_STDLIB_H__
|
||||
#define __LIBS_STDLIB_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
/* the largest number rand will return */
|
||||
#define RAND_MAX 2147483647UL
|
||||
|
||||
/* libs/rand.c */
|
||||
int rand(void);
|
||||
void srand(unsigned int seed);
|
||||
|
||||
/* libs/hash.c */
|
||||
uint32_t hash32(uint32_t val, unsigned int bits);
|
||||
|
||||
#endif /* !__LIBS_RAND_H__ */
|
||||
|
367
related_info/lab5/lab5-spoc-discuss/libs/string.c
Normal file
367
related_info/lab5/lab5-spoc-discuss/libs/string.c
Normal file
@ -0,0 +1,367 @@
|
||||
#include <string.h>
|
||||
#include <x86.h>
|
||||
|
||||
/* *
|
||||
* strlen - calculate the length of the string @s, not including
|
||||
* the terminating '\0' character.
|
||||
* @s: the input string
|
||||
*
|
||||
* The strlen() function returns the length of string @s.
|
||||
* */
|
||||
size_t
|
||||
strlen(const char *s) {
|
||||
size_t cnt = 0;
|
||||
while (*s ++ != '\0') {
|
||||
cnt ++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* *
|
||||
* strnlen - calculate the length of the string @s, not including
|
||||
* the terminating '\0' char acter, but at most @len.
|
||||
* @s: the input string
|
||||
* @len: the max-length that function will scan
|
||||
*
|
||||
* Note that, this function looks only at the first @len characters
|
||||
* at @s, and never beyond @s + @len.
|
||||
*
|
||||
* The return value is strlen(s), if that is less than @len, or
|
||||
* @len if there is no '\0' character among the first @len characters
|
||||
* pointed by @s.
|
||||
* */
|
||||
size_t
|
||||
strnlen(const char *s, size_t len) {
|
||||
size_t cnt = 0;
|
||||
while (cnt < len && *s ++ != '\0') {
|
||||
cnt ++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* *
|
||||
* strcpy - copies the string pointed by @src into the array pointed by @dst,
|
||||
* including the terminating null character.
|
||||
* @dst: pointer to the destination array where the content is to be copied
|
||||
* @src: string to be copied
|
||||
*
|
||||
* The return value is @dst.
|
||||
*
|
||||
* To avoid overflows, the size of array pointed by @dst should be long enough to
|
||||
* contain the same string as @src (including the terminating null character), and
|
||||
* should not overlap in memory with @src.
|
||||
* */
|
||||
char *
|
||||
strcpy(char *dst, const char *src) {
|
||||
#ifdef __HAVE_ARCH_STRCPY
|
||||
return __strcpy(dst, src);
|
||||
#else
|
||||
char *p = dst;
|
||||
while ((*p ++ = *src ++) != '\0')
|
||||
/* nothing */;
|
||||
return dst;
|
||||
#endif /* __HAVE_ARCH_STRCPY */
|
||||
}
|
||||
|
||||
/* *
|
||||
* strncpy - copies the first @len characters of @src to @dst. If the end of string @src
|
||||
* if found before @len characters have been copied, @dst is padded with '\0' until a
|
||||
* total of @len characters have been written to it.
|
||||
* @dst: pointer to the destination array where the content is to be copied
|
||||
* @src: string to be copied
|
||||
* @len: maximum number of characters to be copied from @src
|
||||
*
|
||||
* The return value is @dst
|
||||
* */
|
||||
char *
|
||||
strncpy(char *dst, const char *src, size_t len) {
|
||||
char *p = dst;
|
||||
while (len > 0) {
|
||||
if ((*p = *src) != '\0') {
|
||||
src ++;
|
||||
}
|
||||
p ++, len --;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/* *
|
||||
* strcmp - compares the string @s1 and @s2
|
||||
* @s1: string to be compared
|
||||
* @s2: string to be compared
|
||||
*
|
||||
* This function starts comparing the first character of each string. If
|
||||
* they are equal to each other, it continues with the following pairs until
|
||||
* the characters differ or until a terminanting null-character is reached.
|
||||
*
|
||||
* Returns an integral value indicating the relationship between the strings:
|
||||
* - A zero value indicates that both strings are equal;
|
||||
* - A value greater than zero indicates that the first character that does
|
||||
* not match has a greater value in @s1 than in @s2;
|
||||
* - And a value less than zero indicates the opposite.
|
||||
* */
|
||||
int
|
||||
strcmp(const char *s1, const char *s2) {
|
||||
#ifdef __HAVE_ARCH_STRCMP
|
||||
return __strcmp(s1, s2);
|
||||
#else
|
||||
while (*s1 != '\0' && *s1 == *s2) {
|
||||
s1 ++, s2 ++;
|
||||
}
|
||||
return (int)((unsigned char)*s1 - (unsigned char)*s2);
|
||||
#endif /* __HAVE_ARCH_STRCMP */
|
||||
}
|
||||
|
||||
/* *
|
||||
* strncmp - compares up to @n characters of the string @s1 to those of the string @s2
|
||||
* @s1: string to be compared
|
||||
* @s2: string to be compared
|
||||
* @n: maximum number of characters to compare
|
||||
*
|
||||
* This function starts comparing the first character of each string. If
|
||||
* they are equal to each other, it continues with the following pairs until
|
||||
* the characters differ, until a terminating null-character is reached, or
|
||||
* until @n characters match in both strings, whichever happens first.
|
||||
* */
|
||||
int
|
||||
strncmp(const char *s1, const char *s2, size_t n) {
|
||||
while (n > 0 && *s1 != '\0' && *s1 == *s2) {
|
||||
n --, s1 ++, s2 ++;
|
||||
}
|
||||
return (n == 0) ? 0 : (int)((unsigned char)*s1 - (unsigned char)*s2);
|
||||
}
|
||||
|
||||
/* *
|
||||
* strchr - locates first occurrence of character in string
|
||||
* @s: the input string
|
||||
* @c: character to be located
|
||||
*
|
||||
* The strchr() function returns a pointer to the first occurrence of
|
||||
* character in @s. If the value is not found, the function returns 'NULL'.
|
||||
* */
|
||||
char *
|
||||
strchr(const char *s, char c) {
|
||||
while (*s != '\0') {
|
||||
if (*s == c) {
|
||||
return (char *)s;
|
||||
}
|
||||
s ++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* *
|
||||
* strfind - locates first occurrence of character in string
|
||||
* @s: the input string
|
||||
* @c: character to be located
|
||||
*
|
||||
* The strfind() function is like strchr() except that if @c is
|
||||
* not found in @s, then it returns a pointer to the null byte at the
|
||||
* end of @s, rather than 'NULL'.
|
||||
* */
|
||||
char *
|
||||
strfind(const char *s, char c) {
|
||||
while (*s != '\0') {
|
||||
if (*s == c) {
|
||||
break;
|
||||
}
|
||||
s ++;
|
||||
}
|
||||
return (char *)s;
|
||||
}
|
||||
|
||||
/* *
|
||||
* strtol - converts string to long integer
|
||||
* @s: the input string that contains the representation of an integer number
|
||||
* @endptr: reference to an object of type char *, whose value is set by the
|
||||
* function to the next character in @s after the numerical value. This
|
||||
* parameter can also be a null pointer, in which case it is not used.
|
||||
* @base: x
|
||||
*
|
||||
* The function first discards as many whitespace characters as necessary until
|
||||
* the first non-whitespace character is found. Then, starting from this character,
|
||||
* takes as many characters as possible that are valid following a syntax that
|
||||
* depends on the base parameter, and interprets them as a numerical value. Finally,
|
||||
* a pointer to the first character following the integer representation in @s
|
||||
* is stored in the object pointed by @endptr.
|
||||
*
|
||||
* If the value of base is zero, the syntax expected is similar to that of
|
||||
* integer constants, which is formed by a succession of:
|
||||
* - An optional plus or minus sign;
|
||||
* - An optional prefix indicating octal or hexadecimal base ("0" or "0x" respectively)
|
||||
* - A sequence of decimal digits (if no base prefix was specified) or either octal
|
||||
* or hexadecimal digits if a specific prefix is present
|
||||
*
|
||||
* If the base value is between 2 and 36, the format expected for the integral number
|
||||
* is a succession of the valid digits and/or letters needed to represent integers of
|
||||
* the specified radix (starting from '0' and up to 'z'/'Z' for radix 36). The
|
||||
* sequence may optionally be preceded by a plus or minus sign and, if base is 16,
|
||||
* an optional "0x" or "0X" prefix.
|
||||
*
|
||||
* The strtol() function returns the converted integral number as a long int value.
|
||||
* */
|
||||
long
|
||||
strtol(const char *s, char **endptr, int base) {
|
||||
int neg = 0;
|
||||
long val = 0;
|
||||
|
||||
// gobble initial whitespace
|
||||
while (*s == ' ' || *s == '\t') {
|
||||
s ++;
|
||||
}
|
||||
|
||||
// plus/minus sign
|
||||
if (*s == '+') {
|
||||
s ++;
|
||||
}
|
||||
else if (*s == '-') {
|
||||
s ++, neg = 1;
|
||||
}
|
||||
|
||||
// hex or octal base prefix
|
||||
if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x')) {
|
||||
s += 2, base = 16;
|
||||
}
|
||||
else if (base == 0 && s[0] == '0') {
|
||||
s ++, base = 8;
|
||||
}
|
||||
else if (base == 0) {
|
||||
base = 10;
|
||||
}
|
||||
|
||||
// digits
|
||||
while (1) {
|
||||
int dig;
|
||||
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
dig = *s - '0';
|
||||
}
|
||||
else if (*s >= 'a' && *s <= 'z') {
|
||||
dig = *s - 'a' + 10;
|
||||
}
|
||||
else if (*s >= 'A' && *s <= 'Z') {
|
||||
dig = *s - 'A' + 10;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
if (dig >= base) {
|
||||
break;
|
||||
}
|
||||
s ++, val = (val * base) + dig;
|
||||
// we don't properly detect overflow!
|
||||
}
|
||||
|
||||
if (endptr) {
|
||||
*endptr = (char *) s;
|
||||
}
|
||||
return (neg ? -val : val);
|
||||
}
|
||||
|
||||
/* *
|
||||
* memset - sets the first @n bytes of the memory area pointed by @s
|
||||
* to the specified value @c.
|
||||
* @s: pointer the the memory area to fill
|
||||
* @c: value to set
|
||||
* @n: number of bytes to be set to the value
|
||||
*
|
||||
* The memset() function returns @s.
|
||||
* */
|
||||
void *
|
||||
memset(void *s, char c, size_t n) {
|
||||
#ifdef __HAVE_ARCH_MEMSET
|
||||
return __memset(s, c, n);
|
||||
#else
|
||||
char *p = s;
|
||||
while (n -- > 0) {
|
||||
*p ++ = c;
|
||||
}
|
||||
return s;
|
||||
#endif /* __HAVE_ARCH_MEMSET */
|
||||
}
|
||||
|
||||
/* *
|
||||
* memmove - copies the values of @n bytes from the location pointed by @src to
|
||||
* the memory area pointed by @dst. @src and @dst are allowed to overlap.
|
||||
* @dst pointer to the destination array where the content is to be copied
|
||||
* @src pointer to the source of data to by copied
|
||||
* @n: number of bytes to copy
|
||||
*
|
||||
* The memmove() function returns @dst.
|
||||
* */
|
||||
void *
|
||||
memmove(void *dst, const void *src, size_t n) {
|
||||
#ifdef __HAVE_ARCH_MEMMOVE
|
||||
return __memmove(dst, src, n);
|
||||
#else
|
||||
const char *s = src;
|
||||
char *d = dst;
|
||||
if (s < d && s + n > d) {
|
||||
s += n, d += n;
|
||||
while (n -- > 0) {
|
||||
*-- d = *-- s;
|
||||
}
|
||||
} else {
|
||||
while (n -- > 0) {
|
||||
*d ++ = *s ++;
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
#endif /* __HAVE_ARCH_MEMMOVE */
|
||||
}
|
||||
|
||||
/* *
|
||||
* memcpy - copies the value of @n bytes from the location pointed by @src to
|
||||
* the memory area pointed by @dst.
|
||||
* @dst pointer to the destination array where the content is to be copied
|
||||
* @src pointer to the source of data to by copied
|
||||
* @n: number of bytes to copy
|
||||
*
|
||||
* The memcpy() returns @dst.
|
||||
*
|
||||
* Note that, the function does not check any terminating null character in @src,
|
||||
* it always copies exactly @n bytes. To avoid overflows, the size of arrays pointed
|
||||
* by both @src and @dst, should be at least @n bytes, and should not overlap
|
||||
* (for overlapping memory area, memmove is a safer approach).
|
||||
* */
|
||||
void *
|
||||
memcpy(void *dst, const void *src, size_t n) {
|
||||
#ifdef __HAVE_ARCH_MEMCPY
|
||||
return __memcpy(dst, src, n);
|
||||
#else
|
||||
const char *s = src;
|
||||
char *d = dst;
|
||||
while (n -- > 0) {
|
||||
*d ++ = *s ++;
|
||||
}
|
||||
return dst;
|
||||
#endif /* __HAVE_ARCH_MEMCPY */
|
||||
}
|
||||
|
||||
/* *
|
||||
* memcmp - compares two blocks of memory
|
||||
* @v1: pointer to block of memory
|
||||
* @v2: pointer to block of memory
|
||||
* @n: number of bytes to compare
|
||||
*
|
||||
* The memcmp() functions returns an integral value indicating the
|
||||
* relationship between the content of the memory blocks:
|
||||
* - A zero value indicates that the contents of both memory blocks are equal;
|
||||
* - A value greater than zero indicates that the first byte that does not
|
||||
* match in both memory blocks has a greater value in @v1 than in @v2
|
||||
* as if evaluated as unsigned char values;
|
||||
* - And a value less than zero indicates the opposite.
|
||||
* */
|
||||
int
|
||||
memcmp(const void *v1, const void *v2, size_t n) {
|
||||
const char *s1 = (const char *)v1;
|
||||
const char *s2 = (const char *)v2;
|
||||
while (n -- > 0) {
|
||||
if (*s1 != *s2) {
|
||||
return (int)((unsigned char)*s1 - (unsigned char)*s2);
|
||||
}
|
||||
s1 ++, s2 ++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
25
related_info/lab5/lab5-spoc-discuss/libs/string.h
Normal file
25
related_info/lab5/lab5-spoc-discuss/libs/string.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef __LIBS_STRING_H__
|
||||
#define __LIBS_STRING_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
size_t strlen(const char *s);
|
||||
size_t strnlen(const char *s, size_t len);
|
||||
|
||||
char *strcpy(char *dst, const char *src);
|
||||
char *strncpy(char *dst, const char *src, size_t len);
|
||||
|
||||
int strcmp(const char *s1, const char *s2);
|
||||
int strncmp(const char *s1, const char *s2, size_t n);
|
||||
|
||||
char *strchr(const char *s, char c);
|
||||
char *strfind(const char *s, char c);
|
||||
long strtol(const char *s, char **endptr, int base);
|
||||
|
||||
void *memset(void *s, char c, size_t n);
|
||||
void *memmove(void *dst, const void *src, size_t n);
|
||||
void *memcpy(void *dst, const void *src, size_t n);
|
||||
int memcmp(const void *v1, const void *v2, size_t n);
|
||||
|
||||
#endif /* !__LIBS_STRING_H__ */
|
||||
|
29
related_info/lab5/lab5-spoc-discuss/libs/unistd.h
Normal file
29
related_info/lab5/lab5-spoc-discuss/libs/unistd.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __LIBS_UNISTD_H__
|
||||
#define __LIBS_UNISTD_H__
|
||||
|
||||
#define T_SYSCALL 0x80
|
||||
|
||||
/* syscall number */
|
||||
#define SYS_exit 1
|
||||
#define SYS_fork 2
|
||||
#define SYS_wait 3
|
||||
#define SYS_exec 4
|
||||
#define SYS_clone 5
|
||||
#define SYS_yield 10
|
||||
#define SYS_sleep 11
|
||||
#define SYS_kill 12
|
||||
#define SYS_gettime 17
|
||||
#define SYS_getpid 18
|
||||
#define SYS_brk 19
|
||||
#define SYS_mmap 20
|
||||
#define SYS_munmap 21
|
||||
#define SYS_shmem 22
|
||||
#define SYS_putc 30
|
||||
#define SYS_pgdir 31
|
||||
|
||||
/* SYS_fork flags */
|
||||
#define CLONE_VM 0x00000100 // set if VM shared between processes
|
||||
#define CLONE_THREAD 0x00000200 // thread group
|
||||
|
||||
#endif /* !__LIBS_UNISTD_H__ */
|
||||
|
302
related_info/lab5/lab5-spoc-discuss/libs/x86.h
Normal file
302
related_info/lab5/lab5-spoc-discuss/libs/x86.h
Normal file
@ -0,0 +1,302 @@
|
||||
#ifndef __LIBS_X86_H__
|
||||
#define __LIBS_X86_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
#define do_div(n, base) ({ \
|
||||
unsigned long __upper, __low, __high, __mod, __base; \
|
||||
__base = (base); \
|
||||
asm ("" : "=a" (__low), "=d" (__high) : "A" (n)); \
|
||||
__upper = __high; \
|
||||
if (__high != 0) { \
|
||||
__upper = __high % __base; \
|
||||
__high = __high / __base; \
|
||||
} \
|
||||
asm ("divl %2" : "=a" (__low), "=d" (__mod) \
|
||||
: "rm" (__base), "0" (__low), "1" (__upper)); \
|
||||
asm ("" : "=A" (n) : "a" (__low), "d" (__high)); \
|
||||
__mod; \
|
||||
})
|
||||
|
||||
#define barrier() __asm__ __volatile__ ("" ::: "memory")
|
||||
|
||||
static inline uint8_t inb(uint16_t port) __attribute__((always_inline));
|
||||
static inline void insl(uint32_t port, void *addr, int cnt) __attribute__((always_inline));
|
||||
static inline void outb(uint16_t port, uint8_t data) __attribute__((always_inline));
|
||||
static inline void outw(uint16_t port, uint16_t data) __attribute__((always_inline));
|
||||
static inline void outsl(uint32_t port, const void *addr, int cnt) __attribute__((always_inline));
|
||||
static inline uint32_t read_ebp(void) __attribute__((always_inline));
|
||||
static inline void breakpoint(void) __attribute__((always_inline));
|
||||
static inline uint32_t read_dr(unsigned regnum) __attribute__((always_inline));
|
||||
static inline void write_dr(unsigned regnum, uint32_t value) __attribute__((always_inline));
|
||||
|
||||
/* Pseudo-descriptors used for LGDT, LLDT(not used) and LIDT instructions. */
|
||||
struct pseudodesc {
|
||||
uint16_t pd_lim; // Limit
|
||||
uintptr_t pd_base; // Base address
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static inline void lidt(struct pseudodesc *pd) __attribute__((always_inline));
|
||||
static inline void sti(void) __attribute__((always_inline));
|
||||
static inline void cli(void) __attribute__((always_inline));
|
||||
static inline void ltr(uint16_t sel) __attribute__((always_inline));
|
||||
static inline uint32_t read_eflags(void) __attribute__((always_inline));
|
||||
static inline void write_eflags(uint32_t eflags) __attribute__((always_inline));
|
||||
static inline void lcr0(uintptr_t cr0) __attribute__((always_inline));
|
||||
static inline void lcr3(uintptr_t cr3) __attribute__((always_inline));
|
||||
static inline uintptr_t rcr0(void) __attribute__((always_inline));
|
||||
static inline uintptr_t rcr1(void) __attribute__((always_inline));
|
||||
static inline uintptr_t rcr2(void) __attribute__((always_inline));
|
||||
static inline uintptr_t rcr3(void) __attribute__((always_inline));
|
||||
static inline void invlpg(void *addr) __attribute__((always_inline));
|
||||
|
||||
static inline uint8_t
|
||||
inb(uint16_t port) {
|
||||
uint8_t data;
|
||||
asm volatile ("inb %1, %0" : "=a" (data) : "d" (port) : "memory");
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline void
|
||||
insl(uint32_t port, void *addr, int cnt) {
|
||||
asm volatile (
|
||||
"cld;"
|
||||
"repne; insl;"
|
||||
: "=D" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outb(uint16_t port, uint8_t data) {
|
||||
asm volatile ("outb %0, %1" :: "a" (data), "d" (port) : "memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outw(uint16_t port, uint16_t data) {
|
||||
asm volatile ("outw %0, %1" :: "a" (data), "d" (port) : "memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
outsl(uint32_t port, const void *addr, int cnt) {
|
||||
asm volatile (
|
||||
"cld;"
|
||||
"repne; outsl;"
|
||||
: "=S" (addr), "=c" (cnt)
|
||||
: "d" (port), "0" (addr), "1" (cnt)
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_ebp(void) {
|
||||
uint32_t ebp;
|
||||
asm volatile ("movl %%ebp, %0" : "=r" (ebp));
|
||||
return ebp;
|
||||
}
|
||||
|
||||
static inline void
|
||||
breakpoint(void) {
|
||||
asm volatile ("int $3");
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_dr(unsigned regnum) {
|
||||
uint32_t value = 0;
|
||||
switch (regnum) {
|
||||
case 0: asm volatile ("movl %%db0, %0" : "=r" (value)); break;
|
||||
case 1: asm volatile ("movl %%db1, %0" : "=r" (value)); break;
|
||||
case 2: asm volatile ("movl %%db2, %0" : "=r" (value)); break;
|
||||
case 3: asm volatile ("movl %%db3, %0" : "=r" (value)); break;
|
||||
case 6: asm volatile ("movl %%db6, %0" : "=r" (value)); break;
|
||||
case 7: asm volatile ("movl %%db7, %0" : "=r" (value)); break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void
|
||||
write_dr(unsigned regnum, uint32_t value) {
|
||||
switch (regnum) {
|
||||
case 0: asm volatile ("movl %0, %%db0" :: "r" (value)); break;
|
||||
case 1: asm volatile ("movl %0, %%db1" :: "r" (value)); break;
|
||||
case 2: asm volatile ("movl %0, %%db2" :: "r" (value)); break;
|
||||
case 3: asm volatile ("movl %0, %%db3" :: "r" (value)); break;
|
||||
case 6: asm volatile ("movl %0, %%db6" :: "r" (value)); break;
|
||||
case 7: asm volatile ("movl %0, %%db7" :: "r" (value)); break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
lidt(struct pseudodesc *pd) {
|
||||
asm volatile ("lidt (%0)" :: "r" (pd) : "memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
sti(void) {
|
||||
asm volatile ("sti");
|
||||
}
|
||||
|
||||
static inline void
|
||||
cli(void) {
|
||||
asm volatile ("cli" ::: "memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
ltr(uint16_t sel) {
|
||||
asm volatile ("ltr %0" :: "r" (sel) : "memory");
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
read_eflags(void) {
|
||||
uint32_t eflags;
|
||||
asm volatile ("pushfl; popl %0" : "=r" (eflags));
|
||||
return eflags;
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_eflags(uint32_t eflags) {
|
||||
asm volatile ("pushl %0; popfl" :: "r" (eflags));
|
||||
}
|
||||
|
||||
static inline void
|
||||
lcr0(uintptr_t cr0) {
|
||||
asm volatile ("mov %0, %%cr0" :: "r" (cr0) : "memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
lcr3(uintptr_t cr3) {
|
||||
asm volatile ("mov %0, %%cr3" :: "r" (cr3) : "memory");
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
rcr0(void) {
|
||||
uintptr_t cr0;
|
||||
asm volatile ("mov %%cr0, %0" : "=r" (cr0) :: "memory");
|
||||
return cr0;
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
rcr1(void) {
|
||||
uintptr_t cr1;
|
||||
asm volatile ("mov %%cr1, %0" : "=r" (cr1) :: "memory");
|
||||
return cr1;
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
rcr2(void) {
|
||||
uintptr_t cr2;
|
||||
asm volatile ("mov %%cr2, %0" : "=r" (cr2) :: "memory");
|
||||
return cr2;
|
||||
}
|
||||
|
||||
static inline uintptr_t
|
||||
rcr3(void) {
|
||||
uintptr_t cr3;
|
||||
asm volatile ("mov %%cr3, %0" : "=r" (cr3) :: "memory");
|
||||
return cr3;
|
||||
}
|
||||
|
||||
static inline void
|
||||
invlpg(void *addr) {
|
||||
asm volatile ("invlpg (%0)" :: "r" (addr) : "memory");
|
||||
}
|
||||
|
||||
static inline int __strcmp(const char *s1, const char *s2) __attribute__((always_inline));
|
||||
static inline char *__strcpy(char *dst, const char *src) __attribute__((always_inline));
|
||||
static inline void *__memset(void *s, char c, size_t n) __attribute__((always_inline));
|
||||
static inline void *__memmove(void *dst, const void *src, size_t n) __attribute__((always_inline));
|
||||
static inline void *__memcpy(void *dst, const void *src, size_t n) __attribute__((always_inline));
|
||||
|
||||
#ifndef __HAVE_ARCH_STRCMP
|
||||
#define __HAVE_ARCH_STRCMP
|
||||
static inline int
|
||||
__strcmp(const char *s1, const char *s2) {
|
||||
int d0, d1, ret;
|
||||
asm volatile (
|
||||
"1: lodsb;"
|
||||
"scasb;"
|
||||
"jne 2f;"
|
||||
"testb %%al, %%al;"
|
||||
"jne 1b;"
|
||||
"xorl %%eax, %%eax;"
|
||||
"jmp 3f;"
|
||||
"2: sbbl %%eax, %%eax;"
|
||||
"orb $1, %%al;"
|
||||
"3:"
|
||||
: "=a" (ret), "=&S" (d0), "=&D" (d1)
|
||||
: "1" (s1), "2" (s2)
|
||||
: "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __HAVE_ARCH_STRCMP */
|
||||
|
||||
#ifndef __HAVE_ARCH_STRCPY
|
||||
#define __HAVE_ARCH_STRCPY
|
||||
static inline char *
|
||||
__strcpy(char *dst, const char *src) {
|
||||
int d0, d1, d2;
|
||||
asm volatile (
|
||||
"1: lodsb;"
|
||||
"stosb;"
|
||||
"testb %%al, %%al;"
|
||||
"jne 1b;"
|
||||
: "=&S" (d0), "=&D" (d1), "=&a" (d2)
|
||||
: "0" (src), "1" (dst) : "memory");
|
||||
return dst;
|
||||
}
|
||||
#endif /* __HAVE_ARCH_STRCPY */
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMSET
|
||||
#define __HAVE_ARCH_MEMSET
|
||||
static inline void *
|
||||
__memset(void *s, char c, size_t n) {
|
||||
int d0, d1;
|
||||
asm volatile (
|
||||
"rep; stosb;"
|
||||
: "=&c" (d0), "=&D" (d1)
|
||||
: "0" (n), "a" (c), "1" (s)
|
||||
: "memory");
|
||||
return s;
|
||||
}
|
||||
#endif /* __HAVE_ARCH_MEMSET */
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMMOVE
|
||||
#define __HAVE_ARCH_MEMMOVE
|
||||
static inline void *
|
||||
__memmove(void *dst, const void *src, size_t n) {
|
||||
if (dst < src) {
|
||||
return __memcpy(dst, src, n);
|
||||
}
|
||||
int d0, d1, d2;
|
||||
asm volatile (
|
||||
"std;"
|
||||
"rep; movsb;"
|
||||
"cld;"
|
||||
: "=&c" (d0), "=&S" (d1), "=&D" (d2)
|
||||
: "0" (n), "1" (n - 1 + src), "2" (n - 1 + dst)
|
||||
: "memory");
|
||||
return dst;
|
||||
}
|
||||
#endif /* __HAVE_ARCH_MEMMOVE */
|
||||
|
||||
#ifndef __HAVE_ARCH_MEMCPY
|
||||
#define __HAVE_ARCH_MEMCPY
|
||||
static inline void *
|
||||
__memcpy(void *dst, const void *src, size_t n) {
|
||||
int d0, d1, d2;
|
||||
asm volatile (
|
||||
"rep; movsl;"
|
||||
"movl %4, %%ecx;"
|
||||
"andl $3, %%ecx;"
|
||||
"jz 1f;"
|
||||
"rep; movsb;"
|
||||
"1:"
|
||||
: "=&c" (d0), "=&D" (d1), "=&S" (d2)
|
||||
: "0" (n / 4), "g" (n), "1" (dst), "2" (src)
|
||||
: "memory");
|
||||
return dst;
|
||||
}
|
||||
#endif /* __HAVE_ARCH_MEMCPY */
|
||||
|
||||
#endif /* !__LIBS_X86_H__ */
|
||||
|
15
related_info/lab5/lab5-spoc-discuss/tools/boot.ld
Normal file
15
related_info/lab5/lab5-spoc-discuss/tools/boot.ld
Normal file
@ -0,0 +1,15 @@
|
||||
OUTPUT_FORMAT("elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
|
||||
SECTIONS {
|
||||
. = 0x7C00;
|
||||
|
||||
.startup : {
|
||||
*bootasm.o(.text)
|
||||
}
|
||||
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data .rodata) }
|
||||
|
||||
/DISCARD/ : { *(.eh_*) }
|
||||
}
|
95
related_info/lab5/lab5-spoc-discuss/tools/function.mk
Normal file
95
related_info/lab5/lab5-spoc-discuss/tools/function.mk
Normal file
@ -0,0 +1,95 @@
|
||||
OBJPREFIX := __objs_
|
||||
|
||||
.SECONDEXPANSION:
|
||||
# -------------------- function begin --------------------
|
||||
|
||||
# list all files in some directories: (#directories, #types)
|
||||
listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\
|
||||
$(wildcard $(addsuffix $(SLASH)*,$(1))))
|
||||
|
||||
# get .o obj files: (#files[, packet])
|
||||
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\
|
||||
$(addsuffix .o,$(basename $(1))))
|
||||
|
||||
# get .d dependency files: (#files[, packet])
|
||||
todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2)))
|
||||
|
||||
totarget = $(addprefix $(BINDIR)$(SLASH),$(1))
|
||||
|
||||
# change $(name) to $(OBJPREFIX)$(name): (#names)
|
||||
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX))
|
||||
|
||||
# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir])
|
||||
define cc_template
|
||||
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@)
|
||||
@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@
|
||||
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@)
|
||||
@echo + cc $$<
|
||||
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@
|
||||
ALLOBJS += $$(call toobj,$(1),$(4))
|
||||
endef
|
||||
|
||||
# compile file: (#files, cc[, flags, dir])
|
||||
define do_cc_compile
|
||||
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4))))
|
||||
endef
|
||||
|
||||
# add files to packet: (#files, cc[, flags, packet, dir])
|
||||
define do_add_files_to_packet
|
||||
__temp_packet__ := $(call packetname,$(4))
|
||||
ifeq ($$(origin $$(__temp_packet__)),undefined)
|
||||
$$(__temp_packet__) :=
|
||||
endif
|
||||
__temp_objs__ := $(call toobj,$(1),$(5))
|
||||
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5))))
|
||||
$$(__temp_packet__) += $$(__temp_objs__)
|
||||
endef
|
||||
|
||||
# add objs to packet: (#objs, packet)
|
||||
define do_add_objs_to_packet
|
||||
__temp_packet__ := $(call packetname,$(2))
|
||||
ifeq ($$(origin $$(__temp_packet__)),undefined)
|
||||
$$(__temp_packet__) :=
|
||||
endif
|
||||
$$(__temp_packet__) += $(1)
|
||||
endef
|
||||
|
||||
# add packets and objs to target (target, #packes, #objs[, cc, flags])
|
||||
define do_create_target
|
||||
__temp_target__ = $(call totarget,$(1))
|
||||
__temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3)
|
||||
TARGETS += $$(__temp_target__)
|
||||
ifneq ($(4),)
|
||||
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@)
|
||||
$(V)$(4) $(5) $$^ -o $$@
|
||||
else
|
||||
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@)
|
||||
endif
|
||||
endef
|
||||
|
||||
# finish all
|
||||
define do_finish_all
|
||||
ALLDEPS = $$(ALLOBJS:.o=.d)
|
||||
$$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) $(OBJDIR)$(SLASH)):
|
||||
@$(MKDIR) $$@
|
||||
endef
|
||||
|
||||
# -------------------- function end --------------------
|
||||
# compile file: (#files, cc[, flags, dir])
|
||||
cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4)))
|
||||
|
||||
# add files to packet: (#files, cc[, flags, packet, dir])
|
||||
add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5)))
|
||||
|
||||
# add objs to packet: (#objs, packet)
|
||||
add_objs = $(eval $(call do_add_objs_to_packet,$(1),$(2)))
|
||||
|
||||
# add packets and objs to target (target, #packes, #objs, cc, [, flags])
|
||||
create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5)))
|
||||
|
||||
read_packet = $(foreach p,$(call packetname,$(1)),$($(p)))
|
||||
|
||||
add_dependency = $(eval $(1): $(2))
|
||||
|
||||
finish_all = $(eval $(call do_finish_all))
|
||||
|
3
related_info/lab5/lab5-spoc-discuss/tools/gdbinit
Normal file
3
related_info/lab5/lab5-spoc-discuss/tools/gdbinit
Normal file
@ -0,0 +1,3 @@
|
||||
file bin/kernel
|
||||
target remote :1234
|
||||
break kern_init
|
556
related_info/lab5/lab5-spoc-discuss/tools/grade.sh
Normal file
556
related_info/lab5/lab5-spoc-discuss/tools/grade.sh
Normal file
@ -0,0 +1,556 @@
|
||||
#!/bin/sh
|
||||
|
||||
verbose=false
|
||||
if [ "x$1" = "x-v" ]; then
|
||||
verbose=true
|
||||
out=/dev/stdout
|
||||
err=/dev/stderr
|
||||
else
|
||||
out=/dev/null
|
||||
err=/dev/null
|
||||
fi
|
||||
|
||||
## make & makeopts
|
||||
if gmake --version > /dev/null 2>&1; then
|
||||
make=gmake;
|
||||
else
|
||||
make=make;
|
||||
fi
|
||||
|
||||
makeopts="--quiet --no-print-directory -j"
|
||||
|
||||
make_print() {
|
||||
echo `$make $makeopts print-$1`
|
||||
}
|
||||
|
||||
## command tools
|
||||
awk='awk'
|
||||
bc='bc'
|
||||
date='date'
|
||||
grep='grep'
|
||||
rm='rm -f'
|
||||
sed='sed'
|
||||
|
||||
## symbol table
|
||||
sym_table='obj/kernel.sym'
|
||||
|
||||
## gdb & gdbopts
|
||||
gdb="$(make_print GDB)"
|
||||
gdbport='1234'
|
||||
|
||||
gdb_in="$(make_print GRADE_GDB_IN)"
|
||||
|
||||
## qemu & qemuopts
|
||||
qemu="$(make_print qemu)"
|
||||
|
||||
qemu_out="$(make_print GRADE_QEMU_OUT)"
|
||||
|
||||
if $qemu -nographic -help | grep -q '^-gdb'; then
|
||||
qemugdb="-gdb tcp::$gdbport"
|
||||
else
|
||||
qemugdb="-s -p $gdbport"
|
||||
fi
|
||||
|
||||
## default variables
|
||||
default_timeout=30
|
||||
default_pts=5
|
||||
|
||||
pts=5
|
||||
part=0
|
||||
part_pos=0
|
||||
total=0
|
||||
total_pos=0
|
||||
|
||||
## default functions
|
||||
update_score() {
|
||||
total=`expr $total + $part`
|
||||
total_pos=`expr $total_pos + $part_pos`
|
||||
part=0
|
||||
part_pos=0
|
||||
}
|
||||
|
||||
get_time() {
|
||||
echo `$date +%s.%N 2> /dev/null`
|
||||
}
|
||||
|
||||
show_part() {
|
||||
echo "Part $1 Score: $part/$part_pos"
|
||||
echo
|
||||
update_score
|
||||
}
|
||||
|
||||
show_final() {
|
||||
update_score
|
||||
echo "Total Score: $total/$total_pos"
|
||||
if [ $total -lt $total_pos ]; then
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
show_time() {
|
||||
t1=$(get_time)
|
||||
time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null`
|
||||
echo "(${time}s)"
|
||||
}
|
||||
|
||||
show_build_tag() {
|
||||
echo "$1:" | $awk '{printf "%-24s ", $0}'
|
||||
}
|
||||
|
||||
show_check_tag() {
|
||||
echo "$1:" | $awk '{printf " -%-40s ", $0}'
|
||||
}
|
||||
|
||||
show_msg() {
|
||||
echo $1
|
||||
shift
|
||||
if [ $# -gt 0 ]; then
|
||||
echo "$@" | awk '{printf " %s\n", $0}'
|
||||
echo
|
||||
fi
|
||||
}
|
||||
|
||||
pass() {
|
||||
show_msg OK "$@"
|
||||
part=`expr $part + $pts`
|
||||
part_pos=`expr $part_pos + $pts`
|
||||
}
|
||||
|
||||
fail() {
|
||||
show_msg WRONG "$@"
|
||||
part_pos=`expr $part_pos + $pts`
|
||||
}
|
||||
|
||||
run_qemu() {
|
||||
# Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty,
|
||||
# wait until $brkfun is reached or $timeout expires, then kill QEMU
|
||||
qemuextra=
|
||||
if [ "$brkfun" ]; then
|
||||
qemuextra="-S $qemugdb"
|
||||
fi
|
||||
|
||||
if [ -z "$timeout" ] || [ $timeout -le 0 ]; then
|
||||
timeout=$default_timeout;
|
||||
fi
|
||||
|
||||
t0=$(get_time)
|
||||
(
|
||||
ulimit -t $timeout
|
||||
exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra
|
||||
) > $out 2> $err &
|
||||
pid=$!
|
||||
|
||||
# wait for QEMU to start
|
||||
sleep 1
|
||||
|
||||
if [ -n "$brkfun" ]; then
|
||||
# find the address of the kernel $brkfun function
|
||||
brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'`
|
||||
(
|
||||
echo "target remote localhost:$gdbport"
|
||||
echo "break *0x$brkaddr"
|
||||
echo "continue"
|
||||
) > $gdb_in
|
||||
|
||||
$gdb -batch -nx -x $gdb_in > /dev/null 2>&1
|
||||
|
||||
# make sure that QEMU is dead
|
||||
# on OS X, exiting gdb doesn't always exit qemu
|
||||
kill $pid > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
build_run() {
|
||||
# usage: build_run <tag> <args>
|
||||
show_build_tag "$1"
|
||||
shift
|
||||
|
||||
if $verbose; then
|
||||
echo "$make $@ ..."
|
||||
fi
|
||||
$make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $make $@ failed
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# now run qemu and save the output
|
||||
run_qemu
|
||||
|
||||
show_time
|
||||
}
|
||||
|
||||
check_result() {
|
||||
# usage: check_result <tag> <check> <check args...>
|
||||
show_check_tag "$1"
|
||||
shift
|
||||
|
||||
# give qemu some time to run (for asynchronous mode)
|
||||
if [ ! -s $qemu_out ]; then
|
||||
sleep 4
|
||||
fi
|
||||
|
||||
if [ ! -s $qemu_out ]; then
|
||||
fail > /dev/null
|
||||
echo 'no $qemu_out'
|
||||
else
|
||||
check=$1
|
||||
shift
|
||||
$check "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
check_regexps() {
|
||||
okay=yes
|
||||
not=0
|
||||
reg=0
|
||||
error=
|
||||
for i do
|
||||
if [ "x$i" = "x!" ]; then
|
||||
not=1
|
||||
elif [ "x$i" = "x-" ]; then
|
||||
reg=1
|
||||
else
|
||||
if [ $reg -ne 0 ]; then
|
||||
$grep '-E' "^$i\$" $qemu_out > /dev/null
|
||||
else
|
||||
$grep '-F' "$i" $qemu_out > /dev/null
|
||||
fi
|
||||
found=$(($? == 0))
|
||||
if [ $found -eq $not ]; then
|
||||
if [ $found -eq 0 ]; then
|
||||
msg="!! error: missing '$i'"
|
||||
else
|
||||
msg="!! error: got unexpected line '$i'"
|
||||
fi
|
||||
okay=no
|
||||
if [ -z "$error" ]; then
|
||||
error="$msg"
|
||||
else
|
||||
error="$error\n$msg"
|
||||
fi
|
||||
fi
|
||||
not=0
|
||||
reg=0
|
||||
fi
|
||||
done
|
||||
if [ "$okay" = "yes" ]; then
|
||||
pass
|
||||
else
|
||||
fail "$error"
|
||||
if $verbose; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
run_test() {
|
||||
# usage: run_test [-tag <tag>] [-prog <prog>] [-Ddef...] [-check <check>] checkargs ...
|
||||
tag=
|
||||
prog=
|
||||
check=check_regexps
|
||||
while true; do
|
||||
select=
|
||||
case $1 in
|
||||
-tag|-prog)
|
||||
select=`expr substr $1 2 ${#1}`
|
||||
eval $select='$2'
|
||||
;;
|
||||
esac
|
||||
if [ -z "$select" ]; then
|
||||
break
|
||||
fi
|
||||
shift
|
||||
shift
|
||||
done
|
||||
defs=
|
||||
while expr "x$1" : "x-D.*" > /dev/null; do
|
||||
defs="DEFS+='$1' $defs"
|
||||
shift
|
||||
done
|
||||
if [ "x$1" = "x-check" ]; then
|
||||
check=$2
|
||||
shift
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ -z "$prog" ]; then
|
||||
$make $makeopts touch > /dev/null 2>&1
|
||||
args="$defs"
|
||||
else
|
||||
if [ -z "$tag" ]; then
|
||||
tag="$prog"
|
||||
fi
|
||||
args="build-$prog $defs"
|
||||
fi
|
||||
|
||||
build_run "$tag" "$args"
|
||||
|
||||
check_result 'check result' "$check" "$@"
|
||||
}
|
||||
|
||||
quick_run() {
|
||||
# usage: quick_run <tag> [-Ddef...]
|
||||
tag="$1"
|
||||
shift
|
||||
defs=
|
||||
while expr "x$1" : "x-D.*" > /dev/null; do
|
||||
defs="DEFS+='$1' $defs"
|
||||
shift
|
||||
done
|
||||
|
||||
$make $makeopts touch > /dev/null 2>&1
|
||||
build_run "$tag" "$defs"
|
||||
}
|
||||
|
||||
quick_check() {
|
||||
# usage: quick_check <tag> checkargs ...
|
||||
tag="$1"
|
||||
shift
|
||||
check_result "$tag" check_regexps "$@"
|
||||
}
|
||||
|
||||
## kernel image
|
||||
osimg=$(make_print ucoreimg)
|
||||
|
||||
## swap image
|
||||
swapimg=$(make_print swapimg)
|
||||
|
||||
## set default qemu-options
|
||||
qemuopts="-hda $osimg -drive file=$swapimg,media=disk,cache=writeback"
|
||||
|
||||
## set break-function, default is readline
|
||||
brkfun=readline
|
||||
|
||||
default_check() {
|
||||
pts=7
|
||||
check_regexps "$@"
|
||||
|
||||
pts=3
|
||||
quick_check 'check output' \
|
||||
'memory management: default_pmm_manager' \
|
||||
'check_alloc_page() succeeded!' \
|
||||
'check_pgdir() succeeded!' \
|
||||
'check_boot_pgdir() succeeded!' \
|
||||
'PDE(0e0) c0000000-f8000000 38000000 urw' \
|
||||
' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \
|
||||
'PDE(001) fac00000-fb000000 00400000 -rw' \
|
||||
' |-- PTE(000e0) faf00000-fafe0000 000e0000 urw' \
|
||||
' |-- PTE(00001) fafeb000-fafec000 00001000 -rw' \
|
||||
'check_slab() succeeded!' \
|
||||
'check_vma_struct() succeeded!' \
|
||||
'page fault at 0x00000100: K/W [no page found].' \
|
||||
'check_pgfault() succeeded!' \
|
||||
'check_vmm() succeeded.' \
|
||||
'page fault at 0x00001000: K/W [no page found].' \
|
||||
'page fault at 0x00002000: K/W [no page found].' \
|
||||
'page fault at 0x00003000: K/W [no page found].' \
|
||||
'page fault at 0x00004000: K/W [no page found].' \
|
||||
'write Virt Page e in fifo_check_swap' \
|
||||
'page fault at 0x00005000: K/W [no page found].' \
|
||||
'page fault at 0x00001000: K/W [no page found]' \
|
||||
'page fault at 0x00002000: K/W [no page found].' \
|
||||
'page fault at 0x00003000: K/W [no page found].' \
|
||||
'page fault at 0x00004000: K/W [no page found].' \
|
||||
'check_swap() succeeded!' \
|
||||
'++ setup timer interrupts'
|
||||
}
|
||||
|
||||
## check now!!
|
||||
|
||||
run_test -prog 'badsegment' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "badsegment".' \
|
||||
- 'trapframe at 0xc.......' \
|
||||
'trap 0x0000000d General Protection' \
|
||||
' err 0x00000028' \
|
||||
- ' eip 0x008.....' \
|
||||
- ' esp 0xaff.....' \
|
||||
' cs 0x----001b' \
|
||||
' ss 0x----0023' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
run_test -prog 'divzero' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "divzero".' \
|
||||
- 'trapframe at 0xc.......' \
|
||||
'trap 0x00000000 Divide error' \
|
||||
- ' eip 0x008.....' \
|
||||
- ' esp 0xaff.....' \
|
||||
' cs 0x----001b' \
|
||||
' ss 0x----0023' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
run_test -prog 'softint' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "softint".' \
|
||||
- 'trapframe at 0xc.......' \
|
||||
'trap 0x0000000d General Protection' \
|
||||
' err 0x00000072' \
|
||||
- ' eip 0x008.....' \
|
||||
- ' esp 0xaff.....' \
|
||||
' cs 0x----001b' \
|
||||
' ss 0x----0023' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
pts=10
|
||||
|
||||
run_test -prog 'faultread' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "faultread".' \
|
||||
- 'trapframe at 0xc.......' \
|
||||
'trap 0x0000000e Page Fault' \
|
||||
' err 0x00000004' \
|
||||
- ' eip 0x008.....' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
run_test -prog 'faultreadkernel' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "faultreadkernel".' \
|
||||
- 'trapframe at 0xc.......' \
|
||||
'trap 0x0000000e Page Fault' \
|
||||
' err 0x00000005' \
|
||||
- ' eip 0x008.....' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
run_test -prog 'hello' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "hello".' \
|
||||
'Hello world!!.' \
|
||||
'I am process 2.' \
|
||||
'hello pass.'
|
||||
|
||||
run_test -prog 'testbss' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "testbss".' \
|
||||
'Making sure bss works right...' \
|
||||
'Yes, good. Now doing a wild write off the end...' \
|
||||
'testbss may pass.' \
|
||||
- 'trapframe at 0xc.......' \
|
||||
'trap 0x0000000e Page Fault' \
|
||||
' err 0x00000006' \
|
||||
- ' eip 0x008.....' \
|
||||
'killed by kernel.' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
run_test -prog 'pgdir' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "pgdir".' \
|
||||
'I am 2, print pgdir.' \
|
||||
'PDE(001) 00800000-00c00000 00400000 urw' \
|
||||
' |-- PTE(00002) 00800000-00802000 00002000 ur-' \
|
||||
' |-- PTE(00001) 00802000-00803000 00001000 urw' \
|
||||
'PDE(001) afc00000-b0000000 00400000 urw' \
|
||||
' |-- PTE(00004) afffc000-b0000000 00004000 urw' \
|
||||
'PDE(0e0) c0000000-f8000000 38000000 urw' \
|
||||
' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \
|
||||
'pgdir pass.'
|
||||
|
||||
run_test -prog 'yield' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "yield".' \
|
||||
'Hello, I am process 2.' \
|
||||
'Back in process 2, iteration 0.' \
|
||||
'Back in process 2, iteration 1.' \
|
||||
'Back in process 2, iteration 2.' \
|
||||
'Back in process 2, iteration 3.' \
|
||||
'Back in process 2, iteration 4.' \
|
||||
'All done in process 2.' \
|
||||
'yield pass.'
|
||||
|
||||
|
||||
run_test -prog 'badarg' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "badarg".' \
|
||||
'fork ok.' \
|
||||
'badarg pass.' \
|
||||
'all user-mode processes have quit.' \
|
||||
'init check memory pass.' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
pts=10
|
||||
|
||||
run_test -prog 'exit' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "exit".' \
|
||||
'I am the parent. Forking the child...' \
|
||||
'I am the parent, waiting now..' \
|
||||
'I am the child.' \
|
||||
- 'waitpid [0-9]+ ok\.' \
|
||||
'exit pass.' \
|
||||
'all user-mode processes have quit.' \
|
||||
'init check memory pass.' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
run_test -prog 'spin' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "spin".' \
|
||||
'I am the parent. Forking the child...' \
|
||||
'I am the parent. Running the child...' \
|
||||
'I am the child. spinning ...' \
|
||||
'I am the parent. Killing the child...' \
|
||||
'kill returns 0' \
|
||||
'wait returns 0' \
|
||||
'spin may pass.' \
|
||||
'all user-mode processes have quit.' \
|
||||
'init check memory pass.' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
run_test -prog 'waitkill' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "waitkill".' \
|
||||
'wait child 1.' \
|
||||
'child 2.' \
|
||||
'child 1.' \
|
||||
'kill parent ok.' \
|
||||
'kill child1 ok.' \
|
||||
'all user-mode processes have quit.' \
|
||||
'init check memory pass.' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
pts=15
|
||||
|
||||
run_test -prog 'forktest' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "forktest".' \
|
||||
'I am child 31' \
|
||||
'I am child 19' \
|
||||
'I am child 13' \
|
||||
'I am child 0' \
|
||||
'forktest pass.' \
|
||||
'all user-mode processes have quit.' \
|
||||
'init check memory pass.' \
|
||||
! - 'fork claimed to work [0-9]+ times!' \
|
||||
! 'wait stopped early' \
|
||||
! 'wait got too many' \
|
||||
! - 'user panic at .*'
|
||||
|
||||
pts=10
|
||||
run_test -prog 'forktree' -check default_check \
|
||||
'kernel_execve: pid = 2, name = "forktree".' \
|
||||
- '....: I am '\'''\' \
|
||||
- '....: I am '\''0'\' \
|
||||
- '....: I am '\'''\' \
|
||||
- '....: I am '\''1'\' \
|
||||
- '....: I am '\''0'\' \
|
||||
- '....: I am '\''01'\' \
|
||||
- '....: I am '\''00'\' \
|
||||
- '....: I am '\''11'\' \
|
||||
- '....: I am '\''10'\' \
|
||||
- '....: I am '\''101'\' \
|
||||
- '....: I am '\''100'\' \
|
||||
- '....: I am '\''111'\' \
|
||||
- '....: I am '\''110'\' \
|
||||
- '....: I am '\''001'\' \
|
||||
- '....: I am '\''000'\' \
|
||||
- '....: I am '\''011'\' \
|
||||
- '....: I am '\''010'\' \
|
||||
- '....: I am '\''0101'\' \
|
||||
- '....: I am '\''0100'\' \
|
||||
- '....: I am '\''0111'\' \
|
||||
- '....: I am '\''0110'\' \
|
||||
- '....: I am '\''0001'\' \
|
||||
- '....: I am '\''0000'\' \
|
||||
- '....: I am '\''0011'\' \
|
||||
- '....: I am '\''0010'\' \
|
||||
- '....: I am '\''1101'\' \
|
||||
- '....: I am '\''1100'\' \
|
||||
- '....: I am '\''1111'\' \
|
||||
- '....: I am '\''1110'\' \
|
||||
- '....: I am '\''1001'\' \
|
||||
- '....: I am '\''1000'\' \
|
||||
- '....: I am '\''1011'\' \
|
||||
- '....: I am '\''1010'\' \
|
||||
'all user-mode processes have quit.' \
|
||||
'init check memory pass.'
|
||||
|
||||
## print final-score
|
||||
show_final
|
||||
|
58
related_info/lab5/lab5-spoc-discuss/tools/kernel.ld
Normal file
58
related_info/lab5/lab5-spoc-discuss/tools/kernel.ld
Normal file
@ -0,0 +1,58 @@
|
||||
/* Simple linker script for the ucore kernel.
|
||||
See the GNU ld 'info' manual ("info ld") to learn the syntax. */
|
||||
|
||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
|
||||
OUTPUT_ARCH(i386)
|
||||
ENTRY(kern_entry)
|
||||
|
||||
SECTIONS {
|
||||
/* Load the kernel at this address: "." means the current address */
|
||||
. = 0xC0100000;
|
||||
|
||||
.text : {
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
}
|
||||
|
||||
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
}
|
||||
|
||||
/* Include debugging information in kernel memory */
|
||||
.stab : {
|
||||
PROVIDE(__STAB_BEGIN__ = .);
|
||||
*(.stab);
|
||||
PROVIDE(__STAB_END__ = .);
|
||||
BYTE(0) /* Force the linker to allocate space
|
||||
for this section */
|
||||
}
|
||||
|
||||
.stabstr : {
|
||||
PROVIDE(__STABSTR_BEGIN__ = .);
|
||||
*(.stabstr);
|
||||
PROVIDE(__STABSTR_END__ = .);
|
||||
BYTE(0) /* Force the linker to allocate space
|
||||
for this section */
|
||||
}
|
||||
|
||||
/* Adjust the address for the data segment to the next page */
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
/* The data segment */
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
PROVIDE(edata = .);
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
PROVIDE(end = .);
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame .note.GNU-stack)
|
||||
}
|
||||
}
|
43
related_info/lab5/lab5-spoc-discuss/tools/sign.c
Normal file
43
related_info/lab5/lab5-spoc-discuss/tools/sign.c
Normal file
@ -0,0 +1,43 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
struct stat st;
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: <input filename> <output filename>\n");
|
||||
return -1;
|
||||
}
|
||||
if (stat(argv[1], &st) != 0) {
|
||||
fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size);
|
||||
if (st.st_size > 510) {
|
||||
fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size);
|
||||
return -1;
|
||||
}
|
||||
char buf[512];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
FILE *ifp = fopen(argv[1], "rb");
|
||||
int size = fread(buf, 1, st.st_size, ifp);
|
||||
if (size != st.st_size) {
|
||||
fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size);
|
||||
return -1;
|
||||
}
|
||||
fclose(ifp);
|
||||
buf[510] = 0x55;
|
||||
buf[511] = 0xAA;
|
||||
FILE *ofp = fopen(argv[2], "wb+");
|
||||
size = fwrite(buf, 1, 512, ofp);
|
||||
if (size != 512) {
|
||||
fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size);
|
||||
return -1;
|
||||
}
|
||||
fclose(ofp);
|
||||
printf("build 512 bytes boot sector: '%s' success!\n", argv[2]);
|
||||
return 0;
|
||||
}
|
||||
|
71
related_info/lab5/lab5-spoc-discuss/tools/user.ld
Normal file
71
related_info/lab5/lab5-spoc-discuss/tools/user.ld
Normal file
@ -0,0 +1,71 @@
|
||||
/* Simple linker script for ucore 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)
|
||||
}
|
||||
}
|
29
related_info/lab5/lab5-spoc-discuss/tools/vector.c
Normal file
29
related_info/lab5/lab5-spoc-discuss/tools/vector.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int
|
||||
main(void) {
|
||||
printf("# handler\n");
|
||||
printf(".text\n");
|
||||
printf(".globl __alltraps\n");
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 256; i ++) {
|
||||
printf(".globl vector%d\n", i);
|
||||
printf("vector%d:\n", i);
|
||||
if ((i < 8 || i > 14) && i != 17) {
|
||||
printf(" pushl $0\n");
|
||||
}
|
||||
printf(" pushl $%d\n", i);
|
||||
printf(" jmp __alltraps\n");
|
||||
}
|
||||
printf("\n");
|
||||
printf("# vector table\n");
|
||||
printf(".data\n");
|
||||
printf(".globl __vectors\n");
|
||||
printf("__vectors:\n");
|
||||
for (i = 0; i < 256; i ++) {
|
||||
printf(" .long vector%d\n", i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
22
related_info/lab5/lab5-spoc-discuss/user/badarg.c
Normal file
22
related_info/lab5/lab5-spoc-discuss/user/badarg.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
int
|
||||
main(void) {
|
||||
int pid, exit_code;
|
||||
if ((pid = fork()) == 0) {
|
||||
cprintf("fork ok.\n");
|
||||
int i;
|
||||
for (i = 0; i < 10; i ++) {
|
||||
yield();
|
||||
}
|
||||
exit(0xbeaf);
|
||||
}
|
||||
assert(pid > 0);
|
||||
assert(waitpid(-1, NULL) != 0);
|
||||
assert(waitpid(pid, (void *)0xC0000000) != 0);
|
||||
assert(waitpid(pid, &exit_code) == 0 && exit_code == 0xbeaf);
|
||||
cprintf("badarg pass.\n");
|
||||
return 0;
|
||||
}
|
||||
|
11
related_info/lab5/lab5-spoc-discuss/user/badsegment.c
Normal file
11
related_info/lab5/lab5-spoc-discuss/user/badsegment.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
/* try to load the kernel's TSS selector into the DS register */
|
||||
|
||||
int
|
||||
main(void) {
|
||||
asm volatile("movw $0x28,%ax; movw %ax,%ds");
|
||||
panic("FAIL: T.T\n");
|
||||
}
|
||||
|
11
related_info/lab5/lab5-spoc-discuss/user/divzero.c
Normal file
11
related_info/lab5/lab5-spoc-discuss/user/divzero.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
int zero;
|
||||
|
||||
int
|
||||
main(void) {
|
||||
cprintf("value is %d.\n", 1 / zero);
|
||||
panic("FAIL: T.T\n");
|
||||
}
|
||||
|
34
related_info/lab5/lab5-spoc-discuss/user/exit.c
Normal file
34
related_info/lab5/lab5-spoc-discuss/user/exit.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
int magic = -0x10384;
|
||||
|
||||
int
|
||||
main(void) {
|
||||
int pid, code;
|
||||
cprintf("I am the parent. Forking the child...\n");
|
||||
if ((pid = fork()) == 0) {
|
||||
cprintf("I am the child.\n");
|
||||
yield();
|
||||
yield();
|
||||
yield();
|
||||
yield();
|
||||
yield();
|
||||
yield();
|
||||
yield();
|
||||
exit(magic);
|
||||
}
|
||||
else {
|
||||
cprintf("I am parent, fork a child pid %d\n",pid);
|
||||
}
|
||||
assert(pid > 0);
|
||||
cprintf("I am the parent, waiting now..\n");
|
||||
|
||||
assert(waitpid(pid, &code) == 0 && code == magic);
|
||||
assert(waitpid(pid, &code) != 0 && wait() != 0);
|
||||
cprintf("waitpid %d ok.\n", pid);
|
||||
|
||||
cprintf("exit pass.\n");
|
||||
return 0;
|
||||
}
|
||||
|
9
related_info/lab5/lab5-spoc-discuss/user/faultread.c
Normal file
9
related_info/lab5/lab5-spoc-discuss/user/faultread.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
int
|
||||
main(void) {
|
||||
cprintf("I read %8x from 0.\n", *(unsigned int *)0);
|
||||
panic("FAIL: T.T\n");
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
int
|
||||
main(void) {
|
||||
cprintf("I read %08x from 0xfac00000!\n", *(unsigned *)0xfac00000);
|
||||
panic("FAIL: T.T\n");
|
||||
}
|
||||
|
34
related_info/lab5/lab5-spoc-discuss/user/forktest.c
Normal file
34
related_info/lab5/lab5-spoc-discuss/user/forktest.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include <ulib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
const int max_child = 32;
|
||||
|
||||
int
|
||||
main(void) {
|
||||
int n, pid;
|
||||
for (n = 0; n < max_child; n ++) {
|
||||
if ((pid = fork()) == 0) {
|
||||
cprintf("I am child %d\n", n);
|
||||
exit(0);
|
||||
}
|
||||
assert(pid > 0);
|
||||
}
|
||||
|
||||
if (n > max_child) {
|
||||
panic("fork claimed to work %d times!\n", n);
|
||||
}
|
||||
|
||||
for (; n > 0; n --) {
|
||||
if (wait() != 0) {
|
||||
panic("wait stopped early\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (wait() == 0) {
|
||||
panic("wait got too many\n");
|
||||
}
|
||||
|
||||
cprintf("forktest pass.\n");
|
||||
return 0;
|
||||
}
|
||||
|
37
related_info/lab5/lab5-spoc-discuss/user/forktree.c
Normal file
37
related_info/lab5/lab5-spoc-discuss/user/forktree.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include <ulib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DEPTH 4
|
||||
|
||||
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);
|
||||
yield();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
forktree(const char *cur) {
|
||||
cprintf("%04x: I am '%s'\n", getpid(), cur);
|
||||
|
||||
forkchild(cur, '0');
|
||||
forkchild(cur, '1');
|
||||
}
|
||||
|
||||
int
|
||||
main(void) {
|
||||
forktree("");
|
||||
return 0;
|
||||
}
|
||||
|
11
related_info/lab5/lab5-spoc-discuss/user/hello.c
Normal file
11
related_info/lab5/lab5-spoc-discuss/user/hello.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
int
|
||||
main(void) {
|
||||
cprintf("Hello world!!.\n");
|
||||
cprintf("I am process %d.\n", getpid());
|
||||
cprintf("hello pass.\n");
|
||||
return 0;
|
||||
}
|
||||
|
14
related_info/lab5/lab5-spoc-discuss/user/libs/initcode.S
Normal file
14
related_info/lab5/lab5-spoc-discuss/user/libs/initcode.S
Normal file
@ -0,0 +1,14 @@
|
||||
.text
|
||||
.globl _start
|
||||
_start:
|
||||
# set ebp for backtrace
|
||||
movl $0x0, %ebp
|
||||
|
||||
# move down the esp register
|
||||
# since it may cause page fault in backtrace
|
||||
subl $0x20, %esp
|
||||
|
||||
# call user-program function
|
||||
call umain
|
||||
1: jmp 1b
|
||||
|
28
related_info/lab5/lab5-spoc-discuss/user/libs/panic.c
Normal file
28
related_info/lab5/lab5-spoc-discuss/user/libs/panic.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include <defs.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
#include <error.h>
|
||||
|
||||
void
|
||||
__panic(const char *file, int line, const char *fmt, ...) {
|
||||
// print the 'message'
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
cprintf("user panic at %s:%d:\n ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
exit(-E_PANIC);
|
||||
}
|
||||
|
||||
void
|
||||
__warn(const char *file, int line, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
cprintf("user warning at %s:%d:\n ", file, line);
|
||||
vcprintf(fmt, ap);
|
||||
cprintf("\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
62
related_info/lab5/lab5-spoc-discuss/user/libs/stdio.c
Normal file
62
related_info/lab5/lab5-spoc-discuss/user/libs/stdio.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include <defs.h>
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
/* *
|
||||
* cputch - writes a single character @c to stdout, and it will
|
||||
* increace the value of counter pointed by @cnt.
|
||||
* */
|
||||
static void
|
||||
cputch(int c, int *cnt) {
|
||||
sys_putc(c);
|
||||
(*cnt) ++;
|
||||
}
|
||||
|
||||
/* *
|
||||
* vcprintf - format a string and writes it to stdout
|
||||
*
|
||||
* The return value is the number of characters which would be
|
||||
* written to stdout.
|
||||
*
|
||||
* Call this function if you are already dealing with a va_list.
|
||||
* Or you probably want cprintf() instead.
|
||||
* */
|
||||
int
|
||||
vcprintf(const char *fmt, va_list ap) {
|
||||
int cnt = 0;
|
||||
vprintfmt((void*)cputch, &cnt, fmt, ap);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* *
|
||||
* cprintf - formats a string and writes it to stdout
|
||||
*
|
||||
* The return value is the number of characters which would be
|
||||
* written to stdout.
|
||||
* */
|
||||
int
|
||||
cprintf(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
int cnt = vcprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* *
|
||||
* cputs- writes the string pointed by @str to stdout and
|
||||
* appends a newline character.
|
||||
* */
|
||||
int
|
||||
cputs(const char *str) {
|
||||
int cnt = 0;
|
||||
char c;
|
||||
while ((c = *str ++) != '\0') {
|
||||
cputch(c, &cnt);
|
||||
}
|
||||
cputch('\n', &cnt);
|
||||
return cnt;
|
||||
}
|
||||
|
72
related_info/lab5/lab5-spoc-discuss/user/libs/syscall.c
Normal file
72
related_info/lab5/lab5-spoc-discuss/user/libs/syscall.c
Normal file
@ -0,0 +1,72 @@
|
||||
#include <defs.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <syscall.h>
|
||||
|
||||
#define MAX_ARGS 5
|
||||
|
||||
static inline int
|
||||
syscall(int num, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, num);
|
||||
uint32_t a[MAX_ARGS];
|
||||
int i, ret;
|
||||
for (i = 0; i < MAX_ARGS; i ++) {
|
||||
a[i] = va_arg(ap, uint32_t);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
asm volatile (
|
||||
"int %1;"
|
||||
: "=a" (ret)
|
||||
: "i" (T_SYSCALL),
|
||||
"a" (num),
|
||||
"d" (a[0]),
|
||||
"c" (a[1]),
|
||||
"b" (a[2]),
|
||||
"D" (a[3]),
|
||||
"S" (a[4])
|
||||
: "cc", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
sys_exit(int error_code) {
|
||||
return syscall(SYS_exit, error_code);
|
||||
}
|
||||
|
||||
int
|
||||
sys_fork(void) {
|
||||
return syscall(SYS_fork);
|
||||
}
|
||||
|
||||
int
|
||||
sys_wait(int pid, int *store) {
|
||||
return syscall(SYS_wait, pid, store);
|
||||
}
|
||||
|
||||
int
|
||||
sys_yield(void) {
|
||||
return syscall(SYS_yield);
|
||||
}
|
||||
|
||||
int
|
||||
sys_kill(int pid) {
|
||||
return syscall(SYS_kill, pid);
|
||||
}
|
||||
|
||||
int
|
||||
sys_getpid(void) {
|
||||
return syscall(SYS_getpid);
|
||||
}
|
||||
|
||||
int
|
||||
sys_putc(int c) {
|
||||
return syscall(SYS_putc, c);
|
||||
}
|
||||
|
||||
int
|
||||
sys_pgdir(void) {
|
||||
return syscall(SYS_pgdir);
|
||||
}
|
||||
|
14
related_info/lab5/lab5-spoc-discuss/user/libs/syscall.h
Normal file
14
related_info/lab5/lab5-spoc-discuss/user/libs/syscall.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef __USER_LIBS_SYSCALL_H__
|
||||
#define __USER_LIBS_SYSCALL_H__
|
||||
|
||||
int sys_exit(int error_code);
|
||||
int sys_fork(void);
|
||||
int sys_wait(int pid, int *store);
|
||||
int sys_yield(void);
|
||||
int sys_kill(int pid);
|
||||
int sys_getpid(void);
|
||||
int sys_putc(int c);
|
||||
int sys_pgdir(void);
|
||||
|
||||
#endif /* !__USER_LIBS_SYSCALL_H__ */
|
||||
|
48
related_info/lab5/lab5-spoc-discuss/user/libs/ulib.c
Normal file
48
related_info/lab5/lab5-spoc-discuss/user/libs/ulib.c
Normal file
@ -0,0 +1,48 @@
|
||||
#include <defs.h>
|
||||
#include <syscall.h>
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
void
|
||||
exit(int error_code) {
|
||||
sys_exit(error_code);
|
||||
cprintf("BUG: exit failed.\n");
|
||||
while (1);
|
||||
}
|
||||
|
||||
int
|
||||
fork(void) {
|
||||
return sys_fork();
|
||||
}
|
||||
|
||||
int
|
||||
wait(void) {
|
||||
return sys_wait(0, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
waitpid(int pid, int *store) {
|
||||
return sys_wait(pid, store);
|
||||
}
|
||||
|
||||
void
|
||||
yield(void) {
|
||||
sys_yield();
|
||||
}
|
||||
|
||||
int
|
||||
kill(int pid) {
|
||||
return sys_kill(pid);
|
||||
}
|
||||
|
||||
int
|
||||
getpid(void) {
|
||||
return sys_getpid();
|
||||
}
|
||||
|
||||
//print_pgdir - print the PDT&PT
|
||||
void
|
||||
print_pgdir(void) {
|
||||
sys_pgdir();
|
||||
}
|
||||
|
36
related_info/lab5/lab5-spoc-discuss/user/libs/ulib.h
Normal file
36
related_info/lab5/lab5-spoc-discuss/user/libs/ulib.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef __USER_LIBS_ULIB_H__
|
||||
#define __USER_LIBS_ULIB_H__
|
||||
|
||||
#include <defs.h>
|
||||
|
||||
void __warn(const char *file, int line, const char *fmt, ...);
|
||||
void __noreturn __panic(const char *file, int line, const char *fmt, ...);
|
||||
|
||||
#define warn(...) \
|
||||
__warn(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define panic(...) \
|
||||
__panic(__FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define assert(x) \
|
||||
do { \
|
||||
if (!(x)) { \
|
||||
panic("assertion failed: %s", #x); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
// static_assert(x) will generate a compile-time error if 'x' is false.
|
||||
#define static_assert(x) \
|
||||
switch (x) { case 0: case (x): ; }
|
||||
|
||||
void __noreturn exit(int error_code);
|
||||
int fork(void);
|
||||
int wait(void);
|
||||
int waitpid(int pid, int *store);
|
||||
void yield(void);
|
||||
int kill(int pid);
|
||||
int getpid(void);
|
||||
void print_pgdir(void);
|
||||
|
||||
#endif /* !__USER_LIBS_ULIB_H__ */
|
||||
|
10
related_info/lab5/lab5-spoc-discuss/user/libs/umain.c
Normal file
10
related_info/lab5/lab5-spoc-discuss/user/libs/umain.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <ulib.h>
|
||||
|
||||
int main(void);
|
||||
|
||||
void
|
||||
umain(void) {
|
||||
int ret = main();
|
||||
exit(ret);
|
||||
}
|
||||
|
11
related_info/lab5/lab5-spoc-discuss/user/pgdir.c
Normal file
11
related_info/lab5/lab5-spoc-discuss/user/pgdir.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
int
|
||||
main(void) {
|
||||
cprintf("I am %d, print pgdir.\n", getpid());
|
||||
print_pgdir();
|
||||
cprintf("pgdir pass.\n");
|
||||
return 0;
|
||||
}
|
||||
|
9
related_info/lab5/lab5-spoc-discuss/user/softint.c
Normal file
9
related_info/lab5/lab5-spoc-discuss/user/softint.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <ulib.h>
|
||||
|
||||
int
|
||||
main(void) {
|
||||
asm volatile("int $14");
|
||||
panic("FAIL: T.T\n");
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user