Compare commits

...

30 Commits
1.0.0 ... tma2

Author SHA1 Message Date
Michael Scire
fed1518fb5 cs: fix launching of tio server 2021-09-11 19:40:11 -07:00
Michael Scire
a5e761ceca cs: fix screenshot packet semantics 2021-09-11 19:40:11 -07:00
Michael Scire
a4da7cc8bf cs: implement TakeScreenShot command 2021-09-11 19:40:11 -07:00
Michael Scire
fe8d031708 cs: fix allocator aborts 2021-09-11 19:40:10 -07:00
Michael Scire
999318838f kern: improve single-step around user-exception entry 2021-09-11 19:40:10 -07:00
Michael Scire
325ae30067 kern: fix spsr register in RestoreContext 2021-09-11 19:40:10 -07:00
Michael Scire
d7c93c44f4 kern: optimize hw-single-step management 2021-09-11 19:40:07 -07:00
Michael Scire
6139b7d5ac dmnt: use hardware single step extension if available 2021-09-11 19:36:08 -07:00
Michael Scire
ae91a32059 kern: add hardware single step extension 2021-09-11 19:36:07 -07:00
Michael Scire
e34a9ba521 dmnt: implement remaining basic gdbstub packets 2021-09-11 19:36:07 -07:00
Michael Scire
252b890a12 dmnt: reload modules on NRO load/unload 2021-09-11 19:36:07 -07:00
Michael Scire
32818a7a99 dmnt: first pass at breakpoints/watchpoints 2021-09-11 19:36:07 -07:00
Michael Scire
eb6d18329e dmnt: refactor to use process accessor 2021-09-11 19:36:06 -07:00
Michael Scire
cf27c8a7a2 osdbg: implement thread info api 2021-09-11 19:36:06 -07:00
Michael Scire
74ca199c94 dmnt: add attach support to gdbstub 2021-09-11 19:36:06 -07:00
Michael Scire
22bce9f680 dmnt: refactor/add support for getting process list in gdb 2021-09-11 19:36:06 -07:00
Michael Scire
e5a0f0d3b0 dmnt: begin working on packet parser 2021-09-11 19:36:05 -07:00
Michael Scire
6da88a436f dmnt: add basic gdb packet receive logic 2021-09-11 19:36:05 -07:00
Michael Scire
e96d9950d2 dmnt2: add logging logic, for use with gdbstub development 2021-09-11 19:35:59 -07:00
Michael Scire
49afbd8a63 fix dmnt.gen2 title id (not sure how I typo'd this) 2021-09-11 19:35:14 -07:00
Michael Scire
2e0d197cbe boot2: launch dmnt.gen2 over dmnt, when using htc 2021-09-11 19:35:08 -07:00
Michael Scire
e7187ace55 tma2: include sysmodules in stratosphere.romfs 2021-09-11 19:34:04 -07:00
Michael Scire
6794c147dd cs: implement GetFirmwareVersion command 2021-09-11 19:32:33 -07:00
Michael Scire
6ea5cf5f91 scs: implement EventHandlerThread for shell 2021-09-11 19:32:33 -07:00
Michael Scire
6c2f005a62 scs: implement DoShellServer 2021-09-11 19:32:33 -07:00
SciresM
e9849c74cf LogManager: implement system module, client api, logging api (#1617)
Some notes:

* Unless `atmosphere!enable_log_manager` is true, Nintendo's log manager will be used instead.
  * This prevents paying memory costs for LM when not enabling logging.
  * To facilitate this, Atmosphere's log manager has a different program id from Nintendo's.
  * `atmosphere!enable_htc` implies `atmosphere!enable_log_manager`.
* LogManager logs to tma, and the SD card (if `lm!enable_sd_card_logging` is true, which it is by default).
* Binary logs are saved to `lm!sd_card_log_output_directory`, which is `atmosphere/binlogs` by default.
2021-09-11 19:32:14 -07:00
HamletDuFromage
a1fb8a91c8 Add arguments support to Daybreak (#1616)
* Add arguments support to Daybreak

* Check if Daybreak is run as NRO
2021-09-11 10:55:25 -07:00
Michael Scire
bfeba7c1e8 build: fix building with directory existing 2021-09-09 00:15:35 -07:00
Michael Scire
31d44d821f fusee: fix log_inverted flag parse 2021-09-08 14:32:15 -07:00
Michael Scire
44beeecc9e fusee: fix prodinfo blanking flag detection for sysmmc (closes #1610) 2021-09-07 04:34:57 -07:00
180 changed files with 12931 additions and 133 deletions

View File

@@ -97,12 +97,12 @@ dist-no-debug: all
cp config_templates/override_config.ini atmosphere-$(AMSVER)/atmosphere/config_templates/override_config.ini
cp config_templates/system_settings.ini atmosphere-$(AMSVER)/atmosphere/config_templates/system_settings.ini
cp config_templates/exosphere.ini atmosphere-$(AMSVER)/atmosphere/config_templates/exosphere.ini
mkdir config_templates/kip_patches
mkdir -p config_templates/kip_patches
cp -r config_templates/kip_patches atmosphere-$(AMSVER)/atmosphere/kip_patches
cp -r config_templates/hbl_html atmosphere-$(AMSVER)/atmosphere/hbl_html
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000017
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000032
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000034
@@ -110,8 +110,12 @@ dist-no-debug: all
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000B240
mkdir -p atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623
cp stratosphere/boot2/boot2.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000008/exefs.nsp
cp stratosphere/dmnt/dmnt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000000D/exefs.nsp
cp stratosphere/cs/cs.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000017/exefs.nsp
cp stratosphere/erpt/erpt.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000002B/exefs.nsp
cp stratosphere/eclct.stub/eclct.stub.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000032/exefs.nsp
cp stratosphere/fatal/fatal.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000034/exefs.nsp
@@ -119,6 +123,9 @@ dist-no-debug: all
cp stratosphere/ro/ro.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000037/exefs.nsp
cp stratosphere/jpegdec/jpegdec.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000003C/exefs.nsp
cp stratosphere/pgl/pgl.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp
cp stratosphere/LogManager/LogManager.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/0100000000000420/exefs.nsp
cp stratosphere/htc/htc.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000B240/exefs.nsp
cp stratosphere/TioServer/TioServer.nsp atmosphere-$(AMSVER)/stratosphere_romfs/atmosphere/contents/010000000000D623/exefs.nsp
@build_romfs atmosphere-$(AMSVER)/stratosphere_romfs atmosphere-$(AMSVER)/atmosphere/stratosphere.romfs
rm -r atmosphere-$(AMSVER)/stratosphere_romfs
cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro

View File

@@ -9,6 +9,13 @@
; Control whether RO should ease its validation of NROs.
; (note: this is normally not necessary, and ips patches can be used.)
; ease_nro_restriction = u8!0x1
[lm]
; Control whether lm should log to the SD card.
; Note that this setting does nothing when log manager is not enabled.
; enable_sd_card_logging = u8!0x1
; Control the output directory for SD card logs.
; Note that this setting does nothing when log manager is not enabled/sd card logging is not enabled.
; sd_card_log_output_directory = str!atmosphere/binlogs
; Atmosphere custom settings
[atmosphere]
; Reboot from fatal automatically after some number of milliseconds.
@@ -53,6 +60,10 @@
; Controls whether htc is enabled
; 0 = Disabled, 1 = Enabled
; enable_htc = u8!0x0
; Controls whether atmosphere's log manager is enabled
; Note that this setting is ignored (and treated as 1) when htc is enabled.
; 0 = Disabled, 1 = Enabled
; enable_log_manager = u8!0x0
[hbloader]
; Controls the size of the homebrew heap when running as applet.
; If set to zero, all available applet memory is used as heap.

View File

@@ -622,16 +622,20 @@ namespace ams::nxboot {
storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_EnableUserModePerformanceCounterAccess;
}
} else if (std::strcmp(entry.key, "blank_prodinfo_sysmmc") == 0) {
if (entry.value[0] == '1' && !emummc_enabled) {
storage_ctx.flags[0] |= secmon::SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary;
} else {
storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary;
if (!emummc_enabled) {
if (entry.value[0] == '1') {
storage_ctx.flags[0] |= secmon::SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary;
} else {
storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary;
}
}
} else if (std::strcmp(entry.key, "blank_prodinfo_emummc") == 0) {
if (entry.value[0] == '1' && emummc_enabled) {
storage_ctx.flags[0] |= secmon::SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary;
} else {
storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary;
if (emummc_enabled) {
if (entry.value[0] == '1') {
storage_ctx.flags[0] |= secmon::SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary;
} else {
storage_ctx.flags[0] &= ~secmon::SecureMonitorConfigurationFlag_ShouldUseBlankCalibrationBinary;
}
}
} else if (std::strcmp(entry.key, "allow_writing_to_cal_sysmmc") == 0) {
if (entry.value[0] == '1') {
@@ -647,7 +651,7 @@ namespace ams::nxboot {
} else if (std::strcmp(entry.key, "log_baud_rate") == 0) {
storage_ctx.log_baud_rate = ParseDecimalInteger(entry.value);
} else if (std::strcmp(entry.key, "log_inverted") == 0) {
if (entry.value[0] == 1) {
if (entry.value[0] == '1') {
storage_ctx.log_flags |= uart::Flag_Inverted;
}
}

View File

@@ -14,6 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mesosphere/kern_build_config.hpp>
/* TODO: Different header for this? */
#define AMS_KERN_NUM_SUPERVISOR_CALLS 0xC0
@@ -30,6 +31,10 @@
#define THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER 0x2D
#define THREAD_STACK_PARAMETERS_IS_PINNED 0x2E
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
#define THREAD_STACK_PARAMETERS_IS_SINGLE_STEP 0x2F
#endif
/* ams::kern::arch::arm64::KThreadContext, https://github.com/Atmosphere-NX/Atmosphere/blob/master/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_thread_context.hpp */
#define THREAD_CONTEXT_SIZE 0x290
#define THREAD_CONTEXT_CPU_REGISTERS 0x000

View File

@@ -265,6 +265,10 @@ namespace ams::kern::arch::arm64::cpu {
return this->GetBits(12, 1) != 0;
}
constexpr ALWAYS_INLINE bool GetSoftwareStep() const {
return this->GetBits(0, 1) != 0;
}
constexpr ALWAYS_INLINE decltype(auto) SetMde(bool set) {
this->SetBit(15, set);
return *this;
@@ -274,6 +278,11 @@ namespace ams::kern::arch::arm64::cpu {
this->SetBit(12, set);
return *this;
}
constexpr ALWAYS_INLINE decltype(auto) SetSoftwareStep(bool set) {
this->SetBit(0, set);
return *this;
}
};
MESOSPHERE_CPU_SYSREG_ACCESSOR_CLASS(MultiprocessorAffinity) {

View File

@@ -31,3 +31,4 @@
//#define MESOSPHERE_BUILD_FOR_TRACING
#define MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP
#define MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP

View File

@@ -92,6 +92,9 @@ namespace ams::kern {
bool is_calling_svc;
bool is_in_exception_handler;
bool is_pinned;
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
bool is_single_step;
#endif
};
static_assert(alignof(StackParameters) == 0x10);
static_assert(sizeof(StackParameters) == THREAD_STACK_PARAMETERS_SIZE);
@@ -106,6 +109,10 @@ namespace ams::kern {
static_assert(__builtin_offsetof(StackParameters, is_in_exception_handler) == THREAD_STACK_PARAMETERS_IS_IN_EXCEPTION_HANDLER);
static_assert(__builtin_offsetof(StackParameters, is_pinned) == THREAD_STACK_PARAMETERS_IS_PINNED);
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
static_assert(__builtin_offsetof(StackParameters, is_single_step) == THREAD_STACK_PARAMETERS_IS_SINGLE_STEP);
#endif
struct QueueEntry {
private:
KThread *m_prev;
@@ -325,6 +332,25 @@ namespace ams::kern {
return this->GetStackParameters().current_svc_id;
}
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
ALWAYS_INLINE void SetSingleStep() {
MESOSPHERE_ASSERT_THIS();
this->GetStackParameters().is_single_step = true;
}
ALWAYS_INLINE void ClearSingleStep() {
MESOSPHERE_ASSERT_THIS();
this->GetStackParameters().is_single_step = false;
}
ALWAYS_INLINE bool IsSingleStep() const {
MESOSPHERE_ASSERT_THIS();
return this->GetStackParameters().is_single_step;
}
#endif
ALWAYS_INLINE void RegisterDpc(DpcFlag flag) {
this->GetStackParameters().dpc_flags.fetch_or(flag);
}

View File

@@ -109,6 +109,11 @@ namespace ams::kern::arch::arm64 {
break;
}
/* In the event that we return from this exception, we want SPSR.SS set so that we advance an instruction if single-stepping. */
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
context->psr |= (1ul << 21);
#endif
/* If we should process the user exception (and it's not a breakpoint), try to enter. */
const bool is_software_break = (ec == EsrEc_Unknown || ec == EsrEc_IllegalExecution || ec == EsrEc_BkptInstruction || ec == EsrEc_BrkInstruction);
const bool is_breakpoint = (ec == EsrEc_BreakPointEl0 || ec == EsrEc_SoftwareStepEl0 || ec == EsrEc_WatchPointEl0);
@@ -215,6 +220,15 @@ namespace ams::kern::arch::arm64 {
}
}
/* If we should, clear the thread's state as single-step. */
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
if (AMS_UNLIKELY(GetCurrentThread().IsSingleStep())) {
GetCurrentThread().ClearSingleStep();
cpu::MonitorDebugSystemControlRegisterAccessor().SetSoftwareStep(false).Store();
cpu::EnsureInstructionConsistency();
}
#endif
{
/* Collect additional information based on the ec. */
ams::svc::DebugException exception;
@@ -290,16 +304,40 @@ namespace ams::kern::arch::arm64 {
return;
}
/* Print that an exception occurred. */
MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", GetCurrentProcess().GetProgramId());
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
{
/* Print the current thread's registers. */
KDebug::PrintRegister();
if (ec != EsrEc_SoftwareStepEl0) {
/* If the exception wasn't single-step, print details. */
MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", GetCurrentProcess().GetProgramId());
/* Print a backtrace. */
KDebug::PrintBacktrace();
{
/* Print the current thread's registers. */
KDebug::PrintRegister();
/* Print a backtrace. */
KDebug::PrintBacktrace();
}
} else {
/* If the exception was single-step and we have no debug object, we should just return. */
if (AMS_UNLIKELY(!cur_process.IsAttachedToDebugger())) {
return;
}
}
}
#else
{
/* Print that an exception occurred. */
MESOSPHERE_RELEASE_LOG("Exception occurred. %016lx\n", GetCurrentProcess().GetProgramId());
{
/* Print the current thread's registers. */
KDebug::PrintRegister();
/* Print a backtrace. */
KDebug::PrintBacktrace();
}
}
#endif
/* If the SVC is handled, handle it. */
if (!svc::ResultNotHandled::Includes(result)) {
@@ -559,6 +597,7 @@ namespace ams::kern::arch::arm64 {
KDpcManager::HandleDpc();
}
}
/* Note that we're no longer in an exception handler. */
GetCurrentThread().ClearInExceptionHandler();
}

View File

@@ -89,6 +89,12 @@ _ZN3ams4kern3svc14RestoreContextEm:
ldp x30, x8, [sp, #(EXCEPTION_CONTEXT_X30_SP)]
ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Since we're returning from an exception, set SPSR.SS so that we advance an instruction if single-stepping. */
orr x10, x10, #(1 << 21)
#endif
msr sp_el0, x8
msr elr_el1, x9
msr spsr_el1, x10

View File

@@ -154,6 +154,12 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
ldp x30, x8, [sp, #(EXCEPTION_CONTEXT_X30_SP)]
ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Since we're returning from an SVC, make sure SPSR.SS is cleared so that if we're single-stepping we break instantly on the instruction after the SVC. */
bic x10, x10, #(1 << 21)
#endif
msr sp_el0, x8
msr elr_el1, x9
msr spsr_el1, x10
@@ -203,6 +209,12 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev:
ldp x9, x10, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
ldr x11, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
ldr x18, [sp, #(EXCEPTION_CONTEXT_X18)]
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Since we're returning from an SVC, make sure SPSR.SS is cleared so that if we're single-stepping we break instantly on the instruction after the SVC. */
bic x10, x10, #(1 << 21)
#endif
msr sp_el0, x8
msr elr_el1, x9
msr spsr_el1, x10
@@ -359,6 +371,12 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
/* Restore registers. */
ldp x17, x20, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
ldr x19, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Since we're returning from an SVC, make sure SPSR.SS is cleared so that if we're single-stepping we break instantly on the instruction after the SVC. */
bic x20, x20, #(1 << 21)
#endif
msr elr_el1, x17
msr spsr_el1, x20
msr tpidr_el0, x19
@@ -402,6 +420,12 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev:
ldp x14, xzr, [sp, #(EXCEPTION_CONTEXT_X14_X15)]
ldp x17, x20, [sp, #(EXCEPTION_CONTEXT_PC_PSR)]
ldr x19, [sp, #(EXCEPTION_CONTEXT_TPIDR)]
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Since we're returning from an SVC, make sure SPSR.SS is cleared so that if we're single-stepping we break instantly on the instruction after the SVC. */
bic x20, x20, #(1 << 21)
#endif
msr elr_el1, x17
msr spsr_el1, x20
msr tpidr_el0, x19

View File

@@ -372,6 +372,16 @@ namespace ams::kern {
new_state = state;
}
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Clear single step on all threads. */
{
auto end = target->GetThreadList().end();
for (auto it = target->GetThreadList().begin(); it != end; ++it) {
it->ClearSingleStep();
}
}
#endif
/* Detach from the process. */
target->ClearDebugObject(new_state);
m_process = nullptr;
@@ -479,6 +489,25 @@ namespace ams::kern {
}
}
/* Update thread single-step state. */
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
{
if ((context_flags & ams::svc::ThreadContextFlag_SetSingleStep) != 0) {
/* Set single step. */
thread->SetSingleStep();
/* If no other thread flags are present, we're done. */
R_SUCCEED_IF((context_flags & ~ams::svc::ThreadContextFlag_SetSingleStep) == 0);
} else if ((context_flags & ams::svc::ThreadContextFlag_ClearSingleStep) != 0) {
/* Clear single step. */
thread->ClearSingleStep();
/* If no other thread flags are present, we're done. */
R_SUCCEED_IF((context_flags & ~ams::svc::ThreadContextFlag_ClearSingleStep) == 0);
}
}
#endif
/* Verify that the thread's svc state is valid. */
if (thread->IsCallingSvc()) {
const u8 svc_id = thread->GetSvcId();
@@ -873,6 +902,11 @@ namespace ams::kern {
{
auto end = process->GetThreadList().end();
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
/* Clear the thread's single-step state. */
it->ClearSingleStep();
#endif
if (resume) {
/* If the process isn't crashed, resume threads. */
it->Resume(KThread::SuspendType_Debug);
@@ -960,7 +994,7 @@ namespace ams::kern {
/* Set the process as breaked. */
process->SetDebugBreak();
/* If the event is an exception, set the result. */
/* If the event is an exception, set the result and clear single step. */
if (event == ams::svc::DebugEvent_Exception) {
GetCurrentThread().SetDebugExceptionResult(ResultSuccess());
}

View File

@@ -186,7 +186,24 @@ namespace ams::kern::svc {
R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNotImplemented());
/* Validate the context flags. */
R_UNLESS((context_flags | ams::svc::ThreadContextFlag_All) == ams::svc::ThreadContextFlag_All, svc::ResultInvalidEnumValue());
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
{
/* Check that the flags are a subset of the allowable. */
constexpr u32 AllFlagsMask = ams::svc::ThreadContextFlag_All | ams::svc::ThreadContextFlag_SetSingleStep | ams::svc::ThreadContextFlag_ClearSingleStep;
R_UNLESS((context_flags | AllFlagsMask) == AllFlagsMask, svc::ResultInvalidEnumValue());
/* Check that thread isn't both setting and clearing single step. */
const bool set_ss = (context_flags & ams::svc::ThreadContextFlag_SetSingleStep) != 0;
const bool clear_ss = (context_flags & ams::svc::ThreadContextFlag_ClearSingleStep) != 0;
R_UNLESS(!(set_ss && clear_ss), svc::ResultInvalidEnumValue());
}
#else
{
/* Check that the flags are a subset of the allowable. */
R_UNLESS((context_flags | ams::svc::ThreadContextFlag_All) == ams::svc::ThreadContextFlag_All, svc::ResultInvalidEnumValue());
}
#endif
/* Copy the thread context from userspace. */
ams::svc::ThreadContext context;

View File

@@ -271,6 +271,16 @@ namespace ams::kern::svc {
*out = KTraceValue;
}
break;
case ams::svc::MesosphereMetaInfo_IsSingleStepEnabled:
{
/* Return whether the kernel supports hardware single step. */
#if defined(MESOSPHERE_ENABLE_HARDWARE_SINGLE_STEP)
*out = 1;
#else
*out = 0;
#endif
}
break;
default:
return svc::ResultInvalidCombination();
}

View File

@@ -19,6 +19,9 @@
/* libvapours (pulls in util, svc, results). */
#include <vapours.hpp>
/* Libstratosphere diagnostics. */
#include <stratosphere/diag.hpp>
/* Libstratosphere definitions. */
#include <stratosphere/ams/impl/ams_system_thread_definitions.hpp>
@@ -62,11 +65,13 @@
#include <stratosphere/kvdb.hpp>
#include <stratosphere/ldr.hpp>
#include <stratosphere/lr.hpp>
#include <stratosphere/lm.hpp>
#include <stratosphere/map.hpp>
#include <stratosphere/ncm.hpp>
#include <stratosphere/nim.hpp>
#include <stratosphere/ns.hpp>
#include <stratosphere/nsd.hpp>
#include <stratosphere/osdbg.hpp>
#include <stratosphere/patcher.hpp>
#include <stratosphere/pcv.hpp>
#include <stratosphere/pgl.hpp>

View File

@@ -24,7 +24,7 @@ namespace ams::impl {
};
#define AMS_DEFINE_SYSTEM_THREAD(__AMS_THREAD_PRIORITY__, __AMS_MODULE__, __AMS_THREAD_NAME__) \
constexpr inline const ::ams::impl::SystemThreadDefinition SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__ = { __AMS_THREAD_PRIORITY__, "ams." # __AMS_MODULE__ "." #__AMS_THREAD_NAME__ }
constexpr inline const ::ams::impl::SystemThreadDefinition SystemThreadDefinition_##__AMS_MODULE__##_##__AMS_THREAD_NAME__ = { __AMS_THREAD_PRIORITY__, "ams." # __AMS_MODULE__ "." #__AMS_THREAD_NAME__ }
/* sm. */
AMS_DEFINE_SYSTEM_THREAD(-1, sm, Main);
@@ -69,6 +69,12 @@ namespace ams::impl {
/* boot2. */
AMS_DEFINE_SYSTEM_THREAD(20, boot2, Main);
/* LogManager. */
AMS_DEFINE_SYSTEM_THREAD(10, LogManager, MainThread);
AMS_DEFINE_SYSTEM_THREAD(10, lm, IpcServer);
AMS_DEFINE_SYSTEM_THREAD(10, lm, Flush);
AMS_DEFINE_SYSTEM_THREAD(10, lm, HtcsConnection);
/* dmnt. */
AMS_DEFINE_SYSTEM_THREAD(-3, dmnt, MultiCoreEventManager);
AMS_DEFINE_SYSTEM_THREAD(-1, dmnt, CheatDebugEvents);
@@ -167,5 +173,5 @@ namespace ams::impl {
}
#define AMS_GET_SYSTEM_THREAD_PRIORITY(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__.priority
#define AMS_GET_SYSTEM_THREAD_NAME(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition##__AMS_MODULE__##__AMS_THREAD_NAME__.name
#define AMS_GET_SYSTEM_THREAD_PRIORITY(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition_##__AMS_MODULE__##_##__AMS_THREAD_NAME__.priority
#define AMS_GET_SYSTEM_THREAD_NAME(__AMS_MODULE__, __AMS_THREAD_NAME__) ::ams::impl::SystemThreadDefinition_##__AMS_MODULE__##_##__AMS_THREAD_NAME__.name

View File

@@ -25,6 +25,10 @@ namespace ams::capsrv {
Result InitializeScreenShotControl();
void FinalizeScreenShotControl();
Result OpenRawScreenShotReadStreamForDevelop(size_t *out_data_size, s32 *out_width, s32 *out_height, vi::LayerStack layer_stack, TimeSpan timeout);
Result ReadRawScreenShotReadStreamForDevelop(size_t *out_read_size, void *dst, size_t dst_size, std::ptrdiff_t offset);
void CloseRawScreenShotReadStreamForDevelop();
Result CaptureJpegScreenshot(u64 *out_size, void *dst, size_t dst_size, vi::LayerStack layer_stack, TimeSpan timeout);
}

View File

@@ -16,6 +16,7 @@
#pragma once
#include <vapours.hpp>
#include <stratosphere/scs/scs_command_processor.hpp>
#include <stratosphere/vi/vi_layer_stack.hpp>
namespace ams::cs {
@@ -25,6 +26,10 @@ namespace ams::cs {
class CommandProcessor : public scs::CommandProcessor {
public:
virtual bool ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) override;
private:
void TakeScreenShot(const CommandHeader &header, s32 socket, vi::LayerStack layer_stack);
private:
static void SendFirmwareVersion(s32 socket, const CommandHeader &header);
};
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/diag/diag_log_types.hpp>
#include <stratosphere/diag/diag_log_observer.hpp>
#include <stratosphere/diag/impl/diag_impl_log.hpp>
#include <stratosphere/diag/diag_log.hpp>
#include <stratosphere/diag/diag_sdk_log.hpp>
#include <stratosphere/diag/impl/diag_utf8_util.hpp>

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/diag/impl/diag_impl_structured_log.hpp>
#if defined(AMS_IMPL_ENABLE_LOG)
#define AMS_LOG(...) AMS_IMPL_STRUCTURED_LOG_IMPL("", ::ams::diag::LogSeverity_Info, 0, __VA_ARGS__)
#define AMS_VLOG(_FMT_, _VL_) AMS_IMPL_STRUCTURED_VLOG_IMPL("", ::ams::diag::LogSeverity_Info, 0, _FMT_, _VL_)
#define AMS_PUT(_MSG_, _ML_) AMS_IMPL_STRUCTURED_PUT_IMPL("", ::ams::diag::LogSeverity_Info, 0, _MSG_, _ML_)
#define AMS_STRUCTURED_LOG(_MOD_, _SEV_, _VER_, ...) AMS_IMPL_STRUCTURED_LOG_IMPL(_MOD_, _SEV_, _VER_, __VA_ARGS__)
#define AMS_STRUCTURED_VLOG(_MOD_, _SEV_, _VER_, _FMT_, _VL_) AMS_IMPL_STRUCTURED_VLOG_IMPL(_MOD_, _SEV_, _VER_, _FMT_, _VL_)
#define AMS_STRUCTURED_PUT(_MOD_, _SEV_, _VER_, _MSG_, _ML_) AMS_IMPL_STRUCTURED_PUT_IMPL(_MOD_, _SEV_, _VER_, _MSG_, _ML_)
#else
#define AMS_LOG(...) do { /* ... */ } while (false)
#define AMS_VLOG(_FMT_, _VL_) do { /* ... */ } while (false)
#define AMS_PUT(_MSG_, _ML_) do { /* ... */ } while (false)
#define AMS_STRUCTURED_LOG(_MOD_, _SEV_, _VER_, ...) do { /* ... */ } while (false)
#define AMS_STRUCTURED_VLOG(_MOD_, _SEV_, _VER_, _FMT_, _VL_) do { /* ... */ } while (false)
#define AMS_STRUCTURED_PUT(_MOD_, _SEV_, _VER_, _MSG_, _ML_) do { /* ... */ } while (false)
#endif

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/diag/diag_log_types.hpp>
namespace ams::diag {
using LogObserver = void (*)(const LogMetaData &meta, const LogBody &body, void *arg);
struct LogObserverHolder {
LogObserver log_observer;
LogObserverHolder *next;
bool is_registered;
void *arg;
};
constexpr inline void InitializeLogObserverHolder(LogObserverHolder *holder, LogObserver observer, void *arg) {
holder->log_observer = observer;
holder->next = nullptr;
holder->is_registered = false;
holder->arg = arg;
}
void RegisterLogObserver(LogObserverHolder *holder);
void UnregisterLogObserver(LogObserverHolder *holder);
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::diag {
enum LogSeverity {
LogSeverity_Trace = 0,
LogSeverity_Info = 1,
LogSeverity_Warn = 2,
LogSeverity_Error = 3,
LogSeverity_Fatal = 4,
};
struct SourceInfo {
int line_number;
const char *file_name;
const char *function_name;
};
struct LogMetaData {
SourceInfo source_info;
const char *module_name;
LogSeverity severity;
int verbosity;
bool use_default_locale_charset;
void *additional_data;
size_t additional_data_size;
};
struct LogBody {
const char *message;
size_t message_size;
bool is_head;
bool is_tail;
};
struct LogMessage {
const char *fmt;
std::va_list *vl;
};
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/diag/impl/diag_impl_structured_log.hpp>
#if defined(AMS_IMPL_ENABLE_SDK_LOG)
#define AMS_SDK_LOG(...) AMS_IMPL_STRUCTURED_LOG_IMPL("$", ::ams::diag::LogSeverity_Info, 0, __VA_ARGS__)
#define AMS_SDK_VLOG(_FMT_, _VL_) AMS_IMPL_STRUCTURED_VLOG_IMPL("$", ::ams::diag::LogSeverity_Info, 0, _FMT_, _VL_)
#define AMS_SDK_PUT(_MSG_, _ML_) AMS_IMPL_STRUCTURED_PUT_IMPL("$", ::ams::diag::LogSeverity_Info, 0, _MSG_, _ML_)
#else
#define AMS_SDK_LOG(...) do { /* ... */ } while (false)
#define AMS_SDK_VLOG(_FMT_, _VL_) do { /* ... */ } while (false)
#define AMS_SDK_PUT(_MSG_, _ML_) do { /* ... */ } while (false)
#endif

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
#define AMS_IMPL_ENABLE_SDK_LOG
#else
//#define AMS_IMPL_ENABLE_SDK_LOG
#endif
#if defined(AMS_ENABLE_LOG)
#define AMS_IMPL_ENABLE_LOG
#if defined(AMS_DISABLE_LOG)
#error "Incoherent log configuration"
#endif
#elif defined(AMS_DISABLE_LOG)
#elif defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
#define AMS_IMPL_ENABLE_LOG
#else
//#define AMS_IMPL_ENABLE_LOG
#endif

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/diag/diag_log_types.hpp>
namespace ams::diag::impl {
void LogImpl(const LogMetaData &meta, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void VLogImpl(const LogMetaData &meta, const char *fmt, std::va_list vl);
void PutImpl(const LogMetaData &meta, const char *msg, size_t msg_size);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/diag/impl/diag_impl_build_config.hpp>
#include <stratosphere/diag/impl/diag_impl_log.hpp>
#if defined(AMS_IMPL_ENABLE_LOG) || defined(AMS_IMPL_ENABLE_SDK_LOG)
#define AMS_IMPL_LOG_META_DATA(_MOD_, _SEV_, _VER_) \
(::ams::diag::LogMetaData { \
{ __LINE__, __FILE__, AMS_CURRENT_FUNCTION_NAME }, \
_MOD_, \
_SEV_, \
_VER_, \
false, \
nullptr, \
0, \
})
#define AMS_IMPL_STRUCTURED_LOG_IMPL(_MOD_, _SEV_, _VER_, ...) ::ams::diag::impl::LogImpl(AMS_IMPL_LOG_META_DATA(_MOD_, _SEV_, _VER_), __VA_ARGS__)
#define AMS_IMPL_STRUCTURED_VLOG_IMPL(_MOD_, _SEV_, _VER_, _FMT_, _VL_) ::ams::diag::impl::VLogImpl(AMS_IMPL_LOG_META_DATA(_MOD_, _SEV_, _VER_), _FMT_, _VL_)
#define AMS_IMPL_STRUCTURED_PUT_IMPL(_MOD_, _SEV_, _VER_, _MSG_, _ML_) ::ams::diag::impl::PutImpl(AMS_IMPL_LOG_META_DATA(_MOD_, _SEV_, _VER_), _MSG_, _ML_)
#else
#define AMS_IMPL_STRUCTURED_LOG_IMPL(_MOD_, _SEV_, _VER_, ...) do { /* ... */ } while (false)
#define AMS_IMPL_STRUCTURED_VLOG_IMPL(_MOD_, _SEV_, _VER_, _FMT_, _VL_) do { /* ... */ } while (false)
#define AMS_IMPL_STRUCTURED_PUT_IMPL(_MOD_, _SEV_, _VER_, _MSG_, _ML_) do { /* ... */ } while (false)
#endif

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/diag/impl/diag_impl_build_config.hpp>
#include <stratosphere/diag/impl/diag_impl_structured_log.hpp>
#if defined(AMS_IMPL_ENABLE_SDK_LOG)
#define AMS_IMPL_STRUCTURED_SDK_LOG(_MOD_, _SEV_, _VER_, ...) AMS_IMPL_STRUCTURED_LOG_IMPL("$" #_MOD_ , ::ams::diag::LogSeverity_##_SEV_, _VER_, __VA_ARGS__)
#define AMS_IMPL_STRUCTURED_SDK_VLOG(_MOD_, _SEV_, _VER_, _FMT_, _VL_) AMS_IMPL_STRUCTURED_VLOG_IMPL("$" #_MOD_ , ::ams::diag::LogSeverity_##_SEV_, _VER_, _FMT_, _VL_)
#define AMS_IMPL_STRUCTURED_SDK_PUT(_MOD_, _SEV_, _VER_, _MSG_, _ML_) AMS_IMPL_STRUCTURED_PUT_IMPL("$" #_MOD_ , ::ams::diag::LogSeverity_##_SEV_, _VER_, _MSG_, _ML_)
#else
#define AMS_IMPL_STRUCTURED_SDK_LOG(_MOD_, _SEV_, _VER_, ...) do { /* ... */ } while (false)
#define AMS_IMPL_STRUCTURED_SDK_VLOG(_MOD_, _SEV_, _VER_, _FMT_, _VL_) do { /* ... */ } while (false)
#define AMS_IMPL_STRUCTURED_SDK_PUT(_MOD_, _SEV_, _VER_, _MSG_, _ML_) do { /* ... */ } while (false)
#endif

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::diag::impl {
int GetValidSizeAsUtf8String(const char *str, size_t len);
}

View File

@@ -22,4 +22,10 @@ namespace ams::htc::tenv {
char str[0x40];
};
constexpr inline auto PathLengthMax = 0x300;
struct alignas(4) Path {
char str[PathLengthMax];
};
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/lm/lm_types.hpp>
#include <stratosphere/lm/lm_api.hpp>
#include <stratosphere/lm/lm_log_getter.hpp>

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::lm {
void Initialize();
void Finalize();
void SetDestination(u32 destination);
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::lm {
void StartLogging();
void StopLogging();
void GetLog(char *dst, size_t size, s64 *out_write_size, u32 *out_drop_count);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::lm {
enum LogDestination {
LogDestination_TargetManager = (1 << 0),
LogDestination_Uart = (1 << 1),
LogDestination_UartIfSleep = (1 << 2),
LogDestination_All = 0xFFFF,
};
}

View File

@@ -101,6 +101,7 @@ namespace ams::ncm {
static const SystemProgramId Manu;
static const SystemProgramId Htc;
static const SystemProgramId DmntGen2;
static const SystemProgramId DevServer;
};
@@ -116,12 +117,14 @@ namespace ams::ncm {
}
static const AtmosphereProgramId Mitm;
static const AtmosphereProgramId AtmosphereLogManager;
};
inline constexpr const AtmosphereProgramId AtmosphereProgramId::Mitm = { 0x010041544D530000ul };
inline constexpr const AtmosphereProgramId AtmosphereProgramId::AtmosphereLogManager = { 0x0100000000000420ul };
inline constexpr bool IsAtmosphereProgramId(const ProgramId &program_id) {
return program_id == AtmosphereProgramId::Mitm;
return program_id == AtmosphereProgramId::Mitm || program_id == AtmosphereProgramId::AtmosphereLogManager;
}
inline constexpr bool IsSystemProgramId(const AtmosphereProgramId &program_id) {
@@ -203,6 +206,7 @@ namespace ams::ncm {
inline constexpr const SystemProgramId SystemProgramId::Manu = { 0x010000000000B14Aul };
inline constexpr const SystemProgramId SystemProgramId::Htc = { 0x010000000000B240ul };
inline constexpr const SystemProgramId SystemProgramId::DmntGen2 = { 0x010000000000D609ul };
inline constexpr const SystemProgramId SystemProgramId::DevServer = { 0x010000000000D623ul };
inline constexpr bool IsSystemProgramId(const ProgramId &program_id) {

View File

@@ -36,6 +36,8 @@ namespace ams::os {
using ThreadImpl = ::Thread;
struct ThreadType {
static constexpr u16 Magic = 0xF5A5;
enum State {
State_NotInitialized = 0,
State_Initialized = 1,
@@ -49,7 +51,9 @@ namespace ams::os {
uintptr_t reserved[4];
u8 state;
u8 suspend_count;
s32 base_priority;
u16 magic;
s16 base_priority;
u16 version;
char name_buffer[ThreadNameLengthMax];
const char *name_pointer;
ThreadId thread_id;

View File

@@ -88,6 +88,17 @@ namespace ams::os {
using WaitAnyFunction = WaitableHolderType * (*)(WaitableManagerType *);
class NotBoolButInt {
private:
int m_value;
public:
constexpr ALWAYS_INLINE NotBoolButInt(int v) : m_value(v) { /* ... */ }
constexpr ALWAYS_INLINE operator int() const { return m_value; }
explicit operator bool() const = delete;
};
}
template<typename... Args> requires (sizeof...(Args) > 0)
@@ -100,4 +111,14 @@ namespace ams::os {
return impl::WaitAnyImpl(static_cast<impl::WaitAnyFunction>(&::ams::os::WaitAny), std::forward<Args>(args)...).second;
}
template<typename... Args> requires (sizeof...(Args) > 0)
inline std::pair<WaitableHolderType *, int> TryWaitAny(WaitableManagerType *manager, Args &&... args) {
return impl::WaitAnyImpl(static_cast<impl::WaitAnyFunction>(&::ams::os::TryWaitAny), manager, std::forward<Args>(args)...);
}
template<typename... Args> requires (sizeof...(Args) > 0)
inline impl::NotBoolButInt TryWaitAny(Args &&... args) {
return impl::WaitAnyImpl(static_cast<impl::WaitAnyFunction>(&::ams::os::TryWaitAny), std::forward<Args>(args)...).second;
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/osdbg/osdbg_thread.hpp>

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/osdbg/osdbg_thread_types.hpp>
#include <stratosphere/osdbg/osdbg_thread_api.hpp>

View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/osdbg/osdbg_thread_api_impl.hpp>
namespace ams::osdbg {
struct ThreadInfo;
Result InitializeThreadInfo(ThreadInfo *thread_info, svc::Handle debug_handle, const svc::DebugInfoCreateProcess *create_process, const svc::DebugInfoCreateThread *create_thread);
Result UpdateThreadInfo(ThreadInfo *thread_info);
Result GetThreadName(char *dst, const ThreadInfo *thread_info);
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/osdbg/osdbg_thread_types.hpp>
namespace ams::osdbg {
constexpr inline s32 GetThreadPriority(const ThreadInfo *thread_info) {
return thread_info->_base_priority;
}
constexpr inline s32 GetThreadCurrentPriority(const ThreadInfo *thread_info) {
return thread_info->_current_priority;
}
constexpr inline size_t GetThreadStackSize(const ThreadInfo *thread_info) {
return thread_info->_stack_size;
}
constexpr inline uintptr_t GetThreadStackAddress(const ThreadInfo *thread_info) {
return thread_info->_stack;
}
constexpr inline uintptr_t GetThreadFunction(const ThreadInfo *thread_info) {
return thread_info->_function;
}
constexpr inline uintptr_t GetThreadFunctionArgument(const ThreadInfo *thread_info) {
return thread_info->_argument;
}
constexpr inline uintptr_t GetThreadNamePointer(const ThreadInfo *thread_info) {
return thread_info->_name_pointer;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::osdbg {
namespace impl {
union ThreadTypeCommon;
}
enum ThreadTypeType : u8 {
ThreadTypeType_Unknown = 0,
ThreadTypeType_Nintendo,
ThreadTypeType_Stratosphere,
ThreadTypeType_Libnx,
};
struct ThreadInfo {
s32 _base_priority;
s32 _current_priority;
size_t _stack_size;
uintptr_t _stack;
uintptr_t _argument;
uintptr_t _function;
uintptr_t _name_pointer;
impl::ThreadTypeCommon *_thread_type;
svc::Handle _debug_handle;
ThreadTypeType _thread_type_type;
svc::DebugInfoCreateProcess _debug_info_create_process;
svc::DebugInfoCreateThread _debug_info_create_thread;
};
}

View File

@@ -84,11 +84,17 @@ namespace ams::pgl {
class EventObserver {
NON_COPYABLE(EventObserver);
private:
std::unique_ptr<impl::EventObserverInterface> m_impl;
struct Deleter {
void operator()(impl::EventObserverInterface *);
};
public:
using UniquePtr = std::unique_ptr<impl::EventObserverInterface, Deleter>;
private:
UniquePtr m_impl;
public:
EventObserver() { /* ... */ }
explicit EventObserver(std::unique_ptr<impl::EventObserverInterface> impl) : m_impl(std::move(impl)) { /* ... */ }
explicit EventObserver(UniquePtr impl) : m_impl(std::move(impl)) { /* ... */ }
EventObserver(EventObserver &&rhs) {
m_impl = std::move(rhs.m_impl);

View File

@@ -96,6 +96,27 @@ namespace ams::pm {
struct ProcessEventInfo {
u32 event;
os::ProcessId process_id;
inline ProcessEvent GetProcessEvent() const {
if (hos::GetVersion() >= hos::Version_5_0_0) {
return static_cast<ProcessEvent>(this->event);
}
switch (static_cast<ProcessEventDeprecated>(event)) {
case ProcessEventDeprecated::None:
return ProcessEvent::None;
case ProcessEventDeprecated::Exited:
return ProcessEvent::Exited;
case ProcessEventDeprecated::Started:
return ProcessEvent::Started;
case ProcessEventDeprecated::Exception:
return ProcessEvent::Exception;
case ProcessEventDeprecated::DebugRunning:
return ProcessEvent::DebugRunning;
case ProcessEventDeprecated::DebugBreak:
return ProcessEvent::DebugBreak;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
};
static_assert(sizeof(ProcessEventInfo) == 0x10 && util::is_pod<ProcessEventInfo>::value, "ProcessEventInfo definition!");

View File

@@ -20,12 +20,13 @@
namespace ams::psc {
enum PmState {
PmState_Awake = 0,
PmState_ReadyAwaken = 1,
PmState_ReadySleep = 2,
PmState_ReadySleepCritical = 3,
PmState_ReadyAwakenCritical = 4,
PmState_ReadyShutdown = 5,
PmState_FullAwake = 0,
PmState_MinimumAwake = 1,
PmState_SleepReady = 2,
PmState_EssentialServicesSleepReady = 3,
PmState_EssentialServicesAwake = 4,
PmState_ShutdownReady = 5,
PmState_Unknown = 6,
};
constexpr inline u32 MaximumDependencyLevels = 20;

View File

@@ -19,3 +19,6 @@
#include <stratosphere/ro/ro_types.hpp>
#include <stratosphere/ro/impl/ro_ro_interface.hpp>
#include <stratosphere/ro/impl/ro_debug_monitor_interface.hpp>
#include <stratosphere/ro/impl/ro_ro_exception_info.hpp>
#include <stratosphere/rocrt/rocrt.hpp>

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::ro::impl {
struct ExceptionInfo {
uintptr_t module_address;
size_t module_size;
uintptr_t info_offset;
size_t info_size;
};
bool GetExceptionInfo(ExceptionInfo *out, uintptr_t pc);
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::rocrt {
constexpr inline const u32 ModuleHeaderVersion = util::FourCC<'M','O','D','0'>::Code;
struct ModuleHeader {
u32 signature;
u32 dynamic_offset;
u32 bss_start_offset;
u32 bss_end_offset;
u32 exception_info_start_offset;
u32 exception_info_end_offset;
u32 module_offset;
};
struct ModuleHeaderLocation {
u32 pad;
u32 header_offset;
};
constexpr inline u32 CheckModuleHeaderSignature(const ModuleHeader *header) {
if (header->signature == ModuleHeaderVersion) {
return header->signature;
} else {
return 0;
}
}
constexpr inline ModuleHeader *GetModuleHeader(const ModuleHeaderLocation *loc) {
return reinterpret_cast<ModuleHeader *>(reinterpret_cast<uintptr_t>(loc) + loc->header_offset);
}
constexpr inline uintptr_t GetDynamicOffset(const ModuleHeader *header, const ModuleHeaderLocation *loc) {
return reinterpret_cast<uintptr_t>(loc) + loc->header_offset + header->dynamic_offset;
}
constexpr inline uintptr_t GetBssStartAddress(const ModuleHeader *header, const ModuleHeaderLocation *loc) {
return reinterpret_cast<uintptr_t>(loc) + loc->header_offset + header->bss_start_offset;
}
constexpr inline uintptr_t GetBssEndAddress(const ModuleHeader *header, const ModuleHeaderLocation *loc) {
return reinterpret_cast<uintptr_t>(loc) + loc->header_offset + header->bss_end_offset;
}
constexpr inline uintptr_t GetModuleOffset(const ModuleHeader *header, const ModuleHeaderLocation *loc) {
return reinterpret_cast<uintptr_t>(loc) + loc->header_offset + header->module_offset;
}
}

View File

@@ -18,17 +18,21 @@
namespace ams::scs {
struct CommandHeader {
u64 id;
struct alignas(alignof(u32)) CommandHeader {
u64 id __attribute__((packed));
u32 command;
u32 body_size;
};
static_assert(sizeof(CommandHeader) == 0x10);
static_assert(alignof(CommandHeader) == alignof(u32));
struct ResponseHeader {
u64 id;
struct alignas(alignof(u32)) ResponseHeader {
u64 id __attribute__((packed));
u32 response;
u32 body_size;
};
static_assert(sizeof(ResponseHeader) == 0x10);
static_assert(alignof(ResponseHeader) == alignof(u32));
class CommandProcessor {
protected:
@@ -56,10 +60,15 @@ namespace ams::scs {
};
enum Response {
Response_None = 0,
Response_Success = 1,
Response_Error = 2,
/* ... */
Response_None = 0,
Response_Success = 1,
Response_Error = 2,
Response_ProgramExited = 3,
Response_FirmwareVersion = 4,
Response_JitDebug = 5,
Response_ProgramLaunched = 6,
Response_TitleName = 7,
Response_ScreenShot = 8,
};
public:
constexpr CommandProcessor() = default;
@@ -68,16 +77,20 @@ namespace ams::scs {
public:
virtual bool ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket);
protected:
static std::scoped_lock<os::SdkMutex> MakeSendGuardBlock();
static void Send(s32 socket, const void *data, size_t size);
static void SendSuccess(s32 socket, const CommandHeader &header);
static void SendErrorResult(s32 socket, const CommandHeader &header, Result result);
private:
static void SendErrorResult(s32 socket, u64 id, Result result);
static void SendExited(s32 socket, u64 id, u64 process_id);
static void SendJitDebug(s32 socket, u64 id);
static void SendLaunched(s32 socket, u64 id, u64 process_id);
static void OnProcessStart(u64 id, s32 socket, os::ProcessId process_id);
static void OnProcessExit(u64 id, s32 socket, os::ProcessId process_id);
static void OnProcessJitDebug(u64 id, s32 socket, os::ProcessId process_id);
};
os::SdkMutex &GetHtcsSendMutex();
}

View File

@@ -26,10 +26,14 @@ namespace ams::scs {
void RegisterCommonProcessEventHandler(ProcessEventHandler on_start, ProcessEventHandler on_exit, ProcessEventHandler on_jit_debug);
bool RegisterSocket(s32 socket);
Result RegisterSocket(s32 socket, u64 id);
void UnregisterSocket(s32 socket);
Result LaunchProgram(os::ProcessId *out, ncm::ProgramId program_id, const void *args, size_t args_size, u32 process_flags);
Result LaunchProgram(os::ProcessId *out, const ncm::ProgramLocation &loc, const void *args, size_t args_size, u32 process_flags);
inline Result LaunchProgram(os::ProcessId *out, ncm::ProgramId program_id, const void *args, size_t args_size, u32 process_flags) {
return LaunchProgram(out, ncm::ProgramLocation::Make(program_id, ncm::StorageId::BuiltInSystem), args, args_size, process_flags);
}
Result SubscribeProcessEvent(s32 socket, bool is_register, u64 id);

View File

@@ -17,9 +17,11 @@
#pragma once
#include <stratosphere/time/time_common.hpp>
#include <stratosphere/time/time_posix_time.hpp>
#include <stratosphere/time/time_steady_clock_time_point.hpp>
#include <stratosphere/time/time_api.hpp>
#include <stratosphere/time/time_posix_time.hpp>
#include <stratosphere/time/time_calendar_time.hpp>
#include <stratosphere/time/time_timezone_api.hpp>
#include <stratosphere/time/time_steady_clock_time_point.hpp>
#include <stratosphere/time/time_standard_steady_clock.hpp>
#include <stratosphere/time/time_standard_user_system_clock.hpp>
#include <stratosphere/time/time_standard_network_system_clock.hpp>

View File

@@ -23,4 +23,15 @@ namespace ams::time::impl::util {
Result GetSpanBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to);
bool IsLeapYear(int year);
bool IsValidDate(int year, int month, int day);
int GetDaysInMonth(int year, int month);
int DateToDays(int year, int month, int day);
void DaysToDate(int *out_year, int *out_month, int *out_day, int days);
CalendarTime ToCalendarTimeInUtc(const PosixTime &posix_time);
PosixTime ToPosixTimeFromUtc(const CalendarTime &calendar_time);
}

View File

@@ -29,6 +29,8 @@ namespace ams::time {
bool IsInitialized();
bool IsValidDate(int year, int month, int day);
Result GetElapsedSecondsBetween(s64 *out, const SteadyClockTimePoint &from, const SteadyClockTimePoint &to);
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_common.hpp>
namespace ams::time {
struct CalendarTime {
s16 year;
s8 month;
s8 day;
s8 hour;
s8 minute;
s8 second;
bool IsValid() const;
CalendarTime &operator+=(const TimeSpan &ts);
CalendarTime &operator-=(const TimeSpan &ts);
friend CalendarTime operator+(const CalendarTime &lhs, const TimeSpan &rhs);
friend CalendarTime operator-(const CalendarTime &lhs, const TimeSpan &rhs);
friend TimeSpan operator-(const CalendarTime &lhs, const CalendarTime &rhs);
constexpr friend bool operator==(const CalendarTime &lhs, const CalendarTime &rhs) { return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day && lhs.hour == rhs.hour && lhs.minute == rhs.minute && lhs.second == rhs.second; }
constexpr friend bool operator!=(const CalendarTime &lhs, const CalendarTime &rhs) { return !(lhs == rhs); }
constexpr friend bool operator<=(const CalendarTime &lhs, const CalendarTime &rhs) { return !(rhs < lhs); }
constexpr friend bool operator>=(const CalendarTime &lhs, const CalendarTime &rhs) { return !(lhs < rhs); }
constexpr friend bool operator> (const CalendarTime &lhs, const CalendarTime &rhs) { return rhs < lhs; }
constexpr friend bool operator< (const CalendarTime &lhs, const CalendarTime &rhs) {
if (!std::is_constant_evaluated()) {
AMS_ASSERT(lhs.IsValid());
AMS_ASSERT(rhs.IsValid());
}
constexpr auto ToUint64 = [] ALWAYS_INLINE_LAMBDA (const time::CalendarTime &time) {
return (static_cast<u64>(time.year) << 40) |
(static_cast<u64>(time.month) << 32) |
(static_cast<u64>(time.day) << 24) |
(static_cast<u64>(time.hour) << 16) |
(static_cast<u64>(time.minute) << 8) |
(static_cast<u64>(time.second) << 0);
};
return ToUint64(lhs) < ToUint64(rhs);
}
};
static_assert(sizeof(CalendarTime) == sizeof(u64));
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/time/time_calendar_time.hpp>
#include <stratosphere/time/time_posix_time.hpp>
namespace ams::time {
CalendarTime ToCalendarTimeInUtc(const PosixTime &posix_time);
PosixTime ToPosixTimeFromUtc(const CalendarTime &calendar_time);
}

View File

@@ -13,9 +13,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "util/util_uuid_api.hpp"
#include "util/util_compression.hpp"
#include "util/util_ini.hpp"
#include <stratosphere/util/util_uuid_api.hpp>
#include <stratosphere/util/util_compression.hpp>
#include <stratosphere/util/util_ini.hpp>
#include <stratosphere/util/util_singleton_traits.hpp>

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/os/os_sdk_mutex.hpp>
#define AMS_SINGLETON_TRAITS(_CLASSNAME_) \
private: \
NON_COPYABLE(_CLASSNAME_); \
NON_MOVEABLE(_CLASSNAME_); \
private: \
_CLASSNAME_ (); \
public: \
static _CLASSNAME_ &GetInstance() { \
/* Declare singleton instance variables. */ \
static constinit ::ams::util::TypedStorage<_CLASSNAME_> s_singleton_storage; \
static constinit ::ams::os::SdkMutex s_singleton_mutex; \
static constinit bool s_initialized_singleton = false; \
\
/* Ensure the instance is created. */ \
if (AMS_UNLIKELY(!s_initialized_singleton)) { \
std::scoped_lock lk(s_singleton_mutex); \
\
if (AMS_LIKELY(!s_initialized_singleton)) { \
new (::ams::util::GetPointer(s_singleton_storage)) _CLASSNAME_; \
s_initialized_singleton = true; \
} \
} \
\
return ::ams::util::GetReference(s_singleton_storage); \
}

View File

@@ -40,7 +40,7 @@ namespace ams::boot2 {
ncm::SystemProgramId::Vi, /* vi */
ncm::SystemProgramId::Pgl, /* pgl */
ncm::SystemProgramId::Ns, /* ns */
ncm::SystemProgramId::LogManager, /* lm */
//ncm::SystemProgramId::LogManager, /* lm */
ncm::SystemProgramId::Ppc, /* ppc */
ncm::SystemProgramId::Ptm, /* ptm */
ncm::SystemProgramId::Hid, /* hid */
@@ -83,7 +83,7 @@ namespace ams::boot2 {
ncm::SystemProgramId::Vi, /* vi */
ncm::SystemProgramId::Pgl, /* pgl */
ncm::SystemProgramId::Ns, /* ns */
ncm::SystemProgramId::LogManager, /* lm */
//ncm::SystemProgramId::LogManager, /* lm */
ncm::SystemProgramId::Ppc, /* ppc */
ncm::SystemProgramId::Ptm, /* ptm */
ncm::SystemProgramId::Hid, /* hid */
@@ -183,11 +183,22 @@ namespace ams::boot2 {
}
bool IsHtcEnabled() {
u8 enable_htc = 1;
u8 enable_htc = 0;
settings::fwdbg::GetSettingsItemValue(&enable_htc, sizeof(enable_htc), "atmosphere", "enable_htc");
return enable_htc != 0;
}
bool IsAtmosphereLogManagerEnabled() {
/* If htc is enabled, ams log manager is enabled. */
if (IsHtcEnabled()) {
return true;
}
u8 enable_ams_lm = 0;
settings::fwdbg::GetSettingsItemValue(&enable_ams_lm, sizeof(enable_ams_lm), "atmosphere", "enable_log_manager");
return enable_ams_lm != 0;
}
bool IsMaintenanceMode() {
/* Contact set:sys, retrieve boot!force_maintenance. */
if (IsForceMaintenance()) {
@@ -384,18 +395,24 @@ namespace ams::boot2 {
pm::bm::SetMaintenanceBoot();
}
/* Launch Atmosphere dmnt, using NcmStorageId_None to force SD card boot. */
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0);
/* Check for and forward declare non-atmosphere mitm modules. */
DetectAndDeclareFutureMitms();
/* Device whether to launch tma or htc. */
/* Decide whether to launch tma or htc. */
if (IsHtcEnabled()) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Htc, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Cs, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::DmntGen2, ncm::StorageId::None), 0);
} else {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Dmnt, ncm::StorageId::None), 0);
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::Tma, ncm::StorageId::BuiltInSystem), 0);
}
/* Decide whether to launch atmosphere or nintendo's log manager. */
if (IsAtmosphereLogManagerEnabled()) {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::AtmosphereProgramId::AtmosphereLogManager, ncm::StorageId::None), 0);
} else {
LaunchProgram(nullptr, ncm::ProgramLocation::Make(ncm::SystemProgramId::LogManager, ncm::StorageId::None), 0);
}
/* Launch additional programs. */

View File

@@ -29,4 +29,27 @@ namespace ams::capsrv {
return ::capsscCaptureJpegScreenShot(out_size, dst, dst_size, static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds());
}
Result OpenRawScreenShotReadStreamForDevelop(size_t *out_data_size, s32 *out_width, s32 *out_height, vi::LayerStack layer_stack, TimeSpan timeout) {
u64 data_size, width, height;
R_TRY(::capsscOpenRawScreenShotReadStream(std::addressof(data_size), std::addressof(width), std::addressof(height), static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds()));
*out_data_size = static_cast<size_t>(data_size);
*out_width = static_cast<s32>(width);
*out_height = static_cast<s32>(height);
return ResultSuccess();
}
Result ReadRawScreenShotReadStreamForDevelop(size_t *out_read_size, void *dst, size_t dst_size, std::ptrdiff_t offset) {
u64 read_size;
R_TRY(::capsscReadRawScreenShotReadStream(std::addressof(read_size), dst, dst_size, static_cast<u64>(offset)));
*out_read_size = static_cast<size_t>(read_size);
return ResultSuccess();
}
void CloseRawScreenShotReadStreamForDevelop() {
::capsscCloseRawScreenShotReadStream();
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "cs_command_impl.hpp"
namespace ams::cs {
namespace {
void SendEmptyData(const CommandDataTakeScreenShot &params, size_t remaining_size) {
/* Clear the data buffer. */
std::memset(params.buffer, 0, params.buffer_size);
/* Send data until the end. */
while (remaining_size > 0) {
/* Send as much as we can. */
const auto cur_size = std::min(remaining_size, params.buffer_size);
params.send_data(params.buffer, cur_size);
/* Advance. */
remaining_size -= cur_size;
}
}
}
Result DoGetFirmwareVersionCommand(settings::system::FirmwareVersion *out) {
settings::system::GetFirmwareVersion(out);
return ResultSuccess();
}
Result DoTakeScreenShotCommand(const CommandDataTakeScreenShot &params) {
/* Initialize screenshot control. */
R_TRY(capsrv::InitializeScreenShotControl());
/* Finalize screenshot control when we're done. */
ON_SCOPE_EXIT { capsrv::FinalizeScreenShotControl(); };
/* Open screenshot read stream. */
size_t data_size;
s32 width, height;
R_TRY(capsrv::OpenRawScreenShotReadStreamForDevelop(std::addressof(data_size), std::addressof(width), std::addressof(height), params.layer_stack, TimeSpan::FromSeconds(10)));
/* Close the screenshot stream when we're done. */
ON_SCOPE_EXIT { capsrv::CloseRawScreenShotReadStreamForDevelop(); };
/* Send the header. */
params.send_header(static_cast<s32>(data_size), width, height);
/* Read and send data. */
size_t total_read_size = 0;
auto data_guard = SCOPE_GUARD { SendEmptyData(params, data_size - total_read_size); };
while (total_read_size < data_size) {
/* Read data from the stream. */
size_t read_size;
R_TRY(capsrv::ReadRawScreenShotReadStreamForDevelop(std::addressof(read_size), params.buffer, params.buffer_size, total_read_size));
/* Send the data that was read. */
params.send_data(params.buffer, read_size);
/* Advance. */
total_read_size += read_size;
}
data_guard.Cancel();
return ResultSuccess();
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::cs {
struct CommandDataTakeScreenShot {
vi::LayerStack layer_stack;
std::function<void (s32, s32, s32)> send_header;
std::function<void (u8 *, size_t)> send_data;
u8 *buffer;
size_t buffer_size;
};
Result DoGetFirmwareVersionCommand(settings::system::FirmwareVersion *out);
Result DoTakeScreenShotCommand(const CommandDataTakeScreenShot &params);
}

View File

@@ -14,11 +14,39 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "cs_command_impl.hpp"
namespace ams::cs {
namespace {
struct ResponseFirmwareVersion {
ResponseHeader header;
settings::system::FirmwareVersion firmware_version;
};
struct ResponseTakeScreenShot {
ResponseHeader header;
s32 data_size;
s32 width;
s32 height;
};
constinit u8 g_data[0x1000];
}
bool CommandProcessor::ProcessCommand(const CommandHeader &header, const u8 *body, s32 socket) {
switch (header.command) {
case Command_GetFirmwareVersion:
SendFirmwareVersion(socket, header);
break;
case Command_TakeScreenShot:
this->TakeScreenShot(header, socket, vi::LayerStack_ApplicationForDebug);
break;
case Command_TakeForegroundScreenShot:
this->TakeScreenShot(header, socket, vi::LayerStack_LastFrame);
break;
/* TODO: Command support. */
default:
scs::CommandProcessor::ProcessCommand(header, body, socket);
@@ -28,4 +56,73 @@ namespace ams::cs {
return true;
}
void CommandProcessor::SendFirmwareVersion(s32 socket, const CommandHeader &header) {
/* Build the response. */
ResponseFirmwareVersion response = {
.header = {
.id = header.id,
.response = Response_FirmwareVersion,
.body_size = sizeof(response) - sizeof(response.header),
},
.firmware_version = {},
};
/* Get the firmware version. */
const Result result = DoGetFirmwareVersionCommand(std::addressof(response.firmware_version));
if (R_SUCCEEDED(result)) {
/* Send the response. */
auto lk = MakeSendGuardBlock();
Send(socket, std::addressof(response), sizeof(response));
} else {
SendErrorResult(socket, header, result);
}
}
void CommandProcessor::TakeScreenShot(const CommandHeader &header, s32 socket, vi::LayerStack layer_stack) {
/* Create the command data. */
const CommandDataTakeScreenShot params = {
.layer_stack = layer_stack,
.send_header = [&](s32 data_size, s32 width, s32 height) {
/* Use global buffer for response. */
ResponseTakeScreenShot *response = reinterpret_cast<ResponseTakeScreenShot *>(g_data);
/* Set response header. */
*response = {
.header = {
.id = header.id,
.response = Response_ScreenShot,
.body_size = static_cast<u32>(sizeof(data_size) + sizeof(width) + sizeof(height) + data_size),
},
.data_size = data_size,
.width = width,
.height = height,
};
/* Send data. */
Send(socket, response, sizeof(*response));
},
.send_data = [&](u8 *data, size_t data_size) {
/* Send data. */
Send(socket, data, data_size);
},
.buffer = g_data,
.buffer_size = sizeof(g_data),
};
/* Take the screenshot. */
Result result;
{
/* Acquire the send lock. */
auto lk = MakeSendGuardBlock();
/* Perform the command. */
result = DoTakeScreenShotCommand(params);
}
/* Handle the error case. */
if (R_FAILED(result)) {
SendErrorResult(socket, header, result);
}
}
}

View File

@@ -20,7 +20,7 @@ namespace ams::cs {
void InitializeTargetIoServer() {
/* Launch target io server. */
os::ProcessId process_id;
scs::LaunchProgram(std::addressof(process_id), ncm::SystemProgramId::DevServer, nullptr, 0, 0);
scs::LaunchProgram(std::addressof(process_id), ncm::ProgramLocation::Make(ncm::SystemProgramId::DevServer, ncm::StorageId::None), nullptr, 0, 0);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_log_impl.hpp"
namespace ams::diag::impl {
void CallAllLogObserver(const LogMetaData &meta, const LogBody &body);
namespace {
struct CallPrintDebugString {
void operator()(const LogMetaData &meta, const char *msg, size_t size, bool head, bool tail) {
const LogBody body = {
.message = msg,
.message_size = size,
.is_head = head,
.is_tail = tail
};
CallAllLogObserver(meta, body);
}
};
}
void LogImpl(const LogMetaData &meta, const char *fmt, ...) {
std::va_list vl;
va_start(vl, fmt);
VLogImpl(meta, fmt, vl);
va_end(vl);
}
void VLogImpl(const LogMetaData &meta, const char *fmt, std::va_list vl) {
/* Print to stack buffer. */
char msg_buffer[DebugPrintBufferLength];
/* TODO: VFormatString using utf-8 printer. */
const size_t len = util::VSNPrintf(msg_buffer, sizeof(msg_buffer), fmt, vl);
/* Call log observer. */
CallPrintDebugString()(meta, msg_buffer, len, true, true);
}
void PutImpl(const LogMetaData &meta, const char *msg, size_t msg_size) {
CallPrintDebugString()(meta, msg, msg_size, true, true);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag {
namespace impl {
constexpr inline size_t DebugPrintBufferLength = 0x80;
}
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_log_impl.hpp"
#include "impl/diag_observer_manager.hpp"
#include "impl/diag_print_debug_string.hpp"
namespace ams::diag {
namespace impl {
namespace {
constexpr inline size_t DecorationStringLengthMax = 0x61;
constexpr inline const char *EscapeSequencesForSeverity[] = {
"\x1B[90m", /* Dark Gray (Trace) */
nullptr, /* None (Info) */
"\x1B[33m", /* Yellow (Warn) */
"\x1B[31m", /* Red (Error) */
"\x1B[41m\x1B[37m", /* White-on-red (Fatal) */
};
constexpr inline const char EscapeSequenceReset[] = "\x1B[0m";
constexpr inline size_t PrintBufferLength = DecorationStringLengthMax + impl::DebugPrintBufferLength + 1;
constinit os::SdkMutex g_print_buffer_mutex;
constinit char g_print_buffer[PrintBufferLength];
inline void GetCurrentTime(int *h, int *m, int *s, int *ms) {
/* Get the current time. */
const auto cur_time = os::GetSystemTick().ToTimeSpan();
/* Extract fields. */
const s64 hours = cur_time.GetHours();
const s64 minutes = cur_time.GetMinutes();
const s64 seconds = cur_time.GetSeconds();
const s64 milliseconds = cur_time.GetMilliSeconds();
/* Set out fields. */
*h = static_cast<int>(hours);
*m = static_cast<int>(minutes - hours * 60);
*s = static_cast<int>(seconds - minutes * 60);
*ms = static_cast<int>(milliseconds - seconds * 1000);
}
void TentativeDefaultLogObserver(const LogMetaData &meta, const LogBody &body, void *) {
/* Acquire access to the print buffer */
std::scoped_lock lk(g_print_buffer_mutex);
/* Get the escape sequence. */
const char *escape = nullptr;
if (LogSeverity_Trace <= meta.severity && meta.severity <= LogSeverity_Fatal) {
escape = EscapeSequencesForSeverity[meta.severity];
}
/* Declare message variables. */
const char *msg = nullptr;
size_t msg_size = 0;
/* Handle structured logs. */
const bool structured = meta.module_name != nullptr && std::strlen(meta.module_name) >= 2;
if (escape || structured) {
/* Insert timestamp, if head. */
if (structured && body.is_head) {
/* Get current timestamp. */
int hours, minutes, seconds, milliseconds;
GetCurrentTime(std::addressof(hours), std::addressof(minutes), std::addressof(seconds), std::addressof(milliseconds));
/* Print the timestamp/header. */
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%s%d:%02d:%02d.%03d [%-5.63s] ", escape ? escape : "", hours, minutes, seconds, milliseconds, meta.module_name[0] == '$' ? meta.module_name + 1 : meta.module_name + 0);
AMS_AUDIT(msg_size <= DecorationStringLengthMax);
} else if (escape) {
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%s", escape);
}
/* Determine maximum remaining size. */
const size_t max_msg_size = PrintBufferLength - msg_size - (escape ? sizeof(EscapeSequenceReset) - 1 : 0);
/* Determine printable size. */
size_t printable_size = std::min<size_t>(body.message_size, max_msg_size);
/* Determine newline status. */
bool new_line = false;
if (body.message_size > 0 && body.message[body.message_size - 1] == '\n') {
--printable_size;
new_line = true;
}
/* Print the messsage. */
msg_size += util::SNPrintf(g_print_buffer + msg_size, PrintBufferLength - msg_size, "%.*s%s%s", static_cast<int>(printable_size), body.message, escape ? EscapeSequenceReset : "", new_line ? "\n" : "");
/* Set the message. */
msg = g_print_buffer;
} else {
/* Use the body's message directly. */
msg = body.message;
msg_size = body.message_size;
}
/* Print the string. */
impl::PrintDebugString(msg, msg_size);
}
struct LogObserverContext {
const LogMetaData &meta;
const LogBody &body;
};
using LogObserverManager = ObserverManagerWithDefaultHolder<LogObserverHolder, LogObserverContext>;
constinit LogObserverManager g_log_observer_manager(::ams::diag::InitializeLogObserverHolder, TentativeDefaultLogObserver, nullptr);
}
void CallAllLogObserver(const LogMetaData &meta, const LogBody &body) {
/* Create context. */
const LogObserverContext context = { .meta = meta, .body = body };
/* Invoke the log observer. */
g_log_observer_manager.InvokeAllObserver(context, [] (const LogObserverHolder &holder, const LogObserverContext &context) {
holder.log_observer(context.meta, context.body, holder.arg);
});
}
void ReplaceDefaultLogObserver(LogObserver observer) {
/* Get the default observer. */
auto *default_holder = std::addressof(g_log_observer_manager.GetDefaultObserverHolder());
/* Unregister, replace, and re-register. */
UnregisterLogObserver(default_holder);
InitializeLogObserverHolder(default_holder, observer, nullptr);
RegisterLogObserver(default_holder);
}
void ResetDefaultLogObserver() {
/* Restore the default observer. */
ReplaceDefaultLogObserver(TentativeDefaultLogObserver);
}
}
void RegisterLogObserver(LogObserverHolder *holder) {
impl::g_log_observer_manager.RegisterObserver(holder);
}
void UnregisterLogObserver(LogObserverHolder *holder) {
impl::g_log_observer_manager.UnregisterObserver(holder);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::diag::impl {
namespace {
constexpr inline uintptr_t ModulePathLengthOffset = 4;
constexpr inline uintptr_t ModulePathOffset = 8;
}
uintptr_t GetModuleInfoForHorizon(const char **out_path, size_t *out_path_length, size_t *out_module_size, uintptr_t address) {
/* Check for null address. */
if (address == 0) {
return 0;
}
/* Get module info. */
ro::impl::ExceptionInfo exception_info;
if (!ro::impl::GetExceptionInfo(std::addressof(exception_info), address)) {
return 0;
}
/* Locate the path in the first non-read-execute segment. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
auto cur_address = exception_info.module_address;
while (cur_address < exception_info.module_address + exception_info.module_size) {
if (R_FAILED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), cur_address))) {
return 0;
}
if (mem_info.perm != svc::MemoryPermission_ReadExecute) {
break;
}
cur_address += mem_info.size;
}
/* Set output info. */
*out_path = reinterpret_cast<const char *>(cur_address + ModulePathOffset);
*out_path_length = *reinterpret_cast<const u32 *>(cur_address + ModulePathLengthOffset);
*out_module_size = exception_info.module_size;
return exception_info.module_address;
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
template<typename Holder, typename Context>
class ObserverManager {
NON_COPYABLE(ObserverManager);
NON_MOVEABLE(ObserverManager);
private:
Holder *m_observer_list_head;
Holder **m_observer_list_tail;
os::ReadWriteLock m_lock;
public:
constexpr ObserverManager() : m_observer_list_head(nullptr), m_observer_list_tail(std::addressof(m_observer_list_head)), m_lock() {
/* ... */
}
constexpr ~ObserverManager() {
if (std::is_constant_evaluated()) {
this->UnregisterAllObserverLocked();
} else {
this->UnregisterAllObserver();
}
}
void RegisterObserver(Holder *holder) {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
this->RegisterObserverLocked(holder);
}
void UnregisterObserver(Holder *holder) {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
/* Check that we can unregister. */
AMS_ASSERT(holder->is_registered);
/* Remove the holder. */
if (m_observer_list_head == holder) {
m_observer_list_head = holder->next;
if (m_observer_list_tail == std::addressof(holder->next)) {
m_observer_list_tail = std::addressof(m_observer_list_head);
}
} else {
for (auto *cur = m_observer_list_head; cur != nullptr; cur = cur->next) {
if (cur->next == holder) {
cur->next = holder->next;
if (m_observer_list_tail == std::addressof(holder->next)) {
m_observer_list_tail = std::addressof(cur->next);
}
break;
}
}
}
/* Set unregistered. */
holder->next = nullptr;
}
void UnregisterAllObserver() {
/* Acquire a write hold on our lock. */
std::scoped_lock lk(m_lock);
this->UnregisterAllObserverLocked();
}
void InvokeAllObserver(const Context &context) {
/* Use the holder's observer. */
InvokeAllObserver(context, [] (const Holder &holder, const Context &context) {
holder.observer(context);
});
}
template<typename Observer>
void InvokeAllObserver(const Context &context, Observer observer) {
/* Acquire a read hold on our lock. */
std::shared_lock lk(m_lock);
/* Invoke all observers. */
for (const auto *holder = m_observer_list_head; holder != nullptr; holder = holder->next) {
observer(*holder, context);
}
}
protected:
constexpr void RegisterObserverLocked(Holder *holder) {
/* Check that we can register. */
AMS_ASSERT(!holder->is_registered);
/* Insert the holder. */
*m_observer_list_tail = holder;
m_observer_list_tail = std::addressof(holder->next);
/* Set registered. */
holder->next = nullptr;
holder->is_registered = true;
}
constexpr void UnregisterAllObserverLocked() {
/* Unregister all observers. */
for (auto *holder = m_observer_list_head; holder != nullptr; holder = holder->next) {
holder->is_registered = false;
}
/* Reset head/fail. */
m_observer_list_head = nullptr;
m_observer_list_tail = std::addressof(m_observer_list_head);
}
};
template<typename Holder, typename Context>
class ObserverManagerWithDefaultHolder : public ObserverManager<Holder, Context> {
private:
Holder m_default_holder;
public:
template<typename Initializer, typename... Args>
constexpr ObserverManagerWithDefaultHolder(Initializer initializer, Args &&... args) : ObserverManager<Holder, Context>(), m_default_holder{} {
/* Initialize the default observer. */
initializer(std::addressof(m_default_holder), std::forward<Args>(args)...);
/* Register the default observer. */
if (std::is_constant_evaluated()) {
this->RegisterObserverLocked(std::addressof(m_default_holder));
} else {
this->RegisterObserver(std::addressof(m_default_holder));
}
}
Holder &GetDefaultObserverHolder() {
return m_default_holder;
}
const Holder &GetDefaulObservertHolder() const {
return m_default_holder;
}
};
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::diag::impl {
void PrintDebugString(const char *msg, size_t size);
void PrintDebugString(const char *msg);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "diag_print_debug_string.hpp"
namespace ams::diag::impl {
void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size != 0) {
svc::OutputDebugString(msg, size);
}
}
void PrintDebugString(const char *msg) {
AMS_AUDIT(msg != nullptr);
PrintDebugString(msg, std::strlen(msg));
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
/* TODO: Rename, if we change to e.g. use amsMain? */
extern "C" int main(int argc, char **argv);
namespace ams::diag::impl {
uintptr_t GetModuleInfoForHorizon(const char **out_path, size_t *out_path_length, size_t *out_module_size, uintptr_t address);
namespace {
const char *GetLastCharacterPointer(const char *str, size_t len, char c) {
for (const char *last = str + len - 1; last >= str; --last) {
if (*last == c) {
return last;
}
}
return nullptr;
}
void GetFileNameWithoutExtension(const char **out, size_t *out_size, const char *path, size_t path_length) {
const auto last_sep1 = GetLastCharacterPointer(path, path_length, '\\');
const auto last_sep2 = GetLastCharacterPointer(path, path_length, '/');
const auto ext = GetLastCharacterPointer(path, path_length, '.');
/* Handle last-separator. */
if (last_sep1 && last_sep2) {
if (last_sep1 > last_sep2) {
*out = last_sep1 + 1;
} else {
*out = last_sep2 + 1;
}
} else if (last_sep1) {
*out = last_sep1 + 1;
} else if (last_sep2) {
*out = last_sep2 + 1;
} else {
*out = path;
}
/* Handle extension. */
if (ext && ext >= *out) {
*out_size = ext - *out;
} else {
*out_size = (path + path_length) - *out;
}
}
constinit const char *g_process_name = nullptr;
constinit size_t g_process_name_size = 0;
constinit os::SdkMutex g_process_name_lock;
constinit bool g_got_process_name = false;
void EnsureProcessNameCached() {
/* Ensure process name. */
if (AMS_UNLIKELY(!g_got_process_name)) {
std::scoped_lock lk(g_process_name_lock);
if (AMS_LIKELY(!g_got_process_name)) {
const char *path;
size_t path_length;
size_t module_size;
if (GetModuleInfoForHorizon(std::addressof(path), std::addressof(path_length), std::addressof(module_size), reinterpret_cast<uintptr_t>(main)) != 0) {
GetFileNameWithoutExtension(std::addressof(g_process_name), std::addressof(g_process_name_size), path, path_length);
AMS_ASSERT(g_process_name_size == 0 || util::VerifyUtf8String(g_process_name, g_process_name_size));
} else {
g_process_name = "";
g_process_name_size = 0;
}
}
}
}
}
void GetProcessNamePointer(const char **out, size_t *out_size) {
/* Ensure process name is cached. */
EnsureProcessNameCached();
/* Get cached process name. */
*out = g_process_name;
*out_size = g_process_name_size;
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::diag::impl {
namespace {
bool IsHeadOfCharacter(u8 c) {
return (c & 0xC0) != 0x80;
}
size_t GetCharacterSize(u8 c) {
if ((c & 0x80) == 0) {
return 1;
} else if ((c & 0xE0) == 0xC0) {
return 2;
} else if ((c & 0xF0) == 0xE0) {
return 3;
} else if ((c & 0xF8) == 0xF0) {
return 4;
}
return 0;
}
const char *FindLastCharacterPointer(const char *str, size_t len) {
/* Find the head of the last character. */
const char *cur;
for (cur = str + len - 1; cur >= str && !IsHeadOfCharacter(*reinterpret_cast<const u8 *>(cur)); --cur) {
/* ... */
}
/* Return the last character. */
if (AMS_LIKELY(cur >= str)) {
return cur;
} else {
return nullptr;
}
}
}
int GetValidSizeAsUtf8String(const char *str, size_t len) {
/* Check pre-condition. */
AMS_ASSERT(str != nullptr);
/* Check if we have no data. */
if (len == 0) {
return 0;
}
/* Get the last character pointer. */
const auto *last_char_ptr = FindLastCharacterPointer(str, len);
if (last_char_ptr == nullptr) {
return -1;
}
/* Get sizes. */
const size_t actual_size = (str + len) - last_char_ptr;
const size_t last_char_size = GetCharacterSize(*reinterpret_cast<const u8 *>(last_char_ptr));
if (last_char_size == 0) {
return -1;
} else if (actual_size >= last_char_size) {
if (actual_size == last_char_size) {
return len;
} else {
return -1;
}
} else if (actual_size >= len) {
AMS_ASSERT(actual_size == len);
return -1;
} else {
return static_cast<int>(len - actual_size);
}
}
}

View File

@@ -114,14 +114,14 @@ namespace ams::erpt::srv {
pm_module.GetEventPointer()->Clear();
if (R_SUCCEEDED(pm_module.GetRequest(std::addressof(pm_state), std::addressof(pm_flags)))) {
switch (pm_state) {
case psc::PmState_Awake:
case psc::PmState_ReadyAwaken:
case psc::PmState_FullAwake:
case psc::PmState_MinimumAwake:
Stream::EnableFsAccess(true);
break;
case psc::PmState_ReadyShutdown:
case psc::PmState_ShutdownReady:
FinalizeForcedShutdownDetection();
[[fallthrough]];
case psc::PmState_ReadySleep:
case psc::PmState_SleepReady:
Stream::EnableFsAccess(false);
break;
default:

View File

@@ -60,16 +60,16 @@ namespace ams::htc::server {
/* Update sleeping state. */
switch (pm_state) {
case psc::PmState_Awake:
case psc::PmState_FullAwake:
if (g_is_asleep) {
g_htclow_manager->NotifyAwake();
g_is_asleep = false;
}
break;
case psc::PmState_ReadyAwaken:
case psc::PmState_ReadySleep:
case psc::PmState_ReadySleepCritical:
case psc::PmState_ReadyAwakenCritical:
case psc::PmState_MinimumAwake:
case psc::PmState_SleepReady:
case psc::PmState_EssentialServicesSleepReady:
case psc::PmState_EssentialServicesAwake:
if (!g_is_asleep) {
g_htclow_manager->NotifyAsleep();
g_is_asleep = true;
@@ -81,16 +81,16 @@ namespace ams::htc::server {
/* Update suspend state. */
switch (pm_state) {
case psc::PmState_Awake:
case psc::PmState_ReadyAwaken:
case psc::PmState_FullAwake:
case psc::PmState_MinimumAwake:
if (g_is_suspended) {
g_htclow_manager->Resume();
g_is_suspended = false;
}
break;
case psc::PmState_ReadySleep:
case psc::PmState_ReadySleepCritical:
case psc::PmState_ReadyAwakenCritical:
case psc::PmState_SleepReady:
case psc::PmState_EssentialServicesSleepReady:
case psc::PmState_EssentialServicesAwake:
if (!g_is_suspended) {
g_htclow_manager->Suspend();
g_is_suspended = true;

View File

@@ -15,6 +15,7 @@
*/
#include <stratosphere.hpp>
#include "impl/htc_tenv_allocator.hpp"
#include "impl/htc_tenv_impl.hpp"
namespace ams::htc::tenv {
@@ -23,4 +24,8 @@ namespace ams::htc::tenv {
impl::InitializeAllocator(allocate, deallocate);
}
void UnregisterDefinitionFilePath(os::ProcessId process_id) {
return impl::UnregisterDefinitionFilePath(process_id.value);
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "htc_tenv_allocator.hpp"
namespace ams::htc::tenv::impl {
struct DefinitionFileInfo : public util::IntrusiveListBaseNode<DefinitionFileInfo> {
u64 process_id;
Path path;
DefinitionFileInfo(u64 pid, Path *p) : process_id(pid) {
AMS_ASSERT(p != nullptr);
util::Strlcpy(this->path.str, p->str, PathLengthMax);
}
static void *operator new(size_t size) {
return Allocate(size);
}
static void operator delete(void *p, size_t size) {
Deallocate(p, size);
}
};
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "htc_tenv_impl.hpp"
#include "htc_tenv_definition_file_info.hpp"
namespace ams::htc::tenv::impl {
namespace {
class DefinitionFileInfoManager {
private:
using DefinitionFileInfoList = util::IntrusiveListBaseTraits<DefinitionFileInfo>::ListType;
private:
DefinitionFileInfoList m_list;
os::SdkMutex m_mutex;
public:
constexpr DefinitionFileInfoManager() = default;
~DefinitionFileInfoManager() {
while (!m_list.empty()) {
auto *p = std::addressof(*m_list.rbegin());
m_list.erase(m_list.iterator_to(*p));
delete p;
}
}
void Remove(DefinitionFileInfo *info) {
std::scoped_lock lk(m_mutex);
m_list.erase(m_list.iterator_to(*info));
delete info;
}
DefinitionFileInfo *GetInfo(u64 process_id) {
std::scoped_lock lk(m_mutex);
for (auto &info : m_list) {
if (info.process_id == process_id) {
return std::addressof(info);
}
}
return nullptr;
}
};
constinit DefinitionFileInfoManager g_definition_file_info_manager;
ALWAYS_INLINE DefinitionFileInfoManager &GetDefinitionFileInfoManager() {
return g_definition_file_info_manager;
}
}
void UnregisterDefinitionFilePath(u64 process_id) {
/* Require the process id to be valid. */
if (process_id == 0) {
return;
}
/* Remove the definition file info, if we have one. */
if (auto *info = GetDefinitionFileInfoManager().GetInfo(process_id); info != nullptr) {
GetDefinitionFileInfoManager().Remove(info);
}
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::htc::tenv::impl {
void UnregisterDefinitionFilePath(u64 process_id);
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::lm::impl {
enum LogDataChunkKey {
LogDataChunkKey_LogSessionBegin = 0,
LogDataChunkKey_LogSessionEnd = 1,
LogDataChunkKey_TextLog = 2,
LogDataChunkKey_LineNumber = 3,
LogDataChunkKey_FileName = 4,
LogDataChunkKey_FunctionName = 5,
LogDataChunkKey_ModuleName = 6,
LogDataChunkKey_ThreadName = 7,
LogDataChunkKey_LogPacketDropCount = 8,
LogDataChunkKey_UserSystemClock = 9,
LogDataChunkKey_ProcessName = 10,
};
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::lm::impl {
constexpr inline size_t LogPacketHeaderSize = 0x18;
class LogPacketHeader {
private:
u64 m_process_id;
u64 m_thread_id;
u8 m_flags;
u8 m_padding;
u8 m_severity;
u8 m_verbosity;
u32 m_payload_size;
public:
constexpr u64 GetProcessId() const { return m_process_id; }
constexpr void SetProcessId(u64 v) { m_process_id = v; }
constexpr u64 GetThreadId() const { return m_thread_id; }
constexpr void SetThreadId(u64 v) { m_thread_id = v; }
constexpr bool IsHead() const { return (m_flags & (1 << 0)) != 0; }
constexpr void SetHead(bool v) { m_flags = (m_flags & ~(1 << 0)) | ((v ? 1 : 0) << 0); }
constexpr bool IsTail() const { return (m_flags & (1 << 1)) != 0; }
constexpr void SetTail(bool v) { m_flags = (m_flags & ~(1 << 1)) | ((v ? 1 : 0) << 1); }
constexpr bool IsLittleEndian() const { return (m_flags & (1 << 2)) != 0; }
constexpr void SetLittleEndian(bool v) { m_flags = (m_flags & ~(1 << 2)) | ((v ? 1 : 0) << 2); }
constexpr u8 GetSeverity() const { return m_severity; }
constexpr void SetSeverity(u8 v) { m_severity = v; }
constexpr u8 GetVerbosity() const { return m_verbosity; }
constexpr void SetVerbosity(u8 v) { m_verbosity = v; }
constexpr u32 GetPayloadSize() const { return m_payload_size; }
constexpr void SetPayloadSize(u32 v) { m_payload_size = v; }
};
static_assert(sizeof(LogPacketHeader) == LogPacketHeaderSize);
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "lm_log_packet_transmitter_base.hpp"
namespace ams::lm::impl {
class LogPacketTransmitter : public LogPacketTransmitterBase {
public:
LogPacketTransmitter(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail)
: LogPacketTransmitterBase(buffer, buffer_size, flush_func, severity, verbosity, process_id, head, tail) { /* ... */ }
void PushLogSessionBegin() {
bool value = true;
this->PushDataChunk(LogDataChunkKey_LogSessionBegin, std::addressof(value), sizeof(value));
}
void PushLogSessionEnd() {
bool value = true;
this->PushDataChunk(LogDataChunkKey_LogSessionEnd, std::addressof(value), sizeof(value));
}
void PushTextLog(const char *str, size_t len) {
this->PushDataChunk(LogDataChunkKey_TextLog, str, len);
}
void PushLineNumber(u32 line) {
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(line), sizeof(line));
}
void PushFileName(const char *str, size_t len) {
this->PushDataChunk(LogDataChunkKey_FileName, str, len);
}
void PushFunctionName(const char *str, size_t len) {
this->PushDataChunk(LogDataChunkKey_FunctionName, str, len);
}
void PushModuleName(const char *str, size_t len) {
this->PushDataChunk(LogDataChunkKey_ModuleName, str, len);
}
void PushThreadName(const char *str, size_t len) {
this->PushDataChunk(LogDataChunkKey_ThreadName, str, len);
}
void PushLogPacketDropCount(u64 count) {
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(count), sizeof(count));
}
void PushUserSystemClock(s64 posix_time) {
this->PushDataChunk(LogDataChunkKey_LineNumber, std::addressof(posix_time), sizeof(posix_time));
}
void PushProcessName(const char *str, size_t len) {
this->PushDataChunk(LogDataChunkKey_ProcessName, str, len);
}
};
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_log_packet_transmitter_base.hpp"
namespace ams::lm::impl {
LogPacketTransmitterBase::LogPacketTransmitterBase(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail) {
/* Check pre-conditions. */
AMS_ASSERT(buffer != nullptr);
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(buffer), alignof(LogPacketHeader)));
AMS_ASSERT(buffer_size >= LogPacketHeaderSize);
AMS_ASSERT(flush_func != nullptr);
/* Construct log packet header. */
m_header = std::construct_at(static_cast<LogPacketHeader *>(buffer));
/* Set fields. */
m_start = static_cast<u8 *>(buffer);
m_end = m_start + buffer_size;
m_payload = m_start + LogPacketHeaderSize;
m_current = m_payload;
m_is_tail = tail;
m_flush_function = flush_func;
/* Set header fields. */
m_header->SetProcessId(process_id);
m_header->SetThreadId(os::GetThreadId(os::GetCurrentThread()));
m_header->SetHead(head);
m_header->SetLittleEndian(util::IsLittleEndian());
m_header->SetSeverity(severity);
m_header->SetVerbosity(verbosity);
}
bool LogPacketTransmitterBase::Flush(bool is_tail) {
/* Check if we're already flushed. */
if (m_current == m_payload) {
return true;
}
/* Flush the data. */
m_header->SetTail(is_tail);
m_header->SetPayloadSize(static_cast<u32>(m_current - m_payload));
const auto result = m_flush_function(m_start, static_cast<size_t>(m_current - m_start));
m_header->SetHead(false);
/* Reset. */
m_current = m_payload;
return result;
}
size_t LogPacketTransmitterBase::GetRemainSize() {
return static_cast<size_t>(m_end - m_current);
}
size_t LogPacketTransmitterBase::GetPushableDataSize(size_t uleb_size) {
const size_t remain = this->GetRemainSize();
if (remain < uleb_size + 2) {
return 0;
}
const size_t cmp = remain - uleb_size;
u64 mask = 0x7F;
size_t n;
for (n = 1; mask + n < cmp; ++n) {
mask |= mask << 7;
}
return cmp - n;
}
size_t LogPacketTransmitterBase::GetRequiredSizeToPushUleb128(u64 v) {
/* Determine bytes needed for uleb128 value. */
size_t required = 0;
do {
++required;
v >>= 7;
} while (v > 0);
return required;
}
void LogPacketTransmitterBase::PushUleb128(u64 v) {
const u32 Mask = 0x7F;
const u32 InverseMask = ~Mask;
do {
/* Check we're within bounds. */
AMS_ASSERT(m_current < m_end);
/* Write byte. */
*(m_current++) = static_cast<u8>(v & Mask) | (((v & InverseMask) != 0) ? 0x80 : 0x00);
/* Adjust remaining bit range. */
v >>= 7;
} while (v > 0);
}
void LogPacketTransmitterBase::PushDataChunkImpl(LogDataChunkKey key, const void *data, size_t data_size, bool is_text) {
/* Check pre-conditions. */
AMS_ASSERT(data != nullptr);
/* Push as much data as we can, until the chunk is complete. */
const u8 *cur = static_cast<const u8 *>(data);
const u8 * const end = cur + data_size;
const size_t required_key = this->GetRequiredSizeToPushUleb128(key);
do {
/* Get the pushable size. */
size_t pushable_size = this->GetPushableDataSize(required_key);
size_t required_size = is_text ? 4 : 1;
if (pushable_size < required_size) {
this->Flush(false);
pushable_size = this->GetPushableDataSize(required_key);
}
AMS_ASSERT(pushable_size >= required_size);
/* Determine the current size. */
size_t current_size = std::min<size_t>(pushable_size, end - cur);
if (is_text) {
const auto valid_size = diag::impl::GetValidSizeAsUtf8String(reinterpret_cast<const char *>(cur), current_size);
if (valid_size >= 0) {
current_size = static_cast<size_t>(valid_size);
}
}
/* Push data. */
this->PushUleb128(key);
this->PushUleb128(current_size);
this->PushData(cur, current_size);
/* Advance. */
cur = cur + current_size;
} while (cur < end);
/* Check that we pushed all the data. */
AMS_ASSERT(cur == end);
}
void LogPacketTransmitterBase::PushData(const void *data, size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(data != nullptr);
AMS_ASSERT(size <= this->GetRemainSize());
/* Push the data. */
if (size > 0) {
std::memcpy(m_current, data, size);
m_current += size;
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "lm_log_packet_header.hpp"
#include "lm_log_data_chunk.hpp"
namespace ams::lm::impl {
class LogPacketTransmitterBase {
public:
using FlushFunction = bool (*)(const u8 *data, size_t size);
private:
LogPacketHeader *m_header;
u8 *m_start;
u8 *m_end;
u8 *m_payload;
u8 *m_current;
bool m_is_tail;
FlushFunction m_flush_function;
protected:
LogPacketTransmitterBase(void *buffer, size_t buffer_size, FlushFunction flush_func, u8 severity, u8 verbosity, u64 process_id, bool head, bool tail);
~LogPacketTransmitterBase() {
this->Flush(m_is_tail);
}
void PushDataChunk(LogDataChunkKey key, const void *data, size_t size) {
this->PushDataChunkImpl(key, data, size, false);
}
void PushDataChunk(LogDataChunkKey key, const char *str, size_t size) {
this->PushDataChunkImpl(key, str, size, true);
}
public:
bool Flush(bool is_tail);
private:
size_t GetRemainSize();
size_t GetPushableDataSize(size_t uleb_size);
size_t GetRequiredSizeToPushUleb128(u64 v);
void PushUleb128(u64 v);
void PushDataChunkImpl(LogDataChunkKey key, const void *data, size_t size, bool is_text);
void PushData(const void *data, size_t size);
};
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_remote_log_service.hpp"
#include "impl/lm_log_packet_header.hpp"
#include "impl/lm_log_packet_transmitter.hpp"
namespace ams::diag::impl {
void ReplaceDefaultLogObserver(LogObserver observer);
void ResetDefaultLogObserver();
void GetProcessNamePointer(const char **out, size_t *out_len);
}
namespace ams::lm {
namespace {
constinit sf::SharedPointer<ILogger> g_logger = nullptr;
constexpr inline size_t TransmissionBufferSize = 1_KB;
constexpr inline size_t TransmissionBufferAlign = alignof(impl::LogPacketHeader);
constinit os::SdkMutex g_transmission_buffer_mutex;
alignas(TransmissionBufferAlign) constinit char g_transmission_buffer[TransmissionBufferSize];
using PushTextFunction = void (impl::LogPacketTransmitter::*)(const char *, size_t);
bool LogPacketTransmitterFlushFunction(const u8 *data, size_t size) {
const Result result = g_logger->Log(sf::InAutoSelectBuffer(data, size));
R_ABORT_UNLESS(result);
return R_SUCCEEDED(result);
}
void InvokePushTextWithUtf8Sanitizing(impl::LogPacketTransmitter &transmitter, PushTextFunction push_func, const char *str) {
/* Get the string length. */
const auto len = std::strlen(str);
if (len == 0 || util::VerifyUtf8String(str, len)) {
(transmitter.*push_func)(str, len);
} else {
(transmitter.*push_func)("(Invalid UTF8 string)", sizeof("(Invalid UTF8 string)") - 1);
}
}
void LogManagerLogObserver(const diag::LogMetaData &meta, const diag::LogBody &body, void *) {
/* Check pre-conditions. */
AMS_ASSERT(!meta.use_default_locale_charset);
/* Acquire access to the transmission buffer. */
std::scoped_lock lk(g_transmission_buffer_mutex);
/* Create transmitter. */
impl::LogPacketTransmitter transmitter(g_transmission_buffer, TransmissionBufferSize, LogPacketTransmitterFlushFunction, static_cast<u8>(meta.severity), static_cast<u8>(meta.verbosity), 0, body.is_head, body.is_tail);
/* Push head-only logs. */
if (body.is_head) {
transmitter.PushUserSystemClock(os::GetSystemTick().ToTimeSpan().GetSeconds());
transmitter.PushLineNumber(meta.source_info.line_number);
InvokePushTextWithUtf8Sanitizing(transmitter, &impl::LogPacketTransmitter::PushFileName, meta.source_info.file_name);
InvokePushTextWithUtf8Sanitizing(transmitter, &impl::LogPacketTransmitter::PushFunctionName, meta.source_info.function_name);
InvokePushTextWithUtf8Sanitizing(transmitter, &impl::LogPacketTransmitter::PushModuleName, meta.module_name);
InvokePushTextWithUtf8Sanitizing(transmitter, &impl::LogPacketTransmitter::PushThreadName, os::GetThreadNamePointer(os::GetCurrentThread()));
const char *process_name;
size_t process_name_len;
diag::impl::GetProcessNamePointer(std::addressof(process_name), std::addressof(process_name_len));
transmitter.PushProcessName(process_name, process_name_len);
}
/* Push the actual log. */
transmitter.PushTextLog(body.message, body.message_size);
}
}
void Initialize() {
AMS_ABORT_UNLESS(g_logger == nullptr);
/* Create the logger. */
{
auto service = CreateLogService();
R_ABORT_UNLESS(service->OpenLogger(std::addressof(g_logger), sf::ClientProcessId{}));
}
/* Replace the default log observer. */
diag::impl::ReplaceDefaultLogObserver(LogManagerLogObserver);
}
void Finalize() {
AMS_ABORT_UNLESS(g_logger != nullptr);
/* Reset the default log observer. */
diag::impl::ResetDefaultLogObserver();
/* Destroy the logger. */
g_logger = nullptr;
}
void SetDestination(u32 destination) {
g_logger->SetDestination(destination);
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_remote_log_service.hpp"
#include "lm_service_name.hpp"
namespace ams::lm {
namespace {
struct LmRemoteLogServiceTag;
using RemoteAllocator = ams::sf::ExpHeapStaticAllocator<0x80, LmRemoteLogServiceTag>;
using RemoteObjectFactory = ams::sf::ObjectFactory<typename RemoteAllocator::Policy>;
class StaticAllocatorInitializer {
public:
StaticAllocatorInitializer() {
RemoteAllocator::Initialize(lmem::CreateOption_None);
}
} g_static_allocator_initializer;
}
Result RemoteLogService::OpenLogger(sf::Out<sf::SharedPointer<::ams::lm::ILogger>> out, const sf::ClientProcessId &client_process_id) {
/* Send libnx command. */
::Service logger_srv;
{
#define NX_SERVICE_ASSUME_NON_DOMAIN
R_TRY(serviceDispatch(&m_srv, 0,
.out_num_objects = 1,
.out_objects = &logger_srv,
));
#undef NX_SERVICE_ASSUME_NON_DOMAIN
}
/* Open logger. */
out.SetValue(RemoteObjectFactory::CreateSharedEmplaced<::ams::lm::ILogger, RemoteLogger>(logger_srv));
return ResultSuccess();
}
sf::SharedPointer<ILogService> CreateLogService() {
::Service srv;
R_ABORT_UNLESS(sm::GetService(std::addressof(srv), LogServiceName));
return RemoteObjectFactory::CreateSharedEmplaced<::ams::lm::ILogService, RemoteLogService>(srv);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "sf/lm_i_log_service.hpp"
namespace ams::lm {
/* TODO: Real libnx primitives? */
#define NX_SERVICE_ASSUME_NON_DOMAIN
class RemoteLogger {
private:
::Service m_srv;
public:
RemoteLogger(::Service &s) : m_srv(s) { /* ... */ }
~RemoteLogger() { ::serviceClose(std::addressof(m_srv)); }
public:
/* Actual commands. */
Result Log(const sf::InAutoSelectBuffer &message) {
return serviceDispatch(&m_srv, 0,
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcAutoSelect },
.buffers = { { message.GetPointer(), message.GetSize() } },
);
}
Result SetDestination(u32 destination) {
return serviceDispatchIn(&m_srv, 1, destination);
}
};
static_assert(lm::IsILogger<RemoteLogger>);
class RemoteLogService {
private:
::Service m_srv;
public:
RemoteLogService(::Service &s) : m_srv(s) { /* ... */ }
~RemoteLogService() { ::serviceClose(std::addressof(m_srv)); }
public:
/* Actual commands. */
Result OpenLogger(sf::Out<sf::SharedPointer<::ams::lm::ILogger>> out, const sf::ClientProcessId &client_process_id);
};
static_assert(lm::IsILogService<RemoteLogService>);
#undef NX_SERVICE_ASSUME_NON_DOMAIN
sf::SharedPointer<ILogService> CreateLogService();
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::lm {
constexpr inline const sm::ServiceName LogServiceName = sm::ServiceName::Encode("lm");
constexpr inline const sm::ServiceName LogGetterServiceName = sm::ServiceName::Encode("lm:get");
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#define AMS_LM_I_LOG_GETTER_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, StartLogging, (), ()) \
AMS_SF_METHOD_INFO(C, H, 1, Result, StopLogging, (), ()) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetLog, (const sf::OutAutoSelectBuffer &message, sf::Out<s64> out_size, sf::Out<u32> out_drop_count), (message, out_size, out_drop_count))
AMS_SF_DEFINE_INTERFACE(ams::lm, ILogGetter, AMS_LM_I_LOG_GETTER_INTERFACE_INFO)

View File

@@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#define AMS_LM_I_LOGGER_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, Log, (const sf::InAutoSelectBuffer &message), (message)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, SetDestination, (u32 destination), (destination))
AMS_SF_DEFINE_INTERFACE(ams::lm, ILogger, AMS_LM_I_LOGGER_INTERFACE_INFO)
#define AMS_LM_I_LOG_SERVICE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenLogger, (sf::Out<sf::SharedPointer<::ams::lm::ILogger>> out, const sf::ClientProcessId &client_process_id), (out, client_process_id))
AMS_SF_DEFINE_INTERFACE(ams::lm, ILogService, AMS_LM_I_LOG_SERVICE_INTERFACE_INFO)

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_custom_sink_buffer.hpp"
namespace ams::lm::srv {
bool CustomSinkBuffer::TryPush(const void *data, size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(size <= m_buffer_size);
AMS_ASSERT(data || size == 0);
/* If we have nothing to push, succeed. */
if (size == 0) {
return true;
}
/* Check that we can push the data. */
if (size > m_buffer_size - m_used_buffer_size) {
return false;
}
/* Push the data. */
std::memcpy(m_buffer + m_used_buffer_size, data, size);
m_used_buffer_size += size;
return true;
}
bool CustomSinkBuffer::TryFlush() {
/* Check that we have data to flush. */
if (m_used_buffer_size == 0) {
return false;
}
/* Try to flush the data. */
if (!m_flush_function(m_buffer, m_used_buffer_size)) {
return false;
}
/* Clear our used size. */
m_used_buffer_size = 0;
return true;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::lm::srv {
class CustomSinkBuffer {
NON_COPYABLE(CustomSinkBuffer);
NON_MOVEABLE(CustomSinkBuffer);
public:
using FlushFunction = bool (*)(const u8 *buffer, size_t buffer_size);
private:
u8 *m_buffer;
size_t m_buffer_size;
size_t m_used_buffer_size;
FlushFunction m_flush_function;
public:
constexpr explicit CustomSinkBuffer(void *buffer, size_t buffer_size, FlushFunction f) : m_buffer(static_cast<u8 *>(buffer)), m_buffer_size(buffer_size), m_used_buffer_size(0), m_flush_function(f) {
AMS_ASSERT(m_buffer != nullptr);
AMS_ASSERT(m_buffer_size > 0);
AMS_ASSERT(m_flush_function != nullptr);
}
bool TryPush(const void *data, size_t size);
bool TryFlush();
};
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_event_log_transmitter.hpp"
#include "lm_log_buffer.hpp"
#include "../impl/lm_log_packet_header.hpp"
#include "../impl/lm_log_packet_transmitter.hpp"
namespace ams::lm::srv {
namespace {
constexpr inline size_t TransmitterBufferAlign = alignof(impl::LogPacketHeader);
constexpr inline size_t TransmitterBufferSizeForSessionInfo = impl::LogPacketHeaderSize + 3;
constexpr inline size_t TransmitterBufferSizeForDropCount = impl::LogPacketHeaderSize + 10;
bool DefaultFlushFunction(const u8 *data, size_t size) {
return LogBuffer::GetDefaultInstance().TryPush(data, size);
}
}
EventLogTransmitter &EventLogTransmitter::GetDefaultInstance() {
static constinit EventLogTransmitter s_default_event_log_transmitter(DefaultFlushFunction);
return s_default_event_log_transmitter;
}
bool EventLogTransmitter::PushLogSessionBegin(u64 process_id) {
/* Create a transmitter. */
alignas(TransmitterBufferAlign) u8 buffer[TransmitterBufferSizeForSessionInfo];
impl::LogPacketTransmitter transmitter(buffer, sizeof(buffer), m_flush_function, static_cast<u8>(diag::LogSeverity_Info), 0, process_id, true, true);
/* Push session begin. */
transmitter.PushLogSessionBegin();
/* Flush the data. */
const bool success = transmitter.Flush(true);
/* Update drop count. */
if (!success) {
++m_log_packet_drop_count;
}
return success;
}
bool EventLogTransmitter::PushLogSessionEnd(u64 process_id) {
/* Create a transmitter. */
alignas(TransmitterBufferAlign) u8 buffer[TransmitterBufferSizeForSessionInfo];
impl::LogPacketTransmitter transmitter(buffer, sizeof(buffer), m_flush_function, static_cast<u8>(diag::LogSeverity_Info), 0, process_id, true, true);
/* Push session end. */
transmitter.PushLogSessionEnd();
/* Flush the data. */
const bool success = transmitter.Flush(true);
/* Update drop count. */
if (!success) {
++m_log_packet_drop_count;
}
return success;
}
bool EventLogTransmitter::PushLogPacketDropCountIfExists() {
/* Acquire exclusive access. */
std::scoped_lock lk(m_log_packet_drop_count_mutex);
/* If we have no dropped packets, nothing to push. */
if (m_log_packet_drop_count == 0) {
return true;
}
/* Create a transmitter. */
alignas(TransmitterBufferAlign) u8 buffer[TransmitterBufferSizeForDropCount];
impl::LogPacketTransmitter transmitter(buffer, sizeof(buffer), m_flush_function, static_cast<u8>(diag::LogSeverity_Info), 0, 0, true, true);
/* Push log packet drop count. */
transmitter.PushLogPacketDropCount(m_log_packet_drop_count);
/* Flush the data. */
const bool success = transmitter.Flush(true);
/* Update drop count. */
if (success) {
m_log_packet_drop_count = 0;
} else {
++m_log_packet_drop_count;
}
return success;
}
void EventLogTransmitter::IncreaseLogPacketDropCount() {
/* Acquire exclusive access. */
std::scoped_lock lk(m_log_packet_drop_count_mutex);
/* Increase the dropped packet count. */
++m_log_packet_drop_count;
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::lm::srv {
class EventLogTransmitter {
NON_COPYABLE(EventLogTransmitter);
NON_MOVEABLE(EventLogTransmitter);
public:
using FlushFunction = bool (*)(const u8 *data, size_t size);
private:
FlushFunction m_flush_function;
size_t m_log_packet_drop_count;
os::SdkMutex m_log_packet_drop_count_mutex;
public:
constexpr explicit EventLogTransmitter(FlushFunction f) : m_flush_function(f), m_log_packet_drop_count(0), m_log_packet_drop_count_mutex() {
AMS_ASSERT(f != nullptr);
}
static EventLogTransmitter &GetDefaultInstance();
bool PushLogSessionBegin(u64 process_id);
bool PushLogSessionEnd(u64 process_id);
bool PushLogPacketDropCountIfExists();
void IncreaseLogPacketDropCount();
};
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_log_server_proxy.hpp"
#include "lm_sd_card_logger.hpp"
#include "lm_log_buffer.hpp"
#include "lm_event_log_transmitter.hpp"
namespace ams::lm::srv {
bool IsSleeping();
namespace {
alignas(os::ThreadStackAlignment) u8 g_flush_thread_stack[8_KB];
constinit u8 g_fs_heap[32_KB];
constinit lmem::HeapHandle g_fs_heap_handle;
constinit os::ThreadType g_flush_thread;
os::Event g_stop_event(os::EventClearMode_ManualClear);
os::Event g_sd_logging_event(os::EventClearMode_ManualClear);
os::Event g_host_connection_event(os::EventClearMode_ManualClear);
constinit std::unique_ptr<fs::IEventNotifier> g_sd_card_detection_event_notifier;
os::SystemEvent g_sd_card_detection_event;
void *AllocateForFs(size_t size) {
return lmem::AllocateFromExpHeap(g_fs_heap_handle, size);
}
void DeallocateForFs(void *ptr, size_t size) {
return lmem::FreeToExpHeap(g_fs_heap_handle, ptr);
}
void HostConnectionObserver(bool is_connected) {
/* Update the host connection event. */
if (is_connected) {
g_host_connection_event.Signal();
} else {
g_host_connection_event.Clear();
/* Potentially cancel the log buffer push. */
if (!g_sd_logging_event.TryWait()) {
LogBuffer::GetDefaultInstance().CancelPush();
}
}
}
void SdLoggingObserver(bool is_available) {
/* Update the SD card logging event. */
if (is_available) {
g_sd_logging_event.Signal();
} else {
g_sd_logging_event.Clear();
/* Potentially cancel the log buffer push. */
if (!g_host_connection_event.TryWait()) {
LogBuffer::GetDefaultInstance().CancelPush();
}
}
}
bool WaitForFlush() {
while (true) {
/* Wait for something to be signaled. */
os::WaitAny(g_stop_event.GetBase(), g_host_connection_event.GetBase(), g_sd_logging_event.GetBase(), g_sd_card_detection_event.GetBase());
/* If we're stopping, no flush. */
if (g_stop_event.TryWait()) {
return false;
}
/* If host is connected/we're logging to sd, flush. */
if (g_host_connection_event.TryWait() || g_sd_logging_event.TryWait()) {
return true;
}
/* If the sd card is newly inserted, flush. */
if (g_sd_card_detection_event.TryWait()) {
g_sd_card_detection_event.Clear();
if (fs::IsSdCardInserted()) {
return true;
}
}
}
}
void FlushThreadFunction(void *) {
/* Disable abort. */
fs::SetEnabledAutoAbort(false);
/* Create fs heap. */
g_fs_heap_handle = lmem::CreateExpHeap(g_fs_heap, sizeof(g_fs_heap), lmem::CreateOption_None);
AMS_ABORT_UNLESS(g_fs_heap_handle != nullptr);
/* Set fs allocation functions. */
fs::SetAllocator(AllocateForFs, DeallocateForFs);
/* Create SD card detection event notifier. */
R_ABORT_UNLESS(fs::OpenSdCardDetectionEventNotifier(std::addressof(g_sd_card_detection_event_notifier)));
R_ABORT_UNLESS(g_sd_card_detection_event_notifier->BindEvent(g_sd_card_detection_event.GetBase(), os::EventClearMode_ManualClear));
/* Set connection observers. */
SdCardLogger::GetInstance().SetLoggingObserver(SdLoggingObserver);
LogServerProxy::GetInstance().SetConnectionObserver(HostConnectionObserver);
/* Do flush loop. */
do {
if (LogBuffer::GetDefaultInstance().Flush()) {
EventLogTransmitter::GetDefaultInstance().PushLogPacketDropCountIfExists();
}
} while (WaitForFlush());
/* Clear connection observer. */
LogServerProxy::GetInstance().SetConnectionObserver(nullptr);
/* Finalize the SD card logger. */
SdCardLogger::GetInstance().Finalize();
SdCardLogger::GetInstance().SetLoggingObserver(nullptr);
/* Destroy the fs heap. */
lmem::DestroyExpHeap(g_fs_heap_handle);
}
}
bool IsFlushAvailable() {
/* If we're sleeping, we can't flush. */
if (IsSleeping()) {
return false;
}
/* Try to wait for an event. */
if (os::TryWaitAny(g_stop_event.GetBase(), g_host_connection_event.GetBase(), g_sd_logging_event.GetBase()) < 0) {
return false;
}
/* Return whether we're not stopping. */
return !os::TryWaitEvent(g_stop_event.GetBase());
}
void InitializeFlushThread() {
/* Create the flush thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_flush_thread), FlushThreadFunction, nullptr, g_flush_thread_stack, sizeof(g_flush_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(lm, Flush)));
os::SetThreadNamePointer(std::addressof(g_flush_thread), AMS_GET_SYSTEM_THREAD_NAME(lm, Flush));
/* Clear the stop event. */
g_stop_event.Clear();
/* Start the flush thread. */
os::StartThread(std::addressof(g_flush_thread));
}
void FinalizeFlushThread() {
/* Signal the flush thread to stop. */
g_stop_event.Signal();
/* Wait for the flush thread to stop. */
os::WaitThread(std::addressof(g_flush_thread));
os::DestroyThread(std::addressof(g_flush_thread));
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../lm_service_name.hpp"
#include "lm_log_service_impl.hpp"
#include "lm_log_getter.hpp"
namespace ams::lm::srv {
namespace {
constexpr inline size_t LogSessionCountMax = 42;
constexpr inline size_t LogGetterSessionCountMax = 1;
constexpr inline size_t SessionCountMax = LogSessionCountMax + LogGetterSessionCountMax;
constexpr inline size_t PortCountMax = 2;
struct ServerManagerOptions {
static constexpr size_t PointerBufferSize = 0x400;
static constexpr size_t MaxDomains = 31;
static constexpr size_t MaxDomainObjects = 61;
};
using ServerManager = sf::hipc::ServerManager<PortCountMax, ServerManagerOptions, SessionCountMax>;
constinit util::TypedStorage<ServerManager> g_server_manager_storage;
constinit ServerManager *g_server_manager = nullptr;
constinit util::TypedStorage<psc::PmModule> g_pm_module_storage;
constinit psc::PmModule *g_pm_module;
constinit os::WaitableHolderType g_pm_module_holder;
constexpr const psc::PmModuleId PmModuleDependencies[] = { psc::PmModuleId_TmaHostIo, psc::PmModuleId_Fs };
/* Service objects. */
constinit sf::UnmanagedServiceObject<lm::ILogService, lm::srv::LogServiceImpl> g_log_service_object;
constinit sf::UnmanagedServiceObject<lm::ILogGetter, lm::srv::LogGetter> g_log_getter_service_object;
constinit std::atomic<bool> g_is_sleeping = false;
}
bool IsSleeping() {
return g_is_sleeping;
}
void InitializeIpcServer() {
/* Check that we're not already initialized. */
AMS_ABORT_UNLESS(g_server_manager == nullptr);
AMS_ABORT_UNLESS(g_pm_module == nullptr);
/* Create and initialize the psc module. */
g_pm_module = util::ConstructAt(g_pm_module_storage);
R_ABORT_UNLESS(g_pm_module->Initialize(psc::PmModuleId_Lm, PmModuleDependencies, util::size(PmModuleDependencies), os::EventClearMode_ManualClear));
/* Create the psc module waitable holder. */
os::InitializeWaitableHolder(std::addressof(g_pm_module_holder), g_pm_module->GetEventPointer()->GetBase());
os::SetWaitableHolderUserData(std::addressof(g_pm_module_holder), psc::PmModuleId_Lm);
/* Create the server manager. */
g_server_manager = util::ConstructAt(g_server_manager_storage);
/* Add the pm module holder. */
g_server_manager->AddUserWaitableHolder(std::addressof(g_pm_module_holder));
/* Create services. */
R_ABORT_UNLESS(g_server_manager->RegisterObjectForServer(g_log_service_object.GetShared(), LogServiceName, LogSessionCountMax));
R_ABORT_UNLESS(g_server_manager->RegisterObjectForServer(g_log_getter_service_object.GetShared(), LogGetterServiceName, LogGetterSessionCountMax));
/* Start the server manager. */
g_server_manager->ResumeProcessing();
}
void LoopIpcServer() {
/* Check that we're initialized. */
AMS_ABORT_UNLESS(g_server_manager != nullptr);
/* Loop forever, servicing the server. */
auto prev_state = psc::PmState_Unknown;
while (true) {
/* Get the next signaled holder. */
auto *signaled_holder = g_server_manager->WaitSignaled();
if (signaled_holder != std::addressof(g_pm_module_holder)) {
/* If ipc, process. */
R_ABORT_UNLESS(g_server_manager->Process(signaled_holder));
} else {
/* If pm module, clear the event. */
g_pm_module->GetEventPointer()->Clear();
g_server_manager->AddUserWaitableHolder(signaled_holder);
/* Get the power state. */
psc::PmState pm_state;
psc::PmFlagSet pm_flags;
R_ABORT_UNLESS(g_pm_module->GetRequest(std::addressof(pm_state), std::addressof(pm_flags)));
/* Handle the power state. */
if (prev_state == psc::PmState_EssentialServicesAwake && pm_state == psc::PmState_MinimumAwake) {
g_is_sleeping = false;
} else if (prev_state == psc::PmState_MinimumAwake && pm_state == psc::PmState_SleepReady) {
g_is_sleeping = true;
} else if (pm_state == psc::PmState_ShutdownReady) {
g_is_sleeping = true;
}
/* Set the previous state. */
prev_state = pm_state;
/* Acknowledge the state transition. */
R_ABORT_UNLESS(g_pm_module->Acknowledge(pm_state, ResultSuccess()));
}
}
}
void StopIpcServer() {
/* Check that we're initialized. */
AMS_ABORT_UNLESS(g_server_manager != nullptr);
/* Stop the server manager. */
g_server_manager->RequestStopProcessing();
}
void FinalizeIpcServer() {
/* Check that we're initialized. */
AMS_ABORT_UNLESS(g_server_manager != nullptr);
/* Destroy the server manager. */
std::destroy_at(g_server_manager);
g_server_manager = nullptr;
}
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_log_buffer.hpp"
#include "lm_log_server_proxy.hpp"
#include "lm_sd_card_logger.hpp"
#include "lm_time_util.hpp"
#include "lm_log_packet_parser.hpp"
namespace ams::lm::srv {
namespace {
void UpdateUserSystemClock(const u8 *data, size_t size) {
/* Get the current time. */
const time::PosixTime current_time = GetCurrentTime();
/* Get the base time. */
s64 base_time = current_time.value - os::GetSystemTick().ToTimeSpan().GetSeconds();
/* Modify the message timestamp. */
LogPacketParser::ParsePacket(data, size, [](const impl::LogPacketHeader &header, const void *payload, size_t payload_size, void *arg) -> bool {
/* Check that we're a header message. */
if (!header.IsHead()) {
return true;
}
/* Find the timestamp data chunk. */
return LogPacketParser::ParseDataChunk(payload, payload_size, [](impl::LogDataChunkKey key, const void *chunk, size_t chunk_size, void *arg) -> bool {
/* Convert the argument. */
const s64 *p_base_time = static_cast<const s64 *>(arg);
/* Modify user system clock. */
if (key == impl::LogDataChunkKey_UserSystemClock) {
/* Get the time from the chunk. */
s64 time;
AMS_ASSERT(chunk_size == sizeof(time));
std::memcpy(std::addressof(time), chunk, chunk_size);
/* Add the base time. */
time += *p_base_time;
/* Update the time in the chunk. */
std::memcpy(const_cast<void *>(chunk), std::addressof(time), sizeof(time));
}
return true;
}, arg);
}, std::addressof(base_time));
}
bool DefaultFlushFunction(const u8 *data, size_t size) {
/* Update clock. */
static constinit bool s_is_user_system_clock_updated = false;
if (!s_is_user_system_clock_updated) {
UpdateUserSystemClock(data, size);
s_is_user_system_clock_updated = true;
}
/* Send the message. */
const bool tma_success = LogServerProxy::GetInstance().Send(data, size);
const bool sd_success = SdCardLogger::GetInstance().Write(data, size);
const bool is_success = tma_success || sd_success;
/* If we succeeded, wipe the current time. */
s_is_user_system_clock_updated &= !is_success;
return is_success;
}
}
LogBuffer &LogBuffer::GetDefaultInstance() {
static constinit u8 s_default_buffers[128_KB * 2];
static constinit LogBuffer s_default_log_buffer(s_default_buffers, sizeof(s_default_buffers), DefaultFlushFunction);
return s_default_log_buffer;
}
void LogBuffer::CancelPush() {
/* Acquire exclusive access to the push buffer. */
std::scoped_lock lk(m_push_buffer_mutex);
/* Cancel any pending pushes. */
if (m_push_ready_wait_count > 0) {
m_push_canceled = true;
m_cv_push_ready.Broadcast();
}
}
bool LogBuffer::PushImpl(const void *data, size_t size, bool blocking) {
/* Check pre-conditions. */
AMS_ASSERT(size <= m_buffer_size);
AMS_ASSERT(data != nullptr || size == 0);
/* Check that we have data to push. */
if (size == 0) {
return true;
}
/* Wait to be able to push. */
u8 *dst;
{
/* Acquire exclusive access to the push buffer. */
std::scoped_lock lk(m_push_buffer_mutex);
/* Wait for enough space to be available. */
while (size > m_buffer_size - m_push_buffer->m_stored_size) {
/* Only block if we're allowed to. */
if (!blocking) {
return false;
}
/* Wait for push to be ready. */
{
++m_push_ready_wait_count;
m_cv_push_ready.Wait(m_push_buffer_mutex);
--m_push_ready_wait_count;
}
/* Check if push was canceled. */
if (m_push_canceled) {
if (m_push_ready_wait_count == 0) {
m_push_canceled = false;
}
return false;
}
}
/* Set the destination. */
dst = m_push_buffer->m_head + m_push_buffer->m_stored_size;
/* Advance the push buffer. */
m_push_buffer->m_stored_size += size;
++m_push_buffer->m_reference_count;
}
/* Copy the data to the push buffer. */
std::memcpy(dst, data, size);
/* Close our push buffer reference, and signal that we can flush. */
{
/* Acquire exclusive access to the push buffer. */
std::scoped_lock lk(m_push_buffer_mutex);
/* If there are no pending pushes, signal that we can flush. */
if ((--m_push_buffer->m_reference_count) == 0) {
m_cv_flush_ready.Signal();
}
}
return true;
}
bool LogBuffer::FlushImpl(bool blocking) {
/* Acquire exclusive access to the flush buffer. */
std::scoped_lock lk(m_flush_buffer_mutex);
/* If we don't have data to flush, wait for us to have data. */
if (m_flush_buffer->m_stored_size == 0) {
/* Acquire exclusive access to the push buffer. */
std::scoped_lock lk(m_push_buffer_mutex);
/* Wait for there to be pushed data. */
while (m_push_buffer->m_stored_size == 0 || m_push_buffer->m_reference_count != 0) {
/* Only block if we're allowed to. */
if (!blocking) {
return false;
}
/* Wait for us to be ready to flush. */
m_cv_flush_ready.Wait(m_push_buffer_mutex);
}
/* Swap the push buffer and the flush buffer pointers. */
std::swap(m_push_buffer, m_flush_buffer);
/* Signal that we can push. */
m_cv_push_ready.Broadcast();
}
/* Flush any data. */
if (!m_flush_function(m_flush_buffer->m_head, m_flush_buffer->m_stored_size)) {
return false;
}
/* Reset the flush buffer. */
m_flush_buffer->m_stored_size = 0;
return true;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::lm::srv {
class LogBuffer {
NON_COPYABLE(LogBuffer);
NON_MOVEABLE(LogBuffer);
private:
struct BufferInfo {
u8 *m_head;
size_t m_stored_size;
size_t m_reference_count;
};
public:
using FlushFunction = bool (*)(const u8 *data, size_t size);
private:
BufferInfo m_buffers[2];
BufferInfo *m_push_buffer;
BufferInfo *m_flush_buffer;
size_t m_buffer_size;
FlushFunction m_flush_function;
os::SdkMutex m_push_buffer_mutex;
os::SdkMutex m_flush_buffer_mutex;
os::SdkConditionVariable m_cv_push_ready;
os::SdkConditionVariable m_cv_flush_ready;
bool m_push_canceled;
size_t m_push_ready_wait_count;
public:
constexpr explicit LogBuffer(void *buffer, size_t buffer_size, FlushFunction f)
: m_buffers{}, m_push_buffer(m_buffers + 0), m_flush_buffer(m_buffers + 1),
m_buffer_size(buffer_size / 2), m_flush_function(f), m_push_buffer_mutex{},
m_flush_buffer_mutex{}, m_cv_push_ready{}, m_cv_flush_ready{},
m_push_canceled(false), m_push_ready_wait_count(0)
{
AMS_ASSERT(buffer != nullptr);
AMS_ASSERT(buffer_size > 0);
AMS_ASSERT(f != nullptr);
m_buffers[0].m_head = static_cast<u8 *>(buffer);
m_buffers[1].m_head = static_cast<u8 *>(buffer) + (buffer_size / 2);
}
static LogBuffer &GetDefaultInstance();
bool Push(const void *data, size_t size) { return this->PushImpl(data, size, true); }
bool TryPush(const void *data, size_t size) { return this->PushImpl(data, size, false); }
void CancelPush();
bool Flush() { return this->FlushImpl(true); }
bool TryFlush() { return this->FlushImpl(false); }
private:
bool PushImpl(const void *data, size_t size, bool blocking);
bool FlushImpl(bool blocking);
};
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_log_getter.hpp"
#include "lm_log_getter_impl.hpp"
namespace ams::lm::srv {
extern bool g_is_logging_to_custom_sink;
Result LogGetter::StartLogging() {
g_is_logging_to_custom_sink = true;
return ResultSuccess();
}
Result LogGetter::StopLogging() {
g_is_logging_to_custom_sink = false;
return ResultSuccess();
}
Result LogGetter::GetLog(const sf::OutAutoSelectBuffer &message, sf::Out<s64> out_size, sf::Out<u32> out_drop_count) {
/* Try to flush logs. */
if (LogGetterImpl::GetBuffer().TryFlush()) {
*out_size = LogGetterImpl::GetLog(message.GetPointer(), message.GetSize(), out_drop_count.GetPointer());
} else {
/* Otherwise, we got no data. */
*out_size = 0;
}
return ResultSuccess();
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../sf/lm_i_log_getter.hpp"
namespace ams::lm::srv {
class LogGetter {
public:
Result StartLogging();
Result StopLogging();
Result GetLog(const sf::OutAutoSelectBuffer &message, sf::Out<s64> out_size, sf::Out<u32> out_drop_count);
};
static_assert(lm::IsILogGetter<LogGetter>);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_log_getter_impl.hpp"
namespace ams::lm::srv {
CustomSinkBuffer &LogGetterImpl::GetBuffer() {
static constinit u8 s_buffer[32_KB];
static constinit CustomSinkBuffer s_custom_sink_buffer(s_buffer, sizeof(s_buffer), FlushFunction);
return s_custom_sink_buffer;
}
s64 LogGetterImpl::GetLog(void *buffer, size_t buffer_size, u32 *out_drop_count) {
/* Check pre-condition. */
AMS_ASSERT(buffer != nullptr);
/* Determine how much we can get. */
size_t min_size = s_buffer_size;
if (buffer_size < s_buffer_size) {
min_size = buffer_size;
IncreaseLogPacketDropCount();
}
/* Get the data. */
std::memcpy(buffer, s_message, min_size);
/* Set output drop count. */
*out_drop_count = s_log_packet_drop_count;
s_log_packet_drop_count = 0;
return min_size;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "lm_custom_sink_buffer.hpp"
namespace ams::lm::srv {
class LogGetterImpl {
NON_COPYABLE(LogGetterImpl);
NON_MOVEABLE(LogGetterImpl);
private:
static constinit inline const u8 *s_message = nullptr;
static constinit inline size_t s_buffer_size = 0;
static constinit inline size_t s_log_packet_drop_count = 0;
private:
LogGetterImpl();
public:
static CustomSinkBuffer &GetBuffer();
static s64 GetLog(void *buffer, size_t buffer_size, u32 *out_drop_count);
static void IncreaseLogPacketDropCount() { ++s_log_packet_drop_count; }
private:
static bool FlushFunction(const u8 *buffer, size_t buffer_size) {
s_message = buffer;
s_buffer_size = buffer_size;
return true;
}
};
}

View File

@@ -0,0 +1,276 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "lm_log_packet_parser.hpp"
namespace ams::lm::srv {
namespace {
const u8 *ParseUleb128(u64 *out, const u8 *cur, const u8 *end) {
u64 value = 0;
size_t shift = 0;
while (cur < end && shift + 7 <= BITSIZEOF(u64)) {
value |= static_cast<u64>(*cur & 0x7F) << shift;
if ((*cur & 0x80) == 0) {
*out = value;
return cur;
}
++cur;
shift += 7;
}
return end;
}
}
bool LogPacketParser::ParsePacket(const void *buffer, size_t buffer_size, ParsePacketCallback callback, void *arg) {
const u8 *cur = static_cast<const u8 *>(buffer);
const u8 *end = cur + buffer_size;
while (cur < end) {
/* Check that we can parse a header. */
size_t remaining_size = end - cur;
if (remaining_size < sizeof(impl::LogPacketHeader)) {
AMS_ASSERT(remaining_size >= sizeof(impl::LogPacketHeader));
return false;
}
/* Get the header. */
impl::LogPacketHeader header;
std::memcpy(std::addressof(header), cur, sizeof(header));
/* Advance past the header. */
cur += sizeof(header);
/* Check that we can parse the payload. */
const auto payload_size = header.GetPayloadSize();
remaining_size = end - cur;
if (remaining_size < payload_size) {
AMS_ASSERT(remaining_size >= payload_size);
return false;
}
/* Invoke the callback. */
if (!callback(header, cur, payload_size, arg)) {
return false;
}
/* Advance. */
cur += payload_size;
}
/* Check that we parsed all the data. */
AMS_ASSERT(cur == end);
return true;
}
bool LogPacketParser::ParseDataChunk(const void *payload, size_t payload_size, ParseDataChunkCallback callback, void *arg) {
const u8 *cur = static_cast<const u8 *>(payload);
const u8 *end = cur + payload_size;
while (cur < end) {
/* Get the key. */
u64 key;
const auto key_last = ParseUleb128(std::addressof(key), cur, end);
if (key_last >= end) {
return false;
}
cur = key_last + 1;
/* Get the size. */
u64 size;
const auto size_last = ParseUleb128(std::addressof(size), cur, end);
if (size_last >= end) {
return false;
}
cur = size_last + 1;
/* If we're in bounds, invoke the callback. */
if (cur + size <= end && !callback(static_cast<impl::LogDataChunkKey>(key), cur, size, arg)) {
return false;
}
cur += size;
}
return true;
}
bool LogPacketParser::FindDataChunk(const void **out, size_t *out_size, impl::LogDataChunkKey key, const void *buffer, size_t buffer_size) {
/* Create context for iteration. */
struct FindDataChunkContext {
const void *chunk;
size_t chunk_size;
bool found;
impl::LogDataChunkKey key;
} context = { nullptr, 0, false, key };
/* Find the chunk. */
LogPacketParser::ParsePacket(buffer, buffer_size, [](const impl::LogPacketHeader &header, const void *payload, size_t payload_size, void *arg) -> bool {
/* If the header isn't a header packet, continue. */
if (!header.IsHead()) {
return true;
}
return LogPacketParser::ParseDataChunk(payload, payload_size, [](impl::LogDataChunkKey cur_key, const void *chunk, size_t chunk_size, void *arg) -> bool {
/* Get the context. */
auto *context = static_cast<FindDataChunkContext *>(arg);
/* Check if we found the desired key. */
if (context->key == cur_key) {
context->chunk = chunk;
context->chunk_size = chunk_size;
context->found = true;
return false;
}
/* Otherwise, continue. */
return true;
}, arg);
}, std::addressof(context));
/* Write the chunk we found. */
if (context.found) {
*out = context.chunk;
*out_size = context.chunk_size;
return true;
} else {
return false;
}
}
size_t LogPacketParser::ParseModuleName(char *dst, size_t dst_size, const void *buffer, size_t buffer_size) {
/* Check pre-conditions. */
AMS_ASSERT(dst != nullptr);
AMS_ASSERT(dst_size > 0);
AMS_ASSERT(buffer != nullptr);
AMS_ASSERT(buffer_size > 0);
/* Find the relevant data chunk. */
const void *chunk;
size_t chunk_size;
const bool found = LogPacketParser::FindDataChunk(std::addressof(chunk), std::addressof(chunk_size), impl::LogDataChunkKey_ModuleName, buffer, buffer_size);
if (!found || chunk_size == 0) {
dst[0] = '\x00';
return 0;
}
/* Copy as much of the module name as we can. */
const size_t copy_size = std::min(chunk_size, dst_size - 1);
std::memcpy(dst, chunk, copy_size);
dst[copy_size] = '\x00';
return chunk_size;
}
void LogPacketParser::ParseTextLogWithContext(const void *buffer, size_t buffer_size, ParseTextLogCallback callback, void *arg) {
/* Declare context for inter-call storage. */
struct PreviousPacketContext {
u64 process_id;
u64 thread_id;
size_t carry_size;
bool ends_with_text_log;
};
static constinit PreviousPacketContext s_previous_packet_context = {};
/* Get the packet header. */
auto *header = static_cast<const impl::LogPacketHeader *>(buffer);
auto *payload = static_cast<const char *>(buffer) + impl::LogPacketHeaderSize;
auto payload_size = buffer_size - impl::LogPacketHeaderSize;
/* Determine if the packet is a continuation. */
const bool is_continuation = !header->IsHead() && header->GetProcessId() == s_previous_packet_context.process_id && header->GetThreadId() == s_previous_packet_context.thread_id;
/* Require that the packet be a header or a continuation. */
if (!header->IsHead() && !is_continuation) {
return;
}
/* If the packet is a continuation, handle the leftover data. */
if (is_continuation && s_previous_packet_context.carry_size > 0) {
/* Invoke the callback on what we can. */
const size_t sendable = std::min(s_previous_packet_context.carry_size, payload_size);
if (s_previous_packet_context.ends_with_text_log) {
callback(payload, sendable, arg);
}
/* Advance the leftover data. */
s_previous_packet_context.carry_size -= sendable;
payload += sendable;
payload_size -= sendable;
}
/* If we've sent the whole payload, we're done. */
if (payload_size == 0) {
return;
}
/* Parse the payload. */
size_t carry_size = 0;
bool ends_with_text_log = false;
{
const u8 *cur = reinterpret_cast<const u8 *>(payload);
const u8 *end = cur + payload_size;
while (cur < end) {
/* Get the key. */
u64 key;
const auto key_last = ParseUleb128(std::addressof(key), cur, end);
if (key_last >= end) {
break;
}
cur = key_last + 1;
/* Get the size. */
u64 size;
const auto size_last = ParseUleb128(std::addressof(size), cur, end);
if (size_last >= end) {
break;
}
cur = size_last + 1;
/* Process the data. */
const bool is_text_log = static_cast<impl::LogDataChunkKey>(key) == impl::LogDataChunkKey_TextLog;
const size_t remaining = end - cur;
if (size >= remaining) {
carry_size = size - remaining;
ends_with_text_log = is_text_log;
}
if (is_text_log) {
const size_t sendable_size = std::min(size, remaining);
callback(reinterpret_cast<const char *>(cur), sendable_size, arg);
}
cur += size;
}
}
/* If the packet isn't a tail packet, update the context. */
if (!header->IsTail()) {
s_previous_packet_context.process_id = header->GetProcessId();
s_previous_packet_context.thread_id = header->GetThreadId();
s_previous_packet_context.carry_size = carry_size;
s_previous_packet_context.ends_with_text_log = ends_with_text_log;
}
}
}

Some files were not shown because too many files have changed in this diff Show More