#pragma once #include "sys_sync.h" #include "sys_event.h" #include "Emu/Cell/SPUThread.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Memory/vm_ptr.h" #include "Utilities/File.h" struct lv2_memory_container; enum : s32 { SYS_SPU_THREAD_GROUP_TYPE_NORMAL = 0x00, //SYS_SPU_THREAD_GROUP_TYPE_SEQUENTIAL = 0x01, doesn't exist SYS_SPU_THREAD_GROUP_TYPE_SYSTEM = 0x02, SYS_SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER = 0x04, SYS_SPU_THREAD_GROUP_TYPE_NON_CONTEXT = 0x08, SYS_SPU_THREAD_GROUP_TYPE_EXCLUSIVE_NON_CONTEXT = 0x18, SYS_SPU_THREAD_GROUP_TYPE_COOPERATE_WITH_SYSTEM = 0x20, }; enum { SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT = 0x0001, SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT = 0x0002, SYS_SPU_THREAD_GROUP_JOIN_TERMINATED = 0x0004 }; enum { SYS_SPU_THREAD_GROUP_EVENT_RUN = 1, SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION = 2, SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE = 4, }; enum : u64 { SYS_SPU_THREAD_GROUP_EVENT_RUN_KEY = 0xFFFFFFFF53505500ull, SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION_KEY = 0xFFFFFFFF53505503ull, SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE_KEY = 0xFFFFFFFF53505504ull, }; enum { SYS_SPU_THREAD_GROUP_LOG_ON = 0x0, SYS_SPU_THREAD_GROUP_LOG_OFF = 0x1, SYS_SPU_THREAD_GROUP_LOG_GET_STATUS = 0x2, }; enum spu_group_status : u32 { SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED, SPU_THREAD_GROUP_STATUS_INITIALIZED, SPU_THREAD_GROUP_STATUS_READY, SPU_THREAD_GROUP_STATUS_WAITING, SPU_THREAD_GROUP_STATUS_SUSPENDED, SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED, SPU_THREAD_GROUP_STATUS_RUNNING, SPU_THREAD_GROUP_STATUS_STOPPED, SPU_THREAD_GROUP_STATUS_DESTROYED, // Internal state SPU_THREAD_GROUP_STATUS_UNKNOWN, }; enum : s32 { SYS_SPU_SEGMENT_TYPE_COPY = 1, SYS_SPU_SEGMENT_TYPE_FILL = 2, SYS_SPU_SEGMENT_TYPE_INFO = 4, }; enum : u32 { SYS_SPU_THREAD_STOP_YIELD = 0x0100, SYS_SPU_THREAD_STOP_GROUP_EXIT = 0x0101, SYS_SPU_THREAD_STOP_THREAD_EXIT = 0x0102, SYS_SPU_THREAD_STOP_RECEIVE_EVENT = 0x0110, SYS_SPU_THREAD_STOP_TRY_RECEIVE_EVENT = 0x0111, SYS_SPU_THREAD_STOP_SWITCH_SYSTEM_MODULE = 0x0120, }; struct sys_spu_thread_group_attribute { be_t nsize; // name length including NULL terminator vm::bcptr name; be_t type; be_t ct; // memory container id }; enum : u32 { SYS_SPU_THREAD_OPTION_NONE = 0, SYS_SPU_THREAD_OPTION_ASYNC_INTR_ENABLE = 1, SYS_SPU_THREAD_OPTION_DEC_SYNC_TB_ENABLE = 2, }; struct sys_spu_thread_attribute { vm::bcptr name; be_t name_len; be_t option; }; struct sys_spu_thread_argument { be_t arg1; be_t arg2; be_t arg3; be_t arg4; }; struct sys_spu_segment { be_t type; // copy, fill, info be_t ls; // local storage address be_t size; union { be_t addr; // address or fill value u64 pad; }; }; CHECK_SIZE(sys_spu_segment, 0x18); enum : u32 { SYS_SPU_IMAGE_TYPE_USER = 0, SYS_SPU_IMAGE_TYPE_KERNEL = 1, }; struct sys_spu_image { be_t type; // user, kernel be_t entry_point; // Note: in kernel mode it's used to store id vm::bptr segs; be_t nsegs; template static s32 get_nsegs(const Phdrs& phdrs) { s32 num_segs = 0; for (const auto& phdr : phdrs) { if (phdr.p_type != 1u && phdr.p_type != 4u) { return -1; } if (phdr.p_type == 1u && phdr.p_filesz != phdr.p_memsz && phdr.p_filesz) { num_segs += 2; } else if (phdr.p_type == 1u || CountInfo) { num_segs += 1; } } return num_segs; } template static s32 fill(vm::ptr segs, s32 nsegs, const Phdrs& phdrs, u32 src) { s32 num_segs = 0; for (const auto& phdr : phdrs) { if (phdr.p_type == 1u) { if (phdr.p_filesz) { if (num_segs >= nsegs) { return -2; } auto* seg = &segs[num_segs++]; seg->type = SYS_SPU_SEGMENT_TYPE_COPY; seg->ls = static_cast(phdr.p_vaddr); seg->size = static_cast(phdr.p_filesz); seg->addr = static_cast(phdr.p_offset + src); } if (phdr.p_memsz > phdr.p_filesz) { if (num_segs >= nsegs) { return -2; } auto* seg = &segs[num_segs++]; seg->type = SYS_SPU_SEGMENT_TYPE_FILL; seg->ls = static_cast(phdr.p_vaddr + phdr.p_filesz); seg->size = static_cast(phdr.p_memsz - phdr.p_filesz); seg->addr = 0; } } else if (WriteInfo && phdr.p_type == 4u) { if (num_segs >= nsegs) { return -2; } auto* seg = &segs[num_segs++]; seg->type = SYS_SPU_SEGMENT_TYPE_INFO; seg->size = 0x20; seg->addr = static_cast(phdr.p_offset + 0x14 + src); } else if (phdr.p_type != 4u) { return -1; } } return num_segs; } void load(const fs::file& stream); void free(); static void deploy(u8* loc, sys_spu_segment* segs, u32 nsegs); }; enum : u32 { SYS_SPU_IMAGE_PROTECT = 0, SYS_SPU_IMAGE_DIRECT = 1, }; struct lv2_spu_image : lv2_obj { static const u32 id_base = 0x22000000; const u32 e_entry; const vm::ptr segs; const s32 nsegs; lv2_spu_image(u32 entry, vm::ptr segs, s32 nsegs) : e_entry(entry) , segs(segs) , nsegs(nsegs) { } }; struct sys_spu_thread_group_syscall_253_info { be_t deadlineMeetCounter; // From cellSpursGetInfo be_t deadlineMissCounter; // Same be_t timestamp; be_t _x10[6]; }; struct lv2_spu_group { static const u32 id_base = 0x04000100; static const u32 id_step = 0x100; static const u32 id_count = 255; static constexpr std::pair id_invl_range = {0, 8}; static_assert(spu_thread::id_count == id_count * 6 + 5); const std::string name; const u32 id; const u32 max_num; const u32 mem_size; const s32 type; // SPU Thread Group Type lv2_memory_container* const ct; // Memory Container const bool has_scheduler_context; u32 max_run; shared_mutex mutex; atomic_t init; // Initialization Counter atomic_t prio; // SPU Thread Group Priority atomic_t run_state; // SPU Thread Group State atomic_t exit_status; // SPU Thread Group Exit Status atomic_t join_state; // flags used to detect exit cause and signal atomic_t running; // Number of running threads atomic_t stop_count; class ppu_thread* waiter = nullptr; bool set_terminate = false; std::array>, 8> threads; // SPU Threads std::array threads_map; // SPU Threads map based number std::array>, 8> imgs; // SPU Images std::array, 8> args; // SPU Thread Arguments std::weak_ptr ep_run; // port for SYS_SPU_THREAD_GROUP_EVENT_RUN events std::weak_ptr ep_exception; // TODO: SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION std::weak_ptr ep_sysmodule; // TODO: SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE lv2_spu_group(std::string name, u32 num, s32 prio, s32 type, lv2_memory_container* ct, bool uses_scheduler, u32 mem_size) : name(std::move(name)) , id(idm::last_id()) , max_num(num) , mem_size(mem_size) , type(type) , ct(ct) , has_scheduler_context(uses_scheduler) , max_run(num) , init(0) , prio(prio) , run_state(SPU_THREAD_GROUP_STATUS_NOT_INITIALIZED) , exit_status(0) , join_state(0) , running(0) , stop_count(0) { threads_map.fill(-1); } void send_run_event(u64 data1, u64 data2, u64 data3) { if (const auto queue = ep_run.lock()) { queue->send(SYS_SPU_THREAD_GROUP_EVENT_RUN_KEY, data1, data2, data3); } } void send_exception_event(u64 data1, u64 data2, u64 data3) { if (const auto queue = ep_exception.lock()) { queue->send(SYS_SPU_THREAD_GROUP_EVENT_EXCEPTION_KEY, data1, data2, data3); } } void send_sysmodule_event(u64 data1, u64 data2, u64 data3) { if (const auto queue = ep_sysmodule.lock()) { queue->send(SYS_SPU_THREAD_GROUP_EVENT_SYSTEM_MODULE_KEY, data1, data2, data3); } } static std::pair*, std::shared_ptr> get_thread(u32 id); }; class ppu_thread; // Syscalls error_code sys_spu_initialize(ppu_thread&, u32 max_usable_spu, u32 max_raw_spu); error_code _sys_spu_image_get_information(ppu_thread&, vm::ptr img, vm::ptr entry_point, vm::ptr nsegs); error_code sys_spu_image_open(ppu_thread&, vm::ptr img, vm::cptr path); error_code _sys_spu_image_import(ppu_thread&, vm::ptr img, u32 src, u32 size, u32 arg4); error_code _sys_spu_image_close(ppu_thread&, vm::ptr img); error_code _sys_spu_image_get_segments(ppu_thread&, vm::ptr img, vm::ptr segments, s32 nseg); error_code sys_spu_thread_initialize(ppu_thread&, vm::ptr thread, u32 group, u32 spu_num, vm::ptr, vm::ptr, vm::ptr); error_code sys_spu_thread_set_argument(ppu_thread&, u32 id, vm::ptr arg); error_code sys_spu_thread_group_create(ppu_thread&, vm::ptr id, u32 num, s32 prio, vm::ptr attr); error_code sys_spu_thread_group_destroy(ppu_thread&, u32 id); error_code sys_spu_thread_group_start(ppu_thread&, u32 id); error_code sys_spu_thread_group_suspend(ppu_thread&, u32 id); error_code sys_spu_thread_group_resume(ppu_thread&, u32 id); error_code sys_spu_thread_group_yield(ppu_thread&, u32 id); error_code sys_spu_thread_group_terminate(ppu_thread&, u32 id, s32 value); error_code sys_spu_thread_group_join(ppu_thread&, u32 id, vm::ptr cause, vm::ptr status); error_code sys_spu_thread_group_set_priority(ppu_thread&, u32 id, s32 priority); error_code sys_spu_thread_group_get_priority(ppu_thread&, u32 id, vm::ptr priority); error_code sys_spu_thread_group_connect_event(ppu_thread&, u32 id, u32 eq, u32 et); error_code sys_spu_thread_group_disconnect_event(ppu_thread&, u32 id, u32 et); error_code sys_spu_thread_group_connect_event_all_threads(ppu_thread&, u32 id, u32 eq_id, u64 req, vm::ptr spup); error_code sys_spu_thread_group_disconnect_event_all_threads(ppu_thread&, u32 id, u8 spup); error_code sys_spu_thread_group_set_cooperative_victims(ppu_thread&, u32 id, u32 threads_mask); error_code sys_spu_thread_group_syscall_253(ppu_thread& ppu, u32 id, vm::ptr info); error_code sys_spu_thread_group_log(ppu_thread&, s32 command, vm::ptr stat); error_code sys_spu_thread_write_ls(ppu_thread&, u32 id, u32 address, u64 value, u32 type); error_code sys_spu_thread_read_ls(ppu_thread&, u32 id, u32 address, vm::ptr value, u32 type); error_code sys_spu_thread_write_spu_mb(ppu_thread&, u32 id, u32 value); error_code sys_spu_thread_set_spu_cfg(ppu_thread&, u32 id, u64 value); error_code sys_spu_thread_get_spu_cfg(ppu_thread&, u32 id, vm::ptr value); error_code sys_spu_thread_write_snr(ppu_thread&, u32 id, u32 number, u32 value); error_code sys_spu_thread_connect_event(ppu_thread&, u32 id, u32 eq, u32 et, u8 spup); error_code sys_spu_thread_disconnect_event(ppu_thread&, u32 id, u32 event_type, u8 spup); error_code sys_spu_thread_bind_queue(ppu_thread&, u32 id, u32 spuq, u32 spuq_num); error_code sys_spu_thread_unbind_queue(ppu_thread&, u32 id, u32 spuq_num); error_code sys_spu_thread_get_exit_status(ppu_thread&, u32 id, vm::ptr status); error_code sys_spu_thread_recover_page_fault(ppu_thread&, u32 id); error_code sys_raw_spu_create(ppu_thread&, vm::ptr id, vm::ptr attr); error_code sys_raw_spu_destroy(ppu_thread& ppu, u32 id); error_code sys_raw_spu_create_interrupt_tag(ppu_thread&, u32 id, u32 class_id, u32 hwthread, vm::ptr intrtag); error_code sys_raw_spu_set_int_mask(ppu_thread&, u32 id, u32 class_id, u64 mask); error_code sys_raw_spu_get_int_mask(ppu_thread&, u32 id, u32 class_id, vm::ptr mask); error_code sys_raw_spu_set_int_stat(ppu_thread&, u32 id, u32 class_id, u64 stat); error_code sys_raw_spu_get_int_stat(ppu_thread&, u32 id, u32 class_id, vm::ptr stat); error_code sys_raw_spu_read_puint_mb(ppu_thread&, u32 id, vm::ptr value); error_code sys_raw_spu_set_spu_cfg(ppu_thread&, u32 id, u32 value); error_code sys_raw_spu_get_spu_cfg(ppu_thread&, u32 id, vm::ptr value); error_code sys_raw_spu_recover_page_fault(ppu_thread&, u32 id); error_code sys_isolated_spu_create(ppu_thread&, vm::ptr id, vm::ptr image, u64 arg1, u64 arg2, u64 arg3, u64 arg4); error_code sys_isolated_spu_start(ppu_thread&, u32 id); error_code sys_isolated_spu_destroy(ppu_thread& ppu, u32 id); error_code sys_isolated_spu_create_interrupt_tag(ppu_thread&, u32 id, u32 class_id, u32 hwthread, vm::ptr intrtag); error_code sys_isolated_spu_set_int_mask(ppu_thread&, u32 id, u32 class_id, u64 mask); error_code sys_isolated_spu_get_int_mask(ppu_thread&, u32 id, u32 class_id, vm::ptr mask); error_code sys_isolated_spu_set_int_stat(ppu_thread&, u32 id, u32 class_id, u64 stat); error_code sys_isolated_spu_get_int_stat(ppu_thread&, u32 id, u32 class_id, vm::ptr stat); error_code sys_isolated_spu_read_puint_mb(ppu_thread&, u32 id, vm::ptr value); error_code sys_isolated_spu_set_spu_cfg(ppu_thread&, u32 id, u32 value); error_code sys_isolated_spu_get_spu_cfg(ppu_thread&, u32 id, vm::ptr value);