/* * \brief Linux emulation code * \author Sebastian Sumpf * \author Josef Soentgen * \date 2013-08-30 */ /* * Copyright (C) 2013-2017 Genode Labs GmbH * * This file is distributed under the terms of the GNU General Public License * version 2. */ /* Linux includes */ #include #include #include #include #include #include #include #include #include /*********************** ** linux/genetlink.h ** ***********************/ /* needed by af_netlink.c */ atomic_t genl_sk_destructing_cnt; wait_queue_head_t genl_sk_destructing_waitq; /**************************** ** asm-generic/atomic64.h ** ****************************/ long long atomic64_read(const atomic64_t *v) { return v->counter; } void atomic64_set(atomic64_t *v, long long i) { v->counter = i; } /******************** ** linux/bitmap.h ** ********************/ void bitmap_fill(unsigned long *dst, int nbits) { int count = nbits / (8 * sizeof (long)); int i; memset(dst, 0xff, count * sizeof(long)); dst += count; count = nbits % (8 * sizeof(long)); for (i = 0; i < count; i++) *dst |= (1 << i); } void bitmap_zero(unsigned long *dst, int nbits) { int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long); memset(dst, 0, len); } /***************** ** linux/gfp.h ** *****************/ unsigned long get_zeroed_page(gfp_t gfp_mask) { return (unsigned long)kzalloc(PAGE_SIZE, 0); } /******************** ** linux/percpu.h ** ********************/ void *__alloc_percpu(size_t size, size_t align) { return kzalloc(size, 0); } /****************** ** linux/hash.h ** ******************/ u32 hash_32(u32 val, unsigned int bits) { enum { GOLDEN_RATIO_PRIME_32 = 0x9e370001UL }; u32 hash = val * GOLDEN_RATIO_PRIME_32; hash = hash >> (32 - bits); return hash; } /****************** ** linux/dcache ** ******************/ unsigned int full_name_hash(const unsigned char *name, unsigned int len) { unsigned hash = 0, i; for (i = 0; i < len; i++) hash += name[i]; return hash; } /****************************** * net/core/net/namespace.h ** ******************************/ int register_pernet_subsys(struct pernet_operations *ops) { if (ops->init) ops->init(&init_net); return 0; } int register_pernet_device(struct pernet_operations *ops) { return register_pernet_subsys(ops); } /************************* ** net/net_namespace.h ** *************************/ int rt_genid(struct net *net) { int ret = atomic_read(&net->rt_genid); return -1; } int rt_genid_ipv4(struct net *net) { return atomic_read(&net->ipv4.rt_genid); } void rt_genid_bump_ipv4(struct net *net) { atomic_inc(&net->ipv4.rt_genid); } /********************** ** linx/rtnetlink.h ** **********************/ struct netdev_queue *dev_ingress_queue(struct net_device *dev) { return dev->ingress_queue; } void rtnl_notify(struct sk_buff *skb, struct net *net, u32 pid, u32 group, struct nlmsghdr *nlh, gfp_t flags) { nlmsg_free(skb); } /**************** ** linux/ip.h ** ****************/ struct iphdr *ip_hdr(const struct sk_buff *skb) { return (struct iphdr *)skb_network_header(skb); } /******************************* ** asm-generic/bitops/find.h ** *******************************/ unsigned long find_first_zero_bit(unsigned long const *addr, unsigned long size) { unsigned long i, j; for (i = 0; i < (size / BITS_PER_LONG); i++) if (addr[i] != ~0UL) break; if (i == size) return size; for (j = 0; j < BITS_PER_LONG; j++) if ((~addr[i]) & (1 << j)) break; return (i * BITS_PER_LONG) + j; } /**************************** ** asm-generic/getorder.h ** ****************************/ int get_order(unsigned long size) { int order; size--; size >>= PAGE_SHIFT; order = __builtin_ctzl(size); return order; } /********************* ** linux/jiffies.h ** *********************/ clock_t jiffies_to_clock_t(unsigned long j) { return j / HZ; /* XXX not sure if this is enough */ } /********************* ** linux/utsname.h ** *********************/ struct uts_name init_uts_ns; struct new_utsname *init_utsname(void) { return &init_uts_ns.name; } struct new_utsname *utsname(void) { return init_utsname(); } /********************** ** linux/notifier.h ** **********************/ int raw_notifier_chain_register(struct raw_notifier_head *nh, struct notifier_block *n) { struct notifier_block *nl = nh->head; struct notifier_block *pr = 0; while (nl) { if (n->priority > nl->priority) break; pr = nl; nl = nl->next; } n->next = nl; if (pr) pr->next = n; else nh->head = n; return 0; } int raw_notifier_call_chain(struct raw_notifier_head *nh, unsigned long val, void *v) { int ret = NOTIFY_DONE; struct notifier_block *nb = nh->head; while (nb) { ret = nb->notifier_call(nb, val, v); if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) break; nb = nb->next; } return ret; } int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n) { return raw_notifier_chain_register((struct raw_notifier_head *)nh, n); } int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v) { return raw_notifier_call_chain((struct raw_notifier_head *)nh, val, v); } /**************************** ** asm-generic/checksum.h ** ****************************/ __sum16 csum_fold(__wsum csum) { u32 sum = (u32)csum; sum = (sum & 0xffff) + (sum >> 16); sum = (sum & 0xffff) + (sum >> 16); return (__sum16)~sum; } /******************* ** net/checksum.h ** ********************/ __wsum csum_add(__wsum csum, __wsum addend) { u32 res = (u32)csum; res += (u32)addend; return (__wsum)(res + (res < (u32)addend)); } __wsum csum_block_add(__wsum csum, __wsum csum2, int offset) { u32 sum = (u32)csum2; if (offset&1) sum = ((sum&0xFF00FF)<<8)+((sum>>8)&0xFF00FF); return csum_add(csum, (__wsum)sum); } __wsum csum_block_add_ext(__wsum csum, __wsum csum2, int offset, int len) { return csum_block_add(csum, csum2, offset); } /*************************** ** Linux socket function ** ***************************/ static const struct net_proto_family *net_families[NPROTO]; int sock_register(const struct net_proto_family *ops) { if (ops->family >= NPROTO) { printk("protocol %d >= NPROTO (%d)\n", ops->family, NPROTO); return -ENOBUFS; } net_families[ops->family] = ops; pr_info("NET: Registered protocol family %d\n", ops->family); return 0; } struct socket *sock_alloc(void) { struct socket *sock = (struct socket *)kzalloc(sizeof(struct socket), 0); /* * Linux normally allocates the socket_wq when calling * sock_alloc_inode() while we do it here hoping for the best. */ sock->wq = (struct socket_wq*)kzalloc(sizeof(*sock->wq), 0); if (!sock->wq) { kfree(sock); return NULL; } return sock; } int sock_create_lite(int family, int type, int protocol, struct socket **res) { struct socket *sock = sock_alloc(); if (!sock) return -ENOMEM; sock->type = type; *res = sock; return 0; } int sock_create_kern(struct net *net, int family, int type, int proto, struct socket **res) { struct socket *sock; const struct net_proto_family *pf; int err; if (family < 0 || family > NPROTO) return -EAFNOSUPPORT; if (type < 0 || type > SOCK_MAX) return -EINVAL; pf = net_families[family]; if (!pf) { printk("No protocol found for family %d\n", family); return -ENOPROTOOPT; } sock = sock_alloc(); if (!sock) { printk("Could not allocate socket\n"); return -ENFILE; } sock->type = type; err = pf->create(&init_net, sock, proto, 1); if (err) { kfree(sock); return err; } *res = sock; return 0; } static void sock_init(void) { skb_init(); } core_initcall(sock_init); /************************* ** Lxip initialization ** *************************/ /* * Header declarations and tuning */ struct net init_net; unsigned long *sysctl_local_reserved_ports; struct pernet_operations loopback_net_ops; /** * nr_free_buffer_pages - count number of pages beyond high watermark * * nr_free_buffer_pages() counts the number of pages which are beyond the * high * watermark within ZONE_DMA and ZONE_NORMAL. */ unsigned long nr_free_buffer_pages(void) { return 1000; } /* * Declare stuff used */ int __ip_auto_config_setup(char *addrs); void core_sock_init(void); void core_netlink_proto_init(void); void subsys_net_dev_init(void); void fs_inet_init(void); void module_driver_init(void); void module_cubictcp_register(void); void late_ip_auto_config(void); void late_tcp_congestion_default(void); /** * Initialize sub-systems */ void lxip_init() { /* init data */ INIT_LIST_HEAD(&init_net.dev_base_head); core_sock_init(); core_netlink_proto_init(); /* sub-systems */ subsys_net_dev_init(); fs_inet_init(); /* enable local accepts */ IPV4_DEVCONF_ALL(&init_net, ACCEPT_LOCAL) = 0x1; /* congestion control */ module_cubictcp_register(); /* driver */ module_driver_init(); /* late */ late_tcp_congestion_default(); } /* * Network configuration */ static void lxip_configure(char const *address_config) { __ip_auto_config_setup((char *)address_config); late_ip_auto_config(); } static bool dhcp_configured = false; static bool dhcp_pending = false; void lxip_configure_mtu(unsigned mtu) { /* zero mtu means reset to default */ unsigned new_mtu = mtu ? mtu : ETH_DATA_LEN; struct net *net; struct net_device *dev; for_each_net(net) { for_each_netdev(net, dev) { dev_set_mtu(dev, new_mtu); } } } void lxip_configure_static(char const *addr, char const *netmask, char const *gateway, char const *nameserver) { char address_config[128]; dhcp_configured = false; snprintf(address_config, sizeof(address_config), "%s::%s:%s:::off:%s", addr, gateway, netmask, nameserver); lxip_configure(address_config); } void lxip_configure_dhcp() { dhcp_configured = true; dhcp_pending = true; lxip_configure("dhcp"); dhcp_pending = false; } bool lxip_do_dhcp() { return dhcp_configured && !dhcp_pending; } /****************** ** Lxip private ** ******************/ void set_sock_wait(struct socket *sock, unsigned long ptr) { sock->sk->sk_wq = (struct socket_wq *)ptr; } int socket_check_state(struct socket *socket) { if (socket->sk->sk_state == TCP_CLOSE_WAIT) return -EINTR; return 0; } void log_sock(struct socket *socket) { printk("\nNEW socket %p sk %p fsk %x &sk %p &fsk %p\n\n", socket, socket->sk, socket->flags, &socket->sk, &socket->flags); }