Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / user / liblwk / elf.c
1 /* Copyright (c) 2008, Sandia National Laboratories */
2
3 #include <lwk/liblwk.h>
4 #include <lwk/ctype.h>
5
6 /**
7  * Verifies that an ELF header is sane.
8  * Returns 0 if header is sane and random non-zero values if header is insane.
9  */
10 int
11 elf_check_hdr(const struct elfhdr *hdr)
12 {
13         if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0) {
14                 print(TYPE_ERR "bad e_ident %#x\n",
15                                *((unsigned int *)hdr->e_ident));
16                 return -1;
17         }
18         if (hdr->e_ident[EI_CLASS] != ELF_CLASS) {
19                 print(TYPE_ERR "bad e_ident[EI_CLASS] %#x\n",
20                                (unsigned int)hdr->e_ident[EI_CLASS]);
21                 return -1;
22         }
23         if (hdr->e_ident[EI_DATA] != ELF_DATA) {
24                 print(TYPE_ERR "bad e_ident[EI_DATA] %#x\n",
25                                (unsigned int)hdr->e_ident[EI_DATA]);
26                 return -1;
27         }
28         if (hdr->e_ident[EI_VERSION] != EV_CURRENT) {
29                 print(TYPE_ERR "bad e_ident[EI_VERSION] %#x\n",
30                                (unsigned int)hdr->e_ident[EI_VERSION]);
31                 return -1;
32         }
33         if (hdr->e_ident[EI_OSABI] != ELF_OSABI) {
34                 print(TYPE_ERR "bad e_dent[EI_OSABI] %#x\n",
35                                (unsigned int)hdr->e_ident[EI_OSABI]);
36                 return -1;
37         }
38         if (hdr->e_type != ET_EXEC) {
39                 print(TYPE_ERR "bad e_type %#x\n",
40                                (unsigned int)hdr->e_type);
41                 return -1;
42         }
43         if (hdr->e_machine != ELF_ARCH) {
44                 print(TYPE_ERR "bad e_machine %#x\n",
45                                (unsigned int)hdr->e_machine);
46                 return -1;
47         }
48         if (hdr->e_version != EV_CURRENT) {
49                 print(TYPE_ERR "bad e_version %#x\n",
50                                (unsigned int)hdr->e_version);
51                 return -1;
52         }
53         if (hdr->e_flags != 0) {
54                 print(TYPE_ERR "bad e_flags %#x\n",
55                                (unsigned int)hdr->e_flags);
56                 return -1;
57         }
58
59         return 0;
60 }
61
62 /**
63  * Prints the contents of an ELF file header to the console.
64  */
65 void
66 elf_print_elfhdr(const struct elfhdr *hdr)
67 {
68         print(TYPE_NORM "ELF File Header:\n");
69         print(TYPE_NORM "  type      %0#10x\n",  (unsigned int)  hdr->e_type      );
70         print(TYPE_NORM "  machine   %0#10x\n",  (unsigned int)  hdr->e_machine   );
71         print(TYPE_NORM "  version   %0#10x\n",  (unsigned int)  hdr->e_version   );
72         print(TYPE_NORM "  entry     %0#18lx\n", (unsigned long) hdr->e_entry     );
73         print(TYPE_NORM "  phoff     %0#18lx\n", (unsigned long) hdr->e_phoff     );
74         print(TYPE_NORM "  shoff     %0#18lx\n", (unsigned long) hdr->e_shoff     );
75         print(TYPE_NORM "  flags     %0#10x\n",  (unsigned int)  hdr->e_flags     );
76         print(TYPE_NORM "  ehsize    %0#10x\n",  (unsigned int)  hdr->e_ehsize    );
77         print(TYPE_NORM "  phentsize %0#10x\n",  (unsigned int)  hdr->e_phentsize );
78         print(TYPE_NORM "  phnum     %0#10x\n",  (unsigned int)  hdr->e_phnum     );
79         print(TYPE_NORM "  shentsize %0#10x\n",  (unsigned int)  hdr->e_shentsize );
80         print(TYPE_NORM "  shnum     %0#10x\n",  (unsigned int)  hdr->e_shnum     );
81         print(TYPE_NORM "  shstrndx  %0#10x\n",  (unsigned int)  hdr->e_shstrndx  );
82 }
83
84 /**
85  * Prints the contents of an ELF program header to the console.
86  */
87 void
88 elf_print_phdr(const struct elf_phdr *hdr)
89 {
90         char *name;
91
92         switch (hdr->p_type) {
93                 case PT_NULL:    name = "NULL";    break;
94                 case PT_LOAD:    name = "LOAD";    break;
95                 case PT_DYNAMIC: name = "DYNAMIC"; break;
96                 case PT_INTERP:  name = "INTERP";  break;
97                 case PT_NOTE:    name = "NOTE";    break;
98                 case PT_SHLIB:   name = "SHLIB";   break;
99                 case PT_PHDR:    name = "PHDR";    break;
100                 case PT_LOPROC:  name = "LOPROC";  break;
101                 case PT_HIPROC:  name = "HIPROC";  break;
102                 default:         name = "UNDEFINED TYPE";
103         }
104
105         print(TYPE_NORM "ELF Program Segment Header:\n");
106         print(TYPE_NORM "  type   %s\n", name);
107         print(TYPE_NORM "  flags  %0#10x\n",  (unsigned int)  hdr->p_flags  );
108         print(TYPE_NORM "  offset %0#18lx\n", (unsigned long) hdr->p_offset );
109         print(TYPE_NORM "  vaddr  %0#18lx\n", (unsigned long) hdr->p_vaddr  );
110         print(TYPE_NORM "  paddr  %0#18lx\n", (unsigned long) hdr->p_paddr  );
111         print(TYPE_NORM "  filesz %0#18lx\n", (unsigned long) hdr->p_filesz );
112         print(TYPE_NORM "  memsz  %0#18lx\n", (unsigned long) hdr->p_memsz  );
113         print(TYPE_NORM "  align  %0#18lx\n", (unsigned long) hdr->p_align  );
114 }
115
116 /**
117  * Converts ELF flags to the corresponding kernel memory subsystem flags.
118  */
119 vmflags_t
120 elf_pflags_to_vmflags(unsigned int elf_pflags)
121 {
122         vmflags_t vmflags = VM_USER;
123         if ( elf_pflags & PF_R ) vmflags |= VM_READ;
124         if ( elf_pflags & PF_W ) vmflags |= VM_WRITE;
125         if ( elf_pflags & PF_X ) vmflags |= VM_EXEC;
126         return vmflags;
127 }
128
129 /**
130  * Determines an ELF executable's entry point... where to start executing.
131  * Note: The address returned is in the context of the executing ELF
132  *       image, not an address within the passed in elf_image.
133  */
134 vaddr_t
135 elf_entry_point(const void *elf_image)
136 {
137         const struct elfhdr *ehdr = elf_image;
138         return ehdr->e_entry;
139 }
140
141 /**
142  * Determines the address of an ELF executable's program header table.
143  * Note: The address returned is in the context of the executing ELF
144  *       image, not an address within the passed in elf_image.
145  */
146 vaddr_t
147 elf_phdr_table_addr(const void *elf_image)
148 {
149         const struct elfhdr *ehdr = elf_image;
150         struct elf_phdr *phdr_array, *phdr;
151         unsigned int i;
152
153         phdr_array = (struct elf_phdr *)(elf_image + ehdr->e_phoff);
154
155         for (i = 0; i < ehdr->e_phnum; i++) {
156                 phdr = &phdr_array[i];
157                 if (phdr->p_type == PT_LOAD)
158                         return phdr->p_vaddr - phdr->p_offset + ehdr->e_phoff;
159         }
160         return 0;
161 }
162
163 /**
164  * Returns the number of entries in an ELF executable's program header table.
165  */
166 unsigned int
167 elf_num_phdrs(const void *elf_image)
168 {
169         const struct elfhdr *ehdr = elf_image;
170         return ehdr->e_phnum;
171 }
172
173 /**
174  * Determines where the UNIX heap should start for a given ELF executable.
175  * Note: The address returned is in the context of the executing ELF
176  *       image, not an address relative to the passed in elf_image.
177  */
178 vaddr_t
179 elf_heap_start(const void *elf_image)
180 {
181         const struct elfhdr *ehdr;
182         const struct elf_phdr *phdr_array;
183         const struct elf_phdr *phdr;
184         vaddr_t end, heap_start=0;
185         size_t i;
186
187         /* Locate the program header array (in this context) */
188         ehdr       = elf_image;
189         phdr_array = (struct elf_phdr *)(elf_image + ehdr->e_phoff);
190
191         for (i = 0; i < ehdr->e_phnum; i++) {
192                 phdr = &phdr_array[i];
193                 if (phdr->p_type != PT_LOAD)
194                         continue;
195
196                 /* Calculate the end of the LOAD segment in memory */
197                 end = phdr->p_vaddr + phdr->p_memsz;
198
199                 if (end > heap_start)
200                         heap_start = end;
201         }
202
203         return heap_start;
204 }
205
206 /**
207  * Given an argument string like "arg1=foo arg2=bar", parses it into
208  * an argv[] or envp[] style array of string pointers. Useful for
209  * constructing the argv and envp arguments to elf_init_stack().
210  */
211 int
212 elf_init_str_array(
213         size_t  size,
214         char *  ptrs[],
215         char *  str
216 )
217
218         size_t pos = 0;
219         char *tmp;
220
221         while (strlen(str)) {
222                 /* move past white space */
223                 while (*str && isspace(*str))
224                         ++str;
225
226                 tmp = str;
227
228                 /* find the end of the string */
229                 while (*str && !isspace(*str))
230                         ++str;
231
232                 *str++ = 0;
233
234                 if (strlen(tmp)) {
235                         if (pos == size - 1)
236                                 return -1;
237                         ptrs[pos++] = tmp; 
238                 }
239         
240         }
241         ptrs[pos] = "";
242         return 0;
243 }
244
245 /**
246  * Writes an auxiliary info table entry.
247  */
248 static void
249 write_aux(
250         struct aux_ent * table,
251         int              index,
252         unsigned long    id,
253         unsigned long    val
254 )
255 {
256         table[index].id  = id;
257         table[index].val = val;
258 }
259
260 /**
261  * Determines the value of the current stack pointer in the target
262  * address space. The sp argument is the stack pointer in this context
263  * (i.e., the context this code is executing in), stack_mapping is the
264  * address of the stack in this context, stack_start is the address
265  * of the stack in the target address space, and extent is the size
266  * of the stack.
267  */
268 static vaddr_t
269 sp_in_aspace(void *sp,
270              void *stack_mapping, vaddr_t stack_start, size_t extent)
271 {
272         vaddr_t stack_end    = (vaddr_t)stack_mapping + extent;
273         vaddr_t stack_vend   = stack_start + extent;
274         size_t  stack_offset = stack_end - (vaddr_t)sp;
275
276         return stack_vend - stack_offset;
277 }
278
279 /**
280  * Sets up the initial stack for a new task.  This includes storing the
281  * argv[] argument array, envp[] environment array, and auxiliary info table
282  * to the top of the user stack in the format that the C library expects them.
283  * Eventually the arguments get passed to the application's
284  * main(argc, argv, envp) function.
285  * 
286  * If successful, the initial stack pointer value that should be used when
287  * starting the new task is returned in >stack_ptr.
288  *
289  * This function sets up the initial stack as follows (stack grows down):
290  *
291  *                 Environment Strings
292  *                 Argument Strings
293  *                 Platform String
294  *                 Auxiliary Info Table
295  *                 envp[]
296  *                 argv[]
297  *                 argc
298  *
299  * Arguments:
300  *       [IN]  elf_image     The ELF executable, needed to setup aux info.
301  *       [IN]  stack_mapping Where the stack is mapped in this context.
302  *       [IN]  stack_start   Where the stack is located in the target aspace.
303  *       [IN]  stack_extent  Size of the stack.
304  *       [IN]  argv[]        Array of pointers to argument strings.
305  *       [IN]  envp[]        Array of pointers to environment strings.
306  *       [IN]  uid           User ID of the task
307  *       [IN]  gid           Group ID of the task
308  *       [IN]  hwcap         Hardware capability bitfield
309  *                           (used for AT_HWCAP entry in aux info table)
310  *       [OUT] stack_ptr     The initial stack pointer value for the new task.
311  *                           (note this is an address in the target aspace)
312  *
313  * Returns:
314  *       Success: 0
315  *       Failure: Error Code, stack may have been partially initialized
316  */
317 int
318 elf_init_stack(
319         void *    elf_image,
320         void *    stack_mapping,
321         vaddr_t   stack_start,
322         size_t    stack_extent,
323         char *    argv[],
324         char *    envp[],
325         uid_t     uid,
326         gid_t     gid,
327         uint32_t  hwcap,
328         vaddr_t * stack_ptr
329 )
330 {
331         size_t i, len;
332         uintptr_t sp;
333         const char *platform_str = ELF_PLATFORM;
334         struct aux_ent auxv[AT_ENTRIES];
335         size_t argc=0, envc=0, auxc=0;
336         size_t arg_len=0, env_len=0, auxv_len=0;
337         size_t platform_str_len=0;
338
339         char *strings_sp;
340         char *platform_str_sp;
341         unsigned long *auxv_sp;
342         unsigned long *envp_sp;
343         unsigned long *argv_sp;
344         unsigned long *argc_sp;
345
346         /* Count # of arguments and their total string length */
347         while ((len = strlen(argv[argc])) != 0) {
348                 arg_len += (len + 1);
349                 ++argc;
350         }
351
352         /* Count # of environment variables and their total string length */
353         while ((len = strlen(envp[envc])) != 0) {
354                 env_len += (len + 1);
355                 ++envc;
356         }
357
358         /* Calculate length of the arch's platform string, if there is one */
359         if (platform_str)
360                 platform_str_len = strlen(platform_str) + 1;
361
362         /* Make room on stack for arg, env, and platform strings */
363         sp = (uintptr_t)((vaddr_t)stack_mapping + stack_extent);
364         sp -= (arg_len + env_len + platform_str_len);
365         strings_sp = (void *) sp;
366
367         /* Build the auxilliary information table */
368         write_aux(auxv, auxc++, AT_HWCAP, hwcap);
369         write_aux(auxv, auxc++, AT_PAGESZ, ELF_EXEC_PAGESIZE);
370         write_aux(auxv, auxc++, AT_CLKTCK, 1000000l);
371         write_aux(auxv, auxc++, AT_PHDR, elf_phdr_table_addr(elf_image));
372         write_aux(auxv, auxc++, AT_PHENT, sizeof(struct elf_phdr));
373         write_aux(auxv, auxc++, AT_PHNUM, elf_num_phdrs(elf_image));
374         write_aux(auxv, auxc++, AT_BASE, 0);
375         write_aux(auxv, auxc++, AT_FLAGS, 0);
376         write_aux(auxv, auxc++, AT_ENTRY, elf_entry_point(elf_image));
377         write_aux(auxv, auxc++, AT_UID, uid);
378         write_aux(auxv, auxc++, AT_EUID, uid);
379         write_aux(auxv, auxc++, AT_GID, gid);
380         write_aux(auxv, auxc++, AT_EGID, gid);
381         write_aux(auxv, auxc++, AT_SECURE, 0);
382         if (platform_str) {
383                 platform_str_sp = strings_sp;
384                 write_aux(
385                         auxv, auxc++, AT_PLATFORM,
386                         sp_in_aspace(platform_str_sp,
387                                      stack_mapping, stack_start,
388                                      stack_extent)
389                 );
390         }
391         write_aux(auxv, auxc++, AT_NULL, 0);
392
393         /* Make room on stack for aux info table */
394         auxv_len = auxc * sizeof(struct aux_ent);
395         sp -= auxv_len;
396
397         /* Make room on stack for argc, argv[], envp[] */
398         sp -= ((1 + (argc + 1) + (envc + 1)) * sizeof(unsigned long));
399
400         /* Align stack to 16-byte boundary */
401         sp = round_down(sp, 16);
402
403         /* Calculate stack address to store argc, argv[], envp[], and auxv[] */
404         argc_sp = (unsigned long *) sp;
405         argv_sp = argc_sp  + 1;
406         envp_sp = argv_sp  + argc + 1;
407         auxv_sp = envp_sp  + envc + 1;
408
409         /* Store arch's platform string, if there is one */
410         if (platform_str) {
411                 memcpy(strings_sp, platform_str, platform_str_len);
412                 strings_sp += platform_str_len;
413         }
414
415         /* Store the auxiliary information array */
416         memcpy(auxv_sp, auxv, auxv_len);
417
418         /* Store argv[] */
419         for (i = 0; i < argc; i++) {
420                 len = strlen(argv[i]) + 1;
421                 memcpy(strings_sp, argv[i], len);
422                 argv_sp[i] = sp_in_aspace(strings_sp,
423                                           stack_mapping, stack_start,
424                                           stack_extent),
425                 strings_sp += len;
426         }
427         argv_sp[i] = 0;  /* NULL terminate argv[] */
428
429         /* Store envp[] */
430         for (i = 0; i < envc; i++) {
431                 len = strlen(envp[i]) + 1;
432                 memcpy(strings_sp, envp[i], len);
433                 envp_sp[i] = sp_in_aspace(strings_sp,
434                                           stack_mapping, stack_start,
435                                           stack_extent),
436                 strings_sp += len;
437         }
438         envp_sp[i] = 0;  /* NULL terminate argv[] */
439
440         /* Store argc */
441         *argc_sp = argc;
442
443         if (stack_ptr) {
444                 *stack_ptr = sp_in_aspace((void *)sp,
445                                           stack_mapping, stack_start,
446                                           stack_extent);
447         }
448         return 0;
449 }
450
451 /**
452  * A "default" alloc_pmem() function for use with elf_load_executable().
453  * A user may wish to define a custom replacement alloc_pmem() function
454  * to, for example, keep track of the physical memory that is allocated.
455  */
456 paddr_t
457 elf_dflt_alloc_pmem(size_t size, size_t alignment, uintptr_t arg)
458 {
459         struct pmem_region result;
460
461         if (pmem_alloc_umem(size, alignment, &result))
462                 return 0;
463
464         /* Mark the memory as being used by the init task */
465         result.type = PMEM_TYPE_INIT_TASK;
466         BUG_ON(pmem_update(&result));
467         
468         return result.start;
469 }
470
471 static int
472 load_writable_segment(
473         void *            elf_image,
474         struct elf_phdr * phdr,
475         id_t              aspace_id,
476         vaddr_t           start,
477         size_t            extent,
478         vmpagesize_t      pagesz,
479         uintptr_t         alloc_pmem_arg,
480         paddr_t (*alloc_pmem)(size_t size, size_t alignment, uintptr_t arg)
481 )
482 {
483         int status;
484         paddr_t pmem;
485         vaddr_t local_start;
486         vaddr_t src, dst;
487         id_t my_aspace_id;
488
489         /* Figure out my address space ID */
490         if ((status = aspace_get_myid(&my_aspace_id)))
491                 return status;
492
493         /* Allocate physical memory for the segment */
494         if (!(pmem = alloc_pmem(extent, pagesz, alloc_pmem_arg)))
495                 return -ENOMEM;
496
497         /* Map the segment into the target address space */
498         status =
499         aspace_map_region(
500                 aspace_id,
501                 start,
502                 extent,
503                 elf_pflags_to_vmflags(phdr->p_flags),
504                 pagesz,
505                 "ELF",
506                 pmem
507         );
508         if (status)
509                 return status;
510
511         /* Map the segment into this address space */
512         status =
513         aspace_map_region_anywhere(
514                 my_aspace_id,
515                 &local_start,
516                 extent,
517                 (VM_USER|VM_READ|VM_WRITE),
518                 pagesz,
519                 "temporary",
520                 pmem
521         );
522         if (status)
523                 return status;
524
525         /* Copy segment data from ELF image into the target address space
526          * (via its temporary mapping in our address space) */
527         dst = local_start + (phdr->p_vaddr - start);
528         src = (vaddr_t)elf_image + phdr->p_offset;
529         memcpy((void *)dst, (void *)src, phdr->p_filesz);
530
531         /* Unmap the segment from this address space */
532         status = aspace_del_region(my_aspace_id, local_start, extent);
533         if (status)
534                 return status;
535
536         return 0;
537 }
538
539 static int
540 load_readonly_segment(
541         paddr_t           elf_image_paddr,
542         struct elf_phdr * phdr,
543         id_t              aspace_id,
544         vaddr_t           start,
545         size_t            extent,
546         vmpagesize_t      pagesz
547 )
548 {
549         return aspace_map_region(
550                 aspace_id,
551                 start,
552                 extent,
553                 elf_pflags_to_vmflags(phdr->p_flags),
554                 pagesz,
555                 "ELF (mapped)",
556                 elf_image_paddr +
557                         round_down(phdr->p_offset, pagesz)
558         );
559 }
560
561 /**
562  * Loads an ELF executable image into the specified address space. 
563  *
564  * Arguments:
565  *       [IN]  elf_image:        Location of ELF image in this address space.
566  *       [IN]  elf_image_paddr:  Location of ELF image in physical memory.
567  *       [IN]  aspace_id:        Address space to load ELF image into.
568  *       [IN]  pagesz:           Page size to use when mapping ELF image.
569  *       [IN]  alloc_pmem_arg:   Argument to pass to alloc_pmem().
570  *       [IN]  alloc_pmem:       Function pointer to use to allocate physical
571  *                               memory for the region.  alloc_mem() returns 
572  *                               the physical address of the memory allocated.
573  *
574  * Returns:
575  *       Success: 0
576  *       Failure: Error Code, the target address space is left in an
577  *                undefined state and should be destroyed.
578  */
579 int
580 elf_load_executable(
581         void *       elf_image,
582         paddr_t      elf_image_paddr,
583         id_t         aspace_id,
584         vmpagesize_t pagesz,
585         uintptr_t    alloc_pmem_arg,
586         paddr_t (*alloc_pmem)(size_t size, size_t alignment, uintptr_t arg)
587 )
588 {
589         struct elfhdr *   ehdr;
590         struct elf_phdr * phdr_array;
591         struct elf_phdr * phdr;
592         size_t            i;
593         vaddr_t           start, end;
594         size_t            extent;
595         size_t            num_load_segments=0;
596         int               status;
597
598         /* Locate the program header array (in this context) */
599         ehdr       = elf_image;
600         phdr_array = (struct elf_phdr *)(elf_image + ehdr->e_phoff);
601
602         /* Set up a region for each program segment */
603         for (i = 0; i < ehdr->e_phnum; i++) {
604                 phdr = &phdr_array[i];
605                 if (phdr->p_type != PT_LOAD)
606                         continue;
607
608                 /* Calculate the segment's bounds */
609                 start  = round_down(phdr->p_vaddr, pagesz);
610                 end    = round_up(phdr->p_vaddr + phdr->p_memsz, pagesz);
611                 extent = end - start;
612
613                 if (phdr->p_flags & PF_W) {
614                         /* Writable segments must be copied into the
615                          * target address space */
616                         status =
617                         load_writable_segment(
618                                 elf_image,
619                                 phdr,
620                                 aspace_id,
621                                 start,
622                                 extent,
623                                 pagesz,
624                                 alloc_pmem_arg,
625                                 alloc_pmem
626                         );
627                         if (status)
628                                 return status;
629                 } else {
630                         /* Read-only segments are mapped directly
631                          * from the ELF image */
632                         status =
633                         load_readonly_segment(
634                                 elf_image_paddr,
635                                 phdr,
636                                 aspace_id,
637                                 start,
638                                 extent,
639                                 pagesz
640                         );
641                         if (status)
642                                 return status;
643                 }
644
645                 ++num_load_segments;
646         }
647
648         return (num_load_segments) ? 0 : -ENOENT;
649 }
650
651 static int
652 make_region(
653         id_t         aspace_id,
654         vaddr_t      start,
655         size_t       extent,
656         vmflags_t    flags,
657         vmpagesize_t pagesz,
658         const char * name,
659         uintptr_t    alloc_pmem_arg,
660         paddr_t (*alloc_pmem)(size_t size, size_t alignment, uintptr_t arg),
661         paddr_t *    pmem
662 )
663 {
664         int status;
665
666         *pmem = alloc_pmem(extent, pagesz, alloc_pmem_arg);
667         if (*pmem == 0) {
668                 print("Failed to allocate physical memory for %s.", name);
669                 return -ENOMEM;
670         }
671
672         status = aspace_map_region(aspace_id, start, extent,
673                                    flags, pagesz,
674                                    name, *pmem);
675         if (status) {
676                 print("Failed to map physical memory for %s (status=%d).",
677                        name, status);
678                 return status;
679         }
680
681         return 0;
682 }
683
684 /**
685  * Maximum number of arguments and environment variables that may
686  * be passed to the new task created by elf_load().
687  */
688 #define MAX_ARGC 32
689 #define MAX_ENVC 32
690
691 /**
692  * Kitchen-sink ELF image load function.
693  * If something more custom is desired, this function can be used as a guide
694  * for what needs to be done to load an ELF executable and setup the
695  * accompanying address space.
696  */
697 int
698 elf_load(
699         void *          elf_image,
700         paddr_t         elf_image_paddr,
701         const char *    name,
702         id_t            desired_aspace_id,
703         vmpagesize_t    pagesz,
704         size_t          heap_size,
705         size_t          stack_size,
706         char *          argv_str,
707         char *          envp_str,
708         start_state_t * start_state,
709         uintptr_t       alloc_pmem_arg,
710         paddr_t (*alloc_pmem)(size_t size, size_t alignment, uintptr_t arg)
711 )
712 {
713         int status;
714         char *argv[MAX_ARGC] = { (char *)name };
715         char *envp[MAX_ENVC];
716         id_t my_aspace_id, aspace_id;
717         vaddr_t heap_start, stack_start, stack_end, stack_ptr;
718         vaddr_t local_stack_start;
719         size_t heap_extent, stack_extent;
720         paddr_t heap_pmem, stack_pmem;
721         uint32_t hwcap;
722
723         if (!elf_image || !start_state || !alloc_pmem)
724                 return -EINVAL;
725
726         if (elf_init_str_array(MAX_ARGC-1, argv+1, argv_str)) {
727                 print("Too many ARGV strings.");
728                 return -EINVAL;
729         }
730
731         if (elf_init_str_array(MAX_ENVC, envp, envp_str)) {
732                 print("Too many ENVP strings.");
733                 return -EINVAL;
734         }
735
736         if ((status = aspace_create(desired_aspace_id, "init_task", &aspace_id))) {
737                 print("Failed to create aspace (status=%d).", status);
738                 return status;
739         }
740
741         /* Load the ELF executable's LOAD segments */
742         status =
743         elf_load_executable(
744                 elf_image,       /* where I can access the ELF image */
745                 elf_image_paddr, /* where it is in physical memory */
746                 aspace_id,       /* the address space to map it into */
747                 pagesz,          /* page size to map it with */
748                 0,               /* arg to pass to alloc_pmem */
749                 alloc_pmem       /* func to use to allocate phys mem */
750         );
751         if (status) {
752                 print("Failed to load ELF image (status=%d).", status);
753                 return status;
754         }
755
756         /* Create the UNIX heap */
757         heap_start  = round_up(elf_heap_start(elf_image), pagesz);
758         heap_extent = round_up(heap_size, pagesz);
759         status =
760         make_region(
761                 aspace_id,
762                 heap_start,
763                 heap_extent,
764                 (VM_USER|VM_READ|VM_WRITE|VM_EXEC|VM_HEAP),
765                 pagesz,
766                 "heap",
767                 alloc_pmem_arg,
768                 alloc_pmem,
769                 &heap_pmem
770         );
771         if (status) {
772                 print("Failed to create heap (status=%d).", status);
773                 return status;
774         }
775
776         /* Create the stack region */
777         stack_end    = SMARTMAP_ALIGN;
778         stack_start  = round_down(stack_end - stack_size, pagesz);
779         stack_extent = stack_end - stack_start;
780         status = 
781         make_region(
782                 aspace_id,
783                 stack_start,
784                 stack_extent,
785                 (VM_USER|VM_READ|VM_WRITE|VM_EXEC),
786                 pagesz,
787                 "stack",
788                 alloc_pmem_arg,
789                 alloc_pmem,
790                 &stack_pmem
791         );
792         if (status) {
793                 print("Failed to create stack (status=%d).", status);
794                 return status;
795         }
796
797         /* Map the stack region into this address space */
798         if ((status = aspace_get_myid(&my_aspace_id)))
799                 return status;
800         status =
801         aspace_map_region_anywhere(
802                 my_aspace_id,
803                 &local_stack_start,
804                 stack_extent,
805                 (VM_USER|VM_READ|VM_WRITE),
806                 pagesz,
807                 "temporary",
808                 stack_pmem
809         );
810         if (status) {
811                 print("Failed to map stack locally (status=%d).", status);
812                 return status;
813         }
814
815         /* Initialize the stack */
816         status = elf_hwcap(start_state->cpu_id, &hwcap);
817         if (status) {
818                 print("Failed to get hw capabilities (status=%d).", status);
819                 return status;
820         }
821         status =
822         elf_init_stack(
823                 elf_image,
824                 (void *)local_stack_start,  /* Where I can access it */
825                 stack_start,                /* Where it is in target aspace */
826                 stack_extent,
827                 argv, envp,
828                 start_state->uid, start_state->gid,
829                 hwcap,
830                 &stack_ptr
831         );
832         if (status) {
833                 print("Failed to initialize stack (status=%d).", status);
834                 return status;
835         }
836
837         start_state->aspace_id   = aspace_id;
838         start_state->entry_point = elf_entry_point(elf_image);
839         start_state->stack_ptr   = stack_ptr;
840
841         return 0;
842 }