source: mainline/uspace/srv/loader/main.c@ 9d20ea8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9d20ea8 was 0993087, checked in by Jiri Svoboda <jirik.svoboda@…>, 17 years ago

Fix bugs in getting task ID from loader (was breaking ppc32).

  • Property mode set to 100644
File size: 8.4 KB
Line 
1/*
2 * Copyright (c) 2008 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup loader
30 * @brief Loads and runs programs from VFS.
31 * @{
32 */
33/**
34 * @file
35 * @brief Loads and runs programs from VFS.
36 *
37 * The program loader is a special init binary. Its image is used
38 * to create a new task upon a @c task_spawn syscall. The syscall
39 * returns the id of a phone connected to the newly created task.
40 *
41 * The caller uses this phone to send the pathname and various other
42 * information to the loader. This is normally done by the C library
43 * and completely hidden from applications.
44 */
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <bool.h>
50#include <fcntl.h>
51#include <sys/types.h>
52#include <ipc/ipc.h>
53#include <ipc/loader.h>
54#include <loader/pcb.h>
55#include <errno.h>
56#include <async.h>
57#include <as.h>
58
59#include <elf.h>
60#include <elf_load.h>
61
62/**
63 * Bias used for loading the dynamic linker. This will be soon replaced
64 * by automatic placement.
65 */
66#define RTLD_BIAS 0x80000
67
68/** Pathname of the file that will be loaded */
69static char *pathname = NULL;
70
71/** The Program control block */
72static pcb_t pcb;
73
74/** Number of arguments */
75static int argc = 0;
76/** Argument vector */
77static char **argv = NULL;
78/** Buffer holding all arguments */
79static char *arg_buf = NULL;
80
81static elf_info_t prog_info;
82static elf_info_t interp_info;
83
84static bool is_dyn_linked;
85
86
87static void loader_get_taskid(ipc_callid_t rid, ipc_call_t *request)
88{
89 ipc_callid_t callid;
90 task_id_t task_id;
91 size_t len;
92
93 task_id = task_get_id();
94
95 if (!ipc_data_read_receive(&callid, &len)) {
96 ipc_answer_0(callid, EINVAL);
97 ipc_answer_0(rid, EINVAL);
98 return;
99 }
100
101 if (len > sizeof(task_id)) len = sizeof(task_id);
102
103 ipc_data_read_finalize(callid, &task_id, len);
104 ipc_answer_0(rid, EOK);
105}
106
107
108/** Receive a call setting pathname of the program to execute.
109 *
110 * @param rid
111 * @param request
112 */
113static void loader_set_pathname(ipc_callid_t rid, ipc_call_t *request)
114{
115 ipc_callid_t callid;
116 size_t len;
117 char *name_buf;
118
119 if (!ipc_data_write_receive(&callid, &len)) {
120 ipc_answer_0(callid, EINVAL);
121 ipc_answer_0(rid, EINVAL);
122 return;
123 }
124
125 name_buf = malloc(len + 1);
126 if (!name_buf) {
127 ipc_answer_0(callid, ENOMEM);
128 ipc_answer_0(rid, ENOMEM);
129 return;
130 }
131
132 ipc_data_write_finalize(callid, name_buf, len);
133 ipc_answer_0(rid, EOK);
134
135 if (pathname != NULL) {
136 free(pathname);
137 pathname = NULL;
138 }
139
140 name_buf[len] = '\0';
141 pathname = name_buf;
142}
143
144/** Receive a call setting arguments of the program to execute.
145 *
146 * @param rid
147 * @param request
148 */
149static void loader_set_args(ipc_callid_t rid, ipc_call_t *request)
150{
151 ipc_callid_t callid;
152 size_t buf_len, arg_len;
153 char *p;
154 int n;
155
156 if (!ipc_data_write_receive(&callid, &buf_len)) {
157 ipc_answer_0(callid, EINVAL);
158 ipc_answer_0(rid, EINVAL);
159 return;
160 }
161
162 if (arg_buf != NULL) {
163 free(arg_buf);
164 arg_buf = NULL;
165 }
166
167 if (argv != NULL) {
168 free(argv);
169 argv = NULL;
170 }
171
172 arg_buf = malloc(buf_len + 1);
173 if (!arg_buf) {
174 ipc_answer_0(callid, ENOMEM);
175 ipc_answer_0(rid, ENOMEM);
176 return;
177 }
178
179 ipc_data_write_finalize(callid, arg_buf, buf_len);
180 ipc_answer_0(rid, EOK);
181
182 arg_buf[buf_len] = '\0';
183
184 /*
185 * Count number of arguments
186 */
187 p = arg_buf;
188 n = 0;
189 while (p < arg_buf + buf_len) {
190 arg_len = strlen(p);
191 p = p + arg_len + 1;
192 ++n;
193 }
194
195 /* Allocate argv */
196 argv = malloc((n + 1) * sizeof(char *));
197
198 if (argv == NULL) {
199 free(arg_buf);
200 ipc_answer_0(callid, ENOMEM);
201 ipc_answer_0(rid, ENOMEM);
202 return;
203 }
204
205 /*
206 * Fill argv with argument pointers
207 */
208 p = arg_buf;
209 n = 0;
210 while (p < arg_buf + buf_len) {
211 argv[n] = p;
212
213 arg_len = strlen(p);
214 p = p + arg_len + 1;
215 ++n;
216 }
217
218 argc = n;
219 argv[n] = NULL;
220}
221
222/** Load the previously selected program.
223 *
224 * @param rid
225 * @param request
226 * @return 0 on success, !0 on error.
227 */
228static int loader_load(ipc_callid_t rid, ipc_call_t *request)
229{
230 int rc;
231
232// printf("Load program '%s'\n", pathname);
233
234 rc = elf_load_file(pathname, 0, &prog_info);
235 if (rc < 0) {
236 printf("failed to load program\n");
237 ipc_answer_0(rid, EINVAL);
238 return 1;
239 }
240
241// printf("Create PCB\n");
242 elf_create_pcb(&prog_info, &pcb);
243
244 pcb.argc = argc;
245 pcb.argv = argv;
246
247 if (prog_info.interp == NULL) {
248 /* Statically linked program */
249// printf("Run statically linked program\n");
250// printf("entry point: 0x%llx\n", prog_info.entry);
251 is_dyn_linked = false;
252 ipc_answer_0(rid, EOK);
253 return 0;
254 }
255
256 printf("Load dynamic linker '%s'\n", prog_info.interp);
257 rc = elf_load_file("/rtld.so", RTLD_BIAS, &interp_info);
258 if (rc < 0) {
259 printf("failed to load dynamic linker\n");
260 ipc_answer_0(rid, EINVAL);
261 return 1;
262 }
263
264 /*
265 * Provide dynamic linker with some useful data
266 */
267 pcb.rtld_dynamic = interp_info.dynamic;
268 pcb.rtld_bias = RTLD_BIAS;
269
270 is_dyn_linked = true;
271 ipc_answer_0(rid, EOK);
272
273 return 0;
274}
275
276
277/** Run the previously loaded program.
278 *
279 * @param rid
280 * @param request
281 * @return 0 on success, !0 on error.
282 */
283static void loader_run(ipc_callid_t rid, ipc_call_t *request)
284{
285 if (is_dyn_linked == true) {
286 /* Dynamically linked program */
287 printf("run dynamic linker\n");
288 printf("entry point: 0x%llx\n", interp_info.entry);
289 close_console();
290
291 ipc_answer_0(rid, EOK);
292 elf_run(&interp_info, &pcb);
293
294 } else {
295 /* Statically linked program */
296 close_console();
297 ipc_answer_0(rid, EOK);
298 elf_run(&prog_info, &pcb);
299 }
300
301 /* Not reached */
302}
303
304/** Handle loader connection.
305 *
306 * Receive and carry out commands (of which the last one should be
307 * to execute the loaded program).
308 */
309static void loader_connection(ipc_callid_t iid, ipc_call_t *icall)
310{
311 ipc_callid_t callid;
312 ipc_call_t call;
313 int retval;
314
315 /* Ignore parameters, the connection is already open */
316 (void)iid; (void)icall;
317
318 while (1) {
319 callid = async_get_call(&call);
320
321 switch (IPC_GET_METHOD(call)) {
322 case LOADER_GET_TASKID:
323 loader_get_taskid(callid, &call);
324 continue;
325 case LOADER_SET_PATHNAME:
326 loader_set_pathname(callid, &call);
327 continue;
328 case LOADER_SET_ARGS:
329 loader_set_args(callid, &call);
330 continue;
331 case LOADER_LOAD:
332 loader_load(callid, &call);
333 continue;
334 case LOADER_RUN:
335 loader_run(callid, &call);
336 /* Not reached */
337 default:
338 retval = ENOENT;
339 break;
340 }
341 if ((callid & IPC_CALLID_NOTIFICATION) == 0 &&
342 IPC_GET_METHOD(call) != IPC_M_PHONE_HUNGUP) {
343 printf("responding EINVAL to method %d\n",
344 IPC_GET_METHOD(call));
345 ipc_answer_0(callid, EINVAL);
346 }
347 }
348}
349
350/** Program loader main function.
351 */
352int main(int argc, char *argv[])
353{
354 ipc_callid_t callid;
355 ipc_call_t call;
356 ipcarg_t phone_hash;
357
358 /* The first call only communicates the incoming phone hash */
359 callid = ipc_wait_for_call(&call);
360
361 if (IPC_GET_METHOD(call) != LOADER_HELLO) {
362 if (IPC_GET_METHOD(call) != IPC_M_PHONE_HUNGUP)
363 ipc_answer_0(callid, EINVAL);
364 return 1;
365 }
366
367 ipc_answer_0(callid, EOK);
368 phone_hash = call.in_phone_hash;
369
370 /*
371 * Up until now async must not be used as it couldn't
372 * handle incoming requests. (Which means e.g. printf()
373 * cannot be used)
374 */
375 async_new_connection(phone_hash, 0, NULL, loader_connection);
376 async_manager();
377
378 /* not reached */
379 return 0;
380}
381
382/** @}
383 */
Note: See TracBrowser for help on using the repository browser.