提交 2a270e5c 创建 作者: Silas Boyd-Wickizer's avatar Silas Boyd-Wickizer

Start with rsc's boot.S

上级 4bb8d019
set $lastcs = -1
define hook-stop
# There doesn't seem to be a good way to detect if we're in 16- or
# 32-bit mode, but in 32-bit mode we always run with CS == 8 in the
# kernel and CS == 35 in user space
if $cs == 8 || $cs == 35
if $lastcs != 8 && $lastcs != 35
set architecture i386
end
x/i $pc
else
if $lastcs == -1 || $lastcs == 8 || $lastcs == 35
set architecture i8086
end
# Translate the segment:offset into a physical address
printf "[%4x:%4x] ", $cs, $eip
x/i $cs*16+$eip
end
set $lastcs = $cs
end
echo + target remote localhost:1234\n
target remote localhost:1234
echo + symbol-file kernel\n
symbol-file kernel
-include config.mk
OBJS = \
bio.o\
condvar.o\
console.o\
exec.o\
file.o\
fs.o\
ide.o\
ioapic.o\
kalloc.o\
kbd.o\
lapic.o\
main.o\
mp.o\
picirq.o\
pipe.o\
proc.o\
spinlock.o\
string.o\
swtch.o\
syscall.o\
sysfile.o\
sysproc.o\
timer.o\
trapasm.o\
trap.o\
uart.o\
vectors.o\
vm.o\
ns.o \
rcu.o \
bonsai.o \
asm.o \
main.o \
trap.o
# Cross-compiling (e.g., on Mac OS X)
TOOLPREFIX ?= x86_64-jos-elf-
# Using native tools (e.g., on X86 Linux)
#TOOLPREFIX =
# Try to infer the correct TOOLPREFIX if not set
ifndef TOOLPREFIX
TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; \
then echo 'i386-jos-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-jos-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-jos-elf-', set your TOOLPREFIX" 1>&2; \
echo "*** environment variable to that prefix and run 'make' again." 1>&2; \
echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
echo "***" 1>&2; exit 1; fi)
endif
# The i386 ('qemu') mtrace doesn't work, but 'qemu-system-x86_64' mtrace works.
MTRACE ?= ../mtrace/x86_64-softmmu/qemu-system-x86_64
QEMUSRC ?= ../mtrace
ifeq ($(QEMUSRC),)
$(error You need to set QEMUSRC (e.g. make QEMUSRC=~/qemu))
endif
# If the makefile can't find QEMU, specify its path here
#QEMU =
# Try to infer the correct QEMU
ifndef QEMU
QEMU = $(shell if which qemu > /dev/null; \
then echo qemu; exit; \
else \
qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; \
if test -x $$qemu; then echo $$qemu; exit; fi; fi; \
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 "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; \
echo "***" 1>&2; exit 1)
endif
QEMU = qemu-system-x86_64
NM = $(TOOLPREFIX)nm
CC = $(TOOLPREFIX)gcc
......@@ -86,230 +17,34 @@ AS = $(TOOLPREFIX)gas
LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -I$(QEMUSRC) -std=c99 -fms-extensions -mno-sse
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m64 \
-Werror -fms-extensions -mno-sse
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
ASFLAGS = -m32 -gdwarf-2
# FreeBSD ld wants ``elf_i386_fbsd''
LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null)
ASFLAGS = -m64 -gdwarf-2
LDFLAGS += -m elf_x86_64
xv6.img: bootblock kernel fs.img
dd if=/dev/zero of=xv6.img count=10000
dd if=bootblock of=xv6.img conv=notrunc
dd if=kernel of=xv6.img seek=1 conv=notrunc
kernel: boot.o $(OBJS)
$(LD) $(LDFLAGS) -T kernel.ld -z max-page-size=4096 -e start -o $@ boot.o $(OBJS)
$(OBJDUMP) -S $@ >$@.asm
xv6memfs.img: bootblock kernelmemfs
dd if=/dev/zero of=xv6memfs.img count=10000
dd if=bootblock of=xv6memfs.img conv=notrunc
dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc
bootblock: bootasm.S bootmain.c
$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
$(OBJDUMP) -S bootblock.o > bootblock.asm
$(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
./sign.pl bootblock
bootother: bootother.S
$(CC) $(CFLAGS) -nostdinc -I. -c bootother.S
$(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o bootother.out bootother.o
$(OBJCOPY) -S -O binary bootother.out bootother
$(OBJDUMP) -S bootother.out > bootother.asm
initcode: initcode.S
$(CC) $(CFLAGS) -nostdinc -I. -c initcode.S
$(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o
$(OBJCOPY) -S -O binary initcode.out initcode
$(OBJDUMP) -S initcode.o > initcode.asm
kernel: $(OBJS) multiboot.o data.o bootother initcode
$(LD) $(LDFLAGS) -T kernel.ld -e multiboot_entry -o kernel multiboot.o data.o $(OBJS) -b binary initcode bootother
$(OBJDUMP) -S kernel > kernel.asm
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
# kernelmemfs is a copy of kernel that maintains the
# disk image in memory instead of writing to a disk.
# This is not so useful for testing persistent storage or
# exploring disk buffering implementations, but it is
# great for testing the kernel on real hardware without
# needing a scratch disk.
MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o
kernelmemfs: $(MEMFSOBJS) multiboot.o data.o bootother initcode fs.img
$(LD) $(LDFLAGS) -Ttext 0x100000 -e main -o kernelmemfs multiboot.o data.o $(MEMFSOBJS) -b binary initcode bootother fs.img
$(OBJDUMP) -S kernelmemfs > kernelmemfs.asm
$(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym
tags: $(OBJS) bootother.S _init
etags *.S *.c
vectors.S: vectors.pl
perl vectors.pl > vectors.S
ULIB = ulib.o usys.o printf.o umalloc.o uthread.o
_%: %.o $(ULIB)
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^
$(OBJDUMP) -S $@ > $*.asm
$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
_forktest: forktest.o $(ULIB)
# forktest has less library code linked in - needs to be small
# in order to be able to max out the proc table.
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o
$(OBJDUMP) -S _forktest > forktest.asm
mkfs: mkfs.c fs.h
gcc -m32 -Werror -Wall -o mkfs mkfs.c
UPROGS=\
_cat\
_echo\
_forktest\
_grep\
_init\
_kill\
_ln\
_ls\
_mkdir\
_rm\
_sh\
_stressfs\
_usertests\
_wc\
_zombie\
_halt\
_thrtest\
_sleep\
_maptest\
_forktree\
_forkexectree\
_mapbench\
_dirbench\
fs.img: mkfs README $(UPROGS)
./mkfs fs.img README $(UPROGS)
-include *.d
.PHONY: clean qemu
clean:
rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \
*.o *.d *.asm *.sym vectors.S parport.out \
bootblock kernel xv6.img fs.img mkfs \
$(UPROGS)
# make a printout
FILES = $(shell grep -v '^\#' runoff.list)
PRINT = runoff.list runoff.spec $(FILES)
bootblock kernel xv6.img fs.img mkfs
xv6.pdf: $(PRINT)
./runoff
ls -l xv6.pdf
print: xv6.pdf
# run in emulators
bochs : fs.img xv6.img
if [ ! -e .bochsrc ]; then ln -s dot-bochsrc .bochsrc; fi
bochs -q
mscan.syms: kernel
$(NM) -S $< > $@
mscan.kern: kernel
cp $< $@
# try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000)
# QEMU's gdb stub command line changed in 0.11
QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; \
then echo "-gdb tcp::$(GDBPORT)"; \
else echo "-s -p $(GDBPORT)"; fi)
ifndef CPUS
CPUS := 2
endif
QEMUOPTS = -hdb fs.img xv6.img -smp $(CPUS) -m 512
MTRACEOPTS = -icount 0 -rtc clock=vm \
-mtrace-enable -mtrace-file mtrace.out -mtrace-quantum 1000
qemu: fs.img xv6.img
$(QEMU) -serial mon:stdio $(QEMUOPTS)
qemu-memfs: xv6memfs.img
$(QEMU) xv6memfs.img -smp $(CPUS)
qemu-nox: fs.img xv6.img
$(QEMU) -nographic $(QEMUOPTS)
mtrace-nox: fs.img xv6.img mscan.syms mscan.kern
$(MTRACE) -nographic $(QEMUOPTS) $(MTRACEOPTS)
mtrace-gdb: fs.img xv6.img mscan.syms mscan.kern
$(MTRACE) -nographic $(QEMUOPTS) $(MTRACEOPTS) -S $(QEMUGDB)
mtrace.txt: mtrace.out $(QEMUSRC)/mtrace-tools/m2text
$(QEMUSRC)/mtrace-tools/m2text $< > $@
mscan.out: mtrace.out $(QEMUSRC)/mtrace-tools/mscan
$(QEMUSRC)/mtrace-tools/mscan > $@ || (rm -f $@; exit 2)
mscan.sorted: mscan.out $(QEMUSRC)/mtrace-tools/sersec-sort
$(QEMUSRC)/mtrace-tools/sersec-sort < $< > $@
.gdbinit: .gdbinit.tmpl
sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@
qemu-gdb: fs.img xv6.img .gdbinit
@echo "*** Now run 'gdb'." 1>&2
$(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)
qemu-nox-gdb: fs.img xv6.img .gdbinit
@echo "*** Now run 'gdb'." 1>&2
$(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)
mtrace-nox-gdb: fs.img xv6.img mscan.syms mscan.kern .gdbinit
$(MTRACE) -nographic $(QEMUOPTS) $(MTRACEOPTS) -S -gdb tcp::$(GDBPORT)
# CUT HERE
# prepare dist for students
# after running make dist, probably want to
# rename it to rev0 or rev1 or so on and then
# check in that version.
EXTRA=\
mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\
ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c\
printf.c umalloc.c\
README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\
.gdbinit.tmpl gdbutil\
dist:
rm -rf dist
mkdir dist
for i in $(FILES); \
do \
grep -v PAGEBREAK $$i >dist/$$i; \
done
sed '/CUT HERE/,$$d' Makefile >dist/Makefile
echo >dist/runoff.spec
cp $(EXTRA) dist
dist-test:
rm -rf dist
make dist
rm -rf dist-test
mkdir dist-test
cp dist/* dist-test
cd dist-test; $(MAKE) print
cd dist-test; $(MAKE) bochs || true
cd dist-test; $(MAKE) qemu
# update this rule (change rev#) when it is time to
# make a new revision.
tar:
rm -rf /tmp/xv6
mkdir -p /tmp/xv6
cp dist/* dist/.gdbinit.tmpl /tmp/xv6
(cd /tmp; tar cf - xv6) | gzip >xv6-rev5.tar.gz
QEMUOPTS = -smp $(CPUS) -m 512
.PHONY: dist-test dist
qemu: kernel
$(QEMU) -serial mon:stdio $(QEMUOPTS) -kernel kernel
// This file defines amd64 data structures and constants.
// The constants are used by assembly too, so the data structures
// are guarded with #ifndef __ASSEMBLER__.
// CGATEXTMEM is the physical address of the CGA text memory.
#define CGATEXTMEM 0xB8000
// Segment descriptors. See section 3.4.5 of 253668.pdf.
// User segment bits (SEG_S set).
#define SEG_A (1<<0) /* segment accessed bit */
#define SEG_R (1<<1) /* readable (code) */
#define SEG_W (1<<1) /* writable (data) */
#define SEG_C (1<<2) /* conforming segment (code) */
#define SEG_E (1<<2) /* expand-down bit (data) */
#define SEG_CODE (1<<3) /* code segment (instead of data) */
// System segment bits (SEG_S is clear).
#define SEG_LDT (2<<0) /* local descriptor table */
#define SEG_TSS64A (9<<0) /* available 64-bit TSS */
#define SEG_TSS64B (11<<0) /* busy 64-bit TSS */
#define SEG_CALL64 (12<<0) /* 64-bit call gate */
#define SEG_INTR64 (14<<0) /* 64-bit interrupt gate */
#define SEG_TRAP64 (15<<0) /* 64-bit trap gate */
// User and system segment bits.
#define SEG_S (1<<4) /* if 0, system descriptor */
#define SEG_DPL(x) ((x)<<5) /* descriptor privilege level (2 bits) */
#define SEG_P (1<<7) /* segment present */
#define SEG_AVL (1<<8) /* available for operating system use */
#define SEG_L (1<<9) /* long mode */
#define SEG_D (1<<10) /* default operation size 32-bit */
#define SEG_G (1<<11) /* granularity */
// Segment descriptor memory layout.
#ifndef __ASSEMBLER__
typedef struct Segdesc Segdesc;
struct Segdesc {
uint16 limit0;
uint16 base0;
uint8 base1;
uint8 bits;
uint8 bitslimit1;
uint8 base2;
};
// SEGDESC constructs a segment descriptor literal
// with the given, base, limit, and type bits.
#define SEGDESC(base, limit, bits) (Segdesc){ \
(limit)&0xffff, (base)&0xffff, \
((base)>>16)&0xff, \
(bits)&0xff, \
(((bits)>>4)&0xf0) | ((limit>>16)&0xf), \
((base)>>24)&0xff, \
}
// SEGDESCHI constructs an extension segment descriptor
// literal that records the high bits of base.
#define SEGDESCHI(base) (Segdesc){ \
((base)>>32)&0xffff, ((base)>>48)&0xffff, \
}
#endif
// Segment selectors (indexes) in our GDTs.
// Defined by our convention, not the architecture.
#define KCSEG32 (1<<3) /* kernel 32-bit code segment */
#define KCSEG (2<<3) /* kernel code segment */
#define KDSEG (3<<3) /* kernel data segment */
#define TSSSEG (4<<3) /* tss segment - takes two slots */
#define UCSEG (6<<3) /* user code segment */
#define UDSEG (7<<3) /* user data segment */
#define NUMSEG 8
// Interrupt descriptors.
// See section 6.11 of 253668.pdf.
#define INT_DPL(x) ((x)<<5) /* descriptor privilege level (2 bits) */
#define INT_P (1<<7) /* interrupt descriptor present */
#ifndef __ASSEMBLER__
typedef struct Intdesc Intdesc;
struct Intdesc
{
uint16 rip0;
uint16 cs;
uint8 reserved0;
uint8 bits;
uint16 rip1;
uint32 rip2;
uint32 reserved1;
};
// INTDESC constructs an interrupt descriptor literal
// that records the given code segment, instruction pointer,
// and type bits.
#define INTDESC(cs, rip, bits) (Intdesc){ \
(rip)&0xffff, (cs), 0, bits, ((rip)>>16)&0xffff, \
(uint64)(rip)>>32, 0, \
}
#endif
// Task segment.
// See section 7.7 of 253668.pdf.
#ifndef __ASSEMBLER__
typedef struct Taskseg Taskseg;
struct Taskseg
{
// I have no idea why they insisted on
// misaligning all the uint64 fields.
uint32 reserved0;
uint64 rsp0; /* stack pointers for priv levels 0, 1, 2 */
uint64 rsp1;
uint64 rsp2;
uint32 reserved1;
uint32 reserved2;
uint64 ist1; /* interrupt stack table pointers */
uint64 ist2;
uint64 ist3;
uint64 ist4;
uint64 ist5;
uint64 ist6;
uint64 ist7;
uint32 reserved3;
uint32 reserved4;
uint16 reserved5;
uint16 ioperm; /* offset from TSS base to I/O permission bitmap */
} __attribute__((packed));
#endif
// 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 Enable
#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
.code64
.text
.globl inb
inb:
movq %rdi, %rdx
xorq %rax, %rax
inb %dx, %al
ret
.globl outb
outb:
movq %rdi, %rdx
movq %rsi, %rax
outb %al, %dx
ret
.globl hlt
hlt:
hlt
jmp hlt
.globl lidt
lidt:
# Create pseudo-descriptor on stack:
# 2 bytes length-1 followed by 8 bytes base address.
subq $16, %rsp
subq $1, %rsi
movw %si, 0(%rsp)
movq %rdi, 2(%rsp)
lidt 0(%rsp)
addq $16, %rsp
ret
.globl lgdt
lgdt:
# Create pseudo-descriptor on stack:
# 2 bytes length-1 followed by 8 bytes base address.
subq $16, %rsp
subq $1, %rsi
movw %si, 0(%rsp)
movq %rdi, 2(%rsp)
lgdt 0(%rsp)
addq $16, %rsp
ret
.globl ltr
ltr:
ltr %di
ret
// Delay for the given number of microseconds.
// Assume 20 atomic exchanges = 1 microsecond.
// Close enough for now.
.globl microdelay
microdelay:
movq %rdi, %rcx
imul $20, %rcx
pushq $0
1:
lock xchgq %rax, (%rsp)
loop 1b
popq %rax
ret
.globl readeflags
readeflags:
pushfq
popq %rax
ret
# x86-64 bootstrap, assuming load by MultiBoot-compliant loader.
# The MutliBoot specification is at:
# http://www.gnu.org/software/grub/manual/multiboot/multiboot.html
# GRUB is a MultiBoot loader, as is qemu's -kernel option.
#include "amd64.h"
# KADDR is the kernel virtual address of the first byte of physical memory.
# The linker loads the executable as if starting at KBASE+2MB, but we
# ask the loader to load the kernel at physical 2MB and then set up the
# necessary memory mapping to switch to the higher address.
# The value of KBASE must match the definitions in vmx.h and kernel.ld.
#define KBASE 0xffffff0000000000 /* last TB */
# PADDR(x) is the physical memory address corresponding to x.
# Until we set up the memory map, fairly late in this file, we have to
# refer to PADDR(symbol) instead of symbol, so that we use the
# physical address.
#define PADDR(x) ((x) - KBASE)
# DATAMAGIC is a magic value we set in the data segment and then
# look for to make sure that the data segment was loaded in the right place.
#define DATAMAGIC 0x123456789abcdef0
# STACK is the size of the bootstrap stack.
#define STACK 8192
# PANIC prints a message on the screen and stops execution.
#define PANIC(x) \
movl $PADDR(10f), %edi; \
call bootpanic; \
10: .asciz x
# MultiBoot header.
# http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Header-layout
.align 4
.text
.globl multiboot_header
multiboot_header:
#define magic 0x1badb002
#define flags (1<<16 | 1<<0)
.long magic
.long flags
.long (- magic - flags) # checksum
.long PADDR(multiboot_header) # header address
.long PADDR(multiboot_header) # load address
.long PADDR(edata) # load end address
.long PADDR(end) # bss end address
.long PADDR(start) # entry address
# Entry point jumped to by boot loader. Running in 32-bit mode.
# http://www.gnu.org/software/grub/manual/multiboot/multiboot.html#Machine-state
#
# EAX = 0x2badb002
# EBX = address of multiboot information structure
# CS = 32-bit read/execute code segment with identity map
# DS, ES, FS, GS, SS = 32-bit read/write data segment with identity map
# A20 gate = enabled
# CR0 = PE set, PG clear
# EFLAGS = VM clear, IF clear
#
.code32
.globl start
start:
# Tell BIOS to do "warm reboot" when we shut down.
movw $0x1234, 0x472
# Set up multiboot arguments for cmain.
movl %eax, %edi
movl %ebx, %esi
# Initialize stack.
movl $PADDR(stack+STACK), %esp
# Initialize screen.
call initscreen
# Check for page size extensions.
movl $1, %eax
cpuid
testl $(1<<3), %edx
jnz 1f
PANIC("This processor does not support PSE.")
1:
# Check for 64-bit support.
movl $0x80000001, %eax
cpuid
testl $(1<<29),%edx
jnz 1f
PANIC("This processor does not support 64-bit execution.")
1:
# Zero bss. QEMU's MultiBoot seems not to.
# It's possible that the header above is not right, but it looks right.
# %edi is holding multiboot argument, so save in another register.
# (The stack is in the bss.)
movl %edi, %edx
movl $PADDR(edata), %edi
movl $PADDR(end), %ecx
subl $PADDR(edata), %ecx
movl $0, %eax
cld
rep stosb
movl %edx, %edi
# Load GDT.
subl $8, %esp
movl $PADDR(bootgdt), 4(%esp)
movw $(8*NUMSEG-1), 2(%esp)
lgdt 2(%esp)
addl $8, %esp
movl $KDSEG, %eax // data segment selector
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movl $0, %eax // null segment selector
movw %ax, %fs
movw %ax, %gs
# Enter new 32-bit code segment (already in 32-bit mode).
ljmp $KCSEG32, $PADDR(start32) // code32 segment selector
start32:
# Initialize page table.
call initpagetables
# Initialize IA-32e mode. See section 9.8.5 of 253668.pdf.
# Set CR4.PAE and CR4.PSE = 1.
movl %cr4, %eax
orl $0x30, %eax
movl %eax, %cr4
# Load CR3 with physical base address of level 4 page table.
movl $PADDR(pml4), %eax
movl %eax, %cr3
# Enable IA-32e mode by setting IA32_EFER.LME = 1.
# Also turn on IA32_EFER.SCE (syscall enable).
movl $0xc0000080, %ecx
rdmsr
orl $0x101, %eax
wrmsr
# Enable paging by setting CR0.PG = 1.
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
nop
nop
# Enter 64-bit mode.
ljmp $KCSEG, $PADDR(start64) // code64 segment selector
.code64
start64:
# The linker thinks we are running at start64, but we're actually
# running at PADDR(start64), so use an explicit calculation to
# load and jump to the correct address.
movq $KBASE, %rax
addq $PADDR(start64hi), %rax
jmp *%rax
start64hi:
# Now executing at the real link address.
# Double-check that data segment is set up correctly.
movq $DATAMAGIC, %rax
movq PADDR(datamagic), %rbx
cmpq %rax, %rbx
jz 1f
PANIC("Data segment is not loaded correctly.")
1:
# Clear frame pointer for stack walks, and call into C code.
movl $0, %ebp
call cmain
PANIC("Unexpected return from main.")
.code32
.globl bootpanic
bootpanic:
# This is 32-bit code but can be called from either
# 32- or 64-bit code, since it only uses instructions
# in the intersection of both.
movl $(CGATEXTMEM+80*12*2), %esi # line 12
1:
movb (%edi), %bl # get next char, check for NUL
testb %bl, %bl
jz 2f
movb %bl, (%esi) # write to screen
addl $2, %esi # advance screen pointer
addl $1, %edi # advance string pointer
jmp 1b
2:
# If we just busy loop here, QEMU will never finish
# writing our output to the screen. Instead, keep
# writing the zero byte over and over.
movb %bl, (%esi)
jmp 2b
initscreen:
pushal
# Initialize screen.
# 25 lines of white on black.
cld
movl $CGATEXTMEM, %edi
movl $0x0f20, %eax
movl $(80*25), %ecx
rep stosw
# Hide cursor at beginning of first line.
movw $0, %di
call setcursor
popal
ret
.globl setcursor
setcursor:
pushl %edx
pushl %eax
pushl %ebx
movl %edi, %ebx
movw $0x3d4, %dx
movw $14, %ax
outb %al, %dx
movw $0x3d5, %dx
movb %bh, %al
outb %al, %dx
movw $0x3d4, %dx
movw $15, %ax
outb %al, %dx
movw $0x3d5, %dx
movb %bl, %al
outb %al, %dx
popl %ebx
popl %eax
popl %edx
ret
# Google color bar: spaces with background colors.
colorbar:
.ascii " \x40 \x40 \x40 \x40 \x40 \x40 \x40 \x40 \x40 \x40"
.ascii " \x40 \x40 \x40 \x40 \x40 \x40 \x40 \x40 \x40 \x40"
.ascii " \x10 \x10 \x10 \x10 \x10 \x10 \x10 \x10 \x10 \x10"
.ascii " \x10 \x10 \x10 \x10 \x10 \x10 \x10 \x10 \x10 \x10"
.ascii " \x20 \x20 \x20 \x20 \x20 \x20 \x20 \x20 \x20 \x20"
.ascii " \x20 \x20 \x20 \x20 \x20 \x20 \x20 \x20 \x20 \x20"
.ascii "\xDB\x0E\xDB\x0E\xDB\x0E\xDB\x0E\xDB\x0E"
.ascii "\xDB\x0E\xDB\x0E\xDB\x0E\xDB\x0E\xDB\x0E"
.ascii "\xDB\x0E\xDB\x0E\xDB\x0E\xDB\x0E\xDB\x0E"
.ascii "\xDB\x0E\xDB\x0E\xDB\x0E\xDB\x0E\xDB\x0E"
# Initial stack
.comm stack, STACK
# Page tables. See section 4.5 of 253668.pdf.
# We map the first GB of physical memory at 0 and at 1 TB (not GB) before
# the end of physical memory. At boot time we are using the mapping at 0
# but during ordinary execution we use the high mapping.
# The intent is that after bootstrap the kernel can expand this mapping
# to cover all the available physical memory.
# This would be easier if we could use the PS bit to create GB-sized entries
# and skip the pdt table, but not all chips support it, and QEMU doesn't.
.align 4096
pml4:
.quad PADDR(pdpt) + (1<<0) + (1<<1) // present, read/write
.quad 0
.space 4096 - 2*16
.quad PADDR(pdpt) + (1<<0) + (1<<1) // present, read/write
.quad 0
.align 4096
pdpt:
.quad PADDR(pdt) + (1<<0) + (1<<1) // present, read/write
.space 4096 - 8
.align 4096
pdt:
// Filled in below.
.space 4096
initpagetables:
pushl %edi
pushl %ecx
pushl %eax
// Set up 64-bit entry in %edx:%eax.
// Base address 0, present, read/write, large page.
movl $(0 | 1<<0 | 1<<1 | 1<<7), %eax
movl $0, %edx
// Fill in 512 entries at pdt.
movl $PADDR(pdt), %edi
movl $512, %ecx
1:
// Write this 64-bit entry.
movl %eax, 0(%edi)
movl %edx, 4(%edi)
addl $8, %edi
// 64-bit add to prepare address for next entry.
// Because this is a large page entry, it covers 512 4k pages (2 MB).
add $(512*4096), %eax
adc $0, %edx
loop 1b
popl %eax
popl %ecx
popl %edi
ret
# For sanity check above.
.data
datamagic:
.quad DATAMAGIC
/* 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(_start)
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(xxx)
SECTIONS
{
/* Load the kernel at this address: "." means the current address */
. = 0xF0100000;
. = 0xFFFFFF0000100000;
PROVIDE(text = .);
.text : AT(0x100000) {
*(.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)
}
}
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "spinlock.h"
#include "condvar.h"
#include "queue.h"
#include "proc.h"
#include "x86.h"
static void bootothers(void);
static void mpmain(void);
void jmpkstack(void) __attribute__((noreturn));
void mainc(void);
static volatile int newpgdir;
// Bootstrap processor starts running C code here.
// Allocate a real stack and switch to it, first
// doing some setup required for memory allocator to work.
int
main(void)
{
pginit(pgalloc);
mpinit(); // collect info about this machine
lapicinit(mpbcpu());
seginit(); // set up segments
kinit(); // initialize memory allocator
jmpkstack(); // call mainc() on a properly-allocated stack
}
#include "vmx.h"
#include "multiboot.h"
void
jmpkstack(void)
cmain(void)
{
char *kstack, *top;
kstack = kalloc();
if(kstack == 0)
panic("jmpkstack kalloc");
top = kstack + PGSIZE;
__asm volatile("movl %0,%%esp; call mainc" : : "r" (top));
panic("jmpkstack");
}
// Set up hardware and software.
// Runs only on the boostrap processor.
void
mainc(void)
{
cprintf("\ncpu%d: starting xv6\n\n", cpu->id);
picinit(); // interrupt controller
ioapicinit(); // another interrupt controller
consoleinit(); // I/O devices & their interrupts
uartinit(); // serial port
rcuinit(); // initialize rcu module
nsinit(); // initialize name space module
pinit(); // process table
tvinit(); // trap vectors
binit(); // buffer cache
fileinit(); // file table
iinit(); // inode cache
ideinit(); // disk
if(!ismp)
timerinit(); // uniprocessor timer
//nc_init();
// tree_test();
userinit(); // first user process
bootothers(); // start other processors
kvmalloc(); // new kernel page table wo. bottom mapped
newpgdir = 1;
// Finish setting up this processor in mpmain.
mpmain();
}
// Common CPU setup code.
// Bootstrap CPU comes here from mainc().
// Other CPUs jump here from bootother.S.
static void
mpboot(void)
{
vmenable(); // turn on paging
seginit();
lapicinit(cpunum());
mpmain();
}
static void
mpmain(void)
{
cprintf("cpu%d: starting\n", cpu->id);
idtinit(); // load idt register
xchg(&cpu->booted, 1); // tell bootothers() we're up
while (!newpgdir) ; // wait until we have new page dir
switchkvm(); // switch to new page dir
scheduler(); // start running processes
}
// Start the non-boot processors.
static void
bootothers(void)
{
extern uchar _binary_bootother_start[], _binary_bootother_size[];
uchar *code;
struct cpu *c;
char *stack;
// Write bootstrap code to unused memory at 0x7000.
// The linker has placed the image of bootother.S in
// _binary_bootother_start.
code = p2v(0x7000);
memmove(code, _binary_bootother_start, (uint)_binary_bootother_size);
for(c = cpus; c < cpus+ncpu; c++){
if(c == cpus+cpunum()) // We've started already.
continue;
// Tell bootother.S what stack to use and the address of mpmain;
// it expects to find these two addresses stored just before
// its first instruction.
stack = kalloc();
*(void**)(code-4) = stack + KSTACKSIZE;
*(void**)(code-8) = mpboot;
lapicstartap(c->id, v2p(code));
// Wait for cpu to finish mpmain()
while(c->booted == 0)
;
}
}
//PAGEBREAK!
// Blank page.
typedef struct Mbdata Mbdata;
struct Mbdata
{
uint32 flags;
uint32 mem_lower; // flag 0
uint32 mem_upper; // flag 0
uint32 boot_device; // flag 1
uint32 cmdline; // flag 2
uint32 mods_count; // flag 3
uint32 mods_addr;
uint32 syms[4]; // flag 4, 5
uint32 mmap_length; // flag 6
uint32 mmap_addr;
uint32 drives_length; // flag 7
uint32 drives_addr;
uint32 config_table; // flag 8
uint32 boot_loader_name; // flag 9
uint32 apm_table; // flag 10
uint32 vbe_control_info; // flag 11
uint32 vbe_mode_info;
uint32 vbe_mode;
uint32 vbe_interface_seg;
uint32 vbe_interface_off;
uint32 vbe_interface_len;
};
typedef struct Mbmem Mbmem;
struct Mbmem
{
uint64 base;
uint64 length;
uint32 type;
};
typedef struct Mbmod Mbmod;
struct Mbmod
{
uint32 start;
uint32 end;
uint32 name;
};
typedef struct Page Page;
struct Page
{
Page *next; // only valid when page is on free list
};
#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "spinlock.h"
#include "condvar.h"
#include "queue.h"
#include "proc.h"
#include "x86.h"
#include "traps.h"
#include "xv6-kmtrace.h"
// Interrupt descriptor table (shared by all CPUs).
struct gatedesc idt[256];
extern uint vectors[]; // in vectors.S: array of 256 entry pointers
struct spinlock tickslock __attribute__ ((aligned (CACHELINE)));
struct condvar cv_ticks __attribute__ ((aligned (CACHELINE)));
uint ticks __attribute__ ((aligned (CACHELINE)));
void
tvinit(void)
{
int i;
for(i = 0; i < 256; i++)
SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0);
SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);
initlock(&tickslock, "time");
initcondvar(&cv_ticks, "time");
}
void
idtinit(void)
{
lidt(idt, sizeof(idt));
}
//PAGEBREAK: 41
void
trap(struct trapframe *tf)
{
if(tf->trapno == T_SYSCALL){
if(proc->killed)
exit();
proc->tf = tf;
syscall();
if(proc->killed)
exit();
return;
}
if (proc->mtrace_stacks.curr >= 0)
mtrace_kstack_pause(proc);
mtrace_kstack_start(trap, proc);
switch(tf->trapno){
case T_IRQ0 + IRQ_TIMER:
if(cpu->id == 0){
acquire(&tickslock);
ticks++;
cv_wakeup(&cv_ticks);
release(&tickslock);
}
lapiceoi();
break;
case T_IRQ0 + IRQ_IDE:
ideintr();
lapiceoi();
break;
case T_IRQ0 + IRQ_IDE+1:
// Bochs generates spurious IDE1 interrupts.
break;
case T_IRQ0 + IRQ_KBD:
kbdintr();
lapiceoi();
break;
case T_IRQ0 + IRQ_COM1:
uartintr();
lapiceoi();
break;
case T_IRQ0 + 7:
case T_IRQ0 + IRQ_SPURIOUS:
cprintf("cpu%d: spurious interrupt at %x:%x\n",
cpu->id, tf->cs, tf->eip);
lapiceoi();
case T_TLBFLUSH:
lapiceoi();
lcr3(rcr3());
break;
//PAGEBREAK: 13
default:
if(proc == 0 || (tf->cs&3) == 0){
// In kernel, it must be our mistake.
cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
tf->trapno, cpu->id, tf->eip, rcr2());
cprintf("eax %x\n", tf->eax);
cprintf("ebx %x\n", tf->ebx);
cprintf("ecx %x\n", tf->ecx);
cprintf("edx %x\n", tf->edx);
cprintf("edi %x\n", tf->edi);
cprintf("esi %x\n", tf->esi);
cprintf("esp %x\n", tf->esp);
cprintf("ebp %x\n", tf->ebp);
panic("trap");
}
if(tf->trapno == T_PGFLT){
if(pagefault(proc->vmap, rcr2(), tf->err) >= 0){
mtrace_kstack_stop(proc);
if (proc->mtrace_stacks.curr >= 0)
mtrace_kstack_resume(proc);
return;
}
}
// In user space, assume process misbehaved.
cprintf("pid %d %s: trap %d err %d on cpu %d "
"eip 0x%x addr 0x%x--kill proc\n",
proc->pid, proc->name, tf->trapno, tf->err, cpu->id, tf->eip,
rcr2());
proc->killed = 1;
}
// Force process exit if it has been killed and is in user space.
// (If it is still executing in the kernel, let it keep running
// until it gets to the regular system call return.)
if(proc && proc->killed && (tf->cs&3) == DPL_USER)
exit();
// Force process to give up CPU on clock tick.
// If interrupts were on while locks held, would need to check nlock.
if(proc && proc->state == RUNNING && tf->trapno == T_IRQ0+IRQ_TIMER)
yield();
// Check if the process has been killed since we yielded
if(proc && proc->killed && (tf->cs&3) == DPL_USER)
exit();
mtrace_kstack_stop(proc);
if (proc->mtrace_stacks.curr >= 0)
mtrace_kstack_resume(proc);
}
#include "vmx.h"
#include "amd64.h"
extern Segdesc bootgdt[NUMSEG];
// Bootstrap GDT. Used by boot.S but defined in C
// so we can use the data structure macros in amd64.h.
Segdesc bootgdt[NUMSEG] = {
SEGDESC(0, 0, 0), // null
SEGDESC(0, 0xfffff, SEG_R|SEG_CODE|SEG_S|SEG_DPL(0)|SEG_P|SEG_D|SEG_G), // 32-bit kernel code
SEGDESC(0, 0, SEG_R|SEG_CODE|SEG_S|SEG_DPL(0)|SEG_P|SEG_L|SEG_G), // 64-bit kernel code
SEGDESC(0, 0xfffff, SEG_W|SEG_S|SEG_DPL(0)|SEG_P|SEG_D|SEG_G), // kernel data
};
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef long int64;
typedef unsigned long uint64;
typedef unsigned char uint8;
typedef uint64 uintptr;
struct Mbdata;
void bootpanic(char*);
void initcga(void);
void initmem(struct Mbdata*);
void initpic(void);
void inittrap(void);
void initlapic(int);
int cpunum(void);
void lapiceoi(void);
void lapicstartap(uint8, uint32);
uint8 inb(uint16);
void outb(uint16, uint8);
void microdelay(int);
uint32 readeflags(void);
void setcursor(int);
void memmove(void *dst, void *src, int64 n);
int memcmp(const void*, const void*, int64);
void hlt(void);
void panic(const char*);
void puts(const char*);
void putsn(const char*, int);
int strlen(const char*);
void printf(const char*, ...);
void lidt(void*, int);
void ltr(int);
void lgdt(void*, int);
#define KBASE 0xffffff0000000000ull
#define KADDR(x) ((void*)(KBASE+(uintptr)(x)))
#define PADDR(x) ((uintptr)(x) - KBASE)
#define PGSIZE (2*1024*1024ull)
#define KCSEG (2<<3) /* kernel code segment */
#define KDSEG (3<<3) /* kernel data segment */
#define nil ((void*)0)
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论