Browse Source

add lab4 spoc discuss

main
yuchen 9 years ago
parent
commit
258bcb059e
65 changed files with 8589 additions and 0 deletions
  1. +292
    -0
      related_info/lab4/lab4-spoc-discuss/Makefile
  2. +26
    -0
      related_info/lab4/lab4-spoc-discuss/boot/asm.h
  3. +107
    -0
      related_info/lab4/lab4-spoc-discuss/boot/bootasm.S
  4. +116
    -0
      related_info/lab4/lab4-spoc-discuss/boot/bootmain.c
  5. +27
    -0
      related_info/lab4/lab4-spoc-discuss/kern/debug/assert.h
  6. +326
    -0
      related_info/lab4/lab4-spoc-discuss/kern/debug/kdebug.c
  7. +12
    -0
      related_info/lab4/lab4-spoc-discuss/kern/debug/kdebug.h
  8. +132
    -0
      related_info/lab4/lab4-spoc-discuss/kern/debug/kmonitor.c
  9. +19
    -0
      related_info/lab4/lab4-spoc-discuss/kern/debug/kmonitor.h
  10. +49
    -0
      related_info/lab4/lab4-spoc-discuss/kern/debug/panic.c
  11. +54
    -0
      related_info/lab4/lab4-spoc-discuss/kern/debug/stab.h
  12. +45
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/clock.c
  13. +11
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/clock.h
  14. +465
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/console.c
  15. +11
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/console.h
  16. +15
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/intr.c
  17. +8
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/intr.h
  18. +84
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/kbdreg.h
  19. +86
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/picirq.c
  20. +10
    -0
      related_info/lab4/lab4-spoc-discuss/kern/driver/picirq.h
  21. +49
    -0
      related_info/lab4/lab4-spoc-discuss/kern/init/entry.S
  22. +39
    -0
      related_info/lab4/lab4-spoc-discuss/kern/init/init.c
  23. +50
    -0
      related_info/lab4/lab4-spoc-discuss/kern/libs/readline.c
  24. +78
    -0
      related_info/lab4/lab4-spoc-discuss/kern/libs/stdio.c
  25. +268
    -0
      related_info/lab4/lab4-spoc-discuss/kern/mm/default_pmm.c
  26. +9
    -0
      related_info/lab4/lab4-spoc-discuss/kern/mm/default_pmm.h
  27. +305
    -0
      related_info/lab4/lab4-spoc-discuss/kern/mm/kmalloc.c
  28. +14
    -0
      related_info/lab4/lab4-spoc-discuss/kern/mm/kmalloc.h
  29. +134
    -0
      related_info/lab4/lab4-spoc-discuss/kern/mm/memlayout.h
  30. +272
    -0
      related_info/lab4/lab4-spoc-discuss/kern/mm/mmu.h
  31. +665
    -0
      related_info/lab4/lab4-spoc-discuss/kern/mm/pmm.c
  32. +148
    -0
      related_info/lab4/lab4-spoc-discuss/kern/mm/pmm.h
  33. +10
    -0
      related_info/lab4/lab4-spoc-discuss/kern/process/entry.S
  34. +434
    -0
      related_info/lab4/lab4-spoc-discuss/kern/process/proc.c
  35. +77
    -0
      related_info/lab4/lab4-spoc-discuss/kern/process/proc.h
  36. +30
    -0
      related_info/lab4/lab4-spoc-discuss/kern/process/switch.S
  37. +41
    -0
      related_info/lab4/lab4-spoc-discuss/kern/schedule/sched.c
  38. +10
    -0
      related_info/lab4/lab4-spoc-discuss/kern/schedule/sched.h
  39. +28
    -0
      related_info/lab4/lab4-spoc-discuss/kern/sync/sync.h
  40. +240
    -0
      related_info/lab4/lab4-spoc-discuss/kern/trap/trap.c
  41. +91
    -0
      related_info/lab4/lab4-spoc-discuss/kern/trap/trap.h
  42. +49
    -0
      related_info/lab4/lab4-spoc-discuss/kern/trap/trapentry.S
  43. +1536
    -0
      related_info/lab4/lab4-spoc-discuss/kern/trap/vectors.S
  44. +26
    -0
      related_info/lab4/lab4-spoc-discuss/lab4-spoc-discuss.md
  45. +57
    -0
      related_info/lab4/lab4-spoc-discuss/libs/atomic.h
  46. +68
    -0
      related_info/lab4/lab4-spoc-discuss/libs/defs.h
  47. +40
    -0
      related_info/lab4/lab4-spoc-discuss/libs/elf.h
  48. +16
    -0
      related_info/lab4/lab4-spoc-discuss/libs/error.h
  49. +18
    -0
      related_info/lab4/lab4-spoc-discuss/libs/hash.c
  50. +163
    -0
      related_info/lab4/lab4-spoc-discuss/libs/list.h
  51. +340
    -0
      related_info/lab4/lab4-spoc-discuss/libs/printfmt.c
  52. +26
    -0
      related_info/lab4/lab4-spoc-discuss/libs/rand.c
  53. +12
    -0
      related_info/lab4/lab4-spoc-discuss/libs/stdarg.h
  54. +24
    -0
      related_info/lab4/lab4-spoc-discuss/libs/stdio.h
  55. +17
    -0
      related_info/lab4/lab4-spoc-discuss/libs/stdlib.h
  56. +367
    -0
      related_info/lab4/lab4-spoc-discuss/libs/string.c
  57. +25
    -0
      related_info/lab4/lab4-spoc-discuss/libs/string.h
  58. +302
    -0
      related_info/lab4/lab4-spoc-discuss/libs/x86.h
  59. +15
    -0
      related_info/lab4/lab4-spoc-discuss/tools/boot.ld
  60. +95
    -0
      related_info/lab4/lab4-spoc-discuss/tools/function.mk
  61. +3
    -0
      related_info/lab4/lab4-spoc-discuss/tools/gdbinit
  62. +373
    -0
      related_info/lab4/lab4-spoc-discuss/tools/grade.sh
  63. +58
    -0
      related_info/lab4/lab4-spoc-discuss/tools/kernel.ld
  64. +43
    -0
      related_info/lab4/lab4-spoc-discuss/tools/sign.c
  65. +29
    -0
      related_info/lab4/lab4-spoc-discuss/tools/vector.c

+ 292
- 0
related_info/lab4/lab4-spoc-discuss/Makefile View File

@ -0,0 +1,292 @@
PROJ := 8
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
SLASH := /
V := @
#need llvm/cang-3.5+
#USELLVM := 1
# 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-elf-qemu > /dev/null; \
then echo 'i386-elf-qemu'; exit; \
elif which qemu > /dev/null; \
then echo '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
ifndef USELLVM
HOSTCC := gcc
HOSTCFLAGS := -g -Wall -O2
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)
else
HOSTCC := clang
HOSTCFLAGS := -g -Wall -O2
CC := clang
CFLAGS := -fno-builtin -Wall -g -m32 -mno-sse -nostdinc $(DEFS)
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
endif
GDB := $(GCCPREFIX)gdb
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))
# 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)
# 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,)
# -------------------------------------------------------------------
# kernel
KINCLUDE += kern/debug/ \
kern/driver/ \
kern/trap/ \
kern/mm/ \
kern/libs/ \
kern/sync/ \
kern/fs/ \
kern/process \
kern/schedule
KSRCDIR += kern/init \
kern/libs \
kern/debug \
kern/driver \
kern/trap \
kern/mm \
kern/sync \
kern/fs \
kern/process \
kern/schedule
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)
@echo + ld $@
$(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS)
@$(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-.+ \
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) -no-reboot -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -no-reboot -parallel stdio $(QEMUOPTS) -serial null
qemu-nox: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -no-reboot -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"
.PHONY: grade touch
GRADE_GDB_IN := .gdb.in
GRADE_QEMU_OUT := .qemu.out
HANDIN := proj$(PROJ)-handin.tar.gz
TOUCH_FILES := kern/trap/trap.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 tags
clean:
$(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT) cscope* tags
-$(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)'`
tags:
@echo TAGS ALL
$(V)rm -f cscope.files cscope.in.out cscope.out cscope.po.out tags
$(V)find . -type f -name "*.[chS]" >cscope.files
$(V)cscope -bq
$(V)ctags -L cscope.files

