Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"

This reverts commit 15b7df8ef1.
This commit is contained in:
souldbminersmwc
2025-11-09 16:14:52 -05:00
parent 22ec140738
commit 21a3f953d7
3804 changed files with 435 additions and 570162 deletions

View File

@@ -1,116 +0,0 @@
#---------------------------------------------------------------------------------
# pull in common stratosphere sysmodule configuration
#---------------------------------------------------------------------------------
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).kip $(TARGET).elf
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).kip $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
$(OUTPUT).kip : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
$(OUTPUT).npdm : $(OUTPUT).npdm.json
@echo built ... $< $@
@npdmtool $< $@
@echo built ... $(notdir $@)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@@ -1,128 +0,0 @@
{
"name": "TestSvc",
"title_id": "0x5555555555555555",
"main_thread_stack_size": "0x8000",
"main_thread_priority": 28,
"default_cpu_id": 3,
"process_category": 1,
"use_secure_memory": true,
"immortal": true,
"kernel_capabilities": [
{
"type": "handle_table_size",
"value": 0
},
{
"type": "syscalls",
"value": {
"svcUnknown00": "0x00",
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0A",
"svcSleepThread": "0x0B",
"svcGetThreadPriority": "0x0C",
"svcSetThreadPriority": "0x0D",
"svcGetThreadCoreMask": "0x0E",
"svcSetThreadCoreMask": "0x0F",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1A",
"svcArbitrateUnlock": "0x1B",
"svcWaitProcessWideKeyAtomic": "0x1C",
"svcSignalProcessWideKey": "0x1D",
"svcGetSystemTick": "0x1E",
"svcConnectToNamedPort": "0x1F",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcFlushEntireDataCache": "0x2A",
"svcFlushDataCache": "0x2B",
"svcMapPhysicalMemory": "0x2C",
"svcUnmapPhysicalMemory": "0x2D",
"svcGetDebugFutureThreadInfo": "0x2E",
"svcGetLastThreadInfo": "0x2F",
"svcGetResourceLimitLimitValue": "0x30",
"svcGetResourceLimitCurrentValue": "0x31",
"svcSetThreadActivity": "0x32",
"svcGetThreadContext3": "0x33",
"svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35",
"svcSynchronizePreemptionState": "0x36",
"svcGetResourceLimitPeakValue": "0x37",
"svcUnknown38": "0x38",
"svcUnknown39": "0x39",
"svcUnknown3a": "0x3A",
"svcUnknown3b": "0x3B",
"svcKernelDebug": "0x3C",
"svcChangeKernelTraceState": "0x3D",
"svcUnknown3e": "0x3E",
"svcUnknown3f": "0x3F",
"svcCreateSession": "0x40",
"svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcUnknown46": "0x46",
"svcUnknown47": "0x47",
"svcMapPhysicalMemoryUnsafe": "0x48",
"svcUnmapPhysicalMemoryUnsafe": "0x49",
"svcSetUnsafeLimit": "0x4A",
"svcCreateCodeMemory": "0x4B",
"svcControlCodeMemory": "0x4C",
"svcSleepSystem": "0x4D",
"svcReadWriteRegister": "0x4E",
"svcSetProcessActivity": "0x4F",
"svcCreateSharedMemory": "0x50",
"svcMapTransferMemory": "0x51",
"svcUnmapTransferMemory": "0x52",
"svcQueryIoMapping": "0x55",
"svcDebugActiveProcess": "0x60",
"svcBreakDebugProcess": "0x61",
"svcTerminateDebugProcess": "0x62",
"svcGetDebugEvent": "0x63",
"svcContinueDebugEvent": "0x64",
"svcGetProcessList": "0x65",
"svcGetThreadList": "0x66",
"svcGetDebugThreadContext": "0x67",
"svcSetDebugThreadContext": "0x68",
"svcQueryDebugProcessMemory": "0x69",
"svcReadDebugProcessMemory": "0x6A",
"svcWriteDebugProcessMemory": "0x6B",
"svcSetHardwareBreakPoint": "0x6C",
"svcGetDebugThreadParam": "0x6D",
"svcGetSystemInfo": "0x6F",
"svcConnectToPort": "0x72",
"svcSetProcessMemoryPermission": "0x73",
"svcMapProcessMemory": "0x74",
"svcUnmapProcessMemory": "0x75",
"svcQueryProcessMemory": "0x76",
"svcMapProcessCodeMemory": "0x77",
"svcUnmapProcessCodeMemory": "0x78",
"svcCallSecureMonitor": "0x7F"
}
}
]
}

View File

