Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
81b1fc2d8d |
12
externals/CMakeLists.txt
vendored
12
externals/CMakeLists.txt
vendored
@@ -41,15 +41,9 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Catch2
|
# Catch2
|
||||||
add_library(catch2 INTERFACE)
|
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
|
||||||
if(USE_SYSTEM_CATCH2)
|
set(CATCH_INSTALL_EXTRAS OFF CACHE BOOL "")
|
||||||
find_package(Catch2 3.0.0 REQUIRED)
|
add_subdirectory(catch2)
|
||||||
else()
|
|
||||||
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
|
|
||||||
set(CATCH_INSTALL_EXTRAS OFF CACHE BOOL "")
|
|
||||||
add_subdirectory(catch2)
|
|
||||||
endif()
|
|
||||||
target_link_libraries(catch2 INTERFACE Catch2::Catch2WithMain)
|
|
||||||
|
|
||||||
# Crypto++
|
# Crypto++
|
||||||
if(USE_SYSTEM_CRYPTOPP)
|
if(USE_SYSTEM_CRYPTOPP)
|
||||||
|
@@ -25,7 +25,6 @@ option(USE_SYSTEM_LODEPNG "Use the system lodepng (instead of the bundled one)"
|
|||||||
option(USE_SYSTEM_OPENAL "Use the system OpenAL (instead of the bundled one)" OFF)
|
option(USE_SYSTEM_OPENAL "Use the system OpenAL (instead of the bundled one)" OFF)
|
||||||
option(USE_SYSTEM_VMA "Use the system VulkanMemoryAllocator (instead of the bundled one)" OFF)
|
option(USE_SYSTEM_VMA "Use the system VulkanMemoryAllocator (instead of the bundled one)" OFF)
|
||||||
option(USE_SYSTEM_VULKAN_HEADERS "Use the system Vulkan headers (instead of the bundled ones)" OFF)
|
option(USE_SYSTEM_VULKAN_HEADERS "Use the system Vulkan headers (instead of the bundled ones)" OFF)
|
||||||
option(USE_SYSTEM_CATCH2 "Use the system Catch2 (instead of the bundled one)" OFF)
|
|
||||||
|
|
||||||
# Qt and MoltenVK are handled separately
|
# Qt and MoltenVK are handled separately
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_SDL2 "Disable system SDL2" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_SDL2 "Disable system SDL2" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
@@ -50,7 +49,6 @@ CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_LODEPNG "Disable system lodepng" OFF "USE_
|
|||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_OPENAL "Disable system OpenAL" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_OPENAL "Disable system OpenAL" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_VMA "Disable system VulkanMemoryAllocator" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_VMA "Disable system VulkanMemoryAllocator" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_VULKAN_HEADERS "Disable system Vulkan headers" OFF "USE_SYSTEM_LIBS" OFF)
|
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_VULKAN_HEADERS "Disable system Vulkan headers" OFF "USE_SYSTEM_LIBS" OFF)
|
||||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_CATCH2 "Disable system Catch2" OFF "USE_SYSTEM_LIBS" OFF)
|
|
||||||
|
|
||||||
set(LIB_VAR_LIST
|
set(LIB_VAR_LIST
|
||||||
SDL2
|
SDL2
|
||||||
@@ -75,7 +73,6 @@ set(LIB_VAR_LIST
|
|||||||
OPENAL
|
OPENAL
|
||||||
VMA
|
VMA
|
||||||
VULKAN_HEADERS
|
VULKAN_HEADERS
|
||||||
CATCH2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# First, check that USE_SYSTEM_XXX is not used with USE_SYSTEM_LIBS
|
# First, check that USE_SYSTEM_XXX is not used with USE_SYSTEM_LIBS
|
||||||
|
2
externals/cmake-modules/FindOpenAL.cmake
vendored
2
externals/cmake-modules/FindOpenAL.cmake
vendored
@@ -1,5 +1,5 @@
|
|||||||
if(NOT OPENAL_FOUND)
|
if(NOT OPENAL_FOUND)
|
||||||
pkg_check_modules(OPENAL_TMP openal)
|
pkg_check_modules(OOPENAL_TMP libopanal)
|
||||||
|
|
||||||
find_path(OPENAL_INCLUDE_DIRS NAMES al.h
|
find_path(OPENAL_INCLUDE_DIRS NAMES al.h
|
||||||
PATHS
|
PATHS
|
||||||
|
@@ -76,13 +76,13 @@ const std::array<UISettings::Shortcut, 28> Config::default_hotkeys {{
|
|||||||
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
{QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
{QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Per-Game Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
{QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
|
||||||
{QStringLiteral("Toggle Per-Game Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
|
||||||
{QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}},
|
{QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
|
{QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
|
||||||
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
{QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||||
|
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}},
|
||||||
}};
|
}};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@@ -24,7 +24,6 @@
|
|||||||
// windows.h needs to be included before other windows headers
|
// windows.h needs to be included before other windows headers
|
||||||
#include <direct.h> // getcwd
|
#include <direct.h> // getcwd
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <share.h>
|
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <shlobj.h> // for SHGetFolderPath
|
#include <shlobj.h> // for SHGetFolderPath
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
@@ -1059,13 +1058,14 @@ bool IOFile::Open() {
|
|||||||
Close();
|
Close();
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (flags == 0) {
|
if (flags != 0) {
|
||||||
flags = _SH_DENYNO;
|
|
||||||
}
|
|
||||||
m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
|
m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
|
||||||
Common::UTF8ToUTF16W(openmode).c_str(), flags);
|
Common::UTF8ToUTF16W(openmode).c_str(), flags);
|
||||||
m_good = m_file != nullptr;
|
m_good = m_file != nullptr;
|
||||||
|
} else {
|
||||||
|
m_good = _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
|
||||||
|
Common::UTF8ToUTF16W(openmode).c_str()) == 0;
|
||||||
|
}
|
||||||
#elif ANDROID
|
#elif ANDROID
|
||||||
// Check whether filepath is startsWith content
|
// Check whether filepath is startsWith content
|
||||||
AndroidStorage::AndroidOpenMode android_open_mode = AndroidStorage::ParseOpenmode(openmode);
|
AndroidStorage::AndroidOpenMode android_open_mode = AndroidStorage::ParseOpenmode(openmode);
|
||||||
|
@@ -455,6 +455,7 @@ add_library(citra_core STATIC
|
|||||||
loader/smdh.h
|
loader/smdh.h
|
||||||
memory.cpp
|
memory.cpp
|
||||||
memory.h
|
memory.h
|
||||||
|
mmio.h
|
||||||
movie.cpp
|
movie.cpp
|
||||||
movie.h
|
movie.h
|
||||||
nus_download.cpp
|
nus_download.cpp
|
||||||
|
@@ -50,9 +50,6 @@ public:
|
|||||||
|
|
||||||
Loader::ResultStatus Load(std::span<const u8> file_data, std::size_t offset = 0);
|
Loader::ResultStatus Load(std::span<const u8> file_data, std::size_t offset = 0);
|
||||||
std::optional<std::array<u8, 16>> GetTitleKey() const;
|
std::optional<std::array<u8, 16>> GetTitleKey() const;
|
||||||
u64 GetTitleID() const {
|
|
||||||
return ticket_body.title_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Body ticket_body;
|
Body ticket_body;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#include "core/hle/kernel/vm_manager.h"
|
#include "core/hle/kernel/vm_manager.h"
|
||||||
#include "core/hle/service/plgldr/plgldr.h"
|
#include "core/hle/service/plgldr/plgldr.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
#include "core/mmio.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
@@ -32,6 +33,9 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
|||||||
backing_memory.GetPtr() + size != next.backing_memory.GetPtr()) {
|
backing_memory.GetPtr() + size != next.backing_memory.GetPtr()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (type == VMAType::MMIO && paddr + size != next.paddr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +118,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, Memory
|
|||||||
return MergeAdjacent(vma_handle);
|
return MergeAdjacent(vma_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u32 size,
|
||||||
|
MemoryState state,
|
||||||
|
Memory::MMIORegionPointer mmio_handler) {
|
||||||
|
ASSERT(!is_locked);
|
||||||
|
|
||||||
|
// This is the appropriately sized VMA that will turn into our allocation.
|
||||||
|
CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size));
|
||||||
|
VirtualMemoryArea& final_vma = vma_handle->second;
|
||||||
|
ASSERT(final_vma.size == size);
|
||||||
|
|
||||||
|
final_vma.type = VMAType::MMIO;
|
||||||
|
final_vma.permissions = VMAPermission::ReadWrite;
|
||||||
|
final_vma.meminfo_state = state;
|
||||||
|
final_vma.paddr = paddr;
|
||||||
|
final_vma.mmio_handler = mmio_handler;
|
||||||
|
UpdatePageTableForVMA(final_vma);
|
||||||
|
|
||||||
|
return MergeAdjacent(vma_handle);
|
||||||
|
}
|
||||||
|
|
||||||
ResultCode VMManager::ChangeMemoryState(VAddr target, u32 size, MemoryState expected_state,
|
ResultCode VMManager::ChangeMemoryState(VAddr target, u32 size, MemoryState expected_state,
|
||||||
VMAPermission expected_perms, MemoryState new_state,
|
VMAPermission expected_perms, MemoryState new_state,
|
||||||
VMAPermission new_perms) {
|
VMAPermission new_perms) {
|
||||||
@@ -161,7 +185,9 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
|
|||||||
vma.type = VMAType::Free;
|
vma.type = VMAType::Free;
|
||||||
vma.permissions = VMAPermission::None;
|
vma.permissions = VMAPermission::None;
|
||||||
vma.meminfo_state = MemoryState::Free;
|
vma.meminfo_state = MemoryState::Free;
|
||||||
|
|
||||||
vma.backing_memory = nullptr;
|
vma.backing_memory = nullptr;
|
||||||
|
vma.paddr = 0;
|
||||||
|
|
||||||
UpdatePageTableForVMA(vma);
|
UpdatePageTableForVMA(vma);
|
||||||
|
|
||||||
@@ -318,6 +344,9 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) {
|
|||||||
case VMAType::BackingMemory:
|
case VMAType::BackingMemory:
|
||||||
new_vma.backing_memory += offset_in_vma;
|
new_vma.backing_memory += offset_in_vma;
|
||||||
break;
|
break;
|
||||||
|
case VMAType::MMIO:
|
||||||
|
new_vma.paddr += offset_in_vma;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(old_vma.CanBeMergedWith(new_vma));
|
ASSERT(old_vma.CanBeMergedWith(new_vma));
|
||||||
@@ -352,6 +381,9 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
|||||||
case VMAType::BackingMemory:
|
case VMAType::BackingMemory:
|
||||||
memory.MapMemoryRegion(*page_table, vma.base, vma.size, vma.backing_memory);
|
memory.MapMemoryRegion(*page_table, vma.base, vma.size, vma.backing_memory);
|
||||||
break;
|
break;
|
||||||
|
case VMAType::MMIO:
|
||||||
|
memory.MapIoRegion(*page_table, vma.base, vma.size, vma.mmio_handler);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto plgldr = Service::PLGLDR::GetService(Core::System::GetInstance());
|
auto plgldr = Service::PLGLDR::GetService(Core::System::GetInstance());
|
||||||
|
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
#include <boost/serialization/map.hpp>
|
#include <boost/serialization/map.hpp>
|
||||||
#include <boost/serialization/shared_ptr.hpp>
|
#include <boost/serialization/shared_ptr.hpp>
|
||||||
#include <boost/serialization/split_member.hpp>
|
#include <boost/serialization/split_member.hpp>
|
||||||
@@ -14,6 +16,7 @@
|
|||||||
#include "core/hle/kernel/memory.h"
|
#include "core/hle/kernel/memory.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
#include "core/mmio.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
@@ -22,6 +25,8 @@ enum class VMAType : u8 {
|
|||||||
Free,
|
Free,
|
||||||
/// VMA is backed by a raw, unmanaged pointer.
|
/// VMA is backed by a raw, unmanaged pointer.
|
||||||
BackingMemory,
|
BackingMemory,
|
||||||
|
/// VMA is mapped to MMIO registers at a fixed PAddr.
|
||||||
|
MMIO,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Permissions for mapped memory blocks
|
/// Permissions for mapped memory blocks
|
||||||
@@ -69,10 +74,15 @@ struct VirtualMemoryArea {
|
|||||||
/// Tag returned by svcQueryMemory. Not otherwise used.
|
/// Tag returned by svcQueryMemory. Not otherwise used.
|
||||||
MemoryState meminfo_state = MemoryState::Free;
|
MemoryState meminfo_state = MemoryState::Free;
|
||||||
|
|
||||||
/// Settings for type = BackingMemory
|
// Settings for type = BackingMemory
|
||||||
/// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
|
/// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed.
|
||||||
MemoryRef backing_memory{};
|
MemoryRef backing_memory{};
|
||||||
|
|
||||||
|
// Settings for type = MMIO
|
||||||
|
/// Physical address of the register area this VMA maps to.
|
||||||
|
PAddr paddr = 0;
|
||||||
|
Memory::MMIORegionPointer mmio_handler = nullptr;
|
||||||
|
|
||||||
/// Tests if this area can be merged to the right with `next`.
|
/// Tests if this area can be merged to the right with `next`.
|
||||||
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
|
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
|
||||||
|
|
||||||
@@ -86,6 +96,8 @@ private:
|
|||||||
ar& permissions;
|
ar& permissions;
|
||||||
ar& meminfo_state;
|
ar& meminfo_state;
|
||||||
ar& backing_memory;
|
ar& backing_memory;
|
||||||
|
ar& paddr;
|
||||||
|
ar& mmio_handler;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -154,6 +166,18 @@ public:
|
|||||||
ResultVal<VMAHandle> MapBackingMemory(VAddr target, MemoryRef memory, u32 size,
|
ResultVal<VMAHandle> MapBackingMemory(VAddr target, MemoryRef memory, u32 size,
|
||||||
MemoryState state);
|
MemoryState state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a memory-mapped IO region at a given address.
|
||||||
|
*
|
||||||
|
* @param target The guest address to start the mapping at.
|
||||||
|
* @param paddr The physical address where the registers are present.
|
||||||
|
* @param size Size of the mapping.
|
||||||
|
* @param state MemoryState tag to attach to the VMA.
|
||||||
|
* @param mmio_handler The handler that will implement read and write for this MMIO region.
|
||||||
|
*/
|
||||||
|
ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state,
|
||||||
|
Memory::MMIORegionPointer mmio_handler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the memory state and permissions of the specified range. The range's original memory
|
* Updates the memory state and permissions of the specified range. The range's original memory
|
||||||
* state and permissions must match the `expected` parameters.
|
* state and permissions must match the `expected` parameters.
|
||||||
|
@@ -374,45 +374,6 @@ bool CIAFile::Close() const {
|
|||||||
|
|
||||||
void CIAFile::Flush() const {}
|
void CIAFile::Flush() const {}
|
||||||
|
|
||||||
TicketFile::TicketFile() {}
|
|
||||||
|
|
||||||
TicketFile::~TicketFile() {
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultVal<std::size_t> TicketFile::Read(u64 offset, std::size_t length, u8* buffer) const {
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultVal<std::size_t> TicketFile::Write(u64 offset, std::size_t length, bool flush,
|
|
||||||
const u8* buffer) {
|
|
||||||
written += length;
|
|
||||||
data.resize(written);
|
|
||||||
std::memcpy(data.data() + offset, buffer, length);
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 TicketFile::GetSize() const {
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TicketFile::SetSize(u64 size) const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TicketFile::Close() const {
|
|
||||||
FileSys::Ticket ticket;
|
|
||||||
if (ticket.Load(data, 0) == Loader::ResultStatus::Success) {
|
|
||||||
LOG_WARNING(Service_AM, "Discarding ticket for {:#016X}.", ticket.GetTitleID());
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Service_AM, "Invalid ticket provided to TicketFile.");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TicketFile::Flush() const {}
|
|
||||||
|
|
||||||
InstallStatus InstallCIA(const std::string& path,
|
InstallStatus InstallCIA(const std::string& path,
|
||||||
std::function<ProgressCallback>&& update_callback) {
|
std::function<ProgressCallback>&& update_callback) {
|
||||||
LOG_INFO(Service_AM, "Installing {}...", path);
|
LOG_INFO(Service_AM, "Installing {}...", path);
|
||||||
@@ -981,10 +942,6 @@ void Module::Interface::GetProgramInfos(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.PushMappedBuffer(title_info_out);
|
rb.PushMappedBuffer(title_info_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx) {
|
|
||||||
GetProgramInfos(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Module::Interface::DeleteUserProgram(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::DeleteUserProgram(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
auto media_type = rp.PopEnum<FS::MediaType>();
|
auto media_type = rp.PopEnum<FS::MediaType>();
|
||||||
@@ -1220,16 +1177,6 @@ void Module::Interface::NeedsCleanup(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.Push<bool>(false);
|
rb.Push<bool>(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::DoCleanup(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
const auto media_type = rp.Pop<u8>();
|
|
||||||
|
|
||||||
LOG_DEBUG(Service_AM, "(STUBBED) called, media_type={:#02x}", media_type);
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Module::Interface::QueryAvailableTitleDatabase(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::QueryAvailableTitleDatabase(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
u8 media_type = rp.Pop<u8>();
|
u8 media_type = rp.Pop<u8>();
|
||||||
@@ -1241,45 +1188,6 @@ void Module::Interface::QueryAvailableTitleDatabase(Kernel::HLERequestContext& c
|
|||||||
LOG_WARNING(Service_AM, "(STUBBED) media_type={}", media_type);
|
LOG_WARNING(Service_AM, "(STUBBED) media_type={}", media_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::GetPersonalizedTicketInfoList(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
[[maybe_unused]] u32 ticket_count = rp.Pop<u32>();
|
|
||||||
[[maybe_unused]] auto& buffer = rp.PopMappedBuffer();
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
|
||||||
rb.Push(0);
|
|
||||||
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called, ticket_count={}", ticket_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Module::Interface::GetNumImportTitleContextsFiltered(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
u8 media_type = rp.Pop<u8>();
|
|
||||||
u8 filter = rp.Pop<u8>();
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
|
||||||
rb.Push(0);
|
|
||||||
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called, media_type={}, filter={}", media_type, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Module::Interface::GetImportTitleContextListFiltered(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
[[maybe_unused]] const u32 count = rp.Pop<u32>();
|
|
||||||
const u8 media_type = rp.Pop<u8>();
|
|
||||||
const u8 filter = rp.Pop<u8>();
|
|
||||||
auto& buffer = rp.PopMappedBuffer();
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
|
||||||
rb.Push(0);
|
|
||||||
rb.PushMappedBuffer(buffer);
|
|
||||||
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called, media_type={}, filter={}", media_type, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) {
|
void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
u64 tid = rp.Pop<u64>();
|
u64 tid = rp.Pop<u64>();
|
||||||
@@ -1766,30 +1674,6 @@ void Module::Interface::GetMetaDataFromCia(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.PushMappedBuffer(output_buffer);
|
rb.PushMappedBuffer(output_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::Interface::BeginImportTicket(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
|
|
||||||
// Create our TicketFile handle for the app to write to
|
|
||||||
auto file = std::make_shared<Service::FS::File>(am->kernel, std::make_unique<TicketFile>(),
|
|
||||||
FileSys::Path{});
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
|
||||||
rb.Push(RESULT_SUCCESS); // No error
|
|
||||||
rb.PushCopyObjects(file->Connect());
|
|
||||||
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Module::Interface::EndImportTicket(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
[[maybe_unused]] const auto ticket = rp.PopObject<Kernel::ClientSession>();
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
|
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
|
||||||
}
|
|
||||||
|
|
||||||
Module::Module(Core::System& system) : kernel(system.Kernel()) {
|
Module::Module(Core::System& system) : kernel(system.Kernel()) {
|
||||||
ScanForAllTitles();
|
ScanForAllTitles();
|
||||||
system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex");
|
system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex");
|
||||||
|
@@ -109,25 +109,6 @@ private:
|
|||||||
std::unique_ptr<DecryptionState> decryption_state;
|
std::unique_ptr<DecryptionState> decryption_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A file handled returned for Tickets to be written into and subsequently installed.
|
|
||||||
class TicketFile final : public FileSys::FileBackend {
|
|
||||||
public:
|
|
||||||
explicit TicketFile();
|
|
||||||
~TicketFile();
|
|
||||||
|
|
||||||
ResultVal<std::size_t> Read(u64 offset, std::size_t length, u8* buffer) const override;
|
|
||||||
ResultVal<std::size_t> Write(u64 offset, std::size_t length, bool flush,
|
|
||||||
const u8* buffer) override;
|
|
||||||
u64 GetSize() const override;
|
|
||||||
bool SetSize(u64 size) const override;
|
|
||||||
bool Close() const override;
|
|
||||||
void Flush() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
u64 written = 0;
|
|
||||||
std::vector<u8> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs a CIA file from a specified file path.
|
* Installs a CIA file from a specified file path.
|
||||||
* @param path file path of the CIA file to install
|
* @param path file path of the CIA file to install
|
||||||
@@ -291,18 +272,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void GetProgramInfos(Kernel::HLERequestContext& ctx);
|
void GetProgramInfos(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
|
||||||
* AM::GetProgramInfosIgnorePlatform service function
|
|
||||||
* Inputs:
|
|
||||||
* 1 : u8 Mediatype
|
|
||||||
* 2 : Total titles
|
|
||||||
* 4 : TitleIDList pointer
|
|
||||||
* 6 : TitleList pointer
|
|
||||||
* Outputs:
|
|
||||||
* 1 : Result, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
void GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::DeleteUserProgram service function
|
* AM::DeleteUserProgram service function
|
||||||
* Deletes a user program
|
* Deletes a user program
|
||||||
@@ -420,15 +389,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void NeedsCleanup(Kernel::HLERequestContext& ctx);
|
void NeedsCleanup(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
|
||||||
* AM::DoCleanup service function
|
|
||||||
* Inputs:
|
|
||||||
* 1 : Media Type
|
|
||||||
* Outputs:
|
|
||||||
* 1 : Result, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
void DoCleanup(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::QueryAvailableTitleDatabase service function
|
* AM::QueryAvailableTitleDatabase service function
|
||||||
* Inputs:
|
* Inputs:
|
||||||
@@ -439,42 +399,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void QueryAvailableTitleDatabase(Kernel::HLERequestContext& ctx);
|
void QueryAvailableTitleDatabase(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
|
||||||
* AM::GetPersonalizedTicketInfoList service function
|
|
||||||
* Inputs:
|
|
||||||
* 1 : Count
|
|
||||||
* 2-3 : Buffer
|
|
||||||
* Outputs:
|
|
||||||
* 1 : Result, 0 on success, otherwise error code
|
|
||||||
* 2 : Out count
|
|
||||||
*/
|
|
||||||
void GetPersonalizedTicketInfoList(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AM::GetNumImportTitleContextsFiltered service function
|
|
||||||
* Inputs:
|
|
||||||
* 1 : Count
|
|
||||||
* 2 : Filter
|
|
||||||
* Outputs:
|
|
||||||
* 1 : Result, 0 on success, otherwise error code
|
|
||||||
* 2 : Num import titles
|
|
||||||
*/
|
|
||||||
void GetNumImportTitleContextsFiltered(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AM::GetImportTitleContextListFiltered service function
|
|
||||||
* Inputs:
|
|
||||||
* 1 : Count
|
|
||||||
* 2 : Media type
|
|
||||||
* 3 : filter
|
|
||||||
* 4-5 : Buffer
|
|
||||||
* Outputs:
|
|
||||||
* 1 : Result, 0 on success, otherwise error code
|
|
||||||
* 2 : Out count
|
|
||||||
* 3-4 : Out buffer
|
|
||||||
*/
|
|
||||||
void GetImportTitleContextListFiltered(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AM::CheckContentRights service function
|
* AM::CheckContentRights service function
|
||||||
* Inputs:
|
* Inputs:
|
||||||
@@ -678,25 +602,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void GetMetaDataFromCia(Kernel::HLERequestContext& ctx);
|
void GetMetaDataFromCia(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
/**
|
|
||||||
* AM::BeginImportTicket service function
|
|
||||||
* Inputs:
|
|
||||||
* 1 : Media type to install title to
|
|
||||||
* Outputs:
|
|
||||||
* 1 : Result, 0 on success, otherwise error code
|
|
||||||
* 2-3 : TicketHandle handle for application to write to
|
|
||||||
*/
|
|
||||||
void BeginImportTicket(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AM::EndImportTicket service function
|
|
||||||
* Inputs:
|
|
||||||
* 1-2 : TicketHandle handle application wrote to
|
|
||||||
* Outputs:
|
|
||||||
* 1 : Result, 0 on success, otherwise error code
|
|
||||||
*/
|
|
||||||
void EndImportTicket(Kernel::HLERequestContext& ctx);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<Module> am;
|
std::shared_ptr<Module> am;
|
||||||
};
|
};
|
||||||
|
@@ -42,19 +42,19 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x001E, nullptr, "ReadTwlBackupInfo"},
|
{0x001E, nullptr, "ReadTwlBackupInfo"},
|
||||||
{0x001F, nullptr, "DeleteAllExpiredUserPrograms"},
|
{0x001F, nullptr, "DeleteAllExpiredUserPrograms"},
|
||||||
{0x0020, nullptr, "GetTwlArchiveResourceInfo"},
|
{0x0020, nullptr, "GetTwlArchiveResourceInfo"},
|
||||||
{0x0021, &AM_NET::GetPersonalizedTicketInfoList, "GetPersonalizedTicketInfoList"},
|
{0x0021, nullptr, "GetPersonalizedTicketInfoList"},
|
||||||
{0x0022, nullptr, "DeleteAllImportContextsFiltered"},
|
{0x0022, nullptr, "DeleteAllImportContextsFiltered"},
|
||||||
{0x0023, &AM_NET::GetNumImportTitleContextsFiltered, "GetNumImportTitleContextsFiltered"},
|
{0x0023, nullptr, "GetNumImportTitleContextsFiltered"},
|
||||||
{0x0024, &AM_NET::GetImportTitleContextListFiltered, "GetImportTitleContextListFiltered"},
|
{0x0024, nullptr, "GetImportTitleContextListFiltered"},
|
||||||
{0x0025, &AM_NET::CheckContentRights, "CheckContentRights"},
|
{0x0025, nullptr, "CheckContentRights"},
|
||||||
{0x0026, nullptr, "GetTicketLimitInfos"},
|
{0x0026, nullptr, "GetTicketLimitInfos"},
|
||||||
{0x0027, nullptr, "GetDemoLaunchInfos"},
|
{0x0027, nullptr, "GetDemoLaunchInfos"},
|
||||||
{0x0028, nullptr, "ReadTwlBackupInfoEx"},
|
{0x0028, nullptr, "ReadTwlBackupInfoEx"},
|
||||||
{0x0029, nullptr, "DeleteUserProgramsAtomically"},
|
{0x0029, nullptr, "DeleteUserProgramsAtomically"},
|
||||||
{0x002A, nullptr, "GetNumExistingContentInfosSystem"},
|
{0x002A, nullptr, "GetNumExistingContentInfosSystem"},
|
||||||
{0x002B, nullptr, "ListExistingContentInfosSystem"},
|
{0x002B, nullptr, "ListExistingContentInfosSystem"},
|
||||||
{0x002C, &AM_NET::GetProgramInfosIgnorePlatform, "GetProgramInfosIgnorePlatform"},
|
{0x002C, nullptr, "GetProgramInfosIgnorePlatform"},
|
||||||
{0x002D, &AM_NET::CheckContentRightsIgnorePlatform, "CheckContentRightsIgnorePlatform"},
|
{0x002D, nullptr, "CheckContentRightsIgnorePlatform"},
|
||||||
{0x0401, nullptr, "UpdateFirmwareTo"},
|
{0x0401, nullptr, "UpdateFirmwareTo"},
|
||||||
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
|
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
|
||||||
{0x0403, nullptr, "BeginImportProgramTemporarily"},
|
{0x0403, nullptr, "BeginImportProgramTemporarily"},
|
||||||
@@ -72,7 +72,7 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x040F, nullptr, "UpdateFirmwareAuto"},
|
{0x040F, nullptr, "UpdateFirmwareAuto"},
|
||||||
{0x0410, &AM_NET::DeleteProgram, "DeleteProgram"},
|
{0x0410, &AM_NET::DeleteProgram, "DeleteProgram"},
|
||||||
{0x0411, nullptr, "GetTwlProgramListForReboot"},
|
{0x0411, nullptr, "GetTwlProgramListForReboot"},
|
||||||
{0x0412, &AM_NET::GetSystemUpdaterMutex, "GetSystemUpdaterMutex"},
|
{0x0412, nullptr, "GetSystemUpdaterMutex"},
|
||||||
{0x0413, &AM_NET::GetMetaSizeFromCia, "GetMetaSizeFromCia"},
|
{0x0413, &AM_NET::GetMetaSizeFromCia, "GetMetaSizeFromCia"},
|
||||||
{0x0414, &AM_NET::GetMetaDataFromCia, "GetMetaDataFromCia"},
|
{0x0414, &AM_NET::GetMetaDataFromCia, "GetMetaDataFromCia"},
|
||||||
{0x0415, nullptr, "CheckDemoLaunchRights"},
|
{0x0415, nullptr, "CheckDemoLaunchRights"},
|
||||||
@@ -80,9 +80,9 @@ AM_NET::AM_NET(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x0417, nullptr, "PerpetuateAgbSaveData"},
|
{0x0417, nullptr, "PerpetuateAgbSaveData"},
|
||||||
{0x0418, nullptr, "BeginImportProgramForOverWrite"},
|
{0x0418, nullptr, "BeginImportProgramForOverWrite"},
|
||||||
{0x0419, nullptr, "BeginImportSystemProgram"},
|
{0x0419, nullptr, "BeginImportSystemProgram"},
|
||||||
{0x0801, &AM_NET::BeginImportTicket, "BeginImportTicket"},
|
{0x0801, nullptr, "BeginImportTicket"},
|
||||||
{0x0802, nullptr, "CancelImportTicket"},
|
{0x0802, nullptr, "CancelImportTicket"},
|
||||||
{0x0803, &AM_NET::EndImportTicket, "EndImportTicket"},
|
{0x0803, nullptr, "EndImportTicket"},
|
||||||
{0x0804, nullptr, "BeginImportTitle"},
|
{0x0804, nullptr, "BeginImportTitle"},
|
||||||
{0x0805, nullptr, "StopImportTitle"},
|
{0x0805, nullptr, "StopImportTitle"},
|
||||||
{0x0806, nullptr, "ResumeImportTitle"},
|
{0x0806, nullptr, "ResumeImportTitle"},
|
||||||
|
@@ -29,7 +29,7 @@ AM_SYS::AM_SYS(std::shared_ptr<Module> am) : Module::Interface(std::move(am), "a
|
|||||||
{0x0011, nullptr, "GetImportContentContexts"},
|
{0x0011, nullptr, "GetImportContentContexts"},
|
||||||
{0x0012, nullptr, "DeleteImportContentContexts"},
|
{0x0012, nullptr, "DeleteImportContentContexts"},
|
||||||
{0x0013, &AM_SYS::NeedsCleanup, "NeedsCleanup"},
|
{0x0013, &AM_SYS::NeedsCleanup, "NeedsCleanup"},
|
||||||
{0x0014, &AM_SYS::DoCleanup, "DoCleanup"},
|
{0x0014, nullptr, "DoCleanup"},
|
||||||
{0x0015, nullptr, "DeleteAllImportContexts"},
|
{0x0015, nullptr, "DeleteAllImportContexts"},
|
||||||
{0x0016, nullptr, "DeleteAllTemporaryPrograms"},
|
{0x0016, nullptr, "DeleteAllTemporaryPrograms"},
|
||||||
{0x0017, nullptr, "ImportTwlBackupLegacy"},
|
{0x0017, nullptr, "ImportTwlBackupLegacy"},
|
||||||
|
@@ -875,7 +875,7 @@ void FS_USER::GetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
|||||||
// TODO: Implement Secure Value Lookup & Generation
|
// TODO: Implement Secure Value Lookup & Generation
|
||||||
|
|
||||||
rb.Push<bool>(false); // indicates that the secure value doesn't exist
|
rb.Push<bool>(false); // indicates that the secure value doesn't exist
|
||||||
rb.Push<bool>(true); // seems to override checking the validity so should be true
|
rb.Push<bool>(false); // looks like a boolean value, purpose unknown
|
||||||
rb.Push<u64>(0); // the secure value
|
rb.Push<u64>(0); // the secure value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -913,7 +913,7 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
|||||||
// TODO: Implement Secure Value Lookup & Generation
|
// TODO: Implement Secure Value Lookup & Generation
|
||||||
|
|
||||||
rb.Push<bool>(false); // indicates that the secure value doesn't exist
|
rb.Push<bool>(false); // indicates that the secure value doesn't exist
|
||||||
rb.Push<bool>(true); // seems to override checking the validity so should be true
|
rb.Push<bool>(false); // looks like a boolean value, purpose unknown
|
||||||
rb.Push<u64>(0); // the secure value
|
rb.Push<u64>(0); // the secure value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -298,7 +298,7 @@ struct MIC_U::Impl {
|
|||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push<u8>(state.gain);
|
rb.Push<u8>(state.gain);
|
||||||
LOG_TRACE(Service_MIC, "gain={}", state.gain);
|
LOG_TRACE(Service_MIC, "gain={}", gain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetPower(Kernel::HLERequestContext& ctx) {
|
void SetPower(Kernel::HLERequestContext& ctx) {
|
||||||
|
@@ -9,7 +9,6 @@
|
|||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/service/ps/ps_ps.h"
|
#include "core/hle/service/ps/ps_ps.h"
|
||||||
#include "core/hle/service/ssl/ssl_c.h"
|
|
||||||
#include "core/hw/aes/arithmetic128.h"
|
#include "core/hw/aes/arithmetic128.h"
|
||||||
#include "core/hw/aes/key.h"
|
#include "core/hw/aes/key.h"
|
||||||
|
|
||||||
@@ -147,20 +146,6 @@ void PS_PS::EncryptDecryptAes(Kernel::HLERequestContext& ctx) {
|
|||||||
rb.PushMappedBuffer(destination);
|
rb.PushMappedBuffer(destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PS_PS::GenerateRandomBytes(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
const u32 size = rp.Pop<u32>();
|
|
||||||
auto buffer = rp.PopMappedBuffer();
|
|
||||||
|
|
||||||
std::vector<u8> out_data(size);
|
|
||||||
SSL::GenerateRandomData(out_data);
|
|
||||||
buffer.Write(out_data.data(), 0, size);
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
rb.PushMappedBuffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
PS_PS::PS_PS() : ServiceFramework("ps:ps", DefaultMaxSessions) {
|
PS_PS::PS_PS() : ServiceFramework("ps:ps", DefaultMaxSessions) {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -175,7 +160,7 @@ PS_PS::PS_PS() : ServiceFramework("ps:ps", DefaultMaxSessions) {
|
|||||||
{0x000A, nullptr, "GetLocalFriendCodeSeed"},
|
{0x000A, nullptr, "GetLocalFriendCodeSeed"},
|
||||||
{0x000B, nullptr, "GetDeviceId"},
|
{0x000B, nullptr, "GetDeviceId"},
|
||||||
{0x000C, nullptr, "SeedRNG"},
|
{0x000C, nullptr, "SeedRNG"},
|
||||||
{0x000D, &PS_PS::GenerateRandomBytes, "GenerateRandomBytes"},
|
{0x000D, nullptr, "GenerateRandomBytes"},
|
||||||
{0x000E, nullptr, "InterfaceForPXI_0x04010084"},
|
{0x000E, nullptr, "InterfaceForPXI_0x04010084"},
|
||||||
{0x000F, nullptr, "InterfaceForPXI_0x04020082"},
|
{0x000F, nullptr, "InterfaceForPXI_0x04020082"},
|
||||||
{0x0010, nullptr, "InterfaceForPXI_0x04030044"},
|
{0x0010, nullptr, "InterfaceForPXI_0x04030044"},
|
||||||
|
@@ -10,24 +10,12 @@ SERIALIZE_EXPORT_IMPL(Service::QTM::QTM_S)
|
|||||||
|
|
||||||
namespace Service::QTM {
|
namespace Service::QTM {
|
||||||
|
|
||||||
void QTM_S::GetHeadtrackingInfo(Kernel::HLERequestContext& ctx) {
|
|
||||||
IPC::RequestParser rp(ctx);
|
|
||||||
[[maybe_unused]] const u64 unknown = rp.Pop<u64>();
|
|
||||||
|
|
||||||
std::array<u8, 0x40> data{};
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(17, 0);
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
rb.PushRaw<std::array<u8, 0x40>>(data);
|
|
||||||
|
|
||||||
LOG_DEBUG(Service, "(STUBBED) called");
|
|
||||||
}
|
|
||||||
|
|
||||||
QTM_S::QTM_S() : ServiceFramework("qtm:s", 2) {
|
QTM_S::QTM_S() : ServiceFramework("qtm:s", 2) {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
// qtm common commands
|
// qtm common commands
|
||||||
// clang-format off
|
// clang-format off
|
||||||
{0x0001, nullptr, "GetHeadtrackingInfoRaw"},
|
{0x0001, nullptr, "GetHeadtrackingInfoRaw"},
|
||||||
{0x0002, &QTM_S::GetHeadtrackingInfo, "GetHeadtrackingInfo"},
|
{0x0002, nullptr, "GetHeadtrackingInfo"},
|
||||||
// clang-format on
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -15,8 +15,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
SERVICE_SERIALIZATION_SIMPLE
|
SERVICE_SERIALIZATION_SIMPLE
|
||||||
|
|
||||||
void GetHeadtrackingInfo(Kernel::HLERequestContext& ctx);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::QTM
|
} // namespace Service::QTM
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include "common/archives.h"
|
#include "common/archives.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
@@ -17,6 +16,10 @@ void SSL_C::Initialize(Kernel::HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
rp.PopPID();
|
rp.PopPID();
|
||||||
|
|
||||||
|
// Seed random number generator when the SSL service is initialized
|
||||||
|
std::random_device rand_device;
|
||||||
|
rand_gen.seed(rand_device());
|
||||||
|
|
||||||
// Stub, return success
|
// Stub, return success
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
@@ -24,13 +27,33 @@ void SSL_C::Initialize(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void SSL_C::GenerateRandomData(Kernel::HLERequestContext& ctx) {
|
void SSL_C::GenerateRandomData(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
const u32 size = rp.Pop<u32>();
|
u32 size = rp.Pop<u32>();
|
||||||
auto buffer = rp.PopMappedBuffer();
|
auto buffer = rp.PopMappedBuffer();
|
||||||
|
|
||||||
std::vector<u8> out_data(size);
|
// Fill the output buffer with random data.
|
||||||
SSL::GenerateRandomData(out_data);
|
u32 data = 0;
|
||||||
buffer.Write(out_data.data(), 0, size);
|
u32 i = 0;
|
||||||
|
while (i < size) {
|
||||||
|
if ((i % 4) == 0) {
|
||||||
|
// The random number generator returns 4 bytes worth of data, so generate new random
|
||||||
|
// data when i == 0 and when i is divisible by 4
|
||||||
|
data = rand_gen();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 4) {
|
||||||
|
// Use up the entire 4 bytes of the random data for as long as possible
|
||||||
|
buffer.Write(&data, i, 4);
|
||||||
|
i += 4;
|
||||||
|
} else if (size == 2) {
|
||||||
|
buffer.Write(&data, i, 2);
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
buffer.Write(&data, i, 1);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stub, return success
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 2);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.PushMappedBuffer(buffer);
|
rb.PushMappedBuffer(buffer);
|
||||||
@@ -73,9 +96,4 @@ void InstallInterfaces(Core::System& system) {
|
|||||||
std::make_shared<SSL_C>()->InstallAsService(service_manager);
|
std::make_shared<SSL_C>()->InstallAsService(service_manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenerateRandomData(std::vector<u8>& out) {
|
|
||||||
// Fill the output buffer with random data.
|
|
||||||
RAND_bytes(out.data(), static_cast<int>(out.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Service::SSL
|
} // namespace Service::SSL
|
||||||
|
@@ -21,13 +21,14 @@ private:
|
|||||||
void Initialize(Kernel::HLERequestContext& ctx);
|
void Initialize(Kernel::HLERequestContext& ctx);
|
||||||
void GenerateRandomData(Kernel::HLERequestContext& ctx);
|
void GenerateRandomData(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
// TODO: Implement a proper CSPRNG in the future when actual security is needed
|
||||||
|
std::mt19937 rand_gen;
|
||||||
|
|
||||||
SERVICE_SERIALIZATION_SIMPLE
|
SERVICE_SERIALIZATION_SIMPLE
|
||||||
};
|
};
|
||||||
|
|
||||||
void InstallInterfaces(Core::System& system);
|
void InstallInterfaces(Core::System& system);
|
||||||
|
|
||||||
void GenerateRandomData(std::vector<u8>& out);
|
|
||||||
|
|
||||||
} // namespace Service::SSL
|
} // namespace Service::SSL
|
||||||
|
|
||||||
BOOST_CLASS_EXPORT_KEY(Service::SSL::SSL_C)
|
BOOST_CLASS_EXPORT_KEY(Service::SSL::SSL_C)
|
||||||
|
@@ -156,6 +156,20 @@ public:
|
|||||||
return system.GetRunningCore().GetPC();
|
return system.GetRunningCore().GetPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function should only be called for virtual addreses with attribute `PageType::Special`.
|
||||||
|
*/
|
||||||
|
MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) {
|
||||||
|
for (const auto& region : page_table.special_regions) {
|
||||||
|
if (vaddr >= region.base && vaddr < (region.base + region.size)) {
|
||||||
|
return region.handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_MSG(false, "Mapped IO page without a handler @ {:08X}", vaddr);
|
||||||
|
return nullptr; // Should never happen
|
||||||
|
}
|
||||||
|
|
||||||
template <bool UNSAFE>
|
template <bool UNSAFE>
|
||||||
void ReadBlockImpl(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
|
void ReadBlockImpl(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
|
||||||
const std::size_t size) {
|
const std::size_t size) {
|
||||||
@@ -187,6 +201,12 @@ public:
|
|||||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PageType::Special: {
|
||||||
|
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||||
|
DEBUG_ASSERT(handler);
|
||||||
|
handler->ReadBlock(current_vaddr, dest_buffer, copy_amount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
if constexpr (!UNSAFE) {
|
if constexpr (!UNSAFE) {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
@@ -235,6 +255,12 @@ public:
|
|||||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PageType::Special: {
|
||||||
|
MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr);
|
||||||
|
DEBUG_ASSERT(handler);
|
||||||
|
handler->WriteBlock(current_vaddr, src_buffer, copy_amount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
if constexpr (!UNSAFE) {
|
if constexpr (!UNSAFE) {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
@@ -379,6 +405,16 @@ void MemorySystem::MapMemoryRegion(PageTable& page_table, VAddr base, u32 size,
|
|||||||
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, target, PageType::Memory);
|
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, target, PageType::Memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemorySystem::MapIoRegion(PageTable& page_table, VAddr base, u32 size,
|
||||||
|
MMIORegionPointer mmio_handler) {
|
||||||
|
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||||
|
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||||
|
MapPages(page_table, base / CITRA_PAGE_SIZE, size / CITRA_PAGE_SIZE, nullptr,
|
||||||
|
PageType::Special);
|
||||||
|
|
||||||
|
page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler});
|
||||||
|
}
|
||||||
|
|
||||||
void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
|
void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) {
|
||||||
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
ASSERT_MSG((size & CITRA_PAGE_MASK) == 0, "non-page aligned size: {:08X}", size);
|
||||||
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
ASSERT_MSG((base & CITRA_PAGE_MASK) == 0, "non-page aligned base: {:08X}", base);
|
||||||
@@ -401,6 +437,9 @@ void MemorySystem::UnregisterPageTable(std::shared_ptr<PageTable> page_table) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T MemorySystem::Read(const VAddr vaddr) {
|
T MemorySystem::Read(const VAddr vaddr) {
|
||||||
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||||
@@ -443,6 +482,8 @@ T MemorySystem::Read(const VAddr vaddr) {
|
|||||||
std::memcpy(&value, GetPointerForRasterizerCache(vaddr), sizeof(T));
|
std::memcpy(&value, GetPointerForRasterizerCache(vaddr), sizeof(T));
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
case PageType::Special:
|
||||||
|
return ReadMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@@ -450,6 +491,9 @@ T MemorySystem::Read(const VAddr vaddr) {
|
|||||||
return T{};
|
return T{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void MemorySystem::Write(const VAddr vaddr, const T data) {
|
void MemorySystem::Write(const VAddr vaddr, const T data) {
|
||||||
u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||||
@@ -487,6 +531,9 @@ void MemorySystem::Write(const VAddr vaddr, const T data) {
|
|||||||
std::memcpy(GetPointerForRasterizerCache(vaddr), &data, sizeof(T));
|
std::memcpy(GetPointerForRasterizerCache(vaddr), &data, sizeof(T));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PageType::Special:
|
||||||
|
WriteMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@@ -517,6 +564,9 @@ bool MemorySystem::WriteExclusive(const VAddr vaddr, const T data, const T expec
|
|||||||
reinterpret_cast<volatile T*>(GetPointerForRasterizerCache(vaddr).GetPtr());
|
reinterpret_cast<volatile T*>(GetPointerForRasterizerCache(vaddr).GetPtr());
|
||||||
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
||||||
}
|
}
|
||||||
|
case PageType::Special:
|
||||||
|
WriteMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data);
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@@ -527,12 +577,18 @@ bool MemorySystem::IsValidVirtualAddress(const Kernel::Process& process, const V
|
|||||||
auto& page_table = *process.vm_manager.page_table;
|
auto& page_table = *process.vm_manager.page_table;
|
||||||
|
|
||||||
auto page_pointer = page_table.pointers[vaddr >> CITRA_PAGE_BITS];
|
auto page_pointer = page_table.pointers[vaddr >> CITRA_PAGE_BITS];
|
||||||
if (page_pointer) {
|
if (page_pointer)
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] == PageType::RasterizerCachedMemory) {
|
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] == PageType::RasterizerCachedMemory)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] != PageType::Special)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MMIORegionPointer mmio_region = impl->GetMMIOHandler(page_table, vaddr);
|
||||||
|
if (mmio_region) {
|
||||||
|
return mmio_region->IsValidAddress(vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -867,6 +923,8 @@ void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_ad
|
|||||||
std::size_t page_index = dest_addr >> CITRA_PAGE_BITS;
|
std::size_t page_index = dest_addr >> CITRA_PAGE_BITS;
|
||||||
std::size_t page_offset = dest_addr & CITRA_PAGE_MASK;
|
std::size_t page_offset = dest_addr & CITRA_PAGE_MASK;
|
||||||
|
|
||||||
|
static const std::array<u8, CITRA_PAGE_SIZE> zeros = {};
|
||||||
|
|
||||||
while (remaining_size > 0) {
|
while (remaining_size > 0) {
|
||||||
const std::size_t copy_amount = std::min(CITRA_PAGE_SIZE - page_offset, remaining_size);
|
const std::size_t copy_amount = std::min(CITRA_PAGE_SIZE - page_offset, remaining_size);
|
||||||
const VAddr current_vaddr =
|
const VAddr current_vaddr =
|
||||||
@@ -887,6 +945,12 @@ void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_ad
|
|||||||
std::memset(dest_ptr, 0, copy_amount);
|
std::memset(dest_ptr, 0, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PageType::Special: {
|
||||||
|
MMIORegionPointer handler = impl->GetMMIOHandler(page_table, current_vaddr);
|
||||||
|
DEBUG_ASSERT(handler);
|
||||||
|
handler->WriteBlock(current_vaddr, zeros.data(), copy_amount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
FlushMode::Invalidate);
|
FlushMode::Invalidate);
|
||||||
@@ -936,6 +1000,14 @@ void MemorySystem::CopyBlock(const Kernel::Process& dest_process,
|
|||||||
WriteBlock(dest_process, dest_addr, src_ptr, copy_amount);
|
WriteBlock(dest_process, dest_addr, src_ptr, copy_amount);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case PageType::Special: {
|
||||||
|
MMIORegionPointer handler = impl->GetMMIOHandler(page_table, current_vaddr);
|
||||||
|
DEBUG_ASSERT(handler);
|
||||||
|
std::vector<u8> buffer(copy_amount);
|
||||||
|
handler->ReadBlock(current_vaddr, buffer.data(), buffer.size());
|
||||||
|
WriteBlock(dest_process, dest_addr, buffer.data(), buffer.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
case PageType::RasterizerCachedMemory: {
|
case PageType::RasterizerCachedMemory: {
|
||||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||||
FlushMode::Flush);
|
FlushMode::Flush);
|
||||||
@@ -955,6 +1027,46 @@ void MemorySystem::CopyBlock(const Kernel::Process& dest_process,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
u8 ReadMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr) {
|
||||||
|
return mmio_handler->Read8(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
u16 ReadMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr) {
|
||||||
|
return mmio_handler->Read16(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
u32 ReadMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr) {
|
||||||
|
return mmio_handler->Read32(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
u64 ReadMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr) {
|
||||||
|
return mmio_handler->Read64(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void WriteMMIO<u8>(MMIORegionPointer mmio_handler, VAddr addr, const u8 data) {
|
||||||
|
mmio_handler->Write8(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void WriteMMIO<u16>(MMIORegionPointer mmio_handler, VAddr addr, const u16 data) {
|
||||||
|
mmio_handler->Write16(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void WriteMMIO<u32>(MMIORegionPointer mmio_handler, VAddr addr, const u32 data) {
|
||||||
|
mmio_handler->Write32(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void WriteMMIO<u64>(MMIORegionPointer mmio_handler, VAddr addr, const u64 data) {
|
||||||
|
mmio_handler->Write64(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
u32 MemorySystem::GetFCRAMOffset(const u8* pointer) const {
|
u32 MemorySystem::GetFCRAMOffset(const u8* pointer) const {
|
||||||
ASSERT(pointer >= impl->fcram.get() && pointer <= impl->fcram.get() + Memory::FCRAM_N3DS_SIZE);
|
ASSERT(pointer >= impl->fcram.get() && pointer <= impl->fcram.get() + Memory::FCRAM_N3DS_SIZE);
|
||||||
return static_cast<u32>(pointer - impl->fcram.get());
|
return static_cast<u32>(pointer - impl->fcram.get());
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#include <boost/serialization/vector.hpp>
|
#include <boost/serialization/vector.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/memory_ref.h"
|
#include "common/memory_ref.h"
|
||||||
|
#include "core/mmio.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
class Process;
|
class Process;
|
||||||
@@ -42,6 +43,23 @@ enum class PageType {
|
|||||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||||
/// invalidation
|
/// invalidation
|
||||||
RasterizerCachedMemory,
|
RasterizerCachedMemory,
|
||||||
|
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
||||||
|
Special,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpecialRegion {
|
||||||
|
VAddr base;
|
||||||
|
u32 size;
|
||||||
|
MMIORegionPointer handler;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int file_version) {
|
||||||
|
ar& base;
|
||||||
|
ar& size;
|
||||||
|
ar& handler;
|
||||||
|
}
|
||||||
|
friend class boost::serialization::access;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,6 +108,12 @@ struct PageTable {
|
|||||||
|
|
||||||
Pointers pointers;
|
Pointers pointers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
|
||||||
|
* type `Special`.
|
||||||
|
*/
|
||||||
|
std::vector<SpecialRegion> special_regions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array of fine grained page attributes. If it is set to any value other than `Memory`, then
|
* Array of fine grained page attributes. If it is set to any value other than `Memory`, then
|
||||||
* the corresponding entry in `pointers` MUST be set to null.
|
* the corresponding entry in `pointers` MUST be set to null.
|
||||||
@@ -106,6 +130,7 @@ private:
|
|||||||
template <class Archive>
|
template <class Archive>
|
||||||
void serialize(Archive& ar, const unsigned int) {
|
void serialize(Archive& ar, const unsigned int) {
|
||||||
ar& pointers.refs;
|
ar& pointers.refs;
|
||||||
|
ar& special_regions;
|
||||||
ar& attributes;
|
ar& attributes;
|
||||||
for (std::size_t i = 0; i < PAGE_TABLE_NUM_ENTRIES; i++) {
|
for (std::size_t i = 0; i < PAGE_TABLE_NUM_ENTRIES; i++) {
|
||||||
pointers.raw[i] = pointers.refs[i].GetPtr();
|
pointers.raw[i] = pointers.refs[i].GetPtr();
|
||||||
@@ -277,6 +302,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, MemoryRef target);
|
void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, MemoryRef target);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a region of the emulated process address space as a IO region.
|
||||||
|
* @param page_table The page table of the emulated process.
|
||||||
|
* @param base The address to start mapping at. Must be page-aligned.
|
||||||
|
* @param size The amount of bytes to map. Must be page-aligned.
|
||||||
|
* @param mmio_handler The handler that backs the mapping.
|
||||||
|
*/
|
||||||
|
void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler);
|
||||||
|
|
||||||
void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
|
void UnmapRegion(PageTable& page_table, VAddr base, u32 size);
|
||||||
|
|
||||||
/// Currently active page table
|
/// Currently active page table
|
||||||
|
43
src/core/mmio.h
Normal file
43
src/core/mmio.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Memory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a device with memory mapped IO.
|
||||||
|
* A device may be mapped to multiple regions of memory.
|
||||||
|
*/
|
||||||
|
class MMIORegion {
|
||||||
|
public:
|
||||||
|
virtual ~MMIORegion() = default;
|
||||||
|
|
||||||
|
virtual bool IsValidAddress(VAddr addr) = 0;
|
||||||
|
|
||||||
|
virtual u8 Read8(VAddr addr) = 0;
|
||||||
|
virtual u16 Read16(VAddr addr) = 0;
|
||||||
|
virtual u32 Read32(VAddr addr) = 0;
|
||||||
|
virtual u64 Read64(VAddr addr) = 0;
|
||||||
|
|
||||||
|
virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
|
||||||
|
|
||||||
|
virtual void Write8(VAddr addr, u8 data) = 0;
|
||||||
|
virtual void Write16(VAddr addr, u16 data) = 0;
|
||||||
|
virtual void Write32(VAddr addr, u32 data) = 0;
|
||||||
|
virtual void Write64(VAddr addr, u64 data) = 0;
|
||||||
|
|
||||||
|
virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class boost::serialization::access;
|
||||||
|
template <class Archive>
|
||||||
|
void serialize(Archive& ar, const unsigned int file_version) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
using MMIORegionPointer = std::shared_ptr<MMIORegion>;
|
||||||
|
}; // namespace Memory
|
@@ -2,6 +2,9 @@ add_executable(tests
|
|||||||
common/bit_field.cpp
|
common/bit_field.cpp
|
||||||
common/file_util.cpp
|
common/file_util.cpp
|
||||||
common/param_package.cpp
|
common/param_package.cpp
|
||||||
|
core/arm/arm_test_common.cpp
|
||||||
|
core/arm/arm_test_common.h
|
||||||
|
core/arm/dyncom/arm_dyncom_vfp_tests.cpp
|
||||||
core/core_timing.cpp
|
core/core_timing.cpp
|
||||||
core/file_sys/path_parser.cpp
|
core/file_sys/path_parser.cpp
|
||||||
core/hle/kernel/hle_ipc.cpp
|
core/hle/kernel/hle_ipc.cpp
|
||||||
@@ -18,7 +21,7 @@ add_executable(tests
|
|||||||
create_target_directory_groups(tests)
|
create_target_directory_groups(tests)
|
||||||
|
|
||||||
target_link_libraries(tests PRIVATE citra_common citra_core video_core audio_core)
|
target_link_libraries(tests PRIVATE citra_common citra_core video_core audio_core)
|
||||||
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch2 nihstro-headers Threads::Threads)
|
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain nihstro-headers Threads::Threads)
|
||||||
|
|
||||||
add_test(NAME tests COMMAND tests)
|
add_test(NAME tests COMMAND tests)
|
||||||
|
|
||||||
|
143
src/tests/core/arm/arm_test_common.cpp
Normal file
143
src/tests/core/arm/arm_test_common.cpp
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "core/hle/kernel/process.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
#include "tests/core/arm/arm_test_common.h"
|
||||||
|
|
||||||
|
namespace ArmTests {
|
||||||
|
|
||||||
|
static std::shared_ptr<Memory::PageTable> page_table = nullptr;
|
||||||
|
|
||||||
|
TestEnvironment::TestEnvironment(bool mutable_memory_)
|
||||||
|
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
|
||||||
|
|
||||||
|
timing = std::make_unique<Core::Timing>(1, 100);
|
||||||
|
system = std::make_unique<Core::System>();
|
||||||
|
memory = std::make_unique<Memory::MemorySystem>(*system);
|
||||||
|
kernel = std::make_unique<Kernel::KernelSystem>(
|
||||||
|
*memory, *timing, [] {}, Kernel::MemoryMode::Prod, 1,
|
||||||
|
Kernel::New3dsHwCapabilities{false, false, Kernel::New3dsMemoryMode::Legacy});
|
||||||
|
|
||||||
|
kernel->SetCurrentProcess(kernel->CreateProcess(kernel->CreateCodeSet("", 0)));
|
||||||
|
page_table = kernel->GetCurrentProcess()->vm_manager.page_table;
|
||||||
|
|
||||||
|
page_table->Clear();
|
||||||
|
|
||||||
|
memory->MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
|
||||||
|
memory->MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);
|
||||||
|
|
||||||
|
kernel->SetCurrentMemoryPageTable(page_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestEnvironment::~TestEnvironment() {
|
||||||
|
memory->UnmapRegion(*page_table, 0x80000000, 0x80000000);
|
||||||
|
memory->UnmapRegion(*page_table, 0x00000000, 0x80000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) {
|
||||||
|
SetMemory32(vaddr + 0, static_cast<u32>(value));
|
||||||
|
SetMemory32(vaddr + 4, static_cast<u32>(value >> 32));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::SetMemory32(VAddr vaddr, u32 value) {
|
||||||
|
SetMemory16(vaddr + 0, static_cast<u16>(value));
|
||||||
|
SetMemory16(vaddr + 2, static_cast<u16>(value >> 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::SetMemory16(VAddr vaddr, u16 value) {
|
||||||
|
SetMemory8(vaddr + 0, static_cast<u8>(value));
|
||||||
|
SetMemory8(vaddr + 1, static_cast<u8>(value >> 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::SetMemory8(VAddr vaddr, u8 value) {
|
||||||
|
test_memory->data[vaddr] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WriteRecord> TestEnvironment::GetWriteRecords() const {
|
||||||
|
return write_records;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::ClearWriteRecords() {
|
||||||
|
write_records.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TestEnvironment::TestMemory::~TestMemory() {}
|
||||||
|
|
||||||
|
bool TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 TestEnvironment::TestMemory::Read8(VAddr addr) {
|
||||||
|
auto iter = data.find(addr);
|
||||||
|
if (iter == data.end()) {
|
||||||
|
return addr; // Some arbitrary data
|
||||||
|
}
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 TestEnvironment::TestMemory::Read16(VAddr addr) {
|
||||||
|
return Read8(addr) | static_cast<u16>(Read8(addr + 1)) << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 TestEnvironment::TestMemory::Read32(VAddr addr) {
|
||||||
|
return Read16(addr) | static_cast<u32>(Read16(addr + 2)) << 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 TestEnvironment::TestMemory::Read64(VAddr addr) {
|
||||||
|
return Read32(addr) | static_cast<u64>(Read32(addr + 4)) << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) {
|
||||||
|
VAddr addr = src_addr;
|
||||||
|
u8* data = static_cast<u8*>(dest_buffer);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < size; i++, addr++, data++) {
|
||||||
|
*data = Read8(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::TestMemory::Write8(VAddr addr, u8 data) {
|
||||||
|
env->write_records.emplace_back(8, addr, data);
|
||||||
|
if (env->mutable_memory)
|
||||||
|
env->SetMemory8(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::TestMemory::Write16(VAddr addr, u16 data) {
|
||||||
|
env->write_records.emplace_back(16, addr, data);
|
||||||
|
if (env->mutable_memory)
|
||||||
|
env->SetMemory16(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::TestMemory::Write32(VAddr addr, u32 data) {
|
||||||
|
env->write_records.emplace_back(32, addr, data);
|
||||||
|
if (env->mutable_memory)
|
||||||
|
env->SetMemory32(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestEnvironment::TestMemory::Write64(VAddr addr, u64 data) {
|
||||||
|
env->write_records.emplace_back(64, addr, data);
|
||||||
|
if (env->mutable_memory)
|
||||||
|
env->SetMemory64(addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer,
|
||||||
|
std::size_t size) {
|
||||||
|
VAddr addr = dest_addr;
|
||||||
|
const u8* data = static_cast<const u8*>(src_buffer);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < size; i++, addr++, data++) {
|
||||||
|
env->write_records.emplace_back(8, addr, *data);
|
||||||
|
if (env->mutable_memory)
|
||||||
|
env->SetMemory8(addr, *data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ArmTests
|
93
src/tests/core/arm/arm_test_common.h
Normal file
93
src/tests/core/arm/arm_test_common.h
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hle/kernel/kernel.h"
|
||||||
|
#include "core/mmio.h"
|
||||||
|
|
||||||
|
namespace ArmTests {
|
||||||
|
|
||||||
|
struct WriteRecord {
|
||||||
|
WriteRecord(std::size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {}
|
||||||
|
std::size_t size;
|
||||||
|
VAddr addr;
|
||||||
|
u64 data;
|
||||||
|
bool operator==(const WriteRecord& o) const {
|
||||||
|
return std::tie(size, addr, data) == std::tie(o.size, o.addr, o.data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestEnvironment final {
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Inititalise test environment
|
||||||
|
* @param mutable_memory If false, writes to memory can never be read back.
|
||||||
|
* (Memory is immutable.)
|
||||||
|
*/
|
||||||
|
explicit TestEnvironment(bool mutable_memory = false);
|
||||||
|
|
||||||
|
/// Shutdown test environment
|
||||||
|
~TestEnvironment();
|
||||||
|
|
||||||
|
/// Sets value at memory location vaddr.
|
||||||
|
void SetMemory8(VAddr vaddr, u8 value);
|
||||||
|
void SetMemory16(VAddr vaddr, u16 value);
|
||||||
|
void SetMemory32(VAddr vaddr, u32 value);
|
||||||
|
void SetMemory64(VAddr vaddr, u64 value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whenever Memory::Write{8,16,32,64} is called within the test environment,
|
||||||
|
* a new write-record is made.
|
||||||
|
* @returns A vector of write records made since they were last cleared.
|
||||||
|
*/
|
||||||
|
std::vector<WriteRecord> GetWriteRecords() const;
|
||||||
|
|
||||||
|
/// Empties the internal write-record store.
|
||||||
|
void ClearWriteRecords();
|
||||||
|
|
||||||
|
Memory::MemorySystem& GetMemory() {
|
||||||
|
return *memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct TestMemory;
|
||||||
|
struct TestMemory final : Memory::MMIORegion {
|
||||||
|
explicit TestMemory(TestEnvironment* env_) : env(env_) {}
|
||||||
|
TestEnvironment* env;
|
||||||
|
|
||||||
|
~TestMemory() override;
|
||||||
|
|
||||||
|
bool IsValidAddress(VAddr addr) override;
|
||||||
|
|
||||||
|
u8 Read8(VAddr addr) override;
|
||||||
|
u16 Read16(VAddr addr) override;
|
||||||
|
u32 Read32(VAddr addr) override;
|
||||||
|
u64 Read64(VAddr addr) override;
|
||||||
|
|
||||||
|
bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override;
|
||||||
|
|
||||||
|
void Write8(VAddr addr, u8 data) override;
|
||||||
|
void Write16(VAddr addr, u16 data) override;
|
||||||
|
void Write32(VAddr addr, u32 data) override;
|
||||||
|
void Write64(VAddr addr, u64 data) override;
|
||||||
|
|
||||||
|
bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) override;
|
||||||
|
|
||||||
|
std::unordered_map<VAddr, u8> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool mutable_memory;
|
||||||
|
std::shared_ptr<TestMemory> test_memory;
|
||||||
|
std::vector<WriteRecord> write_records;
|
||||||
|
|
||||||
|
std::unique_ptr<Core::Timing> timing;
|
||||||
|
std::unique_ptr<Core::System> system;
|
||||||
|
std::unique_ptr<Memory::MemorySystem> memory;
|
||||||
|
std::unique_ptr<Kernel::KernelSystem> kernel;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ArmTests
|
51
src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp
Normal file
51
src/tests/core/arm/dyncom/arm_dyncom_vfp_tests.cpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2016 Citra Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include "core/arm/dyncom/arm_dyncom.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/core_timing.h"
|
||||||
|
#include "tests/core/arm/arm_test_common.h"
|
||||||
|
|
||||||
|
namespace ArmTests {
|
||||||
|
|
||||||
|
struct VfpTestCase {
|
||||||
|
u32 initial_fpscr;
|
||||||
|
u32 a;
|
||||||
|
u32 b;
|
||||||
|
u32 result;
|
||||||
|
u32 final_fpscr;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("ARM_DynCom (vfp): vadd", "[arm_dyncom]") {
|
||||||
|
TestEnvironment test_env(false);
|
||||||
|
test_env.SetMemory32(0, 0xEE321A03); // vadd.f32 s2, s4, s6
|
||||||
|
test_env.SetMemory32(4, 0xEAFFFFFE); // b +#0
|
||||||
|
|
||||||
|
Core::System system;
|
||||||
|
Core::ARM_DynCom dyncom(system, test_env.GetMemory(), USER32MODE, 0, nullptr);
|
||||||
|
|
||||||
|
std::vector<VfpTestCase> test_cases{{
|
||||||
|
#include "vfp_vadd_f32.inc"
|
||||||
|
}};
|
||||||
|
|
||||||
|
for (const auto& test_case : test_cases) {
|
||||||
|
dyncom.SetPC(0);
|
||||||
|
dyncom.SetVFPSystemReg(VFP_FPSCR, test_case.initial_fpscr);
|
||||||
|
dyncom.SetVFPReg(4, test_case.a);
|
||||||
|
dyncom.SetVFPReg(6, test_case.b);
|
||||||
|
dyncom.Step();
|
||||||
|
if (dyncom.GetVFPReg(2) != test_case.result ||
|
||||||
|
dyncom.GetVFPSystemReg(VFP_FPSCR) != test_case.final_fpscr) {
|
||||||
|
printf("f: %x\n", test_case.initial_fpscr);
|
||||||
|
printf("a: %x\n", test_case.a);
|
||||||
|
printf("b: %x\n", test_case.b);
|
||||||
|
printf("c: %x (%x)\n", dyncom.GetVFPReg(2), test_case.result);
|
||||||
|
printf("f: %x (%x)\n", dyncom.GetVFPSystemReg(VFP_FPSCR), test_case.final_fpscr);
|
||||||
|
FAIL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ArmTests
|
13456
src/tests/core/arm/dyncom/vfp_vadd_f32.inc
Normal file
13456
src/tests/core/arm/dyncom/vfp_vadd_f32.inc
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user