Compare commits

..

16 Commits

Author SHA1 Message Date
Subv
51d4c772a2 RasterizerCache: Swizzle to a staging buffer before copying it to memory when flushing a texture.
This gives us slightly increased memory safety, writing to out of bounds memory will be caught at the point of write instead of several calls down the line as memory corruption
2018-09-21 13:53:13 -05:00
Subv
2fd06acc8f GPU/DMA: Fixed Tiled->Linear transfers.
We no longer write to out of bounds memory anymore.
2018-09-21 13:53:12 -05:00
Subv
bdb3920753 GPU/DMA: Copy the requested amount of data when doing Linear->Linear 2D transfers. 2018-09-21 13:53:12 -05:00
Subv
bb9eeba670 GPU/DMA: Fixed the Linear->Tiled and Linear->Linear transfer modes.
This fixes the loading bar in Has-Been Heroes.

The Tiled->Tiled transfer mode is not implemented yet and will assert.
2018-09-21 13:53:12 -05:00
Subv
47826fd090 RasterizerGL: Flush dirty cached objects before removing them from the cache.
There might be a case where we draw to a framebuffer (thus making it dirty) and then proceed to overwrite only a portion of it from the CPU. The current code would cause the rest of the modified framebuffer to be discarded.
2018-09-21 13:53:11 -05:00
Subv
b87b8db879 GPU/Engines: Flush and invalidate source/dest regions in Fermi2D and KeplerMemory copies. 2018-09-21 13:53:11 -05:00
Subv
23e1dd3c4d RasterizerCache: Do not flush surfaces that have not been modified.
This should bring the performance closer to what it was before flushing was implemented.
2018-09-21 13:53:10 -05:00
Subv
acfc8d7fc9 RasterizerCache: Convert the S8Z24 format from Z24S8 back to S8Z24 when flushing. 2018-09-21 13:53:10 -05:00
Subv
81afcf3f4e RasterizerCache: Allow flushing compressed formats. 2018-09-21 13:53:10 -05:00
Subv
b27ba353d4 RasterizerCache: Log problematic formats when flushing a surface. 2018-09-21 13:53:09 -05:00
Subv
8474a5adf7 GPU/DMA: Implemented source offset copies for unswizzling Tiled->Linear transfers.
This is used by nouveau to implement glReadPixels.
2018-09-21 13:53:09 -05:00
Subv
ca7eb39b86 RasterizerCache: Remove the glGetError call after a surface flush 2018-09-21 13:53:08 -05:00
Subv
393f2418c5 GPU/DMA: Flush the source memory region before a DMA transfer and invalidate the destination region after the transfer. 2018-09-21 13:53:08 -05:00
Subv
d0a814eaca GPU/DMA: Pass the current rasterizer as a variable when constructing the DMA engine. 2018-09-21 13:53:06 -05:00
Subv
047bbe0881 RasterizerCache: Re-introduced the code to flush the various resource caches. 2018-09-21 13:53:06 -05:00
Subv
181aca9af0 RasterizerCache: Reintroduced code for flushing OpenGL surfaces back to memory.
This is required for a proper implementation of the DMA and 2D engines.
2018-09-21 13:53:05 -05:00
119 changed files with 844 additions and 2559 deletions

4
.gitattributes vendored
View File

@@ -1,4 +0,0 @@
dist/languages/* linguist-vendored
dist/qt_themes/* linguist-vendored
externals/* linguist-vendored
*.h linguist-language=cpp

View File

@@ -24,7 +24,7 @@ matrix:
- os: osx
env: NAME="macos build"
sudo: false
osx_image: xcode10
osx_image: xcode9.3
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"

View File

@@ -123,6 +123,8 @@ else()
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(/DWIN32_LEAN_AND_MEAN)
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
# Tweak optimization settings
@@ -438,12 +440,8 @@ enable_testing()
add_subdirectory(externals)
add_subdirectory(src)
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
if(ENABLE_QT)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
else()
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu-cmd)
endif()
# Set yuzu project as default StartUp Project in Visual Studio
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
# Installation instructions

View File

@@ -163,13 +163,3 @@ artifacts:
name: build
type: zip
deploy:
provider: GitHub
release: $(appveyor_repo_tag_name)
auth_token:
secure: QqePPnXbkzmXct5c8hZ2X5AbsthbI6cS1Sr+VBzcD8oUOIjfWJJKXVAQGUbQAbb0
artifact: update,build
draft: false
prerelease: false
on:
appveyor_repo_tag: true

View File

@@ -79,10 +79,6 @@ u32 AudioRenderer::GetMixBufferCount() const {
return worker_params.mix_buffer_count;
}
Stream::State AudioRenderer::GetStreamState() const {
return stream->GetState();
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};

View File

@@ -170,7 +170,6 @@ public:
u32 GetSampleRate() const;
u32 GetSampleCount() const;
u32 GetMixBufferCount() const;
Stream::State GetStreamState() const;
private:
class VoiceState;

View File

@@ -49,14 +49,9 @@ void Stream::Play() {
}
void Stream::Stop() {
state = State::Stopped;
ASSERT_MSG(false, "Unimplemented");
}
Stream::State Stream::GetState() const {
return state;
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);

View File

@@ -33,12 +33,6 @@ public:
Multi51Channel16,
};
/// Current state of the stream
enum class State {
Stopped,
Playing,
};
/// Callback function type, used to change guest state on a buffer being released
using ReleaseCallback = std::function<void()>;
@@ -78,10 +72,13 @@ public:
/// Gets the number of channels
u32 GetNumChannels() const;
/// Get the state
State GetState() const;
private:
/// Current state of the stream
enum class State {
Stopped,
Playing,
};
/// Plays the next queued buffer in the audio stream, starting playback if necessary
void PlayNextBuffer();

View File

@@ -59,7 +59,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
backlog_fullness);
m_sound_touch.putSamples(in, static_cast<u32>(num_in));

View File

@@ -33,8 +33,6 @@
#define NAND_DIR "nand"
#define SYSDATA_DIR "sysdata"
#define KEYS_DIR "keys"
#define LOAD_DIR "load"
#define DUMP_DIR "dump"
#define LOG_DIR "log"
// Filenames

View File

@@ -705,8 +705,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
#endif
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
// TODO: Put the logs in a better location for each OS

View File

@@ -29,8 +29,6 @@ enum class UserPath {
NANDDir,
RootDir,
SDMCDir,
LoadDir,
DumpDir,
SysDataDir,
UserDir,
};

View File

@@ -183,7 +183,6 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, FS) \
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, IRS) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \

View File

@@ -70,7 +70,6 @@ enum class Class : ClassType {
Service_FS, ///< The FS (Filesystem) service
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_IRS, ///< The IRS service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service

View File

@@ -87,6 +87,14 @@ private:
void SleepCurrentThread(int ms);
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
// Use this function during a spin-wait to make the current thread
// relax while another thread is working. This may be more efficient
// than using events because event functions use kernel calls.
inline void YieldCPU() {
std::this_thread::yield();
}
void SetCurrentThreadName(const char* name);
} // namespace Common

View File

@@ -32,8 +32,6 @@ add_library(core STATIC
file_sys/control_metadata.h
file_sys/directory.h
file_sys/errors.h
file_sys/fsmitm_romfsbuild.cpp
file_sys/fsmitm_romfsbuild.h
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
@@ -61,13 +59,10 @@ add_library(core STATIC
file_sys/vfs.h
file_sys/vfs_concat.cpp
file_sys/vfs_concat.h
file_sys/vfs_layered.cpp
file_sys/vfs_layered.h
file_sys/vfs_offset.cpp
file_sys/vfs_offset.h
file_sys/vfs_real.cpp
file_sys/vfs_real.h
file_sys/vfs_static.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp

View File

@@ -129,8 +129,7 @@ public:
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
auto& current_process = Core::CurrentProcess();
auto** const page_table = current_process->vm_manager.page_table.pointers.data();
auto** const page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
Dynarmic::A64::UserConfig config;
@@ -139,7 +138,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
// Memory
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = current_process->vm_manager.GetAddressSpaceWidth();
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.silently_mirror_page_table = false;
// Multi-process state
@@ -175,7 +174,7 @@ ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} {
ThreadContext ctx{};
ThreadContext ctx;
inner_unicorn.SaveContext(ctx);
PageTableChanged();
LoadContext(ctx);

View File

@@ -64,7 +64,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
if (concat.empty())
return nullptr;
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
return FileSys::ConcatenateFiles(concat, dir->GetName());
}
return vfs->OpenFile(path, FileSys::Mode::Read);
@@ -202,7 +202,7 @@ struct System::Impl {
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())};
const Loader::ResultStatus load_result{app_loader->Load(kernel.CurrentProcess())};
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();

View File

@@ -55,16 +55,16 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index);
#else
arm_interface = std::make_unique<ARM_Unicorn>();
arm_interface = std::make_shared<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
arm_interface = std::make_unique<ARM_Unicorn>();
arm_interface = std::make_shared<ARM_Unicorn>();
}
scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface);
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
}
Cpu::~Cpu() = default;

View File

@@ -76,7 +76,7 @@ public:
private:
void Reschedule();
std::unique_ptr<ARM_Interface> arm_interface;
std::shared_ptr<ARM_Interface> arm_interface;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::shared_ptr<Kernel::Scheduler> scheduler;

View File

@@ -2,14 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/registered_cache.h"
namespace FileSys {
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
BISFactory::BISFactory(VirtualDir nand_root_)
: nand_root(std::move(nand_root_)),
sysnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_shared<RegisteredCache>(
@@ -25,11 +24,4 @@ std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const {
return usrnand_cache;
}
VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
// LayeredFS doesn't work on updates and title id-less homebrew
if (title_id == 0 || (title_id & 0x800) > 0)
return nullptr;
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
}
} // namespace FileSys

View File

@@ -17,17 +17,14 @@ class RegisteredCache;
/// registered caches.
class BISFactory {
public:
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
explicit BISFactory(VirtualDir nand_root);
~BISFactory();
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
VirtualDir GetModificationLoadRoot(u64 title_id) const;
private:
VirtualDir nand_root;
VirtualDir load_root;
std::shared_ptr<RegisteredCache> sysnand_cache;
std::shared_ptr<RegisteredCache> usrnand_cache;

View File

@@ -1,366 +0,0 @@
/*
* Copyright (c) 2018 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Adapted by DarkLordZach for use/interaction with yuzu
*
* Modifications Copyright 2018 yuzu emulator team
* Licensed under GPLv2 or any later version
* Refer to the license.txt file included.
*/
#include <cstring>
#include "common/alignment.h"
#include "common/assert.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
constexpr u64 FS_MAX_PATH = 0x301;
constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200;
// Types for building a RomFS.
struct RomFSHeader {
u64 header_size;
u64 dir_hash_table_ofs;
u64 dir_hash_table_size;
u64 dir_table_ofs;
u64 dir_table_size;
u64 file_hash_table_ofs;
u64 file_hash_table_size;
u64 file_table_ofs;
u64 file_table_size;
u64 file_partition_ofs;
};
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
struct RomFSDirectoryEntry {
u32 parent;
u32 sibling;
u32 child;
u32 file;
u32 hash;
u32 name_size;
};
static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
struct RomFSFileEntry {
u32 parent;
u32 sibling;
u64 offset;
u64 size;
u32 hash;
u32 name_size;
};
static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
struct RomFSBuildFileContext;
struct RomFSBuildDirectoryContext {
std::string path;
u32 cur_path_ofs = 0;
u32 path_len = 0;
u32 entry_offset = 0;
std::shared_ptr<RomFSBuildDirectoryContext> parent;
std::shared_ptr<RomFSBuildDirectoryContext> child;
std::shared_ptr<RomFSBuildDirectoryContext> sibling;
std::shared_ptr<RomFSBuildFileContext> file;
};
struct RomFSBuildFileContext {
std::string path;
u32 cur_path_ofs = 0;
u32 path_len = 0;
u32 entry_offset = 0;
u64 offset = 0;
u64 size = 0;
std::shared_ptr<RomFSBuildDirectoryContext> parent;
std::shared_ptr<RomFSBuildFileContext> sibling;
VirtualFile source;
};
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
u32 hash = parent ^ 123456789;
for (u32 i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);
hash ^= path[start + i];
}
return hash;
}
static u64 romfs_get_hash_table_count(u64 num_entries) {
if (num_entries < 3) {
return 3;
}
if (num_entries < 19) {
return num_entries | 1;
}
u64 count = num_entries;
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
count++;
}
return count;
}
void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
VirtualDir dir;
if (parent->path_len == 0)
dir = root_romfs;
else
dir = root_romfs->GetDirectoryRelative(parent->path);
const auto entries = dir->GetEntries();
for (const auto& kv : entries) {
if (kv.second == VfsEntryType::Directory) {
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
// Set child's path.
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
child->path = parent->path + "/" + kv.first;
// Sanity check on path_len
ASSERT(child->path_len < FS_MAX_PATH);
if (AddDirectory(parent, child)) {
child_dirs.push_back(child);
}
} else {
const auto child = std::make_shared<RomFSBuildFileContext>();
// Set child's path.
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
child->path = parent->path + "/" + kv.first;
// Sanity check on path_len
ASSERT(child->path_len < FS_MAX_PATH);
child->source = root_romfs->GetFileRelative(child->path);
child->size = child->source->GetSize();
AddFile(parent, child);
}
}
for (auto& child : child_dirs) {
this->VisitDirectory(root_romfs, child);
}
}
bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) {
// Check whether it's already in the known directories.
const auto existing = directories.find(dir_ctx->path);
if (existing != directories.end())
return false;
// Add a new directory.
num_dirs++;
dir_table_size +=
sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4);
dir_ctx->parent = parent_dir_ctx;
directories.emplace(dir_ctx->path, dir_ctx);
return true;
}
bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildFileContext> file_ctx) {
// Check whether it's already in the known files.
const auto existing = files.find(file_ctx->path);
if (existing != files.end()) {
return false;
}
// Add a new file.
num_files++;
file_table_size +=
sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4);
file_ctx->parent = parent_dir_ctx;
files.emplace(file_ctx->path, file_ctx);
return true;
}
RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) {
root = std::make_shared<RomFSBuildDirectoryContext>();
root->path = "\0";
directories.emplace(root->path, root);
num_dirs = 1;
dir_table_size = 0x18;
VisitDirectory(base, root);
}
RomFSBuildContext::~RomFSBuildContext() = default;
std::map<u64, VirtualFile> RomFSBuildContext::Build() {
const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
dir_hash_table_size = 4 * dir_hash_table_entry_count;
file_hash_table_size = 4 * file_hash_table_entry_count;
// Assign metadata pointers
RomFSHeader header{};
std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
std::vector<u8> dir_table(dir_table_size);
std::vector<u8> file_table(file_table_size);
std::shared_ptr<RomFSBuildFileContext> cur_file;
// Determine file offsets.
u32 entry_offset = 0;
std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
for (const auto& it : files) {
cur_file = it.second;
file_partition_size = Common::AlignUp(file_partition_size, 16);
cur_file->offset = file_partition_size;
file_partition_size += cur_file->size;
cur_file->entry_offset = entry_offset;
entry_offset += sizeof(RomFSFileEntry) +
Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4);
prev_file = cur_file;
}
// Assign deferred parent/sibling ownership.
for (auto it = files.rbegin(); it != files.rend(); ++it) {
cur_file = it->second;
cur_file->sibling = cur_file->parent->file;
cur_file->parent->file = cur_file;
}
std::shared_ptr<RomFSBuildDirectoryContext> cur_dir;
// Determine directory offsets.
entry_offset = 0;
for (const auto& it : directories) {
cur_dir = it.second;
cur_dir->entry_offset = entry_offset;
entry_offset += sizeof(RomFSDirectoryEntry) +
Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4);
}
// Assign deferred parent/sibling ownership.
for (auto it = directories.rbegin(); it->second != root; ++it) {
cur_dir = it->second;
cur_dir->sibling = cur_dir->parent->child;
cur_dir->parent->child = cur_dir;
}
std::map<u64, VirtualFile> out;
// Populate file tables.
for (const auto& it : files) {
cur_file = it.second;
RomFSFileEntry cur_entry{};
cur_entry.parent = cur_file->parent->entry_offset;
cur_entry.sibling =
cur_file->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
cur_entry.offset = cur_file->offset;
cur_entry.size = cur_file->size;
const auto name_size = cur_file->path_len - cur_file->cur_path_ofs;
const auto hash = romfs_calc_path_hash(cur_file->parent->entry_offset, cur_file->path,
cur_file->cur_path_ofs, name_size);
cur_entry.hash = file_hash_table[hash % file_hash_table_entry_count];
file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
cur_entry.name_size = name_size;
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
Common::AlignUp(cur_entry.name_size, 4));
std::memcpy(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry),
cur_file->path.data() + cur_file->cur_path_ofs, name_size);
}
// Populate dir tables.
for (const auto& it : directories) {
cur_dir = it.second;
RomFSDirectoryEntry cur_entry{};
cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset;
cur_entry.sibling =
cur_dir->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
cur_entry.child =
cur_dir->child == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
cur_entry.file = cur_dir->file == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
const auto name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
const auto hash = romfs_calc_path_hash(cur_dir == root ? 0 : cur_dir->parent->entry_offset,
cur_dir->path, cur_dir->cur_path_ofs, name_size);
cur_entry.hash = dir_hash_table[hash % dir_hash_table_entry_count];
dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
cur_entry.name_size = name_size;
std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
sizeof(RomFSDirectoryEntry));
std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0,
Common::AlignUp(cur_entry.name_size, 4));
std::memcpy(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry),
cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
}
// Set header fields.
header.header_size = sizeof(RomFSHeader);
header.file_hash_table_size = file_hash_table_size;
header.file_table_size = file_table_size;
header.dir_hash_table_size = dir_hash_table_size;
header.dir_table_size = dir_table_size;
header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
std::vector<u8> header_data(sizeof(RomFSHeader));
std::memcpy(header_data.data(), &header, header_data.size());
out.emplace(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
dir_table_size);
std::size_t index = 0;
std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
index += dir_hash_table.size() * sizeof(u32);
std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
index += dir_table.size();
std::memcpy(metadata.data() + index, file_hash_table.data(),
file_hash_table.size() * sizeof(u32));
index += file_hash_table.size() * sizeof(u32);
std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata)));
return out;
}
} // namespace FileSys

View File

@@ -1,70 +0,0 @@
/*
* Copyright (c) 2018 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Adapted by DarkLordZach for use/interaction with yuzu
*
* Modifications Copyright 2018 yuzu emulator team
* Licensed under GPLv2 or any later version
* Refer to the license.txt file included.
*/
#pragma once
#include <map>
#include <memory>
#include <string>
#include <boost/detail/container_fwd.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
struct RomFSBuildDirectoryContext;
struct RomFSBuildFileContext;
struct RomFSDirectoryEntry;
struct RomFSFileEntry;
class RomFSBuildContext {
public:
explicit RomFSBuildContext(VirtualDir base);
~RomFSBuildContext();
// This finalizes the context.
std::map<u64, VirtualFile> Build();
private:
VirtualDir base;
std::shared_ptr<RomFSBuildDirectoryContext> root;
std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
u64 num_dirs = 0;
u64 num_files = 0;
u64 dir_table_size = 0;
u64 file_table_size = 0;
u64 dir_hash_table_size = 0;
u64 file_hash_table_size = 0;
u64 file_partition_size = 0;
void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent);
bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);
bool AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildFileContext> file_ctx);
};
} // namespace FileSys

View File

@@ -11,7 +11,6 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_layered.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
@@ -32,9 +31,8 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{
constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
"Update",
"LayeredFS",
};
std::string FormatPatchTypeName(PatchType type) {
@@ -68,44 +66,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
return exefs;
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
}
auto extracted = ExtractRomFS(romfs);
if (extracted == nullptr) {
return;
}
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
std::vector<VirtualDir> layers;
layers.reserve(patch_dirs.size() + 1);
for (const auto& subdir : patch_dirs) {
auto romfs_dir = subdir->GetSubdirectory("romfs");
if (romfs_dir != nullptr)
layers.push_back(std::move(romfs_dir));
}
layers.push_back(std::move(extracted));
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
if (layered == nullptr) {
return;
}
auto packed = CreateRomFS(std::move(layered));
if (packed == nullptr) {
return;
}
LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
romfs = std::move(packed);
}
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
ContentRecordType type) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
@@ -129,9 +89,6 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
}
}
// LayeredFS
ApplyLayeredFS(romfs, title_id, type);
return romfs;
}
@@ -157,10 +114,6 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
}
}
const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (lfs_dir != nullptr && lfs_dir->GetSize() > 0)
out.insert_or_assign(PatchType::LayeredFS, "");
return out;
}

View File

@@ -26,7 +26,6 @@ std::string FormatTitleVersion(u32 version,
enum class PatchType {
Update,
LayeredFS,
};
std::string FormatPatchTypeName(PatchType type);
@@ -43,7 +42,6 @@ public:
// Currently tracked RomFS patches:
// - Game Updates
// - LayeredFS
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program) const;

View File

@@ -83,12 +83,10 @@ void ProgramMetadata::Print() const {
auto address_space = "Unknown";
switch (npdm_header.address_space_type) {
case ProgramAddressSpaceType::Is36Bit:
case ProgramAddressSpaceType::Is39Bit:
case ProgramAddressSpaceType::Is64Bit:
address_space = "64-bit";
break;
case ProgramAddressSpaceType::Is32Bit:
case ProgramAddressSpaceType::Is32BitNoMap:
address_space = "32-bit";
break;
}

View File

@@ -17,10 +17,8 @@ enum class ResultStatus : u16;
namespace FileSys {
enum class ProgramAddressSpaceType : u8 {
Is32Bit = 0,
Is36Bit = 1,
Is32BitNoMap = 2,
Is39Bit = 3,
Is64Bit = 1,
Is32Bit = 2,
};
enum class ProgramFilePermission : u64 {

View File

@@ -18,10 +18,6 @@
#include "core/loader/loader.h"
namespace FileSys {
// The size of blocks to use when vfs raw copying into nand.
constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
std::string RegisteredCacheEntry::DebugInfo() const {
return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
}
@@ -125,7 +121,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
if (concat.empty())
return nullptr;
file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
file = FileSys::ConcatenateFiles(concat);
}
return file;
@@ -484,8 +480,7 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
auto out = dir->CreateFileRelative(path);
if (out == nullptr)
return InstallResult::ErrorCopyFailed;
return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
: InstallResult::ErrorCopyFailed;
return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed;
}
bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {

View File

@@ -27,7 +27,7 @@ struct ContentRecord;
using NcaID = std::array<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
enum class InstallResult {
Success,

View File

@@ -4,10 +4,8 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
@@ -100,7 +98,7 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file
}
}
VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
VirtualDir ExtractRomFS(VirtualFile file) {
RomFSHeader header{};
if (file->ReadObject(&header) != sizeof(RomFSHeader))
return nullptr;
@@ -119,22 +117,9 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
VirtualDir out = std::move(root);
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
if (out->GetSubdirectories().front()->GetName() == "data" &&
type == RomFSExtractionType::Truncated)
break;
out = out->GetSubdirectories().front();
}
while (out->GetSubdirectory("") != nullptr)
out = out->GetSubdirectory("");
return out;
}
VirtualFile CreateRomFS(VirtualDir dir) {
if (dir == nullptr)
return nullptr;
RomFSBuildContext ctx{dir};
return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
}
} // namespace FileSys

View File

