├── .editorconfig ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml ├── ipas_default.config └── workflows │ ├── bandit.yml │ ├── codeql.yml │ ├── ossf_scorecard.yml │ ├── publish-to-test-pypi.yml │ ├── pull-request-analytics.yml │ ├── python-package.yml │ ├── python-publish.yml │ └── test-execution.yml ├── .gitignore ├── AUTHORS.rst ├── CONTRIBUTING.md ├── HISTORY.rst ├── LICENSE ├── LICENSE_THIRD_PARTY ├── MANIFEST.in ├── Makefile ├── README.md ├── SECURITY.md ├── __init__.py ├── _config.yml ├── docs ├── Makefile ├── _config.yml ├── authors.rst ├── conf.py ├── contributing.rst ├── history.rst ├── index.rst ├── make.bat ├── readme.rst ├── usage.rst └── user_guide │ ├── compare_setup_knobs.md │ ├── installation.md │ ├── log_configuration.md │ └── uefi_binary_parsing.md ├── examples ├── BiosKnobs.ini ├── __init__.py ├── automate_program_knobs.py └── setup_modification.py ├── parse_bin.py ├── poetry.lock ├── pyproject.toml ├── requirements.txt ├── setup.cfg ├── setup.py ├── src └── xmlcli │ ├── UefiFwParser.py │ ├── XmlCli.py │ ├── XmlCliLib.py │ ├── XmlIniParser.py │ ├── __init__.py │ ├── _version.py │ ├── access │ ├── __init__.py │ ├── base │ │ ├── README.md │ │ ├── __init__.py │ │ ├── base.ini │ │ ├── base.py │ │ └── baseTest.py │ ├── generic │ │ ├── README.md │ │ ├── __init__.py │ │ ├── generic.ini │ │ ├── generic.py │ │ └── genericTest.py │ ├── linux │ │ ├── Makefile │ │ ├── README.md │ │ ├── __init__.py │ │ ├── libmem.lso │ │ ├── libport.lso │ │ ├── linux.ini │ │ ├── linux.py │ │ ├── linuxTest.py │ │ ├── mymem.c │ │ └── port.c │ ├── stub │ │ ├── README.md │ │ ├── __init__.py │ │ ├── stub.ini │ │ ├── stub.py │ │ └── stubTest.py │ └── winrwe │ │ ├── README.md │ │ ├── __init__.py │ │ ├── winrwe.ini │ │ ├── winrwe.py │ │ └── winrweTest.py │ ├── cfg │ └── BiosKnobs.ini │ ├── common │ ├── __init__.py │ ├── bios_fw_parser.py │ ├── compress.py │ ├── configurations.py │ ├── logger.py │ ├── structure.py │ ├── uefi_nvar.py │ └── utils.py │ ├── messages.json │ ├── modules │ ├── __init__.py │ ├── eBiosSetupPage │ │ ├── README.md │ │ ├── __init__.py │ │ └── eBiosSetupPage.py │ ├── helpers.py │ └── winContextMenu │ │ ├── README.md │ │ ├── XmlCli-square.ico │ │ ├── XmlCli-square.png │ │ ├── install_context_menu.py │ │ └── xmlcli_registry_listener.py │ ├── out │ └── .gitignore │ ├── start_xmlcli.py │ ├── tools │ ├── EnableXmlCli.cp310-win_amd64.pyd │ ├── EnableXmlCli.cp311-win_amd64.pyd │ ├── EnableXmlCli.cp37-win_amd64.pyd │ ├── EnableXmlCli.cp38-win_amd64.pyd │ ├── EnableXmlCli.cp39-win_amd64.pyd │ ├── EnableXmlCli.cpython-310-x86_64-linux-gnu.so │ ├── EnableXmlCli.cpython-311-x86_64-linux-gnu.so │ ├── EnableXmlCli.cpython-37m-x86_64-linux-gnu.so │ ├── EnableXmlCli.cpython-38-x86_64-linux-gnu.so │ ├── EnableXmlCli.cpython-39-x86_64-linux-gnu.so │ ├── TianoCompress │ ├── TianoCompress.exe │ └── __init__.py │ └── xmlcli.config ├── start_xmlcli.bat ├── start_xmlcli.sh ├── test_requirements.txt ├── tests ├── CommonTests.py ├── UefiParserTest.py ├── UnitTestHelper.py ├── XmlCliTest.py ├── __init__.py ├── bandit_scan.bat ├── conftest.py └── tests.config └── tox.ini /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence, 6 | # @global-owner1 and @global-owner2 will be requested for 7 | # review when someone opens a pull request. 8 | * @gahan9 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Platform Information (please complete the following information):** 27 | - OS: [Win/Linux/VMWare/...] 28 | - Interface used [linux/uefi/...] 29 | - BIOS version label 30 | - Source of the BIOS/IFWI image 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | All applicable below generated files: 35 | - logs 36 | - PlatformConfig.xml 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement, help wanted, question 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | groups: 13 | all-github: 14 | patterns: 15 | - "*" 16 | -------------------------------------------------------------------------------- /.github/ipas_default.config: -------------------------------------------------------------------------------- 1 | 2 | ### Bandit config file generated from: 3 | # './bandit/bandit/cli/config_generator.py --out ipas_default.config' 4 | 5 | ### This config may optionally select a subset of tests to run or skip by 6 | ### filling out the 'tests' and 'skips' lists given below. If no tests are 7 | ### specified for inclusion then it is assumed all tests are desired. The skips 8 | ### set will remove specific tests from the include set. This can be controlled 9 | ### using the -t/-s CLI options. Note that the same test ID should not appear 10 | ### in both 'tests' and 'skips', this would be nonsensical and is detected by 11 | ### Bandit at runtime. 12 | 13 | # Available tests: 14 | # B101 : assert_used 15 | # B102 : exec_used 16 | # B103 : set_bad_file_permissions 17 | # B104 : hardcoded_bind_all_interfaces 18 | # B105 : hardcoded_password_string 19 | # B106 : hardcoded_password_funcarg 20 | # B107 : hardcoded_password_default 21 | # B108 : hardcoded_tmp_directory 22 | # B110 : try_except_pass 23 | # B112 : try_except_continue 24 | # B201 : flask_debug_true 25 | # B301 : pickle 26 | # B302 : marshal 27 | # B303 : md5 28 | # B304 : ciphers 29 | # B305 : cipher_modes 30 | # B306 : mktemp_q 31 | # B307 : eval 32 | # B308 : mark_safe 33 | # B309 : httpsconnection 34 | # B310 : urllib_urlopen 35 | # B311 : random 36 | # B312 : telnetlib 37 | # B313 : xml_bad_cElementTree 38 | # B314 : xml_bad_ElementTree 39 | # B315 : xml_bad_expatreader 40 | # B316 : xml_bad_expatbuilder 41 | # B317 : xml_bad_sax 42 | # B318 : xml_bad_minidom 43 | # B319 : xml_bad_pulldom 44 | # B320 : xml_bad_etree 45 | # B321 : ftplib 46 | # B323 : unverified_context 47 | # B324 : hashlib_new_insecure_functions 48 | # B325 : tempnam 49 | # B401 : import_telnetlib 50 | # B402 : import_ftplib 51 | # B403 : import_pickle 52 | # B404 : import_subprocess 53 | # B405 : import_xml_etree 54 | # B406 : import_xml_sax 55 | # B407 : import_xml_expat 56 | # B408 : import_xml_minidom 57 | # B409 : import_xml_pulldom 58 | # B410 : import_lxml 59 | # B411 : import_xmlrpclib 60 | # B412 : import_httpoxy 61 | # B413 : import_pycrypto 62 | # B501 : request_with_no_cert_validation 63 | # B502 : ssl_with_bad_version 64 | # B503 : ssl_with_bad_defaults 65 | # B504 : ssl_with_no_version 66 | # B505 : weak_cryptographic_key 67 | # B506 : yaml_load 68 | # B507 : ssh_no_host_key_verification 69 | # B601 : paramiko_calls 70 | # B602 : subprocess_popen_with_shell_equals_true 71 | # B603 : subprocess_without_shell_equals_true 72 | # B604 : any_other_function_with_shell_equals_true 73 | # B605 : start_process_with_a_shell 74 | # B606 : start_process_with_no_shell 75 | # B607 : start_process_with_partial_path 76 | # B608 : hardcoded_sql_expressions 77 | # B609 : linux_commands_wildcard_injection 78 | # B610 : django_extra_used 79 | # B611 : django_rawsql_used 80 | # B701 : jinja2_autoescape_false 81 | # B702 : use_of_mako_templates 82 | # B703 : django_mark_safe 83 | 84 | # (optional) list included test IDs here, eg '[B101, B406]': 85 | # IPAS Required Checkers. Do not disable these 86 | # Additional checkers may be added if desired 87 | tests: 88 | [ 'B301', 'B302', 'B303', 'B304', 'B305', 'B306', 'B308', 'B309', 'B310', 'B311', 'B312', 'B313', 'B314', 'B315', 'B316', 'B317', 'B318', 'B319', 'B320', 'B321', 'B323', 'B324', 'B325', 'B401', 'B402', 'B403', 'B404', 'B405', 'B406', 'B407', 'B408', 'B409', 'B410', 'B411', 'B412', 'B413'] 89 | 90 | # (optional) list skipped test IDs here, eg '[B101, B406]': 91 | # The following checkers are not required but be added to tests list if desired 92 | skips: 93 | [ 'B101', 'B102', 'B103', 'B104', 'B105', 'B106', 'B107', 'B108', 'B110', 'B112', 'B201', 'B501', 'B502', 'B503', 'B504', 'B505', 'B506', 'B507', 'B601', 'B602', 'B603', 'B604', 'B605', 'B606', 'B607', 'B608', 'B609', 'B610', 'B611', 'B701', 'B702', 'B703'] 94 | 95 | ### (optional) plugin settings - some test plugins require configuration data 96 | ### that may be given here, per-plugin. All bandit test plugins have a built in 97 | ### set of sensible defaults and these will be used if no configuration is 98 | ### provided. It is not necessary to provide settings for every (or any) plugin 99 | ### if the defaults are acceptable. 100 | 101 | any_other_function_with_shell_equals_true: 102 | no_shell: 103 | - os.execl 104 | - os.execle 105 | - os.execlp 106 | - os.execlpe 107 | - os.execv 108 | - os.execve 109 | - os.execvp 110 | - os.execvpe 111 | - os.spawnl 112 | - os.spawnle 113 | - os.spawnlp 114 | - os.spawnlpe 115 | - os.spawnv 116 | - os.spawnve 117 | - os.spawnvp 118 | - os.spawnvpe 119 | - os.startfile 120 | shell: 121 | - os.system 122 | - os.popen 123 | - os.popen2 124 | - os.popen3 125 | - os.popen4 126 | - popen2.popen2 127 | - popen2.popen3 128 | - popen2.popen4 129 | - popen2.Popen3 130 | - popen2.Popen4 131 | - commands.getoutput 132 | - commands.getstatusoutput 133 | subprocess: 134 | - subprocess.Popen 135 | - subprocess.call 136 | - subprocess.check_call 137 | - subprocess.check_output 138 | - subprocess.run 139 | assert_used: 140 | skips: [] 141 | hardcoded_tmp_directory: 142 | tmp_dirs: 143 | - /tmp 144 | - /var/tmp 145 | - /dev/shm 146 | linux_commands_wildcard_injection: 147 | no_shell: 148 | - os.execl 149 | - os.execle 150 | - os.execlp 151 | - os.execlpe 152 | - os.execv 153 | - os.execve 154 | - os.execvp 155 | - os.execvpe 156 | - os.spawnl 157 | - os.spawnle 158 | - os.spawnlp 159 | - os.spawnlpe 160 | - os.spawnv 161 | - os.spawnve 162 | - os.spawnvp 163 | - os.spawnvpe 164 | - os.startfile 165 | shell: 166 | - os.system 167 | - os.popen 168 | - os.popen2 169 | - os.popen3 170 | - os.popen4 171 | - popen2.popen2 172 | - popen2.popen3 173 | - popen2.popen4 174 | - popen2.Popen3 175 | - popen2.Popen4 176 | - commands.getoutput 177 | - commands.getstatusoutput 178 | subprocess: 179 | - subprocess.Popen 180 | - subprocess.call 181 | - subprocess.check_call 182 | - subprocess.check_output 183 | - subprocess.run 184 | ssl_with_bad_defaults: 185 | bad_protocol_versions: 186 | - PROTOCOL_SSLv2 187 | - SSLv2_METHOD 188 | - SSLv23_METHOD 189 | - PROTOCOL_SSLv3 190 | - PROTOCOL_TLSv1 191 | - SSLv3_METHOD 192 | - TLSv1_METHOD 193 | ssl_with_bad_version: 194 | bad_protocol_versions: 195 | - PROTOCOL_SSLv2 196 | - SSLv2_METHOD 197 | - SSLv23_METHOD 198 | - PROTOCOL_SSLv3 199 | - PROTOCOL_TLSv1 200 | - SSLv3_METHOD 201 | - TLSv1_METHOD 202 | start_process_with_a_shell: 203 | no_shell: 204 | - os.execl 205 | - os.execle 206 | - os.execlp 207 | - os.execlpe 208 | - os.execv 209 | - os.execve 210 | - os.execvp 211 | - os.execvpe 212 | - os.spawnl 213 | - os.spawnle 214 | - os.spawnlp 215 | - os.spawnlpe 216 | - os.spawnv 217 | - os.spawnve 218 | - os.spawnvp 219 | - os.spawnvpe 220 | - os.startfile 221 | shell: 222 | - os.system 223 | - os.popen 224 | - os.popen2 225 | - os.popen3 226 | - os.popen4 227 | - popen2.popen2 228 | - popen2.popen3 229 | - popen2.popen4 230 | - popen2.Popen3 231 | - popen2.Popen4 232 | - commands.getoutput 233 | - commands.getstatusoutput 234 | subprocess: 235 | - subprocess.Popen 236 | - subprocess.call 237 | - subprocess.check_call 238 | - subprocess.check_output 239 | - subprocess.run 240 | start_process_with_no_shell: 241 | no_shell: 242 | - os.execl 243 | - os.execle 244 | - os.execlp 245 | - os.execlpe 246 | - os.execv 247 | - os.execve 248 | - os.execvp 249 | - os.execvpe 250 | - os.spawnl 251 | - os.spawnle 252 | - os.spawnlp 253 | - os.spawnlpe 254 | - os.spawnv 255 | - os.spawnve 256 | - os.spawnvp 257 | - os.spawnvpe 258 | - os.startfile 259 | shell: 260 | - os.system 261 | - os.popen 262 | - os.popen2 263 | - os.popen3 264 | - os.popen4 265 | - popen2.popen2 266 | - popen2.popen3 267 | - popen2.popen4 268 | - popen2.Popen3 269 | - popen2.Popen4 270 | - commands.getoutput 271 | - commands.getstatusoutput 272 | subprocess: 273 | - subprocess.Popen 274 | - subprocess.call 275 | - subprocess.check_call 276 | - subprocess.check_output 277 | - subprocess.run 278 | start_process_with_partial_path: 279 | no_shell: 280 | - os.execl 281 | - os.execle 282 | - os.execlp 283 | - os.execlpe 284 | - os.execv 285 | - os.execve 286 | - os.execvp 287 | - os.execvpe 288 | - os.spawnl 289 | - os.spawnle 290 | - os.spawnlp 291 | - os.spawnlpe 292 | - os.spawnv 293 | - os.spawnve 294 | - os.spawnvp 295 | - os.spawnvpe 296 | - os.startfile 297 | shell: 298 | - os.system 299 | - os.popen 300 | - os.popen2 301 | - os.popen3 302 | - os.popen4 303 | - popen2.popen2 304 | - popen2.popen3 305 | - popen2.popen4 306 | - popen2.Popen3 307 | - popen2.Popen4 308 | - commands.getoutput 309 | - commands.getstatusoutput 310 | subprocess: 311 | - subprocess.Popen 312 | - subprocess.call 313 | - subprocess.check_call 314 | - subprocess.check_output 315 | - subprocess.run 316 | subprocess_popen_with_shell_equals_true: 317 | no_shell: 318 | - os.execl 319 | - os.execle 320 | - os.execlp 321 | - os.execlpe 322 | - os.execv 323 | - os.execve 324 | - os.execvp 325 | - os.execvpe 326 | - os.spawnl 327 | - os.spawnle 328 | - os.spawnlp 329 | - os.spawnlpe 330 | - os.spawnv 331 | - os.spawnve 332 | - os.spawnvp 333 | - os.spawnvpe 334 | - os.startfile 335 | shell: 336 | - os.system 337 | - os.popen 338 | - os.popen2 339 | - os.popen3 340 | - os.popen4 341 | - popen2.popen2 342 | - popen2.popen3 343 | - popen2.popen4 344 | - popen2.Popen3 345 | - popen2.Popen4 346 | - commands.getoutput 347 | - commands.getstatusoutput 348 | subprocess: 349 | - subprocess.Popen 350 | - subprocess.call 351 | - subprocess.check_call 352 | - subprocess.check_output 353 | - subprocess.run 354 | subprocess_without_shell_equals_true: 355 | no_shell: 356 | - os.execl 357 | - os.execle 358 | - os.execlp 359 | - os.execlpe 360 | - os.execv 361 | - os.execve 362 | - os.execvp 363 | - os.execvpe 364 | - os.spawnl 365 | - os.spawnle 366 | - os.spawnlp 367 | - os.spawnlpe 368 | - os.spawnv 369 | - os.spawnve 370 | - os.spawnvp 371 | - os.spawnvpe 372 | - os.startfile 373 | shell: 374 | - os.system 375 | - os.popen 376 | - os.popen2 377 | - os.popen3 378 | - os.popen4 379 | - popen2.popen2 380 | - popen2.popen3 381 | - popen2.popen4 382 | - popen2.Popen3 383 | - popen2.Popen4 384 | - commands.getoutput 385 | - commands.getstatusoutput 386 | subprocess: 387 | - subprocess.Popen 388 | - subprocess.call 389 | - subprocess.check_call 390 | - subprocess.check_output 391 | - subprocess.run 392 | try_except_continue: 393 | check_typed_exception: false 394 | try_except_pass: 395 | check_typed_exception: false 396 | weak_cryptographic_key: 397 | weak_key_size_dsa_high: 1024 398 | weak_key_size_dsa_medium: 2048 399 | weak_key_size_ec_high: 160 400 | weak_key_size_ec_medium: 224 401 | weak_key_size_rsa_high: 1024 402 | weak_key_size_rsa_medium: 2048 403 | 404 | -------------------------------------------------------------------------------- /.github/workflows/bandit.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | # Bandit is a security linter designed to find common security issues in Python code. 7 | # This action will run Bandit on your codebase. 8 | # The results of the scan will be found under the Security tab of your repository. 9 | 10 | # https://github.com/marketplace/actions/bandit-scan is ISC licensed, by abirismyname 11 | # https://pypi.org/project/bandit/ is Apache v2.0 licensed, by PyCQA 12 | 13 | name: Bandit 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '00 18 * * 1' 22 | 23 | jobs: 24 | bandit: 25 | permissions: 26 | contents: read # for actions/checkout to fetch code 27 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 28 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 29 | 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | - name: Bandit Scan 34 | uses: shundor/python-bandit-scan@9cc5aa4a006482b8a7f91134412df6772dbda22c 35 | with: # optional arguments 36 | # exit with 0, even with results found 37 | exit_zero: true # optional, default is DEFAULT 38 | # Github token of the repository (automatically created by Github) 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information. 40 | # File or directory to run bandit on 41 | # path: # optional, default is . 42 | # Report only issues of a given severity level or higher. Can be LOW, MEDIUM or HIGH. Default is UNDEFINED (everything) 43 | # level: # optional, default is UNDEFINED 44 | # Report only issues of a given confidence level or higher. Can be LOW, MEDIUM or HIGH. Default is UNDEFINED (everything) 45 | # confidence: # optional, default is UNDEFINED 46 | # comma-separated list of paths (glob patterns supported) to exclude from scan (note that these are in addition to the excluded paths provided in the config file) (default: .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg) 47 | # excluded_paths: # optional, default is DEFAULT 48 | # comma-separated list of test IDs to skip 49 | # skips: # optional, default is DEFAULT 50 | # path to a .bandit file that supplies command line arguments 51 | ini_path: .github/ipas_default.config 52 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | workflow_dispatch: 16 | inputs: 17 | logLevel: 18 | description: 'Log level' 19 | required: true 20 | default: 'warning' 21 | type: choice 22 | options: 23 | - info 24 | - warning 25 | - debug 26 | tags: 27 | description: 'manual-trigger' 28 | required: false 29 | type: boolean 30 | push: 31 | branches: [ "*" ] 32 | pull_request: 33 | # The branches below must be a subset of the branches above 34 | branches: [ "*" ] 35 | schedule: 36 | - cron: '45 23 * * 0' 37 | 38 | # Declare default permissions as read only. 39 | permissions: read-all 40 | 41 | jobs: 42 | analyze: 43 | name: Analyze 44 | runs-on: ubuntu-latest 45 | permissions: 46 | actions: read 47 | contents: read 48 | security-events: write 49 | 50 | strategy: 51 | fail-fast: false 52 | matrix: 53 | language: [ 'cpp', 'python' ] 54 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 55 | # Use only 'java' to analyze code written in Java, Kotlin or both 56 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 57 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 58 | 59 | steps: 60 | - name: Checkout repository 61 | uses: actions/checkout@v4 62 | 63 | # Initializes the CodeQL tools for scanning. 64 | - name: Initialize CodeQL 65 | uses: github/codeql-action/init@v3.26.13 66 | with: 67 | languages: ${{ matrix.language }} 68 | # If you wish to specify custom queries, you can do so here or in a config file. 69 | # By default, queries listed here will override any specified in a config file. 70 | # Prefix the list here with "+" to use these queries and those in the config file. 71 | 72 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 73 | queries: security-extended, security-and-quality 74 | 75 | 76 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 77 | # If this step fails, then you should remove it and run the build manually (see below) 78 | - if: matrix.language == 'python' 79 | name: Autobuild 80 | uses: github/codeql-action/autobuild@v3.26.13 81 | 82 | - if: matrix.language == 'cpp' 83 | name: Build C 84 | run: | 85 | cd src/xmlcli/access/linux 86 | make 87 | 88 | # ℹ️ Command-line programs to run using the OS shell. 89 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 90 | 91 | # If the Autobuild fails above, remove it and uncomment the following three lines. 92 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 93 | 94 | # - run: | 95 | # echo "Run, Build Application using script" 96 | # ./location_of_script_within_repo/buildscript.sh 97 | 98 | - name: Perform CodeQL Analysis 99 | uses: github/codeql-action/analyze@v3.26.13 100 | with: 101 | category: "/language:${{matrix.language}}" 102 | -------------------------------------------------------------------------------- /.github/workflows/ossf_scorecard.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecard supply-chain security 6 | on: 7 | # For Branch-Protection check. Only the default branch is supported. See 8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 9 | branch_protection_rule: 10 | # To guarantee Maintained check is occasionally updated. See 11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 12 | schedule: 13 | - cron: '45 6 * * 1' 14 | push: 15 | branches: [ "main" ] 16 | 17 | # Declare default permissions as read only. 18 | permissions: read-all 19 | 20 | jobs: 21 | analysis: 22 | name: Scorecard analysis 23 | runs-on: ubuntu-latest 24 | permissions: 25 | # Needed to upload the results to code-scanning dashboard. 26 | security-events: write 27 | # Needed to publish results and get a badge (see publish_results below). 28 | id-token: write 29 | # Uncomment the permissions below if installing in a private repository. 30 | # contents: read 31 | # actions: read 32 | 33 | steps: 34 | - name: "Checkout code" 35 | uses: actions/checkout@6b42224f41ee5dfe5395e27c8b2746f1f9955030 # v3.1.0 36 | with: 37 | persist-credentials: false 38 | 39 | - name: "Run analysis" 40 | uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0 41 | with: 42 | results_file: results.sarif 43 | results_format: sarif 44 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 45 | # - you want to enable the Branch-Protection check on a *public* repository, or 46 | # - you are installing Scorecard on a *private* repository 47 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 48 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 49 | 50 | # Public repositories: 51 | # - Publish results to OpenSSF REST API for easy access by consumers 52 | # - Allows the repository to include the Scorecard badge. 53 | # - See https://github.com/ossf/scorecard-action#publishing-results. 54 | # For private repositories: 55 | # - `publish_results` will always be set to `false`, regardless 56 | # of the value entered here. 57 | publish_results: true 58 | 59 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 60 | # format to the repository Actions tab. 61 | - name: "Upload artifact" 62 | uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 63 | with: 64 | name: SARIF file 65 | path: results.sarif 66 | retention-days: 5 67 | 68 | # Upload the results to GitHub's code scanning dashboard. 69 | - name: "Upload to code-scanning" 70 | uses: github/codeql-action/upload-sarif@b0b722f202d6f76a52f990a286c2b1eacfc5a9ff # v2.26.11 71 | with: 72 | sarif_file: results.sarif 73 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-test-pypi.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python 🐍 distributions 📦 to TestPyPI 10 | 11 | on: 12 | workflow_dispatch: 13 | inputs: 14 | logLevel: 15 | description: 'Log level' 16 | required: true 17 | default: 'warning' 18 | type: choice 19 | options: 20 | - info 21 | - warning 22 | - debug 23 | tags: 24 | description: 'manual-trigger' 25 | required: false 26 | type: boolean 27 | 28 | permissions: 29 | contents: read 30 | 31 | jobs: 32 | build-n-publish: 33 | name: Build and publish Python 🐍 distributions 📦 to TestPyPI 34 | runs-on: ubuntu-latest 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: abatilo/actions-poetry@v3 38 | with: 39 | poetry-version: 1.4.0 40 | - name: Publish distribution 📦 to Test PyPI 41 | run: >- 42 | poetry config repositories.testpypi https://test.pypi.org/legacy/ 43 | poetry publish --build -u __token__ -p ${{ secrets.TEST_PYPI_API_TOKEN }} 44 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-analytics.yml: -------------------------------------------------------------------------------- 1 | name: "PR Analytics" 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | report_date_start: 6 | description: "Report date start(d/MM/yyyy)" 7 | required: false 8 | report_date_end: 9 | description: "Report date end(d/MM/yyyy)" 10 | required: false 11 | 12 | # Declare default permissions as read only. 13 | permissions: read-all 14 | 15 | jobs: 16 | create-report: 17 | name: "Create report" 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: "Run script for analytics" 21 | uses: AlexSim93/pull-request-analytics-action@master 22 | with: 23 | GITHUB_TOKEN: ${{ secrets.TOKEN }} 24 | GITHUB_REPO_FOR_ISSUE: "intel/xml-cli" 25 | GITHUB_OWNER_FOR_ISSUE: "intel/xml-cli-maintain" 26 | GITHUB_OWNERS_REPOS: "intel/xml-cli-maintain" 27 | CORE_HOURS_START: "9:00" 28 | CORE_HOURS_END: "19:00" 29 | TIMEZONE: "Asia/Kolkata" 30 | REPORT_DATE_START: ${{ inputs.report_date_start }} 31 | REPORT_DATE_END: ${{ inputs.report_date_end }} 32 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | logLevel: 10 | description: 'Log level' 11 | required: true 12 | default: 'warning' 13 | type: choice 14 | options: 15 | - info 16 | - warning 17 | - debug 18 | tags: 19 | description: 'manual-trigger' 20 | required: false 21 | type: boolean 22 | push: 23 | branches: [ "*" ] 24 | pull_request: 25 | branches: [ "*" ] 26 | schedule: 27 | - cron: '45 23 * * 0' 28 | 29 | # Declare default permissions as read only. 30 | permissions: read-all 31 | 32 | jobs: 33 | build: 34 | 35 | runs-on: ubuntu-latest 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | python-version: ["3.8", "3.9", "3.10"] 40 | 41 | steps: 42 | - uses: actions/checkout@v4 43 | - name: Set up Python ${{ matrix.python-version }} 44 | uses: actions/setup-python@v5 45 | with: 46 | python-version: ${{ matrix.python-version }} 47 | - uses: abatilo/actions-poetry@v3 48 | with: 49 | poetry-version: 1.4.0 50 | - name: Install dependencies 51 | run: | 52 | poetry install --with dev 53 | - name: Build wheel 54 | run: | 55 | poetry build 56 | - name: Lint with flake8 57 | run: | 58 | # stop the build if there are Python syntax errors or undefined names 59 | poetry run flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 60 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 61 | poetry run flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 62 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | workflow_dispatch: 13 | inputs: 14 | logLevel: 15 | description: 'Log level' 16 | required: true 17 | default: 'warning' 18 | type: choice 19 | options: 20 | - info 21 | - warning 22 | - debug 23 | tags: 24 | description: 'manual-trigger' 25 | required: false 26 | type: boolean 27 | release: 28 | types: [published] 29 | 30 | permissions: 31 | contents: read 32 | 33 | jobs: 34 | deploy: 35 | name: Build and publish Python 🐍 distributions 📦 to TestPyPI 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v4 39 | - uses: abatilo/actions-poetry@v3 40 | with: 41 | poetry-version: 1.4.0 42 | - name: Publish distribution 📦 to PyPI 43 | run: >- 44 | poetry publish --build -u __token__ -p ${{ secrets.TEST_PYPI_API_TOKEN }} 45 | -------------------------------------------------------------------------------- /.github/workflows/test-execution.yml: -------------------------------------------------------------------------------- 1 | name: Execute Common API Test 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | logLevel: 7 | description: 'Log level' 8 | required: true 9 | default: 'warning' 10 | type: choice 11 | options: 12 | - info 13 | - warning 14 | - debug 15 | tags: 16 | description: 'manual-trigger' 17 | required: false 18 | type: boolean 19 | push: 20 | branches: [ "*" ] 21 | pull_request: 22 | branches: [ "*" ] 23 | schedule: 24 | - cron: '45 23 * * 0' 25 | 26 | permissions: 27 | contents: read 28 | 29 | jobs: 30 | build: 31 | 32 | runs-on: ubuntu-latest 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] 37 | poetry-version: ["latest"] 38 | steps: 39 | - uses: actions/checkout@v4 40 | - name: Set up Python ${{ matrix.python-version }} 41 | uses: actions/setup-python@v5 42 | with: 43 | python-version: ${{ matrix.python-version }} 44 | - uses: abatilo/actions-poetry@v2 45 | with: 46 | poetry-version: ${{ matrix.poetry-version }} 47 | - name: View poetry --help 48 | run: poetry --help 49 | - name: Install dependencies 50 | run: | 51 | python -m pip install --upgrade pip 52 | python -m pip install --upgrade pytest 53 | python -m pip install --upgrade poetry 54 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 55 | if [ -f test_requirements.txt ]; then pip install -r test_requirements.txt; fi 56 | poetry install --with dev 57 | - name: Build wheel 58 | run: | 59 | poetry build 60 | - name: Install wheel 61 | run: | 62 | python -m pip install dist/*.whl 63 | - name: Install Package 64 | run: | 65 | python -m poetry install 66 | - name: Test Execution of CommonTest with pytest 67 | run: | 68 | pytest -q tests/CommonTests.py 69 | 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # dotenv 84 | .env 85 | 86 | # virtualenv 87 | .venv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | 104 | # IDE settings 105 | .vscode/ 106 | 107 | # Temp files created by most text editors. 108 | *~ 109 | 110 | # Merge files created by git. 111 | *.orig 112 | 113 | # vim swap files 114 | .*.swp 115 | .sw? 116 | 117 | # sublime text project file 118 | .project 119 | 120 | # Pycharm IDE 121 | .idea 122 | 123 | # VS Code 124 | .vscode 125 | 126 | # Eclipse project files 127 | .autotools 128 | .cproject 129 | 130 | # User-specific files 131 | *.suo 132 | *.user 133 | *.sln.docstates 134 | 135 | # other random built file extentions 136 | *.tmp 137 | *.out 138 | 139 | # TeamCity is a build add-in 140 | _TeamCity* 141 | 142 | # Windows Azure Build Output 143 | csx/ 144 | *.build.csdef 145 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Architecture 6 | ---------------- 7 | 8 | * Shinde, Amol 9 | 10 | Development Lead 11 | ---------------- 12 | 13 | * Saraiya, Gahan 14 | 15 | Contributors 16 | ------------ 17 | 18 | * Emptage, Nick 19 | 20 | Support Contact 21 | --------------- 22 | 23 | * xmlcli@intel.com 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Contributions are welcome, and they are greatly appreciated! Every little bit helps, 5 | and credit will always be given. 6 | 7 | You can contribute in many ways: 8 | 9 | # Types of Contributions 10 | 11 | ### Report Bugs 12 | 13 | Report bugs Under Tab Issues in **_GitHub issues_**. 14 | 15 | If you are reporting a bug, please include: 16 | 17 | - XmlCli Version: ____ 18 | - IFWI/BIOS Label/Version: ____ 19 | - Source link of IFWI/BIOS used: ____ 20 | - Your operating system name and version: ____ 21 | - Any details about your local setup that might be helpful in troubleshooting. 22 | - Detailed steps to reproduce the bug corresponding logs and/or screenshots. 23 | 24 | ### Fix Bugs 25 | 26 | Look through the GitHub issues for bugs. Anything tagged with "bug" and "help 27 | wanted" is open to whoever wants to implement it. 28 | 29 | ### Implement Features 30 | 31 | Look through the GitHub issues for features. Anything tagged with "enhancement" 32 | and "help wanted" is open to whoever wants to implement it. 33 | 34 | ### Write Documentation 35 | 36 | XmlCli could always use more documentation, whether as part of the 37 | official XmlCli docs, docstrings in scripts, or even on the web in blog posts, 38 | articles, and such. 39 | 40 | ### Submit Feedback 41 | 42 | The best way to send feedback is to file an issue at **_Github Issues_**. 43 | 44 | If you are proposing a feature: 45 | 46 | - Explain in detail how it would work. 47 | - Keep the scope as narrow as possible, to make it easier to implement. 48 | - Remember that this is a volunteer-driven project, and that contributions 49 | are welcome :) 50 | 51 | # Get Started! 52 | 53 | 54 | Ready to contribute? Here's how to set up `xmlcli` for local development. 55 | 56 | 1. Fork this repo on GitHub. 57 | 58 | 2. Clone your fork locally: 59 | 60 | ```shell 61 | git clone 62 | ``` 63 | 64 | 3. Create a branch for local development: 65 | 66 | ```shell 67 | git checkout -b name-of-your-bugfix-or-feature 68 | ``` 69 | 70 | Now you can make your changes locally. 71 | 72 | 4. Your changes must follow below guideline before you make pull request: 73 | - [PEP 8](https://www.python.org/dev/peps/pep-0008/) -- Style Guide for Python Code 74 | - Unittest if you are integrating new feature 75 | - [Bandit](https://github.com/PyCQA/bandit) scan with [project configuration](.github/ipas_default.config) 76 | 77 | 5. Commit your changes and push your branch to GitHub:: 78 | 79 | ```shell 80 | git add . 81 | git commit -m "Your detailed description of your changes." 82 | git push origin name-of-your-bugfix-or-feature 83 | ``` 84 | 85 | 6. Submit a pull request through the GitHub website. 86 | 87 | 7. Merge of the pull request is subject to fulfillment of below cases: 88 | - Pass static code analysis 89 | - Checkmarx or Coverity for python files 90 | - KW or Coverity for C files 91 | - BDBA scan for 3rd party binaries 92 | - Snyk scan for 3rd party libraries 93 | - Bandit guideline scan 94 | - `Pylint` scan 95 | - Antivirus and Malware scan 96 | 97 | # Pull Request Guidelines 98 | 99 | Before you submit a pull request, check that it meets these guidelines: 100 | 101 | 1. The pull request should include tests. 102 | 2. If the pull request adds functionality, the docs should be updated. Put 103 | your new functionality into a function with a docstring, and add the 104 | feature to the list in [README](README.md). 105 | 3. The pull request should work for Python 3.6 or above and for PyPy. 106 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | History 3 | ======= 4 | 5 | 2.0.5 (2024-03-21) 6 | ------------------ 7 | - Feature Improvements: 8 | - Base implementation for parse EFI variable data 9 | 10 | 11 | 2.0.4 (2024-02-28) 12 | ------------------ 13 | - Feature Improvements: 14 | - JSON output capability has been introduced to enhance the online mode operation output. 15 | 16 | - Bug Fixes: 17 | - Addressed an issue with CSV generation in the generate_csv() function, ensuring smoother functionality. 18 | 19 | 20 | 21 | 2.0.3 (2024-01-17) 22 | ------------------ 23 | - Feature Improvements: 24 | - Updated the list of invalid XML characters to enhance parsing accuracy. 25 | - Upgraded dependencies to enhance the security of the application 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Intel Corporation. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /LICENSE_THIRD_PARTY: -------------------------------------------------------------------------------- 1 | This license file includes License information of 3rd party component used 2 | and distributed along with this source. 3 | 4 | =============================================================================== 5 | Components: Brotli, Brotli.exe 6 | LICENSE: MIT License 7 | ------------------------------------------------------------------------------- 8 | Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | 28 | =============================================================================== 29 | Components: bootstrap 30 | LICENSE: MIT License 31 | ------------------------------------------------------------------------------- 32 | The MIT License (MIT) 33 | 34 | Copyright (c) 2011-2022 Twitter, Inc. 35 | Copyright (c) 2011-2022 The Bootstrap Authors 36 | 37 | Permission is hereby granted, free of charge, to any person obtaining a copy 38 | of this software and associated documentation files (the "Software"), to deal 39 | in the Software without restriction, including without limitation the rights 40 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 41 | copies of the Software, and to permit persons to whom the Software is 42 | furnished to do so, subject to the following conditions: 43 | 44 | The above copyright notice and this permission notice shall be included in 45 | all copies or substantial portions of the Software. 46 | 47 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 48 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 49 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 50 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 51 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 52 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 53 | THE SOFTWARE. 54 | 55 | =============================================================================== 56 | Components: toastr 57 | LICENSE: MIT License 58 | ------------------------------------------------------------------------------- 59 | MIT License 60 | 61 | Copyright (c) 2017 Toastr Maintainers 62 | 63 | Permission is hereby granted, free of charge, to any person obtaining a copy 64 | of this software and associated documentation files (the "Software"), to deal 65 | in the Software without restriction, including without limitation the rights 66 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 67 | copies of the Software, and to permit persons to whom the Software is 68 | furnished to do so, subject to the following conditions: 69 | 70 | The above copyright notice and this permission notice shall be included in all 71 | copies or substantial portions of the Software. 72 | 73 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 74 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 75 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 76 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 77 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 78 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 79 | SOFTWARE. 80 | 81 | =============================================================================== 82 | Components: jquery-3.4.1 83 | LICENSE: MIT License 84 | ------------------------------------------------------------------------------- 85 | Projects referencing this document are released under the terms of 86 | the MIT license(https://tldrlegal.com/license/mit-license). 87 | 88 | The MIT License is simple and easy to understand and it places almost no 89 | restrictions on what you can do with the Project. 90 | 91 | You are free to use the Project in any other project 92 | (even commercial projects) as long as the copyright header is left intact. 93 | 94 | 95 | =============================================================================== 96 | Components: TianoCompress, TianoCompress.exe 97 | LICENSE: BSD License 98 | ------------------------------------------------------------------------------- 99 | Copyright (c) 1999 - 2016, Intel Corporation. All rights reserved. 100 | 101 | This program and the accompanying materials are licensed and made available 102 | under the terms and conditions of the BSD License which accompanies 103 | this distribution. The full text of the license may be found at: 104 | http://opensource.org/licenses/bsd-license.php 105 | 106 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 107 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 108 | 109 | =============================================================================== 110 | Components: LzmaCompress, LzmaCompress.exe 111 | LICENSE: BSD License 112 | ------------------------------------------------------------------------------- 113 | Based on LZMA Utility 4.65 : Igor Pavlov : Public domain : 2009-02-03 114 | Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved. 115 | 116 | This program and the accompanying materials are licensed and made available 117 | under the terms and conditions of the BSD License which accompanies 118 | this distribution. The full text of the license may be found at: 119 | http://opensource.org/licenses/bsd-license.php 120 | 121 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 122 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 123 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include __init__.py 2 | include AUTHORS.rst 3 | include CONTRIBUTING.md 4 | include HISTORY.rst 5 | include LICENSE 6 | include LICENSE_THIRD_PARTY 7 | include README.md 8 | include requirements.txt 9 | include test_requirements.txt 10 | include start_xmlcli.bat 11 | include start_xmlcli.sh 12 | 13 | recursive-include src/xmlcli/cfg * 14 | 15 | include examples/* 16 | 17 | recursive-include src/xmlcli/modules * 18 | 19 | include src/xmlcli/out/.gitignore 20 | 21 | include src/xmlcli/messages.json 22 | include src/xmlcli/xmlcli.config 23 | recursive-include src/xmlcli/common * 24 | recursive-include src/xmlcli/access/base * 25 | recursive-include src/xmlcli/access/generic * 26 | recursive-include src/xmlcli/access/linux * 27 | recursive-include src/xmlcli/access/stub * 28 | recursive-include src/xmlcli/access/winrwe * 29 | 30 | include tests/__init__.py 31 | include tests/bandit_scan.bat 32 | include tests/CommonTests.py 33 | include tests/tests.config 34 | include tests/UefiParserTest.py 35 | include tests/UnitTestHelper.py 36 | include tests/XmlCliTest.py 37 | 38 | include src/xmlcli/tools/* 39 | include docs/user_guide/* 40 | 41 | recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif *.md *.PNG *.ini *.cfg *.config 42 | 43 | global-exclude Rw.exe Rw.ini 44 | 45 | exclude MANIFEST.in 46 | 47 | recursive-exclude * __pycache__ 48 | recursive-exclude * *.py[co] 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean clean-test clean-pyc clean-build docs help 2 | .DEFAULT_GOAL := help 3 | 4 | define BROWSER_PYSCRIPT 5 | import os, webbrowser, sys 6 | 7 | from urllib.request import pathname2url 8 | 9 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 10 | endef 11 | export BROWSER_PYSCRIPT 12 | 13 | define PRINT_HELP_PYSCRIPT 14 | import re, sys 15 | 16 | for line in sys.stdin: 17 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) 18 | if match: 19 | target, help = match.groups() 20 | print("%-20s %s" % (target, help)) 21 | endef 22 | export PRINT_HELP_PYSCRIPT 23 | 24 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 25 | 26 | help: 27 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) 28 | 29 | clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts 30 | 31 | clean-build: ## remove build artifacts 32 | rm -fr bld/ || echo 'bld' 33 | rm -fr build/ || echo 'build removed' 34 | rm -fr dist/ || echo 'dist removed' 35 | rm -fr .eggs/ || echo '.eggs removed' 36 | rm -fr src/*.eggs/ || echo '.eggs removed' 37 | rm -fr src/*.egg-info/ || echo '.egg-info removed' 38 | find . -name '*.egg-info' -exec rm -fr {} + || echo '.egg-info removed' 39 | find . -name '*.egg' -exec rm -f {} + || echo '.egg removed' 40 | 41 | clean-pyc: ## remove Python file artifacts 42 | find . -name '*.pyc' -exec rm -f {} + 43 | find . -name '*.pyo' -exec rm -f {} + 44 | find . -name '*~' -exec rm -f {} + 45 | find . -name '__pycache__' -exec rm -fr {} + 46 | 47 | clean-test: ## remove test and coverage artifacts 48 | rm -fr .tox/ 49 | rm -f .coverage 50 | rm -fr htmlcov/ 51 | rm -fr .pytest_cache 52 | 53 | lint: ## check style with flake8 54 | flake8 xmlcli tests 55 | 56 | test: ## run tests quickly with the default Python 57 | python setup.py test 58 | 59 | test-all: ## run tests on every Python version with tox 60 | tox 61 | 62 | coverage: ## check code coverage quickly with the default Python 63 | coverage run --source xmlcli setup.py test 64 | coverage report -m 65 | coverage html 66 | $(BROWSER) htmlcov/index.html 67 | 68 | docs: ## generate Sphinx HTML documentation, including API docs 69 | rm -f docs/xmlcli.rst 70 | rm -f docs/modules.rst 71 | sphinx-apidoc -o docs/ xmlcli 72 | $(MAKE) -C docs clean 73 | $(MAKE) -C docs html 74 | $(BROWSER) docs/_build/html/index.html 75 | 76 | dist: clean ## builds source and wheel package 77 | mkdir bld 78 | python setup.py egg_info --egg-base=bld build --build-base=bld bdist_wheel --universal 79 | ls -l dist 80 | 81 | install: clean ## install the package to the active Python's site-packages 82 | python setup.py install 83 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 3 | 4 | ## Reporting a Vulnerability 5 | Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). 6 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # Added to support import requirements 2 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-dinky 2 | title: UFFAF 3 | description: UEFI Firmware Foundational Automation Framework (formerly XmlCli) 4 | show_downloads: true 5 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = xmlcli 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | # 7 | import os 8 | import sys 9 | import importlib 10 | from recommonmark.transform import AutoStructify 11 | 12 | ################################################################################# 13 | package = 'xmlcli' 14 | package_name = package 15 | 16 | # use absolute path for relative directory 17 | current_directory = os.path.abspath(os.path.dirname(__file__)) 18 | # make sure this version of package "wins" so that we get version correct 19 | sys.path.insert(0, f"{current_directory}{os.sep}..") 20 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(current_directory)))) 21 | # get version from our package, assumes we have _version file 22 | mod = importlib.import_module(package) 23 | 24 | # The version info for the project you're documenting 25 | # The short X.Y version. 26 | version = mod.__version__ 27 | # The full version, including alpha/beta/rc tags. 28 | release = mod.__version__ 29 | 30 | ################################################################################# 31 | 32 | # -- Project information ----------------------------------------------------- 33 | project = 'XmlCli' 34 | copyright = '2022, Intel' 35 | author = 'Gahan Saraiya, Amol Shinde' 36 | 37 | # -- General configuration --------------------------------------------------- 38 | 39 | # Add any Sphinx extension module names here, as strings. 40 | # could be extensions with Sphinx (i.e. 'sphinx.ext.*') or custom ones. 41 | extensions = [ 42 | 'sphinx.ext.autodoc', 43 | 'sphinx.ext.viewcode', 44 | 'sphinx.ext.napoleon', 45 | 'sphinx.ext.coverage', 46 | 'sphinx.ext.mathjax', 47 | 'sphinx.ext.autosectionlabel', 48 | "recommonmark", 49 | "sphinx_markdown_tables" 50 | ] 51 | 52 | # Prefix document path to section labels, otherwise autogenerated labels would look like 'heading' 53 | # rather than 'path/to/file:heading' 54 | autosectionlabel_prefix_document = True 55 | 56 | # Napolean settings 57 | napoleon_google_docstring = True 58 | napoleon_numpy_docstring = True 59 | napoleon_include_private_with_doc = False 60 | napoleon_include_special_with_doc = True 61 | napoleon_use_admonition_for_examples = False 62 | napoleon_use_admonition_for_notes = False 63 | napoleon_use_admonition_for_references = False 64 | napoleon_use_ivar = False 65 | napoleon_use_param = True 66 | napoleon_use_rtype = True 67 | 68 | # Add any relative paths that contain templates 69 | templates_path = ['_templates'] 70 | 71 | # List of patterns, relative to source directory, that match files and 72 | # directories to ignore when looking for source files. 73 | # This pattern also affects html_static_path and html_extra_path. 74 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 75 | 76 | # List of directories, that should not be searched for source files. 77 | exclude_trees = ['_build'] 78 | 79 | # If true, the current module name will be prepended to all description 80 | # unit titles (such as .. function::). 81 | add_module_names = True 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # The encoding of source files. 87 | source_encoding = 'utf-8' 88 | 89 | # The master toctree document. 90 | master_doc = 'index' 91 | 92 | 93 | # HTML 94 | html_theme = 'alabaster' 95 | html_static_path = ['_static'] 96 | htmlhelp_basename = package_name 97 | 98 | source_suffix = { 99 | '.rst': 'restructuredtext', 100 | '.md': 'markdown', 101 | } 102 | 103 | # LaTex 104 | latex_paper_size = 'letter' 105 | latex_font_size = '10pt' 106 | latex_documents = [ 107 | (master_doc, f'{package}.tex', 108 | f'{project} Documentation', 109 | 'Saraiya, Gahan', 'manual'), 110 | ] 111 | 112 | # man page 113 | man_pages = [ 114 | (master_doc, package_name, 115 | f'{project} Documentation', 116 | [author], 1) 117 | ] 118 | 119 | # PDF 120 | pdf_documents = [ 121 | ('index', project, f'{project} Documentation', author), 122 | ] 123 | 124 | # A comma-separated list of custom stylesheets. Example: 125 | pdf_stylesheets = ['sphinx', 'kerning', 'a4'] 126 | 127 | # A list of folders to search for stylesheets. Example: 128 | pdf_style_path = ['.', '_styles'] 129 | 130 | # How many levels deep should the table of contents be? 131 | pdf_toc_depth = 9999 132 | 133 | # Add section number to section references 134 | pdf_use_numbered_links = False 135 | 136 | # Background images fitting mode 137 | pdf_fit_background_mode = 'scale' 138 | 139 | 140 | def setup(app): 141 | app.add_config_value('recommonmark_config', { 142 | # 'url_resolver': lambda url: github_doc_root + url, 143 | 'enable_auto_toc_tree': True, 144 | 'auto_toc_tree_section': 'Contents', 145 | 'enable_math': False, 146 | 'enable_inline_math': False, 147 | 'enable_eval_rst': True, 148 | }, True) 149 | app.add_transform(AutoStructify) 150 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.md 2 | -------------------------------------------------------------------------------- /docs/history.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../HISTORY.rst 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Welcome to XmlCli's documentation! 3 | ====================================== 4 | 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | :caption: Contents: 9 | 10 | XmlCli Usage Commands 11 | readme 12 | installation 13 | usage 14 | modules 15 | contributing 16 | authors 17 | history 18 | 19 | .. automodule:: src/xmlcli 20 | :members: 21 | XmlCli.py 22 | 23 | Indices and tables 24 | ================== 25 | 26 | * :ref:`genindex` 27 | * :ref:`modindex` 28 | * :ref:`search` 29 | 30 | =========================================== 31 | XML CLI 32 | =========================================== 33 | 34 | .. toctree:: 35 | :maxdepth: 1 36 | 37 | history.rst 38 | 39 | 40 | test module 41 | =========== 42 | 43 | .. automodule:: tests 44 | :members: 45 | :undoc-members: 46 | :show-inheritance: 47 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=xmlcli 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Usage 3 | ===== 4 | 5 | .. include:: ../README.md 6 | -------------------------------------------------------------------------------- /docs/user_guide/compare_setup_knobs.md: -------------------------------------------------------------------------------- 1 | If you have two different Xml from `cli.savexml()` command you could use below method: 2 | 3 | 4 | Syntax: 5 | ```python 6 | from xmlcli import XmlCli as cli 7 | 8 | cli.helpers.generate_knobs_delta( 9 | ref_xml="path/to/reference.xml", 10 | new_xml="path/to/new.xml", 11 | out_file="path/to/difference-delta.txt", 12 | compare_tag="default" # xml attribute to be compared against (default|CurrentVal|size|prompt|depex|...) 13 | ) 14 | ``` 15 | 16 | 17 | If you have BIOS/IFWI image, instead of doing `cli.savexml` for both image, you could directly use below command syntax: 18 | ```python 19 | from xmlcli import XmlCli as cli 20 | 21 | cli.helpers.compare_bios_knobs("", "", result_log_file="") 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/user_guide/installation.md: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | There are various method through which one can achieve installation of the module in python and same also applicable to 4 | XmlCli as well. 5 | 6 | 7 | ## Method 1: Install from Python Package Index 8 | 9 | ```shell 10 | python -m pip install xmlcli --proxy --index-url 11 | ``` 12 | 13 | ## Method 2: Install via wheel 14 | 15 | ```shell 16 | python -m pip install --proxy 17 | ``` 18 | 19 | ## Method 3: Install via repository 20 | 21 | ```shell 22 | python -m pip install --proxy 23 | ``` 24 | 25 | ## Method 4: Install via Git 26 | 27 | ```shell 28 | python -m pip install git+https://github.com/intel/xml-cli 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/user_guide/log_configuration.md: -------------------------------------------------------------------------------- 1 | # Customizing logging 2 | 3 | ## Simple log level modification 4 | 5 | Configuration file ([xmlcli.config](../../src/xmlcli/xmlcli.config)) for this tool 6 | consists dedicated section `[LOG_SETTINGS]` 7 | 8 | 9 | 10 | ## Using Logging with advanced customization 11 | 12 | Importing module: 13 | 14 | ```python 15 | from xmlcli.common import logger 16 | 17 | settings = logger.Setup( # All setup arguments are optional/ if not specified default value will be overridden 18 | log_level="DEBUG", # default level would be utils.LOG_LEVEL 19 | log_title="XmlCli", # default to utils.LOGGER_TITLE (Avoid overriding this!!!) 20 | sub_module="XmlCliLib", # To allow printing submodule name to logging, for better lookup to filter 21 | log_dir="absolute/path/to/log/directory/", # defaults to utils.LOG_DIR 22 | log_file_name="name_of_log_file.log", # defaults to title and timestamp 23 | write_in_file=True, # Specifies whether to write the log in file or not defaults to utils.FILE_LOG 24 | print_on_console=True, # Specifies whether to write log on console or not defaults to utils.CONSOLE_STREAM_LOG 25 | ) 26 | 27 | # To display config of system run below function: 28 | settings.display_system_configs() 29 | 30 | # Detailed logging for function entry point: 31 | settings.display_system_configs() 32 | 33 | # To log whether your script enters and exit to function along with the arguments feed to the function, add below line overridden any function/method: 34 | @settings.log_function_entry_and_exit 35 | def my_function(param1, param2): 36 | pass 37 | ``` 38 | 39 | This will log the details on when your script enters to function: `my_function`, lists the parameters and also logs when the script exits from the function scope. 40 | 41 | Example: 42 | ```python 43 | from xmlcli.common.logger import settings 44 | 45 | @settings.log_function_entry_and_exit 46 | def process_ucode(Operation='READ', BiosBinaryFile=0, UcodeFile=0, ReqCpuId=0, outPath='', BiosBinListBuff=0, ReqCpuFlags=0xFFFF, PrintEn=True): 47 | # rest of the function definition.. 48 | # calculation, etc. 49 | pass 50 | ``` 51 | 52 | logging individually as: 53 | 54 | ```python 55 | from xmlcli.common.logger import settings 56 | 57 | log = settings.logger 58 | log.debug("log message") 59 | log.info("log message") 60 | log.error("error message") 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/user_guide/uefi_binary_parsing.md: -------------------------------------------------------------------------------- 1 | ## Usage instruction for Uefi Parser 2 | 3 | The parser is able to generate the json representation from BIOS or IFWI image. 4 | 5 | Key features: 6 | - JSON representation, lightweight database with keys and values with ease of readability 7 | - Works with both SUT and offline image 8 | 9 | Working with SUT: 10 | 11 | Below command to be executed only after enabling applicable access method: 12 | 13 | ```python 14 | from xmlcli import XmlCli as cli 15 | 16 | max_bios_size = 12 * (1024**2) # 12 MB - configure based on the platform used 17 | # If max_bios_size argument is not specified then by default it uses 32 MB dump to lookup for BIOS image 18 | bios_image = cli.clb.get_bin_file("linux", max_bios_size=max_bios_size) # variable will have location of bios image dump stored from memory 19 | ``` 20 | 21 | Initiate the Parsing with below commands: 22 | 23 | ```python 24 | from xmlcli.common import bios_fw_parser 25 | 26 | bios_image = "absolute-path/to/bios-image.rom" 27 | 28 | uefi_parser = bios_fw_parser.UefiParser(bin_file=bios_image, # binary file to parse 29 | parsing_level=0, # parsing level to manage number of parsing features 30 | base_address=0, # (optional) provide base address of bios FV region to start the parsing (default 0x0) 31 | guid_to_store=[] # if provided the guid for parsing then parser will look for every GUID in the bios image 32 | ) 33 | # parse binary 34 | output_dict = uefi_parser.parse_binary() 35 | output_dict = uefi_parser.sort_output_fv(output_dict) # (optional) only to sort output by FV address 36 | # write content to json file 37 | output_file = "absolute-path/to/output.json" 38 | uefi_parser.write_result_to_file(output_file, output_dict=output_dict) 39 | # Below code block is only to store map result to json for FV region(s) extracted by guid lookup 40 | if uefi_parser.guid_to_store: 41 | # additional test for GUIDs to store 42 | result = uefi_parser.guid_store_dir # result of passed guid 43 | user_guid_out_file = "absolute-path/to/guid-stored/output.json" 44 | # Store guid stored result to json file 45 | uefi_parser.write_result_to_file(user_guid_out_file, output_dict=uefi_parser.stored_guids) 46 | ``` 47 | -------------------------------------------------------------------------------- /examples/BiosKnobs.ini: -------------------------------------------------------------------------------- 1 | [BiosKnobs] 2 | CvfSupport=1 3 | MipiCam_ControlLogic0=1 4 | MipiCam_ControlLogic1=1 5 | MipiCam_Link0=1 6 | MipiCam_Link1=1 7 | MipiCam_Link1_SensorModel=1 8 | MipiCam_Link1_DriverData_CrdVersion=0x20 9 | -------------------------------------------------------------------------------- /examples/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | 6 | # Custom imports 7 | 8 | 9 | if __name__ == "__main__": 10 | pass 11 | -------------------------------------------------------------------------------- /examples/automate_program_knobs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | import os 6 | import sys 7 | import glob 8 | import shutil 9 | 10 | # Custom imports 11 | from xmlcli import XmlCli as cli 12 | from xmlcli import XmlCliLib as clb 13 | from xmlcli.common.logger import log 14 | from xmlcli import UefiFwParser as fwp 15 | 16 | 17 | def automate_program_knobs(input_bios, config_dir, output_dir, new_major_ver="", new_minor_ver=""): 18 | """Function to perform the CvProgKnobs for multiple bios images using configuration file 19 | 20 | :param input_bios: absolute path to the folder contains bios images or absolute path to the bios file 21 | :param config_dir: absolute path to the folder contains bios knobs configuration file(.ini) 22 | :param output_dir: absolute path of the directory to store the output files 23 | :param new_major_ver: new major version for the file 24 | :param new_minor_ver: new minor version for the file 25 | """ 26 | bios_knob_config_files = glob.glob(os.path.join(config_dir, "*.ini")) 27 | original_knobs_config = clb.KnobsIniFile 28 | input_bios_files = [] 29 | if os.path.isdir(input_bios): 30 | input_bios_files = glob.glob(os.path.join(input_bios, "*.bin")) 31 | elif os.path.isfile(input_bios): 32 | input_bios_files = [input_bios] 33 | for KnobsIni in bios_knob_config_files: 34 | clb.KnobsIniFile = KnobsIni 35 | suffix_text = os.path.splitext(os.path.basename(KnobsIni))[0] 36 | for BiosBinFile in input_bios_files: 37 | log.info(f"Processing BIOS file = {BiosBinFile}") 38 | cli.CvProgKnobs(0, BiosBinFile, suffix_text, True) 39 | temp_file = clb.OutBinFile 40 | fwp.UpdateBiosId(clb.OutBinFile, new_major_ver, new_minor_ver) 41 | if clb.OutBinFile != "": 42 | shutil.move(clb.OutBinFile, output_dir) 43 | clb.RemoveFile(temp_file) 44 | clb.KnobsIniFile = original_knobs_config 45 | 46 | 47 | if __name__ == "__main__": 48 | pass 49 | -------------------------------------------------------------------------------- /examples/setup_modification.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | This file is example to demonstrate usage of XmlCli to modify Values specified 4 | by user. 5 | 6 | Aim: Have various function demonstrating example of usage for xmlcli to modify 7 | setup option values in `ONLINE` mode. Intended to be executed on target platform 8 | 9 | Pre-requisite: 10 | 1. Python Software (Python version 3.6 or above [64-bit]) 11 | 2. XmlCli source code 12 | 3. Elevated Privilege [Execution access to run as administrator/root] 13 | 14 | For the example we are taking setup options below: 15 | ------------------------------------|---------------- 16 | Setup Option | Possible Value 17 | ------------------------------------|---------------- 18 | CvfSupport | 0,1,2 19 | MipiCam_ControlLogic0 | 0,1 20 | MipiCam_ControlLogic1 | 0,1 21 | MipiCam_Link0 | 0,1 22 | MipiCam_Link1 | 0,1 23 | MipiCam_Link1_SensorModel | 0,1 24 | MipiCam_Link1_DriverData_CrdVersion | 0x10, 0x20 25 | ------------------------------------|---------------- 26 | """ 27 | __author__ = "Gahan Saraiya" 28 | 29 | # Built-in imports 30 | import sys 31 | 32 | # Custom imports 33 | # Importing API for xmlcli 34 | from xmlcli import XmlCli as cli 35 | 36 | KNOBS_TO_MODIFY = [ 37 | "CvfSupport=1", 38 | "MipiCam_ControlLogic0=1", 39 | "MipiCam_ControlLogic1=1", 40 | "MipiCam_Link0=1", 41 | "MipiCam_Link1=1", 42 | "MipiCam_Link1_SensorModel=1", 43 | "MipiCam_Link1_DriverData_CrdVersion=0x20", 44 | ] 45 | 46 | 47 | def simple_program_knobs_from_sut(): 48 | """ 49 | Simplest flow to read/program values 50 | :return: 51 | """ 52 | from xmlcli import XmlCli as cli 53 | 54 | cli.clb._setCliAccess("linux") 55 | cli.clb.ConfXmlCli() 56 | cli.CvReadKnobs(", ".join(KNOBS_TO_MODIFY)) # read+verify 57 | cli.CvProgKnobs(", ".join(KNOBS_TO_MODIFY)) # modify 58 | _status = cli.CvReadKnobs(", ".join(KNOBS_TO_MODIFY)) # read+verify 59 | return _status 60 | 61 | 62 | def program_knobs_from_sut(access_method="linux", knob_lis=None, config_file=None): 63 | """ 64 | 65 | :param access_method: 66 | For linux: 67 | linux 68 | For more access method and choices, visit 69 | :param knob_lis: list of setup options to modify 70 | i.e. ["CvfSupport=1", "MipiCam_ControlLogic0=1"] 71 | :param config_file: absolute path to bios knobs configuration file to read knob and value 72 | i.e. refer `cfg/BiosKnobs.ini` under xmlcli source 73 | :return: 74 | """ 75 | cli.clb._setCliAccess(access_method) # on console one should see that the access method is correctly set. 76 | if cli.clb.InterfaceType != access_method: 77 | # validation to confirm interface is selected correctly 78 | # if failed to set interface the return from flow. 79 | return -1 80 | 81 | return_status = cli.clb.ConfXmlCli() 82 | if return_status == 0: # XmlCli is supported and enabled. 83 | # Here we can perform our desire operation... 84 | if not knob_lis and not config_file: 85 | print("Please either provide knob list or config file") 86 | return -1 87 | if knob_lis: 88 | knobs = ", ".join(knob_lis) 89 | status = cli.CvReadKnobs(knobs) # result can be observed in detail in log/console 90 | print(f"status of read: {status}") 91 | if status == 0: # all values are set as expected, do nothing 92 | return status 93 | else: # at least one knob is not having expected value 94 | status = cli.CvProgKnobs(knobs) 95 | if status != 0: # unable to modify knobs 96 | return status 97 | else: # Verify the modification status 98 | status = cli.CvReadKnobs(knobs) 99 | if status == 0: # success 100 | print("Successfully modified and verified values") 101 | else: 102 | print("Write was successful but could not verify the value") 103 | return status 104 | elif config_file: 105 | cli.clb.KnobsIniFile = config_file 106 | status = cli.CvReadKnobs() # result can be observed in detail in log/console 107 | print(f"status of read: {status}") 108 | if status == 0: # all values are set as expected, do nothing 109 | return status 110 | else: # at least one knob is not having expected value 111 | status = cli.CvProgKnobs() 112 | if status != 0: # unable to modify knobs 113 | return status 114 | else: # Verify the modification status 115 | status = cli.CvReadKnobs() 116 | if status == 0: # success 117 | print("Successfully modified and verified values") 118 | else: 119 | print("Write was successful but could not verify the value") 120 | return status 121 | elif return_status == 2: # Reboot requires 122 | print("RESTART SYSTEM") 123 | return return_status 124 | else: 125 | print("ERROR...") 126 | return -1 127 | 128 | 129 | if __name__ == "__main__": 130 | # simple_program_knobs_from_sut() 131 | 132 | # METHOD 1: With Knobs list 133 | status = program_knobs_from_sut(access_method="linux", knob_lis=KNOBS_TO_MODIFY) # [user-param] 134 | print(f"Execution status result={status}") 135 | 136 | # METHOD 2: With config file [comment METHOD 1 and uncomment below line for METHOD 2] 137 | # status = program_knobs_from_sut(access_method="linux", config_file="BiosKnobs.ini") # [user-param] 138 | -------------------------------------------------------------------------------- /parse_bin.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from xmlcli import XmlCli as cli 4 | from xmlcli.common import bios_fw_parser 5 | 6 | workspace_dir = r"D:\PublicShare\gahan\_debug\NVL_bios_data_bin" 7 | bin_file = os.path.join(workspace_dir, "BIOS_NVL_S_Internal_0462.00_Dispatch_VS__PreProd.rom") 8 | # bin_file = os.path.join(workspace_dir, "MTL_FSPWRAPPER_3184_01_R.rom") 9 | output_xml = f"{bin_file}.xml" 10 | output_json = f"{bin_file}.json" 11 | 12 | uefi_parser = bios_fw_parser.UefiParser(bin_file=bin_file, 13 | parsing_level=0, 14 | base_address=0x0, 15 | guid_to_store=[cli.fwp.gBiosKnobsDataBinGuid] 16 | ) 17 | # parse binary 18 | output_dict = uefi_parser.parse_binary() 19 | output_dict = uefi_parser.sort_output_fv(output_dict) 20 | # write content to json file 21 | uefi_parser.write_result_to_file(output_json, output_dict=output_dict) 22 | 23 | cli.savexml(output_xml, bin_file) 24 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "xmlcli" 3 | version = "2.0.5" 4 | description = "UFFAF - UEFI Firmware Foundational Automation Framework (formerly Xml-Cli)" 5 | authors = ["Gahan Saraya "] 6 | maintainers = ["Intel "] 7 | license = "BSD 3-Clause License" 8 | readme = "README.md" 9 | homepage = "https://github.com/intel/xml-cli" 10 | keywords = ["uffaf", "xmlcli", "xml-cli", "framework", "automation", "validation", "bios", "xml"] 11 | classifiers = [ 12 | "Development Status :: 5 - Production/Stable", 13 | "License :: OSI Approved :: BSD License", 14 | "Intended Audience :: Developers", 15 | "Natural Language :: English", 16 | "Programming Language :: Python :: 3", 17 | "Programming Language :: Python :: 3.7", 18 | "Programming Language :: Python :: 3.8", 19 | "Programming Language :: Python :: 3.9", 20 | "Programming Language :: Python :: 3.10", 21 | "Programming Language :: Python :: 3.11", 22 | ] 23 | packages = [{include = "xmlcli", from = "src"}] 24 | 25 | [tool.poetry.dependencies] 26 | python = ">=3.8.1" 27 | defusedxml = ">=0.6.0" 28 | 29 | [tool.poetry.dev-dependencies] 30 | tox = ">=3.23.0" 31 | pygments = ">=2.8.0" 32 | jinja2 = "^3.1.6" 33 | babel = ">=2.9.1" 34 | sphinx = ">=4.0.2" 35 | pytest = ">=7.4.3" 36 | pytest-html = ">=4.1.0" 37 | flake8 = ">=6.1.0" 38 | 39 | [build-system] 40 | requires = ["poetry-core"] 41 | build-backend = "poetry.core.masonry.api" 42 | 43 | [tool.flake8] 44 | exclude = [ 45 | ".git", 46 | "__pycache__", 47 | "docs/source/conf.py", 48 | "old", 49 | "build", 50 | "dist", 51 | "out" 52 | ] 53 | ignore = "E501" 54 | indent-size = 2 55 | 56 | [tool.poetry.scripts] 57 | xmlcli = { reference = "xmlcli.start_xmlcli:cli", type = "console" } 58 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | defusedxml >= 0.6.0 2 | 3 | # Optional Requirements 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # https://setuptools.pypa.io/en/latest/userguide/declarative_config.html 2 | 3 | [metadata] 4 | name = xmlcli 5 | version = attr: xmlcli._version.__version__ 6 | url = https://github.com/intel/xml-cli 7 | description = "UFFAF - UEFI Firmware Foundational Automation Framework (formerly Xml-Cli)" 8 | commit = True 9 | tag = True 10 | license_files = LICENSE 11 | long_description = file: README.md, LICENSE_THIRD_PARTY 12 | long_description_content_type = text/markdown 13 | keywords = uffaf, xmlcli, xml-cli, framework, automation, validation, bios, xml 14 | license = BSD 3-Clause License 15 | classifiers = 16 | Development Status :: 5 - Production/Stable 17 | License :: OSI Approved :: BSD License 18 | Intended Audience :: Developers 19 | Natural Language :: English 20 | Programming Language :: Python :: 3 21 | Programming Language :: Python :: 3.7 22 | Programming Language :: Python :: 3.8 23 | Programming Language :: Python :: 3.9 24 | Programming Language :: Python :: 3.10 25 | Programming Language :: Python :: 3.11 26 | author_email = gahan.saraiya@intel.com 27 | maintainer_email = xmlcli@intel.com 28 | 29 | 30 | [options] 31 | packages = find: 32 | include_package_data = True 33 | install_requires = file: requirements.txt 34 | tests_require = file: test_requirements.txt 35 | 36 | [options.entry_points] 37 | console_scripts = 38 | xmlcli = xmlcli.start_xmlcli:cli 39 | 40 | [options.packages.find] 41 | where = src 42 | 43 | 44 | [bdist_wheel] 45 | universal = 1 46 | 47 | [flake8] 48 | exclude = 49 | # No need to traverse our git directory 50 | .git, 51 | # There's no value in checking cache directories 52 | __pycache__, 53 | # docs are markdown or autogenerated files 54 | docs/source/conf.py, 55 | # The old directory contains Flake8 2.0 56 | old, 57 | # This contains our built documentation 58 | build, 59 | # This contains builds of flake8 that we don't want to check 60 | dist 61 | # This contains output of generated files that we don't want to check 62 | out 63 | ignore = E501 64 | indent-size = 2 65 | 66 | 67 | [aliases] 68 | # Define setup.py command aliases here 69 | 70 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from setuptools import setup 5 | from setuptools.command.install import install 6 | 7 | 8 | class PostInstall(install): 9 | def run(self): 10 | super().run() # Up to here is the default installation flow 11 | if sys.platform.startswith('linux'): # Just for linux, change the attributes 12 | for filepath in self.get_outputs(): 13 | if filepath.find("LzmaCompress") > -1 or filepath.find("TianoCompress") > -1 or filepath.find("Brotli") > -1: 14 | os.chmod(filepath, 0o777) 15 | 16 | 17 | if __name__ == "__main__": 18 | setup(cmdclass={'install': PostInstall}) 19 | -------------------------------------------------------------------------------- /src/xmlcli/__init__.py: -------------------------------------------------------------------------------- 1 | # Added to support import requirements 2 | # coding=utf-8 3 | 4 | import sys 5 | from ._version import __version__ 6 | -------------------------------------------------------------------------------- /src/xmlcli/_version.py: -------------------------------------------------------------------------------- 1 | class Version: 2 | def __init__(self, major, minor, build, tag=""): 3 | self.major=major 4 | self.minor=minor 5 | self.build=build 6 | self.tag=tag 7 | 8 | def __str__(self): 9 | return f"{self.major}.{self.minor}.{self.build}{self.tag}" 10 | 11 | # MAJOR ---------- 12 | # incremented any time you change the API that may break backwards compatibility 13 | # in a fairly major way 14 | MAJOR = 2 15 | # MINOR ------------ 16 | MINOR = 0 17 | # BUILD ------ 18 | BUILD = 5 # or __revision__ 19 | # TAG ------- 20 | TAG = "" 21 | 22 | version_instance = Version(MAJOR, MINOR, BUILD, TAG) 23 | 24 | __version__ = str(version_instance) 25 | -------------------------------------------------------------------------------- /src/xmlcli/access/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | 6 | # Custom imports 7 | 8 | 9 | if __name__ == "__main__": 10 | pass 11 | -------------------------------------------------------------------------------- /src/xmlcli/access/base/README.md: -------------------------------------------------------------------------------- 1 | # BASE ACCESS METHOD 2 | 3 | Every new access Interface which is designed to be integrated with XmlCli must include the file and folder structure from this pattern 4 | 5 | Below are the key parameter to be focused on: 6 | 7 | `` defines method name 8 | below must be the structure and mandatory checklist to complete to add integrate any new access method to xmlcli: 9 | 10 | | | Syntax | Example | 11 | |---------------|--------------------------------------|----------------------| 12 | | Access Method | `` | `linux` | 13 | | Folder Name | `` | `linux` | 14 | | Configuration | `/.ini` | `linux/linux.ini` | 15 | | Documentation | `/README.md` | `linux/README.md` | 16 | | Unit Test | `/Test.py` | `linux/linuxTest.py` | 17 | 18 | Any other dependency binary file should be part of this access folder itself. 19 | 20 | ## Dependencies: 21 | 22 | List out dependencies binaries in order with details. It must answer to basic 23 | question Why is it used?, What is the impact if we don't use it? 24 | 25 | 1. Dependency A 26 | - Details about dependency A 27 | 2. Dependency B 28 | - Details about dependency B 29 | 30 | -------------------------------------------------------------------------------- /src/xmlcli/access/base/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | 6 | # Custom imports 7 | 8 | 9 | if __name__ == "__main__": 10 | pass 11 | -------------------------------------------------------------------------------- /src/xmlcli/access/base/base.ini: -------------------------------------------------------------------------------- 1 | # Config file produced to map base files 2 | [GENERAL] 3 | # specify sections to be read (comma separated) 4 | access_methods = BASE 5 | 6 | [BASE] 7 | # Name of file to be use to import class for this access method 8 | file = base.py 9 | doc = README.md 10 | method_class = BaseAccess 11 | dependency_a = 12 | dependency_b = 13 | -------------------------------------------------------------------------------- /src/xmlcli/access/base/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | import os 6 | import binascii 7 | import warnings 8 | import configparser 9 | 10 | SMI_TRIGGER_PORT = 0xB2 11 | DEPRECATION_WARNINGS = False 12 | 13 | 14 | class CliAccessException(Exception): 15 | def __init__(self, message="CliAccess is a virtual class!", error_code=None, *args, **kwargs): 16 | # Call the base class constructor with the parameters it needs 17 | hints = "\n".join(kwargs.get("hints", [])) 18 | self.message = "[CliAccessExceptionError: {}] {}".format(error_code, message) if error_code else "[CliAccessException] {}".format(message) 19 | if hints: 20 | self.message += "\nHint: " + hints 21 | super(CliAccessException, self).__init__(self.message) 22 | 23 | 24 | class CliOperationException(CliAccessException): 25 | def __init__(self, message="Operation Error!", error_code=None, *args, **kwargs): 26 | # Call the base class constructor with the parameters it needs 27 | hints = "\n".join(kwargs.get("hints", [])) 28 | self.message = "[CliOperationError: {}] {}".format(error_code, message) if error_code else "[CliAccessException] {}".format(message) 29 | if hints: 30 | self.message += "\nHint: " + hints 31 | super(CliOperationException, self).__init__(self.message) 32 | 33 | 34 | def deprecated(message): 35 | """ 36 | This is a decorator which can be used to mark functions 37 | as deprecated. It will result in a warning being emitted 38 | when the function is used. 39 | """ 40 | def deprecated_decorator(func): 41 | def deprecated_func(*args, **kwargs): 42 | if DEPRECATION_WARNINGS: 43 | warnings.warn("{} is a deprecated function. {}".format(func.__name__, message), 44 | category=DeprecationWarning, 45 | stacklevel=2) 46 | warnings.simplefilter('default', DeprecationWarning) 47 | return func(*args, **kwargs) 48 | return deprecated_func 49 | return deprecated_decorator 50 | 51 | 52 | class BaseAccess(object): 53 | def __init__(self, access_name, child_class_directory): 54 | """ 55 | 56 | :param access_name: Name of access method 57 | :param child_class_directory: Name of child class 58 | """ 59 | self.InterfaceType = access_name 60 | self.interface = access_name 61 | self.config = configparser.RawConfigParser(allow_no_value=True) 62 | self.config_file = os.path.join(child_class_directory, "{}.ini".format(access_name)) 63 | self.read_config() 64 | 65 | @staticmethod 66 | def byte_to_int(data): 67 | return int(binascii.hexlify(bytearray(data)[::-1]), 16) 68 | 69 | @staticmethod 70 | def int_to_byte(data, size): 71 | data_dump = b"" 72 | data_dump = data.to_bytes(size, byteorder="little", signed=False) 73 | return data_dump 74 | 75 | def read_config(self): 76 | try: 77 | self.config.read(self.config_file) 78 | except AttributeError: 79 | # EFI Shell may encounter at this flow while reading config file as .read method uses os.popen which is not available at EFI Python 80 | with open(self.config_file, "r") as config_file: 81 | self.config._read(config_file, self.config_file) 82 | 83 | # def __setattr__(self, attribute, value): 84 | # # [cli-2.0.0]: why did we explicitly require this method ??, what are its use case? 85 | # if attribute not in self.__dict__: 86 | # print("Cannot set {}".format(attribute)) 87 | # else: 88 | # self.__dict__[attribute] = value 89 | 90 | def halt_cpu(self, delay): 91 | raise CliAccessException() 92 | 93 | @deprecated("Please use method halt_cpu") 94 | def haltcpu(self, delay): 95 | return self.halt_cpu(delay) 96 | 97 | def run_cpu(self): 98 | raise CliAccessException() 99 | 100 | @deprecated("Please use method run_cpu") 101 | def runcpu(self): 102 | return self.run_cpu() 103 | 104 | def initialize_interface(self): 105 | raise CliAccessException() 106 | 107 | @deprecated("Please use method initialize_interface") 108 | def InitInterface(self): 109 | return self.initialize_interface() 110 | 111 | def close_interface(self): 112 | raise CliAccessException() 113 | 114 | @deprecated("Please use method close_interface") 115 | def CloseInterface(self): 116 | return self.close_interface() 117 | 118 | def warm_reset(self): 119 | raise CliAccessException() 120 | 121 | @deprecated("Please use method warm_reset") 122 | def warmreset(self): 123 | return self.warm_reset() 124 | 125 | def cold_reset(self): 126 | raise CliAccessException() 127 | 128 | @deprecated("Please use method cold_reset") 129 | def coldreset(self): 130 | return self.cold_reset() 131 | 132 | def mem_block(self, address, size): 133 | raise CliAccessException() 134 | 135 | @deprecated("Please use method mem_block") 136 | def memBlock(self, address, size): 137 | return self.mem_block(address, size) 138 | 139 | def mem_save(self, filename, address, size): 140 | raise CliAccessException() 141 | 142 | @deprecated("Please use method mem_save") 143 | def memsave(self, filename, address, size): 144 | return self.mem_save(filename, address, size) 145 | 146 | def mem_read(self, address, size): 147 | raise CliAccessException() 148 | 149 | @deprecated("Please use method mem_read") 150 | def memread(self, address, size): 151 | return self.mem_read(address, size) 152 | 153 | def mem_write(self, address, size, value): 154 | raise CliAccessException() 155 | 156 | @deprecated("Please use method mem_write") 157 | def memwrite(self, address, size, value): 158 | self.mem_write(address, size, value) 159 | 160 | def load_data(self, filename, address): 161 | raise CliAccessException() 162 | 163 | def read_io(self, address, size): 164 | raise CliAccessException() 165 | 166 | @deprecated("Please use method read_io") 167 | def readIO(self, address, size): 168 | return self.read_io(address, size) 169 | 170 | def write_io(self, address, size, value): 171 | raise CliAccessException() 172 | 173 | @deprecated("Please use method write_io") 174 | def writeIO(self, address, size, value): 175 | return self.write_io(address, size, value) 176 | 177 | def trigger_smi(self, smi_value): 178 | """ 179 | Trigger Software (S/W) SMI of desired value 180 | :param smi_value: value of S/W SMI 181 | """ 182 | raise CliAccessException() 183 | 184 | @deprecated("Please use method trigger_smi") 185 | def triggerSMI(self, SmiVal): 186 | """Trigger S/W SMI of desired value""" 187 | return self.trigger_smi(SmiVal) 188 | 189 | def read_msr(self, Ap, address): 190 | """ 191 | Read MSR value at given address 192 | :param Ap: 193 | :param address: MSR Address 194 | """ 195 | raise CliAccessException() 196 | 197 | @deprecated("Please use method read_msr") 198 | def ReadMSR(self, Ap, MSR_Addr): 199 | return self.read_msr(Ap, MSR_Addr) 200 | 201 | def write_msr(self, Ap, address, value): 202 | """ 203 | Read MSR value at given address 204 | :param Ap: 205 | :param address: MSR Address 206 | :param value: Value to be written at MSR Address 207 | """ 208 | raise CliAccessException() 209 | 210 | @deprecated("Please use method write_msr") 211 | def WriteMSR(self, Ap, MSR_Addr, MSR_Val): 212 | return self.write_msr(Ap, MSR_Addr, MSR_Val) 213 | 214 | def read_sm_base(self): 215 | raise CliAccessException() 216 | 217 | @deprecated("Please use method read_sm_base") 218 | def ReadSmbase(self): 219 | return self.read_sm_base() 220 | 221 | @staticmethod 222 | def is_thread_alive(thread): 223 | raise CliAccessException() 224 | 225 | @deprecated("Please use method is_thread_alive") 226 | def isThreadAlive(self, thread): 227 | return self.is_thread_alive(thread) 228 | 229 | 230 | @deprecated("Please use class BaseAccess") 231 | class CliAccess(BaseAccess): 232 | pass 233 | -------------------------------------------------------------------------------- /src/xmlcli/access/base/baseTest.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | As Base Test method demonstrating contributors guidelines 4 | and example to write test cases. 5 | """ 6 | import os 7 | import unittest 8 | from .base import BaseAccess 9 | 10 | 11 | class BaseTestCase(unittest.TestCase): 12 | def setUp(self): 13 | """Initialize interface setup for usage in test methods 14 | 15 | :return: 16 | """ 17 | access_method_name = "base" 18 | self.current_directory = os.path.dirname(os.path.abspath(__file__)) 19 | self.base_object = BaseAccess(access_name=access_method_name, child_class_directory=self.current_directory) 20 | self.assertEqual(self.base_object.interface, access_method_name) 21 | self.assertEqual(self.base_object.interface, self.base_object.InterfaceType) 22 | 23 | def test_mem_read(self): 24 | result = self.base_object.mem_read(0xFFFFFFC0, 4) 25 | self.assertGreaterEqual(result, 0xFF000000, "Read value not as expected") 26 | value = self.base_object.mem_read(result, 8) 27 | self.assertEqual(value, int.from_bytes(bytes('_FIT_ '.encode()), "little"), "Read Signature mismatch") 28 | 29 | def test_mem_write(self): 30 | write_val = 0xff 31 | result = self.base_object.mem_write(0x43760000, 1, write_val) 32 | read_val = self.base_object.mem_read(0x43760000, 1) 33 | self.assertEqual(read_val, write_val, "Write Value does not match!!") 34 | 35 | def test_io_operation(self): 36 | write_val = 0xf1 37 | self.base_object.write_io(0x70, 1, write_val) 38 | read_val = self.base_object.read_io(0x70, 1) 39 | self.assertEqual(read_val, write_val, "Write Value does not match!!") 40 | 41 | 42 | if __name__ == '__main__': 43 | unittest.main() 44 | -------------------------------------------------------------------------------- /src/xmlcli/access/generic/README.md: -------------------------------------------------------------------------------- 1 | # Generic METHOD 2 | 3 | This is a basic skeleton class for various IPC/JTAG methods consuming similar implementation of method calls 4 | 5 | | Description | Details | 6 | | ------------- | ------------------------ | 7 | | Access Method | `generic` | 8 | | Folder Name | `generic` | 9 | | Configuration | `generic/generic.ini` | 10 | | Documentation | `generic/README.md` | 11 | | Unit Test | `generic/genericTest.py` | 12 | 13 | 14 | ## Dependencies: 15 | 16 | > N/A 17 | -------------------------------------------------------------------------------- /src/xmlcli/access/generic/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | 6 | # Custom imports 7 | 8 | 9 | if __name__ == "__main__": 10 | pass 11 | -------------------------------------------------------------------------------- /src/xmlcli/access/generic/generic.ini: -------------------------------------------------------------------------------- 1 | # Config file produced to map base files 2 | [GENERAL] 3 | # specify sections to be read (comma separated) 4 | access_methods = GENERIC 5 | 6 | [GENERIC] 7 | # Name of file to be use to import class for this access method 8 | file = generic.py 9 | doc = README.md 10 | method_class = GenericAccess 11 | -------------------------------------------------------------------------------- /src/xmlcli/access/generic/generic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | __author__ = ["ashinde", "Gahan Saraiya"] 4 | 5 | # Built-in imports 6 | import os 7 | import time 8 | import binascii 9 | 10 | # Custom imports 11 | from ..base import base 12 | 13 | __all__ = ["CommonCliAccess"] 14 | 15 | MAX_READ_SIZE = 0x400 16 | 17 | 18 | class CommonCliAccess(base.BaseAccess): 19 | def __init__(self, access_name, child_class_directory): 20 | super(CommonCliAccess, self).__init__(access_name, child_class_directory=child_class_directory) 21 | self.level_dal = 0 22 | self.is_running = 0x00 # initialize IsRunning [bitmap] global variable to False for all bits 23 | self.thread = None 24 | self.access = None 25 | self._set_access() 26 | self.access.vp = 0 27 | self.access.base = 16 if not hasattr(self.access, 'base') else self.access.base 28 | thread_list = self.get_alive_threads() 29 | self.set_thread(thread_list[0]) 30 | 31 | def _set_access(self): 32 | if not self.access: 33 | raise base.CliAccessException() 34 | 35 | @property 36 | @base.deprecated("Please use variable is_running") 37 | def IsRunning(self): 38 | # this method is for backward compatibility 39 | return self.is_running 40 | 41 | @property 42 | @base.deprecated("Please use variable is_running") 43 | def level_Dal(self): 44 | # this method is for backward compatibility 45 | return self.level_dal 46 | 47 | def halt_cpu(self, delay=0): 48 | time.sleep(delay) 49 | if not self.access.threads: 50 | raise base.CliAccessException(message="halt cpu: Really???", hints=["There are no existing CPU threads?", "Thanks, DAL."]) 51 | if self.thread.cv.isrunning: 52 | try: 53 | self.access.halt() 54 | except Exception as e: 55 | pass 56 | 57 | def run_cpu(self): 58 | if not self.thread.cv.isrunning: 59 | self.access.go() 60 | return 0 61 | 62 | def initialize_interface(self): 63 | thread_list = self.get_alive_threads() 64 | self.set_thread(thread_list[0]) 65 | self.is_running = (self.is_running & (~(0x1 << self.level_dal) & 0xFF)) + ((self.thread.cv.isrunning & 0x1) << self.level_dal) 66 | self.halt_cpu() 67 | self.level_dal = self.level_dal + 1 68 | return 0 69 | 70 | def close_interface(self): 71 | if (self.is_running >> (self.level_dal - 1)) & 0x1: 72 | self.run_cpu() 73 | else: 74 | self.halt_cpu() 75 | self.level_dal = self.level_dal - 1 76 | return 0 77 | 78 | def warm_reset(self): 79 | self.access.resettarget() 80 | 81 | def cold_reset(self): 82 | self.access.pulsepwrgood() 83 | 84 | def mem_block(self, address, size): 85 | result = self.thread.memblock(hex(address).rstrip('L') + 'p', size, 0) 86 | return binascii.unhexlify((hex(result)[2:]).zfill(size*2))[::-1] 87 | 88 | def mem_save(self, filename, address, size): 89 | # Due to a bug in IPC (Lauterbach) relative path names do not resolve correctly. To adjust for this, all files must be absolute 90 | if not os.path.isabs(filename): 91 | filename = os.path.abspath(filename) 92 | self.thread.memsave(filename, hex(address).rstrip('L')+'p', size, 1) 93 | 94 | def mem_read(self, address, size): 95 | return self.thread.mem(hex(address).rstrip('L') + 'p', size) 96 | 97 | def mem_write(self, address, size, value): 98 | self.thread.mem(hex(address).rstrip('L') + 'p', size, value) 99 | 100 | def load_data(self, filename, address): 101 | # Due to a bug in IPC (Lauterbach) relative path names do not resolve correctly. To adjust for this, all files must be absolute 102 | if not os.path.isabs(filename): 103 | filename = os.path.abspath(filename) 104 | self.thread.memload(filename, hex(address).rstrip('L') + 'p') 105 | 106 | def read_io(self, address, size): 107 | if size not in (1, 2, 4): 108 | raise base.CliOperationException(f"Invalid size to read from io port address: 0x{address:x}") 109 | if size == 1: 110 | return self.thread.port(address) 111 | if size == 2: 112 | return self.thread.wport(address) 113 | if size == 4: 114 | return self.thread.dport(address) 115 | 116 | def write_io(self, address, size, value): 117 | if size not in (1, 2, 4): 118 | raise base.CliOperationException(f"Invalid size to write from io port address: 0x{address:x}") 119 | if size == 1: 120 | self.thread.port(address, value) 121 | if size == 2: 122 | self.thread.wport(address, value) 123 | if size == 4: 124 | self.thread.dport(address, value) 125 | 126 | def trigger_smi(self, smi_value): 127 | self.halt_cpu() 128 | self.write_io(base.SMI_TRIGGER_PORT, 1, smi_value) 129 | self.run_cpu() 130 | 131 | def read_msr(self, Ap, address): 132 | return self.access.threads[Ap].msr(address) 133 | 134 | def write_msr(self, Ap, address, value): 135 | return self.access.threads[Ap].msr(address, value) 136 | 137 | def read_sm_base(self): 138 | self.halt_cpu() 139 | return self.thread.msr(0x171) 140 | 141 | def get_alive_threads(self): 142 | return list(filter(self.isThreadAlive, self.access.threads)) 143 | 144 | def get_thread_by_number(self, thread_number, **kwargs): 145 | # [cli-2.0.0]: use case of condition???, turns out all will be eventually be True!! 146 | if 'socketNum' in kwargs: 147 | kwargs['domainNum'] = 0 148 | kwargs['packageNum'] = kwargs['socketNum'] 149 | kwargs['dieNum'] = 0 150 | if 'domainNum' in kwargs: 151 | pobj = self.access.domains[kwargs['domainNum']] 152 | if 'packageNum' in kwargs: 153 | pobj = pobj.packages[kwargs['packageNum']] 154 | if 'dieNum' in kwargs: 155 | pobj = pobj.dies[kwargs['dieNum']] 156 | if 'coreNum' in kwargs: 157 | pobj = pobj.cores[kwargs['coreNum']] 158 | thread_list = pobj.getAllByType('thread') 159 | elif 'coreNum' in kwargs: 160 | thread_list = self.access.cores[kwargs['coreNum']].getAllByType('thread') 161 | else: 162 | thread_list = self.access.threads 163 | return thread_list[thread_number] 164 | 165 | def set_thread(self, thread): 166 | self.thread = thread 167 | 168 | @base.deprecated("Please use method get_alive_threads") 169 | def getAliveThreads(self): 170 | return self.get_alive_threads() 171 | 172 | @base.deprecated("Please use method get_thread_by_number") 173 | def getThreadByNumber(self, thread_number, **kwargs): 174 | return self.get_thread_by_number(thread_number, **kwargs) 175 | 176 | @base.deprecated("Please use method set_thread") 177 | def setThread(self, thread): 178 | return self.set_thread(thread) 179 | -------------------------------------------------------------------------------- /src/xmlcli/access/generic/genericTest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | __author__ = "Gahan Saraiya" 4 | 5 | import os 6 | import unittest 7 | from .generic import CommonCliAccess 8 | 9 | 10 | class GenericTestCase(unittest.TestCase): 11 | def test_initialize_interface(self): 12 | access_method_name = "generic" 13 | self.current_directory = os.path.dirname(os.path.abspath(__file__)) 14 | self.base_object = CommonCliAccess(access_name=access_method_name, child_class_directory=self.current_directory) 15 | self.assertFalse("This Access Method is not meant to be used directly") 16 | 17 | 18 | if __name__ == '__main__': 19 | unittest.main() 20 | -------------------------------------------------------------------------------- /src/xmlcli/access/linux/Makefile: -------------------------------------------------------------------------------- 1 | all: port.c mymem.c; gcc -shared -o libport.lso -fPIC port.c -I.; gcc -shared -o libmem.lso -fPIC mymem.c -I. 2 | -------------------------------------------------------------------------------- /src/xmlcli/access/linux/README.md: -------------------------------------------------------------------------------- 1 | # Linux METHOD 2 | 3 | This is access method used for OS with Linux kernel. 4 | Prerequisites would be that OS should expose below files for `root` user (sudoers): 5 | - `/dev/mem` to allow reading physical memory 6 | 7 | | Description | Details | 8 | |---------------|----------------------| 9 | | Access Method | `linux` | 10 | | Folder Name | `linux` | 11 | | Configuration | `linux/linux.ini` | 12 | | Documentation | `linux/README.md` | 13 | | Unit Test | `linux/linuxTest.py` | 14 | 15 | 16 | ## Dependencies: 17 | 18 | List out dependencies binaries in order with details. It must answer to basic 19 | question Why is it used?, What is the impact if we don't use it? 20 | 21 | 1. `libport.lso` 22 | Linux Shared library file generated from source `port.c` using below commands: 23 | ```shell 24 | # 1. Compile `C` source code file to object file 25 | gcc -c -o port.o port.c 26 | # 2. Create shared library from object file created in Step 1. 27 | gcc -shared -o libport.lso port.o 28 | ``` 29 | 30 | #### Alternatively to generate library for memory and port use below `Makefile` as below 31 | 32 | 1. Make sure you are under directory where `Makefile`, `port.c` and `mymem.c` are present i.e. `/linux` 33 | 2. Run below shell command: 34 | ```shell 35 | make 36 | ``` 37 | -------------------------------------------------------------------------------- /src/xmlcli/access/linux/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | 6 | # Custom imports 7 | 8 | 9 | if __name__ == "__main__": 10 | pass 11 | -------------------------------------------------------------------------------- /src/xmlcli/access/linux/libmem.lso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/access/linux/libmem.lso -------------------------------------------------------------------------------- /src/xmlcli/access/linux/libport.lso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/access/linux/libport.lso -------------------------------------------------------------------------------- /src/xmlcli/access/linux/linux.ini: -------------------------------------------------------------------------------- 1 | # Config file produced to map base files 2 | [GENERAL] 3 | # specify sections to be read (comma separated) 4 | access_methods = LINUX 5 | 6 | [LINUX] 7 | # Name of file to be use to import class for this access method 8 | file = linux.py 9 | doc = README.md 10 | method_class = LinuxAccess 11 | lib_mem = libmem.lso 12 | lib_port = libport.lso 13 | memory_file = /dev/mem 14 | # Set below value to True if usage expected to run from lib_mem 15 | external_mem = True 16 | -------------------------------------------------------------------------------- /src/xmlcli/access/linux/linux.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | __author__ = "Gahan Saraiya" 4 | 5 | # Built-in imports 6 | import os 7 | import mmap 8 | import ctypes 9 | import binascii 10 | 11 | # Custom imports 12 | from ..base import base 13 | 14 | __all__ = ["LinuxAccess"] 15 | 16 | 17 | class LinuxAccess(base.BaseAccess): 18 | def __init__(self, access_name="linux"): 19 | self.current_directory = os.path.dirname(os.path.abspath(__file__)) 20 | super(LinuxAccess, self).__init__(access_name=access_name, child_class_directory=self.current_directory) 21 | self.memory_file = self.config.get(access_name.upper(), "memory_file") 22 | self.map_mask = mmap.PAGESIZE - 1 23 | # Read Port library 24 | self.port_lib_location = os.path.join(self.current_directory, self.config.get(access_name.upper(), "lib_port")) 25 | self.port_library = ctypes.CDLL(self.port_lib_location) 26 | # Read Port configuration 27 | self._read_port = self.port_library.read_port 28 | self._read_port.argtypes = (ctypes.c_uint16, ctypes.c_uint8) 29 | self._read_port.restype = (ctypes.POINTER(ctypes.c_uint8)) 30 | # Write port configuration 31 | self._write_port = self.port_library.write_port 32 | self._write_port.argtypes = (ctypes.c_uint16, ctypes.c_uint8, ctypes.c_uint32) 33 | self.external_mem = self.config.getboolean(access_name.upper(), "external_mem") 34 | if self.external_mem: 35 | self.lib_mem = os.path.join(self.current_directory, self.config.get(access_name.upper(), "lib_mem")) 36 | self.mem_library = ctypes.CDLL(self.lib_mem) 37 | # Read Memory configuration 38 | self._read_mem = self.mem_library.mem_read 39 | self._read_mem.argtypes = (ctypes.c_ulong, ctypes.c_void_p, ctypes.c_size_t) 40 | self._read_mem.restype = ctypes.c_int 41 | # Write Memory configuration 42 | self._write_mem = self.mem_library.mem_write 43 | self._write_mem.argtypes = (ctypes.c_ulong, ctypes.c_void_p, ctypes.c_size_t) 44 | self._write_mem.restype = ctypes.c_int 45 | 46 | def halt_cpu(self, delay=0): 47 | return 0 48 | 49 | def run_cpu(self): 50 | return 0 51 | 52 | def initialize_interface(self): 53 | return 0 54 | 55 | def close_interface(self): 56 | return 0 57 | 58 | def read_port(self, port, size): 59 | read_val = 0 60 | ret = self._read_port(port, size) 61 | if ret: 62 | for i in range(0, size): 63 | read_val += ret[i] << 8 * (i) 64 | return read_val 65 | 66 | def io(self, port, size, val=None): 67 | if val is None: 68 | read_val = self.read_port(port, size) 69 | return int(read_val) 70 | else: 71 | ret = self._write_port(port, size, val) 72 | return ret 73 | 74 | def read_memory_bytes(self, address, size): 75 | mem_file_obj = os.open(self.memory_file, os.O_RDWR | os.O_SYNC) 76 | mem = mmap.mmap(mem_file_obj, mmap.PAGESIZE, mmap.MAP_SHARED, mmap.PROT_WRITE | mmap.PROT_READ, offset=address & ~self.map_mask) 77 | data = None 78 | try: 79 | mem.seek(address & self.map_mask) 80 | data = mem.read(size) 81 | mem.close() 82 | os.close(mem_file_obj) 83 | return data 84 | except Exception as e: # catch any kind of exception and close /dev/mem file 85 | mem.close() 86 | os.close(mem_file_obj) 87 | if data is None: 88 | raise Exception("Unable to read memory on the platform") 89 | return data 90 | 91 | def read_memory(self, address, size): 92 | if self.external_mem: 93 | dest = (ctypes.c_ubyte * size)() 94 | self._read_mem(address, ctypes.cast(dest, ctypes.c_void_p), size) 95 | result = 0 96 | ctypes.cast(dest, ctypes.c_char_p) 97 | for i in range(0, size): 98 | result += dest[i] << 8 * i 99 | else: 100 | result = self.read_memory_bytes(address, size) 101 | result = int.from_bytes(result, byteorder="little", signed=False) 102 | return result 103 | 104 | def write_memory_bytes(self, address, data, size): 105 | mem_file_obj = os.open(self.memory_file, os.O_RDWR | os.O_SYNC) 106 | mem = mmap.mmap(mem_file_obj, mmap.PAGESIZE, mmap.MAP_SHARED, mmap.PROT_WRITE | mmap.PROT_READ, offset=address & ~self.map_mask) 107 | bytes_written = 0 108 | try: 109 | data_dump = data if isinstance(data, bytes) else self.int_to_byte(data, size) 110 | if data_dump: 111 | mem.seek(address & self.map_mask) 112 | bytes_written = mem.write(data_dump) 113 | mem.close() 114 | os.close(mem_file_obj) 115 | return data 116 | except Exception as e: # catch any kind of exception and close /dev/mem file 117 | mem.close() 118 | os.close(mem_file_obj) 119 | if bytes_written == 0: 120 | raise Exception("Unable to write memory on the platform") 121 | return bytes_written 122 | 123 | def write_memory(self, address, data, size): 124 | if self.external_mem: 125 | if isinstance(data, int): 126 | data = ctypes.c_ulonglong(data) 127 | _data = ctypes.byref(data) 128 | else: 129 | _data = data 130 | bytes_written = self._write_mem(address, _data, size) 131 | else: 132 | bytes_written = self.write_memory_bytes(address, data, size) 133 | return bytes_written 134 | 135 | def mem(self, address, size, val=None): 136 | if val is None: 137 | read_val = self.read_memory(address, size) 138 | return int(read_val) 139 | else: 140 | ret = self.write_memory(address, val, size) 141 | return ret 142 | 143 | def read_memory_block(self, address, size, val=None): 144 | if val is None: 145 | read_val = self.read_memory(address, size) 146 | return binascii.unhexlify(hex(read_val)[2:].strip('L').zfill(size * 2))[::-1] 147 | else: 148 | ret = self.write_memory(address, val, size) 149 | return ret 150 | 151 | def warm_reset(self): 152 | self.io(0xCF9, 1, 0x06) 153 | 154 | def cold_reset(self): 155 | self.io(0xCF9, 1, 0x0E) 156 | 157 | def mem_block(self, address, size): 158 | end_address = address + size 159 | temp_address = address & 0xFFFFF000 160 | result1 = [] 161 | if (end_address - temp_address) <= 0x1000: 162 | result = self.read_memory_block(address, size) 163 | result1.extend(result) 164 | else: 165 | first_end_page_address = (address + 0xFFF) & 0xFFFFF000 166 | if first_end_page_address > address: 167 | result = self.read_memory_block(address, (first_end_page_address - address)) 168 | result1.extend(result) 169 | block_count = 0 170 | block_size = (end_address - first_end_page_address) 171 | block_number = int(block_size/0x1000) 172 | for block_count in range(0, block_number): 173 | result = self.read_memory_block(first_end_page_address + (block_count * 0x1000), 0x1000) 174 | result1.extend(result) 175 | if block_number != 0: 176 | block_count = block_count+1 177 | if block_size % 0x1000: 178 | result = self.read_memory_block(first_end_page_address + (block_count * 0x1000), (block_size % 0x1000)) 179 | result1.extend(result) 180 | return bytearray(result1) 181 | 182 | def mem_save(self, filename, address, size): 183 | temp_buffer = self.mem_block(address, size) 184 | with open(filename, 'wb') as out_file: # opening for writing 185 | out_file.write(temp_buffer) 186 | 187 | def mem_read(self, address, size): 188 | return self.mem(address, size) # list of size entries of 1 Byte 189 | 190 | def mem_write(self, address, size, value): 191 | self.mem(address, size, value) # list of size entries of 1 Byte 192 | 193 | def load_data(self, filename, address): 194 | with open(filename, 'rb') as in_file: # opening for [r]eading as [b]inary 195 | data = in_file.read() # if you only wanted to read 512 bytes, do .read(512) 196 | size = len(data) 197 | self.read_memory_block(address, size, data) # list of size entries of 1 Byte 198 | 199 | def read_io(self, address, size): 200 | return self.io(address, size) 201 | 202 | def write_io(self, address, size, value): 203 | self.io(address, size, value) 204 | 205 | def trigger_smi(self, smi_value): 206 | self.io(0xB2, 1, smi_value) 207 | 208 | def read_msr(self, Ap, address): 209 | return 0 210 | 211 | def write_msr(self, Ap, address, value): 212 | return 0 213 | 214 | def read_sm_base(self): 215 | return 0 216 | -------------------------------------------------------------------------------- /src/xmlcli/access/linux/linuxTest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | __author__ = "Gahan Saraiya" 4 | 5 | import unittest 6 | from .linux import LinuxAccess 7 | 8 | 9 | class LinuxTestCase(unittest.TestCase): 10 | def test_initialize_interface(self): 11 | access_method_name = "linux" 12 | self.base_object = LinuxAccess(access_name=access_method_name) 13 | self.assertEqual(self.base_object.interface, access_method_name) 14 | self.assertEqual(self.base_object.interface, self.base_object.InterfaceType) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /src/xmlcli/access/linux/mymem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int mem_read(const unsigned long, void const *, const size_t); 7 | int mem_write(const unsigned long, const void const *, const size_t); 8 | 9 | static int mem_fh = -1; 10 | 11 | static int mem_open() { 12 | int res; 13 | 14 | // Our file handler to /dev/mem is already opened 15 | if (mem_fh != -1) { 16 | return errno = EBADF; 17 | } 18 | 19 | // Constantly try to open the /dev/mem file, taking into account any system interrupts we get 20 | do { 21 | res = open("/dev/mem", O_RDWR | O_SYNC); 22 | } while (res == -1 && errno == EINTR); 23 | 24 | // If we had an issue opening the file, and no system interrupt occurred, then return the errno 25 | if (res == -1) { 26 | return errno; 27 | } 28 | 29 | // Set our file handler to /dev/mem and return 0 30 | mem_fh = res; 31 | return 0; 32 | } 33 | 34 | int mem_read(const unsigned long addr, void const *dest, const size_t bytes) { 35 | 36 | // Cast off our const arguments 37 | unsigned char *d = (unsigned char *) dest; 38 | off_t off = (off_t) addr; 39 | size_t n = bytes; 40 | ssize_t b_read; 41 | 42 | while (n) { 43 | 44 | // Read n bytes from /dev/mem starting at offset equal to our address and store it in dest 45 | b_read = pread(mem_fh, d, n, off); 46 | 47 | // If we read all n bytes in a single read, then return 0 48 | if (b_read == (ssize_t) n) { 49 | return 0; 50 | } 51 | 52 | else if (b_read >= (ssize_t) 0) { 53 | // We need to set up for the next read 54 | d += b_read; // We now need to start r to base_dest + b 55 | off += b_read; // We now need to read from base_addr + b 56 | n -= b_read; // We now need to only read n-b more bytes 57 | } 58 | 59 | // If we have a bad file descriptor, try to re-open the file 60 | else if(errno == EBADF) { 61 | if(mem_open()) { 62 | return errno; 63 | } 64 | } 65 | 66 | // If we get something < -1 then we have some IO error 67 | else if(b_read != (ssize_t)-1) { 68 | return errno = EIO; 69 | } 70 | } 71 | 72 | return 0; 73 | } 74 | 75 | int mem_write(const unsigned long addr, const void *source, const size_t bytes) { 76 | 77 | // Cast off our const arguments 78 | unsigned char *s = (unsigned char *) source; 79 | off_t off = (off_t) addr; 80 | size_t n = bytes; 81 | ssize_t b_written; 82 | 83 | while(n) { 84 | // Write n bytes from source to /dev/mem using our address as offset 85 | b_written = pwrite(mem_fh, s, n, off); 86 | 87 | // If we wrote all n bytes in a single write, then return 0 88 | if (b_written == (ssize_t) n) { 89 | return 0; 90 | } 91 | 92 | else if (b_written >= (ssize_t) 0) { 93 | // We need to set up for the next write 94 | s += b_written; // We now need to start reading to base_source + b 95 | off += b_written; // We now need to write to base_addr + b 96 | n -= b_written; // We now need to only write n-b more bytes 97 | } 98 | 99 | // If we have a bad file descriptor, try to re-open the file 100 | else if (errno == EBADF) { 101 | if (mem_open()) { 102 | return errno; 103 | } 104 | } 105 | 106 | // If we get something < -1 then we have some IO error 107 | else if (b_written != (ssize_t)-1) { 108 | return errno = EIO; 109 | } 110 | } 111 | 112 | return 0; 113 | } 114 | -------------------------------------------------------------------------------- /src/xmlcli/access/linux/port.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | uint8_t *read_port(uint16_t port, uint8_t size) 8 | { 9 | // Get access to all ports on the system 10 | iopl(3); 11 | 12 | // Allocate our array to hold port reads 13 | uint8_t *val = malloc(sizeof(uint8_t) * size); 14 | 15 | if (!val) { // handle condition if malloc referenced to null pointer reference 16 | free(val); 17 | return 0; 18 | } else { 19 | // Read one byte from each port up to the size we want 20 | int r; 21 | for(r = 0; r < size; r++) 22 | { 23 | uint8_t byte = inb(port + r); 24 | val[r] = byte; 25 | } 26 | 27 | return val; 28 | } 29 | } 30 | 31 | void write_port(uint16_t port, uint8_t size, uint32_t val) 32 | { 33 | // Get access to all ports on the system 34 | iopl(3); 35 | 36 | int r; 37 | for(r = 0; r < size; r++) 38 | { 39 | uint8_t byteToWrite = ((val >> (8 * r)) & 0xFF); 40 | outb(byteToWrite, port + r); 41 | } 42 | } 43 | 44 | int main() 45 | { 46 | /* 47 | iopl(3); 48 | 49 | unsigned short rport; 50 | unsigned short wport = 0x500U; 51 | uint8_t val = 6; 52 | 53 | printf("Please enter a port: "); 54 | scanf("%hu", &rport); 55 | printf("Reading port %0x\n", rport); 56 | uint8_t byte = inb(rport); 57 | printf("Value: 0x%0x\n", byte); 58 | 59 | printf("Writing 0x%0x to port %0x\n", val, wport); 60 | outb(val, wport); 61 | 62 | write_port(0xCF9, 1, 0x6); 63 | 64 | uint8_t size = 4; 65 | uint8_t *read = read_port(0xC000, size); 66 | 67 | int i = 0; 68 | for(i = 0; i < size; i++) 69 | { 70 | printf("%02X", read[i]); 71 | } 72 | printf("\n"); 73 | 74 | return 0;*/ 75 | 76 | return 0; 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/xmlcli/access/stub/README.md: -------------------------------------------------------------------------------- 1 | # Stub METHOD 2 | 3 | This is access method is fallback access method for `offline` usage. 4 | 5 | 6 | | Description | Details | 7 | |---------------|--------------------| 8 | | Access Method | `stub` | 9 | | Folder Name | `stub` | 10 | | Configuration | `stub/stub.ini` | 11 | | Documentation | `stub/README.md` | 12 | | Unit Test | `stub/stubTest.py` | 13 | 14 | 15 | ## Dependencies: 16 | 17 | > N/A 18 | -------------------------------------------------------------------------------- /src/xmlcli/access/stub/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | 6 | # Custom imports 7 | 8 | 9 | if __name__ == "__main__": 10 | pass 11 | -------------------------------------------------------------------------------- /src/xmlcli/access/stub/stub.ini: -------------------------------------------------------------------------------- 1 | # Config file produced to map base files 2 | [GENERAL] 3 | # specify sections to be read (comma separated) 4 | access_methods = STUB 5 | 6 | [STUB] 7 | # Name of file to be use to import class for this access method 8 | file = stub.py 9 | doc = README.md 10 | method_class = StubAccess 11 | -------------------------------------------------------------------------------- /src/xmlcli/access/stub/stub.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | __author__ = "Gahan Saraiya" 4 | 5 | # Built-in imports 6 | import os 7 | 8 | # Custom imports 9 | from ..base import base 10 | 11 | __all__ = ["StubAccess"] 12 | 13 | 14 | class StubAccess(base.BaseAccess): 15 | def __init__(self, access_name="stub"): 16 | self.current_directory = os.path.dirname(os.path.abspath(__file__)) 17 | super(StubAccess, self).__init__(access_name=access_name, child_class_directory=self.current_directory) 18 | 19 | def halt_cpu(self, delay=0): 20 | return 0 21 | 22 | def run_cpu(self): 23 | return 0 24 | 25 | def initialize_interface(self): 26 | return 0 27 | 28 | def close_interface(self): 29 | return 0 30 | 31 | def warm_reset(self): 32 | return 0 33 | 34 | def cold_reset(self): 35 | return 0 36 | 37 | def mem_block(self, address, size): 38 | return 0 39 | 40 | def mem_save(self, filename, address, size): 41 | return 0 42 | 43 | def mem_read(self, address, size): 44 | return 0 45 | 46 | def mem_write(self, address, size, value): 47 | return 0 48 | 49 | def load_data(self, filename, address): 50 | return 0 51 | 52 | def read_io(self, address, size): 53 | return 0 54 | 55 | def write_io(self, address, size, value): 56 | return 0 57 | 58 | def trigger_smi(self, smi_value): 59 | return 0 60 | 61 | def read_msr(self, Ap, address): 62 | return 0 63 | 64 | def write_msr(self, Ap, address, value): 65 | return 0 66 | 67 | def read_sm_base(self): 68 | return 0 69 | -------------------------------------------------------------------------------- /src/xmlcli/access/stub/stubTest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | __author__ = "Gahan Saraiya" 4 | 5 | import unittest 6 | from .stub import StubAccess 7 | 8 | 9 | class StubTestCase(unittest.TestCase): 10 | def test_initialize_interface(self): 11 | access_method_name = "stub" 12 | self.base_object = StubAccess(access_name=access_method_name) 13 | self.assertEqual(self.base_object.interface, access_method_name) 14 | self.assertEqual(self.base_object.interface, self.base_object.InterfaceType) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /src/xmlcli/access/winrwe/README.md: -------------------------------------------------------------------------------- 1 | # WinRWe METHOD 2 | 3 | This is access method used for Windows OS Runtime. 4 | 5 | Prerequisites: 6 | - Download Portable version of RWEverything compatible to your system from [http://rweverything.com/download/](http://rweverything.com/download/): 7 | 8 | | Description | Details | 9 | |---------------|------------------------| 10 | | Access Method | `winrwe` | 11 | | Folder Name | `winrwe` | 12 | | Configuration | `winrwe/winrwe.ini` | 13 | | Documentation | `winrwe/README.md` | 14 | | Unit Test | `winrwe/winrweTest.py` | 15 | 16 | > Note: Limitation of this interface is that you should not use any system path which may have **space character (`' '`)** in it. 17 | 18 | ## Dependencies: 19 | 20 | 1. `RW_EXE` under configuration `winrwe.ini` 21 | 2. `RW_INI` under configuration `winrwe.ini` 22 | 1. RWEverything Read & Write Everything 23 | Compressed file can be acquired from: http://rweverything.com/download/ 24 | 2. Make sure to extract executable under the appropriate relative path 25 | - If you are placing it at some unknown location or renaming file please 26 | make sure to modify value of key `RW_EXE` under configuration `winrwe.ini` 27 | 3. `TEMP_DATA_BIN` 28 | 1. Location to store temporary Data 29 | 4. `RESULT_TEXT` 30 | 1. Redirecting result to text location 31 | -------------------------------------------------------------------------------- /src/xmlcli/access/winrwe/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | 6 | # Custom imports 7 | 8 | 9 | if __name__ == "__main__": 10 | pass 11 | -------------------------------------------------------------------------------- /src/xmlcli/access/winrwe/winrwe.ini: -------------------------------------------------------------------------------- 1 | # Config file produced to map base files 2 | [GENERAL] 3 | # specify sections to be read (comma separated) 4 | access_methods = WINRWE 5 | 6 | [WINRWE] 7 | # Name of file to be use to import class for this access method 8 | file = winrwe.py 9 | doc = README.md 10 | method_class = WinRweAccess 11 | RW_EXE = Rw.exe 12 | RW_INI = Rw.ini 13 | TEMP_DATA_BIN = TmpData.bin 14 | RESULT_TEXT = ResOut.txt 15 | 16 | -------------------------------------------------------------------------------- /src/xmlcli/access/winrwe/winrwe.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | import os 6 | import binascii 7 | 8 | # Custom imports 9 | from ..base import base 10 | # Conditional Imports 11 | 12 | __all__ = ["WinRweAccess"] 13 | 14 | 15 | class WinRweAccess(base.BaseAccess): 16 | def __init__(self, access_name="winrwe"): 17 | self.current_directory = os.path.dirname(os.path.abspath(__file__)) 18 | super(WinRweAccess, self).__init__(access_name=access_name, child_class_directory=self.current_directory) 19 | self.rw_executable = self.config.get(access_name.upper(), "RW_EXE") 20 | self.temp_data_bin = self.config.get(access_name.upper(), "TEMP_DATA_BIN") 21 | self.result_text = self.config.get(access_name.upper(), "RESULT_TEXT") 22 | 23 | def halt_cpu(self, delay=0): 24 | return 0 25 | 26 | def run_cpu(self): 27 | return 0 28 | 29 | def initialize_interface(self): 30 | return 0 31 | 32 | def close_interface(self): 33 | return 0 34 | 35 | def warm_reset(self): 36 | os.system('{} /Nologo /Min /Command="O 0xCF9 0x06; RwExit"'.format(self.rw_executable)) 37 | 38 | def cold_reset(self): 39 | os.system('{} /Nologo /Min /Command="O 0xCF9 0x0E; RwExit"'.format(self.rw_executable)) 40 | 41 | def mem_block(self, address, size): 42 | os.system('{} /Nologo /Min /Command="SAVE {} Memory 0x{:x} 0x{:x}; RwExit"'.format(self.rw_executable, self.temp_data_bin, address, size)) 43 | with open(self.temp_data_bin, 'rb') as f: 44 | data_buffer = f.read() 45 | return data_buffer 46 | 47 | def mem_save(self, filename, address, size): 48 | os.system('{} /Nologo /Min /Command="SAVE {} Memory 0x{:x} 0x{:x}; RwExit"'.format(self.rw_executable, filename, address, size)) 49 | 50 | def mem_read(self, address, size): 51 | os.system('{} /Nologo /Min /Command="SAVE {} Memory 0x{:x} 0x{:x}; RwExit"'.format(self.rw_executable, self.temp_data_bin, address, size)) 52 | with open(self.temp_data_bin, 'rb') as f: 53 | data_buffer = f.read() 54 | return int(binascii.hexlify(data_buffer[0:size][::-1]), 16) 55 | 56 | def mem_write(self, address, size, value): 57 | if size in (1, 2, 4, 8): 58 | word_size = "" if size == 1 else 8*size 59 | if size != 8 : 60 | cmd = "W{} 0x{:x} 0x{:x}".format(word_size, address, value) 61 | else: 62 | cmd = "W{} 0x{:x} 0x{:x}; W32 0x{:x} 0x{:x}".format(32, address, (value & 0xFFFFFFFF), (address + 4), (value >> 32)) 63 | os.system('{} /Nologo /Min /Command="{}; RwExit"'.format(self.rw_executable, cmd)) 64 | 65 | def load_data(self, filename, address): 66 | os.system('{} /Nologo /Min /Command="LOAD {} Memory 0x{:x}; RwExit"'.format(self.rw_executable, filename, address)) 67 | 68 | def read_io(self, address, size): 69 | if size in (1, 2, 4): 70 | cmd = "I{} 0x{:x}".format("" if size == 1 else 8*size, address) 71 | os.system('{} /Nologo /Min /LogFile={} /Command="{}; RwExit"'.format(self.rw_executable, self.result_text, cmd)) 72 | with open(self.result_text, 'r') as f: 73 | result = f.read() 74 | temp_str = result.split('=') 75 | if temp_str[0].strip() == 'In Port 0x{:x}'.format(address): 76 | return int(temp_str[1].strip(), 16) 77 | else: 78 | return 0 79 | 80 | def write_io(self, address, size, value): 81 | if size in (1, 2, 4): 82 | cmd = "O{} 0x{:x} 0x{:x}".format("" if size == 1 else 8*size, address, value) 83 | os.system('{} /Nologo /Min /Command="{}; RwExit"'.format(self.rw_executable, cmd)) 84 | 85 | def trigger_smi(self, smi_value): 86 | os.system('{} /Nologo /Min /Command="O 0x{:x} 0x{:x}; RwExit"'.format(self.rw_executable, 0xB2, smi_value)) 87 | 88 | def read_msr(self, Ap, address): 89 | return 0 90 | 91 | def write_msr(self, Ap, address, value): 92 | return 0 93 | 94 | def read_sm_base(self): 95 | return 0 96 | -------------------------------------------------------------------------------- /src/xmlcli/access/winrwe/winrweTest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | __author__ = "Gahan Saraiya" 4 | 5 | import unittest 6 | from .winrwe import WinRweAccess 7 | 8 | 9 | class WinRweTestCase(unittest.TestCase): 10 | def test_initialize_interface(self): 11 | access_method_name = "winrwe" 12 | self.base_object = WinRweAccess(access_name=access_method_name) 13 | self.assertEqual(self.base_object.interface, access_method_name) 14 | self.assertEqual(self.base_object.interface, self.base_object.InterfaceType) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /src/xmlcli/cfg/BiosKnobs.ini: -------------------------------------------------------------------------------- 1 | ;-------------------------------------------------------------------------------------------- 2 | ; XML Shared MailBox settings for XmlCli based setup 3 | ; The name entry here should be identical as the name from the XML file (retain the case) 4 | ;-------------------------------------------------------------------------------------------- 5 | [BiosKnobs] 6 | ;SerialDebug=0 7 | ;SerialDebugBaudRate=7 8 | ;SetShellFirst=1 9 | ;BootFirstToShell=1 10 | ;XmlCliSupport=1 11 | ;MipiCam_Link0_UserHid=L"CamZ" 12 | -------------------------------------------------------------------------------- /src/xmlcli/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/common/__init__.py -------------------------------------------------------------------------------- /src/xmlcli/common/compress.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Built-in imports 4 | import os 5 | import sys 6 | import shlex 7 | from collections import namedtuple 8 | from datetime import datetime 9 | 10 | # Custom imports 11 | from . import utils 12 | from . import logger 13 | from . import configurations 14 | 15 | __author__ = "Gahan Saraiya" 16 | 17 | log = logger.settings.logger 18 | 19 | COMPRESSION_GUIDS = [ 20 | "ee4e5898-3914-4259-9d6edc7bd79403cf", 21 | "3d532050-5cda-4fd0-879e0f7f630d5afb" 22 | ] 23 | 24 | 25 | def lzma_decompress(compressed_file, decompressed_file, min_python=(3, 7, 5)): 26 | if sys.version_info >= min_python: 27 | import lzma 28 | try: 29 | import shutil 30 | with lzma.open(compressed_file, "rb") as compressed_data: 31 | with open(decompressed_file, "wb") as uncompressed_output: 32 | shutil.copyfileobj(compressed_data, uncompressed_output) 33 | except Exception as e: 34 | log.debug("failed to decompress lzma...\n Trying in-memory decompression") 35 | with open(compressed_file, "rb") as compressed_data: 36 | decompressed_data = lzma.decompress(compressed_data.read()) 37 | # write in memory data to file 38 | with open(decompressed_file, "wb") as f: 39 | f.write(decompressed_data) 40 | return True 41 | else: 42 | log.error(f"Decompression not supported. Please move to Python version {min_python} or above") 43 | 44 | 45 | class ProcessEncapsulatedData(object): 46 | def __init__(self, guid, compressed_data, section, **kwargs): 47 | self.guid = utils.guid_formatter(guid) 48 | self.compressed_data = compressed_data 49 | self.section = section 50 | self.decompress_map = namedtuple("Decompressor", ["name", "guid", "method"]) 51 | self.decompression_guid_map = { 52 | "ee4e5898-3914-4259-9d6edc7bd79403cf": self.decompress_map("LZMA_CUSTOM_DECOMPRESS_GUID", [0xEE4E5898, 0x3914, 0x4259, 0x9D, 0x6E, 0xDC, 0x7B, 0xD7, 0x94, 0x03, 0xCF], self.lzma_custom_decompress), 53 | "3d532050-5cda-4fd0-879e0f7f630d5afb": self.decompress_map("BROTLI_CUSTOM_DECOMPRESS_GUID", [0x3D532050, 0x5CDA, 0x4FD0, 0x87, 0x9E, 0x0F, 0x7F, 0x63, 0x0D, 0x5A, 0xFB], self.brotli_custom_decompress), 54 | "a31280ad-481e-41b6-95e8127f4c984779": self.decompress_map("TIANO_CUSTOM_DECOMPRESS_GUID", [0xA31280AD, 0x481E, 0x41B6, 0x95, 0xE8, 0x12, 0x7F, 0x4C, 0x98, 0x47, 0x79], self.tiano_custom_decompress), 55 | } 56 | self.temp_folder = kwargs.get("temp_folder", utils.get_temp_folder()) 57 | self.tool_dir = kwargs.get("tool_dir", utils.get_tools_dir()) 58 | self.brotli_compression_utility = configurations.BROTLI_COMPRESS_BIN 59 | self.tiano_compression_utility = configurations.TIANO_COMPRESS_BIN 60 | self.timestamp = datetime.now().strftime(logger.LOG_DATE_FORMAT) 61 | self.input_file_path = os.path.join(self.temp_folder, "fv_compressed_{}_{}{}.sec") 62 | self.output_file_path = os.path.join(self.temp_folder, "fv_decompressed_{}_{}{}.sec") 63 | self.directory_initialization() 64 | 65 | def directory_initialization(self): 66 | # write in memory data to file 67 | with open(self.temp_file_path, "wb") as f: 68 | f.write(self.compressed_data) 69 | 70 | @property 71 | def decompressed_file_path(self): 72 | guid_str = utils.get_string(self.guid) 73 | return self.output_file_path.format(guid_str, self.timestamp, configurations.PY_VERSION) 74 | 75 | @property 76 | def temp_file_path(self): 77 | guid_str = utils.get_string(self.guid) 78 | return self.input_file_path.format(guid_str, self.timestamp, configurations.PY_VERSION) 79 | 80 | def to_be_implemented(self): 81 | err_msg = "Given decompression method does not implemented so far, will be implemented in upcoming future" 82 | raise NotImplementedError(err_msg) 83 | 84 | @staticmethod 85 | def system_call(output_file_check, cmd=None, cmd_lis=None): 86 | utils.system_call(cmd_lis) 87 | if cmd_lis and not os.path.exists(output_file_check): 88 | import subprocess 89 | subprocess.call([cmd for cmd in cmd_lis]) 90 | elif cmd and not os.path.exists(output_file_check): 91 | utils.system_call(shlex.split(cmd, posix=(configurations.PLATFORM != "win32"))) 92 | 93 | def lzma_custom_decompress(self): 94 | if sys.version_info <= (3, 7, 5): 95 | raise utils.XmlCliException("Decompression not supported on your python version. Please move to Python version 3.7.5 or above") 96 | else: 97 | # lzma is built-in compression method since Python 3.4 (using 3.7.5 which has bug fixed) 98 | import lzma 99 | decompressed_data = lzma.decompress(self.compressed_data) 100 | # write in memory data to file 101 | with open(self.decompressed_file_path, "wb") as f: 102 | f.write(decompressed_data) 103 | return decompressed_data 104 | 105 | def tiano_custom_decompress(self): 106 | # decompress with binary utility 107 | cmd = f'"{self.tiano_compression_utility}" -d -q "{self.temp_file_path}" -o "{self.decompressed_file_path}"' 108 | cmd_lis = [self.tiano_compression_utility, "-d", "-q", self.temp_file_path, "-o", self.decompressed_file_path] 109 | self.system_call(cmd=cmd, cmd_lis=cmd_lis, output_file_check=self.decompressed_file_path) 110 | with open(self.decompressed_file_path, "rb") as f: 111 | decompressed_data = f.read() 112 | return decompressed_data 113 | 114 | def brotli_custom_decompress(self): 115 | # decompress with binary utility 116 | cmd = f'"{self.brotli_compression_utility}" -d -i "{self.temp_file_path}" -o "{self.decompressed_file_path}"' 117 | cmd_lis = [self.brotli_compression_utility, "-d", "-i", self.temp_file_path, "-o", self.decompressed_file_path] 118 | self.system_call(cmd=cmd, cmd_lis=cmd_lis, output_file_check=self.decompressed_file_path) 119 | with open(self.decompressed_file_path, "rb") as f: 120 | decompressed_data = f.read() 121 | return decompressed_data 122 | 123 | def decompress(self): 124 | log.info(f"Decompressing File...GUID: {self.guid}") 125 | decompressor = self.decompression_guid_map.get(self.guid, None) 126 | decrypter = self.decompression_guid_map.get(self.guid, None) 127 | if decompressor: 128 | log.info("Decompressing...") 129 | log.info(decompressor) 130 | decompression_method = decompressor.method 131 | decompressed_data = decompression_method() 132 | log.info("Returning successful decompressed data") 133 | return decompressed_data 134 | elif decrypter: 135 | log.info("Decrypting...") 136 | log.info(decrypter) 137 | else: 138 | err_msg = f"Given decompression/decrypting method (for GUID: {self.guid})does not exist" 139 | log.error(err_msg) 140 | # raise NotImplementedError(err_msg) 141 | 142 | 143 | if __name__ == "__main__": 144 | pass 145 | -------------------------------------------------------------------------------- /src/xmlcli/common/configurations.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | import os 6 | import sys 7 | import tempfile 8 | import configparser 9 | 10 | 11 | def config_read(config_file): 12 | """As the UEFI Python has limitation, 13 | this method is to handle exception for the same in order to read configuration 14 | 15 | :param config_file: file to read in to config parser object 16 | :return: config parser object with config read from file 17 | """ 18 | configparser_object = configparser.RawConfigParser(allow_no_value=True) 19 | try: 20 | configparser_object.read(config_file) 21 | except AttributeError: 22 | # EFI Shell may encounter at this flow while reading config file as .read method uses os.popen which is not available at EFI Python 23 | with open(config_file, "r") as f: 24 | configparser_object._read(f, config_file) 25 | return configparser_object 26 | 27 | 28 | # Platform Details 29 | PY3 = bool(sys.version_info.major == 3) 30 | PLATFORM = sys.platform 31 | PY_VERSION = f"_py{sys.version_info.major}.{sys.version_info.minor}" 32 | SYSTEM_VERSION = (sys.version_info.major, sys.version_info.minor) 33 | 34 | # Current directory src/common 35 | CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) 36 | 37 | # XmlCli source directory 38 | XMLCLI_DIR = os.path.dirname(CURRENT_DIRECTORY) 39 | # Tools directory 40 | TOOL_DIR = os.path.join(XMLCLI_DIR, "tools") 41 | # directory in OS temporary location 42 | TEMP_DIR = os.path.join(tempfile.gettempdir(), "XmlCliOut") 43 | 44 | # Configuration parser object 45 | CONFIG_FILE = os.path.join(XMLCLI_DIR, "xmlcli.config") 46 | XMLCLI_CONFIG = config_read(CONFIG_FILE) 47 | 48 | ENCODING = XMLCLI_CONFIG.get("GENERAL_SETTINGS", "ENCODING") 49 | ACCESS_METHOD = XMLCLI_CONFIG.get("GENERAL_SETTINGS", "ACCESS_METHOD") 50 | PERFORMANCE = XMLCLI_CONFIG.getboolean("GENERAL_SETTINGS", "PERFORMANCE") 51 | # BIOS Knobs Configuration file 52 | BIOS_KNOBS_CONFIG = os.path.join(XMLCLI_DIR, 'cfg', 'BiosKnobs.ini') 53 | 54 | OUT_DIR = os.path.join(XMLCLI_DIR, "out") 55 | # output directory to be overridden if specified in config file 56 | _OUT_DIR = XMLCLI_CONFIG.get("DIRECTORY_SETTINGS", "OUT_DIR") 57 | if _OUT_DIR: 58 | if os.path.exists(os.path.abspath(_OUT_DIR)) and os.access(os.path.abspath(_OUT_DIR), os.W_OK): 59 | # check for absolute directory path and write permission 60 | OUT_DIR = os.path.abspath(_OUT_DIR) 61 | elif os.path.exists(os.path.join(XMLCLI_DIR, _OUT_DIR)) and os.access(os.path.join(XMLCLI_DIR, _OUT_DIR), os.W_OK): 62 | # check for relative directory path and write permission 63 | OUT_DIR = os.path.join(XMLCLI_DIR, _OUT_DIR) 64 | else: 65 | OUT_DIR = TEMP_DIR 66 | 67 | if PLATFORM == "uefi": 68 | if not os.path.isdir(OUT_DIR): 69 | os.makedirs(OUT_DIR) 70 | else: 71 | os.makedirs(OUT_DIR, exist_ok=True) 72 | 73 | # Tools and Utilities 74 | 75 | TIANO_COMPRESS_BIN = XMLCLI_CONFIG.get("TOOL_SETTINGS", "TIANO_COMPRESS_BIN") 76 | if not os.path.isfile(os.path.abspath(TIANO_COMPRESS_BIN)): 77 | TIANO_COMPRESS_BIN = os.path.join(TOOL_DIR, TIANO_COMPRESS_BIN) 78 | TIANO_COMPRESS_BIN = f"{TIANO_COMPRESS_BIN}{'.exe' if PLATFORM == 'win32' and not TIANO_COMPRESS_BIN.endswith('.exe') else ''}" 79 | if not os.path.isfile(TIANO_COMPRESS_BIN): 80 | TIANO_COMPRESS_BIN = os.path.abspath(TIANO_COMPRESS_BIN) 81 | 82 | BROTLI_COMPRESS_BIN = XMLCLI_CONFIG.get("TOOL_SETTINGS", "BROTLI_COMPRESS_BIN") 83 | 84 | if not os.path.isfile(os.path.abspath(BROTLI_COMPRESS_BIN)): 85 | BROTLI_COMPRESS_BIN = os.path.join(TOOL_DIR, BROTLI_COMPRESS_BIN) 86 | BROTLI_COMPRESS_BIN = f"{BROTLI_COMPRESS_BIN}{'.exe' if PLATFORM == 'win32' and not BROTLI_COMPRESS_BIN.endswith('.exe') else ''}" 87 | if not os.path.isfile(BROTLI_COMPRESS_BIN): 88 | BROTLI_COMPRESS_BIN = os.path.abspath(BROTLI_COMPRESS_BIN) 89 | 90 | STATUS_CODE_RECORD_FILE = os.path.join(XMLCLI_DIR, "messages.json") 91 | 92 | # Reading other configuration parameters 93 | CLEANUP = XMLCLI_CONFIG.getboolean("INITIAL_CLEANUP", "CLEANUP") 94 | 95 | ENABLE_EXPERIMENTAL_FEATURES = XMLCLI_CONFIG.getboolean("EXPERIMENTAL_FEATURES_SETTINGS", "ENABLE_EXPERIMENTAL_FEATURES") 96 | 97 | 98 | __all__ = ["XMLCLI_CONFIG", 99 | "PY3", "PY_VERSION", "SYSTEM_VERSION", "PLATFORM", 100 | "XMLCLI_DIR", "TEMP_DIR", "OUT_DIR", 101 | "ACCESS_METHOD", "ENCODING", "PERFORMANCE", 102 | "TIANO_COMPRESS_BIN", "BROTLI_COMPRESS_BIN", 103 | "STATUS_CODE_RECORD_FILE", 104 | "ENABLE_EXPERIMENTAL_FEATURES" 105 | ] 106 | 107 | 108 | if __name__ == "__main__": 109 | pass 110 | -------------------------------------------------------------------------------- /src/xmlcli/common/logger.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | import os 6 | import sys 7 | import json 8 | import logging 9 | from datetime import datetime 10 | 11 | # Custom imports 12 | from .configurations import XMLCLI_CONFIG, XMLCLI_DIR, OUT_DIR, PLATFORM, PY_VERSION, ENCODING, STATUS_CODE_RECORD_FILE 13 | 14 | ############################################################################### 15 | # START: LOG Settings ######################################################### 16 | LOGGER_TITLE = XMLCLI_CONFIG.get("LOG_SETTINGS", "LOGGER_TITLE") 17 | FILE_LOG = XMLCLI_CONFIG.getboolean("LOG_SETTINGS", "FILE_LOG") # prints log on console if True 18 | CONSOLE_STREAM_LOG = XMLCLI_CONFIG.getboolean("LOG_SETTINGS", "CONSOLE_STREAM_LOG") # prints log on console if True 19 | LOG_DIR = os.path.join(OUT_DIR, XMLCLI_CONFIG.get("LOG_SETTINGS", "LOG_DIR")) 20 | LOG_LEVEL = XMLCLI_CONFIG.get("LOG_SETTINGS", "LOG_LEVEL") # options for LOG_LEVEL = DEBUG|INFO|ERROR|WARN 21 | LOG_FORMAT = XMLCLI_CONFIG.get("LOG_SETTINGS", "{}_LOG_FORMAT".format(LOG_LEVEL.upper())) 22 | CONSOLE_LOG_LEVEL = XMLCLI_CONFIG.get("LOG_SETTINGS", "CONSOLE_LOG_LEVEL") # override console log to differ from file logging 23 | CONSOLE_LOG_FORMAT = XMLCLI_CONFIG.get("LOG_SETTINGS", "{}_LOG_FORMAT".format(CONSOLE_LOG_LEVEL.upper())) 24 | LOG_DATE_FORMAT = XMLCLI_CONFIG.get("LOG_SETTINGS", "LOG_DATE_FORMAT") 25 | LOG_FILE_NAME_DATE = XMLCLI_CONFIG.get("LOG_SETTINGS", "LOG_FILE_NAME_DATE") 26 | LOG_FILE_NAME = XMLCLI_CONFIG.get("LOG_SETTINGS", "LOG_FILE_NAME") 27 | RESULT_LEVEL_NUM = 21 # INFO level is 20, RESULT is kept above to always print. 28 | 29 | # END: LOG Settings ########################################################### 30 | 31 | # Add result LOG_LEVEL 32 | logging.addLevelName(RESULT_LEVEL_NUM, "RESULT") 33 | 34 | STATUS_CODE_RECORD = {} 35 | with open(STATUS_CODE_RECORD_FILE, "r") as f: 36 | STATUS_CODE_RECORD.update(json.load(f)) 37 | 38 | 39 | def result(self, message, *args, **kws): 40 | """ 41 | Custom log level result is created, one level above the info level 42 | if info is enabled, it will definitely print result but not vice versa 43 | 44 | :param self: class instance 45 | :param message: message 46 | :param args: arguments 47 | :param kws: keyword arguments 48 | :return: 49 | """ 50 | if self.isEnabledFor(RESULT_LEVEL_NUM): 51 | # Yes, logger takes its '*args' as 'args'. 52 | self._log(RESULT_LEVEL_NUM, message, args, **kws) 53 | 54 | 55 | def error(self, message="", error_code="0x1", *args, **kwargs): 56 | """ 57 | Custom log level exception is created, same level as the error level 58 | It is to print additional information retrieved from messages.json 59 | 60 | :param self: class instance 61 | :param message: Message to be overwritten to modify from what exists in json 62 | :param error_code: error code has hex string is expected 63 | if integer specified then converted to hex 64 | :param args: 65 | :param kwargs: specify any custom hints to tackle problem 66 | """ 67 | # Call the base class constructor with the parameters it needs 68 | if self.isEnabledFor(logging.ERROR): 69 | # Yes, logger takes its '*args' as 'args'. 70 | hints = "\n".join(kwargs.get("hints", [])) 71 | if error_code: 72 | error_code = hex(error_code) if isinstance(error_code, int) else error_code 73 | error_data = STATUS_CODE_RECORD.get(error_code, {}) 74 | if not message: 75 | message = error_data.get("msg", "!invalid_status_code!") 76 | if not hints.strip(): 77 | hints = f"{error_data.get('hint', '')}\n{error_data.get('additional_details', '')}" 78 | message = f"[XmlCliError: {error_code}] {message}" if error_code else f"[XmlCliError] {message}" 79 | if hints.strip(): 80 | message += f"\nHint: {hints}" 81 | self._log(logging.ERROR, message, args, **kwargs) 82 | 83 | 84 | # Updating class instance method with custom log method 85 | logging.Logger.result = result 86 | logging.Logger.error = error 87 | 88 | 89 | class Setup(object): 90 | """Setup Logging module for project 91 | """ 92 | 93 | def __init__(self, **kwargs): 94 | """ 95 | :param log_level: (optional) logging mode or level 96 | :param console_log_level: (optional) logging mode or level for console 97 | this option allows to override log level for console logging 98 | :param log_title: (optional) title for logging module 99 | :param log_format: (optional) format of log 100 | :param log_dir: (optional) logger title 101 | :param binary_dir: (optional) logging mode 102 | default is logging.DEBUG 103 | :param key_dir: (optional) Date Format for logging 104 | default is set to `%Y-%d-%m_%H.%M.%S` 105 | :param out_dir: (optional) output directory for module 106 | :return: None 107 | """ 108 | self.log_level = kwargs.get("log_level", LOG_LEVEL).upper() 109 | self.console_log_level = kwargs.get("console_log_level", CONSOLE_LOG_LEVEL).upper() 110 | self.console_log_format = kwargs.get("console_log_format", CONSOLE_LOG_FORMAT) 111 | self.sub_module = kwargs.get("sub_module", "") 112 | self.log_title = kwargs.get("log_title", LOGGER_TITLE) 113 | if self.sub_module: 114 | self.log_title += ".{}".format(self.sub_module) 115 | self.log_format = kwargs.get("log_format", LOG_FORMAT) 116 | self.logger = logging.getLogger(self.log_title) 117 | self.log_dir = kwargs.get("log_dir", LOG_DIR) 118 | self.out_dir = kwargs.get("out_dir", OUT_DIR) 119 | self.xmlcli_dir = kwargs.get("xmlcli_dir", XMLCLI_DIR) 120 | self.log_file_name = kwargs.get("log_file_name", "{}_{}_{}{}.log".format(self.log_title.split(".")[0], datetime.now().strftime(LOG_FILE_NAME_DATE), PLATFORM, PY_VERSION)) 121 | self.write_in_file = kwargs.get("write_in_file", FILE_LOG) 122 | self.print_on_console = kwargs.get("print_on_console", CONSOLE_STREAM_LOG) 123 | self.directory_maker() 124 | self.configure_logger() 125 | # self.display_system_configs() 126 | 127 | def display_system_configs(self): 128 | """Display logging configurations 129 | """ 130 | config_data = "\n" + "="*50 + \ 131 | " SYSTEM CONFIGS " + "="*50 + \ 132 | "\n\t\t LOG_TITLE : {}".format(self.log_title) + \ 133 | "\n\t\t SYS_VERSION : {}".format(sys.version) + \ 134 | "\n\t\t LOG_DIR : {}".format(self.log_dir) + \ 135 | "\n\t\t OUT_DIR : {}".format(self.out_dir) + \ 136 | "\n\t\t XMLCLI_DIR : {}".format(self.xmlcli_dir) + \ 137 | "\n\t\t LOG_LEVEL : {}".format(self.log_level) + \ 138 | "\n\t\t CONSOLE LOG_LEVEL: {}".format(self.console_log_level) + \ 139 | "\n\t\t WRITE IN FILE : {}".format(self.write_in_file) + \ 140 | "\n\t\t PRINT ON CONSOLE : {}".format(self.print_on_console) + \ 141 | "\n" + "="*100 142 | if not self.print_on_console: 143 | print(config_data) 144 | self.logger.info(config_data) 145 | return config_data 146 | 147 | def directory_maker(self): 148 | """Creates directories for use if not exist 149 | 150 | Important for new initialization when require directory does not exist 151 | or directory is removed due to any reason. 152 | 153 | Aimed to prevent FileNotFoundError caused due to non existence of directory. 154 | """ 155 | directories = [self.log_dir] 156 | for d in directories: 157 | if not os.path.exists(d): # adding condition to support python 2 implementation 158 | os.makedirs(d) 159 | 160 | def configure_logger(self, **kwargs): 161 | """Configures logging module 162 | 163 | :param kwargs: 164 | title: (optional) logger title 165 | mode: (optional) logging mode 166 | default is LOG_LEVEL defined globally 167 | console_mode: (optional) logging mode for console logging 168 | (if specified it will override value of specified `mode`) 169 | default is LOG_LEVEL defined globally 170 | log_file_name: (optional) name of the log file 171 | autogenerated based on timestamp if not specified 172 | log_format: (optional) log format 173 | default is set to `LOG_FORMAT` 174 | write_in_file: (optional) Boolean value Specifies whether to store log into file or not 175 | default value set to `FILE_LOG` 176 | print_on_console: (optional) toggle to `True`/`False`, setting it to True will allow 177 | print log on console 178 | default value set to `CONSOLE_STREAM_LOG` 179 | 180 | :return: status value status True if logger configuration successful 181 | """ 182 | mode = kwargs.get("mode", self.log_level) 183 | write_in_file = kwargs.get("write_in_file", self.write_in_file) 184 | console_log_level = kwargs.get("console_log_level", self.console_log_level) 185 | console_log_format = kwargs.get("console_log_format", self.console_log_format) 186 | print_on_console = kwargs.get("print_on_console", self.print_on_console) 187 | title = kwargs.get("title", self.log_title) 188 | log_format = kwargs.get("log_format", self.log_format) 189 | logger = logging.getLogger(title) # create logger with given title 190 | logger.setLevel(min(mode, console_log_level)) # set log level 191 | # configure basic logging configurations 192 | if print_on_console and not (any([isinstance(i, logging.StreamHandler) for i in logger.handlers])): 193 | handler = self.get_console_handler(console_log_level, console_log_format) 194 | # add the handlers to the logger 195 | logger.addHandler(handler) 196 | 197 | if write_in_file: 198 | log_file_name = kwargs.get("log_file_name", self.log_file_name) 199 | file_handler = self.get_file_handler(log_file_name, mode, log_format) 200 | # add the handlers to the logger 201 | if file_handler not in logger.handlers: 202 | logger.addHandler(file_handler) 203 | return True 204 | 205 | def get_console_handler(self, log_level=None, log_format=None): 206 | """ 207 | 208 | :param log_level: logging level 209 | :param log_format: format for logging 210 | :return: handler for logging on console 211 | """ 212 | log_level = log_level if log_level else self.console_log_level 213 | log_format = log_format if log_format else self.console_log_format 214 | console_handler = logging.StreamHandler() 215 | console_handler.setLevel(log_level) 216 | # create formatter and add it to the handlers 217 | console_handler.setFormatter(logging.Formatter(log_format)) 218 | return console_handler 219 | 220 | def get_file_handler(self, file_name, log_level=None, log_format=None): 221 | """ 222 | 223 | :param file_name: log file name 224 | :param log_level: logging level 225 | :param log_format: format for logging 226 | :return: handler for logging at file 227 | """ 228 | log_level = log_level if log_level else self.log_level 229 | log_format = log_format if log_format else self.log_format 230 | file_handler = logging.FileHandler( 231 | filename=os.path.join(self.log_dir, file_name), 232 | encoding=ENCODING, mode="w") 233 | file_handler.setLevel(log_level) 234 | # create formatter and add it to the handlers 235 | file_handler.setFormatter(logging.Formatter(log_format)) 236 | return file_handler 237 | 238 | def get_logger(self, logger_name, file_name=None): 239 | logger = logging.getLogger(logger_name) 240 | logger.setLevel(self.log_level) 241 | # add the handlers to the logger 242 | console_handler = self.get_console_handler() 243 | if console_handler not in logger.handlers and not (any([isinstance(i, logging.StreamHandler) for i in logger.handlers])): 244 | logger.addHandler(console_handler) 245 | if file_name: 246 | file_handler = self.get_file_handler(self.log_file_name) 247 | if file_handler not in logger.handlers: 248 | logger.addHandler(file_handler) 249 | # with this pattern, it's rarely necessary to propagate the error up to parent 250 | logger.propagate = False 251 | return logger 252 | 253 | def log_function_entry_and_exit(self, decorated_function): 254 | """ 255 | Function decorator logging entry + exit and parameters of functions. 256 | Entry and exit as logging.info, parameters as logging.DEBUG. 257 | """ 258 | from functools import wraps 259 | 260 | @wraps(decorated_function) 261 | def wrapper(*dec_fn_args, **dec_fn_kwargs): 262 | # Log function entry 263 | func_name = decorated_function.__name__ 264 | self.logger.debug('Entering {}()...'.format(func_name)) 265 | 266 | # get function params (args and kwargs) 267 | arg_names = decorated_function.__code__.co_varnames 268 | params = dict( 269 | args=dict(zip(arg_names, dec_fn_args)), 270 | kwargs=dec_fn_kwargs) 271 | 272 | self.logger.debug( 273 | func_name + ">> \t" + ', '.join([ 274 | '{}={}'.format(str(k), repr(v)) for k, v in params.items()])) 275 | # Execute wrapped (decorated) function: 276 | out = decorated_function(*dec_fn_args, **dec_fn_kwargs) 277 | self.logger.debug('Done running {}()!'.format(func_name)) 278 | 279 | return out 280 | return wrapper 281 | 282 | 283 | if LOG_FILE_NAME: 284 | settings = Setup(log_file_name=LOG_FILE_NAME) 285 | else: 286 | settings = Setup() 287 | log = settings.logger 288 | 289 | 290 | if __name__ == "__main__": 291 | pass 292 | -------------------------------------------------------------------------------- /src/xmlcli/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "0x0": { 3 | "status": "success", 4 | "msg": "Operation Completed Successfully", 5 | "hint": "", 6 | "additional_details": "", 7 | "tags": ["general"] 8 | }, 9 | "0x1": { 10 | "status": "error", 11 | "msg": "Operational Error", 12 | "hint": "", 13 | "additional_details": "", 14 | "tags": ["general"] 15 | }, 16 | "0x2": { 17 | "status": "warning", 18 | "msg": "Deprecation Warning", 19 | "hint": "", 20 | "additional_details": "This usage would be removed in next major version release", 21 | "tags": ["general", "deprecation", "warning"] 22 | }, 23 | "0x101": { 24 | "status": "error", 25 | "msg": "Wrong Dram Shared Mailbox address", 26 | "hint": "XmlCli Support may have not been enabled.", 27 | "additional_details": "", 28 | "tags": ["general", "mailbox"] 29 | }, 30 | "0x102": { 31 | "status": "error", 32 | "msg": "Shared Mailbox Buffer too short", 33 | "hint": "Mailbox is not of expected size, could have been overwritten or not populated as expected.", 34 | "additional_details": "", 35 | "tags": ["general", "mailbox"] 36 | }, 37 | "0x1000": { 38 | "status": "error", 39 | "msg": "Memory Operation Failed", 40 | "hint": "Try to check health of the interface you are working with.", 41 | "additional_details": "XmlCli uses physical memory of Device under Test to pursue execution of API.", 42 | "tags": ["interface", "memory", "access", "access_method"] 43 | }, 44 | "0x3001": { 45 | "status": "error", 46 | "msg": "Failed to initialize access method", 47 | "hint": "Try to check health of the interface you are working with.", 48 | "additional_details": "You might have selected wrong interface, please refer documentation: http://goto/xmlcli-resources", 49 | "tags": ["access_methods", "interface"] 50 | }, 51 | "0xC1D1": { 52 | "status": "error", 53 | "msg": "XmlCli Interface is Disabled", 54 | "hint": "Try to select valid interface and follow process for enabling xmlcli.", 55 | "additional_details": "please refer documentation: http://goto/xmlcli-wiki", 56 | "tags": ["access_methods", "interface"] 57 | }, 58 | "0xCE4E": { 59 | "status": "warning", 60 | "msg": "XmlCli support was not Enabled, its now Enabled, Reboot Required.", 61 | "hint": "Since you have enabled XmlCli for the first time, system reset is required. Reboot/Cold Reset is recommended.", 62 | "additional_details": "please refer documentation: http://goto/xmlcli-wiki", 63 | "tags": ["access_methods", "interface"] 64 | }, 65 | "0xFE90": { 66 | "status": "error", 67 | "msg": "GetsetBiosKnobsFromBin - Bios Knobs Data Bin not found", 68 | "hint": "", 69 | "additional_details": "please refer documentation: http://goto/xmlcli-wiki", 70 | "tags": ["bios_knobs", "data", "binary", "knobs"] 71 | }, 72 | "0xFE91": { 73 | "status": "error", 74 | "msg": "GetsetBiosKnobsFromBin - Empty Input Knob List", 75 | "hint": "", 76 | "additional_details": "please refer documentation: http://goto/xmlcli-wiki", 77 | "tags": ["bios_knobs", "data", "binary", "knobs"] 78 | }, 79 | "0x1FD4": { 80 | "status": "error", 81 | "msg": "FlashRegionInfo - Invalid Flash descriptor section", 82 | "hint": "", 83 | "additional_details": "", 84 | "tags": [] 85 | }, 86 | "0xC140": { 87 | "status": "error", 88 | "msg": "XmlCli Req or Resp Buffer Address is Zero", 89 | "hint": "", 90 | "additional_details": "", 91 | "tags": [] 92 | }, 93 | "0xC4B0": { 94 | "status": "error", 95 | "msg": "XmlCli Request Buffer Empty no action needed on XmlCli Command", 96 | "hint": "", 97 | "additional_details": "", 98 | "tags": [] 99 | }, 100 | "0xC4E0": { 101 | "status": "error", 102 | "msg": "XmlCli Resp Buffer Parameter Size is Zero", 103 | "hint": "", 104 | "additional_details": "", 105 | "tags": [] 106 | }, 107 | "0xC42F": { 108 | "status": "error", 109 | "msg": "XmlCli Knobs Verify Operation Failed", 110 | "hint": "", 111 | "additional_details": "", 112 | "tags": [] 113 | }, 114 | "0x1E9A": { 115 | "status": "error", 116 | "msg": "Ganges - Legacy Mailbox Addr is not valid", 117 | "hint": "", 118 | "additional_details": "", 119 | "tags": [] 120 | }, 121 | "0x9E51": { 122 | "status": "error", 123 | "msg": "GetSetVar - Invalid Operation", 124 | "hint": "", 125 | "additional_details": "", 126 | "tags": ["online", "invalid-operation"] 127 | }, 128 | "0x13E4": { 129 | "status": "error", 130 | "msg": "import error", 131 | "hint": "Import error issue on EnableXmlCli for the system python version", 132 | "additional_details": "", 133 | "tags": ["online", "bios-support", "enable", "import", "version", "python-version"] 134 | }, 135 | "0xFCFA": { 136 | "status": "error", 137 | "msg": "CompareFlashRegion - Flash Compare Result for given Region is FAIL", 138 | "hint": "", 139 | "additional_details": "", 140 | "tags": [] 141 | }, 142 | "0x1F4E": { 143 | "status": "error", 144 | "msg": "cliProgBIOS - Invalid Flash region Selected for Update", 145 | "hint": "", 146 | "additional_details": "", 147 | "tags": [] 148 | }, 149 | "0xD5E9": { 150 | "status": "error", 151 | "msg": "spi_flash - Descriptor section not Valid", 152 | "hint": "", 153 | "additional_details": "", 154 | "tags": [] 155 | }, 156 | "0x14E5": { 157 | "status": "error", 158 | "msg": "spi_flash - Invalid Request", 159 | "hint": "", 160 | "additional_details": "", 161 | "tags": [] 162 | }, 163 | "0xB09F": { 164 | "status": "error", 165 | "msg": "GenBootOrderDict - Boot Order Variable not found in XML", 166 | "hint": "", 167 | "additional_details": "", 168 | "tags": ["setup-option", "knob", "boot-order", "not-found"] 169 | }, 170 | "0xB09E": { 171 | "status": "error", 172 | "msg": "GenBootOrderDict - Boot Order Options is empty in XML", 173 | "hint": "", 174 | "additional_details": "", 175 | "tags": ["setup-option", "knob", "boot-order", "empty"] 176 | }, 177 | "0xB09D": { 178 | "status": "error", 179 | "msg": "GenBootOrderDict - Given Boot order list length doesnt match current list", 180 | "hint": "", 181 | "additional_details": "", 182 | "tags": ["setup-option", "knob", "boot-order", "mismatch"] 183 | }, 184 | "0x5B01": { 185 | "status": "error", 186 | "msg": "SetBootOrder - Requested operation is Incomplete", 187 | "hint": "", 188 | "additional_details": "", 189 | "tags": ["setup-option", "knob", "boot-order"] 190 | }, 191 | "0x5B1F": { 192 | "status": "error", 193 | "msg": "SetBootOrder - Invalid format to Set BootOrder", 194 | "hint": "", 195 | "additional_details": "", 196 | "tags": ["setup-option", "knob", "boot-order", "invalid-format"] 197 | }, 198 | "0x3CF9": { 199 | "status": "error", 200 | "msg": "process_ucode - Microcode Firmware Volume not found", 201 | "hint": "", 202 | "additional_details": "", 203 | "tags": ["microcode", "ucode", "not-found"] 204 | }, 205 | "0x3CCE": { 206 | "status": "error", 207 | "msg": "process_ucode - Error Converting inc to pdb format", 208 | "hint": "", 209 | "additional_details": "", 210 | "tags": ["microcode", "ucode", "conversion-error"] 211 | }, 212 | "0x3CFE": { 213 | "status": "error", 214 | "msg": "process_ucode - Wrong Ucode Patch File Format or Extension", 215 | "hint": "", 216 | "additional_details": "", 217 | "tags": ["microcode", "ucode", "invalid-type", "wrong-format"] 218 | }, 219 | "0x3CFC": { 220 | "status": "error", 221 | "msg": "process_ucode - Found invalid checksum for the given PDB file", 222 | "hint": "", 223 | "additional_details": "", 224 | "tags": ["microcode", "ucode", "checksum"] 225 | }, 226 | "0x3C5E": { 227 | "status": "error", 228 | "msg": "process_ucode - Not enough space in Ucode FV", 229 | "hint": "", 230 | "additional_details": "", 231 | "tags": ["microcode", "ucode", "size"] 232 | }, 233 | "0x19FD": { 234 | "status": "error", 235 | "msg": "Error initializing the given Interface Type", 236 | "hint": "Please select valid supported interface for your platform", 237 | "additional_details": "", 238 | "tags": ["online", "bios-support", "enable", "interface"] 239 | }, 240 | "0xD9FD": { 241 | "status": "error", 242 | "msg": "Dram Shared MailBox Not Found, XmlCli may be Disabled", 243 | "hint": "", 244 | "additional_details": "", 245 | "tags": ["online", "bios-support", "enable", "interface"] 246 | }, 247 | "0xC19A": { 248 | "status": "error", 249 | "msg": "XmlCli Support not Available in BIOS", 250 | "hint": "Please refer table on what to expect return value in README.md", 251 | "additional_details": "XmlCli support is not Available in Your BIOS, Contact your BIOS Engineer...", 252 | "tags": ["online", "bios-support", "enable"] 253 | }, 254 | "0xC19E": { 255 | "status": "error", 256 | "msg": "XmlCli Support not Enabled", 257 | "hint": "XmlCli support is not Enable at the moment", 258 | "additional_details": "", 259 | "tags": ["online", "bios-support", "enable"] 260 | }, 261 | "0xE7CA": { 262 | "status": "error", 263 | "msg": "Error Triggering XmlCli command, Authentication Failed", 264 | "hint": "", 265 | "additional_details": "", 266 | "tags": [] 267 | }, 268 | "0xC590": { 269 | "status": "error", 270 | "msg": "XmlCli Return Status is Non-Zero", 271 | "hint": "", 272 | "additional_details": "", 273 | "tags": ["exception", "non-zero"] 274 | }, 275 | "0xCA8E": { 276 | "status": "error", 277 | "msg": "XmlCli Resp. returned Cant Execute", 278 | "hint": "", 279 | "additional_details": "", 280 | "tags": [] 281 | }, 282 | "0xC391": { 283 | "status": "error", 284 | "msg": "XmlCli Resp. returned Wrong Parameter", 285 | "hint": "", 286 | "additional_details": "", 287 | "tags": [] 288 | }, 289 | "0xC2E0": { 290 | "status": "error", 291 | "msg": "XmlCli Resp. Timed-Out even after retries", 292 | "hint": "", 293 | "additional_details": "", 294 | "tags": [] 295 | }, 296 | "0x8311": { 297 | "status": "error", 298 | "msg": "Xml data is invalid", 299 | "hint": "", 300 | "additional_details": "", 301 | "tags": ["bios-support", "xml", "invalid-data"] 302 | }, 303 | "0xEC09": { 304 | "status": "error", 305 | "msg": "Exception detected", 306 | "hint": "Exception detected when determining if xml is valid.", 307 | "additional_details": "", 308 | "tags": ["exception"] 309 | }, 310 | "0x8AD0": { 311 | "status": "error", 312 | "msg": "Xml Address is Zero", 313 | "hint": "Platform Configuration XML not ready, hence exiting", 314 | "additional_details": "", 315 | "tags": ["online", "bios-support", "xml", "not-found"] 316 | }, 317 | "0xEFC9": { 318 | "status": "error", 319 | "msg": "EfiCompatibleTable Not Found", 320 | "hint": "", 321 | "additional_details": "", 322 | "tags": [] 323 | }, 324 | "0x9B79": { 325 | "status": "error", 326 | "msg": "GbtExtTblSig Not Found", 327 | "hint": "", 328 | "additional_details": "", 329 | "tags": [] 330 | }, 331 | "0x51E9": { 332 | "status": "error", 333 | "msg": "CLI buffer Size Error", 334 | "hint": "", 335 | "additional_details": "", 336 | "tags": ["online", "bios-support", "buffer", "size"] 337 | }, 338 | "0x5AC01": { 339 | "status": "error", 340 | "msg": "The Only available operations are 'Read' and 'Update' for ProcessAcm", 341 | "hint": "", 342 | "additional_details": "Use Operation='Read' or 'Update'", 343 | "tags": ["offline", "acm", "sacm", "read", "update"] 344 | }, 345 | "0x5AC02": { 346 | "status": "error", 347 | "msg": "IFWI/BIOS file is invalid", 348 | "hint": "", 349 | "additional_details": "Provide valid IFWI/BIOS binary file", 350 | "tags": ["offline", "acm", "sacm"] 351 | }, 352 | "0x5AC03": { 353 | "status": "error", 354 | "msg": "Startup ACM file path is invalid", 355 | "hint": "", 356 | "additional_details": "Provide valid path of the Startup ACM file", 357 | "tags": ["offline", "acm", "sacm", "invalid-path"] 358 | }, 359 | "0x5AC04": { 360 | "status": "error", 361 | "msg": "Patching criteria for patching is not met. Aborting the ACM Patching", 362 | "hint": "Perform Read operation to know the version of ACM inside the IFWI/BIOS before performing the patching", 363 | "additional_details": "To perform Startup ACM patching both Input startup acm and acm inside IFWI/BIOS should be compatible ", 364 | "tags": ["offline", "acm", "sacm", "bios", "patching-operation"] 365 | }, 366 | "0x5AC05": { 367 | "status": "error", 368 | "msg": "ACM inside BIOS/IFWI and input startup ACM are same so no patching performed", 369 | "hint": "Perform Read operation to know the version of ACM inside the IFWI/BIOS before performing the patching", 370 | "additional_details": "No Patching performed since ACM we are trying to patch is same as the ACM inside the IFWI/BIOS", 371 | "tags": ["offline", "acm", "sacm", "bios"] 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /src/xmlcli/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/modules/__init__.py -------------------------------------------------------------------------------- /src/xmlcli/modules/eBiosSetupPage/README.md: -------------------------------------------------------------------------------- 1 | # XmlCli's Virtual BIOS Setup Page Gui - Usage for `eBiosSetupPage.py` 2 | ====================================================================== 3 | 4 | > Prerequisite for online mode - BIOS Knobs `XmlCliSupport` and `PublishSetupPgPtr` needs to be set to enable 5 | 6 | - open python prompt and run gui 7 | 8 | ```python 9 | from modules.eBiosSetupPage.eBiosSetupPage import gen_gui # import method which generates gui 10 | gen_gui() # generates GUI to launch the virtual setup page 11 | ``` 12 | 13 | - Select valid options in GUI prompt 14 | 15 | - working mode 16 | 17 | - `online` 18 | - for host system 19 | - select valid access method for online mode from GUI dropdown menu 20 | 21 | - `offline` 22 | - for xml/bin/rom file 23 | 24 | - Publish all knobs 25 | - user can select whether to publish all knobs or not (applicable for **online** mode, and for **bin/rom** file for offline mode) 26 | 27 | > Note: For XML or BIOS binary File as input All knobs will be published 28 | 29 | #### Prefix of dropdown Knob options selection values in GUI are as below 30 | 31 | | Prefix | Interpretation | 32 | |--------|-----------------------------------| 33 | | ◇ | Default Value of knob | 34 | | ◈ | Current value of knob | 35 | | ◆ | current and default value of knob | 36 | 37 | #### Interpretation of buttons on Virtual Setup Page GUI 38 | 39 | | Button | Interpretation | 40 | |-----------------|--------------------------------------------------------------------------------------------------------------| 41 | | Push Changes | Apply changes to system if online mode else to `bin/rom` file, (N/A for offline xml) | 42 | | View Changes | View saved changes in new window | 43 | | Exit | Exit the GUI | 44 | | Reload | Reload the GUI | 45 | | Discard Changes | Discard any change made, any value if modified are restored to current value (`CurrentVal`) of knob xml file | 46 | | Load Defaults | Restore to default values and revert any changes made | 47 | 48 | #### Status of buttons and action on Virtual Setup Page GUI on various modes/scenarios 49 | 50 | | Button | Online | Offline `.xml` | Online `.xml` | `.bin` or `.rom` file | 51 | |-----------------|----------------------------------------|----------------|--------------------------------------------------|-----------------------------------| 52 | | Push Changes | ✔ changes directly written to the SUT | ❌ | ❌ (if _Publish all_ selected in previous option) | ✔ changes written to new bin file | 53 | | View Changes | ✔ | ✔ | ✔ | ✔ | 54 | | Exit | ✔ | ✔ | ✔ | ✔ | 55 | | Reload | ✔ | ✔ | ✔ | ✔ | 56 | | Discard Changes | ✔ | ✔ | ✔ | ✔ | 57 | | Load Defaults | ✔ | ❌ | ❌ | ❌ | 58 | -------------------------------------------------------------------------------- /src/xmlcli/modules/eBiosSetupPage/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | 6 | # Custom imports 7 | from .eBiosSetupPage import gen_gui 8 | 9 | 10 | if __name__ == "__main__": 11 | pass 12 | -------------------------------------------------------------------------------- /src/xmlcli/modules/helpers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | This file consists of various helper methods for the XmlCli existing operations 4 | """ 5 | __author__ = "Gahan Saraiya" 6 | 7 | # Built-in imports 8 | import os 9 | 10 | # Custom imports 11 | from .. import XmlCli as cli 12 | from .. import XmlCliLib as clb 13 | from ..common import utils 14 | from ..common.logger import log 15 | from ..XmlIniParser import nstrip 16 | from .winContextMenu.install_context_menu import install_context_menu 17 | 18 | try: 19 | from defusedxml import ElementTree as ET 20 | except ModuleNotFoundError as e: 21 | log.warn("Insecure module import used! Please install all the required dependencies by running `pip install -r requirements.txt`") 22 | from xml.etree import ElementTree as ET 23 | 24 | 25 | def move_output_files_to(destination_path): 26 | """ 27 | Move files from one directory to another 28 | 29 | :param destination_path: destination directory 30 | :return: 31 | """ 32 | if not os.path.exists(destination_path): 33 | log.error(f'{destination_path} does not exists, not able to move out files\n') 34 | return 1 35 | new_out_dir = os.path.join(destination_path, 'out') 36 | os.makedirs(new_out_dir, exist_ok=True) 37 | for root, dirs, files in os.walk(clb.TempFolder): 38 | for name in files: 39 | if not (name == 'Makefile'): 40 | if os.path.exists(os.path.join(new_out_dir, name)): 41 | os.remove(os.path.join(new_out_dir, name)) 42 | os.rename(os.path.join(root, name), os.path.join(new_out_dir, name)) 43 | return 0 44 | 45 | 46 | def move_output_to_current_directory(): 47 | """ 48 | Move xmlcli output directory to current directory 49 | :return: 50 | """ 51 | move_output_files_to(os.getcwd()) 52 | 53 | 54 | def print_nvar_response_buffer(nvar_response_buffer_file=None): 55 | """ 56 | Print nvar response buffer 57 | :param nvar_response_buffer_file: Nvar response buffer file 58 | :return: 59 | """ 60 | if not nvar_response_buffer_file: 61 | nvar_response_buffer_file = os.path.join(clb.TempFolder, 'NvarRespBuff.bin') 62 | with open(nvar_response_buffer_file, 'rb') as out_file: # opening for writing 63 | current_parameters = list(out_file.read()) 64 | current_parameter_size = len(current_parameters) 65 | response_buffer_ptr = 0 66 | header = ['Offset Ptr', 'Status', 'Attribute', 'Data Size', 'Variable Name', 'VarGuid'] 67 | data_lis = [] 68 | for _ in range(0, 0x100): 69 | if response_buffer_ptr >= current_parameter_size: 70 | break 71 | offset = response_buffer_ptr 72 | guid = clb.FetchGuid(current_parameters, response_buffer_ptr) 73 | attribute = clb.ReadList(current_parameters, response_buffer_ptr + 0x10, 4) 74 | size = clb.ReadList(current_parameters, response_buffer_ptr + 0x14, 4) 75 | status = clb.ReadList(current_parameters, response_buffer_ptr + 0x18, 4) 76 | operation = clb.ReadList(current_parameters, response_buffer_ptr + 0x1C, 1) 77 | name = '' 78 | for VarSizeCount in range(0, 0x80): 79 | val = clb.ReadList(current_parameters, (response_buffer_ptr + 0x1D + VarSizeCount), 1) 80 | if val == 0: 81 | response_buffer_ptr = response_buffer_ptr + 0x1D + VarSizeCount + 1 82 | break 83 | name += chr(val) 84 | response_buffer_ptr = response_buffer_ptr + size 85 | data_lis.append([hex(offset), hex(status), hex(attribute), hex(size), name, clb.GuidStr(guid)]) 86 | 87 | log.result(utils.Table().create_table((header, data_lis))) 88 | 89 | 90 | def generate_knobs_delta(ref_xml, new_xml, out_file=r'KnobsDiff.log', compare_tag='default'): 91 | """ 92 | Take difference of Setup Option between two BIOS/IFWI xml file 93 | 94 | :param ref_xml: reference bios/ifwi binary 95 | :param new_xml: another bios/ifwi binary to compare against reference file 96 | :param out_file: output file location to store difference result at 97 | :param compare_tag: xml attribute to be compared against (default|CurrentVal|size|prompt|depex|...) 98 | :return: file content of result_log_file 99 | 100 | Usage: 101 | >>> generate_knobs_delta("/path/to/PlatformConfig.2277_FwInfo.xml", "/path/to/PlatformConfig.2283_FwInfo.xml", "KnobsDiff.log") 102 | """ 103 | ref_tree = None 104 | new_tree = None 105 | ref_tree = ET.parse(ref_xml) 106 | new_tree = ET.parse(new_xml) 107 | ref_knobs_map = {} 108 | new_knobs_map = {} 109 | bios_tag = ['BIOS', 'SVBIOS', 'CPUSVBIOS'] 110 | ref_knobs_bios_version = '' 111 | new_knobs_bios_version = '' 112 | internal_compare_tags = ['default', 'offset', 'CurrentVal', 'size', 'varstoreIndex'] 113 | all_compare_tags = ['default', 'offset', 'CurrentVal', 'size', 'prompt', 'description', 'depex', 'setupType', 'varstoreIndex'] 114 | current_compare_tags = [] 115 | for tag_value in compare_tag.split(','): 116 | tag_value = tag_value.strip() 117 | if tag_value in all_compare_tags: 118 | current_compare_tags.append(tag_value) 119 | 120 | for count in range(0, 3): 121 | if ref_knobs_bios_version == '': 122 | for ref_xml_bios in ref_tree.iter(tag=bios_tag[count]): 123 | ref_knobs_bios_version = nstrip(ref_xml_bios.get('VERSION')) 124 | break 125 | 126 | for count in range(0, 3): 127 | if new_knobs_bios_version == '': 128 | for MyXmlBios in new_tree.iter(tag=bios_tag[count]): 129 | new_knobs_bios_version = nstrip(MyXmlBios.get('VERSION')) 130 | break 131 | 132 | for ref_setup_knobs in ref_tree.iter(tag='biosknobs'): 133 | for ref_bios_knob in ref_setup_knobs: 134 | ref_setup_type = (nstrip(ref_bios_knob.get('setupType'))).upper() 135 | if ref_setup_type in ['ONEOF', 'CHECKBOX', 'NUMRIC', 'NUMERIC', 'STRING']: 136 | ref_knob_name = nstrip(ref_bios_knob.get('name')) 137 | ref_knobs_map[ref_knob_name] = {} 138 | for current_tag in current_compare_tags: 139 | if current_tag in internal_compare_tags: 140 | ref_value = int(nstrip(ref_bios_knob.get(current_tag)), 16) 141 | else: 142 | ref_value = nstrip(ref_bios_knob.get(current_tag)) 143 | ref_knobs_map[ref_knob_name][current_tag] = ref_value 144 | for new_setup_knobs in new_tree.iter(tag='biosknobs'): 145 | for new_bios_knob in new_setup_knobs: 146 | current_setup_type = (nstrip(new_bios_knob.get('setupType'))).upper() 147 | if current_setup_type in ['ONEOF', 'CHECKBOX', 'NUMRIC', 'NUMERIC', 'STRING']: 148 | new_knob_name = nstrip(new_bios_knob.get('name')) 149 | new_knobs_map[new_knob_name] = {} 150 | for current_tag in current_compare_tags: 151 | if current_tag in internal_compare_tags: 152 | new_value = int(nstrip(new_bios_knob.get(current_tag)), 16) 153 | else: 154 | new_value = nstrip(new_bios_knob.get(current_tag)) 155 | new_knobs_map[new_knob_name][current_tag] = new_value 156 | 157 | file_content = "" 158 | log_msg = f'\n\nWriting delta knobs for comparing following fields \"{compare_tag}\"\n RefXmlBiosVer = Arg 1 File = {ref_knobs_bios_version} \n MyXmlBiosVer = Arg 2 File = {new_knobs_bios_version}\n' 159 | log.info(log_msg) 160 | if os.path.splitext(out_file)[-1].lower() not in ['.ini', '.cfg']: 161 | file_content += log_msg 162 | else: 163 | file_content += ';-------------------------------------------------\n; Knob Entries for XmlCli based setup, trying to clone {current_compare_tags[0]} from File 2\n; The name entry here should be identical as the name from the XML file (retain the case)\n;-------------------------------------------------\n[BiosKnobs]\n' 164 | header_list = ['Knob Name (compare_tag)', 'RefXmlDefVal (Arg 1 File)', 'MyXmlDefVal (Arg 2 File)'] 165 | missing_in_new_knobs = [] 166 | knobs_dictionary=[] 167 | for knob in ref_knobs_map: 168 | if knob not in new_knobs_map: 169 | missing_in_new_knobs.append(knob) 170 | else: 171 | print_first_tag = True 172 | for current_tag in current_compare_tags: 173 | ref_str_value = ref_knobs_map[knob][current_tag] 174 | new_str_value = new_knobs_map[knob][current_tag] 175 | if ref_str_value != new_str_value: 176 | knobs_dictionary.append([f"{knob} ({current_tag})",ref_str_value,new_str_value]) 177 | if os.path.splitext(out_file)[-1].lower() in ['.ini', '.cfg']: 178 | if print_first_tag: 179 | file_content += f'{knob} = {new_str_value}\n' 180 | print_first_tag = False 181 | new_knobs_map.pop(knob) 182 | missing_in_ref_knobs = [] 183 | for knob in new_knobs_map: 184 | missing_in_ref_knobs.append(knob) 185 | if os.path.splitext(out_file)[-1].lower() not in ['.ini', '.cfg']: 186 | file_content += utils.Table().create_table(header=header_list, data=knobs_dictionary, width=0) 187 | log.result(utils.Table().create_table(header=header_list, data=knobs_dictionary, width=0)) 188 | 189 | 190 | if len(missing_in_ref_knobs) != 0: 191 | log.info(f'Following Knobs are missing in Arg 1 File\n\t [ {", ".join(missing_in_ref_knobs)} ]') 192 | if os.path.splitext(out_file)[-1].lower() not in ['.ini', '.cfg']: 193 | file_content += f'Following Knobs are missing in Arg 1 File\n\t [ {", ".join(missing_in_ref_knobs)} ]\n' 194 | if len(missing_in_new_knobs) != 0: 195 | log.info(f'Following Knobs are missing in Arg 2 File\n\t [ {", ".join(missing_in_new_knobs)} ]') 196 | if os.path.splitext(out_file)[-1].lower() not in ['.ini', '.cfg']: 197 | file_content += f'Following Knobs are missing in Arg 2 File\n\t [ {", ".join(missing_in_new_knobs)} ]\n' 198 | 199 | with open(out_file, 'w') as out: 200 | out.write(file_content) 201 | 202 | def compare_bios_knobs(reference_bin_file, new_bin_file, result_log_file=r'KnobsDifference.log', compare_tag='default'): 203 | """Take difference of Setup Option between two BIOS/IFWI 204 | 205 | :param reference_bin_file: reference bios/ifwi binary 206 | :param new_bin_file: another bios/ifwi binary to compare against reference file 207 | :param result_log_file: output file location to store difference result at 208 | :param compare_tag: xml attribute to be compared against (default|CurrentVal|size|prompt|depex|...) 209 | :return: file content of result_log_file 210 | """ 211 | reference_xml = clb.KnobsXmlFile.replace('BiosKnobs', 'RefBiosKnobs') 212 | new_xml = clb.KnobsXmlFile.replace('BiosKnobs', 'MyBiosKnobs') 213 | cli.savexml(reference_xml, reference_bin_file) 214 | cli.savexml(new_xml, new_bin_file) 215 | generate_knobs_delta(reference_xml, new_xml, result_log_file, compare_tag) 216 | 217 | 218 | def launch_web_gui(): 219 | from xmlcli.modules.webgui import main 220 | 221 | main.run_gui() 222 | 223 | 224 | if __name__ == "__main__": 225 | install_context_menu() 226 | -------------------------------------------------------------------------------- /src/xmlcli/modules/winContextMenu/README.md: -------------------------------------------------------------------------------- 1 | Context Menu Support 2 | ==================== 3 | 4 | Supported OS: `Windows` 5 | Currently supported interface: `Offline [binary file only]` 6 | 7 | Instruction of Installation 8 | --------------------------- 9 | 10 | #### Step 1: Execute `install_context_menu.py` with python as below: 11 | 12 | ```shell 13 | python install_context_menu.py 14 | ``` 15 | 16 | > It is recommended that you are executing it from path within module then it 17 | > would appear as below result 18 | 19 | ```shell 20 | C:\>python 21 | Python 3.8.8 (tags/v3.8.8:024d805, Feb 19 2021, 13:18:16) [MSC v.1928 64 bit (AMD64)] on win32 22 | Type "help", "copyright", "credits" or "license" for more information. 23 | >>> from xmlcli import XmlCli as cli 24 | >>> cli 25 | 26 | >>> cli.helpers.install_context_menu() 27 | script used from: 28 | script path: C:\Users\gsaraiya\AppData\Local\Programs\Python\Python38\lib\site-packages\xmlcli 29 | Registry install status = 0 30 | 0 31 | >>> 32 | ``` 33 | 34 | #### Step 2: Allow permission for adding in registry 35 | 36 | You will be prompted to ask permission for registry editor. 37 | 38 | As we are modifying Windows registry to add context menu it requires 39 | elevated privilege and confirmation. 40 | You shall see prompt awaiting your response as in this snapshot: 41 | 42 | ![image](https://github.com/intel-innersource/applications.validation.platform-automation.xmlcli.xmlcli/assets/8687603/d5ecd536-ddfa-47f7-afe8-efd316ab202d) 43 | 44 | ##### Step 3: Allow modification 45 | 46 | ![image](https://github.com/intel-innersource/applications.validation.platform-automation.xmlcli.xmlcli/assets/8687603/39bfd218-0ede-4032-be01-eb9dd9f780a0) 47 | 48 | ##### Step 4: Success 49 | 50 | Observe success response as below: 51 | 52 | ![image](https://github.com/intel-innersource/applications.validation.platform-automation.xmlcli.xmlcli/assets/8687603/ea78e5ae-ca39-40eb-81c6-3f1dbf36af18) 53 | 54 | 55 | 56 | Usage 57 | ----- 58 | 59 | #### Step 1: Right-click to launch context menu for any valid binary (BIOS/IFWI) as below: 60 | 61 | ![image](https://github.com/intel-innersource/applications.validation.platform-automation.xmlcli.xmlcli/assets/8687603/0daffa7f-b4ea-4494-bcfd-8afc484ef54b) 62 | 63 | You can find here various options of your choice and use as shortcut instead of using commandline everytime. 64 | -------------------------------------------------------------------------------- /src/xmlcli/modules/winContextMenu/XmlCli-square.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/modules/winContextMenu/XmlCli-square.ico -------------------------------------------------------------------------------- /src/xmlcli/modules/winContextMenu/XmlCli-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/modules/winContextMenu/XmlCli-square.png -------------------------------------------------------------------------------- /src/xmlcli/modules/winContextMenu/install_context_menu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | # Built-in imports 5 | import os 6 | import sys 7 | 8 | # Custom imports 9 | import xmlcli 10 | from xmlcli.modules.winContextMenu.xmlcli_registry_listener import RegistryListener 11 | 12 | XMLCLI_DIR = os.path.dirname(xmlcli.__file__) 13 | CONTEXT_MENU_PROJECT = os.path.join(XMLCLI_DIR, "modules", "winContextMenu") 14 | REGISTRY_HANDLER_FILE = os.path.join(CONTEXT_MENU_PROJECT, "xmlcli_registry_listener.py") 15 | ICON_FILE = os.path.join(CONTEXT_MENU_PROJECT, "XmlCli-square.ico") 16 | 17 | REG_FILE = os.path.join(CONTEXT_MENU_PROJECT, "install_xmlcli_menu.reg") 18 | 19 | CONTEXT_MENU_NAME = "XmlCli Menu" 20 | 21 | 22 | def install_context_menu(): 23 | if not sys.platform.startswith('win32'): 24 | print("Context Menu not supported on this OS") 25 | return False 26 | registry_obj = RegistryListener(xmlcli_path=XMLCLI_DIR) 27 | registry_content = registry_obj.create_registry_file(context_menu_name=CONTEXT_MENU_NAME, icon=ICON_FILE.replace("\\", "\\\\")) 28 | 29 | with open(REG_FILE, "w") as reg_ptr: 30 | reg_ptr.write(registry_content) 31 | 32 | status = os.system(f"cmd /c {REG_FILE}") 33 | print(f"Registry install status = {status}") 34 | return status 35 | 36 | 37 | if __name__ == "__main__": 38 | install_context_menu() 39 | -------------------------------------------------------------------------------- /src/xmlcli/modules/winContextMenu/xmlcli_registry_listener.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is file used to listen to windows context menu registry action 3 | """ 4 | import os 5 | import sys 6 | from collections import namedtuple 7 | from collections import OrderedDict 8 | 9 | # Global variables 10 | CMD_MAP = namedtuple("COMMAND_MAP", ["name", "method", "prompt_name"]) 11 | 12 | 13 | class RegistryListener(object): 14 | """ 15 | XmlCli Class which provides methods to execute for registry 16 | """ 17 | def __init__(self, xmlcli_path=None): 18 | from xmlcli import XmlCli as cli 19 | from xmlcli.common import utils 20 | self.xmlcli_path = xmlcli_path if xmlcli_path else os.path.dirname(cli.__file__) 21 | 22 | self.cli = cli 23 | print(f"script used from: {cli}") 24 | print(f"script path: {self.xmlcli_path}") 25 | 26 | self.interface = "stub" if len(sys.argv) <= 2 else sys.argv[2] 27 | self.command = "all" if len(sys.argv) <= 3 else sys.argv[3] 28 | bin_arg_path = "" if len(sys.argv) <= 1 else f"{sys.argv[1]}" 29 | # validate safe path 30 | if bin_arg_path and utils.is_safe_path(os.getcwd(), bin_arg_path): 31 | self.binary_file_path = os.path.abspath(bin_arg_path) 32 | self.binary_file_name = os.path.basename(self.binary_file_path) 33 | self.output_directory = os.path.dirname(self.binary_file_path) 34 | print(self.output_directory) 35 | else: 36 | pass 37 | # raise utils.XmlCliException("Unsafe file path used...") 38 | self.command_method_map = OrderedDict({ 39 | "all" : CMD_MAP("all", self.command_all, "Run All"), 40 | "savexml" : CMD_MAP("savexml", self.command_savexml, "Save XML"), 41 | "generatejson": CMD_MAP("generatejson", self.command_generate_json, "Parse firmware as json"), 42 | "shell" : CMD_MAP("shell", self.command_launch_shell, "Launch Shell"), 43 | }) 44 | 45 | def create_registry_file(self, context_menu_name="XmlCli Menu", icon=""): 46 | from xmlcli import __version__ 47 | 48 | command_content = f""" 49 | ; -------------------------------------------------------------------------------------------------- 50 | [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CommandStore\\shell\\version] 51 | @="Version - {__version__}" 52 | "Icon"="{icon},0" 53 | """ 54 | sub_commands = "version;" 55 | registry_file_path = __file__.replace("\\", "\\\\") 56 | python_path = sys.executable.replace("\\", "\\\\") 57 | for key, cmd in self.command_method_map.items(): 58 | sub_commands += f"{cmd.name};" 59 | command_content += f""" 60 | ; -------------------------------------------------------------------------------------------------- 61 | [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CommandStore\\shell\\{cmd.name}] 62 | @="{cmd.prompt_name}" 63 | "Icon"="{icon},0" 64 | 65 | [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\CommandStore\\shell\\{cmd.name}\\command] 66 | @="\\"{python_path}\\" \\"{registry_file_path}\\" \\"%1\\" stub {cmd.name}" 67 | """ 68 | 69 | menu_content = f"""Windows Registry Editor Version 5.00 70 | 71 | ; Registry file generated by XmlCli to register/update context menu 72 | [HKEY_CLASSES_ROOT\\*\\shell\\{context_menu_name.replace(" ", "")}] 73 | "MUIVerb"="{context_menu_name}" 74 | "SubCommands"="{sub_commands}" 75 | "Icon"="{icon},0" 76 | """ 77 | registry_content = menu_content + command_content 78 | return registry_content 79 | 80 | def command_all(self): 81 | """ 82 | Execute all commands except for launching 83 | :return: 84 | """ 85 | self.output_directory = os.path.join(self.output_directory, os.path.splitext(self.binary_file_name)[0]) 86 | os.makedirs(self.output_directory, exist_ok=True) 87 | print(self.output_directory) 88 | for key, cmd in self.command_method_map.items(): 89 | if cmd.name not in ("gengui", "all"): 90 | cmd.method() 91 | 92 | def command_generate_json(self): 93 | from xmlcli.common import bios_fw_parser as parser 94 | 95 | print("script used from: ", parser) 96 | uefi_parser = parser.UefiParser(bin_file=self.binary_file_path, clean=False) 97 | output = uefi_parser.parse_binary() 98 | output = uefi_parser.sort_output_fv(output) 99 | output_json_file = os.path.join(self.output_directory, "{}.json".format(self.binary_file_name)) 100 | uefi_parser.write_result_to_file(output_json_file, output_dict=output) 101 | 102 | def command_savexml(self): 103 | output_xml_file = os.path.join(self.output_directory, "{}.xml".format(self.binary_file_name)) 104 | 105 | if self.interface == "stub": 106 | self.cli.savexml(output_xml_file, self.binary_file_path) 107 | else: 108 | self.cli.clb._setCliAccess(self.interface) 109 | self.cli.savexml(output_xml_file) 110 | 111 | def command_launch_shell(self): 112 | import subprocess 113 | 114 | startup_script = os.path.join(self.xmlcli_path, "start_xmlcli.py") 115 | cmd = [sys.executable, "-i", startup_script] 116 | subprocess.check_call(cmd) 117 | 118 | 119 | if __name__ == "__main__": 120 | try: 121 | XMLCLI_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 122 | registry_listener = RegistryListener(xmlcli_path=XMLCLI_DIR) 123 | 124 | cmd_method = registry_listener.command_method_map.get(registry_listener.command, "shell") 125 | cmd_method.method() 126 | 127 | except Exception as e: 128 | print(e) 129 | 130 | input("Press any key to exit") 131 | -------------------------------------------------------------------------------- /src/xmlcli/out/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /src/xmlcli/start_xmlcli.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = "Gahan Saraiya" 3 | 4 | import os 5 | import sys 6 | 7 | 8 | def cli(): 9 | """Shell Entry function for executable 10 | """ 11 | try: 12 | # launch XmlCli modules in shell as default operation to perform 13 | from xmlcli import XmlCli as cli 14 | from xmlcli._version import __version__ 15 | from xmlcli.common import bios_fw_parser 16 | from xmlcli.common import logger 17 | from xmlcli.common import utils 18 | from xmlcli.modules import helpers 19 | from xmlcli.modules.winContextMenu import install_context_menu 20 | 21 | print(f"xmlcli v{__version__}\n{os.path.dirname(cli.__file__)}") 22 | if len(sys.argv) > 1: 23 | if sys.argv[1].lower() in ('--version', '-v'): 24 | exit(1) 25 | if sys.argv[1].lower() in ('install', '--install'): 26 | install_context_menu.install_context_menu() 27 | if not set(sys.argv).intersection({'py', '--py', 'ipy', '--ipy'}): 28 | exit(1) 29 | 30 | except Exception as e: 31 | print( 32 | f"Exception occurred: {e}\nCould not find XmlCli." 33 | ) 34 | 35 | try: 36 | if not set(sys.argv).intersection({'py', '--py'}): 37 | # try to launch interactive python shell 38 | from IPython import embed 39 | 40 | embed(colors="neutral") 41 | except (ImportError, TypeError) as e: 42 | print("IPython needed for interactive usage") 43 | 44 | 45 | if __name__ == "__main__": 46 | cli() 47 | -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cp310-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cp310-win_amd64.pyd -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cp311-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cp311-win_amd64.pyd -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cp37-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cp37-win_amd64.pyd -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cp38-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cp38-win_amd64.pyd -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cp39-win_amd64.pyd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cp39-win_amd64.pyd -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cpython-310-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cpython-310-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cpython-311-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cpython-311-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cpython-37m-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cpython-37m-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cpython-38-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cpython-38-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /src/xmlcli/tools/EnableXmlCli.cpython-39-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/EnableXmlCli.cpython-39-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /src/xmlcli/tools/TianoCompress: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/TianoCompress -------------------------------------------------------------------------------- /src/xmlcli/tools/TianoCompress.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/intel/xml-cli/378edde20ef736ffa6f8c7ec93e7965aaa19b69d/src/xmlcli/tools/TianoCompress.exe -------------------------------------------------------------------------------- /src/xmlcli/tools/__init__.py: -------------------------------------------------------------------------------- 1 | # Added to support import requirements 2 | -------------------------------------------------------------------------------- /src/xmlcli/xmlcli.config: -------------------------------------------------------------------------------- 1 | # Please use `/` only as path separator, it'll allow to work the test suite independent of OS (windows/linux/mac/efi) 2 | [GENERAL_SETTINGS] 3 | # default general configurations 4 | # If blank or not able to use specified access method then "stub" offline method will be considered 5 | ACCESS_METHOD = stub 6 | # Default encoding method to be used i.e. ascii|utf-8|utf-16 7 | ENCODING = utf-8 8 | # Performance settings allows to avoid unnecessary imports of file methods 9 | PERFORMANCE = False 10 | 11 | [DIRECTORY_SETTINGS] 12 | # path from xmlcli package at where all the output file should be stored 13 | # Absolute path will be considered first, but if that does not exist then relative path will be checked and used! 14 | # if OUT_DIR is specified as `out` then it's relative path would be xmlcli/out 15 | OUT_DIR = out 16 | 17 | [TOOL_SETTINGS] 18 | # This settings contains location for binary files 19 | # as default scripts will look for absolute path, else it will look to relative path from tool directory under xmlcli/tools/ 20 | # Tiano Compression Utility 21 | TIANO_COMPRESS_BIN = TianoCompress 22 | # Brotli Compression Utility 23 | BROTLI_COMPRESS_BIN = Brotli 24 | 25 | [LOG_SETTINGS] 26 | # Set logging title which is to be displayed in log file 27 | LOGGER_TITLE = XmlCli 28 | # Toggle whether to log the output processed by test_suite to the file or not 29 | FILE_LOG = True 30 | # Toggle whether to log the output processed to the console or not 31 | CONSOLE_STREAM_LOG = True 32 | # directory name for storing log files (relative to the OUT_DIR folder mentioned at DIRECTORY_SETTINGS > OUT_DIR) 33 | # if LOG_DIR is specified as `logs' then it's relative path would be xmlcli/out/logs 34 | LOG_DIR = logs 35 | # OPTIONS for LOG_LEVEL = DEBUG|INFO|ERROR|WARN|CRITICAL|RESULT 36 | LOG_LEVEL = INFO 37 | # Below option is to differ console log level from file logging 38 | CONSOLE_LOG_LEVEL = INFO 39 | # Defines format of logging 40 | # Print only message if LOG_LEVEL is set to INFO 41 | INFO_LOG_FORMAT = %(message)s 42 | RESULT_LOG_FORMAT = %(message)s 43 | WARN_LOG_FORMAT = %(message)s 44 | ERROR_LOG_FORMAT = %(message)s 45 | # Detailed logging if log level set to DEBUG 46 | DEBUG_LOG_FORMAT = [%(filename)s:%(lineno)3s - %(funcName)20s() T%(asctime)s [%(name)-8s] [%(levelname)s]]: %(message)s 47 | # Date format for logging 48 | # Date format used in log file with log messages 49 | LOG_DATE_FORMAT = %Y-%d-%m_%H.%M.%S.%f 50 | # Name of log file [optional - to create log file name with customized name] 51 | LOG_FILE_NAME = XmlCli.log 52 | # Date format to be append to store log file name 53 | LOG_FILE_NAME_DATE = %Y-%d-%m 54 | # Specify whether to display configurations of directory and logging or not 55 | DISPLAY_CONFIGURATION = False 56 | 57 | [INITIAL_CLEANUP] 58 | # If set to True, it would consume a time to cleanup the files (may looks as a performance gap) 59 | # If set to False, none of the configurations below matter!!! 60 | CLEANUP = True 61 | # Specify file extension to perform/remove those files from directory structure before executing any commands 62 | CACHE_CLEAN_EXTENSIONS = .pyc,.pyo 63 | # Specify whether to delete all files in OUT_DIR 64 | CLEAN_OUT_DIR = False 65 | 66 | [EXPERIMENTAL_FEATURES_SETTINGS] 67 | # Toggle whether to use experimental features or not 68 | ENABLE_EXPERIMENTAL_FEATURES = True 69 | 70 | [ACCESS_METHODS] 71 | # To link access method provide relative path from source (`src/`) directory or absolute path 72 | # To create new access method refer: access/base/README.md 73 | stub = access/stub/stub.ini 74 | offline = access/stub/stub.ini 75 | # Interface for Linux 76 | linux = access/linux/linux.ini 77 | # Interface for Windows 78 | winrwe = access/winrwe/winrwe.ini 79 | 80 | -------------------------------------------------------------------------------- /start_xmlcli.bat: -------------------------------------------------------------------------------- 1 | python -i src/xmlcli/start_xmlcli.py 2 | -------------------------------------------------------------------------------- /start_xmlcli.sh: -------------------------------------------------------------------------------- 1 | chmod +x src/xmlcli/tools/TianoCompress 2 | 3 | python -i src/xmlcli/start_xmlcli.py 4 | -------------------------------------------------------------------------------- /test_requirements.txt: -------------------------------------------------------------------------------- 1 | tox >= 3.23.0 2 | pygments >= 2.8.0 3 | jinja2 == 3.1.6 4 | babel >= 2.9.1 5 | sphinx >= 4.0.2 6 | pytest >= 7.4.3 7 | pytest-html >= 4.1.0 8 | setuptools >= 67.0.0 9 | 10 | urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability 11 | requests>=2.32.0 # not directly required, pinned by Snyk to avoid a vulnerability 12 | zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability 13 | -------------------------------------------------------------------------------- /tests/UefiParserTest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Built-in imports 4 | import os 5 | import sys 6 | import shutil 7 | import warnings 8 | import unittest 9 | 10 | warnings.simplefilter("ignore", ResourceWarning) 11 | 12 | # Custom imports 13 | from .UnitTestHelper import * 14 | from xmlcli.UefiFwParser import ProcessBin, PrintLogFile 15 | from xmlcli.common import utils 16 | from xmlcli.common import structure 17 | from xmlcli.common import bios_fw_parser 18 | from xmlcli.common import configurations 19 | 20 | __author__ = "Gahan Saraiya" 21 | 22 | PARSE_ALL = True 23 | # To override logging level while executing the test 24 | LOG_LEVEL = "DEBUG" # options for LOG_LEVEL = DEBUG|INFO|ERROR|WARN 25 | 26 | 27 | class UtilityTest(UnitTestHelper): 28 | def test_guid_structure_read(self): 29 | guid = "0x92daaf2f-0xc02b-0x455b-0xb2-0xec-0xf5-0xa3-0x59-0x4f-0x4a-0xea" 30 | guid_struct = structure.Guid() 31 | guid_struct.read_guid(guid) 32 | self.assertEqual(utils.guid_formatter(guid_struct.guid, string_format="xmlcli"), guid) 33 | 34 | 35 | class UefiParserTest(UnitTestHelper): 36 | guid_lis1 = [ 37 | # GUIDs available in ADL_FSP_0496_00_D.rom (may exist in other rom) 38 | "fc8fe6b5-cd9b-411e-bd8f31824d0cde3d", 39 | "eed5ea31-38e2-463d-b623-2c57702b8a1c", 40 | "a6aef1f6-f25a-4082-af39-22-29-bc-f5-a6-e1", 41 | "1b45cc0a-156a-428a-af-62-49-86-4d-a0-e6-e6", 42 | "0x9faad0ff-0x0e0c-0x4885-0xa738bab4e4fa1e66", 43 | "0x1008aed2-0x40bb-0x47c8-0xae8e-0x4e8fbefe8a1f", 44 | "0xf57757fc-0x2603-0x404f-0xaa-0xe2-0x34-0xc6-0x23-0x23-0x88-0xe8", 45 | ["4f84e985", "4c3b", "4825", "9f42889109019422"], 46 | ["8958d092", "7b26", "4e47", "bb98", "16ae2dc315a2"], 47 | ["0x9b7fa59d", "0x71c6", "0x4a36", "0x906e9725ea6add5b"], 48 | [0xae265864, 0xcf5d, 0x41a8, 0x91, 0x3d, 0x71, 0xc1, 0x55, 0xe7, 0x64, 0x42], 49 | ["0x6141e486", "0x7543", "0x4f1a", "0xa5", "0x79", "0xff", "0x53", "0x2e", "0xd7", "0x8e", "0x75"], 50 | ] 51 | guid_lis2 = [ 52 | [0x615E6021, 0x603D, 0x4124, 0xB7, 0xEA, 0xC4, 0x8A, 0x37, 0x37, 0xBA, 0xCD], 53 | [0xe3e49b8d, 0x1987, 0x48d0, 0x9a, 0x1, 0xed, 0xa1, 0x79, 0xca, 0xb, 0xd6], 54 | [0xABBCE13D, 0xE25A, 0x4d9f, 0xA1, 0xF9, 0x2F, 0x77, 0x10, 0x78, 0x68, 0x92], 55 | [0xc09c81cb, 0x31e9, 0x4de6, 0xa9, 0xf9, 0x17, 0xa1, 0x44, 0x35, 0x42, 0x45], 56 | [0x6B6FD380, 0x2C55, 0x42C6, 0x98, 0xBF, 0xCB, 0xBC, 0x5A, 0x9A, 0xA6, 0x66], 57 | [0x5c0083db, 0x3f7d, 0x4b20, 0xac, 0x9b, 0x73, 0xfc, 0x65, 0x1b, 0x25, 0x03], 58 | [0x5498AB03, 0x63AE, 0x41A5, 0xB4, 0x90, 0x29, 0x94, 0xE2, 0xDA, 0xC6, 0x8D], 59 | [0xaec3ff43, 0xa70f, 0x4e01, 0xa3, 0x4b, 0xee, 0x1d, 0x11, 0xaa, 0x21, 0x69], 60 | [0xBCEA6548, 0xE204, 0x4486, 0x8F, 0x2A, 0x36, 0xE1, 0x3C, 0x78, 0x38, 0xCE], 61 | [0x22819110, 0x7f6f, 0x4852, 0xb4, 0xbb, 0x13, 0xa7, 0x70, 0x14, 0x9b, 0x0c], 62 | [0xCB105C8B, 0x3B1F, 0x4117, 0x99, 0x3B, 0x6D, 0x18, 0x93, 0x39, 0x37, 0x16], 63 | [0xE6A7A1CE, 0x5881, 0x4b49, 0x80, 0xBE, 0x69, 0xC9, 0x18, 0x11, 0x68, 0x5C], 64 | [0x21535212, 0x83d1, 0x4d4a, 0xae, 0x58, 0x12, 0xf8, 0x4d, 0x1f, 0x71, 0x0d], 65 | [0x003e7b41, 0x98a2, 0x4be2, 0xb2, 0x7a, 0x6c, 0x30, 0xc7, 0x65, 0x52, 0x25], 66 | [0x1ae42876, 0x008f, 0x4161, 0xb2, 0xb7, 0x1c, 0xd, 0x15, 0xc5, 0xef, 0x43], 67 | [0x8C3D856A, 0x9BE6, 0x468E, 0x85, 0x0A, 0x24, 0xF7, 0xA8, 0xD3, 0x8E, 0x08] 68 | ] 69 | 70 | @property 71 | def bios_roms(self): 72 | bios_rom_location = self.bios_image_dir 73 | bios_roms = [os.path.join(bios_rom_location, bios_image) for bios_image in os.listdir(bios_rom_location) if self.is_valid_bios_image(bios_image)] 74 | stored_dump = self.get_online_bios_image 75 | if ONLINE_MODE and stored_dump: 76 | online_bios_image = os.path.join(bios_rom_location, "online_bios.bin") 77 | shutil.copy(stored_dump, online_bios_image) 78 | bios_roms = [online_bios_image] + bios_roms 79 | return bios_roms 80 | 81 | @property 82 | def lookup_guids(self): 83 | guid_lis = [] 84 | # guid_lis = self.guid_lis1 + self.guid_lis2 85 | return guid_lis 86 | 87 | @unittest.skipIf(True, "Always skipping this...") 88 | def test_clean_dir(self): 89 | # remove all files from temp directory 90 | self.assertTrue(utils.clean_directory(self.temp_dir)) 91 | 92 | files_in_temp_dir = [i for i in os.listdir(self.temp_dir) if os.path.isfile(i)] 93 | 94 | self.assertEqual(files_in_temp_dir, []) 95 | 96 | @unittest.skipIf(not PARSE_ALL, "Not Parsing all binaries as set on flag...") 97 | def test_parse_multiple_binaries(self): 98 | for bios_image in self.bios_roms: 99 | # self.log.info("{0}\n>>>>>>>>> PROCESSING IMAGE: {1} <<<<<<<<<\n{0}".format("=" * 50, bios_image)) 100 | self.parse_image(bios_image=bios_image) 101 | 102 | @unittest.skipIf(PARSE_ALL, "Skipping individual test as parsing all...") 103 | def test_write_to_json(self): 104 | self.parse_image(self.bios_image) 105 | 106 | def parse_image(self, bios_image): 107 | self.log.info(f"{'=' * 50}\n>>>>>>>>> PROCESSING IMAGE: {bios_image} <<<<<<<<<\n{'=' * 50}") 108 | binary_file_name = os.path.splitext(bios_image)[0] # get filename without extension 109 | platform = "_{}".format(sys.platform) 110 | output_file = os.path.join(self.bios_image_dir, "{}{}{}.json".format(binary_file_name, configurations.PY_VERSION, platform)) # create output json file to store 111 | base_address = 0x0 # base address to be specified 112 | # Initialize class instance 113 | uefi_parser = bios_fw_parser.UefiParser(bin_file=bios_image, 114 | parsing_level=0, 115 | base_address=base_address, 116 | guid_to_store=self.lookup_guids 117 | ) 118 | # Override logging level 119 | uefi_parser.override_log_level(LOG_LEVEL) 120 | # parse binary 121 | output_dict = uefi_parser.parse_binary() 122 | output_dict = uefi_parser.sort_output_fv(output_dict) 123 | # write content to json file 124 | uefi_parser.write_result_to_file(output_file, output_dict=output_dict) 125 | # Validate whether content written in json or not 126 | self.assertGreater(os.path.getsize(output_file), 0) 127 | 128 | if uefi_parser.guid_to_store: 129 | # additional test for GUIDs to store 130 | result = uefi_parser.guid_store_dir # result of passed guid 131 | user_guid_out_file = os.path.join(result, "{}{}{}_guids.json".format(binary_file_name, configurations.PY_VERSION, platform)) 132 | # Store guid stored result to json file 133 | uefi_parser.write_result_to_file(user_guid_out_file, output_dict=uefi_parser.stored_guids) 134 | # Validate whether content written in json or not 135 | self.assertGreater(os.path.getsize(user_guid_out_file), 0) 136 | 137 | def test_compare_with_old(self): 138 | for bios_image in self.bios_roms: 139 | with open(bios_image, 'rb') as BiosBinFile: 140 | BiosBinListBuff = list(BiosBinFile.read()) 141 | BiosEnd = len(BiosBinListBuff) 142 | with open(PrintLogFile, "w") as LogFile: 143 | ProcessBin(BiosBinListBuff=BiosBinListBuff, BiosFvBase=0x00, Files2saveGuidList=self.lookup_guids, LogFile=LogFile, BiosRegionEnd=BiosEnd) 144 | 145 | 146 | class OnlineTest(UefiParserTest): 147 | @property 148 | def bios_roms(self): 149 | bios_rom_location = self.bios_image_dir 150 | stored_dump = self.get_online_bios_image 151 | bios_roms = [] 152 | if stored_dump: 153 | online_bios_image = os.path.join(bios_rom_location, "online_bios.bin") 154 | shutil.copy(stored_dump, online_bios_image) 155 | bios_roms = [online_bios_image] 156 | return bios_roms 157 | 158 | 159 | class OfflineTest(UefiParserTest): 160 | @property 161 | def bios_roms(self): 162 | bios_rom_location = self.bios_image_dir 163 | bios_roms = [os.path.join(bios_rom_location, bios_image) for bios_image in os.listdir(bios_rom_location) if self.is_valid_bios_image(bios_image)] 164 | return bios_roms 165 | 166 | 167 | if __name__ == "__main__": 168 | unittest.main() 169 | -------------------------------------------------------------------------------- /tests/UnitTestHelper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Built-in imports 4 | import os 5 | import sys 6 | import shutil 7 | import unittest 8 | import warnings 9 | 10 | # Custom imports 11 | from xmlcli.common import utils 12 | from xmlcli.common import logger 13 | from xmlcli.common import configurations 14 | from xmlcli import XmlCli as cli 15 | 16 | __author__ = "Gahan Saraiya" 17 | __all__ = ["UnitTestHelper", 18 | "ONLINE_MODE", "OFFLINE_MODE", "RUN_OPTIONAL_TEST", 19 | "TEST_SUITE_CONFIG", "LOG_LEVEL", "LITE_FEATURE_TESTING"] 20 | 21 | import configparser 22 | 23 | TEST_DIR = os.path.dirname(os.path.abspath(__file__)) 24 | CONFIG_FILE = os.path.join(TEST_DIR, "tests.config") 25 | TEST_SUITE_CONFIG = configparser.RawConfigParser(allow_no_value=True) 26 | with open(CONFIG_FILE, "r") as f: 27 | TEST_SUITE_CONFIG._read(f, CONFIG_FILE) 28 | 29 | # Globals to decide which tests to execute 30 | TEST_SUITE_VERSION = TEST_SUITE_CONFIG.get("GENERAL_CONFIG", "VERSION") 31 | ONLINE_MODE = TEST_SUITE_CONFIG.getboolean("TEST_SETTINGS", "ONLINE_MODE") 32 | OFFLINE_MODE = TEST_SUITE_CONFIG.getboolean("TEST_SETTINGS", "OFFLINE_MODE") 33 | ACCESS_METHOD = TEST_SUITE_CONFIG.get("TEST_SETTINGS", "ACCESS_METHOD") 34 | RUN_OPTIONAL_TEST = TEST_SUITE_CONFIG.getboolean("TEST_SETTINGS", "RUN_OPTIONAL_TEST") 35 | BIOS_IMAGES_DIR = os.path.abspath(TEST_SUITE_CONFIG.get("TEST_SETTINGS", "BIOS_IMAGES_DIR")) 36 | LITE_FEATURE_TESTING = TEST_SUITE_CONFIG.getboolean("TEST_SETTINGS", "LITE_FEATURE_TESTING") 37 | 38 | LOG_TITLE = TEST_SUITE_CONFIG.get("LOG_SETTINGS", "LOGGER_TITLE") 39 | LOG_LEVEL = TEST_SUITE_CONFIG.get("LOG_SETTINGS", "LOG_LEVEL") 40 | 41 | settings = logger.Setup(log_title=LOG_TITLE, 42 | log_level=LOG_LEVEL, 43 | log_format=TEST_SUITE_CONFIG.get("LOG_SETTINGS", "LOG_FORMAT"), 44 | sub_module=TEST_SUITE_CONFIG.get("LOG_SETTINGS", "SUBMODULE_TITLE"), 45 | log_dir=os.path.abspath(os.path.join(configurations.OUT_DIR, TEST_SUITE_CONFIG.get("LOG_SETTINGS", "LOG_FILE_LOCATION"))), 46 | write_in_file=TEST_SUITE_CONFIG.getboolean("LOG_SETTINGS", "FILE_LOG"), 47 | print_on_console=TEST_SUITE_CONFIG.getboolean("LOG_SETTINGS", "CONSOLE_STREAM_LOG")) 48 | 49 | 50 | class UnitTestHelper(unittest.TestCase): 51 | access_mode = ACCESS_METHOD 52 | logger_settings = settings 53 | log = settings.logger 54 | 55 | @staticmethod 56 | def ignore_resource_warning(): 57 | warnings.simplefilter("ignore", ResourceWarning) 58 | 59 | def runTest(self, *args, **kwargs): 60 | pass 61 | 62 | @staticmethod 63 | def is_valid_bios_image(file, valid_extensions=(".rom", ".bin")): 64 | if os.path.splitext(file)[-1] in valid_extensions: 65 | return True 66 | 67 | @property 68 | def temp_dir(self): 69 | return os.path.join(self.bios_image_dir, "temp") 70 | 71 | @property 72 | def access_method(self): 73 | if self.access_mode: 74 | return self.access_mode 75 | else: 76 | access_mode = "winhwa" 77 | _platform = utils.PLATFORM_DETAILS[0].lower() 78 | if _platform.startswith("linux"): 79 | access_mode = "linux" 80 | elif _platform.startswith("vmkernel"): 81 | access_mode = "esxi" 82 | elif _platform.startswith("win"): 83 | access_mode = access_mode 84 | elif sys.platform == "uefi": 85 | access_mode = "uefi" 86 | return access_mode 87 | 88 | @property 89 | def bios_image_dir(self): 90 | return BIOS_IMAGES_DIR 91 | 92 | @property 93 | def bios_image(self): 94 | return self.bios_roms[0] 95 | 96 | @bios_image.setter 97 | def bios_image(self, value): 98 | self._bios_image = value 99 | 100 | @property 101 | def bios_roms(self): 102 | bios_rom_location = self.bios_image_dir 103 | bios_roms = [os.path.join(bios_rom_location, bios_image) for bios_image in os.listdir(bios_rom_location) if self.is_valid_bios_image(bios_image)] 104 | return bios_roms 105 | 106 | @bios_roms.setter 107 | def bios_roms(self, value): 108 | self._bios_roms = value 109 | 110 | 111 | if __name__ == "__main__": 112 | unittest.main() 113 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Unit test package for xmlcli.""" 2 | -------------------------------------------------------------------------------- /tests/bandit_scan.bat: -------------------------------------------------------------------------------- 1 | bandit .. -r -c ../.github/ipas_default.config 2 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Directory-wide pytest settings 3 | """ 4 | 5 | import pytest 6 | -------------------------------------------------------------------------------- /tests/tests.config: -------------------------------------------------------------------------------- 1 | # Please use `/` only as path separator, it'll allow to work the test suite independent of OS (windows/linux/mac/efi) 2 | [GENERAL_CONFIG] 3 | # default general configurations 4 | # VERSION defines version of the test suite 5 | VERSION = 0.0.1 6 | 7 | [TEST_SETTINGS] 8 | ONLINE_MODE = True 9 | OFFLINE_MODE = True 10 | ACCESS_METHOD = linux 11 | # Absolute directory path where BIOS images has been stored 12 | BIOS_IMAGES_DIR = /bios_images 13 | RUN_OPTIONAL_TEST = False 14 | LITE_FEATURE_TESTING = True 15 | 16 | [KNOB_TESTING] 17 | SKIP_RANDOM_KNOB_TESTING = False 18 | SKIP_BATCH_KNOB_TESTING = False 19 | SKIP_INDIVIDUAL_KNOB_TESTING = True 20 | # Subject to the length of specific knob type the minimum number would be choose among total length of knob 21 | # and specified below max knobs to be selected 22 | MAXIMUM_RANDOM_KNOBS_PER_TYPE = 1 23 | # absolute path to the ini file for ini file from which knobs are to be toggled 24 | KNOB_FILE_TESTING = xmlcli/out/BiosKnobs.ini 25 | 26 | [LOG_SETTINGS] 27 | # Set logging title which is to be displayed in log file 28 | LOGGER_TITLE = XmlCli 29 | # Set logging title which is to be displayed in log file 30 | SUBMODULE_TITLE = UnitTest 31 | # Toggle whether to log the output processed by test_suite to the file or not 32 | FILE_LOG = True 33 | # Toggle whether to log the output processed by test_suite to the console or not 34 | CONSOLE_STREAM_LOG = True 35 | # directory name for storing log files (relative to the root directory of xmlcli root directory) 36 | LOG_FILE_LOCATION = xmlcli/out/logs 37 | # OPTIONS for LOG_LEVEL = DEBUG|INFO|ERROR|WARN 38 | LOG_LEVEL = DEBUG 39 | # Defines format of logging 40 | LOG_FORMAT = [%(filename)s:%(lineno)3s - %(funcName)20s() T%(asctime)s [%(name)-8s] [%(levelname)s]]: %(message)s 41 | # Date format for logging 42 | # LOG_DATE_FORMAT = %Y-%d-%m 43 | LOG_DATE_FORMAT = %Y-%d-%m_%H.%M.%S 44 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | lint 4 | py37 5 | py38 6 | py39 7 | requires = 8 | virtualenv 9 | pip 10 | pytest 11 | pytest-html 12 | 13 | [testenv] 14 | setenv = 15 | PYTHONPATH = {toxinidir} 16 | 17 | passenv = 18 | IPC_PATH 19 | SystemDrive 20 | SystemRoot 21 | TEAMCITY_BUILDCONF_NAME 22 | LC_ALL 23 | LANG 24 | HOME 25 | 26 | deps = 27 | pytest 28 | pytest-html 29 | 30 | commands = 31 | pip install -e . 32 | pytest -v tests/CommonTests.py --html=pytest_report.html --self-contained-html 33 | 34 | [testenv:flake8] 35 | basepython = python 36 | deps = flake8 37 | commands = flake8 xmlcli tests 38 | 39 | [testenv:lint] 40 | commands = 41 | python -m pre_commit run {posargs:--all} 42 | deps = pre-commit>=1.20.0 43 | skip_install = true 44 | usedevelop = false 45 | 46 | [testenv:docs] 47 | passenv = 48 | LC_ALL 49 | LANG 50 | HOME 51 | commands = 52 | pip install -e . 53 | pip install -r requirements.txt 54 | pip install -r test_requirements.txt 55 | make docs 56 | whitelist_externals = make 57 | deps = 58 | sphinx 59 | skip_install = true 60 | --------------------------------------------------------------------------------