summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/kernel/timer.cpp39
-rw-r--r--src/core/hle/kernel/timer.h8
-rw-r--r--src/core/hle/service/apt/apt.cpp103
-rw-r--r--src/core/hle/service/apt/apt.h40
-rw-r--r--src/core/hle/service/apt/apt_a.cpp4
-rw-r--r--src/core/hle/service/apt/apt_s.cpp4
-rw-r--r--src/core/hle/service/apt/apt_u.cpp4
-rw-r--r--src/core/hle/svc.cpp5
8 files changed, 185 insertions, 22 deletions
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index 60537f355..c42003e9d 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -52,9 +52,14 @@ void Timer::Set(s64 initial, s64 interval) {
initial_delay = initial;
interval_delay = interval;
- u64 initial_microseconds = initial / 1000;
- CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type,
- callback_handle);
+ if (initial == 0) {
+ // Immediately invoke the callback
+ Signal(0);
+ } else {
+ u64 initial_microseconds = initial / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type,
+ callback_handle);
+ }
}
void Timer::Cancel() {
@@ -72,6 +77,20 @@ void Timer::WakeupAllWaitingThreads() {
signaled = false;
}
+void Timer::Signal(int cycles_late) {
+ LOG_TRACE(Kernel, "Timer %08" PRIx64 " fired", timer_handle);
+
+ // Resume all waiting threads
+ WakeupAllWaitingThreads();
+
+ if (interval_delay != 0) {
+ // Reschedule the timer with the interval delay
+ u64 interval_microseconds = interval_delay / 1000;
+ CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late,
+ timer_callback_event_type, callback_handle);
+ }
+}
+
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
SharedPtr<Timer> timer =
@@ -82,19 +101,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) {
return;
}
- LOG_TRACE(Kernel, "Timer %08" PRIx64 " fired", timer_handle);
-
- timer->signaled = true;
-
- // Resume all waiting threads
- timer->WakeupAllWaitingThreads();
-
- if (timer->interval_delay != 0) {
- // Reschedule the timer with the interval delay
- u64 interval_microseconds = timer->interval_delay / 1000;
- CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late,
- timer_callback_event_type, timer_handle);
- }
+ timer->Signal(cycles_late);
}
void TimersInit() {
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
index c174f5664..b0f818933 100644
--- a/src/core/hle/kernel/timer.h
+++ b/src/core/hle/kernel/timer.h
@@ -54,6 +54,14 @@ public:
void Cancel();
void Clear();
+ /**
+ * Signals the timer, waking up any waiting threads and rescheduling it
+ * for the next interval.
+ * This method should not be called from outside the timer callback handler,
+ * lest multiple callback events get scheduled.
+ */
+ void Signal(int cycles_late);
+
private:
Timer();
~Timer() override;
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 615fe31ea..e57b19c2d 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -18,6 +18,8 @@
#include "core/hle/service/fs/archive.h"
#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/service.h"
+#include "core/hw/aes/ccm.h"
+#include "core/hw/aes/key.h"
namespace Service {
namespace APT {
@@ -470,6 +472,107 @@ void GetStartupArgument(Service::Interface* self) {
cmd_buff[2] = 0;
}
+void Wrap(Service::Interface* self) {
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x46, 4, 4);
+ const u32 output_size = rp.Pop<u32>();
+ const u32 input_size = rp.Pop<u32>();
+ const u32 nonce_offset = rp.Pop<u32>();
+ u32 nonce_size = rp.Pop<u32>();
+ size_t desc_size;
+ IPC::MappedBufferPermissions desc_permission;
+ const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission);
+ ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R);
+ const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission);
+ ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W);
+
+ // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't
+ // check the buffer size and writes data with potential overflow.
+ ASSERT_MSG(output_size == input_size + HW::AES::CCM_MAC_SIZE,
+ "input_size (%d) doesn't match to output_size (%d)", input_size, output_size);
+
+ LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u",
+ output_size, input_size, nonce_offset, nonce_size);
+
+ // Note: This weird nonce size modification is verified against real 3DS
+ nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE);
+
+ // Reads nonce and concatenates the rest of the input as plaintext
+ HW::AES::CCMNonce nonce{};
+ Memory::ReadBlock(input + nonce_offset, nonce.data(), nonce_size);
+ u32 pdata_size = input_size - nonce_size;
+ std::vector<u8> pdata(pdata_size);
+ Memory::ReadBlock(input, pdata.data(), nonce_offset);
+ Memory::ReadBlock(input + nonce_offset + nonce_size, pdata.data() + nonce_offset,
+ pdata_size - nonce_offset);
+
+ // Encrypts the plaintext using AES-CCM
+ auto cipher = HW::AES::EncryptSignCCM(pdata, nonce, HW::AES::KeySlotID::APTWrap);
+
+ // Puts the nonce to the beginning of the output, with ciphertext followed
+ Memory::WriteBlock(output, nonce.data(), nonce_size);
+ Memory::WriteBlock(output + nonce_size, cipher.data(), cipher.size());
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 4);
+ rb.Push(RESULT_SUCCESS);
+
+ // Unmap buffer
+ rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R);
+ rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W);
+}
+
+void Unwrap(Service::Interface* self) {
+ IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x47, 4, 4);
+ const u32 output_size = rp.Pop<u32>();
+ const u32 input_size = rp.Pop<u32>();
+ const u32 nonce_offset = rp.Pop<u32>();
+ u32 nonce_size = rp.Pop<u32>();
+ size_t desc_size;
+ IPC::MappedBufferPermissions desc_permission;
+ const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission);
+ ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R);
+ const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission);
+ ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W);
+
+ // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't
+ // check the buffer size and writes data with potential overflow.
+ ASSERT_MSG(output_size == input_size - HW::AES::CCM_MAC_SIZE,
+ "input_size (%d) doesn't match to output_size (%d)", input_size, output_size);
+
+ LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u",
+ output_size, input_size, nonce_offset, nonce_size);
+
+ // Note: This weird nonce size modification is verified against real 3DS
+ nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE);
+
+ // Reads nonce and cipher text
+ HW::AES::CCMNonce nonce{};
+ Memory::ReadBlock(input, nonce.data(), nonce_size);
+ u32 cipher_size = input_size - nonce_size;
+ std::vector<u8> cipher(cipher_size);
+ Memory::ReadBlock(input + nonce_size, cipher.data(), cipher_size);
+
+ // Decrypts the ciphertext using AES-CCM
+ auto pdata = HW::AES::DecryptVerifyCCM(cipher, nonce, HW::AES::KeySlotID::APTWrap);
+
+ IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
+ if (!pdata.empty()) {
+ // Splits the plaintext and put the nonce in between
+ Memory::WriteBlock(output, pdata.data(), nonce_offset);
+ Memory::WriteBlock(output + nonce_offset, nonce.data(), nonce_size);
+ Memory::WriteBlock(output + nonce_offset + nonce_size, pdata.data() + nonce_offset,
+ pdata.size() - nonce_offset);
+ rb.Push(RESULT_SUCCESS);
+ } else {
+ LOG_ERROR(Service_APT, "Failed to decrypt data");
+ rb.Push(ResultCode(static_cast<ErrorDescription>(1), ErrorModule::PS,
+ ErrorSummary::WrongArgument, ErrorLevel::Status));
+ }
+
+ // Unmap buffer
+ rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R);
+ rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W);
+}
+
void CheckNew3DSApp(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 80325361f..e63b61450 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -137,6 +137,46 @@ void Initialize(Service::Interface* self);
void GetSharedFont(Service::Interface* self);
/**
+ * APT::Wrap service function
+ * Inputs:
+ * 1 : Output buffer size
+ * 2 : Input buffer size
+ * 3 : Nonce offset to the input buffer
+ * 4 : Nonce size
+ * 5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA)
+ * 6 : Input buffer address
+ * 7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC)
+ * 8 : Output buffer address
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA)
+ * 3 : Input buffer address
+ * 4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC)
+ * 5 : Output buffer address
+ */
+void Wrap(Service::Interface* self);
+
+/**
+ * APT::Unwrap service function
+ * Inputs:
+ * 1 : Output buffer size
+ * 2 : Input buffer size
+ * 3 : Nonce offset to the output buffer
+ * 4 : Nonce size
+ * 5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA)
+ * 6 : Input buffer address
+ * 7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC)
+ * 8 : Output buffer address
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA)
+ * 3 : Input buffer address
+ * 4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC)
+ * 5 : Output buffer address
+ */
+void Unwrap(Service::Interface* self);
+
+/**
* APT::NotifyToWait service function
* Inputs:
* 1 : AppID
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index 62dc2d61d..c496cba8d 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00430040, NotifyToWait, "NotifyToWait"},
{0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
- {0x00460104, nullptr, "Wrap"},
- {0x00470104, nullptr, "Unwrap"},
+ {0x00460104, Wrap, "Wrap"},
+ {0x00470104, Unwrap, "Unwrap"},
{0x00480100, nullptr, "GetProgramInfo"},
{0x00490180, nullptr, "Reboot"},
{0x004A0040, nullptr, "GetCaptureInfo"},
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index effd23dce..ec5668d05 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00430040, NotifyToWait, "NotifyToWait"},
{0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
- {0x00460104, nullptr, "Wrap"},
- {0x00470104, nullptr, "Unwrap"},
+ {0x00460104, Wrap, "Wrap"},
+ {0x00470104, Unwrap, "Unwrap"},
{0x00480100, nullptr, "GetProgramInfo"},
{0x00490180, nullptr, "Reboot"},
{0x004A0040, nullptr, "GetCaptureInfo"},
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index e06084a1e..9dd002590 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00430040, NotifyToWait, "NotifyToWait"},
{0x00440000, GetSharedFont, "GetSharedFont"},
{0x00450040, nullptr, "GetWirelessRebootInfo"},
- {0x00460104, nullptr, "Wrap"},
- {0x00470104, nullptr, "Unwrap"},
+ {0x00460104, Wrap, "Wrap"},
+ {0x00470104, Unwrap, "Unwrap"},
{0x00480100, nullptr, "GetProgramInfo"},
{0x00490180, nullptr, "Reboot"},
{0x004A0040, nullptr, "GetCaptureInfo"},
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 96db39ad9..1baa80671 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -837,6 +837,11 @@ static ResultCode SetTimer(Kernel::Handle handle, s64 initial, s64 interval) {
LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle);
+ if (initial < 0 || interval < 0) {
+ return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ }
+
SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle);
if (timer == nullptr)
return ERR_INVALID_HANDLE;