@@ -5,7 +5,6 @@
#pragma once
#include <array>
#include <map>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -13,8 +12,6 @@
namespace FileSys {
struct RomFSHeader;
struct IVFCLevel {
u64_le offset;
u64_le size;
@@ -32,18 +29,8 @@ struct IVFCHeader {
};
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
enum class RomFSExtractionType {
Full, // Includes data directory
Truncated, // Traverses into data directory
};
// Converts a RomFS binary blob to VFS Filesystem
// Returns nullptr on failure
VirtualDir ExtractRomFS(VirtualFile file,
RomFSExtractionType type = RomFSExtractionType::Truncated);
// Converts a VFS filesystem into a RomFS binary
// Returns nullptr on failure
VirtualFile CreateRomFS(VirtualDir dir);
VirtualDir ExtractRomFS(VirtualFile file);
} // namespace FileSys

View File

@@ -399,15 +399,6 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
}
std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const {
std::map<std::string, VfsEntryType, std::less<>> out;
for (const auto& dir : GetSubdirectories())
out.emplace(dir->GetName(), VfsEntryType::Directory);
for (const auto& file : GetFiles())
out.emplace(file->GetName(), VfsEntryType::File);
return out;
}
std::string VfsDirectory::GetFullPath() const {
if (IsRoot())
return GetName();
@@ -463,41 +454,13 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t
return true;
}
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
if (src == nullptr || dest == nullptr)
return false;
if (!dest->Resize(src->GetSize()))
return false;
std::vector<u8> temp(std::min(block_size, src->GetSize()));
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
const auto read = std::min(block_size, src->GetSize() - i);
const auto block = src->Read(temp.data(), read, i);
if (dest->Write(temp.data(), read, i) != read)
return false;
}
return true;
}
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
for (const auto& file : src->GetFiles()) {
const auto out = dest->CreateFile(file->GetName());
if (!VfsRawCopy(file, out, block_size))
return false;
}
for (const auto& dir : src->GetSubdirectories()) {
const auto out = dest->CreateSubdirectory(dir->GetName());
if (!VfsRawCopyD(dir, out, block_size))
return false;
}
return true;
std::vector<u8> data = src->ReadAllBytes();
return dest->WriteBytes(data, 0) == data.size();
}
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {

View File

@@ -4,7 +4,6 @@
#pragma once
#include <map>
#include <memory>
#include <string>
#include <string_view>
@@ -266,10 +265,6 @@ public:
// dest.
virtual bool Copy(std::string_view src, std::string_view dest);
// Gets all of the entries directly in the directory (files and dirs), returning a map between
// item name -> type.
virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
@@ -315,19 +310,13 @@ public:
bool Rename(std::string_view name) override;
};
// Compare the two files, byte-for-byte, in increments specified by block_size
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2,
std::size_t block_size = 0x1000);
// Compare the two files, byte-for-byte, in increments specificed by block_size
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size = 0x200);
// A method that copies the raw data between two different implementations of VirtualFile. If you
// are using the same implementation, it is probably better to use the Copy method in the parent
// directory of src/dest.
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000);
// A method that performs a similar function to VfsRawCopy above, but instead copies entire
// directories. It suffers the same performance penalties as above and an implementation-specific
// Copy should always be preferred.
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000);
bool VfsRawCopy(VirtualFile src, VirtualFile dest);
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
// it attempts to create it and returns the new dir or nullptr on failure.

View File

@@ -5,22 +5,17 @@
#include <algorithm>
#include <utility>
#include "common/assert.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_static.h"
namespace FileSys {
static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& map) {
const auto last_valid = --map.end();
for (auto iter = map.begin(); iter != last_valid;) {
const auto old = iter++;
if (old->first + old->second->GetSize() != iter->first) {
return false;
}
}
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files[0];
return map.begin()->first == 0;
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
@@ -32,48 +27,8 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::s
}
}
ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name)
: files(std::move(files_)), name(std::move(name)) {
ASSERT(VerifyConcatenationMapContinuity(files));
}
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files,
std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files[0];
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
std::map<u64, VirtualFile> files,
std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files.begin()->second;
const auto last_valid = --files.end();
for (auto iter = files.begin(); iter != last_valid;) {
const auto old = iter++;
if (old->first + old->second->GetSize() != iter->first) {
files.emplace(old->first + old->second->GetSize(),
std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first -
old->second->GetSize()));
}
}
// Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
if (files.begin()->first != 0)
files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first));
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
std::string ConcatenatedVfsFile::GetName() const {
if (files.empty())
return "";
@@ -107,7 +62,7 @@ bool ConcatenatedVfsFile::IsReadable() const {
}
std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
auto entry = --files.end();
auto entry = files.end();
for (auto iter = files.begin(); iter != files.end(); ++iter) {
if (iter->first > offset) {
entry = --iter;
@@ -115,17 +70,20 @@ std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t
}
}
if (entry->first + entry->second->GetSize() <= offset)
// Check if the entry should be the last one. The loop above will make it end().
if (entry == files.end() && offset < files.rbegin()->first + files.rbegin()->second->GetSize())
--entry;
if (entry == files.end())
return 0;
const auto read_in =
std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize());
if (length > read_in) {
return entry->second->Read(data, read_in, offset - entry->first) +
Read(data + read_in, length - read_in, offset + read_in);
const auto remaining = entry->second->GetSize() + offset - entry->first;
if (length > remaining) {
return entry->second->Read(data, remaining, offset - entry->first) +
Read(data + remaining, length - remaining, offset + remaining);
}
return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first);
return entry->second->Read(data, length, offset - entry->first);
}
std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
@@ -135,5 +93,4 @@ std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::
bool ConcatenatedVfsFile::Rename(std::string_view name) {
return false;
}
} // namespace FileSys

View File

@@ -4,30 +4,26 @@
#pragma once
#include <map>
#include <memory>
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "core/file_sys/vfs.h"
namespace FileSys {
// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name = "");
// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
// read-only.
class ConcatenatedVfsFile : public VfsFile {
friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
public:
~ConcatenatedVfsFile() override;
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
static VirtualFile MakeConcatenatedFile(std::vector<VirtualFile> files, std::string name);
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
/// gaps with a given filler byte.
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::map<u64, VirtualFile> files,
std::string name);
std::string GetName() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
@@ -40,7 +36,7 @@ public:
private:
// Maps starting offset to file -- more efficient.
std::map<u64, VirtualFile> files;
boost::container::flat_map<u64, VirtualFile> files;
std::string name;
};

View File

@@ -1,132 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "core/file_sys/vfs_layered.h"
namespace FileSys {
LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
: dirs(std::move(dirs)), name(std::move(name)) {}
LayeredVfsDirectory::~LayeredVfsDirectory() = default;
VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs,
std::string name) {
if (dirs.empty())
return nullptr;
if (dirs.size() == 1)
return dirs[0];
return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
}
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
for (const auto& layer : dirs) {
const auto file = layer->GetFileRelative(path);
if (file != nullptr)
return file;
}
return nullptr;
}
std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
std::string_view path) const {
std::vector<VirtualDir> out;
for (const auto& layer : dirs) {
auto dir = layer->GetDirectoryRelative(path);
if (dir != nullptr)
out.push_back(std::move(dir));
}
return MakeLayeredDirectory(std::move(out));
}
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
return GetFileRelative(name);
}
std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetSubdirectory(std::string_view name) const {
return GetDirectoryRelative(name);
}
std::string LayeredVfsDirectory::GetFullPath() const {
return dirs[0]->GetFullPath();
}
std::vector<std::shared_ptr<VfsFile>> LayeredVfsDirectory::GetFiles() const {
std::vector<VirtualFile> out;
for (const auto& layer : dirs) {
for (const auto& file : layer->GetFiles()) {
if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) {
return comp->GetName() == file->GetName();
}) == out.end()) {
out.push_back(file);
}
}
}
return out;
}
std::vector<std::shared_ptr<VfsDirectory>> LayeredVfsDirectory::GetSubdirectories() const {
std::vector<std::string> names;
for (const auto& layer : dirs) {
for (const auto& sd : layer->GetSubdirectories()) {
if (std::find(names.begin(), names.end(), sd->GetName()) == names.end())
names.push_back(sd->GetName());
}
}
std::vector<VirtualDir> out;
out.reserve(names.size());
for (const auto& subdir : names)
out.push_back(GetSubdirectory(subdir));
return out;
}
bool LayeredVfsDirectory::IsWritable() const {
return false;
}
bool LayeredVfsDirectory::IsReadable() const {
return true;
}
std::string LayeredVfsDirectory::GetName() const {
return name.empty() ? dirs[0]->GetName() : name;
}
std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetParentDirectory() const {
return dirs[0]->GetParentDirectory();
}
std::shared_ptr<VfsDirectory> LayeredVfsDirectory::CreateSubdirectory(std::string_view name) {
return nullptr;
}
std::shared_ptr<VfsFile> LayeredVfsDirectory::CreateFile(std::string_view name) {
return nullptr;
}
bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view name) {
return false;
}
bool LayeredVfsDirectory::DeleteFile(std::string_view name) {
return false;
}
bool LayeredVfsDirectory::Rename(std::string_view name_) {
name = name_;
return true;
}
bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -1,50 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "core/file_sys/vfs.h"
namespace FileSys {
// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
// one and falling back to the one after. The highest priority directory (overwrites all others)
// should be element 0 in the dirs vector.
class LayeredVfsDirectory : public VfsDirectory {
LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
public:
~LayeredVfsDirectory() override;
/// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
std::string GetFullPath() const override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
bool IsWritable() const override;
bool IsReadable() const override;
std::string GetName() const override;
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
bool DeleteSubdirectory(std::string_view name) override;
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
std::vector<VirtualDir> dirs;
std::string name;
};
} // namespace FileSys

View File

@@ -413,23 +413,6 @@ std::string RealVfsDirectory::GetFullPath() const {
return out;
}
std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
if (perms == Mode::Append)
return {};
std::map<std::string, VfsEntryType, std::less<>> out;
FileUtil::ForeachDirectoryEntry(
nullptr, path,
[&out](u64* entries_out, const std::string& directory, const std::string& filename) {
const std::string full_path = directory + DIR_SEP + filename;
out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory
: VfsEntryType::File);
return true;
});
return out;
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}

View File

@@ -98,7 +98,6 @@ public:
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
std::string GetFullPath() const override;
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;

View File

@@ -1,79 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <memory>
#include <string_view>
#include "core/file_sys/vfs.h"
namespace FileSys {
class StaticVfsFile : public VfsFile {
public:
explicit StaticVfsFile(u8 value, std::size_t size = 0, std::string name = "",
VirtualDir parent = nullptr)
: value{value}, size{size}, name{std::move(name)}, parent{std::move(parent)} {}
std::string GetName() const override {
return name;
}
std::size_t GetSize() const override {
return size;
}
bool Resize(std::size_t new_size) override {
size = new_size;
return true;
}
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override {
return parent;
}
bool IsWritable() const override {
return false;
}
bool IsReadable() const override {
return true;
}
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override {
const auto read = std::min(length, size - offset);
std::fill(data, data + read, value);
return read;
}
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
return 0;
}
boost::optional<u8> ReadByte(std::size_t offset) const override {
if (offset < size)
return value;
return boost::none;
}
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
const auto read = std::min(length, size - offset);
return std::vector<u8>(read, value);
}
bool Rename(std::string_view new_name) override {
name = new_name;
return true;
}
private:
u8 value;
std::size_t size;
std::string name;
VirtualDir parent;
};
} // namespace FileSys

View File

@@ -3,64 +3,10 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <utility>
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
: data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {}
VectorVfsFile::~VectorVfsFile() = default;
std::string VectorVfsFile::GetName() const {
return name;
}
size_t VectorVfsFile::GetSize() const {
return data.size();
}
bool VectorVfsFile::Resize(size_t new_size) {
data.resize(new_size);
return true;
}
std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const {
return parent;
}
bool VectorVfsFile::IsWritable() const {
return true;
}
bool VectorVfsFile::IsReadable() const {
return true;
}
std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const {
const auto read = std::min(length, data.size() - offset);
std::memcpy(data_, data.data() + offset, read);
return read;
}
std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) {
if (offset + length > data.size())
data.resize(offset + length);
const auto write = std::min(length, data.size() - offset);
std::memcpy(data.data(), data_, write);
return write;
}
bool VectorVfsFile::Rename(std::string_view name_) {
name = name_;
return true;
}
void VectorVfsFile::Assign(std::vector<u8> new_data) {
data = std::move(new_data);
}
VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
std::vector<VirtualDir> dirs_, std::string name_,
VirtualDir parent_)

