diff --git a/rpcs3/Emu/Cell/lv2/sys_spu.cpp b/rpcs3/Emu/Cell/lv2/sys_spu.cpp index 2c6bd6f3ad..d6856f5ee5 100644 --- a/rpcs3/Emu/Cell/lv2/sys_spu.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_spu.cpp @@ -388,19 +388,43 @@ struct spu_limits_t SAVESTATE_INIT_POS(47); - bool check(const limits_data& init) const + bool check_valid(const limits_data& init) const + { + const u32 physical_spus_count = init.physical; + const u32 controllable_spu_count = init.controllable; + + const u32 spu_limit = init.spu_limit != umax ? init.spu_limit : max_spu; + const u32 raw_limit = init.raw_limit != umax ? init.raw_limit : max_raw; + + if (spu_limit + raw_limit > 6 || physical_spus_count > spu_limit || controllable_spu_count > spu_limit) + { + return false; + } + + return true; + } + + bool check_busy(const limits_data& init) const { u32 physical_spus_count = init.physical; u32 raw_spu_count = init.raw_spu; u32 controllable_spu_count = init.controllable; + u32 system_coop = init.controllable != 0 && init.physical != 0 ? 1 : 0; + const u32 spu_limit = init.spu_limit != umax ? init.spu_limit : max_spu; const u32 raw_limit = init.raw_limit != umax ? init.raw_limit : max_raw; idm::select([&](u32, lv2_spu_group& group) { - if (group.has_scheduler_context) + if (group.type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM) { - controllable_spu_count = std::max(controllable_spu_count, group.max_num); + system_coop++; + controllable_spu_count = std::max(controllable_spu_count, 1); + physical_spus_count += group.max_num - 1; + } + else if (group.has_scheduler_context) + { + controllable_spu_count = std::max(controllable_spu_count, group.max_num); } else { @@ -410,11 +434,18 @@ struct spu_limits_t raw_spu_count += spu_thread::g_raw_spu_ctr; - if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count >= spu_limit || physical_spus_count + controllable_spu_count > spu_limit) + // physical_spus_count >= spu_limit returns EBUSY, not EINVAL! + if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count >= spu_limit || physical_spus_count > spu_limit || controllable_spu_count > spu_limit) { return false; } + if (system_coop > 1) + { + // Cannot have more than one SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM group at a time + return false; + } + return true; } }; @@ -437,7 +468,7 @@ error_code sys_spu_initialize(ppu_thread& ppu, u32 max_usable_spu, u32 max_raw_s std::lock_guard lock(limits.mutex); - if (!limits.check(limits_data{.spu_limit = max_usable_spu - max_raw_spu, .raw_limit = max_raw_spu})) + if (!limits.check_busy(limits_data{.spu_limit = max_usable_spu - max_raw_spu, .raw_limit = max_raw_spu})) { return CELL_EBUSY; } @@ -845,23 +876,24 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num switch (type) { case 0x0: - case 0x4: - case 0x18: + case SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER: + case SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT: { break; } - case 0x20: - case 0x22: - case 0x24: - case 0x26: + case SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM: + case (SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM | 0x2): + case (SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM | 0x4): + case (SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM | 0x6): { if (type == 0x22 || type == 0x26) { needs_root = true; } - min_threads = 2; // That's what appears from reversing + // For a single thread that is being shared with system (the cooperative victim) + min_threads = 2; break; } @@ -901,7 +933,7 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num if (is_system_coop) { - // Constant size, unknown what it means + // For a single thread that is being shared with system (the cooperative victim) mem_size = SPU_LS_SIZE; } else if (type & SYS_SPU_THREAD_GROUP_TYPE_NON_CONTEXT) @@ -952,7 +984,29 @@ error_code sys_spu_thread_group_create(ppu_thread& ppu, vm::ptr id, u32 num std::unique_lock lock(limits.mutex); - if (!limits.check(use_scheduler ? limits_data{.controllable = num} : limits_data{.physical = num})) + limits_data group_limits{}; + + if (is_system_coop) + { + group_limits.controllable = 1; + group_limits.physical = num - 1; + } + else if (use_scheduler) + { + group_limits.controllable = num; + } + else + { + group_limits.physical = num; + } + + if (!limits.check_valid(group_limits)) + { + ct->free(mem_size); + return CELL_EINVAL; + } + + if (!limits.check_busy(group_limits)) { ct->free(mem_size); return CELL_EBUSY; @@ -1135,6 +1189,11 @@ error_code sys_spu_thread_group_suspend(ppu_thread& ppu, u32 id) return CELL_EINVAL; } + if (group->type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM) + { + return CELL_EINVAL; + } + std::lock_guard lock(group->mutex); CellError error; @@ -1218,6 +1277,11 @@ error_code sys_spu_thread_group_resume(ppu_thread& ppu, u32 id) return CELL_EINVAL; } + if (group->type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM) + { + return CELL_EINVAL; + } + struct notify_on_exit { usz index = umax; @@ -1565,6 +1629,11 @@ error_code sys_spu_thread_group_set_priority(ppu_thread& ppu, u32 id, s32 priori return CELL_EINVAL; } + if (group->type & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM) + { + return CELL_EINVAL; + } + group->prio.atomic_op([&](std::common_type_t& prio) { prio.prio = priority; @@ -1592,6 +1661,11 @@ error_code sys_spu_thread_group_get_priority(ppu_thread& ppu, u32 id, vm::ptrtype & SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM) + { + // Regardless of the value being set in group creation + *priority = 15; + } else { *priority = group->prio.load().prio; @@ -2275,7 +2349,7 @@ error_code sys_raw_spu_create(ppu_thread& ppu, vm::ptr id, vm::ptr at std::lock_guard lock(limits.mutex); - if (!limits.check(limits_data{.raw_spu = 1})) + if (!limits.check_busy(limits_data{.raw_spu = 1})) { return CELL_EAGAIN; } @@ -2331,7 +2405,7 @@ error_code sys_isolated_spu_create(ppu_thread& ppu, vm::ptr id, vm::ptr