openmoko: kexecboot.c

File kexecboot.c, 13.4 KB (added by ew, 3 years ago)

kexeboot c to boot the openmoko with ts screen support and AUX Button

Line 
1/*
2 *  kexecboot - A kexec based bootloader
3 *
4 *  Copyright (c) 2008-2009 Yuri Bushmelev <jay4mail@gmail.com>
5 *  Copyright (c) 2008 Thomas Kunze <thommycheck@gmx.de>
6 *
7 *  small parts:
8 *  Copyright (c) 2006 Matthew Allum <mallum@o-hand.com>
9 *
10 *  This program is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License as published by
12 *  the Free Software Foundation; either version 2, or (at your option)
13 *  any later version.
14 *
15 *  This program is distributed in the hope that it will be useful,
16 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 *  GNU General Public License for more details.
19 *
20 */
21
22#include "kexecboot.h"
23
24/* Draw background with logo and text */
25void draw_background(FB *fb, const char *text) {
26        int margin = fb->width/64;
27
28        /* Clear the background with #ecece1 */
29        fb_draw_rect(fb, 0, 0, fb->width, fb->height,0xec, 0xec, 0xe1);
30
31        /* logo */
32        fb_draw_image(fb,
33                      0,
34                      0,
35                      LOGO_IMG_WIDTH,
36                      LOGO_IMG_HEIGHT,
37                      LOGO_IMG_BYTES_PER_PIXEL, LOGO_IMG_RLE_PIXEL_DATA);
38
39        fb_draw_text (fb, LOGO_IMG_WIDTH + margin, margin, 0, 0, 0,
40                &radeon_font, text);
41}
42
43/* Draw one slot in menu */
44void draw_slot(FB *fb, struct boot * boot,int slot, int height, int iscurrent)
45{
46        int margin = (height - CF_IMG_HEIGHT)/2;
47        char text[100];
48        if(!iscurrent)
49                fb_draw_rect(fb, 0, slot*height, fb->width, height,
50                        0xec, 0xec, 0xe1);
51        else { //draw red border
52                fb_draw_rect(fb, 0, slot*height, fb->width, height,
53                        0xff, 0x00, 0x00);
54                fb_draw_rect(fb, margin, slot*height+margin, fb->width-2*margin
55                        , height-2*margin, 0xec, 0xec, 0xe1);
56        }
57        if(!strncmp(boot->device,"/dev/hd",strlen("/dev/hd")))
58                fb_draw_image(fb, margin, slot*height+margin, CF_IMG_WIDTH,
59                        CF_IMG_HEIGHT, CF_IMG_BYTES_PER_PIXEL, CF_IMG_RLE_PIXEL_DATA);
60        else if(!strncmp(boot->device,"/dev/mmcblk",strlen("/dev/mmcblk")))
61                fb_draw_image(fb, margin, slot*height + margin, MMC_IMG_WIDTH,
62                        MMC_IMG_HEIGHT, MMC_IMG_BYTES_PER_PIXEL, MMC_IMG_RLE_PIXEL_DATA);
63        else if(!strncmp(boot->device,"/dev/mtdblock",strlen("/dev/mtdblock")))
64                fb_draw_image(fb, margin, slot*height + margin, MEMORY_IMG_WIDTH,
65                        MEMORY_IMG_HEIGHT, MEMORY_IMG_BYTES_PER_PIXEL, MEMORY_IMG_RLE_PIXEL_DATA);
66        sprintf(text,"%s (%s)",boot->device, boot->fstype);
67        fb_draw_text (fb, CF_IMG_WIDTH+margin, slot*height+margin, 0, 0, 0,
68                        &radeon_font, text);
69
70}
71
72/* Display bootlist menu with selection */
73void display_menu(FB *fb, struct bootlist *bl, int current)
74{
75        int i,j;
76        int slotheight = LOGO_IMG_HEIGHT;
77        int slots = fb->height/slotheight -1;
78        // struct boot that is in fist slot
79        static int firstslot=0;
80
81        if (0 == bl->size) {
82                draw_background(fb, "No bootable devices found.\nInsert bootable device!\nR: Reboot  S: Rescan devices");
83        } else {
84                draw_background(fb, "Make your choice by selecting\nan item with the cursor keys.\nOK/Enter: Boot selected device\nR: Reboot  S: Rescan devices");
85        }
86
87        if(current < firstslot)
88                firstslot=current;
89        if(current > firstslot + slots -1)
90                firstslot = current - (slots -1);
91        for(i=1, j=firstslot; i <= slots && j< bl->size; i++, j++){
92                draw_slot(fb, bl->list[j], i, slotheight, j == current);
93        }
94        fb_render(fb);
95}
96
97/* Display custom text near logo */
98void display_text(FB *fb, const char *text) {
99        draw_background(fb, text);
100        fb_render(fb);
101}
102
103/*
104 * Function: get_extra_cmdline()
105 * It gets wanted tags from original cmdline.
106 * Takes 2 args:
107 * - extra_cmdline - buffer to store cmdline parameters;
108 * - extra_cmdline_size - size of buffer
109 *   (inc. terminating '\0').
110 * Return values:
111 * - length of extra_cmdline on success (w/o term. zero);
112 * - -1 on error;
113 * - (- length of extra_cmdline - 1)  on insufficient buffer space.
114 * REFACTOR: do something with wanted_tags and this function (may be move to util)
115 */
116
117int get_extra_cmdline(char *const extra_cmdline, const size_t extra_cmdline_size)
118{
119        char *p, *t, *tag = NULL;
120        char line[COMMAND_LINE_SIZE];
121        int i, len, sp_size;
122        int sum_len = 0;
123        int wanted_tag_found = 0;
124        int overflow = 0;
125
126        const char proc_cmdline_path[] = "/proc/cmdline";
127
128        /* Open /proc/cmdline and read cmdline */
129        FILE *f = fopen(proc_cmdline_path, "r");
130        if (NULL == f) {
131                perror("Can't open /proc/cmdline");
132                return -1;
133        }
134
135        if ( NULL == fgets(line, sizeof(line), f) ) {
136                perror("Can't read /proc/cmdline");
137                fclose(f);
138                return -1;
139        }
140
141        fclose(f);
142
143        /* clean up buffer before parsing */
144        t = extra_cmdline;
145        *t = '\0';
146
147        p = line-1;             /* because of ++p below */
148
149        sp_size = 0;    /* size of (first) space */
150
151        do {
152                ++p;
153
154                if ( (NULL == tag) && (isalnum(*p)) ) {
155                        /* new tag found */
156                        tag = p;
157                } else if (tag) {
158                        /* we are working on some tag */
159
160                        if (isspace(*p) || ('\0' == *p) || ('=' == *p) ) {
161                                /* end of tag or '=' found */
162
163                                if (!wanted_tag_found) {
164                                        /* search in wanted_tags */
165                                        for (i = 0; wanted_tags[i] != NULL; i++) {
166                                                if ( 0 == strncmp(wanted_tags[i], tag, p-tag) ) {
167                                                        /* match found */
168                                                        wanted_tag_found = 1;
169                                                        break;
170                                                }
171                                        }
172                                }
173
174                                if ( ('=' != *p) && wanted_tag_found ) {
175                                        /* end of wanted tag found -> copy */
176
177                                        len = p - tag;
178                                        if ( (sum_len + len + sp_size) < extra_cmdline_size ) {
179                                                if (sp_size) {
180                                                        /* prepend space when have tags already */
181                                                        *t = ' ';
182                                                        ++t;
183                                                        *t = '\0';
184                                                        ++sum_len;
185                                                } else {
186                                                        sp_size = sizeof(char);
187                                                }
188
189                                                /* NOTE: tag is not null-terminated so copy only
190                                                 * len chars and terminate it directly
191                                                 */
192                                                strncpy(t, tag, len);
193                                                /* move pointer to position after copied tag */
194                                                t += len ;
195                                                /* null-terminate */
196                                                *t = '\0';
197                                                /* update summary length */
198                                                sum_len += len;
199                                        } else {
200                                                /* we have no space - skip this tag */
201                                                overflow = 1;
202                                        }
203
204                                        /* reset wanted_tag_found */
205                                        wanted_tag_found = 0;
206                                }
207
208                                /* reset tag */
209                                if (!wanted_tag_found) tag = NULL;
210
211                        }
212                }
213
214        } while ('\0' != *p);
215
216        if (overflow) return -sum_len-1;
217        return sum_len;
218}
219
220void start_kernel(struct boot *boot)
221{
222        /* we use var[] instead of *var because sizeof(var) using */
223        const char mount_point[] = "/mnt";
224        const char kexec_path[] = "/usr/sbin/kexec";
225
226        const char str_cmdline_start[] = "--command-line=";
227        const char str_root[] = " root=";
228        const char str_rootfstype[] = " rootfstype=";
229        const char str_rootwait[] = " rootwait";
230
231        /* empty environment */
232        char *const envp[] = { NULL };
233
234        const char *kexec_load_argv[] = { NULL, "-l", NULL, NULL, NULL };
235        const char *kexec_exec_argv[] = { NULL, "-e", NULL, NULL};
236
237        char extra_cmdline_buffer[COMMAND_LINE_SIZE];
238        char *extra_cmdline, *cmdline_arg = NULL;
239        int n, idx;
240        struct stat *sinfo = malloc(sizeof(struct stat));
241
242        kexec_exec_argv[0] = kexec_path;
243
244        /* Check /proc/sys/net presence */
245        if ( -1 == stat("/proc/sys/net", sinfo) ) {
246                if (ENOENT == errno) {
247                        /* We have no network, don't issue ifdown() while kexec'ing */
248                        kexec_exec_argv[2] = "-x";
249                        DPRINTF("No network is detected, disabling ifdown()\n");
250                } else {
251                        perror("Can't stat /proc/sys/net");
252                }
253        }
254        free(sinfo);
255
256        kexec_load_argv[0] = kexec_path;
257
258        /* --command-line arg generation */
259        idx = 2;        /* kexec_load_argv current option index */
260
261        /* get some parts of cmdline from boot loader (e.g. mtdparts) */
262        n = get_extra_cmdline( extra_cmdline_buffer,
263                        sizeof(extra_cmdline_buffer) );
264        if ( -1 == n ) {
265                /* clean up extra_cmdline on error */
266                extra_cmdline = NULL;
267/*      } else if ( n < -1 ) { */
268                /* Do something when we have no space to get all wanted tags */
269                /* Now do nothing ;) */
270        } else {
271                extra_cmdline = extra_cmdline_buffer;
272        }
273
274        /* fill '--command-line' option */
275        if (boot->cmdline || extra_cmdline || boot->device) {
276                /* allocate space */
277                n = strlenn(str_cmdline_start) + strlenn(boot->cmdline) +
278                                sizeof(char) + strlenn(extra_cmdline) +
279                                strlenn(str_root) + strlenn(boot->device) +
280                                strlenn(str_rootfstype) + strlenn(boot->fstype) +
281                                strlenn(str_rootwait) + sizeof(char);
282
283                cmdline_arg = (char *)malloc(n);
284                if (NULL == cmdline_arg) {
285                        perror("Can't allocate memory for cmdline_arg");
286                        /*  Should we exit?
287                        exit(-1);
288                        */
289                } else {
290                        strcpy(cmdline_arg, str_cmdline_start);
291                        if (extra_cmdline)
292                                strcat(cmdline_arg, extra_cmdline);
293                        if (boot->cmdline && extra_cmdline)
294                                strcat(cmdline_arg, " ");
295                        if (boot->cmdline)
296                                strcat(cmdline_arg, boot->cmdline);
297                        if (boot->device) {
298                                strcat(cmdline_arg, str_root);
299                                strcat(cmdline_arg, boot->device);
300                                if (boot->fstype) {
301                                        strcat(cmdline_arg, str_rootfstype);
302                                        strcat(cmdline_arg, boot->fstype);
303                                }
304                        }
305                        strcat(cmdline_arg, str_rootwait);
306
307                        kexec_load_argv[idx] = cmdline_arg;
308                        ++idx;
309                }
310        }
311
312        /* Append kernelpath as last arg of kexec */
313        kexec_load_argv[idx] = boot->kernelpath;
314
315        DPRINTF("kexec_load_argv: %s, %s, %s, %s\n", kexec_load_argv[0],
316                        kexec_load_argv[1], kexec_load_argv[2],
317                        kexec_load_argv[3]);
318
319        DPRINTF("kexec_exec_argv: %s, %s, %s\n", kexec_exec_argv[0],
320                        kexec_exec_argv[1], kexec_exec_argv[2]);
321
322        /* Mount boot device */
323        if ( -1 == mount(boot->device, mount_point, boot->fstype,
324                        MS_RDONLY, NULL) ) {
325                perror("Can't mount boot device");
326                exit(-1);
327        }
328
329        /* Load kernel */
330        n = fexecw(kexec_path, (char *const *)kexec_load_argv, envp);
331        if (-1 == n) {
332                perror("Kexec can't load kernel");
333                exit(-1);
334        }
335
336        if (cmdline_arg)
337                free(cmdline_arg);
338
339        umount(mount_point);
340
341        /* Boot new kernel */
342        execve(kexec_path, (char *const *)kexec_exec_argv, envp);
343}
344
345int main(int argc, char **argv)
346{
347        int choice = 0;
348        int initmode = 0;
349        FB *fb;
350        FILE *f;
351        int angle = KXB_FBANGLE;
352        struct bootlist * bl;
353        struct input_event evt;
354        struct termios old, new;
355        struct hw_model_info *model;
356        int ev0;
357        int ev1;
358        fd_set fds;
359        /* hardcoded for openmoko gta02 with 2.6.24 kernel */
360        char *eventif = "/dev/event0";
361        char *eventif1 = "/dev/event1";
362
363        struct input_event evt1;
364        int maxfd;
365        int nready;
366        int nread;
367        struct timeval tv;
368        /* When our pid is 1 we are init-process */
369        if ( 1 == getpid() ) {
370                initmode = 1;
371
372                DPRINTF("I'm the init-process!\n");
373
374                /* extra delay for initializing slow SD/CF */
375                sleep(1);
376
377                /* Mount procfs */
378                if ( -1 == mount("proc", "/proc", "proc",
379                                0, NULL) ) {
380                        perror("Can't mount procfs");
381                        exit(-1);
382                }
383
384                DPRINTF("Procfs mounted\n");
385
386                /* Set up console loglevel */
387                f = fopen("/proc/sys/kernel/printk", "w");
388                if (NULL == f) {
389                        perror("/proc/sys/kernel/printk");
390                        exit(-1);
391                }
392                fputs("0 4 1 7\n", f);
393                fclose(f);
394
395                DPRINTF("Console loglevel is set\n");
396
397        }
398
399        /* Get hardware model parameters (now FB angle only) */
400        model = detect_hw_model(model_info);
401        if (model->hw_model_id != HW_MODEL_UNKNOWN) {
402                angle = model->angle;
403                DPRINTF("Model is %s, fbangle is %d\n", model->name, model->angle);
404        }
405
406        /* Check command-line args when not an init-process */
407        if (!initmode) {
408                int i = 0;
409                while (++i < argc) {
410                        if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--angle")) {
411                                if (++i > argc)
412                                        goto fail;
413                                angle = atoi(argv[i]);
414                                continue;
415                        }
416
417                        if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--input")) {
418                                if (++i > argc)
419                                        goto fail;
420                                eventif = argv[i];
421                                continue;
422                        }
423                        if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--event")) {
424                          if (++i > argc)
425                            goto fail;
426                          eventif1 = argv[i];
427                          continue;
428                        }
429
430                        fail:
431                 fprintf(stderr, "Usage: %s [-a|--angle <0|90|180|270>] \
432                                [-i|--input </dev/input/eventX> [-e|--eventt </dev/input/eventX2>\n",
433                         /* kxecboot -i /dev/input4 -e /dev/input0  for GTA02*/
434                         /*                aux             tscreen on GTA running shr-unstable */
435                        argv[0]);
436                        exit(-1);
437                }
438        }
439
440        DPRINTF("FB angle is %d, input device is %s\n", angle, eventif);
441        DPRINTF("Going to fb mode\n");
442
443        if ((fb = fb_new(angle)) == NULL)
444                exit(-1);
445
446        ev0 = open(eventif,O_RDONLY);
447        ev1 = open(eventif1,O_RDONLY);
448        if(!ev0 && !ev1 ){
449            perror(eventif); /* maybe more sane output for for eventif1 */
450            exit(3);
451        }
452
453        /* Switch cursor off. NOTE: works only when master-console is tty */
454        printf("\033[?25l\n");
455
456        // deactivate terminal input
457        tcgetattr(fileno(stdin), &old);
458        new = old;
459        new.c_lflag &= ~ECHO;
460//      new.c_cflag &=~CREAD;
461        tcsetattr(fileno(stdin), TCSANOW, &new);
462
463        bl = scan_devices(model);
464
465        do {
466                display_menu(fb, bl, choice);
467                do{
468                  /* Wait for some input. */
469                  tv.tv_sec =1;
470                  tv.tv_usec = 0;
471                  FD_ZERO(&fds);
472                  FD_SET( ev0,&fds);
473                  FD_SET(ev1,&fds);
474                  maxfd = 8;
475                  nready = select(maxfd, &fds, NULL,NULL,&tv);
476                  /* now GTA02 specific */
477                  if( FD_ISSET(ev0, &fds))
478                    {
479                      read(ev0,&evt, sizeof(struct input_event));
480                      DPRINTF("event: %i %i %i,\n",evt.type, evt.code,evt.value);
481                    }
482                  if( FD_ISSET(ev1, &fds))
483                    {
484                      read(ev1,&evt1 ,sizeof(struct input_event) );
485                      DPRINTF("event1: %i %i %i,\n",evt1.type, evt1.code,evt1.value);
486                      if ((evt1.code == 330) && (evt1.value == 1))
487                        {
488                          if ( choice < (bl->size - 1) ) 
489                            {
490                              choice++;
491                              DPRINTF("Choice %i\n", choice);
492                            }
493                          else
494                            {
495                              choice=0;
496                            }
497                          DPRINTF("Selected: %i ,\n",choice );
498                          break;
499                        }
500                    }
501          } while( ( evt.type != EV_KEY || evt.value != 0) || ( evt.type != EV_KEY ||  evt1.value != 0) );
502          // dev/event0 power 116
503          }while( ( evt.code != 169 ) || (bl->size == 0)  );
504        draw_background(fb, "DEBUG by EW: .\n Boot selected ");
505        fb_render(fb);
506                 
507        fclose(f);
508        // reset terminal
509        tcsetattr(fileno(stdin), TCSANOW, &old);
510        fb_destroy(fb);
511        start_kernel(bl->list[choice]);
512        /* When we reach this point then some error has occured */
513        DPRINTF("We should not reach this point!");
514        return -1;
515}