root/sound/soc/sof/loader.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_ext_windows
  2. snd_sof_fw_parse_ext_data
  3. sof_get_windows
  4. sof_fw_ready
  5. snd_sof_parse_module_memcpy
  6. check_header
  7. load_modules
  8. snd_sof_load_firmware_raw
  9. snd_sof_load_firmware_memcpy
  10. snd_sof_load_firmware
  11. snd_sof_run_firmware
  12. snd_sof_fw_unload

   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 
  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 */
  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  */
  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 */
 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 */
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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);

/* [<][>][^][v][top][bottom][index][help] */