add lab answers

This commit is contained in:
chyyuu
2014-08-20 15:42:20 +08:00
parent d9ec12887b
commit f9773095fe
731 changed files with 92876 additions and 0 deletions

View File

@@ -0,0 +1,369 @@
PROJ := 5
EMPTY :=
SPACE := $(EMPTY) $(EMPTY)
SLASH := /
V := @
# try to infer the correct GCCPREFX
ifndef GCCPREFIX
GCCPREFIX := $(shell if i386-ucore-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
then echo 'i386-ucore-elf-'; \
elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; \
then echo ''; \
else echo "***" 1>&2; \
echo "*** Error: Couldn't find an i386-ucore-elf version of GCC/binutils." 1>&2; \
echo "*** Is the directory with i386-ucore-elf-gcc in your PATH?" 1>&2; \
echo "*** If your i386-ucore-elf toolchain is installed with a command" 1>&2; \
echo "*** prefix other than 'i386-ucore-elf-', set your GCCPREFIX" 1>&2; \
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
echo "*** To turn off this error, run 'gmake GCCPREFIX= ...'." 1>&2; \
echo "***" 1>&2; exit 1; fi)
endif
# try to infer the correct QEMU
ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \
then echo 'qemu'; exit; \
elif which i386-ucore-elf-qemu > /dev/null; \
then echo 'i386-ucore-elf-qemu'; exit; \
else \
echo "***" 1>&2; \
echo "*** Error: Couldn't find a working QEMU executable." 1>&2; \
echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; \
echo "***" 1>&2; exit 1; fi)
endif
# eliminate default suffix rules
.SUFFIXES: .c .S .h
# delete target files if there is an error (or make is interrupted)
.DELETE_ON_ERROR:
# define compiler and flags
HOSTCC := gcc
HOSTCFLAGS := -g -Wall -O2 -D_FILE_OFFSET_BITS=64
GDB := $(GCCPREFIX)gdb
CC ?= $(GCCPREFIX)gcc
CFLAGS := -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc $(DEFS)
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
CTYPE := c S
LD := $(GCCPREFIX)ld
LDFLAGS := -m $(shell $(LD) -V | grep elf_i386 2>/dev/null)
LDFLAGS += -nostdlib
OBJCOPY := $(GCCPREFIX)objcopy
OBJDUMP := $(GCCPREFIX)objdump
COPY := cp
MKDIR := mkdir -p
MV := mv
RM := rm -f
AWK := awk
SED := sed
SH := sh
TR := tr
TOUCH := touch -c
OBJDIR := obj
BINDIR := bin
ALLOBJS :=
ALLDEPS :=
TARGETS :=
USER_PREFIX := __user_
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)
filename = $(basename $(notdir $(1)))
ubinfile = $(call outfile,$(addprefix $(USER_PREFIX),$(call filename,$(1))))
# for match pattern
match = $(shell echo $(2) | $(AWK) '{for(i=1;i<=NF;i++){if(match("$(1)","^"$$(i)"$$")){exit 1;}}}'; echo $$?)
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# include kernel/user
INCLUDE += libs/
CFLAGS += $(addprefix -I,$(INCLUDE))
LIBDIR += libs
$(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,)
# -------------------------------------------------------------------
# user programs
UINCLUDE += user/include/ \
user/libs/
USRCDIR += user
ULIBDIR += user/libs
UCFLAGS += $(addprefix -I,$(UINCLUDE))
USER_BINS :=
$(call add_files_cc,$(call listf_cc,$(ULIBDIR)),ulibs,$(UCFLAGS))
$(call add_files_cc,$(call listf_cc,$(USRCDIR)),uprog,$(UCFLAGS))
UOBJS := $(call read_packet,ulibs libs)
define uprog_ld
__user_bin__ := $$(call ubinfile,$(1))
USER_BINS += $$(__user_bin__)
$$(__user_bin__): tools/user.ld
$$(__user_bin__): $$(UOBJS)
$$(__user_bin__): $(1) | $$$$(dir $$$$@)
$(V)$(LD) $(LDFLAGS) -T tools/user.ld -o $$@ $$(UOBJS) $(1)
@$(OBJDUMP) -S $$@ > $$(call cgtype,$$<,o,asm)
@$(OBJDUMP) -t $$@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$$$/d' > $$(call cgtype,$$<,o,sym)
endef
$(foreach p,$(call read_packet,uprog),$(eval $(call uprog_ld,$(p))))
# -------------------------------------------------------------------
# kernel
KINCLUDE += kern/debug/ \
kern/driver/ \
kern/trap/ \
kern/mm/ \
kern/libs/ \
kern/sync/ \
kern/fs/ \
kern/process/ \
kern/schedule/ \
kern/syscall/ \
kern/fs/swap/ \
kern/fs/vfs/ \
kern/fs/devs/ \
kern/fs/sfs/
KSRCDIR += kern/init \
kern/libs \
kern/debug \
kern/driver \
kern/trap \
kern/mm \
kern/sync \
kern/fs \
kern/process \
kern/schedule \
kern/syscall \
kern/fs/swap \
kern/fs/vfs \
kern/fs/devs \
kern/fs/sfs
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 'mksfs' tools
$(call add_files_host,tools/mksfs.c,mksfs,mksfs)
$(call create_target_host,mksfs,mksfs)
# -------------------------------------------------------------------
# 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)
# -------------------------------------------------------------------
# create sfs.img
SFSIMG := $(call totarget,sfs.img)
SFSBINS :=
SFSROOT := disk0
define fscopy
__fs_bin__ := $(2)$(SLASH)$(patsubst $(USER_PREFIX)%,%,$(basename $(notdir $(1))))
SFSBINS += $$(__fs_bin__)
$$(__fs_bin__): $(1) | $$$$(dir $@)
@$(COPY) $$< $$@
endef
$(foreach p,$(USER_BINS),$(eval $(call fscopy,$(p),$(SFSROOT)$(SLASH))))
$(SFSIMG): $(SFSROOT) $(SFSBINS) | $(call totarget,mksfs)
$(V)dd if=/dev/zero of=$@ bs=1M count=128
@$(call totarget,mksfs) $@ $(SFSROOT)
$(call create_target,sfs.img)
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
$(call finish_all)
IGNORE_ALLDEPS = clean \
dist-clean \
grade \
touch \
print-.+ \
run-.+ \
build-.+ \
sh-.+ \
script-.+ \
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 -drive file=$(SFSIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox monitor
qemu: $(UCOREIMG) $(SWAPIMG) $(SFSIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null
qemu-nox: $(UCOREIMG) $(SWAPIMG) $(SFSIMG)
$(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic
monitor: $(UCOREIMG) $(SWAPING) $(SFSIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
TERMINAL := gnome-terminal
debug: $(UCOREIMG) $(SWAPIMG) $(SFSIMG)
$(V)$(QEMU) -S -s -parallel stdio $(QEMUOPTS) -serial null &
$(V)sleep 2
$(V)$(TERMINAL) -e "$(GDB) -q -x tools/gdbinit"
debug-nox: $(UCOREIMG) $(SWAPIMG) $(SFSIMG)
$(V)$(QEMU) -S -s -serial mon:stdio $(QEMUOPTS) -nographic &
$(V)sleep 2
$(V)$(TERMINAL) -e "$(GDB) -q -x tools/gdbinit"
RUN_PREFIX := _binary_$(OBJDIR)_$(USER_PREFIX)
MAKEOPTS := --quiet --no-print-directory
run-%: build-%
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null
sh-%: script-%
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null
build-%: touch
$(V)$(MAKE) $(MAKEOPTS) "DEFS+=-DTEST=$*"
script-%: touch
$(V)$(MAKE) $(MAKEOPTS) "DEFS+=-DTEST=sh -DTESTSCRIPT=/script/$*"
.PHONY: grade touch buildfs
GRADE_GDB_IN := .gdb.in
GRADE_QEMU_OUT := .qemu.out
HANDIN := proj$(PROJ)-handin.tar.gz
TOUCH_FILES := kern/process/proc.c
MAKEOPTS := --quiet --no-print-directory
grade:
$(V)$(MAKE) $(MAKEOPTS) clean
$(V)$(SH) tools/grade.sh
touch:
$(V)$(foreach f,$(TOUCH_FILES),$(TOUCH) $(f))
print-%:
@echo $($(shell echo $(patsubst print-%,%,$@) | $(TR) [a-z] [A-Z]))
.PHONY: clean dist-clean handin packall
clean:
$(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT) $(SFSBINS)
-$(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)'`

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__ */

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
testb $0x2, %al
jnz seta20.1
movb $0xd1, %al # 0xd1 -> port 0x64
outb %al, $0x64
seta20.2:
inb $0x64, %al # Wait for not busy
testb $0x2, %al
jnz seta20.2
movb $0xdf, %al # 0xdf -> port 0x60
outb %al, $0x60
probe_memory:
movl $0, 0x8000
xorl %ebx, %ebx
movw $0x8004, %di
start_probe:
movl $0xE820, %eax
movl $20, %ecx
movl $SMAP, %edx
int $0x15
jnc cont
movw $12345, 0x8000
jmp finish_probe
cont:
addw $20, %di
incl 0x8000
cmpl $0, %ebx
jnz start_probe
finish_probe:
# Switch from real to protected mode, using a bootstrap GDT
# and segment translation that makes virtual addresses
# identical to physical addresses, so that the
# effective memory map does not change during the switch.
lgdt gdtdesc
movl %cr0, %eax
orl $CR0_PE_ON, %eax
movl %eax, %cr0
# Jump to next instruction, but in 32-bit code segment.
# Switches processor into 32-bit mode.
ljmp $PROT_MODE_CSEG, $protcseg
.code32 # Assemble for 32-bit mode
protcseg:
# Set up the protected-mode data segment registers
movw $PROT_MODE_DSEG, %ax # Our data segment selector
movw %ax, %ds # -> DS: Data Segment
movw %ax, %es # -> ES: Extra Segment
movw %ax, %fs # -> FS
movw %ax, %gs # -> GS
movw %ax, %ss # -> SS: Stack Segment
# Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)
movl $0x0, %ebp
movl $start, %esp
call bootmain
# If bootmain returns (it shouldn't), loop.
spin:
jmp spin
.data
# Bootstrap GDT
.p2align 2 # force 4 byte alignment
gdt:
SEG_NULLASM # null seg
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel
gdtdesc:
.word 0x17 # sizeof(gdt) - 1
.long gdt # address gdt

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);
}

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__ */

View File

@@ -0,0 +1,365 @@
#include <defs.h>
#include <x86.h>
#include <stab.h>
#include <stdio.h>
#include <string.h>
#include <memlayout.h>
#include <sync.h>
#include <vmm.h>
#include <proc.h>
#include <kdebug.h>
#include <kmonitor.h>
#include <assert.h>
#define STACKFRAME_DEPTH 20
extern const struct stab __STAB_BEGIN__[]; // beginning of stabs table
extern const struct stab __STAB_END__[]; // end of stabs table
extern const char __STABSTR_BEGIN__[]; // beginning of string table
extern const char __STABSTR_END__[]; // end of string table
/* debug information about a particular instruction pointer */
struct eipdebuginfo {
const char *eip_file; // source code filename for eip
int eip_line; // source code line number for eip
const char *eip_fn_name; // name of function containing eip
int eip_fn_namelen; // length of function's name
uintptr_t eip_fn_addr; // start address of function
int eip_fn_narg; // number of function arguments
};
/* user STABS data structure */
struct userstabdata {
const struct stab *stabs;
const struct stab *stab_end;
const char *stabstr;
const char *stabstr_end;
};
/* *
* stab_binsearch - according to the input, the initial value of
* range [*@region_left, *@region_right], find a single stab entry
* that includes the address @addr and matches the type @type,
* and then save its boundary to the locations that pointed
* by @region_left and @region_right.
*
* Some stab types are arranged in increasing order by instruction address.
* For example, N_FUN stabs (stab entries with n_type == N_FUN), which
* mark functions, and N_SO stabs, which mark source files.
*
* Given an instruction address, this function finds the single stab entry
* of type @type that contains that address.
*
* The search takes place within the range [*@region_left, *@region_right].
* Thus, to search an entire set of N stabs, you might do:
*
* left = 0;
* right = N - 1; (rightmost stab)
* stab_binsearch(stabs, &left, &right, type, addr);
*
* The search modifies *region_left and *region_right to bracket the @addr.
* *@region_left points to the matching stab that contains @addr,
* and *@region_right points just before the next stab.
* If *@region_left > *region_right, then @addr is not contained in any
* matching stab.
*
* For example, given these N_SO stabs:
* Index Type Address
* 0 SO f0100000
* 13 SO f0100040
* 117 SO f0100176
* 118 SO f0100178
* 555 SO f0100652
* 556 SO f0100654
* 657 SO f0100849
* this code:
* left = 0, right = 657;
* stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184);
* will exit setting left = 118, right = 554.
* */
static void
stab_binsearch(const struct stab *stabs, int *region_left, int *region_right,
int type, uintptr_t addr) {
int l = *region_left, r = *region_right, any_matches = 0;
while (l <= r) {
int true_m = (l + r) / 2, m = true_m;
// search for earliest stab with right type
while (m >= l && stabs[m].n_type != type) {
m --;
}
if (m < l) { // no match in [l, m]
l = true_m + 1;
continue;
}
// actual binary search
any_matches = 1;
if (stabs[m].n_value < addr) {
*region_left = m;
l = true_m + 1;
} else if (stabs[m].n_value > addr) {
*region_right = m - 1;
r = m - 1;
} else {
// exact match for 'addr', but continue loop to find
// *region_right
*region_left = m;
l = m;
addr ++;
}
}
if (!any_matches) {
*region_right = *region_left - 1;
}
else {
// find rightmost region containing 'addr'
l = *region_right;
for (; l > *region_left && stabs[l].n_type != type; l --)
/* do nothing */;
*region_left = l;
}
}
/* *
* debuginfo_eip - Fill in the @info structure with information about
* the specified instruction address, @addr. Returns 0 if information
* was found, and negative if not. But even if it returns negative it
* has stored some information into '*info'.
* */
int
debuginfo_eip(uintptr_t addr, struct eipdebuginfo *info) {
const struct stab *stabs, *stab_end;
const char *stabstr, *stabstr_end;
info->eip_file = "<unknown>";
info->eip_line = 0;
info->eip_fn_name = "<unknown>";
info->eip_fn_namelen = 9;
info->eip_fn_addr = addr;
info->eip_fn_narg = 0;
// find the relevant set of stabs
if (addr >= KERNBASE) {
stabs = __STAB_BEGIN__;
stab_end = __STAB_END__;
stabstr = __STABSTR_BEGIN__;
stabstr_end = __STABSTR_END__;
}
else {
// user-program linker script, tools/user.ld puts the information about the
// program's stabs (included __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__,
// and __STABSTR_END__) in a structure located at virtual address USTAB.
const struct userstabdata *usd = (struct userstabdata *)USTAB;
// make sure that debugger (current process) can access this memory
struct mm_struct *mm;
if (current == NULL || (mm = current->mm) == NULL) {
return -1;
}
if (!user_mem_check(mm, (uintptr_t)usd, sizeof(struct userstabdata), 0)) {
return -1;
}
stabs = usd->stabs;
stab_end = usd->stab_end;
stabstr = usd->stabstr;
stabstr_end = usd->stabstr_end;
// make sure the STABS and string table memory is valid
if (!user_mem_check(mm, (uintptr_t)stabs, (uintptr_t)stab_end - (uintptr_t)stabs, 0)) {
return -1;
}
if (!user_mem_check(mm, (uintptr_t)stabstr, stabstr_end - stabstr, 0)) {
return -1;
}
}
// String table validity checks
if (stabstr_end <= stabstr || stabstr_end[-1] != 0) {
return -1;
}
// Now we find the right stabs that define the function containing
// 'eip'. First, we find the basic source file containing 'eip'.
// Then, we look in that source file for the function. Then we look
// for the line number.
// Search the entire set of stabs for the source file (type N_SO).
int lfile = 0, rfile = (stab_end - stabs) - 1;
stab_binsearch(stabs, &lfile, &rfile, N_SO, addr);
if (lfile == 0)
return -1;
// Search within that file's stabs for the function definition
// (N_FUN).
int lfun = lfile, rfun = rfile;
int lline, rline;
stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr);
if (lfun <= rfun) {
// stabs[lfun] points to the function name
// in the string table, but check bounds just in case.
if (stabs[lfun].n_strx < stabstr_end - stabstr) {
info->eip_fn_name = stabstr + stabs[lfun].n_strx;
}
info->eip_fn_addr = stabs[lfun].n_value;
addr -= info->eip_fn_addr;
// Search within the function definition for the line number.
lline = lfun;
rline = rfun;
} else {
// Couldn't find function stab! Maybe we're in an assembly
// file. Search the whole file for the line number.
info->eip_fn_addr = addr;
lline = lfile;
rline = rfile;
}
info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name;
// Search within [lline, rline] for the line number stab.
// If found, set info->eip_line to the right line number.
// If not found, return -1.
stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
if (lline <= rline) {
info->eip_line = stabs[rline].n_desc;
} else {
return -1;
}
// Search backwards from the line number for the relevant filename stab.
// We can't just use the "lfile" stab because inlined functions
// can interpolate code from a different file!
// Such included source files use the N_SOL stab type.
while (lline >= lfile
&& stabs[lline].n_type != N_SOL
&& (stabs[lline].n_type != N_SO || !stabs[lline].n_value)) {
lline --;
}
if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr) {
info->eip_file = stabstr + stabs[lline].n_strx;
}
// Set eip_fn_narg to the number of arguments taken by the function,
// or 0 if there was no containing function.
if (lfun < rfun) {
for (lline = lfun + 1;
lline < rfun && stabs[lline].n_type == N_PSYM;
lline ++) {
info->eip_fn_narg ++;
}
}
return 0;
}
/* *
* print_kerninfo - print the information about kernel, including the location
* of kernel entry, the start addresses of data and text segements, the start
* address of free memory and how many memory that kernel has used.
* */
void
print_kerninfo(void) {
extern char etext[], edata[], end[], kern_init[];
cprintf("Special kernel symbols:\n");
cprintf(" entry 0x%08x (phys)\n", kern_init);
cprintf(" etext 0x%08x (phys)\n", etext);
cprintf(" edata 0x%08x (phys)\n", edata);
cprintf(" end 0x%08x (phys)\n", end);
cprintf("Kernel executable memory footprint: %dKB\n", (end - kern_init + 1023)/1024);
}
/* *
* print_debuginfo - read and print the stat information for the address @eip,
* and info.eip_fn_addr should be the first address of the related function.
* */
void
print_debuginfo(uintptr_t eip) {
struct eipdebuginfo info;
if (debuginfo_eip(eip, &info) != 0) {
cprintf(" <unknow>: -- 0x%08x --\n", eip);
}
else {
char fnname[256];
int j;
for (j = 0; j < info.eip_fn_namelen; j ++) {
fnname[j] = info.eip_fn_name[j];
}
fnname[j] = '\0';
cprintf(" %s:%d: %s+%d\n", info.eip_file, info.eip_line,
fnname, eip - info.eip_fn_addr);
}
}
static __noinline uint32_t
read_eip(void) {
uint32_t eip;
asm volatile("movl 4(%%ebp), %0" : "=r" (eip));
return eip;
}
/* *
* print_stackframe - print a list of the saved eip values from the nested 'call'
* instructions that led to the current point of execution
*
* The x86 stack pointer, namely esp, points to the lowest location on the stack
* that is currently in use. Everything below that location in stack is free. Pushing
* a value onto the stack will invole decreasing the stack pointer and then writing
* the value to the place that stack pointer pointes to. And popping a value do the
* opposite.
*
* The ebp (base pointer) register, in contrast, is associated with the stack
* primarily by software convention. On entry to a C function, the function's
* prologue code normally saves the previous function's base pointer by pushing
* it onto the stack, and then copies the current esp value into ebp for the duration
* of the function. If all the functions in a program obey this convention,
* then at any given point during the program's execution, it is possible to trace
* back through the stack by following the chain of saved ebp pointers and determining
* exactly what nested sequence of function calls caused this particular point in the
* program to be reached. This capability can be particularly useful, for example,
* when a particular function causes an assert failure or panic because bad arguments
* were passed to it, but you aren't sure who passed the bad arguments. A stack
* backtrace lets you find the offending function.
*
* The inline function read_ebp() can tell us the value of current ebp. And the
* non-inline function read_eip() is useful, it can read the value of current eip,
* since while calling this function, read_eip() can read the caller's eip from
* stack easily.
*
* In print_debuginfo(), the function debuginfo_eip() can get enough information about
* calling-chain. Finally print_stackframe() will trace and print them for debugging.
*
* Note that, the length of ebp-chain is limited. In boot/bootasm.S, before jumping
* to the kernel entry, the value of ebp has been set to zero, that's the boundary.
* */
void
print_stackframe(void) {
/* LAB1 YOUR CODE : STEP 1 */
/* (1) call read_ebp() to get the value of ebp. the type is (uint32_t);
* (2) call read_eip() to get the value of eip. the type is (uint32_t);
* (3) from 0 .. STACKFRAME_DEPTH
* (3.1) printf value of ebp, eip
* (3.2) (uint32_t)calling arguments [0..4] = the contents in address (unit32_t)ebp +2 [0..4]
* (3.3) cprintf("\n");
* (3.4) call print_debuginfo(eip-1) to print the C calling function name and line number, etc.
* (3.5) popup a calling stackframe
* NOTICE: the calling funciton's return addr eip = ss:[ebp+4]
* the calling funciton's ebp = ss:[ebp]
*/
uint32_t ebp = read_ebp(), eip = read_eip();
int i, j;
for (i = 0; ebp != 0 && i < STACKFRAME_DEPTH; i ++) {
cprintf("ebp:0x%08x eip:0x%08x args:", ebp, eip);
uint32_t *args = (uint32_t *)ebp + 2;
for (j = 0; j < 4; j ++) {
cprintf("0x%08x ", args[j]);
}
cprintf("\n");
print_debuginfo(eip - 1);
eip = ((uint32_t *)ebp)[1];
ebp = ((uint32_t *)ebp)[0];
}
}

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__ */

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;
}

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__ */

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;
}

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__ */

View File

@@ -0,0 +1,49 @@
#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;
long SYSTEM_READ_TIMER( void ){
return 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);
}

View File

@@ -0,0 +1,14 @@
#ifndef __KERN_DRIVER_CLOCK_H__
#define __KERN_DRIVER_CLOCK_H__
#include <defs.h>
extern volatile size_t ticks;
void clock_init(void);
long SYSTEM_READ_TIMER( void );
#endif /* !__KERN_DRIVER_CLOCK_H__ */

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;
}

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__ */

View File

@@ -0,0 +1,214 @@
#include <defs.h>
#include <stdio.h>
#include <trap.h>
#include <picirq.h>
#include <fs.h>
#include <ide.h>
#include <x86.h>
#include <assert.h>
#define ISA_DATA 0x00
#define ISA_ERROR 0x01
#define ISA_PRECOMP 0x01
#define ISA_CTRL 0x02
#define ISA_SECCNT 0x02
#define ISA_SECTOR 0x03
#define ISA_CYL_LO 0x04
#define ISA_CYL_HI 0x05
#define ISA_SDH 0x06
#define ISA_COMMAND 0x07
#define ISA_STATUS 0x07
#define IDE_BSY 0x80
#define IDE_DRDY 0x40
#define IDE_DF 0x20
#define IDE_DRQ 0x08
#define IDE_ERR 0x01
#define IDE_CMD_READ 0x20
#define IDE_CMD_WRITE 0x30
#define IDE_CMD_IDENTIFY 0xEC
#define IDE_IDENT_SECTORS 20
#define IDE_IDENT_MODEL 54
#define IDE_IDENT_CAPABILITIES 98
#define IDE_IDENT_CMDSETS 164
#define IDE_IDENT_MAX_LBA 120
#define IDE_IDENT_MAX_LBA_EXT 200
#define IO_BASE0 0x1F0
#define IO_BASE1 0x170
#define IO_CTRL0 0x3F4
#define IO_CTRL1 0x374
#define MAX_IDE 4
#define MAX_NSECS 128
#define MAX_DISK_NSECS 0x10000000U
#define VALID_IDE(ideno) (((ideno) >= 0) && ((ideno) < MAX_IDE) && (ide_devices[ideno].valid))
static const struct {
unsigned short base; // I/O Base
unsigned short ctrl; // Control Base
} channels[2] = {
{IO_BASE0, IO_CTRL0},
{IO_BASE1, IO_CTRL1},
};
#define IO_BASE(ideno) (channels[(ideno) >> 1].base)
#define IO_CTRL(ideno) (channels[(ideno) >> 1].ctrl)
static struct ide_device {
unsigned char valid; // 0 or 1 (If Device Really Exists)
unsigned int sets; // Commend Sets Supported
unsigned int size; // Size in Sectors
unsigned char model[41]; // Model in String
} ide_devices[MAX_IDE];
static int
ide_wait_ready(unsigned short iobase, bool check_error) {
int r;
while ((r = inb(iobase + ISA_STATUS)) & IDE_BSY)
/* nothing */;
if (check_error && (r & (IDE_DF | IDE_ERR)) != 0) {
return -1;
}
return 0;
}
void
ide_init(void) {
static_assert((SECTSIZE % 4) == 0);
unsigned short ideno, iobase;
for (ideno = 0; ideno < MAX_IDE; ideno ++) {
/* assume that no device here */
ide_devices[ideno].valid = 0;
iobase = IO_BASE(ideno);
/* wait device ready */
ide_wait_ready(iobase, 0);
/* step1: select drive */
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4));
ide_wait_ready(iobase, 0);
/* step2: send ATA identify command */
outb(iobase + ISA_COMMAND, IDE_CMD_IDENTIFY);
ide_wait_ready(iobase, 0);
/* step3: polling */
if (inb(iobase + ISA_STATUS) == 0 || ide_wait_ready(iobase, 1) != 0) {
continue ;
}
/* device is ok */
ide_devices[ideno].valid = 1;
/* read identification space of the device */
unsigned int buffer[128];
insl(iobase + ISA_DATA, buffer, sizeof(buffer) / sizeof(unsigned int));
unsigned char *ident = (unsigned char *)buffer;
unsigned int sectors;
unsigned int cmdsets = *(unsigned int *)(ident + IDE_IDENT_CMDSETS);
/* device use 48-bits or 28-bits addressing */
if (cmdsets & (1 << 26)) {
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA_EXT);
}
else {
sectors = *(unsigned int *)(ident + IDE_IDENT_MAX_LBA);
}
ide_devices[ideno].sets = cmdsets;
ide_devices[ideno].size = sectors;
/* check if supports LBA */
assert((*(unsigned short *)(ident + IDE_IDENT_CAPABILITIES) & 0x200) != 0);
unsigned char *model = ide_devices[ideno].model, *data = ident + IDE_IDENT_MODEL;
unsigned int i, length = 40;
for (i = 0; i < length; i += 2) {
model[i] = data[i + 1], model[i + 1] = data[i];
}
do {
model[i] = '\0';
} while (i -- > 0 && model[i] == ' ');
cprintf("ide %d: %10u(sectors), '%s'.\n", ideno, ide_devices[ideno].size, ide_devices[ideno].model);
}
// enable ide interrupt
pic_enable(IRQ_IDE1);
pic_enable(IRQ_IDE2);
}
bool
ide_device_valid(unsigned short ideno) {
return VALID_IDE(ideno);
}
size_t
ide_device_size(unsigned short ideno) {
if (ide_device_valid(ideno)) {
return ide_devices[ideno].size;
}
return 0;
}
int
ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs) {
assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno);
ide_wait_ready(iobase, 0);
// generate interrupt
outb(ioctrl + ISA_CTRL, 0);
outb(iobase + ISA_SECCNT, nsecs);
outb(iobase + ISA_SECTOR, secno & 0xFF);
outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF);
outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF);
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF));
outb(iobase + ISA_COMMAND, IDE_CMD_READ);
int ret = 0;
for (; nsecs > 0; nsecs --, dst += SECTSIZE) {
if ((ret = ide_wait_ready(iobase, 1)) != 0) {
goto out;
}
insl(iobase, dst, SECTSIZE / sizeof(uint32_t));
}
out:
return ret;
}
int
ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs) {
assert(nsecs <= MAX_NSECS && VALID_IDE(ideno));
assert(secno < MAX_DISK_NSECS && secno + nsecs <= MAX_DISK_NSECS);
unsigned short iobase = IO_BASE(ideno), ioctrl = IO_CTRL(ideno);
ide_wait_ready(iobase, 0);
// generate interrupt
outb(ioctrl + ISA_CTRL, 0);
outb(iobase + ISA_SECCNT, nsecs);
outb(iobase + ISA_SECTOR, secno & 0xFF);
outb(iobase + ISA_CYL_LO, (secno >> 8) & 0xFF);
outb(iobase + ISA_CYL_HI, (secno >> 16) & 0xFF);
outb(iobase + ISA_SDH, 0xE0 | ((ideno & 1) << 4) | ((secno >> 24) & 0xF));
outb(iobase + ISA_COMMAND, IDE_CMD_WRITE);
int ret = 0;
for (; nsecs > 0; nsecs --, src += SECTSIZE) {
if ((ret = ide_wait_ready(iobase, 1)) != 0) {
goto out;
}
outsl(iobase, src, SECTSIZE / sizeof(uint32_t));
}
out:
return ret;
}

View File

@@ -0,0 +1,14 @@
#ifndef __KERN_DRIVER_IDE_H__
#define __KERN_DRIVER_IDE_H__
#include <defs.h>
void ide_init(void);
bool ide_device_valid(unsigned short ideno);
size_t ide_device_size(unsigned short ideno);
int ide_read_secs(unsigned short ideno, uint32_t secno, void *dst, size_t nsecs);
int ide_write_secs(unsigned short ideno, uint32_t secno, const void *src, size_t nsecs);
#endif /* !__KERN_DRIVER_IDE_H__ */

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();
}

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__ */

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__ */

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);
}
}

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__ */

View File

@@ -0,0 +1,167 @@
#include <defs.h>
#include <string.h>
#include <stat.h>
#include <dev.h>
#include <inode.h>
#include <unistd.h>
#include <error.h>
/*
* dev_open - Called for each open().
*/
static int
dev_open(struct inode *node, uint32_t open_flags) {
if (open_flags & (O_CREAT | O_TRUNC | O_EXCL | O_APPEND)) {
return -E_INVAL;
}
struct device *dev = vop_info(node, device);
return dop_open(dev, open_flags);
}
/*
* dev_close - Called on the last close(). Just pass through.
*/
static int
dev_close(struct inode *node) {
struct device *dev = vop_info(node, device);
return dop_close(dev);
}
/*
* dev_read -Called for read. Hand off to iobuf.
*/
static int
dev_read(struct inode *node, struct iobuf *iob) {
struct device *dev = vop_info(node, device);
return dop_io(dev, iob, 0);
}
/*
* dev_write -Called for write. Hand off to iobuf.
*/
static int
dev_write(struct inode *node, struct iobuf *iob) {
struct device *dev = vop_info(node, device);
return dop_io(dev, iob, 1);
}
/*
* dev_ioctl - Called for ioctl(). Just pass through.
*/
static int
dev_ioctl(struct inode *node, int op, void *data) {
struct device *dev = vop_info(node, device);
return dop_ioctl(dev, op, data);
}
/*
* dev_fstat - Called for stat().
* Set the type and the size (block devices only).
* The link count for a device is always 1.
*/
static int
dev_fstat(struct inode *node, struct stat *stat) {
int ret;
memset(stat, 0, sizeof(struct stat));
if ((ret = vop_gettype(node, &(stat->st_mode))) != 0) {
return ret;
}
struct device *dev = vop_info(node, device);
stat->st_nlinks = 1;
stat->st_blocks = dev->d_blocks;
stat->st_size = stat->st_blocks * dev->d_blocksize;
return 0;
}
/*
* dev_gettype - Return the type. A device is a "block device" if it has a known
* length. A device that generates data in a stream is a "character
* device".
*/
static int
dev_gettype(struct inode *node, uint32_t *type_store) {
struct device *dev = vop_info(node, device);
*type_store = (dev->d_blocks > 0) ? S_IFBLK : S_IFCHR;
return 0;
}
/*
* dev_tryseek - Attempt a seek.
* For block devices, require block alignment.
* For character devices, prohibit seeking entirely.
*/
static int
dev_tryseek(struct inode *node, off_t pos) {
struct device *dev = vop_info(node, device);
if (dev->d_blocks > 0) {
if ((pos % dev->d_blocksize) == 0) {
if (pos >= 0 && pos < dev->d_blocks * dev->d_blocksize) {
return 0;
}
}
}
return -E_INVAL;
}
/*
* dev_lookup - Name lookup.
*
* One interesting feature of device:name pathname syntax is that you
* can implement pathnames on arbitrary devices. For instance, if you
* had a graphics device that supported multiple resolutions (which we
* don't), you might arrange things so that you could open it with
* pathnames like "video:800x600/24bpp" in order to select the operating
* mode.
*
* However, we have no support for this in the base system.
*/
static int
dev_lookup(struct inode *node, char *path, struct inode **node_store) {
if (*path != '\0') {
return -E_NOENT;
}
vop_ref_inc(node);
*node_store = node;
return 0;
}
/*
* Function table for device inodes.
*/
static const struct inode_ops dev_node_ops = {
.vop_magic = VOP_MAGIC,
.vop_open = dev_open,
.vop_close = dev_close,
.vop_read = dev_read,
.vop_write = dev_write,
.vop_fstat = dev_fstat,
.vop_ioctl = dev_ioctl,
.vop_gettype = dev_gettype,
.vop_tryseek = dev_tryseek,
.vop_lookup = dev_lookup,
};
#define init_device(x) \
do { \
extern void dev_init_##x(void); \
dev_init_##x(); \
} while (0)
/* dev_init - Initialization functions for builtin vfs-level devices. */
void
dev_init(void) {
// init_device(null);
init_device(stdin);
init_device(stdout);
init_device(disk0);
}
/* dev_create_inode - Create inode for a vfs-level device. */
struct inode *
dev_create_inode(void) {
struct inode *node;
if ((node = alloc_inode(device)) != NULL) {
vop_init(node, &dev_node_ops, NULL);
}
return node;
}

View File

@@ -0,0 +1,31 @@
#ifndef __KERN_FS_DEVS_DEV_H__
#define __KERN_FS_DEVS_DEV_H__
#include <defs.h>
struct inode;
struct iobuf;
/*
* Filesystem-namespace-accessible device.
* d_io is for both reads and writes; the iobuf will indicates the direction.
*/
struct device {
size_t d_blocks;
size_t d_blocksize;
int (*d_open)(struct device *dev, uint32_t open_flags);
int (*d_close)(struct device *dev);
int (*d_io)(struct device *dev, struct iobuf *iob, bool write);
int (*d_ioctl)(struct device *dev, int op, void *data);
};
#define dop_open(dev, open_flags) ((dev)->d_open(dev, open_flags))
#define dop_close(dev) ((dev)->d_close(dev))
#define dop_io(dev, iob, write) ((dev)->d_io(dev, iob, write))
#define dop_ioctl(dev, op, data) ((dev)->d_ioctl(dev, op, data))
void dev_init(void);
struct inode *dev_create_inode(void);
#endif /* !__KERN_FS_DEVS_DEV_H__ */

View File

