|
| 1 | +#include <assert.h> |
| 2 | +#include "unicorn_test.h" |
| 3 | + |
| 4 | +#define ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) |
| 5 | + |
| 6 | +#define PAGE_SIZE 256 |
| 7 | +#define PAGE_ALIGN(x) (((x) + PAGE_SIZE - 1) & -PAGE_SIZE) |
| 8 | + |
| 9 | +enum { |
| 10 | + ADDR__init__, |
| 11 | + ADDR_test_func, |
| 12 | + ADDR_test_1, |
| 13 | + ADDR_main, |
| 14 | + ADDR_abort, |
| 15 | + ADDR_exit, |
| 16 | + ADDR__stop_program, |
| 17 | + ADDR__data__, |
| 18 | +}; |
| 19 | + |
| 20 | +static const uint16_t ADDR[] = { |
| 21 | + 0x0000, // __init__ |
| 22 | + 0x001a, // test_func() |
| 23 | + 0x0030, // test_1() |
| 24 | + 0x0058, // main() |
| 25 | + 0x0062, // abort() |
| 26 | + 0x006c, // _exit() |
| 27 | + 0x006e, // __stop_program() |
| 28 | + 0x0070, // __data__ |
| 29 | + 0x0072, // __size__ |
| 30 | +}; |
| 31 | + |
| 32 | +static const uint8_t FLASH[] = |
| 33 | + // 00000000 <__ctors_end>: |
| 34 | + "\x12\xe0" // ldi r17, 0x02 |
| 35 | + "\xa0\xe0" // ldi r26, 0x00 |
| 36 | + "\xb2\xe0" // ldi r27, 0x02 |
| 37 | + "\xe0\xe7" // ldi r30, 0x70 |
| 38 | + "\xf0\xe0" // ldi r31, 0x00 |
| 39 | + "\x00\xe0" // ldi r16, 0x00 |
| 40 | + "\x0b\xbf" // out 0x3b, r16 |
| 41 | + "\x02\xc0" // rjmp .+4 |
| 42 | + "\x07\x90" // elpm r0, Z+ |
| 43 | + "\x0d\x92" // st X+, r0 |
| 44 | + "\xa2\x30" // cpi r26, 0x02 |
| 45 | + "\xb1\x07" // cpc r27, r17 |
| 46 | + "\xd9\xf7" // brne .-10 |
| 47 | + |
| 48 | + // 0000001a <test_func>: |
| 49 | + "\x20\x91\x00\x02" // lds r18, 0x0200 |
| 50 | + "\x30\x91\x01\x02" // lds r19, 0x0201 |
| 51 | + "\x86\x0f" // add r24, r22 |
| 52 | + "\x97\x1f" // adc r25, r23 |
| 53 | + "\x88\x0f" // add r24, r24 |
| 54 | + "\x99\x1f" // adc r25, r25 |
| 55 | + "\x82\x0f" // add r24, r18 |
| 56 | + "\x93\x1f" // adc r25, r19 |
| 57 | + "\x08\x95" // ret |
| 58 | + |
| 59 | + // 00000030 <test_1>: |
| 60 | + "\x62\xe0" // ldi r22, 0x02 |
| 61 | + "\x70\xe0" // ldi r23, 0x00 |
| 62 | + "\x81\xe0" // ldi r24, 0x01 |
| 63 | + "\x90\xe0" // ldi r25, 0x00 |
| 64 | + "\x0e\x94\x0d\x00" // call 0x1a |
| 65 | + "\x07\x97" // sbiw r24, 0x07 |
| 66 | + "\x11\xf0" // breq .+4 |
| 67 | + "\x0e\x94\x31\x00" // call 0x62 |
| 68 | + "\x60\xe8" // ldi r22, 0x80 |
| 69 | + "\x70\xe0" // ldi r23, 0x00 |
| 70 | + "\x80\xe4" // ldi r24, 0x40 |
| 71 | + "\x90\xe0" // ldi r25, 0x00 |
| 72 | + "\x0e\x94\x0d\x00" // call 0x1a |
| 73 | + "\x81\x38" // cpi r24, 0x81 |
| 74 | + "\x91\x40" // sbci r25, 0x01 |
| 75 | + "\xa9\xf7" // brne .-22 |
| 76 | + "\x08\x95" // ret |
| 77 | + |
| 78 | + // 00000058 <main>: |
| 79 | + "\x0e\x94\x18\x00" // call 0x30 |
| 80 | + "\x80\xe0" // ldi r24, 0x00 |
| 81 | + "\x90\xe0" // ldi r25, 0x00 |
| 82 | + "\x08\x95" // ret |
| 83 | + |
| 84 | + // 00000062 <abort>: |
| 85 | + "\x81\xe0" // ldi r24, 0x01 |
| 86 | + "\x90\xe0" // ldi r25, 0x00 |
| 87 | + "\xf8\x94" // cli |
| 88 | + "\x0c\x94\x36\x00" // jmp 0x6c |
| 89 | + |
| 90 | + // 0000006c <_exit>: |
| 91 | + "\xf8\x94" // cli |
| 92 | + |
| 93 | + // 0000006e <__stop_program>: |
| 94 | + "\xff\xcf" // rjmp .-2 |
| 95 | + |
| 96 | + // 0x000070 .data |
| 97 | + "\x01\x00" |
| 98 | + ; |
| 99 | +const uint64_t FLASH_SIZE = sizeof(FLASH); |
| 100 | + |
| 101 | +const uint64_t MEM_BASE = 0x0200; |
| 102 | +const uint64_t MEM_SIZE = 0x0100; |
| 103 | + |
| 104 | +static void uc_common_setup(uc_engine **uc, uc_cpu_avr cpu_model, |
| 105 | + const uint8_t *code, uint64_t code_size) |
| 106 | +{ |
| 107 | + OK(uc_open(UC_ARCH_AVR, UC_MODE_LITTLE_ENDIAN, uc)); |
| 108 | + if (cpu_model != 0) |
| 109 | + OK(uc_ctl_set_cpu_model(*uc, cpu_model)); |
| 110 | + |
| 111 | + OK(uc_mem_map(*uc, UC_AVR_MEM_FLASH, PAGE_ALIGN(code_size), |
| 112 | + UC_PROT_READ|UC_PROT_EXEC)); |
| 113 | + OK(uc_mem_write(*uc, UC_AVR_MEM_FLASH, code, code_size)); |
| 114 | + OK(uc_mem_map(*uc, MEM_BASE, MEM_SIZE, UC_PROT_READ|UC_PROT_WRITE)); |
| 115 | +} |
| 116 | + |
| 117 | +static void test_avr_basic_alu(void) |
| 118 | +{ |
| 119 | + uc_engine *uc = NULL; |
| 120 | + uint8_t r[32] = {0,}; |
| 121 | + uint16_t r_func_arg0 = 1, r_func_arg1 = 2, r_func_ret; |
| 122 | + r[24] = 1; |
| 123 | + r[22] = 2; |
| 124 | + |
| 125 | + uc_common_setup(&uc, 0, FLASH, FLASH_SIZE); |
| 126 | + OK(uc_reg_write(uc, UC_AVR_REG_R24W, &r_func_arg0)); |
| 127 | + OK(uc_reg_write(uc, UC_AVR_REG_R22W, &r_func_arg1)); |
| 128 | + |
| 129 | + const uint64_t code_start = ADDR[ADDR_test_func] + 8; |
| 130 | + OK(uc_emu_start(uc, code_start, code_start + 4, 0, 0)); |
| 131 | + |
| 132 | + uint32_t r_pc; |
| 133 | + OK(uc_reg_read(uc, UC_AVR_REG_PC, &r_pc)); |
| 134 | + OK(uc_reg_read(uc, UC_AVR_REG_R25, &r[25])); |
| 135 | + OK(uc_reg_read(uc, UC_AVR_REG_R24, &r[24])); |
| 136 | + OK(uc_reg_read(uc, UC_AVR_REG_R23, &r[23])); |
| 137 | + OK(uc_reg_read(uc, UC_AVR_REG_R22, &r[22])); |
| 138 | + |
| 139 | + TEST_CHECK(r_pc == code_start + 4); |
| 140 | + TEST_CHECK(r[25] == 0 && r[24] == 3); |
| 141 | + TEST_CHECK(r[23] == 0 && r[22] == 2); |
| 142 | + |
| 143 | + OK(uc_reg_read(uc, UC_AVR_REG_R24W, &r_func_ret)); |
| 144 | + OK(uc_reg_read(uc, UC_AVR_REG_R22W, &r_func_arg1)); |
| 145 | + |
| 146 | + TEST_CHECK(r_func_ret == r[24]); |
| 147 | + TEST_CHECK(r_func_arg1 == r[22]); |
| 148 | + |
| 149 | + OK(uc_close(uc)); |
| 150 | +} |
| 151 | + |
| 152 | +typedef struct MEM_HOOK_RESULT_s { |
| 153 | + uc_mem_type type; |
| 154 | + uint64_t address; |
| 155 | + int size; |
| 156 | + uint64_t value; |
| 157 | +} MEM_HOOK_RESULT; |
| 158 | + |
| 159 | +typedef struct MEM_HOOK_RESULTS_s { |
| 160 | + uint64_t count; |
| 161 | + MEM_HOOK_RESULT results[16]; |
| 162 | +} MEM_HOOK_RESULTS; |
| 163 | + |
| 164 | +static bool test_avr_basic_mem_cb_eventmem(uc_engine *uc, uc_mem_type type, |
| 165 | + uint64_t address, int size, int64_t value, void *user_data) |
| 166 | +{ |
| 167 | + MEM_HOOK_RESULTS *const r = user_data; |
| 168 | + |
| 169 | + uint64_t count = r->count; |
| 170 | + if (count >= ARRAY_ELEMS(r->results)) { |
| 171 | + TEST_ASSERT(false); |
| 172 | + } |
| 173 | + |
| 174 | + r->results[count].type = type; |
| 175 | + r->results[count].address = address; |
| 176 | + r->results[count].size = size; |
| 177 | + r->results[count].value = value; |
| 178 | + r->count++; |
| 179 | + return true; |
| 180 | +} |
| 181 | + |
| 182 | +static void test_avr_basic_mem(void) |
| 183 | +{ |
| 184 | + uc_engine *uc = NULL; |
| 185 | + uc_hook eventmem_hook; |
| 186 | + MEM_HOOK_RESULTS eventmem_trace = {0}; |
| 187 | + |
| 188 | + uc_common_setup(&uc, 0, FLASH, FLASH_SIZE); |
| 189 | + OK(uc_hook_add(uc, &eventmem_hook, UC_HOOK_MEM_VALID, |
| 190 | + test_avr_basic_mem_cb_eventmem, &eventmem_trace, 1, 0)); |
| 191 | + |
| 192 | + const uint64_t code_start = ADDR[ADDR__init__]; |
| 193 | + OK(uc_emu_start(uc, code_start, ADDR[ADDR__init__+1], 0, 0)); |
| 194 | + |
| 195 | + uint32_t r_pc; |
| 196 | + OK(uc_reg_read(uc, UC_AVR_REG_PC, &r_pc)); |
| 197 | + TEST_CHECK(r_pc == ADDR[ADDR__init__+1]); |
| 198 | + |
| 199 | + const uint16_t DATA_BASE = ADDR[ADDR__data__]; |
| 200 | + const uint16_t DATA_SIZE = ADDR[ADDR__data__+1] - DATA_BASE; |
| 201 | + const uint8_t *const DATA = &FLASH[ADDR[ADDR__data__]]; |
| 202 | + |
| 203 | + // Check SRAM was correctly initialized with data from Flash program memory |
| 204 | + uint8_t mem[DATA_SIZE]; |
| 205 | + OK(uc_mem_read(uc, MEM_BASE, mem, sizeof(mem))); |
| 206 | + TEST_CHECK(memcmp(mem, DATA, DATA_SIZE) == 0); |
| 207 | + |
| 208 | + TEST_CHECK(eventmem_trace.count == 2*DATA_SIZE); |
| 209 | + for (unsigned i = 0; i < DATA_SIZE; i++) { |
| 210 | + const MEM_HOOK_RESULT *const mr = &eventmem_trace.results[2*i]; |
| 211 | + TEST_CHECK(mr->type == UC_MEM_READ); |
| 212 | + TEST_CHECK(mr->address == (UC_AVR_MEM_FLASH|(DATA_BASE+i))); |
| 213 | + TEST_CHECK(mr->size == 1); |
| 214 | + TEST_CHECK(mr->value == 0); |
| 215 | + |
| 216 | + const MEM_HOOK_RESULT *const mw = &eventmem_trace.results[2*i+1]; |
| 217 | + TEST_CHECK(mw->type == UC_MEM_WRITE); |
| 218 | + TEST_CHECK(mw->address == MEM_BASE+i); |
| 219 | + TEST_CHECK(mw->size == 1); |
| 220 | + TEST_CHECK(mw->value == DATA[i]); |
| 221 | + } |
| 222 | + |
| 223 | + OK(uc_close(uc)); |
| 224 | +} |
| 225 | + |
| 226 | +static void test_avr_full_exec(void) |
| 227 | +{ |
| 228 | + uc_engine *uc = NULL; |
| 229 | + |
| 230 | + uc_common_setup(&uc, 0, FLASH, FLASH_SIZE); |
| 231 | + |
| 232 | + const uint64_t code_start = ADDR[ADDR__init__]; |
| 233 | + OK(uc_emu_start(uc, code_start, ADDR[ADDR__init__+1], 0, 0)); |
| 234 | + |
| 235 | + uint32_t r_pc; |
| 236 | + OK(uc_reg_read(uc, UC_AVR_REG_PC, &r_pc)); |
| 237 | + TEST_CHECK(r_pc == ADDR[ADDR__init__+1]); |
| 238 | + |
| 239 | + uint32_t r_sp = MEM_BASE + MEM_SIZE - 1; |
| 240 | + OK(uc_reg_write(uc, UC_AVR_REG_SP, &r_sp)); |
| 241 | + |
| 242 | + const uint64_t exits[] = { |
| 243 | + ADDR[ADDR_main], |
| 244 | + ADDR[ADDR__stop_program] |
| 245 | + }; |
| 246 | + OK(uc_ctl_exits_enable(uc)); |
| 247 | + OK(uc_ctl_set_exits(uc, exits, ARRAY_ELEMS(exits))); |
| 248 | + |
| 249 | + const uint64_t code_main = ADDR[ADDR_main]; |
| 250 | + OK(uc_emu_start(uc, code_main, 0, 0, 0)); |
| 251 | + |
| 252 | + uint8_t r[32] = {0,}; |
| 253 | + OK(uc_reg_read(uc, UC_AVR_REG_R25, &r[25])); |
| 254 | + OK(uc_reg_read(uc, UC_AVR_REG_R24, &r[24])); |
| 255 | + TEST_CHECK(r[25] == 0 && r[24] == 0); |
| 256 | + |
| 257 | + OK(uc_close(uc)); |
| 258 | +} |
| 259 | + |
| 260 | +TEST_LIST = { |
| 261 | + {"test_avr_basic_alu", test_avr_basic_alu}, |
| 262 | + {"test_avr_basic_mem", test_avr_basic_mem}, |
| 263 | + {"test_avr_full_exec", test_avr_full_exec}, |
| 264 | + {NULL, NULL} |
| 265 | +}; |
0 commit comments