|
|
/*++
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<Count; i++) { *Buffer++ = *(volatile UCHAR *)VirtualPort; __mfa(); }
if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql(OldIrql); } }
VOID READ_PORT_BUFFER_USHORT ( PUSHORT Port, PUSHORT Buffer, ULONG Count ) {
/*++
Routine Description:
Reads multiple words (16bits) from the speicified PORT address into the destination buffer.
Arguements:
Port - Supplies 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 words to read.
--*/
UINT_PTR VirtualPort; ULONG i; KIRQL OldIrql;
#if DBG
if (DbgIoPorts) DbgPrint("READ_PORT_BUFFER_USHORT(%#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<Count; i++) { *Buffer++ = *(volatile USHORT *)VirtualPort; __mfa(); }
if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql(OldIrql); } }
VOID READ_PORT_BUFFER_ULONG ( PULONG Port, PULONG Buffer, ULONG Count ) {
/*++
Routine Description:
Reads multiple longwords (32bits) from the speicified PORT address into the destination buffer.
Arguements:
Port - Supplies 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 long words to read.
--*/
UINT_PTR VirtualPort; PULONG ReadBuffer = Buffer; ULONG ReadCount; ULONG i; KIRQL OldIrql;
#if DBG
if (DbgIoPorts) DbgPrint("READ_PORT_BUFFER_ULONG(%#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<Count; i++) { *Buffer++ = *(volatile ULONG *)VirtualPort; __mfa(); }
if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql(OldIrql); } }
VOID WRITE_PORT_UCHAR ( PUCHAR Port, UCHAR Value ) {
/*++
Routine Description:
Writes a byte to the Port specified.
Arguements:
Port - The port address of the I/O Port.
Value - The value to be written to the I/O Port.
Return Value:
None.
--*/
UINT_PTR VirtualPort; KIRQL OldIrql;
#if DBG
if (DbgIoPorts) DbgPrint("WRITE_PORT_UCHAR(%#x,%#x)\n",Port,Value); #endif
VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port);
//
// Need to ensure load and mfa are not preemptable
//
__mf(); OldIrql = KeGetCurrentIrql(); if (OldIrql < DISPATCH_LEVEL) { OldIrql = KeRaiseIrqlToDpcLevel(); } *(volatile UCHAR *)VirtualPort = Value; __mf(); __mfa(); if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql (OldIrql); } }
VOID WRITE_PORT_USHORT ( PUSHORT Port, USHORT Value ) {
/*++
Routine Description:
Writes a 16 bit SHORT Integer to the Port specified.
Arguements:
Port - The port address of the I/O Port.
Value - The value to be written to the I/O Port.
Return Value:
None.
--*/
UINT_PTR VirtualPort; KIRQL OldIrql;
#if DBG
if (DbgIoPorts) DbgPrint("WRITE_PORT_USHORT(%#x,%#x)\n",Port,Value); #endif
VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port);
//
// Need to ensure load and mfa are not preemptable
//
__mf();
OldIrql = KeGetCurrentIrql(); if (OldIrql < DISPATCH_LEVEL) { OldIrql = KeRaiseIrqlToDpcLevel(); } *(volatile USHORT *)VirtualPort = Value; __mf(); __mfa(); if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql (OldIrql); } }
VOID WRITE_PORT_ULONG ( PULONG Port, ULONG Value ) {
/*++
Routine Description:
Writes a 32 bit Long Word to the Port specified.
Arguements:
Port - The port address of the I/O Port.
Value - The value to be written to the I/O Port.
Return Value:
None.
--*/
UINT_PTR VirtualPort; KIRQL OldIrql;
#if DBG
if (DbgIoPorts) DbgPrint("WRITE_PORT_ULONG(%#x,%#x)\n",Port,Value); #endif
VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port); //
// Need to ensure load and mfa are not preemptable
//
__mf();
OldIrql = KeGetCurrentIrql();
if (OldIrql < DISPATCH_LEVEL) { OldIrql = KeRaiseIrqlToDpcLevel(); } *(volatile ULONG *)VirtualPort = Value; __mf(); __mfa(); if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql (OldIrql); } }
VOID WRITE_PORT_ULONG_SPECIAL ( PULONG Port, ULONG Value ) {
/*++
Routine Description:
Writes a 32 bit Long Word to the Port specified. Assumes context switch is not possible. Used for A0 workaround. Arguements:
Port - The port address of the I/O Port.
Value - The value to be written to the I/O Port.
Return Value:
None.
--*/
UINT_PTR VirtualPort;
#if DBG
if (DbgIoPorts) DbgPrint("WRITE_PORT_ULONG(%#x,%#x)\n",Port,Value); #endif
VirtualPort = HalpGetPortVirtualAddress((UINT_PTR)Port); *(volatile ULONG *)VirtualPort = Value; __mf(); __mfa();
}
VOID WRITE_PORT_BUFFER_UCHAR ( PUCHAR Port, PUCHAR Buffer, ULONG Count ) {
/*++
Routine Description:
Writes multiple bytes from the source buffer to the specified Port address.
Arguements:
Port - The address of the Port to write to.
Buffer - A pointer to the buffer containing the data to write to the Port.
Count - Supplies the number of bytes to write.
Return Value:
None.
--*/
UINT_PTR VirtualPort; ULONG i; KIRQL OldIrql;
#if DBG
if (DbgIoPorts) DbgPrint("WRITE_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(); }
for (i=0; i<Count; i++) { *(volatile UCHAR *)VirtualPort = *Buffer++; __mfa(); }
if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql(OldIrql); }
__mf(); }
VOID WRITE_PORT_BUFFER_USHORT ( PUSHORT Port, PUSHORT Buffer, ULONG Count ) {
/*++
Routine Description:
Writes multiple 16bit short integers from the source buffer to the specified Port address.
Arguements:
Port - The address of the Port to write to.
Buffer - A pointer to the buffer containing the data to write to the Port.
Count - Supplies the number of (16 bit) words to write.
Return Value:
None.
--*/
UINT_PTR VirtualPort; ULONG i; KIRQL OldIrql;
#if DBG
if (DbgIoPorts) DbgPrint("WRITE_PORT_BUFFER_USHORT(%#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(); }
for (i=0; i<Count; i++) { *(volatile USHORT *)VirtualPort = *Buffer++; __mfa(); }
if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql(OldIrql); }
__mf(); }
VOID WRITE_PORT_BUFFER_ULONG ( PULONG Port, PULONG Buffer, ULONG Count ) {
/*++
Routine Description:
Writes multiple 32bit long words from the source buffer to the specified Port address.
Arguements:
Port - The address of the Port to write to.
Buffer - A pointer to the buffer containing the data to write to the Port.
Count - Supplies the number of (32 bit) long words to write.
Return Value:
None.
--*/
UINT_PTR VirtualPort; ULONG i; KIRQL OldIrql;
#if DBG
if (DbgIoPorts) DbgPrint("WRITE_PORT_BUFFER_ULONG(%#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(); }
for (i=0; i<Count; i++) { *(volatile ULONG *)VirtualPort = *Buffer++; __mfa(); }
if (OldIrql < DISPATCH_LEVEL) { KeLowerIrql(OldIrql); }
__mf(); }
|