citra-emu
/
citra
Archived
1
0
Fork 0

SOC_U: Preliminary implementation of sockets.

Stubbed CreateMemoryBlock

Using Berkeley sockets, and Winsock2.2 on Windows.
So far ftpony creates the socket and accepts incoming connections

SOC_U: Renamed functions to maintain consistency

Also prevents possible scope errors / conflicts with the actual Berkeley socket functions

SOCU: Close all the opened sockets when cleaning up SOCU
This commit is contained in:
Subv 2014-12-08 23:52:27 -05:00
parent 59bba04628
commit 97a7381d29
8 changed files with 726 additions and 25 deletions

View File

@ -24,10 +24,10 @@ endif()
if (APPLE) if (APPLE)
target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY}) target_link_libraries(citra iconv ${COREFOUNDATION_LIBRARY})
elseif (WIN32) elseif (WIN32)
target_link_libraries(citra winmm) target_link_libraries(citra winmm wsock32 ws2_32)
if (MINGW) # GCC does not support codecvt, so use iconv instead if (MINGW) # GCC does not support codecvt, so use iconv instead
target_link_libraries(citra iconv) target_link_libraries(citra iconv)
endif() endif()
else() # Unix else() # Unix
target_link_libraries(citra rt) target_link_libraries(citra rt)
endif() endif()

View File

@ -68,7 +68,7 @@ endif()
if (APPLE) if (APPLE)
target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY}) target_link_libraries(citra-qt iconv ${COREFOUNDATION_LIBRARY})
elseif (WIN32) elseif (WIN32)
target_link_libraries(citra-qt winmm) target_link_libraries(citra-qt winmm wsock32 ws2_32)
else() # Unix else() # Unix
target_link_libraries(citra-qt rt) target_link_libraries(citra-qt rt)
endif() endif()

View File

@ -39,6 +39,7 @@ static std::shared_ptr<Logger> global_logger;
SUB(Service, CFG) \ SUB(Service, CFG) \
SUB(Service, DSP) \ SUB(Service, DSP) \
SUB(Service, HID) \ SUB(Service, HID) \
SUB(Service, SOC) \
CLS(HW) \ CLS(HW) \
SUB(HW, Memory) \ SUB(HW, Memory) \
SUB(HW, GPU) \ SUB(HW, GPU) \

View File

@ -59,6 +59,7 @@ enum class Class : ClassType {
Service_CFG, ///< The CFG (Configuration) service Service_CFG, ///< The CFG (Configuration) service
Service_DSP, ///< The DSP (DSP control) service Service_DSP, ///< The DSP (DSP control) service
Service_HID, ///< The HID (User input) service Service_HID, ///< The HID (User input) service
Service_SOC, ///< The SOC (Socket) service
HW, ///< Low-level hardware emulation HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation HW_Memory, ///< Memory-map and address translation
HW_GPU, ///< GPU control emulation HW_GPU, ///< GPU control emulation

View File

@ -128,6 +128,13 @@ template<s32 func(s32*, u32, s32)> void Wrap() {
FuncReturn(retval); FuncReturn(retval);
} }
template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4));
Core::g_app_core->SetReg(1, param_1);
FuncReturn(retval);
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Function wrappers that return type u32 // Function wrappers that return type u32

View File

