VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/bootsectors/bs3-cpu-generated-1-data.py@ 65944

Last change on this file since 65944 was 65944, checked in by vboxsync, 8 years ago

bs3-cpu-generated-1-data.py: python 3 fix

  • Property svn:eol-style set to LF
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 17.9 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# $Id: bs3-cpu-generated-1-data.py 65944 2017-03-06 12:02:46Z vboxsync $
4
5"""
6Generates testcases from @optest specifications in IEM.
7"""
8
9from __future__ import print_function;
10
11__copyright__ = \
12"""
13Copyright (C) 2017 Oracle Corporation
14
15This file is part of VirtualBox Open Source Edition (OSE), as
16available from http://www.215389.xyz. This file is free software;
17you can redistribute it and/or modify it under the terms of the GNU
18General Public License (GPL) as published by the Free Software
19Foundation, in version 2 as it comes in the "COPYING" file of the
20VirtualBox OSE distribution. VirtualBox OSE is distributed in the
21hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
22
23The contents of this file may alternatively be used under the terms
24of the Common Development and Distribution License Version 1.0
25(CDDL) only, as it comes in the "COPYING.CDDL" file of the
26VirtualBox OSE distribution, in which case the provisions of the
27CDDL are applicable instead of those of the GPL.
28
29You may elect to license modified versions of this file under the
30terms and conditions of either the GPL or the CDDL or both.
31"""
32__version__ = "$Revision: 65944 $"
33
34# Standard python imports.
35import os;
36import re;
37import sys;
38import time;
39
40# Only the main script needs to modify the path.
41g_ksValidationKitDir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)));
42g_ksVmmAllDir = os.path.join(os.path.dirname(g_ksValidationKitDir), 'VMM', 'VMMAll')
43sys.path.append(g_ksVmmAllDir);
44
45import IEMAllInstructionsPython as iai;
46
47# Python 3 hacks:
48if sys.version_info[0] >= 3:
49 long = int; # pylint: disable=redefined-builtin,invalid-name
50
51
52class Bs3Cg1TestEncoder(object):
53 """
54 Does the encoding of a single test.
55 """
56
57 def __init__(self, fLast):
58 self.fLast = fLast;
59 # Each list member (in all lists) are C expression of a byte.
60 self.asHdr = [];
61 self.asSelectors = [];
62 self.asInputs = [];
63 self.asOutputs = [];
64
65 @staticmethod
66 def _compileSelectors(aoSelectors): # (list(iai.TestSelector)) -> list(str)
67 """
68 Compiles a list of iai.TestSelector predicate checks.
69 Returns C byte expression strings.
70 """
71 asRet = [];
72 for oSelector in aoSelectors:
73 sConstant = oSelector.kdVariables[oSelector.sVariable][oSelector.sValue];
74 sConstant = sConstant.upper().replace('.', '_');
75 if oSelector.sValue.sOp == '==':
76 sByte = '(BS3CG1PRED_%s << BS3CG1SEL_OP_PRED_SHIFT) | BS3CG1SEL_OP_IS_TRUE' % (sConstant,);
77 elif oSelector.sValue.sOp == '!=':
78 sByte = '(BS3CG1PRED_%s << BS3CG1SEL_OP_PRED_SHIFT) | BS3CG1SEL_OP_IS_FALSE' % (sConstant,);
79 else:
80 raise Exception('Unknown selector operator: %s' % (oSelector.sOp,));
81 asRet.append(sByte);
82 return asRet;
83
84 kdSmallFields = {
85 'op1': 'BS3CG1_CTXOP_OP1',
86 'op2': 'BS3CG1_CTXOP_OP2',
87 'efl': 'BS3CG1_CTXOP_EFL',
88 };
89 kdOperators = {
90 '=': 'BS3CG1_CTXOP_ASSIGN',
91 '|=': 'BS3CG1_CTXOP_OR',
92 '&=': 'BS3CG1_CTXOP_AND',
93 '&~=': 'BS3CG1_CTXOP_AND_INV',
94 };
95 kdSmallSizes = {
96 1: 'BS3CG1_CTXOP_1_BYTE',
97 2: 'BS3CG1_CTXOP_2_BYTES',
98 4: 'BS3CG1_CTXOP_4_BYTES',
99 8: 'BS3CG1_CTXOP_8_BYTES',
100 16: 'BS3CG1_CTXOP_16_BYTES',
101 32: 'BS3CG1_CTXOP_32_BYTES',
102 12: 'BS3CG1_CTXOP_12_BYTES',
103 };
104
105 @staticmethod
106 def _compileContextModifers(aoOperations): # (list(iai.TestInOut))
107 """
108 Compile a list of iai.TestInOut context modifiers.
109 """
110 asRet = [];
111 for oOperation in aoOperations:
112 oType = iai.TestInOut.kdTypes[oOperation.sType];
113 aaoValues = oType.get(oOperation.sValue);
114 assert len(aaoValues) == 1 or len(aaoValues) == 2;
115
116 sOp = oOperation.sOp;
117 if sOp == '&|=':
118 sOp = '|=' if len(aaoValues) == 1 else '&~=';
119
120 for fSignExtend, abValue in aaoValues:
121 cbValue = len(abValue);
122
123 # The opcode byte.
124 sOpcode = Bs3Cg1TestEncoder.kdOperators[sOp];
125 sOpcode += ' | ';
126 if oOperation.sField in Bs3Cg1TestEncoder.kdSmallFields:
127 sOpcode += Bs3Cg1TestEncoder.kdSmallFields[oOperation.sField];
128 else:
129 sOpcode += 'BS3CG1_CTXOP_DST_ESC';
130 sOpcode += ' | ';
131 if cbValue in Bs3Cg1TestEncoder.kdSmallSizes:
132 sOpcode += Bs3Cg1TestEncoder.kdSmallSizes[cbValue];
133 else:
134 sOpcode += 'BS3CG1_CTXOP_SIZE_ESC';
135 if fSignExtend:
136 sOpcode += '| BS3CG1_CTXOP_SIGN_EXT';
137 asRet.append(sOpcode);
138
139 # Escaped size byte?
140 if cbValue not in Bs3Cg1TestEncoder.kdSmallSizes:
141 if cbValue >= 256 or cbValue not in [ 1, 2, 4, 6, 8, 12, 16, 32, 64, 128, ]:
142 raise Exception('Invalid value size: %s' % (cbValue,));
143 asRet.append('0x%02x' % (cbValue,));
144
145 # Escaped field identifier.
146 if oOperation.sField not in Bs3Cg1TestEncoder.kdSmallFields:
147 asRet.append('BS3CG1DST_%s' % (oOperation.sField.upper().replace('.', '_'),));
148
149 # The value bytes.
150 for b in abValue:
151 asRet.append('0x%02x' % (b,));
152
153 sOp = '|=';
154
155 return asRet;
156
157 def _constructHeader(self):
158 """
159 Returns C byte expression strings for BS3CG1TESTHDR.
160 """
161 cbSelectors = len(self.asSelectors);
162 if cbSelectors >= 256:
163 raise Exception('Too many selectors: %s bytes, max 255 bytes' % (cbSelectors,))
164
165 cbInputs = len(self.asInputs);
166 if cbInputs >= 4096:
167 raise Exception('Too many input context modifiers: %s bytes, max 4095 bytes' % (cbInputs,))
168
169 cbOutputs = len(self.asOutputs);
170 if cbOutputs >= 2048:
171 raise Exception('Too many output context modifiers: %s bytes, max 2047 bytes' % (cbOutputs,))
172
173 return [
174 '%#04x' % (cbSelectors,), # 8-bit
175 '%#05x & 0xff' % (cbInputs,), # first 8 bits of cbInputs
176 '(%#05x >> 8) | ((%#05x & 0xf) << 4)' % (cbInputs, cbOutputs,), # last 4 bits of cbInputs, lower 4 bits of cbOutputs.
177 '(%#05x >> 4) | (%#05x << 7)' % (cbOutputs, self.fLast), # last 7 bits of cbOutputs and 1 bit fLast.
178 ];
179
180 def encodeTest(self, oTest): # type: (iai.InstructionTest)
181 """
182 Does the encoding.
183 """
184 self.asSelectors = self._compileSelectors(oTest.aoSelectors);
185 self.asInputs = self._compileContextModifers(oTest.aoInputs);
186 self.asOutputs = self._compileContextModifers(oTest.aoOutputs);
187 self.asHdr = self._constructHeader();
188
189
190class Bs3Cg1EncodedTests(object):
191 """
192 Encodes the tests for an instruction.
193 """
194
195 def __init__(self, oInstr):
196 self.offTests = -1;
197 self.cbTests = 0;
198 self.asLines = [];
199
200 # Encode the tests.
201 for iTest, oTest in enumerate(oInstr.aoTests):
202 oEncodedTest = Bs3Cg1TestEncoder(iTest + 1 == len(oInstr.aoTests));
203 oEncodedTest.encodeTest(oTest);
204
205 self.cbTests += len(oEncodedTest.asHdr) + len(oEncodedTest.asSelectors) \
206 + len(oEncodedTest.asInputs) + len(oEncodedTest.asOutputs);
207
208 self.asLines += self.bytesToLines(' /*hdr:*/ ', oEncodedTest.asHdr);
209 if oEncodedTest.asSelectors:
210 self.asLines += self.bytesToLines(' /*sel:*/ ', oEncodedTest.asSelectors);
211 if oEncodedTest.asInputs:
212 self.asLines += self.bytesToLines(' /* in:*/ ', oEncodedTest.asInputs);
213 if oEncodedTest.asOutputs:
214 self.asLines += self.bytesToLines(' /*out:*/ ', oEncodedTest.asOutputs);
215
216 @staticmethod
217 def bytesToLines(sPrefix, asBytes):
218 """
219 Formats a series of bytes into one or more lines.
220 A byte ending with a newline indicates that we should start a new line,
221 and prefix it by len(sPrefix) spaces.
222
223 Returns list of lines.
224 """
225 asRet = [];
226 sLine = sPrefix;
227 for sByte in asBytes:
228 if sByte[-1] == '\n':
229 sLine += sByte[:-1] + ',';
230 asRet.append(sLine);
231 sLine = ' ' * len(sPrefix);
232 else:
233 if len(sLine) + 2 + len(sByte) > 132 and len(sLine) > len(sPrefix):
234 asRet.append(sLine[:-1]);
235 sLine = ' ' * len(sPrefix);
236 sLine += sByte + ', ';
237
238
239 if len(sLine) > len(sPrefix):
240 asRet.append(sLine);
241 return asRet;
242
243
244 def isEqual(self, oOther):
245 """ Compares two encoded tests. """
246 if self.cbTests != oOther.cbTests:
247 return False;
248 if len(self.asLines) != len(oOther.asLines):
249 return False;
250 for iLine, sLines in enumerate(self.asLines):
251 if sLines != oOther.asLines[iLine]:
252 return False;
253 return True;
254
255
256
257class Bs3Cg1Instruction(object):
258 """
259 An instruction with tests.
260 """
261
262 def __init__(self, oMap, oInstr, oTests):
263 self.oMap = oMap; # type: iai.InstructionMap
264 self.oInstr = oInstr; # type: iai.Instruction
265 self.oTests = oTests; # type: Bs3Cg1EncodedTests
266
267 self.asOpcodes = oMap.asLeadOpcodes + [ '0x%02x' % (oInstr.getOpcodeByte(),) ];
268 self.sEncoding = iai.g_kdEncodings[oInstr.sEncoding][0];
269 for oOp in oInstr.aoOperands:
270 self.sEncoding += '_' + oOp.sType;
271 self.asFlags = [];
272 self.fAdvanceMnemonic = True; ##< Set by the caller.
273
274 def getOperands(self):
275 """ Returns comma separated string of operand values for g_abBs3Cg1Operands. """
276 return ', '.join(['(uint8_t)BS3CG1OP_%s' % (oOp.sType,) for oOp in self.oInstr.aoOperands]);
277
278 def getInstructionEntry(self):
279 """ Returns an array of BS3CG1INSTR member initializers. """
280 return [
281 ' /* cbOpcode = */ %s,' % (len(self.asOpcodes),),
282 ' /* cOperands = */ %s,' % (len(self.oInstr.aoOperands),),
283 ' /* cchMnemonic = */ %s,' % (len(self.oInstr.sMnemonic),),
284 ' /* fAdvanceMnemonic = */ %s,' % ('true' if self.fAdvanceMnemonic else 'false',),
285 ' /* offTests = */ %s,' % (self.oTests.offTests,),
286 ' /* enmEncoding = */ (unsigned)%s,' % (self.sEncoding,),
287 ' /* uUnused = */ 0,',
288 ' /* fFlags = */ %s' % (' | '.join(self.asFlags) if self.asFlags else '0'),
289 ];
290
291
292class Bs3CpuGenerated1Generator(object):
293 """
294 The generator code for bs3-cpu-generated-1.
295 """
296
297 def __init__(self):
298 self.aoInstructions = []; # type: Bs3Cg1Instruction
299 self.aoTests = []; # type: Bs3Cg1EncodedTests
300 self.cbTests = 0;
301
302 def addTests(self, oTests):
303 """
304 Adds oTests to self.aoTests, setting the oTests.offTests member.
305 Checks for and eliminates duplicates.
306 Returns the tests to use.
307 """
308 # Check for duplicates.
309 for oExisting in self.aoTests:
310 if oTests.isEqual(oExisting):
311 return oExisting;
312
313 # New test, so add it.
314 oTests.offTests = self.cbTests;
315 self.aoTests.append(oTests);
316 self.cbTests += oTests.cbTests;
317
318 return oTests;
319
320 def processInstruction(self):
321 """
322 Processes the IEM specified instructions.
323 Returns success indicator.
324 """
325
326 #
327 # Group instructions by mnemonic to reduce the number of sub-tests.
328 #
329 for oInstr in sorted(iai.g_aoAllInstructions,
330 key = lambda oInstr: oInstr.sMnemonic + ''.join([oOp.sType for oOp in oInstr.aoOperands])
331 + (oInstr.sOpcode if oInstr.sOpcode else 'zz')):
332 if len(oInstr.aoTests) > 0:
333 oTests = Bs3Cg1EncodedTests(oInstr);
334 oTests = self.addTests(oTests);
335
336 for oMap in oInstr.aoMaps:
337 self.aoInstructions.append(Bs3Cg1Instruction(oMap, oInstr, oTests));
338
339 # Set fAdvanceMnemonic.
340 for iInstr, oInstr in enumerate(self.aoInstructions):
341 oInstr.fAdvanceMnemonic = iInstr + 1 >= len(self.aoInstructions) \
342 or oInstr.oInstr.sMnemonic != self.aoInstructions[iInstr + 1].oInstr.sMnemonic;
343
344 return True;
345
346 def generateCode(self, oOut):
347 """
348 Generates the C code.
349 Returns success indicator.
350 """
351
352 # First, a file header.
353 asLines = [
354 '/*',
355 ' * Autogenerated by $Id: bs3-cpu-generated-1-data.py 65944 2017-03-06 12:02:46Z vboxsync $ ',
356 ' * Do not edit!',
357 ' */',
358 '',
359 '/*',
360 ' * Copyright (C) 2017 Oracle Corporation',
361 ' *',
362 ' * This file is part of VirtualBox Open Source Edition (OSE), as',
363 ' * available from http://www.215389.xyz. This file is free software;',
364 ' * you can redistribute it and/or modify it under the terms of the GNU',
365 ' * General Public License (GPL) as published by the Free Software',
366 ' * Foundation, in version 2 as it comes in the "COPYING" file of the',
367 ' * VirtualBox OSE distribution. VirtualBox OSE is distributed in the',
368 ' * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.',
369 ' * ',
370 ' * The contents of this file may alternatively be used under the terms',
371 ' * of the Common Development and Distribution License Version 1.0',
372 ' * (CDDL) only, as it comes in the "COPYING.CDDL" file of the',
373 ' * VirtualBox OSE distribution, in which case the provisions of the',
374 ' * CDDL are applicable instead of those of the GPL.',
375 ' * ',
376 ' * You may elect to license modified versions of this file under the',
377 ' * terms and conditions of either the GPL or the CDDL or both.',
378 ' */',
379 '',
380 '',
381 '#include "bs3-cpu-generated-1.h"',
382 '',
383 '',
384 ];
385
386 # Generate the g_achBs3Cg1Mnemonics array.
387 asLines += [
388 'const char BS3_FAR_DATA g_achBs3Cg1Mnemonics[] = ',
389 '{',
390 ];
391 for oInstr in self.aoInstructions:
392 asLines.append(' \"%s\"' % (oInstr.oInstr.sMnemonic,));
393 asLines += [
394 '};',
395 '',
396 '',
397 ];
398
399 # Generate the g_abBs3Cg1Opcodes array.
400 asLines += [
401 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Opcodes[] = ',
402 '{',
403 ];
404 for oInstr in self.aoInstructions:
405 asLines.append(' ' + ', '.join(oInstr.asOpcodes) + ',');
406 asLines += [
407 '};',
408 '',
409 '',
410 ];
411
412 # Generate the g_abBs3Cg1Opcodes array.
413 asLines += [
414 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Operands[] = ',
415 '{',
416 ];
417 for oInstr in self.aoInstructions:
418 asLines.append(' ' + oInstr.getOperands() + ',');
419 asLines += [
420 '};',
421 '',
422 '',
423 ];
424
425 # Generate the g_abBs3Cg1Operands array.
426 asLines += [
427 'const BS3CG1INSTR BS3_FAR_DATA g_aBs3Cg1Instructions[] = ',
428 '{',
429 ];
430 for oInstr in self.aoInstructions:
431 asLines.append(' {');
432 asLines += oInstr.getInstructionEntry();
433 asLines.append(' },');
434 asLines += [
435 '};',
436 'const uint16_t BS3_FAR_DATA g_cBs3Cg1Instructions = RT_ELEMENTS(g_aBs3Cg1Instructions);',
437 '',
438 '',
439 ];
440
441 # Generate the g_abBs3Cg1Tests array.
442 asLines += [
443 'const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[] = ',
444 '{',
445 ];
446 for oTests in self.aoTests:
447 asLines.append(' /* offTests=%s */' % (oTests.offTests,));
448 asLines += oTests.asLines;
449 asLines += [
450 '};',
451 '',
452 ];
453
454
455 #/** The test data that BS3CG1INSTR.
456 # * In order to simplify generating these, we use a byte array. */
457 #extern const uint8_t BS3_FAR_DATA g_abBs3Cg1Tests[];
458
459
460 oOut.write('\n'.join(asLines));
461 return True;
462
463
464 def usage(self):
465 """ Prints usage. """
466 print('usage: bs3-cpu-generated-1-data.py [output file|-]');
467 return 0;
468
469 def main(self, asArgs):
470 """
471 C-like main function.
472 Returns exit code.
473 """
474
475 #
476 # Quick argument parsing.
477 #
478 if len(asArgs) == 1:
479 sOutFile = '-';
480 elif len(asArgs) != 2:
481 print('syntax error! Expected exactly one argument.');
482 return 2;
483 elif asArgs[1] in [ '-h', '-?', '--help' ]:
484 return self.usage();
485 else:
486 sOutFile = asArgs[1];
487
488 #
489 # Process the instructions specified in the IEM sources.
490 #
491 if self.processInstruction():
492
493 #
494 # Open the output file and generate the code.
495 #
496 if sOutFile == '-':
497 oOut = sys.stdout;
498 else:
499 try:
500 oOut = open(sOutFile, 'wt');
501 except Exception as oXcpt:
502 print('error! Failed open "%s" for writing: %s' % (sOutFile, oXcpt,));
503 return 1;
504 if self.generateCode(oOut):
505 return 0;
506
507 return 1;
508
509
510if __name__ == '__main__':
511 sys.exit(Bs3CpuGenerated1Generator().main(sys.argv));
512
513
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette