1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6
7 #include <assert.h>
8 #include <err.h>
9 #include <inttypes.h>
10 #include <platform.h>
11 #include <trace.h>
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #include <kernel/cmdline.h>
18 #include <vm/vm_object_paged.h>
19 #include <lib/console.h>
20 #include <lib/vdso.h>
21 #include <lk/init.h>
22 #include <mexec.h>
23 #include <object/channel_dispatcher.h>
24 #include <object/handle.h>
25 #include <object/job_dispatcher.h>
26 #include <object/message_packet.h>
27 #include <object/process_dispatcher.h>
28 #include <object/resource_dispatcher.h>
29 #include <object/thread_dispatcher.h>
30 #include <object/vm_address_region_dispatcher.h>
31 #include <object/vm_object_dispatcher.h>
32
33 #include <zircon/processargs.h>
34 #include <zircon/stack.h>
35
36 #if ENABLE_ENTROPY_COLLECTOR_TEST
37 #include <lib/crypto/entropy/quality_test.h>
38 #endif
39
40 static const size_t stack_size = ZIRCON_DEFAULT_STACK_SIZE;
41
42 #define STACK_VMO_NAME "userboot-initial-stack"
43 #define RAMDISK_VMO_NAME "userboot-raw-ramdisk"
44 #define CRASHLOG_VMO_NAME "crashlog"
45
46 namespace {
47
48 #include "userboot-code.h"
49
50 // This is defined in assembly by userboot-image.S; userboot-code.h
51 // gives details about the image's size and layout.
52 extern "C" const char userboot_image[];
53
54 class UserbootImage : private RoDso {
55 public:
UserbootImage(const VDso * vdso)56 explicit UserbootImage(const VDso* vdso)
57 : RoDso("userboot", userboot_image,
58 USERBOOT_CODE_END, USERBOOT_CODE_START),
59 vdso_(vdso) {}
60
61 // The whole userboot image consists of the userboot rodso image
62 // immediately followed by the vDSO image. This returns the size
63 // of that combined image.
size() const64 size_t size() const {
65 return RoDso::size() + vdso_->size();
66 }
67
Map(fbl::RefPtr<VmAddressRegionDispatcher> root_vmar,uintptr_t * vdso_base,uintptr_t * entry)68 zx_status_t Map(fbl::RefPtr<VmAddressRegionDispatcher> root_vmar,
69 uintptr_t* vdso_base, uintptr_t* entry) {
70 // Create a VMAR (placed anywhere) to hold the combined image.
71 fbl::RefPtr<VmAddressRegionDispatcher> vmar;
72 zx_rights_t vmar_rights;
73 zx_status_t status = root_vmar->Allocate(0, size(),
74 ZX_VM_CAN_MAP_READ |
75 ZX_VM_CAN_MAP_WRITE |
76 ZX_VM_CAN_MAP_EXECUTE |
77 ZX_VM_CAN_MAP_SPECIFIC,
78 &vmar, &vmar_rights);
79 if (status != ZX_OK)
80 return status;
81
82 // Map userboot proper.
83 status = RoDso::Map(vmar, 0);
84 if (status == ZX_OK) {
85 *entry = vmar->vmar()->base() + USERBOOT_ENTRY;
86
87 // Map the vDSO right after it.
88 *vdso_base = vmar->vmar()->base() + RoDso::size();
89 status = vdso_->Map(ktl::move(vmar), RoDso::size());
90 }
91 return status;
92 }
93
94 private:
95 const VDso* vdso_;
96 };
97
98 } // anonymous namespace
99
100
101 // Get a handle to a VM object, with full rights except perhaps for writing.
get_vmo_handle(fbl::RefPtr<VmObject> vmo,bool readonly,fbl::RefPtr<VmObjectDispatcher> * disp_ptr,Handle ** ptr)102 static zx_status_t get_vmo_handle(fbl::RefPtr<VmObject> vmo, bool readonly,
103 fbl::RefPtr<VmObjectDispatcher>* disp_ptr,
104 Handle** ptr) {
105 if (!vmo)
106 return ZX_ERR_NO_MEMORY;
107 zx_rights_t rights;
108 fbl::RefPtr<Dispatcher> dispatcher;
109 zx_status_t result = VmObjectDispatcher::Create(
110 ktl::move(vmo), &dispatcher, &rights);
111 if (result == ZX_OK) {
112 if (disp_ptr)
113 *disp_ptr = fbl::RefPtr<VmObjectDispatcher>::Downcast(dispatcher);
114 if (readonly)
115 rights &= ~ZX_RIGHT_WRITE;
116 if (ptr)
117 *ptr = Handle::Make(ktl::move(dispatcher), rights).release();
118 }
119 return result;
120 }
121
get_job_handle(Handle ** ptr)122 static zx_status_t get_job_handle(Handle** ptr) {
123 zx_rights_t rights;
124 fbl::RefPtr<Dispatcher> dispatcher;
125 zx_status_t result = JobDispatcher::Create(
126 0u, GetRootJobDispatcher(), &dispatcher, &rights);
127 if (result == ZX_OK)
128 *ptr = Handle::Make(ktl::move(dispatcher), rights).release();
129 return result;
130 }
131
get_resource_handle(Handle ** ptr)132 static zx_status_t get_resource_handle(Handle** ptr) {
133 zx_rights_t rights;
134 fbl::RefPtr<ResourceDispatcher> root;
135 zx_status_t result = ResourceDispatcher::Create(&root, &rights, ZX_RSRC_KIND_ROOT, 0, 0, 0,
136 "root");
137 if (result == ZX_OK)
138 *ptr = Handle::Make(fbl::RefPtr<Dispatcher>(root.get()),
139 rights).release();
140 return result;
141 }
142
143 // Create a channel and write the bootstrap message down one side of
144 // it, returning the handle to the other side.
make_bootstrap_channel(fbl::RefPtr<ProcessDispatcher> process,MessagePacketPtr msg,zx_handle_t * out)145 static zx_status_t make_bootstrap_channel(
146 fbl::RefPtr<ProcessDispatcher> process,
147 MessagePacketPtr msg,
148 zx_handle_t* out) {
149 HandleOwner user_channel_handle;
150 fbl::RefPtr<ChannelDispatcher> kernel_channel;
151 *out = ZX_HANDLE_INVALID;
152 {
153 fbl::RefPtr<Dispatcher> mpd0, mpd1;
154 zx_rights_t rights;
155 zx_status_t status = ChannelDispatcher::Create(&mpd0, &mpd1, &rights);
156 if (status != ZX_OK)
157 return status;
158 user_channel_handle = Handle::Make(ktl::move(mpd0), rights);
159 kernel_channel = DownCastDispatcher<ChannelDispatcher>(&mpd1);
160 }
161
162 // Here it goes!
163 zx_status_t status = kernel_channel->Write(ZX_KOID_INVALID, ktl::move(msg));
164 if (status != ZX_OK)
165 return status;
166
167 zx_handle_t hv = process->MapHandleToValue(user_channel_handle);
168 process->AddHandle(ktl::move(user_channel_handle));
169
170 *out = hv;
171 return ZX_OK;
172 }
173
174 enum bootstrap_handle_index {
175 BOOTSTRAP_VDSO,
176 BOOTSTRAP_VDSO_LAST_VARIANT = BOOTSTRAP_VDSO + VDso::variants() - 1,
177 BOOTSTRAP_RAMDISK,
178 BOOTSTRAP_RESOURCE_ROOT,
179 BOOTSTRAP_STACK,
180 BOOTSTRAP_PROC,
181 BOOTSTRAP_THREAD,
182 BOOTSTRAP_JOB,
183 BOOTSTRAP_VMAR_ROOT,
184 BOOTSTRAP_CRASHLOG,
185 #if ENABLE_ENTROPY_COLLECTOR_TEST
186 BOOTSTRAP_ENTROPY_FILE,
187 #endif
188 BOOTSTRAP_HANDLES
189 };
190
191 struct bootstrap_message {
192 zx_proc_args_t header;
193 uint32_t handle_info[BOOTSTRAP_HANDLES];
194 char cmdline[CMDLINE_MAX];
195 };
196
prepare_bootstrap_message()197 static MessagePacketPtr prepare_bootstrap_message() {
198 const size_t data_size =
199 offsetof(struct bootstrap_message, cmdline) +
200 __kernel_cmdline_size;
201 bootstrap_message* msg =
202 static_cast<bootstrap_message*>(malloc(data_size));
203 if (msg == nullptr) {
204 return nullptr;
205 }
206
207 memset(&msg->header, 0, sizeof(msg->header));
208 msg->header.protocol = ZX_PROCARGS_PROTOCOL;
209 msg->header.version = ZX_PROCARGS_VERSION;
210 msg->header.environ_off = offsetof(struct bootstrap_message, cmdline);
211 msg->header.environ_num = static_cast<uint32_t>(__kernel_cmdline_count);
212 msg->header.handle_info_off =
213 offsetof(struct bootstrap_message, handle_info);
214 for (int i = 0; i < BOOTSTRAP_HANDLES; ++i) {
215 uint32_t info = 0;
216 switch (static_cast<bootstrap_handle_index>(i)) {
217 case BOOTSTRAP_VDSO ... BOOTSTRAP_VDSO_LAST_VARIANT:
218 info = PA_HND(PA_VMO_VDSO, i - BOOTSTRAP_VDSO);
219 break;
220 case BOOTSTRAP_RAMDISK:
221 info = PA_HND(PA_VMO_BOOTDATA, 0);
222 break;
223 case BOOTSTRAP_RESOURCE_ROOT:
224 info = PA_HND(PA_RESOURCE, 0);
225 break;
226 case BOOTSTRAP_STACK:
227 info = PA_HND(PA_VMO_STACK, 0);
228 break;
229 case BOOTSTRAP_PROC:
230 info = PA_HND(PA_PROC_SELF, 0);
231 break;
232 case BOOTSTRAP_THREAD:
233 info = PA_HND(PA_THREAD_SELF, 0);
234 break;
235 case BOOTSTRAP_JOB:
236 info = PA_HND(PA_JOB_DEFAULT, 0);
237 break;
238 case BOOTSTRAP_VMAR_ROOT:
239 info = PA_HND(PA_VMAR_ROOT, 0);
240 break;
241 case BOOTSTRAP_CRASHLOG:
242 info = PA_HND(PA_VMO_KERNEL_FILE, 0);
243 break;
244 #if ENABLE_ENTROPY_COLLECTOR_TEST
245 case BOOTSTRAP_ENTROPY_FILE:
246 info = PA_HND(PA_VMO_KERNEL_FILE, 1);
247 break;
248 #endif
249 case BOOTSTRAP_HANDLES:
250 __builtin_unreachable();
251 }
252 msg->handle_info[i] = info;
253 }
254 memcpy(msg->cmdline, __kernel_cmdline, __kernel_cmdline_size);
255
256 MessagePacketPtr packet;
257 uint32_t num_handles = BOOTSTRAP_HANDLES;
258 zx_status_t status =
259 MessagePacket::Create(msg, static_cast<uint32_t>(data_size), num_handles, &packet);
260 free(msg);
261 if (status != ZX_OK) {
262 return nullptr;
263 }
264 return packet;
265 }
266
clog_to_vmo(const void * data,size_t off,size_t len,void * cookie)267 static void clog_to_vmo(const void* data, size_t off, size_t len, void* cookie) {
268 VmObject* vmo = static_cast<VmObject*>(cookie);
269 vmo->Write(data, off, len);
270 }
271
272 // Converts platform crashlog into a VMO
crashlog_to_vmo(fbl::RefPtr<VmObject> * out)273 static zx_status_t crashlog_to_vmo(fbl::RefPtr<VmObject>* out) {
274 size_t size = platform_recover_crashlog(0, NULL, NULL);
275 fbl::RefPtr<VmObject> crashlog_vmo;
276 zx_status_t status = VmObjectPaged::Create(PMM_ALLOC_FLAG_ANY, 0u, size, &crashlog_vmo);
277 if (status != ZX_OK) {
278 return status;
279 }
280 platform_recover_crashlog(size, crashlog_vmo.get(), clog_to_vmo);
281 crashlog_vmo->set_name(CRASHLOG_VMO_NAME, sizeof(CRASHLOG_VMO_NAME) - 1);
282 mexec_stash_crashlog(crashlog_vmo);
283 *out = ktl::move(crashlog_vmo);
284 return ZX_OK;
285 }
286
attempt_userboot()287 static zx_status_t attempt_userboot() {
288 size_t rsize;
289 void* rbase = platform_get_ramdisk(&rsize);
290 if (rbase)
291 dprintf(INFO, "userboot: ramdisk %#15zx @ %p\n", rsize, rbase);
292
293 fbl::RefPtr<VmObject> stack_vmo;
294 zx_status_t status = VmObjectPaged::Create(PMM_ALLOC_FLAG_ANY, 0u, stack_size, &stack_vmo);
295 if (status != ZX_OK)
296 return status;
297 stack_vmo->set_name(STACK_VMO_NAME, sizeof(STACK_VMO_NAME) - 1);
298
299 fbl::RefPtr<VmObject> rootfs_vmo;
300 status = VmObjectPaged::CreateFromROData(rbase, rsize, &rootfs_vmo);
301 if (status != ZX_OK)
302 return status;
303 rootfs_vmo->set_name(RAMDISK_VMO_NAME, sizeof(RAMDISK_VMO_NAME) - 1);
304
305 fbl::RefPtr<VmObject> crashlog_vmo;
306 status = crashlog_to_vmo(&crashlog_vmo);
307 if (status != ZX_OK)
308 return status;
309
310 // Prepare the bootstrap message packet. This puts its data (the
311 // kernel command line) in place, and allocates space for its handles.
312 // We'll fill in the handles as we create things.
313 MessagePacketPtr msg = prepare_bootstrap_message();
314 if (!msg)
315 return ZX_ERR_NO_MEMORY;
316
317 Handle** const handles = msg->mutable_handles();
318 DEBUG_ASSERT(msg->num_handles() == BOOTSTRAP_HANDLES);
319 status = get_vmo_handle(rootfs_vmo, false, nullptr,
320 &handles[BOOTSTRAP_RAMDISK]);
321 fbl::RefPtr<VmObjectDispatcher> stack_vmo_dispatcher;
322 if (status == ZX_OK)
323 status = get_vmo_handle(stack_vmo, false, &stack_vmo_dispatcher,
324 &handles[BOOTSTRAP_STACK]);
325 if (status == ZX_OK)
326 status = get_vmo_handle(crashlog_vmo, true, nullptr,
327 &handles[BOOTSTRAP_CRASHLOG]);
328 if (status == ZX_OK)
329 status = get_resource_handle(&handles[BOOTSTRAP_RESOURCE_ROOT]);
330
331 if (status == ZX_OK)
332 status = get_job_handle(&handles[BOOTSTRAP_JOB]);
333
334 #if ENABLE_ENTROPY_COLLECTOR_TEST
335 if (status == ZX_OK) {
336 if (crypto::entropy::entropy_was_lost) {
337 status = ZX_ERR_INTERNAL;
338 } else {
339 status = get_vmo_handle(
340 crypto::entropy::entropy_vmo,
341 /* readonly */ true, /* disp_ptr */ nullptr,
342 &handles[BOOTSTRAP_ENTROPY_FILE]);
343 }
344 }
345 #endif
346 if (status != ZX_OK)
347 return status;
348
349 fbl::RefPtr<Dispatcher> proc_disp;
350 fbl::RefPtr<VmAddressRegionDispatcher> vmar;
351 zx_rights_t rights, vmar_rights;
352 status = ProcessDispatcher::Create(GetRootJobDispatcher(), "userboot", 0,
353 &proc_disp, &rights,
354 &vmar, &vmar_rights);
355 if (status != ZX_OK)
356 return status;
357
358 handles[BOOTSTRAP_PROC] = Handle::Make(proc_disp, rights).release();
359
360 auto proc = DownCastDispatcher<ProcessDispatcher>(&proc_disp);
361 ASSERT(proc);
362
363 handles[BOOTSTRAP_VMAR_ROOT] = Handle::Make(vmar, vmar_rights).release();
364
365 const VDso* vdso = VDso::Create();
366 for (size_t i = BOOTSTRAP_VDSO; i <= BOOTSTRAP_VDSO_LAST_VARIANT; ++i) {
367 HandleOwner vmo_handle =
368 vdso->vmo_handle(static_cast<VDso::Variant>(i - BOOTSTRAP_VDSO));
369 handles[i] = vmo_handle.release();
370 }
371
372 UserbootImage userboot(vdso);
373 uintptr_t vdso_base = 0;
374 uintptr_t entry = 0;
375 status = userboot.Map(vmar, &vdso_base, &entry);
376 if (status != ZX_OK)
377 return status;
378
379 // Map the stack anywhere.
380 fbl::RefPtr<VmMapping> stack_mapping;
381 status = vmar->Map(0,
382 ktl::move(stack_vmo), 0, stack_size,
383 ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
384 &stack_mapping);
385 if (status != ZX_OK)
386 return status;
387
388 uintptr_t stack_base = stack_mapping->base();
389 uintptr_t sp = compute_initial_stack_pointer(stack_base, stack_size);
390
391 // Create the user thread and stash its handle for the bootstrap message.
392 fbl::RefPtr<ThreadDispatcher> thread;
393 {
394 fbl::RefPtr<Dispatcher> ut_disp;
395 // Make a copy of proc, as we need to a keep a copy for the
396 // bootstrap message.
397 status = ThreadDispatcher::Create(proc, 0, "userboot", &ut_disp, &rights);
398 if (status != ZX_OK)
399 return status;
400 handles[BOOTSTRAP_THREAD] = Handle::Make(ut_disp, rights).release();
401 thread = DownCastDispatcher<ThreadDispatcher>(&ut_disp);
402 }
403 DEBUG_ASSERT(thread);
404
405 // All the handles are in place, so we can send the bootstrap message.
406 zx_handle_t hv;
407 status = make_bootstrap_channel(proc, ktl::move(msg), &hv);
408 if (status != ZX_OK)
409 return status;
410
411 dprintf(SPEW, "userboot: %-23s @ %#" PRIxPTR "\n", "entry point", entry);
412
413 // Start the process's initial thread.
414 status = thread->Start(entry, sp, static_cast<uintptr_t>(hv), vdso_base,
415 /* initial_thread= */ true);
416 if (status != ZX_OK) {
417 printf("userboot: failed to start initial thread: %d\n", status);
418 return status;
419 }
420
421 return ZX_OK;
422 }
423
userboot_init(uint level)424 void userboot_init(uint level) {
425 attempt_userboot();
426 }
427
428 LK_INIT_HOOK(userboot, userboot_init, LK_INIT_LEVEL_USER);
429