/* * Copyright (c) 2014, Mentor Graphics Corporation * All rights reserved. * Copyright (c) 2015 Xilinx, Inc. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include /****************************************************************************** * static functions *****************************************************************************/ static struct loader_ops * remoteproc_check_fw_format(const void *img_data, size_t img_len) { if (img_len <= 0) return NULL; else if (elf_identify(img_data, img_len) == 0) return &elf_ops; else return NULL; } static struct remoteproc_mem * remoteproc_get_mem(struct remoteproc *rproc, const char *name, metal_phys_addr_t pa, metal_phys_addr_t da, void *va, size_t size) { struct metal_list *node; struct remoteproc_mem *mem; metal_list_for_each(&rproc->mems, node) { mem = metal_container_of(node, struct remoteproc_mem, node); if (name) { if (!strncmp(name, mem->name, sizeof(mem->name))) return mem; } else if (pa != METAL_BAD_PHYS) { metal_phys_addr_t pa_start, pa_end; pa_start = mem->pa; pa_end = pa_start + mem->size; if (pa >= pa_start && (pa + size) <= pa_end) return mem; } else if (da != METAL_BAD_PHYS) { metal_phys_addr_t da_start, da_end; da_start = mem->da; da_end = da_start + mem->size; if (da >= da_start && (da + size) <= da_end) return mem; } else if (va) { if (metal_io_virt_to_offset(mem->io, va) != METAL_BAD_OFFSET) return mem; } else { return NULL; } } return NULL; } static metal_phys_addr_t remoteproc_datopa(struct remoteproc_mem *mem, metal_phys_addr_t da) { metal_phys_addr_t pa; pa = mem->pa + da - mem->da; return pa; } static metal_phys_addr_t remoteproc_patoda(struct remoteproc_mem *mem, metal_phys_addr_t pa) { metal_phys_addr_t da; da = mem->da + pa - mem->pa; return da; } static void *remoteproc_get_rsc_table(struct remoteproc *rproc, void *store, struct image_store_ops *store_ops, size_t offset, size_t len) { int ret; void *rsc_table = NULL; const void *img_data; /* Copy the resource table to local memory, * the caller should be responsible to release the memory */ rsc_table = metal_allocate_memory(len); if (!rsc_table) { return RPROC_ERR_PTR(-RPROC_ENOMEM); } ret = store_ops->load(store, offset, len, &img_data, RPROC_LOAD_ANYADDR, NULL, 1); if (ret < 0 || ret < (int)len || img_data == NULL) { metal_log(METAL_LOG_ERROR, "get rsc failed: 0x%llx, 0x%llx\r\n", offset, len); rsc_table = RPROC_ERR_PTR(-RPROC_EINVAL); goto error; } memcpy(rsc_table, img_data, len); ret = handle_rsc_table(rproc, rsc_table, len, NULL); if (ret < 0) { rsc_table = RPROC_ERR_PTR(ret); goto error; } return rsc_table; error: metal_free_memory(rsc_table); return rsc_table; } int remoteproc_parse_rsc_table(struct remoteproc *rproc, struct resource_table *rsc_table, size_t rsc_size) { struct metal_io_region *io; io = remoteproc_get_io_with_va(rproc, (void *)rsc_table); return handle_rsc_table(rproc, rsc_table, rsc_size, io); } int remoteproc_set_rsc_table(struct remoteproc *rproc, struct resource_table *rsc_table, size_t rsc_size) { int ret; struct metal_io_region *io; io = remoteproc_get_io_with_va(rproc, (void *)rsc_table); if (!io) return -EINVAL; ret = remoteproc_parse_rsc_table(rproc, rsc_table, rsc_size); if (!ret) { rproc->rsc_table = rsc_table; rproc->rsc_len = rsc_size; rproc->rsc_io = io; } return ret; } struct remoteproc *remoteproc_init(struct remoteproc *rproc, struct remoteproc_ops *ops, void *priv) { if (rproc) { memset(rproc, 0, sizeof (*rproc)); rproc->state = RPROC_OFFLINE; metal_mutex_init(&rproc->lock); metal_list_init(&rproc->mems); metal_list_init(&rproc->vdevs); } rproc = ops->init(rproc, ops, priv); return rproc; } int remoteproc_remove(struct remoteproc *rproc) { int ret; if (rproc) { metal_mutex_acquire(&rproc->lock); if (rproc->state == RPROC_OFFLINE) rproc->ops->remove(rproc); else ret = -EBUSY; metal_mutex_release(&rproc->lock); } else { ret = -EINVAL; } return ret; } int remoteproc_config(struct remoteproc *rproc, void *data) { int ret = -RPROC_ENODEV; if (rproc) { metal_mutex_acquire(&rproc->lock); if (rproc->state == RPROC_OFFLINE) { /* configure operation is allowed if the state is * offline or ready. This function can be called * mulitple times before start the remote. */ if (rproc->ops->config) ret = rproc->ops->config(rproc, data); rproc->state = RPROC_READY; } else { ret = -RPROC_EINVAL; } metal_mutex_release(&rproc->lock); } return ret; } int remoteproc_start(struct remoteproc *rproc) { int ret = -RPROC_ENODEV; if (rproc) { metal_mutex_acquire(&rproc->lock); if (rproc->state == RPROC_READY) { ret = rproc->ops->start(rproc); rproc->state = RPROC_RUNNING; } else { ret = -RPROC_EINVAL; } metal_mutex_release(&rproc->lock); } return ret; } int remoteproc_stop(struct remoteproc *rproc) { int ret = -RPROC_ENODEV; if (rproc) { metal_mutex_acquire(&rproc->lock); if (rproc->state != RPROC_STOPPED && rproc->state != RPROC_OFFLINE) { if (rproc->ops->stop) ret = rproc->ops->stop(rproc); rproc->state = RPROC_STOPPED; } else { ret = 0; } metal_mutex_release(&rproc->lock); } return ret; } int remoteproc_shutdown(struct remoteproc *rproc) { int ret = -RPROC_ENODEV; if (rproc) { ret = 0; metal_mutex_acquire(&rproc->lock); if (rproc->state != RPROC_OFFLINE) { if (rproc->state != RPROC_STOPPED) { if (rproc->ops->stop) ret = rproc->ops->stop(rproc); } if (!ret) { if (rproc->ops->shutdown) ret = rproc->ops->shutdown(rproc); if (!ret) { rproc->state = RPROC_OFFLINE; } } } metal_mutex_release(&rproc->lock); } return ret; } struct metal_io_region * remoteproc_get_io_with_name(struct remoteproc *rproc, const char *name) { struct remoteproc_mem *mem; mem = remoteproc_get_mem(rproc, name, METAL_BAD_PHYS, METAL_BAD_PHYS, NULL, 0); if (mem) return mem->io; else return NULL; } struct metal_io_region * remoteproc_get_io_with_pa(struct remoteproc *rproc, metal_phys_addr_t pa) { struct remoteproc_mem *mem; mem = remoteproc_get_mem(rproc, NULL, pa, METAL_BAD_PHYS, NULL, 0); if (mem) return mem->io; else return NULL; } struct metal_io_region * remoteproc_get_io_with_da(struct remoteproc *rproc, metal_phys_addr_t da, unsigned long *offset) { struct remoteproc_mem *mem; mem = remoteproc_get_mem(rproc, NULL, METAL_BAD_PHYS, da, NULL, 0); if (mem) { struct metal_io_region *io; metal_phys_addr_t pa; io = mem->io; pa = remoteproc_datopa(mem, da); *offset = metal_io_phys_to_offset(io, pa); return io; } else { return NULL; } } struct metal_io_region * remoteproc_get_io_with_va(struct remoteproc *rproc, void *va) { struct remoteproc_mem *mem; mem = remoteproc_get_mem(rproc, NULL, METAL_BAD_PHYS, METAL_BAD_PHYS, va, 0); if (mem) return mem->io; else return NULL; } void *remoteproc_mmap(struct remoteproc *rproc, metal_phys_addr_t *pa, metal_phys_addr_t *da, size_t size, unsigned int attribute, struct metal_io_region **io) { void *va = NULL; metal_phys_addr_t lpa, lda; struct remoteproc_mem *mem; if (!rproc) return NULL; else if (!pa && !da) return NULL; if (pa) lpa = *pa; else lpa = METAL_BAD_PHYS; if (da) lda = *da; else lda = METAL_BAD_PHYS; mem = remoteproc_get_mem(rproc, NULL, lpa, lda, NULL, size); if (mem) { if (lpa != METAL_BAD_PHYS) lda = remoteproc_patoda(mem, lpa); else if (lda != METAL_BAD_PHYS) lpa = remoteproc_datopa(mem, lda); if (io) *io = mem->io; va = metal_io_phys_to_virt(mem->io, lpa); } else if (rproc->ops->mmap) { va = rproc->ops->mmap(rproc, &lpa, &lda, size, attribute, io); } if (pa) *pa = lpa; if (da) *da = lda; return va; } int remoteproc_load(struct remoteproc *rproc, const char *path, void *store, struct image_store_ops *store_ops, void **img_info) { int ret; struct loader_ops *loader; const void *img_data; void *limg_info = NULL; size_t offset, noffset; size_t len, nlen; int last_load_state; metal_phys_addr_t da, rsc_da; int rsc_len; size_t rsc_size; void *rsc_table = NULL; struct metal_io_region *io = NULL; if (!rproc) return -RPROC_ENODEV; metal_mutex_acquire(&rproc->lock); metal_log(METAL_LOG_DEBUG, "%s: check remoteproc status\r\n", __func__); /* If remoteproc is not in ready state, cannot load executable */ if (rproc->state != RPROC_READY && rproc->state != RPROC_CONFIGURED) { metal_log(METAL_LOG_ERROR, "load failure: invalid rproc state %d.\r\n", rproc->state); metal_mutex_release(&rproc->lock); return -RPROC_EINVAL; } if (!store_ops) { metal_log(METAL_LOG_ERROR, "load failure: loader ops is not set.\r\n"); metal_mutex_release(&rproc->lock); return -RPROC_EINVAL; } /* Open exectuable to get ready to parse */ metal_log(METAL_LOG_DEBUG, "%s: open exectuable image\r\n", __func__); ret = store_ops->open(store, path, &img_data); if (ret <= 0) { metal_log(METAL_LOG_ERROR, "load failure: failed to open firmware %d.\n", ret); metal_mutex_release(&rproc->lock); return -RPROC_EINVAL; } len = ret; metal_assert(img_data != NULL); /* Check executable format to select a parser */ loader = rproc->loader; if (!loader) { metal_log(METAL_LOG_DEBUG, "%s: check loader\r\n", __func__); loader = remoteproc_check_fw_format(img_data, len); if (!loader) { metal_log(METAL_LOG_ERROR, "load failure: failed to get store ops.\n"); ret = -RPROC_EINVAL; goto error1; } rproc->loader = loader; } /* Load exectuable headers */ metal_log(METAL_LOG_DEBUG, "%s: loading headers\r\n", __func__); offset = 0; last_load_state = RPROC_LOADER_NOT_READY; while(1) { ret = loader->load_header(img_data, offset, len, &limg_info, last_load_state, &noffset, &nlen); last_load_state = (unsigned int)ret; metal_log(METAL_LOG_DEBUG, "%s, load header 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n", __func__, offset, len, noffset, nlen); if (ret < 0) { metal_log(METAL_LOG_ERROR, "load header failed 0x%lx,%d.\r\n", offset, len); goto error2; } else if ((ret & RPROC_LOADER_READY_TO_LOAD) != 0) { if (nlen == 0) break; else if ((noffset > (offset + len)) && (store_ops->features & SUPPORT_SEEK) == 0) { /* Required data is not continued, however * seek is not supported, stop to load * headers such as ELF section headers which * is usually located to the end of image. * Continue to load binary data to target * memory. */ break; } } /* Continue to load headers image data */ img_data = NULL; ret = store_ops->load(store, noffset, nlen, &img_data, RPROC_LOAD_ANYADDR, NULL, 1); if (ret < (int)nlen) { metal_log(METAL_LOG_ERROR, "load image data failed 0x%x,%d\r\n", noffset, nlen); goto error2; } offset = noffset; len = nlen; } ret = elf_locate_rsc_table(limg_info, &rsc_da, &offset, &rsc_size); if (ret == 0 && rsc_size > 0) { /* parse resource table */ rsc_len = (int)rsc_size; rsc_table = remoteproc_get_rsc_table(rproc, store, store_ops, offset, rsc_len); } else { rsc_len = ret; } /* load executable data */ metal_log(METAL_LOG_DEBUG, "%s: load executable data\r\n", __func__); offset = 0; len = 0; ret = -EINVAL; while(1) { unsigned char padding; size_t nmemsize; metal_phys_addr_t pa; da = RPROC_LOAD_ANYADDR; nlen = 0; nmemsize = 0; noffset = 0; ret = loader->load_data(rproc, img_data, offset, len, &limg_info, last_load_state, &da, &noffset, &nlen, &padding, &nmemsize); if (ret < 0) { metal_log(METAL_LOG_ERROR, "load data failed,0x%lx,%d\r\n", noffset, nlen); goto error3; } metal_log(METAL_LOG_DEBUG, "load data: da 0x%lx, offset 0x%lx, len = 0x%lx, memsize = 0x%lx, state 0x%x\r\n", da, noffset, nlen, nmemsize, ret); last_load_state = ret; if (da != RPROC_LOAD_ANYADDR) { /* Data is supposed to be loaded to target memory */ img_data = NULL; /* get the I/O region from remoteproc */ pa = METAL_BAD_PHYS; (void)remoteproc_mmap(rproc, &pa, &da, nmemsize, 0, &io); if (pa == METAL_BAD_PHYS || io == NULL) { metal_log(METAL_LOG_ERROR, "load failed, no mapping for 0x%llx.\r\n", da); ret = -RPROC_EINVAL; goto error3; } if (nlen > 0) { ret = store_ops->load(store, noffset, nlen, &img_data, pa, io, 1); if (ret != (int)nlen) { metal_log(METAL_LOG_ERROR, "load data failed 0x%lx, 0x%lx, 0x%x\r\n", pa, noffset, nlen); ret = -RPROC_EINVAL; goto error3; } } if (nmemsize > nlen) { size_t tmpoffset; tmpoffset = metal_io_phys_to_offset(io, pa + nlen); metal_io_block_set(io, tmpoffset, padding, (nmemsize - nlen)); } } else if (nlen != 0) { ret = store_ops->load(store, noffset, nlen, &img_data, RPROC_LOAD_ANYADDR, NULL, 1); if (ret < (int)nlen) { if ((last_load_state & RPROC_LOADER_POST_DATA_LOAD) != 0) { metal_log(METAL_LOG_WARNING, "not all the headers are loaded\r\n"); break; } metal_log(METAL_LOG_ERROR, "post-load image data failed 0x%x,%d\r\n", noffset, nlen); goto error3; } offset = noffset; len = nlen; } else { /* (last_load_state & RPROC_LOADER_LOAD_COMPLETE) != 0 */ break; } } if (rsc_len < 0) { ret = elf_locate_rsc_table(limg_info, &rsc_da, &offset, &rsc_size); if (ret == 0 && rsc_size > 0) { /* parse resource table */ rsc_len = (int)rsc_size; rsc_table = remoteproc_get_rsc_table(rproc, store, store_ops, offset, rsc_len); } } /* Update resource table */ if (rsc_len && rsc_da != METAL_BAD_PHYS) { void *rsc_table_cp = rsc_table; metal_log(METAL_LOG_DEBUG, "%s, update resource table\r\n", __func__); rsc_table = remoteproc_mmap(rproc, NULL, &rsc_da, rsc_len, 0, &io); if (rsc_table) { size_t rsc_io_offset; /* Update resource table */ rsc_io_offset = metal_io_virt_to_offset(io, rsc_table); ret = metal_io_block_write(io, rsc_io_offset, rsc_table_cp, rsc_len); if (ret != rsc_len) { metal_log(METAL_LOG_WARNING, "load: failed to update rsc\r\n"); } rproc->rsc_table = rsc_table; rproc->rsc_len = rsc_len; } else { metal_log(METAL_LOG_WARNING, "load: not able to update rsc table.\n"); } metal_free_memory(rsc_table_cp); /* So that the rsc_table will not get released */ rsc_table = NULL; } metal_log(METAL_LOG_DEBUG, "%s: successfully load firmware\r\n", __func__); /* get entry point from the firmware */ rproc->bootaddr = loader->get_entry(limg_info); rproc->state = RPROC_READY; metal_mutex_release(&rproc->lock); if (img_info) *img_info = limg_info; else loader->release(limg_info); store_ops->close(store); return 0; error3: if (rsc_table) metal_free_memory(rsc_table); error2: loader->release(limg_info); error1: store_ops->close(store); metal_mutex_release(&rproc->lock); return ret; } int remoteproc_load_noblock(struct remoteproc *rproc, const void *img_data, size_t offset, size_t len, void **img_info, metal_phys_addr_t *pa, struct metal_io_region **io, size_t *noffset, size_t *nlen, size_t *nmlen, unsigned char *padding) { int ret; struct loader_ops *loader; void *limg_info = NULL; int last_load_state; metal_phys_addr_t da, rsc_da; size_t rsc_size; void *rsc_table = NULL, *lrsc_table = NULL; if (!rproc) return -RPROC_ENODEV; metal_assert(pa != NULL); metal_assert(io != NULL); metal_assert(noffset != NULL); metal_assert(nlen != NULL); metal_assert(nmlen != NULL); metal_assert(padding != NULL); metal_mutex_acquire(&rproc->lock); metal_log(METAL_LOG_DEBUG, "%s: check remoteproc status\r\n", __func__); /* If remoteproc is not in ready state, cannot load executable */ if (rproc->state != RPROC_READY) { metal_log(METAL_LOG_ERROR, "load failure: invalid rproc state %d.\r\n", rproc->state); metal_mutex_release(&rproc->lock); return -RPROC_EINVAL; } /* Check executable format to select a parser */ loader = rproc->loader; if (!loader) { metal_log(METAL_LOG_DEBUG, "%s: check loader\r\n", __func__); if (img_data == NULL || offset != 0 || len == 0) { metal_log(METAL_LOG_ERROR, "load failure, invalid inputs, not able to identify image.\r\n"); metal_mutex_release(&rproc->lock); return -RPROC_EINVAL; } loader = remoteproc_check_fw_format(img_data, len); if (!loader) { metal_log(METAL_LOG_ERROR, "load failure: failed to identify image.\n"); ret = -RPROC_EINVAL; metal_mutex_release(&rproc->lock); return -RPROC_EINVAL; } rproc->loader = loader; } if (img_info == NULL || *img_info == NULL ) { last_load_state = 0; } else { limg_info = *img_info; last_load_state = loader->get_load_state(limg_info); if (last_load_state < 0) { metal_log(METAL_LOG_ERROR, "load failure, not able get load state.\r\n"); metal_mutex_release(&rproc->lock); return -RPROC_EINVAL; } } da = RPROC_LOAD_ANYADDR; *nlen = 0; if ((last_load_state & RPROC_LOADER_READY_TO_LOAD) == 0 && (last_load_state & RPROC_LOADER_LOAD_COMPLETE) == 0) { /* Get the mandatory executable headers */ ret = loader->load_header(img_data, offset, len, &limg_info, last_load_state, noffset, nlen); last_load_state = (unsigned int)ret; metal_log(METAL_LOG_DEBUG, "%s, load header 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n", __func__, offset, len, *noffset, *nlen); if (ret < 0) { metal_log(METAL_LOG_ERROR, "load header failed 0x%lx,%d.\r\n", offset, len); goto error1; } last_load_state = loader->get_load_state(limg_info); if (*nlen != 0 && (last_load_state & RPROC_LOADER_READY_TO_LOAD) == 0) goto out; } if ((last_load_state & RPROC_LOADER_READY_TO_LOAD) != 0 || (last_load_state & RPROC_LOADER_POST_DATA_LOAD) != 0) { /* Enough information to know which target memory for * which data. */ ret = loader->load_data(rproc, img_data, offset, len, &limg_info, last_load_state, &da, noffset, nlen, padding, nmlen); metal_log(METAL_LOG_DEBUG, "%s, load data 0x%lx, 0x%x, next 0x%lx, 0x%x\r\n", __func__, offset, len, *noffset, *nlen); if (ret < 0) { metal_log(METAL_LOG_ERROR, "load data failed,0x%lx,%d\r\n", offset, len); goto error1; } if (da != RPROC_LOAD_ANYADDR) { /* get the I/O region from remoteproc */ *pa = METAL_BAD_PHYS; (void)remoteproc_mmap(rproc, pa, &da, *nmlen, 0, io); if (*pa == METAL_BAD_PHYS || io == NULL) { metal_log(METAL_LOG_ERROR, "load failed, no mapping for 0x%llx.\r\n", da); ret = -RPROC_EINVAL; goto error1; } } if (*nlen != 0) goto out; else last_load_state = loader->get_load_state(limg_info); } if ((last_load_state & RPROC_LOADER_LOAD_COMPLETE) != 0) { /* Get resource table */ size_t rsc_offset; size_t rsc_io_offset; ret = elf_locate_rsc_table(limg_info, &rsc_da, &rsc_offset, &rsc_size); if (ret == 0 && rsc_size > 0) { lrsc_table = metal_allocate_memory(rsc_size); if (lrsc_table == NULL) { ret = -RPROC_ENOMEM; goto error1; } rsc_table = remoteproc_mmap(rproc, NULL, &rsc_da, rsc_size, 0, io); if (*io == NULL) { metal_log(METAL_LOG_ERROR, "load failed: failed to mmap rsc\r\n"); metal_free_memory(lrsc_table); goto error1; } rsc_io_offset = metal_io_virt_to_offset(*io, rsc_table); ret = metal_io_block_read(*io, rsc_io_offset, lrsc_table, (int)rsc_size); if (ret != (int)rsc_size) { metal_log(METAL_LOG_ERROR, "load failed: failed to get rsc\r\n"); metal_free_memory(lrsc_table); goto error1; } /* parse resource table */ ret = remoteproc_parse_rsc_table(rproc, lrsc_table, rsc_size); if (ret == (int)rsc_size) { metal_log(METAL_LOG_ERROR, "load failed: failed to parse rsc\r\n"); metal_free_memory(lrsc_table); goto error1; } /* Update resource table */ ret = metal_io_block_write(*io, rsc_io_offset, lrsc_table, (int)rsc_size); if (ret != (int)rsc_size) { metal_log(METAL_LOG_WARNING, "load exectuable, failed to update rsc\r\n"); } rproc->rsc_table = rsc_table; rproc->rsc_len = (int)rsc_size; metal_free_memory(lrsc_table); } } out: if (img_info != NULL) *img_info = limg_info; else loader->release(limg_info); metal_mutex_release(&rproc->lock); return 0; error1: loader->release(limg_info); metal_mutex_release(&rproc->lock); return ret; } unsigned int remoteproc_allocate_id(struct remoteproc *rproc, unsigned int start, unsigned int end) { unsigned int notifyid; if (start == RSC_NOTIFY_ID_ANY) start = 0; if (end == RSC_NOTIFY_ID_ANY) end = METAL_BITS_PER_ULONG; notifyid = metal_bitmap_next_set_bit(&rproc->bitmap, start, end); if (notifyid != end) metal_bitmap_set_bit(&rproc->bitmap, notifyid); return notifyid; } static int remoteproc_virtio_notify(void *priv, uint32_t id) { struct remoteproc *rproc = priv; return rproc->ops->notify(rproc, id); } struct virtio_device * remoteproc_create_virtio(struct remoteproc *rproc, int vdev_id, unsigned int role, void (*rst_cb)(struct virtio_device *vdev)) { char *rsc_table; struct fw_rsc_vdev *vdev_rsc; struct metal_io_region *vdev_rsc_io; struct virtio_device *vdev; struct remoteproc_virtio *rpvdev; size_t vdev_rsc_offset; unsigned int notifyid; unsigned int num_vrings, i; struct metal_list *node; metal_assert(rproc); metal_mutex_acquire(&rproc->lock); rsc_table = rproc->rsc_table; vdev_rsc_io = rproc->rsc_io; vdev_rsc_offset = find_rsc(rsc_table, RSC_VDEV, vdev_id); if (!vdev_rsc_offset) { metal_mutex_release(&rproc->lock); return NULL; } vdev_rsc = (struct fw_rsc_vdev *)(rsc_table + vdev_rsc_offset); notifyid = vdev_rsc->notifyid; /* Check if the virtio device is already created */ metal_list_for_each(&rproc->vdevs, node) { rpvdev = metal_container_of(node, struct remoteproc_virtio, node); if (rpvdev->vdev.index == notifyid) return &rpvdev->vdev; } vdev = rproc_virtio_create_vdev(role, notifyid, vdev_rsc, vdev_rsc_io, rproc, remoteproc_virtio_notify, rst_cb); rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); metal_list_add_tail(&rproc->vdevs, &rpvdev->node); num_vrings = vdev_rsc->num_of_vrings; /* set the notification id for vrings */ for (i = 0; i < num_vrings; i++) { struct fw_rsc_vdev_vring *vring_rsc; metal_phys_addr_t da; unsigned int num_descs, align; struct metal_io_region *io; void *va; size_t size; int ret; vring_rsc = &vdev_rsc->vring[i]; notifyid = vring_rsc->notifyid; da = vring_rsc->da; num_descs = vring_rsc->num; align = vring_rsc->align; size = vring_size(num_descs, align); va = remoteproc_mmap(rproc, NULL, &da, size, 0, &io); if (!va) goto err1; ret = rproc_virtio_init_vring(vdev, i, notifyid, va, io, num_descs, align); if (ret) goto err1; } metal_mutex_release(&rproc->lock); return vdev; err1: remoteproc_remove_virtio(rproc, vdev); metal_mutex_release(&rproc->lock); return NULL; } void remoteproc_remove_virtio(struct remoteproc *rproc, struct virtio_device *vdev) { struct remoteproc_virtio *rpvdev; (void)rproc; metal_assert(vdev); rpvdev = metal_container_of(vdev, struct remoteproc_virtio, vdev); metal_list_del(&rpvdev->node); rproc_virtio_remove_vdev(&rpvdev->vdev); } int remoteproc_get_notification(struct remoteproc *rproc, uint32_t notifyid) { struct remoteproc_virtio *rpvdev; struct metal_list *node; int ret; metal_list_for_each(&rproc->vdevs, node) { rpvdev = metal_container_of(node, struct remoteproc_virtio, node); ret = rproc_virtio_notified(&rpvdev->vdev, notifyid); if (ret) return ret; } return 0; }