/*++ Copyright (c) 1995 Intel Corporation Module Name: i64ioacc.c Abstract: This module implements the I/O Register access routines. Author: Bernard Lint, M. Jayakumar Sep 16 '97 Environment: Kernel mode Revision History: --*/ // // XXX: Possible issues: // ISA bit // non-ISA bit // testing // Yosemite config // Pluto config // #include "halp.h" #if DBG ULONG DbgIoPorts = 0; #endif typedef struct _PORT_RANGE { BOOLEAN InUse; BOOLEAN IsSparse; // _TRS BOOLEAN PrimaryIsMmio; // _TTP BOOLEAN HalMapped; PVOID VirtBaseAddr; PHYSICAL_ADDRESS PhysBaseAddr; // Only valid if PrimaryIsMmio = TRUE ULONG Length; // Length of VirtBaseAddr and PhysBaseAddr ranges. } PORT_RANGE, *PPORT_RANGE; // // Define a range for the architected IA-64 port space. // PORT_RANGE BasePortRange = { TRUE, // InUse FALSE, // IsSparse FALSE, // PrimaryIsMmio FALSE, // HalMapped (PVOID)VIRTUAL_IO_BASE, // VirtBaseAddr {0}, // PhysBaseAddr (unknown, comes from firmware) 64*1024*1024 // Length }; // // Seed the set of ranges with the architected IA-64 port space. // PPORT_RANGE PortRanges = &BasePortRange; USHORT NumPortRanges = 1; UINT_PTR GetVirtualPort( IN PPORT_RANGE Range, IN USHORT Port ) { UINT_PTR RangeOffset; if (Range->PrimaryIsMmio && !Range->IsSparse) { // // A densely packed range which converts MMIO transactions to // I/O port ones. // RangeOffset = Port; } else { // // Either a sparse MMIO->I/O port range, or primary is not // MMIO (IA-64 I/O port space). // RangeOffset = ((Port & 0xfffc) << 10) | (Port & 0xfff); } ASSERT(RangeOffset < Range->Length); return ((UINT_PTR)Range->VirtBaseAddr) + RangeOffset; } NTSTATUS HalpAllocatePortRange( OUT PUSHORT RangeId ) { NTSTATUS Status = STATUS_SUCCESS; PPORT_RANGE OldPortRanges = PortRanges; PPORT_RANGE NewPortRanges = NULL; ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); // // First scan the existing ranges, looking for an unused one. // for (*RangeId = 0; *RangeId < NumPortRanges; *RangeId += 1) { if (! PortRanges[*RangeId].InUse) { PortRanges[*RangeId].InUse = TRUE; return STATUS_SUCCESS; } } // // Otherwise, grow the set of ranges and copy over the old ones. // NewPortRanges = ExAllocatePool(NonPagedPool, (NumPortRanges + 1) * sizeof(PORT_RANGE)); if (NewPortRanges == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(Status)) { RtlCopyMemory(NewPortRanges, OldPortRanges, NumPortRanges * sizeof(PORT_RANGE)); *RangeId = NumPortRanges; PortRanges = NewPortRanges; NumPortRanges += 1; PortRanges[*RangeId].InUse = TRUE; if (OldPortRanges != &BasePortRange) { ExFreePool(OldPortRanges); } } if (! NT_SUCCESS(Status)) { // // Error case: cleanup. // if (NewPortRanges != NULL) { ExFreePool(NewPortRanges); } } return Status; } VOID HalpFreePortRange( IN USHORT RangeId ) { PPORT_RANGE Range = &PortRanges[RangeId]; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); ASSERT(Range->InUse); Range->InUse = FALSE; if (Range->HalMapped) { MmUnmapIoSpace(Range->VirtBaseAddr, Range->Length); } Range->VirtBaseAddr = NULL; Range->PhysBaseAddr.QuadPart = 0; Range->Length = 0; } NTSTATUS HalpAddPortRange( IN BOOLEAN IsSparse, IN BOOLEAN PrimaryIsMmio, IN PVOID VirtBaseAddr OPTIONAL, IN PHYSICAL_ADDRESS PhysBaseAddr, // Only valid if PrimaryIsMmio = TRUE IN ULONG Length, // Only valid if PrimaryIsMmio = TRUE OUT PUSHORT NewRangeId ) { NTSTATUS Status = STATUS_SUCCESS; BOOLEAN HalMapped = FALSE; BOOLEAN RangeAllocated = FALSE; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); Status = HalpAllocatePortRange(NewRangeId); RangeAllocated = NT_SUCCESS(Status); if (NT_SUCCESS(Status) && (VirtBaseAddr == NULL)) { VirtBaseAddr = MmMapIoSpace(PhysBaseAddr, Length, MmNonCached); if (VirtBaseAddr == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; } else { HalMapped = TRUE; } } if (NT_SUCCESS(Status)) { PortRanges[*NewRangeId].IsSparse = IsSparse; PortRanges[*NewRangeId].PrimaryIsMmio = PrimaryIsMmio; PortRanges[*NewRangeId].HalMapped = HalMapped; PortRanges[*NewRangeId].VirtBaseAddr = VirtBaseAddr; PortRanges[*NewRangeId].PhysBaseAddr.QuadPart = PhysBaseAddr.QuadPart; PortRanges[*NewRangeId].Length = Length; } if (! NT_SUCCESS(Status)) { // // Error case: cleanup. // if (HalMapped) { MmUnmapIoSpace(VirtBaseAddr, Length); } if (RangeAllocated) { HalpFreePortRange(*NewRangeId); } } return Status; } PPORT_RANGE HalpGetPortRange( IN USHORT RangeId ) { PPORT_RANGE Range; ASSERT(RangeId < NumPortRanges); Range = &PortRanges[RangeId]; ASSERT(Range->InUse); return Range; } // // Returns TRUE when RangeId has been set. Overlapping ranges are // allowed. // BOOLEAN HalpLookupPortRange( IN BOOLEAN IsSparse, // _TRS IN BOOLEAN PrimaryIsMmio, // FALSE for I/O port space, _TTP IN PHYSICAL_ADDRESS PhysBaseAddr, IN ULONG Length, OUT PUSHORT RangeId ) { BOOLEAN FoundMatch = FALSE; PPORT_RANGE Range; for (*RangeId = 0; *RangeId < NumPortRanges; *RangeId += 1) { Range = &PortRanges[*RangeId]; if (! Range->InUse) { continue; } if ((Range->PrimaryIsMmio == PrimaryIsMmio) && (Range->IsSparse == IsSparse)) { if (! PrimaryIsMmio) { // // Port space on the primary side. Sparseness doesn't // make sense for primary side port space. Because // there is only one primary side port space, which is // shared by all I/O bridges, don't check the base // address. // ASSERT(! IsSparse); FoundMatch = TRUE; break; } if ((Range->PhysBaseAddr.QuadPart == PhysBaseAddr.QuadPart) && (Range->Length == Length)) { FoundMatch = TRUE; break; } } } // // A matching range was not found. // return FoundMatch; } NTSTATUS HalpQueryAllocatePortRange( IN BOOLEAN IsSparse, IN BOOLEAN PrimaryIsMmio, IN PVOID VirtBaseAddr OPTIONAL, IN PHYSICAL_ADDRESS PhysBaseAddr, // Only valid if PrimaryIsMmio = TRUE IN ULONG Length, // Only valid if PrimaryIsMmio = TRUE OUT PUSHORT NewRangeId ) { NTSTATUS Status = STATUS_SUCCESS; if (! HalpLookupPortRange(IsSparse, PrimaryIsMmio, PhysBaseAddr, Length, NewRangeId)) { Status = HalpAddPortRange(IsSparse, PrimaryIsMmio, NULL, PhysBaseAddr, Length, NewRangeId); } return Status; } UINT_PTR HalpGetPortVirtualAddress( UINT_PTR Port ) { /*++ Routine Description: This routine gives 32 bit virtual address for the I/O Port specified. Arguements: PORT - Supplies PORT address of the I/O PORT. Returned Value: UINT_PTR - Virtual address value. --*/ PPORT_RANGE PortRange; // // Upper 16 bits of the port handle are the range id. // USHORT RangeId = (USHORT)((((ULONG)Port) >> 16) & 0xffff); USHORT OffsetInRange = (USHORT)(Port & 0xffff); ULONG VirtOffset; UINT_PTR VirtualPort = 0; #if 0 { BOOLEAN isUart = FALSE; BOOLEAN isVGA = FALSE; if (RangeId == 0) { if ((OffsetInRange >= 0x3b0) && (OffsetInRange <= 0x3df)) { isVGA = TRUE; } if ((OffsetInRange >= 0x2f8) && (OffsetInRange <= 0x2ff)) { isUart = TRUE; } if ((OffsetInRange >= 0x3f8) && (OffsetInRange <= 0x3ff)) { isUart = TRUE; } if (!isVGA && !isUart) { static UINT32 numRaw = 0; InterlockedIncrement(&numRaw); } } else { static UINT32 numUnTra = 0; InterlockedIncrement(&numUnTra); } } #endif // #if DBG PortRange = HalpGetPortRange(RangeId); return GetVirtualPort(PortRange, OffsetInRange); } UCHAR READ_PORT_UCHAR( PUCHAR Port ) { /*++ Routine Description: Reads a byte location from the PORT Arguements: PORT - Supplies the PORT address to read from Return Value: UCHAR - Returns the byte read from the PORT specified. --*/ UINT_PTR VirtualPort; UCHAR LoadData; KIRQL OldIrql; #if DBG if (DbgIoPorts) DbgPrint("READ_PORT_UCHAR(%#x)\n",Port); #endif VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port); // // Need to ensure load and mfa are not preemptable // __mf(); OldIrql = KeGetCurrentIrql(); if (OldIrql < DISPATCH_LEVEL) { OldIrql = KeRaiseIrqlToDpcLevel(); } LoadData = *(volatile UCHAR *)VirtualPort; __mfa(); if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql (OldIrql); } return (LoadData); } USHORT READ_PORT_USHORT ( PUSHORT Port ) { /*++ Routine Description: Reads a word location (16 bit unsigned value) from the PORT Arguements: PORT - Supplies the PORT address to read from. Returned Value: USHORT - Returns the 16 bit unsigned value from the PORT specified. --*/ UINT_PTR VirtualPort; USHORT LoadData; KIRQL OldIrql; #if DBG if (DbgIoPorts) DbgPrint("READ_PORT_USHORT(%#x)\n",Port); #endif VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port); // // Need to ensure load and mfa are not preemptable // __mf(); OldIrql = KeGetCurrentIrql(); if (OldIrql < DISPATCH_LEVEL) { OldIrql = KeRaiseIrqlToDpcLevel(); } LoadData = *(volatile USHORT *)VirtualPort; __mfa(); if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql (OldIrql); } return (LoadData); } ULONG READ_PORT_ULONG ( PULONG Port ) { /*++ Routine Description: Reads a longword location (32bit unsigned value) from the PORT. Arguements: PORT - Supplies PORT address to read from. Returned Value: ULONG - Returns the 32 bit unsigned value (ULONG) from the PORT specified. --*/ UINT_PTR VirtualPort; ULONG LoadData; KIRQL OldIrql; #if DBG if (DbgIoPorts) DbgPrint("READ_PORT_ULONG(%#x)\n",Port); #endif VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port); // // Need to ensure load and mfa are not preemptable // __mf(); OldIrql = KeGetCurrentIrql(); if (OldIrql < DISPATCH_LEVEL) { OldIrql = KeRaiseIrqlToDpcLevel(); } LoadData = *(volatile ULONG *)VirtualPort; __mfa(); if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql (OldIrql); } return (LoadData); } ULONG READ_PORT_ULONG_SPECIAL ( PULONG Port ) { /*++ Routine Description: Reads a longword location (32bit unsigned value) from the PORT. For A0 bug 2173. Does not enable/disable interrupts. Called from first level interrupt handler. Arguements: PORT - Supplies PORT address to read from. Returned Value: ULONG - Returns the 32 bit unsigned value (ULONG) from the PORT specified. --*/ UINT_PTR VirtualPort; ULONG LoadData; #if DBG if (DbgIoPorts) DbgPrint("READ_PORT_ULONG(%#x)\n",Port); #endif VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port); __mf(); LoadData = *(volatile ULONG *)VirtualPort; __mfa(); return (LoadData); } VOID READ_PORT_BUFFER_UCHAR ( PUCHAR Port, PUCHAR Buffer, ULONG Count ) { /*++ Routine Description: Reads multiple bytes from the specified PORT address into the destination buffer. Arguements: PORT - The address of the PORT to read from. Buffer - A pointer to the buffer to fill with the data read from the PORT. Count - Supplies the number of bytes to read. Return Value: None. --*/ UINT_PTR VirtualPort; ULONG i; KIRQL OldIrql; #if DBG if (DbgIoPorts) DbgPrint("READ_PORT_BUFFER_UCHAR(%#x,%#p,%d)\n",Port,Buffer,Count); #endif VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port); // // Prevent preemption before mfa // OldIrql = KeGetCurrentIrql(); if (OldIrql < DISPATCH_LEVEL) { OldIrql = KeRaiseIrqlToDpcLevel(); } __mf(); for (i=0; i