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