@ -2,40 +2,712 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/platform.h"
#if EMU_PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <poll.h>
#endif
#include "common/log.h" #include "common/log.h"
#include "common/scope_exit.h"
#include "core/hle/hle.h" #include "core/hle/hle.h"
#include "core/hle/service/soc_u.h" #include "core/hle/service/soc_u.h"
#include <unordered_map>
#if EMU_PLATFORM == PLATFORM_WINDOWS
# define WSAEAGAIN WSAEWOULDBLOCK
# define WSAEMULTIHOP -1 // Invalid dummy value
# define ERRNO(x) WSA##x
# define GET_ERRNO WSAGetLastError()
# define poll(x, y, z) WSAPoll(x, y, z);
#else
# define ERRNO(x) x
# define GET_ERRNO errno
# define closesocket(x) close(x)
#endif
static const s32 SOCKET_ERROR_VALUE = -1;
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Namespace SOC_U // Namespace SOC_U
namespace SOC_U { namespace SOC_U {
/// Holds the translation from system network errors to 3DS network errors
static const std::unordered_map<int, int> error_map = { {
{ E2BIG, 1 },
{ ERRNO(EACCES), 2 },
{ ERRNO(EADDRINUSE), 3 },
{ ERRNO(EADDRNOTAVAIL), 4 },
{ ERRNO(EAFNOSUPPORT), 5 },
{ ERRNO(EAGAIN), 6 },
{ ERRNO(EALREADY), 7 },
{ ERRNO(EBADF), 8 },
{ EBADMSG, 9 },
{ EBUSY, 10 },
{ ECANCELED, 11 },
{ ECHILD, 12 },
{ ERRNO(ECONNABORTED), 13 },
{ ERRNO(ECONNREFUSED), 14 },
{ ERRNO(ECONNRESET), 15 },
{ EDEADLK, 16 },
{ ERRNO(EDESTADDRREQ), 17 },
{ EDOM, 18 },
{ ERRNO(EDQUOT), 19 },
{ EEXIST, 20 },
{ ERRNO(EFAULT), 21 },
{ EFBIG, 22 },
{ ERRNO(EHOSTUNREACH), 23 },
{ EIDRM, 24 },
{ EILSEQ, 25 },
{ ERRNO(EINPROGRESS), 26 },
{ ERRNO(EINTR), 27 },
{ ERRNO(EINVAL), 28 },
{ EIO, 29 },
{ ERRNO(EISCONN), 30 },
{ EISDIR, 31 },
{ ERRNO(ELOOP), 32 },
{ ERRNO(EMFILE), 33 },
{ EMLINK, 34 },
{ ERRNO(EMSGSIZE), 35 },
{ ERRNO(EMULTIHOP), 36 },
{ ERRNO(ENAMETOOLONG), 37 },
{ ERRNO(ENETDOWN), 38 },
{ ERRNO(ENETRESET), 39 },
{ ERRNO(ENETUNREACH), 40 },
{ ENFILE, 41 },
{ ERRNO(ENOBUFS), 42 },
{ ENODATA, 43 },
{ ENODEV, 44 },
{ ENOENT, 45 },
{ ENOEXEC, 46 },
{ ENOLCK, 47 },
{ ENOLINK, 48 },
{ ENOMEM, 49 },
{ ENOMSG, 50 },
{ ERRNO(ENOPROTOOPT), 51 },
{ ENOSPC, 52 },
{ ENOSR, 53 },
{ ENOSTR, 54 },
{ ENOSYS, 55 },
{ ERRNO(ENOTCONN), 56 },
{ ENOTDIR, 57 },
{ ERRNO(ENOTEMPTY), 58 },
{ ERRNO(ENOTSOCK), 59 },
{ ENOTSUP, 60 },
{ ENOTTY, 61 },
{ ENXIO, 62 },
{ ERRNO(EOPNOTSUPP), 63 },
{ EOVERFLOW, 64 },
{ EPERM, 65 },
{ EPIPE, 66 },
{ EPROTO, 67 },
{ ERRNO(EPROTONOSUPPORT), 68 },
{ ERRNO(EPROTOTYPE), 69 },
{ ERANGE, 70 },
{ EROFS, 71 },
{ ESPIPE, 72 },
{ ESRCH, 73 },
{ ERRNO(ESTALE), 74 },
{ ETIME, 75 },
{ ERRNO(ETIMEDOUT), 76 }
}};
/// Converts a network error from platform-specific to 3ds-specific
static int TranslateError(int error) {
auto found = error_map.find(error);
if (found != error_map.end())
return -found->second;
return error;
}
/// Holds information about a particular socket
struct SocketHolder {
u32 socket_fd; ///< The socket descriptor
bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows.
};
/// Structure to represent the 3ds' pollfd structure, which is different than most implementations
struct CTRPollFD {
u32 fd; ///< Socket handle
union Events {
u32 hex; ///< The complete value formed by the flags
BitField<0, 1, u32> pollin;
BitField<1, 1, u32> pollpri;
BitField<2, 1, u32> pollhup;
BitField<3, 1, u32> pollerr;
BitField<4, 1, u32> pollout;
BitField<5, 1, u32> pollnval;
Events& operator=(const Events& other) {
hex = other.hex;
return *this;
}
/// Translates the resulting events of a Poll operation from platform-specific to 3ds specific
static Events TranslateTo3DS(u32 input_event) {
Events ev = {};
if (input_event & POLLIN)
ev.pollin = 1;
if (input_event & POLLPRI)
ev.pollpri = 1;
if (input_event & POLLHUP)
ev.pollhup = 1;
if (input_event & POLLERR)
ev.pollerr = 1;
if (input_event & POLLOUT)
ev.pollout = 1;
if (input_event & POLLNVAL)
ev.pollnval = 1;
return ev;
}
/// Translates the resulting events of a Poll operation from 3ds specific to platform specific
static u32 TranslateToPlatform(Events input_event) {
u32 ret = 0;
if (input_event.pollin)
ret |= POLLIN;
if (input_event.pollpri)
ret |= POLLPRI;
if (input_event.pollhup)
ret |= POLLHUP;
if (input_event.pollerr)
ret |= POLLERR;
if (input_event.pollout)
ret |= POLLOUT;
if (input_event.pollnval)
ret |= POLLNVAL;
return ret;
}
};
Events events; ///< Events to poll for (input)
Events revents; ///< Events received (output)
/// Converts a platform-specific pollfd to a 3ds specific structure
static CTRPollFD FromPlatform(pollfd const& fd) {
CTRPollFD result;
result.events.hex = Events::TranslateTo3DS(fd.events).hex;
result.revents.hex = Events::TranslateTo3DS(fd.revents).hex;
result.fd = static_cast<u32>(fd.fd);
return result;
}
/// Converts a 3ds specific pollfd to a platform-specific structure
static pollfd ToPlatform(CTRPollFD const& fd) {
pollfd result;
result.events = Events::TranslateToPlatform(fd.events);
result.revents = Events::TranslateToPlatform(fd.revents);
result.fd = fd.fd;
return result;
}
};
/// Union to represent the 3ds' sockaddr structure
union CTRSockAddr {
/// Structure to represent a raw sockaddr
struct {
u8 len; ///< The length of the entire structure, only the set fields count
u8 sa_family; ///< The address family of the sockaddr
u8 sa_data[0x1A]; ///< The extra data, this varies, depending on the address family
} raw;
/// Structure to represent the 3ds' sockaddr_in structure
struct CTRSockAddrIn {
u8 len; ///< The length of the entire structure
u8 sin_family; ///< The address family of the sockaddr_in
u16 sin_port; ///< The port associated with this sockaddr_in
u32 sin_addr; ///< The actual address of the sockaddr_in
} in;
/// Convert a 3DS CTRSockAddr to a platform-specific sockaddr
static sockaddr ToPlatform(CTRSockAddr const& ctr_addr) {
sockaddr result;
result.sa_family = ctr_addr.raw.sa_family;
memset(result.sa_data, 0, sizeof(result.sa_data));
// We can not guarantee ABI compatibility between platforms so we copy the fields manually
switch (result.sa_family) {
case AF_INET:
{
sockaddr_in* result_in = reinterpret_cast<sockaddr_in*>(&result);
result_in->sin_port = ctr_addr.in.sin_port;
result_in->sin_addr.s_addr = ctr_addr.in.sin_addr;
memset(result_in->sin_zero, 0, sizeof(result_in->sin_zero));
break;
}
default:
_dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
break;
}
return result;
}
/// Convert a platform-specific sockaddr to a 3DS CTRSockAddr
static CTRSockAddr FromPlatform(sockaddr const& addr) {
CTRSockAddr result;
result.raw.sa_family = static_cast<u8>(addr.sa_family);
// We can not guarantee ABI compatibility between platforms so we copy the fields manually
switch (result.raw.sa_family) {
case AF_INET:
{
sockaddr_in const* addr_in = reinterpret_cast<sockaddr_in const*>(&addr);
result.raw.len = sizeof(CTRSockAddrIn);
result.in.sin_port = addr_in->sin_port;
result.in.sin_addr = addr_in->sin_addr.s_addr;
break;
}
default:
_dbg_assert_msg_(Service_SOC, false, "Unhandled address family (sa_family) in CTRSockAddr::ToPlatform");
break;
}
return result;
}
};
/// Holds info about the currently open sockets
static std::unordered_map<u32, SocketHolder> open_sockets;
/// Close all open sockets
static void CleanupSockets() {
for (auto sock : open_sockets)
closesocket(sock.second.socket_fd);
open_sockets.clear();
}
static void Socket(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 domain = cmd_buffer[1]; // Address family
u32 type = cmd_buffer[2];
u32 protocol = cmd_buffer[3];
// Only 0 is allowed according to 3dbrew, using 0 will let the OS decide which protocol to use
if (protocol != 0) {
cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
return;
}
if (domain != AF_INET) {
cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
return;
}
if (type != SOCK_DGRAM && type != SOCK_STREAM) {
cmd_buffer[1] = UnimplementedFunction(ErrorModule::SOC).raw; // TODO(Subv): Correct error code
return;
}
u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol));
if (socket_handle != SOCKET_ERROR_VALUE)
open_sockets[socket_handle] = { socket_handle, true };
int result = 0;
if (socket_handle == SOCKET_ERROR_VALUE)
result = TranslateError(GET_ERRNO);
cmd_buffer[1] = result;
cmd_buffer[2] = socket_handle;
}
static void Bind(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
CTRSockAddr* ctr_sock_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
if (ctr_sock_addr == nullptr) {
cmd_buffer[1] = -1; // TODO(Subv): Correct code
return;
}
sockaddr sock_addr = CTRSockAddr::ToPlatform(*ctr_sock_addr);
int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
int result = 0;
if (res != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = res;
cmd_buffer[1] = result;
}
static void Fcntl(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 ctr_cmd = cmd_buffer[2];
u32 ctr_arg = cmd_buffer[3];
int result = 0;
u32 posix_ret = 0; // TODO: Check what hardware returns for F_SETFL (unspecified by POSIX)
SCOPE_EXIT({
cmd_buffer[1] = result;
cmd_buffer[2] = posix_ret;
});
if (ctr_cmd == 3) { // F_GETFL
#if EMU_PLATFORM == PLATFORM_WINDOWS
posix_ret = 0;
auto iter = open_sockets.find(socket_handle);
if (iter != open_sockets.end() && iter->second.blocking == false)
posix_ret |= 4; // O_NONBLOCK
#else
int ret = ::fcntl(socket_handle, F_GETFL, 0);
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
posix_ret = -1;
return;
}
posix_ret = 0;
if (ret & O_NONBLOCK)
posix_ret |= 4; // O_NONBLOCK
#endif
} else if (ctr_cmd == 4) { // F_SETFL
#if EMU_PLATFORM == PLATFORM_WINDOWS
unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
int ret = ioctlsocket(socket_handle, FIONBIO, &tmp);
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
posix_ret = -1;
return;
}
auto iter = open_sockets.find(socket_handle);
if (iter != open_sockets.end())
iter->second.blocking = (tmp == 0);
#else
int flags = ::fcntl(socket_handle, F_GETFL, 0);
if (flags == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
posix_ret = -1;
return;
}
flags &= ~O_NONBLOCK;
if (ctr_arg & 4) // O_NONBLOCK
flags |= O_NONBLOCK;
int ret = ::fcntl(socket_handle, F_SETFL, flags);
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
posix_ret = -1;
return;
}
#endif
} else {
LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call");
result = TranslateError(EINVAL); // TODO: Find the correct error
posix_ret = -1;
return;
}
}
static void Listen(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 backlog = cmd_buffer[2];
int ret = ::listen(socket_handle, backlog);
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void Accept(Service::Interface* self) {
// TODO(Subv): Calling this function on a blocking socket will block the emu thread,
// preventing graceful shutdown when closing the emulator, this can be fixed by always
// performing nonblocking operations and spinlock until the data is available
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
socklen_t max_addr_len = static_cast<socklen_t>(cmd_buffer[2]);
sockaddr addr;
socklen_t addr_len = sizeof(addr);
u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len));
if (ret != SOCKET_ERROR_VALUE)
open_sockets[ret] = { ret, true };
int result = 0;
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
} else {
CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr);
Memory::WriteBlock(cmd_buffer[0x104 >> 2], (const u8*)&ctr_addr, max_addr_len);
}
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void GetHostId(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
char name[128];
gethostname(name, sizeof(name));
hostent* host = gethostbyname(name);
in_addr* addr = reinterpret_cast<in_addr*>(host->h_addr);
cmd_buffer[2] = addr->s_addr;
cmd_buffer[1] = 0;
}
static void Close(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
int ret = 0;
open_sockets.erase(socket_handle);
ret = closesocket(socket_handle);
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void SendTo(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
u32 flags = cmd_buffer[3];
u32 addr_len = cmd_buffer[4];
u8* input_buff = Memory::GetPointer(cmd_buffer[8]);
CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[10]));
if (ctr_dest_addr == nullptr) {
cmd_buffer[1] = -1; // TODO(Subv): Find the right error code
return;
}
int ret = -1;
if (addr_len > 0) {
sockaddr dest_addr = CTRSockAddr::ToPlatform(*ctr_dest_addr);
ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, &dest_addr, sizeof(dest_addr));
} else {
ret = ::sendto(socket_handle, (const char*)input_buff, len, flags, nullptr, 0);
}
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void RecvFrom(Service::Interface* self) {
// TODO(Subv): Calling this function on a blocking socket will block the emu thread,
// preventing graceful shutdown when closing the emulator, this can be fixed by always
// performing nonblocking operations and spinlock until the data is available
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
u32 len = cmd_buffer[2];
u32 flags = cmd_buffer[3];
socklen_t addr_len = static_cast<socklen_t>(cmd_buffer[4]);
u8* output_buff = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
sockaddr src_addr;
socklen_t src_addr_len = sizeof(src_addr);
int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
if (cmd_buffer[0x1A0 >> 2] != 0) {
CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x1A0 >> 2]));
*ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
}
int result = 0;
int total_received = ret;
if (ret == SOCKET_ERROR_VALUE) {
result = TranslateError(GET_ERRNO);
total_received = 0;
}
cmd_buffer[1] = result;
cmd_buffer[2] = ret;
cmd_buffer[3] = total_received;
}
static void Poll(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 nfds = cmd_buffer[1];
int timeout = cmd_buffer[2];
CTRPollFD* input_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[6]));
CTRPollFD* output_fds = reinterpret_cast<CTRPollFD*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different sizes)
// so we have to copy the data
pollfd* platform_pollfd = new pollfd[nfds];
for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
platform_pollfd[current_fds] = CTRPollFD::ToPlatform(input_fds[current_fds]);
int ret = ::poll(platform_pollfd, nfds, timeout);
// Now update the output pollfd structure
for (unsigned current_fds = 0; current_fds < nfds; ++current_fds)
output_fds[current_fds] = CTRPollFD::FromPlatform(platform_pollfd[current_fds]);
delete[] platform_pollfd;
int result = 0;
if (ret == SOCKET_ERROR_VALUE)
result = TranslateError(GET_ERRNO);
cmd_buffer[1] = result;
cmd_buffer[2] = ret;
}
static void GetSockName(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
socklen_t ctr_len = cmd_buffer[2];
CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr);
int ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len);
if (ctr_dest_addr != nullptr) {
*ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
} else {
cmd_buffer[1] = -1; // TODO(Subv): Verify error
return;
}
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void Shutdown(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
int how = cmd_buffer[2];
int ret = ::shutdown(socket_handle, how);
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void GetPeerName(Service::Interface* self) {
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
socklen_t len = cmd_buffer[2];
CTRSockAddr* ctr_dest_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
sockaddr dest_addr;
socklen_t dest_addr_len = sizeof(dest_addr);
int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len);
if (ctr_dest_addr != nullptr) {
*ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
} else {
cmd_buffer[1] = -1;
return;
}
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void Connect(Service::Interface* self) {
// TODO(Subv): Calling this function on a blocking socket will block the emu thread,
// preventing graceful shutdown when closing the emulator, this can be fixed by always
// performing nonblocking operations and spinlock until the data is available
u32* cmd_buffer = Kernel::GetCommandBuffer();
u32 socket_handle = cmd_buffer[1];
socklen_t len = cmd_buffer[2];
CTRSockAddr* ctr_input_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(cmd_buffer[6]));
if (ctr_input_addr == nullptr) {
cmd_buffer[1] = -1; // TODO(Subv): Verify error
return;
}
sockaddr input_addr = CTRSockAddr::ToPlatform(*ctr_input_addr);
int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
int result = 0;
if (ret != 0)
result = TranslateError(GET_ERRNO);
cmd_buffer[2] = ret;
cmd_buffer[1] = result;
}
static void InitializeSockets(Service::Interface* self) {
// TODO(Subv): Implement
#if EMU_PLATFORM == PLATFORM_WINDOWS
WSADATA data;
WSAStartup(MAKEWORD(2, 2), &data);
#endif
u32* cmd_buffer = Kernel::GetCommandBuffer();
cmd_buffer[1] = 0;
}
static void ShutdownSockets(Service::Interface* self) {
// TODO(Subv): Implement
CleanupSockets();
#if EMU_PLATFORM == PLATFORM_WINDOWS
WSACleanup();
#endif
u32* cmd_buffer = Kernel::GetCommandBuffer();
cmd_buffer[1] = 0;
}
const Interface::FunctionInfo FunctionTable[] = { const Interface::FunctionInfo FunctionTable[] = {
{0x00010044, nullptr, "InitializeSockets"}, {0x00010044, InitializeSockets, "InitializeSockets"},
{0x000200C2, nullptr, "socket"}, {0x000200C2, Socket, "Socket"},
{0x00030082, nullptr, "listen"}, {0x00030082, Listen, "Listen"},
{0x00040082, nullptr, "accept"}, {0x00040082, Accept, "Accept"},
{0x00050084, nullptr, "bind"}, {0x00050084, Bind, "Bind"},
{0x00060084, nullptr, "connect"}, {0x00060084, Connect, "Connect"},
{0x00070104, nullptr, "recvfrom_other"}, {0x00070104, nullptr, "recvfrom_other"},
{0x00080102, nullptr, "recvfrom"}, {0x00080102, RecvFrom, "RecvFrom"},
{0x00090106, nullptr, "sendto_other"}, {0x00090106, nullptr, "sendto_other"},
{0x000A0106, nullptr, "sendto"}, {0x000A0106, SendTo, "SendTo"},
{0x000B0042, nullptr, "close"}, {0x000B0042, Close, "Close"},
{0x000C0082, nullptr, "shutdown"}, {0x000C0082, Shutdown, "Shutdown"},
{0x000D0082, nullptr, "gethostbyname"}, {0x000D0082, nullptr, "GetHostByName"},
{0x000E00C2, nullptr, "gethostbyaddr"}, {0x000E00C2, nullptr, "GetHostByAddr"},
{0x000F0106, nullptr, "unknown_resolve_ip"}, {0x000F0106, nullptr, "unknown_resolve_ip"},
{0x00110102, nullptr, "getsockopt"}, {0x00110102, nullptr, "GetSockOpt"},
{0x00120104, nullptr, "setsockopt"}, {0x00120104, nullptr, "SetSockOpt"},
{0x001300C2, nullptr, "fcntl"}, {0x001300C2, Fcntl, "Fcntl"},
{0x00140084, nullptr, "poll"}, {0x00140084, Poll, "Poll"},
{0x00150042, nullptr, "sockatmark"}, {0x00150042, nullptr, "SockAtMark"},
{0x00160000, nullptr, "gethostid"}, {0x00160000, GetHostId, "GetHostId"},
{0x00170082, nullptr, "getsockname"}, {0x00170082, GetSockName, "GetSockName"},
{0x00180082, nullptr, "getpeername"}, {0x00180082, GetPeerName, "GetPeerName"},
{0x00190000, nullptr, "ShutdownSockets"}, {0x00190000, ShutdownSockets, "ShutdownSockets"},
{0x001A00C0, nullptr, "GetNetworkOpt"}, {0x001A00C0, nullptr, "GetNetworkOpt"},
{0x001B0040, nullptr, "ICMPSocket"}, {0x001B0040, nullptr, "ICMPSocket"},
{0x001C0104, nullptr, "ICMPPing"}, {0x001C0104, nullptr, "ICMPPing"},
@ -52,4 +724,11 @@ Interface::Interface() {
Register(FunctionTable, ARRAY_SIZE(FunctionTable)); Register(FunctionTable, ARRAY_SIZE(FunctionTable));
} }
Interface::~Interface() {
CleanupSockets();
#if EMU_PLATFORM == PLATFORM_WINDOWS
WSACleanup();
#endif
}
} // namespace } // namespace

