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

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

View File

@@ -1,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.

View File

@@ -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))

View File

@@ -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");
}
}

View File

@@ -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
#---------------------------------------------------------------------------------------

View File

@@ -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))

View File

@@ -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");
}
}

View File

@@ -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
#---------------------------------------------------------------------------------------

View File

@@ -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))

View File

@@ -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();
}
}

View File

@@ -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
#---------------------------------------------------------------------------------------

View File

@@ -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))

View File

@@ -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");
}
}

View File

@@ -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
#---------------------------------------------------------------------------------------

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,107 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#define DOCTEST_CONFIG_IMPLEMENT
#include "util_test_framework.hpp"
namespace ams {
namespace {
constexpr size_t MallocBufferSize = 16_MB;
alignas(os::MemoryPageSize) constinit u8 g_malloc_buffer[MallocBufferSize];
}
namespace hos {
bool IsUnitTestProgramForSetVersion() { return true; }
}
namespace init {
void InitializeSystemModuleBeforeConstructors() {
/* Catch has global-ctors which allocate, so we need to do this earlier than normal. */
init::InitializeAllocator(g_malloc_buffer, sizeof(g_malloc_buffer));
}
void InitializeSystemModule() { /* ... */ }
void FinalizeSystemModule() { /* ... */ }
void Startup() { /* ... */ }
}
void NORETURN Exit(int rc) {
AMS_UNUSED(rc);
AMS_ABORT("Exit called by immortal process");
}
void Main() {
/* Ensure our thread priority and core mask is correct. */
{
auto * const cur_thread = os::GetCurrentThread();
os::SetThreadCoreMask(cur_thread, 3, (1ul << 3));
os::ChangeThreadPriority(cur_thread, 0);
}
/* Run tests. */
{
doctest::Context ctx;
ctx.applyCommandLine(os::GetHostArgc(), os::GetHostArgv());
ctx.run();
}
AMS_INFINITE_LOOP();
/* This can never be reached. */
AMS_ASSUME(false);
}
}
namespace doctest {
namespace {
class OutputDebugStringStream : public std::stringbuf {
public:
OutputDebugStringStream() = default;
~OutputDebugStringStream() { pubsync(); }
int sync() override {
const auto message = str();
return R_SUCCEEDED(ams::svc::OutputDebugString(message.c_str(), message.length())) ? 0 : -1;
}
};
}
std::ostream& get_cout() {
static std::ostream ret(new OutputDebugStringStream);
return ret;
}
std::ostream& get_cerr() {
return get_cout();
}
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "util_common.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
namespace {
constinit volatile bool g_spinloop;
void TestPreemptionPriorityThreadFunction(volatile bool *executed) {
/* While we should, note that we're executing. */
while (g_spinloop) {
__asm__ __volatile__("" ::: "memory");
*executed = true;
__asm__ __volatile__("" ::: "memory");
}
/* Exit the thread. */
svc::ExitThread();
}
}
DOCTEST_TEST_CASE( "The scheduler is preemptive at the preemptive priority and cooperative for all other priorities" ) {
/* Create heap. */
ScopedHeap heap(3 * os::MemoryPageSize);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_None)));
ON_SCOPE_EXIT {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_ReadWrite)));
};
const uintptr_t sp_0 = heap.GetAddress() + 1 * os::MemoryPageSize;
const uintptr_t sp_1 = heap.GetAddress() + 3 * os::MemoryPageSize;
for (s32 core = 0; core < NumCores; ++core) {
for (s32 priority = HighestTestPriority; priority <= LowestTestPriority; ++priority) {
svc::Handle thread_handles[2];
volatile bool thread_executed[2] = { false, false };
/* Start spinlooping. */
g_spinloop = true;
/* Create threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestPreemptionPriorityThreadFunction), reinterpret_cast<uintptr_t>(thread_executed + 0), sp_0, priority, core)));
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestPreemptionPriorityThreadFunction), reinterpret_cast<uintptr_t>(thread_executed + 1), sp_1, priority, core)));
/* Start threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
/* Wait long enough that we can be confident the threads have been balanced. */
svc::SleepThread(PreemptionTimeSpan.GetNanoSeconds() * 10);
/* Check that we're in a coherent state. */
if (IsPreemptionPriority(core, priority)) {
DOCTEST_CHECK((thread_executed[0] & thread_executed[1]));
} else {
DOCTEST_CHECK((thread_executed[0] ^ thread_executed[1]));
}
/* Stop spinlooping. */
g_spinloop = false;
/* Wait for threads to exit. */
s32 dummy;
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), thread_handles + 0, 1, -1)));
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), thread_handles + 1, 1, -1)));
/* Close thread handles. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
}
}
}
}

View File

@@ -1,133 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "util_common.hpp"
#include "util_check_memory.hpp"
namespace ams::test {
namespace {
size_t GetPhysicalMemorySizeMax() {
u64 v;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(v), svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
const svc::Handle resource_limit = v;
ON_SCOPE_EXIT { svc::CloseHandle(resource_limit); };
s64 size;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(size), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
return static_cast<size_t>(size);
}
size_t GetPhysicalMemorySizeAvailable() {
u64 v;
R_ABORT_UNLESS(svc::GetInfo(std::addressof(v), svc::InfoType_ResourceLimit, svc::InvalidHandle, 0));
const svc::Handle resource_limit = v;
ON_SCOPE_EXIT { svc::CloseHandle(resource_limit); };
s64 total;
R_ABORT_UNLESS(svc::GetResourceLimitLimitValue(std::addressof(total), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
s64 current;
R_ABORT_UNLESS(svc::GetResourceLimitCurrentValue(std::addressof(current), resource_limit, svc::LimitableResource_PhysicalMemoryMax));
return static_cast<size_t>(total - current);
}
}
DOCTEST_TEST_CASE("svc::SetHeapSize") {
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
uintptr_t dummy;
/* Reset the heap. */
uintptr_t addr;
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
/* Ensure that we don't leak memory. */
const size_t initial_memory = GetPhysicalMemorySizeAvailable();
ON_SCOPE_EXIT { DOCTEST_CHECK(initial_memory == GetPhysicalMemorySizeAvailable()); };
DOCTEST_SUBCASE("Unaligned and too big sizes fail") {
for (size_t i = 1; i < svc::HeapSizeAlignment; i = util::AlignUp(i + 1, os::MemoryPageSize)){
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), i)));
}
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetHeapSize(std::addressof(dummy), 64_GB)));
}
DOCTEST_SUBCASE("Larger size than address space fails") {
DOCTEST_CHECK(svc::ResultOutOfMemory::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(svc::AddressMemoryRegionHeap39Size + 1, svc::HeapSizeAlignment))));
}
DOCTEST_SUBCASE("Bounded by resource limit") {
DOCTEST_CHECK(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeMax() + 1, svc::HeapSizeAlignment))));
DOCTEST_CHECK(svc::ResultLimitReached::Includes(svc::SetHeapSize(std::addressof(dummy), util::AlignUp(GetPhysicalMemorySizeAvailable() + 1, svc::HeapSizeAlignment))));
}
DOCTEST_SUBCASE("SetHeapSize gives heap memory") {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
DOCTEST_SUBCASE("SetHeapSize cannot remove read-only heap") {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_Read)));
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_Read, 0);
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetHeapSize(std::addressof(dummy), 0)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(addr, svc::HeapSizeAlignment, svc::MemoryPermission_ReadWrite)));
TestMemory(addr, svc::HeapSizeAlignment, svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
DOCTEST_SUBCASE("Heap memory does not survive unmap/re-map") {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
u8 * const heap = reinterpret_cast<u8 *>(addr);
std::memset(heap, 0xAA, svc::HeapSizeAlignment);
std::memset(heap + svc::HeapSizeAlignment, 0xBB, svc::HeapSizeAlignment);
DOCTEST_CHECK(heap[svc::HeapSizeAlignment] == 0xBB);
DOCTEST_CHECK(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), svc::HeapSizeAlignment)));
DOCTEST_CHECK(heap[0] == 0xAA);
DOCTEST_CHECK(std::memcmp(heap, heap + 1, svc::HeapSizeAlignment - 1) == 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 2 * svc::HeapSizeAlignment)));
DOCTEST_CHECK(heap[svc::HeapSizeAlignment] == 0x00);
DOCTEST_CHECK(std::memcmp(heap + svc::HeapSizeAlignment, heap + svc::HeapSizeAlignment + 1, svc::HeapSizeAlignment - 1) == 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetHeapSize(std::addressof(addr), 0)));
}
}
}