@@ -0,0 +1,144 @@
#include <defs.h>
#include <mmu.h>
#include <sem.h>
#include <ide.h>
#include <inode.h>
#include <kmalloc.h>
#include <dev.h>
#include <vfs.h>
#include <iobuf.h>
#include <error.h>
#include <assert.h>
#define DISK0_BLKSIZE PGSIZE
#define DISK0_BUFSIZE (4 * DISK0_BLKSIZE)
#define DISK0_BLK_NSECT (DISK0_BLKSIZE / SECTSIZE)
static char *disk0_buffer;
static semaphore_t disk0_sem;
static void
lock_disk0(void) {
down(&(disk0_sem));
}
static void
unlock_disk0(void) {
up(&(disk0_sem));
}
static int
disk0_open(struct device *dev, uint32_t open_flags) {
return 0;
}
static int
disk0_close(struct device *dev) {
return 0;
}
static void
disk0_read_blks_nolock(uint32_t blkno, uint32_t nblks) {
int ret;
uint32_t sectno = blkno * DISK0_BLK_NSECT, nsecs = nblks * DISK0_BLK_NSECT;
if ((ret = ide_read_secs(DISK0_DEV_NO, sectno, disk0_buffer, nsecs)) != 0) {
panic("disk0: read blkno = %d (sectno = %d), nblks = %d (nsecs = %d): 0x%08x.\n",
blkno, sectno, nblks, nsecs, ret);
}
}
static void
disk0_write_blks_nolock(uint32_t blkno, uint32_t nblks) {
int ret;
uint32_t sectno = blkno * DISK0_BLK_NSECT, nsecs = nblks * DISK0_BLK_NSECT;
if ((ret = ide_write_secs(DISK0_DEV_NO, sectno, disk0_buffer, nsecs)) != 0) {
panic("disk0: write blkno = %d (sectno = %d), nblks = %d (nsecs = %d): 0x%08x.\n",
blkno, sectno, nblks, nsecs, ret);
}
}
static int
disk0_io(struct device *dev, struct iobuf *iob, bool write) {
off_t offset = iob->io_offset;
size_t resid = iob->io_resid;
uint32_t blkno = offset / DISK0_BLKSIZE;
uint32_t nblks = resid / DISK0_BLKSIZE;
/* don't allow I/O that isn't block-aligned */
if ((offset % DISK0_BLKSIZE) != 0 || (resid % DISK0_BLKSIZE) != 0) {
return -E_INVAL;
}
/* don't allow I/O past the end of disk0 */
if (blkno + nblks > dev->d_blocks) {
return -E_INVAL;
}
/* read/write nothing ? */
if (nblks == 0) {
return 0;
}
lock_disk0();
while (resid != 0) {
size_t copied, alen = DISK0_BUFSIZE;
if (write) {
iobuf_move(iob, disk0_buffer, alen, 0, &copied);
assert(copied != 0 && copied <= resid && copied % DISK0_BLKSIZE == 0);
nblks = copied / DISK0_BLKSIZE;
disk0_write_blks_nolock(blkno, nblks);
}
else {
if (alen > resid) {
alen = resid;
}
nblks = alen / DISK0_BLKSIZE;
disk0_read_blks_nolock(blkno, nblks);
iobuf_move(iob, disk0_buffer, alen, 1, &copied);
assert(copied == alen && copied % DISK0_BLKSIZE == 0);
}
resid -= copied, blkno += nblks;
}
unlock_disk0();
return 0;
}
static int
disk0_ioctl(struct device *dev, int op, void *data) {
return -E_UNIMP;
}
static void
disk0_device_init(struct device *dev) {
static_assert(DISK0_BLKSIZE % SECTSIZE == 0);
if (!ide_device_valid(DISK0_DEV_NO)) {
panic("disk0 device isn't available.\n");
}
dev->d_blocks = ide_device_size(DISK0_DEV_NO) / DISK0_BLK_NSECT;
dev->d_blocksize = DISK0_BLKSIZE;
dev->d_open = disk0_open;
dev->d_close = disk0_close;
dev->d_io = disk0_io;
dev->d_ioctl = disk0_ioctl;
sem_init(&(disk0_sem), 1);
static_assert(DISK0_BUFSIZE % DISK0_BLKSIZE == 0);
if ((disk0_buffer = kmalloc(DISK0_BUFSIZE)) == NULL) {
panic("disk0 alloc buffer failed.\n");
}
}
void
dev_init_disk0(void) {
struct inode *node;
if ((node = dev_create_inode()) == NULL) {
panic("disk0: dev_create_node.\n");
}
disk0_device_init(vop_info(node, device));
int ret;
if ((ret = vfs_add_dev("disk0", node, 1)) != 0) {
panic("disk0: vfs_add_dev: %e.\n", ret);
}
}

View File

@@ -0,0 +1,126 @@
#include <defs.h>
#include <stdio.h>
#include <wait.h>
#include <sync.h>
#include <proc.h>
#include <sched.h>
#include <dev.h>
#include <vfs.h>
#include <iobuf.h>
#include <inode.h>
#include <unistd.h>
#include <error.h>
#include <assert.h>
#define STDIN_BUFSIZE 4096
static char stdin_buffer[STDIN_BUFSIZE];
static off_t p_rpos, p_wpos;
static wait_queue_t __wait_queue, *wait_queue = &__wait_queue;
void
dev_stdin_write(char c) {
bool intr_flag;
if (c != '\0') {
local_intr_save(intr_flag);
{
stdin_buffer[p_wpos % STDIN_BUFSIZE] = c;
if (p_wpos - p_rpos < STDIN_BUFSIZE) {
p_wpos ++;
}
if (!wait_queue_empty(wait_queue)) {
wakeup_queue(wait_queue, WT_KBD, 1);
}
}
local_intr_restore(intr_flag);
}
}
static int
dev_stdin_read(char *buf, size_t len) {
int ret = 0;
bool intr_flag;
local_intr_save(intr_flag);
{
for (; ret < len; ret ++, p_rpos ++) {
try_again:
if (p_rpos < p_wpos) {
*buf ++ = stdin_buffer[p_rpos % STDIN_BUFSIZE];
}
else {
wait_t __wait, *wait = &__wait;
wait_current_set(wait_queue, wait, WT_KBD);
local_intr_restore(intr_flag);
schedule();
local_intr_save(intr_flag);
wait_current_del(wait_queue, wait);
if (wait->wakeup_flags == WT_KBD) {
goto try_again;
}
break;
}
}
}
local_intr_restore(intr_flag);
return ret;
}
static int
stdin_open(struct device *dev, uint32_t open_flags) {
if (open_flags != O_RDONLY) {
return -E_INVAL;
}
return 0;
}
static int
stdin_close(struct device *dev) {
return 0;
}
static int
stdin_io(struct device *dev, struct iobuf *iob, bool write) {
if (!write) {
int ret;
if ((ret = dev_stdin_read(iob->io_base, iob->io_resid)) > 0) {
iob->io_resid -= ret;
}
return ret;
}
return -E_INVAL;
}
static int
stdin_ioctl(struct device *dev, int op, void *data) {
return -E_INVAL;
}
static void
stdin_device_init(struct device *dev) {
dev->d_blocks = 0;
dev->d_blocksize = 1;
dev->d_open = stdin_open;
dev->d_close = stdin_close;
dev->d_io = stdin_io;
dev->d_ioctl = stdin_ioctl;
p_rpos = p_wpos = 0;
wait_queue_init(wait_queue);
}
void
dev_init_stdin(void) {
struct inode *node;
if ((node = dev_create_inode()) == NULL) {
panic("stdin: dev_create_node.\n");
}
stdin_device_init(vop_info(node, device));
int ret;
if ((ret = vfs_add_dev("stdin", node, 0)) != 0) {
panic("stdin: vfs_add_dev: %e.\n", ret);
}
}

View File

@@ -0,0 +1,64 @@
#include <defs.h>
#include <stdio.h>
#include <dev.h>
#include <vfs.h>
#include <iobuf.h>
#include <inode.h>
#include <unistd.h>
#include <error.h>
#include <assert.h>
static int
stdout_open(struct device *dev, uint32_t open_flags) {
if (open_flags != O_WRONLY) {
return -E_INVAL;
}
return 0;
}
static int
stdout_close(struct device *dev) {
return 0;
}
static int
stdout_io(struct device *dev, struct iobuf *iob, bool write) {
if (write) {
char *data = iob->io_base;
for (; iob->io_resid != 0; iob->io_resid --) {
cputchar(*data ++);
}
return 0;
}
return -E_INVAL;
}
static int
stdout_ioctl(struct device *dev, int op, void *data) {
return -E_INVAL;
}
static void
stdout_device_init(struct device *dev) {
dev->d_blocks = 0;
dev->d_blocksize = 1;
dev->d_open = stdout_open;
dev->d_close = stdout_close;
dev->d_io = stdout_io;
dev->d_ioctl = stdout_ioctl;
}
void
dev_init_stdout(void) {
struct inode *node;
if ((node = dev_create_inode()) == NULL) {
panic("stdout: dev_create_node.\n");
}
stdout_device_init(vop_info(node, device));
int ret;
if ((ret = vfs_add_dev("stdout", node, 0)) != 0) {
panic("stdout: vfs_add_dev: %e.\n", ret);
}
}

View File

@@ -0,0 +1,356 @@
#include <defs.h>
#include <string.h>
#include <vfs.h>
#include <proc.h>
#include <file.h>
#include <unistd.h>
#include <iobuf.h>
#include <inode.h>
#include <stat.h>
#include <dirent.h>
#include <error.h>
#include <assert.h>
#define testfd(fd) ((fd) >= 0 && (fd) < FILES_STRUCT_NENTRY)
// get_fd_array - get current process's open files table
static struct file *
get_fd_array(void) {
struct files_struct *filesp = current->filesp;
assert(filesp != NULL && files_count(filesp) > 0);
return filesp->fd_array;
}
// fd_array_init - initialize the open files table
void
fd_array_init(struct file *fd_array) {
int fd;
struct file *file = fd_array;
for (fd = 0; fd < FILES_STRUCT_NENTRY; fd ++, file ++) {
file->open_count = 0;
file->status = FD_NONE, file->fd = fd;
}
}
// fs_array_alloc - allocate a free file item (with FD_NONE status) in open files table
static int
fd_array_alloc(int fd, struct file **file_store) {
// panic("debug");
struct file *file = get_fd_array();
if (fd == NO_FD) {
for (fd = 0; fd < FILES_STRUCT_NENTRY; fd ++, file ++) {
if (file->status == FD_NONE) {
goto found;
}
}
return -E_MAX_OPEN;
}
else {
if (testfd(fd)) {
file += fd;
if (file->status == FD_NONE) {
goto found;
}
return -E_BUSY;
}
return -E_INVAL;
}
found:
assert(fopen_count(file) == 0);
file->status = FD_INIT, file->node = NULL;
*file_store = file;
return 0;
}
// fd_array_free - free a file item in open files table
static void
fd_array_free(struct file *file) {
assert(file->status == FD_INIT || file->status == FD_CLOSED);
assert(fopen_count(file) == 0);
if (file->status == FD_CLOSED) {
vfs_close(file->node);
}
file->status = FD_NONE;
}
static void
fd_array_acquire(struct file *file) {
assert(file->status == FD_OPENED);
fopen_count_inc(file);
}
// fd_array_release - file's open_count--; if file's open_count-- == 0 , then call fd_array_free to free this file item
static void
fd_array_release(struct file *file) {
assert(file->status == FD_OPENED || file->status == FD_CLOSED);
assert(fopen_count(file) > 0);
if (fopen_count_dec(file) == 0) {
fd_array_free(file);
}
}
// fd_array_open - file's open_count++, set status to FD_OPENED
void
fd_array_open(struct file *file) {
assert(file->status == FD_INIT && file->node != NULL);
file->status = FD_OPENED;
fopen_count_inc(file);
}
// fd_array_close - file's open_count--; if file's open_count-- == 0 , then call fd_array_free to free this file item
void
fd_array_close(struct file *file) {
assert(file->status == FD_OPENED);
assert(fopen_count(file) > 0);
file->status = FD_CLOSED;
if (fopen_count_dec(file) == 0) {
fd_array_free(file);
}
}
//fs_array_dup - duplicate file 'from' to file 'to'
void
fd_array_dup(struct file *to, struct file *from) {
//cprintf("[fd_array_dup]from fd=%d, to fd=%d\n",from->fd, to->fd);
assert(to->status == FD_INIT && from->status == FD_OPENED);
to->pos = from->pos;
to->readable = from->readable;
to->writable = from->writable;
struct inode *node = from->node;
vop_ref_inc(node), vop_open_inc(node);
to->node = node;
fd_array_open(to);
}
// fd2file - use fd as index of fd_array, return the array item (file)
static inline int
fd2file(int fd, struct file **file_store) {
if (testfd(fd)) {
struct file *file = get_fd_array() + fd;
if (file->status == FD_OPENED && file->fd == fd) {
*file_store = file;
return 0;
}
}
return -E_INVAL;
}
// file_testfd - test file is readble or writable?
bool
file_testfd(int fd, bool readable, bool writable) {
int ret;
struct file *file;
if ((ret = fd2file(fd, &file)) != 0) {
return 0;
}
if (readable && !file->readable) {
return 0;
}
if (writable && !file->writable) {
return 0;
}
return 1;
}
// open file
int
file_open(char *path, uint32_t open_flags) {
bool readable = 0, writable = 0;
switch (open_flags & O_ACCMODE) {
case O_RDONLY: readable = 1; break;
case O_WRONLY: writable = 1; break;
case O_RDWR:
readable = writable = 1;
break;
default:
return -E_INVAL;
}
int ret;
struct file *file;
if ((ret = fd_array_alloc(NO_FD, &file)) != 0) {
return ret;
}
struct inode *node;
if ((ret = vfs_open(path, open_flags, &node)) != 0) {
fd_array_free(file);
return ret;
}
file->pos = 0;
if (open_flags & O_APPEND) {
struct stat __stat, *stat = &__stat;
if ((ret = vop_fstat(node, stat)) != 0) {
vfs_close(node);
fd_array_free(file);
return ret;
}
file->pos = stat->st_size;
}
file->node = node;
file->readable = readable;
file->writable = writable;
fd_array_open(file);
return file->fd;
}
// close file
int
file_close(int fd) {
int ret;
struct file *file;
if ((ret = fd2file(fd, &file)) != 0) {
return ret;
}
fd_array_close(file);
return 0;
}
// read file
int
file_read(int fd, void *base, size_t len, size_t *copied_store) {
int ret;
struct file *file;
*copied_store = 0;
if ((ret = fd2file(fd, &file)) != 0) {
return ret;
}
if (!file->readable) {
return -E_INVAL;
}
fd_array_acquire(file);
struct iobuf __iob, *iob = iobuf_init(&__iob, base, len, file->pos);
ret = vop_read(file->node, iob);
size_t copied = iobuf_used(iob);
if (file->status == FD_OPENED) {
file->pos += copied;
}
*copied_store = copied;
fd_array_release(file);
return ret;
}
// write file
int
file_write(int fd, void *base, size_t len, size_t *copied_store) {
int ret;
struct file *file;
*copied_store = 0;
if ((ret = fd2file(fd, &file)) != 0) {
return ret;
}
if (!file->writable) {
return -E_INVAL;
}
fd_array_acquire(file);
struct iobuf __iob, *iob = iobuf_init(&__iob, base, len, file->pos);
ret = vop_write(file->node, iob);
size_t copied = iobuf_used(iob);
if (file->status == FD_OPENED) {
file->pos += copied;
}
*copied_store = copied;
fd_array_release(file);
return ret;
}
// seek file
int
file_seek(int fd, off_t pos, int whence) {
struct stat __stat, *stat = &__stat;
int ret;
struct file *file;
if ((ret = fd2file(fd, &file)) != 0) {
return ret;
}
fd_array_acquire(file);
switch (whence) {
case LSEEK_SET: break;
case LSEEK_CUR: pos += file->pos; break;
case LSEEK_END:
if ((ret = vop_fstat(file->node, stat)) == 0) {
pos += stat->st_size;
}
break;
default: ret = -E_INVAL;
}
if (ret == 0) {
if ((ret = vop_tryseek(file->node, pos)) == 0) {
file->pos = pos;
}
// cprintf("file_seek, pos=%d, whence=%d, ret=%d\n", pos, whence, ret);
}
fd_array_release(file);
return ret;
}
// stat file
int
file_fstat(int fd, struct stat *stat) {
int ret;
struct file *file;
if ((ret = fd2file(fd, &file)) != 0) {
return ret;
}
fd_array_acquire(file);
ret = vop_fstat(file->node, stat);
fd_array_release(file);
return ret;
}
// sync file
int
file_fsync(int fd) {
int ret;
struct file *file;
if ((ret = fd2file(fd, &file)) != 0) {
return ret;
}
fd_array_acquire(file);
ret = vop_fsync(file->node);
fd_array_release(file);
return ret;
}
// get file entry in DIR
int
file_getdirentry(int fd, struct dirent *direntp) {
int ret;
struct file *file;
if ((ret = fd2file(fd, &file)) != 0) {
return ret;
}
fd_array_acquire(file);
struct iobuf __iob, *iob = iobuf_init(&__iob, direntp->name, sizeof(direntp->name), direntp->offset);
if ((ret = vop_getdirentry(file->node, iob)) == 0) {
direntp->offset += iobuf_used(iob);
}
fd_array_release(file);
return ret;
}
// duplicate file
int
file_dup(int fd1, int fd2) {
int ret;
struct file *file1, *file2;
if ((ret = fd2file(fd1, &file1)) != 0) {
return ret;
}
if ((ret = fd_array_alloc(fd2, &file2)) != 0) {
return ret;
}
fd_array_dup(file2, file1);
return file2->fd;
}

View File

@@ -0,0 +1,62 @@
#ifndef __KERN_FS_FILE_H__
#define __KERN_FS_FILE_H__
//#include <types.h>
#include <fs.h>
#include <proc.h>
#include <atomic.h>
#include <assert.h>
struct inode;
struct stat;
struct dirent;
struct file {
enum {
FD_NONE, FD_INIT, FD_OPENED, FD_CLOSED,
} status;
bool readable;
bool writable;
int fd;
off_t pos;
struct inode *node;
int open_count;
};
void fd_array_init(struct file *fd_array);
void fd_array_open(struct file *file);
void fd_array_close(struct file *file);
void fd_array_dup(struct file *to, struct file *from);
bool file_testfd(int fd, bool readable, bool writable);
int file_open(char *path, uint32_t open_flags);
int file_close(int fd);
int file_read(int fd, void *base, size_t len, size_t *copied_store);
int file_write(int fd, void *base, size_t len, size_t *copied_store);
int file_seek(int fd, off_t pos, int whence);
int file_fstat(int fd, struct stat *stat);
int file_fsync(int fd);
int file_getdirentry(int fd, struct dirent *dirent);
int file_dup(int fd1, int fd2);
int file_pipe(int fd[]);
int file_mkfifo(const char *name, uint32_t open_flags);
static inline int
fopen_count(struct file *file) {
return file->open_count;
}
static inline int
fopen_count_inc(struct file *file) {
file->open_count += 1;
return file->open_count;
}
static inline int
fopen_count_dec(struct file *file) {
file->open_count -= 1;
return file->open_count;
}
#endif /* !__KERN_FS_FILE_H__ */

View File

@@ -0,0 +1,99 @@
#include <defs.h>
#include <kmalloc.h>
#include <sem.h>
#include <vfs.h>
#include <dev.h>
#include <file.h>
#include <sfs.h>
#include <inode.h>
#include <assert.h>
//called when init_main proc start
void
fs_init(void) {
vfs_init();
dev_init();
sfs_init();
}
void
fs_cleanup(void) {
vfs_cleanup();
}
void
lock_files(struct files_struct *filesp) {
down(&(filesp->files_sem));
}
void
unlock_files(struct files_struct *filesp) {
up(&(filesp->files_sem));
}
//Called when a new proc init
struct files_struct *
files_create(void) {
//cprintf("[files_create]\n");
static_assert((int)FILES_STRUCT_NENTRY > 128);
struct files_struct *filesp;
if ((filesp = kmalloc(sizeof(struct files_struct) + FILES_STRUCT_BUFSIZE)) != NULL) {
filesp->pwd = NULL;
filesp->fd_array = (void *)(filesp + 1);
filesp->files_count = 0;
sem_init(&(filesp->files_sem), 1);
fd_array_init(filesp->fd_array);
}
return filesp;
}
//Called when a proc exit
void
files_destroy(struct files_struct *filesp) {
// cprintf("[files_destroy]\n");
assert(filesp != NULL && files_count(filesp) == 0);
if (filesp->pwd != NULL) {
vop_ref_dec(filesp->pwd);
}
int i;
struct file *file = filesp->fd_array;
for (i = 0; i < FILES_STRUCT_NENTRY; i ++, file ++) {
if (file->status == FD_OPENED) {
fd_array_close(file);
}
assert(file->status == FD_NONE);
}
kfree(filesp);
}
void
files_closeall(struct files_struct *filesp) {
// cprintf("[files_closeall]\n");
assert(filesp != NULL && files_count(filesp) > 0);
int i;
struct file *file = filesp->fd_array;
//skip the stdin & stdout
for (i = 2, file += 2; i < FILES_STRUCT_NENTRY; i ++, file ++) {
if (file->status == FD_OPENED) {
fd_array_close(file);
}
}
}
int
dup_fs(struct files_struct *to, struct files_struct *from) {
// cprintf("[dup_fs]\n");
assert(to != NULL && from != NULL);
assert(files_count(to) == 0 && files_count(from) > 0);
if ((to->pwd = from->pwd) != NULL) {
vop_ref_inc(to->pwd);
}
int i;
struct file *to_file = to->fd_array, *from_file = from->fd_array;
for (i = 0; i < FILES_STRUCT_NENTRY; i ++, to_file ++, from_file ++) {
if (from_file->status == FD_OPENED) {
/* alloc_fd first */
to_file->status = FD_INIT;
fd_array_dup(to_file, from_file);
}
}
return 0;
}

View File

@@ -0,0 +1,61 @@
#ifndef __KERN_FS_FS_H__
#define __KERN_FS_FS_H__
#include <defs.h>
#include <mmu.h>
#include <sem.h>
#include <atomic.h>
#define SECTSIZE 512
#define PAGE_NSECT (PGSIZE / SECTSIZE)
#define SWAP_DEV_NO 1
#define DISK0_DEV_NO 2
#define DISK1_DEV_NO 3
void fs_init(void);
void fs_cleanup(void);
struct inode;
struct file;
/*
* process's file related informaction
*/
struct files_struct {
struct inode *pwd; // inode of present working directory
struct file *fd_array; // opened files array
int files_count; // the number of opened files
semaphore_t files_sem; // lock protect sem
};
#define FILES_STRUCT_BUFSIZE (PGSIZE - sizeof(struct files_struct))
#define FILES_STRUCT_NENTRY (FILES_STRUCT_BUFSIZE / sizeof(struct file))
void lock_files(struct files_struct *filesp);
void unlock_files(struct files_struct *filesp);
struct files_struct *files_create(void);
void files_destroy(struct files_struct *filesp);
void files_closeall(struct files_struct *filesp);
int dup_files(struct files_struct *to, struct files_struct *from);
static inline int
files_count(struct files_struct *filesp) {
return filesp->files_count;
}
static inline int
files_count_inc(struct files_struct *filesp) {
filesp->files_count += 1;
return filesp->files_count;
}
static inline int
files_count_dec(struct files_struct *filesp) {
filesp->files_count -= 1;
return filesp->files_count;
}
#endif /* !__KERN_FS_FS_H__ */

View File

@@ -0,0 +1,77 @@
#include <defs.h>
#include <string.h>
#include <iobuf.h>
#include <error.h>
#include <assert.h>
/*
* iobuf_init - init io buffer struct.
* set up io_base to point to the buffer you want to transfer to, and set io_len to the length of buffer;
* initialize io_offset as desired;
* initialize io_resid to the total amount of data that can be transferred through this io.
*/
struct iobuf *
iobuf_init(struct iobuf *iob, void *base, size_t len, off_t offset) {
iob->io_base = base;
iob->io_offset = offset;
iob->io_len = iob->io_resid = len;
return iob;
}
/* iobuf_move - move data (iob->io_base ---> data OR data --> iob->io.base) in memory
* @copiedp: the size of data memcopied
*
* iobuf_move may be called repeatedly on the same io to transfer
* additional data until the available buffer space the io refers to
* is exhausted.
*/
int
iobuf_move(struct iobuf *iob, void *data, size_t len, bool m2b, size_t *copiedp) {
size_t alen;
if ((alen = iob->io_resid) > len) {
alen = len;
}
if (alen > 0) {
void *src = iob->io_base, *dst = data;
if (m2b) {
void *tmp = src;
src = dst, dst = tmp;
}
memmove(dst, src, alen);
iobuf_skip(iob, alen), len -= alen;
}
if (copiedp != NULL) {
*copiedp = alen;
}
return (len == 0) ? 0 : -E_NO_MEM;
}
/*
* iobuf_move_zeros - set io buffer zero
* @copiedp: the size of data memcopied
*/
int
iobuf_move_zeros(struct iobuf *iob, size_t len, size_t *copiedp) {
size_t alen;
if ((alen = iob->io_resid) > len) {
alen = len;
}
if (alen > 0) {
memset(iob->io_base, 0, alen);
iobuf_skip(iob, alen), len -= alen;
}
if (copiedp != NULL) {
*copiedp = alen;
}
return (len == 0) ? 0 : -E_NO_MEM;
}
/*
* iobuf_skip - change the current position of io buffer
*/
void
iobuf_skip(struct iobuf *iob, size_t n) {
assert(iob->io_resid >= n);
iob->io_base += n, iob->io_offset += n, iob->io_resid -= n;
}

View File

@@ -0,0 +1,24 @@
#ifndef __KERN_FS_IOBUF_H__
#define __KERN_FS_IOBUF_H__
#include <defs.h>
/*
* iobuf is a buffer Rd/Wr status record
*/
struct iobuf {
void *io_base; // the base addr of buffer (used for Rd/Wr)
off_t io_offset; // current Rd/Wr position in buffer, will have been incremented by the amount transferred
size_t io_len; // the length of buffer (used for Rd/Wr)
size_t io_resid; // current resident length need to Rd/Wr, will have been decremented by the amount transferred.
};
#define iobuf_used(iob) ((size_t)((iob)->io_len - (iob)->io_resid))
struct iobuf *iobuf_init(struct iobuf *iob, void *base, size_t len, off_t offset);
int iobuf_move(struct iobuf *iob, void *data, size_t len, bool m2b, size_t *copiedp);
int iobuf_move_zeros(struct iobuf *iob, size_t len, size_t *copiedp);
void iobuf_skip(struct iobuf *iob, size_t n);
#endif /* !__KERN_FS_IOBUF_H__ */

View File

@@ -0,0 +1,114 @@
#include <defs.h>
#include <string.h>
#include <bitmap.h>
#include <kmalloc.h>
#include <error.h>
#include <assert.h>
#define WORD_TYPE uint32_t
#define WORD_BITS (sizeof(WORD_TYPE) * CHAR_BIT)
struct bitmap {
uint32_t nbits;
uint32_t nwords;
WORD_TYPE *map;
};
// bitmap_create - allocate a new bitmap object.
struct bitmap *
bitmap_create(uint32_t nbits) {
static_assert(WORD_BITS != 0);
assert(nbits != 0 && nbits + WORD_BITS > nbits);
struct bitmap *bitmap;
if ((bitmap = kmalloc(sizeof(struct bitmap))) == NULL) {
return NULL;
}
uint32_t nwords = ROUNDUP_DIV(nbits, WORD_BITS);
WORD_TYPE *map;
if ((map = kmalloc(sizeof(WORD_TYPE) * nwords)) == NULL) {
kfree(bitmap);
return NULL;
}
bitmap->nbits = nbits, bitmap->nwords = nwords;
bitmap->map = memset(map, 0xFF, sizeof(WORD_TYPE) * nwords);
/* mark any leftover bits at the end in use(0) */
if (nbits != nwords * WORD_BITS) {
uint32_t ix = nwords - 1, overbits = nbits - ix * WORD_BITS;
assert(nbits / WORD_BITS == ix);
assert(overbits > 0 && overbits < WORD_BITS);
for (; overbits < WORD_BITS; overbits ++) {
bitmap->map[ix] ^= (1 << overbits);
}
}
return bitmap;
}
// bitmap_alloc - locate a cleared bit, set it, and return its index.
int
bitmap_alloc(struct bitmap *bitmap, uint32_t *index_store) {
WORD_TYPE *map = bitmap->map;
uint32_t ix, offset, nwords = bitmap->nwords;
for (ix = 0; ix < nwords; ix ++) {
if (map[ix] != 0) {
for (offset = 0; offset < WORD_BITS; offset ++) {
WORD_TYPE mask = (1 << offset);
if (map[ix] & mask) {
map[ix] ^= mask;
*index_store = ix * WORD_BITS + offset;
return 0;
}
}
assert(0);
}
}
return -E_NO_MEM;
}
// bitmap_translate - according index, get the related word and mask
static void
bitmap_translate(struct bitmap *bitmap, uint32_t index, WORD_TYPE **word, WORD_TYPE *mask) {
assert(index < bitmap->nbits);
uint32_t ix = index / WORD_BITS, offset = index % WORD_BITS;
*word = bitmap->map + ix;
*mask = (1 << offset);
}
// bitmap_test - according index, get the related value (0 OR 1) in the bitmap
bool
bitmap_test(struct bitmap *bitmap, uint32_t index) {
WORD_TYPE *word, mask;
bitmap_translate(bitmap, index, &word, &mask);
return (*word & mask);
}
// bitmap_free - according index, set related bit to 1
void
bitmap_free(struct bitmap *bitmap, uint32_t index) {
WORD_TYPE *word, mask;
bitmap_translate(bitmap, index, &word, &mask);
assert(!(*word & mask));
*word |= mask;
}
// bitmap_destroy - free memory contains bitmap
void
bitmap_destroy(struct bitmap *bitmap) {
kfree(bitmap->map);
kfree(bitmap);
}
// bitmap_getdata - return bitmap->map, return the length of bits to len_store
void *
bitmap_getdata(struct bitmap *bitmap, size_t *len_store) {
if (len_store != NULL) {
*len_store = sizeof(WORD_TYPE) * bitmap->nwords;
}
return bitmap->map;
}

View File

@@ -0,0 +1,32 @@
#ifndef __KERN_FS_SFS_BITMAP_H__
#define __KERN_FS_SFS_BITMAP_H__
#include <defs.h>
/*
* Fixed-size array of bits. (Intended for storage management.)
*
* Functions:
* bitmap_create - allocate a new bitmap object.
* Returns NULL on error.
* bitmap_getdata - return pointer to raw bit data (for I/O).
* bitmap_alloc - locate a cleared bit, set it, and return its index.
* bitmap_mark - set a clear bit by its index.
* bitmap_unmark - clear a set bit by its index.
* bitmap_isset - return whether a particular bit is set or not.
* bitmap_destroy - destroy bitmap.
*/
struct bitmap;
struct bitmap *bitmap_create(uint32_t nbits); // allocate a new bitmap object.
int bitmap_alloc(struct bitmap *bitmap, uint32_t *index_store); // locate a cleared bit, set it, and return its index.
bool bitmap_test(struct bitmap *bitmap, uint32_t index); // return whether a particular bit is set or not.
void bitmap_free(struct bitmap *bitmap, uint32_t index); // according index, set related bit to 1
void bitmap_destroy(struct bitmap *bitmap); // free memory contains bitmap
void *bitmap_getdata(struct bitmap *bitmap, size_t *len_store); // return pointer to raw bit data (for I/O)
#endif /* !__KERN_FS_SFS_BITMAP_H__ */

View File

@@ -0,0 +1,19 @@
#include <defs.h>
#include <sfs.h>
#include <error.h>
#include <assert.h>
/*
* sfs_init - mount sfs on disk0
*
* CALL GRAPH:
* kern_init-->fs_init-->sfs_init
*/
void
sfs_init(void) {
int ret;
if ((ret = sfs_mount("disk0")) != 0) {
panic("failed: sfs: sfs_mount: %e.\n", ret);
}
}

View File

@@ -0,0 +1,129 @@
#ifndef __KERN_FS_SFS_SFS_H__
#define __KERN_FS_SFS_SFS_H__
#include <defs.h>
#include <mmu.h>
#include <list.h>
#include <sem.h>
#include <unistd.h>
/*
* Simple FS (SFS) definitions visible to ucore. This covers the on-disk format
* and is used by tools that work on SFS volumes, such as mksfs.
*/
#define SFS_MAGIC 0x2f8dbe2a /* magic number for sfs */
#define SFS_BLKSIZE PGSIZE /* size of block */
#define SFS_NDIRECT 12 /* # of direct blocks in inode */
#define SFS_MAX_INFO_LEN 31 /* max length of infomation */
#define SFS_MAX_FNAME_LEN FS_MAX_FNAME_LEN /* max length of filename */
#define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) /* max file size (128M) */
#define SFS_BLKN_SUPER 0 /* block the superblock lives in */
#define SFS_BLKN_ROOT 1 /* location of the root dir inode */
#define SFS_BLKN_FREEMAP 2 /* 1st block of the freemap */
/* # of bits in a block */
#define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT)
/* # of entries in a block */
#define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t))
/* file types */
#define SFS_TYPE_INVAL 0 /* Should not appear on disk */
#define SFS_TYPE_FILE 1
#define SFS_TYPE_DIR 2
#define SFS_TYPE_LINK 3
/*
* On-disk superblock
*/
struct sfs_super {
uint32_t magic; /* magic number, should be SFS_MAGIC */
uint32_t blocks; /* # of blocks in fs */
uint32_t unused_blocks; /* # of unused blocks in fs */
char info[SFS_MAX_INFO_LEN + 1]; /* infomation for sfs */
};
/* inode (on disk) */
struct sfs_disk_inode {
uint32_t size; /* size of the file (in bytes) */
uint16_t type; /* one of SYS_TYPE_* above */
uint16_t nlinks; /* # of hard links to this file */
uint32_t blocks; /* # of blocks */
uint32_t direct[SFS_NDIRECT]; /* direct blocks */
uint32_t indirect; /* indirect blocks */
// uint32_t db_indirect; /* double indirect blocks */
// unused
};
/* file entry (on disk) */
struct sfs_disk_entry {
uint32_t ino; /* inode number */
char name[SFS_MAX_FNAME_LEN + 1]; /* file name */
};
#define sfs_dentry_size \
sizeof(((struct sfs_disk_entry *)0)->name)
/* inode for sfs */
struct sfs_inode {
struct sfs_disk_inode *din; /* on-disk inode */
uint32_t ino; /* inode number */
bool dirty; /* true if inode modified */
int reclaim_count; /* kill inode if it hits zero */
semaphore_t sem; /* semaphore for din */
list_entry_t inode_link; /* entry for linked-list in sfs_fs */
list_entry_t hash_link; /* entry for hash linked-list in sfs_fs */
};
#define le2sin(le, member) \
to_struct((le), struct sfs_inode, member)
/* filesystem for sfs */
struct sfs_fs {
struct sfs_super super; /* on-disk superblock */
struct device *dev; /* device mounted on */
struct bitmap *freemap; /* blocks in use are mared 0 */
bool super_dirty; /* true if super/freemap modified */
void *sfs_buffer; /* buffer for non-block aligned io */
semaphore_t fs_sem; /* semaphore for fs */
semaphore_t io_sem; /* semaphore for io */
semaphore_t mutex_sem; /* semaphore for link/unlink and rename */
list_entry_t inode_list; /* inode linked-list */
list_entry_t *hash_list; /* inode hash linked-list */
};
/* hash for sfs */
#define SFS_HLIST_SHIFT 10
#define SFS_HLIST_SIZE (1 << SFS_HLIST_SHIFT)
#define sin_hashfn(x) (hash32(x, SFS_HLIST_SHIFT))
/* size of freemap (in bits) */
#define sfs_freemap_bits(super) ROUNDUP((super)->blocks, SFS_BLKBITS)
/* size of freemap (in blocks) */
#define sfs_freemap_blocks(super) ROUNDUP_DIV((super)->blocks, SFS_BLKBITS)
struct fs;
struct inode;
void sfs_init(void);
int sfs_mount(const char *devname);
void lock_sfs_fs(struct sfs_fs *sfs);
void lock_sfs_io(struct sfs_fs *sfs);
void unlock_sfs_fs(struct sfs_fs *sfs);
void unlock_sfs_io(struct sfs_fs *sfs);
int sfs_rblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks);
int sfs_wblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks);
int sfs_rbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset);
int sfs_wbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset);
int sfs_sync_super(struct sfs_fs *sfs);
int sfs_sync_freemap(struct sfs_fs *sfs);
int sfs_clear_block(struct sfs_fs *sfs, uint32_t blkno, uint32_t nblks);
int sfs_load_inode(struct sfs_fs *sfs, struct inode **node_store, uint32_t ino);
#endif /* !__KERN_FS_SFS_SFS_H__ */

View File

