0x7C00 to RING 3

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!
Post Reply
ecco
Posts: 19
Joined: Mon Nov 29, 2010 1:21 am
Location: Anchorage, AK

0x7C00 to RING 3

Post by ecco »

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 :D

Code: Select all

; 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
Dario
Member
Member
Posts: 117
Joined: Sun Aug 31, 2008 12:39 pm

Re: 0x7C00 to RING 3

Post by Dario »

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:

Code: Select all

lgdtr (0x07C0<<4)+gdtr
from Intel 2a:
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).
____
Dario
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: 0x7C00 to RING 3

Post by gerryg400 »

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 ?
Dario
Member
Member
Posts: 117
Joined: Sun Aug 31, 2008 12:39 pm

Re: 0x7C00 to RING 3

Post by Dario »

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.
____
Dario
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: 0x7C00 to RING 3

Post by gerryg400 »

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.
No that's not correct. The operand is segmented. But the operand points to a linear address.
If a trainstation is where trains stop, what is a workstation ?
Dario
Member
Member
Posts: 117
Joined: Sun Aug 31, 2008 12:39 pm

Re: 0x7C00 to RING 3

Post by Dario »

gerryg400 wrote:The operand is segmented.
Then why don't we use DS:OFFSET in instruction?
____
Dario
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: 0x7C00 to RING 3

Post by gerryg400 »

As is mostly the case, DS is the default seg for data. But you can use a segment override.
If a trainstation is where trains stop, what is a workstation ?
ecco
Posts: 19
Joined: Mon Nov 29, 2010 1:21 am
Location: Anchorage, AK

Re: 0x7C00 to RING 3

Post by ecco »

LGDT(DS:Offset) is implied if no other segment is specified... apparently :P. 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.
ecco
Posts: 19
Joined: Mon Nov 29, 2010 1:21 am
Location: Anchorage, AK

Re: 0x7C00 to RING 3

Post by ecco »

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...
TylerH
Member
Member
Posts: 285
Joined: Tue Apr 13, 2010 8:00 pm
Contact:

Re: 0x7C00 to RING 3

Post by TylerH »

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:

Code: Select all

lgdtr (0x07C0<<4)+gdtr
from Intel 2a:
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.
ecco
Posts: 19
Joined: Mon Nov 29, 2010 1:21 am
Location: Anchorage, AK

Re: 0x7C00 to RING 3

Post by ecco »

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.
Post Reply