/* * This file is part of libbdplus * Copyright (C) 2013 VideoLAN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #if HAVE_CONFIG_H #include "config.h" #endif #include "bdplus_config.h" #include "file/configfile.h" #include "util/logging.h" #include "util/strutl.h" #include "util/macro.h" #include #include #define MEMORY_MAP_FILE "memory.map" #define AES_KEYS_FILE "aes_keys.bin" #define CERTIFICATES_FILE "certificates.bin" #define ECDSA_KEYS_FILE "ecdsa_keys.txt" #define DEV_DISCOVERY_FILE "device_discovery_%d.bin" static int _load_aes_keys(bdplus_aes_key_t *aes_keys, const char *base) { char *path = str_printf("%s/" AES_KEYS_FILE, base); uint8_t *keys; uint32_t size = 0; uint32_t num_keys, ii; if (!path) { return -1; } keys = (uint8_t *)file_load(path, &size); X_FREE(path); num_keys = size / 16; if (num_keys > MAX_AES_KEYS) { num_keys = MAX_AES_KEYS; } if (num_keys * 16 != size) { BD_DEBUG(DBG_FILE | DBG_CRIT, "Invalid AES key file size\n"); } for (ii = 0; ii < num_keys; ii++) { memcpy(aes_keys[ii].key, keys + 16*ii, 16); } X_FREE(keys); return num_keys > 6 ? (int)num_keys : -1; } static int _load_ecdsa_keys(bdplus_ecdsa_key_t *ecdsa_keys, const char *base) { const char *p; char *path = str_printf("%s/" ECDSA_KEYS_FILE, base); char *cfg; int num_ecdsa_keys = 0; if (!path) { return -1; } cfg = file_load(path, NULL); X_FREE(path); /* parse keys */ p = cfg; while (*p) { char key_d[41], key_Qx[41], key_Qy[41]; p = str_skip_white(p); if (*p == '#') { /* comment */ } else if (3 == sscanf(p, "%40s %40s %40s", key_d, key_Qx, key_Qy)) { if (num_ecdsa_keys >= MAX_ECDSA_KEYS) { BD_DEBUG(DBG_FILE | DBG_CRIT, "Too many ECDSA keys\n"); break; } memcpy(ecdsa_keys[num_ecdsa_keys].d, key_d, 40); memcpy(ecdsa_keys[num_ecdsa_keys].Qx, key_Qx, 40); memcpy(ecdsa_keys[num_ecdsa_keys].Qy, key_Qy, 40); num_ecdsa_keys++; } else { BD_DEBUG(DBG_FILE | DBG_CRIT, "invalid line in config file: %4.4s...\n", p); } p = str_next_line(p); } X_FREE(cfg); return num_ecdsa_keys > 0 ? 0 : -1; } static int _load_ram(bdplus_ram_t **p, const char *base, uint32_t address, const char *file) { bdplus_ram_t *ram; if (!*p) { *p = calloc(1, sizeof(bdplus_ram_t)); if (!*p) { return 0; } } ram = *p; void *tmp = ram->area; ram->area = realloc(ram->area, (ram->num_area + 1) * sizeof(*ram->area)); if (!ram->area) { X_FREE(tmp); BD_DEBUG(DBG_CRIT, "out of memory\n"); return 0; } memset(&ram->area[ram->num_area], 0, sizeof(ram->area[ram->num_area])); ram->area[ram->num_area].start_address = address; if (!strcmp(file, "PSR")) { ram->area[ram->num_area].type = MEM_TYPE_PSR; BD_DEBUG(DBG_BDPLUS, "mapped PSR register file to 0x%x\n", address); } else if (!strcmp(file, "GPR")) { ram->area[ram->num_area].type = MEM_TYPE_PSR; BD_DEBUG(DBG_BDPLUS, "mapped GPR register file to 0x%x\n", address); } else { /* load from file */ char *path = str_printf("%s/%s", base, file); if (!path) { return 0; } ram->area[ram->num_area].memory = file_load(path, &ram->area[ram->num_area].size); ram->area[ram->num_area].mem = ram->area[ram->num_area].memory; X_FREE(path); if (!ram->area[ram->num_area].mem) { return 0; } BD_DEBUG(DBG_BDPLUS, "mapped %d bytes from %s to 0x%x\n", ram->area[ram->num_area].size, file, address); } ram->num_area++; return 1; } static void _ram_free(bdplus_ram_t **pp) { if (pp && *pp) { unsigned int ii; for (ii = 0; ii < (*pp)->num_area; ii++) { X_FREE((*pp)->area[ii].memory); } X_FREE((*pp)->area); X_FREE(*pp); } } static int _load_dev_discovery(bdplus_dev_t *dev, const char *base) { unsigned ii; for (ii = 0; ii < MAX_DEV_DISCOVERY; ii++) { char *path = str_printf("%s/" DEV_DISCOVERY_FILE, base, ii + 1); if (!path) { break; } dev[ii].mem = (uint8_t *)file_load(path, &dev[ii].size); X_FREE(path); if (!dev[ii].mem) { break; } } return ii >= 5 ? 0 : -1; } static void _dev_free(bdplus_dev_t **pp) { if (pp && *pp) { unsigned ii; for (ii = 0; ii < MAX_DEV_DISCOVERY; ii++) { X_FREE((*pp)[ii].mem); } X_FREE(*pp); } } static int _load_memory(bdplus_ram_t **ram, const char *base) { const char *p; char *path; char *cfg = NULL; path = str_printf("%s/" MEMORY_MAP_FILE, base); if (path) { cfg = file_load(path, NULL); X_FREE(path); } if (!cfg) { BD_DEBUG(DBG_FILE | DBG_CRIT, "Error loading memory map file '"MEMORY_MAP_FILE"'\n"); return -1; } /* parse memory map file */ p = cfg; while (*p) { uint32_t address; char name[64]; p = str_skip_white(p); if (*p == '#') { /* comment */ } else if (2 == sscanf(p, "%x %63s", &address, name)) { name[sizeof(name) - 1] = 0; _load_ram(ram, base, address, name); } else { BD_DEBUG(DBG_FILE | DBG_CRIT, "invalid line in config file: %4.4s...\n", p); } p = str_next_line(p); } X_FREE(cfg); return 0; } void bdplus_config_free(bdplus_config_t **p_config) { if (*p_config) { _ram_free(&(*p_config)->ram); _dev_free(&(*p_config)->dev); X_FREE((*p_config)->aes_keys); X_FREE((*p_config)->ecdsa_keys); X_FREE(*p_config); } } int bdplus_config_load(const char *config_path, bdplus_config_t **p_config) { bdplus_config_free(p_config); bdplus_config_t *config = *p_config = calloc(1, sizeof(bdplus_config_t)); if (!config) { BD_DEBUG(DBG_FILE | DBG_CRIT, "out of memory\n"); return -1; } char *base = NULL; if (!config_path) { base = file_get_config_dir(MEMORY_MAP_FILE); config_path = base; if (!base) { BD_DEBUG(DBG_FILE | DBG_CRIT, "VM configuration not found\n"); return -1; } } config->aes_keys = calloc(MAX_AES_KEYS, sizeof(bdplus_aes_key_t)); config->ecdsa_keys = calloc(MAX_ECDSA_KEYS, sizeof(bdplus_ecdsa_key_t)); config->dev = calloc(MAX_DEV_DISCOVERY, sizeof(bdplus_dev_t)); if (!config->aes_keys || !config->ecdsa_keys || !config->dev) { BD_DEBUG(DBG_FILE | DBG_CRIT, "out of memory\n"); return -1; } config->num_aes_keys = _load_aes_keys(config->aes_keys, config_path); if (config->num_aes_keys < 0) { BD_DEBUG(DBG_FILE | DBG_CRIT, "Player AES keys not found\n"); } if (_load_ecdsa_keys(config->ecdsa_keys, config_path) < 0) { BD_DEBUG(DBG_FILE | DBG_CRIT, "Player ECDSA keys not found\n"); } if (_load_dev_discovery(config->dev, config_path) < 0) { BD_DEBUG(DBG_FILE | DBG_CRIT, "Player device discovery signatures not found\n"); } if (_load_memory(&config->ram, config_path) < 0) { BD_DEBUG(DBG_FILE | DBG_CRIT, "Player memory loading failed\n"); } X_FREE(base); return 0; } static bdplus_ram_area_t *_find_mem(bdplus_ram_t *p, uint32_t mask) { if (p) { unsigned int ii; for (ii = 0; ii < p->num_area; ii++) { if (p->area[ii].type & mask) { return &p->area[ii]; } } } return NULL; } void bdplus_config_mmap(bdplus_ram_t *ram, uint32_t type, void *mem, uint32_t size) { bdplus_ram_area_t *p; if (!mem) { BD_DEBUG(DBG_BDPLUS | DBG_CRIT, "[bdplus] mmap: config not read\n"); return; } if (((intptr_t)mem) & 3) { BD_DEBUG(DBG_BDPLUS | DBG_CRIT, "[bdplus] mmap: register file %d not aligned\n", type); return; } p = _find_mem(ram, type); if (!p) { BD_DEBUG(DBG_BDPLUS | DBG_CRIT, "[bdplus] mmap: register file %d not mapped in config\n", type); return; } p->type = type; p->mem = mem; p->size = size; }