#include "prx/libkernel_sys/include/Api.hpp" #include "prx/libkernel/include/Api.hpp" #include "prx/libc/include/Api.hpp" #include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include #include namespace { } extern "C" { void AioHoldWorkerForTest_nid_no_patch(bool hold); } namespace { constexpr int Ebadf = 9; constexpr int Efault = 14; constexpr int Einval = 22; constexpr int Eagain = 35; constexpr int Einprogress = 36; constexpr int Eopnotsupp = 45; constexpr int Ecanceled = 85; constexpr int ORdonly = 0; constexpr int ORdwr = 2; constexpr int OCreat = 0x200; constexpr int OSync = 0x80; constexpr int ODsync = 0x1000000; constexpr int AioCanceled = 1; constexpr int AioAllDone = 3; void Require(bool condition, std::source_location location = std::source_location::current()) { if (condition) return; std::fprintf(stderr, "check failed at line %u\n", static_cast(location.line())); std::exit(1); } int Error() { return *__error_nid_postfix(); } GuestAiocb Request(int descriptor, void* buffer, std::uint64_t bytes, std::int64_t offset = 0) { GuestAiocb request{}; request.fildes = descriptor; request.buf = buffer; request.nbytes = bytes; request.offset = offset; return request; } void Wait(GuestAiocb& request) { const GuestAiocb* list[] = {&request}; Require(aio_suspend_nid_postfix(list, 1, nullptr) == 0); } void BeforeUse() { GuestAiocb request{}; const GuestAiocb* list[] = {&request}; Require(aio_error_nid_postfix(&request) == Einval); Require(aio_return_nid_postfix(&request) == -1 && Error() == Einval); Require(aio_suspend_nid_postfix(list, 1, nullptr) == -1 && Error() == Eagain); } void Validation(int file, int readOnly) { char buffer[8] = {}; Require(aio_read_nid_postfix(nullptr) == -1 && Error() == Efault); auto bad = Request(-1, buffer, sizeof(buffer)); Require(aio_read_nid_postfix(&bad) == -1 && Error() == Ebadf); Require(bad.status == -1 && bad.error == Ebadf && aio_error_nid_postfix(&bad) == Ebadf); auto readOnlyWrite = Request(readOnly, buffer, sizeof(buffer)); Require(aio_write_nid_postfix(&readOnlyWrite) == -1 && Error() == Ebadf); auto negative = Request(file, buffer, sizeof(buffer), -1); Require(aio_read_nid_postfix(&negative) == -1 && Error() == Einval && negative.error == Einval); auto huge = Request(file, buffer, std::uint64_t{std::numeric_limits::max()} + 1); Require(aio_read_nid_postfix(&huge) == -1 && Error() == Einval && huge.error == 0); auto notify = Request(file, buffer, sizeof(buffer)); notify.sigevent.notify = 2; Require(aio_read_nid_postfix(¬ify) == -1 && Error() == Einval && notify.error == Einval); notify.sigevent = {1, 0, 0, {}}; Require(aio_read_nid_postfix(¬ify) == -1 && Error() == Einval); int pipe[2] = {-1, -1}; Require(pipe_nid_postfix(pipe) == 0); auto piped = Request(pipe[0], buffer, sizeof(buffer)); Require(aio_read_nid_postfix(&piped) == -1 && Error() == Eopnotsupp && piped.error == Eopnotsupp); auto pipeSync = Request(pipe[1], nullptr, 0); Require(aio_fsync_nid_postfix(OSync, &pipeSync) == -1 && Error() == Einval); Require(close_nid_postfix(pipe[0]) == 0 && close_nid_postfix(pipe[1]) == 0); auto sync = Request(file, nullptr, 0); Require(aio_fsync_nid_postfix(0, &sync) == -1 && Error() == Einval); Require(aio_fsync_nid_postfix(ODsync, &sync) == -1 && Error() == Einval); } void ReadAndWrite(int file) { char text[] = "asynchronous"; auto write = Request(file, text, 12, 4); Require(aio_write_nid_postfix(&write) == 0); Wait(write); Require(aio_error_nid_postfix(&write) == 0); Require(aio_return_nid_postfix(&write) == 12 && write.status == 12 && write.error == 0); Require(aio_return_nid_postfix(&write) == -1 && Error() == Einval); Require(aio_error_nid_postfix(&write) == Einval); char read[16] = {}; auto request = Request(file, read, sizeof(read), 6); Require(aio_read_nid_postfix(&request) == 0); Wait(request); Require(aio_return_nid_postfix(&request) == 10 && std::memcmp(read, "ynchronous", 10) == 0); auto empty = Request(file, read, 0, 0); Require(aio_read_nid_postfix(&empty) == 0); Wait(empty); Require(aio_return_nid_postfix(&empty) == 0); const GuestAiocb* none[] = {&request}; Require(aio_suspend_nid_postfix(none, 1, nullptr) == 0); Require(aio_suspend_nid_postfix(none, 0, nullptr) == 0); Require(aio_suspend_nid_postfix(none, -1, nullptr) == -1 && Error() == Einval); Require(aio_suspend_nid_postfix(none, 257, nullptr) == -1 && Error() == Einval); const KernelTimespec badTime{0, 1000000000}; Require(aio_suspend_nid_postfix(none, 1, &badTime) == -1 && Error() == Einval); } void Cancel(int file) { char buffer[4] = {}; AioHoldWorkerForTest_nid_no_patch(true); auto first = Request(file, buffer, sizeof(buffer)); auto second = Request(file, buffer, sizeof(buffer)); Require(aio_read_nid_postfix(&first) == 0 && aio_read_nid_postfix(&second) == 0); Require(first.error == Einprogress && aio_error_nid_postfix(&first) == Einprogress); const GuestAiocb* list[] = {&first}; const KernelTimespec brief{0, 5000000}; Require(aio_suspend_nid_postfix(list, 1, &brief) == -1 && Error() == Eagain); Require(aio_cancel_nid_postfix(file, &first) == AioCanceled); Require(aio_error_nid_postfix(&first) == Ecanceled && aio_error_nid_postfix(&second) == Einprogress); Require(aio_suspend_nid_postfix(list, 1, &brief) == 0); Require(aio_return_nid_postfix(&first) == -1 && Error() == Ecanceled); Require(first.status == -1 && first.error == Ecanceled); Require(aio_return_nid_postfix(&first) == -1 && Error() == Einval); Require(aio_cancel_nid_postfix(file, &first) == AioAllDone); Require(aio_cancel_nid_postfix(file, nullptr) == AioCanceled); Require(aio_return_nid_postfix(&second) == -1 && Error() == Ecanceled); Require(aio_cancel_nid_postfix(file, nullptr) == AioAllDone); Require(aio_cancel_nid_postfix(-1, nullptr) == -1 && Error() == Ebadf); AioHoldWorkerForTest_nid_no_patch(false); } void SyncOrder(int file) { char text[] = "ordered"; AioHoldWorkerForTest_nid_no_patch(true); auto write = Request(file, text, 7, 0); auto sync = Request(file, nullptr, 0); Require(aio_write_nid_postfix(&write) == 0 && aio_fsync_nid_postfix(OSync, &sync) == 0); Require(aio_error_nid_postfix(&sync) == Einprogress); AioHoldWorkerForTest_nid_no_patch(false); GuestAiocb* done = nullptr; Require(aio_waitcomplete_nid_postfix(&done, nullptr) == 7 && done == &write); Require(aio_waitcomplete_nid_postfix(&done, nullptr) == 0 && done == &sync && sync.status == 0); const KernelTimespec zero{0, 0}; Require(aio_waitcomplete_nid_postfix(&done, &zero) == -1 && Error() == Eagain && done == nullptr); const KernelTimespec brief{0, 5000000}; Require(aio_waitcomplete_nid_postfix(&done, &brief) == -1 && Error() == Eagain); const KernelTimespec badTime{0, -1}; Require(aio_waitcomplete_nid_postfix(&done, &badTime) == -1 && Error() == Einval); } } int main() { const auto path = "anyps5-kernel-sys-aio-" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) + ".bin"; BeforeUse(); const int file = open_nid_postfix(path.c_str(), ORdwr | OCreat, 0600); const int readOnly = open_nid_postfix(path.c_str(), ORdonly, 0); Require(file >= 0 && readOnly >= 0); Validation(file, readOnly); ReadAndWrite(file); Cancel(file); SyncOrder(file); Require(close_nid_postfix(readOnly) == 0 && close_nid_postfix(file) == 0); Require(std::filesystem::remove(path)); std::puts("guest_kernel_sys_aio passed"); return 0; }