/*++ Copyright (c) 1989 Microsoft Corporation Module Name: abiosc.c Abstract: This module implements ABIOS support C routines for i386 NT. Author: Shie-Lin Tzong (shielint) 20-May-1991 Environment: Boot loader privileged, FLAT mode. Revision History: --*/ #include "ki.h" #pragma hdrstop #include "abios.h" extern PKCOMMON_DATA_AREA KiCommonDataArea; extern BOOLEAN KiAbiosPresent; extern ULONG KiAbiosGetGdt ( VOID ); // // The reason of having these variables defined in here is to isolate // ABIOS from current system. // // // KiNumberFreeSelectors defines the number of available selectors for // ABIOS specific drivers. This number should be the same accross all // the processors. // static USHORT KiNumberFreeSelectors = 0; // // KiFreeGdtListHead points to the head of free GDT list on the processor 0. // static PKFREE_GDT_ENTRY KiFreeGdtListHead = 0L; // // Logica Id Table to control the ownership of logical Id. // PKLID_TABLE_ENTRY KiLogicalIdTable; // // KiAbiosGdt[] defines the Starting address of GDT for each processor. // ULONG KiAbiosGdt[MAXIMUM_PROCESSORS]; // // SpinLock for accessing GDTs // KSPIN_LOCK KiAbiosGdtLock; // // Spinlock for accessing Logical Id Table // KSPIN_LOCK KiAbiosLidTableLock; // // KiStack16GdtEntry defines the address of the gdt entry for 16 bit stack. // ULONG KiStack16GdtEntry; VOID KiInitializeAbiosGdtEntry ( OUT PKGDTENTRY GdtEntry, IN ULONG Base, IN ULONG Limit, IN USHORT Type ) /*++ Routine Description: This function initializes a GDT entry for abios specific code. Base, Limit, and Type (code, data) are set according to parameters. All other fields of the entry are set to match standard system values. N.B. The BIG and GRANULARITY are always set to 0. Arguments: GdtEntry - GDT descriptor to be filled in. Base - Linear address of the first byte mapped by the selector. Limit - Size of the selector in BYTE. Type - Code or Data. All code selectors are marked readable, all data selectors are marked writeable. Return Value: Pointer to the GDT entry. --*/ { GdtEntry->LimitLow = (USHORT)(Limit & 0xffff); GdtEntry->BaseLow = (USHORT)(Base & 0xffff); GdtEntry->HighWord.Bytes.BaseMid = (UCHAR)((Base & 0xff0000) >> 16); GdtEntry->HighWord.Bits.Type = Type; GdtEntry->HighWord.Bits.Dpl = 0; GdtEntry->HighWord.Bits.Pres = 1; GdtEntry->HighWord.Bits.LimitHi = (Limit & 0xf0000) >> 16; GdtEntry->HighWord.Bits.Sys = 0; GdtEntry->HighWord.Bits.Reserved_0 = 0; GdtEntry->HighWord.Bits.Default_Big = 0; GdtEntry->HighWord.Bits.Granularity = 0; GdtEntry->HighWord.Bytes.BaseHi = (UCHAR)((Base & 0xff000000) >> 24); } ULONG KiI386SelectorBase ( IN USHORT Selector ) /*++ Routine Description: This function returns the base address of the specified GDT selector. Arguments: Selector - Supplies the desired selector. Return Value: SelectorBase - Return the base address of the specified selector; (return -1L if invalid selector) --*/ { PKGDTENTRY GdtEntry; GdtEntry = (PKGDTENTRY)(KiAbiosGetGdt() + Selector); if (GdtEntry->HighWord.Bits.Pres) { return ((ULONG)GdtEntry->BaseLow | (ULONG)GdtEntry->HighWord.Bytes.BaseMid << 16 | (ULONG)GdtEntry->HighWord.Bytes.BaseHi << 24); } else { return (ULONG)(-1L); } } NTSTATUS KeI386GetLid( IN USHORT DeviceId, IN USHORT RelativeLid, IN BOOLEAN SharedLid, IN PDRIVER_OBJECT DriverObject, OUT PUSHORT LogicalId ) /*++ Routine Description: This function searches Device Blocks and Common Data Area for the Logical Id matching the specified Device Id. N.B. (WARNING shielint) To speed the search, this routine ASSUMES that the LIDs with the same Device ID always appear consecutively in the Common Data Area. IBM ABIOS doc does not explicitly specify this. But from the way ABIOS initializes Device Block and Function Transfer Table, I think the assumption is true. Arguments: DeviceId - Desired Device Id. RelativeLid - Specifies the Nth logical Id for this device Id. A value of 0 indicates the first available Lid. SharedLid - A boolean value indicates if it is a shared or exclusively owned logical Id. DriverObject - Supplies a 32-bit flat pointer of the requesting device driver's driver object. The DriverObject is used to establish the ownership of the desired LID. LogicalId - A pointer to a variable which will receive the Lid. Return Value: STATUS_SUCCESS - If the requested LID is available. STATUS_ABIOS_NOT_PRESENT - If there is no ABIOS support in the system. STATUS_ABIOS_LID_NOT_EXIST - If the specified LID does not exist. STATUS_ABIOS_LID_ALREADY_OWNED - If the caller requests an exclusively owned LID. --*/ { PKDB_FTT_SECTION CdaPointer; PKDEVICE_BLOCK DeviceBlock; USHORT Lid, RelativeLidCount = 1; ULONG Owner; USHORT Increment; KIRQL OldIrql; NTSTATUS Status; if (!KiAbiosPresent) { return STATUS_ABIOS_NOT_PRESENT; } if (SharedLid) { Owner = LID_NO_SPECIFIC_OWNER; Increment = 1; } else { Owner = (ULONG)DriverObject; Increment = 0; } // // If the Logical Id Table hasn't been created yet, create it now. // if (KiLogicalIdTable==NULL) { KiLogicalIdTable = ExAllocatePool(NonPagedPool, NUMBER_LID_TABLE_ENTRIES * sizeof(KLID_TABLE_ENTRY)); if (KiLogicalIdTable == NULL) { return(STATUS_NO_MEMORY); } RtlZeroMemory(KiLogicalIdTable, NUMBER_LID_TABLE_ENTRIES*sizeof(KLID_TABLE_ENTRY)); } // // For each Lid defined in Common Data Area, we check if it has non // empty device block and function transfer table. If yes, we proceed // to check the device id. Otherwise, we skip the Lid. // CdaPointer = (PKDB_FTT_SECTION)KiCommonDataArea + 2; Status = STATUS_ABIOS_LID_NOT_EXIST; ExAcquireSpinLock(&KiAbiosLidTableLock, &OldIrql); for (Lid = 2; Lid < KiCommonDataArea->NumberLids; Lid++) { if (CdaPointer->DeviceBlock.Selector != 0 && CdaPointer->FunctionTransferTable.Selector != 0) { DeviceBlock = (PKDEVICE_BLOCK)(KiI386SelectorBase( CdaPointer->DeviceBlock.Selector) + (CdaPointer->DeviceBlock.Offset)); if (DeviceBlock->DeviceId == DeviceId) { if (RelativeLid == RelativeLidCount || RelativeLid == 0) { if (KiLogicalIdTable[Lid].Owner == 0L) { KiLogicalIdTable[Lid].Owner = Owner; KiLogicalIdTable[Lid].OwnerCount += Increment; *LogicalId = Lid; Status = STATUS_SUCCESS; } else if (KiLogicalIdTable[Lid].Owner == LID_NO_SPECIFIC_OWNER) { if (SharedLid) { *LogicalId = Lid; KiLogicalIdTable[Lid].OwnerCount += Increment; Status = STATUS_SUCCESS; } else { Status = STATUS_ABIOS_LID_ALREADY_OWNED; } } else if (KiLogicalIdTable[Lid].Owner == (ULONG)DriverObject) { *LogicalId = Lid; Status = STATUS_SUCCESS; } else if (RelativeLid != 0) { Status = STATUS_ABIOS_LID_ALREADY_OWNED; } break; } else { RelativeLidCount++; } } } CdaPointer++; } ExReleaseSpinLock(&KiAbiosLidTableLock, OldIrql); return Status; } NTSTATUS KeI386ReleaseLid( IN USHORT LogicalId, IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This function releases a logical Id. This routine is called at ABIOS device driver destallation or termination. Arguments: LogicalId - Logical Id to be released. DriverObject - Supplies a 32-bit flat pointer of the requesting device driver's driver object. The DriverObject is used to check the ownership of the specified LID. Return Value: STATUS_SUCCESS - If the requested LID is released. STATUS_ABIOS_NOT_PRESENT - If there is no ABIOS support in the system. STATUS_ABIOS_NOT_LID_OWNER - If the caller does not own the LID. --*/ { KIRQL OldIrql; NTSTATUS Status; if (!KiAbiosPresent) { return STATUS_ABIOS_NOT_PRESENT; } ExAcquireSpinLock(&KiAbiosLidTableLock, &OldIrql); if (KiLogicalIdTable[LogicalId].Owner == (ULONG)DriverObject) { KiLogicalIdTable[LogicalId].Owner = 0L; Status = STATUS_SUCCESS; } else if (KiLogicalIdTable[LogicalId].Owner == LID_NO_SPECIFIC_OWNER) { KiLogicalIdTable[LogicalId].OwnerCount--; if (KiLogicalIdTable[LogicalId].OwnerCount == 0L) { KiLogicalIdTable[LogicalId].Owner = 0L; } Status = STATUS_SUCCESS; } else { Status = STATUS_ABIOS_NOT_LID_OWNER; } ExReleaseSpinLock(&KiAbiosLidTableLock, OldIrql); return Status; } NTSTATUS KeI386AbiosCall( IN USHORT LogicalId, IN PDRIVER_OBJECT DriverObject, IN PUCHAR RequestBlock, IN USHORT EntryPoint ) /*++ Routine Description: This function calls an ABIOS service routine on behave of device driver using Operating System Transfer Convension. Arguments: LogicalId - Logical Id for the call. DriverObject - Supplies a 32-bit flat pointer of the requesting device driver's driver object. The DriverObject is used to verify the ownership of the desired LID. RequestBlock - A 16:16 (selector:offset) pointer to the request block. EntryPoint - Specifies which ABIOS entry point: 0 - Start Routine 1 - Interrupt Routine 2 - Timeout Routine Return Value: STATUS_SUCCESS - If no error. STATUS_ABIOS_NOT_PRESENT - If there is no ABIOS support in the system. STATUS_ABIOS_INVALID_COMMAND - if the specified entry point is not supported. STATUS_ABIOS_INVALID_LID - If the Lid specified is invalid. STATUS_ABIOS_NOT_LID_OWNER - If the caller does not own this Lid. (Note that the request specific ABIOS returned code is in RequestBlock.) --*/ { KABIOS_POINTER FuncTransferTable; KABIOS_POINTER DeviceBlock; KABIOS_POINTER AbiosFunction; PKFUNCTION_TRANSFER_TABLE FttPointer; if (!KiAbiosPresent) { return STATUS_ABIOS_NOT_PRESENT; } if (LogicalId >= KiCommonDataArea->NumberLids) { return STATUS_ABIOS_INVALID_LID; } else if (KiLogicalIdTable[LogicalId].Owner != (ULONG)DriverObject && KiLogicalIdTable[LogicalId].Owner != LID_NO_SPECIFIC_OWNER) { return STATUS_ABIOS_NOT_LID_OWNER; } else if (EntryPoint > 2) { return STATUS_ABIOS_INVALID_COMMAND; } FuncTransferTable = ((PKDB_FTT_SECTION)KiCommonDataArea + LogicalId)-> FunctionTransferTable; DeviceBlock = ((PKDB_FTT_SECTION)KiCommonDataArea + LogicalId)->DeviceBlock; FttPointer = (PKFUNCTION_TRANSFER_TABLE)(KiI386SelectorBase(FuncTransferTable.Selector) + (ULONG)FuncTransferTable.Offset); AbiosFunction = FttPointer->CommonRoutine[EntryPoint]; KiI386CallAbios(AbiosFunction, DeviceBlock, FuncTransferTable, *(PKABIOS_POINTER)&RequestBlock ); return STATUS_SUCCESS; } NTSTATUS KeI386AllocateGdtSelectors( OUT PUSHORT SelectorArray, IN USHORT NumberOfSelectors ) /*++ Routine Description: This function allocates a set of GDT selectors for a device driver to use. Usually this allocation is performed at device driver initialization time to reserve the selectors for later use. Arguments: SelectorArray - Supplies a pointer to an array of USHORT to be filled in with the GDT selectors allocated. NumberOfSelectors - Specifies the number of selectors to be allocated. Return Value: STATUS_SUCCESS - If the requested selectors are allocated. STATUS_ABIOS_SELECTOR_NOT_AVAILABLE - if systen can not allocate the number of selectors requested. --*/ { PKFREE_GDT_ENTRY GdtEntry; KIRQL OldIrql; if (KiNumberFreeSelectors >= NumberOfSelectors) { ExAcquireSpinLock(&KiAbiosGdtLock, &OldIrql); // // The Free Gdt link list is maintained on Processor 0's GDT ONLY. // Because the 'selector' is an offset to the beginning of GDT and // it should be the same accross all the processors. // KiNumberFreeSelectors -= NumberOfSelectors; GdtEntry = KiFreeGdtListHead; while (NumberOfSelectors != 0) { *SelectorArray++ = (USHORT)((ULONG)GdtEntry - KiAbiosGdt[0]); GdtEntry = GdtEntry->Flink; NumberOfSelectors--; } KiFreeGdtListHead = GdtEntry; ExReleaseSpinLock(&KiAbiosGdtLock, OldIrql); return STATUS_SUCCESS; } else { return STATUS_ABIOS_SELECTOR_NOT_AVAILABLE; } } NTSTATUS KeI386ReleaseGdtSelectors( OUT PUSHORT SelectorArray, IN USHORT NumberOfSelectors ) /*++ Routine Description: This function releases a set of GDT selectors for a device driver. Usually this function is called at device driver termination or deinstallation time. Arguments: SelectorArray - Supplies a pointer to an array of USHORT selectors to be freed. NumberOfSelectors - Specifies the number of selectors to be released. Return Value: STATUS_SUCCESS - If the requested LID is released. --*/ { PKFREE_GDT_ENTRY GdtEntry; KIRQL OldIrql; ULONG Gdt; ExAcquireSpinLock(&KiAbiosGdtLock, &OldIrql); // // The Free Gdt link list is maintained on Processor 0's GDT ONLY. // Because the 'selector' is an offset to the beginning of GDT and // it should be the same accross all the processors. // KiNumberFreeSelectors += NumberOfSelectors; Gdt = KiAbiosGdt[0]; while (NumberOfSelectors != 0) { GdtEntry = (PKFREE_GDT_ENTRY)(Gdt + *SelectorArray++); GdtEntry->Flink = KiFreeGdtListHead; KiFreeGdtListHead = GdtEntry; NumberOfSelectors--; } ExReleaseSpinLock(&KiAbiosGdtLock, OldIrql); return STATUS_SUCCESS; } NTSTATUS KeI386FlatToGdtSelector( IN ULONG SelectorBase, IN USHORT Length, IN USHORT Selector ) /*++ Routine Description: This function converts a 32-bit flat address to a GDT selector-offset pair. The segment set up is always 16-bit ring 0 data segment. Arguments: SelectorBase - Supplies 32 bit flat address to be set as the base address of the desired selector. Length - Supplies the Length of the segment. The Length is a 16 bit value and zero means 64KB. Selector - Supplies the selector to be set up. Return Value: STATUS_SUCCESS - If the requested LID is released. STATUS_ABIOS_NOT_PRESENT - If there is no ABIOS support in the system. STATUS_ABIOS_INVALID_SELECTOR - If the selector supplied is invalid. --*/ { PKGDTENTRY GdtEntry, GdtEntry1; KIRQL OldIrql; ULONG i; if (!KiAbiosPresent) { return STATUS_ABIOS_NOT_PRESENT; } if (Selector < RESERVED_GDT_ENTRIES * sizeof(KGDTENTRY)) { return STATUS_ABIOS_INVALID_SELECTOR; } else { ExAcquireSpinLock(&KiAbiosGdtLock, &OldIrql); GdtEntry = (PKGDTENTRY)(KiAbiosGdt[0] + Selector); GdtEntry->LimitLow = (USHORT)(Length - 1); GdtEntry->BaseLow = LOWWORD(SelectorBase); GdtEntry->HighWord.Bytes.BaseMid = LOWBYTE(HIGHWORD(SelectorBase)); GdtEntry->HighWord.Bytes.BaseHi = HIGHBYTE(HIGHWORD(SelectorBase)); GdtEntry->HighWord.Bits.Pres = 1; GdtEntry->HighWord.Bits.Type = TYPE_DATA; GdtEntry->HighWord.Bits.Dpl = DPL_SYSTEM; for (i = 1; i < (ULONG)KeNumberProcessors; i++) { GdtEntry1 = (PKGDTENTRY)(KiAbiosGdt[i] + Selector); *GdtEntry1 = *GdtEntry; } ExReleaseSpinLock(&KiAbiosGdtLock, OldIrql); return STATUS_SUCCESS; } } VOID Ki386InitializeGdtFreeList ( PKFREE_GDT_ENTRY EndOfGdt ) /*++ Routine Description: This function initializes gdt free list by linking all the unused gdt entries to a free list. Arguments: EndOfGdt - Supplies the ending address of desired GDT. Return Value: None. --*/ { PKFREE_GDT_ENTRY GdtEntry; GdtEntry = EndOfGdt - 1; KiFreeGdtListHead = (PKFREE_GDT_ENTRY)0; while (GdtEntry != (PKFREE_GDT_ENTRY)KiAbiosGetGdt() + RESERVED_GDT_ENTRIES - 1) { if (GdtEntry->Present == 0) { GdtEntry->Flink = KiFreeGdtListHead; KiFreeGdtListHead = GdtEntry; KiNumberFreeSelectors++; } GdtEntry--; } } VOID KiInitializeAbios ( IN UCHAR Processor ) /*++ Routine Description: This function initializes gdt free list and sets up selector for KiI386AbiosCall (16-bit code). Arguments: Processor - the processor who performs the initialization. Return Value: None. --*/ { ULONG GdtLength; PKGDTENTRY AliasGdtSelectorEntry; PKFREE_GDT_ENTRY EndOfGdt; // // First check if abios is recognized by osloader. // KiCommonDataArea = KeLoaderBlock->u.I386.CommonDataArea; // // NOTE For now we want to disable ABIOS support on MP. // if (KiCommonDataArea == NULL || Processor != 0) { KiAbiosPresent = FALSE; } else { KiAbiosPresent = TRUE; } // // Initialize the spinlocks for accessing GDTs and Lid Table. // KeInitializeSpinLock( &KiAbiosGdtLock ); KeInitializeSpinLock( &KiAbiosLidTableLock ); // // Determine the starting and ending addresses of GDT. // KiAbiosGdt[Processor] = KiAbiosGetGdt(); AliasGdtSelectorEntry = (PKGDTENTRY)(KiAbiosGetGdt() + KGDT_GDT_ALIAS); GdtLength = 1 + (ULONG)(AliasGdtSelectorEntry->LimitLow) + (ULONG)(AliasGdtSelectorEntry->HighWord.Bits.LimitHi << 16); EndOfGdt = (PKFREE_GDT_ENTRY)(KiAbiosGetGdt() + GdtLength); // // Prepare selector for 16 bit stack segment // KiStack16GdtEntry = KiAbiosGetGdt() + KGDT_STACK16; KiInitializeAbiosGdtEntry( (PKGDTENTRY)KiStack16GdtEntry, 0L, 0xffff, TYPE_DATA ); // // Establish the addressability of Common Data Area selector. // KiInitializeAbiosGdtEntry( (PKGDTENTRY)(KiAbiosGetGdt() + KGDT_CDA16), (ULONG)KiCommonDataArea, 0xffff, TYPE_DATA ); // // Set up 16-bit code selector for KiI386AbiosCall // KiInitializeAbiosGdtEntry( (PKGDTENTRY)(KiAbiosGetGdt() + KGDT_CODE16), (ULONG)&KiI386CallAbios, (ULONG)&KiEndOfCode16 - (ULONG)&KiI386CallAbios - 1, 0x18 // TYPE_CODE ); // // Link all the unused GDT entries to our GDT free list. // if (Processor == 0) { Ki386InitializeGdtFreeList(EndOfGdt); } }