/****************************************************************************** * 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 #include #include #include #include "rtas_board.h" #include #include "rtas_flash.h" #include #include "product.h" #include "calculatecrc.h" #undef DEBUG #ifdef DEBUG #define dprintf(_x ...) printf(_x) #else #define dprintf(_x ...) #endif static uint64_t size; static uint64_t flashOffset; unsigned char manage_flash_buffer[BUFSIZE*2]; unsigned long check_flash_image(unsigned long rombase, unsigned long length, unsigned long start_crc); #ifdef DEBUG static void dump_blocklist(uint64_t *bl, int version) { uint64_t bl_size; uint8_t *addr = (uint8_t *)bl; if (version == 1) { /* version 1 blocklist */ bl_size = *bl & 0x00FFFFFFFFFFFFFFUL; } else { bl_size = *bl; } printf("\n\rblocklist_dump %lx", bl_size); while (bl_size) { unsigned int tmpCnt = bl_size; unsigned char x; if (tmpCnt > 8) tmpCnt = 8; printf("\n\r%08x: ", addr); /* print hex */ while (tmpCnt--) { set_ci(); x = *addr++; clr_ci(); printf("%02x ", x); } tmpCnt = bl_size; if (tmpCnt > 8) tmpCnt = 8; bl_size -= tmpCnt; /* reset addr ptr to print ascii */ addr = addr - tmpCnt; /* print ascii */ while (tmpCnt--) { set_ci(); x = *addr++; clr_ci(); if ((x < 32) || (x >= 127)) { /* non-printable char */ x = '.'; } printf("%c", x); } } printf("\r\n"); } #endif void rtas_dump_flash(rtas_args_t *rtas_args) { int retVal = 0; unsigned int size = rtas_args->args[0]; unsigned int offset = rtas_args->args[1]; volatile unsigned char *flash = (volatile unsigned char *)FLASH; printf("\n\rflash_dump %x %x", size, offset); flash += offset; while (size) { unsigned int tmpCnt = size; unsigned char x; if (tmpCnt > 16) tmpCnt = 16; printf("\n\r%p: ", flash); /* print hex */ while (tmpCnt--) { set_ci(); x = *flash++; clr_ci(); printf("%02x ", x); } tmpCnt = size; if (tmpCnt > 16) tmpCnt = 16; size -= tmpCnt; /* reset flash ptr to print ascii */ flash = flash - tmpCnt; /* print ascii */ while (tmpCnt--) { set_ci(); x = *flash++; clr_ci(); if ((x < 32) || (x >= 127)) { /* non-printable char */ x = '.'; } printf("%c", x); } } printf("\r\n"); rtas_args->args[rtas_args->nargs] = retVal; } static void print_block(int i) { int counter = 8; while (counter--) printf("\b"); printf("%08x", i); } /* To enter data mode after flash has been in programming mode * a 0xFF has to be written */ static void enter_data_mode(void) { volatile unsigned char *flash = (volatile unsigned char *)FLASH; set_ci(); *flash = 0xFF; eieio(); clr_ci(); } static void erase_flash_block(unsigned long offset) { volatile unsigned char *flash = (volatile unsigned char *)FLASH; flash += offset; set_ci(); *flash = 0x20; eieio(); *flash = 0xd0; eieio(); while (!(*flash & 0x80)) ; clr_ci(); } void write_flash(unsigned long offset, unsigned char *data) { int cnt = 32; volatile unsigned char *flash = (volatile unsigned char *)FLASH; flash += (offset + flashOffset); set_ci(); while (cnt) { if (!((uint64_t)flash & 0x1F)) { while (cnt) { uint64_t tmpcnt = cnt; if (tmpcnt > 0x20) tmpcnt = 0x20; do { *flash = 0xE8; eieio(); } while (!(*flash & 0x80)); cnt -= tmpcnt; *flash = tmpcnt - 1; while (tmpcnt--) { *flash++ = *data++; } *flash = 0xD0; eieio(); while (!(*flash & 0x80)) ; } break; } *flash = 0x40; eieio(); *flash++ = *data++; eieio(); while (!(*flash & 0x80)) ; cnt--; } clr_ci(); } static void write_flash_page(unsigned long offset, unsigned short *data) { int i = 0; for (i = 0; i < BUFSIZE; i += 32, offset += 32) { write_flash(offset, ((unsigned char *)data + i)); } } /* * 0 reject temporary image * 1 commit temporary image * */ static int copy_flash(short mode) { volatile unsigned char *flash = (volatile unsigned char *)FLASH; uint64_t blockCnt; uint64_t hash = 0; short notmode = mode ^ 0x1; if (bmc_set_flashside(notmode) != notmode) { return -1; } printf("\r\nErasing Flash: 0x "); for (blockCnt = 0; blockCnt <= FLASHSIZE; blockCnt += FLASH_BLOCK_SIZE) { print_block(blockCnt); erase_flash_block(blockCnt); } enter_data_mode(); progress = FLASHSIZE / 38; print_writing(); for (blockCnt = 0; blockCnt <= FLASHSIZE; blockCnt += BUFSIZE) { uint64_t *srcPtr = (uint64_t *)(flash + blockCnt); uint64_t *destPtr = (uint64_t *)manage_flash_buffer; uint64_t cnt = BUFSIZE / 8; if (bmc_set_flashside(mode) != mode) { return -1; } enter_data_mode(); set_ci(); while (cnt--) { *destPtr++ = *srcPtr++; } clr_ci(); if (bmc_set_flashside(notmode) != notmode) { return -1; } write_flash_page(blockCnt, (unsigned short *)manage_flash_buffer); /* progress output... */ print_progress(); if (blockCnt > hash * progress) { print_hash(); hash++; } } enter_data_mode(); if (bmc_set_flashside(mode) != mode) { return -1; } printf("\b#\n"); return 0; } /* * Function: ibm_manage_flash_image * Input: * r3: rtas parm structure * token: 46 * in: 1 * out: 1 * parm0: 0 reject temporary image * 1 commit temporary image * Output: * parm1: Status (hw -1, busy -2, parameter error -3 * -9001 cannot overwrite the active firmware image) * */ void rtas_ibm_manage_flash_image(rtas_args_t *rtas_args) { int side; int result = 0; short mode = rtas_args->args[0]; if (mode < 0 || mode > 1) { rtas_args->args[rtas_args->nargs] = -3; return; } side = bmc_get_flashside(); if (side == 0) { /* we are on the permanent side */ if (mode != 0) { rtas_args->args[rtas_args->nargs] = -9001; return; } } else if (side == 1) { /* we are on the temporary side */ if (mode != 1) { rtas_args->args[rtas_args->nargs] = -9001; return; } } else { rtas_args->args[rtas_args->nargs] = -1; return; } result = copy_flash(mode); bmc_set_flashside(mode); enter_data_mode(); rtas_args->args[rtas_args->nargs] = result; } /** * check, if we find the FLASHFS_MAGIC token in bl **/ static uint8_t check_magic(uint64_t *bl, int version) { struct stH *pHeader; if (version == 1) { /* version 1 blocklist */ /* if block list size <= 0x10, it is only block list header */ /* and address of block list extension, so look at the extension... */ while ((*bl & 0x00FFFFFFFFFFFFFFUL) <= 0x10) bl = (uint64_t *)bl[1]; /* block list item 2 _should_ be the address of our flashfs image */ pHeader = (struct stH *)(bl[2] + 0x28); /* printf("FlashFS Magic: \"%#s\"\r\n", pHeader->magic); */ return strncmp(pHeader->magic, FLASHFS_MAGIC, 8); } else { /* block list item 1 _should_ be the address of our flashfs image */ pHeader = (struct stH *)(bl[1] + 0x28); /* printf("FlashFS Magic: \"%#s\"\r\n", pHeader->magic); */ return strncmp(pHeader->magic, FLASHFS_MAGIC, 8); } } static void get_image_name(char *buffer, int maxsize) { volatile struct stH *flash_header = (volatile struct stH *)(SB_FLASH_adr + 0x28); /* since we cannot read the fh_magic directly from flash as a string, we need to copy it to memory */ uint64_t magic_val = 0; uint64_t addr; /* copy fh_magic to magic_val since, we cannot use it as a string from flash */ magic_val = load64_ci((uint64_t)(flash_header->magic)); if (strncmp((char *)&magic_val, FLASHFS_MAGIC, 8)) { /* magic does not match */ sprintf(buffer, "Unknown"); buffer[maxsize - 1] = '\0'; return; } addr = (uint64_t)flash_header->version; while (--maxsize) { *buffer = load8_ci(addr++); if (!*buffer++) return; } *buffer = '\0'; } /** * validate_flash_image * this function checks if the flash will be updated with the given image * @param args[0] - buffer with minimum 4K of the image to flash * @param args[1] - size of the buffer * @param args[2] - status: * 0 success * -1 hw * -2 busy * -3 parameter error * @param args[3] - update result token */ void rtas_ibm_validate_flash_image(rtas_args_t *rtas_args) { dprintf("\nrtas_ibm_validate_flash_image\n"); unsigned long new_image = rtas_args->args[0]; char *ret_str = (char *)new_image; struct stH *flash_header = (struct stH *)(new_image + 0x28); char current_temp_version[16]; char current_perm_version[16]; char new_version[16]; int side = bmc_get_flashside(); /* fill args[0] with the current values which is needed * in an error case */ bmc_set_flashside(0); get_image_name(current_perm_version, sizeof(current_perm_version)); bmc_set_flashside(1); get_image_name(current_temp_version, sizeof(current_temp_version)); bmc_set_flashside(side); /* check if the candidate image if valid for this platform */ if (strncmp(flash_header->magic, FLASHFS_MAGIC, 8)) { /* magic does not match */ rtas_args->args[rtas_args->nargs] = 0; /* No update done, the candidate image is * not valid for this platform */ rtas_args->args[rtas_args->nargs + 1] = 2; sprintf(ret_str, "MI %s %s\xaMI %s %s", current_temp_version, current_perm_version, current_temp_version, current_perm_version); return; } if (strncmp(flash_header->platform_name, (char *)sig_org, 32)) { /* this image if for a different board */ rtas_args->args[rtas_args->nargs] = 0; /* No update done, the candidate image is * not valid for this platform */ rtas_args->args[rtas_args->nargs + 1] = 2; sprintf(ret_str, "MI %s %s\xaMI %s %s", current_temp_version, current_perm_version, current_temp_version, current_perm_version); return; } /* check header crc */ if (check_flash_image(rtas_args->args[0], 0x88, 0)) { /* header crc failed */ rtas_args->args[rtas_args->nargs] = 0; /* No update done, the candidate image is * not valid for this platform */ rtas_args->args[rtas_args->nargs + 1] = 2; sprintf(ret_str, "MI %s %s\xaMI %s %s", current_temp_version, current_perm_version, current_temp_version, current_perm_version); return; } memcpy(new_version, flash_header->version, 16); sprintf(ret_str, "MI %s %s\xaMI %s %s", current_temp_version, current_perm_version, new_version, current_perm_version); rtas_args->args[rtas_args->nargs] = 0; if (strncmp(new_version, current_temp_version, 16) >= 0) rtas_args->args[rtas_args->nargs + 1] = 0; else rtas_args->args[rtas_args->nargs + 1] = 6; } /* * Function: ibm_update_flash_64 * Input: * r3: rtas parm structure * token: 7 * in: 1 * out: 1 * parm0: A real pointer to a block list * Output: * parm1: Status (hw -1, bad image -3, programming failed -4) * * Description: flash if addresses above 4GB have to be addressed */ void rtas_update_flash(rtas_args_t *rtas_args) { void *bl = (void *)(uint64_t)rtas_args->args[0]; int version = get_block_list_version((unsigned char *)bl); uint64_t erase_size; unsigned int i; int perm_check = 1; #ifdef DEBUG dump_blocklist(bl, version); #endif /* from SLOF we pass a second (unofficial) parameter, if this parameter is 1, we do not * check wether we are on permanent side. Needed for update-flash -c to work! */ if ((rtas_args->nargs > 1) && (rtas_args->args[1] == 1)) perm_check = 0; /* check magic string */ printf("\r\nChecking magic string : "); if (check_magic(bl, version) != 0) { printf("failed!\n"); rtas_args->args[rtas_args->nargs] = -3; /* bad image */ return; } printf("succeeded!\n"); /* check platform */ printf("Checking platform : "); if (check_platform(bl, 0x48, version) == -1) { printf("failed!\n"); rtas_args->args[rtas_args->nargs] = -3; /* bad image */ return; } printf("succeeded!\n"); /* checkcrc */ printf("Checking CRC : "); /* the actual CRC is included at the end of the flash image, thus the resulting CRC must be 0! */ if (image_check_crc(bl, version) != 0) { printf("failed!\n"); rtas_args->args[1] = -3; /* bad image */ return; } printf("succeeded!\n"); /* check if we are running on P * if so, let's switch to temp and flash temp */ if (bmc_get_flashside() == 0 && perm_check) { printf("Set flashside: "); bmc_set_flashside(1); printf("Temp!\n"); } #ifdef DEBUG rtas_args_t ra; ra.args[0] = 0x100; /* size; */ ra.args[1] = flashOffset; ra.nargs = 2; rtas_dump_flash(&ra); printf("\n"); #endif size = get_size(bl, version); erase_size = (size + (FLASH_BLOCK_SIZE - 1)) & ~(FLASH_BLOCK_SIZE - 1); dprintf("Erasing: size: %#x, erase_size: %#x, FLASH_BLOCK_SIZE: %#x\n", size, erase_size, FLASH_BLOCK_SIZE); progress = size / 39; printf("Erasing : 0x%08x", 0); for (i = 0; i < erase_size; i += FLASH_BLOCK_SIZE) { print_block(i); erase_flash_block(i); } enter_data_mode(); #ifdef DEBUG rtas_dump_flash(&ra); printf("\n"); #endif print_writing(); write_block_list(bl, version); printf("\b#\n"); enter_data_mode(); #ifdef DEBUG rtas_dump_flash(&ra); printf("\n"); #endif /* checkcrc */ printf("Recheck CRC : "); if (check_flash_image(FLASH + flashOffset, size, 0) != 0) { /* failed */ printf("failed!\n\r"); dprintf("flash_addr: %#x, flashOffset: %#x, size: %#x\n", FLASH, flashOffset, size); dprintf("crc: %#x\n", check_flash_image(FLASH + flashOffset, size, 0)); rtas_args->args[rtas_args->nargs] = -4; /* programming failed */ return; } printf("succeeded!\n"); rtas_args->args[rtas_args->nargs] = 0; } /* * Function: ibm_update_flash_64_and_reboot * Input: * r3: rtas parm structure * token: 27 * in: 1 * out: 1 * parm0: A real pointer to a block list * Output: * parm1: Status (hw -1, bad image -3, programming failed -4) * Currently -4 and -1 are not returned * * Description: flash and reboot if addresses above 4GB have to be addressed */ void rtas_ibm_update_flash_64_and_reboot(rtas_args_t *rtas_args) { rtas_update_flash(rtas_args); dprintf("rc: %#d\n", rtas_args->args[rtas_args->nargs]); if (rtas_args->args[rtas_args->nargs] == 0) { rtas_system_reboot(rtas_args); } }