├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE.md ├── README.md ├── extra ├── 8A14_GetMem.uvt ├── 8A14_Set_CL34.uvt ├── 8A14_Set_CL34_SAGV.uvt ├── README.md └── uvt-logo.png └── src ├── config.rs ├── config └── locale_en.rs ├── data.rs ├── error.rs ├── firmware.rs ├── main.rs ├── parse.rs └── string.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # 2 | # -|- 3 | # | || /| UEFI Variable Tool (UVT) * Build Workflow 4 | # | || / | https://github.com/GeographicCone/UefiVarTool 5 | # `---'`-' `- Copyright © 2022 Datasone, © 2023 Piotr Szczepański 6 | # 7 | # Builds the application with the specified version data 8 | 9 | name: "UVT Build" 10 | run-name: "UEFI Variable Tool (UVT) Build by ${{ github.actor }}" 11 | 12 | on: 13 | workflow_call: 14 | inputs: 15 | version_number: 16 | description: "Version number (three dot-separated non-negative integers)" 17 | required: true 18 | type: string 19 | version_word: 20 | default: "Snapshot" 21 | description: "Build type (Preview, Release, Snapshot)" 22 | required: false 23 | type: string 24 | 25 | workflow_dispatch: 26 | inputs: 27 | version_number: 28 | default: "0.0.0" 29 | description: "Version number" 30 | required: true 31 | type: string 32 | version_word: 33 | default: "Snapshot" 34 | description: "Build type" 35 | options: 36 | - "Preview" 37 | - "Release" 38 | - "Snapshot" 39 | required: true 40 | type: choice 41 | 42 | env: 43 | CARGO_BUILD_TARGET: x86_64-unknown-uefi 44 | CARGO_TERM_COLOR: always 45 | CARGO_TOML: Cargo.toml 46 | 47 | permissions: 48 | contents: read 49 | 50 | jobs: 51 | build: 52 | name: "Build UVT" 53 | runs-on: ubuntu-latest 54 | 55 | strategy: 56 | matrix: 57 | profile: ["release"] 58 | 59 | steps: 60 | - name: "Checkout UVT" 61 | uses: actions/checkout@v4 62 | with: 63 | fetch-depth: 1 64 | 65 | - name: "Add Target" 66 | working-directory: ${{ github.workspace }} 67 | run: rustup target add "${{ env.CARGO_BUILD_TARGET }}" 68 | 69 | - name: "Update Version Number" 70 | working-directory: ${{ github.workspace }} 71 | run: "sed -e 's/^version = \"0.0.0\".*$/version = \"${{ inputs.version_number }}\" # Automatically overriden/' -i \"${{ env.CARGO_TOML }}\"" 72 | 73 | - name: "Run Cargo Build" 74 | working-directory: ${{ github.workspace }} 75 | run: BUILD_TYPE=${{ inputs.version_word }} cargo build --profile ${{ matrix.profile }} --verbose 76 | 77 | - name: "Upload UVT" 78 | uses: actions/upload-artifact@v3 79 | with: 80 | if-no-files-found: error 81 | name: ${{ github.event.repository.name }} 82 | path: target/${{ env.CARGO_BUILD_TARGET }}/${{ matrix.profile }}/${{ vars.BIN_FILE }} 83 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # 2 | # -|- 3 | # | || /| UEFI Variable Tool (UVT) * Release Workflow 4 | # | || / | https://github.com/GeographicCone/UefiVarTool 5 | # `---'`-' `- Copyright © 2022 Datasone, © 2023 Piotr Szczepański 6 | # 7 | # Triggers a build and uploads artifacts as release assets 8 | 9 | name: "UVT Release" 10 | run-name: "UEFI Variable Tool (UVT) Release by ${{ github.actor }}" 11 | 12 | on: 13 | release: 14 | types: [published] 15 | 16 | permissions: 17 | contents: write 18 | 19 | jobs: 20 | call-build: 21 | name: "Call Build" 22 | secrets: inherit 23 | uses: ./.github/workflows/build.yml 24 | with: 25 | version_number: ${{ github.event.release.tag_name }} 26 | version_word: ${{ github.event.release.prerelease && 'Preview' || 'Release' }} 27 | 28 | release: 29 | name: "Release UVT" 30 | needs: call-build 31 | runs-on: ubuntu-latest 32 | 33 | steps: 34 | - id: download 35 | name: "Download Artifacts" 36 | uses: actions/download-artifact@v3 37 | with: 38 | name: ${{ github.event.repository.name }} 39 | 40 | - name: 'List Artifacts' 41 | run: ls -1R ${{ steps.download.outputs.download-path }} 42 | 43 | - name: "Create Archive" 44 | run: zip -9 -r UVT.zip ./* 45 | 46 | - name: "Create Archive Comment" 47 | run: | 48 | cat >UVT.zip.txt <UEFI Variable Tool (UVT) Logo