View File

@@ -8,31 +8,6 @@
namespace FileSys {
// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
class VectorVfsFile : public VfsFile {
public:
explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name = "",
VirtualDir parent = nullptr);
~VectorVfsFile() override;
std::string GetName() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
bool Rename(std::string_view name) override;
virtual void Assign(std::vector<u8> new_data);
private:
std::vector<u8> data;
VirtualDir parent;
std::string name;
};
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
// Vector data is supplied upon construction.
class VectorVfsDirectory : public VfsDirectory {

View File

@@ -37,9 +37,7 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -587,8 +585,7 @@ static void HandleQuery() {
strlen("Xfer:features:read:target.xml:")) == 0) {
SendReply(target_xml);
} else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
const VAddr base_address = Core::CurrentProcess()->vm_manager.GetCodeRegionBaseAddress();
std::string buffer = fmt::format("TextSeg={:0x}", base_address);
std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR);
SendReply(buffer.c_str());
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m";
@@ -896,11 +893,11 @@ static void ReadMemory() {
static u8 reply[GDB_BUFFER_SIZE - 4];
auto start_offset = command_buffer + 1;
const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
const u64 len =
u64 len =
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len);
@@ -909,9 +906,7 @@ static void ReadMemory() {
SendReply("E01");
}
const auto& vm_manager = Core::CurrentProcess()->vm_manager;
if (addr < vm_manager.GetCodeRegionBaseAddress() ||
addr >= vm_manager.GetMapRegionEndAddress()) {
if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) {
return SendReply("E00");
}

View File

@@ -6,6 +6,7 @@
#include <atomic>
#include <string>
#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
@@ -96,7 +97,7 @@ using SharedPtr = boost::intrusive_ptr<T>;
template <typename T>
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
return boost::static_pointer_cast<T>(object);
return boost::static_pointer_cast<T>(std::move(object));
}
return nullptr;
}

View File

@@ -7,13 +7,10 @@
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
@@ -35,21 +32,14 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
process->name = std::move(name);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
process->svc_access_mask.set();
kernel.AppendNewProcess(process);
return process;
}
void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
program_id = metadata.GetTitleID();
vm_manager.Reset(metadata.GetAddressSpaceType());
}
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
for (std::size_t i = 0; i < len; ++i) {
u32 descriptor = kernel_caps[i];
@@ -127,7 +117,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
vm_manager
.MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
.MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Mapped)
.Unwrap();
@@ -138,91 +128,6 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
}
void Process::PrepareForTermination() {
status = ProcessStatus::Exited;
const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->owner_process != this)
continue;
if (thread == GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
}
};
auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0)->GetThreadList());
stop_threads(system.Scheduler(1)->GetThreadList());
stop_threads(system.Scheduler(2)->GetThreadList());
stop_threads(system.Scheduler(3)->GetThreadList());
}
/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
* Returns a tuple of (page, slot, alloc_needed) where:
* page: The index of the first allocated TLS page that has free slots.
* slot: The index of the first free slot in the indicated page.
* alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
*/
static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot(
const std::vector<std::bitset<8>>& tls_slots) {
// Iterate over all the allocated pages, and try to find one where not all slots are used.
for (std::size_t page = 0; page < tls_slots.size(); ++page) {
const auto& page_tls_slots = tls_slots[page];
if (!page_tls_slots.all()) {
// We found a page with at least one free slot, find which slot it is
for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
if (!page_tls_slots.test(slot)) {
return std::make_tuple(page, slot, false);
}
}
}
}
return std::make_tuple(0, 0, true);
}
VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) {
auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots);
const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress();
if (needs_allocation) {
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
// Allocate some memory from the end of the linear heap for this region.
auto& tls_memory = thread.GetTLSMemory();
tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0);
vm_manager.RefreshMemoryBlockMappings(tls_memory.get());
vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0,
Memory::PAGE_SIZE, MemoryState::ThreadLocal);
}
tls_slots[available_page].set(available_slot);
return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE;
}
void Process::FreeTLSSlot(VAddr tls_address) {
const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress();
const VAddr tls_page = tls_base / Memory::PAGE_SIZE;
const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
tls_slots[tls_page].reset(tls_slot);
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
@@ -240,8 +145,8 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
if (target < vm_manager.GetHeapRegionBaseAddress() ||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END ||
target + size < target) {
return ERR_INVALID_ADDRESS;
}
@@ -276,8 +181,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
}
ResultCode Process::HeapFree(VAddr target, u32 size) {
if (target < vm_manager.GetHeapRegionBaseAddress() ||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END ||
target + size < target) {
return ERR_INVALID_ADDRESS;
}

View File

@@ -17,10 +17,6 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
namespace FileSys {
class ProgramMetadata;
}
namespace Kernel {
class KernelCore;
@@ -135,24 +131,6 @@ public:
return HANDLE_TYPE;
}
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
}
/// Gets the unique ID that identifies this particular process.
u32 GetProcessID() const {
return process_id;
}
/**
* Loads process-specifics configuration info with metadata provided
* by an executable.
*
* @param metadata The provided metadata to load process specific info.
*/
void LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
/// Title ID corresponding to the process
u64 program_id;
@@ -176,6 +154,11 @@ public:
u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
u32 is_virtual_address_memory_enabled = 0;
/// Current status of the process
ProcessStatus status;
/// The ID of this process
u32 process_id = 0;
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
@@ -188,42 +171,13 @@ public:
*/
void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
/**
* Prepares a process for termination by stopping all of its threads
* and clearing any other resources.
*/
void PrepareForTermination();
void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
// Marks the next available region as used and returns the address of the slot.
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
// Frees a used TLS slot identified by the given address
void FreeTLSSlot(VAddr tls_address);
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
VMManager vm_manager;
private:
explicit Process(KernelCore& kernel);
~Process() override;
/// Current status of the process
ProcessStatus status;
/// The ID of this process
u32 process_id = 0;
// Memory used to back the allocations in the regular heap. A single vector is used to cover
// the entire virtual address space extents that bound the allocations, including any holes.
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
@@ -243,6 +197,17 @@ private:
std::vector<std::bitset<8>> tls_slots;
std::string name;
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
private:
explicit Process(KernelCore& kernel);
~Process() override;
};
} // namespace Kernel

View File

