vboot: Hide language selection bar during EC software sync
[depthcharge.git] / src / vboot / screens.c
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * See file CREDITS for list of people who contributed to this
5  * project.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but without any warranty; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17
18 #include <libpayload.h>
19 #include <cbfs.h>
20 #include <gbb_header.h>
21 #include <vboot_api.h>
22 #include <vboot/screens.h>
23 #include "base/graphics.h"
24 #include "drivers/flash/cbfs.h"
25 #include "drivers/video/display.h"
26 #include "vboot/util/commonparams.h"
27
28 /*
29  * This is the base used to specify the size and the coordinate of the image.
30  * For example, height = 40 means 4.0% of the canvas (=drawing area) height.
31  */
32 #define VB_SCALE        1000
33 #define VB_SCALE_HALF   (VB_SCALE / 2)  /* 50.0% */
34
35 /* Height of the text image per line relative to the canvas size */
36 #define VB_TEXT_HEIGHT  40              /* 4.0% */
37
38 /* Indicate width or height is automatically set based on the other value */
39 #define VB_SIZE_AUTO    0
40
41 /* Height of the icons relative to the canvas size */
42 #define VB_ICON_HEIGHT  160
43
44 /* Vertical position and size of the dividers */
45 #define VB_DIVIDER_WIDTH        800     /* 80.0% */
46 #define VB_DIVIDER_V_OFFSET     160     /* 16.0% */
47
48 /* Space between 'MODEL' and a model name */
49 #define VB_MODEL_PADDING        10      /* 1.0 % */
50
51 #define RETURN_ON_ERROR(function_call) do {                             \
52                 VbError_t rv = (function_call);                         \
53                 if (rv)                                                 \
54                         return rv;                                      \
55         } while (0)
56
57 static char initialized = 0;
58 static struct directory *base_graphics;
59 static struct directory *font_graphics;
60 static struct cbfs_media *ro_cbfs;
61 static struct {
62         /* current locale */
63         uint32_t current;
64
65         /* pointer to the localized graphics data and its locale */
66         uint32_t archive_locale;
67         struct directory *archive;
68
69         /* number of supported language and codes: en, ja, ... */
70         uint32_t count;
71         char *codes[256];
72 } locale_data;
73
74 /*
75  * Load archive into RAM
76  */
77 static VbError_t load_archive(const char *name, struct directory **dest)
78 {
79         struct directory *dir;
80         struct dentry *entry;
81         size_t size;
82         int i;
83
84         printf("%s: loading %s\n", __func__, name);
85         *dest = NULL;
86
87         /* load archive from cbfs */
88         dir = cbfs_get_file_content(ro_cbfs, name, CBFS_TYPE_RAW, &size);
89         if (!dir || !size) {
90                 printf("%s: failed to load %s\n", __func__, name);
91                 return VBERROR_INVALID_BMPFV;
92         }
93
94         /* convert endianness of archive header */
95         dir->count = le32toh(dir->count);
96         dir->size = le32toh(dir->size);
97
98         /* validate the total size */
99         if (dir->size != size) {
100                 printf("%s: archive size does not match\n", __func__);
101                 return VBERROR_INVALID_BMPFV;
102         }
103
104         /* validate magic field */
105         if (memcmp(dir->magic, CBAR_MAGIC, sizeof(CBAR_MAGIC))) {
106                 printf("%s: invalid archive magic\n", __func__);
107                 return VBERROR_INVALID_BMPFV;
108         }
109
110         /* validate count field */
111         if (get_first_offset(dir) > dir->size) {
112                 printf("%s: invalid count\n", __func__);
113                 return VBERROR_INVALID_BMPFV;
114         }
115
116         /* convert endianness of file headers */
117         entry = get_first_dentry(dir);
118         for (i = 0; i < dir->count; i++) {
119                 entry[i].offset = le32toh(entry[i].offset);
120                 entry[i].size = le32toh(entry[i].size);
121         }
122
123         *dest = dir;
124
125         return VBERROR_SUCCESS;
126 }
127
128 static VbError_t load_localized_graphics(uint32_t locale)
129 {
130         char str[256];
131
132         /* check whether we've already loaded the archive for this locale */
133         if (locale_data.archive) {
134                 if (locale_data.archive_locale == locale)
135                         return VBERROR_SUCCESS;
136                 /* No need to keep more than one locale graphics at a time */
137                 free(locale_data.archive);
138         }
139
140         /* compose archive name using the language code */
141         snprintf(str, sizeof(str), "locale_%s.bin", locale_data.codes[locale]);
142         RETURN_ON_ERROR(load_archive(str, &locale_data.archive));
143
144         /* Remember what's cached */
145         locale_data.archive_locale = locale;
146
147         return VBERROR_SUCCESS;
148 }
149
150 static struct dentry *find_file_in_archive(const struct directory *dir,
151                                            const char *name)
152 {
153         struct dentry *entry;
154         uintptr_t start;
155         int i;
156
157         if (!dir) {
158                 printf("%s: archive not loaded\n", __func__);
159                 return NULL;
160         }
161
162         /* calculate start of the file content section */
163         start = get_first_offset(dir);
164         entry = get_first_dentry(dir);
165         for (i = 0; i < dir->count; i++) {
166                 if (strncmp(entry[i].name, name, NAME_LENGTH))
167                         continue;
168                 /* validate offset & size */
169                 if (entry[i].offset < start
170                                 || entry[i].offset + entry[i].size > dir->size
171                                 || entry[i].offset > dir->size
172                                 || entry[i].size > dir->size) {
173                         printf("%s: '%s' has invalid offset or size\n",
174                                __func__, name);
175                         return NULL;
176                 }
177                 return &entry[i];
178         }
179
180         printf("%s: file '%s' not found\n", __func__, name);
181
182         return NULL;
183 }
184
185 /*
186  * Find and draw image in archive
187  */
188 static VbError_t draw(struct directory *dir, const char *image_name,
189                       int32_t x, int32_t y, int32_t width, int32_t height,
190                       char pivot)
191 {
192         struct dentry *file;
193
194         file = find_file_in_archive(dir, image_name);
195         if (!file)
196                 return VBERROR_NO_IMAGE_PRESENT;
197
198         struct scale pos = {
199                 .x = { .n = x, .d = VB_SCALE, },
200                 .y = { .n = y, .d = VB_SCALE, },
201         };
202         struct scale dim = {
203                 .x = { .n = width, .d = VB_SCALE, },
204                 .y = { .n = height, .d = VB_SCALE, },
205         };
206
207         return draw_bitmap((uint8_t *)dir + file->offset, file->size,
208                            &pos, pivot, &dim);
209 }
210
211 static VbError_t draw_image(const char *image_name,
212                             int32_t x, int32_t y, int32_t width, int32_t height,
213                             char pivot)
214 {
215         return draw(base_graphics, image_name, x, y, width, height, pivot);
216 }
217
218 static VbError_t draw_image_locale(const char *image_name, uint32_t locale,
219                                    int32_t x, int32_t y,
220                                    int32_t width, int32_t height, char pivot)
221 {
222         VbError_t rv;
223         RETURN_ON_ERROR(load_localized_graphics(locale));
224         rv = draw(locale_data.archive, image_name, x, y, width, height, pivot);
225         if (rv == CBGFX_ERROR_BOUNDARY && width == VB_SIZE_AUTO) {
226                 printf("%s: '%s' overflowed. fit it to canvas width\n",
227                        __func__, image_name);
228                 rv = draw(locale_data.archive, image_name,
229                           x, y, VB_SCALE, VB_SIZE_AUTO, pivot);
230         }
231         return rv;
232 }
233
234 static VbError_t get_image_size(struct directory *dir, const char *image_name,
235                                 int32_t *width, int32_t *height)
236 {
237         struct dentry *file;
238         VbError_t rv;
239
240         file = find_file_in_archive(dir, image_name);
241         if (!file)
242                 return VBERROR_NO_IMAGE_PRESENT;
243
244         struct scale dim = {
245                 .x = { .n = *width, .d = VB_SCALE, },
246                 .y = { .n = *height, .d = VB_SCALE, },
247         };
248
249         rv = get_bitmap_dimension((uint8_t *)dir + file->offset,
250                                   file->size, &dim);
251         if (rv)
252                 return VBERROR_UNKNOWN;
253
254         *width = dim.x.n * VB_SCALE / dim.x.d;
255         *height = dim.y.n * VB_SCALE / dim.y.d;
256
257         return VBERROR_SUCCESS;
258 }
259
260 static VbError_t get_image_size_locale(const char *image_name, uint32_t locale,
261                                        int32_t *width, int32_t *height)
262 {
263         RETURN_ON_ERROR(load_localized_graphics(locale));
264         return get_image_size(locale_data.archive, image_name, width, height);
265 }
266
267 static int draw_icon(const char *image_name)
268 {
269         return draw_image(image_name,
270                           VB_SCALE_HALF, VB_SCALE_HALF,
271                           VB_SIZE_AUTO, VB_ICON_HEIGHT,
272                           PIVOT_H_CENTER|PIVOT_V_BOTTOM);
273 }
274
275 static int draw_text(const char *text, int32_t x, int32_t y,
276                      int32_t height, char pivot)
277 {
278         int32_t w, h;
279         char str[256];
280         while (*text) {
281                 sprintf(str, "idx%03d_%02x.bmp", *text, *text);
282                 w = 0;
283                 h = height;
284                 RETURN_ON_ERROR(get_image_size(font_graphics, str, &w, &h));
285                 RETURN_ON_ERROR(draw(font_graphics, str,
286                                      x, y, VB_SIZE_AUTO, height, pivot));
287                 x += w;
288                 text++;
289         }
290         return VBERROR_SUCCESS;
291 }
292
293 static int get_text_width(const char *text, int32_t *width, int32_t *height)
294 {
295         int32_t w, h;
296         char str[256];
297         while (*text) {
298                 sprintf(str, "idx%03d_%02x.bmp", *text, *text);
299                 w = 0;
300                 h = *height;
301                 RETURN_ON_ERROR(get_image_size(font_graphics, str, &w, &h));
302                 *width += w;
303                 text++;
304         }
305         return VBERROR_SUCCESS;
306 }
307
308 static VbError_t vboot_draw_footer(uint32_t locale)
309 {
310         char *hwid = NULL;
311         int32_t x, y, w1, h1, w2, h2, w3, h3;
312
313         /*
314          * Draw help URL line: 'For help visit http://.../'. It consits of
315          * three parts: [for_help_left.bmp][URL][for_help_right.bmp].
316          * Since the widths vary, we need to get the widths first then calculate
317          * the horizontal positions of the images.
318          */
319         w1 = 0;
320         h1 = VB_TEXT_HEIGHT;
321         RETURN_ON_ERROR(get_image_size_locale("for_help_left.bmp", locale,
322                                               &w1, &h1));
323         w2 = 0;
324         h2 = VB_TEXT_HEIGHT;
325         RETURN_ON_ERROR(get_image_size(base_graphics, "Url.bmp", &w2, &h2));
326
327         w3 = 0;
328         h3 = VB_TEXT_HEIGHT;
329         RETURN_ON_ERROR(get_image_size_locale("for_help_right.bmp", locale,
330                                               &w3, &h3));
331
332         /* Calculate horizontal position to centralize the combined images */
333         x = (VB_SCALE - w1 - w2 - w3) / 2;
334         y = VB_SCALE - VB_DIVIDER_V_OFFSET;
335         if (x < 0) {
336                 /* images are too wide. need to fit them to canvas width */
337                 int32_t total;
338
339                 printf("%s: help line overflowed. fit it to canvas width\n",
340                        __func__);
341                 total = w1 + w2 + w3;
342                 x = 0;
343                 w1 = VB_SCALE * w1 / total;
344                 w2 = VB_SCALE * w2 / total;
345                 w3 = VB_SCALE * w3 / total;
346         }
347         RETURN_ON_ERROR(draw_image_locale("for_help_left.bmp", locale,
348                         x, y, w1, VB_SIZE_AUTO,
349                         PIVOT_H_LEFT|PIVOT_V_TOP));
350         x += w1;
351         RETURN_ON_ERROR(draw_image("Url.bmp",
352                         x, y, w2, VB_SIZE_AUTO,
353                         PIVOT_H_LEFT|PIVOT_V_TOP));
354         x += w2;
355         RETURN_ON_ERROR(draw_image_locale("for_help_right.bmp", locale,
356                         x, y, w3, VB_SIZE_AUTO,
357                         PIVOT_H_LEFT|PIVOT_V_TOP));
358
359         /*
360          * Draw model line: 'Model XYZ'. It consists of two parts: 'Model',
361          * which is locale dependent, and 'XYZ', a model name. Model name
362          * consists of individual font images: 'X' 'Y' 'Z'.
363          */
364         if (is_cparams_initialized()) {
365                 GoogleBinaryBlockHeader *gbb = cparams.gbb_data;
366                 if (gbb)
367                         hwid = (char *)((uintptr_t)gbb + gbb->hwid_offset);
368         }
369         if (!hwid)
370                 hwid = "NOT FOUND";
371
372         w1 = 0;
373         h1 = VB_TEXT_HEIGHT;
374         RETURN_ON_ERROR(get_image_size_locale("model_left.bmp", locale,
375                                               &w1, &h1));
376         w1 += VB_MODEL_PADDING;
377
378         w2 = 0;
379         h2 = VB_TEXT_HEIGHT;
380         RETURN_ON_ERROR(get_text_width(hwid, &w2, &h2));
381         w2 += VB_MODEL_PADDING;
382
383         w3 = 0;
384         h3 = VB_TEXT_HEIGHT;
385         RETURN_ON_ERROR(get_image_size_locale("model_right.bmp", locale,
386                                               &w3, &h3));
387
388         /* Calculate horizontal position to centralize the combined images. */
389         /*
390          * No clever way to redraw the combined images when they overflow but
391          * luckily there is plenty of space for just 'model' + model name.
392          */
393         x = (VB_SCALE - w1 - w2 - w3) / 2;
394         y += VB_TEXT_HEIGHT;
395         RETURN_ON_ERROR(draw_image_locale("model_left.bmp", locale,
396                         x, y, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
397                         PIVOT_H_LEFT|PIVOT_V_TOP));
398         x += w1;
399         RETURN_ON_ERROR(draw_text(hwid, x, y, VB_TEXT_HEIGHT,
400                         PIVOT_H_LEFT|PIVOT_V_TOP));
401         x += w2;
402         RETURN_ON_ERROR(draw_image_locale("model_right.bmp", locale,
403                         x, y, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
404                         PIVOT_H_LEFT|PIVOT_V_TOP));
405
406         return VBERROR_SUCCESS;
407 }
408
409 /*
410  * Draws the language section at the top right corner. The language text image
411  * is placed in the middle surrounded by arrows on each side.
412  */
413 static VbError_t vboot_draw_language(uint32_t locale)
414 {
415         int32_t w, h, x;
416
417         /*
418          * Right arrow starts from the right edge of the divider, which is
419          * positioned horizontally in the center.
420          */
421         x = VB_SCALE_HALF + VB_DIVIDER_WIDTH / 2;
422
423         /* Draw the right arrow */
424         w = 0;
425         h = VB_TEXT_HEIGHT;
426         RETURN_ON_ERROR(get_image_size(base_graphics, "arrow_right.bmp",
427                                        &w, &h));
428         x -= w;
429         RETURN_ON_ERROR(draw_image("arrow_right.bmp",
430                         x, VB_DIVIDER_V_OFFSET, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
431                         PIVOT_H_LEFT|PIVOT_V_BOTTOM));
432
433         /* Draw the language name */
434         w = 0;
435         h = VB_TEXT_HEIGHT;
436         RETURN_ON_ERROR(get_image_size_locale("language.bmp", locale, &w, &h));
437         x -= w;
438         RETURN_ON_ERROR(draw_image_locale("language.bmp", locale,
439                         x, VB_DIVIDER_V_OFFSET, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
440                         PIVOT_H_LEFT|PIVOT_V_BOTTOM));
441
442         /* Draw the left arrow */
443         w = 0;
444         h = VB_TEXT_HEIGHT;
445         RETURN_ON_ERROR(get_image_size(base_graphics, "arrow_left.bmp",
446                                        &w, &h));
447         x -= w;
448         RETURN_ON_ERROR(draw_image("arrow_left.bmp",
449                         x, VB_DIVIDER_V_OFFSET, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
450                         PIVOT_H_LEFT|PIVOT_V_BOTTOM));
451
452         return VBERROR_SUCCESS;
453 }
454
455 static VbError_t draw_base_screen(uint32_t locale, int show_language)
456 {
457         const struct rgb_color white = { 0xff, 0xff, 0xff };
458
459         if (clear_screen(&white))
460                 return VBERROR_UNKNOWN;
461         RETURN_ON_ERROR(draw_image("chrome_logo.bmp",
462                         (VB_SCALE - VB_DIVIDER_WIDTH)/2,
463                         /* '-10' to keep the logo lifted up from the divider */
464                         VB_DIVIDER_V_OFFSET - 10,
465                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
466                         PIVOT_H_LEFT|PIVOT_V_BOTTOM));
467
468         if (show_language)
469                 RETURN_ON_ERROR(vboot_draw_language(locale));
470
471         RETURN_ON_ERROR(draw_image("divider_top.bmp",
472                         VB_SCALE_HALF, VB_DIVIDER_V_OFFSET,
473                         VB_DIVIDER_WIDTH, VB_SIZE_AUTO,
474                         PIVOT_H_CENTER|PIVOT_V_TOP));
475         RETURN_ON_ERROR(draw_image("divider_btm.bmp",
476                         VB_SCALE_HALF, VB_SCALE - VB_DIVIDER_V_OFFSET,
477                         VB_DIVIDER_WIDTH, VB_SIZE_AUTO,
478                         PIVOT_H_CENTER|PIVOT_V_BOTTOM));
479
480         RETURN_ON_ERROR(vboot_draw_footer(locale));
481
482         return VBERROR_SUCCESS;
483 }
484
485 static VbError_t vboot_draw_base_screen(uint32_t locale)
486 {
487         return draw_base_screen(locale, 1);
488 }
489
490 static VbError_t vboot_draw_base_screen_without_language(uint32_t locale)
491 {
492         return draw_base_screen(locale, 0);
493 }
494
495 static VbError_t vboot_draw_blank(uint32_t locale)
496 {
497         video_console_clear();
498         return VBERROR_SUCCESS;
499 }
500
501 static VbError_t vboot_draw_developer_warning(uint32_t locale)
502 {
503         RETURN_ON_ERROR(vboot_draw_base_screen(locale));
504         RETURN_ON_ERROR(draw_icon("VerificationOff.bmp"));
505         RETURN_ON_ERROR(draw_image_locale("verif_off.bmp", locale,
506                         VB_SCALE_HALF, VB_SCALE_HALF,
507                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
508                         PIVOT_H_CENTER|PIVOT_V_TOP));
509         RETURN_ON_ERROR(draw_image_locale("devmode.bmp", locale,
510                         VB_SCALE_HALF, VB_SCALE_HALF + VB_TEXT_HEIGHT,
511                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
512                         PIVOT_H_CENTER|PIVOT_V_TOP));
513         return VBERROR_SUCCESS;
514 }
515
516 static VbError_t vboot_draw_recovery_remove(uint32_t locale)
517 {
518         RETURN_ON_ERROR(vboot_draw_base_screen(locale));
519         RETURN_ON_ERROR(draw_image_locale("remove.bmp", locale,
520                         VB_SCALE_HALF, VB_SCALE_HALF - VB_ICON_HEIGHT,
521                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
522                         PIVOT_H_CENTER|PIVOT_V_BOTTOM));
523         RETURN_ON_ERROR(draw_image("RemoveDevices.bmp",
524                         VB_SCALE_HALF, VB_SCALE_HALF,
525                         VB_SIZE_AUTO, VB_ICON_HEIGHT * 2,
526                         PIVOT_H_CENTER|PIVOT_V_CENTER));
527         return VBERROR_SUCCESS;
528 }
529
530 static VbError_t vboot_draw_recovery_no_good(uint32_t locale)
531 {
532         RETURN_ON_ERROR(vboot_draw_base_screen(locale));
533         RETURN_ON_ERROR(draw_image_locale("yuck.bmp", locale,
534                         VB_SCALE_HALF, VB_SCALE_HALF,
535                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
536                         PIVOT_H_CENTER|PIVOT_V_BOTTOM));
537         RETURN_ON_ERROR(draw_image("BadDevices.bmp",
538                         VB_SCALE_HALF, VB_SCALE_HALF,
539                         VB_SIZE_AUTO, VB_ICON_HEIGHT,
540                         PIVOT_H_CENTER|PIVOT_V_TOP));
541         return VBERROR_SUCCESS;
542 }
543
544 static VbError_t vboot_draw_recovery_insert(uint32_t locale)
545 {
546         RETURN_ON_ERROR(vboot_draw_base_screen(locale));
547         RETURN_ON_ERROR(draw_image_locale("insert.bmp", locale,
548                         VB_SCALE_HALF, VB_SCALE_HALF - VB_ICON_HEIGHT,
549                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
550                         PIVOT_H_CENTER|PIVOT_V_BOTTOM));
551         RETURN_ON_ERROR(draw_image("InsertDevices.bmp",
552                         VB_SCALE_HALF, VB_SCALE_HALF,
553                         VB_SIZE_AUTO, VB_ICON_HEIGHT * 2,
554                         PIVOT_H_CENTER|PIVOT_V_CENTER));
555         return VBERROR_SUCCESS;
556 }
557
558 static VbError_t vboot_draw_recovery_to_dev(uint32_t locale)
559 {
560         RETURN_ON_ERROR(vboot_draw_base_screen(locale));
561         RETURN_ON_ERROR(draw_image_locale("todev.bmp", locale,
562                         VB_SCALE_HALF, VB_SCALE_HALF,
563                         VB_SIZE_AUTO, VB_TEXT_HEIGHT * 4,
564                         PIVOT_H_CENTER|PIVOT_V_CENTER));
565         return VBERROR_SUCCESS;
566 }
567
568 static VbError_t vboot_draw_developer_to_norm(uint32_t locale)
569 {
570         RETURN_ON_ERROR(vboot_draw_base_screen(locale));
571         RETURN_ON_ERROR(draw_icon("VerificationOff.bmp"));
572         RETURN_ON_ERROR(draw_image_locale("verif_off.bmp", locale,
573                         VB_SCALE_HALF, VB_SCALE_HALF,
574                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
575                         PIVOT_H_CENTER|PIVOT_V_TOP));
576         RETURN_ON_ERROR(draw_image_locale("tonorm.bmp", locale,
577                         VB_SCALE_HALF, VB_SCALE_HALF + VB_TEXT_HEIGHT,
578                         VB_SIZE_AUTO, VB_TEXT_HEIGHT * 3,
579                         PIVOT_H_CENTER|PIVOT_V_TOP));
580         return VBERROR_SUCCESS;
581 }
582
583 static VbError_t vboot_draw_wait(uint32_t locale)
584 {
585         /*
586          * Currently, language cannot be changed while EC software sync is
587          * taking place because keyboard is disabled.
588          */
589         RETURN_ON_ERROR(vboot_draw_base_screen_without_language(locale));
590         RETURN_ON_ERROR(draw_image_locale("update.bmp", locale,
591                         VB_SCALE_HALF, VB_SCALE_HALF,
592                         VB_SIZE_AUTO, VB_TEXT_HEIGHT * 3,
593                         PIVOT_H_CENTER|PIVOT_V_CENTER));
594         return VBERROR_SUCCESS;
595 }
596
597 static VbError_t vboot_draw_to_norm_confirmed(uint32_t locale)
598 {
599         RETURN_ON_ERROR(vboot_draw_base_screen(locale));
600         RETURN_ON_ERROR(draw_icon("VerificationOn.bmp"));
601         RETURN_ON_ERROR(draw_image_locale("verif_on.bmp", locale,
602                         VB_SCALE_HALF, VB_SCALE_HALF,
603                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
604                         PIVOT_H_CENTER|PIVOT_V_TOP));
605         RETURN_ON_ERROR(draw_image_locale("reboot_erase.bmp", locale,
606                         VB_SCALE_HALF, VB_SCALE_HALF + VB_TEXT_HEIGHT,
607                         VB_SIZE_AUTO, VB_TEXT_HEIGHT,
608                         PIVOT_H_CENTER|PIVOT_V_TOP));
609         return VBERROR_SUCCESS;
610 }
611
612 static VbError_t vboot_draw_os_broken(uint32_t locale)
613 {
614         RETURN_ON_ERROR(vboot_draw_base_screen(locale));
615         RETURN_ON_ERROR(draw_icon("Warning.bmp"));
616         RETURN_ON_ERROR(draw_image_locale("os_broken.bmp", locale,
617                         VB_SCALE_HALF, VB_SCALE_HALF,
618                         VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
619                         PIVOT_H_CENTER|PIVOT_V_TOP));
620         return VBERROR_SUCCESS;
621 }
622
623 /* we may export this in the future for the board customization */
624 struct vboot_screen_descriptor {
625         uint32_t id;                            /* VB_SCREEN_* */
626         VbError_t (*draw)(uint32_t locale);     /* draw function */
627         const char *mesg;                       /* fallback message */
628 };
629
630 static const struct vboot_screen_descriptor vboot_screens[] = {
631         {
632                 .id = VB_SCREEN_BLANK,
633                 .draw = vboot_draw_blank,
634                 .mesg = NULL,
635         },
636         {
637                 .id = VB_SCREEN_DEVELOPER_WARNING,
638                 .draw = vboot_draw_developer_warning,
639                 .mesg = "OS verification is OFF\n"
640                         "Press SPACE to re-enable.\n",
641         },
642         {
643                 .id = VB_SCREEN_RECOVERY_REMOVE,
644                 .draw = vboot_draw_recovery_remove,
645                 .mesg = "Please remove all external devices to begin recovery\n",
646         },
647         {
648                 .id = VB_SCREEN_RECOVERY_NO_GOOD,
649                 .draw = vboot_draw_recovery_no_good,
650                 .mesg = "The device you inserted does not contain Chrome OS.\n",
651         },
652         {
653                 .id = VB_SCREEN_RECOVERY_INSERT,
654                 .draw = vboot_draw_recovery_insert,
655                 .mesg = "Chrome OS is missing or damaged.\n"
656                         "Please insert a recovery USB stick or SD card.\n",
657         },
658         {
659                 .id = VB_SCREEN_RECOVERY_TO_DEV,
660                 .draw = vboot_draw_recovery_to_dev,
661                 .mesg = "To turn OS verificaion OFF, press ENTER.\n"
662                         "Your system will reboot and local data will be cleared.\n"
663                         "To go back, press ESC.\n",
664         },
665         {
666                 .id = VB_SCREEN_DEVELOPER_TO_NORM,
667                 .draw = vboot_draw_developer_to_norm,
668                 .mesg = "OS verification is OFF\n"
669                         "Press ENTER to confirm you wish to turn OS verification on.\n"
670                         "Your system will reboot and local data will be cleared.\n"
671                         "To go back, press ESC.\n",
672         },
673         {
674                 .id = VB_SCREEN_WAIT,
675                 .draw = vboot_draw_wait,
676                 .mesg = "Your system is applying a critical update.\n"
677                         "Please do not turn off.\n",
678         },
679         {
680                 .id = VB_SCREEN_TO_NORM_CONFIRMED,
681                 .draw = vboot_draw_to_norm_confirmed,
682                 .mesg = "OS verification is ON\n"
683                         "Your system will reboot and local data will be cleared.\n",
684         },
685         {
686                 .id = VB_SCREEN_OS_BROKEN,
687                 .draw = vboot_draw_os_broken,
688                 .mesg = "Chrome OS may be broken.\n"
689                         "Remove media and initiate recovery.\n",
690         },
691 };
692
693 static const struct vboot_screen_descriptor *get_screen_descriptor(uint32_t id)
694 {
695         int i;
696         for (i = 0; i < ARRAY_SIZE(vboot_screens); i++) {
697                 if (vboot_screens[i].id == id)
698                         return &vboot_screens[i];
699         }
700         return NULL;
701 }
702
703 static void print_fallback_message(const struct vboot_screen_descriptor *desc)
704 {
705         const struct rgb_color white = { 0xff, 0xff, 0xff };
706
707         if (desc->mesg)
708                 graphics_print_single_text_block(desc->mesg, &white, 0, 15,
709                                                  VIDEO_PRINTF_ALIGN_CENTER);
710         else
711                 clear_screen(&white);
712 }
713
714 static VbError_t draw_screen(uint32_t screen_type, uint32_t locale)
715 {
716         VbError_t rv = VBERROR_UNKNOWN;
717         const struct vboot_screen_descriptor *desc;
718
719         desc = get_screen_descriptor(screen_type);
720         if (!desc) {
721                 printf("Not a valid screen type: 0x%x\n", screen_type);
722                 return VBERROR_INVALID_SCREEN_INDEX;
723         }
724
725         if (locale >= locale_data.count) {
726                 printf("Unsupported locale (%d)\n", locale);
727                 print_fallback_message(desc);
728                 return VBERROR_INVALID_PARAMETER;
729         }
730
731         /* if no drawing function is registered, fallback msg will be printed */
732         if (desc->draw) {
733                 rv = desc->draw(locale);
734                 if (rv)
735                         printf("Drawing failed (0x%x)\n", rv);
736         }
737         if (rv) {
738                 print_fallback_message(desc);
739                 return VBERROR_SCREEN_DRAW;
740         }
741
742         return VBERROR_SUCCESS;
743 }
744
745 static void vboot_init_locale(void)
746 {
747         char *locales, *loc_start, *loc;
748         size_t size;
749
750         locale_data.count = 0;
751
752         /* Load locale list from cbfs */
753         locales = cbfs_get_file_content(ro_cbfs, "locales",
754                                         CBFS_TYPE_RAW, &size);
755         if (!locales || !size) {
756                 printf("%s: locale list not found\n", __func__);
757                 return;
758         }
759
760         /* Copy the file and null-terminate it */
761         loc_start = malloc(size + 1);
762         if (!loc_start) {
763                 printf("%s: out of memory\n", __func__);
764                 free(locales);
765                 return;
766         }
767         memcpy(loc_start, locales, size);
768         loc_start[size] = '\0';
769
770         /* Parse the list */
771         printf("%s: Supported locales:", __func__);
772         loc = loc_start;
773         while (loc - loc_start < size
774                         && locale_data.count < ARRAY_SIZE(locale_data.codes)) {
775                 char *lang = strsep(&loc, "\n");
776                 if (!lang || !strlen(lang))
777                         break;
778                 printf(" %s,", lang);
779                 locale_data.codes[locale_data.count] = lang;
780                 locale_data.count++;
781         }
782         free(locales);
783
784         printf(" (%d locales)\n", locale_data.count);
785 }
786
787 static VbError_t vboot_init_screen(void)
788 {
789         if (ro_cbfs == NULL) {
790                 ro_cbfs = cbfs_ro_media();
791                 if (ro_cbfs == NULL) {
792                         printf("No RO CBFS found.\n");
793                         return VBERROR_UNKNOWN;
794                 }
795         }
796
797         if (graphics_init())
798                 return VBERROR_UNKNOWN;
799
800         /* create a list of supported locales */
801         vboot_init_locale();
802
803         /* load generic (location-free) graphics data. ignore errors.
804          * fallback screens will be drawn for missing data */
805         load_archive("vbgfx.bin", &base_graphics);
806
807         /* load font graphics */
808         load_archive("font.bin", &font_graphics);
809
810         /* reset localized graphics. we defer loading it. */
811         locale_data.archive = NULL;
812
813         initialized = 1;
814
815         return VBERROR_SUCCESS;
816 }
817
818 int vboot_draw_screen(uint32_t screen, uint32_t locale)
819 {
820         static uint32_t current_screen = VB_SCREEN_BLANK;
821
822         printf("%s: screen=0x%x locale=%d\n", __func__, screen, locale);
823
824         if (!initialized) {
825                 if (vboot_init_screen())
826                         return VBERROR_UNKNOWN;
827         }
828
829         /* If requested screen is the same as the current one, we're done. */
830         if (screen == current_screen && locale == locale_data.current)
831                 return VBERROR_SUCCESS;
832
833         /* If the screen is blank, turn off the backlight; else turn it on. */
834         backlight_update(VB_SCREEN_BLANK == screen ? 0 : 1);
835
836         /* TODO: draw only locale dependent part if current_screen == screen */
837         RETURN_ON_ERROR(draw_screen(screen, locale));
838
839         current_screen = screen;
840         locale_data.current = locale;
841
842         return VBERROR_SUCCESS;
843 }
844
845 int vboot_get_locale_count(void)
846 {
847         if (!initialized) {
848                 if (vboot_init_screen())
849                         return VBERROR_UNKNOWN;
850         }
851         return locale_data.count;
852 }