- UID
- 1029342
- 性别
- 男
|
1. 实模式下的中断机制x86 processor 在加电后被初始化为 real mode 也称为 real-address mode,关于实模式请详见文章:http://www.mouseos.com/arch/001.html
processor 执行的第一条指针在 0xFFFFFFF0 处,这个地址经过 North Bridge(北桥)和 South ridge(南桥)芯片配合解码,最终会访问到固化的 ROM 块,同时,经过别名机制映射在地址空间低端,实际上等于 ROM 被映射到地址空间最高端和低端位置。
此时在系统的内存里其实并不存在 BIOS 代码,ROM BIOS 的一部分职责是负责安装 BIOS 代码进入系统内存。
典型是这条指令就是 0xFFFFFFF0 处的 ROM BIOS 指令,执行后它将跳到 0x000FE05B 处,这条指令的作用很大:
- 更新 CS.base 使 processor 变成纯正的 real mode
- 跳转到低端内存,使之进入 1M 低端区域
前面说过,此时内存中也不存在 BIOS,也就是说 IVT(中断向量表)也是不存在的,中断系统此时是不可用的,那么由 ROM BIOS 设置 IVT 。
1.1 中断向量表(IVT)IDTR.base 被初始化为 0,ROM BIOS 将不会对 IDTR.base 进行更改,因此如果实模式 OS 不更改 IDTR.base 的值,这意味着 IVT 在 0 的位置上,典型的如: DOS 操作系统。
在保护模式下 IDTR.base 将向不再是中断向量表,而是中断描述符表。不再称为 IVT 而是 IDT。那是因为:
- 在实模式下,DITR.base 指向的表格项直接给出中断服务例程(Interrupt Service Routine)的入口地址。
- 在保护模式下,并不直接给出入口地址,而是门描述符(Interrupt/Trap/Task gate),从这些门描述符间接取得中断服务例程入口。
在 x86/x64 体系中允许有 256 个中断存在,中断号从 0x00 - 0xff,共 256 个中断,如图:
上面这个图是实模式下的 IVT 表,每个向量占据 4 个字节,中断服务例程入口是以 segmentffset 形式提供的,offset 在低端,segment 在高端,整个 IVT 表从地址 0x0 - 0x3FF,占据了 1024 个字节,即 1K bytes
1.2 改变中断向量表地址事实上,我们完全可以在实模式下更改 IVT 的地址,下面的代码作为示例:
; ****************************************************************
; * boot.asm for interrupt demo(real mode) on x86 *
; * *
; * Copyright (c) 2009-2011 *
; * All rights reserved. *
; * mik *
; * visit web site : www.mouseos.com *
; * bug send email : mik@mouseos.com *
; * *
; * *
; * version 0.01 by mik *
; ***************************************************************
BOOT_SEG equ 0x7c00 ; boot module load into BOOT_SEG
;----------------------------------------------------------
; Now, the processor is real mode
;----------------------------------------------------------
bits 16
org BOOT_SEG ; for int 19
start:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BOOT_SEG
mov si, msg1
call printmsg
sidt [old_IVT] ; save old IVT
mov cx, [old_IVT]
mov [new_IVT], cx ; limit of new IVT
mov dword [new_IVT+2], 0x8000 ; base of new IVT
mov si, [old_IVT+2]
mov di, [new_IVT+2]
rep movsb
lidt [new_IVT] ; set new IVT
mov si, msg2
call printmsg
jmp $
;-----------------------------------
; printmsg() - print message
;-----------------------------------
printmsg:
mov ah, 0x0e
xor bh, bh
print_loop:
lodsb
test al,al
jz done
int 0x10
jmp print_loop
done:
ret
old_IVT dw 0 ; limit of IVT
dd 0 ; base of IVT
new_IVT dw 0 ; limit of IVT
dd 0 ; base of IVT
msg1 db 'Hi, print message with old IVT', 10,13, 0
msg2 db 'Now,pirnt message with new IVT', 13, 10, 0
times 510-($-$$) db 0
dw 0xaa55
; end of boot.asm
| 在 vmware 上这段代码的执行结果如图:
这段代码在实模式下将 IVT 表复制到 0x8000 位置上,然后将 IVT 地址设为 0x8000 上,这样完全可以正常工作。正如代码上看到的,我做:
- 使用 sidt 指令取得 IDTR 寄存器的值,即 IVT 的 limit 和 base 值,保存在 old_IVT 里
- 设置 new_IVT 值,limit 等于 old_IVT 的 limit,base 设为 0x8000
- 将 IVT 表复制到 0x8000 处
- 使用 lidt 指令加载 IDTR 寄存器,即设 IVT 表在 0x8000 处
|
|