Linux 0.12 OS. math - math_emulate.c

好像math模块的分析接近尾声了,其实不然,之前的内容可以归纳为它的核心算法介绍,而这篇文章将勾画出它的框架。所谓的‘框架’就是把浮点处理器能支持的所有指令给解析了一遍,多是多了点儿,可结构清晰简单。

一 math结构图

Linux 0.12 OS. math - math_emulate.c_第1张图片

二 FPU指令分类



Linux 0.12 OS. math - math_emulate.c_第2张图片



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

你可能感兴趣的:(Linux 0.12 OS. math - math_emulate.c)