@@ -0,0 +1,258 @@
#include <defs.h>
#include <stdio.h>
#include <string.h>
#include <kmalloc.h>
#include <list.h>
#include <fs.h>
#include <vfs.h>
#include <dev.h>
#include <sfs.h>
#include <inode.h>
#include <iobuf.h>
#include <bitmap.h>
#include <error.h>
#include <assert.h>
/*
* sfs_sync - sync sfs's superblock and freemap in memroy into disk
*/
static int
sfs_sync(struct fs *fs) {
struct sfs_fs *sfs = fsop_info(fs, sfs);
lock_sfs_fs(sfs);
{
list_entry_t *list = &(sfs->inode_list), *le = list;
while ((le = list_next(le)) != list) {
struct sfs_inode *sin = le2sin(le, inode_link);
vop_fsync(info2node(sin, sfs_inode));
}
}
unlock_sfs_fs(sfs);
int ret;
if (sfs->super_dirty) {
sfs->super_dirty = 0;
if ((ret = sfs_sync_super(sfs)) != 0) {
sfs->super_dirty = 1;
return ret;
}
if ((ret = sfs_sync_freemap(sfs)) != 0) {
sfs->super_dirty = 1;
return ret;
}
}
return 0;
}
/*
* sfs_get_root - get the root directory inode from disk (SFS_BLKN_ROOT,1)
*/
static struct inode *
sfs_get_root(struct fs *fs) {
struct inode *node;
int ret;
if ((ret = sfs_load_inode(fsop_info(fs, sfs), &node, SFS_BLKN_ROOT)) != 0) {
panic("load sfs root failed: %e", ret);
}
return node;
}
/*
* sfs_unmount - unmount sfs, and free the memorys contain sfs->freemap/sfs_buffer/hash_liskt and sfs itself.
*/
static int
sfs_unmount(struct fs *fs) {
struct sfs_fs *sfs = fsop_info(fs, sfs);
if (!list_empty(&(sfs->inode_list))) {
return -E_BUSY;
}
assert(!sfs->super_dirty);
bitmap_destroy(sfs->freemap);
kfree(sfs->sfs_buffer);
kfree(sfs->hash_list);
kfree(sfs);
return 0;
}
/*
* sfs_cleanup - when sfs failed, then should call this function to sync sfs by calling sfs_sync
*
* NOTICE: nouse now.
*/
static void
sfs_cleanup(struct fs *fs) {
struct sfs_fs *sfs = fsop_info(fs, sfs);
uint32_t blocks = sfs->super.blocks, unused_blocks = sfs->super.unused_blocks;
cprintf("sfs: cleanup: '%s' (%d/%d/%d)\n", sfs->super.info,
blocks - unused_blocks, unused_blocks, blocks);
int i, ret;
for (i = 0; i < 32; i ++) {
if ((ret = fsop_sync(fs)) == 0) {
break;
}
}
if (ret != 0) {
warn("sfs: sync error: '%s': %e.\n", sfs->super.info, ret);
}
}
/*
* sfs_init_read - used in sfs_do_mount to read disk block(blkno, 1) directly.
*
* @dev: the block device
* @blkno: the NO. of disk block
* @blk_buffer: the buffer used for read
*
* (1) init iobuf
* (2) read dev into iobuf
*/
static int
sfs_init_read(struct device *dev, uint32_t blkno, void *blk_buffer) {
struct iobuf __iob, *iob = iobuf_init(&__iob, blk_buffer, SFS_BLKSIZE, blkno * SFS_BLKSIZE);
return dop_io(dev, iob, 0);
}
/*
* sfs_init_freemap - used in sfs_do_mount to read freemap data info in disk block(blkno, nblks) directly.
*
* @dev: the block device
* @bitmap: the bitmap in memroy
* @blkno: the NO. of disk block
* @nblks: Rd number of disk block
* @blk_buffer: the buffer used for read
*
* (1) get data addr in bitmap
* (2) read dev into iobuf
*/
static int
sfs_init_freemap(struct device *dev, struct bitmap *freemap, uint32_t blkno, uint32_t nblks, void *blk_buffer) {
size_t len;
void *data = bitmap_getdata(freemap, &len);
assert(data != NULL && len == nblks * SFS_BLKSIZE);
while (nblks != 0) {
int ret;
if ((ret = sfs_init_read(dev, blkno, data)) != 0) {
return ret;
}
blkno ++, nblks --, data += SFS_BLKSIZE;
}
return 0;
}
/*
* sfs_do_mount - mount sfs file system.
*
* @dev: the block device contains sfs file system
* @fs_store: the fs struct in memroy
*/
static int
sfs_do_mount(struct device *dev, struct fs **fs_store) {
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_super));
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_inode));
static_assert(SFS_BLKSIZE >= sizeof(struct sfs_disk_entry));
if (dev->d_blocksize != SFS_BLKSIZE) {
return -E_NA_DEV;
}
/* allocate fs structure */
struct fs *fs;
if ((fs = alloc_fs(sfs)) == NULL) {
return -E_NO_MEM;
}
struct sfs_fs *sfs = fsop_info(fs, sfs);
sfs->dev = dev;
int ret = -E_NO_MEM;
void *sfs_buffer;
if ((sfs->sfs_buffer = sfs_buffer = kmalloc(SFS_BLKSIZE)) == NULL) {
goto failed_cleanup_fs;
}
/* load and check superblock */
if ((ret = sfs_init_read(dev, SFS_BLKN_SUPER, sfs_buffer)) != 0) {
goto failed_cleanup_sfs_buffer;
}
ret = -E_INVAL;
struct sfs_super *super = sfs_buffer;
if (super->magic != SFS_MAGIC) {
cprintf("sfs: wrong magic in superblock. (%08x should be %08x).\n",
super->magic, SFS_MAGIC);
goto failed_cleanup_sfs_buffer;
}
if (super->blocks > dev->d_blocks) {
cprintf("sfs: fs has %u blocks, device has %u blocks.\n",
super->blocks, dev->d_blocks);
goto failed_cleanup_sfs_buffer;
}
super->info[SFS_MAX_INFO_LEN] = '\0';
sfs->super = *super;
ret = -E_NO_MEM;
uint32_t i;
/* alloc and initialize hash list */
list_entry_t *hash_list;
if ((sfs->hash_list = hash_list = kmalloc(sizeof(list_entry_t) * SFS_HLIST_SIZE)) == NULL) {
goto failed_cleanup_sfs_buffer;
}
for (i = 0; i < SFS_HLIST_SIZE; i ++) {
list_init(hash_list + i);
}
/* load and check freemap */
struct bitmap *freemap;
uint32_t freemap_size_nbits = sfs_freemap_bits(super);
if ((sfs->freemap = freemap = bitmap_create(freemap_size_nbits)) == NULL) {
goto failed_cleanup_hash_list;
}
uint32_t freemap_size_nblks = sfs_freemap_blocks(super);
if ((ret = sfs_init_freemap(dev, freemap, SFS_BLKN_FREEMAP, freemap_size_nblks, sfs_buffer)) != 0) {
goto failed_cleanup_freemap;
}
uint32_t blocks = sfs->super.blocks, unused_blocks = 0;
for (i = 0; i < freemap_size_nbits; i ++) {
if (bitmap_test(freemap, i)) {
unused_blocks ++;
}
}
assert(unused_blocks == sfs->super.unused_blocks);
/* and other fields */
sfs->super_dirty = 0;
sem_init(&(sfs->fs_sem), 1);
sem_init(&(sfs->io_sem), 1);
sem_init(&(sfs->mutex_sem), 1);
list_init(&(sfs->inode_list));
cprintf("sfs: mount: '%s' (%d/%d/%d)\n", sfs->super.info,
blocks - unused_blocks, unused_blocks, blocks);
/* link addr of sync/get_root/unmount/cleanup funciton fs's function pointers*/
fs->fs_sync = sfs_sync;
fs->fs_get_root = sfs_get_root;
fs->fs_unmount = sfs_unmount;
fs->fs_cleanup = sfs_cleanup;
*fs_store = fs;
return 0;
failed_cleanup_freemap:
bitmap_destroy(freemap);
failed_cleanup_hash_list:
kfree(hash_list);
failed_cleanup_sfs_buffer:
kfree(sfs_buffer);
failed_cleanup_fs:
kfree(fs);
return ret;
}
int
sfs_mount(const char *devname) {
return vfs_mount(devname, sfs_do_mount);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,167 @@
#include <defs.h>
#include <string.h>
#include <dev.h>
#include <sfs.h>
#include <iobuf.h>
#include <bitmap.h>
#include <assert.h>
//Basic block-level I/O routines
/* sfs_rwblock_nolock - Basic block-level I/O routine for Rd/Wr one disk block,
* without lock protect for mutex process on Rd/Wr disk block
* @sfs: sfs_fs which will be process
* @buf: the buffer uesed for Rd/Wr
* @blkno: the NO. of disk block
* @write: BOOL: Read or Write
* @check: BOOL: if check (blono < sfs super.blocks)
*/
static int
sfs_rwblock_nolock(struct sfs_fs *sfs, void *buf, uint32_t blkno, bool write, bool check) {
assert((blkno != 0 || !check) && blkno < sfs->super.blocks);
struct iobuf __iob, *iob = iobuf_init(&__iob, buf, SFS_BLKSIZE, blkno * SFS_BLKSIZE);
return dop_io(sfs->dev, iob, write);
}
/* sfs_rwblock - Basic block-level I/O routine for Rd/Wr N disk blocks ,
* with lock protect for mutex process on Rd/Wr disk block
* @sfs: sfs_fs which will be process
* @buf: the buffer uesed for Rd/Wr
* @blkno: the NO. of disk block
* @nblks: Rd/Wr number of disk block
* @write: BOOL: Read - 0 or Write - 1
*/
static int
sfs_rwblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks, bool write) {
int ret = 0;
lock_sfs_io(sfs);
{
while (nblks != 0) {
if ((ret = sfs_rwblock_nolock(sfs, buf, blkno, write, 1)) != 0) {
break;
}
blkno ++, nblks --;
buf += SFS_BLKSIZE;
}
}
unlock_sfs_io(sfs);
return ret;
}
/* sfs_rblock - The Wrap of sfs_rwblock function for Rd N disk blocks ,
*
* @sfs: sfs_fs which will be process
* @buf: the buffer uesed for Rd/Wr
* @blkno: the NO. of disk block
* @nblks: Rd/Wr number of disk block
*/
int
sfs_rblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks) {
return sfs_rwblock(sfs, buf, blkno, nblks, 0);
}
/* sfs_wblock - The Wrap of sfs_rwblock function for Wr N disk blocks ,
*
* @sfs: sfs_fs which will be process
* @buf: the buffer uesed for Rd/Wr
* @blkno: the NO. of disk block
* @nblks: Rd/Wr number of disk block
*/
int
sfs_wblock(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks) {
return sfs_rwblock(sfs, buf, blkno, nblks, 1);
}
/* sfs_rbuf - The Basic block-level I/O routine for Rd( non-block & non-aligned io) one disk block(using sfs->sfs_buffer)
* with lock protect for mutex process on Rd/Wr disk block
* @sfs: sfs_fs which will be process
* @buf: the buffer uesed for Rd
* @len: the length need to Rd
* @blkno: the NO. of disk block
* @offset: the offset in the content of disk block
*/
int
sfs_rbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset) {
assert(offset >= 0 && offset < SFS_BLKSIZE && offset + len <= SFS_BLKSIZE);
int ret;
lock_sfs_io(sfs);
{
if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 0, 1)) == 0) {
memcpy(buf, sfs->sfs_buffer + offset, len);
}
}
unlock_sfs_io(sfs);
return ret;
}
/* sfs_wbuf - The Basic block-level I/O routine for Wr( non-block & non-aligned io) one disk block(using sfs->sfs_buffer)
* with lock protect for mutex process on Rd/Wr disk block
* @sfs: sfs_fs which will be process
* @buf: the buffer uesed for Wr
* @len: the length need to Wr
* @blkno: the NO. of disk block
* @offset: the offset in the content of disk block
*/
int
sfs_wbuf(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset) {
assert(offset >= 0 && offset < SFS_BLKSIZE && offset + len <= SFS_BLKSIZE);
int ret;
lock_sfs_io(sfs);
{
if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 0, 1)) == 0) {
memcpy(sfs->sfs_buffer + offset, buf, len);
ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 1, 1);
}
}
unlock_sfs_io(sfs);
return ret;
}
/*
* sfs_sync_super - write sfs->super (in memory) into disk (SFS_BLKN_SUPER, 1) with lock protect.
*/
int
sfs_sync_super(struct sfs_fs *sfs) {
int ret;
lock_sfs_io(sfs);
{
memset(sfs->sfs_buffer, 0, SFS_BLKSIZE);
memcpy(sfs->sfs_buffer, &(sfs->super), sizeof(sfs->super));
ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, SFS_BLKN_SUPER, 1, 0);
}
unlock_sfs_io(sfs);
return ret;
}
/*
* sfs_sync_freemap - write sfs bitmap into disk (SFS_BLKN_FREEMAP, nblks) without lock protect.
*/
int
sfs_sync_freemap(struct sfs_fs *sfs) {
uint32_t nblks = sfs_freemap_blocks(&(sfs->super));
return sfs_wblock(sfs, bitmap_getdata(sfs->freemap, NULL), SFS_BLKN_FREEMAP, nblks);
}
/*
* sfs_clear_block - write zero info into disk (blkno, nblks) with lock protect.
* @sfs: sfs_fs which will be process
* @blkno: the NO. of disk block
* @nblks: Rd/Wr number of disk block
*/
int
sfs_clear_block(struct sfs_fs *sfs, uint32_t blkno, uint32_t nblks) {
int ret;
lock_sfs_io(sfs);
{
memset(sfs->sfs_buffer, 0, SFS_BLKSIZE);
while (nblks != 0) {
if ((ret = sfs_rwblock_nolock(sfs, sfs->sfs_buffer, blkno, 1, 1)) != 0) {
break;
}
blkno ++, nblks --;
}
}
unlock_sfs_io(sfs);
return ret;
}

View File

@@ -0,0 +1,44 @@
#include <defs.h>
#include <sem.h>
#include <sfs.h>
/*
* lock_sfs_fs - lock the process of SFS Filesystem Rd/Wr Disk Block
*
* called by: sfs_load_inode, sfs_sync, sfs_reclaim
*/
void
lock_sfs_fs(struct sfs_fs *sfs) {
down(&(sfs->fs_sem));
}
/*
* lock_sfs_io - lock the process of SFS File Rd/Wr Disk Block
*
* called by: sfs_rwblock, sfs_clear_block, sfs_sync_super
*/
void
lock_sfs_io(struct sfs_fs *sfs) {
down(&(sfs->io_sem));
}
/*
* unlock_sfs_fs - unlock the process of SFS Filesystem Rd/Wr Disk Block
*
* called by: sfs_load_inode, sfs_sync, sfs_reclaim
*/
void
unlock_sfs_fs(struct sfs_fs *sfs) {
up(&(sfs->fs_sem));
}
/*
* unlock_sfs_io - unlock the process of sfs Rd/Wr Disk Block
*
* called by: sfs_rwblock sfs_clear_block sfs_sync_super
*/
void
unlock_sfs_io(struct sfs_fs *sfs) {
up(&(sfs->io_sem));
}

View File

@@ -0,0 +1,27 @@
#include <swap.h>
#include <swapfs.h>
#include <mmu.h>
#include <fs.h>
#include <ide.h>
#include <pmm.h>
#include <assert.h>
void
swapfs_init(void) {
static_assert((PGSIZE % SECTSIZE) == 0);
if (!ide_device_valid(SWAP_DEV_NO)) {
panic("swap fs isn't available.\n");
}
max_swap_offset = ide_device_size(SWAP_DEV_NO) / (PGSIZE / SECTSIZE);
}
int
swapfs_read(swap_entry_t entry, struct Page *page) {
return ide_read_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT);
}
int
swapfs_write(swap_entry_t entry, struct Page *page) {
return ide_write_secs(SWAP_DEV_NO, swap_offset(entry) * PAGE_NSECT, page2kva(page), PAGE_NSECT);
}

View File

@@ -0,0 +1,12 @@
#ifndef __KERN_FS_SWAP_SWAPFS_H__
#define __KERN_FS_SWAP_SWAPFS_H__
#include <memlayout.h>
#include <swap.h>
void swapfs_init(void);
int swapfs_read(swap_entry_t entry, struct Page *page);
int swapfs_write(swap_entry_t entry, struct Page *page);
#endif /* !__KERN_FS_SWAP_SWAPFS_H__ */

View File

@@ -0,0 +1,317 @@
#include <defs.h>
#include <string.h>
#include <vmm.h>
#include <proc.h>
#include <kmalloc.h>
#include <vfs.h>
#include <file.h>
#include <iobuf.h>
#include <sysfile.h>
#include <stat.h>
#include <dirent.h>
#include <unistd.h>
#include <error.h>
#include <assert.h>
#define IOBUF_SIZE 4096
/* copy_path - copy path name */
static int
copy_path(char **to, const char *from) {
struct mm_struct *mm = current->mm;
char *buffer;
if ((buffer = kmalloc(FS_MAX_FPATH_LEN + 1)) == NULL) {
return -E_NO_MEM;
}
lock_mm(mm);
if (!copy_string(mm, buffer, from, FS_MAX_FPATH_LEN + 1)) {
unlock_mm(mm);
goto failed_cleanup;
}
unlock_mm(mm);
*to = buffer;
return 0;
failed_cleanup:
kfree(buffer);
return -E_INVAL;
}
/* sysfile_open - open file */
int
sysfile_open(const char *__path, uint32_t open_flags) {
int ret;
char *path;
if ((ret = copy_path(&path, __path)) != 0) {
return ret;
}
ret = file_open(path, open_flags);
kfree(path);
return ret;
}
/* sysfile_close - close file */
int
sysfile_close(int fd) {
return file_close(fd);
}
/* sysfile_read - read file */
int
sysfile_read(int fd, void *base, size_t len) {
struct mm_struct *mm = current->mm;
if (len == 0) {
return 0;
}
if (!file_testfd(fd, 1, 0)) {
return -E_INVAL;
}
void *buffer;
if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) {
return -E_NO_MEM;
}
int ret = 0;
size_t copied = 0, alen;
while (len != 0) {
if ((alen = IOBUF_SIZE) > len) {
alen = len;
}
ret = file_read(fd, buffer, alen, &alen);
if (alen != 0) {
lock_mm(mm);
{
if (copy_to_user(mm, base, buffer, alen)) {
assert(len >= alen);
base += alen, len -= alen, copied += alen;
}
else if (ret == 0) {
ret = -E_INVAL;
}
}
unlock_mm(mm);
}
if (ret != 0 || alen == 0) {
goto out;
}
}
out:
kfree(buffer);
if (copied != 0) {
return copied;
}
return ret;
}
/* sysfile_write - write file */
int
sysfile_write(int fd, void *base, size_t len) {
struct mm_struct *mm = current->mm;
if (len == 0) {
return 0;
}
if (!file_testfd(fd, 0, 1)) {
return -E_INVAL;
}
void *buffer;
if ((buffer = kmalloc(IOBUF_SIZE)) == NULL) {
return -E_NO_MEM;
}
int ret = 0;
size_t copied = 0, alen;
while (len != 0) {
if ((alen = IOBUF_SIZE) > len) {
alen = len;
}
lock_mm(mm);
{
if (!copy_from_user(mm, buffer, base, alen, 0)) {
ret = -E_INVAL;
}
}
unlock_mm(mm);
if (ret == 0) {
ret = file_write(fd, buffer, alen, &alen);
if (alen != 0) {
assert(len >= alen);
base += alen, len -= alen, copied += alen;
}
}
if (ret != 0 || alen == 0) {
goto out;
}
}
out:
kfree(buffer);
if (copied != 0) {
return copied;
}
return ret;
}
/* sysfile_seek - seek file */
int
sysfile_seek(int fd, off_t pos, int whence) {
return file_seek(fd, pos, whence);
}
/* sysfile_fstat - stat file */
int
sysfile_fstat(int fd, struct stat *__stat) {
struct mm_struct *mm = current->mm;
int ret;
struct stat __local_stat, *stat = &__local_stat;
if ((ret = file_fstat(fd, stat)) != 0) {
return ret;
}
lock_mm(mm);
{
if (!copy_to_user(mm, __stat, stat, sizeof(struct stat))) {
ret = -E_INVAL;
}
}
unlock_mm(mm);
return ret;
}
/* sysfile_fsync - sync file */
int
sysfile_fsync(int fd) {
return file_fsync(fd);
}
/* sysfile_chdir - change dir */
int
sysfile_chdir(const char *__path) {
int ret;
char *path;
if ((ret = copy_path(&path, __path)) != 0) {
return ret;
}
ret = vfs_chdir(path);
kfree(path);
return ret;
}
/* sysfile_link - link file */
int
sysfile_link(const char *__path1, const char *__path2) {
int ret;
char *old_path, *new_path;
if ((ret = copy_path(&old_path, __path1)) != 0) {
return ret;
}
if ((ret = copy_path(&new_path, __path2)) != 0) {
kfree(old_path);
return ret;
}
ret = vfs_link(old_path, new_path);
kfree(old_path), kfree(new_path);
return ret;
}
/* sysfile_rename - rename file */
int
sysfile_rename(const char *__path1, const char *__path2) {
int ret;
char *old_path, *new_path;
if ((ret = copy_path(&old_path, __path1)) != 0) {
return ret;
}
if ((ret = copy_path(&new_path, __path2)) != 0) {
kfree(old_path);
return ret;
}
ret = vfs_rename(old_path, new_path);
kfree(old_path), kfree(new_path);
return ret;
}
/* sysfile_unlink - unlink file */
int
sysfile_unlink(const char *__path) {
int ret;
char *path;
if ((ret = copy_path(&path, __path)) != 0) {
return ret;
}
ret = vfs_unlink(path);
kfree(path);
return ret;
}
/* sysfile_get cwd - get current working directory */
int
sysfile_getcwd(char *buf, size_t len) {
struct mm_struct *mm = current->mm;
if (len == 0) {
return -E_INVAL;
}
int ret = -E_INVAL;
lock_mm(mm);
{
if (user_mem_check(mm, (uintptr_t)buf, len, 1)) {
struct iobuf __iob, *iob = iobuf_init(&__iob, buf, len, 0);
ret = vfs_getcwd(iob);
}
}
unlock_mm(mm);
return ret;
}
/* sysfile_getdirentry - get the file entry in DIR */
int
sysfile_getdirentry(int fd, struct dirent *__direntp) {
struct mm_struct *mm = current->mm;
struct dirent *direntp;
if ((direntp = kmalloc(sizeof(struct dirent))) == NULL) {
return -E_NO_MEM;
}
int ret = 0;
lock_mm(mm);
{
if (!copy_from_user(mm, &(direntp->offset), &(__direntp->offset), sizeof(direntp->offset), 1)) {
ret = -E_INVAL;
}
}
unlock_mm(mm);
if (ret != 0 || (ret = file_getdirentry(fd, direntp)) != 0) {
goto out;
}
lock_mm(mm);
{
if (!copy_to_user(mm, __direntp, direntp, sizeof(struct dirent))) {
ret = -E_INVAL;
}
}
unlock_mm(mm);
out:
kfree(direntp);
return ret;
}
/* sysfile_dup - duplicate fd1 to fd2 */
int
sysfile_dup(int fd1, int fd2) {
return file_dup(fd1, fd2);
}
int
sysfile_pipe(int *fd_store) {
return -E_UNIMP;
}
int
sysfile_mkfifo(const char *__name, uint32_t open_flags) {
return -E_UNIMP;
}

View File

@@ -0,0 +1,28 @@
#ifndef __KERN_FS_SYSFILE_H__
#define __KERN_FS_SYSFILE_H__
#include <defs.h>
struct stat;
struct dirent;
int sysfile_open(const char *path, uint32_t open_flags); // Open or create a file. FLAGS/MODE per the syscall.
int sysfile_close(int fd); // Close a vnode opened
int sysfile_read(int fd, void *base, size_t len); // Read file
int sysfile_write(int fd, void *base, size_t len); // Write file
int sysfile_seek(int fd, off_t pos, int whence); // Seek file
int sysfile_fstat(int fd, struct stat *stat); // Stat file
int sysfile_fsync(int fd); // Sync file
int sysfile_chdir(const char *path); // change DIR
int sysfile_mkdir(const char *path); // create DIR
int sysfile_link(const char *path1, const char *path2); // set a path1's link as path2
int sysfile_rename(const char *path1, const char *path2); // rename file
int sysfile_unlink(const char *path); // unlink a path
int sysfile_getcwd(char *buf, size_t len); // get current working directory
int sysfile_getdirentry(int fd, struct dirent *direntp); // get the file entry in DIR
int sysfile_dup(int fd1, int fd2); // duplicate file
int sysfile_pipe(int *fd_store); // build PIPE
int sysfile_mkfifo(const char *name, uint32_t open_flags); // build named PIPE
#endif /* !__KERN_FS_SYSFILE_H__ */

View File

@@ -0,0 +1,116 @@
#include <defs.h>
#include <stdio.h>
#include <string.h>
#include <atomic.h>
#include <vfs.h>
#include <inode.h>
#include <error.h>
#include <assert.h>
#include <kmalloc.h>
/* *
* __alloc_inode - alloc a inode structure and initialize in_type
* */
struct inode *
__alloc_inode(int type) {
struct inode *node;
if ((node = kmalloc(sizeof(struct inode))) != NULL) {
node->in_type = type;
}
return node;
}
/* *
* inode_init - initialize a inode structure
* invoked by vop_init
* */
void
inode_init(struct inode *node, const struct inode_ops *ops, struct fs *fs) {
node->ref_count = 0;
node->open_count = 0;
node->in_ops = ops, node->in_fs = fs;
vop_ref_inc(node);
}
/* *
* inode_kill - kill a inode structure
* invoked by vop_kill
* */
void
inode_kill(struct inode *node) {
assert(inode_ref_count(node) == 0);
assert(inode_open_count(node) == 0);
kfree(node);
}
/* *
* inode_ref_inc - increment ref_count
* invoked by vop_ref_inc
* */
int
inode_ref_inc(struct inode *node) {
node->ref_count += 1;
return node->ref_count;
}
/* *
* inode_ref_dec - decrement ref_count
* invoked by vop_ref_dec
* calls vop_reclaim if the ref_count hits zero
* */
int
inode_ref_dec(struct inode *node) {
assert(inode_ref_count(node) > 0);
int ref_count, ret;
node->ref_count-= 1;
ref_count = node->ref_count;
if (ref_count == 0) {
if ((ret = vop_reclaim(node)) != 0 && ret != -E_BUSY) {
cprintf("vfs: warning: vop_reclaim: %e.\n", ret);
}
}
return ref_count;
}
/* *
* inode_open_inc - increment the open_count
* invoked by vop_open_inc
* */
int
inode_open_inc(struct inode *node) {
node->open_count += 1;
return node->open_count;
}
/* *
* inode_open_dec - decrement the open_count
* invoked by vop_open_dec
* calls vop_close if the open_count hits zero
* */
int
inode_open_dec(struct inode *node) {
assert(inode_open_count(node) > 0);
int open_count, ret;
node->open_count -= 1;
open_count = node->open_count;
if (open_count == 0) {
if ((ret = vop_close(node)) != 0) {
cprintf("vfs: warning: vop_close: %e.\n", ret);
}
}
return open_count;
}
/* *
* inode_check - check the various things being valid
* called before all vop_* calls
* */
void
inode_check(struct inode *node, const char *opstr) {
assert(node != NULL && node->in_ops != NULL);
assert(node->in_ops->vop_magic == VOP_MAGIC);
int ref_count = inode_ref_count(node), open_count = inode_open_count(node);
assert(ref_count >= open_count && open_count >= 0);
assert(ref_count < MAX_INODE_COUNT && open_count < MAX_INODE_COUNT);
}

View File

