|
|
//================ 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 ); }
|