Merge pull request #2546 from DarkLordZach/kips
loader, file_sys: Add support for parsing and loading KIP (Kernel Internal Process) files
This commit is contained in:
		@@ -45,6 +45,8 @@ add_library(core STATIC
 | 
			
		||||
    file_sys/fsmitm_romfsbuild.h
 | 
			
		||||
    file_sys/ips_layer.cpp
 | 
			
		||||
    file_sys/ips_layer.h
 | 
			
		||||
    file_sys/kernel_executable.cpp
 | 
			
		||||
    file_sys/kernel_executable.h
 | 
			
		||||
    file_sys/mode.h
 | 
			
		||||
    file_sys/nca_metadata.cpp
 | 
			
		||||
    file_sys/nca_metadata.h
 | 
			
		||||
@@ -440,6 +442,8 @@ add_library(core STATIC
 | 
			
		||||
    loader/deconstructed_rom_directory.h
 | 
			
		||||
    loader/elf.cpp
 | 
			
		||||
    loader/elf.h
 | 
			
		||||
    loader/kip.cpp
 | 
			
		||||
    loader/kip.h
 | 
			
		||||
    loader/loader.cpp
 | 
			
		||||
    loader/loader.h
 | 
			
		||||
    loader/nax.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,10 @@
 | 
			
		||||
#include "core/crypto/key_manager.h"
 | 
			
		||||
#include "core/crypto/partition_data_manager.h"
 | 
			
		||||
#include "core/crypto/xts_encryption_layer.h"
 | 
			
		||||
#include "core/file_sys/kernel_executable.h"
 | 
			
		||||
#include "core/file_sys/vfs.h"
 | 
			
		||||
#include "core/file_sys/vfs_offset.h"
 | 
			
		||||
#include "core/file_sys/vfs_vector.h"
 | 
			
		||||
 | 
			
		||||
using namespace Common;
 | 
			
		||||
 | 
			
		||||
@@ -45,36 +47,6 @@ struct Package2Header {
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
 | 
			
		||||
 | 
			
		||||
struct INIHeader {
 | 
			
		||||
    u32_le magic;
 | 
			
		||||
    u32_le size;
 | 
			
		||||
    u32_le process_count;
 | 
			
		||||
    INSERT_PADDING_BYTES(4);
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
 | 
			
		||||
 | 
			
		||||
struct SectionHeader {
 | 
			
		||||
    u32_le offset;
 | 
			
		||||
    u32_le size_decompressed;
 | 
			
		||||
    u32_le size_compressed;
 | 
			
		||||
    u32_le attribute;
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
 | 
			
		||||
 | 
			
		||||
struct KIPHeader {
 | 
			
		||||
    u32_le magic;
 | 
			
		||||
    std::array<char, 12> name;
 | 
			
		||||
    u64_le title_id;
 | 
			
		||||
    u32_le category;
 | 
			
		||||
    u8 priority;
 | 
			
		||||
    u8 core;
 | 
			
		||||
    INSERT_PADDING_BYTES(1);
 | 
			
		||||
    u8 flags;
 | 
			
		||||
    std::array<SectionHeader, 6> sections;
 | 
			
		||||
    std::array<u32, 0x20> capabilities;
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
 | 
			
		||||
 | 
			
		||||
const std::array<SHA256Hash, 0x10> source_hashes{
 | 
			
		||||
    "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
 | 
			
		||||
    "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
 | 
			
		||||
@@ -170,65 +142,6 @@ const std::array<SHA256Hash, 0x20> master_key_hashes{
 | 
			
		||||
    "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
 | 
			
		||||
    const auto data_size = in.size() - 0xC;
 | 
			
		||||
 | 
			
		||||
    u32 compressed_size{};
 | 
			
		||||
    u32 init_index{};
 | 
			
		||||
    u32 additional_size{};
 | 
			
		||||
    std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
 | 
			
		||||
    std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
 | 
			
		||||
    std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
 | 
			
		||||
 | 
			
		||||
    std::vector<u8> out(in.size() + additional_size);
 | 
			
		||||
 | 
			
		||||
    if (compressed_size == in.size())
 | 
			
		||||
        std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
 | 
			
		||||
    else
 | 
			
		||||
        std::memcpy(out.data(), in.data(), compressed_size);
 | 
			
		||||
 | 
			
		||||
    auto index = in.size() - init_index;
 | 
			
		||||
    auto out_index = out.size();
 | 
			
		||||
 | 
			
		||||
    while (out_index > 0) {
 | 
			
		||||
        --index;
 | 
			
		||||
        auto control = in[index];
 | 
			
		||||
        for (size_t i = 0; i < 8; ++i) {
 | 
			
		||||
            if ((control & 0x80) > 0) {
 | 
			
		||||
                ASSERT(index >= 2);
 | 
			
		||||
                index -= 2;
 | 
			
		||||
                u64 segment_offset = in[index] | in[index + 1] << 8;
 | 
			
		||||
                u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
 | 
			
		||||
                segment_offset &= 0xFFF;
 | 
			
		||||
                segment_offset += 3;
 | 
			
		||||
 | 
			
		||||
                if (out_index < segment_size)
 | 
			
		||||
                    segment_size = out_index;
 | 
			
		||||
 | 
			
		||||
                ASSERT(out_index >= segment_size);
 | 
			
		||||
 | 
			
		||||
                out_index -= segment_size;
 | 
			
		||||
 | 
			
		||||
                for (size_t j = 0; j < segment_size; ++j) {
 | 
			
		||||
                    ASSERT(out_index + j + segment_offset < out.size());
 | 
			
		||||
                    out[out_index + j] = out[out_index + j + segment_offset];
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                ASSERT(out_index >= 1);
 | 
			
		||||
                --out_index;
 | 
			
		||||
                --index;
 | 
			
		||||
                out[out_index] = in[index];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            control <<= 1;
 | 
			
		||||
            if (out_index == 0)
 | 
			
		||||
                return out;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u8 CalculateMaxKeyblobSourceHash() {
 | 
			
		||||
    for (s8 i = 0x1F; i >= 0; --i) {
 | 
			
		||||
        if (keyblob_source_hashes[i] != SHA256Hash{})
 | 
			
		||||
@@ -478,37 +391,22 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
 | 
			
		||||
    cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
 | 
			
		||||
    cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
 | 
			
		||||
 | 
			
		||||
    INIHeader ini;
 | 
			
		||||
    std::memcpy(&ini, c.data(), sizeof(INIHeader));
 | 
			
		||||
    if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
 | 
			
		||||
    const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
 | 
			
		||||
    const FileSys::INI ini{ini_file};
 | 
			
		||||
    if (ini.GetStatus() != Loader::ResultStatus::Success)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    u64 offset = sizeof(INIHeader);
 | 
			
		||||
    for (size_t i = 0; i < ini.process_count; ++i) {
 | 
			
		||||
        KIPHeader kip;
 | 
			
		||||
        std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
 | 
			
		||||
        if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
 | 
			
		||||
    for (const auto& kip : ini.GetKIPs()) {
 | 
			
		||||
        if (kip.GetStatus() != Loader::ResultStatus::Success)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        const auto name =
 | 
			
		||||
            Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
 | 
			
		||||
 | 
			
		||||
        if (name != "FS" && name != "spl") {
 | 
			
		||||
            offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
 | 
			
		||||
                      kip.sections[1].size_compressed + kip.sections[2].size_compressed;
 | 
			
		||||
        if (kip.GetName() != "FS" && kip.GetName() != "spl") {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const u64 initial_offset = sizeof(KIPHeader) + offset;
 | 
			
		||||
        const auto text_begin = c.cbegin() + initial_offset;
 | 
			
		||||
        const auto text_end = text_begin + kip.sections[0].size_compressed;
 | 
			
		||||
        const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
 | 
			
		||||
 | 
			
		||||
        const auto rodata_end = text_end + kip.sections[1].size_compressed;
 | 
			
		||||
        const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
 | 
			
		||||
 | 
			
		||||
        const auto data_end = rodata_end + kip.sections[2].size_compressed;
 | 
			
		||||
        const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
 | 
			
		||||
        const auto& text = kip.GetTextSection();
 | 
			
		||||
        const auto& rodata = kip.GetRODataSection();
 | 
			
		||||
        const auto& data = kip.GetDataSection();
 | 
			
		||||
 | 
			
		||||
        std::vector<u8> out;
 | 
			
		||||
        out.reserve(text.size() + rodata.size() + data.size());
 | 
			
		||||
@@ -516,12 +414,9 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
 | 
			
		||||
        out.insert(out.end(), rodata.begin(), rodata.end());
 | 
			
		||||
        out.insert(out.end(), data.begin(), data.end());
 | 
			
		||||
 | 
			
		||||
        offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
 | 
			
		||||
                  kip.sections[1].size_compressed + kip.sections[2].size_compressed;
 | 
			
		||||
 | 
			
		||||
        if (name == "FS")
 | 
			
		||||
        if (kip.GetName() == "FS")
 | 
			
		||||
            package2_fs[static_cast<size_t>(type)] = std::move(out);
 | 
			
		||||
        else if (name == "spl")
 | 
			
		||||
        else if (kip.GetName() == "spl")
 | 
			
		||||
            package2_spl[static_cast<size_t>(type)] = std::move(out);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										228
									
								
								src/core/file_sys/kernel_executable.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										228
									
								
								src/core/file_sys/kernel_executable.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,228 @@
 | 
			
		||||
// Copyright 2019 yuzu emulator team
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
#include "core/file_sys/kernel_executable.h"
 | 
			
		||||
#include "core/file_sys/vfs_offset.h"
 | 
			
		||||
 | 
			
		||||
namespace FileSys {
 | 
			
		||||
 | 
			
		||||
constexpr u32 INI_MAX_KIPS = 0x50;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
bool DecompressBLZ(std::vector<u8>& data) {
 | 
			
		||||
    if (data.size() < 0xC)
 | 
			
		||||
        return {};
 | 
			
		||||
 | 
			
		||||
    const auto data_size = data.size() - 0xC;
 | 
			
		||||
 | 
			
		||||
    u32 compressed_size{};
 | 
			
		||||
    u32 init_index{};
 | 
			
		||||
    u32 additional_size{};
 | 
			
		||||
    std::memcpy(&compressed_size, data.data() + data_size, sizeof(u32));
 | 
			
		||||
    std::memcpy(&init_index, data.data() + data_size + 0x4, sizeof(u32));
 | 
			
		||||
    std::memcpy(&additional_size, data.data() + data_size + 0x8, sizeof(u32));
 | 
			
		||||
 | 
			
		||||
    const auto start_offset = data.size() - compressed_size;
 | 
			
		||||
    data.resize(compressed_size + additional_size + start_offset);
 | 
			
		||||
 | 
			
		||||
    std::size_t index = compressed_size - init_index;
 | 
			
		||||
    std::size_t out_index = compressed_size + additional_size;
 | 
			
		||||
 | 
			
		||||
    while (out_index > 0) {
 | 
			
		||||
        --index;
 | 
			
		||||
        auto control = data[index + start_offset];
 | 
			
		||||
        for (size_t i = 0; i < 8; ++i) {
 | 
			
		||||
            if (((control << i) & 0x80) > 0) {
 | 
			
		||||
                if (index < 2) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                index -= 2;
 | 
			
		||||
                std::size_t segment_offset =
 | 
			
		||||
                    data[index + start_offset] | data[index + start_offset + 1] << 8;
 | 
			
		||||
                std::size_t segment_size = ((segment_offset >> 12) & 0xF) + 3;
 | 
			
		||||
                segment_offset &= 0xFFF;
 | 
			
		||||
                segment_offset += 3;
 | 
			
		||||
 | 
			
		||||
                if (out_index < segment_size)
 | 
			
		||||
                    segment_size = out_index;
 | 
			
		||||
 | 
			
		||||
                if (out_index < segment_size) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                out_index -= segment_size;
 | 
			
		||||
 | 
			
		||||
                for (size_t j = 0; j < segment_size; ++j) {
 | 
			
		||||
                    if (out_index + j + segment_offset + start_offset >= data.size()) {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                    data[out_index + j + start_offset] =
 | 
			
		||||
                        data[out_index + j + segment_offset + start_offset];
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (out_index < 1) {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                --out_index;
 | 
			
		||||
                --index;
 | 
			
		||||
                data[out_index + start_offset] = data[index + start_offset];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (out_index == 0)
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
KIP::KIP(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
 | 
			
		||||
    if (file == nullptr) {
 | 
			
		||||
        status = Loader::ResultStatus::ErrorNullFile;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (file->GetSize() < sizeof(KIPHeader) || file->ReadObject(&header) != sizeof(KIPHeader)) {
 | 
			
		||||
        status = Loader::ResultStatus::ErrorBadKIPHeader;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (header.magic != Common::MakeMagic('K', 'I', 'P', '1')) {
 | 
			
		||||
        status = Loader::ResultStatus::ErrorBadKIPHeader;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u64 offset = sizeof(KIPHeader);
 | 
			
		||||
    for (std::size_t i = 0; i < header.sections.size(); ++i) {
 | 
			
		||||
        auto compressed = file->ReadBytes(header.sections[i].compressed_size, offset);
 | 
			
		||||
        offset += header.sections[i].compressed_size;
 | 
			
		||||
 | 
			
		||||
        if (header.sections[i].compressed_size == 0 && header.sections[i].decompressed_size != 0) {
 | 
			
		||||
            decompressed_sections[i] = std::vector<u8>(header.sections[i].decompressed_size);
 | 
			
		||||
        } else if (header.sections[i].compressed_size == header.sections[i].decompressed_size) {
 | 
			
		||||
            decompressed_sections[i] = std::move(compressed);
 | 
			
		||||
        } else {
 | 
			
		||||
            decompressed_sections[i] = compressed;
 | 
			
		||||
            if (!DecompressBLZ(decompressed_sections[i])) {
 | 
			
		||||
                status = Loader::ResultStatus::ErrorBLZDecompressionFailed;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Loader::ResultStatus KIP::GetStatus() const {
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string KIP::GetName() const {
 | 
			
		||||
    return Common::StringFromFixedZeroTerminatedBuffer(header.name.data(), header.name.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u64 KIP::GetTitleID() const {
 | 
			
		||||
    return header.title_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<u8> KIP::GetSectionDecompressed(u8 index) const {
 | 
			
		||||
    return decompressed_sections[index];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool KIP::Is64Bit() const {
 | 
			
		||||
    return (header.flags & 0x8) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool KIP::Is39BitAddressSpace() const {
 | 
			
		||||
    return (header.flags & 0x10) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool KIP::IsService() const {
 | 
			
		||||
    return (header.flags & 0x20) != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<u32> KIP::GetKernelCapabilities() const {
 | 
			
		||||
    return std::vector<u32>(header.capabilities.begin(), header.capabilities.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
s32 KIP::GetMainThreadPriority() const {
 | 
			
		||||
    return header.main_thread_priority;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 KIP::GetMainThreadStackSize() const {
 | 
			
		||||
    return header.sections[1].attribute;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 KIP::GetMainThreadCpuCore() const {
 | 
			
		||||
    return header.default_core;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::vector<u8>& KIP::GetTextSection() const {
 | 
			
		||||
    return decompressed_sections[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::vector<u8>& KIP::GetRODataSection() const {
 | 
			
		||||
    return decompressed_sections[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::vector<u8>& KIP::GetDataSection() const {
 | 
			
		||||
    return decompressed_sections[2];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 KIP::GetTextOffset() const {
 | 
			
		||||
    return header.sections[0].offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 KIP::GetRODataOffset() const {
 | 
			
		||||
    return header.sections[1].offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 KIP::GetDataOffset() const {
 | 
			
		||||
    return header.sections[2].offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 KIP::GetBSSSize() const {
 | 
			
		||||
    return header.sections[3].decompressed_size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u32 KIP::GetBSSOffset() const {
 | 
			
		||||
    return header.sections[3].offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
INI::INI(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
 | 
			
		||||
    if (file->GetSize() < sizeof(INIHeader) || file->ReadObject(&header) != sizeof(INIHeader)) {
 | 
			
		||||
        status = Loader::ResultStatus::ErrorBadINIHeader;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (header.magic != Common::MakeMagic('I', 'N', 'I', '1')) {
 | 
			
		||||
        status = Loader::ResultStatus::ErrorBadINIHeader;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (header.kip_count > INI_MAX_KIPS) {
 | 
			
		||||
        status = Loader::ResultStatus::ErrorINITooManyKIPs;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u64 offset = sizeof(INIHeader);
 | 
			
		||||
    for (std::size_t i = 0; i < header.kip_count; ++i) {
 | 
			
		||||
        const auto kip_file =
 | 
			
		||||
            std::make_shared<OffsetVfsFile>(file, file->GetSize() - offset, offset);
 | 
			
		||||
        KIP kip(kip_file);
 | 
			
		||||
        if (kip.GetStatus() == Loader::ResultStatus::Success) {
 | 
			
		||||
            kips.push_back(std::move(kip));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Loader::ResultStatus INI::GetStatus() const {
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::vector<KIP>& INI::GetKIPs() const {
 | 
			
		||||
    return kips;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
							
								
								
									
										99
									
								
								src/core/file_sys/kernel_executable.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/core/file_sys/kernel_executable.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
// Copyright 2019 yuzu emulator team
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "common/common_funcs.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "core/file_sys/vfs_types.h"
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
 | 
			
		||||
namespace FileSys {
 | 
			
		||||
 | 
			
		||||
struct KIPSectionHeader {
 | 
			
		||||
    u32_le offset;
 | 
			
		||||
    u32_le decompressed_size;
 | 
			
		||||
    u32_le compressed_size;
 | 
			
		||||
    u32_le attribute;
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(KIPSectionHeader) == 0x10, "KIPSectionHeader has incorrect size.");
 | 
			
		||||
 | 
			
		||||
struct KIPHeader {
 | 
			
		||||
    u32_le magic;
 | 
			
		||||
    std::array<char, 0xC> name;
 | 
			
		||||
    u64_le title_id;
 | 
			
		||||
    u32_le process_category;
 | 
			
		||||
    u8 main_thread_priority;
 | 
			
		||||
    u8 default_core;
 | 
			
		||||
    INSERT_PADDING_BYTES(1);
 | 
			
		||||
    u8 flags;
 | 
			
		||||
    std::array<KIPSectionHeader, 6> sections;
 | 
			
		||||
    std::array<u32, 0x20> capabilities;
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
 | 
			
		||||
 | 
			
		||||
struct INIHeader {
 | 
			
		||||
    u32_le magic;
 | 
			
		||||
    u32_le size;
 | 
			
		||||
    u32_le kip_count;
 | 
			
		||||
    INSERT_PADDING_BYTES(0x4);
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
 | 
			
		||||
 | 
			
		||||
// Kernel Internal Process
 | 
			
		||||
class KIP {
 | 
			
		||||
public:
 | 
			
		||||
    explicit KIP(const VirtualFile& file);
 | 
			
		||||
 | 
			
		||||
    Loader::ResultStatus GetStatus() const;
 | 
			
		||||
 | 
			
		||||
    std::string GetName() const;
 | 
			
		||||
    u64 GetTitleID() const;
 | 
			
		||||
    std::vector<u8> GetSectionDecompressed(u8 index) const;
 | 
			
		||||
 | 
			
		||||
    // Executable Flags
 | 
			
		||||
    bool Is64Bit() const;
 | 
			
		||||
    bool Is39BitAddressSpace() const;
 | 
			
		||||
    bool IsService() const;
 | 
			
		||||
 | 
			
		||||
    std::vector<u32> GetKernelCapabilities() const;
 | 
			
		||||
 | 
			
		||||
    s32 GetMainThreadPriority() const;
 | 
			
		||||
    u32 GetMainThreadStackSize() const;
 | 
			
		||||
    u32 GetMainThreadCpuCore() const;
 | 
			
		||||
 | 
			
		||||
    const std::vector<u8>& GetTextSection() const;
 | 
			
		||||
    const std::vector<u8>& GetRODataSection() const;
 | 
			
		||||
    const std::vector<u8>& GetDataSection() const;
 | 
			
		||||
 | 
			
		||||
    u32 GetTextOffset() const;
 | 
			
		||||
    u32 GetRODataOffset() const;
 | 
			
		||||
    u32 GetDataOffset() const;
 | 
			
		||||
 | 
			
		||||
    u32 GetBSSSize() const;
 | 
			
		||||
    u32 GetBSSOffset() const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Loader::ResultStatus status;
 | 
			
		||||
 | 
			
		||||
    KIPHeader header{};
 | 
			
		||||
    std::array<std::vector<u8>, 6> decompressed_sections;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class INI {
 | 
			
		||||
public:
 | 
			
		||||
    explicit INI(const VirtualFile& file);
 | 
			
		||||
 | 
			
		||||
    Loader::ResultStatus GetStatus() const;
 | 
			
		||||
 | 
			
		||||
    const std::vector<KIP>& GetKIPs() const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Loader::ResultStatus status;
 | 
			
		||||
 | 
			
		||||
    INIHeader header{};
 | 
			
		||||
    std::vector<KIP> kips;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
@@ -51,6 +51,21 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
 | 
			
		||||
    return Loader::ResultStatus::Success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
 | 
			
		||||
                                 u8 main_thread_prio, u8 main_thread_core,
 | 
			
		||||
                                 u32 main_thread_stack_size, u64 title_id,
 | 
			
		||||
                                 u64 filesystem_permissions,
 | 
			
		||||
                                 KernelCapabilityDescriptors capabilities) {
 | 
			
		||||
    npdm_header.has_64_bit_instructions.Assign(is_64_bit);
 | 
			
		||||
    npdm_header.address_space_type.Assign(address_space);
 | 
			
		||||
    npdm_header.main_thread_priority = main_thread_prio;
 | 
			
		||||
    npdm_header.main_thread_cpu = main_thread_core;
 | 
			
		||||
    npdm_header.main_stack_size = main_thread_stack_size;
 | 
			
		||||
    aci_header.title_id = title_id;
 | 
			
		||||
    aci_file_access.permissions = filesystem_permissions;
 | 
			
		||||
    aci_kernel_capabilities = std ::move(capabilities);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ProgramMetadata::Is64BitProgram() const {
 | 
			
		||||
    return npdm_header.has_64_bit_instructions;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,11 @@ public:
 | 
			
		||||
 | 
			
		||||
    Loader::ResultStatus Load(VirtualFile file);
 | 
			
		||||
 | 
			
		||||
    // Load from parameters instead of NPDM file, used for KIP
 | 
			
		||||
    void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, u8 main_thread_prio,
 | 
			
		||||
                    u8 main_thread_core, u32 main_thread_stack_size, u64 title_id,
 | 
			
		||||
                    u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
 | 
			
		||||
 | 
			
		||||
    bool Is64BitProgram() const;
 | 
			
		||||
    ProgramAddressSpaceType GetAddressSpaceType() const;
 | 
			
		||||
    u8 GetMainThreadPriority() const;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										102
									
								
								src/core/loader/kip.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/core/loader/kip.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
// Copyright 2019 yuzu emulator team
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include "core/file_sys/kernel_executable.h"
 | 
			
		||||
#include "core/file_sys/program_metadata.h"
 | 
			
		||||
#include "core/gdbstub/gdbstub.h"
 | 
			
		||||
#include "core/hle/kernel/code_set.h"
 | 
			
		||||
#include "core/hle/kernel/process.h"
 | 
			
		||||
#include "core/loader/kip.h"
 | 
			
		||||
 | 
			
		||||
namespace Loader {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
constexpr u32 PageAlignSize(u32 size) {
 | 
			
		||||
    return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
 | 
			
		||||
}
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
 | 
			
		||||
    : AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
 | 
			
		||||
 | 
			
		||||
AppLoader_KIP::~AppLoader_KIP() = default;
 | 
			
		||||
 | 
			
		||||
FileType AppLoader_KIP::IdentifyType(const FileSys::VirtualFile& file) {
 | 
			
		||||
    u32_le magic{};
 | 
			
		||||
    if (file->GetSize() < sizeof(u32) || file->ReadObject(&magic) != sizeof(u32)) {
 | 
			
		||||
        return FileType::Error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (magic == Common::MakeMagic('K', 'I', 'P', '1')) {
 | 
			
		||||
        return FileType::KIP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return FileType::Error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FileType AppLoader_KIP::GetFileType() const {
 | 
			
		||||
    return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP
 | 
			
		||||
                                                                         : FileType::Error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {
 | 
			
		||||
    if (is_loaded) {
 | 
			
		||||
        return {ResultStatus::ErrorAlreadyLoaded, {}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (kip == nullptr) {
 | 
			
		||||
        return {ResultStatus::ErrorNullFile, {}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (kip->GetStatus() != ResultStatus::Success) {
 | 
			
		||||
        return {kip->GetStatus(), {}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto get_kip_address_space_type = [](const auto& kip) {
 | 
			
		||||
        return kip.Is64Bit()
 | 
			
		||||
                   ? (kip.Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
 | 
			
		||||
                                                : FileSys::ProgramAddressSpaceType::Is36Bit)
 | 
			
		||||
                   : FileSys::ProgramAddressSpaceType::Is32Bit;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const auto address_space = get_kip_address_space_type(*kip);
 | 
			
		||||
 | 
			
		||||
    FileSys::ProgramMetadata metadata;
 | 
			
		||||
    metadata.LoadManual(kip->Is64Bit(), address_space, kip->GetMainThreadPriority(),
 | 
			
		||||
                        kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(),
 | 
			
		||||
                        kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, kip->GetKernelCapabilities());
 | 
			
		||||
 | 
			
		||||
    const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
 | 
			
		||||
    Kernel::CodeSet codeset;
 | 
			
		||||
    std::vector<u8> program_image;
 | 
			
		||||
 | 
			
		||||
    const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
 | 
			
		||||
                                               const std::vector<u8>& data, u32 offset) {
 | 
			
		||||
        segment.addr = offset;
 | 
			
		||||
        segment.offset = offset;
 | 
			
		||||
        segment.size = PageAlignSize(static_cast<u32>(data.size()));
 | 
			
		||||
        program_image.resize(offset);
 | 
			
		||||
        program_image.insert(program_image.end(), data.begin(), data.end());
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
 | 
			
		||||
    load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset());
 | 
			
		||||
    load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset());
 | 
			
		||||
 | 
			
		||||
    program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
 | 
			
		||||
    codeset.DataSegment().size += kip->GetBSSSize();
 | 
			
		||||
 | 
			
		||||
    GDBStub::RegisterModule(kip->GetName(), base_address, base_address + program_image.size());
 | 
			
		||||
 | 
			
		||||
    codeset.memory = std::move(program_image);
 | 
			
		||||
    process.LoadModule(std::move(codeset), base_address);
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
 | 
			
		||||
 | 
			
		||||
    is_loaded = true;
 | 
			
		||||
    return {ResultStatus::Success,
 | 
			
		||||
            LoadParameters{kip->GetMainThreadPriority(), kip->GetMainThreadStackSize()}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Loader
 | 
			
		||||
							
								
								
									
										35
									
								
								src/core/loader/kip.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/core/loader/kip.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
// Copyright 2019 yuzu emulator team
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
 | 
			
		||||
namespace FileSys {
 | 
			
		||||
class KIP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace Loader {
 | 
			
		||||
 | 
			
		||||
class AppLoader_KIP final : public AppLoader {
 | 
			
		||||
public:
 | 
			
		||||
    explicit AppLoader_KIP(FileSys::VirtualFile file);
 | 
			
		||||
    ~AppLoader_KIP() override;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns the type of the file
 | 
			
		||||
     * @param file std::shared_ptr<VfsFile> open file
 | 
			
		||||
     * @return FileType found, or FileType::Error if this loader doesn't know it
 | 
			
		||||
     */
 | 
			
		||||
    static FileType IdentifyType(const FileSys::VirtualFile& file);
 | 
			
		||||
 | 
			
		||||
    FileType GetFileType() const override;
 | 
			
		||||
 | 
			
		||||
    LoadResult Load(Kernel::Process& process) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::unique_ptr<FileSys::KIP> kip;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Loader
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
#include "core/hle/kernel/process.h"
 | 
			
		||||
#include "core/loader/deconstructed_rom_directory.h"
 | 
			
		||||
#include "core/loader/elf.h"
 | 
			
		||||
#include "core/loader/kip.h"
 | 
			
		||||
#include "core/loader/nax.h"
 | 
			
		||||
#include "core/loader/nca.h"
 | 
			
		||||
#include "core/loader/nro.h"
 | 
			
		||||
@@ -36,6 +37,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
 | 
			
		||||
    CHECK_TYPE(XCI)
 | 
			
		||||
    CHECK_TYPE(NAX)
 | 
			
		||||
    CHECK_TYPE(NSP)
 | 
			
		||||
    CHECK_TYPE(KIP)
 | 
			
		||||
 | 
			
		||||
#undef CHECK_TYPE
 | 
			
		||||
 | 
			
		||||
@@ -63,6 +65,8 @@ FileType GuessFromFilename(const std::string& name) {
 | 
			
		||||
        return FileType::XCI;
 | 
			
		||||
    if (extension == "nsp")
 | 
			
		||||
        return FileType::NSP;
 | 
			
		||||
    if (extension == "kip")
 | 
			
		||||
        return FileType::KIP;
 | 
			
		||||
 | 
			
		||||
    return FileType::Unknown;
 | 
			
		||||
}
 | 
			
		||||
@@ -83,6 +87,8 @@ std::string GetFileTypeString(FileType type) {
 | 
			
		||||
        return "NAX";
 | 
			
		||||
    case FileType::NSP:
 | 
			
		||||
        return "NSP";
 | 
			
		||||
    case FileType::KIP:
 | 
			
		||||
        return "KIP";
 | 
			
		||||
    case FileType::DeconstructedRomDirectory:
 | 
			
		||||
        return "Directory";
 | 
			
		||||
    case FileType::Error:
 | 
			
		||||
@@ -93,7 +99,7 @@ std::string GetFileTypeString(FileType type) {
 | 
			
		||||
    return "unknown";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr std::array<const char*, 62> RESULT_MESSAGES{
 | 
			
		||||
constexpr std::array<const char*, 66> RESULT_MESSAGES{
 | 
			
		||||
    "The operation completed successfully.",
 | 
			
		||||
    "The loader requested to load is already loaded.",
 | 
			
		||||
    "The operation is not implemented.",
 | 
			
		||||
@@ -156,6 +162,10 @@ constexpr std::array<const char*, 62> RESULT_MESSAGES{
 | 
			
		||||
    "The BKTR-type NCA has a bad Subsection bucket.",
 | 
			
		||||
    "The BKTR-type NCA is missing the base RomFS.",
 | 
			
		||||
    "The NSP or XCI does not contain an update in addition to the base game.",
 | 
			
		||||
    "The KIP file has a bad header.",
 | 
			
		||||
    "The KIP BLZ decompression of the section failed unexpectedly.",
 | 
			
		||||
    "The INI file has a bad header.",
 | 
			
		||||
    "The INI file contains more than the maximum allowable number of KIP files.",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::ostream& operator<<(std::ostream& os, ResultStatus status) {
 | 
			
		||||
@@ -205,6 +215,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
 | 
			
		||||
    case FileType::NSP:
 | 
			
		||||
        return std::make_unique<AppLoader_NSP>(std::move(file));
 | 
			
		||||
 | 
			
		||||
    // NX KIP (Kernel Internal Process) file format
 | 
			
		||||
    case FileType::KIP:
 | 
			
		||||
        return std::make_unique<AppLoader_KIP>(std::move(file));
 | 
			
		||||
 | 
			
		||||
    // NX deconstructed ROM directory.
 | 
			
		||||
    case FileType::DeconstructedRomDirectory:
 | 
			
		||||
        return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
 | 
			
		||||
 
 | 
			
		||||
@@ -37,6 +37,7 @@ enum class FileType {
 | 
			
		||||
    NSP,
 | 
			
		||||
    XCI,
 | 
			
		||||
    NAX,
 | 
			
		||||
    KIP,
 | 
			
		||||
    DeconstructedRomDirectory,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -124,6 +125,10 @@ enum class ResultStatus : u16 {
 | 
			
		||||
    ErrorBadSubsectionBuckets,
 | 
			
		||||
    ErrorMissingBKTRBaseRomFS,
 | 
			
		||||
    ErrorNoPackedUpdate,
 | 
			
		||||
    ErrorBadKIPHeader,
 | 
			
		||||
    ErrorBLZDecompressionFailed,
 | 
			
		||||
    ErrorBadINIHeader,
 | 
			
		||||
    ErrorINITooManyKIPs,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::ostream& operator<<(std::ostream& os, ResultStatus status);
 | 
			
		||||
 
 | 
			
		||||
@@ -468,8 +468,7 @@ void GameList::LoadInterfaceLayout() {
 | 
			
		||||
 | 
			
		||||
const QStringList GameList::supported_file_extensions = {
 | 
			
		||||
    QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
 | 
			
		||||
    QStringLiteral("xci"), QStringLiteral("nsp"),
 | 
			
		||||
};
 | 
			
		||||
    QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
 | 
			
		||||
 | 
			
		||||
void GameList::RefreshGameDirectory() {
 | 
			
		||||
    if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user