<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>A Hole In My Head</title><link>http://blogs.msdn.com/b/doronh/</link><description>Doron Holan&amp;#39;s musings on drivers and other nibbles and bits</description><dc:language>en</dc:language><generator>Telligent Evolution Platform Developer Build (Build: 5.6.50428.7875)</generator><item><title>Developing Drivers with the Windows® Driver Foundation, i.e. the WDF book, is apparently back in print!</title><link>http://blogs.msdn.com/b/doronh/archive/2010/12/15/developing-drivers-with-the-windows-174-driver-foundation-i-e-the-wdf-book-is-apparently-back-in-print.aspx</link><pubDate>Wed, 15 Dec 2010 21:59:50 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10105562</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=10105562</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2010/12/15/developing-drivers-with-the-windows-174-driver-foundation-i-e-the-wdf-book-is-apparently-back-in-print.aspx#comments</comments><description>&lt;p&gt;Yeah! The book is now back in print (see &lt;a target="_blank" href="http://www.amazon.com/Developing-Drivers-Windows-Foundation-Developer/dp/0735623740/ref=sr_1_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1292450235&amp;amp;sr=8-1"&gt;Amazon&lt;/a&gt;)&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10105562" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDF/">WDF</category></item><item><title>Developing Drivers with the Windows® Driver Foundation, i.e. the WDF book, is apparently out of print</title><link>http://blogs.msdn.com/b/doronh/archive/2010/08/25/developing-drivers-with-the-windows-174-driver-foundation-i-e-the-wdf-book-is-apparently-out-of-print.aspx</link><pubDate>Thu, 26 Aug 2010 05:31:56 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10054457</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=10054457</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2010/08/25/developing-drivers-with-the-windows-174-driver-foundation-i-e-the-wdf-book-is-apparently-out-of-print.aspx#comments</comments><description>&lt;p&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 11pt; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;"&gt;&lt;span style="color: #0000ff;"&gt;Looks like the going price on Amazon is &amp;gt;$250 :(.&amp;nbsp; The good news is that you can still get the book as an e-book at O'Reilly, &lt;a href="http://oreilly.com/catalog/9780735623743/"&gt;http://oreilly.com/catalog/9780735623743/&lt;/a&gt;, or at Safari, for around $50.&amp;nbsp; Not as convenient as a hard copy IMHO, but it is better than nothing or spending $250.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-family: 'Calibri','sans-serif'; font-size: 11pt; mso-fareast-font-family: Calibri; mso-fareast-theme-font: minor-latin; mso-ansi-language: EN-US; mso-fareast-language: EN-US; mso-bidi-language: AR-SA;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10054457" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/KMDF/">KMDF</category><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDF/">WDF</category></item><item><title>Arbitration and Translation, Part 3</title><link>http://blogs.msdn.com/b/doronh/archive/2010/05/06/arbitration-and-translation-part-3.aspx</link><pubDate>Fri, 07 May 2010 00:14:56 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10008841</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=10008841</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2010/05/06/arbitration-and-translation-part-3.aspx#comments</comments><description>&lt;p&gt;This post is the third in a series.&amp;#160; You can see the others here, &lt;a href="http://blogs.msdn.com/doronh/archive/2010/05/05/arbitration-and-translation-part-1.aspx" target="_blank"&gt;Part 1&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/doronh/archive/2010/05/06/translation-and-windows.aspx" target="_blank"&gt;Part 2&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;What is an Arbiter?&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;In the NT PnP subsystem, an arbiter is an interface that a bus driver can expose which is able to intelligently assign PnP resources of a single specific type (memory, I/O ports, DMA channels, interrupts, bus numbers) to its children.&amp;#160; In general, an arbiter cannot assign resources that it has not claimed from its parent.&lt;/p&gt;  &lt;p&gt;The PnP manager itself exposes five arbiters, one for each type listed above.&amp;#160; These arbiters are relatively dumb.&amp;#160; They give out ranges of numbers, with the only criteria being these:&lt;/p&gt;  &lt;p&gt;· Is this range free?&amp;#160; If so, you can have it.&lt;/p&gt;  &lt;p&gt;· If the range is already claimed, but with the shareable flag, and your claim is marked shareable, you can have it too.&lt;/p&gt;  &lt;p&gt;· If any part of the range is already claimed as exclusive, you can’t have it.&lt;/p&gt;  &lt;p&gt;These arbiters aren’t bus-specific, but they don’t have to be.&amp;#160; They’re enough to get started.&lt;/p&gt;  &lt;p&gt;Yesterday, I covered the translator interface and what it does.&amp;#160; The arbiter interface is similar.&amp;#160; Both are about manipulating the resources for child devices and putting them is less domain-specific terms.&amp;#160; The difference between a translator and an arbiter is simply that translator interfaces are sufficient when you cannot really change the resources available to a child device and arbiters are necessary when you can.&amp;#160; Translators are, as you might expect, much simpler.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;HALMPS&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;To illustrate the difference, I want to talk about HALMPS.&amp;#160; This was a HAL that was shipped as part of Windows NT 3.5 through Windows Server 2003.&amp;#160; It might have even shipped in Server 2008.&amp;#160; I don’t remember when it got pulled from the tree.&amp;#160; &lt;/p&gt;  &lt;p&gt;It ran on machines that conformed to the Intel Multiprocessor Specification, versions 1.1 through 1.4.&amp;#160; If you’re curious, you can find it &lt;a href="http://www.intel.com/design/archives/processors/pro/docs/242016.htm" target="_blank"&gt;here&lt;/a&gt;. That spec has been entirely obsoleted by ACPI.&amp;#160; MPS was simple where ACPI is very complex.&amp;#160; But MPS can’t describe a machine that changes configuration dynamically at run time while ACPI can.&amp;#160; As it turns out, this adds a whole lot of complexity.&lt;/p&gt;  &lt;p&gt;MPS describes a system in terms of, among other things, the number of local APICs (which deliver interrupts to processors) and I/O APICs (which collect interrupts from devices.)&amp;#160; It says which pins on which I/O APICs each PCI device is connected to.&amp;#160; This is actually encoded as device-function-IntPin, and HALMPS represents a devices “IRQ” thusly.&amp;#160; You can see this in Device Manager of a machine running HALMPS.&amp;#160; The assigned IRQ is just these values all run together.&amp;#160; This was very confusing to many people, as they might see two devices with the same IRQ in Device Manager, but that just meant that those two devices occupied the same slot on two different buses.&amp;#160; They might have been sharing interrupts, or they might not.&lt;/p&gt;  &lt;p&gt;The important part of the story here is that the BIOS picked all interrupt-related routing and it was fixed forever at boot.&amp;#160; There aren’t any decisions to make, except one.&amp;#160; The OS gets to pick which of the processors get targeted by a specific I/O APIC input.&lt;/p&gt;  &lt;p&gt;When we were gluing PnP onto the side of the NT driver model, during the development of Windows 2000, the existing scheme for choosing a target processor set for a device’s interrupts involved the driver calling &lt;a href="http://msdn.microsoft.com/en-us/library/ff546612(VS.85).aspx" target="_blank"&gt;HalGetInterruptVector&lt;/a&gt;.&amp;#160; The target IDT entries, the processor set mask and the IRQL for the device all had to be chosen there.&amp;#160; Furthermore, if two devices shared interrupts, they had to get the same answer, even if one driver was PnP-aware and one made this obsolete call.&amp;#160; So I left the IRQ-to-IDT mapping code in the HAL.&lt;/p&gt;  &lt;p&gt;If a PnP driver made a resource claim for an interrupt, then that claim would make its way toward the root of the PnP tree (see yesterday’s post: link) and it would reach an interrupt translator at the HAL device node.&amp;#160; The HAL would see the device’s claim, do the math on how the interrupt was routed, including which I/O APIC and which pin on that I/O APIC, and then make an internal call to HalGetInterruptVector, which would choose a target processor set, an IRQL and a vector.&amp;#160; The target processor set (actually the APIC cluster ID) was then encoded in the upper 24 bits of the “Vector” that the device was assigned and the IDT entry was encoded in the lower 8 bits.&amp;#160; This was then presented to the root interrupt arbiter within the PnP manager, where it was claimed.&lt;/p&gt;  &lt;p&gt;Just for fun, I fired up a VM running HALMPS and dumped this out in the debugger.&amp;#160; You can see the relevant parts here:&lt;/p&gt;  &lt;pre&gt;0: kd&amp;gt; !translator

DEVNODE 83373ee0 (HTREE\ROOT\0)
  BusNumber Translator
    Resources:    nt!IopTranslatorHandlerCm
    Requirements: nt!IopTranslatorHandlerIo
  Port Translator
    Resources:    nt!IopTranslatorHandlerCm
    Requirements: nt!IopTranslatorHandlerIo
Memory Translator
    Resources:    nt!IopTranslatorHandlerCm
    Requirements: nt!IopTranslatorHandlerIo
  Dma Translator
    Resources:    nt!IopTranslatorHandlerCm
    Requirements: nt!IopTranslatorHandlerIo
  Interrupt Translator
    Resources:    nt!IopTranslatorHandlerCm
    Requirements: nt!IopTranslatorHandlerIo

    &lt;strong&gt;DEVNODE 8336f3c0 (PCI_HAL\PNP0A03\0)
      Interrupt Translator
        Resources:    hal!HalpIrqTranslateResourcesPci
        Requirements: hal!HalpIrqTranslateRequirementsPci&lt;/strong&gt;

      DEVNODE 833ba008 (PCI\VEN_8086&amp;amp;DEV_7110&amp;amp;SUBSYS_00000000&amp;amp;REV_01\2&amp;amp;ebb567f&amp;amp;0&amp;amp;38)
        Interrupt Translator
          Resources:    hal!HalIrqTranslateResourcesIsa
          Requirements: hal!HalIrqTranslateResourceRequirementsIsa

      DEVNODE 833baee0 (PCI\VEN_8086&amp;amp;DEV_7111&amp;amp;SUBSYS_00000000&amp;amp;REV_01\2&amp;amp;ebb567f&amp;amp;0&amp;amp;39)
        Interrupt Translator
          Resources:    hal!HalIrqTranslateResourcesIsa
          Requirements: hal!HalIrqTranslateResourceRequirementsIsa
0: kd&amp;gt; !arbiter 

  Interrupt Arbiter &amp;quot;RootIRQ&amp;quot; at 808a5620
    Allocated ranges:
      0000000000000000 - 0000000000000000   B   833c0988  (\Driver\PCI_HAL)
      0000000000000001 - 0000000000000001   B   833c0988  (\Driver\PCI_HAL)
       (some lines omitted for brevity)
      000000000000002f - 000000000000002f   B   833c0988  (\Driver\PCI_HAL)
      00000000000000ff - 00000000000000ff   B   833c0988  (\Driver\PCI_HAL)
&lt;strong&gt;      0000000000000151 - 0000000000000151       83373250  (atapi)
      0000000000000152 - 0000000000000152   B   8336df10 
      0000000000000161 - 0000000000000161    
        0000000000000161 - 0000000000000161  CB   833c0988  (\Driver\PCI_HAL)
        0000000000000161 - 0000000000000161  CB   8336d838  (Serial)
      0000000000000172 - 0000000000000172       8336da80  (i8042prt)
      0000000000000181 - 0000000000000181       8336d5f0  (Serial)
      0000000000000182 - 0000000000000182       833bd1c0  (i8042prt)
      0000000000000192 - 0000000000000192       8336d3a8  (fdc)
      00000000000001a2 - 00000000000001a2       83373030  (atapi)
      00000000000001b1 - 00000000000001b1    
        00000000000001b1 - 00000000000001b1  CB   833c0988  (\Driver\PCI_HAL)
        00000000000001b1 - 00000000000001b1  CB   833bdf10 
      00000000000001b2 - 00000000000001b2   B   833bd408&lt;/strong&gt;&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;HALMACPI&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Now let’s contrast that with HALMACPI.&amp;#160; This is the HAL that runs (to this day) on any machine that conforms to the ACPI spec and has more than one processor, which is nearly anything you can go out and buy.&lt;/p&gt;

&lt;p&gt;The ACPI spec says a few things about interrupts:&lt;/p&gt;

&lt;p&gt;· There are a discrete number of I/O APICs and their base addresses are listed in ACPI tables.&lt;/p&gt;

&lt;p&gt;· ISAPnP- or ACPI-enumerated devices are attached to I/O APIC inputs and those attachments are described in the ACPI namespace under each device.&amp;#160; A device can be moved from one input to another by invoking the _SRS method under the device.&lt;/p&gt;

&lt;p&gt;· PCI devices are either directly attached to I/O APIC inputs or they are attached to IRQ steering “link nodes” which themselves can be attached to one of a set of I/O APIC inputs.&amp;#160; The set of possible attachments is described under the link node (which is itself sort of a device) in the ACPI namespace.&amp;#160; The exact pin that they are attached to can be changed by invoking the link node’s _SRS method.&lt;/p&gt;

&lt;p&gt;This is entirely different from HALMPS.&amp;#160; Now we have a choice about how devices are routed, if the motherboard designer designs the board that way and if the BIOS guy exposes the functionality.&amp;#160; If we want to move one or a group of PCI devices from one IRQ to another, we can.&amp;#160; I put an interrupt arbiter in the ACPI driver, as that was where it was possible, or at least easy, to interact with all the various parts of the ACPI namespace.&lt;/p&gt;

&lt;p&gt;An arbiter gets requests like: “Here’s a set of four devices, each of which has a fairly complex set of possible interrupt assignments.&amp;#160; Please find the optimal configuration which satisfies all the requirements.&amp;#160; When a device needs I/O ports, memory ranges and interrupts, these requests get made by the PnP manager to each type of arbiter simultaneously.&amp;#160; If a fit can be found, all the device eventually get IRP_MN_START_DEVICE with a resource set that meets their needs.&lt;/p&gt;

&lt;p&gt;Note that this problem is NP-complete.&amp;#160; So we don’t look at every possible solution.&amp;#160; There are a bunch of heuristics about which parts of the solution space to look at first and how long to spend looking.&lt;/p&gt;

&lt;p&gt;In truth, the NT PnP team came to a fairly painful conclusion after a couple of years of tweaking these algorithms.&amp;#160; (It was painful mostly because it took so long to fully understand the situation.)&amp;#160; The first major truth is that you can no longer add a truly new bus architecture to a PC because Windows 95 (and now many other OSes) only understood PCI.&amp;#160; At the point that a largely-deployed OS that did PnP natively existed, every machine had to expose the interfaces that that OS understood.&amp;#160; Thus we have HyperTransport, PCI Express and lots of internal bus architectures that never got widely published, all of which pretend to be PCI at a PnP level so that they work with old OSes which do PnP natively.&lt;/p&gt;

&lt;p&gt;The kicker is that all of those, particularly the chipset-internal ones, have deviations from the PCI spec.&amp;#160; I’ve sat in meetings with chipset designers who said that their devices didn’t have to be PCI-compliant because they were inside of a chipset.&amp;#160; From a hardware guy’s perspective, this makes perfect sense.&amp;#160; It doesn’t have PCI pins, it doesn’t have any PCI logic, so it isn’t PCI.&amp;#160; But, for various reasons, it does have a PCI configuration space.&amp;#160; When I point out to them that there’s no way for the OS to differentiate between these “non-PCI PCI devices” and real PCI devices, they shrug and say that’s not their problem, since the BIOS sets it all up right anyhow.&lt;/p&gt;

&lt;p&gt;And that’s the second major truth.&amp;#160; The BIOS sets most or all of it up anyhow.&lt;/p&gt;

&lt;p&gt;So the arbiter interface and NT PnP, in general, have a way of asking about how a device was configured by the BIOS.&amp;#160; When a device is first discovered, the PnP manager sends &lt;a href="http://msdn.microsoft.com/en-us/library/ff551710(VS.85).aspx" target="_blank"&gt;IRP_MN_QUERY_RESOURCES&lt;/a&gt;.&amp;#160;&amp;#160;&amp;#160; This IRP asks the question “what resources is this device using, right now?”&amp;#160; The PCI driver will look at a device’s Base Address Registers and its Interrupt Line register and send that claim back in response.&amp;#160; The PnP manager then calls into the relevant arbiters with the device’s PDO (or a proxy PDO if the driver an NT4-style non-PnP driver) and claim those ranges unconditionally for the device, with a flag saying that this is a “boot reservation.”&amp;#160; See the ‘B’ in some lines of the debugger dump above, and you’ll see these boot claims.&lt;/p&gt;

&lt;p&gt;When the device stack for the device is being built, the PnP manager sends IRP_MN_QUERY_RESOURCE_REQUIREMENTS to ask “what are the set of all possible sets of resources this device could use?”&amp;#160; And once the FDO and filters have been loaded, it sends IRP_MN_FILTER_RESOURCE_REQUIREMENTS to ask “what modifications would you like to make to this claim that the bus driver has generated on your behalf?”&lt;/p&gt;

&lt;p&gt;The resulting claim set is sent to the arbiters.&amp;#160; Now those arbiters know what resources the device booted with, if the device was present in the machine at boot time.&amp;#160; So they, for the most part, just choose what the BIOS chose. This is what makes slippery chipsets work just fine.&amp;#160; The BIOS is the expert and NT leaves that alone.&lt;/p&gt;

&lt;p&gt;Some resource types don’t work this way.&amp;#160; Most notably, there’s no notion of which IRQ a device was connected to at boot time if your machine is running with the APIC enabled.&amp;#160; The BIOS only configures the IRQ routing for the PIC (not APIC) interrupt controller, in preparation for running Windows 98, which never supported APICs.&amp;#160; So the ACPI IRQ arbiter, when running on an APIC system, throws away the boot claims.&lt;/p&gt;

&lt;p&gt;Note that the boot claim system has some interesting properties.&amp;#160; There may be conflicts, and sometimes that’s okay.&amp;#160; BIOSes tend to make claims for ACPI-enumerated dummy devices like “Motherboard Resources” when there is a device which must claim some I/O ports but which mostly doesn’t ever get a driver loaded.&amp;#160; The most famous example of this tends to be an SMBus controller.&amp;#160; Most machines don’t run a driver on it, but the BIOS needs to access it in System Management Mode. So it will claim the ports.&amp;#160; Sometimes, people write drivers for them and then those driver show up as a conflict with a boot claim.&amp;#160; This is mostly benign.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Message-Signaled Interrupts&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Interrupt arbitration tends to be the most complicated part of the system.&amp;#160; Or, at least, it seems that way to me, since I’m still messing around with it almost fifteen years after I first began.&amp;#160; Most of the other arbiters haven’t changed much in years beginning with a ‘2’.&amp;#160; &lt;/p&gt;

&lt;p&gt;Devices which can generate Message-Signaled Interrupts don’t need to use an I/O APIC input.&amp;#160; But they can, usually, also use one, particularly if the OS in question doesn’t understand MSI.&amp;#160; With MSI, the interrupt is sent by doing a short busmaster burst involving 32-bits of data to a special address.&amp;#160; The device need not understand the address nor the data.&amp;#160; It just gets told, when you want to trigger this interrupt, send this blob here.&lt;/p&gt;

&lt;p&gt;The PCI Spec has taken two passes at defining how this should be configured in a device, both of which have proved insufficient for representing the problem at hand.&amp;#160; “MSI” was introduced in PCI 2.2 and it involved writing a single address into the device, and a single data value too.&amp;#160; If the device wanted to send more than one interrupt, it could vary N low-order bits of the data value, at the OS’s discretion.&amp;#160; This meant that the data values were constrained to a naturally aligned range of values, and that range was a power of 2 in length.&amp;#160; (See the PCI Spec for the scary details.)&lt;/p&gt;