@@ -17,7 +17,7 @@ namespace Kernel {
std::mutex Scheduler::scheduler_mutex;
Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
@@ -59,9 +59,9 @@ void Scheduler::SwitchContext(Thread* new_thread) {
// Save context for previous thread
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
cpu_core.SaveContext(previous_thread->context);
cpu_core->SaveContext(previous_thread->context);
// Save the TPIDR_EL0 system register in case it was modified.
previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0();
previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0();
if (previous_thread->status == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
@@ -91,10 +91,10 @@ void Scheduler::SwitchContext(Thread* new_thread) {
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
}
cpu_core.LoadContext(new_thread->context);
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core->ClearExclusiveState();
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because

View File

@@ -19,7 +19,7 @@ namespace Kernel {
class Scheduler final {
public:
explicit Scheduler(Core::ARM_Interface& cpu_core);
explicit Scheduler(Core::ARM_Interface* cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
@@ -72,7 +72,7 @@ private:
SharedPtr<Thread> current_thread = nullptr;
Core::ARM_Interface& cpu_core;
Core::ARM_Interface* cpu_core;
static std::mutex scheduler_mutex;
};

View File

@@ -8,7 +8,6 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
@@ -72,8 +71,7 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
shared_memory->other_permissions = other_permissions;
shared_memory->backing_block = std::move(heap_block);
shared_memory->backing_block_offset = offset;
shared_memory->base_address =
kernel.CurrentProcess()->vm_manager.GetHeapRegionBaseAddress() + offset;
shared_memory->base_address = Memory::HEAP_VADDR + offset;
return shared_memory;
}

View File

@@ -51,9 +51,8 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
}
auto& process = *Core::CurrentProcess();
const VAddr heap_base = process.vm_manager.GetHeapRegionBaseAddress();
CASCADE_RESULT(*heap_addr,
process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite));
return RESULT_SUCCESS;
}
@@ -170,7 +169,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
return ERR_INVALID_HANDLE;
}
*process_id = process->GetProcessID();
*process_id = process->process_id;
return RESULT_SUCCESS;
}
@@ -326,27 +325,26 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
const auto& current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->vm_manager;
const auto& vm_manager = Core::CurrentProcess()->vm_manager;
switch (static_cast<GetInfoType>(info_id)) {
case GetInfoType::AllowedCpuIdBitmask:
*result = current_process->allowed_processor_mask;
*result = Core::CurrentProcess()->allowed_processor_mask;
break;
case GetInfoType::AllowedThreadPrioBitmask:
*result = current_process->allowed_thread_priority_mask;
*result = Core::CurrentProcess()->allowed_thread_priority_mask;
break;
case GetInfoType::MapRegionBaseAddr:
*result = vm_manager.GetMapRegionBaseAddress();
*result = Memory::MAP_REGION_VADDR;
break;
case GetInfoType::MapRegionSize:
*result = vm_manager.GetMapRegionSize();
*result = Memory::MAP_REGION_SIZE;
break;
case GetInfoType::HeapRegionBaseAddr:
*result = vm_manager.GetHeapRegionBaseAddress();
*result = Memory::HEAP_VADDR;
break;
case GetInfoType::HeapRegionSize:
*result = vm_manager.GetHeapRegionSize();
*result = Memory::HEAP_SIZE;
break;
case GetInfoType::TotalMemoryUsage:
*result = vm_manager.GetTotalMemoryUsage();
@@ -361,35 +359,22 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = 0;
break;
case GetInfoType::AddressSpaceBaseAddr:
*result = vm_manager.GetCodeRegionBaseAddress();
*result = vm_manager.GetAddressSpaceBaseAddr();
break;
case GetInfoType::AddressSpaceSize: {
const u64 width = vm_manager.GetAddressSpaceWidth();
switch (width) {
case 32:
*result = 0xFFE00000;
break;
case 36:
*result = 0xFF8000000;
break;
case 39:
*result = 0x7FF8000000;
break;
}
case GetInfoType::AddressSpaceSize:
*result = vm_manager.GetAddressSpaceSize();
break;
}
case GetInfoType::NewMapRegionBaseAddr:
*result = vm_manager.GetNewMapRegionBaseAddress();
*result = Memory::NEW_MAP_REGION_VADDR;
break;
case GetInfoType::NewMapRegionSize:
*result = vm_manager.GetNewMapRegionSize();
*result = Memory::NEW_MAP_REGION_SIZE;
break;
case GetInfoType::IsVirtualAddressMemoryEnabled:
*result = current_process->is_virtual_address_memory_enabled;
*result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
break;
case GetInfoType::TitleId:
*result = current_process->program_id;
*result = Core::CurrentProcess()->program_id;
break;
case GetInfoType::PrivilegedProcessId:
LOG_WARNING(Kernel_SVC,
@@ -545,13 +530,35 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
/// Exits the current process
static void ExitProcess() {
auto& current_process = Core::CurrentProcess();
LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id);
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running,
"Process has already exited");
current_process->PrepareForTermination();
Core::CurrentProcess()->status = ProcessStatus::Exited;
auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->owner_process != Core::CurrentProcess())
continue;
if (thread == GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
}
};
auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0)->GetThreadList());
stop_threads(system.Scheduler(1)->GetThreadList());
stop_threads(system.Scheduler(2)->GetThreadList());
stop_threads(system.Scheduler(3)->GetThreadList());
// Kill the current thread
GetCurrentThread()->Stop();
@@ -1032,7 +1039,7 @@ static const FunctionDef SVC_Table[] = {
{0x2B, nullptr, "FlushDataCache"},
{0x2C, nullptr, "MapPhysicalMemory"},
{0x2D, nullptr, "UnmapPhysicalMemory"},
{0x2E, nullptr, "GetFutureThreadInfo"},
{0x2E, nullptr, "GetNextThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, nullptr, "GetResourceLimitLimitValue"},
{0x31, nullptr, "GetResourceLimitCurrentValue"},
@@ -1058,11 +1065,11 @@ static const FunctionDef SVC_Table[] = {
{0x45, nullptr, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
{0x4B, nullptr, "CreateCodeMemory"},
{0x4C, nullptr, "ControlCodeMemory"},
{0x48, nullptr, "AllocateUnsafeMemory"},
{0x49, nullptr, "FreeUnsafeMemory"},
{0x4A, nullptr, "SetUnsafeAllocationLimit"},
{0x4B, nullptr, "CreateJitMemory"},
{0x4C, nullptr, "MapJitMemory"},
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
@@ -1097,7 +1104,7 @@ static const FunctionDef SVC_Table[] = {
{0x6C, nullptr, "SetHardwareBreakPoint"},
{0x6D, nullptr, "GetDebugThreadParam"},
{0x6E, nullptr, "Unknown"},
{0x6F, nullptr, "GetSystemInfo"},
{0x6F, nullptr, "GetMemoryInfo"},
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},

View File

@@ -65,7 +65,10 @@ void Thread::Stop() {
wait_objects.clear();
// Mark the TLS slot in the thread's page as free.
owner_process->FreeTLSSlot(tls_address);
const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
const u64 tls_slot =
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
}
void WaitCurrentThread_Sleep() {
@@ -174,6 +177,32 @@ void Thread::ResumeFromWait() {
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
* Returns a tuple of (page, slot, alloc_needed) where:
* page: The index of the first allocated TLS page that has free slots.
* slot: The index of the first free slot in the indicated page.
* alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
*/
static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
const std::vector<std::bitset<8>>& tls_slots) {
// Iterate over all the allocated pages, and try to find one where not all slots are used.
for (std::size_t page = 0; page < tls_slots.size(); ++page) {
const auto& page_tls_slots = tls_slots[page];
if (!page_tls_slots.all()) {
// We found a page with at least one free slot, find which slot it is
for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
if (!page_tls_slots.test(slot)) {
return std::make_tuple(page, slot, false);
}
}
}
}
return std::make_tuple(0, 0, true);
}
/**
* Resets a thread context, making it ready to be scheduled and run by the CPU
* @param context Thread context to reset
@@ -235,7 +264,32 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->owner_process = owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
// Find the next available TLS index, and mark it as used
auto& tls_slots = owner_process->tls_slots;
auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
if (needs_allocation) {
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
// Allocate some memory from the end of the linear heap for this region.
const std::size_t offset = thread->tls_memory->size();
thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
auto& vm_manager = owner_process->vm_manager;
vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get());
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
thread->tls_memory, 0, Memory::PAGE_SIZE,
MemoryState::ThreadLocal);
}
// Mark the slot as used
tls_slots[available_page].set(available_slot);
thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
available_slot * Memory::TLS_ENTRY_SIZE;
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
@@ -262,9 +316,8 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
SetCurrentPageTable(&owner_process.vm_manager.page_table);
// Initialize new "main" thread
const VAddr stack_top = owner_process.vm_manager.GetTLSIORegionEndAddress();
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
stack_top, &owner_process);
Memory::STACK_AREA_VADDR_END, &owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();

View File

@@ -62,9 +62,6 @@ enum class ThreadWakeupReason {
class Thread final : public WaitObject {
public:
using TLSMemory = std::vector<u8>;
using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param kernel The kernel instance this thread will be created under.
@@ -137,14 +134,6 @@ public:
return thread_id;
}
TLSMemoryPtr& GetTLSMemory() {
return tls_memory;
}
const TLSMemoryPtr& GetTLSMemory() const {
return tls_memory;
}
/**
* Resumes a thread from waiting
*/
@@ -280,7 +269,7 @@ private:
explicit Thread(KernelCore& kernel);
~Thread() override;
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
};
/**

View File

@@ -9,7 +9,6 @@
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
@@ -55,32 +54,30 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
}
VMManager::VMManager() {
// Default to assuming a 39-bit address space. This way we have a sane
// starting point with executables that don't provide metadata.
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
Reset();
}
VMManager::~VMManager() {
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
Reset();
}
void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
Clear();
InitializeMemoryRegionRanges(type);
page_table.Resize(address_space_width);
void VMManager::Reset() {
vma_map.clear();
// Initialize the map with a single free region covering the entire managed space.
VirtualMemoryArea initial_vma;
initial_vma.size = address_space_end;
initial_vma.size = MAX_ADDRESS;
vma_map.emplace(initial_vma.base, initial_vma);
page_table.pointers.fill(nullptr);
page_table.special_regions.clear();
page_table.attributes.fill(Memory::PageType::Unmapped);
UpdatePageTableForVMA(initial_vma);
}
VMManager::VMAHandle VMManager::FindVMA(VAddr target) const {
if (target >= address_space_end) {
if (target >= MAX_ADDRESS) {
return vma_map.end();
} else {
return std::prev(vma_map.upper_bound(target));
@@ -294,7 +291,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) {
const VAddr target_end = target + size;
ASSERT(target_end >= target);
ASSERT(target_end <= address_space_end);
ASSERT(target_end <= MAX_ADDRESS);
ASSERT(size > 0);
VMAIter begin_vma = StripIterConstness(FindVMA(target));
@@ -385,85 +382,6 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
}
}
void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) {
u64 map_region_size = 0;
u64 heap_region_size = 0;
u64 new_map_region_size = 0;
u64 tls_io_region_size = 0;
switch (type) {
case FileSys::ProgramAddressSpaceType::Is32Bit:
address_space_width = 32;
code_region_base = 0x200000;
code_region_end = code_region_base + 0x3FE00000;
map_region_size = 0x40000000;
heap_region_size = 0x40000000;
break;
case FileSys::ProgramAddressSpaceType::Is36Bit:
address_space_width = 36;
code_region_base = 0x8000000;
code_region_end = code_region_base + 0x78000000;
map_region_size = 0x180000000;
heap_region_size = 0x180000000;
break;
case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
address_space_width = 32;
code_region_base = 0x200000;
code_region_end = code_region_base + 0x3FE00000;
map_region_size = 0;
heap_region_size = 0x80000000;
break;
case FileSys::ProgramAddressSpaceType::Is39Bit:
address_space_width = 39;
code_region_base = 0x8000000;
code_region_end = code_region_base + 0x80000000;
map_region_size = 0x1000000000;
heap_region_size = 0x180000000;
new_map_region_size = 0x80000000;
tls_io_region_size = 0x1000000000;
break;
default:
UNREACHABLE_MSG("Invalid address space type specified: {}", static_cast<u32>(type));
return;
}
address_space_base = 0;
address_space_end = 1ULL << address_space_width;
map_region_base = code_region_end;
map_region_end = map_region_base + map_region_size;
heap_region_base = map_region_end;
heap_region_end = heap_region_base + heap_region_size;
new_map_region_base = heap_region_end;
new_map_region_end = new_map_region_base + new_map_region_size;
tls_io_region_base = new_map_region_end;
tls_io_region_end = tls_io_region_base + tls_io_region_size;
if (new_map_region_size == 0) {
new_map_region_base = address_space_base;
new_map_region_end = address_space_end;
}
}
void VMManager::Clear() {
ClearVMAMap();
ClearPageTable();
}
void VMManager::ClearVMAMap() {
vma_map.clear();
}
void VMManager::ClearPageTable() {
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
page_table.special_regions.clear();
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
Memory::PageType::Unmapped);
}
u64 VMManager::GetTotalMemoryUsage() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0xF8000000;
@@ -474,80 +392,14 @@ u64 VMManager::GetTotalHeapUsage() const {
return 0x0;
}
VAddr VMManager::GetAddressSpaceBaseAddress() const {
return address_space_base;
}
VAddr VMManager::GetAddressSpaceEndAddress() const {
return address_space_end;
VAddr VMManager::GetAddressSpaceBaseAddr() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0x8000000;
}
u64 VMManager::GetAddressSpaceSize() const {
return address_space_end - address_space_base;
}
u64 VMManager::GetAddressSpaceWidth() const {
return address_space_width;
}
VAddr VMManager::GetCodeRegionBaseAddress() const {
return code_region_base;
}
VAddr VMManager::GetCodeRegionEndAddress() const {
return code_region_end;
}
u64 VMManager::GetCodeRegionSize() const {
return code_region_end - code_region_base;
}
VAddr VMManager::GetHeapRegionBaseAddress() const {
return heap_region_base;
}
VAddr VMManager::GetHeapRegionEndAddress() const {
return heap_region_end;
}
u64 VMManager::GetHeapRegionSize() const {
return heap_region_end - heap_region_base;
}
VAddr VMManager::GetMapRegionBaseAddress() const {
return map_region_base;
}
VAddr VMManager::GetMapRegionEndAddress() const {
return map_region_end;
}
u64 VMManager::GetMapRegionSize() const {
return map_region_end - map_region_base;
}
VAddr VMManager::GetNewMapRegionBaseAddress() const {
return new_map_region_base;
}
VAddr VMManager::GetNewMapRegionEndAddress() const {
return new_map_region_end;
}
u64 VMManager::GetNewMapRegionSize() const {
return new_map_region_end - new_map_region_base;
}
VAddr VMManager::GetTLSIORegionBaseAddress() const {
return tls_io_region_base;
}
VAddr VMManager::GetTLSIORegionEndAddress() const {
return tls_io_region_end;
}
u64 VMManager::GetTLSIORegionSize() const {
return tls_io_region_end - tls_io_region_base;
LOG_WARNING(Kernel, "(STUBBED) called");
return MAX_ADDRESS;
}
} // namespace Kernel

View File

@@ -12,10 +12,6 @@
#include "core/memory.h"
#include "core/memory_hook.h"
namespace FileSys {
enum class ProgramAddressSpaceType : u8;
}
namespace Kernel {
enum class VMAType : u8 {
@@ -114,6 +110,12 @@ struct VirtualMemoryArea {
*/
class VMManager final {
public:
/**
* The maximum amount of address space managed by the kernel.
* @todo This was selected arbitrarily, and should be verified for Switch OS.
*/
static constexpr VAddr MAX_ADDRESS{0x1000000000ULL};
/**
* A map covering the entirety of the managed address space, keyed by the `base` field of each
* VMA. It must always be modified by splitting or merging VMAs, so that the invariant
@@ -128,7 +130,7 @@ public:
~VMManager();
/// Clears the address space map, re-initializing with a single free area.
void Reset(FileSys::ProgramAddressSpaceType type);
void Reset();
/// Finds the VMA in which the given address is included in, or `vma_map.end()`.
VMAHandle FindVMA(VAddr target) const;
@@ -193,63 +195,12 @@ public:
/// Gets the total heap usage, used by svcGetInfo
u64 GetTotalHeapUsage() const;
/// Gets the address space base address
VAddr GetAddressSpaceBaseAddress() const;
/// Gets the total address space base address, used by svcGetInfo
VAddr GetAddressSpaceBaseAddr() const;
/// Gets the address space end address
VAddr GetAddressSpaceEndAddress() const;
/// Gets the total address space address size in bytes
/// Gets the total address space address size, used by svcGetInfo
u64 GetAddressSpaceSize() const;
/// Gets the address space width in bits.
u64 GetAddressSpaceWidth() const;
/// Gets the base address of the code region.
VAddr GetCodeRegionBaseAddress() const;
/// Gets the end address of the code region.
VAddr GetCodeRegionEndAddress() const;
/// Gets the total size of the code region in bytes.
u64 GetCodeRegionSize() const;
/// Gets the base address of the heap region.
VAddr GetHeapRegionBaseAddress() const;
/// Gets the end address of the heap region;
VAddr GetHeapRegionEndAddress() const;
/// Gets the total size of the heap region in bytes.
u64 GetHeapRegionSize() const;
/// Gets the base address of the map region.
VAddr GetMapRegionBaseAddress() const;
/// Gets the end address of the map region.
VAddr GetMapRegionEndAddress() const;
/// Gets the total size of the map region in bytes.
u64 GetMapRegionSize() const;
/// Gets the base address of the new map region.
VAddr GetNewMapRegionBaseAddress() const;
/// Gets the end address of the new map region.
VAddr GetNewMapRegionEndAddress() const;
/// Gets the total size of the new map region in bytes.
u64 GetNewMapRegionSize() const;
/// Gets the base address of the TLS IO region.
VAddr GetTLSIORegionBaseAddress() const;
/// Gets the end address of the TLS IO region.
VAddr GetTLSIORegionEndAddress() const;
/// Gets the total size of the TLS IO region in bytes.
u64 GetTLSIORegionSize() const;
/// Each VMManager has its own page table, which is set as the main one when the owning process
/// is scheduled.
Memory::PageTable page_table;
@@ -289,36 +240,5 @@ private:
/// Updates the pages corresponding to this VMA so they match the VMA's attributes.
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
/// Initializes memory region ranges to adhere to a given address space type.
void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type);
/// Clears the underlying map and page table.
void Clear();
/// Clears out the VMA map, unmapping any previously mapped ranges.
void ClearVMAMap();
/// Clears out the page table
void ClearPageTable();
u32 address_space_width = 0;
VAddr address_space_base = 0;
VAddr address_space_end = 0;
VAddr code_region_base = 0;
VAddr code_region_end = 0;
VAddr heap_region_base = 0;
VAddr heap_region_end = 0;
VAddr map_region_base = 0;
VAddr map_region_end = 0;
VAddr new_map_region_base = 0;
VAddr new_map_region_end = 0;
VAddr tls_io_region_base = 0;
VAddr tls_io_region_end = 0;
};
} // namespace Kernel

View File

@@ -69,7 +69,7 @@ private:
template <>
inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) {
if (object != nullptr && object->IsWaitable()) {
return boost::static_pointer_cast<WaitObject>(object);
return boost::static_pointer_cast<WaitObject>(std::move(object));
}
return nullptr;
}

View File

@@ -10,7 +10,6 @@
#include "common/alignment.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -26,7 +25,7 @@ public:
{0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"},
{1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"},
{2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"},
{3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"},
{3, nullptr, "GetAudioRendererState"},
{4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"},
{5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"},
{6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"},
@@ -63,13 +62,6 @@ private:
LOG_DEBUG(Service_Audio, "called");
}
void GetAudioRendererState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
LOG_DEBUG(Service_Audio, "called");
}
void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);

View File

@@ -2,17 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <cstring>
#include <ctime>
#include <fmt/time.h>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/fatal/fatal.h"
#include "core/hle/service/fatal/fatal_p.h"
#include "core/hle/service/fatal/fatal_u.h"
@@ -24,142 +15,16 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
Module::Interface::~Interface() = default;
struct FatalInfo {
std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or
// not(find a game which has non zero valeus)
u64_le unk0{};
u64_le unk1{};
u64_le unk2{};
u64_le unk3{};
u64_le unk4{};
u64_le unk5{};
u64_le unk6{};
std::array<u64_le, 32> backtrace{};
u64_le unk7{};
u64_le unk8{};
u32_le backtrace_size{};
u32_le unk9{};
u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
};
static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
enum class FatalType : u32 {
ErrorReportAndScreen = 0,
ErrorReport = 1,
ErrorScreen = 2,
};
static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
const auto title_id = Core::CurrentProcess()->program_id;
std::string crash_report =
fmt::format("Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"
"Result: 0x{:X} ({:04}-{:04d})\n"
"\n",
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
2000 + static_cast<u32>(error_code.module.Value()),
static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7);
if (info.backtrace_size != 0x0) {
crash_report += "Registers:\n";
// TODO(ogniK): This is just a guess, find a game which actually has non zero values
for (size_t i = 0; i < info.registers.size(); i++) {
crash_report +=
fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
}
crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0);
crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1);
crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2);
crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3);
crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4);
crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5);
crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6);
crash_report += "\nBacktrace:\n";
for (size_t i = 0; i < info.backtrace_size; i++) {
crash_report +=
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
}
crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7);
crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8);
crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9);
crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
}
LOG_ERROR(Service_Fatal, "{}", crash_report);
const std::string crashreport_dir =
FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs";
if (!FileUtil::CreateFullPath(crashreport_dir)) {
LOG_ERROR(
Service_Fatal,
"Unable to create crash report directory. Possible log directory permissions issue.");
return;
}
const std::time_t t = std::time(nullptr);
const std::string crashreport_filename =
fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
auto file = FileUtil::IOFile(crashreport_filename, "wb");
if (file.IsOpen()) {
file.WriteString(crash_report);
LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
} else {
LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
}
}
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
switch (fatal_type) {
case FatalType::ErrorReportAndScreen:
GenerateErrorReport(error_code, info);
[[fallthrough]];
case FatalType::ErrorScreen:
// Since we have no fatal:u error screen. We should just kill execution instead
ASSERT(false);
break;
// Should not throw a fatal screen but should generate an error report
case FatalType::ErrorReport:
GenerateErrorReport(error_code, info);
break;
};
}
void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp{ctx};
auto error_code = rp.Pop<ResultCode>();
ThrowFatalError(error_code, FatalType::ErrorScreen, {});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
auto error_code = rp.Pop<ResultCode>();
auto fatal_type = rp.PopEnum<FatalType>();
ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy
u32 error_code = rp.Pop<u32>();
LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
auto error_code = rp.Pop<ResultCode>();
auto fatal_type = rp.PopEnum<FatalType>();
auto fatal_info = ctx.ReadBuffer();
FatalInfo info{};
ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
ThrowFatalError(error_code, fatal_type, info);
LOG_WARNING(Service_Fatal, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}

View File

@@ -15,7 +15,6 @@ public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
~Interface() override;
void ThrowFatal(Kernel::HLERequestContext& ctx);
void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);

View File

@@ -8,7 +8,7 @@ namespace Service::Fatal {
Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") {
static const FunctionInfo functions[] = {
{0, &Fatal_U::ThrowFatal, "ThrowFatal"},
{0, nullptr, "ThrowFatal"},
{1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
{2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"},
};

View File

@@ -343,15 +343,6 @@ std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() {
return sdmc_factory->GetSDMCContents();
}
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
if (bis_factory == nullptr)
return nullptr;
return bis_factory->GetModificationLoadRoot(title_id);
}
void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
@@ -363,11 +354,9 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) {
FileSys::Mode::ReadWrite);
auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir),
FileSys::Mode::ReadWrite);
auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
FileSys::Mode::ReadWrite);
if (bis_factory == nullptr)
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory);
if (save_data_factory == nullptr)
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
if (sdmc_factory == nullptr)

View File

@@ -52,8 +52,6 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.
void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true);

View File

@@ -2,11 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/swap.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/service/hid/irs.h"
namespace Service::HID {
@@ -14,145 +9,28 @@ namespace Service::HID {
IRS::IRS() : ServiceFramework{"irs"} {
// clang-format off
static const FunctionInfo functions[] = {
{302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
{303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"},
{304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"},
{305, &IRS::StopImageProcessor, "StopImageProcessor"},
{306, &IRS::RunMomentProcessor, "RunMomentProcessor"},
{307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"},
{308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"},
{309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"},
{310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"},
{311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"},
{312, &IRS::RunPointingProcessor, "RunPointingProcessor"},
{313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"},
{314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"},
{315, &IRS::SetFunctionLevel, "SetFunctionLevel"},
{316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"},
{317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"},
{318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"},
{319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"},
{302, nullptr, "ActivateIrsensor"},
{303, nullptr, "DeactivateIrsensor"},
{304, nullptr, "GetIrsensorSharedMemoryHandle"},
{305, nullptr, "StopImageProcessor"},
{306, nullptr, "RunMomentProcessor"},
{307, nullptr, "RunClusteringProcessor"},
{308, nullptr, "RunImageTransferProcessor"},
{309, nullptr, "GetImageTransferProcessorState"},
{310, nullptr, "RunTeraPluginProcessor"},
{311, nullptr, "GetNpadIrCameraHandle"},
{312, nullptr, "RunPointingProcessor"},
{313, nullptr, "SuspendImageProcessor"},
{314, nullptr, "CheckFirmwareVersion"},
{315, nullptr, "SetFunctionLevel"},
{316, nullptr, "RunImageTransferExProcessor"},
{317, nullptr, "RunIrLedProcessor"},
{318, nullptr, "StopImageProcessorAsync"},
{319, nullptr, "ActivateIrsensorWithFunctionLevel"},
};
// clang-format on
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
shared_mem = Kernel::SharedMemory::Create(
kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
}
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(shared_mem);
LOG_DEBUG(Service_IRS, "called");
}
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(CoreTiming::GetTicks());
rb.PushRaw<u32>(0);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(device_handle);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_IRS, "(STUBBED) called");
}
IRS::~IRS() = default;

View File

@@ -4,41 +4,14 @@
#pragma once
#include "core/hle/kernel/object.h"
#include "core/hle/service/service.h"
namespace Kernel {
class SharedMemory;
}
namespace Service::HID {
class IRS final : public ServiceFramework<IRS> {
public:
explicit IRS();
~IRS() override;
private:
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void StopImageProcessor(Kernel::HLERequestContext& ctx);
void RunMomentProcessor(Kernel::HLERequestContext& ctx);
void RunClusteringProcessor(Kernel::HLERequestContext& ctx);
void RunImageTransferProcessor(Kernel::HLERequestContext& ctx);
void GetImageTransferProcessorState(Kernel::HLERequestContext& ctx);
void RunTeraPluginProcessor(Kernel::HLERequestContext& ctx);
void GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx);
void RunPointingProcessor(Kernel::HLERequestContext& ctx);
void SuspendImageProcessor(Kernel::HLERequestContext& ctx);
void CheckFirmwareVersion(Kernel::HLERequestContext& ctx);
void SetFunctionLevel(Kernel::HLERequestContext& ctx);
void RunImageTransferExProcessor(Kernel::HLERequestContext& ctx);
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
const u32 device_handle{0xABCD};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/hid/hid.h"

View File

@@ -4,7 +4,6 @@
#include <chrono>
#include <ctime>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/service/nim/nim.h"

View File

@@ -2,11 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/service/sm/controller.h"

View File

@@ -103,7 +103,6 @@ public:
}
private:
u32 ssl_version{};
void CreateContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
@@ -113,9 +112,10 @@ private:
}
void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SSL, "called");
LOG_WARNING(Service_SSL, "(STUBBED) called");
IPC::RequestParser rp{ctx};
ssl_version = rp.Pop<u32>();
u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -14,9 +14,11 @@
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/nso.h"
#include "core/memory.h"
namespace Loader {
@@ -86,7 +88,8 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
return FileType::Error;
}
ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) {
ResultStatus AppLoader_DeconstructedRomDirectory::Load(
Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
@@ -124,16 +127,12 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
metadata.Print();
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
return ResultStatus::Error32BitISA;
}
process.LoadFromMetadata(metadata);
// Load NSO modules
const VAddr base_address = process.vm_manager.GetCodeRegionBaseAddress();
VAddr next_load_addr = base_address;
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const FileSys::VirtualFile module_file = dir->GetFile(module);
@@ -146,7 +145,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
}
}
process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
auto& kernel = Core::System::GetInstance().Kernel();
process->program_id = metadata.GetTitleID();
process->svc_access_mask.set();
process->resource_limit =
kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
metadata.GetMainThreadStackSize());
// Find the RomFS by searching for a ".romfs" file in this directory
const auto& files = dir->GetFiles();

View File

@@ -7,6 +7,7 @@
#include <string>
#include "common/common_types.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/object.h"
#include "core/loader/loader.h"
namespace Loader {
@@ -37,7 +38,7 @@ public:
return IdentifyType(file);
}
ResultStatus Load(Kernel::Process& process) override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;

View File

@@ -12,7 +12,7 @@
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/elf.h"
#include "core/memory.h"
@@ -189,7 +189,7 @@ private:
u32* sectionAddrs;
bool relocate;
VAddr entryPoint;
u32 entryPoint;
public:
explicit ElfReader(void* ptr);
@@ -205,13 +205,13 @@ public:
ElfMachine GetMachine() const {
return (ElfMachine)(header->e_machine);
}
VAddr GetEntryPoint() const {
u32 GetEntryPoint() const {
return entryPoint;
}
u32 GetFlags() const {
return (u32)(header->e_flags);
}
SharedPtr<CodeSet> LoadInto(VAddr vaddr);
SharedPtr<CodeSet> LoadInto(u32 vaddr);
int GetNumSegments() const {
return (int)(header->e_phnum);
@@ -274,7 +274,7 @@ const char* ElfReader::GetSectionName(int section) const {
return nullptr;
}
SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) {
LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
// Should we relocate?
@@ -289,11 +289,11 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
// First pass : Get the bits into RAM
const VAddr base_addr = relocate ? vaddr : 0;
u32 base_addr = relocate ? vaddr : 0;
u64 total_image_size = 0;
u32 total_image_size = 0;
for (unsigned int i = 0; i < header->e_phnum; ++i) {
const Elf32_Phdr* p = &segments[i];
Elf32_Phdr* p = &segments[i];
if (p->p_type == PT_LOAD) {
total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
}
@@ -306,7 +306,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, "");
for (unsigned int i = 0; i < header->e_phnum; ++i) {
const Elf32_Phdr* p = &segments[i];
Elf32_Phdr* p = &segments[i];
LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
p->p_vaddr, p->p_filesz, p->p_memsz);
@@ -333,8 +333,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) {
continue;
}
const VAddr segment_addr = base_addr + p->p_vaddr;
const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
u32 segment_addr = base_addr + p->p_vaddr;
u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
codeset_segment->offset = current_image_position;
codeset_segment->addr = segment_addr;
@@ -387,7 +387,7 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded)
return ResultStatus::ErrorAlreadyLoaded;
@@ -395,13 +395,19 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
if (buffer.size() != file->GetSize())
return ResultStatus::ErrorIncorrectELFFileSize;
const VAddr base_address = process.vm_manager.GetCodeRegionBaseAddress();
ElfReader elf_reader(&buffer[0]);
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address);
SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR);
codeset->name = file->GetName();
process.LoadModule(codeset, codeset->entrypoint);
process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE);
process->LoadModule(codeset, codeset->entrypoint);
process->svc_access_mask.set();
// Attach the default resource limit (APPLICATION) to the process
auto& kernel = Core::System::GetInstance().Kernel();
process->resource_limit =
kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -8,6 +8,9 @@
#include "common/common_types.h"
#include "core/loader/loader.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// Loader namespace
namespace Loader {
/// Loads an ELF/AXF file
@@ -26,7 +29,7 @@ public:
return IdentifyType(file);
}
ResultStatus Load(Kernel::Process& process) override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
};
} // namespace Loader

View File

@@ -12,6 +12,7 @@
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
#include "core/hle/kernel/object.h"
namespace Kernel {
struct AddressMapping;
@@ -135,7 +136,7 @@ public:
* @param process The newly created process.
* @return The status result of the operation.
*/
virtual ResultStatus Load(Kernel::Process& process) = 0;
virtual ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) = 0;
/**
* Loads the system mode that this application needs.

View File

@@ -41,7 +41,7 @@ FileType AppLoader_NAX::GetFileType() {
return IdentifyTypeImpl(*nax);
}
ResultStatus AppLoader_NAX::Load(Kernel::Process& process) {
ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}

View File

@@ -33,7 +33,7 @@ public:
FileType GetFileType() override;
ResultStatus Load(Kernel::Process& process) override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadProgramId(u64& out_program_id) override;

View File

@@ -30,7 +30,7 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
ResultStatus AppLoader_NCA::Load(Kernel::Process& process) {
ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}

View File

@@ -6,6 +6,7 @@
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
#include "core/hle/kernel/object.h"
#include "core/loader/loader.h"
namespace FileSys {
@@ -33,7 +34,7 @@ public:
return IdentifyType(file);
}
ResultStatus Load(Kernel::Process& process) override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;

View File

@@ -16,7 +16,7 @@
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nro.h"
#include "core/memory.h"
@@ -175,19 +175,23 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
return true;
}
ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
// Load NRO
const VAddr base_address = process.vm_manager.GetCodeRegionBaseAddress();
static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR};
if (!LoadNro(file, base_address)) {
if (!LoadNro(file, base_addr)) {
return ResultStatus::ErrorLoadingNRO;
}
process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
auto& kernel = Core::System::GetInstance().Kernel();
process->svc_access_mask.set();
process->resource_limit =
kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(base_addr, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -6,6 +6,7 @@
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/loader/linker.h"
#include "core/loader/loader.h"
@@ -32,7 +33,7 @@ public:
return IdentifyType(file);
}
ResultStatus Load(Kernel::Process& process) override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadProgramId(u64& out_program_id) override;

View File

@@ -13,7 +13,7 @@
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nso.h"
#include "core/memory.h"
@@ -153,17 +153,21 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) {
return load_base + image_size;
}
ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
// Load module
const VAddr base_address = process.vm_manager.GetCodeRegionBaseAddress();
LoadModule(file, base_address);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
LoadModule(file, Memory::PROCESS_IMAGE_VADDR);
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR);
process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
auto& kernel = Core::System::GetInstance().Kernel();
process->svc_access_mask.set();
process->resource_limit =
kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, Kernel::THREADPRIO_DEFAULT,
Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -4,7 +4,9 @@
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/loader/linker.h"
#include "core/loader/loader.h"
@@ -28,7 +30,7 @@ public:
static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base);
ResultStatus Load(Kernel::Process& process) override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
};
} // namespace Loader

View File

@@ -10,6 +10,8 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
@@ -60,7 +62,7 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}

View File

@@ -35,7 +35,7 @@ public:
return IdentifyType(file);
}
ResultStatus Load(Kernel::Process& process) override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadProgramId(u64& out_program_id) override;

View File

@@ -9,6 +9,8 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/process.h"
#include "core/loader/nca.h"
#include "core/loader/xci.h"
@@ -44,7 +46,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}

View File

@@ -35,7 +35,7 @@ public:
return IdentifyType(file);
}
ResultStatus Load(Kernel::Process& process) override;
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadProgramId(u64& out_program_id) override;

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstring>
#include <utility>
@@ -14,11 +15,11 @@
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/lock.h"
#include "core/memory.h"
#include "core/memory_setup.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Memory {
@@ -40,21 +41,6 @@ PageTable* GetCurrentPageTable() {
return current_page_table;
}
PageTable::PageTable() = default;
PageTable::PageTable(std::size_t address_space_width_in_bits) {
Resize(address_space_width_in_bits);
}
PageTable::~PageTable() = default;
void PageTable::Resize(std::size_t address_space_width_in_bits) {
const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS);
pointers.resize(num_page_table_entries);
attributes.resize(num_page_table_entries);
}
static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) {
LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
(base + size) * PAGE_SIZE);
@@ -64,7 +50,7 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
VAddr end = base + size;
while (base != end) {
ASSERT_MSG(base < page_table.pointers.size(), "out of range mapping at {:016X}", base);
ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at {:016X}", base);
page_table.attributes[base] = type;
page_table.pointers[base] = memory;
@@ -337,7 +323,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
return;
}
const VAddr end = start + size;
VAddr end = start + size;
const auto CheckRegion = [&](VAddr region_start, VAddr region_end) {
if (start >= region_end || end <= region_start) {
@@ -347,7 +333,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
const VAddr overlap_start = std::max(start, region_start);
const VAddr overlap_end = std::min(end, region_end);
const VAddr overlap_size = overlap_end - overlap_start;
const u64 overlap_size = overlap_end - overlap_start;
auto& rasterizer = system_instance.Renderer().Rasterizer();
switch (mode) {
@@ -363,10 +349,8 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
}
};
const auto& vm_manager = Core::CurrentProcess()->vm_manager;
CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress());
CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress());
CheckRegion(PROCESS_IMAGE_VADDR, PROCESS_IMAGE_VADDR_END);
CheckRegion(HEAP_VADDR, HEAP_VADDR_END);
}
u8 Read8(const VAddr addr) {

View File

@@ -4,10 +4,10 @@
#pragma once
#include <array>
#include <cstddef>
#include <string>
#include <tuple>
#include <vector>
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
#include "core/memory_hook.h"
@@ -23,8 +23,10 @@ namespace Memory {
* be mapped.
*/
constexpr std::size_t PAGE_BITS = 12;
constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
constexpr u64 PAGE_SIZE = 1 << PAGE_BITS;
constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
constexpr std::size_t ADDRESS_SPACE_BITS = 36;
constexpr std::size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS);
enum class PageType : u8 {
/// Page is unmapped and should cause an access error.
@@ -60,39 +62,32 @@ struct SpecialRegion {
* mimics the way a real CPU page table works.
*/
struct PageTable {
explicit PageTable();
explicit PageTable(std::size_t address_space_width_in_bits);
~PageTable();
/**
* Resizes the page table to be able to accomodate enough pages within
* a given address space.
*
* @param address_space_width_in_bits The address size width in bits.
* Array of memory pointers backing each page. An entry can only be non-null if the
* corresponding entry in the `attributes` array is of type `Memory`.
*/
void Resize(std::size_t address_space_width_in_bits);
std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers;
/**
* Vector of memory pointers backing each page. An entry can only be non-null if the
* corresponding entry in the `attributes` vector is of type `Memory`.
*/
std::vector<u8*> pointers;
/**
* Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is
* of type `Special`.
* Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of
* type `Special`.
*/
boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions;
/**
* Vector 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.
*/
std::vector<PageType> attributes;
std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes;
};
/// Virtual user-space memory regions
enum : VAddr {
/// Where the application text, data and bss reside.
PROCESS_IMAGE_VADDR = 0x08000000,
PROCESS_IMAGE_MAX_SIZE = 0x08000000,
PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE,
/// Read-only page containing kernel and system configuration values.
CONFIG_MEMORY_VADDR = 0x1FF80000,
CONFIG_MEMORY_SIZE = 0x00001000,
@@ -103,12 +98,36 @@ enum : VAddr {
SHARED_PAGE_SIZE = 0x00001000,
SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE,
/// TLS (Thread-Local Storage) related.
/// Area where TLS (Thread-Local Storage) buffers are allocated.
TLS_AREA_VADDR = 0x40000000,
TLS_ENTRY_SIZE = 0x200,
TLS_AREA_SIZE = 0x10000000,
TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
/// Application stack
STACK_AREA_VADDR = TLS_AREA_VADDR_END,
STACK_AREA_SIZE = 0x10000000,
STACK_AREA_VADDR_END = STACK_AREA_VADDR + STACK_AREA_SIZE,
DEFAULT_STACK_SIZE = 0x100000,
/// Application heap
/// Size is confirmed to be a static value on fw 3.0.0
HEAP_VADDR = 0x108000000,
HEAP_SIZE = 0x180000000,
HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE,
/// New map region
/// Size is confirmed to be a static value on fw 3.0.0
NEW_MAP_REGION_VADDR = HEAP_VADDR_END,
NEW_MAP_REGION_SIZE = 0x80000000,
NEW_MAP_REGION_VADDR_END = NEW_MAP_REGION_VADDR + NEW_MAP_REGION_SIZE,
/// Map region
/// Size is confirmed to be a static value on fw 3.0.0
MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END,
MAP_REGION_SIZE = 0x1000000000,
MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE,
/// Kernel Virtual Address Range
KERNEL_REGION_VADDR = 0xFFFFFF8000000000,
KERNEL_REGION_SIZE = 0x7FFFE00000,

View File

@@ -2,8 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/memory.h"
@@ -18,10 +16,9 @@ TestEnvironment::TestEnvironment(bool mutable_memory_)
Core::CurrentProcess() = Kernel::Process::Create(kernel, "");
page_table = &Core::CurrentProcess()->vm_manager.page_table;
std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
page_table->pointers.fill(nullptr);
page_table->special_regions.clear();
std::fill(page_table->attributes.begin(), page_table->attributes.end(),
Memory::PageType::Unmapped);
page_table->attributes.fill(Memory::PageType::Unmapped);
Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory);
Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory);

View File

@@ -4,11 +4,13 @@
#include "core/memory.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
namespace Tegra::Engines {
Fermi2D::Fermi2D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer} {}
void Fermi2D::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
@@ -52,6 +54,12 @@ void Fermi2D::HandleSurfaceCopy() {
return;
}
rasterizer.FlushRegion(source_cpu, src_bytes_per_pixel * regs.src.width * regs.src.height);
// We have to invalidate the destination region to evict any outdated surfaces from the cache.
// We do this before actually writing the new data because the destination address might contain
// a dirty surface that will have to be written back to memory.
rasterizer.InvalidateRegion(dest_cpu, dst_bytes_per_pixel * regs.dst.width * regs.dst.height);
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);

View File

@@ -12,6 +12,10 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
#define FERMI2D_REG_INDEX(field_name) \
@@ -19,7 +23,7 @@ namespace Tegra::Engines {
class Fermi2D final {
public:
explicit Fermi2D(MemoryManager& memory_manager);
explicit Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
~Fermi2D() = default;
/// Write the value to the register identified by method.
@@ -94,6 +98,8 @@ public:
MemoryManager& memory_manager;
private:
VideoCore::RasterizerInterface& rasterizer;
/// Performs the copy from the source surface to the destination surface as configured in the
/// registers.
void HandleSurfaceCopy();

View File

@@ -5,10 +5,14 @@
#include "common/logging/log.h"
#include "core/memory.h"
#include "video_core/engines/kepler_memory.h"
#include "video_core/rasterizer_interface.h"
namespace Tegra::Engines {
KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
KeplerMemory::KeplerMemory(VideoCore::RasterizerInterface& rasterizer,
MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer} {}
KeplerMemory::~KeplerMemory() = default;
void KeplerMemory::WriteReg(u32 method, u32 value) {
@@ -37,6 +41,11 @@ void KeplerMemory::ProcessData(u32 data) {
VAddr dest_address =
*memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32));
// We have to invalidate the destination region to evict any outdated surfaces from the cache.
// We do this before actually writing the new data because the destination address might contain
// a dirty surface that will have to be written back to memory.
rasterizer.InvalidateRegion(dest_address, sizeof(u32));
Memory::Write32(dest_address, data);
state.write_offset++;

View File

@@ -11,6 +11,10 @@
#include "common/common_types.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
#define KEPLERMEMORY_REG_INDEX(field_name) \
@@ -18,7 +22,7 @@ namespace Tegra::Engines {
class KeplerMemory final {
public:
KeplerMemory(MemoryManager& memory_manager);
KeplerMemory(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
~KeplerMemory();
/// Write the value to the register identified by method.
@@ -72,6 +76,7 @@ public:
private:
MemoryManager& memory_manager;
VideoCore::RasterizerInterface& rasterizer;
void ProcessData(u32 data);
};

View File

@@ -41,7 +41,6 @@ public:
static constexpr std::size_t NumCBData = 16;
static constexpr std::size_t NumVertexArrays = 32;
static constexpr std::size_t NumVertexAttributes = 32;
static constexpr std::size_t NumTextureSamplers = 32;
static constexpr std::size_t MaxShaderProgram = 6;
static constexpr std::size_t MaxShaderStage = 5;
// Maximum number of const buffers per shader stage.
@@ -462,11 +461,7 @@ public:
u32 entry;
} macros;
INSERT_PADDING_WORDS(0x189);
u32 tfb_enabled;
INSERT_PADDING_WORDS(0x2E);
INSERT_PADDING_WORDS(0x1B8);
RenderTargetConfig rt[NumRenderTargets];
@@ -599,9 +594,7 @@ public:
u32 depth_write_enabled;
u32 alpha_test_enabled;
INSERT_PADDING_WORDS(0x6);
INSERT_PADDING_WORDS(0x7);
u32 d3d_cull_mode;
@@ -642,11 +635,7 @@ public:
u32 vb_element_base;
INSERT_PADDING_WORDS(0x38);
float point_size;
INSERT_PADDING_WORDS(0x7);
INSERT_PADDING_WORDS(0x40);
u32 zeta_enable;
@@ -988,7 +977,6 @@ private:
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(macros, 0x45);
ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
ASSERT_REG_POSITION(rt, 0x200);
ASSERT_REG_POSITION(viewport_transform[0], 0x280);
ASSERT_REG_POSITION(viewport, 0x300);
@@ -1008,7 +996,6 @@ ASSERT_REG_POSITION(zeta_height, 0x48b);
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB);
ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
ASSERT_REG_POSITION(depth_test_func, 0x4C3);
ASSERT_REG_POSITION(blend, 0x4CF);
@@ -1022,7 +1009,6 @@ ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(point_size, 0x546);
ASSERT_REG_POSITION(zeta_enable, 0x54E);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);

View File

@@ -2,29 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/core.h"
#include "video_core/engines/maxwell_compute.h"
namespace Tegra {
namespace Engines {
void MaxwellCompute::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid MaxwellCompute register, increase the size of the Regs structure");
regs.reg_array[method] = value;
switch (method) {
case MAXWELL_COMPUTE_REG_INDEX(compute): {
LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented");
UNREACHABLE();
break;
}
default:
break;
}
}
void MaxwellCompute::WriteReg(u32 method, u32 value) {}
} // namespace Engines
} // namespace Tegra

View File

@@ -4,53 +4,17 @@
#pragma once
#include <array>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Tegra::Engines {
#define MAXWELL_COMPUTE_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32))
class MaxwellCompute final {
public:
MaxwellCompute() = default;
~MaxwellCompute() = default;
struct Regs {
static constexpr std::size_t NUM_REGS = 0xCF8;
union {
struct {
INSERT_PADDING_WORDS(0x281);
union {
u32 compute_end;
BitField<0, 1, u32> unknown;
} compute;
INSERT_PADDING_WORDS(0xA76);
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
"MaxwellCompute Regs has wrong size");
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value);
};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MaxwellCompute::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(compute, 0x281);
#undef ASSERT_REG_POSITION
} // namespace Tegra::Engines

View File

@@ -4,12 +4,14 @@
#include "core/memory.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
namespace Tegra {
namespace Engines {
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
MaxwellDMA::MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer} {}
void MaxwellDMA::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
@@ -44,36 +46,79 @@ void MaxwellDMA::HandleCopy() {
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
ASSERT(regs.src_params.pos_x == 0);
ASSERT(regs.src_params.pos_y == 0);
ASSERT(regs.dst_params.pos_x == 0);
ASSERT(regs.dst_params.pos_y == 0);
if (regs.exec.is_dst_linear == regs.exec.is_src_linear) {
std::size_t copy_size = regs.x_count;
if (!regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
// If both the source and the destination are in block layout, assert.
UNREACHABLE_MSG("Tiled->Tiled DMA transfers are not yet implemented");
return;
}
if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
// When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
// buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count).
if (regs.exec.enable_2d) {
copy_size = copy_size * regs.y_count;
// buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
// y_count).
if (!regs.exec.enable_2d) {
Memory::CopyBlock(dest_cpu, source_cpu, regs.x_count);
return;
}
Memory::CopyBlock(dest_cpu, source_cpu, copy_size);
// If both the source and the destination are in linear layout, perform a line-by-line
// copy. We're going to take a subrect of size (x_count, y_count) from the source
// rectangle. There is no need to manually flush/invalidate the regions because
// CopyBlock does that for us.
for (u32 line = 0; line < regs.y_count; ++line) {
const VAddr source_line = source_cpu + line * regs.src_pitch;
const VAddr dest_line = dest_cpu + line * regs.dst_pitch;
Memory::CopyBlock(dest_line, source_line, regs.x_count);
}
return;
}
ASSERT(regs.exec.enable_2d == 1);
size_t copy_size = regs.x_count * regs.y_count;
const auto FlushAndInvalidate = [&](u32 src_size, u32 dst_size) {
// TODO(Subv): For now, manually flush the regions until we implement GPU-accelerated
// copying.
rasterizer.FlushRegion(source_cpu, src_size);
// We have to invalidate the destination region to evict any outdated surfaces from the
// cache. We do this before actually writing the new data because the destination address
// might contain a dirty surface that will have to be written back to memory.
rasterizer.InvalidateRegion(dest_cpu, dst_size);
};
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
ASSERT(regs.src_params.size_z == 1);
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer,
dst_buffer, true, regs.src_params.BlockHeight());
u32 src_bytes_per_pixel = regs.src_pitch / regs.src_params.size_x;
FlushAndInvalidate(regs.src_pitch * regs.src_params.size_y,
copy_size * src_bytes_per_pixel);
Texture::UnswizzleSubrect(regs.x_count, regs.y_count, regs.dst_pitch,
regs.src_params.size_x, src_bytes_per_pixel, source_cpu, dest_cpu,
regs.src_params.BlockHeight(), regs.src_params.pos_x,
regs.src_params.pos_y);
} else {
ASSERT(regs.dst_params.size_z == 1);
ASSERT(regs.src_pitch == regs.x_count);
u32 src_bpp = regs.src_pitch / regs.x_count;
FlushAndInvalidate(regs.src_pitch * regs.y_count,
regs.dst_params.size_x * regs.dst_params.size_y * src_bpp);
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer,
src_buffer, false, regs.dst_params.BlockHeight());
Texture::SwizzleSubrect(regs.x_count, regs.y_count, regs.src_pitch, regs.dst_params.size_x,
src_bpp, dest_cpu, source_cpu, regs.dst_params.BlockHeight());
}
}

View File

@@ -12,11 +12,15 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
class MaxwellDMA final {
public:
explicit MaxwellDMA(MemoryManager& memory_manager);
explicit MaxwellDMA(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
~MaxwellDMA() = default;
/// Write the value to the register identified by method.
@@ -129,6 +133,8 @@ public:
MemoryManager& memory_manager;
private:
VideoCore::RasterizerInterface& rasterizer;
/// Performs the copy from the source buffer to the destination buffer as configured in the
/// registers.
void HandleCopy();

View File

@@ -5,8 +5,9 @@
#pragma once
#include <bitset>
#include <cstring>
#include <map>
#include <string>
#include <tuple>
#include <vector>
#include <boost/optional.hpp>
@@ -314,29 +315,17 @@ enum class TextureMiscMode : u64 {
PTP,
};
enum class IpaInterpMode : u64 {
Linear = 0,
Perspective = 1,
Flat = 2,
Sc = 3,
};
enum class IpaSampleMode : u64 {
Default = 0,
Centroid = 1,
Offset = 2,
};
enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 };
enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 };
struct IpaMode {
IpaInterpMode interpolation_mode;
IpaSampleMode sampling_mode;
bool operator==(const IpaMode& a) const {
return std::tie(interpolation_mode, sampling_mode) ==
std::tie(a.interpolation_mode, a.sampling_mode);
inline bool operator==(const IpaMode& a) {
return (a.interpolation_mode == interpolation_mode) && (a.sampling_mode == sampling_mode);
}
bool operator!=(const IpaMode& a) const {
return !operator==(a);
inline bool operator!=(const IpaMode& a) {
return !((*this) == a);
}
};

View File

@@ -25,10 +25,10 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
memory_manager = std::make_unique<Tegra::MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager);
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(rasterizer, *memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(rasterizer, *memory_manager);
}
GPU::~GPU() = default;

View File

@@ -17,6 +17,22 @@
template <class T>
class RasterizerCache : NonCopyable {
public:
/// Write any cached resources overlapping the region back to memory (if dirty)
void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
if (size == 0)
return;
const ObjectInterval interval{addr, addr + size};
for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
for (auto& cached_object : pair.second) {
if (!cached_object)
continue;
cached_object->Flush();
}
}
}
/// Mark the specified region as being invalidated
void InvalidateRegion(VAddr addr, u64 size) {
if (size == 0)
@@ -71,6 +87,7 @@ protected:
void Unregister(const T& object) {
auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
object->Flush();
object_cache.subtract({GetInterval(object), ObjectSet{object}});
}

Some files were not shown because too many files have changed in this diff Show More