Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,51 +0,0 @@
|
||||
ATMOSPHERE_BUILD_CONFIGS :=
|
||||
all: nx_release
|
||||
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
|
||||
define ATMOSPHERE_ADD_TARGET
|
||||
|
||||
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
|
||||
|
||||
$(strip $1):
|
||||
@echo "Building $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
clean-$(strip $1):
|
||||
@echo "Cleaning $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
endef
|
||||
|
||||
define ATMOSPHERE_ADD_TARGETS
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, $(strip $2)release, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5)" $(strip $6) \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, $(strip $2)debug, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 $(strip $6) \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, $(strip $2)audit, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 $(strip $6) \
|
||||
))
|
||||
|
||||
endef
|
||||
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, , nx-hac-001, arm-cortex-a57,,))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, win_x64, , generic_windows, generic_x64,,))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64, , generic_linux, generic_x64,,))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64_clang, clang_, generic_linux, generic_x64,, ATMOSPHERE_COMPILER_NAME="clang"))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_arm64_clang, clang_, generic_linux, generic_arm64,, ATMOSPHERE_COMPILER_NAME="clang"))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_x64, , generic_macos, generic_x64,,))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_arm64, , generic_macos, generic_arm64,,))
|
||||
|
||||
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
|
||||
|
||||
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))
|
||||
@@ -1,867 +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>
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace fssrv::impl {
|
||||
|
||||
const char *GetExecutionDirectoryPath();
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void GetPath(char *dst, size_t dst_size, const char *src) {
|
||||
if (fs::IsPathAbsolute(src)) {
|
||||
util::SNPrintf(dst, dst_size, "%s", src);
|
||||
} else {
|
||||
util::SNPrintf(dst, dst_size, "%s%s", fssrv::impl::GetExecutionDirectoryPath(), src);
|
||||
}
|
||||
}
|
||||
|
||||
#define TEST_R_EXPECT(__EXPR__, __EXPECTED__) \
|
||||
({ \
|
||||
const Result __test_result = (__EXPR__); \
|
||||
if (!(__EXPECTED__ ::Includes(__test_result))) { \
|
||||
printf("Unexpected result: %s gave 0x%08x (2%03d-%04d)\n", # __EXPR__, __test_result.GetValue(), __test_result.GetModule(), __test_result.GetDescription()); \
|
||||
return; \
|
||||
} \
|
||||
__test_result; \
|
||||
})
|
||||
|
||||
#define TEST_R_TRY(__EXPR__) \
|
||||
({ \
|
||||
const Result __test_result = (__EXPR__); \
|
||||
if (R_FAILED(__test_result)) { \
|
||||
printf("Unexpected result: %s gave 0x%08x (2%03d-%04d)\n", # __EXPR__, __test_result.GetValue(), __test_result.GetModule(), __test_result.GetDescription()); \
|
||||
return; \
|
||||
} \
|
||||
__test_result; \
|
||||
})
|
||||
|
||||
u8 g_buffer[64_KB];
|
||||
|
||||
void DoFsTests() {
|
||||
/* Declare buffer to hold any work paths we have. */
|
||||
char path_buf[fs::EntryNameLengthMax + 1];
|
||||
char path_buf2[fs::EntryNameLengthMax + 1];
|
||||
#define FORMAT_PATH(S) ({ GetPath(path_buf, sizeof(path_buf), S); path_buf; })
|
||||
#define FORMAT_PATH2(S) ({ GetPath(path_buf2, sizeof(path_buf2), S); path_buf2; })
|
||||
AMS_UNUSED(path_buf);
|
||||
AMS_UNUSED(path_buf2);
|
||||
|
||||
/* Clear anything from a previous test run, no obligation for this to succeed. */
|
||||
fs::DeleteDirectoryRecursively(FORMAT_PATH("./test_dir/"));
|
||||
|
||||
/* Verify that the test directory does not exist. */
|
||||
fs::DirectoryEntryType entry_type;
|
||||
TEST_R_EXPECT(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/")), fs::ResultPathNotFound);
|
||||
|
||||
/* Create the subdirectory. */
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/")));
|
||||
|
||||
/* Verify the test directory exists and is a directory. */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Create File */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* Create a file. */
|
||||
TEST_R_EXPECT(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/test_rand.bin")), fs::ResultPathNotFound);
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/test_rand.bin"), sizeof(g_buffer)));
|
||||
|
||||
/* Check the file has correct entry type. */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/test_rand.bin")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_File);
|
||||
|
||||
/* Create already existing file -> fs::ResultPathAlreadyExists(). */
|
||||
TEST_R_EXPECT(fs::CreateFile(FORMAT_PATH("./test_dir/test_rand.bin"), sizeof(g_buffer)), fs::ResultPathAlreadyExists);
|
||||
|
||||
/* Create already existing dir -> fs::ResultPathAlreadyExists(). */
|
||||
TEST_R_EXPECT(fs::CreateFile(FORMAT_PATH("./test_dir/"), sizeof(g_buffer)), fs::ResultPathAlreadyExists);
|
||||
|
||||
/* Create file without parent existing -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::CreateFile(FORMAT_PATH("./test_dir/aaa/bbb.bin"), sizeof(g_buffer)), fs::ResultPathNotFound);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Create Directory */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* Create the subdirectory. */
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/test_subdir/")));
|
||||
|
||||
/* Verify the test directory exists and is a directory. */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/test_subdir/")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
/* Create already existing file -> fs::ResultPathAlreadyExists(). */
|
||||
TEST_R_EXPECT(fs::CreateDirectory(FORMAT_PATH("./test_dir/test_rand.bin")), fs::ResultPathAlreadyExists);
|
||||
|
||||
/* Create already existing dir -> fs::ResultPathAlreadyExists(). */
|
||||
TEST_R_EXPECT(fs::CreateDirectory(FORMAT_PATH("./test_dir/")), fs::ResultPathAlreadyExists);
|
||||
|
||||
/* Create dir without parent existing -> fs::ResultPathAlreadyExists(). */
|
||||
TEST_R_EXPECT(fs::CreateDirectory(FORMAT_PATH("./test_dir/aaa/bbb/")), fs::ResultPathNotFound);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Delete File */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* Delete file succeeds. */
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/tmp_for_delete.bin"), sizeof(g_buffer)));
|
||||
TEST_R_TRY(fs::DeleteFile(FORMAT_PATH("./test_dir/tmp_for_delete.bin")));
|
||||
|
||||
/* Delete on invalid path -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::DeleteFile(FORMAT_PATH("./test_dir/invalid")), fs::ResultPathNotFound);
|
||||
|
||||
/* Delete on directory -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::DeleteFile(FORMAT_PATH("./test_dir/test_subdir/")), fs::ResultPathNotFound);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Delete Directory */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* Delete dir succeeds. */
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/tmp_for_delete/")));
|
||||
TEST_R_TRY(fs::DeleteDirectory(FORMAT_PATH("./test_dir/tmp_for_delete/")));
|
||||
|
||||
/* Delete on invalid path -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::DeleteDirectory(FORMAT_PATH("./test_dir/invalid/")), fs::ResultPathNotFound);
|
||||
|
||||
/* Delete on file -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::DeleteDirectory(FORMAT_PATH("./test_dir/test_rand.bin")), fs::ResultPathNotFound);
|
||||
|
||||
/* Delete on non-empty directory -> fs::ResultDirectoryNotEmpty(). */
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/tmp_for_delete/")));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/tmp_for_delete/tmp_for_delete.bin"), sizeof(g_buffer)));
|
||||
TEST_R_EXPECT(fs::DeleteDirectory(FORMAT_PATH("./test_dir/tmp_for_delete/")), fs::ResultDirectoryNotEmpty);
|
||||
TEST_R_TRY(fs::DeleteFile(FORMAT_PATH("./test_dir/tmp_for_delete/tmp_for_delete.bin")));
|
||||
TEST_R_TRY(fs::DeleteDirectory(FORMAT_PATH("./test_dir/tmp_for_delete/")));
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Delete Directory Recursively */
|
||||
/* ==================================================================================================================== */
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/0/0/")));
|
||||
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/0/y.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/0/z.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/0/0/x.bin"), 0));
|
||||
|
||||
TEST_R_TRY(fs::DeleteDirectoryRecursively(FORMAT_PATH("./test_dir/0/")));
|
||||
|
||||
/* Verify the test directory still exists and is a directory. */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
/* Verify the recursive directory doesn't. */
|
||||
TEST_R_EXPECT(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/0/")), fs::ResultPathNotFound);
|
||||
|
||||
/* Delete recursive on invalid path -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::DeleteDirectoryRecursively(FORMAT_PATH("./test_dir/invalid/")), fs::ResultPathNotFound);
|
||||
|
||||
/* Delete recursive on file -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::DeleteDirectoryRecursively(FORMAT_PATH("./test_dir/test_rand.bin")), fs::ResultPathNotFound);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Clean Directory Recursively */
|
||||
/* ==================================================================================================================== */
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/0/")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/0/0/")));
|
||||
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/0/y.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/0/z.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/0/x.bin"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/0/0/0/0/0/0/0/0/0/1/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/000/b/0/0/0/x.bin"), 0));
|
||||
|
||||
TEST_R_TRY(fs::CleanDirectoryRecursively(FORMAT_PATH("./test_dir/0/")));
|
||||
|
||||
/* Verify the recursive directory still exists and is a directory. */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/0/")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
/* Delete the recursive directory. */
|
||||
TEST_R_TRY(fs::DeleteDirectory(FORMAT_PATH("./test_dir/0/")));
|
||||
|
||||
/* Clean recursive on invalid path -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::CleanDirectoryRecursively(FORMAT_PATH("./test_dir/invalid/")), fs::ResultPathNotFound);
|
||||
|
||||
/* Clean recursive on file -> fs::ResultPathNotFound(). */
|
||||
TEST_R_EXPECT(fs::CleanDirectoryRecursively(FORMAT_PATH("./test_dir/test_rand.bin")), fs::ResultPathNotFound);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Rename File */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* Rename succeeds. */
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/a.bin"), 1_KB));
|
||||
TEST_R_TRY(fs::RenameFile(FORMAT_PATH("./test_dir/a.bin"), FORMAT_PATH2("./test_dir/b.bin")));
|
||||
|
||||
TEST_R_EXPECT(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/a.bin")), fs::ResultPathNotFound);
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/b.bin")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_File);
|
||||
|
||||
/* Rename non-existing -> fs::ResultPathNotFound */
|
||||
TEST_R_EXPECT(fs::RenameFile(FORMAT_PATH("./test_dir/invalid"), FORMAT_PATH2("./test_dir/invalid2")), fs::ResultPathNotFound);
|
||||
|
||||
/* Rename valid -> already existing gives fs::ResultPathAlreadyExists */
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/a.bin"), 1_KB));
|
||||
TEST_R_EXPECT(fs::RenameFile(FORMAT_PATH("./test_dir/a.bin"), FORMAT_PATH2("./test_dir/b.bin")), fs::ResultPathAlreadyExists);
|
||||
|
||||
/* Rename valid -> directory gives fs::ResultPathAlreadyExists */
|
||||
TEST_R_EXPECT(fs::RenameFile(FORMAT_PATH("./test_dir/a.bin"), FORMAT_PATH2("./test_dir/test_subdir/")), fs::ResultPathAlreadyExists);
|
||||
|
||||
/* Rename directory -> fs::ResultPathNotFound */
|
||||
TEST_R_EXPECT(fs::RenameFile(FORMAT_PATH("./test_dir/test_subdir/"), FORMAT_PATH2("./test_dir/c.bin")), fs::ResultPathNotFound);
|
||||
|
||||
/* Invalid doesn't affect the file/dir. */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/a.bin")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_File);
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/test_subdir/")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Rename Directory */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* Rename succeeds. */
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/dir_a/")));
|
||||
TEST_R_TRY(fs::RenameDirectory(FORMAT_PATH("./test_dir/dir_a/"), FORMAT_PATH2("./test_dir/dir_b/")));
|
||||
|
||||
TEST_R_EXPECT(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/dir_a/")), fs::ResultPathNotFound);
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/dir_b/")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
/* Rename non-existing -> fs::ResultPathNotFound */
|
||||
TEST_R_EXPECT(fs::RenameDirectory(FORMAT_PATH("./test_dir/invalid"), FORMAT_PATH2("./test_dir/invalid2")), fs::ResultPathNotFound);
|
||||
|
||||
/* Rename valid -> already existing gives fs::ResultPathAlreadyExists */
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/dir_a/")));
|
||||
TEST_R_EXPECT(fs::RenameDirectory(FORMAT_PATH("./test_dir/dir_a/"), FORMAT_PATH2("./test_dir/dir_b/")), fs::ResultPathAlreadyExists);
|
||||
|
||||
/* Rename valid -> file gives fs::ResultPathAlreadyExists */
|
||||
TEST_R_EXPECT(fs::RenameDirectory(FORMAT_PATH("./test_dir/dir_a/"), FORMAT_PATH2("./test_dir/a.bin")), fs::ResultPathAlreadyExists);
|
||||
|
||||
/* Rename file -> fs::ResultPathNotFound */
|
||||
TEST_R_EXPECT(fs::RenameDirectory(FORMAT_PATH("./test_dir/a.bin"), FORMAT_PATH2("./test_dir/dir_c/")), fs::ResultPathNotFound);
|
||||
|
||||
/* Invalid doesn't affect the file/dir. */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/a.bin")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_File);
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/dir_a/")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Get Entry Type */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* File -> file. */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/a.bin")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_File);
|
||||
|
||||
/* Dir -> dir */
|
||||
TEST_R_TRY(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/dir_a/")));
|
||||
AMS_ABORT_UNLESS(entry_type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
/* Invalid -> fs::ResultPathNotFound */
|
||||
TEST_R_EXPECT(fs::GetEntryType(std::addressof(entry_type), FORMAT_PATH("./test_dir/invalid")), fs::ResultPathNotFound);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Get Free Space Size */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
s64 free_size = 0;
|
||||
TEST_R_TRY(fs::GetFreeSpaceSize(std::addressof(free_size), FORMAT_PATH("./test_dir/")));
|
||||
AMS_ABORT_UNLESS(free_size > 0);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Get Total Space Size */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
s64 total_size = 0;
|
||||
TEST_R_TRY(fs::GetTotalSpaceSize(std::addressof(total_size), FORMAT_PATH("./test_dir/")));
|
||||
AMS_ABORT_UNLESS(total_size >= free_size);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Get File Time Stamp */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* Get timestamp succeeds. */
|
||||
fs::FileTimeStamp timestamp;
|
||||
TEST_R_TRY(fs::GetFileTimeStamp(std::addressof(timestamp), FORMAT_PATH("./test_dir/a.bin")));
|
||||
AMS_ABORT_UNLESS(timestamp.create.value > 0);
|
||||
AMS_ABORT_UNLESS(timestamp.access.value > 0);
|
||||
AMS_ABORT_UNLESS(timestamp.modify.value > 0);
|
||||
AMS_ABORT_UNLESS(!timestamp.is_local_time);
|
||||
|
||||
/* Invalid -> fs::ResultPathNotFound */
|
||||
TEST_R_EXPECT(fs::GetFileTimeStamp(std::addressof(timestamp), FORMAT_PATH("./test_dir/invalid")), fs::ResultPathNotFound);
|
||||
|
||||
/* Directory -> fs::ResultPathNotFound */
|
||||
TEST_R_EXPECT(fs::GetFileTimeStamp(std::addressof(timestamp), FORMAT_PATH("./test_dir/dir_a/")), fs::ResultPathNotFound);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Query Entry */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
TEST_R_EXPECT(fs::SetConcatenationFileAttribute(FORMAT_PATH("./test_dir/")), fs::ResultUnsupportedOperation);
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Open File */
|
||||
/* ==================================================================================================================== */
|
||||
|
||||
/* Open valid succeeds. */
|
||||
fs::FileHandle file;
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_ReadWrite | fs::OpenMode_AllowAppend));
|
||||
fs::CloseFile(file);
|
||||
|
||||
/* Open invalid -> path not found. */
|
||||
TEST_R_EXPECT(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/invalid"), fs::OpenMode_ReadWrite | fs::OpenMode_AllowAppend), fs::ResultPathNotFound);
|
||||
|
||||
/* Open directory -> path not found. */
|
||||
TEST_R_EXPECT(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/dir_a/"), fs::OpenMode_ReadWrite | fs::OpenMode_AllowAppend), fs::ResultPathNotFound);
|
||||
|
||||
/* Open with invalid mode -> fs::ResultInvalidOpenMode */
|
||||
TEST_R_EXPECT(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), static_cast<fs::OpenMode>(~0u)), fs::ResultInvalidOpenMode);
|
||||
|
||||
/* Read only file is read only. */
|
||||
{
|
||||
s64 file_size;
|
||||
u8 buf[1_KB];
|
||||
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* File size matches create. */
|
||||
TEST_R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
AMS_ABORT_UNLESS(file_size == 1_KB);
|
||||
|
||||
/* Read succeeds. */
|
||||
TEST_R_TRY(fs::ReadFile(file, 0, buf, sizeof(buf)));
|
||||
|
||||
/* Completely empty read ok. */
|
||||
TEST_R_TRY(fs::ReadFile(file, 0, nullptr, 0));
|
||||
|
||||
/* Flush succeeds. */
|
||||
TEST_R_TRY(fs::FlushFile(file));
|
||||
}
|
||||
|
||||
/* Incorrect arguments return incorrect results. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::ReadFile(file, -1, buf, sizeof(buf)), fs::ResultOutOfRange);
|
||||
}
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::ReadFile(file, 0, buf, -1), fs::ResultOutOfRange);
|
||||
}
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::ReadFile(file, 0, nullptr, sizeof(buf)), fs::ResultNullptrArgument);
|
||||
}
|
||||
|
||||
/* Write fails. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::WriteFile(file, 0, g_buffer, sizeof(g_buffer), fs::WriteOption::None), fs::ResultWriteNotPermitted);
|
||||
}
|
||||
|
||||
/* Set size fails. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::SetFileSize(file, 2_KB), fs::ResultWriteNotPermitted);
|
||||
}
|
||||
|
||||
/* File size unchanged by bad set size. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
AMS_ABORT_UNLESS(file_size == 1_KB);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write only file is writable but not readable. */
|
||||
{
|
||||
s64 file_size;
|
||||
u8 buf[1_KB];
|
||||
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Write succeeds. */
|
||||
std::memset(buf, 0xcc, sizeof(buf));
|
||||
TEST_R_TRY(fs::WriteFile(file, 0, buf, sizeof(buf), fs::WriteOption::None));
|
||||
|
||||
/* Flush succeeds. */
|
||||
TEST_R_TRY(fs::FlushFile(file));
|
||||
|
||||
/* Write with flush succeeds. */
|
||||
TEST_R_TRY(fs::WriteFile(file, 0, buf, sizeof(buf), fs::WriteOption::Flush));
|
||||
|
||||
/* Get size succeeds. */
|
||||
TEST_R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
AMS_ABORT_UNLESS(file_size == 1_KB);
|
||||
|
||||
/* Set size succeeds. */
|
||||
TEST_R_TRY(fs::SetFileSize(file, 2_KB));
|
||||
TEST_R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
AMS_ABORT_UNLESS(file_size == 2_KB);
|
||||
|
||||
/* Write at updated size works. */
|
||||
TEST_R_TRY(fs::WriteFile(file, 1_KB, buf, sizeof(buf), fs::WriteOption::Flush));
|
||||
|
||||
/* Truncate down succeeds. */
|
||||
TEST_R_TRY(fs::SetFileSize(file, 1_KB));
|
||||
TEST_R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
AMS_ABORT_UNLESS(file_size == 1_KB);
|
||||
|
||||
/* Completely empty write ok. */
|
||||
TEST_R_TRY(fs::WriteFile(file, 0, nullptr, 0, fs::WriteOption::Flush));
|
||||
}
|
||||
|
||||
/* Incorrect arguments return incorrect results. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::WriteFile(file, -1, buf, sizeof(buf), fs::WriteOption::None), fs::ResultOutOfRange);
|
||||
}
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::WriteFile(file, 0, buf, -1, fs::WriteOption::None), fs::ResultOutOfRange);
|
||||
}
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::WriteFile(file, 0, nullptr, sizeof(buf), fs::WriteOption::None), fs::ResultNullptrArgument);
|
||||
}
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::WriteFile(file, 1_KB, buf, sizeof(buf), fs::WriteOption::None), fs::ResultFileExtensionWithoutOpenModeAllowAppend);
|
||||
}
|
||||
|
||||
/* Read fails. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_EXPECT(fs::ReadFile(file, 0, buf, sizeof(buf)), fs::ResultReadNotPermitted);
|
||||
}
|
||||
|
||||
/* Appending works with OpenMode_AllowAppend. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_Write | fs::OpenMode_AllowAppend));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
AMS_ABORT_UNLESS(file_size == 1_KB);
|
||||
|
||||
for (size_t i = 0; i < sizeof(buf); ++i) {
|
||||
buf[i] = static_cast<u8>(i);
|
||||
}
|
||||
TEST_R_TRY(fs::WriteFile(file, 1_KB, buf, sizeof(buf), fs::WriteOption::Flush));
|
||||
|
||||
TEST_R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
AMS_ABORT_UNLESS(file_size == 2_KB);
|
||||
}
|
||||
|
||||
/* Data is persistent. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/a.bin"), fs::OpenMode_ReadWrite));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
TEST_R_TRY(fs::ReadFile(file, 0, buf, sizeof(buf)));
|
||||
|
||||
for (size_t i = 0; i < 1_KB; ++i) {
|
||||
AMS_ABORT_UNLESS(buf[i] == 0xCC);
|
||||
}
|
||||
|
||||
TEST_R_TRY(fs::ReadFile(file, 1_KB, buf, sizeof(buf)));
|
||||
|
||||
for (size_t i = 0; i < 1_KB; ++i) {
|
||||
AMS_ABORT_UNLESS(buf[i] == static_cast<u8>(i));
|
||||
}
|
||||
|
||||
TEST_R_TRY(fs::WriteFile(file, 0, buf, sizeof(buf), fs::WriteOption::Flush));
|
||||
|
||||
TEST_R_TRY(fs::SetFileSize(file, 1_KB));
|
||||
|
||||
TEST_R_TRY(fs::GetFileSize(std::addressof(file_size), file));
|
||||
AMS_ABORT_UNLESS(file_size == 1_KB);
|
||||
|
||||
TEST_R_TRY(fs::ReadFile(file, 0, buf, sizeof(buf)));
|
||||
|
||||
for (size_t i = 0; i < 1_KB; ++i) {
|
||||
AMS_ABORT_UNLESS(buf[i] == static_cast<u8>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* More involved file data test using random buffer. */
|
||||
{
|
||||
u8 buf[1_KB];
|
||||
/* Write random data. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/test_rand.bin"), fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Get a bunch of random data. */
|
||||
os::GenerateRandomBytes(g_buffer, sizeof(g_buffer));
|
||||
|
||||
/* Write it to disk. */
|
||||
TEST_R_TRY(fs::WriteFile(file, 0, g_buffer, sizeof(g_buffer), fs::WriteOption::None));
|
||||
TEST_R_TRY(fs::FlushFile(file));
|
||||
}
|
||||
|
||||
/* Read and verify random data. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenFile(std::addressof(file), FORMAT_PATH("./test_dir/test_rand.bin"), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
u32 ofs;
|
||||
for (size_t i = 0; i < 1000; ++i) {
|
||||
os::GenerateRandomBytes(std::addressof(ofs), sizeof(ofs));
|
||||
ofs %= (sizeof(g_buffer) - sizeof(buf));
|
||||
|
||||
TEST_R_TRY(fs::ReadFile(file, ofs, buf, sizeof(buf)));
|
||||
AMS_ABORT_UNLESS(std::memcmp(buf, g_buffer + ofs, sizeof(buf)) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Open Directory */
|
||||
/* ==================================================================================================================== */
|
||||
fs::DirectoryHandle dir;
|
||||
TEST_R_TRY(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/dir_a/"), fs::OpenDirectoryMode_All | fs::OpenDirectoryMode_NotRequireFileSize));
|
||||
fs::CloseDirectory(dir);
|
||||
|
||||
/* Open invalid -> path not found. */
|
||||
TEST_R_EXPECT(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/invalid"), fs::OpenDirectoryMode_All | fs::OpenDirectoryMode_NotRequireFileSize), fs::ResultPathNotFound);
|
||||
|
||||
/* Open file -> path not found. */
|
||||
TEST_R_EXPECT(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/a.bin"), fs::OpenDirectoryMode_All | fs::OpenDirectoryMode_NotRequireFileSize), fs::ResultPathNotFound);
|
||||
|
||||
/* Open with invalid mode -> fs::ResultInvalidOpenMode */
|
||||
TEST_R_EXPECT(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/dir_a/"), static_cast<fs::OpenDirectoryMode>(~0u)), fs::ResultInvalidOpenMode);
|
||||
|
||||
/* Populate test directory with three files and two dirs. */
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/dir_a/f0"), 0));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/dir_a/f1"), 1_KB));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/dir_a/f2"), 0x42069));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/dir_a/d0")));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/dir_a/d1")));
|
||||
TEST_R_TRY(fs::CreateFile(FORMAT_PATH("./test_dir/dir_a/d0/file"), 0));
|
||||
TEST_R_TRY(fs::CreateDirectory(FORMAT_PATH("./test_dir/dir_a/d0/dir/")));
|
||||
|
||||
/* Directory tests. */
|
||||
{
|
||||
bool seen_file[3];
|
||||
bool seen_dir[2];
|
||||
|
||||
constexpr s64 NumFiles = util::size(seen_file);
|
||||
constexpr s64 NumDirs = util::size(seen_dir);
|
||||
constexpr s64 NumAll = NumFiles + NumDirs;
|
||||
|
||||
fs::DirectoryEntry entries[2 * NumAll];
|
||||
s64 entry_count;
|
||||
|
||||
auto ResetSeenFiles = [&] () { for (auto &b : seen_file) { b = false; } };
|
||||
auto ResetSeenDirs = [&] () { for (auto &b : seen_dir) { b = false; } };
|
||||
auto ResetSeenAll = [&] () { ResetSeenFiles(); ResetSeenDirs(); };
|
||||
|
||||
auto CheckSeenFiles = [&] () { for (const auto b : seen_file) { AMS_ABORT_UNLESS(b); } };
|
||||
auto CheckSeenDirs = [&] () { for (const auto b : seen_dir) { AMS_ABORT_UNLESS(b); } };
|
||||
auto CheckSeenAll = [&] () { CheckSeenFiles(); CheckSeenDirs(); };
|
||||
|
||||
auto CheckNotSeenFiles = [&] () { for (const auto b : seen_file) { AMS_ABORT_UNLESS(!b); } };
|
||||
auto CheckNotSeenDirs = [&] () { for (const auto b : seen_dir) { AMS_ABORT_UNLESS(!b); } };
|
||||
|
||||
auto CheckDirectoryEntry = [&] (const fs::DirectoryEntry &entry) {
|
||||
/* Check name. */
|
||||
AMS_ABORT_UNLESS(entry.name[0] == 'f' || entry.name[0] == 'd');
|
||||
AMS_ABORT_UNLESS('0' <= entry.name[1] && entry.name[1] <= '9');
|
||||
AMS_ABORT_UNLESS(entry.name[2] == 0);
|
||||
|
||||
/* Check type. */
|
||||
if (entry.name[0] == 'f') {
|
||||
AMS_ABORT_UNLESS(entry.type == fs::DirectoryEntryType_File);
|
||||
|
||||
/* If file, check size. */
|
||||
switch (entry.name[1]) {
|
||||
case '0': AMS_ABORT_UNLESS(entry.file_size == 0); break;
|
||||
case '1': AMS_ABORT_UNLESS(entry.file_size == 1_KB); break;
|
||||
case '2': AMS_ABORT_UNLESS(entry.file_size == 0x42069); break;
|
||||
}
|
||||
|
||||
AMS_ABORT_UNLESS(!seen_file[(entry.name[1] - '0')]);
|
||||
seen_file[(entry.name[1] - '0')] = true;
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(entry.type == fs::DirectoryEntryType_Directory);
|
||||
|
||||
AMS_ABORT_UNLESS(!seen_dir[(entry.name[1] - '0')]);
|
||||
seen_dir[(entry.name[1] - '0')] = true;
|
||||
}
|
||||
};
|
||||
|
||||
/* Get EntryCount is correct. */
|
||||
{
|
||||
/* All returns all entries. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/dir_a/"), fs::OpenDirectoryMode_All));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count), dir));
|
||||
AMS_ABORT_UNLESS(entry_count == NumAll);
|
||||
}
|
||||
|
||||
/* File returns only files, and does not count things in subdirectories. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/dir_a/"), fs::OpenDirectoryMode_File));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count), dir));
|
||||
AMS_ABORT_UNLESS(entry_count == NumFiles);
|
||||
}
|
||||
|
||||
/* Dir returns only dirs, and does not count things in subdirectories. */
|
||||
{
|
||||
TEST_R_TRY(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/dir_a/"), fs::OpenDirectoryMode_Directory));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count), dir));
|
||||
AMS_ABORT_UNLESS(entry_count == NumDirs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read is correct, N at a time. */
|
||||
for (s64 at_a_time = 1; at_a_time <= 2 * NumAll; ++at_a_time) {
|
||||
/* All returns all entries. */
|
||||
{
|
||||
ResetSeenAll();
|
||||
|
||||
TEST_R_TRY(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/dir_a/"), fs::OpenDirectoryMode_All));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count), dir));
|
||||
AMS_ABORT_UNLESS(entry_count == NumAll);
|
||||
|
||||
s64 remaining = entry_count;
|
||||
while (remaining > 0) {
|
||||
s64 cur;
|
||||
TEST_R_TRY(fs::ReadDirectory(std::addressof(cur), entries, dir, at_a_time));
|
||||
AMS_ABORT_UNLESS(cur <= remaining);
|
||||
AMS_ABORT_UNLESS(cur == std::min<s64>(at_a_time, remaining));
|
||||
|
||||
for (s64 i = 0; i < cur; ++i) {
|
||||
CheckDirectoryEntry(entries[i]);
|
||||
}
|
||||
|
||||
remaining -= cur;
|
||||
}
|
||||
|
||||
CheckSeenAll();
|
||||
|
||||
/* Read succeeds at end of dir. */
|
||||
s64 cur;
|
||||
TEST_R_TRY(fs::ReadDirectory(std::addressof(cur), entries, dir, at_a_time));
|
||||
AMS_ABORT_UNLESS(cur == 0);
|
||||
|
||||
/* Get entry count still shows correct value. */
|
||||
s64 entry_count2;
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count2), dir));
|
||||
AMS_ABORT_UNLESS(entry_count2 == entry_count);
|
||||
}
|
||||
|
||||
/* File returns only files. */
|
||||
{
|
||||
ResetSeenAll();
|
||||
|
||||
TEST_R_TRY(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/dir_a/"), fs::OpenDirectoryMode_File));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count), dir));
|
||||
AMS_ABORT_UNLESS(entry_count == NumFiles);
|
||||
|
||||
s64 remaining = entry_count;
|
||||
while (remaining > 0) {
|
||||
s64 cur;
|
||||
TEST_R_TRY(fs::ReadDirectory(std::addressof(cur), entries, dir, at_a_time));
|
||||
AMS_ABORT_UNLESS(cur <= remaining);
|
||||
AMS_ABORT_UNLESS(cur == std::min<s64>(at_a_time, remaining));
|
||||
|
||||
for (s64 i = 0; i < cur; ++i) {
|
||||
CheckDirectoryEntry(entries[i]);
|
||||
}
|
||||
|
||||
remaining -= cur;
|
||||
}
|
||||
|
||||
CheckSeenFiles();
|
||||
CheckNotSeenDirs();
|
||||
|
||||
/* Read succeeds at end of dir. */
|
||||
s64 cur;
|
||||
TEST_R_TRY(fs::ReadDirectory(std::addressof(cur), entries, dir, at_a_time));
|
||||
AMS_ABORT_UNLESS(cur == 0);
|
||||
|
||||
/* Get entry count still shows correct value. */
|
||||
s64 entry_count2;
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count2), dir));
|
||||
AMS_ABORT_UNLESS(entry_count2 == entry_count);
|
||||
}
|
||||
|
||||
/* Directory returns only dirs. */
|
||||
{
|
||||
ResetSeenAll();
|
||||
|
||||
TEST_R_TRY(fs::OpenDirectory(std::addressof(dir), FORMAT_PATH("./test_dir/dir_a/"), fs::OpenDirectoryMode_Directory));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count), dir));
|
||||
AMS_ABORT_UNLESS(entry_count == NumDirs);
|
||||
|
||||
s64 remaining = entry_count;
|
||||
while (remaining > 0) {
|
||||
s64 cur;
|
||||
TEST_R_TRY(fs::ReadDirectory(std::addressof(cur), entries, dir, at_a_time));
|
||||
AMS_ABORT_UNLESS(cur <= remaining);
|
||||
AMS_ABORT_UNLESS(cur == std::min<s64>(at_a_time, remaining));
|
||||
|
||||
for (s64 i = 0; i < cur; ++i) {
|
||||
CheckDirectoryEntry(entries[i]);
|
||||
}
|
||||
|
||||
remaining -= cur;
|
||||
}
|
||||
|
||||
CheckSeenDirs();
|
||||
CheckNotSeenFiles();
|
||||
|
||||
/* Read succeeds at end of dir. */
|
||||
s64 cur;
|
||||
TEST_R_TRY(fs::ReadDirectory(std::addressof(cur), entries, dir, at_a_time));
|
||||
AMS_ABORT_UNLESS(cur == 0);
|
||||
|
||||
/* Get entry count still shows correct value. */
|
||||
s64 entry_count2;
|
||||
TEST_R_TRY(fs::GetDirectoryEntryCount(std::addressof(entry_count2), dir));
|
||||
AMS_ABORT_UNLESS(entry_count2 == entry_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================================================================================================================== */
|
||||
/* Cleanup */
|
||||
/* ==================================================================================================================== */
|
||||
TEST_R_TRY(fs::DeleteDirectoryRecursively(FORMAT_PATH("./test_dir/")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Main() {
|
||||
fs::SetEnabledAutoAbort(false);
|
||||
|
||||
printf("Doing FS test!\n");
|
||||
DoFsTests();
|
||||
printf("All tests completed!\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common stratosphere sysmodule configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||
|
||||
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
|
||||
export BOARD_TARGET_SUFFIX := .kip
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
|
||||
export BOARD_TARGET_SUFFIX := .exe
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_linux)
|
||||
export BOARD_TARGET_SUFFIX :=
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_macos)
|
||||
export BOARD_TARGET_SUFFIX :=
|
||||
else
|
||||
export BOARD_TARGET_SUFFIX := $(TARGET)
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(__RECURSIVE__),1)
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
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) \
|
||||
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||
|
||||
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: clean all check_lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
|
||||
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
|
||||
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
|
||||
-f $(THIS_MAKEFILE)
|
||||
|
||||
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
|
||||
@$(SILENTCMD)echo "Checked library."
|
||||
|
||||
check_lib:
|
||||
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
|
||||
|
||||
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(BOARD_TARGET) $(TARGET).elf
|
||||
@for i in $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR); do [ -d $$i ] && rmdir $$i 2>/dev/null || true; done
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT)$(BOARD_TARGET_SUFFIX)
|
||||
|
||||
%.kip : %.elf
|
||||
|
||||
%.nsp : %.nso %.npdm
|
||||
|
||||
%.nso: %.elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT).elf: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $(OUTPUT).lst)
|
||||
|
||||
$(OUTPUT).exe: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
|
||||
ifeq ($(strip $(BOARD_TARGET_SUFFIX)),)
|
||||
$(OUTPUT): $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $@.lst)
|
||||
endif
|
||||
|
||||
%.npdm : %.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,51 +0,0 @@
|
||||
ATMOSPHERE_BUILD_CONFIGS :=
|
||||
all: nx_release
|
||||
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
|
||||
define ATMOSPHERE_ADD_TARGET
|
||||
|
||||
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
|
||||
|
||||
$(strip $1):
|
||||
@echo "Building $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
clean-$(strip $1):
|
||||
@echo "Cleaning $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
endef
|
||||
|
||||
define ATMOSPHERE_ADD_TARGETS
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, $(strip $2)release, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5)" $(strip $6) \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, $(strip $2)debug, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 $(strip $6) \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, $(strip $2)audit, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 $(strip $6) \
|
||||
))
|
||||
|
||||
endef
|
||||
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, , nx-hac-001, arm-cortex-a57,,))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, win_x64, , generic_windows, generic_x64,,))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64, , generic_linux, generic_x64,,))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64_clang, clang_, generic_linux, generic_x64,, ATMOSPHERE_COMPILER_NAME="clang"))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_arm64_clang, clang_, generic_linux, generic_arm64,, ATMOSPHERE_COMPILER_NAME="clang"))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_x64, , generic_macos, generic_x64,,))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_arm64, , generic_macos, generic_arm64,,))
|
||||
|
||||
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
|
||||
|
||||
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))
|
||||
@@ -1,476 +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>
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace {
|
||||
|
||||
struct InterThreadSync {
|
||||
util::Atomic<int> reader_state;
|
||||
util::Atomic<int> writer_state;
|
||||
os::EventType writer_ready_event;
|
||||
os::EventType reader_ready_event;
|
||||
union {
|
||||
struct {
|
||||
os::SystemEventType system_event_as_manual_clear_event;
|
||||
os::SystemEventType system_event_as_manual_clear_interprocess_event;
|
||||
os::SystemEventType system_event_as_auto_clear_event;
|
||||
os::SystemEventType system_event_as_auto_clear_interprocess_event;
|
||||
};
|
||||
os::SystemEventType system_events[4];
|
||||
};
|
||||
};
|
||||
|
||||
bool IsManualClearEventIndex(size_t i) {
|
||||
return i == 0 || i == 1;
|
||||
}
|
||||
|
||||
alignas(os::MemoryPageSize) constinit u8 g_writer_thread_stack[16_KB];
|
||||
alignas(os::MemoryPageSize) constinit u8 g_reader_thread_stack[16_KB];
|
||||
|
||||
void TestWriterThread(void *arg) {
|
||||
/* Get the synchronization arguments. */
|
||||
auto &sync = *static_cast<InterThreadSync *>(arg);
|
||||
AMS_UNUSED(sync);
|
||||
|
||||
/* Wait for reader to be ready. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 1);
|
||||
|
||||
/* Verify that all events can be signaled. */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 1;
|
||||
os::SignalEvent(std::addressof(sync.writer_ready_event));
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 1);
|
||||
}
|
||||
|
||||
/* Verify that all events can be signaled (for TimedWait 0). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 2;
|
||||
os::SignalEvent(std::addressof(sync.writer_ready_event));
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 2);
|
||||
}
|
||||
|
||||
/* Verify that all events can be signaled (for TimedWait 2). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 3;
|
||||
os::SignalEvent(std::addressof(sync.writer_ready_event));
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 3);
|
||||
}
|
||||
|
||||
/* Verify that all events can be signaled (for True Wait). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 4;
|
||||
os::SignalEvent(std::addressof(sync.writer_ready_event));
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 4);
|
||||
}
|
||||
|
||||
/* Verify that all events can be signaled (TryWaitAny). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 5;
|
||||
os::SignalEvent(std::addressof(sync.writer_ready_event));
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 5);
|
||||
}
|
||||
|
||||
/* Verify that all events can be signaled (TimedWaitAny 0). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 6;
|
||||
os::SignalEvent(std::addressof(sync.writer_ready_event));
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 6);
|
||||
}
|
||||
|
||||
/* Verify that all events can be signaled (TimedWaitAny 2). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 7;
|
||||
os::SignalEvent(std::addressof(sync.writer_ready_event));
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 7);
|
||||
}
|
||||
|
||||
/* Verify that all events can be signaled (TrueWaitAny). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 8;
|
||||
os::SignalEvent(std::addressof(sync.writer_ready_event));
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 8);
|
||||
}
|
||||
|
||||
/* Verify that reader can receive without explicit sync. */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Set the event for this go. */
|
||||
os::SignalSystemEvent(sync.system_events + i);
|
||||
|
||||
sync.writer_state = 9;
|
||||
|
||||
}
|
||||
|
||||
/* Wait for the reader to finish. */
|
||||
os::WaitEvent(std::addressof(sync.reader_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.reader_state == 9);
|
||||
}
|
||||
|
||||
void TestReaderThread(void *arg) {
|
||||
/* Get the synchronization arguments. */
|
||||
auto &sync = *static_cast<InterThreadSync *>(arg);
|
||||
AMS_UNUSED(sync);
|
||||
|
||||
/* Set up multi-wait objects. */
|
||||
os::MultiWaitType mw;
|
||||
os::MultiWaitHolderType holders[util::size(sync.system_events)];
|
||||
os::InitializeMultiWait(std::addressof(mw));
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
os::InitializeMultiWaitHolder(holders + i, sync.system_events + i);
|
||||
os::LinkMultiWaitHolder(std::addressof(mw), holders + i);
|
||||
}
|
||||
ON_SCOPE_EXIT {
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
os::UnlinkMultiWaitHolder(holders + i);
|
||||
os::FinalizeMultiWaitHolder(holders + i);
|
||||
}
|
||||
os::FinalizeMultiWait(std::addressof(mw));
|
||||
};
|
||||
|
||||
/* Sanity check: all events are non-signaled. */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + i) == false);
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + i, TimeSpan::FromNanoSeconds(0)) == false);
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + i, TimeSpan::FromMilliSeconds(2)) == false);
|
||||
}
|
||||
|
||||
/* Sanity check that wait any does the right thing when nothing is signaled. */
|
||||
AMS_ABORT_UNLESS(os::TryWaitAny(std::addressof(mw)) == nullptr);
|
||||
AMS_ABORT_UNLESS(os::TimedWaitAny(std::addressof(mw), TimeSpan::FromNanoSeconds(0)) == nullptr);
|
||||
AMS_ABORT_UNLESS(os::TimedWaitAny(std::addressof(mw), TimeSpan::FromNanoSeconds(2)) == nullptr);
|
||||
|
||||
/* Let writer know that we're ready. */
|
||||
sync.reader_state = 1;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
|
||||
/* Verify that we can receive signal on each event. */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Wait for writer to do the relevant work */
|
||||
os::WaitEvent(std::addressof(sync.writer_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.writer_state == 1);
|
||||
|
||||
/* Test all events. */
|
||||
for (size_t n = 0; n < util::size(sync.system_events); ++n) {
|
||||
if (i == n) {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == true);
|
||||
if (IsManualClearEventIndex(n)) {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == true);
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 1;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
/* Verify that we can receive signal on each event (Timed Wait 0). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Wait for writer to do the relevant work */
|
||||
os::WaitEvent(std::addressof(sync.writer_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.writer_state == 2);
|
||||
|
||||
/* Test all events. */
|
||||
for (size_t n = 0; n < util::size(sync.system_events); ++n) {
|
||||
if (i == n) {
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == true);
|
||||
if (IsManualClearEventIndex(n)) {
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == true);
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == false);
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == false);
|
||||
}
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(0)) == false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 2;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
/* Verify that we can receive signal on each event (Timed Wait 2). */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Wait for writer to do the relevant work */
|
||||
os::WaitEvent(std::addressof(sync.writer_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.writer_state == 3);
|
||||
|
||||
/* Test all events. */
|
||||
for (size_t n = 0; n < util::size(sync.system_events); ++n) {
|
||||
if (i == n) {
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == true);
|
||||
if (IsManualClearEventIndex(n)) {
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == true);
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == false);
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == false);
|
||||
}
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(os::TimedWaitSystemEvent(sync.system_events + n, TimeSpan::FromMilliSeconds(2)) == false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 3;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
/* Verify that we can receive signal on each event. */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Wait for writer to do the relevant work */
|
||||
os::WaitEvent(std::addressof(sync.writer_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.writer_state == 4);
|
||||
|
||||
/* Test all events. */
|
||||
for (size_t n = 0; n < util::size(sync.system_events); ++n) {
|
||||
if (i == n) {
|
||||
os::WaitSystemEvent(sync.system_events + n);
|
||||
if (IsManualClearEventIndex(n)) {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == true);
|
||||
os::WaitSystemEvent(sync.system_events + n);
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 4;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
/* Verify that we can receive signal on each event (TryWaitAny) */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Wait for writer to do the relevant work */
|
||||
os::WaitEvent(std::addressof(sync.writer_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.writer_state == 5);
|
||||
|
||||
/* Get the signaled holder. */
|
||||
auto *signaled = os::TryWaitAny(std::addressof(mw));
|
||||
AMS_ABORT_UNLESS(signaled == holders + i);
|
||||
|
||||
/* Test all events. */
|
||||
for (size_t n = 0; n < util::size(sync.system_events); ++n) {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == (i == n));
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 5;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
/* Verify that we can receive signal on each event (TimedWaitAny 0) */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Wait for writer to do the relevant work */
|
||||
os::WaitEvent(std::addressof(sync.writer_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.writer_state == 6);
|
||||
|
||||
/* Get the signaled holder. */
|
||||
auto *signaled = os::TimedWaitAny(std::addressof(mw), TimeSpan::FromMilliSeconds(0));
|
||||
AMS_ABORT_UNLESS(signaled == holders + i);
|
||||
|
||||
/* Test all events. */
|
||||
for (size_t n = 0; n < util::size(sync.system_events); ++n) {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == (i == n));
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 6;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
/* Verify that we can receive signal on each event (TimedWaitAny 2) */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Wait for writer to do the relevant work */
|
||||
os::WaitEvent(std::addressof(sync.writer_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.writer_state == 7);
|
||||
|
||||
/* Get the signaled holder. */
|
||||
auto *signaled = os::TimedWaitAny(std::addressof(mw), TimeSpan::FromMilliSeconds(2));
|
||||
AMS_ABORT_UNLESS(signaled == holders + i);
|
||||
|
||||
/* Test all events. */
|
||||
for (size_t n = 0; n < util::size(sync.system_events); ++n) {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == (i == n));
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 7;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
/* Verify that we can receive signal on each event (True WaitAny) */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
/* Wait for writer to do the relevant work */
|
||||
os::WaitEvent(std::addressof(sync.writer_ready_event));
|
||||
AMS_ABORT_UNLESS(sync.writer_state == 8);
|
||||
|
||||
/* Get the signaled holder. */
|
||||
auto *signaled = os::WaitAny(std::addressof(mw));
|
||||
AMS_ABORT_UNLESS(signaled == holders + i);
|
||||
|
||||
/* Test all events. */
|
||||
for (size_t n = 0; n < util::size(sync.system_events); ++n) {
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == (i == n));
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 8;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
/* Verify that we can receive wait-any signals without sync. */
|
||||
for (size_t i = 0; i < util::size(sync.system_events); ++i) {
|
||||
auto *signaled = os::WaitAny(std::addressof(mw));
|
||||
AMS_ABORT_UNLESS(signaled != nullptr);
|
||||
const size_t n = signaled - holders;
|
||||
AMS_ABORT_UNLESS(n < util::size(sync.system_events));
|
||||
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == true);
|
||||
os::ClearSystemEvent(sync.system_events + n);
|
||||
AMS_ABORT_UNLESS(os::TryWaitSystemEvent(sync.system_events + n) == false);
|
||||
}
|
||||
|
||||
AMS_ABORT_UNLESS(os::TryWaitAny(std::addressof(mw)) == nullptr);
|
||||
|
||||
/* Let writer know we're done. */
|
||||
sync.reader_state = 9;
|
||||
os::SignalEvent(std::addressof(sync.reader_ready_event));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Main() {
|
||||
printf("Doing OS Event tests!\n");
|
||||
{
|
||||
/* Create the synchronization state. */
|
||||
InterThreadSync sync_state;
|
||||
sync_state.reader_state = 0;
|
||||
sync_state.writer_state = 0;
|
||||
os::InitializeEvent(std::addressof(sync_state.writer_ready_event), false, os::EventClearMode_AutoClear);
|
||||
os::InitializeEvent(std::addressof(sync_state.reader_ready_event), false, os::EventClearMode_AutoClear);
|
||||
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(sync_state.system_event_as_manual_clear_event), os::EventClearMode_ManualClear, false));
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(sync_state.system_event_as_manual_clear_interprocess_event), os::EventClearMode_ManualClear, true));
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(sync_state.system_event_as_auto_clear_event), os::EventClearMode_AutoClear, false));
|
||||
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(sync_state.system_event_as_auto_clear_interprocess_event), os::EventClearMode_AutoClear, true));
|
||||
|
||||
/* Ensure we clean up the sync-state when done. */
|
||||
ON_SCOPE_EXIT {
|
||||
os::FinalizeEvent(std::addressof(sync_state.writer_ready_event));
|
||||
os::FinalizeEvent(std::addressof(sync_state.reader_ready_event));
|
||||
|
||||
os::DestroySystemEvent(std::addressof(sync_state.system_event_as_manual_clear_event));
|
||||
os::DestroySystemEvent(std::addressof(sync_state.system_event_as_manual_clear_interprocess_event));
|
||||
os::DestroySystemEvent(std::addressof(sync_state.system_event_as_auto_clear_event));
|
||||
os::DestroySystemEvent(std::addressof(sync_state.system_event_as_auto_clear_interprocess_event));
|
||||
};
|
||||
|
||||
/* Create the threads. */
|
||||
os::ThreadType reader_thread, writer_thread;
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(reader_thread), TestReaderThread, std::addressof(sync_state), g_reader_thread_stack, sizeof(g_reader_thread_stack), os::DefaultThreadPriority));
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(writer_thread), TestWriterThread, std::addressof(sync_state), g_writer_thread_stack, sizeof(g_writer_thread_stack), os::DefaultThreadPriority));
|
||||
os::SetThreadNamePointer(std::addressof(reader_thread), "ReaderThread");
|
||||
os::SetThreadNamePointer(std::addressof(writer_thread), "WriterThread");
|
||||
|
||||
/* Start the threads. */
|
||||
os::StartThread(std::addressof(reader_thread));
|
||||
os::StartThread(std::addressof(writer_thread));
|
||||
|
||||
/* Wait for the threads to complete. */
|
||||
os::WaitThread(std::addressof(reader_thread));
|
||||
os::WaitThread(std::addressof(writer_thread));
|
||||
|
||||
/* Destroy the threads. */
|
||||
os::WaitThread(std::addressof(reader_thread));
|
||||
os::WaitThread(std::addressof(writer_thread));
|
||||
}
|
||||
printf("All tests completed!\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common stratosphere sysmodule configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||
|
||||
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
|
||||
export BOARD_TARGET_SUFFIX := .kip
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
|
||||
export BOARD_TARGET_SUFFIX := .exe
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_linux)
|
||||
export BOARD_TARGET_SUFFIX :=
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_macos)
|
||||
export BOARD_TARGET_SUFFIX :=
|
||||
else
|
||||
export BOARD_TARGET_SUFFIX := $(TARGET)
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(__RECURSIVE__),1)
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
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) \
|
||||
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||
|
||||
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: clean all check_lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
|
||||
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
|
||||
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
|
||||
-f $(THIS_MAKEFILE)
|
||||
|
||||
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
|
||||
@$(SILENTCMD)echo "Checked library."
|
||||
|
||||
check_lib:
|
||||
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
|
||||
|
||||
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(BOARD_TARGET) $(TARGET).elf
|
||||
@for i in $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR); do [ -d $$i ] && rmdir $$i 2>/dev/null || true; done
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT)$(BOARD_TARGET_SUFFIX)
|
||||
|
||||
%.kip : %.elf
|
||||
|
||||
%.nsp : %.nso %.npdm
|
||||
|
||||
%.nso: %.elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT).elf: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $(OUTPUT).lst)
|
||||
|
||||
$(OUTPUT).exe: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
|
||||
ifeq ($(strip $(BOARD_TARGET_SUFFIX)),)
|
||||
$(OUTPUT): $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $@.lst)
|
||||
endif
|
||||
|
||||
%.npdm : %.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,51 +0,0 @@
|
||||
ATMOSPHERE_BUILD_CONFIGS :=
|
||||
all: nx_release
|
||||
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
|
||||
define ATMOSPHERE_ADD_TARGET
|
||||
|
||||
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
|
||||
|
||||
$(strip $1):
|
||||
@echo "Building $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
clean-$(strip $1):
|
||||
@echo "Cleaning $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
endef
|
||||
|
||||
define ATMOSPHERE_ADD_TARGETS
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, $(strip $2)release, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5)" $(strip $6) \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, $(strip $2)debug, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 $(strip $6) \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, $(strip $2)audit, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 $(strip $6) \
|
||||
))
|
||||
|
||||
endef
|
||||
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, , nx-hac-001, arm-cortex-a57,,))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, win_x64, , generic_windows, generic_x64,,))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64, , generic_linux, generic_x64,,))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64_clang, clang_, generic_linux, generic_x64,, ATMOSPHERE_COMPILER_NAME="clang"))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_arm64_clang, clang_, generic_linux, generic_arm64,, ATMOSPHERE_COMPILER_NAME="clang"))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_x64, , generic_macos, generic_x64,,))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_arm64, , generic_macos, generic_arm64,,))
|
||||
|
||||
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
|
||||
|
||||
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))
|
||||
@@ -1,145 +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>
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit u8 g_socket_config_memory[2_MB];
|
||||
|
||||
alignas(os::MemoryPageSize) constinit u8 g_server_thread_stack[16_KB];
|
||||
|
||||
constexpr const u8 TestMessage[0x10] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
|
||||
};
|
||||
|
||||
void TestServerThread(void *arg) {
|
||||
os::EventType *server_ready_event = reinterpret_cast<os::EventType *>(arg);
|
||||
|
||||
s32 listen_fd = socket::Socket(socket::Family::Af_Inet, socket::Type::Sock_Stream, socket::Protocol::IpProto_Ip);
|
||||
AMS_ABORT_UNLESS(listen_fd >= 0);
|
||||
printf("[Server]: Listen fd=%d\n", static_cast<int>(listen_fd));
|
||||
|
||||
socket::SockAddrIn s_addr = {};
|
||||
s_addr.sin_family = socket::Family::Af_Inet;
|
||||
s_addr.sin_addr.s_addr = socket::InAddr_Any;
|
||||
s_addr.sin_port = socket::InetHtons(23337);
|
||||
|
||||
/* Bind. */
|
||||
const auto bind_res = socket::Bind(listen_fd, reinterpret_cast<socket::SockAddr *>(std::addressof(s_addr)), sizeof(s_addr));
|
||||
printf("[Server]: Bind=%d\n", static_cast<int>(bind_res));
|
||||
AMS_ABORT_UNLESS(bind_res == 0);
|
||||
|
||||
/* Listen. */
|
||||
const auto listen_res = socket::Listen(listen_fd, 1);
|
||||
printf("[Server]: Listen=%d\n", static_cast<int>(listen_res));
|
||||
AMS_ABORT_UNLESS(listen_res >= 0);
|
||||
|
||||
printf("[Server]: Ready\n");
|
||||
os::SignalEvent(server_ready_event);
|
||||
|
||||
/* Accept. */
|
||||
s32 conn_fd = socket::Accept(listen_fd, nullptr, nullptr);
|
||||
AMS_ABORT_UNLESS(conn_fd >= 0);
|
||||
printf("[Server]: Conn fd=%d\n", conn_fd);
|
||||
|
||||
/* Receive. */
|
||||
u8 received[sizeof(TestMessage)] = {};
|
||||
AMS_ABORT_UNLESS(socket::Recv(conn_fd, received, sizeof(received), socket::MsgFlag::Msg_None) == sizeof(received));
|
||||
printf("[Server]: Received\n");
|
||||
|
||||
AMS_ABORT_UNLESS(std::memcmp(received, TestMessage, sizeof(TestMessage)) == 0);
|
||||
|
||||
/* Calculate hash. */
|
||||
u8 hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::GenerateSha256(hash, sizeof(hash), received, sizeof(received));
|
||||
|
||||
/* Send hash. */
|
||||
AMS_ABORT_UNLESS(socket::Send(conn_fd, hash, sizeof(hash), socket::MsgFlag::Msg_None) == sizeof(hash));
|
||||
printf("[Server]: Sent\n");
|
||||
|
||||
/* Close sockets. */
|
||||
AMS_ABORT_UNLESS(socket::Close(conn_fd) == 0);
|
||||
AMS_ABORT_UNLESS(socket::Close(listen_fd) == 0);
|
||||
printf("[Server]: Closed\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Main() {
|
||||
auto cfg = socket::SystemConfigDefault(g_socket_config_memory, sizeof(g_socket_config_memory) / 2, sizeof(g_socket_config_memory) / 2);
|
||||
R_ABORT_UNLESS(socket::Initialize(cfg));
|
||||
{
|
||||
/* Set up for the server thread. */
|
||||
os::EventType server_ready_event;
|
||||
os::InitializeEvent(std::addressof(server_ready_event), false, os::EventClearMode_AutoClear);
|
||||
ON_SCOPE_EXIT { os::FinalizeEvent(std::addressof(server_ready_event)); };
|
||||
|
||||
/* Wait for the server thread to be ready */
|
||||
os::ThreadType server_thread;
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(server_thread), TestServerThread, std::addressof(server_ready_event), g_server_thread_stack, sizeof(g_server_thread_stack), os::DefaultThreadPriority));
|
||||
os::SetThreadNamePointer(std::addressof(server_thread), "ServerThread");
|
||||
os::StartThread(std::addressof(server_thread));
|
||||
|
||||
/* Wait for the server thread to be ready. */
|
||||
os::WaitEvent(std::addressof(server_ready_event));
|
||||
|
||||
{
|
||||
/* Create socket. */
|
||||
s32 conn_fd = socket::Socket(socket::Family::Af_Inet, socket::Type::Sock_Stream, socket::Protocol::IpProto_Ip);
|
||||
AMS_ABORT_UNLESS(conn_fd >= 0);
|
||||
printf("[Client]: Conn fd=%d\n", static_cast<int>(conn_fd));
|
||||
|
||||
socket::SockAddrIn s_addr = {};
|
||||
s_addr.sin_family = socket::Family::Af_Inet;
|
||||
s_addr.sin_addr.s_addr = socket::InAddr_Loopback;
|
||||
s_addr.sin_port = socket::InetHtons(23337);
|
||||
|
||||
/* Connect. */
|
||||
const auto connect_res = socket::Connect(conn_fd, reinterpret_cast<socket::SockAddr *>(std::addressof(s_addr)), sizeof(s_addr));
|
||||
printf("[Client]: Connect=%d, last_err=%d\n", connect_res, static_cast<int>(socket::GetLastError()));
|
||||
AMS_ABORT_UNLESS(connect_res == 0);
|
||||
|
||||
/* Send test. */
|
||||
AMS_ABORT_UNLESS(socket::Send(conn_fd, TestMessage, sizeof(TestMessage), socket::MsgFlag::Msg_None) == sizeof(TestMessage));
|
||||
printf("[Client]: Sent\n");
|
||||
|
||||
/* Receive. */
|
||||
u8 received[crypto::Sha256Generator::HashSize] = {};
|
||||
AMS_ABORT_UNLESS(socket::Recv(conn_fd, received, sizeof(received), socket::MsgFlag::Msg_None) == sizeof(received));
|
||||
printf("[Client]: Received\n");
|
||||
|
||||
/* Calculate hash. */
|
||||
u8 hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::GenerateSha256(hash, sizeof(hash), TestMessage, sizeof(TestMessage));
|
||||
|
||||
AMS_ABORT_UNLESS(std::memcmp(received, hash, sizeof(hash)) == 0);
|
||||
|
||||
/* Close sockets. */
|
||||
AMS_ABORT_UNLESS(socket::Close(conn_fd) == 0);
|
||||
printf("[Client]: Closed\n");
|
||||
}
|
||||
|
||||
/* Wait for the server thread to complete. */
|
||||
os::WaitThread(std::addressof(server_thread));
|
||||
}
|
||||
printf("Successfully performed socket test!\n");
|
||||
|
||||
socket::Finalize();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common stratosphere sysmodule configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||
|
||||
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
|
||||
export BOARD_TARGET_SUFFIX := .kip
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
|
||||
export BOARD_TARGET_SUFFIX := .exe
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_linux)
|
||||
export BOARD_TARGET_SUFFIX :=
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_macos)
|
||||
export BOARD_TARGET_SUFFIX :=
|
||||
else
|
||||
export BOARD_TARGET_SUFFIX := $(TARGET)
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(__RECURSIVE__),1)
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
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) \
|
||||
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||
|
||||
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: clean all check_lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
|
||||
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
|
||||
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
|
||||
-f $(THIS_MAKEFILE)
|
||||
|
||||
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
|
||||
@$(SILENTCMD)echo "Checked library."
|
||||
|
||||
check_lib:
|
||||
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
|
||||
|
||||
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(BOARD_TARGET) $(TARGET).elf
|
||||
@for i in $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR); do [ -d $$i ] && rmdir $$i 2>/dev/null || true; done
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT)$(BOARD_TARGET_SUFFIX)
|
||||
|
||||
%.kip : %.elf
|
||||
|
||||
%.nsp : %.nso %.npdm
|
||||
|
||||
%.nso: %.elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT).elf: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $(OUTPUT).lst)
|
||||
|
||||
$(OUTPUT).exe: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
|
||||
ifeq ($(strip $(BOARD_TARGET_SUFFIX)),)
|
||||
$(OUTPUT): $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $@.lst)
|
||||
endif
|
||||
|
||||
%.npdm : %.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,51 +0,0 @@
|
||||
ATMOSPHERE_BUILD_CONFIGS :=
|
||||
all: nx_release
|
||||
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
|
||||
define ATMOSPHERE_ADD_TARGET
|
||||
|
||||
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
|
||||
|
||||
$(strip $1):
|
||||
@echo "Building $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
clean-$(strip $1):
|
||||
@echo "Cleaning $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/unit_test.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
endef
|
||||
|
||||
define ATMOSPHERE_ADD_TARGETS
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, $(strip $2)release, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5)" $(strip $6) \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, $(strip $2)debug, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 $(strip $6) \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, $(strip $2)audit, $(strip $3), $(strip $4), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $5) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 $(strip $6) \
|
||||
))
|
||||
|
||||
endef
|
||||
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, , nx-hac-001, arm-cortex-a57,,))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, win_x64, , generic_windows, generic_x64,,))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64, , generic_linux, generic_x64,,))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_x64_clang, clang_, generic_linux, generic_x64,, ATMOSPHERE_COMPILER_NAME="clang"))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, linux_arm64_clang, clang_, generic_linux, generic_arm64,, ATMOSPHERE_COMPILER_NAME="clang"))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_x64, , generic_macos, generic_x64,,))
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, macos_arm64, , generic_macos, generic_arm64,,))
|
||||
|
||||
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
|
||||
|
||||
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))
|
||||
@@ -1,36 +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>
|
||||
|
||||
namespace ams {
|
||||
|
||||
void Main() {
|
||||
printf("Getting thread stack\n");
|
||||
{
|
||||
uintptr_t stack = 0;
|
||||
size_t stack_size = 0;
|
||||
os::GetCurrentStackInfo(std::addressof(stack), std::addressof(stack_size));
|
||||
|
||||
printf("Got thread stack: %p-%p\n", reinterpret_cast<void *>(stack), reinterpret_cast<void *>(stack + stack_size));
|
||||
|
||||
const uintptr_t stack_var_addr = reinterpret_cast<uintptr_t>(std::addressof(stack));
|
||||
printf("&stack variable address: %p\n", reinterpret_cast<void *>(stack_var_addr));
|
||||
AMS_ASSERT(stack <= stack_var_addr && stack_var_addr < stack + stack_size);
|
||||
}
|
||||
printf("All tests completed!\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common stratosphere sysmodule configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||
|
||||
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
|
||||
export BOARD_TARGET_SUFFIX := .kip
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_windows)
|
||||
export BOARD_TARGET_SUFFIX := .exe
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_linux)
|
||||
export BOARD_TARGET_SUFFIX :=
|
||||
else ifeq ($(ATMOSPHERE_BOARD),generic_macos)
|
||||
export BOARD_TARGET_SUFFIX :=
|
||||
else
|
||||
export BOARD_TARGET_SUFFIX := $(TARGET)
|
||||
endif
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(__RECURSIVE__),1)
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
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) \
|
||||
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||
|
||||
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: clean all check_lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
|
||||
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
|
||||
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
|
||||
-f $(THIS_MAKEFILE)
|
||||
|
||||
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
|
||||
@$(SILENTCMD)echo "Checked library."
|
||||
|
||||
check_lib:
|
||||
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
|
||||
|
||||
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(BOARD_TARGET) $(TARGET).elf
|
||||
@for i in $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR); do [ -d $$i ] && rmdir $$i 2>/dev/null || true; done
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT)$(BOARD_TARGET_SUFFIX)
|
||||
|
||||
%.kip : %.elf
|
||||
|
||||
%.nsp : %.nso %.npdm
|
||||
|
||||
%.nso: %.elf
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(OUTPUT).elf: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $(OUTPUT).lst)
|
||||
|
||||
$(OUTPUT).exe: $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
|
||||
ifeq ($(strip $(BOARD_TARGET_SUFFIX)),)
|
||||
$(OUTPUT): $(OFILES) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@echo linking $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
$(SILENTCMD)$(NM) -CSn $@ > $(notdir $@.lst)
|
||||
endif
|
||||
|
||||
%.npdm : %.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,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