@@ -0,0 +1,248 @@
#ifndef __KERN_FS_VFS_INODE_H__
#define __KERN_FS_VFS_INODE_H__
#include <defs.h>
#include <dev.h>
#include <sfs.h>
#include <atomic.h>
#include <assert.h>
struct stat;
struct iobuf;
/*
* A struct inode is an abstract representation of a file.
*
* It is an interface that allows the kernel's filesystem-independent
* code to interact usefully with multiple sets of filesystem code.
*/
/*
* Abstract low-level file.
*
* Note: in_info is Filesystem-specific data, in_type is the inode type
*
* open_count is managed using VOP_INCOPEN and VOP_DECOPEN by
* vfs_open() and vfs_close(). Code above the VFS layer should not
* need to worry about it.
*/
struct inode {
union {
struct device __device_info;
struct sfs_inode __sfs_inode_info;
} in_info;
enum {
inode_type_device_info = 0x1234,
inode_type_sfs_inode_info,
} in_type;
int ref_count;
int open_count;
struct fs *in_fs;
const struct inode_ops *in_ops;
};
#define __in_type(type) inode_type_##type##_info
#define check_inode_type(node, type) ((node)->in_type == __in_type(type))
#define __vop_info(node, type) \
({ \
struct inode *__node = (node); \
assert(__node != NULL && check_inode_type(__node, type)); \
&(__node->in_info.__##type##_info); \
})
#define vop_info(node, type) __vop_info(node, type)
#define info2node(info, type) \
to_struct((info), struct inode, in_info.__##type##_info)
struct inode *__alloc_inode(int type);
#define alloc_inode(type) __alloc_inode(__in_type(type))
#define MAX_INODE_COUNT 0x10000
int inode_ref_inc(struct inode *node);
int inode_ref_dec(struct inode *node);
int inode_open_inc(struct inode *node);
int inode_open_dec(struct inode *node);
void inode_init(struct inode *node, const struct inode_ops *ops, struct fs *fs);
void inode_kill(struct inode *node);
#define VOP_MAGIC 0x8c4ba476
/*
* Abstract operations on a inode.
*
* These are used in the form VOP_FOO(inode, args), which are macros
* that expands to inode->inode_ops->vop_foo(inode, args). The operations
* "foo" are:
*
* vop_open - Called on open() of a file. Can be used to
* reject illegal or undesired open modes. Note that
* various operations can be performed without the
* file actually being opened.
* The inode need not look at O_CREAT, O_EXCL, or
* O_TRUNC, as these are handled in the VFS layer.
*
* VOP_EACHOPEN should not be called directly from
* above the VFS layer - use vfs_open() to open inodes.
* This maintains the open count so VOP_LASTCLOSE can
* be called at the right time.
*
* vop_close - To be called on *last* close() of a file.
*
* VOP_LASTCLOSE should not be called directly from
* above the VFS layer - use vfs_close() to close
* inodes opened with vfs_open().
*
* vop_reclaim - Called when inode is no longer in use. Note that
* this may be substantially after vop_lastclose is
* called.
*
*****************************************
*
* vop_read - Read data from file to uio, at offset specified
* in the uio, updating uio_resid to reflect the
* amount read, and updating uio_offset to match.
* Not allowed on directories or symlinks.
*
* vop_getdirentry - Read a single filename from a directory into a
* uio, choosing what name based on the offset
* field in the uio, and updating that field.
* Unlike with I/O on regular files, the value of
* the offset field is not interpreted outside
* the filesystem and thus need not be a byte
* count. However, the uio_resid field should be
* handled in the normal fashion.
* On non-directory objects, return ENOTDIR.
*
* vop_write - Write data from uio to file at offset specified
* in the uio, updating uio_resid to reflect the
* amount written, and updating uio_offset to match.
* Not allowed on directories or symlinks.
*
* vop_ioctl - Perform ioctl operation OP on file using data
* DATA. The interpretation of the data is specific
* to each ioctl.
*
* vop_fstat -Return info about a file. The pointer is a
* pointer to struct stat; see stat.h.
*
* vop_gettype - Return type of file. The values for file types
* are in sfs.h.
*
* vop_tryseek - Check if seeking to the specified position within
* the file is legal. (For instance, all seeks
* are illegal on serial port devices, and seeks
* past EOF on files whose sizes are fixed may be
* as well.)
*
* vop_fsync - Force any dirty buffers associated with this file
* to stable storage.
*
* vop_truncate - Forcibly set size of file to the length passed
* in, discarding any excess blocks.
*
* vop_namefile - Compute pathname relative to filesystem root
* of the file and copy to the specified io buffer.
* Need not work on objects that are not
* directories.
*
*****************************************
*
* vop_creat - Create a regular file named NAME in the passed
* directory DIR. If boolean EXCL is true, fail if
* the file already exists; otherwise, use the
* existing file if there is one. Hand back the
* inode for the file as per vop_lookup.
*
*****************************************
*
* vop_lookup - Parse PATHNAME relative to the passed directory
* DIR, and hand back the inode for the file it
* refers to. May destroy PATHNAME. Should increment
* refcount on inode handed back.
*/
struct inode_ops {
unsigned long vop_magic;
int (*vop_open)(struct inode *node, uint32_t open_flags);
int (*vop_close)(struct inode *node);
int (*vop_read)(struct inode *node, struct iobuf *iob);
int (*vop_write)(struct inode *node, struct iobuf *iob);
int (*vop_fstat)(struct inode *node, struct stat *stat);
int (*vop_fsync)(struct inode *node);
int (*vop_namefile)(struct inode *node, struct iobuf *iob);
int (*vop_getdirentry)(struct inode *node, struct iobuf *iob);
int (*vop_reclaim)(struct inode *node);
int (*vop_gettype)(struct inode *node, uint32_t *type_store);
int (*vop_tryseek)(struct inode *node, off_t pos);
int (*vop_truncate)(struct inode *node, off_t len);
int (*vop_create)(struct inode *node, const char *name, bool excl, struct inode **node_store);
int (*vop_lookup)(struct inode *node, char *path, struct inode **node_store);
int (*vop_ioctl)(struct inode *node, int op, void *data);
};
/*
* Consistency check
*/
void inode_check(struct inode *node, const char *opstr);
#define __vop_op(node, sym) \
({ \
struct inode *__node = (node); \
assert(__node != NULL && __node->in_ops != NULL && __node->in_ops->vop_##sym != NULL); \
inode_check(__node, #sym); \
__node->in_ops->vop_##sym; \
})
#define vop_open(node, open_flags) (__vop_op(node, open)(node, open_flags))
#define vop_close(node) (__vop_op(node, close)(node))
#define vop_read(node, iob) (__vop_op(node, read)(node, iob))
#define vop_write(node, iob) (__vop_op(node, write)(node, iob))
#define vop_fstat(node, stat) (__vop_op(node, fstat)(node, stat))
#define vop_fsync(node) (__vop_op(node, fsync)(node))
#define vop_namefile(node, iob) (__vop_op(node, namefile)(node, iob))
#define vop_getdirentry(node, iob) (__vop_op(node, getdirentry)(node, iob))
#define vop_reclaim(node) (__vop_op(node, reclaim)(node))
#define vop_ioctl(node, op, data) (__vop_op(node, ioctl)(node, op, data))
#define vop_gettype(node, type_store) (__vop_op(node, gettype)(node, type_store))
#define vop_tryseek(node, pos) (__vop_op(node, tryseek)(node, pos))
#define vop_truncate(node, len) (__vop_op(node, truncate)(node, len))
#define vop_create(node, name, excl, node_store) (__vop_op(node, create)(node, name, excl, node_store))
#define vop_lookup(node, path, node_store) (__vop_op(node, lookup)(node, path, node_store))
#define vop_fs(node) ((node)->in_fs)
#define vop_init(node, ops, fs) inode_init(node, ops, fs)
#define vop_kill(node) inode_kill(node)
/*
* Reference count manipulation (handled above filesystem level)
*/
#define vop_ref_inc(node) inode_ref_inc(node)
#define vop_ref_dec(node) inode_ref_dec(node)
/*
* Open count manipulation (handled above filesystem level)
*
* VOP_INCOPEN is called by vfs_open. VOP_DECOPEN is called by vfs_close.
* Neither of these should need to be called from above the vfs layer.
*/
#define vop_open_inc(node) inode_open_inc(node)
#define vop_open_dec(node) inode_open_dec(node)
static inline int
inode_ref_count(struct inode *node) {
return node->ref_count;
}
static inline int
inode_open_count(struct inode *node) {
return node->open_count;
}
#endif /* !__KERN_FS_VFS_INODE_H__ */

View File

@@ -0,0 +1,97 @@
#include <defs.h>
#include <stdio.h>
#include <string.h>
#include <vfs.h>
#include <inode.h>
#include <sem.h>
#include <kmalloc.h>
#include <error.h>
static semaphore_t bootfs_sem;
static struct inode *bootfs_node = NULL;
extern void vfs_devlist_init(void);
// __alloc_fs - allocate memory for fs, and set fs type
struct fs *
__alloc_fs(int type) {
struct fs *fs;
if ((fs = kmalloc(sizeof(struct fs))) != NULL) {
fs->fs_type = type;
}
return fs;
}
// vfs_init - vfs initialize
void
vfs_init(void) {
sem_init(&bootfs_sem, 1);
vfs_devlist_init();
}
// lock_bootfs - lock for bootfs
static void
lock_bootfs(void) {
down(&bootfs_sem);
}
// ulock_bootfs - ulock for bootfs
static void
unlock_bootfs(void) {
up(&bootfs_sem);
}
// change_bootfs - set the new fs inode
static void
change_bootfs(struct inode *node) {
struct inode *old;
lock_bootfs();
{
old = bootfs_node, bootfs_node = node;
}
unlock_bootfs();
if (old != NULL) {
vop_ref_dec(old);
}
}
// vfs_set_bootfs - change the dir of file system
int
vfs_set_bootfs(char *fsname) {
struct inode *node = NULL;
if (fsname != NULL) {
char *s;
if ((s = strchr(fsname, ':')) == NULL || s[1] != '\0') {
return -E_INVAL;
}
int ret;
if ((ret = vfs_chdir(fsname)) != 0) {
return ret;
}
if ((ret = vfs_get_curdir(&node)) != 0) {
return ret;
}
}
change_bootfs(node);
return 0;
}
// vfs_get_bootfs - get the inode of bootfs
int
vfs_get_bootfs(struct inode **node_store) {
struct inode *node = NULL;
if (bootfs_node != NULL) {
lock_bootfs();
{
if ((node = bootfs_node) != NULL) {
vop_ref_inc(bootfs_node);
}
}
unlock_bootfs();
}
if (node == NULL) {
return -E_NOENT;
}
*node_store = node;
return 0;
}

View File

@@ -0,0 +1,191 @@
#ifndef __KERN_FS_VFS_VFS_H__
#define __KERN_FS_VFS_VFS_H__
#include <defs.h>
#include <fs.h>
#include <sfs.h>
struct inode; // abstract structure for an on-disk file (inode.h)
struct device; // abstract structure for a device (dev.h)
struct iobuf; // kernel or userspace I/O buffer (iobuf.h)
/*
* Abstract filesystem. (Or device accessible as a file.)
*
* Information:
* fs_info : filesystem-specific data (sfs_fs)
* fs_type : filesystem type
* Operations:
*
* fs_sync - Flush all dirty buffers to disk.
* fs_get_root - Return root inode of filesystem.
* fs_unmount - Attempt unmount of filesystem.
* fs_cleanup - Cleanup of filesystem.???
*
*
* fs_get_root should increment the refcount of the inode returned.
* It should not ever return NULL.
*
* If fs_unmount returns an error, the filesystem stays mounted, and
* consequently the struct fs instance should remain valid. On success,
* however, the filesystem object and all storage associated with the
* filesystem should have been discarded/released.
*
*/
struct fs {
union {
struct sfs_fs __sfs_info;
} fs_info; // filesystem-specific data
enum {
fs_type_sfs_info,
} fs_type; // filesystem type
int (*fs_sync)(struct fs *fs); // Flush all dirty buffers to disk
struct inode *(*fs_get_root)(struct fs *fs); // Return root inode of filesystem.
int (*fs_unmount)(struct fs *fs); // Attempt unmount of filesystem.
void (*fs_cleanup)(struct fs *fs); // Cleanup of filesystem.???
};
#define __fs_type(type) fs_type_##type##_info
#define check_fs_type(fs, type) ((fs)->fs_type == __fs_type(type))
#define __fsop_info(_fs, type) ({ \
struct fs *__fs = (_fs); \
assert(__fs != NULL && check_fs_type(__fs, type)); \
&(__fs->fs_info.__##type##_info); \
})
#define fsop_info(fs, type) __fsop_info(fs, type)
#define info2fs(info, type) \
to_struct((info), struct fs, fs_info.__##type##_info)
struct fs *__alloc_fs(int type);
#define alloc_fs(type) __alloc_fs(__fs_type(type))
// Macros to shorten the calling sequences.
#define fsop_sync(fs) ((fs)->fs_sync(fs))
#define fsop_get_root(fs) ((fs)->fs_get_root(fs))
#define fsop_unmount(fs) ((fs)->fs_unmount(fs))
#define fsop_cleanup(fs) ((fs)->fs_cleanup(fs))
/*
* Virtual File System layer functions.
*
* The VFS layer translates operations on abstract on-disk files or
* pathnames to operations on specific files on specific filesystems.
*/
void vfs_init(void);
void vfs_cleanup(void);
void vfs_devlist_init(void);
/*
* VFS layer low-level operations.
* See inode.h for direct operations on inodes.
* See fs.h for direct operations on filesystems/devices.
*
* vfs_set_curdir - change current directory of current thread by inode
* vfs_get_curdir - retrieve inode of current directory of current thread
* vfs_get_root - get root inode for the filesystem named DEVNAME
* vfs_get_devname - get mounted device name for the filesystem passed in
*/
int vfs_set_curdir(struct inode *dir);
int vfs_get_curdir(struct inode **dir_store);
int vfs_get_root(const char *devname, struct inode **root_store);
const char *vfs_get_devname(struct fs *fs);
/*
* VFS layer high-level operations on pathnames
* Because namei may destroy pathnames, these all may too.
*
* vfs_open - Open or create a file. FLAGS/MODE per the syscall.
* vfs_close - Close a inode opened with vfs_open. Does not fail.
* (See vfspath.c for a discussion of why.)
* vfs_link - Create a hard link to a file.
* vfs_symlink - Create a symlink PATH containing contents CONTENTS.
* vfs_readlink - Read contents of a symlink into a uio.
* vfs_mkdir - Create a directory. MODE per the syscall.
* vfs_unlink - Delete a file/directory.
* vfs_rename - rename a file.
* vfs_chdir - Change current directory of current thread by name.
* vfs_getcwd - Retrieve name of current directory of current thread.
*
*/
int vfs_open(char *path, uint32_t open_flags, struct inode **inode_store);
int vfs_close(struct inode *node);
int vfs_link(char *old_path, char *new_path);
int vfs_symlink(char *old_path, char *new_path);
int vfs_readlink(char *path, struct iobuf *iob);
int vfs_mkdir(char *path);
int vfs_unlink(char *path);
int vfs_rename(char *old_path, char *new_path);
int vfs_chdir(char *path);
int vfs_getcwd(struct iobuf *iob);
/*
* VFS layer mid-level operations.
*
* vfs_lookup - Like VOP_LOOKUP, but takes a full device:path name,
* or a name relative to the current directory, and
* goes to the correct filesystem.
* vfs_lookparent - Likewise, for VOP_LOOKPARENT.
*
* Both of these may destroy the path passed in.
*/
int vfs_lookup(char *path, struct inode **node_store);
int vfs_lookup_parent(char *path, struct inode **node_store, char **endp);
/*
* Misc
*
* vfs_set_bootfs - Set the filesystem that paths beginning with a
* slash are sent to. If not set, these paths fail
* with ENOENT. The argument should be the device
* name or volume name for the filesystem (such as
* "lhd0:") but need not have the trailing colon.
*
* vfs_get_bootfs - return the inode of the bootfs filesystem.
*
* vfs_add_fs - Add a hardwired filesystem to the VFS named device
* list. It will be accessible as "devname:". This is
* intended for filesystem-devices like emufs, and
* gizmos like Linux procfs or BSD kernfs, not for
* mounting filesystems on disk devices.
*
* vfs_add_dev - Add a device to the VFS named device list. If
* MOUNTABLE is zero, the device will be accessible
* as "DEVNAME:". If the mountable flag is set, the
* device will be accessible as "DEVNAMEraw:" and
* mountable under the name "DEVNAME". Thus, the
* console, added with MOUNTABLE not set, would be
* accessed by pathname as "con:", and lhd0, added
* with mountable set, would be accessed by
* pathname as "lhd0raw:" and mounted by passing
* "lhd0" to vfs_mount.
*
* vfs_mount - Attempt to mount a filesystem on a device. The
* device named by DEVNAME will be looked up and
* passed, along with DATA, to the supplied function
* MOUNTFUNC, which should create a struct fs and
* return it in RESULT.
*
* vfs_unmount - Unmount the filesystem presently mounted on the
* specified device.
*
* vfs_unmountall - Unmount all mounted filesystems.
*/
int vfs_set_bootfs(char *fsname);
int vfs_get_bootfs(struct inode **node_store);
int vfs_add_fs(const char *devname, struct fs *fs);
int vfs_add_dev(const char *devname, struct inode *devnode, bool mountable);
int vfs_mount(const char *devname, int (*mountfunc)(struct device *dev, struct fs **fs_store));
int vfs_unmount(const char *devname);
int vfs_unmount_all(void);
#endif /* !__KERN_FS_VFS_VFS_H__ */

View File

@@ -0,0 +1,309 @@
#include <defs.h>
#include <stdio.h>
#include <string.h>
#include <vfs.h>
#include <dev.h>
#include <inode.h>
#include <sem.h>
#include <list.h>
#include <kmalloc.h>
#include <unistd.h>
#include <error.h>
#include <assert.h>
// device info entry in vdev_list
typedef struct {
const char *devname;
struct inode *devnode;
struct fs *fs;
bool mountable;
list_entry_t vdev_link;
} vfs_dev_t;
#define le2vdev(le, member) \
to_struct((le), vfs_dev_t, member)
static list_entry_t vdev_list; // device info list in vfs layer
static semaphore_t vdev_list_sem;
static void
lock_vdev_list(void) {
down(&vdev_list_sem);
}
static void
unlock_vdev_list(void) {
up(&vdev_list_sem);
}
void
vfs_devlist_init(void) {
list_init(&vdev_list);
sem_init(&vdev_list_sem, 1);
}
// vfs_cleanup - finally clean (or sync) fs
void
vfs_cleanup(void) {
if (!list_empty(&vdev_list)) {
lock_vdev_list();
{
list_entry_t *list = &vdev_list, *le = list;
while ((le = list_next(le)) != list) {
vfs_dev_t *vdev = le2vdev(le, vdev_link);
if (vdev->fs != NULL) {
fsop_cleanup(vdev->fs);
}
}
}
unlock_vdev_list();
}
}
/*
* vfs_get_root - Given a device name (stdin, stdout, etc.), hand
* back an appropriate inode.
*/
int
vfs_get_root(const char *devname, struct inode **node_store) {
assert(devname != NULL);
int ret = -E_NO_DEV;
if (!list_empty(&vdev_list)) {
lock_vdev_list();
{
list_entry_t *list = &vdev_list, *le = list;
while ((le = list_next(le)) != list) {
vfs_dev_t *vdev = le2vdev(le, vdev_link);
if (strcmp(devname, vdev->devname) == 0) {
struct inode *found = NULL;
if (vdev->fs != NULL) {
found = fsop_get_root(vdev->fs);
}
else if (!vdev->mountable) {
vop_ref_inc(vdev->devnode);
found = vdev->devnode;
}
if (found != NULL) {
ret = 0, *node_store = found;
}
else {
ret = -E_NA_DEV;
}
break;
}
}
}
unlock_vdev_list();
}
return ret;
}
/*
* vfs_get_devname - Given a filesystem, hand back the name of the device it's mounted on.
*/
const char *
vfs_get_devname(struct fs *fs) {
assert(fs != NULL);
list_entry_t *list = &vdev_list, *le = list;
while ((le = list_next(le)) != list) {
vfs_dev_t *vdev = le2vdev(le, vdev_link);
if (vdev->fs == fs) {
return vdev->devname;
}
}
return NULL;
}
/*
* check_devname_confilct - Is there alreadily device which has the same name?
*/
static bool
check_devname_conflict(const char *devname) {
list_entry_t *list = &vdev_list, *le = list;
while ((le = list_next(le)) != list) {
vfs_dev_t *vdev = le2vdev(le, vdev_link);
if (strcmp(vdev->devname, devname) == 0) {
return 0;
}
}
return 1;
}
/*
* vfs_do_add - Add a new device to the VFS layer's device table.
*
* If "mountable" is set, the device will be treated as one that expects
* to have a filesystem mounted on it, and a raw device will be created
* for direct access.
*/
static int
vfs_do_add(const char *devname, struct inode *devnode, struct fs *fs, bool mountable) {
assert(devname != NULL);
assert((devnode == NULL && !mountable) || (devnode != NULL && check_inode_type(devnode, device)));
if (strlen(devname) > FS_MAX_DNAME_LEN) {
return -E_TOO_BIG;
}
int ret = -E_NO_MEM;
char *s_devname;
if ((s_devname = strdup(devname)) == NULL) {
return ret;
}
vfs_dev_t *vdev;
if ((vdev = kmalloc(sizeof(vfs_dev_t))) == NULL) {
goto failed_cleanup_name;
}
ret = -E_EXISTS;
lock_vdev_list();
if (!check_devname_conflict(s_devname)) {
unlock_vdev_list();
goto failed_cleanup_vdev;
}
vdev->devname = s_devname;
vdev->devnode = devnode;
vdev->mountable = mountable;
vdev->fs = fs;
list_add(&vdev_list, &(vdev->vdev_link));
unlock_vdev_list();
return 0;
failed_cleanup_vdev:
kfree(vdev);
failed_cleanup_name:
kfree(s_devname);
return ret;
}
/*
* vfs_add_fs - Add a new fs, by name. See vfs_do_add information for the description of
* mountable.
*/
int
vfs_add_fs(const char *devname, struct fs *fs) {
return vfs_do_add(devname, NULL, fs, 0);
}
/*
* vfs_add_dev - Add a new device, by name. See vfs_do_add information for the description of
* mountable.
*/
int
vfs_add_dev(const char *devname, struct inode *devnode, bool mountable) {
return vfs_do_add(devname, devnode, NULL, mountable);
}
/*
* find_mount - Look for a mountable device named DEVNAME.
* Should already hold vdev_list lock.
*/
static int
find_mount(const char *devname, vfs_dev_t **vdev_store) {
assert(devname != NULL);
list_entry_t *list = &vdev_list, *le = list;
while ((le = list_next(le)) != list) {
vfs_dev_t *vdev = le2vdev(le, vdev_link);
if (vdev->mountable && strcmp(vdev->devname, devname) == 0) {
*vdev_store = vdev;
return 0;
}
}
return -E_NO_DEV;
}
/*
* vfs_mount - Mount a filesystem. Once we've found the device, call MOUNTFUNC to
* set up the filesystem and hand back a struct fs.
*
* The DATA argument is passed through unchanged to MOUNTFUNC.
*/
int
vfs_mount(const char *devname, int (*mountfunc)(struct device *dev, struct fs **fs_store)) {
int ret;
lock_vdev_list();
vfs_dev_t *vdev;
if ((ret = find_mount(devname, &vdev)) != 0) {
goto out;
}
if (vdev->fs != NULL) {
ret = -E_BUSY;
goto out;
}
assert(vdev->devname != NULL && vdev->mountable);
struct device *dev = vop_info(vdev->devnode, device);
if ((ret = mountfunc(dev, &(vdev->fs))) == 0) {
assert(vdev->fs != NULL);
cprintf("vfs: mount %s.\n", vdev->devname);
}
out:
unlock_vdev_list();
return ret;
}
/*
* vfs_unmount - Unmount a filesystem/device by name.
* First calls FSOP_SYNC on the filesystem; then calls FSOP_UNMOUNT.
*/
int
vfs_unmount(const char *devname) {
int ret;
lock_vdev_list();
vfs_dev_t *vdev;
if ((ret = find_mount(devname, &vdev)) != 0) {
goto out;
}
if (vdev->fs == NULL) {
ret = -E_INVAL;
goto out;
}
assert(vdev->devname != NULL && vdev->mountable);
if ((ret = fsop_sync(vdev->fs)) != 0) {
goto out;
}
if ((ret = fsop_unmount(vdev->fs)) == 0) {
vdev->fs = NULL;
cprintf("vfs: unmount %s.\n", vdev->devname);
}
out:
unlock_vdev_list();
return ret;
}
/*
* vfs_unmount_all - Global unmount function.
*/
int
vfs_unmount_all(void) {
if (!list_empty(&vdev_list)) {
lock_vdev_list();
{
list_entry_t *list = &vdev_list, *le = list;
while ((le = list_next(le)) != list) {
vfs_dev_t *vdev = le2vdev(le, vdev_link);
if (vdev->mountable && vdev->fs != NULL) {
int ret;
if ((ret = fsop_sync(vdev->fs)) != 0) {
cprintf("vfs: warning: sync failed for %s: %e.\n", vdev->devname, ret);
continue ;
}
if ((ret = fsop_unmount(vdev->fs)) != 0) {
cprintf("vfs: warning: unmount failed for %s: %e.\n", vdev->devname, ret);
continue ;
}
vdev->fs = NULL;
cprintf("vfs: unmount %s.\n", vdev->devname);
}
}
}
unlock_vdev_list();
}
return 0;
}

View File

@@ -0,0 +1,110 @@
#include <defs.h>
#include <string.h>
#include <vfs.h>
#include <inode.h>
#include <unistd.h>
#include <error.h>
#include <assert.h>
// open file in vfs, get/create inode for file with filename path.
int
vfs_open(char *path, uint32_t open_flags, struct inode **node_store) {
bool can_write = 0;
switch (open_flags & O_ACCMODE) {
case O_RDONLY:
break;
case O_WRONLY:
case O_RDWR:
can_write = 1;
break;
default:
return -E_INVAL;
}
if (open_flags & O_TRUNC) {
if (!can_write) {
return -E_INVAL;
}
}
int ret;
struct inode *node;
bool excl = (open_flags & O_EXCL) != 0;
bool create = (open_flags & O_CREAT) != 0;
ret = vfs_lookup(path, &node);
if (ret != 0) {
if (ret == -16 && (create)) {
char *name;
struct inode *dir;
if ((ret = vfs_lookup_parent(path, &dir, &name)) != 0) {
return ret;
}
ret = vop_create(dir, name, excl, &node);
} else return ret;
} else if (excl && create) {
return -E_EXISTS;
}
assert(node != NULL);
if ((ret = vop_open(node, open_flags)) != 0) {
vop_ref_dec(node);
return ret;
}
vop_open_inc(node);
if (open_flags & O_TRUNC || create) {
if ((ret = vop_truncate(node, 0)) != 0) {
vop_open_dec(node);
vop_ref_dec(node);
return ret;
}
}
*node_store = node;
return 0;
}
// close file in vfs
int
vfs_close(struct inode *node) {
vop_open_dec(node);
vop_ref_dec(node);
return 0;
}
// unimplement
int
vfs_unlink(char *path) {
return -E_UNIMP;
}
// unimplement
int
vfs_rename(char *old_path, char *new_path) {
return -E_UNIMP;
}
// unimplement
int
vfs_link(char *old_path, char *new_path) {
return -E_UNIMP;
}
// unimplement
int
vfs_symlink(char *old_path, char *new_path) {
return -E_UNIMP;
}
// unimplement
int
vfs_readlink(char *path, struct iobuf *iob) {
return -E_UNIMP;
}
// unimplement
int
vfs_mkdir(char *path){
return -E_UNIMP;
}

View File

@@ -0,0 +1,101 @@
#include <defs.h>
#include <string.h>
#include <vfs.h>
#include <inode.h>
#include <error.h>
#include <assert.h>
/*
* get_device- Common code to pull the device name, if any, off the front of a
* path and choose the inode to begin the name lookup relative to.
*/
static int
get_device(char *path, char **subpath, struct inode **node_store) {
int i, slash = -1, colon = -1;
for (i = 0; path[i] != '\0'; i ++) {
if (path[i] == ':') { colon = i; break; }
if (path[i] == '/') { slash = i; break; }
}
if (colon < 0 && slash != 0) {
/* *
* No colon before a slash, so no device name specified, and the slash isn't leading
* or is also absent, so this is a relative path or just a bare filename. Start from
* the current directory, and use the whole thing as the subpath.
* */
*subpath = path;
return vfs_get_curdir(node_store);
}
if (colon > 0) {
/* device:path - get root of device's filesystem */
path[colon] = '\0';
/* device:/path - skip slash, treat as device:path */
while (path[++ colon] == '/');
*subpath = path + colon;
return vfs_get_root(path, node_store);
}
/* *
* we have either /path or :path
* /path is a path relative to the root of the "boot filesystem"
* :path is a path relative to the root of the current filesystem
* */
int ret;
if (*path == '/') {
if ((ret = vfs_get_bootfs(node_store)) != 0) {
return ret;
}
}
else {
assert(*path == ':');
struct inode *node;
if ((ret = vfs_get_curdir(&node)) != 0) {
return ret;
}
/* The current directory may not be a device, so it must have a fs. */
assert(node->in_fs != NULL);
*node_store = fsop_get_root(node->in_fs);
vop_ref_dec(node);
}
/* ///... or :/... */
while (*(++ path) == '/');
*subpath = path;
return 0;
}
/*
* vfs_lookup - get the inode according to the path filename
*/
int
vfs_lookup(char *path, struct inode **node_store) {
int ret;
struct inode *node;
if ((ret = get_device(path, &path, &node)) != 0) {
return ret;
}
if (*path != '\0') {
ret = vop_lookup(node, path, node_store);
vop_ref_dec(node);
return ret;
}
*node_store = node;
return 0;
}
/*
* vfs_lookup_parent - Name-to-vnode translation.
* (In BSD, both of these are subsumed by namei().)
*/
int
vfs_lookup_parent(char *path, struct inode **node_store, char **endp){
int ret;
struct inode *node;
if ((ret = get_device(path, &path, &node)) != 0) {
return ret;
}
*endp = path;
*node_store = node;
return 0;
}

View File

@@ -0,0 +1,126 @@
#include <defs.h>
#include <string.h>
#include <vfs.h>
#include <inode.h>
#include <iobuf.h>
#include <stat.h>
#include <proc.h>
#include <error.h>
#include <assert.h>
/*
* get_cwd_nolock - retrieve current process's working directory. without lock protect
*/
static struct inode *
get_cwd_nolock(void) {
return current->filesp->pwd;
}
/*
* set_cwd_nolock - set current working directory.
*/
static void
set_cwd_nolock(struct inode *pwd) {
current->filesp->pwd = pwd;
}
/*
* lock_cfs - lock the fs related process on current process
*/
static void
lock_cfs(void) {
lock_files(current->filesp);
}
/*
* unlock_cfs - unlock the fs related process on current process
*/
static void
unlock_cfs(void) {
unlock_files(current->filesp);
}
/*
* vfs_get_curdir - Get current directory as a inode.
*/
int
vfs_get_curdir(struct inode **dir_store) {
struct inode *node;
if ((node = get_cwd_nolock()) != NULL) {
vop_ref_inc(node);
*dir_store = node;
return 0;
}
return -E_NOENT;
}
/*
* vfs_set_curdir - Set current directory as a inode.
* The passed inode must in fact be a directory.
*/
int
vfs_set_curdir(struct inode *dir) {
int ret = 0;
lock_cfs();
struct inode *old_dir;
if ((old_dir = get_cwd_nolock()) != dir) {
if (dir != NULL) {
uint32_t type;
if ((ret = vop_gettype(dir, &type)) != 0) {
goto out;
}
if (!S_ISDIR(type)) {
ret = -E_NOTDIR;
goto out;
}
vop_ref_inc(dir);
}
set_cwd_nolock(dir);
if (old_dir != NULL) {
vop_ref_dec(old_dir);
}
}
out:
unlock_cfs();
return ret;
}
/*
* vfs_chdir - Set current directory, as a pathname. Use vfs_lookup to translate
* it to a inode.
*/
int
vfs_chdir(char *path) {
int ret;
struct inode *node;
if ((ret = vfs_lookup(path, &node)) == 0) {
ret = vfs_set_curdir(node);
vop_ref_dec(node);
}
return ret;
}
/*
* vfs_getcwd - retrieve current working directory(cwd).
*/
int
vfs_getcwd(struct iobuf *iob) {
int ret;
struct inode *node;
if ((ret = vfs_get_curdir(&node)) != 0) {
return ret;
}
assert(node->in_fs != NULL);
const char *devname = vfs_get_devname(node->in_fs);
if ((ret = iobuf_move(iob, (char *)devname, strlen(devname), 1, NULL)) != 0) {
goto out;
}
char colon = ':';
if ((ret = iobuf_move(iob, &colon, sizeof(colon), 1, NULL)) != 0) {
goto out;
}
ret = vop_namefile(node, iob);
out:
vop_ref_dec(node);
return ret;
}

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)

View File

@@ -0,0 +1,116 @@
#include <defs.h>
#include <stdio.h>
#include <string.h>
#include <console.h>
#include <kdebug.h>
#include <picirq.h>
#include <trap.h>
#include <clock.h>
#include <intr.h>
#include <pmm.h>
#include <vmm.h>
#include <ide.h>
#include <swap.h>
#include <proc.h>
#include <fs.h>
int kern_init(void) __attribute__((noreturn));
static void lab1_switch_test(void);
int
kern_init(void) {
extern char edata[], end[];
memset(edata, 0, end - edata);
cons_init(); // init the console
const char *message = "(THU.CST) os is loading ...";
cprintf("%s\n\n", message);
print_kerninfo();
grade_backtrace();
pmm_init(); // init physical memory management
pic_init(); // init interrupt controller
idt_init(); // init interrupt descriptor table
vmm_init(); // init virtual memory management
sched_init(); // init scheduler
proc_init(); // init process table
ide_init(); // init ide devices
swap_init(); // init swap
fs_init(); // init fs
clock_init(); // init clock interrupt
intr_enable(); // enable irq interrupt
//LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test()
// user/kernel mode switch test
//lab1_switch_test();
cpu_idle(); // run idle process
}
void __attribute__((noinline))
grade_backtrace2(int arg0, int arg1, int arg2, int arg3) {
mon_backtrace(0, NULL, NULL);
}
void __attribute__((noinline))
grade_backtrace1(int arg0, int arg1) {
grade_backtrace2(arg0, (int)&arg0, arg1, (int)&arg1);
}
void __attribute__((noinline))
grade_backtrace0(int arg0, int arg1, int arg2) {
grade_backtrace1(arg0, arg2);
}
void
grade_backtrace(void) {
grade_backtrace0(0, (int)kern_init, 0xffff0000);
}
static void
lab1_print_cur_status(void) {
static int round = 0;
uint16_t reg1, reg2, reg3, reg4;
asm volatile (
"mov %%cs, %0;"
"mov %%ds, %1;"
"mov %%es, %2;"
"mov %%ss, %3;"
: "=m"(reg1), "=m"(reg2), "=m"(reg3), "=m"(reg4));
cprintf("%d: @ring %d\n", round, reg1 & 3);
cprintf("%d: cs = %x\n", round, reg1);
cprintf("%d: ds = %x\n", round, reg2);
cprintf("%d: es = %x\n", round, reg3);
cprintf("%d: ss = %x\n", round, reg4);
round ++;
}
static void
lab1_switch_to_user(void) {
//LAB1 CHALLENGE 1 : TODO
}
static void
lab1_switch_to_kernel(void) {
//LAB1 CHALLENGE 1 : TODO
}
static void
lab1_switch_test(void) {
lab1_print_cur_status();
cprintf("+++ switch to user mode +++\n");
lab1_switch_to_user();
lab1_print_cur_status();
cprintf("+++ switch to kernel mode +++\n");
lab1_switch_to_kernel();
lab1_print_cur_status();
}

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;
}
}
}

View File

@@ -0,0 +1,78 @@
#include <defs.h>
#include <stdio.h>
#include <console.h>
#include <unistd.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, NO_FD, &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;
}

View File

@@ -0,0 +1,26 @@
#include <string.h>
#include <kmalloc.h>
char *
strdup(const char *src) {
char *dst;
size_t len = strlen(src);
if ((dst = kmalloc(len + 1)) != NULL) {
memcpy(dst, src, len);
dst[len] = '\0';
}
return dst;
}
char *
stradd(const char *src1, const char *src2) {
char *ret, *dst;
size_t len1 = strlen(src1), len2 = strlen(src2);
if ((ret = dst = kmalloc(len1 + len2 + 1)) != NULL) {
memcpy(dst, src1, len1), dst += len1;
memcpy(dst, src2, len2), dst += len2;
*dst = '\0';
}
return ret;
}

View File

@@ -0,0 +1,294 @@
#include <pmm.h>
#include <list.h>
#include <string.h>
#include <default_pmm.h>
/* In the first fit algorithm, the allocator keeps a list of free blocks (known as the free list) and,
on receiving a request for memory, scans along the list for the first block that is large enough to
satisfy the request. If the chosen block is significantly larger than that requested, then it is
usually split, and the remainder added to the list as another free block.
Please see Page 196~198, Section 8.2 of Yan Wei Ming's chinese book "Data Structure -- C programming language"
*/
// LAB2 EXERCISE 1: YOUR CODE
// you should rewrite functions: default_init,default_init_memmap,default_alloc_pages, default_free_pages.
/*
* Details of FFMA
* (1) Prepare: In order to implement the First-Fit Mem Alloc (FFMA), we should manage the free mem block use some list.
* The struct free_area_t is used for the management of free mem blocks. At first you should
* be familiar to the struct list in list.h. struct list is a simple doubly linked list implementation.
* You should know howto USE: list_init, list_add(list_add_after), list_add_before, list_del, list_next, list_prev
* Another tricky method is to transform a general list struct to a special struct (such as struct page):
* you can find some MACRO: le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.)
* (2) default_init: you can reuse the demo default_init fun to init the free_list and set nr_free to 0.
* free_list is used to record the free mem blocks. nr_free is the total number for free mem blocks.
* (3) default_init_memmap: CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap
* This fun is used to init a free block (with parameter: addr_base, page_number).
* First you should init each page (in memlayout.h) in this free block, include:
* p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c),
* the bit PG_reserved is setted in p->flags)
* if this page is free and is not the first page of free block, p->property should be set to 0.
* if this page is free and is the first page of free block, p->property should be set to total num of block.
* p->ref should be 0, because now p is free and no reference.
* We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); )
* Finally, we should sum the number of free mem block: nr_free+=n
* (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr
* of malloced block.
* (4.1) So you should search freelist like this:
* list_entry_t le = &free_list;
* while((le=list_next(le)) != &free_list) {
* ....
* (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n?
* struct Page *p = le2page(le, page_link);
* if(p->property >= n){ ...
* (4.1.2) If we find this p, then it' means we find a free block(block size >=n), and the first n pages can be malloced.
* Some flag bits of this page should be setted: PG_reserved =1, PG_property =0
* unlink the pages from free_list
* (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block,
* (such as: le2page(le,page_link))->property = p->property - n;)
* (4.1.3) re-caluclate nr_free (number of the the rest of all free block)
* (4.1.4) return p
* (4.2) If we can not find a free block (block size >=n), then return NULL
* (5) default_free_pages: relink the pages into free list, maybe merge small free blocks into big free blocks.
* (5.1) according the base addr of withdrawed blocks, search free list, find the correct position
* (from low to high addr), and insert the pages. (may use list_next, le2page, list_add_before)
* (5.2) reset the fields of pages, such as p->ref, p->flags (PageProperty)
* (5.3) try to merge low addr or high addr blocks. Notice: should change some pages's p->property correctly.
*/
free_area_t free_area;
#define free_list (free_area.free_list)
#define nr_free (free_area.nr_free)
static void
default_init(void) {
list_init(&free_list);
nr_free = 0;
}
static void
default_init_memmap(struct Page *base, size_t n) {
assert(n > 0);
struct Page *p = base;
for (; p != base + n; p ++) {
assert(PageReserved(p));
p->flags = 0;
SetPageProperty(p);
p->property = 0;
set_page_ref(p, 0);
list_add_before(&free_list, &(p->page_link));
}
nr_free += n;
//first block
base->property = n;
}
static struct Page *
default_alloc_pages(size_t n) {
assert(n > 0);
if (n > nr_free) {
return NULL;
}
list_entry_t *le, *len;
le = &free_list;
while((le=list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
if(p->property >= n){
int i;
for(i=0;i<n;i++){
len = list_next(le);
struct Page *pp = le2page(le, page_link);
SetPageReserved(pp);
ClearPageProperty(pp);
list_del(le);
le = len;
}
if(p->property>n){
(le2page(le,page_link))->property = p->property - n;
}
ClearPageProperty(p);
SetPageReserved(p);
nr_free -= n;
return p;
}
}
return NULL;
}
static void
default_free_pages(struct Page *base, size_t n) {
assert(n > 0);
assert(PageReserved(base));
list_entry_t *le = &free_list;
struct Page * p;
while((le=list_next(le)) != &free_list) {
p = le2page(le, page_link);
if(p>base){
break;
}
}
//list_add_before(le, base->page_link);
for(p=base;p<base+n;p++){
list_add_before(le, &(p->page_link));
}
base->flags = 0;
set_page_ref(base, 0);
ClearPageProperty(base);
SetPageProperty(base);
base->property = n;
p = le2page(le,page_link) ;
if( base+n == p ){
base->property += p->property;
p->property = 0;
}
le = list_prev(&(base->page_link));
p = le2page(le, page_link);
if(le!=&free_list && p==base-1){
while(le!=&free_list){
if(p->property){
p->property += base->property;
base->property = 0;
break;
}
le = list_prev(le);
p = le2page(le,page_link);
}
}
nr_free += n;
return ;
}
static size_t
default_nr_free_pages(void) {
return nr_free;
}
static void
basic_check(void) {
struct Page *p0, *p1, *p2;
p0 = p1 = p2 = NULL;
assert((p0 = alloc_page()) != NULL);
assert((p1 = alloc_page()) != NULL);
assert((p2 = alloc_page()) != NULL);
assert(p0 != p1 && p0 != p2 && p1 != p2);
assert(page_ref(p0) == 0 && page_ref(p1) == 0 && page_ref(p2) == 0);
assert(page2pa(p0) < npage * PGSIZE);
assert(page2pa(p1) < npage * PGSIZE);
assert(page2pa(p2) < npage * PGSIZE);
list_entry_t free_list_store = free_list;
list_init(&free_list);
assert(list_empty(&free_list));
unsigned int nr_free_store = nr_free;
nr_free = 0;
assert(alloc_page() == NULL);
free_page(p0);
free_page(p1);
free_page(p2);
assert(nr_free == 3);
assert((p0 = alloc_page()) != NULL);
assert((p1 = alloc_page()) != NULL);
assert((p2 = alloc_page()) != NULL);
assert(alloc_page() == NULL);
free_page(p0);
assert(!list_empty(&free_list));
struct Page *p;
assert((p = alloc_page()) == p0);
assert(alloc_page() == NULL);
assert(nr_free == 0);
free_list = free_list_store;
nr_free = nr_free_store;
free_page(p);
free_page(p1);
free_page(p2);
}
// LAB2: below code is used to check the first fit allocation algorithm (your EXERCISE 1)
// NOTICE: You SHOULD NOT CHANGE basic_check, default_check functions!
static void
default_check(void) {
int count = 0, total = 0;
list_entry_t *le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
assert(PageProperty(p));
count ++, total += p->property;
}
assert(total == nr_free_pages());
basic_check();
struct Page *p0 = alloc_pages(5), *p1, *p2;
assert(p0 != NULL);
assert(!PageProperty(p0));
list_entry_t free_list_store = free_list;
list_init(&free_list);
assert(list_empty(&free_list));
assert(alloc_page() == NULL);
unsigned int nr_free_store = nr_free;
nr_free = 0;
free_pages(p0 + 2, 3);
assert(alloc_pages(4) == NULL);
assert(PageProperty(p0 + 2) && p0[2].property == 3);
assert((p1 = alloc_pages(3)) != NULL);
assert(alloc_page() == NULL);
assert(p0 + 2 == p1);
p2 = p0 + 1;
free_page(p0);
free_pages(p1, 3);
assert(PageProperty(p0) && p0->property == 1);
assert(PageProperty(p1) && p1->property == 3);
assert((p0 = alloc_page()) == p2 - 1);
free_page(p0);
assert((p0 = alloc_pages(2)) == p2 + 1);
free_pages(p0, 2);
free_page(p2);
assert((p0 = alloc_pages(5)) != NULL);
assert(alloc_page() == NULL);
assert(nr_free == 0);
nr_free = nr_free_store;
free_list = free_list_store;
free_pages(p0, 5);
le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
count --, total -= p->property;
}
assert(count == 0);
assert(total == 0);
}
const struct pmm_manager default_pmm_manager = {
.name = "default_pmm_manager",
.init = default_init,
.init_memmap = default_init_memmap,
.alloc_pages = default_alloc_pages,
.free_pages = default_free_pages,
.nr_free_pages = default_nr_free_pages,
.check = default_check,
};

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;
extern free_area_t free_area;
#endif /* ! __KERN_MM_DEFAULT_PMM_H__ */

