Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
Just thought I'd share some code. Was able to go from 0x7C00 to ring 3 in 102 bytes (including gdt but not the boot magic). Finding out that LGDT instruction was using DS kept me busy for a while. The best part is that it works on my native hardware too! Now I just need to find a way back
; Boot sector offset in memory
ORG 0x7C00
; Initially in 16 bit Real Mode
BITS 16
; Keep interrupts for crashing the mode switch
cli
; LGDT uses the DS register offset
xor ax,ax
mov ds,ax
lgdt[gdtr]
; Set the PE bit in CR0 which enables Protected Mode
mov eax, cr0
or al, 1
mov cr0, eax
; According the the Intel Architectures Software Developer's Manual
; the following will clear the prefetch cache that will contain
; invalid information in 32 bit mode.
jmp long code_segment:pmode
; This is the entry point into 32 bit Protected Mode
BITS 32
pmode:
; Load a valid SS register
mov ax, data_segment
mov ss, ax
; Set the RING 0 stack
mov esp, 0x7C00
; Here we setup a stack from for the IRET instruction
; which will pop the following SS / ESP / EFLAGS / CS / EIP
; since RPL > CPL
push dword udata_segment | 3
push dword 0x8000
pushf
push dword ucode_segment | 3
push ring3
; Switch to RING 3
iret
; At this point we are running under the rules of RING 3
ring3:
.loop:
inc byte [ss:0xB8001]
jmp .loop
; Global Descriptor Table
gdt:
; null descriptor / GDTR
gdt_null:
; Global Descriptor Table Register
gdtr:
dw gdt_end-gdt-1
dd gdt
dw 0
; code
gdt_cs:
; RING 0 Code Segment
code_segment equ $-gdt
dd 0x0000FFFF
dd 0x00CF9A00
; data
gdt_ds:
; RING 0 Data Segment
data_segment equ $-gdt
dd 0x0000FFFF
dd 0x00CF9200
; code
gdt_ucs:
; RING 3 Code Segment
ucode_segment equ $-gdt
dd 0x0000FFFF
dd 0x00CFFA00
; data
gdt_uds:
; RING 3 Data Segment
udata_segment equ $-gdt
dd 0x0000FFFF
dd 0x00CFF200
gdt_end:
; Skip to end of MBR and insert the boot magic
TIMES 510-($-$$) db 0
dw 0xAA55
Finding out that LGDT instruction was using DS kept me busy for a while.
Actually, LGDT is using linear address...it doesn't care what is in DS, but that address is easier to get when DS=0x0000.
Otherwise if your DS was something like this: DS:0x07C0, then you would do this to obtain linear address:
The source operand specifies a 6-byte memory location that contains the base address (a linear address) and
the limit (size of table in bytes) of the global descriptor table (GDT) or the interrupt
descriptor table (IDT).
Actually, LGDT is using linear address...it doesn't care what is in DS, but that address is easier to get when DS=0x0000.
Otherwise if your DS was something like this: DS:0x07C0, then you would do this to obtain linear address:
Are you sure ? I understood that the operand uses segmented addressing as usual but the value that is loaded is linear. In other words, DS is relevant when LGDT is executed.
If a trainstation is where trains stop, what is a workstation ?
Are you sure ? I understood that the operand uses segmented addressing as usual but the value that is loaded is linear. In other words, DS is relevant when LGDT is executed.
I just wanted to point out that he could use what ever segment was specified in DS, but transform it to linear address before using it as an operand.
LGDT(DS:Offset) is implied if no other segment is specified... apparently . It worked fine if I loaded it using some other bootloader but not when I booted it directly because I wasn't clearing DS and my computer boots with DS=0x9FC0.
Well now I've got a task scheduler in place and can trigger GPFs and DIV0 errors that merely cause that particular RING 3 task to be removed from the scheduling queue. My setup now has a kernel stack and user stack allocated to each process. I'm thinking that theoretically I can leave interrupts enabled full time except for the part where I switch contexts...
Dario wrote:Actually, LGDT is using linear address...it doesn't care what is in DS, but that address is easier to get when DS=0x0000.
Otherwise if your DS was something like this: DS:0x07C0, then you would do this to obtain linear address:
The source operand specifies a 6-byte memory location that contains the base address (a linear address) and
the limit (size of table in bytes) of the global descriptor table (GDT) or the interrupt
descriptor table (IDT).
It says that the base part of the 6 byte structure, usually called the GDTR, at the memory location is linear; not that the pointer specifying the address of the structure has to be linear.
Agreed, when the processor uses the 6-byte pointer stored in GDTR to access the segment descriptors DS nor any other segment register is used (i.e. linear). However when executing the LGDT instruction the operand is defaulted to [DS:value] but can be overridden with any of the other segment registers.