xref: /minix3/minix/servers/vm/main.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1 
2 #define _SYSTEM		1
3 
4 #include <minix/callnr.h>
5 #include <minix/com.h>
6 #include <minix/config.h>
7 #include <minix/const.h>
8 #include <minix/ds.h>
9 #include <minix/endpoint.h>
10 #include <minix/minlib.h>
11 #include <minix/type.h>
12 #include <minix/ipc.h>
13 #include <minix/sysutil.h>
14 #include <minix/syslib.h>
15 #include <minix/const.h>
16 #include <minix/bitmap.h>
17 #include <minix/rs.h>
18 #include <minix/vfsif.h>
19 
20 #include <sys/exec.h>
21 
22 #include <libexec.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <env.h>
27 #include <stdio.h>
28 #include <assert.h>
29 
30 #define _MAIN 1
31 #include "glo.h"
32 #include "proto.h"
33 #include "util.h"
34 #include "vm.h"
35 #include "sanitycheck.h"
36 
37 extern int missing_spares;
38 
39 #include <machine/archtypes.h>
40 #include <sys/param.h>
41 #include "kernel/const.h"
42 #include "kernel/config.h"
43 #include "kernel/proc.h"
44 
45 #include <signal.h>
46 #include <lib.h>
47 
48 /* Table of calls and a macro to test for being in range. */
49 struct {
50 	int (*vmc_func)(message *);	/* Call handles message. */
51 	const char *vmc_name;			/* Human-readable string. */
52 } vm_calls[NR_VM_CALLS];
53 
54 /* Macro to verify call range and map 'high' range to 'base' range
55  * (starting at 0) in one. Evaluates to zero-based call number if call
56  * number is valid, returns -1 otherwise.
57  */
58 #define CALLNUMBER(c) (((c) >= VM_RQ_BASE && 				\
59 			(c) < VM_RQ_BASE + ELEMENTS(vm_calls)) ?	\
60 			((c) - VM_RQ_BASE) : -1)
61 
62 static int map_service(struct rprocpub *rpub);
63 static int do_rs_init(message *m);
64 
65 /* SEF functions and variables. */
66 static void sef_cb_signal_handler(int signo);
67 
68 void init_vm(void);
69 
70 /*===========================================================================*
71  *				main					     *
72  *===========================================================================*/
73 int main(void)
74 {
75   message msg;
76   int result, who_e, rcv_sts;
77   int caller_slot;
78 
79   /* Initialize system so that all processes are runnable */
80   init_vm();
81 
82   /* Register init callbacks. */
83   sef_setcb_init_restart(sef_cb_init_fail);
84   sef_setcb_signal_handler(sef_cb_signal_handler);
85 
86   /* Let SEF perform startup. */
87   sef_startup();
88 
89   SANITYCHECK(SCL_TOP);
90 
91   /* This is VM's main loop. */
92   while (TRUE) {
93 	int r, c;
94 	int type;
95 	int transid = 0;	/* VFS transid if any */
96 
97 	SANITYCHECK(SCL_TOP);
98 	if(missing_spares > 0) {
99 		alloc_cycle();	/* mem alloc code wants to be called */
100 	}
101 
102   	if ((r=sef_receive_status(ANY, &msg, &rcv_sts)) != OK)
103 		panic("sef_receive_status() error: %d", r);
104 
105 	if (is_ipc_notify(rcv_sts)) {
106 		/* Unexpected ipc_notify(). */
107 		printf("VM: ignoring ipc_notify() from %d\n", msg.m_source);
108 		continue;
109 	}
110 	who_e = msg.m_source;
111 	if(vm_isokendpt(who_e, &caller_slot) != OK)
112 		panic("invalid caller %d", who_e);
113 
114 	/* We depend on this being false for the initialized value. */
115 	assert(!IS_VFS_FS_TRANSID(transid));
116 
117 	type = msg.m_type;
118 	c = CALLNUMBER(type);
119 	result = ENOSYS; /* Out of range or restricted calls return this. */
120 
121 	transid = TRNS_GET_ID(msg.m_type);
122 
123 	if((msg.m_source == VFS_PROC_NR) && IS_VFS_FS_TRANSID(transid)) {
124 		/* If it's a request from VFS, it might have a transaction id. */
125 		msg.m_type = TRNS_DEL_ID(msg.m_type);
126 
127 		/* Calls that use the transid */
128 		result = do_procctl(&msg, transid);
129 	} else if(msg.m_type == RS_INIT && msg.m_source == RS_PROC_NR) {
130 		result = do_rs_init(&msg);
131 	} else if (msg.m_type == VM_PAGEFAULT) {
132 		if (!IPC_STATUS_FLAGS_TEST(rcv_sts, IPC_FLG_MSG_FROM_KERNEL)) {
133 			printf("VM: process %d faked VM_PAGEFAULT "
134 					"message!\n", msg.m_source);
135 		}
136 		do_pagefaults(&msg);
137 		/*
138 		 * do not reply to this call, the caller is unblocked by
139 		 * a sys_vmctl() call in do_pagefaults if success. VM panics
140 		 * otherwise
141 		 */
142 		continue;
143 	} else if(c < 0 || !vm_calls[c].vmc_func) {
144 		/* out of range or missing callnr */
145 	} else {
146 		if (acl_check(&vmproc[caller_slot], c) != OK) {
147 			printf("VM: unauthorized %s by %d\n",
148 					vm_calls[c].vmc_name, who_e);
149 		} else {
150 			SANITYCHECK(SCL_FUNCTIONS);
151 			result = vm_calls[c].vmc_func(&msg);
152 			SANITYCHECK(SCL_FUNCTIONS);
153 		}
154 	}
155 
156 	/* Send reply message, unless the return code is SUSPEND,
157 	 * which is a pseudo-result suppressing the reply message.
158 	 */
159 	if(result != SUSPEND) {
160 		msg.m_type = result;
161 
162 		assert(!IS_VFS_FS_TRANSID(transid));
163 
164 		if((r=ipc_send(who_e, &msg)) != OK) {
165 			printf("VM: couldn't send %d to %d (err %d)\n",
166 				msg.m_type, who_e, r);
167 			panic("ipc_send() error");
168 		}
169 	}
170   }
171   return(OK);
172 }
173 
174 static int do_rs_init(message *m)
175 {
176 	int s, i;
177 	static struct rprocpub rprocpub[NR_BOOT_PROCS];
178 
179 	/* Map all the services in the boot image. */
180 	if((s = sys_safecopyfrom(RS_PROC_NR, m->m_rs_init.rproctab_gid, 0,
181 		(vir_bytes) rprocpub, sizeof(rprocpub))) != OK) {
182 		panic("vm: sys_safecopyfrom (rs) failed: %d", s);
183 	}
184 
185 	for(i=0;i < NR_BOOT_PROCS;i++) {
186 		if(rprocpub[i].in_use) {
187 			if((s = map_service(&rprocpub[i])) != OK) {
188 				panic("unable to map service: %d", s);
189 			}
190 		}
191 	}
192 
193 	/* RS expects this response that it then again wants to reply to: */
194 	m->m_rs_init.result = OK;
195 	ipc_sendrec(RS_PROC_NR, m);
196 
197 	return(SUSPEND);
198 }
199 
200 static struct vmproc *init_proc(endpoint_t ep_nr)
201 {
202 	static struct boot_image *ip;
203 
204 	for (ip = &kernel_boot_info.boot_procs[0];
205 		ip < &kernel_boot_info.boot_procs[NR_BOOT_PROCS]; ip++) {
206 		struct vmproc *vmp;
207 
208 		if(ip->proc_nr != ep_nr) continue;
209 
210 		if(ip->proc_nr >= _NR_PROCS || ip->proc_nr < 0)
211 			panic("proc: %d", ip->proc_nr);
212 
213 		vmp = &vmproc[ip->proc_nr];
214 		assert(!(vmp->vm_flags & VMF_INUSE));	/* no double procs */
215 		clear_proc(vmp);
216 		vmp->vm_flags = VMF_INUSE;
217 		vmp->vm_endpoint = ip->endpoint;
218 		vmp->vm_boot = ip;
219 
220 		return vmp;
221 	}
222 
223 	panic("no init_proc");
224 }
225 
226 struct vm_exec_info {
227 	struct exec_info execi;
228 	struct boot_image *ip;
229 	struct vmproc *vmp;
230 };
231 
232 static int libexec_copy_physcopy(struct exec_info *execi,
233 	off_t off, vir_bytes vaddr, size_t len)
234 {
235 	vir_bytes end;
236 	struct vm_exec_info *ei = execi->opaque;
237 	end = ei->ip->start_addr + ei->ip->len;
238 	assert(ei->ip->start_addr + off + len <= end);
239 	return sys_physcopy(NONE, ei->ip->start_addr + off,
240 		execi->proc_e, vaddr, len, 0);
241 }
242 
243 static void boot_alloc(struct exec_info *execi, off_t vaddr,
244 	size_t len, int flags)
245 {
246 	struct vmproc *vmp = ((struct vm_exec_info *) execi->opaque)->vmp;
247 
248 	if(!(map_page_region(vmp, vaddr, 0, len,
249 		VR_ANON | VR_WRITABLE | VR_UNINITIALIZED, flags,
250 		&mem_type_anon))) {
251 		panic("VM: exec: map_page_region for boot process failed");
252 	}
253 }
254 
255 static int libexec_alloc_vm_prealloc(struct exec_info *execi,
256 	vir_bytes vaddr, size_t len)
257 {
258 	boot_alloc(execi, vaddr, len, MF_PREALLOC);
259 	return OK;
260 }
261 
262 static int libexec_alloc_vm_ondemand(struct exec_info *execi,
263 	vir_bytes vaddr, size_t len)
264 {
265 	boot_alloc(execi, vaddr, len, 0);
266 	return OK;
267 }
268 
269 static void exec_bootproc(struct vmproc *vmp, struct boot_image *ip)
270 {
271 	struct vm_exec_info vmexeci;
272 	struct exec_info *execi = &vmexeci.execi;
273 	char hdr[VM_PAGE_SIZE];
274 
275 	size_t frame_size = 0;	/* Size of the new initial stack. */
276 	int argc = 0;		/* Argument count. */
277 	int envc = 0;		/* Environment count */
278 	char overflow = 0;	/* No overflow yet. */
279 	struct ps_strings *psp;
280 
281 	int vsp = 0;	/* (virtual) Stack pointer in new address space. */
282 	char *argv[] = { ip->proc_name, NULL };
283 	char *envp[] = { NULL };
284 	char *path = ip->proc_name;
285 	char frame[VM_PAGE_SIZE];
286 
287 	memset(&vmexeci, 0, sizeof(vmexeci));
288 
289 	if(pt_new(&vmp->vm_pt) != OK)
290 		panic("VM: no new pagetable");
291 
292 	if(pt_bind(&vmp->vm_pt, vmp) != OK)
293 		panic("VM: pt_bind failed");
294 
295 	if(sys_physcopy(NONE, ip->start_addr, SELF,
296 		(vir_bytes) hdr, sizeof(hdr), 0) != OK)
297 		panic("can't look at boot proc header");
298 
299 	execi->stack_high = kernel_boot_info.user_sp;
300 	execi->stack_size = DEFAULT_STACK_LIMIT;
301 	execi->proc_e = vmp->vm_endpoint;
302 	execi->hdr = hdr;
303 	execi->hdr_len = sizeof(hdr);
304 	strlcpy(execi->progname, ip->proc_name, sizeof(execi->progname));
305 	execi->frame_len = 0;
306 	execi->opaque = &vmexeci;
307 	execi->filesize = ip->len;
308 
309 	vmexeci.ip = ip;
310 	vmexeci.vmp = vmp;
311 
312 	/* callback functions and data */
313 	execi->copymem = libexec_copy_physcopy;
314 	execi->clearproc = NULL;
315 	execi->clearmem = libexec_clear_sys_memset;
316 	execi->allocmem_prealloc_junk = libexec_alloc_vm_prealloc;
317 	execi->allocmem_prealloc_cleared = libexec_alloc_vm_prealloc;
318 	execi->allocmem_ondemand = libexec_alloc_vm_ondemand;
319 
320 	if (libexec_load_elf(execi) != OK)
321 		panic("vm: boot process load of process %s (ep=%d) failed\n",
322 			execi->progname, vmp->vm_endpoint);
323 
324 	/* Setup a minimal stack. */
325 	minix_stack_params(path, argv, envp, &frame_size, &overflow, &argc,
326 		&envc);
327 
328 	/* The party is off if there is an overflow, or it is too big for our
329 	 * pre-allocated space. */
330 	if(overflow || frame_size > sizeof(frame))
331 		panic("vm: could not alloc stack for boot process %s (ep=%d)\n",
332 			execi->progname, vmp->vm_endpoint);
333 
334 	minix_stack_fill(path, argc, argv, envc, envp, frame_size, frame, &vsp,
335 		&psp);
336 
337 	if(handle_memory_once(vmp, vsp, frame_size, 1) != OK)
338 		panic("vm: could not map stack for boot process %s (ep=%d)\n",
339 			execi->progname, vmp->vm_endpoint);
340 
341 	if(sys_datacopy(SELF, (vir_bytes)frame, vmp->vm_endpoint, vsp, frame_size) != OK)
342 		panic("vm: could not copy stack for boot process %s (ep=%d)\n",
343 			execi->progname, vmp->vm_endpoint);
344 
345 	if(sys_exec(vmp->vm_endpoint, (vir_bytes)vsp,
346 		   (vir_bytes)execi->progname, execi->pc,
347 		   vsp + ((int)psp - (int)frame)) != OK)
348 		panic("vm: boot process exec of process %s (ep=%d) failed\n",
349 			execi->progname,vmp->vm_endpoint);
350 
351 	/* make it runnable */
352 	if(sys_vmctl(vmp->vm_endpoint, VMCTL_BOOTINHIBIT_CLEAR, 0) != OK)
353 		panic("VMCTL_BOOTINHIBIT_CLEAR failed");
354 }
355 
356 static int do_procctl_notrans(message *msg)
357 {
358 	int transid = 0;
359 
360 	assert(!IS_VFS_FS_TRANSID(transid));
361 
362 	return do_procctl(msg, transid);
363 }
364 
365 void init_vm(void)
366 {
367 	int s, i;
368 	static struct memory mem_chunks[NR_MEMS];
369 	static struct boot_image *ip;
370 	extern void __minix_init(void);
371 	multiboot_module_t *mod;
372 	vir_bytes kern_dyn, kern_static;
373 
374 #if SANITYCHECKS
375 	incheck = nocheck = 0;
376 #endif
377 
378 	/* Retrieve various crucial boot parameters */
379 	if(OK != (s=sys_getkinfo(&kernel_boot_info))) {
380 		panic("couldn't get bootinfo: %d", s);
381 	}
382 
383 	/* Turn file mmap on? */
384 	enable_filemap=1;	/* yes by default */
385 	env_parse("filemap", "d", 0, &enable_filemap, 0, 1);
386 
387 	/* Sanity check */
388 	assert(kernel_boot_info.mmap_size > 0);
389 	assert(kernel_boot_info.mods_with_kernel > 0);
390 
391 	/* Get chunks of available memory. */
392 	get_mem_chunks(mem_chunks);
393 
394 	/* Set table to 0. This invalidates all slots (clear VMF_INUSE). */
395 	memset(vmproc, 0, sizeof(vmproc));
396 
397 	for(i = 0; i < ELEMENTS(vmproc); i++) {
398 		vmproc[i].vm_slot = i;
399 	}
400 
401 	/* Initialize ACL data structures. */
402 	acl_init();
403 
404 	/* region management initialization. */
405 	map_region_init();
406 
407 	/* Initialize tables to all physical memory. */
408 	mem_init(mem_chunks);
409 
410 	/* Architecture-dependent initialization. */
411 	init_proc(VM_PROC_NR);
412 	pt_init();
413 
414 	/* Acquire kernel ipc vectors that weren't available
415 	 * before VM had determined kernel mappings
416 	 */
417 	__minix_init();
418 
419 	/* The kernel's freelist does not include boot-time modules; let
420 	 * the allocator know that the total memory is bigger.
421 	 */
422 	for (mod = &kernel_boot_info.module_list[0];
423 		mod < &kernel_boot_info.module_list[kernel_boot_info.mods_with_kernel-1]; mod++) {
424 		phys_bytes len = mod->mod_end-mod->mod_start+1;
425 		len = roundup(len, VM_PAGE_SIZE);
426 		mem_add_total_pages(len/VM_PAGE_SIZE);
427 	}
428 
429 	kern_dyn = kernel_boot_info.kernel_allocated_bytes_dynamic;
430 	kern_static = kernel_boot_info.kernel_allocated_bytes;
431 	kern_static = roundup(kern_static, VM_PAGE_SIZE);
432 	mem_add_total_pages((kern_dyn + kern_static)/VM_PAGE_SIZE);
433 
434 	/* Give these processes their own page table. */
435 	for (ip = &kernel_boot_info.boot_procs[0];
436 		ip < &kernel_boot_info.boot_procs[NR_BOOT_PROCS]; ip++) {
437 		struct vmproc *vmp;
438 
439 		if(ip->proc_nr < 0) continue;
440 
441 		assert(ip->start_addr);
442 
443 		/* VM has already been set up by the kernel and pt_init().
444 		 * Any other boot process is already in memory and is set up
445 		 * here.
446 		 */
447 		if(ip->proc_nr == VM_PROC_NR) continue;
448 
449 		vmp = init_proc(ip->proc_nr);
450 
451 		exec_bootproc(vmp, ip);
452 
453 		/* Free the file blob */
454 		assert(!(ip->start_addr % VM_PAGE_SIZE));
455 		ip->len = roundup(ip->len, VM_PAGE_SIZE);
456 		free_mem(ABS2CLICK(ip->start_addr), ABS2CLICK(ip->len));
457 	}
458 
459 	/* Set up table of calls. */
460 #define CALLMAP(code, func) { int _cmi;		      \
461 	_cmi=CALLNUMBER(code);				\
462 	assert(_cmi >= 0);					\
463 	assert(_cmi < NR_VM_CALLS);		\
464 	vm_calls[_cmi].vmc_func = (func); 	      \
465 	vm_calls[_cmi].vmc_name = #code;	      \
466 }
467 
468 	/* Set call table to 0. This invalidates all calls (clear
469 	 * vmc_func).
470 	 */
471 	memset(vm_calls, 0, sizeof(vm_calls));
472 
473 	/* Basic VM calls. */
474 	CALLMAP(VM_MMAP, do_mmap);
475 	CALLMAP(VM_MUNMAP, do_munmap);
476 	CALLMAP(VM_MAP_PHYS, do_map_phys);
477 	CALLMAP(VM_UNMAP_PHYS, do_munmap);
478 
479 	/* Calls from PM. */
480 	CALLMAP(VM_EXIT, do_exit);
481 	CALLMAP(VM_FORK, do_fork);
482 	CALLMAP(VM_BRK, do_brk);
483 	CALLMAP(VM_WILLEXIT, do_willexit);
484 	CALLMAP(VM_NOTIFY_SIG, do_notify_sig);
485 
486 	CALLMAP(VM_PROCCTL, do_procctl_notrans);
487 
488 	/* Calls from VFS. */
489 	CALLMAP(VM_VFS_REPLY, do_vfs_reply);
490 	CALLMAP(VM_VFS_MMAP, do_vfs_mmap);
491 
492 	/* Calls from RS */
493 	CALLMAP(VM_RS_SET_PRIV, do_rs_set_priv);
494 	CALLMAP(VM_RS_UPDATE, do_rs_update);
495 	CALLMAP(VM_RS_MEMCTL, do_rs_memctl);
496 
497 	/* Generic calls. */
498 	CALLMAP(VM_REMAP, do_remap);
499 	CALLMAP(VM_REMAP_RO, do_remap);
500 	CALLMAP(VM_GETPHYS, do_get_phys);
501 	CALLMAP(VM_SHM_UNMAP, do_munmap);
502 	CALLMAP(VM_GETREF, do_get_refcount);
503 	CALLMAP(VM_INFO, do_info);
504 	CALLMAP(VM_QUERY_EXIT, do_query_exit);
505 	CALLMAP(VM_WATCH_EXIT, do_watch_exit);
506 
507 	/* Cache blocks. */
508 	CALLMAP(VM_MAPCACHEPAGE, do_mapcache);
509 	CALLMAP(VM_SETCACHEPAGE, do_setcache);
510 	CALLMAP(VM_CLEARCACHE, do_clearcache);
511 
512 	/* getrusage */
513 	CALLMAP(VM_GETRUSAGE, do_getrusage);
514 
515 	/* Initialize the structures for queryexit */
516 	init_query_exit();
517 }
518 
519 /*===========================================================================*
520  *                         sef_cb_signal_handler                             *
521  *===========================================================================*/
522 static void sef_cb_signal_handler(int signo)
523 {
524 	/* Check for known kernel signals, ignore anything else. */
525 	switch(signo) {
526 		/* There is a pending memory request from the kernel. */
527 		case SIGKMEM:
528 			do_memory();
529 		break;
530 	}
531 
532 	/* It can happen that we get stuck receiving signals
533 	 * without sef_receive() returning. We could need more memory
534 	 * though.
535 	 */
536 	if(missing_spares > 0) {
537 		alloc_cycle();	/* pagetable code wants to be called */
538 	}
539 
540 	pt_clearmapcache();
541 }
542 
543 /*===========================================================================*
544  *                             map_service                                   *
545  *===========================================================================*/
546 static int map_service(struct rprocpub *rpub)
547 {
548 /* Map a new service by initializing its call mask. */
549 	int r, proc_nr;
550 
551 	if ((r = vm_isokendpt(rpub->endpoint, &proc_nr)) != OK) {
552 		return r;
553 	}
554 
555 	/* Copy the call mask. */
556 	acl_set(&vmproc[proc_nr], rpub->vm_call_mask, !IS_RPUB_BOOT_USR(rpub));
557 
558 	return(OK);
559 }
560