@@ -1,147 +0,0 @@
{
"name": "TestSvc",
"title_id": "0x5555555555555555",
"title_id_range_min": "0x5555555555555555",
"title_id_range_max": "0x5555555555555555",
"main_thread_stack_size": "0x8000",
"main_thread_priority": 28,
"default_cpu_id": 3,
"process_category": 0,
"is_retail": true,
"pool_partition": 2,
"is_64_bit": true,
"address_space_type": 3,
"disable_device_address_space_merge": true,
"filesystem_access": {
"permissions": "0xFFFFFFFFFFFFFFFF"
},
"service_access": ["*"],
"service_host": ["*"],
"kernel_capabilities": [
{
"type": "kernel_flags",
"value": {
"highest_thread_priority": 63,
"lowest_thread_priority": 16,
"lowest_cpu_id": 0,
"highest_cpu_id": 3
}
},
{
"type": "handle_table_size",
"value": 0
},
{
"type": "syscalls",
"value": {
"svcUnknown00": "0x00",
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0A",
"svcSleepThread": "0x0B",
"svcGetThreadPriority": "0x0C",
"svcSetThreadPriority": "0x0D",
"svcGetThreadCoreMask": "0x0E",
"svcSetThreadCoreMask": "0x0F",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1A",
"svcArbitrateUnlock": "0x1B",
"svcWaitProcessWideKeyAtomic": "0x1C",
"svcSignalProcessWideKey": "0x1D",
"svcGetSystemTick": "0x1E",
"svcConnectToNamedPort": "0x1F",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcFlushEntireDataCache": "0x2A",
"svcFlushDataCache": "0x2B",
"svcMapPhysicalMemory": "0x2C",
"svcUnmapPhysicalMemory": "0x2D",
"svcGetDebugFutureThreadInfo": "0x2E",
"svcGetLastThreadInfo": "0x2F",
"svcGetResourceLimitLimitValue": "0x30",
"svcGetResourceLimitCurrentValue": "0x31",
"svcSetThreadActivity": "0x32",
"svcGetThreadContext3": "0x33",
"svcWaitForAddress": "0x34",
"svcSignalToAddress": "0x35",
"svcSynchronizePreemptionState": "0x36",
"svcGetResourceLimitPeakValue": "0x37",
"svcUnknown38": "0x38",
"svcUnknown39": "0x39",
"svcUnknown3a": "0x3A",
"svcUnknown3b": "0x3B",
"svcKernelDebug": "0x3C",
"svcChangeKernelTraceState": "0x3D",
"svcUnknown3e": "0x3E",
"svcUnknown3f": "0x3F",
"svcCreateSession": "0x40",
"svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcUnknown46": "0x46",
"svcUnknown47": "0x47",
"svcMapPhysicalMemoryUnsafe": "0x48",
"svcUnmapPhysicalMemoryUnsafe": "0x49",
"svcSetUnsafeLimit": "0x4A",
"svcCreateCodeMemory": "0x4B",
"svcControlCodeMemory": "0x4C",
"svcSleepSystem": "0x4D",
"svcReadWriteRegister": "0x4E",
"svcSetProcessActivity": "0x4F",
"svcCreateSharedMemory": "0x50",
"svcMapTransferMemory": "0x51",
"svcUnmapTransferMemory": "0x52",
"svcQueryIoMapping": "0x55",
"svcDebugActiveProcess": "0x60",
"svcBreakDebugProcess": "0x61",
"svcTerminateDebugProcess": "0x62",
"svcGetDebugEvent": "0x63",
"svcContinueDebugEvent": "0x64",
"svcGetProcessList": "0x65",
"svcGetThreadList": "0x66",
"svcGetDebugThreadContext": "0x67",
"svcSetDebugThreadContext": "0x68",
"svcQueryDebugProcessMemory": "0x69",
"svcReadDebugProcessMemory": "0x6A",
"svcWriteDebugProcessMemory": "0x6B",
"svcSetHardwareBreakPoint": "0x6C",
"svcGetDebugThreadParam": "0x6D",
"svcGetSystemInfo": "0x6F",
"svcConnectToPort": "0x72",
"svcSetProcessMemoryPermission": "0x73",
"svcMapProcessMemory": "0x74",
"svcUnmapProcessMemory": "0x75",
"svcQueryProcessMemory": "0x76",
"svcMapProcessCodeMemory": "0x77",
"svcUnmapProcessCodeMemory": "0x78",
"svcCallSecureMonitor": "0x7F"
}
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,107 +0,0 @@
/*
* Copyright (c) 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>
#define DOCTEST_CONFIG_IMPLEMENT
#include "util_test_framework.hpp"
namespace ams {
namespace {
constexpr size_t MallocBufferSize = 16_MB;
alignas(os::MemoryPageSize) constinit u8 g_malloc_buffer[MallocBufferSize];
}
namespace hos {
bool IsUnitTestProgramForSetVersion() { return true; }
}
namespace init {
void InitializeSystemModuleBeforeConstructors() {
/* Catch has global-ctors which allocate, so we need to do this earlier than normal. */
init::InitializeAllocator(g_malloc_buffer, sizeof(g_malloc_buffer));
}
void InitializeSystemModule() { /* ... */ }
void FinalizeSystemModule() { /* ... */ }
void Startup() { /* ... */ }
}
void NORETURN Exit(int rc) {
AMS_UNUSED(rc);
AMS_ABORT("Exit called by immortal process");
}
void Main() {
/* Ensure our thread priority and core mask is correct. */
{
auto * const cur_thread = os::GetCurrentThread();
os::SetThreadCoreMask(cur_thread, 3, (1ul << 3));
os::ChangeThreadPriority(cur_thread, 0);
}
/* Run tests. */
{
doctest::Context ctx;
ctx.applyCommandLine(os::GetHostArgc(), os::GetHostArgv());
ctx.run();
}
AMS_INFINITE_LOOP();
/* This can never be reached. */
AMS_ASSUME(false);
}
}
namespace doctest {
namespace {
class OutputDebugStringStream : public std::stringbuf {
public:
OutputDebugStringStream() = default;
~OutputDebugStringStream() { pubsync(); }
int sync() override {
const auto message = str();
return R_SUCCEEDED(ams::svc::OutputDebugString(message.c_str(), message.length())) ? 0 : -1;
}
};
}
std::ostream& get_cout() {
static std::ostream ret(new OutputDebugStringStream);
return ret;
}
std::ostream& get_cerr() {
return get_cout();
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (c) 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 "util_common.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
namespace {
constinit volatile bool g_spinloop;
void TestPreemptionPriorityThreadFunction(volatile bool *executed) {
/* While we should, note that we're executing. */
while (g_spinloop) {
__asm__ __volatile__("" ::: "memory");
*executed = true;
__asm__ __volatile__("" ::: "memory");
}
/* Exit the thread. */
svc::ExitThread();
}
}
DOCTEST_TEST_CASE( "The scheduler is preemptive at the preemptive priority and cooperative for all other priorities" ) {
/* Create heap. */
ScopedHeap heap(3 * os::MemoryPageSize);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_None)));
ON_SCOPE_EXIT {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_ReadWrite)));
};
const uintptr_t sp_0 = heap.GetAddress() + 1 * os::MemoryPageSize;
const uintptr_t sp_1 = heap.GetAddress() + 3 * os::MemoryPageSize;
for (s32 core = 0; core < NumCores; ++core) {
for (s32 priority = HighestTestPriority; priority <= LowestTestPriority; ++priority) {
svc::Handle thread_handles[2];
volatile bool thread_executed[2] = { false, false };
/* Start spinlooping. */
g_spinloop = true;
/* Create threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestPreemptionPriorityThreadFunction), reinterpret_cast<uintptr_t>(thread_executed + 0), sp_0, priority, core)));
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestPreemptionPriorityThreadFunction), reinterpret_cast<uintptr_t>(thread_executed + 1), sp_1, priority, core)));
/* Start threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
/* Wait long enough that we can be confident the threads have been balanced. */
svc::SleepThread(PreemptionTimeSpan.GetNanoSeconds() * 10);
/* Check that we're in a coherent state. */
if (IsPreemptionPriority(core, priority)) {
DOCTEST_CHECK((thread_executed[0] & thread_executed[1]));
} else {
DOCTEST_CHECK((thread_executed[0] ^ thread_executed[1]));
}
/* Stop spinlooping. */
g_spinloop = false;
/* Wait for threads to exit. */
s32 dummy;
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), thread_handles + 0, 1, -1)));
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), thread_handles + 1, 1, -1)));
/* Close thread handles. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
}
}
}
}

View File

@@ -1,133 +0,0 @@
/*
* Copyright (c) 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 "util_common.hpp"
#include "util_check_memory.hpp"
namespace ams::test {
namespace {
size_t GetPhysicalMemorySizeMax() {
u64 v;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(v), svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
const svc::Handle resource_limit = v;
ON_SCOPE_EXIT { svc::CloseHandle(resource_limit); };
s64 size;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(size), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
return static_cast<size_t>(size);
}
size_t GetPhysicalMemorySizeAvailable() {
u64 v;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(v), svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
const svc::Handle resource_limit = v;
ON_SCOPE_EXIT { svc::CloseHandle(resource_limit); };
s64 total;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
s64 current;
R_ABORT_UNLESS(svc::GetResourceLimitCurrentValue(std::addressof(current), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
return static_cast<size_t>(total - current);
}
}
DOCTEST_TEST_CASE("svc::SetHeapSize") {
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
uintptr_t dummy;
/* Reset the heap. */
uintptr_t addr;
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
/* Ensure that we don't leak memory. */
const size_t initial_memory = GetPhysicalMemorySizeAvailable();
ON_SCOPE_EXIT { DOCTEST_CHECK(initial_memory == GetPhysicalMemorySizeAvailable()); };
DOCTEST_SUBCASE("Unaligned and too big sizes fail") {
for (size_t i = 1; i < svc::HeapSizeAlignment; i = util::AlignUp(i + 1, os::MemoryPageSize)){
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), i)));
}
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), 64_GB)));
}
DOCTEST_SUBCASE("Larger size than address space fails") {
DOCTEST_CHECK(svc::ResultOutOfMemory::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(svc::AddressMemoryRegionHeap39Size + 1, svc::HeapSizeAlignment))));
}
DOCTEST_SUBCASE("Bounded by resource limit") {
DOCTEST_CHECK(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeMax() + 1, svc::HeapSizeAlignment))));
DOCTEST_CHECK(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeAvailable() + 1, svc::HeapSizeAlignment))));
}
DOCTEST_SUBCASE("SetHeapSize gives heap memory") {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
DOCTEST_SUBCASE("SetHeapSize cannot remove read-only heap") {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_Read)));
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_Read, 0);
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetHeapSize(std::addressof(dummy), 0)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_ReadWrite)));
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
DOCTEST_SUBCASE("Heap memory does not survive unmap/re-map") {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
u8 * const heap = reinterpret_cast<u8 *>(addr);
std::memset(heap, 0xAA, svc::HeapSizeAlignment);
std::memset(heap + svc::HeapSizeAlignment, 0xBB, svc::HeapSizeAlignment);
DOCTEST_CHECK(heap[svc::HeapSizeAlignment] == 0xBB);
DOCTEST_CHECK(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
DOCTEST_CHECK(heap[0] == 0xAA);
DOCTEST_CHECK(std::memcmp(heap, heap + 1, svc::HeapSizeAlignment - 1) == 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
DOCTEST_CHECK(heap[svc::HeapSizeAlignment] == 0x00);
DOCTEST_CHECK(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
}
}

View File

@@ -1,156 +0,0 @@
/*
* Copyright (c) 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 "util_common.hpp"
#include "util_check_memory.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
namespace {
bool CanSetMemoryPermission(u8 state) {
return state == svc::MemoryState_CodeData || state == svc::MemoryState_AliasCodeData || state == svc::MemoryState_Normal;
}
}
alignas(os::MemoryPageSize) constinit u8 g_memory_permission_buffer[2 * os::MemoryPageSize];
DOCTEST_TEST_CASE("svc::SetMemoryPermission invalid arguments") {
const uintptr_t buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
for (size_t i = 1; i < os::MemoryPageSize; ++i) {
DOCTEST_CHECK(svc::ResultInvalidAddress::Includes(svc::SetMemoryPermission(buffer + i, os::MemoryPageSize, svc::MemoryPermission_Read)));
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize + i, svc::MemoryPermission_Read)));
}
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetMemoryPermission(buffer, 0, svc::MemoryPermission_Read)));
{
const u64 vmem_end = util::AlignDown(std::numeric_limits<u64>::max(), os::MemoryPageSize);
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(vmem_end, 2 * os::MemoryPageSize, svc::MemoryPermission_Read)));
}
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(svc::AddressMap39End, os::MemoryPageSize, svc::MemoryPermission_Read)));
for (size_t i = 0; i < 0x100; ++i) {
const auto perm = static_cast<svc::MemoryPermission>(i);
if (perm == svc::MemoryPermission_None || perm == svc::MemoryPermission_Read || perm == svc::MemoryPermission_ReadWrite) {
continue;
}
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, perm)));
}
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_ReadExecute)));
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_Write)));
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_DontCare)));
}
DOCTEST_TEST_CASE("svc::SetMemoryPermission works on specific states") {
/* Check that we have CodeData. */
const uintptr_t bss_buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
/* Create a heap. */
ScopedHeap scoped_heap(2 * svc::HeapSizeAlignment);
TestMemory(scoped_heap.GetAddress(), scoped_heap.GetSize(), svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
/* TODO: Ensure we have alias code data? */
uintptr_t addr = 0;
while (true) {
/* Get current mapping. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
/* Try to set permission. */
if (CanSetMemoryPermission(mem_info.state) && mem_info.attribute == 0) {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, svc::MemoryPermission_ReadWrite)));
TestMemory(mem_info.base_address, mem_info.size, mem_info.state, svc::MemoryPermission_ReadWrite, mem_info.attribute);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, mem_info.permission)));
} else {
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, svc::MemoryPermission_Read)));
}
const uintptr_t next_address = mem_info.base_address + mem_info.size;
if (next_address <= addr) {
break;
}
addr = next_address;
}
}
DOCTEST_TEST_CASE("svc::SetMemoryPermission allows for free movement between RW-, R--, ---") {
/* Define helper. */
auto test_set_memory_permission = [](uintptr_t address, size_t size){
/* Get the permission. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
const svc::MemoryPermission legal_states[] = { svc::MemoryPermission_None, svc::MemoryPermission_Read, svc::MemoryPermission_ReadWrite };
for (const auto src_state : legal_states) {
for (const auto dst_state : legal_states) {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, svc::MemoryPermission_None)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, src_state)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, dst_state)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, svc::MemoryPermission_None)));
}
}
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, mem_info.permission)));
};
/* Test that we can freely move about .bss buffers. */
test_set_memory_permission(reinterpret_cast<uintptr_t>(g_memory_permission_buffer), sizeof(g_memory_permission_buffer));
/* Create a heap. */
ScopedHeap scoped_heap(svc::HeapSizeAlignment);
TestMemory(scoped_heap.GetAddress(), scoped_heap.GetSize(), svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
/* Test that we can freely move about heap. */
test_set_memory_permission(scoped_heap.GetAddress(), scoped_heap.GetSize());
/* TODO: AliasCodeData */
}
DOCTEST_TEST_CASE("svc::SetMemoryPermission fails when the memory has non-zero attribute") {
const uintptr_t bss_buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryAttribute(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryAttribute_Uncached, svc::MemoryAttribute_Uncached)));
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, svc::MemoryAttribute_Uncached);
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryAttribute(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryAttribute_Uncached, 0)));
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
}
}

