2008-03-05 09:52:00 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: ResourceTracker.cs
//
// Note: Provides tracking for all IoPorts, IoMemory, IoDma, and IoIrq
// that are created by the kernel
//
// #define DEBUG_PARSING
// #define VERBOSE
using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.SingSharp;
using Microsoft.Singularity;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Extending;
using Microsoft.Singularity.Hal;
using Microsoft.Singularity.Loader;
using Microsoft.Singularity.Memory;
using Microsoft.Singularity.V1.Types;
using Microsoft.Singularity.Xml;
namespace Microsoft.Singularity.Io
{
2008-11-17 18:29:00 -05:00
// TODO: this implementation uses an O(n^2) search. If the
2008-03-05 09:52:00 -05:00
// resources were stored in a red-black tree that allowed for duplicate
// entries, we could use an Any-Segments-Intersect algorithm to get it down
// to O(n log n)
public class ResourceTracker
{
// In order to work with the different IoRange objects consistently,
// we're going to wrap them in this object before putting them in
// array lists:
private class ResourceRange
{
public DeviceNode! owner;
public uint begin;
public uint limit;
public bool shared;
public ResourceRange(DeviceNode! owner,
uint begin, uint limit, bool shared)
{
this.owner = owner;
this.begin = begin;
this.limit = limit;
this.shared = shared;
base();
}
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
public override string! ToString()
{
return String.Format("[ResRange: owner={0} begin={1,8:x} limit={2,8:x} shared={3}]",
owner.location,
begin,
limit,
shared.ToString());
}
}
// we track the four types of IoResources with these lists
private static ArrayList ports;
private static ArrayList irqs;
private static ArrayList dmas;
private static ArrayList memory;
// initialize the logs
public static void Initialize()
{
// configure IoResource tracking:
ports = new ArrayList();
irqs = new ArrayList();
dmas = new ArrayList();
memory = new ArrayList();
}
// Find the correct internal list for the give IoRange type.
private static ArrayList FindRangeList(IoRange range)
{
if (range is IoPortRange) {
return ports;
}
else if (range is IoIrqRange) {
return irqs;
}
else if (range is IoDmaRange) {
return dmas;
}
else if (range is IoMemoryRange) {
return memory;
}
else {
// Unknown IoRange type.
DebugStub.Break();
return null;
}
}
2008-11-17 18:29:00 -05:00
#if VERBOSE
2008-03-05 09:52:00 -05:00
private static void DumpIndent(int depth)
{
for (int i = 0; i < depth; i++) {
DebugStub.Write(" ");
}
}
private static void DumpNode(int depth, XmlNode! node)
{
DumpIndent(depth);
DebugStub.Write("<{0} {1}", __arglist(node.Name, node.GetAttributes()));
XmlNode [] children = node.Children;
if (children != null && children.Length != 0) {
DebugStub.WriteLine(">");
for (int i = 0; i < children.Length; i++) {
DumpNode(depth + 1, (XmlNode!)children[i]);
}
DumpIndent(depth);
DebugStub.WriteLine("</{0}>", __arglist(node.Name));
}
else {
DebugStub.WriteLine("/>");
}
}
2008-11-17 18:29:00 -05:00
#endif // VERBOSE
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
// TODO: what about conflicts within the resourceSet?
2008-03-05 09:52:00 -05:00
// Test if a particular IoRange satisfies the requirements of its
// associated Xml metadata
// inefficient O(n) algorithm for finding range overlaps:
private static DeviceNode FindRangeConflict(IoRange! range, bool shared)
{
ArrayList list = FindRangeList(range);
uint begin = range.RangeBase;
uint limit = range.RangeBase + range.RangeLength;
// finally, make sure this resource isn't already claimed
if (begin >= limit || list == null) {
return null;
}
foreach (ResourceRange! r in list) {
if ((begin < r.limit && limit > r.begin) ||
(r.begin < limit && r.limit > begin)) {
if (!shared || !r.shared) {
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("I/O Conflict on {0} with {1}. shared={2}, r.shared={3}",
2008-03-05 09:52:00 -05:00
__arglist(range.ToString(),
2008-11-17 18:29:00 -05:00
r.owner.location,
shared,
r.shared));
DebugStub.WriteLine(" begin={0:x} limit={1:x}, r.begin={2:x}, r.limit={3:x}",
__arglist(begin, limit, r.begin, r.limit));
2008-03-05 09:52:00 -05:00
return r.owner;
}
}
}
return null;
}
// Check if a set of ranges have any conflicts.
private static DeviceNode FindRangeConflicts(IoRange []! ranges, XmlNode! ruleSet)
{
XmlNode[] rules = ruleSet.Children;
if (rules == null) {
return null;
}
foreach (XmlNode! rule in rules) {
int index = rule.GetAttribute(IoSystem.IndexXmlAttribute, -1);
bool shared = rule.GetAttribute(IoSystem.SharedXmlAttribute, false);
DebugStub.Assert(index >= 0);
if (ranges[index] != null) {
DeviceNode conflict = FindRangeConflict((!)ranges[index], shared);
if (conflict != null) {
return conflict;
}
}
}
return null;
}
private static void DescribeResourceConflict(DeviceNode! newDevice,
DeviceNode! oldDevice)
requires newDevice.driver != null;
requires oldDevice.driver != null;
{
String newName = newDevice.driver.name;
String oldName = oldDevice.driver.name;
if (newName == null) {
newName = newDevice.MatchingDeviceId;
}
if (oldName == null) {
oldName = oldDevice.MatchingDeviceId;
}
DebugStub.WriteLine("I/O Conflict between {0} ({1}) and {2} ({3}).n",
__arglist(newDevice.location,
newName,
oldDevice.location,
oldName));
}
// Given an IoConfig object and an Xml blob, we must determine if the
// the resources stated in the IoConfig object are already allocated.
// If they are, we return the identity of the conflicting device.
internal static DeviceNode FindResourceConflicts(DeviceNode! device)
requires device.driver != null;
{
DeviceNode conflict;
XmlNode fixedRules
= device.driver.metadata.GetChild(IoSystem.FixedHardwareSetXmlTag);
if (fixedRules != null) {
conflict = FindRangeConflicts(device.config.FixedRanges, fixedRules);
if (conflict != null) {
DescribeResourceConflict(device, conflict);
return conflict;
}
}
XmlNode dynamicRules =
device.driver.metadata.GetChild(IoSystem.DynamicHardwareSetXmlTag);
if (dynamicRules != null) {
conflict = FindRangeConflicts(device.config.DynamicRanges, dynamicRules);
if (conflict != null) {
DescribeResourceConflict(device, conflict);
return conflict;
}
}
return null;
}
// Test if an IoRange matches the Xml metadata.
private static bool IsRangeValid(IoRange! range, XmlNode! rule)
{
if (range is IoPortRange) {
if (rule.Name != IoSystem.PortRangeXmlTag) {
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("Bad port range tag");
2008-03-05 09:52:00 -05:00
return false;
}
}
else if (range is IoIrqRange) {
if (rule.Name != IoSystem.IrqRangeXmlTag) {
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("Bad Irq range tag");
2008-03-05 09:52:00 -05:00
return false;
}
}
else if (range is IoDmaRange) {
if (rule.Name != IoSystem.DmaRangeXmlTag) {
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("Bad dma range tag");
2008-03-05 09:52:00 -05:00
return false;
}
}
else if (range is IoMemoryRange) {
#if false
if (rule.Name == IoSystem.RegionXmlTag) {
return true; // Note we don't check bounds on this one!
}
#endif
2008-11-17 18:29:00 -05:00
if (rule.Name != IoSystem.MemoryRangeXmlTag) {
DebugStub.WriteLine("Bad dma range tag");
2008-03-05 09:52:00 -05:00
return false;
}
}
else {
// Unknown IoRange type.
DebugStub.Break();
return false;
}
// now make sure that the range length is >= metadata spec
// exclude regions since we always make them ourselves, and
// can trust them
uint expectedLength =
(uint)rule.GetAttribute(IoSystem.RangeLengthXmlAttribute, 1);
if (range.RangeLength < expectedLength) {
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("Bad length {0} < {1}",
__arglist(range.RangeLength,
expectedLength)
);
2008-03-05 09:52:00 -05:00
return false;
}
return true;
}
// We let the resource list contain null entries, and we only check
// those entries that are not null; the metadata doesn't need to know
// about holes.
private static bool AreResourcesValid(IoRange[]! ranges, XmlNode ruleSet)
{
if (ruleSet == null) {
return true;
}
#if DEBUG_PARSING
DebugStub.WriteLine(" AreResourcesValid:");
DumpNode(2, ruleSet);
DebugStub.WriteLine(" I/O Resources:");
2008-11-17 18:29:00 -05:00
if (ranges != null) {
for (int i = 0; i < ranges.Length; i++) {
if (ranges[i] != null) {
DebugStub.WriteLine(" {0:d3}: {1}",
__arglist(i, ((!)ranges[i]).ToString()));
}
2008-03-05 09:52:00 -05:00
}
}
#endif
XmlNode[] rules = ruleSet.Children;
if (rules == null) {
return true;
}
foreach (XmlNode! rule in rules) {
int index = rule.GetAttribute(IoSystem.IndexXmlAttribute, -1);
2008-11-17 18:29:00 -05:00
if (index < 0) {
DebugStub.WriteLine("ResourceTracker.AreResourcesValid: An XML rule either did not specify an 'index' attribute, or the value is invalid.");
DebugStub.WriteLine("ResourceTracker.AreResourcesValid: Returning false to indicate that resources are not valid.");
return false;
}
if (index >= ranges.Length) {
DebugStub.WriteLine("ResourceTracker.AreResourcesValid: An XML rule specifies an index (index {0}) that is out of range for this resource range (length {1}).",
__arglist(index, ranges.Length));
DebugStub.WriteLine("ResourceTracker.AreResourcesValid: Returning false to indicate that resources are not valid.");
return false;
}
2008-03-05 09:52:00 -05:00
// Fail if the range doesn't exist.
if (ranges[index] == null) {
DebugStub.WriteLine("Resource range[" + index + "] is null!");
return false;
}
if (!IsRangeValid((!)ranges[index], rule)) {
DebugStub.WriteLine("Resource range[" + index + "] is not valid!");
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine(((!)ranges[index]).ToString());
DebugStub.Break();
IsRangeValid((!)ranges[index], rule);
2008-03-05 09:52:00 -05:00
return false;
}
#if false
// make sure that the resources are currently available for this driver.
bool shared = rule.GetAttribute(IoSystem.SharedXmlAttribute, false);
DeviceNode conflict = FindRangeConflict((!)ranges[index], shared);
if (conflict != null) {
return false;
}
#endif
}
return true;
}
// Given an IoConfig object and an Xml blob, determine if the
// IoConfig object satisfies the needs expressed as Xml.
internal static bool AreResourcesValid(IoConfig! config, XmlNode! metadata)
{
XmlNode fixedRules
= metadata.GetChild(IoSystem.FixedHardwareSetXmlTag);
XmlNode dynamicRules
= metadata.GetChild(IoSystem.DynamicHardwareSetXmlTag);
2008-11-17 18:29:00 -05:00
if (!AreResourcesValid(config.FixedRanges, fixedRules)) {
DebugStub.WriteLine("AreResourcesValid: config.FixedRanges failed.\n {0}\n", __arglist(config.ToPrint()));
return false;
}
else if (!AreResourcesValid(config.DynamicRanges, dynamicRules)) {
DebugStub.WriteLine("AreResourcesValid: config.DynamicRanges failed.\n {0}", __arglist(config.ToPrint()));
return false;
}
return true;
2008-03-05 09:52:00 -05:00
}
// add one resource to the accounting logs
private static void AddResource(DeviceNode! device,
IoRange! range,
XmlNode rule)
{
ArrayList list = FindRangeList(range);
uint begin = range.RangeBase;
uint limit = range.RangeBase + range.RangeLength;
bool shared = false;
if (rule != null) {
shared = rule.GetAttribute(IoSystem.SharedXmlAttribute, false);
}
if (begin < limit && list != null) {
ResourceRange new_range = new ResourceRange(device, begin, limit, shared);
2008-11-17 18:29:00 -05:00
#if VERBOSE
2008-03-05 09:52:00 -05:00
DebugStub.WriteLine(" " + new_range);
2008-11-17 18:29:00 -05:00
#endif
2008-03-05 09:52:00 -05:00
list.Add(new_range);
}
}
// add one set of resources (either fixed or dynamic)
private static void AddResources(DeviceNode! device,
IoRange []! ranges,
XmlNode ruleSet)
{
2008-11-17 18:29:00 -05:00
if (ranges == null || device == null) {
return;
}
2008-03-05 09:52:00 -05:00
if (ruleSet == null) {
foreach (IoRange range in ranges) {
if (range != null) {
AddResource(device, range, null);
}
}
return;
}
2008-11-17 18:29:00 -05:00
#if VERBOSE
2008-03-05 09:52:00 -05:00
DebugStub.WriteLine("ResourceTracker: Adding resources for device: " + device);
2008-11-17 18:29:00 -05:00
#endif
2008-03-05 09:52:00 -05:00
XmlNode[] rules = (!)ruleSet.Children;
2008-11-17 18:29:00 -05:00
if(rules.Length == 0) {
#if VERBOSE
DebugStub.Print("Ignoring device with rules list of length 0\n");
#endif
return;
}
#if VERBOSE
for (int i = 0; i < rules.Length; i++) {
DebugStub.WriteLine(" ruleset[{0}]:", __arglist(i));
if (rules[i] != null) {
assert rules[i] != null;
DumpNode(2,(!)rules[i]);
}
}
#endif
// Construct a rulearray equal in size to the number of ranges.
// Fill in elements in the ruleArray based on the id field in the rule metadata.
// This ensures that the appropriate metadata is used without there having
// to be 1-to-1, ordered mapping with the ranges.
XmlNode[] ruleArray = new XmlNode[ranges.Length];
assert ruleArray != null;
for (int i = 0; i < rules.Length; i++) {
XmlNode n = rules[i];
assert n != null;
int index = n.GetAttribute(IoSystem.IndexXmlAttribute, -1);
if (index == -1 || index > ruleArray.Length) {
DebugStub.Break();
}
ruleArray[index] = n;
}
for (int x = 0; x < ranges.Length; x++) {
IoRange range = ranges[x];
2008-03-05 09:52:00 -05:00
if (range == null) {
continue;
}
2008-11-17 18:29:00 -05:00
XmlNode rule = ruleArray[x];
2008-03-05 09:52:00 -05:00
AddResource(device, range, rule);
}
}
// Assume that the resources list is no shorter than the rules list
// if the rules list is null, then we assume nothing is shared
internal static void AddResources(DeviceNode! device)
requires device.driver != null;
{
XmlNode fixedRules =
device.driver.metadata.GetChild(IoSystem.FixedHardwareSetXmlTag);
XmlNode dynamicRules =
device.driver.metadata.GetChild(IoSystem.DynamicHardwareSetXmlTag);
AddResources(device, device.config.FixedRanges, fixedRules);
AddResources(device, device.config.DynamicRanges, dynamicRules);
}
}
}