| /****************************************************************************** |
| * 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 <cpu.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <hw.h> |
| #include <rtas.h> |
| #include "rtas_board.h" |
| #include <bmc.h> |
| #include "rtas_flash.h" |
| #include <flash/block_lists.h> |
| #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 short manage_flash_buffer[BUFSIZE]; |
| 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(); |
| } |
| |
| 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 *)((void*)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] = 0; |
| } |
| |
| /** |
| * 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); |
| } |
| } |