blob: 9a564df459247e10f3148b8f5b9481ef0294df6b [file] [log] [blame]
Matt Spinler7d381e12016-09-27 14:27:24 -05001#!/usr/bin/env perl
2
3#Generates a BMC device tree syntax file from the machine
4#readable workbook.
5
6use strict;
7use XML::Simple;
8use mrw::Targets;
9use Getopt::Long;
10
11use constant VERSION => "/dts-v1/;";
12use constant STANDALONE_PROPERTY => "standalone_property";
13use constant DTSI_INCLUDE => "DTSI_INCLUDE";
14
15my $serverwizFile;
16my $outputFile;
17my $debug;
18
19GetOptions("x=s" => \$serverwizFile,
20 "o=s" => \$outputFile,
21 "d" => \$debug)
22or printUsage();
23
24if ((not defined $serverwizFile) || (not defined $outputFile)) {
25 printUsage();
26}
27
28my $g_targetObj = Targets->new;
29$g_targetObj->loadXML($serverwizFile);
30
31my $g_bmc = getBMCTarget();
32if (length($g_bmc) == 0) {
33 die "Unable to find a BMC in this system\n";
34}
35
36my $g_bmcModel = $g_targetObj->getAttribute($g_bmc, "MODEL");
37my $g_bmcMfgr = $g_targetObj->getAttribute($g_bmc, "MANUFACTURER");
38my $g_systemName = $g_targetObj->getSystemName();
39
40open (my $f, ">$outputFile") or die "Could not open $outputFile\n";
41
42printVersion($f);
43printIncludes($f, 0);
44printRootNodeStart($f);
45
46printPropertyList($f, 1, "model", getSystemBMCModel());
47
48printPropertyList($f, 1, "compatible", getBMCCompatibles());
49printNode($f, 1, "chosen", getChosen());
50printNode($f, 1, "memory", getMemory($g_bmc));
51
52#TODO: LEDs, UART, I2C, aliases, pinctlr
53printRootNodeEnd($f, 0);
54
55printNodes($f, 0, getMacNodes());
56
57printNodes($f, 0, getVuartNodes());
58
59close $f;
60exit 0;
61
62
63
64#Return a hash that represents the 'chosen' node
65sub getChosen()
66{
67 my $bmcStdOut = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_CHOSEN",
68 "stdout-path");
69 my $args = $g_targetObj->getAttributeField($g_bmc, "BMC_DT_CHOSEN",
70 "bootargs");
71 my %chosen;
72 $chosen{"stdout-path"} = $bmcStdOut;
73 $chosen{"bootargs"} = $args;
74 return %chosen;
75}
76
77
78#Returns a list of hashes that represent the MAC (ethernet) nodes on the BMC
79sub getMacNodes()
80{
81 my @nodes;
82 my $children = $g_targetObj->getTargetChildren($g_bmc);
83
84 #The next version of this will look for ethernet connections in the
85 #MRW instead of just the units...
86 foreach my $c (@$children) {
87
88 if ($g_targetObj->getTargetType($c) eq "unit-ethernet-master") {
89
90 if ($g_targetObj->getAttribute($c, "UNIT_ENABLED") == 1) {
91 my %node;
92 my $num = $g_targetObj->getAttribute($c, "CHIP_UNIT");
93 my $ncsi = $g_targetObj->getAttribute($c, "NCSI_MODE");
94 my $hwChecksum = $g_targetObj->getAttribute($c,
95 "USE_HW_CHECKSUM");
96
97 my $name = "mac$num";
98 $node{$name}{status} = "okay";
99 if ($ncsi == 1) {
100 $node{$name}{"use-ncsi"} = STANDALONE_PROPERTY;
101 }
102 if ($hwChecksum == 0) {
103 $node{$name}{"no-hw-checksum"} = STANDALONE_PROPERTY;
104 }
105
106 push @nodes, { %node };
107 }
108 }
109 }
110 return @nodes;
111}
112
113
114#Returns a last of hashes that represent the virtual UART nodes
115sub getVuartNodes()
116{
117 my @nodes;
118 my %node;
119
120 #For now, enable 1 node all the time.
121 #TODO if this needs to be fixed.
122 $node{vuart}{status} = "okay";
123
124 push @nodes, { %node };
125
126 return @nodes;
127}
128
129
130#Returns a hash{'reg'} = "<.....>" based on the
131#BMC_DT_MEMORY attribute. This is used to display
132#memory ranges.
133sub getMemory()
134{
135 my $target = shift;
136 my $memory = $g_targetObj->getAttribute($target, "BMC_DT_MEMORY");
137 my @mem = split(',', $memory);
138 my %property;
139 my $val = "<";
140
141 #Encoded as 4 <base address>,<size> pairs of memory ranges
142 #Unused ranges are all 0s.
143 #For now, assumes 32 bit numbers, revisit later for 64 bit support
144 #Convert it into: <num1 num2 num3 num4 etc>
145
146 for (my $i = 0;$i < scalar @mem;$i += 2) {
147
148 #pair is valid if size is nonzero
149 if (hex($mem[$i+1]) != 0) {
150 $val .= "$mem[$i] $mem[$i+1] ";
151 }
152 }
153
154 $val =~ s/\s$//;
155 $val .= ">";
156 $property{reg} = $val;
157
158 return %property;
159}
160
161
162#Returns a list of compatible fields for the BMC itself.
163sub getBMCCompatibles()
164{
165 my @compats;
166
167 #The first one is from the MRW, the next one is more generic
168 #and just <mfgr>-<model>.
169
170 if (!$g_targetObj->isBadAttribute($g_bmc, "BMC_DT_COMPATIBLE", "NA")) {
171 my $attr = $g_targetObj->getAttribute($g_bmc, "BMC_DT_COMPATIBLE");
172 push @compats, $attr;
173 }
174
175 push @compats, lc($g_bmcMfgr).",".lc($g_bmcModel);
176
177 return @compats;
178}
179
180
181#Returns a string for the system's BMC model property
182sub getSystemBMCModel()
183{
184 #<System> BMC
185 my $sys = lc $g_systemName;
186 $sys = uc(substr($sys, 0, 1)) . substr($sys, 1);
187
188 return $sys . " BMC";
189}
190
191
192#Prints a list of nodes at the same indent level
193# $f = file handle
194# $level = indent level (0,1,etc)
195# @nodes = array of node hashes to print, where the
196# key for the hash is the name of the node
197sub printNodes()
198{
199 my ($f, $level, @nodes) = @_;
200
201 foreach my $n (@nodes) {
202 my %node = %$n;
203
204 foreach my $name (sort keys %node) {
205 my %n = %{ $node{$name} };
206 printNode($f, $level, $name, %n);
207 }
208 }
209}
210
211
212#Print a single node and its children
213# $f = file handle
214# $level = indent level (0,1,etc)
215# $name = the name of the node - shows up as:
216# name { ...
217# %vals = The contents of the node, with the following options:
218# if the key is:
219# - 'DTSI_INCLUDE', then value gets turned into a #include
220# - 'COMMENT', then value gets turned into a // comment (coming soon)
221# - 'STANDALONE_PROPERTY' then value gets turned into: value;
222#
223# If the value is:
224# - a hash - then that hash gets turned into a child node
225# where the key is the name of the child node
226# - an array of hashes indicates an array of nodes (coming soon)
227sub printNode()
228{
229 my ($f, $level, $name, %vals) = @_;
230 my $include = "";
231
232 if ($level == 0) {
233 $name = "&".$name;
234 }
235
236 print $f "\n".indent($level) . "$name {\n";
237
238 foreach my $v (sort keys %vals) {
239
240 #A header file include, print it later
241 if ($v eq DTSI_INCLUDE) {
242 $include = $vals{$v};
243 }
244 #A nested node
245 elsif (ref($vals{$v}) eq "HASH") {
246 printNode($f, $level+1, $v, %{$vals{$v}});
247 }
248 elsif ($vals{$v} ne STANDALONE_PROPERTY) {
249 printProperty($f, $level+1, $v, $vals{$v});
250 }
251 else {
252 printStandaloneProperty($f, $level+1, $v);
253 }
254 }
255
256 #Now print the includes, if any.
257 if ($include ne "") {
258 my @incs = split(',', $include);
259 foreach my $i (@incs) {
260 print $f "#include \"$i\";\n";
261 }
262 }
263
264 print $f indent($level) . "};\n";
265}
266
267
268#Prints a comma separated list of properties.
269#e.g. a = "b, c, d";
270# $f = file handle
271# $level = indent level (0,1,etc)
272# $name = name of property
273# @vals = list of property values
274sub printPropertyList()
275{
276 my ($f, $level, $name, @vals) = @_;
277
278 print $f indent($level) . "$name = ";
279
280 for (my $i = 0;$i < scalar @vals; $i++) {
281 print $f "\"$vals[$i]\"";
282 if ($i < (scalar(@vals) - 1)) {
283 print $f ", ";
284 }
285 }
286 print $f ";\n"
287}
288
289
290#Prints a single property. e.g. a = "b";
291# $f = file handle
292# $level = indent level (0,1,etc)
293# $name = name of property
294# @vals = property values
295sub printProperty()
296{
297 my ($f, $level, $name, $val) = @_;
298 print $f indent($level) . "$name = \"" . convertAlias($val) . "\";\n";
299}
300
301
302#Prints a standalone property e.g. some-property;
303# $f = file handle
304# $level = indent level (0,1,etc)
305# $name = name of property
306sub printStandaloneProperty()
307{
308 my ($f, $level, $name) = @_;
309 print $f indent($level) . "$name;\n";
310}
311
312
313#Replace '(alias)' with '&'.
314#Needed because Serverwiz doesn't properly escape '&'s in the XML,
315#so the '(alias)' string is used to represent the alias
316#specifier instead of '&'.
317sub convertAlias() {
318 my $val = shift;
319 $val =~ s/\(alias\)/&/g;
320 return $val
321}
322
323
324#Returns the target for the BMC chip.
325#Not worrying about multiple BMC systems for now.
326sub getBMCTarget()
327{
328 foreach my $target (sort keys %{ $g_targetObj->getAllTargets() })
329 {
330 if ($g_targetObj->getType($target) eq "BMC") {
331 return $target;
332 }
333 }
334 return "";
335}
336
337
338#Prints the device tree version line.
339# $f = file handle
340sub printVersion()
341{
342 my $f = shift;
343 print $f VERSION."\n"
344}
345
346
347#Prints the #include line for pulling in an include file.
348# $f = file handle
349# $level = indent level (0,1,etc)
350sub printIncludes()
351{
352 my ($f, $level) = @_;
353 my @includes = getIncludes($g_bmc);
354
355 foreach my $i (@includes) {
356 #if a .dtsi, gets " ", otherwise < >
357 if ($i =~ /\.dtsi$/) {
358 $i = "\"" . $i . "\"";
359 }
360 else {
361 $i = "<" . $i . ">";
362 }
363 print $f indent($level) . "#include $i;\n";
364 }
365}
366
367
368#Returns an array of includes from the BMC_DT_INCLUDES attribute
369#on the target passed in.
370# $target = the target to get the includes from
371sub getIncludes()
372{
373 my $target = shift;
374 my @includes;
375
376
377 if (!$g_targetObj->isBadAttribute($target, "BMC_DT_INCLUDES")) {
378 my $attr = $g_targetObj->getAttribute($target, "BMC_DT_INCLUDES");
379 my @incs = split(',', $attr);
380
381 foreach my $i (@incs) {
382 if ($i ne "NA") {
383 push @includes, $i
384 }
385 }
386 }
387
388 return @includes;
389}
390
391
392#Prints the root node starting bracket.
393# $f = file handle
394sub printRootNodeStart() {
395 my $f = shift;
396 print $f "\\ \{\n";
397}
398
399
400#Prints the root node ending bracket.
401# $f = file handle
402# $level = indent level (0,1,etc)
403sub printRootNodeEnd() {
404 my ($f, $level) = @_;
405 print $f indent($level)."\};\n";
406}
407
408
409#Returns a string that can be used to indent based on the
410#level passed in. Each level is an additional 4 spaces.
411# $level = indent level (0,1,etc)
412sub indent() {
413 my $level = shift;
414 return ' ' x ($level * 4);
415}
416
417
418sub printUsage
419{
420 print "gen_devtree.pl -x [XML filename] -o [output filename]\n";
421 exit(1);
422}