Merge pull request #3714 from MarcZX100/codex/libkernel-aio-set-param-3708
feat(libkernel): implement sceKernelAioSetParam
This commit is contained in:
commit
b6555c8454
|
|
@ -95,6 +95,14 @@ struct KernelAioRwRequest {
|
|||
std::int32_t fd;
|
||||
};
|
||||
|
||||
struct KernelAioSchedulingParam {
|
||||
std::int32_t schedulingWindowSize;
|
||||
std::int32_t delayedCountLimit;
|
||||
std::uint32_t enableSplit;
|
||||
std::uint32_t splitSize;
|
||||
std::uint32_t splitChunkSize;
|
||||
};
|
||||
|
||||
struct KernelBatchMapEntry {
|
||||
void* start;
|
||||
std::uint64_t offset;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ constexpr std::int32_t AioRequestNumMax = 128;
|
|||
constexpr std::int32_t AioIdNumMax = 128;
|
||||
constexpr std::uint32_t AioWaitAnd = 1;
|
||||
constexpr std::uint32_t AioWaitOr = 2;
|
||||
constexpr std::int32_t AioSchedulingWindowMax = 128;
|
||||
constexpr std::int32_t AioDelayedCountMax = 128;
|
||||
constexpr std::uint32_t AioSplitSizeMax = 0x1000000;
|
||||
constexpr std::uint32_t AioSplitChunkSizeMax = 0x1000000;
|
||||
|
||||
std::mutex g_mutex;
|
||||
std::array<std::int32_t, AioMaxQueues> g_states{};
|
||||
|
|
@ -204,6 +208,20 @@ void APS5_VABI sceKernelAioInitializeParam(void* param) {
|
|||
if (param == nullptr) throw std::invalid_argument("sceKernelAioInitializeParam: param is null");
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelAioSetParam(KernelAioSchedulingParam* param, int32_t schedulingWindowSize, int32_t delayedCountLimit, uint32_t enableSplit, uint32_t splitSize, uint32_t splitChunkSize) {
|
||||
if (param == nullptr) throw std::invalid_argument("sceKernelAioSetParam: param is null");
|
||||
if (schedulingWindowSize < 0 || schedulingWindowSize > AioSchedulingWindowMax) throw std::runtime_error("sceKernelAioSetParam: a scheduling window outside 0..128 is not modelled");
|
||||
if (delayedCountLimit < 0 || delayedCountLimit > AioDelayedCountMax) throw std::runtime_error("sceKernelAioSetParam: a delayed count limit outside 0..128 is not modelled");
|
||||
if (enableSplit > 1) throw std::runtime_error("sceKernelAioSetParam: a split flag other than 0 or 1 is not modelled");
|
||||
if (splitSize > AioSplitSizeMax || splitChunkSize > AioSplitChunkSizeMax) throw std::runtime_error("sceKernelAioSetParam: a split size above 0x1000000 is not modelled");
|
||||
param->schedulingWindowSize = schedulingWindowSize;
|
||||
param->delayedCountLimit = delayedCountLimit;
|
||||
param->enableSplit = enableSplit;
|
||||
param->splitSize = splitSize;
|
||||
param->splitChunkSize = splitChunkSize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelAioSubmitReadCommands(KernelAioRwRequest* req, int32_t size, int32_t prio, int32_t* id) {
|
||||
(void)prio;
|
||||
return SubmitCommands(req, size, false, id);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ std::int64_t APS5_VABI sceKernelLseek(int d, std::int64_t offset, int whence);
|
|||
int APS5_VABI sceKernelAioDeleteRequest(std::int32_t id, std::int32_t* ret);
|
||||
int APS5_VABI sceKernelAioInitializeImpl(void* param, std::int32_t size);
|
||||
void APS5_VABI sceKernelAioInitializeParam(void* param);
|
||||
int APS5_VABI sceKernelAioSetParam(KernelAioSchedulingParam* param, std::int32_t schedulingWindowSize, std::int32_t delayedCountLimit, std::uint32_t enableSplit, std::uint32_t splitSize, std::uint32_t splitChunkSize);
|
||||
int APS5_VABI sceKernelAioSubmitReadCommands(KernelAioRwRequest* req, std::int32_t size, std::int32_t prio, std::int32_t* id);
|
||||
int APS5_VABI sceKernelAioSubmitWriteCommands(KernelAioRwRequest* req, std::int32_t size, std::int32_t prio, std::int32_t* id);
|
||||
int APS5_VABI sceKernelAioPollRequest(std::int32_t id, std::int32_t* state);
|
||||
|
|
@ -48,7 +49,35 @@ int main() {
|
|||
const auto file = root / "data.bin";
|
||||
const std::string seed = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
{ std::ofstream stream(file, std::ios::binary); stream << seed; }
|
||||
Check(sceKernelAioInitializeImpl(nullptr, 0) == 0);
|
||||
static_assert(sizeof(KernelAioSchedulingParam) == 20);
|
||||
std::array<KernelAioSchedulingParam, 3> schedulingParams{};
|
||||
schedulingParams.fill({7, 7, 7, 7, 7});
|
||||
Check(sceKernelAioSetParam(&schedulingParams[1], 64, 16, 1, 0x200000, 0x40000) == 0);
|
||||
Check(schedulingParams[1].schedulingWindowSize == 64 && schedulingParams[1].delayedCountLimit == 16);
|
||||
Check(schedulingParams[1].enableSplit == 1 && schedulingParams[1].splitSize == 0x200000 && schedulingParams[1].splitChunkSize == 0x40000);
|
||||
Check(schedulingParams[0].schedulingWindowSize == 7 && schedulingParams[0].splitChunkSize == 7);
|
||||
Check(schedulingParams[2].schedulingWindowSize == 7 && schedulingParams[2].splitChunkSize == 7);
|
||||
Check(sceKernelAioSetParam(&schedulingParams[2], 128, 128, 0, 0x1000000, 0x1000000) == 0);
|
||||
Check(schedulingParams[2].schedulingWindowSize == 128 && schedulingParams[2].delayedCountLimit == 128);
|
||||
Check(schedulingParams[2].enableSplit == 0 && schedulingParams[2].splitSize == 0x1000000 && schedulingParams[2].splitChunkSize == 0x1000000);
|
||||
const auto setParamThrows = [&](std::int32_t window, std::int32_t delayed, std::uint32_t split, std::uint32_t size, std::uint32_t chunk) {
|
||||
KernelAioSchedulingParam untouchedParam{7, 7, 7, 7, 7};
|
||||
bool thrown = false;
|
||||
try { sceKernelAioSetParam(&untouchedParam, window, delayed, split, size, chunk); } catch (const std::exception&) { thrown = true; }
|
||||
return thrown && untouchedParam.schedulingWindowSize == 7 && untouchedParam.delayedCountLimit == 7 &&
|
||||
untouchedParam.enableSplit == 7 && untouchedParam.splitSize == 7 && untouchedParam.splitChunkSize == 7;
|
||||
};
|
||||
Check(setParamThrows(129, 32, 1, 0x100000, 0x100000));
|
||||
Check(setParamThrows(-1, 32, 1, 0x100000, 0x100000));
|
||||
Check(setParamThrows(32, 129, 1, 0x100000, 0x100000));
|
||||
Check(setParamThrows(32, -1, 1, 0x100000, 0x100000));
|
||||
Check(setParamThrows(32, 32, 2, 0x100000, 0x100000));
|
||||
Check(setParamThrows(32, 32, 1, 0x1000001, 0x100000));
|
||||
Check(setParamThrows(32, 32, 1, 0x100000, 0x1000001));
|
||||
bool nullThrew = false;
|
||||
try { sceKernelAioSetParam(nullptr, 32, 32, 1, 0x100000, 0x100000); } catch (const std::invalid_argument&) { nullThrew = true; }
|
||||
Check(nullThrew);
|
||||
Check(sceKernelAioInitializeImpl(schedulingParams.data(), static_cast<std::int32_t>(sizeof(schedulingParams))) == 0);
|
||||
int initParam = 0;
|
||||
sceKernelAioInitializeParam(&initParam);
|
||||
const int fd = sceKernelOpen(file.string().c_str(), SCE_KERNEL_O_RDONLY, 0);
|
||||
|
|
|
|||
|
|
@ -408,6 +408,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [Runtime-loaded modules](../../core/relinker/relinker/src/guest/GuestModuleBuilder.cpp) (`--module-dir`) that another module needs are initialized at startup with no start arguments, before the module that needs them is loaded; the console is assumed to start them when that module is loaded.
|
||||
- [sceKernelPread, sceKernelPwrite, sceKernelPreadv, sceKernelPwritev](../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) on Windows transfer at the given offset with an overlapped `ReadFile`/`WriteFile` and restore the descriptor's file pointer afterwards, so a concurrent `read`/`write` on the same descriptor can see the moved pointer in between, unlike on the console. Handles that are not disk files return `ESPIPE`. `readv` on a pipe or console handle stops after the first iovec read, a short read the console is assumed to allow; each iovec is transferred separately, so a failure after the first transfer returns the bytes already done
|
||||
- [sceKernelAioSubmitReadCommands, sceKernelAioSubmitWriteCommands and their Multiple variants](../../core/libs/prx/libkernel/Aio/src/Aio.cpp) (libkernel) on Windows transfer through the same positioned path as `sceKernelPread`/`sceKernelPwrite` and restore the descriptor's file pointer afterwards, so a concurrent `read`/`write` on the same descriptor can see the moved pointer in between, unlike on the console
|
||||
- [sceKernelAioSetParam](../../core/libs/prx/libkernel/Aio/src/Aio.cpp) (libkernel) writes the five values into the 20-byte scheduling struct the first argument points to, following the declarations in [Orbital](https://github.com/AlexAltea/orbital/blob/cc408367da6a6ebbf5ac1ac559fefeadc73b2831/tools/ida/db_types.json), [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Kernel/KernelAio.cs) and [ampr_emu](https://github.com/drakmor/ampr_emu/blob/cfa85df379f6eeeb165d7badf9b648e266fe77b7/ps5/include/sce/_fs.h). Whether the console checks the values is unknown, so a null struct, a scheduling window or delayed count limit outside 0..128, a split flag other than 0 or 1 and a split size or chunk size above 0x1000000 (SharpProspero's maximums) throw. The scheduling settings are not used: `sceKernelAioInitializeImpl` ignores the struct and requests run synchronously on submit
|
||||
- [sceKernelGetFsSandboxRandomWord](../../core/libs/prx/libkernel/System/src/Sandbox.cpp) (libkernel) returns a fixed directory name, `gkbHtXc3Wq`. On the console the name is the process's randomized path (syscall 602, `randomized_path`), and the sandbox exposes system files under it, such as `common/lib` and `common/cert`. Nothing is mounted under the name here, so `/gkbHtXc3Wq/common/...` resolves under the guest root like any other path and does not exist
|
||||
- [sceKernelSync](../../core/libs/prx/libc/src/General.cpp) (libkernel) flushes only the host files and directories recorded through `RecordWrittenPath_nid_no_patch` (the libc, libkernel, save data and app content file functions) and their changed parent directories. A host write in a library that does not record its path is not flushed by `sceKernelSync`
|
||||
- [sceKernelSync](../../core/libs/prx/libc/src/General.cpp) (libkernel) on Windows skips a recorded file it cannot open for writing (read-only, or held open by a handle that denies write sharing), since `FlushFileBuffers` needs a writable handle. On Linux, it skips a recorded file it cannot open for reading. Such a file's data may not reach the disk before a host crash; the guest reads the same data either way
|
||||
|
|
|
|||
Loading…
Reference in New Issue