Files
KirkOS/user/include/mlibc/sysdeps/managarm/generic/ioctl.cpp
T
kaguya 9a9b91c940 user: implement mlibc as the libc, finally.
It's finally done..

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
2026-05-02 03:31:49 -04:00

1225 lines
40 KiB
C++

#ifdef _GNU_SOURCE
#undef _GNU_SOURCE
#endif
#include <errno.h>
#include <fcntl.h>
#include <linux/cdrom.h>
#include <linux/fiemap.h>
#include <linux/fs.h>
#include <linux/input.h>
#include <linux/kd.h>
#include <linux/nvme_ioctl.h>
#include <linux/usb/cdc-wdm.h>
#include <linux/vt.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>
#include <bits/ensure.h>
#include <bits/errors.hpp>
#include <bragi/helpers-frigg.hpp>
#include <frg/vector.hpp>
#include <mlibc/all-sysdeps.hpp>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>
#include <mlibc/posix-pipe.hpp>
#include <fs.frigg_bragi.hpp>
#include <posix.frigg_bragi.hpp>
#define SIOCETHTOOL 0x8946
#define SIOCGSKNS 0x894C
#define TFD_IOC_SET_TICKS _IOW('T', 0, __u64)
namespace mlibc {
static constexpr bool logIoctls = false;
int ioctl_drm(int fd, unsigned long request, void *arg, int *result, HelHandle handle);
int sys_ioctl(int fd, unsigned long request, void *arg, int *result) {
if (logIoctls)
mlibc::infoLogger() << "mlibc: ioctl with"
<< " type: 0x" << frg::hex_fmt(_IOC_TYPE(request)) << ", number: 0x"
<< frg::hex_fmt(_IOC_NR(request))
<< " (raw request: " << frg::hex_fmt(request) << ")"
<< " on fd " << fd << frg::endlog;
SignalGuard sguard;
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
if (_IOC_TYPE(request) == 'd') {
return ioctl_drm(fd, request, arg, result, handle);
}
auto handle_siocgif =
[&arg, &request, &result](
void (*req_setup)(managarm::fs::IfreqRequest<MemoryAllocator> &req, struct ifreq *ifr),
int (*resp_parse)(managarm::fs::IfreqReply<MemoryAllocator> &resp, struct ifreq *ifr)
) -> int {
if (!arg)
return EFAULT;
auto ifr = reinterpret_cast<struct ifreq *>(arg);
managarm::posix::NetserverRequest<MemoryAllocator> token_req(getSysdepsAllocator());
managarm::fs::IfreqRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
req_setup(req, ifr);
auto [offer, send_token_req, send_req, send_req_tail, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::want_lane,
helix_ng::sendBragiHeadOnly(token_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_token_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(send_req_tail.error());
HEL_CHECK(recv_resp.error());
auto preamble = bragi::read_preamble(recv_resp);
frg::vector<uint8_t, MemoryAllocator> tailBuffer{getSysdepsAllocator()};
tailBuffer.resize(preamble.tail_size());
auto [recv_tail] = exchangeMsgsSync(
offer.descriptor().getHandle(),
helix_ng::recvBuffer(tailBuffer.data(), tailBuffer.size())
);
HEL_CHECK(recv_tail.error());
auto resp = *bragi::parse_head_tail<managarm::fs::IfreqReply>(
recv_resp, tailBuffer, getSysdepsAllocator()
);
recv_resp.reset();
int ret = resp_parse(resp, ifr);
if (result)
*result = 0;
return ret;
};
managarm::fs::IoctlRequest<MemoryAllocator> ioctl_req(getSysdepsAllocator());
switch (request) {
case FIONBIO: {
auto mode = reinterpret_cast<int *>(arg);
int flags = fcntl(fd, F_GETFL, 0);
if (*mode) {
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
} else {
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
}
return 0;
}
case FIONREAD: {
auto argp = reinterpret_cast<int *>(arg);
auto handle = getHandleForFd(fd);
if (!handle)
return EBADF;
if (!argp)
return EINVAL;
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(FIONREAD);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::fs::Errors::NOT_CONNECTED) {
return ENOTCONN;
} else {
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*argp = resp.fionread_count();
return 0;
}
}
case FIOCLEX: {
managarm::posix::IoctlFioclexRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_fd(fd);
auto [offer, sendReq, recvResp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()), helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(sendReq.error());
if (recvResp.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(recvResp.error());
managarm::posix::SvrResponse<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recvResp.data(), recvResp.length());
__ensure(resp.error() == managarm::posix::Errors::SUCCESS);
return 0;
}
case TCGETS: {
auto param = reinterpret_cast<struct termios *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_ioctl_req, send_req, recv_resp, recv_attrs] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline(),
helix_ng::recvBuffer(param, sizeof(struct termios))
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
HEL_CHECK(recv_attrs.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
__ensure(recv_attrs.actualLength() == sizeof(struct termios));
*result = resp.result();
return 0;
}
case TCSETS: {
auto param = reinterpret_cast<struct termios *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_ioctl_req, send_req, send_attrs, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::sendBuffer(param, sizeof(struct termios)),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(send_attrs.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
if (result)
*result = resp.result();
return 0;
}
case TIOCSCTTY: {
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::imbueCredentials(),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(imbue_creds.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
return EINVAL;
} else if (resp.error() == managarm::fs::Errors::INSUFFICIENT_PERMISSIONS) {
return EPERM;
}
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
return 0;
}
case TIOCGWINSZ: {
auto param = reinterpret_cast<struct winsize *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return ENOTTY;
HEL_CHECK(send_req.error());
if (recv_resp.error() == kHelErrDismissed)
return ENOTTY;
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() != managarm::fs::Errors::SUCCESS)
return resp.error() | toErrno;
*result = resp.result();
param->ws_col = resp.pts_width();
param->ws_row = resp.pts_height();
param->ws_xpixel = resp.pts_pixel_width();
param->ws_ypixel = resp.pts_pixel_height();
return 0;
}
case TIOCSWINSZ: {
auto param = reinterpret_cast<const struct winsize *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
req.set_pts_width(param->ws_col);
req.set_pts_height(param->ws_row);
req.set_pts_pixel_width(param->ws_xpixel);
req.set_pts_pixel_height(param->ws_ypixel);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
return 0;
}
case TIOCGPTN: {
auto param = reinterpret_cast<int *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*param = resp.pts_index();
if (result)
*result = resp.result();
return 0;
}
case TIOCGPGRP: {
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
frg::string<MemoryAllocator> ser(getSysdepsAllocator());
req.SerializeToString(&ser);
auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBuffer(ser.data(), ser.size()),
helix_ng::imbueCredentials(),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error())
return EINVAL;
HEL_CHECK(send_req.error());
if (imbue_creds.error()) {
infoLogger(
) << "mlibc: TIOCGPGRP used on unexpected socket, returning EINVAL (FIXME)"
<< frg::endlog;
return EINVAL;
}
HEL_CHECK(imbue_creds.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() != managarm::fs::Errors::SUCCESS)
return resp.error() | toErrno;
*result = resp.result();
*static_cast<int *>(arg) = resp.pid();
return 0;
}
case TIOCSPGRP: {
auto param = reinterpret_cast<int *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
req.set_pgid(*param);
frg::string<MemoryAllocator> ser(getSysdepsAllocator());
req.SerializeToString(&ser);
auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBuffer(ser.data(), ser.size()),
helix_ng::imbueCredentials(),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(imbue_creds.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::fs::Errors::INSUFFICIENT_PERMISSIONS) {
return EPERM;
} else if (resp.error() == managarm::fs::Errors::ILLEGAL_ARGUMENT) {
return EINVAL;
}
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
return 0;
}
case TIOCGSID: {
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
frg::string<MemoryAllocator> ser(getSysdepsAllocator());
req.SerializeToString(&ser);
auto [offer, send_ioctl_req, send_req, imbue_creds, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBuffer(ser.data(), ser.size()),
helix_ng::imbueCredentials(),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
if (send_ioctl_req.error())
return EINVAL;
HEL_CHECK(send_ioctl_req.error());
if (send_req.error())
return EINVAL;
HEL_CHECK(send_req.error());
if (imbue_creds.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(imbue_creds.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::fs::Errors::NOT_A_TERMINAL) {
return ENOTTY;
}
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
*static_cast<int *>(arg) = resp.pid();
return 0;
}
case CDROM_GET_CAPABILITY: {
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
frg::string<MemoryAllocator> ser(getSysdepsAllocator());
req.SerializeToString(&ser);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBuffer(ser.data(), ser.size()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
if (send_ioctl_req.error())
return EINVAL;
HEL_CHECK(send_ioctl_req.error());
if (send_req.error())
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
if (resp.error() == managarm::fs::Errors::NOT_A_TERMINAL) {
return ENOTTY;
}
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
return 0;
}
case SIOCETHTOOL:
mlibc::infoLogger() << "\e[35mmlibc: SIOCETHTOOL is a stub" << frg::endlog;
*result = 0;
return ENOSYS;
case SIOCGSKNS:
mlibc::infoLogger() << "\e[35mmlibc: SIOCGSKNS is a stub" << frg::endlog;
*result = 0;
return ENOSYS;
case SG_IO:
mlibc::infoLogger() << "\e[35mmlibc: SG_IO is a stub" << frg::endlog;
*result = 0;
return ENOSYS;
} // end of switch()
if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGVERSION)) {
*reinterpret_cast<int *>(arg) = EV_VERSION;
*result = 0;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGID)) {
memset(arg, 0, sizeof(struct input_id));
auto param = reinterpret_cast<struct input_id *>(arg);
managarm::fs::EvioGetIdRequest<MemoryAllocator> req(getSysdepsAllocator());
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::want_lane,
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
auto conversation = offer.descriptor();
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
auto resp =
*bragi::parse_head_only<managarm::fs::EvioGetIdReply>(recv_resp, getSysdepsAllocator());
recv_resp.reset();
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
param->bustype = resp.bustype();
param->vendor = resp.vendor();
param->product = resp.product();
param->version = resp.version();
*result = 0;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGNAME(0))) {
managarm::fs::EvioGetNameRequest<MemoryAllocator> req(getSysdepsAllocator());
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::want_lane,
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
auto conversation = offer.descriptor();
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
auto preamble = bragi::read_preamble(recv_resp);
__ensure(!preamble.error());
frg::vector<uint8_t, MemoryAllocator> tailBuffer{getSysdepsAllocator()};
tailBuffer.resize(preamble.tail_size());
auto [recv_tail] = exchangeMsgsSync(
conversation.getHandle(), helix_ng::recvBuffer(tailBuffer.data(), tailBuffer.size())
);
HEL_CHECK(recv_tail.error());
auto resp = *bragi::parse_head_tail<managarm::fs::EvioGetNameReply>(
recv_resp, tailBuffer, getSysdepsAllocator()
);
recv_resp.reset();
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
auto chunk = frg::min(_IOC_SIZE(request), resp.name().size() + 1);
memcpy(arg, resp.name().data(), chunk);
*result = chunk;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGRAB)) {
mlibc::infoLogger() << "mlibc: EVIOCGRAB is a no-op" << frg::endlog;
*result = 0;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGPHYS(0))) {
// Returns the sysfs path of the device.
const char *s = "input0";
auto chunk = frg::min(_IOC_SIZE(request), strlen(s) + 1);
memcpy(arg, s, chunk);
*result = chunk;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGUNIQ(0))) {
// Returns a unique ID for the device.
const char *s = "0";
auto chunk = frg::min(_IOC_SIZE(request), strlen(s) + 1);
memcpy(arg, s, chunk);
*result = chunk;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGPROP(0))) {
// Returns a bitmask of properties of the device.
auto size = _IOC_SIZE(request);
memset(arg, 0, size);
*result = size;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGKEY(0))) {
// Returns the current key state.
auto size = _IOC_SIZE(request);
memset(arg, 0, size);
*result = size;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGMTSLOTS(0))) {
// this ioctl is completely, utterly undocumented
// the _IOC_SIZE is a buffer size in bytes, which should be a multiple of int32_t
// bytes should be at least sizeof(int32_t) large.
// the argument (the pointer to the buffer) is an array of int32_t
// the first entry is the number of values supplied, followed by the values
// this would have been worthwhile to document ffs
// the length argument is the buffer size, in bytes
auto bytes = _IOC_SIZE(request);
// the length argument should be a multiple of int32_t
if (!bytes || bytes % sizeof(int32_t))
return EINVAL;
// the number of entries the buffer can hold
auto entries = (bytes / sizeof(int32_t)) - 1;
managarm::fs::EvioGetMultitouchSlotsRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_code(*reinterpret_cast<uint32_t *>(arg));
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::want_lane,
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
auto conversation = offer.descriptor();
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
auto preamble = bragi::read_preamble(recv_resp);
__ensure(!preamble.error());
frg::vector<uint8_t, MemoryAllocator> tailBuffer{getSysdepsAllocator()};
tailBuffer.resize(preamble.tail_size());
auto [recv_tail] = exchangeMsgsSync(
conversation.getHandle(), helix_ng::recvBuffer(tailBuffer.data(), tailBuffer.size())
);
HEL_CHECK(recv_tail.error());
auto resp = *bragi::parse_head_tail<managarm::fs::EvioGetMultitouchSlotsReply>(
recv_resp, tailBuffer, getSysdepsAllocator()
);
recv_resp.reset();
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
auto param = reinterpret_cast<int32_t *>(arg);
for (size_t i = 0; i < resp.values_size() && i < entries; i++) {
param[i + 1] = resp.values(i);
}
param[0] = resp.values_size();
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGLED(0))) {
// Returns the current LED state.
auto size = _IOC_SIZE(request);
memset(arg, 0, size);
*result = size;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCGSW(0))) {
auto size = _IOC_SIZE(request);
memset(arg, 0, size);
*result = size;
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) >= _IOC_NR(EVIOCGBIT(0, 0))
&& _IOC_NR(request) <= _IOC_NR(EVIOCGBIT(EV_MAX, 0))) {
// Returns a bitmask of capabilities of the device.
// If type is zero, return a mask of supported types.
// As EV_SYN is zero, this implies that it is impossible
// to get the mask of supported synthetic events.
auto type = _IOC_NR(request) - _IOC_NR(EVIOCGBIT(0, 0));
if (!type) {
// TODO: Check with the Linux ABI if we have to do this.
memset(arg, 0, _IOC_SIZE(request));
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(EVIOCGBIT(0, 0));
req.set_size(_IOC_SIZE(request));
auto [offer, send_ioctl_req, send_req, recv_resp, recv_data] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline(),
helix_ng::recvBuffer(arg, _IOC_SIZE(request))
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
HEL_CHECK(recv_data.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = recv_data.actualLength();
return 0;
} else {
// TODO: Check with the Linux ABI if we have to do this.
memset(arg, 0, _IOC_SIZE(request));
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(EVIOCGBIT(1, 0));
req.set_input_type(type);
req.set_size(_IOC_SIZE(request));
auto [offer, send_ioctl_req, send_req, recv_resp, recv_data] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline(),
helix_ng::recvBuffer(arg, _IOC_SIZE(request))
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
HEL_CHECK(recv_data.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = recv_data.actualLength();
return 0;
}
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) == _IOC_NR(EVIOCSCLOCKID)) {
auto param = reinterpret_cast<int *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
req.set_input_clock(*param);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
return 0;
} else if (_IOC_TYPE(request) == 'E' && _IOC_NR(request) >= _IOC_NR(EVIOCGABS(0))
&& _IOC_NR(request) <= _IOC_NR(EVIOCGABS(ABS_MAX))) {
auto param = reinterpret_cast<struct input_absinfo *>(arg);
auto type = _IOC_NR(request) - _IOC_NR(EVIOCGABS(0));
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(EVIOCGABS(0));
req.set_input_type(type);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
if (send_req.error() == kHelErrDismissed)
return EINVAL;
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
param->value = resp.input_value();
param->minimum = resp.input_min();
param->maximum = resp.input_max();
param->fuzz = resp.input_fuzz();
param->flat = resp.input_flat();
param->resolution = resp.input_resolution();
*result = resp.result();
return 0;
} else if (request == KDSETMODE) {
auto param = reinterpret_cast<unsigned int *>(arg);
mlibc::infoLogger() << "\e[35mmlibc: KD_SETMODE(" << frg::hex_fmt(param) << ") is a no-op"
<< frg::endlog;
*result = 0;
return 0;
} else if (request == KDGETMODE) {
auto param = reinterpret_cast<unsigned int *>(arg);
mlibc::infoLogger() << "\e[35mmlibc: KD_GETMODE is a no-op" << frg::endlog;
*param = 0;
*result = 0;
return 0;
} else if (request == KDSKBMODE) {
auto param = reinterpret_cast<long>(arg);
mlibc::infoLogger() << "\e[35mmlibc: KD_SKBMODE(" << frg::hex_fmt(param) << ") is a no-op"
<< frg::endlog;
*result = 0;
return 0;
} else if (request == VT_SETMODE) {
// auto param = reinterpret_cast<struct vt_mode *>(arg);
mlibc::infoLogger() << "\e[35mmlibc: VT_SETMODE is a no-op" << frg::endlog;
*result = 0;
return 0;
} else if (request == VT_GETSTATE) {
auto param = reinterpret_cast<struct vt_stat *>(arg);
param->v_active = 0;
param->v_signal = 0;
param->v_state = 0;
mlibc::infoLogger() << "\e[35mmlibc: VT_GETSTATE is a no-op" << frg::endlog;
*result = 0;
return 0;
} else if (request == VT_ACTIVATE || request == VT_WAITACTIVE) {
mlibc::infoLogger() << "\e[35mmlibc: VT_ACTIVATE/VT_WAITACTIVE are no-ops" << frg::endlog;
*result = 0;
return 0;
} else if (request == TIOCSPTLCK) {
mlibc::infoLogger() << "\e[35mmlibc: TIOCSPTLCK is a no-op" << frg::endlog;
if (result)
*result = 0;
return 0;
} else if (request == SIOCGIFNAME) {
return handle_siocgif(
[](auto req, auto ifr) { req.set_index(ifr->ifr_ifindex); },
[](auto resp, auto ifr) {
if (resp.error() != managarm::fs::Errors::SUCCESS)
return EINVAL;
strncpy(ifr->ifr_name, resp.name().data(), IFNAMSIZ);
return 0;
}
);
} else if (request == SIOCGIFCONF) {
if (!arg)
return EFAULT;
auto ifc = reinterpret_cast<struct ifconf *>(arg);
managarm::posix::NetserverRequest<MemoryAllocator> token_req(getSysdepsAllocator());
managarm::fs::IfreqRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_token_req, send_req, send_tail, recv_resp] = exchangeMsgsSync(
getPosixLane(),
helix_ng::offer(
helix_ng::want_lane,
helix_ng::sendBragiHeadOnly(token_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadTail(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
auto conversation = offer.descriptor();
HEL_CHECK(offer.error());
HEL_CHECK(send_token_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(send_tail.error());
HEL_CHECK(recv_resp.error());
auto preamble = bragi::read_preamble(recv_resp);
__ensure(!preamble.error());
frg::vector<uint8_t, MemoryAllocator> tailBuffer{getSysdepsAllocator()};
tailBuffer.resize(preamble.tail_size());
auto [recv_tail] = exchangeMsgsSync(
conversation.getHandle(), helix_ng::recvBuffer(tailBuffer.data(), tailBuffer.size())
);
HEL_CHECK(recv_tail.error());
auto resp = *bragi::parse_head_tail<managarm::fs::IfconfReply>(
recv_resp, tailBuffer, getSysdepsAllocator()
);
recv_resp.reset();
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
if (ifc->ifc_buf == nullptr) {
ifc->ifc_len = int(resp.ifconf_size() * sizeof(struct ifreq));
return 0;
}
ifc->ifc_len = frg::min(int(resp.ifconf_size() * sizeof(struct ifreq)), ifc->ifc_len);
for (size_t i = 0; i < frg::min(resp.ifconf_size(), ifc->ifc_len / sizeof(struct ifreq));
++i) {
auto &conf = resp.ifconf()[i];
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(conf.ip4());
ifreq *req = &ifc->ifc_req[i];
strncpy(req->ifr_name, conf.name().data(), IFNAMSIZ);
memcpy(&req->ifr_addr, &addr, sizeof(addr));
}
if (result)
*result = 0;
return 0;
} else if (request == SIOCGIFNETMASK) {
return handle_siocgif(
[](auto req, auto ifr) {
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
},
[](auto resp, auto ifr) {
if (resp.error() != managarm::fs::Errors::SUCCESS)
return EINVAL;
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr = {htonl(resp.ip4_netmask())};
memcpy(&ifr->ifr_netmask, &addr, sizeof(addr));
return 0;
}
);
} else if (request == SIOCGIFINDEX) {
return handle_siocgif(
[](auto req, auto ifr) {
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
},
[](auto resp, auto ifr) {
if (resp.error() != managarm::fs::Errors::SUCCESS)
return EINVAL;
ifr->ifr_ifindex = resp.index();
return 0;
}
);
} else if (request == SIOCGIFFLAGS) {
return handle_siocgif(
[](auto req, auto ifr) {
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
},
[](auto resp, auto ifr) {
if (resp.error() != managarm::fs::Errors::SUCCESS)
return EINVAL;
ifr->ifr_flags = resp.flags();
return 0;
}
);
} else if (request == SIOCGIFADDR) {
return handle_siocgif(
[](auto req, auto ifr) {
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
},
[](auto resp, auto ifr) {
if (resp.error() != managarm::fs::Errors::SUCCESS)
return EINVAL;
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr = {htonl(resp.ip4_addr())};
memcpy(&ifr->ifr_addr, &addr, sizeof(addr));
return 0;
}
);
} else if (request == SIOCGIFMTU) {
return handle_siocgif(
[](auto req, auto ifr) {
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
},
[](auto resp, auto ifr) {
if (resp.error() != managarm::fs::Errors::SUCCESS)
return EINVAL;
ifr->ifr_mtu = resp.mtu();
return 0;
}
);
} else if (request == SIOCGIFBRDADDR) {
return handle_siocgif(
[](auto req, auto ifr) {
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
},
[](auto resp, auto ifr) {
if (resp.error() != managarm::fs::Errors::SUCCESS)
return EINVAL;
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr = {htonl(resp.ip4_broadcast_addr())};
memcpy(&ifr->ifr_broadaddr, &addr, sizeof(addr));
return 0;
}
);
} else if (request == SIOCGIFHWADDR) {
return handle_siocgif(
[](auto req, auto ifr) {
req.set_name(frg::string<MemoryAllocator>{ifr->ifr_name, getSysdepsAllocator()});
},
[](auto resp, auto ifr) {
if (resp.error() != managarm::fs::Errors::SUCCESS)
return EINVAL;
sockaddr addr{};
addr.sa_family = ARPHRD_ETHER;
memcpy(addr.sa_data, resp.mac().data(), 6);
memcpy(&ifr->ifr_hwaddr, &addr, sizeof(addr));
return 0;
}
);
} else if (request == IOCTL_WDM_MAX_COMMAND) {
auto param = reinterpret_cast<int *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
*param = resp.size();
return 0;
} else if (request == NVME_IOCTL_ID) {
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
return 0;
} else if (request == NVME_IOCTL_ADMIN_CMD) {
auto param = reinterpret_cast<struct nvme_admin_cmd *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
auto [offer, send_ioctl_req, send_req, send_buffer, send_data, recv_resp, recv_data] =
exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::sendBuffer(param, sizeof(*param)),
helix_ng::sendBuffer(reinterpret_cast<void *>(param->addr), param->data_len),
helix_ng::recvInline(),
helix_ng::recvBuffer(reinterpret_cast<void *>(param->addr), param->data_len)
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(send_buffer.error());
HEL_CHECK(send_data.error());
HEL_CHECK(recv_resp.error());
HEL_CHECK(recv_data.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
__ensure(resp.error() == managarm::fs::Errors::SUCCESS);
*result = resp.result();
param->result = resp.status();
return 0;
} else if (request == TFD_IOC_SET_TICKS) {
if (!arg)
return EFAULT;
auto param = reinterpret_cast<uint64_t *>(arg);
managarm::fs::GenericIoctlRequest<MemoryAllocator> req(getSysdepsAllocator());
req.set_command(request);
req.set_ticks(*param);
auto [offer, send_ioctl_req, send_req, recv_resp] = exchangeMsgsSync(
handle,
helix_ng::offer(
helix_ng::sendBragiHeadOnly(ioctl_req, getSysdepsAllocator()),
helix_ng::sendBragiHeadOnly(req, getSysdepsAllocator()),
helix_ng::recvInline()
)
);
HEL_CHECK(offer.error());
HEL_CHECK(send_ioctl_req.error());
HEL_CHECK(send_req.error());
HEL_CHECK(recv_resp.error());
managarm::fs::GenericIoctlReply<MemoryAllocator> resp(getSysdepsAllocator());
resp.ParseFromArray(recv_resp.data(), recv_resp.length());
return 0;
} else if (request == FICLONE || request == FICLONERANGE) {
mlibc::infoLogger() << "\e[35mmlibc: FICLONE/FICLONERANGE are no-ops" << frg::endlog;
*result = -1;
return EOPNOTSUPP;
} else if (request == FS_IOC_GETFLAGS) {
mlibc::infoLogger() << "\e[35mmlibc: FS_IOC_GETFLAGS is a no-op" << frg::endlog;
*result = 0;
return ENOSYS;
} else if (request == FS_IOC_FIEMAP) {
mlibc::infoLogger() << "\e[35mmlibc: FS_IOC_FIEMAP is a no-op" << frg::endlog;
*result = 0;
return ENOSYS;
}
mlibc::infoLogger() << "mlibc: Unexpected ioctl with"
<< " type: 0x" << frg::hex_fmt(_IOC_TYPE(request)) << ", number: 0x"
<< frg::hex_fmt(_IOC_NR(request))
<< " (raw request: " << frg::hex_fmt(request) << ")" << frg::endlog;
return ENOSYS;
}
} // namespace mlibc