2 | 3 | # UEFI Variable Tool (UVT) 4 | 5 | **UEFI Variable Tool (UVT)** is a command-line application that runs from the UEFI shell. It can be launched in seconds from any FAT flash drive with no prior machine-specific setup, and lets you view and modify the content of individual UEFI variables at a byte level. 6 | 7 | **UVT**'s purpose is to allow changing all the hidden _UEFI (BIOS) Setup_ hardware settings. It is well-suited for situations when custom firmware, such as a modified BIOS that would unhide all the menu forms, cannot be flashed due to restrictive anti-features such as _Boot Guard_ being enabled. 8 | 9 | While various utilities have existed for a long time to allow doing just the same, making the functionality as such hardly a novelty, **UVT** aims to make the process as efficient and unencumbered as possible. To that effect: 10 | * It greatly streamlines the command-line **argument syntax** 11 | * It can also work in **scripted mode** to execute an arbitrarily-long series of operations from the **standard input** (_stdin_) 12 | * While in this mode, it allows **defining aliases** to identify data of interest (variable name, offset, and size), and **referencing** these later for read and write operations 13 | * The **output format follows the input**, which means it can be saved to a file and fed back as standard input to restore the **saved settings** later 14 | 15 | ---- 16 | 17 | _**UVT** is free software, and full source code is available for anyone to tinker with. It is a quite heavily modified version of [setup_var.efi](https://github.com/datasone/setup_var.efi) by **[@datasone](https://github.com/datasone)**, to whom I originally made some [improvement suggestions](https://github.com/datasone/setup_var.efi/issues/14#issue-1863056272). He [followed up](https://github.com/datasone/setup_var.efi/issues/14#issuecomment-1727910880) on them and even graciously included me as a co-author in his commit, never mind that I had not at that point written a single line of code._ 18 | 19 | _**@datasone**'s [last-posted version](https://github.com/datasone/setup_var.efi/commit/8c72429113f6fc5e7a4aac63a323d51a2d9f9dd8) seemed at least 90% there but wasn't working for me in a couple of ways. Since he's probably busy with other stuff in his life, I decided it was my turn to contribute something. Hence this fork, which although is completely refactored to the point it might not look very much like the original, would have never been possible without all of **@datasone**'s work, which he generously shared with the world. For that I am eternally grateful to him, and want everyone to know he deserves all the credit as the **original author** of this utility._ 20 | 21 | ## How to Use 22 | 23 | The in-application usage information summary is reproduced below for reference: 24 | 25 | ```` 26 | Usage: uvt[.efi] [] [ [... []] 27 | - or - uvt[.efi] < 28 | Where: 29 | : Optional global-scope application settings 30 | -f --force Force-write values even if already set as requested 31 | -h --help Show usage information (precludes other operations) 32 | -r --restart Upon successful completion, perform a system restart 33 | -s --simulate Do not write, only simulate actions (will still read) 34 | : Operation(s) to perform, can be multiple, each in the format: 35 | [()]:[()][=] 36 | Arg Overview: 37 | UEFI variable name to read or write to, case-sensitive 38 | If two variables share a name, will prompt to use this 39 | Data starting position within the given UEFI variable 40 | Optional, a byte (1) by default if omitted; little-endian 41 | Value to write, 8 bytes (64 bits) maximum; read if absent 42 | Script to run, same base format as arguments + see below 43 | File Overview: 44 | # Comment, ignored until end of line 45 | ! Set options, same as above arguments 46 | ,:[()] Define a variable to reference later 47 | @[=] Assign to a referenced variable 48 | Example Command Line: 49 | uvt -s Lang:0x00 Lang:0x00(4)=0x01020304 Lang:0x00(4) 50 | Read byte at offset 0, simulate-set the dword (4 bytes), then read again 51 | Example Input File: 52 | !simulate # Simulate only, do not perform actual writes 53 | Language,Lang:0x00(4) # Define a reference under the alias "Language" 54 | @Language=0x01020304 # Write to the target referred to by "Language" 55 | 56 | , and can be decimal or hexadecimal: use prefix "0x" 57 | File should be a UTF-16 LE text, UEFI firmware and shell version-dependent 58 | Output saved to a file can be re-used as input again: format is the same 59 | ```` 60 | 61 | ### Prerequisites 62 | 63 | You need to boot into UEFI shell on the machine where you want to use the utility. This typically involves setting up a FAT flash drive and placing an EFI shell binary under the path `efi/boot/bootx64.efi`. You can then place the **UVT** binary under `efi/tools/uvt.efi` and run it as `uvt` regardless of what the current directory is. More on this in the _Background_ section. 64 | 65 | On a broader note, you need to know the variable layout, which is specific to your particular hardware, and possibly even the firmware version. How to obtain this information is also addressed in the _Background_ section. 66 | 67 | Separately, it is possible to run **UVT** in an emulator. As this would be mostly of interest to a developer, it is described in the _Building_ section. 68 | 69 | ### Command Line 70 | 71 | There are two ways to use **UVT**. The first one is to run it with command-line arguments. 72 | 73 | **UVT** accepts two kinds of command-line arguments: _operations_ and _options_. Arguments are separated with ` ` spaces. There is no limit on the number of arguments. 74 | 75 | #### Options 76 | 77 | _Options_ start with a `-` (minus) sign and are used to define global-scope settings. Each option has a short and a long form, taking a single `-` and a letter or a double `--` and a keyword respectively. The options are: 78 | * `-f` or `--force` Force-write values where the current values is equal to the new one. The default behavior is to skip such operations, and annotate such entries with an `# Already` comment in the output. 79 | * `-h` or `--help` Shows the usage information. If this option is selected, no other operations will be performed. 80 | * `-r` or `--restart` Reboots the system upon successful completion. No restart will be performed if any of the operations failed. 81 | * `-s` or `--simulate` If set, no changes will be made to UEFI variables. All the other aspects of the application will still be functioning exactly in the same way. This might be useful for checking what an operation would do, or whether the arguments are syntactically correct. If `-f` or `--force` is specified together with this option, no writing will happen regardless: the simulation takes precedence. 82 | 83 | #### Operations 84 | 85 | _Operations_ define either reading (querying, or getting) or writing (assigning, or setting) a value. The syntax is: 86 | 87 | ```` 88 | [()]:[()][=] 89 | ```` 90 | Where: 91 | * `` is the UEFI variable name. It is case-sensitive and mandatory: there is no default. 92 | * `` is an optional identifier to distinguish between variables in a situation when two or more share the same name. In the unlikely scenario this happens, the application will automatically list all the variables with the matching name, alongside with their respective identifiers. 93 | * `` is the position of data within the variable where the value data starts. Remember the count starts from 0, not 1. 94 | * `` is the optional size of the variable: it defaults to a single byte, i.e. `(1)`, which can also be specified, although that's unnecessary. The application can write at most 8 bytes (or 64 bits) at a time. 95 | * `` is the _new_ value to be written at the given offset. The value must fit within the `` constraint, which is checked. Multi-byte values are little-endian, which means that if you write `0x01` to 4 bytes starting at offset `0x00`, the value of `0x01` will be at the offset of `0x00` and not `0x03`, although if you _read_ these 4 bytes again, the result will also be shown as `0x00000001`. If you are unfamiliar with the concept or do not understand its implications, it's best to write individual bytes, and that's what the vast majority of _UEFI Setup_ settings are anyway. This part, alongside the `=` assignment operator, is optional: if absent, the default action is to query and output the _current_ value. 96 | 97 | For example: 98 | * `uvt Lang:0x00` reads the byte value at offset `0x00` in the variable `Lang` 99 | * `uvt -s Lang:0x00(4)=0x01020304` _simulates_ writing a double-word (four-byte) value to an offset starting at `0x00` in the variable `Lang` 100 | * `uvt Lang:0x00(4)` reads again the double word that has just been written with the preceding command 101 | 102 | An arbitrary number of command-line operations can be specified. They will be executed in the order entered. An error interrupts the processing of any further operations and arguments, terminating the application. 103 | 104 | #### Numerical Values 105 | 106 | Any number can be specified as either _decimal_ (base 10) or _hexadecimal_ (base 16). Hexadecimal values should be preceded by `0x` or `0X`, otherwise they will be parsed as decimal. Only digits `0-9` are allowed in decimal values. The additional digits `a-f` and `A-F` in hexadecimal values are case-insensitive. 107 | 108 | _Offsets_ and _values_ are output in hexadecimal, while _sizes_ are shown in decimal. When printed, hexadecimal values for _offsets_ will be zero-padded to 2 bytes. _Values_ will be zero-padded to their _size_. The padding does not have to be preserved in input, i.e. you can type `0x1` for a word-sized (two-byte) value, instead of writing `0x0001`. 109 | 110 | #### Output 111 | 112 | **UVT**'s output follows the same syntax as the input it accepts. This way, nearly everything it spits out can be fed back to it, for example to restore some previously-saved settings. 113 | 114 | The application prints out a header as the first thing it does after it launches, which provides some useful information. It might look like that: 115 | 116 | ```` 117 | # UEFI Variable Tool (uvt) Version 0.0.0 @ American Megatrends 5.24 UEFI 2.8 118 | ```` 119 | 120 | This prompt starting with `#` is also a valid comment, which means it will not interfere if you decided to feed the same file back to **UVT**. The three items following the `@` sign are: the firmware _vendor_, _firmware version_ (_major_._minor_) and the _UEFI revision_ (specification version compatibility). 121 | 122 | ### Input Stream 123 | 124 | **UVT**'s other mode of operation is to take an arbitrarily-long list of commands from the standard input (_stdin_). To use the application in this mode, make sure _not_ to provide _any_ command-line arguments, other than the redirection operator, which is however handled by the shell. 125 | 126 | #### Redirection 127 | 128 | **UVT** does not read or write files directly: it depends on the UEFI shell redirection. This comes with some quirks, which might also be implementation-dependent. 129 | 130 | To feed data to the application's _standard input_, use the following shell command: 131 | 132 | ````shell 133 | uvt < in.txt 134 | ```` 135 | 136 | The file `in.txt` should be properly formatted, as discussed in the next section. You can also save the application's output: 137 | 138 | ````shell 139 | uvt > out.txt 140 | ```` 141 | 142 | To combine both operations: 143 | 144 | ````shell 145 | uvt < in.txt > out.txt 146 | ```` 147 | 148 | The quirks mentioned are as follows: 149 | * The ` ` space between the filename and the redirection sign is mandatory: neither `uvt a` despite being specified in the [UEFI Shell Manual](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf) do not seem to work properly in the latest _EFI Shell_ 2.2 (as of August 2023). Do not use them. 151 | * Input redirection via the `|` pipe operator does not seem to work either with built-in commands such as `echo` or `type`. The standard-input stream received by **UVT** is empty. 152 | 153 | #### File Format 154 | 155 | As the UEFI shell operates internally with UCS-2 encoding, the accepted standard input file format is _Unicode UTF-16 Little-Endian (LE)_: standard _ASCII_ text files will not work. This is a minor inconvenience, although even the _Notepad_ application bundled with Windows can save text in this format, as long as it is specified explicitly. 156 | 157 | Any output files produced by a redirection will also be in the same format. 158 | 159 | The _Byte Order Mark_ (BOM), mandated by the UTF-16 specification, is optional as far as the application is concerned. In fact, any BOM instances will be filtered out at an early parsing stage. 160 | 161 | The input file is split into individual _entries_, which are rows separated by the _Line Feed_ character `LF` or `\n`. Each line can contain at most a single operation and is self-contained, i.e. no operation can span multiple lines. The _Carriage Return_ character `CR` or `\r` may be present and will be discarded if that's the case. 162 | 163 | The format to define _operations_ is just the same as for the command-line arguments. _Options_ can also be defined, however with a different syntax (read on). Beyond that, there are also _definitions_, which can be referenced by _operations_, and _comments_. 164 | 165 | ##### Comments 166 | 167 | Comments are marked with the pound sign `#`. Anything to the right of that sign is discarded. Comments do not have to be separate lines, they can appear on the same line as an _operation_, an _option_ or a _definition_: 168 | 169 | ```` 170 | # This is an example comment on a separate line 171 | !simulate # Simulate only, do not write 172 | Lang:0x00 # Retrieve the byte at offset 0 in "Lang" 173 | ```` 174 | 175 | When the input is parsed, after filtering out the comments, an entry is trimmed of any leading and trailing whitespace characters. Entries that end up blank are at this point entirely discarded. 176 | 177 | ##### Definitions & References 178 | 179 | A target for an _operation_, consisting of a _variable name_, _offset_ and, optionally, _size_, can be defined to be referenced elsewhere in the file. The syntax is: 180 | 181 | ```` 182 | ,:[()] 183 | ```` 184 | 185 | Where ``, `` and `` have the same interpretation as discussed in the command-line arguments section, and `` is an identifier that can be reused later to identify the target. For example: 186 | 187 | ```` 188 | Language,Lang:0x00(4) 189 | ```` 190 | 191 | Note that any amount of whitespace can appear on either side of the `,` comma separator, which allows for better legibility in script formatting. For example: 192 | 193 | ```` 194 | Language9 , Lang:0x09 195 | Language10, Lang:0x0a 196 | ```` 197 | 198 | The syntax for _operations_ in the input stream is extended to include the following: 199 | 200 | ```` 201 | @[=] 202 | ```` 203 | 204 | The following example illustrates accessing a value by reference for the purposes of reading and writing respectively: 205 | 206 | ```` 207 | @Language 208 | @Language=0x01020304 209 | ```` 210 | 211 | #### Options 212 | 213 | Some of the _options_ (excluding usage information) can be defined in the input stream as well but the syntax for that is different. Namely, it's the `!` bang (exclamation mark) followed by the option keyword: 214 | 215 | ```` 216 | !