/****************************************************************************** * Copyright (c) 2004, 2008 IBM Corporation * All rights reserved. * This program and the accompanying materials * are made available under the terms of the BSD License * which accompanies this distribution, and is available at * http://www.opensource.org/licenses/bsd-license.php * * Contributors: * IBM Corporation - initial implementation *****************************************************************************/ #include #include #include "block_lists.h" unsigned char sig_org[] = FLASHFS_PLATFORM_MAGIC; /* this function is part of the crc_lib assembler code */ unsigned long check_flash_image(unsigned long, unsigned long, unsigned long); /* this functions needs to be implemented by the board specific flash code * the functions always get 32 bytes and needs to deal with the data */ void write_flash(unsigned long, unsigned short *); int progress = 0; int print_progress(void) { static int i = 3; switch (i--) { case 3: printf("\b|"); break; case 2: printf("\b/"); break; case 1: printf("\b-"); break; case 0: printf("\b\\"); default: i = 3; } return 0; } void print_hash(void) { printf("\b# "); } void print_writing(void) { int counter = 42; printf("\nWriting Flash: |"); while (counter--) printf(" "); printf("|"); counter = 41; while (counter--) printf("\b"); } int get_block_list_version(unsigned char *data) { if (data[0] == 0x01) return 1; return 0; } static long get_image_size(unsigned long *data, unsigned long length) { long size = 0; unsigned long i; for (i = 0; i < length / 8; i += 2) { size += data[1 + i]; } return size; } static long get_image_size_v0(unsigned long *data) { unsigned long bl_size = data[0]; return get_image_size(data + 1, bl_size - 8); } static long get_image_size_v1(unsigned long *data) { unsigned long *bl_addr = data; unsigned long bl_size; unsigned long *next; long size = 0; while (bl_addr) { bl_size = bl_addr[0]; next = (unsigned long *) bl_addr[1]; bl_size = bl_size & 0x00FFFFFFFFFFFFFFUL; size += get_image_size(bl_addr + 2, bl_size - 0x10); bl_addr = next; } return size; } long get_size(unsigned long *data, int version) { if (version == 1) return get_image_size_v1(data); return get_image_size_v0(data); } static unsigned long write_one_block(unsigned long *block, unsigned long length, unsigned long offset) { unsigned long block_addr = (unsigned long) block; unsigned long i = 0; static unsigned int hash; if (offset == 0) hash = 0; for (i = 0; i < length; i += 32, offset += 32, block_addr += 32) { write_flash(offset, (unsigned short *) block_addr); if (offset % 10 == 0) { print_progress(); } if (offset > hash * progress) { print_hash(); hash++; } } return offset; } static unsigned long write_one_list(unsigned long *bl, unsigned long length, unsigned long offset) { unsigned long i; // 0x10: /8 for pointer /2 it has to be done in steps of 2 for (i = 0; i < length / 0x10; i++) { offset = write_one_block((unsigned long *) *bl, *(bl + 1), offset); bl += 2; } return offset; } void write_block_list(unsigned long *bl, int version) { unsigned long offset = 0; unsigned long *bl_addr = bl; unsigned long bl_size; unsigned long *next; if (version == 0) { // -8 = removed header length write_one_list(bl + 1, *(bl) - 8, offset); return; } while (bl_addr) { bl_size = bl_addr[0]; next = (unsigned long *) bl_addr[1]; bl_size = bl_size & 0x00FFFFFFFFFFFFFFUL; // -0x10 = removed header length offset = write_one_list(bl_addr + 2, bl_size - 0x10, offset); bl_addr = next; } } static int check_one_list(unsigned long *bl, unsigned long length, unsigned long crc) { unsigned long i; // 0x10: /8 for pointer /2 it has to be done in steps of 2 for (i = 0; i < length / 0x10; i++) { crc = check_flash_image((unsigned long) *bl, *(bl + 1), crc); bl += 2; } return crc; } int image_check_crc(unsigned long *bl, int version) { unsigned long *bl_addr = bl; unsigned long bl_size; unsigned long *next; unsigned long crc = 0; if (version == 0) { // -8 = removed header length return check_one_list(bl + 1, *(bl) - 8, crc); } while (bl_addr) { bl_size = bl_addr[0]; next = (unsigned long *) bl_addr[1]; bl_size = bl_size & 0x00FFFFFFFFFFFFFFUL; // -0x10 = removed header length crc = check_one_list(bl_addr + 2, bl_size - 0x10, crc); bl_addr = next; } return crc; } static int check_platform_one_list(unsigned long *bl, unsigned long bytesec) { unsigned long pos = bytesec; unsigned char *sig_tmp, *sig; unsigned long size = 0; sig = sig_org; while (size < bytesec) { size += bl[1]; while (size > pos) { // 32 == FLASHFS_PLATFORM_MAGIC length sig_tmp = (unsigned char *) (bl[0] + pos); if (*sig++ != *sig_tmp) return -1; if (*sig_tmp == '\0' || (pos == bytesec + 32)) { pos = bytesec + 32; break; } pos++; } if (pos == (bytesec + 32)) return 0; bl += 2; } return 0; } int check_platform(unsigned long *bl, unsigned int bytesec, int version) { unsigned long *bl_addr = bl; unsigned long bl_size; unsigned long *next; unsigned long *ptr; ptr = bl; if (version == 0) { ptr += 1; // -8 = removed header length return check_platform_one_list(ptr, bytesec); } while (bl_addr) { ptr = bl_addr + 2; // -0x10 = removed header length bl_size = bl_addr[0]; next = (unsigned long *) bl_addr[1]; bl_size = bl_size & 0x00FFFFFFFFFFFFFFUL; if ((bl_size - 0x10) == 0) { bl_addr = next; continue; } if (check_platform_one_list(ptr, bytesec) == 0) return 0; bl_addr = next; } return -1; }