diff --git a/src/xenia/base/threading_win.cc b/src/xenia/base/threading_win.cc index 139d69c59..e30f16e34 100644 --- a/src/xenia/base/threading_win.cc +++ b/src/xenia/base/threading_win.cc @@ -149,10 +149,11 @@ void MaybeYield() { // MemoryBarrier(); } void NanoSleep(int64_t ns) { -//nanosleep is done in 100 nanosecond increments + // nanosleep is done in 100 nanosecond increments int64_t in_nt_increments = ns / 100LL; if (in_nt_increments == 0 && ns != 0) { - //if we're explicitly requesting a delay of 0 ns, let it go through, otherwise if it was less than a 100ns increment we round up to 100ns + // if we're explicitly requesting a delay of 0 ns, let it go through, + // otherwise if it was less than a 100ns increment we round up to 100ns in_nt_increments = 1; } in_nt_increments = -in_nt_increments; @@ -514,6 +515,10 @@ class Win32Thread : public Win32Handle { ~Win32Thread() = default; void set_name(std::string name) override { + // this can actually happen in some debug builds + if (&name == nullptr) { + return; + } xe::threading::set_name(handle_, name); Thread::set_name(name); } diff --git a/src/xenia/kernel/info/volume.h b/src/xenia/kernel/info/volume.h index bbb6ec3ac..28329ecfb 100644 --- a/src/xenia/kernel/info/volume.h +++ b/src/xenia/kernel/info/volume.h @@ -54,6 +54,16 @@ struct X_FILE_FS_ATTRIBUTE_INFORMATION { }; static_assert_size(X_FILE_FS_ATTRIBUTE_INFORMATION, 16); +enum X_FILE_DEVICE_TYPE : uint32_t { + FILE_DEVICE_UNKNOWN = 0x22 +}; + +struct X_FILE_FS_DEVICE_INFORMATION { + be device_type; + be characteristics; +}; +static_assert_size(X_FILE_FS_DEVICE_INFORMATION, 8); + #pragma pack(pop) } // namespace kernel diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 6835ef6fe..9e6be4764 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -854,10 +854,11 @@ void KernelState::CompleteOverlappedDeferredEx( XOverlappedSetContext(ptr, XThread::GetCurrentThreadHandle()); X_HANDLE event_handle = XOverlappedGetEvent(ptr); if (event_handle) { - auto ev = object_table()->LookupObject(event_handle); + auto ev = object_table()->LookupObject(event_handle); + assert_not_null(ev); - if (ev) { - ev->Reset(); + if (ev && ev->type() == XObject::Type::Event) { + ev.get()->Reset(); } } auto global_lock = global_critical_region_.Acquire(); diff --git a/src/xenia/kernel/xam/xam_info.cc b/src/xenia/kernel/xam/xam_info.cc index 86221e871..6ffaccc2e 100644 --- a/src/xenia/kernel/xam/xam_info.cc +++ b/src/xenia/kernel/xam/xam_info.cc @@ -15,6 +15,7 @@ #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_module.h" #include "xenia/kernel/xam/xam_private.h" +#include "xenia/kernel/xboxkrnl/xboxkrnl_memory.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h" #include "xenia/kernel/xenumerator.h" #include "xenia/kernel/xthread.h" @@ -261,10 +262,8 @@ dword_result_t RtlSleep_entry(dword_t dwMilliseconds, dword_t bAlertable) { ? LLONG_MAX : static_cast(-10000) * dwMilliseconds; - X_STATUS result = xboxkrnl::KeDelayExecutionThread( - MODE::UserMode, - bAlertable, - (uint64_t*)&delay); + X_STATUS result = xboxkrnl::KeDelayExecutionThread(MODE::UserMode, bAlertable, + (uint64_t*)&delay); // If the delay was interrupted by an APC, keep delaying the thread while (bAlertable && result == X_STATUS_ALERTED) { @@ -396,7 +395,8 @@ void XamLoaderTerminateTitle_entry() { } DECLARE_XAM_EXPORT1(XamLoaderTerminateTitle, kNone, kSketchy); -dword_result_t XamAlloc_entry(dword_t flags, dword_t size, lpdword_t out_ptr) { +uint32_t XamAllocImpl(uint32_t flags, uint32_t size, + xe::be* out_ptr) { if (flags & 0x00100000) { // HEAP_ZERO_memory used unless this flag // do nothing! // maybe we ought to fill it with nonzero garbage, but otherwise this is a @@ -412,8 +412,44 @@ dword_result_t XamAlloc_entry(dword_t flags, dword_t size, lpdword_t out_ptr) { return X_ERROR_SUCCESS; } + +dword_result_t XamAlloc_entry(dword_t flags, dword_t size, lpdword_t out_ptr) { + return XamAllocImpl(flags, size, out_ptr); +} DECLARE_XAM_EXPORT1(XamAlloc, kMemory, kImplemented); +static const unsigned short XamPhysicalProtTable[4] = { + X_PAGE_READONLY, + X_PAGE_READWRITE | X_PAGE_NOCACHE, + X_PAGE_READWRITE, + X_PAGE_WRITECOMBINE | X_PAGE_READWRITE +}; + +dword_result_t XamAllocEx_entry(dword_t phys_flags, dword_t flags, dword_t size, + lpdword_t out_ptr, const ppc_context_t& ctx) { + if ((flags & 0x40000000) == 0) { + return XamAllocImpl(flags, size, out_ptr); + } + + uint32_t flags_remapped = phys_flags; + if ((phys_flags & 0xF000000) == 0) { + // setting default alignment + flags_remapped = 0xC000000 | phys_flags & 0xF0FFFFFF; + } + + uint32_t result = xboxkrnl::xeMmAllocatePhysicalMemoryEx( + 2, size, XamPhysicalProtTable[(flags_remapped >> 28) & 0b11], 0, + 0xFFFFFFFF, 1 << ((flags_remapped >> 24) & 0xF)); + + if (result && (flags_remapped & 0x40000000) != 0) { + memset(ctx->TranslateVirtual(result), 0, size); + } + + *out_ptr = result; + return result ? 0 : 0x8007000E; +} +DECLARE_XAM_EXPORT1(XamAllocEx, kMemory, kImplemented); + dword_result_t XamFree_entry(lpdword_t ptr) { kernel_state()->memory()->SystemHeapFree(ptr.guest_address()); @@ -428,6 +464,11 @@ dword_result_t XamQueryLiveHiveW_entry(lpu16string_t name, lpvoid_t out_buf, } DECLARE_XAM_EXPORT1(XamQueryLiveHiveW, kNone, kStub); +dword_result_t XamIsCurrentTitleDash_entry(const ppc_context_t& ctx) { + return ctx->kernel_state->title_id() == 0xFFFE07D1; +} +DECLARE_XAM_EXPORT1(XamIsCurrentTitleDash, kNone, kImplemented); + } // namespace xam } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/xam/xam_locale.cc b/src/xenia/kernel/xam/xam_locale.cc index e45c771ad..b1a512034 100644 --- a/src/xenia/kernel/xam/xam_locale.cc +++ b/src/xenia/kernel/xam/xam_locale.cc @@ -431,6 +431,26 @@ dword_result_t XamGetLocaleEx_entry(dword_t max_country_id, static_cast(max_locale_id)); } DECLARE_XAM_EXPORT1(XamGetLocaleEx, kLocale, kImplemented); +//originally a switch table, wrote a script to extract the values for all possible cases + +static constexpr uint8_t XamLocaleDateFmtTable[] = { + 2, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 1, 4, 2, 3, 1, 2, 2, 3, + 3, 3, 3, 3, 2, 1, 3, 2, 2, 3, 0, 3, 0, 3, 3, 5, 3, 1, 3, 2, 3, 3, + 3, 2, 3, 3, 5, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 3, 3, 0, 2, 1, 3, 3, 3, 3, 3, 5, 3, 2, 3, 3, 3, 2, 3, 5, 0, 3, + 1, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 3, 3, 5, 1, 1, 1, 1}; + +dword_result_t XamGetLocaleDateFormat_entry(dword_t locale) { + uint32_t biased_locale = locale - 5; + int result = 3; + if (biased_locale > 0x68) { + return result; + } else { + return XamLocaleDateFmtTable[biased_locale]; + } +} + +DECLARE_XAM_EXPORT1(XamGetLocaleDateFormat, kLocale, kImplemented); } // namespace xam } // namespace kernel diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc index 1ace11c97..d042a723a 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io_info.cc @@ -92,10 +92,14 @@ dword_result_t NtQueryInformationFile_entry( break; } case XFileSectorInformation: { - // TODO(benvanik): return sector this file's on. - XELOGE("NtQueryInformationFile(XFileSectorInformation) unimplemented"); - status = X_STATUS_INVALID_PARAMETER; - out_length = 0; + // SW that uses this seems to use the output as a way of uniquely + // identifying a file for sorting/lookup so we can just give it an + // arbitrary 4 byte integer most of the time + XELOGW("Stub XFileSectorInformation!"); + auto info = info_ptr.as(); + size_t fname_hash = xe::memory::hash_combine(82589933LL, file->path()); + *info = static_cast(fname_hash ^ (fname_hash >> 32)); + out_length = sizeof(uint32_t); break; } case XFileXctdCompressionInformation: { @@ -338,9 +342,16 @@ dword_result_t NtQueryVolumeInformationFile_entry( } break; } - case XFileFsDeviceInformation: + case XFileFsDeviceInformation: { + auto info = info_ptr.as(); + auto file_device = file->device(); + XELOGW("Stub XFileFsDeviceInformation!"); + info->device_type = FILE_DEVICE_UNKNOWN; // 415608D8 checks for 0x46; + info->characteristics = 0; + out_length = sizeof(X_FILE_FS_DEVICE_INFORMATION); + break; + } default: { - // Unsupported, for now. assert_always(); out_length = 0; break; diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc index d307aac2e..c74fef18b 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.cc @@ -16,7 +16,7 @@ #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" #include "xenia/xbox.h" - +#include "xenia/kernel/xboxkrnl/xboxkrnl_memory.h" DEFINE_bool( ignore_offset_for_ranged_allocations, false, "Allows to ignore 4k offset for physical allocations with provided range. " @@ -379,9 +379,11 @@ dword_result_t NtAllocateEncryptedMemory_entry(dword_t unk, dword_t region_size, } DECLARE_XBOXKRNL_EXPORT1(NtAllocateEncryptedMemory, kMemory, kImplemented); -dword_result_t MmAllocatePhysicalMemoryEx_entry( - dword_t flags, dword_t region_size, dword_t protect_bits, - dword_t min_addr_range, dword_t max_addr_range, dword_t alignment) { +uint32_t xeMmAllocatePhysicalMemoryEx(uint32_t flags, uint32_t region_size, + uint32_t protect_bits, + uint32_t min_addr_range, + uint32_t max_addr_range, + uint32_t alignment) { // Type will usually be 0 (user request?), where 1 and 2 are sometimes made // by D3D/etc. @@ -430,9 +432,9 @@ dword_result_t MmAllocatePhysicalMemoryEx_entry( } uint32_t heap_min_addr = - xe::sat_sub(min_addr_range.value(), heap_physical_address_offset); + xe::sat_sub(min_addr_range, heap_physical_address_offset); uint32_t heap_max_addr = - xe::sat_sub(max_addr_range.value(), heap_physical_address_offset); + xe::sat_sub(max_addr_range, heap_physical_address_offset); uint32_t heap_size = heap->heap_size(); heap_min_addr = heap_base + std::min(heap_min_addr, heap_size - 1); heap_max_addr = heap_base + std::min(heap_max_addr, heap_size - 1); @@ -447,12 +449,20 @@ dword_result_t MmAllocatePhysicalMemoryEx_entry( return base_address; } + +dword_result_t MmAllocatePhysicalMemoryEx_entry( + dword_t flags, dword_t region_size, dword_t protect_bits, + dword_t min_addr_range, dword_t max_addr_range, dword_t alignment) { + return xeMmAllocatePhysicalMemoryEx(flags, region_size, protect_bits, + min_addr_range, max_addr_range, + alignment); +} DECLARE_XBOXKRNL_EXPORT1(MmAllocatePhysicalMemoryEx, kMemory, kImplemented); dword_result_t MmAllocatePhysicalMemory_entry(dword_t flags, dword_t region_size, dword_t protect_bits) { - return MmAllocatePhysicalMemoryEx_entry(flags, region_size, protect_bits, 0, + return xeMmAllocatePhysicalMemoryEx(flags, region_size, protect_bits, 0, 0xFFFFFFFFu, 0); } DECLARE_XBOXKRNL_EXPORT1(MmAllocatePhysicalMemory, kMemory, kImplemented); diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.h b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.h new file mode 100644 index 000000000..ed7905b59 --- /dev/null +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_memory.h @@ -0,0 +1,32 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XBOXKRNL_XBOXKRNL_MEMORY_H_ +#define XENIA_KERNEL_XBOXKRNL_XBOXKRNL_MEMORY_H_ + +#include "xenia/kernel/util/shim_utils.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { + + +namespace xboxkrnl { + +uint32_t xeMmAllocatePhysicalMemoryEx(uint32_t flags, uint32_t region_size, + uint32_t protect_bits, + uint32_t min_addr_range, + uint32_t max_addr_range, + uint32_t alignment); + +} // namespace xboxkrnl +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_XBOXKRNL_XBOXKRNL_MEMORY_H_ diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc index 0e7562c50..b537eccaf 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_rtl.cc @@ -12,6 +12,7 @@ #include #include +#include "third_party/pe/pe_image.h" #include "xenia/base/atomic.h" #include "xenia/base/chrono.h" #include "xenia/base/logging.h" @@ -24,7 +25,6 @@ #include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h" #include "xenia/kernel/xevent.h" #include "xenia/kernel/xthread.h" - namespace xe { namespace kernel { namespace xboxkrnl { @@ -402,14 +402,14 @@ DECLARE_XBOXKRNL_EXPORT3(RtlUnicodeToMultiByteN, kNone, kImplemented, kHighFrequency, kSketchy); // https://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Executable%20Images/RtlImageNtHeader.html -pointer_result_t RtlImageNtHeader_entry(lpvoid_t module) { +static IMAGE_NT_HEADERS32* ImageNtHeader(uint8_t* module) { if (!module) { return 0; } // Little-endian! no swapping! - auto dos_header = module.as(); + auto dos_header = module; auto dos_magic = *reinterpret_cast(&dos_header[0x00]); if (dos_magic != 0x5A4D) { // 'MZ' return 0; @@ -421,9 +421,82 @@ pointer_result_t RtlImageNtHeader_entry(lpvoid_t module) { if (nt_magic != 0x4550) { // 'PE' return 0; } - return kernel_memory()->HostToGuestVirtual(nt_header); + return reinterpret_cast(nt_header); +} + +pointer_result_t RtlImageNtHeader_entry(lpvoid_t module) { + auto result = ImageNtHeader(module.as()); + if (!result) { + return 0; + } + + return kernel_memory()->HostToGuestVirtual(result); } DECLARE_XBOXKRNL_EXPORT1(RtlImageNtHeader, kNone, kImplemented); +// https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-imagedirectoryentrytodata +dword_result_t RtlImageDirectoryEntryToData_entry(dword_t Base, dword_t MappedAsImage_, + word_t DirectoryEntry, dword_t Size, + const ppc_context_t& ctx) { + bool MappedAsImage = static_cast(MappedAsImage_); + uint32_t aligned_base = Base; + if ((Base & 1) != 0) { + aligned_base = Base & 0xFFFFFFFE; + MappedAsImage = false; + } + IMAGE_NT_HEADERS32* nt_header = + ImageNtHeader(ctx->TranslateVirtual(aligned_base)); + + if (!nt_header) { + return 0; + } + if (nt_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + return 0; + } + if (DirectoryEntry >= nt_header->OptionalHeader.NumberOfRvaAndSizes) { + return 0; + } + uint32_t Address = + nt_header->OptionalHeader.DataDirectory[DirectoryEntry].VirtualAddress; + if (!Address) { + return 0; + } + xe::store_and_swap( + ctx->TranslateVirtual(Size), + nt_header->OptionalHeader.DataDirectory[DirectoryEntry].Size); + if (MappedAsImage || Address < nt_header->OptionalHeader.SizeOfHeaders) { + return aligned_base + Address; + } + + uint32_t n_sections = nt_header->FileHeader.NumberOfSections; + IMAGE_SECTION_HEADER* v8 = reinterpret_cast( + reinterpret_cast(&nt_header->OptionalHeader) + + nt_header->FileHeader.SizeOfOptionalHeader); + if (!n_sections) { + return 0; + } + + uint32_t i = 0; + while (true) { + uint32_t section_virtual_address = v8->VirtualAddress; + uint32_t sizeof_section = v8->SizeOfRawData; + if (Address >= section_virtual_address && + Address < sizeof_section + section_virtual_address) { + break; + } + ++i; + ++v8; + if (i >= n_sections) { + return 0; + } + } + + if (v8) { + return aligned_base + Address - v8->VirtualAddress; + } + return 0; +} + +DECLARE_XBOXKRNL_EXPORT1(RtlImageDirectoryEntryToData, kNone, kImplemented); pointer_result_t RtlImageXexHeaderField_entry(pointer_t xex_header, dword_t field_dword) { diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index 104b6bbf5..591848907 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -185,7 +185,7 @@ dword_result_t NtResumeThread_entry(dword_t handle, } DECLARE_XBOXKRNL_EXPORT1(NtResumeThread, kThreading, kImplemented); -dword_result_t KeResumeThread_entry(lpvoid_t thread_ptr) { +dword_result_t KeResumeThread_entry(pointer_t thread_ptr) { X_STATUS result = X_STATUS_SUCCESS; auto thread = XObject::GetNativeObject(kernel_state(), thread_ptr); if (thread) { @@ -230,11 +230,27 @@ dword_result_t NtSuspendThread_entry(dword_t handle, } DECLARE_XBOXKRNL_EXPORT1(NtSuspendThread, kThreading, kImplemented); +dword_result_t KeSuspendThread_entry(pointer_t kthread, + const ppc_context_t& context) { + auto thread = XObject::GetNativeObject(context->kernel_state, kthread); + uint32_t suspend_count_out = 0; + + if (thread) { + suspend_count_out = thread->suspend_count(); + + uint32_t discarded_new_suspend_count = 0; + thread->Suspend(&discarded_new_suspend_count); + } + + return suspend_count_out; +} +DECLARE_XBOXKRNL_EXPORT1(KeSuspendThread, kThreading, kImplemented); + void KeSetCurrentStackPointers_entry(lpvoid_t stack_ptr, pointer_t thread, lpvoid_t stack_alloc_base, - lpvoid_t stack_base, - lpvoid_t stack_limit, const ppc_context_t& context) { + lpvoid_t stack_base, lpvoid_t stack_limit, + const ppc_context_t& context) { auto current_thread = XThread::GetCurrentThread(); auto pcr = context->TranslateVirtualGPR(context->r[13]);