• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2 //
3 // This file is provided under a dual BSD/GPLv2 license.  When using or
4 // redistributing this file, you may do so under either license.
5 //
6 // Copyright(c) 2018 Intel Corporation. All rights reserved.
7 //
8 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
9 //
10 // Generic firmware loader.
11 //
12 
13 #include <linux/firmware.h>
14 #include <sound/sof.h>
15 #include "ops.h"
16 
get_ext_windows(struct snd_sof_dev * sdev,struct sof_ipc_ext_data_hdr * ext_hdr)17 static int get_ext_windows(struct snd_sof_dev *sdev,
18 			   struct sof_ipc_ext_data_hdr *ext_hdr)
19 {
20 	struct sof_ipc_window *w =
21 		container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
22 
23 	if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
24 		return -EINVAL;
25 
26 	/* keep a local copy of the data */
27 	sdev->info_window = kmemdup(w, struct_size(w, window, w->num_windows),
28 				    GFP_KERNEL);
29 	if (!sdev->info_window)
30 		return -ENOMEM;
31 
32 	return 0;
33 }
34 
35 /* parse the extended FW boot data structures from FW boot message */
snd_sof_fw_parse_ext_data(struct snd_sof_dev * sdev,u32 bar,u32 offset)36 int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
37 {
38 	struct sof_ipc_ext_data_hdr *ext_hdr;
39 	void *ext_data;
40 	int ret = 0;
41 
42 	ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
43 	if (!ext_data)
44 		return -ENOMEM;
45 
46 	/* get first header */
47 	snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
48 			       sizeof(*ext_hdr));
49 	ext_hdr = ext_data;
50 
51 	while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
52 		/* read in ext structure */
53 		offset += sizeof(*ext_hdr);
54 		snd_sof_dsp_block_read(sdev, bar, offset,
55 				   (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
56 				   ext_hdr->hdr.size - sizeof(*ext_hdr));
57 
58 		dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
59 			ext_hdr->type, ext_hdr->hdr.size);
60 
61 		/* process structure data */
62 		switch (ext_hdr->type) {
63 		case SOF_IPC_EXT_DMA_BUFFER:
64 			break;
65 		case SOF_IPC_EXT_WINDOW:
66 			ret = get_ext_windows(sdev, ext_hdr);
67 			break;
68 		default:
69 			dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n",
70 				 ext_hdr->type, ext_hdr->hdr.size);
71 			break;
72 		}
73 
74 		if (ret < 0) {
75 			dev_err(sdev->dev, "error: failed to parse ext data type %d\n",
76 				ext_hdr->type);
77 			break;
78 		}
79 
80 		/* move to next header */
81 		offset += ext_hdr->hdr.size;
82 		snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
83 				       sizeof(*ext_hdr));
84 		ext_hdr = ext_data;
85 	}
86 
87 	kfree(ext_data);
88 	return ret;
89 }
90 EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
91 
92 /*
93  * IPC Firmware ready.
94  */
sof_get_windows(struct snd_sof_dev * sdev)95 static void sof_get_windows(struct snd_sof_dev *sdev)
96 {
97 	struct sof_ipc_window_elem *elem;
98 	u32 outbox_offset = 0;
99 	u32 stream_offset = 0;
100 	u32 inbox_offset = 0;
101 	u32 outbox_size = 0;
102 	u32 stream_size = 0;
103 	u32 inbox_size = 0;
104 	int window_offset;
105 	int bar;
106 	int i;
107 
108 	if (!sdev->info_window) {
109 		dev_err(sdev->dev, "error: have no window info\n");
110 		return;
111 	}
112 
113 	bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
114 	if (bar < 0) {
115 		dev_err(sdev->dev, "error: have no bar mapping\n");
116 		return;
117 	}
118 
119 	for (i = 0; i < sdev->info_window->num_windows; i++) {
120 		elem = &sdev->info_window->window[i];
121 
122 		window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
123 		if (window_offset < 0) {
124 			dev_warn(sdev->dev, "warn: no offset for window %d\n",
125 				 elem->id);
126 			continue;
127 		}
128 
129 		switch (elem->type) {
130 		case SOF_IPC_REGION_UPBOX:
131 			inbox_offset = window_offset + elem->offset;
132 			inbox_size = elem->size;
133 			snd_sof_debugfs_io_item(sdev,
134 						sdev->bar[bar] +
135 						inbox_offset,
136 						elem->size, "inbox",
137 						SOF_DEBUGFS_ACCESS_D0_ONLY);
138 			break;
139 		case SOF_IPC_REGION_DOWNBOX:
140 			outbox_offset = window_offset + elem->offset;
141 			outbox_size = elem->size;
142 			snd_sof_debugfs_io_item(sdev,
143 						sdev->bar[bar] +
144 						outbox_offset,
145 						elem->size, "outbox",
146 						SOF_DEBUGFS_ACCESS_D0_ONLY);
147 			break;
148 		case SOF_IPC_REGION_TRACE:
149 			snd_sof_debugfs_io_item(sdev,
150 						sdev->bar[bar] +
151 						window_offset +
152 						elem->offset,
153 						elem->size, "etrace",
154 						SOF_DEBUGFS_ACCESS_D0_ONLY);
155 			break;
156 		case SOF_IPC_REGION_DEBUG:
157 			snd_sof_debugfs_io_item(sdev,
158 						sdev->bar[bar] +
159 						window_offset +
160 						elem->offset,
161 						elem->size, "debug",
162 						SOF_DEBUGFS_ACCESS_D0_ONLY);
163 			break;
164 		case SOF_IPC_REGION_STREAM:
165 			stream_offset = window_offset + elem->offset;
166 			stream_size = elem->size;
167 			snd_sof_debugfs_io_item(sdev,
168 						sdev->bar[bar] +
169 						stream_offset,
170 						elem->size, "stream",
171 						SOF_DEBUGFS_ACCESS_D0_ONLY);
172 			break;
173 		case SOF_IPC_REGION_REGS:
174 			snd_sof_debugfs_io_item(sdev,
175 						sdev->bar[bar] +
176 						window_offset +
177 						elem->offset,
178 						elem->size, "regs",
179 						SOF_DEBUGFS_ACCESS_D0_ONLY);
180 			break;
181 		case SOF_IPC_REGION_EXCEPTION:
182 			sdev->dsp_oops_offset = window_offset + elem->offset;
183 			snd_sof_debugfs_io_item(sdev,
184 						sdev->bar[bar] +
185 						window_offset +
186 						elem->offset,
187 						elem->size, "exception",
188 						SOF_DEBUGFS_ACCESS_D0_ONLY);
189 			break;
190 		default:
191 			dev_err(sdev->dev, "error: get illegal window info\n");
192 			return;
193 		}
194 	}
195 
196 	if (outbox_size == 0 || inbox_size == 0) {
197 		dev_err(sdev->dev, "error: get illegal mailbox window\n");
198 		return;
199 	}
200 
201 	snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size,
202 				 outbox_offset, outbox_size);
203 	sdev->stream_box.offset = stream_offset;
204 	sdev->stream_box.size = stream_size;
205 
206 	dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
207 		inbox_offset, inbox_size);
208 	dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
209 		outbox_offset, outbox_size);
210 	dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
211 		stream_offset, stream_size);
212 }
213 
214 /* check for ABI compatibility and create memory windows on first boot */
sof_fw_ready(struct snd_sof_dev * sdev,u32 msg_id)215 int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
216 {
217 	struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
218 	int offset;
219 	int bar;
220 	int ret;
221 
222 	/* mailbox must be on 4k boundary */
223 	offset = snd_sof_dsp_get_mailbox_offset(sdev);
224 	if (offset < 0) {
225 		dev_err(sdev->dev, "error: have no mailbox offset\n");
226 		return offset;
227 	}
228 
229 	bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
230 	if (bar < 0) {
231 		dev_err(sdev->dev, "error: have no bar mapping\n");
232 		return -EINVAL;
233 	}
234 
235 	dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n",
236 		msg_id, offset);
237 
238 	/* no need to re-check version/ABI for subsequent boots */
239 	if (!sdev->first_boot)
240 		return 0;
241 
242 	/* copy data from the DSP FW ready offset */
243 	sof_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready));
244 
245 	/* make sure ABI version is compatible */
246 	ret = snd_sof_ipc_valid(sdev);
247 	if (ret < 0)
248 		return ret;
249 
250 	/* now check for extended data */
251 	snd_sof_fw_parse_ext_data(sdev, bar, offset +
252 				  sizeof(struct sof_ipc_fw_ready));
253 
254 	sof_get_windows(sdev);
255 
256 	return 0;
257 }
258 EXPORT_SYMBOL(sof_fw_ready);
259 
260 /* generic module parser for mmaped DSPs */
snd_sof_parse_module_memcpy(struct snd_sof_dev * sdev,struct snd_sof_mod_hdr * module)261 int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
262 				struct snd_sof_mod_hdr *module)
263 {
264 	struct snd_sof_blk_hdr *block;
265 	int count, bar;
266 	u32 offset;
267 	size_t remaining;
268 
269 	dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n",
270 		module->size, module->num_blocks, module->type);
271 
272 	block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
273 
274 	/* module->size doesn't include header size */
275 	remaining = module->size;
276 	for (count = 0; count < module->num_blocks; count++) {
277 		/* check for wrap */
278 		if (remaining < sizeof(*block)) {
279 			dev_err(sdev->dev, "error: not enough data remaining\n");
280 			return -EINVAL;
281 		}
282 
283 		/* minus header size of block */
284 		remaining -= sizeof(*block);
285 
286 		if (block->size == 0) {
287 			dev_warn(sdev->dev,
288 				 "warning: block %d size zero\n", count);
289 			dev_warn(sdev->dev, " type 0x%x offset 0x%x\n",
290 				 block->type, block->offset);
291 			continue;
292 		}
293 
294 		switch (block->type) {
295 		case SOF_FW_BLK_TYPE_RSRVD0:
296 		case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
297 			continue;	/* not handled atm */
298 		case SOF_FW_BLK_TYPE_IRAM:
299 		case SOF_FW_BLK_TYPE_DRAM:
300 		case SOF_FW_BLK_TYPE_SRAM:
301 			offset = block->offset;
302 			bar = snd_sof_dsp_get_bar_index(sdev, block->type);
303 			if (bar < 0) {
304 				dev_err(sdev->dev,
305 					"error: no BAR mapping for block type 0x%x\n",
306 					block->type);
307 				return bar;
308 			}
309 			break;
310 		default:
311 			dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n",
312 				block->type, count);
313 			return -EINVAL;
314 		}
315 
316 		dev_dbg(sdev->dev,
317 			"block %d type 0x%x size 0x%x ==>  offset 0x%x\n",
318 			count, block->type, block->size, offset);
319 
320 		/* checking block->size to avoid unaligned access */
321 		if (block->size % sizeof(u32)) {
322 			dev_err(sdev->dev, "error: invalid block size 0x%x\n",
323 				block->size);
324 			return -EINVAL;
325 		}
326 		snd_sof_dsp_block_write(sdev, bar, offset,
327 					block + 1, block->size);
328 
329 		if (remaining < block->size) {
330 			dev_err(sdev->dev, "error: not enough data remaining\n");
331 			return -EINVAL;
332 		}
333 
334 		/* minus body size of block */
335 		remaining -= block->size;
336 		/* next block */
337 		block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
338 			+ block->size);
339 	}
340 
341 	return 0;
342 }
343 EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
344 
check_header(struct snd_sof_dev * sdev,const struct firmware * fw)345 static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw)
346 {
347 	struct snd_sof_fw_header *header;
348 
349 	/* Read the header information from the data pointer */
350 	header = (struct snd_sof_fw_header *)fw->data;
351 
352 	/* verify FW sig */
353 	if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
354 		dev_err(sdev->dev, "error: invalid firmware signature\n");
355 		return -EINVAL;
356 	}
357 
358 	/* check size is valid */
359 	if (fw->size != header->file_size + sizeof(*header)) {
360 		dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
361 			fw->size, header->file_size + sizeof(*header));
362 		return -EINVAL;
363 	}
364 
365 	dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
366 		header->file_size, header->num_modules,
367 		header->abi, sizeof(*header));
368 
369 	return 0;
370 }
371 
load_modules(struct snd_sof_dev * sdev,const struct firmware * fw)372 static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw)
373 {
374 	struct snd_sof_fw_header *header;
375 	struct snd_sof_mod_hdr *module;
376 	int (*load_module)(struct snd_sof_dev *sof_dev,
377 			   struct snd_sof_mod_hdr *hdr);
378 	int ret, count;
379 	size_t remaining;
380 
381 	header = (struct snd_sof_fw_header *)fw->data;
382 	load_module = sof_ops(sdev)->load_module;
383 	if (!load_module)
384 		return -EINVAL;
385 
386 	/* parse each module */
387 	module = (struct snd_sof_mod_hdr *)((u8 *)(fw->data) + sizeof(*header));
388 	remaining = fw->size - sizeof(*header);
389 	/* check for wrap */
390 	if (remaining > fw->size) {
391 		dev_err(sdev->dev, "error: fw size smaller than header size\n");
392 		return -EINVAL;
393 	}
394 
395 	for (count = 0; count < header->num_modules; count++) {
396 		/* check for wrap */
397 		if (remaining < sizeof(*module)) {
398 			dev_err(sdev->dev, "error: not enough data remaining\n");
399 			return -EINVAL;
400 		}
401 
402 		/* minus header size of module */
403 		remaining -= sizeof(*module);
404 
405 		/* module */
406 		ret = load_module(sdev, module);
407 		if (ret < 0) {
408 			dev_err(sdev->dev, "error: invalid module %d\n", count);
409 			return ret;
410 		}
411 
412 		if (remaining < module->size) {
413 			dev_err(sdev->dev, "error: not enough data remaining\n");
414 			return -EINVAL;
415 		}
416 
417 		/* minus body size of module */
418 		remaining -=  module->size;
419 		module = (struct snd_sof_mod_hdr *)((u8 *)module
420 			+ sizeof(*module) + module->size);
421 	}
422 
423 	return 0;
424 }
425 
snd_sof_load_firmware_raw(struct snd_sof_dev * sdev)426 int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
427 {
428 	struct snd_sof_pdata *plat_data = sdev->pdata;
429 	const char *fw_filename;
430 	int ret;
431 
432 	/* set code loading condition to true */
433 	sdev->code_loading = 1;
434 
435 	/* Don't request firmware again if firmware is already requested */
436 	if (plat_data->fw)
437 		return 0;
438 
439 	fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
440 				plat_data->fw_filename_prefix,
441 				plat_data->fw_filename);
442 	if (!fw_filename)
443 		return -ENOMEM;
444 
445 	ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev);
446 
447 	if (ret < 0) {
448 		dev_err(sdev->dev, "error: request firmware %s failed err: %d\n",
449 			fw_filename, ret);
450 	}
451 
452 	kfree(fw_filename);
453 
454 	return ret;
455 }
456 EXPORT_SYMBOL(snd_sof_load_firmware_raw);
457 
snd_sof_load_firmware_memcpy(struct snd_sof_dev * sdev)458 int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
459 {
460 	struct snd_sof_pdata *plat_data = sdev->pdata;
461 	int ret;
462 
463 	ret = snd_sof_load_firmware_raw(sdev);
464 	if (ret < 0)
465 		return ret;
466 
467 	/* make sure the FW header and file is valid */
468 	ret = check_header(sdev, plat_data->fw);
469 	if (ret < 0) {
470 		dev_err(sdev->dev, "error: invalid FW header\n");
471 		goto error;
472 	}
473 
474 	/* prepare the DSP for FW loading */
475 	ret = snd_sof_dsp_reset(sdev);
476 	if (ret < 0) {
477 		dev_err(sdev->dev, "error: failed to reset DSP\n");
478 		goto error;
479 	}
480 
481 	/* parse and load firmware modules to DSP */
482 	ret = load_modules(sdev, plat_data->fw);
483 	if (ret < 0) {
484 		dev_err(sdev->dev, "error: invalid FW modules\n");
485 		goto error;
486 	}
487 
488 	return 0;
489 
490 error:
491 	release_firmware(plat_data->fw);
492 	plat_data->fw = NULL;
493 	return ret;
494 
495 }
496 EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
497 
snd_sof_load_firmware(struct snd_sof_dev * sdev)498 int snd_sof_load_firmware(struct snd_sof_dev *sdev)
499 {
500 	dev_dbg(sdev->dev, "loading firmware\n");
501 
502 	if (sof_ops(sdev)->load_firmware)
503 		return sof_ops(sdev)->load_firmware(sdev);
504 	return 0;
505 }
506 EXPORT_SYMBOL(snd_sof_load_firmware);
507 
snd_sof_run_firmware(struct snd_sof_dev * sdev)508 int snd_sof_run_firmware(struct snd_sof_dev *sdev)
509 {
510 	int ret;
511 	int init_core_mask;
512 
513 	init_waitqueue_head(&sdev->boot_wait);
514 
515 	/* create read-only fw_version debugfs to store boot version info */
516 	if (sdev->first_boot) {
517 		ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
518 					       sizeof(sdev->fw_version),
519 					       "fw_version", 0444);
520 		/* errors are only due to memory allocation, not debugfs */
521 		if (ret < 0) {
522 			dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
523 			return ret;
524 		}
525 	}
526 
527 	/* perform pre fw run operations */
528 	ret = snd_sof_dsp_pre_fw_run(sdev);
529 	if (ret < 0) {
530 		dev_err(sdev->dev, "error: failed pre fw run op\n");
531 		return ret;
532 	}
533 
534 	dev_dbg(sdev->dev, "booting DSP firmware\n");
535 
536 	/* boot the firmware on the DSP */
537 	ret = snd_sof_dsp_run(sdev);
538 	if (ret < 0) {
539 		dev_err(sdev->dev, "error: failed to reset DSP\n");
540 		return ret;
541 	}
542 
543 	init_core_mask = ret;
544 
545 	/*
546 	 * now wait for the DSP to boot. There are 3 possible outcomes:
547 	 * 1. Boot wait times out indicating FW boot failure.
548 	 * 2. FW boots successfully and fw_ready op succeeds.
549 	 * 3. FW boots but fw_ready op fails.
550 	 */
551 	ret = wait_event_timeout(sdev->boot_wait,
552 				 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
553 				 msecs_to_jiffies(sdev->boot_timeout));
554 	if (ret == 0) {
555 		dev_err(sdev->dev, "error: firmware boot failure\n");
556 		snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX |
557 			SOF_DBG_TEXT | SOF_DBG_PCI);
558 		sdev->fw_state = SOF_FW_BOOT_FAILED;
559 		return -EIO;
560 	}
561 
562 	if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
563 		dev_info(sdev->dev, "firmware boot complete\n");
564 	else
565 		return -EIO; /* FW boots but fw_ready op failed */
566 
567 	/* perform post fw run operations */
568 	ret = snd_sof_dsp_post_fw_run(sdev);
569 	if (ret < 0) {
570 		dev_err(sdev->dev, "error: failed post fw run op\n");
571 		return ret;
572 	}
573 
574 	/* fw boot is complete. Update the active cores mask */
575 	sdev->enabled_cores_mask = init_core_mask;
576 
577 	return 0;
578 }
579 EXPORT_SYMBOL(snd_sof_run_firmware);
580 
snd_sof_fw_unload(struct snd_sof_dev * sdev)581 void snd_sof_fw_unload(struct snd_sof_dev *sdev)
582 {
583 	/* TODO: support module unloading at runtime */
584 }
585 EXPORT_SYMBOL(snd_sof_fw_unload);
586