Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
abcf6b8256 | |||
ca6dae1744 | |||
b6acebcb11 | |||
ba702043f0 | |||
2a4c60c1dd | |||
a1532f813b | |||
26d5727b19 | |||
680e132318 | |||
90a5d989e7 | |||
de40153fa4 | |||
e9936e01c2 |
12
externals/CMakeLists.txt
vendored
12
externals/CMakeLists.txt
vendored
@ -41,9 +41,15 @@ else()
|
||||
endif()
|
||||
|
||||
# Catch2
|
||||
set(CATCH_INSTALL_DOCS OFF CACHE BOOL "")
|
||||
set(CATCH_INSTALL_EXTRAS OFF CACHE BOOL "")
|
||||
add_subdirectory(catch2)
|
||||
add_library(catch2 INTERFACE)
|
||||
if(USE_SYSTEM_CATCH2)
|
||||
find_package(Catch2 3.0.0 REQUIRED)
|
||||
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++
|
||||
if(USE_SYSTEM_CRYPTOPP)
|
||||
|
@ -25,6 +25,7 @@ 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_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_CATCH2 "Use the system Catch2 (instead of the bundled one)" OFF)
|
||||
|
||||
# Qt and MoltenVK are handled separately
|
||||
CMAKE_DEPENDENT_OPTION(DISABLE_SYSTEM_SDL2 "Disable system SDL2" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
@ -49,6 +50,7 @@ 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_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_CATCH2 "Disable system Catch2" OFF "USE_SYSTEM_LIBS" OFF)
|
||||
|
||||
set(LIB_VAR_LIST
|
||||
SDL2
|
||||
@ -73,6 +75,7 @@ set(LIB_VAR_LIST
|
||||
OPENAL
|
||||
VMA
|
||||
VULKAN_HEADERS
|
||||
CATCH2
|
||||
)
|
||||
|
||||
# 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)
|
||||
pkg_check_modules(OOPENAL_TMP libopanal)
|
||||
pkg_check_modules(OPENAL_TMP openal)
|
||||
|
||||
find_path(OPENAL_INCLUDE_DIRS NAMES al.h
|
||||
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("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
|
||||
{QStringLiteral("Toggle 3D"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+3"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Per-Game Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}},
|
||||
{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 Per-Game Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
|
||||
{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 Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral(""), Qt::ApplicationShortcut}},
|
||||
{QStringLiteral("Toggle Custom Textures"), QStringLiteral("Main Window"), {QStringLiteral("F7"), Qt::ApplicationShortcut}},
|
||||
}};
|
||||
// clang-format on
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
// windows.h needs to be included before other windows headers
|
||||
#include <direct.h> // getcwd
|
||||
#include <io.h>
|
||||
#include <share.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h> // for SHGetFolderPath
|
||||
#include <tchar.h>
|
||||
@ -1058,14 +1059,13 @@ bool IOFile::Open() {
|
||||
Close();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (flags != 0) {
|
||||
m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
|
||||
Common::UTF8ToUTF16W(openmode).c_str(), flags);
|
||||
m_good = m_file != nullptr;
|
||||
} else {
|
||||
m_good = _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
|
||||
Common::UTF8ToUTF16W(openmode).c_str()) == 0;
|
||||
if (flags == 0) {
|
||||
flags = _SH_DENYNO;
|
||||
}
|
||||
m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
|
||||
Common::UTF8ToUTF16W(openmode).c_str(), flags);
|
||||
m_good = m_file != nullptr;
|
||||
|
||||
#elif ANDROID
|
||||
// Check whether filepath is startsWith content
|
||||
AndroidStorage::AndroidOpenMode android_open_mode = AndroidStorage::ParseOpenmode(openmode);
|
||||
|
@ -455,7 +455,6 @@ add_library(citra_core STATIC
|
||||
loader/smdh.h
|
||||
memory.cpp
|
||||
memory.h
|
||||
mmio.h
|
||||
movie.cpp
|
||||
movie.h
|
||||
nus_download.cpp
|
||||
|
@ -50,6 +50,9 @@ public:
|
||||
|
||||
Loader::ResultStatus Load(std::span<const u8> file_data, std::size_t offset = 0);
|
||||
std::optional<std::array<u8, 16>> GetTitleKey() const;
|
||||
u64 GetTitleID() const {
|
||||
return ticket_body.title_id;
|
||||
}
|
||||
|
||||
private:
|
||||
Body ticket_body;
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "core/hle/kernel/vm_manager.h"
|
||||
#include "core/hle/service/plgldr/plgldr.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/mmio.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -33,9 +32,6 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
||||
backing_memory.GetPtr() + size != next.backing_memory.GetPtr()) {
|
||||
return false;
|
||||
}
|
||||
if (type == VMAType::MMIO && paddr + size != next.paddr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -118,26 +114,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, Memory
|
||||
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,
|
||||
VMAPermission expected_perms, MemoryState new_state,
|
||||
VMAPermission new_perms) {
|
||||
@ -185,9 +161,7 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
|
||||
vma.type = VMAType::Free;
|
||||
vma.permissions = VMAPermission::None;
|
||||
vma.meminfo_state = MemoryState::Free;
|
||||
|
||||
vma.backing_memory = nullptr;
|
||||
vma.paddr = 0;
|
||||
|
||||
UpdatePageTableForVMA(vma);
|
||||
|
||||
@ -344,9 +318,6 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) {
|
||||
case VMAType::BackingMemory:
|
||||
new_vma.backing_memory += offset_in_vma;
|
||||
break;
|
||||
case VMAType::MMIO:
|
||||
new_vma.paddr += offset_in_vma;
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT(old_vma.CanBeMergedWith(new_vma));
|
||||
@ -381,9 +352,6 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
||||
case VMAType::BackingMemory:
|
||||
memory.MapMemoryRegion(*page_table, vma.base, vma.size, vma.backing_memory);
|
||||
break;
|
||||
case VMAType::MMIO:
|
||||
memory.MapIoRegion(*page_table, vma.base, vma.size, vma.mmio_handler);
|
||||
break;
|
||||
}
|
||||
|
||||
auto plgldr = Service::PLGLDR::GetService(Core::System::GetInstance());
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <boost/serialization/map.hpp>
|
||||
#include <boost/serialization/shared_ptr.hpp>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
@ -16,7 +14,6 @@
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/mmio.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -25,8 +22,6 @@ enum class VMAType : u8 {
|
||||
Free,
|
||||
/// VMA is backed by a raw, unmanaged pointer.
|
||||
BackingMemory,
|
||||
/// VMA is mapped to MMIO registers at a fixed PAddr.
|
||||
MMIO,
|
||||
};
|
||||
|
||||
/// Permissions for mapped memory blocks
|
||||
@ -74,15 +69,10 @@ struct VirtualMemoryArea {
|
||||
/// Tag returned by svcQueryMemory. Not otherwise used.
|
||||
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.
|
||||
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`.
|
||||
bool CanBeMergedWith(const VirtualMemoryArea& next) const;
|
||||
|
||||
@ -96,8 +86,6 @@ private:
|
||||
ar& permissions;
|
||||
ar& meminfo_state;
|
||||
ar& backing_memory;
|
||||
ar& paddr;
|
||||
ar& mmio_handler;
|
||||
}
|
||||
};
|
||||
|
||||
@ -166,18 +154,6 @@ public:
|
||||
ResultVal<VMAHandle> MapBackingMemory(VAddr target, MemoryRef memory, u32 size,
|
||||
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
|
||||
* state and permissions must match the `expected` parameters.
|
||||
|
@ -374,6 +374,45 @@ bool CIAFile::Close() 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,
|
||||
std::function<ProgressCallback>&& update_callback) {
|
||||
LOG_INFO(Service_AM, "Installing {}...", path);
|
||||
@ -942,6 +981,10 @@ void Module::Interface::GetProgramInfos(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushMappedBuffer(title_info_out);
|
||||
}
|
||||
|
||||
void Module::Interface::GetProgramInfosIgnorePlatform(Kernel::HLERequestContext& ctx) {
|
||||
GetProgramInfos(ctx);
|
||||
}
|
||||
|
||||
void Module::Interface::DeleteUserProgram(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
auto media_type = rp.PopEnum<FS::MediaType>();
|
||||
@ -1177,6 +1220,16 @@ void Module::Interface::NeedsCleanup(Kernel::HLERequestContext& ctx) {
|
||||
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) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u8 media_type = rp.Pop<u8>();
|
||||
@ -1188,6 +1241,45 @@ void Module::Interface::QueryAvailableTitleDatabase(Kernel::HLERequestContext& c
|
||||
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) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u64 tid = rp.Pop<u64>();
|
||||
@ -1674,6 +1766,30 @@ void Module::Interface::GetMetaDataFromCia(Kernel::HLERequestContext& ctx) {
|
||||
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()) {
|
||||
ScanForAllTitles();
|
||||
system_updater_mutex = system.Kernel().CreateMutex(false, "AM::SystemUpdaterMutex");
|
||||
|
@ -109,6 +109,25 @@ private:
|
||||
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.
|
||||
* @param path file path of the CIA file to install
|
||||
@ -272,6 +291,18 @@ public:
|
||||
*/
|
||||
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
|
||||
* Deletes a user program
|
||||
@ -389,6 +420,15 @@ public:
|
||||
*/
|
||||
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
|
||||
* Inputs:
|
||||
@ -399,6 +439,42 @@ public:
|
||||
*/
|
||||
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
|
||||
* Inputs:
|
||||
@ -602,6 +678,25 @@ public:
|
||||
*/
|
||||
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:
|
||||
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"},
|
||||
{0x001F, nullptr, "DeleteAllExpiredUserPrograms"},
|
||||
{0x0020, nullptr, "GetTwlArchiveResourceInfo"},
|
||||
{0x0021, nullptr, "GetPersonalizedTicketInfoList"},
|
||||
{0x0021, &AM_NET::GetPersonalizedTicketInfoList, "GetPersonalizedTicketInfoList"},
|
||||
{0x0022, nullptr, "DeleteAllImportContextsFiltered"},
|
||||
{0x0023, nullptr, "GetNumImportTitleContextsFiltered"},
|
||||
{0x0024, nullptr, "GetImportTitleContextListFiltered"},
|
||||
{0x0025, nullptr, "CheckContentRights"},
|
||||
{0x0023, &AM_NET::GetNumImportTitleContextsFiltered, "GetNumImportTitleContextsFiltered"},
|
||||
{0x0024, &AM_NET::GetImportTitleContextListFiltered, "GetImportTitleContextListFiltered"},
|
||||
{0x0025, &AM_NET::CheckContentRights, "CheckContentRights"},
|
||||
{0x0026, nullptr, "GetTicketLimitInfos"},
|
||||
{0x0027, nullptr, "GetDemoLaunchInfos"},
|
||||
{0x0028, nullptr, "ReadTwlBackupInfoEx"},
|
||||
{0x0029, nullptr, "DeleteUserProgramsAtomically"},
|
||||
{0x002A, nullptr, "GetNumExistingContentInfosSystem"},
|
||||
{0x002B, nullptr, "ListExistingContentInfosSystem"},
|
||||
{0x002C, nullptr, "GetProgramInfosIgnorePlatform"},
|
||||
{0x002D, nullptr, "CheckContentRightsIgnorePlatform"},
|
||||
{0x002C, &AM_NET::GetProgramInfosIgnorePlatform, "GetProgramInfosIgnorePlatform"},
|
||||
{0x002D, &AM_NET::CheckContentRightsIgnorePlatform, "CheckContentRightsIgnorePlatform"},
|
||||
{0x0401, nullptr, "UpdateFirmwareTo"},
|
||||
{0x0402, &AM_NET::BeginImportProgram, "BeginImportProgram"},
|
||||
{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"},
|
||||
{0x0410, &AM_NET::DeleteProgram, "DeleteProgram"},
|
||||
{0x0411, nullptr, "GetTwlProgramListForReboot"},
|
||||
{0x0412, nullptr, "GetSystemUpdaterMutex"},
|
||||
{0x0412, &AM_NET::GetSystemUpdaterMutex, "GetSystemUpdaterMutex"},
|
||||
{0x0413, &AM_NET::GetMetaSizeFromCia, "GetMetaSizeFromCia"},
|
||||
{0x0414, &AM_NET::GetMetaDataFromCia, "GetMetaDataFromCia"},
|
||||
{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"},
|
||||
{0x0418, nullptr, "BeginImportProgramForOverWrite"},
|
||||
{0x0419, nullptr, "BeginImportSystemProgram"},
|
||||
{0x0801, nullptr, "BeginImportTicket"},
|
||||
{0x0801, &AM_NET::BeginImportTicket, "BeginImportTicket"},
|
||||
{0x0802, nullptr, "CancelImportTicket"},
|
||||
{0x0803, nullptr, "EndImportTicket"},
|
||||
{0x0803, &AM_NET::EndImportTicket, "EndImportTicket"},
|
||||
{0x0804, nullptr, "BeginImportTitle"},
|
||||
{0x0805, nullptr, "StopImportTitle"},
|
||||
{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"},
|
||||
{0x0012, nullptr, "DeleteImportContentContexts"},
|
||||
{0x0013, &AM_SYS::NeedsCleanup, "NeedsCleanup"},
|
||||
{0x0014, nullptr, "DoCleanup"},
|
||||
{0x0014, &AM_SYS::DoCleanup, "DoCleanup"},
|
||||
{0x0015, nullptr, "DeleteAllImportContexts"},
|
||||
{0x0016, nullptr, "DeleteAllTemporaryPrograms"},
|
||||
{0x0017, nullptr, "ImportTwlBackupLegacy"},
|
||||
|
@ -875,7 +875,7 @@ void FS_USER::GetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
// TODO: Implement Secure Value Lookup & Generation
|
||||
|
||||
rb.Push<bool>(false); // indicates that the secure value doesn't exist
|
||||
rb.Push<bool>(false); // looks like a boolean value, purpose unknown
|
||||
rb.Push<bool>(true); // seems to override checking the validity so should be true
|
||||
rb.Push<u64>(0); // the secure value
|
||||
}
|
||||
|
||||
@ -913,7 +913,7 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
// TODO: Implement Secure Value Lookup & Generation
|
||||
|
||||
rb.Push<bool>(false); // indicates that the secure value doesn't exist
|
||||
rb.Push<bool>(false); // looks like a boolean value, purpose unknown
|
||||
rb.Push<bool>(true); // seems to override checking the validity so should be true
|
||||
rb.Push<u64>(0); // the secure value
|
||||
}
|
||||
|
||||
|
@ -298,7 +298,7 @@ struct MIC_U::Impl {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u8>(state.gain);
|
||||
LOG_TRACE(Service_MIC, "gain={}", gain);
|
||||
LOG_TRACE(Service_MIC, "gain={}", state.gain);
|
||||
}
|
||||
|
||||
void SetPower(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.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/key.h"
|
||||
|
||||
@ -146,6 +147,20 @@ void PS_PS::EncryptDecryptAes(Kernel::HLERequestContext& ctx) {
|
||||
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) {
|
||||
static const FunctionInfo functions[] = {
|
||||
// clang-format off
|
||||
@ -160,7 +175,7 @@ PS_PS::PS_PS() : ServiceFramework("ps:ps", DefaultMaxSessions) {
|
||||
{0x000A, nullptr, "GetLocalFriendCodeSeed"},
|
||||
{0x000B, nullptr, "GetDeviceId"},
|
||||
{0x000C, nullptr, "SeedRNG"},
|
||||
{0x000D, nullptr, "GenerateRandomBytes"},
|
||||
{0x000D, &PS_PS::GenerateRandomBytes, "GenerateRandomBytes"},
|
||||
{0x000E, nullptr, "InterfaceForPXI_0x04010084"},
|
||||
{0x000F, nullptr, "InterfaceForPXI_0x04020082"},
|
||||
{0x0010, nullptr, "InterfaceForPXI_0x04030044"},
|
||||
|
@ -10,12 +10,24 @@ SERIALIZE_EXPORT_IMPL(Service::QTM::QTM_S)
|
||||
|
||||
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) {
|
||||
static const FunctionInfo functions[] = {
|
||||
// qtm common commands
|
||||
// clang-format off
|
||||
{0x0001, nullptr, "GetHeadtrackingInfoRaw"},
|
||||
{0x0002, nullptr, "GetHeadtrackingInfo"},
|
||||
{0x0002, &QTM_S::GetHeadtrackingInfo, "GetHeadtrackingInfo"},
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
|
@ -15,6 +15,8 @@ public:
|
||||
|
||||
private:
|
||||
SERVICE_SERIALIZATION_SIMPLE
|
||||
|
||||
void GetHeadtrackingInfo(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::QTM
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <openssl/rand.h>
|
||||
#include "common/archives.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core.h"
|
||||
@ -16,10 +17,6 @@ void SSL_C::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
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
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@ -27,33 +24,13 @@ void SSL_C::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void SSL_C::GenerateRandomData(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 size = rp.Pop<u32>();
|
||||
const u32 size = rp.Pop<u32>();
|
||||
auto buffer = rp.PopMappedBuffer();
|
||||
|
||||
// Fill the output buffer with random data.
|
||||
u32 data = 0;
|
||||
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();
|
||||
}
|
||||
std::vector<u8> out_data(size);
|
||||
SSL::GenerateRandomData(out_data);
|
||||
buffer.Write(out_data.data(), 0, size);
|
||||
|
||||
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);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushMappedBuffer(buffer);
|
||||
@ -96,4 +73,9 @@ void InstallInterfaces(Core::System& system) {
|
||||
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
|
||||
|
@ -21,14 +21,13 @@ private:
|
||||
void Initialize(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
|
||||
};
|
||||
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
void GenerateRandomData(std::vector<u8>& out);
|
||||
|
||||
} // namespace Service::SSL
|
||||
|
||||
BOOST_CLASS_EXPORT_KEY(Service::SSL::SSL_C)
|
||||
|
@ -156,20 +156,6 @@ public:
|
||||
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>
|
||||
void ReadBlockImpl(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,
|
||||
const std::size_t size) {
|
||||
@ -201,12 +187,6 @@ public:
|
||||
std::memcpy(dest_buffer, src_ptr, copy_amount);
|
||||
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: {
|
||||
if constexpr (!UNSAFE) {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
@ -255,12 +235,6 @@ public:
|
||||
std::memcpy(dest_ptr, src_buffer, copy_amount);
|
||||
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: {
|
||||
if constexpr (!UNSAFE) {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
@ -405,16 +379,6 @@ void MemorySystem::MapMemoryRegion(PageTable& page_table, VAddr base, u32 size,
|
||||
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) {
|
||||
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);
|
||||
@ -437,9 +401,6 @@ void MemorySystem::UnregisterPageTable(std::shared_ptr<PageTable> page_table) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr);
|
||||
|
||||
template <typename T>
|
||||
T MemorySystem::Read(const VAddr vaddr) {
|
||||
const u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
@ -482,8 +443,6 @@ T MemorySystem::Read(const VAddr vaddr) {
|
||||
std::memcpy(&value, GetPointerForRasterizerCache(vaddr), sizeof(T));
|
||||
return value;
|
||||
}
|
||||
case PageType::Special:
|
||||
return ReadMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -491,9 +450,6 @@ T MemorySystem::Read(const VAddr vaddr) {
|
||||
return T{};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void WriteMMIO(MMIORegionPointer mmio_handler, VAddr addr, const T data);
|
||||
|
||||
template <typename T>
|
||||
void MemorySystem::Write(const VAddr vaddr, const T data) {
|
||||
u8* page_pointer = impl->current_page_table->pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
@ -531,9 +487,6 @@ void MemorySystem::Write(const VAddr vaddr, const T data) {
|
||||
std::memcpy(GetPointerForRasterizerCache(vaddr), &data, sizeof(T));
|
||||
break;
|
||||
}
|
||||
case PageType::Special:
|
||||
WriteMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -564,9 +517,6 @@ bool MemorySystem::WriteExclusive(const VAddr vaddr, const T data, const T expec
|
||||
reinterpret_cast<volatile T*>(GetPointerForRasterizerCache(vaddr).GetPtr());
|
||||
return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
|
||||
}
|
||||
case PageType::Special:
|
||||
WriteMMIO<T>(impl->GetMMIOHandler(*impl->current_page_table, vaddr), vaddr, data);
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -577,18 +527,12 @@ bool MemorySystem::IsValidVirtualAddress(const Kernel::Process& process, const V
|
||||
auto& page_table = *process.vm_manager.page_table;
|
||||
|
||||
auto page_pointer = page_table.pointers[vaddr >> CITRA_PAGE_BITS];
|
||||
if (page_pointer)
|
||||
if (page_pointer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] == PageType::RasterizerCachedMemory)
|
||||
if (page_table.attributes[vaddr >> CITRA_PAGE_BITS] == PageType::RasterizerCachedMemory) {
|
||||
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;
|
||||
@ -923,8 +867,6 @@ 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_offset = dest_addr & CITRA_PAGE_MASK;
|
||||
|
||||
static const std::array<u8, CITRA_PAGE_SIZE> zeros = {};
|
||||
|
||||
while (remaining_size > 0) {
|
||||
const std::size_t copy_amount = std::min(CITRA_PAGE_SIZE - page_offset, remaining_size);
|
||||
const VAddr current_vaddr =
|
||||
@ -945,12 +887,6 @@ void MemorySystem::ZeroBlock(const Kernel::Process& process, const VAddr dest_ad
|
||||
std::memset(dest_ptr, 0, copy_amount);
|
||||
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: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Invalidate);
|
||||
@ -1000,14 +936,6 @@ void MemorySystem::CopyBlock(const Kernel::Process& dest_process,
|
||||
WriteBlock(dest_process, dest_addr, src_ptr, copy_amount);
|
||||
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: {
|
||||
RasterizerFlushVirtualRegion(current_vaddr, static_cast<u32>(copy_amount),
|
||||
FlushMode::Flush);
|
||||
@ -1027,46 +955,6 @@ 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 {
|
||||
ASSERT(pointer >= impl->fcram.get() && pointer <= impl->fcram.get() + Memory::FCRAM_N3DS_SIZE);
|
||||
return static_cast<u32>(pointer - impl->fcram.get());
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "common/memory_ref.h"
|
||||
#include "core/mmio.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Process;
|
||||
@ -43,23 +42,6 @@ enum class PageType {
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -108,12 +90,6 @@ struct PageTable {
|
||||
|
||||
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
|
||||
* the corresponding entry in `pointers` MUST be set to null.
|
||||
@ -130,7 +106,6 @@ private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& pointers.refs;
|
||||
ar& special_regions;
|
||||
ar& attributes;
|
||||
for (std::size_t i = 0; i < PAGE_TABLE_NUM_ENTRIES; i++) {
|
||||
pointers.raw[i] = pointers.refs[i].GetPtr();
|
||||
@ -302,15 +277,6 @@ public:
|
||||
*/
|
||||
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);
|
||||
|
||||
/// Currently active page table
|
||||
|
@ -1,43 +0,0 @@
|
||||
// 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,9 +2,6 @@ add_executable(tests
|
||||
common/bit_field.cpp
|
||||
common/file_util.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/file_sys/path_parser.cpp
|
||||
core/hle/kernel/hle_ipc.cpp
|
||||
@ -21,7 +18,7 @@ add_executable(tests
|
||||
create_target_directory_groups(tests)
|
||||
|
||||
target_link_libraries(tests PRIVATE citra_common citra_core video_core audio_core)
|
||||
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} Catch2::Catch2WithMain nihstro-headers Threads::Threads)
|
||||
target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch2 nihstro-headers Threads::Threads)
|
||||
|
||||
add_test(NAME tests COMMAND tests)
|
||||
|
||||
|
@ -1,143 +0,0 @@
|
||||
// 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
|
@ -1,93 +0,0 @@
|
||||
// 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
|
@ -1,51 +0,0 @@
|
||||
// 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
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user