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调用返回值复制到该地址。

最后恢复栈, 恢复其他寄存器,返回。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 上古卷轴5数值修改错了怎么办 小时候打针把屁股脂肪打扁了怎么办 大繁盛满腹市场2对话时闪退怎么办 月经来了一个月了还不停怎么办 对办公室的异性老师产生好感怎么办 上古卷轴5任务NPC老打我怎么办 1岁半宝宝走路内八字怎么办 最近几个月例假周期都25天怎么办 从pr导出的视频大小不一样怎么办 合作医疗收据丢了不给报销怎么办 沧州新生医院—老人腹胀了该怎么办 内痔疮术后一个月吃了点辣椒怎么办 肛周脓肿手术后大便干怎么办 月经半个月了还没干净怎么办 房东出租违建房不退房租怎么办 上海公租房住满5年后怎么办 监狱对死缓犯人延长转为无期怎么办 手机号码办理的宽带不想要了怎么办 朋友诈骗罪被关看守所了该怎么办 打架被拘留家里有孩子没人看怎么办 刑事拘留满37天给逮捕了怎么办 因打架被拘留十五天释放后会怎么办 犯罪人在拘留所生了小孩怎么办 我申请了进京证更换车辆怎么办 丈夫去世前想把财产留给妻子怎么办 假货中通代收货款发现是假货怎么办 注册志愿者时身份证被使用该怎么办 双眼皮贴贴的皮肤送了怎么办? 满60岁社保末满十五年怎么办 眼角膜少了一块怎么办应该吃什么 左右胸相差一个罩杯左右怎么办 穿一字肩的裙子没有无肩内衣怎么办 农业网柑橘被奄24小时怎么办 钱包被偷了小偷抓到了钱不认怎么办 快高考了很想学却没有动力怎么办? 孩子高三了学习状态不好怎么办 离婚时对方说把钱都花了怎么办 挂科太多学校不给毕业证怎么办 大专挂科太多学校让延期毕业怎么办 安卓手机老是收到垃圾短信怎么办 高铁站行李拉安检仪上应该怎么办