Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -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
|
||||
#---------------------------------------------------------------------------------------
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user