/*
 * Copyright 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.
 */

#ifndef APF_INTERPRETER_V6_H_
#define APF_INTERPRETER_V6_H_

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Returns the max version of the APF instruction set supported by apf_run().
 * APFv6 is a superset of APFv4. APFv6 interpreters are able to run APFv4 code.
 */
uint32_t apf_version(void);

/**
 * Allocates a buffer for the APF program to build a reply packet.
 *
 * Unless in a critical low memory state, the firmware must allow allocating at
 * least one 1514 byte buffer for every call to apf_run(). The interpreter will
 * have at most one active allocation at any given time, and will always either
 * transmit or deallocate the buffer before apf_run() returns.
 *
 * It is OK if the firmware decides to limit allocations to at most one per
 * apf_run() invocation. This allows the firmware to delay transmitting
 * the buffer until after apf_run() has returned (by keeping track of whether
 * a buffer was allocated/deallocated/scheduled for transmit) and may
 * allow the use of a single statically allocated 1514+ byte buffer.
 *
 * The firmware MAY choose to allocate a larger buffer than requested, and
 * give the apf_interpreter a pointer to the middle of the buffer. This will
 * allow firmware to later (during or after apf_transmit_buffer call) populate
 * any required headers, trailers, etc.
 *
 * @param ctx - unmodified ctx pointer passed into apf_run().
 * @param size - the minimum size of buffer to allocate
 * @return the pointer to the allocated region. The function can return NULL to
 *         indicate allocation failure, for example if too many buffers are
 *         pending transmit. Returning NULL will most likely result in
 *         apf_run() returning PASS.
 */
uint8_t* apf_allocate_buffer(void* ctx, uint32_t size);

/**
 * Transmits the allocated buffer and deallocates it.
 *
 * The apf_interpreter will not read/write from/to the buffer once it calls
 * this function.
 *
 * The content of the buffer between [ptr, ptr + len) are the bytes to be
 * transmitted, starting from the ethernet header and not including any
 * ethernet CRC bytes at the end.
 *
 * The firmware is expected to make its best effort to transmit. If it
 * exhausts retries, or if there is no channel for too long and the transmit
 * queue is full, then it is OK for the packet to be dropped. The firmware should
 * prefer to fail allocation if it can predict transmit will fail.
 *
 * apf_transmit_buffer() may be asynchronous, which means the actual packet
 * transmission can happen sometime after the function returns.
 *
 * @param ctx - unmodified ctx pointer passed into apf_run().
 * @param ptr - pointer to the transmit buffer, must have been previously
 *              returned by apf_allocate_buffer() and not deallocated.
 * @param len - the number of bytes to be transmitted (possibly less than
 *              the allocated buffer), 0 means don't transmit the buffer
 *              but only deallocate it.
 * @param dscp - value in [0..63] - the upper 6 bits of the TOS field in
 *               the IPv4 header or traffic class field in the IPv6 header.
 * @return non-zero if the firmware *knows* the transmit will fail, zero if
 *         the transmit succeeded or the firmware thinks it will succeed.
 *         Returning an error will likely result in apf_run() returning PASS.
 */
int apf_transmit_buffer(void* ctx, uint8_t* ptr, uint32_t len, uint8_t dscp);

/**
 * Runs an APF program over a packet.
 *
 * The return value of apf_run indicates whether the packet should
 * be passed or dropped. As a part of apf_run() execution, the APF
 * program can call apf_allocate_buffer()/apf_transmit_buffer() to construct
 * a reply packet and transmit it.
 *
 * The text section containing the program instructions starts at address
 * program and stops at + program_len - 1, and the writable data section
 * begins at program + program_len and ends at program + ram_len - 1,
 * as described in the following diagram:
 *
 *     program         program + program_len    program + ram_len
 *        |    text section    |      data section      |
 *        +--------------------+------------------------+
 *
 * @param ctx - pointer to any additional context required for allocation and transmit.
 *              May be NULL if no such context is required. This is opaque to
 *              the interpreter and will be passed through unmodified
 *              to apf_allocate_buffer() and apf_transmit_buffer() calls.
 * @param program - the program bytecode, followed by the writable data region.
 *                  Note: this *MUST* be a 4 byte aligned region.
 * @param program_len - the length in bytes of the read-only portion of the APF
 *                    buffer pointed to by {@code program}.
 *                    This is determined by the size of the loaded APF program.
 * @param ram_len - total length of the APF buffer pointed to by
 *                  {@code program}, including the read-only bytecode
 *                  portion and the read-write data portion.
 *                  This is expected to be a constant which doesn't change
 *                  value even when a new APF program is loaded.
 *                  Note: this *MUST* be a multiple of 4.
 * @param packet - the packet bytes, starting from the ethernet header.
 * @param packet_len - the length of {@code packet} in bytes, not
 *                     including trailers/CRC.
 * @param filter_age_16384ths - the number of 1/16384 seconds since the filter
 *                     was programmed.
 *
 * @return zero if packet should be dropped,
 *         non-zero if packet should be passed, specifically:
 *         - 1 on normal apf program execution,
 *         - 2 on exceptional circumstances (caused by bad firmware integration),
 *           these include:
 *             - program pointer which is not aligned to 4 bytes
 *             - ram_len which is not a multiple of 4 bytes
 *             - excessively large (>= 2GiB) program_len or ram_len
 *             - packet pointer which is a null pointer
 *             - packet_len shorter than ETH_HLEN (14)
 *           As such, you may want to log a firmware exception if 2 is ever returned...
 *
 *
 * NOTE: How to calculate filter_age_16384ths:
 *
 * - if you have a u64 clock source counting nanoseconds:
 *     u64 nanoseconds = current_nanosecond_time_u64() - filter_installation_nanosecond_time_u64;
 *     u32 filter_age_16384ths = (u32)((nanoseconds << 5) / 1953125);
 *
 * - if you have a u64 clock source counting microseconds:
 *     u64 microseconds = current_microsecond_time_u64() - filter_installation_microsecond_time_u64;
 *     u32 filter_age_16384ths = (u32)((microseconds << 8) / 15625);
 *
 * - if you have a u64 clock source counting milliseconds:
 *     u64 milliseconds = current_millisecond_time_u64() - filter_installation_millisecond_time_u64;
 *     u32 filter_age_16384ths = (u32)((milliseconds << 11) / 125);
 *
 * - if you have a u32 clock source counting milliseconds and cannot use 64-bit arithmetic:
 *     u32 milliseconds = current_millisecond_time_u32() - filter_installation_millisecond_time_u32;
 *     u32 filter_age_16384ths = ((((((milliseconds << 4) / 5) << 2) / 5) << 2) / 5) << 3;
 *   or the less precise:
 *     u32 filter_age_16384ths = ((milliseconds << 4) / 125) << 7;
 *
 * - if you have a u32 clock source counting seconds:
 *     u32 seconds = current_second_time_u32() - filter_installation_second_time_u32;
 *     u32 filter_age_16384ths = seconds << 14;
 */
int apf_run(void* ctx, uint32_t* const program, const uint32_t program_len,
            const uint32_t ram_len, const uint8_t* const packet,
            const uint32_t packet_len, const uint32_t filter_age_16384ths);

#ifdef __cplusplus
}
#endif

#endif  /* APF_INTERPRETER_V6_H_ */
