/* * AVR8 emulator * * Copyright (C) 2007 Michael Buesch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "avr8emu.h" #include "avr8emu_private.h" #include "util.h" #include "cpu.h" #include "hardware.h" #include #include #include #include #include #include #include #include #include static void __attribute__((noreturn)) worker_shutdown(int error) { cpu_exit(); avr_do_cleanup(); exit(error); } static void worker_signal_handler(int signr) { switch (signr) { case SIGSEGV: fprintf(stderr, "Segmentation fault\n"); if (CHECK(1)) fprintf(stderr, "This is probably a bug in the emulator.\n"); else fprintf(stderr, "This is either a bug in the running AVR " "program or in the emulator software.\n"); dump_cpu(stderr); worker_shutdown(1); break; case SIGTERM: case SIGINT: printf("Emulation worker shutting down...\n"); trap_cpu(CPU_TRAP_EXIT, 1); dump_cpu(stdout); worker_shutdown(0); break; default: fprintf(stderr, "Worker caught unknown signal\n"); worker_shutdown(1); } } static void worker_send_result(struct avr8emu_worker *wrk, int result) { ssize_t res; res = write(wrk->result_to_host, &result, sizeof(result)); if (res != sizeof(result)) fprintf(stderr, "Failed to send result to host.\n"); } static char * receive_payload(struct avr8emu_worker *wrk, uint32_t payload_len) { char *buf; ssize_t res; buf = malloc(payload_len); if (!buf) { fprintf(stderr, "Out of memory\n"); return NULL; } res = read(wrk->message_from_host, buf, payload_len); if (res != payload_len) { fprintf(stderr, "Payload receive error\n"); free(buf); return NULL; } return buf; } static void free_payload(char *buf, uint32_t payload_len) { free(buf); } static void worker_handle_message(struct avr8emu_worker *wrk, enum avr8emu_worker_message id, uint32_t payload_len) { char *buf; int res; switch (id) { case WORKERMSG_INIT: buf = receive_payload(wrk, payload_len); if (!buf) return; res = cpu_initialize(buf, payload_len); free_payload(buf, payload_len); break; case WORKERMSG_RESET: res = cpu_reset(); break; default: res = -EINVAL; fprintf(stderr, "Worker received unknown message.\n"); } worker_send_result(wrk, res); } /* This is an emulator worker process. */ static int worker_process(struct avr8emu_worker *wrk) { struct sigaction sa; fd_set fdset; int err; ssize_t len; enum avr8emu_worker_message id; uint32_t payload_len; sa.sa_handler = worker_signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); err = avr_do_setup(wrk->type); if (err) return err; FD_ZERO(&fdset); FD_SET(wrk->message_from_host, &fdset); while (1) { err = select(FD_SETSIZE, &fdset, NULL, NULL, NULL); if (err <= 0) { if (errno == EINTR) continue; perror("Worker pipe select"); return 1; } len = read(wrk->message_from_host, &id, sizeof(id)); if (len != sizeof(id)) { fprintf(stderr, "Worker ID receive error\n"); return 1; } len = read(wrk->message_from_host, &payload_len, sizeof(payload_len)); if (len != sizeof(payload_len)) { fprintf(stderr, "Worker payload size receive error\n"); return 1; } worker_handle_message(wrk, id, payload_len); } return 1; } void send_host_message(enum avr8emu_host_message msg, const void *payload, uint32_t payload_len) {//TODO } static int send_worker_message(struct avr8emu *emu, enum avr8emu_worker_message msg, const void *payload, uint32_t payload_len) { ssize_t res; int result_code; res = write(emu->message_to_worker, &msg, sizeof(msg)); if (res != sizeof(msg)) { fprintf(stderr, "Failed to send message ID\n"); return 1; } res = write(emu->message_to_worker, &payload_len, sizeof(payload_len)); if (res != sizeof(payload_len)) { fprintf(stderr, "Failed to send payload size\n"); return 1; } if (payload_len) { res = write(emu->message_to_worker, payload, payload_len); if (res != payload_len) { fprintf(stderr, "Failed to send payload\n"); return 1; } } /* Read the result code. */ res = read(emu->result_from_worker, &result_code, sizeof(result_code)); if (res != sizeof(result_code)) { fprintf(stderr, "Failed to read message result code\n"); return 1; } if (result_code) fprintf(stderr, "Message %d failed with %d\n", msg, result_code); return result_code; } static void close_two(int fds[2]) { close(fds[0]); close(fds[1]); } static int fork_worker(struct avr8emu *emu) { struct avr8emu_worker *wrk; int err; pid_t pid; int host_pipes[2]; int worker_pipes[2]; int result_pipes[2]; int error_pipes[2]; err = pipe(host_pipes); if (err) { perror("Host communication pipes"); return err; } err = pipe(worker_pipes); if (err) { perror("Worker communication pipes"); close_two(host_pipes); return err; } err = pipe(result_pipes); if (err) { perror("Result communication pipes"); close_two(host_pipes); close_two(worker_pipes); return err; } err = pipe(error_pipes); if (err) { perror("Error communication pipes"); close_two(host_pipes); close_two(worker_pipes); close_two(result_pipes); } pid = fork(); if (pid == (pid_t)0) { /* This is the worker process. */ close(host_pipes[0]); close(worker_pipes[1]); close(result_pipes[0]); close(error_pipes[0]); wrk = avr_malloc(sizeof(*wrk)); if (!wrk) exit(1); wrk->type = emu->type; wrk->message_to_host = host_pipes[1]; wrk->message_from_host = worker_pipes[0]; wrk->result_to_host = result_pipes[1]; wrk->error_pipe = error_pipes[1]; /* Map stderr and stdout to the error_pipe. Enable line-buffering. */ dup2(wrk->error_pipe, STDERR_FILENO); dup2(wrk->error_pipe, STDOUT_FILENO); setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IOLBF, 0); /* Finally run the worker. */ err = worker_process(wrk); exit(err); } else if (pid < (pid_t)0) { perror("Forking worker process"); close_two(host_pipes); close_two(worker_pipes); close_two(result_pipes); close_two(error_pipes); return -ENOMEM; } else { /* This is the host process. */ close(host_pipes[1]); close(worker_pipes[0]); close(result_pipes[1]); close(error_pipes[1]); emu->message_to_worker = worker_pipes[1]; emu->message_from_worker = host_pipes[0]; emu->result_from_worker = result_pipes[0]; emu->error_pipe = error_pipes[0]; emu->worker_pid = pid; } return 0; } static void kill_worker(struct avr8emu *emu) { int err; err = kill(emu->worker_pid, SIGTERM); if (err) { perror("Kill worker child process"); return; } waitpid(emu->worker_pid, NULL, 0); } struct avr8emu * avr8emu_create(enum avr_setup_type type, const struct avr8emu_callbacks *cb) { struct avr8emu *emu; int err; if (!cb) return NULL; emu = avr_malloc(sizeof(*emu)); if (!emu) return NULL; emu->type = type; emu->callback = cb; err = fork_worker(emu); if (err) { free(emu); return NULL; } return emu; } void avr8emu_remove(struct avr8emu *emu) { kill_worker(emu); free(emu); } int avr8emu_initialize(struct avr8emu *emu, const char *code, size_t code_size) { return send_worker_message(emu, WORKERMSG_INIT, code, code_size); } int avr8emu_reset(struct avr8emu *emu) { return send_worker_message(emu, WORKERMSG_RESET, NULL, 0); } void avr8emu_poll_messages(struct avr8emu *emu) { fd_set fdset; struct timeval tv; int err; FD_ZERO(&fdset); FD_SET(emu->message_from_worker, &fdset); tv.tv_sec = 0; tv.tv_usec = 5000; err = select(FD_SETSIZE, &fdset, NULL, NULL, &tv); if (err > 0) { //TODO handle. } } int avr8emu_poll_status(struct avr8emu *emu) { char *buf = NULL; size_t bufsize = 0; ssize_t res; FILE *file; fd_set fdset; int err = -1; struct timeval tv; FD_ZERO(&fdset); FD_SET(emu->error_pipe, &fdset); tv.tv_sec = 0; tv.tv_usec = 5000; res = select(FD_SETSIZE, &fdset, NULL, NULL, &tv); if (res > 0) { file = fdopen(emu->error_pipe, "r"); if (!file) { fprintf(stderr, "Failed to open error pipe\n"); return -1; } while (1) { res = getline(&buf, &bufsize, file); if (res <= 1) break; if (emu->callback) emu->callback->status_message(emu, buf); err = 0; } free(buf); fclose(file); } return err; }