Esempio n. 1
0
error_code prx_load_module(std::string path, u64 flags, vm::ptr<sys_prx_load_module_option_t> pOpt)
{
	if (s_prx_ignore.count(path))
	{
		sys_prx.warning("Ignored module: %s", path);

		const auto prx = idm::make_ptr<lv2_obj, lv2_prx>();

		prx->name = path.substr(path.find_last_of('/') + 1);

		return not_an_error(idm::last_id());
	}

	const auto loadedkeys = fxm::get_always<LoadedNpdrmKeys_t>();

	const ppu_prx_object obj = decrypt_self(fs::file(vfs::get(path)), loadedkeys->devKlic.data());

	if (obj != elf_error::ok)
	{
		return CELL_PRX_ERROR_ILLEGAL_LIBRARY;
	}

	const auto prx = ppu_load_prx(obj, path.substr(path.find_last_of('/') + 1));

	if (!prx)
	{
		return CELL_PRX_ERROR_ILLEGAL_LIBRARY;
	}

	ppu_initialize(*prx);

	sys_prx.success("Loaded module: %s", path);

	return not_an_error(idm::last_id());
}
Esempio n. 2
0
error_code cellVideoOutGetNumberOfDevice(u32 videoOut)
{
	cellSysutil.warning("cellVideoOutGetNumberOfDevice(videoOut=%d)", videoOut);

	switch (videoOut)
	{
	case CELL_VIDEO_OUT_PRIMARY: return not_an_error(1);
	case CELL_VIDEO_OUT_SECONDARY: return not_an_error(0);
	}

	return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT;
}
Esempio n. 3
0
error_code cellSubDisplayGetRequiredMemory(vm::ptr<CellSubDisplayParam> pParam)
{
	cellSubDisplay.warning("cellSubDisplayGetRequiredMemory(pParam=*0x%x)", pParam);

	switch (pParam->version)
	{
		case CELL_SUBDISPLAY_VERSION_0001: return not_an_error(CELL_SUBDISPLAY_0001_MEMORY_CONTAINER_SIZE);
		case CELL_SUBDISPLAY_VERSION_0002: return not_an_error(CELL_SUBDISPLAY_0002_MEMORY_CONTAINER_SIZE);
		case CELL_SUBDISPLAY_VERSION_0003: return not_an_error(CELL_SUBDISPLAY_0003_MEMORY_CONTAINER_SIZE);
	}

	return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
}
Esempio n. 4
0
error_code cellVideoOutGetResolutionAvailability(u32 videoOut, u32 resolutionId, u32 aspect, u32 option)
{
	cellSysutil.warning("cellVideoOutGetResolutionAvailability(videoOut=%d, resolutionId=0x%x, aspect=%d, option=%d)", videoOut, resolutionId, aspect, option);

	switch (videoOut)
	{
	case CELL_VIDEO_OUT_PRIMARY: return not_an_error(
		resolutionId == g_video_out_resolution_id.at(g_cfg.video.resolution)
		&& (aspect == CELL_VIDEO_OUT_ASPECT_AUTO || aspect == g_video_out_aspect_id.at(g_cfg.video.aspect_ratio))
	);
	case CELL_VIDEO_OUT_SECONDARY: return not_an_error(0);
	}

	return CELL_VIDEO_OUT_ERROR_UNSUPPORTED_VIDEO_OUT;
}
Esempio n. 5
0
error_code sys_semaphore_trywait(u32 sem_id)
{
	sys_semaphore.trace("sys_semaphore_trywait(sem_id=0x%x)", sem_id);

	const auto sem = idm::check<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;
			}
		}

		return false;
	});

	if (!sem)
	{
		return CELL_ESRCH;
	}

	if (!sem.ret)
	{
		return not_an_error(CELL_EBUSY);
	}

	return CELL_OK;
}
Esempio n. 6
0
error_code sys_mutex_trylock(ppu_thread& ppu, u32 mutex_id)
{
	sys_mutex.trace("sys_mutex_trylock(mutex_id=0x%x)", mutex_id);

	const auto mutex = idm::check<lv2_obj, lv2_mutex>(mutex_id, [&](lv2_mutex& mutex)
	{
		return mutex.try_lock(ppu.id);
	});

	if (!mutex)
	{
		return CELL_ESRCH;
	}

	if (mutex.ret)
	{
		if (mutex.ret == CELL_EBUSY)
		{
			return not_an_error(CELL_EBUSY);
		}

		return mutex.ret;
	}

	return CELL_OK;
}
Esempio n. 7
0
error_code _sys_timer_start(u32 timer_id, u64 base_time, u64 period)
{
	sys_timer.trace("_sys_timer_start(timer_id=0x%x, base_time=0x%llx, period=0x%llx)", timer_id, base_time, period);

	const u64 start_time = get_system_time();

	if (!period && start_time >= base_time)
	{
		// Invalid oneshot (TODO: what will happen if both args are 0?)
		return not_an_error(CELL_ETIMEDOUT);
	}

	if (period && period < 100)
	{
		// Invalid periodic timer
		return CELL_EINVAL;
	}

	const auto timer = idm::check<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
	{
		semaphore_lock lock(timer.mutex);

		if (timer.state != SYS_TIMER_STATE_STOP)
		{
			return CELL_EBUSY;
		}

		if (timer.port.expired())
		{
			return CELL_ENOTCONN;
		}

		// sys_timer_start_periodic() will use current time (TODO: is it correct?)
		timer.expire = base_time ? base_time : start_time + period;
		timer.period = period;
		timer.state  = SYS_TIMER_STATE_RUN;
		timer.notify();
		return {};
	});

	if (!timer)
	{
		return CELL_ESRCH;
	}

	if (timer.ret)
	{
		return timer.ret;
	}

	return CELL_OK;
}
Esempio n. 8
0
static void assert_eval_result(struct silc_ctx_t* c, const char* input, const char* expected_eval_result) {
  /* Given: */
  write_and_rewind(out, input);

  /* When: */
  silc_obj result = not_an_error(silc_eval(c, silc_read(c, out, silc_err_from_code(SILC_ERR_UNEXPECTED_EOF))));

  /* Then: */
  silc_print(c, result, in);
  READ_BUF(in, buf);
  if (0 != strcmp(buf, expected_eval_result)) {
    fprintf(stderr, "[eval] actual_result=%s, expected_result=%s\n", buf, expected_eval_result);
    ASSERT(!"results are not matching");
  }
}
Esempio n. 9
0
error_code cellGameDataCheck(u32 type, vm::cptr<char> dirName, vm::ptr<CellGameContentSize> size)
{
	cellGame.warning("cellGameDataCheck(type=%d, dirName=%s, size=*0x%x)", type, dirName, size);

	if ((type - 1) >= 3 || (type != CELL_GAME_GAMETYPE_DISC && !dirName))
	{
		return {CELL_GAME_ERROR_PARAM, type};
	}

	if (size)
	{
		// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
		size->hddFreeSizeKB = 40 * 1024 * 1024 - 1; // Read explanation in cellHddGameCheck

		// TODO: Calculate data size for game data, if necessary.
		size->sizeKB = CELL_GAME_SIZEKB_NOTCALC;
		size->sysSizeKB = 0;
	}

	// TODO: not sure what should be checked there
	const auto prm = fxm::make<content_permission>(type == CELL_GAME_GAMETYPE_DISC ? "" : dirName.get_ptr(), psf::registry{});

	if (!prm)
	{
		return CELL_GAME_ERROR_BUSY;
	}

	if (type == CELL_GAME_GAMETYPE_GAMEDATA)
	{
		prm->can_create = true;
	}

	const std::string dir = prm->dir.empty() ? "/dev_bdvd/PS3_GAME"s : "/dev_hdd0/game/" + prm->dir;

	if (!fs::is_dir(vfs::get(dir)))
	{
		cellGame.warning("cellGameDataCheck(): directory '%s' not found", dir);
		return not_an_error(CELL_GAME_RET_NONE);
	}

	prm->sfo = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO")));
	return CELL_OK;
}
Esempio n. 10
0
error_code _sys_lwmutex_trylock(u32 lwmutex_id)
{
	sys_lwmutex.trace("_sys_lwmutex_trylock(lwmutex_id=0x%x)", lwmutex_id);

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

	if (!mutex)
	{
		return CELL_ESRCH;
	}

	if (!mutex.ret)
	{
		return not_an_error(CELL_EBUSY);
	}

	return CELL_OK;
}
Esempio n. 11
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;
}
Esempio n. 12
0
error_code sys_event_port_send(ppu_thread& ppu, u32 eport_id, u64 data1, u64 data2, u64 data3)
{
	sys_event.trace("sys_event_port_send(eport_id=0x%x, data1=0x%llx, data2=0x%llx, data3=0x%llx)", eport_id, data1, data2, data3);

	const auto port = idm::get<lv2_obj, lv2_event_port>(eport_id, [&](lv2_event_port& port) -> CellError
	{
		if (const auto queue = port.queue.lock())
		{
			const u64 source = port.name ? port.name : ((u64)process_getpid() << 32) | (u64)eport_id;

			if (queue->send(source, data1, data2, data3))
			{
				return {};
			}

			return CELL_EBUSY;
		}

		return CELL_ENOTCONN;
	});

	if (!port)
	{
		return CELL_ESRCH;
	}

	if (port.ret)
	{
		if (port.ret == CELL_EBUSY)
		{
			return not_an_error(CELL_EBUSY);
		}

		return port.ret;
	}

	return CELL_OK;
}
Esempio n. 13
0
error_code sys_lwmutex_trylock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
{
	sysPrxForUser.trace("sys_lwmutex_trylock(lwmutex=*0x%x)", lwmutex);

	if (g_cfg.core.hle_lwmutex)
	{
		return sys_mutex_trylock(ppu, lwmutex->sleep_queue);
	}

	const be_t<u32> tid(ppu.id);

	// try to lock lightweight mutex
	const be_t<u32> old_owner = lwmutex->vars.owner.compare_and_swap(lwmutex_free, tid);

	if (old_owner == lwmutex_free)
	{
		// locking succeeded
		return CELL_OK;
	}

	if (old_owner == tid)
	{
		// recursive locking

		if ((lwmutex->attribute & SYS_SYNC_RECURSIVE) == 0)
		{
			// if not recursive
			return CELL_EDEADLK;
		}

		if (lwmutex->recursive_count == -1)
		{
			// if recursion limit reached
			return CELL_EKRESOURCE;
		}

		// recursive locking succeeded
		lwmutex->recursive_count++;
		_mm_mfence();

		return CELL_OK;
	}

	if (old_owner == lwmutex_dead)
	{
		// invalid or deleted mutex
		return CELL_EINVAL;
	}

	if (old_owner == lwmutex_reserved)
	{
		// should be locked by the syscall
		const error_code res = _sys_lwmutex_trylock(lwmutex->sleep_queue);

		if (res == CELL_OK)
		{
			// locking succeeded
			auto old = lwmutex->vars.owner.exchange(tid);

			if (old != lwmutex_reserved)
			{
				fmt::throw_exception("Locking failed (lwmutex=*0x%x, owner=0x%x)" HERE, lwmutex, old);
			}
		}

		return res;
	}

	// locked by another thread
	return not_an_error(CELL_EBUSY);
}
Esempio n. 14
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]);
}
Esempio n. 15
0
error_code _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode)
{
	sys_lwcond.trace("_sys_lwcond_signal_all(lwcond_id=0x%x, lwmutex_id=0x%x, mode=%d)", lwcond_id, lwmutex_id, mode);

	// Mode 1: lwmutex was initially owned by the calling thread
	// Mode 2: lwmutex was not owned by the calling thread and waiter hasn't been increased

	if (mode < 1 || mode > 2)
	{
		fmt::throw_exception("Unknown mode (%d)" HERE, mode);
	}

	std::basic_string<cpu_thread*> threads;

	lv2_lwmutex* mutex = nullptr;

	const auto cond = idm::check<lv2_obj, lv2_lwcond>(lwcond_id, [&](lv2_lwcond& cond) -> u32
	{
		mutex = idm::check_unlocked<lv2_obj, lv2_lwmutex>(lwmutex_id);

		if (mutex && cond.waiters)
		{
			semaphore_lock lock(mutex->mutex);

			u32 result = 0;

			while (const auto cpu = cond.schedule<ppu_thread>(cond.sq, mutex->protocol))
			{
				cond.waiters--;

				static_cast<ppu_thread*>(cpu)->gpr[3] = mode == 2;

				if (mode != 2 && !mutex->signaled.fetch_op([](u32& v) { if (v) v--; }))
				{
					mutex->sq.emplace_back(cpu);
				}
				else
				{
					threads.push_back(cpu);
				}

				result++;
			}

			return result;
		}

		return 0;
	});

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

	// TODO: signal only one thread
	for (auto cpu : threads)
	{
		cpu->set_signal();
	}

	if (mode == 1)
	{
		// Mode 1: return the amount of threads (TODO)
		return not_an_error(cond.ret);
	}

	return CELL_OK;
}
Esempio n. 16
0
error_code _sys_lwcond_signal(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mode)
{
	sys_lwcond.trace("_sys_lwcond_signal(lwcond_id=0x%x, lwmutex_id=0x%x, ppu_thread_id=0x%x, mode=%d)", lwcond_id, lwmutex_id, ppu_thread_id, mode);

	// Mode 1: lwmutex was initially owned by the calling thread
	// Mode 2: lwmutex was not owned by the calling thread and waiter hasn't been increased
	// Mode 3: lwmutex was forcefully owned by the calling thread

	if (mode < 1 || mode > 3)
	{
		fmt::throw_exception("Unknown mode (%d)" HERE, mode);
	}

	lv2_lwmutex* mutex = nullptr;

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

		if (cond.waiters)
		{
			std::lock_guard lock(cond.mutex);

			cpu_thread* result = nullptr;

			if (ppu_thread_id != -1)
			{
				for (auto cpu : cond.sq)
				{
					if (cpu->id == ppu_thread_id)
					{
						verify(HERE), cond.unqueue(cond.sq, cpu);
						result = cpu;
						break;
					}
				}
			}
			else
			{
				result = cond.schedule<ppu_thread>(cond.sq, cond.control->lwmutex->attribute & SYS_SYNC_ATTR_PROTOCOL_MASK);
			}

			if (result)
			{
				cond.waiters--;

				if (mode == 2)
				{
					static_cast<ppu_thread*>(result)->gpr[3] = CELL_EBUSY;
				}

				if (mode == 1)
				{
					verify(HERE), !mutex->signaled;
					std::lock_guard lock(mutex->mutex);
					mutex->sq.emplace_back(result);
					result = nullptr;
					mode = 2; // Enforce CELL_OK
				}

				return result;
			}
		}

		return nullptr;
	});

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

	if (cond.ret)
	{
		cond->awake(*cond.ret);
	}
	else if (mode == 2)
	{
		return CELL_OK;
	}
	else if (mode == 1 || ppu_thread_id == -1)
	{
		return not_an_error(CELL_EPERM);
	}
	else
	{
		return not_an_error(CELL_ENOENT);
	}

	return CELL_OK;
}
Esempio n. 17
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]);
}
Esempio n. 18
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]);
}
Esempio n. 19
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);

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

	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;
		}

		semaphore_lock lock(mutex->mutex);

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

		// 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.ret->set_signal();
	}

	// SLEEP

	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(mutex->mutex);

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

				cond->waiters--;

				if (mutex->signaled.fetch_op([](u32& v) { if (v) v--; }))
				{
					return not_an_error(CELL_EDEADLK);
				}

				return not_an_error(CELL_ETIMEDOUT);
			}

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

	// Return cause
	return not_an_error(ppu.gpr[3] ? CELL_EBUSY : CELL_OK);
}
Esempio n. 20
0
error_code sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout)
{
	sysPrxForUser.trace("sys_lwmutex_lock(lwmutex=*0x%x, timeout=0x%llx)", lwmutex, timeout);

	if (g_cfg.core.hle_lwmutex)
	{
		return sys_mutex_lock(ppu, lwmutex->sleep_queue, timeout);
	}

	const be_t<u32> tid(ppu.id);

	// try to lock lightweight mutex
	const be_t<u32> old_owner = lwmutex->vars.owner.compare_and_swap(lwmutex_free, tid);

	if (old_owner == lwmutex_free)
	{
		// locking succeeded
		return CELL_OK;
	}

	if (old_owner == tid)
	{
		// recursive locking

		if ((lwmutex->attribute & SYS_SYNC_RECURSIVE) == 0)
		{
			// if not recursive
			return CELL_EDEADLK;
		}

		if (lwmutex->recursive_count == -1)
		{
			// if recursion limit reached
			return CELL_EKRESOURCE;
		}

		// recursive locking succeeded
		lwmutex->recursive_count++;
		_mm_mfence();

		return CELL_OK;
	}

	if (old_owner == lwmutex_dead)
	{
		// invalid or deleted mutex
		return CELL_EINVAL;
	}

	for (u32 i = 0; i < 10; i++)
	{
		busy_wait();

		if (lwmutex->vars.owner.load() == lwmutex_free)
		{
			if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid))
			{
				// locking succeeded
				return CELL_OK;
			}
		}
	}

	// atomically increment waiter value using 64 bit op
	lwmutex->all_info++;

	if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid))
	{
		// locking succeeded
		--lwmutex->all_info;

		return CELL_OK;
	}

	// lock using the syscall
	const error_code res = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);

	lwmutex->all_info--;

	if (res == CELL_OK)
	{
		// locking succeeded
		auto old = lwmutex->vars.owner.exchange(tid);

		if (old != lwmutex_reserved)
		{
			fmt::throw_exception("Locking failed (lwmutex=*0x%x, owner=0x%x)" HERE, lwmutex, old);
		}

		return CELL_OK;
	}

	if (res == CELL_EBUSY && lwmutex->attribute & SYS_SYNC_RETRY)
	{
		while (true)
		{
			for (u32 i = 0; i < 10; i++)
			{
				busy_wait();

				if (lwmutex->vars.owner.load() == lwmutex_free)
				{
					if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid))
					{
						return CELL_OK;
					}
				}
			}

			lwmutex->all_info++;

			if (lwmutex->vars.owner.compare_and_swap_test(lwmutex_free, tid))
			{
				lwmutex->all_info--;
				return CELL_OK;
			}

			const u64 time0 = timeout ? get_system_time() : 0;

			const error_code res_ = _sys_lwmutex_lock(ppu, lwmutex->sleep_queue, timeout);

			if (res_ == CELL_OK)
			{
				lwmutex->vars.owner.release(tid);
			}
			else if (timeout && res_ != CELL_ETIMEDOUT)
			{
				const u64 time_diff = get_system_time() - time0;

				if (timeout <= time_diff)
				{
					lwmutex->all_info--;
					return not_an_error(CELL_ETIMEDOUT);
				}

				timeout -= time_diff;
			}

			lwmutex->all_info--;

			if (res_ != CELL_EBUSY)
			{
				return res_;
			}
		}
	}

	return res;
}
Esempio n. 21
0
error_code _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mode)
{
	sys_lwcond.trace("_sys_lwcond_signal(lwcond_id=0x%x, lwmutex_id=0x%x, ppu_thread_id=0x%x, mode=%d)", lwcond_id, lwmutex_id, ppu_thread_id, mode);

	// Mode 1: lwmutex was initially owned by the calling thread
	// Mode 2: lwmutex was not owned by the calling thread and waiter hasn't been increased
	// Mode 3: lwmutex was forcefully owned by the calling thread

	if (mode < 1 || mode > 3)
	{
		fmt::throw_exception("Unknown mode (%d)" HERE, mode);
	}

	lv2_lwmutex* mutex = nullptr;

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

		if (mutex && cond.waiters)
		{
			semaphore_lock lock(mutex->mutex);

			cpu_thread* result = nullptr;

			if (ppu_thread_id != -1)
			{
				for (auto cpu : cond.sq)
				{
					if (cpu->id == ppu_thread_id)
					{
						verify(HERE), cond.unqueue(cond.sq, cpu);
						result = cpu;
						break;
					}
				}
			}
			else
			{
				result = cond.schedule<ppu_thread>(cond.sq, mutex->protocol);
			}

			if (result)
			{
				cond.waiters--;

				static_cast<ppu_thread*>(result)->gpr[3] = mode == 2;

				if (mode != 2 && !mutex->signaled.fetch_op([](u32& v) { if (v) v--; }))
				{
					mutex->sq.emplace_back(result);
					result = nullptr;
					mode = 2; // Enforce CELL_OK
				}

				return result;
			}
		}

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

	if (cond.ret)
	{
		cond.ret->set_signal();
	}
	else if (mode == 2)
	{
		return CELL_OK;
	}
	else if (mode == 1 || ppu_thread_id == -1)
	{
		return not_an_error(CELL_EPERM);
	}
	else
	{
		return not_an_error(CELL_ENOENT);
	}

	return CELL_OK;
}
Esempio n. 22
0
error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout)
{
	sys_event.trace("sys_event_queue_receive(equeue_id=0x%x, *0x%x, timeout=0x%llx)", equeue_id, dummy_event, timeout);

	const u64 start_time = get_system_time();

	const auto queue = idm::get<lv2_event_queue>(equeue_id, [&](lv2_event_queue& queue) -> CellError
	{
		if (queue.type != SYS_PPU_QUEUE)
		{
			return CELL_EINVAL;
		}

		lv2_lock lock{queue};

		if (queue.events.size())
		{
			verify(HERE), queue.waiters.empty();

			// Return event data in registers r4-r7 (dummy_event is not used)
			std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = queue.events.front();
			queue.events.pop_front();
			return {};
		}

		// If cancelled, r3 will contain error code
		ppu.gpr[3] = CELL_OK;

		// Add thread to the wait queue
		queue.waiters.emplace_back(&ppu);

		return CELL_EBUSY;
	});

	if (!queue)
	{
		return CELL_ESRCH;
	}

	if (!queue.value)
	{
		return CELL_OK;
	}
	else if (queue.value != CELL_EBUSY)
	{
		return queue.value;
	}

	thread_lock lock(ppu);

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

			if (passed >= timeout)
			{
				lv2_lock lock{queue};

				for (auto it = queue->waiters.begin(), end = queue->waiters.end(); it != end; it++)
				{
					if (*it == &ppu)
					{
						queue->waiters.erase(it);
						return not_an_error(CELL_ETIMEDOUT);
					}
				}

				timeout = 0;
				continue;
			}

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

	if (ppu.gpr[3])
	{
		return static_cast<CellError>(ppu.gpr[3]);
	}

	return CELL_OK;
}
Esempio n. 23
0
error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout)
{
	sys_event.trace("sys_event_queue_receive(equeue_id=0x%x, *0x%x, timeout=0x%llx)", equeue_id, dummy_event, timeout);

	const auto queue = idm::get<lv2_obj, lv2_event_queue>(equeue_id, [&](lv2_event_queue& queue) -> CellError
	{
		if (queue.type != SYS_PPU_QUEUE)
		{
			return CELL_EINVAL;
		}

		semaphore_lock lock(queue.mutex);
		
		if (queue.events.empty())
		{
			queue.sq.emplace_back(&ppu);
			queue.sleep(ppu, timeout);
			return CELL_EBUSY;
		}

		std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = queue.events.front();
		queue.events.pop_front();
		return {};
	});

	if (!queue)
	{
		return CELL_ESRCH;
	}

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

	// If cancelled, gpr[3] will be non-zero. Other registers must contain event data.
	ppu.gpr[3] = 0;

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

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

				if (!queue->unqueue(queue->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]);
}
Esempio n. 24
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;
}
Esempio n. 25
0
    " (define a 3)\n"
    " (define b 4)\n"
    " (define x (lambda (a) (lambda (b) (+ a (* 2 b) 50000))))\n"
    " (define y (x 1))\n"
    " (+ (y 10) (* a 100) (* b 1000))\n"
    ")",
    "54321");
  silc_free_context(c);
