Files
AnyPS5/core/libs/tests/GuestKernelSysAio.cpp
T

203 lines
7.8 KiB
C++

#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 <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <limits>
#include <source_location>
#include <string>
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<unsigned>(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<int>::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(&notify) == -1 && Error() == Einval && notify.error == Einval);
notify.sigevent = {1, 0, 0, {}};
Require(aio_read_nid_postfix(&notify) == -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;
}