source: mainline/kernel/generic/src/proc/program.c@ d3109ff

Last change on this file since d3109ff was 3fcea34, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 9 months ago

Simplify the SYS_THREAD_CREATE syscall interface

Removed the beefy uarg structure. Instead, the syscall gets two
parameters: %pc (program counter) and %sp (stack pointer). It starts
a thread with those values in corresponding registers, with no other
fuss whatsoever.

libc initializes threads by storing any other needed arguments on
the stack and retrieving them in thread_entry. Importantly, this
includes the address of the
thread_main function which is now
called indirectly to fix dynamic linking issues on some archs.

There's a bit of weirdness on SPARC and IA-64, because of their
stacked register handling. The current solution is that we require
some space *above* the stack pointer to be available for those
architectures. I think for SPARC, it can be made more normal.

For the remaining ones, we can (probably) just set the initial
%sp to the top edge of the stack. There's some lingering offsets
on some archs just because I didn't want to accidentally break
anything. The initial thread bringup should be functionally
unchanged from the previous state, and no binaries are currently
multithreaded except thread1 test, so there should be minimal
risk of breakage. Naturally, I tested all available emulator
builds, save for msim.

  • Property mode set to 100644
File size: 7.1 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * Copyright (c) 2008 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup kernel_generic_proc
31 * @{
32 */
33
34/**
35 * @file
36 * @brief Running userspace programs.
37 */
38
39#include <main/uinit.h>
40#include <proc/thread.h>
41#include <proc/task.h>
42#include <mm/as.h>
43#include <stdlib.h>
44#include <arch.h>
45#include <adt/list.h>
46#include <ipc/ipc.h>
47#include <ipc/ipcrsc.h>
48#include <security/perm.h>
49#include <lib/elf_load.h>
50#include <str.h>
51#include <str_error.h>
52#include <log.h>
53#include <syscall/copy.h>
54#include <proc/program.h>
55#include <userspace.h>
56
57/**
58 * Points to the binary image used as the program loader. All non-initial
59 * tasks are created from this executable image.
60 */
61void *program_loader = NULL;
62
63/** Create a program using an existing address space.
64 *
65 * @param as Address space containing a binary program image.
66 * @param entry_addr Program entry-point address in program address space.
67 * @param name Name to set for the program's task.
68 * @param prg Buffer for storing program information.
69 *
70 * @return EOK on success or an error code.
71 *
72 */
73errno_t program_create(as_t *as, uspace_addr_t entry_addr, char *name, program_t *prg)
74{
75 uinit_arg_t *kernel_uarg = malloc(sizeof(uinit_arg_t));
76 if (!kernel_uarg)
77 return ENOMEM;
78
79 prg->loader_status = EOK;
80 prg->task = task_create(as, name);
81 if (!prg->task) {
82 free(kernel_uarg);
83 return ELIMIT;
84 }
85
86 /*
87 * Create the stack address space area.
88 */
89 uintptr_t virt = (uintptr_t) AS_AREA_ANY;
90 uintptr_t bound = USER_ADDRESS_SPACE_END - (STACK_SIZE_USER - 1);
91
92 /* Adjust bound to create space for the desired guard page. */
93 bound -= PAGE_SIZE;
94
95 as_area_t *area = as_area_create(as,
96 AS_AREA_READ | AS_AREA_WRITE | AS_AREA_CACHEABLE | AS_AREA_GUARD |
97 AS_AREA_LATE_RESERVE, STACK_SIZE_USER, AS_AREA_ATTR_NONE,
98 &anon_backend, NULL, &virt, bound);
99 if (!area) {
100 free(kernel_uarg);
101 task_release(prg->task);
102 prg->task = NULL;
103 return ENOMEM;
104 }
105
106 kernel_uarg->pc = entry_addr;
107 kernel_uarg->sp = arch_get_initial_sp(virt, STACK_SIZE_USER);
108
109 /*
110 * Create the main thread.
111 */
112 prg->main_thread = thread_create(uinit, kernel_uarg, prg->task,
113 THREAD_FLAG_USPACE, "uinit");
114 if (!prg->main_thread) {
115 free(kernel_uarg);
116 as_area_destroy(as, virt);
117 task_release(prg->task);
118 prg->task = NULL;
119 return ELIMIT;
120 }
121
122 return EOK;
123}
124
125/** Parse an executable image in the kernel memory.
126 *
127 * If the image belongs to a program loader, it is registered as such,
128 * (and *task is set to NULL). Otherwise a task is created from the
129 * executable image. The task is returned in *task.
130 *
131 * @param[in] image_addr Address of an executable program image.
132 * @param[in] name Name to set for the program's task.
133 * @param[out] prg Buffer for storing program info.
134 * If image_addr points to a loader image,
135 * prg->task will be set to NULL and EOK
136 * will be returned.
137 *
138 * @return EOK on success or an error code.
139 *
140 */
141errno_t program_create_from_image(void *image_addr, size_t image_size, char *name, program_t *prg)
142{
143 as_t *as = as_create(0);
144 if (!as)
145 return ENOMEM;
146
147 prg->loader_status = elf_load((elf_header_t *) image_addr, as);
148 if (prg->loader_status != EOK) {
149 as_release(as);
150 prg->task = NULL;
151 prg->main_thread = NULL;
152 return ENOTSUP;
153 }
154
155 errno_t rc = program_create(as, ((elf_header_t *) image_addr)->e_entry,
156 name, prg);
157
158 if (rc == EOK) {
159 prg->task->debug_sections = calloc(1, sizeof(debug_sections_t));
160 if (prg->task->debug_sections != NULL)
161 *prg->task->debug_sections = get_debug_sections(image_addr, image_size);
162 }
163
164 return rc;
165}
166
167/** Create a task from the program loader image.
168 *
169 * @param prg Buffer for storing program info.
170 * @param name Name to set for the program's task.
171 *
172 * @return EOK on success or an error code.
173 *
174 */
175errno_t program_create_loader(program_t *prg, char *name)
176{
177 as_t *as = as_create(0);
178 if (!as)
179 return ENOMEM;
180
181 void *loader = program_loader;
182 if (!loader) {
183 as_release(as);
184 log(LF_OTHER, LVL_ERROR,
185 "Cannot spawn loader as none was registered");
186 return ENOENT;
187 }
188
189 prg->loader_status = elf_load((elf_header_t *) program_loader, as);
190 if (prg->loader_status != EOK) {
191 as_release(as);
192 log(LF_OTHER, LVL_ERROR, "Cannot spawn loader (%s)",
193 str_error(prg->loader_status));
194 return prg->loader_status;
195 }
196
197 return program_create(as, ((elf_header_t *) program_loader)->e_entry,
198 name, prg);
199}
200
201/** Make program ready.
202 *
203 * Switch program's main thread to the ready state.
204 *
205 * @param prg Program to make ready.
206 *
207 */
208void program_ready(program_t *prg)
209{
210 thread_start(prg->main_thread);
211 thread_detach(prg->main_thread);
212 prg->main_thread = NULL;
213}
214
215/** Syscall for creating a new loader instance from userspace.
216 *
217 * Creates a new task from the program loader image and sets
218 * the task name.
219 *
220 * @param uspace_name Name to set on the new task (typically the same
221 * as the command used to execute it).
222 * @param name_len Length of the name.
223 *
224 * @return EOK on success or an error code from @ref errno.h.
225 *
226 */
227sys_errno_t sys_program_spawn_loader(uspace_ptr_char uspace_name, size_t name_len)
228{
229 /* Cap length of name and copy it from userspace. */
230 if (name_len > TASK_NAME_BUFLEN - 1)
231 name_len = TASK_NAME_BUFLEN - 1;
232
233 char namebuf[TASK_NAME_BUFLEN];
234 errno_t rc = copy_from_uspace(namebuf, uspace_name, name_len);
235 if (rc != EOK)
236 return (sys_errno_t) rc;
237
238 namebuf[name_len] = 0;
239
240 /* Spawn the new task. */
241 program_t prg;
242 rc = program_create_loader(&prg, namebuf);
243 if (rc != EOK)
244 return rc;
245
246 // FIXME: control the permissions
247 perm_set(prg.task, perm_get(TASK));
248 program_ready(&prg);
249
250 task_release(prg.task);
251
252 return EOK;
253}
254
255/** @}
256 */
Note: See TracBrowser for help on using the repository browser.