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