Compare commits

..

11 Commits

Author SHA1 Message Date
yellows8
94cfa2be1c
ns: Added nsEstimateSizeToMove. 2023-10-20 18:53:41 -04:00
fincs
4ff1c52869 crt0/dynamic: refactoring + add support for reprotecting relro segment 2023-10-21 00:18:04 +02:00
fincs
3a5d0dae35 Linker script refactoring, see details:
- Added separate relro "segment" (introduced by [17.0.0+])
- Reordered sections to better reflect official layout
- Fixed handling of TLS segment alignment
2023-10-21 00:18:04 +02:00
Michael Scire
288a09c4eb uart: use hosversionBetween when possible 2023-10-21 00:18:04 +02:00
Michael Scire
3bb619f46e restrict gpio/uart commands on 17.0.0 2023-10-21 00:18:04 +02:00
Michael Scire
3cb556acb3 ts: update for 17.0.0 2023-10-21 00:18:04 +02:00
Michael Scire
140400d566 svc: add PermissionLocked attribute 2023-10-21 00:18:04 +02:00
Michael Scire
6f9bc920c9 add capsdcShrinkJpeg 2023-10-21 00:18:04 +02:00
Michael Scire
e322bdf82d fs: fix GetProgramId 2023-10-21 00:18:04 +02:00
Michael Scire
e6626a7825 fsldr: update OpenCodeFileSystem for 17.0.0 2023-10-21 00:18:04 +02:00
Michael Scire
25a8f21796 ncm/fs: add new 17.0.0 commands 2023-10-21 00:18:04 +02:00
24 changed files with 448 additions and 161 deletions

View File

