好像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)); /* 发送浮点异常信号 */
}