/* * Copyright (c) International Business Machines Corp., 2006 * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Oliver Lohmann * * Read in PFI (partial flash image) data and store it into internal * data structures for further processing. Take also care about * special handling if the data contains PDD (platform description * data/boot-parameters). */ #include #include #include #include #include #include "bootenv.h" #include "reader.h" #define __unused __attribute__((unused)) /* @FIXME hard coded offsets right now - get them from Artem? */ #define NAND2048_DEFAULT_VID_HDR_OFF 1984 #define NAND512_DEFAULT_VID_HDR_OFF 448 #define NOR_DEFAULT_VID_HDR_OFF 64 #define EBUF_PFI(fmt...) \ do { int i = snprintf(err_buf, err_buf_size, "%s\n", label); \ snprintf(err_buf + i, err_buf_size - i, fmt); \ } while (0) #define EBUF(fmt...) \ do { snprintf(err_buf, err_buf_size, fmt); } while (0) int read_pdd_data(FILE* fp_pdd, pdd_data_t* pdd_data, char* err_buf, size_t err_buf_size) { int rc = 0; bootenv_t pdd = NULL; pdd_data_t res = NULL; const char* value; res = (pdd_data_t) malloc(sizeof(struct pdd_data)); if (!res) { rc = -ENOMEM; goto err; } rc = bootenv_create(&pdd); if (rc != 0) { goto err; } rc = bootenv_read_txt(fp_pdd, pdd); if (rc != 0) { goto err; } rc = bootenv_get(pdd, "flash_type", &value); if (rc != 0) { goto err; } if (strcmp(value, "NAND") == 0) { rc = bootenv_get_num(pdd, "flash_page_size", &(res->flash_page_size)); if (rc != 0) { EBUF("Cannot read 'flash_page_size' from pdd."); goto err; } res->flash_type = NAND_FLASH; switch (res->flash_page_size) { case 512: res->vid_hdr_offset = NAND512_DEFAULT_VID_HDR_OFF; break; case 2048: res->vid_hdr_offset = NAND2048_DEFAULT_VID_HDR_OFF; break; default: EBUF("Unsupported 'flash_page_size' %d.", res->flash_page_size); goto err; } } else if (strcmp(value, "NOR") == 0){ res->flash_type = NOR_FLASH; res->vid_hdr_offset = NOR_DEFAULT_VID_HDR_OFF; } else { snprintf(err_buf, err_buf_size, "Unkown flash type: %s", value); goto err; } rc = bootenv_get_num(pdd, "flash_eraseblock_size", &(res->eb_size)); if (rc != 0) { EBUF("Cannot read 'flash_eraseblock_size' from pdd."); goto err; } rc = bootenv_get_num(pdd, "flash_size", &(res->flash_size)); if (rc != 0) { EBUF("Cannot read 'flash_size' from pdd."); goto err; } goto out; err: if (res) { free(res); res = NULL; } out: bootenv_destroy(&pdd); *pdd_data = res; return rc; } int read_pfi_raw(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_raw_t* pfi_raw, const char* label, char* err_buf, size_t err_buf_size) { int rc = 0; char tmp_str[PFI_KEYWORD_LEN]; bootenv_list_t raw_start_list = NULL; pfi_raw_t res; res = (pfi_raw_t) malloc(sizeof(struct pfi_raw)); if (!res) return -ENOMEM; rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size)); if (rc != 0) { EBUF_PFI("Cannot read 'size' from PFI."); goto err; } rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc)); if (rc != 0) { EBUF_PFI("Cannot read 'crc' from PFI."); goto err; } rc = pfi_header_getstring(pfi_hd, "raw_starts", tmp_str, PFI_KEYWORD_LEN); if (rc != 0) { EBUF_PFI("Cannot read 'raw_starts' from PFI."); goto err; } rc = bootenv_list_create(&raw_start_list); if (rc != 0) { goto err; } rc = bootenv_list_import(raw_start_list, tmp_str); if (rc != 0) { EBUF_PFI("Cannot translate PFI value: %s", tmp_str); goto err; } rc = bootenv_list_to_num_vector(raw_start_list, &(res->starts_size), &(res->starts)); if (rc != 0) { EBUF_PFI("Cannot create numeric value array: %s", tmp_str); goto err; } goto out; err: if (res) { free(res); res = NULL; } out: bootenv_list_destroy(&raw_start_list); *pfi_raw = res; return rc; } int read_pfi_ubi(pfi_header pfi_hd, FILE* fp_pfi __unused, pfi_ubi_t* pfi_ubi, const char *label, char* err_buf, size_t err_buf_size) { int rc = 0; const char** tmp_names = NULL; char tmp_str[PFI_KEYWORD_LEN]; bootenv_list_t ubi_id_list = NULL; bootenv_list_t ubi_name_list = NULL; pfi_ubi_t res; uint32_t i; res = (pfi_ubi_t) calloc(1, sizeof(struct pfi_ubi)); if (!res) return -ENOMEM; rc = pfi_header_getnumber(pfi_hd, "size", &(res->data_size)); if (rc != 0) { EBUF_PFI("Cannot read 'size' from PFI."); goto err; } rc = pfi_header_getnumber(pfi_hd, "crc", &(res->crc)); if (rc != 0) { EBUF_PFI("Cannot read 'crc' from PFI."); goto err; } rc = pfi_header_getstring(pfi_hd, "ubi_ids", tmp_str, PFI_KEYWORD_LEN); if (rc != 0) { EBUF_PFI("Cannot read 'ubi_ids' from PFI."); goto err; } rc = bootenv_list_create(&ubi_id_list); if (rc != 0) { goto err; } rc = bootenv_list_create(&ubi_name_list); if (rc != 0) { goto err; } rc = bootenv_list_import(ubi_id_list, tmp_str); if (rc != 0) { EBUF_PFI("Cannot translate PFI value: %s", tmp_str); goto err; } rc = bootenv_list_to_num_vector(ubi_id_list, &(res->ids_size), &(res->ids)); if (rc != 0) { EBUF_PFI("Cannot create numeric value array: %s", tmp_str); goto err; } if (res->ids_size == 0) { rc = -1; EBUF_PFI("Sanity check failed: No ubi_ids specified."); goto err; } rc = pfi_header_getstring(pfi_hd, "ubi_type", tmp_str, PFI_KEYWORD_LEN); if (rc != 0) { EBUF_PFI("Cannot read 'ubi_type' from PFI."); goto err; } if (strcmp(tmp_str, "static") == 0) res->type = pfi_ubi_static; else if (strcmp(tmp_str, "dynamic") == 0) res->type = pfi_ubi_dynamic; else { EBUF_PFI("Unknown ubi_type in PFI."); goto err; } rc = pfi_header_getnumber(pfi_hd, "ubi_alignment", &(res->alignment)); if (rc != 0) { EBUF_PFI("Cannot read 'ubi_alignment' from PFI."); goto err; } rc = pfi_header_getnumber(pfi_hd, "ubi_size", &(res->size)); if (rc != 0) { EBUF_PFI("Cannot read 'ubi_size' from PFI."); goto err; } rc = pfi_header_getstring(pfi_hd, "ubi_names", tmp_str, PFI_KEYWORD_LEN); if (rc != 0) { EBUF_PFI("Cannot read 'ubi_names' from PFI."); goto err; } rc = bootenv_list_import(ubi_name_list, tmp_str); if (rc != 0) { EBUF_PFI("Cannot translate PFI value: %s", tmp_str); goto err; } rc = bootenv_list_to_vector(ubi_name_list, &(res->names_size), &(tmp_names)); if (rc != 0) { EBUF_PFI("Cannot create string array: %s", tmp_str); goto err; } if (res->names_size != res->ids_size) { EBUF_PFI("Sanity check failed: ubi_ids list does not match " "sizeof ubi_names list."); rc = -1; } /* copy tmp_names to own structure */ res->names = (char**) calloc(1, res->names_size * sizeof (char*)); if (res->names == NULL) goto err; for (i = 0; i < res->names_size; i++) { res->names[i] = calloc(PFI_UBI_VOL_NAME_LEN + 1, sizeof(char)); if (res->names[i] == NULL) goto err; strncpy(res->names[i], tmp_names[i], PFI_UBI_VOL_NAME_LEN + 1); } goto out; err: if (res) { if (res->names) { for (i = 0; i < res->names_size; i++) { if (res->names[i]) { free(res->names[i]); } } free(res->names); } if (res->ids) { free(res->ids); } free(res); res = NULL; } out: bootenv_list_destroy(&ubi_id_list); bootenv_list_destroy(&ubi_name_list); if (tmp_names != NULL) free(tmp_names); *pfi_ubi = res; return rc; } int free_pdd_data(pdd_data_t* pdd_data) { if (*pdd_data) { free(*pdd_data); } *pdd_data = NULL; return 0; } int free_pfi_raw(pfi_raw_t* pfi_raw) { pfi_raw_t tmp = *pfi_raw; if (tmp) { if (tmp->starts) free(tmp->starts); free(tmp); } *pfi_raw = NULL; return 0; } int free_pfi_ubi(pfi_ubi_t* pfi_ubi) { size_t i; pfi_ubi_t tmp = *pfi_ubi; if (tmp) { if (tmp->ids) free(tmp->ids); if (tmp->names) { for (i = 0; i < tmp->names_size; i++) { if (tmp->names[i]) { free(tmp->names[i]); } } free(tmp->names); } free(tmp); } *pfi_ubi = NULL; return 0; } int read_pfi_headers(list_t *pfi_raws, list_t *pfi_ubis, FILE* fp_pfi, char* err_buf, size_t err_buf_size) { int rc = 0; char mode[PFI_KEYWORD_LEN]; char label[PFI_LABEL_LEN]; *pfi_raws = mk_empty(); pfi_raw_t raw = NULL; *pfi_ubis = mk_empty(); pfi_ubi_t ubi = NULL; pfi_header pfi_header = NULL; /* read all headers from PFI and store them in lists */ rc = pfi_header_init(&pfi_header); if (rc != 0) { EBUF("Cannot initialize pfi header."); goto err; } while ((rc == 0) && !feof(fp_pfi)) { rc = pfi_header_read(fp_pfi, pfi_header); if (rc != 0) { if (rc == PFI_DATA_START) { rc = 0; break; /* data section starts, all headers read */ } else { goto err; } } rc = pfi_header_getstring(pfi_header, "label", label, PFI_LABEL_LEN); if (rc != 0) { EBUF("Cannot read 'label' from PFI."); goto err; } rc = pfi_header_getstring(pfi_header, "mode", mode, PFI_KEYWORD_LEN); if (rc != 0) { EBUF("Cannot read 'mode' from PFI."); goto err; } if (strcmp(mode, "ubi") == 0) { rc = read_pfi_ubi(pfi_header, fp_pfi, &ubi, label, err_buf, err_buf_size); if (rc != 0) { goto err; } *pfi_ubis = append_elem(ubi, *pfi_ubis); } else if (strcmp(mode, "raw") == 0) { rc = read_pfi_raw(pfi_header, fp_pfi, &raw, label, err_buf, err_buf_size); if (rc != 0) { goto err; } *pfi_raws = append_elem(raw, *pfi_raws); } else { EBUF("Recvieved unknown mode from PFI: %s", mode); goto err; } } goto out; err: *pfi_raws = remove_all((free_func_t)&free_pfi_raw, *pfi_raws); *pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, *pfi_ubis); out: pfi_header_destroy(&pfi_header); return rc; }