forked from mit-pdos/xv6-public
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
rtm
committed
Jun 12, 2006
0 parents
commit 55e95b1
Showing
18 changed files
with
1,505 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
OBJS = main.o console.o string.o kalloc.o proc.o trapasm.o | ||
|
||
CC = i386-jos-elf-gcc | ||
LD = i386-jos-elf-ld | ||
OBJCOPY = i386-jos-elf-objcopy | ||
OBJDUMP = i386-jos-elf-objdump | ||
|
||
xv6.img : bootblock kernel | ||
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 | ||
|
||
bootblock : bootasm.S bootmain.c | ||
$(CC) -O -nostdinc -I. -c bootmain.c | ||
$(CC) -nostdinc -I. -c bootasm.S | ||
$(LD) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o | ||
$(OBJDUMP) -S bootblock.o > bootblock.asm | ||
$(OBJCOPY) -S -O binary bootblock.o bootblock | ||
./sign.pl bootblock | ||
|
||
kernel : $(OBJS) | ||
$(LD) -Ttext 0x100000 -e main -o kernel $(OBJS) | ||
$(OBJDUMP) -S kernel > kernel.asm | ||
|
||
%.o: %.c | ||
$(CC) -nostdinc -I. -O -c -o $@ $< | ||
|
||
clean : | ||
rm -f bootmain.o bootasm.o bootblock.o bootblock | ||
rm -f kernel main.o kernel.asm xv6.img |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
bootmain.c doesn't work right if the ELF sections aren't | ||
sector-aligned. so you can't use ld -N. and the sections may also need | ||
to be non-zero length, only really matters for tiny "kernels". | ||
|
||
kernel loaded at 1 megabyte. stack same place that bootasm.S left it. | ||
|
||
kinit() should find real mem size | ||
and rescue useable memory below 1 meg | ||
|
||
no paging, no use of page table hardware, just segments | ||
|
||
no user area: no magic kernel stack mapping | ||
so no copying of kernel stack during fork | ||
though there is a kernel stack page for each process | ||
|
||
no kernel malloc(), just kalloc() for user core | ||
|
||
user pointers aren't valid in the kernel | ||
|
||
setting up first process | ||
we do want a process zero, as template | ||
but not runnable | ||
just set up return-from-trap frame on new kernel stack | ||
fake user program that calls exec | ||
|
||
map text read-only? | ||
shared text? | ||
|
||
what's on the stack during a trap or sys call? | ||
PUSHA before scheduler switch? for callee-saved registers. | ||
segment contents? | ||
what does iret need to get out of the kernel? | ||
how does INT know what kernel stack to use? | ||
|
||
are interrupts turned on in the kernel? probably. | ||
|
||
per-cpu curproc | ||
one tss per process, or one per cpu? | ||
one segment array per cpu, or per process? | ||
|
||
pass curproc explicitly, or implicit from cpu #? | ||
e.g. argument to newproc()? | ||
|
||
test stack expansion | ||
test running out of memory, process slots | ||
|
||
we can't really use a separate stack segment, since stack addresses | ||
need to work correctly as ordinary pointers. the same may be true of | ||
data vs text. how can we have a gap between data and stack, so that | ||
both can grow, without committing 4GB of physical memory? does this | ||
mean we need paging? | ||
|
||
what's the simplest way to add the paging we need? | ||
one page table, re-write it each time we leave the kernel? | ||
page table per process? | ||
probably need to use 0-0xffffffff segments, so that | ||
both data and stack pointers always work | ||
so is it now worth it to make a process's phys mem contiguous? | ||
or could use segment limits and 4 meg pages? | ||
but limits would prevent using stack pointers as data pointers | ||
how to write-protect text? not important? | ||
|
||
perhaps have fixed-size stack, put it in the data segment? | ||
|
||
oops, if kernel stack is in contiguous user phys mem, then moving | ||
users' memory (e.g. to expand it) will wreck any pointers into the | ||
kernel stack. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
#define SEG_NULL \ | ||
.word 0, 0; \ | ||
.byte 0, 0, 0, 0 | ||
#define SEG(type,base,lim) \ | ||
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \ | ||
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \ | ||
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff) | ||
|
||
#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 | ||
|
||
.set PROT_MODE_CSEG,0x8 # code segment selector | ||
.set PROT_MODE_DSEG,0x10 # data segment selector | ||
.set CR0_PE_ON,0x1 # protected mode enable flag | ||
|
||
################################################################################### | ||
# ENTRY POINT | ||
# This code should be stored in the first sector of the hard disk. | ||
# After the BIOS initializes the hardware on startup or system reset, | ||
# it loads this code at physical address 0x7c00 - 0x7d00 (512 bytes). | ||
# Then the BIOS jumps to the beginning of it, address 0x7c00, | ||
# while running in 16-bit real-mode (8086 compatibility mode). | ||
# The Code Segment register (CS) is initially zero on entry. | ||
# | ||
# This code switches into 32-bit protected mode so that all of | ||
# memory can accessed, then calls into C. | ||
################################################################################### | ||
|
||
.globl start # Entry point | ||
start: .code16 # This runs in real 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 | ||
|
||
# Set up the stack pointer, growing downward from 0x7c00. | ||
movw $start,%sp # Stack Pointer | ||
|
||
#### Enable A20: | ||
#### For fascinating historical reasons (related to the fact that | ||
#### the earliest 8086-based PCs could only address 1MB of physical memory | ||
#### and subsequent 80286-based PCs wanted to retain maximum compatibility), | ||
#### physical address line 20 is tied to low when the machine boots. | ||
#### Obviously this a bit of a drag for us, especially when trying to | ||
#### address memory above 1MB. This code undoes this. | ||
|
||
seta20.1: inb $0x64,%al # Get status | ||
testb $0x2,%al # Busy? | ||
jnz seta20.1 # Yes | ||
movb $0xd1,%al # Command: Write | ||
outb %al,$0x64 # output port | ||
seta20.2: inb $0x64,%al # Get status | ||
testb $0x2,%al # Busy? | ||
jnz seta20.2 # Yes | ||
movb $0xdf,%al # Enable | ||
outb %al,$0x60 # A20 | ||
|
||
#### Switch from real to protected mode | ||
#### The descriptors in our GDT allow all physical memory to be accessed. | ||
#### Furthermore, the descriptors have base addresses of 0, so that the | ||
#### segment translation is a NOP, ie. virtual addresses are identical to | ||
#### their physical addresses. With this setup, immediately after | ||
#### enabling protected mode it will still appear to this code | ||
#### that it is running directly on physical memory with no translation. | ||
#### This initial NOP-translation setup is required by the processor | ||
#### to ensure that the transition to protected mode occurs smoothly. | ||
|
||
real_to_prot: cli # Mandatory since we dont set up an IDT | ||
lgdt gdtdesc # load GDT -- mandatory in protected mode | ||
movl %cr0, %eax # turn on protected mode | ||
orl $CR0_PE_ON, %eax # | ||
movl %eax, %cr0 # | ||
### CPU magic: jump to relocation, flush prefetch queue, and reload %cs | ||
### Has the effect of just jmp to the next instruction, but simultaneous | ||
### loads CS with $PROT_MODE_CSEG. | ||
ljmp $PROT_MODE_CSEG, $protcseg | ||
|
||
#### we are in 32-bit protected mode (hence the .code32) | ||
.code32 | ||
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 | ||
|
||
call cmain # finish the boot load from C. | ||
# cmain() should not return | ||
spin: jmp spin # ..but in case it does, spin | ||
|
||
.p2align 2 # force 4 byte alignment | ||
gdt: | ||
SEG_NULL # null seg | ||
SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg | ||
SEG(STA_W, 0x0, 0xffffffff) # data seg | ||
|
||
gdtdesc: | ||
.word 0x17 # sizeof(gdt) - 1 | ||
.long gdt # address gdt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
#include <types.h> | ||
#include <elf.h> | ||
#include <x86.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(boot.S and main.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 bootloader.S -- which sets up protected mode, | ||
* and a stack so C code then run, then calls cmain() | ||
* | ||
* * cmain() in this file takes over, reads in the kernel and jumps to it. | ||
**********************************************************************/ | ||
|
||
#define SECTSIZE 512 | ||
#define ELFHDR ((struct Elf *) 0x10000) // scratch space | ||
|
||
void readsect(void*, uint32_t); | ||
void readseg(uint32_t, uint32_t, uint32_t); | ||
|
||
void | ||
cmain(void) | ||
{ | ||
struct Proghdr *ph, *eph; | ||
|
||
// read 1st page off disk | ||
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0); | ||
|
||
// is this a valid ELF? | ||
if (ELFHDR->e_magic != ELF_MAGIC) | ||
goto bad; | ||
|
||
// load each program segment (ignores ph flags) | ||
ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff); | ||
eph = ph + ELFHDR->e_phnum; | ||
for (; ph < eph; ph++) | ||
readseg(ph->p_va, 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); | ||
while (1) | ||
/* do nothing */; | ||
} | ||
|
||
// Read 'count' bytes at 'offset' from kernel into virtual address 'va'. | ||
// Might copy more than asked | ||
void | ||
readseg(uint32_t va, uint32_t count, uint32_t offset) | ||
{ | ||
uint32_t end_va; | ||
|
||
va &= 0xFFFFFF; | ||
end_va = va + count; | ||
|
||
// round down to sector boundary | ||
va &= ~(SECTSIZE - 1); | ||
|
||
// translate from bytes to sectors, and kernel starts at sector 1 | ||
offset = (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. | ||
while (va < end_va) { | ||
readsect((uint8_t*) va, offset); | ||
va += SECTSIZE; | ||
offset++; | ||
} | ||
} | ||
|
||
void | ||
waitdisk(void) | ||
{ | ||
// wait for disk reaady | ||
while ((inb(0x1F7) & 0xC0) != 0x40) | ||
/* do nothing */; | ||
} | ||
|
||
void | ||
readsect(void *dst, uint32_t offset) | ||
{ | ||
// wait for disk to be ready | ||
waitdisk(); | ||
|
||
outb(0x1F2, 1); // count = 1 | ||
outb(0x1F3, offset); | ||
outb(0x1F4, offset >> 8); | ||
outb(0x1F5, offset >> 16); | ||
outb(0x1F6, (offset >> 24) | 0xE0); | ||
outb(0x1F7, 0x20); // cmd 0x20 - read sectors | ||
|
||
// wait for disk to be ready | ||
waitdisk(); | ||
|
||
// read a sector | ||
insl(0x1F0, dst, SECTSIZE/4); | ||
} | ||
|
Oops, something went wrong.