//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: PnpBios.cs // // Note: // //#define VERBOSE using Microsoft.Singularity.Channels; using Microsoft.Singularity.Io; using Microsoft.Singularity.Configuration; using System; using System.Collections; using System.Diagnostics; using System.Text; using System.Configuration.Assemblies; using System.Runtime.Remoting; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Reflection; namespace Microsoft.Singularity.Drivers { [CLSCompliant(false)] public class PnpBios : IBusDevice { // since this is in the kernel, it can make whatever ports and memory it // wants. The purpose of having a decorated DeviceRequirement object is // just to generate metadata correctly; in truth, we can't do CTR on // this because these resources aren't dynamic, and so we can't count on // IoSystem to get the resources for us. Instead, we get our own // resources, and then we tell IoSystem about them via the method // ReportConfig [DriverCategory] [Signature("/root/pnp0")] [EnumeratesDevice("/pnp/...")] class MyConfig : DriverCategoryDeclaration { [IoMemoryRange(0, Default = 0xf3c30000, Length = 1, AllowWrite = false)] IoMemoryRange mem1; [IoPortRange(1, Default = 0x20b, Length = 1, AllowWrite = false)] IoPortRange port1; [IoFixedPortRange(Base = 0x0a79, Length = 0x01, AllowRead = false)] IoPortRange isaWritePort; [IoFixedPortRange(Base = 0x0279, Length = 0x01, AllowRead = false)] IoPortRange isaReadport; } private static byte lowestbit(uint bits) { for (byte i = 0; i < 32; i++, bits >>= 1) { if ((bits & 1) != 0) { return i; } } return byte.MaxValue; } private static char hexdigit(byte c) { return (char)((c >= 10) ? ('A' + (char)c - 10) : ('0' + (char)c)); } [StructLayout(LayoutKind.Explicit)] [CLSCompliant(false)] public struct PNPNODE { [FieldOffset( 0)] public ushort Size; //0 [FieldOffset( 2)] public byte Node; //2 [FieldOffset( 3)] public byte ProductId0; //3 [FieldOffset( 4)] public byte ProductId1; //4 [FieldOffset( 5)] public byte ProductId2; //5 [FieldOffset( 6)] public byte ProductId3; //6 [FieldOffset( 7)] public byte DeviceType0; //7 [FieldOffset( 8)] public byte DeviceType1; //8 [FieldOffset( 9)] public byte DeviceType2; //9 [FieldOffset(10)] public ushort DeviceAttributes; //10..11 } private IoMemory! region; private IoPort! isaReadPort; private IoPort! isaWritePort; private IoPort! isaAddressPort; private byte isaCsns; private string newId; // not thread safe! private const ushort ISA_ADDRESS_PORT = 0x0279; private const ushort ISA_WRITE_PORT = 0x0A79; private const byte ISA_ADDR_WAKE = 0x03; private const byte ISA_ADDR_CONFIG = 0x04; private const byte ISA_ADDR_STATUS = 0x05; private byte GetIsaResByte() { isaAddressPort.Write8(ISA_ADDR_STATUS); while ((isaReadPort.Read8() & 0x1) == 0) { // wait for ready. } isaAddressPort.Write8(ISA_ADDR_CONFIG); return isaReadPort.Read8(); } private void GetIsaResBytes(byte[]! buffer, int begin, int limit) { for (int i = begin; i < limit; i++) { buffer[i] = GetIsaResByte(); } } public PnpBios(Resources.PnpBiosInfo pbi) requires pbi.pnpRegion != null; requires pbi.isaReadPort != null; { region = pbi.pnpRegion.AtOffset(0, (int)pbi.pnpRegion.Length); isaReadPort = pbi.isaReadPort; isaWritePort = new IoPort(ISA_WRITE_PORT, 1, Access.Write); isaAddressPort = new IoPort(ISA_ADDRESS_PORT, 1, Access.Write); isaCsns = (byte)pbi.isaCsns; } public void Initialize() { } public void Finalize() { } private string DecodeId(byte[]! buffer, int offset) { return "" + (char)('@' + (buffer[offset + 0] >> 2)) + (char)('@' + (((buffer[offset + 0] & 0x3) << 3) | (buffer[offset + 1] >> 5))) + (char)('@' + ((buffer[offset + 1] & 0x1f))) + hexdigit((byte)(buffer[offset + 2] >> 4)) + hexdigit((byte)(buffer[offset + 2] & 0xf)) + hexdigit((byte)(buffer[offset + 3] >> 4)) + hexdigit((byte)(buffer[offset + 3] & 0xf)); } private string DecodePnpId(int offset) { byte[] buffer = new byte [7]; region.Read8(offset, buffer, 0, 7); return DecodeId(buffer, 0); } private void DecodeIsaLarge(byte[]! buffer, int size, ArrayList! list) { #if VERBOSE DebugStub.Print(" IsaLarge[{0}]: ", __arglist(buffer[0] & 0x7f)); #endif switch (buffer[0] & 0x7f) { default: #if VERBOSE DebugStub.Print(" ???1 {0,2:x2}\n", __arglist(buffer[0] & 0x7f)); #endif break; case 0x1: if (buffer[10] == 0) { break; } #if VERBOSE DebugStub.Print(" Memory inf={0,2:x2} min={1,4:x4}00 max={2,4:x4}00 aln={3,4:x4} len={4,4:x4}00\n", __arglist( buffer[3], BitConverter.ToUInt16(buffer, 4), BitConverter.ToUInt16(buffer, 6), BitConverter.ToUInt16(buffer, 8), BitConverter.ToUInt16(buffer, 10) )); #endif list.Add(new IoMemoryRange((uint)BitConverter.ToUInt16(buffer, 4) << 8, (uint)BitConverter.ToUInt16(buffer, 10) << 8, Access.ReadWrite)); break; case 0x2: #if VERBOSE DebugStub.Print(" PNP ANSI Id\n"); #endif break; case 0x4: #if VERBOSE DebugStub.Print(" PNP Vendor\n"); #endif break; case 0x5: if (BitConverter.ToUInt32(buffer, 16) == 0) { break; } #if VERBOSE DebugStub.Print(" 32-bit Memory inf={0,2:x2} min={1,8:x8} max={2,8:x8} aln={3,8:x8} len={4,8:x8}\n", __arglist( buffer[3], BitConverter.ToUInt32(buffer, 4), BitConverter.ToUInt32(buffer, 8), BitConverter.ToUInt32(buffer, 12), BitConverter.ToUInt32(buffer, 16))); #endif list.Add(new IoMemoryRange((uint)BitConverter.ToUInt32(buffer, 4), (uint)BitConverter.ToUInt32(buffer, 16), Access.ReadWrite)); break; case 0x6: if (BitConverter.ToUInt32(buffer, 8) == 0) { break; } #if VERBOSE DebugStub.Print(" 32-bit Fixed Memory inf={0,2:x2} bas={1,8:x8}..{2,8:x8}\n", __arglist( buffer[3], BitConverter.ToUInt32(buffer, 4), BitConverter.ToUInt32(buffer, 4) + BitConverter.ToUInt32(buffer, 8) - 1 )); #endif list.Add(new IoMemoryRange((uint)BitConverter.ToUInt32(buffer, 4), (uint)BitConverter.ToUInt32(buffer, 8), Access.ReadWrite)); break; } } private void DecodeIsaSmall(byte[]! buffer, int size, ArrayList! list) { #if VERBOSE DebugStub.Print(" IsaSmall[{0}]: ", __arglist(buffer[0] >> 3)); #endif switch (buffer[0] >> 3) { default: #if VERBOSE DebugStub.Print(" ???2 {0,2:x2}\n", __arglist(buffer[0] >> 3)); #endif break; case 0x1: // Version #if VERBOSE DebugStub.Print(" Version PnP={0,2:x2}, Vendor={1,2:x2}\n", __arglist( buffer[1], buffer[2])); #endif break; case 0x2: // Logical Device #if VERBOSE DebugStub.Print(" Logical Device={0} Flags={1,2:x2} {2,2:x2}\n", __arglist( DecodeId(buffer, 1), buffer[5], buffer[6])); #endif break; case 0x3: // Compatible Device #if VERBOSE DebugStub.Print(" Compatible Device={0}\n", __arglist(DecodeId(buffer, 1))); #endif newId = "/" + DecodeId(buffer,1) + newId; break; case 0x4: // IRQ byte irq = lowestbit(BitConverter.ToUInt16(buffer, 1)); if (size >= 4) { #if VERBOSE DebugStub.Print(" IRQ {0,4:x4} type={1,2:x2}\n", __arglist(irq, buffer[3])); #endif } else { #if VERBOSE DebugStub.Print(" IRQ {0,4:x4}\n", __arglist(irq)); #endif } list.Add(new IoIrqRange(irq, 1)); break; case 0x5: // DMA byte dma = lowestbit(buffer[1]); #if VERBOSE DebugStub.Print(" DMA {0,2:x2} type={1,2:x2}\n", __arglist(dma, buffer[2])); #endif list.Add(new IoDmaRange(dma, 1)); break; case 0x6: // [ #if VERBOSE DebugStub.Print(" Start pri={0} [\n", __arglist((buffer[0] >> 3 == 2 ? buffer[1] : (byte)0))); #endif break; case 0x7: // ] #if VERBOSE DebugStub.Print(" ] End\n"); #endif break; case 0x8: // Port if (buffer[7] == 0) { break; } #if VERBOSE DebugStub.Print(" I/O Port inf={0,2:x2} min={1,4:x4} max={2,4:x4} aln={3,2:x2} len={4,2:x2} {5,4:x4}..{6,4:x4}\n", __arglist( buffer[1], BitConverter.ToUInt16(buffer, 2), BitConverter.ToUInt16(buffer, 4), buffer[6], buffer[7], BitConverter.ToUInt16(buffer, 2), BitConverter.ToUInt16(buffer, 2) + buffer[7] - 1)); #endif list.Add(new IoPortRange(BitConverter.ToUInt16(buffer, 2), buffer[7], Access.ReadWrite)); break; case 0x9: // Fixed Port if (buffer[3] == 0) { break; } #if VERBOSE DebugStub.Print(" Fixed I/O Port bas={0,4:x4} len={1,2:x2} {2,4:x4}..{3,4:x4}\n", __arglist( BitConverter.ToUInt16(buffer, 1), buffer[3], BitConverter.ToUInt16(buffer, 1), BitConverter.ToUInt16(buffer, 1) + buffer[3] - 1)); #endif list.Add(new IoPortRange(BitConverter.ToUInt16(buffer, 1), buffer[3], Access.ReadWrite)); break; case 0xe: // Vendor #if VERBOSE DebugStub.Print(" Vendor\n"); #endif break; } } private byte[]! SizeBuffer(byte[] buffer, int size) { if (buffer == null || buffer.Length < size) { return new byte[size]; } return buffer; } private PnpConfig DecodeIsa() { ArrayList list = new ArrayList(); byte[] buffer = null; for (;;) { byte b = GetIsaResByte(); int size; if (b == 0x79) { break; } if ((b & 0x80) != 0) { byte b1 = GetIsaResByte(); byte b2 = GetIsaResByte(); size = 3 + (((int)b2 << 8) | b1); buffer = SizeBuffer(buffer, size); buffer[0] = b; buffer[1] = b1; buffer[2] = b2; GetIsaResBytes(buffer, 3, size); DecodeIsaLarge(buffer, size, list); } else { size = 1 + (b & 0x7); buffer = SizeBuffer(buffer, size); buffer[0] = b; GetIsaResBytes(buffer, 1, size); DecodeIsaSmall(buffer, size, list); } } return NewPnpConfig((!)newId, list); } private PnpConfig DecodePnp(int offset, int limit) { ArrayList list = new ArrayList(); byte[] buffer = null; while (offset < limit) { if (region.Read8(offset) == 0x79) { offset += 2; break; } int size; if ((region.Read8(offset) & 0x80) != 0) { size = 3 + region.Read16(offset + 1); buffer = SizeBuffer(buffer, size); region.Read8(offset, buffer, 0, size); DecodeIsaLarge(buffer, size, list); } else { size = 1 + (region.Read8(offset) & 0x7); buffer = SizeBuffer(buffer, size); region.Read8(offset, buffer, 0, size); DecodeIsaSmall(buffer, size, list); } offset += size; } return NewPnpConfig((!)newId, list); } private static PnpConfig! NewPnpConfig(string! id, ArrayList! list) { ArrayList idlist = new ArrayList(); string pnp_id = "/pnp" + id; idlist.Add(pnp_id); int pos = 5; // skip "/pnp/" // DebugStub.WriteLine("parsing isa pnp id: " + pnp_id); for (;;) { int next = pnp_id.IndexOf('/', pos); if (next == -1) break; string variant = pnp_id.Substring(0, next); idlist.Add(variant); // DebugStub.WriteLine("adding variant: " + variant); pos = next + 1; } string[]! ids = (!)(string[])idlist.ToArray(typeof(string)); // DebugStub.Break(); return new PnpConfig(ids, list); } // create a PnpConfig object that tells all of the resources being used // by this resource, so that we can account for everything in IoSystem // This may seem perverse, but it is necessary! We don't use // enumeration to get the root device, and our accounting tool requires // the IoConfig objects that are created via enumeration. Since the // memory range and readport range are gleaned through the boot process, // without enumeration, we have to provide this hack to get the resource // config into an IoConfig object, or else our accounting will not // include the root bus. #if SINGULARITY_KERNEL public PnpConfig! ReportConfig() #else internal PnpConfig! ReportConfig() #endif { ArrayList list = new ArrayList(); IoRange [] fixedRanges = new IoRange[2]; list.Add(new IoMemoryRange((UIntPtr)region.PhysicalAddress.Value, (UIntPtr)region.Length, true, true)); list.Add(new IoPortRange(isaReadPort.Port, isaReadPort.Size, isaReadPort.Readable, isaReadPort.Writable)); // since we're creating manual (and, for that matter, trusted) // accounting, let's save a bit of work and generate the fixed // resources right now. fixedRanges[0] = new IoPortRange(isaWritePort.Port, isaWritePort.Size, isaWritePort.Readable, isaWritePort.Writable); fixedRanges[1] = new IoPortRange(isaAddressPort.Port, isaAddressPort.Size, isaAddressPort.Readable, isaAddressPort.Writable); PnpConfig p = new PnpConfig(new string[] { "" }, list); p.FixedRanges = fixedRanges; return p; } public SortedList Enumerate() { SortedList found = new SortedList(); Tracing.Log(Tracing.Audit, "Probing ISA Information"); DebugStub.WriteLine("Enumerating ISA PNP:"); int node = 0x000; for (byte csn = 1; csn <= isaCsns; csn++) { #if VERBOSE DebugStub.Print("Reading csn={0}\n", __arglist(csn)); #endif isaAddressPort.Write8(ISA_ADDR_WAKE); isaWritePort.Write8(csn); byte[] buffer = new byte[9]; GetIsaResBytes(buffer, 0, 9); newId = "/" + DecodeId(buffer, 0); DebugStub.Print(newId); DebugStub.Print("/"); DebugStub.Print(node); DebugStub.Print(":\n"); found.Add(String.Format("/{0,3:x3}", node++), DecodeIsa()); } #if VERBOSE DebugStub.Print("\n"); #endif // See http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/devids.txt node = 0x100; for (int offset = 0; offset < region.Length;) { ushort size = region.Read16(offset); #if VERBOSE ushort attr = region.Read16(offset + 10); #endif newId = "/" + DecodePnpId(offset + 3); #if VERBOSE DebugStub.Print(" {0,8:x8} {1} {2,4:x4}", __arglist((uint)(region.PhysicalAddress.Value + (ulong)offset), newId, attr)); String s = ""; if ((attr & 0x0100) != 0) s += " dyn"; if ((attr & 0x0080) != 0) s += " onl"; if ((attr & 0x0040) != 0) s += " rmv"; if ((attr & 0x0020) != 0) s += " dck"; if ((attr & 0x0010) != 0) s += " ipl"; if ((attr & 0x0008) != 0) s += " inp"; if ((attr & 0x0004) != 0) s += " out"; if ((attr & 0x0002) != 0) s += " cfg"; if ((attr & 0x0001) != 0) s += " dis"; DebugStub.WriteLine(s); #endif DebugStub.WriteLine("{0}/{1:x3}:", __arglist(newId, node)); found.Add(String.Format("/{0,3:x3}", node++), DecodePnp(offset + 12, offset + size)); offset += size; } DebugStub.WriteLine(""); return found; } } }