305 lines
5.6 KiB
C
305 lines
5.6 KiB
C
/*
|
|
* 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 <unistd.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
|
|
#include "cpu.h"
|
|
#include "mips_memory.h"
|
|
#include "device.h"
|
|
#include "mips.h"
|
|
#include "mips_cp0.h"
|
|
#include "mips_exec.h"
|
|
#include "vm.h"
|
|
|
|
#define LOG_ENABLE 0
|
|
|
|
/*
|
|
* Log a message for a CPU
|
|
*/
|
|
void cpu_log (cpu_mips_t * cpu, char *module, char *format, ...)
|
|
{
|
|
#if LOG_ENABLE
|
|
char buffer[256];
|
|
va_list ap;
|
|
|
|
va_start (ap, format);
|
|
snprintf (buffer, sizeof (buffer), "CPU%u: %s", cpu->id, module);
|
|
vm_flog (cpu->vm, buffer, format, ap);
|
|
va_end (ap);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Start a CPU
|
|
*/
|
|
void cpu_start (cpu_mips_t * cpu)
|
|
{
|
|
if (cpu) {
|
|
cpu->state = CPU_STATE_RUNNING;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Stop a CPU
|
|
*/
|
|
void cpu_stop (cpu_mips_t * cpu)
|
|
{
|
|
if (cpu) {
|
|
cpu_log (cpu, "CPU_STATE", "Halting CPU (old state=%u)...\n",
|
|
cpu->state);
|
|
cpu->state = CPU_STATE_HALTED;
|
|
}
|
|
}
|
|
|
|
void cpu_restart (cpu_mips_t * cpu)
|
|
{
|
|
if (cpu) {
|
|
cpu_log (cpu, "CPU_STATE", "Restartting CPU (old state=%u)...\n",
|
|
cpu->state);
|
|
cpu->state = CPU_STATE_RESTARTING;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create a new CPU
|
|
*/
|
|
cpu_mips_t *cpu_create (vm_instance_t * vm, u_int type, u_int id)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
cpu = malloc (sizeof (*cpu));
|
|
if (!cpu)
|
|
return NULL;
|
|
|
|
memset (cpu, 0, sizeof (*cpu));
|
|
cpu->vm = vm;
|
|
cpu->id = id;
|
|
cpu->type = type;
|
|
cpu->state = CPU_STATE_SUSPENDED;
|
|
cpu->vm = vm;
|
|
mips_init (cpu);
|
|
return cpu;
|
|
}
|
|
|
|
/*
|
|
* Delete a CPU
|
|
*/
|
|
void cpu_delete (cpu_mips_t * cpu)
|
|
{
|
|
if (cpu) {
|
|
/* Stop activity of this CPU */
|
|
cpu_stop (cpu);
|
|
pthread_join (cpu->cpu_thread, NULL);
|
|
mips_delete (cpu);
|
|
free (cpu);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find a CPU in a group given its ID
|
|
*/
|
|
cpu_mips_t *cpu_group_find_id (cpu_group_t * group, u_int id)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
if (!group)
|
|
return NULL;
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next)
|
|
if (cpu->id == id)
|
|
return cpu;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Find the highest CPU ID in a CPU group
|
|
*/
|
|
int cpu_group_find_highest_id (cpu_group_t * group, u_int * highest_id)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
u_int max_id = 0;
|
|
|
|
if (!group || group->cpu_list)
|
|
return (-1);
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next)
|
|
if (cpu->id >= max_id)
|
|
max_id = cpu->id;
|
|
|
|
*highest_id = max_id;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Add a CPU in a CPU group
|
|
*/
|
|
int cpu_group_add (cpu_group_t * group, cpu_mips_t * cpu)
|
|
{
|
|
if (!group)
|
|
return (-1);
|
|
|
|
/* check that we don't already have a CPU with this id */
|
|
if (cpu_group_find_id (group, cpu->id) != NULL) {
|
|
fprintf (stderr, "cpu_group_add: CPU%u already present in group.\n",
|
|
cpu->id);
|
|
return (-1);
|
|
}
|
|
cpu->next = group->cpu_list;
|
|
group->cpu_list = cpu;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Create a new CPU group
|
|
*/
|
|
cpu_group_t *cpu_group_create (char *name)
|
|
{
|
|
cpu_group_t *group;
|
|
|
|
if (!(group = malloc (sizeof (*group))))
|
|
return NULL;
|
|
|
|
group->name = name;
|
|
group->cpu_list = NULL;
|
|
return group;
|
|
}
|
|
|
|
/*
|
|
* Delete a CPU group
|
|
*/
|
|
void cpu_group_delete (cpu_group_t * group)
|
|
{
|
|
cpu_mips_t *cpu, *next;
|
|
|
|
if (group != NULL) {
|
|
for (cpu = group->cpu_list; cpu; cpu = next) {
|
|
next = cpu->next;
|
|
cpu_delete (cpu);
|
|
}
|
|
free (group);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Rebuild the MTS subsystem for a CPU group
|
|
*/
|
|
int cpu_group_rebuild_mts (cpu_group_t * group)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next)
|
|
cpu->mts_rebuild (cpu);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Start all CPUs of a CPU group
|
|
*/
|
|
void cpu_group_start_all_cpu (cpu_group_t * group)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next)
|
|
cpu_start (cpu);
|
|
}
|
|
|
|
/*
|
|
* Stop all CPUs of a CPU group
|
|
*/
|
|
void cpu_group_stop_all_cpu (cpu_group_t * group)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next)
|
|
cpu_stop (cpu);
|
|
}
|
|
|
|
/*
|
|
* Set a state of all CPUs of a CPU group
|
|
*/
|
|
void cpu_group_set_state (cpu_group_t * group, u_int state)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next)
|
|
cpu->state = state;
|
|
}
|
|
|
|
/*
|
|
* Returns TRUE if all CPUs in a CPU group are inactive
|
|
*/
|
|
static int cpu_group_check_activity (cpu_group_t * group)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next) {
|
|
if (!cpu->cpu_thread_running)
|
|
continue;
|
|
if (cpu->state == CPU_STATE_RUNNING)
|
|
return (FALSE);
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* Synchronize on CPUs (all CPUs must be inactive)
|
|
*/
|
|
int cpu_group_sync_state (cpu_group_t * group)
|
|
{
|
|
m_tmcnt_t t1, t2;
|
|
|
|
/* Check that CPU activity is really suspended */
|
|
t1 = m_gettime ();
|
|
|
|
while (!cpu_group_check_activity (group)) {
|
|
t2 = m_gettime ();
|
|
|
|
if (t2 > (t1 + 10000))
|
|
return (-1);
|
|
|
|
usleep (50000);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Save state of all CPUs
|
|
*/
|
|
int cpu_group_save_state (cpu_group_t * group)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next)
|
|
cpu->prev_state = cpu->state;
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* Restore state of all CPUs
|
|
*/
|
|
int cpu_group_restore_state (cpu_group_t * group)
|
|
{
|
|
cpu_mips_t *cpu;
|
|
|
|
for (cpu = group->cpu_list; cpu; cpu = cpu->next)
|
|
cpu->state = cpu->prev_state;
|
|
|
|
return (TRUE);
|
|
}
|