&lt;p&gt;Given the way Intel defined the special address/data format in the Software Developer’s Manual, Volume 3, Chapter 8, Section 11 (&lt;a href="http://www.intel.com/products/processor/manuals/"&gt;http://www.intel.com/products/processor/manuals/&lt;/a&gt;) the address determine the target processor set.&amp;#160; This means that MSI (as defined in PCI 2.2) can only work if every interrupt targets the same processor or set of processors.&amp;#160; You can’t choose to send one interrupt message to one processor and one to another.&lt;/p&gt;

&lt;p&gt;Thus MSI-X was defined in PCI 3.0.&amp;#160; Both still exist, and they’ve been carried into PCI-X and PCI Express.&amp;#160; MSI-X allows each interrupt message to have separate address and data values.&amp;#160; It also allows as many as 2048 messages per PCI function.&lt;/p&gt;

&lt;p&gt;Given that the processor-set-to-address mapping was fixed by Intel, virtualization and large numbers of cores is forcing another level of indirection through I/O MMUs, called “VT-d” by Intel and “IOMMU” by AMD.&lt;/p&gt;

&lt;p&gt;The fundamental problem here is that the PCI spec never should have tried to define message-signaled interrupts at all.&amp;#160; They just don’t have anything to do with the PCI bus.&amp;#160; Every interesting thing about them is external to the PCI bus.&amp;#160; (Full disclosure:&amp;#160; I didn’t always understand this, and I sat on the committee that defined MSI-X.)&amp;#160; The only thing the PCI spec allows you to do is to have a defined mechanism for telling the device to target a busmaster transaction to a specific address with specific data when the device needs attention.&amp;#160; &lt;/p&gt;

&lt;p&gt;There’s no standard mechanism for telling a PCI NIC to send your network data to a specific address, as that’s just part of the definition of the device behavior.&amp;#160; You don’t want to standardize that because it removes degrees of freedom when you want to do it differently in the future.&amp;#160; There shouldn’t be one for interrupts, either, on exactly the same grounds.&amp;#160; I’ll quit ranting now.&lt;/p&gt;

&lt;p&gt;What you really need is a way to say, for example, “my device needs to trigger 36 interrupts, two-per core in this 16-core machine, plus four more for various housekeeping tasks.”&amp;#160; That’s not really expressible in the PCI capability structs which define MSI and MSI-X, but it is expressible inside of Windows.&lt;/p&gt;

&lt;p&gt;Once the PnP manager has assigned IDT vectors, IRQLs, target processors and the lot, you need a way of programming these into the device.&amp;#160; This is expressible in the PCI spec, though it’s redundant in my mind.&amp;#160; Whether the bus driver does it or the function driver does it doesn’t matter much.&lt;/p&gt;

&lt;p&gt;Mechanically, it works like this:&lt;/p&gt;

&lt;p&gt;1. The PnP manager sends IRP_MN_QUERY_RESOURCE_REQUIREMENTS.&amp;#160; The PCI driver reads the various capability structs and some registry keys that were set during INF processing (since, as we saw above, the PCI spec can’t express everything necessary) and responds to this IRP with some interrupt claims.&amp;#160; Typically, there will be three possibilities expressed in the resultant IO Resource Requirements List: lots of message-signaled interrupts, one message-signaled interrupt and, lastly, one line-based interrupt.&lt;/p&gt;

&lt;p&gt;2. The PnP manager builds the rest of the device stack and sends IRP_MN_FILTER_RESOURCE_REQUIREMENTS.&amp;#160; If the device is trying really hard to squeeze out performance by targeting specific interrupts at specific processors, the FDO (usually NDIS or storport, along with the miniport) will “filter” that claim to affinitize certain interrupts to certain cores, and possibly to cut down the total number of messages in the first claim to some multiple of the number of cores actually installed.&lt;/p&gt;

&lt;p&gt;3. The PnP manager passes these sets of claims to the interrupt arbiter in the ACPI driver, which looks at them and tries to satisfy them in the order that they’re listed.&amp;#160; If there are enough free IDT entries (and the underlying processor and chipset support MSI at all) then the first claim gets satisfied.&amp;#160; If not, it goes for the single message claim.&amp;#160; If that can’t be satisfied, it will back off to the line-based interrupt, which is usually shared with something else and will almost certainly succeed.&lt;/p&gt;

&lt;p&gt;4. The PnP manager translates these resources down to the bus terms.&amp;#160; (See yesterday’s post.)&amp;#160; This involves changing these vector and target processor sets into addresses and data again.&amp;#160; These values end up in your interrupt resources in your raw resource list.&lt;/p&gt;

&lt;p&gt;5. The PnP manager translates these “up” into processor-relative terms.&amp;#160; This populates the translated resource list with Vector, Level and Affinity for each interrupt message.&lt;/p&gt;

&lt;p&gt;6. The PnP manager sends IRP_MN_START_DEVICE with both lists.&amp;#160; The PCI driver sees the IRP first (since the FDO handles start on the way up, remember) and programs the MSI or MSI-X capability structures, if they exist.&amp;#160; The FDO sees the IRP next, and stores the information for calling IoConnectInterruptEx.&amp;#160; It may use the raw resources to derive address and data values if it likes.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;ACPI IRQ Arbiter Dumps&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;The ACPI IRQ arbiter handles all this by considering a list of things simultaneously.&lt;/p&gt;

&lt;p&gt;· Free IDT entries on all the potential cores.&lt;/p&gt;

&lt;p&gt;· Free I/O APIC inputs for devices which have some flexibility.&lt;/p&gt;

&lt;p&gt;· Whether MSI is available in the processor and the chipset&lt;/p&gt;

&lt;p&gt;· Whether the device has an MSI request&lt;/p&gt;

&lt;p&gt;Since that arbiter is looking across a couple of dimensions simultaneously, dumping it is a little more complicated.&amp;#160; The default debugger command “!arbiter” will show you the IRQ claims.&amp;#160; “!acpiirqarb” will show you the other state.&amp;#160; I’ll walk through these dumps below.&lt;/p&gt;

&lt;p&gt;This first dump is of the default arbiter in the PnP manager.&amp;#160; It says that lots of vectors are reserved for internal use and lots of vectors are assigned to ACPI (across every core) for redistribution to other devices.&lt;/p&gt;

&lt;pre&gt;0: kd&amp;gt; !arbiter 4

DEVNODE fffffa8003c49d90 (HTREE\ROOT\0)
  Interrupt Arbiter &amp;quot;RootIRQ&amp;quot; at fffff800014aebc0
    Allocated ranges:
      0000000000000000 - 0000000000000000   B   fffffa8003c48e30 
      0000000000000001 - 0000000000000001   B   fffffa8003c48e30 
       (lines omitted)
      000000000000003f - 000000000000003f   B   fffffa8003c48e30 
      0000000000000051 - 0000000000000051       fffffa8003c4dbd0  (ACPI)
       (lines omitted)
      00000000000000bd - 00000000000000bd       fffffa8003c4dbd0  (ACPI)
      00000000000000be - 00000000000000be       fffffa8003c4dbd0  (ACPI)
      00000000000000ff - 00000000000000ff   B   fffffa8003c48e30 
    Possible allocation:
      &amp;lt; none &amp;gt;&lt;/pre&gt;

&lt;p&gt;This next dump is of the arbiter state of the ACPI IRQ arbiter.&amp;#160; It’s just the “IRQ” part, as that’s what’s done in terms that !arbiter can interpret&lt;/p&gt;

&lt;pre&gt;    DEVNODE fffffa8003c394a0 (ACPI_HAL\PNP0C08\0)
      Interrupt Arbiter &amp;quot;ACPI_IRQ&amp;quot; at fffff880010fdfc0
        Allocated ranges:
          0000000000000002 - 0000000000000002   B   fffffa80039a05c0 
          0000000000000008 - 0000000000000008       fffffa80039a0c20 
          0000000000000009 - 0000000000000009 S     fffffa8003c4dbd0  (ACPI)
          000000000000000d - 000000000000000d   B   fffffa80039a07e0 
          0000000000000010 - 0000000000000010 S  
            0000000000000010 - 0000000000000010 S     fffffa800399b060  (pciide)
            0000000000000010 - 0000000000000010 S     fffffa80039b3a20  (usbuhci)
          0000000000000012 - 0000000000000012 S  
            0000000000000012 - 0000000000000012 S     fffffa80039ab060  (pciide)
            0000000000000012 - 0000000000000012 S     fffffa80039b1060  (usbehci)
            0000000000000012 - 0000000000000012 S     fffffa80039ae060  (usbuhci)
          0000000000000013 - 0000000000000013 S  
            0000000000000013 - 0000000000000013 S     fffffa80039ac060  (pciide)
            0000000000000013 - 0000000000000013 S     fffffa80039b2a20  (usbuhci)
            0000000000000013 - 0000000000000013 S     fffffa80039afa20  (usbuhci)
          0000000000000015 - 0000000000000015 S     fffffa80039b2060  (usbuhci)
          0000000000000017 - 0000000000000017 S  
            0000000000000017 - 0000000000000017 S     fffffa80039aea20  (usbehci)
            0000000000000017 - 0000000000000017 S     fffffa80039af060  (usbuhci)
          00000000fffffff9 - 00000000fffffff9       fffffa8003997a20  (pci)
          00000000fffffffa - 00000000fffffffa       fffffa80039b0060  (pci)
          00000000fffffffb - 00000000fffffffb       fffffa80039b1a20  (pci)
          00000000fffffffc - 00000000fffffffc       fffffa80039b0a20  (pci)
          00000000fffffffd - 00000000fffffffd       fffffa80039b7a20  (pci)
          00000000fffffffe - 00000000fffffffe       fffffa80039b7060  (pci)
        Possible allocation:
          &amp;lt; none &amp;gt;&lt;/pre&gt;

&lt;p&gt;The large numbers for IRQs are placeholders for MSI assignments, which in this machine are all PCI Express root ports.&lt;/p&gt;

&lt;p&gt;!acpiirqarb tells us about the other internal arbiter state, including IDT assignments on every core and state of the ACPI link nodes, which exist but aren’t used in APIC mode in this machine.&amp;#160; It also details all the I/O APICs in the machine, including the metadata on all the inputs.&lt;/p&gt;

&lt;p&gt;The “not on bus” claims are interesting.&amp;#160; They’re the inverse of the IDT entries that got claimed above in the root arbiter.&amp;#160; It means, essentially, that ACPI can’t give them out because it doesn’t own them.&lt;/p&gt;

&lt;pre&gt;0: kd&amp;gt; !acpiirqarb


Processor 0 (0, 0):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000061 - 0000000000000061 S  
      0000000000000061 - 0000000000000061 S B   fffffa80039ac060  (pciide)  A:fffff8a00189b230 IRQ:13
      0000000000000061 - 0000000000000061 S B   fffffa80039b2a20  (usbuhci)  A:fffff8a0018d8e90 IRQ:13
      0000000000000061 - 0000000000000061 S B   fffffa80039afa20  (usbuhci)  A:fffff8a0001986b0 IRQ:13
    0000000000000081 - 0000000000000081   D   fffffa80039a05c0   A:fffff8a0018f74e0 IRQ:2
    00000000000000a0 - 00000000000000a1   D   fffffa80039b7a20  (pci)  A:fffff8a0017eaa10 IRQ:fffffffd
    00000000000000a2 - 00000000000000a2 S B   fffffa80039b2060  (usbuhci)  A:fffff8a0006cc860 IRQ:15
    00000000000000b1 - 00000000000000b1 S B   fffffa8003c4dbd0  (ACPI)  A:fffff8a0001120f0 IRQ:9
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:10

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 1 (0, 1):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000061 - 0000000000000061 S  
      0000000000000061 - 0000000000000061 S B   fffffa80039ac060  (pciide)  A:fffff8a00189b230 IRQ:13
      0000000000000061 - 0000000000000061 S B   fffffa80039b2a20  (usbuhci)  A:fffff8a0018d8e90 IRQ:13
      0000000000000061 - 0000000000000061 S B   fffffa80039afa20  (usbuhci)  A:fffff8a0001986b0 IRQ:13
    0000000000000081 - 0000000000000081   D   fffffa80039a05c0   A:fffff8a0018f74e0 IRQ:2
    00000000000000a0 - 00000000000000a1   D   fffffa80039b7a20  (pci)  A:fffff8a0017eaa10 IRQ:fffffffd
    00000000000000a2 - 00000000000000a2 S B   fffffa80039b2060  (usbuhci)  A:fffff8a0006cc860 IRQ:15
    00000000000000b1 - 00000000000000b1 S B   fffffa8003c4dbd0  (ACPI)  A:fffff8a0001120f0 IRQ:9
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 2 (0, 2):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000061 - 0000000000000061 S  
      0000000000000061 - 0000000000000061 S B   fffffa80039ac060  (pciide)  A:fffff8a00189b230 IRQ:13
      0000000000000061 - 0000000000000061 S B   fffffa80039b2a20  (usbuhci)  A:fffff8a0018d8e90 IRQ:13
      0000000000000061 - 0000000000000061 S B   fffffa80039afa20  (usbuhci)  A:fffff8a0001986b0 IRQ:13
    0000000000000081 - 0000000000000081   D   fffffa80039a05c0   A:fffff8a0018f74e0 IRQ:2
    00000000000000a0 - 00000000000000a1   D   fffffa80039b7a20  (pci)  A:fffff8a0017eaa10 IRQ:fffffffd
    00000000000000a2 - 00000000000000a2 S B   fffffa80039b2060  (usbuhci)  A:fffff8a0006cc860 IRQ:15
    00000000000000b1 - 00000000000000b1 S B   fffffa8003c4dbd0  (ACPI)  A:fffff8a0001120f0 IRQ:9
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 3 (0, 3):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000061 - 0000000000000061 S  
      0000000000000061 - 0000000000000061 S B   fffffa80039ac060  (pciide)  A:fffff8a00189b230 IRQ:13
      0000000000000061 - 0000000000000061 S B   fffffa80039b2a20  (usbuhci)  A:fffff8a0018d8e90 IRQ:13
      0000000000000061 - 0000000000000061 S B   fffffa80039afa20  (usbuhci)  A:fffff8a0001986b0 IRQ:13
    0000000000000081 - 0000000000000081   D   fffffa80039a05c0   A:fffff8a0018f74e0 IRQ:2
    00000000000000a0 - 00000000000000a1   D   fffffa80039b7a20  (pci)  A:fffff8a0017eaa10 IRQ:fffffffd
    00000000000000a2 - 00000000000000a2 S B   fffffa80039b2060  (usbuhci)  A:fffff8a0006cc860 IRQ:15
    00000000000000b1 - 00000000000000b1 S B   fffffa8003c4dbd0  (ACPI)  A:fffff8a0001120f0 IRQ:9
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 4 (0, 4):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000051 - 0000000000000051 S  
      0000000000000051 - 0000000000000051 S B   fffffa80039ab060  (pciide)  A:fffff8a0006eaa10 IRQ:12
      0000000000000051 - 0000000000000051 S B   fffffa80039b1060  (usbehci)  A:fffff8a0006d83e0 IRQ:12
      0000000000000051 - 0000000000000051 S B   fffffa80039ae060  (usbuhci)  A:fffff8a0006c5a80 IRQ:12
    0000000000000090 - 0000000000000091   D   fffffa8003997a20  (pci)  A:fffff8a0006edeb0 IRQ:fffffff9
    00000000000000a0 - 00000000000000a0   D   fffffa80039b0060  (pci)  A:fffff8a001909750 IRQ:fffffffa
    00000000000000a1 - 00000000000000a1   D   fffffa80039a0c20   A:fffff8a00187f840 IRQ:8
    00000000000000b0 - 00000000000000b0   D   fffffa80039b0a20  (pci)  A:fffff8a001890b50 IRQ:fffffffc
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 5 (0, 5):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000051 - 0000000000000051 S  
      0000000000000051 - 0000000000000051 S B   fffffa80039ab060  (pciide)  A:fffff8a0006eaa10 IRQ:12
      0000000000000051 - 0000000000000051 S B   fffffa80039b1060  (usbehci)  A:fffff8a0006d83e0 IRQ:12
      0000000000000051 - 0000000000000051 S B   fffffa80039ae060  (usbuhci)  A:fffff8a0006c5a80 IRQ:12
    0000000000000090 - 0000000000000091   D   fffffa8003997a20  (pci)  A:fffff8a0006edeb0 IRQ:fffffff9
    00000000000000a0 - 00000000000000a0   D   fffffa80039b0060  (pci)  A:fffff8a001909750 IRQ:fffffffa
    00000000000000a1 - 00000000000000a1   D   fffffa80039a0c20   A:fffff8a00187f840 IRQ:8
    00000000000000b0 - 00000000000000b0   D   fffffa80039b0a20  (pci)  A:fffff8a001890b50 IRQ:fffffffc
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 6 (0, 6):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000051 - 0000000000000051 S  
      0000000000000051 - 0000000000000051 S B   fffffa80039ab060  (pciide)  A:fffff8a0006eaa10 IRQ:12
      0000000000000051 - 0000000000000051 S B   fffffa80039b1060  (usbehci)  A:fffff8a0006d83e0 IRQ:12
      0000000000000051 - 0000000000000051 S B   fffffa80039ae060  (usbuhci)  A:fffff8a0006c5a80 IRQ:12
    0000000000000090 - 0000000000000091   D   fffffa8003997a20  (pci)  A:fffff8a0006edeb0 IRQ:fffffff9
    00000000000000a0 - 00000000000000a0   D   fffffa80039b0060  (pci)  A:fffff8a001909750 IRQ:fffffffa
    00000000000000a1 - 00000000000000a1   D   fffffa80039a0c20   A:fffff8a00187f840 IRQ:8
    00000000000000b0 - 00000000000000b0   D   fffffa80039b0a20  (pci)  A:fffff8a001890b50 IRQ:fffffffc
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 7 (0, 7):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000051 - 0000000000000051 S  
      0000000000000051 - 0000000000000051 S B   fffffa80039ab060  (pciide)  A:fffff8a0006eaa10 IRQ:12
      0000000000000051 - 0000000000000051 S B   fffffa80039b1060  (usbehci)  A:fffff8a0006d83e0 IRQ:12
      0000000000000051 - 0000000000000051 S B   fffffa80039ae060  (usbuhci)  A:fffff8a0006c5a80 IRQ:12
    0000000000000090 - 0000000000000091   D   fffffa8003997a20  (pci)  A:fffff8a0006edeb0 IRQ:fffffff9
    00000000000000a0 - 00000000000000a0   D   fffffa80039b0060  (pci)  A:fffff8a001909750 IRQ:fffffffa
    00000000000000a1 - 00000000000000a1   D   fffffa80039a0c20   A:fffff8a00187f840 IRQ:8
    00000000000000b0 - 00000000000000b0   D   fffffa80039b0a20  (pci)  A:fffff8a001890b50 IRQ:fffffffc
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 8 (0, 8):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000071 - 0000000000000071 S  
      0000000000000071 - 0000000000000071 S B   fffffa800399b060  (pciide)  A:fffff8a000154d20 IRQ:10
      0000000000000071 - 0000000000000071 S B   fffffa80039b3a20  (usbuhci)  A:fffff8a0000a0b20 IRQ:10
    0000000000000091 - 0000000000000091   D   fffffa80039a07e0   A:fffff8a00193a8a0 IRQ:d
    00000000000000a0 - 00000000000000a0   D   fffffa80039b1a20  (pci)  A:fffff8a00193a870 IRQ:fffffffb
    00000000000000b0 - 00000000000000b1   D   fffffa80039b7060  (pci)  A:fffff8a00197a3e0 IRQ:fffffffe
    00000000000000b2 - 00000000000000b2 S  
      00000000000000b2 - 00000000000000b2 S B   fffffa80039aea20  (usbehci)  A:fffff8a00197a3b0 IRQ:17
      00000000000000b2 - 00000000000000b2 S B   fffffa80039af060  (usbuhci)  A:fffff8a0011c5460 IRQ:17
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 9 (0, 9):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000071 - 0000000000000071 S  
      0000000000000071 - 0000000000000071 S B   fffffa800399b060  (pciide)  A:fffff8a000154d20 IRQ:10
      0000000000000071 - 0000000000000071 S B   fffffa80039b3a20  (usbuhci)  A:fffff8a0000a0b20 IRQ:10
    0000000000000091 - 0000000000000091   D   fffffa80039a07e0   A:fffff8a00193a8a0 IRQ:d
    00000000000000a0 - 00000000000000a0   D   fffffa80039b1a20  (pci)  A:fffff8a00193a870 IRQ:fffffffb
    00000000000000b0 - 00000000000000b1   D   fffffa80039b7060  (pci)  A:fffff8a00197a3e0 IRQ:fffffffe
    00000000000000b2 - 00000000000000b2 S  
      00000000000000b2 - 00000000000000b2 S B   fffffa80039aea20  (usbehci)  A:fffff8a00197a3b0 IRQ:17
      00000000000000b2 - 00000000000000b2 S B   fffffa80039af060  (usbuhci)  A:fffff8a0011c5460 IRQ:17
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 10 (0, 10):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000071 - 0000000000000071 S  
      0000000000000071 - 0000000000000071 S B   fffffa800399b060  (pciide)  A:fffff8a000154d20 IRQ:10
      0000000000000071 - 0000000000000071 S B   fffffa80039b3a20  (usbuhci)  A:fffff8a0000a0b20 IRQ:10
    0000000000000091 - 0000000000000091   D   fffffa80039a07e0   A:fffff8a00193a8a0 IRQ:d
    00000000000000a0 - 00000000000000a0   D   fffffa80039b1a20  (pci)  A:fffff8a00193a870 IRQ:fffffffb
    00000000000000b0 - 00000000000000b1   D   fffffa80039b7060  (pci)  A:fffff8a00197a3e0 IRQ:fffffffe
    00000000000000b2 - 00000000000000b2 S  
      00000000000000b2 - 00000000000000b2 S B   fffffa80039aea20  (usbehci)  A:fffff8a00197a3b0 IRQ:17
      00000000000000b2 - 00000000000000b2 S B   fffffa80039af060  (usbuhci)  A:fffff8a0011c5460 IRQ:17
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;


Processor 11 (0, 11):
Device Object: 0000000000000000
Current IDT Allocation:
    0000000000000000 - 0000000000000050       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0
    0000000000000071 - 0000000000000071 S  
      0000000000000071 - 0000000000000071 S B   fffffa800399b060  (pciide)  A:fffff8a000154d20 IRQ:10
      0000000000000071 - 0000000000000071 S B   fffffa80039b3a20  (usbuhci)  A:fffff8a0000a0b20 IRQ:10
    0000000000000091 - 0000000000000091   D   fffffa80039a07e0   A:fffff8a00193a8a0 IRQ:d
    00000000000000a0 - 00000000000000a0   D   fffffa80039b1a20  (pci)  A:fffff8a00193a870 IRQ:fffffffb
    00000000000000b0 - 00000000000000b1   D   fffffa80039b7060  (pci)  A:fffff8a00197a3e0 IRQ:fffffffe
    00000000000000b2 - 00000000000000b2 S  
      00000000000000b2 - 00000000000000b2 S B   fffffa80039aea20  (usbehci)  A:fffff8a00197a3b0 IRQ:17
      00000000000000b2 - 00000000000000b2 S B   fffffa80039af060  (usbuhci)  A:fffff8a0011c5460 IRQ:17
    00000000000000bf - ffffffffffffffff       00000000 &lt;not bus on&gt;  A:0000000000000000 IRQ:0

Possible IDT Allocation:
    &amp;lt; none &amp;gt;

Interrupt Controller (Inputs: 0x0-0x17  Dev: 0000000000000000):
       (00)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (01)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (02)Cur:IDT-81 Ref-1 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (03)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (04)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (05)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (06)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (07)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (08)Cur:IDT-a1 Ref-1 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (09)Cur:IDT-b1 Ref-1 lev hi   Pos:IDT-00 Ref-0 edg hi 
       (0a)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (0b)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (0c)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (0d)Cur:IDT-91 Ref-1 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (0e)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (0f)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (10)Cur:IDT-71 Ref-2 lev low  Pos:IDT-00 Ref-0 edg hi 
       (11)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (12)Cur:IDT-51 Ref-3 lev low  Pos:IDT-00 Ref-0 edg hi 
       (13)Cur:IDT-61 Ref-3 lev low  Pos:IDT-00 Ref-0 edg hi 
       (14)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (15)Cur:IDT-a2 Ref-1 lev low  Pos:IDT-00 Ref-0 edg hi 
       (16)Cur:IDT-00 Ref-0 edg hi   Pos:IDT-00 Ref-0 edg hi 
       (17)Cur:IDT-b2 Ref-2 lev low  Pos:IDT-00 Ref-0 edg hi 

Link Node: LNKA
       Current   IRQ: 0x0 - 0 reference(s)
       Possible  IRQ: 0x0 - 0 reference(s)
       Preferred IRQ: 0xffffffff - ResourceOverride (IO_List) 0000000000000000

Link Node: LNKB
       Current   IRQ: 0x0 - 0 reference(s)
       Possible  IRQ: 0x0 - 0 reference(s)
       Preferred IRQ: 0xffffffff - ResourceOverride (IO_List) 0000000000000000

Link Node: LNKC
       Current   IRQ: 0x0 - 0 reference(s)
       Possible  IRQ: 0x0 - 0 reference(s)
       Preferred IRQ: 0xffffffff - ResourceOverride (IO_List) 0000000000000000

Link Node: LNKD
       Current   IRQ: 0x0 - 0 reference(s)
       Possible  IRQ: 0x0 - 0 reference(s)
       Preferred IRQ: 0xffffffff - ResourceOverride (IO_List) 0000000000000000

Link Node: LNKE
       Current   IRQ: 0x0 - 0 reference(s)
       Possible  IRQ: 0x0 - 0 reference(s)
       Preferred IRQ: 0xffffffff - ResourceOverride (IO_List) 0000000000000000

Link Node: LNKF
       Current   IRQ: 0x0 - 0 reference(s)
       Possible  IRQ: 0x0 - 0 reference(s)
       Preferred IRQ: 0xffffffff - ResourceOverride (IO_List) 0000000000000000

Link Node: LNKG
       Current   IRQ: 0x0 - 0 reference(s)
       Possible  IRQ: 0x0 - 0 reference(s)
       Preferred IRQ: 0xffffffff - ResourceOverride (IO_List) 0000000000000000

Link Node: LNKH
       Current   IRQ: 0x0 - 0 reference(s)
       Possible  IRQ: 0x0 - 0 reference(s)
       Preferred IRQ: 0xffffffff - ResourceOverride (IO_List) 0000000000000000&lt;/pre&gt;

&lt;p&gt;In conclusion, arbitration is complicated and we keep adjusting it.&amp;#160; Windows 7 actually added a little bit of knowledge about VT-d to interrupt arbitration so that we could easily go beyond 64 cores.&amp;#160; &lt;/p&gt;

&lt;p&gt;People have been asking us for years to document the interfaces so that non-Microsoft-employed driver writers could write their own arbiters.&amp;#160; This would be most useful for “converged NICs” where a single PCI function exposes a bus driver which in turn exposes a NIC, an RDMA device, an iSCSI initiator and/or an FCoE HBA.&amp;#160; These bus drivers jump through many hoops to do second-level interrupt dispatch for their children, which they wouldn’t have to do if they could write an interrupt arbiter.&lt;/p&gt;

&lt;p&gt;It’s particularly difficult, though, to do interrupt arbitration in a distributed manner.&amp;#160; I/O port or memory arbitration can be done locally on the bus related to the device.&amp;#160; But interrupts are often run as side-band signals straight from one part of the motherboard to another.&amp;#160; It’s difficult to prove that you can make this code work if it’s decentralized.&lt;/p&gt;

&lt;p&gt;We wrote a simple bus driver that claims resources and doles them out for children.&amp;#160; It’s called “MF.sys” and it works so long as the resources you need for one child are completely disjoint from the resources you need for another child.&amp;#160; This tends not to be the case with converged NICs.&amp;#160; Some register or some interrupt gets used for some shared purpose.&lt;/p&gt;

&lt;p&gt;For now though, the best answer I can give is that all this information is mostly useful for debugging. &lt;/p&gt;

&lt;p&gt;- Jake Oshins&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10008841" width="1" height="1"&gt;</description></item><item><title>Translation and Windows</title><link>http://blogs.msdn.com/b/doronh/archive/2010/05/06/translation-and-windows.aspx</link><pubDate>Thu, 06 May 2010 22:12:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10008812</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=10008812</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2010/05/06/translation-and-windows.aspx#comments</comments><description>&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;&lt;STRONG&gt;Arbitration and Translation, Part 2&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;Building on &lt;SPAN style="COLOR: #1f497d"&gt;&lt;A href="http://blogs.msdn.com/doronh/archive/2010/05/05/arbitration-and-translation-part-1.aspx"&gt;yesterday’s post&lt;/A&gt;&lt;/SPAN&gt;, I’m going to try to explain how Windows copes with machines with strange resource translations.&amp;nbsp; I’ll use two examples in this post, one related to I/O &lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;port resources and one related to interrupts.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;Just for convenience, I’ll duplicate the diagram from my last post, which diagramed the address space translations in a fairly complex multi-PCI-root machine.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;IMG style="WIDTH: 345px; HEIGHT: 530px" align=middle src="http://blogs.msdn.com/photos/doronh/images/10008109/original.aspx" width=345 height=530 mce_src="http://blogs.msdn.com/photos/doronh/images/10008109/original.aspx"&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;Into such a machine, imagine that there’s a NIC plugged into the secondary root PCI bus and an UART plugged into the ISA/LPC bus, probably soldered onto the motherboard.&amp;nbsp; The resulting PnP tree would look like this:&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;IMG style="WIDTH: 512px; HEIGHT: 908px" src="http://blogs.msdn.com/photos/doronh/images/10008807/original.aspx" width=512 height=908 mce_src="http://blogs.msdn.com/photos/doronh/images/10008807/original.aspx"&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;Of course, a fully populated PnP tree would be much more complicated.&amp;nbsp; If you want to see the real thing, in full, look in Device Manager and choose “Show Devices by Connection.”&amp;nbsp; (I took flack a few years ago for admitting that internally, we called this “Show as God Intended.”&amp;nbsp; I still think of it that way, even though I understand why no user could use it that way.)&amp;nbsp; Alternatively, you can see the same thing in the kernel debugger by typing “!devnode 0 1”.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;For this example, assume the following things are true:&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l1 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;The UART is not an ISA PnP device.&amp;nbsp; It’s enumerated by the ACPI BIOS.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l1 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;The ACPI BIOS claims (through the _PRS object under the UART) that the device requires eight consecutive I/O ports, at one of several locations.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l1 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;The ACPI BIOS claims that the device can use one of two IRQs, 2 or 5.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l1 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;The ACPI BIOS contains a “control method” (labeled _SRS) which allows the ACPI driver to set the resources of the device.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l1 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;This device lies under the PCI root bus which is “Bus 0” in the example above.&amp;nbsp; It has a native I/O port address space.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;These things will cause the ACPI driver to respond to IRP_MN_QUERY_RESOURCE_REQUIREMENTS for this device with a structure that means “this device should be assigned one of three I/O port blocks which is eight bytes long and it needs one IRQ, which can be either 2 or 5, not shareable, edge triggered.”&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;For a full description on how this statement is constructed, see the &lt;/FONT&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/ff550609(v=VS.85).aspx"&gt;&lt;FONT size=3 face=Calibri&gt;documentation on IO_RESOURCE_REQUIREMENTS_LIST in the WDK&lt;/FONT&gt;&lt;/A&gt;&lt;FONT size=3 face=Calibri&gt;. &amp;nbsp;In short, I/O Resource Requirements lists are the “set of all possible sets of resources that a device could use.” &amp;nbsp;&amp;nbsp;For more detail on ACPI, see &lt;/FONT&gt;&lt;A href="http://acpi.info/spec40.htm"&gt;&lt;FONT color=#0000ff size=3 face=Calibri&gt;the spec&lt;/FONT&gt;&lt;/A&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;As for the NIC, assume the following:&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo2" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;It is a PCI device, not PCI-X or PCI Express.&amp;nbsp; The upstream bridge is a PCIe to PCI-X bridge, which allows PCI devices to be plugged in.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo2" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;It has one PCI Base Address register and that BAR is of type “I/O,” implying that it must use the I/O address space. &amp;nbsp;That BAR also implies that the registers of the NIC lie in a block that is 0x100 bytes long.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo2" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;It has a “1” in its Interrupt Pin register, implying that it will trigger its INTA signal with level-triggered semantics.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo2" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;This device lies under the PCI root “Bus 1” above.&amp;nbsp; It has its I/O port space mapped into memory space.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;These things will cause the PCI driver to respond to IRP_MN_QUERY_RESOURCE_REQUIREMENTS with “this device should be assigned one block of I/O ports which is naturally aligned and 0x100 bytes long. &amp;nbsp;It can use any single IRQ, shareable and level-triggered.”&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Upon receiving the response to these IRPs, the PnP manager starts trying to satisfy the requirements.&amp;nbsp; To do this, it works its way toward the root of the PnP tree looking first for bus drivers which expose an “arbiter interface” for each device type.&amp;nbsp; It also queries for a “translator interface.”&amp;nbsp; I’ll cover arbiters in my next post.&amp;nbsp; Today’s is really only about translators.&amp;nbsp; But they’re somewhat intertwined, so I’ll define arbiters today as “something which knows about a specific resource type and knows the bus-local rules for deciding how these resources are allocated.”&amp;nbsp; Allocating I/O ports on a PCI bus is different from allocating them on an ISA bus.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Once the PnP manager has searched to the root of the PnP tree, it will have found some interfaces.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;IMG style="WIDTH: 512px; HEIGHT: 1022px" align=middle src="http://blogs.msdn.com/photos/doronh/images/10008808/original.aspx" width=512 height=1022 mce_src="http://blogs.msdn.com/photos/doronh/images/10008808/original.aspx"&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The exact details have changed a little bit over the years and from release to release.&amp;nbsp; I believe that I’ve accurately represented the state of affairs since Vista.&amp;nbsp; Incidentally, you can see these in the debugger by typing “!translator” and “!arbiter.”&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Translating from ISA to Interrupt Controller Input Pins&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Since the ISA/LPC bridge devnode responded with an interrupt translator interface, the PnP manager needs to translate interrupts from ISA to the parent PCI.&amp;nbsp; To really understand what this means, we need to have a little history lesson.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Thirtyish years ago, somebody at IBM decided that they were going to build a “personal computer” which had a single interrupt controller chip called the “8259 Programmable Interrupt Controller (PIC).”&amp;nbsp; It had eight inputs.&amp;nbsp; Each of these inputs was exposed in every expansion slot.&amp;nbsp; The output pins were directly connected to the processor.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;A few years later, some other guy at IBM designed the “IBM PC/AT.”&amp;nbsp; When they built the AT, they used an 80286 processor which had a sixteen-bit expansion bus.&amp;nbsp; They also added a few I/O devices.&amp;nbsp; Since the expansion bus was wider, and since they needed more interrupt controller inputs now, they added a second 8259 to the machine.&amp;nbsp; This second one was chained onto the first one.&amp;nbsp; Its output pin was connected to IRQ 2 on the first one. &amp;nbsp;Interestingly IRQ2 was still exposed in the older part of the expansion bus, so they connected that signal to Input 1 on the second PIC.&amp;nbsp; So any old eight-bit device which was triggering the IRQ2 pin on the bus was actually going to cause IRQ9 to interrupt the processor.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Fast forward twenty-six or -seven years.&amp;nbsp; We still have code to comprehend this, and it’s called a “translator interface for interrupts on the ISA devnode.”&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The PnP manager invokes the translator from the ISA devnode and hands it two IO_RESOURCE_REQUIREMENTS, one saying “IRQ 2” and one saying “IRQ 5,” both edge-triggered and non-shareable.&amp;nbsp; The ISA devnode modifies the first one to say IRQ 9.&amp;nbsp; It leaves everything else alone.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The PnP manager keeps looking toward the root of the tree.&amp;nbsp; The PCI driver really knows very little about interrupts.&amp;nbsp; (This is because the PCI spec is nearly silent on the topic.&amp;nbsp; Don’t get me started on how many years I’ve spent on filling that gap.)&amp;nbsp; So the PCI driver doesn’t provide translator or arbiter interfaces for interrupts.&amp;nbsp; The ACPI driver, on the other hand, knows quite a bit about interrupts, as the ACPI spec has quite a bit of text allowing BIOSes to describe the ways that the motherboard designer handled interrupts in a specific machine.&amp;nbsp; So the ACPI driver exposes both interfaces.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The PnP manager, at this point, can stop translating interrupts from both devices because it has reached a common parent in the PnP with exposes an arbiter for interrupts.&amp;nbsp; The arbiter is then invoked to choose which resources each device will be assigned.&amp;nbsp; (Again, more on that in my next post.)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Translating from I/O Ports – Step 1&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;For both devices, the PnP manager starts looking for translators and arbiters for the device’s I/O port claims.&amp;nbsp; It finds arbiters at the PCI layer, as PCI knows how to sub-allocate I/O port space to its children.&amp;nbsp; Those rules are, thankfully, laid out quite clearly in the PCI spec, and aside from a few chipsets where the chipset designer didn’t think that the PCI spec applied to him, we can successfully figure out what configuration will work at that level.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Note that no translation has happened yet.&amp;nbsp; We’re still talking about I/O ports as viewed on the buses which contain the devices, where the bus cycles will definitely be tagged as “I/O.”&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;B&gt;Translation after Arbitration&lt;/B&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Assume that for this example, the arbiters picked this set of choices:&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;UART: &amp;nbsp; IRQ 9 and I/O ports 0x2040 through 0x2047&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;NIC: &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IRQ 11 and I/O ports 0x2000 through 0x20FF&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;No, that’s not a typo.&amp;nbsp; Their I/O port claims actually seem like they overlap. This is fine, as they’re disjoint address spaces on different buses.&amp;nbsp; (This can’t really happen on most PCs, but it can and does happen on some machines.&amp;nbsp; See my last post.)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Now that the PnP manager has a resource assignment, it has to figure out how to present that choice to two separate audiences with two very different sets of needs.&amp;nbsp; The first audience is the bus drivers.&amp;nbsp; Now that we’ve chosen a resource set for each device, we need to program the devices so that they actually embody those choices.&amp;nbsp; For the PCI device, this involves writing 0x2000 to its I/O BAR. &amp;nbsp;For the LPC-attached UART, this involves executing the _SRS control method in the ACPI namespace underneath the UART device.&amp;nbsp; Both of them need to be in bus-relative terms.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The second audience is the functional drivers, for the NIC and the UART.&amp;nbsp; They don’t need to see the bus-relative view, as the driver can’t really directly generate bus traffic. &amp;nbsp;The FDOs are made up of driver code running on the processor, so they need the processor-relative view of those resource claims.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;To achieve that, I need to show you something we internally call the “checkmark diagram.”&amp;nbsp; To truly understand this diagram, I have to apologize for the fact that, in house, all the PnP trees are drawn on whiteboards with the “root” at the top and the devices are leaves down at the bottom.&amp;nbsp; This corresponds nicely with diagrams of physical machines where the processors and memory are at the top and the I/O devices hang down below like little appendages. &amp;nbsp;The DDK/WDK tech writers convinced us that all public documentation should have the “root” of a “tree” firmly planted in the “ground.”&amp;nbsp; Oh well.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;IMG style="WIDTH: 352px; HEIGHT: 523px" align=middle src="http://blogs.msdn.com/photos/doronh/images/10008809/original.aspx" width=352 height=523 mce_src="http://blogs.msdn.com/photos/doronh/images/10008809/original.aspx"&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;I’ve already described steps 1 through 3.&amp;nbsp; After arbitration, though, the PnP manager has to put these claims back in terms of the I/O bus. &amp;nbsp;The only resource that went through translation on the way to arbitration was the IRQ for the UART.&amp;nbsp; So now the translator interface from the ISA devnode reverses that process and changes that 9 back into a 2.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;So the resulting “raw resource” assignments are now in bus-relative terms.&amp;nbsp; They’re also now in terms of CM Resource Lists.&amp;nbsp; Those are &lt;/FONT&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/ff541994(VS.85).aspx"&gt;&lt;FONT color=#0000ff size=3 face=Calibri&gt;documented in the WDK&lt;/FONT&gt;&lt;/A&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;, too.&amp;nbsp; Again, in short, a CM Resource List is a single complete set of resources that a device either is using or could be using.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The raw resource lists for the devices are:&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;UART: &amp;nbsp; IRQ 2 and I/O Ports 0x2040 through 0x2047&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;NIC: &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IRQ 11 and I/O Ports 0x2000 through 0x20ff&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Lastly, the PnP manager goes back to toward the root of the PnP tree, passing the various resource assignments to any translators that may be at each node of the tree, trying to build a different CM Resource List, this time in terms of the processor.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The ISA devnode’s Interrupt translator immediately reverses itself again, and changes that 2 back into a 9.&amp;nbsp; But there’s another interrupt translator in the tree, too, at the ACPI level. &amp;nbsp;That translator is actually privy to some internal choices that the interrupt arbiter made, involving the IRQL and IDT entries (and in Windows 7 and later, IOMMU Interrupt Redirection Table entries) that the arbiter chose. &amp;nbsp;So that translator can translate into processor-relative terms.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;For the root PCI bus which maps its I/O Port space into processor memory, ACPI supplies an I/O Port translator interface.&amp;nbsp; (It knows to do this based on contents of the ACPI namespace.)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Thus the “translated resource lists” for these end up looking like this:&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;UART:&amp;nbsp;&amp;nbsp; IRQL 11, Vector 0xb3, Affinity (target processor set) 0xF0 and I/O Ports 0x2040 through 0x2047&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;NIC:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IRQL 10, Vector 0xa9, Affinity 0x0F and memory range 0x1’00002000 through 0x1’000020FF&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;B&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;B&gt;Presenting Resources to Drivers&lt;/B&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;When all of this is complete, there are two CM Resource Lists in the PnP manager for the device.&amp;nbsp; Both get sent as part of &lt;/FONT&gt;&lt;A href="http://msdn.microsoft.com/en-us/library/ff551749(VS.85).aspx"&gt;&lt;FONT color=#0000ff size=3 face=Calibri&gt;IRP_MN_START_DEVICE&lt;/FONT&gt;&lt;/A&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;As explained in &lt;SPAN style="COLOR: #1f497d"&gt;&lt;A href="http://blogs.msdn.com/doronh/archive/2010/05/05/arbitration-and-translation-part-1.aspx"&gt;my last post&lt;/A&gt;&lt;/SPAN&gt;, the driver contract is that the bus driver (or a bus filter like ACPI, sometimes) programs the device using the raw resources.&amp;nbsp; The function driver calls MmMapIoSpace, IoConnectInterrupt, etc., using only the translated resources.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;My next post will go into detail on what arbiters do.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;- Jake Oshins&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10008812" width="1" height="1"&gt;</description></item><item><title>Arbitration and Translation, Part 1</title><link>http://blogs.msdn.com/b/doronh/archive/2010/05/05/arbitration-and-translation-part-1.aspx</link><pubDate>Wed, 05 May 2010 22:27:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:10008111</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=10008111</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2010/05/05/arbitration-and-translation-part-1.aspx#comments</comments><description>&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;A while back Jake Oshins answered a question on NTDEV about bus arbitration and afterwards I asked him if he could write a couple of posts about it for the blog. Here is part 1.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;History Lesson&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;In the history of computing, most machines weren’t PCs.&amp;nbsp; PCs, and the related “industry standard” server platforms, may constitute a huge portion of the computers that have been sold in the last couple of decades, but even during that time, there have been countless machines, both big and small, which weren’t PCs.&amp;nbsp; Windows, at least those variants which are derived from Windows NT, (which include Windows XP and everything since,) was originally targeted at non-PC machines, specifically those with a MIPS processor and a custom motherboard which was designed by in-house at Microsoft.&amp;nbsp; In the fifteen years that followed that machine, NT ran on a whole pile of other machines, many with different processor architectures.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;My own career path involved working on the port of Windows NT to PowerPC machines.&amp;nbsp; I wrote HALs and worked on device drivers for several RS/6000 workstations and servers which (briefly) ran NT.&amp;nbsp; When I came to Microsoft from IBM, the NT team was just getting into the meat of the PnP problem.&amp;nbsp; The Windows 95 team had already done quite a bit to understand PnP, but their problem space was really strongly constrained.&amp;nbsp; Win95 only ran on PCs, and only those with a single processor and a single root PCI bus.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Very quickly, I got sucked into the discussion about how to apply PnP concepts to machines which were not PCs, and also how to extend the driver model in ways that would continue to make it possible to have one driver which ran on any machine, PC or not.&amp;nbsp; If the processor target wasn’t x86, you’d need to recompile it.&amp;nbsp; But the code itself wouldn’t need changing.&amp;nbsp; If the processor target was x86, even if the machine wasn’t strictly a PC, your driver would just run.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;In order to talk about non-PC bus architectures, I want to briefly cover PC buses, for contrast.&amp;nbsp; PC’s have two address spaces, I/O and memory.&amp;nbsp; You use different instructions to access each.&amp;nbsp; I/O uses “IN, OUT, INS, and OUTS.”&amp;nbsp; That’s it.&amp;nbsp; Memory uses just about any other instruction, at least any that can involve a pointer.&amp;nbsp; I/O has no way of indirecting it, like virtual memory indirects memory.&amp;nbsp; That’s all I’ll say about those here.&amp;nbsp; If you want more detail, there have been hundreds of good explanations for this.&amp;nbsp; My favorite comes from Mindshare’s &lt;/FONT&gt;&lt;A href="http://mindshare.com/shop/?c=b&amp;amp;section=0BA211E3132B"&gt;&lt;FONT color=#0000ff size=3 face=Calibri&gt;ISA System Architecture&lt;/FONT&gt;&lt;/A&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;, although that’s partly because that one existed back when I didn’t fully understand the problem space.&amp;nbsp; Perhaps there are better ones now.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;In the early PC days, the processor bus and the I/O bus weren’t really separate.&amp;nbsp; There were distinctions, but those weren’t strongly delineated until PCI came along, in the early ‘90s.&amp;nbsp; PCI was successful and enduring because, in no small part, it was defined entirely without reference to a specific processor or processor architecture.&amp;nbsp; The &lt;/FONT&gt;&lt;A href="http://www.pcisig.com/specifications/conventional/"&gt;&lt;FONT color=#0000ff size=3 face=Calibri&gt;PCI Spec&lt;/FONT&gt;&lt;/A&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt; has almost completely avoided talking about anything that happens outside of the PCI bus.&amp;nbsp; This means, however, that any specific implementation has to have something which bridges the PCI spec to the processor bus.&amp;nbsp; (I’m saying “processor bus” loosely here to mean any system of interconnecting processors, memory and the non-cache-coherent I/O domains.&amp;nbsp; This sometimes gets referred to as a “North Bridge,” too.)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The processor bus then gets mapped onto the I/O subsystem, specifically one or more root PCI buses.&amp;nbsp; The following diagram shows a machine that has two root PCI buses (which is not at all typical this year, but was very typical of PC servers a decade ago.)&amp;nbsp; The specific addresses could change from motherboard to motherboard and were reported to the OS by the BIOS.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;IMG title="multi root PCI" alt="multi root PCI" align=middle src="http://blogs.msdn.com/photos/doronh/images/10008108/original.aspx" mce_src="http://blogs.msdn.com/photos/doronh/images/10008108/original.aspx"&gt;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="TEXT-ALIGN: center; MARGIN: 0in 0in 0pt" class=MsoNormal align=center&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /&gt;&lt;v:shapetype id=_x0000_t75 stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"&gt;&lt;v:stroke joinstyle="miter"&gt;&lt;/v:stroke&gt;&lt;v:formulas&gt;&lt;v:f eqn="if lineDrawn pixelLineWidth 0"&gt;&lt;/v:f&gt;&lt;v:f eqn="sum @0 1 0"&gt;&lt;/v:f&gt;&lt;v:f eqn="sum 0 0 @1"&gt;&lt;/v:f&gt;&lt;v:f eqn="prod @2 1 2"&gt;&lt;/v:f&gt;&lt;v:f eqn="prod @3 21600 pixelWidth"&gt;&lt;/v:f&gt;&lt;v:f eqn="prod @3 21600 pixelHeight"&gt;&lt;/v:f&gt;&lt;v:f eqn="sum @0 0 1"&gt;&lt;/v:f&gt;&lt;v:f eqn="prod @6 1 2"&gt;&lt;/v:f&gt;&lt;v:f eqn="prod @7 21600 pixelWidth"&gt;&lt;/v:f&gt;&lt;v:f eqn="sum @8 21600 0"&gt;&lt;/v:f&gt;&lt;v:f eqn="prod @7 21600 pixelHeight"&gt;&lt;/v:f&gt;&lt;v:f eqn="sum @10 21600 0"&gt;&lt;/v:f&gt;&lt;/v:formulas&gt;&lt;v:path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"&gt;&lt;/v:path&gt;&lt;o:lock aspectratio="t" v:ext="edit"&gt;&lt;/o:lock&gt;&lt;/v:shapetype&gt;&lt;v:shape style="WIDTH: 258.75pt; HEIGHT: 328.5pt" id=Picture_x0020_1 alt="Description: cid:image001.jpg@01CAEBAE.DB167780" type="#_x0000_t75" o:spid="_x0000_i1025"&gt;&lt;v:imagedata o:href="cid:image001.jpg@01CAEBAE.DB167780" src="file:///C:\Users\doronh\AppData\Local\Temp\msohtmlclip1\01\clip_image001.jpg"&gt;&lt;/v:imagedata&gt;&lt;/v:shape&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;You’ll notice that processor I/O space is pretty limited.&amp;nbsp; It’s even more limited when you look at the PCI to PCI bridge specification, which says that down-stream PCI busses must allocate chucks of I/O address space on 4K boundaries.&amp;nbsp; This means that there are only a few possible “slots” to allocate from and a relatively small number of PCI busses can allocate I/O address space at all.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Attempts to expand I/O Space&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Today, this lack of I/O space problem is mostly handled by creating devices which only use memory space (or memory-mapped I/O space as it’s sometimes called.)&amp;nbsp; But in the past, and in some current very-high-end machines, multiple PCI I/O spaces are mapped into a machine by mapping them into processor memory space rather than processor I/O space.&amp;nbsp; I’ve debugged many a machine that had a memory map like the following.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;IMG style="WIDTH: 345px; HEIGHT: 530px" align=middle src="http://blogs.msdn.com/photos/doronh/images/10008109/original.aspx" width=345 height=530 mce_src="http://blogs.msdn.com/photos/doronh/images/10008109/original.aspx"&gt;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="TEXT-ALIGN: center; MARGIN: 0in 0in 0pt" class=MsoNormal align=center&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;v:shape style="WIDTH: 258.75pt; HEIGHT: 397.5pt" id=Picture_x0020_2 alt="Description: cid:image002.jpg@01CAEBB2.DA29A9B0" type="#_x0000_t75" o:spid="_x0000_i1026"&gt;&lt;v:imagedata o:href="cid:image002.jpg@01CAEBB2.DA29A9B0" src="file:///C:\Users\doronh\AppData\Local\Temp\msohtmlclip1\01\clip_image002.jpg"&gt;&lt;/v:imagedata&gt;&lt;/v:shape&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;In this machine, you need to use memory instructions, complete with virtual address mappings, if you want to manipulate the registers of your device, as long as that device is on Root PCI Bus 1 or one of its children.&amp;nbsp; If your device is plugged into Root PCI Bus 0, then you use I/O instructions.&amp;nbsp; While that’s a little bit hard to code for (more on that later) it’s nice because each PCI bus has its full 16K of I/O address space.&amp;nbsp; &lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;In theory, the secondary root PCI buses can have even more than 16K of space.&amp;nbsp; The PCI spec allows for 32-bits of I/O space and devices are required to decode 32-bit addresses of I/O.&amp;nbsp; Since it’s all just mapped into processor memory space, which is large, you can have a really large I/O space.&amp;nbsp; In practice, though, many devices didn’t follow the spec and the one machine I’ve seen that depended on this capability had a very, very short list of compatible adapters.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;&lt;B&gt;Non-Intel Processors&lt;/B&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;If you’ve ever written code for a processor that Intel didn’t have a hand in designing, you’ve probably noticed that the concept of I/O address spaces is pretty rare elsewhere.&amp;nbsp; (Now please don’t write to me telling me about some machine that you worked on early in your career.&amp;nbsp; I’ve heard those stories.&amp;nbsp; I’ll even bore you with my own as penance for sending me yours.)&amp;nbsp; Let’s just stop the discussing by pointing out that MIPS, Alpha and PowerPC never had any notion of I/O address space and Itanic has an I/O space, but only if you look at it from certain angles.&amp;nbsp; And those are the set of non-x86 processors that NT has historically run on.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Chipset designers who deal with non-PC processors and non-PC chipsets often do something really similar to what was just described above where the north bridge translates I/O to memory, except that not even PCI Bus 0 has any native I/O space mapping.&amp;nbsp; All the root PCI buses map their I/O spaces into processor memory space.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;B&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Windows NT Driver Contract&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/B&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;About now, you’re probably itching to challenge my statement (above) where I said you could write a driver which runs just fine regardless of which sort of processor address space your device shows up in.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Interestingly, I’ve been working on HALs and drivers within Microsoft (and at IBM before that) for about 16 years now and I always knew that I understood the contract.&amp;nbsp; I also knew that few drivers not shipped with NT followed the contract.&amp;nbsp; What I didn’t know was that, even though the “rules” are more or less described in the old DDK docs, very few people outside of Microsoft had internalized those rules, and in fact one major driver consulting and teaching outfit (who shall remain nameless, but who’s initials are “OSR”) was actually teaching a different contract.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;After much discussion about this a few years ago, and from my own experience, I believe that it was essentially an unreasonable contract, in that it was untestable if you didn’t own a big-iron machine with weird translations or a non-PC machine running a minority processor.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;I’ll lay out the contract here, though, for the sake of completeness.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="mso-fareast-font-family: Calibri"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3 face=Calibri&gt;1.&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;There are “raw” resources and “translated” resources.&amp;nbsp; Raw resources are in terms of the I/O bus which contains the device.&amp;nbsp; Translated resources are in terms of the processor.&amp;nbsp; Every resource claim has both forms.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="mso-fareast-font-family: Calibri"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3 face=Calibri&gt;2.&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Bus drivers take raw resources and program the bus, the device or both so that the device registers show up at that set of addresses.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="mso-fareast-font-family: Calibri"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3 face=Calibri&gt;3.&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;Function drivers take the translated resources and use them in the driver code, as the code runs on the processor.&amp;nbsp; Function drivers must ignore the raw resource list.&amp;nbsp; Even if the function driver was written by a guy who is absolutely certain that his device appears in I/O space, because it is a PCI device with one Base Address Register of type I/O, the driver must still look at the resource type in the translated resources.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="mso-fareast-font-family: Calibri"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3 face=Calibri&gt;4.&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;If your device registers are in I/O space from the point of view of the processor, your translated resources will be presented as CmResourceTypePort.&amp;nbsp; If your translated resources are of this type, you must use “port” functions to access your device.&amp;nbsp; These functions have names that start with READ_PORT_ and WRITE_PORT_.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l0 level1 lfo1" class=MsoListParagraph&gt;&lt;SPAN style="mso-fareast-font-family: Calibri"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3 face=Calibri&gt;5.&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;If your device registers are in memory space from the point of view of the processor, your translated resources will be presented as CmResourceTypeMemory.&amp;nbsp; If they are of this type, you must first call MmMapIoSpace to get a virtual address for that physical address.&amp;nbsp; Then you use “memory” functions, with names that start with READ_REGISTER_ and WRITE_REGISTER_.&amp;nbsp; When your device gets stopped, you call MmUnmapIoSpace to release the virtual address space that you allocated above.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;This contract works.&amp;nbsp; (No, really, I’m certain.&amp;nbsp; I’ve written a lot of code that uses it.)&amp;nbsp; But it’s not an easy contract to code to, and I’ll lay out the issues:&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l1 level1 lfo2" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;The “PORT” functions and the “REGISTER” functions are not truly symmetric.&amp;nbsp; The forms that take a string and transfer it do different things.&amp;nbsp; The PORT functions assume the register is a FIFO. The REGISTER functions assume it’s a region of memory space that’s being referred to.&amp;nbsp; So you pretty much have to ignore the string forms of these and code your own with a loop.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l1 level1 lfo2" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT face=Calibri&gt;&lt;FONT size=3&gt;All access to your device either has an “if port then, else memory” structure to it.&amp;nbsp; Or you create a function table that access the device, with variant port/memory forms.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="TEXT-INDENT: -0.25in; MARGIN: 0in 0in 0pt 0.5in; mso-list: l1 level1 lfo2" class=MsoListParagraph&gt;&lt;SPAN style="FONT-FAMILY: Symbol; mso-fareast-font-family: Symbol; mso-bidi-font-family: Symbol"&gt;&lt;SPAN style="mso-list: Ignore"&gt;&lt;FONT size=3&gt;·&lt;/FONT&gt;&lt;SPAN style="FONT: 7pt 'Times New Roman'"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/SPAN&gt;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;The ever-so-popular driver structure where you define your registers in a C-style struct and then call MmMapIoSpace and lay your struct over top of your device memory just doesn’t work in any machine that translates device memory to processor I/O.&amp;nbsp; (Yes, I’ve even seen one of those.)&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;In the end, most driver writers outside of the NT team either ignore the contract because they are unaware of it, or ignore it because they have no way to test their driver in non-PC machines.&amp;nbsp; Imagine telling your boss that you have functions which deal with I/O mapped into processor memory in your driver but you’ve never seen them run.&amp;nbsp; So he can either ship untested code or pony up and buy you an HP Superdome Itanic, fully populated with 256 processors just to test on.&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=10008111" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDM/">WDM</category></item><item><title>WDK v7.1 is now available</title><link>http://blogs.msdn.com/b/doronh/archive/2010/02/28/wdk-v7-1-is-now-available.aspx</link><pubDate>Mon, 01 Mar 2010 06:15:53 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9970658</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=9970658</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2010/02/28/wdk-v7-1-is-now-available.aspx#comments</comments><description>&lt;p&gt;A refresh of the WDK is now available on connect. You can download the v7.1 WDK following the directions on &lt;a href="http://www.microsoft.com/whdc/DevTools/WDK/WDKpkg.mspx" target="_blank"&gt;WHDC&lt;/a&gt;.&amp;#160; The change list for the WDK can be found &lt;a href="http://www.microsoft.com/whdc/devtools/wdk/RelNotesW7.mspx" target="_blank"&gt;here&lt;/a&gt;, I copying it here as well:&lt;/p&gt;  &lt;h4&gt;WDK Version 7.1.0 Changes and Issues&lt;/h4&gt;  &lt;p&gt;This section contains information about the changes to the WDK for the 7.1.0 refresh release.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Windows XP x64 is now supported as an installation platform&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Debugger Changes&lt;/b&gt;     &lt;br /&gt;The Debugging Tools for Windows have been updated in this release of the WDK. The following changes were made:&lt;/p&gt;  &lt;p&gt;•Debugger Version changed to 6.12.2.633. See the release notes in the debugger package for more information.&lt;/p&gt;  &lt;p&gt;•Updated ndiskd.dll&lt;/p&gt;  &lt;p&gt;•Miscellaneous bugfixes in UMDF and KMDF debugger extensions:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;KMDF:&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Ability to print more than 50 requests on a queue&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•!wdfdevice correctly displays FileObjectClass Name&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Display if a request is cancelled if it is on the driver notified list&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;UMDF&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Correctly display a umirps cancel callback&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Dump a devices Cleanup and Close callbacks&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Display the file object associated with a request&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;b&gt;Redistributable Change: Update to offreg.dll &lt;/b&gt;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Build Environment Changes &lt;/b&gt;&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;•Updated MSVCRT.lib to fix driver crashes in Vista&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Updated ws2_32.lib&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Added ntddump.h&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Added NPIV.mof&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Added headers for Vista 7ip&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Fixed annotations on I/O routines&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Added wudftrace.ctl containing all umdf trace guids&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;b&gt;Sample Changes and Issues &lt;/b&gt;&lt;/p&gt;  &lt;p&gt;NDIS:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;•Xframeii: bugs fixed&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Added NetVmini sample&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Sensor skeleton sample: memory leak issues fixed&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•KMDF Toaster sample: bug fixed&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•WDM Event sample: bug fixed&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;•Port I/O sample driver: There is a syntax issue in the file …\src\general\portio\sys\genport.inx that prevents the driver from being successfully installed on pre-Windows 7 systems. The workaround is to replace all occurrences of “PORTIO_Device” with “PortIO_Inst”. &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;&lt;b&gt;Update to Sensor Adapter Test Suite &lt;/b&gt;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9970658" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDK+Docs/">WDK Docs</category></item><item><title>What is IRQL?</title><link>http://blogs.msdn.com/b/doronh/archive/2010/02/02/what-is-irql.aspx</link><pubDate>Wed, 03 Feb 2010 01:05:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9957323</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>15</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=9957323</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2010/02/02/what-is-irql.aspx#comments</comments><description>&lt;P&gt;Jake Oshins wanted to write about IRQLs and I am gladly letting him use my blog as a platform.&amp;nbsp; Here it is…&lt;/P&gt;
&lt;P&gt;I’ve found myself explaining IRQL a lot lately, sometimes to people who want to know because they’re trying to write Windows drivers and sometimes to people who are accustomed to Linux or some other variant of Unix and they want to know why something like IRQL is required within Windows when those systems so clearly get by without it.&lt;/P&gt;
&lt;P&gt;Penny Orwick covered this topic before, in the following two papers, with a lot of help from me and some others:&lt;/P&gt;
&lt;P&gt;&lt;A href="http://www.microsoft.com/whdc/driver/kernel/irql.mspx" mce_href="http://www.microsoft.com/whdc/driver/kernel/irql.mspx"&gt;http://www.microsoft.com/whdc/driver/kernel/irql.mspx&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://www.microsoft.com/whdc/driver/kernel/locks.mspx" mce_href="http://www.microsoft.com/whdc/driver/kernel/locks.mspx"&gt;http://www.microsoft.com/whdc/driver/kernel/locks.mspx&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;I’ll try to do it a little more briefly here.&lt;/P&gt;
&lt;P&gt;Computers have many things within them that can interrupt a processor.&amp;nbsp; These include timers, I/O devices, other processors, internal processor performance counters, etc.&amp;nbsp; All processors have an instruction for disabling interrupts, somehow, but that instruction (cli in x64 processors) isn’t selective about which interrupts it disables.&lt;/P&gt;
&lt;P&gt;The people who built DEC’s VMS operating system also helped design the processors that DEC used, and many of them came to Microsoft and designed Windows NT, which was the basis for modern versions of Windows, including Windows XP and Windows 7.&amp;nbsp; These guys wanted a way to disable (very quickly) just some of the interrupts in the system.&amp;nbsp; They considered it useful to hold off interrupts from some sources while servicing interrupts from other sources.&lt;/P&gt;
&lt;P&gt;They also realized that, just as you must acquire locks in the same order everywhere in your code to avoid deadlocks, you must also service interrupts with the same relative priority every time.&amp;nbsp; It doesn’t work if the clock interrupts are sometimes more important than the IDE controller’s interrupts and sometimes they aren’t.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Interrupts are frequently called “Interrupt ReQuests” and the priority of a specific IRQ is its Level.&amp;nbsp; These letters, all run together, are IRQL.&lt;/P&gt;
&lt;P&gt;So if you lay out all the interrupt sources in the system and create a priority for each one, or sometimes a priority for each group, you can start to do interesting things.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Consider a spinlock.&amp;nbsp; Spinlocks (at least in the traditional sense) are implemented by having a processor spin in a tight loop trying to atomically modify a variable.&amp;nbsp; The cache coherency hardware guarantees that only one processor can do that at a time, so lock acquisition goes only to the processor that succeeds.&amp;nbsp; Other processors keep spinning until they succeed.&lt;/P&gt;
&lt;P&gt;The processor that “owns” the lock needs to release the lock as soon as possible, as the other (waiting) processors are burning up processor time waiting to acquire the lock.&amp;nbsp; So you really don’t want to interrupt that processor and schedule some other thread for execution, causing all the waiters to spin until the owning thread is rescheduled.&lt;/P&gt;
&lt;P&gt;In this situation, some operating systems encourage the owner of the spinlock to disable all interrupts so that the code can’t be interrupted.&amp;nbsp; (Note, too, that interrupts really need to be disabled before trying to acquire the lock, or the thread might be interrupted between acquiring the lock and disabling interrupts.)&lt;/P&gt;
&lt;P&gt;The designers of VMS and NT decided that they didn’t want to disable all interrupts just because some code somewhere acquired a spinlock.&amp;nbsp; Some things shouldn’t wait.&amp;nbsp; TLB flushes, are a good example.&amp;nbsp; So if only some interrupts are disabled while a spinlock is held, then you can still briefly interrupt the code that owns the lock for much more important tasks.&amp;nbsp; Perhaps even more importantly, you can interrupt the processors which are spinning, waiting to acquire a spinlock for these important tasks, causing them to do something useful instead of just spinning.&lt;/P&gt;
&lt;P&gt;Note that this means that every spinlock has an associated IRQL, and you have to use that IRQL consistently, or the machine will deadlock.&amp;nbsp; In NT, by default, every spinlock has the same IRQL, called DISPATCH_LEVEL.&amp;nbsp; DISPATCH_LEVEL means, essentially, that the interrupts which can cause a thread to stop running are disabled.&amp;nbsp; (More about that later.)&lt;/P&gt;
&lt;P&gt;Here’s a table of all IRQLs, as defined in the Windows NT header files (easily seen in the WDK.) &lt;/P&gt;
&lt;TABLE border=1 cellSpacing=0 cellPadding=0&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD width=118&gt;
&lt;P align=center&gt;&lt;STRONG&gt;IRQL&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;
&lt;TD width=53&gt;
&lt;P&gt;&lt;STRONG&gt;X86 IRQL Value&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;
&lt;TD width=53&gt;
&lt;P&gt;&lt;STRONG&gt;AMD64 &lt;/STRONG&gt;&lt;STRONG&gt;IRQL Value&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;
&lt;TD width=53&gt;
&lt;P&gt;&lt;STRONG&gt;IA64 IRQL Value&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;
&lt;TD width=232&gt;
&lt;P align=center&gt;&lt;STRONG&gt;Description&lt;/STRONG&gt;&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;PASSIVE_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;0&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;0&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;0&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;User threads and most kernel-mode operations&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;APC_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;1&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;1&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;1&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Asynchronous procedure calls and page faults&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;DISPATCH_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;2&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;2&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;2&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Thread scheduler and deferred procedure calls (DPCs)&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;CMC_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;N/A&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;N/A&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;3&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Correctable machine-check level (IA64 platforms only)&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;Device interrupt levels (DIRQL)&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;3-26&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;3-11&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;4-11&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Device interrupts&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;PC_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;N/A&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;N/A&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;12&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Performance counter (IA64 platforms only)&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;PROFILE_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;27&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;15&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;15&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Profiling timer for releases earlier than Windows 2000&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;SYNCH_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;27&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;13&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;13&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Synchronization of code and instruction streams across processors&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;CLOCK_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;N/A&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;13&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;13&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Clock timer&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;CLOCK2_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;28&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;N/A&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;N/A&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Clock timer for x86 hardware&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;IPI_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;29&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;14&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;14&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Interprocessor interrupt for enforcing cache consistency&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;POWER_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;30&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;14&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;15&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Power failure&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD vAlign=top width=118&gt;
&lt;P&gt;HIGH_LEVEL&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;31&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;15&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=53&gt;
&lt;P align=center&gt;15&lt;/P&gt;&lt;/TD&gt;
&lt;TD vAlign=top width=232&gt;
&lt;P&gt;Machine checks and catastrophic errors; profiling timer for Windows XP and later releases&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;For driver writers, the only IRQLs that are usually interesting are 0 through 2 and DIRQL.&amp;nbsp; It’s worth mentioning, though, that the NT kernel itself internally has spinlocks at DISPATCH_LEVEL and all the levels above that.&lt;/P&gt;
&lt;P&gt;So, now for a tour of interesting IRQLs:&lt;/P&gt;
&lt;P&gt;&lt;B&gt;PASSIVE_LEVEL&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;This is the level at which threads run.&amp;nbsp; In fact, if you look at the specific definition of “thread” in NT, it pretty much only covers code that runs in the context of a specific process, at PASSIVE_LEVEL or APC_LEVEL.&amp;nbsp; Deferred Procedure Calls (DPCs) are not threads, in that sense.&lt;/P&gt;
&lt;P&gt;Any interrupt can occur at PASSIVE_LEVEL.&amp;nbsp; User-mode code executes at PASSIVE_LEVEL.&lt;/P&gt;
&lt;P&gt;&lt;B&gt;APC_LEVEL&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;Windows NT has an interesting mechanism for getting into a certain thread context.&amp;nbsp; You can queue an interrupt to a thread, so that your function will run on that thread’s stack, with that thread’s address space, with that thread’s local storage.&amp;nbsp; This is useful for I/O completion.&amp;nbsp; When I/O completes, you queue an APC back to the requesting thread which does the last part of I/O completion in the initiator’s address space.&amp;nbsp; It’s a neat way to solve a bunch of problems.&lt;/P&gt;
&lt;P&gt;If you want to disable interrupts to your thread, you raise to APC_LEVEL.&amp;nbsp; At least that was the original design.&amp;nbsp; APCs and the rules around them have grown much more complicated over the years.&amp;nbsp; At this point, the best that you can say is that if you care to disable APCs, call KeEnterCriticalRegion (&lt;A href="http://msdn.microsoft.com/en-us/library/ms801955.aspx" mce_href="http://msdn.microsoft.com/en-us/library/ms801955.aspx"&gt;http://msdn.microsoft.com/en-us/library/ms801955.aspx&lt;/A&gt;) or KeEnterGuardedRegion (&lt;A href="http://msdn.microsoft.com/en-us/library/ms801643.aspx" mce_href="http://msdn.microsoft.com/en-us/library/ms801643.aspx"&gt;http://msdn.microsoft.com/en-us/library/ms801643.aspx&lt;/A&gt;.)&lt;/P&gt;
&lt;P&gt;Your code generally won’t need to run at APC_LEVEL at all, unless you use Fast Mutexes (&lt;A href="http://msdn.microsoft.com/en-us/library/aa490219.aspx" mce_href="http://msdn.microsoft.com/en-us/library/aa490219.aspx"&gt;http://msdn.microsoft.com/en-us/library/aa490219.aspx&lt;/A&gt;.)&amp;nbsp; Fast Mutexes are somewhat faster than Mutexes (&lt;A href="http://msdn.microsoft.com/en-us/library/aa490228.aspx" mce_href="http://msdn.microsoft.com/en-us/library/aa490228.aspx"&gt;http://msdn.microsoft.com/en-us/library/aa490228.aspx&lt;/A&gt;) or other dispatcher objects because, among other things, they hold off APCs by raising to APC_LEVEL.&lt;/P&gt;
&lt;P&gt;APC interrupts, by the way, are sent by a processor, either to itself or to another processor.&amp;nbsp; No external device is involved.&lt;/P&gt;
&lt;P&gt;&lt;B&gt;DISPATCH_LEVEL&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;Windows NT doesn’t have a “scheduler” in the sense that most Unix variants do.&amp;nbsp; There is no process that decides which other processes should run.&amp;nbsp; Each processor “dispatches” itself by looking at runnable threads and deciding which one to run next.&amp;nbsp; This is a scheduler, of sorts, but not the same thing that many people coming from Linux will imagine.&lt;/P&gt;
&lt;P&gt;The dispatcher is interrupt driven, in that it won’t allow a thread to run longer than its quantum before scheduling another thread.&amp;nbsp; But the scheduling clock doesn’t generate dispatcher interrupts directly.&amp;nbsp; The clock interrupt fires at CLOCK_LEVEL, somewhat more frequently than the thread scheduling quantum.&amp;nbsp; Various housekeeping tasks happen as a result of the clock interrupt, and one of them is that a dispatcher interrupt is generated by the processor to itself.&amp;nbsp; (Actually, this internal self-interrupt is often optimized away, but the architectural result is the same as if an interrupt were generated.)&lt;/P&gt;
&lt;P&gt;If your code raises IRQL to DISPATCH_LEVEL, you have disabled the dispatcher on that processor, and only on that processor.&amp;nbsp; This means that your thread will not be pre-empted by another thread and it will not be moved to another processor until you lower IRQL.&lt;/P&gt;
&lt;P&gt;Since, as noted above, I/O completion depends on code running at APC_LEVEL, and since APC_LEVEL code won’t run while the processor is at DISPATCH_LEVEL, page faults can’t be resolved at DISPATCH_LEVEL.&amp;nbsp; So code that holds a DISPATCH_LEVEL lock (like a spinlock) can’t reference memory which might be paged out.&lt;/P&gt;
&lt;P&gt;Furthermore, most of the locking primitives that the NT kernel provides are what are called “dispatcher objects” (&lt;A href="http://msdn.microsoft.com/en-us/library/aa490210.aspx" mce_href="http://msdn.microsoft.com/en-us/library/aa490210.aspx"&gt;http://msdn.microsoft.com/en-us/library/aa490210.aspx&lt;/A&gt;.)&amp;nbsp; You can wait on dispatcher objects until they are signaled and, while your code is waiting, the processor is free to get other work done, on behalf of other threads.&amp;nbsp; This is nice, because, in contrast with the spinlock, which consumes the processor doing no useful work while it’s waiting, dispatcher objects allow the dispatcher to find other work until the reason for waiting can be satisfied.&lt;/P&gt;
&lt;P&gt;What this means to you, though, is that you can’t wait on a dispatcher object at DISPATCH_LEVEL.&amp;nbsp; You’ve already disabled the dispatcher.&amp;nbsp; Your only choice at DISPATCH_LEVEL is a spinlock.&lt;/P&gt;
&lt;P&gt;&lt;B&gt;DIRQL&lt;/B&gt;&lt;/P&gt;
&lt;P&gt;“DIRQL” is the shorthand that many people (internal to Microsoft and external) use when they mean “the IRQL that the PnP manager assigned to my device’s interrupt, and the associated interrupt spinlock and interrupt service routine.”&amp;nbsp; When a bus driver requests an interrupt for a device (as when the PCI driver finds the Interrupt Pin register set to some non-zero value, or when it discovers an MSI-X table) it tells the PnP manager two things.&amp;nbsp; First, it says that the device needs to register an ISR or a set of ISRs.&amp;nbsp; Next it says something about how the device is attached to any interrupt controllers present in the machine.&amp;nbsp; The PnP manager picks a processor to attach the interrupt to and picks the IRQL for that interrupt.&amp;nbsp; Sometimes that choice is constrained by the way the wires are laid out on the motherboard, sometimes not.&amp;nbsp; That topic is too big for this post.&amp;nbsp; (I might go into it later.&amp;nbsp; I wrote the code.)&lt;/P&gt;
&lt;P&gt;As you can see from the table above, there is more than one DIRQL.&amp;nbsp; Unless your device generates more than one interrupt, you don’t really have to care.&amp;nbsp; Just pass along the values that you were given.&amp;nbsp; Your interrupt spinlock’s IRQL is that which was assigned to you.&amp;nbsp; The only thing you have to know about it is that acquiring that lock means that you’ve pre-empted everything happening at lower IRQL.&amp;nbsp; You haven’t pre-empted things like TLB updates, though, as those still come in at higher IRQL.&lt;/P&gt;
&lt;P&gt;If your device does generate more than one interrupt, and if you need one spinlock that is used for both interrupt sources, you need to register your interrupt service routines with the highest of your DIRQLs as the SynchronizeIrql, which will avoid deadlocks by guaranteeing that all your interrupt-related code runs at the highest necessary IRQL.&lt;/P&gt;
&lt;P&gt;In summary, IRQL is a concept that was intended to allow spinlocks to be sorted into more-important and less-important buckets, so that some interrupts can occur while other interrupts are disabled.&lt;/P&gt;
&lt;P&gt;Most people agree that this is fairly complex to work with.&amp;nbsp; Whether you believe this was a necessary addition to the driver model is the source of a debate that’s been raging on the ‘net since before Windows NT actually existed.&lt;/P&gt;
&lt;P&gt;- Jake Oshins&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9957323" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDM/">WDM</category></item><item><title>WDFREQUESTs are for sharing in KMDF v1.9</title><link>http://blogs.msdn.com/b/doronh/archive/2009/03/05/wdfrequests-are-for-sharing-in-kmdf-v1-9.aspx</link><pubDate>Fri, 06 Mar 2009 00:48:14 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9460771</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=9460771</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2009/03/05/wdfrequests-are-for-sharing-in-kmdf-v1-9.aspx#comments</comments><description>&lt;p&gt;In my last &lt;a href="http://blogs.msdn.com/doronh/archive/2009/03/04/wdfrequests-are-not-for-sharing.aspx" target="_blank"&gt;post&lt;/a&gt; I described why a WDFREQUEST is unique to a particular WDFDEVICE.&amp;#160; There is one particular programming pattern where this is not the behavior you want.&amp;#160; This pattern is when you have each PDO accepting IO requests which it then forwards on to the parent WDFDEVICE for processing. One great in box example of this is usbhub.sys.&amp;#160; Each usbhub PDO receives URBs which are then forwarded to the parent FDO and the FDO is where all IO processing occurs.&amp;#160; &lt;/p&gt;  &lt;p&gt;If you want to apply this pattern to a KMDF driver written to a v1.7 or earlier and take advantage of WDFQUEUEs you had to send the requests from the PDO to the FDO with WdfRequestSend so that they were represented to the FDO. The easiest way to do this is to create a WDFIOTARGET for the FDO itself and then have each PDO send IO to that WDFIOTARGET as shown in the following 3 code snippets. &lt;/p&gt;  &lt;h3&gt;EvtDriverDeviceAdd for the FDO&lt;/h3&gt;  &lt;pre&gt;NTSTATUS EvtDriverDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit) 
{
   WDFDEVICE device;

   WDF_OBJECT_ATTRIBUTES woa;
   WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&amp;amp;woa, FDO_EXTENSION);

   // ...initialize the DeviceInit...
   status = WdfDeviceCreate(&amp;amp;DeviceInit, &amp;amp;woa, &amp;amp;device);

   if (!NT_SUCCESS(status)) {
      return status;
   }

   PFDO_EXTENSION pFdoExt = GetFdoExt(device);
   status = WdfIoTargetCreate(device, WDF_NO_OBJECT_ATTRIBUTES, &amp;amp;pFdoExt-&amp;gt;SelfTarget);
   if (!NT_SUCCESS(status)) {
      return status;
   }

   // open the WDFIOTARGET to point our own PDEVICE_OBJECT
   WDF_IO_TARGET_OPEN_PARAMS openParams;
   WDF_IO_TARGET_OPEN_PARAMS_INIT_EXISTING_DEVICE(openParams, WdfDeviceWdmGetDeviceObject(device));

   status = WdfIoTargetOpen(pFdoExt-&amp;gt;SelfTarget, &amp;amp;openParams);
   if (!NT_SUCCESS(status)) {
      return status;
   }
   ...
   return status;
}&lt;/pre&gt;

&lt;h3&gt;EvtChildListCreateDevice for the PDO&lt;/h3&gt;

&lt;pre&gt;NTSTATUS EvtChildListCreateDevice(
    WDFCHILDLIST ChildList, 
    PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
    PWDFDEVICE_INIT ChildInit
    )
{
   WDFDEVICE pdo;
   NTSTATUS status;

   WDF_OBJECT_ATTRIBUTES woa;
   WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&amp;amp;woa, PDO_EXTENSION);

   status = WdfDeviceCreate(&amp;amp;ChildInit, &amp;amp;woa, &amp;amp;device);
   if (!NT_SUCCESS(status)) {
      return status;
   }

   PPDO_EXTENSION pPdoExt = GetPdoExt(device);
   PFDO_EXTENSION pFdoExt = GetFdoExt(WdfPdoGetParent(device));

   pPdoExt-&amp;gt;ParentTarget = pFdoExt-&amp;gt;SelfTarget;
   ...
   return status;
}&lt;/pre&gt;

&lt;h3&gt;EvtIoDefault for the PDO&lt;/h3&gt;

&lt;pre&gt;typedef
VOID EvtIoDefault(WDFQUEUE Queue, WDFREQUEST Request)&lt;/pre&gt;

&lt;pre&gt;{
    // ...  extract Request type ...
    if (&lt;i&gt;RequestShouldBeSentToParent&lt;/i&gt;()) {
       WdfRequestFormatRequestUsingCurrentType(Request);
       WdfRequestSend(Request, GetPdoExt(WdfIoQueueGetDevice(Queue))-&amp;gt;ParentTarget);
    }
}&lt;/pre&gt;

&lt;p&gt;As you can see, this is a bit cumbersome!&amp;#160; While it works, it is not ideal.&amp;#160; The KMDF team addressed this issue in v1.9 by adding 2 new DDIs that must be used together&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/dd434715.aspx" target="_blank"&gt;WdfPdoInitAllowForwardingRequestToParent&lt;/a&gt; which tells KMDF that you will be forwarding IO from a PDO to the parent FDO.&amp;#160; Internally this sets up some bookkeeping and, more importantly, sets the PDO’s PDEVICE_OBJECT’s StackSize to the FDO’s PDEVICE_OBJECT StackSize+1 so that there will be enough stack locations in the underlying PIRP for both the parent and child stacks &lt;/li&gt;

  &lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/dd434849.aspx" target="_blank"&gt;WdfRequestForwardToParentDeviceIoQueue&lt;/a&gt; which removes the need for the custom WDFIOTARGET and WdfRequestSend and directly presents the PDO’s WDFREQUEST to the FDO’s WDFQUEUE &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s now rewrite the 3 code snippets to make use of these new DDIs, new code in &lt;font color="#ff0000"&gt;red&lt;/font&gt;&lt;/p&gt;

&lt;h3&gt;NEW EvtDriverDeviceAdd for the FDO&lt;/h3&gt;

&lt;pre&gt;NTSTATUS EvtDriverDeviceAdd(WDFDRIVER Driver, PWDFDEVICE_INIT DeviceInit) 
{
   WDFDEVICE device;

   WDF_OBJECT_ATTRIBUTES woa;
   WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&amp;amp;woa, FDO_EXTENSION);

   // ...initialize the DeviceInit...
   status = WdfDeviceCreate(&amp;amp;DeviceInit, &amp;amp;woa, &amp;amp;device);

   if (!NT_SUCCESS(status)) {
      return status;
   }

   PFDO_EXTENSION pFdoExt = GetFdoExt(device);
&lt;strike&gt;
   status = WdfIoTargetCreate(device, WDF_NO_OBJECT_ATTRIBUTES, &amp;amp;pFdoExt-&amp;gt;SelfTarget);
   if (!NT_SUCCESS(status)) {
      return status;
   }

   // open the WDFIOTARGET to point our own PDEVICE_OBJECT
   WDF_IO_TARGET_OPEN_PARAMS openParams;
   WDF_IO_TARGET_OPEN_PARAMS_INIT_EXISTING_DEVICE(openParams, WdfDeviceWdmGetDeviceObject(device));

   status = WdfIoTargetOpen(pFdoExt-&amp;gt;SelfTarget, &amp;amp;openParams);
   if (!NT_SUCCESS(status)) {
      return status;
   }
&lt;/strike&gt;

&lt;font color="#ff0000"&gt;
   // initialize a WDF_IO_QUEUE_CONFIG to your needs
   status = WdfIoQueueCreate(device, ..., &amp;amp;pFdoExt-&amp;gt;&amp;gt;ChildProcessingQueue);
   if (!NT_SUCCESS(status)) {
      return status;
   }
&lt;/font&gt;
   ...
   return status;
}&lt;/pre&gt;

&lt;h3&gt;NEW EvtChildListCreateDevice for the PDO&lt;/h3&gt;

&lt;pre&gt;NTSTATUS EvtChildListCreateDevice(
    WDFCHILDLIST ChildList, 
    PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
    PWDFDEVICE_INIT ChildInit
    )
{
   WDFDEVICE pdo;
   NTSTATUS status;

   WDF_OBJECT_ATTRIBUTES woa;
   WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&amp;amp;woa, PDO_EXTENSION);

&lt;font color="#ff0000"&gt;
   WdfPdoInitAllowForwardingRequestToParent(ChildInit);
&lt;/font&gt;   

   status = WdfDeviceCreate(&amp;amp;ChildInit, &amp;amp;woa, &amp;amp;device);
   if (!NT_SUCCESS(status)) {
      return status;
   }

&lt;strike&gt;   PPDO_EXTENSION pPdoExt = GetPdoExt(device);
   PFDO_EXTENSION pFdoExt = GetFdoExt(WdfPdoGetParent(device));

   pPdoExt-&amp;gt;ParentTarget = pFdoExt-&amp;gt;SelfTarget;&lt;/strike&gt;
   ...
   return status;
}&lt;/pre&gt;

