singrdk/base/Kernel/Singularity.Io/ResourceTracker.sg

434 lines
16 KiB
Plaintext

///////////////////////////////////////////////////////////////////////////////
//
// 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
{
// [TODO] - this implementation uses an O(n^2) search. If the
// 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();
}
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;
}
}
#if DEBUG_PARSING
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("/>");
}
}
#endif // DEBUG_PARSING
// [TODO] what about conflicts within the resourceSet?
// 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) {
DebugStub.WriteLine("I/O Conflict on {0} with {1}.",
__arglist(range.ToString(),
r.owner.location));
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) {
return false;
}
}
else if (range is IoIrqRange) {
if (rule.Name != IoSystem.IrqRangeXmlTag) {
return false;
}
}
else if (range is IoDmaRange) {
if (rule.Name != IoSystem.DmaRangeXmlTag) {
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!
}
else
#endif
if (rule.Name != IoSystem.MemoryRangeXmlTag) {
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) {
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:");
for (int i = 0; i < ranges.Length; i++) {
if (ranges[i] != null) {
DebugStub.WriteLine(" {0:d3}: {1}",
__arglist(i, ((!)ranges[i]).ToString()));
}
}
#endif
XmlNode[] rules = ruleSet.Children;
if (rules == null) {
return true;
}
foreach (XmlNode! rule in rules) {
int index = rule.GetAttribute(IoSystem.IndexXmlAttribute, -1);
DebugStub.Assert(index >= 0);
// Fail if the range doesn't exist.
if (ranges[index] == null) {
DebugStub.WriteLine("Resource range[" + index + "] is null!");
// DebugStub.Break();
return false;
}
if (!IsRangeValid((!)ranges[index], rule)) {
DebugStub.WriteLine("Resource range[" + index + "] is not valid!");
// DebugStub.Break();
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);
return (AreResourcesValid(config.FixedRanges, fixedRules) &&
AreResourcesValid(config.DynamicRanges, dynamicRules));
}
// 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);
#if VERBOSE
DebugStub.WriteLine(" " + new_range);
#endif
list.Add(new_range);
}
}
// add one set of resources (either fixed or dynamic)
private static void AddResources(DeviceNode! device,
IoRange []! ranges,
XmlNode ruleSet)
{
if (ruleSet == null) {
foreach (IoRange range in ranges) {
if (range != null) {
AddResource(device, range, null);
}
}
return;
}
#if VERBOSE
DebugStub.WriteLine("ResourceTracker: Adding resources for device: " + device);
#endif
XmlNode[] rules = (!)ruleSet.Children;
int ruleCounter = 0;
foreach (IoRange range in ranges) {
if (range == null) {
continue;
}
XmlNode rule = null;
if (ruleCounter < rules.Length) {
// if we aren't out of rules, get a rule for this
// resource (it's ok to run out of rules)
rule = rules[ruleCounter++];
}
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);
}
}
}