@ -69,10 +69,11 @@ typedef enum {
/// Memory attribute bitmasks.
typedef enum {
MemAttr_IsBorrowed=BIT(0), ///< Is borrowed memory.
MemAttr_IsIpcMapped=BIT(1), ///< Is IPC mapped (when IpcRefCount > 0).
MemAttr_IsDeviceMapped=BIT(2), ///< Is device mapped (when DeviceRefCount > 0).
MemAttr_IsUncached=BIT(3), ///< Is uncached.
MemAttr_IsBorrowed=BIT(0), ///< Is borrowed memory.
MemAttr_IsIpcMapped=BIT(1), ///< Is IPC mapped (when IpcRefCount > 0).
MemAttr_IsDeviceMapped=BIT(2), ///< Is device mapped (when DeviceRefCount > 0).
MemAttr_IsUncached=BIT(3), ///< Is uncached.
MemAttr_IsPermissionLocked=BIT(4), ///< Is permission locked.
} MemoryAttribute;
/// Memory permission bitmasks.

View File

@ -30,3 +30,8 @@ Service* capsdcGetServiceSession(void);
* @param[in] out_image_size Output image buffer size, should be at least large enough for RGBA8 width x height.
*/
Result capsdcDecodeJpeg(u32 width, u32 height, const CapsScreenShotDecodeOption *opts, const void* jpeg, size_t jpeg_size, void* out_image, size_t out_image_size);
/**
* @brief Shrinks a jpeg's dimensions by 2.
*/
Result capsdcShrinkJpeg(u32 width, u32 height, const CapsScreenShotDecodeOption *opts, const void* jpeg, size_t jpeg_size, void* out_jpeg, size_t out_jpeg_size, u64 *out_result_size);

View File

@ -405,6 +405,8 @@ Result fsOpenSdCardDetectionEventNotifier(FsEventNotifier* out);
Result fsIsSignedSystemPartitionOnSdCardValid(bool *out);
Result fsGetProgramId(u64* out, const char *path, FsContentAttributes attr); ///< [17.0.0+]
/// Retrieves the rights id corresponding to the content path. Only available on [2.0.0-15.0.1].
Result fsGetRightsIdByPath(const char* path, FsRightsId* out_rights_id);

View File

@ -64,8 +64,8 @@ Result gpioPadSetInterruptMode(GpioPadSession *p, GpioInterruptMode mode);
Result gpioPadGetInterruptMode(GpioPadSession *p, GpioInterruptMode *out);
Result gpioPadSetInterruptEnable(GpioPadSession *p, bool en);
Result gpioPadGetInterruptEnable(GpioPadSession *p, bool *out);
Result gpioPadGetInterruptStatus(GpioPadSession *p, GpioInterruptStatus *out);
Result gpioPadClearInterruptStatus(GpioPadSession *p);
Result gpioPadGetInterruptStatus(GpioPadSession *p, GpioInterruptStatus *out); ///< [1.0.0-16.1.0]
Result gpioPadClearInterruptStatus(GpioPadSession *p); ///< [1.0.0-16.1.0]
Result gpioPadSetValue(GpioPadSession *p, GpioValue val);
Result gpioPadGetValue(GpioPadSession *p, GpioValue *out);
Result gpioPadBindInterrupt(GpioPadSession *p, Event *out);

View File

@ -83,6 +83,7 @@ Result ncmContentStorageRepairInvalidFileAttribute(NcmContentStorage* cs); ///<
Result ncmContentStorageGetRightsIdFromPlaceHolderIdWithCache(NcmContentStorage* cs, NcmRightsId* out_rights_id, const NcmPlaceHolderId* placeholder_id, const NcmContentId* cache_content_id, FsContentAttributes attr); ///< [8.0.0+]
Result ncmContentStorageRegisterPath(NcmContentStorage* cs, const NcmContentId* content_id, const char *path); ///< [13.0.0+]
Result ncmContentStorageClearRegisteredPath(NcmContentStorage* cs); ///< [13.0.0+]
Result ncmContentStorageGetProgramId(NcmContentStorage* cs, u64* out, const NcmContentId* content_id, FsContentAttributes attr); ///< [17.0.0+]
void ncmContentMetaDatabaseClose(NcmContentMetaDatabase* db);
Result ncmContentMetaDatabaseSet(NcmContentMetaDatabase* db, const NcmContentMetaKey* key, const void* data, u64 data_size);
@ -106,3 +107,4 @@ Result ncmContentMetaDatabaseListContentMetaInfo(NcmContentMetaDatabase* db, s32
Result ncmContentMetaDatabaseGetAttributes(NcmContentMetaDatabase* db, const NcmContentMetaKey* key, u8* out);
Result ncmContentMetaDatabaseGetRequiredApplicationVersion(NcmContentMetaDatabase* db, u32* out_version, const NcmContentMetaKey* key); ///< [2.0.0+]
Result ncmContentMetaDatabaseGetContentIdByTypeAndIdOffset(NcmContentMetaDatabase* db, NcmContentId* out_content_id, const NcmContentMetaKey* key, NcmContentType type, u8 id_offset); ///< [5.0.0+]
Result ncmContentMetaDatabaseGetPlatform(NcmContentMetaDatabase* db, u8* out, const NcmContentMetaKey* key); ///< [17.0.0+]

View File

@ -60,6 +60,11 @@ typedef enum {
NcmContentInstallType_Unknown = 7, ///< Unknown
} NcmContentInstallType;
/// ContentMetaPlatform
typedef enum {
NcmContentMetaPlatform_Nx = 0, ///< Nx
} NcmContentMetaPlatform;
/// ContentId
typedef struct {
u8 c[0x10]; ///< Id

View File

@ -771,6 +771,18 @@ Result nsIsAnyApplicationEntityInstalled(u64 application_id, bool *out);
*/
Result nsCleanupUnavailableAddOnContents(u64 application_id, AccountUid uid);
/**
* @brief EstimateSizeToMove
* @note Only available on [10.0.0+].
* @param[in] storage_ids Array of u8 \ref NcmStorageId.
* @param[in] count Size of the storage_ids array in entries.
* @param[in] storage_id storage_id \ref NcmStorageId
* @param[in] flags Flags
* @param[in] application_id ApplicationId.
* @param[out] Out Output value.
*/
Result nsEstimateSizeToMove(u8 *storage_ids, s32 count, NcmStorageId storage_id, u32 flags, u64 application_id, s64 *out);
/**
* @brief FormatSdCard
* @note Only available on [2.0.0+].

View File

@ -14,6 +14,15 @@ typedef enum {
TsLocation_External = 1, ///< TMP451 External: SoC
} TsLocation;
typedef enum {
TsDeviceCode_LocationInternal = 0x41000001u,
TsDeviceCode_LocationExternal = 0x41000002u,
} TsDeviceCode;
typedef struct {
Service s;
} TsSession;
/// Initialize ts.
Result tsInitialize(void);
@ -45,3 +54,7 @@ Result tsGetTemperature(TsLocation location, s32 *temperature);
*/
Result tsGetTemperatureMilliC(TsLocation location, s32 *temperature);
Result tsOpenSession(TsSession *s, u32 device_code); ///< [8.0.0+]
Result tsSessionGetTemperature(TsSession *s, float *temperature); ///< [10.0.0+]
void tsSessionClose(TsSession *s);

View File

@ -54,6 +54,7 @@ Service* uartGetServiceSession(void);
/**
* @brief HasPort
* @note Only available on [1.0.0-16.1.0].
* @param[in] port \ref UartPort
* @param[out] out Output success flag.
*/
@ -61,6 +62,7 @@ Result uartHasPort(UartPort port, bool *out);
/**
* @brief HasPortForDev
* @note Only available on [1.0.0-16.1.0].
* @param[in] port \ref UartPortForDev
* @param[out] out Output success flag.
*/
@ -68,6 +70,7 @@ Result uartHasPortForDev(UartPortForDev port, bool *out);
/**
* @brief IsSupportedBaudRate
* @note Only available on [1.0.0-16.1.0].
* @param[in] port \ref UartPort
* @param[in] baud_rate BaudRate
* @param[out] out Output success flag.
@ -76,6 +79,7 @@ Result uartIsSupportedBaudRate(UartPort port, u32 baud_rate, bool *out);
/**
* @brief IsSupportedBaudRateForDev
* @note Only available on [1.0.0-16.1.0].
* @param[in] port \ref UartPortForDev
* @param[in] baud_rate BaudRate
* @param[out] out Output success flag.
@ -84,6 +88,7 @@ Result uartIsSupportedBaudRateForDev(UartPortForDev port, u32 baud_rate, bool *o
/**
* @brief IsSupportedFlowControlMode
* @note Only available on [1.0.0-16.1.0].
* @param[in] port \ref UartPort
* @param[in] flow_control_mode \ref UartFlowControlMode
* @param[out] out Output success flag.
@ -92,6 +97,7 @@ Result uartIsSupportedFlowControlMode(UartPort port, UartFlowControlMode flow_co
/**
* @brief IsSupportedFlowControlModeForDev
* @note Only available on [1.0.0-16.1.0].
* @param[in] port \ref UartPortForDev
* @param[in] flow_control_mode \ref UartFlowControlMode
* @param[out] out Output success flag.
@ -107,6 +113,7 @@ Result uartCreatePortSession(UartPortSession *s);
/**
* @brief IsSupportedPortEvent
* @note Only available on [1.0.0-16.1.0].
* @param[in] port \ref UartPort
* @param[in] port_event_type \ref UartPortEventType
* @param[out] out Output success flag.
@ -115,6 +122,7 @@ Result uartIsSupportedPortEvent(UartPort port, UartPortEventType port_event_type
/**
* @brief IsSupportedPortEventForDev
* @note Only available on [1.0.0-16.1.0].
* @param[in] port \ref UartPortForDev
* @param[in] port_event_type \ref UartPortEventType
* @param[out] out Output success flag.
@ -123,7 +131,7 @@ Result uartIsSupportedPortEventForDev(UartPortForDev port, UartPortEventType por
/**
* @brief IsSupportedDeviceVariation
* @note Only available on [7.0.0+].
* @note Only available on [7.0.0-16.1.0].
* @param[in] port \ref UartPort
* @param[in] device_variation DeviceVariation
* @param[out] out Output success flag.
@ -132,7 +140,7 @@ Result uartIsSupportedDeviceVariation(UartPort port, u32 device_variation, bool
/**
* @brief IsSupportedDeviceVariationForDev
* @note Only available on [7.0.0+].
* @note Only available on [7.0.0-16.1.0].
* @param[in] port \ref UartPortForDev
* @param[in] device_variation DeviceVariation
* @param[out] out Output success flag.

View File

@ -23,6 +23,21 @@ typedef struct {
void* tls_tp; // !! Offset needs to be TLS+0x1F8 for __aarch64_read_tp !!
} ThreadVars;
extern const u8 __tdata_lma[];
extern const u8 __tdata_lma_end[];
extern u8 __tls_start[];
extern u8 __tls_end[];
extern size_t __tls_align;
static inline ThreadVars* getThreadVars(void) {
return (ThreadVars*)((u8*)armGetTls() + 0x200 - sizeof(ThreadVars));
}
NX_INLINE size_t getTlsStartOffset(void)
{
// TLS region begins with the Thread Control Block (TCB), which is intended
// to contain two pointers. The actual tdata/tbss segment follows the TCB,
// however if it requires special alignment the offset is rounded up.
size_t tcb_sz = 2*sizeof(void*);
return __tls_align > tcb_sz ? __tls_align : tcb_sz;
}

View File

@ -16,11 +16,6 @@
#define USER_TLS_END (0x200 - sizeof(ThreadVars))
#define NUM_TLS_SLOTS ((USER_TLS_END - USER_TLS_BEGIN) / sizeof(void*))
extern const u8 __tdata_lma[];
extern const u8 __tdata_lma_end[];
extern u8 __tls_start[];
extern u8 __tls_end[];
static Mutex g_threadMutex;
static Thread* g_threadList;
@ -45,7 +40,7 @@ static void _EntryWrap(ThreadEntryArgs* args) {
tv->magic = THREADVARS_MAGIC;
tv->thread_ptr = args->t;
tv->reent = args->reent;
tv->tls_tp = (u8*)args->tls-2*sizeof(void*); // subtract size of Thread Control Block (TCB)
tv->tls_tp = (u8*)args->tls-getTlsStartOffset();
tv->handle = args->t->handle;
// Initialize thread info
@ -94,28 +89,35 @@ Result threadCreate(
Thread* t, ThreadFunc entry, void* arg, void* stack_mem, size_t stack_sz,
int prio, int cpuid)
{
const size_t tls_sz = (__tls_end-__tls_start+0xF) &~ 0xF;
const size_t reent_sz = (sizeof(struct _reent)+0xF) &~ 0xF;
const size_t tls_sz = (__tls_end-__tls_start+0xF) &~ 0xF;
// Verify stack size alignment
if (stack_sz & 0xFFF) {
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
}
bool owns_stack_mem;
if (stack_mem == NULL) {
// Allocate new memory, stack then reent then tls.
stack_mem = __libnx_aligned_alloc(0x1000, stack_sz + reent_sz + tls_sz);
// Allocate new memory for the stack, tls and reent.
stack_mem = __libnx_aligned_alloc(0x1000, stack_sz + tls_sz + reent_sz);
owns_stack_mem = true;
} else {
// Use provided memory for stack, reent, and tls.
if (((uintptr_t)stack_mem & 0xFFF) || (stack_sz & 0xFFF)) {
// Verify alignment of provided memory.
if ((uintptr_t)stack_mem & 0xFFF) {
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
}
// Ensure we don't go out of bounds.
if (stack_sz <= tls_sz + reent_sz) {
size_t align_mask = getTlsStartOffset()-1;
size_t needed_sz = (tls_sz + reent_sz + align_mask) &~ align_mask;
if (stack_sz <= needed_sz + sizeof(ThreadEntryArgs)) {
return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
stack_sz -= tls_sz + reent_sz;
// Use provided memory for the stack, tls and reent.
stack_sz -= needed_sz;
owns_stack_mem = false;
}
@ -123,9 +125,9 @@ Result threadCreate(
return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
// Stack size may be unaligned in either case.
// Total allocation size may be unaligned in either case.
virtmemLock();
const size_t aligned_stack_sz = (stack_sz + tls_sz + reent_sz +0xFFF) & ~0xFFF;
const size_t aligned_stack_sz = (stack_sz + tls_sz + reent_sz + 0xFFF) & ~0xFFF;
void* stack_mirror = virtmemFindStack(aligned_stack_sz, 0x4000);
Result rc = svcMapMemory(stack_mirror, stack_mem, aligned_stack_sz);
virtmemUnlock();
@ -134,8 +136,9 @@ Result threadCreate(
{
uintptr_t stack_top = (uintptr_t)stack_mirror + stack_sz - sizeof(ThreadEntryArgs);
ThreadEntryArgs* args = (ThreadEntryArgs*) stack_top;
void *reent = (void*)((uintptr_t)stack_mirror + stack_sz);
void *tls = (void*)((uintptr_t)reent + reent_sz);
void *tls = (void*)((uintptr_t)stack_mirror + stack_sz);
void *reent = (void*)((uintptr_t)tls + tls_sz);
Handle handle;
t->handle = INVALID_HANDLE;

View File

@ -1,34 +1,42 @@
#include "result.h"
#include "kernel/svc.h"
#include "runtime/diag.h"
#include <elf.h>
#include <string.h>
void __nx_dynamic(uintptr_t base, const Elf64_Dyn* dyn)
typedef struct Mod0Header {
u32 magic_mod0;
s32 dyn_offset;
s32 bss_start_offset;
s32 bss_end_offset;
s32 eh_frame_hdr_start_offset;
s32 eh_frame_hdr_end_offset;
s32 unused;
u32 magic_lny0;
s32 got_start_offset;
s32 got_end_offset;
u32 magic_lny1;
s32 relro_start_offset;
s32 relro_end_offset;
} Mod0Header;
NX_INLINE void* _dynResolveOffset(const Mod0Header* mod0, s32 offset)
{
const Elf64_Rela* rela = NULL;
u64 relasz = 0;
return (void*)((uintptr_t)mod0 + offset);
}
for (; dyn->d_tag != DT_NULL; dyn++)
{
switch (dyn->d_tag)
{
case DT_RELA:
rela = (const Elf64_Rela*)(base + dyn->d_un.d_ptr);
static void _dynProcessRela(uintptr_t base, const Elf64_Rela* rela, size_t relasz)
{
for (; relasz--; rela++) {
switch (ELF64_R_TYPE(rela->r_info)) {
default: {
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_BadReloc));
break;
case DT_RELASZ:
relasz = dyn->d_un.d_val / sizeof(Elf64_Rela);
break;
}
}
}
if (rela == NULL)
diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_BadReloc));
for (; relasz--; rela++)
{
switch (ELF64_R_TYPE(rela->r_info))
{
case R_AARCH64_RELATIVE:
{
case R_AARCH64_RELATIVE: {
u64* ptr = (u64*)(base + rela->r_offset);
*ptr = base + rela->r_addend;
break;
@ -36,3 +44,57 @@ void __nx_dynamic(uintptr_t base, const Elf64_Dyn* dyn)
}
}
}
void __nx_dynamic(uintptr_t base, const Mod0Header* mod0)
{
// Return early if MOD0 header has been invalidated
if (mod0->magic_mod0 != 0x30444f4d) { // MOD0
return;
}
// Clear the BSS area
u8* bss_start = _dynResolveOffset(mod0, mod0->bss_start_offset);
u8* bss_end = _dynResolveOffset(mod0, mod0->bss_end_offset);
if (bss_start != bss_end) {
memset(bss_start, 0, bss_end - bss_start);
}
// Retrieve pointer to the ELF dynamic section
const Elf64_Dyn* dyn = _dynResolveOffset(mod0, mod0->dyn_offset);
// Extract relevant information from the ELF dynamic section
const Elf64_Rela* rela = NULL;
size_t relasz = 0;
for (; dyn->d_tag != DT_NULL; dyn++) {
switch (dyn->d_tag) {
case DT_RELA:
rela = (const Elf64_Rela*)(base + dyn->d_un.d_ptr);
break;
case DT_RELASZ:
relasz = dyn->d_un.d_val / sizeof(Elf64_Rela);
break;
}
}
// Apply RELA relocations if present
if (rela && relasz) {
_dynProcessRela(base, rela, relasz);
}
// Return early if LNY0/LNY1 extensions are not present
if (mod0->magic_lny0 != 0x30594e4c || mod0->magic_lny1 != 0x31594e4c) { // LNY0, LNY1
return;
}
// Reprotect relro segment as read-only now that we're done processing relocations
u8* relro_start = _dynResolveOffset(mod0, mod0->relro_start_offset);
size_t relro_sz = (u8*)_dynResolveOffset(mod0, mod0->relro_end_offset) - relro_start;
Result rc = svcSetMemoryPermission(relro_start, relro_sz, Perm_R);
if (R_FAILED(rc)) {
diagAbortWithResult(rc);
}
// Lock the relro segment's permissions
svcSetMemoryAttribute(relro_start, relro_sz, MemAttr_IsPermissionLocked, MemAttr_IsPermissionLocked);
}

View File

@ -30,10 +30,6 @@ struct __pthread_t
void __attribute__((weak)) NORETURN __libnx_exit(int rc);
extern const u8 __tdata_lma[];
extern const u8 __tdata_lma_end[];
extern u8 __tls_start[];
/// TimeType passed to timeGetCurrentTime() during time initialization. If that fails and __nx_time_type isn't TimeType_Default, timeGetCurrentTime() will be called again with TimeType_Default.
__attribute__((weak)) TimeType __nx_time_type = TimeType_Default;
@ -438,7 +434,7 @@ void newlibSetup(void)
tv->reent = _impure_ptr;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
tv->tls_tp = __tls_start-2*sizeof(void*); // subtract size of Thread Control Block (TCB)
tv->tls_tp = __tls_start-getTlsStartOffset();
#pragma GCC diagnostic pop
tv->handle = envGetMainThreadHandle();

View File

@ -23,36 +23,20 @@ _start:
b __libnx_exception_entry
.Lcrt0_main_entry:
// Get pointer to MOD0 struct (contains offsets to important places)
adr x28, __nx_mod0
// Calculate BSS address/size
ldp w8, w9, [x28, #8] // load BSS start/end offset from MOD0
sub w9, w9, w8 // calculate BSS size
add w9, w9, #7 // round up to 8
bic w9, w9, #7 // ^
add x8, x28, x8 // fixup the start pointer
// Clear the BSS in 8-byte units
1: subs w9, w9, #8
str xzr, [x8], #8
bne 1b
// Preserve registers across function calls
mov x25, x0 // entrypoint argument 0
mov x26, x1 // entrypoint argument 1
mov x27, x30 // loader return address
mov x28, sp // initial stack pointer
// Perform runtime linking on ourselves (including relocations)
adr x0, _start // get aslr base
adr x1, __nx_mod0 // get pointer to MOD0 struct
bl __nx_dynamic
// Save initial stack pointer
mov x8, sp
adrp x9, __stack_top
str x8, [x9, #:lo12:__stack_top]
// Parse ELF .dynamic section (which applies relocations to our module)
adr x0, _start // get aslr base
ldr w1, [x28, #4] // pointer to .dynamic section
add x1, x28, x1
bl __nx_dynamic
str x28, [x9, #:lo12:__stack_top]
// Perform system initialization
mov x0, x25
@ -65,8 +49,8 @@ _start:
ldr w0, [x0, #:lo12:__system_argc]
adrp x1, __system_argv // argv
ldr x1, [x1, #:lo12:__system_argv]
adrp x30, exit
add x30, x30, #:lo12:exit
adrp x30, :got:exit
ldr x30, [x30, #:got_lo12:exit]
b main
.global __nx_exit
@ -95,6 +79,10 @@ __nx_mod0:
.word __got_start__ - __nx_mod0
.word __got_end__ - __nx_mod0
.ascii "LNY1"
.word __relro_start - __nx_mod0
.word __data_start - __nx_mod0
.section .bss.__stack_top, "aw", %nobits
.global __stack_top
.align 3

View File

@ -39,3 +39,21 @@ Result capsdcDecodeJpeg(u32 width, u32 height, const CapsScreenShotDecodeOption
}
);
}
Result capsdcShrinkJpeg(u32 width, u32 height, const CapsScreenShotDecodeOption *opts, const void* jpeg, size_t jpeg_size, void* out_jpeg, size_t out_jpeg_size, u64 *out_result_size) {
const struct {
u32 width;
u32 height;
CapsScreenShotDecodeOption opts;
} in = { width, height, *opts };
return serviceDispatchInOut(&g_capsdcSrv, 4001, in, *out_result_size,
.buffer_attrs = {
SfBufferAttr_In | SfBufferAttr_HipcMapAlias,
SfBufferAttr_Out | SfBufferAttr_HipcMapAlias | SfBufferAttr_HipcMapTransferAllowsNonSecure,
},
.buffers = {
{ jpeg, jpeg_size },
{ out_jpeg, out_jpeg_size },
}
);
}

View File

@ -538,6 +538,20 @@ Result fsIsSignedSystemPartitionOnSdCardValid(bool *out) {
return _fsCmdNoInOutBool(&g_fsSrv, out, 640);
}
Result fsGetProgramId(u64* out, const char *path, FsContentAttributes attr) {
if (hosversionBefore(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
char send_path[FS_MAX_PATH] = {0};
strncpy(send_path, path, FS_MAX_PATH-1);
const u8 in = attr;
return _fsObjectDispatchInOut(&g_fsSrv, 618, in, *out,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { send_path, sizeof(send_path) } },
);
}
Result fsGetRightsIdByPath(const char* path, FsRightsId* out_rights_id) {
if (!hosversionBetween(2, 16))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

View File

@ -38,7 +38,26 @@ Result fsldrOpenCodeFileSystem(FsCodeInfo* out_code_info, u64 tid, const char *p
char send_path[FS_MAX_PATH]={0};
strncpy(send_path, path, FS_MAX_PATH-1);
if (hosversionAtLeast(16,0,0)) {
if (hosversionAtLeast(17,0,0)) {
const struct {
u8 attr;
u64 tid;
} in = { attr, tid };
serviceAssumeDomain(&g_fsldrSrv);
return serviceDispatchIn(&g_fsldrSrv, 0, in,
.buffer_attrs = {
SfBufferAttr_FixedSize | SfBufferAttr_HipcPointer | SfBufferAttr_In,
SfBufferAttr_HipcMapAlias | SfBufferAttr_Out,
},
.buffers = {
{ send_path, FS_MAX_PATH },
{ out_code_info, sizeof(*out_code_info) },
},
.out_num_objects = 1,
.out_objects = &out->s,
);
} else if (hosversionAtLeast(16,0,0)) {
const struct {
u8 attr;
u64 tid;

View File

@ -123,10 +123,16 @@ Result gpioPadGetInterruptEnable(GpioPadSession *p, bool *out) {
Result gpioPadGetInterruptStatus(GpioPadSession *p, GpioInterruptStatus *out) {
_Static_assert(sizeof(*out) == sizeof(u32), "GpioInterruptStatus size");
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _gpioCmdNoInOutU32(&p->s, (u32 *)out, 6);
}
Result gpioPadClearInterruptStatus(GpioPadSession *p) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _gpioCmdNoInNoOut(&p->s, 7);
}

View File

@ -407,6 +407,16 @@ Result ncmContentStorageClearRegisteredPath(NcmContentStorage* cs) {
return _ncmCmdNoIO(&cs->s, 29);
}
Result ncmContentStorageGetProgramId(NcmContentStorage* cs, u64* out, const NcmContentId* content_id, FsContentAttributes attr) {
if (hosversionBefore(17,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
const struct {
NcmContentId content_id;
u8 attr;
} in = { *content_id, attr };
return serviceDispatchInOut(&cs->s, 30, in, *out);
}
void ncmContentMetaDatabaseClose(NcmContentMetaDatabase* db) {
serviceClose(&db->s);
}
@ -586,3 +596,9 @@ Result ncmContentMetaDatabaseGetContentIdByTypeAndIdOffset(NcmContentMetaDatabas
} in = { type, id_offset, {0}, *key };
return serviceDispatchInOut(&db->s, 20, in, *out_content_id);
}
Result ncmContentMetaDatabaseGetPlatform(NcmContentMetaDatabase* db, u8* out, const NcmContentMetaKey* key) {
if (hosversionBefore(17,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return serviceDispatchInOut(&db->s, 26, *key, *out);
}

View File

@ -1309,6 +1309,29 @@ Result nsCleanupUnavailableAddOnContents(u64 application_id, AccountUid uid) {
return rc;
}
Result nsEstimateSizeToMove(u8 *storage_ids, s32 count, NcmStorageId storage_id, u32 flags, u64 application_id, s64 *out) {
if (hosversionBefore(10,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
Service srv={0};
Result rc = nsGetApplicationManagerInterface(&srv);
const struct {
u8 storage_id;
u8 pad[3];
u32 flags;
u64 application_id;
} in = { storage_id, {0}, flags, application_id };
if (R_SUCCEEDED(rc)) rc = serviceDispatchInOut(&srv, 1311, in, *out,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffers = { { storage_ids, count*sizeof(u8) } },
);
serviceClose(&srv);
return rc;
}
Result nsFormatSdCard(void) {
if (hosversionBefore(2,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);

View File

@ -25,6 +25,9 @@ static Result _tsCmdInU8Out32(u8 inval, u32 *out, u64 cmd_id) {
}
Result tsGetTemperatureRange(TsLocation location, s32 *min_temperature, s32 *max_temperature) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
u8 tmp_location = location;
struct {
@ -39,6 +42,8 @@ Result tsGetTemperatureRange(TsLocation location, s32 *min_temperature, s32 *max
}
Result tsGetTemperature(TsLocation location, s32 *temperature) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _tsCmdInU8Out32(location, (u32*)temperature, 1);
}
@ -48,3 +53,23 @@ Result tsGetTemperatureMilliC(TsLocation location, s32 *temperature) {
return _tsCmdInU8Out32(location, (u32*)temperature, 3);
}
Result tsOpenSession(TsSession *s, u32 device_code) {
if (hosversionBefore(8,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return serviceDispatchIn(&g_tsSrv, 4, device_code,
.out_num_objects = 1,
.out_objects = &s->s,
);
}
Result tsSessionGetTemperature(TsSession *s, float *temperature) {
if (hosversionBefore(10,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return serviceDispatchOut(&s->s, 4, *temperature);
}
void tsSessionClose(TsSession *s) {
serviceClose(&s->s);
}

View File

@ -45,26 +45,44 @@ static Result _uartCmdInTwoU32sOutBool(Service* srv, u32 inval0, u32 inval1, boo
}
Result uartHasPort(UartPort port, bool *out) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInU32OutBool(&g_uartSrv, port, out, 0);
}
Result uartHasPortForDev(UartPortForDev port, bool *out) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInU32OutBool(&g_uartSrv, port, out, 1);
}
Result uartIsSupportedBaudRate(UartPort port, u32 baud_rate, bool *out) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, baud_rate, out, 2);
}
Result uartIsSupportedBaudRateForDev(UartPortForDev port, u32 baud_rate, bool *out) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, baud_rate, out, 3);
}
Result uartIsSupportedFlowControlMode(UartPort port, UartFlowControlMode flow_control_mode, bool *out) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, flow_control_mode, out, 4);
}
Result uartIsSupportedFlowControlModeForDev(UartPortForDev port, UartFlowControlMode flow_control_mode, bool *out) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, flow_control_mode, out, 5);
}
@ -76,22 +94,28 @@ Result uartCreatePortSession(UartPortSession *s) {
}
Result uartIsSupportedPortEvent(UartPort port, UartPortEventType port_event_type, bool *out) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, port_event_type, out, 7);
}
Result uartIsSupportedPortEventForDev(UartPortForDev port, UartPortEventType port_event_type, bool *out) {
if (hosversionAtLeast(17,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, port_event_type, out, 8);
}
Result uartIsSupportedDeviceVariation(UartPort port, u32 device_variation, bool *out) {
if (hosversionBefore(7,0,0))
if (!hosversionBetween(7,17))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, device_variation, out, 9);
}
Result uartIsSupportedDeviceVariationForDev(UartPortForDev port, u32 device_variation, bool *out) {
if (hosversionBefore(7,0,0))
if (!hosversionBetween(7,17))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _uartCmdInTwoU32sOutBool(&g_uartSrv, port, device_variation, out, 10);

View File

@ -11,10 +11,11 @@ PHDRS
SECTIONS
{
PROVIDE_HIDDEN( __start__ = 0x0 );
/* =========== CODE section =========== */
PROVIDE(__start__ = 0x0);
. = __start__;
__code_start = . ;
PROVIDE_HIDDEN( __code_start = . );
.text :
{
@ -48,111 +49,143 @@ SECTIONS
/* =========== RODATA section =========== */
. = ALIGN(0x1000);
__rodata_start = . ;
PROVIDE_HIDDEN( __rodata_start = . );
.nx-module-name : { KEEP (*(.nx-module-name)) } :rodata
.rela.dyn : { *(.rela.*) } :rodata
.relr.dyn : { *(.relr.*) } :rodata
.hash : { *(.hash) } :rodata
.gnu.hash : { *(.gnu.hash) } :rodata
.dynsym : { *(.dynsym) } :rodata
.dynstr : { *(.dynstr) } :rodata
.rodata :
{
*(.rodata .rodata.* .gnu.linkonce.r.*)
. = ALIGN(8);
} :rodata
.eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } :rodata
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } :rodata
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } :rodata
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } : rodata
.tls.align :
{
QUAD( MAX( ALIGNOF(.tdata), ALIGNOF(.tbss) ) )
} :rodata
PROVIDE_HIDDEN( __tls_align = ADDR(.tls.align) );
.gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) } :rodata
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } :rodata
.eh_frame : { KEEP (*(.eh_frame)) *(.eh_frame.*) } :rodata
.gnu_extab : { *(.gnu_extab*) } : rodata
.exception_ranges : { *(.exception_ranges .exception_ranges*) } :rodata
PROVIDE_HIDDEN( __eh_frame_hdr_start = ADDR(.eh_frame_hdr) );
PROVIDE_HIDDEN( __eh_frame_hdr_end = ADDR(.eh_frame_hdr) + SIZEOF(.eh_frame_hdr) );
.dynamic : { *(.dynamic) } :rodata :dyn
.dynsym : { *(.dynsym) } :rodata
.dynstr : { *(.dynstr) } :rodata
.rela.dyn : { *(.rela.*) } :rodata
.interp : { *(.interp) } :rodata
.hash : { *(.hash) } :rodata
.gnu.hash : { *(.gnu.hash) } :rodata
.gnu.version : { *(.gnu.version) } :rodata
.gnu.version_d : { *(.gnu.version_d) } :rodata
.gnu.version_r : { *(.gnu.version_r) } :rodata
.note.gnu.build-id : { *(.note.gnu.build-id) } :rodata
/* =========== RELRO section =========== */
. = ALIGN(0x1000);
PROVIDE_HIDDEN( __relro_start = . );
.preinit_array :
{
PROVIDE_HIDDEN( __preinit_array_start = . );
KEEP (*(.preinit_array))
PROVIDE_HIDDEN( __preinit_array_end = . );
} :data
.init_array :
{
PROVIDE_HIDDEN( __init_array_start = . );
KEEP( *(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)) )
KEEP( *(.init_array .ctors) )
PROVIDE_HIDDEN( __init_array_end = . );
} :data
.fini_array :
{
PROVIDE_HIDDEN( __fini_array_start = . );
KEEP( *(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)) )
KEEP( *(.fini_array .dtors) )
PROVIDE_HIDDEN( __fini_array_end = . );
} :data
.data.rel.ro :
{
*(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*)
*(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*)
. = ALIGN(8);
} :data
.dynamic : { *(.dynamic) } :data :dyn
.got : { *(.got) *(.igot) } :data
.got.plt : { *(.got.plt) *(.igot.plt) } :data
PROVIDE_HIDDEN( __got_start__ = ADDR(.got) );
PROVIDE_HIDDEN( __got_end__ = ADDR(.got.plt) + SIZEOF(.got.plt) );
/* =========== DATA section =========== */
. = ALIGN(0x1000);
__data_start = . ;
PROVIDE_HIDDEN( __data_start = . );
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } :data
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } :data
.gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } : data
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } :data
.tdata ALIGN(8) :
{
__tdata_lma = .;
*(.tdata .tdata.* .gnu.linkonce.td.*)
. = ALIGN(8);
__tdata_lma_end = .;
} :data
.tbss ALIGN(8) :
{
*(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon)
. = ALIGN(8);
} :data
.preinit_array ALIGN(8) :
{
PROVIDE (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
} :data
.init_array ALIGN(8) :
{
PROVIDE (__init_array_start = .);
KEEP( *(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)) )
KEEP( *(.init_array .ctors) )
PROVIDE (__init_array_end = .);
} :data
.fini_array ALIGN(8) :
{
PROVIDE (__fini_array_start = .);
KEEP( *(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)) )
KEEP( *(.fini_array .dtors) )
PROVIDE (__fini_array_end = .);
} :data
__got_start__ = .;
.got : { *(.got) *(.igot) } :data
.got.plt : { *(.got.plt) *(.igot.plt) } :data
__got_end__ = .;
.data ALIGN(8) :
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
. = ALIGN(8);
} :data
.bss ALIGN(8) :
.tdata :
{
*(.tdata .tdata.* .gnu.linkonce.td.*)
. = ALIGN(8);
} :data
PROVIDE_HIDDEN( __tdata_lma = ADDR(.tdata) );
PROVIDE_HIDDEN( __tdata_lma_end = ADDR(.tdata) + SIZEOF(.tdata) );
.tbss :
{
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
. = ALIGN(8);
} :data
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(8);
} :data
/* Reserve space for the TLS segment of the main thread */
__tls_start = .;
. += + SIZEOF(.tdata) + SIZEOF(.tbss);
__tls_end = .;
} : data
__bss_start__ = ADDR(.bss);
__bss_end__ = ADDR(.bss) + SIZEOF(.bss);
/* Reserve space for the TLS segment of the main thread */
.main.tls ALIGN(MAX(ALIGNOF(.tdata),ALIGNOF(.tbss))) :
{
. += SIZEOF(.tdata);
. = ALIGN(ALIGNOF(.tbss));
. += SIZEOF(.tbss);
} :data
__end__ = ABSOLUTE(.) ;
PROVIDE_HIDDEN( __tls_start = ADDR(.main.tls) );
PROVIDE_HIDDEN( __tls_end = ADDR(.main.tls) + SIZEOF(.main.tls) );
PROVIDE_HIDDEN( __bss_start__ = ADDR(.bss) );
PROVIDE_HIDDEN( __bss_end__ = __tls_end );
PROVIDE_HIDDEN( __end__ = ABSOLUTE(.) );
/* =========== Argument buffer =========== */
. = ALIGN(0x1000);
__argdata__ = ABSOLUTE(.) ;
PROVIDE_HIDDEN( __argdata__ = ABSOLUTE(.) );
/* ==================
==== Metadata ====

View File

@ -1,8 +1,5 @@
%rename link old_link
*link:
%(old_link) -T %:getenv(DEVKITPRO /libnx/switch.ld) -pie --no-dynamic-linker --spare-dynamic-tags=0 --gc-sections -z text -z nodynamic-undefined-weak --build-id=sha1 --nx-module-name
+ -T %:getenv(DEVKITPRO /libnx/switch.ld) -pie --no-dynamic-linker --spare-dynamic-tags=0 --gc-sections -z text -z now -z nodynamic-undefined-weak --build-id=sha1 --nx-module-name
*startfile:
crti%O%s crtbegin%O%s --require-defined=main