[funini.com] -> [kei@sodan] -> Kernel Reading

root/arch/x86/mm/pf_in.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. skip_prefix
  2. get_opcode
  3. get_ins_type
  4. get_ins_reg_width
  5. get_ins_mem_width
  6. get_reg_w8
  7. get_reg_w32
  8. get_ins_reg_val
  9. get_ins_imm_val

/*
 *  Fault Injection Test harness (FI)
 *  Copyright (C) Intel Crop.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 *
 */

/*  Id: pf_in.c,v 1.1.1.1 2002/11/12 05:56:32 brlock Exp
 *  Copyright by Intel Crop., 2002
 *  Louis Zhuang (louis.zhuang@intel.com)
 *
 *  Bjorn Steinbrink (B.Steinbrink@gmx.de), 2007
 */

#include <linux/module.h>
#include <linux/ptrace.h> /* struct pt_regs */
#include "pf_in.h"

#ifdef __i386__
/* IA32 Manual 3, 2-1 */
static unsigned char prefix_codes[] = {
        0xF0, 0xF2, 0xF3, 0x2E, 0x36, 0x3E, 0x26, 0x64,
        0x65, 0x2E, 0x3E, 0x66, 0x67
};
/* IA32 Manual 3, 3-432*/
static unsigned int reg_rop[] = {
        0x8A, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F
};
static unsigned int reg_wop[] = { 0x88, 0x89 };
static unsigned int imm_wop[] = { 0xC6, 0xC7 };
/* IA32 Manual 3, 3-432*/
static unsigned int rw8[] = { 0x88, 0x8A, 0xC6 };
static unsigned int rw32[] = {
        0x89, 0x8B, 0xC7, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F
};
static unsigned int mw8[] = { 0x88, 0x8A, 0xC6, 0xB60F, 0xBE0F };
static unsigned int mw16[] = { 0xB70F, 0xBF0F };
static unsigned int mw32[] = { 0x89, 0x8B, 0xC7 };
static unsigned int mw64[] = {};
#else /* not __i386__ */
static unsigned char prefix_codes[] = {
        0x66, 0x67, 0x2E, 0x3E, 0x26, 0x64, 0x65, 0x36,
        0xF0, 0xF3, 0xF2,
        /* REX Prefixes */
        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
        0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
};
/* AMD64 Manual 3, Appendix A*/
static unsigned int reg_rop[] = {
        0x8A, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F
};
static unsigned int reg_wop[] = { 0x88, 0x89 };
static unsigned int imm_wop[] = { 0xC6, 0xC7 };
static unsigned int rw8[] = { 0xC6, 0x88, 0x8A };
static unsigned int rw32[] = {
        0xC7, 0x89, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F
};
/* 8 bit only */
static unsigned int mw8[] = { 0xC6, 0x88, 0x8A, 0xB60F, 0xBE0F };
/* 16 bit only */
static unsigned int mw16[] = { 0xB70F, 0xBF0F };
/* 16 or 32 bit */
static unsigned int mw32[] = { 0xC7 };
/* 16, 32 or 64 bit */
static unsigned int mw64[] = { 0x89, 0x8B };
#endif /* not __i386__ */

static int skip_prefix(unsigned char *addr, int *shorted, int *enlarged,
                                                                int *rexr)
{
        int i;
        unsigned char *p = addr;
        *shorted = 0;
        *enlarged = 0;
        *rexr = 0;

restart:
        for (i = 0; i < ARRAY_SIZE(prefix_codes); i++) {
                if (*p == prefix_codes[i]) {
                        if (*p == 0x66)
                                *shorted = 1;
#ifdef __amd64__
                        if ((*p & 0xf8) == 0x48)
                                *enlarged = 1;
                        if ((*p & 0xf4) == 0x44)
                                *rexr = 1;
#endif
                        p++;
                        goto restart;
                }
        }

        return (p - addr);
}

static int get_opcode(unsigned char *addr, unsigned int *opcode)
{
        int len;

        if (*addr == 0x0F) {
                /* 0x0F is extension instruction */
                *opcode = *(unsigned short *)addr;
                len = 2;
        } else {
                *opcode = *addr;
                len = 1;
        }

        return len;
}

#define CHECK_OP_TYPE(opcode, array, type) \
        for (i = 0; i < ARRAY_SIZE(array); i++) { \
                if (array[i] == opcode) { \
                        rv = type; \
                        goto exit; \
                } \
        }

enum reason_type get_ins_type(unsigned long ins_addr)
{
        unsigned int opcode;
        unsigned char *p;
        int shorted, enlarged, rexr;
        int i;
        enum reason_type rv = OTHERS;

