sj95126 wrote:
I would strongly recommend not just dropping in example code as a starting point. It's good as a reference, but paging is so fundamental that you have to understand it thoroughly.
I just wanted to see what the end result should like. But apparently this "good reference" doesn't actually work...
sj95126 wrote:
You'll want to do a lot of breakpoints and single-stepping to manually examine your page table in memory before and after enabling it.
I'm doing it right now, trying to understand where it triple-faults. Here's the code and its disassembly:
Code:
section .multiboot.text
global _start:function
_start:
cli
.gdt_setup:
lgdt [gdt_descriptor]
jmp 0x08:.clear_pipeline
.clear_pipeline:
mov ax, 0x10 ; load index 2 of GDT
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Now we're using the flat memory model set up via GDT
.setup_paging:
; Physical address of boot_page_table1.
mov edi, boot_page_table1 - 0xC0000000
; First address to map is address 0.
mov esi, 0
; Map 1023 pages. The 1024th will be the VGA text buffer.
mov ecx, 1023
._1:
; Only map the kernel.
cmp esi, (_kernel_start - 0xC0000000)
jl ._2
cmp esi, (_kernel_end - 0xC0000000)
jge ._3
; Map physical address as "present, writable"
mov edx, esi
or edx, 0x003
mov [edi], edx
._2:
add esi, 4096 ; Size of page is 4096 bytes.
add edi, 4 ; Size of entries in boot_page_table1 is 4 bytes.
loop ._1
._3:
; Map VGA video memory to 0xC03FF000 as "present, writable".
mov DWORD [boot_page_table1 - 0xC0000000 + 1023 * 4], (0x000B8000 | 0x003)
; Map the page table to both virtual addresses 0x00000000 and 0xC0000000.
mov DWORD [boot_page_directory - 0xC0000000 + 0], (boot_page_table1 - 0xC0000000 + 0x003)
mov DWORD [boot_page_directory - 0xC0000000 + 768 * 4], (boot_page_table1 - 0xC0000000 + 0x003)
; Set cr3 to the address of the boot_page_directory.
mov ecx, (boot_page_directory - 0xC0000000)
mov cr3, ecx
; Enable paging and the write-protect bit.
mov ecx, cr0
or ecx, 0x80010000
mov cr0, ecx
; Jump to higher half with an absolute jump.
lea ecx, [_4]
jmp ecx
.end:
Code:
0x10002a cli
0x10002b lgdtl 0x100024
0x100032 ljmp $0x8,$0x100039
0x100039 mov $0x10,%ax
0x10003d mov %eax,%ds
0x10003f mov %eax,%es
0x100041 mov %eax,%fs
0x100043 mov %eax,%gs
0x100045 mov %eax,%ss
0x100047 mov $0x104000,%edi
0x10004c mov $0x0,%esi
0x100051 mov $0x3ff,%ecx
0x100056 cmp $0x1000b2,%esi
0x10005c jl 0x10006d
0x10005e cmp $0x109000,%esi
0x100064 jge 0x100078
0x100066 mov %esi,%edx
0x100068 or $0x3,%edx
0x10006b mov %edx,(%edi)
0x10006d add $0x1000,%esi
0x100073 add $0x4,%edi
0x100076 loop 0x100056
// _start._3:
B+ 0x100078 movl $0xb8003,0x104ffc
0x100082 movl $0x104003,0x103000
0x10008c movl $0x104003,0x103c00
0x100096 mov $0x103000,%ecx
0x10009b mov %ecx,%cr3
0x10009e mov %cr0,%ecx
0x1000a1 or $0x80010000,%ecx
>0x1000a7 mov %ecx,%cr0
0x1000aa lea 0xc0101000,%ecx ; lea ecx, [_4]
0x1000b0 jmp *%ecx
So I put a breakpoint at label "_start._3", after writing the page tables and stuff:
Code:
(gdb) b *0x00100078
Breakpoint 2 at 0x100078
(gdb) c
Continuing.
Breakpoint 2, 0x00100078 in _start._3 ()
(gdb) si
0x00100082 in _start._3 ()
0x0010008c in _start._3 ()
0x00100096 in _start._3 ()
0x0010009b in _start._3 ()
0x0010009e in _start._3 ()
0x001000a1 in _start._3 ()
0x001000a7 in _start._3 ()
0x001000aa in _start._3 ()
Cannot access memory at address 0x100078
(gdb) libc++abi.dylib: terminating with uncaught exception of type gdb_exception_error
fish: 'gdb kernel.elf' terminated by signal SIGABRT (Abort)
So apparently "lea ecx, [_4]" (aka "lea 0xc0101000,%ecx") attempts to access address 0x100078???? Given that 0x00100078 is the address of the breakpoint, I'd say GDB is getting confused and printing nonsense. But there
is an invalid memory access at this instruction anyway.