Counter Strike : Global Offensive Source Code
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

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