View File

@@ -0,0 +1,310 @@
#include <defs.h>
#include <list.h>
#include <memlayout.h>
#include <assert.h>
#include <kmalloc.h>
#include <sync.h>
#include <pmm.h>
#include <stdio.h>
/*
* SLOB Allocator: Simple List Of Blocks
*
* Matt Mackall <mpm@selenic.com> 12/30/03
*
* How SLOB works:
*
* The core of SLOB is a traditional K&R style heap allocator, with
* support for returning aligned objects. The granularity of this
* allocator is 8 bytes on x86, though it's perhaps possible to reduce
* this to 4 if it's deemed worth the effort. The slob heap is a
* singly-linked list of pages from __get_free_page, grown on demand
* and allocation from the heap is currently first-fit.
*
* Above this is an implementation of kmalloc/kfree. Blocks returned
* from kmalloc are 8-byte aligned and prepended with a 8-byte header.
* If kmalloc is asked for objects of PAGE_SIZE or larger, it calls
* __get_free_pages directly so that it can return page-aligned blocks
* and keeps a linked list of such pages and their orders. These
* objects are detected in kfree() by their page alignment.
*
* SLAB is emulated on top of SLOB by simply calling constructors and
* destructors for every SLAB allocation. Objects are returned with
* the 8-byte alignment unless the SLAB_MUST_HWCACHE_ALIGN flag is
* set, in which case the low-level allocator will fragment blocks to
* create the proper alignment. Again, objects of page-size or greater
* are allocated by calling __get_free_pages. As SLAB objects know
* their size, no separate size bookkeeping is necessary and there is
* essentially no allocation space overhead.
*/
//some helper
#define spin_lock_irqsave(l, f) local_intr_save(f)
#define spin_unlock_irqrestore(l, f) local_intr_restore(f)
typedef unsigned int gfp_t;
#ifndef PAGE_SIZE
#define PAGE_SIZE PGSIZE
#endif
#ifndef L1_CACHE_BYTES
#define L1_CACHE_BYTES 64
#endif
#ifndef ALIGN
#define ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1)))
#endif
struct slob_block {
int units;
struct slob_block *next;
};
typedef struct slob_block slob_t;
#define SLOB_UNIT sizeof(slob_t)
#define SLOB_UNITS(size) (((size) + SLOB_UNIT - 1)/SLOB_UNIT)
#define SLOB_ALIGN L1_CACHE_BYTES
struct bigblock {
int order;
void *pages;
struct bigblock *next;
};
typedef struct bigblock bigblock_t;
static slob_t arena = { .next = &arena, .units = 1 };
static slob_t *slobfree = &arena;
static bigblock_t *bigblocks;
static void* __slob_get_free_pages(gfp_t gfp, int order)
{
struct Page * page = alloc_pages(1 << order);
if(!page)
return NULL;
return page2kva(page);
}
#define __slob_get_free_page(gfp) __slob_get_free_pages(gfp, 0)
static inline void __slob_free_pages(unsigned long kva, int order)
{
free_pages(kva2page(kva), 1 << order);
}
static void slob_free(void *b, int size);
static void *slob_alloc(size_t size, gfp_t gfp, int align)
{
assert( (size + SLOB_UNIT) < PAGE_SIZE );
slob_t *prev, *cur, *aligned = 0;
int delta = 0, units = SLOB_UNITS(size);
unsigned long flags;
spin_lock_irqsave(&slob_lock, flags);
prev = slobfree;
for (cur = prev->next; ; prev = cur, cur = cur->next) {
if (align) {
aligned = (slob_t *)ALIGN((unsigned long)cur, align);
delta = aligned - cur;
}
if (cur->units >= units + delta) { /* room enough? */
if (delta) { /* need to fragment head to align? */
aligned->units = cur->units - delta;
aligned->next = cur->next;
cur->next = aligned;
cur->units = delta;
prev = cur;
cur = aligned;
}
if (cur->units == units) /* exact fit? */
prev->next = cur->next; /* unlink */
else { /* fragment */
prev->next = cur + units;
prev->next->units = cur->units - units;
prev->next->next = cur->next;
cur->units = units;
}
slobfree = prev;
spin_unlock_irqrestore(&slob_lock, flags);
return cur;
}
if (cur == slobfree) {
spin_unlock_irqrestore(&slob_lock, flags);
if (size == PAGE_SIZE) /* trying to shrink arena? */
return 0;
cur = (slob_t *)__slob_get_free_page(gfp);
if (!cur)
return 0;
slob_free(cur, PAGE_SIZE);
spin_lock_irqsave(&slob_lock, flags);
cur = slobfree;
}
}
}
static void slob_free(void *block, int size)
{
slob_t *cur, *b = (slob_t *)block;
unsigned long flags;
if (!block)
return;
if (size)
b->units = SLOB_UNITS(size);
/* Find reinsertion point */
spin_lock_irqsave(&slob_lock, flags);
for (cur = slobfree; !(b > cur && b < cur->next); cur = cur->next)
if (cur >= cur->next && (b > cur || b < cur->next))
break;
if (b + b->units == cur->next) {
b->units += cur->next->units;
b->next = cur->next->next;
} else
b->next = cur->next;
if (cur + cur->units == b) {
cur->units += b->units;
cur->next = b->next;
} else
cur->next = b;
slobfree = cur;
spin_unlock_irqrestore(&slob_lock, flags);
}
void check_slab(void) {
cprintf("check_slab() success\n");
}
void
slab_init(void) {
cprintf("use SLOB allocator\n");
check_slab();
}
inline void
kmalloc_init(void) {
slab_init();
cprintf("kmalloc_init() succeeded!\n");
}
size_t
slab_allocated(void) {
return 0;
}
size_t
kallocated(void) {
return slab_allocated();
}
static int find_order(int size)
{
int order = 0;
for ( ; size > 4096 ; size >>=1)
order++;
return order;
}
static void *__kmalloc(size_t size, gfp_t gfp)
{
slob_t *m;
bigblock_t *bb;
unsigned long flags;
if (size < PAGE_SIZE - SLOB_UNIT) {
m = slob_alloc(size + SLOB_UNIT, gfp, 0);
return m ? (void *)(m + 1) : 0;
}
bb = slob_alloc(sizeof(bigblock_t), gfp, 0);
if (!bb)
return 0;
bb->order = find_order(size);
bb->pages = (void *)__slob_get_free_pages(gfp, bb->order);
if (bb->pages) {
spin_lock_irqsave(&block_lock, flags);
bb->next = bigblocks;
bigblocks = bb;
spin_unlock_irqrestore(&block_lock, flags);
return bb->pages;
}
slob_free(bb, sizeof(bigblock_t));
return 0;
}
void *
kmalloc(size_t size)
{
return __kmalloc(size, 0);
}
void kfree(void *block)
{
bigblock_t *bb, **last = &bigblocks;
unsigned long flags;
if (!block)
return;
if (!((unsigned long)block & (PAGE_SIZE-1))) {
/* might be on the big block list */
spin_lock_irqsave(&block_lock, flags);
for (bb = bigblocks; bb; last = &bb->next, bb = bb->next) {
if (bb->pages == block) {
*last = bb->next;
spin_unlock_irqrestore(&block_lock, flags);
__slob_free_pages((unsigned long)block, bb->order);
slob_free(bb, sizeof(bigblock_t));
return;
}
}
spin_unlock_irqrestore(&block_lock, flags);
}
slob_free((slob_t *)block - 1, 0);
return;
}
unsigned int ksize(const void *block)
{
bigblock_t *bb;
unsigned long flags;
if (!block)
return 0;
if (!((unsigned long)block & (PAGE_SIZE-1))) {
spin_lock_irqsave(&block_lock, flags);
for (bb = bigblocks; bb; bb = bb->next)
if (bb->pages == block) {
spin_unlock_irqrestore(&slob_lock, flags);
return PAGE_SIZE << bb->order;
}
spin_unlock_irqrestore(&block_lock, flags);
}
return ((slob_t *)block - 1)->units * SLOB_UNIT;
}

View File

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

View File

@@ -0,0 +1,169 @@
#ifndef __KERN_MM_MEMLAYOUT_H__
#define __KERN_MM_MEMLAYOUT_H__
/* This file contains the definitions for memory management in our OS. */
/* global segment number */
#define SEG_KTEXT 1
#define SEG_KDATA 2
#define SEG_UTEXT 3
#define SEG_UDATA 4
#define SEG_TSS 5
/* global descrptor numbers */
#define GD_KTEXT ((SEG_KTEXT) << 3) // kernel text
#define GD_KDATA ((SEG_KDATA) << 3) // kernel data
#define GD_UTEXT ((SEG_UTEXT) << 3) // user text
#define GD_UDATA ((SEG_UDATA) << 3) // user data
#define GD_TSS ((SEG_TSS) << 3) // task segment selector
#define DPL_KERNEL (0)
#define DPL_USER (3)
#define KERNEL_CS ((GD_KTEXT) | DPL_KERNEL)
#define KERNEL_DS ((GD_KDATA) | DPL_KERNEL)
#define USER_CS ((GD_UTEXT) | DPL_USER)
#define USER_DS ((GD_UDATA) | DPL_USER)
/* *
* Virtual memory map: Permissions
* kernel/user
*
* 4G ------------------> +---------------------------------+
* | |
* | Empty Memory (*) |
* | |
* +---------------------------------+ 0xFB000000
* | Cur. Page Table (Kern, RW) | RW/-- PTSIZE
* VPT -----------------> +---------------------------------+ 0xFAC00000
* | Invalid Memory (*) | --/--
* KERNTOP -------------> +---------------------------------+ 0xF8000000
* | |
* | Remapped Physical Memory | RW/-- KMEMSIZE
* | |
* KERNBASE ------------> +---------------------------------+ 0xC0000000
* | Invalid Memory (*) | --/--
* USERTOP -------------> +---------------------------------+ 0xB0000000
* | User stack |
* +---------------------------------+
* | |
* : :
* | ~~~~~~~~~~~~~~~~ |
* : :
* | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* | User Program & Heap |
* UTEXT ---------------> +---------------------------------+ 0x00800000
* | Invalid Memory (*) | --/--
* | - - - - - - - - - - - - - - - |
* | User STAB Data (optional) |
* USERBASE, USTAB------> +---------------------------------+ 0x00200000
* | Invalid Memory (*) | --/--
* 0 -------------------> +---------------------------------+ 0x00000000
* (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped.
* "Empty Memory" is normally unmapped, but user programs may map pages
* there if desired.
*
* */
/* All physical memory mapped at this address */
#define KERNBASE 0xC0000000
#define KMEMSIZE 0x38000000 // the maximum amount of physical memory
#define KERNTOP (KERNBASE + KMEMSIZE)
/* *
* Virtual page table. Entry PDX[VPT] in the PD (Page Directory) contains
* a pointer to the page directory itself, thereby turning the PD into a page
* table, which maps all the PTEs (Page Table Entry) containing the page mappings
* for the entire virtual address space into that 4 Meg region starting at VPT.
* */
#define VPT 0xFAC00000
#define KSTACKPAGE 2 // # of pages in kernel stack
#define KSTACKSIZE (KSTACKPAGE * PGSIZE) // sizeof kernel stack
#define USERTOP 0xB0000000
#define USTACKTOP USERTOP
#define USTACKPAGE 256 // # of pages in user stack
#define USTACKSIZE (USTACKPAGE * PGSIZE) // sizeof user stack
#define USERBASE 0x00200000
#define UTEXT 0x00800000 // where user programs generally begin
#define USTAB USERBASE // the location of the user STABS data structure
#define USER_ACCESS(start, end) \
(USERBASE <= (start) && (start) < (end) && (end) <= USERTOP)
#define KERN_ACCESS(start, end) \
(KERNBASE <= (start) && (start) < (end) && (end) <= KERNTOP)
#ifndef __ASSEMBLER__
#include <defs.h>
#include <atomic.h>
#include <list.h>
typedef uintptr_t pte_t;
typedef uintptr_t pde_t;
typedef pte_t swap_entry_t; //the pte can also be a swap entry
// some constants for bios interrupt 15h AX = 0xE820
#define E820MAX 20 // number of entries in E820MAP
#define E820_ARM 1 // address range memory
#define E820_ARR 2 // address range reserved
struct e820map {
int nr_map;
struct {
uint64_t addr;
uint64_t size;
uint32_t type;
} __attribute__((packed)) map[E820MAX];
};
/* *
* struct Page - Page descriptor structures. Each Page describes one
* physical page. In kern/mm/pmm.h, you can find lots of useful functions
* that convert Page to other data types, such as phyical address.
* */
struct Page {
int ref; // page frame's reference counter
uint32_t flags; // array of flags that describe the status of the page frame
unsigned int property; // used in buddy system, stores the order (the X in 2^X) of the continuous memory block
int zone_num; // used in buddy system, the No. of zone which the page belongs to
list_entry_t page_link; // free list link
list_entry_t pra_page_link; // used for pra (page replace algorithm)
uintptr_t pra_vaddr; // used for pra (page replace algorithm)
};
/* Flags describing the status of a page frame */
#define PG_reserved 0 // the page descriptor is reserved for kernel or unusable
#define PG_property 1 // the member 'property' is valid
#define SetPageReserved(page) set_bit(PG_reserved, &((page)->flags))
#define ClearPageReserved(page) clear_bit(PG_reserved, &((page)->flags))
#define PageReserved(page) test_bit(PG_reserved, &((page)->flags))
#define SetPageProperty(page) set_bit(PG_property, &((page)->flags))
#define ClearPageProperty(page) clear_bit(PG_property, &((page)->flags))
#define PageProperty(page) test_bit(PG_property, &((page)->flags))
// convert list entry to page
#define le2page(le, member) \
to_struct((le), struct Page, member)
/* free_area_t - maintains a doubly linked list to record free (unused) pages */
typedef struct {
list_entry_t free_list; // the list header
unsigned int nr_free; // # of free pages in this free list
} free_area_t;
/* for slab style kmalloc */
//#define PG_slab 2 // page frame is included in a slab
//#define SetPageSlab(page) set_bit(PG_slab, &((page)->flags))
//#define ClearPageSlab(page) clear_bit(PG_slab, &((page)->flags))
//#define PageSlab(page) test_bit(PG_slab, &((page)->flags))
#endif /* !__ASSEMBLER__ */
#endif /* !__KERN_MM_MEMLAYOUT_H__ */

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__ */

View File

@@ -0,0 +1,785 @@
#include <defs.h>
#include <x86.h>
#include <stdio.h>
#include <string.h>
#include <mmu.h>
#include <memlayout.h>
#include <pmm.h>
#include <default_pmm.h>
#include <sync.h>
#include <error.h>
#include <swap.h>
#include <vmm.h>
#include <kmalloc.h>
/* *
* Task State Segment:
*
* The TSS may reside anywhere in memory. A special segment register called
* the Task Register (TR) holds a segment selector that points a valid TSS
* segment descriptor which resides in the GDT. Therefore, to use a TSS
* the following must be done in function gdt_init:
* - create a TSS descriptor entry in GDT
* - add enough information to the TSS in memory as needed
* - load the TR register with a segment selector for that segment
*
* There are several fileds in TSS for specifying the new stack pointer when a
* privilege level change happens. But only the fields SS0 and ESP0 are useful
* in our os kernel.
*
* The field SS0 contains the stack segment selector for CPL = 0, and the ESP0
* contains the new ESP value for CPL = 0. When an interrupt happens in protected
* mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their value
* into SS and ESP respectively.
* */
static struct taskstate ts = {0};
// virtual address of physicall page array
struct Page *pages;
// amount of physical memory (in pages)
size_t npage = 0;
// virtual address of boot-time page directory
pde_t *boot_pgdir = NULL;
// physical address of boot-time page directory
uintptr_t boot_cr3;
// physical memory management
const struct pmm_manager *pmm_manager;
/* *
* The page directory entry corresponding to the virtual address range
* [VPT, VPT + PTSIZE) points to the page directory itself. Thus, the page
* directory is treated as a page table as well as a page directory.
*
* One result of treating the page directory as a page table is that all PTEs
* can be accessed though a "virtual page table" at virtual address VPT. And the
* PTE for number n is stored in vpt[n].
*
* A second consequence is that the contents of the current page directory will
* always available at virtual address PGADDR(PDX(VPT), PDX(VPT), 0), to which
* vpd is set bellow.
* */
pte_t * const vpt = (pte_t *)VPT;
pde_t * const vpd = (pde_t *)PGADDR(PDX(VPT), PDX(VPT), 0);
/* *
* Global Descriptor Table:
*
* The kernel and user segments are identical (except for the DPL). To load
* the %ss register, the CPL must equal the DPL. Thus, we must duplicate the
* segments for the user and the kernel. Defined as follows:
* - 0x0 : unused (always faults -- for trapping NULL far pointers)
* - 0x8 : kernel code segment
* - 0x10: kernel data segment
* - 0x18: user code segment
* - 0x20: user data segment
* - 0x28: defined for tss, initialized in gdt_init
* */
static struct segdesc gdt[] = {
SEG_NULL,
[SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL),
[SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL),
[SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER),
[SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER),
[SEG_TSS] = SEG_NULL,
};
static struct pseudodesc gdt_pd = {
sizeof(gdt) - 1, (uintptr_t)gdt
};
static void check_alloc_page(void);
static void check_pgdir(void);
static void check_boot_pgdir(void);
/* *
* lgdt - load the global descriptor table register and reset the
* data/code segement registers for kernel.
* */
static inline void
lgdt(struct pseudodesc *pd) {
asm volatile ("lgdt (%0)" :: "r" (pd));
asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS));
asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS));
asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS));
asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS));
asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS));
// reload cs
asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS));
}
/* *
* load_esp0 - change the ESP0 in default task state segment,
* so that we can use different kernel stack when we trap frame
* user to kernel.
* */
void
load_esp0(uintptr_t esp0) {
ts.ts_esp0 = esp0;
}
/* gdt_init - initialize the default GDT and TSS */
static void
gdt_init(void) {
// set boot kernel stack and default SS0
load_esp0((uintptr_t)bootstacktop);
ts.ts_ss0 = KERNEL_DS;
// initialize the TSS filed of the gdt
gdt[SEG_TSS] = SEGTSS(STS_T32A, (uintptr_t)&ts, sizeof(ts), DPL_KERNEL);
// reload all segment registers
lgdt(&gdt_pd);
// load the TSS
ltr(GD_TSS);
}
//init_pmm_manager - initialize a pmm_manager instance
static void
init_pmm_manager(void) {
pmm_manager = &default_pmm_manager;
cprintf("memory management: %s\n", pmm_manager->name);
pmm_manager->init();
}
//init_memmap - call pmm->init_memmap to build Page struct for free memory
static void
init_memmap(struct Page *base, size_t n) {
pmm_manager->init_memmap(base, n);
}
//alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE memory
struct Page *
alloc_pages(size_t n) {
struct Page *page=NULL;
bool intr_flag;
while (1)
{
local_intr_save(intr_flag);
{
page = pmm_manager->alloc_pages(n);
}
local_intr_restore(intr_flag);
if (page != NULL || n > 1 || swap_init_ok == 0) break;
extern struct mm_struct *check_mm_struct;
//cprintf("page %x, call swap_out in alloc_pages %d\n",page, n);
swap_out(check_mm_struct, n, 0);
}
//cprintf("n %d,get page %x, No %d in alloc_pages\n",n,page,(page-pages));
return page;
}
//free_pages - call pmm->free_pages to free a continuous n*PAGESIZE memory
void
free_pages(struct Page *base, size_t n) {
bool intr_flag;
local_intr_save(intr_flag);
{
pmm_manager->free_pages(base, n);
}
local_intr_restore(intr_flag);
}
//nr_free_pages - call pmm->nr_free_pages to get the size (nr*PAGESIZE)
//of current free memory
size_t
nr_free_pages(void) {
size_t ret;
bool intr_flag;
local_intr_save(intr_flag);
{
ret = pmm_manager->nr_free_pages();
}
local_intr_restore(intr_flag);
return ret;
}
/* pmm_init - initialize the physical memory management */
static void
page_init(void) {
struct e820map *memmap = (struct e820map *)(0x8000 + KERNBASE);
uint64_t maxpa = 0;
cprintf("e820map:\n");
int i;
for (i = 0; i < memmap->nr_map; i ++) {
uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;
cprintf(" memory: %08llx, [%08llx, %08llx], type = %d.\n",
memmap->map[i].size, begin, end - 1, memmap->map[i].type);
if (memmap->map[i].type == E820_ARM) {
if (maxpa < end && begin < KMEMSIZE) {
maxpa = end;
}
}
}
if (maxpa > KMEMSIZE) {
maxpa = KMEMSIZE;
}
extern char end[];
npage = maxpa / PGSIZE;
pages = (struct Page *)ROUNDUP((void *)end, PGSIZE);
for (i = 0; i < npage; i ++) {
SetPageReserved(pages + i);
}
uintptr_t freemem = PADDR((uintptr_t)pages + sizeof(struct Page) * npage);
for (i = 0; i < memmap->nr_map; i ++) {
uint64_t begin = memmap->map[i].addr, end = begin + memmap->map[i].size;
if (memmap->map[i].type == E820_ARM) {
if (begin < freemem) {
begin = freemem;
}
if (end > KMEMSIZE) {
end = KMEMSIZE;
}
if (begin < end) {
begin = ROUNDUP(begin, PGSIZE);
end = ROUNDDOWN(end, PGSIZE);
if (begin < end) {
init_memmap(pa2page(begin), (end - begin) / PGSIZE);
}
}
}
}
}
static void
enable_paging(void) {
lcr3(boot_cr3);
// turn on paging
uint32_t cr0 = rcr0();
cr0 |= CR0_PE | CR0_PG | CR0_AM | CR0_WP | CR0_NE | CR0_TS | CR0_EM | CR0_MP;
cr0 &= ~(CR0_TS | CR0_EM);
lcr0(cr0);
}
//boot_map_segment - setup&enable the paging mechanism
// parameters
// la: linear address of this memory need to map (after x86 segment map)
// size: memory size
// pa: physical address of this memory
// perm: permission of this memory
static void
boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, uintptr_t pa, uint32_t perm) {
assert(PGOFF(la) == PGOFF(pa));
size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE;
la = ROUNDDOWN(la, PGSIZE);
pa = ROUNDDOWN(pa, PGSIZE);
for (; n > 0; n --, la += PGSIZE, pa += PGSIZE) {
pte_t *ptep = get_pte(pgdir, la, 1);
assert(ptep != NULL);
*ptep = pa | PTE_P | perm;
}
}
//boot_alloc_page - allocate one page using pmm->alloc_pages(1)
// return value: the kernel virtual address of this allocated page
//note: this function is used to get the memory for PDT(Page Directory Table)&PT(Page Table)
static void *
boot_alloc_page(void) {
struct Page *p = alloc_page();
if (p == NULL) {
panic("boot_alloc_page failed.\n");
}
return page2kva(p);
}
//pmm_init - setup a pmm to manage physical memory, build PDT&PT to setup paging mechanism
// - check the correctness of pmm & paging mechanism, print PDT&PT
void
pmm_init(void) {
//We need to alloc/free the physical memory (granularity is 4KB or other size).
//So a framework of physical memory manager (struct pmm_manager)is defined in pmm.h
//First we should init a physical memory manager(pmm) based on the framework.
//Then pmm can alloc/free the physical memory.
//Now the first_fit/best_fit/worst_fit/buddy_system pmm are available.
init_pmm_manager();
// detect physical memory space, reserve already used memory,
// then use pmm->init_memmap to create free page list
page_init();
//use pmm->check to verify the correctness of the alloc/free function in a pmm
check_alloc_page();
// create boot_pgdir, an initial page directory(Page Directory Table, PDT)
boot_pgdir = boot_alloc_page();
memset(boot_pgdir, 0, PGSIZE);
boot_cr3 = PADDR(boot_pgdir);
check_pgdir();
static_assert(KERNBASE % PTSIZE == 0 && KERNTOP % PTSIZE == 0);
// recursively insert boot_pgdir in itself
// to form a virtual page table at virtual address VPT
boot_pgdir[PDX(VPT)] = PADDR(boot_pgdir) | PTE_P | PTE_W;
// map all physical memory to linear memory with base linear addr KERNBASE
//linear_addr KERNBASE~KERNBASE+KMEMSIZE = phy_addr 0~KMEMSIZE
//But shouldn't use this map until enable_paging() & gdt_init() finished.
boot_map_segment(boot_pgdir, KERNBASE, KMEMSIZE, 0, PTE_W);
//temporary map:
//virtual_addr 3G~3G+4M = linear_addr 0~4M = linear_addr 3G~3G+4M = phy_addr 0~4M
boot_pgdir[0] = boot_pgdir[PDX(KERNBASE)];
enable_paging();
//reload gdt(third time,the last time) to map all physical memory
//virtual_addr 0~4G=liear_addr 0~4G
//then set kernel stack(ss:esp) in TSS, setup TSS in gdt, load TSS
gdt_init();
//disable the map of virtual_addr 0~4M
boot_pgdir[0] = 0;
//now the basic virtual memory map(see memalyout.h) is established.
//check the correctness of the basic virtual memory map.
check_boot_pgdir();
print_pgdir();
kmalloc_init();
}
//get_pte - get pte and return the kernel virtual address of this pte for la
// - if the PT contians this pte didn't exist, alloc a page for PT
// parameter:
// pgdir: the kernel virtual base address of PDT
// la: the linear address need to map
// create: a logical value to decide if alloc a page for PT
// return vaule: the kernel virtual address of this pte
pte_t *
get_pte(pde_t *pgdir, uintptr_t la, bool create) {
/* LAB2 EXERCISE 2: YOUR CODE
*
* If you need to visit a physical address, please use KADDR()
* please read pmm.h for useful macros
*
* Maybe you want help comment, BELOW comments can help you finish the code
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* PDX(la) = the index of page directory entry of VIRTUAL ADDRESS la.
* KADDR(pa) : takes a physical address and returns the corresponding kernel virtual address.
* set_page_ref(page,1) : means the page be referenced by one time
* page2pa(page): get the physical address of memory which this (struct Page *) page manages
* struct Page * alloc_page() : allocation a page
* memset(void *s, char c, size_t n) : sets the first n bytes of the memory area pointed by s
* to the specified value c.
* DEFINEs:
* PTE_P 0x001 // page table/directory entry flags bit : Present
* PTE_W 0x002 // page table/directory entry flags bit : Writeable
* PTE_U 0x004 // page table/directory entry flags bit : User can access
*/
#if 0
pde_t *pdep = NULL; // (1) find page directory entry
if (0) { // (2) check if entry is not present
// (3) check if creating is needed, then alloc page for page table
// CAUTION: this page is used for page table, not for common data page
// (4) set page reference
uintptr_t pa = 0; // (5) get linear address of page
// (6) clear page content using memset
// (7) set page directory entry's permission
}
return NULL; // (8) return page table entry
#endif
pde_t *pdep = &pgdir[PDX(la)];
if (!(*pdep & PTE_P)) {
struct Page *page;
if (!create || (page = alloc_page()) == NULL) {
return NULL;
}
set_page_ref(page, 1);
uintptr_t pa = page2pa(page);
memset(KADDR(pa), 0, PGSIZE);
*pdep = pa | PTE_U | PTE_W | PTE_P;
}
return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)];
}
//get_page - get related Page struct for linear address la using PDT pgdir
struct Page *
get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store) {
pte_t *ptep = get_pte(pgdir, la, 0);
if (ptep_store != NULL) {
*ptep_store = ptep;
}
if (ptep != NULL && *ptep & PTE_P) {
return pa2page(*ptep);
}
return NULL;
}
//page_remove_pte - free an Page sturct which is related linear address la
// - and clean(invalidate) pte which is related linear address la
//note: PT is changed, so the TLB need to be invalidate
static inline void
page_remove_pte(pde_t *pgdir, uintptr_t la, pte_t *ptep) {
/* LAB2 EXERCISE 3: YOUR CODE
*
* Please check if ptep is valid, and tlb must be manually updated if mapping is updated
*
* Maybe you want help comment, BELOW comments can help you finish the code
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* struct Page *page pte2page(*ptep): get the according page from the value of a ptep
* free_page : free a page
* page_ref_dec(page) : decrease page->ref. NOTICE: ff page->ref == 0 , then this page should be free.
* tlb_invalidate(pde_t *pgdir, uintptr_t la) : Invalidate a TLB entry, but only if the page tables being
* edited are the ones currently in use by the processor.
* DEFINEs:
* PTE_P 0x001 // page table/directory entry flags bit : Present
*/
#if 0
if (0) { //(1) check if page directory is present
struct Page *page = NULL; //(2) find corresponding page to pte
//(3) decrease page reference
//(4) and free this page when page reference reachs 0
//(5) clear second page table entry
//(6) flush tlb
}
#endif
if (*ptep & PTE_P) {
struct Page *page = pte2page(*ptep);
if (page_ref_dec(page) == 0) {
free_page(page);
}
*ptep = 0;
tlb_invalidate(pgdir, la);
}
}
void
unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end) {
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
assert(USER_ACCESS(start, end));
do {
pte_t *ptep = get_pte(pgdir, start, 0);
if (ptep == NULL) {
start = ROUNDDOWN(start + PTSIZE, PTSIZE);
continue ;
}
if (*ptep != 0) {
page_remove_pte(pgdir, start, ptep);
}
start += PGSIZE;
} while (start != 0 && start < end);
}
void
exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end) {
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
assert(USER_ACCESS(start, end));
start = ROUNDDOWN(start, PTSIZE);
do {
int pde_idx = PDX(start);
if (pgdir[pde_idx] & PTE_P) {
free_page(pde2page(pgdir[pde_idx]));
pgdir[pde_idx] = 0;
}
start += PTSIZE;
} while (start != 0 && start < end);
}
/* copy_range - copy content of memory (start, end) of one process A to another process B
* @to: the addr of process B's Page Directory
* @from: the addr of process A's Page Directory
* @share: flags to indicate to dup OR share. We just use dup method, so it didn't be used.
*
* CALL GRAPH: copy_mm-->dup_mmap-->copy_range
*/
int
copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share) {
assert(start % PGSIZE == 0 && end % PGSIZE == 0);
assert(USER_ACCESS(start, end));
// copy content by page unit.
do {
//call get_pte to find process A's pte according to the addr start
pte_t *ptep = get_pte(from, start, 0), *nptep;
if (ptep == NULL) {
start = ROUNDDOWN(start + PTSIZE, PTSIZE);
continue ;
}
//call get_pte to find process B's pte according to the addr start. If pte is NULL, just alloc a PT
if (*ptep & PTE_P) {
if ((nptep = get_pte(to, start, 1)) == NULL) {
return -E_NO_MEM;
}
uint32_t perm = (*ptep & PTE_USER);
//get page from ptep
struct Page *page = pte2page(*ptep);
// alloc a page for process B
struct Page *npage=alloc_page();
assert(page!=NULL);
assert(npage!=NULL);
int ret=0;
/* LAB5:EXERCISE2 YOUR CODE
* replicate content of page to npage, build the map of phy addr of nage with the linear addr start
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* page2kva(struct Page *page): return the kernel vritual addr of memory which page managed (SEE pmm.h)
* page_insert: build the map of phy addr of an Page with the linear addr la
* memcpy: typical memory copy function
*
* (1) find src_kvaddr: the kernel virtual address of page
* (2) find dst_kvaddr: the kernel virtual address of npage
* (3) memory copy from src_kvaddr to dst_kvaddr, size is PGSIZE
* (4) build the map of phy addr of nage with the linear addr start
*/
void * kva_src = page2kva(page);
void * kva_dst = page2kva(npage);
memcpy(kva_dst, kva_src, PGSIZE);
ret = page_insert(to, npage, start, perm);
assert(ret == 0);
}
start += PGSIZE;
} while (start != 0 && start < end);
return 0;
}
//page_remove - free an Page which is related linear address la and has an validated pte
void
page_remove(pde_t *pgdir, uintptr_t la) {
pte_t *ptep = get_pte(pgdir, la, 0);
if (ptep != NULL) {
page_remove_pte(pgdir, la, ptep);
}
}
//page_insert - build the map of phy addr of an Page with the linear addr la
// paramemters:
// pgdir: the kernel virtual base address of PDT
// page: the Page which need to map
// la: the linear address need to map
// perm: the permission of this Page which is setted in related pte
// return value: always 0
//note: PT is changed, so the TLB need to be invalidate
int
page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm) {
pte_t *ptep = get_pte(pgdir, la, 1);
if (ptep == NULL) {
return -E_NO_MEM;
}
page_ref_inc(page);
if (*ptep & PTE_P) {
struct Page *p = pte2page(*ptep);
if (p == page) {
page_ref_dec(page);
}
else {
page_remove_pte(pgdir, la, ptep);
}
}
*ptep = page2pa(page) | PTE_P | perm;
tlb_invalidate(pgdir, la);
return 0;
}
// invalidate a TLB entry, but only if the page tables being
// edited are the ones currently in use by the processor.
void
tlb_invalidate(pde_t *pgdir, uintptr_t la) {
if (rcr3() == PADDR(pgdir)) {
invlpg((void *)la);
}
}
// pgdir_alloc_page - call alloc_page & page_insert functions to
// - allocate a page size memory & setup an addr map
// - pa<->la with linear address la and the PDT pgdir
struct Page *
pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm) {
struct Page *page = alloc_page();
if (page != NULL) {
if (page_insert(pgdir, page, la, perm) != 0) {
free_page(page);
return NULL;
}
if (swap_init_ok){
if(check_mm_struct!=NULL) {
swap_map_swappable(check_mm_struct, la, page, 0);
page->pra_vaddr=la;
assert(page_ref(page) == 1);
//cprintf("get No. %d page: pra_vaddr %x, pra_link.prev %x, pra_link_next %x in pgdir_alloc_page\n", (page-pages), page->pra_vaddr,page->pra_page_link.prev, page->pra_page_link.next);
}
else { //now current is existed, should fix it in the future
//swap_map_swappable(current->mm, la, page, 0);
//page->pra_vaddr=la;
//assert(page_ref(page) == 1);
//panic("pgdir_alloc_page: no pages. now current is existed, should fix it in the future\n");
}
}
}
return page;
}
static void
check_alloc_page(void) {
pmm_manager->check();
cprintf("check_alloc_page() succeeded!\n");
}
static void
check_pgdir(void) {
assert(npage <= KMEMSIZE / PGSIZE);
assert(boot_pgdir != NULL && (uint32_t)PGOFF(boot_pgdir) == 0);
assert(get_page(boot_pgdir, 0x0, NULL) == NULL);
struct Page *p1, *p2;
p1 = alloc_page();
assert(page_insert(boot_pgdir, p1, 0x0, 0) == 0);
pte_t *ptep;
assert((ptep = get_pte(boot_pgdir, 0x0, 0)) != NULL);
assert(pa2page(*ptep) == p1);
assert(page_ref(p1) == 1);
ptep = &((pte_t *)KADDR(PDE_ADDR(boot_pgdir[0])))[1];
assert(get_pte(boot_pgdir, PGSIZE, 0) == ptep);
p2 = alloc_page();
assert(page_insert(boot_pgdir, p2, PGSIZE, PTE_U | PTE_W) == 0);
assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL);
assert(*ptep & PTE_U);
assert(*ptep & PTE_W);
assert(boot_pgdir[0] & PTE_U);
assert(page_ref(p2) == 1);
assert(page_insert(boot_pgdir, p1, PGSIZE, 0) == 0);
assert(page_ref(p1) == 2);
assert(page_ref(p2) == 0);
assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL);
assert(pa2page(*ptep) == p1);
assert((*ptep & PTE_U) == 0);
page_remove(boot_pgdir, 0x0);
assert(page_ref(p1) == 1);
assert(page_ref(p2) == 0);
page_remove(boot_pgdir, PGSIZE);
assert(page_ref(p1) == 0);
assert(page_ref(p2) == 0);
assert(page_ref(pa2page(boot_pgdir[0])) == 1);
free_page(pa2page(boot_pgdir[0]));
boot_pgdir[0] = 0;
cprintf("check_pgdir() succeeded!\n");
}
static void
check_boot_pgdir(void) {
pte_t *ptep;
int i;
for (i = 0; i < npage; i += PGSIZE) {
assert((ptep = get_pte(boot_pgdir, (uintptr_t)KADDR(i), 0)) != NULL);
assert(PTE_ADDR(*ptep) == i);
}
assert(PDE_ADDR(boot_pgdir[PDX(VPT)]) == PADDR(boot_pgdir));
assert(boot_pgdir[0] == 0);
struct Page *p;
p = alloc_page();
assert(page_insert(boot_pgdir, p, 0x100, PTE_W) == 0);
assert(page_ref(p) == 1);
assert(page_insert(boot_pgdir, p, 0x100 + PGSIZE, PTE_W) == 0);
assert(page_ref(p) == 2);
const char *str = "ucore: Hello world!!";
strcpy((void *)0x100, str);
assert(strcmp((void *)0x100, (void *)(0x100 + PGSIZE)) == 0);
*(char *)(page2kva(p) + 0x100) = '\0';
assert(strlen((const char *)0x100) == 0);
free_page(p);
free_page(pa2page(PDE_ADDR(boot_pgdir[0])));
boot_pgdir[0] = 0;
cprintf("check_boot_pgdir() succeeded!\n");
}
//perm2str - use string 'u,r,w,-' to present the permission
static const char *
perm2str(int perm) {
static char str[4];
str[0] = (perm & PTE_U) ? 'u' : '-';
str[1] = 'r';
str[2] = (perm & PTE_W) ? 'w' : '-';
str[3] = '\0';
return str;
}
//get_pgtable_items - In [left, right] range of PDT or PT, find a continuous linear addr space
// - (left_store*X_SIZE~right_store*X_SIZE) for PDT or PT
// - X_SIZE=PTSIZE=4M, if PDT; X_SIZE=PGSIZE=4K, if PT
// paramemters:
// left: no use ???
// right: the high side of table's range
// start: the low side of table's range
// table: the beginning addr of table
// left_store: the pointer of the high side of table's next range
// right_store: the pointer of the low side of table's next range
// return value: 0 - not a invalid item range, perm - a valid item range with perm permission
static int
get_pgtable_items(size_t left, size_t right, size_t start, uintptr_t *table, size_t *left_store, size_t *right_store) {
if (start >= right) {
return 0;
}
while (start < right && !(table[start] & PTE_P)) {
start ++;
}
if (start < right) {
if (left_store != NULL) {
*left_store = start;
}
int perm = (table[start ++] & PTE_USER);
while (start < right && (table[start] & PTE_USER) == perm) {
start ++;
}
if (right_store != NULL) {
*right_store = start;
}
return perm;
}
return 0;
}
//print_pgdir - print the PDT&PT
void
print_pgdir(void) {
cprintf("-------------------- BEGIN --------------------\n");
size_t left, right = 0, perm;
while ((perm = get_pgtable_items(0, NPDEENTRY, right, vpd, &left, &right)) != 0) {
cprintf("PDE(%03x) %08x-%08x %08x %s\n", right - left,
left * PTSIZE, right * PTSIZE, (right - left) * PTSIZE, perm2str(perm));
size_t l, r = left * NPTEENTRY;
while ((perm = get_pgtable_items(left * NPTEENTRY, right * NPTEENTRY, r, vpt, &l, &r)) != 0) {
cprintf(" |-- PTE(%05x) %08x-%08x %08x %s\n", r - l,
l * PGSIZE, r * PGSIZE, (r - l) * PGSIZE, perm2str(perm));
}
}
cprintf("--------------------- END ---------------------\n");
}

