| /* |
| * Copyright 2016 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include <assert.h> |
| #include <stdio.h> |
| |
| #include "base/algorithm.h" |
| #include "base/cbfs/cbfs.h" |
| #include "base/die.h" |
| #include "base/xalloc.h" |
| #include "drivers/board/board_helpers.h" |
| #include "drivers/layout/coreboot.h" |
| #include "drivers/storage/cbfs.h" |
| #include "drivers/storage/fmap.h" |
| #include "drivers/storage/section_index.h" |
| #include "net/netboot/params.h" |
| #include "vboot/board.h" |
| |
| PRIV_DYN(fmap_media, new_fmap_storage_media(board__coreboot_storage(), |
| CONFIG_FMAP_OFFSET)) |
| |
| PRIV_DYN(fw_main_a, &new_fmap_storage(get_fmap_media(), "FW_MAIN_A")->ops) |
| PRIV_DYN(main_a_index, new_section_index_storage(get_fw_main_a())) |
| PRIV_DYN(fw_main_b, &new_fmap_storage(get_fmap_media(), "FW_MAIN_B")->ops) |
| PRIV_DYN(main_b_index, new_section_index_storage(get_fw_main_b())) |
| |
| PUB_DYN(storage_gbb, &new_fmap_storage(get_fmap_media(), "GBB")->ops) |
| PUB_DYN(storage_fwid_ro, &new_fmap_storage(get_fmap_media(), "RO_FRID")->ops) |
| PUB_DYN(storage_fwid_rwa, &new_fmap_storage(get_fmap_media(), "RW_FWID_A")->ops) |
| PUB_DYN(storage_fwid_rwb, &new_fmap_storage(get_fmap_media(), "RW_FWID_B")->ops) |
| PUB_DYN(storage_legacy, &new_fmap_storage(get_fmap_media(), "RW_LEGACY")->ops) |
| PUB_DYN(storage_main_fw_a, |
| &new_section_index_entry_storage(get_main_a_index(), 0)->ops) |
| PUB_DYN(storage_main_fw_b, |
| &new_section_index_entry_storage(get_main_b_index(), 0)->ops) |
| PUB_DYN(storage_nv_scratch, &new_fmap_storage(get_fmap_media(), |
| "SHARED_DATA")->ops) |
| PUB_DYN(storage_vblock_a, &new_fmap_storage(get_fmap_media(), "VBLOCK_A")->ops) |
| PUB_DYN(storage_vblock_b, &new_fmap_storage(get_fmap_media(), "VBLOCK_B")->ops) |
| PUB_DYN(storage_vboot_nvstorage, &new_fmap_storage(get_fmap_media(), |
| "RW_NVRAM")->ops) |
| PUB_DYN(storage_verified_a, &get_main_a_index()->ops) |
| PUB_DYN(storage_verified_b, &get_main_b_index()->ops) |
| |
| StorageOps *netboot_params_storage(void) |
| { |
| return board_storage_nv_scratch(); |
| } |
| |
| |
| |
| typedef StorageOps *EcStorageCache[CONFIG_MAX_EC_DEV_IDX + 1]; |
| |
| |
| |
| static StorageOps *board_storage_ec_hash(EcStorageCache *cache, |
| SectionIndexStorage *index, int devidx) |
| { |
| die_if(devidx > ARRAY_SIZE(*cache), |
| "EC devidx %d is out of bounds.\n", devidx); |
| |
| if (!(*cache)[devidx]) { |
| (*cache)[devidx] = &new_section_index_entry_storage( |
| index, devidx + 1)->ops; |
| } |
| |
| return (*cache)[devidx]; |
| } |
| |
| StorageOps *board_storage_ec_hash_a(int devidx) |
| { |
| static EcStorageCache ec_hashes; |
| return board_storage_ec_hash(&ec_hashes, get_main_a_index(), devidx); |
| } |
| |
| StorageOps *board_storage_ec_hash_b(int devidx) |
| { |
| static EcStorageCache ec_hashes; |
| return board_storage_ec_hash(&ec_hashes, get_main_b_index(), devidx); |
| } |
| |
| |
| |
| static StorageOps *board_storage_ec(EcStorageCache *cache, |
| const char *name, int devidx) |
| { |
| die_if(devidx > ARRAY_SIZE(*cache), |
| "EC devidx %d is out of bounds.\n", devidx); |
| |
| if (!(*cache)[devidx]) { |
| FmapStorage *fmap_area = |
| new_fmap_storage(get_fmap_media(), name); |
| SectionIndexStorage *index = |
| new_section_index_storage(&fmap_area->ops); |
| SectionIndexEntryStorage *entry = |
| new_section_index_entry_storage(index, 0); |
| |
| (*cache)[devidx] = &entry->ops; |
| } |
| |
| return (*cache)[devidx]; |
| } |
| |
| StorageOps *board_storage_ec_a(int devidx) |
| { |
| static EcStorageCache ecs; |
| |
| const char *name = NULL; |
| if (devidx == 0) |
| name = "EC_MAIN_A"; |
| else if (devidx == 1) |
| name = "PD_MAIN_A"; |
| |
| die_if(!name, "Unrecognized devidx %d.\n", devidx); |
| |
| return board_storage_ec(&ecs, name, devidx); |
| } |
| |
| StorageOps *board_storage_ec_b(int devidx) |
| { |
| static EcStorageCache ecs; |
| |
| const char *name = NULL; |
| if (devidx == 0) |
| name = "EC_MAIN_B"; |
| else if (devidx == 1) |
| name = "PD_MAIN_B"; |
| |
| die_if(!name, "Unrecognized devidx %d.\n", devidx); |
| |
| return board_storage_ec(&ecs, name, devidx); |
| } |
| |
| |
| |
| static const char *image_name[IMAGE_MAX_INDEX] = { |
| [IMAGE_ARROW_LEFT] = "arrow_left.bmp", |
| [IMAGE_ARROW_RIGHT] = "arrow_right.bmp", |
| [IMAGE_BAD_SD] = "BadSD.bmp", |
| [IMAGE_BAD_USB] = "BadUSB.bmp", |
| [IMAGE_CHROME_LOGO] = "chrome_logo.bmp", |
| [IMAGE_DEV_MODE] = "devmode.bmp", |
| [IMAGE_DIVIDER_TOP] = "divider_top.bmp", |
| [IMAGE_DIVIDER_BOTTOM] = "divider_btm.bmp", |
| [IMAGE_FOR_HELP_LEFT] = "for_help_left.bmp", |
| [IMAGE_ICON_VERIFICATION_OFF] = "VerificationOff.bmp", |
| [IMAGE_ICON_VERIFICATION_ON] = "VerificationOn.bmp", |
| [IMAGE_ICON_WARNING] = "Warning.bmp", |
| [IMAGE_INSERT] = "insert.bmp", |
| [IMAGE_LANGUAGE] = "language.bmp", |
| [IMAGE_MODEL] = "model.bmp", |
| [IMAGE_OS_BROKEN] = "os_broken.bmp", |
| [IMAGE_REBOOT_ERASE] = "reboot_erase.bmp", |
| [IMAGE_REMOVE] = "remove.bmp", |
| [IMAGE_REMOVE_DEVICES] = "RemoveDevices.bmp", |
| [IMAGE_TO_DEV] = "todev.bmp", |
| [IMAGE_TO_NORM] = "tonorm.bmp", |
| [IMAGE_UPDATE] = "update.bmp", |
| [IMAGE_URL] = "Url.bmp", |
| [IMAGE_VERIFICIATION_OFF] = "verif_off.bmp", |
| [IMAGE_VERIFICATION_ON] = "verif_on.bmp", |
| [IMAGE_YUCK] = "yuck.bmp", |
| }; |
| |
| typedef CbfsStorage *ImageStorageCache[IMAGE_MAX_INDEX]; |
| |
| static int get_locales(uint32_t *locale_count, char ***supported_locales) |
| { |
| static uint32_t count; |
| static char *supported[256]; |
| |
| if (!count) { |
| // Load locale list from cbfs. |
| size_t size; |
| char *locales = cbfs_get_file_content( |
| CBFS_DEFAULT_MEDIA, "locales", CBFS_TYPE_RAW, &size); |
| if (!locales || !size) { |
| printf("%s: Locale list not found.", __func__); |
| return 1; |
| } |
| |
| // Copy the file and null-terminate it. |
| char *loc = xmalloc(size + 1); |
| memcpy(loc, locales, size); |
| loc[size] = '\0'; |
| free(locales); |
| locales = loc; |
| |
| // Parse the list. |
| printf("%s: Supported locales:", __func__); |
| while (loc - locales < size && count < ARRAY_SIZE(supported)) { |
| char *lang = strsep(&loc, "\n"); |
| if (!lang || !strlen(lang)) |
| break; |
| printf(" %s,", lang); |
| supported[count++] = lang; |
| } |
| printf(" (%d locales)\n", count); |
| } |
| |
| *locale_count = count; |
| *supported_locales = supported; |
| |
| return 0; |
| } |
| |
| int board_storage_locale_count(uint32_t *count) |
| { |
| char **locales; |
| return get_locales(count, &locales); |
| } |
| |
| static int check_image_index(BoardImageIdx idx) |
| { |
| if (idx >= IMAGE_MAX_INDEX) { |
| printf("Image index %d out of range.\n", idx); |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| StorageOps *board_storage_image(BoardImageIdx idx) |
| { |
| static ImageStorageCache cache; |
| |
| assert(!check_image_index(idx)); |
| |
| if (!cache[idx]) { |
| const char *cbfs_name = image_name[idx]; |
| cache[idx] = new_cbfs_storage( |
| CBFS_DEFAULT_MEDIA, cbfs_name, CBFS_TYPE_RAW); |
| } |
| return &cache[idx]->ops; |
| } |
| |
| StorageOps *board_storage_image_locale(uint32_t locale, BoardImageIdx idx) |
| { |
| static ImageStorageCache **caches; |
| |
| uint32_t count; |
| char **locales; |
| assert(!get_locales(&count, &locales)); |
| assert(!check_image_index(idx)); |
| |
| die_if(locale >= count, "Locale %d out of bounds.\n", locale); |
| |
| if (!caches) { |
| // Construct the cache of image caches, indexed by locale. |
| caches = xzalloc(sizeof(caches[0]) * count); |
| } |
| |
| if (!caches[locale]) { |
| // If this locale's cache hasn't been allocated, do so now. |
| caches[locale] = xzalloc(sizeof(*caches[locale])); |
| } |
| ImageStorageCache *cache = caches[locale]; |
| |
| if (!(*cache)[idx]) { |
| // Set up this image's storage ops. |
| char *cbfs_name = xmalloc(256); |
| snprintf(cbfs_name, 256, "locale/%s/%s", |
| locales[locale], image_name[idx]); |
| (*cache)[idx] = new_cbfs_storage( |
| CBFS_DEFAULT_MEDIA, cbfs_name, CBFS_TYPE_RAW); |
| } |
| CbfsStorage *storage = (*cache)[idx]; |
| |
| return &storage->ops; |
| } |
| |
| typedef CbfsStorage *GlyphStorageCache[sizeof(char) * (1 << 8)]; |
| |
| StorageOps *board_storage_glyph(char c) |
| { |
| static GlyphStorageCache cache; |
| |
| int ch = c; |
| if (!cache[ch]) { |
| char *cbfs_name = xmalloc(256); |
| snprintf(cbfs_name, 256, "font/idx%03d_%02x.bmp", c, c); |
| cache[ch] = new_cbfs_storage( |
| CBFS_DEFAULT_MEDIA, cbfs_name, CBFS_TYPE_RAW); |
| } |
| |
| return &cache[ch]->ops; |
| } |