#if NET_FILTER_COMPILER
#define USE_EXTRA_REGS 0
#define REG_ZERO 0
#define REG_DATAADDR 3
#define REG_DATALEN 4
#define REG_HDRADDR 5
#define REG_RET 3
const unsigned char scratchregs[] = {
6, 7, 8, 9, 10, 11, 12,
#if USE_EXTRA_REGS
#define INITIAL_NSCRATCHREGS 8
#error not yet written
#endif
};
#define NSCRATCHREGS (sizeof scratchregs / sizeof scratchregs[0])
#define NREGS 32
#define NO_REG 1
#define MAX_LI 0x7fff
#define BCLR(cond) ((19 << 26) | (cond) | (16 << 1))
#define BLR() BCLR(COND_ALWAYS)
#define BC(cond, off) ((16 << 26) | (cond) | ((off) << 2))
#define COND(BO, BI) (((BO) << (16 + 5)) | ((BI) << 16))
#define COND_ALWAYS COND(COND_IF_ALWAYS, 0)
#define COND_EQ COND(COND_IF_TRUE, COND_BIT(0, BIT_EQ))
#define COND_NE COND(COND_IF_FALSE, COND_BIT(0, BIT_EQ))
#define COND_LE COND(COND_IF_FALSE, COND_BIT(0, BIT_GT))
#define COND_GE COND(COND_IF_FALSE, COND_BIT(0, BIT_LT))
#define COND_BIT(crf, bit) \
((crf) * 4 + (bit))
#define BIT_EQ 2
#define BIT_GT 1
#define BIT_LT 0
#define COND_IF_FALSE 0x04
#define COND_IF_TRUE 0x0c
#define COND_IF_ALWAYS 0x14
#define IMMED(opcode, a, b, imm) \
(((opcode) << 26) | ((a) << 21) | ((b) << 16) | \
((imm) & 0xffff))
#define ADDI(dst, src, imm) \
IMMED(14, dst, src, imm)
#define ADDIC(dst, src, imm) \
IMMED(12, dst, src, imm)
#define SUBFIC(dst, src, imm) \
IMMED(8, dst, src, imm)
#define LI(dst, imm) ADDI(dst, 0, (imm))
#define ANDI(dst, src, imm) \
IMMED(28, src, dst, imm)
#define ORI(dst, src, imm) \
IMMED(24, src, dst, imm)
#define XORI(dst, src, imm) \
IMMED(26, src, dst, imm)
#define CMPL(lhs, rhs) ((31 << 26) | ((lhs) << 16) | ((rhs) << 11) | (32 << 1))
#define CMPLI(lhs, imm) ((10 << 26) | ((lhs) << 16) | ((imm) & 0xffff))
#define INTEGER_OP(opcode, a, b, c) \
((31 << 26) | ((a) << 21) | ((b) << 16) | \
((c) << 11) | ((opcode) << 1))
#define ARITH_OP(opcode, dst, lhs, rhs) \
INTEGER_OP(opcode, dst, lhs, rhs)
#define ADD(dst, lhs, rhs) \
ARITH_OP(OP_ADD, dst, lhs, rhs)
#define ADDE(dst, lhs, rhs) \
ARITH_OP(OP_ADDE, dst, lhs, rhs)
#define SUBF(dst, lhs, rhs) \
ARITH_OP(OP_SUBF, dst, lhs, rhs)
#define SUBFC(dst, lhs, rhs) \
ARITH_OP(OP_SUBFC, dst, lhs, rhs)
#define SUBFE(dst, lhs, rhs) \
ARITH_OP(OP_SUBFE, dst, lhs, rhs)
#define LOGIC_OP(opcode, dst, lhs, rhs) \
INTEGER_OP(opcode, lhs, dst, rhs)
#define OR(dst, lhs, rhs) \
LOGIC_OP(OP_OR, dst, lhs, rhs)
#define XOR(dst, lhs, rhs) \
LOGIC_OP(OP_XOR, dst, lhs, rhs)
#define OP_ADD 266
#define OP_ADDE 138
#define OP_AND 28
#define OP_OR 444
#define OP_SRW 536
#define OP_SUBF 40
#define OP_SUBFC 8
#define OP_SUBFE 136
#define OP_XOR 316
#define MR(dst, src) OR(dst, src, src)
#define LHZ(dst, base, offset) \
((40 << 26) | ((dst) << 21) | ((base) << 16) | \
((offset) & 0xffff))
#define LHZX(dst, base, index) \
INTEGER_OP(279, dst, base, index)
#define MFCR(dst) INTEGER_OP(19, dst, 0, 0)
#define RLWINM(dst, src, shiftimm, mbegin, mend) \
((21 << 26) | ((src) << 21) | ((dst) << 16) | \
((shiftimm) << 11) | ((mbegin) << 6) | ((mend) << 1))
#define RLWNM(dst, src, shiftreg, mbegin, mend) \
((23 << 26) | ((src) << 21) | ((dst) << 16) | \
((shiftreg) << 11) | ((mbegin) << 6) | ((mend) << 1))
#define MAX_INSTR_PER_ARG 4
#define MAX_INSTR_PER_OP 3
#define MAX_INSTR_PER_ITEM (MAX_INSTR_PER_ARG + MAX_INSTR_PER_OP)
int junk_filter[MAX_INSTR_PER_ITEM];
enum {NF_LITERAL, NF_HEADER, NF_DATA};
struct common {
char type;
char nuses;
unsigned char reg;
unsigned short value;
};
struct reg {
unsigned char commoni;
#define NOT_COMMON_VALUE NET_MAX_FILTER
unsigned char stacktimes;
};
struct local {
struct common common[NET_MAX_FILTER];
struct reg regs[NREGS];
unsigned char commonpos[NET_MAX_FILTER];
unsigned char stackregs[NET_FILTER_STACK_DEPTH];
#if USE_EXTRA_REGS
unsigned char maxreg;
#endif
};
int allocate_register(struct local *s, int commoni);
int compile_preamble(int *instructions, struct local *s);
filter_fct_t
net_filter_alloc(filter_t *filter, unsigned int size, unsigned int *lenp)
{
struct local *s;
int len, oldi, i, j, t, ncommon, sp;
int type, value, arg, op, reg, reg1, dst, commoni;
int returnfalseoffset;
int *instructions, *instp, *returnfalse;
#if USE_EXTRA_REGS
int oldmaxreg;
#endif
boolean_t compiling;
#define SCHAR_MAX 127
assert(NET_MAX_FILTER <= SCHAR_MAX);
assert(NET_FILTER_STACK_DEPTH <= SCHAR_MAX);
assert(NREGS <= SCHAR_MAX);
assert(size < NET_MAX_FILTER);
s = (struct local *) kalloc(sizeof *s);
#if USE_EXTRA_REGS
s->maxreg = INITIAL_NSCRATCHREGS;
#endif
len = 0;
compiling = FALSE;
returnfalse = junk_filter;
while (1) {
ncommon = 0;
for (i = 0; i < size; i++) {
oldi = i;
arg = NETF_ARG(filter[i]);
if (arg == NETF_PUSHLIT) {
type = NF_LITERAL;
value = filter[++i];
} else if (arg >= NETF_PUSHSTK) {
continue;
} else if (arg >= NETF_PUSHHDR) {
type = NF_HEADER;
value = arg - NETF_PUSHHDR;
} else if (arg >= NETF_PUSHWORD) {
type = NF_DATA;
value = arg - NETF_PUSHWORD;
} else {
continue;
}
for (j = 0; j < ncommon; j++) {
if (s->common[j].type == type && s->common[j].value == value) {
s->common[j].nuses++;
break;
}
}
if (j == ncommon) {
s->common[j].type = type;
s->common[j].value = value;
s->common[j].nuses = 1;
ncommon++;
}
s->commonpos[oldi] = j;
}
#if USE_EXTRA_REGS
oldmaxreg = s->maxreg;
#endif
for (i = 0; i < ncommon; i++)
s->common[i].reg = NO_REG;
for (i = 0; i < NSCRATCHREGS; i++) {
s->regs[scratchregs[i]].commoni = NOT_COMMON_VALUE;
s->regs[scratchregs[i]].stacktimes = 0;
}
sp = -1;
for (i = 0; i < size; i++) {
if (!compiling)
instp = junk_filter;
assert(sp >= -1);
assert(sp < NET_FILTER_STACK_DEPTH - 1);
commoni = s->commonpos[i];
arg = NETF_ARG(filter[i]);
op = NETF_OP(filter[i]);
switch (arg) {
case NETF_PUSHLIT:
value = filter[++i];
reg = s->common[commoni].reg;
if (reg == NO_REG) {
if ((reg = allocate_register(s, commoni)) == NO_REG)
goto fail;
assert(value >= 0);
*instp++ = ORI(reg, REG_ZERO, value);
}
s->common[commoni].nuses--;
break;
case NETF_NOPUSH:
reg = s->stackregs[sp--];
s->regs[reg].stacktimes--;
break;
case NETF_PUSHZERO:
reg = REG_ZERO;
break;
case NETF_PUSHIND:
case NETF_PUSHHDRIND:
reg1 = s->stackregs[sp--];
s->regs[reg1].stacktimes--;
if (arg == NETF_PUSHIND)
*instp++ = CMPL(reg1, REG_DATALEN);
else
*instp++ = CMPLI(reg1,
NET_HDW_HDR_MAX/sizeof (unsigned short));
*instp = BC(COND_GE, returnfalse - instp);
instp++;
if ((reg = allocate_register(s, -1)) == NO_REG)
goto fail;
*instp++ = ADD(reg, reg1, reg1);
*instp++ = LHZX(reg, (arg == NETF_PUSHIND) ?
REG_DATAADDR : REG_HDRADDR, reg);
break;
default:
if (arg >= NETF_PUSHSTK)
reg = s->stackregs[sp - (arg - NETF_PUSHSTK)];
else if (arg >= NETF_PUSHWORD) {
assert(2 * (NETF_PUSHHDR - NETF_PUSHWORD) <= MAX_LI);
assert(NETF_PUSHSTK - NETF_PUSHHDR <= MAX_LI);
reg = s->common[commoni].reg;
if (reg == NO_REG) {
if ((reg = allocate_register(s, commoni)) == NO_REG)
goto fail;
if (arg < NETF_PUSHHDR) {
value = arg - NETF_PUSHWORD;
*instp++ = CMPLI(REG_DATALEN, value);
*instp = BC(COND_LE, returnfalse - instp);
instp++;
reg1 = REG_DATAADDR;
} else {
value = arg - NETF_PUSHHDR;
reg1 = REG_HDRADDR;
}
*instp++ = LHZ(reg, reg1, 2 * value);
}
s->common[commoni].nuses--;
}
}
if (op != NETF_NOP) {
reg1 = s->stackregs[sp--];
s->regs[reg1].stacktimes--;
}
switch (op) {
case NETF_OP(NETF_CAND):
case NETF_OP(NETF_COR):
case NETF_OP(NETF_CNAND):
case NETF_OP(NETF_CNOR):
dst = -1;
case NETF_OP(NETF_NOP):
break;
default:
if ((dst = allocate_register(s, -1)) == NO_REG)
goto fail;
}
switch (op) {
case NETF_OP(NETF_NOP):
dst = reg;
break;
case NETF_OP(NETF_EQ):
case NETF_OP(NETF_NEQ):
if (reg == REG_ZERO)
t = reg1;
else {
*instp++ = XOR(dst, reg1, reg);
t = dst;
}
*instp++ = (op == NETF_OP(NETF_EQ)) ?
SUBFIC(dst, t, 0) : ADDIC(dst, t, -1);
*instp++ = ADDE(dst, REG_ZERO, REG_ZERO);
break;
case NETF_OP(NETF_LT):
*instp++ = SUBF(dst, reg, reg1);
*instp++ = RLWINM(dst, dst, 1, 31, 31);
break;
case NETF_OP(NETF_GT):
*instp++ = SUBF(dst, reg1, reg);
*instp++ = RLWINM(dst, dst, 1, 31, 31);
break;
case NETF_OP(NETF_LE):
*instp++ = SUBFC(dst, reg1, reg);
*instp++ = ADDE(dst, REG_ZERO, REG_ZERO);
break;
case NETF_OP(NETF_GE):
*instp++ = SUBFC(dst, reg, reg1);
*instp++ = ADDE(dst, REG_ZERO, REG_ZERO);
break;
case NETF_OP(NETF_AND):
j = OP_AND;
goto logical;
case NETF_OP(NETF_OR):
j = OP_OR;
goto logical;
case NETF_OP(NETF_XOR):
j = OP_XOR;
goto logical;
case NETF_OP(NETF_RSH):
j = OP_SRW;
logical:
*instp++ = LOGIC_OP(j, dst, reg1, reg);
break;
case NETF_OP(NETF_ADD):
j = OP_ADD;
goto arithmetical;
case NETF_OP(NETF_SUB):
j = OP_SUBF;
arithmetical:
*instp++ = ARITH_OP(j, dst, reg, reg1);
*instp++ = ANDI(dst, dst, 0xffff);
break;
case NETF_OP(NETF_LSH):
*instp++ = RLWNM(dst, reg1, reg, 16, 31);
break;
case NETF_OP(NETF_COR):
case NETF_OP(NETF_CNAND):
*instp++ = CMPL(reg1, reg);
*instp++ = BCLR((op == NETF_OP(NETF_COR)) ? COND_EQ : COND_NE);
break;
case NETF_OP(NETF_CAND):
case NETF_OP(NETF_CNOR):
*instp++ = CMPL(reg1, reg);
*instp = BC((op == NETF_OP(NETF_CAND)) ? COND_NE : COND_EQ,
returnfalse - instp);
instp++;
break;
default:
printf("op == 0x%x\n", op);
panic("net_filter_alloc: bad op");
}
if (dst >= 0) {
s->stackregs[++sp] = dst;
s->regs[dst].stacktimes++;
}
if (!compiling) {
assert(instp - junk_filter <= MAX_INSTR_PER_ITEM);
len += instp - junk_filter;
}
}
if (compiling) {
if (sp >= 0)
*instp++ = MR(REG_RET, s->stackregs[sp]);
*instp++ = BLR();
assert(instp == returnfalse);
*instp++ = LI(REG_RET, 0);
*instp++ = BLR();
break;
} else {
len += 1 + (sp >= 0);
#if USE_EXTRA_REGS
if (s->maxreg > oldmaxreg) {
len = 0;
continue;
}
#endif
len += compile_preamble(NULL, s);
returnfalseoffset = len;
len += 2;
}
if ((instructions = (int *) kalloc(len * sizeof (int))) == NULL)
return NULL;
returnfalse = instructions + returnfalseoffset;
instp = instructions;
instp += compile_preamble(instp, s);
compiling = TRUE;
}
assert(instp - instructions == len);
*lenp = len * sizeof (int);
{
kern_return_t kr;
vm_machine_attribute_val_t val = MATTR_VAL_CACHE_SYNC;
kr = pmap_attribute(kernel_pmap, (vm_offset_t) instructions,
len * sizeof (int), MATTR_CACHE, &val);
if (kr != KERN_SUCCESS) {
printf("net_filter_alloc: pmap_attribute -> 0x%x\n", kr);
return NULL;
}
}
kfree((vm_offset_t) s, sizeof *s);
return (filter_fct_t) instructions;
fail:
assert(!compiling);
kfree((vm_offset_t) s, sizeof *s);
printf("net_filter_alloc: failed to compile (filter too complex)\n");
printf("-- will work, but more slowly; consider enabling USE_EXTRA_REGS\n");
return NULL;
}
int
allocate_register(struct local *s, int commoni)
{
int i, reg, bestreg, nuses, bestregnuses, maxreg;
bestreg = NO_REG;
#if USE_EXTRA_REGS
maxreg = s->maxreg;
#else
maxreg = NSCRATCHREGS;
#endif
while (1) {
bestregnuses = NOT_COMMON_VALUE;
for (i = 0; i < maxreg; i++) {
reg = scratchregs[i];
if (s->regs[reg].stacktimes == 0) {
nuses = (s->regs[reg].commoni == NOT_COMMON_VALUE) ?
0 : s->common[s->regs[reg].commoni].nuses;
if (nuses < bestregnuses) {
bestreg = reg;
bestregnuses = nuses;
}
}
}
if (bestreg != NO_REG)
break;
#if USE_EXTRA_REGS
if (maxreg == NSCRATCHREGS)
return NO_REG;
s->maxreg = ++maxreg;
#else
return NO_REG;
#endif
}
if (bestregnuses > 0)
printf("net_filter_alloc: forced to reallocate r%d\n", bestreg);
if (s->regs[bestreg].commoni != NOT_COMMON_VALUE)
s->common[s->regs[bestreg].commoni].reg = NO_REG;
if (commoni >= 0) {
s->regs[bestreg].commoni = commoni;
s->common[commoni].reg = bestreg;
} else
s->regs[bestreg].commoni = NOT_COMMON_VALUE;
return bestreg;
}
#define FIXED_PREAMBLE_INSTRUCTIONS 1
int
compile_preamble(int *instructions, struct local *s)
{
int *instp;
int len = FIXED_PREAMBLE_INSTRUCTIONS;
#if USE_EXTRA_REGS
#error this hp code must be ported to the ppc
int extra_regs, i, j, t, disp;
extra_regs = s->maxreg - INITIAL_NSCRATCHREGS;
if (extra_regs > 0) {
len = extra_regs * 2 + 4;
} else
return 0;
#endif
if (instructions == NULL)
return len;
instp = instructions;
*instp++ = LI(REG_ZERO, 0);
assert(instp - instructions == FIXED_PREAMBLE_INSTRUCTIONS);
#if USE_EXTRA_REGS
#error this hp code must be ported to the ppc
#define FRAME_SIZE 128
*instp++ = STW_NEG(REG_RTN, 20, REG_SP);
i = INITIAL_NSCRATCHREGS;
t = STWM(scratchregs[i], FRAME_SIZE, REG_SP);
j = FRAME_SIZE;
while (++i < s->maxreg) {
*instp++ = t;
j -= sizeof (int);
t = STW_NEG(scratchregs[i], j, REG_SP);
}
disp = extra_regs + 2;
*instp++ = BL(disp, REG_RTN);
*instp++ = t;
*instp++ = LDW_NEG(FRAME_SIZE + 20, REG_SP, REG_RTN);
while (--i > INITIAL_NSCRATCHREGS) {
*instp++ = LDW_NEG(j, REG_SP, scratchregs[i]);
j += sizeof (int);
}
*instp++ = BV(0, REG_RTN);
*instp++ = LDWM_NEG(FRAME_SIZE, REG_SP, scratchregs[i]);
#endif
assert(instp - instructions == len);
return len;
}
void
net_filter_free(filter_fct_t fp, unsigned int len)
{
kfree((vm_offset_t) fp, len);
}
#else
filter_fct_t
net_filter_alloc(
filter_t *fpstart,
unsigned int fplen,
unsigned int *len)
{
*len = 0;
return ((filter_fct_t)0);
}
void
net_filter_free(
filter_fct_t fp,
unsigned int len)
{
assert(fp == (filter_fct_t)0 && len == 0);
}
#endif