View File

@@ -1,156 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "util_common.hpp"
#include "util_check_memory.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
namespace {
bool CanSetMemoryPermission(u8 state) {
return state == svc::MemoryState_CodeData || state == svc::MemoryState_AliasCodeData || state == svc::MemoryState_Normal;
}
}
alignas(os::MemoryPageSize) constinit u8 g_memory_permission_buffer[2 * os::MemoryPageSize];
DOCTEST_TEST_CASE("svc::SetMemoryPermission invalid arguments") {
const uintptr_t buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
for (size_t i = 1; i < os::MemoryPageSize; ++i) {
DOCTEST_CHECK(svc::ResultInvalidAddress::Includes(svc::SetMemoryPermission(buffer + i, os::MemoryPageSize, svc::MemoryPermission_Read)));
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize + i, svc::MemoryPermission_Read)));
}
DOCTEST_CHECK(svc::ResultInvalidSize::Includes(svc::SetMemoryPermission(buffer, 0, svc::MemoryPermission_Read)));
{
const u64 vmem_end = util::AlignDown(std::numeric_limits<u64>::max(), os::MemoryPageSize);
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(vmem_end, 2 * os::MemoryPageSize, svc::MemoryPermission_Read)));
}
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(svc::AddressMap39End, os::MemoryPageSize, svc::MemoryPermission_Read)));
for (size_t i = 0; i < 0x100; ++i) {
const auto perm = static_cast<svc::MemoryPermission>(i);
if (perm == svc::MemoryPermission_None || perm == svc::MemoryPermission_Read || perm == svc::MemoryPermission_ReadWrite) {
continue;
}
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, perm)));
}
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_ReadExecute)));
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_Write)));
DOCTEST_CHECK(svc::ResultInvalidNewMemoryPermission::Includes(svc::SetMemoryPermission(buffer, os::MemoryPageSize, svc::MemoryPermission_DontCare)));
}
DOCTEST_TEST_CASE("svc::SetMemoryPermission works on specific states") {
/* Check that we have CodeData. */
const uintptr_t bss_buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
/* Create a heap. */
ScopedHeap scoped_heap(2 * svc::HeapSizeAlignment);
TestMemory(scoped_heap.GetAddress(), scoped_heap.GetSize(), svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
/* TODO: Ensure we have alias code data? */
uintptr_t addr = 0;
while (true) {
/* Get current mapping. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), addr)));
/* Try to set permission. */
if (CanSetMemoryPermission(mem_info.state) && mem_info.attribute == 0) {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, svc::MemoryPermission_ReadWrite)));
TestMemory(mem_info.base_address, mem_info.size, mem_info.state, svc::MemoryPermission_ReadWrite, mem_info.attribute);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, mem_info.permission)));
} else {
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(mem_info.base_address, mem_info.size, svc::MemoryPermission_Read)));
}
const uintptr_t next_address = mem_info.base_address + mem_info.size;
if (next_address <= addr) {
break;
}
addr = next_address;
}
}
DOCTEST_TEST_CASE("svc::SetMemoryPermission allows for free movement between RW-, R--, ---") {
/* Define helper. */
auto test_set_memory_permission = [](uintptr_t address, size_t size){
/* Get the permission. */
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
const svc::MemoryPermission legal_states[] = { svc::MemoryPermission_None, svc::MemoryPermission_Read, svc::MemoryPermission_ReadWrite };
for (const auto src_state : legal_states) {
for (const auto dst_state : legal_states) {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, svc::MemoryPermission_None)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, src_state)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, dst_state)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, svc::MemoryPermission_None)));
}
}
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(address, size, mem_info.permission)));
};
/* Test that we can freely move about .bss buffers. */
test_set_memory_permission(reinterpret_cast<uintptr_t>(g_memory_permission_buffer), sizeof(g_memory_permission_buffer));
/* Create a heap. */
ScopedHeap scoped_heap(svc::HeapSizeAlignment);
TestMemory(scoped_heap.GetAddress(), scoped_heap.GetSize(), svc::MemoryState_Normal, svc::MemoryPermission_ReadWrite, 0);
/* Test that we can freely move about heap. */
test_set_memory_permission(scoped_heap.GetAddress(), scoped_heap.GetSize());
/* TODO: AliasCodeData */
}
DOCTEST_TEST_CASE("svc::SetMemoryPermission fails when the memory has non-zero attribute") {
const uintptr_t bss_buffer = reinterpret_cast<uintptr_t>(g_memory_permission_buffer);
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryAttribute(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryAttribute_Uncached, svc::MemoryAttribute_Uncached)));
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, svc::MemoryAttribute_Uncached);
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
DOCTEST_CHECK(svc::ResultInvalidCurrentMemory::Includes(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryAttribute(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryAttribute_Uncached, 0)));
TestMemory(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryState_CodeData, svc::MemoryPermission_ReadWrite, 0);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_None)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_Read)));
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(bss_buffer, sizeof(g_memory_permission_buffer), svc::MemoryPermission_ReadWrite)));
}
}

