├── .gitignore ├── LICENCE ├── Makefile.am ├── README.md ├── TODO.md ├── autogen.sh ├── configure.ac └── src ├── args.c ├── args.h ├── peripheral.c ├── peripheral.h ├── peripheral ├── aux.c ├── aux.h ├── gpio.c └── gpio.h ├── raspcorn.c ├── uc_debug.c └── uc_debug.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Compiled Headers 19 | *.gch 20 | 21 | # Executables 22 | *.exe 23 | *.out 24 | *.app 25 | 26 | 27 | *.swp 28 | *.DS_Store 29 | *.in 30 | 31 | # Autotools' files 32 | m4/* 33 | aclocal.m4 34 | autom4te.cache/ 35 | aux-dist/ 36 | compile 37 | config.guess 38 | config.log 39 | config.status 40 | config.sub 41 | configure 42 | depcomp 43 | missing 44 | ltmain.sh 45 | libtool 46 | install-sh 47 | stamp-h1 48 | Makefile 49 | src/Makefile 50 | include/Makefile 51 | 52 | .deps 53 | src/raspcorn 54 | build/ 55 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Kitling, Kitlith 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = raspcorn 2 | 3 | raspcorn_SOURCES = src/peripheral.c src/args.c src/uc_debug.c src/raspcorn.c \ 4 | src/peripheral/gpio.c 5 | raspcorn_CFLAGS = $(UNICORN_CFLAGS) 6 | raspcorn_LDADD = $(UNICORN_LIBS) 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raspcorn 2 | _In case you need to program for the Raspberry Pi without a Raspberry Pi..._ 3 | 4 | This is made in the hopes that it will be useful. This isn't, yet. Move along. 5 | 6 | I'm also hacking this up for https://github.com/kitling/NTRPi , as that was the primary reason that I made this. 7 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | - Implement GPIO interface. 3 | - Finish implementing GPIO. 4 | - Implement UART (and interface). 5 | - Implement some sort of profile system for the various raspberry pis. 6 | - Make this actually useful. 7 | - Make this not a hack on top of a project. 8 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf -vfi 3 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([raspcorn], [0.0.0], [kitlith@kitl.pw]) 2 | AC_CONFIG_AUX_DIR(aux-dist) 3 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 4 | 5 | AC_PROG_MAKE_SET 6 | AC_PROG_CC 7 | AC_LANG(C) 8 | 9 | PKG_CHECK_MODULES([UNICORN], [unicorn]) 10 | AC_SUBST([UNICORN_CFLAGS]) 11 | AC_SUBST([UNICORN_LIBS]) 12 | 13 | AC_CONFIG_FILES([Makefile]) 14 | AC_OUTPUT 15 | -------------------------------------------------------------------------------- /src/args.c: -------------------------------------------------------------------------------- 1 | #include "args.h" 2 | #include 3 | #include 4 | #include 5 | 6 | static struct option longopts[] = { 7 | {"load_address", required_argument, NULL, 'l'}, 8 | {"peripherals", required_argument, NULL, 'p'}, 9 | {0,0,0,0} 10 | }; 11 | 12 | void parse_args(int argc, char **argv, struct prog_options *opt) { 13 | // memset(opt, 0, sizeof(*opt)); 14 | char c; 15 | while ((c = getopt_long(argc, argv, "l:p:", longopts, 0)) != -1) { 16 | switch(c) { 17 | case 'l': 18 | if (optarg != NULL) { 19 | opt->load_addr = strtol(optarg, NULL, 0); 20 | } else exit(EXIT_FAILURE); 21 | break; 22 | case 'p': 23 | if (optarg != NULL){ 24 | opt->peripheral_base = strtol(optarg, NULL, 0); 25 | } else exit(EXIT_FAILURE); 26 | break; 27 | case '?': 28 | break; // error already handled 29 | } // TODO: Actually add options. 30 | } 31 | unsigned int nonoptions = argc - optind; 32 | if (nonoptions > 0) { 33 | opt->bin_filename = argv[optind]; --nonoptions; 34 | if (nonoptions > 0){ 35 | opt->extra_args = argv + (sizeof(*argv) * optind); 36 | opt->extra_argc = argc - optind - 1; 37 | } 38 | } else { 39 | puts("Not enough arguments! Pass a filename, or '-' for stdin."); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/args.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef RASPCORN_ARGS 4 | #define RASPCORN_ARGS 5 | 6 | #include 7 | #include 8 | 9 | struct prog_options { 10 | uint64_t load_addr; 11 | uint64_t peripheral_base; 12 | char **extra_args; 13 | unsigned int extra_argc; 14 | char *bin_filename; 15 | }; 16 | 17 | void parse_args(int argc, char **argv, struct prog_options *opt); 18 | 19 | #endif /* end of include guard: RASPCORN_ARGS */ 20 | -------------------------------------------------------------------------------- /src/peripheral.c: -------------------------------------------------------------------------------- 1 | #include "peripheral.h" 2 | 3 | #include "peripheral/gpio.h" 4 | 5 | int peripheral_init(uc_engine *emu, uint64_t peripheral_base) { 6 | gpio_init(emu, peripheral_base); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /src/peripheral.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef RASPCORN_PERIPHERAL 4 | #define RASPCORN_PERIPHERAL 5 | 6 | #include 7 | 8 | int peripheral_init(uc_engine *emu, uint64_t peripheral_base); 9 | 10 | #endif /* end of include guard: RASPCORN_PERIPHERAL */ 11 | -------------------------------------------------------------------------------- /src/peripheral/aux.c: -------------------------------------------------------------------------------- 1 | #include "aux.h" 2 | #include "../uc_debug.h" 3 | 4 | static void aux_callback(uc_engine *emu, uc_mem_type type, uint64_t addr, 5 | int size, uint64_t *value, void *nothing) { 6 | if ((type == UC_MEM_READ) || (type == UC_MEM_FETCH)) { 7 | 8 | } else if (type == UC_MEM_WRITE) { 9 | switch (addr) { 10 | case 0: break; 11 | } 12 | } 13 | } 14 | 15 | int aux_init(uc_engine *emu) { 16 | UC(mmio_map, AUX_BASE, 0x1000, aux_callback, NULL); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /src/peripheral/aux.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef RASPCORN_PERIPH_AUX 4 | #define RASPCORN_PERIPH_AUX 5 | 6 | #include "../peripheral.h" 7 | 8 | #define AUX_BASE (PERIPHERAL_BASE + 0x00215000) 9 | 10 | int aux_init(uc_engine *emu); 11 | 12 | #endif /* end of include guard: RASPCORN_PERIPH_AUX */ 13 | -------------------------------------------------------------------------------- /src/peripheral/gpio.c: -------------------------------------------------------------------------------- 1 | #include "gpio.h" 2 | #include "../uc_debug.h" 3 | 4 | #include 5 | #include 6 | 7 | #define UC_ENGINE_VAR emu 8 | 9 | // static struct gpioPin pins[54]; 10 | static const uint8_t testCmds[] = { 11 | 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 13 | 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 14 | }; 15 | 16 | static unsigned testPos; 17 | static unsigned dataPos; 18 | 19 | static void gpio_callback(uc_engine *emu, uc_mem_type type, uint64_t addr, 20 | int size, uint64_t *value, void *nothing) { 21 | if ((type == UC_MEM_READ) || (type == UC_MEM_FETCH)) { 22 | switch (addr) { 23 | case GPLEV0: 24 | if (testPos < sizeof(testCmds)) { 25 | printf("CMDByte: 0x%02x\n", testCmds[testPos]); 26 | *value = testCmds[testPos++] << 2; 27 | dataPos = 0; 28 | } 29 | break; 30 | case GPEDS0: 31 | // puts("Checked pin status!"); 32 | *value = (1 << 10) | ((dataPos >= 0x20) << 11); 33 | if (dataPos > 0x20) dataPos = 0; 34 | break; 35 | } 36 | // *value = 0; 37 | // printf(">>> GPIO Read at 0x%" PRIx64 ", size = 0x%x, return = 0x%lx\n", 38 | // GPIO_BASE + addr, size, *value); 39 | } 40 | else if (type == UC_MEM_WRITE) { 41 | switch (addr) { 42 | // case GPFSEL1: 43 | // for (unsigned pinno = 0; pinno < 10; ++pinno) { 44 | // pins[pinno].function = (enum gpioFunction)((*value >> (pinno*3)) & 0b111); 45 | // } 46 | // printf(">>> GFPSEL1 == 0x%" PRIx64 "\n", *value); 47 | // break; 48 | case GPSET0: 49 | printf("0x%02x\n", (unsigned char)(*value >> 2)); 50 | ++dataPos; 51 | break; 52 | case GPCLR0: 53 | // if (*value & (1<<16)) { 54 | // puts("Status LED off!"); 55 | // } 56 | break; 57 | default: 58 | // printf(">>> GPIO Write at 0x%" PRIx64 ", size = 0x%x, value = 0x%lx\n", 59 | // GPIO_BASE + addr, size, *value); 60 | break; 61 | } 62 | } 63 | } 64 | 65 | int gpio_init(uc_engine *emu, uint64_t peripheral_base) { 66 | UC(mmio_map, peripheral_base + GPIO_BASE, 0x1000, gpio_callback, NULL); 67 | return 0; 68 | } 69 | 70 | #undef UC_ENGINE_VAR 71 | -------------------------------------------------------------------------------- /src/peripheral/gpio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef RASPCORN_PERIPH_GPIO 4 | #define RASPCORN_PERIPH_GPIO 5 | 6 | #include "../peripheral.h" 7 | 8 | #define GPIO_BASE 0x00200000 9 | #define GPFSEL0 0x00 10 | #define GPFSEL1 0x04 11 | 12 | #define GPSET0 0x1C 13 | 14 | #define GPCLR0 0x28 15 | 16 | #define GPLEV0 0x34 17 | 18 | #define GPEDS0 0x40 19 | 20 | #define GPREN0 0x4C 21 | 22 | #define GPFEN0 0x58 23 | 24 | enum gpioFunction { 25 | GPIO_INPUT = 0b000, 26 | GPIO_OUTPUT = 0b001, 27 | GPIO_ALT0 = 0b100, 28 | GPIO_ALT1 = 0b101, 29 | GPIO_ALT2 = 0b110, 30 | GPIO_ALT3 = 0b111, 31 | GPIO_ALT4 = 0b011, 32 | GPIO_ALT5 = 0b010 33 | }; 34 | 35 | struct gpioPin { 36 | enum gpioFunction function; 37 | bool status; 38 | }; 39 | 40 | int gpio_init(uc_engine *emu, uint64_t peripheral_base); 41 | 42 | #endif /* end of include guard: RASPCORN_PERIPH_GPIO */ 43 | -------------------------------------------------------------------------------- /src/raspcorn.c: -------------------------------------------------------------------------------- 1 | // windows specific 2 | #ifdef _MSC_VER 3 | #include 4 | #include 5 | #define PRIx64 "llX" 6 | #ifdef DYNLOAD 7 | #include "unicorn_dynload.h" 8 | #else // DYNLOAD 9 | #include 10 | #ifdef _WIN64 11 | #pragma comment(lib, "unicorn_staload64.lib") 12 | #else // _WIN64 13 | #pragma comment(lib, "unicorn_staload.lib") 14 | #endif // _WIN64 15 | #endif // DYNLOAD 16 | 17 | // posix specific 18 | #else // _MSC_VER 19 | #include 20 | #include 21 | #include 22 | #endif // _MSC_VER 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "uc_debug.h" 29 | #include "args.h" 30 | #include "peripheral.h" 31 | 32 | #define UC_ENGINE_VAR emu 33 | 34 | uc_engine *tmp; // Need a better way to do this! 35 | 36 | void sigINT(int whocares) { 37 | puts("\nCPU Context:"); 38 | print_ctx(tmp); 39 | exit(0); 40 | } 41 | 42 | int main(int argc, char **argv) { 43 | struct prog_options opt = { 44 | .load_addr = 0x8000, 45 | .peripheral_base = 0x20000000 46 | }; 47 | parse_args(argc, argv, &opt); 48 | 49 | FILE *codefile; 50 | if (opt.bin_filename && !strcmp(opt.bin_filename, "-")) { 51 | codefile = stdin; 52 | } else if (opt.bin_filename) { 53 | codefile = fopen(opt.bin_filename, "rb"); 54 | if (codefile == NULL) { 55 | puts("The file does not appear to exist!"); 56 | exit(-1); 57 | } 58 | } else { 59 | exit(-1); 60 | } 61 | 62 | puts("\x28\x20\xcd\xa1\xc2\xb0\xcd\x9c\xca\x96\xcd\xa1\xc2\xb0\x29"); // ( ͡°͜ʖ͡°) 63 | 64 | uc_engine *emu; 65 | 66 | errorp(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &emu), __LINE__, __FILE__, "uc_open", emu); 67 | UC(mem_map, 0, 2*1024*1024, UC_PROT_ALL); 68 | tmp = emu; 69 | signal(SIGINT, sigINT); 70 | 71 | char c = fgetc(codefile); 72 | unsigned int code_size = 0; 73 | while (!feof(codefile)) { 74 | UC(mem_write, opt.load_addr + (sizeof(char)*code_size), &c, sizeof(char)); 75 | ++code_size; 76 | c = fgetc(codefile); 77 | } 78 | 79 | peripheral_init(emu, opt.peripheral_base); 80 | 81 | uc_hook invalid, read; 82 | UC(hook_add, &invalid, UC_HOOK_MEM_INVALID, (void*)hook_mem_invalid, NULL, 1, 0); 83 | // UC(hook_add, &read, UC_HOOK_MEM_READ, (void*)hook_mem_read, NULL, 1, 0); 84 | // That cast doesn't seem right... 85 | 86 | UC(emu_start, opt.load_addr, opt.load_addr + (sizeof(char)*code_size), 0, 0); 87 | 88 | puts("Emulation Finished. CPU Context:"); 89 | print_ctx(emu); 90 | 91 | return 0; 92 | } 93 | 94 | #undef UC_ENGINE_VAR 95 | -------------------------------------------------------------------------------- /src/uc_debug.c: -------------------------------------------------------------------------------- 1 | #include "uc_debug.h" 2 | #include 3 | #include 4 | 5 | void errorp(uc_err error, int lineno, const char* file, const char *func_name, uc_engine *emu) { 6 | if (error) { 7 | printf("%s() errored, and returned: %u (%s)\n", 8 | func_name, error, uc_strerror(error)); 9 | print_ctx(emu); 10 | exit(-1); 11 | } 12 | } 13 | 14 | #define UC_ENGINE_VAR emu 15 | 16 | static const int registers[] = { UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3, 17 | UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R6, UC_ARM_REG_R7, 18 | UC_ARM_REG_R8, UC_ARM_REG_R9, UC_ARM_REG_R10, UC_ARM_REG_R11, 19 | UC_ARM_REG_R12, UC_ARM_REG_SP, UC_ARM_REG_LR, UC_ARM_REG_PC }; 20 | 21 | void print_ctx(uc_engine *emu) { 22 | 23 | int64_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr, pc; 24 | int64_t *reg_array[] = {&r0, &r1, &r2, &r3, &r4, &r5, &r6, &r7, &r8, &r9, &r10, &r11, &r12, &sp, &lr, &pc}; 25 | // No error checking wrapper because this can be called from inside... 26 | // Don't want to infinitely loop and stackoverflow. 27 | // There is a way to read a bunch of registers at once! But is this everything... 28 | uc_reg_read_batch(emu, (int*)registers, (void**)reg_array, 16); 29 | printf( "R0 > 0x%016lx | R1 > 0x%016lx | R2 > 0x%016lx | R3 > 0x%016lx\n" 30 | "R4 > 0x%016lx | R5 > 0x%016lx | R6 > 0x%016lx | R7 > 0x%016lx\n" 31 | "R8 > 0x%016lx | R9 > 0x%016lx | R10> 0x%016lx | R11> 0x%016lx\n" 32 | "R12> 0x%016lx | SP > 0x%016lx | LR > 0x%016lx | PC > 0x%016lx\n", 33 | r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr, pc); 34 | } 35 | 36 | #undef UC_ENGINE_VAR 37 | 38 | // callback for tracing invalid memory access (READ/WRITE/EXEC) 39 | // Thanks, unicorn examples. 40 | bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint64_t addr, int size, 41 | int64_t value, void *user_data) { 42 | switch(type) { 43 | default: 44 | printf(">>> UC_HOOK_MEM_INVALID type: %d at 0x%" PRIx64 "\n", type, addr); 45 | return false; 46 | case UC_MEM_READ_UNMAPPED: 47 | printf(">>> Read from unmapped memory at 0x%" PRIx64 ", Size = %u\n", addr, size); 48 | return false; 49 | case UC_MEM_WRITE_UNMAPPED: 50 | printf(">>> Write to unmapped memory at 0x%" PRIx64 ", Size = %u, Value = 0x%" PRIx64 "\n", addr, size, value); 51 | return false; 52 | case UC_MEM_FETCH_UNMAPPED: 53 | printf(">>> Fetch from unmapped memory at 0x%" PRIx64 "\n", addr); 54 | return false; 55 | case UC_MEM_FETCH_PROT: 56 | printf(">>> Fetch from non-executable memory at 0x%" PRIx64 "\n", addr); 57 | return false; 58 | case UC_MEM_WRITE_PROT: 59 | printf(">>> Write to non-writeable memory at 0x%" PRIx64 ", Size = %u, Value = 0x%" PRIx64 "\n", addr, size, value); 60 | return false; 61 | case UC_MEM_READ_PROT: 62 | printf(">>> Read from non-readable memory at 0x%" PRIx64 ", Size = %u\n", addr, size); 63 | return false; 64 | } 65 | } 66 | 67 | void hook_mem_read(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *n) { 68 | printf(">>> Read 0x%08lx from 0x%08lx.\n", value, address); 69 | } 70 | -------------------------------------------------------------------------------- /src/uc_debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef RASPCORN_UC_DEBUG 4 | #define RASPCORN_UC_DEBUG 5 | 6 | #include 7 | 8 | void errorp(uc_err error, int lineno, const char* file, 9 | const char *func_name, uc_engine *emu); 10 | 11 | void print_ctx(uc_engine *emu); 12 | 13 | bool hook_mem_invalid(uc_engine *uc, uc_mem_type type, uint64_t addr, int size, 14 | int64_t value, void *user_data); 15 | 16 | void hook_mem_read(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, void *n); 17 | // This uses a define, UC_ENGINE_VAR, so that I'm not hardcoding 'emu', though 18 | // that will probably be the variable name in most cases. 19 | #define UC(func, ...) errorp(uc_##func(UC_ENGINE_VAR, __VA_ARGS__), __LINE__, __FILE__, "uc_" #func, UC_ENGINE_VAR); 20 | 21 | #endif /* end of include guard: RASPCORN_UC_DEBUG */ 22 | --------------------------------------------------------------------------------