/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.apf; import static android.net.apf.BaseApfGenerator.Rbit.Rbit0; import static android.net.apf.BaseApfGenerator.Register.R0; import static android.net.apf.BaseApfGenerator.Register.R1; import android.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import java.util.List; import java.util.Set; /** * APF assembler/generator. A tool for generating an APF program. * * Call add*() functions to add instructions to the program, then call * {@link BaseApfGenerator#generate} to get the APF bytecode for the program. *
* Choose between these approaches for your instruction helper methods: If the functionality must
* be identical across APF versions, make it a final method within the base class. If it needs
* version-specific adjustments, use an abstract method in the base class with final
* implementations in generator instances.
*
* @param
* load from packet
* compare loaded data, jump if not equal to "next_filter"
* load from packet
* compare loaded data, jump if not equal to "next_filter"
* jump to drop label
* define "next_filter" here
*
* In this case "next_filter" may not have any generated code associated with it.
*/
public final Type defineLabel(String name) throws IllegalInstructionException {
return append(new Instruction(Opcodes.LABEL).setLabel(name));
}
/**
* Add an unconditional jump instruction to the end of the program.
*/
public final Type addJump(String target) {
return append(new Instruction(Opcodes.JMP).setTargetLabel(target));
}
/**
* Add an unconditional jump instruction to the next instruction - ie. a no-op.
*/
public final Type addNop() {
return append(new Instruction(Opcodes.JMP).addUnsigned(0));
}
/**
* Add an instruction to the end of the program to load the byte at offset {@code offset}
* bytes from the beginning of the packet into {@code register}.
*/
public final Type addLoad8(Register r, int ofs) {
return append(new Instruction(Opcodes.LDB, r).addPacketOffset(ofs));
}
/**
* Add an instruction to the end of the program to load 16-bits at offset {@code offset}
* bytes from the beginning of the packet into {@code register}.
*/
public final Type addLoad16(Register r, int ofs) {
return append(new Instruction(Opcodes.LDH, r).addPacketOffset(ofs));
}
/**
* Add an instruction to the end of the program to load 32-bits at offset {@code offset}
* bytes from the beginning of the packet into {@code register}.
*/
public final Type addLoad32(Register r, int ofs) {
return append(new Instruction(Opcodes.LDW, r).addPacketOffset(ofs));
}
/**
* Add an instruction to the end of the program to load a byte from the packet into
* {@code register}. The offset of the loaded byte from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
public final Type addLoad8Indexed(Register r, int ofs) {
return append(new Instruction(Opcodes.LDBX, r).addTwosCompUnsigned(ofs));
}
/**
* Add an instruction to the end of the program to load 16-bits from the packet into
* {@code register}. The offset of the loaded 16-bits from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
public final Type addLoad16Indexed(Register r, int ofs) {
return append(new Instruction(Opcodes.LDHX, r).addTwosCompUnsigned(ofs));
}
/**
* Add an instruction to the end of the program to load 32-bits from the packet into
* {@code register}. The offset of the loaded 32-bits from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
public final Type addLoad32Indexed(Register r, int ofs) {
return append(new Instruction(Opcodes.LDWX, r).addTwosCompUnsigned(ofs));
}
/**
* Add an instruction to the end of the program to add {@code value} to register R0.
*/
public final Type addAdd(long val) {
if (val == 0) return self(); // nop, as APFv6 would '+= R1'
return append(new Instruction(Opcodes.ADD).addTwosCompUnsigned(val));
}
/**
* Add an instruction to the end of the program to subtract {@code value} from register R0.
*/
public final Type addSub(long val) {
return addAdd(-val); // note: addSub(4 billion) isn't valid, as addAdd(-4 billion) isn't
}
/**
* Add an instruction to the end of the program to multiply register R0 by {@code value}.
*/
public final Type addMul(long val) {
if (val == 0) return addLoadImmediate(R0, 0); // equivalent, as APFv6 would '*= R1'
return append(new Instruction(Opcodes.MUL).addUnsigned(val));
}
/**
* Add an instruction to the end of the program to divide register R0 by {@code value}.
*/
public final Type addDiv(long val) {
if (val == 0) return addPass(); // equivalent, as APFv6 would '/= R1'
return append(new Instruction(Opcodes.DIV).addUnsigned(val));
}
/**
* Add an instruction to the end of the program to logically and register R0 with {@code value}.
*/
public final Type addAnd(long val) {
if (val == 0) return addLoadImmediate(R0, 0); // equivalent, as APFv6 would '+= R1'
return append(new Instruction(Opcodes.AND).addTwosCompUnsigned(val));
}
/**
* Add an instruction to the end of the program to logically or register R0 with {@code value}.
*/
public final Type addOr(long val) {
if (val == 0) return self(); // nop, as APFv6 would '|= R1'
return append(new Instruction(Opcodes.OR).addTwosCompUnsigned(val));
}
/**
* Add an instruction to the end of the program to shift left register R0 by {@code value} bits.
*/
// TODO: consider whether should change the argument type to byte
public final Type addLeftShift(int val) {
if (val == 0) return self(); // nop, as APFv6 would '<<= R1'
return append(new Instruction(Opcodes.SH).addSigned(val));
}
/**
* Add an instruction to the end of the program to shift right register R0 by {@code value}
* bits.
*/
// TODO: consider whether should change the argument type to byte
public final Type addRightShift(int val) {
return addLeftShift(-val);
}
// R0 op= R1, where op should be one of Opcodes.{ADD,MUL,DIV,AND,OR,SH}
abstract void addR0ArithR1(Opcodes opcode);
/**
* Add an instruction to the end of the program to add register R1 to register R0.
*/
public final Type addAddR1ToR0() {
addR0ArithR1(Opcodes.ADD); // R0 += R1
return self();
}
/**
* Add an instruction to the end of the program to multiply register R0 by register R1.
*/
public final Type addMulR0ByR1() {
addR0ArithR1(Opcodes.MUL); // R0 *= R1
return self();
}
/**
* Add an instruction to the end of the program to divide register R0 by register R1.
*/
public final Type addDivR0ByR1() {
addR0ArithR1(Opcodes.DIV); // R0 /= R1
return self();
}
/**
* Add an instruction to the end of the program to logically and register R0 with register R1
* and store the result back into register R0.
*/
public final Type addAndR0WithR1() {
addR0ArithR1(Opcodes.AND); // R0 &= R1
return self();
}
/**
* Add an instruction to the end of the program to logically or register R0 with register R1
* and store the result back into register R0.
*/
public final Type addOrR0WithR1() {
addR0ArithR1(Opcodes.OR); // R0 |= R1
return self();
}
/**
* Add an instruction to the end of the program to shift register R0 left by the value in
* register R1.
*/
public final Type addLeftShiftR0ByR1() {
addR0ArithR1(Opcodes.SH); // R0 <<= R1
return self();
}
/**
* Add an instruction to the end of the program to move {@code value} into {@code register}.
*/
public final Type addLoadImmediate(Register register, int value) {
return append(new Instruction(Opcodes.LI, register).addSigned(value));
}
/**
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value equals {@code value}.
*/
public final Type addJumpIfR0Equals(long val, String tgt) {
return append(new Instruction(Opcodes.JEQ).addTwosCompUnsigned(val).setTargetLabel(tgt));
}
/**
* Add instructions to the end of the program to increase counter and drop packet if R0 equals
* {@code val}
* WARNING: may modify R1
*/
public abstract Type addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add instructions to the end of the program to increase counter and pass packet if R0 equals
* {@code val}
* WARNING: may modify R1
*/
public abstract Type addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value does not equal {@code value}.
*/
public final Type addJumpIfR0NotEquals(long val, String tgt) {
return append(new Instruction(Opcodes.JNE).addTwosCompUnsigned(val).setTargetLabel(tgt));
}
/**
* Add instructions to the end of the program to increase counter and drop packet if R0 not
* equals {@code val}
* WARNING: may modify R1
*/
public abstract Type addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add instructions to the end of the program to increase counter and pass packet if R0 not
* equals {@code val}
* WARNING: may modify R1
*/
public abstract Type addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value is greater than {@code value}.
*/
public final Type addJumpIfR0GreaterThan(long val, String tgt) {
return append(new Instruction(Opcodes.JGT).addUnsigned(val).setTargetLabel(tgt));
}
/**
* Add instructions to the end of the program to increase counter and drop packet if R0 greater
* than {@code val}
* WARNING: may modify R1
*/
public abstract Type addCountAndDropIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add instructions to the end of the program to increase counter and pass packet if R0 greater
* than {@code val}
* WARNING: may modify R1
*/
public abstract Type addCountAndPassIfR0GreaterThan(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value is less than {@code value}.
*/
public final Type addJumpIfR0LessThan(long val, String tgt) {
return append(new Instruction(Opcodes.JLT).addUnsigned(val).setTargetLabel(tgt));
}
/**
* Add instructions to the end of the program to increase counter and drop packet if R0 less
* than {@code val}
* WARNING: may modify R1
*/
public abstract Type addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add instructions to the end of the program to increase counter and pass packet if R0 less
* than {@code val}
* WARNING: may modify R1
*/
public abstract Type addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add an instruction to the end of the program to jump to {@code target} if register R0's
* value has any bits set that are also set in {@code value}.
*/
public final Type addJumpIfR0AnyBitsSet(long val, String tgt) {
return append(new Instruction(Opcodes.JSET).addTwosCompUnsigned(val).setTargetLabel(tgt));
}
/**
* Add an instruction to the end of the program to count and drop packet if register R0's
* value has any bits set that are also set in {@code value}.
* WARNING: may modify R1
*/
public abstract Type addCountAndDropIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add an instruction to the end of the program to count and pass packet if register R0's
* value has any bits set that are also set in {@code value}.
* WARNING: may modify R1
*/
public abstract Type addCountAndPassIfR0AnyBitsSet(long val, ApfCounterTracker.Counter cnt)
throws IllegalInstructionException;
/**
* Add an instruction to the end of the program to count and drop if the bytes of the
* packet at an offset specified by register R0 match any of the elements in {@code bytesList}.
* WARNING: may modify R1
*/
public abstract Type addCountAndDropIfBytesAtR0EqualsAnyOf(@NonNull List