forked from eden-emu/eden
		
	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:
		
						commit
						e2f7933b3f
					
				
					 11 changed files with 522 additions and 121 deletions
				
			
		|  | @ -45,6 +45,8 @@ add_library(core STATIC | ||||||
|     file_sys/fsmitm_romfsbuild.h |     file_sys/fsmitm_romfsbuild.h | ||||||
|     file_sys/ips_layer.cpp |     file_sys/ips_layer.cpp | ||||||
|     file_sys/ips_layer.h |     file_sys/ips_layer.h | ||||||
|  |     file_sys/kernel_executable.cpp | ||||||
|  |     file_sys/kernel_executable.h | ||||||
|     file_sys/mode.h |     file_sys/mode.h | ||||||
|     file_sys/nca_metadata.cpp |     file_sys/nca_metadata.cpp | ||||||
|     file_sys/nca_metadata.h |     file_sys/nca_metadata.h | ||||||
|  | @ -440,6 +442,8 @@ add_library(core STATIC | ||||||
|     loader/deconstructed_rom_directory.h |     loader/deconstructed_rom_directory.h | ||||||
|     loader/elf.cpp |     loader/elf.cpp | ||||||
|     loader/elf.h |     loader/elf.h | ||||||
|  |     loader/kip.cpp | ||||||
|  |     loader/kip.h | ||||||
|     loader/loader.cpp |     loader/loader.cpp | ||||||
|     loader/loader.h |     loader/loader.h | ||||||
|     loader/nax.cpp |     loader/nax.cpp | ||||||
|  |  | ||||||
|  | @ -22,8 +22,10 @@ | ||||||
| #include "core/crypto/key_manager.h" | #include "core/crypto/key_manager.h" | ||||||
| #include "core/crypto/partition_data_manager.h" | #include "core/crypto/partition_data_manager.h" | ||||||
| #include "core/crypto/xts_encryption_layer.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.h" | ||||||
| #include "core/file_sys/vfs_offset.h" | #include "core/file_sys/vfs_offset.h" | ||||||
|  | #include "core/file_sys/vfs_vector.h" | ||||||
| 
 | 
 | ||||||
| using namespace Common; | using namespace Common; | ||||||
| 
 | 
 | ||||||
|  | @ -45,36 +47,6 @@ struct Package2Header { | ||||||
| }; | }; | ||||||
| static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); | 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{ | const std::array<SHA256Hash, 0x10> source_hashes{ | ||||||
|     "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
 |     "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
 | ||||||
|     "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
 |     "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
 | ||||||
|  | @ -170,65 +142,6 @@ const std::array<SHA256Hash, 0x20> master_key_hashes{ | ||||||
|     "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
 |     "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() { | static u8 CalculateMaxKeyblobSourceHash() { | ||||||
|     for (s8 i = 0x1F; i >= 0; --i) { |     for (s8 i = 0x1F; i >= 0; --i) { | ||||||
|         if (keyblob_source_hashes[i] != SHA256Hash{}) |         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.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()}); | ||||||
|     cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); |     cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); | ||||||
| 
 | 
 | ||||||
|     INIHeader ini; |     const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c); | ||||||
|     std::memcpy(&ini, c.data(), sizeof(INIHeader)); |     const FileSys::INI ini{ini_file}; | ||||||
|     if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) |     if (ini.GetStatus() != Loader::ResultStatus::Success) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     u64 offset = sizeof(INIHeader); |     for (const auto& kip : ini.GetKIPs()) { | ||||||
|     for (size_t i = 0; i < ini.process_count; ++i) { |         if (kip.GetStatus() != Loader::ResultStatus::Success) | ||||||
|         KIPHeader kip; |  | ||||||
|         std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader)); |  | ||||||
|         if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1')) |  | ||||||
|             return; |             return; | ||||||
| 
 | 
 | ||||||
|         const auto name = |         if (kip.GetName() != "FS" && kip.GetName() != "spl") { | ||||||
|             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; |  | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const u64 initial_offset = sizeof(KIPHeader) + offset; |         const auto& text = kip.GetTextSection(); | ||||||
|         const auto text_begin = c.cbegin() + initial_offset; |         const auto& rodata = kip.GetRODataSection(); | ||||||
|         const auto text_end = text_begin + kip.sections[0].size_compressed; |         const auto& data = kip.GetDataSection(); | ||||||
|         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}); |  | ||||||
| 
 | 
 | ||||||
|         std::vector<u8> out; |         std::vector<u8> out; | ||||||
|         out.reserve(text.size() + rodata.size() + data.size()); |         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(), rodata.begin(), rodata.end()); | ||||||
|         out.insert(out.end(), data.begin(), data.end()); |         out.insert(out.end(), data.begin(), data.end()); | ||||||
| 
 | 
 | ||||||
|         offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + |         if (kip.GetName() == "FS") | ||||||
|                   kip.sections[1].size_compressed + kip.sections[2].size_compressed; |  | ||||||
| 
 |  | ||||||
