1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Intel MIC Platform Software Stack (MPSS)
4  *
5  * Copyright(c) 2015 Intel Corporation.
6  *
7  * Intel MIC Coprocessor State Management (COSM) Driver
8  */
9 
10 #include <linux/module.h>
11 #include <linux/delay.h>
12 #include <linux/idr.h>
13 #include <linux/slab.h>
14 #include <linux/cred.h>
15 #include "cosm_main.h"
16 
17 static const char cosm_driver_name[] = "mic";
18 
19 /* COSM ID allocator */
20 static struct ida g_cosm_ida;
21 /* Class of MIC devices for sysfs accessibility. */
22 static struct class *g_cosm_class;
23 /* Number of MIC devices */
24 static atomic_t g_num_dev;
25 
26 /**
27  * cosm_hw_reset - Issue a HW reset for the MIC device
28  * @cdev: pointer to cosm_device instance
29  */
cosm_hw_reset(struct cosm_device * cdev,bool force)30 static void cosm_hw_reset(struct cosm_device *cdev, bool force)
31 {
32 	int i;
33 
34 #define MIC_RESET_TO (45)
35 	if (force && cdev->hw_ops->force_reset)
36 		cdev->hw_ops->force_reset(cdev);
37 	else
38 		cdev->hw_ops->reset(cdev);
39 
40 	for (i = 0; i < MIC_RESET_TO; i++) {
41 		if (cdev->hw_ops->ready(cdev)) {
42 			cosm_set_state(cdev, MIC_READY);
43 			return;
44 		}
45 		/*
46 		 * Resets typically take 10s of seconds to complete.
47 		 * Since an MMIO read is required to check if the
48 		 * firmware is ready or not, a 1 second delay works nicely.
49 		 */
50 		msleep(1000);
51 	}
52 	cosm_set_state(cdev, MIC_RESET_FAILED);
53 }
54 
55 /**
56  * cosm_start - Start the MIC
57  * @cdev: pointer to cosm_device instance
58  *
59  * This function prepares an MIC for boot and initiates boot.
60  * RETURNS: An appropriate -ERRNO error value on error, or 0 for success.
61  */
cosm_start(struct cosm_device * cdev)62 int cosm_start(struct cosm_device *cdev)
63 {
64 	const struct cred *orig_cred;
65 	struct cred *override_cred;
66 	int rc;
67 
68 	mutex_lock(&cdev->cosm_mutex);
69 	if (!cdev->bootmode) {
70 		dev_err(&cdev->dev, "%s %d bootmode not set\n",
71 			__func__, __LINE__);
72 		rc = -EINVAL;
73 		goto unlock_ret;
74 	}
75 retry:
76 	if (cdev->state != MIC_READY) {
77 		dev_err(&cdev->dev, "%s %d MIC state not READY\n",
78 			__func__, __LINE__);
79 		rc = -EINVAL;
80 		goto unlock_ret;
81 	}
82 	if (!cdev->hw_ops->ready(cdev)) {
83 		cosm_hw_reset(cdev, false);
84 		/*
85 		 * The state will either be MIC_READY if the reset succeeded
86 		 * or MIC_RESET_FAILED if the firmware reset failed.
87 		 */
88 		goto retry;
89 	}
90 
91 	/*
92 	 * Set credentials to root to allow non-root user to download initramsfs
93 	 * with 600 permissions
94 	 */
95 	override_cred = prepare_creds();
96 	if (!override_cred) {
97 		dev_err(&cdev->dev, "%s %d prepare_creds failed\n",
98 			__func__, __LINE__);
99 		rc = -ENOMEM;
100 		goto unlock_ret;
101 	}
102 	override_cred->fsuid = GLOBAL_ROOT_UID;
103 	orig_cred = override_creds(override_cred);
104 
105 	rc = cdev->hw_ops->start(cdev, cdev->index);
106 
107 	revert_creds(orig_cred);
108 	put_cred(override_cred);
109 	if (rc)
110 		goto unlock_ret;
111 
112 	/*
113 	 * If linux is being booted, card is treated 'online' only
114 	 * when the scif interface in the card is up. If anything else
115 	 * is booted, we set card to 'online' immediately.
116 	 */
117 	if (!strcmp(cdev->bootmode, "linux"))
118 		cosm_set_state(cdev, MIC_BOOTING);
119 	else
120 		cosm_set_state(cdev, MIC_ONLINE);
121 unlock_ret:
122 	mutex_unlock(&cdev->cosm_mutex);
123 	if (rc)
124 		dev_err(&cdev->dev, "cosm_start failed rc %d\n", rc);
125 	return rc;
126 }
127 
128 /**
129  * cosm_stop - Prepare the MIC for reset and trigger reset
130  * @cdev: pointer to cosm_device instance
131  * @force: force a MIC to reset even if it is already reset and ready.
132  *
133  * RETURNS: None
134  */
cosm_stop(struct cosm_device * cdev,bool force)135 void cosm_stop(struct cosm_device *cdev, bool force)
136 {
137 	mutex_lock(&cdev->cosm_mutex);
138 	if (cdev->state != MIC_READY || force) {
139 		/*
140 		 * Don't call hw_ops if they have been called previously.
141 		 * stop(..) calls device_unregister and will crash the system if
142 		 * called multiple times.
143 		 */
144 		u8 state = cdev->state == MIC_RESETTING ?
145 					cdev->prev_state : cdev->state;
146 		bool call_hw_ops = state != MIC_RESET_FAILED &&
147 					state != MIC_READY;
148 
149 		if (cdev->state != MIC_RESETTING)
150 			cosm_set_state(cdev, MIC_RESETTING);
151 		cdev->heartbeat_watchdog_enable = false;
152 		if (call_hw_ops)
153 			cdev->hw_ops->stop(cdev, force);
154 		cosm_hw_reset(cdev, force);
155 		cosm_set_shutdown_status(cdev, MIC_NOP);
156 		if (call_hw_ops && cdev->hw_ops->post_reset)
157 			cdev->hw_ops->post_reset(cdev, cdev->state);
158 	}
159 	mutex_unlock(&cdev->cosm_mutex);
160 	flush_work(&cdev->scif_work);
161 }
162 
163 /**
164  * cosm_reset_trigger_work - Trigger MIC reset
165  * @work: The work structure
166  *
167  * This work is scheduled whenever the host wants to reset the MIC.
168  */
cosm_reset_trigger_work(struct work_struct * work)169 static void cosm_reset_trigger_work(struct work_struct *work)
170 {
171 	struct cosm_device *cdev = container_of(work, struct cosm_device,
172 						reset_trigger_work);
173 	cosm_stop(cdev, false);
174 }
175 
176 /**
177  * cosm_reset - Schedule MIC reset
178  * @cdev: pointer to cosm_device instance
179  *
180  * RETURNS: An -EINVAL if the card is already READY or 0 for success.
181  */
cosm_reset(struct cosm_device * cdev)182 int cosm_reset(struct cosm_device *cdev)
183 {
184 	int rc = 0;
185 
186 	mutex_lock(&cdev->cosm_mutex);
187 	if (cdev->state != MIC_READY) {
188 		if (cdev->state != MIC_RESETTING) {
189 			cdev->prev_state = cdev->state;
190 			cosm_set_state(cdev, MIC_RESETTING);
191 			schedule_work(&cdev->reset_trigger_work);
192 		}
193 	} else {
194 		dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__);
195 		rc = -EINVAL;
196 	}
197 	mutex_unlock(&cdev->cosm_mutex);
198 	return rc;
199 }
200 
201 /**
202  * cosm_shutdown - Initiate MIC shutdown.
203  * @cdev: pointer to cosm_device instance
204  *
205  * RETURNS: None
206  */
cosm_shutdown(struct cosm_device * cdev)207 int cosm_shutdown(struct cosm_device *cdev)
208 {
209 	struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN };
210 	int rc = 0;
211 
212 	mutex_lock(&cdev->cosm_mutex);
213 	if (cdev->state != MIC_ONLINE) {
214 		rc = -EINVAL;
215 		dev_err(&cdev->dev, "%s %d skipping shutdown in state: %s\n",
216 			__func__, __LINE__, cosm_state_string[cdev->state]);
217 		goto err;
218 	}
219 
220 	if (!cdev->epd) {
221 		rc = -ENOTCONN;
222 		dev_err(&cdev->dev, "%s %d scif endpoint not connected rc %d\n",
223 			__func__, __LINE__, rc);
224 		goto err;
225 	}
226 
227 	rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
228 	if (rc < 0) {
229 		dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n",
230 			__func__, __LINE__, rc);
231 		goto err;
232 	}
233 	cdev->heartbeat_watchdog_enable = false;
234 	cosm_set_state(cdev, MIC_SHUTTING_DOWN);
235 	rc = 0;
236 err:
237 	mutex_unlock(&cdev->cosm_mutex);
238 	return rc;
239 }
240 
cosm_driver_probe(struct cosm_device * cdev)241 static int cosm_driver_probe(struct cosm_device *cdev)
242 {
243 	int rc;
244 
245 	/* Initialize SCIF server at first probe */
246 	if (atomic_add_return(1, &g_num_dev) == 1) {
247 		rc = cosm_scif_init();
248 		if (rc)
249 			goto scif_exit;
250 	}
251 	mutex_init(&cdev->cosm_mutex);
252 	INIT_WORK(&cdev->reset_trigger_work, cosm_reset_trigger_work);
253 	INIT_WORK(&cdev->scif_work, cosm_scif_work);
254 	cdev->sysfs_heartbeat_enable = true;
255 	cosm_sysfs_init(cdev);
256 	cdev->sdev = device_create_with_groups(g_cosm_class, cdev->dev.parent,
257 			       MKDEV(0, cdev->index), cdev, cdev->attr_group,
258 			       "mic%d", cdev->index);
259 	if (IS_ERR(cdev->sdev)) {
260 		rc = PTR_ERR(cdev->sdev);
261 		dev_err(&cdev->dev, "device_create_with_groups failed rc %d\n",
262 			rc);
263 		goto scif_exit;
264 	}
265 
266 	cdev->state_sysfs = sysfs_get_dirent(cdev->sdev->kobj.sd,
267 		"state");
268 	if (!cdev->state_sysfs) {
269 		rc = -ENODEV;
270 		dev_err(&cdev->dev, "sysfs_get_dirent failed rc %d\n", rc);
271 		goto destroy_device;
272 	}
273 	cosm_create_debug_dir(cdev);
274 	return 0;
275 destroy_device:
276 	device_destroy(g_cosm_class, MKDEV(0, cdev->index));
277 scif_exit:
278 	if (atomic_dec_and_test(&g_num_dev))
279 		cosm_scif_exit();
280 	return rc;
281 }
282 
cosm_driver_remove(struct cosm_device * cdev)283 static void cosm_driver_remove(struct cosm_device *cdev)
284 {
285 	cosm_delete_debug_dir(cdev);
286 	sysfs_put(cdev->state_sysfs);
287 	device_destroy(g_cosm_class, MKDEV(0, cdev->index));
288 	flush_work(&cdev->reset_trigger_work);
289 	cosm_stop(cdev, false);
290 	if (atomic_dec_and_test(&g_num_dev))
291 		cosm_scif_exit();
292 
293 	/* These sysfs entries might have allocated */
294 	kfree(cdev->cmdline);
295 	kfree(cdev->firmware);
296 	kfree(cdev->ramdisk);
297 	kfree(cdev->bootmode);
298 }
299 
cosm_suspend(struct device * dev)300 static int cosm_suspend(struct device *dev)
301 {
302 	struct cosm_device *cdev = dev_to_cosm(dev);
303 
304 	mutex_lock(&cdev->cosm_mutex);
305 	switch (cdev->state) {
306 	/**
307 	 * Suspend/freeze hooks in userspace have already shutdown the card.
308 	 * Card should be 'ready' in most cases. It is however possible that
309 	 * some userspace application initiated a boot. In those cases, we
310 	 * simply reset the card.
311 	 */
312 	case MIC_ONLINE:
313 	case MIC_BOOTING:
314 	case MIC_SHUTTING_DOWN:
315 		mutex_unlock(&cdev->cosm_mutex);
316 		cosm_stop(cdev, false);
317 		break;
318 	default:
319 		mutex_unlock(&cdev->cosm_mutex);
320 		break;
321 	}
322 	return 0;
323 }
324 
325 static const struct dev_pm_ops cosm_pm_ops = {
326 	.suspend = cosm_suspend,
327 	.freeze = cosm_suspend
328 };
329 
330 static struct cosm_driver cosm_driver = {
331 	.driver = {
332 		.name =  KBUILD_MODNAME,
333 		.owner = THIS_MODULE,
334 		.pm = &cosm_pm_ops,
335 	},
336 	.probe = cosm_driver_probe,
337 	.remove = cosm_driver_remove
338 };
339 
cosm_init(void)340 static int __init cosm_init(void)
341 {
342 	int ret;
343 
344 	cosm_init_debugfs();
345 
346 	g_cosm_class = class_create(THIS_MODULE, cosm_driver_name);
347 	if (IS_ERR(g_cosm_class)) {
348 		ret = PTR_ERR(g_cosm_class);
349 		pr_err("class_create failed ret %d\n", ret);
350 		goto cleanup_debugfs;
351 	}
352 
353 	ida_init(&g_cosm_ida);
354 	ret = cosm_register_driver(&cosm_driver);
355 	if (ret) {
356 		pr_err("cosm_register_driver failed ret %d\n", ret);
357 		goto ida_destroy;
358 	}
359 	return 0;
360 ida_destroy:
361 	ida_destroy(&g_cosm_ida);
362 	class_destroy(g_cosm_class);
363 cleanup_debugfs:
364 	cosm_exit_debugfs();
365 	return ret;
366 }
367 
cosm_exit(void)368 static void __exit cosm_exit(void)
369 {
370 	cosm_unregister_driver(&cosm_driver);
371 	ida_destroy(&g_cosm_ida);
372 	class_destroy(g_cosm_class);
373 	cosm_exit_debugfs();
374 }
375 
376 module_init(cosm_init);
377 module_exit(cosm_exit);
378 
379 MODULE_AUTHOR("Intel Corporation");
380 MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver");
381 MODULE_LICENSE("GPL v2");
382