View File

@ -14,6 +14,7 @@ namespace SOC_U {
class Interface : public Service::Interface { class Interface : public Service::Interface {
public: public:
Interface(); Interface();
~Interface();
std::string GetPortName() const override { std::string GetPortName() const override {
return "soc:U"; return "soc:U";

View File

@ -352,6 +352,18 @@ static s64 GetSystemTick() {
return (s64)Core::g_app_core->GetTicks(); return (s64)Core::g_app_core->GetTicks();
} }
/// Creates a memory block at the specified address with the specified permissions and size
static Result CreateMemoryBlock(Handle* memblock, u32 addr, u32 size, u32 my_permission,
u32 other_permission) {
// TODO(Subv): Implement this function
Handle shared_memory = Kernel::CreateSharedMemory();
*memblock = shared_memory;
LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr);
return 0;
}
const HLE::FunctionDef SVC_Table[] = { const HLE::FunctionDef SVC_Table[] = {
{0x00, nullptr, "Unknown"}, {0x00, nullptr, "Unknown"},
{0x01, HLE::Wrap<ControlMemory>, "ControlMemory"}, {0x01, HLE::Wrap<ControlMemory>, "ControlMemory"},
@ -383,7 +395,7 @@ const HLE::FunctionDef SVC_Table[] = {
{0x1B, nullptr, "SetTimer"}, {0x1B, nullptr, "SetTimer"},
{0x1C, nullptr, "CancelTimer"}, {0x1C, nullptr, "CancelTimer"},
{0x1D, nullptr, "ClearTimer"}, {0x1D, nullptr, "ClearTimer"},
{0x1E, nullptr, "CreateMemoryBlock"}, {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"},
{0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"},
{0x20, nullptr, "UnmapMemoryBlock"}, {0x20, nullptr, "UnmapMemoryBlock"},
{0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"}, {0x21, HLE::Wrap<CreateAddressArbiter>, "CreateAddressArbiter"},