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