Updated ApfGenerator to support WRITE/EWRITE opcodes.
Added support for WRITE opcode, which writes 1, 2, or 4 bytes from an
immediate value to the output buffer. Added support for EWRITE opcode,
which writes 1, 2, or 4 bytes from a register to the output buffer.
Bug: 293811969
Test: TH
Change-Id: Ia644a80bfd0655296f178010ee1a5a941021afd6
diff --git a/src/android/net/apf/ApfGenerator.java b/src/android/net/apf/ApfGenerator.java
index 4b261d2..0460c83 100644
--- a/src/android/net/apf/ApfGenerator.java
+++ b/src/android/net/apf/ApfGenerator.java
@@ -63,7 +63,8 @@
JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455"
EXT(21), // Followed by immediate indicating ExtendedOpcodes.
LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1"
- STDW(23); // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1"
+ STDW(23), // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1"
+ WRITE(24); // Write 1, 2 or 4 bytes imm to the output buffer, e.g. "WRITE 5"
final int value;
@@ -81,7 +82,10 @@
SWAP(34), // Swap, e.g. "swap R0,R1"
MOVE(35), // Move, e.g. "move R0,R1"
ALLOC(36), // Allocate buffer, "e.g. ALLOC R0"
- TRANS(37); // Transmit buffer, "e.g. TRANS R0"
+ TRANS(37), // Transmit buffer, "e.g. TRANS R0"
+ EWRITE1(38), // Write 1 byte from register to the output buffer, e.g. "EWRITE1 R0"
+ EWRITE2(39), // Write 2 bytes from register to the output buffer, e.g. "EWRITE2 R0"
+ EWRITE4(40); // Write 4 bytes from register to the output buffer, e.g. "EWRITE4 R0"
final int value;
@@ -106,9 +110,13 @@
public final int mValue;
Immediate(int value, boolean signed) {
+ this(value, signed, calculateImmSize(value, signed));
+ }
+
+ Immediate(int value, boolean signed, byte size) {
mValue = value;
mSigned = signed;
- mImmSize = calculateImmSize(value, signed);
+ mImmSize = size;
}
}
@@ -141,21 +149,25 @@
this(opcode, Register.R0);
}
- void addImm(int imm, boolean signed) {
+ void addUnsignedImm(int imm) {
+ addImm(new Immediate(imm, false));
+ }
+
+ void addUnsignedImm(int imm, byte size) {
+ addImm(new Immediate(imm, false, size));
+ }
+
+ void addSignedImm(int imm) {
+ addImm(new Immediate(imm, true));
+ }
+
+ void addImm(Immediate imm) {
if (mImms.size() == mMaxSupportedImmediates) {
throw new IllegalArgumentException(
String.format("Opcode: %d only support at max: %d imms", mOpcode,
mMaxSupportedImmediates));
}
- mImms.add(new Immediate(imm, signed));
- }
-
- void addUnsignedImm(int imm) {
- addImm(imm, false);
- }
-
- void addSignedImm(int imm) {
- addImm(imm, true);
+ mImms.add(imm);
}
void setLabel(String label) throws IllegalInstructionException {
@@ -873,6 +885,57 @@
}
/**
+ * Add an instruction to the end of the program to write 1, 2 or 4 bytes value to output buffer.
+ *
+ * @param value the value to write
+ * @param size the size of the value
+ * @return the ApfGenerator object
+ * @throws IllegalInstructionException throws when size is not 1, 2 or 4
+ */
+ public ApfGenerator addWrite(int value, byte size) throws IllegalInstructionException {
+ requireApfVersion(5);
+ if (!(size == 1 || size == 2 || size == 4)) {
+ throw new IllegalInstructionException("length field must be 1, 2 or 4");
+ }
+ if (size < calculateImmSize(value, false)) {
+ throw new IllegalInstructionException(
+ String.format("the value %d is unfit into size: %d", value, size));
+ }
+ Instruction instruction = new Instruction(Opcodes.WRITE);
+ instruction.addUnsignedImm(value, size);
+ addInstruction(instruction);
+ return this;
+ }
+
+ /**
+ * Add an instruction to the end of the program to write 1, 2 or 4 bytes value from register
+ * to output buffer.
+ *
+ * @param register the register contains the value to be written
+ * @param size the size of the value
+ * @return the ApfGenerator object
+ * @throws IllegalInstructionException throws when size is not 1, 2 or 4
+ */
+ public ApfGenerator addWrite(Register register, byte size)
+ throws IllegalInstructionException {
+ requireApfVersion(5);
+ if (!(size == 1 || size == 2 || size == 4)) {
+ throw new IllegalInstructionException(
+ "length field must be 1, 2 or 4");
+ }
+ Instruction instruction = new Instruction(Opcodes.EXT, register);
+ if (size == 1) {
+ instruction.addUnsignedImm(ExtendedOpcodes.EWRITE1.value);
+ } else if (size == 2) {
+ instruction.addUnsignedImm(ExtendedOpcodes.EWRITE2.value);
+ } else {
+ instruction.addUnsignedImm(ExtendedOpcodes.EWRITE4.value);
+ }
+ addInstruction(instruction);
+ return this;
+ }
+
+ /**
* Add an instruction to the end of the program to load 32 bits from the data memory into
* {@code register}. The source address is computed by adding the signed immediate
* @{code offset} to the other register.
diff --git a/tests/unit/src/android/net/apf/ApfV5Test.kt b/tests/unit/src/android/net/apf/ApfV5Test.kt
index 67589d7..d42396a 100644
--- a/tests/unit/src/android/net/apf/ApfV5Test.kt
+++ b/tests/unit/src/android/net/apf/ApfV5Test.kt
@@ -41,10 +41,41 @@
program = gen.generate()
assertContentEquals(byteArrayOf(encodeInstruction(21, 1, 1), 37), program)
assertContentEquals(arrayOf(" 0: trans r1"), ApfJniUtils.disassembleApf(program))
+
+ gen = ApfGenerator(MIN_APF_VERSION)
+ gen.addWrite(0x01, 1)
+ gen.addWrite(0x0102, 2)
+ gen.addWrite(0x01020304, 4)
+ program = gen.generate()
+ assertContentEquals(byteArrayOf(
+ encodeInstruction(24, 1, 0), 0x01,
+ encodeInstruction(24, 2, 0), 0x01, 0x02,
+ encodeInstruction(24, 4, 0), 0x01, 0x02, 0x03, 0x04
+ ), program)
+ assertContentEquals(arrayOf(
+ " 0: write 0x01",
+ " 2: write 0x0102",
+ " 5: write 0x01020304"), ApfJniUtils.disassembleApf(program))
+
+ gen = ApfGenerator(MIN_APF_VERSION)
+ gen.addWrite(ApfGenerator.Register.R0, 1)
+ gen.addWrite(ApfGenerator.Register.R0, 2)
+ gen.addWrite(ApfGenerator.Register.R0, 4)
+ program = gen.generate()
+ assertContentEquals(byteArrayOf(
+ encodeInstruction(21, 1, 0), 38,
+ encodeInstruction(21, 1, 0), 39,
+ encodeInstruction(21, 1, 0), 40
+ ), program)
+ assertContentEquals(arrayOf(
+ " 0: write r0, 1",
+ " 2: write r0, 2",
+ " 4: write r0, 4"), ApfJniUtils.disassembleApf(program))
}
private fun encodeInstruction(opcode: Int, immLength: Int, register: Int): Byte {
- return opcode.shl(3).or(immLength.shl(1)).or(register).toByte()
+ val immLengthEncoding = if (immLength == 4) 3 else immLength
+ return opcode.shl(3).or(immLengthEncoding.shl(1)).or(register).toByte()
}
companion object {