Last active
December 15, 2018 02:44
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%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: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%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