+ 26
- 0
related_info/lab4/lab4-spoc-discuss/boot/asm.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/boot/bootasm.S View 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(8042 input buffer empty).
testb $0x2, %al
jnz seta20.1
movb $0xd1, %al # 0xd1 -> port 0x64
outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port
seta20.2:
inb $0x64, %al # Wait for not busy(8042 input buffer empty).
testb $0x2, %al
jnz seta20.2
movb $0xdf, %al # 0xdf -> port 0x60
outb %al, $0x60 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1
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
- 0
related_info/lab4/lab4-spoc-discuss/boot/bootmain.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/debug/assert.h View 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__ */

+ 326
- 0
related_info/lab4/lab4-spoc-discuss/kern/debug/kdebug.c View File

@ -0,0 +1,326 @@
#include <defs.h>
#include <x86.h>
#include <stab.h>
#include <stdio.h>
#include <string.h>
#include <sync.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
};
/* *
* 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;
stabs = __STAB_BEGIN__;
stab_end = __STAB_END__;
stabstr = __STABSTR_BEGIN__;
stabstr_end = __STABSTR_END__;
// 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 2012011346 : 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, eip;
int i, j;
ebp = read_ebp();
eip = read_eip();
for (i = 0; i < STACKFRAME_DEPTH; i++) {
uint32_t *start = (uint32_t *)ebp + 2;
cprintf("ebp:0x%08x eip:0x%08x args:", ebp, eip);
for (j = 0; j < 4; j++) {
cprintf("0x%08x", *(start + j));
if (j != 3)
cprintf(" ");
}
cprintf("\n");
print_debuginfo(eip - 1);
ebp = *((uint32_t *)ebp);
eip = *((uint32_t *)ebp + 1);
}
}

+ 12
- 0
related_info/lab4/lab4-spoc-discuss/kern/debug/kdebug.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/debug/kmonitor.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/debug/kmonitor.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/debug/panic.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/debug/stab.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/clock.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/clock.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/console.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/console.h View 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__ */

+ 15
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/intr.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/intr.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/kbdreg.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/picirq.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/driver/picirq.h View 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__ */

+ 49
- 0
related_info/lab4/lab4-spoc-discuss/kern/init/entry.S View 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)

+ 39
- 0
related_info/lab4/lab4-spoc-discuss/kern/init/init.c View File

@ -0,0 +1,39 @@
#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 <proc.h>
#include <kmonitor.h>
int kern_init(void) __attribute__((noreturn));
void grade_backtrace(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();
pmm_init(); // init physical memory management
pic_init(); // init interrupt controller
idt_init(); // init interrupt descriptor table
proc_init(); // init process table
clock_init(); // init clock interrupt
intr_enable(); // enable irq interrupt
schedule(); //let init proc run
while (do_wait(1, NULL) == 0) {
schedule();
}
}

+ 50
- 0
related_info/lab4/lab4-spoc-discuss/kern/libs/readline.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/libs/stdio.c View 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;
}

+ 268
- 0
related_info/lab4/lab4-spoc-discuss/kern/mm/default_pmm.c View File