View File

@@ -0,0 +1,147 @@
#ifndef __KERN_MM_PMM_H__
#define __KERN_MM_PMM_H__
#include <defs.h>
#include <mmu.h>
#include <memlayout.h>
#include <atomic.h>
#include <assert.h>
// pmm_manager is a physical memory management class. A special pmm manager - XXX_pmm_manager
// only needs to implement the methods in pmm_manager class, then XXX_pmm_manager can be used
// by ucore to manage the total physical memory space.
struct pmm_manager {
const char *name; // XXX_pmm_manager's name
void (*init)(void); // initialize internal description&management data structure
// (free block list, number of free block) of XXX_pmm_manager
void (*init_memmap)(struct Page *base, size_t n); // setup description&management data structcure according to
// the initial free physical memory space
struct Page *(*alloc_pages)(size_t n); // allocate >=n pages, depend on the allocation algorithm
void (*free_pages)(struct Page *base, size_t n); // free >=n pages with "base" addr of Page descriptor structures(memlayout.h)
size_t (*nr_free_pages)(void); // return the number of free pages
void (*check)(void); // check the correctness of XXX_pmm_manager
};
extern const struct pmm_manager *pmm_manager;
extern pde_t *boot_pgdir;
extern uintptr_t boot_cr3;
void pmm_init(void);
struct Page *alloc_pages(size_t n);
void free_pages(struct Page *base, size_t n);
size_t nr_free_pages(void);
#define alloc_page() alloc_pages(1)
#define free_page(page) free_pages(page, 1)
pte_t *get_pte(pde_t *pgdir, uintptr_t la, bool create);
struct Page *get_page(pde_t *pgdir, uintptr_t la, pte_t **ptep_store);
void page_remove(pde_t *pgdir, uintptr_t la);
int page_insert(pde_t *pgdir, struct Page *page, uintptr_t la, uint32_t perm);
void load_esp0(uintptr_t esp0);
void tlb_invalidate(pde_t *pgdir, uintptr_t la);
struct Page *pgdir_alloc_page(pde_t *pgdir, uintptr_t la, uint32_t perm);
void unmap_range(pde_t *pgdir, uintptr_t start, uintptr_t end);
void exit_range(pde_t *pgdir, uintptr_t start, uintptr_t end);
int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share);
void print_pgdir(void);
/* *
* PADDR - takes a kernel virtual address (an address that points above KERNBASE),
* where the machine's maximum 256MB of physical memory is mapped and returns the
* corresponding physical address. It panics if you pass it a non-kernel virtual address.
* */
#define PADDR(kva) ({ \
uintptr_t __m_kva = (uintptr_t)(kva); \
if (__m_kva < KERNBASE) { \
panic("PADDR called with invalid kva %08lx", __m_kva); \
} \
__m_kva - KERNBASE; \
})
/* *
* KADDR - takes a physical address and returns the corresponding kernel virtual
* address. It panics if you pass an invalid physical address.
* */
#define KADDR(pa) ({ \
uintptr_t __m_pa = (pa); \
size_t __m_ppn = PPN(__m_pa); \
if (__m_ppn >= npage) { \
panic("KADDR called with invalid pa %08lx", __m_pa); \
} \
(void *) (__m_pa + KERNBASE); \
})
extern struct Page *pages;
extern size_t npage;
static inline ppn_t
page2ppn(struct Page *page) {
return page - pages;
}
static inline uintptr_t
page2pa(struct Page *page) {
return page2ppn(page) << PGSHIFT;
}
static inline struct Page *
pa2page(uintptr_t pa) {
if (PPN(pa) >= npage) {
panic("pa2page called with invalid pa");
}
return &pages[PPN(pa)];
}
static inline void *
page2kva(struct Page *page) {
return KADDR(page2pa(page));
}
static inline struct Page *
kva2page(void *kva) {
return pa2page(PADDR(kva));
}
static inline struct Page *
pte2page(pte_t pte) {
if (!(pte & PTE_P)) {
panic("pte2page called with invalid pte");
}
return pa2page(PTE_ADDR(pte));
}
static inline struct Page *
pde2page(pde_t pde) {
return pa2page(PDE_ADDR(pde));
}
static inline int
page_ref(struct Page *page) {
return page->ref;
}
static inline void
set_page_ref(struct Page *page, int val) {
page->ref = val;
}
static inline int
page_ref_inc(struct Page *page) {
page->ref += 1;
return page->ref;
}
static inline int
page_ref_dec(struct Page *page) {
page->ref -= 1;
return page->ref;
}
extern char bootstack[], bootstacktop[];
#endif /* !__KERN_MM_PMM_H__ */

View File

@@ -0,0 +1,284 @@
#include <swap.h>
#include <swapfs.h>
#include <swap_fifo.h>
#include <stdio.h>
#include <string.h>
#include <memlayout.h>
#include <pmm.h>
#include <mmu.h>
#include <default_pmm.h>
#include <kdebug.h>
// the valid vaddr for check is between 0~CHECK_VALID_VADDR-1
#define CHECK_VALID_VIR_PAGE_NUM 5
#define BEING_CHECK_VALID_VADDR 0X1000
#define CHECK_VALID_VADDR (CHECK_VALID_VIR_PAGE_NUM+1)*0x1000
// the max number of valid physical page for check
#define CHECK_VALID_PHY_PAGE_NUM 4
// the max access seq number
#define MAX_SEQ_NO 10
static struct swap_manager *sm;
size_t max_swap_offset;
volatile int swap_init_ok = 0;
unsigned int swap_page[CHECK_VALID_VIR_PAGE_NUM];
unsigned int swap_in_seq_no[MAX_SEQ_NO],swap_out_seq_no[MAX_SEQ_NO];
static void check_swap(void);
int
swap_init(void)
{
swapfs_init();
if (!(1024 <= max_swap_offset && max_swap_offset < MAX_SWAP_OFFSET_LIMIT))
{
panic("bad max_swap_offset %08x.\n", max_swap_offset);
}
sm = &swap_manager_fifo;
int r = sm->init();
if (r == 0)
{
swap_init_ok = 1;
cprintf("SWAP: manager = %s\n", sm->name);
check_swap();
}
return r;
}
int
swap_init_mm(struct mm_struct *mm)
{
return sm->init_mm(mm);
}
int
swap_tick_event(struct mm_struct *mm)
{
return sm->tick_event(mm);
}
int
swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
{
return sm->map_swappable(mm, addr, page, swap_in);
}
int
swap_set_unswappable(struct mm_struct *mm, uintptr_t addr)
{
return sm->set_unswappable(mm, addr);
}
volatile unsigned int swap_out_num=0;
int
swap_out(struct mm_struct *mm, int n, int in_tick)
{
int i;
for (i = 0; i != n; ++ i)
{
uintptr_t v;
//struct Page **ptr_page=NULL;
struct Page *page;
// cprintf("i %d, SWAP: call swap_out_victim\n",i);
int r = sm->swap_out_victim(mm, &page, in_tick);
if (r != 0) {
cprintf("i %d, swap_out: call swap_out_victim failed\n",i);
break;
}
//assert(!PageReserved(page));
//cprintf("SWAP: choose victim page 0x%08x\n", page);
v=page->pra_vaddr;
pte_t *ptep = get_pte(mm->pgdir, v, 0);
assert((*ptep & PTE_P) != 0);
if (swapfs_write( (page->pra_vaddr/PGSIZE+1)<<8, page) != 0) {
cprintf("SWAP: failed to save\n");
sm->map_swappable(mm, v, page, 0);
continue;
}
else {
cprintf("swap_out: i %d, store page in vaddr 0x%x to disk swap entry %d\n", i, v, page->pra_vaddr/PGSIZE+1);
*ptep = (page->pra_vaddr/PGSIZE+1)<<8;
free_page(page);
}
tlb_invalidate(mm->pgdir, v);
}
return i;
}
int
swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result)
{
struct Page *result = alloc_page();
assert(result!=NULL);
pte_t *ptep = get_pte(mm->pgdir, addr, 0);
// cprintf("SWAP: load ptep %x swap entry %d to vaddr 0x%08x, page %x, No %d\n", ptep, (*ptep)>>8, addr, result, (result-pages));
int r;
if ((r = swapfs_read((*ptep), result)) != 0)
{
assert(r!=0);
}
cprintf("swap_in: load disk swap entry %d with swap_page in vadr 0x%x\n", (*ptep)>>8, addr);
*ptr_result=result;
return 0;
}
static inline void
check_content_set(void)
{
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==1);
*(unsigned char *)0x1010 = 0x0a;
assert(pgfault_num==1);
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==2);
*(unsigned char *)0x2010 = 0x0b;
assert(pgfault_num==2);
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==3);
*(unsigned char *)0x3010 = 0x0c;
assert(pgfault_num==3);
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==4);
*(unsigned char *)0x4010 = 0x0d;
assert(pgfault_num==4);
}
static inline int
check_content_access(void)
{
int ret = sm->check_swap();
return ret;
}
struct Page * check_rp[CHECK_VALID_PHY_PAGE_NUM];
pte_t * check_ptep[CHECK_VALID_PHY_PAGE_NUM];
unsigned int check_swap_addr[CHECK_VALID_VIR_PAGE_NUM];
extern free_area_t free_area;
#define free_list (free_area.free_list)
#define nr_free (free_area.nr_free)
static void
check_swap(void)
{
//backup mem env
int ret, count = 0, total = 0, i;
list_entry_t *le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
assert(PageProperty(p));
count ++, total += p->property;
}
assert(total == nr_free_pages());
cprintf("BEGIN check_swap: count %d, total %d\n",count,total);
//now we set the phy pages env
struct mm_struct *mm = mm_create();
assert(mm != NULL);
extern struct mm_struct *check_mm_struct;
assert(check_mm_struct == NULL);
check_mm_struct = mm;
pde_t *pgdir = mm->pgdir = boot_pgdir;
assert(pgdir[0] == 0);
struct vma_struct *vma = vma_create(BEING_CHECK_VALID_VADDR, CHECK_VALID_VADDR, VM_WRITE | VM_READ);
assert(vma != NULL);
insert_vma_struct(mm, vma);
//setup the temp Page Table vaddr 0~4MB
cprintf("setup Page Table for vaddr 0X1000, so alloc a page\n");
pte_t *temp_ptep=NULL;
temp_ptep = get_pte(mm->pgdir, BEING_CHECK_VALID_VADDR, 1);
assert(temp_ptep!= NULL);
cprintf("setup Page Table vaddr 0~4MB OVER!\n");
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
check_rp[i] = alloc_page();
assert(check_rp[i] != NULL );
assert(!PageProperty(check_rp[i]));
}
list_entry_t free_list_store = free_list;
list_init(&free_list);
assert(list_empty(&free_list));
//assert(alloc_page() == NULL);
unsigned int nr_free_store = nr_free;
nr_free = 0;
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
free_pages(check_rp[i],1);
}
assert(nr_free==CHECK_VALID_PHY_PAGE_NUM);
cprintf("set up init env for check_swap begin!\n");
//setup initial vir_page<->phy_page environment for page relpacement algorithm
pgfault_num=0;
check_content_set();
assert( nr_free == 0);
for(i = 0; i<MAX_SEQ_NO ; i++)
swap_out_seq_no[i]=swap_in_seq_no[i]=-1;
for (i= 0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
check_ptep[i]=0;
check_ptep[i] = get_pte(pgdir, (i+1)*0x1000, 0);
//cprintf("i %d, check_ptep addr %x, value %x\n", i, check_ptep[i], *check_ptep[i]);
assert(check_ptep[i] != NULL);
assert(pte2page(*check_ptep[i]) == check_rp[i]);
assert((*check_ptep[i] & PTE_P));
}
cprintf("set up init env for check_swap over!\n");
// now access the virt pages to test page relpacement algorithm
ret=check_content_access();
assert(ret==0);
//restore kernel mem env
for (i=0;i<CHECK_VALID_PHY_PAGE_NUM;i++) {
free_pages(check_rp[i],1);
}
//free_page(pte2page(*temp_ptep));
free_page(pa2page(pgdir[0]));
pgdir[0] = 0;
mm->pgdir = NULL;
mm_destroy(mm);
check_mm_struct = NULL;
nr_free = nr_free_store;
free_list = free_list_store;
le = &free_list;
while ((le = list_next(le)) != &free_list) {
struct Page *p = le2page(le, page_link);
count --, total -= p->property;
}
cprintf("count is %d, total is %d\n",count,total);
//assert(count == 0);
cprintf("check_swap() succeeded!\n");
}

View File

@@ -0,0 +1,65 @@
#ifndef __KERN_MM_SWAP_H__
#define __KERN_MM_SWAP_H__
#include <defs.h>
#include <memlayout.h>
#include <pmm.h>
#include <vmm.h>
/* *
* swap_entry_t
* --------------------------------------------
* | offset | reserved | 0 |
* --------------------------------------------
* 24 bits 7 bits 1 bit
* */
#define MAX_SWAP_OFFSET_LIMIT (1 << 24)
extern size_t max_swap_offset;
/* *
* swap_offset - takes a swap_entry (saved in pte), and returns
* the corresponding offset in swap mem_map.
* */
#define swap_offset(entry) ({ \
size_t __offset = (entry >> 8); \
if (!(__offset > 0 && __offset < max_swap_offset)) { \
panic("invalid swap_entry_t = %08x.\n", entry); \
} \
__offset; \
})
struct swap_manager
{
const char *name;
/* Global initialization for the swap manager */
int (*init) (void);
/* Initialize the priv data inside mm_struct */
int (*init_mm) (struct mm_struct *mm);
/* Called when tick interrupt occured */
int (*tick_event) (struct mm_struct *mm);
/* Called when map a swappable page into the mm_struct */
int (*map_swappable) (struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
/* When a page is marked as shared, this routine is called to
* delete the addr entry from the swap manager */
int (*set_unswappable) (struct mm_struct *mm, uintptr_t addr);
/* Try to swap out a page, return then victim */
int (*swap_out_victim) (struct mm_struct *mm, struct Page **ptr_page, int in_tick);
/* check the page relpacement algorithm */
int (*check_swap)(void);
};
extern volatile int swap_init_ok;
int swap_init(void);
int swap_init_mm(struct mm_struct *mm);
int swap_tick_event(struct mm_struct *mm);
int swap_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in);
int swap_set_unswappable(struct mm_struct *mm, uintptr_t addr);
int swap_out(struct mm_struct *mm, int n, int in_tick);
int swap_in(struct mm_struct *mm, uintptr_t addr, struct Page **ptr_result);
//#define MEMBER_OFFSET(m,t) ((int)(&((t *)0)->m))
//#define FROM_MEMBER(m,t,a) ((t *)((char *)(a) - MEMBER_OFFSET(m,t)))
#endif

View File

@@ -0,0 +1,144 @@
#include <defs.h>
#include <x86.h>
#include <stdio.h>
#include <string.h>
#include <swap.h>
#include <swap_fifo.h>
#include <list.h>
/* [wikipedia]The simplest Page Replacement Algorithm(PRA) is a FIFO algorithm. The first-in, first-out
* page replacement algorithm is a low-overhead algorithm that requires little book-keeping on
* the part of the operating system. The idea is obvious from the name - the operating system
* keeps track of all the pages in memory in a queue, with the most recent arrival at the back,
* and the earliest arrival in front. When a page needs to be replaced, the page at the front
* of the queue (the oldest page) is selected. While FIFO is cheap and intuitive, it performs
* poorly in practical application. Thus, it is rarely used in its unmodified form. This
* algorithm experiences Belady's anomaly.
*
* Details of FIFO PRA
* (1) Prepare: In order to implement FIFO PRA, we should manage all swappable pages, so we can
* link these pages into pra_list_head according the time order. At first you should
* be familiar to the struct list in list.h. struct list is a simple doubly linked list
* implementation. You should know howto USE: list_init, list_add(list_add_after),
* list_add_before, list_del, list_next, list_prev. Another tricky method is to transform
* a general list struct to a special struct (such as struct page). You can find some MACRO:
* le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.
*/
list_entry_t pra_list_head;
/*
* (2) _fifo_init_mm: init pra_list_head and let mm->sm_priv point to the addr of pra_list_head.
* Now, From the memory control struct mm_struct, we can access FIFO PRA
*/
static int
_fifo_init_mm(struct mm_struct *mm)
{
list_init(&pra_list_head);
mm->sm_priv = &pra_list_head;
//cprintf(" mm->sm_priv %x in fifo_init_mm\n",mm->sm_priv);
return 0;
}
/*
* (3)_fifo_map_swappable: According FIFO PRA, we should link the most recent arrival page at the back of pra_list_head qeueue
*/
static int
_fifo_map_swappable(struct mm_struct *mm, uintptr_t addr, struct Page *page, int swap_in)
{
list_entry_t *head=(list_entry_t*) mm->sm_priv;
list_entry_t *entry=&(page->pra_page_link);
assert(entry != NULL && head != NULL);
//record the page access situlation
/*LAB3 EXERCISE 2: YOUR CODE*/
//(1)link the most recent arrival page at the back of the pra_list_head qeueue.
list_add(head, entry);
return 0;
}
/*
* (4)_fifo_swap_out_victim: According FIFO PRA, we should unlink the earliest arrival page in front of pra_list_head qeueue,
* then set the addr of addr of this page to ptr_page.
*/
static int
_fifo_swap_out_victim(struct mm_struct *mm, struct Page ** ptr_page, int in_tick)
{
list_entry_t *head=(list_entry_t*) mm->sm_priv;
assert(head != NULL);
assert(in_tick==0);
/* Select the victim */
/*LAB3 EXERCISE 2: YOUR CODE*/
//(1) unlink the earliest arrival page in front of pra_list_head qeueue
//(2) set the addr of addr of this page to ptr_page
/* Select the tail */
list_entry_t *le = head->prev;
assert(head!=le);
struct Page *p = le2page(le, pra_page_link);
list_del(le);
assert(p !=NULL);
*ptr_page = p;
return 0;
}
static int
_fifo_check_swap(void) {
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==4);
cprintf("write Virt Page a in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==4);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==4);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==4);
cprintf("write Virt Page e in fifo_check_swap\n");
*(unsigned char *)0x5000 = 0x0e;
assert(pgfault_num==5);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==5);
cprintf("write Virt Page a in fifo_check_swap\n");
*(unsigned char *)0x1000 = 0x0a;
assert(pgfault_num==6);
cprintf("write Virt Page b in fifo_check_swap\n");
*(unsigned char *)0x2000 = 0x0b;
assert(pgfault_num==7);
cprintf("write Virt Page c in fifo_check_swap\n");
*(unsigned char *)0x3000 = 0x0c;
assert(pgfault_num==8);
cprintf("write Virt Page d in fifo_check_swap\n");
*(unsigned char *)0x4000 = 0x0d;
assert(pgfault_num==9);
return 0;
}
static int
_fifo_init(void)
{
return 0;
}
static int
_fifo_set_unswappable(struct mm_struct *mm, uintptr_t addr)
{
return 0;
}
static int
_fifo_tick_event(struct mm_struct *mm)
{ return 0; }
struct swap_manager swap_manager_fifo =
{
.name = "fifo swap manager",
.init = &_fifo_init,
.init_mm = &_fifo_init_mm,
.tick_event = &_fifo_tick_event,
.map_swappable = &_fifo_map_swappable,
.set_unswappable = &_fifo_set_unswappable,
.swap_out_victim = &_fifo_swap_out_victim,
.check_swap = &_fifo_check_swap,
};

View File

@@ -0,0 +1,7 @@
#ifndef __KERN_MM_SWAP_FIFO_H__
#define __KERN_MM_SWAP_FIFO_H__
#include <swap.h>
extern struct swap_manager swap_manager_fifo;
#endif

View File

@@ -0,0 +1,593 @@
#include <vmm.h>
#include <sync.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <error.h>
#include <pmm.h>
#include <x86.h>
#include <swap.h>
#include <kmalloc.h>
/*
vmm design include two parts: mm_struct (mm) & vma_struct (vma)
mm is the memory manager for the set of continuous virtual memory
area which have the same PDT. vma is a continuous virtual memory area.
There a linear link list for vma & a redblack link list for vma in mm.
---------------
mm related functions:
golbal functions
struct mm_struct * mm_create(void)
void mm_destroy(struct mm_struct *mm)
int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr)
--------------
vma related functions:
global functions
struct vma_struct * vma_create (uintptr_t vm_start, uintptr_t vm_end,...)
void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma)
struct vma_struct * find_vma(struct mm_struct *mm, uintptr_t addr)
local functions
inline void check_vma_overlap(struct vma_struct *prev, struct vma_struct *next)
---------------
check correctness functions
void check_vmm(void);
void check_vma_struct(void);
void check_pgfault(void);
*/
static void check_vmm(void);
static void check_vma_struct(void);
static void check_pgfault(void);
// mm_create - alloc a mm_struct & initialize it.
struct mm_struct *
mm_create(void) {
struct mm_struct *mm = kmalloc(sizeof(struct mm_struct));
if (mm != NULL) {
list_init(&(mm->mmap_list));
mm->mmap_cache = NULL;
mm->pgdir = NULL;
mm->map_count = 0;
if (swap_init_ok) swap_init_mm(mm);
else mm->sm_priv = NULL;
set_mm_count(mm, 0);
sem_init(&(mm->mm_sem), 1);
}
return mm;
}
// vma_create - alloc a vma_struct & initialize it. (addr range: vm_start~vm_end)
struct vma_struct *
vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags) {
struct vma_struct *vma = kmalloc(sizeof(struct vma_struct));
if (vma != NULL) {
vma->vm_start = vm_start;
vma->vm_end = vm_end;
vma->vm_flags = vm_flags;
}
return vma;
}
// find_vma - find a vma (vma->vm_start <= addr <= vma_vm_end)
struct vma_struct *
find_vma(struct mm_struct *mm, uintptr_t addr) {
struct vma_struct *vma = NULL;
if (mm != NULL) {
vma = mm->mmap_cache;
if (!(vma != NULL && vma->vm_start <= addr && vma->vm_end > addr)) {
bool found = 0;
list_entry_t *list = &(mm->mmap_list), *le = list;
while ((le = list_next(le)) != list) {
vma = le2vma(le, list_link);
if (vma->vm_start<=addr && addr < vma->vm_end) {
found = 1;
break;
}
}
if (!found) {
vma = NULL;
}
}
if (vma != NULL) {
mm->mmap_cache = vma;
}
}
return vma;
}
// check_vma_overlap - check if vma1 overlaps vma2 ?
static inline void
check_vma_overlap(struct vma_struct *prev, struct vma_struct *next) {
assert(prev->vm_start < prev->vm_end);
assert(prev->vm_end <= next->vm_start);
assert(next->vm_start < next->vm_end);
}
// insert_vma_struct -insert vma in mm's list link
void
insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma) {
assert(vma->vm_start < vma->vm_end);
list_entry_t *list = &(mm->mmap_list);
list_entry_t *le_prev = list, *le_next;
list_entry_t *le = list;
while ((le = list_next(le)) != list) {
struct vma_struct *mmap_prev = le2vma(le, list_link);
if (mmap_prev->vm_start > vma->vm_start) {
break;
}
le_prev = le;
}
le_next = list_next(le_prev);
/* check overlap */
if (le_prev != list) {
check_vma_overlap(le2vma(le_prev, list_link), vma);
}
if (le_next != list) {
check_vma_overlap(vma, le2vma(le_next, list_link));
}
vma->vm_mm = mm;
list_add_after(le_prev, &(vma->list_link));
mm->map_count ++;
}
// mm_destroy - free mm and mm internal fields
void
mm_destroy(struct mm_struct *mm) {
assert(mm_count(mm) == 0);
list_entry_t *list = &(mm->mmap_list), *le;
while ((le = list_next(list)) != list) {
list_del(le);
kfree(le2vma(le, list_link)); //kfree vma
}
kfree(mm); //kfree mm
mm=NULL;
}
int
mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags,
struct vma_struct **vma_store) {
uintptr_t start = ROUNDDOWN(addr, PGSIZE), end = ROUNDUP(addr + len, PGSIZE);
if (!USER_ACCESS(start, end)) {
return -E_INVAL;
}
assert(mm != NULL);
int ret = -E_INVAL;
struct vma_struct *vma;
if ((vma = find_vma(mm, start)) != NULL && end > vma->vm_start) {
goto out;
}
ret = -E_NO_MEM;
if ((vma = vma_create(start, end, vm_flags)) == NULL) {
goto out;
}
insert_vma_struct(mm, vma);
if (vma_store != NULL) {
*vma_store = vma;
}
ret = 0;
out:
return ret;
}
int
dup_mmap(struct mm_struct *to, struct mm_struct *from) {
assert(to != NULL && from != NULL);
list_entry_t *list = &(from->mmap_list), *le = list;
while ((le = list_prev(le)) != list) {
struct vma_struct *vma, *nvma;
vma = le2vma(le, list_link);
nvma = vma_create(vma->vm_start, vma->vm_end, vma->vm_flags);
if (nvma == NULL) {
return -E_NO_MEM;
}
insert_vma_struct(to, nvma);
bool share = 0;
if (copy_range(to->pgdir, from->pgdir, vma->vm_start, vma->vm_end, share) != 0) {
return -E_NO_MEM;
}
}
return 0;
}
void
exit_mmap(struct mm_struct *mm) {
assert(mm != NULL && mm_count(mm) == 0);
pde_t *pgdir = mm->pgdir;
list_entry_t *list = &(mm->mmap_list), *le = list;
while ((le = list_next(le)) != list) {
struct vma_struct *vma = le2vma(le, list_link);
unmap_range(pgdir, vma->vm_start, vma->vm_end);
}
while ((le = list_next(le)) != list) {
struct vma_struct *vma = le2vma(le, list_link);
exit_range(pgdir, vma->vm_start, vma->vm_end);
}
}
bool
copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable) {
if (!user_mem_check(mm, (uintptr_t)src, len, writable)) {
return 0;
}
memcpy(dst, src, len);
return 1;
}
bool
copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len) {
if (!user_mem_check(mm, (uintptr_t)dst, len, 1)) {
return 0;
}
memcpy(dst, src, len);
return 1;
}
// vmm_init - initialize virtual memory management
// - now just call check_vmm to check correctness of vmm
void
vmm_init(void) {
check_vmm();
}
// check_vmm - check correctness of vmm
static void
check_vmm(void) {
size_t nr_free_pages_store = nr_free_pages();
check_vma_struct();
check_pgfault();
//assert(nr_free_pages_store == nr_free_pages());
cprintf("check_vmm() succeeded.\n");
}
static void
check_vma_struct(void) {
size_t nr_free_pages_store = nr_free_pages();
struct mm_struct *mm = mm_create();
assert(mm != NULL);
int step1 = 10, step2 = step1 * 10;
int i;
for (i = step1; i >= 1; i --) {
struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
assert(vma != NULL);
insert_vma_struct(mm, vma);
}
for (i = step1 + 1; i <= step2; i ++) {
struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
assert(vma != NULL);
insert_vma_struct(mm, vma);
}
list_entry_t *le = list_next(&(mm->mmap_list));
for (i = 1; i <= step2; i ++) {
assert(le != &(mm->mmap_list));
struct vma_struct *mmap = le2vma(le, list_link);
assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2);
le = list_next(le);
}
for (i = 5; i <= 5 * step2; i +=5) {
struct vma_struct *vma1 = find_vma(mm, i);
assert(vma1 != NULL);
struct vma_struct *vma2 = find_vma(mm, i+1);
assert(vma2 != NULL);
struct vma_struct *vma3 = find_vma(mm, i+2);
assert(vma3 == NULL);
struct vma_struct *vma4 = find_vma(mm, i+3);
assert(vma4 == NULL);
struct vma_struct *vma5 = find_vma(mm, i+4);
assert(vma5 == NULL);
assert(vma1->vm_start == i && vma1->vm_end == i + 2);
assert(vma2->vm_start == i && vma2->vm_end == i + 2);
}
for (i =4; i>=0; i--) {
struct vma_struct *vma_below_5= find_vma(mm,i);
if (vma_below_5 != NULL ) {
cprintf("vma_below_5: i %x, start %x, end %x\n",i, vma_below_5->vm_start, vma_below_5->vm_end);
}
assert(vma_below_5 == NULL);
}
mm_destroy(mm);
// assert(nr_free_pages_store == nr_free_pages());
cprintf("check_vma_struct() succeeded!\n");
}
struct mm_struct *check_mm_struct;
// check_pgfault - check correctness of pgfault handler
static void
check_pgfault(void) {
size_t nr_free_pages_store = nr_free_pages();
check_mm_struct = mm_create();
assert(check_mm_struct != NULL);
struct mm_struct *mm = check_mm_struct;
pde_t *pgdir = mm->pgdir = boot_pgdir;
assert(pgdir[0] == 0);
struct vma_struct *vma = vma_create(0, PTSIZE, VM_WRITE);
assert(vma != NULL);
insert_vma_struct(mm, vma);
uintptr_t addr = 0x100;
assert(find_vma(mm, addr) == vma);
int i, sum = 0;
for (i = 0; i < 100; i ++) {
*(char *)(addr + i) = i;
sum += i;
}
for (i = 0; i < 100; i ++) {
sum -= *(char *)(addr + i);
}
assert(sum == 0);
page_remove(pgdir, ROUNDDOWN(addr, PGSIZE));
free_page(pa2page(pgdir[0]));
pgdir[0] = 0;
mm->pgdir = NULL;
mm_destroy(mm);
check_mm_struct = NULL;
assert(nr_free_pages_store == nr_free_pages());
cprintf("check_pgfault() succeeded!\n");
}
//page fault number
volatile unsigned int pgfault_num=0;
/* do_pgfault - interrupt handler to process the page fault execption
* @mm : the control struct for a set of vma using the same PDT
* @error_code : the error code recorded in trapframe->tf_err which is setted by x86 hardware
* @addr : the addr which causes a memory access exception, (the contents of the CR2 register)
*
* CALL GRAPH: trap--> trap_dispatch-->pgfault_handler-->do_pgfault
* The processor provides ucore's do_pgfault function with two items of information to aid in diagnosing
* the exception and recovering from it.
* (1) The contents of the CR2 register. The processor loads the CR2 register with the
* 32-bit linear address that generated the exception. The do_pgfault fun can
* use this address to locate the corresponding page directory and page-table
* entries.
* (2) An error code on the kernel stack. The error code for a page fault has a format different from
* that for other exceptions. The error code tells the exception handler three things:
* -- The P flag (bit 0) indicates whether the exception was due to a not-present page (0)
* or to either an access rights violation or the use of a reserved bit (1).
* -- The W/R flag (bit 1) indicates whether the memory access that caused the exception
* was a read (0) or write (1).
* -- The U/S flag (bit 2) indicates whether the processor was executing at user mode (1)
* or supervisor mode (0) at the time of the exception.
*/
int
do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr) {
int ret = -E_INVAL;
//try to find a vma which include addr
struct vma_struct *vma = find_vma(mm, addr);
pgfault_num++;
//If the addr is in the range of a mm's vma?
if (vma == NULL || vma->vm_start > addr) {
cprintf("not valid addr %x, and can not find it in vma\n", addr);
goto failed;
}
//check the error_code
switch (error_code & 3) {
default:
/* error code flag : default is 3 ( W/R=1, P=1): write, present */
case 2: /* error code flag : (W/R=1, P=0): write, not present */
if (!(vma->vm_flags & VM_WRITE)) {
cprintf("do_pgfault failed: error code flag = write AND not present, but the addr's vma cannot write\n");
goto failed;
}
break;
case 1: /* error code flag : (W/R=0, P=1): read, present */
cprintf("do_pgfault failed: error code flag = read AND present\n");
goto failed;
case 0: /* error code flag : (W/R=0, P=0): read, not present */
if (!(vma->vm_flags & (VM_READ | VM_EXEC))) {
cprintf("do_pgfault failed: error code flag = read AND not present, but the addr's vma cannot read or exec\n");
goto failed;
}
}
/* IF (write an existed addr ) OR
* (write an non_existed addr && addr is writable) OR
* (read an non_existed addr && addr is readable)
* THEN
* continue process
*/
uint32_t perm = PTE_U;
if (vma->vm_flags & VM_WRITE) {
perm |= PTE_W;
}
addr = ROUNDDOWN(addr, PGSIZE);
ret = -E_NO_MEM;
pte_t *ptep=NULL;
/*LAB3 EXERCISE 1: YOUR CODE
* Maybe you want help comment, BELOW comments can help you finish the code
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* get_pte : get an pte and return the kernel virtual address of this pte for la
* if the PT contians this pte didn't exist, alloc a page for PT (notice the 3th parameter '1')
* pgdir_alloc_page : call alloc_page & page_insert functions to allocate a page size memory & setup
* an addr map pa<--->la with linear address la and the PDT pgdir
* DEFINES:
* VM_WRITE : If vma->vm_flags & VM_WRITE == 1/0, then the vma is writable/non writable
* PTE_W 0x002 // page table/directory entry flags bit : Writeable
* PTE_U 0x004 // page table/directory entry flags bit : User can access
* VARIABLES:
* mm->pgdir : the PDT of these vma
*
*/
#if 0
/*LAB3 EXERCISE 1: YOUR CODE*/
ptep = ??? //(1) try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
if (*ptep == 0) {
//(2) if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
}
else {
/*LAB3 EXERCISE 2: YOUR CODE
* Now we think this pte is a swap entry, we should load data from disk to a page with phy addr,
* and map the phy addr with logical addr, trigger swap manager to record the access situation of this page.
*
* Some Useful MACROs and DEFINEs, you can use them in below implementation.
* MACROs or Functions:
* swap_in(mm, addr, &page) : alloc a memory page, then according to the swap entry in PTE for addr,
* find the addr of disk page, read the content of disk page into this memroy page
* page_insert build the map of phy addr of an Page with the linear addr la
* swap_map_swappable set the page swappable
*/
/*
* LAB5 CHALLENGE ( the implmentation Copy on Write)
There are 2 situlations when code comes here.
1) *ptep & PTE_P == 1, it means one process try to write a readonly page.
If the vma includes this addr is writable, then we can set the page writable by rewrite the *ptep.
This method could be used to implement the Copy on Write (COW) thchnology(a fast fork process method).
2) *ptep & PTE_P == 0 & but *ptep!=0, it means this pte is a swap entry.
We should add the LAB3's results here.
*/
if(swap_init_ok) {
struct Page *page=NULL;
//(1According to the mm AND addr, try to load the content of right disk page
// into the memory which page managed.
//(2) According to the mm, addr AND page, setup the map of phy addr <---> logical addr
//(3) make the page swappable.
//(4) [NOTICE]: you myabe need to update your lab3's implementation for LAB5's normal execution.
}
else {
cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
goto failed;
}
}
#endif
// try to find a pte, if pte's PT(Page Table) isn't existed, then create a PT.
// (notice the 3th parameter '1')
if ((ptep = get_pte(mm->pgdir, addr, 1)) == NULL) {
cprintf("get_pte in do_pgfault failed\n");
goto failed;
}
if (*ptep == 0) { // if the phy addr isn't exist, then alloc a page & map the phy addr with logical addr
if (pgdir_alloc_page(mm->pgdir, addr, perm) == NULL) {
cprintf("pgdir_alloc_page in do_pgfault failed\n");
goto failed;
}
}
else {
struct Page *page=NULL;
cprintf("do pgfault: ptep %x, pte %x\n",ptep, *ptep);
if (*ptep & PTE_P) {
//if process write to this existed readonly page (PTE_P means existed), then should be here now.
//we can implement the delayed memory space copy for fork child process (AKA copy on write, COW).
//we didn't implement now, we will do it in future.
panic("error write a non-writable pte");
//page = pte2page(*ptep);
} else{
// if this pte is a swap entry, then load data from disk to a page with phy addr
// and call page_insert to map the phy addr with logical addr
if(swap_init_ok) {
if ((ret = swap_in(mm, addr, &page)) != 0) {
cprintf("swap_in in do_pgfault failed\n");
goto failed;
}
}
else {
cprintf("no swap_init_ok but ptep is %x, failed\n",*ptep);
goto failed;
}
}
page_insert(mm->pgdir, page, addr, perm);
swap_map_swappable(mm, addr, page, 1);
}
ret = 0;
failed:
return ret;
}
bool
user_mem_check(struct mm_struct *mm, uintptr_t addr, size_t len, bool write) {
if (mm != NULL) {
if (!USER_ACCESS(addr, addr + len)) {
return 0;
}
struct vma_struct *vma;
uintptr_t start = addr, end = addr + len;
while (start < end) {
if ((vma = find_vma(mm, start)) == NULL || start < vma->vm_start) {
return 0;
}
if (!(vma->vm_flags & ((write) ? VM_WRITE : VM_READ))) {
return 0;
}
if (write && (vma->vm_flags & VM_STACK)) {
if (start < vma->vm_start + PGSIZE) { //check stack start & size
return 0;
}
}
start = vma->vm_end;
}
return 1;
}
return KERN_ACCESS(addr, addr + len);
}
bool
copy_string(struct mm_struct *mm, char *dst, const char *src, size_t maxn) {
size_t alen, part = ROUNDDOWN((uintptr_t)src + PGSIZE, PGSIZE) - (uintptr_t)src;
while (1) {
if (part > maxn) {
part = maxn;
}
if (!user_mem_check(mm, (uintptr_t)src, part, 0)) {
return 0;
}
if ((alen = strnlen(src, part)) < part) {
memcpy(dst, src, alen + 1);
return 1;
}
if (part == maxn) {
return 0;
}
memcpy(dst, src, part);
dst += part, src += part, maxn -= part;
part = PGSIZE;
}
}

