|
|
|
@ -13,6 +13,36 @@ |
|
|
|
|
|
|
|
namespace Service::LDR { |
|
|
|
|
|
|
|
namespace ErrCodes { |
|
|
|
enum { |
|
|
|
InvalidNRO = 52, |
|
|
|
InvalidNRR = 53, |
|
|
|
MissingNRRHash = 54, |
|
|
|
MaximumNRO = 55, |
|
|
|
MaximumNRR = 56, |
|
|
|
AlreadyLoaded = 57, |
|
|
|
InvalidAlignment = 81, |
|
|
|
InvalidSize = 82, |
|
|
|
InvalidNROAddress = 84, |
|
|
|
InvalidNRRAddress = 85, |
|
|
|
NotInitialized = 87, |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
constexpr ResultCode ERROR_INVALID_NRO(ErrorModule::Loader, ErrCodes::InvalidNRO); |
|
|
|
constexpr ResultCode ERROR_INVALID_NRR(ErrorModule::Loader, ErrCodes::InvalidNRR); |
|
|
|
constexpr ResultCode ERROR_MISSING_NRR_HASH(ErrorModule::Loader, ErrCodes::MissingNRRHash); |
|
|
|
constexpr ResultCode ERROR_MAXIMUM_NRO(ErrorModule::Loader, ErrCodes::MaximumNRO); |
|
|
|
constexpr ResultCode ERROR_MAXIMUM_NRR(ErrorModule::Loader, ErrCodes::MaximumNRR); |
|
|
|
constexpr ResultCode ERROR_ALREADY_LOADED(ErrorModule::Loader, ErrCodes::AlreadyLoaded); |
|
|
|
constexpr ResultCode ERROR_INVALID_ALIGNMENT(ErrorModule::Loader, ErrCodes::InvalidAlignment); |
|
|
|
constexpr ResultCode ERROR_INVALID_SIZE(ErrorModule::Loader, ErrCodes::InvalidSize); |
|
|
|
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNROAddress); |
|
|
|
constexpr ResultCode ERROR_INVALID_NRR_ADDRESS(ErrorModule::Loader, ErrCodes::InvalidNRRAddress); |
|
|
|
constexpr ResultCode ERROR_NOT_INITIALIZED(ErrorModule::Loader, ErrCodes::NotInitialized); |
|
|
|
|
|
|
|
constexpr u64 MAXIMUM_LOADED_RO = 0x40; |
|
|
|
|
|
|
|
class DebugMonitor final : public ServiceFramework<DebugMonitor> { |
|
|
|
public: |
|
|
|
explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} { |
|
|
|
@ -75,6 +105,88 @@ public: |
|
|
|
} |
|
|
|
|
|
|
|
void LoadNrr(Kernel::HLERequestContext& ctx) { |
|
|
|
IPC::RequestParser rp{ctx}; |
|
|
|
rp.Skip(2, false); |
|
|
|
const VAddr nrr_addr{rp.Pop<VAddr>()}; |
|
|
|
const u64 nrr_size{rp.Pop<u64>()}; |
|
|
|
|
|
|
|
if (!initialized) { |
|
|
|
LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); |
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(ERROR_NOT_INITIALIZED); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (nro.size() >= MAXIMUM_LOADED_RO) { |
|
|
|
LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs " |
|
|
|
"(0x40)! Failing..."); |
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(ERROR_MAXIMUM_NRR); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// NRR Address does not fall on 0x1000 byte boundary
|
|
|
|
if ((nrr_addr & 0xFFF) != 0) { |
|
|
|
LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr); |
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(ERROR_INVALID_ALIGNMENT); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// NRR Size is zero or causes overflow
|
|
|
|
if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || (nrr_size & 0xFFF) != 0) { |
|
|
|
LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})", |
|
|
|
nrr_addr, nrr_size); |
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(ERROR_INVALID_SIZE); |
|
|
|
return; |
|
|
|
} |
|
|
|
// Read NRR data from memory
|
|
|
|
std::vector<u8> nrr_data(nrr_size); |
|
|
|
Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size); |
|
|
|
NRRHeader header; |
|
|
|
std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader)); |
|
|
|
|
|
|
|
if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) { |
|
|
|
LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic); |
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(ERROR_INVALID_NRR); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (header.size != nrr_size) { |
|
|
|
LOG_ERROR(Service_LDR, |
|
|
|
"NRR header reported size did not match LoadNrr parameter size! " |
|
|
|
"(header_size={:016X}, loadnrr_size={:016X})", |
|
|
|
header.size, nrr_size); |
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(ERROR_INVALID_SIZE); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (Core::CurrentProcess()->GetTitleID() != header.title_id) { |
|
|
|
LOG_ERROR(Service_LDR, |
|
|
|
"Attempting to load NRR with title ID other than current process. (actual " |
|
|
|
"{:016X})!", |
|
|
|
header.title_id); |
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(ERROR_INVALID_NRR); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<SHA256Hash> hashes; |
|
|
|
for (std::size_t i = header.hash_offset; |
|
|
|
i < (header.hash_offset + (header.hash_count << 5)); i += 8) { |
|
|
|
hashes.emplace_back(); |
|
|
|
std::memcpy(hashes.back().data(), nrr_data.data() + i, sizeof(SHA256Hash)); |
|
|
|
} |
|
|
|
|
|
|
|
nrr.insert_or_assign(nrr_addr, std::move(hashes)); |
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(RESULT_SUCCESS); |
|
|
|
} |
|
|
|
|
|
|
|
IPC::ResponseBuilder rb{ctx, 2}; |
|
|
|
rb.Push(RESULT_SUCCESS); |
|
|
|
LOG_WARNING(Service_LDR, "(STUBBED) called"); |
|
|
|
|