windows-nt/Source/XPSP1/NT/base/boot/detect/i386/hwvbiosc.c

873 lines
22 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}
}
}