Files
retrobsd/tools/virtualmips/vm.c
2014-04-09 14:27:18 +01:00

459 lines
10 KiB
C

/*
* Cisco router simulation platform.
* Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
*
* Virtual machine abstraction.
*/
/*
* Copyright (C) yajin 2008 <yajinzhou@gmail.com >
*
* This file is part of the virtualmips distribution.
* See LICENSE file for terms of the license.
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <assert.h>
#include "device.h"
#include "cpu.h"
#include "vm.h"
#include "dev_vtty.h"
#include "debug.h"
/* Get VM type */
char *vm_get_type (vm_instance_t * vm)
{
char *machine;
switch (vm->type) {
case VM_TYPE_SWARM:
machine = "swarm";
break;
case VM_TYPE_ADM5120:
machine = "ADM5120";
break;
case VM_TYPE_PAVO:
machine = "PAVO";
break;
case VM_TYPE_PIC32:
machine = "PIC32";
break;
default:
machine = "unknown";
break;
}
return machine;
}
/* Get platform type */
char *vm_get_platform_type (vm_instance_t * vm)
{
char *machine;
switch (vm->type) {
case VM_TYPE_SWARM:
machine = "swarm";
break;
case VM_TYPE_ADM5120:
machine = "ADM5120";
break;
case VM_TYPE_PIC32:
machine = "PIC32";
break;
default:
machine = "VM";
break;
}
return machine;
}
/* Generate a filename for use by the instance */
static char *vm_build_filename (vm_instance_t * vm, char *name)
{
char *filename;
filename = dyn_sprintf ("%s-%s", vm->name, name);
assert (filename != NULL);
return filename;
}
/* Log a message */
void vm_flog (vm_instance_t * vm, char *module, char *format, va_list ap)
{
if (vm->log_fd)
m_flog (vm->log_fd, module, format, ap);
}
/* Log a message */
void vm_log (vm_instance_t * vm, char *module, char *format, ...)
{
va_list ap;
if (vm->log_fd) {
va_start (ap, format);
vm_flog (vm, module, format, ap);
va_end (ap);
}
}
/* Close the log file */
int vm_close_log (vm_instance_t * vm)
{
if (vm->log_fd)
fclose (vm->log_fd);
free (vm->log_file);
vm->log_file = NULL;
vm->log_fd = NULL;
return (0);
}
/* Create the log file */
int vm_create_log (vm_instance_t * vm)
{
if (vm->log_file_enabled) {
vm_close_log (vm);
if (!(vm->log_file = vm_build_filename (vm, "log.txt")))
return (-1);
if (!(vm->log_fd = fopen (vm->log_file, "w"))) {
fprintf (stderr, "VM %s: unable to create log file '%s'\n",
vm->name, vm->log_file);
free (vm->log_file);
vm->log_file = NULL;
return (-1);
}
}
return (0);
}
/* Error message */
void vm_error (vm_instance_t * vm, char *format, ...)
{
char buffer[2048];
va_list ap;
va_start (ap, format);
vsnprintf (buffer, sizeof (buffer), format, ap);
va_end (ap);
fprintf (stderr, "%s '%s': %s", vm_get_platform_type (vm), vm->name,
buffer);
}
/* Create a new VM instance */
vm_instance_t *vm_create (const char *name, int machine_type)
{
vm_instance_t *vm;
int i;
vm = malloc (sizeof (*vm));
if (!vm) {
fprintf (stderr, "VM %s: unable to create new instance!\n", name);
return NULL;
}
memset (vm, 0, sizeof (*vm));
vm->type = machine_type;
vm->status = VM_STATUS_HALTED;
vm->jit_use = 0;
vm->vtty_type[0] = VTTY_TYPE_TERM;
for (i=1; i<NVTTY; i++)
vm->vtty_type[i] = VTTY_TYPE_NONE;
//vm->timer_irq_check_itv = VM_TIMER_IRQ_CHECK_ITV;
vm->log_file_enabled = TRUE;
vm->name = strdup (name);
if (!name) {
fprintf (stderr, "VM %s: unable to store instance name!\n", name);
goto err_name;
}
/* create log file */
if (vm_create_log (vm) == -1) {
free (vm->name);
err_name:
free (vm);
return NULL;
}
return vm;
}
/*
* Shutdown hardware resources used by a VM.
* The CPU must have been stopped.
*/
static int vm_hardware_shutdown (vm_instance_t * vm)
{
//int i;
if ((vm->status == VM_STATUS_HALTED) || !vm->cpu_group) {
vm_log (vm, "VM", "trying to shutdown an inactive VM.\n");
return (-1);
}
vm_log (vm, "VM", "shutdown procedure engaged.\n");
/* Mark the VM as halted */
vm->status = VM_STATUS_HALTED;
/* Disable NVRAM operations */
// vm->nvram_extract_config = NULL;
// vm->nvram_push_config = NULL;
/* Free the object list */
// vm_object_free_list(vm);
/* Free resources used by PCI busses */
// vm_log(vm,"VM","removing PCI busses.\n");
// pci_io_data_remove(vm,vm->pci_io_space);
// pci_bus_remove(vm->pci_bus[0]);
// pci_bus_remove(vm->pci_bus[1]);
// vm->pci_bus[0] = vm->pci_bus[1] = NULL;
/* Free the PCI bus pool */
/* for(i=0;i<VM_PCI_POOL_SIZE;i++) {
* if (vm->pci_bus_pool[i] != NULL) {
* pci_bus_remove(vm->pci_bus_pool[i]);
* vm->pci_bus_pool[i] = NULL;
* }
* } */
/* Remove the IRQ routing vectors */
vm->set_irq = NULL;
vm->clear_irq = NULL;
/* Delete the VTTY for Console and AUX ports */
vm_log (vm, "VM", "deleting VTTY.\n");
vm_delete_vtty (vm);
/* Delete system CPU group */
vm_log (vm, "VM", "deleting system CPUs.\n");
cpu_group_delete (vm->cpu_group);
vm->cpu_group = NULL;
vm->boot_cpu = NULL;
vm_log (vm, "VM", "shutdown procedure completed.\n");
return (0);
}
/* Free resources used by a VM */
void vm_free (vm_instance_t * vm)
{
if (vm != NULL) {
/* Free hardware resources */
vm_hardware_shutdown (vm);
/* Close log file */
vm_close_log (vm);
/* Remove the lock file */
// vm_release_lock(vm,TRUE);
/* Free all chunks */
// vm_chunk_free_all(vm);
/* Free various elements */
// free(vm->ghost_ram_filename);
// free(vm->sym_filename);
//free(vm->ios_image);
// free(vm->ios_config);
//free(vm->rom_filename);
free (vm->name);
free (vm);
}
}
int dev_ram_init (vm_instance_t * vm, char *name, m_pa_t paddr,
m_uint32_t len);
/*
* Initialize RAM
*/
int vm_ram_init (vm_instance_t * vm, m_pa_t paddr)
{
m_uint32_t len;
len = vm->ram_size * 1024;
#ifdef SIM_PAVO
/*
Why plus 0x2000 (8k) for PAVO??
It seems that jz4740 has an extra 8k boot memory.
I am not sure. But uboot use the extra memory space beyond 64M memory space.
So I just add 0x2000 to ram.
*/
len += 0x2000;
#endif
return (dev_ram_init (vm, "ram", paddr, len));
}
/* Initialize VTTY */
int vm_init_vtty (vm_instance_t * vm)
{
int i;
/* Create Console and AUX ports */
vm->vtty_con[0] = vtty_create (vm, "Console port",
vm->vtty_type[0], vm->vtty_tcp_port[0],
&vm->vtty_serial_option[0]);
for (i=1; i<NVTTY; i++)
vm->vtty_con[i] = vtty_create (vm, "UART port",
vm->vtty_type[i], vm->vtty_tcp_port[i],
&vm->vtty_serial_option[i]);
return (0);
}
/* Delete VTTY */
void vm_delete_vtty (vm_instance_t * vm)
{
int i;
for (i=0; i<NVTTY; i++) {
vtty_delete (vm->vtty_con[i]);
vm->vtty_con[i] = NULL;
}
}
/* Bind a device to a virtual machine */
int vm_bind_device (vm_instance_t * vm, struct vdevice *dev)
{
struct vdevice **cur;
u_int i;
/*
* Add this device to the device array. The index in the device array
* is used by the MTS subsystem.
*/
for (i = 0; i < VM_DEVICE_MAX; i++)
if (!vm->dev_array[i])
break;
if (i == VM_DEVICE_MAX) {
fprintf (stderr, "VM: vm_bind_device: device table full.\n");
return (-1);
}
vm->dev_array[i] = dev;
dev->id = i;
/*
* Add it to the linked-list (devices are ordered by physical addresses).
*/
for (cur = &vm->dev_list; *cur; cur = &(*cur)->next)
if ((*cur)->phys_addr > dev->phys_addr)
break;
dev->next = *cur;
if (*cur)
(*cur)->pprev = &dev->next;
dev->pprev = cur;
*cur = dev;
return (0);
}
/* Unbind a device from a virtual machine */
int vm_unbind_device (vm_instance_t * vm, struct vdevice *dev)
{
u_int i;
if (!dev || !dev->pprev)
return (-1);
/* Remove the device from the linked list */
if (dev->next)
dev->next->pprev = dev->pprev;
*(dev->pprev) = dev->next;
/* Remove the device from the device array */
for (i = 0; i < VM_DEVICE_MAX; i++)
if (vm->dev_array[i] == dev) {
vm->dev_array[i] = NULL;
break;
}
/* Clear device list info */
dev->next = NULL;
dev->pprev = NULL;
return (0);
}
/* Map a device at the specified physical address */
int vm_map_device (vm_instance_t * vm, struct vdevice *dev, m_pa_t base_addr)
{
#if 0
/* Suspend VM activity */
vm_suspend (vm);
if (cpu_group_sync_state (vm->cpu_group) == -1) {
fprintf (stderr, "VM%u: unable to sync with system CPUs.\n",
vm->instance_id);
return (-1);
}
#endif
/* Unbind the device if it was already active */
vm_unbind_device (vm, dev);
/* Map the device at the new base address and rebuild MTS */
dev->phys_addr = base_addr;
vm_bind_device (vm, dev);
cpu_group_rebuild_mts (vm->cpu_group);
#if 0
vm_resume (vm);
#endif
return (0);
}
/* Suspend a VM instance */
int vm_suspend (vm_instance_t * vm)
{
if (vm->status == VM_STATUS_RUNNING) {
cpu_group_save_state (vm->cpu_group);
cpu_group_set_state (vm->cpu_group, CPU_STATE_SUSPENDED);
vm->status = VM_STATUS_SUSPENDED;
}
return (0);
}
/* Resume a VM instance */
int vm_resume (vm_instance_t * vm)
{
if (vm->status == VM_STATUS_SUSPENDED) {
cpu_group_restore_state (vm->cpu_group);
vm->status = VM_STATUS_RUNNING;
}
return (0);
}
/* Stop an instance */
int vm_stop (vm_instance_t * vm)
{
cpu_group_stop_all_cpu (vm->cpu_group);
vm->status = VM_STATUS_SHUTDOWN;
return (0);
}
/* Monitor an instance periodically */
void vm_monitor (vm_instance_t * vm)
{
while (vm->status != VM_STATUS_SHUTDOWN)
usleep (1000);
}