linux kernel -- intcall
来源:互联网 发布:get到了网络用语 编辑:程序博客网 时间:2024/05/17 04:51
注:只针对x86平台
Linux kernel在boot过程中,需要频繁调用BIOS中断。比如在实模式代码 – arch/x86/boot/main.c 中, 函数set_bios_mode()
, detect_memory()
等, 都依赖BIOS中断。
几乎所有的BIOS中断调用,都间接地通过函数intcall
(arch/x86/boot/bioscall.S)实现, 这个函数对BIOS调用进行了封装,防止在中断调用过程中,对寄存器产生的意外修改。
首先查看intcall
的函数声明,定义在arch/x86/boot.h:
(可以用这个工具定位某个变量或函数的定义、声明和使用:http://lxr.free-electrons.com/)
void intcall(u8 int_no, const struct biosregs *ireg, struct biosregs *oreg);
函数接受三个参数,int_no
表示中断号,ireg
表示input registers, 保存输入寄存器内容的结构体, oreg
表示output regisers, 保存输出寄存器内容的结构体。
结构体struct biosregs
定义在arch/x86/boot.h 中:
231 struct biosregs {232 union {233 struct {234 u32 edi;235 u32 esi;236 u32 ebp;237 u32 _esp;238 u32 ebx;239 u32 edx;240 u32 ecx;241 u32 eax;242 u32 _fsgs;243 u32 _dses;244 u32 eflags;245 };246 struct {247 u16 di, hdi;248 u16 si, hsi;249 u16 bp, hbp;250 u16 _sp, _hsp;251 u16 bx, hbx;252 u16 dx, hdx;253 u16 cx, hcx;254 u16 ax, hax;255 u16 gs, fs;256 u16 es, ds;257 u16 flags, hflags;258 };259 struct {260 u8 dil, dih, edi2, edi3;261 u8 sil, sih, esi2, esi3;262 u8 bpl, bph, ebp2, ebp3;263 u8 _spl, _sph, _esp2, _esp3;264 u8 bl, bh, ebx2, ebx3;265 u8 dl, dh, edx2, edx3;266 u8 cl, ch, ecx2, ecx3;267 u8 al, ah, eax2, eax3;268 };269 };270};
它定义了所有可能被BIOS中断修改的寄存器(BIOS中断程序会将若干个寄存器用作输入/输出). 下面是intcall的汇编代码:
16 .code1617 .section ".inittext","ax"18 .globl intcall19 .type intcall, @function20 intcall:21 /* Self-modify the INT instruction. Ugly, but works. */22 cmpb %al, 3f23 je 1f24 movb %al, 3f25 jmp 1f /* Synchronize pipeline */26 1:27 /* Save state */28 pushfl29 pushw %fs30 pushw %gs31 pushal3233 /* Copy input state to stack frame */34 subw $44, %sp35 movw %dx, %si36 movw %sp, %di37 movw $11, %cx38 rep; movsd3940 /* Pop full state from the stack */41 popal42 popw %gs43 popw %fs44 popw %es45 popw %ds46 popfl4748 /* Actual INT */49 .byte 0xcd /* INT opcode */50 3: .byte 05152 /* Push full state to the stack */53 pushfl54 pushw %ds55 pushw %es56 pushw %fs57 pushw %gs58 pushal5960 /* Re-establish C environment invariants */61 cld62 movzwl %sp, %esp63 movw %cs, %ax64 movw %ax, %ds65 movw %ax, %es6667 /* Copy output state from stack frame */68 movw 68(%esp), %di /* Original %cx == 3rd argument */69 andw %di, %di70 jz 4f71 movw %sp, %si72 movw $11, %cx73 rep; movsd74 4: addw $44, %sp7576 /* Restore state and return */77 popal78 popw %gs79 popw %fs80 popfl81 retl82 .size intcall, .-intcall
函数首先从第22行开始执行,22行至25行完成了一件事情:将参数int_no
中断号保存在标号3的地方(3f 中的f表示forward), 即第50行,因为实际的中断调用发生在49和50行(分别是中断指令的操作符和操作数)。
这里可能会有个疑问, 为什么参数int_no
是直接通过寄存器ax传递的,而不是从当前的栈中获得? 因为在编译Real-Mode代码时,GCC使用了选项 -mregparm=3, 参数的传递方式如下所示:
(arch/x86/Makefile)
first argument --> axsecond argument --> dxthird argument --> cxremaining arguments --> stack
所以在调用intcall时,第一个参数int_no
在寄存器ax中,第二个参数ireg
在寄存器dx中,第三个参数oreg
在寄存器cx中。
从27行到31行,保存当前的寄存器内容到栈: pushfl
表示将flag寄存器如栈(f
表示flag, l
表示long, 32位), pushw %fs; pushw %gs
分别将fs
, gs
寄存器入栈, 由于fs
, gs
寄存器都是16位, 因此push
的后缀使用w
(word), pushal
将所有通用寄存器入栈。
从33行到38行,将ireg
指向的内容(ireg
的值在%dx
中)拷贝到栈中,因为struct biosregs
定义了11个32位寄存器,所以在第34行,首先做的,是将栈指针%sp
减去44。
从40行到46行,将栈中的输入参数(*ireg
)通过pop操作,保存到相应的寄存器中。然后第49行和50行,完成实际的中断调用。
第52行,实际的中断调用返回,其输出保存在当前的各个寄存器中。从53行到58行,将BIOS中断调用的输出保存到栈中。
在把输出复制到oreg
之前,需要恢复C语言运行环境,将%ds, %es
恢复到之前的状态(内核启动时,将%ds, %es,%cs
设置为相同的值, 参考arch/x86/boot/header.S, 在BIOS调用过程中,%es, %ds
均有可能被修改,只有%cs
会保留)。同时,将%esp
的高位设置为零, 是因为C语言部分编译后使用%esp
??
为什么要通过这种方式恢复%ds, %es
, 而不是在一开始就将%ds, %es
入栈, 和%fs, %gs
一样,在函数返回前最后一步恢复呢? 因为在接下去的一步,需要利用movsd
指令,将BIOS调用的输出复制到oreg
指向的内存区域, 该指令以ds:si
作为源地址,es:di
作为目标地址进行, 所以必须在此之前,将%ds, %es
恢复。
从67行到73行, 首先获得最初%cx
的值(此时,它被保存在栈中), 因为输出地址oreg
作为第三个参数,传递给了%cx
。 然后将栈中的BIOS调用返回值复制到该地址。
最后恢复栈, 恢复其他寄存器,返回。
- linux kernel -- intcall
- linux kernel
- Linux Kernel
- Linux kernel
- Linux kernel
- linux kernel
- linux kernel
- Linux Kernel
- Linux Kernel
- "android linux kernel" VS "standard linux kernel"
- "android linux kernel" VS "standard linux kernel"
- qemu linux kernel & vmware linux kernel
- Linux kernel management style
- Linux Kernel Makefiles(转)
- Linux Kernel拒绝服务漏洞
- Linux Kernel Configuration
- LINUX KERNEL CODING STYLE
- Linux Kernel Hack (1)
- 【斜率DP】BZOJ 1010:玩具装箱
- 创建序号递增的文件序列
- Revit二次开发之“族”操作
- iOS解决Cell的分割线宽度不满屏的方法
- Android自定义组合控件
- linux kernel -- intcall
- 虚拟机CentOS6.3环境下配置ip地址(慕课学习笔记)
- Android ViewPager Parallax视觉差切换动画
- IOS 程序执行
- java中的反射
- POJ 1274 The Perfect Stall(二分匹配-hungary)
- Liquidfun的编译与运行
- Android 之 注解
- 【斜率DP】BZOJ 1911:特别行动队