#include "filesys/fsutil.h"
#endif
-/* Amount of physical memory, in 4 kB pages. */
-size_t ram_pages;
-
/* Page directory with kernel mappings only. */
uint32_t *base_page_dir;
argv_init ();
/* Initialize memory system. */
+ paging_init ();
palloc_init ();
malloc_init ();
- paging_init ();
/* Segmentation. */
#ifdef USERPROG
linker as _start_bss and _end_bss. See kernel.lds. */
extern char _start_bss, _end_bss;
memset (&_start_bss, 0, &_end_bss - &_start_bss);
-
- /* Get RAM size from loader. See loader.S. */
- ram_pages = *(uint32_t *) ptov (LOADER_RAM_PAGES);
}
/* Populates the base page directory and page table with the
directory it creates.
At the time this function is called, the active page table
- (set up by loader.S) only maps the first 4 MB of RAM, so we
- should not try to use extravagant amounts of memory.
+ (set up by start.S) only maps the first 4 MB of RAM, so we
+ should not try to access memory beyond that limit.
Fortunately, there is no need to do so. */
static void
paging_init (void)
uint32_t *pd, *pt;
size_t page;
- pd = base_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
- pt = NULL;
+ pd = base_page_dir = ptov (LOADER_PD_BASE);
+ pt = ptov (LOADER_PT_BASE);
for (page = 0; page < ram_pages; page++)
{
uintptr_t paddr = page * PGSIZE;
if (pd[pde_idx] == 0)
{
- pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
+ pt += PGSIZE / sizeof *pt;
+ memset (pt, 0, PGSIZE);
pd[pde_idx] = pde_create (pt);
}
pt[pte_idx] = pte_create_kernel (vaddr, true);
}
+ /* start.S mapped the beginning of physical memory to virtual
+ address 0. We don't want that mapping anymore, so erase
+ it. */
+ pd[0] = 0;
+
/* Store the physical address of the page directory into CR3
- aka PDBR (page directory base register). This activates our
- new page tables immediately. See [IA32-v2a] "MOV--Move
+ aka PDBR (page directory base register). This flushes the
+ TLB to make sure . See [IA32-v2a] "MOV--Move
to/from Control Registers" and [IA32-v3] 3.7.5. */
asm volatile ("mov %%cr3, %0" :: "r" (vtop (base_page_dir)));
}
#ifndef THREADS_LOADER_H
#define THREADS_LOADER_H
-/* Constants fixed by the PC BIOS. */
-#define LOADER_BASE 0x7c00 /* Physical address of loader's base. */
-#define LOADER_END 0x7e00 /* Physical address of end of loader. */
-
-/* Physical address of kernel base. */
-#define LOADER_KERN_BASE 0x100000 /* 1 MB. */
+/* Physical addresses of important kernel components. */
+#define LOADER_PD_BASE 0x0f000 /* Page directory: 4 kB starting at 60 kB. */
+#define LOADER_PT_BASE 0x10000 /* Page tables: 64 kB starting at 64 kB. */
+#define LOADER_KERN_BASE 0x20000 /* Kernel: up to 512 kB starting at 128 kB. */
/* Kernel virtual address at which all physical memory is mapped.
This must be aligned on a 4 MB boundary. */
#define LOADER_PHYS_BASE 0xc0000000 /* 3 GB. */
-/* Offsets within the loader. */
-#define LOADER_BIOS_SIG (LOADER_END - 2) /* 0xaa55 BIOS signature. */
-#define LOADER_CMD_LINE_LEN 0x80 /* Command line length. */
-#define LOADER_CMD_LINE (LOADER_BIOS_SIG - LOADER_CMD_LINE_LEN)
- /* Kernel command line. */
-#define LOADER_RAM_PAGES (LOADER_CMD_LINE - 4) /* # of pages of RAM. */
-
/* GDT selectors defined by loader.
More selectors are defined by userprog/gdt.h. */
#define SEL_NULL 0x00 /* Null selector. */
#### so that this module appears at the very beginning of the kernel
#### image, and then using that as the entry point.
+#include "threads/loader.h"
+ .intel_syntax noprefix
+
.section .start
+# Code runs in real mode, which is a 16-bit segment.
+ .code16
+
.globl start
-.func start
- # Call main.
-start: call main
+start:
+
+# Disable interrupts.
+# String instructions go upward.
+
+ cli
+ cld
+
+# Set up data segments.
+
+ sub ax, ax
+ mov ds, ax
+ mov es, ax
+
+#### Enable A20. Address line 20 is tied to low when the machine
+#### boots, which prevents addressing memory about 1 MB. This code
+#### fixes it.
+
+# Poll status register while busy.
+
+1: in al, 0x64
+ test al, 0x2
+ jnz 1b
+
+# Send command for writing output port.
+
+ mov al, 0xd1
+ outb 0x64, al
+
+# Poll status register while busy.
+
+1: in al, 0x64
+ test al, 0x2
+ jnz 1b
+
+# Enable A20 line.
+
+ mov al, 0xdf
+ out 0x60, al
+
+#### Get memory size, via interrupt 15h function 88h, which returns CF
+#### clear if successful, with AX = (kB of physical memory) - 1024.
+#### This only works for memory sizes <= 65 MB, which should be fine
+#### for our purposes. We only reserve enough memory for page tables
+#### for 64 MB of RAM, so we cap it at that value.
+
+ mov ah, 0x88
+ int 0x15
+ jc panic
+ cli # BIOS might have enabled interrupts
+ add eax, 1024 # Total kB memory
+ cmp eax, 64 * 1024
+ jbe 1f
+ mov eax, 64 * 1024
+1: shr eax, 2 # Total 4 kB pages
+ mov [ram_pages], eax
+
+#### Create temporary page directory and page table and set page
+#### directory base register.
+
+# Create page directory at 64 kB and fill with zeroes.
+ mov ax, LOADER_PD_BASE / 16
+ mov es, ax
+ sub eax, eax
+ sub edi, edi
+ mov ecx, 0x400
+ rep stosd
+
+# Add a PDE mapping both virtual addresses 0 and LOADER_PHYS_BASE
+# to a single PTE.
+# See [IA32-v3] section 3.7.6 for a description of the bits in eax.
+
+ mov eax, LOADER_PT_BASE / 16 + 7
+ mov es:[0], eax
+ mov es:[LOADER_PHYS_BASE / 1024 / 1024], eax
+
+# Set up one-to-map linear to physical map for the first 4 MB of RAM.
+# See [IA32-v3] section 3.7.6 for a description of the bits in eax.
- # main() should not return, but if it does, spin.
+ mov ax, LOADER_PT_BASE / 16
+ mov es, ax
+ mov eax, 0x7
+ mov cx, 0x400
+1: stosd
+ add eax, 0x1000
+ loop 1b
+
+# Set page directory base register.
+
+ mov eax, LOADER_PD_BASE
+ mov cr3, eax
+
+#### Switch to protected mode.
+
+# Then we point the GDTR to our GDT. Protected mode requires a GDT.
+# We need a data32 prefix to ensure that all 32 bits of the GDT
+# descriptor are loaded (default is to load only 24 bits).
+
+ data32 lgdt gdtdesc
+
+# Then we turn on the following bits in CR0:
+# PE (Protect Enable): this turns on protected mode.
+# PG (Paging): turns on paging.
+# WP (Write Protect): if unset, ring 0 code ignores
+# write-protect bits in page tables (!).
+# EM (Emulation): forces floating-point instructions to trap.
+# We don't support floating point.
+
+#define CR0_PE 0x00000001
+#define CR0_EM 0x00000004
+#define CR0_PG 0x80000000
+#define CR0_WP 0x00010000
+
+ mov eax, cr0
+ or eax, CR0_PE + CR0_PG + CR0_WP + CR0_EM
+ mov cr0, eax
+
+# We're now in protected mode in a 16-bit segment. The CPU still has
+# the real-mode code segment cached in cs's segment descriptor. We
+# need to reload cs, and the easiest way is to use a far jump.
+# Because we're not in a 32-bit segment the data32 prefix is needed to
+# jump to a 32-bit offset.
+
+ data32 ljmp SEL_KCSEG, 1f + LOADER_PHYS_BASE
+
+# We're now in protected mode in a 32-bit segment.
+
+ .code32
+
+# Reload all the other segment registers and the stack pointer to
+# point into our new GDT.
+
+1: mov ax, SEL_KDSEG
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+ add esp, LOADER_PHYS_BASE
+
+#### Jump to kernel entry point.
+
+ mov eax, main
+ call eax
1: jmp 1b
-.endfunc
+
+#### GDT
+
+gdt:
+ .quad 0x0000000000000000 # null seg
+ .quad 0x00cf9a000000ffff # code seg
+ .quad 0x00cf92000000ffff # data seg
+
+gdtdesc:
+ .word 0x17 # sizeof (gdt) - 1
+ .long gdt + LOADER_PHYS_BASE # address gdt
+
+#### Fatal error.
+#### Print panicmsg (with help from the BIOS) and spin.
+
+panic: .code16 # We only panic in real mode.
+ mov si, offset panicmsg
+ mov ah, 0xe
+ sub bh, bh
+1: lodsb
+ test al, al
+2: jz 2b # Spin.
+ int 0x10
+ jmp 1b
+
+panicmsg:
+ .ascii "Panic!"
+ .byte 0