        p = (unsigned char *)ins_addr;
        p += skip_prefix(p, &shorted, &enlarged, &rexr);
        p += get_opcode(p, &opcode);

        CHECK_OP_TYPE(opcode, reg_rop, REG_READ);
        CHECK_OP_TYPE(opcode, reg_wop, REG_WRITE);
        CHECK_OP_TYPE(opcode, imm_wop, IMM_WRITE);

exit:
        return rv;
}
#undef CHECK_OP_TYPE

static unsigned int get_ins_reg_width(unsigned long ins_addr)
{
        unsigned int opcode;
        unsigned char *p;
        int i, shorted, enlarged, rexr;

        p = (unsigned char *)ins_addr;
        p += skip_prefix(p, &shorted, &enlarged, &rexr);
        p += get_opcode(p, &opcode);

        for (i = 0; i < ARRAY_SIZE(rw8); i++)
                if (rw8[i] == opcode)
                        return 1;

        for (i = 0; i < ARRAY_SIZE(rw32); i++)
                if (rw32[i] == opcode)
                        return (shorted ? 2 : (enlarged ? 8 : 4));

        printk(KERN_ERR "mmiotrace: Unknown opcode 0x%02x\n", opcode);
        return 0;
}

unsigned int get_ins_mem_width(unsigned long ins_addr)
{
        unsigned int opcode;
        unsigned char *p;
        int i, shorted, enlarged, rexr;

        p = (unsigned char *)ins_addr;
        p += skip_prefix(p, &shorted, &enlarged, &rexr);
        p += get_opcode(p, &opcode);

        for (i = 0; i < ARRAY_SIZE(mw8); i++)
                if (mw8[i] == opcode)
                        return 1;

        for (i = 0; i < ARRAY_SIZE(mw16); i++)
                if (mw16[i] == opcode)
                        return 2;

        for (i = 0; i < ARRAY_SIZE(mw32); i++)
                if (mw32[i] == opcode)
                        return shorted ? 2 : 4;

        for (i = 0; i < ARRAY_SIZE(mw64); i++)
                if (mw64[i] == opcode)
                        return shorted ? 2 : (enlarged ? 8 : 4);

        printk(KERN_ERR "mmiotrace: Unknown opcode 0x%02x\n", opcode);
        return 0;
}

/*
 * Define register ident in mod/rm byte.
 * Note: these are NOT the same as in ptrace-abi.h.
 */
enum {
        arg_AL = 0,
        arg_CL = 1,
        arg_DL = 2,
        arg_BL = 3,
        arg_AH = 4,
        arg_CH = 5,
        arg_DH = 6,
        arg_BH = 7,

        arg_AX = 0,
        arg_CX = 1,
        arg_DX = 2,
        arg_BX = 3,
        arg_SP = 4,
        arg_BP = 5,
        arg_SI = 6,
        arg_DI = 7,
#ifdef __amd64__
        arg_R8  = 8,
        arg_R9  = 9,
        arg_R10 = 10,
        arg_R11 = 11,
        arg_R12 = 12,
        arg_R13 = 13,
        arg_R14 = 14,
        arg_R15 = 15
#endif
};

static unsigned char *get_reg_w8(int no, struct pt_regs *regs)
{
        unsigned char *rv = NULL;

        switch (no) {
        case arg_AL:
                rv = (unsigned char *)&regs->ax;
                break;
        case arg_BL:
                rv = (unsigned char *)&regs->bx;
                break;
        case arg_CL:
                rv = (unsigned char *)&regs->cx;
                break;
        case arg_DL:
                rv = (unsigned char *)&regs->dx;
                break;
        case arg_AH:
                rv = 1 + (unsigned char *)&regs->ax;
                break;
        case arg_BH:
                rv = 1 + (unsigned char *)&regs->bx;
                break;
        case arg_CH:
                rv = 1 + (unsigned char *)&regs->cx;
                break;
        case arg_DH:
                rv = 1 + (unsigned char *)&regs->dx;
                break;
#ifdef __amd64__
        case arg_R8:
                rv = (unsigned char *)&regs->r8;
                break;
        case arg_R9:
                rv = (unsigned char *)&regs->r9;
                break;
        case arg_R10:
                rv = (unsigned char *)&regs->r10;
                break;
        case arg_R11:
                rv = (unsigned char *)&regs->r11;
                break;
        case arg_R12:
                rv = (unsigned char *)&regs->r12;
                break;
        case arg_R13:
                rv = (unsigned char *)&regs->r13;
                break;
        case arg_R14:
                rv = (unsigned char *)&regs->r14;
                break;
        case arg_R15:
                rv = (unsigned char *)&regs->r15;
                break;
#endif
        default:
                printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no);
                break;
        }
        return rv;
}

