/** * Set a repeated alarm to execute a callback every interval from start. */ BOOL OSSetPeriodicAlarm(OSAlarm *alarm, OSTime start, OSTime interval, AlarmCallback callback) { internal::acquireIdLock(sAlarmLock, alarm); // Set alarm alarm->nextFire = start; alarm->callback = callback; alarm->period = interval; alarm->context = nullptr; alarm->state = OSAlarmState::Set; // Erase from old alarm queue if (alarm->alarmQueue) { internal::AlarmQueue::erase(alarm->alarmQueue, alarm); alarm->alarmQueue = nullptr; } // Add to this core's alarm queue auto core = OSGetCoreId(); auto queue = sAlarmQueue[core]; alarm->alarmQueue = queue; internal::AlarmQueue::append(queue, alarm); // Set the interrupt timer in processor // TODO: Store the last set CPU alarm time, and simply check this // alarm against that time to make finding the soonest alarm cheaper. internal::updateCpuAlarmNoALock(); internal::releaseIdLock(sAlarmLock, alarm); return TRUE; }
/** * Set a repeated alarm to execute a callback every interval from start. */ BOOL OSSetPeriodicAlarm(OSAlarm *alarm, OSTime start, OSTime interval, AlarmCallback callback) { ScopedSpinLock lock(gAlarmLock); // Set alarm alarm->nextFire = start; alarm->callback = callback; alarm->period = interval; alarm->context = nullptr; alarm->state = OSAlarmState::Set; // Erase from old alarm queue if (alarm->alarmQueue) { OSEraseFromQueue(static_cast<OSAlarmQueue*>(alarm->alarmQueue), alarm); alarm->alarmQueue = nullptr; } // Add to this core's alarm queue auto core = OSGetCoreId(); auto queue = gAlarmQueue[core]; alarm->alarmQueue = queue; OSAppendQueue(queue, alarm); // Set the interrupt timer in processor gProcessor.setInterruptTimer(core, coreinit::internal::toTimepoint(alarm->nextFire)); return TRUE; }
BOOL OSWaitRendezvousWithTimeout(OSRendezvous *rendezvous, uint32_t coreMask, OSTime timeout) { auto core = OSGetCoreId(); auto success = FALSE; auto endTime = OSGetTime() + timeout; // Set our core flag rendezvous->core[core].store(1, std::memory_order_release); do { success = TRUE; // Check all core flags for (auto i = 0u; i < 3; ++i) { if (coreMask & (1 << i)) { if (!rendezvous->core[i].load(std::memory_order_acquire)) { success = FALSE; } } } // Check for timeout if (timeout != -1 && OSGetTime() >= endTime) { break; } } while (!success); return success; }
int main(int argc, char **argv) { OSReport("Main thread running on core %d", OSGetCoreId()); // Run thread on core 0 OSThread *threadCore0 = OSGetDefaultThread(0); const char *core0Args[] = { "Core 0" }; OSRunThread(threadCore0, CoreEntryPoint, 0, core0Args); // Run thread on core 2 OSThread *threadCore2 = OSGetDefaultThread(2); const char *core2Args[] = { "Core 2" }; OSRunThread(threadCore2, CoreEntryPoint, 2, core2Args); // Wait for threads to return int resultCore0 = -1, resultCore2 = -1; OSJoinThread(threadCore0, &resultCore0); OSJoinThread(threadCore2, &resultCore2); OSReport("Core 0 thread returned %d", resultCore0); OSReport("Core 2 thread returned %d", resultCore2); return 0; }
void GX2Init(be_val<uint32_t> *attributes) { virtual_ptr<uint32_t> cbPoolBase = nullptr; uint32_t cbPoolSize = 0x400000; uint32_t cbPoolItemSize = 0x100; virtual_ptr<char *> argv = nullptr; uint32_t argc = 0; // Set main gx2 core gMainCoreId = OSGetCoreId(); // Parse attributes while (attributes && *attributes != GX2InitAttrib::End) { uint32_t id = *(attributes++); uint32_t value = *(attributes++); switch (id) { case GX2InitAttrib::CommandBufferPoolBase: cbPoolBase = make_virtual_ptr<uint32_t>(value); break; case GX2InitAttrib::CommandBufferPoolSize: cbPoolSize = value; break; case GX2InitAttrib::ArgC: argc = value; break; case GX2InitAttrib::ArgV: argv = make_virtual_ptr<char *>(value); break; } } // Ensure minimum size if (cbPoolSize < 0x2000) { cbPoolSize = 0x2000; } // Allocate command buffer pool if (!cbPoolBase) { cbPoolBase = reinterpret_cast<uint32_t*>((*pMEMAllocFromDefaultHeapEx)(cbPoolSize, 0x100)); } // Init event handler stuff (vsync, flips, etc) gx2::internal::initEvents(); // Initialise command buffer pools gx2::internal::initCommandBufferPool(cbPoolBase, cbPoolSize, cbPoolItemSize); // Start our driver! gpu::driver::start(); // Setup default gx2 state GX2SetDefaultState(); }
/** * Run a specific task. * * The task must belong to a queue. * The task must be in the Ready state. * * \return Returns TRUE if task was run. */ BOOL MPRunTask(virt_ptr<MPTask> task) { auto queue = task->queue; if (task->state != MPTaskState::Ready) { return FALSE; } if (!queue || queue->state == MPTaskQueueState::Stopping || queue->state == MPTaskQueueState::Stopped) { return FALSE; } OSUninterruptibleSpinLock_Acquire(virt_addrof(queue->lock)); queue->tasksReady--; queue->tasksRunning++; OSUninterruptibleSpinLock_Release(virt_addrof(queue->lock)); task->state = MPTaskState::Running; task->coreID = OSGetCoreId(); auto start = OSGetTime(); task->result = cafe::invoke(cpu::this_core::state(), task->func, task->userArg1, task->userArg2); task->duration = OSGetTime() - start; task->state = MPTaskState::Finished; OSUninterruptibleSpinLock_Acquire(virt_addrof(queue->lock)); queue->tasksRunning--; queue->tasksFinished++; if (queue->state == MPTaskQueueState::Stopping && queue->tasksRunning == 0) { queue->state = MPTaskQueueState::Stopped; } if (queue->tasks == queue->tasksFinished) { queue->state = MPTaskQueueState::Finished; } OSUninterruptibleSpinLock_Release(virt_addrof(queue->lock)); return TRUE; }
/** * Wait on a rendezvous with a timeout. * * This will wait with a timeout until all cores matching coreMask have * reached the rendezvous point. * * \return Returns TRUE on success, FALSE on timeout. */ BOOL OSWaitRendezvousWithTimeout(OSRendezvous *rendezvous, uint32_t coreMask, OSTime timeoutNS) { auto core = OSGetCoreId(); auto success = FALSE; auto endTime = OSGetTime() + internal::nanosToTicks(timeoutNS); auto waitCore0 = (coreMask & (1 << 0)) != 0; auto waitCore1 = (coreMask & (1 << 1)) != 0; auto waitCore2 = (coreMask & (1 << 2)) != 0; // Set our core flag rendezvous->core[core].store(1, std::memory_order_release); do { if (waitCore0 && rendezvous->core[0].load(std::memory_order_acquire)) { waitCore0 = false; } if (waitCore1 && rendezvous->core[1].load(std::memory_order_acquire)) { waitCore1 = false; } if (waitCore2 && rendezvous->core[2].load(std::memory_order_acquire)) { waitCore2 = false; } if (!waitCore0 && !waitCore1 && !waitCore2) { success = TRUE; break; } if (timeoutNS != -1 && OSGetTime() >= endTime) { break; } // We must manually check for interrupts here, as we are busy-looping. // Note that this is only safe as no locks are held during the wait. cpu::this_core::checkInterrupts(); } while (true); return success; }
void WHBProcInit() { uint64_t titleID = OSGetTitleID(); // Homebrew Launcher does not like the standard ProcUI application loop, // so instead we disable the home buttom menu and use the home button // to trigger an exit. if (titleID == HBL_TITLE_ID || titleID == MII_MAKER_JPN_TITLE_ID || titleID == MII_MAKER_USA_TITLE_ID || titleID == MII_MAKER_EUR_TITLE_ID) { // Important: OSEnableHomeButtonMenu must come before ProcUIInitEx. OSEnableHomeButtonMenu(FALSE); sFromHBL = TRUE; } sMainCore = OSGetCoreId(); sRunning = TRUE; ProcUIInitEx(&procSaveCallback, NULL); ProcUIRegisterCallback(PROCUI_CALLBACK_HOME_BUTTON_DENIED, &procHomeButtonDenied, NULL, 100); }
BOOL WHBProcIsRunning() { ProcUIStatus status; if (sMainCore != OSGetCoreId()) { ProcUISubProcessMessages(TRUE); return sRunning; } status = ProcUIProcessMessages(TRUE); if (status == PROCUI_STATUS_EXITING) { WHBProcStopRunning(); } else if (status == PROCUI_STATUS_RELEASE_FOREGROUND) { ProcUIDrawDoneRelease(); } if (!sRunning) { ProcUIShutdown(); } return sRunning; }
OSMessageQueue * OSGetDefaultAppIOQueue() { return &sAppIo->queues[OSGetCoreId()]; }
/** * Run N tasks from queue. * * Does not remove tasks from queue. * Can be run from multiple threads at once. * * Side Effects: * - Sets state to Stopped if state is Stopping and tasksRunning reaches 0. * - Sets state to Finished if all tasks are finished. * - TasksReady -> TasksRunning -> TasksFinished. * * Returns TRUE if at least 1 task is run. */ BOOL MPRunTasksFromTaskQ(MPTaskQueue *queue, uint32_t tasks) { BOOL result = FALSE; while (queue->state == MPTaskQueueState::Ready) { uint32_t first, count, available; OSUninterruptibleSpinLock_Acquire(&queue->lock); available = queue->queueSize - queue->queueIndex; count = std::min(available, tasks); first = queue->queueIndex; tasks -= count; queue->tasksReady -= count; queue->tasksRunning += count; queue->queueIndex += count; OSUninterruptibleSpinLock_Release(&queue->lock); if (count == 0) { // Nothing to run, lets go home! break; } // Result is TRUE if at least 1 task is run result = TRUE; // Mark all tasks as running for (auto i = 0u; i < count; ++i) { auto task = queue->queue[first + i]; task->state = MPTaskState::Running; task->coreID = OSGetCoreId(); } // Run all tasks for (auto i = 0u; i < count; ++i) { auto task = queue->queue[first + i]; auto start = OSGetTime(); task->result = task->func(task->userArg1, task->userArg2); task->state = MPTaskState::Finished; task->duration = OSGetTime() - start; } OSUninterruptibleSpinLock_Acquire(&queue->lock); queue->tasksRunning -= count; queue->tasksFinished += count; if (queue->state == MPTaskQueueState::Stopping && queue->tasksRunning == 0) { queue->state = MPTaskQueueState::Stopped; } if (queue->tasks == queue->tasksFinished) { queue->state = MPTaskQueueState::Finished; } OSUninterruptibleSpinLock_Release(&queue->lock); } return result; }
void _start() { asm( "lis %r1, 0x1ab5 ;" "ori %r1, %r1, 0xd138 ;" ); unsigned int coreinit_handle, gx2_handle; OSDynLoad_Acquire("coreinit", &coreinit_handle); OSDynLoad_Acquire("gx2", &gx2_handle); //OS Memory functions void*(*memset)(void * dest, unsigned int value, unsigned int bytes); void*(*OSAllocFromSystem)(unsigned int size, int align); void(*OSFreeToSystem)(void *ptr); //IM functions int(*IM_Open)(); int(*IM_Close)(int fd); int(*IM_SetDeviceState)(int fd, void *mem, int state, int a, int b); //OS Memory functions OSDynLoad_FindExport(coreinit_handle, 0, "memset", &memset); OSDynLoad_FindExport(coreinit_handle, 0, "OSAllocFromSystem", &OSAllocFromSystem); OSDynLoad_FindExport(coreinit_handle, 0, "OSFreeToSystem", &OSFreeToSystem); //IM functions OSDynLoad_FindExport(coreinit_handle, 0, "IM_Open", &IM_Open); OSDynLoad_FindExport(coreinit_handle, 0, "IM_Close", &IM_Close); OSDynLoad_FindExport(coreinit_handle, 0, "IM_SetDeviceState", &IM_SetDeviceState); //Restart system to get lib access int fd = IM_Open(); void *mem = OSAllocFromSystem(0x100, 64); memset(mem, 0, 0x100); //set restart flag to force quit browser IM_SetDeviceState(fd, mem, 3, 0, 0); IM_Close(fd); OSFreeToSystem(mem); //wait a bit for browser end unsigned int t1 = 0x1FFFFFFF; while(t1--) ; /* Get the framebuffer of the TV or DRC */ void(*GX2SwapScanBuffers)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2SwapScanBuffers", &GX2SwapScanBuffers); unsigned char *abuseFunc = (unsigned char*)GX2SwapScanBuffers; unsigned short f_hi = *(unsigned short*)(abuseFunc+0x12); unsigned short f_lo = *(unsigned short*)(abuseFunc+0x16); unsigned int gx2settingBase = (((f_lo & 0x8000) ? (f_hi-1) : f_hi) << 16) | f_lo; unsigned int args[2]; args[0] = *((unsigned int*)(gx2settingBase + 0x304)); args[1] = *((unsigned int*)(gx2settingBase + 0x304 + 0x20)); int(*OSGetCoreId)(); OSDynLoad_FindExport(coreinit_handle, 0, "OSGetCoreId", &OSGetCoreId); void(*OSTestThreadCancel)(); OSDynLoad_FindExport(coreinit_handle, 0, "OSTestThreadCancel", &OSTestThreadCancel); void(*GX2Shutdown)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2Shutdown", &GX2Shutdown); int(*GX2GetMainCoreId)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2GetMainCoreId", &GX2GetMainCoreId); /* Prepare for our own death */ void*(*OSGetCurrentThread)(); OSDynLoad_FindExport(coreinit_handle, 0, "OSGetCurrentThread", &OSGetCurrentThread); void *myBorkedThread = OSGetCurrentThread(); int (*OSSuspendThread)(void *thread); OSDynLoad_FindExport(coreinit_handle, 0, "OSSuspendThread", &OSSuspendThread); /* Prepare for thread startups */ int (*OSCreateThread)(void *thread, void *entry, int argc, void *args, unsigned int stack, unsigned int stack_size, int priority, unsigned short attr); int (*OSResumeThread)(void *thread); int (*OSIsThreadTerminated)(void *thread); OSDynLoad_FindExport(coreinit_handle, 0, "OSCreateThread", &OSCreateThread); OSDynLoad_FindExport(coreinit_handle, 0, "OSResumeThread", &OSResumeThread); OSDynLoad_FindExport(coreinit_handle, 0, "OSIsThreadTerminated", &OSIsThreadTerminated); /* Allocate a stack for the thread */ unsigned int stack = (unsigned int) OSAllocFromSystem(0x1000, 0x10); stack += 0x1000; /* Create the thread */ void *thread = OSAllocFromSystem(OSTHREAD_SIZE, 8); if(OSGetCoreId() != GX2GetMainCoreId()) //needed for access without crashing { int ret = OSCreateThread(thread, GX2Shutdown, 0, (void*)0, stack, 0x1000, 0, 0x10 | (1<<GX2GetMainCoreId())); if (ret == 0) OSFatal("Failed to create thread"); /* Schedule it for execution */ OSResumeThread(thread); while(OSIsThreadTerminated(thread) == 0) ; } else //same core, easy GX2Shutdown(); //current thread is broken, switch to a working one in core 1 int ret = OSCreateThread(thread, myGXthread, 2, args, stack, 0x1000, 0, 0xA); if (ret == 0) OSFatal("Failed to create thread"); /* Schedule it for execution */ OSResumeThread(thread); /* SO UGLY but it works magically */ while(1) ; //would be better but again, crashes 5.3.2 //OSSuspendThread(myBorkedThread); //OSFatal("I am still not dead!"); }
void myGXthread(int argc, int *argv) { if(argc != 2) OSFatal("GX Thread did not start proper!"); unsigned int *tv_fb = (unsigned int*)argv[0]; unsigned int *drc_fb = (unsigned int*)argv[1]; unsigned int coreinit_handle, gx2_handle; OSDynLoad_Acquire("coreinit", &coreinit_handle); OSDynLoad_Acquire("gx2", &gx2_handle); void(*GX2Init)(void *args); OSDynLoad_FindExport(gx2_handle, 0, "GX2Init", &GX2Init); GX2Init((void*)0); int(*OSGetCoreId)(); OSDynLoad_FindExport(coreinit_handle, 0, "OSGetCoreId", &OSGetCoreId); void(*GX2SwapScanBuffers)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2SwapScanBuffers", &GX2SwapScanBuffers); void(*GX2Shutdown)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2Shutdown", &GX2Shutdown); int(*GX2GetMainCoreId)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2GetMainCoreId", &GX2GetMainCoreId); if(OSGetCoreId() != GX2GetMainCoreId()) OSFatal("GX Not switched!"); void(*GX2DrawDone)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2DrawDone", &GX2DrawDone); void(*GX2Flush)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2Flush", &GX2Flush); //void(*GX2SwapScanBuffers)(); //OSDynLoad_FindExport(gx2_handle, 0, "GX2SwapScanBuffers", &GX2SwapScanBuffers); void(*GX2WaitForVsync)(); OSDynLoad_FindExport(gx2_handle, 0, "GX2WaitForVsync", &GX2WaitForVsync); //Make sure buffer is usable for GX void(*GX2Invalidate)(unsigned int flags,void *buffer,unsigned int size); OSDynLoad_FindExport(gx2_handle, 0, "GX2Invalidate", &GX2Invalidate); GX2Invalidate(0x40,tv_fb,(1280*720*4)*2); GX2Invalidate(0x40,drc_fb,(854*480*4)*2); //set it! int(*GX2SetTVBuffer)(void *buffer,unsigned int size,unsigned int flag1,unsigned int flag2,unsigned int flag3); OSDynLoad_FindExport(gx2_handle, 0, "GX2SetTVBuffer", &GX2SetTVBuffer); int(*GX2SetDRCBuffer)(void *buffer,unsigned int size,unsigned int flag1,unsigned int flag2,unsigned int flag3); OSDynLoad_FindExport(gx2_handle, 0, "GX2SetDRCBuffer", &GX2SetDRCBuffer); GX2SetTVBuffer(tv_fb,(1280*720*4)*2,3,0x1A,2); //test gradient demo setup for show GX2SetDRCBuffer(drc_fb,(854*480*4)*2,1,0x1A,2); //will look broken, no color buffer setup yet /* Draw is far from complete */ /*void(*GX2DrawEx)(int type, int count, int start, int instances); OSDynLoad_FindExport(gx2_handle, 0, "GX2DrawEx", &GX2DrawEx); void(*GX2SetAttribBuffer)(int index, int size, int vtxStride, void *buf); OSDynLoad_FindExport(gx2_handle, 0, "GX2SetAttribBuffer", &GX2SetAttribBuffer); float myclearer[8] = { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f }; GX2Invalidate(0x40, myclearer, sizeof(myclearer));*/ GX2ColorBuffer myCBuf; setupColorBuffer(&myCBuf, gx2_handle); void(*GX2ClearColor)(GX2ColorBuffer *buffer, float r, float g, float b, float a); OSDynLoad_FindExport(gx2_handle, 0, "GX2ClearColor", &GX2ClearColor); void(*GX2SwapBuffers)(GX2ColorBuffer *buffer); OSDynLoad_FindExport(gx2_handle, 0, "GX2SwapBuffers", &GX2SwapBuffers); void(*GX2CopyColorBufferToScanBuffer)(GX2ColorBuffer *buffer, unsigned int target); OSDynLoad_FindExport(gx2_handle, 0, "GX2CopyColorBufferToScanBuffer", &GX2CopyColorBufferToScanBuffer); float val = 1; float valInc = -0.005; val += valInc; while(val < 1) { val += valInc; //GX2SetAttribBuffer(0,sizeof(mybuf),8,mybuf); //GX2DrawEx(6,4,0,1); GX2ClearColor(&myCBuf, val, val, val, 1); GX2Invalidate(0x40, myCBuf.surface.imagePtr,myCBuf.surface.imageSize); GX2DrawDone(); GX2Flush(); GX2CopyColorBufferToScanBuffer(&myCBuf,1); GX2SwapScanBuffers(); GX2Flush(); GX2WaitForVsync(); if(val <= 0) valInc = 0.005; } //clear for browser (needed?) GX2Shutdown(); void(*_Exit)(); OSDynLoad_FindExport(coreinit_handle, 0, "_Exit", &_Exit); _Exit(); }
void LCDealloc(void * addr) { uint32_t coreId = OSGetCoreId(); gLcHeaps[coreId]->free(addr); }
void * LCAlloc(uint32_t size) { uint32_t coreId = OSGetCoreId(); return gLcHeaps[coreId]->alloc(size, 512); }