View File

@@ -0,0 +1,111 @@
#ifndef __KERN_MM_VMM_H__
#define __KERN_MM_VMM_H__
#include <defs.h>
#include <list.h>
#include <memlayout.h>
#include <sync.h>
#include <proc.h>
#include <sem.h>
//pre define
struct mm_struct;
// the virtual continuous memory area(vma)
struct vma_struct {
struct mm_struct *vm_mm; // the set of vma using the same PDT
uintptr_t vm_start; // start addr of vma
uintptr_t vm_end; // end addr of vma
uint32_t vm_flags; // flags of vma
list_entry_t list_link; // linear list link which sorted by start addr of vma
};
#define le2vma(le, member) \
to_struct((le), struct vma_struct, member)
#define VM_READ 0x00000001
#define VM_WRITE 0x00000002
#define VM_EXEC 0x00000004
#define VM_STACK 0x00000008
// the control struct for a set of vma using the same PDT
struct mm_struct {
list_entry_t mmap_list; // linear list link which sorted by start addr of vma
struct vma_struct *mmap_cache; // current accessed vma, used for speed purpose
pde_t *pgdir; // the PDT of these vma
int map_count; // the count of these vma
void *sm_priv; // the private data for swap manager
int mm_count; // the number ofprocess which shared the mm
semaphore_t mm_sem; // mutex for using dup_mmap fun to duplicat the mm
int locked_by; // the lock owner process's pid
};
struct vma_struct *find_vma(struct mm_struct *mm, uintptr_t addr);
struct vma_struct *vma_create(uintptr_t vm_start, uintptr_t vm_end, uint32_t vm_flags);
void insert_vma_struct(struct mm_struct *mm, struct vma_struct *vma);
struct mm_struct *mm_create(void);
void mm_destroy(struct mm_struct *mm);
void vmm_init(void);
int mm_map(struct mm_struct *mm, uintptr_t addr, size_t len, uint32_t vm_flags,
struct vma_struct **vma_store);
int do_pgfault(struct mm_struct *mm, uint32_t error_code, uintptr_t addr);
int mm_unmap(struct mm_struct *mm, uintptr_t addr, size_t len);
int dup_mmap(struct mm_struct *to, struct mm_struct *from);
void exit_mmap(struct mm_struct *mm);
uintptr_t get_unmapped_area(struct mm_struct *mm, size_t len);
int mm_brk(struct mm_struct *mm, uintptr_t addr, size_t len);
extern volatile unsigned int pgfault_num;
extern struct mm_struct *check_mm_struct;
bool user_mem_check(struct mm_struct *mm, uintptr_t start, size_t len, bool write);
bool copy_from_user(struct mm_struct *mm, void *dst, const void *src, size_t len, bool writable);
bool copy_to_user(struct mm_struct *mm, void *dst, const void *src, size_t len);
bool copy_string(struct mm_struct *mm, char *dst, const char *src, size_t maxn);
static inline int
mm_count(struct mm_struct *mm) {
return mm->mm_count;
}
static inline void
set_mm_count(struct mm_struct *mm, int val) {
mm->mm_count = val;
}
static inline int
mm_count_inc(struct mm_struct *mm) {
mm->mm_count += 1;
return mm->mm_count;
}
static inline int
mm_count_dec(struct mm_struct *mm) {
mm->mm_count -= 1;
return mm->mm_count;
}
static inline void
lock_mm(struct mm_struct *mm) {
if (mm != NULL) {
down(&(mm->mm_sem));
if (current != NULL) {
mm->locked_by = current->pid;
}
}
}
static inline void
unlock_mm(struct mm_struct *mm) {
if (mm != NULL) {
up(&(mm->mm_sem));
mm->locked_by = 0;
}
}
#endif /* !__KERN_MM_VMM_H__ */

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,105 @@
#ifndef __KERN_PROCESS_PROC_H__
#define __KERN_PROCESS_PROC_H__
#include <defs.h>
#include <list.h>
#include <trap.h>
#include <memlayout.h>
#include <skew_heap.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 50
#define MAX_PROCESS 4096
#define MAX_PID (MAX_PROCESS * 2)
extern list_entry_t proc_list;
struct inode;
struct fs_struct;
struct proc_struct {
enum proc_state state; // Process state
int pid; // Process ID
int runs; // the running times of Proces
uintptr_t kstack; // Process kernel stack
volatile bool need_resched; // bool value: need to be rescheduled to release CPU?
struct proc_struct *parent; // the parent process
struct mm_struct *mm; // Process's memory management field
struct context context; // Switch here to run process
struct trapframe *tf; // Trap frame for current interrupt
uintptr_t cr3; // CR3 register: the base addr of Page Directroy Table(PDT)
uint32_t flags; // Process flag
char name[PROC_NAME_LEN + 1]; // Process name
list_entry_t list_link; // Process link list
list_entry_t hash_link; // Process hash list
int exit_code; // exit code (be sent to parent proc)
uint32_t wait_state; // waiting state
struct proc_struct *cptr, *yptr, *optr; // relations between processes
struct run_queue *rq; // running queue contains Process
list_entry_t run_link; // the entry linked in run queue
int time_slice; // time slice for occupying the CPU
skew_heap_entry_t lab6_run_pool; // FOR LAB6 ONLY: the entry in the run pool
uint32_t lab6_stride; // FOR LAB6 ONLY: the current stride of the process
uint32_t lab6_priority; // FOR LAB6 ONLY: the priority of process, set by lab6_set_priority(uint32_t)
struct files_struct *filesp; // the file related info(pwd, files_count, files_array, fs_semaphore) of process
};
#define PF_EXITING 0x00000001 // getting shutdown
#define WT_INTERRUPTED 0x80000000 // the wait state could be interrupted
#define WT_CHILD (0x00000001 | WT_INTERRUPTED) // wait child process
#define WT_KSEM 0x00000100 // wait kernel semaphore
#define WT_TIMER (0x00000002 | WT_INTERRUPTED) // wait timer
#define WT_KBD (0x00000004 | WT_INTERRUPTED) // wait the input of keyboard
#define le2proc(le, member) \
to_struct((le), struct proc_struct, member)
extern struct proc_struct *idleproc, *initproc, *current;
void proc_init(void);
void proc_run(struct proc_struct *proc);
int kernel_thread(int (*fn)(void *), void *arg, uint32_t clone_flags);
char *set_proc_name(struct proc_struct *proc, const char *name);
char *get_proc_name(struct proc_struct *proc);
void cpu_idle(void) __attribute__((noreturn));
struct proc_struct *find_proc(int pid);
int do_fork(uint32_t clone_flags, uintptr_t stack, struct trapframe *tf);
int do_exit(int error_code);
int do_yield(void);
int do_execve(const char *name, int argc, const char **argv);
int do_wait(int pid, int *code_store);
int do_kill(int pid);
//FOR LAB6, set the process's priority (bigger value will get more CPU time)
void lab6_set_priority(uint32_t priority);
int do_sleep(unsigned int time);
#endif /* !__KERN_PROCESS_PROC_H__ */

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

View File

@@ -0,0 +1,163 @@
#include <defs.h>
#include <list.h>
#include <proc.h>
#include <assert.h>
#include <default_sched.h>
#define USE_SKEW_HEAP 1
/* You should define the BigStride constant here*/
/* LAB6: YOUR CODE */
#define BIG_STRIDE 0x7FFFFFFF /* ??? */
/* The compare function for two skew_heap_node_t's and the
* corresponding procs*/
static int
proc_stride_comp_f(void *a, void *b)
{
struct proc_struct *p = le2proc(a, lab6_run_pool);
struct proc_struct *q = le2proc(b, lab6_run_pool);
int32_t c = p->lab6_stride - q->lab6_stride;
if (c > 0) return 1;
else if (c == 0) return 0;
else return -1;
}
/*
* stride_init initializes the run-queue rq with correct assignment for
* member variables, including:
*
* - run_list: should be a empty list after initialization.
* - lab6_run_pool: NULL
* - proc_num: 0
* - max_time_slice: no need here, the variable would be assigned by the caller.
*
* hint: see proj13.1/libs/list.h for routines of the list structures.
*/
static void
stride_init(struct run_queue *rq) {
/* LAB6: YOUR CODE */
list_init(&(rq->run_list));
rq->lab6_run_pool = NULL;
rq->proc_num = 0;
}
/*
* stride_enqueue inserts the process ``proc'' into the run-queue
* ``rq''. The procedure should verify/initialize the relevant members
* of ``proc'', and then put the ``lab6_run_pool'' node into the
* queue(since we use priority queue here). The procedure should also
* update the meta date in ``rq'' structure.
*
* proc->time_slice denotes the time slices allocation for the
* process, which should set to rq->max_time_slice.
*
* hint: see proj13.1/libs/skew_heap.h for routines of the priority
* queue structures.
*/
static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
rq->lab6_run_pool =
skew_heap_insert(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f);
#else
assert(list_empty(&(proc->run_link)));
list_add_before(&(rq->run_list), &(proc->run_link));
#endif
if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) {
proc->time_slice = rq->max_time_slice;
}
proc->rq = rq;
rq->proc_num ++;
}
/*
* stride_dequeue removes the process ``proc'' from the run-queue
* ``rq'', the operation would be finished by the skew_heap_remove
* operations. Remember to update the ``rq'' structure.
*
* hint: see proj13.1/libs/skew_heap.h for routines of the priority
* queue structures.
*/
static void
stride_dequeue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
rq->lab6_run_pool =
skew_heap_remove(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f);
#else
assert(!list_empty(&(proc->run_link)) && proc->rq == rq);
list_del_init(&(proc->run_link));
#endif
rq->proc_num --;
}
/*
* stride_pick_next pick the element from the ``run-queue'', with the
* minimum value of stride, and returns the corresponding process
* pointer. The process pointer would be calculated by macro le2proc,
* see proj13.1/kern/process/proc.h for definition. Return NULL if
* there is no process in the queue.
*
* When one proc structure is selected, remember to update the stride
* property of the proc. (stride += BIG_STRIDE / priority)
*
* hint: see proj13.1/libs/skew_heap.h for routines of the priority
* queue structures.
*/
static struct proc_struct *
stride_pick_next(struct run_queue *rq) {
/* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
if (rq->lab6_run_pool == NULL) return NULL;
struct proc_struct *p = le2proc(rq->lab6_run_pool, lab6_run_pool);
#else
list_entry_t *le = list_next(&(rq->run_list));
if (le == &rq->run_list)
return NULL;
struct proc_struct *p = le2proc(le, run_link);
le = list_next(le);
while (le != &rq->run_list)
{
struct proc_struct *q = le2proc(le, run_link);
if ((int32_t)(p->lab6_stride - q->lab6_stride) > 0)
p = q;
le = list_next(le);
}
#endif
if (p->lab6_priority == 0)
p->lab6_stride += BIG_STRIDE;
else p->lab6_stride += BIG_STRIDE / p->lab6_priority;
return p;
}
/*
* stride_proc_tick works with the tick event of current process. You
* should check whether the time slices for current process is
* exhausted and update the proc struct ``proc''. proc->time_slice
* denotes the time slices left for current
* process. proc->need_resched is the flag variable for process
* switching.
*/
static void
stride_proc_tick(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
if (proc->time_slice > 0) {
proc->time_slice --;
}
if (proc->time_slice == 0) {
proc->need_resched = 1;
}
}
struct sched_class default_sched_class = {
.name = "stride_scheduler",
.init = stride_init,
.enqueue = stride_enqueue,
.dequeue = stride_dequeue,
.pick_next = stride_pick_next,
.proc_tick = stride_proc_tick,
};

View File

@@ -0,0 +1,9 @@
#ifndef __KERN_SCHEDULE_SCHED_RR_H__
#define __KERN_SCHEDULE_SCHED_RR_H__
#include <sched.h>
extern struct sched_class default_sched_class;
#endif /* !__KERN_SCHEDULE_SCHED_RR_H__ */

View File

@@ -0,0 +1,133 @@
#include <defs.h>
#include <list.h>
#include <proc.h>
#include <assert.h>
#include <default_sched.h>
#define USE_SKEW_HEAP 1
/* You should define the BigStride constant here*/
/* LAB6: YOUR CODE */
#define BIG_STRIDE /* you should give a value, and is ??? */
/* The compare function for two skew_heap_node_t's and the
* corresponding procs*/
static int
proc_stride_comp_f(void *a, void *b)
{
struct proc_struct *p = le2proc(a, lab6_run_pool);
struct proc_struct *q = le2proc(b, lab6_run_pool);
int32_t c = p->lab6_stride - q->lab6_stride;
if (c > 0) return 1;
else if (c == 0) return 0;
else return -1;
}
/*
* stride_init initializes the run-queue rq with correct assignment for
* member variables, including:
*
* - run_list: should be a empty list after initialization.
* - lab6_run_pool: NULL
* - proc_num: 0
* - max_time_slice: no need here, the variable would be assigned by the caller.
*
* hint: see libs/list.h for routines of the list structures.
*/
static void
stride_init(struct run_queue *rq) {
/* LAB6: YOUR CODE
* (1) init the ready process list: rq->run_list
* (2) init the run pool: rq->lab6_run_pool
* (3) set number of process: rq->proc_num to 0
*/
}
/*
* stride_enqueue inserts the process ``proc'' into the run-queue
* ``rq''. The procedure should verify/initialize the relevant members
* of ``proc'', and then put the ``lab6_run_pool'' node into the
* queue(since we use priority queue here). The procedure should also
* update the meta date in ``rq'' structure.
*
* proc->time_slice denotes the time slices allocation for the
* process, which should set to rq->max_time_slice.
*
* hint: see libs/skew_heap.h for routines of the priority
* queue structures.
*/
static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE
* (1) insert the proc into rq correctly
* NOTICE: you can use skew_heap or list. Important functions
* skew_heap_insert: insert a entry into skew_heap
* list_add_before: insert a entry into the last of list
* (2) recalculate proc->time_slice
* (3) set proc->rq pointer to rq
* (4) increase rq->proc_num
*/
}
/*
* stride_dequeue removes the process ``proc'' from the run-queue
* ``rq'', the operation would be finished by the skew_heap_remove
* operations. Remember to update the ``rq'' structure.
*
* hint: see libs/skew_heap.h for routines of the priority
* queue structures.
*/
static void
stride_dequeue(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE
* (1) remove the proc from rq correctly
* NOTICE: you can use skew_heap or list. Important functions
* skew_heap_remove: remove a entry from skew_heap
* list_del_init: remove a entry from the list
*/
}
/*
* stride_pick_next pick the element from the ``run-queue'', with the
* minimum value of stride, and returns the corresponding process
* pointer. The process pointer would be calculated by macro le2proc,
* see kern/process/proc.h for definition. Return NULL if
* there is no process in the queue.
*
* When one proc structure is selected, remember to update the stride
* property of the proc. (stride += BIG_STRIDE / priority)
*
* hint: see libs/skew_heap.h for routines of the priority
* queue structures.
*/
static struct proc_struct *
stride_pick_next(struct run_queue *rq) {
/* LAB6: YOUR CODE
* (1) get a proc_struct pointer p with the minimum value of stride
(1.1) If using skew_heap, we can use le2proc get the p from rq->lab6_run_poll
(1.2) If using list, we have to search list to find the p with minimum stride value
* (2) update p;s stride value: p->lab6_stride
* (3) return p
*/
}
/*
* stride_proc_tick works with the tick event of current process. You
* should check whether the time slices for current process is
* exhausted and update the proc struct ``proc''. proc->time_slice
* denotes the time slices left for current
* process. proc->need_resched is the flag variable for process
* switching.
*/
static void
stride_proc_tick(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
}
struct sched_class default_sched_class = {
.name = "stride_scheduler",
.init = stride_init,
.enqueue = stride_enqueue,
.dequeue = stride_dequeue,
.pick_next = stride_pick_next,
.proc_tick = stride_proc_tick,
};

View File

@@ -0,0 +1,172 @@
#include <list.h>
#include <sync.h>
#include <proc.h>
#include <sched.h>
#include <stdio.h>
#include <assert.h>
#include <default_sched.h>
static list_entry_t timer_list;
static struct sched_class *sched_class;
static struct run_queue *rq;
static inline void
sched_class_enqueue(struct proc_struct *proc) {
if (proc != idleproc) {
sched_class->enqueue(rq, proc);
}
}
static inline void
sched_class_dequeue(struct proc_struct *proc) {
sched_class->dequeue(rq, proc);
}
static inline struct proc_struct *
sched_class_pick_next(void) {
return sched_class->pick_next(rq);
}
static void
sched_class_proc_tick(struct proc_struct *proc) {
if (proc != idleproc) {
sched_class->proc_tick(rq, proc);
}
else {
proc->need_resched = 1;
}
}
static struct run_queue __rq;
void
sched_init(void) {
list_init(&timer_list);
sched_class = &default_sched_class;
rq = &__rq;
rq->max_time_slice = 20;
sched_class->init(rq);
cprintf("sched class: %s\n", sched_class->name);
}
void
wakeup_proc(struct proc_struct *proc) {
assert(proc->state != PROC_ZOMBIE);
bool intr_flag;
local_intr_save(intr_flag);
{
if (proc->state != PROC_RUNNABLE) {
proc->state = PROC_RUNNABLE;
proc->wait_state = 0;
if (proc != current) {
sched_class_enqueue(proc);
}
}
else {
warn("wakeup runnable process.\n");
}
}
local_intr_restore(intr_flag);
}
void
schedule(void) {
bool intr_flag;
struct proc_struct *next;
local_intr_save(intr_flag);
{
current->need_resched = 0;
if (current->state == PROC_RUNNABLE) {
sched_class_enqueue(current);
}
if ((next = sched_class_pick_next()) != NULL) {
sched_class_dequeue(next);
}
if (next == NULL) {
next = idleproc;
}
next->runs ++;
if (next != current) {
proc_run(next);
}
}
local_intr_restore(intr_flag);
}
void
add_timer(timer_t *timer) {
bool intr_flag;
local_intr_save(intr_flag);
{
assert(timer->expires > 0 && timer->proc != NULL);
assert(list_empty(&(timer->timer_link)));
list_entry_t *le = list_next(&timer_list);
while (le != &timer_list) {
timer_t *next = le2timer(le, timer_link);
if (timer->expires < next->expires) {
next->expires -= timer->expires;
break;
}
timer->expires -= next->expires;
le = list_next(le);
}
list_add_before(le, &(timer->timer_link));
}
local_intr_restore(intr_flag);
}
void
del_timer(timer_t *timer) {
bool intr_flag;
local_intr_save(intr_flag);
{
if (!list_empty(&(timer->timer_link))) {
if (timer->expires != 0) {
list_entry_t *le = list_next(&(timer->timer_link));
if (le != &timer_list) {
timer_t *next = le2timer(le, timer_link);
next->expires += timer->expires;
}
}
list_del_init(&(timer->timer_link));
}
}
local_intr_restore(intr_flag);
}
void
run_timer_list(void) {
bool intr_flag;
local_intr_save(intr_flag);
{
list_entry_t *le = list_next(&timer_list);
if (le != &timer_list) {
timer_t *timer = le2timer(le, timer_link);
assert(timer->expires != 0);
timer->expires --;
while (timer->expires == 0) {
le = list_next(le);
struct proc_struct *proc = timer->proc;
if (proc->wait_state != 0) {
assert(proc->wait_state & WT_INTERRUPTED);
}
else {
warn("process %d's wait_state == 0.\n", proc->pid);
}
wakeup_proc(proc);
del_timer(timer);
if (le == &timer_list) {
break;
}
timer = le2timer(le, timer_link);
}
}
sched_class_proc_tick(current);
}
local_intr_restore(intr_flag);
}

View File

@@ -0,0 +1,70 @@
#ifndef __KERN_SCHEDULE_SCHED_H__
#define __KERN_SCHEDULE_SCHED_H__
#include <defs.h>
#include <list.h>
#include <skew_heap.h>
struct proc_struct;
typedef struct {
unsigned int expires;
struct proc_struct *proc;
list_entry_t timer_link;
} timer_t;
#define le2timer(le, member) \
to_struct((le), timer_t, member)
static inline timer_t *
timer_init(timer_t *timer, struct proc_struct *proc, int expires) {
timer->expires = expires;
timer->proc = proc;
list_init(&(timer->timer_link));
return timer;
}
struct run_queue;
// The introduction of scheduling classes is borrrowed from Linux, and makes the
// core scheduler quite extensible. These classes (the scheduler modules) encapsulate
// the scheduling policies.
struct sched_class {
// the name of sched_class
const char *name;
// Init the run queue
void (*init)(struct run_queue *rq);
// put the proc into runqueue, and this function must be called with rq_lock
void (*enqueue)(struct run_queue *rq, struct proc_struct *proc);
// get the proc out runqueue, and this function must be called with rq_lock
void (*dequeue)(struct run_queue *rq, struct proc_struct *proc);
// choose the next runnable task
struct proc_struct *(*pick_next)(struct run_queue *rq);
// dealer of the time-tick
void (*proc_tick)(struct run_queue *rq, struct proc_struct *proc);
/* for SMP support in the future
* load_balance
* void (*load_balance)(struct rq* rq);
* get some proc from this rq, used in load_balance,
* return value is the num of gotten proc
* int (*get_proc)(struct rq* rq, struct proc* procs_moved[]);
*/
};
struct run_queue {
list_entry_t run_list;
unsigned int proc_num;
int max_time_slice;
// For LAB6 ONLY
skew_heap_entry_t *lab6_run_pool;
};
void sched_init(void);
void wakeup_proc(struct proc_struct *proc);
void schedule(void);
void add_timer(timer_t *timer);
void del_timer(timer_t *timer);
void run_timer_list(void);
#endif /* !__KERN_SCHEDULE_SCHED_H__ */

View File

