Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions core/libs/prx/libkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1066,13 +1066,15 @@ if(BUILD_TESTING)
set_tests_properties(guest_select_wait PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
if(NOT WIN32)
add_test_executable(guest_select_tests ${LIBS_INCLUDE_DIR}/tests/GuestSelect.cpp)
target_include_directories(guest_select_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_select_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_select_tests)
add_test(NAME guest_select COMMAND guest_select_tests)
set_tests_properties(guest_select PROPERTIES TIMEOUT 20)
add_test_executable(guest_select_tests ${LIBS_INCLUDE_DIR}/tests/GuestSelect.cpp)
target_include_directories(guest_select_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_select_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_select_tests)
add_test(NAME guest_select COMMAND guest_select_tests)
set_tests_properties(guest_select PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_select PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_tcp_tests ${LIBS_INCLUDE_DIR}/tests/GuestTcp.cpp)
target_include_directories(guest_tcp_tests PRIVATE ${LIBS_INCLUDE_DIR})
Expand Down
3 changes: 3 additions & 0 deletions core/libs/prx/libkernel/Socket/include/SocketRuntime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ std::int64_t Write(int descriptor, const void* buffer, std::size_t length);
int Stat(int descriptor, FileStat* status);
bool Ready(int descriptor, bool write, std::int64_t* data, bool* eof);
int Poll(KernelSocketPoll::Entry* entries, int count, int timeoutMilliseconds);
#ifdef _WIN32
bool PipeReady(int descriptor, short events, short& revents);
#endif
struct Interest {
int descriptor;
bool write;
Expand Down
33 changes: 25 additions & 8 deletions core/libs/prx/libkernel/Socket/src/Socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/PreciseWait.hpp"
#include "prx/libkernel/File/include/File.hpp"
#include "prx/libkernel/File/include/RandomDevice.hpp"
#include "prx/libkernel/Socket/include/SocketPoll.hpp"
#include "prx/libkernel/Socket/include/SocketSelectDeadline.hpp"
Expand All @@ -21,6 +22,7 @@

namespace {

constexpr int GuestEbadf = 9;
constexpr int GuestEinval = 22;
constexpr int SelectDescriptorLimit = 1024;
constexpr int SelectPollSliceMilliseconds = 10;
Expand All @@ -38,8 +40,8 @@ bool DescriptorSet(const void* set, int descriptor) {
return set != nullptr && ((static_cast<const std::uint64_t*>(set)[descriptor / 64] >> (descriptor % 64)) & 1u) != 0;
}

void UnknownDescriptor(int descriptor, bool polled) {
const auto what = "select on descriptor " + std::to_string(descriptor) + (polled ? " (not a libSceNet socket)" : " (libSceNet is not loaded)");
void UnknownDescriptor(int descriptor) {
const auto what = "select on descriptor " + std::to_string(descriptor) + " (not a libSceNet socket)";
NotImplemented_nid_no_patch(what.c_str());
}

Expand Down Expand Up @@ -91,6 +93,25 @@ int HostPoll(KernelSocketPoll::Entry* descriptors, int count, int timeoutMillise
return result;
}

#else

int HostPoll(KernelSocketPoll::Entry* descriptors, int count, int timeoutMilliseconds) {
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMilliseconds);
for (;;) {
int ready = 0;
for (int index = 0; index < count; ++index) {
auto& entry = descriptors[index];
if (!DescriptorIsOpen_nid_no_patch(entry.descriptor)) return -GuestEbadf;
if (!GuestSockets::PipeReady(entry.descriptor, entry.events, entry.revents)) {
NotImplemented_nid_no_patch(("select on descriptor " + std::to_string(entry.descriptor) + " (neither a socket nor a pipe)").c_str());
}
if (entry.revents != 0) ++ready;
}
if (ready > 0 || std::chrono::steady_clock::now() >= deadline) return ready;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}

#endif

}
Expand Down Expand Up @@ -165,10 +186,7 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
std::vector<PollGroup> groups;
if (!kernel.entries.empty()) groups.push_back(std::move(kernel));
if (!net.entries.empty()) {
PollGroup hosts{nullptr, {}, {}};
#ifndef _WIN32
hosts.poll = &HostPoll;
#endif
PollGroup hosts{&HostPoll, {}, {}};
net.poll = g_socketPoller.load();
if (net.poll != nullptr) {
for (auto& socket : net.entries) socket.revents = 0;
Expand All @@ -184,7 +202,6 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
continue;
}
const int descriptor = net.entries[index].descriptor;
if (hosts.poll == nullptr) UnknownDescriptor(descriptor, net.poll != nullptr);
hosts.entries.push_back({descriptor, net.entries[index].events, 0});
hosts.slots.push_back(net.slots[index]);
net.entries.erase(net.entries.begin() + static_cast<std::ptrdiff_t>(index));
Expand Down Expand Up @@ -226,7 +243,7 @@ int APS5_VABI select_nid_postfix(int nfds, void* readfds, void* writefds, void*
}
int ready = 0;
for (const auto& entry : requested) {
if ((entry.revents & KernelSocketPoll::Unknown) != 0) UnknownDescriptor(entry.descriptor, true);
if ((entry.revents & KernelSocketPoll::Unknown) != 0) UnknownDescriptor(entry.descriptor);
const bool readable = (entry.events & KernelSocketPoll::Readable) != 0 && (entry.revents & (KernelSocketPoll::Readable | KernelSocketPoll::HangUp | KernelSocketPoll::Error)) != 0;
const bool writable = (entry.events & KernelSocketPoll::Writable) != 0 && (entry.revents & (KernelSocketPoll::Writable | KernelSocketPoll::Error)) != 0;
const bool urgent = (entry.events & KernelSocketPoll::Urgent) != 0 && (entry.revents & KernelSocketPoll::Urgent) != 0;
Expand Down
6 changes: 6 additions & 0 deletions core/libs/prx/libkernel/Socket/src/Udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,12 @@ bool IsPipe(int descriptor, short events) {
#endif
}

#ifdef _WIN32
bool GuestSockets::PipeReady(int descriptor, short events, short& revents) {
return PipeReadiness(descriptor, events, revents);
}
#endif

int GuestSockets::Duplicate(int descriptor) {
std::lock_guard lock(socketsMutex);
const auto found = sockets.find(descriptor);
Expand Down
4 changes: 2 additions & 2 deletions docs/dev/debt/libkernel.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
- [Map hints inside a reservation](../../../core/libs/prx/libkernel/DirectMemory/DirectMemory.cpp) (libkernel) - `sceKernelMapDirectMemory` and the flexible and anonymous maps place a mapping without `MAP_FIXED` at its hint address when the whole range lies in a range still reserved by `sceKernelReserveVirtualRange`, as if `MAP_FIXED` were set. Taken from PPSA01474, which reserves 64 GiB and then maps its heaps inside it with hints and no `MAP_FIXED`, and uses the hint addresses afterwards; not measured on the console. Hints outside a reservation, or over a range that is partly mapped, stay hints
- [_sceKernelRtldThreadAtexitIncrement, _sceKernelRtldThreadAtexitDecrement](../../../core/libs/prx/libkernel/System/src/Rtld.cpp) (libkernel) - the argument is the `dso` address a title's `libc.prx` passes to `__cxa_thread_atexit` (PPSA14632), which fails when Increment returns nonzero. As in the [fpPS4](https://github.com/red-prig/fpPS4) reimplementation, Increment takes a reference on the loaded image containing the address and Decrement releases one; the variants without the leading underscore are assumed to match
- [tcgetattr, tcsetattr](../../../core/libs/prx/libkernel/File/src/Terminal.cpp) (libkernel) - every file and pipe descriptor is taken as not a terminal (`ENOTTY`), and a socket gets `EADDRNOTAVAIL` for `AF_INET` and `EOPNOTSUPP` otherwise, as FreeBSD's `in_control` and `pru_control_notsupp` answer a termios ioctl; what descriptors 0 to 2 of a title are on the console, and whether any of them is a terminal there, is unknown
- [select](../../../core/libs/prx/libkernel/Socket/src/Socket.cpp) (libkernel) - descriptor 0 is never reported readable and descriptors 1 and 2 are always writable, as for a console title with no input; reads from descriptor 0 still go to the host stdin. `/dev/random` and `/dev/urandom` are always readable and writable, as FreeBSD reports a seeded random device. Guest sockets and libSceNet sockets are polled; other descriptors throw on Windows and are polled through the host on Linux. Dungeons (PPSA16064) selects on descriptor 0
- [select](../../../core/libs/prx/libkernel/Socket/src/Socket.cpp) (libkernel) - a descriptor the socket poller reports as unknown that is below `GuestSockets::FirstDescriptor` is polled on the host instead of throwing, except on Windows where it still throws; an invalid host descriptor fails with `EBADF`, and mixed host and socket sets are polled in short alternating slices so neither side waits out the full slice alone. Wobbly Life (PPSA29413) selects on file descriptors while streaming its bundles
- [select](../../../core/libs/prx/libkernel/Socket/src/Socket.cpp) (libkernel) - descriptor 0 is never reported readable and descriptors 1 and 2 are always writable, as for a console title with no input; reads from descriptor 0 still go to the host stdin. `/dev/random` and `/dev/urandom` are always readable and writable, as FreeBSD reports a seeded random device. Guest sockets and libSceNet sockets are polled; other descriptors are polled through the host on Linux, while on Windows pipes are checked every millisecond with the readiness test `poll` uses and any other descriptor throws. Dungeons (PPSA16064) selects on descriptor 0, and RetroArch (PPSA99169) selects on its web server's wake-up pipe next to its listening socket
- [select](../../../core/libs/prx/libkernel/Socket/src/Socket.cpp) (libkernel) - a descriptor the socket poller reports as unknown that is below `GuestSockets::FirstDescriptor` is polled on the host instead of throwing (on Windows only a pipe, as above); an invalid host descriptor fails with `EBADF`, and mixed host and socket sets are polled in short alternating slices so neither side waits out the full slice alone. Wobbly Life (PPSA29413) selects on file descriptors while streaming its bundles
- [__progname](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - unknown data export
- [environ](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - data export pointing to a fixed empty environment so titles that import it load; it is not the environment libc's `getenv` reads, so it lists none of those variables and `setenv`/`putenv` changes do not appear in it
- [seteuid, setegid, setuid, setreuid, setregid, setgroups, getgroups](../../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the guest process is assumed to run as root, with real, effective and saved user and group ids 0. As in FreeBSD's `sys_seteuid`, `sys_setegid`, `sys_setuid`, `sys_setreuid` and `sys_setregid`, id 0 (or -1 for an unchanged `setreuid`/`setregid` id) is then accepted without a privilege check and changes no credential, so 0 is returned; any other id would change an id, which is not modelled, and throws. `setgroups` keeps the supplementary group list as `kern_setgroups` does (`NGROUPS_MAX` 1023, an empty list leaves the effective group as the only entry, a first entry other than 0 would change the effective group and throws) and `getgroups` reports it. The ids and privileges a title has on a console are unverified
Expand Down
Loading