cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

core.c (8516B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2007, 2008, 2009 Siemens AG
      4 */
      5
      6#include <linux/slab.h>
      7#include <linux/kernel.h>
      8#include <linux/module.h>
      9#include <linux/device.h>
     10
     11#include <net/cfg802154.h>
     12#include <net/rtnetlink.h>
     13
     14#include "ieee802154.h"
     15#include "nl802154.h"
     16#include "sysfs.h"
     17#include "core.h"
     18
     19/* name for sysfs, %d is appended */
     20#define PHY_NAME "phy"
     21
     22/* RCU-protected (and RTNL for writers) */
     23LIST_HEAD(cfg802154_rdev_list);
     24int cfg802154_rdev_list_generation;
     25
     26struct wpan_phy *wpan_phy_find(const char *str)
     27{
     28	struct device *dev;
     29
     30	if (WARN_ON(!str))
     31		return NULL;
     32
     33	dev = class_find_device_by_name(&wpan_phy_class, str);
     34	if (!dev)
     35		return NULL;
     36
     37	return container_of(dev, struct wpan_phy, dev);
     38}
     39EXPORT_SYMBOL(wpan_phy_find);
     40
     41struct wpan_phy_iter_data {
     42	int (*fn)(struct wpan_phy *phy, void *data);
     43	void *data;
     44};
     45
     46static int wpan_phy_iter(struct device *dev, void *_data)
     47{
     48	struct wpan_phy_iter_data *wpid = _data;
     49	struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
     50
     51	return wpid->fn(phy, wpid->data);
     52}
     53
     54int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
     55		      void *data)
     56{
     57	struct wpan_phy_iter_data wpid = {
     58		.fn = fn,
     59		.data = data,
     60	};
     61
     62	return class_for_each_device(&wpan_phy_class, NULL,
     63			&wpid, wpan_phy_iter);
     64}
     65EXPORT_SYMBOL(wpan_phy_for_each);
     66
     67struct cfg802154_registered_device *
     68cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
     69{
     70	struct cfg802154_registered_device *result = NULL, *rdev;
     71
     72	ASSERT_RTNL();
     73
     74	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
     75		if (rdev->wpan_phy_idx == wpan_phy_idx) {
     76			result = rdev;
     77			break;
     78		}
     79	}
     80
     81	return result;
     82}
     83
     84struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx)
     85{
     86	struct cfg802154_registered_device *rdev;
     87
     88	ASSERT_RTNL();
     89
     90	rdev = cfg802154_rdev_by_wpan_phy_idx(wpan_phy_idx);
     91	if (!rdev)
     92		return NULL;
     93	return &rdev->wpan_phy;
     94}
     95
     96struct wpan_phy *
     97wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
     98{
     99	static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
    100	struct cfg802154_registered_device *rdev;
    101	size_t alloc_size;
    102
    103	alloc_size = sizeof(*rdev) + priv_size;
    104	rdev = kzalloc(alloc_size, GFP_KERNEL);
    105	if (!rdev)
    106		return NULL;
    107
    108	rdev->ops = ops;
    109
    110	rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
    111
    112	if (unlikely(rdev->wpan_phy_idx < 0)) {
    113		/* ugh, wrapped! */
    114		atomic_dec(&wpan_phy_counter);
    115		kfree(rdev);
    116		return NULL;
    117	}
    118
    119	/* atomic_inc_return makes it start at 1, make it start at 0 */
    120	rdev->wpan_phy_idx--;
    121
    122	INIT_LIST_HEAD(&rdev->wpan_dev_list);
    123	device_initialize(&rdev->wpan_phy.dev);
    124	dev_set_name(&rdev->wpan_phy.dev, PHY_NAME "%d", rdev->wpan_phy_idx);
    125
    126	rdev->wpan_phy.dev.class = &wpan_phy_class;
    127	rdev->wpan_phy.dev.platform_data = rdev;
    128
    129	wpan_phy_net_set(&rdev->wpan_phy, &init_net);
    130
    131	init_waitqueue_head(&rdev->dev_wait);
    132
    133	return &rdev->wpan_phy;
    134}
    135EXPORT_SYMBOL(wpan_phy_new);
    136
    137int wpan_phy_register(struct wpan_phy *phy)
    138{
    139	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
    140	int ret;
    141
    142	rtnl_lock();
    143	ret = device_add(&phy->dev);
    144	if (ret) {
    145		rtnl_unlock();
    146		return ret;
    147	}
    148
    149	list_add_rcu(&rdev->list, &cfg802154_rdev_list);
    150	cfg802154_rdev_list_generation++;
    151
    152	/* TODO phy registered lock */
    153	rtnl_unlock();
    154
    155	/* TODO nl802154 phy notify */
    156
    157	return 0;
    158}
    159EXPORT_SYMBOL(wpan_phy_register);
    160
    161void wpan_phy_unregister(struct wpan_phy *phy)
    162{
    163	struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
    164
    165	wait_event(rdev->dev_wait, ({
    166		int __count;
    167		rtnl_lock();
    168		__count = rdev->opencount;
    169		rtnl_unlock();
    170		__count == 0; }));
    171
    172	rtnl_lock();
    173	/* TODO nl802154 phy notify */
    174	/* TODO phy registered lock */
    175
    176	WARN_ON(!list_empty(&rdev->wpan_dev_list));
    177
    178	/* First remove the hardware from everywhere, this makes
    179	 * it impossible to find from userspace.
    180	 */
    181	list_del_rcu(&rdev->list);
    182	synchronize_rcu();
    183
    184	cfg802154_rdev_list_generation++;
    185
    186	device_del(&phy->dev);
    187
    188	rtnl_unlock();
    189}
    190EXPORT_SYMBOL(wpan_phy_unregister);
    191
    192void wpan_phy_free(struct wpan_phy *phy)
    193{
    194	put_device(&phy->dev);
    195}
    196EXPORT_SYMBOL(wpan_phy_free);
    197
    198int cfg802154_switch_netns(struct cfg802154_registered_device *rdev,
    199			   struct net *net)
    200{
    201	struct wpan_dev *wpan_dev;
    202	int err = 0;
    203
    204	list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
    205		if (!wpan_dev->netdev)
    206			continue;
    207		wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
    208		err = dev_change_net_namespace(wpan_dev->netdev, net, "wpan%d");
    209		if (err)
    210			break;
    211		wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
    212	}
    213
    214	if (err) {
    215		/* failed -- clean up to old netns */
    216		net = wpan_phy_net(&rdev->wpan_phy);
    217
    218		list_for_each_entry_continue_reverse(wpan_dev,
    219						     &rdev->wpan_dev_list,
    220						     list) {
    221			if (!wpan_dev->netdev)
    222				continue;
    223			wpan_dev->netdev->features &= ~NETIF_F_NETNS_LOCAL;
    224			err = dev_change_net_namespace(wpan_dev->netdev, net,
    225						       "wpan%d");
    226			WARN_ON(err);
    227			wpan_dev->netdev->features |= NETIF_F_NETNS_LOCAL;
    228		}
    229
    230		return err;
    231	}
    232
    233	wpan_phy_net_set(&rdev->wpan_phy, net);
    234
    235	err = device_rename(&rdev->wpan_phy.dev, dev_name(&rdev->wpan_phy.dev));
    236	WARN_ON(err);
    237
    238	return 0;
    239}
    240
    241void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
    242{
    243	kfree(rdev);
    244}
    245
    246static void
    247cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
    248			   int iftype, int num)
    249{
    250	ASSERT_RTNL();
    251
    252	rdev->num_running_ifaces += num;
    253}
    254
    255static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
    256					  unsigned long state, void *ptr)
    257{
    258	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
    259	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
    260	struct cfg802154_registered_device *rdev;
    261
    262	if (!wpan_dev)
    263		return NOTIFY_DONE;
    264
    265	rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
    266
    267	/* TODO WARN_ON unspec type */
    268
    269	switch (state) {
    270		/* TODO NETDEV_DEVTYPE */
    271	case NETDEV_REGISTER:
    272		dev->features |= NETIF_F_NETNS_LOCAL;
    273		wpan_dev->identifier = ++rdev->wpan_dev_id;
    274		list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
    275		rdev->devlist_generation++;
    276
    277		wpan_dev->netdev = dev;
    278		break;
    279	case NETDEV_DOWN:
    280		cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
    281
    282		rdev->opencount--;
    283		wake_up(&rdev->dev_wait);
    284		break;
    285	case NETDEV_UP:
    286		cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
    287
    288		rdev->opencount++;
    289		break;
    290	case NETDEV_UNREGISTER:
    291		/* It is possible to get NETDEV_UNREGISTER
    292		 * multiple times. To detect that, check
    293		 * that the interface is still on the list
    294		 * of registered interfaces, and only then
    295		 * remove and clean it up.
    296		 */
    297		if (!list_empty(&wpan_dev->list)) {
    298			list_del_rcu(&wpan_dev->list);
    299			rdev->devlist_generation++;
    300		}
    301		/* synchronize (so that we won't find this netdev
    302		 * from other code any more) and then clear the list
    303		 * head so that the above code can safely check for
    304		 * !list_empty() to avoid double-cleanup.
    305		 */
    306		synchronize_rcu();
    307		INIT_LIST_HEAD(&wpan_dev->list);
    308		break;
    309	default:
    310		return NOTIFY_DONE;
    311	}
    312
    313	return NOTIFY_OK;
    314}
    315
    316static struct notifier_block cfg802154_netdev_notifier = {
    317	.notifier_call = cfg802154_netdev_notifier_call,
    318};
    319
    320static void __net_exit cfg802154_pernet_exit(struct net *net)
    321{
    322	struct cfg802154_registered_device *rdev;
    323
    324	rtnl_lock();
    325	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
    326		if (net_eq(wpan_phy_net(&rdev->wpan_phy), net))
    327			WARN_ON(cfg802154_switch_netns(rdev, &init_net));
    328	}
    329	rtnl_unlock();
    330}
    331
    332static struct pernet_operations cfg802154_pernet_ops = {
    333	.exit = cfg802154_pernet_exit,
    334};
    335
    336static int __init wpan_phy_class_init(void)
    337{
    338	int rc;
    339
    340	rc = register_pernet_device(&cfg802154_pernet_ops);
    341	if (rc)
    342		goto err;
    343
    344	rc = wpan_phy_sysfs_init();
    345	if (rc)
    346		goto err_sysfs;
    347
    348	rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
    349	if (rc)
    350		goto err_nl;
    351
    352	rc = ieee802154_nl_init();
    353	if (rc)
    354		goto err_notifier;
    355
    356	rc = nl802154_init();
    357	if (rc)
    358		goto err_ieee802154_nl;
    359
    360	return 0;
    361
    362err_ieee802154_nl:
    363	ieee802154_nl_exit();
    364
    365err_notifier:
    366	unregister_netdevice_notifier(&cfg802154_netdev_notifier);
    367err_nl:
    368	wpan_phy_sysfs_exit();
    369err_sysfs:
    370	unregister_pernet_device(&cfg802154_pernet_ops);
    371err:
    372	return rc;
    373}
    374subsys_initcall(wpan_phy_class_init);
    375
    376static void __exit wpan_phy_class_exit(void)
    377{
    378	nl802154_exit();
    379	ieee802154_nl_exit();
    380	unregister_netdevice_notifier(&cfg802154_netdev_notifier);
    381	wpan_phy_sysfs_exit();
    382	unregister_pernet_device(&cfg802154_pernet_ops);
    383}
    384module_exit(wpan_phy_class_exit);
    385
    386MODULE_LICENSE("GPL v2");
    387MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
    388MODULE_AUTHOR("Dmitry Eremin-Solenikov");