diff options
author | Ohad Ben-Cohen <ohad@wizery.com> | 2012-02-01 14:56:16 -0500 |
---|---|---|
committer | Ohad Ben-Cohen <ohad@wizery.com> | 2012-03-06 12:13:39 -0500 |
commit | fd2c15ec1dd3c2fdfc6ff03bb9644da9d530e3b9 (patch) | |
tree | 29e38853a3fac9e547a20fcb9f857c53ca7019b9 /drivers/remoteproc | |
parent | 9d8ae5c22b73852e9b23ba4e520a64c29bbfc939 (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')
-rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 306 |
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); | |||
63 | static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); | 63 | static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put); |
64 | 64 | ||
65 | typedef int (*rproc_handle_resources_t)(struct rproc *rproc, | 65 | typedef int (*rproc_handle_resources_t)(struct rproc *rproc, |
66 | struct fw_resource *rsc, int len); | 66 | struct resource_table *table, int len); |
67 | typedef int (*rproc_handle_resource_t)(struct rproc *rproc, | 67 | typedef 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 | */ |
312 | static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc) | 300 | static 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 | */ |
363 | static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc) | 359 | static 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 | */ |
433 | static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc) | 455 | static 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 | */ |
502 | static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc) | 537 | static 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 | */ |
561 | static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc) | 609 | static 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 | */ |
667 | static rproc_handle_resource_t rproc_handle_rsc[] = { | 730 | static 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 */ |
676 | static int | 738 | static int |
677 | rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) | 739 | rproc_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 */ |
706 | static int | 777 | static int |
707 | rproc_handle_virtio_rsc(struct rproc *rproc, struct fw_resource *rsc, int len) | 778 | rproc_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) { |