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 2006 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 \"ao_mca_disp.h\"\n\n";
115}
116
117sub print_footer() {
118	print 'const ao_error_disp_t *ao_error_disp[] = {' . "\n";
119
120	foreach my $name (@funcunits) {
121		print "\t$name,\n";
122	}
123
124	print "};\n";
125}
126
127sub funcunit_begin() {
128	my $arrnm = "ao_error_disp_" . $_[0];
129	print "static const ao_error_disp_t " . $arrnm . "[] = {\n";
130
131	@funcunits = (@funcunits, $arrnm);
132}
133
134sub funcunit_end() {
135	print "\t{ NULL }\n};\n\n";
136}
137
138sub error_begin() {
139	my ($ereport_name) = @_;
140
141	$ereport_name =~ tr/[a-z]./[A-Z]_/;
142	my $flags_name = $ereport_name;
143	$flags_name =~ s/EREPORT_/EREPORT_PAYLOAD_FLAGS_/;
144
145	&errout("\tFM_$ereport_name,\n\tFM_$flags_name,\n");
146}
147
148sub error_end() {
149	&errout("\t},\n\n");
150
151	&print_errout();
152
153	&error_reset();
154}
155
156sub print_bits() {
157	my $name = $_[0];
158	my @bits = @_[1..$#_];
159	my $out = "";
160
161	if (@bits == 0) {
162		$out = "\t0,";
163	} elsif (@bits == 1) {
164		$out = "\t$bits[0],";
165	} else {
166		$out = "\t( " . join(" | ", @bits) . " ),";
167	}
168
169	$out .= " /* $name */" if (defined $name);
170	$out .= "\n";
171
172	return ($out);
173}
174
175sub field_burst() {
176	my ($field, $valuesref, $name, $prefix) = @_;
177
178	if ($field eq "-") {
179		return ();
180	}
181
182	map {
183		if (!defined ${$valuesref}{$_}) {
184			&parsebail("unknown $name value `$_'");
185		}
186		$_ = ${$valuesref}{$_};
187		tr/[a-z]/[A-Z]/;
188		$prefix . "_" . $_;
189	} split(/\//, $field);
190}
191
192sub bin2dec() {
193	my $bin = $_[0];
194	my $dec = 0;
195
196	foreach my $bit (split(//, $bin)) {
197		$dec = $dec * 2 + ($bit eq "1" ? 1 : 0);
198	}
199
200	$dec;
201}
202
203sub state_funcunit() {
204	my $val = $_[0];
205
206	if (defined $::funcunit) {
207		&funcunit_end();
208	}
209
210	$::funcunit = $val;
211	undef $::error;
212	&funcunit_begin($::funcunit);
213}
214
215sub state_desc() {
216	my $desc = $_[0];
217
218	&error_init();
219
220	&errout("\t/* $desc */\n\t{\n");
221}
222
223sub state_error() {
224	$::error = $_[0];
225	&error_begin($::error);
226}
227
228sub state_mask_on() {
229	@::mask_on = map { tr/[a-z]/[A-Z]/; $_; } split(/,\s*/, $_[0]);
230}
231
232sub state_mask_off() {
233	my @mask_off = map { tr/[a-z]/[A-Z]/; $_; } split(/,\s*/, $_[0]);
234
235	&errout(&print_bits("mask", @::mask_on, @mask_off));
236	&errout(&print_bits("mask_res", @::mask_on));
237}
238
239sub state_code() {
240	my ($ext, $type, $pp, $t, $r4, $addr, $ii, $ll, $tt) =
241	    split(/\s+/, $_[0]);
242
243	my %tt_values = ( instr => 1, data => 1, gen => 1, '-' => 1 );
244	my %ll_values = ( l0 => 1, l1 => 1, l2 => 1, lg => 1 );
245
246	my %r4_values = (
247		gen => 'gen',
248		rd => 'rd',
249		wr => 'wr',
250		drd => 'drd',
251		dwr => 'dwr',
252		ird => 'ird',
253		pf => 'prefetch',
254		ev => 'evict',
255		snp => 'snoop',
256	        '-' => '-');
257
258	my %pp_values = (
259		src => 'src',
260		rsp => 'rsp',
261		obs => 'obs',
262		gen => 'gen',
263		'-' => '-' );
264
265	my %t_values = ( 0 => 1, 1 => 1, '-' => 1 );
266
267	my %ii_values = (
268		mem => 'mem',
269		io => 'io',
270		gen => 'gen',
271		'-' => '-' );
272
273	my $instance = &code_lines();
274	if ($instance > 0) {
275		&error_dup($::codeoutlen);	# dup info thus far
276	}
277
278	if (!defined $tt_values{$tt}) {
279		&parsebail("unknown tt value `$tt'");
280	}
281
282	if (!defined $ll_values{$ll}) {
283		&parsebail("unknown ll value `$ll'");
284	}
285
286	my @r4 = &field_burst($r4, \%r4_values, "r4", "AO_MCA_R4_BIT");
287
288	my @pp = ($pp eq '-') ? () :
289	    &field_burst($pp, \%pp_values, "pp", "AO_MCA_PP_BIT");
290
291	if (!defined $t_values{$t}) {
292		&parsebail("unknown t value `$t'");
293	}
294
295	my @ii = ($ii eq '-') ? () :
296	    &field_burst($ii, \%ii_values, "ii", "AO_MCA_II_BIT");
297
298	map {
299		tr/[a-z]/[A-Z]/;
300	} ($ii, $ll, $tt);
301
302	if ($type eq "bus") {
303		if ($pp eq "-" || $t eq "-" || $r4 eq "-" || $ii eq "-" ||
304		    $ll eq "-" ||
305		    $tt ne "-") {
306			&parsebail("invalid members for bus code type");
307		}
308
309		$::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKBUS(" .
310		    "0, " . # pp
311		    "AMD_ERRCODE_T_" . ($t ? "TIMEOUT" : "NONE") . ", " .
312		    "0, " . # r4
313		    "0, " . # ii
314		    "AMD_ERRCODE_LL_$ll),\n");
315
316	} elsif ($type eq "mem") {
317		if ($r4 eq "-" || $tt eq "-" || $ll eq "-" ||
318		    $pp ne "-" || $t ne "-" || $ii ne "-") {
319			&parsebail("invalid members for mem code type");
320		}
321
322		$::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKMEM(" .
323		    "0, " . # r4
324		    "AMD_ERRCODE_TT_$tt, " .
325		    "AMD_ERRCODE_LL_$ll),\n");
326
327	} elsif ($type eq "tlb") {
328		if ($tt eq "-" || $ll eq "-" ||
329		    $r4 ne "-" || $pp ne "-" || $t ne "-" || $ii ne "-") {
330			&parsebail("invalid members for tlb code type");
331		}
332
333		$::codeoutlen += &errout_N($instance, "\tAMD_ERRCODE_MKTLB(" .
334		    "AMD_ERRCODE_TT_$tt, " .
335		    "AMD_ERRCODE_LL_$ll),\n");
336	} else {
337		&parsebail("unknown code type `$type'");
338	}
339
340	$::codeoutlen += &errout_N($instance, "\t" . &bin2dec($ext) .
341	    ", /* ext code $ext */\n");
342
343	$::codeoutlen += &errout_N($instance, &print_bits("pp_bits", @pp));
344	$::codeoutlen += &errout_N($instance, &print_bits("ii_bits", @ii));
345	$::codeoutlen += &errout_N($instance, &print_bits("r4_bits", @r4));
346
347	my $valid_hi;
348	my $valid_lo;
349
350	if ($addr eq "none") {
351		$valid_hi = $valid_lo = 0;
352	} elsif ($addr =~ /<(\d+):(\d+)>/) {
353		$valid_hi = $1;
354		$valid_lo = $2;
355	} else {
356		&parsebail("invalid addr specification");
357	}
358	$::codeoutlen += &errout_N($instance, "\t" . $valid_hi .
359	    ", /* addr valid hi */\n");
360	$::codeoutlen += &errout_N($instance, "\t" . $valid_lo .
361	    ", /* addr valid lo */\n");
362}
363
364sub state_panic() {
365	my @vals = split(/,\s*/, $_[0]);
366
367	if ($#vals < 0) {
368		&errout("\t0, /* panic_when */\n");
369	} else {
370		@vals = map { tr/[a-z]/[A-Z]/; "AO_AED_PANIC_" . $_; } @vals;
371		&errout(&print_bits("panic_when", @vals));
372	}
373}
374
375sub state_flags() {
376	my @flags = split(/,\s*/, $_[0]);
377
378	@flags = map { tr/[a-z]/[A-Z]/; "AO_AED_F_" . $_; } @flags;
379
380	&errout(&print_bits("flags", @flags));
381}
382
383my %stateparse = (
384	funcunit	=> [ \&state_funcunit, 'desc' ],
385	desc		=> [ \&state_desc, 'error' ],
386	error		=> [ \&state_error, 'mask on' ],
387	'mask on'	=> [ \&state_mask_on, 'mask off' ],
388	'mask off'	=> [ \&state_mask_off, 'code' ],
389	code		=> [ \&state_code, 'code|panic' ],
390	panic		=> [ \&state_panic, 'flags' ],
391	flags		=> [ \&state_flags, 'initial' ]
392);
393
394usage unless (@ARGV == 1);
395
396my $infile = $ARGV[0];
397open(INFILE, "<$infile") || &bail("failed to open $infile: $!");
398
399&print_header();
400
401while (<INFILE>) {
402	chop;
403
404	/^#/ && next;
405	/^$/ && next;
406
407	if (!/^\s*(\S[^=]*\S)\s*=\s*(\S.*)?$/) {
408		&parsebail("failed to parse");
409	}
410
411	my ($keyword, $val) = ($1, $2);
412
413	if ($state eq "initial") {
414		if ($keyword eq "funcunit") {
415			$state = "funcunit";
416		} elsif ($keyword eq "desc") {
417			$state = "desc";
418		} else {
419			&parsebail("unexpected keyword $keyword between " .
420			    "errors");
421		}
422
423	} elsif ($state eq "desc") {
424		if ($keyword eq "funcunit") {
425			$state = "funcunit";
426		}
427	}
428
429	if (!($keyword =~ /$state/)) {
430		&parsebail("keyword `$keyword' invalid here; expected " .
431		    "`$state'");
432	}
433	$state = $keyword;	# disambiguate between multiple legal states
434
435	if (!defined $stateparse{$state}) {
436		&parsebail("attempt to transition to invalid state `$state'");
437	}
438
439	my ($handler, $next) = @{$stateparse{$state}};
440
441	&{$handler}($val);
442
443	$state = $next;
444
445	if ($state eq "initial") {
446		&error_end();
447	}
448}
449
450close(INFILE);
451
452if ($state ne "initial" && $state ne "desc") {
453	&bail("input file ended prematurely");
454}
455
456if (defined $::funcunit) {
457	&funcunit_end();
458} else {
459	&bail("no functional units defined");
460}
461
462&print_footer;
463