&lt;h3&gt;NEW EvtIoDefault for the PDO&lt;/h3&gt;

&lt;pre&gt;VOID EvtIoDefault(WDFQUEUE Queue, WDFREQUEST Request)&lt;/pre&gt;

&lt;pre&gt;{
    // ...  extract Request type ...
    if (&lt;i&gt;RequestShouldBeSentToParent&lt;/i&gt;()) {
&lt;strike&gt;
       WdfRequestFormatRequestUsingCurrentType(Request);
       WdfRequestSend(Request, GetPdoExt(WdfIoQueueGetDevice(Queue))-&amp;gt;ParentTarget);
&lt;/strike&gt;
&lt;font color="#ff0000"&gt;
       WDF_REQUEST_FORWARD_OPTIONS options;
       WDF_REQUEST_FORWARD_OPTIONS_INIT(&amp;amp;options);
       options.Flags = WDF_REQUEST_FORWARD_OPTIONS_FLAGS;&lt;/font&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;font color="#ff0000"&gt;
       status = WdfRequestForwardToParentDeviceIoQueue(
           Request, 
           GetFdoExt(WdfPdoGetParent(WdfIoQueueGetDevice(Queue)))-&amp;gt;ChildProcessingQueue,
           &amp;amp;options);
&lt;/font&gt;
    }
}&lt;/pre&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9460771" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/KMDF/">KMDF</category><category domain="http://blogs.msdn.com/b/doronh/archive/tags/Design+Patterns/">Design Patterns</category></item><item><title>WDFREQUESTs are not for sharing</title><link>http://blogs.msdn.com/b/doronh/archive/2009/03/04/wdfrequests-are-not-for-sharing.aspx</link><pubDate>Thu, 05 Mar 2009 03:58:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9459155</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=9459155</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2009/03/04/wdfrequests-are-not-for-sharing.aspx#comments</comments><description>&lt;P&gt;&lt;EM&gt;FYI: this is a bit of a long post, but I wanted to be thorough and illustrative and give some insight into how the framework works and potential design that could have been made, but were not for the sake of simplicity and performance&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;A common misconception a WDFREQUEST handle is the assumption that the WDFREQUEST handle value “follows” (e.g. stays the same) the PIRP around &lt;EM&gt;everywhere &lt;/EM&gt;that the PIRP goes to.&amp;nbsp; Basically, the idea is that everywhere that the PIRP is sent or presented, the same WDFREQUST handle will be used.&amp;nbsp; The reality is that the same WDFREQUEST handle value will only be used while the PIRP is owned by the WDFDEVICE for which it was created.&amp;nbsp; This means that if you send the WDFREQUEST to another driver with a call to &lt;A href="http://msdn.microsoft.com/en-us/library/aa492032.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/aa492032.aspx"&gt;WdfRequestSend&lt;/A&gt; (e.g. transfer ownership to the driver you are sending it to), the driver which receives the PIRP will have a different handle for the WFDREQUEST.&lt;/P&gt;
&lt;P&gt;This means that the WDFREQUEST cannot be used to send extra data or buffers to the driver which will receive the sent request.&amp;nbsp; For instance, this pattern does not work.&amp;nbsp; First, in the sending driver (let's say request's handle value is 0xA) you format a request context, format the request and send it to the WDFIOTARGET&lt;/P&gt;&lt;PRE&gt;typedef struct _EXTRA_BUFFER {
   PVOID Data;
   ULONG DataLength;
   ...
} EXTRA_BUFFER, *PEXTRA_BUFFER;

