Commit | Line | Data |
---|---|---|
7d55524d ORL |
1 | /* |
2 | * nldr.c | |
3 | * | |
4 | * DSP-BIOS Bridge driver support functions for TI OMAP processors. | |
5 | * | |
6 | * DSP/BIOS Bridge dynamic + overlay Node loader. | |
7 | * | |
8 | * Copyright (C) 2005-2006 Texas Instruments, Inc. | |
9 | * | |
10 | * This package is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | * | |
14 | * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
15 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
16 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
17 | */ | |
18 | ||
2094f12d NM |
19 | #include <linux/types.h> |
20 | ||
7d55524d ORL |
21 | #include <dspbridge/host_os.h> |
22 | ||
7d55524d ORL |
23 | #include <dspbridge/dbdefs.h> |
24 | ||
7d55524d ORL |
25 | /* Platform manager */ |
26 | #include <dspbridge/cod.h> | |
27 | #include <dspbridge/dev.h> | |
28 | ||
29 | /* Resource manager */ | |
30 | #include <dspbridge/dbll.h> | |
31 | #include <dspbridge/dbdcd.h> | |
32 | #include <dspbridge/rmm.h> | |
33 | #include <dspbridge/uuidutil.h> | |
34 | ||
35 | #include <dspbridge/nldr.h> | |
d1000921 | 36 | #include <linux/lcm.h> |
7d55524d ORL |
37 | |
38 | /* Name of section containing dynamic load mem */ | |
39 | #define DYNMEMSECT ".dspbridge_mem" | |
40 | ||
41 | /* Name of section containing dependent library information */ | |
42 | #define DEPLIBSECT ".dspbridge_deplibs" | |
43 | ||
44 | /* Max depth of recursion for loading node's dependent libraries */ | |
45 | #define MAXDEPTH 5 | |
46 | ||
47 | /* Max number of persistent libraries kept by a node */ | |
48 | #define MAXLIBS 5 | |
49 | ||
50 | /* | |
51 | * Defines for extracting packed dynamic load memory requirements from two | |
52 | * masks. | |
53 | * These defines must match node.cdb and dynm.cdb | |
54 | * Format of data/code mask is: | |
55 | * uuuuuuuu|fueeeeee|fudddddd|fucccccc| | |
56 | * where | |
57 | * u = unused | |
25985edc LDM |
58 | * cccccc = preferred/required dynamic mem segid for create phase data/code |
59 | * dddddd = preferred/required dynamic mem segid for delete phase data/code | |
60 | * eeeeee = preferred/req. dynamic mem segid for execute phase data/code | |
7d55524d ORL |
61 | * f = flag indicating if memory is preferred or required: |
62 | * f = 1 if required, f = 0 if preferred. | |
63 | * | |
64 | * The 6 bits of the segid are interpreted as follows: | |
65 | * | |
66 | * If the 6th bit (bit 5) is not set, then this specifies a memory segment | |
67 | * between 0 and 31 (a maximum of 32 dynamic loading memory segments). | |
68 | * If the 6th bit (bit 5) is set, segid has the following interpretation: | |
69 | * segid = 32 - Any internal memory segment can be used. | |
70 | * segid = 33 - Any external memory segment can be used. | |
71 | * segid = 63 - Any memory segment can be used (in this case the | |
72 | * required/preferred flag is irrelevant). | |
73 | * | |
74 | */ | |
75 | /* Maximum allowed dynamic loading memory segments */ | |
76 | #define MAXMEMSEGS 32 | |
77 | ||
78 | #define MAXSEGID 3 /* Largest possible (real) segid */ | |
79 | #define MEMINTERNALID 32 /* Segid meaning use internal mem */ | |
80 | #define MEMEXTERNALID 33 /* Segid meaning use external mem */ | |
81 | #define NULLID 63 /* Segid meaning no memory req/pref */ | |
82 | #define FLAGBIT 7 /* 7th bit is pref./req. flag */ | |
83 | #define SEGMASK 0x3f /* Bits 0 - 5 */ | |
84 | ||
85 | #define CREATEBIT 0 /* Create segid starts at bit 0 */ | |
86 | #define DELETEBIT 8 /* Delete segid starts at bit 8 */ | |
87 | #define EXECUTEBIT 16 /* Execute segid starts at bit 16 */ | |
88 | ||
89 | /* | |
90 | * Masks that define memory type. Must match defines in dynm.cdb. | |
91 | */ | |
92 | #define DYNM_CODE 0x2 | |
93 | #define DYNM_DATA 0x4 | |
94 | #define DYNM_CODEDATA (DYNM_CODE | DYNM_DATA) | |
95 | #define DYNM_INTERNAL 0x8 | |
96 | #define DYNM_EXTERNAL 0x10 | |
97 | ||
98 | /* | |
99 | * Defines for packing memory requirement/preference flags for code and | |
100 | * data of each of the node's phases into one mask. | |
101 | * The bit is set if the segid is required for loading code/data of the | |
102 | * given phase. The bit is not set, if the segid is preferred only. | |
103 | * | |
104 | * These defines are also used as indeces into a segid array for the node. | |
105 | * eg node's segid[CREATEDATAFLAGBIT] is the memory segment id that the | |
106 | * create phase data is required or preferred to be loaded into. | |
107 | */ | |
108 | #define CREATEDATAFLAGBIT 0 | |
109 | #define CREATECODEFLAGBIT 1 | |
110 | #define EXECUTEDATAFLAGBIT 2 | |
111 | #define EXECUTECODEFLAGBIT 3 | |
112 | #define DELETEDATAFLAGBIT 4 | |
113 | #define DELETECODEFLAGBIT 5 | |
114 | #define MAXFLAGS 6 | |
115 | ||
7d55524d ORL |
116 | /* |
117 | * These names may be embedded in overlay sections to identify which | |
118 | * node phase the section should be overlayed. | |
119 | */ | |
120 | #define PCREATE "create" | |
121 | #define PDELETE "delete" | |
122 | #define PEXECUTE "execute" | |
123 | ||
bf968b0a AU |
124 | static inline bool is_equal_uuid(struct dsp_uuid *uuid1, |
125 | struct dsp_uuid *uuid2) | |
126 | { | |
127 | return !memcmp(uuid1, uuid2, sizeof(struct dsp_uuid)); | |
128 | } | |
7d55524d ORL |
129 | |
130 | /* | |
131 | * ======== mem_seg_info ======== | |
132 | * Format of dynamic loading memory segment info in coff file. | |
133 | * Must match dynm.h55. | |
134 | */ | |
135 | struct mem_seg_info { | |
136 | u32 segid; /* Dynamic loading memory segment number */ | |
137 | u32 base; | |
138 | u32 len; | |
139 | u32 type; /* Mask of DYNM_CODE, DYNM_INTERNAL, etc. */ | |
140 | }; | |
141 | ||
142 | /* | |
143 | * ======== lib_node ======== | |
144 | * For maintaining a tree of library dependencies. | |
145 | */ | |
146 | struct lib_node { | |
147 | struct dbll_library_obj *lib; /* The library */ | |
148 | u16 dep_libs; /* Number of dependent libraries */ | |
149 | struct lib_node *dep_libs_tree; /* Dependent libraries of lib */ | |
150 | }; | |
151 | ||
152 | /* | |
153 | * ======== ovly_sect ======== | |
154 | * Information needed to overlay a section. | |
155 | */ | |
156 | struct ovly_sect { | |
157 | struct ovly_sect *next_sect; | |
158 | u32 sect_load_addr; /* Load address of section */ | |
159 | u32 sect_run_addr; /* Run address of section */ | |
160 | u32 size; /* Size of section */ | |
161 | u16 page; /* DBL_CODE, DBL_DATA */ | |
162 | }; | |
163 | ||
164 | /* | |
165 | * ======== ovly_node ======== | |
166 | * For maintaining a list of overlay nodes, with sections that need to be | |
167 | * overlayed for each of the nodes phases. | |
168 | */ | |
169 | struct ovly_node { | |
170 | struct dsp_uuid uuid; | |
171 | char *node_name; | |
172 | struct ovly_sect *create_sects_list; | |
173 | struct ovly_sect *delete_sects_list; | |
174 | struct ovly_sect *execute_sects_list; | |
175 | struct ovly_sect *other_sects_list; | |
176 | u16 create_sects; | |
177 | u16 delete_sects; | |
178 | u16 execute_sects; | |
179 | u16 other_sects; | |
180 | u16 create_ref; | |
181 | u16 delete_ref; | |
182 | u16 execute_ref; | |
183 | u16 other_ref; | |
184 | }; | |
185 | ||
186 | /* | |
187 | * ======== nldr_object ======== | |
188 | * Overlay loader object. | |
189 | */ | |
190 | struct nldr_object { | |
085467b8 RS |
191 | struct dev_object *dev_obj; /* Device object */ |
192 | struct dcd_manager *dcd_mgr; /* Proc/Node data manager */ | |
7d55524d ORL |
193 | struct dbll_tar_obj *dbll; /* The DBL loader */ |
194 | struct dbll_library_obj *base_lib; /* Base image library */ | |
195 | struct rmm_target_obj *rmm; /* Remote memory manager for DSP */ | |
196 | struct dbll_fxns ldr_fxns; /* Loader function table */ | |
197 | struct dbll_attrs ldr_attrs; /* attrs to pass to loader functions */ | |
198 | nldr_ovlyfxn ovly_fxn; /* "write" for overlay nodes */ | |
199 | nldr_writefxn write_fxn; /* "write" for dynamic nodes */ | |
200 | struct ovly_node *ovly_table; /* Table of overlay nodes */ | |
201 | u16 ovly_nodes; /* Number of overlay nodes in base */ | |
202 | u16 ovly_nid; /* Index for tracking overlay nodes */ | |
203 | u16 dload_segs; /* Number of dynamic load mem segs */ | |
204 | u32 *seg_table; /* memtypes of dynamic memory segs | |
205 | * indexed by segid | |
206 | */ | |
085467b8 RS |
207 | u16 dsp_mau_size; /* Size of DSP MAU */ |
208 | u16 dsp_word_size; /* Size of DSP word */ | |
7d55524d ORL |
209 | }; |
210 | ||
211 | /* | |
212 | * ======== nldr_nodeobject ======== | |
213 | * Dynamic node object. This object is created when a node is allocated. | |
214 | */ | |
215 | struct nldr_nodeobject { | |
216 | struct nldr_object *nldr_obj; /* Dynamic loader handle */ | |
217 | void *priv_ref; /* Handle to pass to dbl_write_fxn */ | |
218 | struct dsp_uuid uuid; /* Node's UUID */ | |
219 | bool dynamic; /* Dynamically loaded node? */ | |
220 | bool overlay; /* Overlay node? */ | |
dab7f7fe | 221 | bool *phase_split; /* Multiple phase libraries? */ |
7d55524d ORL |
222 | struct lib_node root; /* Library containing node phase */ |
223 | struct lib_node create_lib; /* Library with create phase lib */ | |
224 | struct lib_node execute_lib; /* Library with execute phase lib */ | |
225 | struct lib_node delete_lib; /* Library with delete phase lib */ | |
226 | /* libs remain loaded until Delete */ | |
227 | struct lib_node pers_lib_table[MAXLIBS]; | |
228 | s32 pers_libs; /* Number of persistent libraries */ | |
229 | /* Path in lib dependency tree */ | |
230 | struct dbll_library_obj *lib_path[MAXDEPTH + 1]; | |
231 | enum nldr_phase phase; /* Node phase currently being loaded */ | |
232 | ||
233 | /* | |
234 | * Dynamic loading memory segments for data and code of each phase. | |
235 | */ | |
236 | u16 seg_id[MAXFLAGS]; | |
237 | ||
238 | /* | |
239 | * Mask indicating whether each mem segment specified in seg_id[] | |
240 | * is preferred or required. | |
241 | * For example | |
242 | * if (code_data_flag_mask & (1 << EXECUTEDATAFLAGBIT)) != 0, | |
243 | * then it is required to load execute phase data into the memory | |
244 | * specified by seg_id[EXECUTEDATAFLAGBIT]. | |
245 | */ | |
246 | u32 code_data_flag_mask; | |
247 | }; | |
248 | ||
249 | /* Dynamic loader function table */ | |
250 | static struct dbll_fxns ldr_fxns = { | |
251 | (dbll_close_fxn) dbll_close, | |
252 | (dbll_create_fxn) dbll_create, | |
253 | (dbll_delete_fxn) dbll_delete, | |
254 | (dbll_exit_fxn) dbll_exit, | |
255 | (dbll_get_attrs_fxn) dbll_get_attrs, | |
256 | (dbll_get_addr_fxn) dbll_get_addr, | |
257 | (dbll_get_c_addr_fxn) dbll_get_c_addr, | |
258 | (dbll_get_sect_fxn) dbll_get_sect, | |
259 | (dbll_init_fxn) dbll_init, | |
260 | (dbll_load_fxn) dbll_load, | |
7d55524d ORL |
261 | (dbll_open_fxn) dbll_open, |
262 | (dbll_read_sect_fxn) dbll_read_sect, | |
7d55524d | 263 | (dbll_unload_fxn) dbll_unload, |
7d55524d ORL |
264 | }; |
265 | ||
7d55524d ORL |
266 | static int add_ovly_info(void *handle, struct dbll_sect_info *sect_info, |
267 | u32 addr, u32 bytes); | |
268 | static int add_ovly_node(struct dsp_uuid *uuid_obj, | |
9d7d0a52 | 269 | enum dsp_dcdobjtype obj_type, void *handle); |
7d55524d | 270 | static int add_ovly_sect(struct nldr_object *nldr_obj, |
daa89e6c | 271 | struct ovly_sect **lst, |
13b18c29 | 272 | struct dbll_sect_info *sect_inf, |
a5120278 | 273 | bool *exists, u32 addr, u32 bytes); |
b301c858 | 274 | static s32 fake_ovly_write(void *handle, u32 dsp_address, void *buf, u32 bytes, |
7d55524d ORL |
275 | s32 mtype); |
276 | static void free_sects(struct nldr_object *nldr_obj, | |
277 | struct ovly_sect *phase_sects, u16 alloc_num); | |
278 | static bool get_symbol_value(void *handle, void *parg, void *rmm_handle, | |
0cd343a4 | 279 | char *sym_name, struct dbll_sym_val **sym); |
7d55524d ORL |
280 | static int load_lib(struct nldr_nodeobject *nldr_node_obj, |
281 | struct lib_node *root, struct dsp_uuid uuid, | |
318b5df9 | 282 | bool root_prstnt, |
7d55524d ORL |
283 | struct dbll_library_obj **lib_path, |
284 | enum nldr_phase phase, u16 depth); | |
285 | static int load_ovly(struct nldr_nodeobject *nldr_node_obj, | |
286 | enum nldr_phase phase); | |
c8c1ad8c | 287 | static int remote_alloc(void **ref, u16 mem_sect, u32 size, |
b301c858 | 288 | u32 align, u32 *dsp_address, |
21aaf42e MN |
289 | s32 segmnt_id, |
290 | s32 req, bool reserve); | |
13b18c29 | 291 | static int remote_free(void **ref, u16 space, u32 dsp_address, u32 size, |
7d55524d ORL |
292 | bool reserve); |
293 | ||
294 | static void unload_lib(struct nldr_nodeobject *nldr_node_obj, | |
295 | struct lib_node *root); | |
296 | static void unload_ovly(struct nldr_nodeobject *nldr_node_obj, | |
297 | enum nldr_phase phase); | |
298 | static bool find_in_persistent_lib_array(struct nldr_nodeobject *nldr_node_obj, | |
299 | struct dbll_library_obj *lib); | |
7d55524d ORL |
300 | |
301 | /* | |
302 | * ======== nldr_allocate ======== | |
303 | */ | |
304 | int nldr_allocate(struct nldr_object *nldr_obj, void *priv_ref, | |
9d7d0a52 | 305 | const struct dcd_nodeprops *node_props, |
e6bf74f0 | 306 | struct nldr_nodeobject **nldr_nodeobj, |
9d7d0a52 | 307 | bool *pf_phase_split) |
7d55524d ORL |
308 | { |
309 | struct nldr_nodeobject *nldr_node_obj = NULL; | |
310 | int status = 0; | |
311 | ||
7d55524d | 312 | /* Initialize handle in case of failure */ |
e436d07d | 313 | *nldr_nodeobj = NULL; |
7d55524d ORL |
314 | /* Allocate node object */ |
315 | nldr_node_obj = kzalloc(sizeof(struct nldr_nodeobject), GFP_KERNEL); | |
316 | ||
317 | if (nldr_node_obj == NULL) { | |
318 | status = -ENOMEM; | |
319 | } else { | |
dab7f7fe | 320 | nldr_node_obj->phase_split = pf_phase_split; |
7d55524d ORL |
321 | nldr_node_obj->pers_libs = 0; |
322 | nldr_node_obj->nldr_obj = nldr_obj; | |
323 | nldr_node_obj->priv_ref = priv_ref; | |
324 | /* Save node's UUID. */ | |
325 | nldr_node_obj->uuid = node_props->ndb_props.ui_node_id; | |
326 | /* | |
327 | * Determine if node is a dynamically loaded node from | |
328 | * ndb_props. | |
329 | */ | |
a534f17b | 330 | if (node_props->load_type == NLDR_DYNAMICLOAD) { |
7d55524d ORL |
331 | /* Dynamic node */ |
332 | nldr_node_obj->dynamic = true; | |
333 | /* | |
334 | * Extract memory requirements from ndb_props masks | |
335 | */ | |
336 | /* Create phase */ | |
337 | nldr_node_obj->seg_id[CREATEDATAFLAGBIT] = (u16) | |
dab7f7fe | 338 | (node_props->data_mem_seg_mask >> CREATEBIT) & |
7d55524d ORL |
339 | SEGMASK; |
340 | nldr_node_obj->code_data_flag_mask |= | |
dab7f7fe | 341 | ((node_props->data_mem_seg_mask >> |
7d55524d ORL |
342 | (CREATEBIT + FLAGBIT)) & 1) << CREATEDATAFLAGBIT; |
343 | nldr_node_obj->seg_id[CREATECODEFLAGBIT] = (u16) | |
dab7f7fe | 344 | (node_props->code_mem_seg_mask >> |
7d55524d ORL |
345 | CREATEBIT) & SEGMASK; |
346 | nldr_node_obj->code_data_flag_mask |= | |
dab7f7fe | 347 | ((node_props->code_mem_seg_mask >> |
7d55524d ORL |
348 | (CREATEBIT + FLAGBIT)) & 1) << CREATECODEFLAGBIT; |
349 | /* Execute phase */ | |
350 | nldr_node_obj->seg_id[EXECUTEDATAFLAGBIT] = (u16) | |
dab7f7fe | 351 | (node_props->data_mem_seg_mask >> |
7d55524d ORL |
352 | EXECUTEBIT) & SEGMASK; |
353 | nldr_node_obj->code_data_flag_mask |= | |
dab7f7fe | 354 | ((node_props->data_mem_seg_mask >> |
7d55524d ORL |
355 | (EXECUTEBIT + FLAGBIT)) & 1) << |
356 | EXECUTEDATAFLAGBIT; | |
357 | nldr_node_obj->seg_id[EXECUTECODEFLAGBIT] = (u16) | |
dab7f7fe | 358 | (node_props->code_mem_seg_mask >> |
7d55524d ORL |
359 | EXECUTEBIT) & SEGMASK; |
360 | nldr_node_obj->code_data_flag_mask |= | |
dab7f7fe | 361 | ((node_props->code_mem_seg_mask >> |
7d55524d ORL |
362 | (EXECUTEBIT + FLAGBIT)) & 1) << |
363 | EXECUTECODEFLAGBIT; | |
364 | /* Delete phase */ | |
365 | nldr_node_obj->seg_id[DELETEDATAFLAGBIT] = (u16) | |
dab7f7fe | 366 | (node_props->data_mem_seg_mask >> DELETEBIT) & |
7d55524d ORL |
367 | SEGMASK; |
368 | nldr_node_obj->code_data_flag_mask |= | |
dab7f7fe | 369 | ((node_props->data_mem_seg_mask >> |
7d55524d ORL |
370 | (DELETEBIT + FLAGBIT)) & 1) << DELETEDATAFLAGBIT; |
371 | nldr_node_obj->seg_id[DELETECODEFLAGBIT] = (u16) | |
dab7f7fe | 372 | (node_props->code_mem_seg_mask >> |
7d55524d ORL |
373 | DELETEBIT) & SEGMASK; |
374 | nldr_node_obj->code_data_flag_mask |= | |
dab7f7fe | 375 | ((node_props->code_mem_seg_mask >> |
7d55524d ORL |
376 | (DELETEBIT + FLAGBIT)) & 1) << DELETECODEFLAGBIT; |
377 | } else { | |
378 | /* Non-dynamically loaded nodes are part of the | |
379 | * base image */ | |
380 | nldr_node_obj->root.lib = nldr_obj->base_lib; | |
381 | /* Check for overlay node */ | |
a534f17b | 382 | if (node_props->load_type == NLDR_OVLYLOAD) |
7d55524d ORL |
383 | nldr_node_obj->overlay = true; |
384 | ||
385 | } | |
e436d07d | 386 | *nldr_nodeobj = (struct nldr_nodeobject *)nldr_node_obj; |
7d55524d ORL |
387 | } |
388 | /* Cleanup on failure */ | |
b66e0986 | 389 | if (status && nldr_node_obj) |
7d55524d ORL |
390 | kfree(nldr_node_obj); |
391 | ||
7d55524d ORL |
392 | return status; |
393 | } | |
394 | ||
395 | /* | |
396 | * ======== nldr_create ======== | |
397 | */ | |
e6bf74f0 | 398 | int nldr_create(struct nldr_object **nldr, |
7d55524d | 399 | struct dev_object *hdev_obj, |
9d7d0a52 | 400 | const struct nldr_attrs *pattrs) |
7d55524d ORL |
401 | { |
402 | struct cod_manager *cod_mgr; /* COD manager */ | |
403 | char *psz_coff_buf = NULL; | |
404 | char sz_zl_file[COD_MAXPATHLENGTH]; | |
405 | struct nldr_object *nldr_obj = NULL; | |
406 | struct dbll_attrs save_attrs; | |
407 | struct dbll_attrs new_attrs; | |
408 | dbll_flags flags; | |
409 | u32 ul_entry; | |
410 | u16 dload_segs = 0; | |
411 | struct mem_seg_info *mem_info_obj; | |
412 | u32 ul_len = 0; | |
413 | u32 ul_addr; | |
414 | struct rmm_segment *rmm_segs = NULL; | |
415 | u16 i; | |
416 | int status = 0; | |
7d55524d ORL |
417 | |
418 | /* Allocate dynamic loader object */ | |
419 | nldr_obj = kzalloc(sizeof(struct nldr_object), GFP_KERNEL); | |
420 | if (nldr_obj) { | |
085467b8 | 421 | nldr_obj->dev_obj = hdev_obj; |
7d55524d ORL |
422 | /* warning, lazy status checking alert! */ |
423 | dev_get_cod_mgr(hdev_obj, &cod_mgr); | |
424 | if (cod_mgr) { | |
425 | status = cod_get_loader(cod_mgr, &nldr_obj->dbll); | |
7d55524d | 426 | status = cod_get_base_lib(cod_mgr, &nldr_obj->base_lib); |
7d55524d ORL |
427 | status = |
428 | cod_get_base_name(cod_mgr, sz_zl_file, | |
429 | COD_MAXPATHLENGTH); | |
7d55524d ORL |
430 | } |
431 | status = 0; | |
432 | /* end lazy status checking */ | |
085467b8 RS |
433 | nldr_obj->dsp_mau_size = pattrs->dsp_mau_size; |
434 | nldr_obj->dsp_word_size = pattrs->dsp_word_size; | |
7d55524d ORL |
435 | nldr_obj->ldr_fxns = ldr_fxns; |
436 | if (!(nldr_obj->ldr_fxns.init_fxn())) | |
437 | status = -ENOMEM; | |
438 | ||
439 | } else { | |
440 | status = -ENOMEM; | |
441 | } | |
442 | /* Create the DCD Manager */ | |
a741ea6e | 443 | if (!status) |
085467b8 | 444 | status = dcd_create_manager(NULL, &nldr_obj->dcd_mgr); |
7d55524d ORL |
445 | |
446 | /* Get dynamic loading memory sections from base lib */ | |
a741ea6e | 447 | if (!status) { |
7d55524d ORL |
448 | status = |
449 | nldr_obj->ldr_fxns.get_sect_fxn(nldr_obj->base_lib, | |
450 | DYNMEMSECT, &ul_addr, | |
451 | &ul_len); | |
a741ea6e | 452 | if (!status) { |
7d55524d | 453 | psz_coff_buf = |
085467b8 | 454 | kzalloc(ul_len * nldr_obj->dsp_mau_size, |
7d55524d ORL |
455 | GFP_KERNEL); |
456 | if (!psz_coff_buf) | |
457 | status = -ENOMEM; | |
458 | } else { | |
459 | /* Ok to not have dynamic loading memory */ | |
460 | status = 0; | |
461 | ul_len = 0; | |
462 | dev_dbg(bridge, "%s: failed - no dynamic loading mem " | |
463 | "segments: 0x%x\n", __func__, status); | |
464 | } | |
465 | } | |
a741ea6e | 466 | if (!status && ul_len > 0) { |
7d55524d ORL |
467 | /* Read section containing dynamic load mem segments */ |
468 | status = | |
469 | nldr_obj->ldr_fxns.read_sect_fxn(nldr_obj->base_lib, | |
470 | DYNMEMSECT, psz_coff_buf, | |
471 | ul_len); | |
472 | } | |
a741ea6e | 473 | if (!status && ul_len > 0) { |
7d55524d ORL |
474 | /* Parse memory segment data */ |
475 | dload_segs = (u16) (*((u32 *) psz_coff_buf)); | |
476 | if (dload_segs > MAXMEMSEGS) | |
477 | status = -EBADF; | |
478 | } | |
479 | /* Parse dynamic load memory segments */ | |
a741ea6e | 480 | if (!status && dload_segs > 0) { |
7d55524d ORL |
481 | rmm_segs = kzalloc(sizeof(struct rmm_segment) * dload_segs, |
482 | GFP_KERNEL); | |
483 | nldr_obj->seg_table = | |
484 | kzalloc(sizeof(u32) * dload_segs, GFP_KERNEL); | |
485 | if (rmm_segs == NULL || nldr_obj->seg_table == NULL) { | |
486 | status = -ENOMEM; | |
487 | } else { | |
488 | nldr_obj->dload_segs = dload_segs; | |
489 | mem_info_obj = (struct mem_seg_info *)(psz_coff_buf + | |
490 | sizeof(u32)); | |
491 | for (i = 0; i < dload_segs; i++) { | |
492 | rmm_segs[i].base = (mem_info_obj + i)->base; | |
493 | rmm_segs[i].length = (mem_info_obj + i)->len; | |
494 | rmm_segs[i].space = 0; | |
495 | nldr_obj->seg_table[i] = | |
496 | (mem_info_obj + i)->type; | |
497 | dev_dbg(bridge, | |
498 | "(proc) DLL MEMSEGMENT: %d, " | |
499 | "Base: 0x%x, Length: 0x%x\n", i, | |
500 | rmm_segs[i].base, rmm_segs[i].length); | |
501 | } | |
502 | } | |
503 | } | |
504 | /* Create Remote memory manager */ | |
a741ea6e | 505 | if (!status) |
7d55524d ORL |
506 | status = rmm_create(&nldr_obj->rmm, rmm_segs, dload_segs); |
507 | ||
a741ea6e | 508 | if (!status) { |
7d55524d ORL |
509 | /* set the alloc, free, write functions for loader */ |
510 | nldr_obj->ldr_fxns.get_attrs_fxn(nldr_obj->dbll, &save_attrs); | |
511 | new_attrs = save_attrs; | |
512 | new_attrs.alloc = (dbll_alloc_fxn) remote_alloc; | |
513 | new_attrs.free = (dbll_free_fxn) remote_free; | |
514 | new_attrs.sym_lookup = (dbll_sym_lookup) get_symbol_value; | |
515 | new_attrs.sym_handle = nldr_obj; | |
dab7f7fe | 516 | new_attrs.write = (dbll_write_fxn) pattrs->write; |
09f13304 | 517 | nldr_obj->ovly_fxn = pattrs->ovly; |
dab7f7fe | 518 | nldr_obj->write_fxn = pattrs->write; |
7d55524d ORL |
519 | nldr_obj->ldr_attrs = new_attrs; |
520 | } | |
521 | kfree(rmm_segs); | |
522 | ||
523 | kfree(psz_coff_buf); | |
524 | ||
525 | /* Get overlay nodes */ | |
a741ea6e | 526 | if (!status) { |
7d55524d ORL |
527 | status = |
528 | cod_get_base_name(cod_mgr, sz_zl_file, COD_MAXPATHLENGTH); | |
529 | /* lazy check */ | |
7d55524d ORL |
530 | /* First count number of overlay nodes */ |
531 | status = | |
085467b8 | 532 | dcd_get_objects(nldr_obj->dcd_mgr, sz_zl_file, |
7d55524d ORL |
533 | add_ovly_node, (void *)nldr_obj); |
534 | /* Now build table of overlay nodes */ | |
a741ea6e | 535 | if (!status && nldr_obj->ovly_nodes > 0) { |
7d55524d ORL |
536 | /* Allocate table for overlay nodes */ |
537 | nldr_obj->ovly_table = | |
538 | kzalloc(sizeof(struct ovly_node) * | |
539 | nldr_obj->ovly_nodes, GFP_KERNEL); | |
540 | /* Put overlay nodes in the table */ | |
541 | nldr_obj->ovly_nid = 0; | |
085467b8 | 542 | status = dcd_get_objects(nldr_obj->dcd_mgr, sz_zl_file, |
7d55524d ORL |
543 | add_ovly_node, |
544 | (void *)nldr_obj); | |
545 | } | |
546 | } | |
547 | /* Do a fake reload of the base image to get overlay section info */ | |
a741ea6e | 548 | if (!status && nldr_obj->ovly_nodes > 0) { |
7d55524d ORL |
549 | save_attrs.write = fake_ovly_write; |
550 | save_attrs.log_write = add_ovly_info; | |
551 | save_attrs.log_write_handle = nldr_obj; | |
552 | flags = DBLL_CODE | DBLL_DATA | DBLL_SYMB; | |
553 | status = nldr_obj->ldr_fxns.load_fxn(nldr_obj->base_lib, flags, | |
554 | &save_attrs, &ul_entry); | |
555 | } | |
a741ea6e | 556 | if (!status) { |
daa89e6c | 557 | *nldr = (struct nldr_object *)nldr_obj; |
7d55524d ORL |
558 | } else { |
559 | if (nldr_obj) | |
560 | nldr_delete((struct nldr_object *)nldr_obj); | |
561 | ||
daa89e6c | 562 | *nldr = NULL; |
7d55524d ORL |
563 | } |
564 | /* FIXME:Temp. Fix. Must be removed */ | |
7d55524d ORL |
565 | return status; |
566 | } | |
567 | ||
568 | /* | |
569 | * ======== nldr_delete ======== | |
570 | */ | |
571 | void nldr_delete(struct nldr_object *nldr_obj) | |
572 | { | |
573 | struct ovly_sect *ovly_section; | |
574 | struct ovly_sect *next; | |
575 | u16 i; | |
7d55524d ORL |
576 | |
577 | nldr_obj->ldr_fxns.exit_fxn(); | |
578 | if (nldr_obj->rmm) | |
579 | rmm_delete(nldr_obj->rmm); | |
580 | ||
581 | kfree(nldr_obj->seg_table); | |
582 | ||
085467b8 RS |
583 | if (nldr_obj->dcd_mgr) |
584 | dcd_destroy_manager(nldr_obj->dcd_mgr); | |
7d55524d ORL |
585 | |
586 | /* Free overlay node information */ | |
587 | if (nldr_obj->ovly_table) { | |
588 | for (i = 0; i < nldr_obj->ovly_nodes; i++) { | |
589 | ovly_section = | |
590 | nldr_obj->ovly_table[i].create_sects_list; | |
591 | while (ovly_section) { | |
592 | next = ovly_section->next_sect; | |
593 | kfree(ovly_section); | |
594 | ovly_section = next; | |
595 | } | |
596 | ovly_section = | |
597 | nldr_obj->ovly_table[i].delete_sects_list; | |
598 | while (ovly_section) { | |
599 | next = ovly_section->next_sect; | |
600 | kfree(ovly_section); | |
601 | ovly_section = next; | |
602 | } | |
603 | ovly_section = | |
604 | nldr_obj->ovly_table[i].execute_sects_list; | |
605 | while (ovly_section) { | |
606 | next = ovly_section->next_sect; | |
607 | kfree(ovly_section); | |
608 | ovly_section = next; | |
609 | } | |
610 | ovly_section = nldr_obj->ovly_table[i].other_sects_list; | |
611 | while (ovly_section) { | |
612 | next = ovly_section->next_sect; | |
613 | kfree(ovly_section); | |
614 | ovly_section = next; | |
615 | } | |
616 | } | |
617 | kfree(nldr_obj->ovly_table); | |
618 | } | |
619 | kfree(nldr_obj); | |
620 | } | |
621 | ||
7d55524d ORL |
622 | /* |
623 | * ======== nldr_get_fxn_addr ======== | |
624 | */ | |
625 | int nldr_get_fxn_addr(struct nldr_nodeobject *nldr_node_obj, | |
383b8345 | 626 | char *str_fxn, u32 * addr) |
7d55524d ORL |
627 | { |
628 | struct dbll_sym_val *dbll_sym; | |
629 | struct nldr_object *nldr_obj; | |
630 | int status = 0; | |
631 | bool status1 = false; | |
632 | s32 i = 0; | |
633 | struct lib_node root = { NULL, 0, NULL }; | |
7d55524d ORL |
634 | |
635 | nldr_obj = nldr_node_obj->nldr_obj; | |
636 | /* Called from node_create(), node_delete(), or node_run(). */ | |
dab7f7fe | 637 | if (nldr_node_obj->dynamic && *nldr_node_obj->phase_split) { |
7d55524d ORL |
638 | switch (nldr_node_obj->phase) { |
639 | case NLDR_CREATE: | |
640 | root = nldr_node_obj->create_lib; | |
641 | break; | |
642 | case NLDR_EXECUTE: | |
643 | root = nldr_node_obj->execute_lib; | |
644 | break; | |
645 | case NLDR_DELETE: | |
646 | root = nldr_node_obj->delete_lib; | |
647 | break; | |
648 | default: | |
7d55524d ORL |
649 | break; |
650 | } | |
651 | } else { | |
652 | /* for Overlay nodes or non-split Dynamic nodes */ | |
653 | root = nldr_node_obj->root; | |
654 | } | |
655 | status1 = | |
383b8345 | 656 | nldr_obj->ldr_fxns.get_c_addr_fxn(root.lib, str_fxn, &dbll_sym); |
7d55524d ORL |
657 | if (!status1) |
658 | status1 = | |
383b8345 | 659 | nldr_obj->ldr_fxns.get_addr_fxn(root.lib, str_fxn, |
7d55524d ORL |
660 | &dbll_sym); |
661 | ||
662 | /* If symbol not found, check dependent libraries */ | |
663 | if (!status1) { | |
664 | for (i = 0; i < root.dep_libs; i++) { | |
665 | status1 = | |
666 | nldr_obj->ldr_fxns.get_addr_fxn(root.dep_libs_tree | |
383b8345 | 667 | [i].lib, str_fxn, |
7d55524d ORL |
668 | &dbll_sym); |
669 | if (!status1) { | |
670 | status1 = | |
671 | nldr_obj->ldr_fxns. | |
672 | get_c_addr_fxn(root.dep_libs_tree[i].lib, | |
383b8345 | 673 | str_fxn, &dbll_sym); |
7d55524d ORL |
674 | } |
675 | if (status1) { | |
676 | /* Symbol found */ | |
677 | break; | |
678 | } | |
679 | } | |
680 | } | |
681 | /* Check persistent libraries */ | |
682 | if (!status1) { | |
683 | for (i = 0; i < nldr_node_obj->pers_libs; i++) { | |
684 | status1 = | |
685 | nldr_obj->ldr_fxns. | |
686 | get_addr_fxn(nldr_node_obj->pers_lib_table[i].lib, | |
383b8345 | 687 | str_fxn, &dbll_sym); |
7d55524d ORL |
688 | if (!status1) { |
689 | status1 = | |
690 | nldr_obj->ldr_fxns. | |
691 | get_c_addr_fxn(nldr_node_obj->pers_lib_table | |
383b8345 | 692 | [i].lib, str_fxn, &dbll_sym); |
7d55524d ORL |
693 | } |
694 | if (status1) { | |
695 | /* Symbol found */ | |
696 | break; | |
697 | } | |
698 | } | |
699 | } | |
700 | ||
701 | if (status1) | |
383b8345 | 702 | *addr = dbll_sym->value; |
7d55524d ORL |
703 | else |
704 | status = -ESPIPE; | |
705 | ||
706 | return status; | |
707 | } | |
708 | ||
709 | /* | |
710 | * ======== nldr_get_rmm_manager ======== | |
711 | * Given a NLDR object, retrieve RMM Manager Handle | |
712 | */ | |
e6890692 | 713 | int nldr_get_rmm_manager(struct nldr_object *nldr, |
e6bf74f0 | 714 | struct rmm_target_obj **rmm_mgr) |
7d55524d ORL |
715 | { |
716 | int status = 0; | |
e6890692 | 717 | struct nldr_object *nldr_obj = nldr; |
7d55524d | 718 | |
e6890692 | 719 | if (nldr) { |
daa89e6c | 720 | *rmm_mgr = nldr_obj->rmm; |
7d55524d | 721 | } else { |
daa89e6c | 722 | *rmm_mgr = NULL; |
7d55524d ORL |
723 | status = -EFAULT; |
724 | } | |
725 | ||
7d55524d ORL |
726 | return status; |
727 | } | |
728 | ||
7d55524d ORL |
729 | /* |
730 | * ======== nldr_load ======== | |
731 | */ | |
732 | int nldr_load(struct nldr_nodeobject *nldr_node_obj, | |
733 | enum nldr_phase phase) | |
734 | { | |
735 | struct nldr_object *nldr_obj; | |
736 | struct dsp_uuid lib_uuid; | |
737 | int status = 0; | |
738 | ||
7d55524d ORL |
739 | nldr_obj = nldr_node_obj->nldr_obj; |
740 | ||
741 | if (nldr_node_obj->dynamic) { | |
742 | nldr_node_obj->phase = phase; | |
743 | ||
744 | lib_uuid = nldr_node_obj->uuid; | |
745 | ||
746 | /* At this point, we may not know if node is split into | |
747 | * different libraries. So we'll go ahead and load the | |
748 | * library, and then save the pointer to the appropriate | |
749 | * location after we know. */ | |
750 | ||
751 | status = | |
752 | load_lib(nldr_node_obj, &nldr_node_obj->root, lib_uuid, | |
753 | false, nldr_node_obj->lib_path, phase, 0); | |
754 | ||
a741ea6e | 755 | if (!status) { |
dab7f7fe | 756 | if (*nldr_node_obj->phase_split) { |
7d55524d ORL |
757 | switch (phase) { |
758 | case NLDR_CREATE: | |
759 | nldr_node_obj->create_lib = | |
760 | nldr_node_obj->root; | |
761 | break; | |
762 | ||
763 | case NLDR_EXECUTE: | |
764 | nldr_node_obj->execute_lib = | |
765 | nldr_node_obj->root; | |
766 | break; | |
767 | ||
768 | case NLDR_DELETE: | |
769 | nldr_node_obj->delete_lib = | |
770 | nldr_node_obj->root; | |
771 | break; | |
772 | ||
773 | default: | |
7d55524d ORL |
774 | break; |
775 | } | |
776 | } | |
777 | } | |
778 | } else { | |
779 | if (nldr_node_obj->overlay) | |
780 | status = load_ovly(nldr_node_obj, phase); | |
781 | ||
782 | } | |
783 | ||
784 | return status; | |
785 | } | |
786 | ||
787 | /* | |
788 | * ======== nldr_unload ======== | |
789 | */ | |
790 | int nldr_unload(struct nldr_nodeobject *nldr_node_obj, | |
791 | enum nldr_phase phase) | |
792 | { | |
793 | int status = 0; | |
794 | struct lib_node *root_lib = NULL; | |
795 | s32 i = 0; | |
796 | ||
7d55524d ORL |
797 | if (nldr_node_obj != NULL) { |
798 | if (nldr_node_obj->dynamic) { | |
dab7f7fe | 799 | if (*nldr_node_obj->phase_split) { |
7d55524d ORL |
800 | switch (phase) { |
801 | case NLDR_CREATE: | |
802 | root_lib = &nldr_node_obj->create_lib; | |
803 | break; | |
804 | case NLDR_EXECUTE: | |
805 | root_lib = &nldr_node_obj->execute_lib; | |
806 | break; | |
807 | case NLDR_DELETE: | |
808 | root_lib = &nldr_node_obj->delete_lib; | |
809 | /* Unload persistent libraries */ | |
810 | for (i = 0; | |
811 | i < nldr_node_obj->pers_libs; | |
812 | i++) { | |
813 | unload_lib(nldr_node_obj, | |
814 | &nldr_node_obj-> | |
815 | pers_lib_table[i]); | |
816 | } | |
817 | nldr_node_obj->pers_libs = 0; | |
818 | break; | |
819 | default: | |
7d55524d ORL |
820 | break; |
821 | } | |
822 | } else { | |
823 | /* Unload main library */ | |
824 | root_lib = &nldr_node_obj->root; | |
825 | } | |
826 | if (root_lib) | |
827 | unload_lib(nldr_node_obj, root_lib); | |
828 | } else { | |
829 | if (nldr_node_obj->overlay) | |
830 | unload_ovly(nldr_node_obj, phase); | |
831 | ||
832 | } | |
833 | } | |
834 | return status; | |
835 | } | |
836 | ||
837 | /* | |
838 | * ======== add_ovly_info ======== | |
839 | */ | |
840 | static int add_ovly_info(void *handle, struct dbll_sect_info *sect_info, | |
841 | u32 addr, u32 bytes) | |
842 | { | |
843 | char *node_name; | |
844 | char *sect_name = (char *)sect_info->name; | |
845 | bool sect_exists = false; | |
846 | char seps = ':'; | |
847 | char *pch; | |
848 | u16 i; | |
849 | struct nldr_object *nldr_obj = (struct nldr_object *)handle; | |
850 | int status = 0; | |
851 | ||
852 | /* Is this an overlay section (load address != run address)? */ | |
853 | if (sect_info->sect_load_addr == sect_info->sect_run_addr) | |
854 | goto func_end; | |
855 | ||
856 | /* Find the node it belongs to */ | |
857 | for (i = 0; i < nldr_obj->ovly_nodes; i++) { | |
858 | node_name = nldr_obj->ovly_table[i].node_name; | |
7d55524d ORL |
859 | if (strncmp(node_name, sect_name + 1, strlen(node_name)) == 0) { |
860 | /* Found the node */ | |
861 | break; | |
862 | } | |
863 | } | |
864 | if (!(i < nldr_obj->ovly_nodes)) | |
865 | goto func_end; | |
866 | ||
867 | /* Determine which phase this section belongs to */ | |
868 | for (pch = sect_name + 1; *pch && *pch != seps; pch++) | |
859171ca | 869 | ; |
7d55524d ORL |
870 | |
871 | if (*pch) { | |
872 | pch++; /* Skip over the ':' */ | |
873 | if (strncmp(pch, PCREATE, strlen(PCREATE)) == 0) { | |
874 | status = | |
875 | add_ovly_sect(nldr_obj, | |
876 | &nldr_obj-> | |
877 | ovly_table[i].create_sects_list, | |
878 | sect_info, §_exists, addr, bytes); | |
a741ea6e | 879 | if (!status && !sect_exists) |
7d55524d ORL |
880 | nldr_obj->ovly_table[i].create_sects++; |
881 | ||
882 | } else if (strncmp(pch, PDELETE, strlen(PDELETE)) == 0) { | |
883 | status = | |
884 | add_ovly_sect(nldr_obj, | |
885 | &nldr_obj-> | |
886 | ovly_table[i].delete_sects_list, | |
887 | sect_info, §_exists, addr, bytes); | |
a741ea6e | 888 | if (!status && !sect_exists) |
7d55524d ORL |
889 | nldr_obj->ovly_table[i].delete_sects++; |
890 | ||
891 | } else if (strncmp(pch, PEXECUTE, strlen(PEXECUTE)) == 0) { | |
892 | status = | |
893 | add_ovly_sect(nldr_obj, | |
894 | &nldr_obj-> | |
895 | ovly_table[i].execute_sects_list, | |
896 | sect_info, §_exists, addr, bytes); | |
a741ea6e | 897 | if (!status && !sect_exists) |
7d55524d ORL |
898 | nldr_obj->ovly_table[i].execute_sects++; |
899 | ||
900 | } else { | |
0142919c | 901 | /* Put in "other" sections */ |
7d55524d ORL |
902 | status = |
903 | add_ovly_sect(nldr_obj, | |
904 | &nldr_obj-> | |
905 | ovly_table[i].other_sects_list, | |
906 | sect_info, §_exists, addr, bytes); | |
a741ea6e | 907 | if (!status && !sect_exists) |
7d55524d ORL |
908 | nldr_obj->ovly_table[i].other_sects++; |
909 | ||
910 | } | |
911 | } | |
912 | func_end: | |
913 | return status; | |
914 | } | |
915 | ||
916 | /* | |
917 | * ======== add_ovly_node ========= | |
918 | * Callback function passed to dcd_get_objects. | |
919 | */ | |
920 | static int add_ovly_node(struct dsp_uuid *uuid_obj, | |
9d7d0a52 | 921 | enum dsp_dcdobjtype obj_type, void *handle) |
7d55524d ORL |
922 | { |
923 | struct nldr_object *nldr_obj = (struct nldr_object *)handle; | |
924 | char *node_name = NULL; | |
925 | char *pbuf = NULL; | |
926 | u32 len; | |
927 | struct dcd_genericobj obj_def; | |
928 | int status = 0; | |
929 | ||
930 | if (obj_type != DSP_DCDNODETYPE) | |
931 | goto func_end; | |
932 | ||
933 | status = | |
085467b8 | 934 | dcd_get_object_def(nldr_obj->dcd_mgr, uuid_obj, obj_type, |
7d55524d | 935 | &obj_def); |
b66e0986 | 936 | if (status) |
7d55524d ORL |
937 | goto func_end; |
938 | ||
939 | /* If overlay node, add to the list */ | |
a534f17b | 940 | if (obj_def.obj_data.node_obj.load_type == NLDR_OVLYLOAD) { |
7d55524d ORL |
941 | if (nldr_obj->ovly_table == NULL) { |
942 | nldr_obj->ovly_nodes++; | |
943 | } else { | |
944 | /* Add node to table */ | |
945 | nldr_obj->ovly_table[nldr_obj->ovly_nid].uuid = | |
946 | *uuid_obj; | |
7d55524d ORL |
947 | len = |
948 | strlen(obj_def.obj_data.node_obj.ndb_props.ac_name); | |
949 | node_name = obj_def.obj_data.node_obj.ndb_props.ac_name; | |
950 | pbuf = kzalloc(len + 1, GFP_KERNEL); | |
951 | if (pbuf == NULL) { | |
952 | status = -ENOMEM; | |
953 | } else { | |
954 | strncpy(pbuf, node_name, len); | |
955 | nldr_obj->ovly_table[nldr_obj->ovly_nid]. | |
956 | node_name = pbuf; | |
957 | nldr_obj->ovly_nid++; | |
958 | } | |
959 | } | |
960 | } | |
961 | /* These were allocated in dcd_get_object_def */ | |
ee4317f7 | 962 | kfree(obj_def.obj_data.node_obj.str_create_phase_fxn); |
7d55524d | 963 | |
ee4317f7 | 964 | kfree(obj_def.obj_data.node_obj.str_execute_phase_fxn); |
7d55524d | 965 | |
ee4317f7 | 966 | kfree(obj_def.obj_data.node_obj.str_delete_phase_fxn); |
7d55524d | 967 | |
ee4317f7 | 968 | kfree(obj_def.obj_data.node_obj.str_i_alg_name); |
7d55524d ORL |
969 | |
970 | func_end: | |
971 | return status; | |
972 | } | |
973 | ||
974 | /* | |
975 | * ======== add_ovly_sect ======== | |
976 | */ | |
977 | static int add_ovly_sect(struct nldr_object *nldr_obj, | |
daa89e6c | 978 | struct ovly_sect **lst, |
13b18c29 | 979 | struct dbll_sect_info *sect_inf, |
a5120278 | 980 | bool *exists, u32 addr, u32 bytes) |
7d55524d ORL |
981 | { |
982 | struct ovly_sect *new_sect = NULL; | |
983 | struct ovly_sect *last_sect; | |
984 | struct ovly_sect *ovly_section; | |
985 | int status = 0; | |
986 | ||
daa89e6c | 987 | ovly_section = last_sect = *lst; |
a5120278 | 988 | *exists = false; |
7d55524d ORL |
989 | while (ovly_section) { |
990 | /* | |
991 | * Make sure section has not already been added. Multiple | |
992 | * 'write' calls may be made to load the section. | |
993 | */ | |
994 | if (ovly_section->sect_load_addr == addr) { | |
995 | /* Already added */ | |
a5120278 | 996 | *exists = true; |
7d55524d ORL |
997 | break; |
998 | } | |
999 | last_sect = ovly_section; | |
1000 | ovly_section = ovly_section->next_sect; | |
1001 | } | |
1002 | ||
1003 | if (!ovly_section) { | |
1004 | /* New section */ | |
1005 | new_sect = kzalloc(sizeof(struct ovly_sect), GFP_KERNEL); | |
1006 | if (new_sect == NULL) { | |
1007 | status = -ENOMEM; | |
1008 | } else { | |
1009 | new_sect->sect_load_addr = addr; | |
13b18c29 RS |
1010 | new_sect->sect_run_addr = sect_inf->sect_run_addr + |
1011 | (addr - sect_inf->sect_load_addr); | |
7d55524d | 1012 | new_sect->size = bytes; |
13b18c29 | 1013 | new_sect->page = sect_inf->type; |
7d55524d ORL |
1014 | } |
1015 | ||
1016 | /* Add to the list */ | |
a741ea6e | 1017 | if (!status) { |
daa89e6c | 1018 | if (*lst == NULL) { |
7d55524d | 1019 | /* First in the list */ |
daa89e6c | 1020 | *lst = new_sect; |
7d55524d ORL |
1021 | } else { |
1022 | last_sect->next_sect = new_sect; | |
1023 | } | |
1024 | } | |
1025 | } | |
1026 | ||
1027 | return status; | |
1028 | } | |
1029 | ||
1030 | /* | |
1031 | * ======== fake_ovly_write ======== | |
1032 | */ | |
b301c858 | 1033 | static s32 fake_ovly_write(void *handle, u32 dsp_address, void *buf, u32 bytes, |
7d55524d ORL |
1034 | s32 mtype) |
1035 | { | |
1036 | return (s32) bytes; | |
1037 | } | |
1038 | ||
1039 | /* | |
1040 | * ======== free_sects ======== | |
1041 | */ | |
1042 | static void free_sects(struct nldr_object *nldr_obj, | |
1043 | struct ovly_sect *phase_sects, u16 alloc_num) | |
1044 | { | |
1045 | struct ovly_sect *ovly_section = phase_sects; | |
1046 | u16 i = 0; | |
1047 | bool ret; | |
1048 | ||
1049 | while (ovly_section && i < alloc_num) { | |
1050 | /* 'Deallocate' */ | |
1051 | /* segid - page not supported yet */ | |
1052 | /* Reserved memory */ | |
1053 | ret = | |
1054 | rmm_free(nldr_obj->rmm, 0, ovly_section->sect_run_addr, | |
1055 | ovly_section->size, true); | |
7d55524d ORL |
1056 | ovly_section = ovly_section->next_sect; |
1057 | i++; | |
1058 | } | |
1059 | } | |
1060 | ||
1061 | /* | |
1062 | * ======== get_symbol_value ======== | |
1063 | * Find symbol in library's base image. If not there, check dependent | |
1064 | * libraries. | |
1065 | */ | |
1066 | static bool get_symbol_value(void *handle, void *parg, void *rmm_handle, | |
c8c1ad8c | 1067 | char *sym_name, struct dbll_sym_val **sym) |
7d55524d ORL |
1068 | { |
1069 | struct nldr_object *nldr_obj = (struct nldr_object *)handle; | |
1070 | struct nldr_nodeobject *nldr_node_obj = | |
1071 | (struct nldr_nodeobject *)rmm_handle; | |
1072 | struct lib_node *root = (struct lib_node *)parg; | |
1073 | u16 i; | |
1074 | bool status = false; | |
1075 | ||
1076 | /* check the base image */ | |
c8c1ad8c RS |
1077 | status = nldr_obj->ldr_fxns.get_addr_fxn(nldr_obj->base_lib, |
1078 | sym_name, sym); | |
7d55524d ORL |
1079 | if (!status) |
1080 | status = | |
c8c1ad8c RS |
1081 | nldr_obj->ldr_fxns.get_c_addr_fxn(nldr_obj->base_lib, |
1082 | sym_name, sym); | |
7d55524d ORL |
1083 | |
1084 | /* | |
1085 | * Check in root lib itself. If the library consists of | |
1086 | * multiple object files linked together, some symbols in the | |
1087 | * library may need to be resolved. | |
1088 | */ | |
1089 | if (!status) { | |
c8c1ad8c RS |
1090 | status = nldr_obj->ldr_fxns.get_addr_fxn(root->lib, sym_name, |
1091 | sym); | |
7d55524d ORL |
1092 | if (!status) { |
1093 | status = | |
c8c1ad8c RS |
1094 | nldr_obj->ldr_fxns.get_c_addr_fxn(root->lib, |
1095 | sym_name, sym); | |
7d55524d ORL |
1096 | } |
1097 | } | |
1098 | ||
1099 | /* | |
1100 | * Check in root lib's dependent libraries, but not dependent | |
1101 | * libraries' dependents. | |
1102 | */ | |
1103 | if (!status) { | |
1104 | for (i = 0; i < root->dep_libs; i++) { | |
1105 | status = | |
c8c1ad8c RS |
1106 | nldr_obj->ldr_fxns.get_addr_fxn(root-> |
1107 | dep_libs_tree | |
1108 | [i].lib, | |
1109 | sym_name, sym); | |
7d55524d ORL |
1110 | if (!status) { |
1111 | status = | |
1112 | nldr_obj->ldr_fxns. | |
1113 | get_c_addr_fxn(root->dep_libs_tree[i].lib, | |
c8c1ad8c | 1114 | sym_name, sym); |
7d55524d ORL |
1115 | } |
1116 | if (status) { | |
1117 | /* Symbol found */ | |
1118 | break; | |
1119 | } | |
1120 | } | |
1121 | } | |
1122 | /* | |
1123 | * Check in persistent libraries | |
1124 | */ | |
1125 | if (!status) { | |
1126 | for (i = 0; i < nldr_node_obj->pers_libs; i++) { | |
1127 | status = | |
1128 | nldr_obj->ldr_fxns. | |
1129 | get_addr_fxn(nldr_node_obj->pers_lib_table[i].lib, | |
c8c1ad8c | 1130 | sym_name, sym); |
7d55524d ORL |
1131 | if (!status) { |
1132 | status = nldr_obj->ldr_fxns.get_c_addr_fxn | |
c8c1ad8c RS |
1133 | (nldr_node_obj->pers_lib_table[i].lib, |
1134 | sym_name, sym); | |
7d55524d ORL |
1135 | } |
1136 | if (status) { | |
1137 | /* Symbol found */ | |
1138 | break; | |
1139 | } | |
1140 | } | |
1141 | } | |
1142 | ||
1143 | return status; | |
1144 | } | |
1145 | ||
1146 | /* | |
1147 | * ======== load_lib ======== | |
1148 | * Recursively load library and all its dependent libraries. The library | |
1149 | * we're loading is specified by a uuid. | |
1150 | */ | |
1151 | static int load_lib(struct nldr_nodeobject *nldr_node_obj, | |
1152 | struct lib_node *root, struct dsp_uuid uuid, | |
318b5df9 | 1153 | bool root_prstnt, |
7d55524d ORL |
1154 | struct dbll_library_obj **lib_path, |
1155 | enum nldr_phase phase, u16 depth) | |
1156 | { | |
1157 | struct nldr_object *nldr_obj = nldr_node_obj->nldr_obj; | |
1158 | u16 nd_libs = 0; /* Number of dependent libraries */ | |
1159 | u16 np_libs = 0; /* Number of persistent libraries */ | |
1160 | u16 nd_libs_loaded = 0; /* Number of dep. libraries loaded */ | |
1161 | u16 i; | |
1162 | u32 entry; | |
1163 | u32 dw_buf_size = NLDR_MAXPATHLENGTH; | |
1164 | dbll_flags flags = DBLL_SYMB | DBLL_CODE | DBLL_DATA | DBLL_DYNAMIC; | |
1165 | struct dbll_attrs new_attrs; | |
1166 | char *psz_file_name = NULL; | |
1167 | struct dsp_uuid *dep_lib_uui_ds = NULL; | |
1168 | bool *persistent_dep_libs = NULL; | |
1169 | int status = 0; | |
1170 | bool lib_status = false; | |
1171 | struct lib_node *dep_lib; | |
1172 | ||
1173 | if (depth > MAXDEPTH) { | |
1174 | /* Error */ | |
7d55524d ORL |
1175 | } |
1176 | root->lib = NULL; | |
1177 | /* Allocate a buffer for library file name of size DBL_MAXPATHLENGTH */ | |
1178 | psz_file_name = kzalloc(DBLL_MAXPATHLENGTH, GFP_KERNEL); | |
1179 | if (psz_file_name == NULL) | |
1180 | status = -ENOMEM; | |
1181 | ||
a741ea6e | 1182 | if (!status) { |
7d55524d ORL |
1183 | /* Get the name of the library */ |
1184 | if (depth == 0) { | |
1185 | status = | |
1186 | dcd_get_library_name(nldr_node_obj->nldr_obj-> | |
085467b8 | 1187 | dcd_mgr, &uuid, psz_file_name, |
7d55524d | 1188 | &dw_buf_size, phase, |
dab7f7fe | 1189 | nldr_node_obj->phase_split); |
7d55524d ORL |
1190 | } else { |
1191 | /* Dependent libraries are registered with a phase */ | |
1192 | status = | |
1193 | dcd_get_library_name(nldr_node_obj->nldr_obj-> | |
085467b8 | 1194 | dcd_mgr, &uuid, psz_file_name, |
7d55524d ORL |
1195 | &dw_buf_size, NLDR_NOPHASE, |
1196 | NULL); | |
1197 | } | |
1198 | } | |
a741ea6e | 1199 | if (!status) { |
7d55524d ORL |
1200 | /* Open the library, don't load symbols */ |
1201 | status = | |
1202 | nldr_obj->ldr_fxns.open_fxn(nldr_obj->dbll, psz_file_name, | |
1203 | DBLL_NOLOAD, &root->lib); | |
1204 | } | |
1205 | /* Done with file name */ | |
1206 | kfree(psz_file_name); | |
1207 | ||
1208 | /* Check to see if library not already loaded */ | |
a741ea6e | 1209 | if (!status && root_prstnt) { |
7d55524d ORL |
1210 | lib_status = |
1211 | find_in_persistent_lib_array(nldr_node_obj, root->lib); | |
1212 | /* Close library */ | |
1213 | if (lib_status) { | |
1214 | nldr_obj->ldr_fxns.close_fxn(root->lib); | |
1215 | return 0; | |
1216 | } | |
1217 | } | |
a741ea6e | 1218 | if (!status) { |
7d55524d ORL |
1219 | /* Check for circular dependencies. */ |
1220 | for (i = 0; i < depth; i++) { | |
1221 | if (root->lib == lib_path[i]) { | |
1222 | /* This condition could be checked by a | |
1223 | * tool at build time. */ | |
1224 | status = -EILSEQ; | |
1225 | } | |
1226 | } | |
1227 | } | |
a741ea6e | 1228 | if (!status) { |
7d55524d ORL |
1229 | /* Add library to current path in dependency tree */ |
1230 | lib_path[depth] = root->lib; | |
1231 | depth++; | |
1232 | /* Get number of dependent libraries */ | |
1233 | status = | |
085467b8 | 1234 | dcd_get_num_dep_libs(nldr_node_obj->nldr_obj->dcd_mgr, |
7d55524d ORL |
1235 | &uuid, &nd_libs, &np_libs, phase); |
1236 | } | |
a741ea6e | 1237 | if (!status) { |
dab7f7fe | 1238 | if (!(*nldr_node_obj->phase_split)) |
7d55524d ORL |
1239 | np_libs = 0; |
1240 | ||
1241 | /* nd_libs = #of dependent libraries */ | |
1242 | root->dep_libs = nd_libs - np_libs; | |
1243 | if (nd_libs > 0) { | |
1244 | dep_lib_uui_ds = kzalloc(sizeof(struct dsp_uuid) * | |
1245 | nd_libs, GFP_KERNEL); | |
1246 | persistent_dep_libs = | |
1247 | kzalloc(sizeof(bool) * nd_libs, GFP_KERNEL); | |
1248 | if (!dep_lib_uui_ds || !persistent_dep_libs) | |
1249 | status = -ENOMEM; | |
1250 | ||
1251 | if (root->dep_libs > 0) { | |
1252 | /* Allocate arrays for dependent lib UUIDs, | |
1253 | * lib nodes */ | |
1254 | root->dep_libs_tree = kzalloc | |
1255 | (sizeof(struct lib_node) * | |
1256 | (root->dep_libs), GFP_KERNEL); | |
1257 | if (!(root->dep_libs_tree)) | |
1258 | status = -ENOMEM; | |
1259 | ||
1260 | } | |
1261 | ||
a741ea6e | 1262 | if (!status) { |
7d55524d ORL |
1263 | /* Get the dependent library UUIDs */ |
1264 | status = | |
1265 | dcd_get_dep_libs(nldr_node_obj-> | |
085467b8 | 1266 | nldr_obj->dcd_mgr, &uuid, |
7d55524d ORL |
1267 | nd_libs, dep_lib_uui_ds, |
1268 | persistent_dep_libs, | |
1269 | phase); | |
1270 | } | |
1271 | } | |
1272 | } | |
1273 | ||
1274 | /* | |
1275 | * Recursively load dependent libraries. | |
1276 | */ | |
a741ea6e | 1277 | if (!status) { |
7d55524d ORL |
1278 | for (i = 0; i < nd_libs; i++) { |
1279 | /* If root library is NOT persistent, and dep library | |
1280 | * is, then record it. If root library IS persistent, | |
1281 | * the deplib is already included */ | |
318b5df9 | 1282 | if (!root_prstnt && persistent_dep_libs[i] && |
dab7f7fe | 1283 | *nldr_node_obj->phase_split) { |
7d55524d ORL |
1284 | if ((nldr_node_obj->pers_libs) >= MAXLIBS) { |
1285 | status = -EILSEQ; | |
1286 | break; | |
1287 | } | |
1288 | ||
1289 | /* Allocate library outside of phase */ | |
1290 | dep_lib = | |
1291 | &nldr_node_obj->pers_lib_table | |
1292 | [nldr_node_obj->pers_libs]; | |
1293 | } else { | |
318b5df9 | 1294 | if (root_prstnt) |
7d55524d ORL |
1295 | persistent_dep_libs[i] = true; |
1296 | ||
1297 | /* Allocate library within phase */ | |
1298 | dep_lib = &root->dep_libs_tree[nd_libs_loaded]; | |
1299 | } | |
1300 | ||
1301 | status = load_lib(nldr_node_obj, dep_lib, | |
1302 | dep_lib_uui_ds[i], | |
1303 | persistent_dep_libs[i], lib_path, | |
1304 | phase, depth); | |
1305 | ||
a741ea6e | 1306 | if (!status) { |
7d55524d | 1307 | if ((status != 0) && |
318b5df9 | 1308 | !root_prstnt && persistent_dep_libs[i] && |
dab7f7fe | 1309 | *nldr_node_obj->phase_split) { |
7d55524d ORL |
1310 | (nldr_node_obj->pers_libs)++; |
1311 | } else { | |
1312 | if (!persistent_dep_libs[i] || | |
dab7f7fe | 1313 | !(*nldr_node_obj->phase_split)) { |
7d55524d ORL |
1314 | nd_libs_loaded++; |
1315 | } | |
1316 | } | |
1317 | } else { | |
1318 | break; | |
1319 | } | |
1320 | } | |
1321 | } | |
1322 | ||
1323 | /* Now we can load the root library */ | |
a741ea6e | 1324 | if (!status) { |
7d55524d ORL |
1325 | new_attrs = nldr_obj->ldr_attrs; |
1326 | new_attrs.sym_arg = root; | |
1327 | new_attrs.rmm_handle = nldr_node_obj; | |
1328 | new_attrs.input_params = nldr_node_obj->priv_ref; | |
1329 | new_attrs.base_image = false; | |
1330 | ||
1331 | status = | |
1332 | nldr_obj->ldr_fxns.load_fxn(root->lib, flags, &new_attrs, | |
1333 | &entry); | |
1334 | } | |
1335 | ||
1336 | /* | |
1337 | * In case of failure, unload any dependent libraries that | |
1338 | * were loaded, and close the root library. | |
1339 | * (Persistent libraries are unloaded from the very top) | |
1340 | */ | |
b66e0986 | 1341 | if (status) { |
7d55524d ORL |
1342 | if (phase != NLDR_EXECUTE) { |
1343 | for (i = 0; i < nldr_node_obj->pers_libs; i++) | |
1344 | unload_lib(nldr_node_obj, | |
1345 | &nldr_node_obj->pers_lib_table[i]); | |
1346 | ||
1347 | nldr_node_obj->pers_libs = 0; | |
1348 | } | |
1349 | for (i = 0; i < nd_libs_loaded; i++) | |
1350 | unload_lib(nldr_node_obj, &root->dep_libs_tree[i]); | |
1351 | ||
1352 | if (root->lib) | |
1353 | nldr_obj->ldr_fxns.close_fxn(root->lib); | |
1354 | ||
1355 | } | |
1356 | ||
1357 | /* Going up one node in the dependency tree */ | |
1358 | depth--; | |
1359 | ||
1360 | kfree(dep_lib_uui_ds); | |
1361 | dep_lib_uui_ds = NULL; | |
1362 | ||
1363 | kfree(persistent_dep_libs); | |
1364 | persistent_dep_libs = NULL; | |
1365 | ||
1366 | return status; | |
1367 | } | |
1368 | ||
1369 | /* | |
1370 | * ======== load_ovly ======== | |
1371 | */ | |
1372 | static int load_ovly(struct nldr_nodeobject *nldr_node_obj, | |
1373 | enum nldr_phase phase) | |
1374 | { | |
1375 | struct nldr_object *nldr_obj = nldr_node_obj->nldr_obj; | |
1376 | struct ovly_node *po_node = NULL; | |
1377 | struct ovly_sect *phase_sects = NULL; | |
1378 | struct ovly_sect *other_sects_list = NULL; | |
1379 | u16 i; | |
1380 | u16 alloc_num = 0; | |
1381 | u16 other_alloc = 0; | |
1382 | u16 *ref_count = NULL; | |
1383 | u16 *other_ref = NULL; | |
1384 | u32 bytes; | |
1385 | struct ovly_sect *ovly_section; | |
1386 | int status = 0; | |
1387 | ||
1388 | /* Find the node in the table */ | |
1389 | for (i = 0; i < nldr_obj->ovly_nodes; i++) { | |
bf968b0a AU |
1390 | if (is_equal_uuid |
1391 | (&nldr_node_obj->uuid, &nldr_obj->ovly_table[i].uuid)) { | |
7d55524d ORL |
1392 | /* Found it */ |
1393 | po_node = &(nldr_obj->ovly_table[i]); | |
1394 | break; | |
1395 | } | |
1396 | } | |
1397 | ||
7d55524d ORL |
1398 | |
1399 | if (!po_node) { | |
1400 | status = -ENOENT; | |
1401 | goto func_end; | |
1402 | } | |
1403 | ||
1404 | switch (phase) { | |
1405 | case NLDR_CREATE: | |
1406 | ref_count = &(po_node->create_ref); | |
1407 | other_ref = &(po_node->other_ref); | |
1408 | phase_sects = po_node->create_sects_list; | |
1409 | other_sects_list = po_node->other_sects_list; | |
1410 | break; | |
1411 | ||
1412 | case NLDR_EXECUTE: | |
1413 | ref_count = &(po_node->execute_ref); | |
1414 | phase_sects = po_node->execute_sects_list; | |
1415 | break; | |
1416 | ||
1417 | case NLDR_DELETE: | |
1418 | ref_count = &(po_node->delete_ref); | |
1419 | phase_sects = po_node->delete_sects_list; | |
1420 | break; | |
1421 | ||
1422 | default: | |
7d55524d ORL |
1423 | break; |
1424 | } | |
1425 | ||
1426 | if (ref_count == NULL) | |
1427 | goto func_end; | |
1428 | ||
1429 | if (*ref_count != 0) | |
1430 | goto func_end; | |
1431 | ||
1432 | /* 'Allocate' memory for overlay sections of this phase */ | |
1433 | ovly_section = phase_sects; | |
1434 | while (ovly_section) { | |
1435 | /* allocate *//* page not supported yet */ | |
1436 | /* reserve *//* align */ | |
1437 | status = rmm_alloc(nldr_obj->rmm, 0, ovly_section->size, 0, | |
1438 | &(ovly_section->sect_run_addr), true); | |
a741ea6e | 1439 | if (!status) { |
7d55524d ORL |
1440 | ovly_section = ovly_section->next_sect; |
1441 | alloc_num++; | |
1442 | } else { | |
1443 | break; | |
1444 | } | |
1445 | } | |
1446 | if (other_ref && *other_ref == 0) { | |
1447 | /* 'Allocate' memory for other overlay sections | |
1448 | * (create phase) */ | |
a741ea6e | 1449 | if (!status) { |
7d55524d ORL |
1450 | ovly_section = other_sects_list; |
1451 | while (ovly_section) { | |
1452 | /* page not supported *//* align */ | |
1453 | /* reserve */ | |
1454 | status = | |
1455 | rmm_alloc(nldr_obj->rmm, 0, | |
1456 | ovly_section->size, 0, | |
1457 | &(ovly_section->sect_run_addr), | |
1458 | true); | |
a741ea6e | 1459 | if (!status) { |
7d55524d ORL |
1460 | ovly_section = ovly_section->next_sect; |
1461 | other_alloc++; | |
1462 | } else { | |
1463 | break; | |
1464 | } | |
1465 | } | |
1466 | } | |
1467 | } | |
1468 | if (*ref_count == 0) { | |
a741ea6e | 1469 | if (!status) { |
7d55524d ORL |
1470 | /* Load sections for this phase */ |
1471 | ovly_section = phase_sects; | |
a741ea6e | 1472 | while (ovly_section && !status) { |
7d55524d ORL |
1473 | bytes = |
1474 | (*nldr_obj->ovly_fxn) (nldr_node_obj-> | |
1475 | priv_ref, | |
1476 | ovly_section-> | |
1477 | sect_run_addr, | |
1478 | ovly_section-> | |
1479 | sect_load_addr, | |
1480 | ovly_section->size, | |
1481 | ovly_section->page); | |
1482 | if (bytes != ovly_section->size) | |
1483 | status = -EPERM; | |
1484 | ||
1485 | ovly_section = ovly_section->next_sect; | |
1486 | } | |
1487 | } | |
1488 | } | |
1489 | if (other_ref && *other_ref == 0) { | |
a741ea6e | 1490 | if (!status) { |
7d55524d ORL |
1491 | /* Load other sections (create phase) */ |
1492 | ovly_section = other_sects_list; | |
a741ea6e | 1493 | while (ovly_section && !status) { |
7d55524d ORL |
1494 | bytes = |
1495 | (*nldr_obj->ovly_fxn) (nldr_node_obj-> | |
1496 | priv_ref, | |
1497 | ovly_section-> | |
1498 | sect_run_addr, | |
1499 | ovly_section-> | |
1500 | sect_load_addr, | |
1501 | ovly_section->size, | |
1502 | ovly_section->page); | |
1503 | if (bytes != ovly_section->size) | |
1504 | status = -EPERM; | |
1505 | ||
1506 | ovly_section = ovly_section->next_sect; | |
1507 | } | |
1508 | } | |
1509 | } | |
b66e0986 | 1510 | if (status) { |
7d55524d ORL |
1511 | /* 'Deallocate' memory */ |
1512 | free_sects(nldr_obj, phase_sects, alloc_num); | |
1513 | free_sects(nldr_obj, other_sects_list, other_alloc); | |
1514 | } | |
1515 | func_end: | |
a741ea6e | 1516 | if (!status && (ref_count != NULL)) { |
7d55524d ORL |
1517 | *ref_count += 1; |
1518 | if (other_ref) | |
1519 | *other_ref += 1; | |
1520 | ||
1521 | } | |
1522 | ||
1523 | return status; | |
1524 | } | |
1525 | ||
1526 | /* | |
1527 | * ======== remote_alloc ======== | |
1528 | */ | |
c8c1ad8c | 1529 | static int remote_alloc(void **ref, u16 mem_sect, u32 size, |
b301c858 | 1530 | u32 align, u32 *dsp_address, |
21aaf42e | 1531 | s32 segmnt_id, s32 req, |
7d55524d ORL |
1532 | bool reserve) |
1533 | { | |
13b18c29 | 1534 | struct nldr_nodeobject *hnode = (struct nldr_nodeobject *)ref; |
7d55524d ORL |
1535 | struct nldr_object *nldr_obj; |
1536 | struct rmm_target_obj *rmm; | |
1537 | u16 mem_phase_bit = MAXFLAGS; | |
1538 | u16 segid = 0; | |
1539 | u16 i; | |
1540 | u16 mem_sect_type; | |
1541 | u32 word_size; | |
b301c858 | 1542 | struct rmm_addr *rmm_addr_obj = (struct rmm_addr *)dsp_address; |
7d55524d ORL |
1543 | bool mem_load_req = false; |
1544 | int status = -ENOMEM; /* Set to fail */ | |
7d55524d ORL |
1545 | nldr_obj = hnode->nldr_obj; |
1546 | rmm = nldr_obj->rmm; | |
1547 | /* Convert size to DSP words */ | |
1548 | word_size = | |
085467b8 RS |
1549 | (size + nldr_obj->dsp_word_size - |
1550 | 1) / nldr_obj->dsp_word_size; | |
7d55524d | 1551 | /* Modify memory 'align' to account for DSP cache line size */ |
d1000921 | 1552 | align = lcm(GEM_CACHE_LINE_SIZE, align); |
7d55524d | 1553 | dev_dbg(bridge, "%s: memory align to 0x%x\n", __func__, align); |
0cd343a4 RS |
1554 | if (segmnt_id != -1) { |
1555 | rmm_addr_obj->segid = segmnt_id; | |
1556 | segid = segmnt_id; | |
7d55524d ORL |
1557 | mem_load_req = req; |
1558 | } else { | |
1559 | switch (hnode->phase) { | |
1560 | case NLDR_CREATE: | |
1561 | mem_phase_bit = CREATEDATAFLAGBIT; | |
1562 | break; | |
1563 | case NLDR_DELETE: | |
1564 | mem_phase_bit = DELETEDATAFLAGBIT; | |
1565 | break; | |
1566 | case NLDR_EXECUTE: | |
1567 | mem_phase_bit = EXECUTEDATAFLAGBIT; | |
1568 | break; | |
1569 | default: | |
7d55524d ORL |
1570 | break; |
1571 | } | |
c8c1ad8c | 1572 | if (mem_sect == DBLL_CODE) |
7d55524d ORL |
1573 | mem_phase_bit++; |
1574 | ||
1575 | if (mem_phase_bit < MAXFLAGS) | |
1576 | segid = hnode->seg_id[mem_phase_bit]; | |
1577 | ||
1578 | /* Determine if there is a memory loading requirement */ | |
1579 | if ((hnode->code_data_flag_mask >> mem_phase_bit) & 0x1) | |
1580 | mem_load_req = true; | |
1581 | ||
1582 | } | |
c8c1ad8c | 1583 | mem_sect_type = (mem_sect == DBLL_CODE) ? DYNM_CODE : DYNM_DATA; |
7d55524d | 1584 | |
c8c1ad8c | 1585 | /* Find an appropriate segment based on mem_sect */ |
7d55524d ORL |
1586 | if (segid == NULLID) { |
1587 | /* No memory requirements of preferences */ | |
7d55524d ORL |
1588 | goto func_cont; |
1589 | } | |
1590 | if (segid <= MAXSEGID) { | |
7d55524d ORL |
1591 | /* Attempt to allocate from segid first. */ |
1592 | rmm_addr_obj->segid = segid; | |
1593 | status = | |
b301c858 | 1594 | rmm_alloc(rmm, segid, word_size, align, dsp_address, false); |
b66e0986 | 1595 | if (status) { |
7d55524d ORL |
1596 | dev_dbg(bridge, "%s: Unable allocate from segment %d\n", |
1597 | __func__, segid); | |
1598 | } | |
1599 | } else { | |
1600 | /* segid > MAXSEGID ==> Internal or external memory */ | |
7d55524d ORL |
1601 | /* Check for any internal or external memory segment, |
1602 | * depending on segid. */ | |
1603 | mem_sect_type |= segid == MEMINTERNALID ? | |
1604 | DYNM_INTERNAL : DYNM_EXTERNAL; | |
1605 | for (i = 0; i < nldr_obj->dload_segs; i++) { | |
1606 | if ((nldr_obj->seg_table[i] & mem_sect_type) != | |
1607 | mem_sect_type) | |
1608 | continue; | |
1609 | ||
b301c858 | 1610 | status = rmm_alloc(rmm, i, word_size, align, |
a741ea6e ER |
1611 | dsp_address, false); |
1612 | if (!status) { | |
7d55524d ORL |
1613 | /* Save segid for freeing later */ |
1614 | rmm_addr_obj->segid = i; | |
1615 | break; | |
1616 | } | |
1617 | } | |
1618 | } | |
1619 | func_cont: | |
1620 | /* Haven't found memory yet, attempt to find any segment that works */ | |
1621 | if (status == -ENOMEM && !mem_load_req) { | |
1622 | dev_dbg(bridge, "%s: Preferred segment unavailable, trying " | |
1623 | "another\n", __func__); | |
1624 | for (i = 0; i < nldr_obj->dload_segs; i++) { | |
1625 | /* All bits of mem_sect_type must be set */ | |
1626 | if ((nldr_obj->seg_table[i] & mem_sect_type) != | |
1627 | mem_sect_type) | |
1628 | continue; | |
1629 | ||
b301c858 RS |
1630 | status = rmm_alloc(rmm, i, word_size, align, |
1631 | dsp_address, false); | |
a741ea6e | 1632 | if (!status) { |
7d55524d ORL |
1633 | /* Save segid */ |
1634 | rmm_addr_obj->segid = i; | |
1635 | break; | |
1636 | } | |
1637 | } | |
1638 | } | |
1639 | ||
1640 | return status; | |
1641 | } | |
1642 | ||
13b18c29 | 1643 | static int remote_free(void **ref, u16 space, u32 dsp_address, |
7d55524d ORL |
1644 | u32 size, bool reserve) |
1645 | { | |
13b18c29 | 1646 | struct nldr_object *nldr_obj = (struct nldr_object *)ref; |
7d55524d ORL |
1647 | struct rmm_target_obj *rmm; |
1648 | u32 word_size; | |
1649 | int status = -ENOMEM; /* Set to fail */ | |
1650 | ||
7d55524d ORL |
1651 | rmm = nldr_obj->rmm; |
1652 | ||
1653 | /* Convert size to DSP words */ | |
1654 | word_size = | |
085467b8 RS |
1655 | (size + nldr_obj->dsp_word_size - |
1656 | 1) / nldr_obj->dsp_word_size; | |
7d55524d | 1657 | |
b301c858 | 1658 | if (rmm_free(rmm, space, dsp_address, word_size, reserve)) |
7d55524d ORL |
1659 | status = 0; |
1660 | ||
1661 | return status; | |
1662 | } | |
1663 | ||
1664 | /* | |
1665 | * ======== unload_lib ======== | |
1666 | */ | |
1667 | static void unload_lib(struct nldr_nodeobject *nldr_node_obj, | |
1668 | struct lib_node *root) | |
1669 | { | |
1670 | struct dbll_attrs new_attrs; | |
1671 | struct nldr_object *nldr_obj = nldr_node_obj->nldr_obj; | |
1672 | u16 i; | |
1673 | ||
7d55524d ORL |
1674 | |
1675 | /* Unload dependent libraries */ | |
1676 | for (i = 0; i < root->dep_libs; i++) | |
1677 | unload_lib(nldr_node_obj, &root->dep_libs_tree[i]); | |
1678 | ||
1679 | root->dep_libs = 0; | |
1680 | ||
1681 | new_attrs = nldr_obj->ldr_attrs; | |
1682 | new_attrs.rmm_handle = nldr_obj->rmm; | |
1683 | new_attrs.input_params = nldr_node_obj->priv_ref; | |
1684 | new_attrs.base_image = false; | |
1685 | new_attrs.sym_arg = root; | |
1686 | ||
1687 | if (root->lib) { | |
1688 | /* Unload the root library */ | |
1689 | nldr_obj->ldr_fxns.unload_fxn(root->lib, &new_attrs); | |
1690 | nldr_obj->ldr_fxns.close_fxn(root->lib); | |
1691 | } | |
1692 | ||
1693 | /* Free dependent library list */ | |
1694 | kfree(root->dep_libs_tree); | |
1695 | root->dep_libs_tree = NULL; | |
1696 | } | |
1697 | ||
1698 | /* | |
1699 | * ======== unload_ovly ======== | |
1700 | */ | |
1701 | static void unload_ovly(struct nldr_nodeobject *nldr_node_obj, | |
1702 | enum nldr_phase phase) | |
1703 | { | |
1704 | struct nldr_object *nldr_obj = nldr_node_obj->nldr_obj; | |
1705 | struct ovly_node *po_node = NULL; | |
1706 | struct ovly_sect *phase_sects = NULL; | |
1707 | struct ovly_sect *other_sects_list = NULL; | |
1708 | u16 i; | |
1709 | u16 alloc_num = 0; | |
1710 | u16 other_alloc = 0; | |
1711 | u16 *ref_count = NULL; | |
1712 | u16 *other_ref = NULL; | |
1713 | ||
1714 | /* Find the node in the table */ | |
1715 | for (i = 0; i < nldr_obj->ovly_nodes; i++) { | |
bf968b0a AU |
1716 | if (is_equal_uuid |
1717 | (&nldr_node_obj->uuid, &nldr_obj->ovly_table[i].uuid)) { | |
7d55524d ORL |
1718 | /* Found it */ |
1719 | po_node = &(nldr_obj->ovly_table[i]); | |
1720 | break; | |
1721 | } | |
1722 | } | |
1723 | ||
7d55524d ORL |
1724 | |
1725 | if (!po_node) | |
1726 | /* TODO: Should we print warning here? */ | |
1727 | return; | |
1728 | ||
1729 | switch (phase) { | |
1730 | case NLDR_CREATE: | |
1731 | ref_count = &(po_node->create_ref); | |
1732 | phase_sects = po_node->create_sects_list; | |
1733 | alloc_num = po_node->create_sects; | |
1734 | break; | |
1735 | case NLDR_EXECUTE: | |
1736 | ref_count = &(po_node->execute_ref); | |
1737 | phase_sects = po_node->execute_sects_list; | |
1738 | alloc_num = po_node->execute_sects; | |
1739 | break; | |
1740 | case NLDR_DELETE: | |
1741 | ref_count = &(po_node->delete_ref); | |
1742 | other_ref = &(po_node->other_ref); | |
1743 | phase_sects = po_node->delete_sects_list; | |
1744 | /* 'Other' overlay sections are unloaded in the delete phase */ | |
1745 | other_sects_list = po_node->other_sects_list; | |
1746 | alloc_num = po_node->delete_sects; | |
1747 | other_alloc = po_node->other_sects; | |
1748 | break; | |
1749 | default: | |
7d55524d ORL |
1750 | break; |
1751 | } | |
7d55524d ORL |
1752 | if (ref_count && (*ref_count > 0)) { |
1753 | *ref_count -= 1; | |
1754 | if (other_ref) { | |
7d55524d ORL |
1755 | *other_ref -= 1; |
1756 | } | |
1757 | } | |
1758 | ||
1759 | if (ref_count && *ref_count == 0) { | |
1760 | /* 'Deallocate' memory */ | |
1761 | free_sects(nldr_obj, phase_sects, alloc_num); | |
1762 | } | |
1763 | if (other_ref && *other_ref == 0) | |
1764 | free_sects(nldr_obj, other_sects_list, other_alloc); | |
1765 | } | |
1766 | ||
1767 | /* | |
1768 | * ======== find_in_persistent_lib_array ======== | |
1769 | */ | |
1770 | static bool find_in_persistent_lib_array(struct nldr_nodeobject *nldr_node_obj, | |
1771 | struct dbll_library_obj *lib) | |
1772 | { | |
1773 | s32 i = 0; | |
1774 | ||
1775 | for (i = 0; i < nldr_node_obj->pers_libs; i++) { | |
1776 | if (lib == nldr_node_obj->pers_lib_table[i].lib) | |
1777 | return true; | |
1778 | ||
1779 | } | |
1780 | ||
1781 | return false; | |
1782 | } | |
1783 | ||
4f551c8f | 1784 | #ifdef CONFIG_TIDSPBRIDGE_BACKTRACE |
7d55524d ORL |
1785 | /** |
1786 | * nldr_find_addr() - Find the closest symbol to the given address based on | |
1787 | * dynamic node object. | |
1788 | * | |
1789 | * @nldr_node: Dynamic node object | |
1790 | * @sym_addr: Given address to find the dsp symbol | |
1791 | * @offset_range: offset range to look for dsp symbol | |
1792 | * @offset_output: Symbol Output address | |
1793 | * @sym_name: String with the dsp symbol | |
1794 | * | |
1795 | * This function finds the node library for a given address and | |
1796 | * retrieves the dsp symbol by calling dbll_find_dsp_symbol. | |
1797 | */ | |
1798 | int nldr_find_addr(struct nldr_nodeobject *nldr_node, u32 sym_addr, | |
1799 | u32 offset_range, void *offset_output, char *sym_name) | |
1800 | { | |
1801 | int status = 0; | |
1802 | bool status1 = false; | |
1803 | s32 i = 0; | |
1804 | struct lib_node root = { NULL, 0, NULL }; | |
7d55524d ORL |
1805 | pr_debug("%s(0x%x, 0x%x, 0x%x, 0x%x, %s)\n", __func__, (u32) nldr_node, |
1806 | sym_addr, offset_range, (u32) offset_output, sym_name); | |
1807 | ||
dab7f7fe | 1808 | if (nldr_node->dynamic && *nldr_node->phase_split) { |
7d55524d ORL |
1809 | switch (nldr_node->phase) { |
1810 | case NLDR_CREATE: | |
1811 | root = nldr_node->create_lib; | |
1812 | break; | |
1813 | case NLDR_EXECUTE: | |
1814 | root = nldr_node->execute_lib; | |
1815 | break; | |
1816 | case NLDR_DELETE: | |
1817 | root = nldr_node->delete_lib; | |
1818 | break; | |
1819 | default: | |
7d55524d ORL |
1820 | break; |
1821 | } | |
1822 | } else { | |
1823 | /* for Overlay nodes or non-split Dynamic nodes */ | |
1824 | root = nldr_node->root; | |
1825 | } | |
1826 | ||
1827 | status1 = dbll_find_dsp_symbol(root.lib, sym_addr, | |
1828 | offset_range, offset_output, sym_name); | |
1829 | ||
1830 | /* If symbol not found, check dependent libraries */ | |
1831 | if (!status1) | |
1832 | for (i = 0; i < root.dep_libs; i++) { | |
1833 | status1 = dbll_find_dsp_symbol( | |
1834 | root.dep_libs_tree[i].lib, sym_addr, | |
1835 | offset_range, offset_output, sym_name); | |
1836 | if (status1) | |
1837 | /* Symbol found */ | |
1838 | break; | |
1839 | } | |
1840 | /* Check persistent libraries */ | |
1841 | if (!status1) | |
1842 | for (i = 0; i < nldr_node->pers_libs; i++) { | |
1843 | status1 = dbll_find_dsp_symbol( | |
1844 | nldr_node->pers_lib_table[i].lib, sym_addr, | |
1845 | offset_range, offset_output, sym_name); | |
1846 | if (status1) | |
1847 | /* Symbol found */ | |
1848 | break; | |
1849 | } | |
1850 | ||
1851 | if (!status1) { | |
1852 | pr_debug("%s: Address 0x%x not found in range %d.\n", | |
1853 | __func__, sym_addr, offset_range); | |
1854 | status = -ESPIPE; | |
1855 | } | |
1856 | ||
1857 | return status; | |
1858 | } | |
4f551c8f | 1859 | #endif |