/*++ Copyright (c) 1990, 1991 Microsoft Corporation Module Name: hwheap.c Abstract: This module goes through ROM area and tries to pick up all the ROM blocks. Author: Shie-Lin Tzong (shielint) 21-Jan-92 Environment: Real mode. Revision History: --*/ #include "hwdetect.h" #include "hwvbios.h" FPTEMPORARY_ROM_BLOCK BlockHead; FPTEMPORARY_ROM_BLOCK BlockPointer; BOOLEAN AddRomBlock ( ULONG RomAddress, ULONG RomSize ) /*++ Routine Description: This routine adds a ROM/RAM block to our ROM list. Arguments: RomAddress - the starting address of the ROM/RAM block to be added. RomSize - the size of the ROM/RAM block (in byte). Return Value: A value of TRUE is returned if success. Otherwise, a value of FALSE is returned. --*/ { LONG AddSize; ULONG AddAddress; FPTEMPORARY_ROM_BLOCK pCurrentBlock, pNextBlock; ULONG CurrentBlock, NextBlock, AddBlock; ULONG EndAddBlock, EndCurrentBlock, EndNextBlock; BOOLEAN fOverlap=FALSE; pCurrentBlock = NULL; pNextBlock = NULL; AddSize = RomSize; AddAddress = RomAddress; AddBlock = RomAddress; // // If there are other blocks, make sure there is no overlap with them // if (BlockHead) { pCurrentBlock = BlockHead; pNextBlock = pCurrentBlock->Next; CurrentBlock = pCurrentBlock->RomBlock.Address; EndCurrentBlock = CurrentBlock + pCurrentBlock->RomBlock.Size; EndAddBlock = RomAddress + RomSize; while (pCurrentBlock!=NULL) { // // calculate location of next block (if it's there) // if(pNextBlock) { NextBlock = pNextBlock->RomBlock.Address; EndNextBlock = NextBlock + pNextBlock->RomBlock.Size; } // // if overlapping with current block, then stop and // resolve overlap // if((RomAddress < EndCurrentBlock)&& (RomAddress >= CurrentBlock)){ fOverlap = TRUE; break; } // // if add block is lower than the current one, // or there is not a next block, then no need to search further // if((EndAddBlock <= CurrentBlock) || (pNextBlock == NULL)) { break; } // // if block is lower than next one, but greater than current // one, we have found the right area // if ((EndAddBlock <= NextBlock) && (AddBlock >= EndCurrentBlock)) { break; } // // if conflicting with next block, stop searching and // resolve conflict after this loop // if((EndAddBlock > NextBlock) && (EndAddBlock <= EndNextBlock)) { fOverlap = TRUE; break; } pCurrentBlock = pNextBlock; pNextBlock = pCurrentBlock->Next; CurrentBlock = NextBlock; EndCurrentBlock = EndNextBlock; } } // // if we have reached this point, there may be a conflict // with the current block. // if(fOverlap) { if(AddBlock < EndCurrentBlock) { AddAddress = EndCurrentBlock; AddSize = EndAddBlock - EndCurrentBlock; if(AddSize <= 0) { return TRUE; } } if((pNextBlock != NULL) && (EndAddBlock > NextBlock)) { AddSize = NextBlock - AddBlock; if(AddSize <= 0) { return TRUE; } } } BlockPointer->RomBlock.Address = AddAddress; BlockPointer->RomBlock.Size = AddSize; // // Put it on the list. // if it belongs on top, put it there // if ((pCurrentBlock == NULL)|| ((pCurrentBlock == BlockHead) && (CurrentBlock > AddBlock))) { BlockPointer->Next = pCurrentBlock; BlockHead = BlockPointer; } else { // // else add to middle or bottom depending on NextBlock // BlockPointer->Next = pNextBlock; pCurrentBlock->Next = BlockPointer; } BlockPointer++; // Note that this works because // we know the offset part of // the addr is always < 64k. return TRUE; } BOOLEAN ScanRomBlocks( VOID ) /*++ Routine Description: This routine scans the ROM IO area and checks for 55AA at every 512 bytes for valid ROM blocks. NOTES: ------------- | | | | ------------------100000 ^ | | | | | | -------------f0000 (ROMBIOS_START) --- | | | ^ | | | | EXTROM_LEN -------------e0000 (PS2BIOS_START) --- | | | | ^ Search | | | | Search | Range | | -------------d0000 Range | on AT | | | | on PS/2| | V | | V V ------------------c0000 (EXTROM_START) --- --- ON AT: Scan through EXTROM_START-effff for ROM Blocks ON PS2 Scan through EXTROM_START-dffff for ROM Blocks Arguments: None. Return Value: None. --*/ { ULONG BlockSize; BOOLEAN Success = TRUE; FPUCHAR Current; ULONG RomAddr, RomEnd; // // As per the machine type restrict the search range // MAKE_FP(Current, EXTROM_START); RomAddr = EXTROM_START; if ((HwBusType == MACHINE_TYPE_MCA) || (BiosSystemEnvironment.Model == PS2_L40) || (BiosSystemEnvironment.Model == PS1_386) || (BiosSystemEnvironment.Model == PS2_AT)) { RomEnd = PS2BIOS_START; } else { RomEnd = ROMBIOS_START; } while (RomAddr < RomEnd) { if (((FPROM_HEADER)Current)->Signature == ROM_HEADER_SIGNATURE) { BlockSize = (ULONG)((FPROM_HEADER)Current)->NumberBlocks * BLOCKSIZE; if ((RomAddr + BlockSize) > RomEnd) { BlockSize = RomEnd - RomAddr; } // // V7 VRAM card does not correctly report its BlockSize. Since // this is a very popular video card, we provide a workaround // for it. // if ((RomAddr == 0xC0000) && (BlockSize < 0x8000)) { BlockSize = 0x8000; } if (BlockSize != 0) { if (!AddRomBlock(RomAddr, BlockSize)) { Success = FALSE; break; } RomAddr += BlockSize; RomAddr = ALIGN_UP(RomAddr, ROM_HEADER_INCREMENT); MAKE_FP(Current, RomAddr); continue; } } RomAddr += ROM_HEADER_INCREMENT; MAKE_FP(Current, RomAddr); } // // Last but not least, add the system ROM to the list // if (Success) { RomAddr = ROMBIOS_START; BlockSize = ROMBIOS_LEN; if ((HwBusType == MACHINE_TYPE_MCA) || (BiosSystemEnvironment.Model == PS2_L40) || (BiosSystemEnvironment.Model == PS1_386) || (BiosSystemEnvironment.Model == PS2_AT)) { RomAddr = PS2BIOS_START; BlockSize = PS2BIOS_LEN; } if (!AddRomBlock(RomAddr, BlockSize)) { Success = FALSE; } } return Success; } FPTEMPORARY_ROM_BLOCK MatchRomBlock ( ULONG PhysicalAddr ) /*++ Routine Description: This routine finds the ROM block which the 'PhysicalAddr' is in. Arguments: PhysicalAddr - the physical address ... Return Value: A pointer to the detected ROM block. --*/ { FPTEMPORARY_ROM_BLOCK CurrentBlock; ROM_BLOCK RomBlock; CurrentBlock = BlockHead; while (CurrentBlock) { RomBlock = CurrentBlock->RomBlock; if (RomBlock.Address <= PhysicalAddr && RomBlock.Address + RomBlock.Size > PhysicalAddr) { break; } else { CurrentBlock = CurrentBlock->Next; } } return(CurrentBlock); } BOOLEAN IsSameRomBlock ( FPTEMPORARY_ROM_BLOCK Source, FPTEMPORARY_ROM_BLOCK Destination ) /*++ Routine Description: This routine checks if the passed in ROM blocks contain the same information. This ususally happens when the two ROM blocks are for video ROM and shadowed video ROM. Arguments: Source - the source ROM block. Destination - the ROM block to compare with. Return Value: BOOLEAN TRUE if the two ROM blocks are the same else FALSE is returned. --*/ { if (Source == NULL || Destination == NULL) { return(FALSE); } // // First make sure their sizes are the same. // if (Source->RomBlock.Size == Destination->RomBlock.Size) { if (!HwRomCompare(Source->RomBlock.Address, Destination->RomBlock.Address, Source->RomBlock.Size)){ return(TRUE); } } return(FALSE); } VOID CheckVideoRom ( VOID ) /*++ Routine Description: This routine checks if the int 10h video handler is in the video ROM block detected by us. If not, the video ROM must have been remapped/shadowed to other area (usually 0xE0000.) NOTE: In this function, I commented out the code which removes the Video ROM block if it has been shadowed. I found out machine POST code does not modify ALL the VIDEO ROM related pointers. Arguments: None. Return Value: None. --*/ { ULONG Vector, Handler, VectorAddr = 0x10 * sizeof(ULONG); FPULONG pVectorAddr; FPTEMPORARY_ROM_BLOCK RomBlock, VideoRomBlock; ULONG Size; MAKE_FP(pVectorAddr, VectorAddr); Vector = *pVectorAddr; Handler = ((Vector >> 16) << 4) + (Vector & 0xffff); RomBlock = MatchRomBlock(Handler); // // Check if the int 10h handler falls in one of our ROM blocks. // if (RomBlock) { if (RomBlock->RomBlock.Address >= 0xC0000 && RomBlock->RomBlock.Address < 0xC8000) { // // if int 10h handler is in the standard video ROM area, we simply // return. Either the video ROM is not shadowed or it // is a in-place shadow. // return; } else { // // The ROM block associated with the int 10h handler is not in // standard video bios ROM area. It must have been mapped to // the current location. We now need to make sure we have the // ROM block which contains the 40:a8 VGA parameter. // VectorAddr = VGA_PARAMETER_POINTER; MAKE_FP(pVectorAddr, VectorAddr); Vector = *pVectorAddr; Handler = ((Vector >> 16) << 4) + (Vector & 0xffff); VideoRomBlock = MatchRomBlock(Handler); if (VideoRomBlock == NULL) { // // We did not find the Video ROM associated with the // VGA parameters. Try detect it. // // // In the following memory comparison, we skip the first 16 bytes. // Because most likely the reason we did not find the standard // Video ROM is because the signature word is missing. // Handler = (Handler & 0xF0000) + (RomBlock->RomBlock.Address & 0xFFFF); if (!HwRomCompare(RomBlock->RomBlock.Address + 0x10, Handler + 0x10, RomBlock->RomBlock.Size - 0x10)) { // // Note: The old code looked like this for many years: // /* if ((Handler & 0xFFFF == 0) && (RomBlock->RomBlock.Size < 0x8000)){ Size = 0x8000; } else { Size = RomBlock->RomBlock.Size; } */ // // But (Handler & 0xFFFF == 0) is always false. So // Size always equals RomBlock->RomBlock.Size. Rather than // fix the comparison, which might cause machines to break, // I'm going to assume that it's fine to just make the code // do what it's always done. - JakeO 8/9/00 // Size = RomBlock->RomBlock.Size; AddRomBlock(Handler, Size); } } } } else { // // There is no ROM block associated with the int 10h handler. // We can find the shadowed video ROM block if: // We detected the original video ROM in 0xC0000 - 0xC8000 range // VideoRomBlock = MatchRomBlock((Handler & 0xFFFF) + 0xC0000); if (VideoRomBlock != NULL) { // // In the following memory comparison, we skip the first 16 bytes. // Because most likely the reason we did not find the shadow rom // is the signature word is missing. // if (!HwRomCompare(VideoRomBlock->RomBlock.Address + 0x10, (Handler & 0xF0000) + (VideoRomBlock->RomBlock.Address & 0xFFFF) + 0x10, VideoRomBlock->RomBlock.Size - 0x10)) { AddRomBlock((VideoRomBlock->RomBlock.Address & 0xFFFF) + (Handler & 0xF0000), VideoRomBlock->RomBlock.Size); } } } } VOID GetRomBlocks( FPUCHAR ReservedBuffer, PUSHORT Size ) /*++ Routine Description: This routine scans the ROM IO area and collects all the ROM blocks. Arguments: ReservedBuffer - Supplies a far pointer to the buffer. Size - Supplies a near pointer to a variable to receive the size of the ROM block. Return Value: None. --*/ { FPTEMPORARY_ROM_BLOCK Source; ULONG StartAddr, EndAddr; FPUSHORT TestAddr; FPROM_BLOCK Destination; USHORT BufferSize; ULONG EBiosAddress = 0, EBiosLength = 0; ULONG far *EBiosInformation = (ULONG far *) ((DOS_BEGIN_SEGMENT << 4) + EBIOS_INFO_OFFSET); // // First we reserve the max space needed and build our temporary rom // block list in the heap space below the space reservedand. After // the temporary list is built, we then copy it to the caller supplied // reserved space. // BlockPointer = (FPTEMPORARY_ROM_BLOCK)HwAllocateHeap(0, FALSE); BlockHead = NULL; *Size = 0; GetBiosSystemEnvironment((PUCHAR)&BiosSystemEnvironment); if (BiosSystemEnvironment.ConfigurationFlags & 0x4) { // // If extened BIOS data area is allocated, we will find out its // location and size and save in ROM blocks. // _asm { push es mov ah, 0xC1 int 15h jc short Exit cmp ah, 0x86 je short Exit mov bx, 0 mov dx, 0 mov ax, 0 mov al, es:[bx] shl ax, 10 mov word ptr EBiosLength, ax mov ax, es mov dx, es shl ax, 4 shr dx, 12 mov word ptr EBiosAddress, ax mov word ptr EBiosAddress + 2, dx Exit: pop es } } // // Save the Extended BIOS data area address and size at 700:40 // if (EBiosLength) { *EBiosInformation++ = EBiosAddress; *EBiosInformation = EBiosLength; } else { *EBiosInformation++ = 0L; *EBiosInformation = 0L; } if (!ScanRomBlocks()) { return; } // // On some machines, when they shadow video ROM from 0xC0000 to // 0xE0000, they copy code only (no signature.) So, we need // special code to work around the problem. // CheckVideoRom(); // // Now do our special hack for IBM. On SOME IBM PCs, they use // E0000-FFFFF for system BIOS (even on non PS/2 machines.) Since // system BIOS has no ROM header, it is very hard to know the starting // address of system ROM. So we: // // 1. Make sure there is no ROM block in E0000-EFFFF area. // 2. and E0000-EFFFF contains valid data. // // If both 1 and 2 are true, we assume E0000-EFFFF is part of system // ROM. // Source = BlockHead; while (Source) { StartAddr = Source->RomBlock.Address; EndAddr = StartAddr + Source->RomBlock.Size - 1; if ((StartAddr < 0xE0000 && EndAddr < 0xE0000) || (StartAddr >= 0xF0000)) { Source = Source->Next; } else { break; } } if (Source == NULL) { for (StartAddr = 0xE0000; StartAddr < 0xF0000; StartAddr += 0x800) { MAKE_FP(TestAddr, StartAddr); if (*TestAddr != 0xffff) { AddRomBlock(0xE0000, 0x10000); break; } } } // // Now copy the rom block list to our reserved space and release // the extra space we reserved. // Source = BlockHead; Destination = (FPROM_BLOCK)ReservedBuffer; BufferSize = 0; while (Source) { *Destination = *((FPROM_BLOCK)&Source->RomBlock); BufferSize += sizeof(ROM_BLOCK); Source = Source->Next; Destination++; } *Size = BufferSize; } VOID HwGetBiosDate( ULONG StartingAddress, USHORT Length, PUSHORT Year, PUSHORT Month, PUSHORT Day ) /*++ Routine Description: Scans the specified area for the most recent date of the form xx/xx/xx. Arguments: StartingAddress - First address to scan Length - Length of area to scan Return Value: Year - If non-zero, the year of the date (1991, 1992, ...) Month - If non-zero, then month of the date found Day - If non-zero, the day of the date found --*/ { FPUCHAR fp, date; USHORT y, m, d; UCHAR c; ULONG i, temp; // // Zero return values // *Year = 0; *Month = 0; *Day = 0; // // Search for date with the format MM/DD/YY or M1M1M2M2//D1D1D2D2//Y1Y1Y2Y2 // MAKE_FP(fp, StartingAddress); // initialize fp pointer while (Length > 8) { c = fp[7]; if ((c < '0' || c > '9') && (c != '/' && c != '-')) { // these 8 bytes are not a date, next location fp += 8; Length -= 8; continue; } date = fp; // check for date at this pointer fp += 1; // skip to next byte Length -= 1; // // Check for date of the form MM/DD/YY // y = 0; if (date[0] >= '0' && date[0] <= '9' && date[1] >= '0' && date[1] <= '9' && (date[2] == '/' || date[2] == '-') && date[3] >= '0' && date[3] <= '9' && date[4] >= '0' && date[4] <= '9' && (date[5] == '/' || date[5] == '-') && date[6] >= '0' && date[6] <= '9' && date[7] >= '0' && date[7] <= '9' ) { // // A valid looking date field at date, crack it // y = (date[6] - '0') * 10 + date[7] - '0' + 1900; m = (date[0] - '0') * 10 + date[1] - '0'; d = (date[3] - '0') * 10 + date[4] - '0'; } // // Check for date of the form M1M1M2M2//D1D1D2D2//Y1Y1Y2Y2 // if (Length >= 15 && date[ 0] >= '0' && date[ 0] <= '9' && date[ 0] == date[ 1] && date[ 2] >= '0' && date[ 2] <= '9' && date[ 2] == date[ 3] && (date[ 4] == '/' || date[ 4] == '-') && date[ 4] == date[ 5] && date[ 6] >= '0' && date[ 6] <= '9' && date[ 6] == date[ 7] && date[ 8] >= '0' && date[ 8] <= '9' && date[ 8] == date[ 9] && (date[10] == '/' || date[10] == '-') && date[10] == date[11] && date[12] >= '0' && date[12] <= '9' && date[12] == date[13] && date[14] >= '0' && date[14] <= '9' && date[14] == date[15]) { // // A valid looking date field at date, crack it // y = (date[12] - '0') * 10 + date[14] - '0' + 1900; m = (date[ 0] - '0') * 10 + date[ 2] - '0'; d = (date[ 6] - '0') * 10 + date[ 8] - '0'; } if (y != 0) { if (m < 1 || m > 12 || d < 1 || d > 31) { y = 0; // bad field in date, skip it } else { if (y < 1980) { // // Roll to next century. // y += 100; } } } // // Check for date of the form 19xx or 20xx // // First, check the 5th character is not a digit. // #define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9')) if (!IS_DIGIT(date[4])) { for (i = 0, temp = 0; i < 4; i++) { if (!IS_DIGIT(date[i])) { temp = 0; break; } temp = (temp * 10) + date[i] - '0'; } if ((temp >= 1980) && (temp < 2599)) { // // Looks like a reasonable date, use it. // y = (USHORT)temp; m = 0; d = 0; } } if (!y) { // not a date - skip it continue; } if ((y > *Year) || (y == *Year && m > *Month) || (y == *Year && m == *Month && d > *Day) ) { // // This date is more recent // *Year = y; *Month = m; *Day = d; } } }