📄 pm9a.asm
字号:
; pm9a.asm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; pm9a.asm - protected-mode demo code
; Christopher Giese <geezer[AT]execpc.com>
;
; Release date 8/27/98. Distribute freely. ABSOLUTELY NO WARRANTY.
; Assemble with NASM: nasm -o pm9a.com pm9a.asm
;
; Demonstrates:
; - Transitions between Ring 0 (system/kernel mode) and Ring 3
; (user mode).
; Notes:
; - Code to return to real mode removed for clarity.
; Put it back in if you like.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[SECTION .text]
[ORG 0x100]
[BITS 16]
; set base of code/data descriptors to CS<<4/DS<<4 (CS=DS)
xor ebx,ebx
mov bx,cs ; EBX=segment
shl ebx,4 ; << 4
lea eax,[ebx] ; EAX=linear address of segment base
mov [gdt2 + 2],ax
mov [gdt3 + 2],ax
mov [gdt4 + 2],ax
mov [gdt5 + 2],ax
shr eax,16
mov [gdt2 + 4],al
mov [gdt3 + 4],al
mov [gdt4 + 4],al
mov [gdt5 + 4],al
mov [gdt2 + 7],ah
mov [gdt3 + 7],ah
mov [gdt4 + 7],ah
mov [gdt5 + 7],ah
; fix up TSS entry, too. x86 task-switch mechanism is not used, but the
; TSS is needed to specify the location of system (Ring 0) and user
; (Ring 3) stack pointers.
lea eax,[ebx + tss] ; EAX=linear address of tss
mov [gdt6 + 2],ax
shr eax,16
mov [gdt6 + 4],al
mov [gdt6 + 7],ah
; point gdtr to the gdt, idtr to the idt
lea eax,[ebx + gdt] ; EAX=linear address of gdt
mov [gdtr + 2],eax
lea eax,[ebx + idt] ; EAX=linear address of idt
mov [idtr + 2],eax
; clear NT bit (so iret does normal iret, instead of task-switch),
; set IOPL=00, and set IF=0 (disable interrupts)
push dword 0
popfd
; load GDT and IDT for full protected mode
lgdt [gdtr]
lidt [idtr]
; set PE [protected mode enable] bit and go
mov eax,cr0
or al,1
mov cr0,eax
jmp SYS_CODE_SEL:do_pm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32-bit protected mode, ring 0 (kernel/system mode)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 32]
do_pm: mov ax,SYS_DATA_SEL
mov ds,ax
mov ss,ax
nop
mov es,ax
mov fs,ax
mov gs,ax
; load task register.
mov ax,USER_TSS
ltr ax
; print starting msg
lea esi,[msg1]
call wrstr
; set Ring 0 and Ring 3 stack pointers in the TSS
mov [tss_esp0],esp ; ring 0 task uses system stack
lea eax,[esp - 256]
mov [tss_esp],eax ; ring 3 task stack is 256 bytes lower
; move_to_user_mode, from Linux 0.01 (linux/include/asm/system.h):
mov eax,esp
push dword USER_DATA_SEL ; SS
push eax ; ESP
push dword 0x00 ; EFLAGS
push dword USER_CODE_SEL ; CS
lea eax,[ring3]
push eax ; EIP
iret ; jumps to ring3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32-bit protected mode, ring 3 (user mode)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ring3: mov ax,USER_DATA_SEL
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
; print CS register to prove we're in Ring 3
xor eax,eax
mov ax,cs
call hexout
lea esi,[msg2]
call wrstr
; demonstrate interrupt 0x20 (syscall)
int 0x20
; another message
xor eax,eax
mov ax,cs
call hexout
lea esi,[msg4]
call wrstr
; DPL of all IDT entries except int 0x20 is Ring 0, so int 0x18 causes
; GPF instead of int 0x18. It doesn't matter -- all interrupts but
; syscall point to ring0, below.
int 0x18
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32-bit protected mode, ring 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ring0: mov ax,SYS_DATA_SEL
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
; print CS register to prove it
xor eax,eax
mov ax,cs
call hexout
lea esi,[msg5]
call wrstr
; freeze
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; "You are the Syscall. You are of Kernel."
;
; (My apologies to those who don't watch DS9 :)
;
; The CPU pushes user-mode SS, ESP, EFLAGS, CS, and EIP;
; then sets SS:ESP and CS:EIP. We have to do the rest.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
syscall:push gs ; save other user-mode seg regs
push fs
push es
push ds
pusha ; save user-mode GP regs
mov ax,ss ; SS was set to SYS_DATA_SEL by interrupt
mov ds,ax
mov es,ax
mov fs,ax
; prove that the ring transition worked
xor eax,eax
mov ax,cs
call hexout
lea esi,[msg3]
call wrstr
popa
pop ds
pop es
pop fs
pop gs
iret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; QuickHack(TM) character-output video routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrch: push gs
push ebx
mov bx,LINEAR_SEL ; RPL=3 and DPL=3, so both Ring 0
mov gs,bx ; and Ring 3 code can use it.
xor ebx,ebx
mov bx,[CsrPos]
add ebx,0xB8000
mov [gs:ebx],al
inc word [CsrPos]
inc word [CsrPos]
pop ebx
pop gs
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; string-output video routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrstr: push esi
push eax
cld
jmp wrstr2
wrstr1: call wrch
wrstr2: lodsb
or al,al
jne wrstr1
pop eax
pop esi
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; an even uglier hex value output routine...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
hexout: push eax
shr eax,16
call hexo2
pop eax
hexo2: push eax
mov al,ah
call hexo3
pop eax
hexo3: push eax
shr al,4
call hexo4
pop eax
hexo4: and al,0x0F
add al,'0'
cmp al,'9'
jbe hexo5
add al,7
hexo5: call wrch
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CsrPos: dw 0
msg1: db "Greetings from 32-bit Ring 0 (system/ker"
db "nel mode). CS=0x", 0
msg2: db " - lower 2 bits of CS indic"
db "ate we're in Ring 3 (user mode). CS=0x", 0
msg3: db " - syscall switched us back"
db " to Ring 0. CS=0x", 0
msg4: db " - back to user mode. Help,"
db " I'm getting dizzy... CS=0x", 0
msg5: db " - back to Ring 0. System halted.", 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 16-bit limit/32-bit linear base address of GDT and IDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
gdtr: dw gdt_end - gdt - 1 ; GDT limit
dd gdt ; linear, physical address of GDT
idtr: dw idt_end - idt - 1 ; IDT limit
dd idt ; linear, physical address of IDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; global descriptor table (GDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; null descriptor
gdt: dw 0 ; limit 15:0
dw 0 ; base 15:0
db 0 ; base 23:16
db 0 ; type
db 0 ; limit 19:16, flags
db 0 ; base 31:24
; linear data segment descriptor
LINEAR_SEL equ $-gdt+3 ; RPL=3
dw 0xFFFF ; limit 0xFFFFF
dw 0 ; base for this one is always 0
db 0
db 0xF2 ; present, DPL=ring 3, data, expand-up, writable
db 0xFC ; page-granular, 32-bit
db 0
; code segment descriptor
SYS_CODE_SEL equ $-gdt
gdt2: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x9A ; present, ring 0, code, non-conforming, readable
db 0xFC
db 0
; data segment descriptor
SYS_DATA_SEL equ $-gdt
gdt3: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
db 0xFC
db 0
; code segment descriptor
USER_CODE_SEL equ $-gdt+3
gdt4: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0xFA ; present, ring 3, code, non-conforming, readable
db 0xFC
db 0
; data segment descriptor
USER_DATA_SEL equ $-gdt+3
gdt5: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0xF2 ; present, ring 3, data, expand-up, writable
db 0xFC
db 0
; user TSS
USER_TSS equ $-gdt
gdt6: dw 103
dw 0 ; set to tss
db 0
db 0xE9 ; present, ring 3, 32-bit available TSS
db 0
db 0
gdt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; interrupt descriptor table (IDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32 reserved interrupts:
idt: dw ring0 ; entry point 15:0
dw SYS_CODE_SEL ; selector
db 0 ; word count
db 0x8E ; type (32-bit Ring 0 interrupt gate)
dw 0 ; entry point 31:16 (XXX - ring0 >> 16)
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw ring0
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
; a DPL=3 interrupt gate, used for syscalls
dw syscall
dw SYS_CODE_SEL
db 0
db 0xEE ; DPL=3
dw 0
idt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; task state segment
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
tss: dw 0, 0 ; back link
tss_esp0:
dd 0 ; ESP0
dw SYS_DATA_SEL, 0 ; SS0, reserved
dd 0 ; ESP1
dw 0, 0 ; SS1, reserved
dd 0 ; ESP2
dw 0, 0 ; SS2, reserved
dd 0 ; CR3
dd 0, 0 ; EIP, EFLAGS (EFLAGS=0x200 for ints)
dd 0, 0, 0, 0 ; EAX, ECX, EDX, EBX
tss_esp:
dd 0, 0, 0, 0 ; ESP, EBP, ESI, EDI
dw 0, 0 ; ES, reserved
dw 0, 0 ; CS, reserved
dw USER_DATA_SEL, 0 ; SS, reserved
dw 0, 0 ; DS, reserved
dw 0, 0 ; FS, reserved
dw 0, 0 ; GS, reserved
dw 0, 0 ; LDT, reserved
dw 0, 0 ; debug, IO perm. bitmap
end:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -