diff --git a/include/phoenix/ethtool.h b/include/phoenix/ethtool.h new file mode 100644 index 00000000..32d960bf --- /dev/null +++ b/include/phoenix/ethtool.h @@ -0,0 +1,284 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * phoenix/ethtool.h + * + * Copyright 2025 Phoenix Systems + * Author: Julian Uziembło + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _PHOENIX_ETHTOOL_H_ +#define _PHOENIX_ETHTOOL_H_ + +#include +#include + +/* ethtool commands */ +#define ETHTOOL_GSET 0x00000001 +#define ETHTOOL_SSET 0x00000002 +#define ETHTOOL_TEST 0x0000001a +#define ETHTOOL_GSTRINGS 0x0000001b +#define ETHTOOL_GSSET_INFO 0x00000037 +#define ETHTOOL_GLINKSETTINGS 0x0000004c +#define ETHTOOL_SLINKSETTINGS 0x0000004d +#define ETHTOOL_GLOOPBACK 0x0000004e +#define ETHTOOL_SLOOPBACK 0x0000004f + +#define ETH_MDIO_SUPPORTS_C22 1 +#define ETH_MDIO_SUPPORTS_C45 2 + +#define ETH_GSTRING_LEN 32 + +#define BIT_FROM_NAME(name) (1uLL << (ETHTOOL_LINK_MODE##_##name##_##BIT)) + +#define SUPPORTED_10baseT_Half BIT_FROM_NAME(10baseT_Half) +#define SUPPORTED_10baseT_Full BIT_FROM_NAME(10baseT_Full) +#define SUPPORTED_100baseT_Half BIT_FROM_NAME(100baseT_Half) +#define SUPPORTED_100baseT_Full BIT_FROM_NAME(100baseT_Full) +#define SUPPORTED_1000baseT_Half BIT_FROM_NAME(1000baseT_Half) +#define SUPPORTED_1000baseT_Full BIT_FROM_NAME(1000baseT_Full) +#define SUPPORTED_Autoneg BIT_FROM_NAME(Autoneg) +#define SUPPORTED_TP BIT_FROM_NAME(TP) +#define SUPPORTED_AUI BIT_FROM_NAME(AUI) +#define SUPPORTED_MII BIT_FROM_NAME(MII) +#define SUPPORTED_FIBRE BIT_FROM_NAME(FIBRE) +#define SUPPORTED_BNC BIT_FROM_NAME(BNC) +#define SUPPORTED_10000baseT_Full BIT_FROM_NAME(10000baseT_Full) +#define SUPPORTED_Pause BIT_FROM_NAME(Pause) +#define SUPPORTED_Asym_Pause BIT_FROM_NAME(Asym_Pause) +#define SUPPORTED_2500baseX_Full BIT_FROM_NAME(2500baseX_Full) +#define SUPPORTED_Backplane BIT_FROM_NAME(Backplane) +#define SUPPORTED_1000baseKX_Full BIT_FROM_NAME(1000baseKX_Full) +#define SUPPORTED_10000baseKX4_Full BIT_FROM_NAME(10000baseKX4_Full) +#define SUPPORTED_10000baseKR_Full BIT_FROM_NAME(10000baseKR_Full) +#define SUPPORTED_10000baseR_FEC BIT_FROM_NAME(10000baseR_FEC) +#define SUPPORTED_20000baseMLD2_Full BIT_FROM_NAME(20000baseMLD2_Full) +#define SUPPORTED_20000baseKR2_Full BIT_FROM_NAME(20000baseKR2_Full) +#define SUPPORTED_40000baseKR4_Full BIT_FROM_NAME(40000baseKR4_Full) +#define SUPPORTED_40000baseCR4_Full BIT_FROM_NAME(40000baseCR4_Full) +#define SUPPORTED_40000baseSR4_Full BIT_FROM_NAME(40000baseSR4_Full) +#define SUPPORTED_40000baseLR4_Full BIT_FROM_NAME(40000baseLR4_Full) +#define SUPPORTED_56000baseKR4_Full BIT_FROM_NAME(56000baseKR4_Full) +#define SUPPORTED_56000baseCR4_Full BIT_FROM_NAME(56000baseCR4_Full) +#define SUPPORTED_56000baseSR4_Full BIT_FROM_NAME(56000baseSR4_Full) +#define SUPPORTED_56000baseLR4_Full BIT_FROM_NAME(56000baseLR4_Full) + +#define ADVERTISED_10baseT_Half BIT_FROM_NAME(10baseT_Half) +#define ADVERTISED_10baseT_Full BIT_FROM_NAME(10baseT_Full) +#define ADVERTISED_100baseT_Half BIT_FROM_NAME(100baseT_Half) +#define ADVERTISED_100baseT_Full BIT_FROM_NAME(100baseT_Full) +#define ADVERTISED_1000baseT_Half BIT_FROM_NAME(1000baseT_Half) +#define ADVERTISED_1000baseT_Full BIT_FROM_NAME(1000baseT_Full) +#define ADVERTISED_Autoneg BIT_FROM_NAME(Autoneg) +#define ADVERTISED_TP BIT_FROM_NAME(TP) +#define ADVERTISED_AUI BIT_FROM_NAME(AUI) +#define ADVERTISED_MII BIT_FROM_NAME(MII) +#define ADVERTISED_FIBRE BIT_FROM_NAME(FIBRE) +#define ADVERTISED_BNC BIT_FROM_NAME(BNC) +#define ADVERTISED_10000baseT_Full BIT_FROM_NAME(10000baseT_Full) +#define ADVERTISED_Pause BIT_FROM_NAME(Pause) +#define ADVERTISED_Asym_Pause BIT_FROM_NAME(Asym_Pause) +#define ADVERTISED_2500baseX_Full BIT_FROM_NAME(2500baseX_Full) +#define ADVERTISED_Backplane BIT_FROM_NAME(Backplane) +#define ADVERTISED_1000baseKX_Full BIT_FROM_NAME(1000baseKX_Full) +#define ADVERTISED_10000baseKX4_Full BIT_FROM_NAME(10000baseKX4_Full) +#define ADVERTISED_10000baseKR_Full BIT_FROM_NAME(10000baseKR_Full) +#define ADVERTISED_10000baseR_FEC BIT_FROM_NAME(10000baseR_FEC) +#define ADVERTISED_20000baseMLD2_Full BIT_FROM_NAME(20000baseMLD2_Full) +#define ADVERTISED_20000baseKR2_Full BIT_FROM_NAME(20000baseKR2_Full) +#define ADVERTISED_40000baseKR4_Full BIT_FROM_NAME(40000baseKR4_Full) +#define ADVERTISED_40000baseCR4_Full BIT_FROM_NAME(40000baseCR4_Full) +#define ADVERTISED_40000baseSR4_Full BIT_FROM_NAME(40000baseSR4_Full) +#define ADVERTISED_40000baseLR4_Full BIT_FROM_NAME(40000baseLR4_Full) +#define ADVERTISED_56000baseKR4_Full BIT_FROM_NAME(56000baseKR4_Full) +#define ADVERTISED_56000baseCR4_Full BIT_FROM_NAME(56000baseCR4_Full) +#define ADVERTISED_56000baseSR4_Full BIT_FROM_NAME(56000baseSR4_Full) +#define ADVERTISED_56000baseLR4_Full BIT_FROM_NAME(56000baseLR4_Full) + +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_2500 2500 +#define SPEED_5000 5000 +#define SPEED_10000 10000 +#define SPEED_14000 14000 +#define SPEED_20000 20000 +#define SPEED_25000 25000 +#define SPEED_40000 40000 +#define SPEED_50000 50000 +#define SPEED_56000 56000 +#define SPEED_100000 100000 +#define SPEED_200000 200000 +#define SPEED_400000 400000 +#define SPEED_800000 800000 +#define SPEED_UNKNOWN -1 + +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 +#define DUPLEX_UNKNOWN 0xff + +#define PORT_TP 0x00 +#define PORT_AUI 0x01 +#define PORT_MII 0x02 +#define PORT_FIBRE 0x03 +#define PORT_BNC 0x04 +#define PORT_DA 0x05 +#define PORT_NONE 0xef +#define PORT_OTHER 0xff + +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +#define ETH_TP_MDI_INVALID 0x00 +#define ETH_TP_MDI 0x01 +#define ETH_TP_MDI_X 0x02 +#define ETH_TP_MDI_AUTO 0x03 + +#define ETH_PHY_LOOPBACK_DISABLED 0x0 +#define ETH_PHY_LOOPBACK_ENABLED 0x1 +#define ETH_PHY_LOOPBACK_SET_FAILED 0xff + + +enum { + ETHTOOL_LINK_MODE_10baseT_Half_BIT, + ETHTOOL_LINK_MODE_10baseT_Full_BIT, + ETHTOOL_LINK_MODE_100baseT_Half_BIT, + ETHTOOL_LINK_MODE_100baseT_Full_BIT, + ETHTOOL_LINK_MODE_1000baseT_Half_BIT, + ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + ETHTOOL_LINK_MODE_Autoneg_BIT, + ETHTOOL_LINK_MODE_TP_BIT, + ETHTOOL_LINK_MODE_AUI_BIT, + ETHTOOL_LINK_MODE_MII_BIT, + ETHTOOL_LINK_MODE_FIBRE_BIT, + ETHTOOL_LINK_MODE_BNC_BIT, + ETHTOOL_LINK_MODE_10000baseT_Full_BIT, + ETHTOOL_LINK_MODE_Pause_BIT, + ETHTOOL_LINK_MODE_Asym_Pause_BIT, + ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + ETHTOOL_LINK_MODE_Backplane_BIT, + ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, + ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT, + ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT, + ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, + ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, + ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, + ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, + ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, + ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, + ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, + ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, + ETHTOOL_LINK_MODE_25000baseCR_Full_BIT +}; + +enum ethtool_stringset { + ETH_SS_TEST = 0, + ETH_SS_STATS, + ETH_SS_PRIV_FLAGS, + ETH_SS_NTUPLE_FILTERS, + ETH_SS_FEATURES, + ETH_SS_RSS_HASH_FUNCS, + ETH_SS_TUNABLES, + ETH_SS_PHY_STATS, + ETH_SS_PHY_TUNABLES, + ETH_SS_LINK_MODES, + ETH_SS_MSG_CLASSES, + ETH_SS_WOL_MODES, + ETH_SS_SOF_TIMESTAMPING, + ETH_SS_TS_TX_TYPES, + ETH_SS_TS_RX_FILTERS, + ETH_SS_UDP_TUNNEL_TYPES, + ETH_SS_STATS_STD, + ETH_SS_STATS_ETH_PHY, + ETH_SS_STATS_ETH_MAC, + ETH_SS_STATS_ETH_CTRL, + ETH_SS_STATS_RMON, + ETH_SS_STATS_PHY, + ETH_SS_TS_FLAGS, + + ETH_SS_COUNT +}; + +enum { + ETH_TEST_FL_OFFLINE = (1u << 0), + ETH_TEST_FL_FAILED = (1u << 1), + ETH_TEST_FL_EXTERNAL_LB = (1u << 2), + ETH_TEST_FL_EXTERNAL_LB_DONE = (1u << 3), +}; + +struct ethtool_cmd { + uint32_t cmd; + uint32_t supported; + uint32_t advertising; + uint16_t speed; + uint8_t duplex; + uint8_t port; + uint8_t phy_address; + uint8_t transceiver; + uint8_t autoneg; + uint8_t mdio_support; + uint32_t maxtxpkt; + uint32_t maxrxpkt; + uint16_t speed_hi; + uint8_t eth_tp_mdix; + uint8_t eth_tp_mdix_ctrl; + uint32_t lp_advertising; + uint32_t reserved[2]; +}; + +struct ethtool_gstrings { + uint32_t cmd; + uint32_t string_set; + uint32_t len; + uint8_t data[]; +}; + +struct ethtool_sset_info { + uint32_t cmd; + uint32_t reserved; + uint64_t sset_mask; + uint32_t data[]; +}; + +struct ethtool_test { + uint32_t cmd; + uint32_t flags; + uint32_t reserved; + uint32_t len; + uint64_t data[]; +}; + +struct ethtool_value { + uint32_t cmd; + uint32_t data; +}; + +struct ethtool_link_settings { + uint32_t cmd; + uint32_t speed; + uint8_t duplex; + uint8_t port; + uint8_t phy_address; + uint8_t autoneg; + uint8_t mdio_support; + uint8_t eth_tp_mdix; + uint8_t eth_tp_mdix_ctrl; + int8_t link_mode_masks_nwords; + uint8_t transceiver; + uint8_t master_slave_cfg; + uint8_t master_slave_state; + uint8_t rate_matching; + uint32_t reserved[7]; + uint32_t link_mode_masks[]; +}; + +#endif /* _PHOENIX_ETHTOOL_H_ */ diff --git a/include/sys/sockios.h b/include/sys/sockios.h index 9c4c9367..c90a1228 100644 --- a/include/sys/sockios.h +++ b/include/sys/sockios.h @@ -20,8 +20,9 @@ #include #include -#define SOCK_IOC_TYPE 'S' +#define SOCK_IOC_TYPE 'S' +/* clang-format off */ /* Socket configuration controls. */ #define SIOCGIFNAME _IOWR(SOCK_IOC_TYPE, 0x10, struct ifreq) /* get name of interface with given index */ #define SIOCGIFCONF _IOWR(SOCK_IOC_TYPE, 0x12, struct ifconf) /* get iface list */ @@ -53,4 +54,6 @@ #define SIOCADDRT _IOW(SOCK_IOC_TYPE, 0x44, struct rtentry) /* add routing table entry */ #define SIOCDELRT _IOW(SOCK_IOC_TYPE, 0x45, struct rtentry) /* delete routing table entry */ -#endif // LIBPHOENIX_SYS_SOCKIOS_H +#define SIOCETHTOOL _IOWR(SOCK_IOC_TYPE, 0x46, struct ifreq) /* ethtool interface */ + +#endif /* LIBPHOENIX_SYS_SOCKIOS_H */ diff --git a/sys/ioctl.c b/sys/ioctl.c index ad74da9b..ca4e321b 100644 --- a/sys/ioctl.c +++ b/sys/ioctl.c @@ -20,11 +20,7 @@ #include #include -/* SIOCGIFCONF handling */ #include -#include -#include -#include const void *ioctl_unpack(msg_t *msg, unsigned long *request, id_t *id) @@ -47,7 +43,7 @@ const void *ioctl_unpackEx(msg_t *msg, unsigned long *request, id_t *id, void ** *id = msg->oid.id; } - size = IOCPARM_LEN(ioctl->request); + size = ioctl->size; if ((ioctl->request & IOC_IN) != 0) { if (size <= (sizeof(msg->i.raw) - sizeof(ioctl_in_t))) { @@ -62,31 +58,6 @@ const void *ioctl_unpackEx(msg_t *msg, unsigned long *request, id_t *id, void ** size = min(size, sizeof(void *)); (void)memcpy(&data, ioctl->data, size); } - - /* ioctl special case: arg is structure with pointer - has to be custom-packed into message */ - if (ioctl->request == SIOCGIFCONF) { - struct ifconf *ifc = (struct ifconf *)data; - if (ifc->ifc_len > 0) { - ifc->ifc_buf = msg->o.data; - } - } - else if ((ioctl->request == SIOCADDRT) || (ioctl->request == SIOCDELRT)) { - /* input data is read only so we have allocate the in_data if - * we want to change it. TODO: find better place to allocate and free - * message */ - struct rtentry *rt = malloc(msg->i.size); - if (rt == NULL) { - return NULL; - } - (void)memcpy(rt, msg->i.data, msg->i.size); - rt->rt_dev = malloc(msg->o.size); - if (rt->rt_dev == NULL) { - free(rt); - return NULL; - } - (void)memcpy(rt->rt_dev, msg->o.data, msg->o.size); - data = (void *)rt; - } else { } diff --git a/unistd/Makefile b/unistd/Makefile index 192278d0..3f5d4183 100644 --- a/unistd/Makefile +++ b/unistd/Makefile @@ -4,5 +4,5 @@ # Copyright 2020 Phoenix Systems # -OBJS += $(addprefix $(PREFIX_O)unistd/, alarm.o conf.o crypt.o dir.o file.o hostname.o getlogin.o getopt.o getpass.o group.o \ +OBJS += $(addprefix $(PREFIX_O)unistd/, alarm.o conf.o crypt.o dir.o ioctl-helper.o file.o hostname.o getlogin.o getopt.o getpass.o group.o \ hostid.o pause.o pwd.o sys.o user.o) diff --git a/unistd/file.c b/unistd/file.c index bce8b792..d253baf4 100644 --- a/unistd/file.c +++ b/unistd/file.c @@ -29,6 +29,7 @@ #include #include "posix/utils.h" +#include "ioctl-helper.h" extern ssize_t sys_read(int fildes, void *buf, size_t nbyte, off_t offset); @@ -644,20 +645,37 @@ int fcntl(int fd, int cmd, ...) } -extern int sys_ioctl(int fildes, unsigned long request, void *val); +extern int sys_ioctl(int fildes, unsigned long request, void *val, size_t size); int ioctl(int fildes, unsigned long request, ...) { va_list ap; void *val; + void *val_packed; + size_t size; + int ret; /* FIXME: handle varargs properly */ va_start(ap, request); val = va_arg(ap, void *); va_end(ap); - return SET_ERRNO(sys_ioctl(fildes, request, val)); + ret = ioctl_serialize(request, val, &val_packed, &size); + if (ret < 0) { + /* frees val_packed internally on error */ + return SET_ERRNO(ret); + } + + ret = sys_ioctl(fildes, request, val_packed, size); + if (ret < 0) { + ioctl_free(request, val_packed); + return SET_ERRNO(ret); + } + + ioctl_deserialize(request, val, val_packed); + + return SET_ERRNO(ret); } diff --git a/unistd/ioctl-helper.c b/unistd/ioctl-helper.c new file mode 100644 index 00000000..c28fe139 --- /dev/null +++ b/unistd/ioctl-helper.c @@ -0,0 +1,278 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * ioctl special case input serialization/deserialization + * + * Copyright 2025 Phoenix Systems + * Author: Julian Uziemblo + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ +#include +#include +#include +#include +#include + +#include "ioctl-helper.h" + +#define PTR_ADD(ptr, offset) ((void *)(((uint8_t *)(ptr)) + (offset))) +#define PTR_ADD_AND_DEREF(ptr, offset) (*(void **)PTR_ADD(ptr, offset)) + +#define IS_SPECIAL_IOCTL(request) ( \ + (request == SIOCGIFCONF) || \ + (request == SIOCADDRT) || \ + (request == SIOCDELRT) || \ + (request == SIOCETHTOOL)) + + +typedef struct { + size_t offset; + size_t size; +} subptr_info_t; + +typedef int (*ioctl_op_t)(void **, size_t *, void *, size_t, const subptr_info_t *, size_t); + + +static inline size_t ioctl_totalSize(size_t ioctl_val_size, const subptr_info_t *subptrs, size_t nsubptrs) +{ + size_t total_size = ioctl_val_size; + + for (size_t i = 0; i < nsubptrs; i++) { + total_size += subptrs[i].size; + } + + return total_size; +} + + +static int ioctl_packStructWithSubptrs(void **packed, size_t *packed_size, void *ioctl_val, size_t ioctl_val_size, const subptr_info_t *subptrs, size_t nsubptrs) +{ + if (nsubptrs == 0 || subptrs == NULL) { + /* nothing to pack */ + *packed = ioctl_val; + *packed_size = ioctl_val_size; + return 0; + } + + const size_t total_size = ioctl_totalSize(ioctl_val_size, subptrs, nsubptrs); + size_t offset = 0; + + void *mem = malloc(total_size); + if (mem == NULL) { + return -ENOMEM; + } + + (void)memcpy(PTR_ADD(mem, offset), ioctl_val, ioctl_val_size); + offset += ioctl_val_size; + + for (size_t i = 0; i < nsubptrs; i++) { + (void)memcpy(PTR_ADD(mem, offset), PTR_ADD_AND_DEREF(ioctl_val, subptrs[i].offset), subptrs[i].size); + offset += subptrs[i].size; + } + + *packed = mem; + *packed_size = total_size; + + return 0; +} + + +static int ioctl_unpackStructWithSubptrs(void **packed, size_t *packed_size, void *ioctl_val, size_t ioctl_val_size, const subptr_info_t *subptrs, size_t nsubptrs) +{ + (void)packed_size; + void *from = *packed; + size_t offset = ioctl_val_size; + + if (nsubptrs == 0 || subptrs == NULL) { + /* nothing to unpack */ + (void)memcpy(ioctl_val, from, ioctl_val_size); + return 0; + } + + for (size_t i = 0; i < nsubptrs; i++) { + /* copy old pointer value to helper variable */ + void *subptr = PTR_ADD_AND_DEREF(ioctl_val, subptrs[i].offset); + /* set pointer value in allocated structure to copy the right pointer at the end */ + PTR_ADD_AND_DEREF(from, subptrs[i].offset) = subptr; + + /* copy current substructure */ + (void)memcpy(subptr, PTR_ADD(from, offset), subptrs[i].size); + offset += subptrs[i].size; + } + + /* copy back the main struct */ + (void)memcpy(ioctl_val, from, ioctl_val_size); + + return 0; +} + + +static inline int ioctl_ethtool(struct ifreq *ifr, void **packed, size_t *packed_size, ioctl_op_t op) +{ + size_t ethtool_size; + void *ethtool = ifr->ifr_data; + + if (ethtool == NULL) { + return -EINVAL; + } + + /* first field of every ethtool struct is always uint32_t cmd */ + uint32_t cmd = *((uint32_t *)ethtool); + + switch (cmd) { + case ETHTOOL_GSET: + case ETHTOOL_SSET: + ethtool_size = sizeof(struct ethtool_cmd); + break; + + case ETHTOOL_TEST: { + struct ethtool_test *test = ethtool; + ethtool_size = sizeof(*test) + (test->len * sizeof(test->data[0])); + break; + } + + case ETHTOOL_GSTRINGS: { + struct ethtool_gstrings *gstrings = ethtool; + ethtool_size = sizeof(*gstrings) + (gstrings->len * ETH_GSTRING_LEN); + break; + } + + case ETHTOOL_GSSET_INFO: { + struct ethtool_sset_info *sset = ethtool; + ethtool_size = sizeof(*sset) + (__builtin_popcountll(sset->sset_mask) * sizeof(sset->data[0])); + break; + } + + case ETHTOOL_GLOOPBACK: + case ETHTOOL_SLOOPBACK: + ethtool_size = sizeof(struct ethtool_value); + break; + + case ETHTOOL_GLINKSETTINGS: + case ETHTOOL_SLINKSETTINGS: { + struct ethtool_link_settings *settings = ethtool; + ethtool_size = sizeof(*settings) + (settings->link_mode_masks_nwords * sizeof(settings->link_mode_masks[0])); + break; + } + + default: + return -EOPNOTSUPP; + } + + const subptr_info_t info = { .size = ethtool_size, .offset = __builtin_offsetof(struct ifreq, ifr_data) }; + return op(packed, packed_size, ifr, sizeof(*ifr), &info, 1); +} + + +static inline int ioctl_rtEntry(struct rtentry *rt, void **packed, size_t *packed_size, ioctl_op_t op) +{ + size_t rt_dev_size = 0; + if (rt->rt_dev == NULL) { + return -EINVAL; + } + rt_dev_size = strlen(rt->rt_dev); + + const subptr_info_t info = { .size = rt_dev_size, .offset = __builtin_offsetof(struct rtentry, rt_dev) }; + return op(packed, packed_size, rt, sizeof(*rt), &info, 1); +} + + +static inline int ioctl_ifconf(struct ifconf *ifc, void **packed, size_t *packed_size, ioctl_op_t op) +{ + size_t ifc_buf_len = ifc->ifc_len; + if (ifc->ifc_buf == NULL) { + if (ifc_buf_len == 0) { + /* device returns the needed length + -> no packing/unpacking needed */ + return op(packed, packed_size, ifc, sizeof(*ifc), NULL, 0); + } + else { + return -EINVAL; + } + } + + const subptr_info_t info = { .size = ifc_buf_len, .offset = __builtin_offsetof(struct ifconf, ifc_buf) }; + return op(packed, packed_size, ifc, sizeof(*ifc), &info, 1); +} + + +void ioctl_free(unsigned long request, void *packed) +{ + if (request == SIOCGIFCONF) { + struct ifconf *ifc = packed; + if (ifc->ifc_buf == NULL) { + /* special case - value was not packed */ + return; + } + } + if (IS_SPECIAL_IOCTL(request)) { + if (packed != NULL) { + free(packed); + } + } +} + + +int ioctl_serialize(unsigned long request, void *ioctl_val, void **packed, size_t *packed_size) +{ + int ret = 0; + + /* set to NULL so if we don't allocate the memory, we won't call free on it */ + *packed = NULL; + + switch (request) { + case SIOCADDRT: + case SIOCDELRT: + ret = ioctl_rtEntry(ioctl_val, packed, packed_size, ioctl_packStructWithSubptrs); + break; + + case SIOCGIFCONF: + ret = ioctl_ifconf(ioctl_val, packed, packed_size, ioctl_packStructWithSubptrs); + break; + + case SIOCETHTOOL: + ret = ioctl_ethtool(ioctl_val, packed, packed_size, ioctl_packStructWithSubptrs); + break; + + default: + *packed = ioctl_val; + *packed_size = IOCPARM_LEN(request); + ret = 0; + break; + } + if (ret < 0) { + ioctl_free(request, *packed); + } + + return ret; +} + + +void ioctl_deserialize(unsigned long request, void *ioctl_val, void *packed) +{ + switch (request) { + case SIOCADDRT: + case SIOCDELRT: + (void)ioctl_rtEntry(ioctl_val, &packed, NULL, ioctl_unpackStructWithSubptrs); + break; + + case SIOCGIFCONF: + (void)ioctl_ifconf(ioctl_val, &packed, NULL, ioctl_unpackStructWithSubptrs); + break; + + case SIOCETHTOOL: + (void)ioctl_ethtool(ioctl_val, &packed, NULL, ioctl_unpackStructWithSubptrs); + break; + + default: + /* nothing to do */ + return; + } + + ioctl_free(request, packed); +} diff --git a/unistd/ioctl-helper.h b/unistd/ioctl-helper.h new file mode 100644 index 00000000..cf52bbd2 --- /dev/null +++ b/unistd/ioctl-helper.h @@ -0,0 +1,36 @@ +/* + * Phoenix-RTOS + * + * libphoenix + * + * ioctl special case input serialization/deserialization + * + * Copyright 2025 Phoenix Systems + * Author: Julian Uziembło + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _LIBPHOENIX_IOCTL_HELPER_H_ +#define _LIBPHOENIX_IOCTL_HELPER_H_ + +#include + + +/* deallocates memory if it was allocated by ioctl_serialize */ +void ioctl_free(unsigned long request, void *packed); + + +/* serialize the request's structure into packed. + allocates memory in special cases to be freed by ioctl_free */ +int ioctl_serialize(unsigned long request, void *ioctl_val, void **packed, size_t *packed_size); + + +/* deserialize the request's structure from packed back into ioctl_val. + deallocates memory if it was allocated by ioctl_serialize */ +void ioctl_deserialize(unsigned long request, void *ioctl_val, void *packed); + + +#endif /* _LIBPHOENIX_IOCTL_HELPER_H_ */