PEXTRA_BUFFER pExtra = NULL;
WDF_OBEJCT_ATTRIBUTES woa;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&amp;amp;woa, EXTRA_BUFFER);
NTSTATUS status;

status = WdfObjectAllocateContext(request, &amp;amp;woa, (PVOID*) &amp;amp;pExtra);

// .. initialize pExtra ...
// ... format request as an internal IOCTL ...

if (WdfRequestSend(request, ...) == FALSE) {
   WdfRequestComplete(request, WdfRequestGetStatus(request));
}&lt;/PRE&gt;
&lt;P&gt;And then in the receiving driver's WDFQUEUE dispatch routine would have a new WDFREQUEST handle value (let’s say 0xAA) and not the sender’s WDFREQUEST (0xA).&amp;nbsp; &lt;/P&gt;&lt;PRE&gt;VOID EvtInternalDeviceIoControl(
    __in WDFQUEUE Queue,
    __in WDFREQUEST Request,
    __in size_t OutputBufferLength,
    __in size_t InputBufferLength,
    __in ULONG IoControlCode
    ) { ... }&lt;/PRE&gt;
&lt;P&gt;Think about how KMDf would have been implemented if the same WDFREQUEST was presented to the lower driver.&amp;nbsp; One of two things would have had to occur:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;The first option would have been for the WDFREQUEST handle value to be smuggled somewhere in the PIRP (with the most likely candidate being DriverContext[]), but that would have been unreliable and prone to compatibility issues with non KMDF drivers in the stack. &lt;/LI&gt;
&lt;LI&gt;The second option would have been to maintain a global mapping in KMDF that mapped from PIRP pointer value to WDFREQUEST handle.&amp;nbsp; This would have been very expensive to maintain because we would have had to look up the mapping every time a WDFQUEUE handled PIRP arrived in the framework.&amp;nbsp; It would have grown more even more expensive because this proposed map would have been guarded by a global lock which meant that all KMDF drivers would have been acquiring and releasing this one lock, making it very contentious and a huge performance problem.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Let’s take a step back and look at why there is a WDFREQUEST handle value when you send a request to another device.&amp;nbsp; Here is what happens when a PIRP arrives in a KMDF driver&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Call the potentially registered WDM preprocess routine&lt;/LI&gt;
&lt;LI&gt;If it is a PIRP that will be processed by a WDFQUEUE (a read/write/IOCTL/internal IOCTL), allocate a WDFREQUEST for the PIRP. If it is not one of these types, pass it to the appropriate pipeline in the framework&lt;/LI&gt;
&lt;LI&gt;Call the potentially registered in process callback&lt;/LI&gt;
&lt;LI&gt;Pass the WDFREQUEST to the WDFQUEUE handler so that it will be presented on the appropriate WDFQUEUE&lt;/LI&gt;
&lt;LI&gt;The WDFQUEUE calls the right IO event callback that you registered when the WDFQUEUE was created&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Step #2 is the key here.&amp;nbsp; The WDFREQUEST is always allocated (from a lookaside if that matters).&amp;nbsp; It means that there is no 1:1 correspondence between a PIRP and WDFREQUEST value. Let’s say we wanted to have a 1:1 correspondence for a singular WDFDEVICE though(and not across multiple WDFDEVICE as in the first example). We would need a WDFDEVICE based mapping of active PIRPs to WDFREQUEST handles to see if the PIRP is an active PIRP in the WDFDEVICE.&amp;nbsp; As in the case with the previously proposed global mapping, such a WDFDEVICE wide mapping would also be prohibitively expensive.&amp;nbsp; While the lock contention would move from a global scope to a WDFDEVICE scope and thus reducing some contention, such a lock would still be a performance hot spot since all PIRPs arriving into the driver would be contending on this lock. &lt;/P&gt;
&lt;P&gt;When you look at how KMDF allocates a WDFREQUEST it becomes clear that the WDFRQUEST loosely maps back to the current stack location in the PIRP, not the PIRP itself!&amp;nbsp; Think about the sending the WDFREQUEST to another driver case.&amp;nbsp; The sending driver has its own stack location and its own WDFREQUEST. The receiving driver has its own stack location and WDFREQUEST as well.&amp;nbsp; Just to reinforce this idea, let’s consider a final example.&amp;nbsp; Let’s say &lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Your driver received a WDFREQUEST formatted for IOCTL ‘A’ in your dispatch routine&lt;/LI&gt;
&lt;LI&gt;In processing the request your driver formatted (the next stack location) it for IOCTL ‘B’ &lt;/LI&gt;
&lt;LI&gt;The request is sent to the top of your stack (which means your driver will see the PIRP in its dispatch routine eventually as IOCTL ‘B’).&amp;nbsp; &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;When PIRP enters your driver as IOCTL 'B', it will have a new WDFREQUEST handle!&amp;nbsp; This is completely by design.&amp;nbsp; The first stack location (IOCTL ‘A’) has one WDFREQUEST, the subsequent stack location (IOCTL ‘B’) has another WDFREQUEST.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;In conclusion, WDFREQUEST handles are local to a specific WDFDEVICE.&amp;nbsp; In fact, they are local to a specific stack location.&amp;nbsp; The context off of a WDFREQUEST can only be used by your driver for that particular WDFDEVICE and is not meant to be shared across WDFDEVICEs.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9459155" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/KMDF/">KMDF</category></item><item><title>Great WinHEC presentation on device interfaces compared to device clases</title><link>http://blogs.msdn.com/b/doronh/archive/2009/02/03/great-winhec-presentation-on-device-interfaces-compared-to-device-clases.aspx</link><pubDate>Wed, 04 Feb 2009 04:03:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9394119</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=9394119</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2009/02/03/great-winhec-presentation-on-device-interfaces-compared-to-device-clases.aspx#comments</comments><description>&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;&lt;EM&gt;[This is a repeat of a post I made to NTDEV, but I wanted to make sure I reached as many people as possible.]&lt;/EM&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3 face=Calibri&gt;I just read this deck, &lt;/FONT&gt;&lt;A href="http://download.microsoft.com/download/5/E/6/5E66B27B-988B-4F50-AF3A-C2FF1E62180F/CON-T615_WH08.pptx"&gt;&lt;FONT size=3 face=Calibri&gt;http://download.microsoft.com/download/5/E/6/5E66B27B-988B-4F50-AF3A-C2FF1E62180F/CON-T615_WH08.pptx&lt;/FONT&gt;&lt;/A&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;, which was presented at WinHEC this past year.&amp;nbsp; It is by far the best explanation of device interfaces and device classes that I have seen in 10 years.&amp;nbsp; It worthwhile reading for both the new and experienced driver developer.&amp;nbsp; &lt;?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;FONT size=3&gt;&lt;FONT face=Calibri&gt;I was asked to enter a name and password to down the file, I hit cancel and it downloaded just fine.&amp;nbsp; I don’t know if this was an IE8/Win7 quirk or not, so YMMV.&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;
&lt;P style="MARGIN: 0in 0in 0pt" class=MsoNormal&gt;&lt;o:p&gt;&lt;FONT size=3 face=Calibri&gt;&amp;nbsp;&lt;/FONT&gt;&lt;/o:p&gt;&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9394119" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDK+Docs/">WDK Docs</category></item><item><title>MSDN link on how to set up a user or kernel debugger</title><link>http://blogs.msdn.com/b/doronh/archive/2009/01/29/msdn-link-on-how-to-set-up-a-user-or-kernel-debugger.aspx</link><pubDate>Thu, 29 Jan 2009 22:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9383543</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=9383543</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2009/01/29/msdn-link-on-how-to-set-up-a-user-or-kernel-debugger.aspx#comments</comments><description>&lt;P&gt;This has got to be one of the top FAQs out there: &lt;FONT color=#0000ff&gt;&lt;STRONG&gt;&lt;EM&gt;&lt;FONT color=#990000&gt;how do I set up a kernel debugger?&lt;/FONT&gt;&lt;/EM&gt;&lt;/STRONG&gt;&amp;nbsp; &lt;/FONT&gt;I just stumbled across a link on MSDN which gives instructions not only on how to set up a kernel debugger on all transports (serial, 1394, usb2), but also how to set up a user mode debugger or how to attach to a virtual machine. Pretty cool.&amp;nbsp; This used to be an internal web page at Microsoft (and I think a topic in debugger.chm), it is great that it is now public&lt;/P&gt;
&lt;P&gt;The MSDN topic is &lt;A href="http://msdn.microsoft.com/en-us/library/cc266370.aspx" target=_blank mce_href="http://msdn.microsoft.com/en-us/library/cc266370.aspx"&gt;Starting the Debugger&lt;/A&gt;. Add it to your bookmarks and maybe we can create room in the FAQ for another question ;)&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9383543" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WinDBG_2F00_KD+Fun/">WinDBG/KD Fun</category></item><item><title>Dude, what happened to Doron?</title><link>http://blogs.msdn.com/b/doronh/archive/2009/01/27/dude-what-happened-to-doron.aspx</link><pubDate>Wed, 28 Jan 2009 01:09:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9378732</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=9378732</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2009/01/27/dude-what-happened-to-doron.aspx#comments</comments><description>&lt;P&gt;So, I have not written anything in over 6 months and yet I have posted on NTDEV and public newsgroups. What gives?&amp;nbsp; Well, the short answer is that I have been short on time these past few months.&amp;nbsp; I have had only enough extra curricular time to read NTDEV and occassionally the newsgroups. Why? Well, for starters we had a second baby this past June and that took the majority of my time ;).&amp;nbsp; Combine that with parental leave and a project at work taking all of my time for nearly all of last year led to a very poor showing on the blog for 2008.&amp;nbsp; I promise to change that this year. &lt;/P&gt;
&lt;P&gt;Have a good new year and hopefully better quality drivers!&lt;/P&gt;
&lt;P&gt;d&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9378732" width="1" height="1"&gt;</description></item><item><title>How do I cancel an IRP that another thread may be completing at the same time?</title><link>http://blogs.msdn.com/b/doronh/archive/2008/06/29/how-do-i-cancel-an-irp-that-another-thread-may-be-completing-at-the-same-time.aspx</link><pubDate>Mon, 30 Jun 2008 09:16:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8670424</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8670424</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/06/29/how-do-i-cancel-an-irp-that-another-thread-may-be-completing-at-the-same-time.aspx#comments</comments><description>&lt;P&gt;Let's say that you allocated a PIRP and sent it down your device stack.&amp;nbsp; You free the PIRP in the completion routine and then return STATUS_MORE_PROCESSING_REQUIRED.&amp;nbsp; To make life more fun, you decide that you want to be able to cancel the sent IRP after you have sent it so you try to do it simple like this&lt;/P&gt;&lt;PRE&gt;typedef struct _DEVICE_EXTENSION {
    KSPIN_LOCK SentIrpLock;
    PIRP SentIrp;
} DEVICE_EXTENSION;&lt;/PRE&gt;
&lt;P&gt;Sending thread:&lt;/P&gt;&lt;PRE&gt;KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
devext-&amp;gt;SentIrp = Irp;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&lt;/PRE&gt;
&lt;P&gt;Canceling thread:&lt;/P&gt;&lt;PRE&gt;KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
if (devext-&amp;gt;AllocatedIrp != NULL) {
   IoCancelIrp(devext-&amp;gt;SentIrp);
}
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&lt;/PRE&gt;
&lt;P&gt;Completion routine:&lt;/P&gt;&lt;PRE&gt;PIRP irp;

KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
irp = devext-&amp;gt;SentIrp;&lt;/PRE&gt;&lt;PRE&gt;devext-&amp;gt;SentIrp = NULL;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);

IoFreeIrp(irp);

return STATUS_MORE_PROCESSING_REQUIRED;&lt;/PRE&gt;
&lt;P&gt;And it then deadlocks ;). If the call to IoCancelIrp causes the IRP to be completed in the calling context (e.g. the one which has acquired the lock), the completion routine will run and try to acquire the lock (SentIrpLock) on the same thread which holds it.&lt;/P&gt;
&lt;P&gt;So, life is not that simple and you have to do something more. The basic solution is that you need extra state to track who is touching the PIRP and who can free it. Walter Oney's book has a solution (IIRC, it is in the self initiated I/O section, but I do not have the book handy), but IMHO it is a bit complicated. KMDF has a solution to this problem which I like much more (imagine that ;)).&lt;/P&gt;
&lt;P&gt;You need an extra LONG, calling it CompletionCount per PIRP that you want to be able to cancel.&lt;/P&gt;&lt;PRE&gt;&amp;nbsp;&lt;/PRE&gt;
&lt;OL&gt;
&lt;LI&gt;You initialize CompletionCount to 1 before sending it down the stack and storing it in devext. &lt;/LI&gt;
&lt;LI&gt;Whenever there is a thread that wants to cancel the PIRP, it tries to interlocked increment CompletionCount only if and only if the current CompletionCount value is &amp;gt; 0. For this you need to roll your own InterlockedIncrementWithFloor which is fortunately not that hard and I have already shown you how to do that, &lt;A href="http://blogs.msdn.com/doronh/archive/2006/12/06/creating-your-own-interlockedxxx-operation.aspx" mce_href="http://blogs.msdn.com/doronh/archive/2006/12/06/creating-your-own-interlockedxxx-operation.aspx"&gt;http://blogs.msdn.com/doronh/archive/2006/12/06/creating-your-own-interlockedxxx-operation.aspx&lt;/A&gt;. &lt;/LI&gt;
&lt;LI&gt;After the canceling thread has called InterlockedIncrementWithFloor and IoCancelIrp, it calls InterlockedDecrement. &lt;/LI&gt;
&lt;LI&gt;Whomever wants to complete the PIRP, like the completion routine, interlock decrements CompletionCount. &lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;If the returned value from InterlockedDecrement is zero, the caller can complete the PIRP. If not, somebody else is trying to touch the PIRP and you must leave the PIRP alone.&amp;nbsp; So here is the revised code:&lt;/P&gt;&lt;PRE&gt;typedef struct _DEVICE_EXTENSION {
    KSPIN_LOCK SentIrpLock;
    PIRP SentIrp;
    ULONG CompletionCount;
} DEVICE_EXTENSION;&lt;/PRE&gt;
&lt;P&gt;Sending thread:&lt;/P&gt;&lt;PRE&gt;KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
&lt;FONT color=#ff0000&gt;devext-&amp;gt;CompletionCount = 1;
&lt;/FONT&gt;devext-&amp;gt;SentIrp = Irp;&lt;/PRE&gt;&lt;PRE&gt;KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);&lt;/PRE&gt;
&lt;P&gt;Canceling thread:&lt;/P&gt;&lt;PRE&gt;PIRP irp = NULL;
KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
if (devext-&amp;gt;AllocatedIrp != NULL &lt;FONT color=#ff0000&gt;&amp;amp;&amp;amp; MyInterlockedIcrementeWithFloor(&amp;amp;devext-&amp;gt;CompletionCount, 0) &amp;gt; 0&lt;/FONT&gt;) {
&lt;FONT color=#ff0000&gt;   irp = devext-&amp;gt;SentIrp;
&lt;/FONT&gt;}
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);

