/*++ Copyright (c) 1992 Microsoft Corporation Copyright (c) 1993 Digital Equipment Corporation Module Name: jnupdate.c Abstract: The main module for the JNUPDATE.EXE Jensen firwmare update program. Author: John DeRosa 14-October-1992 Environment: Kernel mode only. Revision History: --*/ #include "fwp.h" #include "machdef.h" //#include "jnsnrtc.h" #include "string.h" #include "jnfs.h" #include "jnvendor.h" #include "iodevice.h" #include "rom.h" #include "fwupstr.h" // Number of times we will try to update a block before telling the user. #define ROM_BLOCK_RETRY_COUNT 4 #define MAXIMUM_IDENTIFIER_LENGTH 400 #define MAXIMUM_NUMBER_ROM_BLOCKS 16 #define MINIMUM_UPDATE_FILE_SIZE ( 5 + \ (SIXTY_FOUR_KB * 1) + \ 4 ) #define MAXIMUM_UPDATE_FILE_SIZE ( MAXIMUM_IDENTIFIER_LENGTH + \ (SIXTY_FOUR_KB * 16 ) + \ 4 ) // // The type of ROM in this machine, and the array that that describes // how to talk to different kinds of ROMs. // extern ROM_TYPE MachineROMType; extern ROM_VALUES RomValues[InvalidROM]; // // The indicator of a bad Firmware stack for _RtlCheckStack // ULONG FwRtlStackPanic; extern PCHAR FirmwareVersion; // // Function prototypes // ARC_STATUS FwpFindCDROM ( OUT PCHAR PathName ); #ifdef MORGAN VOID MorganReadFlashRomId( ULONG MorganFlashRomChipSelect, PULONG MfgCode, PULONG DeviceCode ); ULONG MorganBlastFlash( ULONG MorganFlashRomChipSelect, PUCHAR FirmwareStart, ULONG FirmwareBufferLength ); ARC_STATUS MorganFsStoreIntoROM( IN ULONG MorganFlashRomChipSelect, IN PUCHAR FirmwareStart, IN ULONG FirmwareBufferLength ); #endif // ifdef MORGAN VOID main ( VOID ) /*++ Routine Description: This calls other functions to communicate with the user, get the firmware update file, verify it, and do the update. Success and error messages are printed out by this function and/or functions below this one. Arguments: None. Return Value: None. --*/ { ARC_STATUS Status; LONG Index; ULONG CurrentLine; ULONG FirmwareBufferAddress; ULONG FirmwareBufferLength; ULONG FirmwareStart; ULONG HighROMBlockToBeWritten; ULONG LowROMBlockToBeWritten; BOOLEAN VariableFound; PCONFIGURATION_COMPONENT Controller; PCHAR Colon; PCHAR EnvironmentValue; CHAR PathName[128]; PCHAR TempArgs; CHAR UserSpecifiedPath[128]; GETSTRING_ACTION Action; PCONFIGURATION_COMPONENT Cdrom; FwRtlStackPanic = 0; VenClearScreen(); VenSetPosition(1,0); VenPrint1(FWUP_INTRO1_MSG, FirmwareVersion); VenPrint (FWUP_INTRO2_MSG); VenPrint (FWUP_INTRO3_MSG); if (!JnFsProceedWithUpdate()) { return; } VenClearScreen(); VenSetPosition(1,0); VenPrint(FWUP_SELECT_LOCATION_MSG); VenPrint(FWUP_USE_ARROW_KEYS_MSG); VenPrint(FWUP_HIT_ESC_TO_ABORT_MSG); Index = JzDisplayMenu(UpdateLocationChoices, NUMBER_OF_UPDATELOCATIONCHOICES, 0, 7, 0, FALSE); CurrentLine = 12; switch (Index) { // // Floppy drive // case 0: strcpy(PathName, FWUP_DEFAULT_FLOPPY_LOCATION); break; // // CD-ROM // case 1: FwpFindCDROM(PathName); strcat(PathName, FWUP_DEFAULT_CDROM_FILENAME); break; // // Some other location // case 2: VenSetPosition(12,0); VenPrint(FWUP_LOCATION_OF_UPDATE_FILE_MSG); do { Action = JzGetString(UserSpecifiedPath, sizeof(UserSpecifiedPath), NULL, 12, strlen(FWUP_LOCATION_OF_UPDATE_FILE_MSG), FALSE ); } while ((Action != GetStringEscape) && (Action != GetStringSuccess)); if (Action == GetStringEscape) { return; } CurrentLine += 2; // // Strip off any arguments. // if ((TempArgs = strchr(UserSpecifiedPath, ' ')) != NULL) { *TempArgs++ = 0; } // // If the name does not contain a "(", then assume it is not a full // pathname. // if (strchr( UserSpecifiedPath, '(') == NULL) { // // If the name contains a semicolon, look for an environment // variable that defines the path. // if ((Colon = strchr( UserSpecifiedPath, ':')) != NULL) { for (Index = 0; UserSpecifiedPath[Index] != ':' ; Index++ ) { PathName[Index] = tolower(UserSpecifiedPath[Index]); } PathName[Index++] = ':'; PathName[Index++] = 0; EnvironmentValue = ArcGetEnvironmentVariable(PathName); VariableFound = FALSE; if (EnvironmentValue != NULL) { strcpy( PathName, EnvironmentValue); VariableFound = TRUE; } else if (!strcmp(PathName, "cd:")) { for ( Index = 0 ; Index < 8 ; Index++ ) { sprintf(PathName, "scsi(0)cdrom(%d)fdisk(0)", Index); Controller = ArcGetComponent(PathName); if ((Controller != NULL) && (Controller->Type == FloppyDiskPeripheral)) { VariableFound = TRUE; break; } } } if (!VariableFound) { VenSetPosition( 17, 0); VenSetScreenColor(ArcColorRed, ArcColorWhite); VenPrint(FWUP_UNDEFINED_PATHNAME_MSG); FwWaitForKeypress(FALSE); VenSetScreenColor(ArcColorWhite, ArcColorBlue); return; } else { strcat( PathName, Colon + 1); } } else { // // The filespec does not contain a '(' or a :. // FWSEARCHPATH is not supported in this tool. // Therefore, declare an error. // VenSetPosition( 17, 0); VenSetScreenColor(ArcColorRed, ArcColorWhite); VenPrint1(FWUP_BAD_PATHNAME_MSG, UserSpecifiedPath); FwWaitForKeypress(FALSE); VenSetScreenColor(ArcColorWhite, ArcColorBlue); return; } } else { strcpy( PathName, UserSpecifiedPath); } break; // // Exit, or internal error. // case 3: default: return; } VenSetPosition(CurrentLine, 0); VenPrint(FWUP_LOCATING_THE_FILE_MSG); Status = ReadAndVerifyUpdateFile(PathName, &FirmwareBufferAddress, &FirmwareStart, &FirmwareBufferLength, &LowROMBlockToBeWritten, &HighROMBlockToBeWritten); if (Status != ESUCCESS) { JnFsDecodeBadStatus(Status); FwWaitForKeypress(FALSE); return; } VenClearScreen(); VenSetPosition(1,0); VenPrint(FWUP_UPDATE_FILE_IS_GOOD_MSG); VenPrint ((PUCHAR)FirmwareBufferAddress); // // If manufacturing followed the rules, we should be at or above row #14. // VenPrint("\r\n\n"); if (!JnFsProceedWithUpdate()) { return; } VenPrint(FWUP_ARE_YOU_REALLY_SURE_MSG); if (!JnFsProceedWithUpdate()) { return; } #ifdef JENSEN Status = JnFsStoreIntoROM((PUCHAR)FirmwareStart, LowROMBlockToBeWritten, HighROMBlockToBeWritten); #endif // JENSEN #ifdef MORGAN VenPrint1(FWUP_ABOUT_TO_WRITE_ROM_MSG, LowROMBlockToBeWritten); FwWaitForKeypress(); Status = MorganFsStoreIntoROM( LowROMBlockToBeWritten, (PUCHAR)FirmwareStart, FirmwareBufferLength); #endif // MORGAN if (Status != ESUCCESS) { JnFsDecodeBadStatus(Status); VenPrint(FWUP_FAILED_UPDATE_MSG); } else { VenPrint(FWUP_SUCCESSFUL_UPDATE_MSG); } FwWaitForKeypress(FALSE); return; } BOOLEAN HackGetYesNo ( VOID ) /*++ Routine Description: This asks the user to type a Y or N for a debug clause located later in this module. This is only needed while I am trying to understand the FlashFile ROM update failures, and should be removed when the problem is resolved. Arguments: None. Return Value: TRUE if the user hits the Y key. FALSE otherwise. --*/ { CHAR Character; ULONG Count; VenPrint(FWUP_YORN_MSG); while (ArcGetReadStatus(ARC_CONSOLE_INPUT) != ESUCCESS) { } ArcRead(ARC_CONSOLE_INPUT, &Character, 1, &Count); VenPrint1("%c\r\n", Character); if ((Character == FWUP_LOWER_Y) || (Character == FWUP_UPPER_Y)) { return TRUE; } else { return FALSE; } } #ifdef JENSEN ARC_STATUS JnFsStoreIntoROM( IN PUCHAR FirmwareStart, IN ULONG LowROMBlock, IN ULONG HighROMBlock ) /*++ Routine Description: The firmware update has been read into memory and verified to be correct, and the user has told us to go ahead with the update. Operation: - Clear the LowROMBlock so that if we are updating the VMS Console, a reboot will bring up the FailSafe Booter. - Update the ROM in reverse order, high block -> low block. - Verify the ROM against the information in the update buffer. - Tells the user to power-cycle the machine if everything is fine. - Retries failing blocks, and when retry count is exceeded, asks the user if we should try again. Arguments: FirmwareStart A pointer to the beginning of the ROM binary information. This is byte 0 of the low block. LowROMBlock The number of the first ROM block to be updated. HighROMBlock The number of the last ROM block to be updated. HighROMBlock is guaranteed by the caller to be >= LowROMBlock. Return Value: ESUCCESS if update was successful. Otherwise, an error code. --*/ { ULONG Count; UCHAR Character; ARC_STATUS Status; BOOLEAN BlockErased[MAXIMUM_NUMBER_ROM_BLOCKS]; BOOLEAN BlockStored[MAXIMUM_NUMBER_ROM_BLOCKS]; ULONG TempX; ULONG TempY; PUCHAR PromBlockAddress; PUCHAR LocalPromAddress; PCHAR BufferData; PCHAR LocalBufferData; ULONG RetryIndex; ULONG BadCount = 0; // Needed while debugging --- remove later.. VenClearScreen(); // // Verify the block arguments // if ((LowROMBlock > HighROMBlock) || (HighROMBlock > (MAXIMUM_NUMBER_ROM_BLOCKS-1))) { return ENODEV; } // // Initialize the ROM block status arrays to "not done". // for (TempX = LowROMBlock; TempX <= HighROMBlock; TempX++) { BlockErased[TempX] = FALSE; BlockStored[TempX] = FALSE; } // // Determine the type of ROM in the machine. // if (FwROMDetermineMachineROMType() != ESUCCESS) { VenPrint(FWUP_UNKNOWN_ROM_MSG); return ENODEV; } VenPrint1(FWUP_ROM_TYPE_IS_MSG, MachineROMType); // // Erase the low block. // VenPrint(FWUP_CLEARING_LOW_PROM_BLOCK_MSG); RetryIndex = 0; do { PromBlockAddress = (PUCHAR)(PROM_VIRTUAL_BASE + (LowROMBlock * SIXTY_FOUR_KB)); if (!BlockErased[LowROMBlock]) { if (FwROMErase64KB(PromBlockAddress) == ESUCCESS) { BlockErased[LowROMBlock] = TRUE; } } // // Now verify that the block has been cleared. // for (TempY = 0; TempY < SIXTY_FOUR_KB; TempY++) { if (READ_PORT_UCHAR(PromBlockAddress++) != 0xff) { BlockErased[LowROMBlock] = FALSE; break; } } // // If the block has not been cleared after ROM_BLOCK_RETRY_COUNT // attempts, ask the user if we should continue trying. // RetryIndex++; if (!BlockErased[LowROMBlock] && ((RetryIndex % ROM_BLOCK_RETRY_COUNT) == 0)) { VenPrint2(FWUP_BLOCK_CANNOT_BE_ERASED_MSG, LowROMBlock, RetryIndex); VenPrint(FWUP_KEEP_TRYING_MSG); if (!JnFsProceedWithUpdate()) { return EIO; } } } while (!BlockErased[LowROMBlock]); // // Now we write the update into the PROM starting at the highest block. // Reverse writing is used because the serial ROM transfers control to // the VMS console if the checksum of the console is good *and* the // first block is not all 1s. So, if this update includes the VMS console, // we want to write the lowest block last. For other updates (e.g., // updates to the NT firmware only) it does not matter. // // // For writes of at least two blocks, start at the high block and // repeatedly try to update every block except the low block. // if (HighROMBlock > LowROMBlock) { VenPrint(FWUP_CLEARING_AND_WRITING_HIGHER_BLOCKS_MSG); RetryIndex = 0; do { // // Start at the base of the highest block to be written // PromBlockAddress = (PUCHAR)(PROM_VIRTUAL_BASE + (HighROMBlock * SIXTY_FOUR_KB)); BufferData = FirmwareStart + (HighROMBlock * SIXTY_FOUR_KB) - (LowROMBlock * SIXTY_FOUR_KB); // // TempX > LowROMBlock is intentional, so low block is not updated. // for (TempX = HighROMBlock; TempX > LowROMBlock; --TempX) { VenPrint("."); // // Have we cleared and written this block? If not, do so. // if (!BlockStored[TempX]) { // // First erase the block. // BlockErased[TempX] = FALSE; if (FwROMErase64KB(PromBlockAddress) == ESUCCESS) { BlockErased[TempX] = TRUE; } // // Verify that the block has been erased properly. // LocalPromAddress = PromBlockAddress; for (TempY = 0; TempY < SIXTY_FOUR_KB; TempY++) { if (READ_PORT_UCHAR(LocalPromAddress++) != 0xff) { BlockErased[TempX] = FALSE; break; } } // // Write the block if the erase succeeded. // if (BlockErased[TempX]) { BlockStored[TempX] = TRUE; LocalPromAddress = PromBlockAddress; LocalBufferData = BufferData; for (TempY = 0; TempY < SIXTY_FOUR_KB; TempY++) { if (FwROMByteWrite(LocalPromAddress++, *LocalBufferData++) != ESUCCESS) { BlockStored[TempX] = FALSE; break; } } if (BlockStored[TempX]) { // // Verify that the writes have succeeded. // // // First, set the device(s) to read-mode. To // support devices with a block size smaller than // 64KB, we issues set read-mode commands to every // block within this virtual 64KB block. This is // overkill, but it is easy and quick. // LocalPromAddress = PromBlockAddress; for (TempY = 0; TempY < (SIXTY_FOUR_KB / RomValues[MachineROMType].BytesPerBlock); TempY++) { FwROMSetReadMode(LocalPromAddress); LocalPromAddress += RomValues[MachineROMType].BytesPerBlock; } LocalPromAddress = PromBlockAddress; LocalBufferData = BufferData; for (TempY = 0; TempY < SIXTY_FOUR_KB; TempY++) { if (READ_PORT_UCHAR(LocalPromAddress++) != *LocalBufferData++) { BlockStored[TempX] = FALSE; break; } } } } // if (BlockErased[TempX]) } // if... // Decrement pointers to the next block PromBlockAddress -= SIXTY_FOUR_KB; BufferData -= SIXTY_FOUR_KB; } // for... // // BlockErased[TempX] is TRUE iff the block was erased. // BlockStored[TempX] is TRUE iff the block was erased and stored. // RetryIndex++; if (AllBlocksNotDone(BlockStored, LowROMBlock+1, HighROMBlock) && ((RetryIndex % ROM_BLOCK_RETRY_COUNT) == 0)) { VenPrint1(FWUP_SOME_BLOCKS_CANNOT_BE_ERASED_MSG, RetryIndex); VenPrint(FWUP_ERASE_FAILURES_MSG); // // TempX = LowROMBlock+1 is intentional, to not check the // lowest block. // for (TempX = LowROMBlock+1; TempX <= HighROMBlock; TempX++) { if (!BlockErased[TempX]) { VenPrint1("%d. ", TempX); } } if (!AllBlocksNotDone(BlockErased, LowROMBlock+1, HighROMBlock)) { VenPrint(FWUP_NONE_MSG); } VenPrint(FWUP_WRITE_FAILURES_MSG); // // TempX = LowROMBlock+1 is intentional, to not check the // lowest block. // for (TempX = LowROMBlock+1; TempX <= HighROMBlock; TempX++) { if (!BlockStored[TempX]) { VenPrint1("%d. ", TempX); } } if (!AllBlocksNotDone(BlockStored, LowROMBlock+1, HighROMBlock)) { VenPrint(FWUP_INTERNAL_ERROR_MSG); } VenPrint(FWUP_DO_YOU_WISH_TO_KEEP_TRYING_MSG); if (!JnFsProceedWithUpdate()) { return EIO; } } } while (AllBlocksNotDone(BlockStored, LowROMBlock+1, HighROMBlock)); } // // Now write the lowest/only block. // if (HighROMBlock == LowROMBlock) { VenPrint(FWUP_WRITING_THE_BLOCK_MSG); } else { VenPrint(FWUP_WRITING_THE_LOW_BLOCK_MSG); } BlockStored[LowROMBlock] = FALSE; RetryIndex = 0; do { PromBlockAddress = (PUCHAR)(PROM_VIRTUAL_BASE + (LowROMBlock * SIXTY_FOUR_KB)); BufferData = FirmwareStart; // // First erase the block. // BlockErased[LowROMBlock] = FALSE; if (FwROMErase64KB(PromBlockAddress) == ESUCCESS) { BlockErased[LowROMBlock] = TRUE; } else { VenPrint(FWUP_BLOCK_CANNOT_BE_ERASED_AFTER_10_MSG); } // // Verify that the block has been erased. // LocalPromAddress = PromBlockAddress; for (TempY = 0; TempY < SIXTY_FOUR_KB; TempY++) { if (READ_PORT_UCHAR(LocalPromAddress++) != 0xff) { BlockErased[LowROMBlock] = FALSE; VenPrint(FWUP_ERASURE_VERIFICATION_FAILED_MSG); break; } } // // Write the block if the erase succeeded. // if (BlockErased[LowROMBlock]) { BlockStored[LowROMBlock] = TRUE; LocalPromAddress = PromBlockAddress; LocalBufferData = BufferData; for (TempY = 0; TempY < SIXTY_FOUR_KB; TempY++) { if (FwROMByteWrite(LocalPromAddress++, *LocalBufferData++) != ESUCCESS) { BlockStored[LowROMBlock] = FALSE; break; } } if (BlockStored[LowROMBlock]) { // // Verify that the writes have succeeded // // // First, set the device(s) to read-mode. To // support devices with a block size smaller than // 64KB, we issues set read-mode commands to every // block within this virtual 64KB block. This is // overkill, but it is easy and quick. // LocalPromAddress = PromBlockAddress; for (TempY = 0; TempY < (SIXTY_FOUR_KB / RomValues[MachineROMType].BytesPerBlock); TempY++) { FwROMSetReadMode(LocalPromAddress); LocalPromAddress += RomValues[MachineROMType].BytesPerBlock; } LocalPromAddress = PromBlockAddress; LocalBufferData = BufferData; for (TempY = 0; TempY < SIXTY_FOUR_KB; TempY++) { if (READ_PORT_UCHAR(LocalPromAddress++) != *LocalBufferData++) { #if 1 // // This is temporary debugging assistance for // the ROM update failures. The #if should be // turned off once the problem has been // understood and fixed. // PUCHAR TempPromAddress; PCHAR TempBufferData; TempPromAddress = LocalPromAddress - 1; TempBufferData = LocalBufferData - 1; VenPrint2(FWUP_BAD_DATA_MSG, TempPromAddress, READ_PORT_UCHAR(TempPromAddress) ); VenPrint2(FWUP_BUFFER_MSG, TempBufferData, *TempBufferData); BadCount++; BlockStored[LowROMBlock] = FALSE; if ((BadCount % 5) == 0) { if (!HackGetYesNo()) { break; } } #else BlockStored[LowROMBlock] = FALSE; break; #endif } // if } // for } // if } // if (BlockErased[LowROMBlock]) // // BlockErased[LowROMBlock] is TRUE iff the block was erased. // BlockStored[LowROMBlock] is TRUE iff the block was erased and stored. // VenPrint("."); RetryIndex++; if (!BlockStored[LowROMBlock] && ((RetryIndex % ROM_BLOCK_RETRY_COUNT) == 0)) { VenPrint1(FWUP_LOW_BLOCK_CANNOT_BE_ERASED_MSG, RetryIndex); VenPrint(FWUP_ERASE_FAILURES_MSG); if (!BlockErased[LowROMBlock]) { VenPrint1("%d", LowROMBlock); } else { VenPrint(FWUP_NONE_MSG); } VenPrint(FWUP_WRITE_FAILURES_MSG); if (!BlockStored[LowROMBlock]) { VenPrint1("%d.", LowROMBlock); } else { VenPrint(FWUP_NONE_MSG); } VenPrint(FWUP_DO_YOU_WISH_TO_KEEP_TRYING_MSG); if (!JnFsProceedWithUpdate()) { return EIO; } } } while (!BlockStored[LowROMBlock]); // // All done!! // return; } BOOLEAN AllBlocksNotDone ( IN PBOOLEAN StatusArray, IN ULONG StartIndex, IN ULONG EndIndex ) /*++ Routine Description: Returns TRUE if any block has not been "done", where "done" depends on what status array is passed in. Arguments: Return Value: --*/ { ULONG X; for (X = StartIndex; X <= EndIndex; X++) { if (StatusArray[X] == FALSE) { return TRUE; } } return FALSE; } #endif // ifdef JENSEN #ifdef MORGAN ARC_STATUS MorganFsStoreIntoROM( IN ULONG MorganFlashRomChipSelect, IN PUCHAR FirmwareStart, IN ULONG FirmwareBufferLength ) /*++ Routine Description: The firmware update has been read into memory and verified to be correct, and the user has told us to go ahead with the update. Operation: Morgan strategy differs from Jensen in that the POST and fail-safe loader code resides in a separate jumper-protected FLASH ROM, and that the FLASH ROM containing the Morgan compressed firmware is a bulk erase part with different erase and program methods than the Jensen FLASH ROM parts. Arguments: FirmwareStart A pointer to the beginning of the ROM binary information. This is byte 0 of the Morgan firmware image. MorganFlashRomChipSelect Selects which Morgan FLASH ROM the NT will be firmware written into. FirmwareBufferLength Length of the Morgan NT firmware image in bytes. Return Value: ESUCCESS if update was successful. Otherwise, an error code. --*/ { ARC_STATUS Status; VenClearScreen(); // // Verify the Morgan FLASH ROM chip select value // if ((MorganFlashRomChipSelect < 1) || (MorganFlashRomChipSelect > 2)) { VenPrint1(FWUP_ROM_CHIP_SELECT_MSG, MorganFlashRomChipSelect); FwWaitForKeypress(); return ENODEV; } if (MorganBlastFlash( MorganFlashRomChipSelect, FirmwareStart, FirmwareBufferLength) == TRUE) { VenPrint(FWUP_ROM_UPDATE_SUCCEEDED_MSG); FwWaitForKeypress(); return ESUCCESS; } else { VenPrint(FWUP_ROM_UPDATE_FAILED_MSG); FwWaitForKeypress(); return ENODEV; } } // MorganFsStoreIntoROM() #endif // ifdef MORGAN ARC_STATUS ReadAndVerifyUpdateFile( IN PCHAR PathName, OUT PULONG BufferAddress, OUT PULONG FirmwareStart, OUT PULONG BufferLength, OUT PULONG LowROMBlock, OUT PULONG HighROMBlock ) /*++ Routine Description: This attempts to load and verify the firmware update file. Arguments: PathName A pointer to string descriptor for the name of the file to load. BufferAddress A pointer to a variable that receives the address of the image base. FirmwareStart A pointer to a variable that receives the address of the start of the firmware. This is the first byte after the null byte that terminates the identifier string. BufferLength A pointer to a variable that receives the length of the image. LowROMBlock A pointer to a variable that receives the low ROM block to be updated. HighROMBlock A pointer to a variable that receives the low ROM block to be updated. Return Value: ESUCCESS is returned if the image file is loaded and verified. Otherwise, an unsuccessful status is returned that describes the reason for failure. Additionally, some detailed error messages may be printed out by this function. With any return, the file will have already been closed. --*/ { ULONG ActualBase; ULONG Count; ULONG FileId; ULONG Index; ULONG PageCount; ARC_STATUS Status; LARGE_INTEGER SeekPosition; FILE_INFORMATION FileInfo; ULONG FileSize; CHAR ChecksumAdjustment[4]; ULONG AccumulatedSum; ULONG AmountOfBinaryData; // // Attempt to open the file. // Status = ArcOpen(PathName, ArcOpenReadOnly, &FileId); if (Status != ESUCCESS) { VenPrint(FWUP_READ_CANT_OPEN_MSG); return Status; } // // Get the file information to figure out how big the file is. // Status = ArcGetFileInformation(FileId, &FileInfo); if (Status != ESUCCESS) { VenPrint(FWUP_READ_CANT_GET_FILE_INFO_MSG); ArcClose(FileId); return Status; } FileSize = FileInfo.EndingAddress.LowPart - FileInfo.StartingAddress.LowPart; if ((FileSize < MINIMUM_UPDATE_FILE_SIZE) || (FileSize > MAXIMUM_UPDATE_FILE_SIZE)) { VenPrint(FWUP_READ_BAD_SIZE_MSG); ArcClose(FileId); return EINVAL; } // // Compute number of pages in the file and read it in. // PageCount = (FileSize + PAGE_SIZE - 1) >> PAGE_SHIFT; Status = JnFsAllocateDescriptor(MemoryFree, PageCount, &ActualBase); if (Status != ESUCCESS) { VenPrint1(FWUP_READ_NOT_ENOUGH_MEMORY, PageCount); ArcClose(FileId); return Status; } // // Compute the byte address of the update file buffer // *BufferAddress = KSEG0_BASE | (ActualBase << PAGE_SHIFT); VenPrint(FWUP_READ_READING_MSG); Status = ArcRead(FileId, (PUCHAR)*BufferAddress, FileSize, &Count); ArcClose(FileId); if (Count != FileSize) { VenPrint2(FWUP_READ_BAD_READ_COUNT_MSG, FileSize, Count); if (Status != ESUCCESS) { return Status; } else { return EIO; } } if (Status != ESUCCESS) { return Status; } // // Verify the file's checksum. // VenPrint(FWUP_READ_VERIFYING_CHECKSUM_MSG); AccumulatedSum = 0; Index = 0; do { ChecksumAdjustment[0] = ChecksumAdjustment[1]; ChecksumAdjustment[1] = ChecksumAdjustment[2]; ChecksumAdjustment[2] = ChecksumAdjustment[3]; ChecksumAdjustment[3] = *(((PUCHAR)*BufferAddress)+Index); AccumulatedSum += *(((PUCHAR)*BufferAddress)+Index); Index++; } while (Index < FileSize); // Filesize is 1-based, Index is 0-based // The last four bytes were really a 32-bit additive-zero checksum, // order of {low byte -- high byte } AccumulatedSum = AccumulatedSum - ChecksumAdjustment[3] - ChecksumAdjustment[2] - ChecksumAdjustment[1] - ChecksumAdjustment[0]; AccumulatedSum = AccumulatedSum + ((ChecksumAdjustment[3] << 24) | (ChecksumAdjustment[2] << 16) | (ChecksumAdjustment[1] << 8) | (ChecksumAdjustment[0])); if (AccumulatedSum != 0) { VenPrint1 (FWUP_READ_BAD_CHECKSUM_MSG, AccumulatedSum); return ENOEXEC; } // // The checksum is good. Find the start of the firmware data. // Index = 0; while ((Index < MAXIMUM_IDENTIFIER_LENGTH) && (*(((PUCHAR)*BufferAddress)+Index) != 0)) { Index++; } if (Index == MAXIMUM_IDENTIFIER_LENGTH) { VenPrint(FWUP_READ_IDENTIFIER_TOO_LONG_MSG); return ENOEXEC; } // // Skip over the starting block byte // *FirmwareStart = (ULONG)(((PUCHAR)*BufferAddress) + Index + 2); // // Now verify legality of the starting block number, and verify that // we have at least that much data in the binary section of the file. // *LowROMBlock = *(((PUCHAR)*BufferAddress) + Index + 1); if (*LowROMBlock > 0xf) { VenPrint1(FWUP_READ_BAD_START_BLOCK_MSG, *LowROMBlock); return ENOEXEC; } AmountOfBinaryData = FileSize - (ULONG)((PUCHAR)*FirmwareStart - (PUCHAR)*BufferAddress) - 4; *BufferLength = AmountOfBinaryData; if ((AmountOfBinaryData % SIXTY_FOUR_KB) != 0) { VenPrint(FWUP_READ_BAD_BINARY_DATA_MSG); return EBADF; } *HighROMBlock = *LowROMBlock + (AmountOfBinaryData / SIXTY_FOUR_KB) - 1; if ((*HighROMBlock < *LowROMBlock) || (*HighROMBlock > 0xf)) { VenPrint2(FWUP_READ_TOO_MUCH_DATA_MSG, *LowROMBlock, *HighROMBlock); return E2BIG; } return ESUCCESS; } ARC_STATUS JnFsAllocateDescriptor( IN ULONG MemoryType, IN ULONG PageCount, OUT PULONG ActualBase ) /*++ Routine Description: This attempts to find a free section of memory of the requested size and type. It does *not* update the memory descriptor list to reflect a successful allocation. Arguments: MemoryType The type of memory requested. PageCount The number of pages requested. ActualBase A pointer to a variable that receives the base page number of the found section. Return Value: ESUCCESS is returned if the memory descriptor is found. ENOMEM if not. --*/ { PMEMORY_DESCRIPTOR MemoryDescriptor; ULONG MemoryBase; ARC_STATUS ReturnStatus = ENOMEM; // // Find a memory descriptor of the right size // MemoryDescriptor = ArcGetMemoryDescriptor(NULL); while (MemoryDescriptor != NULL) { if ((MemoryDescriptor->MemoryType == MemoryType) && (MemoryDescriptor->PageCount >= PageCount)) { MemoryBase = MemoryDescriptor->BasePage; ReturnStatus = ESUCCESS; break; } MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor); } *ActualBase = MemoryBase; return ReturnStatus; } BOOLEAN JnFsProceedWithUpdate ( VOID ) /*++ Routine Description: This function asks the user if she wants to proceed. Arguments: None. Return Value: Returns TRUE if the user typed a Y on the keyboard. FALSE if some other key is typed. --*/ { CHAR Character; ULONG Count; ARC_STATUS Status = EAGAIN; VenPrint(FWUP_PRESS_Y_TO_CONTINUE_MSG); while (Status != ESUCCESS) { Status = ArcRead(ARC_CONSOLE_INPUT, &Character, 1, &Count); } if ((Character == FWUP_LOWER_Y) || (Character == FWUP_UPPER_Y)) { return TRUE; } else { VenPrint(FWUP_UPDATE_ABORTED_MSG); return FALSE; } } VOID JnFsDecodeBadStatus ( IN ARC_STATUS Status ) /*++ Routine Description: This prints out error messages of type ARC_STATUS. ESUCCESS codes cause nothing to be done. Arguments: Status The error code to be decoded. Return Value: None. --*/ { if (Status != ESUCCESS) { if (Status <= EROFS) { VenPrint(FWUP_ERROR_MSG[Status - 1]); } else { VenPrint1(FWUP_ERROR_CODE_MSG, Status); } VenPrint(FWUP_UPDATE_ABORTED_MSG); } return; } ARC_STATUS FwpFindCDROM ( OUT PCHAR PathName ) /*++ Routine Description: This function finds the first CD-ROM in the machine, and returns an ARC pathstring to it. Arguments: PathName A pointer to a buffer area that can receive the CDROM pathname string. Return Value: ESUCCESS if the PathName was loaded. Otherwise, an error code. On an error return, PathName is loaded with "scsi(0)cdrom(4)fdisk(0)". --*/ { PCONFIGURATION_COMPONENT Controller; BOOLEAN VariableFound = FALSE; ULONG Index; for ( Index = 0 ; Index < 8 ; Index++ ) { sprintf(PathName, "scsi(0)cdrom(%d)fdisk(0)", Index); Controller = ArcGetComponent(PathName); if ((Controller != NULL) && (Controller->Type == FloppyDiskPeripheral)) { VariableFound = TRUE; break; } } if (VariableFound) { return (ESUCCESS); } else { sprintf(PathName, "scsi0)cdrom(4)fdisk(0)"); return (EIO); } }