@ -0,0 +1,268 @@
#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: 2012011346
// 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
dump_list() {
// check order
list_entry_t *le = &free_list;
cprintf("Start list dump:\n");
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
cprintf("Page %x property %d\n", p, p->property);
}
}
static void
check_order() {
// check order
list_entry_t *le = &free_list;
struct Page *before = NULL;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
if (before != NULL)
if (before + before->property > p) {
dump_list();
panic("Warning: disordered %x+%d=%x > %x\n",
before, before->property,
before + before->property, p);
return ;
}
before = p;
}
}
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 = p->property = 0;
set_page_ref(p, 0);
}
base->property = n;
SetPageProperty(base);
nr_free += n;
list_add_after(&free_list, &(base->page_link));
check_order();
cprintf("default_init_memmap: nr free page is %d\n",nr_free);
}
static struct Page *
default_alloc_pages(size_t n) {
assert(n > 0);
if (n > nr_free) {
return NULL;
}
struct Page *page = NULL;
list_entry_t *le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
if (p->property >= n) {
page = p;
break;
}
}
if (page != NULL) {
list_del(&(page->page_link));
if (page->property > n) {
struct Page *p = page + n;
p->property = page->property - n;
SetPageProperty(p);
list_add_after(page->page_link.prev, &(p->page_link));
}
nr_free -= n;
ClearPageProperty(page);
}
check_order();
return page;
}
static void
default_free_pages(struct Page *base, size_t n) {
assert(n > 0);
struct Page *p = base;
for (; p != base + n; p ++) {
assert(!PageReserved(p) && !PageProperty(p));
p->flags = 0;
set_page_ref(p, 0);
}
base->property = n;
SetPageProperty(base);
list_entry_t *le = list_next(&free_list);
while (le != &free_list) {
p = le2page(le, page_link);
le = list_next(le);
if (base + base->property == p) {
base->property += p->property;
ClearPageProperty(p);
list_del(&(p->page_link));
}
else if (p + p->property == base) {
p->property += base->property;
ClearPageProperty(base);
base = p;
list_del(&(p->page_link));
}
}
le = &free_list;
if (list_empty(&free_list))
list_add(&free_list, &(base->page_link));
else if (base < le2page(list_next(le), page_link))
list_add_after(&free_list, &(base->page_link));
else if (base > le2page(list_prev(le), page_link))
list_add_before(&free_list, &(base->page_link));
else {
bool no_add = 0;
while ((le = list_next(le)) != &free_list) {
if (le2page(le, page_link) > base) {
list_add_before(le, &(base->page_link));
no_add = 1;
break;
}
}
if (!no_add)
panic("Failed to add %x %d\n", base, base->property);
}
nr_free += n;
check_order();
}
static size_t
default_nr_free_pages(void) {
return nr_free;
}
static void
basic_check(void) {
struct Page *p0, *p1, *p2;
cprintf("default_pmm basic_check\n");
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) {
}
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,
};

+ 9
- 0
related_info/lab4/lab4-spoc-discuss/kern/mm/default_pmm.h View File

@ -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;
#endif /* ! __KERN_MM_DEFAULT_PMM_H__ */

+ 305
- 0
related_info/lab4/lab4-spoc-discuss/kern/mm/kmalloc.c View File

@ -0,0 +1,305 @@
#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
slob_init(void) {
cprintf("use SLOB allocator\n");
}
inline void
kmalloc_init(void) {
slob_init();
cprintf("kmalloc_init() succeeded!\n");
}
size_t
slob_allocated(void) {
return 0;
}
size_t
kallocated(void) {
return slob_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;
}

+ 14
- 0
related_info/lab4/lab4-spoc-discuss/kern/mm/kmalloc.h View File

@ -0,0 +1,14 @@
#ifndef __KERN_MM_KMALLOC_H__
#define __KERN_MM_KMALLOC_H__
#include <defs.h>
#define KMALLOC_MAX_ORDER 10
void kmalloc_init(void);
void *kmalloc(size_t n);
void kfree(void *objp);
#endif /* !__KERN_MM_KMALLOC_H__ */

+ 134
- 0
related_info/lab4/lab4-spoc-discuss/kern/mm/memlayout.h View File

