1e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen/* 2e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * Copyright 2016, The Android Open Source Project 3e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * 4e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * Licensed under the Apache License, Version 2.0 (the "License"); 5e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * you may not use this file except in compliance with the License. 6e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * You may obtain a copy of the License at 7e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * 8e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * http://www.apache.org/licenses/LICENSE-2.0 9e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * 10e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * Unless required by applicable law or agreed to in writing, software 11e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * distributed under the License is distributed on an "AS IS" BASIS, 12e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * See the License for the specific language governing permissions and 14e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * limitations under the License. 15e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen */ 16e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen 17e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#include "apf_interpreter.h" 18e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen 19e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#include <string.h> // For memcmp 20e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen 21497d4ee96c2459750e06c5c6a445dfaf0ced18faPaul Jensen#include "apf.h" 22e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen 23e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// Return code indicating "packet" should accepted. 24e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define PASS_PACKET 1 25e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// Return code indicating "packet" should be dropped. 26e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define DROP_PACKET 0 27e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// Verify an internal condition and accept packet if it fails. 28e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define ASSERT_RETURN(c) if (!(c)) return PASS_PACKET 29e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// If "c" is of an unsigned type, generate a compile warning that gets promoted to an error. 30e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// This makes bounds checking simpler because ">= 0" can be avoided. Otherwise adding 31e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// superfluous ">= 0" with unsigned expressions generates compile warnings. 32e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define ENFORCE_UNSIGNED(c) ((c)==(uint32_t)(c)) 33e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen 34e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen/** 35e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * Runs a packet filtering program over a packet. 36e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * 37e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * @param program the program bytecode. 38e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * @param program_len the length of {@code apf_program} in bytes. 39e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * @param packet the packet bytes, starting from the 802.3 header and not 40e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * including any CRC bytes at the end. 41e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * @param packet_len the length of {@code packet} in bytes. 42e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * @param filter_age the number of seconds since the filter was programmed. 43e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * 44e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * @return non-zero if packet should be passed to AP, zero if 45e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen * packet should be dropped. 46e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen */ 47e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensenint accept_packet(const uint8_t* program, uint32_t program_len, 48e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint8_t* packet, uint32_t packet_len, 49e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t filter_age) { 50e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// Is offset within program bounds? 5151d3c5fa9c0ad7dd551c05b06a62ecec09c029d8Paul Jensen#define IN_PROGRAM_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < program_len) 52e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// Is offset within packet bounds? 5351d3c5fa9c0ad7dd551c05b06a62ecec09c029d8Paul Jensen#define IN_PACKET_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < packet_len) 54e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// Accept packet if not within program bounds 55e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define ASSERT_IN_PROGRAM_BOUNDS(p) ASSERT_RETURN(IN_PROGRAM_BOUNDS(p)) 56e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// Accept packet if not within packet bounds 57e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define ASSERT_IN_PACKET_BOUNDS(p) ASSERT_RETURN(IN_PACKET_BOUNDS(p)) 58e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Program counter. 59e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t pc = 0; 60e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// Accept packet if not within program or not ahead of program counter 61e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define ASSERT_FORWARD_IN_PROGRAM(p) ASSERT_RETURN(IN_PROGRAM_BOUNDS(p) && (p) >= pc) 62e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Memory slot values. 63e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t memory[MEMORY_ITEMS] = {}; 64e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Fill in pre-filled memory slot values. 65e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen memory[MEMORY_OFFSET_PACKET_SIZE] = packet_len; 66e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen memory[MEMORY_OFFSET_FILTER_AGE] = filter_age; 67e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_IN_PACKET_BOUNDS(APF_FRAME_HEADER_SIZE); 68e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Only populate if IP version is IPv4. 69e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if ((packet[APF_FRAME_HEADER_SIZE] & 0xf0) == 0x40) { 70e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen memory[MEMORY_OFFSET_IPV4_HEADER_SIZE] = (packet[APF_FRAME_HEADER_SIZE] & 15) * 4; 71e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 72e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Register values. 73e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t registers[2] = {}; 74e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Count of instructions remaining to execute. This is done to ensure an 75e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // upper bound on execution time. It should never be hit and is only for 76e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // safety. Initialize to the number of bytes in the program which is an 77e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // upper bound on the number of instructions in the program. 78e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t instructions_remaining = program_len; 79e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen 80e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen do { 81e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (pc == program_len) { 82e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen return PASS_PACKET; 83e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } else if (pc == (program_len + 1)) { 84e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen return DROP_PACKET; 85e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 86e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_IN_PROGRAM_BOUNDS(pc); 87e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint8_t bytecode = program[pc++]; 88e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint32_t opcode = EXTRACT_OPCODE(bytecode); 89e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint32_t reg_num = EXTRACT_REGISTER(bytecode); 90e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define REG (registers[reg_num]) 91e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#define OTHER_REG (registers[reg_num ^ 1]) 92e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // All instructions have immediate fields, so load them now. 93e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint32_t len_field = EXTRACT_IMM_LENGTH(bytecode); 94e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t imm = 0; 95e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen int32_t signed_imm = 0; 96e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (len_field != 0) { 97e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint32_t imm_len = 1 << (len_field - 1); 98e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_FORWARD_IN_PROGRAM(pc + imm_len - 1); 99d0f1e60dcd609299fc4defb847e0729a5554d488Paul Jensen uint32_t i; 100d0f1e60dcd609299fc4defb847e0729a5554d488Paul Jensen for (i = 0; i < imm_len; i++) 101e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen imm = (imm << 8) | program[pc++]; 102e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Sign extend imm into signed_imm. 103e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen signed_imm = imm << ((4 - imm_len) * 8); 104e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen signed_imm >>= (4 - imm_len) * 8; 105e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 106e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen switch (opcode) { 107e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDB_OPCODE: 108e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDH_OPCODE: 109e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDW_OPCODE: 110e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDBX_OPCODE: 111e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDHX_OPCODE: 112e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDWX_OPCODE: { 113e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t offs = imm; 114e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (opcode >= LDBX_OPCODE) { 115e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Note: this can overflow and actually decrease offs. 116e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen offs += registers[1]; 117e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 118e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_IN_PACKET_BOUNDS(offs); 119e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t load_size; 120e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen switch (opcode) { 121e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDB_OPCODE: 122e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDBX_OPCODE: 123e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen load_size = 1; 124e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 125e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDH_OPCODE: 126e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDHX_OPCODE: 127e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen load_size = 2; 128e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 129e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDW_OPCODE: 130e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LDWX_OPCODE: 131e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen load_size = 4; 132e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 133e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Immediately enclosing switch statement guarantees 134e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // opcode cannot be any other value. 135e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 136e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint32_t end_offs = offs + (load_size - 1); 137e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Catch overflow/wrap-around. 138e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_RETURN(end_offs >= offs); 139e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_IN_PACKET_BOUNDS(end_offs); 140e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t val = 0; 141e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen while (load_size--) 142e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen val = (val << 8) | packet[offs++]; 143e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen REG = val; 144e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 145e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 146e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JMP_OPCODE: 147e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // This can jump backwards. Infinite looping prevented by instructions_remaining. 148e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen pc += imm; 149e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 150e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JEQ_OPCODE: 151e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JNE_OPCODE: 152e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JGT_OPCODE: 153e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JLT_OPCODE: 154e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JSET_OPCODE: 155e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JNEBS_OPCODE: { 156e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Load second immediate field. 157e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t cmp_imm = 0; 158e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (reg_num == 1) { 159e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen cmp_imm = registers[1]; 160e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } else if (len_field != 0) { 161e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t cmp_imm_len = 1 << (len_field - 1); 162e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm_len - 1); 163d0f1e60dcd609299fc4defb847e0729a5554d488Paul Jensen uint32_t i; 164d0f1e60dcd609299fc4defb847e0729a5554d488Paul Jensen for (i = 0; i < cmp_imm_len; i++) 165e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen cmp_imm = (cmp_imm << 8) | program[pc++]; 166e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 167e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen switch (opcode) { 168e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JEQ_OPCODE: 169e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (registers[0] == cmp_imm) 170e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen pc += imm; 171e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 172e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JNE_OPCODE: 173e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (registers[0] != cmp_imm) 174e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen pc += imm; 175e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 176e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JGT_OPCODE: 177e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (registers[0] > cmp_imm) 178e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen pc += imm; 179e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 180e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JLT_OPCODE: 181e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (registers[0] < cmp_imm) 182e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen pc += imm; 183e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 184e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JSET_OPCODE: 185e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (registers[0] & cmp_imm) 186e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen pc += imm; 187e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 188e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case JNEBS_OPCODE: { 189e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // cmp_imm is size in bytes of data to compare. 190e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // pc is offset of program bytes to compare. 191e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // imm is jump target offset. 192e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // REG is offset of packet bytes to compare. 193e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1); 194e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_IN_PACKET_BOUNDS(REG); 195e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint32_t last_packet_offs = REG + cmp_imm - 1; 196e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_RETURN(last_packet_offs >= REG); 197e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_IN_PACKET_BOUNDS(last_packet_offs); 198e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (memcmp(program + pc, packet + REG, cmp_imm)) 199e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen pc += imm; 200e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // skip past comparison bytes 201e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen pc += cmp_imm; 202e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 203e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 204e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 205e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 206e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 207e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case ADD_OPCODE: 208e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen registers[0] += reg_num ? registers[1] : imm; 209e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 210e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case MUL_OPCODE: 211e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen registers[0] *= reg_num ? registers[1] : imm; 212e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 213e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case DIV_OPCODE: { 214e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const uint32_t div_operand = reg_num ? registers[1] : imm; 215e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen ASSERT_RETURN(div_operand); 216e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen registers[0] /= div_operand; 217e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 218e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 219e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case AND_OPCODE: 220e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen registers[0] &= reg_num ? registers[1] : imm; 221e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 222e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case OR_OPCODE: 223e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen registers[0] |= reg_num ? registers[1] : imm; 224e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 225e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case SH_OPCODE: { 226e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen const int32_t shift_val = reg_num ? (int32_t)registers[1] : signed_imm; 227e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if (shift_val > 0) 228e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen registers[0] <<= shift_val; 229e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen else 230e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen registers[0] >>= -shift_val; 231e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 232e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 233e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case LI_OPCODE: 234e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen REG = signed_imm; 235e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 236e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case EXT_OPCODE: 237e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen if ( 238e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will result, 239e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen// instead just enforce that imm is unsigned (so it's always greater or equal to 0). 240e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#if LDM_EXT_OPCODE == 0 24151d3c5fa9c0ad7dd551c05b06a62ecec09c029d8Paul Jensen ENFORCE_UNSIGNED(imm) && 242e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#else 243e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen imm >= LDM_EXT_OPCODE && 244e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen#endif 245e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen imm < (LDM_EXT_OPCODE + MEMORY_ITEMS)) { 246e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen REG = memory[imm - LDM_EXT_OPCODE]; 247e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } else if (imm >= STM_EXT_OPCODE && imm < (STM_EXT_OPCODE + MEMORY_ITEMS)) { 248e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen memory[imm - STM_EXT_OPCODE] = REG; 249e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } else switch (imm) { 250e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case NOT_EXT_OPCODE: 251e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen REG = ~REG; 252e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 253e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case NEG_EXT_OPCODE: 254e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen REG = -REG; 255e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 256e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case SWAP_EXT_OPCODE: { 257e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen uint32_t tmp = REG; 258e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen REG = OTHER_REG; 259e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen OTHER_REG = tmp; 260e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 261e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 262e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen case MOV_EXT_OPCODE: 263e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen REG = OTHER_REG; 264e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 265e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Unknown extended opcode 266e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen default: 267e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Bail out 268e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen return PASS_PACKET; 269e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 270e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen break; 271e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Unknown opcode 272e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen default: 273e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen // Bail out 274e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen return PASS_PACKET; 275e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } 276e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen } while (instructions_remaining--); 277e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen return PASS_PACKET; 278e1caf6d87d16f7dfbc83a5501d4ecfb85faa83f3Paul Jensen} 279