xref: /linux/net/psp/psp_main.c (revision 6b46ca260e2290e3453d1355ab5b6d283d73d780)
100c94ca2SJakub Kicinski // SPDX-License-Identifier: GPL-2.0-only
200c94ca2SJakub Kicinski 
300c94ca2SJakub Kicinski #include <linux/list.h>
400c94ca2SJakub Kicinski #include <linux/netdevice.h>
500c94ca2SJakub Kicinski #include <linux/xarray.h>
600c94ca2SJakub Kicinski #include <net/net_namespace.h>
700c94ca2SJakub Kicinski #include <net/psp.h>
800c94ca2SJakub Kicinski 
900c94ca2SJakub Kicinski #include "psp.h"
1000c94ca2SJakub Kicinski #include "psp-nl-gen.h"
1100c94ca2SJakub Kicinski 
1200c94ca2SJakub Kicinski DEFINE_XARRAY_ALLOC1(psp_devs);
1300c94ca2SJakub Kicinski struct mutex psp_devs_lock;
1400c94ca2SJakub Kicinski 
1500c94ca2SJakub Kicinski /**
1600c94ca2SJakub Kicinski  * DOC: PSP locking
1700c94ca2SJakub Kicinski  *
1800c94ca2SJakub Kicinski  * psp_devs_lock protects the psp_devs xarray.
1900c94ca2SJakub Kicinski  * Ordering is take the psp_devs_lock and then the instance lock.
2000c94ca2SJakub Kicinski  * Each instance is protected by RCU, and has a refcount.
2100c94ca2SJakub Kicinski  * When driver unregisters the instance gets flushed, but struct sticks around.
2200c94ca2SJakub Kicinski  */
2300c94ca2SJakub Kicinski 
2400c94ca2SJakub Kicinski /**
2500c94ca2SJakub Kicinski  * psp_dev_check_access() - check if user in a given net ns can access PSP dev
2600c94ca2SJakub Kicinski  * @psd:	PSP device structure user is trying to access
2700c94ca2SJakub Kicinski  * @net:	net namespace user is in
2800c94ca2SJakub Kicinski  *
2900c94ca2SJakub Kicinski  * Return: 0 if PSP device should be visible in @net, errno otherwise.
3000c94ca2SJakub Kicinski  */
3100c94ca2SJakub Kicinski int psp_dev_check_access(struct psp_dev *psd, struct net *net)
3200c94ca2SJakub Kicinski {
3300c94ca2SJakub Kicinski 	if (dev_net(psd->main_netdev) == net)
3400c94ca2SJakub Kicinski 		return 0;
3500c94ca2SJakub Kicinski 	return -ENOENT;
3600c94ca2SJakub Kicinski }
3700c94ca2SJakub Kicinski 
3800c94ca2SJakub Kicinski /**
3900c94ca2SJakub Kicinski  * psp_dev_create() - create and register PSP device
4000c94ca2SJakub Kicinski  * @netdev:	main netdevice
4100c94ca2SJakub Kicinski  * @psd_ops:	driver callbacks
4200c94ca2SJakub Kicinski  * @psd_caps:	device capabilities
4300c94ca2SJakub Kicinski  * @priv_ptr:	back-pointer to driver private data
4400c94ca2SJakub Kicinski  *
4500c94ca2SJakub Kicinski  * Return: pointer to allocated PSP device, or ERR_PTR.
4600c94ca2SJakub Kicinski  */
4700c94ca2SJakub Kicinski struct psp_dev *
4800c94ca2SJakub Kicinski psp_dev_create(struct net_device *netdev,
4900c94ca2SJakub Kicinski 	       struct psp_dev_ops *psd_ops, struct psp_dev_caps *psd_caps,
5000c94ca2SJakub Kicinski 	       void *priv_ptr)
5100c94ca2SJakub Kicinski {
5200c94ca2SJakub Kicinski 	struct psp_dev *psd;
5300c94ca2SJakub Kicinski 	static u32 last_id;
5400c94ca2SJakub Kicinski 	int err;
5500c94ca2SJakub Kicinski 
5600c94ca2SJakub Kicinski 	if (WARN_ON(!psd_caps->versions ||
57117f02a4SJakub Kicinski 		    !psd_ops->set_config ||
58*6b46ca26SJakub Kicinski 		    !psd_ops->key_rotate ||
59*6b46ca26SJakub Kicinski 		    !psd_ops->rx_spi_alloc ||
60*6b46ca26SJakub Kicinski 		    !psd_ops->tx_key_add ||
61*6b46ca26SJakub Kicinski 		    !psd_ops->tx_key_del))
6200c94ca2SJakub Kicinski 		return ERR_PTR(-EINVAL);
6300c94ca2SJakub Kicinski 
6400c94ca2SJakub Kicinski 	psd = kzalloc(sizeof(*psd), GFP_KERNEL);
6500c94ca2SJakub Kicinski 	if (!psd)
6600c94ca2SJakub Kicinski 		return ERR_PTR(-ENOMEM);
6700c94ca2SJakub Kicinski 
6800c94ca2SJakub Kicinski 	psd->main_netdev = netdev;
6900c94ca2SJakub Kicinski 	psd->ops = psd_ops;
7000c94ca2SJakub Kicinski 	psd->caps = psd_caps;
7100c94ca2SJakub Kicinski 	psd->drv_priv = priv_ptr;
7200c94ca2SJakub Kicinski 
7300c94ca2SJakub Kicinski 	mutex_init(&psd->lock);
74*6b46ca26SJakub Kicinski 	INIT_LIST_HEAD(&psd->active_assocs);
7500c94ca2SJakub Kicinski 	refcount_set(&psd->refcnt, 1);
7600c94ca2SJakub Kicinski 
7700c94ca2SJakub Kicinski 	mutex_lock(&psp_devs_lock);
7800c94ca2SJakub Kicinski 	err = xa_alloc_cyclic(&psp_devs, &psd->id, psd, xa_limit_16b,
7900c94ca2SJakub Kicinski 			      &last_id, GFP_KERNEL);
8000c94ca2SJakub Kicinski 	if (err) {
8100c94ca2SJakub Kicinski 		mutex_unlock(&psp_devs_lock);
8200c94ca2SJakub Kicinski 		kfree(psd);
8300c94ca2SJakub Kicinski 		return ERR_PTR(err);
8400c94ca2SJakub Kicinski 	}
8500c94ca2SJakub Kicinski 	mutex_lock(&psd->lock);
8600c94ca2SJakub Kicinski 	mutex_unlock(&psp_devs_lock);
8700c94ca2SJakub Kicinski 
8800c94ca2SJakub Kicinski 	psp_nl_notify_dev(psd, PSP_CMD_DEV_ADD_NTF);
8900c94ca2SJakub Kicinski 
9000c94ca2SJakub Kicinski 	rcu_assign_pointer(netdev->psp_dev, psd);
9100c94ca2SJakub Kicinski 
9200c94ca2SJakub Kicinski 	mutex_unlock(&psd->lock);
9300c94ca2SJakub Kicinski 
9400c94ca2SJakub Kicinski 	return psd;
9500c94ca2SJakub Kicinski }
9600c94ca2SJakub Kicinski EXPORT_SYMBOL(psp_dev_create);
9700c94ca2SJakub Kicinski 
9800c94ca2SJakub Kicinski void psp_dev_destroy(struct psp_dev *psd)
9900c94ca2SJakub Kicinski {
10000c94ca2SJakub Kicinski 	mutex_lock(&psp_devs_lock);
10100c94ca2SJakub Kicinski 	xa_erase(&psp_devs, psd->id);
10200c94ca2SJakub Kicinski 	mutex_unlock(&psp_devs_lock);
10300c94ca2SJakub Kicinski 
10400c94ca2SJakub Kicinski 	mutex_destroy(&psd->lock);
10500c94ca2SJakub Kicinski 	kfree_rcu(psd, rcu);
10600c94ca2SJakub Kicinski }
10700c94ca2SJakub Kicinski 
10800c94ca2SJakub Kicinski /**
10900c94ca2SJakub Kicinski  * psp_dev_unregister() - unregister PSP device
11000c94ca2SJakub Kicinski  * @psd:	PSP device structure
11100c94ca2SJakub Kicinski  */
11200c94ca2SJakub Kicinski void psp_dev_unregister(struct psp_dev *psd)
11300c94ca2SJakub Kicinski {
114*6b46ca26SJakub Kicinski 	struct psp_assoc *pas, *next;
115*6b46ca26SJakub Kicinski 
11600c94ca2SJakub Kicinski 	mutex_lock(&psp_devs_lock);
11700c94ca2SJakub Kicinski 	mutex_lock(&psd->lock);
11800c94ca2SJakub Kicinski 
11900c94ca2SJakub Kicinski 	psp_nl_notify_dev(psd, PSP_CMD_DEV_DEL_NTF);
12000c94ca2SJakub Kicinski 
12100c94ca2SJakub Kicinski 	/* Wait until psp_dev_destroy() to call xa_erase() to prevent a
12200c94ca2SJakub Kicinski 	 * different psd from being added to the xarray with this id, while
12300c94ca2SJakub Kicinski 	 * there are still references to this psd being held.
12400c94ca2SJakub Kicinski 	 */
12500c94ca2SJakub Kicinski 	xa_store(&psp_devs, psd->id, NULL, GFP_KERNEL);
12600c94ca2SJakub Kicinski 	mutex_unlock(&psp_devs_lock);
12700c94ca2SJakub Kicinski 
128*6b46ca26SJakub Kicinski 	list_for_each_entry_safe(pas, next, &psd->active_assocs, assocs_list)
129*6b46ca26SJakub Kicinski 		psp_dev_tx_key_del(psd, pas);
130*6b46ca26SJakub Kicinski 
13100c94ca2SJakub Kicinski 	rcu_assign_pointer(psd->main_netdev->psp_dev, NULL);
13200c94ca2SJakub Kicinski 
13300c94ca2SJakub Kicinski 	psd->ops = NULL;
13400c94ca2SJakub Kicinski 	psd->drv_priv = NULL;
13500c94ca2SJakub Kicinski 
13600c94ca2SJakub Kicinski 	mutex_unlock(&psd->lock);
13700c94ca2SJakub Kicinski 
13800c94ca2SJakub Kicinski 	psp_dev_put(psd);
13900c94ca2SJakub Kicinski }
14000c94ca2SJakub Kicinski EXPORT_SYMBOL(psp_dev_unregister);
14100c94ca2SJakub Kicinski 
142*6b46ca26SJakub Kicinski unsigned int psp_key_size(u32 version)
143*6b46ca26SJakub Kicinski {
144*6b46ca26SJakub Kicinski 	switch (version) {
145*6b46ca26SJakub Kicinski 	case PSP_VERSION_HDR0_AES_GCM_128:
146*6b46ca26SJakub Kicinski 	case PSP_VERSION_HDR0_AES_GMAC_128:
147*6b46ca26SJakub Kicinski 		return 16;
148*6b46ca26SJakub Kicinski 	case PSP_VERSION_HDR0_AES_GCM_256:
149*6b46ca26SJakub Kicinski 	case PSP_VERSION_HDR0_AES_GMAC_256:
150*6b46ca26SJakub Kicinski 		return 32;
151*6b46ca26SJakub Kicinski 	default:
152*6b46ca26SJakub Kicinski 		return 0;
153*6b46ca26SJakub Kicinski 	}
154*6b46ca26SJakub Kicinski }
155*6b46ca26SJakub Kicinski EXPORT_SYMBOL(psp_key_size);
156*6b46ca26SJakub Kicinski 
15700c94ca2SJakub Kicinski static int __init psp_init(void)
15800c94ca2SJakub Kicinski {
15900c94ca2SJakub Kicinski 	mutex_init(&psp_devs_lock);
16000c94ca2SJakub Kicinski 
16100c94ca2SJakub Kicinski 	return genl_register_family(&psp_nl_family);
16200c94ca2SJakub Kicinski }
16300c94ca2SJakub Kicinski 
16400c94ca2SJakub Kicinski subsys_initcall(psp_init);
165