.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Remote Processor Framework |
---|
3 | 4 | * |
---|
.. | .. |
---|
11 | 12 | * Suman Anna <s-anna@ti.com> |
---|
12 | 13 | * Robert Tivy <rtivy@ti.com> |
---|
13 | 14 | * Armando Uribe De Leon <x0095078@ti.com> |
---|
14 | | - * |
---|
15 | | - * This program is free software; you can redistribute it and/or |
---|
16 | | - * modify it under the terms of the GNU General Public License |
---|
17 | | - * version 2 as published by the Free Software Foundation. |
---|
18 | | - * |
---|
19 | | - * This program is distributed in the hope that it will be useful, |
---|
20 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
21 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
22 | | - * GNU General Public License for more details. |
---|
23 | 15 | */ |
---|
24 | 16 | |
---|
25 | 17 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
---|
26 | 18 | |
---|
| 19 | +#include <linux/delay.h> |
---|
27 | 20 | #include <linux/kernel.h> |
---|
28 | 21 | #include <linux/module.h> |
---|
29 | 22 | #include <linux/device.h> |
---|
30 | 23 | #include <linux/slab.h> |
---|
31 | 24 | #include <linux/mutex.h> |
---|
| 25 | +#include <linux/dma-map-ops.h> |
---|
32 | 26 | #include <linux/dma-mapping.h> |
---|
| 27 | +#include <linux/dma-direct.h> /* XXX: pokes into bus_dma_range */ |
---|
33 | 28 | #include <linux/firmware.h> |
---|
34 | 29 | #include <linux/string.h> |
---|
35 | 30 | #include <linux/debugfs.h> |
---|
36 | | -#include <linux/devcoredump.h> |
---|
| 31 | +#include <linux/rculist.h> |
---|
37 | 32 | #include <linux/remoteproc.h> |
---|
38 | 33 | #include <linux/iommu.h> |
---|
39 | 34 | #include <linux/idr.h> |
---|
40 | 35 | #include <linux/elf.h> |
---|
41 | 36 | #include <linux/crc32.h> |
---|
| 37 | +#include <linux/of_reserved_mem.h> |
---|
42 | 38 | #include <linux/virtio_ids.h> |
---|
43 | 39 | #include <linux/virtio_ring.h> |
---|
44 | 40 | #include <asm/byteorder.h> |
---|
| 41 | +#include <linux/platform_device.h> |
---|
| 42 | +#include <trace/hooks/remoteproc.h> |
---|
45 | 43 | |
---|
46 | 44 | #include "remoteproc_internal.h" |
---|
47 | 45 | |
---|
| 46 | +#define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL |
---|
| 47 | + |
---|
48 | 48 | static DEFINE_MUTEX(rproc_list_mutex); |
---|
49 | 49 | static LIST_HEAD(rproc_list); |
---|
| 50 | +static struct notifier_block rproc_panic_nb; |
---|
50 | 51 | |
---|
51 | | -typedef int (*rproc_handle_resources_t)(struct rproc *rproc, |
---|
52 | | - struct resource_table *table, int len); |
---|
53 | 52 | typedef int (*rproc_handle_resource_t)(struct rproc *rproc, |
---|
54 | 53 | void *, int offset, int avail); |
---|
55 | 54 | |
---|
| 55 | +static int rproc_alloc_carveout(struct rproc *rproc, |
---|
| 56 | + struct rproc_mem_entry *mem); |
---|
| 57 | +static int rproc_release_carveout(struct rproc *rproc, |
---|
| 58 | + struct rproc_mem_entry *mem); |
---|
| 59 | + |
---|
56 | 60 | /* Unique indices for remoteproc devices */ |
---|
57 | 61 | static DEFINE_IDA(rproc_dev_index); |
---|
| 62 | +static struct workqueue_struct *rproc_recovery_wq; |
---|
58 | 63 | |
---|
59 | 64 | static const char * const rproc_crash_names[] = { |
---|
60 | 65 | [RPROC_MMUFAULT] = "mmufault", |
---|
.. | .. |
---|
140 | 145 | iommu_domain_free(domain); |
---|
141 | 146 | } |
---|
142 | 147 | |
---|
| 148 | +phys_addr_t rproc_va_to_pa(void *cpu_addr) |
---|
| 149 | +{ |
---|
| 150 | + /* |
---|
| 151 | + * Return physical address according to virtual address location |
---|
| 152 | + * - in vmalloc: if region ioremapped or defined as dma_alloc_coherent |
---|
| 153 | + * - in kernel: if region allocated in generic dma memory pool |
---|
| 154 | + */ |
---|
| 155 | + if (is_vmalloc_addr(cpu_addr)) { |
---|
| 156 | + return page_to_phys(vmalloc_to_page(cpu_addr)) + |
---|
| 157 | + offset_in_page(cpu_addr); |
---|
| 158 | + } |
---|
| 159 | + |
---|
| 160 | + WARN_ON(!virt_addr_valid(cpu_addr)); |
---|
| 161 | + return virt_to_phys(cpu_addr); |
---|
| 162 | +} |
---|
| 163 | +EXPORT_SYMBOL(rproc_va_to_pa); |
---|
| 164 | + |
---|
143 | 165 | /** |
---|
144 | 166 | * rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address |
---|
145 | 167 | * @rproc: handle of a remote processor |
---|
.. | .. |
---|
169 | 191 | * here the output of the DMA API for the carveouts, which should be more |
---|
170 | 192 | * correct. |
---|
171 | 193 | */ |
---|
172 | | -void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) |
---|
| 194 | +void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) |
---|
173 | 195 | { |
---|
174 | 196 | struct rproc_mem_entry *carveout; |
---|
175 | 197 | void *ptr = NULL; |
---|
176 | 198 | |
---|
177 | 199 | if (rproc->ops->da_to_va) { |
---|
178 | | - ptr = rproc->ops->da_to_va(rproc, da, len); |
---|
| 200 | + ptr = rproc->ops->da_to_va(rproc, da, len, is_iomem); |
---|
179 | 201 | if (ptr) |
---|
180 | 202 | goto out; |
---|
181 | 203 | } |
---|
182 | 204 | |
---|
183 | 205 | list_for_each_entry(carveout, &rproc->carveouts, node) { |
---|
184 | 206 | int offset = da - carveout->da; |
---|
| 207 | + |
---|
| 208 | + /* Verify that carveout is allocated */ |
---|
| 209 | + if (!carveout->va) |
---|
| 210 | + continue; |
---|
185 | 211 | |
---|
186 | 212 | /* try next carveout if da is too small */ |
---|
187 | 213 | if (offset < 0) |
---|
.. | .. |
---|
193 | 219 | |
---|
194 | 220 | ptr = carveout->va + offset; |
---|
195 | 221 | |
---|
| 222 | + if (is_iomem) |
---|
| 223 | + *is_iomem = carveout->is_iomem; |
---|
| 224 | + |
---|
196 | 225 | break; |
---|
197 | 226 | } |
---|
198 | 227 | |
---|
.. | .. |
---|
201 | 230 | } |
---|
202 | 231 | EXPORT_SYMBOL(rproc_da_to_va); |
---|
203 | 232 | |
---|
| 233 | +/** |
---|
| 234 | + * rproc_find_carveout_by_name() - lookup the carveout region by a name |
---|
| 235 | + * @rproc: handle of a remote processor |
---|
| 236 | + * @name: carveout name to find (format string) |
---|
| 237 | + * @...: optional parameters matching @name string |
---|
| 238 | + * |
---|
| 239 | + * Platform driver has the capability to register some pre-allacoted carveout |
---|
| 240 | + * (physically contiguous memory regions) before rproc firmware loading and |
---|
| 241 | + * associated resource table analysis. These regions may be dedicated memory |
---|
| 242 | + * regions internal to the coprocessor or specified DDR region with specific |
---|
| 243 | + * attributes |
---|
| 244 | + * |
---|
| 245 | + * This function is a helper function with which we can go over the |
---|
| 246 | + * allocated carveouts and return associated region characteristics like |
---|
| 247 | + * coprocessor address, length or processor virtual address. |
---|
| 248 | + * |
---|
| 249 | + * Return: a valid pointer on carveout entry on success or NULL on failure. |
---|
| 250 | + */ |
---|
| 251 | +__printf(2, 3) |
---|
| 252 | +struct rproc_mem_entry * |
---|
| 253 | +rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...) |
---|
| 254 | +{ |
---|
| 255 | + va_list args; |
---|
| 256 | + char _name[32]; |
---|
| 257 | + struct rproc_mem_entry *carveout, *mem = NULL; |
---|
| 258 | + |
---|
| 259 | + if (!name) |
---|
| 260 | + return NULL; |
---|
| 261 | + |
---|
| 262 | + va_start(args, name); |
---|
| 263 | + vsnprintf(_name, sizeof(_name), name, args); |
---|
| 264 | + va_end(args); |
---|
| 265 | + |
---|
| 266 | + list_for_each_entry(carveout, &rproc->carveouts, node) { |
---|
| 267 | + /* Compare carveout and requested names */ |
---|
| 268 | + if (!strcmp(carveout->name, _name)) { |
---|
| 269 | + mem = carveout; |
---|
| 270 | + break; |
---|
| 271 | + } |
---|
| 272 | + } |
---|
| 273 | + |
---|
| 274 | + return mem; |
---|
| 275 | +} |
---|
| 276 | + |
---|
| 277 | +/** |
---|
| 278 | + * rproc_check_carveout_da() - Check specified carveout da configuration |
---|
| 279 | + * @rproc: handle of a remote processor |
---|
| 280 | + * @mem: pointer on carveout to check |
---|
| 281 | + * @da: area device address |
---|
| 282 | + * @len: associated area size |
---|
| 283 | + * |
---|
| 284 | + * This function is a helper function to verify requested device area (couple |
---|
| 285 | + * da, len) is part of specified carveout. |
---|
| 286 | + * If da is not set (defined as FW_RSC_ADDR_ANY), only requested length is |
---|
| 287 | + * checked. |
---|
| 288 | + * |
---|
| 289 | + * Return: 0 if carveout matches request else error |
---|
| 290 | + */ |
---|
| 291 | +static int rproc_check_carveout_da(struct rproc *rproc, |
---|
| 292 | + struct rproc_mem_entry *mem, u32 da, u32 len) |
---|
| 293 | +{ |
---|
| 294 | + struct device *dev = &rproc->dev; |
---|
| 295 | + int delta; |
---|
| 296 | + |
---|
| 297 | + /* Check requested resource length */ |
---|
| 298 | + if (len > mem->len) { |
---|
| 299 | + dev_err(dev, "Registered carveout doesn't fit len request\n"); |
---|
| 300 | + return -EINVAL; |
---|
| 301 | + } |
---|
| 302 | + |
---|
| 303 | + if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) { |
---|
| 304 | + /* Address doesn't match registered carveout configuration */ |
---|
| 305 | + return -EINVAL; |
---|
| 306 | + } else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) { |
---|
| 307 | + delta = da - mem->da; |
---|
| 308 | + |
---|
| 309 | + /* Check requested resource belongs to registered carveout */ |
---|
| 310 | + if (delta < 0) { |
---|
| 311 | + dev_err(dev, |
---|
| 312 | + "Registered carveout doesn't fit da request\n"); |
---|
| 313 | + return -EINVAL; |
---|
| 314 | + } |
---|
| 315 | + |
---|
| 316 | + if (delta + len > mem->len) { |
---|
| 317 | + dev_err(dev, |
---|
| 318 | + "Registered carveout doesn't fit len request\n"); |
---|
| 319 | + return -EINVAL; |
---|
| 320 | + } |
---|
| 321 | + } |
---|
| 322 | + |
---|
| 323 | + return 0; |
---|
| 324 | +} |
---|
| 325 | + |
---|
204 | 326 | int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) |
---|
205 | 327 | { |
---|
206 | 328 | struct rproc *rproc = rvdev->rproc; |
---|
207 | 329 | struct device *dev = &rproc->dev; |
---|
208 | 330 | struct rproc_vring *rvring = &rvdev->vring[i]; |
---|
209 | 331 | struct fw_rsc_vdev *rsc; |
---|
210 | | - dma_addr_t dma; |
---|
211 | | - void *va; |
---|
212 | | - int ret, size, notifyid; |
---|
| 332 | + int ret, notifyid; |
---|
| 333 | + struct rproc_mem_entry *mem; |
---|
| 334 | + size_t size; |
---|
213 | 335 | |
---|
214 | 336 | /* actual size of vring (in bytes) */ |
---|
215 | 337 | size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); |
---|
216 | 338 | |
---|
217 | | - /* |
---|
218 | | - * Allocate non-cacheable memory for the vring. In the future |
---|
219 | | - * this call will also configure the IOMMU for us |
---|
220 | | - */ |
---|
221 | | - va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); |
---|
222 | | - if (!va) { |
---|
223 | | - dev_err(dev->parent, "dma_alloc_coherent failed\n"); |
---|
224 | | - return -EINVAL; |
---|
| 339 | + rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; |
---|
| 340 | + |
---|
| 341 | + /* Search for pre-registered carveout */ |
---|
| 342 | + mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index, |
---|
| 343 | + i); |
---|
| 344 | + if (mem) { |
---|
| 345 | + if (rproc_check_carveout_da(rproc, mem, rsc->vring[i].da, size)) |
---|
| 346 | + return -ENOMEM; |
---|
| 347 | + } else { |
---|
| 348 | + /* Register carveout in in list */ |
---|
| 349 | + mem = rproc_mem_entry_init(dev, NULL, 0, |
---|
| 350 | + size, rsc->vring[i].da, |
---|
| 351 | + rproc_alloc_carveout, |
---|
| 352 | + rproc_release_carveout, |
---|
| 353 | + "vdev%dvring%d", |
---|
| 354 | + rvdev->index, i); |
---|
| 355 | + if (!mem) { |
---|
| 356 | + dev_err(dev, "Can't allocate memory entry structure\n"); |
---|
| 357 | + return -ENOMEM; |
---|
| 358 | + } |
---|
| 359 | + |
---|
| 360 | + rproc_add_carveout(rproc, mem); |
---|
225 | 361 | } |
---|
226 | 362 | |
---|
227 | 363 | /* |
---|
.. | .. |
---|
232 | 368 | ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL); |
---|
233 | 369 | if (ret < 0) { |
---|
234 | 370 | dev_err(dev, "idr_alloc failed: %d\n", ret); |
---|
235 | | - dma_free_coherent(dev->parent, size, va, dma); |
---|
236 | 371 | return ret; |
---|
237 | 372 | } |
---|
238 | 373 | notifyid = ret; |
---|
.. | .. |
---|
241 | 376 | if (notifyid > rproc->max_notifyid) |
---|
242 | 377 | rproc->max_notifyid = notifyid; |
---|
243 | 378 | |
---|
244 | | - dev_dbg(dev, "vring%d: va %pK dma %pad size 0x%x idr %d\n", |
---|
245 | | - i, va, &dma, size, notifyid); |
---|
246 | | - |
---|
247 | | - rvring->va = va; |
---|
248 | | - rvring->dma = dma; |
---|
249 | 379 | rvring->notifyid = notifyid; |
---|
250 | 380 | |
---|
251 | | - /* |
---|
252 | | - * Let the rproc know the notifyid and da of this vring. |
---|
253 | | - * Not all platforms use dma_alloc_coherent to automatically |
---|
254 | | - * set up the iommu. In this case the device address (da) will |
---|
255 | | - * hold the physical address and not the device address. |
---|
256 | | - */ |
---|
257 | | - rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; |
---|
258 | | - rsc->vring[i].da = dma; |
---|
| 381 | + /* Let the rproc know the notifyid of this vring.*/ |
---|
259 | 382 | rsc->vring[i].notifyid = notifyid; |
---|
260 | 383 | return 0; |
---|
261 | 384 | } |
---|
.. | .. |
---|
287 | 410 | |
---|
288 | 411 | void rproc_free_vring(struct rproc_vring *rvring) |
---|
289 | 412 | { |
---|
290 | | - int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); |
---|
291 | 413 | struct rproc *rproc = rvring->rvdev->rproc; |
---|
292 | 414 | int idx = rvring - rvring->rvdev->vring; |
---|
293 | 415 | struct fw_rsc_vdev *rsc; |
---|
294 | 416 | |
---|
295 | | - dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); |
---|
296 | 417 | idr_remove(&rproc->notifyids, rvring->notifyid); |
---|
297 | 418 | |
---|
298 | | - /* reset resource entry info */ |
---|
299 | | - rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset; |
---|
300 | | - rsc->vring[idx].da = 0; |
---|
301 | | - rsc->vring[idx].notifyid = -1; |
---|
| 419 | + /* |
---|
| 420 | + * At this point rproc_stop() has been called and the installed resource |
---|
| 421 | + * table in the remote processor memory may no longer be accessible. As |
---|
| 422 | + * such and as per rproc_stop(), rproc->table_ptr points to the cached |
---|
| 423 | + * resource table (rproc->cached_table). The cached resource table is |
---|
| 424 | + * only available when a remote processor has been booted by the |
---|
| 425 | + * remoteproc core, otherwise it is NULL. |
---|
| 426 | + * |
---|
| 427 | + * Based on the above, reset the virtio device section in the cached |
---|
| 428 | + * resource table only if there is one to work with. |
---|
| 429 | + */ |
---|
| 430 | + if (rproc->table_ptr) { |
---|
| 431 | + rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset; |
---|
| 432 | + rsc->vring[idx].da = 0; |
---|
| 433 | + rsc->vring[idx].notifyid = -1; |
---|
| 434 | + } |
---|
302 | 435 | } |
---|
303 | 436 | |
---|
304 | 437 | static int rproc_vdev_do_start(struct rproc_subdev *subdev) |
---|
.. | .. |
---|
311 | 444 | static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) |
---|
312 | 445 | { |
---|
313 | 446 | struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); |
---|
| 447 | + int ret; |
---|
314 | 448 | |
---|
315 | | - rproc_remove_virtio_dev(rvdev); |
---|
| 449 | + ret = device_for_each_child(&rvdev->dev, NULL, rproc_remove_virtio_dev); |
---|
| 450 | + if (ret) |
---|
| 451 | + dev_warn(&rvdev->dev, "can't remove vdev child device: %d\n", ret); |
---|
| 452 | +} |
---|
| 453 | + |
---|
| 454 | +/** |
---|
| 455 | + * rproc_rvdev_release() - release the existence of a rvdev |
---|
| 456 | + * |
---|
| 457 | + * @dev: the subdevice's dev |
---|
| 458 | + */ |
---|
| 459 | +static void rproc_rvdev_release(struct device *dev) |
---|
| 460 | +{ |
---|
| 461 | + struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev); |
---|
| 462 | + |
---|
| 463 | + of_reserved_mem_device_release(dev); |
---|
| 464 | + dma_release_coherent_memory(dev); |
---|
| 465 | + |
---|
| 466 | + kfree(rvdev); |
---|
| 467 | +} |
---|
| 468 | + |
---|
| 469 | +static int copy_dma_range_map(struct device *to, struct device *from) |
---|
| 470 | +{ |
---|
| 471 | + const struct bus_dma_region *map = from->dma_range_map, *new_map, *r; |
---|
| 472 | + int num_ranges = 0; |
---|
| 473 | + |
---|
| 474 | + if (!map) |
---|
| 475 | + return 0; |
---|
| 476 | + |
---|
| 477 | + for (r = map; r->size; r++) |
---|
| 478 | + num_ranges++; |
---|
| 479 | + |
---|
| 480 | + new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)), |
---|
| 481 | + GFP_KERNEL); |
---|
| 482 | + if (!new_map) |
---|
| 483 | + return -ENOMEM; |
---|
| 484 | + to->dma_range_map = new_map; |
---|
| 485 | + return 0; |
---|
316 | 486 | } |
---|
317 | 487 | |
---|
318 | 488 | /** |
---|
319 | 489 | * rproc_handle_vdev() - handle a vdev fw resource |
---|
320 | 490 | * @rproc: the remote processor |
---|
321 | | - * @rsc: the vring resource descriptor |
---|
| 491 | + * @ptr: the vring resource descriptor |
---|
| 492 | + * @offset: offset of the resource entry |
---|
322 | 493 | * @avail: size of available data (for sanity checking the image) |
---|
323 | 494 | * |
---|
324 | 495 | * This resource entry requests the host to statically register a virtio |
---|
.. | .. |
---|
342 | 513 | * |
---|
343 | 514 | * Returns 0 on success, or an appropriate error code otherwise |
---|
344 | 515 | */ |
---|
345 | | -static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, |
---|
| 516 | +static int rproc_handle_vdev(struct rproc *rproc, void *ptr, |
---|
346 | 517 | int offset, int avail) |
---|
347 | 518 | { |
---|
| 519 | + struct fw_rsc_vdev *rsc = ptr; |
---|
348 | 520 | struct device *dev = &rproc->dev; |
---|
349 | 521 | struct rproc_vdev *rvdev; |
---|
350 | 522 | int i, ret; |
---|
| 523 | + char name[16]; |
---|
351 | 524 | |
---|
352 | 525 | /* make sure resource isn't truncated */ |
---|
353 | | - if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) |
---|
354 | | - + rsc->config_len > avail) { |
---|
| 526 | + if (struct_size(rsc, vring, rsc->num_of_vrings) + rsc->config_len > |
---|
| 527 | + avail) { |
---|
355 | 528 | dev_err(dev, "vdev rsc is truncated\n"); |
---|
356 | 529 | return -EINVAL; |
---|
357 | 530 | } |
---|
.. | .. |
---|
379 | 552 | |
---|
380 | 553 | rvdev->id = rsc->id; |
---|
381 | 554 | rvdev->rproc = rproc; |
---|
| 555 | + rvdev->index = rproc->nb_vdev++; |
---|
| 556 | + |
---|
| 557 | + /* Initialise vdev subdevice */ |
---|
| 558 | + snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); |
---|
| 559 | + rvdev->dev.parent = &rproc->dev; |
---|
| 560 | + rvdev->dev.release = rproc_rvdev_release; |
---|
| 561 | + dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); |
---|
| 562 | + dev_set_drvdata(&rvdev->dev, rvdev); |
---|
| 563 | + |
---|
| 564 | + ret = device_register(&rvdev->dev); |
---|
| 565 | + if (ret) { |
---|
| 566 | + put_device(&rvdev->dev); |
---|
| 567 | + return ret; |
---|
| 568 | + } |
---|
| 569 | + |
---|
| 570 | + ret = copy_dma_range_map(&rvdev->dev, rproc->dev.parent); |
---|
| 571 | + if (ret) |
---|
| 572 | + goto free_rvdev; |
---|
| 573 | + |
---|
| 574 | + /* Make device dma capable by inheriting from parent's capabilities */ |
---|
| 575 | + set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent)); |
---|
| 576 | + |
---|
| 577 | + ret = dma_coerce_mask_and_coherent(&rvdev->dev, |
---|
| 578 | + dma_get_mask(rproc->dev.parent)); |
---|
| 579 | + if (ret) { |
---|
| 580 | + dev_warn(dev, |
---|
| 581 | + "Failed to set DMA mask %llx. Trying to continue... %x\n", |
---|
| 582 | + dma_get_mask(rproc->dev.parent), ret); |
---|
| 583 | + } |
---|
382 | 584 | |
---|
383 | 585 | /* parse the vrings */ |
---|
384 | 586 | for (i = 0; i < rsc->num_of_vrings; i++) { |
---|
.. | .. |
---|
410 | 612 | for (i--; i >= 0; i--) |
---|
411 | 613 | rproc_free_vring(&rvdev->vring[i]); |
---|
412 | 614 | free_rvdev: |
---|
413 | | - kfree(rvdev); |
---|
| 615 | + device_unregister(&rvdev->dev); |
---|
414 | 616 | return ret; |
---|
415 | 617 | } |
---|
416 | 618 | |
---|
.. | .. |
---|
423 | 625 | |
---|
424 | 626 | for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) { |
---|
425 | 627 | rvring = &rvdev->vring[id]; |
---|
426 | | - if (!rvring->va) |
---|
427 | | - continue; |
---|
428 | | - |
---|
429 | 628 | rproc_free_vring(rvring); |
---|
430 | 629 | } |
---|
431 | 630 | |
---|
432 | 631 | rproc_remove_subdev(rproc, &rvdev->subdev); |
---|
433 | 632 | list_del(&rvdev->node); |
---|
434 | | - kfree(rvdev); |
---|
| 633 | + device_unregister(&rvdev->dev); |
---|
435 | 634 | } |
---|
436 | 635 | |
---|
437 | 636 | /** |
---|
438 | 637 | * rproc_handle_trace() - handle a shared trace buffer resource |
---|
439 | 638 | * @rproc: the remote processor |
---|
440 | | - * @rsc: the trace resource descriptor |
---|
| 639 | + * @ptr: the trace resource descriptor |
---|
| 640 | + * @offset: offset of the resource entry |
---|
441 | 641 | * @avail: size of available data (for sanity checking the image) |
---|
442 | 642 | * |
---|
443 | 643 | * In case the remote processor dumps trace logs into memory, |
---|
.. | .. |
---|
450 | 650 | * |
---|
451 | 651 | * Returns 0 on success, or an appropriate error code otherwise |
---|
452 | 652 | */ |
---|
453 | | -static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, |
---|
| 653 | +static int rproc_handle_trace(struct rproc *rproc, void *ptr, |
---|
454 | 654 | int offset, int avail) |
---|
455 | 655 | { |
---|
456 | | - struct rproc_mem_entry *trace; |
---|
| 656 | + struct fw_rsc_trace *rsc = ptr; |
---|
| 657 | + struct rproc_debug_trace *trace; |
---|
457 | 658 | struct device *dev = &rproc->dev; |
---|
458 | | - void *ptr; |
---|
459 | 659 | char name[15]; |
---|
460 | 660 | |
---|
461 | 661 | if (sizeof(*rsc) > avail) { |
---|
.. | .. |
---|
469 | 669 | return -EINVAL; |
---|
470 | 670 | } |
---|
471 | 671 | |
---|
472 | | - /* what's the kernel address of this resource ? */ |
---|
473 | | - ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); |
---|
474 | | - if (!ptr) { |
---|
475 | | - dev_err(dev, "erroneous trace resource entry\n"); |
---|
476 | | - return -EINVAL; |
---|
477 | | - } |
---|
478 | | - |
---|
479 | 672 | trace = kzalloc(sizeof(*trace), GFP_KERNEL); |
---|
480 | 673 | if (!trace) |
---|
481 | 674 | return -ENOMEM; |
---|
482 | 675 | |
---|
483 | 676 | /* set the trace buffer dma properties */ |
---|
484 | | - trace->len = rsc->len; |
---|
485 | | - trace->va = ptr; |
---|
| 677 | + trace->trace_mem.len = rsc->len; |
---|
| 678 | + trace->trace_mem.da = rsc->da; |
---|
| 679 | + |
---|
| 680 | + /* set pointer on rproc device */ |
---|
| 681 | + trace->rproc = rproc; |
---|
486 | 682 | |
---|
487 | 683 | /* make sure snprintf always null terminates, even if truncating */ |
---|
488 | 684 | snprintf(name, sizeof(name), "trace%d", rproc->num_traces); |
---|
489 | 685 | |
---|
490 | 686 | /* create the debugfs entry */ |
---|
491 | | - trace->priv = rproc_create_trace_file(name, rproc, trace); |
---|
492 | | - if (!trace->priv) { |
---|
493 | | - trace->va = NULL; |
---|
| 687 | + trace->tfile = rproc_create_trace_file(name, rproc, trace); |
---|
| 688 | + if (!trace->tfile) { |
---|
494 | 689 | kfree(trace); |
---|
495 | 690 | return -EINVAL; |
---|
496 | 691 | } |
---|
.. | .. |
---|
499 | 694 | |
---|
500 | 695 | rproc->num_traces++; |
---|
501 | 696 | |
---|
502 | | - dev_dbg(dev, "%s added: va %pK, da 0x%x, len 0x%x\n", |
---|
503 | | - name, ptr, rsc->da, rsc->len); |
---|
| 697 | + dev_dbg(dev, "%s added: da 0x%x, len 0x%x\n", |
---|
| 698 | + name, rsc->da, rsc->len); |
---|
504 | 699 | |
---|
505 | 700 | return 0; |
---|
506 | 701 | } |
---|
.. | .. |
---|
508 | 703 | /** |
---|
509 | 704 | * rproc_handle_devmem() - handle devmem resource entry |
---|
510 | 705 | * @rproc: remote processor handle |
---|
511 | | - * @rsc: the devmem resource entry |
---|
| 706 | + * @ptr: the devmem resource entry |
---|
| 707 | + * @offset: offset of the resource entry |
---|
512 | 708 | * @avail: size of available data (for sanity checking the image) |
---|
513 | 709 | * |
---|
514 | 710 | * Remote processors commonly need to access certain on-chip peripherals. |
---|
.. | .. |
---|
530 | 726 | * and not allow firmwares to request access to physical addresses that |
---|
531 | 727 | * are outside those ranges. |
---|
532 | 728 | */ |
---|
533 | | -static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, |
---|
| 729 | +static int rproc_handle_devmem(struct rproc *rproc, void *ptr, |
---|
534 | 730 | int offset, int avail) |
---|
535 | 731 | { |
---|
| 732 | + struct fw_rsc_devmem *rsc = ptr; |
---|
536 | 733 | struct rproc_mem_entry *mapping; |
---|
537 | 734 | struct device *dev = &rproc->dev; |
---|
538 | 735 | int ret; |
---|
.. | .. |
---|
584 | 781 | } |
---|
585 | 782 | |
---|
586 | 783 | /** |
---|
587 | | - * rproc_handle_carveout() - handle phys contig memory allocation requests |
---|
| 784 | + * rproc_alloc_carveout() - allocated specified carveout |
---|
588 | 785 | * @rproc: rproc handle |
---|
589 | | - * @rsc: the resource entry |
---|
590 | | - * @avail: size of available data (for image validation) |
---|
| 786 | + * @mem: the memory entry to allocate |
---|
591 | 787 | * |
---|
592 | | - * This function will handle firmware requests for allocation of physically |
---|
593 | | - * contiguous memory regions. |
---|
594 | | - * |
---|
595 | | - * These request entries should come first in the firmware's resource table, |
---|
596 | | - * as other firmware entries might request placing other data objects inside |
---|
597 | | - * these memory regions (e.g. data/code segments, trace resource entries, ...). |
---|
598 | | - * |
---|
599 | | - * Allocating memory this way helps utilizing the reserved physical memory |
---|
600 | | - * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries |
---|
601 | | - * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB |
---|
602 | | - * pressure is important; it may have a substantial impact on performance. |
---|
| 788 | + * This function allocate specified memory entry @mem using |
---|
| 789 | + * dma_alloc_coherent() as default allocator |
---|
603 | 790 | */ |
---|
604 | | -static int rproc_handle_carveout(struct rproc *rproc, |
---|
605 | | - struct fw_rsc_carveout *rsc, |
---|
606 | | - int offset, int avail) |
---|
| 791 | +static int rproc_alloc_carveout(struct rproc *rproc, |
---|
| 792 | + struct rproc_mem_entry *mem) |
---|
607 | 793 | { |
---|
608 | | - struct rproc_mem_entry *carveout, *mapping; |
---|
| 794 | + struct rproc_mem_entry *mapping = NULL; |
---|
609 | 795 | struct device *dev = &rproc->dev; |
---|
610 | 796 | dma_addr_t dma; |
---|
611 | 797 | void *va; |
---|
612 | 798 | int ret; |
---|
613 | 799 | |
---|
614 | | - if (sizeof(*rsc) > avail) { |
---|
615 | | - dev_err(dev, "carveout rsc is truncated\n"); |
---|
616 | | - return -EINVAL; |
---|
617 | | - } |
---|
618 | | - |
---|
619 | | - /* make sure reserved bytes are zeroes */ |
---|
620 | | - if (rsc->reserved) { |
---|
621 | | - dev_err(dev, "carveout rsc has non zero reserved bytes\n"); |
---|
622 | | - return -EINVAL; |
---|
623 | | - } |
---|
624 | | - |
---|
625 | | - dev_dbg(dev, "carveout rsc: name: %s, da 0x%x, pa 0x%x, len 0x%x, flags 0x%x\n", |
---|
626 | | - rsc->name, rsc->da, rsc->pa, rsc->len, rsc->flags); |
---|
627 | | - |
---|
628 | | - carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); |
---|
629 | | - if (!carveout) |
---|
630 | | - return -ENOMEM; |
---|
631 | | - |
---|
632 | | - va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); |
---|
| 800 | + va = dma_alloc_coherent(dev->parent, mem->len, &dma, GFP_KERNEL); |
---|
633 | 801 | if (!va) { |
---|
634 | 802 | dev_err(dev->parent, |
---|
635 | | - "failed to allocate dma memory: len 0x%x\n", rsc->len); |
---|
636 | | - ret = -ENOMEM; |
---|
637 | | - goto free_carv; |
---|
| 803 | + "failed to allocate dma memory: len 0x%zx\n", |
---|
| 804 | + mem->len); |
---|
| 805 | + return -ENOMEM; |
---|
638 | 806 | } |
---|
639 | 807 | |
---|
640 | | - dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n", |
---|
641 | | - va, &dma, rsc->len); |
---|
| 808 | + dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%zx\n", |
---|
| 809 | + va, &dma, mem->len); |
---|
| 810 | + |
---|
| 811 | + if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) { |
---|
| 812 | + /* |
---|
| 813 | + * Check requested da is equal to dma address |
---|
| 814 | + * and print a warn message in case of missalignment. |
---|
| 815 | + * Don't stop rproc_start sequence as coprocessor may |
---|
| 816 | + * build pa to da translation on its side. |
---|
| 817 | + */ |
---|
| 818 | + if (mem->da != (u32)dma) |
---|
| 819 | + dev_warn(dev->parent, |
---|
| 820 | + "Allocated carveout doesn't fit device address request\n"); |
---|
| 821 | + } |
---|
642 | 822 | |
---|
643 | 823 | /* |
---|
644 | 824 | * Ok, this is non-standard. |
---|
.. | .. |
---|
657 | 837 | * to use the iommu-based DMA API: we expect 'dma' to contain the |
---|
658 | 838 | * physical address in this case. |
---|
659 | 839 | */ |
---|
660 | | - if (rproc->domain) { |
---|
| 840 | + if (mem->da != FW_RSC_ADDR_ANY && rproc->domain) { |
---|
661 | 841 | mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); |
---|
662 | 842 | if (!mapping) { |
---|
663 | 843 | ret = -ENOMEM; |
---|
664 | 844 | goto dma_free; |
---|
665 | 845 | } |
---|
666 | 846 | |
---|
667 | | - ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len, |
---|
668 | | - rsc->flags); |
---|
| 847 | + ret = iommu_map(rproc->domain, mem->da, dma, mem->len, |
---|
| 848 | + mem->flags); |
---|
669 | 849 | if (ret) { |
---|
670 | 850 | dev_err(dev, "iommu_map failed: %d\n", ret); |
---|
671 | 851 | goto free_mapping; |
---|
.. | .. |
---|
678 | 858 | * We can't trust the remote processor not to change the |
---|
679 | 859 | * resource table, so we must maintain this info independently. |
---|
680 | 860 | */ |
---|
681 | | - mapping->da = rsc->da; |
---|
682 | | - mapping->len = rsc->len; |
---|
| 861 | + mapping->da = mem->da; |
---|
| 862 | + mapping->len = mem->len; |
---|
683 | 863 | list_add_tail(&mapping->node, &rproc->mappings); |
---|
684 | 864 | |
---|
685 | 865 | dev_dbg(dev, "carveout mapped 0x%x to %pad\n", |
---|
686 | | - rsc->da, &dma); |
---|
| 866 | + mem->da, &dma); |
---|
687 | 867 | } |
---|
688 | 868 | |
---|
689 | | - /* |
---|
690 | | - * Some remote processors might need to know the pa |
---|
691 | | - * even though they are behind an IOMMU. E.g., OMAP4's |
---|
692 | | - * remote M3 processor needs this so it can control |
---|
693 | | - * on-chip hardware accelerators that are not behind |
---|
694 | | - * the IOMMU, and therefor must know the pa. |
---|
695 | | - * |
---|
696 | | - * Generally we don't want to expose physical addresses |
---|
697 | | - * if we don't have to (remote processors are generally |
---|
698 | | - * _not_ trusted), so we might want to do this only for |
---|
699 | | - * remote processor that _must_ have this (e.g. OMAP4's |
---|
700 | | - * dual M3 subsystem). |
---|
701 | | - * |
---|
702 | | - * Non-IOMMU processors might also want to have this info. |
---|
703 | | - * In this case, the device address and the physical address |
---|
704 | | - * are the same. |
---|
705 | | - */ |
---|
706 | | - rsc->pa = dma; |
---|
| 869 | + if (mem->da == FW_RSC_ADDR_ANY) { |
---|
| 870 | + /* Update device address as undefined by requester */ |
---|
| 871 | + if ((u64)dma & HIGH_BITS_MASK) |
---|
| 872 | + dev_warn(dev, "DMA address cast in 32bit to fit resource table format\n"); |
---|
707 | 873 | |
---|
708 | | - carveout->va = va; |
---|
709 | | - carveout->len = rsc->len; |
---|
710 | | - carveout->dma = dma; |
---|
711 | | - carveout->da = rsc->da; |
---|
| 874 | + mem->da = (u32)dma; |
---|
| 875 | + } |
---|
712 | 876 | |
---|
713 | | - list_add_tail(&carveout->node, &rproc->carveouts); |
---|
| 877 | + mem->dma = dma; |
---|
| 878 | + mem->va = va; |
---|
714 | 879 | |
---|
715 | 880 | return 0; |
---|
716 | 881 | |
---|
717 | 882 | free_mapping: |
---|
718 | 883 | kfree(mapping); |
---|
719 | 884 | dma_free: |
---|
720 | | - dma_free_coherent(dev->parent, rsc->len, va, dma); |
---|
721 | | -free_carv: |
---|
722 | | - kfree(carveout); |
---|
| 885 | + dma_free_coherent(dev->parent, mem->len, va, dma); |
---|
723 | 886 | return ret; |
---|
724 | 887 | } |
---|
| 888 | + |
---|
| 889 | +/** |
---|
| 890 | + * rproc_release_carveout() - release acquired carveout |
---|
| 891 | + * @rproc: rproc handle |
---|
| 892 | + * @mem: the memory entry to release |
---|
| 893 | + * |
---|
| 894 | + * This function releases specified memory entry @mem allocated via |
---|
| 895 | + * rproc_alloc_carveout() function by @rproc. |
---|
| 896 | + */ |
---|
| 897 | +static int rproc_release_carveout(struct rproc *rproc, |
---|
| 898 | + struct rproc_mem_entry *mem) |
---|
| 899 | +{ |
---|
| 900 | + struct device *dev = &rproc->dev; |
---|
| 901 | + |
---|
| 902 | + /* clean up carveout allocations */ |
---|
| 903 | + dma_free_coherent(dev->parent, mem->len, mem->va, mem->dma); |
---|
| 904 | + return 0; |
---|
| 905 | +} |
---|
| 906 | + |
---|
| 907 | +/** |
---|
| 908 | + * rproc_handle_carveout() - handle phys contig memory allocation requests |
---|
| 909 | + * @rproc: rproc handle |
---|
| 910 | + * @ptr: the resource entry |
---|
| 911 | + * @offset: offset of the resource entry |
---|
| 912 | + * @avail: size of available data (for image validation) |
---|
| 913 | + * |
---|
| 914 | + * This function will handle firmware requests for allocation of physically |
---|
| 915 | + * contiguous memory regions. |
---|
| 916 | + * |
---|
| 917 | + * These request entries should come first in the firmware's resource table, |
---|
| 918 | + * as other firmware entries might request placing other data objects inside |
---|
| 919 | + * these memory regions (e.g. data/code segments, trace resource entries, ...). |
---|
| 920 | + * |
---|
| 921 | + * Allocating memory this way helps utilizing the reserved physical memory |
---|
| 922 | + * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries |
---|
| 923 | + * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB |
---|
| 924 | + * pressure is important; it may have a substantial impact on performance. |
---|
| 925 | + */ |
---|
| 926 | +static int rproc_handle_carveout(struct rproc *rproc, |
---|
| 927 | + void *ptr, int offset, int avail) |
---|
| 928 | +{ |
---|
| 929 | + struct fw_rsc_carveout *rsc = ptr; |
---|
| 930 | + struct rproc_mem_entry *carveout; |
---|
| 931 | + struct device *dev = &rproc->dev; |
---|
| 932 | + |
---|
| 933 | + if (sizeof(*rsc) > avail) { |
---|
| 934 | + dev_err(dev, "carveout rsc is truncated\n"); |
---|
| 935 | + return -EINVAL; |
---|
| 936 | + } |
---|
| 937 | + |
---|
| 938 | + /* make sure reserved bytes are zeroes */ |
---|
| 939 | + if (rsc->reserved) { |
---|
| 940 | + dev_err(dev, "carveout rsc has non zero reserved bytes\n"); |
---|
| 941 | + return -EINVAL; |
---|
| 942 | + } |
---|
| 943 | + |
---|
| 944 | + dev_dbg(dev, "carveout rsc: name: %s, da 0x%x, pa 0x%x, len 0x%x, flags 0x%x\n", |
---|
| 945 | + rsc->name, rsc->da, rsc->pa, rsc->len, rsc->flags); |
---|
| 946 | + |
---|
| 947 | + /* |
---|
| 948 | + * Check carveout rsc already part of a registered carveout, |
---|
| 949 | + * Search by name, then check the da and length |
---|
| 950 | + */ |
---|
| 951 | + carveout = rproc_find_carveout_by_name(rproc, rsc->name); |
---|
| 952 | + |
---|
| 953 | + if (carveout) { |
---|
| 954 | + if (carveout->rsc_offset != FW_RSC_ADDR_ANY) { |
---|
| 955 | + dev_err(dev, |
---|
| 956 | + "Carveout already associated to resource table\n"); |
---|
| 957 | + return -ENOMEM; |
---|
| 958 | + } |
---|
| 959 | + |
---|
| 960 | + if (rproc_check_carveout_da(rproc, carveout, rsc->da, rsc->len)) |
---|
| 961 | + return -ENOMEM; |
---|
| 962 | + |
---|
| 963 | + /* Update memory carveout with resource table info */ |
---|
| 964 | + carveout->rsc_offset = offset; |
---|
| 965 | + carveout->flags = rsc->flags; |
---|
| 966 | + |
---|
| 967 | + return 0; |
---|
| 968 | + } |
---|
| 969 | + |
---|
| 970 | + /* Register carveout in in list */ |
---|
| 971 | + carveout = rproc_mem_entry_init(dev, NULL, 0, rsc->len, rsc->da, |
---|
| 972 | + rproc_alloc_carveout, |
---|
| 973 | + rproc_release_carveout, rsc->name); |
---|
| 974 | + if (!carveout) { |
---|
| 975 | + dev_err(dev, "Can't allocate memory entry structure\n"); |
---|
| 976 | + return -ENOMEM; |
---|
| 977 | + } |
---|
| 978 | + |
---|
| 979 | + carveout->flags = rsc->flags; |
---|
| 980 | + carveout->rsc_offset = offset; |
---|
| 981 | + rproc_add_carveout(rproc, carveout); |
---|
| 982 | + |
---|
| 983 | + return 0; |
---|
| 984 | +} |
---|
| 985 | + |
---|
| 986 | +/** |
---|
| 987 | + * rproc_add_carveout() - register an allocated carveout region |
---|
| 988 | + * @rproc: rproc handle |
---|
| 989 | + * @mem: memory entry to register |
---|
| 990 | + * |
---|
| 991 | + * This function registers specified memory entry in @rproc carveouts list. |
---|
| 992 | + * Specified carveout should have been allocated before registering. |
---|
| 993 | + */ |
---|
| 994 | +void rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem) |
---|
| 995 | +{ |
---|
| 996 | + list_add_tail(&mem->node, &rproc->carveouts); |
---|
| 997 | +} |
---|
| 998 | +EXPORT_SYMBOL(rproc_add_carveout); |
---|
| 999 | + |
---|
| 1000 | +/** |
---|
| 1001 | + * rproc_mem_entry_init() - allocate and initialize rproc_mem_entry struct |
---|
| 1002 | + * @dev: pointer on device struct |
---|
| 1003 | + * @va: virtual address |
---|
| 1004 | + * @dma: dma address |
---|
| 1005 | + * @len: memory carveout length |
---|
| 1006 | + * @da: device address |
---|
| 1007 | + * @alloc: memory carveout allocation function |
---|
| 1008 | + * @release: memory carveout release function |
---|
| 1009 | + * @name: carveout name |
---|
| 1010 | + * |
---|
| 1011 | + * This function allocates a rproc_mem_entry struct and fill it with parameters |
---|
| 1012 | + * provided by client. |
---|
| 1013 | + */ |
---|
| 1014 | +__printf(8, 9) |
---|
| 1015 | +struct rproc_mem_entry * |
---|
| 1016 | +rproc_mem_entry_init(struct device *dev, |
---|
| 1017 | + void *va, dma_addr_t dma, size_t len, u32 da, |
---|
| 1018 | + int (*alloc)(struct rproc *, struct rproc_mem_entry *), |
---|
| 1019 | + int (*release)(struct rproc *, struct rproc_mem_entry *), |
---|
| 1020 | + const char *name, ...) |
---|
| 1021 | +{ |
---|
| 1022 | + struct rproc_mem_entry *mem; |
---|
| 1023 | + va_list args; |
---|
| 1024 | + |
---|
| 1025 | + mem = kzalloc(sizeof(*mem), GFP_KERNEL); |
---|
| 1026 | + if (!mem) |
---|
| 1027 | + return mem; |
---|
| 1028 | + |
---|
| 1029 | + mem->va = va; |
---|
| 1030 | + mem->dma = dma; |
---|
| 1031 | + mem->da = da; |
---|
| 1032 | + mem->len = len; |
---|
| 1033 | + mem->alloc = alloc; |
---|
| 1034 | + mem->release = release; |
---|
| 1035 | + mem->rsc_offset = FW_RSC_ADDR_ANY; |
---|
| 1036 | + mem->of_resm_idx = -1; |
---|
| 1037 | + |
---|
| 1038 | + va_start(args, name); |
---|
| 1039 | + vsnprintf(mem->name, sizeof(mem->name), name, args); |
---|
| 1040 | + va_end(args); |
---|
| 1041 | + |
---|
| 1042 | + return mem; |
---|
| 1043 | +} |
---|
| 1044 | +EXPORT_SYMBOL(rproc_mem_entry_init); |
---|
| 1045 | + |
---|
| 1046 | +/** |
---|
| 1047 | + * rproc_of_resm_mem_entry_init() - allocate and initialize rproc_mem_entry struct |
---|
| 1048 | + * from a reserved memory phandle |
---|
| 1049 | + * @dev: pointer on device struct |
---|
| 1050 | + * @of_resm_idx: reserved memory phandle index in "memory-region" |
---|
| 1051 | + * @len: memory carveout length |
---|
| 1052 | + * @da: device address |
---|
| 1053 | + * @name: carveout name |
---|
| 1054 | + * |
---|
| 1055 | + * This function allocates a rproc_mem_entry struct and fill it with parameters |
---|
| 1056 | + * provided by client. |
---|
| 1057 | + */ |
---|
| 1058 | +__printf(5, 6) |
---|
| 1059 | +struct rproc_mem_entry * |
---|
| 1060 | +rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len, |
---|
| 1061 | + u32 da, const char *name, ...) |
---|
| 1062 | +{ |
---|
| 1063 | + struct rproc_mem_entry *mem; |
---|
| 1064 | + va_list args; |
---|
| 1065 | + |
---|
| 1066 | + mem = kzalloc(sizeof(*mem), GFP_KERNEL); |
---|
| 1067 | + if (!mem) |
---|
| 1068 | + return mem; |
---|
| 1069 | + |
---|
| 1070 | + mem->da = da; |
---|
| 1071 | + mem->len = len; |
---|
| 1072 | + mem->rsc_offset = FW_RSC_ADDR_ANY; |
---|
| 1073 | + mem->of_resm_idx = of_resm_idx; |
---|
| 1074 | + |
---|
| 1075 | + va_start(args, name); |
---|
| 1076 | + vsnprintf(mem->name, sizeof(mem->name), name, args); |
---|
| 1077 | + va_end(args); |
---|
| 1078 | + |
---|
| 1079 | + return mem; |
---|
| 1080 | +} |
---|
| 1081 | +EXPORT_SYMBOL(rproc_of_resm_mem_entry_init); |
---|
| 1082 | + |
---|
| 1083 | +/** |
---|
| 1084 | + * rproc_of_parse_firmware() - parse and return the firmware-name |
---|
| 1085 | + * @dev: pointer on device struct representing a rproc |
---|
| 1086 | + * @index: index to use for the firmware-name retrieval |
---|
| 1087 | + * @fw_name: pointer to a character string, in which the firmware |
---|
| 1088 | + * name is returned on success and unmodified otherwise. |
---|
| 1089 | + * |
---|
| 1090 | + * This is an OF helper function that parses a device's DT node for |
---|
| 1091 | + * the "firmware-name" property and returns the firmware name pointer |
---|
| 1092 | + * in @fw_name on success. |
---|
| 1093 | + * |
---|
| 1094 | + * Return: 0 on success, or an appropriate failure. |
---|
| 1095 | + */ |
---|
| 1096 | +int rproc_of_parse_firmware(struct device *dev, int index, const char **fw_name) |
---|
| 1097 | +{ |
---|
| 1098 | + int ret; |
---|
| 1099 | + |
---|
| 1100 | + ret = of_property_read_string_index(dev->of_node, "firmware-name", |
---|
| 1101 | + index, fw_name); |
---|
| 1102 | + return ret ? ret : 0; |
---|
| 1103 | +} |
---|
| 1104 | +EXPORT_SYMBOL(rproc_of_parse_firmware); |
---|
725 | 1105 | |
---|
726 | 1106 | /* |
---|
727 | 1107 | * A lookup table for resource handlers. The indices are defined in |
---|
728 | 1108 | * enum fw_resource_type. |
---|
729 | 1109 | */ |
---|
730 | 1110 | static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = { |
---|
731 | | - [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, |
---|
732 | | - [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, |
---|
733 | | - [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, |
---|
734 | | - [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev, |
---|
| 1111 | + [RSC_CARVEOUT] = rproc_handle_carveout, |
---|
| 1112 | + [RSC_DEVMEM] = rproc_handle_devmem, |
---|
| 1113 | + [RSC_TRACE] = rproc_handle_trace, |
---|
| 1114 | + [RSC_VDEV] = rproc_handle_vdev, |
---|
735 | 1115 | }; |
---|
736 | 1116 | |
---|
737 | 1117 | /* handle firmware resource entries before booting the remote processor */ |
---|
.. | .. |
---|
758 | 1138 | } |
---|
759 | 1139 | |
---|
760 | 1140 | dev_dbg(dev, "rsc: type %d\n", hdr->type); |
---|
| 1141 | + |
---|
| 1142 | + if (hdr->type >= RSC_VENDOR_START && |
---|
| 1143 | + hdr->type <= RSC_VENDOR_END) { |
---|
| 1144 | + ret = rproc_handle_rsc(rproc, hdr->type, rsc, |
---|
| 1145 | + offset + sizeof(*hdr), avail); |
---|
| 1146 | + if (ret == RSC_HANDLED) |
---|
| 1147 | + continue; |
---|
| 1148 | + else if (ret < 0) |
---|
| 1149 | + break; |
---|
| 1150 | + |
---|
| 1151 | + dev_warn(dev, "unsupported vendor resource %d\n", |
---|
| 1152 | + hdr->type); |
---|
| 1153 | + continue; |
---|
| 1154 | + } |
---|
761 | 1155 | |
---|
762 | 1156 | if (hdr->type >= RSC_LAST) { |
---|
763 | 1157 | dev_warn(dev, "unsupported resource %d\n", hdr->type); |
---|
.. | .. |
---|
845 | 1239 | } |
---|
846 | 1240 | |
---|
847 | 1241 | /** |
---|
848 | | - * rproc_coredump_cleanup() - clean up dump_segments list |
---|
| 1242 | + * rproc_alloc_registered_carveouts() - allocate all carveouts registered |
---|
| 1243 | + * in the list |
---|
849 | 1244 | * @rproc: the remote processor handle |
---|
| 1245 | + * |
---|
| 1246 | + * This function parses registered carveout list, performs allocation |
---|
| 1247 | + * if alloc() ops registered and updates resource table information |
---|
| 1248 | + * if rsc_offset set. |
---|
| 1249 | + * |
---|
| 1250 | + * Return: 0 on success |
---|
850 | 1251 | */ |
---|
851 | | -static void rproc_coredump_cleanup(struct rproc *rproc) |
---|
| 1252 | +static int rproc_alloc_registered_carveouts(struct rproc *rproc) |
---|
852 | 1253 | { |
---|
853 | | - struct rproc_dump_segment *entry, *tmp; |
---|
| 1254 | + struct rproc_mem_entry *entry, *tmp; |
---|
| 1255 | + struct fw_rsc_carveout *rsc; |
---|
| 1256 | + struct device *dev = &rproc->dev; |
---|
| 1257 | + u64 pa; |
---|
| 1258 | + int ret; |
---|
854 | 1259 | |
---|
855 | | - list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) { |
---|
856 | | - list_del(&entry->node); |
---|
857 | | - kfree(entry); |
---|
| 1260 | + list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { |
---|
| 1261 | + if (entry->alloc) { |
---|
| 1262 | + ret = entry->alloc(rproc, entry); |
---|
| 1263 | + if (ret) { |
---|
| 1264 | + dev_err(dev, "Unable to allocate carveout %s: %d\n", |
---|
| 1265 | + entry->name, ret); |
---|
| 1266 | + return -ENOMEM; |
---|
| 1267 | + } |
---|
| 1268 | + } |
---|
| 1269 | + |
---|
| 1270 | + if (entry->rsc_offset != FW_RSC_ADDR_ANY) { |
---|
| 1271 | + /* update resource table */ |
---|
| 1272 | + rsc = (void *)rproc->table_ptr + entry->rsc_offset; |
---|
| 1273 | + |
---|
| 1274 | + /* |
---|
| 1275 | + * Some remote processors might need to know the pa |
---|
| 1276 | + * even though they are behind an IOMMU. E.g., OMAP4's |
---|
| 1277 | + * remote M3 processor needs this so it can control |
---|
| 1278 | + * on-chip hardware accelerators that are not behind |
---|
| 1279 | + * the IOMMU, and therefor must know the pa. |
---|
| 1280 | + * |
---|
| 1281 | + * Generally we don't want to expose physical addresses |
---|
| 1282 | + * if we don't have to (remote processors are generally |
---|
| 1283 | + * _not_ trusted), so we might want to do this only for |
---|
| 1284 | + * remote processor that _must_ have this (e.g. OMAP4's |
---|
| 1285 | + * dual M3 subsystem). |
---|
| 1286 | + * |
---|
| 1287 | + * Non-IOMMU processors might also want to have this info. |
---|
| 1288 | + * In this case, the device address and the physical address |
---|
| 1289 | + * are the same. |
---|
| 1290 | + */ |
---|
| 1291 | + |
---|
| 1292 | + /* Use va if defined else dma to generate pa */ |
---|
| 1293 | + if (entry->va) |
---|
| 1294 | + pa = (u64)rproc_va_to_pa(entry->va); |
---|
| 1295 | + else |
---|
| 1296 | + pa = (u64)entry->dma; |
---|
| 1297 | + |
---|
| 1298 | + if (((u64)pa) & HIGH_BITS_MASK) |
---|
| 1299 | + dev_warn(dev, |
---|
| 1300 | + "Physical address cast in 32bit to fit resource table format\n"); |
---|
| 1301 | + |
---|
| 1302 | + rsc->pa = (u32)pa; |
---|
| 1303 | + rsc->da = entry->da; |
---|
| 1304 | + rsc->len = entry->len; |
---|
| 1305 | + } |
---|
858 | 1306 | } |
---|
| 1307 | + |
---|
| 1308 | + return 0; |
---|
859 | 1309 | } |
---|
| 1310 | + |
---|
860 | 1311 | |
---|
861 | 1312 | /** |
---|
862 | 1313 | * rproc_resource_cleanup() - clean up and free all acquired resources |
---|
.. | .. |
---|
865 | 1316 | * This function will free all resources acquired for @rproc, and it |
---|
866 | 1317 | * is called whenever @rproc either shuts down or fails to boot. |
---|
867 | 1318 | */ |
---|
868 | | -static void rproc_resource_cleanup(struct rproc *rproc) |
---|
| 1319 | +void rproc_resource_cleanup(struct rproc *rproc) |
---|
869 | 1320 | { |
---|
870 | 1321 | struct rproc_mem_entry *entry, *tmp; |
---|
| 1322 | + struct rproc_debug_trace *trace, *ttmp; |
---|
871 | 1323 | struct rproc_vdev *rvdev, *rvtmp; |
---|
872 | 1324 | struct device *dev = &rproc->dev; |
---|
873 | 1325 | |
---|
874 | 1326 | /* clean up debugfs trace entries */ |
---|
875 | | - list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { |
---|
876 | | - rproc_remove_trace_file(entry->priv); |
---|
| 1327 | + list_for_each_entry_safe(trace, ttmp, &rproc->traces, node) { |
---|
| 1328 | + rproc_remove_trace_file(trace->tfile); |
---|
877 | 1329 | rproc->num_traces--; |
---|
878 | | - list_del(&entry->node); |
---|
879 | | - kfree(entry); |
---|
| 1330 | + list_del(&trace->node); |
---|
| 1331 | + kfree(trace); |
---|
880 | 1332 | } |
---|
881 | 1333 | |
---|
882 | 1334 | /* clean up iommu mapping entries */ |
---|
.. | .. |
---|
886 | 1338 | unmapped = iommu_unmap(rproc->domain, entry->da, entry->len); |
---|
887 | 1339 | if (unmapped != entry->len) { |
---|
888 | 1340 | /* nothing much to do besides complaining */ |
---|
889 | | - dev_err(dev, "failed to unmap %u/%zu\n", entry->len, |
---|
| 1341 | + dev_err(dev, "failed to unmap %zx/%zu\n", entry->len, |
---|
890 | 1342 | unmapped); |
---|
891 | 1343 | } |
---|
892 | 1344 | |
---|
.. | .. |
---|
896 | 1348 | |
---|
897 | 1349 | /* clean up carveout allocations */ |
---|
898 | 1350 | list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { |
---|
899 | | - dma_free_coherent(dev->parent, entry->len, entry->va, |
---|
900 | | - entry->dma); |
---|
| 1351 | + if (entry->release) |
---|
| 1352 | + entry->release(rproc, entry); |
---|
901 | 1353 | list_del(&entry->node); |
---|
902 | 1354 | kfree(entry); |
---|
903 | 1355 | } |
---|
.. | .. |
---|
908 | 1360 | |
---|
909 | 1361 | rproc_coredump_cleanup(rproc); |
---|
910 | 1362 | } |
---|
| 1363 | +EXPORT_SYMBOL(rproc_resource_cleanup); |
---|
911 | 1364 | |
---|
912 | 1365 | static int rproc_start(struct rproc *rproc, const struct firmware *fw) |
---|
913 | 1366 | { |
---|
.. | .. |
---|
974 | 1427 | return ret; |
---|
975 | 1428 | } |
---|
976 | 1429 | |
---|
| 1430 | +static int rproc_attach(struct rproc *rproc) |
---|
| 1431 | +{ |
---|
| 1432 | + struct device *dev = &rproc->dev; |
---|
| 1433 | + int ret; |
---|
| 1434 | + |
---|
| 1435 | + ret = rproc_prepare_subdevices(rproc); |
---|
| 1436 | + if (ret) { |
---|
| 1437 | + dev_err(dev, "failed to prepare subdevices for %s: %d\n", |
---|
| 1438 | + rproc->name, ret); |
---|
| 1439 | + goto out; |
---|
| 1440 | + } |
---|
| 1441 | + |
---|
| 1442 | + /* Attach to the remote processor */ |
---|
| 1443 | + ret = rproc_attach_device(rproc); |
---|
| 1444 | + if (ret) { |
---|
| 1445 | + dev_err(dev, "can't attach to rproc %s: %d\n", |
---|
| 1446 | + rproc->name, ret); |
---|
| 1447 | + goto unprepare_subdevices; |
---|
| 1448 | + } |
---|
| 1449 | + |
---|
| 1450 | + /* Start any subdevices for the remote processor */ |
---|
| 1451 | + ret = rproc_start_subdevices(rproc); |
---|
| 1452 | + if (ret) { |
---|
| 1453 | + dev_err(dev, "failed to probe subdevices for %s: %d\n", |
---|
| 1454 | + rproc->name, ret); |
---|
| 1455 | + goto stop_rproc; |
---|
| 1456 | + } |
---|
| 1457 | + |
---|
| 1458 | + rproc->state = RPROC_RUNNING; |
---|
| 1459 | + |
---|
| 1460 | + dev_info(dev, "remote processor %s is now attached\n", rproc->name); |
---|
| 1461 | + |
---|
| 1462 | + return 0; |
---|
| 1463 | + |
---|
| 1464 | +stop_rproc: |
---|
| 1465 | + rproc->ops->stop(rproc); |
---|
| 1466 | +unprepare_subdevices: |
---|
| 1467 | + rproc_unprepare_subdevices(rproc); |
---|
| 1468 | +out: |
---|
| 1469 | + return ret; |
---|
| 1470 | +} |
---|
| 1471 | + |
---|
977 | 1472 | /* |
---|
978 | 1473 | * take a firmware and boot a remote processor with it. |
---|
979 | 1474 | */ |
---|
.. | .. |
---|
999 | 1494 | return ret; |
---|
1000 | 1495 | } |
---|
1001 | 1496 | |
---|
| 1497 | + /* Prepare rproc for firmware loading if needed */ |
---|
| 1498 | + ret = rproc_prepare_device(rproc); |
---|
| 1499 | + if (ret) { |
---|
| 1500 | + dev_err(dev, "can't prepare rproc %s: %d\n", rproc->name, ret); |
---|
| 1501 | + goto disable_iommu; |
---|
| 1502 | + } |
---|
| 1503 | + |
---|
1002 | 1504 | rproc->bootaddr = rproc_get_boot_addr(rproc, fw); |
---|
1003 | 1505 | |
---|
1004 | 1506 | /* Load resource table, core dump segment list etc from the firmware */ |
---|
1005 | 1507 | ret = rproc_parse_fw(rproc, fw); |
---|
1006 | 1508 | if (ret) |
---|
1007 | | - goto disable_iommu; |
---|
| 1509 | + goto unprepare_rproc; |
---|
1008 | 1510 | |
---|
1009 | 1511 | /* reset max_notifyid */ |
---|
1010 | 1512 | rproc->max_notifyid = -1; |
---|
| 1513 | + |
---|
| 1514 | + /* reset handled vdev */ |
---|
| 1515 | + rproc->nb_vdev = 0; |
---|
1011 | 1516 | |
---|
1012 | 1517 | /* handle fw resources which are required to boot rproc */ |
---|
1013 | 1518 | ret = rproc_handle_resources(rproc, rproc_loading_handlers); |
---|
1014 | 1519 | if (ret) { |
---|
1015 | 1520 | dev_err(dev, "Failed to process resources: %d\n", ret); |
---|
| 1521 | + goto clean_up_resources; |
---|
| 1522 | + } |
---|
| 1523 | + |
---|
| 1524 | + /* Allocate carveout resources associated to rproc */ |
---|
| 1525 | + ret = rproc_alloc_registered_carveouts(rproc); |
---|
| 1526 | + if (ret) { |
---|
| 1527 | + dev_err(dev, "Failed to allocate associated carveouts: %d\n", |
---|
| 1528 | + ret); |
---|
1016 | 1529 | goto clean_up_resources; |
---|
1017 | 1530 | } |
---|
1018 | 1531 | |
---|
.. | .. |
---|
1027 | 1540 | kfree(rproc->cached_table); |
---|
1028 | 1541 | rproc->cached_table = NULL; |
---|
1029 | 1542 | rproc->table_ptr = NULL; |
---|
| 1543 | +unprepare_rproc: |
---|
| 1544 | + /* release HW resources if needed */ |
---|
| 1545 | + rproc_unprepare_device(rproc); |
---|
| 1546 | +disable_iommu: |
---|
| 1547 | + rproc_disable_iommu(rproc); |
---|
| 1548 | + return ret; |
---|
| 1549 | +} |
---|
| 1550 | + |
---|
| 1551 | +/* |
---|
| 1552 | + * Attach to remote processor - similar to rproc_fw_boot() but without |
---|
| 1553 | + * the steps that deal with the firmware image. |
---|
| 1554 | + */ |
---|
| 1555 | +static int rproc_actuate(struct rproc *rproc) |
---|
| 1556 | +{ |
---|
| 1557 | + struct device *dev = &rproc->dev; |
---|
| 1558 | + int ret; |
---|
| 1559 | + |
---|
| 1560 | + /* |
---|
| 1561 | + * if enabling an IOMMU isn't relevant for this rproc, this is |
---|
| 1562 | + * just a nop |
---|
| 1563 | + */ |
---|
| 1564 | + ret = rproc_enable_iommu(rproc); |
---|
| 1565 | + if (ret) { |
---|
| 1566 | + dev_err(dev, "can't enable iommu: %d\n", ret); |
---|
| 1567 | + return ret; |
---|
| 1568 | + } |
---|
| 1569 | + |
---|
| 1570 | + /* reset max_notifyid */ |
---|
| 1571 | + rproc->max_notifyid = -1; |
---|
| 1572 | + |
---|
| 1573 | + /* reset handled vdev */ |
---|
| 1574 | + rproc->nb_vdev = 0; |
---|
| 1575 | + |
---|
| 1576 | + /* |
---|
| 1577 | + * Handle firmware resources required to attach to a remote processor. |
---|
| 1578 | + * Because we are attaching rather than booting the remote processor, |
---|
| 1579 | + * we expect the platform driver to properly set rproc->table_ptr. |
---|
| 1580 | + */ |
---|
| 1581 | + ret = rproc_handle_resources(rproc, rproc_loading_handlers); |
---|
| 1582 | + if (ret) { |
---|
| 1583 | + dev_err(dev, "Failed to process resources: %d\n", ret); |
---|
| 1584 | + goto disable_iommu; |
---|
| 1585 | + } |
---|
| 1586 | + |
---|
| 1587 | + /* Allocate carveout resources associated to rproc */ |
---|
| 1588 | + ret = rproc_alloc_registered_carveouts(rproc); |
---|
| 1589 | + if (ret) { |
---|
| 1590 | + dev_err(dev, "Failed to allocate associated carveouts: %d\n", |
---|
| 1591 | + ret); |
---|
| 1592 | + goto clean_up_resources; |
---|
| 1593 | + } |
---|
| 1594 | + |
---|
| 1595 | + ret = rproc_attach(rproc); |
---|
| 1596 | + if (ret) |
---|
| 1597 | + goto clean_up_resources; |
---|
| 1598 | + |
---|
| 1599 | + return 0; |
---|
| 1600 | + |
---|
| 1601 | +clean_up_resources: |
---|
| 1602 | + rproc_resource_cleanup(rproc); |
---|
1030 | 1603 | disable_iommu: |
---|
1031 | 1604 | rproc_disable_iommu(rproc); |
---|
1032 | 1605 | return ret; |
---|
.. | .. |
---|
1052 | 1625 | static int rproc_trigger_auto_boot(struct rproc *rproc) |
---|
1053 | 1626 | { |
---|
1054 | 1627 | int ret; |
---|
| 1628 | + |
---|
| 1629 | + /* |
---|
| 1630 | + * Since the remote processor is in a detached state, it has already |
---|
| 1631 | + * been booted by another entity. As such there is no point in waiting |
---|
| 1632 | + * for a firmware image to be loaded, we can simply initiate the process |
---|
| 1633 | + * of attaching to it immediately. |
---|
| 1634 | + */ |
---|
| 1635 | + if (rproc->state == RPROC_DETACHED) |
---|
| 1636 | + return rproc_boot(rproc); |
---|
1055 | 1637 | |
---|
1056 | 1638 | /* |
---|
1057 | 1639 | * We're initiating an asynchronous firmware loading, so we can |
---|
.. | .. |
---|
1088 | 1670 | |
---|
1089 | 1671 | rproc->state = RPROC_OFFLINE; |
---|
1090 | 1672 | |
---|
| 1673 | + /* |
---|
| 1674 | + * The remote processor has been stopped and is now offline, which means |
---|
| 1675 | + * that the next time it is brought back online the remoteproc core will |
---|
| 1676 | + * be responsible to load its firmware. As such it is no longer |
---|
| 1677 | + * autonomous. |
---|
| 1678 | + */ |
---|
| 1679 | + rproc->autonomous = false; |
---|
| 1680 | + |
---|
1091 | 1681 | dev_info(dev, "stopped remote processor %s\n", rproc->name); |
---|
1092 | 1682 | |
---|
1093 | 1683 | return 0; |
---|
1094 | 1684 | } |
---|
1095 | 1685 | |
---|
1096 | | -/** |
---|
1097 | | - * rproc_coredump_add_segment() - add segment of device memory to coredump |
---|
1098 | | - * @rproc: handle of a remote processor |
---|
1099 | | - * @da: device address |
---|
1100 | | - * @size: size of segment |
---|
1101 | | - * |
---|
1102 | | - * Add device memory to the list of segments to be included in a coredump for |
---|
1103 | | - * the remoteproc. |
---|
1104 | | - * |
---|
1105 | | - * Return: 0 on success, negative errno on error. |
---|
1106 | | - */ |
---|
1107 | | -int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size) |
---|
1108 | | -{ |
---|
1109 | | - struct rproc_dump_segment *segment; |
---|
1110 | | - |
---|
1111 | | - segment = kzalloc(sizeof(*segment), GFP_KERNEL); |
---|
1112 | | - if (!segment) |
---|
1113 | | - return -ENOMEM; |
---|
1114 | | - |
---|
1115 | | - segment->da = da; |
---|
1116 | | - segment->size = size; |
---|
1117 | | - |
---|
1118 | | - list_add_tail(&segment->node, &rproc->dump_segments); |
---|
1119 | | - |
---|
1120 | | - return 0; |
---|
1121 | | -} |
---|
1122 | | -EXPORT_SYMBOL(rproc_coredump_add_segment); |
---|
1123 | | - |
---|
1124 | | -/** |
---|
1125 | | - * rproc_coredump() - perform coredump |
---|
1126 | | - * @rproc: rproc handle |
---|
1127 | | - * |
---|
1128 | | - * This function will generate an ELF header for the registered segments |
---|
1129 | | - * and create a devcoredump device associated with rproc. |
---|
1130 | | - */ |
---|
1131 | | -static void rproc_coredump(struct rproc *rproc) |
---|
1132 | | -{ |
---|
1133 | | - struct rproc_dump_segment *segment; |
---|
1134 | | - struct elf32_phdr *phdr; |
---|
1135 | | - struct elf32_hdr *ehdr; |
---|
1136 | | - size_t data_size; |
---|
1137 | | - size_t offset; |
---|
1138 | | - void *data; |
---|
1139 | | - void *ptr; |
---|
1140 | | - int phnum = 0; |
---|
1141 | | - |
---|
1142 | | - if (list_empty(&rproc->dump_segments)) |
---|
1143 | | - return; |
---|
1144 | | - |
---|
1145 | | - data_size = sizeof(*ehdr); |
---|
1146 | | - list_for_each_entry(segment, &rproc->dump_segments, node) { |
---|
1147 | | - data_size += sizeof(*phdr) + segment->size; |
---|
1148 | | - |
---|
1149 | | - phnum++; |
---|
1150 | | - } |
---|
1151 | | - |
---|
1152 | | - data = vmalloc(data_size); |
---|
1153 | | - if (!data) |
---|
1154 | | - return; |
---|
1155 | | - |
---|
1156 | | - ehdr = data; |
---|
1157 | | - |
---|
1158 | | - memset(ehdr, 0, sizeof(*ehdr)); |
---|
1159 | | - memcpy(ehdr->e_ident, ELFMAG, SELFMAG); |
---|
1160 | | - ehdr->e_ident[EI_CLASS] = ELFCLASS32; |
---|
1161 | | - ehdr->e_ident[EI_DATA] = ELFDATA2LSB; |
---|
1162 | | - ehdr->e_ident[EI_VERSION] = EV_CURRENT; |
---|
1163 | | - ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; |
---|
1164 | | - ehdr->e_type = ET_CORE; |
---|
1165 | | - ehdr->e_machine = EM_NONE; |
---|
1166 | | - ehdr->e_version = EV_CURRENT; |
---|
1167 | | - ehdr->e_entry = rproc->bootaddr; |
---|
1168 | | - ehdr->e_phoff = sizeof(*ehdr); |
---|
1169 | | - ehdr->e_ehsize = sizeof(*ehdr); |
---|
1170 | | - ehdr->e_phentsize = sizeof(*phdr); |
---|
1171 | | - ehdr->e_phnum = phnum; |
---|
1172 | | - |
---|
1173 | | - phdr = data + ehdr->e_phoff; |
---|
1174 | | - offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum; |
---|
1175 | | - list_for_each_entry(segment, &rproc->dump_segments, node) { |
---|
1176 | | - memset(phdr, 0, sizeof(*phdr)); |
---|
1177 | | - phdr->p_type = PT_LOAD; |
---|
1178 | | - phdr->p_offset = offset; |
---|
1179 | | - phdr->p_vaddr = segment->da; |
---|
1180 | | - phdr->p_paddr = segment->da; |
---|
1181 | | - phdr->p_filesz = segment->size; |
---|
1182 | | - phdr->p_memsz = segment->size; |
---|
1183 | | - phdr->p_flags = PF_R | PF_W | PF_X; |
---|
1184 | | - phdr->p_align = 0; |
---|
1185 | | - |
---|
1186 | | - ptr = rproc_da_to_va(rproc, segment->da, segment->size); |
---|
1187 | | - if (!ptr) { |
---|
1188 | | - dev_err(&rproc->dev, |
---|
1189 | | - "invalid coredump segment (%pad, %zu)\n", |
---|
1190 | | - &segment->da, segment->size); |
---|
1191 | | - memset(data + offset, 0xff, segment->size); |
---|
1192 | | - } else { |
---|
1193 | | - memcpy(data + offset, ptr, segment->size); |
---|
1194 | | - } |
---|
1195 | | - |
---|
1196 | | - offset += phdr->p_filesz; |
---|
1197 | | - phdr++; |
---|
1198 | | - } |
---|
1199 | | - |
---|
1200 | | - dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL); |
---|
1201 | | -} |
---|
1202 | 1686 | |
---|
1203 | 1687 | /** |
---|
1204 | 1688 | * rproc_trigger_recovery() - recover a remoteproc |
---|
.. | .. |
---|
1216 | 1700 | struct device *dev = &rproc->dev; |
---|
1217 | 1701 | int ret; |
---|
1218 | 1702 | |
---|
1219 | | - dev_err(dev, "recovering %s\n", rproc->name); |
---|
1220 | | - |
---|
1221 | 1703 | ret = mutex_lock_interruptible(&rproc->lock); |
---|
1222 | 1704 | if (ret) |
---|
1223 | 1705 | return ret; |
---|
| 1706 | + |
---|
| 1707 | + /* State could have changed before we got the mutex */ |
---|
| 1708 | + if (rproc->state != RPROC_CRASHED) |
---|
| 1709 | + goto unlock_mutex; |
---|
| 1710 | + |
---|
| 1711 | + dev_err(dev, "recovering %s\n", rproc->name); |
---|
1224 | 1712 | |
---|
1225 | 1713 | ret = rproc_stop(rproc, true); |
---|
1226 | 1714 | if (ret) |
---|
1227 | 1715 | goto unlock_mutex; |
---|
1228 | 1716 | |
---|
1229 | 1717 | /* generate coredump */ |
---|
1230 | | - rproc_coredump(rproc); |
---|
| 1718 | + rproc->ops->coredump(rproc); |
---|
1231 | 1719 | |
---|
1232 | 1720 | /* load firmware */ |
---|
1233 | 1721 | ret = request_firmware(&firmware_p, rproc->firmware, dev); |
---|
.. | .. |
---|
1242 | 1730 | release_firmware(firmware_p); |
---|
1243 | 1731 | |
---|
1244 | 1732 | unlock_mutex: |
---|
| 1733 | + trace_android_vh_rproc_recovery(rproc); |
---|
1245 | 1734 | mutex_unlock(&rproc->lock); |
---|
1246 | 1735 | return ret; |
---|
1247 | 1736 | } |
---|
1248 | 1737 | |
---|
1249 | 1738 | /** |
---|
1250 | 1739 | * rproc_crash_handler_work() - handle a crash |
---|
| 1740 | + * @work: work treating the crash |
---|
1251 | 1741 | * |
---|
1252 | 1742 | * This function needs to handle everything related to a crash, like cpu |
---|
1253 | 1743 | * registers and stack dump, information to help to debug the fatal error, etc. |
---|
.. | .. |
---|
1275 | 1765 | |
---|
1276 | 1766 | if (!rproc->recovery_disabled) |
---|
1277 | 1767 | rproc_trigger_recovery(rproc); |
---|
| 1768 | + |
---|
| 1769 | + pm_relax(rproc->dev.parent); |
---|
1278 | 1770 | } |
---|
1279 | 1771 | |
---|
1280 | 1772 | /** |
---|
.. | .. |
---|
1313 | 1805 | goto unlock_mutex; |
---|
1314 | 1806 | } |
---|
1315 | 1807 | |
---|
1316 | | - /* skip the boot process if rproc is already powered up */ |
---|
| 1808 | + /* skip the boot or attach process if rproc is already powered up */ |
---|
1317 | 1809 | if (atomic_inc_return(&rproc->power) > 1) { |
---|
1318 | 1810 | ret = 0; |
---|
1319 | 1811 | goto unlock_mutex; |
---|
1320 | 1812 | } |
---|
1321 | 1813 | |
---|
1322 | | - dev_info(dev, "powering up %s\n", rproc->name); |
---|
| 1814 | + if (rproc->state == RPROC_DETACHED) { |
---|
| 1815 | + dev_info(dev, "attaching to %s\n", rproc->name); |
---|
1323 | 1816 | |
---|
1324 | | - /* load firmware */ |
---|
1325 | | - ret = request_firmware(&firmware_p, rproc->firmware, dev); |
---|
1326 | | - if (ret < 0) { |
---|
1327 | | - dev_err(dev, "request_firmware failed: %d\n", ret); |
---|
1328 | | - goto downref_rproc; |
---|
| 1817 | + ret = rproc_actuate(rproc); |
---|
| 1818 | + } else { |
---|
| 1819 | + dev_info(dev, "powering up %s\n", rproc->name); |
---|
| 1820 | + |
---|
| 1821 | + /* load firmware */ |
---|
| 1822 | + ret = request_firmware(&firmware_p, rproc->firmware, dev); |
---|
| 1823 | + if (ret < 0) { |
---|
| 1824 | + dev_err(dev, "request_firmware failed: %d\n", ret); |
---|
| 1825 | + goto downref_rproc; |
---|
| 1826 | + } |
---|
| 1827 | + |
---|
| 1828 | + ret = rproc_fw_boot(rproc, firmware_p); |
---|
| 1829 | + |
---|
| 1830 | + release_firmware(firmware_p); |
---|
1329 | 1831 | } |
---|
1330 | | - |
---|
1331 | | - ret = rproc_fw_boot(rproc, firmware_p); |
---|
1332 | | - |
---|
1333 | | - release_firmware(firmware_p); |
---|
1334 | 1832 | |
---|
1335 | 1833 | downref_rproc: |
---|
1336 | 1834 | if (ret) |
---|
.. | .. |
---|
1384 | 1882 | /* clean up all acquired resources */ |
---|
1385 | 1883 | rproc_resource_cleanup(rproc); |
---|
1386 | 1884 | |
---|
| 1885 | + /* release HW resources if needed */ |
---|
| 1886 | + rproc_unprepare_device(rproc); |
---|
| 1887 | + |
---|
1387 | 1888 | rproc_disable_iommu(rproc); |
---|
1388 | 1889 | |
---|
1389 | 1890 | /* Free the copy of the resource table */ |
---|
.. | .. |
---|
1417 | 1918 | if (!np) |
---|
1418 | 1919 | return NULL; |
---|
1419 | 1920 | |
---|
1420 | | - mutex_lock(&rproc_list_mutex); |
---|
1421 | | - list_for_each_entry(r, &rproc_list, node) { |
---|
| 1921 | + rcu_read_lock(); |
---|
| 1922 | + list_for_each_entry_rcu(r, &rproc_list, node) { |
---|
1422 | 1923 | if (r->dev.parent && r->dev.parent->of_node == np) { |
---|
1423 | 1924 | /* prevent underlying implementation from being removed */ |
---|
1424 | 1925 | if (!try_module_get(r->dev.parent->driver->owner)) { |
---|
.. | .. |
---|
1431 | 1932 | break; |
---|
1432 | 1933 | } |
---|
1433 | 1934 | } |
---|
1434 | | - mutex_unlock(&rproc_list_mutex); |
---|
| 1935 | + rcu_read_unlock(); |
---|
1435 | 1936 | |
---|
1436 | 1937 | of_node_put(np); |
---|
1437 | 1938 | |
---|
.. | .. |
---|
1444 | 1945 | } |
---|
1445 | 1946 | #endif |
---|
1446 | 1947 | EXPORT_SYMBOL(rproc_get_by_phandle); |
---|
| 1948 | + |
---|
| 1949 | +/** |
---|
| 1950 | + * rproc_set_firmware() - assign a new firmware |
---|
| 1951 | + * @rproc: rproc handle to which the new firmware is being assigned |
---|
| 1952 | + * @fw_name: new firmware name to be assigned |
---|
| 1953 | + * |
---|
| 1954 | + * This function allows remoteproc drivers or clients to configure a custom |
---|
| 1955 | + * firmware name that is different from the default name used during remoteproc |
---|
| 1956 | + * registration. The function does not trigger a remote processor boot, |
---|
| 1957 | + * only sets the firmware name used for a subsequent boot. This function |
---|
| 1958 | + * should also be called only when the remote processor is offline. |
---|
| 1959 | + * |
---|
| 1960 | + * This allows either the userspace to configure a different name through |
---|
| 1961 | + * sysfs or a kernel-level remoteproc or a remoteproc client driver to set |
---|
| 1962 | + * a specific firmware when it is controlling the boot and shutdown of the |
---|
| 1963 | + * remote processor. |
---|
| 1964 | + * |
---|
| 1965 | + * Return: 0 on success or a negative value upon failure |
---|
| 1966 | + */ |
---|
| 1967 | +int rproc_set_firmware(struct rproc *rproc, const char *fw_name) |
---|
| 1968 | +{ |
---|
| 1969 | + struct device *dev; |
---|
| 1970 | + int ret, len; |
---|
| 1971 | + char *p; |
---|
| 1972 | + |
---|
| 1973 | + if (!rproc || !fw_name) |
---|
| 1974 | + return -EINVAL; |
---|
| 1975 | + |
---|
| 1976 | + dev = rproc->dev.parent; |
---|
| 1977 | + |
---|
| 1978 | + ret = mutex_lock_interruptible(&rproc->lock); |
---|
| 1979 | + if (ret) { |
---|
| 1980 | + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); |
---|
| 1981 | + return -EINVAL; |
---|
| 1982 | + } |
---|
| 1983 | + |
---|
| 1984 | + if (rproc->state != RPROC_OFFLINE) { |
---|
| 1985 | + dev_err(dev, "can't change firmware while running\n"); |
---|
| 1986 | + ret = -EBUSY; |
---|
| 1987 | + goto out; |
---|
| 1988 | + } |
---|
| 1989 | + |
---|
| 1990 | + len = strcspn(fw_name, "\n"); |
---|
| 1991 | + if (!len) { |
---|
| 1992 | + dev_err(dev, "can't provide empty string for firmware name\n"); |
---|
| 1993 | + ret = -EINVAL; |
---|
| 1994 | + goto out; |
---|
| 1995 | + } |
---|
| 1996 | + |
---|
| 1997 | + p = kstrndup(fw_name, len, GFP_KERNEL); |
---|
| 1998 | + if (!p) { |
---|
| 1999 | + ret = -ENOMEM; |
---|
| 2000 | + goto out; |
---|
| 2001 | + } |
---|
| 2002 | + |
---|
| 2003 | + kfree_const(rproc->firmware); |
---|
| 2004 | + rproc->firmware = p; |
---|
| 2005 | + |
---|
| 2006 | +out: |
---|
| 2007 | + mutex_unlock(&rproc->lock); |
---|
| 2008 | + return ret; |
---|
| 2009 | +} |
---|
| 2010 | +EXPORT_SYMBOL(rproc_set_firmware); |
---|
| 2011 | + |
---|
| 2012 | +static int rproc_validate(struct rproc *rproc) |
---|
| 2013 | +{ |
---|
| 2014 | + switch (rproc->state) { |
---|
| 2015 | + case RPROC_OFFLINE: |
---|
| 2016 | + /* |
---|
| 2017 | + * An offline processor without a start() |
---|
| 2018 | + * function makes no sense. |
---|
| 2019 | + */ |
---|
| 2020 | + if (!rproc->ops->start) |
---|
| 2021 | + return -EINVAL; |
---|
| 2022 | + break; |
---|
| 2023 | + case RPROC_DETACHED: |
---|
| 2024 | + /* |
---|
| 2025 | + * A remote processor in a detached state without an |
---|
| 2026 | + * attach() function makes not sense. |
---|
| 2027 | + */ |
---|
| 2028 | + if (!rproc->ops->attach) |
---|
| 2029 | + return -EINVAL; |
---|
| 2030 | + /* |
---|
| 2031 | + * When attaching to a remote processor the device memory |
---|
| 2032 | + * is already available and as such there is no need to have a |
---|
| 2033 | + * cached table. |
---|
| 2034 | + */ |
---|
| 2035 | + if (rproc->cached_table) |
---|
| 2036 | + return -EINVAL; |
---|
| 2037 | + break; |
---|
| 2038 | + default: |
---|
| 2039 | + /* |
---|
| 2040 | + * When adding a remote processor, the state of the device |
---|
| 2041 | + * can be offline or detached, nothing else. |
---|
| 2042 | + */ |
---|
| 2043 | + return -EINVAL; |
---|
| 2044 | + } |
---|
| 2045 | + |
---|
| 2046 | + return 0; |
---|
| 2047 | +} |
---|
1447 | 2048 | |
---|
1448 | 2049 | /** |
---|
1449 | 2050 | * rproc_add() - register a remote processor |
---|
.. | .. |
---|
1470 | 2071 | struct device *dev = &rproc->dev; |
---|
1471 | 2072 | int ret; |
---|
1472 | 2073 | |
---|
| 2074 | + /* add char device for this remoteproc */ |
---|
| 2075 | + ret = rproc_char_device_add(rproc); |
---|
| 2076 | + if (ret < 0) |
---|
| 2077 | + return ret; |
---|
| 2078 | + |
---|
1473 | 2079 | ret = device_add(dev); |
---|
| 2080 | + if (ret < 0) |
---|
| 2081 | + return ret; |
---|
| 2082 | + |
---|
| 2083 | + ret = rproc_validate(rproc); |
---|
1474 | 2084 | if (ret < 0) |
---|
1475 | 2085 | return ret; |
---|
1476 | 2086 | |
---|
.. | .. |
---|
1478 | 2088 | |
---|
1479 | 2089 | /* create debugfs entries */ |
---|
1480 | 2090 | rproc_create_debug_dir(rproc); |
---|
| 2091 | + |
---|
| 2092 | + /* |
---|
| 2093 | + * Remind ourselves the remote processor has been attached to rather |
---|
| 2094 | + * than booted by the remoteproc core. This is important because the |
---|
| 2095 | + * RPROC_DETACHED state will be lost as soon as the remote processor |
---|
| 2096 | + * has been attached to. Used in firmware_show() and reset in |
---|
| 2097 | + * rproc_stop(). |
---|
| 2098 | + */ |
---|
| 2099 | + if (rproc->state == RPROC_DETACHED) |
---|
| 2100 | + rproc->autonomous = true; |
---|
1481 | 2101 | |
---|
1482 | 2102 | /* if rproc is marked always-on, request it to boot */ |
---|
1483 | 2103 | if (rproc->auto_boot) { |
---|
.. | .. |
---|
1488 | 2108 | |
---|
1489 | 2109 | /* expose to rproc_get_by_phandle users */ |
---|
1490 | 2110 | mutex_lock(&rproc_list_mutex); |
---|
1491 | | - list_add(&rproc->node, &rproc_list); |
---|
| 2111 | + list_add_rcu(&rproc->node, &rproc_list); |
---|
1492 | 2112 | mutex_unlock(&rproc_list_mutex); |
---|
1493 | 2113 | |
---|
1494 | 2114 | return 0; |
---|
1495 | 2115 | } |
---|
1496 | 2116 | EXPORT_SYMBOL(rproc_add); |
---|
| 2117 | + |
---|
| 2118 | +static void devm_rproc_remove(void *rproc) |
---|
| 2119 | +{ |
---|
| 2120 | + rproc_del(rproc); |
---|
| 2121 | +} |
---|
| 2122 | + |
---|
| 2123 | +/** |
---|
| 2124 | + * devm_rproc_add() - resource managed rproc_add() |
---|
| 2125 | + * @dev: the underlying device |
---|
| 2126 | + * @rproc: the remote processor handle to register |
---|
| 2127 | + * |
---|
| 2128 | + * This function performs like rproc_add() but the registered rproc device will |
---|
| 2129 | + * automatically be removed on driver detach. |
---|
| 2130 | + * |
---|
| 2131 | + * Returns: 0 on success, negative errno on failure |
---|
| 2132 | + */ |
---|
| 2133 | +int devm_rproc_add(struct device *dev, struct rproc *rproc) |
---|
| 2134 | +{ |
---|
| 2135 | + int err; |
---|
| 2136 | + |
---|
| 2137 | + err = rproc_add(rproc); |
---|
| 2138 | + if (err) |
---|
| 2139 | + return err; |
---|
| 2140 | + |
---|
| 2141 | + return devm_add_action_or_reset(dev, devm_rproc_remove, rproc); |
---|
| 2142 | +} |
---|
| 2143 | +EXPORT_SYMBOL(devm_rproc_add); |
---|
1497 | 2144 | |
---|
1498 | 2145 | /** |
---|
1499 | 2146 | * rproc_type_release() - release a remote processor instance |
---|
.. | .. |
---|
1515 | 2162 | if (rproc->index >= 0) |
---|
1516 | 2163 | ida_simple_remove(&rproc_dev_index, rproc->index); |
---|
1517 | 2164 | |
---|
1518 | | - kfree(rproc->firmware); |
---|
| 2165 | + kfree_const(rproc->firmware); |
---|
| 2166 | + kfree_const(rproc->name); |
---|
1519 | 2167 | kfree(rproc->ops); |
---|
1520 | 2168 | kfree(rproc); |
---|
1521 | 2169 | } |
---|
.. | .. |
---|
1524 | 2172 | .name = "remoteproc", |
---|
1525 | 2173 | .release = rproc_type_release, |
---|
1526 | 2174 | }; |
---|
| 2175 | + |
---|
| 2176 | +static int rproc_alloc_firmware(struct rproc *rproc, |
---|
| 2177 | + const char *name, const char *firmware) |
---|
| 2178 | +{ |
---|
| 2179 | + const char *p; |
---|
| 2180 | + |
---|
| 2181 | + /* |
---|
| 2182 | + * Allocate a firmware name if the caller gave us one to work |
---|
| 2183 | + * with. Otherwise construct a new one using a default pattern. |
---|
| 2184 | + */ |
---|
| 2185 | + if (firmware) |
---|
| 2186 | + p = kstrdup_const(firmware, GFP_KERNEL); |
---|
| 2187 | + else |
---|
| 2188 | + p = kasprintf(GFP_KERNEL, "rproc-%s-fw", name); |
---|
| 2189 | + |
---|
| 2190 | + if (!p) |
---|
| 2191 | + return -ENOMEM; |
---|
| 2192 | + |
---|
| 2193 | + rproc->firmware = p; |
---|
| 2194 | + |
---|
| 2195 | + return 0; |
---|
| 2196 | +} |
---|
| 2197 | + |
---|
| 2198 | +static int rproc_alloc_ops(struct rproc *rproc, const struct rproc_ops *ops) |
---|
| 2199 | +{ |
---|
| 2200 | + rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL); |
---|
| 2201 | + if (!rproc->ops) |
---|
| 2202 | + return -ENOMEM; |
---|
| 2203 | + |
---|
| 2204 | + /* Default to rproc_coredump if no coredump function is specified */ |
---|
| 2205 | + if (!rproc->ops->coredump) |
---|
| 2206 | + rproc->ops->coredump = rproc_coredump; |
---|
| 2207 | + |
---|
| 2208 | + if (rproc->ops->load) |
---|
| 2209 | + return 0; |
---|
| 2210 | + |
---|
| 2211 | + /* Default to ELF loader if no load function is specified */ |
---|
| 2212 | + rproc->ops->load = rproc_elf_load_segments; |
---|
| 2213 | + rproc->ops->parse_fw = rproc_elf_load_rsc_table; |
---|
| 2214 | + rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; |
---|
| 2215 | + rproc->ops->sanity_check = rproc_elf_sanity_check; |
---|
| 2216 | + rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; |
---|
| 2217 | + |
---|
| 2218 | + return 0; |
---|
| 2219 | +} |
---|
1527 | 2220 | |
---|
1528 | 2221 | /** |
---|
1529 | 2222 | * rproc_alloc() - allocate a remote processor handle |
---|
.. | .. |
---|
1553 | 2246 | const char *firmware, int len) |
---|
1554 | 2247 | { |
---|
1555 | 2248 | struct rproc *rproc; |
---|
1556 | | - char *p, *template = "rproc-%s-fw"; |
---|
1557 | | - int name_len; |
---|
1558 | 2249 | |
---|
1559 | 2250 | if (!dev || !name || !ops) |
---|
1560 | 2251 | return NULL; |
---|
1561 | 2252 | |
---|
1562 | | - if (!firmware) { |
---|
1563 | | - /* |
---|
1564 | | - * If the caller didn't pass in a firmware name then |
---|
1565 | | - * construct a default name. |
---|
1566 | | - */ |
---|
1567 | | - name_len = strlen(name) + strlen(template) - 2 + 1; |
---|
1568 | | - p = kmalloc(name_len, GFP_KERNEL); |
---|
1569 | | - if (!p) |
---|
1570 | | - return NULL; |
---|
1571 | | - snprintf(p, name_len, template, name); |
---|
1572 | | - } else { |
---|
1573 | | - p = kstrdup(firmware, GFP_KERNEL); |
---|
1574 | | - if (!p) |
---|
1575 | | - return NULL; |
---|
1576 | | - } |
---|
1577 | | - |
---|
1578 | 2253 | rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL); |
---|
1579 | | - if (!rproc) { |
---|
1580 | | - kfree(p); |
---|
| 2254 | + if (!rproc) |
---|
1581 | 2255 | return NULL; |
---|
1582 | | - } |
---|
1583 | 2256 | |
---|
1584 | | - rproc->ops = kmemdup(ops, sizeof(*ops), GFP_KERNEL); |
---|
1585 | | - if (!rproc->ops) { |
---|
1586 | | - kfree(p); |
---|
1587 | | - kfree(rproc); |
---|
1588 | | - return NULL; |
---|
1589 | | - } |
---|
1590 | | - |
---|
1591 | | - rproc->firmware = p; |
---|
1592 | | - rproc->name = name; |
---|
1593 | 2257 | rproc->priv = &rproc[1]; |
---|
1594 | 2258 | rproc->auto_boot = true; |
---|
| 2259 | + rproc->elf_class = ELFCLASSNONE; |
---|
| 2260 | + rproc->elf_machine = EM_NONE; |
---|
1595 | 2261 | |
---|
1596 | 2262 | device_initialize(&rproc->dev); |
---|
1597 | 2263 | rproc->dev.parent = dev; |
---|
.. | .. |
---|
1600 | 2266 | rproc->dev.driver_data = rproc; |
---|
1601 | 2267 | idr_init(&rproc->notifyids); |
---|
1602 | 2268 | |
---|
| 2269 | + rproc->name = kstrdup_const(name, GFP_KERNEL); |
---|
| 2270 | + if (!rproc->name) |
---|
| 2271 | + goto put_device; |
---|
| 2272 | + |
---|
| 2273 | + if (rproc_alloc_firmware(rproc, name, firmware)) |
---|
| 2274 | + goto put_device; |
---|
| 2275 | + |
---|
| 2276 | + if (rproc_alloc_ops(rproc, ops)) |
---|
| 2277 | + goto put_device; |
---|
| 2278 | + |
---|
1603 | 2279 | /* Assign a unique device index and name */ |
---|
1604 | 2280 | rproc->index = ida_simple_get(&rproc_dev_index, 0, 0, GFP_KERNEL); |
---|
1605 | 2281 | if (rproc->index < 0) { |
---|
1606 | 2282 | dev_err(dev, "ida_simple_get failed: %d\n", rproc->index); |
---|
1607 | | - put_device(&rproc->dev); |
---|
1608 | | - return NULL; |
---|
| 2283 | + goto put_device; |
---|
1609 | 2284 | } |
---|
1610 | 2285 | |
---|
1611 | 2286 | dev_set_name(&rproc->dev, "remoteproc%d", rproc->index); |
---|
1612 | 2287 | |
---|
1613 | 2288 | atomic_set(&rproc->power, 0); |
---|
1614 | | - |
---|
1615 | | - /* Default to ELF loader if no load function is specified */ |
---|
1616 | | - if (!rproc->ops->load) { |
---|
1617 | | - rproc->ops->load = rproc_elf_load_segments; |
---|
1618 | | - rproc->ops->parse_fw = rproc_elf_load_rsc_table; |
---|
1619 | | - rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; |
---|
1620 | | - rproc->ops->sanity_check = rproc_elf_sanity_check; |
---|
1621 | | - rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; |
---|
1622 | | - } |
---|
1623 | 2289 | |
---|
1624 | 2290 | mutex_init(&rproc->lock); |
---|
1625 | 2291 | |
---|
.. | .. |
---|
1635 | 2301 | rproc->state = RPROC_OFFLINE; |
---|
1636 | 2302 | |
---|
1637 | 2303 | return rproc; |
---|
| 2304 | + |
---|
| 2305 | +put_device: |
---|
| 2306 | + put_device(&rproc->dev); |
---|
| 2307 | + return NULL; |
---|
1638 | 2308 | } |
---|
1639 | 2309 | EXPORT_SYMBOL(rproc_alloc); |
---|
1640 | 2310 | |
---|
.. | .. |
---|
1702 | 2372 | |
---|
1703 | 2373 | /* the rproc is downref'ed as soon as it's removed from the klist */ |
---|
1704 | 2374 | mutex_lock(&rproc_list_mutex); |
---|
1705 | | - list_del(&rproc->node); |
---|
| 2375 | + list_del_rcu(&rproc->node); |
---|
1706 | 2376 | mutex_unlock(&rproc_list_mutex); |
---|
1707 | 2377 | |
---|
| 2378 | + /* Ensure that no readers of rproc_list are still active */ |
---|
| 2379 | + synchronize_rcu(); |
---|
| 2380 | + |
---|
1708 | 2381 | device_del(&rproc->dev); |
---|
| 2382 | + rproc_char_device_remove(rproc); |
---|
1709 | 2383 | |
---|
1710 | 2384 | return 0; |
---|
1711 | 2385 | } |
---|
1712 | 2386 | EXPORT_SYMBOL(rproc_del); |
---|
| 2387 | + |
---|
| 2388 | +static void devm_rproc_free(struct device *dev, void *res) |
---|
| 2389 | +{ |
---|
| 2390 | + rproc_free(*(struct rproc **)res); |
---|
| 2391 | +} |
---|
| 2392 | + |
---|
| 2393 | +/** |
---|
| 2394 | + * devm_rproc_alloc() - resource managed rproc_alloc() |
---|
| 2395 | + * @dev: the underlying device |
---|
| 2396 | + * @name: name of this remote processor |
---|
| 2397 | + * @ops: platform-specific handlers (mainly start/stop) |
---|
| 2398 | + * @firmware: name of firmware file to load, can be NULL |
---|
| 2399 | + * @len: length of private data needed by the rproc driver (in bytes) |
---|
| 2400 | + * |
---|
| 2401 | + * This function performs like rproc_alloc() but the acquired rproc device will |
---|
| 2402 | + * automatically be released on driver detach. |
---|
| 2403 | + * |
---|
| 2404 | + * Returns: new rproc instance, or NULL on failure |
---|
| 2405 | + */ |
---|
| 2406 | +struct rproc *devm_rproc_alloc(struct device *dev, const char *name, |
---|
| 2407 | + const struct rproc_ops *ops, |
---|
| 2408 | + const char *firmware, int len) |
---|
| 2409 | +{ |
---|
| 2410 | + struct rproc **ptr, *rproc; |
---|
| 2411 | + |
---|
| 2412 | + ptr = devres_alloc(devm_rproc_free, sizeof(*ptr), GFP_KERNEL); |
---|
| 2413 | + if (!ptr) |
---|
| 2414 | + return NULL; |
---|
| 2415 | + |
---|
| 2416 | + rproc = rproc_alloc(dev, name, ops, firmware, len); |
---|
| 2417 | + if (rproc) { |
---|
| 2418 | + *ptr = rproc; |
---|
| 2419 | + devres_add(dev, ptr); |
---|
| 2420 | + } else { |
---|
| 2421 | + devres_free(ptr); |
---|
| 2422 | + } |
---|
| 2423 | + |
---|
| 2424 | + return rproc; |
---|
| 2425 | +} |
---|
| 2426 | +EXPORT_SYMBOL(devm_rproc_alloc); |
---|
1713 | 2427 | |
---|
1714 | 2428 | /** |
---|
1715 | 2429 | * rproc_add_subdev() - add a subdevice to a remoteproc |
---|
.. | .. |
---|
1770 | 2484 | return; |
---|
1771 | 2485 | } |
---|
1772 | 2486 | |
---|
| 2487 | + /* Prevent suspend while the remoteproc is being recovered */ |
---|
| 2488 | + pm_stay_awake(rproc->dev.parent); |
---|
| 2489 | + |
---|
1773 | 2490 | dev_err(&rproc->dev, "crash detected in %s: type %s\n", |
---|
1774 | 2491 | rproc->name, rproc_crash_to_string(type)); |
---|
1775 | 2492 | |
---|
1776 | | - /* create a new task to handle the error */ |
---|
1777 | | - schedule_work(&rproc->crash_handler); |
---|
| 2493 | + if (rproc_recovery_wq) |
---|
| 2494 | + queue_work(rproc_recovery_wq, &rproc->crash_handler); |
---|
| 2495 | + else |
---|
| 2496 | + /* Have a worker handle the error; ensure system is not suspended */ |
---|
| 2497 | + queue_work(system_freezable_wq, &rproc->crash_handler); |
---|
1778 | 2498 | } |
---|
1779 | 2499 | EXPORT_SYMBOL(rproc_report_crash); |
---|
1780 | 2500 | |
---|
| 2501 | +static int rproc_panic_handler(struct notifier_block *nb, unsigned long event, |
---|
| 2502 | + void *ptr) |
---|
| 2503 | +{ |
---|
| 2504 | + unsigned int longest = 0; |
---|
| 2505 | + struct rproc *rproc; |
---|
| 2506 | + unsigned int d; |
---|
| 2507 | + |
---|
| 2508 | + rcu_read_lock(); |
---|
| 2509 | + list_for_each_entry_rcu(rproc, &rproc_list, node) { |
---|
| 2510 | + if (!rproc->ops->panic || rproc->state != RPROC_RUNNING) |
---|
| 2511 | + continue; |
---|
| 2512 | + |
---|
| 2513 | + d = rproc->ops->panic(rproc); |
---|
| 2514 | + longest = max(longest, d); |
---|
| 2515 | + } |
---|
| 2516 | + rcu_read_unlock(); |
---|
| 2517 | + |
---|
| 2518 | + /* |
---|
| 2519 | + * Delay for the longest requested duration before returning. This can |
---|
| 2520 | + * be used by the remoteproc drivers to give the remote processor time |
---|
| 2521 | + * to perform any requested operations (such as flush caches), when |
---|
| 2522 | + * it's not possible to signal the Linux side due to the panic. |
---|
| 2523 | + */ |
---|
| 2524 | + mdelay(longest); |
---|
| 2525 | + |
---|
| 2526 | + return NOTIFY_DONE; |
---|
| 2527 | +} |
---|
| 2528 | + |
---|
| 2529 | +static void __init rproc_init_panic(void) |
---|
| 2530 | +{ |
---|
| 2531 | + rproc_panic_nb.notifier_call = rproc_panic_handler; |
---|
| 2532 | + atomic_notifier_chain_register(&panic_notifier_list, &rproc_panic_nb); |
---|
| 2533 | +} |
---|
| 2534 | + |
---|
| 2535 | +static void __exit rproc_exit_panic(void) |
---|
| 2536 | +{ |
---|
| 2537 | + atomic_notifier_chain_unregister(&panic_notifier_list, &rproc_panic_nb); |
---|
| 2538 | +} |
---|
| 2539 | + |
---|
1781 | 2540 | static int __init remoteproc_init(void) |
---|
1782 | 2541 | { |
---|
| 2542 | + rproc_recovery_wq = alloc_workqueue("rproc_recovery_wq", |
---|
| 2543 | + WQ_UNBOUND | WQ_FREEZABLE, 0); |
---|
| 2544 | + if (!rproc_recovery_wq) |
---|
| 2545 | + pr_err("remoteproc: creation of rproc_recovery_wq failed\n"); |
---|
| 2546 | + |
---|
1783 | 2547 | rproc_init_sysfs(); |
---|
1784 | 2548 | rproc_init_debugfs(); |
---|
| 2549 | + rproc_init_cdev(); |
---|
| 2550 | + rproc_init_panic(); |
---|
1785 | 2551 | |
---|
1786 | 2552 | return 0; |
---|
1787 | 2553 | } |
---|
.. | .. |
---|
1791 | 2557 | { |
---|
1792 | 2558 | ida_destroy(&rproc_dev_index); |
---|
1793 | 2559 | |
---|
| 2560 | + rproc_exit_panic(); |
---|
1794 | 2561 | rproc_exit_debugfs(); |
---|
1795 | 2562 | rproc_exit_sysfs(); |
---|
| 2563 | + if (rproc_recovery_wq) |
---|
| 2564 | + destroy_workqueue(rproc_recovery_wq); |
---|
1796 | 2565 | } |
---|
1797 | 2566 | module_exit(remoteproc_exit); |
---|
1798 | 2567 | |
---|