|         if (name == "FS") |  | ||||||
|             package2_fs[static_cast<size_t>(type)] = std::move(out); |             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); |             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; |     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 { | bool ProgramMetadata::Is64BitProgram() const { | ||||||
|     return npdm_header.has_64_bit_instructions; |     return npdm_header.has_64_bit_instructions; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -46,6 +46,11 @@ public: | ||||||
| 
 | 
 | ||||||
|     Loader::ResultStatus Load(VirtualFile file); |     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; |     bool Is64BitProgram() const; | ||||||
|     ProgramAddressSpaceType GetAddressSpaceType() const; |     ProgramAddressSpaceType GetAddressSpaceType() const; | ||||||
|     u8 GetMainThreadPriority() 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/hle/kernel/process.h" | ||||||
| #include "core/loader/deconstructed_rom_directory.h" | #include "core/loader/deconstructed_rom_directory.h" | ||||||
| #include "core/loader/elf.h" | #include "core/loader/elf.h" | ||||||
|  | #include "core/loader/kip.h" | ||||||
| #include "core/loader/nax.h" | #include "core/loader/nax.h" | ||||||
| #include "core/loader/nca.h" | #include "core/loader/nca.h" | ||||||
| #include "core/loader/nro.h" | #include "core/loader/nro.h" | ||||||
|  | @ -36,6 +37,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) { | ||||||
|     CHECK_TYPE(XCI) |     CHECK_TYPE(XCI) | ||||||
|     CHECK_TYPE(NAX) |     CHECK_TYPE(NAX) | ||||||
|     CHECK_TYPE(NSP) |     CHECK_TYPE(NSP) | ||||||
|  |     CHECK_TYPE(KIP) | ||||||
| 
 | 
 | ||||||
| #undef CHECK_TYPE | #undef CHECK_TYPE | ||||||
| 
 | 
 | ||||||
|  | @ -63,6 +65,8 @@ FileType GuessFromFilename(const std::string& name) { | ||||||
|         return FileType::XCI; |         return FileType::XCI; | ||||||
|     if (extension == "nsp") |     if (extension == "nsp") | ||||||
|         return FileType::NSP; |         return FileType::NSP; | ||||||
|  |     if (extension == "kip") | ||||||
|  |         return FileType::KIP; | ||||||
| 
 | 
 | ||||||
|     return FileType::Unknown; |     return FileType::Unknown; | ||||||
| } | } | ||||||
|  | @ -83,6 +87,8 @@ std::string GetFileTypeString(FileType type) { | ||||||
|         return "NAX"; |         return "NAX"; | ||||||
|     case FileType::NSP: |     case FileType::NSP: | ||||||
|         return "NSP"; |         return "NSP"; | ||||||
|  |     case FileType::KIP: | ||||||
|  |         return "KIP"; | ||||||
|     case FileType::DeconstructedRomDirectory: |     case FileType::DeconstructedRomDirectory: | ||||||
|         return "Directory"; |         return "Directory"; | ||||||
|     case FileType::Error: |     case FileType::Error: | ||||||
|  | @ -93,7 +99,7 @@ std::string GetFileTypeString(FileType type) { | ||||||
|     return "unknown"; |     return "unknown"; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| constexpr std::array<const char*, 62> RESULT_MESSAGES{ | constexpr std::array<const char*, 66> RESULT_MESSAGES{ | ||||||
|     "The operation completed successfully.", |     "The operation completed successfully.", | ||||||
|     "The loader requested to load is already loaded.", |     "The loader requested to load is already loaded.", | ||||||
|     "The operation is not implemented.", |     "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 has a bad Subsection bucket.", | ||||||
|     "The BKTR-type NCA is missing the base RomFS.", |     "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 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) { | 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: |     case FileType::NSP: | ||||||
|         return std::make_unique<AppLoader_NSP>(std::move(file)); |         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.
 |     // NX deconstructed ROM directory.
 | ||||||
|     case FileType::DeconstructedRomDirectory: |     case FileType::DeconstructedRomDirectory: | ||||||
|         return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); |         return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ enum class FileType { | ||||||
|     NSP, |     NSP, | ||||||
|     XCI, |     XCI, | ||||||
|     NAX, |     NAX, | ||||||
|  |     KIP, | ||||||
|     DeconstructedRomDirectory, |     DeconstructedRomDirectory, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -124,6 +125,10 @@ enum class ResultStatus : u16 { | ||||||
|     ErrorBadSubsectionBuckets, |     ErrorBadSubsectionBuckets, | ||||||
|     ErrorMissingBKTRBaseRomFS, |     ErrorMissingBKTRBaseRomFS, | ||||||
|     ErrorNoPackedUpdate, |     ErrorNoPackedUpdate, | ||||||
|  |     ErrorBadKIPHeader, | ||||||
|  |     ErrorBLZDecompressionFailed, | ||||||
|  |     ErrorBadINIHeader, | ||||||
|  |     ErrorINITooManyKIPs, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| std::ostream& operator<<(std::ostream& os, ResultStatus status); | std::ostream& operator<<(std::ostream& os, ResultStatus status); | ||||||
|  |  | ||||||
|  | @ -468,8 +468,7 @@ void GameList::LoadInterfaceLayout() { | ||||||
| 
 | 
 | ||||||
| const QStringList GameList::supported_file_extensions = { | const QStringList GameList::supported_file_extensions = { | ||||||
|     QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), |     QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), | ||||||
|     QStringLiteral("xci"), QStringLiteral("nsp"), |     QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| void GameList::RefreshGameDirectory() { | void GameList::RefreshGameDirectory() { | ||||||
|     if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { |     if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bunnei
						bunnei