@ -0,0 +1,134 @@
#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
* | |
* | |
* | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* (*) 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
#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; // the num of free block, used in first fit pm manager
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 // if this bit=1: the Page is reserved for kernel, cannot be used in alloc/free_pages; otherwise, this bit=0
#define PG_property 1 // if this bit=1: the Page is the head page of a free memory block(contains some continuous_addrress pages), and can be used in alloc_pages; if this bit=0: if the Page is the the head page of a free memory block, then this Page and the memory block is alloced. Or this Page isn't the head page.
#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;
#endif /* !__ASSEMBLER__ */
#endif /* !__KERN_MM_MEMLAYOUT_H__ */

+ 272
- 0
related_info/lab4/lab4-spoc-discuss/kern/mm/mmu.h View 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__ */

+ 665
- 0
related_info/lab4/lab4-spoc-discuss/kern/mm/pmm.c View File

@ -0,0 +1,665 @@
#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;
local_intr_save(intr_flag);
page = pmm_manager->alloc_pages(n);
local_intr_restore(intr_flag);
if (page == NULL )
panic("alloc_pages: NO FREE PAGES!!!\n");
//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: 2012011346
*
* 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
*/
// (1) find page directory entry
pde_t *pdep = pgdir + PDX(la);
pte_t *ret = NULL;
// (2) check if entry is not present
if (!(*pdep & PTE_P)) {
// (3) check if creating is needed, then alloc page for page table
if (!create)
return NULL;
// CAUTION: this page is used for page table, not for common data page
struct Page *page = alloc_page();
// (4) set page reference
set_page_ref(page, 1);
// (5) get linear address of page
uintptr_t pa = page2pa(page); //physical
// (6) clear page content using memset
memset((void*)KADDR(pa), 0, PGSIZE);
// (7) set page directory entry's permission
assert(!(pa & 0xFFF));
*pdep = pa | PTE_U | PTE_W | PTE_P;
}
ret = KADDR((pte_t *)(*pdep & ~0xFFF) + PTX(la));
return ret; // (8) return page table entry
}
//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: 2012011346
*
* 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
*/
//(1) check if this page table entry is present
if (*ptep & PTE_P) {
//(2) find corresponding page to pte
struct Page *page = pte2page(*ptep);
//(3) decrease page reference
assert(page->ref > 0);
if (!page_ref_dec(page)) {
//(4) and free this page when page reference reachs 0
free_page(page);
}
//(5) clear second page table entry
*ptep = 0;
//(6) flush tlb
}
tlb_invalidate(pgdir, la);
}
//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) ==-E_NO_MEM) {
free_page(page);
panic("pgdir_alloc_page:NO FREE PAGES1!!");
}
}
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");
}

+ 148
- 0
related_info/lab4/lab4-spoc-discuss/kern/mm/pmm.h View File

@ -0,0 +1,148 @@
#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>
/* fork flags used in do_fork*/
#define CLONE_VM 0x00000100 // set if VM shared between processes
#define CLONE_THREAD 0x00000200 // thread group
// 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 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__ */

+ 10
- 0
related_info/lab4/lab4-spoc-discuss/kern/process/entry.S View 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

+ 434
- 0
related_info/lab4/lab4-spoc-discuss/kern/process/proc.c View File