View File

@@ -1,253 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "util_common.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
namespace {
constinit svc::Handle g_read_handles[3] = { svc::InvalidHandle, svc::InvalidHandle, svc::InvalidHandle };
constinit svc::Handle g_write_handles[3] = { svc::InvalidHandle, svc::InvalidHandle, svc::InvalidHandle };
constinit s64 g_thread_wait_ns;
constinit bool g_should_switch_threads;
constinit bool g_switched_threads;
constinit bool g_correct_switch_threads;
void WaitSynchronization(svc::Handle handle) {
s32 dummy;
R_ABORT_UNLESS(svc::WaitSynchronization(std::addressof(dummy), std::addressof(handle), 1, -1));
}
void TestYieldHigherOrSamePriorityThread() {
/* Wait to run. */
WaitSynchronization(g_read_handles[0]);
/* Reset our event. */
R_ABORT_UNLESS(svc::ClearEvent(g_read_handles[0]));
/* Signal the other thread's event. */
R_ABORT_UNLESS(svc::SignalEvent(g_write_handles[1]));
/* Wait, potentially yielding to the lower/same priority thread. */
g_switched_threads = false;
svc::SleepThread(g_thread_wait_ns);
/* Check whether we switched correctly. */
g_correct_switch_threads = g_should_switch_threads == g_switched_threads;
/* Exit. */
svc::ExitThread();
}
void TestYieldLowerOrSamePriorityThread() {
/* Signal thread the higher/same priority thread to run. */
R_ABORT_UNLESS(svc::SignalEvent(g_write_handles[0]));
/* Wait to run. */
WaitSynchronization(g_read_handles[1]);
/* Reset our event. */
R_ABORT_UNLESS(svc::ClearEvent(g_read_handles[1]));
/* We've switched to the lower/same priority thread. */
g_switched_threads = true;
/* Wait to be instructed to exit. */
WaitSynchronization(g_read_handles[2]);
/* Reset the exit signal. */
R_ABORT_UNLESS(svc::ClearEvent(g_read_handles[2]));
/* Exit. */
svc::ExitThread();
}
void TestYieldSamePriority(uintptr_t sp_higher, uintptr_t sp_lower) {
/* Test each core. */
for (s32 core = 0; core < NumCores; ++core) {
for (s32 priority = HighestTestPriority; priority <= LowestTestPriority && !IsPreemptionPriority(core, priority); ++priority) {
svc::Handle thread_handles[2];
/* Create threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestYieldHigherOrSamePriorityThread), 0, sp_higher, priority, core)));
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestYieldLowerOrSamePriorityThread), 0, sp_lower, priority, core)));
/* Start threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
/* Wait for higher priority thread. */
WaitSynchronization(thread_handles[0]);
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
/* Signal the lower priority thread to exit. */
DOCTEST_CHECK(R_SUCCEEDED(svc::SignalEvent(g_write_handles[2])));
/* Wait for the lower priority thread. */
WaitSynchronization(thread_handles[1]);
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
/* Check that the switch was correct. */
DOCTEST_CHECK(g_correct_switch_threads);
}
}
}
void TestYieldDifferentPriority(uintptr_t sp_higher, uintptr_t sp_lower) {
/* Test each core. */
for (s32 core = 0; core < NumCores; ++core) {
for (s32 priority = HighestTestPriority; priority < LowestTestPriority && !IsPreemptionPriority(core, priority); ++priority) {
svc::Handle thread_handles[2];
/* Create threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 0, reinterpret_cast<uintptr_t>(&TestYieldHigherOrSamePriorityThread), 0, sp_higher, priority, core)));
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(thread_handles + 1, reinterpret_cast<uintptr_t>(&TestYieldLowerOrSamePriorityThread), 0, sp_lower, priority + 1, core)));
/* Start threads. */
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[1])));
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handles[0])));
/* Wait for higher priority thread. */
WaitSynchronization(thread_handles[0]);
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[0])));
/* Signal the lower priority thread to exit. */
DOCTEST_CHECK(R_SUCCEEDED(svc::SignalEvent(g_write_handles[2])));
/* Wait for the lower priority thread. */
WaitSynchronization(thread_handles[1]);
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handles[1])));
/* Check that the switch was correct. */
DOCTEST_CHECK(g_correct_switch_threads);
}
}
}
}
DOCTEST_TEST_CASE( "svc::SleepThread: Thread sleeps for time specified" ) {
for (s64 ns = 1; ns < TimeSpan::FromSeconds(1).GetNanoSeconds(); ns *= 2) {
const auto start = os::GetSystemTickOrdered();
svc::SleepThread(ns);
const auto end = os::GetSystemTickOrdered();
const s64 taken_ns = (end - start).ToTimeSpan().GetNanoSeconds();
DOCTEST_CHECK( taken_ns >= ns );
}
}
DOCTEST_TEST_CASE( "svc::SleepThread: Yield is behaviorally correct" ) {
/* Create events. */
for (size_t i = 0; i < util::size(g_write_handles); ++i) {
g_read_handles[i] = svc::InvalidHandle;
g_write_handles[i] = svc::InvalidHandle;
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateEvent(g_write_handles + i, g_read_handles + i)));
}
ON_SCOPE_EXIT {
for (size_t i = 0; i < util::size(g_write_handles); ++i) {
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(g_read_handles[i])));
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(g_write_handles[i])));
g_read_handles[i] = svc::InvalidHandle;
g_write_handles[i] = svc::InvalidHandle;
}
};
/* Create heap. */
ScopedHeap heap(3 * os::MemoryPageSize);
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_None)));
ON_SCOPE_EXIT {
DOCTEST_CHECK(R_SUCCEEDED(svc::SetMemoryPermission(heap.GetAddress() + os::MemoryPageSize, os::MemoryPageSize, svc::MemoryPermission_ReadWrite)));
};
const uintptr_t sp_higher = heap.GetAddress() + 1 * os::MemoryPageSize;
const uintptr_t sp_lower = heap.GetAddress() + 3 * os::MemoryPageSize;
DOCTEST_SUBCASE("svc::SleepThread: Yields do not switch to a thread of lower priority.") {
/* Test yield without migration. */
{
/* Configure for yield test. */
g_should_switch_threads = false;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_WithoutCoreMigration);
TestYieldDifferentPriority(sp_higher, sp_lower);
}
/* Test yield with migration. */
{
/* Configure for yield test. */
g_should_switch_threads = false;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_WithoutCoreMigration);
TestYieldDifferentPriority(sp_higher, sp_lower);
}
}
DOCTEST_SUBCASE("svc::SleepThread: ToAnyThread switches to a thread of same or lower priority.") {
/* Test to same priority. */
{
/* Configure for yield test. */
g_should_switch_threads = true;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_ToAnyThread);
TestYieldSamePriority(sp_higher, sp_lower);
}
/* Test to lower priority. */
{
/* Configure for yield test. */
g_should_switch_threads = true;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_ToAnyThread);
TestYieldDifferentPriority(sp_higher, sp_lower);
}
}
DOCTEST_SUBCASE("svc::SleepThread: Yield switches to another thread of same priority.") {
/* Test yield without migration. */
{
/* Configure for yield test. */
g_should_switch_threads = true;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_WithoutCoreMigration);
TestYieldSamePriority(sp_higher, sp_lower);
}
/* Test yield with migration. */
{
/* Configure for yield test. */
g_should_switch_threads = true;
g_thread_wait_ns = static_cast<s64>(svc::YieldType_WithCoreMigration);
TestYieldSamePriority(sp_higher, sp_lower);
}
}
DOCTEST_SUBCASE("svc::SleepThread: Yield with bogus timeout does not switch to another thread same priority") {
/* Configure for yield test. */
g_should_switch_threads = false;
g_thread_wait_ns = INT64_C(-5);
TestYieldSamePriority(sp_higher, sp_lower);
}
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ams::test::TestThreadCreateRegistersOnFunctionEntry(void *ctx) */
.section .text._ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv, "ax", %progbits
.global _ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv
.type _ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv, %function
_ZN3ams4test40TestThreadCreateRegistersOnFunctionEntryEPv:
/* Save all registers to our context. */
stp x0, x1, [x0, #0x00]
stp x2, x3, [x0, #0x10]
stp x4, x5, [x0, #0x20]
stp x6, x7, [x0, #0x30]
stp x8, x9, [x0, #0x40]
stp x10, x11, [x0, #0x50]
stp x12, x13, [x0, #0x60]
stp x14, x15, [x0, #0x70]
stp x16, x17, [x0, #0x80]
stp x18, x19, [x0, #0x90]
stp x20, x21, [x0, #0xA0]
stp x22, x23, [x0, #0xB0]
stp x24, x25, [x0, #0xC0]
stp x26, x27, [x0, #0xD0]
stp x28, x29, [x0, #0xE0]
mov x1, sp
stp x30, x1, [x0, #0xF0]
/* Exit the thread. */
svc 0xa

View File

@@ -1,65 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "util_common.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
void TestThreadCreateRegistersOnFunctionEntry(void *ctx);
DOCTEST_TEST_CASE( "Creating a thread results in fixed register contents." ) {
/* Create heap. */
ScopedHeap heap(os::MemoryPageSize);
/* Create register buffer. */
u64 thread_registers[32];
std::memset(thread_registers, 0xCC, sizeof(thread_registers));
/* Create thread. */
svc::Handle thread_handle;
DOCTEST_CHECK(R_SUCCEEDED(svc::CreateThread(std::addressof(thread_handle), reinterpret_cast<uintptr_t>(&TestThreadCreateRegistersOnFunctionEntry), reinterpret_cast<uintptr_t>(thread_registers), heap.GetAddress() + os::MemoryPageSize, HighestTestPriority, NumCores - 1)));
/* Start thread. */
DOCTEST_CHECK(R_SUCCEEDED(svc::StartThread(thread_handle)));
/* Wait for thread to exit. */
s32 dummy;
DOCTEST_CHECK(R_SUCCEEDED(svc::WaitSynchronization(std::addressof(dummy), std::addressof(thread_handle), 1, -1)));
/* Close thread handle. */
DOCTEST_CHECK(R_SUCCEEDED(svc::CloseHandle(thread_handle)));
/* Check thread initial registers. */
for (size_t i = 0; i < util::size(thread_registers); ++i) {
if (i == 0) {
/* X0 is argument. */
DOCTEST_CHECK(thread_registers[i] == reinterpret_cast<uintptr_t>(thread_registers));
} else if (i == 18) {
/* X18 is an odd cfi value. */
DOCTEST_CHECK(thread_registers[i] != 0);
DOCTEST_CHECK((thread_registers[i] & 0x1) != 0);
} else if (i == 31) {
/* SP is user-provided sp. */
DOCTEST_CHECK(thread_registers[i] == (heap.GetAddress() + os::MemoryPageSize));
} else {
/* All other registers are zero. */
DOCTEST_CHECK(thread_registers[i] == 0);
}
}
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "util_common.hpp"
#include "util_scoped_heap.hpp"
namespace ams::test {
DOCTEST_TEST_CASE( "Setting a thread's disable count will cause it to become pinned." ) {
DoWithThreadPinning([]() {
__asm__ __volatile__("" ::: "memory");
});
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "util_test_framework.hpp"
namespace ams::test {
inline void TestMemory(uintptr_t address, svc::MemoryState state, svc::MemoryPermission perm, u32 attr) {
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
DOCTEST_CHECK(mem_info.base_address <= address);
DOCTEST_CHECK(address < (mem_info.base_address + mem_info.size));
DOCTEST_CHECK(mem_info.state == state);
DOCTEST_CHECK(mem_info.permission == perm);
DOCTEST_CHECK(mem_info.attribute == attr);
}
inline void TestMemory(uintptr_t address, size_t size, svc::MemoryState state, svc::MemoryPermission perm, u32 attr) {
svc::MemoryInfo mem_info;
svc::PageInfo page_info;
DOCTEST_CHECK(R_SUCCEEDED(svc::QueryMemory(std::addressof(mem_info), std::addressof(page_info), address)));
DOCTEST_CHECK(mem_info.base_address <= address);
DOCTEST_CHECK(mem_info.base_address < (address + size));
DOCTEST_CHECK((address + size) <= (mem_info.base_address + mem_info.size));
DOCTEST_CHECK(mem_info.state == state);
DOCTEST_CHECK(mem_info.permission == perm);
DOCTEST_CHECK(mem_info.attribute == attr);
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "util_test_framework.hpp"
namespace ams::test {
static constexpr s32 NumCores = 4;
static constexpr s32 DpcManagerNormalThreadPriority = 59;
static constexpr s32 DpcManagerPreemptionThreadPriority = 63;
static constexpr s32 HighestTestPriority = 32;
static constexpr s32 LowestTestPriority = svc::LowestThreadPriority;
static_assert(HighestTestPriority < LowestTestPriority);
static constexpr TimeSpan PreemptionTimeSpan = TimeSpan::FromMilliSeconds(10);
constexpr inline bool IsPreemptionPriority(s32 core, s32 priority) {
return priority == ((core == (NumCores - 1)) ? DpcManagerPreemptionThreadPriority : DpcManagerNormalThreadPriority);
}
template<typename F>
void DoWithThreadPinning(F f) {
/* Get the thread local region. */
auto * const tlr = svc::GetThreadLocalRegion();
/* Require that we're not currently pinned. */
DOCTEST_CHECK((tlr->disable_count == 0));
DOCTEST_CHECK((!tlr->interrupt_flag));
/* Request to pin ourselves. */
tlr->disable_count = 1;
/* Wait long enough that we can be confident preemption will occur, and therefore our interrupt flag will be set. */
{
constexpr auto MinimumTicksToGuaranteeInterruptFlag = ::ams::svc::Tick(PreemptionTimeSpan) + ::ams::svc::Tick(PreemptionTimeSpan) + 2;
auto GetSystemTickForPinnedThread = []() ALWAYS_INLINE_LAMBDA -> ::ams::svc::Tick {
s64 v;
__asm__ __volatile__ ("mrs %x[v], cntpct_el0" : [v]"=r"(v) :: "memory");
return ::ams::svc::Tick(v);
};
const auto start_tick = GetSystemTickForPinnedThread();
while (true) {
if (tlr->interrupt_flag) {
break;
}
if (const auto cur_tick = GetSystemTickForPinnedThread(); (cur_tick - start_tick) > MinimumTicksToGuaranteeInterruptFlag) {
break;
}
}
}
/* We're pinned. Execute the user callback. */
bool callback_succeeded = true;
{
if constexpr (requires { { f() } -> std::convertible_to<bool>; }) {
callback_succeeded = f();
} else {
f();
}
}
/* Clear our disable count. */
tlr->disable_count = 0;
/* Get our interrupt flag. */
const auto interrupt_flag_while_pinned = tlr->interrupt_flag;
/* Unpin ourselves. */
if (interrupt_flag_while_pinned) {
svc::SynchronizePreemptionState();
}
/* Get our interrupt flag. */
const auto interrupt_flag_after_unpin = tlr->interrupt_flag;
/* We have access to all SVCs again. Check that our pinning happened as expected. */
DOCTEST_CHECK(interrupt_flag_while_pinned);
DOCTEST_CHECK(!interrupt_flag_after_unpin);
/* Check that our callback succeeded. */
DOCTEST_CHECK(callback_succeeded);
}
}

View File

@@ -1,48 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "util_test_framework.hpp"
namespace ams::test {
class ScopedHeap {
NON_COPYABLE(ScopedHeap);
NON_MOVEABLE(ScopedHeap);
private:
uintptr_t m_address;
size_t m_size;
public:
explicit ScopedHeap(size_t size) {
this->SetHeapSize(size);
}
~ScopedHeap() {
const auto result = svc::SetHeapSize(std::addressof(m_address), 0);
DOCTEST_CHECK(R_SUCCEEDED(result));
}
void SetHeapSize(size_t size) {
m_size = util::AlignUp(size, svc::HeapSizeAlignment);
const auto result = svc::SetHeapSize(std::addressof(m_address), m_size);
DOCTEST_CHECK(R_SUCCEEDED(result));
}
uintptr_t GetAddress() const { return m_address; }
size_t GetSize() const { return m_size; }
};
}

View File

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