@ -0,0 +1,250 @@ | |||||
PROJ := challenge | |||||
EMPTY := | |||||
SPACE := $(EMPTY) $(EMPTY) | |||||
SLASH := / | |||||
V := @ | |||||
# try to infer the correct GCCPREFX | |||||
ifndef GCCPREFIX | |||||
GCCPREFIX := $(shell if i386-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \ | |||||
then echo 'i386-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-elf version of GCC/binutils." 1>&2; \ | |||||
echo "*** Is the directory with i386-elf-gcc in your PATH?" 1>&2; \ | |||||
echo "*** If your i386-elf toolchain is installed with a command" 1>&2; \ | |||||
echo "*** prefix other than 'i386-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-elf-qemu > /dev/null; \ | |||||
then echo 'i386-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 | |||||
CC := $(GCCPREFIX)gcc | |||||
CFLAGS := -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc $(DEFS) | |||||
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) | |||||
CTYPE := c S | |||||
LD := $(GCCPREFIX)ld | |||||
LDFLAGS := -m $(shell $(LD) -V | grep elf_i386 2>/dev/null) | |||||
LDFLAGS += -nostdlib | |||||
OBJCOPY := $(GCCPREFIX)objcopy | |||||
OBJDUMP := $(GCCPREFIX)objdump | |||||
COPY := cp | |||||
MKDIR := mkdir -p | |||||
MV := mv | |||||
RM := rm -f | |||||
AWK := awk | |||||
SED := sed | |||||
SH := sh | |||||
TR := tr | |||||
TOUCH := touch -c | |||||
OBJDIR := obj | |||||
BINDIR := bin | |||||
ALLOBJS := | |||||
ALLDEPS := | |||||
TARGETS := | |||||
include tools/function.mk | |||||
listf_cc = $(call listf,$(1),$(CTYPE)) | |||||
# for cc | |||||
add_files_cc = $(call add_files,$(1),$(CC),$(CFLAGS) $(3),$(2),$(4)) | |||||
create_target_cc = $(call create_target,$(1),$(2),$(3),$(CC),$(CFLAGS)) | |||||
# for hostcc | |||||
add_files_host = $(call add_files,$(1),$(HOSTCC),$(HOSTCFLAGS),$(2),$(3)) | |||||
create_target_host = $(call create_target,$(1),$(2),$(3),$(HOSTCC),$(HOSTCFLAGS)) | |||||
cgtype = $(patsubst %.$(2),%.$(3),$(1)) | |||||
objfile = $(call toobj,$(1)) | |||||
asmfile = $(call cgtype,$(call toobj,$(1)),o,asm) | |||||
outfile = $(call cgtype,$(call toobj,$(1)),o,out) | |||||
symfile = $(call cgtype,$(call toobj,$(1)),o,sym) | |||||
# for match pattern | |||||
match = $(shell echo $(2) | $(AWK) '{for(i=1;i<=NF;i++){if(match("$(1)","^"$$(i)"$$")){exit 1;}}}'; echo $$?) | |||||
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | |||||
# include kernel/user | |||||
INCLUDE += libs/ | |||||
CFLAGS += $(addprefix -I,$(INCLUDE)) | |||||
LIBDIR += libs | |||||
$(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,) | |||||
# ------------------------------------------------------------------- | |||||
# kernel | |||||
KINCLUDE += kern/debug/ \ | |||||
kern/driver/ \ | |||||
kern/trap/ \ | |||||
kern/mm/ | |||||
KSRCDIR += kern/init \ | |||||
kern/libs \ | |||||
kern/debug \ | |||||
kern/driver \ | |||||
kern/trap \ | |||||
kern/mm | |||||
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,$(bootfiles)) | $(call totarget,sign) | |||||
@echo + ld $@ | |||||
$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ -o $(call toobj,bootblock) | |||||
@$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock) | |||||
@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock) | |||||
@$(call totarget,sign) $(call outfile,bootblock) $(bootblock) | |||||
$(call create_target,bootblock) | |||||
# ------------------------------------------------------------------- | |||||
# create 'sign' tools | |||||
$(call add_files_host,tools/sign.c,sign,sign) | |||||
$(call create_target_host,sign,sign) | |||||
# ------------------------------------------------------------------- | |||||
# create ucore.img | |||||
UCOREIMG := $(call totarget,ucore.img) | |||||
$(UCOREIMG): $(kernel) $(bootblock) | |||||
$(V)dd if=/dev/zero of=$@ count=10000 | |||||
$(V)dd if=$(bootblock) of=$@ conv=notrunc | |||||
$(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc | |||||
$(call create_target,ucore.img) | |||||
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | |||||
$(call finish_all) | |||||
IGNORE_ALLDEPS = clean \ | |||||
dist-clean \ | |||||
grade \ | |||||
touch \ | |||||
print-.+ \ | |||||
handin | |||||
ifeq ($(call match,$(MAKECMDGOALS),$(IGNORE_ALLDEPS)),0) | |||||
-include $(ALLDEPS) | |||||
endif | |||||
# files for grade script | |||||
TARGETS: $(TARGETS) | |||||
.DEFAULT_GOAL := TARGETS | |||||
.PHONY: qemu qemu-nox debug debug-nox | |||||
qemu: $(UCOREIMG) | |||||
$(V)$(QEMU) -parallel stdio -hda $< -serial null | |||||
qemu-nox: $(UCOREIMG) | |||||
$(V)$(QEMU) -serial mon:stdio -hda $< -nographic | |||||
TERMINAL :=gnome-terminal | |||||
debug: $(UCOREIMG) | |||||
$(V)$(QEMU) -S -s -parallel stdio -hda $< -serial null & | |||||
$(V)sleep 2 | |||||
$(V)$(TERMINAL) -e "gdb -q -x tools/gdbinit" | |||||
debug-nox: $(UCOREIMG) | |||||
$(V)$(QEMU) -S -s -serial mon:stdio -hda $< -nographic & | |||||
$(V)sleep 2 | |||||
$(V)$(TERMINAL) -e "gdb -q -x tools/gdbinit" | |||||
.PHONY: grade touch | |||||
GRADE_GDB_IN := .gdb.in | |||||
GRADE_QEMU_OUT := .qemu.out | |||||
HANDIN := proj$(PROJ)-handin.tar.gz | |||||
TOUCH_FILES := kern/trap/trap.c | |||||
MAKEOPTS := --quiet --no-print-directory | |||||
grade: | |||||
$(V)$(MAKE) $(MAKEOPTS) clean | |||||
$(V)$(SH) tools/grade.sh | |||||
touch: | |||||
$(V)$(foreach f,$(TOUCH_FILES),$(TOUCH) $(f)) | |||||
print-%: | |||||
@echo $($(shell echo $(patsubst print-%,%,$@) | $(TR) [a-z] [A-Z])) | |||||
.PHONY: clean dist-clean handin packall | |||||
clean: | |||||
$(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT) | |||||
-$(RM) -r $(OBJDIR) $(BINDIR) | |||||
dist-clean: clean | |||||
-$(RM) $(HANDIN) | |||||
handin: packall | |||||
@echo Please visit http://learn.tsinghua.edu.cn and upload $(HANDIN). Thanks! | |||||
packall: clean | |||||
@$(RM) -f $(HANDIN) | |||||
@tar -czf $(HANDIN) `find . -type f -o -type d | grep -v '^\.*$$' | grep -vF '$(HANDIN)'` | |||||
@ -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__ */ | |||||
@ -0,0 +1,86 @@ | |||||
#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 | |||||
# 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 | |||||
# 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 | |||||
# 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 |
@ -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); | |||||
} | |||||
@ -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__ */ | |||||
@ -0,0 +1,320 @@ | |||||
#include <defs.h> | |||||
#include <x86.h> | |||||
#include <stab.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include <kdebug.h> | |||||
#define STACKFRAME_DEPTH 20 | |||||
extern const struct stab __STAB_BEGIN__[]; // beginning of stabs table | |||||
extern const struct stab __STAB_END__[]; // end of stabs table | |||||
extern const char __STABSTR_BEGIN__[]; // beginning of string table | |||||
extern const char __STABSTR_END__[]; // end of string table | |||||
/* debug information about a particular instruction pointer */ | |||||
struct eipdebuginfo { | |||||
const char *eip_file; // source code filename for eip | |||||
int eip_line; // source code line number for eip | |||||
const char *eip_fn_name; // name of function containing eip | |||||
int eip_fn_namelen; // length of function's name | |||||
uintptr_t eip_fn_addr; // start address of function | |||||
int eip_fn_narg; // number of function arguments | |||||
}; | |||||
/* * | |||||
* stab_binsearch - according to the input, the initial value of | |||||
* range [*@region_left, *@region_right], find a single stab entry | |||||
* that includes the address @addr and matches the type @type, | |||||
* and then save its boundary to the locations that pointed | |||||
* by @region_left and @region_right. | |||||
* | |||||
* Some stab types are arranged in increasing order by instruction address. | |||||
* For example, N_FUN stabs (stab entries with n_type == N_FUN), which | |||||
* mark functions, and N_SO stabs, which mark source files. | |||||
* | |||||
* Given an instruction address, this function finds the single stab entry | |||||
* of type @type that contains that address. | |||||
* | |||||
* The search takes place within the range [*@region_left, *@region_right]. | |||||
* Thus, to search an entire set of N stabs, you might do: | |||||
* | |||||
* left = 0; | |||||
* right = N - 1; (rightmost stab) | |||||
* stab_binsearch(stabs, &left, &right, type, addr); | |||||
* | |||||
* The search modifies *region_left and *region_right to bracket the @addr. | |||||
* *@region_left points to the matching stab that contains @addr, | |||||
* and *@region_right points just before the next stab. | |||||
* If *@region_left > *region_right, then @addr is not contained in any | |||||
* matching stab. | |||||
* | |||||
* For example, given these N_SO stabs: | |||||
* Index Type Address | |||||
* 0 SO f0100000 | |||||
* 13 SO f0100040 | |||||
* 117 SO f0100176 | |||||
* 118 SO f0100178 | |||||
* 555 SO f0100652 | |||||
* 556 SO f0100654 | |||||
* 657 SO f0100849 | |||||
* this code: | |||||
* left = 0, right = 657; | |||||
* stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184); | |||||
* will exit setting left = 118, right = 554. | |||||
* */ | |||||
static void | |||||
stab_binsearch(const struct stab *stabs, int *region_left, int *region_right, | |||||
int type, uintptr_t addr) { | |||||
int l = *region_left, r = *region_right, any_matches = 0; | |||||
while (l <= r) { | |||||
int true_m = (l + r) / 2, m = true_m; | |||||
// search for earliest stab with right type | |||||
while (m >= l && stabs[m].n_type != type) { | |||||
m --; | |||||
} | |||||
if (m < l) { // no match in [l, m] | |||||
l = true_m + 1; | |||||
continue; | |||||
} | |||||
// actual binary search | |||||
any_matches = 1; | |||||
if (stabs[m].n_value < addr) { | |||||
*region_left = m; | |||||
l = true_m + 1; | |||||
} else if (stabs[m].n_value > addr) { | |||||
*region_right = m - 1; | |||||
r = m - 1; | |||||
} else { | |||||
// exact match for 'addr', but continue loop to find | |||||
// *region_right | |||||
*region_left = m; | |||||
l = m; | |||||
addr ++; | |||||
} | |||||
} | |||||
if (!any_matches) { | |||||
*region_right = *region_left - 1; | |||||
} | |||||
else { | |||||
// find rightmost region containing 'addr' | |||||
l = *region_right; | |||||
for (; l > *region_left && stabs[l].n_type != type; l --) | |||||
/* do nothing */; | |||||
*region_left = l; | |||||
} | |||||
} | |||||
/* * | |||||
* debuginfo_eip - Fill in the @info structure with information about | |||||
* the specified instruction address, @addr. Returns 0 if information | |||||
* was found, and negative if not. But even if it returns negative it | |||||
* has stored some information into '*info'. | |||||
* */ | |||||
int | |||||
debuginfo_eip(uintptr_t addr, struct eipdebuginfo *info) { | |||||
const struct stab *stabs, *stab_end; | |||||
const char *stabstr, *stabstr_end; | |||||
info->eip_file = "<unknown>"; | |||||
info->eip_line = 0; | |||||
info->eip_fn_name = "<unknown>"; | |||||
info->eip_fn_namelen = 9; | |||||
info->eip_fn_addr = addr; | |||||
info->eip_fn_narg = 0; | |||||
stabs = __STAB_BEGIN__; | |||||
stab_end = __STAB_END__; | |||||
stabstr = __STABSTR_BEGIN__; | |||||
stabstr_end = __STABSTR_END__; | |||||
// String table validity checks | |||||
if (stabstr_end <= stabstr || stabstr_end[-1] != 0) { | |||||
return -1; | |||||
} | |||||
// Now we find the right stabs that define the function containing | |||||
// 'eip'. First, we find the basic source file containing 'eip'. | |||||
// Then, we look in that source file for the function. Then we look | |||||
// for the line number. | |||||
// Search the entire set of stabs for the source file (type N_SO). | |||||
int lfile = 0, rfile = (stab_end - stabs) - 1; | |||||
stab_binsearch(stabs, &lfile, &rfile, N_SO, addr); | |||||
if (lfile == 0) | |||||
return -1; | |||||
// Search within that file's stabs for the function definition | |||||
// (N_FUN). | |||||
int lfun = lfile, rfun = rfile; | |||||
int lline, rline; | |||||
stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr); | |||||
if (lfun <= rfun) { | |||||
// stabs[lfun] points to the function name | |||||
// in the string table, but check bounds just in case. | |||||
if (stabs[lfun].n_strx < stabstr_end - stabstr) { | |||||
info->eip_fn_name = stabstr + stabs[lfun].n_strx; | |||||
} | |||||
info->eip_fn_addr = stabs[lfun].n_value; | |||||
addr -= info->eip_fn_addr; | |||||
// Search within the function definition for the line number. | |||||
lline = lfun; | |||||
rline = rfun; | |||||
} else { | |||||
// Couldn't find function stab! Maybe we're in an assembly | |||||
// file. Search the whole file for the line number. | |||||
info->eip_fn_addr = addr; | |||||
lline = lfile; | |||||
rline = rfile; | |||||
} | |||||
info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name; | |||||
// Search within [lline, rline] for the line number stab. | |||||
// If found, set info->eip_line to the right line number. | |||||
// If not found, return -1. | |||||
stab_binsearch(stabs, &lline, &rline, N_SLINE, addr); | |||||
if (lline <= rline) { | |||||
info->eip_line = stabs[rline].n_desc; | |||||
} else { | |||||
return -1; | |||||
} | |||||
// Search backwards from the line number for the relevant filename stab. | |||||
// We can't just use the "lfile" stab because inlined functions | |||||
// can interpolate code from a different file! | |||||
// Such included source files use the N_SOL stab type. | |||||
while (lline >= lfile | |||||
&& stabs[lline].n_type != N_SOL | |||||
&& (stabs[lline].n_type != N_SO || !stabs[lline].n_value)) { | |||||
lline --; | |||||
} | |||||
if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr) { | |||||
info->eip_file = stabstr + stabs[lline].n_strx; | |||||
} | |||||
// Set eip_fn_narg to the number of arguments taken by the function, | |||||
// or 0 if there was no containing function. | |||||
if (lfun < rfun) { | |||||
for (lline = lfun + 1; | |||||
lline < rfun && stabs[lline].n_type == N_PSYM; | |||||
lline ++) { | |||||
info->eip_fn_narg ++; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
/* * | |||||
* print_kerninfo - print the information about kernel, including the location | |||||
* of kernel entry, the start addresses of data and text segements, the start | |||||
* address of free memory and how many memory that kernel has used. | |||||
* */ | |||||
void | |||||
print_kerninfo(void) { | |||||
extern char etext[], edata[], end[], kern_init[]; | |||||
cprintf("Special kernel symbols:\n"); | |||||
cprintf(" entry 0x%08x (phys)\n", kern_init); | |||||
cprintf(" etext 0x%08x (phys)\n", etext); | |||||
cprintf(" edata 0x%08x (phys)\n", edata); | |||||
cprintf(" end 0x%08x (phys)\n", end); | |||||
cprintf("Kernel executable memory footprint: %dKB\n", (end - kern_init + 1023)/1024); | |||||
} | |||||
/* * | |||||
* print_debuginfo - read and print the stat information for the address @eip, | |||||
* and info.eip_fn_addr should be the first address of the related function. | |||||
* */ | |||||
void | |||||
print_debuginfo(uintptr_t eip) { | |||||
struct eipdebuginfo info; | |||||
if (debuginfo_eip(eip, &info) != 0) { | |||||
cprintf(" <unknow>: -- 0x%08x --\n", eip); | |||||
} | |||||
else { | |||||
char fnname[256]; | |||||
int j; | |||||
for (j = 0; j < info.eip_fn_namelen; j ++) { | |||||
fnname[j] = info.eip_fn_name[j]; | |||||
} | |||||
fnname[j] = '\0'; | |||||
cprintf(" %s:%d: %s+%d\n", info.eip_file, info.eip_line, | |||||
fnname, eip - info.eip_fn_addr); | |||||
} | |||||
} | |||||
static __noinline uint32_t | |||||
read_eip(void) { | |||||
uint32_t eip; | |||||
asm volatile("movl 4(%%ebp), %0" : "=r" (eip)); | |||||
return eip; | |||||
} | |||||
/* * | |||||
* print_stackframe - print a list of the saved eip values from the nested 'call' | |||||
* instructions that led to the current point of execution | |||||
* | |||||
* The x86 stack pointer, namely esp, points to the lowest location on the stack | |||||
* that is currently in use. Everything below that location in stack is free. Pushing | |||||
* a value onto the stack will invole decreasing the stack pointer and then writing | |||||
* the value to the place that stack pointer pointes to. And popping a value do the | |||||
* opposite. | |||||
* | |||||
* The ebp (base pointer) register, in contrast, is associated with the stack | |||||
* primarily by software convention. On entry to a C function, the function's | |||||
* prologue code normally saves the previous function's base pointer by pushing | |||||
* it onto the stack, and then copies the current esp value into ebp for the duration | |||||
* of the function. If all the functions in a program obey this convention, | |||||
* then at any given point during the program's execution, it is possible to trace | |||||
* back through the stack by following the chain of saved ebp pointers and determining | |||||
* exactly what nested sequence of function calls caused this particular point in the | |||||
* program to be reached. This capability can be particularly useful, for example, | |||||
* when a particular function causes an assert failure or panic because bad arguments | |||||
* were passed to it, but you aren't sure who passed the bad arguments. A stack | |||||
* backtrace lets you find the offending function. | |||||
* | |||||
* The inline function read_ebp() can tell us the value of current ebp. And the | |||||
* non-inline function read_eip() is useful, it can read the value of current eip, | |||||
* since while calling this function, read_eip() can read the caller's eip from | |||||
* stack easily. | |||||
* | |||||
* In print_debuginfo(), the function debuginfo_eip() can get enough information about | |||||
* calling-chain. Finally print_stackframe() will trace and print them for debugging. | |||||
* | |||||
* Note that, the length of ebp-chain is limited. In boot/bootasm.S, before jumping | |||||
* to the kernel entry, the value of ebp has been set to zero, that's the boundary. | |||||
* */ | |||||
void | |||||
print_stackframe(void) { | |||||
/* LAB1 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]; | |||||
} | |||||
} | |||||
@ -0,0 +1,11 @@ | |||||
#ifndef __KERN_DEBUG_KDEBUG_H__ | |||||
#define __KERN_DEBUG_KDEBUG_H__ | |||||
#include <defs.h> | |||||
void print_kerninfo(void); | |||||
void print_stackframe(void); | |||||
void print_debuginfo(uintptr_t eip); | |||||
#endif /* !__KERN_DEBUG_KDEBUG_H__ */ | |||||
@ -0,0 +1,128 @@ | |||||
#include <stdio.h> | |||||
#include <string.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}, | |||||
}; | |||||
#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; | |||||
} | |||||
@ -0,0 +1,13 @@ | |||||
#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); | |||||
#endif /* !__KERN_DEBUG_MONITOR_H__ */ | |||||
@ -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; | |||||
} | |||||
@ -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__ */ | |||||
@ -0,0 +1,45 @@ | |||||
#include <x86.h> | |||||
#include <trap.h> | |||||
#include <stdio.h> | |||||
#include <picirq.h> | |||||
/* * | |||||
* Support for time-related hardware gadgets - the 8253 timer, | |||||
* which generates interruptes on IRQ-0. | |||||
* */ | |||||
#define IO_TIMER1 0x040 // 8253 Timer #1 | |||||
/* * | |||||
* Frequency of all three count-down timers; (TIMER_FREQ/freq) | |||||
* is the appropriate count to generate a frequency of freq Hz. | |||||
* */ | |||||
#define TIMER_FREQ 1193182 | |||||
#define TIMER_DIV(x) ((TIMER_FREQ + (x) / 2) / (x)) | |||||
#define TIMER_MODE (IO_TIMER1 + 3) // timer mode port | |||||
#define TIMER_SEL0 0x00 // select counter 0 | |||||
#define TIMER_RATEGEN 0x04 // mode 2, rate generator | |||||
#define TIMER_16BIT 0x30 // r/w counter 16 bits, LSB first | |||||
volatile size_t ticks; | |||||
/* * | |||||
* clock_init - initialize 8253 clock to interrupt 100 times per second, | |||||
* and then enable IRQ_TIMER. | |||||
* */ | |||||
void | |||||
clock_init(void) { | |||||
// set 8253 timer-chip | |||||
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); | |||||
outb(IO_TIMER1, TIMER_DIV(100) % 256); | |||||
outb(IO_TIMER1, TIMER_DIV(100) / 256); | |||||
// initialize time counter 'ticks' to zero | |||||
ticks = 0; | |||||
cprintf("++ setup timer interrupts\n"); | |||||
pic_enable(IRQ_TIMER); | |||||
} | |||||
@ -0,0 +1,11 @@ | |||||
#ifndef __KERN_DRIVER_CLOCK_H__ | |||||
#define __KERN_DRIVER_CLOCK_H__ | |||||
#include <defs.h> | |||||
extern volatile size_t ticks; | |||||
void clock_init(void); | |||||
#endif /* !__KERN_DRIVER_CLOCK_H__ */ | |||||
@ -0,0 +1,455 @@ | |||||
#include <defs.h> | |||||
#include <x86.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include <kbdreg.h> | |||||
#include <picirq.h> | |||||
#include <trap.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; | |||||
uint16_t was = *cp; | |||||
*cp = (uint16_t) 0xA55A; | |||||
if (*cp != 0xA55A) { | |||||
cp = (uint16_t*)MONO_BUF; | |||||
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) { | |||||
lpt_putc(c); | |||||
cga_putc(c); | |||||
serial_putc(c); | |||||
} | |||||
/* * | |||||
* cons_getc - return the next input character from console, | |||||
* or 0 if none waiting. | |||||
* */ | |||||
int | |||||
cons_getc(void) { | |||||
int c; | |||||
// 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; | |||||
} | |||||
return c; | |||||
} | |||||
return 0; | |||||
} | |||||
@ -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__ */ | |||||
@ -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(); | |||||
} | |||||
@ -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__ */ | |||||
@ -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__ */ | |||||
@ -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); | |||||
} | |||||
} | |||||
@ -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__ */ | |||||
@ -0,0 +1,104 @@ | |||||
#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> | |||||
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 | |||||
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(); | |||||
/* do nothing */ | |||||
while (1); | |||||
} | |||||
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(); | |||||
} | |||||
@ -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; | |||||
} | |||||
} | |||||
} | |||||
@ -0,0 +1,78 @@ | |||||
#include <defs.h> | |||||
#include <stdio.h> | |||||
#include <console.h> | |||||
/* HIGH level console I/O */ | |||||
/* * | |||||
* cputch - writes a single character @c to stdout, and it will | |||||
* increace the value of counter pointed by @cnt. | |||||
* */ | |||||
static void | |||||
cputch(int c, int *cnt) { | |||||
cons_putc(c); | |||||
(*cnt) ++; | |||||
} | |||||
/* * | |||||
* vcprintf - format a string and writes it to stdout | |||||
* | |||||
* The return value is the number of characters which would be | |||||
* written to stdout. | |||||
* | |||||
* Call this function if you are already dealing with a va_list. | |||||
* Or you probably want cprintf() instead. | |||||
* */ | |||||
int | |||||
vcprintf(const char *fmt, va_list ap) { | |||||
int cnt = 0; | |||||
vprintfmt((void*)cputch, &cnt, fmt, ap); | |||||
return cnt; | |||||
} | |||||
/* * | |||||
* cprintf - formats a string and writes it to stdout | |||||
* | |||||
* The return value is the number of characters which would be | |||||
* written to stdout. | |||||
* */ | |||||
int | |||||
cprintf(const char *fmt, ...) { | |||||
va_list ap; | |||||
int cnt; | |||||
va_start(ap, fmt); | |||||
cnt = vcprintf(fmt, ap); | |||||
va_end(ap); | |||||
return cnt; | |||||
} | |||||
/* cputchar - writes a single character to stdout */ | |||||
void | |||||
cputchar(int c) { | |||||
cons_putc(c); | |||||
} | |||||
/* * | |||||
* cputs- writes the string pointed by @str to stdout and | |||||
* appends a newline character. | |||||
* */ | |||||
int | |||||
cputs(const char *str) { | |||||
int cnt = 0; | |||||
char c; | |||||
while ((c = *str ++) != '\0') { | |||||
cputch(c, &cnt); | |||||
} | |||||
cputch('\n', &cnt); | |||||
return cnt; | |||||
} | |||||
/* getchar - reads a single non-zero character from stdin */ | |||||
int | |||||
getchar(void) { | |||||
int c; | |||||
while ((c = cons_getc()) == 0) | |||||
/* do nothing */; | |||||
return c; | |||||
} | |||||
@ -0,0 +1,29 @@ | |||||
#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) | |||||
#endif /* !__KERN_MM_MEMLAYOUT_H__ */ | |||||
@ -0,0 +1,174 @@ | |||||
#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 | |||||
/* 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 SEG16(type, base, lim, dpl) \ | |||||
(struct segdesc){ \ | |||||
(lim) & 0xffff, (base) & 0xffff, \ | |||||
((base) >> 16) & 0xff, type, 1, 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 | |||||
}; | |||||
#endif /* !__KERN_MM_MMU_H__ */ | |||||
@ -0,0 +1,99 @@ | |||||
#include <defs.h> | |||||
#include <x86.h> | |||||
#include <mmu.h> | |||||
#include <memlayout.h> | |||||
#include <pmm.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}; | |||||
/* * | |||||
* 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, (uint32_t)gdt | |||||
}; | |||||
/* * | |||||
* 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)); | |||||
} | |||||
/* temporary kernel stack */ | |||||
uint8_t stack0[1024]; | |||||
/* gdt_init - initialize the default GDT and TSS */ | |||||
static void | |||||
gdt_init(void) { | |||||
// Setup a TSS so that we can get the right stack when we trap from | |||||
// user to the kernel. But not safe here, it's only a temporary value, | |||||
// it will be set to KSTACKTOP in lab2. | |||||
ts.ts_esp0 = (uint32_t)&stack0 + sizeof(stack0); | |||||
ts.ts_ss0 = KERNEL_DS; | |||||
// initialize the TSS filed of the gdt | |||||
gdt[SEG_TSS] = SEG16(STS_T32A, (uint32_t)&ts, sizeof(ts), DPL_KERNEL); | |||||
gdt[SEG_TSS].sd_s = 0; | |||||
// reload all segment registers | |||||
lgdt(&gdt_pd); | |||||
// load the TSS | |||||
ltr(GD_TSS); | |||||
} | |||||
/* pmm_init - initialize the physical memory management */ | |||||
void | |||||
pmm_init(void) { | |||||
gdt_init(); | |||||
} | |||||
@ -0,0 +1,7 @@ | |||||
#ifndef __KERN_MM_PMM_H__ | |||||
#define __KERN_MM_PMM_H__ | |||||
void pmm_init(void); | |||||
#endif /* !__KERN_MM_PMM_H__ */ | |||||
@ -0,0 +1,197 @@ | |||||
#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 <kdebug.h> | |||||
#define TICK_NUM 100 | |||||
static void print_ticks() { | |||||
cprintf("%d ticks\n",TICK_NUM); | |||||
#ifdef DEBUG_GRADE | |||||
cprintf("End of Test.\n"); | |||||
panic("EOT: kernel seems ok."); | |||||
#endif | |||||
} | |||||
/* * | |||||
* Interrupt descriptor table: | |||||
* | |||||
* Must be built at run time because shifted function addresses can't | |||||
* be represented in relocation records. | |||||
* */ | |||||
static struct gatedesc idt[256] = {{0}}; | |||||
static struct pseudodesc idt_pd = { | |||||
sizeof(idt) - 1, (uintptr_t)idt | |||||
}; | |||||
/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */ | |||||
void | |||||
idt_init(void) { | |||||
/* LAB1 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! | |||||
*/ | |||||
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); | |||||
} | |||||
lidt(&idt_pd); | |||||
} | |||||
static const char * | |||||
trapname(int trapno) { | |||||
static const char * const excnames[] = { | |||||
"Divide error", | |||||
"Debug", | |||||
"Non-Maskable Interrupt", | |||||
"Breakpoint", | |||||
"Overflow", | |||||
"BOUND Range Exceeded", | |||||
"Invalid Opcode", | |||||
"Device Not Available", | |||||
"Double Fault", | |||||
"Coprocessor Segment Overrun", | |||||
"Invalid TSS", | |||||
"Segment Not Present", | |||||
"Stack Fault", | |||||
"General Protection", | |||||
"Page Fault", | |||||
"(unknown trap)", | |||||
"x87 FPU Floating-Point Error", | |||||
"Alignment Check", | |||||
"Machine-Check", | |||||
"SIMD Floating-Point Exception" | |||||
}; | |||||
if (trapno < sizeof(excnames)/sizeof(const char * const)) { | |||||
return excnames[trapno]; | |||||
} | |||||
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) { | |||||
return "Hardware Interrupt"; | |||||
} | |||||
return "(unknown trap)"; | |||||
} | |||||
/* trap_in_kernel - test if trap happened in kernel */ | |||||
bool | |||||
trap_in_kernel(struct trapframe *tf) { | |||||
return (tf->tf_cs == (uint16_t)KERNEL_CS); | |||||
} | |||||
static const char *IA32flags[] = { | |||||
"CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF", | |||||
"TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL, | |||||
"RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL, | |||||
}; | |||||
void | |||||
print_trapframe(struct trapframe *tf) { | |||||
cprintf("trapframe at %p\n", tf); | |||||
print_regs(&tf->tf_regs); | |||||
cprintf(" ds 0x----%04x\n", tf->tf_ds); | |||||
cprintf(" es 0x----%04x\n", tf->tf_es); | |||||
cprintf(" fs 0x----%04x\n", tf->tf_fs); | |||||
cprintf(" gs 0x----%04x\n", tf->tf_gs); | |||||
cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); | |||||
cprintf(" err 0x%08x\n", tf->tf_err); | |||||
cprintf(" eip 0x%08x\n", tf->tf_eip); | |||||
cprintf(" cs 0x----%04x\n", tf->tf_cs); | |||||
cprintf(" flag 0x%08x ", tf->tf_eflags); | |||||
int i, j; | |||||
for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) { | |||||
if ((tf->tf_eflags & j) && IA32flags[i] != NULL) { | |||||
cprintf("%s,", IA32flags[i]); | |||||
} | |||||
} | |||||
cprintf("IOPL=%d\n", (tf->tf_eflags & FL_IOPL_MASK) >> 12); | |||||
if (!trap_in_kernel(tf)) { | |||||
cprintf(" esp 0x%08x\n", tf->tf_esp); | |||||
cprintf(" ss 0x----%04x\n", tf->tf_ss); | |||||
} | |||||
} | |||||
void | |||||
print_regs(struct pushregs *regs) { | |||||
cprintf(" edi 0x%08x\n", regs->reg_edi); | |||||
cprintf(" esi 0x%08x\n", regs->reg_esi); | |||||
cprintf(" ebp 0x%08x\n", regs->reg_ebp); | |||||
cprintf(" oesp 0x%08x\n", regs->reg_oesp); | |||||
cprintf(" ebx 0x%08x\n", regs->reg_ebx); | |||||
cprintf(" edx 0x%08x\n", regs->reg_edx); | |||||
cprintf(" ecx 0x%08x\n", regs->reg_ecx); | |||||
cprintf(" eax 0x%08x\n", regs->reg_eax); | |||||
} | |||||
/* trap_dispatch - dispatch based on what type of trap occurred */ | |||||
static void | |||||
trap_dispatch(struct trapframe *tf) { | |||||
char c; | |||||
switch (tf->tf_trapno) { | |||||
case IRQ_OFFSET + IRQ_TIMER: | |||||
/* 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! | |||||
*/ | |||||
ticks ++; | |||||
if (ticks % TICK_NUM == 0) { | |||||
print_ticks(); | |||||
} | |||||
break; | |||||
case IRQ_OFFSET + IRQ_COM1: | |||||
c = cons_getc(); | |||||
cprintf("serial [%03d] %c\n", c, c); | |||||
break; | |||||
case IRQ_OFFSET + IRQ_KBD: | |||||
c = cons_getc(); | |||||
cprintf("kbd [%03d] %c\n", c, c); | |||||
break; | |||||
//LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes. | |||||
case T_SWITCH_TOU: | |||||
case T_SWITCH_TOK: | |||||
panic("T_SWITCH_** ??\n"); | |||||
break; | |||||
case IRQ_OFFSET + IRQ_IDE1: | |||||
case IRQ_OFFSET + IRQ_IDE2: | |||||
/* do nothing */ | |||||
break; | |||||
default: | |||||
// in kernel, it must be a mistake | |||||
if ((tf->tf_cs & 3) == 0) { | |||||
print_trapframe(tf); | |||||
panic("unexpected trap in kernel.\n"); | |||||
} | |||||
} | |||||
} | |||||
/* * | |||||
* trap - handles or dispatches an exception/interrupt. if and when trap() returns, | |||||
* the code in kern/trap/trapentry.S restores the old CPU state saved in the | |||||
* trapframe and then uses the iret instruction to return from the exception. | |||||
* */ | |||||
void | |||||
trap(struct trapframe *tf) { | |||||
// dispatch based on what type of trap occurred | |||||
trap_dispatch(tf); | |||||
} | |||||
@ -0,0 +1,91 @@ | |||||
#ifndef __KERN_TRAP_TRAP_H__ | |||||
#define __KERN_TRAP_TRAP_H__ | |||||
#include <defs.h> | |||||
/* Trap Numbers */ | |||||
/* Processor-defined: */ | |||||
#define T_DIVIDE 0 // divide error | |||||
#define T_DEBUG 1 // debug exception | |||||
#define T_NMI 2 // non-maskable interrupt | |||||
#define T_BRKPT 3 // breakpoint | |||||
#define T_OFLOW 4 // overflow | |||||
#define T_BOUND 5 // bounds check | |||||
#define T_ILLOP 6 // illegal opcode | |||||
#define T_DEVICE 7 // device not available | |||||
#define T_DBLFLT 8 // double fault | |||||
// #define T_COPROC 9 // reserved (not used since 486) | |||||
#define T_TSS 10 // invalid task switch segment | |||||
#define T_SEGNP 11 // segment not present | |||||
#define T_STACK 12 // stack exception | |||||
#define T_GPFLT 13 // general protection fault | |||||
#define T_PGFLT 14 // page fault | |||||
// #define T_RES 15 // reserved | |||||
#define T_FPERR 16 // floating point error | |||||
#define T_ALIGN 17 // aligment check | |||||
#define T_MCHK 18 // machine check | |||||
#define T_SIMDERR 19 // SIMD floating point error | |||||
#define T_SYSCALL 0x80 // SYSCALL, ONLY FOR THIS PROJ | |||||
/* Hardware IRQ numbers. We receive these as (IRQ_OFFSET + IRQ_xx) */ | |||||
#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET | |||||
#define IRQ_TIMER 0 | |||||
#define IRQ_KBD 1 | |||||
#define IRQ_COM1 4 | |||||
#define IRQ_IDE1 14 | |||||
#define IRQ_IDE2 15 | |||||
#define IRQ_ERROR 19 | |||||
#define IRQ_SPURIOUS 31 | |||||
/* * | |||||
* These are arbitrarily chosen, but with care not to overlap | |||||
* processor defined exceptions or interrupt vectors. | |||||
* */ | |||||
#define T_SWITCH_TOU 120 // user/kernel switch | |||||
#define T_SWITCH_TOK 121 // user/kernel switch | |||||
/* registers as pushed by pushal */ | |||||
struct pushregs { | |||||
uint32_t reg_edi; | |||||
uint32_t reg_esi; | |||||
uint32_t reg_ebp; | |||||
uint32_t reg_oesp; /* Useless */ | |||||
uint32_t reg_ebx; | |||||
uint32_t reg_edx; | |||||
uint32_t reg_ecx; | |||||
uint32_t reg_eax; | |||||
}; | |||||
struct trapframe { | |||||
struct pushregs tf_regs; | |||||
uint16_t tf_gs; | |||||
uint16_t tf_padding0; | |||||
uint16_t tf_fs; | |||||
uint16_t tf_padding1; | |||||
uint16_t tf_es; | |||||
uint16_t tf_padding2; | |||||
uint16_t tf_ds; | |||||
uint16_t tf_padding3; | |||||
uint32_t tf_trapno; | |||||
/* below here defined by x86 hardware */ | |||||
uint32_t tf_err; | |||||
uintptr_t tf_eip; | |||||
uint16_t tf_cs; | |||||
uint16_t tf_padding4; | |||||
uint32_t tf_eflags; | |||||
/* below here only when crossing rings, such as from user to kernel */ | |||||
uintptr_t tf_esp; | |||||
uint16_t tf_ss; | |||||
uint16_t tf_padding5; | |||||
} __attribute__((packed)); | |||||
void idt_init(void); | |||||
void print_trapframe(struct trapframe *tf); | |||||
void print_regs(struct pushregs *regs); | |||||
bool trap_in_kernel(struct trapframe *tf); | |||||
#endif /* !__KERN_TRAP_TRAP_H__ */ | |||||
@ -0,0 +1,44 @@ | |||||
#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 | |||||
@ -0,0 +1,68 @@ | |||||
#ifndef __LIBS_DEFS_H__ | |||||
#define __LIBS_DEFS_H__ | |||||
#ifndef NULL | |||||
#define NULL ((void *)0) | |||||
#endif | |||||
#define __always_inline inline __attribute__((always_inline)) | |||||
#define __noinline __attribute__((noinline)) | |||||
#define __noreturn __attribute__((noreturn)) | |||||
/* Represents true-or-false values */ | |||||
typedef int bool; | |||||
/* Explicitly-sized versions of integer types */ | |||||
typedef char int8_t; | |||||
typedef unsigned char uint8_t; | |||||
typedef short int16_t; | |||||
typedef unsigned short uint16_t; | |||||
typedef int int32_t; | |||||
typedef unsigned int uint32_t; | |||||
typedef long long int64_t; | |||||
typedef unsigned long long uint64_t; | |||||
/* * | |||||
* Pointers and addresses are 32 bits long. | |||||
* We use pointer types to represent addresses, | |||||
* uintptr_t to represent the numerical values of addresses. | |||||
* */ | |||||
typedef int32_t intptr_t; | |||||
typedef uint32_t uintptr_t; | |||||
/* size_t is used for memory object sizes */ | |||||
typedef uintptr_t size_t; | |||||
/* used for page numbers */ | |||||
typedef size_t ppn_t; | |||||
/* * | |||||
* Rounding operations (efficient when n is a power of 2) | |||||
* Round down to the nearest multiple of n | |||||
* */ | |||||
#define ROUNDDOWN(a, n) ({ \ | |||||
size_t __a = (size_t)(a); \ | |||||
(typeof(a))(__a - __a % (n)); \ | |||||
}) | |||||
/* Round up to the nearest multiple of n */ | |||||
#define ROUNDUP(a, n) ({ \ | |||||
size_t __n = (size_t)(n); \ | |||||
(typeof(a))(ROUNDDOWN((size_t)(a) + __n - 1, __n)); \ | |||||
}) | |||||
/* Return the offset of 'member' relative to the beginning of a struct type */ | |||||
#define offsetof(type, member) \ | |||||
((size_t)(&((type *)0)->member)) | |||||
/* * | |||||
* to_struct - get the struct from a ptr | |||||
* @ptr: a struct pointer of member | |||||
* @type: the type of the struct this is embedded in | |||||
* @member: the name of the member within the struct | |||||
* */ | |||||
#define to_struct(ptr, type, member) \ | |||||
((type *)((char *)(ptr) - offsetof(type, member))) | |||||
#endif /* !__LIBS_DEFS_H__ */ | |||||
@ -0,0 +1,40 @@ | |||||
#ifndef __LIBS_ELF_H__ | |||||
#define __LIBS_ELF_H__ | |||||
#include <defs.h> | |||||
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian | |||||
/* file header */ | |||||
struct elfhdr { | |||||
uint32_t e_magic; // must equal ELF_MAGIC | |||||
uint8_t e_elf[12]; | |||||
uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core image | |||||
uint16_t e_machine; // 3=x86, 4=68K, etc. | |||||
uint32_t e_version; // file version, always 1 | |||||
uint32_t e_entry; // entry point if executable | |||||
uint32_t e_phoff; // file position of program header or 0 | |||||
uint32_t e_shoff; // file position of section header or 0 | |||||
uint32_t e_flags; // architecture-specific flags, usually 0 | |||||
uint16_t e_ehsize; // size of this elf header | |||||
uint16_t e_phentsize; // size of an entry in program header | |||||
uint16_t e_phnum; // number of entries in program header or 0 | |||||
uint16_t e_shentsize; // size of an entry in section header | |||||
uint16_t e_shnum; // number of entries in section header or 0 | |||||
uint16_t e_shstrndx; // section number that contains section name strings | |||||
}; | |||||
/* program section header */ | |||||
struct proghdr { | |||||
uint32_t p_type; // loadable code or data, dynamic linking info,etc. | |||||
uint32_t p_offset; // file offset of segment | |||||
uint32_t p_va; // virtual address to map segment | |||||
uint32_t p_pa; // physical address, not used | |||||
uint32_t p_filesz; // size of segment in file | |||||
uint32_t p_memsz; // size of segment in memory (bigger if contains bss) | |||||
uint32_t p_flags; // read/write/execute bits | |||||
uint32_t p_align; // required alignment, invariably hardware page size | |||||
}; | |||||
#endif /* !__LIBS_ELF_H__ */ | |||||
@ -0,0 +1,16 @@ | |||||
#ifndef __LIBS_ERROR_H__ | |||||
#define __LIBS_ERROR_H__ | |||||
/* kernel error codes -- keep in sync with list in lib/printfmt.c */ | |||||
#define E_UNSPECIFIED 1 // Unspecified or unknown problem | |||||
#define E_BAD_PROC 2 // Process doesn't exist or otherwise | |||||
#define E_INVAL 3 // Invalid parameter | |||||
#define E_NO_MEM 4 // Request failed due to memory shortage | |||||
#define E_NO_FREE_PROC 5 // Attempt to create a new process beyond | |||||
#define E_FAULT 6 // Memory fault | |||||
/* the maximum allowed */ | |||||
#define MAXERROR 6 | |||||
#endif /* !__LIBS_ERROR_H__ */ | |||||
@ -0,0 +1,340 @@ | |||||
#include <defs.h> | |||||
#include <x86.h> | |||||
#include <error.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
/* * | |||||
* Space or zero padding and a field width are supported for the numeric | |||||
* formats only. | |||||
* | |||||
* The special format %e takes an integer error code | |||||
* and prints a string describing the error. | |||||
* The integer may be positive or negative, | |||||
* so that -E_NO_MEM and E_NO_MEM are equivalent. | |||||
* */ | |||||
static const char * const error_string[MAXERROR + 1] = { | |||||
[0] NULL, | |||||
[E_UNSPECIFIED] "unspecified error", | |||||
[E_BAD_PROC] "bad process", | |||||
[E_INVAL] "invalid parameter", | |||||
[E_NO_MEM] "out of memory", | |||||
[E_NO_FREE_PROC] "out of processes", | |||||
[E_FAULT] "segmentation fault", | |||||
}; | |||||
/* * | |||||
* printnum - print a number (base <= 16) in reverse order | |||||
* @putch: specified putch function, print a single character | |||||
* @putdat: used by @putch function | |||||
* @num: the number will be printed | |||||
* @base: base for print, must be in [1, 16] | |||||
* @width: maximum number of digits, if the actual width is less than @width, use @padc instead | |||||
* @padc: character that padded on the left if the actual width is less than @width | |||||
* */ | |||||
static void | |||||
printnum(void (*putch)(int, void*), void *putdat, | |||||
unsigned long long num, unsigned base, int width, int padc) { | |||||
unsigned long long result = num; | |||||
unsigned mod = do_div(result, base); | |||||
// first recursively print all preceding (more significant) digits | |||||
if (num >= base) { | |||||
printnum(putch, putdat, result, base, width - 1, padc); | |||||
} else { | |||||
// print any needed pad characters before first digit | |||||
while (-- width > 0) | |||||
putch(padc, putdat); | |||||
} | |||||
// then print this (the least significant) digit | |||||
putch("0123456789abcdef"[mod], putdat); | |||||
} | |||||
/* * | |||||
* getuint - get an unsigned int of various possible sizes from a varargs list | |||||
* @ap: a varargs list pointer | |||||
* @lflag: determines the size of the vararg that @ap points to | |||||
* */ | |||||
static unsigned long long | |||||
getuint(va_list *ap, int lflag) { | |||||
if (lflag >= 2) { | |||||
return va_arg(*ap, unsigned long long); | |||||
} | |||||
else if (lflag) { | |||||
return va_arg(*ap, unsigned long); | |||||
} | |||||
else { | |||||
return va_arg(*ap, unsigned int); | |||||
} | |||||
} | |||||
/* * | |||||
* getint - same as getuint but signed, we can't use getuint because of sign extension | |||||
* @ap: a varargs list pointer | |||||
* @lflag: determines the size of the vararg that @ap points to | |||||
* */ | |||||
static long long | |||||
getint(va_list *ap, int lflag) { | |||||
if (lflag >= 2) { | |||||
return va_arg(*ap, long long); | |||||
} | |||||
else if (lflag) { | |||||
return va_arg(*ap, long); | |||||
} | |||||
else { | |||||
return va_arg(*ap, int); | |||||
} | |||||
} | |||||
/* * | |||||
* printfmt - format a string and print it by using putch | |||||
* @putch: specified putch function, print a single character | |||||
* @putdat: used by @putch function | |||||
* @fmt: the format string to use | |||||
* */ | |||||
void | |||||
printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...) { | |||||
va_list ap; | |||||
va_start(ap, fmt); | |||||
vprintfmt(putch, putdat, fmt, ap); | |||||
va_end(ap); | |||||
} | |||||
/* * | |||||
* vprintfmt - format a string and print it by using putch, it's called with a va_list | |||||
* instead of a variable number of arguments | |||||
* @putch: specified putch function, print a single character | |||||
* @putdat: used by @putch function | |||||
* @fmt: the format string to use | |||||
* @ap: arguments for the format string | |||||
* | |||||
* Call this function if you are already dealing with a va_list. | |||||
* Or you probably want printfmt() instead. | |||||
* */ | |||||
void | |||||
vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) { | |||||
register const char *p; | |||||
register int ch, err; | |||||
unsigned long long num; | |||||
int base, width, precision, lflag, altflag; | |||||
while (1) { | |||||
while ((ch = *(unsigned char *)fmt ++) != '%') { | |||||
if (ch == '\0') { | |||||
return; | |||||
} | |||||
putch(ch, putdat); | |||||
} | |||||
// Process a %-escape sequence | |||||
char padc = ' '; | |||||
width = precision = -1; | |||||
lflag = altflag = 0; | |||||
reswitch: | |||||
switch (ch = *(unsigned char *)fmt ++) { | |||||
// flag to pad on the right | |||||
case '-': | |||||
padc = '-'; | |||||
goto reswitch; | |||||
// flag to pad with 0's instead of spaces | |||||
case '0': | |||||
padc = '0'; | |||||
goto reswitch; | |||||
// width field | |||||
case '1' ... '9': | |||||
for (precision = 0; ; ++ fmt) { | |||||
precision = precision * 10 + ch - '0'; | |||||
ch = *fmt; | |||||
if (ch < '0' || ch > '9') { | |||||
break; | |||||
} | |||||
} | |||||
goto process_precision; | |||||
case '*': | |||||
precision = va_arg(ap, int); | |||||
goto process_precision; | |||||
case '.': | |||||
if (width < 0) | |||||
width = 0; | |||||
goto reswitch; | |||||
case '#': | |||||
altflag = 1; | |||||
goto reswitch; | |||||
process_precision: | |||||
if (width < 0) | |||||
width = precision, precision = -1; | |||||
goto reswitch; | |||||
// long flag (doubled for long long) | |||||
case 'l': | |||||
lflag ++; | |||||
goto reswitch; | |||||
// character | |||||
case 'c': | |||||
putch(va_arg(ap, int), putdat); | |||||
break; | |||||
// error message | |||||
case 'e': | |||||
err = va_arg(ap, int); | |||||
if (err < 0) { | |||||
err = -err; | |||||
} | |||||
if (err > MAXERROR || (p = error_string[err]) == NULL) { | |||||
printfmt(putch, putdat, "error %d", err); | |||||
} | |||||
else { | |||||
printfmt(putch, putdat, "%s", p); | |||||
} | |||||
break; | |||||
// string | |||||
case 's': | |||||
if ((p = va_arg(ap, char *)) == NULL) { | |||||
p = "(null)"; | |||||
} | |||||
if (width > 0 && padc != '-') { | |||||
for (width -= strnlen(p, precision); width > 0; width --) { | |||||
putch(padc, putdat); | |||||
} | |||||
} | |||||
for (; (ch = *p ++) != '\0' && (precision < 0 || -- precision >= 0); width --) { | |||||
if (altflag && (ch < ' ' || ch > '~')) { | |||||
putch('?', putdat); | |||||
} | |||||
else { | |||||
putch(ch, putdat); | |||||
} | |||||
} | |||||
for (; width > 0; width --) { | |||||
putch(' ', putdat); | |||||
} | |||||
break; | |||||
// (signed) decimal | |||||
case 'd': | |||||
num = getint(&ap, lflag); | |||||
if ((long long)num < 0) { | |||||
putch('-', putdat); | |||||
num = -(long long)num; | |||||
} | |||||
base = 10; | |||||
goto number; | |||||
// unsigned decimal | |||||
case 'u': | |||||
num = getuint(&ap, lflag); | |||||
base = 10; | |||||
goto number; | |||||
// (unsigned) octal | |||||
case 'o': | |||||
num = getuint(&ap, lflag); | |||||
base = 8; | |||||
goto number; | |||||
// pointer | |||||
case 'p': | |||||
putch('0', putdat); | |||||
putch('x', putdat); | |||||
num = (unsigned long long)(uintptr_t)va_arg(ap, void *); | |||||
base = 16; | |||||
goto number; | |||||
// (unsigned) hexadecimal | |||||
case 'x': | |||||
num = getuint(&ap, lflag); | |||||
base = 16; | |||||
number: | |||||
printnum(putch, putdat, num, base, width, padc); | |||||
break; | |||||
// escaped '%' character | |||||
case '%': | |||||
putch(ch, putdat); | |||||
break; | |||||
// unrecognized escape sequence - just print it literally | |||||
default: | |||||
putch('%', putdat); | |||||
for (fmt --; fmt[-1] != '%'; fmt --) | |||||
/* do nothing */; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/* sprintbuf is used to save enough information of a buffer */ | |||||
struct sprintbuf { | |||||
char *buf; // address pointer points to the first unused memory | |||||
char *ebuf; // points the end of the buffer | |||||
int cnt; // the number of characters that have been placed in this buffer | |||||
}; | |||||
/* * | |||||
* sprintputch - 'print' a single character in a buffer | |||||
* @ch: the character will be printed | |||||
* @b: the buffer to place the character @ch | |||||
* */ | |||||
static void | |||||
sprintputch(int ch, struct sprintbuf *b) { | |||||
b->cnt ++; | |||||
if (b->buf < b->ebuf) { | |||||
*b->buf ++ = ch; | |||||
} | |||||
} | |||||
/* * | |||||
* snprintf - format a string and place it in a buffer | |||||
* @str: the buffer to place the result into | |||||
* @size: the size of buffer, including the trailing null space | |||||
* @fmt: the format string to use | |||||
* */ | |||||
int | |||||
snprintf(char *str, size_t size, const char *fmt, ...) { | |||||
va_list ap; | |||||
int cnt; | |||||
va_start(ap, fmt); | |||||
cnt = vsnprintf(str, size, fmt, ap); | |||||
va_end(ap); | |||||
return cnt; | |||||
} | |||||
/* * | |||||
* vsnprintf - format a string and place it in a buffer, it's called with a va_list | |||||
* instead of a variable number of arguments | |||||
* @str: the buffer to place the result into | |||||
* @size: the size of buffer, including the trailing null space | |||||
* @fmt: the format string to use | |||||
* @ap: arguments for the format string | |||||
* | |||||
* The return value is the number of characters which would be generated for the | |||||
* given input, excluding the trailing '\0'. | |||||
* | |||||
* Call this function if you are already dealing with a va_list. | |||||
* Or you probably want snprintf() instead. | |||||
* */ | |||||
int | |||||
vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { | |||||
struct sprintbuf b = {str, str + size - 1, 0}; | |||||
if (str == NULL || b.buf > b.ebuf) { | |||||
return -E_INVAL; | |||||
} | |||||
// print the string to the buffer | |||||
vprintfmt((void*)sprintputch, &b, fmt, ap); | |||||
// null terminate the buffer | |||||
*b.buf = '\0'; | |||||
return b.cnt; | |||||
} | |||||
@ -0,0 +1,12 @@ | |||||
#ifndef __LIBS_STDARG_H__ | |||||
#define __LIBS_STDARG_H__ | |||||
/* compiler provides size of save area */ | |||||
typedef __builtin_va_list va_list; | |||||
#define va_start(ap, last) (__builtin_va_start(ap, last)) | |||||
#define va_arg(ap, type) (__builtin_va_arg(ap, type)) | |||||
#define va_end(ap) /*nothing*/ | |||||
#endif /* !__LIBS_STDARG_H__ */ | |||||
@ -0,0 +1,24 @@ | |||||
#ifndef __LIBS_STDIO_H__ | |||||
#define __LIBS_STDIO_H__ | |||||
#include <defs.h> | |||||
#include <stdarg.h> | |||||
/* kern/libs/stdio.c */ | |||||
int cprintf(const char *fmt, ...); | |||||
int vcprintf(const char *fmt, va_list ap); | |||||
void cputchar(int c); | |||||
int cputs(const char *str); | |||||
int getchar(void); | |||||
/* kern/libs/readline.c */ | |||||
char *readline(const char *prompt); | |||||
/* libs/printfmt.c */ | |||||
void printfmt(void (*putch)(int, void *), void *putdat, const char *fmt, ...); | |||||
void vprintfmt(void (*putch)(int, void *), void *putdat, const char *fmt, va_list ap); | |||||
int snprintf(char *str, size_t size, const char *fmt, ...); | |||||
int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); | |||||
#endif /* !__LIBS_STDIO_H__ */ | |||||
@ -0,0 +1,367 @@ | |||||
#include <string.h> | |||||
#include <x86.h> | |||||
/* * | |||||
* strlen - calculate the length of the string @s, not including | |||||
* the terminating '\0' character. | |||||
* @s: the input string | |||||
* | |||||
* The strlen() function returns the length of string @s. | |||||
* */ | |||||
size_t | |||||
strlen(const char *s) { | |||||
size_t cnt = 0; | |||||
while (*s ++ != '\0') { | |||||
cnt ++; | |||||
} | |||||
return cnt; | |||||
} | |||||
/* * | |||||
* strnlen - calculate the length of the string @s, not including | |||||
* the terminating '\0' char acter, but at most @len. | |||||
* @s: the input string | |||||
* @len: the max-length that function will scan | |||||
* | |||||
* Note that, this function looks only at the first @len characters | |||||
* at @s, and never beyond @s + @len. | |||||
* | |||||
* The return value is strlen(s), if that is less than @len, or | |||||
* @len if there is no '\0' character among the first @len characters | |||||
* pointed by @s. | |||||
* */ | |||||
size_t | |||||
strnlen(const char *s, size_t len) { | |||||
size_t cnt = 0; | |||||
while (cnt < len && *s ++ != '\0') { | |||||
cnt ++; | |||||
} | |||||
return cnt; | |||||
} | |||||
/* * | |||||
* strcpy - copies the string pointed by @src into the array pointed by @dst, | |||||
* including the terminating null character. | |||||
* @dst: pointer to the destination array where the content is to be copied | |||||
* @src: string to be copied | |||||
* | |||||
* The return value is @dst. | |||||
* | |||||
* To avoid overflows, the size of array pointed by @dst should be long enough to | |||||
* contain the same string as @src (including the terminating null character), and | |||||
* should not overlap in memory with @src. | |||||
* */ | |||||
char * | |||||
strcpy(char *dst, const char *src) { | |||||
#ifdef __HAVE_ARCH_STRCPY | |||||
return __strcpy(dst, src); | |||||
#else | |||||
char *p = dst; | |||||
while ((*p ++ = *src ++) != '\0') | |||||
/* nothing */; | |||||
return dst; | |||||
#endif /* __HAVE_ARCH_STRCPY */ | |||||
} | |||||
/* * | |||||
* strncpy - copies the first @len characters of @src to @dst. If the end of string @src | |||||
* if found before @len characters have been copied, @dst is padded with '\0' until a | |||||
* total of @len characters have been written to it. | |||||
* @dst: pointer to the destination array where the content is to be copied | |||||
* @src: string to be copied | |||||
* @len: maximum number of characters to be copied from @src | |||||
* | |||||
* The return value is @dst | |||||
* */ | |||||
char * | |||||
strncpy(char *dst, const char *src, size_t len) { | |||||
char *p = dst; | |||||
while (len > 0) { | |||||
if ((*p = *src) != '\0') { | |||||
src ++; | |||||
} | |||||
p ++, len --; | |||||
} | |||||
return dst; | |||||
} | |||||
/* * | |||||
* strcmp - compares the string @s1 and @s2 | |||||
* @s1: string to be compared | |||||
* @s2: string to be compared | |||||
* | |||||
* This function starts comparing the first character of each string. If | |||||
* they are equal to each other, it continues with the following pairs until | |||||
* the characters differ or until a terminanting null-character is reached. | |||||
* | |||||
* Returns an integral value indicating the relationship between the strings: | |||||
* - A zero value indicates that both strings are equal; | |||||
* - A value greater than zero indicates that the first character that does | |||||
* not match has a greater value in @s1 than in @s2; | |||||
* - And a value less than zero indicates the opposite. | |||||
* */ | |||||
int | |||||
strcmp(const char *s1, const char *s2) { | |||||
#ifdef __HAVE_ARCH_STRCMP | |||||
return __strcmp(s1, s2); | |||||
#else | |||||
while (*s1 != '\0' && *s1 == *s2) { | |||||
s1 ++, s2 ++; | |||||
} | |||||
return (int)((unsigned char)*s1 - (unsigned char)*s2); | |||||
#endif /* __HAVE_ARCH_STRCMP */ | |||||
} | |||||
/* * | |||||
* strncmp - compares up to @n characters of the string @s1 to those of the string @s2 | |||||
* @s1: string to be compared | |||||
* @s2: string to be compared | |||||
* @n: maximum number of characters to compare | |||||
* | |||||
* This function starts comparing the first character of each string. If | |||||
* they are equal to each other, it continues with the following pairs until | |||||
* the characters differ, until a terminating null-character is reached, or | |||||
* until @n characters match in both strings, whichever happens first. | |||||
* */ | |||||
int | |||||
strncmp(const char *s1, const char *s2, size_t n) { | |||||
while (n > 0 && *s1 != '\0' && *s1 == *s2) { | |||||
n --, s1 ++, s2 ++; | |||||
} | |||||
return (n == 0) ? 0 : (int)((unsigned char)*s1 - (unsigned char)*s2); | |||||
} | |||||
/* * | |||||
* strchr - locates first occurrence of character in string | |||||
* @s: the input string | |||||
* @c: character to be located | |||||
* | |||||
* The strchr() function returns a pointer to the first occurrence of | |||||
* character in @s. If the value is not found, the function returns 'NULL'. | |||||
* */ | |||||
char * | |||||
strchr(const char *s, char c) { | |||||
while (*s != '\0') { | |||||
if (*s == c) { | |||||
return (char *)s; | |||||
} | |||||
s ++; | |||||
} | |||||
return NULL; | |||||
} | |||||
/* * | |||||
* strfind - locates first occurrence of character in string | |||||
* @s: the input string | |||||
* @c: character to be located | |||||
* | |||||
* The strfind() function is like strchr() except that if @c is | |||||
* not found in @s, then it returns a pointer to the null byte at the | |||||
* end of @s, rather than 'NULL'. | |||||
* */ | |||||
char * | |||||
strfind(const char *s, char c) { | |||||
while (*s != '\0') { | |||||
if (*s == c) { | |||||
break; | |||||
} | |||||
s ++; | |||||
} | |||||
return (char *)s; | |||||
} | |||||
/* * | |||||
* strtol - converts string to long integer | |||||
* @s: the input string that contains the representation of an integer number | |||||
* @endptr: reference to an object of type char *, whose value is set by the | |||||
* function to the next character in @s after the numerical value. This | |||||
* parameter can also be a null pointer, in which case it is not used. | |||||
* @base: x | |||||
* | |||||
* The function first discards as many whitespace characters as necessary until | |||||
* the first non-whitespace character is found. Then, starting from this character, | |||||
* takes as many characters as possible that are valid following a syntax that | |||||
* depends on the base parameter, and interprets them as a numerical value. Finally, | |||||
* a pointer to the first character following the integer representation in @s | |||||
* is stored in the object pointed by @endptr. | |||||
* | |||||
* If the value of base is zero, the syntax expected is similar to that of | |||||
* integer constants, which is formed by a succession of: | |||||
* - An optional plus or minus sign; | |||||
* - An optional prefix indicating octal or hexadecimal base ("0" or "0x" respectively) | |||||
* - A sequence of decimal digits (if no base prefix was specified) or either octal | |||||
* or hexadecimal digits if a specific prefix is present | |||||
* | |||||
* If the base value is between 2 and 36, the format expected for the integral number | |||||
* is a succession of the valid digits and/or letters needed to represent integers of | |||||
* the specified radix (starting from '0' and up to 'z'/'Z' for radix 36). The | |||||
* sequence may optionally be preceded by a plus or minus sign and, if base is 16, | |||||
* an optional "0x" or "0X" prefix. | |||||
* | |||||
* The strtol() function returns the converted integral number as a long int value. | |||||
* */ | |||||
long | |||||
strtol(const char *s, char **endptr, int base) { | |||||
int neg = 0; | |||||
long val = 0; | |||||
// gobble initial whitespace | |||||
while (*s == ' ' || *s == '\t') { | |||||
s ++; | |||||
} | |||||
// plus/minus sign | |||||
if (*s == '+') { | |||||
s ++; | |||||
} | |||||
else if (*s == '-') { | |||||
s ++, neg = 1; | |||||
} | |||||
// hex or octal base prefix | |||||
if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x')) { | |||||
s += 2, base = 16; | |||||
} | |||||
else if (base == 0 && s[0] == '0') { | |||||
s ++, base = 8; | |||||
} | |||||
else if (base == 0) { | |||||
base = 10; | |||||
} | |||||
// digits | |||||
while (1) { | |||||
int dig; | |||||
if (*s >= '0' && *s <= '9') { | |||||
dig = *s - '0'; | |||||
} | |||||
else if (*s >= 'a' && *s <= 'z') { | |||||
dig = *s - 'a' + 10; | |||||
} | |||||
else if (*s >= 'A' && *s <= 'Z') { | |||||
dig = *s - 'A' + 10; | |||||
} | |||||
else { | |||||
break; | |||||
} | |||||
if (dig >= base) { | |||||
break; | |||||
} | |||||
s ++, val = (val * base) + dig; | |||||
// we don't properly detect overflow! | |||||
} | |||||
if (endptr) { | |||||
*endptr = (char *) s; | |||||
} | |||||
return (neg ? -val : val); | |||||
} | |||||
/* * | |||||
* memset - sets the first @n bytes of the memory area pointed by @s | |||||
* to the specified value @c. | |||||
* @s: pointer the the memory area to fill | |||||
* @c: value to set | |||||
* @n: number of bytes to be set to the value | |||||
* | |||||
* The memset() function returns @s. | |||||
* */ | |||||
void * | |||||
memset(void *s, char c, size_t n) { | |||||
#ifdef __HAVE_ARCH_MEMSET | |||||
return __memset(s, c, n); | |||||
#else | |||||
char *p = s; | |||||
while (n -- > 0) { | |||||
*p ++ = c; | |||||
} | |||||
return s; | |||||
#endif /* __HAVE_ARCH_MEMSET */ | |||||
} | |||||
/* * | |||||
* memmove - copies the values of @n bytes from the location pointed by @src to | |||||
* the memory area pointed by @dst. @src and @dst are allowed to overlap. | |||||
* @dst pointer to the destination array where the content is to be copied | |||||
* @src pointer to the source of data to by copied | |||||
* @n: number of bytes to copy | |||||
* | |||||
* The memmove() function returns @dst. | |||||
* */ | |||||
void * | |||||
memmove(void *dst, const void *src, size_t n) { | |||||
#ifdef __HAVE_ARCH_MEMMOVE | |||||
return __memmove(dst, src, n); | |||||
#else | |||||
const char *s = src; | |||||
char *d = dst; | |||||
if (s < d && s + n > d) { | |||||
s += n, d += n; | |||||
while (n -- > 0) { | |||||
*-- d = *-- s; | |||||
} | |||||
} else { | |||||
while (n -- > 0) { | |||||
*d ++ = *s ++; | |||||
} | |||||
} | |||||
return dst; | |||||
#endif /* __HAVE_ARCH_MEMMOVE */ | |||||
} | |||||
/* * | |||||
* memcpy - copies the value of @n bytes from the location pointed by @src to | |||||
* the memory area pointed by @dst. | |||||
* @dst pointer to the destination array where the content is to be copied | |||||
* @src pointer to the source of data to by copied | |||||
* @n: number of bytes to copy | |||||
* | |||||
* The memcpy() returns @dst. | |||||
* | |||||
* Note that, the function does not check any terminating null character in @src, | |||||
* it always copies exactly @n bytes. To avoid overflows, the size of arrays pointed | |||||
* by both @src and @dst, should be at least @n bytes, and should not overlap | |||||
* (for overlapping memory area, memmove is a safer approach). | |||||
* */ | |||||
void * | |||||
memcpy(void *dst, const void *src, size_t n) { | |||||
#ifdef __HAVE_ARCH_MEMCPY | |||||
return __memcpy(dst, src, n); | |||||
#else | |||||
const char *s = src; | |||||
char *d = dst; | |||||
while (n -- > 0) { | |||||
*d ++ = *s ++; | |||||
} | |||||
return dst; | |||||
#endif /* __HAVE_ARCH_MEMCPY */ | |||||
} | |||||
/* * | |||||
* memcmp - compares two blocks of memory | |||||
* @v1: pointer to block of memory | |||||
* @v2: pointer to block of memory | |||||
* @n: number of bytes to compare | |||||
* | |||||
* The memcmp() functions returns an integral value indicating the | |||||
* relationship between the content of the memory blocks: | |||||
* - A zero value indicates that the contents of both memory blocks are equal; | |||||
* - A value greater than zero indicates that the first byte that does not | |||||
* match in both memory blocks has a greater value in @v1 than in @v2 | |||||
* as if evaluated as unsigned char values; | |||||
* - And a value less than zero indicates the opposite. | |||||
* */ | |||||
int | |||||
memcmp(const void *v1, const void *v2, size_t n) { | |||||
const char *s1 = (const char *)v1; | |||||
const char *s2 = (const char *)v2; | |||||
while (n -- > 0) { | |||||
if (*s1 != *s2) { | |||||
return (int)((unsigned char)*s1 - (unsigned char)*s2); | |||||
} | |||||
s1 ++, s2 ++; | |||||
} | |||||
return 0; | |||||
} | |||||
@ -0,0 +1,25 @@ | |||||
#ifndef __LIBS_STRING_H__ | |||||
#define __LIBS_STRING_H__ | |||||
#include <defs.h> | |||||
size_t strlen(const char *s); | |||||
size_t strnlen(const char *s, size_t len); | |||||
char *strcpy(char *dst, const char *src); | |||||
char *strncpy(char *dst, const char *src, size_t len); | |||||
int strcmp(const char *s1, const char *s2); | |||||
int strncmp(const char *s1, const char *s2, size_t n); | |||||
char *strchr(const char *s, char c); | |||||
char *strfind(const char *s, char c); | |||||
long strtol(const char *s, char **endptr, int base); | |||||
void *memset(void *s, char c, size_t n); | |||||
void *memmove(void *dst, const void *src, size_t n); | |||||
void *memcpy(void *dst, const void *src, size_t n); | |||||
int memcmp(const void *v1, const void *v2, size_t n); | |||||
#endif /* !__LIBS_STRING_H__ */ | |||||
@ -0,0 +1,191 @@ | |||||
#ifndef __LIBS_X86_H__ | |||||
#define __LIBS_X86_H__ | |||||
#include <defs.h> | |||||
#define do_div(n, base) ({ \ | |||||
unsigned long __upper, __low, __high, __mod, __base; \ | |||||
__base = (base); \ | |||||
asm("" : "=a" (__low), "=d" (__high) : "A" (n)); \ | |||||
__upper = __high; \ | |||||
if (__high != 0) { \ | |||||
__upper = __high % __base; \ | |||||
__high = __high / __base; \ | |||||
} \ | |||||
asm("divl %2" : "=a" (__low), "=d" (__mod) \ | |||||
: "rm" (__base), "0" (__low), "1" (__upper)); \ | |||||
asm("" : "=A" (n) : "a" (__low), "d" (__high)); \ | |||||
__mod; \ | |||||
}) | |||||
static inline uint8_t inb(uint16_t port) __attribute__((always_inline)); | |||||
static inline void insl(uint32_t port, void *addr, int cnt) __attribute__((always_inline)); | |||||
static inline void outb(uint16_t port, uint8_t data) __attribute__((always_inline)); | |||||
static inline void outw(uint16_t port, uint16_t data) __attribute__((always_inline)); | |||||
static inline uint32_t read_ebp(void) __attribute__((always_inline)); | |||||
/* Pseudo-descriptors used for LGDT, LLDT(not used) and LIDT instructions. */ | |||||
struct pseudodesc { | |||||
uint16_t pd_lim; // Limit | |||||
uint32_t pd_base; // Base address | |||||
} __attribute__ ((packed)); | |||||
static inline void lidt(struct pseudodesc *pd) __attribute__((always_inline)); | |||||
static inline void sti(void) __attribute__((always_inline)); | |||||
static inline void cli(void) __attribute__((always_inline)); | |||||
static inline void ltr(uint16_t sel) __attribute__((always_inline)); | |||||
static inline uint8_t | |||||
inb(uint16_t port) { | |||||
uint8_t data; | |||||
asm volatile ("inb %1, %0" : "=a" (data) : "d" (port)); | |||||
return data; | |||||
} | |||||
static inline void | |||||
insl(uint32_t port, void *addr, int cnt) { | |||||
asm volatile ( | |||||
"cld;" | |||||
"repne; insl;" | |||||
: "=D" (addr), "=c" (cnt) | |||||
: "d" (port), "0" (addr), "1" (cnt) | |||||
: "memory", "cc"); | |||||
} | |||||
static inline void | |||||
outb(uint16_t port, uint8_t data) { | |||||
asm volatile ("outb %0, %1" :: "a" (data), "d" (port)); | |||||
} | |||||
static inline void | |||||
outw(uint16_t port, uint16_t data) { | |||||
asm volatile ("outw %0, %1" :: "a" (data), "d" (port)); | |||||
} | |||||
static inline uint32_t | |||||
read_ebp(void) { | |||||
uint32_t ebp; | |||||
asm volatile ("movl %%ebp, %0" : "=r" (ebp)); | |||||
return ebp; | |||||
} | |||||
static inline void | |||||
lidt(struct pseudodesc *pd) { | |||||
asm volatile ("lidt (%0)" :: "r" (pd)); | |||||
} | |||||
static inline void | |||||
sti(void) { | |||||
asm volatile ("sti"); | |||||
} | |||||
static inline void | |||||
cli(void) { | |||||
asm volatile ("cli"); | |||||
} | |||||
static inline void | |||||
ltr(uint16_t sel) { | |||||
asm volatile ("ltr %0" :: "r" (sel)); | |||||
} | |||||
static inline int __strcmp(const char *s1, const char *s2) __attribute__((always_inline)); | |||||
static inline char *__strcpy(char *dst, const char *src) __attribute__((always_inline)); | |||||
static inline void *__memset(void *s, char c, size_t n) __attribute__((always_inline)); | |||||
static inline void *__memmove(void *dst, const void *src, size_t n) __attribute__((always_inline)); | |||||
static inline void *__memcpy(void *dst, const void *src, size_t n) __attribute__((always_inline)); | |||||
#ifndef __HAVE_ARCH_STRCMP | |||||
#define __HAVE_ARCH_STRCMP | |||||
static inline int | |||||
__strcmp(const char *s1, const char *s2) { | |||||
int d0, d1, ret; | |||||
asm volatile ( | |||||
"1: lodsb;" | |||||
"scasb;" | |||||
"jne 2f;" | |||||
"testb %%al, %%al;" | |||||
"jne 1b;" | |||||
"xorl %%eax, %%eax;" | |||||
"jmp 3f;" | |||||
"2: sbbl %%eax, %%eax;" | |||||
"orb $1, %%al;" | |||||
"3:" | |||||
: "=a" (ret), "=&S" (d0), "=&D" (d1) | |||||
: "1" (s1), "2" (s2) | |||||
: "memory"); | |||||
return ret; | |||||
} | |||||
#endif /* __HAVE_ARCH_STRCMP */ | |||||
#ifndef __HAVE_ARCH_STRCPY | |||||
#define __HAVE_ARCH_STRCPY | |||||
static inline char * | |||||
__strcpy(char *dst, const char *src) { | |||||
int d0, d1, d2; | |||||
asm volatile ( | |||||
"1: lodsb;" | |||||
"stosb;" | |||||
"testb %%al, %%al;" | |||||
"jne 1b;" | |||||
: "=&S" (d0), "=&D" (d1), "=&a" (d2) | |||||
: "0" (src), "1" (dst) : "memory"); | |||||
return dst; | |||||
} | |||||
#endif /* __HAVE_ARCH_STRCPY */ | |||||
#ifndef __HAVE_ARCH_MEMSET | |||||
#define __HAVE_ARCH_MEMSET | |||||
static inline void * | |||||
__memset(void *s, char c, size_t n) { | |||||
int d0, d1; | |||||
asm volatile ( | |||||
"rep; stosb;" | |||||
: "=&c" (d0), "=&D" (d1) | |||||
: "0" (n), "a" (c), "1" (s) | |||||
: "memory"); | |||||
return s; | |||||
} | |||||
#endif /* __HAVE_ARCH_MEMSET */ | |||||
#ifndef __HAVE_ARCH_MEMMOVE | |||||
#define __HAVE_ARCH_MEMMOVE | |||||
static inline void * | |||||
__memmove(void *dst, const void *src, size_t n) { | |||||
if (dst < src) { | |||||
return __memcpy(dst, src, n); | |||||
} | |||||
int d0, d1, d2; | |||||
asm volatile ( | |||||
"std;" | |||||
"rep; movsb;" | |||||
"cld;" | |||||
: "=&c" (d0), "=&S" (d1), "=&D" (d2) | |||||
: "0" (n), "1" (n - 1 + src), "2" (n - 1 + dst) | |||||
: "memory"); | |||||
return dst; | |||||
} | |||||
#endif /* __HAVE_ARCH_MEMMOVE */ | |||||
#ifndef __HAVE_ARCH_MEMCPY | |||||
#define __HAVE_ARCH_MEMCPY | |||||
static inline void * | |||||
__memcpy(void *dst, const void *src, size_t n) { | |||||
int d0, d1, d2; | |||||
asm volatile ( | |||||
"rep; movsl;" | |||||
"movl %4, %%ecx;" | |||||
"andl $3, %%ecx;" | |||||
"jz 1f;" | |||||
"rep; movsb;" | |||||
"1:" | |||||
: "=&c" (d0), "=&D" (d1), "=&S" (d2) | |||||
: "0" (n / 4), "g" (n), "1" (dst), "2" (src) | |||||
: "memory"); | |||||
return dst; | |||||
} | |||||
#endif /* __HAVE_ARCH_MEMCPY */ | |||||
#endif /* !__LIBS_X86_H__ */ | |||||
@ -0,0 +1,95 @@ | |||||
OBJPREFIX := __objs_ | |||||
.SECONDEXPANSION: | |||||
# -------------------- function begin -------------------- | |||||
# list all files in some directories: (#directories, #types) | |||||
listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\ | |||||
$(wildcard $(addsuffix $(SLASH)*,$(1)))) | |||||
# get .o obj files: (#files[, packet]) | |||||
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\ | |||||
$(addsuffix .o,$(basename $(1)))) | |||||
# get .d dependency files: (#files[, packet]) | |||||
todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2))) | |||||
totarget = $(addprefix $(BINDIR)$(SLASH),$(1)) | |||||
# change $(name) to $(OBJPREFIX)$(name): (#names) | |||||
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX)) | |||||
# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir]) | |||||
define cc_template | |||||
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@) | |||||
@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@ | |||||
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@) | |||||
@echo + cc $$< | |||||
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@ | |||||
ALLOBJS += $$(call toobj,$(1),$(4)) | |||||
endef | |||||
# compile file: (#files, cc[, flags, dir]) | |||||
define do_cc_compile | |||||
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4)))) | |||||
endef | |||||
# add files to packet: (#files, cc[, flags, packet, dir]) | |||||
define do_add_files_to_packet | |||||
__temp_packet__ := $(call packetname,$(4)) | |||||
ifeq ($$(origin $$(__temp_packet__)),undefined) | |||||
$$(__temp_packet__) := | |||||
endif | |||||
__temp_objs__ := $(call toobj,$(1),$(5)) | |||||
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5)))) | |||||
$$(__temp_packet__) += $$(__temp_objs__) | |||||
endef | |||||
# add objs to packet: (#objs, packet) | |||||
define do_add_objs_to_packet | |||||
__temp_packet__ := $(call packetname,$(2)) | |||||
ifeq ($$(origin $$(__temp_packet__)),undefined) | |||||
$$(__temp_packet__) := | |||||
endif | |||||
$$(__temp_packet__) += $(1) | |||||
endef | |||||
# add packets and objs to target (target, #packes, #objs[, cc, flags]) | |||||
define do_create_target | |||||
__temp_target__ = $(call totarget,$(1)) | |||||
__temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3) | |||||
TARGETS += $$(__temp_target__) | |||||
ifneq ($(4),) | |||||
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) | |||||
$(V)$(4) $(5) $$^ -o $$@ | |||||
else | |||||
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) | |||||
endif | |||||
endef | |||||
# finish all | |||||
define do_finish_all | |||||
ALLDEPS = $$(ALLOBJS:.o=.d) | |||||
$$(sort $$(dir $$(ALLOBJS)) $(BINDIR) $(OBJDIR)): | |||||
@$(MKDIR) $$@ | |||||
endef | |||||
# -------------------- function end -------------------- | |||||
# compile file: (#files, cc[, flags, dir]) | |||||
cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4))) | |||||
# add files to packet: (#files, cc[, flags, packet, dir]) | |||||
add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5))) | |||||
# add objs to packet: (#objs, packet) | |||||
add_objs = $(eval $(call do_add_objs_to_packet,$(1),$(2))) | |||||
# add packets and objs to target (target, #packes, #objs, cc, [, flags]) | |||||
create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5))) | |||||
read_packet = $(foreach p,$(call packetname,$(1)),$($(p))) | |||||
add_dependency = $(eval $(1): $(2)) | |||||
finish_all = $(eval $(call do_finish_all)) | |||||
@ -0,0 +1,4 @@ | |||||
file bin/kernel | |||||
target remote :1234 | |||||
break kern_init | |||||
continue |
@ -0,0 +1,348 @@ | |||||
#!/bin/sh | |||||
verbose=false | |||||
if [ "x$1" = "x-v" ]; then | |||||
verbose=true | |||||
out=/dev/stdout | |||||
err=/dev/stderr | |||||
else | |||||
out=/dev/null | |||||
err=/dev/null | |||||
fi | |||||
## make & makeopts | |||||
if gmake --version > /dev/null 2>&1; then | |||||
make=gmake; | |||||
else | |||||
make=make; | |||||
fi | |||||
makeopts="--quiet --no-print-directory -j" | |||||
make_print() { | |||||
echo `$make $makeopts print-$1` | |||||
} | |||||
## command tools | |||||
awk='awk' | |||||
bc='bc' | |||||
date='date' | |||||
grep='grep' | |||||
rm='rm -f' | |||||
sed='sed' | |||||
## symbol table | |||||
sym_table='obj/kernel.sym' | |||||
## gdb & gdbopts | |||||
gdb="$(make_print GCCPREFIX)gdb" | |||||
gdbport='1234' | |||||
gdb_in="$(make_print GRADE_GDB_IN)" | |||||
## qemu & qemuopts | |||||
qemu="$(make_print qemu)" | |||||
qemu_out="$(make_print GRADE_QEMU_OUT)" | |||||
if $qemu -nographic -help | grep -q '^-gdb'; then | |||||
qemugdb="-gdb tcp::$gdbport" | |||||
else | |||||
qemugdb="-s -p $gdbport" | |||||
fi | |||||
## default variables | |||||
default_timeout=30 | |||||
default_pts=5 | |||||
pts=5 | |||||
part=0 | |||||
part_pos=0 | |||||
total=0 | |||||
total_pos=0 | |||||
## default functions | |||||
update_score() { | |||||
total=`expr $total + $part` | |||||
total_pos=`expr $total_pos + $part_pos` | |||||
part=0 | |||||
part_pos=0 | |||||
} | |||||
get_time() { | |||||
echo `$date +%s.%N 2> /dev/null` | |||||
} | |||||
show_part() { | |||||
echo "Part $1 Score: $part/$part_pos" | |||||
echo | |||||
update_score | |||||
} | |||||
show_final() { | |||||
update_score | |||||
echo "Total Score: $total/$total_pos" | |||||
if [ $total -lt $total_pos ]; then | |||||
exit 1 | |||||
fi | |||||
} | |||||
show_time() { | |||||
t1=$(get_time) | |||||
time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null` | |||||
echo "(${time}s)" | |||||
} | |||||
show_build_tag() { | |||||
echo "$1:" | $awk '{printf "%-24s ", $0}' | |||||
} | |||||
show_check_tag() { | |||||
echo "$1:" | $awk '{printf " -%-40s ", $0}' | |||||
} | |||||
show_msg() { | |||||
echo $1 | |||||
shift | |||||
if [ $# -gt 0 ]; then | |||||
echo "$@" | awk '{printf " %s\n", $0}' | |||||
echo | |||||
fi | |||||
} | |||||
pass() { | |||||
show_msg OK "$@" | |||||
part=`expr $part + $pts` | |||||
part_pos=`expr $part_pos + $pts` | |||||
} | |||||
fail() { | |||||
show_msg WRONG "$@" | |||||
part_pos=`expr $part_pos + $pts` | |||||
} | |||||
run_qemu() { | |||||
# Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty, | |||||
# wait until $brkfun is reached or $timeout expires, then kill QEMU | |||||
qemuextra= | |||||
if [ "$brkfun" ]; then | |||||
qemuextra="-S $qemugdb" | |||||
fi | |||||
if [ -z "$timeout" ] || [ $timeout -le 0 ]; then | |||||
timeout=$default_timeout; | |||||
fi | |||||
t0=$(get_time) | |||||
( | |||||
ulimit -t $timeout | |||||
exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra | |||||
) > $out 2> $err & | |||||
pid=$! | |||||
# wait for QEMU to start | |||||
sleep 1 | |||||
if [ -n "$brkfun" ]; then | |||||
# find the address of the kernel $brkfun function | |||||
brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'` | |||||
( | |||||
echo "target remote localhost:$gdbport" | |||||
echo "break *0x$brkaddr" | |||||
echo "continue" | |||||
) > $gdb_in | |||||
$gdb -batch -nx -x $gdb_in > /dev/null 2>&1 | |||||
# make sure that QEMU is dead | |||||
# on OS X, exiting gdb doesn't always exit qemu | |||||
kill $pid > /dev/null 2>&1 | |||||
fi | |||||
} | |||||
build_run() { | |||||
# usage: build_run <tag> <args> | |||||
show_build_tag "$1" | |||||
shift | |||||
if $verbose; then | |||||
echo "$make $@ ..." | |||||
fi | |||||
$make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err | |||||
if [ $? -ne 0 ]; then | |||||
echo $make $@ failed | |||||
exit 1 | |||||
fi | |||||
# now run qemu and save the output | |||||
run_qemu | |||||
show_time | |||||
} | |||||
check_result() { | |||||
# usage: check_result <tag> <check> <check args...> | |||||
show_check_tag "$1" | |||||
shift | |||||
# give qemu some time to run (for asynchronous mode) | |||||
if [ ! -s $qemu_out ]; then | |||||
sleep 4 | |||||
fi | |||||
if [ ! -s $qemu_out ]; then | |||||
fail > /dev/null | |||||
echo 'no $qemu_out' | |||||
else | |||||
check=$1 | |||||
shift | |||||
$check "$@" | |||||
fi | |||||
} | |||||
check_regexps() { | |||||
okay=yes | |||||
not=0 | |||||
reg=0 | |||||
error= | |||||
for i do | |||||
if [ "x$i" = "x!" ]; then | |||||
not=1 | |||||
elif [ "x$i" = "x-" ]; then | |||||
reg=1 | |||||
else | |||||
if [ $reg -ne 0 ]; then | |||||
$grep '-E' "^$i\$" $qemu_out > /dev/null | |||||
else | |||||
$grep '-F' "$i" $qemu_out > /dev/null | |||||
fi | |||||
found=$(($? == 0)) | |||||
if [ $found -eq $not ]; then | |||||
if [ $found -eq 0 ]; then | |||||
msg="!! error: missing '$i'" | |||||
else | |||||
msg="!! error: got unexpected line '$i'" | |||||
fi | |||||
okay=no | |||||
if [ -z "$error" ]; then | |||||
error="$msg" | |||||
else | |||||
error="$error\n$msg" | |||||
fi | |||||
fi | |||||
not=0 | |||||
reg=0 | |||||
fi | |||||
done | |||||
if [ "$okay" = "yes" ]; then | |||||
pass | |||||
else | |||||
fail "$error" | |||||
if $verbose; then | |||||
exit 1 | |||||
fi | |||||
fi | |||||
} | |||||
run_test() { | |||||
# usage: run_test [-tag <tag>] [-Ddef...] [-check <check>] checkargs ... | |||||
tag= | |||||
check=check_regexps | |||||
while true; do | |||||
select= | |||||
case $1 in | |||||
-tag) | |||||
select=`expr substr $1 2 ${#1}` | |||||
eval $select='$2' | |||||
;; | |||||
esac | |||||
if [ -z "$select" ]; then | |||||
break | |||||
fi | |||||
shift | |||||
shift | |||||
done | |||||
defs= | |||||
while expr "x$1" : "x-D.*" > /dev/null; do | |||||
defs="DEFS+='$1' $defs" | |||||
shift | |||||
done | |||||
if [ "x$1" = "x-check" ]; then | |||||
check=$2 | |||||
shift | |||||
shift | |||||
fi | |||||
$make $makeopts touch > /dev/null 2>&1 | |||||
build_run "$tag" "$defs" | |||||
check_result 'check result' "$check" "$@" | |||||
} | |||||
quick_run() { | |||||
# usage: quick_run <tag> [-Ddef...] | |||||
tag="$1" | |||||
shift | |||||
defs= | |||||
while expr "x$1" : "x-D.*" > /dev/null; do | |||||
defs="DEFS+='$1' $defs" | |||||
shift | |||||
done | |||||
$make $makeopts touch > /dev/null 2>&1 | |||||
build_run "$tag" "$defs" | |||||
} | |||||
quick_check() { | |||||
# usage: quick_check <tag> checkargs ... | |||||
tag="$1" | |||||
shift | |||||
check_result "$tag" check_regexps "$@" | |||||
} | |||||
## kernel image | |||||
osimg=$(make_print ucoreimg) | |||||
## set default qemu-options | |||||
qemuopts="-hda $osimg" | |||||
## set break-function, default is readline | |||||
brkfun=readline | |||||
## check now!! | |||||
quick_run 'Check Output' | |||||
pts=10 | |||||
quick_check 'check ring 0' \ | |||||
'0: @ring 0' \ | |||||
'0: cs = 8' \ | |||||
'0: ds = 10' \ | |||||
'0: es = 10' \ | |||||
'0: ss = 10' | |||||
quick_check 'check switch to ring 3' \ | |||||
'+++ switch to user mode +++' \ | |||||
'1: @ring 3' \ | |||||
'1: cs = 1b' \ | |||||
'1: ds = 23' \ | |||||
'1: es = 23' \ | |||||
'1: ss = 23' | |||||
quick_check 'check switch to ring 0' \ | |||||
'+++ switch to kernel mode +++' \ | |||||
'2: @ring 0' \ | |||||
'2: cs = 8' \ | |||||
'2: ds = 10' \ | |||||
'2: es = 10' \ | |||||
'2: ss = 10' | |||||
quick_check 'check ticks' \ | |||||
'++ setup timer interrupts' \ | |||||
'100 ticks' \ | |||||
'End of Test.' | |||||
## print final-score | |||||
show_final | |||||
@ -0,0 +1,58 @@ | |||||
/* Simple linker script for the JOS kernel. | |||||
See the GNU ld 'info' manual ("info ld") to learn the syntax. */ | |||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") | |||||
OUTPUT_ARCH(i386) | |||||
ENTRY(kern_init) | |||||
SECTIONS { | |||||
/* Load the kernel at this address: "." means the current address */ | |||||
. = 0x100000; | |||||
.text : { | |||||
*(.text .stub .text.* .gnu.linkonce.t.*) | |||||
} | |||||
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ | |||||
.rodata : { | |||||
*(.rodata .rodata.* .gnu.linkonce.r.*) | |||||
} | |||||
/* Include debugging information in kernel memory */ | |||||
.stab : { | |||||
PROVIDE(__STAB_BEGIN__ = .); | |||||
*(.stab); | |||||
PROVIDE(__STAB_END__ = .); | |||||
BYTE(0) /* Force the linker to allocate space | |||||
for this section */ | |||||
} | |||||
.stabstr : { | |||||
PROVIDE(__STABSTR_BEGIN__ = .); | |||||
*(.stabstr); | |||||
PROVIDE(__STABSTR_END__ = .); | |||||
BYTE(0) /* Force the linker to allocate space | |||||
for this section */ | |||||
} | |||||
/* Adjust the address for the data segment to the next page */ | |||||
. = ALIGN(0x1000); | |||||
/* The data segment */ | |||||
.data : { | |||||
*(.data) | |||||
} | |||||
PROVIDE(edata = .); | |||||
.bss : { | |||||
*(.bss) | |||||
} | |||||
PROVIDE(end = .); | |||||
/DISCARD/ : { | |||||
*(.eh_frame .note.GNU-stack) | |||||
} | |||||
} |
@ -0,0 +1,43 @@ | |||||
#include <stdio.h> | |||||
#include <errno.h> | |||||
#include <string.h> | |||||
#include <sys/stat.h> | |||||
int | |||||
main(int argc, char *argv[]) { | |||||
struct stat st; | |||||
if (argc != 3) { | |||||
fprintf(stderr, "Usage: <input filename> <output filename>\n"); | |||||
return -1; | |||||
} | |||||
if (stat(argv[1], &st) != 0) { | |||||
fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno)); | |||||
return -1; | |||||
} | |||||
printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size); | |||||
if (st.st_size > 510) { | |||||
fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size); | |||||
return -1; | |||||
} | |||||
char buf[512]; | |||||
memset(buf, 0, sizeof(buf)); | |||||
FILE *ifp = fopen(argv[1], "rb"); | |||||
int size = fread(buf, 1, st.st_size, ifp); | |||||
if (size != st.st_size) { | |||||
fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size); | |||||
return -1; | |||||
} | |||||
fclose(ifp); | |||||
buf[510] = 0x55; | |||||
buf[511] = 0xAA; | |||||
FILE *ofp = fopen(argv[2], "wb+"); | |||||
size = fwrite(buf, 1, 512, ofp); | |||||
if (size != 512) { | |||||
fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size); | |||||
return -1; | |||||
} | |||||
fclose(ofp); | |||||
printf("build 512 bytes boot sector: '%s' success!\n", argv[2]); | |||||
return 0; | |||||
} | |||||
@ -0,0 +1,28 @@ | |||||
#include <stdio.h> | |||||
int | |||||
main(void) { | |||||
printf(".text\n"); | |||||
printf(".globl __alltraps\n"); | |||||
int i; | |||||
for (i = 0; i < 256; i ++) { | |||||
printf(".globl vector%d\n", i); | |||||
printf("vector%d:\n", i); | |||||
if ((i < 8 || i > 14) && i != 17) { | |||||
printf(" pushl \\$0\n"); | |||||
} | |||||
printf(" pushl $%d\n", i); | |||||
printf(" jmp __alltraps\n"); | |||||
} | |||||
printf("\n"); | |||||
printf("# vector table\n"); | |||||
printf(".data\n"); | |||||
printf(".globl __vectors\n"); | |||||
printf("__vectors:\n"); | |||||
for (i = 0; i < 256; i ++) { | |||||
printf(" .long vector%d\n", i); | |||||
} | |||||
return 0; | |||||
} | |||||
@ -0,0 +1,261 @@ | |||||
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 | |||||
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 | |||||
TAR := tar | |||||
ZIP := gzip | |||||
OBJDIR := obj | |||||
BINDIR := bin | |||||
ALLOBJS := | |||||
ALLDEPS := | |||||
TARGETS := | |||||
include tools/function.mk | |||||
listf_cc = $(call listf,$(1),$(CTYPE)) | |||||
# for cc | |||||
add_files_cc = $(call add_files,$(1),$(CC),$(CFLAGS) $(3),$(2),$(4)) | |||||
create_target_cc = $(call create_target,$(1),$(2),$(3),$(CC),$(CFLAGS)) | |||||
# for hostcc | |||||
add_files_host = $(call add_files,$(1),$(HOSTCC),$(HOSTCFLAGS),$(2),$(3)) | |||||
create_target_host = $(call create_target,$(1),$(2),$(3),$(HOSTCC),$(HOSTCFLAGS)) | |||||
cgtype = $(patsubst %.$(2),%.$(3),$(1)) | |||||
objfile = $(call toobj,$(1)) | |||||
asmfile = $(call cgtype,$(call toobj,$(1)),o,asm) | |||||
outfile = $(call cgtype,$(call toobj,$(1)),o,out) | |||||
symfile = $(call cgtype,$(call toobj,$(1)),o,sym) | |||||
# for match pattern | |||||
match = $(shell echo $(2) | $(AWK) '{for(i=1;i<=NF;i++){if(match("$(1)","^"$$(i)"$$")){exit 1;}}}'; echo $$?) | |||||
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | |||||
# include kernel/user | |||||
INCLUDE += libs/ | |||||
CFLAGS += $(addprefix -I,$(INCLUDE)) | |||||
LIBDIR += libs | |||||
$(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,) | |||||
# ------------------------------------------------------------------- | |||||
# kernel | |||||
KINCLUDE += kern/debug/ \ | |||||
kern/driver/ \ | |||||
kern/trap/ \ | |||||
kern/mm/ \ | |||||
kern/libs/ \ | |||||
kern/sync/ | |||||
KSRCDIR += kern/init \ | |||||
kern/libs \ | |||||
kern/debug \ | |||||
kern/driver \ | |||||
kern/trap \ | |||||
kern/mm \ | |||||
kern/sync | |||||
KCFLAGS += $(addprefix -I,$(KINCLUDE)) | |||||
$(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS)) | |||||
KOBJS = $(call read_packet,kernel libs) | |||||
# create kernel target | |||||
kernel = $(call totarget,kernel) | |||||
$(kernel): tools/kernel.ld | |||||
$(kernel): $(KOBJS) | |||||
@echo + ld $@ | |||||
$(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) | |||||
@$(OBJDUMP) -S $@ > $(call asmfile,kernel) | |||||
@$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel) | |||||
$(call create_target,kernel) | |||||
# ------------------------------------------------------------------- | |||||
# create bootblock | |||||
bootfiles = $(call listf_cc,boot) | |||||
$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc)) | |||||
bootblock = $(call totarget,bootblock) | |||||
$(bootblock): $(call toobj,boot/bootasm.S) $(call toobj,$(bootfiles)) | $(call totarget,sign) | |||||
@echo + ld $@ | |||||
$(V)$(LD) $(LDFLAGS) -N -T tools/boot.ld $^ -o $(call toobj,bootblock) | |||||
@$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock) | |||||
@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock) | |||||
@$(call totarget,sign) $(call outfile,bootblock) $(bootblock) | |||||
$(call create_target,bootblock) | |||||
# ------------------------------------------------------------------- | |||||
# create 'sign' tools | |||||
$(call add_files_host,tools/sign.c,sign,sign) | |||||
$(call create_target_host,sign,sign) | |||||
# ------------------------------------------------------------------- | |||||
# create ucore.img | |||||
UCOREIMG := $(call totarget,ucore.img) | |||||
$(UCOREIMG): $(kernel) $(bootblock) | |||||
$(V)dd if=/dev/zero of=$@ count=10000 | |||||
$(V)dd if=$(bootblock) of=$@ conv=notrunc | |||||
$(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc | |||||
$(call create_target,ucore.img) | |||||
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | |||||
$(call finish_all) | |||||
IGNORE_ALLDEPS = gdb \ | |||||
clean \ | |||||
distclean \ | |||||
grade \ | |||||
touch \ | |||||
print-.+ \ | |||||
handin | |||||
ifeq ($(call match,$(MAKECMDGOALS),$(IGNORE_ALLDEPS)),0) | |||||
-include $(ALLDEPS) | |||||
endif | |||||
# files for grade script | |||||
targets: $(TARGETS) | |||||
.DEFAULT_GOAL := targets | |||||
QEMUOPTS = -hda $(UCOREIMG) | |||||
.PHONY: qemu qemu-nox gdb debug debug-mon debug-nox | |||||
qemu: targets | |||||
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null | |||||
qemu-nox: targets | |||||
$(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic | |||||
gdb: | |||||
$(V)$(GDB) -q -x tools/gdbinit | |||||
debug: targets | |||||
$(V)$(QEMU) -S -s -parallel stdio $(QEMUOPTS) -serial null | |||||
debug-mon: targets | |||||
$(V)$(QEMU) -S -s -monitor stdio $(QEMUOPTS) -parallel null -serial null | |||||
debug-nox: targets | |||||
$(V)$(QEMU) -S -s -serial mon:stdio $(QEMUOPTS) -nographic | |||||
.PHONY: grade touch | |||||
GRADE_GDB_IN := .gdb.in | |||||
GRADE_QEMU_OUT := .qemu.out | |||||
HANDIN := lab2-handin.tar.gz | |||||
TOUCH_FILES := kern/trap/trap.c | |||||
MAKEOPTS := --quiet --no-print-directory | |||||
grade: | |||||
$(V)$(MAKE) $(MAKEOPTS) clean | |||||
$(V)$(SH) tools/grade.sh | |||||
touch: | |||||
$(V)$(foreach f,$(TOUCH_FILES),$(TOUCH) $(f)) | |||||
print-%: | |||||
@echo $($(shell echo $(patsubst print-%,%,$@) | $(TR) [a-z] [A-Z])) | |||||
.PHONY: clean distclean handin | |||||
clean: | |||||
$(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT) | |||||
$(V)$(RM) -r $(OBJDIR) $(BINDIR) | |||||
distclean: clean | |||||
$(V)$(RM) $(HANDIN) | |||||
handin: distclean | |||||
$(V)$(TAR) -cf - `find . -type f -o -type d | grep -v '^\.$$' | grep -v '/CVS/' \ | |||||
| grep -v '/\.git/' | grep -v '/\.svn/' | grep -v "$(HANDIN)"` \ | |||||
| $(ZIP) > $(HANDIN) | |||||
@ -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__ */ | |||||
@ -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 |
@ -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); | |||||
} | |||||
@ -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__ */ | |||||
@ -0,0 +1,323 @@ | |||||
#include <defs.h> | |||||
#include <x86.h> | |||||
#include <stab.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include <sync.h> | |||||
#include <kdebug.h> | |||||
#include <kmonitor.h> | |||||
#include <assert.h> | |||||
#define STACKFRAME_DEPTH 20 | |||||
extern const struct stab __STAB_BEGIN__[]; // beginning of stabs table | |||||
extern const struct stab __STAB_END__[]; // end of stabs table | |||||
extern const char __STABSTR_BEGIN__[]; // beginning of string table | |||||
extern const char __STABSTR_END__[]; // end of string table | |||||
/* debug information about a particular instruction pointer */ | |||||
struct eipdebuginfo { | |||||
const char *eip_file; // source code filename for eip | |||||
int eip_line; // source code line number for eip | |||||
const char *eip_fn_name; // name of function containing eip | |||||
int eip_fn_namelen; // length of function's name | |||||
uintptr_t eip_fn_addr; // start address of function | |||||
int eip_fn_narg; // number of function arguments | |||||
}; | |||||
/* * | |||||
* stab_binsearch - according to the input, the initial value of | |||||
* range [*@region_left, *@region_right], find a single stab entry | |||||
* that includes the address @addr and matches the type @type, | |||||
* and then save its boundary to the locations that pointed | |||||
* by @region_left and @region_right. | |||||
* | |||||
* Some stab types are arranged in increasing order by instruction address. | |||||
* For example, N_FUN stabs (stab entries with n_type == N_FUN), which | |||||
* mark functions, and N_SO stabs, which mark source files. | |||||
* | |||||
* Given an instruction address, this function finds the single stab entry | |||||
* of type @type that contains that address. | |||||
* | |||||
* The search takes place within the range [*@region_left, *@region_right]. | |||||
* Thus, to search an entire set of N stabs, you might do: | |||||
* | |||||
* left = 0; | |||||
* right = N - 1; (rightmost stab) | |||||
* stab_binsearch(stabs, &left, &right, type, addr); | |||||
* | |||||
* The search modifies *region_left and *region_right to bracket the @addr. | |||||
* *@region_left points to the matching stab that contains @addr, | |||||
* and *@region_right points just before the next stab. | |||||
* If *@region_left > *region_right, then @addr is not contained in any | |||||
* matching stab. | |||||
* | |||||
* For example, given these N_SO stabs: | |||||
* Index Type Address | |||||
* 0 SO f0100000 | |||||
* 13 SO f0100040 | |||||
* 117 SO f0100176 | |||||
* 118 SO f0100178 | |||||
* 555 SO f0100652 | |||||
* 556 SO f0100654 | |||||
* 657 SO f0100849 | |||||
* this code: | |||||
* left = 0, right = 657; | |||||
* stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184); | |||||
* will exit setting left = 118, right = 554. | |||||
* */ | |||||
static void | |||||
stab_binsearch(const struct stab *stabs, int *region_left, int *region_right, | |||||
int type, uintptr_t addr) { | |||||
int l = *region_left, r = *region_right, any_matches = 0; | |||||
while (l <= r) { | |||||
int true_m = (l + r) / 2, m = true_m; | |||||
// search for earliest stab with right type | |||||
while (m >= l && stabs[m].n_type != type) { | |||||
m --; | |||||
} | |||||
if (m < l) { // no match in [l, m] | |||||
l = true_m + 1; | |||||
continue; | |||||
} | |||||
// actual binary search | |||||
any_matches = 1; | |||||
if (stabs[m].n_value < addr) { | |||||
*region_left = m; | |||||
l = true_m + 1; | |||||
} else if (stabs[m].n_value > addr) { | |||||
*region_right = m - 1; | |||||
r = m - 1; | |||||
} else { | |||||
// exact match for 'addr', but continue loop to find | |||||
// *region_right | |||||
*region_left = m; | |||||
l = m; | |||||
addr ++; | |||||
} | |||||
} | |||||
if (!any_matches) { | |||||
*region_right = *region_left - 1; | |||||
} | |||||
else { | |||||
// find rightmost region containing 'addr' | |||||
l = *region_right; | |||||
for (; l > *region_left && stabs[l].n_type != type; l --) | |||||
/* do nothing */; | |||||
*region_left = l; | |||||
} | |||||
} | |||||
/* * | |||||
* debuginfo_eip - Fill in the @info structure with information about | |||||
* the specified instruction address, @addr. Returns 0 if information | |||||
* was found, and negative if not. But even if it returns negative it | |||||
* has stored some information into '*info'. | |||||
* */ | |||||
int | |||||
debuginfo_eip(uintptr_t addr, struct eipdebuginfo *info) { | |||||
const struct stab *stabs, *stab_end; | |||||
const char *stabstr, *stabstr_end; | |||||
info->eip_file = "<unknown>"; | |||||
info->eip_line = 0; | |||||
info->eip_fn_name = "<unknown>"; | |||||
info->eip_fn_namelen = 9; | |||||
info->eip_fn_addr = addr; | |||||
info->eip_fn_narg = 0; | |||||
stabs = __STAB_BEGIN__; | |||||
stab_end = __STAB_END__; | |||||
stabstr = __STABSTR_BEGIN__; | |||||
stabstr_end = __STABSTR_END__; | |||||
// String table validity checks | |||||
if (stabstr_end <= stabstr || stabstr_end[-1] != 0) { | |||||
return -1; | |||||
} | |||||
// Now we find the right stabs that define the function containing | |||||
// 'eip'. First, we find the basic source file containing 'eip'. | |||||
// Then, we look in that source file for the function. Then we look | |||||
// for the line number. | |||||
// Search the entire set of stabs for the source file (type N_SO). | |||||
int lfile = 0, rfile = (stab_end - stabs) - 1; | |||||
stab_binsearch(stabs, &lfile, &rfile, N_SO, addr); | |||||
if (lfile == 0) | |||||
return -1; | |||||
// Search within that file's stabs for the function definition | |||||
// (N_FUN). | |||||
int lfun = lfile, rfun = rfile; | |||||
int lline, rline; | |||||
stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr); | |||||
if (lfun <= rfun) { | |||||
// stabs[lfun] points to the function name | |||||
// in the string table, but check bounds just in case. | |||||
if (stabs[lfun].n_strx < stabstr_end - stabstr) { | |||||
info->eip_fn_name = stabstr + stabs[lfun].n_strx; | |||||
} | |||||
info->eip_fn_addr = stabs[lfun].n_value; | |||||
addr -= info->eip_fn_addr; | |||||
// Search within the function definition for the line number. | |||||
lline = lfun; | |||||
rline = rfun; | |||||
} else { | |||||
// Couldn't find function stab! Maybe we're in an assembly | |||||
// file. Search the whole file for the line number. | |||||
info->eip_fn_addr = addr; | |||||
lline = lfile; | |||||
rline = rfile; | |||||
} | |||||
info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name; | |||||
// Search within [lline, rline] for the line number stab. | |||||
// If found, set info->eip_line to the right line number. | |||||
// If not found, return -1. | |||||
stab_binsearch(stabs, &lline, &rline, N_SLINE, addr); | |||||
if (lline <= rline) { | |||||
info->eip_line = stabs[rline].n_desc; | |||||
} else { | |||||
return -1; | |||||
} | |||||
// Search backwards from the line number for the relevant filename stab. | |||||
// We can't just use the "lfile" stab because inlined functions | |||||
// can interpolate code from a different file! | |||||
// Such included source files use the N_SOL stab type. | |||||
while (lline >= lfile | |||||
&& stabs[lline].n_type != N_SOL | |||||
&& (stabs[lline].n_type != N_SO || !stabs[lline].n_value)) { | |||||
lline --; | |||||
} | |||||
if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr) { | |||||
info->eip_file = stabstr + stabs[lline].n_strx; | |||||
} | |||||
// Set eip_fn_narg to the number of arguments taken by the function, | |||||
// or 0 if there was no containing function. | |||||
if (lfun < rfun) { | |||||
for (lline = lfun + 1; | |||||
lline < rfun && stabs[lline].n_type == N_PSYM; | |||||
lline ++) { | |||||
info->eip_fn_narg ++; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
/* * | |||||
* print_kerninfo - print the information about kernel, including the location | |||||
* of kernel entry, the start addresses of data and text segements, the start | |||||
* address of free memory and how many memory that kernel has used. | |||||
* */ | |||||
void | |||||
print_kerninfo(void) { | |||||
extern char etext[], edata[], end[], kern_init[]; | |||||
cprintf("Special kernel symbols:\n"); | |||||
cprintf(" entry 0x%08x (phys)\n", kern_init); | |||||
cprintf(" etext 0x%08x (phys)\n", etext); | |||||
cprintf(" edata 0x%08x (phys)\n", edata); | |||||
cprintf(" end 0x%08x (phys)\n", end); | |||||
cprintf("Kernel executable memory footprint: %dKB\n", (end - kern_init + 1023)/1024); | |||||
} | |||||
/* * | |||||
* print_debuginfo - read and print the stat information for the address @eip, | |||||
* and info.eip_fn_addr should be the first address of the related function. | |||||
* */ | |||||
void | |||||
print_debuginfo(uintptr_t eip) { | |||||
struct eipdebuginfo info; | |||||
if (debuginfo_eip(eip, &info) != 0) { | |||||
cprintf(" <unknow>: -- 0x%08x --\n", eip); | |||||
} | |||||
else { | |||||
char fnname[256]; | |||||
int j; | |||||
for (j = 0; j < info.eip_fn_namelen; j ++) { | |||||
fnname[j] = info.eip_fn_name[j]; | |||||
} | |||||
fnname[j] = '\0'; | |||||
cprintf(" %s:%d: %s+%d\n", info.eip_file, info.eip_line, | |||||
fnname, eip - info.eip_fn_addr); | |||||
} | |||||
} | |||||
static __noinline uint32_t | |||||
read_eip(void) { | |||||
uint32_t eip; | |||||
asm volatile("movl 4(%%ebp), %0" : "=r" (eip)); | |||||
return eip; | |||||
} | |||||
/* * | |||||
* print_stackframe - print a list of the saved eip values from the nested 'call' | |||||
* instructions that led to the current point of execution | |||||
* | |||||
* The x86 stack pointer, namely esp, points to the lowest location on the stack | |||||
* that is currently in use. Everything below that location in stack is free. Pushing | |||||
* a value onto the stack will invole decreasing the stack pointer and then writing | |||||
* the value to the place that stack pointer pointes to. And popping a value do the | |||||
* opposite. | |||||
* | |||||
* The ebp (base pointer) register, in contrast, is associated with the stack | |||||
* primarily by software convention. On entry to a C function, the function's | |||||
* prologue code normally saves the previous function's base pointer by pushing | |||||
* it onto the stack, and then copies the current esp value into ebp for the duration | |||||
* of the function. If all the functions in a program obey this convention, | |||||
* then at any given point during the program's execution, it is possible to trace | |||||
* back through the stack by following the chain of saved ebp pointers and determining | |||||
* exactly what nested sequence of function calls caused this particular point in the | |||||
* program to be reached. This capability can be particularly useful, for example, | |||||
* when a particular function causes an assert failure or panic because bad arguments | |||||
* were passed to it, but you aren't sure who passed the bad arguments. A stack | |||||
* backtrace lets you find the offending function. | |||||
* | |||||
* The inline function read_ebp() can tell us the value of current ebp. And the | |||||
* non-inline function read_eip() is useful, it can read the value of current eip, | |||||
* since while calling this function, read_eip() can read the caller's eip from | |||||
* stack easily. | |||||
* | |||||
* In print_debuginfo(), the function debuginfo_eip() can get enough information about | |||||
* calling-chain. Finally print_stackframe() will trace and print them for debugging. | |||||
* | |||||
* Note that, the length of ebp-chain is limited. In boot/bootasm.S, before jumping | |||||
* to the kernel entry, the value of ebp has been set to zero, that's the boundary. | |||||
* */ | |||||
void | |||||
print_stackframe(void) { | |||||
/* LAB1 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]; | |||||
} | |||||
} | |||||
@ -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__ */ | |||||
@ -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; | |||||
} | |||||
@ -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__ */ | |||||
@ -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; | |||||
} | |||||
@ -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__ */ | |||||
@ -0,0 +1,45 @@ | |||||
#include <x86.h> | |||||
#include <trap.h> | |||||
#include <stdio.h> | |||||
#include <picirq.h> | |||||
/* * | |||||
* Support for time-related hardware gadgets - the 8253 timer, | |||||
* which generates interruptes on IRQ-0. | |||||
* */ | |||||
#define IO_TIMER1 0x040 // 8253 Timer #1 | |||||
/* * | |||||
* Frequency of all three count-down timers; (TIMER_FREQ/freq) | |||||
* is the appropriate count to generate a frequency of freq Hz. | |||||
* */ | |||||
#define TIMER_FREQ 1193182 | |||||
#define TIMER_DIV(x) ((TIMER_FREQ + (x) / 2) / (x)) | |||||
#define TIMER_MODE (IO_TIMER1 + 3) // timer mode port | |||||
#define TIMER_SEL0 0x00 // select counter 0 | |||||
#define TIMER_RATEGEN 0x04 // mode 2, rate generator | |||||
#define TIMER_16BIT 0x30 // r/w counter 16 bits, LSB first | |||||
volatile size_t ticks; | |||||
/* * | |||||
* clock_init - initialize 8253 clock to interrupt 100 times per second, | |||||
* and then enable IRQ_TIMER. | |||||
* */ | |||||
void | |||||
clock_init(void) { | |||||
// set 8253 timer-chip | |||||
outb(TIMER_MODE, TIMER_SEL0 | TIMER_RATEGEN | TIMER_16BIT); | |||||
outb(IO_TIMER1, TIMER_DIV(100) % 256); | |||||
outb(IO_TIMER1, TIMER_DIV(100) / 256); | |||||
// initialize time counter 'ticks' to zero | |||||
ticks = 0; | |||||
cprintf("++ setup timer interrupts\n"); | |||||
pic_enable(IRQ_TIMER); | |||||
} | |||||
@ -0,0 +1,11 @@ | |||||
#ifndef __KERN_DRIVER_CLOCK_H__ | |||||
#define __KERN_DRIVER_CLOCK_H__ | |||||
#include <defs.h> | |||||
extern volatile size_t ticks; | |||||
void clock_init(void); | |||||
#endif /* !__KERN_DRIVER_CLOCK_H__ */ | |||||
@ -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; | |||||
} | |||||
@ -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__ */ | |||||
@ -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(); | |||||
} | |||||
@ -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__ */ | |||||
@ -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__ */ | |||||
@ -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); | |||||
} | |||||
} | |||||
@ -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__ */ | |||||
@ -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) | |||||
@ -0,0 +1,104 @@ | |||||
#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> | |||||
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 | |||||
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(); | |||||
/* do nothing */ | |||||
while (1); | |||||
} | |||||
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(); | |||||
} | |||||
@ -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; | |||||
} | |||||
} | |||||
} | |||||
@ -0,0 +1,78 @@ | |||||
#include <defs.h> | |||||
#include <stdio.h> | |||||
#include <console.h> | |||||
/* HIGH level console I/O */ | |||||
/* * | |||||
* cputch - writes a single character @c to stdout, and it will | |||||
* increace the value of counter pointed by @cnt. | |||||
* */ | |||||
static void | |||||
cputch(int c, int *cnt) { | |||||
cons_putc(c); | |||||
(*cnt) ++; | |||||
} | |||||
/* * | |||||
* vcprintf - format a string and writes it to stdout | |||||
* | |||||
* The return value is the number of characters which would be | |||||
* written to stdout. | |||||
* | |||||
* Call this function if you are already dealing with a va_list. | |||||
* Or you probably want cprintf() instead. | |||||
* */ | |||||
int | |||||
vcprintf(const char *fmt, va_list ap) { | |||||
int cnt = 0; | |||||
vprintfmt((void*)cputch, &cnt, fmt, ap); | |||||
return cnt; | |||||
} | |||||
/* * | |||||
* cprintf - formats a string and writes it to stdout | |||||
* | |||||
* The return value is the number of characters which would be | |||||
* written to stdout. | |||||
* */ | |||||
int | |||||
cprintf(const char *fmt, ...) { | |||||
va_list ap; | |||||
int cnt; | |||||
va_start(ap, fmt); | |||||
cnt = vcprintf(fmt, ap); | |||||
va_end(ap); | |||||
return cnt; | |||||
} | |||||
/* cputchar - writes a single character to stdout */ | |||||
void | |||||
cputchar(int c) { | |||||
cons_putc(c); | |||||
} | |||||
/* * | |||||
* cputs- writes the string pointed by @str to stdout and | |||||
* appends a newline character. | |||||
* */ | |||||
int | |||||
cputs(const char *str) { | |||||
int cnt = 0; | |||||
char c; | |||||
while ((c = *str ++) != '\0') { | |||||
cputch(c, &cnt); | |||||
} | |||||
cputch('\n', &cnt); | |||||
return cnt; | |||||
} | |||||
/* getchar - reads a single non-zero character from stdin */ | |||||
int | |||||
getchar(void) { | |||||
int c; | |||||
while ((c = cons_getc()) == 0) | |||||
/* do nothing */; | |||||
return c; | |||||
} | |||||
@ -0,0 +1,294 @@ | |||||
#include <pmm.h> | |||||
#include <list.h> | |||||
#include <string.h> | |||||
#include <default_pmm.h> | |||||
/* In the first fit algorithm, the allocator keeps a list of free blocks (known as the free list) and, | |||||
on receiving a request for memory, scans along the list for the first block that is large enough to | |||||
satisfy the request. If the chosen block is significantly larger than that requested, then it is | |||||
usually split, and the remainder added to the list as another free block. | |||||
Please see Page 196~198, Section 8.2 of Yan Wei Ming's chinese book "Data Structure -- C programming language" | |||||
*/ | |||||
// LAB2 EXERCISE 1: YOUR CODE | |||||
// you should rewrite functions: default_init,default_init_memmap,default_alloc_pages, default_free_pages. | |||||
/* | |||||
* Details of FFMA | |||||
* (1) Prepare: In order to implement the First-Fit Mem Alloc (FFMA), we should manage the free mem block use some list. | |||||
* The struct free_area_t is used for the management of free mem blocks. At first you should | |||||
* be familiar to the struct list in list.h. struct list is a simple doubly linked list implementation. | |||||
* You should know howto USE: list_init, list_add(list_add_after), list_add_before, list_del, list_next, list_prev | |||||
* Another tricky method is to transform a general list struct to a special struct (such as struct page): | |||||
* you can find some MACRO: le2page (in memlayout.h), (in future labs: le2vma (in vmm.h), le2proc (in proc.h),etc.) | |||||
* (2) default_init: you can reuse the demo default_init fun to init the free_list and set nr_free to 0. | |||||
* free_list is used to record the free mem blocks. nr_free is the total number for free mem blocks. | |||||
* (3) default_init_memmap: CALL GRAPH: kern_init --> pmm_init-->page_init-->init_memmap--> pmm_manager->init_memmap | |||||
* This fun is used to init a free block (with parameter: addr_base, page_number). | |||||
* First you should init each page (in memlayout.h) in this free block, include: | |||||
* p->flags should be set bit PG_property (means this page is valid. In pmm_init fun (in pmm.c), | |||||
* the bit PG_reserved is setted in p->flags) | |||||
* if this page is free and is not the first page of free block, p->property should be set to 0. | |||||
* if this page is free and is the first page of free block, p->property should be set to total num of block. | |||||
* p->ref should be 0, because now p is free and no reference. | |||||
* We can use p->page_link to link this page to free_list, (such as: list_add_before(&free_list, &(p->page_link)); ) | |||||
* Finally, we should sum the number of free mem block: nr_free+=n | |||||
* (4) default_alloc_pages: search find a first free block (block size >=n) in free list and reszie the free block, return the addr | |||||
* of malloced block. | |||||
* (4.1) So you should search freelist like this: | |||||
* list_entry_t le = &free_list; | |||||
* while((le=list_next(le)) != &free_list) { | |||||
* .... | |||||
* (4.1.1) In while loop, get the struct page and check the p->property (record the num of free block) >=n? | |||||
* struct Page *p = le2page(le, page_link); | |||||
* if(p->property >= n){ ... | |||||
* (4.1.2) If we find this p, then it' means we find a free block(block size >=n), and the first n pages can be malloced. | |||||
* Some flag bits of this page should be setted: PG_reserved =1, PG_property =0 | |||||
* unlink the pages from free_list | |||||
* (4.1.2.1) If (p->property >n), we should re-caluclate number of the the rest of this free block, | |||||
* (such as: le2page(le,page_link))->property = p->property - n;) | |||||
* (4.1.3) re-caluclate nr_free (number of the the rest of all free block) | |||||
* (4.1.4) return p | |||||
* (4.2) If we can not find a free block (block size >=n), then return NULL | |||||
* (5) default_free_pages: relink the pages into free list, maybe merge small free blocks into big free blocks. | |||||
* (5.1) according the base addr of withdrawed blocks, search free list, find the correct position | |||||
* (from low to high addr), and insert the pages. (may use list_next, le2page, list_add_before) | |||||
* (5.2) reset the fields of pages, such as p->ref, p->flags (PageProperty) | |||||
* (5.3) try to merge low addr or high addr blocks. Notice: should change some pages's p->property correctly. | |||||
*/ | |||||
free_area_t free_area; | |||||
#define free_list (free_area.free_list) | |||||
#define nr_free (free_area.nr_free) | |||||
static void | |||||
default_init(void) { | |||||
list_init(&free_list); | |||||
nr_free = 0; | |||||
} | |||||
static void | |||||
default_init_memmap(struct Page *base, size_t n) { | |||||
assert(n > 0); | |||||
struct Page *p = base; | |||||
for (; p != base + n; p ++) { | |||||
assert(PageReserved(p)); | |||||
p->flags = 0; | |||||
SetPageProperty(p); | |||||
p->property = 0; | |||||
set_page_ref(p, 0); | |||||
list_add_before(&free_list, &(p->page_link)); | |||||
} | |||||
nr_free += n; | |||||
//first block | |||||
base->property = n; | |||||
} | |||||
static struct Page * | |||||
default_alloc_pages(size_t n) { | |||||
assert(n > 0); | |||||
if (n > nr_free) { | |||||
return NULL; | |||||
} | |||||
list_entry_t *le, *len; | |||||
le = &free_list; | |||||
while((le=list_next(le)) != &free_list) { | |||||
struct Page *p = le2page(le, page_link); | |||||
if(p->property >= n){ | |||||
int i; | |||||
for(i=0;i<n;i++){ | |||||
len = list_next(le); | |||||
struct Page *pp = le2page(le, page_link); | |||||
SetPageReserved(pp); | |||||
ClearPageProperty(pp); | |||||
list_del(le); | |||||
le = len; | |||||
} | |||||
if(p->property>n){ | |||||
(le2page(le,page_link))->property = p->property - n; | |||||
} | |||||
ClearPageProperty(p); | |||||
SetPageReserved(p); | |||||
nr_free -= n; | |||||
return p; | |||||
} | |||||
} | |||||
return NULL; | |||||
} | |||||
static void | |||||
default_free_pages(struct Page *base, size_t n) { | |||||
assert(n > 0); | |||||
assert(PageReserved(base)); | |||||
list_entry_t *le = &free_list; | |||||
struct Page * p; | |||||
while((le=list_next(le)) != &free_list) { | |||||
p = le2page(le, page_link); | |||||
if(p>base){ | |||||
break; | |||||
} | |||||
} | |||||
//list_add_before(le, base->page_link); | |||||
for(p=base;p<base+n;p++){ | |||||
list_add_before(le, &(p->page_link)); | |||||
} | |||||
base->flags = 0; | |||||
set_page_ref(base, 0); | |||||
ClearPageProperty(base); | |||||
SetPageProperty(base); | |||||
base->property = n; | |||||
p = le2page(le,page_link) ; | |||||
if( base+n == p ){ | |||||
base->property += p->property; | |||||
p->property = 0; | |||||
} | |||||
le = list_prev(&(base->page_link)); | |||||
p = le2page(le, page_link); | |||||
if(le!=&free_list && p==base-1){ | |||||
while(le!=&free_list){ | |||||
if(p->property){ | |||||
p->property += base->property; | |||||
base->property = 0; | |||||
break; | |||||
} | |||||
le = list_prev(le); | |||||
p = le2page(le,page_link); | |||||
} | |||||
} | |||||
nr_free += n; | |||||
return ; | |||||
} | |||||
static size_t | |||||
default_nr_free_pages(void) { | |||||
return nr_free; | |||||
} | |||||
static void | |||||
basic_check(void) { | |||||
struct Page *p0, *p1, *p2; | |||||
p0 = p1 = p2 = NULL; | |||||
assert((p0 = alloc_page()) != NULL); | |||||
assert((p1 = alloc_page()) != NULL); | |||||
assert((p2 = alloc_page()) != NULL); | |||||
assert(p0 != p1 && p0 != p2 && p1 != p2); | |||||
assert(page_ref(p0) == 0 && page_ref(p1) == 0 && page_ref(p2) == 0); | |||||
assert(page2pa(p0) < npage * PGSIZE); | |||||
assert(page2pa(p1) < npage * PGSIZE); | |||||
assert(page2pa(p2) < npage * PGSIZE); | |||||
list_entry_t free_list_store = free_list; | |||||
list_init(&free_list); | |||||
assert(list_empty(&free_list)); | |||||
unsigned int nr_free_store = nr_free; | |||||
nr_free = 0; | |||||
assert(alloc_page() == NULL); | |||||
free_page(p0); | |||||
free_page(p1); | |||||
free_page(p2); | |||||
assert(nr_free == 3); | |||||
assert((p0 = alloc_page()) != NULL); | |||||
assert((p1 = alloc_page()) != NULL); | |||||
assert((p2 = alloc_page()) != NULL); | |||||
assert(alloc_page() == NULL); | |||||
free_page(p0); | |||||
assert(!list_empty(&free_list)); | |||||
struct Page *p; | |||||
assert((p = alloc_page()) == p0); | |||||
assert(alloc_page() == NULL); | |||||
assert(nr_free == 0); | |||||
free_list = free_list_store; | |||||
nr_free = nr_free_store; | |||||
free_page(p); | |||||
free_page(p1); | |||||
free_page(p2); | |||||
} | |||||
// LAB2: below code is used to check the first fit allocation algorithm (your EXERCISE 1) | |||||
// NOTICE: You SHOULD NOT CHANGE basic_check, default_check functions! | |||||
static void | |||||
default_check(void) { | |||||
int count = 0, total = 0; | |||||
list_entry_t *le = &free_list; | |||||
while ((le = list_next(le)) != &free_list) { | |||||
struct Page *p = le2page(le, page_link); | |||||
assert(PageProperty(p)); | |||||
count ++, total += p->property; | |||||
} | |||||
assert(total == nr_free_pages()); | |||||
basic_check(); | |||||
struct Page *p0 = alloc_pages(5), *p1, *p2; | |||||
assert(p0 != NULL); | |||||
assert(!PageProperty(p0)); | |||||
list_entry_t free_list_store = free_list; | |||||
list_init(&free_list); | |||||
assert(list_empty(&free_list)); | |||||
assert(alloc_page() == NULL); | |||||
unsigned int nr_free_store = nr_free; | |||||
nr_free = 0; | |||||
free_pages(p0 + 2, 3); | |||||
assert(alloc_pages(4) == NULL); | |||||
assert(PageProperty(p0 + 2) && p0[2].property == 3); | |||||
assert((p1 = alloc_pages(3)) != NULL); | |||||
assert(alloc_page() == NULL); | |||||
assert(p0 + 2 == p1); | |||||
p2 = p0 + 1; | |||||
free_page(p0); | |||||
free_pages(p1, 3); | |||||
assert(PageProperty(p0) && p0->property == 1); | |||||
assert(PageProperty(p1) && p1->property == 3); | |||||
assert((p0 = alloc_page()) == p2 - 1); | |||||
free_page(p0); | |||||
assert((p0 = alloc_pages(2)) == p2 + 1); | |||||
free_pages(p0, 2); | |||||
free_page(p2); | |||||
assert((p0 = alloc_pages(5)) != NULL); | |||||
assert(alloc_page() == NULL); | |||||
assert(nr_free == 0); | |||||
nr_free = nr_free_store; | |||||
free_list = free_list_store; | |||||
free_pages(p0, 5); | |||||
le = &free_list; | |||||
while ((le = list_next(le)) != &free_list) { | |||||
struct Page *p = le2page(le, page_link); | |||||
count --, total -= p->property; | |||||
} | |||||
assert(count == 0); | |||||
assert(total == 0); | |||||
} | |||||
const struct pmm_manager default_pmm_manager = { | |||||
.name = "default_pmm_manager", | |||||
.init = default_init, | |||||
.init_memmap = default_init_memmap, | |||||
.alloc_pages = default_alloc_pages, | |||||
.free_pages = default_free_pages, | |||||
.nr_free_pages = default_nr_free_pages, | |||||
.check = default_check, | |||||
}; | |||||
@ -0,0 +1,9 @@ | |||||
#ifndef __KERN_MM_DEFAULT_PMM_H__ | |||||
#define __KERN_MM_DEFAULT_PMM_H__ | |||||
#include <pmm.h> | |||||
extern const struct pmm_manager default_pmm_manager; | |||||
#endif /* ! __KERN_MM_DEFAULT_PMM_H__ */ | |||||
@ -0,0 +1,130 @@ | |||||
#ifndef __KERN_MM_MEMLAYOUT_H__ | |||||
#define __KERN_MM_MEMLAYOUT_H__ | |||||
/* This file contains the definitions for memory management in our OS. */ | |||||
/* global segment number */ | |||||
#define SEG_KTEXT 1 | |||||
#define SEG_KDATA 2 | |||||
#define SEG_UTEXT 3 | |||||
#define SEG_UDATA 4 | |||||
#define SEG_TSS 5 | |||||
/* global descrptor numbers */ | |||||
#define GD_KTEXT ((SEG_KTEXT) << 3) // kernel text | |||||
#define GD_KDATA ((SEG_KDATA) << 3) // kernel data | |||||
#define GD_UTEXT ((SEG_UTEXT) << 3) // user text | |||||
#define GD_UDATA ((SEG_UDATA) << 3) // user data | |||||
#define GD_TSS ((SEG_TSS) << 3) // task segment selector | |||||
#define DPL_KERNEL (0) | |||||
#define DPL_USER (3) | |||||
#define KERNEL_CS ((GD_KTEXT) | DPL_KERNEL) | |||||
#define KERNEL_DS ((GD_KDATA) | DPL_KERNEL) | |||||
#define USER_CS ((GD_UTEXT) | DPL_USER) | |||||
#define USER_DS ((GD_UDATA) | DPL_USER) | |||||
/* * | |||||
* Virtual memory map: Permissions | |||||
* kernel/user | |||||
* | |||||
* 4G ------------------> +---------------------------------+ | |||||
* | | | |||||
* | Empty Memory (*) | | |||||
* | | | |||||
* +---------------------------------+ 0xFB000000 | |||||
* | Cur. Page Table (Kern, RW) | RW/-- PTSIZE | |||||
* VPT -----------------> +---------------------------------+ 0xFAC00000 | |||||
* | Invalid Memory (*) | --/-- | |||||
* KERNTOP -------------> +---------------------------------+ 0xF8000000 | |||||
* | | | |||||
* | Remapped Physical Memory | RW/-- KMEMSIZE | |||||
* | | | |||||
* KERNBASE ------------> +---------------------------------+ 0xC0000000 | |||||
* | | | |||||
* | | | |||||
* | | | |||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
* (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped. | |||||
* "Empty Memory" is normally unmapped, but user programs may map pages | |||||
* there if desired. | |||||
* | |||||
* */ | |||||
/* All physical memory mapped at this address */ | |||||
#define KERNBASE 0xC0000000 | |||||
#define KMEMSIZE 0x38000000 // the maximum amount of physical memory | |||||
#define KERNTOP (KERNBASE + KMEMSIZE) | |||||
/* * | |||||
* Virtual page table. Entry PDX[VPT] in the PD (Page Directory) contains | |||||
* a pointer to the page directory itself, thereby turning the PD into a page | |||||
* table, which maps all the PTEs (Page Table Entry) containing the page mappings | |||||
* for the entire virtual address space into that 4 Meg region starting at VPT. | |||||
* */ | |||||
#define VPT 0xFAC00000 | |||||
#define KSTACKPAGE 2 // # of pages in kernel stack | |||||
#define KSTACKSIZE (KSTACKPAGE * PGSIZE) // sizeof kernel stack | |||||
#ifndef __ASSEMBLER__ | |||||
#include <defs.h> | |||||
#include <atomic.h> | |||||
#include <list.h> | |||||
typedef uintptr_t pte_t; | |||||
typedef uintptr_t pde_t; | |||||
// some constants for bios interrupt 15h AX = 0xE820 | |||||
#define E820MAX 20 // number of entries in E820MAP | |||||
#define E820_ARM 1 // address range memory | |||||
#define E820_ARR 2 // address range reserved | |||||
struct e820map { | |||||
int nr_map; | |||||
struct { | |||||
uint64_t addr; | |||||
uint64_t size; | |||||
uint32_t type; | |||||
} __attribute__((packed)) map[E820MAX]; | |||||
}; | |||||
/* * | |||||
* struct Page - Page descriptor structures. Each Page describes one | |||||
* physical page. In kern/mm/pmm.h, you can find lots of useful functions | |||||
* that convert Page to other data types, such as phyical address. | |||||
* */ | |||||
struct Page { | |||||
int ref; // page frame's reference counter | |||||
uint32_t flags; // array of flags that describe the status of the page frame | |||||
unsigned int property; // the num of free block, used in first fit pm manager | |||||
list_entry_t page_link; // free list link | |||||
}; | |||||
/* 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; | |||||
#endif /* !__ASSEMBLER__ */ | |||||
#endif /* !__KERN_MM_MEMLAYOUT_H__ */ | |||||
@ -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__ */ | |||||
@ -0,0 +1,647 @@ | |||||
#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> | |||||
/* * | |||||
* Task State Segment: | |||||
* | |||||
* The TSS may reside anywhere in memory. A special segment register called | |||||
* the Task Register (TR) holds a segment selector that points a valid TSS | |||||
* segment descriptor which resides in the GDT. Therefore, to use a TSS | |||||
* the following must be done in function gdt_init: | |||||
* - create a TSS descriptor entry in GDT | |||||
* - add enough information to the TSS in memory as needed | |||||
* - load the TR register with a segment selector for that segment | |||||
* | |||||
* There are several fileds in TSS for specifying the new stack pointer when a | |||||
* privilege level change happens. But only the fields SS0 and ESP0 are useful | |||||
* in our os kernel. | |||||
* | |||||
* The field SS0 contains the stack segment selector for CPL = 0, and the ESP0 | |||||
* contains the new ESP value for CPL = 0. When an interrupt happens in protected | |||||
* mode, the x86 CPU will look in the TSS for SS0 and ESP0 and load their value | |||||
* into SS and ESP respectively. | |||||
* */ | |||||
static struct taskstate ts = {0}; | |||||
// virtual address of physicall page array | |||||
struct Page *pages; | |||||
// amount of physical memory (in pages) | |||||
size_t npage = 0; | |||||
// virtual address of boot-time page directory | |||||
pde_t *boot_pgdir = NULL; | |||||
// physical address of boot-time page directory | |||||
uintptr_t boot_cr3; | |||||
// physical memory management | |||||
const struct pmm_manager *pmm_manager; | |||||
/* * | |||||
* The page directory entry corresponding to the virtual address range | |||||
* [VPT, VPT + PTSIZE) points to the page directory itself. Thus, the page | |||||
* directory is treated as a page table as well as a page directory. | |||||
* | |||||
* One result of treating the page directory as a page table is that all PTEs | |||||
* can be accessed though a "virtual page table" at virtual address VPT. And the | |||||
* PTE for number n is stored in vpt[n]. | |||||
* | |||||
* A second consequence is that the contents of the current page directory will | |||||
* always available at virtual address PGADDR(PDX(VPT), PDX(VPT), 0), to which | |||||
* vpd is set bellow. | |||||
* */ | |||||
pte_t * const vpt = (pte_t *)VPT; | |||||
pde_t * const vpd = (pde_t *)PGADDR(PDX(VPT), PDX(VPT), 0); | |||||
/* * | |||||
* Global Descriptor Table: | |||||
* | |||||
* The kernel and user segments are identical (except for the DPL). To load | |||||
* the %ss register, the CPL must equal the DPL. Thus, we must duplicate the | |||||
* segments for the user and the kernel. Defined as follows: | |||||
* - 0x0 : unused (always faults -- for trapping NULL far pointers) | |||||
* - 0x8 : kernel code segment | |||||
* - 0x10: kernel data segment | |||||
* - 0x18: user code segment | |||||
* - 0x20: user data segment | |||||
* - 0x28: defined for tss, initialized in gdt_init | |||||
* */ | |||||
static struct segdesc gdt[] = { | |||||
SEG_NULL, | |||||
[SEG_KTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_KERNEL), | |||||
[SEG_KDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_KERNEL), | |||||
[SEG_UTEXT] = SEG(STA_X | STA_R, 0x0, 0xFFFFFFFF, DPL_USER), | |||||
[SEG_UDATA] = SEG(STA_W, 0x0, 0xFFFFFFFF, DPL_USER), | |||||
[SEG_TSS] = SEG_NULL, | |||||
}; | |||||
static struct pseudodesc gdt_pd = { | |||||
sizeof(gdt) - 1, (uintptr_t)gdt | |||||
}; | |||||
static void check_alloc_page(void); | |||||
static void check_pgdir(void); | |||||
static void check_boot_pgdir(void); | |||||
/* * | |||||
* lgdt - load the global descriptor table register and reset the | |||||
* data/code segement registers for kernel. | |||||
* */ | |||||
static inline void | |||||
lgdt(struct pseudodesc *pd) { | |||||
asm volatile ("lgdt (%0)" :: "r" (pd)); | |||||
asm volatile ("movw %%ax, %%gs" :: "a" (USER_DS)); | |||||
asm volatile ("movw %%ax, %%fs" :: "a" (USER_DS)); | |||||
asm volatile ("movw %%ax, %%es" :: "a" (KERNEL_DS)); | |||||
asm volatile ("movw %%ax, %%ds" :: "a" (KERNEL_DS)); | |||||
asm volatile ("movw %%ax, %%ss" :: "a" (KERNEL_DS)); | |||||
// reload cs | |||||
asm volatile ("ljmp %0, $1f\n 1:\n" :: "i" (KERNEL_CS)); | |||||
} | |||||
/* * | |||||
* load_esp0 - change the ESP0 in default task state segment, | |||||
* so that we can use different kernel stack when we trap frame | |||||
* user to kernel. | |||||
* */ | |||||
void | |||||
load_esp0(uintptr_t esp0) { | |||||
ts.ts_esp0 = esp0; | |||||
} | |||||
/* gdt_init - initialize the default GDT and TSS */ | |||||
static void | |||||
gdt_init(void) { | |||||
// set boot kernel stack and default SS0 | |||||
load_esp0((uintptr_t)bootstacktop); | |||||
ts.ts_ss0 = KERNEL_DS; | |||||
// initialize the TSS filed of the gdt | |||||
gdt[SEG_TSS] = SEGTSS(STS_T32A, (uintptr_t)&ts, sizeof(ts), DPL_KERNEL); | |||||
// reload all segment registers | |||||
lgdt(&gdt_pd); | |||||
// load the TSS | |||||
ltr(GD_TSS); | |||||
} | |||||
//init_pmm_manager - initialize a pmm_manager instance | |||||
static void | |||||
init_pmm_manager(void) { | |||||
pmm_manager = &default_pmm_manager; | |||||
cprintf("memory management: %s\n", pmm_manager->name); | |||||
pmm_manager->init(); | |||||
} | |||||
//init_memmap - call pmm->init_memmap to build Page struct for free memory | |||||
static void | |||||
init_memmap(struct Page *base, size_t n) { | |||||
pmm_manager->init_memmap(base, n); | |||||
} | |||||
//alloc_pages - call pmm->alloc_pages to allocate a continuous n*PAGESIZE memory | |||||
struct Page * | |||||
alloc_pages(size_t n) { | |||||
struct Page *page=NULL; | |||||
bool intr_flag; | |||||
local_intr_save(intr_flag); | |||||
{ | |||||
page = pmm_manager->alloc_pages(n); | |||||
} | |||||
local_intr_restore(intr_flag); | |||||
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(); | |||||
} | |||||
//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); | |||||
} | |||||
} | |||||
//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); | |||||
} | |||||
} | |||||
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"); | |||||
} | |||||
@ -0,0 +1,143 @@ | |||||
#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); | |||||
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__ */ | |||||
@ -0,0 +1,28 @@ | |||||
#ifndef __KERN_SYNC_SYNC_H__ | |||||
#define __KERN_SYNC_SYNC_H__ | |||||
#include <x86.h> | |||||
#include <intr.h> | |||||
#include <mmu.h> | |||||
static inline bool | |||||
__intr_save(void) { | |||||
if (read_eflags() & FL_IF) { | |||||
intr_disable(); | |||||
return 1; | |||||
} | |||||
return 0; | |||||
} | |||||
static inline void | |||||
__intr_restore(bool flag) { | |||||
if (flag) { | |||||
intr_enable(); | |||||
} | |||||
} | |||||
#define local_intr_save(x) do { x = __intr_save(); } while (0) | |||||
#define local_intr_restore(x) __intr_restore(x); | |||||
#endif /* !__KERN_SYNC_SYNC_H__ */ | |||||
@ -0,0 +1,197 @@ | |||||
#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 <kdebug.h> | |||||
#define TICK_NUM 100 | |||||
static void print_ticks() { | |||||
cprintf("%d ticks\n",TICK_NUM); | |||||
#ifdef DEBUG_GRADE | |||||
cprintf("End of Test.\n"); | |||||
panic("EOT: kernel seems ok."); | |||||
#endif | |||||
} | |||||
/* * | |||||
* Interrupt descriptor table: | |||||
* | |||||
* Must be built at run time because shifted function addresses can't | |||||
* be represented in relocation records. | |||||
* */ | |||||
static struct gatedesc idt[256] = {{0}}; | |||||
static struct pseudodesc idt_pd = { | |||||
sizeof(idt) - 1, (uintptr_t)idt | |||||
}; | |||||
/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */ | |||||
void | |||||
idt_init(void) { | |||||
/* LAB1 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! | |||||
*/ | |||||
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); | |||||
} | |||||
lidt(&idt_pd); | |||||
} | |||||
static const char * | |||||
trapname(int trapno) { | |||||
static const char * const excnames[] = { | |||||
"Divide error", | |||||
"Debug", | |||||
"Non-Maskable Interrupt", | |||||
"Breakpoint", | |||||
"Overflow", | |||||
"BOUND Range Exceeded", | |||||
"Invalid Opcode", | |||||
"Device Not Available", | |||||
"Double Fault", | |||||
"Coprocessor Segment Overrun", | |||||
"Invalid TSS", | |||||
"Segment Not Present", | |||||
"Stack Fault", | |||||
"General Protection", | |||||
"Page Fault", | |||||
"(unknown trap)", | |||||
"x87 FPU Floating-Point Error", | |||||
"Alignment Check", | |||||
"Machine-Check", | |||||
"SIMD Floating-Point Exception" | |||||
}; | |||||
if (trapno < sizeof(excnames)/sizeof(const char * const)) { | |||||
return excnames[trapno]; | |||||
} | |||||
if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) { | |||||
return "Hardware Interrupt"; | |||||
} | |||||
return "(unknown trap)"; | |||||
} | |||||
/* trap_in_kernel - test if trap happened in kernel */ | |||||
bool | |||||
trap_in_kernel(struct trapframe *tf) { | |||||
return (tf->tf_cs == (uint16_t)KERNEL_CS); | |||||
} | |||||
static const char *IA32flags[] = { | |||||
"CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF", | |||||
"TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL, | |||||
"RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL, | |||||
}; | |||||
void | |||||
print_trapframe(struct trapframe *tf) { | |||||
cprintf("trapframe at %p\n", tf); | |||||
print_regs(&tf->tf_regs); | |||||
cprintf(" ds 0x----%04x\n", tf->tf_ds); | |||||
cprintf(" es 0x----%04x\n", tf->tf_es); | |||||
cprintf(" fs 0x----%04x\n", tf->tf_fs); | |||||
cprintf(" gs 0x----%04x\n", tf->tf_gs); | |||||
cprintf(" trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno)); | |||||
cprintf(" err 0x%08x\n", tf->tf_err); | |||||
cprintf(" eip 0x%08x\n", tf->tf_eip); | |||||
cprintf(" cs 0x----%04x\n", tf->tf_cs); | |||||
cprintf(" flag 0x%08x ", tf->tf_eflags); | |||||
int i, j; | |||||
for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) { | |||||
if ((tf->tf_eflags & j) && IA32flags[i] != NULL) { | |||||
cprintf("%s,", IA32flags[i]); | |||||
} | |||||
} | |||||
cprintf("IOPL=%d\n", (tf->tf_eflags & FL_IOPL_MASK) >> 12); | |||||
if (!trap_in_kernel(tf)) { | |||||
cprintf(" esp 0x%08x\n", tf->tf_esp); | |||||
cprintf(" ss 0x----%04x\n", tf->tf_ss); | |||||
} | |||||
} | |||||
void | |||||
print_regs(struct pushregs *regs) { | |||||
cprintf(" edi 0x%08x\n", regs->reg_edi); | |||||
cprintf(" esi 0x%08x\n", regs->reg_esi); | |||||
cprintf(" ebp 0x%08x\n", regs->reg_ebp); | |||||
cprintf(" oesp 0x%08x\n", regs->reg_oesp); | |||||
cprintf(" ebx 0x%08x\n", regs->reg_ebx); | |||||
cprintf(" edx 0x%08x\n", regs->reg_edx); | |||||
cprintf(" ecx 0x%08x\n", regs->reg_ecx); | |||||
cprintf(" eax 0x%08x\n", regs->reg_eax); | |||||
} | |||||
/* trap_dispatch - dispatch based on what type of trap occurred */ | |||||
static void | |||||
trap_dispatch(struct trapframe *tf) { | |||||
char c; | |||||
switch (tf->tf_trapno) { | |||||
case IRQ_OFFSET + IRQ_TIMER: | |||||
/* 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! | |||||
*/ | |||||
ticks ++; | |||||
if (ticks % TICK_NUM == 0) { | |||||
print_ticks(); | |||||
} | |||||
break; | |||||
case IRQ_OFFSET + IRQ_COM1: | |||||
c = cons_getc(); | |||||
cprintf("serial [%03d] %c\n", c, c); | |||||
break; | |||||
case IRQ_OFFSET + IRQ_KBD: | |||||
c = cons_getc(); | |||||
cprintf("kbd [%03d] %c\n", c, c); | |||||
break; | |||||
//LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes. | |||||
case T_SWITCH_TOU: | |||||
case T_SWITCH_TOK: | |||||
panic("T_SWITCH_** ??\n"); | |||||
break; | |||||
case IRQ_OFFSET + IRQ_IDE1: | |||||
case IRQ_OFFSET + IRQ_IDE2: | |||||
/* do nothing */ | |||||
break; | |||||
default: | |||||
// in kernel, it must be a mistake | |||||
if ((tf->tf_cs & 3) == 0) { | |||||
print_trapframe(tf); | |||||
panic("unexpected trap in kernel.\n"); | |||||
} | |||||
} | |||||
} | |||||
/* * | |||||
* trap - handles or dispatches an exception/interrupt. if and when trap() returns, | |||||
* the code in kern/trap/trapentry.S restores the old CPU state saved in the | |||||
* trapframe and then uses the iret instruction to return from the exception. | |||||
* */ | |||||
void | |||||
trap(struct trapframe *tf) { | |||||
// dispatch based on what type of trap occurred | |||||
trap_dispatch(tf); | |||||
} | |||||
@ -0,0 +1,91 @@ | |||||
#ifndef __KERN_TRAP_TRAP_H__ | |||||
#define __KERN_TRAP_TRAP_H__ | |||||
#include <defs.h> | |||||
/* Trap Numbers */ | |||||
/* Processor-defined: */ | |||||
#define T_DIVIDE 0 // divide error | |||||
#define T_DEBUG 1 // debug exception | |||||
#define T_NMI 2 // non-maskable interrupt | |||||
#define T_BRKPT 3 // breakpoint | |||||
#define T_OFLOW 4 // overflow | |||||
#define T_BOUND 5 // bounds check | |||||
#define T_ILLOP 6 // illegal opcode | |||||
#define T_DEVICE 7 // device not available | |||||
#define T_DBLFLT 8 // double fault | |||||
// #define T_COPROC 9 // reserved (not used since 486) | |||||
#define T_TSS 10 // invalid task switch segment | |||||
#define T_SEGNP 11 // segment not present | |||||
#define T_STACK 12 // stack exception | |||||
#define T_GPFLT 13 // general protection fault | |||||
#define T_PGFLT 14 // page fault | |||||
// #define T_RES 15 // reserved | |||||
#define T_FPERR 16 // floating point error | |||||
#define T_ALIGN 17 // aligment check | |||||
#define T_MCHK 18 // machine check | |||||
#define T_SIMDERR 19 // SIMD floating point error | |||||
#define T_SYSCALL 0x80 // SYSCALL, ONLY FOR THIS PROJ | |||||
/* Hardware IRQ numbers. We receive these as (IRQ_OFFSET + IRQ_xx) */ | |||||
#define IRQ_OFFSET 32 // IRQ 0 corresponds to int IRQ_OFFSET | |||||
#define IRQ_TIMER 0 | |||||
#define IRQ_KBD 1 | |||||
#define IRQ_COM1 4 | |||||
#define IRQ_IDE1 14 | |||||
#define IRQ_IDE2 15 | |||||
#define IRQ_ERROR 19 | |||||
#define IRQ_SPURIOUS 31 | |||||
/* * | |||||
* These are arbitrarily chosen, but with care not to overlap | |||||
* processor defined exceptions or interrupt vectors. | |||||
* */ | |||||
#define T_SWITCH_TOU 120 // user/kernel switch | |||||
#define T_SWITCH_TOK 121 // user/kernel switch | |||||
/* registers as pushed by pushal */ | |||||
struct pushregs { | |||||
uint32_t reg_edi; | |||||
uint32_t reg_esi; | |||||
uint32_t reg_ebp; | |||||
uint32_t reg_oesp; /* Useless */ | |||||
uint32_t reg_ebx; | |||||
uint32_t reg_edx; | |||||
uint32_t reg_ecx; | |||||
uint32_t reg_eax; | |||||
}; | |||||
struct trapframe { | |||||
struct pushregs tf_regs; | |||||
uint16_t tf_gs; | |||||
uint16_t tf_padding0; | |||||
uint16_t tf_fs; | |||||
uint16_t tf_padding1; | |||||
uint16_t tf_es; | |||||
uint16_t tf_padding2; | |||||
uint16_t tf_ds; | |||||
uint16_t tf_padding3; | |||||
uint32_t tf_trapno; | |||||
/* below here defined by x86 hardware */ | |||||
uint32_t tf_err; | |||||
uintptr_t tf_eip; | |||||
uint16_t tf_cs; | |||||
uint16_t tf_padding4; | |||||
uint32_t tf_eflags; | |||||
/* below here only when crossing rings, such as from user to kernel */ | |||||
uintptr_t tf_esp; | |||||
uint16_t tf_ss; | |||||
uint16_t tf_padding5; | |||||
} __attribute__((packed)); | |||||
void idt_init(void); | |||||
void print_trapframe(struct trapframe *tf); | |||||
void print_regs(struct pushregs *regs); | |||||
bool trap_in_kernel(struct trapframe *tf); | |||||
#endif /* !__KERN_TRAP_TRAP_H__ */ | |||||
@ -0,0 +1,44 @@ | |||||
#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 | |||||
@ -0,0 +1,57 @@ | |||||
#ifndef __LIBS_ATOMIC_H__ | |||||
#define __LIBS_ATOMIC_H__ | |||||
/* Atomic operations that C can't guarantee us. Useful for resource counting etc.. */ | |||||
static inline void set_bit(int nr, volatile void *addr) __attribute__((always_inline)); | |||||
static inline void clear_bit(int nr, volatile void *addr) __attribute__((always_inline)); | |||||
static inline void change_bit(int nr, volatile void *addr) __attribute__((always_inline)); | |||||
static inline bool test_bit(int nr, volatile void *addr) __attribute__((always_inline)); | |||||
/* * | |||||
* set_bit - Atomically set a bit in memory | |||||
* @nr: the bit to set | |||||
* @addr: the address to start counting from | |||||
* | |||||
* Note that @nr may be almost arbitrarily large; this function is not | |||||
* restricted to acting on a single-word quantity. | |||||
* */ | |||||
static inline void | |||||
set_bit(int nr, volatile void *addr) { | |||||
asm volatile ("btsl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr)); | |||||
} | |||||
/* * | |||||
* clear_bit - Atomically clears a bit in memory | |||||
* @nr: the bit to clear | |||||
* @addr: the address to start counting from | |||||
* */ | |||||
static inline void | |||||
clear_bit(int nr, volatile void *addr) { | |||||
asm volatile ("btrl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr)); | |||||
} | |||||
/* * | |||||
* change_bit - Atomically toggle a bit in memory | |||||
* @nr: the bit to change | |||||
* @addr: the address to start counting from | |||||
* */ | |||||
static inline void | |||||
change_bit(int nr, volatile void *addr) { | |||||
asm volatile ("btcl %1, %0" :"=m" (*(volatile long *)addr) : "Ir" (nr)); | |||||
} | |||||
/* * | |||||
* test_bit - Determine whether a bit is set | |||||
* @nr: the bit to test | |||||
* @addr: the address to count from | |||||
* */ | |||||
static inline bool | |||||
test_bit(int nr, volatile void *addr) { | |||||
int oldbit; | |||||
asm volatile ("btl %2, %1; sbbl %0,%0" : "=r" (oldbit) : "m" (*(volatile long *)addr), "Ir" (nr)); | |||||
return oldbit != 0; | |||||
} | |||||
#endif /* !__LIBS_ATOMIC_H__ */ | |||||
@ -0,0 +1,68 @@ | |||||
#ifndef __LIBS_DEFS_H__ | |||||
#define __LIBS_DEFS_H__ | |||||
#ifndef NULL | |||||
#define NULL ((void *)0) | |||||
#endif | |||||
#define __always_inline inline __attribute__((always_inline)) | |||||
#define __noinline __attribute__((noinline)) | |||||
#define __noreturn __attribute__((noreturn)) | |||||
/* Represents true-or-false values */ | |||||
typedef int bool; | |||||
/* Explicitly-sized versions of integer types */ | |||||
typedef char int8_t; | |||||
typedef unsigned char uint8_t; | |||||
typedef short int16_t; | |||||
typedef unsigned short uint16_t; | |||||
typedef int int32_t; | |||||
typedef unsigned int uint32_t; | |||||
typedef long long int64_t; | |||||
typedef unsigned long long uint64_t; | |||||
/* * | |||||
* Pointers and addresses are 32 bits long. | |||||
* We use pointer types to represent addresses, | |||||
* uintptr_t to represent the numerical values of addresses. | |||||
* */ | |||||
typedef int32_t intptr_t; | |||||
typedef uint32_t uintptr_t; | |||||
/* size_t is used for memory object sizes */ | |||||
typedef uintptr_t size_t; | |||||
/* used for page numbers */ | |||||
typedef size_t ppn_t; | |||||
/* * | |||||
* Rounding operations (efficient when n is a power of 2) | |||||
* Round down to the nearest multiple of n | |||||
* */ | |||||
#define ROUNDDOWN(a, n) ({ \ | |||||
size_t __a = (size_t)(a); \ | |||||
(typeof(a))(__a - __a % (n)); \ | |||||
}) | |||||
/* Round up to the nearest multiple of n */ | |||||
#define ROUNDUP(a, n) ({ \ | |||||
size_t __n = (size_t)(n); \ | |||||
(typeof(a))(ROUNDDOWN((size_t)(a) + __n - 1, __n)); \ | |||||
}) | |||||
/* Return the offset of 'member' relative to the beginning of a struct type */ | |||||
#define offsetof(type, member) \ | |||||
((size_t)(&((type *)0)->member)) | |||||
/* * | |||||
* to_struct - get the struct from a ptr | |||||
* @ptr: a struct pointer of member | |||||
* @type: the type of the struct this is embedded in | |||||
* @member: the name of the member within the struct | |||||
* */ | |||||
#define to_struct(ptr, type, member) \ | |||||
((type *)((char *)(ptr) - offsetof(type, member))) | |||||
#endif /* !__LIBS_DEFS_H__ */ | |||||
@ -0,0 +1,40 @@ | |||||
#ifndef __LIBS_ELF_H__ | |||||
#define __LIBS_ELF_H__ | |||||
#include <defs.h> | |||||
#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian | |||||
/* file header */ | |||||
struct elfhdr { | |||||
uint32_t e_magic; // must equal ELF_MAGIC | |||||
uint8_t e_elf[12]; | |||||
uint16_t e_type; // 1=relocatable, 2=executable, 3=shared object, 4=core image | |||||
uint16_t e_machine; // 3=x86, 4=68K, etc. | |||||
uint32_t e_version; // file version, always 1 | |||||
uint32_t e_entry; // entry point if executable | |||||
uint32_t e_phoff; // file position of program header or 0 | |||||
uint32_t e_shoff; // file position of section header or 0 | |||||
uint32_t e_flags; // architecture-specific flags, usually 0 | |||||
uint16_t e_ehsize; // size of this elf header | |||||
uint16_t e_phentsize; // size of an entry in program header | |||||
uint16_t e_phnum; // number of entries in program header or 0 | |||||
uint16_t e_shentsize; // size of an entry in section header | |||||
uint16_t e_shnum; // number of entries in section header or 0 | |||||
uint16_t e_shstrndx; // section number that contains section name strings | |||||
}; | |||||
/* program section header */ | |||||
struct proghdr { | |||||
uint32_t p_type; // loadable code or data, dynamic linking info,etc. | |||||
uint32_t p_offset; // file offset of segment | |||||
uint32_t p_va; // virtual address to map segment | |||||
uint32_t p_pa; // physical address, not used | |||||
uint32_t p_filesz; // size of segment in file | |||||
uint32_t p_memsz; // size of segment in memory (bigger if contains bss) | |||||
uint32_t p_flags; // read/write/execute bits | |||||
uint32_t p_align; // required alignment, invariably hardware page size | |||||
}; | |||||
#endif /* !__LIBS_ELF_H__ */ | |||||
@ -0,0 +1,16 @@ | |||||
#ifndef __LIBS_ERROR_H__ | |||||
#define __LIBS_ERROR_H__ | |||||
/* kernel error codes -- keep in sync with list in lib/printfmt.c */ | |||||
#define E_UNSPECIFIED 1 // Unspecified or unknown problem | |||||
#define E_BAD_PROC 2 // Process doesn't exist or otherwise | |||||
#define E_INVAL 3 // Invalid parameter | |||||
#define E_NO_MEM 4 // Request failed due to memory shortage | |||||
#define E_NO_FREE_PROC 5 // Attempt to create a new process beyond | |||||
#define E_FAULT 6 // Memory fault | |||||
/* the maximum allowed */ | |||||
#define MAXERROR 6 | |||||
#endif /* !__LIBS_ERROR_H__ */ | |||||
@ -0,0 +1,163 @@ | |||||
#ifndef __LIBS_LIST_H__ | |||||
#define __LIBS_LIST_H__ | |||||
#ifndef __ASSEMBLER__ | |||||
#include <defs.h> | |||||
/* * | |||||
* Simple doubly linked list implementation. | |||||
* | |||||
* Some of the internal functions ("__xxx") are useful when manipulating | |||||
* whole lists rather than single entries, as sometimes we already know | |||||
* the next/prev entries and we can generate better code by using them | |||||
* directly rather than using the generic single-entry routines. | |||||
* */ | |||||
struct list_entry { | |||||
struct list_entry *prev, *next; | |||||
}; | |||||
typedef struct list_entry list_entry_t; | |||||
static inline void list_init(list_entry_t *elm) __attribute__((always_inline)); | |||||
static inline void list_add(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); | |||||
static inline void list_add_before(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); | |||||
static inline void list_add_after(list_entry_t *listelm, list_entry_t *elm) __attribute__((always_inline)); | |||||
static inline void list_del(list_entry_t *listelm) __attribute__((always_inline)); | |||||
static inline void list_del_init(list_entry_t *listelm) __attribute__((always_inline)); | |||||
static inline bool list_empty(list_entry_t *list) __attribute__((always_inline)); | |||||
static inline list_entry_t *list_next(list_entry_t *listelm) __attribute__((always_inline)); | |||||
static inline list_entry_t *list_prev(list_entry_t *listelm) __attribute__((always_inline)); | |||||
static inline void __list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) __attribute__((always_inline)); | |||||
static inline void __list_del(list_entry_t *prev, list_entry_t *next) __attribute__((always_inline)); | |||||
/* * | |||||
* list_init - initialize a new entry | |||||
* @elm: new entry to be initialized | |||||
* */ | |||||
static inline void | |||||
list_init(list_entry_t *elm) { | |||||
elm->prev = elm->next = elm; | |||||
} | |||||
/* * | |||||
* list_add - add a new entry | |||||
* @listelm: list head to add after | |||||
* @elm: new entry to be added | |||||
* | |||||
* Insert the new element @elm *after* the element @listelm which | |||||
* is already in the list. | |||||
* */ | |||||
static inline void | |||||
list_add(list_entry_t *listelm, list_entry_t *elm) { | |||||
list_add_after(listelm, elm); | |||||
} | |||||
/* * | |||||
* list_add_before - add a new entry | |||||
* @listelm: list head to add before | |||||
* @elm: new entry to be added | |||||
* | |||||
* Insert the new element @elm *before* the element @listelm which | |||||
* is already in the list. | |||||
* */ | |||||
static inline void | |||||
list_add_before(list_entry_t *listelm, list_entry_t *elm) { | |||||
__list_add(elm, listelm->prev, listelm); | |||||
} | |||||
/* * | |||||
* list_add_after - add a new entry | |||||
* @listelm: list head to add after | |||||
* @elm: new entry to be added | |||||
* | |||||
* Insert the new element @elm *after* the element @listelm which | |||||
* is already in the list. | |||||
* */ | |||||
static inline void | |||||
list_add_after(list_entry_t *listelm, list_entry_t *elm) { | |||||
__list_add(elm, listelm, listelm->next); | |||||
} | |||||
/* * | |||||
* list_del - deletes entry from list | |||||
* @listelm: the element to delete from the list | |||||
* | |||||
* Note: list_empty() on @listelm does not return true after this, the entry is | |||||
* in an undefined state. | |||||
* */ | |||||
static inline void | |||||
list_del(list_entry_t *listelm) { | |||||
__list_del(listelm->prev, listelm->next); | |||||
} | |||||
/* * | |||||
* list_del_init - deletes entry from list and reinitialize it. | |||||
* @listelm: the element to delete from the list. | |||||
* | |||||
* Note: list_empty() on @listelm returns true after this. | |||||
* */ | |||||
static inline void | |||||
list_del_init(list_entry_t *listelm) { | |||||
list_del(listelm); | |||||
list_init(listelm); | |||||
} | |||||
/* * | |||||
* list_empty - tests whether a list is empty | |||||
* @list: the list to test. | |||||
* */ | |||||
static inline bool | |||||
list_empty(list_entry_t *list) { | |||||
return list->next == list; | |||||
} | |||||
/* * | |||||
* list_next - get the next entry | |||||
* @listelm: the list head | |||||
**/ | |||||
static inline list_entry_t * | |||||
list_next(list_entry_t *listelm) { | |||||
return listelm->next; | |||||
} | |||||
/* * | |||||
* list_prev - get the previous entry | |||||
* @listelm: the list head | |||||
**/ | |||||
static inline list_entry_t * | |||||
list_prev(list_entry_t *listelm) { | |||||
return listelm->prev; | |||||
} | |||||
/* * | |||||
* Insert a new entry between two known consecutive entries. | |||||
* | |||||
* This is only for internal list manipulation where we know | |||||
* the prev/next entries already! | |||||
* */ | |||||
static inline void | |||||
__list_add(list_entry_t *elm, list_entry_t *prev, list_entry_t *next) { | |||||
prev->next = next->prev = elm; | |||||
elm->next = next; | |||||
elm->prev = prev; | |||||
} | |||||
/* * | |||||
* Delete a list entry by making the prev/next entries point to each other. | |||||
* | |||||
* This is only for internal list manipulation where we know | |||||
* the prev/next entries already! | |||||
* */ | |||||
static inline void | |||||
__list_del(list_entry_t *prev, list_entry_t *next) { | |||||
prev->next = next; | |||||
next->prev = prev; | |||||
} | |||||
#endif /* !__ASSEMBLER__ */ | |||||
#endif /* !__LIBS_LIST_H__ */ | |||||
@ -0,0 +1,340 @@ | |||||
#include <defs.h> | |||||
#include <x86.h> | |||||
#include <error.h> | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
/* * | |||||
* Space or zero padding and a field width are supported for the numeric | |||||
* formats only. | |||||
* | |||||
* The special format %e takes an integer error code | |||||
* and prints a string describing the error. | |||||
* The integer may be positive or negative, | |||||
* so that -E_NO_MEM and E_NO_MEM are equivalent. | |||||
* */ | |||||
static const char * const error_string[MAXERROR + 1] = { | |||||
[0] NULL, | |||||
[E_UNSPECIFIED] "unspecified error", | |||||
[E_BAD_PROC] "bad process", | |||||
[E_INVAL] "invalid parameter", | |||||
[E_NO_MEM] "out of memory", | |||||
[E_NO_FREE_PROC] "out of processes", | |||||
[E_FAULT] "segmentation fault", | |||||
}; | |||||
/* * | |||||
* printnum - print a number (base <= 16) in reverse order | |||||
* @putch: specified putch function, print a single character | |||||
* @putdat: used by @putch function | |||||
* @num: the number will be printed | |||||
* @base: base for print, must be in [1, 16] | |||||
* @width: maximum number of digits, if the actual width is less than @width, use @padc instead | |||||
* @padc: character that padded on the left if the actual width is less than @width | |||||
* */ | |||||
static void | |||||
printnum(void (*putch)(int, void*), void *putdat, | |||||
unsigned long long num, unsigned base, int width, int padc) { | |||||
unsigned long long result = num; | |||||
unsigned mod = do_div(result, base); | |||||
// first recursively print all preceding (more significant) digits | |||||
if (num >= base) { | |||||
printnum(putch, putdat, result, base, width - 1, padc); | |||||
} else { | |||||
// print any needed pad characters before first digit | |||||
while (-- width > 0) | |||||
putch(padc, putdat); | |||||
} | |||||
// then print this (the least significant) digit | |||||
putch("0123456789abcdef"[mod], putdat); | |||||
} | |||||
/* * | |||||
* getuint - get an unsigned int of various possible sizes from a varargs list | |||||
* @ap: a varargs list pointer | |||||
* @lflag: determines the size of the vararg that @ap points to | |||||
* */ | |||||
static unsigned long long | |||||
getuint(va_list *ap, int lflag) { | |||||
if (lflag >= 2) { | |||||
return va_arg(*ap, unsigned long long); | |||||
} | |||||
else if (lflag) { | |||||
return va_arg(*ap, unsigned long); | |||||
} | |||||
else { | |||||
return va_arg(*ap, unsigned int); | |||||
} | |||||
} | |||||
/* * | |||||
* getint - same as getuint but signed, we can't use getuint because of sign extension | |||||
* @ap: a varargs list pointer | |||||
* @lflag: determines the size of the vararg that @ap points to | |||||
* */ | |||||
static long long | |||||
getint(va_list *ap, int lflag) { | |||||
if (lflag >= 2) { | |||||
return va_arg(*ap, long long); | |||||
} | |||||
else if (lflag) { | |||||
return va_arg(*ap, long); | |||||
} | |||||
else { | |||||
return va_arg(*ap, int); | |||||
} | |||||
} | |||||
/* * | |||||
* printfmt - format a string and print it by using putch | |||||
* @putch: specified putch function, print a single character | |||||
* @putdat: used by @putch function | |||||
* @fmt: the format string to use | |||||
* */ | |||||
void | |||||
printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...) { | |||||
va_list ap; | |||||
va_start(ap, fmt); | |||||
vprintfmt(putch, putdat, fmt, ap); | |||||
va_end(ap); | |||||
} | |||||
/* * | |||||
* vprintfmt - format a string and print it by using putch, it's called with a va_list | |||||
* instead of a variable number of arguments | |||||
* @putch: specified putch function, print a single character | |||||
* @putdat: used by @putch function | |||||
* @fmt: the format string to use | |||||
* @ap: arguments for the format string | |||||
* | |||||
* Call this function if you are already dealing with a va_list. | |||||
* Or you probably want printfmt() instead. | |||||
* */ | |||||
void | |||||
vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) { | |||||
register const char *p; | |||||
register int ch, err; | |||||
unsigned long long num; | |||||
int base, width, precision, lflag, altflag; | |||||
while (1) { | |||||
while ((ch = *(unsigned char *)fmt ++) != '%') { | |||||
if (ch == '\0') { | |||||
return; | |||||
} | |||||
putch(ch, putdat); | |||||
} | |||||
// Process a %-escape sequence | |||||
char padc = ' '; | |||||
width = precision = -1; | |||||
lflag = altflag = 0; | |||||
reswitch: | |||||
switch (ch = *(unsigned char *)fmt ++) { | |||||
// flag to pad on the right | |||||
case '-': | |||||
padc = '-'; | |||||
goto reswitch; | |||||
// flag to pad with 0's instead of spaces | |||||
case '0': | |||||
padc = '0'; | |||||
goto reswitch; | |||||
// width field | |||||
case '1' ... '9': | |||||
for (precision = 0; ; ++ fmt) { | |||||
precision = precision * 10 + ch - '0'; | |||||
ch = *fmt; | |||||
if (ch < '0' || ch > '9') { | |||||
break; | |||||
} | |||||
} | |||||
goto process_precision; | |||||
case '*': | |||||
precision = va_arg(ap, int); | |||||
goto process_precision; | |||||
case '.': | |||||
if (width < 0) | |||||
width = 0; | |||||
goto reswitch; | |||||
case '#': | |||||
altflag = 1; | |||||
goto reswitch; | |||||
process_precision: | |||||
if (width < 0) | |||||
width = precision, precision = -1; | |||||
goto reswitch; | |||||
// long flag (doubled for long long) | |||||
case 'l': | |||||
lflag ++; | |||||
goto reswitch; | |||||
// character | |||||
case 'c': | |||||
putch(va_arg(ap, int), putdat); | |||||
break; | |||||
// error message | |||||
case 'e': | |||||
err = va_arg(ap, int); | |||||
if (err < 0) { | |||||
err = -err; | |||||
} | |||||
if (err > MAXERROR || (p = error_string[err]) == NULL) { | |||||
printfmt(putch, putdat, "error %d", err); | |||||
} | |||||
else { | |||||
printfmt(putch, putdat, "%s", p); | |||||
} | |||||
break; | |||||
// string | |||||
case 's': | |||||
if ((p = va_arg(ap, char *)) == NULL) { | |||||
p = "(null)"; | |||||
} | |||||
if (width > 0 && padc != '-') { | |||||
for (width -= strnlen(p, precision); width > 0; width --) { | |||||
putch(padc, putdat); | |||||
} | |||||
} | |||||
for (; (ch = *p ++) != '\0' && (precision < 0 || -- precision >= 0); width --) { | |||||
if (altflag && (ch < ' ' || ch > '~')) { | |||||
putch('?', putdat); | |||||
} | |||||
else { | |||||
putch(ch, putdat); | |||||
} | |||||
} | |||||
for (; width > 0; width --) { | |||||
putch(' ', putdat); | |||||
} | |||||
break; | |||||
// (signed) decimal | |||||
case 'd': | |||||
num = getint(&ap, lflag); | |||||
if ((long long)num < 0) { | |||||
putch('-', putdat); | |||||
num = -(long long)num; | |||||
} | |||||
base = 10; | |||||
goto number; | |||||
// unsigned decimal | |||||
case 'u': | |||||
num = getuint(&ap, lflag); | |||||
base = 10; | |||||
goto number; | |||||
// (unsigned) octal | |||||
case 'o': | |||||
num = getuint(&ap, lflag); | |||||
base = 8; | |||||
goto number; | |||||
// pointer | |||||
case 'p': | |||||
putch('0', putdat); | |||||
putch('x', putdat); | |||||
num = (unsigned long long)(uintptr_t)va_arg(ap, void *); | |||||
base = 16; | |||||
goto number; | |||||
// (unsigned) hexadecimal | |||||
case 'x': | |||||
num = getuint(&ap, lflag); | |||||
base = 16; | |||||
number: | |||||
printnum(putch, putdat, num, base, width, padc); | |||||
break; | |||||
// escaped '%' character | |||||
case '%': | |||||
putch(ch, putdat); | |||||
break; | |||||
// unrecognized escape sequence - just print it literally | |||||
default: | |||||
putch('%', putdat); | |||||
for (fmt --; fmt[-1] != '%'; fmt --) | |||||
/* do nothing */; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/* sprintbuf is used to save enough information of a buffer */ | |||||
struct sprintbuf { | |||||
char *buf; // address pointer points to the first unused memory | |||||
char *ebuf; // points the end of the buffer | |||||
int cnt; // the number of characters that have been placed in this buffer | |||||
}; | |||||
/* * | |||||
* sprintputch - 'print' a single character in a buffer | |||||
* @ch: the character will be printed | |||||
* @b: the buffer to place the character @ch | |||||
* */ | |||||
static void | |||||
sprintputch(int ch, struct sprintbuf *b) { | |||||
b->cnt ++; | |||||
if (b->buf < b->ebuf) { | |||||
*b->buf ++ = ch; | |||||
} | |||||
} | |||||
/* * | |||||
* snprintf - format a string and place it in a buffer | |||||
* @str: the buffer to place the result into | |||||
* @size: the size of buffer, including the trailing null space | |||||
* @fmt: the format string to use | |||||
* */ | |||||
int | |||||
snprintf(char *str, size_t size, const char *fmt, ...) { | |||||
va_list ap; | |||||
int cnt; | |||||
va_start(ap, fmt); | |||||
cnt = vsnprintf(str, size, fmt, ap); | |||||
va_end(ap); | |||||
return cnt; | |||||
} | |||||
/* * | |||||
* vsnprintf - format a string and place it in a buffer, it's called with a va_list | |||||
* instead of a variable number of arguments | |||||
* @str: the buffer to place the result into | |||||
* @size: the size of buffer, including the trailing null space | |||||
* @fmt: the format string to use | |||||
* @ap: arguments for the format string | |||||
* | |||||
* The return value is the number of characters which would be generated for the | |||||
* given input, excluding the trailing '\0'. | |||||
* | |||||
* Call this function if you are already dealing with a va_list. | |||||
* Or you probably want snprintf() instead. | |||||
* */ | |||||
int | |||||
vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { | |||||
struct sprintbuf b = {str, str + size - 1, 0}; | |||||
if (str == NULL || b.buf > b.ebuf) { | |||||
return -E_INVAL; | |||||
} | |||||
// print the string to the buffer | |||||
vprintfmt((void*)sprintputch, &b, fmt, ap); | |||||
// null terminate the buffer | |||||
*b.buf = '\0'; | |||||
return b.cnt; | |||||
} | |||||
@ -0,0 +1,12 @@ | |||||
#ifndef __LIBS_STDARG_H__ | |||||
#define __LIBS_STDARG_H__ | |||||
/* compiler provides size of save area */ | |||||
typedef __builtin_va_list va_list; | |||||
#define va_start(ap, last) (__builtin_va_start(ap, last)) | |||||
#define va_arg(ap, type) (__builtin_va_arg(ap, type)) | |||||
#define va_end(ap) /*nothing*/ | |||||
#endif /* !__LIBS_STDARG_H__ */ | |||||
@ -0,0 +1,24 @@ | |||||
#ifndef __LIBS_STDIO_H__ | |||||
#define __LIBS_STDIO_H__ | |||||
#include <defs.h> | |||||
#include <stdarg.h> | |||||
/* kern/libs/stdio.c */ | |||||
int cprintf(const char *fmt, ...); | |||||
int vcprintf(const char *fmt, va_list ap); | |||||
void cputchar(int c); | |||||
int cputs(const char *str); | |||||
int getchar(void); | |||||
/* kern/libs/readline.c */ | |||||
char *readline(const char *prompt); | |||||
/* libs/printfmt.c */ | |||||
void printfmt(void (*putch)(int, void *), void *putdat, const char *fmt, ...); | |||||
void vprintfmt(void (*putch)(int, void *), void *putdat, const char *fmt, va_list ap); | |||||
int snprintf(char *str, size_t size, const char *fmt, ...); | |||||
int vsnprintf(char *str, size_t size, const char *fmt, va_list ap); | |||||
#endif /* !__LIBS_STDIO_H__ */ | |||||
@ -0,0 +1,367 @@ | |||||
#include <string.h> | |||||
#include <x86.h> | |||||
/* * | |||||
* strlen - calculate the length of the string @s, not including | |||||
* the terminating '\0' character. | |||||
* @s: the input string | |||||
* | |||||
* The strlen() function returns the length of string @s. | |||||
* */ | |||||
size_t | |||||
strlen(const char *s) { | |||||
size_t cnt = 0; | |||||
while (*s ++ != '\0') { | |||||
cnt ++; | |||||
} | |||||
return cnt; | |||||
} | |||||
/* * | |||||
* strnlen - calculate the length of the string @s, not including | |||||
* the terminating '\0' char acter, but at most @len. | |||||
* @s: the input string | |||||
* @len: the max-length that function will scan | |||||
* | |||||
* Note that, this function looks only at the first @len characters | |||||
* at @s, and never beyond @s + @len. | |||||
* | |||||
* The return value is strlen(s), if that is less than @len, or | |||||
* @len if there is no '\0' character among the first @len characters | |||||
* pointed by @s. | |||||
* */ | |||||
size_t | |||||
strnlen(const char *s, size_t len) { | |||||
size_t cnt = 0; | |||||
while (cnt < len && *s ++ != '\0') { | |||||
cnt ++; | |||||
} | |||||
return cnt; | |||||
} | |||||
/* * | |||||
* strcpy - copies the string pointed by @src into the array pointed by @dst, | |||||
* including the terminating null character. | |||||
* @dst: pointer to the destination array where the content is to be copied | |||||
* @src: string to be copied | |||||
* | |||||
* The return value is @dst. | |||||
* | |||||
* To avoid overflows, the size of array pointed by @dst should be long enough to | |||||
* contain the same string as @src (including the terminating null character), and | |||||
* should not overlap in memory with @src. | |||||
* */ | |||||
char * | |||||
strcpy(char *dst, const char *src) { | |||||
#ifdef __HAVE_ARCH_STRCPY | |||||
return __strcpy(dst, src); | |||||
#else | |||||
char *p = dst; | |||||
while ((*p ++ = *src ++) != '\0') | |||||
/* nothing */; | |||||
return dst; | |||||
#endif /* __HAVE_ARCH_STRCPY */ | |||||
} | |||||
/* * | |||||
* strncpy - copies the first @len characters of @src to @dst. If the end of string @src | |||||
* if found before @len characters have been copied, @dst is padded with '\0' until a | |||||
* total of @len characters have been written to it. | |||||
* @dst: pointer to the destination array where the content is to be copied | |||||
* @src: string to be copied | |||||
* @len: maximum number of characters to be copied from @src | |||||
* | |||||
* The return value is @dst | |||||
* */ | |||||
char * | |||||
strncpy(char *dst, const char *src, size_t len) { | |||||
char *p = dst; | |||||
while (len > 0) { | |||||
if ((*p = *src) != '\0') { | |||||
src ++; | |||||
} | |||||
p ++, len --; | |||||
} | |||||
return dst; | |||||
} | |||||
/* * | |||||
* strcmp - compares the string @s1 and @s2 | |||||
* @s1: string to be compared | |||||
* @s2: string to be compared | |||||
* | |||||
* This function starts comparing the first character of each string. If | |||||
* they are equal to each other, it continues with the following pairs until | |||||
* the characters differ or until a terminanting null-character is reached. | |||||
* | |||||
* Returns an integral value indicating the relationship between the strings: | |||||
* - A zero value indicates that both strings are equal; | |||||
* - A value greater than zero indicates that the first character that does | |||||
* not match has a greater value in @s1 than in @s2; | |||||
* - And a value less than zero indicates the opposite. | |||||
* */ | |||||
int | |||||
strcmp(const char *s1, const char *s2) { | |||||
#ifdef __HAVE_ARCH_STRCMP | |||||
return __strcmp(s1, s2); | |||||
#else | |||||
while (*s1 != '\0' && *s1 == *s2) { | |||||
s1 ++, s2 ++; | |||||
} | |||||
return (int)((unsigned char)*s1 - (unsigned char)*s2); | |||||
#endif /* __HAVE_ARCH_STRCMP */ | |||||
} | |||||
/* * | |||||
* strncmp - compares up to @n characters of the string @s1 to those of the string @s2 | |||||
* @s1: string to be compared | |||||
* @s2: string to be compared | |||||
* @n: maximum number of characters to compare | |||||
* | |||||
* This function starts comparing the first character of each string. If | |||||
* they are equal to each other, it continues with the following pairs until | |||||
* the characters differ, until a terminating null-character is reached, or | |||||
* until @n characters match in both strings, whichever happens first. | |||||
* */ | |||||
int | |||||
strncmp(const char *s1, const char *s2, size_t n) { | |||||
while (n > 0 && *s1 != '\0' && *s1 == *s2) { | |||||
n --, s1 ++, s2 ++; | |||||
} | |||||
return (n == 0) ? 0 : (int)((unsigned char)*s1 - (unsigned char)*s2); | |||||
} | |||||
/* * | |||||
* strchr - locates first occurrence of character in string | |||||
* @s: the input string | |||||
* @c: character to be located | |||||
* | |||||
* The strchr() function returns a pointer to the first occurrence of | |||||
* character in @s. If the value is not found, the function returns 'NULL'. | |||||
* */ | |||||
char * | |||||
strchr(const char *s, char c) { | |||||
while (*s != '\0') { | |||||
if (*s == c) { | |||||
return (char *)s; | |||||
} | |||||
s ++; | |||||
} | |||||
return NULL; | |||||
} | |||||
/* * | |||||
* strfind - locates first occurrence of character in string | |||||
* @s: the input string | |||||
* @c: character to be located | |||||
* | |||||
* The strfind() function is like strchr() except that if @c is | |||||
* not found in @s, then it returns a pointer to the null byte at the | |||||
* end of @s, rather than 'NULL'. | |||||
* */ | |||||
char * | |||||
strfind(const char *s, char c) { | |||||
while (*s != '\0') { | |||||
if (*s == c) { | |||||
break; | |||||
} | |||||
s ++; | |||||
} | |||||
return (char *)s; | |||||
} | |||||
/* * | |||||
* strtol - converts string to long integer | |||||
* @s: the input string that contains the representation of an integer number | |||||
* @endptr: reference to an object of type char *, whose value is set by the | |||||
* function to the next character in @s after the numerical value. This | |||||
* parameter can also be a null pointer, in which case it is not used. | |||||
* @base: x | |||||
* | |||||
* The function first discards as many whitespace characters as necessary until | |||||
* the first non-whitespace character is found. Then, starting from this character, | |||||
* takes as many characters as possible that are valid following a syntax that | |||||
* depends on the base parameter, and interprets them as a numerical value. Finally, | |||||
* a pointer to the first character following the integer representation in @s | |||||
* is stored in the object pointed by @endptr. | |||||
* | |||||
* If the value of base is zero, the syntax expected is similar to that of | |||||
* integer constants, which is formed by a succession of: | |||||
* - An optional plus or minus sign; | |||||
* - An optional prefix indicating octal or hexadecimal base ("0" or "0x" respectively) | |||||
* - A sequence of decimal digits (if no base prefix was specified) or either octal | |||||
* or hexadecimal digits if a specific prefix is present | |||||
* | |||||
* If the base value is between 2 and 36, the format expected for the integral number | |||||
* is a succession of the valid digits and/or letters needed to represent integers of | |||||
* the specified radix (starting from '0' and up to 'z'/'Z' for radix 36). The | |||||
* sequence may optionally be preceded by a plus or minus sign and, if base is 16, | |||||
* an optional "0x" or "0X" prefix. | |||||
* | |||||
* The strtol() function returns the converted integral number as a long int value. | |||||
* */ | |||||
long | |||||
strtol(const char *s, char **endptr, int base) { | |||||
int neg = 0; | |||||
long val = 0; | |||||
// gobble initial whitespace | |||||
while (*s == ' ' || *s == '\t') { | |||||
s ++; | |||||
} | |||||
// plus/minus sign | |||||
if (*s == '+') { | |||||
s ++; | |||||
} | |||||
else if (*s == '-') { | |||||
s ++, neg = 1; | |||||
} | |||||
// hex or octal base prefix | |||||
if ((base == 0 || base == 16) && (s[0] == '0' && s[1] == 'x')) { | |||||
s += 2, base = 16; | |||||
} | |||||
else if (base == 0 && s[0] == '0') { | |||||
s ++, base = 8; | |||||
} | |||||
else if (base == 0) { | |||||
base = 10; | |||||
} | |||||
// digits | |||||
while (1) { | |||||
int dig; | |||||
if (*s >= '0' && *s <= '9') { | |||||
dig = *s - '0'; | |||||
} | |||||
else if (*s >= 'a' && *s <= 'z') { | |||||
dig = *s - 'a' + 10; | |||||
} | |||||
else if (*s >= 'A' && *s <= 'Z') { | |||||
dig = *s - 'A' + 10; | |||||
} | |||||
else { | |||||
break; | |||||
} | |||||
if (dig >= base) { | |||||
break; | |||||
} | |||||
s ++, val = (val * base) + dig; | |||||
// we don't properly detect overflow! | |||||
} | |||||
if (endptr) { | |||||
*endptr = (char *) s; | |||||
} | |||||
return (neg ? -val : val); | |||||
} | |||||
/* * | |||||
* memset - sets the first @n bytes of the memory area pointed by @s | |||||
* to the specified value @c. | |||||
* @s: pointer the the memory area to fill | |||||
* @c: value to set | |||||
* @n: number of bytes to be set to the value | |||||
* | |||||
* The memset() function returns @s. | |||||
* */ | |||||
void * | |||||
memset(void *s, char c, size_t n) { | |||||
#ifdef __HAVE_ARCH_MEMSET | |||||
return __memset(s, c, n); | |||||
#else | |||||
char *p = s; | |||||
while (n -- > 0) { | |||||
*p ++ = c; | |||||
} | |||||
return s; | |||||
#endif /* __HAVE_ARCH_MEMSET */ | |||||
} | |||||
/* * | |||||
* memmove - copies the values of @n bytes from the location pointed by @src to | |||||
* the memory area pointed by @dst. @src and @dst are allowed to overlap. | |||||
* @dst pointer to the destination array where the content is to be copied | |||||
* @src pointer to the source of data to by copied | |||||
* @n: number of bytes to copy | |||||
* | |||||
* The memmove() function returns @dst. | |||||
* */ | |||||
void * | |||||
memmove(void *dst, const void *src, size_t n) { | |||||
#ifdef __HAVE_ARCH_MEMMOVE | |||||
return __memmove(dst, src, n); | |||||
#else | |||||
const char *s = src; | |||||
char *d = dst; | |||||
if (s < d && s + n > d) { | |||||
s += n, d += n; | |||||
while (n -- > 0) { | |||||
*-- d = *-- s; | |||||
} | |||||
} else { | |||||
while (n -- > 0) { | |||||
*d ++ = *s ++; | |||||
} | |||||
} | |||||
return dst; | |||||
#endif /* __HAVE_ARCH_MEMMOVE */ | |||||
} | |||||
/* * | |||||
* memcpy - copies the value of @n bytes from the location pointed by @src to | |||||
* the memory area pointed by @dst. | |||||
* @dst pointer to the destination array where the content is to be copied | |||||
* @src pointer to the source of data to by copied | |||||
* @n: number of bytes to copy | |||||
* | |||||
* The memcpy() returns @dst. | |||||
* | |||||
* Note that, the function does not check any terminating null character in @src, | |||||
* it always copies exactly @n bytes. To avoid overflows, the size of arrays pointed | |||||
* by both @src and @dst, should be at least @n bytes, and should not overlap | |||||
* (for overlapping memory area, memmove is a safer approach). | |||||
* */ | |||||
void * | |||||
memcpy(void *dst, const void *src, size_t n) { | |||||
#ifdef __HAVE_ARCH_MEMCPY | |||||
return __memcpy(dst, src, n); | |||||
#else | |||||
const char *s = src; | |||||
char *d = dst; | |||||
while (n -- > 0) { | |||||
*d ++ = *s ++; | |||||
} | |||||
return dst; | |||||
#endif /* __HAVE_ARCH_MEMCPY */ | |||||
} | |||||
/* * | |||||
* memcmp - compares two blocks of memory | |||||
* @v1: pointer to block of memory | |||||
* @v2: pointer to block of memory | |||||
* @n: number of bytes to compare | |||||
* | |||||
* The memcmp() functions returns an integral value indicating the | |||||
* relationship between the content of the memory blocks: | |||||
* - A zero value indicates that the contents of both memory blocks are equal; | |||||
* - A value greater than zero indicates that the first byte that does not | |||||
* match in both memory blocks has a greater value in @v1 than in @v2 | |||||
* as if evaluated as unsigned char values; | |||||
* - And a value less than zero indicates the opposite. | |||||
* */ | |||||
int | |||||
memcmp(const void *v1, const void *v2, size_t n) { | |||||
const char *s1 = (const char *)v1; | |||||
const char *s2 = (const char *)v2; | |||||
while (n -- > 0) { | |||||
if (*s1 != *s2) { | |||||
return (int)((unsigned char)*s1 - (unsigned char)*s2); | |||||
} | |||||
s1 ++, s2 ++; | |||||
} | |||||
return 0; | |||||
} | |||||
@ -0,0 +1,25 @@ | |||||
#ifndef __LIBS_STRING_H__ | |||||
#define __LIBS_STRING_H__ | |||||
#include <defs.h> | |||||
size_t strlen(const char *s); | |||||
size_t strnlen(const char *s, size_t len); | |||||
char *strcpy(char *dst, const char *src); | |||||
char *strncpy(char *dst, const char *src, size_t len); | |||||
int strcmp(const char *s1, const char *s2); | |||||
int strncmp(const char *s1, const char *s2, size_t n); | |||||
char *strchr(const char *s, char c); | |||||
char *strfind(const char *s, char c); | |||||
long strtol(const char *s, char **endptr, int base); | |||||
void *memset(void *s, char c, size_t n); | |||||
void *memmove(void *dst, const void *src, size_t n); | |||||
void *memcpy(void *dst, const void *src, size_t n); | |||||
int memcmp(const void *v1, const void *v2, size_t n); | |||||
#endif /* !__LIBS_STRING_H__ */ | |||||
@ -0,0 +1,302 @@ | |||||
#ifndef __LIBS_X86_H__ | |||||
#define __LIBS_X86_H__ | |||||
#include <defs.h> | |||||
#define do_div(n, base) ({ \ | |||||
unsigned long __upper, __low, __high, __mod, __base; \ | |||||
__base = (base); \ | |||||
asm ("" : "=a" (__low), "=d" (__high) : "A" (n)); \ | |||||
__upper = __high; \ | |||||
if (__high != 0) { \ | |||||
__upper = __high % __base; \ | |||||
__high = __high / __base; \ | |||||
} \ | |||||
asm ("divl %2" : "=a" (__low), "=d" (__mod) \ | |||||
: "rm" (__base), "0" (__low), "1" (__upper)); \ | |||||
asm ("" : "=A" (n) : "a" (__low), "d" (__high)); \ | |||||
__mod; \ | |||||
}) | |||||
#define barrier() __asm__ __volatile__ ("" ::: "memory") | |||||
static inline uint8_t inb(uint16_t port) __attribute__((always_inline)); | |||||
static inline void insl(uint32_t port, void *addr, int cnt) __attribute__((always_inline)); | |||||
static inline void outb(uint16_t port, uint8_t data) __attribute__((always_inline)); | |||||
static inline void outw(uint16_t port, uint16_t data) __attribute__((always_inline)); | |||||
static inline void outsl(uint32_t port, const void *addr, int cnt) __attribute__((always_inline)); | |||||
static inline uint32_t read_ebp(void) __attribute__((always_inline)); | |||||
static inline void breakpoint(void) __attribute__((always_inline)); | |||||
static inline uint32_t read_dr(unsigned regnum) __attribute__((always_inline)); | |||||
static inline void write_dr(unsigned regnum, uint32_t value) __attribute__((always_inline)); | |||||
/* Pseudo-descriptors used for LGDT, LLDT(not used) and LIDT instructions. */ | |||||
struct pseudodesc { | |||||
uint16_t pd_lim; // Limit | |||||
uintptr_t pd_base; // Base address | |||||
} __attribute__ ((packed)); | |||||
static inline void lidt(struct pseudodesc *pd) __attribute__((always_inline)); | |||||
static inline void sti(void) __attribute__((always_inline)); | |||||
static inline void cli(void) __attribute__((always_inline)); | |||||
static inline void ltr(uint16_t sel) __attribute__((always_inline)); | |||||
static inline uint32_t read_eflags(void) __attribute__((always_inline)); | |||||
static inline void write_eflags(uint32_t eflags) __attribute__((always_inline)); | |||||
static inline void lcr0(uintptr_t cr0) __attribute__((always_inline)); | |||||
static inline void lcr3(uintptr_t cr3) __attribute__((always_inline)); | |||||
static inline uintptr_t rcr0(void) __attribute__((always_inline)); | |||||
static inline uintptr_t rcr1(void) __attribute__((always_inline)); | |||||
static inline uintptr_t rcr2(void) __attribute__((always_inline)); | |||||
static inline uintptr_t rcr3(void) __attribute__((always_inline)); | |||||
static inline void invlpg(void *addr) __attribute__((always_inline)); | |||||
static inline uint8_t | |||||
inb(uint16_t port) { | |||||
uint8_t data; | |||||
asm volatile ("inb %1, %0" : "=a" (data) : "d" (port) : "memory"); | |||||
return data; | |||||
} | |||||
static inline void | |||||
insl(uint32_t port, void *addr, int cnt) { | |||||
asm volatile ( | |||||
"cld;" | |||||
"repne; insl;" | |||||
: "=D" (addr), "=c" (cnt) | |||||
: "d" (port), "0" (addr), "1" (cnt) | |||||
: "memory", "cc"); | |||||
} | |||||
static inline void | |||||
outb(uint16_t port, uint8_t data) { | |||||
asm volatile ("outb %0, %1" :: "a" (data), "d" (port) : "memory"); | |||||
} | |||||
static inline void | |||||
outw(uint16_t port, uint16_t data) { | |||||
asm volatile ("outw %0, %1" :: "a" (data), "d" (port) : "memory"); | |||||
} | |||||
static inline void | |||||
outsl(uint32_t port, const void *addr, int cnt) { | |||||
asm volatile ( | |||||
"cld;" | |||||
"repne; outsl;" | |||||
: "=S" (addr), "=c" (cnt) | |||||
: "d" (port), "0" (addr), "1" (cnt) | |||||
: "memory", "cc"); | |||||
} | |||||
static inline uint32_t | |||||
read_ebp(void) { | |||||
uint32_t ebp; | |||||
asm volatile ("movl %%ebp, %0" : "=r" (ebp)); | |||||
return ebp; | |||||
} | |||||
static inline void | |||||
breakpoint(void) { | |||||
asm volatile ("int $3"); | |||||
} | |||||
static inline uint32_t | |||||
read_dr(unsigned regnum) { | |||||
uint32_t value = 0; | |||||
switch (regnum) { | |||||
case 0: asm volatile ("movl %%db0, %0" : "=r" (value)); break; | |||||
case 1: asm volatile ("movl %%db1, %0" : "=r" (value)); break; | |||||
case 2: asm volatile ("movl %%db2, %0" : "=r" (value)); break; | |||||
case 3: asm volatile ("movl %%db3, %0" : "=r" (value)); break; | |||||
case 6: asm volatile ("movl %%db6, %0" : "=r" (value)); break; | |||||
case 7: asm volatile ("movl %%db7, %0" : "=r" (value)); break; | |||||
} | |||||
return value; | |||||
} | |||||
static void | |||||
write_dr(unsigned regnum, uint32_t value) { | |||||
switch (regnum) { | |||||
case 0: asm volatile ("movl %0, %%db0" :: "r" (value)); break; | |||||
case 1: asm volatile ("movl %0, %%db1" :: "r" (value)); break; | |||||
case 2: asm volatile ("movl %0, %%db2" :: "r" (value)); break; | |||||
case 3: asm volatile ("movl %0, %%db3" :: "r" (value)); break; | |||||
case 6: asm volatile ("movl %0, %%db6" :: "r" (value)); break; | |||||
case 7: asm volatile ("movl %0, %%db7" :: "r" (value)); break; | |||||
} | |||||
} | |||||
static inline void | |||||
lidt(struct pseudodesc *pd) { | |||||
asm volatile ("lidt (%0)" :: "r" (pd) : "memory"); | |||||
} | |||||
static inline void | |||||
sti(void) { | |||||
asm volatile ("sti"); | |||||
} | |||||
static inline void | |||||
cli(void) { | |||||
asm volatile ("cli" ::: "memory"); | |||||
} | |||||
static inline void | |||||
ltr(uint16_t sel) { | |||||
asm volatile ("ltr %0" :: "r" (sel) : "memory"); | |||||
} | |||||
static inline uint32_t | |||||
read_eflags(void) { | |||||
uint32_t eflags; | |||||
asm volatile ("pushfl; popl %0" : "=r" (eflags)); | |||||
return eflags; | |||||
} | |||||
static inline void | |||||
write_eflags(uint32_t eflags) { | |||||
asm volatile ("pushl %0; popfl" :: "r" (eflags)); | |||||
} | |||||
static inline void | |||||
lcr0(uintptr_t cr0) { | |||||
asm volatile ("mov %0, %%cr0" :: "r" (cr0) : "memory"); | |||||
} | |||||
static inline void | |||||
lcr3(uintptr_t cr3) { | |||||
asm volatile ("mov %0, %%cr3" :: "r" (cr3) : "memory"); | |||||
} | |||||
static inline uintptr_t | |||||
rcr0(void) { | |||||
uintptr_t cr0; | |||||
asm volatile ("mov %%cr0, %0" : "=r" (cr0) :: "memory"); | |||||
return cr0; | |||||
} | |||||
static inline uintptr_t | |||||
rcr1(void) { | |||||
uintptr_t cr1; | |||||
asm volatile ("mov %%cr1, %0" : "=r" (cr1) :: "memory"); | |||||
return cr1; | |||||
} | |||||
static inline uintptr_t | |||||
rcr2(void) { | |||||
uintptr_t cr2; | |||||
asm volatile ("mov %%cr2, %0" : "=r" (cr2) :: "memory"); | |||||
return cr2; | |||||
} | |||||
static inline uintptr_t | |||||
rcr3(void) { | |||||
uintptr_t cr3; | |||||
asm volatile ("mov %%cr3, %0" : "=r" (cr3) :: "memory"); | |||||
return cr3; | |||||
} | |||||
static inline void | |||||
invlpg(void *addr) { | |||||
asm volatile ("invlpg (%0)" :: "r" (addr) : "memory"); | |||||
} | |||||
static inline int __strcmp(const char *s1, const char *s2) __attribute__((always_inline)); | |||||
static inline char *__strcpy(char *dst, const char *src) __attribute__((always_inline)); | |||||
static inline void *__memset(void *s, char c, size_t n) __attribute__((always_inline)); | |||||
static inline void *__memmove(void *dst, const void *src, size_t n) __attribute__((always_inline)); | |||||
static inline void *__memcpy(void *dst, const void *src, size_t n) __attribute__((always_inline)); | |||||
#ifndef __HAVE_ARCH_STRCMP | |||||
#define __HAVE_ARCH_STRCMP | |||||
static inline int | |||||
__strcmp(const char *s1, const char *s2) { | |||||
int d0, d1, ret; | |||||
asm volatile ( | |||||
"1: lodsb;" | |||||
"scasb;" | |||||
"jne 2f;" | |||||
"testb %%al, %%al;" | |||||
"jne 1b;" | |||||
"xorl %%eax, %%eax;" | |||||
"jmp 3f;" | |||||
"2: sbbl %%eax, %%eax;" | |||||
"orb $1, %%al;" | |||||
"3:" | |||||
: "=a" (ret), "=&S" (d0), "=&D" (d1) | |||||
: "1" (s1), "2" (s2) | |||||
: "memory"); | |||||
return ret; | |||||
} | |||||
#endif /* __HAVE_ARCH_STRCMP */ | |||||
#ifndef __HAVE_ARCH_STRCPY | |||||
#define __HAVE_ARCH_STRCPY | |||||
static inline char * | |||||
__strcpy(char *dst, const char *src) { | |||||
int d0, d1, d2; | |||||
asm volatile ( | |||||
"1: lodsb;" | |||||
"stosb;" | |||||
"testb %%al, %%al;" | |||||
"jne 1b;" | |||||
: "=&S" (d0), "=&D" (d1), "=&a" (d2) | |||||
: "0" (src), "1" (dst) : "memory"); | |||||
return dst; | |||||
} | |||||
#endif /* __HAVE_ARCH_STRCPY */ | |||||
#ifndef __HAVE_ARCH_MEMSET | |||||
#define __HAVE_ARCH_MEMSET | |||||
static inline void * | |||||
__memset(void *s, char c, size_t n) { | |||||
int d0, d1; | |||||
asm volatile ( | |||||
"rep; stosb;" | |||||
: "=&c" (d0), "=&D" (d1) | |||||
: "0" (n), "a" (c), "1" (s) | |||||
: "memory"); | |||||
return s; | |||||
} | |||||
#endif /* __HAVE_ARCH_MEMSET */ | |||||
#ifndef __HAVE_ARCH_MEMMOVE | |||||
#define __HAVE_ARCH_MEMMOVE | |||||
static inline void * | |||||
__memmove(void *dst, const void *src, size_t n) { | |||||
if (dst < src) { | |||||
return __memcpy(dst, src, n); | |||||
} | |||||
int d0, d1, d2; | |||||
asm volatile ( | |||||
"std;" | |||||
"rep; movsb;" | |||||
"cld;" | |||||
: "=&c" (d0), "=&S" (d1), "=&D" (d2) | |||||
: "0" (n), "1" (n - 1 + src), "2" (n - 1 + dst) | |||||
: "memory"); | |||||
return dst; | |||||
} | |||||
#endif /* __HAVE_ARCH_MEMMOVE */ | |||||
#ifndef __HAVE_ARCH_MEMCPY | |||||
#define __HAVE_ARCH_MEMCPY | |||||
static inline void * | |||||
__memcpy(void *dst, const void *src, size_t n) { | |||||
int d0, d1, d2; | |||||
asm volatile ( | |||||
"rep; movsl;" | |||||
"movl %4, %%ecx;" | |||||
"andl $3, %%ecx;" | |||||
"jz 1f;" | |||||
"rep; movsb;" | |||||
"1:" | |||||
: "=&c" (d0), "=&D" (d1), "=&S" (d2) | |||||
: "0" (n / 4), "g" (n), "1" (dst), "2" (src) | |||||
: "memory"); | |||||
return dst; | |||||
} | |||||
#endif /* __HAVE_ARCH_MEMCPY */ | |||||
#endif /* !__LIBS_X86_H__ */ | |||||
@ -0,0 +1,15 @@ | |||||
OUTPUT_FORMAT("elf32-i386") | |||||
OUTPUT_ARCH(i386) | |||||
SECTIONS { | |||||
. = 0x7C00; | |||||
.startup : { | |||||
*bootasm.o(.text) | |||||
} | |||||
.text : { *(.text) } | |||||
.data : { *(.data .rodata) } | |||||
/DISCARD/ : { *(.eh_*) } | |||||
} |
@ -0,0 +1,95 @@ | |||||
OBJPREFIX := __objs_ | |||||
.SECONDEXPANSION: | |||||
# -------------------- function begin -------------------- | |||||
# list all files in some directories: (#directories, #types) | |||||
listf = $(filter $(if $(2),$(addprefix %.,$(2)),%),\ | |||||
$(wildcard $(addsuffix $(SLASH)*,$(1)))) | |||||
# get .o obj files: (#files[, packet]) | |||||
toobj = $(addprefix $(OBJDIR)$(SLASH)$(if $(2),$(2)$(SLASH)),\ | |||||
$(addsuffix .o,$(basename $(1)))) | |||||
# get .d dependency files: (#files[, packet]) | |||||
todep = $(patsubst %.o,%.d,$(call toobj,$(1),$(2))) | |||||
totarget = $(addprefix $(BINDIR)$(SLASH),$(1)) | |||||
# change $(name) to $(OBJPREFIX)$(name): (#names) | |||||
packetname = $(if $(1),$(addprefix $(OBJPREFIX),$(1)),$(OBJPREFIX)) | |||||
# cc compile template, generate rule for dep, obj: (file, cc[, flags, dir]) | |||||
define cc_template | |||||
$$(call todep,$(1),$(4)): $(1) | $$$$(dir $$$$@) | |||||
@$(2) -I$$(dir $(1)) $(3) -MM $$< -MT "$$(patsubst %.d,%.o,$$@) $$@"> $$@ | |||||
$$(call toobj,$(1),$(4)): $(1) | $$$$(dir $$$$@) | |||||
@echo + cc $$< | |||||
$(V)$(2) -I$$(dir $(1)) $(3) -c $$< -o $$@ | |||||
ALLOBJS += $$(call toobj,$(1),$(4)) | |||||
endef | |||||
# compile file: (#files, cc[, flags, dir]) | |||||
define do_cc_compile | |||||
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(4)))) | |||||
endef | |||||
# add files to packet: (#files, cc[, flags, packet, dir]) | |||||
define do_add_files_to_packet | |||||
__temp_packet__ := $(call packetname,$(4)) | |||||
ifeq ($$(origin $$(__temp_packet__)),undefined) | |||||
$$(__temp_packet__) := | |||||
endif | |||||
__temp_objs__ := $(call toobj,$(1),$(5)) | |||||
$$(foreach f,$(1),$$(eval $$(call cc_template,$$(f),$(2),$(3),$(5)))) | |||||
$$(__temp_packet__) += $$(__temp_objs__) | |||||
endef | |||||
# add objs to packet: (#objs, packet) | |||||
define do_add_objs_to_packet | |||||
__temp_packet__ := $(call packetname,$(2)) | |||||
ifeq ($$(origin $$(__temp_packet__)),undefined) | |||||
$$(__temp_packet__) := | |||||
endif | |||||
$$(__temp_packet__) += $(1) | |||||
endef | |||||
# add packets and objs to target (target, #packes, #objs[, cc, flags]) | |||||
define do_create_target | |||||
__temp_target__ = $(call totarget,$(1)) | |||||
__temp_objs__ = $$(foreach p,$(call packetname,$(2)),$$($$(p))) $(3) | |||||
TARGETS += $$(__temp_target__) | |||||
ifneq ($(4),) | |||||
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) | |||||
$(V)$(4) $(5) $$^ -o $$@ | |||||
else | |||||
$$(__temp_target__): $$(__temp_objs__) | $$$$(dir $$$$@) | |||||
endif | |||||
endef | |||||
# finish all | |||||
define do_finish_all | |||||
ALLDEPS = $$(ALLOBJS:.o=.d) | |||||
$$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) $(OBJDIR)$(SLASH)): | |||||
@$(MKDIR) $$@ | |||||
endef | |||||
# -------------------- function end -------------------- | |||||
# compile file: (#files, cc[, flags, dir]) | |||||
cc_compile = $(eval $(call do_cc_compile,$(1),$(2),$(3),$(4))) | |||||
# add files to packet: (#files, cc[, flags, packet, dir]) | |||||
add_files = $(eval $(call do_add_files_to_packet,$(1),$(2),$(3),$(4),$(5))) | |||||
# add objs to packet: (#objs, packet) | |||||
add_objs = $(eval $(call do_add_objs_to_packet,$(1),$(2))) | |||||
# add packets and objs to target (target, #packes, #objs, cc, [, flags]) | |||||
create_target = $(eval $(call do_create_target,$(1),$(2),$(3),$(4),$(5))) | |||||
read_packet = $(foreach p,$(call packetname,$(1)),$($(p))) | |||||
add_dependency = $(eval $(1): $(2)) | |||||
finish_all = $(eval $(call do_finish_all)) | |||||
@ -0,0 +1,3 @@ | |||||
file bin/kernel | |||||
target remote :1234 | |||||
break kern_init |
@ -0,0 +1,340 @@ | |||||
#!/bin/sh | |||||
verbose=false | |||||
if [ "x$1" = "x-v" ]; then | |||||
verbose=true | |||||
out=/dev/stdout | |||||
err=/dev/stderr | |||||
else | |||||
out=/dev/null | |||||
err=/dev/null | |||||
fi | |||||
## make & makeopts | |||||
if gmake --version > /dev/null 2>&1; then | |||||
make=gmake; | |||||
else | |||||
make=make; | |||||
fi | |||||
makeopts="--quiet --no-print-directory -j" | |||||
make_print() { | |||||
echo `$make $makeopts print-$1` | |||||
} | |||||
## command tools | |||||
awk='awk' | |||||
bc='bc' | |||||
date='date' | |||||
grep='grep' | |||||
rm='rm -f' | |||||
sed='sed' | |||||
## symbol table | |||||
sym_table='obj/kernel.sym' | |||||
## gdb & gdbopts | |||||
gdb="$(make_print GDB)" | |||||
gdbport='1234' | |||||
gdb_in="$(make_print GRADE_GDB_IN)" | |||||
## qemu & qemuopts | |||||
qemu="$(make_print qemu)" | |||||
qemu_out="$(make_print GRADE_QEMU_OUT)" | |||||
if $qemu -nographic -help | grep -q '^-gdb'; then | |||||
qemugdb="-gdb tcp::$gdbport" | |||||
else | |||||
qemugdb="-s -p $gdbport" | |||||
fi | |||||
## default variables | |||||
default_timeout=30 | |||||
default_pts=5 | |||||
pts=5 | |||||
part=0 | |||||
part_pos=0 | |||||
total=0 | |||||
total_pos=0 | |||||
## default functions | |||||
update_score() { | |||||
total=`expr $total + $part` | |||||
total_pos=`expr $total_pos + $part_pos` | |||||
part=0 | |||||
part_pos=0 | |||||
} | |||||
get_time() { | |||||
echo `$date +%s.%N 2> /dev/null` | |||||
} | |||||
show_part() { | |||||
echo "Part $1 Score: $part/$part_pos" | |||||
echo | |||||
update_score | |||||
} | |||||
show_final() { | |||||
update_score | |||||
echo "Total Score: $total/$total_pos" | |||||
if [ $total -lt $total_pos ]; then | |||||
exit 1 | |||||
fi | |||||
} | |||||
show_time() { | |||||
t1=$(get_time) | |||||
time=`echo "scale=1; ($t1-$t0)/1" | $sed 's/.N/.0/g' | $bc 2> /dev/null` | |||||
echo "(${time}s)" | |||||
} | |||||
show_build_tag() { | |||||
echo "$1:" | $awk '{printf "%-24s ", $0}' | |||||
} | |||||
show_check_tag() { | |||||
echo "$1:" | $awk '{printf " -%-40s ", $0}' | |||||
} | |||||
show_msg() { | |||||
echo $1 | |||||
shift | |||||
if [ $# -gt 0 ]; then | |||||
echo "$@" | awk '{printf " %s\n", $0}' | |||||
echo | |||||
fi | |||||
} | |||||
pass() { | |||||
show_msg OK "$@" | |||||
part=`expr $part + $pts` | |||||
part_pos=`expr $part_pos + $pts` | |||||
} | |||||
fail() { | |||||
show_msg WRONG "$@" | |||||
part_pos=`expr $part_pos + $pts` | |||||
} | |||||
run_qemu() { | |||||
# Run qemu with serial output redirected to $qemu_out. If $brkfun is non-empty, | |||||
# wait until $brkfun is reached or $timeout expires, then kill QEMU | |||||
qemuextra= | |||||
if [ "$brkfun" ]; then | |||||
qemuextra="-S $qemugdb" | |||||
fi | |||||
if [ -z "$timeout" ] || [ $timeout -le 0 ]; then | |||||
timeout=$default_timeout; | |||||
fi | |||||
t0=$(get_time) | |||||
( | |||||
ulimit -t $timeout | |||||
exec $qemu -nographic $qemuopts -serial file:$qemu_out -monitor null -no-reboot $qemuextra | |||||
) > $out 2> $err & | |||||
pid=$! | |||||
# wait for QEMU to start | |||||
sleep 1 | |||||
if [ -n "$brkfun" ]; then | |||||
# find the address of the kernel $brkfun function | |||||
brkaddr=`$grep " $brkfun\$" $sym_table | $sed -e's/ .*$//g'` | |||||
( | |||||
echo "target remote localhost:$gdbport" | |||||
echo "break *0x$brkaddr" | |||||
echo "continue" | |||||
) > $gdb_in | |||||
$gdb -batch -nx -x $gdb_in > /dev/null 2>&1 | |||||
# make sure that QEMU is dead | |||||
# on OS X, exiting gdb doesn't always exit qemu | |||||
kill $pid > /dev/null 2>&1 | |||||
fi | |||||
} | |||||
build_run() { | |||||
# usage: build_run <tag> <args> | |||||
show_build_tag "$1" | |||||
shift | |||||
if $verbose; then | |||||
echo "$make $@ ..." | |||||
fi | |||||
$make $makeopts $@ 'DEFS+=-DDEBUG_GRADE' > $out 2> $err | |||||
if [ $? -ne 0 ]; then | |||||
echo $make $@ failed | |||||
exit 1 | |||||
fi | |||||
# now run qemu and save the output | |||||
run_qemu | |||||
show_time | |||||
} | |||||
check_result() { | |||||
# usage: check_result <tag> <check> <check args...> | |||||
show_check_tag "$1" | |||||
shift | |||||
# give qemu some time to run (for asynchronous mode) | |||||
if [ ! -s $qemu_out ]; then | |||||
sleep 4 | |||||
fi | |||||
if [ ! -s $qemu_out ]; then | |||||
fail > /dev/null | |||||
echo 'no $qemu_out' | |||||
else | |||||
check=$1 | |||||
shift | |||||
$check "$@" | |||||
fi | |||||
} | |||||
check_regexps() { | |||||
okay=yes | |||||
not=0 | |||||
reg=0 | |||||
error= | |||||
for i do | |||||
if [ "x$i" = "x!" ]; then | |||||
not=1 | |||||
elif [ "x$i" = "x-" ]; then | |||||
reg=1 | |||||
else | |||||
if [ $reg -ne 0 ]; then | |||||
$grep '-E' "^$i\$" $qemu_out > /dev/null | |||||
else | |||||
$grep '-F' "$i" $qemu_out > /dev/null | |||||
fi | |||||
found=$(($? == 0)) | |||||
if [ $found -eq $not ]; then | |||||
if [ $found -eq 0 ]; then | |||||
msg="!! error: missing '$i'" | |||||
else | |||||
msg="!! error: got unexpected line '$i'" | |||||
fi | |||||
okay=no | |||||
if [ -z "$error" ]; then | |||||
error="$msg" | |||||
else | |||||
error="$error\n$msg" | |||||
fi | |||||
fi | |||||
not=0 | |||||
reg=0 | |||||
fi | |||||
done | |||||
if [ "$okay" = "yes" ]; then | |||||
pass | |||||
else | |||||
fail "$error" | |||||
if $verbose; then | |||||
exit 1 | |||||
fi | |||||
fi | |||||
} | |||||
run_test() { | |||||
# usage: run_test [-tag <tag>] [-Ddef...] [-check <check>] checkargs ... | |||||
tag= | |||||
check=check_regexps | |||||
while true; do | |||||
select= | |||||
case $1 in | |||||
-tag) | |||||
select=`expr substr $1 2 ${#1}` | |||||
eval $select='$2' | |||||
;; | |||||
esac | |||||
if [ -z "$select" ]; then | |||||
break | |||||
fi | |||||
shift | |||||
shift | |||||
done | |||||
defs= | |||||
while expr "x$1" : "x-D.*" > /dev/null; do | |||||
defs="DEFS+='$1' $defs" | |||||
shift | |||||
done | |||||
if [ "x$1" = "x-check" ]; then | |||||
check=$2 | |||||
shift | |||||
shift | |||||
fi | |||||
$make $makeopts touch > /dev/null 2>&1 | |||||
build_run "$tag" "$defs" | |||||
check_result 'check result' "$check" "$@" | |||||
} | |||||
quick_run() { | |||||
# usage: quick_run <tag> [-Ddef...] | |||||
tag="$1" | |||||
shift | |||||
defs= | |||||
while expr "x$1" : "x-D.*" > /dev/null; do | |||||
defs="DEFS+='$1' $defs" | |||||
shift | |||||
done | |||||
$make $makeopts touch > /dev/null 2>&1 | |||||
build_run "$tag" "$defs" | |||||
} | |||||
quick_check() { | |||||
# usage: quick_check <tag> checkargs ... | |||||
tag="$1" | |||||
shift | |||||
check_result "$tag" check_regexps "$@" | |||||
} | |||||
## kernel image | |||||
osimg=$(make_print ucoreimg) | |||||
## set default qemu-options | |||||
qemuopts="-hda $osimg" | |||||
## set break-function, default is readline | |||||
brkfun=readline | |||||
## check now!! | |||||
quick_run 'Check PMM' | |||||
pts=20 | |||||
quick_check 'check pmm' \ | |||||
'memory management: default_pmm_manager' \ | |||||
'check_alloc_page() succeeded!' \ | |||||
'check_pgdir() succeeded!' \ | |||||
'check_boot_pgdir() succeeded!' | |||||
pts=20 | |||||
quick_check 'check page table' \ | |||||
'PDE(0e0) c0000000-f8000000 38000000 urw' \ | |||||
' |-- PTE(38000) c0000000-f8000000 38000000 -rw' \ | |||||
'PDE(001) fac00000-fb000000 00400000 -rw' \ | |||||
' |-- PTE(000e0) faf00000-fafe0000 000e0000 urw' \ | |||||
' |-- PTE(00001) fafeb000-fafec000 00001000 -rw' | |||||
pts=10 | |||||
quick_check 'check ticks' \ | |||||
'++ setup timer interrupts' \ | |||||
'100 ticks' \ | |||||
'End of Test.' | |||||
## print final-score | |||||
show_final | |||||
@ -0,0 +1,58 @@ | |||||
/* Simple linker script for the ucore kernel. | |||||
See the GNU ld 'info' manual ("info ld") to learn the syntax. */ | |||||
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") | |||||
OUTPUT_ARCH(i386) | |||||
ENTRY(kern_entry) | |||||
SECTIONS { | |||||
/* Load the kernel at this address: "." means the current address */ | |||||
. = 0xC0100000; | |||||
.text : { | |||||
*(.text .stub .text.* .gnu.linkonce.t.*) | |||||
} | |||||
PROVIDE(etext = .); /* Define the 'etext' symbol to this value */ | |||||
.rodata : { | |||||
*(.rodata .rodata.* .gnu.linkonce.r.*) | |||||
} | |||||
/* Include debugging information in kernel memory */ | |||||
.stab : { | |||||
PROVIDE(__STAB_BEGIN__ = .); | |||||
*(.stab); | |||||
PROVIDE(__STAB_END__ = .); | |||||
BYTE(0) /* Force the linker to allocate space | |||||
for this section */ | |||||
} | |||||
.stabstr : { | |||||
PROVIDE(__STABSTR_BEGIN__ = .); | |||||
*(.stabstr); | |||||
PROVIDE(__STABSTR_END__ = .); | |||||
BYTE(0) /* Force the linker to allocate space | |||||
for this section */ | |||||
} | |||||
/* Adjust the address for the data segment to the next page */ | |||||
. = ALIGN(0x1000); | |||||
/* The data segment */ | |||||
.data : { | |||||
*(.data) | |||||
} | |||||
PROVIDE(edata = .); | |||||
.bss : { | |||||
*(.bss) | |||||
} | |||||
PROVIDE(end = .); | |||||
/DISCARD/ : { | |||||
*(.eh_frame .note.GNU-stack) | |||||
} | |||||
} |
@ -0,0 +1,43 @@ | |||||
#include <stdio.h> | |||||
#include <errno.h> | |||||
#include <string.h> | |||||
#include <sys/stat.h> | |||||
int | |||||
main(int argc, char *argv[]) { | |||||
struct stat st; | |||||
if (argc != 3) { | |||||
fprintf(stderr, "Usage: <input filename> <output filename>\n"); | |||||
return -1; | |||||
} | |||||
if (stat(argv[1], &st) != 0) { | |||||
fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno)); | |||||
return -1; | |||||
} | |||||
printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size); | |||||
if (st.st_size > 510) { | |||||
fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size); | |||||
return -1; | |||||
} | |||||
char buf[512]; | |||||
memset(buf, 0, sizeof(buf)); | |||||
FILE *ifp = fopen(argv[1], "rb"); | |||||
int size = fread(buf, 1, st.st_size, ifp); | |||||
if (size != st.st_size) { | |||||
fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size); | |||||
return -1; | |||||
} | |||||
fclose(ifp); | |||||
buf[510] = 0x55; | |||||
buf[511] = 0xAA; | |||||
FILE *ofp = fopen(argv[2], "wb+"); | |||||
size = fwrite(buf, 1, 512, ofp); | |||||
if (size != 512) { | |||||
fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size); | |||||
return -1; | |||||
} | |||||
fclose(ofp); | |||||
printf("build 512 bytes boot sector: '%s' success!\n", argv[2]); | |||||
return 0; | |||||
} | |||||
@ -0,0 +1,29 @@ | |||||
#include <stdio.h> | |||||
int | |||||
main(void) { | |||||
printf("# handler\n"); | |||||
printf(".text\n"); | |||||
printf(".globl __alltraps\n"); | |||||
int i; | |||||
for (i = 0; i < 256; i ++) { | |||||
printf(".globl vector%d\n", i); | |||||
printf("vector%d:\n", i); | |||||
if ((i < 8 || i > 14) && i != 17) { | |||||
printf(" pushl $0\n"); | |||||
} | |||||
printf(" pushl $%d\n", i); | |||||
printf(" jmp __alltraps\n"); | |||||
} | |||||
printf("\n"); | |||||
printf("# vector table\n"); | |||||
printf(".data\n"); | |||||
printf(".globl __vectors\n"); | |||||
printf("__vectors:\n"); | |||||
for (i = 0; i < 256; i ++) { | |||||
printf(" .long vector%d\n", i); | |||||
} | |||||
return 0; | |||||
} | |||||
@ -0,0 +1,271 @@ | |||||
PROJ := 8 | |||||
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 | |||||
GDB := $(GCCPREFIX)gdb | |||||
CC ?= $(GCCPREFIX)gcc | |||||
CFLAGS := -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc $(DEFS) | |||||
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector) | |||||
CTYPE := c S | |||||
LD := $(GCCPREFIX)ld | |||||
LDFLAGS := -m $(shell $(LD) -V | grep elf_i386 2>/dev/null) | |||||
LDFLAGS += -nostdlib | |||||
OBJCOPY := $(GCCPREFIX)objcopy | |||||
OBJDUMP := $(GCCPREFIX)objdump | |||||
COPY := cp | |||||
MKDIR := mkdir -p | |||||
MV := mv | |||||
RM := rm -f | |||||
AWK := awk | |||||
SED := sed | |||||
SH := sh | |||||
TR := tr | |||||
TOUCH := touch -c | |||||
OBJDIR := obj | |||||
BINDIR := bin | |||||
ALLOBJS := | |||||
ALLDEPS := | |||||
TARGETS := | |||||
include tools/function.mk | |||||
listf_cc = $(call listf,$(1),$(CTYPE)) | |||||
# for cc | |||||
add_files_cc = $(call add_files,$(1),$(CC),$(CFLAGS) $(3),$(2),$(4)) | |||||
create_target_cc = $(call create_target,$(1),$(2),$(3),$(CC),$(CFLAGS)) | |||||
# for hostcc | |||||
add_files_host = $(call add_files,$(1),$(HOSTCC),$(HOSTCFLAGS),$(2),$(3)) | |||||
create_target_host = $(call create_target,$(1),$(2),$(3),$(HOSTCC),$(HOSTCFLAGS)) | |||||
cgtype = $(patsubst %.$(2),%.$(3),$(1)) | |||||
objfile = $(call toobj,$(1)) | |||||
asmfile = $(call cgtype,$(call toobj,$(1)),o,asm) | |||||
outfile = $(call cgtype,$(call toobj,$(1)),o,out) | |||||
symfile = $(call cgtype,$(call toobj,$(1)),o,sym) | |||||
# for match pattern | |||||
match = $(shell echo $(2) | $(AWK) '{for(i=1;i<=NF;i++){if(match("$(1)","^"$$(i)"$$")){exit 1;}}}'; echo $$?) | |||||
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | |||||
# include kernel/user | |||||
INCLUDE += libs/ | |||||
CFLAGS += $(addprefix -I,$(INCLUDE)) | |||||
LIBDIR += libs | |||||
$(call add_files_cc,$(call listf_cc,$(LIBDIR)),libs,) | |||||
# ------------------------------------------------------------------- | |||||
# kernel | |||||
KINCLUDE += kern/debug/ \ | |||||
kern/driver/ \ | |||||
kern/trap/ \ | |||||
kern/mm/ \ | |||||
kern/libs/ \ | |||||
kern/sync/ \ | |||||
kern/fs/ | |||||
KSRCDIR += kern/init \ | |||||
kern/libs \ | |||||
kern/debug \ | |||||
kern/driver \ | |||||
kern/trap \ | |||||
kern/mm \ | |||||
kern/sync \ | |||||
kern/fs | |||||
KCFLAGS += $(addprefix -I,$(KINCLUDE)) | |||||
$(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,$(KCFLAGS)) | |||||
KOBJS = $(call read_packet,kernel libs) | |||||
# create kernel target | |||||
kernel = $(call totarget,kernel) | |||||
$(kernel): tools/kernel.ld | |||||
$(kernel): $(KOBJS) | |||||
@echo + ld $@ | |||||
$(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS) | |||||
@$(OBJDUMP) -S $@ > $(call asmfile,kernel) | |||||
@$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $(call symfile,kernel) | |||||
$(call create_target,kernel) | |||||
# ------------------------------------------------------------------- | |||||
# create bootblock | |||||
bootfiles = $(call listf_cc,boot) | |||||
$(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),$(CFLAGS) -Os -nostdinc)) | |||||
bootblock = $(call totarget,bootblock) | |||||
$(bootblock): $(call toobj,boot/bootasm.S) $(call toobj,$(bootfiles)) | $(call totarget,sign) | |||||
@echo + ld $@ | |||||
$(V)$(LD) $(LDFLAGS) -N -T tools/boot.ld $^ -o $(call toobj,bootblock) | |||||
@$(OBJDUMP) -S $(call objfile,bootblock) > $(call asmfile,bootblock) | |||||
@$(OBJCOPY) -S -O binary $(call objfile,bootblock) $(call outfile,bootblock) | |||||
@$(call totarget,sign) $(call outfile,bootblock) $(bootblock) | |||||
$(call create_target,bootblock) | |||||
# ------------------------------------------------------------------- | |||||
# create 'sign' tools | |||||
$(call add_files_host,tools/sign.c,sign,sign) | |||||
$(call create_target_host,sign,sign) | |||||
# ------------------------------------------------------------------- | |||||
# create ucore.img | |||||
UCOREIMG := $(call totarget,ucore.img) | |||||
$(UCOREIMG): $(kernel) $(bootblock) | |||||
$(V)dd if=/dev/zero of=$@ count=10000 | |||||
$(V)dd if=$(bootblock) of=$@ conv=notrunc | |||||
$(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc | |||||
$(call create_target,ucore.img) | |||||
# ------------------------------------------------------------------- | |||||
# create swap.img | |||||
SWAPIMG := $(call totarget,swap.img) | |||||
$(SWAPIMG): | |||||
$(V)dd if=/dev/zero of=$@ bs=1M count=128 | |||||
$(call create_target,swap.img) | |||||
# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> | |||||
$(call finish_all) | |||||
IGNORE_ALLDEPS = clean \ | |||||
dist-clean \ | |||||
grade \ | |||||
touch \ | |||||
print-.+ \ | |||||
handin | |||||
ifeq ($(call match,$(MAKECMDGOALS),$(IGNORE_ALLDEPS)),0) | |||||
-include $(ALLDEPS) | |||||
endif | |||||
# files for grade script | |||||
TARGETS: $(TARGETS) | |||||
.DEFAULT_GOAL := TARGETS | |||||
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback | |||||
.PHONY: qemu qemu-nox debug debug-nox | |||||
qemu: $(UCOREIMG) $(SWAPIMG) | |||||
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null | |||||
qemu-nox: $(UCOREIMG) $(SWAPIMG) | |||||
$(V)$(QEMU) -serial mon:stdio $(QEMUOPTS) -nographic | |||||
TERMINAL := gnome-terminal | |||||
debug: $(UCOREIMG) $(SWAPIMG) | |||||
$(V)$(QEMU) -S -s -parallel stdio $(QEMUOPTS) -serial null & | |||||
$(V)sleep 2 | |||||
$(V)$(TERMINAL) -e "$(GDB) -q -x tools/gdbinit" | |||||
debug-nox: $(UCOREIMG) $(SWAPIMG) | |||||
$(V)$(QEMU) -S -s -serial mon:stdio $(QEMUOPTS) -nographic & | |||||
$(V)sleep 2 | |||||
$(V)$(TERMINAL) -e "$(GDB) -q -x tools/gdbinit" | |||||
.PHONY: grade touch | |||||
GRADE_GDB_IN := .gdb.in | |||||
GRADE_QEMU_OUT := .qemu.out | |||||
HANDIN := proj$(PROJ)-handin.tar.gz | |||||
TOUCH_FILES := kern/trap/trap.c | |||||
MAKEOPTS := --quiet --no-print-directory | |||||
grade: | |||||
$(V)$(MAKE) $(MAKEOPTS) clean | |||||
$(V)$(SH) tools/grade.sh | |||||
touch: | |||||
$(V)$(foreach f,$(TOUCH_FILES),$(TOUCH) $(f)) | |||||
print-%: | |||||
@echo $($(shell echo $(patsubst print-%,%,$@) | $(TR) [a-z] [A-Z])) | |||||
.PHONY: clean dist-clean handin packall | |||||
clean: | |||||
$(V)$(RM) $(GRADE_GDB_IN) $(GRADE_QEMU_OUT) | |||||
-$(RM) -r $(OBJDIR) $(BINDIR) | |||||
dist-clean: clean | |||||
-$(RM) $(HANDIN) | |||||
handin: packall | |||||
@echo Please visit http://learn.tsinghua.edu.cn and upload $(HANDIN). Thanks! | |||||
packall: clean | |||||
@$(RM) -f $(HANDIN) | |||||
@tar -czf $(HANDIN) `find . -type f -o -type d | grep -v '^\.*$$' | grep -vF '$(HANDIN)'` | |||||