@ -0,0 +1,434 @@
#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>
/* ------------- 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;
// idle proc
struct proc_struct *idleproc = NULL;
// init procs
struct proc_struct *initproc1 = NULL;
struct proc_struct *initproc2 = 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 2012011346
/*
* 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
*/
memset(proc, 0, sizeof(struct proc_struct));
proc->state = PROC_UNINIT;
proc->pid = -1;
proc->cr3 = boot_cr3;
}
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);
}
// 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);
}
// 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 = &proc_list, *le = list;
while ((le = list_next(le)) != list) {
struct proc_struct *proc = le2proc(le, list_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);
}
// 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 2012011346
/*
* 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_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
proc = alloc_proc();
proc->pid = get_pid();
// 2. call setup_kstack to allocate a kernel stack for child process
setup_kstack(proc);
// 3. call copy_thread to setup tf & context in proc_struct
copy_thread(proc, stack, tf);
// 4. insert proc_struct into proc_list
list_add_before(&proc_list, &proc->list_link);
// 5. call wakup_proc to make the new child process RUNNABLE
wakeup_proc(proc);
// 7. set ret vaule using child proc's pid
nr_process++;
ret = proc->pid;
// 8. set parent
proc->parent=current;
fork_out:
return ret;
bad_fork_cleanup_kstack:
put_kstack(proc);
bad_fork_cleanup_proc:
kfree(proc);
goto fork_out;
}
// remove_links - clean the relation links of process
static void
remove_links(struct proc_struct *proc) {
list_del(&(proc->list_link));
nr_process --;
}
// 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 proc_struct *proc;
bool intr_flag, haskid;
repeat:
cprintf("do_wait: begin\n");
haskid = 0;
list_entry_t *list = &proc_list, *le = list;
while ((le = list_next(le)) != list) {
proc = le2proc(le, list_link);
if (proc != NULL) {
haskid = 1;
if (proc->state == PROC_ZOMBIE) {
goto found;
}
}
}
if (haskid) {
cprintf("do_wait: has kid begin\n");
current->state = PROC_SLEEPING;
current->wait_state = WT_CHILD;
schedule();
goto repeat;
}
return -E_BAD_PROC;
found:
cprintf("do_wait: has kid find child pid%d\n",proc->pid);
if (proc == idleproc ) {
panic("wait idleproc \n");
}
local_intr_save(intr_flag);
{
remove_links(proc);
}
local_intr_restore(intr_flag);
put_kstack(proc);
kfree(proc);
return 0;
}
// do_exit - called by sys_exit
// 1. set process' state as PROC_ZOMBIE, then call wakeup_proc(parent) to ask parent reclaim itself.
// 2. call scheduler to switch to other process
int
do_exit(int error_code) {
if (current == idleproc) {
panic("idleproc exit.\n");
}
cprintf(" do_exit: proc pid %d will exit\n", current->pid);
cprintf(" do_exit: proc parent %x\n", current->parent);
current->state = PROC_ZOMBIE;
bool intr_flag;
struct proc_struct *proc;
local_intr_save(intr_flag);
{
proc = current->parent;
if (proc->wait_state == WT_CHILD) {
wakeup_proc(proc);
}
}
local_intr_restore(intr_flag);
schedule();
panic("do_exit will not return!! %d.\n", current->pid);
}
// init_main - the second kernel thread used to create user_main kernel threads
static int
init_main(void *arg) {
cprintf(" kernel_thread, pid = %d, name = %s\n", current->pid, get_proc_name(current));
schedule();
cprintf(" kernel_thread, pid = %d, name = %s , arg %s \n", current->pid, get_proc_name(current), (const char *)arg);
schedule();
cprintf(" kernel_thread, pid = %d, name = %s , en.., Bye, Bye. :)\n",current->pid, get_proc_name(current));
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);
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 pid1= kernel_thread(init_main, "init main1: Hello world!!", 0);
int pid2= kernel_thread(init_main, "init main2: Hello world!!", 0);
if (pid1 <= 0 || pid2<=0) {
panic("create kernel thread init_main1 or 2 failed.\n");
}
initproc1 = find_proc(pid1);
initproc2 = find_proc(pid2);
set_proc_name(initproc1, "init1");
set_proc_name(initproc2, "init2");
cprintf("proc_init:: Created kernel thread init_main--> pid: %d, name: %s\n",initproc1->pid, initproc1->name);
cprintf("proc_init:: Created kernel thread init_main--> pid: %d, name: %s\n",initproc2->pid, initproc2->name);
assert(idleproc != NULL && idleproc->pid == 0);
}
// 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();
}
}
}

+ 77
- 0
related_info/lab4/lab4-spoc-discuss/kern/process/proc.h View File

@ -0,0 +1,77 @@
#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 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
uint32_t wait_state; // waiting state
char name[PROC_NAME_LEN + 1]; // Process name
list_entry_t list_link; // Process link list
};
#define WT_CHILD (0x00000001)
#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);
#endif /* !__KERN_PROCESS_PROC_H__ */

