blob: 7958c3e22a28270bcd7ea9e3357e121fb2a789da [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <assert.h>
#include <libpayload.h>
#include <cbfs.h>
#include <gbb_header.h>
#include <vboot_api.h>
#include <vboot/screens.h>
#include "base/list.h"
#include "boot/payload.h"
#include "config.h"
#include "drivers/flash/cbfs.h"
#include "drivers/video/display.h"
#include "vboot/util/commonparams.h"
/*
* This is the base used to specify the size and the coordinate of the image.
* For example, height = 40 means 4.0% of the canvas (=drawing area) height.
*/
#define VB_SCALE 1000 /* 100.0% */
#define VB_SCALE_HALF (VB_SCALE / 2) /* 50.0% */
/* Height of the text image per line relative to the canvas size */
#define VB_TEXT_HEIGHT 36 /* 3.6% */
/* Chrome logo size and distance from the divider */
#define VB_LOGO_HEIGHT 39 /* 3.9% */
#define VB_LOGO_LIFTUP 0
/* Indicate width or height is automatically set based on the other value */
#define VB_SIZE_AUTO 0
/* Height of the icons relative to the canvas size */
#define VB_ICON_HEIGHT 169 /* 16.9% */
/* Height of InsertDevices, RemoveDevices */
#define VB_DEVICE_HEIGHT 371 /* 37.1% */
/* Vertical position and size of the dividers */
#define VB_DIVIDER_WIDTH 900 /* 90.0% -> 5% padding on each side */
#define VB_DIVIDER_V_OFFSET 160 /* 16.0% */
/* Positions of the text for the altfw menu */
#define VB_ALTFW_SEQ_LEFT 160
#define VB_ALTFW_NAME_LEFT 200
#define VB_ALTFW_DESC_LEFT 400
/* Space between sections of text */
#define VB_PADDING 3 /* 0.3 % */
/* Downshift for vertical characters to match middle of text in Noto Sans */
#define VB_ARROW_V_OFF 3 /* 0.3 % */
/* draw_box() uses a different scale then we do, this helps with conversions */
#define VB_TO_CANVAS(offset) (offset * CANVAS_SCALE / VB_SCALE)
#define RETURN_ON_ERROR(function_call) do { \
VbError_t rv = (function_call); \
if (rv) \
return rv; \
} while (0)
static char initialized = 0;
static int prev_lang_page_num = -1;
static int prev_selected_index = -1;
static struct directory *base_graphics;
static struct directory *font_graphics;
static struct cbfs_media *ro_cbfs;
static struct {
/* current locale */
uint32_t current;
/* pointer to the localized graphics data and its locale */
uint32_t archive_locale;
struct directory *archive;
/* number of supported language and codes: en, ja, ... */
uint32_t count;
char *codes[256];
} locale_data;
/* params structure for vboot draw functions */
struct params {
uint32_t locale;
uint32_t selected_index;
uint32_t disabled_idx_mask;
uint32_t redraw_base;
const VbScreenData *data;
};
/* struct for passing around menu string arrays */
struct menu {
const char *const *strings;
uint32_t count;
};
/*
* Load archive into RAM
*/
static VbError_t load_archive(const char *name, struct directory **dest)
{
struct directory *dir;
struct dentry *entry;
size_t size;
int i;
printf("%s: loading %s\n", __func__, name);
*dest = NULL;
/* load archive from cbfs */
dir = cbfs_get_file_content(ro_cbfs, name, CBFS_TYPE_RAW, &size);
if (!dir || !size) {
printf("%s: failed to load %s\n", __func__, name);
return VBERROR_INVALID_BMPFV;
}
/* convert endianness of archive header */
dir->count = le32toh(dir->count);
dir->size = le32toh(dir->size);
/* validate the total size */
if (dir->size != size) {
printf("%s: archive size does not match\n", __func__);
return VBERROR_INVALID_BMPFV;
}
/* validate magic field */
if (memcmp(dir->magic, CBAR_MAGIC, sizeof(CBAR_MAGIC))) {
printf("%s: invalid archive magic\n", __func__);
return VBERROR_INVALID_BMPFV;
}
/* validate count field */
if (get_first_offset(dir) > dir->size) {
printf("%s: invalid count\n", __func__);
return VBERROR_INVALID_BMPFV;
}
/* convert endianness of file headers */
entry = get_first_dentry(dir);
for (i = 0; i < dir->count; i++) {
entry[i].offset = le32toh(entry[i].offset);
entry[i].size = le32toh(entry[i].size);
}
*dest = dir;
return VBERROR_SUCCESS;
}
static VbError_t load_localized_graphics(uint32_t locale)
{
char str[256];
/* check whether we've already loaded the archive for this locale */
if (locale_data.archive) {
if (locale_data.archive_locale == locale)
return VBERROR_SUCCESS;
/* No need to keep more than one locale graphics at a time */
free(locale_data.archive);
}
/* compose archive name using the language code */
snprintf(str, sizeof(str), "locale_%s.bin", locale_data.codes[locale]);
RETURN_ON_ERROR(load_archive(str, &locale_data.archive));
/* Remember what's cached */
locale_data.archive_locale = locale;
return VBERROR_SUCCESS;
}
static struct dentry *find_file_in_archive(const struct directory *dir,
const char *name)
{
struct dentry *entry;
uintptr_t start;
int i;
if (!dir) {
printf("%s: archive not loaded\n", __func__);
return NULL;
}
/* calculate start of the file content section */
start = get_first_offset(dir);
entry = get_first_dentry(dir);
for (i = 0; i < dir->count; i++) {
if (strncmp(entry[i].name, name, NAME_LENGTH))
continue;
/* validate offset & size */
if (entry[i].offset < start
|| entry[i].offset + entry[i].size > dir->size
|| entry[i].offset > dir->size
|| entry[i].size > dir->size) {
printf("%s: '%s' has invalid offset or size\n",
__func__, name);
return NULL;
}
return &entry[i];
}
printf("%s: file '%s' not found\n", __func__, name);
return NULL;
}
/*
* Find and draw image in archive
*/
static VbError_t draw(struct directory *dir, const char *image_name,
int32_t x, int32_t y, int32_t width, int32_t height,
uint32_t flags)
{
struct dentry *file;
void *bitmap;
file = find_file_in_archive(dir, image_name);
if (!file)
return VBERROR_NO_IMAGE_PRESENT;
bitmap = (uint8_t *)dir + file->offset;
struct scale pos = {
.x = { .n = x, .d = VB_SCALE, },
.y = { .n = y, .d = VB_SCALE, },
};
struct scale dim = {
.x = { .n = width, .d = VB_SCALE, },
.y = { .n = height, .d = VB_SCALE, },
};
if (get_bitmap_dimension(bitmap, file->size, &dim))
return VBERROR_UNKNOWN;
if ((int64_t)dim.x.n * VB_SCALE <= (int64_t)dim.x.d * VB_DIVIDER_WIDTH)
return draw_bitmap((uint8_t *)dir + file->offset, file->size,
&pos, &dim, flags);
/*
* If we get here the image is too wide, so fit it to the content width.
* This only works if it is horizontally centered (x == VB_SCALE_HALF
* and flags & PIVOT_H_CENTER), but that applies to our current stuff
* which might be too wide (locale-dependent strings). Only exception is
* the "For help" footer, which was already fitted in its own function.
*/
printf("vbgfx: '%s' too wide, fitting to content width\n", image_name);
dim.x.n = VB_DIVIDER_WIDTH;
dim.x.d = VB_SCALE;
dim.y.n = VB_SIZE_AUTO;
dim.y.d = VB_SCALE;
return draw_bitmap((uint8_t *)dir + file->offset, file->size,
&pos, &dim, flags);
}
static VbError_t draw_image(const char *image_name,
int32_t x, int32_t y, int32_t width, int32_t height,
uint32_t pivot)
{
return draw(base_graphics, image_name, x, y, width, height, pivot);
}
static VbError_t draw_image_locale(const char *image_name, uint32_t locale,
int32_t x, int32_t y, int32_t w, int32_t h,
uint32_t flags)
{
RETURN_ON_ERROR(load_localized_graphics(locale));
return draw(locale_data.archive, image_name, x, y, w, h, flags);
}
static VbError_t get_image_size(struct directory *dir, const char *image_name,
int32_t *width, int32_t *height)
{
struct dentry *file;
VbError_t rv;
file = find_file_in_archive(dir, image_name);
if (!file)
return VBERROR_NO_IMAGE_PRESENT;
struct scale dim = {
.x = { .n = *width, .d = VB_SCALE, },
.y = { .n = *height, .d = VB_SCALE, },
};
rv = get_bitmap_dimension((uint8_t *)dir + file->offset,
file->size, &dim);
if (rv)
return VBERROR_UNKNOWN;
*width = dim.x.n * VB_SCALE / dim.x.d;
*height = dim.y.n * VB_SCALE / dim.y.d;
return VBERROR_SUCCESS;
}
static VbError_t get_image_size_locale(const char *image_name, uint32_t locale,
int32_t *width, int32_t *height)
{
RETURN_ON_ERROR(load_localized_graphics(locale));
return get_image_size(locale_data.archive, image_name, width, height);
}
static int draw_icon(const char *image_name)
{
return draw_image(image_name,
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_ICON_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_BOTTOM);
}
static const char *get_char_image_file(int height, const char c, int32_t *width)
{
int ret;
static char filename[16]; // result becomes invalid after next call
const char pattern[] = "idx%03d_%02x.bmp";
snprintf(filename, sizeof(filename), pattern, c, c);
*width = VB_SIZE_AUTO;
ret = get_image_size(font_graphics, filename, width, &height);
if (ret != VBERROR_SUCCESS) {
snprintf(filename, sizeof(filename), pattern, '?', '?');
*width = VB_SIZE_AUTO;
ret = get_image_size(font_graphics, filename, width, &height);
if (ret != VBERROR_SUCCESS)
return NULL;
printf("ERROR: Trying to display unprintable char: %#.2x\n", c);
}
return filename;
}
static int get_text_width(int32_t height, const char *text, int32_t *width)
{
const char *char_filename;
*width = 0;
while (*text) {
int char_width;
char_filename = get_char_image_file(height, *text, &char_width);
if (!char_filename)
return VBERROR_NO_IMAGE_PRESENT;
*width += char_width;
text++;
}
return VBERROR_SUCCESS;
}
static int draw_text(const char *text, int32_t x, int32_t y,
int32_t height, uint32_t pivot)
{
int32_t char_width;
const char *char_filename;
if (pivot & PIVOT_H_CENTER) {
char_width = VB_SIZE_AUTO;
RETURN_ON_ERROR(get_text_width(height, text, &char_width));
pivot &= ~(PIVOT_H_CENTER);
pivot |= PIVOT_H_LEFT;
x -= char_width / 2;
}
while (*text) {
char_filename = get_char_image_file(height, *text, &char_width);
if (!char_filename)
return VBERROR_NO_IMAGE_PRESENT;
RETURN_ON_ERROR(draw(font_graphics, char_filename,
x, y, VB_SIZE_AUTO, height, pivot));
x += char_width;
text++;
}
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_footer(uint32_t locale)
{
char *hwid = NULL;
int32_t x, y, w1, h1, w2, h2, w3, h3;
int32_t total;
/*
* Draw help URL line: 'For help visit http://.../'. It consists of
* three parts: [for_help_left.bmp][URL][for_help_right.bmp].
* Since the widths vary, we need to get the widths first then calculate
* the horizontal positions of the images.
*/
w1 = VB_SIZE_AUTO;
h1 = VB_TEXT_HEIGHT;
/* Expected to fail in locales which don't have left part */
get_image_size_locale("for_help_left.bmp", locale, &w1, &h1);
w2 = VB_SIZE_AUTO;
h2 = VB_TEXT_HEIGHT;
RETURN_ON_ERROR(get_image_size(base_graphics, "Url.bmp", &w2, &h2));
w3 = VB_SIZE_AUTO;
h3 = VB_TEXT_HEIGHT;
/* Expected to fail in locales which don't have right part */
get_image_size_locale("for_help_right.bmp", locale, &w3, &h3);
total = w1 + VB_PADDING + w2 + VB_PADDING + w3;
y = VB_SCALE - VB_DIVIDER_V_OFFSET;
if (VB_DIVIDER_WIDTH - total >= 0) {
/* Calculate position to centralize the images combined */
x = (VB_SCALE - total) / 2;
/* Expected to fail in locales which don't have left part */
draw_image_locale("for_help_left.bmp", locale,
x, y, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP);
x += w1 + VB_PADDING;
RETURN_ON_ERROR(draw_image("Url.bmp",
x, y,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP));
x += w2 + VB_PADDING;
/* Expected to fail in locales which don't have right part */
draw_image_locale("for_help_right.bmp", locale,
x, y, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP);
} else {
int32_t pad;
/* images are too wide. need to fit them to content width */
printf("%s: help line overflowed. fit it to content width\n",
__func__);
x = (VB_SCALE - VB_DIVIDER_WIDTH) / 2;
/* Shrink all images */
w1 = VB_DIVIDER_WIDTH * w1 / total;
w2 = VB_DIVIDER_WIDTH * w2 / total;
w3 = VB_DIVIDER_WIDTH * w3 / total;
pad = VB_DIVIDER_WIDTH * VB_PADDING / total;
/* Render using width as a base */
draw_image_locale("for_help_left.bmp", locale,
x, y, w1, VB_SIZE_AUTO,
PIVOT_H_LEFT|PIVOT_V_TOP);
x += w1 + pad;
RETURN_ON_ERROR(draw_image("Url.bmp",
x, y, w2, VB_SIZE_AUTO,
PIVOT_H_LEFT|PIVOT_V_TOP));
x += w2 + pad;
draw_image_locale("for_help_right.bmp", locale,
x, y, w3, VB_SIZE_AUTO,
PIVOT_H_LEFT|PIVOT_V_TOP);
}
/*
* Draw model line: 'Model XYZ'. It consists of two parts: 'Model',
* which is locale dependent, and 'XYZ', a model name. Model name
* consists of individual font images: 'X' 'Y' 'Z'.
*/
if (is_cparams_initialized()) {
GoogleBinaryBlockHeader *gbb = cparams.gbb_data;
if (gbb)
hwid = (char *)((uintptr_t)gbb + gbb->hwid_offset);
}
if (!hwid)
hwid = "NOT FOUND";
w1 = VB_SIZE_AUTO;
h1 = VB_TEXT_HEIGHT;
get_image_size_locale("model_left.bmp", locale, &w1, &h1);
w1 += VB_PADDING;
w2 = VB_SIZE_AUTO;
h2 = VB_TEXT_HEIGHT;
RETURN_ON_ERROR(get_text_width(h2, hwid, &w2));
w2 += VB_PADDING;
w3 = VB_SIZE_AUTO;
h3 = VB_TEXT_HEIGHT;
get_image_size_locale("model_right.bmp", locale, &w3, &h3);
/* Calculate horizontal position to centralize the combined images. */
/*
* No clever way to redraw the combined images when they overflow but
* luckily there is plenty of space for just 'model' + model name.
*/
x = (VB_SCALE - w1 - w2 - w3) / 2;
y += VB_TEXT_HEIGHT;
draw_image_locale("model_left.bmp", locale,
x, y, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP);
x += w1;
RETURN_ON_ERROR(draw_text(hwid, x, y, VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP));
x += w2;
draw_image_locale("model_right.bmp", locale,
x, y, VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP);
return VBERROR_SUCCESS;
}
/*
* Draws the language section at the top right corner. The language text image
* is placed in the middle surrounded by arrows on each side.
*/
static VbError_t vboot_draw_language(uint32_t locale)
{
int32_t w, h, x;
/*
* Right arrow starts from the right edge of the divider, which is
* positioned horizontally in the center.
*/
x = VB_SCALE_HALF + VB_DIVIDER_WIDTH / 2;
/* Draw right arrow */
if (!CONFIG(DETACHABLE_UI)) {
w = VB_SIZE_AUTO;
h = VB_TEXT_HEIGHT;
RETURN_ON_ERROR(draw_image("arrow_right.bmp", x,
VB_DIVIDER_V_OFFSET + VB_ARROW_V_OFF,
w, h, PIVOT_H_RIGHT|PIVOT_V_BOTTOM));
RETURN_ON_ERROR(get_image_size(base_graphics, "arrow_right.bmp",
&w, &h));
x -= w + VB_PADDING;
}
/* Draw language name */
w = VB_SIZE_AUTO;
h = VB_TEXT_HEIGHT;
RETURN_ON_ERROR(draw_image_locale("language.bmp", locale,
x, VB_DIVIDER_V_OFFSET, w, h,
PIVOT_H_RIGHT|PIVOT_V_BOTTOM));
RETURN_ON_ERROR(get_image_size_locale("language.bmp", locale, &w, &h));
if (!CONFIG(DETACHABLE_UI)) {
x -= w + VB_PADDING;
/* Draw left arrow */
w = VB_SIZE_AUTO;
h = VB_TEXT_HEIGHT;
RETURN_ON_ERROR(draw_image("arrow_left.bmp", x,
VB_DIVIDER_V_OFFSET + VB_ARROW_V_OFF,
w, h, PIVOT_H_RIGHT|PIVOT_V_BOTTOM));
}
return VBERROR_SUCCESS;
}
static VbError_t draw_base_screen(uint32_t locale, int show_language)
{
if (clear_screen(&color_white))
return VBERROR_UNKNOWN;
RETURN_ON_ERROR(draw_image("chrome_logo.bmp",
(VB_SCALE - VB_DIVIDER_WIDTH)/2,
VB_DIVIDER_V_OFFSET - VB_LOGO_LIFTUP,
VB_SIZE_AUTO, VB_LOGO_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_BOTTOM));
if (show_language)
RETURN_ON_ERROR(vboot_draw_language(locale));
RETURN_ON_ERROR(draw_image("divider_top.bmp",
VB_SCALE_HALF, VB_DIVIDER_V_OFFSET,
VB_DIVIDER_WIDTH, VB_SIZE_AUTO,
PIVOT_H_CENTER|PIVOT_V_TOP));
RETURN_ON_ERROR(draw_image("divider_btm.bmp",
VB_SCALE_HALF, VB_SCALE - VB_DIVIDER_V_OFFSET,
VB_DIVIDER_WIDTH, VB_SIZE_AUTO,
PIVOT_H_CENTER|PIVOT_V_BOTTOM));
RETURN_ON_ERROR(vboot_draw_footer(locale));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_base_screen(struct params *p)
{
return draw_base_screen(p->locale, 1);
}
static VbError_t vboot_draw_base_screen_without_language(struct params *p)
{
return draw_base_screen(p->locale, 0);
}
static VbError_t vboot_draw_blank(struct params *p)
{
clear_screen(&color_black);
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_menu(struct params *p, const struct menu *m)
{
int i = 0;
int yoffset;
uint32_t flags;
/* find starting point y offset */
yoffset = 0 - m->count/2;
for (i = 0; i < m->count; i++) {
if ((p->disabled_idx_mask & (1 << i)) != 0)
continue;
flags = PIVOT_H_CENTER|PIVOT_V_TOP;
if (p->selected_index == i)
flags |= INVERT_COLORS;
RETURN_ON_ERROR(draw_image_locale(m->strings[i], p->locale,
VB_SCALE_HALF, VB_SCALE_HALF + VB_TEXT_HEIGHT * yoffset,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
flags));
if (!strncmp(m->strings[i], "lang.bmp", NAME_LENGTH)) {
int32_t w = VB_SIZE_AUTO, h = VB_TEXT_HEIGHT;
RETURN_ON_ERROR(get_image_size_locale(m->strings[i],
p->locale, &w, &h));
RETURN_ON_ERROR(draw_image("globe.bmp",
VB_SCALE_HALF + w / 2,
VB_SCALE_HALF + VB_TEXT_HEIGHT * yoffset,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_LEFT | PIVOT_V_TOP));
}
yoffset++;
}
RETURN_ON_ERROR(draw_image_locale("navigate.bmp", p->locale,
VB_SCALE_HALF,
VB_SCALE - VB_DIVIDER_V_OFFSET - VB_TEXT_HEIGHT,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
PIVOT_H_CENTER|PIVOT_V_BOTTOM));
return VBERROR_SUCCESS;
}
/*
* String arrays with bmp file names for detachable Menus
*/
static const char *const dev_warning_menu_files[] = {
"dev_option.bmp", /* Developer Options */
"debug_info.bmp", /* Show Debug Info */
"enable_ver.bmp", /* Enable Root Verification */
"power_off.bmp", /* Power Off */
"lang.bmp", /* Language */
};
static const char *const dev_menu_files[] = {
"boot_network.bmp", /* Boot Network Image */
"boot_legacy.bmp", /* Boot Legacy BIOS */
"boot_usb.bmp", /* Boot USB Image */
"boot_dev.bmp", /* Boot Developer Image */
"cancel.bmp", /* Cancel */
"power_off.bmp", /* Power Off */
"lang.bmp", /* Language */
};
static const char *const rec_to_dev_files[] = {
"confirm_dev.bmp", /* Confirm enabling developer mode */
"cancel.bmp", /* Cancel */
"power_off.bmp", /* Power Off */
"lang.bmp", /* Language */
};
static const char *const dev_to_norm_files[] = {
"confirm_ver.bmp", /* Confirm Enabling Verified Boot */
"cancel.bmp", /* Cancel */
"power_off.bmp", /* Power Off */
"lang.bmp", /* Language */
};
static const char *const options_files[] = {
"debug_info.bmp", /* Show Debug Info */
"cancel.bmp", /* Cancel */
"power_off.bmp", /* Power Off */
"lang.bmp", /* Language */
};
static VbError_t vboot_draw_developer_warning(struct params *p)
{
uint32_t locale = p->locale;
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_icon("VerificationOff.bmp"));
RETURN_ON_ERROR(draw_image_locale("verif_off.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_TOP));
RETURN_ON_ERROR(draw_image_locale("devmode.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF + VB_TEXT_HEIGHT * 2,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_TOP));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_developer_warning_menu(struct params *p)
{
if (p->redraw_base)
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_image_locale("enable_hint.bmp", p->locale,
VB_SCALE_HALF, VB_DIVIDER_V_OFFSET + VB_TEXT_HEIGHT,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
PIVOT_H_CENTER|PIVOT_V_TOP));
const struct menu m = { dev_warning_menu_files,
ARRAY_SIZE(dev_warning_menu_files) };
return vboot_draw_menu(p, &m);
}
static VbError_t vboot_draw_developer_menu(struct params *p)
{
if (p->redraw_base)
RETURN_ON_ERROR(vboot_draw_base_screen(p));
const struct menu m = { dev_menu_files, ARRAY_SIZE(dev_menu_files) };
return vboot_draw_menu(p, &m);
}
static VbError_t vboot_draw_recovery_no_good(struct params *p)
{
uint32_t locale = p->locale;
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_image_locale("yuck.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF - VB_DEVICE_HEIGHT / 2,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_BOTTOM));
RETURN_ON_ERROR(draw_image("BadDevices.bmp",
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_ICON_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_CENTER));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_recovery_insert(struct params *p)
{
const int32_t h = VB_DEVICE_HEIGHT;
uint32_t locale = p->locale;
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_image_locale("insert.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF - h/2,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_BOTTOM));
RETURN_ON_ERROR(draw_image("InsertDevices.bmp",
VB_SCALE_HALF, VB_SCALE_HALF, VB_SIZE_AUTO, h,
PIVOT_H_CENTER|PIVOT_V_CENTER));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_recovery_to_dev(struct params *p)
{
uint32_t locale = p->locale;
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_image_locale("todev.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 4,
PIVOT_H_CENTER|PIVOT_V_CENTER));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_recovery_to_dev_menu(struct params *p)
{
if (p->redraw_base)
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_image_locale("disable_warn.bmp", p->locale,
VB_SCALE_HALF, VB_DIVIDER_V_OFFSET + VB_TEXT_HEIGHT,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
PIVOT_H_CENTER|PIVOT_V_TOP));
const struct menu m = { rec_to_dev_files,
ARRAY_SIZE(rec_to_dev_files) };
return vboot_draw_menu(p, &m);
}
static VbError_t vboot_draw_developer_to_norm(struct params *p)
{
uint32_t locale = p->locale;
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_icon("VerificationOff.bmp"));
RETURN_ON_ERROR(draw_image_locale("verif_off.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_TOP));
RETURN_ON_ERROR(draw_image_locale("tonorm.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF + VB_TEXT_HEIGHT * 2,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 4,
PIVOT_H_CENTER|PIVOT_V_TOP));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_developer_to_norm_menu(struct params *p)
{
if (p->redraw_base)
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_image_locale("confirm_hint.bmp", p->locale,
VB_SCALE_HALF, VB_DIVIDER_V_OFFSET + VB_TEXT_HEIGHT,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
PIVOT_H_CENTER|PIVOT_V_TOP));
const struct menu m = { dev_to_norm_files,
ARRAY_SIZE(dev_to_norm_files) };
return vboot_draw_menu(p, &m);
}
static VbError_t vboot_draw_wait(struct params *p)
{
/*
* Currently, language cannot be changed while EC software sync is
* taking place because keyboard is disabled.
*/
RETURN_ON_ERROR(vboot_draw_base_screen_without_language(p));
RETURN_ON_ERROR(draw_image_locale("update.bmp", p->locale,
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
PIVOT_H_CENTER|PIVOT_V_CENTER));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_to_norm_confirmed(struct params *p)
{
uint32_t locale = p->locale;
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_icon("VerificationOn.bmp"));
RETURN_ON_ERROR(draw_image_locale("verif_on.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_TOP));
RETURN_ON_ERROR(draw_image_locale("reboot_erase.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF + VB_TEXT_HEIGHT * 2,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_TOP));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_os_broken(struct params *p)
{
uint32_t locale = p->locale;
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_icon("Warning.bmp"));
RETURN_ON_ERROR(draw_image_locale("os_broken.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
PIVOT_H_CENTER|PIVOT_V_TOP));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_languages_menu(struct params *p)
{
int i = 0;
/*
* There are too many languages to fit onto a page. Let's try to list
* about 15 at a time. Since the explanatory text needs to fit on the
* bottom, center the list two entries higher than the screen center.
*/
const int lang_per_page = 15;
const int yoffset_start = 0 - lang_per_page/2 - 2;
int yoffset = yoffset_start;
int selected_index = p->selected_index % locale_data.count;
locale_data.current = selected_index;
int page_num = selected_index / lang_per_page;
int page_start_index = lang_per_page * page_num;
int total_pages = locale_data.count / lang_per_page;
if (locale_data.count % lang_per_page > 0)
total_pages++;
/*
* redraw screen if we cross a page boundary
* or if we're instructed to do so (because of screen change)
*/
if (prev_lang_page_num != page_num || p->redraw_base)
RETURN_ON_ERROR(vboot_draw_base_screen(p));
/* Print out page #s (1/5, 2/5, etc.) */
char page_count[6];
snprintf(page_count, sizeof(page_count), "%d/%d", page_num + 1,
total_pages);
/* draw_text() cannot pivot center, so must fudge x-coord a little. */
RETURN_ON_ERROR(draw_text(page_count, VB_SCALE_HALF - 20,
VB_DIVIDER_V_OFFSET, VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_BOTTOM));
/*
* Check if we can just redraw some entries (staying on the
* same page) instead of the whole page because opening the
* archives for each language slows things down.
*/
int num_lang_to_draw = lang_per_page;
int start_index = page_start_index;
if (prev_lang_page_num == page_num && !p->redraw_base) {
/* Redraw selected index and previously selected index */
num_lang_to_draw = 2;
start_index = MIN(prev_selected_index, selected_index);
/* previous index invalid. */
if (prev_selected_index == -1) {
start_index = selected_index;
num_lang_to_draw = 1;
}
yoffset = yoffset_start + (start_index - page_start_index);
}
uint32_t flags;
for (i = start_index;
i < start_index + num_lang_to_draw && i < locale_data.count;
i++, yoffset++) {
flags = PIVOT_H_CENTER|PIVOT_V_TOP;
if (selected_index == i)
flags |= INVERT_COLORS;
RETURN_ON_ERROR(draw_image_locale("language.bmp", i,
VB_SCALE_HALF,
VB_SCALE_HALF + VB_TEXT_HEIGHT * yoffset,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
flags));
}
prev_lang_page_num = page_num;
prev_selected_index = selected_index;
RETURN_ON_ERROR(draw_image_locale("navigate.bmp", p->locale,
VB_SCALE_HALF,
VB_SCALE - VB_DIVIDER_V_OFFSET - VB_TEXT_HEIGHT,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
PIVOT_H_CENTER|PIVOT_V_BOTTOM));
return VBERROR_SUCCESS;
}
VbError_t vboot_print_string(char *str)
{
// Left align with divider, begin right under it.
int left = (VB_SCALE - VB_DIVIDER_WIDTH) / 2;
int top = VB_DIVIDER_V_OFFSET + 10;
char *line = str;
int lines = 0;
while ((line = strchr(line, '\n'))) {
lines++;
line++; // skip over '\n' to keep counting
}
int max_height = VB_TEXT_HEIGHT;
if (lines * max_height > VB_SCALE - top * 2)
max_height = (VB_SCALE - top * 2) / lines;
struct rect box = {
{ .x = VB_TO_CANVAS(left), .y = VB_TO_CANVAS(top) },
{ .width = VB_TO_CANVAS(VB_DIVIDER_WIDTH),
.height = VB_TO_CANVAS(lines * max_height) },
};
draw_box(&box, &color_white);
while ((line = strsep(&str, "\n"))) {
int height = max_height;
int width;
RETURN_ON_ERROR(get_text_width(height, line, &width));
if (width > VB_DIVIDER_WIDTH)
height = height * VB_DIVIDER_WIDTH / width;
RETURN_ON_ERROR(draw_text(line, left, top, height,
PIVOT_H_LEFT|PIVOT_V_TOP));
top += height;
}
return VBERROR_SUCCESS;
}
static VbError_t draw_altfw_text(int menutop, int linenum, int seqnum,
const char *name, const char *desc)
{
int top = menutop + VB_TEXT_HEIGHT * linenum;
if (seqnum != -1) {
char seq[2] = {'0' + seqnum, '\0'};
RETURN_ON_ERROR(draw_text(seq,
VB_ALTFW_SEQ_LEFT,
top,
VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP));
}
RETURN_ON_ERROR(draw_text(name,
VB_ALTFW_NAME_LEFT,
top,
VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP));
RETURN_ON_ERROR(draw_text(desc,
VB_ALTFW_DESC_LEFT,
top,
VB_TEXT_HEIGHT,
PIVOT_H_LEFT|PIVOT_V_TOP));
return VBERROR_SUCCESS;
}
static VbError_t vboot_draw_altfw_pick(struct params *p)
{
ListNode *head;
head = payload_get_altfw_list();
RETURN_ON_ERROR(vboot_draw_base_screen(p));
if (head) {
struct altfw_info *node;
int pos = 2;
int node_cnt = 0;
int top;
list_for_each(node, *head, list_node) {
if (!node->seqnum)
continue;
node_cnt++;
}
top = VB_SCALE_HALF - (node_cnt + 2) * VB_TEXT_HEIGHT / 2;
RETURN_ON_ERROR(draw_image_locale("select_altfw.bmp", p->locale,
VB_SCALE_HALF, top,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_TOP));
list_for_each(node, *head, list_node) {
if (!node->seqnum)
continue;
RETURN_ON_ERROR(draw_altfw_text(top,
pos,
node->seqnum,
node->name,
node->desc));
pos++;
}
}
return VBERROR_SUCCESS;
}
#if CONFIG(DIAGNOSTIC_UI)
static VbError_t vboot_draw_confirm_diag(struct params *p)
{
uint32_t locale = p->locale;
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_image_locale("diag_confirm.bmp", locale,
VB_SCALE_HALF, VB_SCALE_HALF,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 3,
PIVOT_H_CENTER|PIVOT_V_CENTER));
return VBERROR_SUCCESS;
}
#endif
static VbError_t vboot_draw_options_menu(struct params *p)
{
if (p->redraw_base)
RETURN_ON_ERROR(vboot_draw_base_screen(p));
const struct menu m = { options_files,
ARRAY_SIZE(options_files) };
return vboot_draw_menu(p, &m);
}
static VbError_t vboot_draw_altfw_menu(struct params *p)
{
struct altfw_info *node;
char str[256];
ListNode *head;
int top = VB_SCALE_HALF - VB_TEXT_HEIGHT / 2;
uint32_t flags;
if (p->redraw_base)
RETURN_ON_ERROR(vboot_draw_base_screen(p));
head = payload_get_altfw_list();
int i = 0;
if (head) {
int node_cnt = 0;
list_for_each(node, *head, list_node) {
if (!node->seqnum)
continue;
if ((p->disabled_idx_mask &
(1 << (node->seqnum - 1))) != 0)
continue;
node_cnt++;
}
top = VB_SCALE_HALF - (node_cnt + 1) * VB_TEXT_HEIGHT / 2;
list_for_each(node, *head, list_node) {
if (!node->seqnum)
continue;
if ((p->disabled_idx_mask &
(1 << (node->seqnum - 1))) != 0)
continue;
flags = PIVOT_H_CENTER|PIVOT_V_TOP;
if (p->selected_index == node->seqnum - 1)
flags |= INVERT_COLORS;
snprintf(str, sizeof(str), " %s - %s ",
node->name, node->desc);
RETURN_ON_ERROR(draw_text(str,
VB_SCALE_HALF,
top + VB_TEXT_HEIGHT * i,
VB_TEXT_HEIGHT,
flags));
i++;
}
}
flags = PIVOT_H_CENTER|PIVOT_V_TOP;
if (p->selected_index == 9)
flags |= INVERT_COLORS;
RETURN_ON_ERROR(draw_image_locale("cancel.bmp", p->locale,
VB_SCALE_HALF, top + VB_TEXT_HEIGHT * i,
VB_SIZE_AUTO, VB_TEXT_HEIGHT,
flags));
RETURN_ON_ERROR(draw_image_locale("navigate.bmp", p->locale,
VB_SCALE_HALF,
VB_SCALE - VB_DIVIDER_V_OFFSET - VB_TEXT_HEIGHT,
VB_SIZE_AUTO, VB_TEXT_HEIGHT * 2,
PIVOT_H_CENTER|PIVOT_V_BOTTOM));
return 0;
}
#if CONFIG_VENDOR_DATA_LENGTH > 0
static VbError_t vboot_draw_vendor_data_prompt(struct params *p,
const char *string) {
if (p->redraw_base)
RETURN_ON_ERROR(vboot_draw_base_screen(p));
RETURN_ON_ERROR(draw_image_locale(string, p->locale,
VB_SCALE_HALF,
VB_SCALE_HALF - VB_TEXT_HEIGHT / 2,
VB_SIZE_AUTO,
VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_CENTER));
RETURN_ON_ERROR(draw_text(p->data->vendor_data.input_text,
VB_SCALE_HALF,
VB_SCALE_HALF + VB_TEXT_HEIGHT / 2,
VB_TEXT_HEIGHT,
PIVOT_H_CENTER|PIVOT_V_CENTER));
return 0;
}
static VbError_t vboot_draw_set_vendor_data(struct params *p)
{
return vboot_draw_vendor_data_prompt(p, "set_vendor_data.bmp");
}
static VbError_t vboot_draw_confirm_vendor_data(struct params *p)
{
return vboot_draw_vendor_data_prompt(p, "conf_vendor_data.bmp");
}
#endif // CONFIG_VENDOR_DATA_LENGTH > 0
/* we may export this in the future for the board customization */
struct vboot_ui_descriptor {
uint32_t id; /* VB_SCREEN_* */
VbError_t (*draw)(struct params *p); /* draw function */
const char *mesg; /* fallback message */
};
static const struct vboot_ui_descriptor vboot_screens[] = {
{
.id = VB_SCREEN_BLANK,
.draw = vboot_draw_blank,
.mesg = NULL,
},
{
.id = VB_SCREEN_DEVELOPER_WARNING,
.draw = vboot_draw_developer_warning,
.mesg = "OS verification is OFF\n"
"Press SPACE to re-enable.\n",
},
{
.id = VB_SCREEN_RECOVERY_NO_GOOD,
.draw = vboot_draw_recovery_no_good,
.mesg = "The device you inserted does not contain Chrome OS.\n",
},
{
.id = VB_SCREEN_RECOVERY_INSERT,
.draw = vboot_draw_recovery_insert,
.mesg = "Chrome OS is missing or damaged.\n"
"Please insert a recovery USB stick or SD card.\n",
},
{
.id = VB_SCREEN_RECOVERY_TO_DEV,
.draw = vboot_draw_recovery_to_dev,
.mesg = "To turn OS verificaion OFF, press ENTER.\n"
"Your system will reboot and local data will be cleared.\n"
"To go back, press ESC.\n",
},
{
.id = VB_SCREEN_DEVELOPER_TO_NORM,
.draw = vboot_draw_developer_to_norm,
.mesg = "OS verification is OFF\n"
"Press ENTER to confirm you wish to turn OS verification on.\n"
"Your system will reboot and local data will be cleared.\n"
"To go back, press ESC.\n",
},
{
.id = VB_SCREEN_WAIT,
.draw = vboot_draw_wait,
.mesg = "Your system is applying a critical update.\n"
"Please do not turn off.\n",
},
{
.id = VB_SCREEN_TO_NORM_CONFIRMED,
.draw = vboot_draw_to_norm_confirmed,
.mesg = "OS verification is ON\n"
"Your system will reboot and local data will be cleared.\n",
},
{
.id = VB_SCREEN_OS_BROKEN,
.draw = vboot_draw_os_broken,
.mesg = "Chrome OS may be broken.\n"
"Remove media and initiate recovery.\n",
},
{
.id = VB_SCREEN_DEVELOPER_WARNING_MENU,
.draw = vboot_draw_developer_warning_menu,
.mesg = "Developer Warning Menu\n",
},
{
.id = VB_SCREEN_DEVELOPER_MENU,
.draw = vboot_draw_developer_menu,
.mesg = "Developer Menu\n",
},
{
.id = VB_SCREEN_RECOVERY_TO_DEV_MENU,
.draw = vboot_draw_recovery_to_dev_menu,
.mesg = "Recovery to Dev Menu\n",
},
{
.id = VB_SCREEN_DEVELOPER_TO_NORM_MENU,
.draw = vboot_draw_developer_to_norm_menu,
.mesg = "Developer to Norm Menu",
},
{
.id = VB_SCREEN_LANGUAGES_MENU,
.draw = vboot_draw_languages_menu,
.mesg = "Languages Menu",
},
{
.id = VB_SCREEN_OPTIONS_MENU,
.draw = vboot_draw_options_menu,
.mesg = "Options Menu",
},
{
.id = VB_SCREEN_ALT_FW_PICK,
.draw = vboot_draw_altfw_pick,
.mesg = "Alternative Firmware Menu",
},
{
.id = VB_SCREEN_ALT_FW_MENU,
.draw = vboot_draw_altfw_menu,
.mesg = "Alternative Firmware Menu",
},
#if CONFIG_VENDOR_DATA_LENGTH > 0
{
.id = VB_SCREEN_SET_VENDOR_DATA,
.draw = vboot_draw_set_vendor_data,
.mesg = "Set Vendor Data",
},
{
.id = VB_SCREEN_CONFIRM_VENDOR_DATA,
.draw = vboot_draw_confirm_vendor_data,
.mesg = "Confirm Vendor Data",
},
#endif
#if CONFIG(DIAGNOSTIC_UI)
{
.id = VB_SCREEN_CONFIRM_DIAG,
.draw = vboot_draw_confirm_diag,
.mesg = "To run diagnostics tap the power button.\n"
"To go back, press esc.\n",
},
#endif
};
static const struct vboot_ui_descriptor *get_ui_descriptor(uint32_t id)
{
int i;
for (i = 0; i < ARRAY_SIZE(vboot_screens); i++) {
if (vboot_screens[i].id == id)
return &vboot_screens[i];
}
return NULL;
}
static void print_fallback_message(const struct vboot_ui_descriptor *desc)
{
char msg[256];
if (!desc->mesg)
return;
strncpy(msg, desc->mesg, sizeof(msg) - 1);
msg[sizeof(msg) - 1] = '\0';
vboot_print_string(msg);
}
static VbError_t draw_ui(uint32_t screen_type, struct params *p)
{
VbError_t rv = VBERROR_UNKNOWN;
const struct vboot_ui_descriptor *desc;
desc = get_ui_descriptor(screen_type);
if (!desc) {
printf("Not a valid screen type: 0x%x\n", screen_type);
return VBERROR_INVALID_SCREEN_INDEX;
}
if (p->locale >= locale_data.count) {
printf("Unsupported locale (%d)\n", p->locale);
print_fallback_message(desc);
return VBERROR_INVALID_PARAMETER;
}
/* if no drawing function is registered, fallback msg will be printed */
if (desc->draw) {
rv = desc->draw(p);
if (rv)
printf("Drawing failed (0x%x)\n", rv);
}
if (rv) {
print_fallback_message(desc);
return VBERROR_SCREEN_DRAW;
}
return VBERROR_SUCCESS;
}
static void vboot_init_locale(void)
{
char *locales, *loc_start, *loc;
size_t size;
locale_data.count = 0;
/* Load locale list from cbfs */
locales = cbfs_get_file_content(ro_cbfs, "locales",
CBFS_TYPE_RAW, &size);
if (!locales || !size) {
printf("%s: locale list not found\n", __func__);
return;
}
/* Copy the file and null-terminate it */
loc_start = malloc(size + 1);
if (!loc_start) {
printf("%s: out of memory\n", __func__);
free(locales);
return;
}
memcpy(loc_start, locales, size);
loc_start[size] = '\0';
/* Parse the list */
printf("%s: Supported locales:", __func__);
loc = loc_start;
while (loc - loc_start < size
&& locale_data.count < ARRAY_SIZE(locale_data.codes)) {
char *lang = strsep(&loc, "\n");
if (!lang || !strlen(lang))
break;
printf(" %s,", lang);
locale_data.codes[locale_data.count] = lang;
locale_data.count++;
}
free(locales);
printf(" (%d locales)\n", locale_data.count);
}
static VbError_t vboot_init_screen(void)
{
if (ro_cbfs == NULL) {
ro_cbfs = cbfs_ro_media();
if (ro_cbfs == NULL) {
printf("No RO CBFS found.\n");
return VBERROR_UNKNOWN;
}
}
/* Make sure framebuffer is initialized before turning display on. */
clear_screen(&color_white);
if (display_init())
return VBERROR_UNKNOWN;
/* create a list of supported locales */
vboot_init_locale();
/* load generic (location-free) graphics data. ignore errors.
* fallback screens will be drawn for missing data */
load_archive("vbgfx.bin", &base_graphics);
/* load font graphics */
load_archive("font.bin", &font_graphics);
/* reset localized graphics. we defer loading it. */
locale_data.archive = NULL;
initialized = 1;
return VBERROR_SUCCESS;
}
int vboot_draw_screen(uint32_t screen, uint32_t locale,
const VbScreenData *data)
{
printf("%s: screen=0x%x locale=%d\n", __func__, screen, locale);
if (!initialized) {
if (vboot_init_screen())
return VBERROR_UNKNOWN;
}
/* If the screen is blank, turn off the backlight; else turn it on. */
backlight_update(VB_SCREEN_BLANK == screen ? 0 : 1);
/* TODO: draw only locale dependent part if current_screen == screen */
/* setting selected_index value to 0xFFFFFFFF invalidates the field */
struct params p = { locale, 0xFFFFFFFF, 0, 1, data };
RETURN_ON_ERROR(draw_ui(screen, &p));
locale_data.current = locale;
return VBERROR_SUCCESS;
}
int vboot_draw_ui(uint32_t screen, uint32_t locale,
uint32_t selected_index, uint32_t disabled_idx_mask,
uint32_t redraw_base)
{
printf("%s: screen=0x%x locale=%d, selected_index=%d,"
"disabled_idx_mask=0x%x\n",
__func__, screen, locale, selected_index, disabled_idx_mask);
if (!initialized) {
if (vboot_init_screen())
return VBERROR_UNKNOWN;
}
/* If the screen is blank, turn off the backlight; else turn it on. */
backlight_update(screen == VB_SCREEN_BLANK ? 0 : 1);
struct params p = { locale, selected_index,
disabled_idx_mask, redraw_base, NULL };
return draw_ui(screen, &p);
}
int vboot_get_locale_count(void)
{
if (!initialized) {
if (vboot_init_screen())
return VBERROR_UNKNOWN;
}
return locale_data.count;
}