/* * AVR emulator * The device emulator subprocess * * 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 "devprocess.h" #include "hardware.h" #include "cpu.h" #include #include /* Global pointer to the device instance from within the device process. */ struct avremu_device_instance *avremu_dev; static void device_cleanup(void) { cpu_exit(); dump_cpu(stdout); avr_do_cleanup(); } static void __attribute__((noreturn)) device_shutdown(int error) { device_cleanup(); sendnotify_to_host(avremu_dev, HOSTMSG_EXIT); exit(error >= 0 ? error : -error); } static void device_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"); device_shutdown(1); break; case SIGTERM: case SIGINT: printf("Emulated device shutting down...\n"); device_shutdown(0); break; default: fprintf(stderr, "Device caught unknown signal %d\n", signr); device_shutdown(ENOSYS); } } static int device_handle_msg_from_host(struct avrmsg_to_device *msg) { char *data; size_t size; int err; ipc_result_t result; //FIXME we should probably try to drain the buffer on error instead of exiting. switch (msg->msg) { case DEVMSG_LOADFLASH: size = msg->size; data = avr_malloc(size); if (!data) { ipc_send_result(&avremu_dev->msg_from_host, -ENOMEM); return 0; } ipc_send_result(&avremu_dev->msg_from_host, 0); err = ipc_payload_receive(&avremu_dev->msg_from_host, data, size); if (err) { ipc_send_result(&avremu_dev->msg_from_host, err); free(data); return 0; } result = cpu_initialize(data, size); free(data); ipc_send_result(&avremu_dev->msg_from_host, result); if (!result) printf("CPU initialized\n"); break; case DEVMSG_RESET: result = cpu_reset(); ipc_send_result(&avremu_dev->msg_from_host, result); if (!result) printf("CPU reset\n"); break; } return 0; } int avr_device_process_run(struct avremu_device_instance *dev) { struct sigaction sa; int err; struct avrmsg_to_device msg; sigset_t blockset; avremu_dev = dev; sa.sa_handler = device_signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGSEGV, &sa, NULL); sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); /* Block these signals while processing messages. */ sigemptyset(&blockset); sigaddset(&blockset, SIGTERM); sigaddset(&blockset, SIGINT); err = avr_do_setup(dev->type); if (err) return err; err = sendnotify_to_host(dev, HOSTMSG_READY); if (err) return -EIO; while (1) { err = pollmsg_from_host(dev, &msg); if (err == 0) continue; if (unlikely(err < 0)) { fprintf(stderr, "Device message RX failed %d\n", err); err = -EIO; goto error; } pthread_sigmask(SIG_BLOCK, &blockset, NULL); err = device_handle_msg_from_host(&msg); pthread_sigmask(SIG_UNBLOCK, &blockset, NULL); if (unlikely(err)) goto error; } return ENOTTY; error: device_cleanup(); return err; }