1#!/usr/bin/awk -f 2 3#- 4# Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org> 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer, 12# without modification. 13# 2. Redistributions in binary form must reproduce at minimum a disclaimer 14# similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 15# redistribution must be conditioned upon including a substantially 16# similar Disclaimer requirement for further binary redistribution. 17# 18# NO WARRANTY 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 22# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 23# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 24# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 27# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 29# THE POSSIBILITY OF SUCH DAMAGES. 30# 31# $FreeBSD$ 32 33BEGIN { main() } 34END { at_exit() } 35 36# 37# Print usage 38# 39function usage() { 40 print "usage: bhnd_nvram_map.awk <input map> [-hd] [-o output file]" 41 _EARLY_EXIT = 1 42 exit 1 43} 44 45function main(_i) { 46 RS="\n" 47 48 OUTPUT_FILE = null 49 50 # Probe awk implementation's hex digit handling 51 if ("0xA" + 0 != 10) { 52 AWK_REQ_HEX_PARSING=1 53 } 54 55 # Output type 56 OUT_T = null 57 OUT_T_HEADER = "HEADER" 58 OUT_T_DATA = "DATA" 59 VERBOSE = 0 60 61 # Tab width to use when calculating output alignment 62 TAB_WIDTH = 8 63 64 # Enable debug output 65 DEBUG = 0 66 67 # Maximum revision 68 REV_MAX = 256 69 70 # Parse arguments 71 if (ARGC < 2) 72 usage() 73 74 for (_i = 1; _i < ARGC; _i++) { 75 if (ARGV[_i] == "--debug") { 76 DEBUG = 1 77 } else if (ARGV[_i] == "-d" && OUT_T == null) { 78 OUT_T = OUT_T_DATA 79 } else if (ARGV[_i] == "-h" && OUT_T == null) { 80 OUT_T = OUT_T_HEADER 81 } else if (ARGV[_i] == "-v") { 82 VERBOSE = 1 83 } else if (ARGV[_i] == "-o") { 84 _i++ 85 if (_i >= ARGC) 86 usage() 87 88 OUTPUT_FILE = ARGV[_i] 89 } else if (ARGV[_i] == "--") { 90 _i++ 91 break 92 } else if (ARGV[_i] !~ /^-/) { 93 FILENAME = ARGV[_i] 94 } else { 95 print "unknown option " ARGV[_i] 96 usage() 97 } 98 } 99 100 ARGC=2 101 102 if (OUT_T == null) { 103 print("error: one of -d or -h required") 104 usage() 105 } 106 107 if (FILENAME == null) { 108 print("error: no input file specified") 109 usage() 110 } 111 112 if (OUTPUT_FILE == "-") { 113 OUTPUT_FILE = "/dev/stdout" 114 } else if (OUTPUT_FILE == null) { 115 OUTPUT_FILE_IDX = split(FILENAME, _g_output_path, "/") 116 OUTPUT_FILE = _g_output_path[OUTPUT_FILE_IDX] 117 118 if (OUTPUT_FILE !~ /^bhnd_/) 119 OUTPUT_FILE = "bhnd_" OUTPUT_FILE 120 121 if (OUT_T == OUT_T_HEADER) 122 OUTPUT_FILE = OUTPUT_FILE ".h" 123 else 124 OUTPUT_FILE = OUTPUT_FILE "_data.h" 125 } 126 127 # Common Regexs 128 UINT_REGEX = "^(0|[1-9][0-9]*)$" 129 HEX_REGEX = "^(0x[A-Fa-f0-9]+)$" 130 OFF_REGEX = "^(0|[1-9][0-9]*)|^(0x[A-Fa-f0-9]+)" 131 REL_OFF_REGEX = "^\\+(0|[1-9][0-9]*)|^\\+(0x[A-Fa-f0-9]+)" 132 133 ARRAY_REGEX = "\\[(0|[1-9][0-9]*)\\]" 134 TYPES_REGEX = "^(((u|i)(8|16|32))|char)("ARRAY_REGEX")?$" 135 136 IDENT_REGEX = "[A-Za-z_][A-Za-z0-9_]*" 137 SVAR_IDENT_REGEX = "^<"IDENT_REGEX">{?$" # <var> identifiers 138 VAR_IDENT_REGEX = "^"IDENT_REGEX"{?$" # var identifiers 139 140 VACCESS_REGEX = "^(private|internal)$" 141 142 # Property array keys 143 PROP_ID = "p_id" 144 PROP_NAME = "p_name" 145 146 # Prop path array keys 147 PPATH_HEAD = "ppath_head" 148 PPATH_TAIL = "ppath_tail" 149 150 # Object array keys 151 OBJ_IS_CLS = "o_is_cls" 152 OBJ_SUPER = "o_super" 153 OBJ_PROP = "o_prop" 154 155 # Class array keys 156 CLS_NAME = "cls_name" 157 CLS_PROP = "cls_prop" 158 159 # C SPROM binding opcodes/opcode flags 160 SPROM_OPCODE_EOF = "SPROM_OPCODE_EOF" 161 SPROM_OPCODE_NELEM = "SPROM_OPCODE_NELEM" 162 SPROM_OPCODE_VAR_END = "SPROM_OPCODE_VAR_END" 163 SPROM_OPCODE_VAR_IMM = "SPROM_OPCODE_VAR_IMM" 164 SPROM_OPCODE_VAR_REL_IMM = "SPROM_OPCODE_VAR_REL_IMM" 165 SPROM_OPCODE_VAR = "SPROM_OPCODE_VAR" 166 SPROM_OPCODE_REV_IMM = "SPROM_OPCODE_REV_IMM" 167 SPROM_OPCODE_REV_RANGE = "SPROM_OPCODE_REV_RANGE" 168 SPROM_OP_REV_START_MASK = "SPROM_OP_REV_START_MASK" 169 SPROM_OP_REV_START_SHIFT = "SPROM_OP_REV_START_SHIFT" 170 SPROM_OP_REV_END_MASK = "SPROM_OP_REV_END_MASK" 171 SPROM_OP_REV_END_SHIFT = "SPROM_OP_REV_END_SHIFT" 172 SPROM_OPCODE_MASK_IMM = "SPROM_OPCODE_MASK_IMM" 173 SPROM_OPCODE_MASK = "SPROM_OPCODE_MASK" 174 SPROM_OPCODE_SHIFT_IMM = "SPROM_OPCODE_SHIFT_IMM" 175 SPROM_OPCODE_SHIFT = "SPROM_OPCODE_SHIFT" 176 SPROM_OPCODE_OFFSET_REL_IMM = "SPROM_OPCODE_OFFSET_REL_IMM" 177 SPROM_OPCODE_OFFSET = "SPROM_OPCODE_OFFSET" 178 SPROM_OPCODE_TYPE = "SPROM_OPCODE_TYPE" 179 SPROM_OPCODE_TYPE_IMM = "SPROM_OPCODE_TYPE_IMM" 180 SPROM_OPCODE_DO_BINDN_IMM = "SPROM_OPCODE_DO_BINDN_IMM" 181 SPROM_OPCODE_DO_BIND = "SPROM_OPCODE_DO_BIND" 182 SPROM_OPCODE_DO_BINDN = "SPROM_OPCODE_DO_BINDN" 183 SPROM_OP_BIND_SKIP_IN_MASK = "SPROM_OP_BIND_SKIP_IN_MASK" 184 SPROM_OP_BIND_SKIP_IN_SHIFT = "SPROM_OP_BIND_SKIP_IN_SHIFT" 185 SPROM_OP_BIND_SKIP_IN_SIGN = "SPROM_OP_BIND_SKIP_IN_SIGN" 186 SPROM_OP_BIND_SKIP_OUT_MASK = "SPROM_OP_BIND_SKIP_OUT_MASK" 187 SPROM_OP_BIND_SKIP_OUT_SHIFT = "SPROM_OP_BIND_SKIP_OUT_SHIFT" 188 189 SPROM_OP_DATA_U8 = "SPROM_OP_DATA_U8" 190 SPROM_OP_DATA_U8_SCALED = "SPROM_OP_DATA_U8_SCALED" 191 SPROM_OP_DATA_U16 = "SPROM_OP_DATA_U16" 192 SPROM_OP_DATA_U32 = "SPROM_OP_DATA_U32" 193 SPROM_OP_DATA_I8 = "SPROM_OP_DATA_I8" 194 195 SPROM_OP_BIND_SKIP_IN_MAX = 3 # maximum SKIP_IN value 196 SPROM_OP_BIND_SKIP_IN_MIN = -3 # minimum SKIP_IN value 197 SPROM_OP_BIND_SKIP_OUT_MAX = 1 # maximum SKIP_OUT value 198 SPROM_OP_BIND_SKIP_OUT_MIN = 0 # minimum SKIP_OUT value 199 SPROM_OP_IMM_MAX = 15 # maximum immediate value 200 SPROM_OP_REV_RANGE_MAX = 15 # maximum SROM rev range value 201 202 # SPROM opcode encoding state 203 SromOpStream = class_new("SromOpStream") 204 class_add_prop(SromOpStream, p_layout, "layout") 205 class_add_prop(SromOpStream, p_revisions, "revisions") 206 class_add_prop(SromOpStream, p_vid, "vid") 207 class_add_prop(SromOpStream, p_offset, "offset") 208 class_add_prop(SromOpStream, p_type, "type") 209 class_add_prop(SromOpStream, p_nelem, "nelem") 210 class_add_prop(SromOpStream, p_mask, "mask") 211 class_add_prop(SromOpStream, p_shift, "shift") 212 class_add_prop(SromOpStream, p_bind_total, "bind_total") 213 class_add_prop(SromOpStream, p_pending_bind, "pending_bind") 214 215 # SROM pending bind operation 216 SromOpBind = class_new("SromOpBind") 217 class_add_prop(SromOpBind, p_segment, "segment") 218 class_add_prop(SromOpBind, p_count, "count") 219 class_add_prop(SromOpBind, p_offset, "offset") 220 class_add_prop(SromOpBind, p_width, "width") 221 class_add_prop(SromOpBind, p_skip_in, "skip_in") 222 class_add_prop(SromOpBind, p_skip_out, "skip_out") 223 class_add_prop(SromOpBind, p_buffer, "buffer") 224 225 # Map class definition 226 Map = class_new("Map") 227 228 # Array class definition 229 Array = class_new("Array") 230 class_add_prop(Array, p_count, "count") 231 232 # MacroType class definition 233 # Used to define a set of known macro types that may be generated 234 MacroType = class_new("MacroType") 235 class_add_prop(MacroType, p_name, "name") 236 class_add_prop(MacroType, p_const_suffix, "const_suffix") 237 238 MTypeVarName = macro_type_new("name", "") # var name 239 MTypeVarID = macro_type_new("id", "_ID") # var unique ID 240 MTypeVarMaxLen = macro_type_new("len", "_MAXLEN") # var max array length 241 242 # Preprocessor Constant 243 MacroDefine = class_new("MacroDefine") 244 class_add_prop(MacroDefine, p_name, "name") 245 class_add_prop(MacroDefine, p_value, "value") 246 247 # ParseState definition 248 ParseState = class_new("ParseState") 249 class_add_prop(ParseState, p_ctx, "ctx") 250 class_add_prop(ParseState, p_is_block, "is_block") 251 class_add_prop(ParseState, p_line, "line") 252 253 # Value Formats 254 Fmt = class_new("Fmt") 255 class_add_prop(Fmt, p_name, "name") 256 class_add_prop(Fmt, p_symbol, "symbol") 257 class_add_prop(Fmt, p_array_fmt, "array_fmt") 258 259 FmtHex = fmt_new("hex", "bhnd_nvram_val_bcm_hex_fmt") 260 FmtDec = fmt_new("decimal", "bhnd_nvram_val_bcm_decimal_fmt") 261 FmtMAC = fmt_new("macaddr", "bhnd_nvram_val_bcm_macaddr_fmt") 262 FmtLEDDC = fmt_new("leddc", "bhnd_nvram_val_bcm_leddc_fmt") 263 FmtCharArray = fmt_new("char_array", "bhnd_nvram_val_char_array_fmt") 264 FmtChar = fmt_new("char", "bhnd_nvram_val_char_array_fmt", 265 FmtCharArray) 266 FmtStr = fmt_new("string", "bhnd_nvram_val_bcm_string_fmt") 267 268 # User-specifiable value formats 269 ValueFormats = map_new() 270 map_set(ValueFormats, get(FmtHex, p_name), FmtHex) 271 map_set(ValueFormats, get(FmtDec, p_name), FmtDec) 272 map_set(ValueFormats, get(FmtMAC, p_name), FmtMAC) 273 map_set(ValueFormats, get(FmtLEDDC, p_name), FmtLEDDC) 274 map_set(ValueFormats, get(FmtStr, p_name), FmtStr) 275 276 # Data Types 277 Type = class_new("Type") 278 class_add_prop(Type, p_name, "name") 279 class_add_prop(Type, p_width, "width") 280 class_add_prop(Type, p_signed, "signed") 281 class_add_prop(Type, p_const, "const") 282 class_add_prop(Type, p_const_val, "const_val") 283 class_add_prop(Type, p_array_const, "array_const") 284 class_add_prop(Type, p_array_const_val, "array_const_val") 285 class_add_prop(Type, p_default_fmt, "default_fmt") 286 class_add_prop(Type, p_mask, "mask") 287 288 ArrayType = class_new("ArrayType", AST) 289 class_add_prop(ArrayType, p_type, "type") 290 class_add_prop(ArrayType, p_count, "count") 291 292 UInt8Max = 255 293 UInt16Max = 65535 294 UInt32Max = 4294967295 295 Int8Min = -128 296 Int8Max = 127 297 Int16Min = -32768 298 Int16Max = 32767 299 Int32Min = -2147483648 300 Int32Max = 2147483648 301 CharMin = Int8Min 302 CharMax = Int8Max 303 304 UInt8 = type_new("u8", 1, 0, "BHND_NVRAM_TYPE_UINT8", 305 "BHND_NVRAM_TYPE_UINT8_ARRAY", FmtHex, UInt8Max, 0, 16) 306 307 UInt16 = type_new("u16", 2, 0, "BHND_NVRAM_TYPE_UINT16", 308 "BHND_NVRAM_TYPE_UINT16_ARRAY", FmtHex, UInt16Max, 1, 17) 309 310 UInt32 = type_new("u32", 4, 0, "BHND_NVRAM_TYPE_UINT32", 311 "BHND_NVRAM_TYPE_UINT32_ARRAY", FmtHex, UInt32Max, 2, 18) 312 313 Int8 = type_new("i8", 1, 1, "BHND_NVRAM_TYPE_INT8", 314 "BHND_NVRAM_TYPE_INT8_ARRAY", FmtDec, UInt8Max, 4, 20) 315 316 Int16 = type_new("i16", 2, 1, "BHND_NVRAM_TYPE_INT16", 317 "BHND_NVRAM_TYPE_INT16_ARRAY", FmtDec, UInt16Max, 5, 21) 318 319 Int32 = type_new("i32", 4, 1, "BHND_NVRAM_TYPE_INT32", 320 "BHND_NVRAM_TYPE_INT32_ARRAY", FmtDec, UInt32Max, 6, 22) 321 322 Char = type_new("char", 1, 1, "BHND_NVRAM_TYPE_CHAR", 323 "BHND_NVRAM_TYPE_CHAR_ARRAY", FmtChar, UInt8Max, 8, 24) 324 325 BaseTypes = map_new() 326 map_set(BaseTypes, get(UInt8, p_name), UInt8) 327 map_set(BaseTypes, get(UInt16, p_name), UInt16) 328 map_set(BaseTypes, get(UInt32, p_name), UInt32) 329 map_set(BaseTypes, get(Int8, p_name), Int8) 330 map_set(BaseTypes, get(Int16, p_name), Int16) 331 map_set(BaseTypes, get(Int32, p_name), Int32) 332 map_set(BaseTypes, get(Char, p_name), Char) 333 334 BaseTypesArray = map_to_array(BaseTypes) 335 BaseTypesCount = array_size(BaseTypesArray) 336 337 # Variable Flags 338 VFlag = class_new("VFlag") 339 class_add_prop(VFlag, p_name, "name") 340 class_add_prop(VFlag, p_const, "const") 341 342 VFlagPrivate = vflag_new("private", "BHND_NVRAM_VF_MFGINT") 343 VFlagIgnoreAll1 = vflag_new("ignall1", "BHND_NVRAM_VF_IGNALL1") 344 345 # Variable Access Type Constants 346 VAccess = class_new("VAccess") 347 VAccessPublic = obj_new(VAccess) # Public 348 VAccessPrivate = obj_new(VAccess) # MFG Private 349 VAccessInternal = obj_new(VAccess) # Implementation-Internal 350 351 # 352 # AST node classes 353 # 354 AST = class_new("AST") 355 class_add_prop(AST, p_line, "line") 356 357 SymbolContext = class_new("SymbolContext", AST) 358 class_add_prop(SymbolContext, p_vars, "vars") 359 360 # NVRAM root parser context 361 NVRAM = class_new("NVRAM", SymbolContext) 362 class_add_prop(NVRAM, p_var_groups, "var_groups") 363 class_add_prop(NVRAM, p_srom_layouts, "srom_layouts") 364 class_add_prop(NVRAM, p_srom_table, "srom_table") 365 366 # Variable Group 367 VarGroup = class_new("VarGroup", SymbolContext) 368 class_add_prop(VarGroup, p_name, "name") 369 370 # Revision Range 371 RevRange = class_new("RevRange", AST) 372 class_add_prop(RevRange, p_start, "start") 373 class_add_prop(RevRange, p_end, "end") 374 375 # String Constant 376 StringConstant = class_new("StringConstant", AST) 377 class_add_prop(StringConstant, p_value, "value") # string 378 class_add_prop(StringConstant, p_continued, "continued") # bool 379 380 # Variable Declaration 381 Var = class_new("Var", AST) 382 class_add_prop(Var, p_access, "access") # VAccess 383 class_add_prop(Var, p_name, "name") # string 384 class_add_prop(Var, p_desc, "desc") # StringConstant 385 class_add_prop(Var, p_help, "help") # StringConstant 386 class_add_prop(Var, p_type, "type") # AbstractType 387 class_add_prop(Var, p_fmt, "fmt") # Fmt 388 class_add_prop(Var, p_ignall1, "ignall1") # bool 389 # ID is assigned once all variables are sorted 390 class_add_prop(Var, p_vid, "vid") # int 391 392 # Common interface inherited by parser contexts that support 393 # registration of SROM variable entries 394 SromContext = class_new("SromContext", AST) 395 class_add_prop(SromContext, p_revisions, "revisions") 396 397 # SROM Layout Node 398 SromLayout = class_new("SromLayout", SromContext) 399 class_add_prop(SromLayout, p_entries, "entries") # Array<SromEntry> 400 class_add_prop(SromLayout, p_revmap, "revmap") # Map<(string,int), SromEntry> 401 class_add_prop(SromLayout, p_output_var_counts, # Map<int, int> (rev->count) 402 "output_var_counts") 403 404 # SROM Layout Filter Node 405 # Represents a filter over a parent SromLayout's revisions 406 SromLayoutFilter = class_new("SromLayoutFilter", SromContext) 407 class_add_prop(SromLayoutFilter, p_parent, "parent") 408 409 # SROM variable entry 410 SromEntry = class_new("SromEntry", AST) 411 class_add_prop(SromEntry, p_var, "var") 412 class_add_prop(SromEntry, p_revisions, "revisions") 413 class_add_prop(SromEntry, p_base_offset, "base_offset") 414 class_add_prop(SromEntry, p_type, "type") 415 class_add_prop(SromEntry, p_offsets, "offsets") 416 417 # SROM variable offset 418 SromOffset = class_new("SromOffset", AST) 419 class_add_prop(SromOffset, p_segments, "segments") 420 421 # SROM variable offset segment 422 SromSegment = class_new("SromSegment", AST) 423 class_add_prop(SromSegment, p_offset, "offset") 424 class_add_prop(SromSegment, p_type, "type") 425 class_add_prop(SromSegment, p_mask, "mask") 426 class_add_prop(SromSegment, p_shift, "shift") 427 class_add_prop(SromSegment, p_value, "value") 428 429 # Create the parse state stack 430 _g_parse_stack_depth = 0 431 _g_parse_stack[0] = null 432 433 # Push the root parse state 434 parser_state_push(nvram_new(), 0) 435} 436 437function at_exit(_block_start, _state, _output_vars, _noutput_vars, _name, _var, 438 _i) 439{ 440 # Skip completion handling if exiting from an error 441 if (_EARLY_EXIT) 442 exit 1 443 444 # Check for complete block closure 445 if (!in_parser_context(NVRAM)) { 446 _state = parser_state_get() 447 _block_start = get(_state, p_line) 448 errorx("missing '}' for block opened on line " _block_start "") 449 } 450 451 # Apply lexicographical sorting to our variable names. To support more 452 # effecient table searching, we guarantee a stable sort order (using C 453 # collation). 454 # 455 # This also has a side-effect of generating a unique monotonic ID 456 # for all variables, which we will emit as a #define and can use as a 457 # direct index into the C variable table 458 _output_vars = array_new() 459 for (_name in _g_var_names) { 460 _var = _g_var_names[_name] 461 462 # Don't include internal variables in the output 463 if (var_is_internal(_var)) 464 continue 465 466 array_append(_output_vars, _var) 467 } 468 469 # Sort by variable name 470 array_sort(_output_vars, prop_to_path(p_name)) 471 472 # Set all variable ID properties to their newly assigned ID value 473 _noutput_vars = array_size(_output_vars) 474 for (_i = 0; _i < _noutput_vars; _i++) { 475 _var = array_get(_output_vars, _i) 476 set(_var, p_vid, _i) 477 } 478 479 # Truncate output file and write common header 480 printf("") > OUTPUT_FILE 481 emit("/*\n") 482 emit(" * THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.\n") 483 emit(" *\n") 484 emit(" * generated from nvram map: " FILENAME "\n") 485 emit(" */\n") 486 emit("\n") 487 488 # Emit all variable definitions 489 if (OUT_T == OUT_T_DATA) { 490 write_data(_output_vars) 491 } else if (OUT_T == OUT_T_HEADER) { 492 write_header(_output_vars) 493 } 494 if (VERBOSE == 1) { 495 printf("%u variable records written to %s\n", array_size(_output_vars), 496 OUTPUT_FILE) >> "/dev/stderr" 497 } 498} 499 500# Write the public header (output type HEADER) 501function write_header(output_vars, _noutput_vars, _var, 502 _tab_align, _macro, _macros, _num_macros, _i) 503{ 504 # Produce our array of #defines 505 _num_macros = 0 506 _noutput_vars = array_size(output_vars) 507 for (_i = 0; _i < _noutput_vars; _i++) { 508 _var = array_get(output_vars, _i) 509 510 # Variable name 511 _macro = var_get_macro(_var, MTypeVarName, \ 512 "\"" get(_var, p_name) "\"") 513 _macros[_num_macros++] = _macro 514 515 # Variable array length 516 if (var_has_array_type(_var)) { 517 _macro = var_get_macro(_var, MTypeVarMaxLen, 518 var_get_array_len(_var)) 519 _macros[_num_macros++] = _macro 520 } 521 } 522 523 # Calculate value tab alignment position for our macros 524 _tab_align = macros_get_tab_alignment(_macros, _num_macros) 525 526 # Write the macros 527 for (_i = 0; _i < _num_macros; _i++) 528 write_macro_define(_macros[_i], _tab_align) 529} 530 531# Write the private data header (output type DATA) 532function write_data(output_vars, _noutput_vars, _var, _nvram, _layouts, 533 _nlayouts, _layout, _revs, _rev, _rev_start, _rev_end, _base_type, 534 _srom_table, _nsrom_table, _i, _j) 535{ 536 _nvram = parser_state_get_context(NVRAM) 537 _layouts = get(_nvram, p_srom_layouts) 538 _nlayouts = array_size(_layouts) 539 540 _noutput_vars = array_size(output_vars) 541 542 # Write all our private NVAR_ID defines 543 write_data_defines(output_vars) 544 545 # Write all layout binding opcodes, and build an array 546 # mapping SROM revision to corresponding SROM layout 547 _srom_table = array_new() 548 for (_i = 0; _i < _nlayouts; _i++) { 549 _layout = array_get(_layouts, _i) 550 551 # Write binding opcode table to our output file 552 write_srom_bindings(_layout) 553 554 # Add entries to _srom_table for all covered revisions 555 _revs = get(_layout, p_revisions) 556 _rev_start = get(_revs, p_start) 557 _rev_end = get(_revs, p_end) 558 559 for (_j = _rev_start; _j <= _rev_end; _j++) 560 array_append(_srom_table, _j) 561 } 562 563 # Sort in ascending order, by SROM revision 564 array_sort(_srom_table) 565 _nsrom_table = array_size(_srom_table) 566 567 # Write the variable definitions 568 emit("/* Variable definitions */\n") 569 emit("const struct bhnd_nvram_vardefn " \ 570 "bhnd_nvram_vardefns[] = {\n") 571 output_depth++ 572 for (_i = 0; _i < _noutput_vars; _i++) { 573 write_data_nvram_vardefn(array_get(output_vars, _i)) 574 } 575 output_depth-- 576 emit("};\n") 577 emit("const size_t bhnd_nvram_num_vardefns = " _noutput_vars ";\n") 578 579 # Write static asserts for raw type constant values that must be kept 580 # synchronized with the code 581 for (_i = 0; _i < BaseTypesCount; _i++) { 582 _base_type = array_get(BaseTypesArray, _i) 583 584 emit(sprintf("_Static_assert(%s == %u, \"%s\");\n", 585 type_get_const(_base_type), type_get_const_val(_base_type), 586 "type constant out of sync")) 587 588 emit(sprintf("_Static_assert(%s == %u, \"%s\");\n", 589 get(_base_type, p_array_const), 590 get(_base_type, p_array_const_val), 591 "array type constant out of sync")) 592 } 593 594 # Write all top-level bhnd_sprom_layout entries 595 emit("/* SPROM layouts */\n") 596 emit("const struct bhnd_sprom_layout bhnd_sprom_layouts[] = {\n") 597 output_depth++ 598 for (_i = 0; _i < _nsrom_table; _i++) { 599 _rev = array_get(_srom_table, _i) 600 _layout = nvram_get_srom_layout(_nvram, _rev) 601 write_data_srom_layout(_layout, _rev) 602 } 603 output_depth-- 604 emit("};\n") 605 emit("const size_t bhnd_sprom_num_layouts = " _nsrom_table ";\n") 606} 607 608# Write a bhnd_nvram_vardef entry for the given variable 609function write_data_nvram_vardefn(v, _desc, _help, _type, _fmt) { 610 obj_assert_class(v, Var) 611 612 _desc = get(v, p_desc) 613 _help = get(v, p_help) 614 _type = get(v, p_type) 615 _fmt = var_get_fmt(v) 616 617 emit("{\n") 618 output_depth++ 619 emit(sprintf(".name = \"%s\",\n", get(v, p_name))) 620 621 if (_desc != null) 622 emit(sprintf(".desc = \"%s\",\n", get(_desc, p_value))) 623 else 624 emit(".desc = NULL,\n") 625 626 if (_help != null) 627 emit(sprintf(".help = \"%s\",\n", get(_help, p_value))) 628 else 629 emit(".help = NULL,\n") 630 631 emit(".type = " type_get_const(_type) ",\n") 632 emit(".nelem = " var_get_array_len(v) ",\n") 633 emit(".fmt = &" get(_fmt, p_symbol) ",\n") 634 emit(".flags = " gen_var_flags(v) ",\n") 635 636 output_depth-- 637 emit("},\n") 638} 639 640# Write a top-level bhnd_sprom_layout entry for the given revision 641# and layout definition 642function write_data_srom_layout(layout, revision, _flags, _size, 643 _sromcrc, _crc_seg, _crc_off, 644 _sromsig, _sig_seg, _sig_offset, _sig_value, 645 _sromrev, _rev_seg, _rev_off, 646 _num_vars) 647{ 648 _flags = array_new() 649 650 # Calculate the size; it always follows the internal CRC variable 651 _sromcrc = srom_layout_find_entry(layout, "<sromcrc>", revision) 652 if (_sromcrc == null) { 653 errorx("missing '<sromcrc>' entry for '"revision"' layout, " \ 654 "cannot compute total size") 655 } else { 656 _crc_seg = srom_entry_get_single_segment(_sromcrc) 657 _crc_off = get(_crc_seg, p_offset) 658 _size = _crc_off 659 _size += get(get(_crc_seg, p_type), p_width) 660 } 661 662 # Fetch signature definition 663 _sromsig = srom_layout_find_entry(layout, "<sromsig>", revision) 664 if (_sromsig == null) { 665 array_append(_flags, "SPROM_LAYOUT_MAGIC_NONE") 666 } else { 667 _sig_seg = srom_entry_get_single_segment(_sromsig) 668 669 _sig_offset = get(_sig_seg, p_offset) 670 _sig_value = get(_sig_seg, p_value) 671 if (_sig_value == "") 672 errorc(get(_sromsig, p_line), "missing signature value") 673 } 674 675 # Fetch sromrev definition 676 _sromrev = srom_layout_find_entry(layout, "sromrev", revision) 677 if (_sromrev == null) { 678 errorx("missing 'sromrev' entry for '"revision"' layout, " \ 679 "cannot determine offset") 680 } else { 681 # Must be a u8 value 682 if (!type_equal(get(_sromrev, p_type), UInt8)) { 683 errorx("'sromrev' entry has non-u8 type '" \ 684 type_to_string(get(_sromrev, p_type))) 685 } 686 687 _rev_seg = srom_entry_get_single_segment(_sromrev) 688 _rev_off = get(_rev_seg, p_offset) 689 } 690 691 # Write layout entry 692 emit("{\n") 693 output_depth++ 694 emit(".size = "_size",\n") 695 emit(".rev = "revision",\n") 696 697 if (array_size(_flags) > 0) { 698 emit(".flags = " array_join(_flags, "|") ",\n") 699 } else { 700 emit(".flags = 0,\n") 701 } 702 703 emit(".srev_offset = " _rev_off ",\n") 704 705 if (_sromsig != null) { 706 emit(".magic_offset = " _sig_offset ",\n") 707 emit(".magic_value = " _sig_value ",\n") 708 } else { 709 emit(".magic_offset = 0,\n") 710 emit(".magic_value = 0,\n") 711 } 712 713 emit(".crc_offset = " _crc_off ",\n") 714 715 emit(".bindings = " srom_layout_get_variable_name(layout) ",\n") 716 emit(".bindings_size = nitems(" \ 717 srom_layout_get_variable_name(layout) "),\n") 718 719 emit(".num_vars = " srom_layout_num_output_vars(layout, revision) ",\n") 720 721 obj_delete(_flags) 722 723 output_depth-- 724 emit("},\n"); 725} 726 727# Create a new opstream encoding state instance for the given layout 728function srom_ops_new(layout, _obj) { 729 obj_assert_class(layout, SromLayout) 730 731 _obj = obj_new(SromOpStream) 732 set(_obj, p_layout, layout) 733 set(_obj, p_revisions, get(layout, p_revisions)) 734 set(_obj, p_vid, 0) 735 set(_obj, p_offset, 0) 736 set(_obj, p_type, null) 737 set(_obj, p_mask, null) 738 set(_obj, p_shift, null) 739 740 return (_obj) 741} 742 743# Return the current type width, or throw an error if no type is currently 744# specified. 745function srom_ops_get_type_width(opstream, _type) 746{ 747 obj_assert_class(opstream, SromOpStream) 748 749 _type = get(opstream, p_type) 750 if (_type == null) 751 errorx("no type value set") 752 753 return (get(type_get_base(_type), p_width)) 754} 755 756# Write a string to the SROM opcode stream, either buffering the write, 757# or emitting it directly. 758function srom_ops_emit(opstream, string, _pending_bind, _buffer) { 759 obj_assert_class(opstream, SromOpStream) 760 761 # Buffered? 762 if ((_pending_bind = get(opstream, p_pending_bind)) != null) { 763 _buffer = get(_pending_bind, p_buffer) 764 array_append(_buffer, string) 765 return 766 } 767 768 # Emit directly 769 emit(string) 770} 771 772# Emit a SROM opcode followed by up to four optional bytes 773function srom_ops_emit_opcode(opstream, opcode, arg0, arg1, arg2, arg3) { 774 obj_assert_class(opstream, SromOpStream) 775 776 srom_ops_emit(opstream, opcode",\n") 777 if (arg0 != "") srom_ops_emit(opstream, arg0",\n") 778 if (arg1 != "") srom_ops_emit(opstream, arg1",\n") 779 if (arg2 != "") srom_ops_emit(opstream, arg2",\n") 780 if (arg3 != "") srom_ops_emit(opstream, arg3",\n") 781} 782 783# Emit a SROM opcode and associated integer value, choosing the best 784# SROM_OP_DATA variant for encoding the value. 785# 786# opc: The standard opcode for non-IMM encoded data, or null if none 787# opc_imm: The IMM opcode, or null if none 788# value: The value to encode 789# svalue: Symbolic representation of value to include in output, or null 790function srom_ops_emit_int_opcode(opstream, opc, opc_imm, value, svalue, 791 _width, _offset, _delta) 792{ 793 obj_assert_class(opstream, SromOpStream) 794 795 # Fetch current type width 796 _width = srom_ops_get_type_width(opstream) 797 798 # Special cases: 799 if (opc_imm == SPROM_OPCODE_SHIFT_IMM) { 800 # SHIFT_IMM -- the imm value must be positive and divisible by 801 # two (shift/2) to use the IMM form. 802 if (value >= 0 && value % 2 == 0) { 803 value = (value/2) 804 opc = null 805 } else { 806 opc_imm = null 807 } 808 } else if (opc_imm == SPROM_OPCODE_OFFSET_REL_IMM) { 809 # OFFSET_REL_IMM -- the imm value must be positive, divisible 810 # by the type width, and relative to the last offset to use 811 # the IMM form. 812 813 # Assert that callers correctly flushed any pending bind before 814 # attempting to set a relative offset 815 if (get(opstream, p_pending_bind) != null) 816 errorx("can't set relative offset with a pending bind") 817 818 # Fetch current offset, calculate relative value and determine 819 # whether we can issue an IMM opcode 820 _offset = get(opstream, p_offset) 821 _delta = value - _offset 822 if (_delta >= 0 && 823 _delta % _width == 0 && 824 (_delta/_width) <= SPROM_OP_IMM_MAX) 825 { 826 srom_ops_emit(opstream, 827 sprintf("/* %#x + %#x -> %#x */\n", _offset, 828 _delta, value)) 829 value = (_delta / _width) 830 opc = null 831 } else { 832 opc_imm = null 833 } 834 } 835 836 # If no symbolic representation provided, write the raw value 837 if (svalue == null) 838 svalue = value 839 840 # Try to encode as IMM value? 841 if (opc_imm != null && value >= 0 && value <= SPROM_OP_IMM_MAX) { 842 srom_ops_emit_opcode(opstream, "("opc_imm"|"svalue")") 843 return 844 } 845 846 # Can't encode as immediate; do we have a non-immediate form? 847 if (opc == null) 848 errorx("can't encode '" value "' as immediate, and no " \ 849 "non-immediate form was provided") 850 851 # Determine and emit minimal encoding 852 # We let the C compiler perform the bit operations, rather than 853 # trying to wrestle awk's floating point arithmetic 854 if (value < 0) { 855 # Only Int8 is used 856 if (value < Int8Min) 857 errorx("cannot int8 encode '" value "'") 858 859 srom_ops_emit_opcode(opstream, 860 "("opc"|"SPROM_OP_DATA_I8")", svalue) 861 862 } else if (value <= UInt8Max) { 863 864 srom_ops_emit_opcode(opstream, 865 "("opc"|"SPROM_OP_DATA_U8")", svalue) 866 867 } else if (value % _width == 0 && (value / _width) <= UInt8Max) { 868 869 srom_ops_emit_opcode(opstream, 870 "("opc"|"SPROM_OP_DATA_U8_SCALED")", svalue / _width) 871 872 } else if (value <= UInt16Max) { 873 874 srom_ops_emit_opcode(opstream, 875 "("opc"|"SPROM_OP_DATA_U16")", 876 "("svalue" & 0xFF)", 877 "("svalue" >> 8)") 878 879 } else if (value <= UInt32Max) { 880 881 srom_ops_emit_opcode(opstream, 882 "("opc"|"SPROM_OP_DATA_U32")", 883 "("svalue" & 0xFF)", 884 "(("svalue" >> 8) & 0xFF)", 885 "(("svalue" >> 16) & 0xFF)", 886 "(("svalue" >> 24) & 0xFF)") 887 888 } else { 889 errorx("can't encode '" value "' (too large)") 890 } 891} 892 893# Emit initial OPCODE_VAR opcode and update opstream state 894function srom_ops_reset_var(opstream, var, _vid_prev, _vid, _vid_name, 895 _type, _base_type) 896{ 897 obj_assert_class(opstream, SromOpStream) 898 obj_assert_class(var, Var) 899 900 # Flush any pending bind for the previous variable 901 srom_ops_flush_bind(opstream, 1) 902 903 # Fetch current state 904 _vid_prev = get(opstream, p_vid) 905 906 _vid = get(var, p_vid) 907 _vid_name = var_get_macro_name(var, MTypeVarID) 908 909 # Update state 910 _type = get(var, p_type) 911 set(opstream, p_vid, _vid) 912 set(opstream, p_type, type_get_base(_type)) 913 set(opstream, p_nelem, var_get_array_len(var)) 914 set(opstream, p_mask, type_get_default_mask(_type)) 915 set(opstream, p_shift, 0) 916 set(opstream, p_bind_total, 0) 917 918 # Always provide a human readable comment 919 srom_ops_emit(opstream, sprintf("/* %s (%#x) */\n", get(var, p_name), 920 get(opstream, p_offset))) 921 922 # Prefer a single VAR_IMM byte 923 if (_vid_prev == 0 || _vid <= SPROM_OP_IMM_MAX) { 924 srom_ops_emit_int_opcode(opstream, 925 null, SPROM_OPCODE_VAR_IMM, 926 _vid, _vid_name) 927 return 928 } 929 930 # Try encoding as a single VAR_REL_IMM byte 931 if (_vid_prev <= _vid && (_vid - _vid_prev) <= SPROM_OP_IMM_MAX) { 932 srom_ops_emit_int_opcode(opstream, 933 null, SPROM_OPCODE_VAR_REL_IMM, 934 _vid - _vid_prev, null) 935 return 936 } 937 938 # Fall back on a multibyte encoding 939 srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_VAR, null, _vid, 940 _vid_name) 941} 942 943# Emit OPCODE_REV/OPCODE_REV_RANGE (if necessary) for a new revision range 944function srom_ops_emit_revisions(opstream, revisions, _prev_revs, 945 _start, _end) 946{ 947 obj_assert_class(opstream, SromOpStream) 948 _prev_revs = get(opstream, p_revisions) 949 950 if (revrange_equal(_prev_revs, revisions)) 951 return; 952 953 # Update stream state 954 set(opstream, p_revisions, revisions) 955 956 _start = get(revisions, p_start) 957 _end = get(revisions, p_end) 958 959 # Sanity-check range values 960 if (_start < 0 || _end < 0) 961 errorx("invalid range: " revrange_to_string(revisions)) 962 963 # If range covers a single revision, and can be encoded within 964 # SROM_OP_IMM_MAX, we can use the single byte encoding 965 if (_start == _end && _start <= SPROM_OP_IMM_MAX) { 966 srom_ops_emit_int_opcode(opstream, 967 null, SPROM_OPCODE_REV_IMM, _start) 968 return 969 } 970 971 # Otherwise, we need to use the two byte range encoding 972 if (_start > SPROM_OP_REV_RANGE_MAX || _end > SPROM_OP_REV_RANGE_MAX) { 973 errorx(sprintf("cannot encode range values %s (>= %u)", 974 revrange_to_string(revisions), SPROM_OP_REV_RANGE_MAX)) 975 } 976 977 srom_ops_emit_opcode(opstream, 978 SPROM_OPCODE_REV_RANGE, 979 sprintf("(%u << %s) | (%u << %s)", 980 _start, SPROM_OP_REV_START_SHIFT, 981 _end, SPROM_OP_REV_END_SHIFT)) 982} 983 984# Emit OPCODE_OFFSET (if necessary) for a new offset 985function srom_ops_emit_offset(opstream, offset, _prev_offset, _rel_offset, 986 _bind) 987{ 988 obj_assert_class(opstream, SromOpStream) 989 990 # Flush any pending bind before adjusting the offset 991 srom_ops_flush_bind(opstream, 0) 992 993 # Fetch current offset 994 _prev_offset = get(opstream, p_offset) 995 if (_prev_offset == offset) 996 return 997 998 # Encode (possibly a relative, 1-byte form) of the offset opcode 999 srom_ops_emit_int_opcode(opstream, SPROM_OPCODE_OFFSET, 1000 SPROM_OPCODE_OFFSET_REL_IMM, offset, null) 1001 1002 # Update state 1003 set(opstream, p_offset, offset) 1004} 1005 1006# Emit OPCODE_TYPE (if necessary) for a new type value; this also 1007# resets the mask to the type default. 1008function srom_ops_emit_type(opstream, type, _base_type, _prev_type, _prev_mask, 1009 _default_mask) 1010{ 1011 obj_assert_class(opstream, SromOpStream) 1012 if (!obj_is_instanceof(type, ArrayType)) 1013 obj_assert_class(type, Type) 1014 1015 _default_mask = type_get_default_mask(type) 1016 _base_type = type_get_base(type) 1017 1018 # If state already matches the requested type, nothing to be 1019 # done 1020 _prev_type = get(opstream, p_type) 1021 _prev_mask = get(opstream, p_mask) 1022 if (type_equal(_prev_type, _base_type) && _prev_mask == _default_mask) 1023 return 1024 1025 # Update state 1026 set(opstream, p_type, _base_type) 1027 set(opstream, p_mask, _default_mask) 1028 1029 # Emit opcode. 1030 if (type_get_const_val(_base_type) <= SPROM_OP_IMM_MAX) { 1031 # Single byte IMM encoding 1032 srom_ops_emit_opcode(opstream, 1033 SPROM_OPCODE_TYPE_IMM "|" type_get_const(_base_type)) 1034 } else { 1035 # Two byte encoding 1036 srom_ops_emit_opcode(opstream, SPROM_OPCODE_TYPE, 1037 type_get_const(_base_type)) 1038 } 1039} 1040 1041# Emit OPCODE_MASK (if necessary) for a new mask value 1042function srom_ops_emit_mask(opstream, mask, _prev_mask) { 1043 obj_assert_class(opstream, SromOpStream) 1044 _prev_mask = get(opstream, p_mask) 1045 1046 if (_prev_mask == mask) 1047 return 1048 1049 set(opstream, p_mask, mask) 1050 srom_ops_emit_int_opcode(opstream, 1051 SPROM_OPCODE_MASK, SPROM_OPCODE_MASK_IMM, 1052 mask, sprintf("0x%x", mask)) 1053} 1054 1055# Emit OPCODE_SHIFT (if necessary) for a new shift value 1056function srom_ops_emit_shift(opstream, shift, _prev_shift) { 1057 obj_assert_class(opstream, SromOpStream) 1058 _prev_shift = get(opstream, p_shift) 1059 1060 if (_prev_shift == shift) 1061 return 1062 1063 set(opstream, p_shift, shift) 1064 srom_ops_emit_int_opcode(opstream, 1065 SPROM_OPCODE_SHIFT, SPROM_OPCODE_SHIFT_IMM, 1066 shift, null) 1067} 1068 1069# Return true if a valid BIND/BINDN encoding exists for the given SKIP_IN 1070# value, false if the skip values exceed the limits of the bind opcode 1071# family. 1072function srom_ops_can_encode_skip_in(skip_in) { 1073 return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN && 1074 skip_in <= SPROM_OP_BIND_SKIP_IN_MAX) 1075} 1076 1077# Return true if a valid BIND/BINDN encoding exists for the given SKIP_OUT 1078# value, false if the skip values exceed the limits of the bind opcode 1079# family. 1080function srom_ops_can_encode_skip_out(skip_out) { 1081 return (skip_in >= SPROM_OP_BIND_SKIP_IN_MIN && 1082 skip_in <= SPROM_OP_BIND_SKIP_IN_MAX) 1083} 1084 1085# Return true if a valid BIND/BINDN encoding exists for the given skip 1086# values, false if the skip values exceed the limits of the bind opcode 1087# family. 1088function srom_ops_can_encode_skip(skip_in, skip_out) { 1089 return (srom_ops_can_encode_skip_in(skip_in) && 1090 srom_ops_can_encode_skip_out(skip_out)) 1091} 1092 1093# Create a new SromOpBind instance for the given segment 1094function srom_opbind_new(segment, skip_in, skip_out, _obj, _type, _width, 1095 _offset) 1096{ 1097 obj_assert_class(segment, SromSegment) 1098 1099 # Verify that an encoding exists for the skip values 1100 if (!srom_ops_can_encode_skip_in(skip_in)) { 1101 errorx(sprintf("cannot encode SKIP_IN=%d; maximum supported " \ 1102 "range %d-%d", skip_in, 1103 SPROM_OP_BIND_SKIP_IN_MIN, SPROM_OP_BIND_SKIP_IN_MAX)) 1104 } 1105 1106 if (!srom_ops_can_encode_skip_out(skip_out)) { 1107 errorx(sprintf("cannot encode SKIP_OUT=%d; maximum supported " \ 1108 "range %d-%d", skip_out, 1109 SPROM_OP_BIND_SKIP_OUT_MIN, SPROM_OP_BIND_SKIP_OUT_MAX)) 1110 } 1111 1112 # Fetch basic segment info 1113 _offset = get(segment, p_offset) 1114 _type = srom_segment_get_base_type(segment) 1115 _width = get(_type, p_width) 1116 1117 # Construct new instance 1118 _obj = obj_new(SromOpBind) 1119 1120 set(_obj, p_segment, segment) 1121 set(_obj, p_count, 1) 1122 set(_obj, p_offset, _offset) 1123 set(_obj, p_width, _width) 1124 set(_obj, p_skip_in, skip_in) 1125 set(_obj, p_skip_out, skip_out) 1126 set(_obj, p_buffer, array_new()) 1127 1128 return (_obj) 1129} 1130 1131# Try to coalesce a BIND for the given segment with an existing bind request, 1132# returning true on success, or false if the two segments cannot be coalesced 1133# into the existing request 1134function srom_opbind_append(bind, segment, skip_out, _bind_seg, _bind_off, 1135 _width, _count, _skip_in, _seg_offset, _delta) 1136{ 1137 obj_assert_class(bind, SromOpBind) 1138 obj_assert_class(segment, SromSegment) 1139 1140 # Are the segments compatible? 1141 _bind_seg = get(bind, p_segment) 1142 if (!srom_segment_attributes_equal(_bind_seg, segment)) 1143 return (0) 1144 1145 # Are the output skip values compatible? 1146 if (get(bind, p_skip_out) != skip_out) 1147 return (0) 1148 1149 # Find bind offset/count/width/skip 1150 _bind_off = get(bind, p_offset) 1151 _count = get(bind, p_count) 1152 _skip_in = get(bind, p_skip_in) 1153 _width = get(bind, p_width) 1154 1155 # Fetch new segment's offset 1156 _seg_offset = get(segment, p_offset) 1157 1158 # If there's only one segment in the bind op, we ned to compute the 1159 # skip value to be used for all later segments (including the 1160 # segment we're attempting to append) 1161 # 1162 # If there's already multiple segments, we just need to verify that 1163 # the bind_offset + (count * width * skip_in) produces the new 1164 # segment's offset 1165 if (_count == 1) { 1166 # Determine the delta between the two segment offsets. This 1167 # must be a multiple of the type width to be encoded 1168 # as a BINDN entry 1169 _delta = _seg_offset - _bind_off 1170 if ((_delta % _width) != 0) 1171 return (0) 1172 1173 # The skip byte count is calculated as (type width * skip) 1174 _skip_in = _delta / _width 1175 1176 # Is the skip encodable? 1177 if (!srom_ops_can_encode_skip_in(_skip_in)) 1178 return (0) 1179 1180 # Save required skip 1181 set(bind, p_skip_in, _skip_in) 1182 } else if (_count > 1) { 1183 # Get the final offset of the binding if we were to add 1184 # one additional segment 1185 _bind_off = _bind_off + (_width * _skip_in * (_count + 1)) 1186 1187 # If it doesn't match our segment's offset, we can't 1188 # append this segment 1189 if (_bind_off != _seg_offset) 1190 return (0) 1191 } 1192 1193 # Success! Increment the bind count in the existing bind 1194 set(bind, p_count, _count + 1) 1195 return (1) 1196} 1197 1198# Return true if the given binding operation can be omitted from the output 1199# if it would be immediately followed by a VAR, VAR_REL_IMM, or EOF opcode. 1200# 1201# The bind operatin must be configured with default count, skip_in, and 1202# skip_out values of 1, and must contain no buffered post-BIND opcodes 1203function srom_opbind_is_implicit_encodable(bind) { 1204 obj_assert_class(bind, SromOpBind) 1205 1206 if (get(bind, p_count) != 1) 1207 return (0) 1208 1209 if (get(bind, p_skip_in) != 1) 1210 return (0) 1211 1212 if (get(bind, p_skip_out) != 1) 1213 return (0) 1214 1215 if (array_size(get(bind, p_buffer)) != 0) 1216 return (0) 1217 1218 return (1) 1219} 1220 1221 1222# Encode all segment settings for a single offset segment, followed by a bind 1223# request. 1224# 1225# opstream: Opcode stream 1226# segment: Segment to be written 1227# continued: If this segment's value should be OR'd with the value of a 1228# following segment 1229function srom_ops_emit_segment(opstream, segment, continued, _value, 1230 _bind, _skip_in, _skip_out) 1231{ 1232 obj_assert_class(opstream, SromOpStream) 1233 obj_assert_class(segment, SromSegment) 1234 1235 # Determine basic bind parameters 1236 _count = 1 1237 _skip_in = 1 1238 _skip_out = continued ? 0 : 1 1239 1240 # Try to coalesce with a pending binding 1241 if ((_bind = get(opstream, p_pending_bind)) != null) { 1242 if (srom_opbind_append(_bind, segment, _skip_out)) 1243 return 1244 } 1245 1246 # Otherwise, flush any pending bind and enqueue our own 1247 srom_ops_flush_bind(opstream, 0) 1248 if (get(opstream, p_pending_bind)) 1249 errorx("bind not flushed!") 1250 1251 # Encode type 1252 _value = get(segment, p_type) 1253 srom_ops_emit_type(opstream, _value) 1254 1255 # Encode offset 1256 _value = get(segment, p_offset) 1257 srom_ops_emit_offset(opstream, _value) 1258 1259 # Encode mask 1260 _value = get(segment, p_mask) 1261 srom_ops_emit_mask(opstream, _value) 1262 1263 # Encode shift 1264 _value = get(segment, p_shift) 1265 srom_ops_emit_shift(opstream, _value) 1266 1267 # Enqueue binding with opstream 1268 _bind = srom_opbind_new(segment, _skip_in, _skip_out) 1269 set(opstream, p_pending_bind, _bind) 1270} 1271 1272# (private) Adjust the stream's input offset by applying the given bind 1273# operation's skip_in * width * count. 1274function _srom_ops_apply_bind_offset(opstream, bind, _count, _offset, _width, 1275 _skip_in, _opstream_offset) 1276{ 1277 obj_assert_class(opstream, SromOpStream) 1278 obj_assert_class(bind, SromOpBind) 1279 1280 _opstream_offset = get(opstream, p_offset) 1281 _offset = get(bind, p_offset) 1282 if (_opstream_offset != _offset) 1283 errorx("stream/bind offset state mismatch") 1284 1285 _count = get(bind, p_count) 1286 _width = get(bind, p_width) 1287 _skip_in = get(bind, p_skip_in) 1288 1289 set(opstream, p_offset, 1290 _opstream_offset + ((_width * _skip_in) * _count)) 1291} 1292 1293# (private) Write a bind instance and all buffered opcodes 1294function _srom_ops_emit_bind(opstream, bind, _count, _skip_in, _skip_out, 1295 _off_start, _width, _si_signbit, _written, _nbuffer, _buffer) 1296{ 1297 obj_assert_class(opstream, SromOpStream) 1298 obj_assert_class(bind, SromOpBind) 1299 1300 # Assert that any pending bind state has already been cleared 1301 if (get(opstream, p_pending_bind) != null) 1302 errorx("cannot flush bind with an existing pending_bind active") 1303 1304 # Fetch (and assert valid) our skip values 1305 _skip_in = get(bind, p_skip_in) 1306 _skip_out = get(bind, p_skip_out) 1307 1308 if (!srom_ops_can_encode_skip(_skip_in, _skip_out)) 1309 errorx("invalid skip values in buffered bind") 1310 1311 # Determine SKIP_IN sign bit 1312 _si_signbit = "0" 1313 if (_skip_in < 0) 1314 _si_signbit = SPROM_OP_BIND_SKIP_IN_SIGN 1315 1316 # Emit BIND/BINDN opcodes until the full count is encoded 1317 _count = get(bind, p_count) 1318 while (_count > 0) { 1319 if (_count > 1 && _count <= SPROM_OP_IMM_MAX && 1320 _skip_in == 1 && _skip_out == 1) 1321 { 1322 # The one-byte BINDN form requires encoding the count 1323 # as a IMM, and has an implicit in/out skip of 1. 1324 srom_ops_emit_opcode(opstream, 1325 "("SPROM_OPCODE_DO_BINDN_IMM"|"_count")") 1326 _count -= _count 1327 1328 } else if (_count > 1) { 1329 # The two byte BINDN form can encode skip values and a 1330 # larger U8 count 1331 _written = min(_count, UInt8Max) 1332 1333 srom_ops_emit_opcode(opstream, 1334 sprintf("(%s|%s|(%u<<%s)|(%u<<%s))", 1335 SPROM_OPCODE_DO_BINDN, 1336 _si_signbit, 1337 abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT, 1338 _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT), 1339 _written) 1340 _count -= _written 1341 1342 } else { 1343 # The 1-byte BIND form can encode the same SKIP values 1344 # as the 2-byte BINDN, with a implicit count of 1 1345 srom_ops_emit_opcode(opstream, 1346 sprintf("(%s|%s|(%u<<%s)|(%u<<%s))", 1347 SPROM_OPCODE_DO_BIND, 1348 _si_signbit, 1349 abs(_skip_in), SPROM_OP_BIND_SKIP_IN_SHIFT, 1350 _skip_out, SPROM_OP_BIND_SKIP_OUT_SHIFT)) 1351 _count-- 1352 } 1353 } 1354 1355 # Update the stream's input offset 1356 _srom_ops_apply_bind_offset(opstream, bind) 1357 1358 # Write any buffered post-BIND opcodes 1359 _buffer = get(bind, p_buffer) 1360 _nbuffer = array_size(_buffer) 1361 for (_i = 0; _i < _nbuffer; _i++) 1362 srom_ops_emit(opstream, array_get(_buffer, _i)) 1363} 1364 1365# Flush any buffered binding 1366function srom_ops_flush_bind(opstream, allow_implicit, _bind, _bind_total) 1367{ 1368 obj_assert_class(opstream, SromOpStream) 1369 1370 # If no pending bind, nothing to flush 1371 if ((_bind = get(opstream, p_pending_bind)) == null) 1372 return 1373 1374 # Check the per-variable bind count to determine whether 1375 # we can encode an implicit bind. 1376 # 1377 # If there have been any explicit bind statements, implicit binding 1378 # cannot be used. 1379 _bind_total = get(opstream, p_bind_total) 1380 if (allow_implicit && _bind_total > 0) { 1381 # Disable implicit encoding; explicit bind statements have 1382 # been issued for this variable previously. 1383 allow_implicit = 0 1384 } 1385 1386 # Increment bind count 1387 set(opstream, p_bind_total, _bind_total + 1) 1388 1389 # Clear the property value 1390 set(opstream, p_pending_bind, null) 1391 1392 # If a pending bind operation can be encoded as an implicit bind, 1393 # emit a descriptive comment and update the stream state. 1394 # 1395 # Otherwise, emit the full set of bind opcode(s) 1396 _base_off = get(opstream, p_offset) 1397 if (allow_implicit && srom_opbind_is_implicit_encodable(_bind)) { 1398 # Update stream's input offset 1399 _srom_ops_apply_bind_offset(opstream, _bind) 1400 } else { 1401 _srom_ops_emit_bind(opstream, _bind) 1402 } 1403 1404 # Provide bind information as a comment 1405 srom_ops_emit(opstream, 1406 sprintf("/* bind (%s @ %#x -> %#x) */\n", 1407 type_to_string(get(opstream, p_type)), 1408 _base_off, get(opstream, p_offset))) 1409 1410 # Clean up 1411 obj_delete(_bind) 1412} 1413 1414# Write OPCODE_EOF after flushing any buffered writes 1415function srom_ops_emit_eof(opstream) { 1416 obj_assert_class(opstream, SromOpStream) 1417 1418 # Flush any buffered writes 1419 srom_ops_flush_bind(opstream, 1) 1420 1421 # Emit an explicit VAR_END opcode for the last entry 1422 srom_ops_emit_opcode(opstream, SPROM_OPCODE_VAR_END) 1423 1424 # Emit EOF 1425 srom_ops_emit_opcode(opstream, SPROM_OPCODE_EOF) 1426} 1427 1428# Write the SROM offset segment bindings to the opstream 1429function write_srom_offset_bindings(opstream, offsets, 1430 _noffsets, _offset, _segs, _nsegs, _segment, _cont, 1431 _i, _j) 1432{ 1433 _noffsets = array_size(offsets) 1434 for (_i = 0; _i < _noffsets; _i++) { 1435 # Encode each segment in this offset 1436 _offset = array_get(offsets, _i) 1437 _segs = get(_offset, p_segments) 1438 _nsegs = array_size(_segs) 1439 1440 for (_j = 0; _j < _nsegs; _j++) { 1441 _segment = array_get(_segs, _j) 1442 _cont = 0 1443 1444 # Should this value be OR'd with the next segment? 1445 if (_j+1 < _nsegs) 1446 _cont = 1 1447 1448 # Encode segment 1449 srom_ops_emit_segment(opstream, _segment, _cont) 1450 } 1451 } 1452} 1453 1454# Write the SROM entry stream for a SROM entry to the output file 1455function write_srom_entry_bindings(entry, opstream, _var, _vid, 1456 _var_type, _entry_type, _offsets, _noffsets) 1457{ 1458 _var = get(entry, p_var) 1459 _vid = get(_var, p_vid) 1460 1461 # Encode revision switch. This resets variable state, so must 1462 # occur before any variable definitions to which it applies 1463 srom_ops_emit_revisions(opstream, get(entry, p_revisions)) 1464 1465 # Encode variable ID 1466 srom_ops_reset_var(opstream, _var, _vid) 1467 output_depth++ 1468 1469 # Write entry-specific array length (SROM layouts may define array 1470 # mappings with fewer elements than in the variable definition) 1471 if (srom_entry_has_array_type(entry)) { 1472 _var_type = get(_var, p_type) 1473 _entry_type = get(entry, p_type) 1474 1475 # If the array length differs from the variable default, 1476 # write an OPCODE_EXT_NELEM entry 1477 if (type_get_nelem(_var_type) != type_get_nelem(_entry_type)) { 1478 srom_ops_emit_opcode(opstream, SPROM_OPCODE_NELEM, 1479 srom_entry_get_array_len(entry)) 1480 } 1481 } 1482 1483 # Write offset segment bindings 1484 _offsets = get(entry, p_offsets) 1485 write_srom_offset_bindings(opstream, _offsets) 1486 output_depth-- 1487} 1488 1489# Write a SROM layout binding opcode table to the output file 1490function write_srom_bindings(layout, _varname, _var, _all_entries, 1491 _nall_entries, _entries, _nentries, _entry, _opstream, _i) 1492{ 1493 _varname = srom_layout_get_variable_name(layout) 1494 _all_entries = get(layout, p_entries) 1495 _opstream = srom_ops_new(layout) 1496 1497 # 1498 # Collect all entries to be included in the output, and then 1499 # sort by their variable's assigned ID (ascending). 1500 # 1501 # The variable IDs were previously assigned in lexigraphical sort 1502 # order; since the variable *offsets* tend to match this order, this 1503 # works out well for our compact encoding, allowing us to make use of 1504 # compact relative encoding of both variable IDs and variable offsets. 1505 # 1506 _entries = array_new() 1507 _nall_entries = array_size(_all_entries) 1508 for (_i = 0; _i < _nall_entries; _i++) { 1509 _entry = array_get(_all_entries, _i) 1510 _var = get(_entry, p_var) 1511 1512 # Skip internal variables 1513 if (var_is_internal(_var)) 1514 continue 1515 1516 # Sanity check variable ID assignment 1517 if (get(_var, p_vid) == "") 1518 errorx("missing variable ID for " obj_to_string(_var)) 1519 1520 array_append(_entries, _entry) 1521 } 1522 1523 # Sort entries by (variable ID, revision range), ascending 1524 array_sort(_entries, prop_path_create(p_var, p_vid), 1525 prop_path_create(p_revisions, p_start), 1526 prop_path_create(p_revisions, p_end)) 1527 1528 # Emit all entry binding opcodes 1529 emit("static const uint8_t " _varname "[] = {\n") 1530 output_depth++ 1531 1532 _nentries = array_size(_entries) 1533 for (_i = 0; _i < _nentries; _i++) { 1534 _entry = array_get(_entries, _i) 1535 write_srom_entry_bindings(_entry, _opstream) 1536 } 1537 1538 # Flush and write EOF 1539 srom_ops_emit_eof(_opstream) 1540 1541 output_depth-- 1542 emit("};\n") 1543 1544 obj_delete(_opstream) 1545 obj_delete(_entries) 1546} 1547 1548# Write the BHND_NVAR_<NAME>_ID #defines to the output file 1549function write_data_defines(output_vars, _noutput_vars, _tab_align, _var, 1550 _macro, _macros, _num_macros, _i) 1551{ 1552 # Produce our array of #defines 1553 _num_macros = 0 1554 _noutput_vars = array_size(output_vars) 1555 for (_i = 0; _i < _noutput_vars; _i++) { 1556 _var = array_get(output_vars, _i) 1557 1558 # Variable ID 1559 _macro = var_get_macro(_var, MTypeVarID, get(_var, p_vid)) 1560 _macros[_num_macros++] = _macro 1561 } 1562 1563 # Calculate value tab alignment position for our macros 1564 _tab_align = macros_get_tab_alignment(_macros, _num_macros) 1565 1566 # Write the #defines 1567 emit("/* ID constants provide an index into the variable array */\n") 1568 for (_i = 0; _i < _num_macros; _i++) 1569 write_macro_define(_macros[_i], _tab_align) 1570 emit("\n\n"); 1571} 1572 1573# Calculate the common tab alignment to be used with a set of prefix strings 1574# with the given maximum length 1575function tab_alignment(max_len, _tab_align) { 1576 _tab_align = max_len 1577 _tab_align += (TAB_WIDTH - (_tab_align % TAB_WIDTH)) % TAB_WIDTH 1578 _tab_align /= TAB_WIDTH 1579 1580 return (_tab_align) 1581} 1582 1583# Generate and return a tab string that can be appended to a string of 1584# `strlen` to pad the column out to `align_to` 1585# 1586# Note: If the string from which strlen was derived contains tabs, the result 1587# is undefined 1588function tab_str(strlen, align_to, _lead, _pad, _result, _i) { 1589 _lead = strlen 1590 _lead -= (_lead % TAB_WIDTH); 1591 _lead /= TAB_WIDTH; 1592 1593 # Determine required padding to reach the desired alignment 1594 if (align_to >= _lead) 1595 _pad = align_to - _lead; 1596 else 1597 _pad = 1; 1598 1599 for (_i = 0; _i < _pad; _i++) 1600 _result = _result "\t" 1601 1602 return (_result) 1603} 1604 1605 1606# Write a MacroDefine constant, padding the constant out to `align_to` 1607function write_macro_define(macro, align_to, _tabstr, _i) { 1608 # Determine required padding to reach the desired alignment 1609 _tabstr = tab_str(length(get(macro, p_name)), align_to) 1610 1611 emit("#define\t" get(macro, p_name) _tabstr get(macro, p_value) "\n") 1612} 1613 1614# Calculate the tab alignment to be used with a given integer-indexed array 1615# of Macro instances. 1616function macros_get_tab_alignment(macros, macros_len, _macro, _max_len, _i) { 1617 _max_len = 0 1618 for (_i = 0; _i < macros_len; _i++) { 1619 _macro = macros[_i] 1620 _max_len = max(_max_len, length(get(_macro, p_name))) 1621 } 1622 1623 return (tab_alignment(_max_len)) 1624} 1625 1626# Variable group block 1627$1 == "group" && in_parser_context(NVRAM) { 1628 parse_variable_group() 1629} 1630 1631# Variable definition 1632(($1 ~ VACCESS_REGEX && $2 ~ TYPES_REGEX) || $1 ~ TYPES_REGEX) && 1633 in_parser_context(SymbolContext) \ 1634{ 1635 parse_variable_defn() 1636} 1637 1638# Variable "fmt" parameter 1639$1 == "fmt" && in_parser_context(Var) { 1640 parse_variable_param($1) 1641 next 1642} 1643 1644# Variable "all1" parameter 1645$1 == "all1" && in_parser_context(Var) { 1646 parse_variable_param($1) 1647 next 1648} 1649 1650# Variable desc/help parameters 1651($1 == "desc" || $1 == "help") && in_parser_context(Var) { 1652 parse_variable_param($1) 1653 next 1654} 1655 1656# SROM layout block 1657$1 == "srom" && in_parser_context(NVRAM) { 1658 parse_srom_layout() 1659} 1660 1661 1662# SROM layout revision filter block 1663$1 == "srom" && in_parser_context(SromLayout) { 1664 parse_srom_layout_filter() 1665} 1666 1667# SROM layout variable entry 1668$1 ~ "("OFF_REGEX"):$" && \ 1669 (in_parser_context(SromLayout) || in_parser_context(SromLayoutFilter)) \ 1670{ 1671 parse_srom_variable_entry() 1672} 1673 1674 1675# SROM entry segment 1676$1 ~ "("REL_OFF_REGEX"|"OFF_REGEX")[:,|]?" && in_parser_context(SromEntry) { 1677 parse_srom_entry_segments() 1678} 1679 1680# Skip comments and blank lines 1681/^[ \t]*#/ || /^$/ { 1682 next 1683} 1684 1685# Close blocks 1686/}/ && !in_parser_context(NVRAM) { 1687 while (!in_parser_context(NVRAM) && $0 ~ "}") { 1688 parser_state_close_block(); 1689 } 1690 next 1691} 1692 1693# Report unbalanced '}' 1694/}/ && in_parser_context(NVRAM) { 1695 error("extra '}'") 1696} 1697 1698# Invalid variable type 1699$1 && in_parser_context(SymbolContext) { 1700 error("unknown type '" $1 "'") 1701} 1702 1703# Generic parse failure 1704{ 1705 error("unrecognized statement") 1706} 1707 1708# Create a class instance with the given name 1709function class_new(name, superclass, _class) { 1710 if (_class != null) 1711 errorx("class_get() must be called with one or two arguments") 1712 1713 # Look for an existing class instance 1714 if (name in _g_class_names) 1715 errorx("redefining class: " name) 1716 1717 # Create and register the class object 1718 _class = obj_new(superclass) 1719 _g_class_names[name] = _class 1720 _g_obj[_class,OBJ_IS_CLS] = 1 1721 _g_obj[_class,CLS_NAME] = name 1722 1723 return (_class) 1724} 1725 1726# Return the class instance with the given name 1727function class_get(name) { 1728 if (name in _g_class_names) 1729 return (_g_class_names[name]) 1730 1731 errorx("no such class " name) 1732} 1733 1734# Return the name of cls 1735function class_get_name(cls) { 1736 if (cls == null) { 1737 warnx("class_get_name() called with null class") 1738 return "<null>" 1739 } 1740 1741 if (!obj_is_class(cls)) 1742 errorx(cls " is not a class object") 1743 1744 return (_g_obj[cls,CLS_NAME]) 1745} 1746 1747# Return true if the given property property ID is defined on class 1748function class_has_prop_id(class, prop_id, _super) { 1749 if (_super != null) 1750 errorx("class_has_prop_id() must be called with two arguments") 1751 1752 if (class == null) 1753 return (0) 1754 1755 if (prop_id == null) 1756 return (0) 1757 1758 # Check class<->prop cache 1759 if ((class, prop_id) in _g_class_prop_cache) 1760 return (1) 1761 1762 # Otherwise, slow path 1763 if (!obj_is_class(class)) 1764 errorx(class " is not a class object") 1765 1766 if (_super != null) 1767 errorx("class_has_prop_id() must be called with two arguments") 1768 1769 for (_super = class; _super != null; _super = obj_get_class(_super)) { 1770 if (!((_super,CLS_PROP,prop_id) in _g_obj)) 1771 continue 1772 1773 # Found; add to class<->prop cache 1774 _g_class_prop_cache[class,prop_id] = 1 1775 return (1) 1776 } 1777 1778 return (0) 1779} 1780 1781# Return true if the given property prop is defined on class 1782function class_has_property(class, prop) { 1783 if (!(PROP_ID in prop)) 1784 return (0) 1785 1786 return (class_has_prop_id(class, prop[PROP_ID])) 1787} 1788 1789# Define a `prop` on `class` with the given `name` string 1790function class_add_prop(class, prop, name, _prop_id) { 1791 if (_prop_id != null) 1792 errorx("class_add_prop() must be called with three arguments") 1793 1794 # Check for duplicate property definition 1795 if (class_has_property(class, prop)) 1796 errorx("property " prop[PROP_NAME] " already defined on " \ 1797 class_get_name(class)) 1798 1799 # Init property IDs 1800 if (_g_prop_ids == null) 1801 _g_prop_ids = 1 1802 1803 # Get (or create) new property entry 1804 if (name in _g_prop_names) { 1805 _prop_id = _g_prop_names[name] 1806 } else { 1807 _prop_id = _g_prop_ids++ 1808 _g_prop_names[name] = _prop_id 1809 _g_props[_prop_id] = name 1810 1811 prop[PROP_NAME] = name 1812 prop[PROP_ID] = _prop_id 1813 } 1814 1815 # Add to class definition 1816 _g_obj[class,CLS_PROP,prop[PROP_ID]] = name 1817 return (name) 1818} 1819 1820# Return the property ID for a given class-defined property 1821function class_get_prop_id(class, prop) { 1822 if (class == null) 1823 errorx("class_get_prop_id() on null class") 1824 1825 if (!class_has_property(class, prop)) { 1826 errorx("requested undefined property '" prop[PROP_NAME] "on " \ 1827 class_get_name(class)) 1828 } 1829 1830 return (prop[PROP_ID]) 1831} 1832 1833# Return the property ID for a given class-defined property name 1834function class_get_named_prop_id(class, name, _prop_id) { 1835 if (class == null) 1836 errorx("class_get_prop_id() on null class") 1837 1838 if (!(name in _g_prop_names)) 1839 errorx("requested undefined property '" name "'") 1840 1841 _prop_id = _g_prop_names[name] 1842 1843 if (!class_has_prop_id(class, _prop_id)) { 1844 errorx("requested undefined property '" _g_props[_prop_id] \ 1845 "' on " class_get_name(class)) 1846 } 1847 1848 return (_prop_id) 1849} 1850 1851# Create a new instance of the given class 1852function obj_new(class, _obj) { 1853 if (_obj != null) 1854 errorx("obj_new() must be called with one argument") 1855 1856 if (_g_obj_ids == null) 1857 _g_obj_ids = 1 1858 1859 # Assign ID and set superclass 1860 _obj = _g_obj_ids++ 1861 _g_obj[_obj,OBJ_SUPER] = class 1862 1863 return (_obj) 1864} 1865 1866# obj_delete() support for Map instances 1867function _obj_delete_map(obj, _prefix, _key) { 1868 obj_assert_class(obj, Map) 1869 _prefix = "^" obj SUBSEP 1870 for (_key in _g_maps) { 1871 if (!match(_key, _prefix) && _key != obj) 1872 continue 1873 delete _g_maps[_key] 1874 } 1875} 1876 1877# obj_delete() support for Array instances 1878function _obj_delete_array(obj, _size, _i) { 1879 obj_assert_class(obj, Array) 1880 _size = array_size(obj) 1881 1882 for (_i = 0; _i < _size; _i++) 1883 delete _g_arrays[obj,OBJ_PROP,_i] 1884} 1885 1886# Destroy all metadata associated with the given object 1887function obj_delete(obj, _prop_id, _prop_name, _prefix, _key, _size, _i) { 1888 if (obj_is_class(obj)) 1889 errorx("cannot delete class objects") 1890 1891 # Handle classes that use external global array storage 1892 # for effeciency 1893 if (obj_is_instanceof(obj, Map)) { 1894 _obj_delete_map(obj) 1895 } else if (obj_is_instanceof(obj, Array)) { 1896 _obj_delete_array(obj) 1897 } 1898 1899 # Delete all object properties 1900 for (_prop_name in _g_prop_names) { 1901 if (!obj_has_prop_id(obj, _prop_id)) 1902 continue 1903 1904 _prop_id = _g_prop_names[_prop_name] 1905 delete _g_obj[obj,OBJ_PROP,_prop_id] 1906 delete _g_obj_nr[obj,OBJ_PROP,_prop_id] 1907 } 1908 1909 # Delete instance state 1910 delete _g_obj[obj,OBJ_IS_CLS] 1911 delete _g_obj[obj,OBJ_SUPER] 1912} 1913 1914# Print an object's unique ID, class, and properties to 1915# stdout 1916function obj_dump(obj, _pname, _prop_id, _prop_val) { 1917 print(class_get_name(obj_get_class(obj)) "<" obj ">:") 1918 1919 # Dump all properties 1920 for (_pname in _g_prop_names) { 1921 _prop_id = _g_prop_names[_pname] 1922 1923 if (!obj_has_prop_id(obj, _prop_id)) 1924 continue 1925 1926 _prop_val = prop_get(obj, _prop_id) 1927 printf("\t%s: %s\n", _pname, _prop_val) 1928 } 1929} 1930 1931# Return true if obj is a class object 1932function obj_is_class(obj) { 1933 return (_g_obj[obj,OBJ_IS_CLS] == 1) 1934} 1935 1936# Return the class of obj, if any. 1937function obj_get_class(obj) { 1938 if (obj == null) 1939 errorx("obj_get_class() on null object") 1940 return (_g_obj[obj,OBJ_SUPER]) 1941} 1942 1943# Return true if obj is an instance of the given class 1944function obj_is_instanceof(obj, class, _super) { 1945 if (_super != null) 1946 errorx("obj_is_instanceof() must be called with two arguments") 1947 1948 if (!obj_is_class(class)) 1949 errorx(class " is not a class object") 1950 1951 if (obj == null) { 1952 errorx("obj_is_instanceof() called with null obj (class " \ 1953 class_get_name(class) ")") 1954 } 1955 1956 for (_super = obj_get_class(obj); _super != null; 1957 _super = obj_get_class(_super)) 1958 { 1959 if (_super == class) 1960 return (1) 1961 } 1962 1963 return (0) 1964} 1965 1966# Default object shallow equality implementation. Returns true if the two 1967# objects share a common superclass and have identity equality across all defined 1968# properties. 1969function obj_trivially_equal(lhs, rhs, _class, _pname, _prop_id) { 1970 # Simple case 1971 if (lhs == rhs) 1972 return (1) 1973 1974 # Must share a common superclass 1975 _class = obj_get_class(lhs) 1976 if (_class != obj_get_class(rhs)) 1977 return (0) 1978 1979 # Compare all properties 1980 _prop_count = 0 1981 for (_pname in _g_prop_names) { 1982 _prop_id = _g_prop_names[_pname] 1983 1984 if (!class_has_prop_id(_class, _prop_id)) 1985 continue 1986 1987 if (prop_get(lhs, _prop_id) != prop_get(rhs, _prop_id)) 1988 return (0) 1989 } 1990 1991 # All properties are trivially equal 1992 return (1) 1993} 1994 1995 1996# Return a debug string representation of an object's unique ID, class, and 1997# properties 1998function obj_to_string(obj, _pname, _prop_id, _prop_val, _prop_count, _result) { 1999 _result = class_get_name(obj_get_class(obj)) "<" obj ">: { " 2000 2001 # Fetch all properties 2002 _prop_count = 0 2003 for (_pname in _g_prop_names) { 2004 _prop_id = _g_prop_names[_pname] 2005 2006 if (!obj_has_prop_id(obj, _prop_id)) 2007 continue 2008 2009 if (_prop_count >= 0) 2010 _result = _result ", " 2011 2012 _result = _result sprintf("\t%s: %s\n", _pname, _prop_val) 2013 _prop_count++ 2014 } 2015 2016 return (_result " }") 2017} 2018 2019# Assert that obj is an instance of the given class 2020function obj_assert_class(obj, class) { 2021 if (!obj_is_instanceof(obj, class)) { 2022 errorx(class_get_name(obj_get_class(obj)) "<" obj "> is not " \ 2023 "an instance of " class_get_name(class)) 2024 } 2025} 2026 2027# Return true if the given property prop is defined by the object's superclass 2028function obj_has_property(obj, prop, _class) { 2029 if (obj == null) 2030 errorx("obj_has_property() on null object") 2031 2032 _class = obj_get_class(obj) 2033 return (class_has_property(_class, prop)) 2034} 2035 2036# Return true if the given property ID is defined by the object's superclass 2037function obj_has_prop_id(obj, prop_id, _class) { 2038 if (obj == null) 2039 errorx("obj_has_prop_id() on null object") 2040 2041 _class = obj_get_class(obj) 2042 return (class_has_prop_id(_class, prop_id)) 2043} 2044 2045# Return the line (NR) at which a given property ID was set on the object 2046# Will throw an error if the property has not been set on obj 2047function obj_get_prop_id_nr(obj, prop_id) { 2048 if (obj == null) 2049 errorx("obj_get_prop_id_nr() on null object") 2050 2051 if (!obj_has_prop_id(obj, prop_id)) { 2052 errorx("requested undefined property '" _g_props[prop_id] \ 2053 "' (" prop_id ") on " obj_to_string(obj)) 2054 } 2055 2056 # Fetch NR 2057 if ((obj,OBJ_PROP,prop_id) in _g_obj_nr) 2058 return (_g_obj_nr[obj,OBJ_PROP,prop_id]) 2059 2060 errorx("property '" _g_props[prop_id] "' (" prop_id ") not " \ 2061 "previously set on " obj_to_string(obj)) 2062} 2063 2064# Return the line (NR) at which a given property was set on the object 2065# Will throw an error if the property has not been set on obj 2066function obj_get_prop_nr(obj, prop) { 2067 return (obj_get_prop_id_nr(obj, prop[PROP_ID])) 2068} 2069 2070# Return an abstract property ID for a given property 2071function obj_get_prop_id(obj, prop) { 2072 if (obj == null) 2073 errorx("obj_get_prop_id() on null object") 2074 2075 return (class_get_prop_id(obj_get_class(obj), prop)) 2076} 2077 2078 2079# Return the property ID for a given property name 2080function obj_get_named_prop_id(obj, name) { 2081 if (obj == null) 2082 errorx("obj_get_named_prop_id() on null object") 2083 2084 return (class_get_named_prop_id(obj_get_class(obj), name)) 2085} 2086 2087# Set a property on obj 2088function set(obj, prop, value, _class) { 2089 return (prop_set(obj, prop[PROP_ID], value)) 2090} 2091 2092# Get a property value defined on obj 2093function get(obj, prop, _class) { 2094 return (prop_get(obj, prop[PROP_ID])) 2095} 2096 2097# Set a property on obj, using a property ID returned by obj_get_prop_id() or 2098# class_get_prop_id() 2099function prop_set(obj, prop_id, value, _class) { 2100 if (obj == null) { 2101 errorx("setting property '" _g_props[prop_id] \ 2102 "' on null object") 2103 } 2104 2105 _class = obj_get_class(obj) 2106 if (_class == null) 2107 errorx(obj " has no superclass") 2108 2109 if (!class_has_prop_id(_class, prop_id)) { 2110 errorx("requested undefined property '" _g_props[prop_id] \ 2111 "' (" prop_id ") on " class_get_name(_class)) 2112 } 2113 2114 # Track the line on which the property was set 2115 _g_obj_nr[obj,OBJ_PROP,prop_id] = NR 2116 _g_obj[obj,OBJ_PROP,prop_id] = value 2117} 2118 2119# Convert a property ID to a property path. 2120function prop_id_to_path(prop_id) { 2121 if (!(prop_id in _g_props)) 2122 errorx("'" prop_id "' is not a property ID") 2123 2124 # Convert to path string representation 2125 return (""prop_id) 2126} 2127 2128# Convert a property to a property path. 2129function prop_to_path(prop) { 2130 if (!(PROP_ID in prop)) 2131 errorx("prop_to_path() called with non-property head") 2132 2133 return (prop_id_to_path(prop[PROP_ID])) 2134} 2135 2136# Create a property path from head and tail properties 2137# Additional properties may be appended via prop_path_append() or 2138# prop_path_append_id() 2139function prop_path_create(head, tail) { 2140 if (!(PROP_ID in head)) 2141 errorx("prop_path() called with non-property head") 2142 2143 if (!(PROP_ID in tail)) 2144 errorx("prop_path() called with non-property tail") 2145 2146 return (head[PROP_ID] SUBSEP tail[PROP_ID]) 2147} 2148 2149# Append a property to the given property path 2150function prop_path_append(path, tail) { 2151 if (!(PROP_ID in tail)) 2152 errorx("prop_path_append() called with non-property tail") 2153 2154 return (prop_path_append_id(path, tail[PROP_ID])) 2155} 2156 2157# Append a property ID to the given property path 2158function prop_path_append_id(path, tail_id) { 2159 if (!(tail_id in _g_props)) 2160 errorx("'" tail_id "' is not a property ID") 2161 2162 return (path SUBSEP tail_id) 2163} 2164 2165# Fetch a value from obj using a property path previously returned by 2166# prop_path_create(), prop_to_path(), etc. 2167function prop_get_path(obj, prop_path, _class, _prop_ids, _nprop_ids, _next, 2168 _prop_head, _prop_len, _prop_tail) 2169{ 2170 if (obj == null) { 2171 errorx("requested property path '" \ 2172 gsub(SUBSEP, ".", prop_path) "' on null object") 2173 } 2174 2175 # Try the cache first 2176 _class = obj_get_class(obj) 2177 if ((_class,prop_path,PPATH_HEAD) in _g_ppath_cache) { 2178 _prop_head = _g_ppath_cache[_class,prop_path,PPATH_HEAD] 2179 _next = prop_get(obj, _prop_head) 2180 2181 if ((_class,prop_path,PPATH_TAIL) in _g_ppath_cache) { 2182 _prop_tail = _g_ppath_cache[_class,prop_path,PPATH_TAIL] 2183 return (prop_get_path(_next, _prop_tail)) 2184 } 2185 2186 return (_next) 2187 } 2188 2189 # Parse the head/tail of the property path and add to cache 2190 _nprop_ids = split(prop_path, _prop_ids, SUBSEP) 2191 if (_nprop_ids == 0) 2192 errorx("empty property path") 2193 _prop_head = _prop_ids[1] 2194 _g_ppath_cache[_class,prop_path,PPATH_HEAD] = _prop_head 2195 2196 if (_nprop_ids > 1) { 2197 _prop_len = length(_prop_head) 2198 _prop_tail = substr(prop_path, _prop_len+2) 2199 2200 # Add to cache 2201 _g_ppath_cache[_class,prop_path,PPATH_TAIL] = _prop_tail 2202 } 2203 2204 # Recursively call out implementation, this time fetching from 2205 # cache 2206 return (prop_get_path(obj, prop_path)) 2207} 2208 2209# Fetch a value property value from obj, using a property ID returned by 2210# obj_get_prop_id() or class_get_prop_id() 2211function prop_get(obj, prop_id, _class) { 2212 if (obj == null) { 2213 errorx("requested property '" _g_props[prop_id] \ 2214 "' on null object") 2215 } 2216 2217 _class = obj_get_class(obj) 2218 if (_class == null) 2219 errorx(obj " has no superclass") 2220 2221 if (!class_has_prop_id(_class, prop_id)) { 2222 errorx("requested undefined property '" _g_props[prop_id] \ 2223 "' (" prop_id ") on " class_get_name(_class)) 2224 } 2225 2226 return (_g_obj[obj,OBJ_PROP,prop_id]) 2227} 2228 2229# Create a new MacroType instance 2230function macro_type_new(name, const_suffix, _obj) { 2231 _obj = obj_new(MacroType) 2232 2233 set(_obj, p_name, name) 2234 set(_obj, p_const_suffix, const_suffix) 2235 2236 return (_obj) 2237} 2238 2239# Create a new MacroDefine instance 2240function macro_new(name, value, _obj) { 2241 _obj = obj_new(MacroDefine) 2242 set(_obj, p_name, name) 2243 set(_obj, p_value, value) 2244 2245 return (_obj) 2246} 2247 2248# Create an empty array; this uses _g_arrays to store integer 2249# keys/values under the object's property prefix. 2250function array_new(_obj) { 2251 _obj = obj_new(Array) 2252 set(_obj, p_count, 0) 2253 2254 return (_obj) 2255} 2256 2257# Return the number of elements in the array 2258function array_size(array) { 2259 obj_assert_class(array, Array) 2260 return (get(array, p_count)) 2261} 2262 2263# Return true if the array is empty 2264function array_empty(array) { 2265 return (array_size(array) == 0) 2266} 2267 2268# Append a value to the array 2269function array_append(array, value, _i) { 2270 obj_assert_class(array, Array) 2271 2272 _i = get(array, p_count) 2273 _g_arrays[array,OBJ_PROP,_i] = value 2274 set(array, p_count, _i+1) 2275} 2276 2277# Set an array value 2278# An error will be thrown if the idx is outside array bounds 2279function array_set(array, idx, value) { 2280 obj_assert_class(array, Array) 2281 2282 if (!((array,OBJ_PROP,idx) in _g_arrays)) 2283 errorx(idx " out of range of array " obj_to_string(array)) 2284 2285 _g_arrays[array,OBJ_PROP,idx] = value 2286} 2287 2288# Return value at the given index from the array 2289# An error will be thrown if 'idx' is outside the array bounds 2290function array_get(array, idx) { 2291 obj_assert_class(array, Array) 2292 2293 if (!((array,OBJ_PROP,idx) in _g_arrays)) 2294 errorx(idx " out of range of array " obj_to_string(array)) 2295 2296 return (_g_arrays[array,OBJ_PROP,idx]) 2297} 2298 2299 2300# 2301# Sort an array, using standard awk comparison operators over its values. 2302# 2303# If `prop_path*` is non-NULL, the corresponding property path (or property ID) 2304# will be fetched from each array element and used as the sorting value. 2305# 2306# If multiple property paths are specified, the array is first sorted by 2307# the first path, and then any equal values are sorted by the second path, 2308# and so on. 2309# 2310function array_sort(array, prop_path0, prop_path1, prop_path2, _size) { 2311 obj_assert_class(array, Array) 2312 2313 if (_size != null) 2314 errorx("no more than three property paths may be specified") 2315 2316 _size = array_size(array) 2317 if (_size <= 1) 2318 return 2319 2320 _qsort(array, prop_path0, prop_path1, prop_path2, 0, _size-1) 2321} 2322 2323function _qsort_get_key(array, idx, prop_path, _v) { 2324 _v = array_get(array, idx) 2325 2326 if (prop_path == null) 2327 return (_v) 2328 2329 return (prop_get_path(_v, prop_path)) 2330} 2331 2332function _qsort_compare(array, lhs_idx, rhs_val, ppath0, ppath1, ppath2, 2333 _lhs_val, _rhs_prop_val) 2334{ 2335 _lhs_val = _qsort_get_key(array, lhs_idx, ppath0) 2336 if (ppath0 == null) 2337 _rhs_prop_val = rhs_val 2338 else 2339 _rhs_prop_val = prop_get_path(rhs_val, ppath0) 2340 2341 if (_lhs_val == _rhs_prop_val && ppath1 != null) { 2342 _lhs_val = _qsort_get_key(array, lhs_idx, ppath1) 2343 _rhs_prop_val = prop_get_path(rhs_val, ppath1) 2344 2345 if (_lhs_val == _rhs_prop_val && ppath2 != null) { 2346 _lhs_val = _qsort_get_key(array, lhs_idx, ppath2) 2347 _rhs_prop_val = prop_get_path(rhs_val, ppath2) 2348 } 2349 } 2350 2351 if (_lhs_val < _rhs_prop_val) 2352 return (-1) 2353 else if (_lhs_val > _rhs_prop_val) 2354 return (1) 2355 else 2356 return (0) 2357} 2358 2359function _qsort(array, ppath0, ppath1, ppath2, first, last, _qpivot, 2360 _qleft, _qleft_val, _qright, _qright_val) 2361{ 2362 if (first >= last) 2363 return 2364 2365 # select pivot element 2366 _qpivot = int(first + int((last-first+1) * rand())) 2367 _qleft = first 2368 _qright = last 2369 2370 _qpivot_val = array_get(array, _qpivot) 2371 2372 # partition 2373 while (_qleft <= _qright) { 2374 while (_qsort_compare(array, _qleft, _qpivot_val, ppath0, ppath1, 2375 ppath2) < 0) 2376 { 2377 _qleft++ 2378 } 2379 2380 while (_qsort_compare(array, _qright, _qpivot_val, ppath0, ppath1, 2381 ppath2) > 0) 2382 { 2383 _qright-- 2384 } 2385 2386 # swap 2387 if (_qleft <= _qright) { 2388 _qleft_val = array_get(array, _qleft) 2389 _qright_val = array_get(array, _qright) 2390 2391 array_set(array, _qleft, _qright_val) 2392 array_set(array, _qright, _qleft_val) 2393 2394 _qleft++ 2395 _qright-- 2396 } 2397 } 2398 2399 # sort the partitions 2400 _qsort(array, ppath0, ppath1, ppath2, first, _qright) 2401 _qsort(array, ppath0, ppath1, ppath2, _qleft, last) 2402} 2403 2404 2405# 2406# Join all array values with the given separator 2407# 2408# If `prop_path` is non-NULL, the corresponding property path (or property ID) 2409# will be fetched from each array value and included in the result, rather than 2410# immediate array value 2411# 2412function array_join(array, sep, prop_path, _i, _size, _value, _result) { 2413 obj_assert_class(array, Array) 2414 2415 _result = "" 2416 _size = array_size(array) 2417 for (_i = 0; _i < _size; _i++) { 2418 # Fetch the value (and optionally, a target property) 2419 _value = array_get(array, _i) 2420 if (prop_path != null) 2421 _value = prop_get_path(_value, prop_path) 2422 2423 if (_i+1 < _size) 2424 _result = _result _value sep 2425 else 2426 _result = _result _value 2427 } 2428 2429 return (_result) 2430} 2431 2432# Return the first value in the array, or null if empty 2433function array_first(array) { 2434 obj_assert_class(array, Array) 2435 2436 if (array_size(array) == 0) 2437 return (null) 2438 else 2439 return (array_get(array, 0)) 2440} 2441 2442# Return the last value in the array, or null if empty 2443function array_tail(list, _size) { 2444 obj_assert_class(array, Array) 2445 2446 _size = array_size(array) 2447 if (_size == 0) 2448 return (null) 2449 else 2450 return (array_get(array, _size-1)) 2451} 2452 2453# Create an empty hash table; this uses the _g_maps array to store arbitrary 2454# keys/values under the object's property prefix. 2455function map_new(_obj) { 2456 _obj = obj_new(Map) 2457 return (_obj) 2458} 2459 2460# Add `key` with `value` to `map` 2461function map_set(map, key, value) { 2462 obj_assert_class(map, Map) 2463 _g_maps[map,OBJ_PROP,key] = value 2464} 2465 2466# Remove `key` from the map 2467function map_remove(map, key) { 2468 obj_assert_class(map, Map) 2469 delete _g_maps[map,OBJ_PROP,key] 2470} 2471 2472# Return true if `key` is found in `map`, false otherwise 2473function map_contains(map, key) { 2474 obj_assert_class(map, Map) 2475 return ((map,OBJ_PROP,key) in _g_maps) 2476} 2477 2478# Fetch the value of `key` from the map. Will throw an error if the 2479# key does not exist 2480function map_get(map, key) { 2481 obj_assert_class(map, Map) 2482 return _g_maps[map,OBJ_PROP,key] 2483} 2484 2485# Create and return a new list containing all defined values in `map` 2486function map_to_array(map, _key, _prefix, _values) { 2487 obj_assert_class(map, Map) 2488 2489 _values = array_new() 2490 _prefix = "^" map SUBSEP OBJ_PROP SUBSEP 2491 for (_key in _g_maps) { 2492 if (!match(_key, _prefix)) 2493 continue 2494 2495 array_append(_values, _g_maps[_key]) 2496 } 2497 2498 return (_values) 2499} 2500 2501# Create a new Type instance 2502function type_new(name, width, signed, constant, array_constant, fmt, mask, 2503 constant_value, array_constant_value, _obj) 2504{ 2505 obj_assert_class(fmt, Fmt) 2506 2507 _obj = obj_new(Type) 2508 set(_obj, p_name, name) 2509 set(_obj, p_width, width) 2510 set(_obj, p_signed, signed) 2511 set(_obj, p_const, constant) 2512 set(_obj, p_const_val, constant_value) 2513 set(_obj, p_array_const, array_constant) 2514 set(_obj, p_array_const_val, array_constant_value) 2515 set(_obj, p_default_fmt, fmt) 2516 set(_obj, p_mask, mask) 2517 2518 return (_obj) 2519} 2520 2521# Return true if two types are equal 2522function type_equal(lhs, rhs) { 2523 # Simple case 2524 if (lhs == rhs) 2525 return (1) 2526 2527 # Must share a common class 2528 if (obj_get_class(lhs) != obj_get_class(rhs)) 2529 return (0) 2530 2531 # Handle ArrayType equality 2532 if (obj_is_instanceof(lhs, ArrayType)) { 2533 # Size must be equal 2534 if (get(lhs, p_count) != get(rhs, p_count)) 2535 return (0) 2536 2537 # The base types must be equal 2538 return (type_equal(type_get_base(lhs), type_get_base(rhs))) 2539 } 2540 2541 # Handle Type equality -- we just check for trivial identity 2542 # equality of all members 2543 obj_assert_class(lhs, Type) 2544 return (obj_trivially_equal(lhs, rhs)) 2545} 2546 2547# Return the type's default value mask. If the type is an array type, 2548# the default mask of the base type will be returned. 2549function type_get_default_mask(type) { 2550 if (obj_is_instanceof(type, ArrayType)) 2551 return (type_get_default_mask(type_get_base(type))) 2552 2553 obj_assert_class(type, Type) 2554 return (get(type, p_mask)) 2555} 2556 2557# Return the type's C constant representation 2558function type_get_const(type) { 2559 if (obj_is_instanceof(type, ArrayType)) 2560 return (get(type_get_base(type), p_array_const)) 2561 2562 obj_assert_class(type, Type) 2563 return (get(type, p_const)) 2564} 2565 2566# Return the type's C constant integer value 2567function type_get_const_val(type) { 2568 if (obj_is_instanceof(type, ArrayType)) 2569 return (get(type_get_base(type), p_array_const_val)) 2570 2571 obj_assert_class(type, Type) 2572 return (get(type, p_const_val)) 2573} 2574 2575# Return an array type's element count, or 1 if the type is not 2576# an array type 2577function type_get_nelem(type) { 2578 if (obj_is_instanceof(type, ArrayType)) 2579 return (get(type, p_count)) 2580 2581 obj_assert_class(type, Type) 2582 return (1) 2583} 2584 2585# Return the base type for a given type instance. 2586function type_get_base(type) { 2587 if (obj_is_instanceof(type, ArrayType)) 2588 return (type_get_base(get(type, p_type))) 2589 2590 obj_assert_class(type, Type) 2591 return (type) 2592} 2593 2594# Return the default fmt for a given type instance 2595function type_get_default_fmt(type, _base, _fmt, _array_fmt) { 2596 _base = type_get_base(type) 2597 _fmt = get(_base, p_default_fmt) 2598 2599 if (obj_is_instanceof(type, ArrayType)) { 2600 _array_fmt = get(_fmt, p_array_fmt) 2601 if (_array_fmt != null) 2602 _fmt = _array_fmt 2603 } 2604 2605 return (_fmt) 2606} 2607 2608# Return a string representation of the given type 2609function type_to_string(type, _base_type) { 2610 if (obj_is_instanceof(type, ArrayType)) { 2611 _base_type = type_get_base(type) 2612 return (type_to_string(_base_type) "[" get(type, p_count) "]") 2613 } 2614 return get(type, p_name) 2615} 2616 2617# Return true if type `rhs` is can be coerced to type `lhs` without data 2618# loss 2619function type_can_represent(lhs, rhs) { 2620 # Must be of the same class (Type or ArrayType) 2621 if (obj_get_class(lhs) != obj_get_class(rhs)) 2622 return (0) 2623 2624 if (obj_is_instanceof(lhs, ArrayType)) { 2625 # The base types must have a representable relationship 2626 if (!type_can_represent(type_get_base(lhs), type_get_base(rhs))) 2627 return (0) 2628 2629 # The lhs type must be able to represent -at least- as 2630 # many elements as the RHS type 2631 if (get(lhs, p_count) < get(rhs, p_count)) 2632 return (0) 2633 2634 return (1) 2635 } 2636 2637 # A signed type could represent the full range of a smaller unsigned 2638 # type, but we don't bother; the two should agree when used in a SROM 2639 # layout. Instead simply assert that both are signed or unsigned. 2640 if (get(lhs, p_signed) != get(rhs, p_signed)) 2641 return (0) 2642 2643 # The `rhs` type must be equal or smaller in width to the `lhs` type 2644 if (get(lhs, p_width) < get(rhs, p_width)) 2645 return (0) 2646 2647 return (1) 2648} 2649 2650# Create a new ArrayType instance 2651function array_type_new(type, count, _obj) { 2652 _obj = obj_new(ArrayType) 2653 set(_obj, p_type, type) 2654 set(_obj, p_count, count) 2655 2656 return (_obj) 2657} 2658 2659# 2660# Parse a type string to either the Type, ArrayType, or null if 2661# the type is not recognized. 2662# 2663function parse_type_string(str, _base, _count) { 2664 if (match(str, ARRAY_REGEX"$") > 0) { 2665 # Extract count and base type 2666 _count = substr(str, RSTART+1, RLENGTH-2) 2667 sub(ARRAY_REGEX"$", "", str) 2668 2669 # Look for base type 2670 if ((_base = type_named(str)) == null) 2671 return (null) 2672 2673 return (array_type_new(_base, int(_count))) 2674 } else { 2675 return (type_named(str)) 2676 } 2677} 2678 2679# 2680# Parse a variable name in the form of 'name' or 'name[len]', returning 2681# either the provided base_type if no array specifiers are found, or 2682# the fully parsed ArrayType. 2683# 2684function parse_array_type_specifier(str, base_type, _count) { 2685 if (match(str, ARRAY_REGEX"$") > 0) { 2686 # Extract count 2687 _count = substr(str, RSTART+1, RLENGTH-2) 2688 return (array_type_new(base_type, int(_count))) 2689 } else { 2690 return (base_type) 2691 } 2692} 2693 2694# Return the type constant for `name`, if any 2695function type_named(name, _n, _type) { 2696 if (name == null) 2697 errorx("called type_named() with null name") 2698 2699 if (map_contains(BaseTypes, name)) 2700 return (map_get(BaseTypes, name)) 2701 2702 return (null) 2703} 2704 2705# Create a new Fmt instance 2706function fmt_new(name, symbol, array_fmt, _obj) { 2707 _obj = obj_new(Fmt) 2708 set(_obj, p_name, name) 2709 set(_obj, p_symbol, symbol) 2710 2711 if (array_fmt != null) 2712 set(_obj, p_array_fmt, array_fmt) 2713 2714 return (_obj) 2715} 2716 2717 2718# Return the Fmt constant for `name`, if any 2719function fmt_named(name, _n, _fmt) { 2720 if (map_contains(ValueFormats, name)) 2721 return (map_get(ValueFormats, name)) 2722 2723 return (null) 2724} 2725 2726# Create a new VFlag instance 2727function vflag_new(name, constant, _obj) { 2728 _obj = obj_new(VFlag) 2729 set(_obj, p_name, name) 2730 set(_obj, p_const, constant) 2731 2732 return (_obj) 2733} 2734 2735# Create a new StringConstant AST node 2736function stringconstant_new(value, continued, _obj) { 2737 _obj = obj_new(StringConstant) 2738 set(_obj, p_value, value) 2739 set(_obj, p_continued, continued) 2740 set(_obj, p_line, NR) 2741 2742 return (_obj) 2743} 2744 2745# Create an empty StringConstant AST node to which additional lines 2746# may be appended 2747function stringconstant_empty(_obj) { 2748 return (stringconstant_new("", 1)) 2749} 2750 2751# Parse an input string and return a new string constant 2752# instance 2753function stringconstant_parse_line(line, _obj) { 2754 _obj = stringconstant_empty() 2755 stringconstant_append_line(_obj, line) 2756 return (_obj) 2757} 2758 2759# Parse and apend an additional line to this string constant 2760function stringconstant_append_line(str, line, _cont, _strbuf, _regex, _eol) { 2761 obj_assert_class(str, StringConstant) 2762 2763 # Must be marked for continuation 2764 if (!get(str, p_continued)) { 2765 errorx("can't append to non-continuation string '" \ 2766 get(str, p_value) "'") 2767 } 2768 2769 _strbuf = get(str, p_value) 2770 2771 # If start of string, look for (and remove) initial double quote 2772 if (_strbuf == null) { 2773 _regex = "^[ \t]*\"" 2774 if (!sub(_regex, "", line)) { 2775 error("expected quoted string") 2776 } 2777 } 2778 2779 # Look for a terminating double quote 2780 _regex = "([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"" 2781 2782 _eol = match(line, _regex) 2783 if (_eol > 0) { 2784 # Drop everything following the terminating quote 2785 line = substr(line, 1, RLENGTH-1) 2786 _cont = 0 2787 } else { 2788 # No terminating quote found, continues on next line 2789 _cont = 1 2790 } 2791 2792 # Trim leading and trailing whitespace 2793 sub(/(^[ \t]+|[ \t]+$)/, "", line) 2794 2795 # Append to existing buffer 2796 if ((_strbuf = get(str, p_value)) == NULL) 2797 set(str, p_value, line) 2798 else 2799 set(str, p_value, _strbuf " " line) 2800 2801 # Update line continuation setting 2802 set(str, p_continued, _cont) 2803} 2804 2805# Create a new RevRange instance 2806function revrange_new(start, end, _obj) { 2807 _obj = obj_new(RevRange) 2808 set(_obj, p_start, start) 2809 set(_obj, p_end, end) 2810 set(_obj, p_line, NR) 2811 2812 return (_obj) 2813} 2814 2815# Return true if the two revision ranges are equal 2816function revrange_equal(lhs, rhs) { 2817 if (get(lhs, p_start) != get(rhs, p_start)) 2818 return (0) 2819 2820 if (get(lhs, p_end) != get(rhs, p_end)) 2821 return (0) 2822 2823 return (1) 2824} 2825 2826# Return true if the requested rev is covered by revrange, false otherwise 2827function revrange_contains(range, rev) { 2828 obj_assert_class(range, RevRange) 2829 2830 if (rev < get(range, p_start)) 2831 return (0) 2832 else if (rev > get(range, p_end)) { 2833 return (0) 2834 } else { 2835 return (1) 2836 } 2837} 2838 2839# 2840# Return a string representation of the given revision range 2841# 2842function revrange_to_string(revs, _start, _end) { 2843 obj_assert_class(revs, RevRange) 2844 2845 _start = get(revs, p_start) 2846 _end = get(revs, p_end) 2847 2848 if (_start == 0) 2849 return ("<= " _end) 2850 else if (_end == REV_MAX) 2851 return (">= " _start) 2852 else 2853 return (_start "-" _end) 2854} 2855 2856# Create a new VarGroup instance 2857function var_group_new(name, _obj) { 2858 _obj = obj_new(VarGroup) 2859 set(_obj, p_name, name) 2860 set(_obj, p_vars, array_new()) 2861 set(_obj, p_line, NR) 2862 2863 return (_obj) 2864} 2865 2866# Create a new NVRAM instance 2867function nvram_new(_obj, _vars, _v) { 2868 _obj = obj_new(NVRAM) 2869 _vars = array_new() 2870 set(_obj, p_vars, _vars) 2871 set(_obj, p_var_groups, array_new()) 2872 set(_obj, p_srom_layouts, array_new()) 2873 set(_obj, p_srom_table, map_new()) 2874 2875 # 2876 # Register our implicit variable definitions 2877 # 2878 2879 # SROM signature offset 2880 _v = var_new(VAccessInternal, "<sromsig>", UInt16) 2881 array_append(_vars, _v) 2882 _g_var_names[get(_v, p_name)] = _v 2883 2884 # SROM CRC8 offset 2885 _v = var_new(VAccessInternal, "<sromcrc>", UInt8) 2886 array_append(_vars, _v) 2887 _g_var_names[get(_v, p_name)] = _v 2888 2889 return (_obj) 2890} 2891 2892# Register a new SROM layout instance 2893# An error will be thrown if the layout overlaps any revisions covered 2894# by an existing instance. 2895function nvram_add_srom_layout(nvram, layout, _table, _revs, _start, _end, _i) { 2896 obj_assert_class(nvram, NVRAM) 2897 obj_assert_class(layout, SromLayout) 2898 2899 # revision:layout hash table 2900 _table = get(nvram, p_srom_table) 2901 2902 # register the layout's revisions 2903 _revs = get(layout, p_revisions) 2904 _start = get(_revs, p_start) 2905 _end = get(_revs, p_end) 2906 2907 for (_i = _start; _i <= _end; _i++) { 2908 if (map_contains(_table, _i)) { 2909 error("SROM layout redeclares layout for revision '" \ 2910 _i "' (originally declared on line " \ 2911 get(map_get(_table, _i), p_line) ")") 2912 } 2913 2914 map_set(_table, _i, layout) 2915 } 2916 2917 # append to srom_layouts 2918 array_append(get(nvram, p_srom_layouts), layout) 2919} 2920 2921# Return the first SROM layout registered for a given SROM revision, 2922# or null if no matching layout is found 2923function nvram_get_srom_layout(nvram, revision, _layouts, _nlayouts, _layout, 2924 _i) 2925{ 2926 obj_assert_class(nvram, NVRAM) 2927 2928 _layouts = get(nvram, p_srom_layouts) 2929 _nlayouts = array_size(_layouts) 2930 for (_i = 0; _i < _nlayouts; _i++) { 2931 _layout = array_get(_layouts, _i) 2932 2933 if (srom_layout_has_rev(_layout, revision)) 2934 return (_layout) 2935 } 2936 2937 # Not found 2938 return (null) 2939} 2940 2941# Create a new Var instance 2942function var_new(access, name, type, _obj) { 2943 obj_assert_class(access, VAccess) 2944 2945 # Validate the variable identifier 2946 # 2947 # The access modifier dictates the permitted identifier format. 2948 # VAccessInternal: <ident> 2949 # VAccess(Public|Private): ident 2950 if (access != VAccessInternal && name ~ SVAR_IDENT_REGEX) { 2951 error("invalid identifier '"name"'; did you mean to " \ 2952 "mark this variable as internal?") 2953 } else if (access == VAccessInternal) { 2954 if (name !~ SVAR_IDENT_REGEX) 2955 error("invalid identifier '"name"' for internal " \ 2956 "variable; did you mean '<" name ">'?") 2957 } else if (name !~ VAR_IDENT_REGEX) { 2958 error("invalid identifier '"name"'") 2959 } 2960 2961 _obj = obj_new(Var) 2962 set(_obj, p_access, access) 2963 set(_obj, p_name, name) 2964 set(_obj, p_type, type) 2965 set(_obj, p_line, NR) 2966 2967 return (_obj) 2968} 2969 2970# Return true if var is internal-only, and should not be included 2971# in any output (e.g. has an access specifier of VAccessInternal). 2972function var_is_internal(var) { 2973 return (get(var, p_access) == VAccessInternal) 2974} 2975 2976# Return true if `var` has an array type 2977function var_has_array_type(var, _vtype) { 2978 obj_assert_class(var, Var) 2979 _vtype = get(var, p_type) 2980 return (obj_is_instanceof(_vtype, ArrayType)) 2981} 2982 2983# Return the number of array elements defined by this variable's type, 2984# or 1 if the variable does not have an array type. 2985function var_get_array_len(var) { 2986 obj_assert_class(var, Var) 2987 return (type_get_nelem(get(var, p_type))) 2988} 2989 2990# Return the fmt for var. If not explicitly set on var, will return then 2991# return of calling type_get_default_fmt() with the variable's type 2992function var_get_fmt(var, _fmt) { 2993 obj_assert_class(var, Var) 2994 2995 # If defined on var, return it 2996 if ((_fmt = get(var, p_fmt)) != null) 2997 return (_fmt) 2998 2999 # Fall back on the type's default 3000 return (type_get_default_fmt(get(var, p_type))) 3001} 3002 3003# Return a new MacroDefine instance for the given variable, macro type, 3004# and value 3005function var_get_macro(var, macro_type, value, _macro) { 3006 obj_assert_class(var, Var) 3007 obj_assert_class(macro_type, MacroType) 3008 3009 return (macro_new(var_get_macro_name(var, macro_type), value)) 3010} 3011 3012# Return the preprocessor constant name to be used with `var` for the given 3013# macro_type 3014function var_get_macro_name(var, macro_type, _var_name, _suffix) { 3015 obj_assert_class(var, Var) 3016 obj_assert_class(macro_type, MacroType) 3017 3018 _var_name = get(var, p_name) 3019 _suffix = get(macro_type, p_const_suffix) 3020 3021 return("BHND_NVAR_" toupper(_var_name) _suffix) 3022} 3023 3024# Create a new SromLayout instance 3025function srom_layout_new(rev_desc, _obj) 3026{ 3027 _obj = obj_new(SromLayout) 3028 set(_obj, p_revisions, rev_desc) 3029 set(_obj, p_entries, array_new()) 3030 set(_obj, p_revmap, map_new()) 3031 set(_obj, p_output_var_counts, map_new()) 3032 set(_obj, p_line, NR) 3033 3034 return (_obj) 3035} 3036 3037# Register a new entry with the srom layout 3038function srom_layout_add_entry(layout, entry, _revmap, _name, _rev_start, 3039 _rev_end, _var, _prev_entry, _count, _i) 3040{ 3041 obj_assert_class(layout, SromLayout) 3042 obj_assert_class(entry, SromEntry) 3043 3044 _layout_revmap = get(layout, p_revmap) 3045 _layout_var_count = get(layout, p_output_var_counts) 3046 3047 _var = get(entry, p_var) 3048 _name = get(_var, p_name) 3049 3050 # Add to revision array 3051 array_append(get(layout, p_entries), entry) 3052 3053 # Add to the revision map tables 3054 _rev_start = get(get(entry, p_revisions), p_start) 3055 _rev_end = get(get(entry, p_revisions), p_end) 3056 3057 for (_i = _rev_start; _i <= _rev_end; _i++) { 3058 # Check for existing entry 3059 _prev_entry = srom_layout_find_entry(layout, _name, _i) 3060 if (_prev_entry != null) { 3061 error("redefinition of variable '" _name "' for SROM " \ 3062 "revision " _i " (previously defined on line " \ 3063 get(_prev_entry, p_line) ")") 3064 } 3065 3066 # Add to the (varname,revision) map 3067 map_set(_layout_revmap, (_name SUBSEP _i), entry) 3068 3069 # If an output variable, set or increment the output variable 3070 # count 3071 if (!srom_entry_should_output(entry, _i)) 3072 continue 3073 3074 if (!map_contains(_layout_var_count, _i)) { 3075 map_set(_layout_var_count, _i, 1) 3076 } else { 3077 _count = map_get(_layout_var_count, _i) 3078 map_set(_layout_var_count, _i, _count + 1) 3079 } 3080 } 3081} 3082 3083 3084# Return the variable name to be used when emitting C declarations 3085# for this SROM layout 3086# 3087# The name is gauranteed to be unique across SROM layouts with non-overlapping 3088# revision ranges 3089function srom_layout_get_variable_name(layout, _revs) { 3090 obj_assert_class(layout, SromLayout) 3091 3092 _revs = get(layout, p_revisions) 3093 3094 return ("bhnd_sprom_layout_r" get(_revs, p_start) \ 3095 "_r" get(_revs, p_end)) 3096} 3097 3098# Return true if the given SROM revision is defined by the layout, false 3099# otherwise 3100function srom_layout_has_rev(layout, rev) { 3101 obj_assert_class(layout, SromLayout) 3102 return (revrange_contains(get(layout, p_revisions), rev)) 3103} 3104 3105 3106# Return the total number of output variables (variables to be included 3107# in the SROM layout bindings) for the given SROM revision 3108function srom_layout_num_output_vars(layout, rev, _counts) 3109{ 3110 obj_assert_class(layout, SromLayout) 3111 3112 _counts = get(layout, p_output_var_counts) 3113 if (!map_contains(_counts, rev)) 3114 return (0) 3115 3116 return (map_get(_counts, rev)) 3117} 3118 3119# Return the SromEntry defined for the given variable name and SROM revision, 3120# or null if none 3121function srom_layout_find_entry(layout, vname, revision, _key, _srom_revmap) { 3122 obj_assert_class(layout, SromLayout) 3123 3124 _srom_revmap = get(layout, p_revmap) 3125 3126 # SromEntry are mapped by name,revision composite keys 3127 _key = vname SUBSEP revision 3128 if (!map_contains(_srom_revmap, _key)) 3129 return (null) 3130 3131 return (map_get(_srom_revmap, _key)) 3132 3133} 3134 3135# Create a new SromLayoutFilter instance, checking that `revs` 3136# falls within the parent's revision range 3137function srom_layout_filter_new(parent, revs, _obj, _start, _end, _parent_revs) { 3138 obj_assert_class(parent, SromLayout) 3139 obj_assert_class(revs, RevRange) 3140 3141 # Fetch our parent's revision range, confirm that we're 3142 # a strict subset 3143 _start = get(revs, p_start) 3144 _end = get(revs, p_end) 3145 _parent_revs = get(parent, p_revisions) 3146 3147 if (!revrange_contains(_parent_revs, _start)) 3148 error("'" _start "' is outside of parent range") 3149 3150 if (!revrange_contains(_parent_revs, _end)) 3151 error("'" _end "' is outside of parent range") 3152 3153 if (revrange_equal(revs, _parent_revs)) { 3154 error("srom range '" revrange_to_string(revs) "' is " \ 3155 "identical to parent range of '" \ 3156 revrange_to_string(_parent_revs) "'") 3157 } 3158 3159 # Construct and return new filter instance 3160 _obj = obj_new(SromLayoutFilter) 3161 set(_obj, p_parent, parent) 3162 set(_obj, p_revisions, revs) 3163 set(_obj, p_line, NR) 3164 3165 return (_obj) 3166} 3167 3168# 3169# Create a new SromEntry instance 3170# 3171# var: The variable referenced by this entry 3172# revisions: The SROM revisions to which this entry applies 3173# base_offset: The SROM entry offset; any relative segment offsets will be 3174# calculated relative to the base offset 3175# type: The SROM's value type; this may be a subtype of the variable 3176# type, and defines the data (width, sign, etc) to be read from 3177# SROM. 3178# 3179function srom_entry_new(var, revisions, base_offset, type, _obj) { 3180 obj_assert_class(var, Var) 3181 if (revisions != null) 3182 obj_assert_class(revisions, RevRange) 3183 3184 _obj = obj_new(SromEntry) 3185 set(_obj, p_var, var) 3186 set(_obj, p_revisions, revisions) 3187 set(_obj, p_base_offset, base_offset) 3188 set(_obj, p_type, type) 3189 set(_obj, p_offsets, array_new()) 3190 set(_obj, p_line, NR) 3191 3192 return (_obj) 3193} 3194 3195# Return true if the SromEntry has an array type 3196function srom_entry_has_array_type(entry) { 3197 obj_assert_class(entry, SromEntry) 3198 3199 return (obj_is_instanceof(get(entry, p_type), ArrayType)) 3200} 3201 3202# Return the number of array elements defined by this SromEntry's type, 3203# or 1 if the entry does not have an array type. 3204function srom_entry_get_array_len(entry, _type) { 3205 obj_assert_class(entry, SromEntry) 3206 3207 return (type_get_nelem(get(entry, p_type))) 3208} 3209 3210# 3211# Return true if the given entry should be included in the output bindings 3212# generated for the given revision, false otherwise. 3213# 3214function srom_entry_should_output(entry, rev, _var, _revs) 3215{ 3216 obj_assert_class(entry, SromEntry) 3217 3218 _var = get(entry, p_var) 3219 _revs = get(entry, p_revisions) 3220 3221 # Exclude internal variables 3222 if (var_is_internal(_var)) 3223 return (0) 3224 3225 # Exclude inapplicable entry revisions 3226 if (!revrange_contains(_revs, rev)) 3227 return (0) 3228 3229 return (1) 3230} 3231 3232# 3233# Return the single, non-shifted, non-masked offset/segment for the given 3234# SromEntry, or throw an error if the entry contains multiple offsets/segments. 3235# 3236# This is used to fetch special-cased variable definitions that are required 3237# to present a single simple offset. 3238# 3239function srom_entry_get_single_segment(entry, _offsets, _segments, _seg, 3240 _base_type, _default_mask) 3241{ 3242 obj_assert_class(entry, SromEntry) 3243 3244 # Fetch the single offset's segment list 3245 _offsets = get(entry, p_offsets) 3246 if (array_size(_offsets) != 1) 3247 errorc(get(entry, p_line), "unsupported offset count") 3248 3249 _segments = get(array_first(_offsets), p_segments) 3250 if (array_size(_segments) != 1) 3251 errorc(get(entry, p_line), "unsupported segment count") 3252 3253 # Fetch the single segment 3254 _seg = array_first(_segments) 3255 _base_type = srom_segment_get_base_type(_seg) 3256 _default_mask = get(_base_type, p_mask) 3257 3258 # Must not be shifted/masked 3259 if (get(_seg, p_shift) != 0) 3260 errorc(obj_get_prop_nr(_seg, p_mask), "shift unsupported") 3261 3262 if (get(_seg, p_mask) != _default_mask) 3263 errorc(obj_get_prop_nr(_seg, p_mask), "mask unsupported") 3264 3265 return (_seg) 3266} 3267 3268# Create a new SromOffset instance 3269function srom_offset_new(_obj) { 3270 _obj = obj_new(SromOffset) 3271 set(_obj, p_segments, array_new()) 3272 set(_obj, p_line, NR) 3273 3274 return (_obj) 3275} 3276 3277# Return the number of SromSegment instances defined by this offset. 3278function srom_offset_segment_count(offset) { 3279 obj_assert_class(offset, SromOffset) 3280 return (array_size(get(offset, p_segments))) 3281} 3282 3283# Return the idx'th segment. Will throw an error if idx falls outside 3284# the number of available segments. 3285function srom_offset_get_segment(offset, idx, _segments, _seg) { 3286 obj_assert_class(offset, SromOffset) 3287 3288 return (array_get(get(offset, p_segments), idx)) 3289} 3290 3291# Create a new SromSegment instance 3292function srom_segment_new(offset, type, mask, shift, value, _obj) { 3293 _obj = obj_new(SromSegment) 3294 set(_obj, p_offset, offset) 3295 set(_obj, p_type, type) 3296 set(_obj, p_mask, mask) 3297 set(_obj, p_shift, shift) 3298 set(_obj, p_value, value) 3299 set(_obj, p_line, NR) 3300 3301 return (_obj) 3302} 3303 3304# Return true if the segment has an array type 3305function srom_segment_has_array_type(seg, _type) { 3306 _type = srom_segment_get_type(seg) 3307 return (obj_is_instanceof(_type, ArrayType)) 3308} 3309 3310# Return the array count of the segment, or 1 if the segment does not have 3311# an array type 3312function srom_segment_get_array_len(seg, _type) { 3313 if (!srom_segment_has_array_type(seg)) 3314 return (1) 3315 3316 _type = srom_segment_get_type(seg) 3317 return (get(_type, p_count)) 3318} 3319 3320# Return the type of the segment 3321function srom_segment_get_type(seg) { 3322 obj_assert_class(seg, SromSegment) 3323 return (get(seg, p_type)) 3324 3325} 3326 3327# Return the base type of the segment 3328function srom_segment_get_base_type(seg) { 3329 return (type_get_base(srom_segment_get_type(seg))) 3330} 3331 3332# Return true if the two segments have identical types and attributes (i.e. 3333# differing only by offset) 3334function srom_segment_attributes_equal(lhs, rhs) { 3335 obj_assert_class(lhs, SromSegment) 3336 obj_assert_class(rhs, SromSegment) 3337 3338 # type 3339 if (!type_equal(get(lhs, p_type), get(rhs, p_type))) 3340 return (0) 3341 3342 # mask 3343 if (get(lhs, p_mask) != get(rhs, p_mask)) 3344 return (0) 3345 3346 # shift 3347 if (get(lhs, p_shift) != get(rhs, p_shift)) 3348 return (0) 3349 3350 # value 3351 if (get(lhs, p_value) != get(rhs, p_value)) 3352 return (0) 3353 3354 return (1) 3355} 3356 3357# Return a human-readable representation of a Segment instance 3358function segment_to_string(seg, _str, _t, _m, _s, _attrs, _attr_str) { 3359 _attrs = array_new() 3360 3361 # include type (if specified) 3362 if ((_t = get(seg, p_type)) != null) 3363 _str = (type_to_string(_t) " ") 3364 3365 # include offset 3366 _str = (_str sprintf("0x%X", get(seg, p_offset))) 3367 3368 # append list of attributes 3369 if ((_m = get(seg, p_mask)) != null) 3370 array_append(_attrs, ("&" _m)) 3371 3372 if ((_s = get(seg, p_shift)) != null) { 3373 if (_s > 0) 3374 _s = ">>" _s 3375 else 3376 _s = "<<" _s 3377 array_append(_attrs, _s) 3378 } 3379 3380 _attr_str = array_join(_attrs, ", ") 3381 obj_delete(_attrs) 3382 3383 if (_attr_str == "") 3384 return (_str) 3385 else 3386 return (_str " (" _attr_str ")") 3387} 3388 3389# return the flag definition for variable `v` 3390function gen_var_flags(v, _type, _flags, _flag, _str) 3391{ 3392 _num_flags = 0; 3393 _type = get(v, p_type) 3394 _flags = array_new() 3395 3396 # VF_PRIVATE 3397 if (get(v, p_access) == VAccessPrivate) 3398 array_append(_flags, VFlagPrivate) 3399 3400 # VF_IGNALL1 3401 if (get(v, p_ignall1)) 3402 array_append(_flags, VFlagIgnoreAll1) 3403 3404 # If empty, return empty flag value 3405 if (array_size(_flags) == 0) { 3406 obj_delete(_flags) 3407 return ("0") 3408 } 3409 3410 # Join all flag constants with | 3411 _str = array_join(_flags, "|", class_get_prop_id(VFlag, p_const)) 3412 3413 # Clean up 3414 obj_delete(_flags) 3415 3416 return (_str) 3417} 3418 3419# 3420# Return the absolute value 3421# 3422function abs(i) { 3423 return (i < 0 ? -i : i) 3424} 3425 3426# 3427# Return the minimum of two values 3428# 3429function min(lhs, rhs) { 3430 return (lhs < rhs ? lhs : rhs) 3431} 3432 3433# 3434# Return the maximum of two values 3435# 3436function max(lhs, rhs) { 3437 return (lhs > rhs ? lhs : rhs) 3438} 3439 3440# 3441# Parse a hex string 3442# 3443function parse_hex_string(str, _hex_pstate, _out, _p, _count) { 3444 if (!AWK_REQ_HEX_PARSING) 3445 return (str + 0) 3446 3447 # Populate hex parsing lookup table on-demand 3448 if (!("F" in _g_hex_table)) { 3449 for (_p = 0; _p < 16; _p++) { 3450 _g_hex_table[sprintf("%X", _p)] = _p 3451 _g_hex_table[sprintf("%x", _p)] = _p 3452 } 3453 } 3454 3455 # Split input into an array 3456 _count = split(toupper(str), _hex_pstate, "") 3457 _p = 1 3458 3459 # Skip leading '0x' 3460 if (_count >= 2 && _hex_pstate[1] == "0") { 3461 if (_hex_pstate[2] == "x" || _hex_pstate[2] == "X") 3462 _p += 2 3463 } 3464 3465 # Parse the hex_digits 3466 _out = 0 3467 for (; _p <= _count; _p++) 3468 _out = (_out * 16) + _g_hex_table[_hex_pstate[_p]] 3469 3470 return (_out) 3471} 3472 3473# 3474# Return the integer representation of an unsigned decimal, hexadecimal, or 3475# octal string 3476# 3477function parse_uint_string(str) { 3478 if (str ~ UINT_REGEX) 3479 return (int(str)) 3480 else if (str ~ HEX_REGEX) 3481 return (parse_hex_string(str)) 3482 else 3483 error("invalid integer value: '" str "'") 3484} 3485 3486# 3487# Parse an offset string, stripping any leading '+' or trailing ':' or ',' 3488# characters 3489# 3490# +0x0: 3491# 0x0, 3492# ... 3493# 3494function parse_uint_offset(str) { 3495 # Drop any leading '+' 3496 sub(/^\+/, "", str) 3497 3498 # Drop any trailing ':', ',', or '|' 3499 sub("[,|:]$", "", str) 3500 3501 # Parse the cleaned up string 3502 return (parse_uint_string(str)) 3503} 3504 3505# 3506# Print msg to output file, without indentation 3507# 3508function emit_ni(msg) { 3509 printf("%s", msg) >> OUTPUT_FILE 3510} 3511 3512# 3513# Print msg to output file, indented for the current `output_depth` 3514# 3515function emit(msg, _ind) { 3516 for (_ind = 0; _ind < output_depth; _ind++) 3517 emit_ni("\t") 3518 3519 emit_ni(msg) 3520} 3521 3522# 3523# Print a warning to stderr 3524# 3525function warn(msg) { 3526 print "warning:", msg, "at", FILENAME, "line", NR > "/dev/stderr" 3527} 3528 3529# 3530# Print an warning message without including the source line information 3531# 3532function warnx(msg) { 3533 print "warning:", msg > "/dev/stderr" 3534} 3535 3536# 3537# Print a compiler error to stderr with a caller supplied 3538# line number 3539# 3540function errorc(line, msg) { 3541 errorx(msg " at " FILENAME " line " line) 3542} 3543 3544# 3545# Print a compiler error to stderr 3546# 3547function error(msg) { 3548 errorx(msg " at " FILENAME " line " NR ":\n\t" $0) 3549} 3550 3551# 3552# Print an error message without including the source line information 3553# 3554function errorx(msg) { 3555 print "error:", msg > "/dev/stderr" 3556 _EARLY_EXIT=1 3557 exit 1 3558} 3559 3560# 3561# Print a debug output message 3562# 3563function debug(msg, _i) { 3564 if (!DEBUG) 3565 return 3566 for (_i = 1; _i < _g_parse_stack_depth; _i++) 3567 printf("\t") > "/dev/stderr" 3568 print msg > "/dev/stderr" 3569} 3570 3571# 3572# Advance to the next non-comment input record 3573# 3574function next_line(_result) { 3575 do { 3576 _result = getline 3577 } while (_result > 0 && $0 ~ /^[ \t]*#.*/) # skip comment lines 3578 return (_result) 3579} 3580 3581# 3582# Advance to the next input record and verify that it matches @p regex 3583# 3584function getline_matching(regex, _result) { 3585 _result = next_line() 3586 if (_result <= 0) 3587 return (_result) 3588 3589 if ($0 ~ regex) 3590 return (1) 3591 3592 return (-1) 3593} 3594 3595# 3596# Shift the current fields left by `n`. 3597# 3598# If all fields are consumed and the optional do_getline argument is true, 3599# read the next line. 3600# 3601function shiftf(n, do_getline, _i) { 3602 if (n > NF) 3603 error("shift past end of line") 3604 3605 if (n == NF) { 3606 # If shifting the entire line, just reset the line value 3607 $0 = "" 3608 } else { 3609 for (_i = 1; _i <= NF-n; _i++) { 3610 $(_i) = $(_i+n) 3611 } 3612 NF = NF - n 3613 } 3614 3615 if (NF == 0 && do_getline) 3616 next_line() 3617} 3618 3619# Push a new parser state. 3620function parser_state_push(ctx, is_block, _state) { 3621 _state = obj_new(ParseState) 3622 set(_state, p_ctx, ctx) 3623 set(_state, p_is_block, is_block) 3624 set(_state, p_line, NR) 3625 3626 _g_parse_stack_depth++ 3627 _g_parse_stack[_g_parse_stack_depth] = _state 3628} 3629 3630# Fetch the current parser state 3631function parser_state_get() { 3632 if (_g_parse_stack_depth == 0) 3633 errorx("parser_state_get() called with empty parse stack") 3634 3635 return (_g_parse_stack[_g_parse_stack_depth]) 3636} 3637 3638# Pop the current parser state 3639function parser_state_pop(_block_state, _closes_block) { 3640 if (_g_parse_stack_depth == 0) 3641 errorx("parser_state_pop() called with empty parse stack") 3642 3643 _closes_block = get(parser_state_get(), p_is_block) 3644 3645 delete _g_parse_stack[_g_parse_stack_depth] 3646 _g_parse_stack_depth-- 3647 3648 if (_closes_block) 3649 debug("}") 3650} 3651 3652# Fetch the current context object associated with this parser state 3653# The object will be asserted as being an instance of the given class. 3654function parser_state_get_context(class, _ctx_obj) { 3655 _ctx_obj = get(parser_state_get(), p_ctx) 3656 obj_assert_class(_ctx_obj, class) 3657 3658 return (_ctx_obj) 3659} 3660 3661# Walk the parser state stack until a context object of the given class 3662# is found. If the top of the stack is reached without finding a context object 3663# of the requested type, an error will be thrown. 3664function parser_state_find_context(class, _state, _ctx, _i) { 3665 if (class == null) 3666 errorx("parser_state_find_context() called with null class") 3667 3668 # Find the first context instance inheriting from `class` 3669 for (_i = 0; _i < _g_parse_stack_depth; _i++) { 3670 _state = _g_parse_stack[_g_parse_stack_depth - _i] 3671 _ctx = get(_state, p_ctx) 3672 3673 # Check for match 3674 if (obj_is_instanceof(_ctx, class)) 3675 return (_ctx) 3676 } 3677 3678 # Not found 3679 errorx("no context instance of type '" class_get_name(class) "' " \ 3680 "found in parse stack") 3681} 3682 3683# 3684# Find opening brace and push a new parser state for a brace-delimited block. 3685# 3686function parser_state_open_block(ctx) { 3687 if ($0 ~ "{" || getline_matching("^[ \t]*{") > 0) { 3688 parser_state_push(ctx, 1) 3689 sub("^[^{]*{", "", $0) 3690 return 3691 } 3692 3693 error("found '"$1 "' instead of expected '{'") 3694} 3695 3696# 3697# Find closing brace and pop parser states until the first 3698# brace-delimited block is discarded. 3699# 3700function parser_state_close_block(_next_state, _found_block) { 3701 if ($0 !~ "}") 3702 error("internal error - no closing brace") 3703 3704 # pop states until we exit the first enclosing block 3705 do { 3706 _next_state = parser_state_get() 3707 _found_block = get(_next_state, p_is_block) 3708 parser_state_pop() 3709 } while (!_found_block) 3710 3711 # strip everything prior to the block closure 3712 sub("^[^}]*}", "", $0) 3713} 3714 3715# Evaluates to true if the current parser state is defined with a context of 3716# the given class 3717function in_parser_context(class, _ctx) { 3718 if (class == null) 3719 errorx("called in_parser_context() with null class") 3720 3721 _ctx = get(parser_state_get(), p_ctx) 3722 return (obj_is_instanceof(_ctx, class)) 3723} 3724 3725# 3726# Parse and return a revision range from the current line. 3727# 3728# 4 3729# 4-10 # revisions 4-10, inclusive 3730# > 4 3731# < 4 3732# >= 4 3733# <= 4 3734# 3735function parse_revrange(_start, _end, _robj) { 3736 _start = 0 3737 _end = 0 3738 3739 if ($2 ~ "[0-9]*-[0-9*]") { 3740 split($2, _g_rev_range, "[ \t]*-[ \t]*") 3741 _start = int(_g_rev_range[1]) 3742 _end = int(_g_rev_range[2]) 3743 } else if ($2 ~ "(>|>=|<|<=)" && $3 ~ "[1-9][0-9]*") { 3744 if ($2 == ">") { 3745 _start = int($3)+1 3746 _end = REV_MAX 3747 } else if ($2 == ">=") { 3748 _start = int($3) 3749 _end = REV_MAX 3750 } else if ($2 == "<" && int($3) > 0) { 3751 _start = 0 3752 _end = int($3)-1 3753 } else if ($2 == "<=") { 3754 _start = 0 3755 _end = int($3)-1 3756 } else { 3757 error("invalid revision descriptor") 3758 } 3759 } else if ($2 ~ "[1-9][0-9]*") { 3760 _start = int($2) 3761 _end = int($2) 3762 } else { 3763 error("invalid revision descriptor") 3764 } 3765 3766 return (revrange_new(_start, _end)) 3767} 3768 3769# 3770# Parse a variable group block starting at the current line 3771# 3772# group "Group Name" { 3773# u8 var_name[10] { 3774# ... 3775# } 3776# ... 3777# } 3778# 3779function parse_variable_group(_ctx, _groups, _group, _group_name) { 3780 _ctx = parser_state_get_context(NVRAM) 3781 3782 # Seek to the start of the name string 3783 shiftf(1) 3784 3785 # Parse the first line 3786 _group_name = stringconstant_parse_line($0) 3787 3788 # Incrementally parse line continuations 3789 while (get(_group_name, p_continued)) { 3790 getline 3791 stringconstant_append_line(_group_name, $0) 3792 } 3793 3794 debug("group \"" get(_group_name, p_value) "\" {") 3795 3796 # Register the new variable group 3797 _groups = get(_ctx, p_var_groups) 3798 _group = var_group_new(_group_name) 3799 array_append(_groups, _group) 3800 3801 # Push our variable group block 3802 parser_state_open_block(_group) 3803} 3804 3805 3806# 3807# Parse a variable definition block starting at the current line 3808# 3809# u8 var_name[10] { 3810# all1 ignore 3811# desc ... 3812# } 3813# 3814function parse_variable_defn(_ctx, _vaccess, _type, _name, _fmt, _var, 3815 _var_list) 3816{ 3817 _ctx = parser_state_get_context(SymbolContext) 3818 3819 # Check for access modifier 3820 if ($1 == "private") { 3821 _vaccess = VAccessPrivate 3822 shiftf(1) 3823 } else if ($1 == "internal") { 3824 _vaccess = VAccessInternal 3825 shiftf(1) 3826 } else { 3827 _vaccess = VAccessPublic 3828 } 3829 3830 # Find the base type 3831 if ((_type = type_named($1)) == null) 3832 error("unknown type '" $1 "'") 3833 3834 # Parse (and trim) any array specifier from the variable name 3835 _name = $2 3836 _type = parse_array_type_specifier(_name, _type) 3837 sub(ARRAY_REGEX"$", "", _name) 3838 3839 # Look for an existing variable definition 3840 if (_name in _g_var_names) { 3841 error("variable identifier '" _name "' previously defined at " \ 3842 "line " get(_g_var_names[_name], p_line)) 3843 } 3844 3845 # Construct new variable instance 3846 _var = var_new(_vaccess, _name, _type) 3847 debug((_private ? "private " : "") type_to_string(_type) " " _name " {") 3848 3849 # Register in global name table 3850 _g_var_names[_name] = _var 3851 3852 # Add to our parent context 3853 _var_list = get(_ctx, p_vars) 3854 array_append(_var_list, _var) 3855 3856 # Push our variable definition block 3857 parser_state_open_block(_var) 3858} 3859 3860 3861# 3862# Return a string containing the human-readable list of valid Fmt names 3863# 3864function fmt_get_human_readable_list(_result, _fmts, _fmt, _nfmts, _i) 3865{ 3866 # Build up a string listing the valid formats 3867 _fmts = map_to_array(ValueFormats) 3868 _result = "" 3869 3870 _nfmts = array_size(_fmts) 3871 for (_i = 0; _i < _nfmts; _i++) { 3872 _fmt = array_get(_fmts, _i) 3873 if (_i+1 == _nfmts) 3874 _result = _result "or " 3875 3876 _result = _name_str \ 3877 "'" get(_fmt, p_name) "'" 3878 3879 if (_i+1 < _nfmts) 3880 _result = _result ", " 3881 } 3882 3883 obj_delete(_fmts) 3884 return (_result) 3885} 3886 3887# 3888# Parse a variable parameter from the current line 3889# 3890# fmt (decimal|hex|macaddr|...) 3891# all1 ignore 3892# desc "quoted string" 3893# help "quoted string" 3894# 3895function parse_variable_param(param_name, _var, _vprops, _prop_id, _pval) { 3896 _var = parser_state_get_context(Var) 3897 3898 if (param_name == "fmt") { 3899 debug($1 " " $2) 3900 3901 # Check for an existing definition 3902 if ((_pval = get(_var, p_fmt)) != null) { 3903 error("fmt previously specified on line " \ 3904 obj_get_prop_nr(_var, p_fmt)) 3905 } 3906 3907 # Validate arguments 3908 if (NF != 2) { 3909 error("'" $1 "' requires a single parameter value of " \ 3910 fmt_get_human_readable_list()) 3911 } 3912 3913 if ((_pval = fmt_named($2)) == null) { 3914 error("'" $1 "' value '" $2 "' unrecognized. Must be " \ 3915 "one of " fmt_get_human_readable_list()) 3916 } 3917 3918 # Set fmt reference 3919 set(_var, p_fmt, _pval) 3920 } else if (param_name == "all1") { 3921 debug($1 " " $2) 3922 3923 # Check for an existing definition 3924 if ((_pval = get(_var, p_ignall1)) != null) { 3925 error("all1 previously specified on line " \ 3926 obj_get_prop_nr(_var, p_ignall1)) 3927 } 3928 3929 # Check argument 3930 if (NF != 2) 3931 error("'" $1 "'requires a single 'ignore' argument") 3932 else if ($2 != "ignore") 3933 error("unknown "$1" value '"$2"', expected 'ignore'") 3934 3935 # Set variable property 3936 set(_var, p_ignall1, 1) 3937 } else if (param_name == "desc" || param_name == "help") { 3938 # Fetch an indirect property reference for either the 'desc' 3939 # or 'help' property 3940 _prop_id = obj_get_named_prop_id(_var, param_name) 3941 3942 # Check for an existing definition 3943 if ((_pval = prop_get(_var, _prop_id)) != null) { 3944 error(get(_var, p_name) " '" $1 "' redefined " \ 3945 "(previously defined on line " \ 3946 obj_get_prop_id_nr(_var, _prop_id) ")") 3947 } 3948 3949 # Seek to the start of the desc/help string 3950 shiftf(1) 3951 3952 # Parse the first line 3953 _pval = stringconstant_parse_line($0) 3954 3955 # Incrementally parse line continuations 3956 while (get(_pval, p_continued)) { 3957 getline 3958 stringconstant_append_line(_pval, $0) 3959 } 3960 3961 debug(param_name " \"" get(_pval, p_value) "\"") 3962 3963 # Add to the var object 3964 prop_set(_var, _prop_id, _pval) 3965 } else { 3966 error("unknown variable property type: '" param_name "'") 3967 } 3968} 3969 3970 3971# 3972# Parse a top-level SROM layout block starting at the current line 3973# 3974# srom 4-7 { 3975# 0x000: ... 3976# } 3977# 3978function parse_srom_layout(_nvram, _srom_layouts, _revs, _layout) { 3979 _nvram = parser_state_get_context(NVRAM) 3980 _srom_layouts = get(_nvram, p_srom_layouts) 3981 3982 # Parse revision descriptor and register SROM 3983 # instance 3984 _revs = parse_revrange() 3985 _layout = srom_layout_new(_revs) 3986 nvram_add_srom_layout(_nvram, _layout) 3987 3988 debug("srom " revrange_to_string(_revs) " {") 3989 3990 # Push new SROM parser state 3991 parser_state_open_block(_layout) 3992} 3993 3994 3995# 3996# Parse a nested srom range filter block starting at the current line 3997# srom 4-7 { 3998# # Filter block 3999# srom 5 { 4000# 0x000: ... 4001# } 4002# } 4003# 4004function parse_srom_layout_filter(_parent, _revs, _filter) { 4005 _parent = parser_state_get_context(SromLayout) 4006 4007 # Parse revision descriptor 4008 _revs = parse_revrange() 4009 4010 # Construct the filter (which also validates the revision range) 4011 _filter = srom_layout_filter_new(_parent, _revs) 4012 4013 debug("srom " revrange_to_string(_revs) " {") 4014 4015 # Push new SROM parser state 4016 parser_state_open_block(_filter) 4017} 4018 4019 4020# 4021# Parse a SROM offset segment's attribute list from the current line 4022# 4023# <empty line> 4024# (&0xF0, >>4, =0x5340) 4025# () 4026# 4027# Attribute designators: 4028# &0xF Mask value with 0xF 4029# <<4 Shift left 4 bits 4030# >>4 Shift right 4 bits 4031# =0x53 The parsed value must be equal to this constant value 4032# 4033# May be followed by a | indicating that this segment should be OR'd with the 4034# segment that follows, or a terminating , indicating that a new offset's 4035# list of segments may follow. 4036# 4037function parse_srom_segment_attributes(offset, type, _attrs, _num_attr, _attr, 4038 _mask, _shift, _value, _i) 4039{ 4040 # seek to offset (attributes...) or end of the offset expr (|,) 4041 sub("^[^,(|){}]+", "", $0) 4042 4043 # defaults 4044 _mask = type_get_default_mask(type) 4045 _shift = 0 4046 4047 # parse attributes 4048 if ($1 ~ "^\\(") { 4049 # extract attribute list 4050 if (match($0, /\([^|\(\)]*\)/) <= 0) 4051 error("expected attribute list") 4052 4053 _attrs = substr($0, RSTART+1, RLENGTH-2) 4054 4055 # drop attribute list from the input line 4056 $0 = substr($0, RSTART+RLENGTH, length($0) - RSTART+RLENGTH) 4057 4058 # parse attributes 4059 _num_attr = split(_attrs, _g_attrs, ",[ \t]*") 4060 for (_i = 1; _i <= _num_attr; _i++) { 4061 _attr = _g_attrs[_i] 4062 4063 if (sub("^&[ \t]*", "", _attr) > 0) { 4064 _mask = parse_uint_string(_attr) 4065 } else if (sub("^<<[ \t]*", "", _attr) > 0) { 4066 _shift = - parse_uint_string(_attr) 4067 } else if (sub("^>>[ \t]*", "", _attr) > 0) { 4068 _shift = parse_uint_string(_attr) 4069 } else if (sub("^=[ \t]*", "", _attr) > 0) { 4070 _value = _attr 4071 } else { 4072 error("unknown attribute '" _attr "'") 4073 } 4074 } 4075 } 4076 4077 return (srom_segment_new(offset, type, _mask, _shift, _value)) 4078} 4079 4080# 4081# Parse a SROM offset's segment declaration from the current line 4082# 4083# +0x0: u8 (&0xF0, >>4) # read 8 bits at +0x0 (relative to srom entry 4084# # offset, apply 0xF0 mask, shift >> 4 4085# 0x10: u8 (&0xF0, >>4) # identical to above, but perform the read at 4086# # absolute offset 0x10 4087# 4088# +0x0: u8 # no attributes 4089# 0x10: u8 4090# 4091# +0x0 # simplified forms denoted by lack of ':'; the 4092# 0x0 # type is inherited from the parent SromEntry 4093# 4094# 4095function parse_srom_segment(base_offset, base_type, _simple, _type, _type_str, 4096 _offset, _attrs, _num_attr, _attr, _mask, _shift, _off_desc) 4097{ 4098 # Fetch the offset value 4099 _offset = $1 4100 4101 # Offset string must be one of: 4102 # simplified entry: <offset|+reloff> 4103 # Provides only the offset, with the type inherited 4104 # from the original variable definition 4105 # standard entry: <offset|+reloff>: 4106 # Provides the offset, followed by a type 4107 # 4108 # We differentiate the two by looking for (and simultaneously removing) 4109 # the trailing ':' 4110 if (!sub(/:$/, "", _offset)) 4111 _simple = 1 4112 4113 # The offset may either be absolute (e.g. 0x180) or relative (e.g. 4114 # +0x01). 4115 # 4116 # If we find a relative offset definition, we must trim the leading '+' 4117 # and then add the base offset 4118 if (sub(/^\+/, "", _offset)) { 4119 _offset = base_offset + parse_uint_offset(_offset) 4120 } else { 4121 4122 _offset = parse_uint_offset(_offset) 4123 } 4124 4125 # If simplified form, use the base type of the SROM entry. Otherwise, 4126 # we need to parse the type. 4127 if (_simple) { 4128 _type = base_type 4129 } else { 4130 _type_str = $2 4131 sub(/,$/, "", _type_str) # trim trailing ',', if any 4132 4133 if ((_type = parse_type_string(_type_str)) == null) 4134 error("unknown type '" _type_str "'") 4135 } 4136 4137 # Parse the trailing (... attributes ...), if any 4138 return (parse_srom_segment_attributes(_offset, _type)) 4139} 4140 4141# 4142# Parse a SROM variable entry from the current line 4143# <offset>: <type> <varname><array spec> ... 4144# 4145function parse_srom_variable_entry(_srom, _srom_revs, _rev_start, _rev_end, 4146 _srom_entries, _srom_revmap, _prev_entry, _ctx, _base_offset, _name, 4147 _stype, _var, _entry, _offset, _seg, _i) 4148{ 4149 # Fetch our parent context 4150 _ctx = parser_state_get_context(SromContext) 4151 _srom_revs = get(_ctx, p_revisions) 4152 _rev_start = get(_srom_revs, p_start) 4153 _rev_end = get(_srom_revs, p_end) 4154 4155 # Locate our enclosing layout 4156 _srom = parser_state_find_context(SromLayout) 4157 _srom_entries = get(_srom, p_entries) 4158 _srom_revmap = get(_srom, p_revmap) 4159 4160 # Verify argument count 4161 if (NF < 3) { 4162 error("unrecognized srom entry syntax; must specify at " \ 4163 "least \"<offset>: <type> <variable name>\"") 4164 } 4165 4166 # Parse the base offset 4167 _base_offset = parse_uint_offset($1) 4168 4169 # Parse the base type 4170 if ((_stype = type_named($2)) == null) 4171 error("unknown type '" $2 "'") 4172 4173 # Parse (and trim) any array specifier from the variable name 4174 _name = $3 4175 _stype = parse_array_type_specifier(_name, _stype) 4176 sub(ARRAY_REGEX"$", "", _name) 4177 4178 # Locate the variable definition 4179 if (!(_name in _g_var_names)) 4180 error("no definition found for variable '" _name "'") 4181 _var = _g_var_names[_name] 4182 4183 # The SROM entry type must be a subtype of the variable's declared 4184 # type 4185 if (!type_can_represent(get(_var, p_type), _stype)) { 4186 error("'" type_to_string(_stype) "' SROM value cannot be " \ 4187 "coerced to '" type_to_string(get(_var, p_type)) " " _name \ 4188 "' variable") 4189 } 4190 4191 # Create and register our new offset entry 4192 _entry = srom_entry_new(_var, _srom_revs, _base_offset, _stype) 4193 srom_layout_add_entry(_srom, _entry) 4194 4195 # Seek to either the block start ('{'), or the attributes to be 4196 # used for a single offset/segment entry at `offset` 4197 shiftf(3) 4198 4199 # Using the block syntax? */ 4200 if ($1 == "{") { 4201 debug(sprintf("0x%03x: %s %s {", _base_offset, 4202 type_to_string(_stype), _name)) 4203 parser_state_open_block(_entry) 4204 } else { 4205 # Otherwise, we're using the simplified syntax -- create and 4206 # register our implicit SromOffset 4207 _offset = srom_offset_new() 4208 array_append(get(_entry, p_offsets), _offset) 4209 4210 # Parse and register simplified segment syntax 4211 _seg = parse_srom_segment_attributes(_base_offset, _stype) 4212 array_append(get(_offset, p_segments), _seg) 4213 4214 debug(sprintf("0x%03x: %s %s { %s }", _base_offset, 4215 type_to_string(_stype), _name, segment_to_string(_seg))) 4216 } 4217} 4218 4219# 4220# Parse all SromSegment entry segments readable starting at the current line 4221# 4222# <offset|+reloff>[,|]? 4223# <offset|+reloff>: <type>[,|]? 4224# <offset|+reloff>: <type> (<attributes>)[,|]? 4225# 4226function parse_srom_entry_segments(_entry, _base_off, _base_type, _offs, 4227 _offset, _segs, _seg, _more_seg, _more_vals) 4228{ 4229 _entry = parser_state_get_context(SromEntry) 4230 _base_off = get(_entry, p_base_offset) 4231 _offs = get(_entry, p_offsets) 4232 4233 _base_type = get(_entry, p_type) 4234 _base_type = type_get_base(_base_type) 4235 4236 # Parse all offsets 4237 do { 4238 # Create a SromOffset 4239 _offset = srom_offset_new() 4240 _segs = get(_offset, p_segments) 4241 4242 array_append(_offs, _offset) 4243 4244 # Parse all segments 4245 do { 4246 _seg = parse_srom_segment(_base_off, _base_type) 4247 array_append(_segs, _seg) 4248 4249 # Do more segments follow? 4250 _more_seg = ($1 == "|") 4251 if (_more_seg) 4252 shiftf(1, 1) 4253 4254 if (_more_seg) 4255 debug(segment_to_string(_seg) " |") 4256 else 4257 debug(segment_to_string(_seg)) 4258 } while (_more_seg) 4259 4260 # Do more offsets follow? 4261 _more_vals = ($1 == ",") 4262 if (_more_vals) 4263 shiftf(1, 1) 4264 } while (_more_vals) 4265} 4266