好像math模块的分析接近尾声了,其实不然,之前的内容可以归纳为它的核心算法介绍,而这篇文章将勾画出它的框架。所谓的‘框架’就是把浮点处理器能支持的所有指令给解析了一遍,多是多了点儿,可结构清晰简单。
一 math结构图
二 FPU指令分类
三 code部分
I. __math_abort
void __math_abort(struct info * info, unsigned int signal) { EIP = ORIG_EIP; /* 重新执行协处理器指令 */ current->signal |= signal; /* 发送信号 */ __asm__("movl %0,%%esp ; ret"::"g" ((long) info)); /* 直接扔掉先前的栈数据, 返回到开始模拟处的地址去执行。 */ }
II. fpop
static void fpop(void) { unsigned long tmp; tmp = I387.swd & 0xffffc7ff; /* top of stack */ I387.swd += 0x00000800; /* top = (top +1)%8 */ I387.swd &= 0x00003800; I387.swd |= tmp; }
III. fpush
static void fpush(void) { unsigned long tmp; tmp = I387.swd & 0xffffc7ff; /* top of stack */ I387.swd += 0x00003800; /* top = (top-1 +8)%8 */ I387.swd &= 0x00003800; I387.swd |= tmp; }
IV. fxchg
static void fxchg(temp_real_unaligned * a, temp_real_unaligned * b) { temp_real_unaligned c; c = *a; *a = *b; *b = c; }
V. __st
static temp_real_unaligned * __st(int i) { i += I387.swd >> 11; /* top no. [bit13~11] */ i &= 7; /* i = i%7 */ return (temp_real_unaligned *) (i*10 + (char *)(I387.st_space)); }
VI. math_emulate
void math_emulate(long ___false) { if (!current->used_math) { /* 如果该进程还没有使用过数学协处理器,需要先初始化一下 */ current->used_math = 1; I387.cwd = 0x037f; /* 初始化控制寄存器 */ I387.swd = 0x0000; /* 初始化状态寄存器 */ I387.twd = 0x0000; /* 初始化标示寄存器 */ } /* &___false points to info->___orig_eip, so subtract 1 to get info */ do_emu((struct info *) ((&___false) - 1)); /* 开始模拟,注意这个info结构体 的首个成员是'___math_ret',是math_emulate的返回地址,它已经被压栈了, 而'___false'是栈的第一个元素的首地址,所以info结构体的地址需要___false减去 一个指针的大小(栈是向低地址延展的)。 */ }
VII. do_emu
static void do_emu(struct info * info) { unsigned short code; temp_real tmp; char * address; if (I387.cwd & I387.swd & 0x3f) /* 有异常 */ I387.swd |= 0x8000; /* 置忙标志 */ else I387.swd &= 0x7fff; /* 清忙标志 */ ORIG_EIP = EIP; /* 保存当前协处理器指令地址 */ /* 0x0007 means user code space */ if (CS != 0x000F) { /* 确保是在用户态 */ printk("math_emulate: %04x:%08x\n\r",CS,EIP); panic("Math emulation needed in kernel"); } code = get_fs_word((unsigned short *) EIP); /* 取两个字节的指令 */ bswapw(code); /* 指令的第一个字节被放在了code的低字节,反之,指令 的第二个字节被放在了高字节,bswapw将高低字节交换,使指令顺序正确 */ code &= 0x7ff; /* 浮点处理器指令的第一个字节的高5bit都是相同的0b11011, 因此这里将前面的5位去掉了,合成的时候我们把指令加上0xb800即可 */ I387.fip = EIP; /* 浮点指令地址段偏移 */ *(unsigned short *) &I387.fcs = CS; /* 浮点指令代码段选择子 */ *(1+(unsigned short *) &I387.fcs) = code; /* 浮点指令操作码opcode(11bit) */ EIP += 2; /* 移动代码指令指针 */ switch (code) { /* 下面开始对浮点指令分类处理 */ case 0x1d0: /* d9 d0, fnop */ return; case 0x1d1: case 0x1d2: case 0x1d3: case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7: math_abort(info,1<<(SIGILL-1)); case 0x1e0: /* d9 e0, fchs */ ST(0).exponent ^= 0x8000; return; case 0x1e1: /* d9 e1, fabs */ ST(0).exponent &= 0x7fff; return; case 0x1e2: case 0x1e3: math_abort(info,1<<(SIGILL-1)); case 0x1e4: /* d9 e4, ftst */ ftst(PST(0)); return; case 0x1e5: printk("fxam not implemented\n\r"); math_abort(info,1<<(SIGILL-1)); case 0x1e6: case 0x1e7: math_abort(info,1<<(SIGILL-1)); case 0x1e8: /* d9 e8, fld1 */ fpush(); ST(0) = CONST1; return; case 0x1e9: /* d9 e9, fld2t */ fpush(); ST(0) = CONSTL2T; return; case 0x1ea: /* d9 ea, fldl2e */ fpush(); ST(0) = CONSTL2E; return; case 0x1eb: /* d9 eb, fldpi */ fpush(); ST(0) = CONSTPI; return; case 0x1ec: /* d9 ec, fldlg2 */ fpush(); ST(0) = CONSTLG2; return; case 0x1ed: /* d9 ed, fldln2 */ fpush(); ST(0) = CONSTLN2; return; case 0x1ee: /* d9 ee, fldz */ fpush(); ST(0) = CONSTZ; return; case 0x1ef: math_abort(info,1<<(SIGILL-1)); case 0x1f0: case 0x1f1: case 0x1f2: case 0x1f3: case 0x1f4: case 0x1f5: case 0x1f6: case 0x1f7: case 0x1f8: case 0x1f9: case 0x1fa: case 0x1fb: case 0x1fc: case 0x1fd: case 0x1fe: case 0x1ff: printk("%04x fxxx not implemented\n\r",code + 0xc800); math_abort(info,1<<(SIGILL-1)); case 0x2e9: /* da e9, fucompp */ fucom(PST(1),PST(0)); fpop(); fpop(); return; case 0x3d0: case 0x3d1: return; case 0x3e2: /* db e2, fnclex */ I387.swd &= 0x7f00; return; case 0x3e3: /* db e3, fninit */ I387.cwd = 0x037f; I387.swd = 0x0000; I387.twd = 0x0000; return; case 0x3e4: return; case 0x6d9: /* de d9, fcompp */ fcom(PST(1),PST(0)); fpop(); fpop(); return; case 0x7e0: /* df e0, fnstsw */ *(short *) &EAX = I387.swd; return; } switch (code >> 3) { /* d8 xx */ case 0x18: /* d8 c0+i, fadd st,st(i), st := st + st(i). */ fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x19: /* d8 c8+i, fmul st,st(i), st := st*st(i). */ fmul(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1a: /* d8 d0+i, fcom st(i), compare st with st(i). */ // fcom(PST(code & 7),PST(0)); fcom(PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1b: /* d8 d8+i, fcomp st(i), compare st with st(i), pop. */ // fcom(PST(code & 7),PST(0)); fcom(PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); fpop(); return; case 0x1c: /* d8 e0+i, fsub st,st(i), st := st - st(i). */ real_to_real(&ST(code & 7),&tmp); tmp.exponent ^= 0x8000; fadd(PST(0),&tmp,&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1d: /* d8 e8+i, fsubr st,st(i), st := st(i) - st. */ ST(0).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1e: /* d8 f0+i, fdiv st,st(i), st := st/st(i). */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x1f: /* d8 f8+i, fdivr st,st(i), st := st(i)/st. */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 0x38: /* d9 c0+i, fld st(i), push, st := old st(i). */ fpush(); ST(0) = ST((code & 7)+1); return; case 0x39: /* d9 c8+i, fxch st(i), exchange st and st(i). */ fxchg(&ST(0),&ST(code & 7)); return; case 0x3b: /* d9 d8+i, NULL */ ST(code & 7) = ST(0); fpop(); return; case 0x98: /* dc c0+i, fadd st(i),st, st(i) := st(i) + st. */ fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x99: /* dc c8+i, fmul st(i),st, st(i) := st(i)*st. */ fmul(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x9a: /* dc d0+i, NULL */ fcom(PST(code & 7),PST(0)); return; case 0x9b: /* dc d8+i, NULL */ fcom(PST(code & 7),PST(0)); fpop(); return; case 0x9c: /* dc e0+i, fsubr st(i),st, st(i) := st - st(i). */ ST(code & 7).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x9d: /* dc e8+i, fsub st(i),st, st(i) := st(i) - st. */ real_to_real(&ST(0),&tmp); tmp.exponent ^= 0x8000; fadd(PST(code & 7),&tmp,&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x9e: /* dc f0+i, fdivr st(i),st, st(i) := st/st(i). */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0x9f: /* dc f8+i, fdiv st(i),st, st(i) := st(i)/st. */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); return; case 0xb8: printk("ffree not implemented\n\r"); math_abort(info,1<<(SIGILL-1)); case 0xb9: /* dd c8+i, NULL */ fxchg(&ST(0),&ST(code & 7)); return; case 0xba: /* dd d0+i, fst st(i), st(i) := st. */ ST(code & 7) = ST(0); return; case 0xbb: /* dd d8+i, fstp st(i), st(i) := st, pop. */ ST(code & 7) = ST(0); fpop(); return; case 0xbc: /* dd e0+i, fucom st(i), unordered compare st with st(i). */ fucom(PST(code & 7),PST(0)); return; case 0xbd: /* dd e8+i, fucomp st(i), unordered compare st with st(i), pop. */ fucom(PST(code & 7),PST(0)); fpop(); return; case 0xd8: /* de c0+i, faddp st(i),st, st(i) := st(i) + st, pop. */ fadd(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xd9: /* de c8+i, fmulp st(i),st, st(i) := st(i)*st, pop. */ fmul(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xda: /* de d0+i, NULL */ fcom(PST(code & 7),PST(0)); fpop(); return; case 0xdc: /* de e0+i, fsubrp st(i),st, st(i) := st - st(i), pop. */ ST(code & 7).exponent ^= 0x8000; fadd(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xdd: /* de e8+i, fsubp st(i),st, st(i) := st(i) - st, pop. */ real_to_real(&ST(0),&tmp); tmp.exponent ^= 0x8000; fadd(PST(code & 7),&tmp,&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xde: /* de f0+i, fdivrp st(i),st, st(i) := st/st(i), pop. */ fdiv(PST(0),PST(code & 7),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xdf: /* de f8+i, fdivp st(i),st, st(i) := st(i)/st, pop. */ fdiv(PST(code & 7),PST(0),&tmp); real_to_real(&tmp,&ST(code & 7)); fpop(); return; case 0xf8: printk("ffree not implemented\n\r"); math_abort(info,1<<(SIGILL-1)); fpop(); return; case 0xf9: /* df c8+i, NULL */ fxchg(&ST(0),&ST(code & 7)); return; case 0xfa: /* df d0+i, NULL */ case 0xfb: ST(code & 7) = ST(0); fpop(); return; } switch ((code>>3) & 0xe7) { case 0x22: /* d9 /2, fst m32r, m32r := st. */ put_short_real(PST(0),info,code); return; case 0x23: /* d9 /3, fstp m32r, m32r := st, pop. */ put_short_real(PST(0),info,code); fpop(); return; case 0x24: /* d9 /4, fldenv m14/28by, environment := m14by or m28by. */ address = ea(info,code); for (code = 0 ; code < 7 ; code++) { /* 4*7 = 28 bytes */ ((long *) & I387)[code] = get_fs_long((unsigned long *) address); address += 4; } return; case 0x25: /* d9 /5, fldcw m2by, control word := m2by. */ address = ea(info,code); *(unsigned short *) &I387.cwd = get_fs_word((unsigned short *) address); return; case 0x26: /* d9 /6, fnstenv m14/28by, m14/28by := environment. */ address = ea(info,code); verify_area(address,28); for (code = 0 ; code < 7 ; code++) { put_fs_long( ((long *) & I387)[code], (unsigned long *) address); address += 4; } return; case 0x27: /* d9 /7, fnstcw m2by, m2by := control word. */ address = ea(info,code); verify_area(address,2); put_fs_word(I387.cwd,(short *) address); return; case 0x62: /* db /2, fist m32j, m32j := st. */ put_long_int(PST(0),info,code); return; case 0x63: /* db /3, fistp m32j, m32j := st, pop. */ put_long_int(PST(0),info,code); fpop(); return; case 0x65: /* db /5, fld m80r, push, st := m80r, [fldt m80r]. */ fpush(); get_temp_real(&tmp,info,code); real_to_real(&tmp,&ST(0)); return; case 0x67: /* db /7, fstp m80r, m80r := st, pop. */ put_temp_real(PST(0),info,code); fpop(); return; case 0xa2: /* dd /2, fst m64r, m64r := st. */ put_long_real(PST(0),info,code); return; case 0xa3: /* dd /3, fstp m64r, m64r := st, pop. */ put_long_real(PST(0),info,code); fpop(); return; case 0xa4: /* dd /4, frstor m94/108by, machine state := m94by or m108by. */ address = ea(info,code); for (code = 0 ; code < 27 ; code++) { /* 4*27 = 108 bytes */ ((long *) & I387)[code] = get_fs_long((unsigned long *) address); address += 4; } return; case 0xa6: /* dd /6, fnsave m94/108by, m94/108by := machine state. */ address = ea(info,code); verify_area(address,108); for (code = 0 ; code < 27 ; code++) { put_fs_long( ((long *) & I387)[code], (unsigned long *) address); address += 4; } I387.cwd = 0x037f; I387.swd = 0x0000; I387.twd = 0x0000; return; case 0xa7: /* dd /7, fnstsw m2by, m2by := status word. */ address = ea(info,code); verify_area(address,2); put_fs_word(I387.swd,(short *) address); return; case 0xe2: /* df /2, fist m16j, m16j := st. */ put_short_int(PST(0),info,code); return; case 0xe3: /* df /3, fistp m16j, m16j := st, pop. */ put_short_int(PST(0),info,code); fpop(); return; case 0xe4: /* about BCD */ fpush(); get_BCD(&tmp,info,code); real_to_real(&tmp,&ST(0)); return; case 0xe5: /* df /5, fild m64j, push, st := m64j, [fildll m64j]. */ fpush(); get_longlong_int(&tmp,info,code); real_to_real(&tmp,&ST(0)); return; case 0xe6: /* about BCD */ put_BCD(PST(0),info,code); fpop(); return; case 0xe7: /* df /7, fistp m64j, m64j := st, pop. */ put_longlong_int(PST(0),info,code); fpop(); return; } switch (code >> 9) { case 0: /* d8 /n, fxxx m32r */ get_short_real(&tmp,info,code); break; case 1: /* da /n, fxxx m32j */ get_long_int(&tmp,info,code); break; case 2: /* dc /n, fxxx m64r */ get_long_real(&tmp,info,code); break; case 4: // case 3: /* de /n, fxxx m16j */ get_short_int(&tmp,info,code); } switch ((code>>3) & 0x27) { /* d8|da|dc|de /0~7 */ case 0: fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 1: fmul(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 2: fcom(&tmp,PST(0)); return; case 3: fcom(&tmp,PST(0)); fpop(); return; case 4: tmp.exponent ^= 0x8000; fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 5: ST(0).exponent ^= 0x8000; fadd(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; case 6: fdiv(PST(0),&tmp,&tmp); real_to_real(&tmp,&ST(0)); return; case 7: fdiv(&tmp,PST(0),&tmp); real_to_real(&tmp,&ST(0)); return; } if ((code & 0x138) == 0x100) { /* d9|db|dd|df /0, fld */ fpush(); real_to_real(&tmp,&ST(0)); return; } printk("Unknown math-insns: %04x:%08x %04x\n\r",CS,EIP,code); math_abort(info,1<<(SIGFPE-1)); /* 发送浮点异常信号 */ }