blob: a2f93a1c6d092f1c02dcd7581c1d3d04e2bfa2e9 [file] [log] [blame]
/*
* 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.Rbit1;
import static android.net.apf.BaseApfGenerator.Register.R0;
import static android.net.apf.BaseApfGenerator.Register.R1;
import com.android.internal.annotations.VisibleForTesting;
/**
* APFv4 assembler/generator. A tool for generating an APFv4 program.
*
* @hide
*/
public final class ApfV4Generator extends ApfV4GeneratorBase<ApfV4Generator> {
/**
* Jump to this label to terminate the program, increment the counter and indicate the packet
* should be passed to the AP.
*/
private static final String COUNT_AND_PASS_LABEL = "__COUNT_AND_PASS__";
/**
* Jump to this label to terminate the program, increment counter, and indicate the packet
* should be dropped.
*/
private static final String COUNT_AND_DROP_LABEL = "__COUNT_AND_DROP__";
public final String mCountAndDropLabel;
public final String mCountAndPassLabel;
/**
* Returns true if we support the specified {@code version}, otherwise false.
*/
public static boolean supportsVersion(int version) {
return version >= APF_VERSION_2;
}
/**
* Creates an ApfV4Generator instance which is able to emit instructions for the specified
* {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
* the requested version is unsupported.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public ApfV4Generator(int version, boolean disableCounterRangeCheck)
throws IllegalInstructionException {
// make sure mVersion is not greater than 4 when using this class
super(version >= 4 ? 4 : version, disableCounterRangeCheck);
mCountAndDropLabel = version >= 4 ? COUNT_AND_DROP_LABEL : DROP_LABEL;
mCountAndPassLabel = version >= 4 ? COUNT_AND_PASS_LABEL : PASS_LABEL;
}
/**
* Creates an ApfV4Generator instance which is able to emit instructions for the specified
* {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
* the requested version is unsupported.
*/
public ApfV4Generator(int version) throws IllegalInstructionException {
this(version, false);
}
@Override
void addR0ArithR1(Opcodes opcode) {
append(new Instruction(opcode, Rbit1)); // APFv2/4: R0 op= R1
}
/**
* Generates instructions to prepare to increment the specified counter and jump to the
* "__COUNT_AND_PASS__" label.
* In APFv2, it will directly return PASS.
*
* @param counter The ApfCounterTracker.Counter to increment
* @return Type the generator object
*/
@Override
public ApfV4Generator addCountAndPass(ApfCounterTracker.Counter counter) {
checkPassCounterRange(counter);
return maybeAddLoadR1CounterOffset(counter).addJump(mCountAndPassLabel);
}
/**
* Generates instructions to prepare to increment the specified counter and jump to the
* "__COUNT_AND_DROP__" label.
* In APFv2, it will directly return DROP.
*
* @param counter The ApfCounterTracker.Counter to increment
* @return Type the generator object
*/
@Override
public ApfV4Generator addCountAndDrop(ApfCounterTracker.Counter counter) {
checkDropCounterRange(counter);
return maybeAddLoadR1CounterOffset(counter).addJump(mCountAndDropLabel);
}
@Override
public ApfV4Generator addCountAndDropIfR0Equals(long val, ApfCounterTracker.Counter cnt) {
checkDropCounterRange(cnt);
return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0Equals(val, mCountAndDropLabel);
}
@Override
public ApfV4Generator addCountAndPassIfR0Equals(long val, ApfCounterTracker.Counter cnt) {
checkPassCounterRange(cnt);
return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0Equals(val, mCountAndPassLabel);
}
@Override
public ApfV4Generator addCountAndDropIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) {
checkDropCounterRange(cnt);
return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0NotEquals(val, mCountAndDropLabel);
}
@Override
public ApfV4Generator addCountAndPassIfR0NotEquals(long val, ApfCounterTracker.Counter cnt) {
checkPassCounterRange(cnt);
return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0NotEquals(val, mCountAndPassLabel);
}
@Override
public ApfV4Generator addCountAndDropIfR0LessThan(long val, ApfCounterTracker.Counter cnt) {
checkDropCounterRange(cnt);
if (val <= 0) {
throw new IllegalArgumentException("val must > 0, current val: " + val);
}
return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0LessThan(val, mCountAndDropLabel);
}
@Override
public ApfV4Generator addCountAndPassIfR0LessThan(long val, ApfCounterTracker.Counter cnt) {
checkPassCounterRange(cnt);
if (val <= 0) {
throw new IllegalArgumentException("val must > 0, current val: " + val);
}
return maybeAddLoadR1CounterOffset(cnt).addJumpIfR0LessThan(val, mCountAndPassLabel);
}
@Override
public ApfV4Generator addCountAndDropIfBytesAtR0NotEqual(byte[] bytes,
ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
checkDropCounterRange(cnt);
return maybeAddLoadR1CounterOffset(cnt).addJumpIfBytesAtR0NotEqual(bytes,
mCountAndDropLabel);
}
@Override
public ApfV4Generator addCountAndPassIfBytesAtR0NotEqual(byte[] bytes,
ApfCounterTracker.Counter cnt) throws IllegalInstructionException {
checkPassCounterRange(cnt);
return maybeAddLoadR1CounterOffset(cnt).addJumpIfBytesAtR0NotEqual(bytes,
mCountAndPassLabel);
}
/**
* 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.
* Requires APF v4 or greater.
*/
public final ApfV4Generator addLoadData(Register dst, int ofs)
throws IllegalInstructionException {
requireApfVersion(APF_VERSION_4);
return append(new Instruction(Opcodes.LDDW, dst).addSigned(ofs));
}
/**
* Add an instruction to the end of the program to store 32 bits from {@code register} into the
* data memory. The destination address is computed by adding the signed immediate
* {@code offset} to the other register.
* Requires APF v4 or greater.
*/
public final ApfV4Generator addStoreData(Register src, int ofs)
throws IllegalInstructionException {
requireApfVersion(APF_VERSION_4);
return append(new Instruction(Opcodes.STDW, src).addSigned(ofs));
}
@Override
public ApfV4Generator addLoadCounter(Register register, ApfCounterTracker.Counter counter)
throws IllegalInstructionException {
if (mVersion < 4) return self();
return maybeAddLoadCounterOffset(register.other(), counter).addLoadData(register, 0);
}
@Override
public ApfV4Generator addStoreCounter(ApfCounterTracker.Counter counter, Register register)
throws IllegalInstructionException {
if (mVersion < 4) return self();
return maybeAddLoadCounterOffset(register.other(), counter).addStoreData(register, 0);
}
/**
* Append the count & (pass|drop) trampoline, which increments the counter at the data address
* pointed to by R1, then jumps to the (pass|drop) label. This saves a few bytes over inserting
* the entire sequence inline for every counter.
* This instruction is necessary to be called at the end of any APFv4 program in order to make
* counter incrementing logic work.
* In APFv2, it is a noop.
*/
@Override
public ApfV4Generator addCountTrampoline() throws IllegalInstructionException {
if (mVersion < 4) return self();
return defineLabel(COUNT_AND_PASS_LABEL)
.addLoadData(R0, 0) // R0 = *(R1 + 0)
.addAdd(1) // R0++
.addStoreData(R0, 0) // *(R1 + 0) = R0
.addJump(PASS_LABEL)
.defineLabel(COUNT_AND_DROP_LABEL)
.addLoadData(R0, 0) // R0 = *(R1 + 0)
.addAdd(1) // R0++
.addStoreData(R0, 0) // *(R1 + 0) = R0
.addJump(DROP_LABEL);
}
/**
* This function is no-op in APFv4
*/
@Override
void updateExceptionBufferSize(int programSize) { }
private ApfV4Generator maybeAddLoadCounterOffset(Register reg, ApfCounterTracker.Counter cnt) {
if (mVersion < 4) return self();
return addLoadImmediate(reg, cnt.offset());
}
private ApfV4Generator maybeAddLoadR1CounterOffset(ApfCounterTracker.Counter counter) {
return maybeAddLoadCounterOffset(R1, counter);
}
}