| 1 | |
| 2 |
|
| 3 |
|
| 4 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 5 |
|
| 6 | #include <linux/tsm.h>
|
| 7 | #include <linux/device.h>
|
| 8 | #include <linux/module.h>
|
| 9 | #include <linux/cleanup.h>
|
| 10 | #include <linux/pci-tsm.h>
|
| 11 |
|
| 12 | static void tsm_release(struct device *);
|
| 13 | static const struct class tsm_class = {
|
| 14 | .name = "tsm",
|
| 15 | .dev_release = tsm_release
|
| 16 | };
|
| 17 | static DEFINE_IDA(tsm_ida);
|
| 18 |
|
| 19 | static int match_id(struct device *dev, const void *data)
|
| 20 | {
|
| 21 | struct tsm_dev *tsm_dev = container_of(dev, struct tsm_dev, dev);
|
| 22 | int id = *(const int *)data;
|
| 23 |
|
| 24 | return tsm_dev->id == id;
|
| 25 | }
|
| 26 |
|
| 27 | struct tsm_dev *find_tsm_dev(int id)
|
| 28 | {
|
| 29 | struct device *dev = class_find_device(&tsm_class, NULL, &id, match_id);
|
| 30 |
|
| 31 | if (!dev)
|
| 32 | return NULL;
|
| 33 | return container_of(dev, struct tsm_dev, dev);
|
| 34 | }
|
| 35 |
|
| 36 | static struct tsm_dev *alloc_tsm_dev(struct device *parent)
|
| 37 | {
|
| 38 | struct device *dev;
|
| 39 | int id;
|
| 40 |
|
| 41 | struct tsm_dev *tsm_dev __free(kfree) =
|
| 42 | kzalloc_obj(*tsm_dev);
|
| 43 | if (!tsm_dev)
|
| 44 | return ERR_PTR(-ENOMEM);
|
| 45 |
|
| 46 | id = ida_alloc(&tsm_ida, GFP_KERNEL);
|
| 47 | if (id < 0)
|
| 48 | return ERR_PTR(id);
|
| 49 |
|
| 50 | tsm_dev->id = id;
|
| 51 | dev = &tsm_dev->dev;
|
| 52 | dev->parent = parent;
|
| 53 | dev->class = &tsm_class;
|
| 54 | device_initialize(dev);
|
| 55 |
|
| 56 | return no_free_ptr(tsm_dev);
|
| 57 | }
|
| 58 |
|
| 59 | static struct tsm_dev *tsm_register_pci_or_reset(struct tsm_dev *tsm_dev,
|
| 60 | struct pci_tsm_ops *pci_ops)
|
| 61 | {
|
| 62 | int rc;
|
| 63 |
|
| 64 | if (!pci_ops)
|
| 65 | return tsm_dev;
|
| 66 |
|
| 67 | tsm_dev->pci_ops = pci_ops;
|
| 68 | rc = pci_tsm_register(tsm_dev);
|
| 69 | if (rc) {
|
| 70 | dev_err(tsm_dev->dev.parent,
|
| 71 | "PCI/TSM registration failure: %d\n", rc);
|
| 72 | device_unregister(&tsm_dev->dev);
|
| 73 | return ERR_PTR(rc);
|
| 74 | }
|
| 75 |
|
| 76 |
|
| 77 | kobject_uevent(&tsm_dev->dev.kobj, KOBJ_CHANGE);
|
| 78 | return tsm_dev;
|
| 79 | }
|
| 80 |
|
| 81 | struct tsm_dev *tsm_register(struct device *parent, struct pci_tsm_ops *pci_ops)
|
| 82 | {
|
| 83 | struct tsm_dev *tsm_dev __free(put_tsm_dev) = alloc_tsm_dev(parent);
|
| 84 | struct device *dev;
|
| 85 | int rc;
|
| 86 |
|
| 87 | if (IS_ERR(tsm_dev))
|
| 88 | return tsm_dev;
|
| 89 |
|
| 90 | dev = &tsm_dev->dev;
|
| 91 | rc = dev_set_name(dev, "tsm%d", tsm_dev->id);
|
| 92 | if (rc)
|
| 93 | return ERR_PTR(rc);
|
| 94 |
|
| 95 | rc = device_add(dev);
|
| 96 | if (rc)
|
| 97 | return ERR_PTR(rc);
|
| 98 |
|
| 99 | return tsm_register_pci_or_reset(no_free_ptr(tsm_dev), pci_ops);
|
| 100 | }
|
| 101 | EXPORT_SYMBOL_GPL(tsm_register);
|
| 102 |
|
| 103 | void tsm_unregister(struct tsm_dev *tsm_dev)
|
| 104 | {
|
| 105 | if (tsm_dev->pci_ops)
|
| 106 | pci_tsm_unregister(tsm_dev);
|
| 107 | device_unregister(&tsm_dev->dev);
|
| 108 | }
|
| 109 | EXPORT_SYMBOL_GPL(tsm_unregister);
|
| 110 |
|
| 111 | static void tsm_release(struct device *dev)
|
| 112 | {
|
| 113 | struct tsm_dev *tsm_dev = container_of(dev, typeof(*tsm_dev), dev);
|
| 114 |
|
| 115 | ida_free(&tsm_ida, tsm_dev->id);
|
| 116 | kfree(tsm_dev);
|
| 117 | }
|
| 118 |
|
| 119 | static int __init tsm_init(void)
|
| 120 | {
|
| 121 | return class_register(&tsm_class);
|
| 122 | }
|
| 123 | module_init(tsm_init)
|
| 124 |
|
| 125 | static void __exit tsm_exit(void)
|
| 126 | {
|
| 127 | class_unregister(&tsm_class);
|
| 128 | }
|
| 129 | module_exit(tsm_exit)
|
| 130 |
|
| 131 | MODULE_LICENSE("GPL");
|
| 132 | MODULE_DESCRIPTION("TEE Security Manager Class Device");
|