diff options
| author | Pekka Paalanen <pq@iki.fi> | 2008-07-21 18:49:56 +0300 | 
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-10-14 10:33:50 +0200 | 
| commit | 611b1597680dd4a57896bcca1af0484be463c55e (patch) | |
| tree | 29356a233cad80a0bcf8c42c966b782726ac9892 | |
| parent | 5f87f1121895dc09d2d1c1db5f14af6aa4ce3e94 (diff) | |
| download | olio-linux-3.10-611b1597680dd4a57896bcca1af0484be463c55e.tar.xz olio-linux-3.10-611b1597680dd4a57896bcca1af0484be463c55e.zip  | |
x86: fix mmiotrace 8-bit register decoding
When SIL, DIL, BPL or SPL registers were used in MMIO, the datum
was extracted from AH, BH, CH, or DH, which are incorrect.
Signed-off-by: Pekka Paalanen <pq@iki.fi>
Cc: "Vegard Nossum" <vegard.nossum@gmail.com>
Cc: "Steven Rostedt" <srostedt@redhat.com>
Cc: proski@gnu.org
Cc: "Pekka Enberg"
	<penberg@cs.helsinki.fi>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | arch/x86/mm/pf_in.c | 121 | 
1 files changed, 84 insertions, 37 deletions
diff --git a/arch/x86/mm/pf_in.c b/arch/x86/mm/pf_in.c index efa1911e20c..df3d5c861cd 100644 --- a/arch/x86/mm/pf_in.c +++ b/arch/x86/mm/pf_in.c @@ -79,25 +79,34 @@ static unsigned int mw32[] = { 0xC7 };  static unsigned int mw64[] = { 0x89, 0x8B };  #endif /* not __i386__ */ -static int skip_prefix(unsigned char *addr, int *shorted, int *enlarged, -								int *rexr) +struct prefix_bits { +	unsigned shorted:1; +	unsigned enlarged:1; +	unsigned rexr:1; +	unsigned rex:1; +}; + +static int skip_prefix(unsigned char *addr, struct prefix_bits *prf)  {  	int i;  	unsigned char *p = addr; -	*shorted = 0; -	*enlarged = 0; -	*rexr = 0; +	prf->shorted = 0; +	prf->enlarged = 0; +	prf->rexr = 0; +	prf->rex = 0;  restart:  	for (i = 0; i < ARRAY_SIZE(prefix_codes); i++) {  		if (*p == prefix_codes[i]) {  			if (*p == 0x66) -				*shorted = 1; +				prf->shorted = 1;  #ifdef __amd64__  			if ((*p & 0xf8) == 0x48) -				*enlarged = 1; +				prf->enlarged = 1;  			if ((*p & 0xf4) == 0x44) -				*rexr = 1; +				prf->rexr = 1; +			if ((*p & 0xf0) == 0x40) +				prf->rex = 1;  #endif  			p++;  			goto restart; @@ -135,12 +144,12 @@ enum reason_type get_ins_type(unsigned long ins_addr)  {  	unsigned int opcode;  	unsigned char *p; -	int shorted, enlarged, rexr; +	struct prefix_bits prf;  	int i;  	enum reason_type rv = OTHERS;  	p = (unsigned char *)ins_addr; -	p += skip_prefix(p, &shorted, &enlarged, &rexr); +	p += skip_prefix(p, &prf);  	p += get_opcode(p, &opcode);  	CHECK_OP_TYPE(opcode, reg_rop, REG_READ); @@ -156,10 +165,11 @@ static unsigned int get_ins_reg_width(unsigned long ins_addr)  {  	unsigned int opcode;  	unsigned char *p; -	int i, shorted, enlarged, rexr; +	struct prefix_bits prf; +	int i;  	p = (unsigned char *)ins_addr; -	p += skip_prefix(p, &shorted, &enlarged, &rexr); +	p += skip_prefix(p, &prf);  	p += get_opcode(p, &opcode);  	for (i = 0; i < ARRAY_SIZE(rw8); i++) @@ -168,7 +178,7 @@ static unsigned int get_ins_reg_width(unsigned long ins_addr)  	for (i = 0; i < ARRAY_SIZE(rw32); i++)  		if (rw32[i] == opcode) -			return (shorted ? 2 : (enlarged ? 8 : 4)); +			return prf.shorted ? 2 : (prf.enlarged ? 8 : 4);  	printk(KERN_ERR "mmiotrace: Unknown opcode 0x%02x\n", opcode);  	return 0; @@ -178,10 +188,11 @@ unsigned int get_ins_mem_width(unsigned long ins_addr)  {  	unsigned int opcode;  	unsigned char *p; -	int i, shorted, enlarged, rexr; +	struct prefix_bits prf; +	int i;  	p = (unsigned char *)ins_addr; -	p += skip_prefix(p, &shorted, &enlarged, &rexr); +	p += skip_prefix(p, &prf);  	p += get_opcode(p, &opcode);  	for (i = 0; i < ARRAY_SIZE(mw8); i++) @@ -194,11 +205,11 @@ unsigned int get_ins_mem_width(unsigned long ins_addr)  	for (i = 0; i < ARRAY_SIZE(mw32); i++)  		if (mw32[i] == opcode) -			return shorted ? 2 : 4; +			return prf.shorted ? 2 : 4;  	for (i = 0; i < ARRAY_SIZE(mw64); i++)  		if (mw64[i] == opcode) -			return shorted ? 2 : (enlarged ? 8 : 4); +			return prf.shorted ? 2 : (prf.enlarged ? 8 : 4);  	printk(KERN_ERR "mmiotrace: Unknown opcode 0x%02x\n", opcode);  	return 0; @@ -238,7 +249,7 @@ enum {  #endif  }; -static unsigned char *get_reg_w8(int no, struct pt_regs *regs) +static unsigned char *get_reg_w8(int no, int rex, struct pt_regs *regs)  {  	unsigned char *rv = NULL; @@ -255,18 +266,6 @@ static unsigned char *get_reg_w8(int no, struct pt_regs *regs)  	case arg_DL:  		rv = (unsigned char *)®s->dx;  		break; -	case arg_AH: -		rv = 1 + (unsigned char *)®s->ax; -		break; -	case arg_BH: -		rv = 1 + (unsigned char *)®s->bx; -		break; -	case arg_CH: -		rv = 1 + (unsigned char *)®s->cx; -		break; -	case arg_DH: -		rv = 1 + (unsigned char *)®s->dx; -		break;  #ifdef __amd64__  	case arg_R8:  		rv = (unsigned char *)®s->r8; @@ -294,9 +293,55 @@ static unsigned char *get_reg_w8(int no, struct pt_regs *regs)  		break;  #endif  	default: -		printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no);  		break;  	} + +	if (rv) +		return rv; + +	if (rex) { +		/* +		 * If REX prefix exists, access low bytes of SI etc. +		 * instead of AH etc. +		 */ +		switch (no) { +		case arg_SI: +			rv = (unsigned char *)®s->si; +			break; +		case arg_DI: +			rv = (unsigned char *)®s->di; +			break; +		case arg_BP: +			rv = (unsigned char *)®s->bp; +			break; +		case arg_SP: +			rv = (unsigned char *)®s->sp; +			break; +		default: +			break; +		} +	} else { +		switch (no) { +		case arg_AH: +			rv = 1 + (unsigned char *)®s->ax; +			break; +		case arg_BH: +			rv = 1 + (unsigned char *)®s->bx; +			break; +		case arg_CH: +			rv = 1 + (unsigned char *)®s->cx; +			break; +		case arg_DH: +			rv = 1 + (unsigned char *)®s->dx; +			break; +		default: +			break; +		} +	} + +	if (!rv) +		printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no); +  	return rv;  } @@ -368,11 +413,12 @@ unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs)  	unsigned char mod_rm;  	int reg;  	unsigned char *p; -	int i, shorted, enlarged, rexr; +	struct prefix_bits prf; +	int i;  	unsigned long rv;  	p = (unsigned char *)ins_addr; -	p += skip_prefix(p, &shorted, &enlarged, &rexr); +	p += skip_prefix(p, &prf);  	p += get_opcode(p, &opcode);  	for (i = 0; i < ARRAY_SIZE(reg_rop); i++)  		if (reg_rop[i] == opcode) { @@ -392,10 +438,10 @@ unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs)  do_work:  	mod_rm = *p; -	reg = ((mod_rm >> 3) & 0x7) | (rexr << 3); +	reg = ((mod_rm >> 3) & 0x7) | (prf.rexr << 3);  	switch (get_ins_reg_width(ins_addr)) {  	case 1: -		return *get_reg_w8(reg, regs); +		return *get_reg_w8(reg, prf.rex, regs);  	case 2:  		return *(unsigned short *)get_reg_w32(reg, regs); @@ -422,11 +468,12 @@ unsigned long get_ins_imm_val(unsigned long ins_addr)  	unsigned char mod_rm;  	unsigned char mod;  	unsigned char *p; -	int i, shorted, enlarged, rexr; +	struct prefix_bits prf; +	int i;  	unsigned long rv;  	p = (unsigned char *)ins_addr; -	p += skip_prefix(p, &shorted, &enlarged, &rexr); +	p += skip_prefix(p, &prf);  	p += get_opcode(p, &opcode);  	for (i = 0; i < ARRAY_SIZE(imm_wop); i++)  		if (imm_wop[i] == opcode) {  |