Page fault handler works!
This commit is contained in:
10
README.md
10
README.md
@@ -44,12 +44,14 @@ Minimal operating systems to learn low level programming.
|
||||
1. [in beep_illinois](in_beep_illinois.S)
|
||||
1. [in mouse (TODO)](in_mouse.S)
|
||||
1. [Protected mode](protected_mode.S)
|
||||
1. [Segment base (TODO)](segment_base.S)
|
||||
1. [IDT](idt.S)
|
||||
1. [Segment base (TODO)](segment_base.S)
|
||||
1. [IDT](idt.S)
|
||||
1. [IDT 1](idt1.S)
|
||||
1. [IDT zero divide](idt_zero_divide.S)
|
||||
1. IDT PIT
|
||||
1. Segmentation fault handler: memory bound, ring, RWX violations
|
||||
1. [Paging](paging.S)
|
||||
1. Segmentation fault handler: memory bound, ring, RWX violations
|
||||
1. [Paging](paging.S)
|
||||
1. [Page fault](page-fault.S)
|
||||
1. APM
|
||||
1. [APM shutdown](apm_shutdown.S)
|
||||
1. [APM shutdown 2](apm_shutdown2.S)
|
||||
|
||||
19
TODO.md
19
TODO.md
@@ -32,6 +32,15 @@
|
||||
|
||||
- http://stackoverflow.com/questions/7924031/how-prompt-is-x86-at-setting-the-page-dirty-bit/7926931#7926931
|
||||
|
||||
Page fault:
|
||||
|
||||
- http://stackoverflow.com/questions/5684365/what-causes-page-faults/5690636#5690636
|
||||
- http://stackoverflow.com/questions/23899567/distinguishing-between-code-and-data-pages-on-x86-64-mmu/23900691#23900691
|
||||
- http://stackoverflow.com/questions/12607288/what-happens-when-a-mov-instruction-causes-a-page-fault-with-interrupts-disabled
|
||||
- http://stackoverflow.com/questions/21211942/x86-page-fault-description
|
||||
- http://stackoverflow.com/questions/15275059/whats-the-purpose-of-x86-cr0-wp-bit
|
||||
- http://stackoverflow.com/questions/21820814/what-happens-with-a-processor-when-it-tries-to-access-a-nonexistent-address
|
||||
|
||||
- Segment registers /segmentation and protected mode. Then try to answer all of: GDT
|
||||
|
||||
- http://reverseengineering.stackexchange.com/questions/2006/how-are-the-segment-registers-fs-gs-cs-ss-ds-es-used-in-linux
|
||||
@@ -58,6 +67,12 @@
|
||||
- http://stackoverflow.com/questions/9249315/what-is-gs-in-assembly?rq=1
|
||||
- http://stackoverflow.com/questions/928082/why-does-the-mov-instruction-have-to-be-used-this-way?lq=1
|
||||
|
||||
Segfault:
|
||||
|
||||
- http://stackoverflow.com/questions/6950549/segmentation-fault-vs-page-fault
|
||||
- http://stackoverflow.com/questions/10948930/page-fault-and-segmentation-fault
|
||||
- http://stackoverflow.com/questions/10948930/page-fault-and-segmentation-fault
|
||||
|
||||
64-bit:
|
||||
|
||||
- http://stackoverflow.com/questions/22962251/how-to-enter-64-bit-mode-on-a-x86-64/22963701#22963701
|
||||
@@ -126,3 +141,7 @@
|
||||
|
||||
- http://stackoverflow.com/questions/15322892/linux-usb-mouse-drivers
|
||||
- http://stackoverflow.com/questions/25175960/which-drivers-are-used-by-usb-mouse-in-linux-kernel
|
||||
|
||||
- control registers CRX
|
||||
|
||||
- why CR1 does not exist, but CR8 does http://www.pagetable.com/?p=364
|
||||
|
||||
138
common.h
138
common.h
@@ -16,7 +16,7 @@ But the downsides are severe:
|
||||
|
||||
## Conventions
|
||||
|
||||
Every "function-like macro" should maintain register state
|
||||
Every "function-like macro" should maintain GP register state
|
||||
(flags currently not maintained).
|
||||
|
||||
%sp cannot be used to pass most arguments.
|
||||
@@ -240,6 +240,76 @@ protected_mode:
|
||||
mov %ebp, %esp
|
||||
.endm
|
||||
|
||||
/*
|
||||
Setup a single page directory, which give us 2^10 * 2^12 == 4MiB
|
||||
of identity memory starting at address 0.
|
||||
The currently executing code is inside that range, or else we'd jump somewhere and die.
|
||||
*/
|
||||
.equ page_directory, __end_align_4k
|
||||
.equ page_table, __end_align_4k + 0x1000
|
||||
.macro SETUP_PAGING_4M
|
||||
LOCAL page_setup_start page_setup_end
|
||||
PUSH_EADX
|
||||
|
||||
/* Page directory steup. */
|
||||
/* Set the top 20 address bits. */
|
||||
mov $page_table, %eax
|
||||
/* Zero out the 4 low flag bits of the second byte (top 20 are address). */
|
||||
and $0xF000, %ax
|
||||
mov %eax, page_directory
|
||||
/* Set flags for the first byte. */
|
||||
mov $0b00100111, %al
|
||||
mov %al, page_directory
|
||||
|
||||
/* Page table setup. */
|
||||
mov $0, %eax
|
||||
mov $page_table, %ebx
|
||||
page_setup_start:
|
||||
cmp $0x400, %eax
|
||||
je page_setup_end
|
||||
/* Top 20 address bits. */
|
||||
mov %eax, %edx
|
||||
shl $12, %edx
|
||||
/*
|
||||
Set flag bits 0-7. We only set to 1:
|
||||
- bit 0: Page present
|
||||
|
||||
- bit 1: Page is writable.
|
||||
Might work without this as the permission also depends on CR0.WP.
|
||||
*/
|
||||
mov $0b00000011, %dl
|
||||
/* Zero flag bits 8-11 */
|
||||
and $0xF0, %dh
|
||||
mov %edx, (%ebx)
|
||||
inc %eax
|
||||
add $4, %ebx
|
||||
jmp page_setup_start
|
||||
page_setup_end:
|
||||
POP_EDAX
|
||||
.endm
|
||||
|
||||
/*
|
||||
Turn paging on.
|
||||
Registers are not saved because memory will be all messed up.
|
||||
*/
|
||||
.macro PAGING_ON
|
||||
/* Tell the CPU where the page directory is. */
|
||||
mov $page_directory, %eax
|
||||
mov %eax, %cr3
|
||||
|
||||
/* Turn paging on. */
|
||||
mov %cr0, %eax
|
||||
or $0x80000000, %eax
|
||||
mov %eax, %cr0
|
||||
.endm
|
||||
|
||||
/* Turn paging off. */
|
||||
.macro PAGING_OFF
|
||||
mov %cr0, %eax
|
||||
and $0x7FFFFFFF, %eax
|
||||
mov %eax, %cr0
|
||||
.endm
|
||||
|
||||
/* IDT */
|
||||
|
||||
.macro IDT_START
|
||||
@@ -255,7 +325,8 @@ idt_descriptor:
|
||||
.endm
|
||||
|
||||
.macro IDT_ENTRY
|
||||
/* Low handler address.
|
||||
/*
|
||||
Low handler address.
|
||||
It is impossible to write:
|
||||
.word (handler & 0x0000FFFF)
|
||||
as we would like:
|
||||
@@ -280,17 +351,25 @@ idt_descriptor:
|
||||
.word 0
|
||||
.endm
|
||||
|
||||
/* Skip n IDT entries, usually to set the Nth one next. */
|
||||
.macro IDT_SKIP n=1
|
||||
.skip n * 8
|
||||
.endm
|
||||
|
||||
/*
|
||||
- index: r/m/imm32 Index of the entry to setup.
|
||||
- handler: r/m/imm32 Address of the handler function.
|
||||
*/
|
||||
.macro IDT_SETUP_ENTRY index, handler
|
||||
push %eax
|
||||
push %edx
|
||||
mov \index, %eax
|
||||
mov \handler, %ebx
|
||||
mov %bx, idt_start(%eax, 8)
|
||||
shr $16, %ebx
|
||||
mov $6, %ecx
|
||||
mov %bx, idt_start(%ecx, %eax, 8)
|
||||
mov \handler, %edx
|
||||
mov %dx, idt_start(,%eax, 8)
|
||||
shr $16, %edx
|
||||
mov %dx, (idt_start + 6)(,%eax, 8)
|
||||
pop %edx
|
||||
pop %eax
|
||||
.endm
|
||||
|
||||
/* BIOS */
|
||||
@@ -471,18 +550,18 @@ end:
|
||||
.endm
|
||||
|
||||
/*
|
||||
Print a 32-bit register in hex.
|
||||
Print a 32-bit r/m/immm in hex.
|
||||
|
||||
Sample usage:
|
||||
|
||||
mov $12345678, %eax
|
||||
VGA_PRINT_HEX <%eax>
|
||||
VGA_PRINT_HEX_4 <%eax>
|
||||
|
||||
Expected output on screen:
|
||||
|
||||
12345678
|
||||
*/
|
||||
.macro VGA_PRINT_HEX reg=<%eax>
|
||||
.macro VGA_PRINT_HEX_4 reg=<%eax>
|
||||
LOCAL loop
|
||||
PUSH_EADX
|
||||
/* Null terminator. */
|
||||
@@ -511,3 +590,42 @@ loop:
|
||||
add $12, %esp
|
||||
POP_EDAX
|
||||
.endm
|
||||
|
||||
/*
|
||||
Dump memory.
|
||||
|
||||
- s: starting address
|
||||
- n: number of bytes to dump
|
||||
|
||||
TODO implement
|
||||
*/
|
||||
.macro VGA_PRINT_BYTES s, n=$16
|
||||
LOCAL end, loop, no_newline
|
||||
PUSH_ADX
|
||||
push %edi
|
||||
mov s, %esi
|
||||
mov \n, %ecx
|
||||
mov $0, %edi
|
||||
cld
|
||||
loop:
|
||||
cmp $0, %ecx
|
||||
je end
|
||||
dec %ecx
|
||||
lodsb
|
||||
PRINT_HEX
|
||||
PUTC
|
||||
/* Print a newline for every 8 bytes. */
|
||||
mov $0, %edx
|
||||
mov %di, %eax
|
||||
mov $8, %ebx
|
||||
div %ebx
|
||||
cmp $7, %edx
|
||||
jne no_newline
|
||||
/*VGA_PRINT_NEWLINE*/
|
||||
no_newline:
|
||||
inc %edi
|
||||
jmp loop
|
||||
end:
|
||||
pop %di
|
||||
POP_DAX
|
||||
.endm
|
||||
|
||||
14
idt.S
14
idt.S
@@ -3,16 +3,20 @@
|
||||
|
||||
# Interrupt Descriptor Table
|
||||
|
||||
Expected output: "a"
|
||||
Expected output: "int 0 handled"
|
||||
|
||||
The first 32 handlers are reserved by the processor and have predefined meanings.
|
||||
|
||||
TODO on Linux kernel.
|
||||
The first 32 handlers are reserved by the processor and have predefined meanings, as specified in:
|
||||
https://web.archive.org/web/20151025081259/http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-system-programming-manual-325384.pdf
|
||||
Table 3-3. "Intel 64 and IA-32 General Exceptions".
|
||||
|
||||
## lidt
|
||||
|
||||
Analogous to lgdt but for the IDT.
|
||||
|
||||
## Linux kernel usage
|
||||
|
||||
TODO
|
||||
|
||||
## Bibliography
|
||||
|
||||
- http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html
|
||||
@@ -37,4 +41,4 @@ handler:
|
||||
VGA_PRINT_STRING $message
|
||||
iret
|
||||
message:
|
||||
.asciz "a"
|
||||
.asciz "int 0 handled"
|
||||
|
||||
27
idt1.S
Normal file
27
idt1.S
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
# IDT 1
|
||||
|
||||
Sanity check that we can also handle int 1 besides just int 0.
|
||||
|
||||
Expected output: "int 1 handled"
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
BEGIN
|
||||
STAGE2
|
||||
CLEAR
|
||||
PROTECTED_MODE
|
||||
IDT_SETUP_ENTRY $1, $handler
|
||||
lidt idt_descriptor
|
||||
int $1
|
||||
jmp .
|
||||
IDT_START
|
||||
IDT_SKIP 1
|
||||
IDT_ENTRY
|
||||
IDT_END
|
||||
handler:
|
||||
VGA_PRINT_STRING $message
|
||||
iret
|
||||
message:
|
||||
.asciz "int 1 handled"
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
Division by zero causes a Divide Error which Intel notes as `#DE`.
|
||||
|
||||
Expected output: "a"
|
||||
Expected output: "division by zero handled"
|
||||
|
||||
It is then handled by IDT 0.
|
||||
|
||||
@@ -36,4 +36,4 @@ handler:
|
||||
mov $1, %ecx
|
||||
iret
|
||||
message:
|
||||
.asciz "a"
|
||||
.asciz "division by zero handled"
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
/*
|
||||
# Initial state.
|
||||
|
||||
Check the initial state the firmware leaves us in.
|
||||
|
||||
Could be done with GDB on the emulator, but this will also work on real hardware.
|
||||
|
||||
## ax
|
||||
|
||||
When I don't use ax to zero ds in the initialization,
|
||||
it has value 0x55AA, which is the magic bytes.
|
||||
|
||||
Is that mandatory / does it have a function?
|
||||
|
||||
## dx
|
||||
|
||||
This looks like the only interesting regular register:
|
||||
the firmware stores the value of the current disk number (to help with int 0x15) there.
|
||||
Thus it usually contains 0x80.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
@@ -11,30 +26,61 @@ Could be done with GDB on the emulator, but this will also work on real hardware
|
||||
.endm
|
||||
|
||||
.macro INITIAL_DATA x
|
||||
\x: .word 0
|
||||
\x: .skip 2
|
||||
\x\()s: .ascii "\x = \0"
|
||||
.endm
|
||||
|
||||
.macro INITIAL_PRINT x
|
||||
PRINT $\x\()s
|
||||
PRINT_HEX <\x>
|
||||
PRINT_BYTES $\x, $2
|
||||
PRINT_NEWLINE
|
||||
.endm
|
||||
|
||||
.irp reg, bx, cx, dx, cs, ds, es, fs, gs, ss
|
||||
INITIAL_STORE \reg
|
||||
.endr
|
||||
/*
|
||||
Indispensable initialization.
|
||||
This initialization is a bit redundant with BEGIN,
|
||||
and does dirty some registers, but I haven't found a better option.
|
||||
*/
|
||||
.code16
|
||||
cli
|
||||
xor %ax, %ax
|
||||
mov %ax, %ds
|
||||
|
||||
/*
|
||||
We want our data do be before STAGE2,
|
||||
or it will get overwritten during the load.
|
||||
*/
|
||||
jmp after_data
|
||||
|
||||
.irp reg, ax, bx, cx, dx, cs, ds, es, fs, gs, ss
|
||||
INITIAL_DATA \reg
|
||||
.endr
|
||||
cr0: .long 0
|
||||
cr0s: .ascii "cr0 = \0"
|
||||
|
||||
after_data:
|
||||
|
||||
.irp reg, ax, bx, cx, dx, cs, ds, es, fs, gs, ss
|
||||
INITIAL_STORE \reg
|
||||
.endr
|
||||
|
||||
/* Does not have a 16-bit mov version. */
|
||||
mov %cr0, %eax
|
||||
mov %eax, cr0
|
||||
|
||||
/*
|
||||
We delay a full BEGIN as late as possible
|
||||
to mess with less initial state.
|
||||
*/
|
||||
BEGIN
|
||||
|
||||
STAGE2
|
||||
|
||||
.irp reg, bx, cx, dx, cs, ds, es, fs, gs, ss
|
||||
INITIAL_PRINT \reg
|
||||
.endr
|
||||
.irp reg, ax, bx, cx, dx, cs, ds, es, fs, gs, ss
|
||||
INITIAL_PRINT \reg
|
||||
.endr
|
||||
PRINT $cr0s
|
||||
PRINT_BYTES cr0, $4
|
||||
PRINT_NEWLINE
|
||||
|
||||
hlt
|
||||
|
||||
.irp reg, bx, cx, dx, cs, ds, es, fs, gs, ss
|
||||
INITIAL_DATA \reg
|
||||
.endr
|
||||
hlt
|
||||
|
||||
15
interrupt.S
15
interrupt.S
@@ -7,8 +7,6 @@ Expected outcome: 'ab' gets printed to the screen.
|
||||
|
||||
TODO: is STI not needed because this interrupt is not maskable?
|
||||
|
||||
TODO: interrupt priority: order looks like: 0, 1, 2, 8, 9, 10, 11, 12, 13, 14, 15, 3, 4, 5, 6, 7. What is that?
|
||||
|
||||
## int
|
||||
|
||||
What it does:
|
||||
@@ -54,6 +52,19 @@ http://wiki.osdev.org/IVT
|
||||
|
||||
osdev says that the default address is 0:0, and that it shouldn't be changed by LIDT,
|
||||
as it is incompatible with older CPUs.
|
||||
|
||||
## Interrupt priority
|
||||
|
||||
Volume 3 6.9 "PRIORITY AMONG SIMULTANEOUS EXCEPTIONS AND INTERRUPTS"
|
||||
says that interrupts have different priorities that arrive
|
||||
at the same cycle have different priorities.
|
||||
|
||||
TODO make a minimal example.
|
||||
|
||||
## Fault vs interrupt vs trap vs abort
|
||||
|
||||
Volume 3 Table 6-1. "Protected-Mode Exceptions and Interrupts"
|
||||
classifies interrupts into multiple types. What is the difference between them?
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
55
page_fault.S
Normal file
55
page_fault.S
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
# Page fault
|
||||
|
||||
Generate and handle a page fault.
|
||||
|
||||
Expected output:
|
||||
|
||||
Page fault handled. Error code:
|
||||
00000002
|
||||
|
||||
## Error code
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
BEGIN
|
||||
CLEAR
|
||||
STAGE2
|
||||
PROTECTED_MODE
|
||||
IDT_SETUP_ENTRY $14, $handler
|
||||
lidt idt_descriptor
|
||||
SETUP_PAGING_4M
|
||||
|
||||
/* Make page 0 not present, so that any access to it will segfault. */
|
||||
mov page_table, %eax
|
||||
and $0xFFFFFFFE, %eax
|
||||
mov %eax, page_table
|
||||
|
||||
PAGING_ON
|
||||
/* Access page 0, generating a segfault. */
|
||||
mov %eax, 0
|
||||
PAGING_OFF
|
||||
|
||||
jmp .
|
||||
|
||||
IDT_START
|
||||
IDT_SKIP 14
|
||||
IDT_ENTRY
|
||||
IDT_END
|
||||
handler:
|
||||
VGA_PRINT_STRING $message
|
||||
/*
|
||||
Mandatory because page faults push the error code to the stack.
|
||||
If we don't do this, then the stack will be wrong for iret, likely leading to a general fault exception:
|
||||
http://stackoverflow.com/questions/10581224/iret-with-13-interruptgeneral-protection-fault-and-error-0x18
|
||||
*/
|
||||
pop %eax
|
||||
VGA_PRINT_HEX_4 <%eax>
|
||||
/* Make the page present. because iret will return to before the mov, and we'd get and infinite loop. */
|
||||
mov page_table, %eax
|
||||
or $1, %eax
|
||||
mov %eax, page_table
|
||||
iret
|
||||
message:
|
||||
.asciz "Page fault handled. Error code:"
|
||||
143
paging.S
143
paging.S
@@ -1,14 +1,12 @@
|
||||
/*
|
||||
# Paging.
|
||||
# Paging
|
||||
|
||||
Expected output:
|
||||
|
||||
00001234
|
||||
00005678
|
||||
|
||||
Verbose tutorial: http://www.cirosantilli.com/x86-paging/
|
||||
|
||||
TODO: move all this info to the tutorial.
|
||||
Verbose beginner's tutorial: http://www.cirosantilli.com/x86-paging/
|
||||
|
||||
Keep the following Intel shorthands in mind:
|
||||
|
||||
@@ -16,33 +14,9 @@ Keep the following Intel shorthands in mind:
|
||||
- PDE: Page directory
|
||||
- PDPTE: Page-directory-
|
||||
|
||||
## Intel manual
|
||||
|
||||
Part 3 has the chapter on Paging.
|
||||
|
||||
## Linux kernel
|
||||
|
||||
4.2: look under arch/x86/:
|
||||
|
||||
- include/asm/pgtable*
|
||||
- include/asm/page*
|
||||
- mm/pgtable*
|
||||
- mm/page*
|
||||
|
||||
There seems to be no structs defined to represent the pages, only macros:
|
||||
`include/asm/page_types.h` is specially interesting. Excerpt:
|
||||
|
||||
#define _PAGE_BIT_PRESENT 0 // is present
|
||||
#define _PAGE_BIT_RW 1 // writeable
|
||||
#define _PAGE_BIT_USER 2 // userspace addressable
|
||||
#define _PAGE_BIT_PWT 3 // page write through
|
||||
|
||||
`arch/x86/include/uapi/asm/processor-flags.h` defines CR0
|
||||
|
||||
## cr3
|
||||
|
||||
The cr3 register does have a format,
|
||||
it is not simply the address of the page directory:
|
||||
The cr3 register does have a format, it is not simply the address of the page directory:
|
||||
|
||||
- 20 top bits: 4KiB address. Since those are the only address bits,
|
||||
this implies that the page directory must be aligned to 4Kib.
|
||||
@@ -56,48 +30,25 @@ Many tutorials simply ignore bits 3 and 4, and do a direct address mov to `cr3`.
|
||||
|
||||
#include "common.h"
|
||||
|
||||
.equ page_directory, __end_align_4k
|
||||
.equ page_table, __end_align_4k + 0x1000
|
||||
|
||||
BEGIN
|
||||
CLEAR
|
||||
STAGE2
|
||||
PROTECTED_MODE
|
||||
|
||||
/*
|
||||
Make the first page directory entry point to the page table.
|
||||
We must do this at runtime because the first 4 bits are not aligned to bytes.
|
||||
*/
|
||||
mov $page_table, %eax
|
||||
/* Zero out the 4 low flag bits of byte 2 (top 20 are address). */
|
||||
and $0xF000, %ax
|
||||
mov %eax, page_directory
|
||||
/* Flags for byte 0. */
|
||||
mov $0b00100111, %al
|
||||
mov %al, page_directory
|
||||
|
||||
call setup_page_table
|
||||
SETUP_PAGING_4M
|
||||
|
||||
/* Setup a test canary value. */
|
||||
mov $0x1234, %eax
|
||||
mov %eax, 0x1000
|
||||
|
||||
/* Print the canary to make sure it is really there. */
|
||||
VGA_PRINT_HEX 0x1000
|
||||
VGA_PRINT_HEX_4 0x1000
|
||||
|
||||
/* Make the page 0 point to page 1. */
|
||||
/* Make page 0 point to 4KiB. */
|
||||
mov page_table, %eax
|
||||
or $0x00001000, %eax
|
||||
mov %eax, page_table
|
||||
|
||||
/* Tell the CPU where the page directory is. */
|
||||
mov $page_directory, %eax
|
||||
mov %eax, %cr3
|
||||
|
||||
/* Turn paging on. */
|
||||
mov %cr0, %eax
|
||||
or $0x80000000, %eax
|
||||
mov %eax, %cr0
|
||||
PAGING_ON
|
||||
|
||||
/*
|
||||
THIS is what we've been working for!!!
|
||||
@@ -112,84 +63,8 @@ BEGIN
|
||||
Remember that VGA does memory accesses, so if paging is still on,
|
||||
we must identity map up to it, which we have, so this is not mandatory.
|
||||
*/
|
||||
mov %cr0, %eax
|
||||
and $0x7FFFFFFF, %eax
|
||||
mov %eax, %cr0
|
||||
PAGING_OFF
|
||||
|
||||
/* Print the (hopefully) modified value 0x5678. */
|
||||
VGA_PRINT_HEX 0x1000
|
||||
VGA_PRINT_HEX_4 0x1000
|
||||
jmp .
|
||||
|
||||
message:
|
||||
.asciz "hello world"
|
||||
|
||||
setup_page_table:
|
||||
/*
|
||||
Setup a single directory: 2^10 * 2^12 == 4MiB of identity memory.
|
||||
Make all pages of the first directory into an identity map (linear address == logical address).
|
||||
This is particularly important because our code segment is running there.
|
||||
*/
|
||||
mov $0, %eax
|
||||
mov $page_table, %ebx
|
||||
page_setup_start:
|
||||
cmp $0x400, %eax
|
||||
je page_setup_end
|
||||
|
||||
/*
|
||||
Byte 0: fixed flags:
|
||||
- 0: present
|
||||
- 1: RW
|
||||
- 2: user mode can access iff 1
|
||||
- 3: Page-level write-through
|
||||
- 4: Page-level cache disable
|
||||
- 5: accessed
|
||||
- 6: dirty
|
||||
- 7: PAT: TODO
|
||||
*/
|
||||
movb $0b00100111, (%ebx)
|
||||
|
||||
/*
|
||||
Byte 1:
|
||||
- 4 bits of flags:
|
||||
- 8: Global
|
||||
- 9:11: ignored
|
||||
- 4 low bits of page address
|
||||
*/
|
||||
mov %eax, %edx
|
||||
/*
|
||||
4 because the 4 low bits of eax are the 4 high bits of the second byte.
|
||||
The 4 low bits of the second byte are flags / ignored and set to 0.
|
||||
*/
|
||||
shl $4, %edx
|
||||
mov %dl, 1(%ebx)
|
||||
|
||||
/* Bytes 2 and 3: 16 high bits of page address. */
|
||||
mov %eax, %edx
|
||||
shr $4, %edx
|
||||
mov %dx, 2(%ebx)
|
||||
|
||||
inc %eax
|
||||
add $4, %ebx
|
||||
jmp page_setup_start
|
||||
page_setup_end:
|
||||
ret
|
||||
|
||||
/*
|
||||
.align could use aligned symbols here, but that is less good
|
||||
as it blows up the size of the image.
|
||||
The better option is to use the linker script instead.
|
||||
|
||||
For this to work, we MUST use STAGE2:
|
||||
otherwise this align would try to move the location counter to 0x1000,
|
||||
and then when the linker tries to add the magic boot byte at 510 it blows up with
|
||||
cannot move location counter backwards.
|
||||
*/
|
||||
/*
|
||||
.align 0x1000
|
||||
page_directory:
|
||||
.byte 0b00100111
|
||||
.skip 3
|
||||
|
||||
.align 0x1000
|
||||
page_table:
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,7 @@ Expected output:
|
||||
#include "common.h"
|
||||
BEGIN
|
||||
CLEAR
|
||||
PRINT_BYTES $s, $17
|
||||
PRINT_BYTES $s, $s_len
|
||||
hlt
|
||||
s:
|
||||
.ascii "@ABCDEFGHIJKLMNOP"
|
||||
s: .ascii "@ABCDEFGHIJKLMNOP"
|
||||
.equ s_len, . - s
|
||||
|
||||
14
test_vga_print_bytes.S
Normal file
14
test_vga_print_bytes.S
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Test VGA_PRINT_BYTES
|
||||
|
||||
TODO implement
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
BEGIN
|
||||
CLEAR
|
||||
PROTECTED_MODE
|
||||
/* VGA_BYTES $s, $s_len */
|
||||
hlt
|
||||
s: .ascii "@ABCDEFGHIJKLMNOP"
|
||||
.equ s_len, . - s
|
||||
Reference in New Issue
Block a user