aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/remoteproc/remoteproc_core.c
diff options
context:
space:
mode:
authorOhad Ben-Cohen <ohad@wizery.com>2012-02-01 14:56:16 -0500
committerOhad Ben-Cohen <ohad@wizery.com>2012-03-06 12:13:39 -0500
commitfd2c15ec1dd3c2fdfc6ff03bb9644da9d530e3b9 (patch)
tree29e38853a3fac9e547a20fcb9f857c53ca7019b9 /drivers/remoteproc/remoteproc_core.c
parent9d8ae5c22b73852e9b23ba4e520a64c29bbfc939 (diff)
remoteproc: resource table overhaul
The resource table is an array of 'struct fw_resource' members, where each resource entry is expressed as a single member of that array. This approach got us this far, but it has a few drawbacks: 1. Different resource entries end up overloading the same members of 'struct fw_resource' with different meanings. The resulting code is error prone and hard to read and maintain. 2. It's impossible to extend 'struct fw_resource' without breaking the existing firmware images (and we already want to: we can't introduce the new virito device resource entry with the current scheme). 3. It doesn't scale: 'struct fw_resource' must be as big as the largest resource entry type. As a result, smaller resource entries end up utilizing only small part of it. This is fixed by defining a dedicated structure for every resource type, and then converting the resource table to a list of type-value members. Instead of a rigid array of homogeneous structs, the resource table is turned into a collection of heterogeneous structures. This way: 1. Resource entries consume exactly the amount of bytes they need. 2. It's easy to extend: just create a new resource entry structure, and assign it a new type. 3. The code is easier to read and maintain: the structures' members names are meaningful. While we're at it, this patch has several other resource table changes: 1. The resource table gains a simple header which contains the number of entries in the table and their offsets within the table. This makes the parsing code simpler and easier to read. 2. A version member is added to the resource table. Should we change the format again, we'll bump up this version to prevent breakage with existing firmware images. 3. The VRING and VIRTIO_DEV resource entries are combined to a single VDEV entry. This paves the way to supporting multiple VDEV entries. 4. Since we don't really support 64-bit rprocs yet, convert two stray u64 members to u32. Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com> Cc: Brian Swetland <swetland@google.com> Cc: Iliyan Malchev <malchev@google.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Grant Likely <grant.likely@secretlab.ca> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Mark Grosen <mgrosen@ti.com> Cc: John Williams <john.williams@petalogix.com> Cc: Michal Simek <monstr@monstr.eu> Cc: Loic PALLARDY <loic.pallardy@stericsson.com> Cc: Ludovic BARRE <ludovic.barre@stericsson.com> Cc: Omar Ramirez Luna <omar.luna@linaro.org> Cc: Guzman Lugo Fernando <fernando.lugo@ti.com> Cc: Anna Suman <s-anna@ti.com> Cc: Clark Rob <rob@ti.com> Cc: Stephen Boyd <sboyd@codeaurora.org> Cc: Saravana Kannan <skannan@codeaurora.org> Cc: David Brown <davidb@codeaurora.org> Cc: Kieran Bingham <kieranbingham@gmail.com> Cc: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'drivers/remoteproc/remoteproc_core.c')
-rw-r--r--drivers/remoteproc/remoteproc_core.c306
1 files changed, 209 insertions, 97 deletions
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 8990c51c16f0..10348451c6c9 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -63,9 +63,8 @@ static void klist_rproc_put(struct klist_node *n);
63static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); 63static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put);
64 64
65typedef int (*rproc_handle_resources_t)(struct rproc *rproc, 65typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
66 struct fw_resource *rsc, int len); 66 struct resource_table *table, int len);
67typedef int (*rproc_handle_resource_t)(struct rproc *rproc, 67typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
68 struct fw_resource *rsc);
69 68
70/* 69/*
71 * This is the IOMMU fault handler we register with the IOMMU API 70 * This is the IOMMU fault handler we register with the IOMMU API
@@ -281,9 +280,10 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len)
281} 280}
282 281
283/** 282/**
284 * rproc_handle_virtio_hdr() - handle a virtio header resource 283 * rproc_handle_early_vdev() - early handle a virtio header resource
285 * @rproc: the remote processor 284 * @rproc: the remote processor
286 * @rsc: the resource descriptor 285 * @rsc: the resource descriptor
286 * @avail: size of available data (for sanity checking the image)
287 * 287 *
288 * The existence of this virtio hdr resource entry means that the firmware 288 * The existence of this virtio hdr resource entry means that the firmware
289 * of this @rproc supports this virtio device. 289 * of this @rproc supports this virtio device.
@@ -291,37 +291,32 @@ rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len)
291 * Currently we support only a single virtio device of type VIRTIO_ID_RPMSG, 291 * Currently we support only a single virtio device of type VIRTIO_ID_RPMSG,
292 * but the plan is to remove this limitation and support any number 292 * but the plan is to remove this limitation and support any number
293 * of virtio devices (and of any type). We'll also add support for dynamically 293 * of virtio devices (and of any type). We'll also add support for dynamically
294 * adding (and removing) virtio devices over the rpmsg bus, but small 294 * adding (and removing) virtio devices over the rpmsg bus, but simple
295 * firmwares that doesn't want to get involved with rpmsg will be able 295 * firmwares that doesn't want to get involved with rpmsg will be able
296 * to simple use the resource table for this. 296 * to simply use the resource table for this.
297 *
298 * At this point this virtio header entry is rather simple: it just
299 * announces the virtio device id and the supported virtio device features.
300 * The plan though is to extend this to include the vring information and
301 * the virtio config space, too (but first, some resource table overhaul
302 * is needed: move from fixed-sized to variable-length TLV entries).
303 *
304 * For now, the 'flags' member of the resource entry contains the virtio
305 * device id, the 'da' member contains the device features, and 'pa' is
306 * where we need to store the guest features once negotiation completes.
307 * As usual, the 'id' member of this resource contains the index of this
308 * resource type (i.e. is this the first virtio hdr entry, the 2nd, ...).
309 * 297 *
310 * Returns 0 on success, or an appropriate error code otherwise 298 * Returns 0 on success, or an appropriate error code otherwise
311 */ 299 */
312static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc) 300static int rproc_handle_early_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
301 int avail)
313{ 302{
314 struct rproc_vdev *rvdev; 303 struct rproc_vdev *rvdev;
315 304
305 /* make sure resource isn't truncated */
306 if (sizeof(*rsc) > avail) {
307 dev_err(rproc->dev, "vdev rsc is truncated\n");
308 return -EINVAL;
309 }
310
316 /* we only support VIRTIO_ID_RPMSG devices for now */ 311 /* we only support VIRTIO_ID_RPMSG devices for now */
317 if (rsc->flags != VIRTIO_ID_RPMSG) { 312 if (rsc->id != VIRTIO_ID_RPMSG) {
318 dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->flags); 313 dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->id);
319 return -EINVAL; 314 return -EINVAL;
320 } 315 }
321 316
322 /* we only support a single vdev per rproc for now */ 317 /* we only support a single vdev per rproc for now */
323 if (rsc->id || rproc->rvdev) { 318 if (rproc->rvdev) {
324 dev_warn(rproc->dev, "redundant vdev entry: %s\n", rsc->name); 319 dev_warn(rproc->dev, "redundant vdev entry\n");
325 return -EINVAL; 320 return -EINVAL;
326 } 321 }
327 322
@@ -330,7 +325,7 @@ static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc)
330 return -ENOMEM; 325 return -ENOMEM;
331 326
332 /* remember the device features */ 327 /* remember the device features */
333 rvdev->dfeatures = rsc->da; 328 rvdev->dfeatures = rsc->dfeatures;
334 329
335 rproc->rvdev = rvdev; 330 rproc->rvdev = rvdev;
336 rvdev->rproc = rproc; 331 rvdev->rproc = rproc;
@@ -339,9 +334,10 @@ static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc)
339} 334}
340 335
341/** 336/**
342 * rproc_handle_vring() - handle a vring fw resource 337 * rproc_handle_vdev() - handle a vdev fw resource
343 * @rproc: the remote processor 338 * @rproc: the remote processor
344 * @rsc: the vring resource descriptor 339 * @rsc: the vring resource descriptor
340 * @avail: size of available data (for sanity checking the image)
345 * 341 *
346 * This resource entry requires allocation of non-cacheable memory 342 * This resource entry requires allocation of non-cacheable memory
347 * for a virtio vring. Currently we only support two vrings per remote 343 * for a virtio vring. Currently we only support two vrings per remote
@@ -360,57 +356,82 @@ static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc)
360 * 356 *
361 * Returns 0 on success, or an appropriate error code otherwise 357 * Returns 0 on success, or an appropriate error code otherwise
362 */ 358 */
363static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc) 359static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
360 int avail)
364{ 361{
365 struct device *dev = rproc->dev; 362 struct device *dev = rproc->dev;
366 struct rproc_vdev *rvdev = rproc->rvdev; 363 struct rproc_vdev *rvdev = rproc->rvdev;
367 dma_addr_t dma; 364 int i;
368 int size, id = rsc->id;
369 void *va;
370 365
371 /* no vdev is in place ? */ 366 /* make sure resource isn't truncated */
372 if (!rvdev) { 367 if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring)
373 dev_err(dev, "vring requested without a virtio dev entry\n"); 368 + rsc->config_len > avail) {
369 dev_err(rproc->dev, "vdev rsc is truncated\n");
374 return -EINVAL; 370 return -EINVAL;
375 } 371 }
376 372
377 /* the firmware must provide the expected queue size */ 373 /* make sure reserved bytes are zeroes */
378 if (!rsc->len) { 374 if (rsc->reserved[0] || rsc->reserved[1]) {
379 dev_err(dev, "missing expected queue size\n"); 375 dev_err(dev, "vdev rsc has non zero reserved bytes\n");
380 return -EINVAL; 376 return -EINVAL;
381 } 377 }
382 378
383 /* we currently support two vrings per rproc (for rx and tx) */ 379 dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n",
384 if (id >= ARRAY_SIZE(rvdev->vring)) { 380 rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings);
385 dev_err(dev, "%s: invalid vring id %d\n", rsc->name, id); 381
382 /* no vdev is in place ? */
383 if (!rvdev) {
384 dev_err(dev, "vring requested without a virtio dev entry\n");
386 return -EINVAL; 385 return -EINVAL;
387 } 386 }
388 387
389 /* have we already allocated this vring id ? */ 388 /* we currently support two vrings per rproc (for rx and tx) */
390 if (rvdev->vring[id].len) { 389 if (rsc->num_of_vrings != ARRAY_SIZE(rvdev->vring)) {
391 dev_err(dev, "%s: duplicated id %d\n", rsc->name, id); 390 dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings);
392 return -EINVAL; 391 return -EINVAL;
393 } 392 }
394 393
395 /* actual size of vring (in bytes) */ 394 /* initialize the vrings */
396 size = PAGE_ALIGN(vring_size(rsc->len, AMP_VRING_ALIGN)); 395 for (i = 0; i < rsc->num_of_vrings; i++) {
396 struct fw_rsc_vdev_vring *vring = &rsc->vring[i];
397 dma_addr_t dma;
398 int size;
399 void *va;
400
401 /* make sure reserved bytes are zeroes */
402 if (vring->reserved) {
403 dev_err(dev, "vring rsc has non zero reserved bytes\n");
404 return -EINVAL;
405 }
397 406
398 /* 407 /* the firmware must provide the expected queue size */
399 * Allocate non-cacheable memory for the vring. In the future 408 if (!vring->num) {
400 * this call will also configure the IOMMU for us 409 dev_err(dev, "missing expected queue size\n");
401 */ 410 /* potential cleanups are taken care of later on */
402 va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL); 411 return -EINVAL;
403 if (!va) { 412 }
404 dev_err(dev, "dma_alloc_coherent failed\n");
405 return -ENOMEM;
406 }
407 413
408 dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", id, va, 414 /* actual size of vring (in bytes) */
409 dma, rsc->len, size); 415 size = PAGE_ALIGN(vring_size(vring->num, AMP_VRING_ALIGN));
410 416
411 rvdev->vring[id].len = rsc->len; 417 /*
412 rvdev->vring[id].va = va; 418 * Allocate non-cacheable memory for the vring. In the future
413 rvdev->vring[id].dma = dma; 419 * this call will also configure the IOMMU for us
420 */
421 va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL);
422 if (!va) {
423 dev_err(dev, "dma_alloc_coherent failed\n");
424 /* potential cleanups are taken care of later on */
425 return -EINVAL;
426 }
427
428 dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", i,
429 va, dma, vring->num, size);
430
431 rvdev->vring[i].len = vring->num;
432 rvdev->vring[i].va = va;
433 rvdev->vring[i].dma = dma;
434 }
414 435
415 return 0; 436 return 0;
416} 437}
@@ -419,6 +440,7 @@ static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc)
419 * rproc_handle_trace() - handle a shared trace buffer resource 440 * rproc_handle_trace() - handle a shared trace buffer resource
420 * @rproc: the remote processor 441 * @rproc: the remote processor
421 * @rsc: the trace resource descriptor 442 * @rsc: the trace resource descriptor
443 * @avail: size of available data (for sanity checking the image)
422 * 444 *
423 * In case the remote processor dumps trace logs into memory, 445 * In case the remote processor dumps trace logs into memory,
424 * export it via debugfs. 446 * export it via debugfs.
@@ -430,13 +452,25 @@ static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc)
430 * 452 *
431 * Returns 0 on success, or an appropriate error code otherwise 453 * Returns 0 on success, or an appropriate error code otherwise
432 */ 454 */
433static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) 455static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
456 int avail)
434{ 457{
435 struct rproc_mem_entry *trace; 458 struct rproc_mem_entry *trace;
436 struct device *dev = rproc->dev; 459 struct device *dev = rproc->dev;
437 void *ptr; 460 void *ptr;
438 char name[15]; 461 char name[15];
439 462
463 if (sizeof(*rsc) > avail) {
464 dev_err(rproc->dev, "trace rsc is truncated\n");
465 return -EINVAL;
466 }
467
468 /* make sure reserved bytes are zeroes */
469 if (rsc->reserved) {
470 dev_err(dev, "trace rsc has non zero reserved bytes\n");
471 return -EINVAL;
472 }
473
440 /* what's the kernel address of this resource ? */ 474 /* what's the kernel address of this resource ? */
441 ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); 475 ptr = rproc_da_to_va(rproc, rsc->da, rsc->len);
442 if (!ptr) { 476 if (!ptr) {
@@ -469,7 +503,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
469 503
470 rproc->num_traces++; 504 rproc->num_traces++;
471 505
472 dev_dbg(dev, "%s added: va %p, da 0x%llx, len 0x%x\n", name, ptr, 506 dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", name, ptr,
473 rsc->da, rsc->len); 507 rsc->da, rsc->len);
474 508
475 return 0; 509 return 0;
@@ -479,6 +513,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
479 * rproc_handle_devmem() - handle devmem resource entry 513 * rproc_handle_devmem() - handle devmem resource entry
480 * @rproc: remote processor handle 514 * @rproc: remote processor handle
481 * @rsc: the devmem resource entry 515 * @rsc: the devmem resource entry
516 * @avail: size of available data (for sanity checking the image)
482 * 517 *
483 * Remote processors commonly need to access certain on-chip peripherals. 518 * Remote processors commonly need to access certain on-chip peripherals.
484 * 519 *
@@ -499,7 +534,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
499 * and not allow firmwares to request access to physical addresses that 534 * and not allow firmwares to request access to physical addresses that
500 * are outside those ranges. 535 * are outside those ranges.
501 */ 536 */
502static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) 537static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
538 int avail)
503{ 539{
504 struct rproc_mem_entry *mapping; 540 struct rproc_mem_entry *mapping;
505 int ret; 541 int ret;
@@ -508,6 +544,17 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
508 if (!rproc->domain) 544 if (!rproc->domain)
509 return -EINVAL; 545 return -EINVAL;
510 546
547 if (sizeof(*rsc) > avail) {
548 dev_err(rproc->dev, "devmem rsc is truncated\n");
549 return -EINVAL;
550 }
551
552 /* make sure reserved bytes are zeroes */
553 if (rsc->reserved) {
554 dev_err(rproc->dev, "devmem rsc has non zero reserved bytes\n");
555 return -EINVAL;
556 }
557
511 mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); 558 mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
512 if (!mapping) { 559 if (!mapping) {
513 dev_err(rproc->dev, "kzalloc mapping failed\n"); 560 dev_err(rproc->dev, "kzalloc mapping failed\n");
@@ -531,7 +578,7 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
531 mapping->len = rsc->len; 578 mapping->len = rsc->len;
532 list_add_tail(&mapping->node, &rproc->mappings); 579 list_add_tail(&mapping->node, &rproc->mappings);
533 580
534 dev_dbg(rproc->dev, "mapped devmem pa 0x%llx, da 0x%llx, len 0x%x\n", 581 dev_dbg(rproc->dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n",
535 rsc->pa, rsc->da, rsc->len); 582 rsc->pa, rsc->da, rsc->len);
536 583
537 return 0; 584 return 0;
@@ -545,6 +592,7 @@ out:
545 * rproc_handle_carveout() - handle phys contig memory allocation requests 592 * rproc_handle_carveout() - handle phys contig memory allocation requests
546 * @rproc: rproc handle 593 * @rproc: rproc handle
547 * @rsc: the resource entry 594 * @rsc: the resource entry
595 * @avail: size of available data (for image validation)
548 * 596 *
549 * This function will handle firmware requests for allocation of physically 597 * This function will handle firmware requests for allocation of physically
550 * contiguous memory regions. 598 * contiguous memory regions.
@@ -558,7 +606,8 @@ out:
558 * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB 606 * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
559 * pressure is important; it may have a substantial impact on performance. 607 * pressure is important; it may have a substantial impact on performance.
560 */ 608 */
561static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) 609static int rproc_handle_carveout(struct rproc *rproc,
610 struct fw_rsc_carveout *rsc, int avail)
562{ 611{
563 struct rproc_mem_entry *carveout, *mapping; 612 struct rproc_mem_entry *carveout, *mapping;
564 struct device *dev = rproc->dev; 613 struct device *dev = rproc->dev;
@@ -566,6 +615,20 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
566 void *va; 615 void *va;
567 int ret; 616 int ret;
568 617
618 if (sizeof(*rsc) > avail) {
619 dev_err(rproc->dev, "carveout rsc is truncated\n");
620 return -EINVAL;
621 }
622
623 /* make sure reserved bytes are zeroes */
624 if (rsc->reserved) {
625 dev_err(dev, "carveout rsc has non zero reserved bytes\n");
626 return -EINVAL;
627 }
628
629 dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n",
630 rsc->da, rsc->pa, rsc->len, rsc->flags);
631
569 mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); 632 mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
570 if (!mapping) { 633 if (!mapping) {
571 dev_err(dev, "kzalloc mapping failed\n"); 634 dev_err(dev, "kzalloc mapping failed\n");
@@ -624,7 +687,7 @@ static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
624 mapping->len = rsc->len; 687 mapping->len = rsc->len;
625 list_add_tail(&mapping->node, &rproc->mappings); 688 list_add_tail(&mapping->node, &rproc->mappings);
626 689
627 dev_dbg(dev, "carveout mapped 0x%llx to 0x%x\n", rsc->da, dma); 690 dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma);
628 691
629 /* 692 /*
630 * Some remote processors might need to know the pa 693 * Some remote processors might need to know the pa
@@ -665,36 +728,44 @@ free_mapping:
665 * enum fw_resource_type. 728 * enum fw_resource_type.
666 */ 729 */
667static rproc_handle_resource_t rproc_handle_rsc[] = { 730static rproc_handle_resource_t rproc_handle_rsc[] = {
668 [RSC_CARVEOUT] = rproc_handle_carveout, 731 [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
669 [RSC_DEVMEM] = rproc_handle_devmem, 732 [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
670 [RSC_TRACE] = rproc_handle_trace, 733 [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
671 [RSC_VRING] = rproc_handle_vring, 734 [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev,
672 [RSC_VIRTIO_DEV] = NULL, /* handled early upon registration */
673}; 735};
674 736
675/* handle firmware resource entries before booting the remote processor */ 737/* handle firmware resource entries before booting the remote processor */
676static int 738static int
677rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) 739rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
678{ 740{
679 struct device *dev = rproc->dev; 741 struct device *dev = rproc->dev;
680 rproc_handle_resource_t handler; 742 rproc_handle_resource_t handler;
681 int ret = 0; 743 int ret = 0, i;
744
745 for (i = 0; i < table->num; i++) {
746 int offset = table->offset[i];
747 struct fw_rsc_hdr *hdr = (void *)table + offset;
748 int avail = len - offset - sizeof(*hdr);
749 void *rsc = (void *)hdr + sizeof(*hdr);
750
751 /* make sure table isn't truncated */
752 if (avail < 0) {
753 dev_err(dev, "rsc table is truncated\n");
754 return -EINVAL;
755 }
682 756
683 for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) { 757 dev_dbg(dev, "rsc: type %d\n", hdr->type);
684 dev_dbg(dev, "rsc: type %d, da 0x%llx, pa 0x%llx, len 0x%x, "
685 "id %d, name %s, flags %x\n", rsc->type, rsc->da,
686 rsc->pa, rsc->len, rsc->id, rsc->name, rsc->flags);
687 758
688 if (rsc->type >= RSC_LAST) { 759 if (hdr->type >= RSC_LAST) {
689 dev_warn(dev, "unsupported resource %d\n", rsc->type); 760 dev_warn(dev, "unsupported resource %d\n", hdr->type);
690 continue; 761 continue;
691 } 762 }
692 763
693 handler = rproc_handle_rsc[rsc->type]; 764 handler = rproc_handle_rsc[hdr->type];
694 if (!handler) 765 if (!handler)
695 continue; 766 continue;
696 767
697 ret = handler(rproc, rsc); 768 ret = handler(rproc, rsc, avail);
698 if (ret) 769 if (ret)
699 break; 770 break;
700 } 771 }
@@ -704,18 +775,31 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
704 775
705/* handle firmware resource entries while registering the remote processor */ 776/* handle firmware resource entries while registering the remote processor */
706static int 777static int
707rproc_handle_virtio_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) 778rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
708{ 779{
709 struct device *dev = rproc->dev; 780 struct device *dev = rproc->dev;
710 int ret = -ENODEV; 781 int ret = 0, i;
782
783 for (i = 0; i < table->num; i++) {
784 int offset = table->offset[i];
785 struct fw_rsc_hdr *hdr = (void *)table + offset;
786 int avail = len - offset - sizeof(*hdr);
711 787
712 for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) 788 /* make sure table isn't truncated */
713 if (rsc->type == RSC_VIRTIO_DEV) { 789 if (avail < 0) {
714 dev_dbg(dev, "found vdev %d/%s features %llx\n", 790 dev_err(dev, "rsc table is truncated\n");
715 rsc->flags, rsc->name, rsc->da); 791 return -EINVAL;
716 ret = rproc_handle_virtio_hdr(rproc, rsc); 792 }
793
794 dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type);
795
796 if (hdr->type == RSC_VDEV) {
797 struct fw_rsc_vdev *vrsc =
798 (struct fw_rsc_vdev *)hdr->data;
799 ret = rproc_handle_early_vdev(rproc, vrsc, avail);
717 break; 800 break;
718 } 801 }
802 }
719 803
720 return ret; 804 return ret;
721} 805}
@@ -744,7 +828,9 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
744 struct elf32_hdr *ehdr; 828 struct elf32_hdr *ehdr;
745 struct elf32_shdr *shdr; 829 struct elf32_shdr *shdr;
746 const char *name_table; 830 const char *name_table;
831 struct device *dev = rproc->dev;
747 int i, ret = -EINVAL; 832 int i, ret = -EINVAL;
833 struct resource_table *table;
748 834
749 ehdr = (struct elf32_hdr *)elf_data; 835 ehdr = (struct elf32_hdr *)elf_data;
750 shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); 836 shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
@@ -752,21 +838,47 @@ static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
752 838
753 /* look for the resource table and handle it */ 839 /* look for the resource table and handle it */
754 for (i = 0; i < ehdr->e_shnum; i++, shdr++) { 840 for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
755 if (!strcmp(name_table + shdr->sh_name, ".resource_table")) { 841 int size = shdr->sh_size;
756 struct fw_resource *table = (struct fw_resource *) 842 int offset = shdr->sh_offset;
757 (elf_data + shdr->sh_offset);
758 843
759 if (shdr->sh_offset + shdr->sh_size > len) { 844 if (strcmp(name_table + shdr->sh_name, ".resource_table"))
760 dev_err(rproc->dev, 845 continue;
761 "truncated fw: need 0x%x avail 0x%x\n",
762 shdr->sh_offset + shdr->sh_size, len);
763 ret = -EINVAL;
764 }
765 846
766 ret = handler(rproc, table, shdr->sh_size); 847 table = (struct resource_table *)(elf_data + offset);
767 848
768 break; 849 /* make sure we have the entire table */
850 if (offset + size > len) {
851 dev_err(dev, "resource table truncated\n");
852 return -EINVAL;
853 }
854
855 /* make sure table has at least the header */
856 if (sizeof(struct resource_table) > size) {
857 dev_err(dev, "header-less resource table\n");
858 return -EINVAL;
769 } 859 }
860
861 /* we don't support any version beyond the first */
862 if (table->ver != 1) {
863 dev_err(dev, "unsupported fw ver: %d\n", table->ver);
864 return -EINVAL;
865 }
866
867 /* make sure reserved bytes are zeroes */
868 if (table->reserved[0] || table->reserved[1]) {
869 dev_err(dev, "non zero reserved bytes\n");
870 return -EINVAL;
871 }
872
873 /* make sure the offsets array isn't truncated */
874 if (table->num * sizeof(table->offset[0]) +
875 sizeof(struct resource_table) > size) {
876 dev_err(dev, "resource table incomplete\n");
877 return -EINVAL;
878 }
879
880 ret = handler(rproc, table, shdr->sh_size);
881 break;
770 } 882 }
771 883
772 return ret; 884 return ret;
@@ -980,7 +1092,7 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
980 if (rproc_fw_sanity_check(rproc, fw) < 0) 1092 if (rproc_fw_sanity_check(rproc, fw) < 0)
981 goto out; 1093 goto out;
982 1094
983 /* does the fw supports any virtio devices ? */ 1095 /* does the fw support any virtio devices ? */
984 ret = rproc_handle_resources(rproc, fw->data, fw->size, 1096 ret = rproc_handle_resources(rproc, fw->data, fw->size,
985 rproc_handle_virtio_rsc); 1097 rproc_handle_virtio_rsc);
986 if (ret) { 1098 if (ret) {