/*
* Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved.
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*/
/*
* VPE support module
*
* Provides support for loading a MIPS SP program on VPE1.
* The SP enviroment is rather simple, no tlb's. It needs to be relocatable
* (or partially linked). You should initialise your stack in the startup
* code. This loader looks for the symbol __start and sets up
* execution to resume from there. The MIPS SDE kit contains suitable examples.
*
* To load and run, simply cat a SP 'program file' to /dev/vpe1.
* i.e cat spapp >/dev/vpe1.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/vmalloc.h>
#include <linux/elf.h>
#include <linux/seq_file.h>
#include <linux/syscalls.h>
#include <linux/moduleloader.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/bootmem.h>
#include <asm/mipsregs.h>
#include <asm/mipsmtregs.h>
#include <asm/cacheflush.h>
#include <asm/atomic.h>
#include <asm/cpu.h>
#include <asm/mips_mt.h>
#include <asm/processor.h>
#include <asm/system.h>
#include <asm/vpe.h>
#include <asm/kspd.h>
typedef void *vpe_handle;
#ifndef ARCH_SHF_SMALL
#define ARCH_SHF_SMALL 0
#endif
/* If this is set, the section belongs in the init part of the module */
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
/*
* The number of TCs and VPEs physically available on the core
*/
static int hw_tcs, hw_vpes;
static char module_name[] = "vpe";
static int major;
static const int minor = 1; /* fixed for now */
#ifdef CONFIG_MIPS_APSP_KSPD
static struct kspd_notifications kspd_events;
static int kspd_events_reqd = 0;
#endif
/* grab the likely amount of memory we will need. */
#ifdef CONFIG_MIPS_VPE_LOADER_TOM
#define P_SIZE (2 * 1024 * 1024)
#else
/* add an overhead to the max kmalloc size for non-striped symbols/etc */
#define P_SIZE (256 * 1024)
#endif
extern unsigned long physical_memsize;
#define MAX_VPES 16
#define VPE_PATH_MAX 256
enum vpe_state {
VPE_STATE_UNUSED = 0,
VPE_STATE_INUSE,
VPE_STATE_RUNNING
};
enum tc_state {
TC_STATE_UNUSED = 0,
TC_STATE_INUSE,
TC_STATE_RUNNING,
TC_STATE_DYNAMIC
};
struct vpe {
enum vpe_state state;
/* (device) minor associated with this vpe */
int minor;
/* elfloader stuff */
void *load_addr;
unsigned long len;
char *pbuffer;
unsigned long plen;
unsigned int uid, gid;
char cwd[VPE_PATH_MAX];
unsigned long __start;
/* tc's associated with this vpe */
struct list_head tc;
/* The list of vpe's */
struct list_head list;
/* shared symbol address */
void *shared_ptr;
/* the list of who wants to know when something major happens */
struct list_head notify;
unsigned int ntcs;
};
struct tc {
enum tc_state state;
int index;
struct vpe *pvpe; /* parent VPE */
struct list_head tc; /* The list of TC's with this VPE */
struct list_head list; /* The global list of tc's */
};
struct {
/* Virtual processing elements */
struct list_head vpe_list;
/* Thread contexts */
struct list_head tc_list;
} vpecontrol = {
.vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list),
.tc_list = LIST_HEAD_INIT(vpecontrol.tc_list)
};
static void release_progmem(void *ptr);
extern void save_gp_address(unsigned int secbase, unsigned int rel);
/* get the vpe associated with this minor */
struct vpe *get_vpe(int minor)
{
struct vpe *v;
if (!cpu_has_mipsmt)
return NULL;
list_for_each_entry(v, &vpecontrol.vpe_list, list) {
if (v->minor == minor)
return v;
}
return NULL;
}
/* get the vpe associated with this minor */
struct tc *get_tc(int index)
{
struct tc *t;
list_for_each_entry(t, &vpecontrol.tc_list, list) {
if (t->index == index)
return t;
}
return NULL;
}
struct tc *get_tc_unused(void)
{
struct tc *t;
list_for_each_entry(t, &vpecontrol.tc_list, list) {
if (t->state == TC_STATE_UNUSED)
return t;
}
return NULL;
}
/* allocate a vpe and associate it with this minor (or index) */
struct vpe *alloc_vpe(int minor)
{
struct vpe *v;
if ((v = kzalloc(sizeof(struct vpe), GFP_KERNEL)) == NULL) {
return NULL;
}
INIT_LIST_HEAD(&v->tc);
list_add_tail(&v->list, &vpecontrol.vpe_list);
INIT_LIST_HEAD(&v->notify);
v->minor = minor;
return v;
}
/* allocate a tc. At startup only tc0 is running, all other can be halted. */
struc
|