linux/sound/soc/sof/loader.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only 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 <sound/sof/ext_manifest.h>
  16#include "ops.h"
  17
  18static int get_ext_windows(struct snd_sof_dev *sdev,
  19                           const struct sof_ipc_ext_data_hdr *ext_hdr)
  20{
  21        const struct sof_ipc_window *w =
  22                container_of(ext_hdr, struct sof_ipc_window, ext_hdr);
  23
  24        if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS)
  25                return -EINVAL;
  26
  27        if (sdev->info_window) {
  28                if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) {
  29                        dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox");
  30                        return -EINVAL;
  31                }
  32                return 0;
  33        }
  34
  35        /* keep a local copy of the data */
  36        sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size,
  37                                         GFP_KERNEL);
  38        if (!sdev->info_window)
  39                return -ENOMEM;
  40
  41        return 0;
  42}
  43
  44static int get_cc_info(struct snd_sof_dev *sdev,
  45                       const struct sof_ipc_ext_data_hdr *ext_hdr)
  46{
  47        int ret;
  48
  49        const struct sof_ipc_cc_version *cc =
  50                container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr);
  51
  52        if (sdev->cc_version) {
  53                if (memcmp(sdev->cc_version, cc, cc->ext_hdr.hdr.size)) {
  54                        dev_err(sdev->dev, "error: receive diverged cc_version descriptions");
  55                        return -EINVAL;
  56                }
  57                return 0;
  58        }
  59
  60        dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n",
  61                cc->name, cc->major, cc->minor, cc->micro, cc->desc,
  62                cc->optim);
  63
  64        /* create read-only cc_version debugfs to store compiler version info */
  65        /* use local copy of the cc_version to prevent data corruption */
  66        if (sdev->first_boot) {
  67                sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size,
  68                                                GFP_KERNEL);
  69
  70                if (!sdev->cc_version)
  71                        return -ENOMEM;
  72
  73                memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size);
  74                ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version,
  75                                               cc->ext_hdr.hdr.size,
  76                                               "cc_version", 0444);
  77
  78                /* errors are only due to memory allocation, not debugfs */
  79                if (ret < 0) {
  80                        dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
  81                        return ret;
  82                }
  83        }
  84
  85        return 0;
  86}
  87
  88/* parse the extended FW boot data structures from FW boot message */
  89int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset)
  90{
  91        struct sof_ipc_ext_data_hdr *ext_hdr;
  92        void *ext_data;
  93        int ret = 0;
  94
  95        ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL);
  96        if (!ext_data)
  97                return -ENOMEM;
  98
  99        /* get first header */
 100        snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
 101                               sizeof(*ext_hdr));
 102        ext_hdr = ext_data;
 103
 104        while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) {
 105                /* read in ext structure */
 106                snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr),
 107                                   (void *)((u8 *)ext_data + sizeof(*ext_hdr)),
 108                                   ext_hdr->hdr.size - sizeof(*ext_hdr));
 109
 110                dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n",
 111                        ext_hdr->type, ext_hdr->hdr.size);
 112
 113                /* process structure data */
 114                switch (ext_hdr->type) {
 115                case SOF_IPC_EXT_WINDOW:
 116                        ret = get_ext_windows(sdev, ext_hdr);
 117                        break;
 118                case SOF_IPC_EXT_CC_INFO:
 119                        ret = get_cc_info(sdev, ext_hdr);
 120                        break;
 121                case SOF_IPC_EXT_UNUSED:
 122                case SOF_IPC_EXT_PROBE_INFO:
 123                case SOF_IPC_EXT_USER_ABI_INFO:
 124                        /* They are supported but we don't do anything here */
 125                        break;
 126                default:
 127                        dev_info(sdev->dev, "unknown ext header type %d size 0x%x\n",
 128                                 ext_hdr->type, ext_hdr->hdr.size);
 129                        ret = 0;
 130                        break;
 131                }
 132
 133                if (ret < 0) {
 134                        dev_err(sdev->dev, "error: failed to parse ext data type %d\n",
 135                                ext_hdr->type);
 136                        break;
 137                }
 138
 139                /* move to next header */
 140                offset += ext_hdr->hdr.size;
 141                snd_sof_dsp_block_read(sdev, bar, offset, ext_data,
 142                                       sizeof(*ext_hdr));
 143                ext_hdr = ext_data;
 144        }
 145
 146        kfree(ext_data);
 147        return ret;
 148}
 149EXPORT_SYMBOL(snd_sof_fw_parse_ext_data);
 150
 151static int ext_man_get_fw_version(struct snd_sof_dev *sdev,
 152                                  const struct sof_ext_man_elem_header *hdr)
 153{
 154        const struct sof_ext_man_fw_version *v =
 155                container_of(hdr, struct sof_ext_man_fw_version, hdr);
 156
 157        memcpy(&sdev->fw_ready.version, &v->version, sizeof(v->version));
 158        sdev->fw_ready.flags = v->flags;
 159
 160        /* log ABI versions and check FW compatibility */
 161        return snd_sof_ipc_valid(sdev);
 162}
 163
 164static int ext_man_get_windows(struct snd_sof_dev *sdev,
 165                               const struct sof_ext_man_elem_header *hdr)
 166{
 167        const struct sof_ext_man_window *w;
 168
 169        w = container_of(hdr, struct sof_ext_man_window, hdr);
 170
 171        return get_ext_windows(sdev, &w->ipc_window.ext_hdr);
 172}
 173
 174static int ext_man_get_cc_info(struct snd_sof_dev *sdev,
 175                               const struct sof_ext_man_elem_header *hdr)
 176{
 177        const struct sof_ext_man_cc_version *cc;
 178
 179        cc = container_of(hdr, struct sof_ext_man_cc_version, hdr);
 180
 181        return get_cc_info(sdev, &cc->cc_version.ext_hdr);
 182}
 183
 184static int ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev,
 185                                    const struct sof_ext_man_elem_header *hdr)
 186{
 187        const struct ext_man_dbg_abi *dbg_abi =
 188                container_of(hdr, struct ext_man_dbg_abi, hdr);
 189
 190        if (sdev->first_boot)
 191                dev_dbg(sdev->dev,
 192                        "Firmware: DBG_ABI %d:%d:%d\n",
 193                        SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version),
 194                        SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version),
 195                        SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version));
 196
 197        return 0;
 198}
 199
 200static int ext_man_get_config_data(struct snd_sof_dev *sdev,
 201                                   const struct sof_ext_man_elem_header *hdr)
 202{
 203        const struct sof_ext_man_config_data *config =
 204                container_of(hdr, struct sof_ext_man_config_data, hdr);
 205        const struct sof_config_elem *elem;
 206        int elems_counter;
 207        int elems_size;
 208        int ret = 0;
 209        int i;
 210
 211        /* calculate elements counter */
 212        elems_size = config->hdr.size - sizeof(struct sof_ext_man_elem_header);
 213        elems_counter = elems_size / sizeof(struct sof_config_elem);
 214
 215        dev_dbg(sdev->dev, "%s can hold up to %d config elements\n",
 216                __func__, elems_counter);
 217
 218        for (i = 0; i < elems_counter; ++i) {
 219                elem = &config->elems[i];
 220                dev_dbg(sdev->dev, "%s get index %d token %d val %d\n",
 221                        __func__, i, elem->token, elem->value);
 222                switch (elem->token) {
 223                case SOF_EXT_MAN_CONFIG_EMPTY:
 224                        /* unused memory space is zero filled - mapped to EMPTY elements */
 225                        break;
 226                case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE:
 227                        /* TODO: use ipc msg size from config data */
 228                        break;
 229                case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN:
 230                        if (sdev->first_boot && elem->value)
 231                                ret = snd_sof_dbg_memory_info_init(sdev);
 232                        break;
 233                default:
 234                        dev_info(sdev->dev, "Unknown firmware configuration token %d value %d",
 235                                 elem->token, elem->value);
 236                        break;
 237                }
 238                if (ret < 0) {
 239                        dev_err(sdev->dev, "error: processing sof_ext_man_config_data failed for token %d value 0x%x, %d\n",
 240                                elem->token, elem->value, ret);
 241                        return ret;
 242                }
 243        }
 244
 245        return 0;
 246}
 247
 248static ssize_t snd_sof_ext_man_size(const struct firmware *fw)
 249{
 250        const struct sof_ext_man_header *head;
 251
 252        head = (struct sof_ext_man_header *)fw->data;
 253
 254        /*
 255         * assert fw size is big enough to contain extended manifest header,
 256         * it prevents from reading unallocated memory from `head` in following
 257         * step.
 258         */
 259        if (fw->size < sizeof(*head))
 260                return -EINVAL;
 261
 262        /*
 263         * When fw points to extended manifest,
 264         * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER.
 265         */
 266        if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER)
 267                return head->full_size;
 268
 269        /* otherwise given fw don't have an extended manifest */
 270        return 0;
 271}
 272
 273/* parse extended FW manifest data structures */
 274static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev,
 275                                    const struct firmware *fw)
 276{
 277        const struct sof_ext_man_elem_header *elem_hdr;
 278        const struct sof_ext_man_header *head;
 279        ssize_t ext_man_size;
 280        ssize_t remaining;
 281        uintptr_t iptr;
 282        int ret = 0;
 283
 284        head = (struct sof_ext_man_header *)fw->data;
 285        remaining = head->full_size - head->header_size;
 286        ext_man_size = snd_sof_ext_man_size(fw);
 287
 288        /* Assert firmware starts with extended manifest */
 289        if (ext_man_size <= 0)
 290                return ext_man_size;
 291
 292        /* incompatible version */
 293        if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION,
 294                                             head->header_version)) {
 295                dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n",
 296                        head->header_version, SOF_EXT_MAN_VERSION);
 297                return -EINVAL;
 298        }
 299
 300        /* get first extended manifest element header */
 301        iptr = (uintptr_t)fw->data + head->header_size;
 302
 303        while (remaining > sizeof(*elem_hdr)) {
 304                elem_hdr = (struct sof_ext_man_elem_header *)iptr;
 305
 306                dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n",
 307                        elem_hdr->type, elem_hdr->size);
 308
 309                if (elem_hdr->size < sizeof(*elem_hdr) ||
 310                    elem_hdr->size > remaining) {
 311                        dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n",
 312                                elem_hdr->type, elem_hdr->size);
 313                        return -EINVAL;
 314                }
 315
 316                /* process structure data */
 317                switch (elem_hdr->type) {
 318                case SOF_EXT_MAN_ELEM_FW_VERSION:
 319                        ret = ext_man_get_fw_version(sdev, elem_hdr);
 320                        break;
 321                case SOF_EXT_MAN_ELEM_WINDOW:
 322                        ret = ext_man_get_windows(sdev, elem_hdr);
 323                        break;
 324                case SOF_EXT_MAN_ELEM_CC_VERSION:
 325                        ret = ext_man_get_cc_info(sdev, elem_hdr);
 326                        break;
 327                case SOF_EXT_MAN_ELEM_DBG_ABI:
 328                        ret = ext_man_get_dbg_abi_info(sdev, elem_hdr);
 329                        break;
 330                case SOF_EXT_MAN_ELEM_CONFIG_DATA:
 331                        ret = ext_man_get_config_data(sdev, elem_hdr);
 332                        break;
 333                case SOF_EXT_MAN_ELEM_PLATFORM_CONFIG_DATA:
 334                        ret = snd_sof_dsp_parse_platform_ext_manifest(sdev, elem_hdr);
 335                        break;
 336                default:
 337                        dev_info(sdev->dev, "unknown sof_ext_man header type %d size 0x%X\n",
 338                                 elem_hdr->type, elem_hdr->size);
 339                        break;
 340                }
 341
 342                if (ret < 0) {
 343                        dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n",
 344                                elem_hdr->type, elem_hdr->size);
 345                        return ret;
 346                }
 347
 348                remaining -= elem_hdr->size;
 349                iptr += elem_hdr->size;
 350        }
 351
 352        if (remaining) {
 353                dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n");
 354                return -EINVAL;
 355        }
 356
 357        return ext_man_size;
 358}
 359
 360/*
 361 * IPC Firmware ready.
 362 */
 363static void sof_get_windows(struct snd_sof_dev *sdev)
 364{
 365        struct sof_ipc_window_elem *elem;
 366        u32 outbox_offset = 0;
 367        u32 stream_offset = 0;
 368        u32 inbox_offset = 0;
 369        u32 outbox_size = 0;
 370        u32 stream_size = 0;
 371        u32 inbox_size = 0;
 372        u32 debug_size = 0;
 373        u32 debug_offset = 0;
 374        int window_offset;
 375        int bar;
 376        int i;
 377
 378        if (!sdev->info_window) {
 379                dev_err(sdev->dev, "error: have no window info\n");
 380                return;
 381        }
 382
 383        bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
 384        if (bar < 0) {
 385                dev_err(sdev->dev, "error: have no bar mapping\n");
 386                return;
 387        }
 388
 389        for (i = 0; i < sdev->info_window->num_windows; i++) {
 390                elem = &sdev->info_window->window[i];
 391
 392                window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id);
 393                if (window_offset < 0) {
 394                        dev_warn(sdev->dev, "warn: no offset for window %d\n",
 395                                 elem->id);
 396                        continue;
 397                }
 398
 399                switch (elem->type) {
 400                case SOF_IPC_REGION_UPBOX:
 401                        inbox_offset = window_offset + elem->offset;
 402                        inbox_size = elem->size;
 403                        snd_sof_debugfs_io_item(sdev,
 404                                                sdev->bar[bar] +
 405                                                inbox_offset,
 406                                                elem->size, "inbox",
 407                                                SOF_DEBUGFS_ACCESS_D0_ONLY);
 408                        break;
 409                case SOF_IPC_REGION_DOWNBOX:
 410                        outbox_offset = window_offset + elem->offset;
 411                        outbox_size = elem->size;
 412                        snd_sof_debugfs_io_item(sdev,
 413                                                sdev->bar[bar] +
 414                                                outbox_offset,
 415                                                elem->size, "outbox",
 416                                                SOF_DEBUGFS_ACCESS_D0_ONLY);
 417                        break;
 418                case SOF_IPC_REGION_TRACE:
 419                        snd_sof_debugfs_io_item(sdev,
 420                                                sdev->bar[bar] +
 421                                                window_offset +
 422                                                elem->offset,
 423                                                elem->size, "etrace",
 424                                                SOF_DEBUGFS_ACCESS_D0_ONLY);
 425                        break;
 426                case SOF_IPC_REGION_DEBUG:
 427                        debug_offset = window_offset + elem->offset;
 428                        debug_size = elem->size;
 429                        snd_sof_debugfs_io_item(sdev,
 430                                                sdev->bar[bar] +
 431                                                window_offset +
 432                                                elem->offset,
 433                                                elem->size, "debug",
 434                                                SOF_DEBUGFS_ACCESS_D0_ONLY);
 435                        break;
 436                case SOF_IPC_REGION_STREAM:
 437                        stream_offset = window_offset + elem->offset;
 438                        stream_size = elem->size;
 439                        snd_sof_debugfs_io_item(sdev,
 440                                                sdev->bar[bar] +
 441                                                stream_offset,
 442                                                elem->size, "stream",
 443                                                SOF_DEBUGFS_ACCESS_D0_ONLY);
 444                        break;
 445                case SOF_IPC_REGION_REGS:
 446                        snd_sof_debugfs_io_item(sdev,
 447                                                sdev->bar[bar] +
 448                                                window_offset +
 449                                                elem->offset,
 450                                                elem->size, "regs",
 451                                                SOF_DEBUGFS_ACCESS_D0_ONLY);
 452                        break;
 453                case SOF_IPC_REGION_EXCEPTION:
 454                        sdev->dsp_oops_offset = window_offset + elem->offset;
 455                        snd_sof_debugfs_io_item(sdev,
 456                                                sdev->bar[bar] +
 457                                                window_offset +
 458                                                elem->offset,
 459                                                elem->size, "exception",
 460                                                SOF_DEBUGFS_ACCESS_D0_ONLY);
 461                        break;
 462                default:
 463                        dev_err(sdev->dev, "error: get illegal window info\n");
 464                        return;
 465                }
 466        }
 467
 468        if (outbox_size == 0 || inbox_size == 0) {
 469                dev_err(sdev->dev, "error: get illegal mailbox window\n");
 470                return;
 471        }
 472
 473        snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size,
 474                                 outbox_offset, outbox_size);
 475        sdev->stream_box.offset = stream_offset;
 476        sdev->stream_box.size = stream_size;
 477
 478        sdev->debug_box.offset = debug_offset;
 479        sdev->debug_box.size = debug_size;
 480
 481        dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n",
 482                inbox_offset, inbox_size);
 483        dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n",
 484                outbox_offset, outbox_size);
 485        dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n",
 486                stream_offset, stream_size);
 487        dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n",
 488                debug_offset, debug_size);
 489}
 490
 491/* check for ABI compatibility and create memory windows on first boot */
 492int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id)
 493{
 494        struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready;
 495        int offset;
 496        int bar;
 497        int ret;
 498
 499        /* mailbox must be on 4k boundary */
 500        offset = snd_sof_dsp_get_mailbox_offset(sdev);
 501        if (offset < 0) {
 502                dev_err(sdev->dev, "error: have no mailbox offset\n");
 503                return offset;
 504        }
 505
 506        bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM);
 507        if (bar < 0) {
 508                dev_err(sdev->dev, "error: have no bar mapping\n");
 509                return -EINVAL;
 510        }
 511
 512        dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n",
 513                msg_id, offset);
 514
 515        /* no need to re-check version/ABI for subsequent boots */
 516        if (!sdev->first_boot)
 517                return 0;
 518
 519        /* copy data from the DSP FW ready offset */
 520        snd_sof_dsp_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready));
 521
 522        /* make sure ABI version is compatible */
 523        ret = snd_sof_ipc_valid(sdev);
 524        if (ret < 0)
 525                return ret;
 526
 527        /* now check for extended data */
 528        snd_sof_fw_parse_ext_data(sdev, bar, offset +
 529                                  sizeof(struct sof_ipc_fw_ready));
 530
 531        sof_get_windows(sdev);
 532
 533        return 0;
 534}
 535EXPORT_SYMBOL(sof_fw_ready);
 536
 537/* generic module parser for mmaped DSPs */
 538int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev,
 539                                struct snd_sof_mod_hdr *module)
 540{
 541        struct snd_sof_blk_hdr *block;
 542        int count, bar;
 543        u32 offset;
 544        size_t remaining;
 545
 546        dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n",
 547                module->size, module->num_blocks, module->type);
 548
 549        block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module));
 550
 551        /* module->size doesn't include header size */
 552        remaining = module->size;
 553        for (count = 0; count < module->num_blocks; count++) {
 554                /* check for wrap */
 555                if (remaining < sizeof(*block)) {
 556                        dev_err(sdev->dev, "error: not enough data remaining\n");
 557                        return -EINVAL;
 558                }
 559
 560                /* minus header size of block */
 561                remaining -= sizeof(*block);
 562
 563                if (block->size == 0) {
 564                        dev_warn(sdev->dev,
 565                                 "warning: block %d size zero\n", count);
 566                        dev_warn(sdev->dev, " type 0x%x offset 0x%x\n",
 567                                 block->type, block->offset);
 568                        continue;
 569                }
 570
 571                switch (block->type) {
 572                case SOF_FW_BLK_TYPE_RSRVD0:
 573                case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14:
 574                        continue;       /* not handled atm */
 575                case SOF_FW_BLK_TYPE_IRAM:
 576                case SOF_FW_BLK_TYPE_DRAM:
 577                case SOF_FW_BLK_TYPE_SRAM:
 578                        offset = block->offset;
 579                        bar = snd_sof_dsp_get_bar_index(sdev, block->type);
 580                        if (bar < 0) {
 581                                dev_err(sdev->dev,
 582                                        "error: no BAR mapping for block type 0x%x\n",
 583                                        block->type);
 584                                return bar;
 585                        }
 586                        break;
 587                default:
 588                        dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n",
 589                                block->type, count);
 590                        return -EINVAL;
 591                }
 592
 593                dev_dbg(sdev->dev,
 594                        "block %d type 0x%x size 0x%x ==>  offset 0x%x\n",
 595                        count, block->type, block->size, offset);
 596
 597                /* checking block->size to avoid unaligned access */
 598                if (block->size % sizeof(u32)) {
 599                        dev_err(sdev->dev, "error: invalid block size 0x%x\n",
 600                                block->size);
 601                        return -EINVAL;
 602                }
 603                snd_sof_dsp_block_write(sdev, bar, offset,
 604                                        block + 1, block->size);
 605
 606                if (remaining < block->size) {
 607                        dev_err(sdev->dev, "error: not enough data remaining\n");
 608                        return -EINVAL;
 609                }
 610
 611                /* minus body size of block */
 612                remaining -= block->size;
 613                /* next block */
 614                block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block)
 615                        + block->size);
 616        }
 617
 618        return 0;
 619}
 620EXPORT_SYMBOL(snd_sof_parse_module_memcpy);
 621
 622static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw,
 623                        size_t fw_offset)
 624{
 625        struct snd_sof_fw_header *header;
 626        size_t fw_size = fw->size - fw_offset;
 627
 628        if (fw->size <= fw_offset) {
 629                dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n");
 630                return -EINVAL;
 631        }
 632
 633        /* Read the header information from the data pointer */
 634        header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
 635
 636        /* verify FW sig */
 637        if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
 638                dev_err(sdev->dev, "error: invalid firmware signature\n");
 639                return -EINVAL;
 640        }
 641
 642        /* check size is valid */
 643        if (fw_size != header->file_size + sizeof(*header)) {
 644                dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n",
 645                        fw_size, header->file_size + sizeof(*header));
 646                return -EINVAL;
 647        }
 648
 649        dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n",
 650                header->file_size, header->num_modules,
 651                header->abi, sizeof(*header));
 652
 653        return 0;
 654}
 655
 656static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw,
 657                        size_t fw_offset)
 658{
 659        struct snd_sof_fw_header *header;
 660        struct snd_sof_mod_hdr *module;
 661        int (*load_module)(struct snd_sof_dev *sof_dev,
 662                           struct snd_sof_mod_hdr *hdr);
 663        int ret, count;
 664        size_t remaining;
 665
 666        header = (struct snd_sof_fw_header *)(fw->data + fw_offset);
 667        load_module = sof_ops(sdev)->load_module;
 668        if (!load_module)
 669                return -EINVAL;
 670
 671        /* parse each module */
 672        module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset +
 673                                            sizeof(*header));
 674        remaining = fw->size - sizeof(*header) - fw_offset;
 675        /* check for wrap */
 676        if (remaining > fw->size) {
 677                dev_err(sdev->dev, "error: fw size smaller than header size\n");
 678                return -EINVAL;
 679        }
 680
 681        for (count = 0; count < header->num_modules; count++) {
 682                /* check for wrap */
 683                if (remaining < sizeof(*module)) {
 684                        dev_err(sdev->dev, "error: not enough data remaining\n");
 685                        return -EINVAL;
 686                }
 687
 688                /* minus header size of module */
 689                remaining -= sizeof(*module);
 690
 691                /* module */
 692                ret = load_module(sdev, module);
 693                if (ret < 0) {
 694                        dev_err(sdev->dev, "error: invalid module %d\n", count);
 695                        return ret;
 696                }
 697
 698                if (remaining < module->size) {
 699                        dev_err(sdev->dev, "error: not enough data remaining\n");
 700                        return -EINVAL;
 701                }
 702
 703                /* minus body size of module */
 704                remaining -=  module->size;
 705                module = (struct snd_sof_mod_hdr *)((u8 *)module
 706                        + sizeof(*module) + module->size);
 707        }
 708
 709        return 0;
 710}
 711
 712int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev)
 713{
 714        struct snd_sof_pdata *plat_data = sdev->pdata;
 715        const char *fw_filename;
 716        ssize_t ext_man_size;
 717        int ret;
 718
 719        /* Don't request firmware again if firmware is already requested */
 720        if (plat_data->fw)
 721                return 0;
 722
 723        fw_filename = kasprintf(GFP_KERNEL, "%s/%s",
 724                                plat_data->fw_filename_prefix,
 725                                plat_data->fw_filename);
 726        if (!fw_filename)
 727                return -ENOMEM;
 728
 729        ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev);
 730
 731        if (ret < 0) {
 732                dev_err(sdev->dev,
 733                        "error: sof firmware file is missing, you might need to\n");
 734                dev_err(sdev->dev,
 735                        "       download it from https://github.com/thesofproject/sof-bin/\n");
 736                goto err;
 737        } else {
 738                dev_dbg(sdev->dev, "request_firmware %s successful\n",
 739                        fw_filename);
 740        }
 741
 742        /* check for extended manifest */
 743        ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw);
 744        if (ext_man_size > 0) {
 745                /* when no error occurred, drop extended manifest */
 746                plat_data->fw_offset = ext_man_size;
 747        } else if (!ext_man_size) {
 748                /* No extended manifest, so nothing to skip during FW load */
 749                dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n");
 750        } else {
 751                ret = ext_man_size;
 752                dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n",
 753                        fw_filename, ret);
 754        }
 755
 756err:
 757        kfree(fw_filename);
 758
 759        return ret;
 760}
 761EXPORT_SYMBOL(snd_sof_load_firmware_raw);
 762
 763int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev)
 764{
 765        struct snd_sof_pdata *plat_data = sdev->pdata;
 766        int ret;
 767
 768        ret = snd_sof_load_firmware_raw(sdev);
 769        if (ret < 0)
 770                return ret;
 771
 772        /* make sure the FW header and file is valid */
 773        ret = check_header(sdev, plat_data->fw, plat_data->fw_offset);
 774        if (ret < 0) {
 775                dev_err(sdev->dev, "error: invalid FW header\n");
 776                goto error;
 777        }
 778
 779        /* prepare the DSP for FW loading */
 780        ret = snd_sof_dsp_reset(sdev);
 781        if (ret < 0) {
 782                dev_err(sdev->dev, "error: failed to reset DSP\n");
 783                goto error;
 784        }
 785
 786        /* parse and load firmware modules to DSP */
 787        ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset);
 788        if (ret < 0) {
 789                dev_err(sdev->dev, "error: invalid FW modules\n");
 790                goto error;
 791        }
 792
 793        return 0;
 794
 795error:
 796        release_firmware(plat_data->fw);
 797        plat_data->fw = NULL;
 798        return ret;
 799
 800}
 801EXPORT_SYMBOL(snd_sof_load_firmware_memcpy);
 802
 803int snd_sof_load_firmware(struct snd_sof_dev *sdev)
 804{
 805        dev_dbg(sdev->dev, "loading firmware\n");
 806
 807        if (sof_ops(sdev)->load_firmware)
 808                return sof_ops(sdev)->load_firmware(sdev);
 809        return 0;
 810}
 811EXPORT_SYMBOL(snd_sof_load_firmware);
 812
 813int snd_sof_run_firmware(struct snd_sof_dev *sdev)
 814{
 815        int ret;
 816
 817        init_waitqueue_head(&sdev->boot_wait);
 818
 819        /* create read-only fw_version debugfs to store boot version info */
 820        if (sdev->first_boot) {
 821                ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version,
 822                                               sizeof(sdev->fw_version),
 823                                               "fw_version", 0444);
 824                /* errors are only due to memory allocation, not debugfs */
 825                if (ret < 0) {
 826                        dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n");
 827                        return ret;
 828                }
 829        }
 830
 831        /* perform pre fw run operations */
 832        ret = snd_sof_dsp_pre_fw_run(sdev);
 833        if (ret < 0) {
 834                dev_err(sdev->dev, "error: failed pre fw run op\n");
 835                return ret;
 836        }
 837
 838        dev_dbg(sdev->dev, "booting DSP firmware\n");
 839
 840        /* boot the firmware on the DSP */
 841        ret = snd_sof_dsp_run(sdev);
 842        if (ret < 0) {
 843                dev_err(sdev->dev, "error: failed to reset DSP\n");
 844                return ret;
 845        }
 846
 847        /*
 848         * now wait for the DSP to boot. There are 3 possible outcomes:
 849         * 1. Boot wait times out indicating FW boot failure.
 850         * 2. FW boots successfully and fw_ready op succeeds.
 851         * 3. FW boots but fw_ready op fails.
 852         */
 853        ret = wait_event_timeout(sdev->boot_wait,
 854                                 sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS,
 855                                 msecs_to_jiffies(sdev->boot_timeout));
 856        if (ret == 0) {
 857                dev_err(sdev->dev, "error: firmware boot failure\n");
 858                snd_sof_dsp_dbg_dump(sdev, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX |
 859                        SOF_DBG_DUMP_TEXT | SOF_DBG_DUMP_PCI | SOF_DBG_DUMP_FORCE_ERR_LEVEL);
 860                sdev->fw_state = SOF_FW_BOOT_FAILED;
 861                return -EIO;
 862        }
 863
 864        if (sdev->fw_state == SOF_FW_BOOT_COMPLETE)
 865                dev_dbg(sdev->dev, "firmware boot complete\n");
 866        else
 867                return -EIO; /* FW boots but fw_ready op failed */
 868
 869        /* perform post fw run operations */
 870        ret = snd_sof_dsp_post_fw_run(sdev);
 871        if (ret < 0) {
 872                dev_err(sdev->dev, "error: failed post fw run op\n");
 873                return ret;
 874        }
 875
 876        return 0;
 877}
 878EXPORT_SYMBOL(snd_sof_run_firmware);
 879
 880void snd_sof_fw_unload(struct snd_sof_dev *sdev)
 881{
 882        /* TODO: support module unloading at runtime */
 883        release_firmware(sdev->pdata->fw);
 884        sdev->pdata->fw = NULL;
 885}
 886EXPORT_SYMBOL(snd_sof_fw_unload);
 887