2 * \file con/server/src/main.c
3 * \brief 'main' of the con server
6 * \author Christian Helmuth <ch12@os.inf.tu-dresden.de>
7 * Frank Mehnert <fm3@os.inf.tu-dresden.de> */
9 * (c) 2001-2009 Technische Universität Dresden
10 * This file is part of TUD:OS and distributed under the terms of the
11 * GNU General Public License 2.
12 * Please see the COPYING-GPL-2 file for details.
16 #include <l4/l4con/l4con.h>
17 #include <l4/l4con/l4con_ev.h>
18 #include <l4/input/libinput.h>
19 #include <l4/sys/kdebug.h>
20 #include <l4/util/parse_cmd.h>
21 #include <l4/util/macros.h>
22 #include <l4/sys/err.h>
23 #include <l4/re/c/mem_alloc.h>
24 #include <l4/re/c/namespace.h>
25 #include <l4/sys/thread.h>
26 #if defined(ARCH_x86) || defined(ARCH_amd64)
27 #include <l4/util/rdtsc.h>
35 #include <pthread-l4.h>
47 int noaccel; /* 1=disable pslim HW acceleration */
48 int nolog; /* 1=disable logging to logserver */
49 int pan; /* 1=pan display to 4MB boundary */
50 int use_fastmemcpy = 1; /* 1=fast memcpy using SSE2 */
55 struct l4con_vc *vc[MAX_NR_L4CONS]; /* virtual consoles */
56 int fg_vc = -1; /* current foreground vc */
57 int want_vc = 0; /* the vc we want to switch to */
58 /* if we set want_vc = -1, con_loop
59 * must look for an open vc in list;
60 * don't switch to USED VCs, only to
61 * that in OUT/INOUT mode */
62 int update_id; /* 1=redraw vc id */
63 pthread_mutex_t want_vc_lock = PTHREAD_MUTEX_INITIALIZER;/* mutex for want_vc */
64 l4_cap_idx_t ev_partner_l4id = L4_INVALID_CAP;/* current event handler */
65 l4_cap_idx_t vc_partner_l4id = L4_INVALID_CAP;/* current active client */
66 l4_uint8_t vc_mode = CON_CLOSED; /* mode of current console */
67 l4re_util_video_goos_fb_t goosfb;
70 static void do_switch(int i_new, int i_old);
73 fast_memcpy(l4_uint8_t *dst, l4_uint8_t *src, l4_size_t size)
75 #if defined(ARCH_x86) || defined(ARCH_amd64)
78 asm volatile ("cld ; rep movsl"
79 :"=S"(dummy), "=D"(dummy), "=c"(dummy)
80 :"S"(src), "D"(dst),"c"(size/sizeof(l4_umword_t)));
82 memcpy(dst, src, size);
86 /* fast memcpy using movntq (moving using non-temporal hint)
87 * cache line size is 32 bytes */
89 fast_memcpy_mmx2_32(l4_uint8_t *dst, l4_uint8_t *src, l4_size_t size)
91 #if defined(ARCH_x86) || defined(ARCH_amd64)
94 /* don't execute emms in side the timer loop because at this point the
95 * fpu state is lazy allocated so we may have a kernel entry here */
98 asm volatile ("lea (%%esi,%%edx,8),%%esi \n\t"
99 "lea (%%edi,%%edx,8),%%edi \n\t"
105 "# block prefetch 4096 bytes \n\t"
107 "movl (%%esi,%%edx,8),%%eax \n\t"
108 "movl 32(%%esi,%%edx,8),%%eax \n\t"
112 "sub $(32*16),%%edx \n\t"
113 "mov $128,%%ecx \n\t"
115 "2: # copy 4096 bytes \n\t"
116 "movq (%%esi,%%edx,8),%%mm0 \n\t"
117 "movq 8(%%esi,%%edx,8),%%mm1 \n\t"
118 "movq 16(%%esi,%%edx,8),%%mm2 \n\t"
119 "movq 24(%%esi,%%edx,8),%%mm3 \n\t"
120 "movntq %%mm0,(%%edi,%%edx,8) \n\t"
121 "movntq %%mm1,8(%%edi,%%edx,8) \n\t"
122 "movntq %%mm2,16(%%edi,%%edx,8) \n\t"
123 "movntq %%mm3,24(%%edi,%%edx,8) \n\t"
127 "or %%edx,%%edx \n\t"
132 : "S" (src), "D" (dst), "d" (size/8)
133 : "eax", "ebx", "ecx", "memory");
135 memcpy(dst, src, size);
139 /** Find first available (non-opened) vc in list. */
145 for (i=0; i < MAX_NR_L4CONS; i++)
147 if (vc[i]->mode == CON_CLOSED)
151 printf("Ooops, all vcs occupied\n");
155 /** send con_event redraw.
156 * @pre have want_vc_lock */
160 struct l4input ev_struct;
162 if (vcnum >= MAX_NR_L4CONS)
165 ev_struct.time = l4re_kip()->clock;
166 ev_struct.type = EV_CON;
167 ev_struct.code = EV_CON_REDRAW;
170 send_event_client(vc[vcnum], &ev_struct);
174 /** send con_event background. */
176 background_vc(int vcnum)
178 struct l4input ev_struct;
180 if (vcnum >= MAX_NR_L4CONS || vcnum < 0)
183 ev_struct.time = l4re_kip()->clock;
184 ev_struct.type = EV_CON;
185 ev_struct.code = EV_CON_BACKGROUND;
188 send_event_client(vc[vcnum], &ev_struct);
190 vc[vcnum]->fb_mapped = 0;
193 /** Announce that we want to switch to another console.
194 * This function should not block for a longer time. */
198 pthread_mutex_lock(&want_vc_lock);
200 pthread_mutex_unlock(&want_vc_lock);
203 /** Announce that we want to switch to another console.
204 * This function should not block for a longer time. */
206 request_vc_delta(int delta)
212 pthread_mutex_lock(&want_vc_lock);
213 new_vc = fg_vc + delta;
217 new_vc = MAX_NR_L4CONS-1;
218 else if (new_vc >= MAX_NR_L4CONS)
220 if (new_vc == fg_vc || (vc[new_vc]->mode & CON_OUT))
222 new_vc += delta < 0 ? -1 : +1;
225 pthread_mutex_unlock(&want_vc_lock);
228 /** Switch to other console (want_vc != fg_vc).
229 * @pre have want_vc_lock */
235 if ((want_vc > 0) && (want_vc & 0x1000))
237 /* special case: close current console */
238 printf("Still missing: %s %d\n", __func__, __LINE__);
240 CORBA_Environment env = dice_default_environment;
241 pthread_mutex_unlock(&want_vc_lock);
242 con_vc_close_call(&(vc[want_vc & ~0x1000]->vc_l4id), &env);
243 pthread_mutex_lock(&want_vc_lock);
250 struct l4con_vc *new;
252 if ((new = vc[fg_vc]))
254 for (; (new = new->prev); )
256 if ((new->mode & CON_OUT) && !(new->mode & CON_MASTER))
264 for (i=0; i<MAX_NR_L4CONS; i++)
266 if ((vc[i]->mode & CON_OUT) && !(vc[i]->mode & CON_MASTER))
269 printf("switch to vc %02d\n", i);
271 /* need fb_lock for that */
278 if (i == MAX_NR_L4CONS)
280 printf("All vc's closed\n");
281 /* switch to vc 0 (master) */
288 /* request for explicit switch (event or open) */
289 if (want_vc >= MAX_NR_L4CONS)
291 /* return to sane state */
293 ev_partner_l4id = vc[fg_vc]->ev_partner_l4id;
294 vc_partner_l4id = vc[fg_vc]->vc_partner_l4id;
295 vc_mode = vc[fg_vc]->mode;
299 /* check if want_vc is open */
300 if (vc[want_vc]->mode & CON_OUT)
302 do_switch(want_vc, fg_vc);
305 /* set a sane state */
310 /* set a sane state */
317 do_switch(int i_new, int i_old)
319 struct l4con_vc *old = (i_old >= 0) ? vc[i_old] : 0;
320 struct l4con_vc *new = (i_new >= 0) ? vc[i_new] : 0;
325 pthread_mutex_lock(&old->fb_lock);
326 if (old->save_restore && old->vfb)
329 if (use_fastmemcpy && (new->vfb_size % 4096 == 0))
330 fast_memcpy_mmx2_32(old->vfb, vis_vmem, old->vfb_size);
332 fast_memcpy (old->vfb, vis_vmem, old->vfb_size);
334 old->fb = old->vfb_in_server ? old->vfb : 0;
335 old->pan_xofs = old->vfb_in_server ? 0 : pan_offs_x;
336 old->pan_yofs = old->vfb_in_server ? 0 : pan_offs_y;
337 old->do_copy = bg_do_copy;
338 old->do_fill = bg_do_fill;
339 old->do_sync = bg_do_sync;
342 pthread_mutex_unlock(&old->fb_lock);
348 pthread_mutex_lock(&new->fb_lock);
350 new->pan_xofs = pan_offs_x;
351 new->pan_yofs = pan_offs_y;
352 new->do_copy = fg_do_copy;
353 new->do_fill = fg_do_fill;
354 new->do_sync = fg_do_sync;
355 new->do_drty = fg_do_drty;
358 if (i_new == 0 || !new->save_restore || !new->vfb)
360 /* We clear the screen here for security reasons: If save_restore
361 * is false the client is responsible for updating the screen when
362 * it receives the EV_CON_REDRAW event. A malicous client could
363 * "forget" to respond but now has the input focus. */
367 if (new->save_restore && new->vfb)
370 if (use_fastmemcpy && (new->vfb_size % 4096 == 0))
371 fast_memcpy_mmx2_32(vis_vmem, new->vfb, new->vfb_size);
373 fast_memcpy (vis_vmem, new->vfb, new->vfb_size);
376 /* force redraw of changed screen content (needed by VMware) */
378 new->do_drty(0, 0, fb_info.width, fb_info.height);
380 pthread_mutex_unlock(&new->fb_lock);
385 /* tell old console that we flash its video memory */
387 background_vc(i_old);
389 if (old && old->fb_mapped)
391 /* Flush video memory of old console.
392 * XXX This does not work on current Fiasco implementation because the
393 * mapping database does not record mappings of pages beyond end of
397 for (map_addr=(l4_addr_t)gr_vmem;
398 map_addr+L4_SUPERPAGESIZE < (l4_addr_t)gr_vmem_maxmap;
399 map_addr+=L4_SUPERPAGESIZE)
402 l4_fpage_unmap(l4_fpage(map_addr, L4_LOG2_SUPERPAGESIZE, 0, 0),
403 L4_FP_FLUSH_PAGE | L4_FP_OTHER_SPACES);
407 ev_partner_l4id = vc[i_new]->ev_partner_l4id;
408 vc_partner_l4id = vc[i_new]->vc_partner_l4id;
409 vc_mode = vc[i_new]->mode;
412 /******************************************************************************
413 * con_if - IDL server functions *
414 ******************************************************************************/
416 /** \brief Query for new (not opened) virtual console
418 * in: request ... Flick request structure
419 * sbuf_size ... max IPC string buffer
420 * out: vcid ... threadid of vc_thread
421 * _ev ... Flick exception (unused)
423 * -??? ... if no VC unused */
426 con_if_openqry_component (CORBA_Object _dice_corba_obj,
427 unsigned long sbuf1_size,
428 unsigned long sbuf2_size,
429 unsigned long sbuf3_size,
430 unsigned char priority,
433 CORBA_Server_Environment *_dice_corba_env)
436 l4_threadid_t vc_l4id, dummy;
439 l4_addr_t ds_map_addr;
440 l4_size_t ds_map_size;
444 /* check sbuf_size */
446 || (sbuf2_size != 0 && sbuf3_size == 0)
447 || (sbuf2_size == 0 && sbuf3_size != 0)
448 || (sbuf1_size + sbuf2_size + sbuf3_size) > CONFIG_MAX_SBUF_SIZE)
450 printf("Wrong string buffer size\n");
454 /* find first available (non-opened) vc in list */
455 if ((vc_num = get_free_vc()) == -CON_EFREE)
458 /* allocate memory for sbuf */
459 sprintf(name, "ipcbuf1 for "l4util_idfmt"", l4util_idstr(*_dice_corba_obj));
460 if (!(vc[vc_num]->sbuf1 =
461 l4dm_mem_allocate_named(sbuf1_size, L4RM_MAP, name)))
463 printf("Ooops, not enough memory for 1st string buffer\n");
467 vc[vc_num]->sbuf2 = 0;
470 sprintf(name, "ipcbuf2 for "l4util_idfmt"",
471 l4util_idstr(*_dice_corba_obj));
474 if (!(vc[vc_num]->sbuf2 =
475 l4dm_mem_allocate_named(sbuf2_size, L4RM_MAP, name)))
477 printf("Ooops, not enough memory for 2nd string buffer\n");
478 l4dm_mem_release(vc[vc_num]->sbuf1);
484 vc[vc_num]->sbuf3 = 0;
487 sprintf(name, "ipcbuf3 for "l4util_idfmt"",
488 l4util_idstr(*_dice_corba_obj));
491 if (!(vc[vc_num]->sbuf3 =
492 l4dm_mem_allocate_named(sbuf3_size, L4RM_MAP, name)))
494 printf("Ooops, not enough memory for 3rd string buffer\n");
495 l4dm_mem_release(vc[vc_num]->sbuf1);
496 l4dm_mem_release(vc[vc_num]->sbuf2);
504 vc[vc_num]->vfb_in_server = vfbmode;
505 vc[vc_num]->mode = CON_OPENING;
506 vc[vc_num]->vc_partner_l4id = *_dice_corba_obj;
507 vc[vc_num]->sbuf1_size = sbuf1_size;
508 vc[vc_num]->sbuf2_size = sbuf2_size;
509 vc[vc_num]->sbuf3_size = sbuf3_size;
510 vc[vc_num]->fb_mapped = 0;
513 vc[vc_num]->save_restore = 1;
515 sprintf(name, ".vc-%.2d", vc_num);
516 vc_tid = l4thread_create_long(L4THREAD_INVALID_ID,
517 (l4thread_fn_t) vc_loop, name,
518 L4THREAD_INVALID_SP, L4THREAD_DEFAULT_SIZE,
519 priority, (void *) vc[vc_num],
520 L4THREAD_CREATE_SYNC);
522 vc_l4id = l4thread_l4_id(vc_tid);
524 vc[vc_num]->vc_l4id = vc_l4id;
525 vc[vc_num]->ev_partner_l4id = L4_INVALID_CAP;
527 // transfer ownership of ipc buffers to client thread
528 if (L4RM_REGION_DATASPACE == l4rm_lookup(vc[vc_num]->sbuf1, &ds_map_addr,
529 &ds_map_size, &ds, &ds_offs, &dummy))
530 l4dm_transfer(&ds, vc_l4id);
531 if (L4RM_REGION_DATASPACE == l4rm_lookup(vc[vc_num]->sbuf2, &ds_map_addr,
532 &ds_map_size, &ds, &ds_offs, &dummy))
533 l4dm_transfer(&ds, vc_l4id);
534 if (L4RM_REGION_DATASPACE == l4rm_lookup(vc[vc_num]->sbuf3, &ds_map_addr,
535 &ds_map_size, &ds, &ds_offs, &dummy))
536 l4dm_transfer(&ds, vc_l4id);
538 /* reply vc_thread id */
546 vc_open(struct l4con_vc *vc, l4_uint8_t mode, l4_cap_idx_t ev_handler);
549 con_if_open_component(short vfbmode)
552 //l4_threadid_t vc_l4id, dummy;
553 //l4dm_dataspace_t ds;
555 //l4_addr_t ds_map_addr;
556 //l4_size_t ds_map_size;
561 /* find first available (non-opened) vc in list */
562 if ((vc_num = get_free_vc()) == -CON_EFREE)
567 vc[vc_num]->vfb_in_server = vfbmode;
568 vc[vc_num]->mode = CON_OPENING;
569 //vc[vc_num]->vc_partner_l4id = *_dice_corba_obj;
570 //vc[vc_num]->sbuf1_size = sbuf1_size;
571 //vc[vc_num]->sbuf2_size = sbuf2_size;
572 //vc[vc_num]->sbuf3_size = sbuf3_size;
573 vc[vc_num]->fb_mapped = 0;
576 vc[vc_num]->save_restore = 1;
579 sprintf(name, ".vc-%.2d", vc_num);
580 vc_tid = l4thread_create_long(L4THREAD_INVALID_ID,
581 (l4thread_fn_t) vc_loop, name,
582 L4THREAD_INVALID_SP, L4THREAD_DEFAULT_SIZE,
583 priority, (void *) vc[vc_num],
584 L4THREAD_CREATE_SYNC);
586 vc_l4id = l4thread_l4_id(vc_tid);
588 vc[vc_num]->vc_l4id = vc_l4id;
590 // vc[vc_num]->ev_partner_l4id = L4_INVALID_CAP;
592 //return vc_open(vc, mode & CON_INOUT, *ev_handler);
594 if ((r = vc_open(vc[vc_num], CON_INOUT, L4_INVALID_CAP)))
597 create_event(vc[vc_num]);
599 fill_out_info(vc[vc_num]);
606 con_if_screenshot_component (CORBA_Object _dice_corba_obj,
608 l4dm_dataspace_t *ds,
612 CORBA_Server_Environment *_dice_corba_env)
615 struct l4con_vc *vc_shoot;
617 if (vc_nr >= MAX_NR_L4CONS)
623 vc_shoot = vc[vc_nr];
625 if (!(vc_shoot->mode & CON_INOUT))
628 if (!vc_shoot->vfb_in_server && vc_nr != fg_vc)
631 pthread_mutex_lock(&vc_shoot->fb_lock);
633 if (!(addr = l4dm_mem_ds_allocate_named(vc_shoot->vfb_size, 0, "screenshot",
634 (l4dm_dataspace_t*)ds)))
636 printf("Allocating dataspace failed\n");
640 /* XXX consider situations where
641 * bytes_per_line != bytes_per_pixel * xres !!! */
642 memcpy(addr, vc_shoot->vfb_in_server ? vc_shoot->vfb : vc_shoot->fb,
645 pthread_mutex_unlock(&vc_shoot->fb_lock);
647 *xres = fb_info.width;
648 *yres = fb_info.height;
649 *bpp = fb_info.bits_per_pixel;
653 /* transfer ds to caller so that it can be freed again */
654 l4dm_transfer((l4dm_dataspace_t*)ds, *_dice_corba_obj);
660 /** \brief Close all virtual consoles of a client
662 * in: request ... Flick request structure
663 * client ... id of client to close all vcs off
664 * out: _ev ... Flick exception (unused)
666 * -??? ... if some error occured on closing a vc */
669 con_if_close_all_component (CORBA_Object _dice_corba_obj,
670 const l4_threadid_t *client,
671 CORBA_Server_Environment *_dice_corba_env)
675 for (i=0; i<MAX_NR_L4CONS; i++)
677 if (vc[i]->mode != CON_CLOSED && vc[i]->mode != CON_CLOSING)
679 /* don't use l4_task_equal here since we only know the task number */
680 if (vc[i]->vc_partner_l4id.id.task == client->id.task)
682 /* found console bound to client -- close it */
683 CORBA_Environment env = dice_default_environment;
684 int ret = con_vc_close_call(&(vc[i]->vc_l4id), &env);
685 if (ret || DICE_HAS_EXCEPTION(&env))
686 printf("Error %d (env=%02x) closing app "l4util_idfmt
687 " service thread "l4util_idfmt,
688 ret, DICE_IPC_ERROR(&env), l4util_idstr(*client),
689 l4util_idstr(vc[i]->vc_l4id));
701 static l4_kernel_clock_t last_active_fast;
702 static l4_kernel_clock_t last_active_slow;
703 l4_kernel_clock_t clock = l4re_kip()->clock;
705 if (clock - last_active_fast >= 25000)
707 // switch to another console?
708 pthread_mutex_lock(&want_vc_lock);
709 if (want_vc != fg_vc)
711 pthread_mutex_unlock(&want_vc_lock);
714 if (update_id && fg_vc >= 0)
716 pthread_mutex_lock(&vc[fg_vc]->fb_lock);
717 vc_show_id(vc[fg_vc]);
718 pthread_mutex_unlock(&vc[fg_vc]->fb_lock);
720 // force updating load indicator
721 last_active_slow = clock - 1000000;
725 if (vc[fg_vc]->logo_x != 100000)
727 pthread_mutex_lock(&vc[fg_vc]->fb_lock);
728 vc_show_drops_cscs_logo();
729 pthread_mutex_unlock(&vc[fg_vc]->fb_lock);
732 last_active_fast = clock;
735 if (clock - last_active_slow >= 1000000)
737 pthread_mutex_lock(&vc[fg_vc]->fb_lock);
739 // update memory information
740 vc_show_dmphys_poolsize(vc[fg_vc]);
743 // update load indicator
744 vc_show_cpu_load(vc[fg_vc]);
746 pthread_mutex_unlock(&vc[fg_vc]->fb_lock);
747 last_active_slow = clock;
754 static int sse_faulted;
757 rm_fault_sse(l4_msgtag_t tag, l4_utcb_t *utcb, l4_cap_idx_t src)
759 extern char after_sse_insn[];
761 utcb->exc.eip = (l4_umword_t)after_sse_insn;
762 return L4RM_REPLY_EMPTY;
768 check_fast_memcpy(void)
770 #if defined(ARCH_x86) || defined(ARCH_amd64)
773 /* assume AMD64 has SSE */
775 l4_uint64_t src, dst;
776 #warning Restore exception handler hook!!!
777 //l4rm_set_unkown_fault_callback(rm_fault_sse);
778 asm volatile("emms; movq (%0),%%mm0; movntq %%mm0,(%1); sfence; emms"
779 : : "r"(&src), "r"(&dst) , "m"(src) : "memory");
780 asm volatile(".global after_sse_insn; after_sse_insn:" ::: "memory");
781 //l4rm_set_unkown_fault_callback(NULL);
782 if (1) //!sse_faulted)
785 printf("Using fast memcpy.\n");
790 printf("Fast memcpy not supported by this CPU.\n");
799 printf("Not using fast memcpy\n");
802 #if defined(ARCH_x86) || defined(ARCH_amd64)
804 static int rdpmc_faulted;
807 rm_fault_rdpmc(l4_msgtag_t tag, l4_utcb_t *utcb, l4_cap_idx_t src)
809 extern char after_rdpmc_insn[];
810 if (*(l4_uint16_t *)utcb->exc.eip != 0x330f) // sanity check
812 printf("Not faulted at rdpmc function (pc = %lx)", utcb->exc.eip);
813 return L4RM_REPLY_NO_REPLY;
815 utcb->exc.eip = (l4_umword_t)after_rdpmc_insn;
817 return L4RM_REPLY_EMPTY;
826 #warning restore exception handler hook (PMC)!!!
827 // l4rm_set_unkown_fault_callback(rm_fault_rdpmc);
828 asm volatile("" ::: "memory");
830 asm volatile(".global after_rdpmc_insn; after_rdpmc_insn:" ::: "memory");
831 //l4rm_set_unkown_fault_callback(NULL);
832 if (1) //!rdpmc_faulted)
834 printf("Enabling CPU load indicator\n"
835 "\033[32mALTGR+PAUSE switches CPU load indicator!\033[m\n");
839 printf("Disabling CPU load indicator since rdpmc not available\n");
853 extern int console_puts(const char *s);
855 my_LOG_outstring(const char *s)
861 /* Make sure that the jiffies symbol is taken from libio.a not libinput.a. */
862 asm (".globl jiffies");
864 /** \brief Main function
867 main(int argc, const char *argv[])
876 if ((error = parse_cmdline(&argc, &argv,
877 'a', "noaccel", "disable hardware acceleration",
878 PARSE_CMD_SWITCH, 1, &noaccel,
880 'c', "cpuload", "show CPU load using rdtsc and rdpmc(0)",
881 PARSE_CMD_SWITCH, 1, &cpu_load,
883 'e', "events", "use event server to free resources",
884 PARSE_CMD_SWITCH, 1, &use_events,
885 'l', "nolog", "don't connect to logserver",
886 PARSE_CMD_SWITCH, 1, &nolog,
887 'm', "nomouse", "don't transmit mouse events to clients",
888 PARSE_CMD_SWITCH, 1, &nomouse,
889 'b', "genabsevents", "generate absolute mouse events out of relatives",
890 PARSE_CMD_SWITCH, 1, &gen_abs_events,
891 'n', "nofastmemcpy", "force to not use fast memcpy",
892 PARSE_CMD_SWITCH, 0, &use_fastmemcpy,
893 'p', "pan", "use panning to restrict client window",
894 PARSE_CMD_SWITCH, 1, &pan,
895 ' ', "noshift", "no shift key for console switching",
896 PARSE_CMD_SWITCH, 1, &noshift,
897 'v', "vbemode", "set VESA mode",
898 PARSE_CMD_INT, 0, &vbemode,
903 case -1: printf("Bad parameter for parse_cmdline()\n"); break;
904 case -2: printf("Out of memory in parse_cmdline()\n"); break;
906 default: printf("Error %d in parse_cmdline()\n", error); break;
910 cpu_load_history = 1;
912 /* do not use logserver (in case the log goes to a console) */
915 LOG_outstring = my_LOG_outstring;
918 /* check if CPU supports fast memcpy */
924 /* switch to master console: initial screen output (DROPS logo) */
932 /* start thread listening for exit events */
936 printf("Running. Video mode is %ldx%ld@%d.\n",
937 fb_info.width, fb_info.height,
938 l4re_video_bits_per_pixel(&fb_info.pixel_info));
940 /* idl service loop */
941 return server_loop();