1#!/bin/perl
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22
23#
24# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27# ident	"%Z%%M%	%I%	%E% SMI"
28#
29
30use strict;
31use File::Basename;
32
33my $PROGNAME = basename($0);
34
35my ($funcunit, $error);
36my @funcunits = ();
37my @errorrefs = ();
38
39my $codelinesin = 0;	# number of input 'code' lines for an ereport type
40my $codeoutlen = 0;	# number of output lines from sub state_code
41
42my $state = "initial";
43
44sub usage() {
45	print STDERR "Usage: $PROGNAME inputfile\n";
46	exit(2);
47}
48
49sub bail() {
50	print STDERR "$PROGNAME: ", join(" ", @_), "\n";
51	exit(1);
52}
53
54sub parsebail() {
55	print STDERR "$PROGNAME: $::infile: $.: ", join(" ", @_), "\n";
56	exit(1);
57}
58
59sub error_alloc() {
60	my @a = ();
61
62	push(@::errorrefs, \@a);
63	return (\@a);
64}
65
66sub error_dup() {
67	my ($drop) = @_;
68	my $newref = &error_alloc();
69
70	my $zeroref = $::errorrefs[0];
71
72	my $n = $#$zeroref - $drop;
73
74	@$newref = @$zeroref[0 .. $n];
75}
76
77sub code_lines() {
78	return ($::codelinesin++);
79}
80
81sub error_init() {
82	&error_alloc();
83	$::codelinesin = 0;
84}
85
86sub error_reset() {
87	@::errorrefs = ();
88	$::codelinesin = 0;
89	$::codeoutlen = 0;
90}
91
92sub errout() {
93	my ($line) = @_;
94
95	foreach my $ref (@::errorrefs) {
96		push(@$ref, $line);
97	}
98}
99
100sub errout_N() {
101	my ($instance, $line) = @_;
102	my $ref = @::errorrefs[$instance];
103	push(@$ref, $line);
104	return 1;
105}
106
107sub print_errout() {
108	foreach my $ref (@::errorrefs) {
109		print @$ref;
110	}
111}
112
113sub print_header() {
114	print "#include <sys/mca_x86.h>\n";
115	print "#include \"ao_mca_disp.h\"\n\n";
116}
117
118sub print_footer() {
119	print 'const ao_error_disp_t *ao_error_disp[] = {' . "\n";
120
121	foreach my $name (@funcunits) {
122		print "\t$name,\n";
123	}
124
125	print "};\n";
126}
127
128sub funcunit_begin() {
129	my $arrnm = "ao_error_disp_" . $_[0];
130	print "static const ao_error_disp_t " . $arrnm . "[] = {\n";
131
132	@funcunits = (@funcunits, $arrnm);
133}
134
135sub funcunit_end() {
136	print "\t{ NULL }\n};\n\n";
137}
138
139sub error_begin() {
140	my ($ereport_name) = @_;
141
142	$ereport_name =~ tr/[a-z]./[A-Z]_/;
143	my $flags_name = $ereport_name;
144	$flags_name =~ s/EREPORT_/EREPORT_PAYLOAD_FLAGS_/;
145
146	&errout("\tFM_$ereport_name,\n\tFM_$flags_name,\n");
147}
148
149sub error_end() {
150	&errout("\t},\n\n");
151
152	&print_errout();
153
154	&error_reset();
155}
156
157sub print_bits() {
158	my $name = $_[0];
159	my @bits = @_[1..$#_];
160	my $out = "";
161
162	if (@bits == 0) {
163		$out = "\t0,";
164	} elsif (@bits == 1) {
165		$out = "\t$bits[0],";
166	} else {
167		$out = "\t( " . join(" | ", @bits) . " ),";
168	}
169
170	$out .= " /* $name */" if (defined $name);
171	$out .= "\n";
172
173	return ($out);
174}
175
176sub field_burst() {
177	my ($field, $valuesref, $name, $prefix) = @_;
178
179	if ($field eq "-") {
180		return ();
181	}
182
183	map {
184		if (!defined ${$valuesref}{$_}) {
185			&parsebail("unknown $name value `$_'");
186		}
187		$_ = ${$valuesref}{$_};
188		tr/[a-z]/[A-Z]/;
189		$prefix . "_" . $_;
190	} split(/\//, $field);
191}
192
193sub bin2dec() {
194	my $bin = $_[0];
195	my $dec = 0;
196
197	foreach my $bit (split(//, $bin)) {
198		$dec = $dec * 2 + ($bit eq "1" ? 1 : 0);
199	}
200
201	$dec;
202}
203
204sub state_funcunit() {
205	my $val = $_[0];
206
207	if (defined $::funcunit) {
208		&funcunit_end();
209	}
210
211	$::funcunit = $val;
212	undef $::error;
213	&funcunit_begin($::funcunit);
214}
215
216sub state_desc() {
217	my $desc = $_[0];
218
219	&error_init();
220
221	&errout("\t/* $desc */\n\t{\n");
222}
223
224sub state_error() {
225	$::error = $_[0];
226	&error_begin($::error);
227}
228
229sub state_mask_on() {
230	@::mask_on = map { tr/[a-z]/[A-Z]/; $_; } split(/,\s*/, $_[0]);
231}
232
233sub state_mask_off() {
234	my @mask_off = map { tr/[a-z]/[A-Z]/; $_; } split(/,\s*/, $_[0]);
235
236	&errout(&print_bits("mask", @::mask_on, @mask_off));
237	&errout(&print_bits("mask_res", @::mask_on));
238}
239
240sub state_code() {
241	my ($ext, $type, $pp, $t, $r4, $addr, $ii, $ll, $tt) =
242	    split(/\s+/, $_[0]);
243
244	my %tt_values = ( instr => 1, data => 1, gen => 1, '-' => 1 );
245	my %ll_values = ( l0 => 1, l1 => 1, l2 => 1, lg => 1 );
246
247	my %r4_values = (
248		'err' => 'err',
249		'rd' => 'rd',
250		'wr' => 'wr',
251		'drd' => 'drd',
252		'dwr' => 'dwr',
253		'ird' => 'ird',
254		'pf' => 'prefetch',
255		'ev' => 'evict',
256		'snp' => 'snoop',
257	        '-' => '-');
258
259	my %pp_values = (
260		'src' => 'src',
261		'res' => 'res',
262		'obs' => 'obs',
263		'gen' => 'gen',
264		'-' => '-' );
265
266	my %t_values = ( 0 => 1, 1 => 1, '-' => 1 );
267
268	my %ii_values = (
269		'mem' => 'mem',
270		'io' => 'io',
271		'gen' => 'gen',
272		'-' => '-' );
273
274	my $instance = &code_lines();
275	if ($instance > 0) {
276		&error_dup($::codeoutlen);	# dup info thus far
277	}
278
279	if (!defined $tt_values{$tt}) {
280		&parsebail("unknown tt value `$tt'");
281	}
282
283	if (!defined $ll_values{$ll}) {
284		&parsebail("unknown ll value `$ll'");
285	}
286
287	my @r4 = &field_burst($r4, \%r4_values, "r4", "AO_MCA_R4_BIT");
288
289	my @pp = ($pp eq '-') ? () :
290	    &field_burst($pp, \%pp_values, "pp", "AO_MCA_PP_BIT");
291
292	if (!defined $t_values{$t}) {
293		&parsebail("unknown t value `$t'");
294	}
295
296	my @ii = ($ii eq '-') ? () :
297	    &field_burst($ii, \%ii_values, "ii", "AO_MCA_II_BIT");
298
299	map {
300		tr/[a-z]/[A-Z]/;
301	} ($ii, $ll, $tt);
302
303	if ($type eq "bus") {
304		if ($pp eq "-" || $t eq "-" || $r4 eq "-" || $ii eq "-" ||
305		    $ll eq "-" ||
306		    $tt ne "-") {
307			&parsebail("invalid members for bus code type");
308		}
309
310		$::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKBUS(" .
311		    "0, " . # pp
312		    "MCAX86_ERRCODE_T_" . ($t ? "TIMEOUT" : "NONE") . ", " .
313		    "0, " . # r4
314		    "0, " . # ii
315		    "MCAX86_ERRCODE_LL_$ll),\n");
316
317	} elsif ($type eq "mem") {
318		if ($r4 eq "-" || $tt eq "-" || $ll eq "-" ||
319		    $pp ne "-" || $t ne "-" || $ii ne "-") {
320			&parsebail("invalid members for mem code type");
321		}
322
323		$::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKMEM(" .
324		    "0, " . # r4
325		    "MCAX86_ERRCODE_TT_$tt, " .
326		    "MCAX86_ERRCODE_LL_$ll),\n");
327
328	} elsif ($type eq "tlb") {
329		if ($tt eq "-" || $ll eq "-" ||
330		    $r4 ne "-" || $pp ne "-" || $t ne "-" || $ii ne "-") {
331			&parsebail("invalid members for tlb code type");
332		}
333
334		$::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKTLB(" .
335		    "MCAX86_ERRCODE_TT_$tt, " .
336		    "MCAX86_ERRCODE_LL_$ll),\n");
337	} else {
338		&parsebail("unknown code type `$type'");
339	}
340
341	$::codeoutlen += &errout_N($instance, "\t" . &bin2dec($ext) .
342	    ", /* ext code $ext */\n");
343
344	$::codeoutlen += &errout_N($instance, &print_bits("pp_bits", @pp));
345	$::codeoutlen += &errout_N($instance, &print_bits("ii_bits", @ii));
346	$::codeoutlen += &errout_N($instance, &print_bits("r4_bits", @r4));
347
348	my $valid_hi;
349	my $valid_lo;
350
351	if ($addr eq "none") {
352		$valid_hi = $valid_lo = 0;
353	} elsif ($addr =~ /<(\d+):(\d+)>/) {
354		$valid_hi = $1;
355		$valid_lo = $2;
356	} else {
357		&parsebail("invalid addr specification");
358	}
359	$::codeoutlen += &errout_N($instance, "\t" . $valid_hi .
360	    ", /* addr valid hi */\n");
361	$::codeoutlen += &errout_N($instance, "\t" . $valid_lo .
362	    ", /* addr valid lo */\n");
363}
364
365sub state_panic() {
366	my @vals = split(/,\s*/, $_[0]);
367
368	if ($#vals < 0) {
369		&errout("\t0, /* panic_when */\n");
370	} else {
371		@vals = map { tr/[a-z]/[A-Z]/; "AO_AED_PANIC_" . $_; } @vals;
372		&errout(&print_bits("panic_when", @vals));
373	}
374}
375
376sub state_flags() {
377	my @flags = split(/,\s*/, $_[0]);
378
379	@flags = map { tr/[a-z]/[A-Z]/; "AO_AED_F_" . $_; } @flags;
380
381	&errout(&print_bits("flags", @flags));
382}
383
384sub state_errtype() {
385	my @types = split(/,\s*/, $_[0]);
386
387	@types = map { tr/[a-z]/[A-Z]/; "AO_AED_ET_" . $_; } @types;
388
389	&errout(&print_bits("errtype", @types));
390}
391
392my %stateparse = (
393	funcunit	=> [ \&state_funcunit, 'desc' ],
394	desc		=> [ \&state_desc, 'error' ],
395	error		=> [ \&state_error, 'mask on' ],
396	'mask on'	=> [ \&state_mask_on, 'mask off' ],
397	'mask off'	=> [ \&state_mask_off, 'code' ],
398	code		=> [ \&state_code, 'code|panic' ],
399	panic		=> [ \&state_panic, 'flags' ],
400	flags		=> [ \&state_flags, 'errtype' ],
401	errtype		=> [ \&state_errtype, 'initial' ]
402);
403
404usage unless (@ARGV == 1);
405
406my $infile = $ARGV[0];
407open(INFILE, "<$infile") || &bail("failed to open $infile: $!");
408
409&print_header();
410
411while (<INFILE>) {
412	chop;
413
414	/^#/ && next;
415	/^$/ && next;
416
417	if (!/^\s*(\S[^=]*\S)\s*=\s*(\S.*)?$/) {
418		&parsebail("failed to parse");
419	}
420
421	my ($keyword, $val) = ($1, $2);
422
423	if ($state eq "initial") {
424		if ($keyword eq "funcunit") {
425			$state = "funcunit";
426		} elsif ($keyword eq "desc") {
427			$state = "desc";
428		} else {
429			&parsebail("unexpected keyword $keyword between " .
430			    "errors");
431		}
432
433	} elsif ($state eq "desc") {
434		if ($keyword eq "funcunit") {
435			$state = "funcunit";
436		}
437	}
438
439	if (!($keyword =~ /$state/)) {
440		&parsebail("keyword `$keyword' invalid here; expected " .
441		    "`$state'");
442	}
443	$state = $keyword;	# disambiguate between multiple legal states
444
445	if (!defined $stateparse{$state}) {
446		&parsebail("attempt to transition to invalid state `$state'");
447	}
448
449	my ($handler, $next) = @{$stateparse{$state}};
450
451	&{$handler}($val);
452
453	$state = $next;
454
455	if ($state eq "initial") {
456		&error_end();
457	}
458}
459
460close(INFILE);
461
462if ($state ne "initial" && $state ne "desc") {
463	&bail("input file ended prematurely");
464}
465
466if (defined $::funcunit) {
467	&funcunit_end();
468} else {
469	&bail("no functional units defined");
470}
471
472&print_footer;
473