+ 30
- 0
related_info/lab4/lab4-spoc-discuss/kern/process/switch.S View 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

+ 41
- 0
related_info/lab4/lab4-spoc-discuss/kern/schedule/sched.c View File

@ -0,0 +1,41 @@
#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 && proc->state != PROC_RUNNABLE);
proc->state = PROC_RUNNABLE;
}
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
- 0
related_info/lab4/lab4-spoc-discuss/kern/schedule/sched.h View 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__ */

+ 28
- 0
related_info/lab4/lab4-spoc-discuss/kern/sync/sync.h View File

@ -0,0 +1,28 @@
#ifndef __KERN_SYNC_SYNC_H__
#define __KERN_SYNC_SYNC_H__
#include <x86.h>
#include <intr.h>
#include <mmu.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);
#endif /* !__KERN_SYNC_SYNC_H__ */

+ 240
- 0
related_info/lab4/lab4-spoc-discuss/kern/trap/trap.c View File

@ -0,0 +1,240 @@
#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>
#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 2012011346 : 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!
*/
extern uintptr_t __vectors[];
int i;
for (i = 0; i < 256; i++) {
if (i != T_SYSCALL) {
SETGATE(idt[i], 0, 8, __vectors[i], 0);
} else {
SETGATE(idt[i], 1, 8, __vectors[i], 3);
}
}
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);
}
/* trap_dispatch - dispatch based on what type of trap occurred */
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;
// print_pgfault(tf);
// if (check_mm_struct != NULL) {
// return do_pgfault(check_mm_struct, tf->tf_err, rcr2());
// }
// panic("unhandled page fault.\n");
//}
static volatile int in_swap_tick_event = 0;
extern struct mm_struct *check_mm_struct;
static void
trap_dispatch(struct trapframe *tf) {
char c;
static int count = 0;
int ret;
switch (tf->tf_trapno) {
case T_PGFLT: //page fault
panic("pgfault execption!!!\n");
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 2012011346 : 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!
*/
count++;
if (count % 100 == 0) {
print_ticks();
count = 0;
}
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 : 2012011346 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:
// in kernel, it must be a mistake
if ((tf->tf_cs & 3) == 0) {
print_trapframe(tf);
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
trap_dispatch(tf);
}

+ 91
- 0
related_info/lab4/lab4-spoc-discuss/kern/trap/trap.h View File

@ -0,0 +1,91 @@
#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
#define T_SYSCALL 0x80 // SYSCALL, ONLY FOR THIS PROJ
/* 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/trap/trapentry.S View 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
- 0
related_info/lab4/lab4-spoc-discuss/kern/trap/vectors.S
File diff suppressed because it is too large
View File


+ 26
- 0
related_info/lab4/lab4-spoc-discuss/lab4-spoc-discuss.md View File

@ -0,0 +1,26 @@
# lab4 spoc discuss
## 掌握知识点
1. 内核线程的启动、运行、就绪、等待、退出
2. 内核线程的管理与简单调度
3. 内核线程的切换过程
请完成如下练习,完成代码填写,并形成spoc练习报告
## 1. 分析并描述创建分配进程的过程
> 注意 state、pid、cr3,context,trapframe的含义
## 练习2:分析并描述新创建的内核线程是如何分配资源的
###设计实现
> 注意 理解对kstack, trapframe, context等的初始化
当前进程中唯一,操作系统的整个生命周期不唯一,在get_pid中会循环使用pid,耗尽会等待
## 练习3:阅读代码,在现有基础上再增加一个内核线程,并通过增加cprintf函数到ucore代码中
能够把进程的生命周期和调度动态执行过程完整地展现出来
## 扩展练习4:增加可以睡眠的内核线程,睡眠的条件和唤醒的条件可自行设计,并给出测试用例,并在spoc练习报告中给出设计实现说明

+ 57
- 0
related_info/lab4/lab4-spoc-discuss/libs/atomic.h View File

@ -0,0 +1,57 @@
#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;
}
#endif /* !__LIBS_ATOMIC_H__ */

+ 68
- 0
related_info/lab4/lab4-spoc-discuss/libs/defs.h View 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__ */

+ 40
- 0
related_info/lab4/lab4-spoc-discuss/libs/elf.h View File

@ -0,0 +1,40 @@
#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
};
#endif /* !__LIBS_ELF_H__ */

+ 16
- 0
related_info/lab4/lab4-spoc-discuss/libs/error.h View File

@ -0,0 +1,16 @@
#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
/* the maximum allowed */
#define MAXERROR 6
#endif /* !__LIBS_ERROR_H__ */

+ 18
- 0
related_info/lab4/lab4-spoc-discuss/libs/hash.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/libs/list.h View 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__ */

+ 340
- 0
related_info/lab4/lab4-spoc-discuss/libs/printfmt.c View File

@ -0,0 +1,340 @@
#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",
};
/* *
* 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
- 0
related_info/lab4/lab4-spoc-discuss/libs/rand.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/libs/stdarg.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/libs/stdio.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/libs/stdlib.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/libs/string.c View 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
- 0
related_info/lab4/lab4-spoc-discuss/libs/string.h View 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__ */