&lt;FONT color=#ff0000&gt;if (irp != NULL) {
    IoCancelIrp(irp);
    if (InterlockedDecrement(&amp;amp;devext-&amp;gt;CompletionCount) == 0) {
        IoFreeIrp(irp);
    }
}
&lt;/FONT&gt;&lt;/PRE&gt;
&lt;P&gt;Completion routine:&lt;/P&gt;&lt;PRE&gt;PIRP irp = NULL;

KeAcquireSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);
irp = devext-&amp;gt;SentIrp;&lt;PRE&gt;devext-&amp;gt;SentIrp = NULL;
KeReleaseSpinLock(&amp;amp;devext-&amp;gt;SentIrpLock, ...);

&lt;FONT color=#ff0000&gt;if (InterlockedDecrement(&amp;amp;devext-&amp;gt;CompletionCount) == 0) {&lt;/FONT&gt;
    IoFreeIrp(irp);
&lt;FONT color=#ff0000&gt;}&lt;/FONT&gt;

return STATUS_MORE_PROCESSING_REQUIRED;&lt;/PRE&gt;&lt;/PRE&gt;
&lt;P&gt;The beauty of this solution is that if you add more actors (let's say a timer for an async timeout, all you have to do is bump the CompletionCount to account for them to asynchronously rundown if you cannot cancel them.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8670424" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/KMDF/">KMDF</category><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDM/">WDM</category></item><item><title>What should you change in a sample before you ship it?</title><link>http://blogs.msdn.com/b/doronh/archive/2008/04/23/what-should-you-change-in-a-sample-before-you-ship-it.aspx</link><pubDate>Wed, 23 Apr 2008 21:41:19 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8419605</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8419605</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/04/23/what-should-you-change-in-a-sample-before-you-ship-it.aspx#comments</comments><description>&lt;p&gt;I was going to write about how to do this, but the awesome folks at WHDC got to it before I did.&amp;#160; I did get to review it before it was published, so I did have some influence in what is in the tip ;). So on this one my job is easy, just go read the &lt;a href="http://www.microsoft.com/whdc/driver/tips/SampleChange.mspx" target="_blank"&gt;tip&lt;/a&gt;!&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8419605" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/Coding+Thoughts/">Coding Thoughts</category></item><item><title>The WDF 1.7 cointstallers are now available</title><link>http://blogs.msdn.com/b/doronh/archive/2008/04/17/the-wdf-1-7-cointstallers-are-now-available.aspx</link><pubDate>Fri, 18 Apr 2008 02:20:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8405110</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8405110</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/04/17/the-wdf-1-7-cointstallers-are-now-available.aspx#comments</comments><description>&lt;P&gt;After a long wait (thank you for your patience!), the WDF 1.7 coinstallers are now up on the connect site.&amp;nbsp; To get the bits&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;go to &lt;A class="" href="http://connect.microsoft.com/" target=_blank mce_href="http://connect.microsoft.com"&gt;http://connect.microsoft.com&lt;/A&gt; &lt;/LI&gt;
&lt;LI&gt;Log in using your passport account&lt;/LI&gt;
&lt;LI&gt;Navigate to the WDF &lt;A class="" href="https://connect.microsoft.com/site/sitehome.aspx?SiteID=148" target=_blank mce_href="https://connect.microsoft.com/site/sitehome.aspx?SiteID=148"&gt;page&lt;/A&gt; (I don't know where it lives in the connection directory, &lt;EM&gt;sigh&lt;/EM&gt;)&lt;/LI&gt;
&lt;LI&gt;Choose Downloads on the left&lt;/LI&gt;
&lt;LI&gt;The package is dated 4/17/2008, you may be able to get it directly from &lt;A class="" href="https://connect.microsoft.com/Downloads/DownloadDetails.aspx?SiteID=148&amp;amp;DownloadID=11680" target=_blank mce_href="https://connect.microsoft.com/Downloads/DownloadDetails.aspx?SiteID=148&amp;amp;DownloadID=11680"&gt;here&lt;/A&gt; once you have logged on&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;Enjoy and let the signing and shipping of v1.7 WDF drivers begin!&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8405110" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDF/">WDF</category></item><item><title>Debugger commands (.step_filter) that make my life easier</title><link>http://blogs.msdn.com/b/doronh/archive/2008/04/16/debugger-commands-step-filter-that-make-my-life-easier.aspx</link><pubDate>Thu, 17 Apr 2008 02:02:19 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8399699</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8399699</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/04/16/debugger-commands-step-filter-that-make-my-life-easier.aspx#comments</comments><description>&lt;p&gt;This is a pretty cool and somewhat obscure debugger command.&amp;#160; It allows you to tell the debugger what functions to skip if you are using the &lt;a href="http://msdn2.microsoft.com/en-us/library/cc266759.aspx" target="_blank"&gt;trace command&lt;/a&gt; ('t').&amp;#160; I think of the trace command as the 'step into' command though, but that is just me.&amp;#160; Let's say we have the following simple application:&lt;/p&gt;  &lt;pre&gt;#include &amp;lt;stdio.h&amp;gt;&lt;stdio.h&gt;

struct Foo {
    Foo() : m_value(0) { }

    int Increment() { return ++m_value; }
    static void Print(int i) { printf(&amp;quot;%d\n&amp;quot;, i); }

    int m_value;
};

int _cdecl main(int argc, char *argv[])
{
    Foo f;
    Foo::Print(f.Increment());
    return 0;
}&lt;/pre&gt;

&lt;p&gt;If I were to run the program under the debugger and use the 't' command for each line, it would step into every function. I typically use 't' instead of 'p' because I usually want to step into a function at some point in time and I tend to press 'p' one too many times ;). Here is an example of the debugger session:&lt;/p&gt;

&lt;pre&gt;0:000&amp;gt; g test!main
&amp;gt;   13: {
0:000&amp;gt; t
&amp;gt;   14:     Foo f;
0:000&amp;gt; t
&amp;gt;    4:     Foo() : m_value(0) { }
0:000&amp;gt; t
&amp;gt;   15:     Foo::Print(&lt;font color="blue"&gt;f.Increment()&lt;/font&gt;);
0:000&amp;gt; t
&amp;gt;    6:     int Increment() { return ++m_value; } [1]
0:000&amp;gt; t
&amp;gt;   15:     Foo::Print(f.Increment());
0:000&amp;gt; t
&amp;gt;    7:     static void Print(int i) { printf(&amp;quot;%d\n&amp;quot;, i); } [2]
0:000&amp;gt; gu
&amp;gt;   16:     return 0;
0:000&amp;gt; t
&amp;gt;   17: }
0:000&amp;gt; t
test!__mainCRTStartup+0x102:&lt;/pre&gt;

&lt;p&gt;Let's look at the statement Foo::Print(f.Increment()); When using the trace command, it will first step into Foo::Increment ([1]) before stepping into Foo::Print() ([2]). But let's say that I never want to step into Foo::Increment because I know that it is a simple function that I do not want to debug. I can tell the debugger to ignore trace commands into this function with the &lt;a href="http://msdn2.microsoft.com/en-us/library/cc266868.aspx" target="_blank"&gt;.step_filter&lt;/a&gt; command. The command takes a semi-colon delineated list of &lt;em&gt;fully qualified &lt;/em&gt;symbol names (which can include wildcards so you can filter out entire modules) to ignore. Let's see the debugger session again with this command:&lt;/p&gt;

&lt;pre&gt;0:000&amp;gt; g test!main
&amp;gt;   13: {
0:000&amp;gt; .step_filter &amp;quot;test!Foo::Increment&amp;quot;
Filter out code symbols matching:
  test!Foo::Increment
0:000&amp;gt; t
&amp;gt;   14:     Foo f;
0:000&amp;gt; t
&amp;gt;    4:     Foo() : m_value(0) { }
0:000&amp;gt; t
&lt;font color="red"&gt;&amp;gt;   15:     Foo::Print(f.Increment());&lt;/font&gt;
0:000&amp;gt; t
&amp;gt;    7:     static void Print(int i) { printf(&amp;quot;%d\n&amp;quot;, i); }
0:000&amp;gt; gu
&amp;gt;   16:     return 0;
0:000&amp;gt; t
&amp;gt;   17: }
0:000&amp;gt; t
test!__mainCRTStartup+0x102:&lt;/pre&gt;

&lt;p&gt;You will see now that when I trace into Foo::Print(f.Increment()); that the f.Increment() call is executed but not trace into (ignored is not the right word because it has run, I just didn't see it line by line) and I step directly into Foo::Print(). I think this is a pretty powerful debugger command, it can save you a lot of time if you are always accidentally stepping into the wrong function like I always do ;).&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8399699" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WinDBG_2F00_KD+Fun/">WinDBG/KD Fun</category></item><item><title>EvtDevicePreprocessWdmIrp is not entirely free</title><link>http://blogs.msdn.com/b/doronh/archive/2008/04/02/evtdevicepreprocesswdmirp-is-not-entirely-free.aspx</link><pubDate>Thu, 03 Apr 2008 00:14:09 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8352404</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8352404</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/04/02/evtdevicepreprocesswdmirp-is-not-entirely-free.aspx#comments</comments><description>&lt;p&gt;One of the WDM escapes in KMDF is &lt;a href="http://msdn2.microsoft.com/en-us/library/aa490986.aspx" target="_blank"&gt;EvtDeviceWdmIrpPreprocess&lt;/a&gt; (or EvtDevicePreprocessWdmIrp in the API in which you register it) which you can register for by calling &lt;a href="http://msdn2.microsoft.com/en-us/library/aa491157.aspx" target="_blank"&gt;WdfDeviceInitAssignWdmIrpPreprocessCallback&lt;/a&gt;.&amp;#160; This function allows you to process a WDM PIRP before KMDF sees it and potentially processes it.&amp;#160; From a KMDF adoption point of view, this functionaltiy was a very strong requirement.&amp;#160; Without, there would be not be a defined way for a KMDF to support IRPs that KMDF did not natively support.&amp;#160; For instance, the KMDF serial example registers a preprocess routine for &lt;a href="http://msdn2.microsoft.com/en-us/library/ms795838.aspx" target="_blank"&gt;IRP_MJ_FLUSH_BUFFERS&lt;/a&gt;, from [ddkroot]\src\kmdf\serial\pnp.c: &lt;/p&gt;  &lt;pre&gt;//
// Since framework queues doesn't handle IRP_MJ_FLUSH_BUFFERS,
// IRP_MJ_QUERY_INFORMATION and IRP_MJ_SET_INFORMATION requests,
// we will register a preprocess callback to handle them.
//
status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
    DeviceInit,
    SerialFlush
    IRP_MJ_FLUSH_BUFFERS,
    NULL, // pointer minor function table
    0); // number of entries in the table&lt;/pre&gt;

&lt;p&gt;You should use this functionality in your driver only if this is the last resort you have; it does not come for free.&amp;#160; When you register for any preprocess routine, KMDF will increase the &lt;a href="http://msdn2.microsoft.com/en-us/library/aa491677.aspx" target="_blank"&gt;StackSize&lt;/a&gt; in the underlying WDM PDEVICE_OBJECT.&amp;#160; This is called out in the documention for &amp;quot;&lt;a href="http://msdn2.microsoft.com/en-us/library/aa490286.aspx" target="_blank"&gt;Preprocessing and Postprocessing IRPs&lt;/a&gt;&amp;quot; in KMDF. By increasing the stack size, &lt;em&gt;all &lt;/em&gt;PIRPs that are sent to your device will have an extra &lt;a href="http://msdn2.microsoft.com/en-us/library/aa491675.aspx" target="_blank"&gt;IO stack location&lt;/a&gt;.&amp;#160; This means that the PIRP is a bit larger than it would otherwise be; larger not only for the IRP_MJ code that you registered for, but for each and every PIRP that is sent to your device regardless of IRP_MJ code.&amp;#160; Additionally, if your StackSize goes over an internal threshhold in the IO manager, the way the PIRP is allocated could be different (for instance, devices with a StackSize of &amp;lt;= N might have a PIRP allocated out of a lookaside list while a StackSize &amp;gt; N might always be allocated by calling ExAllocatePool each time).&amp;#160; Mind you, the internal threshhold is a bit high so if you have a root enumerated device or your initial StackSize is low, this is not something that you will see.&lt;/p&gt;

&lt;p&gt;So while registering a preprocess routine is a very useful thing to be able to do, you should consider your options carefully and only register a preprocess routine if you have no other option.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8352404" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/KMDF/">KMDF</category></item><item><title>Using KeAcquireSpinLockAtDpcLevel is only a perf gain if you know you are DISPATCH_LEVEL</title><link>http://blogs.msdn.com/b/doronh/archive/2008/03/28/using-keacquirespinlockatdpclevel-is-only-a-perf-gain-if-you-know-you-are-dispatch-level.aspx</link><pubDate>Sat, 29 Mar 2008 01:42:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8342502</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8342502</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/03/28/using-keacquirespinlockatdpclevel-is-only-a-perf-gain-if-you-know-you-are-dispatch-level.aspx#comments</comments><description>&lt;P&gt;Well, that is certainly a long title ;). &lt;/P&gt;
&lt;P&gt;First, let us look at an approximate implementation of &lt;A href="http://msdn2.microsoft.com/en-us/library/ms801656.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms801656.aspx"&gt;KeAcquireSpinLock&lt;/A&gt; and &lt;A href="http://msdn2.microsoft.com/en-us/library/ms801898.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms801898.aspx"&gt;KeRaiseIrql&lt;/A&gt; (and yes I know that KeRaiseIrql is really a #define to KfRaiseIrql, but it is the same thing that happens in the end…) &lt;/P&gt;&lt;PRE&gt;KIRQL KeAcquireSpinLock(PKSPIN_LOCK SpinLock, PKIRQL PreviousIrql)
{
    KeRaiseIrql(DISPATCH_LEVEL, PreviousIrql);
    &lt;EM&gt;[spin on the lock until it has been acquired]&lt;/EM&gt;
}

VOID KeRaiseIrql(KIRQL NewIrql, PKIRQL, PKIRQL OldIrql)
{
    OldIrql = KeGetCurrentIrql();
&lt;EM&gt;    [raise IRQL to NewIrql]&lt;/EM&gt;
}
&lt;/PRE&gt;
&lt;P&gt;What I want to emphasize is that KeAcquireSpinLock will retrieve the current IRQL (to know what to restore the IRQL to when the lock is released) as a part of acquiring the spin lock. Retrieving the current irql is a relatively expensive operation. Enter &lt;A href="http://msdn2.microsoft.com/en-us/library/ms801669.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms801669.aspx"&gt;KeAcquireSpinLockAtDpcLevel&lt;/A&gt;. KeAcquireSpinLockAtDpcLevel does away with the IRQL change and just implements &lt;EM&gt;[spin on the lock until it has been acquired]&lt;/EM&gt;, but it does this with a large caveat…you must be running at DISPATCH_LEVEL (in reality it requires IRQL &amp;gt;= DISPATCH_LEVEL, but that is another discussion for another day) . It requires DISPATCH_LEVEL or higher so that you do not deadlock. Another caveat to effectively use KeAcquireSpinLockAtLevel you must know 100% that you are at DISPATCH_LEVEL. Naively, one could think that the following code optimizes for both cases &lt;/P&gt;&lt;PRE&gt;if (KeGetCurrentIrql() &amp;lt; DISPATCH_LEVEL) {
    KeAcquireSpinLock(&amp;amp;lock, &amp;amp;oldIrql);
}
else {
    KeAcquireSpinLockAtDpcLevel(&amp;amp;lock);
}
&lt;/PRE&gt;
&lt;P&gt;But the problem here is that in the case of the current IRQL &amp;lt; DISPATCH_LEVEL, the current IRQL is being retrieved twice (once in your code, once in KeAcquireSpinLock), so this relatively expensive operation is being performed twice when it should be performed only once. What all of this boils down to is that you must know with 100% certainty that the current IRQL is DISPATCH_LEVEL before you can use KeAcquireSpinLockAtDpcLevel effectively. Here are a couple of contexts here you can know for certain that the IRQL is DISPATCH_LEVEL. &lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;In a DPC (for ISR, for a timer, your own) &lt;/LI&gt;
&lt;LI&gt;After you have acquired a spin lock. In the case where you have nested locked being acquired (first A, then B), after you have called KeAcquireSpinLock(&amp;amp;A) you can then call KeAcquireSpinLockAtDpcLevel(&amp;amp;B) &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Notice that a completion routine is not guaranteed to be called at IRQL == DISPATCH_LEVEL! While you may see that it is called at dispatch, it is not something that you can rely on 100% of the time. For instance, the lower driver could complete the IRP at passive level in an error condition.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8342502" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/Design+Patterns/">Design Patterns</category></item><item><title>Inconceivableable</title><link>http://blogs.msdn.com/b/doronh/archive/2008/03/18/inconceivableable.aspx</link><pubDate>Wed, 19 Mar 2008 02:55:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8324240</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8324240</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/03/18/inconceivableable.aspx#comments</comments><description>&lt;P&gt;I have no idea who created the name for PNP_DEVICE_NOT_DISABLEABLE, but I probably have the same reaction as you ... "seriously?&amp;nbsp; that is what they named?"&amp;nbsp; I mean come on, I think it could have at least been named PNP_DEVICE_CANNOT_BE_DISABLED.&amp;nbsp; I am sure you can think of some better names too. If so, please leave a comment with your suggestions!&amp;nbsp; While we had a chance to rectify this in KMDF in the &lt;FONT color=#000000&gt;&lt;A class="" href="http://msdn2.microsoft.com/en-us/library/aa491094.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa491094.aspx"&gt;WDF_DEVICE_STATE&lt;/A&gt;&lt;/FONT&gt;&amp;nbsp;structure, we chose to keep the field name (NotDisableable)&amp;nbsp;similar to the WDM name to avoid confusion.&lt;/P&gt;
&lt;P&gt;For any readers who have not encountered this bit, it is a part of &lt;A href="http://msdn2.microsoft.com/en-us/library/ms805981.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms805981.aspx"&gt;PNP_DEVICE_STATE&lt;/A&gt;.&amp;nbsp; You set this bit in the IRP_MJ_PNP/&lt;A href="http://msdn2.microsoft.com/en-us/library/ms806467.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms806467.aspx"&gt;IRP_MN_QUERY_PNP_DEVICE_STATE&lt;/A&gt; IRP after calling &lt;A href="http://msdn2.microsoft.com/en-us/library/aa490455.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa490455.aspx"&gt;IoInvalidateDeviceState&lt;/A&gt;.&amp;nbsp; &lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8324240" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDM/">WDM</category></item><item><title>Once not disableable, forever not disableable</title><link>http://blogs.msdn.com/b/doronh/archive/2008/03/18/once-not-disableable-forever-not-disableable.aspx</link><pubDate>Tue, 18 Mar 2008 18:21:12 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8324256</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8324256</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/03/18/once-not-disableable-forever-not-disableable.aspx#comments</comments><description>&lt;p&gt;One interesting quirk about the &lt;a href="http://msdn2.microsoft.com/en-us/library/ms805981.aspx" target="_blank"&gt;PNP_DEVICE_NOT_DISABLEABLE&lt;/a&gt; state is that once it has been set and the PnP manager has processed it, the state is sticky.&amp;#160; By sticky I mean that even if you attempt to clear this bit on a subsequent IRP_MN_QUERY_PNP_DEVICE_STATE IRP, the PnP manager ignores your changes to this state.&amp;#160; This state remains stuck until any of the following occur&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;The machine is rebooted and the device is reenumerated&lt;/li&gt;    &lt;li&gt;The device (or any device in its ancestry) is surprise removed&lt;/li&gt;    &lt;li&gt;The device (or any device in its ancestry) is ejected&lt;/li&gt; &lt;/ol&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8324256" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WDM/">WDM</category></item><item><title>Returning failure from DriverEntry</title><link>http://blogs.msdn.com/b/doronh/archive/2008/03/17/returning-failure-from-driverentry.aspx</link><pubDate>Tue, 18 Mar 2008 02:56:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8298729</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8298729</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/03/17/returning-failure-from-driverentry.aspx#comments</comments><description>&lt;P&gt;One thing that is easily overlooked about &lt;A href="http://msdn2.microsoft.com/en-us/library/ms794742.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms794742.aspx"&gt;implementing&lt;/A&gt; &lt;A href="http://msdn2.microsoft.com/en-us/library/ms795702.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms795702.aspx"&gt;DriverEntry&lt;/A&gt; is that upon return !NT_SUCCESS, DriverUnload is not called.&amp;nbsp; I mentioned this anecdotally in a previous &lt;A href="http://blogs.msdn.com/doronh/archive/2006/10/23/hindsight-is-20-20-evtdriverunload-should-have-not-been-in-kmdf.aspx" target=_blank mce_href="http://blogs.msdn.com/doronh/archive/2006/10/23/hindsight-is-20-20-evtdriverunload-should-have-not-been-in-kmdf.aspx"&gt;post&lt;/A&gt;, but it is worth expanding on.&amp;nbsp; I was bit by this oversight when I was working on the Bluetooth stack.&amp;nbsp; Driver verifier&amp;nbsp; correctly identified that my driver had leaked pool.&amp;nbsp; The code looked something like this&lt;/P&gt;&lt;PRE&gt;// Globals
UNICODE_STRING gRegistryPath = { 0 };

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;

    DriverObject-&amp;gt;DriverUnload = DriverUnload;

    gRegistryPath.Length = RegistryPath-&amp;gt;Length;
    gRegistryPath.MaximumLength = RegistryPath-&amp;gt;MaximumLength;
    gRegistryPath.Buffer = (PWCHAR) ExAllocatePoolWithTag(PagedPool, gRegistryPath.MaximumLength, [tag]);
    if (gRegistryPath.Buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
    RtlCopyMemory(gRegistryPath.Buffer, RegistryPath-&amp;gt;Buffer, gRegistryPath.Length);

    status = RegisterWithPortDriver(DriverObject, ...);
    if (!NT_SUCCESS(status)) { return status; } &amp;lt;== leak right here!

    // ... other init ...

    return status;
}

void DriverUnload(PDRIVER_OBJECT DriverObject)
{
    ExFreePool(gRegistryPath.Buffer);
    RtlZeroMemory(&amp;amp;gRegistryPath, sizeof(gRegistryPath));
}&lt;/PRE&gt;
&lt;P&gt;While many WDM drivers do very little outside of initializing the dispatch table and other fields in their DriverObject, a miniport driver or a KMDF driver must register with their port driver (like &lt;A href="http://msdn2.microsoft.com/en-us/library/ms805428.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms805428.aspx"&gt;ScsiPortInitialize&lt;/A&gt;) or framework (&lt;A href="http://msdn2.microsoft.com/en-us/library/aa491318.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa491318.aspx"&gt;WdfDriverCreate&lt;/A&gt;) and this registration can introduce failure in DriverEntry (just like in my code sample above).&amp;nbsp; What to do?&amp;nbsp; &lt;/P&gt;
&lt;P&gt;In a WDM driver you have to be very careful and manage this manually.&amp;nbsp; Either you have a common error exit path out of DriverEntry which performs the cleanup (or manually calls your DriverUnload routine) or cleanup on each possible point of error.&amp;nbsp; This pattern is very easy to get wrong and is not very maintainable, it is quite easy to add a new allocation and forget to cleanup it up later.&lt;/P&gt;
&lt;P&gt;In a KMDF driver things are a bit easier to manage if you follow a particular pattern.&amp;nbsp; While &lt;A href="http://msdn2.microsoft.com/en-us/library/aa491324.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa491324.aspx"&gt;EvtDriverUnload&lt;/A&gt; has the same problems as the WDM DriverUnload, the EvtObjectCleanup routine registered on the WDFDRIVER is called in both scenarios.&amp;nbsp; To re-emphasize, the EvtObjectCleanup registered on WDFDRIVER will be called when either DriverEntry returns !NT_SUCCESS or if your driver is gracefully unloaded later.&amp;nbsp; This means that if you put all of your cleanup in the cleanup routine your DriverEntry implemention becomes much simpler.&amp;nbsp; The one caveat is that the call to WdfDriverCreate must come before any allocations in your driver or state chaning APIs.&amp;nbsp; &lt;A href="http://msdn2.microsoft.com/en-us/library/ms793144.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms793144.aspx"&gt;WPP_INIT_TRACING&lt;/A&gt;&amp;nbsp;is one such state changing API where you must undo its effects by calling &lt;A class="" href="http://msdn2.microsoft.com/en-us/library/ms793159.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/ms793159.aspx"&gt;WPP_CLEANUP&lt;/A&gt;.&amp;nbsp; Quite a few WDK samples show this pattern (although suprisingly to me, not all!), let us look at the nonpnp sample (%wdk%\src\kmdf\nonpnp\sys\nonpnp.c) &lt;/P&gt;&lt;PRE&gt;NTSTATUS
DriverEntry(
    IN OUT PDRIVER_OBJECT   DriverObject,
    IN PUNICODE_STRING      RegistryPath
    )
{
    NTSTATUS                       status;
    WDF_DRIVER_CONFIG              config;
    WDFDRIVER                      hDriver;
    PWDFDEVICE_INIT                pInit = NULL;
    WDF_OBJECT_ATTRIBUTES          attributes;

    WDF_DRIVER_CONFIG_INIT(&amp;amp;config, WDF_NO_EVENT_CALLBACK);

    // Tell the framework that this is non-pnp driver so that it doesn't set the default AddDevice routine.
    config.DriverInitFlags |= WdfDriverInitNonPnpDriver;

    // NonPnp driver must explicitly register an unload routine for the driver to be unloaded.
    config.EvtDriverUnload = NonPnpEvtDriverUnload;

&lt;FONT color=#ff0000&gt;    &lt;/FONT&gt;&lt;B&gt;&lt;FONT color=#ff0000&gt;// Register a cleanup callback so that we can call WPP_CLEANUP when
    // the framework driver object is deleted during driver unload.&lt;/FONT&gt;&lt;/B&gt;
    WDF_OBJECT_ATTRIBUTES_INIT(&amp;amp;attributes);
    attributes.EvtCleanupCallback = NonPnpEvtDriverContextCleanup;

    status = WdfDriverCreate(DriverObject,
                            RegistryPath,
                            &amp;amp;attributes,
                            &amp;amp;config,
                            &amp;amp;hDriver);
    if (!NT_SUCCESS(status)) {
        KdPrint (("NonPnp: WdfDriverCreate failed with status 0x%x\n", status));
        return status;
    }

&lt;FONT color=#ff0000&gt;    &lt;/FONT&gt;&lt;B&gt;&lt;FONT color=#ff0000&gt;// Since we are calling WPP_CLEANUP in the DriverContextCleanup
    // callback we should initialize WPP Tracing after WDFDRIVER
    // object is created to ensure that we cleanup WPP properly
    // if we return failure status from DriverEntry. This
    // eliminates the need to call WPP_CLEANUP in every path
    // of DriverEntry.&lt;/FONT&gt;&lt;/B&gt;
    WPP_INIT_TRACING( DriverObject, RegistryPath );

    ...

    return status;
}

VOID NonPnpEvtDriverContextCleanup(WDFDRIVER Driver)
{
    WPP_CLEANUP(WdfDriverWdmGetDriverObject(Driver));
}&lt;/PRE&gt;
&lt;P&gt;The red comments show what is going on.&amp;nbsp; Hopefully the code is self explanatory.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Incidentally, another pattern you can use for global memory allocations is to allocate the memory with &lt;A href="http://msdn2.microsoft.com/en-us/library/aa491566.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/aa491566.aspx"&gt;WdfMemoryCreate&lt;/A&gt; without specifying a parent object.&amp;nbsp; The WDFDRIVER will be the parent object by default and since all child objects are destroyed when the parent is destroyed, all of your allocations will be destroyed after EvtDriverUnload has been called when the WDFDRIVER is destroyed in the unload path.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8298729" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/Design+Patterns/">Design Patterns</category></item><item><title>one of the books that started it all...</title><link>http://blogs.msdn.com/b/doronh/archive/2008/03/12/one-of-the-books-that-started-it-all.aspx</link><pubDate>Wed, 12 Mar 2008 18:38:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8163222</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8163222</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/03/12/one-of-the-books-that-started-it-all.aspx#comments</comments><description>&lt;P&gt;During my sophomore year at &lt;A href="http://www.calpoly.edu/" target=_blank mce_href="http://www.calpoly.edu/"&gt;Cal Poly&lt;/A&gt;, I decided that I wanted to learn about threads, synchronization techniques and other topics associated modern operating systems.&amp;nbsp; Windows 95 had made its debut (yes, it is not a modern OS, but I didn't know that at the time!) and I had heard about Windows NT, but had never seen or used it.&amp;nbsp; Since I had Win95 on my machine at home, I decided that I would go to the &lt;A href="http://www.elcorralbookstore.com/" target=_blank mce_href="http://www.elcorralbookstore.com/"&gt;bookstore&lt;/A&gt; and buy a book on threading.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Well, needless to say there was not a book that was dedicated to threading, but I found a couple of books on Windows programming that included threading as topics.&amp;nbsp; It came down to 2 books, Jeffrey Richter's "Advanced Windows for Win95 and NT" (with &lt;A href="http://www.amazon.com/Advanced-Windows-Developers-Guide-Win32/dp/1556156774/ref=sr_1_11?ie=UTF8&amp;amp;s=books&amp;amp;qid=1205277849&amp;amp;sr=1-11" target=_blank mce_href="http://www.amazon.com/Advanced-Windows-Developers-Guide-Win32/dp/1556156774/ref=sr_1_11?ie=UTF8&amp;amp;s=books&amp;amp;qid=1205277849&amp;amp;sr=1-11"&gt;Napoleon&lt;/A&gt; on the cover) and some other book.&amp;nbsp; I obviously picked Richter's book ;), although now it is called &lt;A href="http://www.amazon.com/Windows-via-C%2B%2B-Pro-Developer/dp/0735624240/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1205278444&amp;amp;sr=1-1" target=_blank mce_href="http://www.amazon.com/Windows-via-C%2B%2B-Pro-Developer/dp/0735624240/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1205278444&amp;amp;sr=1-1"&gt;"Windows via C/C++&lt;/A&gt;."&lt;/P&gt;
&lt;P&gt;This book was, and still is, awesome.&amp;nbsp; I have both the original and new editions.&amp;nbsp; Great detail, very easy to read and met it my needs perfectly (unlike "&lt;A class="" href="http://www.amazon.com/Inside-Microsoft-Programming-Kraig-Brockschmidt/dp/1556158432/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1205278587&amp;amp;sr=1-1" target=_blank mce_href="http://www.amazon.com/Inside-Microsoft-Programming-Kraig-Brockschmidt/dp/1556158432/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1205278587&amp;amp;sr=1-1"&gt;Inside OLE&lt;/A&gt;" by Kraig Brockschmidt which was full of information, I just could not stay awake reading it :) ).&amp;nbsp; IMHO, it successfully started me down the road to becoming a well rounded developer and I think it helped in getting my foot in the door at Microsoft.&amp;nbsp; I have even had the pleasure of letting Jeff know personally what a great book it is and how it shaped me for years to com.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8163222" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/Coding+Thoughts/">Coding Thoughts</category></item><item><title>Getting 64 bit Vista to open my Inbox the way I want it to</title><link>http://blogs.msdn.com/b/doronh/archive/2008/03/11/getting-64-bit-vista-to-open-my-inbox-the-way-i-want-it-to.aspx</link><pubDate>Wed, 12 Mar 2008 02:00:05 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8162496</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>1</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=8162496</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2008/03/11/getting-64-bit-vista-to-open-my-inbox-the-way-i-want-it-to.aspx#comments</comments><description>&lt;p&gt;A bit over a year ago I had to figure out why my Mail key started behaving differently on Vista vs XP and &lt;a href="http://blogs.msdn.com/doronh/archive/2006/11/30/vista-mail-client-quirk.aspx" target="_blank"&gt;wrote&lt;/a&gt; about how I fixed it.&amp;#160; Well, my dev box was so slow that I was able to employ enough sympathy that I got a new one.&amp;#160; While my old box was a 32 bit machine, the new one came preinstalled with 64 bit Vista.&amp;#160; &lt;em&gt;Cool&lt;/em&gt;. &lt;em&gt;&amp;#160;&lt;/em&gt;I finally had a 64 bit box that was powerful enough to develop on (previously all I had a very noisy 64 bit test machine).&amp;#160; &lt;/p&gt;  &lt;p&gt;When I get a new machine that I will be using for dev work I do the following:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Blow away the preinstalled OS and install the latest OS release (beta or RTM) that I can do work on.&amp;#160; In this case, I left the OS alone ;). &lt;/li&gt;    &lt;li&gt;Enlist in windows sources &lt;/li&gt;    &lt;li&gt;Install the latest WDK &lt;/li&gt;    &lt;li&gt;Install Office Pro and Visio Pro (for the lovely WDF pnp/power/power policy state machines) &lt;/li&gt;    &lt;li&gt;Apply the mail key registry change so Outlook works the way I want it to. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Well steps 1 through 4 went by flawlessly and I updated the registry to fix the mail key.&amp;#160; All happy with myself I made the change and pressed the key.&amp;#160; &lt;em&gt;Nothing happened&lt;/em&gt;.&amp;#160; The focus did not change nor did Outlook move to the default folder.&amp;#160; &lt;strong&gt;&lt;em&gt;Phooey&lt;/em&gt;&lt;/strong&gt;.&amp;#160; I double checked the changes in the registry and lo and behold I mistyped the value name and fixed it.&amp;#160; Pressed the mail key.&amp;#160; &lt;em&gt;Still nothing happened.&amp;#160; &lt;strong&gt;Rats&lt;/strong&gt;&lt;/em&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;em&gt;&amp;#160; &lt;/em&gt;&lt;/p&gt;  &lt;p&gt;So now my only recourse was to debug it ;).&amp;#160; First searched for &amp;quot;RegisteredApp&amp;quot; in the source tree (via content indexing) to find the function which processed the WM_APPCOMMAND message.&amp;#160; Once found, I traced it into the internal shlwapi function which looked up the appropriate locations in the registry and traced through it as well.&amp;#160; Not the most enjoyable task either....first, I do not own the code so I am figuring out it intent while determining if it is behaving correctly...and second, it is optimized code so debugging with source was not an option.&amp;#160; &lt;em&gt;Thank god it was not IA64 assembly though.&amp;#160; &lt;strong&gt;Phew&lt;/strong&gt;.&amp;#160; &lt;/em&gt;The one positive through this part of the debugging was that it was 100% reproducible, so when I got lost, I could just hit 'g' and press the mail key again to restart.&lt;/p&gt;  &lt;p&gt;After looking at this for a half an hour or so, the logic was this&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Query for the default mail client in HKCU &lt;/li&gt;    &lt;li&gt;if (WIN64) { If that failed, 1) open the default mail client in the 32 bit HKLM software key and 2) query for the default mail client } &lt;/li&gt;    &lt;li&gt;If that failed, query for the default mail client in the normal HKLM software key (which would be the 64 bit HKLM on 64 bit, the 32 bit HKLM on 32 bit). &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Upon successfully querying for the default mail client, attempt to execute its open verb as registered in the key 64 bit &lt;font face="Courier New"&gt;HKLM\Software\Clients\Mail\&amp;lt;application&amp;gt;\shell\open\command. &lt;/font&gt;If this failed, attempt the same thing under the 32 bit version of the key.&amp;#160; &lt;/p&gt;  &lt;p&gt;The bug was that the result of the 2nd bullet (query on the 32 bit key on 64 bit machines) was being misinterpreted.&amp;#160; The code treated the return value as if a &lt;a href="http://msdn2.microsoft.com/en-us/library/ms724911.aspx" target="_blank"&gt;RegQueryValueEx&lt;/a&gt; call was made, but the call was to &lt;a href="http://msdn2.microsoft.com/en-us/library/ms724897(VS.85).aspx" target="_blank"&gt;RegOpenKeyEx&lt;/a&gt;.&amp;#160; Because the return value was not being evaluated correct, step 2) was never performed (querying for the client on the new key) which led to an error when formatting the string that was used to query for the verb.&amp;#160; &lt;/p&gt;  &lt;p&gt;OK, great.&amp;#160; Now at least I knew where the bug was, but I still wanted my mail key to work and I was definitely not going to run my own custom shlwapi.dll on my own machine.&amp;#160; It is used by too many components for me to have confidence in making that kind of change.&amp;#160; I needed a solution that was as minimal as possible and the least invasive.&amp;#160; In the end I ended up attaching windbg and using the assembler (the 'a' command) to remove the incorrect checks.&amp;#160; My initial removal was to nop the entire sequence...&lt;/p&gt;  &lt;pre&gt;0:004&amp;gt; u xxxxxxxxx`xxxxxx6c  (somewhere in the guts of shlwapi.dll)
$ the call whose result will be misinterpreted
xxxxxxxxx`xxxxxx6c ff153e08fcff    call    qword ptr [SHLWAPI!_imp_RegOpenKeyExW (000007fe`fee918b0)]

$ incorrect tests
xxxxxxxxx`xxxxxx72 85c0            test    eax,eax
xxxxxxxxx`xxxxxx74 8bf8            mov     edi,eax
xxxxxxxxx`xxxxxx76 7474            je      xxxxxxxxx`xxxxxxec
xxxxxxxxx`xxxxxx78 3bc3            cmp     eax,ebx
xxxxxxxxx`xxxxxx7a 742c            je      xxxxxxxxx`xxxxxxa8

$ call to query the value
xxxxxxxxx`xxxxxx7c 488b4c2438      mov     rcx,qword ptr [rsp+38h]
xxxxxxxxx`xxxxxx81 4c8d4c2430      lea     r9,[rsp+30h]
xxxxxxxxx`xxxxxx86 4c8d442440      lea     r8,[rsp+40h]
xxxxxxxxx`xxxxxx8b 33d2            xor     edx,edx
xxxxxxxxx`xxxxxx8d c7442430a0000000 mov     dword ptr [rsp+30h],0A0h
xxxxxxxxx`xxxxxx95 ff150d08fcff    call    qword ptr [SHLWAPI!_imp_RegQueryValueW (000007fe`fee918a8)]