static unsigned long *get_reg_w32(int no, struct pt_regs *regs)
{
        unsigned long *rv = NULL;

        switch (no) {
        case arg_AX:
                rv = &regs->ax;
                break;
        case arg_BX:
                rv = &regs->bx;
                break;
        case arg_CX:
                rv = &regs->cx;
                break;
        case arg_DX:
                rv = &regs->dx;
                break;
        case arg_SP:
                rv = &regs->sp;
                break;
        case arg_BP:
                rv = &regs->bp;
                break;
        case arg_SI:
                rv = &regs->si;
                break;
        case arg_DI:
                rv = &regs->di;
                break;
#ifdef __amd64__
        case arg_R8:
                rv = &regs->r8;
                break;
        case arg_R9:
                rv = &regs->r9;
                break;
        case arg_R10:
                rv = &regs->r10;
                break;
        case arg_R11:
                rv = &regs->r11;
                break;
        case arg_R12:
                rv = &regs->r12;
                break;
        case arg_R13:
                rv = &regs->r13;
                break;
        case arg_R14:
                rv = &regs->r14;
                break;
        case arg_R15:
                rv = &regs->r15;
                break;
#endif
        default:
                printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no);
        }

        return rv;
}

unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs)
{
        unsigned int opcode;
        unsigned char mod_rm;
        int reg;
        unsigned char *p;
        int i, shorted, enlarged, rexr;
        unsigned long rv;

        p = (unsigned char *)ins_addr;
        p += skip_prefix(p, &shorted, &enlarged, &rexr);
        p += get_opcode(p, &opcode);
        for (i = 0; i < ARRAY_SIZE(reg_rop); i++)
                if (reg_rop[i] == opcode) {
                        rv = REG_READ;
                        goto do_work;
                }

        for (i = 0; i < ARRAY_SIZE(reg_wop); i++)
                if (reg_wop[i] == opcode) {
                        rv = REG_WRITE;
                        goto do_work;
                }

        printk(KERN_ERR "mmiotrace: Not a register instruction, opcode "
                                                        "0x%02x\n", opcode);
        goto err;

do_work:
        mod_rm = *p;
        reg = ((mod_rm >> 3) & 0x7) | (rexr << 3);
        switch (get_ins_reg_width(ins_addr)) {
        case 1:
                return *get_reg_w8(reg, regs);

        case 2:
                return *(unsigned short *)get_reg_w32(reg, regs);

        case 4:
                return *(unsigned int *)get_reg_w32(reg, regs);

#ifdef __amd64__
        case 8:
                return *(unsigned long *)get_reg_w32(reg, regs);
#endif

        default:
                printk(KERN_ERR "mmiotrace: Error width# %d\n", reg);
        }

err:
        return 0;
}

unsigned long get_ins_imm_val(unsigned long ins_addr)
{
        unsigned int opcode;
        unsigned char mod_rm;
        unsigned char mod;
        unsigned char *p;
        int i, shorted, enlarged, rexr;
        unsigned long rv;

        p = (unsigned char *)ins_addr;
        p += skip_prefix(p, &shorted, &enlarged, &rexr);
        p += get_opcode(p, &opcode);
        for (i = 0; i < ARRAY_SIZE(imm_wop); i++)
                if (imm_wop[i] == opcode) {
                        rv = IMM_WRITE;
                        goto do_work;
                }

        printk(KERN_ERR "mmiotrace: Not an immediate instruction, opcode "
                                                        "0x%02x\n", opcode);
        goto err;

do_work:
        mod_rm = *p;
        mod = mod_rm >> 6;
        p++;
        switch (mod) {
        case 0:
                /* if r/m is 5 we have a 32 disp (IA32 Manual 3, Table 2-2)  */
                /* AMD64: XXX Check for address size prefix? */
                if ((mod_rm & 0x7) == 0x5)
                        p += 4;
                break;

        case 1:
                p += 1;
                break;

        case 2:
                p += 4;
                break;

        case 3:
        default:
                printk(KERN_ERR "mmiotrace: not a memory access instruction "
                                                "at 0x%lx, rm_mod=0x%02x\n",
                                                ins_addr, mod_rm);
        }

        switch (get_ins_reg_width(ins_addr)) {
        case 1:
                return *(unsigned char *)p;

        case 2:
                return *(unsigned short *)p;

        case 4:
                return *(unsigned int *)p;

#ifdef __amd64__
        case 8:
                return *(unsigned long *)p;
#endif

        default:
                printk(KERN_ERR "mmiotrace: Error: width.\n");
        }

err:
        return 0;
}

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading