Commit de9c9f86be0dc3495de98dc65c80abe6e7c7d643
Exists in
master
and in
20 other branches
Merge tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc
Pull remoteproc update from Ohad Ben-Cohen: - Some refactoring, cleanups and small improvements from Sjur Brændeland. The improvements are mainly about better supporting varios virtio properties (such as virtio's config space, status and features). I now see that I messed up while commiting one of Sjur's patches and erroneously put myself as the author, as well as letting a nasty typo sneak in. I will not fix this in order to avoid rebasing the patches. Sjur - sorry! - A new remoteproc driver for OMAP-L13x (technically a DaVinci platform) from Robert Tivy. - Extend OMAP support to OMAP5 as well, from Vincent Stehlé. - Fix Kconfig VIRTUALIZATION dependency, from Suman Anna (a non-critical fix which arrived late during the rc cycle). * tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc: remoteproc: fix kconfig dependencies for VIRTIO remoteproc/davinci: add a remoteproc driver for OMAP-L13x DSP remoteproc: support default firmware name in rproc_alloc() remoteproc/omap: support OMAP5 too remoteproc: set vring addresses in resource table remoteproc: support virtio config space. remoteproc: perserve resource table data remoteproc: calculate max_notifyid by counting vrings remoteproc: code cleanup of resource parsing remoteproc: parse STE-firmware and find resource table address remoteproc: add find_loaded_rsc_table firmware ops remoteproc: refactor rproc_elf_find_rsc_table()
Showing 9 changed files Side-by-side Diff
- drivers/remoteproc/Kconfig
- drivers/remoteproc/Makefile
- drivers/remoteproc/da8xx_remoteproc.c
- drivers/remoteproc/remoteproc_core.c
- drivers/remoteproc/remoteproc_elf_loader.c
- drivers/remoteproc/remoteproc_internal.h
- drivers/remoteproc/remoteproc_virtio.c
- drivers/remoteproc/ste_modem_rproc.c
- include/linux/remoteproc.h
drivers/remoteproc/Kconfig
... | ... | @@ -4,13 +4,15 @@ |
4 | 4 | config REMOTEPROC |
5 | 5 | tristate |
6 | 6 | depends on HAS_DMA |
7 | + select CRC32 | |
7 | 8 | select FW_LOADER |
8 | 9 | select VIRTIO |
10 | + select VIRTUALIZATION | |
9 | 11 | |
10 | 12 | config OMAP_REMOTEPROC |
11 | 13 | tristate "OMAP remoteproc support" |
12 | 14 | depends on HAS_DMA |
13 | - depends on ARCH_OMAP4 | |
15 | + depends on ARCH_OMAP4 || SOC_OMAP5 | |
14 | 16 | depends on OMAP_IOMMU |
15 | 17 | depends on OMAP_MBOX_FWK |
16 | 18 | select REMOTEPROC |
... | ... | @@ -37,6 +39,29 @@ |
37 | 39 | Say y or m here to support STE-Modem shared memory driver. |
38 | 40 | This can be either built-in or a loadable module. |
39 | 41 | If unsure say N. |
42 | + | |
43 | +config DA8XX_REMOTEPROC | |
44 | + tristate "DA8xx/OMAP-L13x remoteproc support" | |
45 | + depends on ARCH_DAVINCI_DA8XX | |
46 | + select CMA | |
47 | + select REMOTEPROC | |
48 | + select RPMSG | |
49 | + help | |
50 | + Say y here to support DA8xx/OMAP-L13x remote processors via the | |
51 | + remote processor framework. | |
52 | + | |
53 | + You want to say y here in order to enable AMP | |
54 | + use-cases to run on your platform (multimedia codecs are | |
55 | + offloaded to remote DSP processors using this framework). | |
56 | + | |
57 | + This module controls the name of the firmware file that gets | |
58 | + loaded on the DSP. This file must reside in the /lib/firmware | |
59 | + directory. It can be specified via the module parameter | |
60 | + da8xx_fw_name=<filename>, and if not specified will default to | |
61 | + "rproc-dsp-fw". | |
62 | + | |
63 | + It's safe to say n here if you're not interested in multimedia | |
64 | + offloading. | |
40 | 65 | |
41 | 66 | endmenu |
drivers/remoteproc/Makefile
drivers/remoteproc/da8xx_remoteproc.c
1 | +/* | |
2 | + * Remote processor machine-specific module for DA8XX | |
3 | + * | |
4 | + * Copyright (C) 2013 Texas Instruments, Inc. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public License | |
8 | + * version 2 as published by the Free Software Foundation. | |
9 | + */ | |
10 | + | |
11 | +#include <linux/bitops.h> | |
12 | +#include <linux/clk.h> | |
13 | +#include <linux/err.h> | |
14 | +#include <linux/interrupt.h> | |
15 | +#include <linux/io.h> | |
16 | +#include <linux/irq.h> | |
17 | +#include <linux/kernel.h> | |
18 | +#include <linux/module.h> | |
19 | +#include <linux/platform_device.h> | |
20 | +#include <linux/remoteproc.h> | |
21 | + | |
22 | +#include <mach/clock.h> /* for davinci_clk_reset_assert/deassert() */ | |
23 | + | |
24 | +#include "remoteproc_internal.h" | |
25 | + | |
26 | +static char *da8xx_fw_name; | |
27 | +module_param(da8xx_fw_name, charp, S_IRUGO); | |
28 | +MODULE_PARM_DESC(da8xx_fw_name, | |
29 | + "\n\t\tName of DSP firmware file in /lib/firmware" | |
30 | + " (if not specified defaults to 'rproc-dsp-fw')"); | |
31 | + | |
32 | +/* | |
33 | + * OMAP-L138 Technical References: | |
34 | + * http://www.ti.com/product/omap-l138 | |
35 | + */ | |
36 | +#define SYSCFG_CHIPSIG0 BIT(0) | |
37 | +#define SYSCFG_CHIPSIG1 BIT(1) | |
38 | +#define SYSCFG_CHIPSIG2 BIT(2) | |
39 | +#define SYSCFG_CHIPSIG3 BIT(3) | |
40 | +#define SYSCFG_CHIPSIG4 BIT(4) | |
41 | + | |
42 | +/** | |
43 | + * struct da8xx_rproc - da8xx remote processor instance state | |
44 | + * @rproc: rproc handle | |
45 | + * @dsp_clk: placeholder for platform's DSP clk | |
46 | + * @ack_fxn: chip-specific ack function for ack'ing irq | |
47 | + * @irq_data: ack_fxn function parameter | |
48 | + * @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR) | |
49 | + * @bootreg: virt ptr to DSP boot address register (HOST1CFG) | |
50 | + * @irq: irq # used by this instance | |
51 | + */ | |
52 | +struct da8xx_rproc { | |
53 | + struct rproc *rproc; | |
54 | + struct clk *dsp_clk; | |
55 | + void (*ack_fxn)(struct irq_data *data); | |
56 | + struct irq_data *irq_data; | |
57 | + void __iomem *chipsig; | |
58 | + void __iomem *bootreg; | |
59 | + int irq; | |
60 | +}; | |
61 | + | |
62 | +/** | |
63 | + * handle_event() - inbound virtqueue message workqueue function | |
64 | + * | |
65 | + * This function is registered as a kernel thread and is scheduled by the | |
66 | + * kernel handler. | |
67 | + */ | |
68 | +static irqreturn_t handle_event(int irq, void *p) | |
69 | +{ | |
70 | + struct rproc *rproc = (struct rproc *)p; | |
71 | + | |
72 | + /* Process incoming buffers on all our vrings */ | |
73 | + rproc_vq_interrupt(rproc, 0); | |
74 | + rproc_vq_interrupt(rproc, 1); | |
75 | + | |
76 | + return IRQ_HANDLED; | |
77 | +} | |
78 | + | |
79 | +/** | |
80 | + * da8xx_rproc_callback() - inbound virtqueue message handler | |
81 | + * | |
82 | + * This handler is invoked directly by the kernel whenever the remote | |
83 | + * core (DSP) has modified the state of a virtqueue. There is no | |
84 | + * "payload" message indicating the virtqueue index as is the case with | |
85 | + * mailbox-based implementations on OMAP4. As such, this handler "polls" | |
86 | + * each known virtqueue index for every invocation. | |
87 | + */ | |
88 | +static irqreturn_t da8xx_rproc_callback(int irq, void *p) | |
89 | +{ | |
90 | + struct rproc *rproc = (struct rproc *)p; | |
91 | + struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; | |
92 | + u32 chipsig; | |
93 | + | |
94 | + chipsig = readl(drproc->chipsig); | |
95 | + if (chipsig & SYSCFG_CHIPSIG0) { | |
96 | + /* Clear interrupt level source */ | |
97 | + writel(SYSCFG_CHIPSIG0, drproc->chipsig + 4); | |
98 | + | |
99 | + /* | |
100 | + * ACK intr to AINTC. | |
101 | + * | |
102 | + * It has already been ack'ed by the kernel before calling | |
103 | + * this function, but since the ARM<->DSP interrupts in the | |
104 | + * CHIPSIG register are "level" instead of "pulse" variety, | |
105 | + * we need to ack it after taking down the level else we'll | |
106 | + * be called again immediately after returning. | |
107 | + */ | |
108 | + drproc->ack_fxn(drproc->irq_data); | |
109 | + | |
110 | + return IRQ_WAKE_THREAD; | |
111 | + } | |
112 | + | |
113 | + return IRQ_HANDLED; | |
114 | +} | |
115 | + | |
116 | +static int da8xx_rproc_start(struct rproc *rproc) | |
117 | +{ | |
118 | + struct device *dev = rproc->dev.parent; | |
119 | + struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; | |
120 | + struct clk *dsp_clk = drproc->dsp_clk; | |
121 | + | |
122 | + /* hw requires the start (boot) address be on 1KB boundary */ | |
123 | + if (rproc->bootaddr & 0x3ff) { | |
124 | + dev_err(dev, "invalid boot address: must be aligned to 1KB\n"); | |
125 | + | |
126 | + return -EINVAL; | |
127 | + } | |
128 | + | |
129 | + writel(rproc->bootaddr, drproc->bootreg); | |
130 | + | |
131 | + clk_enable(dsp_clk); | |
132 | + davinci_clk_reset_deassert(dsp_clk); | |
133 | + | |
134 | + return 0; | |
135 | +} | |
136 | + | |
137 | +static int da8xx_rproc_stop(struct rproc *rproc) | |
138 | +{ | |
139 | + struct da8xx_rproc *drproc = rproc->priv; | |
140 | + | |
141 | + clk_disable(drproc->dsp_clk); | |
142 | + | |
143 | + return 0; | |
144 | +} | |
145 | + | |
146 | +/* kick a virtqueue */ | |
147 | +static void da8xx_rproc_kick(struct rproc *rproc, int vqid) | |
148 | +{ | |
149 | + struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; | |
150 | + | |
151 | + /* Interupt remote proc */ | |
152 | + writel(SYSCFG_CHIPSIG2, drproc->chipsig); | |
153 | +} | |
154 | + | |
155 | +static struct rproc_ops da8xx_rproc_ops = { | |
156 | + .start = da8xx_rproc_start, | |
157 | + .stop = da8xx_rproc_stop, | |
158 | + .kick = da8xx_rproc_kick, | |
159 | +}; | |
160 | + | |
161 | +static int reset_assert(struct device *dev) | |
162 | +{ | |
163 | + struct clk *dsp_clk; | |
164 | + | |
165 | + dsp_clk = clk_get(dev, NULL); | |
166 | + if (IS_ERR(dsp_clk)) { | |
167 | + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); | |
168 | + return PTR_RET(dsp_clk); | |
169 | + } | |
170 | + | |
171 | + davinci_clk_reset_assert(dsp_clk); | |
172 | + clk_put(dsp_clk); | |
173 | + | |
174 | + return 0; | |
175 | +} | |
176 | + | |
177 | +static int da8xx_rproc_probe(struct platform_device *pdev) | |
178 | +{ | |
179 | + struct device *dev = &pdev->dev; | |
180 | + struct da8xx_rproc *drproc; | |
181 | + struct rproc *rproc; | |
182 | + struct irq_data *irq_data; | |
183 | + struct resource *bootreg_res; | |
184 | + struct resource *chipsig_res; | |
185 | + struct clk *dsp_clk; | |
186 | + void __iomem *chipsig; | |
187 | + void __iomem *bootreg; | |
188 | + int irq; | |
189 | + int ret; | |
190 | + | |
191 | + irq = platform_get_irq(pdev, 0); | |
192 | + if (irq < 0) { | |
193 | + dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq); | |
194 | + return irq; | |
195 | + } | |
196 | + | |
197 | + irq_data = irq_get_irq_data(irq); | |
198 | + if (!irq_data) { | |
199 | + dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq); | |
200 | + return -EINVAL; | |
201 | + } | |
202 | + | |
203 | + bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
204 | + if (!bootreg_res) { | |
205 | + dev_err(dev, | |
206 | + "platform_get_resource(IORESOURCE_MEM, 0): NULL\n"); | |
207 | + return -EADDRNOTAVAIL; | |
208 | + } | |
209 | + | |
210 | + chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | |
211 | + if (!chipsig_res) { | |
212 | + dev_err(dev, | |
213 | + "platform_get_resource(IORESOURCE_MEM, 1): NULL\n"); | |
214 | + return -EADDRNOTAVAIL; | |
215 | + } | |
216 | + | |
217 | + bootreg = devm_ioremap_resource(dev, bootreg_res); | |
218 | + if (IS_ERR(bootreg)) | |
219 | + return PTR_ERR(bootreg); | |
220 | + | |
221 | + chipsig = devm_ioremap_resource(dev, chipsig_res); | |
222 | + if (IS_ERR(chipsig)) | |
223 | + return PTR_ERR(chipsig); | |
224 | + | |
225 | + dsp_clk = devm_clk_get(dev, NULL); | |
226 | + if (IS_ERR(dsp_clk)) { | |
227 | + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); | |
228 | + | |
229 | + return PTR_ERR(dsp_clk); | |
230 | + } | |
231 | + | |
232 | + rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name, | |
233 | + sizeof(*drproc)); | |
234 | + if (!rproc) | |
235 | + return -ENOMEM; | |
236 | + | |
237 | + drproc = rproc->priv; | |
238 | + drproc->rproc = rproc; | |
239 | + | |
240 | + platform_set_drvdata(pdev, rproc); | |
241 | + | |
242 | + /* everything the ISR needs is now setup, so hook it up */ | |
243 | + ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback, | |
244 | + handle_event, 0, "da8xx-remoteproc", | |
245 | + rproc); | |
246 | + if (ret) { | |
247 | + dev_err(dev, "devm_request_threaded_irq error: %d\n", ret); | |
248 | + goto free_rproc; | |
249 | + } | |
250 | + | |
251 | + /* | |
252 | + * rproc_add() can end up enabling the DSP's clk with the DSP | |
253 | + * *not* in reset, but da8xx_rproc_start() needs the DSP to be | |
254 | + * held in reset at the time it is called. | |
255 | + */ | |
256 | + ret = reset_assert(dev); | |
257 | + if (ret) | |
258 | + goto free_rproc; | |
259 | + | |
260 | + drproc->chipsig = chipsig; | |
261 | + drproc->bootreg = bootreg; | |
262 | + drproc->ack_fxn = irq_data->chip->irq_ack; | |
263 | + drproc->irq_data = irq_data; | |
264 | + drproc->irq = irq; | |
265 | + drproc->dsp_clk = dsp_clk; | |
266 | + | |
267 | + ret = rproc_add(rproc); | |
268 | + if (ret) { | |
269 | + dev_err(dev, "rproc_add failed: %d\n", ret); | |
270 | + goto free_rproc; | |
271 | + } | |
272 | + | |
273 | + return 0; | |
274 | + | |
275 | +free_rproc: | |
276 | + rproc_put(rproc); | |
277 | + | |
278 | + return ret; | |
279 | +} | |
280 | + | |
281 | +static int da8xx_rproc_remove(struct platform_device *pdev) | |
282 | +{ | |
283 | + struct device *dev = &pdev->dev; | |
284 | + struct rproc *rproc = platform_get_drvdata(pdev); | |
285 | + struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; | |
286 | + | |
287 | + /* | |
288 | + * It's important to place the DSP in reset before going away, | |
289 | + * since a subsequent insmod of this module may enable the DSP's | |
290 | + * clock before its program/boot-address has been loaded and | |
291 | + * before this module's probe has had a chance to reset the DSP. | |
292 | + * Without the reset, the DSP can lockup permanently when it | |
293 | + * begins executing garbage. | |
294 | + */ | |
295 | + reset_assert(dev); | |
296 | + | |
297 | + /* | |
298 | + * The devm subsystem might end up releasing things before | |
299 | + * freeing the irq, thus allowing an interrupt to sneak in while | |
300 | + * the device is being removed. This should prevent that. | |
301 | + */ | |
302 | + disable_irq(drproc->irq); | |
303 | + | |
304 | + devm_clk_put(dev, drproc->dsp_clk); | |
305 | + | |
306 | + rproc_del(rproc); | |
307 | + rproc_put(rproc); | |
308 | + | |
309 | + return 0; | |
310 | +} | |
311 | + | |
312 | +static struct platform_driver da8xx_rproc_driver = { | |
313 | + .probe = da8xx_rproc_probe, | |
314 | + .remove = da8xx_rproc_remove, | |
315 | + .driver = { | |
316 | + .name = "davinci-rproc", | |
317 | + .owner = THIS_MODULE, | |
318 | + }, | |
319 | +}; | |
320 | + | |
321 | +module_platform_driver(da8xx_rproc_driver); | |
322 | + | |
323 | +MODULE_LICENSE("GPL v2"); | |
324 | +MODULE_DESCRIPTION("DA8XX Remote Processor control driver"); |
drivers/remoteproc/remoteproc_core.c
... | ... | @@ -37,6 +37,7 @@ |
37 | 37 | #include <linux/iommu.h> |
38 | 38 | #include <linux/idr.h> |
39 | 39 | #include <linux/elf.h> |
40 | +#include <linux/crc32.h> | |
40 | 41 | #include <linux/virtio_ids.h> |
41 | 42 | #include <linux/virtio_ring.h> |
42 | 43 | #include <asm/byteorder.h> |
... | ... | @@ -45,7 +46,8 @@ |
45 | 46 | |
46 | 47 | typedef int (*rproc_handle_resources_t)(struct rproc *rproc, |
47 | 48 | struct resource_table *table, int len); |
48 | -typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); | |
49 | +typedef int (*rproc_handle_resource_t)(struct rproc *rproc, | |
50 | + void *, int offset, int avail); | |
49 | 51 | |
50 | 52 | /* Unique indices for remoteproc devices */ |
51 | 53 | static DEFINE_IDA(rproc_dev_index); |
... | ... | @@ -192,6 +194,7 @@ |
192 | 194 | struct rproc *rproc = rvdev->rproc; |
193 | 195 | struct device *dev = &rproc->dev; |
194 | 196 | struct rproc_vring *rvring = &rvdev->vring[i]; |
197 | + struct fw_rsc_vdev *rsc; | |
195 | 198 | dma_addr_t dma; |
196 | 199 | void *va; |
197 | 200 | int ret, size, notifyid; |
... | ... | @@ -202,7 +205,6 @@ |
202 | 205 | /* |
203 | 206 | * Allocate non-cacheable memory for the vring. In the future |
204 | 207 | * this call will also configure the IOMMU for us |
205 | - * TODO: let the rproc know the da of this vring | |
206 | 208 | */ |
207 | 209 | va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); |
208 | 210 | if (!va) { |
... | ... | @@ -213,7 +215,6 @@ |
213 | 215 | /* |
214 | 216 | * Assign an rproc-wide unique index for this vring |
215 | 217 | * TODO: assign a notifyid for rvdev updates as well |
216 | - * TODO: let the rproc know the notifyid of this vring | |
217 | 218 | * TODO: support predefined notifyids (via resource table) |
218 | 219 | */ |
219 | 220 | ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL); |
... | ... | @@ -224,9 +225,6 @@ |
224 | 225 | } |
225 | 226 | notifyid = ret; |
226 | 227 | |
227 | - /* Store largest notifyid */ | |
228 | - rproc->max_notifyid = max(rproc->max_notifyid, notifyid); | |
229 | - | |
230 | 228 | dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va, |
231 | 229 | (unsigned long long)dma, size, notifyid); |
232 | 230 | |
... | ... | @@ -234,6 +232,15 @@ |
234 | 232 | rvring->dma = dma; |
235 | 233 | rvring->notifyid = notifyid; |
236 | 234 | |
235 | + /* | |
236 | + * Let the rproc know the notifyid and da of this vring. | |
237 | + * Not all platforms use dma_alloc_coherent to automatically | |
238 | + * set up the iommu. In this case the device address (da) will | |
239 | + * hold the physical address and not the device address. | |
240 | + */ | |
241 | + rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; | |
242 | + rsc->vring[i].da = dma; | |
243 | + rsc->vring[i].notifyid = notifyid; | |
237 | 244 | return 0; |
238 | 245 | } |
239 | 246 | |
240 | 247 | |
241 | 248 | |
... | ... | @@ -268,25 +275,20 @@ |
268 | 275 | return 0; |
269 | 276 | } |
270 | 277 | |
271 | -static int rproc_max_notifyid(int id, void *p, void *data) | |
272 | -{ | |
273 | - int *maxid = data; | |
274 | - *maxid = max(*maxid, id); | |
275 | - return 0; | |
276 | -} | |
277 | - | |
278 | 278 | void rproc_free_vring(struct rproc_vring *rvring) |
279 | 279 | { |
280 | 280 | int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); |
281 | 281 | struct rproc *rproc = rvring->rvdev->rproc; |
282 | - int maxid = 0; | |
282 | + int idx = rvring->rvdev->vring - rvring; | |
283 | + struct fw_rsc_vdev *rsc; | |
283 | 284 | |
284 | 285 | dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); |
285 | 286 | idr_remove(&rproc->notifyids, rvring->notifyid); |
286 | 287 | |
287 | - /* Find the largest remaining notifyid */ | |
288 | - idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid); | |
289 | - rproc->max_notifyid = maxid; | |
288 | + /* reset resource entry info */ | |
289 | + rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset; | |
290 | + rsc->vring[idx].da = 0; | |
291 | + rsc->vring[idx].notifyid = -1; | |
290 | 292 | } |
291 | 293 | |
292 | 294 | /** |
... | ... | @@ -317,7 +319,7 @@ |
317 | 319 | * Returns 0 on success, or an appropriate error code otherwise |
318 | 320 | */ |
319 | 321 | static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, |
320 | - int avail) | |
322 | + int offset, int avail) | |
321 | 323 | { |
322 | 324 | struct device *dev = &rproc->dev; |
323 | 325 | struct rproc_vdev *rvdev; |
... | ... | @@ -358,8 +360,8 @@ |
358 | 360 | goto free_rvdev; |
359 | 361 | } |
360 | 362 | |
361 | - /* remember the device features */ | |
362 | - rvdev->dfeatures = rsc->dfeatures; | |
363 | + /* remember the resource offset*/ | |
364 | + rvdev->rsc_offset = offset; | |
363 | 365 | |
364 | 366 | list_add_tail(&rvdev->node, &rproc->rvdevs); |
365 | 367 | |
... | ... | @@ -394,7 +396,7 @@ |
394 | 396 | * Returns 0 on success, or an appropriate error code otherwise |
395 | 397 | */ |
396 | 398 | static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, |
397 | - int avail) | |
399 | + int offset, int avail) | |
398 | 400 | { |
399 | 401 | struct rproc_mem_entry *trace; |
400 | 402 | struct device *dev = &rproc->dev; |
... | ... | @@ -476,7 +478,7 @@ |
476 | 478 | * are outside those ranges. |
477 | 479 | */ |
478 | 480 | static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, |
479 | - int avail) | |
481 | + int offset, int avail) | |
480 | 482 | { |
481 | 483 | struct rproc_mem_entry *mapping; |
482 | 484 | struct device *dev = &rproc->dev; |
... | ... | @@ -549,7 +551,9 @@ |
549 | 551 | * pressure is important; it may have a substantial impact on performance. |
550 | 552 | */ |
551 | 553 | static int rproc_handle_carveout(struct rproc *rproc, |
552 | - struct fw_rsc_carveout *rsc, int avail) | |
554 | + struct fw_rsc_carveout *rsc, | |
555 | + int offset, int avail) | |
556 | + | |
553 | 557 | { |
554 | 558 | struct rproc_mem_entry *carveout, *mapping; |
555 | 559 | struct device *dev = &rproc->dev; |
556 | 560 | |
557 | 561 | |
558 | 562 | |
559 | 563 | |
... | ... | @@ -671,28 +675,45 @@ |
671 | 675 | return ret; |
672 | 676 | } |
673 | 677 | |
678 | +static int rproc_count_vrings(struct rproc *rproc, struct fw_rsc_vdev *rsc, | |
679 | + int offset, int avail) | |
680 | +{ | |
681 | + /* Summarize the number of notification IDs */ | |
682 | + rproc->max_notifyid += rsc->num_of_vrings; | |
683 | + | |
684 | + return 0; | |
685 | +} | |
686 | + | |
674 | 687 | /* |
675 | 688 | * A lookup table for resource handlers. The indices are defined in |
676 | 689 | * enum fw_resource_type. |
677 | 690 | */ |
678 | -static rproc_handle_resource_t rproc_handle_rsc[] = { | |
691 | +static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = { | |
679 | 692 | [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, |
680 | 693 | [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, |
681 | 694 | [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, |
682 | 695 | [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */ |
683 | 696 | }; |
684 | 697 | |
698 | +static rproc_handle_resource_t rproc_vdev_handler[RSC_LAST] = { | |
699 | + [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev, | |
700 | +}; | |
701 | + | |
702 | +static rproc_handle_resource_t rproc_count_vrings_handler[RSC_LAST] = { | |
703 | + [RSC_VDEV] = (rproc_handle_resource_t)rproc_count_vrings, | |
704 | +}; | |
705 | + | |
685 | 706 | /* handle firmware resource entries before booting the remote processor */ |
686 | -static int | |
687 | -rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len) | |
707 | +static int rproc_handle_resources(struct rproc *rproc, int len, | |
708 | + rproc_handle_resource_t handlers[RSC_LAST]) | |
688 | 709 | { |
689 | 710 | struct device *dev = &rproc->dev; |
690 | 711 | rproc_handle_resource_t handler; |
691 | 712 | int ret = 0, i; |
692 | 713 | |
693 | - for (i = 0; i < table->num; i++) { | |
694 | - int offset = table->offset[i]; | |
695 | - struct fw_rsc_hdr *hdr = (void *)table + offset; | |
714 | + for (i = 0; i < rproc->table_ptr->num; i++) { | |
715 | + int offset = rproc->table_ptr->offset[i]; | |
716 | + struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset; | |
696 | 717 | int avail = len - offset - sizeof(*hdr); |
697 | 718 | void *rsc = (void *)hdr + sizeof(*hdr); |
698 | 719 | |
699 | 720 | |
... | ... | @@ -709,11 +730,11 @@ |
709 | 730 | continue; |
710 | 731 | } |
711 | 732 | |
712 | - handler = rproc_handle_rsc[hdr->type]; | |
733 | + handler = handlers[hdr->type]; | |
713 | 734 | if (!handler) |
714 | 735 | continue; |
715 | 736 | |
716 | - ret = handler(rproc, rsc, avail); | |
737 | + ret = handler(rproc, rsc, offset + sizeof(*hdr), avail); | |
717 | 738 | if (ret) |
718 | 739 | break; |
719 | 740 | } |
... | ... | @@ -721,40 +742,6 @@ |
721 | 742 | return ret; |
722 | 743 | } |
723 | 744 | |
724 | -/* handle firmware resource entries while registering the remote processor */ | |
725 | -static int | |
726 | -rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len) | |
727 | -{ | |
728 | - struct device *dev = &rproc->dev; | |
729 | - int ret = 0, i; | |
730 | - | |
731 | - for (i = 0; i < table->num; i++) { | |
732 | - int offset = table->offset[i]; | |
733 | - struct fw_rsc_hdr *hdr = (void *)table + offset; | |
734 | - int avail = len - offset - sizeof(*hdr); | |
735 | - struct fw_rsc_vdev *vrsc; | |
736 | - | |
737 | - /* make sure table isn't truncated */ | |
738 | - if (avail < 0) { | |
739 | - dev_err(dev, "rsc table is truncated\n"); | |
740 | - return -EINVAL; | |
741 | - } | |
742 | - | |
743 | - dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type); | |
744 | - | |
745 | - if (hdr->type != RSC_VDEV) | |
746 | - continue; | |
747 | - | |
748 | - vrsc = (struct fw_rsc_vdev *)hdr->data; | |
749 | - | |
750 | - ret = rproc_handle_vdev(rproc, vrsc, avail); | |
751 | - if (ret) | |
752 | - break; | |
753 | - } | |
754 | - | |
755 | - return ret; | |
756 | -} | |
757 | - | |
758 | 745 | /** |
759 | 746 | * rproc_resource_cleanup() - clean up and free all acquired resources |
760 | 747 | * @rproc: rproc handle |
761 | 748 | |
... | ... | @@ -805,9 +792,12 @@ |
805 | 792 | { |
806 | 793 | struct device *dev = &rproc->dev; |
807 | 794 | const char *name = rproc->firmware; |
808 | - struct resource_table *table; | |
795 | + struct resource_table *table, *loaded_table; | |
809 | 796 | int ret, tablesz; |
810 | 797 | |
798 | + if (!rproc->table_ptr) | |
799 | + return -ENOMEM; | |
800 | + | |
811 | 801 | ret = rproc_fw_sanity_check(rproc, fw); |
812 | 802 | if (ret) |
813 | 803 | return ret; |
814 | 804 | |
... | ... | @@ -833,8 +823,15 @@ |
833 | 823 | goto clean_up; |
834 | 824 | } |
835 | 825 | |
826 | + /* Verify that resource table in loaded fw is unchanged */ | |
827 | + if (rproc->table_csum != crc32(0, table, tablesz)) { | |
828 | + dev_err(dev, "resource checksum failed, fw changed?\n"); | |
829 | + ret = -EINVAL; | |
830 | + goto clean_up; | |
831 | + } | |
832 | + | |
836 | 833 | /* handle fw resources which are required to boot rproc */ |
837 | - ret = rproc_handle_boot_rsc(rproc, table, tablesz); | |
834 | + ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers); | |
838 | 835 | if (ret) { |
839 | 836 | dev_err(dev, "Failed to process resources: %d\n", ret); |
840 | 837 | goto clean_up; |
... | ... | @@ -847,6 +844,19 @@ |
847 | 844 | goto clean_up; |
848 | 845 | } |
849 | 846 | |
847 | + /* | |
848 | + * The starting device has been given the rproc->cached_table as the | |
849 | + * resource table. The address of the vring along with the other | |
850 | + * allocated resources (carveouts etc) is stored in cached_table. | |
851 | + * In order to pass this information to the remote device we must | |
852 | + * copy this information to device memory. | |
853 | + */ | |
854 | + loaded_table = rproc_find_loaded_rsc_table(rproc, fw); | |
855 | + if (!loaded_table) | |
856 | + goto clean_up; | |
857 | + | |
858 | + memcpy(loaded_table, rproc->cached_table, tablesz); | |
859 | + | |
850 | 860 | /* power up the remote processor */ |
851 | 861 | ret = rproc->ops->start(rproc); |
852 | 862 | if (ret) { |
... | ... | @@ -854,6 +864,13 @@ |
854 | 864 | goto clean_up; |
855 | 865 | } |
856 | 866 | |
867 | + /* | |
868 | + * Update table_ptr so that all subsequent vring allocations and | |
869 | + * virtio fields manipulation update the actual loaded resource table | |
870 | + * in device memory. | |
871 | + */ | |
872 | + rproc->table_ptr = loaded_table; | |
873 | + | |
857 | 874 | rproc->state = RPROC_RUNNING; |
858 | 875 | |
859 | 876 | dev_info(dev, "remote processor %s is now up\n", rproc->name); |
860 | 877 | |
... | ... | @@ -888,11 +905,30 @@ |
888 | 905 | if (!table) |
889 | 906 | goto out; |
890 | 907 | |
891 | - /* look for virtio devices and register them */ | |
892 | - ret = rproc_handle_virtio_rsc(rproc, table, tablesz); | |
908 | + rproc->table_csum = crc32(0, table, tablesz); | |
909 | + | |
910 | + /* | |
911 | + * Create a copy of the resource table. When a virtio device starts | |
912 | + * and calls vring_new_virtqueue() the address of the allocated vring | |
913 | + * will be stored in the cached_table. Before the device is started, | |
914 | + * cached_table will be copied into devic memory. | |
915 | + */ | |
916 | + rproc->cached_table = kmalloc(tablesz, GFP_KERNEL); | |
917 | + if (!rproc->cached_table) | |
918 | + goto out; | |
919 | + | |
920 | + memcpy(rproc->cached_table, table, tablesz); | |
921 | + rproc->table_ptr = rproc->cached_table; | |
922 | + | |
923 | + /* count the number of notify-ids */ | |
924 | + rproc->max_notifyid = -1; | |
925 | + ret = rproc_handle_resources(rproc, tablesz, rproc_count_vrings_handler); | |
893 | 926 | if (ret) |
894 | 927 | goto out; |
895 | 928 | |
929 | + /* look for virtio devices and register them */ | |
930 | + ret = rproc_handle_resources(rproc, tablesz, rproc_vdev_handler); | |
931 | + | |
896 | 932 | out: |
897 | 933 | release_firmware(fw); |
898 | 934 | /* allow rproc_del() contexts, if any, to proceed */ |
... | ... | @@ -950,6 +986,9 @@ |
950 | 986 | /* wait until there is no more rproc users */ |
951 | 987 | wait_for_completion(&rproc->crash_comp); |
952 | 988 | |
989 | + /* Free the copy of the resource table */ | |
990 | + kfree(rproc->cached_table); | |
991 | + | |
953 | 992 | return rproc_add_virtio_devices(rproc); |
954 | 993 | } |
955 | 994 | |
... | ... | @@ -1105,6 +1144,9 @@ |
1105 | 1144 | |
1106 | 1145 | rproc_disable_iommu(rproc); |
1107 | 1146 | |
1147 | + /* Give the next start a clean resource table */ | |
1148 | + rproc->table_ptr = rproc->cached_table; | |
1149 | + | |
1108 | 1150 | /* if in crash state, unlock crash handler */ |
1109 | 1151 | if (rproc->state == RPROC_CRASHED) |
1110 | 1152 | complete_all(&rproc->crash_comp); |
1111 | 1153 | |
... | ... | @@ -1196,11 +1238,11 @@ |
1196 | 1238 | * @dev: the underlying device |
1197 | 1239 | * @name: name of this remote processor |
1198 | 1240 | * @ops: platform-specific handlers (mainly start/stop) |
1199 | - * @firmware: name of firmware file to load | |
1241 | + * @firmware: name of firmware file to load, can be NULL | |
1200 | 1242 | * @len: length of private data needed by the rproc driver (in bytes) |
1201 | 1243 | * |
1202 | 1244 | * Allocates a new remote processor handle, but does not register |
1203 | - * it yet. | |
1245 | + * it yet. if @firmware is NULL, a default name is used. | |
1204 | 1246 | * |
1205 | 1247 | * This function should be used by rproc implementations during initialization |
1206 | 1248 | * of the remote processor. |
1207 | 1249 | |
1208 | 1250 | |
1209 | 1251 | |
... | ... | @@ -1219,19 +1261,39 @@ |
1219 | 1261 | const char *firmware, int len) |
1220 | 1262 | { |
1221 | 1263 | struct rproc *rproc; |
1264 | + char *p, *template = "rproc-%s-fw"; | |
1265 | + int name_len = 0; | |
1222 | 1266 | |
1223 | 1267 | if (!dev || !name || !ops) |
1224 | 1268 | return NULL; |
1225 | 1269 | |
1226 | - rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL); | |
1270 | + if (!firmware) | |
1271 | + /* | |
1272 | + * Make room for default firmware name (minus %s plus '\0'). | |
1273 | + * If the caller didn't pass in a firmware name then | |
1274 | + * construct a default name. We're already glomming 'len' | |
1275 | + * bytes onto the end of the struct rproc allocation, so do | |
1276 | + * a few more for the default firmware name (but only if | |
1277 | + * the caller doesn't pass one). | |
1278 | + */ | |
1279 | + name_len = strlen(name) + strlen(template) - 2 + 1; | |
1280 | + | |
1281 | + rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL); | |
1227 | 1282 | if (!rproc) { |
1228 | 1283 | dev_err(dev, "%s: kzalloc failed\n", __func__); |
1229 | 1284 | return NULL; |
1230 | 1285 | } |
1231 | 1286 | |
1287 | + if (!firmware) { | |
1288 | + p = (char *)rproc + sizeof(struct rproc) + len; | |
1289 | + snprintf(p, name_len, template, name); | |
1290 | + } else { | |
1291 | + p = (char *)firmware; | |
1292 | + } | |
1293 | + | |
1294 | + rproc->firmware = p; | |
1232 | 1295 | rproc->name = name; |
1233 | 1296 | rproc->ops = ops; |
1234 | - rproc->firmware = firmware; | |
1235 | 1297 | rproc->priv = &rproc[1]; |
1236 | 1298 | |
1237 | 1299 | device_initialize(&rproc->dev); |
... | ... | @@ -1314,6 +1376,9 @@ |
1314 | 1376 | /* clean up remote vdev entries */ |
1315 | 1377 | list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node) |
1316 | 1378 | rproc_remove_virtio_dev(rvdev); |
1379 | + | |
1380 | + /* Free the copy of the resource table */ | |
1381 | + kfree(rproc->cached_table); | |
1317 | 1382 | |
1318 | 1383 | device_del(&rproc->dev); |
1319 | 1384 |
drivers/remoteproc/remoteproc_elf_loader.c
... | ... | @@ -208,41 +208,22 @@ |
208 | 208 | return ret; |
209 | 209 | } |
210 | 210 | |
211 | -/** | |
212 | - * rproc_elf_find_rsc_table() - find the resource table | |
213 | - * @rproc: the rproc handle | |
214 | - * @fw: the ELF firmware image | |
215 | - * @tablesz: place holder for providing back the table size | |
216 | - * | |
217 | - * This function finds the resource table inside the remote processor's | |
218 | - * firmware. It is used both upon the registration of @rproc (in order | |
219 | - * to look for and register the supported virito devices), and when the | |
220 | - * @rproc is booted. | |
221 | - * | |
222 | - * Returns the pointer to the resource table if it is found, and write its | |
223 | - * size into @tablesz. If a valid table isn't found, NULL is returned | |
224 | - * (and @tablesz isn't set). | |
225 | - */ | |
226 | -static struct resource_table * | |
227 | -rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw, | |
228 | - int *tablesz) | |
211 | +static struct elf32_shdr * | |
212 | +find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size) | |
229 | 213 | { |
230 | - struct elf32_hdr *ehdr; | |
231 | 214 | struct elf32_shdr *shdr; |
215 | + int i; | |
232 | 216 | const char *name_table; |
233 | - struct device *dev = &rproc->dev; | |
234 | 217 | struct resource_table *table = NULL; |
235 | - int i; | |
236 | - const u8 *elf_data = fw->data; | |
218 | + const u8 *elf_data = (void *)ehdr; | |
237 | 219 | |
238 | - ehdr = (struct elf32_hdr *)elf_data; | |
220 | + /* look for the resource table and handle it */ | |
239 | 221 | shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); |
240 | 222 | name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; |
241 | 223 | |
242 | - /* look for the resource table and handle it */ | |
243 | 224 | for (i = 0; i < ehdr->e_shnum; i++, shdr++) { |
244 | - int size = shdr->sh_size; | |
245 | - int offset = shdr->sh_offset; | |
225 | + u32 size = shdr->sh_size; | |
226 | + u32 offset = shdr->sh_offset; | |
246 | 227 | |
247 | 228 | if (strcmp(name_table + shdr->sh_name, ".resource_table")) |
248 | 229 | continue; |
... | ... | @@ -250,7 +231,7 @@ |
250 | 231 | table = (struct resource_table *)(elf_data + offset); |
251 | 232 | |
252 | 233 | /* make sure we have the entire table */ |
253 | - if (offset + size > fw->size) { | |
234 | + if (offset + size > fw_size || offset + size < size) { | |
254 | 235 | dev_err(dev, "resource table truncated\n"); |
255 | 236 | return NULL; |
256 | 237 | } |
257 | 238 | |
258 | 239 | |
259 | 240 | |
... | ... | @@ -280,16 +261,77 @@ |
280 | 261 | return NULL; |
281 | 262 | } |
282 | 263 | |
283 | - *tablesz = shdr->sh_size; | |
284 | - break; | |
264 | + return shdr; | |
285 | 265 | } |
286 | 266 | |
267 | + return NULL; | |
268 | +} | |
269 | + | |
270 | +/** | |
271 | + * rproc_elf_find_rsc_table() - find the resource table | |
272 | + * @rproc: the rproc handle | |
273 | + * @fw: the ELF firmware image | |
274 | + * @tablesz: place holder for providing back the table size | |
275 | + * | |
276 | + * This function finds the resource table inside the remote processor's | |
277 | + * firmware. It is used both upon the registration of @rproc (in order | |
278 | + * to look for and register the supported virito devices), and when the | |
279 | + * @rproc is booted. | |
280 | + * | |
281 | + * Returns the pointer to the resource table if it is found, and write its | |
282 | + * size into @tablesz. If a valid table isn't found, NULL is returned | |
283 | + * (and @tablesz isn't set). | |
284 | + */ | |
285 | +static struct resource_table * | |
286 | +rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw, | |
287 | + int *tablesz) | |
288 | +{ | |
289 | + struct elf32_hdr *ehdr; | |
290 | + struct elf32_shdr *shdr; | |
291 | + struct device *dev = &rproc->dev; | |
292 | + struct resource_table *table = NULL; | |
293 | + const u8 *elf_data = fw->data; | |
294 | + | |
295 | + ehdr = (struct elf32_hdr *)elf_data; | |
296 | + | |
297 | + shdr = find_table(dev, ehdr, fw->size); | |
298 | + if (!shdr) | |
299 | + return NULL; | |
300 | + | |
301 | + table = (struct resource_table *)(elf_data + shdr->sh_offset); | |
302 | + *tablesz = shdr->sh_size; | |
303 | + | |
287 | 304 | return table; |
288 | 305 | } |
289 | 306 | |
307 | +/** | |
308 | + * rproc_elf_find_loaded_rsc_table() - find the loaded resource table | |
309 | + * @rproc: the rproc handle | |
310 | + * @fw: the ELF firmware image | |
311 | + * | |
312 | + * This function finds the location of the loaded resource table. Don't | |
313 | + * call this function if the table wasn't loaded yet - it's a bug if you do. | |
314 | + * | |
315 | + * Returns the pointer to the resource table if it is found or NULL otherwise. | |
316 | + * If the table wasn't loaded yet the result is unspecified. | |
317 | + */ | |
318 | +static struct resource_table * | |
319 | +rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) | |
320 | +{ | |
321 | + struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; | |
322 | + struct elf32_shdr *shdr; | |
323 | + | |
324 | + shdr = find_table(&rproc->dev, ehdr, fw->size); | |
325 | + if (!shdr) | |
326 | + return NULL; | |
327 | + | |
328 | + return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size); | |
329 | +} | |
330 | + | |
290 | 331 | const struct rproc_fw_ops rproc_elf_fw_ops = { |
291 | 332 | .load = rproc_elf_load_segments, |
292 | 333 | .find_rsc_table = rproc_elf_find_rsc_table, |
334 | + .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, | |
293 | 335 | .sanity_check = rproc_elf_sanity_check, |
294 | 336 | .get_boot_addr = rproc_elf_get_boot_addr |
295 | 337 | }; |
drivers/remoteproc/remoteproc_internal.h
... | ... | @@ -27,7 +27,8 @@ |
27 | 27 | |
28 | 28 | /** |
29 | 29 | * struct rproc_fw_ops - firmware format specific operations. |
30 | - * @find_rsc_table: finds the resource table inside the firmware image | |
30 | + * @find_rsc_table: find the resource table inside the firmware image | |
31 | + * @find_loaded_rsc_table: find the loaded resouce table | |
31 | 32 | * @load: load firmeware to memory, where the remote processor |
32 | 33 | * expects to find it |
33 | 34 | * @sanity_check: sanity check the fw image |
... | ... | @@ -37,6 +38,8 @@ |
37 | 38 | struct resource_table *(*find_rsc_table) (struct rproc *rproc, |
38 | 39 | const struct firmware *fw, |
39 | 40 | int *tablesz); |
41 | + struct resource_table *(*find_loaded_rsc_table)(struct rproc *rproc, | |
42 | + const struct firmware *fw); | |
40 | 43 | int (*load)(struct rproc *rproc, const struct firmware *fw); |
41 | 44 | int (*sanity_check)(struct rproc *rproc, const struct firmware *fw); |
42 | 45 | u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw); |
... | ... | @@ -100,6 +103,16 @@ |
100 | 103 | return rproc->fw_ops->find_rsc_table(rproc, fw, tablesz); |
101 | 104 | |
102 | 105 | return NULL; |
106 | +} | |
107 | + | |
108 | +static inline | |
109 | +struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc, | |
110 | + const struct firmware *fw) | |
111 | +{ | |
112 | + if (rproc->fw_ops->find_loaded_rsc_table) | |
113 | + return rproc->fw_ops->find_loaded_rsc_table(rproc, fw); | |
114 | + | |
115 | + return NULL; | |
103 | 116 | } |
104 | 117 | |
105 | 118 | extern const struct rproc_fw_ops rproc_elf_fw_ops; |
drivers/remoteproc/remoteproc_virtio.c
... | ... | @@ -173,25 +173,35 @@ |
173 | 173 | return ret; |
174 | 174 | } |
175 | 175 | |
176 | -/* | |
177 | - * We don't support yet real virtio status semantics. | |
178 | - * | |
179 | - * The plan is to provide this via the VDEV resource entry | |
180 | - * which is part of the firmware: this way the remote processor | |
181 | - * will be able to access the status values as set by us. | |
182 | - */ | |
183 | 176 | static u8 rproc_virtio_get_status(struct virtio_device *vdev) |
184 | 177 | { |
185 | - return 0; | |
178 | + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); | |
179 | + struct fw_rsc_vdev *rsc; | |
180 | + | |
181 | + rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; | |
182 | + | |
183 | + return rsc->status; | |
186 | 184 | } |
187 | 185 | |
188 | 186 | static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status) |
189 | 187 | { |
188 | + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); | |
189 | + struct fw_rsc_vdev *rsc; | |
190 | + | |
191 | + rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; | |
192 | + | |
193 | + rsc->status = status; | |
190 | 194 | dev_dbg(&vdev->dev, "status: %d\n", status); |
191 | 195 | } |
192 | 196 | |
193 | 197 | static void rproc_virtio_reset(struct virtio_device *vdev) |
194 | 198 | { |
199 | + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); | |
200 | + struct fw_rsc_vdev *rsc; | |
201 | + | |
202 | + rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; | |
203 | + | |
204 | + rsc->status = 0; | |
195 | 205 | dev_dbg(&vdev->dev, "reset !\n"); |
196 | 206 | } |
197 | 207 | |
198 | 208 | |
199 | 209 | |
200 | 210 | |
201 | 211 | |
202 | 212 | |
203 | 213 | |
... | ... | @@ -199,29 +209,66 @@ |
199 | 209 | static u32 rproc_virtio_get_features(struct virtio_device *vdev) |
200 | 210 | { |
201 | 211 | struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); |
212 | + struct fw_rsc_vdev *rsc; | |
202 | 213 | |
203 | - return rvdev->dfeatures; | |
214 | + rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; | |
215 | + | |
216 | + return rsc->dfeatures; | |
204 | 217 | } |
205 | 218 | |
206 | 219 | static void rproc_virtio_finalize_features(struct virtio_device *vdev) |
207 | 220 | { |
208 | 221 | struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); |
222 | + struct fw_rsc_vdev *rsc; | |
209 | 223 | |
224 | + rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; | |
225 | + | |
210 | 226 | /* Give virtio_ring a chance to accept features */ |
211 | 227 | vring_transport_features(vdev); |
212 | 228 | |
213 | 229 | /* |
214 | 230 | * Remember the finalized features of our vdev, and provide it |
215 | 231 | * to the remote processor once it is powered on. |
216 | - * | |
217 | - * Similarly to the status field, we don't expose yet the negotiated | |
218 | - * features to the remote processors at this point. This will be | |
219 | - * fixed as part of a small resource table overhaul and then an | |
220 | - * extension of the virtio resource entries. | |
221 | 232 | */ |
222 | - rvdev->gfeatures = vdev->features[0]; | |
233 | + rsc->gfeatures = vdev->features[0]; | |
223 | 234 | } |
224 | 235 | |
236 | +static void rproc_virtio_get(struct virtio_device *vdev, unsigned offset, | |
237 | + void *buf, unsigned len) | |
238 | +{ | |
239 | + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); | |
240 | + struct fw_rsc_vdev *rsc; | |
241 | + void *cfg; | |
242 | + | |
243 | + rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; | |
244 | + cfg = &rsc->vring[rsc->num_of_vrings]; | |
245 | + | |
246 | + if (offset + len > rsc->config_len || offset + len < len) { | |
247 | + dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n"); | |
248 | + return; | |
249 | + } | |
250 | + | |
251 | + memcpy(buf, cfg + offset, len); | |
252 | +} | |
253 | + | |
254 | +static void rproc_virtio_set(struct virtio_device *vdev, unsigned offset, | |
255 | + const void *buf, unsigned len) | |
256 | +{ | |
257 | + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); | |
258 | + struct fw_rsc_vdev *rsc; | |
259 | + void *cfg; | |
260 | + | |
261 | + rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; | |
262 | + cfg = &rsc->vring[rsc->num_of_vrings]; | |
263 | + | |
264 | + if (offset + len > rsc->config_len || offset + len < len) { | |
265 | + dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n"); | |
266 | + return; | |
267 | + } | |
268 | + | |
269 | + memcpy(cfg + offset, buf, len); | |
270 | +} | |
271 | + | |
225 | 272 | static const struct virtio_config_ops rproc_virtio_config_ops = { |
226 | 273 | .get_features = rproc_virtio_get_features, |
227 | 274 | .finalize_features = rproc_virtio_finalize_features, |
... | ... | @@ -230,6 +277,8 @@ |
230 | 277 | .reset = rproc_virtio_reset, |
231 | 278 | .set_status = rproc_virtio_set_status, |
232 | 279 | .get_status = rproc_virtio_get_status, |
280 | + .get = rproc_virtio_get, | |
281 | + .set = rproc_virtio_set, | |
233 | 282 | }; |
234 | 283 | |
235 | 284 | /* |
drivers/remoteproc/ste_modem_rproc.c
... | ... | @@ -64,26 +64,18 @@ |
64 | 64 | } |
65 | 65 | |
66 | 66 | /* Find the entry for resource table in the Table of Content */ |
67 | -static struct ste_toc_entry *sproc_find_rsc_entry(const struct firmware *fw) | |
67 | +static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data) | |
68 | 68 | { |
69 | 69 | int i; |
70 | - struct ste_toc *toc; | |
70 | + const struct ste_toc *toc; | |
71 | + toc = data; | |
71 | 72 | |
72 | - if (!fw) | |
73 | - return NULL; | |
74 | - | |
75 | - toc = (void *)fw->data; | |
76 | - | |
77 | 73 | /* Search the table for the resource table */ |
78 | 74 | for (i = 0; i < SPROC_MAX_TOC_ENTRIES && |
79 | 75 | toc->table[i].start != 0xffffffff; i++) { |
80 | - | |
81 | 76 | if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME, |
82 | - sizeof(toc->table[i].name))) { | |
83 | - if (toc->table[i].start > fw->size) | |
84 | - return NULL; | |
77 | + sizeof(toc->table[i].name))) | |
85 | 78 | return &toc->table[i]; |
86 | - } | |
87 | 79 | } |
88 | 80 | |
89 | 81 | return NULL; |
90 | 82 | |
... | ... | @@ -96,9 +88,12 @@ |
96 | 88 | { |
97 | 89 | struct sproc *sproc = rproc->priv; |
98 | 90 | struct resource_table *table; |
99 | - struct ste_toc_entry *entry; | |
91 | + const struct ste_toc_entry *entry; | |
100 | 92 | |
101 | - entry = sproc_find_rsc_entry(fw); | |
93 | + if (!fw) | |
94 | + return NULL; | |
95 | + | |
96 | + entry = sproc_find_rsc_entry(fw->data); | |
102 | 97 | if (!entry) { |
103 | 98 | sproc_err(sproc, "resource table not found in fw\n"); |
104 | 99 | return NULL; |
105 | 100 | |
... | ... | @@ -149,10 +144,30 @@ |
149 | 144 | return table; |
150 | 145 | } |
151 | 146 | |
147 | +/* Find the resource table inside the remote processor's firmware. */ | |
148 | +static struct resource_table * | |
149 | +sproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) | |
150 | +{ | |
151 | + struct sproc *sproc = rproc->priv; | |
152 | + const struct ste_toc_entry *entry; | |
153 | + | |
154 | + if (!fw || !sproc->fw_addr) | |
155 | + return NULL; | |
156 | + | |
157 | + entry = sproc_find_rsc_entry(sproc->fw_addr); | |
158 | + if (!entry) { | |
159 | + sproc_err(sproc, "resource table not found in fw\n"); | |
160 | + return NULL; | |
161 | + } | |
162 | + | |
163 | + return sproc->fw_addr + entry->start; | |
164 | +} | |
165 | + | |
152 | 166 | /* STE modem firmware handler operations */ |
153 | 167 | const struct rproc_fw_ops sproc_fw_ops = { |
154 | 168 | .load = sproc_load_segments, |
155 | 169 | .find_rsc_table = sproc_find_rsc_table, |
170 | + .find_loaded_rsc_table = sproc_find_loaded_rsc_table, | |
156 | 171 | }; |
157 | 172 | |
158 | 173 | /* Kick the modem with specified notification id */ |
... | ... | @@ -198,7 +213,7 @@ |
198 | 213 | } |
199 | 214 | |
200 | 215 | /* Subscribe to notifications */ |
201 | - for (i = 0; i < rproc->max_notifyid; i++) { | |
216 | + for (i = 0; i <= rproc->max_notifyid; i++) { | |
202 | 217 | err = sproc->mdev->ops.kick_subscribe(sproc->mdev, i); |
203 | 218 | if (err) { |
204 | 219 | sproc_err(sproc, |
include/linux/remoteproc.h
... | ... | @@ -401,6 +401,9 @@ |
401 | 401 | * @crash_comp: completion used to sync crash handler and the rproc reload |
402 | 402 | * @recovery_disabled: flag that state if recovery was disabled |
403 | 403 | * @max_notifyid: largest allocated notify id. |
404 | + * @table_ptr: pointer to the resource table in effect | |
405 | + * @cached_table: copy of the resource table | |
406 | + * @table_csum: checksum of the resource table | |
404 | 407 | */ |
405 | 408 | struct rproc { |
406 | 409 | struct klist_node node; |
407 | 410 | |
... | ... | @@ -429,9 +432,13 @@ |
429 | 432 | struct completion crash_comp; |
430 | 433 | bool recovery_disabled; |
431 | 434 | int max_notifyid; |
435 | + struct resource_table *table_ptr; | |
436 | + struct resource_table *cached_table; | |
437 | + u32 table_csum; | |
432 | 438 | }; |
433 | 439 | |
434 | 440 | /* we currently support only two vrings per rvdev */ |
441 | + | |
435 | 442 | #define RVDEV_NUM_VRINGS 2 |
436 | 443 | |
437 | 444 | /** |
438 | 445 | |
... | ... | @@ -462,16 +469,14 @@ |
462 | 469 | * @rproc: the rproc handle |
463 | 470 | * @vdev: the virio device |
464 | 471 | * @vring: the vrings for this vdev |
465 | - * @dfeatures: virtio device features | |
466 | - * @gfeatures: virtio guest features | |
472 | + * @rsc_offset: offset of the vdev's resource entry | |
467 | 473 | */ |
468 | 474 | struct rproc_vdev { |
469 | 475 | struct list_head node; |
470 | 476 | struct rproc *rproc; |
471 | 477 | struct virtio_device vdev; |
472 | 478 | struct rproc_vring vring[RVDEV_NUM_VRINGS]; |
473 | - unsigned long dfeatures; | |
474 | - unsigned long gfeatures; | |
479 | + u32 rsc_offset; | |
475 | 480 | }; |
476 | 481 | |
477 | 482 | struct rproc *rproc_alloc(struct device *dev, const char *name, |