DRY up protected mode
This commit is contained in:
@@ -42,6 +42,7 @@ Hello world programs that run without an operating system.
|
|||||||
1. [in beep_illinois](in_beep_illinois.S)
|
1. [in beep_illinois](in_beep_illinois.S)
|
||||||
1. [in mouse (TODO)](in_mouse.S)
|
1. [in mouse (TODO)](in_mouse.S)
|
||||||
1. [Protected mode](protected-mode.S)
|
1. [Protected mode](protected-mode.S)
|
||||||
|
1. [Segmentation](segmentation.md)
|
||||||
1. Segmentation offset
|
1. Segmentation offset
|
||||||
1. Segmentation fault handler: memory bound, ring, RWX violations
|
1. Segmentation fault handler: memory bound, ring, RWX violations
|
||||||
1. APM
|
1. APM
|
||||||
|
|||||||
18
TODO.md
18
TODO.md
@@ -23,14 +23,6 @@
|
|||||||
- https://github.com/torvalds/linux/blob/v4.2/arch/x86/boot/boot.h#L78
|
- https://github.com/torvalds/linux/blob/v4.2/arch/x86/boot/boot.h#L78
|
||||||
- http://stackoverflow.com/questions/6793899/what-does-the-0x80-port-address-connects
|
- http://stackoverflow.com/questions/6793899/what-does-the-0x80-port-address-connects
|
||||||
|
|
||||||
- lgdt:
|
|
||||||
|
|
||||||
- http://stackoverflow.com/questions/21128311/the-physical-address-of-global-descriptor-table
|
|
||||||
- http://stackoverflow.com/questions/7415515/problem-accessing-control-registers-cr0-cr2-cr3
|
|
||||||
- http://stackoverflow.com/questions/10671147/how-do-x86-page-tables-work?rq=1
|
|
||||||
- http://stackoverflow.com/questions/14354626/how-to-create-two-separate-segments-in-global-descriptor-table
|
|
||||||
- http://stackoverflow.com/questions/14812160/near-and-far-jmps
|
|
||||||
|
|
||||||
- lidt, interrupts, IDTR:
|
- lidt, interrupts, IDTR:
|
||||||
|
|
||||||
- http://stackoverflow.com/questions/3392831/what-happens-in-an-interrupt-service-routine
|
- http://stackoverflow.com/questions/3392831/what-happens-in-an-interrupt-service-routine
|
||||||
@@ -43,7 +35,7 @@
|
|||||||
|
|
||||||
- WRMSR https://en.wikipedia.org/wiki/Model-specific_register http://x86.renejeschke.de/html/file_module_x86_id_326.html
|
- WRMSR https://en.wikipedia.org/wiki/Model-specific_register http://x86.renejeschke.de/html/file_module_x86_id_326.html
|
||||||
|
|
||||||
- Segment registers and protected mode. Then try to answer all of:
|
- Segment registers /segmentation and protected mode. Then try to answer all of:
|
||||||
|
|
||||||
http://stackoverflow.com/questions/18736663/what-does-the-colon-mean-in-x86-assembly-gas-syntax-as-in-dsbx
|
http://stackoverflow.com/questions/18736663/what-does-the-colon-mean-in-x86-assembly-gas-syntax-as-in-dsbx
|
||||||
- http://reverseengineering.stackexchange.com/questions/2006/how-are-the-segment-registers-fs-gs-cs-ss-ds-es-used-in-linux
|
- http://reverseengineering.stackexchange.com/questions/2006/how-are-the-segment-registers-fs-gs-cs-ss-ds-es-used-in-linux
|
||||||
@@ -75,6 +67,14 @@
|
|||||||
- http://stackoverflow.com/questions/19502868/meaning-of-cs-and-ss-registers-on-x86-64-linux-in-userland
|
- http://stackoverflow.com/questions/19502868/meaning-of-cs-and-ss-registers-on-x86-64-linux-in-userland
|
||||||
- http://stackoverflow.com/questions/7844963/how-to-interpret-segment-register-accesses-on-x86-64
|
- http://stackoverflow.com/questions/7844963/how-to-interpret-segment-register-accesses-on-x86-64
|
||||||
|
|
||||||
|
lgdt:
|
||||||
|
|
||||||
|
- http://stackoverflow.com/questions/21128311/the-physical-address-of-global-descriptor-table
|
||||||
|
- http://stackoverflow.com/questions/7415515/problem-accessing-control-registers-cr0-cr2-cr3
|
||||||
|
- http://stackoverflow.com/questions/10671147/how-do-x86-page-tables-work?rq=1
|
||||||
|
- http://stackoverflow.com/questions/14354626/how-to-create-two-separate-segments-in-global-descriptor-table
|
||||||
|
- http://stackoverflow.com/questions/14812160/near-and-far-jmps
|
||||||
|
|
||||||
- timer, IPT
|
- timer, IPT
|
||||||
|
|
||||||
- BIOS
|
- BIOS
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ The following did not work on my machine out of the box:
|
|||||||
|
|
||||||
Just works, but examples are non-minimal, lots of code duplication and blobs. There must be around 20 El Torito blobs in that repo.
|
Just works, but examples are non-minimal, lots of code duplication and blobs. There must be around 20 El Torito blobs in that repo.
|
||||||
|
|
||||||
|
Multiboot based.
|
||||||
|
|
||||||
- <https://github.com/SamyPesse/How-to-Make-a-Computer-Operating-System>
|
- <https://github.com/SamyPesse/How-to-Make-a-Computer-Operating-System>
|
||||||
|
|
||||||
- <http://www.brokenthorn.com/Resources/OSDevIndex.html>
|
- <http://www.brokenthorn.com/Resources/OSDevIndex.html>
|
||||||
|
|||||||
162
common.h
162
common.h
@@ -2,29 +2,32 @@
|
|||||||
Using macros for everything to make linking simpler.
|
Using macros for everything to make linking simpler.
|
||||||
|
|
||||||
The big ones do bloat the executable.
|
The big ones do bloat the executable.
|
||||||
|
|
||||||
|
## Calling convention
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.altmacro
|
.altmacro
|
||||||
|
|
||||||
#define BEGIN \
|
.macro BEGIN
|
||||||
.code16 ;\
|
.code16
|
||||||
cli ;\
|
cli
|
||||||
/* This sets %cs to 0. TODO Is that really needed? */ ;\
|
/* Set %cs to 0. TODO Is that really needed? */ ;\
|
||||||
ljmp $0, $1f;\
|
ljmp $0, $1f
|
||||||
1:;\
|
1:
|
||||||
xor %ax, %ax ;\
|
xor %ax, %ax
|
||||||
/* We must zero %ds for any data access. */ \
|
/* We must zero %ds for any data access. */ \
|
||||||
mov %ax, %ds ;\
|
mov %ax, %ds
|
||||||
/* TODO is this really need to clear all those segment registers, e.g. for BIOS calls? */ \
|
/* TODO is it really need to clear all those segment registers, e.g. for BIOS calls? */ \
|
||||||
mov %ax, %es ;\
|
mov %ax, %es
|
||||||
mov %ax, %fs ;\
|
mov %ax, %fs
|
||||||
mov %ax, %gs ;\
|
mov %ax, %gs
|
||||||
/* TODO What to move into BP and SP? http://stackoverflow.com/questions/10598802/which-value-should-be-used-for-sp-for-booting-process */ \
|
/* TODO What to move into BP and SP? http://stackoverflow.com/questions/10598802/which-value-should-be-used-for-sp-for-booting-process */ \
|
||||||
mov 0x0000, %bp ;\
|
mov 0x0000, %bp
|
||||||
/* Automatically disables interrupts until the end of the next instruction. */ \
|
/* Automatically disables interrupts until the end of the next instruction. */ \
|
||||||
mov %ax, %ss ;\
|
mov %ax, %ss
|
||||||
/* We should set SP because BIOS calls may depend on that. TODO confirm. */ \
|
/* We should set SP because BIOS calls may depend on that. TODO confirm. */ \
|
||||||
mov %bp, %sp
|
mov %bp, %sp
|
||||||
|
.endm
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Load stage2 from disk to memory, and jump to it.
|
Load stage2 from disk to memory, and jump to it.
|
||||||
@@ -38,20 +41,86 @@ Sample usage:
|
|||||||
STAGE2
|
STAGE2
|
||||||
Stage 2 code here.
|
Stage 2 code here.
|
||||||
*/
|
*/
|
||||||
#define STAGE2 \
|
.macro STAGE2
|
||||||
mov $2, %ah;\
|
mov $2, %ah
|
||||||
/* TODO get working on linker script. Above my paygrade for now, so I just load a bunch of sectors instead. */;\
|
/*
|
||||||
/* mov __stage2_size, %al;\ */;\
|
TODO get working on linker script.
|
||||||
mov $9, %al;\
|
Above my paygrade for now, so I just load a bunch of sectors instead.
|
||||||
mov $0x80, %dl;\
|
*/
|
||||||
mov $0, %ch;\
|
/* mov __stage2_size, %al;\ */
|
||||||
mov $0, %dh;\
|
mov $9, %al
|
||||||
mov $2, %cl;\
|
mov $0x80, %dl
|
||||||
mov $1f, %bx;\
|
mov $0, %ch
|
||||||
int $0x13;\
|
mov $0, %dh
|
||||||
jmp 1f;\
|
mov $2, %cl
|
||||||
.section .stage2;\
|
mov $1f, %bx
|
||||||
|
int $0x13
|
||||||
|
jmp 1f
|
||||||
|
.section .stage2
|
||||||
1:
|
1:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
/*
|
||||||
|
Enter protected mode.
|
||||||
|
|
||||||
|
Use the simplest GDT possible.
|
||||||
|
*/
|
||||||
|
.macro PROTECTED_MODE
|
||||||
|
cli
|
||||||
|
/* Tell the processor where our Global Descriptor Table is in memory. */
|
||||||
|
lgdt gdt_descriptor
|
||||||
|
|
||||||
|
/* Set PE (Protection Enable) bit in CR0 (Control Register 0) */
|
||||||
|
mov %cr0, %eax
|
||||||
|
orl $0x1, %eax
|
||||||
|
mov %eax, %cr0
|
||||||
|
|
||||||
|
/* TODO why does equ not work? What is the alternative? */
|
||||||
|
/*ljmp $CODE_SEG, $b32*/
|
||||||
|
/*
|
||||||
|
This is needed to set `%cs` to 8.
|
||||||
|
|
||||||
|
8 means the second entry, since each entry is 8 bytes wide,
|
||||||
|
and we have an initial null entry.
|
||||||
|
*/
|
||||||
|
ljmp $0x08, $protected_mode
|
||||||
|
.code32
|
||||||
|
gdt_start:
|
||||||
|
gdt_null:
|
||||||
|
.long 0x0
|
||||||
|
.long 0x0
|
||||||
|
gdt_code:
|
||||||
|
.word 0xffff
|
||||||
|
.word 0x0
|
||||||
|
.byte 0x0
|
||||||
|
.byte 0b10011010
|
||||||
|
.byte 0b11001111
|
||||||
|
.byte 0x0
|
||||||
|
gdt_data:
|
||||||
|
.word 0xffff
|
||||||
|
.word 0x0
|
||||||
|
.byte 0x0
|
||||||
|
.byte 0b10010010
|
||||||
|
.byte 0b11001111
|
||||||
|
.byte 0x0
|
||||||
|
gdt_end:
|
||||||
|
gdt_descriptor:
|
||||||
|
.word gdt_end - gdt_start
|
||||||
|
.long gdt_start
|
||||||
|
.equ CODE_SEG, gdt_code - gdt_start
|
||||||
|
.equ DATA_SEG, gdt_data - gdt_start
|
||||||
|
protected_mode:
|
||||||
|
/* Setup the other segments. */
|
||||||
|
mov $DATA_SEG, %ax
|
||||||
|
mov %ax, %ds
|
||||||
|
mov %ax, %es
|
||||||
|
mov %ax, %fs
|
||||||
|
mov %ax, %gs
|
||||||
|
mov %ax, %ss
|
||||||
|
/* TODO: what is the maximum we can put were? Why does `FFFFFFFF` fail? */
|
||||||
|
mov $0XFFFFFF00, %ebp
|
||||||
|
mov %ebp, %esp
|
||||||
|
.endm
|
||||||
|
|
||||||
/* BIOS */
|
/* BIOS */
|
||||||
|
|
||||||
@@ -62,13 +131,14 @@ Sample usage:
|
|||||||
int $0x10
|
int $0x10
|
||||||
|
|
||||||
/* Clear the screen, move to position 0, 0. */
|
/* Clear the screen, move to position 0, 0. */
|
||||||
#define CLEAR \
|
.macro CLEAR
|
||||||
mov $0x0600, %ax;\
|
mov $0x0600, %ax
|
||||||
mov $0x7, %bh;\
|
mov $0x7, %bh
|
||||||
mov $0x0, %cx;\
|
mov $0x0, %cx
|
||||||
mov $0x184f, %dx;\
|
mov $0x184f, %dx
|
||||||
int $0x10;\
|
int $0x10
|
||||||
CURSOR_POSITION(0, 0)
|
CURSOR_POSITION(0, 0)
|
||||||
|
.endm
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Print a single immediate byte or 8 bit register.
|
Print a single immediate byte or 8 bit register.
|
||||||
@@ -152,7 +222,6 @@ Use as:
|
|||||||
We use this `cpp` macro to allow writing `PRINT(S)` with parenthesis.
|
We use this `cpp` macro to allow writing `PRINT(S)` with parenthesis.
|
||||||
*/
|
*/
|
||||||
#define PRINT(s) GAS_PRINT s
|
#define PRINT(s) GAS_PRINT s
|
||||||
/* We need a Gas macro for the LOCAL labels. */
|
|
||||||
.macro GAS_PRINT s
|
.macro GAS_PRINT s
|
||||||
LOCAL loop, end
|
LOCAL loop, end
|
||||||
mov s, %si
|
mov s, %si
|
||||||
@@ -167,3 +236,28 @@ end:
|
|||||||
.endm
|
.endm
|
||||||
|
|
||||||
/* VGA */
|
/* VGA */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Print a NULL terminated string to position 0 in VGA.
|
||||||
|
|
||||||
|
s: 32-bit register or memory containing the address of the string to print.
|
||||||
|
|
||||||
|
Clobbers: eax, ecx, edx
|
||||||
|
*/
|
||||||
|
.macro VGA_PRINT s
|
||||||
|
LOCAL loop, end
|
||||||
|
mov s, %ecx
|
||||||
|
/* Video memory address. */
|
||||||
|
mov $0xb8000, %edx
|
||||||
|
/* White on black. */
|
||||||
|
mov $0x0f, %ah
|
||||||
|
loop:
|
||||||
|
mov (%ecx), %al
|
||||||
|
cmp $0, %al
|
||||||
|
je end
|
||||||
|
mov %ax, (%edx)
|
||||||
|
add $1, %ecx
|
||||||
|
add $2, %edx
|
||||||
|
jmp loop
|
||||||
|
end:
|
||||||
|
.endm
|
||||||
|
|||||||
160
protected_mode.S
160
protected_mode.S
@@ -1,170 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
# Protected mode
|
# Protected mode
|
||||||
|
|
||||||
Major changes:
|
Major changes from real moe:
|
||||||
|
|
||||||
- BIOS cannot be used anymore
|
- BIOS cannot be used anymore
|
||||||
|
|
||||||
- GDT takes effect immediately so we *have* to deal with it now,
|
- GDT and segmentation take effect immediately so we *have*
|
||||||
and we will later turn on paging
|
to deal with it now.
|
||||||
|
|
||||||
- we have to encode instructions differently.
|
- we have to encode instructions differently, thus a `.code32` is needed.
|
||||||
Note that in 16-bit 32-bit instructions were encodable, but with a prefix.
|
Note that in 16-bit, 32-bit instructions were encodable, but with a prefix.
|
||||||
|
|
||||||
## Bibliography
|
## Bibliography
|
||||||
|
|
||||||
- http://stackoverflow.com/questions/28645439/how-do-i-enter-32-bit-protected-mode-in-nasm-assembly
|
- http://stackoverflow.com/questions/28645439/how-do-i-enter-32-bit-protected-mode-in-nasm-assembly Initially adapted from this.
|
||||||
- http://wiki.osdev.org/Journey_To_The_Protected_Land
|
- http://wiki.osdev.org/Journey_To_The_Protected_Land
|
||||||
- http://wiki.osdev.org/Protected_Mode
|
- http://wiki.osdev.org/Protected_Mode
|
||||||
- https://github.com/chrisdew/xv6/blob/master/bootasm.S
|
- https://github.com/chrisdew/xv6/blob/master/bootasm.S
|
||||||
- https://thiscouldbebetter.wordpress.com/2011/03/17/entering-protected-mode-from-assembly/ FASM based. Did not word on first try, but looks real clean.
|
- https://thiscouldbebetter.wordpress.com/2011/03/17/entering-protected-mode-from-assembly/ FASM based. Did not word on first try, but looks real clean.
|
||||||
- http://skelix.net/skelixos/tutorial02_en.html
|
- http://skelix.net/skelixos/tutorial02_en.html
|
||||||
|
|
||||||
## GDT
|
|
||||||
|
|
||||||
Table in memory that gives properties of segment registers.
|
|
||||||
|
|
||||||
Segment registers in protected mode point to entries of that table.
|
|
||||||
|
|
||||||
GDT is used as soon as we enter protected mode, so that's why we have to deal with it, but the preferred way of managing program memory spaces is paging.
|
|
||||||
|
|
||||||
### Effect on memory access
|
|
||||||
|
|
||||||
The GDT modifies every memory access of a given segment by:
|
|
||||||
|
|
||||||
- adding an offset to it
|
|
||||||
- limiting how big the segment is
|
|
||||||
|
|
||||||
If an access is made at an offset larger than allowed: TODO some exception happens, which is like an interrupt, and gets handled by a previously registered handler.
|
|
||||||
|
|
||||||
The GDT could be used to implement virtual memory by using one segment per program:
|
|
||||||
|
|
||||||
+-----------+--------+--------------------------+
|
|
||||||
| Program 1 | Unused | Program 2 |
|
|
||||||
+-----------+--------+--------------------------+
|
|
||||||
^ ^ ^ ^
|
|
||||||
| | | |
|
|
||||||
Start1 End1 Start2 End2
|
|
||||||
|
|
||||||
The problem with that is that each program must have one segment, so if we have too many programs, fragmentation will be very large.
|
|
||||||
|
|
||||||
Paging gets around this by allowing discontinuous memory ranges of fixed size for each program.
|
|
||||||
|
|
||||||
The format of the GDT is given at: http://wiki.osdev.org/Global_Descriptor_Table
|
|
||||||
|
|
||||||
### Effect on permissions
|
|
||||||
|
|
||||||
Besides fixing segment sizes, the GDT also specifies permissions to the program that is running:
|
|
||||||
|
|
||||||
- ring level: limits several things that can or not be done, in particular:
|
|
||||||
- instructions: e.g. no in / out in ring 3
|
|
||||||
- register access: e.g. cannot modify control registers like the GDTR in ring 3. Otherwise user programs could just escape restrictions by changing that!
|
|
||||||
- executable, readable and writable bits: which operations can be done
|
|
||||||
|
|
||||||
## GDTR
|
|
||||||
|
|
||||||
## GDT register
|
|
||||||
|
|
||||||
In 32-bit, a 6 byte register that holds:
|
|
||||||
|
|
||||||
- 2 byte length of the GDT (TODO in bytes or number of entries?)
|
|
||||||
- 4 byte address of the GDT in memory
|
|
||||||
|
|
||||||
In 64 bit, makes 10 bytes, with the address having 8 bytes
|
|
||||||
|
|
||||||
GRUB seems to setup one for you: http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
CLEAR
|
CLEAR
|
||||||
|
PROTECTED_MODE
|
||||||
cli
|
VGA_PRINT $message
|
||||||
/* Tell the processor where our Global Descriptor Table is in memory. */
|
|
||||||
lgdt gdt_descriptor
|
|
||||||
|
|
||||||
/* Set PE (Protection Enable) bit in CR0 (Control Register 0) */
|
|
||||||
mov %cr0, %eax
|
|
||||||
orl $0x1, %eax
|
|
||||||
mov %eax, %cr0
|
|
||||||
|
|
||||||
/* TODO why does equ not work? What is the alternative? */
|
|
||||||
/*ljmp $CODE_SEG, $b32*/
|
|
||||||
/*
|
|
||||||
This is needed to set `%cs` to 8.
|
|
||||||
|
|
||||||
8 means the second entry, since each entry is 8 bytes wide,
|
|
||||||
and we have an initial null entry.
|
|
||||||
*/
|
|
||||||
ljmp $0x08, $b32
|
|
||||||
|
|
||||||
.code32
|
|
||||||
|
|
||||||
print32:
|
|
||||||
pusha
|
|
||||||
# Video memory address.
|
|
||||||
mov $0xb8000, %edx
|
|
||||||
print32.loop:
|
|
||||||
mov (%ebx), %al
|
|
||||||
# White on black.
|
|
||||||
mov $0x0f, %ah
|
|
||||||
cmp $0, %al
|
|
||||||
je print32.done
|
|
||||||
mov %ax, (%edx)
|
|
||||||
add $1, %ebx
|
|
||||||
add $2, %edx
|
|
||||||
jmp print32.loop
|
|
||||||
print32.done:
|
|
||||||
popa
|
|
||||||
ret
|
|
||||||
|
|
||||||
b32:
|
|
||||||
/* Setup the other segments. */
|
|
||||||
mov $DATA_SEG, %ax
|
|
||||||
mov %ax, %ds
|
|
||||||
mov %ax, %es
|
|
||||||
mov %ax, %fs
|
|
||||||
mov %ax, %gs
|
|
||||||
mov %ax, %ss
|
|
||||||
/* TODO: what is the maximum we can put were? Why does `FFFFFFFF` fail? */
|
|
||||||
mov $0XFFFFFF00, %ebp
|
|
||||||
mov %ebp, %esp
|
|
||||||
|
|
||||||
mov $message, %ebx
|
|
||||||
call print32
|
|
||||||
|
|
||||||
jmp .
|
jmp .
|
||||||
|
message:
|
||||||
gdt_start:
|
.asciz "hello world"
|
||||||
|
|
||||||
gdt_null:
|
|
||||||
.long 0x0
|
|
||||||
.long 0x0
|
|
||||||
|
|
||||||
gdt_code:
|
|
||||||
.word 0xffff
|
|
||||||
.word 0x0
|
|
||||||
.byte 0x0
|
|
||||||
.byte 0b10011010
|
|
||||||
.byte 0b11001111
|
|
||||||
.byte 0x0
|
|
||||||
|
|
||||||
gdt_data:
|
|
||||||
.word 0xffff
|
|
||||||
.word 0x0
|
|
||||||
.byte 0x0
|
|
||||||
.byte 0b10010010
|
|
||||||
.byte 0b11001111
|
|
||||||
.byte 0x0
|
|
||||||
|
|
||||||
gdt_end:
|
|
||||||
|
|
||||||
gdt_descriptor:
|
|
||||||
.word gdt_end - gdt_start
|
|
||||||
.long gdt_start
|
|
||||||
|
|
||||||
.equ CODE_SEG, gdt_code - gdt_start
|
|
||||||
.equ DATA_SEG, gdt_data - gdt_start
|
|
||||||
|
|
||||||
message: .asciz "hello world"
|
|
||||||
|
|||||||
86
segmentation.md
Normal file
86
segmentation.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Segmentation
|
||||||
|
|
||||||
|
This is about protected mode segmentation, which coverts liner to global addresses.
|
||||||
|
|
||||||
|
## GDT
|
||||||
|
|
||||||
|
Table in memory that gives properties of segment registers.
|
||||||
|
|
||||||
|
Segment registers in protected mode point to entries of that table.
|
||||||
|
|
||||||
|
GDT is used as soon as we enter protected mode, so that's why we have to deal with it, but the preferred way of managing program memory spaces is paging.
|
||||||
|
|
||||||
|
Format:
|
||||||
|
|
||||||
|
- Intel Manual 325384-053US Volume 3, 3.4.5 Segment Descriptors
|
||||||
|
- https://en.wikipedia.org/wiki/Global_Descriptor_Table
|
||||||
|
- http://wiki.osdev.org/GDT
|
||||||
|
|
||||||
|
+-------------------------------------------------+
|
||||||
|
| segment address 24-31 | flags #2 | len 16-19 |
|
||||||
|
+-------------------------------------------------+
|
||||||
|
| flags #1 | segment address 16-23 |
|
||||||
|
+-------------------------------------------------+
|
||||||
|
| segment address bits 0-15 |
|
||||||
|
+-------------------------------------------------+
|
||||||
|
| segment length bits 0-15 |
|
||||||
|
+-------------------------------------------------+
|
||||||
|
|
||||||
|
### Null segment selector
|
||||||
|
|
||||||
|
### Null descriptor
|
||||||
|
|
||||||
|
Intel manual 3.4.2 Segment Selectors says:
|
||||||
|
|
||||||
|
> The first entry of the GDT is not used by the processor. A segment selector that points to this entry of the GDT (that
|
||||||
|
is, a segment selector with an index of 0 and the TI flag set to 0) is used as a “null segment selector.” The processor
|
||||||
|
does not generate an exception when a segment register (other than the CS or SS registers) is loaded with a null
|
||||||
|
selector. It does, however, generate an exception when a segment register holding a null selector is used to access
|
||||||
|
memory. A null selector can be used to initialize unused segment registers. Loading the CS or SS register with a null
|
||||||
|
segment selector causes a general-protection exception (#GP) to be generated.
|
||||||
|
|
||||||
|
### Effect on memory access
|
||||||
|
|
||||||
|
The GDT modifies every memory access of a given segment by:
|
||||||
|
|
||||||
|
- adding an offset to it
|
||||||
|
- limiting how big the segment is
|
||||||
|
|
||||||
|
If an access is made at an offset larger than allowed: TODO some exception happens, which is like an interrupt, and gets handled by a previously registered handler.
|
||||||
|
|
||||||
|
The GDT could be used to implement virtual memory by using one segment per program:
|
||||||
|
|
||||||
|
+-----------+--------+--------------------------+
|
||||||
|
| Program 1 | Unused | Program 2 |
|
||||||
|
+-----------+--------+--------------------------+
|
||||||
|
^ ^ ^ ^
|
||||||
|
| | | |
|
||||||
|
Start1 End1 Start2 End2
|
||||||
|
|
||||||
|
The problem with that is that each program must have one segment, so if we have too many programs, fragmentation will be very large.
|
||||||
|
|
||||||
|
Paging gets around this by allowing discontinuous memory ranges of fixed size for each program.
|
||||||
|
|
||||||
|
The format of the GDT is given at: http://wiki.osdev.org/Global_Descriptor_Table
|
||||||
|
|
||||||
|
### Effect on permissions
|
||||||
|
|
||||||
|
Besides fixing segment sizes, the GDT also specifies permissions to the program that is running:
|
||||||
|
|
||||||
|
- ring level: limits several things that can or not be done, in particular:
|
||||||
|
- instructions: e.g. no in / out in ring 3
|
||||||
|
- register access: e.g. cannot modify control registers like the GDTR in ring 3. Otherwise user programs could just escape restrictions by changing that!
|
||||||
|
- executable, readable and writable bits: which operations can be done
|
||||||
|
|
||||||
|
## GDTR
|
||||||
|
|
||||||
|
## GDT register
|
||||||
|
|
||||||
|
In 32-bit, a 6 byte register that holds:
|
||||||
|
|
||||||
|
- 2 byte length of the GDT (TODO in bytes or number of entries?)
|
||||||
|
- 4 byte address of the GDT in memory
|
||||||
|
|
||||||
|
In 64 bit, makes 10 bytes, with the address having 8 bytes
|
||||||
|
|
||||||
|
GRUB seems to setup one for you: http://www.jamesmolloy.co.uk/tutorial_html/4.-The%20GDT%20and%20IDT.html
|
||||||
Reference in New Issue
Block a user