@@ -0,0 +1,209 @@
#include <stdio.h>
#include <proc.h>
#include <sem.h>
#include <monitor.h>
#include <assert.h>
#define N 5 /* 哲学家数目 */
#define LEFT (i-1+N)%N /* i的左邻号码 */
#define RIGHT (i+1)%N /* i的右邻号码 */
#define THINKING 0 /* 哲学家正在思考 */
#define HUNGRY 1 /* 哲学家想取得叉子 */
#define EATING 2 /* 哲学家正在吃面 */
#define TIMES 4 /* 吃4次饭 */
#define SLEEP_TIME 10
//---------- philosophers problem using semaphore ----------------------
int state_sema[N]; /* 记录每个人状态的数组 */
/* 信号量是一个特殊的整型变量 */
semaphore_t mutex; /* 临界区互斥 */
semaphore_t s[N]; /* 每个哲学家一个信号量 */
struct proc_struct *philosopher_proc_sema[N];
void phi_test_sema(i) /* i哲学家号码从0到N-1 */
{
if(state_sema[i]==HUNGRY&&state_sema[LEFT]!=EATING
&&state_sema[RIGHT]!=EATING)
{
state_sema[i]=EATING;
up(&s[i]);
}
}
void phi_take_forks_sema(int i) /* i哲学家号码从0到N-1 */
{
down(&mutex); /* 进入临界区 */
state_sema[i]=HUNGRY; /* 记录下哲学家i饥饿的事实 */
phi_test_sema(i); /* 试图得到两只叉子 */
up(&mutex); /* 离开临界区 */
down(&s[i]); /* 如果得不到叉子就阻塞 */
}
void phi_put_forks_sema(int i) /* i哲学家号码从0到N-1 */
{
down(&mutex); /* 进入临界区 */
state_sema[i]=THINKING; /* 哲学家进餐结束 */
phi_test_sema(LEFT); /* 看一下左邻居现在是否能进餐 */
phi_test_sema(RIGHT); /* 看一下右邻居现在是否能进餐 */
up(&mutex); /* 离开临界区 */
}
int philosopher_using_semaphore(void * arg) /* i哲学家号码从0到N-1 */
{
int i, iter=0;
i=(int)arg;
cprintf("I am No.%d philosopher_sema\n",i);
while(iter++<TIMES)
{ /* 无限循环 */
cprintf("Iter %d, No.%d philosopher_sema is thinking\n",iter,i); /* 哲学家正在思考 */
do_sleep(SLEEP_TIME);
phi_take_forks_sema(i);
/* 需要两只叉子,或者阻塞 */
cprintf("Iter %d, No.%d philosopher_sema is eating\n",iter,i); /* 进餐 */
do_sleep(SLEEP_TIME);
phi_put_forks_sema(i);
/* 把两把叉子同时放回桌子 */
}
cprintf("No.%d philosopher_sema quit\n",i);
return 0;
}
//-----------------philosopher problem using monitor ------------
/*PSEUDO CODE :philosopher problem using monitor
* monitor dp
* {
* enum {thinking, hungry, eating} state[5];
* condition self[5];
*
* void pickup(int i) {
* state[i] = hungry;
* if ((state[(i+4)%5] != eating) && (state[(i+1)%5] != eating)) {
* state[i] = eating;
* else
* self[i].wait();
* }
*
* void putdown(int i) {
* state[i] = thinking;
* if ((state[(i+4)%5] == hungry) && (state[(i+3)%5] != eating)) {
* state[(i+4)%5] = eating;
* self[(i+4)%5].signal();
* }
* if ((state[(i+1)%5] == hungry) && (state[(i+2)%5] != eating)) {
* state[(i+1)%5] = eating;
* self[(i+1)%5].signal();
* }
* }
*
* void init() {
* for (int i = 0; i < 5; i++)
* state[i] = thinking;
* }
* }
*/
struct proc_struct *philosopher_proc_condvar[N]; // N philosopher
int state_condvar[N]; // the philosopher's state: EATING, HUNGARY, THINKING
monitor_t mt, *mtp=&mt; // mp is mutex semaphore for monitor's procedures
void phi_test_condvar (i) {
if(state_condvar[i]==HUNGRY&&state_condvar[LEFT]!=EATING
&&state_condvar[RIGHT]!=EATING) {
cprintf("phi_test_condvar: state_condvar[%d] will eating\n",i);
state_condvar[i] = EATING ;
cprintf("phi_test_condvar: signal self_cv[%d] \n",i);
cond_signal(&mtp->cv[i]) ;
}
}
void phi_take_forks_condvar(int i) {
down(&(mtp->mutex));
//--------into routine in monitor--------------
// LAB7 EXERCISE1: YOUR CODE
// I am hungry
// try to get fork
// I am hungry
state_condvar[i]=HUNGRY;
// try to get fork
phi_test_condvar(i);
while (state_condvar[i] != EATING) {
cprintf("phi_take_forks_condvar: %d didn't get fork and will wait\n",i);
cond_wait(&mtp->cv[i]);
}
//--------leave routine in monitor--------------
if(mtp->next_count>0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}
void phi_put_forks_condvar(int i) {
down(&(mtp->mutex));
//--------into routine in monitor--------------
// LAB7 EXERCISE1: YOUR CODE
// I ate over
// test left and right neighbors
// I ate over
state_condvar[i]=THINKING;
// test left and right neighbors
phi_test_condvar(LEFT);
phi_test_condvar(RIGHT);
//--------leave routine in monitor--------------
if(mtp->next_count>0)
up(&(mtp->next));
else
up(&(mtp->mutex));
}
//---------- philosophers using monitor (condition variable) ----------------------
int philosopher_using_condvar(void * arg) { /* arg is the No. of philosopher 0~N-1*/
int i, iter=0;
i=(int)arg;
cprintf("I am No.%d philosopher_condvar\n",i);
while(iter++<TIMES)
{ /* iterate*/
cprintf("Iter %d, No.%d philosopher_condvar is thinking\n",iter,i); /* thinking*/
do_sleep(SLEEP_TIME);
phi_take_forks_condvar(i);
/* need two forks, maybe blocked */
cprintf("Iter %d, No.%d philosopher_condvar is eating\n",iter,i); /* eating*/
do_sleep(SLEEP_TIME);
phi_put_forks_condvar(i);
/* return two forks back*/
}
cprintf("No.%d philosopher_condvar quit\n",i);
return 0;
}
void check_sync(void){
int i;
//check semaphore
sem_init(&mutex, 1);
for(i=0;i<N;i++){
sem_init(&s[i], 0);
int pid = kernel_thread(philosopher_using_semaphore, (void *)i, 0);
if (pid <= 0) {
panic("create No.%d philosopher_using_semaphore failed.\n");
}
philosopher_proc_sema[i] = find_proc(pid);
set_proc_name(philosopher_proc_sema[i], "philosopher_sema_proc");
}
//check condition variable
monitor_init(&mt, N);
for(i=0;i<N;i++){
state_condvar[i]=THINKING;
int pid = kernel_thread(philosopher_using_condvar, (void *)i, 0);
if (pid <= 0) {
panic("create No.%d philosopher_using_condvar failed.\n");
}
philosopher_proc_condvar[i] = find_proc(pid);
set_proc_name(philosopher_proc_condvar[i], "philosopher_condvar_proc");
}
}

View File

@@ -0,0 +1,72 @@
#include <stdio.h>
#include <monitor.h>
#include <kmalloc.h>
#include <assert.h>
// Initialize monitor.
void
monitor_init (monitor_t * mtp, size_t num_cv) {
int i;
assert(num_cv>0);
mtp->next_count = 0;
mtp->cv = NULL;
sem_init(&(mtp->mutex), 1); //unlocked
sem_init(&(mtp->next), 0);
mtp->cv =(condvar_t *) kmalloc(sizeof(condvar_t)*num_cv);
assert(mtp->cv!=NULL);
for(i=0; i<num_cv; i++){
mtp->cv[i].count=0;
sem_init(&(mtp->cv[i].sem),0);
mtp->cv[i].owner=mtp;
}
}
// Unlock one of threads waiting on the condition variable.
void
cond_signal (condvar_t *cvp) {
//LAB7 EXERCISE1: YOUR CODE
cprintf("cond_signal begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
/*
* cond_signal(cv) {
* if(cv.count>0) {
* mt.next_count ++;
* signal(cv.sem);
* wait(mt.next);
* mt.next_count--;
* }
* }
*/
if(cvp->count>0) {
cvp->owner->next_count ++;
up(&(cvp->sem));
down(&(cvp->owner->next));
cvp->owner->next_count --;
}
cprintf("cond_signal end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
}
// Suspend calling thread on a condition variable waiting for condition Atomically unlocks
// mutex and suspends calling thread on conditional variable after waking up locks mutex. Notice: mp is mutex semaphore for monitor's procedures
void
cond_wait (condvar_t *cvp) {
//LAB7 EXERCISE1: YOUR CODE
cprintf("cond_wait begin: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
/*
* cv.count ++;
* if(mt.next_count>0)
* signal(mt.next)
* else
* signal(mt.mutex);
* wait(cv.sem);
* cv.count --;
*/
cvp->count++;
if(cvp->owner->next_count > 0)
up(&(cvp->owner->next));
else
up(&(cvp->owner->mutex));
down(&(cvp->sem));
cvp->count --;
cprintf("cond_wait end: cvp %x, cvp->count %d, cvp->owner->next_count %d\n", cvp, cvp->count, cvp->owner->next_count);
}

View File

@@ -0,0 +1,90 @@
#ifndef __KERN_SYNC_MONITOR_CONDVAR_H__
#define __KERN_SYNC_MOINTOR_CONDVAR_H__
#include <sem.h>
/* In [OS CONCEPT] 7.7 section, the accurate define and approximate implementation of MONITOR was introduced.
* INTRODUCTION:
* Monitors were invented by C. A. R. Hoare and Per Brinch Hansen, and were first implemented in Brinch Hansen's
* Concurrent Pascal language. Generally, a monitor is a language construct and the compiler usually enforces mutual exclusion. Compare this with semaphores, which are usually an OS construct.
* DEFNIE & CHARACTERISTIC:
* A monitor is a collection of procedures, variables, and data structures grouped together.
* Processes can call the monitor procedures but cannot access the internal data structures.
* Only one process at a time may be be active in a monitor.
* Condition variables allow for blocking and unblocking.
* cv.wait() blocks a process.
* The process is said to be waiting for (or waiting on) the condition variable cv.
* cv.signal() (also called cv.notify) unblocks a process waiting for the condition variable cv.
* When this occurs, we need to still require that only one process is active in the monitor. This can be done in several ways:
* on some systems the old process (the one executing the signal) leaves the monitor and the new one enters
* on some systems the signal must be the last statement executed inside the monitor.
* on some systems the old process will block until the monitor is available again.
* on some systems the new process (the one unblocked by the signal) will remain blocked until the monitor is available again.
* If a condition variable is signaled with nobody waiting, the signal is lost. Compare this with semaphores, in which a signal will allow a process that executes a wait in the future to no block.
* You should not think of a condition variable as a variable in the traditional sense.
* It does not have a value.
* Think of it as an object in the OOP sense.
* It has two methods, wait and signal that manipulate the calling process.
* IMPLEMENTATION:
* monitor mt {
* ----------------variable------------------
* semaphore mutex;
* semaphore next;
* int next_count;
* condvar {int count, sempahore sem} cv[N];
* other variables in mt;
* --------condvar wait/signal---------------
* cond_wait (cv) {
* cv.count ++;
* if(mt.next_count>0)
* signal(mt.next)
* else
* signal(mt.mutex);
* wait(cv.sem);
* cv.count --;
* }
*
* cond_signal(cv) {
* if(cv.count>0) {
* mt.next_count ++;
* signal(cv.sem);
* wait(mt.next);
* mt.next_count--;
* }
* }
* --------routines in monitor---------------
* routineA_in_mt () {
* wait(mt.mutex);
* ...
* real body of routineA
* ...
* if(next_count>0)
* signal(mt.next);
* else
* signal(mt.mutex);
* }
*/
typedef struct monitor monitor_t;
typedef struct condvar{
semaphore_t sem; // the sem semaphore is used to down the waiting proc, and the signaling proc should up the waiting proc
int count; // the number of waiters on condvar
monitor_t * owner; // the owner(monitor) of this condvar
} condvar_t;
typedef struct monitor{
semaphore_t mutex; // the mutex lock for going into the routines in monitor, should be initialized to 1
semaphore_t next; // the next semaphore is used to down the signaling proc itself, and the other OR wakeuped waiting proc should wake up the sleeped signaling proc.
int next_count; // the number of of sleeped signaling proc
condvar_t *cv; // the condvars in monitor
} monitor_t;
// Initialize variables in monitor.
void monitor_init (monitor_t *cvp, size_t num_cv);
// Unlock one of threads waiting on the condition variable.
void cond_signal (condvar_t *cvp);
// Suspend calling thread on a condition variable waiting for condition atomically unlock mutex in monitor,
// and suspends calling thread on conditional variable after waking up locks mutex.
void cond_wait (condvar_t *cvp);
#endif /* !__KERN_SYNC_MONITOR_CONDVAR_H__ */

View File

@@ -0,0 +1,77 @@
#include <defs.h>
#include <wait.h>
#include <atomic.h>
#include <kmalloc.h>
#include <sem.h>
#include <proc.h>
#include <sync.h>
#include <assert.h>
void
sem_init(semaphore_t *sem, int value) {
sem->value = value;
wait_queue_init(&(sem->wait_queue));
}
static __noinline void __up(semaphore_t *sem, uint32_t wait_state) {
bool intr_flag;
local_intr_save(intr_flag);
{
wait_t *wait;
if ((wait = wait_queue_first(&(sem->wait_queue))) == NULL) {
sem->value ++;
}
else {
assert(wait->proc->wait_state == wait_state);
wakeup_wait(&(sem->wait_queue), wait, wait_state, 1);
}
}
local_intr_restore(intr_flag);
}
static __noinline uint32_t __down(semaphore_t *sem, uint32_t wait_state) {
bool intr_flag;
local_intr_save(intr_flag);
if (sem->value > 0) {
sem->value --;
local_intr_restore(intr_flag);
return 0;
}
wait_t __wait, *wait = &__wait;
wait_current_set(&(sem->wait_queue), wait, wait_state);
local_intr_restore(intr_flag);
schedule();
local_intr_save(intr_flag);
wait_current_del(&(sem->wait_queue), wait);
local_intr_restore(intr_flag);
if (wait->wakeup_flags != wait_state) {
return wait->wakeup_flags;
}
return 0;
}
void
up(semaphore_t *sem) {
__up(sem, WT_KSEM);
}
void
down(semaphore_t *sem) {
uint32_t flags = __down(sem, WT_KSEM);
assert(flags == 0);
}
bool
try_down(semaphore_t *sem) {
bool intr_flag, ret = 0;
local_intr_save(intr_flag);
if (sem->value > 0) {
sem->value --, ret = 1;
}
local_intr_restore(intr_flag);
return ret;
}

View File

@@ -0,0 +1,19 @@
#ifndef __KERN_SYNC_SEM_H__
#define __KERN_SYNC_SEM_H__
#include <defs.h>
#include <atomic.h>
#include <wait.h>
typedef struct {
int value;
wait_queue_t wait_queue;
} semaphore_t;
void sem_init(semaphore_t *sem, int value);
void up(semaphore_t *sem);
void down(semaphore_t *sem);
bool try_down(semaphore_t *sem);
#endif /* !__KERN_SYNC_SEM_H__ */

View File

@@ -0,0 +1,31 @@
#ifndef __KERN_SYNC_SYNC_H__
#define __KERN_SYNC_SYNC_H__
#include <x86.h>
#include <intr.h>
#include <mmu.h>
#include <assert.h>
#include <atomic.h>
#include <sched.h>
static inline bool
__intr_save(void) {
if (read_eflags() & FL_IF) {
intr_disable();
return 1;
}
return 0;
}
static inline void
__intr_restore(bool flag) {
if (flag) {
intr_enable();
}
}
#define local_intr_save(x) do { x = __intr_save(); } while (0)
#define local_intr_restore(x) __intr_restore(x);
#endif /* !__KERN_SYNC_SYNC_H__ */

View File

@@ -0,0 +1,122 @@
#include <defs.h>
#include <list.h>
#include <sync.h>
#include <wait.h>
#include <proc.h>
void
wait_init(wait_t *wait, struct proc_struct *proc) {
wait->proc = proc;
wait->wakeup_flags = WT_INTERRUPTED;
list_init(&(wait->wait_link));
}
void
wait_queue_init(wait_queue_t *queue) {
list_init(&(queue->wait_head));
}
void
wait_queue_add(wait_queue_t *queue, wait_t *wait) {
assert(list_empty(&(wait->wait_link)) && wait->proc != NULL);
wait->wait_queue = queue;
list_add_before(&(queue->wait_head), &(wait->wait_link));
}
void
wait_queue_del(wait_queue_t *queue, wait_t *wait) {
assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue);
list_del_init(&(wait->wait_link));
}
wait_t *
wait_queue_next(wait_queue_t *queue, wait_t *wait) {
assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue);
list_entry_t *le = list_next(&(wait->wait_link));
if (le != &(queue->wait_head)) {
return le2wait(le, wait_link);
}
return NULL;
}
wait_t *
wait_queue_prev(wait_queue_t *queue, wait_t *wait) {
assert(!list_empty(&(wait->wait_link)) && wait->wait_queue == queue);
list_entry_t *le = list_prev(&(wait->wait_link));
if (le != &(queue->wait_head)) {
return le2wait(le, wait_link);
}
return NULL;
}
wait_t *
wait_queue_first(wait_queue_t *queue) {
list_entry_t *le = list_next(&(queue->wait_head));
if (le != &(queue->wait_head)) {
return le2wait(le, wait_link);
}
return NULL;
}
wait_t *
wait_queue_last(wait_queue_t *queue) {
list_entry_t *le = list_prev(&(queue->wait_head));
if (le != &(queue->wait_head)) {
return le2wait(le, wait_link);
}
return NULL;
}
bool
wait_queue_empty(wait_queue_t *queue) {
return list_empty(&(queue->wait_head));
}
bool
wait_in_queue(wait_t *wait) {
return !list_empty(&(wait->wait_link));
}
void
wakeup_wait(wait_queue_t *queue, wait_t *wait, uint32_t wakeup_flags, bool del) {
if (del) {
wait_queue_del(queue, wait);
}
wait->wakeup_flags = wakeup_flags;
wakeup_proc(wait->proc);
}
void
wakeup_first(wait_queue_t *queue, uint32_t wakeup_flags, bool del) {
wait_t *wait;
if ((wait = wait_queue_first(queue)) != NULL) {
wakeup_wait(queue, wait, wakeup_flags, del);
}
}
void
wakeup_queue(wait_queue_t *queue, uint32_t wakeup_flags, bool del) {
wait_t *wait;
if ((wait = wait_queue_first(queue)) != NULL) {
if (del) {
do {
wakeup_wait(queue, wait, wakeup_flags, 1);
} while ((wait = wait_queue_first(queue)) != NULL);
}
else {
do {
wakeup_wait(queue, wait, wakeup_flags, 0);
} while ((wait = wait_queue_next(queue, wait)) != NULL);
}
}
}
void
wait_current_set(wait_queue_t *queue, wait_t *wait, uint32_t wait_state) {
assert(current != NULL);
wait_init(wait, current);
current->state = PROC_SLEEPING;
current->wait_state = wait_state;
wait_queue_add(queue, wait);
}

View File

@@ -0,0 +1,48 @@
#ifndef __KERN_SYNC_WAIT_H__
#define __KERN_SYNC_WAIT_H__
#include <list.h>
typedef struct {
list_entry_t wait_head;
} wait_queue_t;
struct proc_struct;
typedef struct {
struct proc_struct *proc;
uint32_t wakeup_flags;
wait_queue_t *wait_queue;
list_entry_t wait_link;
} wait_t;
#define le2wait(le, member) \
to_struct((le), wait_t, member)
void wait_init(wait_t *wait, struct proc_struct *proc);
void wait_queue_init(wait_queue_t *queue);
void wait_queue_add(wait_queue_t *queue, wait_t *wait);
void wait_queue_del(wait_queue_t *queue, wait_t *wait);
wait_t *wait_queue_next(wait_queue_t *queue, wait_t *wait);
wait_t *wait_queue_prev(wait_queue_t *queue, wait_t *wait);
wait_t *wait_queue_first(wait_queue_t *queue);
wait_t *wait_queue_last(wait_queue_t *queue);
bool wait_queue_empty(wait_queue_t *queue);
bool wait_in_queue(wait_t *wait);
void wakeup_wait(wait_queue_t *queue, wait_t *wait, uint32_t wakeup_flags, bool del);
void wakeup_first(wait_queue_t *queue, uint32_t wakeup_flags, bool del);
void wakeup_queue(wait_queue_t *queue, uint32_t wakeup_flags, bool del);
void wait_current_set(wait_queue_t *queue, wait_t *wait, uint32_t wait_state);
#define wait_current_del(queue, wait) \
do { \
if (wait_in_queue(wait)) { \
wait_queue_del(queue, wait); \
} \
} while (0)
#endif /* !__KERN_SYNC_WAIT_H__ */

View File

@@ -0,0 +1,207 @@
#include <defs.h>
#include <unistd.h>
#include <proc.h>
#include <syscall.h>
#include <trap.h>
#include <stdio.h>
#include <pmm.h>
#include <assert.h>
#include <clock.h>
#include <stat.h>
#include <dirent.h>
#include <sysfile.h>
static int
sys_exit(uint32_t arg[]) {
int error_code = (int)arg[0];
return do_exit(error_code);
}
static int
sys_fork(uint32_t arg[]) {
struct trapframe *tf = current->tf;
uintptr_t stack = tf->tf_esp;
return do_fork(0, stack, tf);
}
static int
sys_wait(uint32_t arg[]) {
int pid = (int)arg[0];
int *store = (int *)arg[1];
return do_wait(pid, store);
}
static int
sys_exec(uint32_t arg[]) {
const char *name = (const char *)arg[0];
int argc = (int)arg[1];
const char **argv = (const char **)arg[2];
return do_execve(name, argc, argv);
}
static int
sys_yield(uint32_t arg[]) {
return do_yield();
}
static int
sys_kill(uint32_t arg[]) {
int pid = (int)arg[0];
return do_kill(pid);
}
static int
sys_getpid(uint32_t arg[]) {
return current->pid;
}
static int
sys_putc(uint32_t arg[]) {
int c = (int)arg[0];
cputchar(c);
return 0;
}
static int
sys_pgdir(uint32_t arg[]) {
print_pgdir();
return 0;
}
static uint32_t
sys_gettime(uint32_t arg[]) {
return (int)ticks;
}
static uint32_t
sys_lab6_set_priority(uint32_t arg[])
{
uint32_t priority = (uint32_t)arg[0];
lab6_set_priority(priority);
return 0;
}
static int
sys_sleep(uint32_t arg[]) {
unsigned int time = (unsigned int)arg[0];
return do_sleep(time);
}
static int
sys_open(uint32_t arg[]) {
const char *path = (const char *)arg[0];
uint32_t open_flags = (uint32_t)arg[1];
return sysfile_open(path, open_flags);
}
static int
sys_close(uint32_t arg[]) {
int fd = (int)arg[0];
return sysfile_close(fd);
}
static int
sys_read(uint32_t arg[]) {
int fd = (int)arg[0];
void *base = (void *)arg[1];
size_t len = (size_t)arg[2];
return sysfile_read(fd, base, len);
}
static int
sys_write(uint32_t arg[]) {
int fd = (int)arg[0];
void *base = (void *)arg[1];
size_t len = (size_t)arg[2];
return sysfile_write(fd, base, len);
}
static int
sys_seek(uint32_t arg[]) {
int fd = (int)arg[0];
off_t pos = (off_t)arg[1];
int whence = (int)arg[2];
return sysfile_seek(fd, pos, whence);
}
static int
sys_fstat(uint32_t arg[]) {
int fd = (int)arg[0];
struct stat *stat = (struct stat *)arg[1];
return sysfile_fstat(fd, stat);
}
static int
sys_fsync(uint32_t arg[]) {
int fd = (int)arg[0];
return sysfile_fsync(fd);
}
static int
sys_getcwd(uint32_t arg[]) {
char *buf = (char *)arg[0];
size_t len = (size_t)arg[1];
return sysfile_getcwd(buf, len);
}
static int
sys_getdirentry(uint32_t arg[]) {
int fd = (int)arg[0];
struct dirent *direntp = (struct dirent *)arg[1];
return sysfile_getdirentry(fd, direntp);
}
static int
sys_dup(uint32_t arg[]) {
int fd1 = (int)arg[0];
int fd2 = (int)arg[1];
return sysfile_dup(fd1, fd2);
}
static int (*syscalls[])(uint32_t arg[]) = {
[SYS_exit] sys_exit,
[SYS_fork] sys_fork,
[SYS_wait] sys_wait,
[SYS_exec] sys_exec,
[SYS_yield] sys_yield,
[SYS_kill] sys_kill,
[SYS_getpid] sys_getpid,
[SYS_putc] sys_putc,
[SYS_pgdir] sys_pgdir,
[SYS_gettime] sys_gettime,
[SYS_lab6_set_priority] sys_lab6_set_priority,
[SYS_sleep] sys_sleep,
[SYS_open] sys_open,
[SYS_close] sys_close,
[SYS_read] sys_read,
[SYS_write] sys_write,
[SYS_seek] sys_seek,
[SYS_fstat] sys_fstat,
[SYS_fsync] sys_fsync,
[SYS_getcwd] sys_getcwd,
[SYS_getdirentry] sys_getdirentry,
[SYS_dup] sys_dup,
};
#define NUM_SYSCALLS ((sizeof(syscalls)) / (sizeof(syscalls[0])))
void
syscall(void) {
struct trapframe *tf = current->tf;
uint32_t arg[5];
int num = tf->tf_regs.reg_eax;
if (num >= 0 && num < NUM_SYSCALLS) {
if (syscalls[num] != NULL) {
arg[0] = tf->tf_regs.reg_edx;
arg[1] = tf->tf_regs.reg_ecx;
arg[2] = tf->tf_regs.reg_ebx;
arg[3] = tf->tf_regs.reg_edi;
arg[4] = tf->tf_regs.reg_esi;
tf->tf_regs.reg_eax = syscalls[num](arg);
return ;
}
}
print_trapframe(tf);
panic("undefined syscall %d, pid = %d, name = %s.\n",
num, current->pid, current->name);
}

View File

@@ -0,0 +1,7 @@
#ifndef __KERN_SYSCALL_SYSCALL_H__
#define __KERN_SYSCALL_SYSCALL_H__
void syscall(void);
#endif /* !__KERN_SYSCALL_SYSCALL_H__ */

View File

@@ -0,0 +1,311 @@
#include <defs.h>
#include <mmu.h>
#include <memlayout.h>
#include <clock.h>
#include <trap.h>
#include <x86.h>
#include <stdio.h>
#include <assert.h>
#include <console.h>
#include <vmm.h>
#include <swap.h>
#include <kdebug.h>
#include <unistd.h>
#include <syscall.h>
#include <error.h>
#include <sched.h>
#include <sync.h>
#include <proc.h>
#define TICK_NUM 100
static void print_ticks() {
cprintf("%d ticks\n",TICK_NUM);
#ifdef DEBUG_GRADE
cprintf("End of Test.\n");
panic("EOT: kernel seems ok.");
#endif
}
/* *
* Interrupt descriptor table:
*
* Must be built at run time because shifted function addresses can't
* be represented in relocation records.
* */
static struct gatedesc idt[256] = {{0}};
static struct pseudodesc idt_pd = {
sizeof(idt) - 1, (uintptr_t)idt
};
/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
void
idt_init(void) {
/* LAB1 YOUR CODE : STEP 2 */
/* (1) Where are the entry addrs of each Interrupt Service Routine (ISR)?
* All ISR's entry addrs are stored in __vectors. where is uintptr_t __vectors[] ?
* __vectors[] is in kern/trap/vector.S which is produced by tools/vector.c
* (try "make" command in lab1, then you will find vector.S in kern/trap DIR)
* You can use "extern uintptr_t __vectors[];" to define this extern variable which will be used later.
* (2) Now you should setup the entries of ISR in Interrupt Description Table (IDT).
* Can you see idt[256] in this file? Yes, it's IDT! you can use SETGATE macro to setup each item of IDT
* (3) After setup the contents of IDT, you will let CPU know where is the IDT by using 'lidt' instruction.
* You don't know the meaning of this instruction? just google it! and check the libs/x86.h to know more.
* Notice: the argument of lidt is idt_pd. try to find it!
*/
/* LAB5 YOUR CODE */
//you should update your lab1 code (just add ONE or TWO lines of code), let user app to use syscall to get the service of ucore
//so you should setup the syscall interrupt gate in here
extern uintptr_t __vectors[];
int i;
for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
}
SETGATE(idt[T_SYSCALL], 1, GD_KTEXT, __vectors[T_SYSCALL], DPL_USER);
lidt(&idt_pd);
}
static const char *
trapname(int trapno) {
static const char * const excnames[] = {
"Divide error",
"Debug",
"Non-Maskable Interrupt",
"Breakpoint",
"Overflow",
"BOUND Range Exceeded",
"Invalid Opcode",
"Device Not Available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack Fault",
"General Protection",
"Page Fault",
"(unknown trap)",
"x87 FPU Floating-Point Error",
"Alignment Check",
"Machine-Check",
"SIMD Floating-Point Exception"
};
if (trapno < sizeof(excnames)/sizeof(const char * const)) {
return excnames[trapno];
}
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) {
return "Hardware Interrupt";
}
return "(unknown trap)";
}
/* trap_in_kernel - test if trap happened in kernel */
bool
trap_in_kernel(struct trapframe *tf) {
return (tf->tf_cs == (uint16_t)KERNEL_CS);
}
static const char *IA32flags[] = {
"CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF",
"TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL,
"RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL,
};
void
print_trapframe(struct trapframe *tf) {
cprintf("trapframe at %p\n", tf);
print_regs(&tf->tf_regs);
cprintf(" ds 0x----%04x\n", tf->tf_ds);
cprintf(" es 0x----%04x\n", tf->tf_es);
cprintf(" fs 0x----%04x\n", tf->tf_fs);
cprintf(" gs 0x----%04x\n", tf->tf_gs);
cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
cprintf(" err 0x%08x\n", tf->tf_err);
cprintf(" eip 0x%08x\n", tf->tf_eip);
cprintf(" cs 0x----%04x\n", tf->tf_cs);
cprintf(" flag 0x%08x ", tf->tf_eflags);
int i, j;
for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) {
if ((tf->tf_eflags & j) && IA32flags[i] != NULL) {
cprintf("%s,", IA32flags[i]);
}
}
cprintf("IOPL=%d\n", (tf->tf_eflags & FL_IOPL_MASK) >> 12);
if (!trap_in_kernel(tf)) {
cprintf(" esp 0x%08x\n", tf->tf_esp);
cprintf(" ss 0x----%04x\n", tf->tf_ss);
}
}
void
print_regs(struct pushregs *regs) {
cprintf(" edi 0x%08x\n", regs->reg_edi);
cprintf(" esi 0x%08x\n", regs->reg_esi);
cprintf(" ebp 0x%08x\n", regs->reg_ebp);
cprintf(" oesp 0x%08x\n", regs->reg_oesp);
cprintf(" ebx 0x%08x\n", regs->reg_ebx);
cprintf(" edx 0x%08x\n", regs->reg_edx);
cprintf(" ecx 0x%08x\n", regs->reg_ecx);
cprintf(" eax 0x%08x\n", regs->reg_eax);
}
static inline void
print_pgfault(struct trapframe *tf) {
/* error_code:
* bit 0 == 0 means no page found, 1 means protection fault
* bit 1 == 0 means read, 1 means write
* bit 2 == 0 means kernel, 1 means user
* */
cprintf("page fault at 0x%08x: %c/%c [%s].\n", rcr2(),
(tf->tf_err & 4) ? 'U' : 'K',
(tf->tf_err & 2) ? 'W' : 'R',
(tf->tf_err & 1) ? "protection fault" : "no page found");
}
static int
pgfault_handler(struct trapframe *tf) {
extern struct mm_struct *check_mm_struct;
if(check_mm_struct !=NULL) { //used for test check_swap
print_pgfault(tf);
}
struct mm_struct *mm;
if (check_mm_struct != NULL) {
assert(current == idleproc);
mm = check_mm_struct;
}
else {
if (current == NULL) {
print_trapframe(tf);
print_pgfault(tf);
panic("unhandled page fault.\n");
}
mm = current->mm;
}
return do_pgfault(mm, tf->tf_err, rcr2());
}
static volatile int in_swap_tick_event = 0;
extern struct mm_struct *check_mm_struct;
static void
trap_dispatch(struct trapframe *tf) {
char c;
int ret=0;
switch (tf->tf_trapno) {
case T_PGFLT: //page fault
if ((ret = pgfault_handler(tf)) != 0) {
print_trapframe(tf);
if (current == NULL) {
panic("handle pgfault failed. ret=%d\n", ret);
}
else {
if (trap_in_kernel(tf)) {
panic("handle pgfault failed in kernel mode. ret=%d\n", ret);
}
cprintf("killed by kernel.\n");
panic("handle user mode pgfault failed. ret=%d\n", ret);
do_exit(-E_KILLED);
}
}
break;
case T_SYSCALL:
syscall();
break;
case IRQ_OFFSET + IRQ_TIMER:
#if 0
LAB3 : If some page replacement algorithm(such as CLOCK PRA) need tick to change the priority of pages,
then you can add code here.
#endif
/* LAB1 YOUR CODE : STEP 3 */
/* handle the timer interrupt */
/* (1) After a timer interrupt, you should record this event using a global variable (increase it), such as ticks in kern/driver/clock.c
* (2) Every TICK_NUM cycle, you can print some info using a funciton, such as print_ticks().
* (3) Too Simple? Yes, I think so!
*/
/* LAB5 YOUR CODE */
/* you should upate you lab1 code (just add ONE or TWO lines of code):
* Every TICK_NUM cycle, you should set current process's current->need_resched = 1
*/
/* LAB6 YOUR CODE */
/* IMPORTANT FUNCTIONS:
* run_timer_list
*----------------------
* you should update your lab5 code (just add ONE or TWO lines of code):
* Every tick, you should update the system time, iterate the timers, and trigger the timers which are end to call scheduler.
* You can use one funcitons to finish all these things.
*/
ticks ++;
assert(current != NULL);
run_timer_list();
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);
{
extern void dev_stdin_write(char c);
dev_stdin_write(c);
}
break;
//LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes.
case T_SWITCH_TOU:
case T_SWITCH_TOK:
panic("T_SWITCH_** ??\n");
break;
case IRQ_OFFSET + IRQ_IDE1:
case IRQ_OFFSET + IRQ_IDE2:
/* do nothing */
break;
default:
print_trapframe(tf);
if (current != NULL) {
cprintf("unhandled trap.\n");
do_exit(-E_KILLED);
}
// in kernel, it must be a mistake
panic("unexpected trap in kernel.\n");
}
}
/* *
* trap - handles or dispatches an exception/interrupt. if and when trap() returns,
* the code in kern/trap/trapentry.S restores the old CPU state saved in the
* trapframe and then uses the iret instruction to return from the exception.
* */
void
trap(struct trapframe *tf) {
// dispatch based on what type of trap occurred
// used for previous projects
if (current == NULL) {
trap_dispatch(tf);
}
else {
// keep a trapframe chain in stack
struct trapframe *otf = current->tf;
current->tf = tf;
bool in_kernel = trap_in_kernel(tf);
trap_dispatch(tf);
current->tf = otf;
if (!in_kernel) {
if (current->flags & PF_EXITING) {
do_exit(-E_KILLED);
}
if (current->need_resched) {
schedule();
}
}
}
}

View File

@@ -0,0 +1,89 @@
#ifndef __KERN_TRAP_TRAP_H__
#define __KERN_TRAP_TRAP_H__
#include <defs.h>
/* Trap Numbers */
/* Processor-defined: */
#define T_DIVIDE 0 // divide error
#define T_DEBUG 1 // debug exception
#define T_NMI 2 // non-maskable interrupt
#define T_BRKPT 3 // breakpoint
#define T_OFLOW 4 // overflow
#define T_BOUND 5 // bounds check
#define T_ILLOP 6 // illegal opcode
#define T_DEVICE 7 // device not available
#define T_DBLFLT 8 // double fault
// #define T_COPROC 9 // reserved (not used since 486)
#define T_TSS 10 // invalid task switch segment
#define T_SEGNP 11 // segment not present
#define T_STACK 12 // stack exception
#define T_GPFLT 13 // general protection fault
#define T_PGFLT 14 // page fault
// #define T_RES 15 // reserved
#define T_FPERR 16 // floating point error
#define T_ALIGN 17 // aligment check
#define T_MCHK 18 // machine check
#define T_SIMDERR 19 // SIMD floating point error
/* Hardware IRQ numbers. We receive these as (IRQ_OFFSET + IRQ_xx) */
#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET
#define IRQ_TIMER 0
#define IRQ_KBD 1
#define IRQ_COM1 4
#define IRQ_IDE1 14
#define IRQ_IDE2 15
#define IRQ_ERROR 19
#define IRQ_SPURIOUS 31
/* *
* These are arbitrarily chosen, but with care not to overlap
* processor defined exceptions or interrupt vectors.
* */
#define T_SWITCH_TOU 120 // user/kernel switch
#define T_SWITCH_TOK 121 // user/kernel switch
/* registers as pushed by pushal */
struct pushregs {
uint32_t reg_edi;
uint32_t reg_esi;
uint32_t reg_ebp;
uint32_t reg_oesp; /* Useless */
uint32_t reg_ebx;
uint32_t reg_edx;
uint32_t reg_ecx;
uint32_t reg_eax;
};
struct trapframe {
struct pushregs tf_regs;
uint16_t tf_gs;
uint16_t tf_padding0;
uint16_t tf_fs;
uint16_t tf_padding1;
uint16_t tf_es;
uint16_t tf_padding2;
uint16_t tf_ds;
uint16_t tf_padding3;
uint32_t tf_trapno;
/* below here defined by x86 hardware */
uint32_t tf_err;
uintptr_t tf_eip;
uint16_t tf_cs;
uint16_t tf_padding4;
uint32_t tf_eflags;
/* below here only when crossing rings, such as from user to kernel */
uintptr_t tf_esp;
uint16_t tf_ss;
uint16_t tf_padding5;
} __attribute__((packed));
void idt_init(void);
void print_trapframe(struct trapframe *tf);
void print_regs(struct pushregs *regs);
bool trap_in_kernel(struct trapframe *tf);
#endif /* !__KERN_TRAP_TRAP_H__ */

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
#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_and_set_bit(int nr, volatile void *addr) __attribute__((always_inline));
static inline bool test_and_clear_bit(int nr, volatile void *addr) __attribute__((always_inline));
static inline bool test_bit(int nr, volatile void *addr) __attribute__((always_inline));
/* *
* set_bit - Atomically set a bit in memory
* @nr: the bit to set
* @addr: the address to start counting from
*
* Note that @nr may be almost arbitrarily large; this function is not
* restricted to acting on a single-word quantity.
* */
static inline void
set_bit(int nr, volatile void *addr) {
asm volatile ("btsl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr));
}
/* *
* clear_bit - Atomically clears a bit in memory
* @nr: the bit to clear
* @addr: the address to start counting from
* */
static inline void
clear_bit(int nr, volatile void *addr) {
asm volatile ("btrl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr));
}
/* *
* change_bit - Atomically toggle a bit in memory
* @nr: the bit to change
* @addr: the address to start counting from
* */
static inline void
change_bit(int nr, volatile void *addr) {
asm volatile ("btcl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr));
}
/* *
* test_bit - Determine whether a bit is set
* @nr: the bit to test
* @addr: the address to count from
* */
static inline bool
test_bit(int nr, volatile void *addr) {
int oldbit;
asm volatile ("btl %2, %1; sbbl %0,%0" : "=r" (oldbit) : "m" (*(volatile long *)addr), "Ir" (nr));
return oldbit != 0;
}
/* *
* test_and_set_bit - Atomically set a bit and return its old value
* @nr: the bit to set
* @addr: the address to count from
* */
static inline bool
test_and_set_bit(int nr, volatile void *addr) {
int oldbit;
asm volatile ("btsl %2, %1; sbbl %0, %0" : "=r" (oldbit), "=m" (*(volatile long *)addr) : "Ir" (nr) : "memory");
return oldbit != 0;
}
/* *
* test_and_clear_bit - Atomically clear a bit and return its old value
* @nr: the bit to clear
* @addr: the address to count from
* */
static inline bool
test_and_clear_bit(int nr, volatile void *addr) {
int oldbit;
asm volatile ("btrl %2, %1; sbbl %0, %0" : "=r" (oldbit), "=m" (*(volatile long *)addr) : "Ir" (nr) : "memory");
return oldbit != 0;
}
#endif /* !__LIBS_ATOMIC_H__ */

View File

@@ -0,0 +1,79 @@
#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))
#define CHAR_BIT 8
/* 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;
/* off_t is used for file offsets and lengths */
typedef intptr_t off_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)); \
})
/* Round up the result of dividing of n */
#define ROUNDUP_DIV(a, n) ({ \
uint32_t __n = (uint32_t)(n); \
(typeof(a))(((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__ */

View File

@@ -0,0 +1,13 @@
#ifndef __LIBS_DIRENT_H__
#define __LIBS_DIRENT_H__
#include <defs.h>
#include <unistd.h>
struct dirent {
off_t offset;
char name[FS_MAX_FNAME_LEN + 1];
};
#endif /* !__LIBS_DIRENT_H__ */

View File

@@ -0,0 +1,48 @@
#ifndef __LIBS_ELF_H__
#define __LIBS_ELF_H__
#include <defs.h>
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian
/* file header */
struct elfhdr {
uint32_t e_magic; // must equal ELF_MAGIC
uint8_t e_elf[12];
uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core image
uint16_t e_machine; // 3=x86, 4=68K, etc.
uint32_t e_version; // file version, always 1
uint32_t e_entry; // entry point if executable
uint32_t e_phoff; // file position of program header or 0
uint32_t e_shoff; // file position of section header or 0
uint32_t e_flags; // architecture-specific flags, usually 0
uint16_t e_ehsize; // size of this elf header
uint16_t e_phentsize; // size of an entry in program header
uint16_t e_phnum; // number of entries in program header or 0
uint16_t e_shentsize; // size of an entry in section header
uint16_t e_shnum; // number of entries in section header or 0
uint16_t e_shstrndx; // section number that contains section name strings
};
/* program section header */
struct proghdr {
uint32_t p_type; // loadable code or data, dynamic linking info,etc.
uint32_t p_offset; // file offset of segment
uint32_t p_va; // virtual address to map segment
uint32_t p_pa; // physical address, not used
uint32_t p_filesz; // size of segment in file
uint32_t p_memsz; // size of segment in memory (bigger if contains bss
uint32_t p_flags; // read/write/execute bits
uint32_t p_align; // required alignment, invariably hardware page size
};
/* values for Proghdr::p_type */
#define ELF_PT_LOAD 1
/* flag bits for Proghdr::p_flags */
#define ELF_PF_X 1
#define ELF_PF_W 2
#define ELF_PF_R 4
#endif /* !__LIBS_ELF_H__ */

View File

@@ -0,0 +1,33 @@
#ifndef __LIBS_ERROR_H__
#define __LIBS_ERROR_H__
/* kernel error codes -- keep in sync with list in lib/printfmt.c */
#define E_UNSPECIFIED 1 // Unspecified or unknown problem
#define E_BAD_PROC 2 // Process doesn't exist or otherwise
#define E_INVAL 3 // Invalid parameter
#define E_NO_MEM 4 // Request failed due to memory shortage
#define E_NO_FREE_PROC 5 // Attempt to create a new process beyond
#define E_FAULT 6 // Memory fault
#define E_SWAP_FAULT 7 // SWAP READ/WRITE fault
#define E_INVAL_ELF 8 // Invalid elf file
#define E_KILLED 9 // Process is killed
#define E_PANIC 10 // Panic Failure
#define E_TIMEOUT 11 // Timeout
#define E_TOO_BIG 12 // Argument is Too Big
#define E_NO_DEV 13 // No such Device
#define E_NA_DEV 14 // Device Not Available
#define E_BUSY 15 // Device/File is Busy
#define E_NOENT 16 // No Such File or Directory
#define E_ISDIR 17 // Is a Directory
#define E_NOTDIR 18 // Not a Directory
#define E_XDEV 19 // Cross Device-Link
#define E_UNIMP 20 // Unimplemented Feature
#define E_SEEK 21 // Illegal Seek
#define E_MAX_OPEN 22 // Too Many Files are Open
#define E_EXISTS 23 // File/Directory Already Exists
#define E_NOTEMPTY 24 // Directory is Not Empty
/* the maximum allowed */
#define MAXERROR 24
#endif /* !__LIBS_ERROR_H__ */

Some files were not shown because too many files have changed in this diff Show More