diff options
author | Nick Bofferding <bofferdn@us.ibm.com> | 2015-02-04 16:50:07 -0600 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2015-02-19 16:53:58 -0600 |
commit | b24f1ca24db62f833c7f46e4244dbe8f00ee8a4c (patch) | |
tree | 1043c5f3639390a17f0c0417d4cfb8cea05f2b81 /src/usr | |
parent | 7859f0c8e486326308c3f17200ca5ada75b71a98 (diff) | |
download | talos-hostboot-b24f1ca24db62f833c7f46e4244dbe8f00ee8a4c.tar.gz talos-hostboot-b24f1ca24db62f833c7f46e4244dbe8f00ee8a4c.zip |
Support BIOS framework
- Added BIOS schema file
- Added BIOS xslt petitboot BIOS transformation file
- Updated attribute compiler to generate BIOS metadata
- Updated rules to distribute schema/xslt files
Change-Id: If2df4bfab3861fc128a6cea50077fc370c2cdb0c
RTC: 117599
Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/15583
Tested-by: Jenkins Server
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
Diffstat (limited to 'src/usr')
-rw-r--r-- | src/usr/targeting/common/xmltohb/bios.xsd | 302 | ||||
-rw-r--r-- | src/usr/targeting/common/xmltohb/bios_metadata_petitboot.xslt | 93 | ||||
-rwxr-xr-x | src/usr/targeting/common/xmltohb/xmltohb.pl | 1030 |
3 files changed, 1422 insertions, 3 deletions
diff --git a/src/usr/targeting/common/xmltohb/bios.xsd b/src/usr/targeting/common/xmltohb/bios.xsd new file mode 100644 index 000000000..6cd7ff453 --- /dev/null +++ b/src/usr/targeting/common/xmltohb/bios.xsd @@ -0,0 +1,302 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- IBM_PROLOG_BEGIN_TAG --> +<!-- This is an automatically generated prolog. --> +<!-- --> +<!-- $Source: src/usr/targeting/common/xmltohb/bios.xsd $ --> +<!-- --> +<!-- OpenPOWER HostBoot Project --> +<!-- --> +<!-- Contributors Listed Below - COPYRIGHT 2015 --> +<!-- [+] International Business Machines Corp. --> +<!-- --> +<!-- --> +<!-- Licensed under the Apache License, Version 2.0 (the "License"); --> +<!-- you may not use this file except in compliance with the License. --> +<!-- You may obtain a copy of the License at --> +<!-- --> +<!-- http://www.apache.org/licenses/LICENSE-2.0 --> +<!-- --> +<!-- Unless required by applicable law or agreed to in writing, software --> +<!-- distributed under the License is distributed on an "AS IS" BASIS, --> +<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or --> +<!-- implied. See the License for the specific language governing --> +<!-- permissions and limitations under the License. --> +<!-- --> +<!-- IBM_PROLOG_END_TAG --> + +<xs:schema + xmlns:xs="http://www.w3.org/2001/XMLSchema" > + + <xs:element name="firmware-overrides"> + <xs:annotation> + <xs:documentation>The firmware-overrides element is the root element. An + empty firmware-overrides element implies there are no firmware overrides + for a given platform. The firmware-overrides element can have any + number of attribute or group elements. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element ref="group"/> + <xs:element ref="attribute"/> + </xs:choice> + </xs:sequence> + </xs:complexType> + </xs:element> + + <xs:element name="group"> + <xs:annotation> + <xs:documentation>A group element defines a logical collection of + firmware-overrides, and sub-groups of the same. A group element + contains a name element and any number of attribute + group elements, + but it must have at least one element other than the name element. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element ref="name"/> + <xs:sequence> + <xs:choice maxOccurs="unbounded"> + <xs:element ref="group"/> + <xs:element ref="attribute"/> + </xs:choice> + </xs:sequence> + </xs:sequence> + </xs:complexType> + </xs:element> + + <xs:element name="attribute"> + <xs:annotation> + <xs:documentation>An attribute element must have an id element, and up to + one each of a description element, display-name element, + targetRestriction element. It may also have either a numericOverride + element (if signed/unsigned number) or enumerationOverride element (if + enumeration), in that order. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element ref="id"/> + <xs:element ref="description" minOccurs="0"/> + <xs:element ref="display-name" minOccurs="0"/> + <xs:element ref="targetRestriction" minOccurs="0"/> + <xs:choice minOccurs="0"> + <xs:element ref="enumerationOverride" minOccurs="0"/> + <xs:element ref="numericOverride" minOccurs="0"/> + </xs:choice> + </xs:sequence> + </xs:complexType> + </xs:element> + + <xs:element name="numericOverride"> + <xs:annotation> + <xs:documentation>A numeric override limits the set of values an + attribute can be set to. A numericOverride element must have a start + and end element in any order. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice> + <xs:sequence> + <xs:element ref="start" minOccurs="1" maxOccurs="1"/> + <xs:element ref="end" minOccurs="0" maxOccurs="1"/> + </xs:sequence> + <xs:sequence> + <xs:element ref="end" minOccurs="1" maxOccurs="1"/> + <xs:element ref="start" minOccurs="0" maxOccurs="1"/> + </xs:sequence> + </xs:choice> + </xs:complexType> + </xs:element> + + <xs:element name="targetRestriction"> + <xs:annotation> + <xs:documentation>A target restriction restricts the attribute to apply + only to the target indicated by the sub-restrictions. A + targetRestriction element may have up to one each of + a type, node, position, and unit element, in any order, provided at + least one of those appears. This schema cannot easily restrict the + latter condition, so the code must ensure at least one child element is + present. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:all minOccurs="0" maxOccurs="1"> + <xs:element ref="type" minOccurs="0"/> + <xs:element ref="node" minOccurs="0"/> + <xs:element ref="position" minOccurs="0"/> + <xs:element ref="unit" minOccurs="0"/> + </xs:all> + </xs:complexType> + </xs:element> + + <xs:element name="enumerationOverride"> + <xs:annotation> + <xs:documentation>An enumerationOverride allows an attribute to change + the behavior of the underlying enumeration. An enumerationOverride + element can have one allowedEnumerator element, any number of + enumeratorOverride elements, or a union of those. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice> + <xs:sequence> + <xs:element ref="allowedEnumerators"/> + <xs:element ref="enumeratorOverride" + minOccurs="0" maxOccurs="unbounded"/> + </xs:sequence> + <xs:sequence> + <xs:element ref="enumeratorOverride" maxOccurs="unbounded"/> + <xs:element ref="allowedEnumerators" minOccurs="0"/> + </xs:sequence> + </xs:choice> + </xs:complexType> + </xs:element> + + <xs:element name="allowedEnumerators"> + <xs:annotation> + <xs:documentation>An allowedEnumerator restricts which enumerators can be + used by the attribute.An allowedEnumerators element requires one or more + name elements. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element ref="name" maxOccurs="unbounded"/> + </xs:sequence> + </xs:complexType> + </xs:element> + + <xs:element name="enumeratorOverride"> + <xs:annotation> + <xs:documentation>An enumeratorOverride allows an attribute to change the + default text associated with an enumerator. An enumeratorOverride + requires a name element and a display-name element, descriptionElement, + or union of both. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:sequence> + <xs:element ref="name"/> + <xs:choice> + <xs:sequence> + <xs:element ref="display-name"/> + <xs:element ref="description" minOccurs="0"/> + </xs:sequence> + <xs:sequence> + <xs:element ref="description"/> + <xs:element ref="display-name" minOccurs="0"/> + </xs:sequence> + </xs:choice> + </xs:sequence> + </xs:complexType> + </xs:element> + + <xs:element name="id" type="xs:string"> + <xs:annotation> + <xs:documentation>An id element provides a symbolic attribute ID that maps + to a valid attribute in targeting + </xs:documentation> + </xs:annotation> + </xs:element> + + <xs:element name="description" type="xs:string"> + <xs:annotation> + <xs:documentation>A description element provides a longer help text for an + attribute, enumeration, etc. + </xs:documentation> + </xs:annotation> + </xs:element> + + <xs:element name="display-name" type="xs:string"> + <xs:annotation> + <xs:documentation>A display-name element provides a short name for an + enumeration, attribute, etc. + </xs:documentation> + </xs:annotation> + </xs:element> + + <xs:element name="type" type="xs:string"> + <xs:annotation> + <xs:documentation>A type element restricts the attribute to a specific + named attribute type. + </xs:documentation> + </xs:annotation> + </xs:element> + + <xs:element name="start" type="xs:decimal"> + <xs:annotation> + <xs:documentation>A start element gives the first value, inclusive, of one + of the valid ranges for a numerical attribute. It cannot fall outside + of the base range allowed for the given attribute type, and must be at + least one less than the max of the base range. + </xs:documentation> + </xs:annotation> + </xs:element> + + <xs:element name="end" type="xs:decimal"> + <xs:annotation> + <xs:documentation>An end element gives the last value, inclusive, of one + of the valid ranges for a numerical attribute. It cannot fall outside + of the base range allowed for the given attribute type, and must be at + least one less than the max of the base range. + </xs:documentation> + </xs:annotation> + </xs:element> + + <xs:element name="name" type="xs:string"> + <xs:annotation> + <xs:documentation>A name element gives the name of the attribute group, + etc. + </xs:documentation> + </xs:annotation> + </xs:element> + + <xs:element name="unit"> + <xs:annotation> + <xs:documentation>A unit element allows an attribute to restrict its + scope to a specific target unit. If a position element is specified in + a targetRestriction along with a unit, then the unit's chip's position + must match that as well. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:unsignedByte"> + <xs:minInclusive value="0"/> + <xs:maxInclusive value="254"/> + </xs:restriction> + </xs:simpleType> + </xs:element> + + <xs:element name="position"> + <xs:annotation> + <xs:documentation>A position element allows an attribute to restrict + its scope to a specific position. Only applicable for targets with + positions, unless used in conjuction with the unit element. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:unsignedShort"> + <xs:minInclusive value="0"/> + <xs:maxInclusive value="65534"/> + </xs:restriction> + </xs:simpleType> + </xs:element> + + <xs:element name="node"> + <xs:annotation> + <xs:documentation>A node element allows an attribute to restrict + its scope to a specific node. + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:unsignedByte"> + <xs:minInclusive value="0"/> + <xs:maxInclusive value="14"/> + </xs:restriction> + </xs:simpleType> + </xs:element> + +</xs:schema> + diff --git a/src/usr/targeting/common/xmltohb/bios_metadata_petitboot.xslt b/src/usr/targeting/common/xmltohb/bios_metadata_petitboot.xslt new file mode 100644 index 000000000..ee2a2bcbe --- /dev/null +++ b/src/usr/targeting/common/xmltohb/bios_metadata_petitboot.xslt @@ -0,0 +1,93 @@ +<?xml version="1.0"?> +<!-- IBM_PROLOG_BEGIN_TAG --> +<!-- This is an automatically generated prolog. --> +<!-- --> +<!-- $Source: src/usr/targeting/common/xmltohb/bios_metadata_petitboot.xslt $ --> +<!-- --> +<!-- OpenPOWER HostBoot Project --> +<!-- --> +<!-- Contributors Listed Below - COPYRIGHT 2015 --> +<!-- [+] International Business Machines Corp. --> +<!-- --> +<!-- --> +<!-- Licensed under the Apache License, Version 2.0 (the "License"); --> +<!-- you may not use this file except in compliance with the License. --> +<!-- You may obtain a copy of the License at --> +<!-- --> +<!-- http://www.apache.org/licenses/LICENSE-2.0 --> +<!-- --> +<!-- Unless required by applicable law or agreed to in writing, software --> +<!-- distributed under the License is distributed on an "AS IS" BASIS, --> +<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or --> +<!-- implied. See the License for the specific language governing --> +<!-- permissions and limitations under the License. --> +<!-- --> +<!-- IBM_PROLOG_END_TAG --> + +<xsl:stylesheet version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> + +<xsl:output method="xml" version="1.0" + encoding="UTF-8" indent="yes"/> + +<xsl:template match="/"> + <firmware-overrides> + <xsl:apply-templates select="firmware-overrides/attribute"/> + <xsl:apply-templates select="firmware-overrides/group"/> + </firmware-overrides> +</xsl:template> + +<xsl:template match="attribute"> + <attribute> + <xsl:attribute name="id"> + <xsl:value-of select="id"/> + </xsl:attribute> + <numeric-id><xsl:value-of select="numeric-id"/></numeric-id> + <type> + <size><xsl:value-of select="size"/></size> + <encoding><xsl:value-of select="encoding"/></encoding> + </type> + <default><xsl:value-of select="default"/></default> + <display-name><xsl:value-of select="display-name"/></display-name> + <description><xsl:value-of select="description"/></description> + <target> + <type><xsl:value-of select="target/type"/></type> + <node><xsl:value-of select="target/node"/></node> + <position><xsl:value-of select="target/position"/></position> + <unit><xsl:value-of select="target/unit"/></unit> + </target> + <xsl:apply-templates select="numericOverride"/> + <xsl:apply-templates select="enumeration"/> + </attribute> +</xsl:template> + +<xsl:template match="numericOverride"> + <range> + <start><xsl:value-of select="start"/></start> + <end><xsl:value-of select="end"/></end> + </range> +</xsl:template> + +<xsl:template match="enumeration"> + <enumeration> + <xsl:apply-templates select="./enumerator"/> + </enumeration> +</xsl:template> + +<xsl:template match="enumerator"> + <enumerator> + <display-name><xsl:value-of select="display-name"/></display-name> + <description><xsl:value-of select="description"/></description> + <value><xsl:value-of select="value"/></value> + </enumerator> +</xsl:template> + +<xsl:template match="group"> + <group> + <name><xsl:value-of select="name"/></name> + <xsl:apply-templates select="./attribute"/> + <xsl:apply-templates select="./group"/> + </group> +</xsl:template> + +</xsl:stylesheet> diff --git a/src/usr/targeting/common/xmltohb/xmltohb.pl b/src/usr/targeting/common/xmltohb/xmltohb.pl index d02c1a7ed..6e8db84ca 100755 --- a/src/usr/targeting/common/xmltohb/xmltohb.pl +++ b/src/usr/targeting/common/xmltohb/xmltohb.pl @@ -48,6 +48,7 @@ use Text::Wrap; use Data::Dumper; use POSIX; use Env; +use XML::LibXML; ################################################################################ # Set PREFERRED_PARSER to XML::Parser. Otherwise it uses XML::SAX which contains @@ -76,6 +77,10 @@ my $cfgIncludeFspAttributes = 0; my $CfgSMAttrFile = ""; my $cfgAddVersionPage = 0; +my $cfgBiosXmlFile = undef; +my $cfgBiosSchemaFile = undef; +my $cfgBiosOutputFile = undef; + GetOptions("hb-xml-file:s" => \$cfgHbXmlFile, "src-output-dir:s" => \$cfgSrcOutputDir, "img-output-dir:s" => \$cfgImgOutputDir, @@ -87,6 +92,9 @@ GetOptions("hb-xml-file:s" => \$cfgHbXmlFile, "smattr-output-file:s" => \$CfgSMAttrFile, "include-fsp-attributes!" => \$cfgIncludeFspAttributes, "version-page!" => \$cfgAddVersionPage, + "bios-xml-file:s" => \$cfgBiosXmlFile, + "bios-schema-file:s" => \$cfgBiosSchemaFile, + "bios-output-file:s" => \$cfgBiosOutputFile, "help" => \$cfgHelp, "man" => \$cfgMan, "verbose" => \$cfgVerbose ) || pod2usage(-verbose => 0); @@ -116,6 +124,9 @@ if($cfgVerbose) print STDOUT "Big endian = $cfgBigEndian\n"; print STDOUT "include-fsp-attributes = $cfgIncludeFspAttributes\n", print STDOUT "version-page = $cfgAddVersionPage\n", + print STDOUT "bios-schema-file = $cfgBiosSchemaFile\n"; + print STDOUT "bios-xml-file = $cfgBiosXmlFile\n"; + print STDOUT "bios-output-file = $cfgBiosOutputFile\n"; } ################################################################################ @@ -140,6 +151,11 @@ my @associationTypes = ( PARENT_BY_CONTAINMENT, use constant ATTR_PHYS_PATH => "PHYS_PATH"; use constant ATTR_AFFINITY_PATH => "AFFINITY_PATH"; use constant ATTR_UNKNOWN => "UnknownAttributeName"; +use constant ATTR_POSITION => "POSITION"; +use constant ATTR_CHIP_UNIT => "CHIP_UNIT"; +use constant ATTR_CLASS => "CLASS"; +use constant ATTR_TYPE => "TYPE"; +use constant ATTR_MODEL => "MODEL"; # Data manipulation constants use constant BITS_PER_BYTE => 8; @@ -459,6 +475,7 @@ sub validateAttributes { = { required => 0, isscalar => 0}; $elements{"hwpfToHbAttrMap"} = { required => 0, isscalar => 0}; + $elements{"display-name"} = { required => 0, isscalar => 1}; foreach my $attribute (@{$attributes->{attribute}}) { @@ -5250,6 +5267,10 @@ sub generateTargetingImage { my $pnorRoOffset = $offset; my $attributesWritten = 0; + my %biosData = (); + my %attributeDefCache = + map { $_->{id} => $_} @{$attributes->{attribute}}; + foreach my $targetInstance (@targetsAoH) { my $data; @@ -5285,9 +5306,6 @@ sub generateTargetingImage { # themselves FSP specific. Only need to do this 1x per target instance my $fspTarget = isFspTargetInstance($attributes,$targetInstance); - my %attributeDefCache = - map { $_->{id} => $_} @{$attributes->{attribute}}; - # Must have the same order as the attribute list from above. for my $attributeId (sort @@ -5304,6 +5322,20 @@ sub generateTargetingImage { $attrhash{$attributeId}->{default}; } + # Cache these attributes away in the BIOS data structure as + # identifying information that will be used to enforce the target + # restrictions + if( ($attributeId eq ATTR_PHYS_PATH) + || ($attributeId eq ATTR_POSITION ) + || ($attributeId eq ATTR_CHIP_UNIT) + || ($attributeId eq ATTR_CLASS ) + || ($attributeId eq ATTR_TYPE ) + || ($attributeId eq ATTR_MODEL ) ) + { + $biosData{$targetInstance->{id}}{_identity_}{$attributeId} = + $attrhash{$attributeId}->{default}; + } + my $attrValue = enumNameToValue($attributeIdEnumeration,$attributeId); $attrValue = sprintf ("%0x", $attrValue); @@ -5466,6 +5498,11 @@ sub generateTargetingImage { $attributes, $attributeDef,$attrhash{$attributeId}->{default}); + $biosData{$targetInstance->{id}}{$attributeId}{size} = + (length $heapZeroInitData); + $biosData{$targetInstance->{id}}{$attributeId}{default} = + $attrhash{$attributeId}->{default}; + my $hex = unpack ("H*",$heapZeroInitData); push @attrDataforSM, [$attrValue, $huidValue, $hex, $section, $targetInstance->{id}, $attributeId]; @@ -5490,6 +5527,11 @@ sub generateTargetingImage { $attributes, $attributeDef,$attrhash{$attributeId}->{default}); + $biosData{$targetInstance->{id}}{$attributeId}{size} = + (length $heapPnorInitData); + $biosData{$targetInstance->{id}}{$attributeId}{default} = + $attrhash{$attributeId}->{default}; + my $hex = unpack ("H*",$heapPnorInitData); push @attrDataforSM, [$attrValue, $huidValue, $hex, $section, $targetInstance->{id}, $attributeId]; @@ -5659,6 +5701,11 @@ sub generateTargetingImage { $attributes, $attributeDef,$attrhash{$attributeId}->{default}); + $biosData{$targetInstance->{id}}{$attributeId}{size} = + (length $hbHeapZeroInitData); + $biosData{$targetInstance->{id}}{$attributeId}{default} = + $attrhash{$attributeId}->{default}; + # Align the data as necessary my $pads = ($alignment - ($hbHeapZeroInitOffset % $alignment)) % $alignment; @@ -5981,6 +6028,37 @@ sub generateTargetingImage { - $fspP1DefaultedFromP3Offset)); } + if(defined $cfgBiosXmlFile) + { + unless (-e $cfgBiosXmlFile) + { + fatal("BIOS XML file $cfgBiosXmlFile does not exist.\n"); + } + + unless (defined $cfgBiosSchemaFile) + { + fatal("BIOS XML file $cfgBiosXmlFile specified, but a BIOS schema " + . "file was not.\n"); + } + + unless (-e $cfgBiosSchemaFile) + { + fatal("BIOS schema file $cfgBiosSchemaFile does not exist.\n"); + } + + unless (defined $cfgBiosOutputFile) + { + fatal("BIOS output file not specified.\n"); + } + + my $bios = new + Bios($cfgBiosXmlFile,$cfgBiosSchemaFile,$cfgBiosOutputFile); + $bios->load(); + $bios->processBios( + \%attributeDefCache,\$attributes,\%biosData,%targetPhysicalPath); + $bios->export(); + } + return $outFile; } @@ -6012,6 +6090,935 @@ print SM_TARGET_FILE" close(SM_TARGET_FILE); } +################################################################################ +# BIOS Package +# Consumes platform-specific BIOS XML file, validates it, and outputs +# extended data on the attributes, which will be transformed (via xslt +# stylesheets) and used by 3rd parties (like Petitboot). The BIOS +# package validates the input file against a stylesheet to ensure +# proper formatting. +################################################################################ + +{ + +package Bios; + +################################################################################ +# Constructor; create a new Bios object +################################################################################ + +sub new +{ + my ($class,$biosInputXmlFile,$biosSchemaXsdFile,$biosOutputXmlFile) = @_; + my $self = { + _biosInputXmlFile => $biosInputXmlFile, + _libXmlParser => XML::LibXML->new(), + _biosXmlDoc => undef, + _biosSchemaXsdFile => $biosSchemaXsdFile, + _biosOutputXmlFile => $biosOutputXmlFile, + }; + + bless $self, $class; + + return $self; +} + +################################################################################ +# Load and parse the BIOS XML data, then validate it +################################################################################ + +sub load +{ + my ($self) = @_; + my $biosSchemaXsd = undef; + + eval + { + $self->{_biosXmlDoc} = + $self->{_libXmlParser}->parse_file($self->{_biosInputXmlFile}); + }; + + main::fatal ("Failed to parse BIOS file [$self->{_biosInputXmlFile}].\n" + . " Reason: $@") if $@; + + eval + { + $biosSchemaXsd = + XML::LibXML::Schema->new(location => $self->{_biosSchemaXsdFile} ); + }; + + main::fatal ("Failed to load valid schema [$self->{_biosSchemaXsdFile}].\n" + . "Reason: $@") if $@; + + eval + { + $biosSchemaXsd->validate($self->{_biosXmlDoc}) + }; + + main::fatal ("Failed to validate [$self->{_biosInputXmlFile}] " + . "using schema [$self->{_biosSchemaXsdFile}].\n" + . "Reason: $@") if $@; +} + +################################################################################ +# Export the working version of the BIOS document to a file our STDOUT +################################################################################ + +sub export +{ + my ($self,$forceStdout) = @_; + + if(defined $forceStdout + && ($forceStdout == 1)) + { + print STDOUT "In-memory BIOS XML dump:\n"; + print STDOUT $self->{_biosXmlDoc}->toString(); + } + else + { + open(OUTPUT_XML,">$self->{_biosOutputXmlFile}") or + main::fatal("Could not open output BIOS XML file " + . "[$self->{_biosOutputXmlFile}] for writing.\n" + . "Reason: $!"); + + print OUTPUT_XML $self->{_biosXmlDoc}->toString() or + main::fatal ("Failed write output BIOS XML file " + . "[$self->{_biosOutputXmlFile}].\n" + . "Reason: $!"); + + close OUTPUT_XML or + main::fatal ("Failed to close output BIOS XML file " + . "[$self->{_biosOutputXmlFile}].\n" + . "Reason: $!"); + } +} + +################################################################################ +# Create child element in BIOS XML tree and return it +################################################################################ + +sub createChildElement() +{ + my ($self,$parent,$name,$value) = @_; + + my $child = XML::LibXML::Element->new($name); + + # Value is optional parameter; if not specified, only the child container + # element is created + if(defined $value) + { + $child->appendTextNode($value); + } + $parent->addChild($child); + + return $child; +} + +################################################################################ +# Validate any constraint not enforced by the schema and amend the XML tree +################################################################################ + +sub processBios +{ + my($self,$attrMapRef,$attributesRef,$instanceRef,%targetPhysicalPath) = @_; + + use bigint; + + my %attrTargAttrSetByBios = (); + + # Process all attribute elements regardless of tree location + foreach my $attribute ($self->{_biosXmlDoc}->findnodes('//attribute')) + { + my($id) = $attribute->findnodes('./id'); + my $attributeId = $id->to_literal; + + # Attribute must be defined in targeting + if(!exists $attrMapRef->{$attributeId}) + { + main::fatal("BIOS definition specified attribute $attributeId, but " + . "that attribute is not defined in targeting.\n"); + } + + # Attribute must have volatile persistency + if($attrMapRef->{$attributeId}{persistency} ne "volatile" + && $attrMapRef->{$attributeId}{persistency} ne "volatile-zeroed") + { + main::fatal("BIOS definition specified attribute $attributeId, but " + . "that attribute is neither volatile nor volatile-zeroed " + . "in targeting. Actual persistency is " + . "$attrMapRef->{$attributeId}{persistency}.\n"); + } + + # Attribute must be read only + my $readable = exists $attrMapRef->{$attributeId}{readable} ? 1 : 0; + my $writeable = exists $attrMapRef->{$attributeId}{writeable} ? 1 : 0; + if(!$readable || $writeable) + { + main::fatal("BIOS definition specified attribute $attributeId, but " + . "that attribute is not read-only in targeting. " + . "Readable? " . $readable + . " writeable? " . $writeable . "\n"); + } + + # Attribute must not be an FSP-only attribute + my $fspOnly = exists $attrMapRef->{$attributeId}{fspOnly} ? 1 : 0; + if($fspOnly) + { + main::fatal("BIOS definition specified attribute $attributeId, but " + . "that attribute is FSP only in targeting.\n"); + } + + # Attribute must be an allowed type -and- supported by the BIOS code + # Current support is for signed/unsigned ints (1,2,4,8 bytes in size) + # and enumerations + my $simpleType = exists $attrMapRef->{$attributeId}{simpleType} ? 1 : 0; + my $complexType = + exists $attrMapRef->{$attributeId}{complexType} ? 1 : 0; + my $nativeType = exists $attrMapRef->{$attributeId}{nativeType} ? 1 : 0; + if(!$simpleType) + { + main::fatal("BIOS definition specified attribute $attributeId, but " + . "that attribute is not a simple type in targeting. " + . "Complex type? $complexType, native type? $nativeType.\n"); + } + + # Defines which attributes can be put in the BIOS and provides + # associated BIOS type hint for 3rd party consumer + my %typeHash = (); + + $typeHash{"int8_t"} = + { generalType => 'signed', + min => -128, + max => 127, + ror => 1, + }; + $typeHash{"int16_t"} = + { generalType => 'signed', + min => -32768, + max => 32767, + ror => 1, + }; + $typeHash{"int32_t"} = + { generalType => 'signed', + min => -2147483648, + max => 2147483647, + ror => 1, + }; + $typeHash{"int64_t"} = + { generalType => 'signed', + min => -9223372036854775808, + max => 9223372036854775807, + ror => 1, + }; + $typeHash{"uint8_t"} = + { generalType => 'unsigned', + min => 0, + max => 255, + ror => 1, + }; + $typeHash{"uint16_t"} = + { generalType => 'unsigned', + min => 0, + max => 65535, + ror => 1, + }; + $typeHash{"uint32_t"} = + { generalType => 'unsigned', + min => 0, + max => 4294967295, + ror => 1, + }; + $typeHash{"uint64_t"} = + { generalType => 'unsigned', + min => 0, + max => 18446744073709551615, + ror => 1, + }; + $typeHash{"enumeration"} = + { generalType => 'unsigned', + min => 'na', + max => 'na', + ror => 1, + }; + + my $validType = 0; + my $attrType = "unknown"; + # Convert actual type into a generalized type hint for the BIOS consumer + # and append to the XML tree + foreach my $type (keys %typeHash) + { + if(exists $attrMapRef->{$attributeId}{simpleType}{$type}) + { + $self->createChildElement($attribute,"encoding", + $typeHash{$type}{generalType}); + $attrType = $type; + $validType = 1; + last; + } + } + if(!$validType) + { + main::fatal("BIOS definition specified attribute $attributeId, but " + . "that attribute's type is not supported in BIOS context. " + . "Dump of type:\n" + . ::Dumper($attrMapRef->{$attributeId}{simpleType}) . "\n"); + } + + # Simple type must not be array + if(exists $attrMapRef->{$attributeId}{simpleType}{array}) + { + main::fatal("BIOS definition specified attribute $attributeId, but " + . "that attribute is an array which is not supported in " + . "BIOS context.\n"); + } + + # If attribute definition doesn't have a short name, it must be + # overridden by the BIOS config. Add final value to the XML tree + my($displayNameNodes) = $attribute->findnodes('./display-name'); + if(!$displayNameNodes) + { + if(exists $attrMapRef->{$attributeId}{'display-name'}) + { + $self->createChildElement( + $attribute,"display-name", + $attrMapRef->{$attributeId}{'display-name'}); + } + else + { + main::fatal("BIOS definition specified attribute $attributeId, " + . "but attribute definition does not give a display name, " + . "so BIOS config must (but failed to do so).\n"); + } + } + + # If attribute definition doesn't have a description, it must be + # overridden by the BIOS config. Add final value to the XML tree + my($descriptionNodes) = $attribute->findnodes('./description'); + if(!$descriptionNodes) + { + if(exists $attrMapRef->{$attributeId}{description}) + { + $self->createChildElement( + $attribute,"description", + $attrMapRef->{$attributeId}{description}); + } + else + { + main::fatal("BIOS definition specified attribute $attributeId, " + . "but attribute definition does not give a description, " + . "so BIOS config must (but failed to do so).\n"); + } + } + + my $default = undef; + my $size = undef; + my @targetRestrictions = $attribute->findnodes('./targetRestriction'); + + # Assume all targets have this BIOS attribute to begin with, then prove + # otherwise during processing + my %filteredTargets = %$instanceRef; + foreach my $target (keys %filteredTargets) + { + $filteredTargets{$target}{_allowed_} = 1; + } + + # Hold the target override data + my %restriction = (); + $restriction{node} = 0x0F; # FAPI override value = any position + $restriction{position} = 0xFFFF; # FAPI override value = any position + $restriction{unit} = 0xFF; # FAPI override value = any unit + $restriction{symbolicType} = "*"; # Easy to understand target type + $restriction{numericType} = 0x0; # FAPI type indicator; 0 = any + + # Scan target restriction(s). If no restriction, the BIOS attribute + # must have same default for all instances. If target restriction, all + # attributes that are grouped by the restriction must have the same + # default value. + if(scalar @targetRestrictions > 0) + { + my $restrictNode = "*"; + my $restrictPos = "*"; # NA except for things with positions + # including units + my $restrictUnit = "*"; # NA except for units + my $restrictType = "*"; + + # By default, a BIOS setting applies to every target the attribute + # is assigned to in targeting. However, a target restriction can + # restrict an attribute to one or more subsets of those. The set of + # targets tied to same BIOS attribute+restriction cannot intersect + # with any other similar grouping. Further, all attributes of + # same type tied to same BIOS attribute via the restriction must + # have same default value. Only one target restriction allowed by + # the schema + foreach my $targetRestriction (@targetRestrictions) + { + # Schema validation doesn't easily force targetRestrictions to + # have at least one child, so bail if that's the case + my @childElements = $targetRestriction->findnodes('./*'); + if(scalar @childElements == 0) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, but requested targetRestriction with " + . "no parameters.\n"); + } + + my @nodeElements = $targetRestriction->findnodes('./node'); + my @positionElements + = $targetRestriction->findnodes('./position'); + my @unitElements = $targetRestriction->findnodes('./unit'); + my @typeElements = $targetRestriction->findnodes('./type'); + + if(scalar @nodeElements) + { + $restriction{node} = $nodeElements[0]->to_literal; + } + + if(scalar @positionElements) + { + $restriction{position} = $positionElements[0]->to_literal; + } + + if(scalar @unitElements) + { + $restriction{unit} = $unitElements[0]->to_literal; + } + + # If any type restriction, verify it is a valid type designator + # any hold onto the numeric ID which will end up in the XML tree + if(scalar @typeElements) + { + my $symbolicType = $typeElements[0]->to_literal; + my $typeEnumDef = + ::getEnumerationType($$attributesRef,"TYPE"); + my $numericType = + ::enumNameToValue($typeEnumDef,$symbolicType); + + $restriction{numericType} = $numericType; + $restriction{symbolicType} = $symbolicType; + } + + # Compute the affected targets + foreach my $target (keys %filteredTargets) + { + if(scalar @typeElements > 0) + { + $restrictType = $typeElements[0]->to_literal; + + # Screen out targets that don't match the given type + if($filteredTargets{$target}{_allowed_} == 1) + { + my $type = + $filteredTargets{$target}{_identity_}{TYPE}; + if($restrictType ne $type + && $filteredTargets{$target}{_allowed_} == 1) + { + $filteredTargets{$target}{_allowed_} = 0; + next; + } + } + } + + if(scalar @nodeElements > 0) + { + $restrictNode = $nodeElements[0]->to_literal; + + # Screen out targets that don't match the given node. + # A target matches the node restriction if its physical + # path contains the same node ID. + my $isInSameNodeExpr = + quotemeta "physical:sys-0/node-$restrictNode" ; + my $path = + $filteredTargets{$target}{_identity_}{PHYS_PATH}; + + if($path !~ m/^$isInSameNodeExpr/) + { + $filteredTargets{$target}{_allowed_} = 0; + next; + } + } + + if(scalar @unitElements > 0) + { + $restrictUnit = $unitElements[0]->to_literal; + + my $checkPosition = 0; + + # Screen out targets that do not have unit attributes, + # or which have non-matching unit attributes + if( (!exists + $filteredTargets{$target}{_identity_}{CHIP_UNIT}) + || ($filteredTargets{$target}{_identity_}{CHIP_UNIT} + != $restrictUnit) ) + { + $filteredTargets{$target}{_allowed_} = 0; + next; + } + else + { + $checkPosition = 1; + } + + # If unit matches, need to make sure it also sits on + # chip at requested position, if specified + if($checkPosition && ((scalar @positionElements) > 0)) + { + my $candidatePath = + $filteredTargets{$target}{_identity_}{PHYS_PATH}; + + my $parent_phys_path = substr( + $candidatePath, 0,(rindex $candidatePath, "/")); + my $foundPosition = 0; + + # Walk from unit to whatever parent has the position + # attribute; Remove this target from consideration + # if the position does not match the restriction + while( + defined $targetPhysicalPath{$parent_phys_path}) + { + if(defined $filteredTargets{ + $targetPhysicalPath{$parent_phys_path} + }{_identity_}{POSITION}) + { + if ($filteredTargets{ + $targetPhysicalPath{$parent_phys_path} + }{_identity_}{POSITION} + == $positionElements[0]->to_literal) + { + $foundPosition = 1; + } + + last; + } + + $candidatePath = $parent_phys_path; + $parent_phys_path = substr( + $candidatePath, 0, + (rindex $candidatePath, "/")); + } + + if(!$foundPosition) + { + $filteredTargets{$target}{_allowed_} = 0; + next; + } + } + } + + # If no unit restriction but position restriction, screen + # out targets that don't have position attribute or whose + # position attribute does not match the restriction + if( (scalar @positionElements > 0) + && (scalar @unitElements == 0) ) + { + $restrictPos = $positionElements[0]->to_literal; + + if( (!exists + $filteredTargets{$target}{_identity_}{POSITION}) + || ( $filteredTargets{$target}{_identity_}{POSITION} + != $restrictPos)) + { + $filteredTargets{$target}{_allowed_} = 0; + } + } + } + } + } + + foreach my $target (keys %filteredTargets) + { + # Don't bother with any target that is not in play + if($filteredTargets{$target}{_allowed_} != 1) + { + next; + } + + # Candidate is any target having the attribute and a default + # value determined + if( exists $instanceRef->{$target}{$attributeId} + && exists $instanceRef->{$target}{$attributeId}{default} + && exists $instanceRef->{$target}{$attributeId}{size}) + { + # Multiple BIOS directives cannot touch same + # target/attribute + if(defined $attrTargAttrSetByBios{$target}{$attributeId}) + { + main::fatal("$target, $attributeId set by multiple BIOS " + . "definitions.\n"); + } + else + { + $attrTargAttrSetByBios{$target}{$attributeId} = 1; + } + + # If no default set yet + if(! (defined $default) ) + { + $default = $instanceRef->{$target}{$attributeId}{default}; + $size = $instanceRef->{$target}{$attributeId}{size}; + } + # Cannot have different defaults in same group + elsif( $instanceRef->{$target}{$attributeId}{default} + != $default ) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, but default values are not same " + . "across all qualifying targets. " + . "$target/ $attributeId = " + . "$instanceRef->{$target}{$attributeId}{default}.\n"); + } + } + } + + # If no targets had the attribute + if(!defined $default || !defined $size) + { + main::fatal("BIOS definition specified attribute $attributeId, but " + . "after considering target restrictions/etc., no " + . "valid targets were found.\n"); + } + + # Add the restriction info to the XML tree. This info is useful for 3rd + # parties to create Hostboot attribute overrides. + my $targetElement = $self->createChildElement($attribute,"target"); + $self->createChildElement( + $targetElement,"type",sprintf("0x%08X",$restriction{numericType})); + $self->createChildElement( + $targetElement,"node",sprintf("0x%02X",$restriction{node})); + $self->createChildElement( + $targetElement,"position",sprintf("0x%04X",$restriction{position})); + $self->createChildElement( + $targetElement,"unit",sprintf("0x%02X",$restriction{unit})); + + my @enumerationOverrideElement + = $attribute->findnodes('./enumerationOverride'); + if($attrType eq "enumeration") + { + # Get a copy of the enumeration description. Schema requires + # enumerator to have only a name (symbolic handle) + value. + # Customization allows overriding description + short name + # Customization allows specifying subset of values to actually use + # Output includes short name, long name, value + my $enumType = + $attrMapRef->{$attributeId}{simpleType}{enumeration}{id}; + my $enumerationType = + ::getEnumerationType($$attributesRef,$enumType); + + my %foundAllowed = (); + my $enableRestrictions = 0; + if(scalar @enumerationOverrideElement) + { + # Each enumerator name must tie back to a valid enumeration + foreach my $overrideNameElement ( + $attribute->findnodes( + './enumerationOverride/allowedEnumerators/name')) + { + my $overrideName = $overrideNameElement->to_literal; + my $nameFound = 0; + foreach my $enumerator (@{$enumerationType->{enumerator}}) + { + if($enumerator->{name} eq $overrideName) + { + # Each allowed value must be different from all the + # rest previously found for this attribute + if(exists $foundAllowed{$overrideName}) + { + main::fatal("BIOS definition specified " + . "attribute $attributeId, and supplied " + . "override to allowed enumeration values, " + . "but duplicated a value of " + . "$overrideName.\n"); + } + else + { + $foundAllowed{$overrideName} = 1; + $enumerator->{allowed} = 1; + } + + $enableRestrictions = 1; + $nameFound = 1; + + last; + } + } + + if(!$nameFound) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, and supplied override to allowed " + . "enumeration values, but requested enumerator " + . "$overrideName is not valid.\n"); + } + } + + # Apply any overrides to the enumerator text + my %textOverrideAllowed = (); + foreach my $enumeratorOverrideElement ( + $attribute->findnodes( + './enumerationOverride/enumeratorOverride')) + { + # Each override must have exactly one name element + my @name = $enumeratorOverrideElement->findnodes('./name'); + + # Each override must have 0 or 1 display-name and + # descriptions elements + my @displayName = + $enumeratorOverrideElement->findnodes('./display-name'); + my @description = + $enumeratorOverrideElement->findnodes('./description'); + + # Make sure enumerator name is valid + my $overrideName = $name[0]->to_literal; + my $nameFound = 0; + foreach my $enumerator (@{$enumerationType->{enumerator}}) + { + if($enumerator->{name} eq $overrideName) + { + # Must not duplicate already overridden enumerator + if(exists $textOverrideAllowed{$overrideName}) + { + main::fatal("BIOS definition specified " + . "attribute $attributeId, and supplied " + . "override to allowed enumerator, " + . "but already overrode this " + . "enuemrator.\n"); + } + # Can only override things that are not restricted + elsif( !exists $foundAllowed{$overrideName} + && $enableRestrictions) + { + main::fatal("BIOS definition specified " + . "attribute $attributeId, and supplied " + . "override to allowed enumerator, but " + . "this enumerator $overrideName is " + . "restricted.\n"); + } + else + { + $textOverrideAllowed{$overrideName} = 1; + } + + if(scalar @displayName) + { + $enumerator->{'display-name'} = + $displayName[0]->to_literal; + } + + if(scalar @description) + { + $enumerator->{description} = + $description[0]->to_literal; + } + + $nameFound = 1; + last; + } + } + + if(!$nameFound) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, and supplied override to allowed " + . "enumeration, but requested invalid enumerator " + . "name to override of $overrideName.\n"); + } + } + } + + my $enumerationElement = + $self->createChildElement($attribute,"enumeration"); + + foreach my $enumerator (@{$enumerationType->{enumerator}}) + { + if($enableRestrictions && !exists $enumerator->{allowed}) + { + # If enumerator is not allowed and is same as attribute + # default then can't continue + if($default eq $enumerator->{name}) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId as not allowing " + . "$enumerator->{name}, but that is the attribute " + . "default.\n"); + } + + next; + } + + my $enumeratorElement = + $self->createChildElement($enumerationElement,"enumerator"); + + if(!exists $enumerator->{'display-name'}) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, and supplied override to enumerator " + . "$enumerator->{name}, but there is no " + . "display-name.\n"); + } + + $self->createChildElement( + $enumeratorElement,"display-name", + $enumerator->{'display-name'}); + + if(!exists $enumerator->{description}) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, and supplied override to enumerator " + . "$enumerator->{name}, but there is no " + . "description.\n"); + } + + $self->createChildElement( + $enumeratorElement,"description",$enumerator->{description}); + $self->createChildElement( + $enumeratorElement,"value", + sprintf("0x%08X",$enumerator->{value})); + } + + # Translate attribute default to actual value + $default = ::enumNameToValue($enumerationType,$default); + } + # No enums allowed to be specified + elsif(scalar @enumerationOverrideElement) + { + main::fatal("BIOS definition specified attribute $attributeId, " + . "and supplied enumerated values, but this is not an " + . "enumeration attribute, it is of type $attrType.\n"); + } + + # Can only specify numeric override for signed/unsigned values of + # 1,2,4,8 byte values + my @numericOverrideElements= $attribute->findnodes('./numericOverride'); + + # Hash of min(key), max(value) pairs + my %allowedRange = (); + if(scalar @numericOverrideElements) + { + # If range override not allowed for this attribute, bail + if($typeHash{$attrType}{ror} != 1) + { + main::fatal("BIOS definition specified attribute $attributeId, " + . "and supplied numeric override, but this attribute " + . "type does not support such override. Type is " + . "$attrType.\n"); + } + + # A numericOverride has exactly one start + end element + my @startElements = + @numericOverrideElements[0]->findnodes('./start'); + my @endElements = @numericOverrideElements[0]->findnodes('./end'); + my $min = $typeHash{$attrType}{min}; + my $max = $typeHash{$attrType}{max}; + + # Validate range min + if(scalar @startElements) + { + my $rawMin = @startElements[0]->to_literal; + + if($rawMin < $min || $rawMin > $max) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, and supplied valid start or end " + . "range, but min out of range. requested " + . "min = $rawMin, allowed min = " + . "$typeHash{$attrType}{min}, allowed max = " + . "$typeHash{$attrType}{max}.\n"); + } + $min = $rawMin; + } + + # Validate range max + if(scalar @endElements) + { + my $rawMax = @endElements[0]->to_literal; + + if($rawMax > $max || $rawMax < $min) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, and supplied valid start or end " + . "range, but max out of range. requested " + . "max = $rawMax, current min = $min, " + . "allowed min = " + . "$typeHash{$attrType}{min}, allowed max = " + . "$typeHash{$attrType}{max}.\n"); + } + $max = $rawMax; + } + + # Min must be <= Max + if($min > $max) + { + main::fatal("BIOS definition specified attribute " + . "$attributeId, and supplied valid start or end " + . "range, but min > max. min = $min, " + . "max = $max.\n"); + } + + # To prevent range overlaps, cannot duplicate the same start of + # a range + if(defined $allowedRange{$min}) + { + main::fatal("Already range starting at $min.\n"); + } + + $allowedRange{$min} = $max; + my $defaultInRange = 0; + + my $min = undef; + my $max = undef; + + # Ensure default value falls within a defined range. The ranges are + # sorted in numerically ascending order according to the start of + # each range, so if the start of + # range N falls within range N-1, we know there is an illegal range + # overlap and fail. + foreach my $rangeKey (sort {$a <=> $b} keys %allowedRange) + { + if( ($default >= $rangeKey) + && ($default <= $allowedRange{$rangeKey}) ) + { + $defaultInRange = 1; + } + + if(!defined $min) + { + $min = $rangeKey; + $max = $allowedRange{$rangeKey}; + next; + } + + if($rangeKey <= $max) + { + main::fatal("Range starting with $rangeKey overlaps " + . "range ending at $max.\n"); + } + + $min = $rangeKey; + $max = $allowedRange{$rangeKey}; + } + + if(!$defaultInRange) + { + main::fatal("Default value $default not in any valid range.\n"); + } + } + + # Convert attribute ID to numerical value and append to XML tree + my $attributeIdEnumeration + = ::getAttributeIdEnumeration($$attributesRef); + my $attrValue = ::enumNameToValue($attributeIdEnumeration,$attributeId); + $attrValue = sprintf ("0x%0X", $attrValue); + + # Add the remaining metadata to the XML tree + $self->createChildElement($attribute,"default",$default); + $self->createChildElement($attribute,"numeric-id",$attrValue); + $self->createChildElement($attribute,"size",$size); + } +} + +1; + +} + __END__ =head1 NAME @@ -6110,6 +7117,23 @@ Adds 4096 bytes of version page as first page in the generated binaries. Does not add 4096 bytes of version page as first page in the generated binaries . This is the default behavior. +=item B<--bios-xml-file> + +Path + file name of XML file describing the platform's BIOS configuration. +Optional. + +=item B<--bios-schema-file> + +Path + file name of XSD schema file used to validate the BIOS XML file. Required +if a BIOS XML file is given. + +=item B<--bios-output-file> + +Path + filename of output XML file describing the amended BIOS configuration. +This output is commonly later modified by 3rd parties via xslt transformation to +tailor the output for a given application. Required if a BIOS XML file is +given. + =item B<--verbose> Prints out some internal workings |