/* C file for cdbc */ #include "cdbcport.h" #include "cdbc.h" #include "cdbcapi.h" PCDBC_INSTRUCTION pinst_set[] = { i_nop, /* 0x00, nop instruction */ i_move, /* 0x01, move data */ CDBC_NULL, CDBC_NULL, CDBC_NULL, CDBC_NULL, CDBC_NULL, CDBC_NULL, i_rdwr, /* 0x08, read 8bit reg from pci cfg or bar */ i_rdwr, /* 0x09, read 16bit reg from pci cfg or bar */ i_rdwr, /* 0x0a, read 32bit reg from pci cfg or bar */ CDBC_NULL, /* 0x0b, rsvd */ i_rdwr, /* 0x0c, write 8bit reg from pci cfg or bar */ i_rdwr, /* 0x0d, write 16bit reg from pci cfg or bar */ i_rdwr, /* 0x0e, write 32bit reg from pci cfg or bar */ CDBC_NULL, /* 0x0f, rsvd */ i_dump, /* 0x10, report data */ i_jump, /* 0x11, jump to offset */ i_add, /* 0x12, add operands */ i_sub, /* 0x13, substract operands */ i_mul, /* 0x14, multiply operands */ i_div, /* 0x15, divide operands */ i_mod, /* 0x16, mod of operands */ i_and, /* 0x17, and operands */ i_or, /* 0x18, or operands */ i_not, /* 0x19, not of operand */ i_xor, /* 0x1a, xor operands */ i_compare, /* 0x1b, compare operands */ i_call, /* 0x1c, call */ i_stall, /* 0x1d, stall/sleep for time */ i_ret, /* 0x1e, return */ }; CDBC_VOID i_nop(PCDBC_U32 pbc, CDBC_U32 instruction) { OS_ASSERT(instruction == 0, "CDBC: encode error in 'nop' byte code"); return; } CDBC_VOID i_move(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'move' byte code"); if(domain == IDOMAIN_IMM) { /* move immediate value in the reg */ R(reg) = value; } else if(domain == IDOMAIN_REG) { /* move one reg to another */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'move' byte code"); R(reg) = R(value); } else if(domain == IDOMAIN_VAR) { /* move a variable to a reg */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand error in 'move' byte code"); R(reg) = VAR(value); } else { /* move uses this domain to indicate that data is being moved */ /* to an external vairable from a register. */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand error in 'moveout' byte code"); VAR(value) = R(reg); } return; } CDBC_VOID i_rdwr(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 size = (instruction) & 0x3; CDBC_U32 write = (instruction >> 2) & 0x1; CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_U32 space = (instruction >> 14) & 0x3; CDBC_U16 value = (CDBC_U16)(instruction >> 16); CDBC_U32 offset = 0; CDBC_U32 data = 0; PCDBC_VOID context; /* get the OS context to give back */ OS_GET_CONTEXT(context); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'read' byte code"); if(domain == IDOMAIN_IMM) { /* offset is the immediate value */ offset = value; } else if(domain == IDOMAIN_REG) { /* offset is provided in the reg */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'read' byte code"); offset = R(value); } else if(domain == IDOMAIN_VAR) { /* offset is provided in a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand error in 'read' byte code"); offset = VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'read' byte code"); } if(write) { /* data that needs to be written */ data = R(reg); if(space) { if (size == 0) OS_BAR_WRITE8 (context, (space - 1), offset, data); else if (size == 1) OS_BAR_WRITE16(context, (space - 1), offset, SWAP16(data)); else if (size == 2) OS_BAR_WRITE32(context, (space - 1), offset, SWAP32(data)); else if (size == 3) OS_ASSERT(CDBC_FALSE, "Invalid size encode for 'write' BAR byte code"); } else { if (size == 0) OS_CFG_WRITE8 (context, offset, data); else if (size == 1) OS_CFG_WRITE16(context, offset, SWAP16(data)); else if (size == 2) OS_CFG_WRITE32(context, offset, SWAP32(data)); else if (size == 3) OS_ASSERT(CDBC_FALSE, "Invalid size encode for 'write' PCI CFG byte code"); } } else { if(space) { if (size == 0) { OS_BAR_READ8 (context, (space - 1), offset, &data); } else if (size == 1) { OS_BAR_READ16(context, (space - 1), offset, &data); data = SWAP16(data); } else if (size == 2) { OS_BAR_READ32(context, (space - 1), offset, &data); data = SWAP32(data); } else if (size == 3) OS_ASSERT(CDBC_FALSE, "Invalid size encode for 'read' BAR byte code"); } else { if (size == 0) OS_CFG_READ8 (context, offset, &data); else if (size == 1) { OS_CFG_READ16(context, offset, &data); data = SWAP16(data); } else if (size == 2) { OS_CFG_READ32(context, offset, &data); data = SWAP32(data); } else if (size == 3) OS_ASSERT(CDBC_FALSE, "Invalid size encode for 'read' PCI CFG byte code"); } /* grab the data that was read */ R(reg) = data; } return; } CDBC_VOID i_dump(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 size = (instruction >> 14) & 0x3; PCDBC_VOID context; /* get the OS context to give back */ OS_GET_CONTEXT(context); if(size == 0) { OS_DUMP_WRITE8(context, R_OFFSET, R(reg)); } else if(size == 1) { OS_DUMP_WRITE16(context, R_OFFSET, SWAP16(R(reg))); } else if(size == 2) { OS_DUMP_WRITE32(context, R_OFFSET, SWAP32(R(reg))); } else { OS_ASSERT(CDBC_FALSE, "CDBC: size error in 'dump' byte code"); } R_OFFSET += (1 << size); return; } CDBC_VOID i_jump(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); CDBC_U32 jump_true = CDBC_FALSE; if(reg) { /* reg is used to indicate a conditional jump */ if((reg & CDBC_FLAG_EQUAL) && (R_FLAG & CDBC_FLAG_EQUAL)) jump_true = CDBC_TRUE; if((reg & CDBC_FLAG_GREATER) && (R_FLAG & CDBC_FLAG_GREATER)) jump_true = CDBC_TRUE; if((reg & CDBC_FLAG_LESS) && (R_FLAG & CDBC_FLAG_LESS)) jump_true = CDBC_TRUE; OS_ASSERT(reg < 8, "CDBC: condition error in 'jump' byte code"); if(jump_true == CDBC_FALSE) return; } if(domain == IDOMAIN_IMM) { /* jump with immediate offset value in the instruction */ R_IP += (value - 1); } else if(domain == IDOMAIN_REG) { /* jump with value available in another REG */ R_IP += (R(value) - 1); } else if(domain == IDOMAIN_VAR) { /* jump with value stored as a variable */ R_IP += (VAR(value) - 1); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'jump' byte code"); } return; } CDBC_VOID i_add(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'add' byte code"); if(domain == IDOMAIN_IMM) { /* add with immediate value in the instruction */ R(reg) += value; } else if(domain == IDOMAIN_REG) { /* add with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'add' byte code"); R(reg) += R(value); } else if(domain == IDOMAIN_VAR) { /* add with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand error in 'add' byte code"); R(reg) += VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'add' byte code"); } return; } CDBC_VOID i_sub(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'sub' byte code"); if(domain == IDOMAIN_IMM) { /* sub with immediate value in the instruction */ R(reg) -= value; } else if(domain == IDOMAIN_REG) { /* sub with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'sub' byte code"); R(reg) -= R(value); } else if(domain == IDOMAIN_VAR) { /* sub with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'sub' byte code"); R(reg) -= VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'sub' byte code"); } return; } CDBC_VOID i_mul(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'mul' byte code"); if(domain == IDOMAIN_IMM) { /* mul with immediate value in the instruction */ R(reg) *= value; } else if(domain == IDOMAIN_REG) { /* mul with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'mul' byte code"); R(reg) *= R(value); } else if(domain == IDOMAIN_VAR) { /* mul with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'mul' byte code"); R(reg) *= VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'mul' byte code"); } return; } CDBC_VOID i_div(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'div' byte code"); if(domain == IDOMAIN_IMM) { /* div with immediate value in the instruction */ /* move mod to R0 if R0 is not being used. */ if(reg != 0) R(0) = R(reg) % value; R(reg) /= value; } else if(domain == IDOMAIN_REG) { /* div with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'div' byte code"); /* move mod to R0 if R0 is not being used. */ if( (reg != 0) && (value != 0) ) R(0) = R(reg) % R(value); R(reg) /= R(value); } else if(domain == IDOMAIN_VAR) { /* div with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'div' byte code"); /* move mod to R0 if R0 is not being used. */ if(reg != 0) R(0) = R(reg) % VAR(value); R(reg) /= VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'div' byte code"); } return; } CDBC_VOID i_mod(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'mod' byte code"); if(domain == IDOMAIN_IMM) { /* mod with immediate value in the instruction */ /* move div to R0 if R0 is not being used. */ if(reg != 0) R(0) = R(reg) / value; R(reg) %= value; } else if(domain == IDOMAIN_REG) { /* mod with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'mod' byte code"); /* move div to R0 if R0 is not being used. */ if( (reg != 0) && (value != 0) ) R(0) = R(reg) / R(value); R(reg) %= R(value); } else if(domain == IDOMAIN_VAR) { /* mod with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'mod' byte code"); /* move div to R0 if R0 is not being used. */ if(reg != 0) R(0) = R(reg) / VAR(value); R(reg) %= VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'mod' byte code"); } return; } CDBC_VOID i_and(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_U16 value = (CDBC_U16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'and' byte code"); if(domain == IDOMAIN_IMM) { /* and with immediate value in the instruction */ R(reg) &= value; } else if(domain == IDOMAIN_REG) { /* and with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'and' byte code"); R(reg) &= R(value); } else if(domain == IDOMAIN_VAR) { /* and with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'and' byte code"); R(reg) &= VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'and' byte code"); } return; } CDBC_VOID i_or(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_U16 value = (CDBC_U16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'or' byte code"); if(domain == IDOMAIN_IMM) { /* or with immediate value in the instruction */ R(reg) |= value; } else if(domain == IDOMAIN_REG) { /* or with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'or' byte code"); R(reg) |= R(value); } else if(domain == IDOMAIN_VAR) { /* or with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'or' byte code"); R(reg) |= VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'or' byte code"); } return; } CDBC_VOID i_not(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_U16 value = (CDBC_U16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'not' byte code"); if(domain == IDOMAIN_IMM) { /* not with immediate value in the instruction */ R(reg) = ~(value); } else if(domain == IDOMAIN_REG) { /* not with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'not' byte code"); R(reg) = ~R(value); } else if(domain == IDOMAIN_VAR) { /* not with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'not' byte code"); R(reg) = ~VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'not' byte code"); } return; } CDBC_VOID i_xor(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_U16 value = (CDBC_U16)(instruction >> 16); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'xor' byte code"); if(domain == IDOMAIN_IMM) { /* xor with immediate value in the instruction */ R(reg) ^= (value); } else if(domain == IDOMAIN_REG) { /* xor with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'xor' byte code"); R(reg) ^= R(value); } else if(domain == IDOMAIN_VAR) { /* xor with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'xor' byte code"); R(reg) ^= VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'xor' byte code"); } return; } CDBC_VOID i_compare(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); CDBC_U32 compare_value = 0; OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'compare' byte code"); if(domain == IDOMAIN_IMM) { /* compare with immediate value available in instruction */ compare_value = value; } else if(domain == IDOMAIN_REG) { /* compare with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'compare' byte code"); compare_value = R(value); } else if(domain == IDOMAIN_VAR) { /* compare with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'compare' byte code"); compare_value = VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'compare' byte code"); } /* erase the existing flags if any */ R_FLAG = R_FLAG & (~(CDBC_FLAG_EQUAL | CDBC_FLAG_GREATER | CDBC_FLAG_LESS)); /* compare the values and set appropriate flags */ if(R(reg) == compare_value) R_FLAG |= CDBC_FLAG_EQUAL; if(R(reg) > compare_value) R_FLAG |= CDBC_FLAG_GREATER; if(R(reg) < compare_value) R_FLAG |= CDBC_FLAG_LESS; return; } CDBC_VOID i_call(PCDBC_U32 pbc, CDBC_U32 instruction) { // CDBC_U32 reg = (instruction >> 8) & 0xF; /* unused */ CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_S16 value = (CDBC_S16)(instruction >> 16); CDBC_U32 pushed_regs[CDBC_REG_SAVED_MAX]; /* save last 12 registers on the call stack */ CDBC_MEMCPY(&pushed_regs, &pbc[4], (sizeof(CDBC_U32)*CDBC_REG_SAVED_MAX)); if(domain == IDOMAIN_IMM) { /* jump with immediate offset value in the instruction */ R_IP += (value - 1); } else if(domain == IDOMAIN_REG) { /* jump with value available in another REG */ R_IP += (R(value) - 1); } else if(domain == IDOMAIN_VAR) { /* jump with value stored as a variable */ R_IP += (VAR(value) - 1); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'call' byte code"); } cdbc_run(pbc); /* restore registers from the call stack */ CDBC_MEMCPY(&pbc[4], &pushed_regs, (sizeof(CDBC_U32)*CDBC_REG_SAVED_MAX)); return; } CDBC_VOID i_stall(PCDBC_U32 pbc, CDBC_U32 instruction) { CDBC_U32 reg = (instruction >> 8) & 0xF; /* unused */ CDBC_U32 domain = (instruction >> 12) & 0x3; CDBC_U16 value = (CDBC_U16)(instruction >> 16); CDBC_U32 stall_value = 0; PCDBC_VOID context; /* get the OS context to give back */ OS_GET_CONTEXT(context); OS_ASSERT((reg <= CDBC_REG_GEN_MAX), "CDBC: Reg operand1 error in 'stall' byte code"); if(domain == IDOMAIN_IMM) { /* stall with immediate value available in instruction */ stall_value = value; } else if(domain == IDOMAIN_REG) { /* stall with value available in another REG */ OS_ASSERT((value < CDBC_REG_MAX), "CDBC: Reg operand2 error in 'stall' byte code"); stall_value = R(value); } else if(domain == IDOMAIN_VAR) { /* stall with value stored as a variable */ OS_ASSERT((value > CDBC_REG_MAX), "CDBC: Variable operand2 error in 'stall' byte code"); stall_value = VAR(value); } else { OS_ASSERT(CDBC_FALSE, "CDBC: domain error in 'stall' byte code"); } OS_STALL(context, stall_value); return; } CDBC_VOID i_ret(PCDBC_U32 pbc, CDBC_U32 instruction) { /* this routine is never called */ OS_ASSERT(CDBC_FALSE, "CDBC: error 'ret' instruction cannot be invoked"); return; } CDBC_VOID cdbc_run(PCDBC_U32 pbc) { CDBC_U32 curr_instruction; /* Run till we encounter a ret instruction */ while(CDBC_INSTRUCTION != CDBC_OP_RET) { curr_instruction = CDBC_INSTRUCTION; R_IP ++; /* move on to next instruction */ OS_ASSERT(((curr_instruction & 0xFF) <= CDBC_OP_MAX_INS_OFFSET), "CDBC: invalid byte code instruction"); (pinst_set[(curr_instruction & 0xFF)])(pbc, curr_instruction); } } CDBC_U32 cdbc_get_dump( PCDBC_VOID pcontext, CDBC_U32 offset, CDBC_U32 instance, CDBC_U32 length, PCDBC_VOID pbytecode, PCDBC_VOID psgl) { PCDBC_U32 pbc = (PCDBC_U32) pbytecode; CDBC_U64 ctx = (CDBC_UPTR) pcontext; /* Setup the parameters for bytecode in known locations */ R(CDBC_REG_OFST) = offset; R(CDBC_REG_SIZE) = length; R(CDBC_REG_CNT1) = instance; R(CDBC_REG_CONTEXT_LO) = (CDBC_U32)ctx; R(CDBC_REG_CONTEXT_HI) = (CDBC_U32)(ctx >> 32); /* run the bytecode processor */ cdbc_run(pbc); /* bytecode processor returns final status in R0, return to caller */ return R(0); }