You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
628 lines
15 KiB
628 lines
15 KiB
//================ Copyright (c) Valve Corporation. All Rights Reserved. ===========================
|
|
//
|
|
//
|
|
//
|
|
//==================================================================================================
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Headers
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
#include "sys/memory.h"
|
|
#include "sysutil/sysutil_sysparam.h"
|
|
#include "cell/sysmodule.h"
|
|
|
|
#include "tier0/platform.h"
|
|
#include "tier0/dbg.h"
|
|
#include "tier1/utlbuffer.h"
|
|
|
|
#include <sys/timer.h>
|
|
#include <sys/spu_image.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <cell/cell_fs.h>
|
|
#include <cell/atomic.h>
|
|
#include <string.h>
|
|
|
|
#include "ps3_pathinfo.h"
|
|
|
|
#include <cell/spurs/control.h>
|
|
|
|
#include "SpuMgr_ppu.h"
|
|
|
|
#include "memdbgon.h"
|
|
|
|
typedef uint32_t uint32;
|
|
|
|
#define ASSERT Assert
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Defines
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
// Spu Mailbox Status Register
|
|
// Described in CBE architecture chapter 8.6.3 SPU Mailbox Status Register (SPU_Mbox_Stat)
|
|
|
|
#define SPU_IN_MBOX_COUNT_SHIFT (8)
|
|
#define SPU_IN_MBOX_COUNT (0xFF << SPU_IN_MBOX_COUNT_SHIFT)
|
|
|
|
#define SPU_OUT_MBOX_COUNT (0xFF)
|
|
|
|
#define SPU_OUT_INTR_MBOX_COUNT_SHIFT (16)
|
|
#define SPU_OUT_INTR_MBOX_COUNT (0xFF << SPU_OUT_INTR_MBOX_COUNT_SHIFT)
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Globals
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
// SPU manager instance
|
|
SpuMgr gSpuMgr;
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// DmaCheckAlignment
|
|
// Checks restrictions specified in SpuMgr::DmaGet
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
int DmaCheckAlignment(uint32_t src, uint32_t dest, uint32_t size)
|
|
{
|
|
#if !defined( _CERT )
|
|
|
|
uint32_t align = size;
|
|
bool error = false;
|
|
|
|
if (size >= 16 && ((size & 0xf) == 0))
|
|
{
|
|
align = 16;
|
|
}
|
|
else if (size == 8 || size == 4 || size == 2 || size == 1)
|
|
{
|
|
error = ((src & 0xF) != (dest & 0xF));
|
|
}
|
|
else
|
|
{
|
|
error = true; // bad size
|
|
}
|
|
|
|
return (!error && src && dest &&
|
|
SPUMGR_IS_ALIGNED(src, align) &&
|
|
SPUMGR_IS_ALIGNED(dest, align));
|
|
|
|
#else //!_CERT
|
|
return 1;
|
|
#endif //!_CERT
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Internal functions
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// handle_syscall
|
|
//
|
|
// interrupt handler to handle SPU interrupts
|
|
// see Handle SPU Interrupts Lv2-Uders_manual_e P34
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
void handle_syscall (uint64_t arg)
|
|
{
|
|
sys_raw_spu_t id = arg;
|
|
uint64_t stat;
|
|
int ret;
|
|
|
|
#ifndef _CERT
|
|
g_snRawSPULockHandler();
|
|
#endif
|
|
|
|
// Create a tag to handle class 2 interrupt, SPU halts fall in
|
|
// this category
|
|
|
|
ret = sys_raw_spu_get_int_stat(id, 2, &stat);
|
|
if (ret)
|
|
{
|
|
#ifndef _CERT
|
|
g_snRawSPUUnlockHandler();
|
|
#endif
|
|
sys_interrupt_thread_eoi();
|
|
}
|
|
|
|
//
|
|
// SPU Stop-and-Signal Instruction Trap
|
|
// This interrupt occurs when the SPU executes a stop-and-signal
|
|
// instruction.
|
|
//
|
|
|
|
if (stat & INTR_STOP_MASK) //stop
|
|
{
|
|
//We've hit a stop, so what kind of value is it?
|
|
uint32_t signalVal = GetStopSignal( id );
|
|
|
|
switch ( signalVal )
|
|
{
|
|
case 0x3:
|
|
|
|
// it was a stop that is in the SPU code to signal to the PPU
|
|
|
|
// do any processing for the user defined stop here
|
|
// if we do not restart the SPU then we need to call g_snRawSPUNotifySPUStopped(id)
|
|
// to inform the debugger that SPU has stopped
|
|
|
|
//restart the SPU
|
|
sys_raw_spu_mmio_write( id, SPU_RunCntl, 0x1 );
|
|
break;
|
|
|
|
default:
|
|
#ifndef _CERT
|
|
g_snRawSPUNotifySPUStopped(id);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
else if (stat & INTR_HALT_MASK) // halt
|
|
{
|
|
#ifndef _CERT
|
|
g_snRawSPUNotifySPUStopped(id);
|
|
#endif
|
|
}
|
|
|
|
// Other class 2 interrupts could be handled here
|
|
// ...
|
|
|
|
//
|
|
// Must reset interrupt status bit of those not handled.
|
|
//
|
|
ret = sys_raw_spu_set_int_stat(id, 2, stat);
|
|
if (ret)
|
|
{
|
|
#ifndef _CERT
|
|
g_snRawSPUUnlockHandler();
|
|
#endif
|
|
sys_interrupt_thread_eoi();
|
|
}
|
|
|
|
//
|
|
// End of interrupt
|
|
//
|
|
#ifndef _CERT
|
|
g_snRawSPUUnlockHandler();
|
|
#endif
|
|
sys_interrupt_thread_eoi();
|
|
}
|
|
|
|
int CreateDefaultInterruptHandler(SpuTaskHandle *pTask)
|
|
{
|
|
int res = 0;
|
|
|
|
//
|
|
// Create a SPU interrupt handler thread, an interrupt tag,
|
|
// and associate it with the thread
|
|
//
|
|
|
|
// create thread
|
|
|
|
if (sys_ppu_thread_create(&pTask->m_ppuThread, handle_syscall,
|
|
0, INTR_HANDLER_THREAD_PRIORITY, INTR_HANDLER_THREAD_STACK_SIZE,
|
|
SYS_PPU_THREAD_CREATE_INTERRUPT, "Interrupt PPU Thread"))
|
|
{
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
|
|
// create interrupt tag for handling class 2 interrupts from this spu
|
|
|
|
if (sys_raw_spu_create_interrupt_tag(pTask->m_spuId, 2, SYS_HW_THREAD_ANY, &pTask->m_intrTag))
|
|
{
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
|
|
// associate interrupt tag with thread
|
|
|
|
if (sys_interrupt_thread_establish(&pTask->m_interruptThread, pTask->m_intrTag,
|
|
pTask->m_ppuThread, pTask->m_spuId))
|
|
{
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
|
|
// Set interrupt mask - enable Halt, Stop-and-Signal interrupts
|
|
if (sys_raw_spu_set_int_mask(pTask->m_spuId, 2, INTR_STOP_MASK | INTR_HALT_MASK))
|
|
{
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
|
|
xit:
|
|
return res;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Class Methods
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
int SpuMgr::Init(int numRawSpu)
|
|
{
|
|
// Need at least 2 SPUs for SPURS instances
|
|
ASSERT(numRawSpu < 5);
|
|
|
|
|
|
// Run SPURS on all SPUs that are not in raw mode
|
|
|
|
// Creating two SPURS instances. One with a thread group of 5 - numRawSpu threads and one
|
|
// with a thread group of 1 thread.
|
|
|
|
// The instance with a single thread is designed to be singled out as the preemption victim
|
|
// when the OS needs to use an SPU. We ensure this by giving it a lower priority than the
|
|
// dedicated SPURS instance.
|
|
|
|
|
|
// Init dedicated SPUs SPURS instance
|
|
// CellSpursAttribute attr;
|
|
// int32 ret = cellSpursAttributeInitialize(&attr, 5 - numRawSpu, 99, 2, false);
|
|
// ASSERT(ret == CELL_OK);
|
|
// ret = cellSpursAttributeEnableSpuPrintfIfAvailable(&attr);
|
|
// ASSERT(ret == CELL_OK);
|
|
// ret = cellSpursAttributeSetNamePrefix(&attr, "gameSpusSpurs", std::strlen("gameSpusSpurs"));
|
|
// ASSERT(ret == CELL_OK);
|
|
// ret = cellSpursInitializeWithAttribute2(&m_exclusiveSpusSpurs, &attr);
|
|
// ASSERT(ret == CELL_OK);
|
|
|
|
// Init pre-emption SPU SPURS instance
|
|
// ret = cellSpursAttributeInitialize(&attr, 1, 100, 2, false);
|
|
// ASSERT(ret == CELL_OK);
|
|
// ret = cellSpursAttributeEnableSpuPrintfIfAvailable(&attr);
|
|
// ASSERT(ret == CELL_OK);
|
|
// ret = cellSpursAttributeSetNamePrefix(&attr, "sharedSpuSpurs", std::strlen("sharedSpuSpurs"));
|
|
// ASSERT(ret == CELL_OK);
|
|
// ret = cellSpursInitializeWithAttribute2(&m_preemptedSpuSpurs, &attr);
|
|
// ASSERT(ret == CELL_OK);
|
|
|
|
|
|
int res = 0;
|
|
|
|
// set up members
|
|
m_numSpus = 0;
|
|
|
|
// Initialize SPUs
|
|
if (sys_spu_initialize(6, numRawSpu) != SUCCEEDED)
|
|
{
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
|
|
// Create raw spus
|
|
for (; m_numSpus < (uint32)numRawSpu; m_numSpus++)
|
|
{
|
|
if (sys_raw_spu_create(&m_spuIds[m_numSpus], NULL) != SUCCEEDED)
|
|
{
|
|
Error("Unable to create saw spu\n");
|
|
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
|
|
#ifndef _CERT
|
|
g_snRawSPUNotifyCreation(m_spuIds[m_numSpus]);
|
|
#endif
|
|
m_spuInUse[m_numSpus] = 0;
|
|
}
|
|
|
|
xit:
|
|
return res;
|
|
}
|
|
|
|
void SpuMgr::Term()
|
|
{
|
|
uint32 spu;
|
|
|
|
// destroy raw spus
|
|
for (spu = 0; spu < m_numSpus; spu++)
|
|
{
|
|
sys_raw_spu_destroy(m_spuIds[spu]);
|
|
}
|
|
|
|
// destroy the SPURS instances
|
|
// int ret;
|
|
// ret = cellSpursfinalize(&m_exclusiveSpusSpurs);
|
|
// ASSERT(ret == CELL_OK);
|
|
//
|
|
// ret = cellSpursfinalize(&m_preemptedSpuSpurs);
|
|
// ASSERT(ret == CELL_OK);
|
|
|
|
m_numSpus = 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
uint32_t spumgr_mmio_read(uint32_t spu, uint32_t regoffset)
|
|
{
|
|
uint64_t addr = get_reg_addr(spu,regoffset);
|
|
addr &= 0xffffffffUL;
|
|
volatile uint32_t * pAddr = (uint32_t*) addr;
|
|
return *pAddr;
|
|
}
|
|
|
|
void spumgr_mmio_write(int spu, int regoffset, uint32_t value)
|
|
{
|
|
uint64_t addr = get_reg_addr(spu,regoffset);
|
|
addr &= 0xffffffffUL;
|
|
volatile uint32_t * pAddr = (uint32_t*) addr;
|
|
*pAddr = value;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
// Create Spu task from file based image
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
|
|
static char modPath[MAX_PATH];
|
|
|
|
int SpuMgr::CreateSpuTask(const char *path, SpuTaskHandle *pTask,
|
|
CreateSPUTaskCallback *pfnCallback /* = NULL */)
|
|
{
|
|
int res = 0;
|
|
int ret;
|
|
uint32 spu;
|
|
register uint32 spuid;
|
|
uint32 entry;
|
|
FILE* fp;
|
|
void* pSpuProg = NULL;
|
|
|
|
sys_spu_image_t img;
|
|
|
|
pTask->m_spuId = -1;
|
|
pTask->m_ppuThread = NULL;
|
|
pTask->m_intrTag = NULL;
|
|
pTask->m_interruptThread = NULL;
|
|
|
|
// find free raw spu
|
|
for (spu = 0; spu < m_numSpus; spu++)
|
|
{
|
|
if (!m_spuInUse[spu])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check we found free spu
|
|
if (spu == m_numSpus)
|
|
{
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
|
|
// Loading an SPU program to the Raw SPU.
|
|
//if (sys_raw_spu_load(m_spuIds[spu], path, &entry) != SUCCEEDED)
|
|
|
|
sprintf(modPath, "%s/%s", g_pPS3PathInfo->PrxPath(), path);
|
|
path = modPath;
|
|
|
|
if(strstr(path,".self"))
|
|
{
|
|
ret = sys_spu_image_open(&img, path);
|
|
if(ret != CELL_OK)
|
|
{
|
|
// (Running on Main Thread)
|
|
Error("Failed to open SPU program: %s\n", path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Allocate mem for SPU prog
|
|
|
|
CellFsStat stat;
|
|
cellFsStat(path,&stat);
|
|
pSpuProg = memalign(4096,((uint32)stat.st_size + 0x7f)&0xffffff80);
|
|
fp = fopen(path, "rb");
|
|
fread(pSpuProg, 1, stat.st_size, fp );
|
|
fclose(fp);
|
|
|
|
ret = sys_spu_image_import(&img, pSpuProg, SYS_SPU_IMAGE_PROTECT);
|
|
if (ret != CELL_OK)
|
|
{
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
}
|
|
|
|
ret = sys_raw_spu_image_load(m_spuIds[spu], &img);
|
|
|
|
spuid = m_spuIds[spu];
|
|
|
|
if (ret == CELL_OK)
|
|
{
|
|
// successfully loaded - mark spu as used and fill in o/p
|
|
m_spuInUse[spu] = 1;
|
|
pTask->m_spuId = spuid;
|
|
}
|
|
else
|
|
{
|
|
res = 1;
|
|
goto xit;
|
|
}
|
|
|
|
//Free PPU resources used to load image
|
|
if(pSpuProg)
|
|
{
|
|
free(pSpuProg);
|
|
}
|
|
sys_spu_image_close(&img);
|
|
|
|
entry = sys_raw_spu_mmio_read((uint32_t)spuid, (uint32_t)SPU_NPC);
|
|
|
|
#ifndef _CERT
|
|
g_snRawSPUNotifyElfLoad(spuid, entry, path);
|
|
#endif
|
|
|
|
// call callback or create default interrupt handler
|
|
if (!pfnCallback)
|
|
{
|
|
res = CreateDefaultInterruptHandler(pTask);
|
|
}
|
|
else
|
|
{
|
|
res = pfnCallback(pTask);
|
|
}
|
|
|
|
if (res)
|
|
{
|
|
goto xit;
|
|
}
|
|
|
|
// Run the Raw SPU
|
|
|
|
#ifndef _CERT
|
|
g_snRawSPUNotifySPUStarted(m_spuIds[spu]);
|
|
#endif
|
|
sys_raw_spu_mmio_write(spuid, SPU_NPC, entry);
|
|
sys_raw_spu_mmio_write(spuid, SPU_RunCntl, 0x1);
|
|
__asm("eieio");
|
|
|
|
// Once the SPU has started, write a mailbox with the effective address of the
|
|
// SPU lock.
|
|
WriteMailbox( pTask, (uint32) &pTask->m_lock );
|
|
WriteMailbox( pTask, (uint32) &pTask->m_memcpyLock );
|
|
|
|
xit:
|
|
if(res)
|
|
{
|
|
// Error("Error: CreateSpuTask error attempting to load and run %s on SPU\n", path);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
void SpuMgr::DestroySpuTask(SpuTaskHandle *pTask)
|
|
{
|
|
if (pTask->m_spuId != -1)
|
|
{
|
|
// Stop the Raw spu
|
|
|
|
#ifndef _CERT
|
|
g_snRawSPUNotifySPUStopped(pTask->m_spuId);
|
|
#endif
|
|
|
|
sys_raw_spu_mmio_write(pTask->m_spuId, SPU_RunCntl, 0x0);
|
|
__asm("eieio");
|
|
|
|
// Cleanup interrupt handling mechanism
|
|
|
|
if (pTask->m_interruptThread)
|
|
{
|
|
sys_interrupt_thread_disestablish(pTask->m_interruptThread); // also kills the thread
|
|
}
|
|
|
|
if (pTask->m_intrTag)
|
|
{
|
|
sys_interrupt_tag_destroy(pTask->m_intrTag);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
int SpuMgr::WriteMailbox(SpuTaskHandle *pTask, uint32 val, bool bBlocking /* =true */)
|
|
{
|
|
uint32 mboxAvailable;
|
|
|
|
do
|
|
{
|
|
// Check the SPU Mailbox Status Register
|
|
mboxAvailable = sys_raw_spu_mmio_read(pTask->m_spuId, SPU_MBox_Status) & SPU_IN_MBOX_COUNT;
|
|
} while (bBlocking && !mboxAvailable);
|
|
|
|
if (mboxAvailable)
|
|
sys_raw_spu_mmio_write(pTask->m_spuId, SPU_In_MBox, (std::uint32_t)val);
|
|
|
|
return !mboxAvailable;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
int SpuMgr::ReadMailbox(SpuTaskHandle *pTask, uint32 *pVal, bool bBlocking /* = true */)
|
|
{
|
|
uint32 mailAvailable;
|
|
|
|
do
|
|
{
|
|
// Check the SPU Mailbox Status Register
|
|
mailAvailable = sys_raw_spu_mmio_read(pTask->m_spuId, SPU_MBox_Status) & SPU_OUT_MBOX_COUNT;
|
|
} while (bBlocking && !mailAvailable);
|
|
|
|
if (mailAvailable)
|
|
{
|
|
// Read the SPU Outbound Mailbox Register
|
|
*pVal = sys_raw_spu_mmio_read(pTask->m_spuId, SPU_Out_MBox);
|
|
}
|
|
|
|
return !mailAvailable;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
int SpuMgr::ReadIntrMailbox(SpuTaskHandle *pTask, uint32 *pVal, bool bBlocking /* = true */)
|
|
{
|
|
uint32 mailAvailable;
|
|
|
|
do
|
|
{
|
|
// Check the SPU Mailbox Status Register
|
|
mailAvailable = sys_raw_spu_mmio_read(pTask->m_spuId, SPU_MBox_Status) & SPU_OUT_INTR_MBOX_COUNT;
|
|
|
|
} while (bBlocking && !mailAvailable);
|
|
|
|
if (mailAvailable)
|
|
{
|
|
// Read the SPU Outbound Mailbox Register
|
|
sys_raw_spu_read_puint_mb(pTask->m_spuId, pVal);
|
|
}
|
|
|
|
return !mailAvailable;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
bool SpuMgr::Lock( SpuTaskHandle *pTask )
|
|
{
|
|
return cellAtomicCompareAndSwap32( &pTask->m_lock, 0, 1 ) == 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
void SpuMgr::Unlock( SpuTaskHandle *pTask )
|
|
{
|
|
cellAtomicCompareAndSwap32( &pTask->m_lock, 1, 0 );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
bool SpuMgr::MemcpyLock( SpuTaskHandle *pTask )
|
|
{
|
|
return cellAtomicCompareAndSwap32( &pTask->m_memcpyLock, 0, 1 ) == 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
void SpuMgr::MemcpyUnlock( SpuTaskHandle *pTask )
|
|
{
|
|
cellAtomicCompareAndSwap32( &pTask->m_memcpyLock, 1, 0 );
|
|
}
|
|
|