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