View File

@@ -1,253 +0,0 @@
/*
* Copyright (c) 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 "util_common.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
namespace {
constinit svc::Handle g_read_handles[3] = { svc::InvalidHandle, svc::InvalidHandle, svc::InvalidHandle };
constinit svc::Handle g_write_handles[3] = { svc::InvalidHandle, svc::InvalidHandle, svc::InvalidHandle };
constinit s64 g_thread_wait_ns;
constinit bool g_should_switch_threads;
constinit bool g_switched_threads;
constinit bool g_correct_switch_threads;
void WaitSynchronization(svc::Handle handle) {
s32 dummy;
R_ABORT_UNLESS(svc::WaitSynchronization(std::addressof(dummy), std::addressof(handle), 1, -1));
}
void TestYieldHigherOrSamePriorityThread() {
/* Wait to run. */
WaitSynchronization(g_read_handles[0]);
/* Reset our event. */
R_ABORT_UNLESS(svc::ClearEvent(g_read_handles[0]));
/* Signal the other thread's event. */
R_ABORT_UNLESS(svc::SignalEvent(g_write_handles[1]));
/* Wait, potentially yielding to the lower/same priority thread. */
g_switched_threads = false;
svc::SleepThread(g_thread_wait_ns);
/* Check whether we switched correctly. */
g_correct_switch_threads = g_should_switch_threads == g_switched_threads;
/* Exit. */
svc::ExitThread();
}
void TestYieldLowerOrSamePriorityThread() {
/* Signal thread the higher/same priority thread to run. */
R_ABORT_UNLESS(svc::SignalEvent(g_write_handles[0]));
/* Wait to run. */
WaitSynchronization(g_read_handles[1]);
/* Reset our event. */
R_ABORT_UNLESS(svc::ClearEvent(g_read_handles[1]));
/* We've switched to the lower/same priority thread. */
g_switched_threads = true;
/* Wait to be instructed to exit. */
WaitSynchronization(g_read_handles[2]);
/* Reset the exit signal. */
R_ABORT_UNLESS(svc::ClearEvent(g_read_handles[2]));
/* Exit. */
svc::ExitThread();
}
void TestYieldSamePriority(uintptr_t sp_higher, uintptr_t sp_lower) {
/* Test each core. */
for (s32 core = 0; core < NumCores; ++core) {
for (s32 priority = HighestTestPriority; priority <= LowestTestPriority && !IsPreemptionPriority(core, priority); ++priority) {
svc::Handle thread_handles[2];
/* Create threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestYieldHigherOrSamePriorityThread), 0, sp_higher, priority, core)));
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestYieldLowerOrSamePriorityThread), 0, sp_lower, priority, core)));
/* Start threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
/* Wait for higher priority thread. */
WaitSynchronization(thread_handles[0]);
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
/* Signal the lower priority thread to exit. */
DOCTEST_CHECK(R_SUCCEEDED(svc::SignalEvent(g_write_handles[2])));
/* Wait for the lower priority thread. */
WaitSynchronization(thread_handles[1]);
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
/* Check that the switch was correct. */
DOCTEST_CHECK(g_correct_switch_threads);
}
}
}
void TestYieldDifferentPriority(uintptr_t sp_higher, uintptr_t sp_lower) {
/* Test each core. */
for (s32 core = 0; core < NumCores; ++core) {
for (s32 priority = HighestTestPriority; priority < LowestTestPriority && !IsPreemptionPriority(core, priority); ++priority) {
svc::Handle thread_handles[2];
/* Create threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestYieldHigherOrSamePriorityThread), 0, sp_higher, priority, core)));
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestYieldLowerOrSamePriorityThread), 0, sp_lower, priority + 1, core)));
/* Start threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
/* Wait for higher priority thread. */
WaitSynchronization(thread_handles[0]);
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
/* Signal the lower priority thread to exit. */
DOCTEST_CHECK(R_SUCCEEDED(svc::SignalEvent(g_write_handles[2])));
/* Wait for the lower priority thread. */
WaitSynchronization(thread_handles[1]);
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
/* Check that the switch was correct. */
DOCTEST_CHECK(g_correct_switch_threads);
}
}
}
}
DOCTEST_TEST_CASE( "svc::SleepThread: Thread sleeps for time specified" ) {
for (s64 ns = 1; ns < TimeSpan::FromSeconds(1).GetNanoSeconds(); ns *= 2) {
const auto start = os::GetSystemTickOrdered();
svc::SleepThread(ns);
const auto end = os::GetSystemTickOrdered();
const s64 taken_ns = (end - start).ToTimeSpan().GetNanoSeconds();
DOCTEST_CHECK( taken_ns >= ns );
}
}
DOCTEST_TEST_CASE( "svc::SleepThread: Yield is behaviorally correct" ) {
/* Create events. */
for (size_t i = 0; i < util::size(g_write_handles); ++i) {
g_read_handles[i] = svc::InvalidHandle;
g_write_handles[i] = svc::InvalidHandle;
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateEvent(g_write_handles + i, g_read_handles + i)));
}
ON_SCOPE_EXIT {
for (size_t i = 0; i < util::size(g_write_handles); ++i) {
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(g_read_handles[i])));
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(g_write_handles[i])));
g_read_handles[i] = svc::InvalidHandle;
g_write_handles[i] = svc::InvalidHandle;
}
};
/* Create heap. */
ScopedHeap heap(3 * os::MemoryPageSize);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_None)));
ON_SCOPE_EXIT {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_ReadWrite)));
};
const uintptr_t sp_higher = heap.GetAddress() + 1 * os::MemoryPageSize;
const uintptr_t sp_lower = heap.GetAddress() + 3 * os::MemoryPageSize;
DOCTEST_SUBCASE("svc::SleepThread: Yields do not switch to a thread of lower priority.") {
/* Test yield without migration. */
{
/* Configure for yield test. */
g_should_switch_threads = false;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_WithoutCoreMigration);
TestYieldDifferentPriority(sp_higher, sp_lower);
}
/* Test yield with migration. */
{
/* Configure for yield test. */
g_should_switch_threads = false;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_WithoutCoreMigration);
TestYieldDifferentPriority(sp_higher, sp_lower);
}
}
DOCTEST_SUBCASE("svc::SleepThread: ToAnyThread switches to a thread of same or lower priority.") {
/* Test to same priority. */
{
/* Configure for yield test. */
g_should_switch_threads = true;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_ToAnyThread);
TestYieldSamePriority(sp_higher, sp_lower);
}
/* Test to lower priority. */
{
/* Configure for yield test. */
g_should_switch_threads = true;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_ToAnyThread);
TestYieldDifferentPriority(sp_higher, sp_lower);
}
}
DOCTEST_SUBCASE("svc::SleepThread: Yield switches to another thread of same priority.") {
/* Test yield without migration. */
{
/* Configure for yield test. */
g_should_switch_threads = true;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_WithoutCoreMigration);
TestYieldSamePriority(sp_higher, sp_lower);
}
/* Test yield with migration. */
{
/* Configure for yield test. */
g_should_switch_threads = true;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_WithCoreMigration);
TestYieldSamePriority(sp_higher, sp_lower);
}
}
DOCTEST_SUBCASE("svc::SleepThread: Yield with bogus timeout does not switch to another thread same priority") {
/* Configure for yield test. */
g_should_switch_threads = false;
g_thread_wait_ns = INT64_C(-5);
TestYieldSamePriority(sp_higher, sp_lower);
}
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (c) 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/>.
*/
/* ams::test::TestThreadCreateRegistersOnFunctionEntry(void *ctx) */
.section .text._ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv, "ax", %progbits
.global _ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv
.type _ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv, %function
_ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv:
/* Save all registers to our context. */
stp x0, x1, [x0, #0x00]
stp x2, x3, [x0, #0x10]
stp x4, x5, [x0, #0x20]
stp x6, x7, [x0, #0x30]
stp x8, x9, [x0, #0x40]
stp x10, x11, [x0, #0x50]
stp x12, x13, [x0, #0x60]
stp x14, x15, [x0, #0x70]
stp x16, x17, [x0, #0x80]
stp x18, x19, [x0, #0x90]
stp x20, x21, [x0, #0xA0]
stp x22, x23, [x0, #0xB0]
stp x24, x25, [x0, #0xC0]
stp x26, x27, [x0, #0xD0]
stp x28, x29, [x0, #0xE0]
mov x1, sp
stp x30, x1, [x0, #0xF0]
/* Exit the thread. */
svc 0xa

View File

@@ -1,65 +0,0 @@
/*
* Copyright (c) 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 "util_common.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
void TestThreadCreateRegistersOnFunctionEntry(void *ctx);
DOCTEST_TEST_CASE( "Creating a thread results in fixed register contents." ) {
/* Create heap. */
ScopedHeap heap(os::MemoryPageSize);
/* Create register buffer. */
u64 thread_registers[32];
std::memset(thread_registers, 0xCC, sizeof(thread_registers));
/* Create thread. */
svc::Handle thread_handle;
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(std::addressof(thread_handle), reinterpret_cast<uintptr_t>(&TestThreadCreateRegistersOnFunctionEntry), reinterpret_cast<uintptr_t>(thread_registers), heap.GetAddress() + os::MemoryPageSize, HighestTestPriority, NumCores - 1)));
/* Start thread. */
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handle)));
/* Wait for thread to exit. */
s32 dummy;
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), std::addressof(thread_handle), 1, -1)));
/* Close thread handle. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handle)));
/* Check thread initial registers. */
for (size_t i = 0; i < util::size(thread_registers); ++i) {
if (i == 0) {
/* X0 is argument. */
DOCTEST_CHECK(thread_registers[i] == reinterpret_cast<uintptr_t>(thread_registers));
} else if (i == 18) {
/* X18 is an odd cfi value. */
DOCTEST_CHECK(thread_registers[i] != 0);
DOCTEST_CHECK((thread_registers[i] & 0x1) != 0);
} else if (i == 31) {
/* SP is user-provided sp. */
DOCTEST_CHECK(thread_registers[i] == (heap.GetAddress() + os::MemoryPageSize));
} else {
/* All other registers are zero. */
DOCTEST_CHECK(thread_registers[i] == 0);
}
}
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (c) 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 "util_common.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
DOCTEST_TEST_CASE( "Setting a thread's disable count will cause it to become pinned." ) {
DoWithThreadPinning([]() {
__asm__ __volatile__("" ::: "memory");
});
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (c) 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 "util_test_framework.hpp"
namespace ams::test {
inline void TestMemory(uintptr_t address, svc::MemoryState state, svc::MemoryPermission perm, u32 attr) {
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
DOCTEST_CHECK(mem_info.base_address <= address);
DOCTEST_CHECK(address < (mem_info.base_address + mem_info.size));
DOCTEST_CHECK(mem_info.state == state);
DOCTEST_CHECK(mem_info.permission == perm);
DOCTEST_CHECK(mem_info.attribute == attr);
}
inline void TestMemory(uintptr_t address, size_t size, svc::MemoryState state, svc::MemoryPermission perm, u32 attr) {
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
DOCTEST_CHECK(mem_info.base_address <= address);
DOCTEST_CHECK(mem_info.base_address < (address + size));
DOCTEST_CHECK((address + size) <= (mem_info.base_address + mem_info.size));
DOCTEST_CHECK(mem_info.state == state);
DOCTEST_CHECK(mem_info.permission == perm);
DOCTEST_CHECK(mem_info.attribute == attr);
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright (c) 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 "util_test_framework.hpp"
namespace ams::test {
static constexpr s32 NumCores = 4;
static constexpr s32 DpcManagerNormalThreadPriority = 59;
static constexpr s32 DpcManagerPreemptionThreadPriority = 63;
static constexpr s32 HighestTestPriority = 32;
static constexpr s32 LowestTestPriority = svc::LowestThreadPriority;
static_assert(HighestTestPriority < LowestTestPriority);
static constexpr TimeSpan PreemptionTimeSpan = TimeSpan::FromMilliSeconds(10);
constexpr inline bool IsPreemptionPriority(s32 core, s32 priority) {
return priority == ((core == (NumCores - 1)) ? DpcManagerPreemptionThreadPriority : DpcManagerNormalThreadPriority);
}
template<typename F>
void DoWithThreadPinning(F f) {
/* Get the thread local region. */
auto * const tlr = svc::GetThreadLocalRegion();
/* Require that we're not currently pinned. */
DOCTEST_CHECK((tlr->disable_count == 0));
DOCTEST_CHECK((!tlr->interrupt_flag));
/* Request to pin ourselves. */
tlr->disable_count = 1;
/* Wait long enough that we can be confident preemption will occur, and therefore our interrupt flag will be set. */
{
constexpr auto MinimumTicksToGuaranteeInterruptFlag = ::ams::svc::Tick(PreemptionTimeSpan) + ::ams::svc::Tick(PreemptionTimeSpan) + 2;
auto GetSystemTickForPinnedThread = []() ALWAYS_INLINE_LAMBDA -> ::ams::svc::Tick {
s64 v;
__asm__ __volatile__ ("mrs %x[v], cntpct_el0" : [v]"=r"(v) :: "memory");
return ::ams::svc::Tick(v);
};
const auto start_tick = GetSystemTickForPinnedThread();
while (true) {
if (tlr->interrupt_flag) {
break;
}
if (const auto cur_tick = GetSystemTickForPinnedThread(); (cur_tick - start_tick) > MinimumTicksToGuaranteeInterruptFlag) {
break;
}
}
}
/* We're pinned. Execute the user callback. */
bool callback_succeeded = true;
{
if constexpr (requires { { f() } -> std::convertible_to<bool>; }) {
callback_succeeded = f();
} else {
f();
}
}
/* Clear our disable count. */
tlr->disable_count = 0;
/* Get our interrupt flag. */
const auto interrupt_flag_while_pinned = tlr->interrupt_flag;
/* Unpin ourselves. */
if (interrupt_flag_while_pinned) {
svc::SynchronizePreemptionState();
}
/* Get our interrupt flag. */
const auto interrupt_flag_after_unpin = tlr->interrupt_flag;
/* We have access to all SVCs again. Check that our pinning happened as expected. */
DOCTEST_CHECK(interrupt_flag_while_pinned);
DOCTEST_CHECK(!interrupt_flag_after_unpin);
/* Check that our callback succeeded. */
DOCTEST_CHECK(callback_succeeded);
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (c) 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 "util_test_framework.hpp"
namespace ams::test {
class ScopedHeap {
NON_COPYABLE(ScopedHeap);
NON_MOVEABLE(ScopedHeap);
private:
uintptr_t m_address;
size_t m_size;
public:
explicit ScopedHeap(size_t size) {
this->SetHeapSize(size);
}
~ScopedHeap() {
const auto result = svc::SetHeapSize(std::addressof(m_address), 0);
DOCTEST_CHECK(R_SUCCEEDED(result));
}
void SetHeapSize(size_t size) {
m_size = util::AlignUp(size, svc::HeapSizeAlignment);
const auto result = svc::SetHeapSize(std::addressof(m_address), m_size);
DOCTEST_CHECK(R_SUCCEEDED(result));
}
uintptr_t GetAddress() const { return m_address; }
size_t GetSize() const { return m_size; }
};
}

View File

@@ -1,24 +0,0 @@
/*
* Copyright (c) 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 CATCH_CONFIG_NOSTDOUT
#define DOCTEST_CONFIG_NO_SHORT_MACRO_NAMES
#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS
#define DOCTEST_CONFIG_NO_EXCEPTIONS
#define DOCTEST_CONFIG_NO_POSIX_SIGNALS
#include "doctest.h"