Skip to content

Instantly share code, notes, and snippets.

@thatreguy
Last active December 15, 2018 02:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thatreguy/a91d1d6c44938494fb37430bcd4b419d to your computer and use it in GitHub Desktop.
Save thatreguy/a91d1d6c44938494fb37430bcd4b419d to your computer and use it in GitHub Desktop.
Ring Switcher using call and trap gates nasm -f bin -o ring_x_gate.img ring_x_gate.asm https://www.reddit.com/r/osdev/comments/5tgete/what_is_the_canonical_way_to_enter_ring_3_on_x86/ddpt39r
%define SEL_RING_0_CODE 0x0008
%define SEL_RING_0_DATA 0x0010
%define SEL_RING_1_CODE (0x0018 | 0x0001)
%define SEL_RING_1_DATA (0x0020 | 0x0001)
%define SEL_RING_2_CODE (0x0028 | 0x0002)
%define SEL_RING_2_DATA (0x0030 | 0x0002)
%define SEL_RING_3_CODE (0x0038 | 0x0003)
%define SEL_RING_3_DATA (0x0040 | 0x0003)
%define SEL_TSS 0x0048
%define SEL_RING_0_CALL 0x0050
%define SEL_RING_1_CALL 0x0058
%define SEL_RING_2_CALL 0x0060
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%define ADDRESS_OF(label) \
(0x00007C00 + (label - $$))
%define MAKE_DESC(base, limit, access, flags) \
(((base & 0xFFFFFF) << 16) | \
((base & 0xFF000000) << 32) | \
(limit & 0xFFFF) | \
((limit & 0xFF0000) << 32) | \
((access & 0xFF) << 40) | \
((flags & 0xF) << 52))
%define MAKE_CALL_GATE(selector, offset, access) \
(((selector & 0xFFFF) << 16) | \
(offset & 0xFFFF) | \
((offset & 0xFFFF0000) << 32) | \
((access & 0xFF) << 40))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ORG 0x00007C00
BITS 16
start16:
; Disable interrupts
cli
; Load global descriptor table
lgdt [gdtr]
; Enable protected mode
mov eax, cr0
or al, 0x01
mov cr0, eax
; Jump to 32-bit ring 0 code segment
jmp SEL_RING_0_CODE:start32
BITS 32
start32:
; Load 32-bit ring 0 data segments
mov ax, SEL_RING_0_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Load stack pointer
mov esp, ring_0_stack.top
; Prepare task state segment
mov dword [tss.esp0], ring_0_stack.top
mov dword [tss.ss0], SEL_RING_0_DATA
mov dword [tss.esp1], ring_1_stack.top
mov dword [tss.ss1], SEL_RING_1_DATA
mov dword [tss.esp2], ring_2_stack.top
mov dword [tss.ss2], SEL_RING_2_DATA
mov dword [tss.iopb], (tss.end - tss)
; Load task state segment
mov ax, SEL_TSS
ltr ax
; Jump to 32-bit ring 3 code segment
push dword SEL_RING_3_DATA ; ss
push dword ring_3_stack.top ; esp
push dword 0x00000000 ; eflags
push dword SEL_RING_3_CODE ; cs
push dword ring3 ; eip
iretd
ring0:
; Save incoming data segments
push ds
push es
push fs
push gs
; Load 32-bit ring 0 data segments
mov ax, SEL_RING_0_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Print a '0' in the top-left corner of the screen
mov word [0xB8000], 0x1F00 | '0'
; Restore incoming data segments
pop gs
pop fs
pop es
pop ds
; Return to ring 3
retf
ring1:
; Save incoming data segments
push ds
push es
push fs
push gs
; Load 32-bit ring 0 data segments
mov ax, SEL_RING_1_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Print a '1' in the top-left corner of the screen
mov word [0xB8002], 0x1F00 | '1'
; Restore incoming data segments
pop gs
pop fs
pop es
pop ds
; Return to ring 3
retf
ring2:
; Save incoming data segments
push ds
push es
push fs
push gs
; Load 32-bit ring 2 data segments
mov ax, SEL_RING_2_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Print a '2' in the top-left corner of the screen
mov word [0xB8004], 0x1F00 | '2'
; Restore incoming data segments
pop gs
pop fs
pop es
pop ds
; Return to ring 3
retf
ring3:
; Load 32-bit ring 3 data segments
mov ax, SEL_RING_3_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Call a ring 0 function
call SEL_RING_0_CALL:0x00000000
; Call a ring 1 function
call SEL_RING_1_CALL:0x00000000
; Call a ring 2 function
call SEL_RING_2_CALL:0x00000000
; Print a '3' in the top-left corner of the screen
mov word [0xB8006], 0x1F00 | '3'
; Spin forever (can't use HLT in ring 3)
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ALIGN 8
gdt:
; Null descriptor
dq MAKE_DESC(0x00000000, 0x00000, 0x00, 0x0)
; 32-bit ring 0 code descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0x9A, 0xC)
; 32-bit ring 0 data descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0x92, 0xC)
; 32-bit ring 1 code descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xBA, 0xC)
; 32-bit ring 1 data descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xB2, 0xC)
; 32-bit ring 2 code descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xDA, 0xC)
; 32-bit ring 2 data descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xD2, 0xC)
; 32-bit ring 3 code descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xFA, 0xC)
; 32-bit ring 3 data descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xF2, 0xC)
; Task state segment descriptor
dq MAKE_DESC(tss, tss.end - tss - 1, 0xE9, 0x4)
; Ring 0 call gate descriptor
dq MAKE_CALL_GATE(SEL_RING_0_CODE, ADDRESS_OF(ring0), 0xEC)
; Ring 1 call gate descriptor
dq MAKE_CALL_GATE(SEL_RING_1_CODE, ADDRESS_OF(ring1), 0xEC)
; Ring 2 call gate descriptor
dq MAKE_CALL_GATE(SEL_RING_2_CODE, ADDRESS_OF(ring2), 0xEC)
gdtr:
dw (gdtr - gdt - 1)
dd gdt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times (510 - ($ - $$)) \
db 0x00
dw 0xAA55
times (1474560 - ($ - $$)) \
db 0x00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ABSOLUTE 0x00001000
tss:
.link: resw 1
resw 1
.esp0: resd 1
.ss0: resw 1
resw 1
.esp1: resd 1
.ss1: resw 1
resw 1
.esp2: resd 1
.ss2: resw 1
resw 1
.cr3: resd 1
.eip: resd 1
.eflags:resd 1
.eax: resd 1
.ecx: resd 1
.edx: resd 1
.ebx: resd 1
.esp: resd 1
.ebp: resd 1
.esi: resd 1
.edi: resd 1
.es: resw 1
resw 1
.cs: resw 1
resw 1
.ss: resw 1
resw 1
.ds: resw 1
resw 1
.fs: resw 1
resw 1
.gs: resw 1
resw 1
.ldtr: resw 1
resw 1
resw 1
.iopb: resw 1
.end:
ABSOLUTE 0x00002000
ring_0_stack:
resb 0x00001000
.top:
ABSOLUTE 0x00003000
ring_1_stack:
resb 0x00001000
.top:
ABSOLUTE 0x00004000
ring_2_stack:
resb 0x00001000
.top:
ABSOLUTE 0x00005000
ring_3_stack:
resb 0x00001000
.top:
%define SEL_RING_0_CODE 0x0008
%define SEL_RING_0_DATA 0x0010
%define SEL_RING_1_CODE (0x0018 | 0x0001)
%define SEL_RING_1_DATA (0x0020 | 0x0001)
%define SEL_RING_2_CODE (0x0028 | 0x0002)
%define SEL_RING_2_DATA (0x0030 | 0x0002)
%define SEL_RING_3_CODE (0x0038 | 0x0003)
%define SEL_RING_3_DATA (0x0040 | 0x0003)
%define SEL_TSS 0x0048
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
%define ADDRESS_OF(label) \
(0x00007C00 + (label - $$))
%define MAKE_DESC(base, limit, access, flags) \
(((base & 0xFFFFFF) << 16) | \
((base & 0xFF000000) << 32) | \
(limit & 0xFFFF) | \
((limit & 0xFF0000) << 32) | \
((access & 0xFF) << 40) | \
((flags & 0xF) << 52))
%define MAKE_TRAP_GATE(selector, offset, access) \
(((selector & 0xFFFF) << 16) | \
(offset & 0xFFFF) | \
((offset & 0xFFFF0000) << 32) | \
((access & 0xFF) << 40))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ORG 0x00007C00
BITS 16
start16:
; Disable interrupts
cli
; Load global descriptor table
lgdt [gdtr]
; Enable protected mode
mov eax, cr0
or al, 0x01
mov cr0, eax
; Jump to 32-bit ring 0 code segment
jmp SEL_RING_0_CODE:start32
BITS 32
start32:
; Load 32-bit ring 0 data segments
mov ax, SEL_RING_0_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Load stack pointer
mov esp, ring_0_stack.top
; Prepare task state segment
mov dword [tss.esp0], ring_0_stack.top
mov dword [tss.ss0], SEL_RING_0_DATA
mov dword [tss.esp1], ring_1_stack.top
mov dword [tss.ss1], SEL_RING_1_DATA
mov dword [tss.esp2], ring_2_stack.top
mov dword [tss.ss2], SEL_RING_2_DATA
mov dword [tss.iopb], (tss.end - tss)
; Load task state segment
mov ax, SEL_TSS
ltr ax
; Load interrupt descriptor table
lidt [idtr]
; Jump to 32-bit ring 3 code segment
push dword SEL_RING_3_DATA ; ss
push dword ring_3_stack.top ; esp
push dword 0x00000000 ; eflags
push dword SEL_RING_3_CODE ; cs
push dword ring3 ; eip
iretd
ring0:
; Save incoming data segments
push ds
push es
push fs
push gs
; Load 32-bit ring 0 data segments
mov ax, SEL_RING_0_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Print a '0' in the top-left corner of the screen
mov word [0xB8000], 0x1F00 | '0'
; Restore incoming data segments
pop gs
pop fs
pop es
pop ds
; Return to ring 3
iretd
ring1:
; Save incoming data segments
push ds
push es
push fs
push gs
; Load 32-bit ring 0 data segments
mov ax, SEL_RING_1_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Print a '1' in the top-left corner of the screen
mov word [0xB8002], 0x1F00 | '1'
; Restore incoming data segments
pop gs
pop fs
pop es
pop ds
; Return to ring 3
iretd
ring2:
; Save incoming data segments
push ds
push es
push fs
push gs
; Load 32-bit ring 2 data segments
mov ax, SEL_RING_2_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Print a '2' in the top-left corner of the screen
mov word [0xB8004], 0x1F00 | '2'
; Restore incoming data segments
pop gs
pop fs
pop es
pop ds
; Return to ring 3
iretd
ring3:
; Load 32-bit ring 3 data segments
mov ax, SEL_RING_3_DATA
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Call a ring 0 function
int 0x00
; Call a ring 1 function
int 0x01
; Call a ring 2 function
int 0x02
; Print a '3' in the top-left corner of the screen
mov word [0xB8006], 0x1F00 | '3'
; Spin forever (can't use HLT in ring 3)
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ALIGN 8
gdt:
; Null descriptor
dq MAKE_DESC(0x00000000, 0x00000, 0x00, 0x0)
; 32-bit ring 0 code descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0x9A, 0xC)
; 32-bit ring 0 data descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0x92, 0xC)
; 32-bit ring 1 code descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xBA, 0xC)
; 32-bit ring 1 data descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xB2, 0xC)
; 32-bit ring 2 code descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xDA, 0xC)
; 32-bit ring 2 data descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xD2, 0xC)
; 32-bit ring 3 code descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xFA, 0xC)
; 32-bit ring 3 data descriptor
dq MAKE_DESC(0x00000000, 0xFFFFF, 0xF2, 0xC)
; Task state segment descriptor
dq MAKE_DESC(tss, tss.end - tss - 1, 0xE9, 0x4)
gdtr:
dw (gdtr - gdt - 1)
dd gdt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
idt:
; Ring 0 trap gate descriptor
dq MAKE_TRAP_GATE(SEL_RING_0_CODE, ADDRESS_OF(ring0), 0xEF)
; Ring 1 trap gate descriptor
dq MAKE_TRAP_GATE(SEL_RING_1_CODE, ADDRESS_OF(ring1), 0xEF)
; Ring 2 trap gate descriptor
dq MAKE_TRAP_GATE(SEL_RING_2_CODE, ADDRESS_OF(ring2), 0xEF)
idtr:
dw (idtr - idt - 1)
dd idt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
times (510 - ($ - $$)) \
db 0x00
dw 0xAA55
times (1474560 - ($ - $$)) \
db 0x00
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ABSOLUTE 0x00001000
tss:
.link: resw 1
resw 1
.esp0: resd 1
.ss0: resw 1
resw 1
.esp1: resd 1
.ss1: resw 1
resw 1
.esp2: resd 1
.ss2: resw 1
resw 1
.cr3: resd 1
.eip: resd 1
.eflags:resd 1
.eax: resd 1
.ecx: resd 1
.edx: resd 1
.ebx: resd 1
.esp: resd 1
.ebp: resd 1
.esi: resd 1
.edi: resd 1
.es: resw 1
resw 1
.cs: resw 1
resw 1
.ss: resw 1
resw 1
.ds: resw 1
resw 1
.fs: resw 1
resw 1
.gs: resw 1
resw 1
.ldtr: resw 1
resw 1
resw 1
.iopb: resw 1
.end:
ABSOLUTE 0x00002000
ring_0_stack:
resb 0x00001000
.top:
ABSOLUTE 0x00003000
ring_1_stack:
resb 0x00001000
.top:
ABSOLUTE 0x00004000
ring_2_stack:
resb 0x00001000
.top:
ABSOLUTE 0x00005000
ring_3_stack:
resb 0x00001000
.top:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment