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