+ 302
- 0
related_info/lab4/lab4-spoc-discuss/libs/x86.h View 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
- 0
related_info/lab4/lab4-spoc-discuss/tools/boot.ld View 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
- 0
related_info/lab4/lab4-spoc-discuss/tools/function.mk View 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
- 0
related_info/lab4/lab4-spoc-discuss/tools/gdbinit View File

@ -0,0 +1,3 @@
file bin/kernel
target remote :1234
break kern_init

+ 373
- 0
related_info/lab4/lab4-spoc-discuss/tools/grade.sh View File

@ -0,0 +1,373 @@
#!/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 -e "$@" | 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'`
brkaddr_phys=`echo $brkaddr | sed "s/^c0/00/g"`
(
echo "target remote localhost:$gdbport"
echo "break *0x$brkaddr"
if [ "$brkaddr" != "$brkaddr_phys" ]; then
echo "break *0x$brkaddr_phys"
fi
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
cp $qemu_out .`echo $tag | tr '[:upper:]' '[:lower:]' | sed 's/ /_/g'`.log
}
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>] [-Ddef...] [-check <check>] checkargs ...
tag=
check=check_regexps
while true; do
select=
case $1 in
-tag)
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
$make $makeopts touch > /dev/null 2>&1
build_run "$tag" "$defs"
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
## check now!!
quick_run 'Check VMM'
pts=5
quick_check 'check pmm' \
'memory management: default_pmm_manager' \
'check_alloc_page() succeeded!' \
'check_pgdir() succeeded!' \
'check_boot_pgdir() succeeded!'
pts=5
quick_check 'check page table' \
'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'
pts=25
quick_check 'check vmm' \
'check_vma_struct() succeeded!' \
'page fault at 0x00000100: K/W [no page found].' \
'check_pgfault() succeeded!' \
'check_vmm() succeeded.'
pts=20
quick_check 'check swap page fault' \
'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!'
pts=5
quick_check 'check ticks' \
'++ setup timer interrupts'
pts=30
quick_check 'check initproc' \
'this initproc, pid = 1, name = "init"' \
'To U: "Hello world!!".' \
'To U: "en.., Bye, Bye. :)"'
## print final-score
show_final

+ 58
- 0
related_info/lab4/lab4-spoc-discuss/tools/kernel.ld View 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
- 0
related_info/lab4/lab4-spoc-discuss/tools/sign.c View 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;
}

+ 29
- 0
related_info/lab4/lab4-spoc-discuss/tools/vector.c View 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;
}

Loading…
Cancel
Save