END_TEST_METHOD()

BEGIN_TEST_METHOD(test_eval_gc)
  struct silc_ctx_t* c = silc_new_context();
  silc_set_default_out(c, in);

  write_and_rewind(out, "(gc)");
  silc_obj result = not_an_error(silc_eval(c, silc_read(c, out, silc_err_from_code(SILC_ERR_UNEXPECTED_EOF))));

  ASSERT(SILC_OBJ_NIL == result);

  silc_free_context(c);
END_TEST_METHOD()

BEGIN_TEST_METHOD(test_eval_nonfunction)
  struct silc_ctx_t* c = silc_new_context();

  write_and_rewind(out, "(1)");
  silc_obj result = silc_eval(c, silc_read(c, out, silc_err_from_code(SILC_ERR_UNEXPECTED_EOF)));

  ASSERT(SILC_ERR_NOT_A_FUNCTION == silc_try_get_err_code(result));

  silc_free_context(c);
Esempio n. 26
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]);
}
Esempio n. 27
0
error_code _sys_lwcond_signal_all(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u32 mode)
{
	sys_lwcond.trace("_sys_lwcond_signal_all(lwcond_id=0x%x, lwmutex_id=0x%x, mode=%d)", lwcond_id, lwmutex_id, mode);

	// Mode 1: lwmutex was initially owned by the calling thread
	// Mode 2: lwmutex was not owned by the calling thread and waiter hasn't been increased

	if (mode < 1 || mode > 2)
	{
		fmt::throw_exception("Unknown mode (%d)" HERE, mode);
	}

	std::basic_string<cpu_thread*> threads;

	lv2_lwmutex* mutex = nullptr;

	const auto cond = idm::check<lv2_obj, lv2_lwcond>(lwcond_id, [&](lv2_lwcond& cond) -> u32
	{
		mutex = idm::check_unlocked<lv2_obj, lv2_lwmutex>(lwmutex_id);

		if (cond.waiters)
		{
			std::lock_guard lock(cond.mutex);

			u32 result = 0;

			while (const auto cpu = cond.schedule<ppu_thread>(cond.sq, cond.control->lwmutex->attribute & SYS_SYNC_ATTR_PROTOCOL_MASK))
			{
				cond.waiters--;

				if (mode == 2)
				{
					static_cast<ppu_thread*>(cpu)->gpr[3] = CELL_EBUSY;
				}

				if (mode == 1)
				{
					verify(HERE), !mutex->signaled;
					std::lock_guard lock(mutex->mutex);
					mutex->sq.emplace_back(cpu);
				}
				else
				{
					threads.push_back(cpu);
				}

				result++;
			}

			return result;
		}

		return 0;
	});

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

	for (auto cpu : threads)
	{
		cond->awake(*cpu);
	}

	if (mode == 1)
	{
		// Mode 1: return the amount of threads (TODO)
		return not_an_error(cond.ret);
	}

	return CELL_OK;
}
Esempio n. 28
0
error_code sceNpTrophyGetTrophyInfo(u32 context, u32 handle, s32 trophyId, vm::ptr<SceNpTrophyDetails> details, vm::ptr<SceNpTrophyData> data)
{
	sceNpTrophy.warning("sceNpTrophyGetTrophyInfo(context=0x%x, handle=0x%x, trophyId=%d, details=*0x%x, data=*0x%x)", context, handle, trophyId, details, data);

	if (!details && !data)
	{
		return SCE_NP_TROPHY_ERROR_INVALID_ARGUMENT;
	}

	const auto ctxt = idm::get<trophy_context_t>(context);

	if (!ctxt)
	{
		return SCE_NP_TROPHY_ERROR_UNKNOWN_CONTEXT;
	}

	const auto hndl = idm::get<trophy_handle_t>(handle);

	if (!hndl)
	{
		return SCE_NP_TROPHY_ERROR_UNKNOWN_HANDLE;
	}

	fs::file config(vfs::get("/dev_hdd0/home/" + Emu.GetUsr() + "/trophy/" + ctxt->trp_name + "/TROPCONF.SFM"));

	if (!config)
	{
		return SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST;
	}

	if (details)
		memset(details.get_ptr(), 0, sizeof(SceNpTrophyDetails));
	if (data)
		memset(data.get_ptr(), 0, sizeof(SceNpTrophyData));

	rXmlDocument doc;
	doc.Read(config.to_string());

	auto trophy_base = doc.GetRoot();
	if (trophy_base->GetChildren()->GetName() == "trophyconf")
	{
		trophy_base = trophy_base->GetChildren();
	}

	bool found = false;
	for (std::shared_ptr<rXmlNode> n = trophy_base->GetChildren(); n; n = n->GetNext())
	{
		if (n->GetName() == "trophy" && (trophyId == atoi(n->GetAttribute("id").c_str())))
		{
			found = true;

			if (n->GetAttribute("hidden")[0] == 'y' && !ctxt->tropusr->GetTrophyUnlockState(trophyId)) // Trophy is hidden
			{
				return SCE_NP_TROPHY_ERROR_HIDDEN;
			}

			if (details)
			{
				details->trophyId = trophyId;
				switch (n->GetAttribute("ttype")[0])
				{
				case 'B': details->trophyGrade = SCE_NP_TROPHY_GRADE_BRONZE;   break;
				case 'S': details->trophyGrade = SCE_NP_TROPHY_GRADE_SILVER;   break;
				case 'G': details->trophyGrade = SCE_NP_TROPHY_GRADE_GOLD;     break;
				case 'P': details->trophyGrade = SCE_NP_TROPHY_GRADE_PLATINUM; break;
				}

				switch (n->GetAttribute("hidden")[0])
				{
				case 'y': details->hidden = true;  break;
				case 'n': details->hidden = false; break;
				}

				for (std::shared_ptr<rXmlNode> n2 = n->GetChildren(); n2; n2 = n2->GetNext())
				{
					const std::string n2_name = n2->GetName();

					if (n2_name == "name")
					{
						strcpy_trunc(details->name, n2->GetNodeContent());
					}
					else if (n2_name == "detail")
					{
						strcpy_trunc(details->description, n2->GetNodeContent());
					}
				}
			}

			if (data)
			{
				data->trophyId = trophyId;
				data->unlocked = ctxt->tropusr->GetTrophyUnlockState(trophyId) != 0; // ???
				data->timestamp = ctxt->tropusr->GetTrophyTimestamp(trophyId);
			}
			break;
		}
	}

	if (!found)
	{
		return not_an_error(SCE_NP_TROPHY_INVALID_TROPHY_ID);
	}

	return CELL_OK;
}