Exemple #1
0
error_code sys_ppu_thread_start(ppu_thread& ppu, u32 thread_id)
{
	sys_ppu_thread.trace("sys_ppu_thread_start(thread_id=0x%x)", thread_id);

	const auto thread = idm::get<named_thread<ppu_thread>>(thread_id, [&](ppu_thread& thread)
	{
		lv2_obj::awake(thread, -2);
	});

	if (!thread)
	{
		return CELL_ESRCH;
	}

	if (!thread->state.test_and_reset(cpu_flag::stop))
	{
		// TODO: what happens there?
		return CELL_EPERM;
	}
	else
	{
		thread_ctrl::notify(*thread);

		// Dirty hack for sound: confirm the creation of _mxr000 event queue
		if (thread->ppu_name.get() == "_cellsurMixerMain"sv)
		{
			lv2_obj::sleep(ppu);

			while (!idm::select<lv2_obj, lv2_event_queue>([](u32, lv2_event_queue& eq)
			{
				//some games do not set event queue name, though key seems constant for them
				return (eq.name == "_mxr000\0"_u64) || (eq.key == 0x8000cafe02460300);
			}))
			{
				if (ppu.is_stopped())
				{
					return 0;
				}

				thread_ctrl::wait_for(50000);
			}

			if (ppu.test_stopped())
			{
				return 0;
			}
		}
	}

	return CELL_OK;
}
Exemple #2
0
void sys_interrupt_thread_eoi(ppu_thread& ppu) // Low-level PPU function example
{
	// Low-level function body must guard all C++-ish calls and all objects with non-trivial destructors
	thread_guard{ppu}, sys_interrupt.trace("sys_interrupt_thread_eoi()");

	ppu.state += cpu_flag::ret;

	// Throw if this syscall was not called directly by the SC instruction (hack)
	if (ppu.lr == 0 || ppu.gpr[11] != 88)
	{
		// Low-level function must disable interrupts before throwing (not related to sys_interrupt_*, it's rather coincidence)
		ppu->interrupt_disable();
		throw cpu_flag::ret;
	}
}
Exemple #3
0
error_code cellAudioQuit(ppu_thread& ppu)
{
	cellAudio.warning("cellAudioQuit()");

	// Stop audio thread
	auto g_audio = g_idm->lock<named_thread<audio_thread>>(0);

	if (!g_audio)
	{
		return CELL_AUDIO_ERROR_NOT_INIT;
	}

	// Signal to abort, release lock
	*g_audio.get() = thread_state::aborting;
	g_audio.unlock();

	while (true)
	{
		if (ppu.is_stopped())
		{
			return 0;
		}

		thread_ctrl::wait_for(1000);

		auto g_audio = g_idm->lock<named_thread<audio_thread>>(0);

		if (*g_audio.get() == thread_state::finished)
		{
			const auto buf = g_audio->ports[0].addr;
			const auto ind = g_audio->ports[0].index;
			g_audio.destroy();
			g_audio.unlock();
			vm::dealloc(buf.addr());
			vm::dealloc(ind.addr());
			break;
		}
	}

	return CELL_OK;
}
Exemple #4
0
// Note: the way we do things here is inaccurate(but maybe sufficient)
// The real function goes over a table of 0x20 entries[ event_code:u32 callback_addr:u32 ]
// Those callbacks are registered through cellSysutilRegisterCallbackDispatcher(u32 event_code, vm::ptr<void> func_addr)
// The function goes through all the callback looking for one callback associated with event 0x100, if any is found it is called with parameters r3=0x101 r4=0
// This particular CB seems to be associated with sysutil itself
// Then it checks for events on an event_queue associated with sysutil, checks if any cb is associated with that event and calls them with parameters that come from the event
error_code cellSysutilCheckCallback(ppu_thread& ppu)
{
	cellSysutil.trace("cellSysutilCheckCallback()");

	const auto cbm = fxm::get_always<sysutil_cb_manager>();

	for (auto&& func : cbm->registered.pop_all())
	{
		if (s32 res = func(ppu))
		{
			// Currently impossible
			return not_an_error(res);
		}

		if (ppu.is_stopped())
		{
			return 0;
		}
	}

	return CELL_OK;
}
Exemple #5
0
error_code sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr<u64> vptr)
{
	vm::temporary_unlock(ppu);

	sys_ppu_thread.trace("sys_ppu_thread_join(thread_id=0x%x, vptr=*0x%x)", thread_id, vptr);

	const auto thread = idm::get<named_thread<ppu_thread>>(thread_id, [&](ppu_thread& thread) -> CellError
	{
		CellError result = thread.joiner.atomic_op([&](u32& value) -> CellError
		{
			if (value == -3)
			{
				value = -2;
				return CELL_EBUSY;
			}

			if (value == -2)
			{
				return CELL_ESRCH;
			}

			if (value)
			{
				return CELL_EINVAL;
			}

			// TODO: check precedence?
			if (&ppu == &thread)
			{
				return CELL_EDEADLK;
			}

			value = ppu.id;
			return {};
		});

		if (!result)
		{
			lv2_obj::sleep(ppu);
		}

		return result;
	});

	if (!thread)
	{
		return CELL_ESRCH;
	}

	if (thread.ret && thread.ret != CELL_EBUSY)
	{
		return thread.ret;
	}

	// Wait for cleanup
	(*thread.ptr)();

	if (ppu.test_stopped())
	{
		return 0;
	}

	// Cleanup
	idm::remove<named_thread<ppu_thread>>(thread->id);

	if (!vptr)
	{
		return CELL_EFAULT;
	}

	// Get the exit status from the register
	*vptr = thread->gpr[3];
	return CELL_OK;
}
Exemple #6
0
error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout)
{
	sys_lwmutex.trace("_sys_lwmutex_lock(lwmutex_id=0x%x, timeout=0x%llx)", lwmutex_id, timeout);

	const auto mutex = idm::get<lv2_obj, lv2_lwmutex>(lwmutex_id, [&](lv2_lwmutex& mutex)
	{
		if (mutex.signaled.try_dec())
		{
			return true;
		}

		std::lock_guard lock(mutex.mutex);

		if (mutex.signaled.try_dec())
		{
			return true;
		}

		mutex.sq.emplace_back(&ppu);
		mutex.sleep(ppu, timeout);
		return false;
	});

	if (!mutex)
	{
		return CELL_ESRCH;
	}

	if (mutex.ret)
	{
		return CELL_OK;
	}

	ppu.gpr[3] = CELL_OK;

	while (!ppu.state.test_and_reset(cpu_flag::signal))
	{
		if (ppu.is_stopped())
		{
			return 0;
		}

		if (timeout)
		{
			const u64 passed = get_system_time() - ppu.start_time;

			if (passed >= timeout)
			{
				std::lock_guard lock(mutex->mutex);

				if (!mutex->unqueue(mutex->sq, &ppu))
				{
					timeout = 0;
					continue;
				}

				ppu.gpr[3] = CELL_ETIMEDOUT;
				break;
			}

			thread_ctrl::wait_for(timeout - passed);
		}
		else
		{
			thread_ctrl::wait();
		}
	}

	return not_an_error(ppu.gpr[3]);
}
Exemple #7
0
error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout)
{
	sys_lwcond.trace("_sys_lwcond_queue_wait(lwcond_id=0x%x, lwmutex_id=0x%x, timeout=0x%llx)", lwcond_id, lwmutex_id, timeout);

	std::shared_ptr<lv2_lwmutex> mutex;

	const auto cond = idm::get<lv2_obj, lv2_lwcond>(lwcond_id, [&](lv2_lwcond& cond) -> cpu_thread*
	{
		mutex = idm::get_unlocked<lv2_obj, lv2_lwmutex>(lwmutex_id);

		if (!mutex)
		{
			return nullptr;
		}

		std::lock_guard lock(cond.mutex);

		// Add a waiter
		cond.waiters++;
		cond.sq.emplace_back(&ppu);
		cond.sleep(ppu, timeout);

		std::lock_guard lock2(mutex->mutex);

		// Process lwmutex sleep queue
		if (const auto cpu = mutex->schedule<ppu_thread>(mutex->sq, mutex->protocol))
		{
			return cpu;
		}

		mutex->signaled++;
		return nullptr;
	});

	if (!cond || !mutex)
	{
		return CELL_ESRCH;
	}

	if (cond.ret)
	{
		cond->awake(*cond.ret);
	}

	ppu.gpr[3] = CELL_OK;

	while (!ppu.state.test_and_reset(cpu_flag::signal))
	{
		if (ppu.is_stopped())
		{
			return 0;
		}

		if (timeout)
		{
			const u64 passed = get_system_time() - ppu.start_time;

			if (passed >= timeout)
			{
				std::lock_guard lock(cond->mutex);

				if (!cond->unqueue(cond->sq, &ppu))
				{
					timeout = 0;
					continue;
				}

				cond->waiters--;

				if (mutex->signaled.try_dec())
				{
					ppu.gpr[3] = CELL_EDEADLK;
					break;
				}

				ppu.gpr[3] = CELL_ETIMEDOUT;
				break;
			}

			thread_ctrl::wait_for(timeout - passed);
		}
		else
		{
			thread_ctrl::wait();
		}
	}

	// Return cause
	return not_an_error(ppu.gpr[3]);
}
Exemple #8
0
	T get(std::size_t index) const
	{
		const u32 i = (u32)index + g_count;
		return ppu_gpr_cast<T>(i < 8 ? ctx->gpr[3 + i] : +*ctx->get_stack_arg(i));
	}
