1#! /usr/bin/perl 2 3# This script is used for generating callout lists from the MRW for devices 4# that can be accessed from the BMC. The callouts have a location code, the 5# target name (for debug), a priority, and in some cases a MRU. The script 6# supports I2C, FSI, SPI, FSI-I2C, and FSI-SPI devices. The output is a JSON 7# file organized into sections for each callout type, with keys based on the 8# type. I2c uses a bus and address, FSI uses a link, and SPI uses a bus 9# number. If FSI is combined with I2C or SPI, then the link plus the I2C/SPI 10# keys is used. Multi-hop FSI links are indicated by a dash in between the 11# links, eg "0-1". 12# 13# An example section is: 14# "FSI": 15# { 16# "5": 17# { 18# "Callouts":[ 19# { 20# "Priority":"H" 21# "LocationCode": "P1-C50", 22# "MRU":"/sys-0/node-0/motherboard/proc_socket-0/module-0/power9-0", 23# "Name":"/sys-0/node-0/motherboard/cpu0" 24# }, 25# { 26# "Priority":"H", 27# "LocationCode": "P1-C42", 28# "MRU":"/sys-0/node-0/motherboard/ebmc-card/BMC-0", 29# "Name":"/sys-0/node-0/motherboard/ebmc-card" 30# }, 31# { 32# "Priority":"L", 33# "LocationCode": "P1", 34# "Name":"/sys-0/node-0/motherboard" 35# } 36# ], 37# "Dest":"/sys-0/node-0/motherboard-0/proc_socket-0/module-0/power9-0", 38# "Source":"/sys-0/node-0/motherboard-0/ebmc-card-connector-0/card-0/bmc-0" 39# } 40# } 41# The Name, Dest and Source entries are MRW targets and are just for debug. 42# 43# The optional --segments argument will output a JSON file of all the bus 44# segments in the system, which is what the callouts are made from. 45 46use strict; 47use warnings; 48 49use mrw::Targets; 50use mrw::Util; 51use Getopt::Long; 52use File::Basename; 53use JSON; 54 55my $mrwFile = ""; 56my $outFile = ""; 57my $printSegments = 0; 58 59# Not supporting priorites A, B, or C until necessary 60my %priorities = (H => 3, M => 2, L => 1); 61 62# Segment bus types 63my %busTypes = ( I2C => 1, FSIM => 1, FSICM => 1, SPI => 1 ); 64 65GetOptions( 66 "m=s" => \$mrwFile, 67 "o=s" => \$outFile, 68 "segments" => \$printSegments 69) 70 or printUsage(); 71 72if (($mrwFile eq "") or ($outFile eq "")) 73{ 74 printUsage(); 75} 76 77# Load system MRW 78my $targets = Targets->new; 79$targets->loadXML($mrwFile); 80 81# Find all single segment buses that we care about 82my %allSegments = getPathSegments(); 83 84# Write the segments to a JSON file 85if ($printSegments) 86{ 87 my $outDir = dirname($outFile); 88 my $segmentsFile = "$outDir/segments.json"; 89 90 open(my $fh, '>', $segmentsFile) or 91 die "Could not open file '$segmentsFile' $!"; 92 93 my $json = JSON->new; 94 $json->indent(1); 95 $json->canonical(1); 96 my $text = $json->encode(\%allSegments); 97 print $fh $text; 98 close $fh; 99} 100 101# Returns a hash of all the FSI, I2C, and SPI segments in the MRW 102sub getPathSegments 103{ 104 my %segments; 105 foreach my $target (sort keys %{$targets->getAllTargets()}) 106 { 107 my $numConnections = $targets->getNumConnections($target); 108 109 if ($numConnections == 0) 110 { 111 next; 112 } 113 114 for (my $connIndex=0;$connIndex<$numConnections;$connIndex++) 115 { 116 my $connBusObj = $targets->getConnectionBus($target, $connIndex); 117 my $busType = $connBusObj->{bus_type}; 118 119 # We only care about certain bus types 120 if (not exists $busTypes{$busType}) 121 { 122 next; 123 } 124 125 my $dest = $targets->getConnectionDestination($target, $connIndex); 126 127 my %segment; 128 $segment{BusType} = $busType; 129 $segment{SourceUnit} = $target; 130 $segment{SourceChip} = getParentByClass($target, "CHIP"); 131 if ($segment{SourceChip} eq "") 132 { 133 die "Warning: Could not get parent chip for source $target\n"; 134 } 135 136 $segment{DestUnit} = $dest; 137 $segment{DestChip} = getParentByClass($dest, "CHIP"); 138 139 # If the unit's direct parent is a connector that's OK too. 140 if ($segment{DestChip} eq "") 141 { 142 my $parent = $targets->getTargetParent($dest); 143 if ($targets->getAttribute($parent, "CLASS") eq "CONNECTOR") 144 { 145 $segment{DestChip} = $parent; 146 } 147 } 148 149 if ($segment{DestChip} eq "") 150 { 151 die "Warning: Could not get parent chip for dest $dest\n"; 152 } 153 154 my $fruPath = $targets->getBusAttribute( 155 $target, $connIndex, "FRU_PATH"); 156 157 if (defined $fruPath) 158 { 159 $segment{FRUPath} = $fruPath; 160 my @callouts = getFRUPathCallouts($fruPath); 161 $segment{Callouts} = \@callouts; 162 } 163 else 164 { 165 $segment{FRUPath} = ""; 166 my @empty; 167 $segment{Callouts} = \@empty; 168 } 169 170 if ($busType eq "I2C") 171 { 172 $segment{I2CBus} = $targets->getAttribute($target, "I2C_PORT"); 173 $segment{I2CAddress} = 174 hex($targets->getAttribute($dest, "I2C_ADDRESS")); 175 176 $segment{I2CBus} = $segment{I2CBus}; 177 178 # Convert to the 7 bit address that linux uses 179 $segment{I2CAddress} = 180 Util::adjustI2CAddress($segment{I2CAddress}); 181 } 182 elsif ($busType eq "FSIM") 183 { 184 $segment{FSILink} = 185 hex($targets->getAttribute($target, "FSI_LINK")); 186 } 187 elsif ($busType eq "SPI") 188 { 189 $segment{SPIBus} = $targets->getAttribute($target, "SPI_PORT"); 190 191 # Seems to be in HEX sometimes 192 if ($segment{SPIBus} =~ /^0x/i) 193 { 194 $segment{SPIBus} = hex($segment{SPIBus}); 195 } 196 } 197 198 push @{$segments{$busType}}, { %segment }; 199 } 200 } 201 202 return %segments; 203} 204 205#Breaks the FRU_PATH atttribute up into its component callouts. 206#It looks like: "H:<some target>,L:<some other target>(<MRU>)" 207#Where H/L are the priorities and can be H/M/L. 208#The MRU that is in parentheses is optional and is a chip name on that 209#FRU target. 210sub getFRUPathCallouts 211{ 212 my @callouts; 213 my $fruPath = shift; 214 215 my @entries = split(',', $fruPath); 216 217 for my $entry (@entries) 218 { 219 my %callout; 220 my ($priority, $path) = split(':', $entry); 221 222 # pull the MRU out of the parentheses at the end and then 223 # remove the parentheses. 224 if ($path =~ /\(.+\)$/) 225 { 226 ($callout{MRU}) = $path =~ /\((.+)\)/; 227 228 $path =~ s/\(.+\)$//; 229 } 230 231 # check if the target we read out is valid by 232 # checking for a required attribute 233 if ($targets->isBadAttribute($path, "CLASS")) 234 { 235 die "FRU Path $path not a valid target\n"; 236 } 237 238 $callout{Priority} = $priority; 239 if (not exists $priorities{$priority}) 240 { 241 die "Invalid priority: '$priority' on callout $path\n"; 242 } 243 244 $callout{Name} = $path; 245 246 push @callouts, \%callout; 247 } 248 249 return @callouts; 250} 251 252# Returns an ancestor target based on its class 253sub getParentByClass 254{ 255 my ($target, $class) = @_; 256 my $parent = $targets->getTargetParent($target); 257 258 while (defined $parent) 259 { 260 if (!$targets->isBadAttribute($parent, "CLASS")) 261 { 262 if ($class eq $targets->getAttribute($parent, "CLASS")) 263 { 264 return $parent; 265 } 266 } 267 $parent = $targets->getTargetParent($parent); 268 } 269 270 return ""; 271} 272 273sub printUsage 274{ 275 print "$0 -m <MRW file> -o <Output filename> [--segments] [-n]\n" . 276 " -m <MRW file> = The MRW XML\n" . 277 " -o <Output filename> = The output JSON\n" . 278 " [--segments] = Optionally create a segments.json file\n" . 279 exit(1); 280} 281