$ now nop out the check, always do the query.  Stop @ xxxx7c since that is the start of the query call
0:004&amp;gt; a xxxxxxxxx`xxxxxx72 
xxxxxxxxx`xxxxxx72 nop
xxxxxxxxx`xxxxxx73 nop
xxxxxxxxx`xxxxxx74 nop
xxxxxxxxx`xxxxxx75 nop
xxxxxxxxx`xxxxxx76 nop
xxxxxxxxx`xxxxxx77 nop
xxxxxxxxx`xxxxxx78 nop
xxxxxxxxx`xxxxxx79 nop
xxxxxxxxx`xxxxxx7a nop
xxxxxxxxx`xxxxxx7b nop

$ and dump out the old code again to make sure the right thing was nop'ed
0:004&amp;gt; u xxxxxxxxx`xxxxxx6c 
xxxxxxxxx`xxxxxx6c ff153e08fcff    call    qword ptr [SHLWAPI!_imp_RegOpenKeyExW (000007fe`fee918b0)]
xxxxxxxxx`xxxxxx72 90              nop
xxxxxxxxx`xxxxxx73 90              nop
xxxxxxxxx`xxxxxx74 90              nop
xxxxxxxxx`xxxxxx75 90              nop
xxxxxxxxx`xxxxxx76 90              nop
xxxxxxxxx`xxxxxx77 90              nop
xxxxxxxxx`xxxxxx78 90              nop
xxxxxxxxx`xxxxxx79 90              nop
xxxxxxxxx`xxxxxx7a 90              nop
xxxxxxxxx`xxxxxx7b 90              nop
xxxxxxxxx`xxxxxx7c 488b4c2438      mov     rcx,qword ptr [rsp+38h]
xxxxxxxxx`xxxxxx81 4c8d4c2430      lea     r9,[rsp+30h]
xxxxxxxxx`xxxxxx86 4c8d442440      lea     r8,[rsp+40h]
xxxxxxxxx`xxxxxx8b 33d2            xor     edx,edx
xxxxxxxxx`xxxxxx8d c7442430a0000000 mov     dword ptr [rsp+30h],0A0h
xxxxxxxxx`xxxxxx95 ff150d08fcff    call    qword ptr [SHLWAPI!_imp_RegQueryValueW (000007fe`fee918a8)]&lt;/pre&gt;

&lt;p&gt;...but my final (and more refined) approach was the actual comparisons in the assembly.&amp;#160; After the in place edits, my mail key now works again!&amp;#160; The only issue is that I have to reapply every time explorer is started, I can live with that. I also filed a bug for the next release of Windows, so I will not have to make the edits in the future and neither will you ;).&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8162496" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WinDBG_2F00_KD+Fun/">WinDBG/KD Fun</category></item><item><title>A must have book for any Windows developer</title><link>http://blogs.msdn.com/b/doronh/archive/2007/12/19/a-must-have-book-for-any-windows-developer.aspx</link><pubDate>Wed, 19 Dec 2007 21:21:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:6808549</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=6808549</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2007/12/19/a-must-have-book-for-any-windows-developer.aspx#comments</comments><description>&lt;P&gt;I saw a book,&amp;nbsp;&amp;nbsp;&lt;A class="" href="http://www.amazon.com/Advanced-Debugging-Addison-Wesley-Microsoft-Technology/dp/0321374460/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1198017273&amp;amp;sr=8-1" mce_href="http://www.amazon.com/Advanced-Debugging-Addison-Wesley-Microsoft-Technology/dp/0321374460/ref=pd_bbs_sr_1?ie=UTF8&amp;amp;s=books&amp;amp;qid=1198017273&amp;amp;sr=8-1"&gt;Advanced Windows Debugging&lt;/A&gt;, in the Microsoft company store and quickly read through it. It looked pretty awesome in the level of detail and breadth that it covered.&amp;nbsp; I ordered my own copy and I think it would be an invaluable resource for anyone who develops drivers or applications on Windows.&amp;nbsp; I learn something new everytime I read it.&lt;/P&gt;
&lt;P&gt;d&lt;/P&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6808549" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/b/doronh/archive/tags/WinDBG_2F00_KD+Fun/">WinDBG/KD Fun</category></item><item><title>Device installation on Vista failing when it used to work?</title><link>http://blogs.msdn.com/b/doronh/archive/2007/11/21/device-installation-on-vista-failing-when-it-used-to-work.aspx</link><pubDate>Thu, 22 Nov 2007 01:55:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:6460535</guid><dc:creator>Doron Holan [MSFT]</dc:creator><slash:comments>8</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://blogs.msdn.com/b/doronh/rsscomments.aspx?WeblogPostID=6460535</wfw:commentRss><comments>http://blogs.msdn.com/b/doronh/archive/2007/11/21/device-installation-on-vista-failing-when-it-used-to-work.aspx#comments</comments><description>There is a hotfix for setupapi.dll, read the KB article &lt;A href="http://support.microsoft.com/kb/937187"&gt;http://support.microsoft.com/kb/937187&lt;/A&gt;&amp;nbsp;and see if it applies to your machine&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6460535" width="1" height="1"&gt;</description></item></channel></rss>