Exemple #9
0
// TODO
static std::string ps3_fmt(ppu_thread& context, vm::cptr<char> fmt, u32 g_count)
{
	std::string result;

	for (char c = *fmt++; c; c = *fmt++)
	{
		switch (c)
		{
		case '%':
		{
			const auto start = fmt - 1;

			// read flags
			const bool plus_sign = *fmt == '+' ? fmt++, true : false;
			const bool minus_sign = *fmt == '-' ? fmt++, true : false;
			const bool space_sign = *fmt == ' ' ? fmt++, true : false;
			const bool number_sign = *fmt == '#' ? fmt++, true : false;
			const bool zero_padding = *fmt == '0' ? fmt++, true : false;

			// read width
			const u32 width = [&]() -> u32
			{
				u32 width = 0;

				if (*fmt == '*')
				{
					fmt++;
					return context.get_next_arg(g_count);
				}

				while (*fmt - '0' < 10)
				{
					width = width * 10 + (*fmt++ - '0');
				}

				return width;
			}();

			// read precision
			const u32 prec = [&]() -> u32
			{
				u32 prec = 0;

				if (*fmt != '.')
				{
					return 0;
				}

				if (*++fmt == '*')
				{
					fmt++;
					return context.get_next_arg(g_count);
				}

				while (*fmt - '0' < 10)
				{
					prec = prec * 10 + (*fmt++ - '0');
				}

				return prec;
			}();

			switch (char cf = *fmt++)
			{
			case '%':
			{
				if (plus_sign || minus_sign || space_sign || number_sign || zero_padding || width || prec) break;

				result += '%';
				continue;
			}
			case 'd':
			case 'i':
			{
				// signed decimal
				const s64 value = context.get_next_arg(g_count);

				if (plus_sign || minus_sign || space_sign || number_sign || zero_padding || width || prec) break;

				result += fmt::format("%lld", value);
				continue;
			}
			case 'x':
			case 'X':
			{
				// hexadecimal
				const u64 value = context.get_next_arg(g_count);

				if (plus_sign || minus_sign || space_sign || prec) break;

				if (number_sign && value)
				{
					result += cf == 'x' ? "0x" : "0X";
				}

				const std::string& hex = fmt::format(cf == 'x' ? "%llx" : "%llX", value);

				if (hex.length() >= width)
				{
					result += hex;
				}
				else if (zero_padding)
				{
					result += std::string(width - hex.length(), '0') + hex;
				}
				else
				{
					result += hex + std::string(width - hex.length(), ' ');
				}
				continue;
			}
			case 's':
			{
				// string
				auto string = vm::cptr<char, u64>::make(context.get_next_arg(g_count));

				if (plus_sign || minus_sign || space_sign || number_sign || zero_padding || width || prec) break;

				result += string.get_ptr();
				continue;
			}
			case 'u':
			{
				// unsigned decimal
				const u64 value = context.get_next_arg(g_count);

				if (plus_sign || minus_sign || space_sign || number_sign || zero_padding || width || prec) break;

				result += fmt::format("%llu", value);
				continue;
			}
			}

			fmt::throw_exception("Unknown formatting: '%s'" HERE, start.get_ptr());
		}
		}

		result += c;
	}

	return result;
}
Exemple #10
0
error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout)
{
	sys_mutex.trace("sys_mutex_lock(mutex_id=0x%x, timeout=0x%llx)", mutex_id, timeout);

	const auto mutex = idm::get<lv2_obj, lv2_mutex>(mutex_id, [&](lv2_mutex& mutex)
	{
		CellError result = mutex.try_lock(ppu.id);

		if (result == CELL_EBUSY)
		{
			std::lock_guard lock(mutex.mutex);

			if (mutex.try_own(ppu, ppu.id))
			{
				result = {};
			}
			else
			{
				mutex.sleep(ppu, timeout);
			}
		}

		return result;
	});

	if (!mutex)
	{
		return CELL_ESRCH;
	}

	if (mutex.ret)
	{
		if (mutex.ret != CELL_EBUSY)
		{
			return mutex.ret;
		}
	}
	else
	{
		return CELL_OK;
	}

	ppu.gpr[3] = CELL_OK;

	while (!ppu.state.test_and_reset(cpu_flag::signal))
	{
		if (ppu.is_stopped())
		{
			return 0;
		}

		if (timeout)
		{
			const u64 passed = get_system_time() - ppu.start_time;

			if (passed >= timeout)
			{
				std::lock_guard lock(mutex->mutex);

				if (!mutex->unqueue(mutex->sq, &ppu))
				{
					timeout = 0;
					continue;
				}

				ppu.gpr[3] = CELL_ETIMEDOUT;
				break;
			}

			thread_ctrl::wait_for(timeout - passed);
		}
		else
		{
			thread_ctrl::wait();
		}
	}

	return not_an_error(ppu.gpr[3]);
}
Exemple #11
0
error_code sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout)
{
	sys_semaphore.trace("sys_semaphore_wait(sem_id=0x%x, timeout=0x%llx)", sem_id, timeout);

	const u64 start_time = ppu.gpr[10] = get_system_time();

	const auto sem = idm::get<lv2_obj, lv2_sema>(sem_id, [&](lv2_sema& sema)
	{
		const s32 val = sema.val;
		
		if (val > 0)
		{
			if (sema.val.compare_and_swap_test(val, val - 1))
			{
				return true;
			}
		}

		semaphore_lock lock(sema.mutex);

		if (sema.val-- <= 0)
		{
			sema.sq.emplace_back(&ppu);
			sema.sleep(ppu, start_time, timeout);
			return false;
		}

		return true;
	});

	if (!sem)
	{
		return CELL_ESRCH;
	}

	if (sem.ret)
	{
		return CELL_OK;
	}

	ppu.gpr[3] = CELL_OK;

	while (!ppu.state.test_and_reset(cpu_flag::signal))
	{
		if (timeout)
		{
			const u64 passed = get_system_time() - start_time;

			if (passed >= timeout)
			{
				semaphore_lock lock(sem->mutex);

				const s32 val = sem->val.fetch_op([](s32& val)
				{
					if (val < 0)
					{
						val++;
					}
				});

				if (val >= 0)
				{
					timeout = 0;
					continue;
				}

				verify(HERE), sem->unqueue(sem->sq, &ppu);
				ppu.gpr[3] = CELL_ETIMEDOUT;
				break;
			}

			thread_ctrl::wait_for(timeout - passed);
		}
		else
		{
			thread_ctrl::wait();
		}
	}

	ppu.check_state();
	return not_an_error(ppu.gpr[3]);
}
Exemple #12
0
error_code sys_semaphore_post(ppu_thread& ppu, u32 sem_id, s32 count)
{
	sys_semaphore.trace("sys_semaphore_post(sem_id=0x%x, count=%d)", sem_id, count);

	if (count < 0)
	{
		return CELL_EINVAL;
	}

	const auto sem = idm::get<lv2_obj, lv2_sema>(sem_id, [&](lv2_sema& sema)
	{
		const s32 val = sema.val;

		if (val >= 0 && count <= sema.max - val)
		{
			if (sema.val.compare_and_swap_test(val, val + count))
			{
				return true;
			}
		}

		return false;
	});

	if (!sem)
	{
		return CELL_ESRCH;
	}

	if (sem.ret)
	{
		return CELL_OK;
	}
	else
	{
		semaphore_lock lock(sem->mutex);

		const s32 val = sem->val.fetch_op([=](s32& val)
		{
			if (val + s64{count} <= sem->max)
			{
				val += count;
			}
		});

		if (val + s64{count} > sem->max)
		{
			return not_an_error(CELL_EBUSY);
		}

		// Wake threads
		for (s32 i = std::min<s32>(-std::min<s32>(val, 0), count); i > 0; i--)
		{
			const auto cpu = verify(HERE, sem->schedule<ppu_thread>(sem->sq, sem->protocol));

			sem->awake(*cpu);
		}
	}

	ppu.check_state();
	return CELL_OK;
}