1 // Copyright 2019 yuzu emulator team
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <cstring>
6 #include "core/file_sys/kernel_executable.h"
7 #include "core/file_sys/program_metadata.h"
8 #include "core/hle/kernel/code_set.h"
9 #include "core/hle/kernel/memory/page_table.h"
10 #include "core/hle/kernel/process.h"
11 #include "core/loader/kip.h"
12 #include "core/memory.h"
13 
14 namespace Loader {
15 
16 namespace {
PageAlignSize(u32 size)17 constexpr u32 PageAlignSize(u32 size) {
18     return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
19 }
20 } // Anonymous namespace
21 
AppLoader_KIP(FileSys::VirtualFile file_)22 AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
23     : AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
24 
25 AppLoader_KIP::~AppLoader_KIP() = default;
26 
IdentifyType(const FileSys::VirtualFile & file)27 FileType AppLoader_KIP::IdentifyType(const FileSys::VirtualFile& file) {
28     u32_le magic{};
29     if (file->GetSize() < sizeof(u32) || file->ReadObject(&magic) != sizeof(u32)) {
30         return FileType::Error;
31     }
32 
33     if (magic == Common::MakeMagic('K', 'I', 'P', '1')) {
34         return FileType::KIP;
35     }
36 
37     return FileType::Error;
38 }
39 
GetFileType() const40 FileType AppLoader_KIP::GetFileType() const {
41     return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP
42                                                                          : FileType::Error;
43 }
44 
Load(Kernel::Process & process,Core::System & system)45 AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process,
46                                           [[maybe_unused]] Core::System& system) {
47     if (is_loaded) {
48         return {ResultStatus::ErrorAlreadyLoaded, {}};
49     }
50 
51     if (kip == nullptr) {
52         return {ResultStatus::ErrorNullFile, {}};
53     }
54 
55     if (kip->GetStatus() != ResultStatus::Success) {
56         return {kip->GetStatus(), {}};
57     }
58 
59     const auto get_kip_address_space_type = [](const auto& kip) {
60         return kip.Is64Bit()
61                    ? (kip.Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
62                                                 : FileSys::ProgramAddressSpaceType::Is36Bit)
63                    : FileSys::ProgramAddressSpaceType::Is32Bit;
64     };
65 
66     const auto address_space = get_kip_address_space_type(*kip);
67 
68     FileSys::ProgramMetadata metadata;
69     metadata.LoadManual(kip->Is64Bit(), address_space, kip->GetMainThreadPriority(),
70                         kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(),
71                         kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, kip->GetKernelCapabilities());
72 
73     const VAddr base_address = process.PageTable().GetCodeRegionStart();
74     Kernel::CodeSet codeset;
75     Kernel::PhysicalMemory program_image;
76 
77     const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
78                                                const std::vector<u8>& data, u32 offset) {
79         segment.addr = offset;
80         segment.offset = offset;
81         segment.size = PageAlignSize(static_cast<u32>(data.size()));
82         program_image.resize(offset + data.size());
83         std::memcpy(program_image.data() + offset, data.data(), data.size());
84     };
85 
86     load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
87     load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset());
88     load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset());
89 
90     program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
91     codeset.DataSegment().size += kip->GetBSSSize();
92 
93     codeset.memory = std::move(program_image);
94     process.LoadModule(std::move(codeset), base_address);
95 
96     LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
97 
98     is_loaded = true;
99     return {ResultStatus::Success,
100             LoadParameters{kip->GetMainThreadPriority(), kip->GetMainThreadStackSize()}};
101 }
102 
103 } // namespace Loader
104