├── .codespellignore ├── .github ├── linters │ └── .markdown-lint.yml └── workflows │ ├── mppl.yml │ └── super-linter.yml ├── .gitignore ├── .gitmodules ├── .markdownlint.json ├── README.md ├── lint.bat ├── pdf └── Unified Extensible Firmware Interface (UEFI) Specification V2.9.pdf ├── pic ├── 1-1.jpg ├── 2-1.jpg ├── 2-10.jpg ├── 2-11.jpg ├── 2-12.jpg ├── 2-13.jpg ├── 2-14.jpg ├── 2-2.jpg ├── 2-3.jpg ├── 2-4.jpg ├── 2-5.jpg ├── 2-6.jpg ├── 2-7.jpg ├── 2-8.jpg ├── 2-9.jpg ├── 5-1.jpg ├── 5-2.jpg ├── 5-3.jpg ├── 5-4.jpg ├── 6-1.jpg ├── 6-2.jpg ├── 6-3.jpg ├── 6-4.jpg ├── 6-5.jpg ├── 7-1.jpg ├── 7-2.jpg ├── 8-1.jpg ├── get-pdf.jpg ├── logo.png ├── table1-1.jpg ├── table1-2.jpg ├── table2-1.jpg ├── table2-5.jpg ├── table2-6.jpg ├── table2-7.jpg ├── table2-8.jpg ├── table2-9.jpg ├── table3-2.png ├── table4-1.jpg ├── table7-1.jpg ├── table7-10.jpg ├── table7-10_1.jpg ├── table7-10_2.jpg ├── table7-10_5.jpg ├── table7-10_6.jpg ├── table7-10_7.jpg ├── table7-2-1.jpg ├── table7-2-2.jpg ├── table7-3-1.jpg ├── table7-3-2.jpg ├── table7-3-3.jpg ├── table7-3-4.jpg ├── table7-3_0.jpg ├── table7-3_1.jpg ├── table7-3_2.jpg ├── table7-3_3.jpg ├── table7-3_4.jpg ├── table7-3_5.jpg ├── table7-3_6.jpg ├── table7-4.jpg ├── table7-5.jpg ├── table7-6-1.jpg ├── table7-6-2.jpg ├── table7-6_0.jpg ├── table7-6_1.jpg ├── table7-6_2.jpg ├── table7-6_3.jpg ├── table7-6_4.jpg ├── table7-7.jpg ├── table7-7_0.jpg ├── table7-7_1.jpg ├── table7-7_10.jpg ├── table7-7_11.jpg ├── table7-7_12.jpg ├── table7-7_13.jpg ├── table7-7_14.jpg ├── table7-7_15.jpg ├── table7-7_16.jpg ├── table7-7_2.jpg ├── table7-7_3.jpg ├── table7-7_4.jpg ├── table7-7_5.jpg ├── table7-7_6.jpg ├── table7-7_7.jpg ├── table7-7_8.jpg ├── table7-7_9.jpg ├── table7-8-1.jpg ├── table7-8-2.jpg ├── table7-9.jpg ├── table7-9_1.jpg ├── table7-9_2.jpg ├── table7-9_3.jpg ├── table7-9_4.jpg ├── table7-9_5.jpg ├── table8-1.jpg ├── table8-2.jpg ├── table8-3.jpg ├── table8-3_1.jpg ├── table8-3_2.jpg ├── table8-3_3.jpg ├── table8-3_4.jpg ├── table8-4.jpg ├── table8-5.jpg ├── table8-5_1.jpg ├── table8-5_2.jpg ├── table8-5_3.jpg ├── table8-5_4.jpg ├── table8-6.jpg ├── table8-6_1.jpg ├── table8-6_2.jpg ├── table8-7.jpg ├── table8-7_1.jpg ├── table8-8.jpg ├── table8-8_1.jpg ├── table8-8_2.jpg ├── table8-9.jpg └── table8-9_1.jpg └── src ├── .autocorrectrc ├── 0-Preface.md ├── 1-Introduction.md ├── 2-Overview.md ├── 3-Boot-Manager.md ├── 4-EFI-System-Table.md ├── 5-GUID-Partition-Table-Disk-Layout.md ├── 6-Block-Translation-Table-Layout.md ├── 7-Services-Boot-Services.md └── 8-Services-Runtime-Services.md /.codespellignore: -------------------------------------------------------------------------------- 1 | acn 2 | AFE 3 | edn 4 | fot 5 | hart 6 | HART 7 | ist 8 | lod 9 | sav 10 | SIE 11 | TRE -------------------------------------------------------------------------------- /.github/linters/.markdown-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ########################### 3 | ########################### 4 | ## Markdown Linter rules ## 5 | ########################### 6 | ########################### 7 | 8 | # Linter rules doc: 9 | # - https://github.com/DavidAnson/markdownlint 10 | # 11 | # Note: 12 | # To comment out a single error: 13 | # 14 | # any violations you want 15 | # 16 | # 17 | 18 | ############### 19 | # Rules by id # 20 | ############### 21 | MD004: false # Unordered list style 22 | MD007: false 23 | MD010: false 24 | MD013: false 25 | MD024: false 26 | MD025: false 27 | MD026: 28 | punctuation: ".,;:!。,;:" # List of not allowed 29 | MD029: false # Ordered list item prefix 30 | MD033: false # Allow inline HTML 31 | MD036: false # Emphasis used instead of a heading 32 | MD045: false 33 | MD047: false 34 | ################# 35 | # Rules by tags # 36 | ################# 37 | blank_lines: false # Error on blank lines 38 | -------------------------------------------------------------------------------- /.github/workflows/mppl.yml: -------------------------------------------------------------------------------- 1 | name: MPPL 2 | 3 | on: 4 | push: 5 | env: 6 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 7 | jobs: 8 | convert_via_pandoc: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Git Sumbodule Update 13 | run: | 14 | git submodule update --init --remote --recursive 15 | - name: add fonts 16 | run: | 17 | sudo apt-get install ttf-mscorefonts-installer 18 | sudo apt-get install fontconfig 19 | fc-list :lang=zh 20 | ls -lh /usr/share/fonts/ 21 | cp -rf ./MPPL/fonts/* /usr/share/fonts/ 22 | mkfontscale 23 | mkfontdir 24 | fc-cache 25 | fc-list 26 | - name: install pandoc 27 | run: | 28 | sudo apt-get update 29 | sudo apt-get install texlive-full 30 | sudo apt-get install pandoc 31 | sudo apt-get clean 32 | - name: build pdf 33 | run: | 34 | cd src 35 | pandoc -f markdown-auto_identifiers --listings --pdf-engine=xelatex --template=../MPPL/templates/mppl.tex --output=UEFI-Spec-ZH.pdf *.md 36 | # - uses: actions/upload-artifact@master 37 | # with: 38 | # name: UEFI 规范 - 中文 39 | # path: output/UEFI规范-中文.pdf 40 | # - name: echo 41 | # run: | 42 | # pwd 43 | # ls -a 44 | - name: Create Release and Upload Release Asset 45 | uses: softprops/action-gh-release@v1 46 | # if: startsWith(github.ref, 'refs/tags/') 47 | with: 48 | tag_name: v0.1 49 | # name: Release v0.1 50 | body: UEFI 手册第 1-8 章初稿 51 | draft: false 52 | prerelease: false 53 | files: | 54 | src/UEFI-Spec-ZH.pdf -------------------------------------------------------------------------------- /.github/workflows/super-linter.yml: -------------------------------------------------------------------------------- 1 | # This workflow executes several linters on changed files based on languages used in your code base whenever 2 | # you push a code or open a pull request. 3 | # 4 | # You can adjust the behavior by modifying this file. 5 | # For more information, see: 6 | # https://github.com/github/super-linter 7 | name: Lint Code Base 8 | 9 | on: 10 | push: 11 | branches: [ "main" ] 12 | pull_request: 13 | branches: [ "main" ] 14 | jobs: 15 | run-lint: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | with: 21 | # Full git history is needed to get a proper list of changed files within `super-linter` 22 | fetch-depth: 0 23 | 24 | - name: Lint Code Base 25 | uses: github/super-linter@v4 26 | env: 27 | VALIDATE_MARKDOWN: true 28 | DEFAULT_BRANCH: "main" 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | - name: Codespell with annotations 31 | uses: codespell-project/actions-codespell@v1.0 32 | with: 33 | check_filenames: true 34 | check_hidden: true 35 | skip: ./.git,./.github/workflows/*,./.gitignore,./.codespellignore 36 | ignore_words_file: ./.codespellignore 37 | only_warn: 1 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Core latex/pdflatex auxiliary files: 2 | *.aux 3 | *.lof 4 | *.log 5 | *.lot 6 | *.fls 7 | *.out 8 | *.toc 9 | *.fmt 10 | *.fot 11 | *.cb 12 | *.cb2 13 | .*.lb 14 | 15 | ## Intermediate documents: 16 | *.dvi 17 | *.xdv 18 | *-converted-to.* 19 | # these rules might exclude image files for figures etc. 20 | # *.ps 21 | # *.eps 22 | # *.pdf 23 | 24 | ## Generated if empty string is given at "Please type another file name for output:" 25 | .pdf 26 | 27 | ## Bibliography auxiliary files (bibtex/biblatex/biber): 28 | *.bbl 29 | *.bcf 30 | *.blg 31 | *-blx.aux 32 | *-blx.bib 33 | *.run.xml 34 | 35 | ## Build tool auxiliary files: 36 | *.fdb_latexmk 37 | *.synctex 38 | *.synctex(busy) 39 | *.synctex.gz 40 | *.synctex.gz(busy) 41 | *.pdfsync 42 | 43 | ## Build tool directories for auxiliary files 44 | # latexrun 45 | latex.out/ 46 | 47 | ## Auxiliary and intermediate files from other packages: 48 | # algorithms 49 | *.alg 50 | *.loa 51 | 52 | # achemso 53 | acs-*.bib 54 | 55 | # amsthm 56 | *.thm 57 | 58 | # beamer 59 | *.nav 60 | *.pre 61 | *.snm 62 | *.vrb 63 | 64 | # changes 65 | *.soc 66 | 67 | # comment 68 | *.cut 69 | 70 | # cprotect 71 | *.cpt 72 | 73 | # elsarticle (documentclass of Elsevier journals) 74 | *.spl 75 | 76 | # endnotes 77 | *.ent 78 | 79 | # fixme 80 | *.lox 81 | 82 | # feynmf/feynmp 83 | *.mf 84 | *.mp 85 | *.t[1-9] 86 | *.t[1-9][0-9] 87 | *.tfm 88 | 89 | #(r)(e)ledmac/(r)(e)ledpar 90 | *.end 91 | *.?end 92 | *.[1-9] 93 | *.[1-9][0-9] 94 | *.[1-9][0-9][0-9] 95 | *.[1-9]R 96 | *.[1-9][0-9]R 97 | *.[1-9][0-9][0-9]R 98 | *.eledsec[1-9] 99 | *.eledsec[1-9]R 100 | *.eledsec[1-9][0-9] 101 | *.eledsec[1-9][0-9]R 102 | *.eledsec[1-9][0-9][0-9] 103 | *.eledsec[1-9][0-9][0-9]R 104 | 105 | # glossaries 106 | *.acn 107 | *.acr 108 | *.glg 109 | *.glo 110 | *.gls 111 | *.glsdefs 112 | *.lzo 113 | *.lzs 114 | 115 | # uncomment this for glossaries-extra (will ignore makeindex's style files!) 116 | # *.ist 117 | 118 | # gnuplottex 119 | *-gnuplottex-* 120 | 121 | # gregoriotex 122 | *.gaux 123 | *.gtex 124 | 125 | # htlatex 126 | *.4ct 127 | *.4tc 128 | *.idv 129 | *.lg 130 | *.trc 131 | *.xref 132 | 133 | # hyperref 134 | *.brf 135 | 136 | # knitr 137 | *-concordance.tex 138 | # TODO Comment the next line if you want to keep your tikz graphics files 139 | *.tikz 140 | *-tikzDictionary 141 | 142 | # listings 143 | *.lol 144 | 145 | # luatexja-ruby 146 | *.ltjruby 147 | 148 | # makeidx 149 | *.idx 150 | *.ilg 151 | *.ind 152 | 153 | # minitoc 154 | *.maf 155 | *.mlf 156 | *.mlt 157 | *.mtc[0-9]* 158 | *.slf[0-9]* 159 | *.slt[0-9]* 160 | *.stc[0-9]* 161 | 162 | # minted 163 | _minted* 164 | *.pyg 165 | 166 | # morewrites 167 | *.mw 168 | 169 | # nomencl 170 | *.nlg 171 | *.nlo 172 | *.nls 173 | 174 | # pax 175 | *.pax 176 | 177 | # pdfpcnotes 178 | *.pdfpc 179 | 180 | # sagetex 181 | *.sagetex.sage 182 | *.sagetex.py 183 | *.sagetex.scmd 184 | 185 | # scrwfile 186 | *.wrt 187 | 188 | # sympy 189 | *.sout 190 | *.sympy 191 | sympy-plots-for-*.tex/ 192 | 193 | # pdfcomment 194 | *.upa 195 | *.upb 196 | 197 | # pythontex 198 | *.pytxcode 199 | pythontex-files-*/ 200 | 201 | # tcolorbox 202 | *.listing 203 | 204 | # thmtools 205 | *.loe 206 | 207 | # TikZ & PGF 208 | *.dpth 209 | *.md5 210 | *.auxlock 211 | 212 | # todonotes 213 | *.tdo 214 | 215 | # vhistory 216 | *.hst 217 | *.ver 218 | 219 | # easy-todo 220 | *.lod 221 | 222 | # xcolor 223 | *.xcp 224 | 225 | # xmpincl 226 | *.xmpi 227 | 228 | # xindy 229 | *.xdy 230 | 231 | # xypic precompiled matrices and outlines 232 | *.xyc 233 | *.xyd 234 | 235 | # endfloat 236 | *.ttt 237 | *.fff 238 | 239 | # Latexian 240 | TSWLatexianTemp* 241 | 242 | ## Editors: 243 | # WinEdt 244 | *.bak 245 | *.sav 246 | 247 | # Texpad 248 | .texpadtmp 249 | 250 | # LyX 251 | *.lyx~ 252 | 253 | # Kile 254 | *.backup 255 | 256 | # gummi 257 | .*.swp 258 | 259 | # KBibTeX 260 | *~[0-9]* 261 | 262 | # TeXnicCenter 263 | *.tps 264 | 265 | # auto folder when using emacs and auctex 266 | ./auto/* 267 | *.el 268 | 269 | # expex forward references with \gathertags 270 | *-tags.tex 271 | 272 | # standalone packages 273 | *.sta 274 | 275 | # Makeindex log files 276 | *.lpz 277 | build/** -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "MPPL"] 2 | path = MPPL 3 | url = https://github.com/Dunky-Z/MPPL.git 4 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "MD001": false, 3 | "MD007": false, 4 | "MD010": false, 5 | "MD013": false, 6 | "MD024": false, 7 | "MD025": false, 8 | "MD036": false, 9 | "MD045": false, 10 | "MD047": false 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UEFI 规范 - 中文![](https://geps.dev/progress/100) 2 | 3 | ## 简介 4 | 5 | 为快速学习 UEFI,免于翻译软件反复翻译的痛苦,将 [UEFI 规范(V2.9)]()翻译为中文。**内容均为机翻加微调**,欢迎大家提 PR 修正机翻的表述。 6 | 7 | 因 UEFI 规范主要是定义 Protocol,是用来查阅的一个手册,想要学习入门 UEFI 只需要学习前八章即可,所以计划**只翻译前八章内容**。 8 | 9 | ## 翻译约定 10 | 11 | - 规范中拿不准的术语翻译请添加(TODO)标识,以便后续完善; 12 | -------------------------------------------------------------------------------- /lint.bat: -------------------------------------------------------------------------------- 1 | cd ./src 2 | 3 | @REM Just for test 4 | dir 5 | 6 | @REM Use autocorrect to fix typo 7 | autocorrect.exe --fix 1-Introduction.md 2-Overview.md 3-Boot-Manager.md 8 | 9 | @REM Use markdownlint to check markdown style 10 | markdownlint -c ../.markdownlint.json -f 1-Introduction.md 2-Overview.md 3-Boot-Manager.md 11 | 12 | @REM Use pandoc to build pdf 13 | pandoc.exe -f markdown-auto_identifiers --pdf-engine=xelatex --template=mppl.latex -s --listings 1-Introduction.md 2-Overview.md 3-Boot-Manager.md -o ../build/UEFI-Spec-zh.pdf -------------------------------------------------------------------------------- /pdf/Unified Extensible Firmware Interface (UEFI) Specification V2.9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pdf/Unified Extensible Firmware Interface (UEFI) Specification V2.9.pdf -------------------------------------------------------------------------------- /pic/1-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/1-1.jpg -------------------------------------------------------------------------------- /pic/2-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-1.jpg -------------------------------------------------------------------------------- /pic/2-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-10.jpg -------------------------------------------------------------------------------- /pic/2-11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-11.jpg -------------------------------------------------------------------------------- /pic/2-12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-12.jpg -------------------------------------------------------------------------------- /pic/2-13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-13.jpg -------------------------------------------------------------------------------- /pic/2-14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-14.jpg -------------------------------------------------------------------------------- /pic/2-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-2.jpg -------------------------------------------------------------------------------- /pic/2-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-3.jpg -------------------------------------------------------------------------------- /pic/2-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-4.jpg -------------------------------------------------------------------------------- /pic/2-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-5.jpg -------------------------------------------------------------------------------- /pic/2-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-6.jpg -------------------------------------------------------------------------------- /pic/2-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-7.jpg -------------------------------------------------------------------------------- /pic/2-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-8.jpg -------------------------------------------------------------------------------- /pic/2-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/2-9.jpg -------------------------------------------------------------------------------- /pic/5-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/5-1.jpg -------------------------------------------------------------------------------- /pic/5-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/5-2.jpg -------------------------------------------------------------------------------- /pic/5-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/5-3.jpg -------------------------------------------------------------------------------- /pic/5-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/5-4.jpg -------------------------------------------------------------------------------- /pic/6-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/6-1.jpg -------------------------------------------------------------------------------- /pic/6-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/6-2.jpg -------------------------------------------------------------------------------- /pic/6-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/6-3.jpg -------------------------------------------------------------------------------- /pic/6-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/6-4.jpg -------------------------------------------------------------------------------- /pic/6-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/6-5.jpg -------------------------------------------------------------------------------- /pic/7-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/7-1.jpg -------------------------------------------------------------------------------- /pic/7-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/7-2.jpg -------------------------------------------------------------------------------- /pic/8-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/8-1.jpg -------------------------------------------------------------------------------- /pic/get-pdf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/get-pdf.jpg -------------------------------------------------------------------------------- /pic/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/logo.png -------------------------------------------------------------------------------- /pic/table1-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table1-1.jpg -------------------------------------------------------------------------------- /pic/table1-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table1-2.jpg -------------------------------------------------------------------------------- /pic/table2-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table2-1.jpg -------------------------------------------------------------------------------- /pic/table2-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table2-5.jpg -------------------------------------------------------------------------------- /pic/table2-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table2-6.jpg -------------------------------------------------------------------------------- /pic/table2-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table2-7.jpg -------------------------------------------------------------------------------- /pic/table2-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table2-8.jpg -------------------------------------------------------------------------------- /pic/table2-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table2-9.jpg -------------------------------------------------------------------------------- /pic/table3-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table3-2.png -------------------------------------------------------------------------------- /pic/table4-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table4-1.jpg -------------------------------------------------------------------------------- /pic/table7-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-1.jpg -------------------------------------------------------------------------------- /pic/table7-10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-10.jpg -------------------------------------------------------------------------------- /pic/table7-10_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-10_1.jpg -------------------------------------------------------------------------------- /pic/table7-10_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-10_2.jpg -------------------------------------------------------------------------------- /pic/table7-10_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-10_5.jpg -------------------------------------------------------------------------------- /pic/table7-10_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-10_6.jpg -------------------------------------------------------------------------------- /pic/table7-10_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-10_7.jpg -------------------------------------------------------------------------------- /pic/table7-2-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-2-1.jpg -------------------------------------------------------------------------------- /pic/table7-2-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-2-2.jpg -------------------------------------------------------------------------------- /pic/table7-3-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3-1.jpg -------------------------------------------------------------------------------- /pic/table7-3-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3-2.jpg -------------------------------------------------------------------------------- /pic/table7-3-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3-3.jpg -------------------------------------------------------------------------------- /pic/table7-3-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3-4.jpg -------------------------------------------------------------------------------- /pic/table7-3_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3_0.jpg -------------------------------------------------------------------------------- /pic/table7-3_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3_1.jpg -------------------------------------------------------------------------------- /pic/table7-3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3_2.jpg -------------------------------------------------------------------------------- /pic/table7-3_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3_3.jpg -------------------------------------------------------------------------------- /pic/table7-3_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3_4.jpg -------------------------------------------------------------------------------- /pic/table7-3_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3_5.jpg -------------------------------------------------------------------------------- /pic/table7-3_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-3_6.jpg -------------------------------------------------------------------------------- /pic/table7-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-4.jpg -------------------------------------------------------------------------------- /pic/table7-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-5.jpg -------------------------------------------------------------------------------- /pic/table7-6-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-6-1.jpg -------------------------------------------------------------------------------- /pic/table7-6-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-6-2.jpg -------------------------------------------------------------------------------- /pic/table7-6_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-6_0.jpg -------------------------------------------------------------------------------- /pic/table7-6_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-6_1.jpg -------------------------------------------------------------------------------- /pic/table7-6_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-6_2.jpg -------------------------------------------------------------------------------- /pic/table7-6_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-6_3.jpg -------------------------------------------------------------------------------- /pic/table7-6_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-6_4.jpg -------------------------------------------------------------------------------- /pic/table7-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7.jpg -------------------------------------------------------------------------------- /pic/table7-7_0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_0.jpg -------------------------------------------------------------------------------- /pic/table7-7_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_1.jpg -------------------------------------------------------------------------------- /pic/table7-7_10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_10.jpg -------------------------------------------------------------------------------- /pic/table7-7_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_11.jpg -------------------------------------------------------------------------------- /pic/table7-7_12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_12.jpg -------------------------------------------------------------------------------- /pic/table7-7_13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_13.jpg -------------------------------------------------------------------------------- /pic/table7-7_14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_14.jpg -------------------------------------------------------------------------------- /pic/table7-7_15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_15.jpg -------------------------------------------------------------------------------- /pic/table7-7_16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_16.jpg -------------------------------------------------------------------------------- /pic/table7-7_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_2.jpg -------------------------------------------------------------------------------- /pic/table7-7_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_3.jpg -------------------------------------------------------------------------------- /pic/table7-7_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_4.jpg -------------------------------------------------------------------------------- /pic/table7-7_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_5.jpg -------------------------------------------------------------------------------- /pic/table7-7_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_6.jpg -------------------------------------------------------------------------------- /pic/table7-7_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_7.jpg -------------------------------------------------------------------------------- /pic/table7-7_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_8.jpg -------------------------------------------------------------------------------- /pic/table7-7_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-7_9.jpg -------------------------------------------------------------------------------- /pic/table7-8-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-8-1.jpg -------------------------------------------------------------------------------- /pic/table7-8-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-8-2.jpg -------------------------------------------------------------------------------- /pic/table7-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-9.jpg -------------------------------------------------------------------------------- /pic/table7-9_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-9_1.jpg -------------------------------------------------------------------------------- /pic/table7-9_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-9_2.jpg -------------------------------------------------------------------------------- /pic/table7-9_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-9_3.jpg -------------------------------------------------------------------------------- /pic/table7-9_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-9_4.jpg -------------------------------------------------------------------------------- /pic/table7-9_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table7-9_5.jpg -------------------------------------------------------------------------------- /pic/table8-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-1.jpg -------------------------------------------------------------------------------- /pic/table8-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-2.jpg -------------------------------------------------------------------------------- /pic/table8-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-3.jpg -------------------------------------------------------------------------------- /pic/table8-3_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-3_1.jpg -------------------------------------------------------------------------------- /pic/table8-3_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-3_2.jpg -------------------------------------------------------------------------------- /pic/table8-3_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-3_3.jpg -------------------------------------------------------------------------------- /pic/table8-3_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-3_4.jpg -------------------------------------------------------------------------------- /pic/table8-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-4.jpg -------------------------------------------------------------------------------- /pic/table8-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-5.jpg -------------------------------------------------------------------------------- /pic/table8-5_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-5_1.jpg -------------------------------------------------------------------------------- /pic/table8-5_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-5_2.jpg -------------------------------------------------------------------------------- /pic/table8-5_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-5_3.jpg -------------------------------------------------------------------------------- /pic/table8-5_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-5_4.jpg -------------------------------------------------------------------------------- /pic/table8-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-6.jpg -------------------------------------------------------------------------------- /pic/table8-6_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-6_1.jpg -------------------------------------------------------------------------------- /pic/table8-6_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-6_2.jpg -------------------------------------------------------------------------------- /pic/table8-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-7.jpg -------------------------------------------------------------------------------- /pic/table8-7_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-7_1.jpg -------------------------------------------------------------------------------- /pic/table8-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-8.jpg -------------------------------------------------------------------------------- /pic/table8-8_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-8_1.jpg -------------------------------------------------------------------------------- /pic/table8-8_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-8_2.jpg -------------------------------------------------------------------------------- /pic/table8-9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-9.jpg -------------------------------------------------------------------------------- /pic/table8-9_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dunky-Z/uefi-spec-zh/458a30c7219d9f3952b539f3487d3b4876582324/pic/table8-9_1.jpg -------------------------------------------------------------------------------- /src/.autocorrectrc: -------------------------------------------------------------------------------- 1 | 2 | rules: 3 | # Auto add spacing between CJK (Chinese, Japanese, Korean) and English words. 4 | # 0 - off, 1 - error, 2 - warning 5 | space-word: 1 6 | # Add space between some punctuations. 7 | space-punctuation: 1 8 | # Add space between brackets (), [] when near the CJK. 9 | space-bracket: 1 10 | # Convert to fullwidth. 11 | fullwidth: 1 12 | # To remove space near the fullwidth punctuations. 13 | no-space-fullwidth: 1 14 | # To remove space arouned the fullwidth quotes “”, ''. 15 | no-space-fullwidth-quote: 1 16 | # Fullwidth alphanumeric characters to halfwidth. 17 | halfwidth-word: 1 18 | # Fullwidth punctuations to halfwidth in english. 19 | halfwidth-punctuation: 1 20 | # Spellcheck 21 | spellcheck: 2 22 | 23 | spellcheck: 24 | mode: 1 25 | words: 26 | - 遗留 = 传统 27 | - 选项 ROM = Option ROM 28 | - 手柄 = 句柄 29 | - 哈特 = HART 30 | - 呼叫者 = 调用者 31 | - 实施 = 实现 32 | - 映像 = 镜像 33 | - 名称空间 = 命名空间 34 | - 地图 = 映射 35 | - 易失 = volatile 36 | - 引导 = 启动 37 | - 列举 = 枚举 38 | - 媒体 = 媒介 39 | - 图像 = 镜像 40 | - ActiveMQ 41 | - AirPods 42 | - Aliyun 43 | - API 44 | - App 45 | - App Store 46 | - AppKit 47 | - AppStore = App Store 48 | - AWS 49 | - Bootchain 50 | - Boot rom = BootROM 51 | - BootROM 52 | - CacheStorage 53 | - CDN 54 | - CentOS 55 | - CloudFront 56 | - CORS 57 | - CPU 58 | - DNS 59 | - Elasticsearch 60 | - eMMC 61 | - ESLint 62 | - Facebook 63 | - GeForce 64 | - GitHub 65 | - Google 66 | - GPU 67 | - H5 68 | - Hadoop 69 | - HBase 70 | - HDFS 71 | - HKEX 72 | - HTML 73 | - HTTP 74 | - HTTPS 75 | - I10n 76 | - I18n 77 | - iMovie 78 | - IndexedDB 79 | - Intel 80 | - iOS 81 | - iPad 82 | - iPadOS 83 | - iPhone 84 | - iTunes 85 | - JavaScript 86 | - jQuery 87 | - JSON 88 | - JWT 89 | - LaTeX 90 | - Linux 91 | - load = 加载 92 | - LocalStorage 93 | - macOS 94 | - Markdown 95 | - Microsoft 96 | - MongoDB 97 | - Mozilla 98 | - MVC 99 | - MySQL 100 | - Nasdaq 101 | - Netflix 102 | - NodeJS = Node.js 103 | - NoSQL 104 | - NSIGN 105 | - NVDIA 106 | - NYSE 107 | - OAuth 108 | - Objective-C 109 | - OLAP 110 | - OpenSSL 111 | - OSS 112 | - P2P 113 | - PaaS 114 | - RabbitMQ 115 | - Redis 116 | - RESTful 117 | - riscv = RISC-V 118 | - RISC-V 119 | - RSS 120 | - RubyGem 121 | - RubyGems 122 | - SaaS 123 | - Sass 124 | - SDK 125 | - Shopify 126 | - SoC 127 | - SQL 128 | - SQLite 129 | - SQLServer 130 | - SSL 131 | - Tesla 132 | - TikTok 133 | - tvOS 134 | - TypeScript 135 | - UBoot 136 | - Ubuntu 137 | - UDP 138 | - UEFI 139 | - ui = UI 140 | - UML 141 | - URI 142 | - URL 143 | - Verdi 144 | - VIM 145 | - watchOS 146 | - WebAssembly 147 | - WebKit 148 | - Webpack 149 | - Wi-Fi 150 | - Windows 151 | - WWDC 152 | - Xcode 153 | - XML 154 | - YAML 155 | - YML 156 | - YouTube -------------------------------------------------------------------------------- /src/0-Preface.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Unified Extensible Firmware Interface (UEFI) 规范" 3 | subtitle: "UEFI 规范 - 中文" 4 | version: "V2.9" 5 | date: "2022-10" 6 | company: UEFI 7 | logo: true 8 | logo-url: ../pic/logo.png 9 | lot: false 10 | lof: true 11 | mainfont: Lato 12 | header-right: UEFI 13 | department: UEFI 14 | history: 15 | - version: V0.1 16 | author: 张栋栋 17 | date: 2022 年 10 年 09 日 18 | desc: 创建文档 19 | - version: V0.2 20 | author: GeorgyKwe 21 | date: 2022 年 12 年 29 日 22 | desc: 添加 Chapt 6,7,8 23 | --- 24 | -------------------------------------------------------------------------------- /src/1-Introduction.md: -------------------------------------------------------------------------------- 1 | # 引言 2 | 3 | 统一可扩展固件接口 (UEFI) 规范描述了操作系统和平台固件之间的接口。UEFI 之前是可扩展固件接口规范 1.10 (EFI)。因此,一些代码和某些协议名称保留了 EFI 名称。除非另有说明,本规范中的 EFI 名称可假定为 UEFI 的一部分。 4 | 5 | 该接口采用数据表的形式,其中包含与平台相关的信息,以及可供 OS 加载程序和 OS 使用的启动和运行时服务调用。它们共同提供了一个启动操作系统的标准环境。本规范是作为一个纯粹的接口规范设计的。因此,**该规范定义了平台固件必须实现的接口和结构集**。类似地,该规范定义了操作系统在启动时可能使用的一组接口和结构。无论是固件开发者选择如何实现所需的元素,还是操作系统开发者选择如何利用这些接口和结构,都由开发者自己决定。 6 | 7 | 该规范的目的是定义一种方法,使操作系统和平台固件仅通信支持操作系统启动过程所必需的信息。这是通过平台和固件提供给操作系统的软件可见接口的正式和完整的抽象规范来实现的。 8 | 9 | 本规范的目的是为操作系统和平台固件定义一种方式,以仅传递支持操作系统启动过程所需的信息。这是通过平台和固件呈现给操作系统的软件可见接口的抽象规范来实现的。 10 | 11 | 使用这一正式定义,旨在运行在与受支持的处理器规范兼容的平台上的收缩包装操作系统将能够在各种系统设计上启动,而无需进一步的平台或操作系统定制。该定义还允许平台创新引入新特性和功能,以增强平台的能力,而不需要按照操作系统的启动顺序编写新代码。 12 | 13 | 此外,抽象规范开辟了一条替代遗留设备和固件代码的路径。新的设备类型和相关代码可以通过相同定义的抽象接口提供同等的功能,同样不会影响 OS 启动支持代码。 14 | 15 | 该规范适用于从移动系统到服务器的所有硬件平台。该规范提供了一组核心服务以及一组协议接口。协议接口的选择可以随着时间的推移而发展,并针对不同的平台市场细分进行优化。与此同时,该规范允许 oem 提供最大限度的可扩展性和定制能力,以实现差异化。在这方面,UEFI 的目的是定义一个从传统的“PC-AT”风格的启动世界到一个没有遗留 API 的环境的进化路径。 16 | 17 | ## UEFI 驱动模型扩展 18 | 19 | 对启动设备的访问是通过一系列的协议接口提供的。UEFI 驱动模型的一个目的是为 "PC-AT"式的 Option ROM(TODO)提供一个替代品。需要指出的是,写在 UEFI 驱动模型上的驱动,被设计为在预启动环境中访问启动设备。它们并不是为了取代高性能的、针对操作系统的驱动程序。 20 | 21 | UEFI 驱动模型被设计为支持执行模块化的代码,也被称为驱动,在预启动环境中运行。这些驱动程序可以管理或控制平台上的硬件总线和设备,也可以提供一些软件衍生的、平台特定的服务。 22 | 23 | UEFI 驱动模型还包含了 UEFI 驱动编写者所需的信息,以设计和实现平台启动 UEFI 兼容的操作系统可能需要的任何总线驱动和设备驱动的组合。 24 | 25 | UEFI 驱动模型被设计为通用的,可以适应任何类型的总线或设备。UEFI 规范描述了如何编写 PCI 总线驱动程序、PCI 设备驱动程序、USB 总线驱动程序、USB 设备驱动程序和 SCSI 驱动程序。提供了允许将 UEFI 驱动程序存储在 PCI Option ROM 中的其他详细信息,同时保持了与旧 Option ROM 镜像的兼容性。 26 | 27 | UEFI 规范的一个设计目标是使驱动镜像尽可能的小。然而,如果一个驱动程序需要支持多个处理器架构,那么也需要为每个支持的处理器架构提供一个驱动程序对象文件。为了解决这个空间问题,本规范还定义了 EFI 字节代码虚拟机(EFI Byte Code Virtual Machine)。一个 UEFI 驱动可以被编译成一个 EFI 字节代码对象文件。UEFI Specification-complaint(TODO)的固件必须包含一个 EFI 字节代码解释器。这使得支持多种处理器架构的单一 EFI 字节代码对象文件可以被运出。另一种节省空间的技术是使用压缩。该规范定义了压缩和解压算法,可用于减少 UEFI 驱动程序的大小,从而减少 UEFI 驱动程序存储在 ROM 设备中时的开销。 28 | 29 | OSV、IHV、OEM 和固件供应商可以使用 UEFI 规范中包含的信息来设计和实现符合本规范的固件、生成标准协议接口的驱动程序以及可用于启动 UEFI 兼容的操作系统加载程序操作系统。 30 | 31 | ## 章节安排 32 | 33 | 本规范的章节组织如下: 34 | 35 | | 章节名 | 内容 | 36 | | :---------------- | :----------------------------------- | 37 | | 引言/概述 | 介绍 UEFI 规范,并描述 UEFI 的主要组件。 | 38 | | 启动管理器 | 管理器用于加载写入此规范的驱动程序和应用程序。 | 39 | | EFI 系统表和分区 | 描述了一个 EFI 系统表,它被传递给每个兼容的驱动程序和应用程序,并定义了一个基于 GUID 的分区方案。 | 40 | | 块转换表 | 用于执行块 I/O 的布局和规则集,可提供单个块的断电写入原子性。 | 41 | | 启动服务 | 包含在启动操作系统之前存在于 UEFI 兼容系统中的基本服务的定义。 | 42 | | 运行时服务 | 包含在操作系统启动之前和之后存在于兼容系统中的基本服务的定义。 | 43 | | 协议 | EFI 加载图像协议描述已加载到内存的 UEFI 镜像。 | 44 | | | 设备路径协议提供了在 UEFI 环境中构建和管理设备路径所需的信息。 | 45 | | | UEFI 驱动模型描述了一组服务和协议,适用于每个总线和设备类型。 | 46 | | | 控制台支持协议定义了I/O协议,处理系统用户在启动服务环境中执行的基于文本的信息的输入和输出。 | 47 | | | 媒介访问协议定义了加载文件协议,文件系统格式和媒介格式处理可移动媒介。 | 48 | | | PCI 总线支持协议定义 PCI 总线驱动程序,PCI 设备驱动程序和 PCI Option ROM 布局。所描述的协议包括 PCI 根桥 I/O 协议和 PCI I/O协议。 | 49 | | | SCSI 驱动程序模型和总线支持定义了 SCSI I/O协议和扩展SCSI Pass Thru 协议,用于抽象访问由 SCSI 主机控制器产生的 SCSI 通道。 | 50 | | | iSCSI协议定义了通过TCP/IP传输SCSI数据。 | 51 | | | USB 支持协议定义了 USB 总线驱动程序和 USB 设备驱动程序。 | 52 | | | 调试器支持协议描述了一组可选的协议,提供所需的服务,以实现一个源级调试器的 UEFI 环境。 | 53 | | | 压缩算法规范详细描述了压缩/解压缩算法,外加一个标准的EFI解压缩接口,用于启动时使用。 | 54 | | | ACPI 协议可用于从平台上安装或删除 ACPI 表。 | 55 | | | 字符串服务:Unicode 排序协议允许在启动服务环境中运行的代码对给定语言的 Unicode 字符串执行词法比较函数;正则表达式协议用于根据正则表达式模式匹配 Unicode 字符串。 | 56 | | EFI 字节码虚拟机 | 定义 EFI 字节码虚拟处理器及其指令集。它还定义了如何将 EBC 对象文件加载到内存中,以及从本机代码到 EBC 代码再转换到本机代码的机制。 | 57 | | 固件更新和报告 | 为设备提供一个抽象,以提供固件管理支持。 | 58 | | 网络协议 | SNP、PXE、BIS 和 HTTP 启动协议定义了在 UEFI 启动服务环境中执行时提供对网络设备访问的协议。 | 59 | | | 受管网络协议定义了 EFI 受管网络协议,它提供原始 (未格式化) 异步网络数据包 I/O 服务和托管网络服务绑定协议,用于定位 MNP 驱动支持的通信设备。 | 60 | | | VLAN、EAP、Wi-Fi 和 Supplicant 协议定义了一个协议,为 VLAN 配置提供可管理性接口。 | 61 | | | 蓝牙协议定义。 | 62 | | | TCP、IP、PIPsec、FTP、GTLS 和 Configurations 协议定义了 EFI TCPv4 (Transmission Control Protocol version 4) 协议和 EFI IPv4 (Internet Protocol version 4) 协议。 | 63 | | | ARP、DHCP、DNS、HTTP 和 REST 协议定义了 ARP (Address Resolution Protocol) 协议接口和 EFI DHCPv4 协议。 | 64 | | | UDP 和 MTFTP 协议定义了 EFI UDPv4 (User Datagram Protocol version 4) 协议,该协议在 EFI IPv4 协议上接口,并定义了 EFI MTFTPv4 协议接口,该接口建立在 EFI UDPv4 协议之上。 | 65 | | 安全启动和驱动程序签名 | 介绍 Secure Boot 和生成 UEFI 数字签名的方法。 | 66 | | 人机界面基础设施 (HII) | 定义实现人机接口基础设施 (HII) 所需的核心代码和服务,包括管理用户输入和相关协议的代码定义的基本机制。 | 67 | | | 描述用于管理系统配置的数据和 api:描述旋钮和设置的实际数据。 | 68 | | 用户标识 | 描述描述平台当前用户的服务。 | 69 | | 安全技术 | 描述用于利用安全技术的协议,包括加密散列和密钥管理。 | 70 | | 杂项协议 | Timestamp 协议提供了一个独立于平台的接口来检索高分辨率的时间戳计数器。当调用 ResetSystem 时,重置通知协议提供注册通知的服务。 | 71 | | 附录 | GUID 和时间格式。 | 72 | | | 基于基本文本的控制台要求,符合 efi 系统需要提供通信能力。 | 73 | | | 设备路径使用数据结构的例子,定义各种硬件设备的启动服务。 | 74 | | | 状态代码列出了 UEFI 接口返回的成功、错误和警告代码。 | 75 | | | 通用网络驱动程序接口定义了32/64位硬件和软件通用网络驱动程序接口(UNDIs)。 | 76 | | | 使用简单指针协议。 | 77 | | | 使用 EFI 扩展 SCISI 直通协议。 | 78 | | | 压缩源代码的一个压缩算法的实现。 | 79 | | | 一个 EFI 解压缩算法的实现的解压源代码。 | 80 | | | EFI 字节码虚拟机操作码列表提供了相应指令集的摘要。 | 81 | | | 字母功能列表按字母顺序标识所有 UEFI 接口功能。 | 82 | | | EFI 1.10 协议变更和折旧清单标识了协议、GUID、修订标识符名称变更以及与 EFI 1.10 规范相比已弃用的协议。 | 83 | | | 平台错误记录描述了用于表示平台硬件错误的常见平台错误记录格式。 | 84 | | | UEFI ACPI Data Table 定义了 UEFI ACPI 表格式。 | 85 | | | 硬件错误记录持久性使用。 | 86 | | | 引用 | 87 | | | 术语表 | 88 | | 索引 | 提供规范中关键术语和概念的索引。 | 89 | 90 | ## 目标 91 | 92 | “PC-AT”启动环境对行业内的创新提出了重大挑战。每一个新的平台功能或硬件创新都要求固件开发人员设计越来越复杂的解决方案,并且通常要求操作系统开发人员修改启动代码,然后客户才能从创新中受益。这可能是一个耗时的过程,需要大量的资源投资。 93 | 94 | UEFI 规范的主要目标是定义一个替代启动环境,可以减轻这些考虑。在这个目标中,该规范类似于其他现有的启动规范。本规范的主要属性可以概括为以下属性: 95 | 96 | - **一致的、可扩展的平台环境**。该规范为固件定义了一个完整的解决方案,以描述所有平台特性和 OS 的 surface platform(TODO) 功能在启动过程中。这些定义非常丰富,足以涵盖一系列当代处理器设计。 97 | 98 | - **从固件中抽象操作系统**。该规范定义了平台功能的接口。通过使用抽象接口,该规范允许在构建 OS 加载器时,而无需了解作为这些接口基础的平台和固件。这些接口代表了底层平台和固件实现与操作系统加载程序之间定义良好的稳定边界。这样的边界允许底层固件和操作系统加载程序更改,前提是两者都将交互限制在定义的接口上。 99 | 100 | - **合理的设备抽象,不需要遗留接口**。“PC-AT”BIOS 接口要求操作系统加载程序对某些硬件设备的工作有特定的了解。该规范为 OS 加载器开发人员提供了一些不同的东西:抽象接口使得可以构建在一系列底层硬件设备上工作的代码,而无需明确了解该范围内每个设备的细节。 101 | 102 | - **从固件中提取 Option ROM**。该规范定义了平台功能的接口,包括 PCI、USB 和 SCSI 等标准总线类型。支持的总线类型可能会随着时间的推移而增加,因此包括了一种扩展到未来总线类型的机制。这些定义的接口以及扩展到未来总线类型的能力是 UEFI 驱动程序模型的组件。UEFI 驱动模型的一个目的是解决现有“PC-AT”Option ROM 中存在的广泛问题。与 OS 加载程序一样,驱动程序使用抽象接口,因此可以构建设备驱动程序和总线驱动程序,而无需了解作为这些接口基础的平台和固件。 103 | 104 | - **架构上可共享的系统分区**。扩展平台功能和添加新设备的计划通常需要软件支持。在许多情况下,当这些平台创新(TODO)在操作系统控制平台之前被激活时,它们必须由特定于平台而不是客户选择的操作系统的代码支持。解决这个问题的传统方法是在制造过程中将代码嵌入平台中(例如,在闪存设备中)。对这种持久存储的需求正在快速增长。该规范定义了大型海量存储媒介类型上的持久存储,以供平台支持代码扩展使用,以补充传统方法。规范中明确了其工作原理的定义,以确保固件开发商、OEM、操作系统供应商甚至第三方可以安全地共享空间,同时增加平台功能。 105 | 106 | 可以通过多种方式定义提供这些属性的启动环境。实际上,在编写本规范时,已经存在几种替代方案,从学术角度来看可能是可行的。然而,考虑到当前围绕支持的处理器平台的基础设施能力,这些替代方案通常会带来很高的门槛。本规范旨在提供上面列出的属性,同时也认识到行业的独特需求,该行业在兼容性方面进行了大量投资,并且拥有大量无法立即放弃的系统安装基础。这些需求推动了对本规范中体现的附加属性的要求: 107 | 108 | - **进化性的,而不是革命性的**。规范中的接口和结构旨在尽可能地减少初始实现的负担。虽然已经小注意保在接口本身中维护适当的抽象,但该设计还确保可以重用 BIOS 代码来实现接口,而只需要最少的额外编码工作。换句话说,在 PC-AT 平台上,规范最初可以作为基于现有代码的底层实现之上的薄接口(thin Interface TODO)层来实现。同时,抽象接口的引入提供了将来从遗留代码的迁移。一旦抽象被确立为固件和操作系统加载程序在启动期间交互的手段,开发人员就可以随意替换抽象接口下的遗留代码。类似的硬件遗留迁移也是可能的。由于抽象隐藏了设备的细节,因此可以移除底层硬件,并用提供改进功能、降低成本或两者兼而有之的新硬件替换它。显然,这需要编写新的平台固件来支持设备并通过抽象接口将其呈现给 OS 加载器。但是,如果没有接口抽象,则可能根本无法移除旧设备。 109 | - **设计上的兼容性**。系统分区结构的设计还保留了当前在“PC-AT”启动环境中使用的所有结构。因此,构建一个能够从同一磁盘启动传统操作系统或 EFI-aware 操作系统的单一系统是一件简单的事情。 110 | - **简化了操作系统中立的平台增值的添加**。该规范定义了一个开放的、可扩展的接口,它有助于创建平台“驱动程序”。这些可能类似于操作系统驱动程序,在启动过程中为新设备类型提供支持,或者它们可能用于实现增强的平台功能,例如容错或安全性。此外,这种扩展平台能力的能力从一开始就被设计到规范中。这旨在帮助开发人员避免在尝试将新代码挤入传统 BIOS 环境时所固有的许多挫败感。由于包含用于添加新协议的接口,OEM 或固件开发人员拥有以模块化方式向平台添加功能的基础设施。由于规范中定义的调用约定和环境,此类驱动程序可能会使用高级编码语言来实现。这反过来可能有助于降低创新的难度和成本。系统分区选项为此类扩展提供了非易失性存储器存储的替代方案。 111 | - **建立在现有投入的基础上**。在可能的情况下,规范避免在现有行业规范提供足够覆盖的领域重新定义接口和结构。例如,ACPI 规范为操作系统提供了发现和配置平台资源所需的所有信息。同样,规范设计的这种哲学选择旨在尽可能降低采用该规范的障碍。 112 | 113 | ## 目标受众 114 | 115 | 本文档主要适用于以下读者: 116 | 117 | - 将实现 UEFI 驱动程序的 IHV 和 OEM。 118 | - 将创建支持的处理器平台的 OEM 厂商,旨在启动 shrink-wrap(TODO)的操作系统。 119 | - BIOS 开发人员,无论是创建通用 BIOS 和其他固件产品的人员,还是修改这些产品的支持人员。 120 | - 操作系统开发人员将调整他们的 shrink-wrap(TODO)操作系统产品,用来在支持的基于处理器的平台上运行。 121 | 122 | ## UEFI 设计概述 123 | 124 | UEFI 的设计基于以下基本要素: 125 | 126 | - **重用现有的基于表格的接口**。为了保持对现有基础支持代码(包括操作系统和固件)的投资,必须在希望符合 UEFI 规范的平台上,实现通常在与支持的处理器规范兼容的平台上,实现的许多现有规范。 (有关更多信息,请参阅附录 Q:参考资料。) 127 | - **系统分区**。系统分区定义了一个分区和文件系统,可允许多个供应商之间安全共享,并用于不同目的。包含单独的、可共享的系统分区的能力提供了增加平台附加值的机会,而不会显著增加对非易失性平台存储器的需求。 128 | - **启动服务**。启动服务为可在启动期间使用的设备和系统功能提供接口。设备访问是通过“句柄”(handles)和“协议”(protocols) 抽象出来的。这有利于重用现有 BIOS 代码,将基本实现要求保持在规范之外,而不会给访问设备的消费者带来负担。 129 | - **运行时服务**。提供了一组最小的运行时服务,以确保对基础平台的硬件资源进行适当的抽象,这些资源可能是操作系统在正常运行时需要的。 130 | 131 | --- 132 | 133 | ![UEFI 概念概述](../pic/1-1.jpg "") 134 | 135 | --- 136 | 137 | 图 1-1 描述了用于完成平台和操作系统启动的符合 UEFI 规范的系统的各种组件的交互。 138 | 139 | 平台固件能够从系统分区中检索操作系统加载器镜像。该规范提供了各种大容量存储设备类型,包括磁盘、CD-ROM 和 DVD,以及通过网络的远程启动。通过可扩展的协议接口,有可能增加其他的启动媒介类型,尽管如果这些媒介需要使用本文件中定义的协议以外的协议,可能需要修改操作系统加载器。 140 | 141 | 一旦启动,操作系统加载程序将继续启动整个操作系统。为此,它可以使用本规范或其他所需规范定义的 EFI 启动服务和接口来探测、解析和初始化各种平台组件和管理它们的操作系统软件。在启动阶段,EFI 运行时服务也可供 OS 加载器使用。 142 | 143 | ## UEFI 驱动模型 144 | 145 | 本节描述了符合本规范的固件的驱动模型的目标。目标是让这个驱动模型为所有类型的总线和设备提供一个实现总线驱动和设备驱动的机制。在撰写本文时,支持的总线类型包括 PCI、USB 等。 146 | 147 | 随着硬件架构的不断发展,平台中存在的总线数量和类型也在不断增加。这种趋势在高端服务器中尤为明显。然而,更多样化的总线类型被设计到桌面和移动系统,甚至一些嵌入式系统中。这种日益增长的复杂性,意味着在预启动环境中,需要一种简单的方法来描述和管理平台中的所有总线和设备。UEFI 驱动模型以协议、服务和启动服务的形式提供了这种简单的方法。 148 | 149 | ### UEFI 驱动程序模型目标 150 | 151 | UEFI 驱动模型有以下目标: 152 | 153 | - **兼容** – 符合此规范的驱动程序必须保持与 EFI 1.10 规范和 UEFI 规范的兼容性。这意味着 UEFI 驱动程序模型利用 UEFI 2. 0 规范中的可扩展性机制来添加所需的功能。 154 | - **简单** - 符合本规范的驱动程序必须易于实现,易于维护。UEFI 驱动模型必须允许驱动编写者专注于正在开发的特定设备的驱动。驱动程序不应关注平台策略或平台管理问题。这些考虑应该留给系统固件。 155 | - **可扩展性** - UEFI 驱动模型必须能够适应所有类型的平台。这些平台包括嵌入式系统、移动和桌面系统,以及工作站和服务器。 156 | - **灵活** - UEFI 驱动模型必须支持枚举所有设备的能力,或者只枚举启动所需操作系统的那些设备。最小的设备枚举提供了对更快速的启动能力的支持,而完整的设备媒体提供了在系统中存在的任何启动设备上执行操作系统安装、系统维护或系统诊断的能力。 157 | - **可扩展性** - UEFI 驱动模型必须能够扩展到未来定义的总线类型。 158 | - **可移植性** - 根据 UEFI 驱动模型编写的驱动,必须在不同平台和支持的处理器架构之间可移植。 159 | - **可互操作性** - 驱动程序必须与其他驱动程序和系统固件共存,并且必须在不产生资源冲突的情况下进行操作。 160 | - **描述复杂的总线层次结构** - UEFI 驱动模型必须能够描述各种总线拓扑结构,从非常简单的单总线平台到包含许多不同类型总线的非常复杂的平台。 161 | - **驱动占用空间小** - 由 UEFI 驱动程序模型产生的可执行文件的大小必须最小化,以减少整体平台成本。虽然灵活性和可扩展性是目标,但支持这些所需的额外开销必须保持在最低水平,以防止固件组件的大小变得无法管理。 162 | - **解决遗留 Option ROM 的问题** - UEFI 驱动模型必须直接解决遗留 Option ROM 的约束和限制。具体来说,必须能够建立同时支持 UEFI 驱动和传统 Option ROM 的插件卡,这种卡可以在传统 BIOS 系统和符合 UEFI 的平台上执行,而无需修改卡上的代码。该解决方案必须提供一个从传统 Option ROM 驱动程序迁移到 UEFI 驱动程序的进化路径。 163 | 164 | ### 传统 Option ROM 问题 165 | 166 | 这个支持驱动模型的想法来自于对 UEFI 规范的反馈,它提供了一个明确的、由市场驱动的对传统选项 ROM(有时也被称为扩展 ROM)的替代要求。人们认为,UEFI 规范的出现代表了一个机会,通过用一种在 UEFI 规范框架内工作的替代机制来取代传统选项 ROM 镜像的构建和操作,从而摆脱隐含的限制。 167 | 168 | ## 迁移要求 169 | 170 | 迁移要求涵盖了从最初实施本规范到未来所有平台和操作系统都实施本规范的过渡时期。在这一时期,有两个主要的兼容性考虑是很重要的。 171 | 172 | - 能够继续启动传统的操作系统; 173 | - 能够在现有的平台上实现 UEFI,尽可能多地复用现有的固件代码,将开发资源和时间要求降到最低。 174 | 175 | ### 旧版操作系统支持 176 | 177 | UEFI 规范代表了收缩式操作系统和固件在启动过程中进行通信的首选方式。然而,选择制作一个符合该规范的平台,并不排除该平台,也支持不了解 UEFI 规范的,现有传统操作系统二进制文件。 178 | 179 | UEFI 规范并不限制平台设计者,选择同时支持 UEFI 规范和更传统的 "PC-AT "启动基础架构。如果要实现这样的传统基础架构,应该按照现有的行业惯例来开发,这些惯例是在本规范范围之外定义的。在任何给定的平台上,支持的传统操作系统的选项是由该平台的制造商决定的。 180 | 181 | ### 在旧平台上支持 UEFI 规范 182 | 183 | UEFI 规范经过精心设计,允许以最少的开发工作扩展现有系统以支持它。特别是 UEFI 规范中定义的抽象结构和服务,都可以在遗留平台上得到支持 184 | 185 | 例如,要在现有且受支持的基于 32 位的平台上实现此类支持,该平台使用传统 BIOS 来支持操作系统启动,需要提供额外的固件代码层。需要这些额外的代码来将服务和设备的现有接口转换为对本规范中定义的抽象的支持。 186 | 187 | ## 本文档中使用的约定 188 | 189 | ### 数据结构描述 190 | 191 | **支持的处理器是“小端”机器**。这种区别意味着内存中多字节数据项的低位字节位于最低地址,而高位字节位于最高地址。一些受支持的 64 位处理器可以配置为“小端”和“大端”操作。所有旨在符合本规范的实现都使用“小端”操作。 192 | 193 | 在某些内存布局描述中,某些字段被标记为保留。软件必须将这些字段初始化为零并在读取时忽略它们。在更新操作中,软件必须保留任何保留字段。 194 | 195 | ### 协议描述 196 | 197 | 协议描述一般有以下格式: 198 | 199 | - **协议名称**:协议接口的正式名称。 200 | - **摘要**:协议接口的简要描述。 201 | - **GUID**:协议接口的 128 位 GUID (Globally Unique Identifier)。 202 | - **协议接口结构**:一种“c 风格”的数据结构定义,包含由该协议接口产生的过程和数据字段。 203 | - **参数**:协议接口结构中各字段的简要说明。 204 | - **描述**:对接口提供的功能的描述,包括调用者应该知道的任何限制和警告。 205 | - **相关定义**:协议接口结构或其任何过程中使用的类型声明和常量。 206 | 207 | ### 过程描述 208 | 209 | 过程描述通常具有以下格式: 210 | 211 | - **过程名称**:过程的正式名称。 212 | - **摘要**:过程的简要说明。 213 | - **原型**:定义调用序列的“C 风格”过程标头。 214 | - **参数**:对程序原型中每个字段的简要描述。 215 | - **描述**:对接口所提供的功能的描述,包括调用者应该注意的任何限制和注意事项。 216 | - **相关定义**:仅由该过程使用的类型声明和常量。 217 | - **返回的状态代码**:对接口所返回的任何代码的描述。该过程需要实现本表中列出的任何状态代码。可以返回更多的错误代码,但是它们不会被标准的符合性测试所测试,而且任何使用该程序的软件,都不能依赖于实现可能提供的任何扩展错误代码。 218 | 219 | ### 指令描述 220 | 221 | EBC 指令的指令描述一般有以下格式: 222 | 223 | - **指令名称**:指令的正式名称。 224 | - **语法**:指令的简要描述。 225 | - **描述**:对指令所提供的功能的描述,并附有指令编码的详细表格。 226 | - **操作**:详细说明对操作数进行的操作。 227 | - **行为和限制**:逐项描述指令中涉及的每个操作数的行为,以及适用于操作数或指令的任何限制。 228 | 229 | ### 伪代码约定 230 | 231 | 提出伪代码是为了以更简洁的形式描述算法。本文件中的所有算法都不打算直接进行编译。代码是在与周围文本相对应的水平上呈现的。 232 | 233 | 在描述变量时,列表是一个无序的同质对象的集合。一个队列是一个同质对象的有序列表。除非另有说明,否则假设排序为先进先出。 234 | 235 | 伪代码以类似于 C 的格式呈现,在适当的地方使用 C 约定。编码风格,特别是缩进风格,是为了可读性,不一定符合 UEFI 规范的实现。 236 | 237 | ### 排版约定 238 | 239 | 本文件采用了以下描述的排版和说明性惯例。 240 | 241 | - 纯文本:规范中的绝大部分描述性文本都使用普通文本字体。 242 | - 纯文本(蓝色):任何有下划线和蓝色的纯文本都表示与交叉参考资料的活动链接。点击该词,就可以跟踪超链接。 243 | - 加粗:在文本中,粗体字标识了一个处理器寄存器的名称。在其他情况下,黑体字可以作为段落中的标题。 244 | - 斜体:在文本中,斜体字可以用作强调,以引入一个新的术语或表示手册或规范的名称。 245 | - 加粗等宽(暗红色):计算机代码、示例代码段和所有原型代码段使用 BOLD Monospace 字体,颜色为暗红色。这些代码列表通常出现在一个或多个独立的段落中,尽管单词或片段也可以嵌入到一个正常的文本段落中。 246 | - 加粗等宽(蓝色):用粗体单色字体的字,下划线和蓝色的字,表示该功能或类型定义的代码定义的活动超链接。点击该词,即可进入超链接。 247 | 248 | **注意**:出于管理和文件大小的考虑,每一页上只有第一次出现的参考文献是一个主动链接。同一页上的后续参考文献不会被主动链接到定义上,而是使用标准的、无下划线的 BOLD Monospace 字体。在页面上找到该名称的第一个实例(使用下划线的 BOLD Monospace 字体),点击该词即可跳转到该功能或类型的定义。 249 | 250 | - 斜体等宽:在代码或文本中,斜体字表示必须提供的变量信息的占位符名称(即参数)。 251 | 252 | ### 数字格式 253 | 254 | 在本标准中,二进制数字是由仅由西方阿拉伯数字 0 和 1 组成的任何数字序列表示的,后面紧跟一个小写的 b(例如,0101b)。在二进制数字表示中的字符之间可以包含下划线或空格,以增加可读性或划分领域边界(例如,0 0101 1010b 或 0_0101_1010b)。 255 | 256 | #### 十六进制 257 | 258 | 十六进制数字在本标准中用 0x 表示,前面是仅由西阿拉伯数字 0 至 9 和/或大写英文字母 A 至 F 组成的任何数字序列(例如,0xFA23)。十六进制数字表示中的字符之间可以包含下划线或空格,以增加可读性或划定字段边界(例如,0xB FD8C FA23 或 0xB_FD8C_FA23)。 259 | 260 | #### 十进制 261 | 262 | 在本标准中,小数是由仅由阿拉伯数字 0 到 9 组成的任何数字序列来表示的,后面不紧跟小写的 b 或小写的 h(例如,25)。本标准使用以下惯例来表示小数: 263 | 264 | - 小数点分隔符(即分隔数字的整数部分和小数部分)是一个句号; 265 | - 千位数分隔符(即分隔数字部分的三位数组)是一个逗号; 266 | - 千位数分隔符用于数字的整数部分,不用于数字的小数部分。 267 | 268 | ### 二进制前缀 269 | 270 | 本标准使用国际单位制(SI)中定义的前缀来表示 10 的幂值。见 "SI 二进制前缀 "标题下的 "UEFI 相关文件链接"()。 271 | 272 | ![](../pic/table1-1.jpg "") 273 | 274 | 本标准使用ISO/IEC 80000-13《数量和单位--第 13 部分:信息科学和技术》和 IEEE 1514《二进制倍数前缀标准》中定义的二进制前缀,用于表示 2 的幂值。 275 | 276 | ![](../pic/table1-2.jpg "") 277 | 278 | 例如,4 KB 意味着 4000 个字节,4 KiB 意味着 4096 个字节。 279 | 280 | ### 修订号 281 | 282 | 对 UEFI 规范的更新被认为是新的修订或勘误表,如下所述: 283 | 284 | - 当有实质性的新内容或可能修改现有行为的变化时,就会产生一个新的修订。新的修订版由一个主要的。次要的版本号来指定(例如:xx.yy)。在变化特别小的情况下,我们可能有一个 major.minor.minor 的命名惯例(例如 xx.yy.zz)。 285 | - 当批准的规范更新不包括任何重要的新材料或修改现有行为时,就会产生勘误的版本。勘误的指定方法是在版本号后面加上一个大写字母,如 xx.yy 勘误 A。 286 | -------------------------------------------------------------------------------- /src/3-Boot-Manager.md: -------------------------------------------------------------------------------- 1 | # 启动管理器 2 | 3 | UEFI 启动管理器是一个固件策略引擎,可以通过修改架构上定义的全局 NVRAM 变量来进行配置。启动管理器将尝试按照全局 NVRAM 变量定义的顺序加载 UEFI 驱动程序和 UEFI 应用程序(包括 UEFI 操作系统启动加载器)。平台固件必须使用全局 NVRAM 变量中指定的启动顺序进行正常启动。平台固件可以添加额外的启动选项或从启动顺序列表中删除无效的启动选项。 4 | 5 | 如果在固件启动过程中发现异常情况,平台固件也可以在启动管理器中实现增值功能 (TODO)。一个增值功能 (TODO) 的例子是,如果第一次加载 UEFI 驱动程序时启动失败,则不加载该驱动程序。另一个例子是,如果在启动过程中发现关键错误,则启动到 OEM 定义的诊断环境。 6 | 7 | UEFI 的启动顺序包括以下内容: 8 | 9 | - 启动顺序列表是从全局定义的 NVRAM 变量中读取的。对这个变量的修改只保证在下次平台重置后生效。启动顺序列表定义了一个 NVRAM 变量的列表,其中包含了要启动的内容的信息。每个 NVRAM 变量定义了一个可以显示给用户的启动选项的名称。 10 | - 该变量还包含一个指向硬件设备和该硬件设备上包含要加载的 UEFI 镜像的文件的指针。 11 | - 该变量还可能包含操作系统分区和目录的路径,以及其他配置特定的目录。 12 | 13 | NVRAM 也可以包含直接传递给 UEFI 镜像的加载选项。平台固件不知道加载选项中包含什么。当更高级别的软件写到全局 NVRAM 变量来设置平台固件的启动策略时,加载选项就被设置了。如果操作系统内核的位置与 UEFI 操作系统加载器的位置不同,该信息可以用来定义操作系统内核的位置。 14 | 15 | ## 固件启动管理器 16 | 17 | 启动管理器是符合本规范的固件中的一个组件,它决定哪些驱动程序和应用程序应该被显式加载以及何时加载。一旦符合规范的固件被初始化,它就会把控制权交给启动管理器。然后启动管理器负责决定加载什么,以及在做出这样的决定时可能需要与用户进行的任何互动。 18 | 19 | 启动管理器采取的行动取决于系统类型和系统设计者设置的策略。对于允许安装新启动变量(第 3.4 节)的系统,启动管理器必须自动或根据加载项的请求,初始化至少一个系统控制台,并执行主设备中指示的所有必需的初始化启动目标。对于此类系统,启动管理器还需要遵守在 `BootOrder` 变量中设置的优先级。 20 | 21 | 特别是,可能的实施方案可能包括任何有关启动的控制台界面,启动选择的综合平台管理,以及可能的对其他内部应用或恢复驱动的了解,这些都可能通过启动管理器集成到系统中。 22 | 23 | ### 启动管理器编程 24 | 25 | 与启动管理器的编程交互是通过全局定义的变量完成的。初始化时,启动管理器读取包含 UEFI 环境变量中所有已发布加载选项的值。通过使用 `SetVariable()` 函数,可以修改包含这些环境变量的数据。此类修改保证在下一次系统启动后生效。但是,启动管理器实现可以选择改进此保证,并让更改对所有后续访问影响启动管理器行为的变量立即生效,而无需任何形式的系统重置。 26 | 27 | 每个加载选项条目都驻留在 `Boot####`、`Driver####`、`SysPrep####`、`OsRecovery####`或 `PlatformRecovery####`变量中,其中`####`被一个唯一的选项号码所取代,该号码为可打印的十六进制表示,使用数字 0-9 和字符 A-F 的大写版本(0000-FFFF)。 28 | 29 | `####`,必须始终是四位数,所以小数字必须使用前导零。然后,加载的选项由一个以所需顺序列出的选项号码阵列进行逻辑排序。正常启动时有两个这样的选项排序列表。第一个是 `DriverOrder`,它将 `Driver####` 的加载选项变量排序到它们的加载顺序。第二个是 `BootOrder`,它将 `Boot####` 加载的选项变量排序到它们的加载顺序中。 30 | 31 | 例如,要增加一个新的启动选项,就要增加一个新的`Boot####` 变量。然后,新的 `Boot####` 变量的选项号将被添加到 `BootOrder` 列表中,`BootOrder` 变量将被重写。要改变现有的 `Boot####` 的启动选项,只需要重写 `Boot####` 变量。类似的操作也可以用来增加、删除或修改驱动加载列表。 32 | 33 | 如果通过 `Boot####` 返回的状态是 `EFI_SUCCESS`,平台固件支持启动管理器菜单,如果固件被配置为以交互模式启动,那么启动管理器将停止处理 `BootOrder` 变量并向用户展示启动管理器菜单。如果不满足上述任何一个条件,`BootOrder` 变量中的下一个 `Boot####`,直到所有的可能性都被用完。在这种情况下,必须恢复启动选项(见 3.4 节)。 34 | 35 | 启动管理器可以对数据库变量进行自动维护。例如,它可以删除未被引用的加载选项变量或任何不能被解析的加载选项变量,它可以重写任何有序的列表,以删除任何没有相应加载选项变量的加载选项。启动管理器也可以根据自己的判断,为管理员提供调用手动维护操作的能力。例子包括选择任何或所有加载选项的顺序,激活或停用加载选项,启动操作系统定义的或平台定义的恢复等。此外,如果平台打算创建 `PlatformRecovery####`,在尝试加载和执行任何 `DriverOrder` 或 `BootOrder` 条目之前,固件必须创建任何和所有 `PlatformRecovery####`变量(见 3.4.2 节)。在正常操作下,固件不应自动删除当前由 `BootOrder` 或 `BootNext` 变量引用的任何正确形成的 `Boot####`变量。这种删除应仅限于固件由用户直接交互式操作的情况。 36 | 37 | 启动管理器可以对数据库变量进行自动维护。例如,它可以删除未被引用的加载选项变量或任何不能被解析的加载选项变量,它可以重写任何有序的列表,以删除任何没有相应加载选项变量的加载选项。启动管理器也可以根据自己的判断,为管理员提供调用手动维护操作的能力。例子包括选择任何或所有加载选项的顺序,激活或停用加载选项,启动操作系统定义的或平台定义的恢复等。此外,如果平台打算创建 `PlatformRecovery####`,在尝试加载和执行任何 `DriverOrder` 或 `BootOrder` 条目之前,固件必须创建任何和所有 `PlatformRecovery####`变量(见 3.4.2 节)。在正常操作下,固件不应自动删除当前由 `BootOrder` 或 `BootNext` 变量引用的任何正确形成的 `Boot####`变量。这种删除应限于固件由用户直接互动指导的情况。 38 | 39 | `PlatformRecovery####` 的内容表示如果在当前启动期间启动恢复,固件将尝试的最终恢复选项,并且不需要包含反映重大硬件重新配置等突发事件的条目,或与固件的特定硬件相对应的条目目前还不知道。 40 | 41 | 当安全启动被启用时,UEFI 启动管理器的行为会受到影响,见第 32.4 节。 42 | 43 | ### 加载选项处理 44 | 45 | 启动管理器需要在启动加载选项条目之前处理驱动加载选项条目。如果 `EFI_OS_INDICATIONS_START_OS_RECOVERY` 位在 `OsIndications` 中被设置,固件应尝试操作系统定义的恢复(见 3.4.1 节)而不是正常的启动处理。如果 `EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY` 位在 `OsIndications` 中被设置,固件应尝试平台定义的恢复(见 3.4.2 节),而不是正常启动处理或处理 `EFI_OS_INDICATIONS_START_OS_RECOVERY` 位。在任何一种情况下,这两个位都应该被清空。 46 | 47 | 否则,启动管理器还需要启动 `BootNext` 变量所指定的启动选项作为下一次启动的第一个启动选项,而且只在下一次启动时启动。在把控制权转移到 `BootNext` 启动选项之前,启动管理器会删除 `BootNext` 变量。在尝试了 `BootNext` 启动选项之后,会使用正常的 `BootOrder` 列表。为了防止循环,启动管理器在把控制权转移到预选的启动选项之前删除了 `BootNext`。 48 | 49 | 如果 `BootNext` 和 `BootOrder` 的所有条目都被用尽而没有成功,或者如果固件被指示尝试启动顺序恢复,那么固件必须尝试启动选项恢复(见 3.4 节)。 50 | 51 | 启动管理器必须调用 `EFI_BOOT_SERVICES.LoadImage()`,它至少支持 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 和 `EFI_LOAD_FILE_PROTOCOL` 来解析加载选项。如果 `LoadImage()` 成功,在调用 `EFI_BOOT_SERVICES.SetWatchdogTimer()` 启动服务之前,启动管理器必须通过使用 `EFI_BOOT_SERVICES.StartImage()` 启用看门狗计时器 5 分钟。如果一个启动选项将控制权返回给启动管理器,启动管理器必须通过额外调用 `SetWatchdogTimer()` 启动服务来禁用看门狗计时器。 52 | 53 | 如果启动镜像没有通过 `EFI_BOOT_SERVICES.LoadImage()` 加载,那么 启动管理器 需要检查一个默认的应用程序来启动。在可移动和固定的媒体类型上都会发生搜索默认的应用程序来启动。当任何启动选项中列出的启动镜像的设备路径直接指向 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 设备,并且没有指定要加载的确切文件时,就会发生这种搜索。文件发现方法在第 3.4 节中有解释。`EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 以外的协议的默认媒体启动情况由目标设备路径的 `EFI_LOAD_FILE_PROTOCOL` 处理,不需要由启动管理器处理。 54 | 55 | UEFI 启动管理器必须支持从一个简短的设备路径启动,该路径的第一个元素是 `USB WWID`(见表 10-23)或 `USB Class`(见表 10-25)设备路径。对于 `USB WWID`,启动管理器必须使用设备供应商 ID、设备产品 ID 和序列号,并且必须匹配系统中任何包含这些信息的 USB 设备。如果有一个以上的设备与 `USB WWID` 设备路径相匹配,启动管理器将任意选择一个。对于 USB 类,启动管理器必须使用供应商 ID、产品 ID、设备类、设备子类和设备协议,并且必须与系统中任何包含这些信息的 USB 设备相匹配。如果任何一个 ID、产品 ID、设备类、设备子类、设备协议中包含所有的 F(0xFFFF 或 0xFF),这个元素就会被跳过,以进行匹配。如果有一个以上的设备与 USB 类设备路径相匹配,启动管理器将任意选择一个。 56 | 57 | 如果 Boot Image 没有通过 `EFI_BOOT_SERVICES.LoadImage()` 加载,那么 启动管理器 需要检查一个默认的应用程序来启动。在可移动和固定的媒体类型上都会发生搜索默认的应用程序来启动。当任何启动选项中列出的启动镜像的设备路径直接指向 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 设备,并且没有指定要加载的确切文件时,就会发生这种搜索。文件发现方法在第 3.4 `节中有解释。EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 以外的协议的默认媒体启动情况由目标设备路径的 `EFI_LOAD_FILE_PROTOCOL` 处理,不需要由启动管理器处理。 58 | 59 | UEFI 启动管理器必须支持从一个简短的设备路径启动,该路径的第一个元素是 `USB WWID`(见表 10-23)或 `USB Class`(见表 10-25)设备路径。对于 `USB WWID`,启动管理器必须使用设备供应商 ID、设备产品 ID 和序列号,并且必须匹配系统中任何包含这些信息的 USB 设备。如果有一个以上的设备与 `USB WWID` 设备路径相匹配,启动管理器将任意选择一个。对于 USB 类,启动管理器必须使用供应商 ID、产品 ID、设备类、设备子类和设备协议,并且必须与系统中任何包含这些信息的 USB 设备相匹配。如果任何一个 ID、产品 ID、设备类、设备子类、设备协议中包含所有的 F(0xFFFF 或 0xFF),这个元素就会被跳过,以进行匹配。如果有一个以上的设备与 USB 类设备路径相匹配,启动管理器将任意选择一个。 60 | 61 | 启动管理器还必须支持从简短的设备路径启动,该路径的第一个元素是硬盘媒体设备路径(见表 10-49)。启动管理器必须使用硬盘设备路径中的 GUID 或签名和分区号来将其与系统中的设备相匹配。如果硬盘支持 GPT 分区方案,那么硬盘介质设备路径中的 GUID 将与 GUID 分区条目的 `UniquePartitionGuid` 字段进行比较(见表 5-6)。如果硬盘支持 PC-AT MBR 方案,则将硬盘介质设备路径中的签名与传统主启动记录中的 `UniqueMBRSignature` 进行比较(见表 5-1)。如果签名匹配,那么分区号也必须匹配。硬盘设备路径可以被附加到匹配的硬件设备路径上,然后可以使用正常的启动行为。如果有一个以上的设备与硬盘设备路径相匹配,启动管理器将任意选择一个。因此操作系统必须保证硬盘上签名的唯一性,以保证确定的启动行为。 62 | 63 | 启动管理器还必须支持从以第一个元素为硬盘驱动器媒体设备路径开始的短格式设备路径启动(参见表 10-49)。启动管理器必须使用硬盘驱动器设备路径中的 GUID 或签名和分区号,以将其与系统中的设备相匹配。如果驱动器支持 GPT 分区方案,则将硬盘驱动器媒体设备路径中的 GUID 与 GUID 分区条目的 `UniquePartitionGuid` 字段进行比较(参见表 5-6)。如果驱动器支持 PC-AT MBR 方案,则将硬盘驱动器媒体设备路径中的签名与 `Legacy Master Boot Record` 中的 `UniqueMBRSignature` 进行比较(参见表 5-1)。如果进行了签名匹配,则分区号也必须匹配。可以将硬盘驱动器设备路径附加到匹配的硬件设备路径,然后可以使用正常的启动行为。如果多个设备与硬盘驱动器设备路径匹配,则启动管理器将任意选择一个。因此,操作系统必须确保硬盘驱动器上签名的唯一性,以保证确定的启动行为。 64 | 65 | 启动管理器还必须支持从一个简短的设备路径启动,这个路径的第一个元素是文件路径媒体设备路径(见表 10-52)。当启动管理器试图启动一个短格式的文件路径媒体设备路径时,它将列举所有可移动媒体设备,然后是所有固定媒体设备,为每个设备创建启动选项。启动选项 `FilePathList[0]` 是通过将短格式的 File Path Media Device Path 附加到一个媒体的设备路径上而构建的。每组内的顺序是未定义的。这些新的启动选项不能被保存到非易失性存储中,也不能被添加到 `BootOrder` 中。然后启动管理器将尝试从每个启动选项启动。如果一个设备不支持 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL`,但是支持 `EFI_BLOCK_IO_PROTOCOL` 协议,那么 EFI Boot Service ConnectController 必须在 `DriverImageHandle` 和 `RemainingDevicePath` 设置为 `NULL`,并且递归标志设置为 `TRUE` 的情况下为这个设备调用。然后,固件将尝试使用上述算法从产生的任何子手柄启动。 66 | 67 | 启动管理器还必须支持从一个短形式的设备路径启动,该路径的第一个元素是 URI 设备路径(见表 10-40)。当启动管理器试图启动一个短形式的 URI 设备路径时,它可以尝试连接任何设备,这些设备会产生一个包括 URI 设备路径节点的设备路径协议,直到它匹配到一个设备,或者无法匹配任何设备。启动管理器将枚举所有的 `LoadFile` 协议实例,并在匹配过程中调用 `LoadFile` 协议,并将 `FilePath` 设置为短格式设备路径 68 | 69 | ### 加载选项 70 | 71 | 每个加载选项变量包含一个 `EFI_LOAD_OPTION` 描述符,它是一个由可变长度的字段组成的字节打包的缓冲区。 72 | 73 | ```C 74 | typedef struct _EFI_LOAD_OPTION { 75 | UINT32 Attributes; UINT16 FilePathListLength; 76 | // CHAR16 Description[]; 77 | // EFI_DEVICE_PATH_PROTOCOL FilePathList[]; 78 | // UINT8 OptionalData[]; 79 | } EFI_LOAD_OPTION; 80 | ``` 81 | 82 | 参数 83 | 84 | - **Attributes**:此加载选项条目的属性。所有未使用的位必须为零,并由 UEFI 规范为未来的发展保留。参见 "相关定义"。 85 | - **FilePathListLength**:以字节为单位的 `FilePathList` 的长度。`OptionalData` 从 `EFI_LOAD_OPTION` 描述符的偏移量 `sizeof(UINT32) + sizeof(UINT16) + StrSize(Description) + FilePathListLength` 开始。 86 | - **Description**:加载选项的用户可读描述。这个字段以一个空字符结束。 87 | - **FilePathList**:一个 UEFI 设备路径的打包数组。数组的第一个元素是一个设备路径,描述了这个加载选项的设备和镜像的位置。`FilePathList[0]` 是特定于设备类型的。其他设备路径可以选择存在于 `FilePathList` 中,但它们的使用是 OSV 特定的。数组中的每个元素都是可变长度的,并在设备路径的末端结构处结束。因为 `Description` 的大小是任意的,这个数据结构不能保证在自然边界上对齐。这个数据结构在使用前可能要被复制到一个对齐的自然边界上。 88 | - **OptionalData**:加载选项描述符中的剩余字节是一个二进制数据缓冲区,它被传递给加载的图像。如果该字段的长度为 0 字节,则将传递一个 NULL `指针给加载的图像。OptionalData` 中的字节数可以通过从 `EFI_LOAD_OPTION` 的总大小(字节)中减去 `OptionalData` 的起始偏移来计算。 89 | 90 | 相关定义 91 | 92 | ```C 93 | //******************************************************* 94 | // Attributes 95 | //******************************************************* 96 | #define LOAD_OPTION_ACTIVE 0x00000001 97 | #define LOAD_OPTION_FORCE_RECONNECT 0x00000002 98 | #define LOAD_OPTION_HIDDEN 0x00000008 99 | #define LOAD_OPTION_CATEGORY 0x00001F00 100 | #define LOAD_OPTION_CATEGORY_BOOT 0x00000000 101 | #define LOAD_OPTION_CATEGORY_APP 0x00000100// All values 0x00000200-0x00001F00 are reserved 102 | ``` 103 | 104 | 调用 `SetVariable()` 会创建一个加载选项。加载选项的大小与创建该变量的 `SetVariable()` 调用的 `DataSize` 参数的大小相同。当创建一个新的加载选项时,所有未定义的属性位必须写成 0。当更新一个加载选项时,所有未定义的属性位必须被保留下来。 105 | 106 | 如果一个加载选项被标记为 `LOAD_OPTION_ACTIVE`,那么启动管理器将尝试使用加载选项中的设备路径信息自动启动。这提供了一个简单的方法来禁用或启用加载选项,而不需要删除和重新添加它们。 107 | 108 | 如果任何 `Driver####`加载选项被标记为 `LOAD_OPTION_FORCE_RECONNECT`,那么系统中所有的 UEFI 驱动将被断开连接,并在最后一个 `Driver####`加载选项被处理后重新连接上。这允许用 `Driver####`加载选项加载的 UEFI 驱动覆盖在执行 UEFI 启动管理器之前加载的 UEFI 驱动。 109 | 110 | 在 `Driver####` 加载选项中,`FilePathList[0]` 指示的可执行文件必须是 `EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER` 或 `EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER` 类型,否则指示的可执行文件将不会被输入初始化。 111 | 112 | 在 `SysPrep###`、`Boot####`或 `OsRecovery####`加载选项中,`FilePathList[0]` 指示的可执行文件必须是 `EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION` 类型,否则指示的可执行文件将不会被输入。 113 | 114 | `LOAD_OPTION_CATEGORY` 是 `Attributes` 的一个子字段,它为 启动管理器 提供细节,描述它应该如何分组 `Boot####` 加载选项。这个字段对于 `Driver####`, `SysPrep####`,或者 `OsRecovery####`形式的变量是被忽略的。 115 | 116 | 将 `LOAD_OPTION_CATEGORY` 设置为 `LOAD_OPTION_CATEGORY_BOOT` 的 `Boot####`加载选项是正常启动处理的一部分。 117 | 118 | 将 `LOAD_OPTION_CATEGORY` 设置为 `LOAD_OPTION_CATEGORY_APP` 的 `Boot####`加载选项是可执行文件,它不是正常启动处理的一部分,但是如果提供了启动菜单,可以选择执行,或者通过热键。详见 3.1.6 节。 119 | 120 | 带有保留类别值的启动选项,将被启动管理器忽略。 121 | 122 | 如果任何 `Boot####` 的加载选项被标记为 `LOAD_OPTION_HIDDEN`,那么这个加载选项就不会出现在 启动管理器 提供的用于加载选项选择的菜单中(如果有的话)。 123 | 124 | ### 启动管理器的功能 125 | 126 | 启动管理器可以通过全局变量 `BootOptionSupport` 来报告它的能力。如果全局变量不存在,那么安装程序或应用程序必须像返回值为 0 一样行事。 127 | 128 | ```C 129 | #define EFI_BOOT_OPTION_SUPPORT_KEY 0x00000001 130 | #define EFI_BOOT_OPTION_SUPPORT_APP 0x00000002 131 | #define EFI_BOOT_OPTION_SUPPORT_SYSPREP 0x00000010 132 | #define EFI_BOOT_OPTION_SUPPORT_COUNT 0x00000300 133 | ``` 134 | 135 | 如果 `EFI_BOOT_OPTION_SUPPORT_KEY` 被设置,那么启动管理器支持使用按键启动 `Boot####`加载选项。如果 `EFI_BOOT_OPTION_SUPPORT_APP` 被设置,那么启动管理器支持使用 `LOAD_OPTION_CATEGORY_APP` 启动选项。如果 `EFI_BOOT_OPTION_SUPPORT_SYSPREP` 被设置,那么引导管理器支持 `SysPrep####`形式的引导选项。 136 | 137 | `EFI_BOOT_OPTION_SUPPORT_COUNT` 中指定的值描述了启动管理器在 `EFI_KEY_OPTION.KeyData.InputKeyCount` 中支持的最大按键数。这个值只有在 `EFI_BOOT_OPTION_SUPPORT_KEY` 被设置时才有效。指定了更多按键的按键序列会被忽略。 138 | 139 | ### 启动 Boot#### 应用程序 140 | 141 | 引导管理器可以为应用程序支持一个单独的 `Boot####` 加载选项类别。启动管理器通过在 `BootOptionSupport` 全局变量中设置 `EFI_BOOT_OPTION_SUPPORT_APP` 来表明它支持这个单独的类别。 142 | 143 | 当一个应用程序的 `Boot####` 选项被添加到 `BootOrder` 中时,安装者应该清除 `LOAD_OPTION_ACTIVE`,这样引导管理器就不会试图自动 "启动 "这个应用程序。如果启动管理器指出它支持一个单独的应用程序类别,如上所述,安装者应该设置 `LOAD_OPTION_CATEGORY_APP`。如果不是,它应该设置 `LOAD_OPTION_CATEGORY_BOOT`。 144 | 145 | ### 使用热键启动 Boot#### 加载选项 146 | 147 | 启动管理器可能支持使用一个特殊的按键来启动 `Boot####` 加载选项。如果是这样,引导管理器通过在 `BootOptionSupport` 全局变量中设置 `EFI_BOOT_OPTION_SUPPORT_KEY` 来报告这种能力。 148 | 149 | 一个支持按键启动的启动管理器从控制台读取当前的按键信息。然后,如果有一个按键被按下,它将返回的按键与零个或多个 `Key####` 全局变量进行比较。如果找到了匹配,它就会验证指定的 Boot####加载选项是否有效,如果有效,就会尝试立即启动它。`Key####` 中的`####` 是一个可打印的十六进制数字('0'-'9','A'-'F'),前面是零。检查 `Key####`变量的顺序是根据具体实施情况而定的。 150 | 151 | 当指定的热键与内部引导管理器功能重叠时,引导管理器可以忽略 `Key####` 变量。建议 启动管理器 删除这些键。`Key####` 变量有以下格式。 152 | 153 | 结构体原型 154 | 155 | ```C 156 | typedef struct _EFI_KEY_OPTION { 157 | EFI_BOOT_KEY_DATA KeyData; 158 | UINT32 BootOptionCrc; 159 | UINT16 BootOption; 160 | // EFI_INPUT_KEY Keys[]; 161 | } EFI_KEY_OPTION; 162 | ``` 163 | 164 | 参数 165 | 166 | - **KeyData**:指定关于如何处理钥匙的选项。`EFI_BOOT_KEY_DATA` 类型在下面 "相关定义 "中定义。 167 | - **BootOptionCrc**:应该与 BootOption 所指的整个 `EFI_LOAD_OPTION` 的 CRC-32 匹配。如果 CRC-32 与此值不匹配,那么这个关键选项将被忽略。 168 | - **BootOption**:`Boot####`,如果按下这个键,并且启动选项处于激活状态(`LOAD_OPTION_ACTIVE` 被设置),将被调用。 169 | - **Keys**:与 `EFI_SIMPLE_TEXT_INPUT` 和 `EFI_SIMPLE_TEXT_INPUT_EX` 协议返回的密钥代码进行比较。键码的数量(0-3)由 `KeyOptions` 中的 `EFI_KEY_CODE_COUNT` 字段指定。 170 | 171 | 相关定义 172 | 173 | ```C 174 | typedef union { 175 | struct { 176 | UINT32 Revision : 8; 177 | UINT32 ShiftPressed : 1; 178 | UINT32 ControlPressed : 1; 179 | UINT32 AltPressed : 1; 180 | UINT32 LogoPressed : 1; 181 | UINT32 MenuPressed : 1; 182 | UINT32 SysReqPressed : 1; 183 | UINT32 Reserved : 16; 184 | UINT32 InputKeyCount : 2; 185 | }Options; 186 | UINT32 PackedValue; 187 | } EFI_BOOT_KEY_DATA; 188 | ``` 189 | 190 | - **Revision**:表示 `EFI_KEY_OPTION` 结构的修订。这个修订级别应该是 0。 191 | - **ShiftPressed**:必须按下左或右的 Shift 键(1)或不按下(0)。 192 | - **ControlPressed**:左边或右边的 Control 键必须被按下(1)或不能被按下(0)。 193 | - **AltPressed**:左边或右边的 Alt 键必须被按下(1)或不能被按下(0)。 194 | - **LogoPressed**:左边或右边的 徽标 键必须被按下(1)或不能被按下(0)。 195 | - **MenuPressed**:菜单键必须按下(1)或不按下(0)。 196 | - **SysReqPressed**:SysReq 键必须被按下(1)或不被按下(0)。 197 | - **InputKeyCount**:指定 `EFI_KEY_OPTION.Keys` 中的实际条目数,从 0-3。如果为零,那么只考虑移位状态。如果多于一个,那么只有当所有指定的键都以相同的移位状态按下时,启动选项才会被启动。 198 | 例#1:ALT 是热键。`KeyData.PackedValue = 0x00000400`。 199 | 例#2:CTRL-ALT-P-R。`KeyData.PackedValue = 0x80000600`。 200 | 例#3: CTRL-F1. `KeyData.PackedValue = 0x40000200`. 201 | 202 | ### 必要的系统准备应用 203 | 204 | `SysPrep####`形式的加载选项旨在指定一个 UEFI 应用程序,该应用程序需要执行,以便在处理任何 `Boot####`变量之前完成系统准备。`SysPrep####`应用程序的执行顺序由变量 `SysPrepOrder` 的内容决定,其方式直接类似于 `BootOrder` 对 `Boot####`选项的排序。 205 | 206 | 平台需要检查在 `SysPrepOrder` 中引用的所有 `SysPrep####` 变量。如果属性位 `LOAD_OPTION_ACTIVE` 被设置,并且`FilePathList[0]` 所引用的应用程序存在,那么由此确定的 UEFI 应用程序必须按照它们在 `SysPrepOrder` 中出现的顺序加载和启动,并且在启动任何 `Boot####`类型的加载选项之前。 207 | 208 | 当启动时,平台需要为由 `SysPrep####`加载的应用程序提供相同的服务,如控制台和网络,就像通常在启动时提供给由 `Boot####`变量引用的应用程序一样。`SysPrep####` 应用程序必须退出,不得调用 `ExitBootServices()`。对退出时返回的任何 `Error Code` 的处理是根据系统策略进行的,不一定会改变对以下启动选项的处理。任何由 `SysPrep####`启动选项支持的、需要保持驻留的功能的驱动部分都应该通过使用 `Driver####`变量加载。 209 | 210 | `LOAD_OPTION_FORCE_RECONNECT` 属性选项对于 `SysPrep####`变量来说是被忽略的,如果这样启动的应用程序执行了一些增加可用硬件或驱动程序的操作,系统准备应用程序本身应利用对 `ConnectController()` 或 `DisconnectController()`的适当调用来修改驱动程序和硬件之间的连接。 211 | 212 | 在所有 `SysPrep####` 变量启动和退出后,平台应通知 `EFI_EVENT_GROUP_READY_TO_BOOT` 和 `EFI_EVENT_GROUP_AFTER_READY_TO_BOOT` 事件组,并开始按照 `BootOrder` 定义的顺序评估属性设置为 `LOAD_OPTION_CATEGORY_BOOT` 的 `Boot####`变量。在 `EFI_EVENT_GROUP_AFTER_READY_TO_BOOT` 事件组处理完成之前,不应评估标记为 `LOAD_OPTION_CATEGORY_BOOT` 的变量的 `FilePathList`。 213 | 214 | ## 启动管理器策略协议 215 | 216 | ### EFI_BOOT_MANAGER_POLICY_PROTOCOL 217 | 218 | #### 摘要 219 | 220 | 这个协议被 EFI 应用程序用来请求 UEFI 启动管理器使用平台策略连接设备。 221 | 222 | #### GUID 223 | 224 | ```C 225 | #define EFI_BOOT_MANAGER_POLICY_PROTOCOL_GUID \ 226 | { 0xFEDF8E0C, 0xE147, 0x11E3,\ 227 | { 0x99, 0x03, 0xB8, 0xE8, 0x56, 0x2C, 0xBA, 0xFA } } 228 | ``` 229 | 230 | #### 协议接口结构 231 | 232 | ```C 233 | typedef struct _EFI_BOOT_MANAGER_POLICY_PROTOCOL EFI_BOOT_MANAGER_POLICY_PROTOCOL;struct _EFI_BOOT_MANAGER_POLICY_PROTOCOL { UINT64 Revision; 234 | EFI_BOOT_MANAGER_POLICY_CONNECT_DEVICE_PATH ConnectDevicePath; EFI_BOOT_MANAGER_POLICY_CONNECT_DEVICE_CLASS ConnectDeviceClass; 235 | }; 236 | 237 | ConnectDevicePath Connect a Device Path following the platforms EFI Boot Manager policy. 238 | ConnectDeviceClassConnect a class of devices, named by EFI_GUID, following the platforms UEFI Boot Manager policy. 239 | ``` 240 | 241 | #### 描述 242 | 243 | EFI_BOOT_MANAGER_POLICY_PROTOCOL 是由平台固件产生的,以暴露启动管理器策略和平台特定的 EFI_BOOT_SERVICES.ConnectController() 行为。 244 | 245 | #### 相关定义 246 | 247 | ```C 248 | #define EFI_BOOT_MANAGER_POLICY_PROTOCOL_REVISION 0x00010000 249 | ``` 250 | 251 | ### EFI_BOOT_MANAGER_POLICY_PROTOCOL.ConnectDevicePath() 252 | 253 | #### 摘要 254 | 255 | 按照平台的 EFI 启动管理器策略,连接一个设备路径。 256 | 257 | #### 原型 258 | 259 | ```C 260 | typedef EFI_STATUS(EFIAPI *EFI_BOOT_MANAGER_POLICY_CONNECT_DEVICE_PATH)( 261 | IN EFI_BOOT_MANAGER_POLICY_PROTOCOL *This, 262 | IN EFI_DEVICE_PATH *DevicePath, 263 | IN BOOLEAN Recursive 264 | ); 265 | ``` 266 | 267 | #### 参数 268 | 269 | - This:指向 EFI_BOOT_MANAGER_POLICY_PROTOCOL 实例的指针。类型为上面定义的 EFI_BOOT_MANAGER_POLICY_PROTOCOL。 270 | - DevicePath:指向要连接的 EFI 设备路径的起点。如果 DevicePath 为 NULL,那么系统中的所有控制器都将使用平台的 EFI 启动管理器策略进行连接。 271 | - Recursive:如果是 TRUE,那么 ConnectController() 将被递归调用,直到 DevicePath 指定的控制器下面的整个控制器树都被创建。如果是 FALSE,那么控制器树只扩展一级。如果 DevicePath 是 NULL,那么递归将被忽略。 272 | 273 | #### 描述 274 | 275 | ConnectDevicePath() 函数允许调用者使用与 EFI Boot Manager 相同的策略连接 DevicePath。如果递归为 true,那么 ConnectController() 将被递归调用,直到 DevicePath 指定的控制器下面的整个控制器树都被创建。如果递归为 FALSE,那么控制器树只扩展一级。如果 DevicePath 是 NULL,那么递归将被忽略。 276 | 277 | #### 返回的状态代码 278 | 279 | | 状态码 | 描述 | 280 | | :----: | :----: | 281 | | EFI_SUCCESS | 设备路径已被连接 | 282 | | EFI_NOT_FOUND | 未找到设备路径 | 283 | | EFI_NOT_FOUND | 没有驱动程序被连接到 DevicePath | 284 | | EFI_SECURITY_VIOLATION | 用户没有权限启动 UEFI 设备驱动程序设备路径 | 285 | | EFI_UNSUPPORTED | 当前的 TPL 不是 TPL_APPLICATION | 286 | 287 | ### EFI_BOOT_MANAGER_POLICY_PROTOCOL.ConnectDeviceClass() 288 | 289 | #### 摘要 290 | 291 | 使用平台启动管理器策略连接一类设备。 292 | 293 | #### 原型 294 | 295 | ```C 296 | typedef EFI_STATUS(EFIAPI *EFI_BOOT_MANAGER_POLICY_CONNECT_DEVICE_CLASS)( 297 | IN EFI_BOOT_MANAGER_POLICY_PROTOCOL *This, 298 | IN EFI_GUID *Class 299 | ); 300 | ``` 301 | 302 | #### 参数 303 | 304 | - This:指向 EFI_BOOT_MANAGER_POLICY_PROTOCOL 实例的一个指针。上面定义了 EFI_BOOT_MANAGER_POLICY_PROTOCOL 类型 305 | - Class:一个指向 EFI_GUID 的指针,代表将使用 Boot Manager 的平台策略连接的设备类别 306 | 307 | #### 描述 308 | 309 | ConnectDeviceClass() 函数允许调用者请求引导管理器连接一个设备类别。 310 | 311 | 如果 Class 是 EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID,那么 Boot Manager 将使用平台策略来 312 | 连接控制台。一些平台在尝试快速启动时可能会限制连接的控制台数量,调用 Class 值为 313 | EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID 的 ConnectDeviceClass() 必须连接遵循引导管理器 314 | 平台策略的控制台集合。并且 EFI_SIMPLE_TEXT_INPUT_PROTOCOL、 315 | EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL 和 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 为 316 | 在连接的手柄上产生。开机管理器可以根据平台策略限制哪些控制台可以被连接,例如,安全策略 317 | 可能要求不连接某个特定的控制台。 318 | 如果 Class 是 EFI_BOOT_MANAGER_POLICY_NETWORK_GUID,那么启动管理器将在一个或多个手 319 | 柄上连接平台支持的 UEFI 通用网络应用的协议。与 UEFI 通用网络应用相关的协议在第 2.6.2 节中定 320 | 义,列表中的第 7 项。如果有一个以上的网络控制器,平台将根据平台策略连接一个、多个或所 321 | 有的网络。连接 UEFI 网络协议,如 EFI_DHCP4_PROTOCOL,并不在网络上建立连接。调用 322 | ConnectDeviceClass() 的 UEFI 通用网络应用程序可能需要使用发布的协议来建立网络连接。启 323 | 动管理器可以选择有一个策略来建立网络连接。 324 | 如果 Class 是 EFI_BOOT_MANAGER_POLICY_CONNECT_ALL_GUID,那么 Boot Manager 将使用 UEFI 325 | Boot Service EFI_BOOT_SERVICES.ConnectController() 连接所有 UEFI 驱动程序。如果 Boot 326 | Manager 有与连接所有 UEFI 驱动相关的策略,将使用这个策略 327 | 328 | 一个平台也可以定义平台特定的 Class 值,因为正确生成的 EFI_GUID 绝不会与本规范冲突。 329 | 330 | #### 相关定义 331 | 332 | ```C 333 | #define EFI_BOOT_MANAGER_POLICY_CONSOLE_GUID \ 334 | { 0xCAB0E94C, 0xE15F, 0x11E3,\ 335 | { 0x91, 0x8D, 0xB8, 0xE8, 0x56, 0x2C, 0xBA, 0xFA } } 336 | #define EFI_BOOT_MANAGER_POLICY_NETWORK_GUID \ 337 | { 0xD04159DC, 0xE15F, 0x11E3,\ 338 | { 0xB2, 0x61, 0xB8, 0xE8, 0x56, 0x2C, 0xBA, 0xFA } } 339 | #define EFI_BOOT_MANAGER_POLICY_CONNECT_ALL_GUID \ 340 | { 0x113B2126, 0xFC8A, 0x11E3,\ 341 | { 0xBD, 0x6C, 0xB8, 0xE8, 0x56, 0x2C, 0xBA, 0xFA } } 342 | ``` 343 | 344 | #### 返回的状态代码 345 | 346 | | 状态码 | 描述 | 347 | | :----: | :----: | 348 | | EFI_SUCCESS | 该类至少有一个设备被连接 | 349 | | EFI_DEVICE_ERROR | 由于一个错误,设备没有被连接 | 350 | | EFI_NOT_FOUND | 该类平台不支持 | 351 | | EFI_UNSUPPORTED | 当前的 TPL 不是 TPL_APPLICATION | 352 | 353 | ## 全局定义的变量 354 | 355 | 本节定义了一组具有架构定义含义的变量。除了定义的数据内容外,每个这样的变量都有一个架构上定义的属性,表明当数据变量可以被访问。属性为 NV 的变量是非易失性的。这意味着它们的值在复位和电源循环中是持久的。任何没有这个属性的环境变量的值都会在系统断电后丢失,而且固件保留内存的状态也不会被保留下来。具有 BS 属性的变量仅在 `EFI_BOOT_SERVICES.ExitBootServices()` 被调用之前可用。这意味着这些环境变量只能在预启动环境中被检索或修改。它们对操作系统是不可见的。属性为 RT 的环境变量在 `ExitBootServices()` 被调用之前和之后都可用。这种类型的环境变量可以在预启动环境和操作系统中被检索和修改。属性为 AT 的变量是具有第 8.2.1 节中定义的基于时间的验证写入权限的变量。所有架构定义的变量都使用 `EFI_GLOBAL_VARIABLE`。 356 | 357 | ```c 358 | EFI_GLOBAL_VARIABLE VendorGuid: 359 | # define EFI_GLOBAL_VARIABLE \{0x8BE4DF61,0x93CA,0x11d2,\ {0xAA,0x0D,0x00,0xE0,0x98,0x03,0x2B,0x8C}} 360 | ``` 361 | 362 | 为了防止与未来可能的全局定义的变量发生名称冲突,这里没有定义的其他内部固件数据变量必须用一个唯一的 VendorGuid 来保存,而不是 `EFI_GLOBAL_VARIABLE` 或 UEFI 规范定义的任何其他 GUID。只有当 UEFI 规范中记录了这些变量时,实施才必须允许用 UEFI 规范定义的 `VendorGuid` 创建这些变量。 363 | 364 | | 变量名 | 属性 | 描述 | 365 | | :----: | :----: | :----: | 366 | | AuditMode | BS, RT | 系统是否在审核模式下运行 (1) 或 (0)。所有其他值均保留。应被视为只读,除非 DeployedMode 为 0。调用 ExitBootServices() 后始终变为只读。 | 367 | | Boot#### | NV, BS, RT | 引导加载选项。 #### 是打印的十六进制值。十六进制值中不包含 0x 或 h | 368 | | BootCurrent | BS, RT | 引导加载选项。 #### 是打印的十六进制值。十六进制值中不包含 0x 或 h | 369 | | BootNext | NV, BS, RT | 仅用于下次启动的启动选项。 | 370 | | BootOrder | NV, BS, RT | 有序引导选项加载列表。 | 371 | | BootOptionSupport | BS,RT | 引导管理器支持的引导选项类型。应视为只读。 | 372 | | ConIn | NV, BS, RT | 默认输入控制台的设备路径。 | 373 | | ConInDev | BS, RT | 所有可能的控制台输入设备的设备路径。 | 374 | | ConOut | NV, BS, RT | 默认输出控制台的设备路径。 | 375 | | ConOutDev | BS, RT | 所有可能的控制台输出设备的设备路径。 | 376 | | dbDefault | BS, RT | OEM 的默认安全启动签名存储。应被视为只读。 | 377 | | dbrDefault | BS, RT | OEM 的默认操作系统恢复签名存储。应视为只读。 | 378 | | dbtDefault | BS, RT | OEM 的默认安全启动时间戳签名存储。应被视为只读 | 379 | | dbxDefault | BS, RT | OEM 的默认安全启动黑名单签名存储。应视为只读。 | 380 | | DeployedMode | BS, RT | 系统是否在部署模式下运行 (1) 或 (0)。所有其他值均保留。当其值为 1 时应被视为只读。在调用 ExitBootServices() 后始终变为只读。 | 381 | | Driver#### | NV, BS, RT | 驱动程序加载选项。 #### 是打印的十六进制值。 | 382 | | DriverOrder | NV, BS, RT | bbbbbb |有序的驱动程序加载选项列表。 383 | | ErrOut | NV, BS, RT | 默认错误输出设备的设备路径。 | 384 | | ErrOutDev | BS, RT | 所有可能的错误输出设备的设备路径。 | 385 | | HwErrRecSupport | NV, BS, RT | 标识平台实现的硬件错误记录持久性支持级别。此变量仅由固件修改并且对操作系统是只读的。 | 386 | | KEK | NV, BS, RT,AT | 密钥交换密钥签名数据库。 | 387 | | KEKDefault | BS, RT | OEM 的默认密钥交换密钥签名数据库。应被视为只读。 | 388 | | Key#### | NV, BS, RT | 描述热键与 Boot#### 加载选项的关系。 | 389 | | Lang | NV, BS, RT | 系统配置的语言代码。此值已弃用。 | 390 | | LangCodes | BS, RT | 固件所支持的语言代码。此值已被废弃。 | 391 | | OsIndications | NV, BS, RT | 允许操作系统请求固件启用某些功能并执行某些操作。 | 392 | | OsIndicationsSupported | BS, RT | 允许固件向操作系统指示支持的功能和操作 | 393 | | OsRecoveryOrder | BS,RT,NV,AT | 操作系统指定的恢复选项 | 394 | | PK | NV, BS, RT,AT | 公共平台密钥。 | 395 | | PKDefault | BS, RT | OEM 的默认公共平台密钥。应被视为只读 | 396 | | PlatformLangCodes | BS, RT | 固件支持的语言代码。 | 397 | | PlatformLang | NV, BS, RT | 系统配置的语言代码。 | 398 | | PlatformRecovery#### | BS, RT | 平台指定的恢复选项。这些变量仅由固件修改并且对操作系统是只读的。 | 399 | | SignatureSupport | BS, RT | 表示平台固件支持的签名类型的 GUID 数组。应被视为只读 | 400 | | SecureBoot | BS, RT | 平台固件是否在安全启动模式下运行 (1) 或 (0)。所有其他值均保留。应被视为只读。 | 401 | | SetupMode | BS, RT | 系统是否应要求对安全启动策略变量的 SetVariable() 请求进行身份验证 (0) 或 (1)。应视为只读。当 SetupMode==1,AuditMode==0,DeployedMode==0 时,系统处于“设置模式” | 402 | | SysPrep#### | NV, BS, RT | 包含 EFI_LOAD_OPTION 描述符的 System Prep 应用程序加载选项。 #### 是打印的十六进制值。 | 403 | | SysPrepOrder | NV, BS, RT | 有序的系统准备应用程序加载选项列表。 | 404 | | Timeout | NV, BS, RT | 在启动默认引导选择之前,固件的引导管理器超时(以秒为单位) | 405 | | VendorKeys | BS, RT | 系统是否配置为仅使用供应商提供的密钥。应视为只读。 | 406 | 407 | `PlatformLangCodes` 变量包含一个以 null 结尾的 ASCII 字符串,表示固件可以支持的语言代码。在初始化时,固件会计算支持的语言并创建此数据变量。由于固件在每次初始化时都会创建此值,因此其内容不会存储在非易失性存储器中。该值被认为是只读的。`PlatformLangCodes` 以 Native RFC 4646 格式指定。请参阅附录 M。`LangCodes` 已弃用,可能会提供以实现向后兼容性。 408 | 409 | `PlatformLang` 变量包含机器已配置的以 n​​ull 结尾的 ASCII 字符串语言代码。该值可以更改为 `PlatformLangCodes` 支持的任何值。如果在预引导环境中进行此更改,则更改将立即生效。如果此更改是在操作系统运行时进行的,则更改要到下次启动时才会生效。如果语言代码设置为不受支持的值,固件将在初始化时选择受支持的默认值并将 `PlatformLang` 设置为受支持的值。`PlatformLang` 以 Native RFC 4646 数组格式指定。请参阅附录 M。`Lang` 已弃用,可能会提供以实现向后兼容性。 410 | 411 | `Lang` 已被弃用。如果平台支持这个变量,它必须以适当的形式将 `Lang` 变量中的任何更改映射到 `PlatformLang`。 412 | 413 | `Langcodes` 已被弃用。如果平台支持此变量,则它必须以适当的格式将 `Langcodes` 变量中的任何更改映射到 `PlatformLang`。 414 | 415 | `Timeout` 变量包含一个二进制 `UINT16`,它提供固件在启动原始默认引导选择之前将等待的秒数。值 0 表示默认引导选择将在引导时立即启动。如果该值不存在,或包含 `0xFFFF` 的值,则固件将在引导前等待用户输入。这意味着固件不会自动启动默认启动选择。 416 | 417 | `ConIn`、`ConOut` 和 `ErrOut` 变量分别包含一个 `EFI_DEVICE_PATH_PROTOCOL` 描述符,定义了启动时使用的默认设备。在预启动环境中对这些值的改变会立即生效。在操作系统运行时对这些值的改变要到下一次启动时才会生效。如果固件不能解决设备路径,允许它根据需要自动替换这些值,为系统提供一个控制台。如果设备路径以 USB 类设备路径开始(见表 10-25),那么任何符合设备路径的输入或输出设备都必须作为控制台使用,如果它被固件支持的话。 418 | 419 | `ConInDev`、`ConOutDev` 和 `ErrOutDev` 变量均包含一个 `EFI_DEVICE_PATH_PROTOCOL` 描述符,该描述符定义了所有可能在引导时使用的默认设备。这些变量是易变的,并且在每次启动时动态设置。`ConIn`、`ConOut` 和 `ErrOut` 始终是 `ConInDev`、`ConOutDev` 和 `ErrOutDev` 的真子集。 420 | 421 | 每个 `Boot####` 变量都包含一个 `EFI_LOAD_OPTION`。每个 `Boot####` 变量都是名称“Boot”附加一个唯一的四位十六进制数字。例如,`Boot0001`、`Boot0002`、`Boot0A02` 等。 422 | 423 | `OsRecoveryOrder` 变量包含一个 `EFI_GUID` 结构的数组。每个 `EFI_GUID` 结构为包含操作系统定义的恢复条目的变量指定一个命名空间(见第 3.4.1 节)。对这个变量的写访问由安全密钥数据库 dbr 控制(见第 8.2.1 节)。 424 | 425 | `PlatformRecovery####` 变量与 `Boot####` 变量共享相同的结构。这些变量是在系统执行引导选项恢复时处理的。 426 | 427 | `BootOrder` 变量包含一个 UINT16 的数组,构成了 `Boot####` 选项的有序列表。数组中的第一个元素是第一个逻辑启动选项的值,第二个元素是第二个逻辑启动选项的值,等等。`BootOrder` 顺序列表被固件的引导管理器作为默认的引导顺序。 428 | 429 | `BootNext` 变量是单个 UINT16,它定义了 `Boot####` 选项,该选项将在下次启动时首先尝试。在尝试使用 `BootNext` 引导选项后,将使用正常的 `BootOrder` 列表。为防止循环,引导管理器在将控制转移到预选引导选项之前删除此变量。 430 | 431 | `BootCurrent` 变量是一个单一的 UINT16,它定义了当前启动时选择的 `Boot####` 选项。 432 | 433 | `BootOptionSupport` 变量是一个 UINT32,它定义了引导管理器支持的引导选项类型。 434 | 435 | 每个 `Driver####` 变量都包含一个 `EFI_LOAD_OPTION`。每个加载选项变量都附加一个唯一的编号,例如 `Driver0001`、`Driver0002` 等。 436 | 437 | `DriverOrder` 变量包含一个 UINT16 的数组,构成了 `Driver####`变量的有序列表。数组中的第一个元素是第一个逻辑驱动加载选项的值,第二个元素是第二个逻辑驱动加载选项的值,等等。该 `DriverOrder` 列表被固件的启动管理器用作 UEFI 驱动程序的默认加载顺序,它应该明确地加载这些驱动程序。 438 | 439 | `Key####` 变量将按键与单个引导选项相关联。每个 `Key####` 变量都是名称“Key”附加一个唯一的四位十六进制数字。例如,`Key0001`、`Key0002`、`Key00A0` 等。 440 | 441 | `HwErrRecSupport` 变量包含一个二进制 UINT16,它提供对由平台实现的硬件错误记录持久性(请参阅第 8.2.4 节)的支持级别。如果该值不存在,则平台不实现对硬件错误记录持久性的支持。零值表示平台不支持硬件错误记录持久性。值为 1 表示平台实现了第 8.2.4 节中定义的硬件错误记录持久性。固件初始化这个变量。所有其他值保留供将来使用。 442 | 443 | `SetupMode` 变量是一个 8 位无符号整数,它定义系统是否应该在 `SetVariable()` 请求安全引导策略变量时要求身份验证 (0) 或不需要 (1)。安全启动策略变量包括: 444 | 445 | - 全局变量 `PK`、`KEK` 和 `OsRecoveryOrder` 446 | - 所有 VendorGuid 下名为 `OsRecovery####` 的所有变量 447 | - 具有 VendorGuid `EFI_IMAGE_SECURITY_DATABASE_GUID` 的所有变量。 448 | 449 | 必须使用 `EFI_VARIABLE_AUTHENTICATION_2` 结构创建安全启动策略变量。 450 | 451 | `AuditMode` 变量是一个 8 位无符号整数,它定义系统当前是否在审计模式下运行。 452 | 453 | `DeployedMode` 变量是一个 8 位无符号整数,定义系统当前是否在部署模式下运行。 454 | 455 | `KEK` 变量包含当前密钥交换密钥数据库。 456 | 457 | `PK` 变量包含当前的平台密钥。 458 | 459 | `VendorKeys` 变量是一个 8 位无符号整数,用于定义安全引导策略变量是否已被平台供应商或供应商提供的密钥持有者以外的任何人修改。值为 0 表示平台供应商或供应商提供的密钥的持有者以外的其他人修改了安全启动策略变量,否则该值为 1。 460 | 461 | `KEKDefault` 变量(如果存在)包含平台定义的密钥交换密钥数据库。这在运行时不使用,但提供是为了允许操作系统恢复 OEM 的默认密钥设置。此变量的内容不包括 `EFI_VARIABLE_AUTHENTICATION` 或 `EFI_VARIABLE_AUTHENTICATION2` 结构。 462 | 463 | `PKDefault` 变量(如果存在)包含平台定义的平台密钥。这在运行时不使用,但提供是为了允许操作系统恢复 `OEM` 的默认密钥设置。此变量的内容不包括 `EFI_VARIABLE_AUTHENTICATION2` 结构。 464 | 465 | `dbDefault` 变量(如果存在)包含平台定义的安全启动签名数据库。这在运行时不使用,但提供是为了允许操作系统恢复 OEM 的默认密钥设置。此变量的内容不包括 `EFI_VARIABLE_AUTHENTICATION2` 结构。 466 | 467 | `dbrDefault` 变量(如果存在)包含平台定义的安全启动授权恢复签名数据库。这在运行时不使用,但提供是为了允许操作系统恢复 OEM 的默认密钥设置。此变量的内容不包括 `EFI_VARIABLE_AUTHENTICATION2` 结构。 468 | 469 | `dbtDefault` 变量(如果存在)包含平台定义的安全启动时间戳签名数据库。这在运行时不使用,但提供是为了允许操作系统恢复 OEM 的默认密钥设置。此变量的内容不包括 `EFI_VARIABLE_AUTHENTICATION2` 结构。 470 | 471 | ## 引导选项恢复 472 | 473 | 引导选项恢复由两个独立的部分组成,操作系统定义的恢复和平台定义的恢复。操作系统定义的恢复是一种尝试,允许已安装的操作系统恢复任何需要的引导选项,或启动完整的操作系统恢复。平台定义的恢复包括平台在未找到操作系统时作为最后手段执行的任何补救措施,例如默认引导行为(请参阅第 3.4.3 节)。这可能包括保修服务重新配置或诊断选项等行为。 474 | 475 | 如果必须执行启动选项恢复,启动管理器必须首先尝试操作系统定义的恢复,然后通过 `Boot####` 和 `BootOrder` 变量重新尝试正常启动,如果没有选项成功,最后尝试平台定义的恢复。 476 | 477 | ### 操作系统定义的引导选项恢复 478 | 479 | 如果在 `OsIndications` 中设置了 `EFI_OS_INDICATIONS_START_OS_RECOVERY` 位,或者如果 `BootOrder` 的处理没有成功,则平台必须处理操作系统定义的恢复选项。如果由于 `OsIndications` 而进入操作系统定义的恢复,则不应处理 `SysPrepOrder` 和 `SysPrep####` 变量。请注意,为了避免意图歧义,如果设置了 `EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY`,则在 `OsIndications` 中忽略此位。 480 | 481 | 操作系统定义的恢复使用 `OsRecoveryOrder` 变量,以及使用供应商特定的 `VendorGuid` 值创建的变量和遵循模式 `OsRecovery####` 的名称。这些变量中的每一个都必须是具有 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性集的经过身份验证的变量。 482 | 483 | 为了处理这些变量,引导管理器遍历 `OsRecoveryOrder` 变量中的 `EFI_GUID` 结构数组,并且每个指定的 `GUID` 都被视为与一系列变量名称相关联的 `VendorGuid`。对于每个 `GUID`,固件会尝试以十六进制排序顺序加载和执行每个具有该 `GUID` 和名称遵循模式 `OsRecovery####` 的变量。这些变量与 `Boot####` 变量具有相同的格式,并且启动管理器必须验证它尝试加载的每个变量都是使用与证书相关联的公钥创建的,该证书链接到授权恢复签名数据库 `dbr` 中列出的证书并且不在禁止的签名数据库中,或者由密钥交换密钥数据库 `KEK` 或当前平台密钥 `PK` 中的密钥创建。 484 | 485 | 如果引导管理器在没有调用 `EFI_BOOT_SERVICES.ExitBootServices()` 或 `ResetSystem()` 的情况下完成 `OsRecovery####` 选项的处理,则它必须尝试第二次处理 `BootOrder`。如果在该过程中引导不成功,操作系统定义的恢复失败,引导管理器必须尝试基于平台的恢复。 486 | 487 | 如果在处理 `OsRecovery####` 变量时,引导管理器遇到由于违反安全策略而无法加载或执行的条目,则它必须忽略该变量。 488 | 489 | ### 平台定义的引导选项恢复 490 | 491 | 如果在 `OsIndications` 中设置了 `EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY` 位,或者如果操作系统定义的恢复失败,则系统固件必须通过以与 `OsRecovery####` 相同的方式迭代其 `PlatformRecovery####`变量来开始特定于平台的恢复,但必须如果任何条目成功,则停止处理。如果由于 `OsIndications` 而输入特定于平台的恢复,则不应处理 `SysPrepOrder` 和 `SysPrep####` 变量。 492 | 493 | ### 引导选项变量默认引导行为 494 | 495 | 全局定义变量的默认状态是特定于固件供应商的。但是,在平台上不存在有效引导选项的例外情况下,引导选项需要标准默认行为。任何时候 `BootOrder` 变量不存在或仅指向不存在的引导选项,或者如果 `BootOrder` 中的条目都无法成功执行,则必须调用默认行为。 496 | 497 | 如果系统固件支持启动选项恢复,如第 3.4 节所述,系统固件必须包含一个 `PlatformRecovery####` 变量,指定一个简短格式的文件路径媒体设备路径(请参阅第 3.1.2 节),其中包含可移动媒体的平台默认文件路径(见表 3-2)。为了最大程度地兼容本规范的先前版本,建议将此条目作为第一个此类变量,尽管它可能位于列表中的任何位置。 498 | 499 | 预计此默认引导将加载操作系统或维护实用程序。如果这是操作系统设置程序,则它负责为后续引导设置必要的环境变量。平台固件还可以决定恢复或设置为一组已知的启动选项。 500 | 501 | ## 引导机制 502 | 503 | EFI 可以使用 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 或 `EFI_LOAD_FILE_PROTOCOL` 从设备引导。支持 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 的设备必须实现该设备可引导的文件系统协议。如果设备不希望支持完整的文件系统,它可能会生成一个 `EFI_LOAD_FILE_PROTOCOL`,允许它直接实现图像。引导管理器将首先尝试使用 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 进行引导。如果失败,则将使用 `EFI_LOAD_FILE_PROTOCOL`。 504 | 505 | ### 通过简单文件协议启动 506 | 507 | 当通过 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 引导时,`FilePath` 将以指向实现 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 或 `EFI_BLOCK_IO_PROTOCOL` 的设备的设备路径开始。 `FilePath` 的下一部分可能指向文件名,包括包含可引导映像的子目录。如果文件名是空设备路径,则文件名必须根据下面定义的规则生成。 508 | 509 | 如果 `FilePathList[0]` 设备不支持 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL`,但支持 `EFI_BLOCK_IO_PROTOCOL` 协议,则必须为 `FilePathList[0]` 调用 EFI 引导服务 `EFI_BOOT_SERVICES.ConnectController()`,`DriverImageHandle` 和 `RemainingDevicePath` 设置为 `NULL`,递归标志设置为 `TRUE`。然后固件将尝试从使用下面概述的算法生成的任何子句柄启动。 510 | 511 | 指定文件系统的格式包含在第 13.3 节中。虽然固件必须生成一个理解 UEFI 文件系统的 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL`,但是任何文件系统都可以使用 `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 接口进行抽象。 512 | 513 | #### 移动媒介引导行为 514 | 515 | 要在 `FilePath` 中不存在文件名时生成文件名,固件必须以 `\EFI\BOOT\BOOT{machine type short-name}.EFI` 形式附加默认文件名,其中 machine type short-name 定义 PE32+ 图像格式建筑学。每个文件只包含一种 UEFI 映像类型,系统可能支持从一种或多种映像类型启动。表 3-2 列出了 UEFI 图像类型。 516 | 517 | ![UEFI 镜像类型](../pic/table3-2.png "UEFI镜像类型") 518 | 519 | 媒体可以通过简单地拥有一个 `\EFI\BOOT\BOOT{machine type short-name}.EFI` 文件来支持多种体系结构。 520 | 521 | ### 通过加载文件协议启动 522 | 523 | 当通过 `EFI_LOAD_FILE_PROTOCOL` 协议启动时,`FilePath` 是一个设备路径,指向“说出” `EFI_LOAD_FILE_PROTOCOL` 的设备。图像直接从支持 `EFI_LOAD_FILE_PROTOCOL` 的设备加载。`FilePath` 的其余部分将包含特定于设备的信息。固件将此特定于设备的数据传递给加载的图像,但不使用它来加载图像。如果 `FilePath` 的其余部分是空设备路径,则加载的映像有责任实施策略以找到正确的引导设备。 524 | 525 | `EFI_LOAD_FILE_PROTOCOL` 用于不直接支持文件系统的设备。网络设备通常以这种模式启动,在这种模式下图像无需文件系统即可实现。 526 | 527 | #### 网络引导 528 | 529 | 网络引导由预引导执行环境 (Preboot eXecution Environment,PXE) BIOS 支持规范描述,该规范是 Wired for Management Baseline 规范的一部分。PXE 指定引导平台可用于与智能系统加载服务器交互的 UDP、DHCP 和 TFTP 网络协议。UEFI 定义了用于实现 PXE 的特殊接口。这些接口包含在 EFI_PXE_BASE_CODE_PROTOCOL 中(参见第 24.3 节)。 530 | 531 | #### 未来引导媒介 532 | 533 | 由于 UEFI 定义了平台和操作系统及其加载程序之间的抽象,因此随着技术的发展,应该可以添加新类型的引导媒体。操作系统加载程序不一定要更改以支持新类型的引导。UEFI 平台服务的实现可能会发生变化,但接口将保持不变。操作系统将需要驱动程序来支持新型引导媒体,以便它可以从 UEFI 引导服务过渡到引导媒体的操作系统控制。 -------------------------------------------------------------------------------- /src/4-EFI-System-Table.md: -------------------------------------------------------------------------------- 1 | # 4-EFI-System-Table 2 | 3 | 本节介绍 UEFI 映像的入口点以及传递到该入口点的参数。符合本规范的固件可以加载和执行三种类型的 UEFI 镜像。它们是 UEFI 应用程序(参见第 2.1.2 节)、UEFI 引导服务驱动程序(参见第 2.1.4 节)和 UEFI 运行时驱动程序(参见第 2.1.4 节)。UEFI 应用程序包括 UEFI OS 加载程序(参见第 2.1.3 节)。这三种镜像类型的入口点没有区别。 4 | 5 | ## UEFI Image Entry Point 6 | 7 | 传递给镜像的最重要参数是指向系统表的指针。该指针是 `EFI_IMAGE_ENTRY_POINT`(参见下面的定义),它是 UEFI 映像的主要入口点。系统表包含指向活动控制台设备的指针、指向启动服务表的指针、指向运行时服务表的指针以及指向系统配置表(如 ACPI、SMBIOS 和 SAL 系统表)列表的指针。本节详细介绍系统表。 8 | 9 | **EFI_IMAGE_ENTRY_POINT** 10 | 11 | **概述** 12 | 13 | 这是 UEFI 映像的主要入口点。此入口点对于 UEFI 应用程序和 UEFI 驱动程序是相同的。 14 | 15 | **原型** 16 | 17 | ```C 18 | typedef 19 | EFI_STATUS 20 | (EFIAPI *EFI_IMAGE_ENTRY_POINT) ( 21 | IN EFI_HANDLE ImageHandle, 22 | IN EFI_SYSTEM_TABLE *SystemTable 23 | ); 24 | ``` 25 | 26 | **参数** 27 | 28 | - **ImageHandle**:为 UEFI 映像分配的固件句柄 29 | - **SystemTable**:指向 EFI 系统表的指针 30 | 31 | **描述** 32 | 33 | 此函数是 EFI 映像的入口点。EFI 映像由 EFI 引导服务`EFI_BOOT_SERVICES.LoadImage()` 加载并重新定位在系统内存中。EFI 映像通过 EFI 引导服务 `EFI_BOOT_SERVICES.StartImage()` 调用。 34 | 35 | 传递给镜像的最重要参数是指向系统表的指针。这个指针是 `EFI_IMAGE_ENTRY_POINT`(见下面的定义),UEFI Image 的主要入口点。系统表包含指向活动控制台设备的指针、指向引导服务表的指针、指向运行时服务表的指针以及指向系统配置表列表(例如 ACPI、SMBIOS 和 SAL 系统表)的指针。本节详细介绍系统表。 36 | 37 | `ImageHandle` 是固件分配的句柄,用于在各种功能上识别镜像。该句柄还支持镜像可以使用的一种或多种协议。所有镜像都支持 `EFI_LOADED_IMAGE_PROTOCOL` 和 `EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL`,它们返回镜像的源位置、镜像的内存位置、镜像的加载选项等。确切的 `EFI_LOADED_IMAGE_PROTOCOL` 和 `EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL` 结构在第 9 节中定义。 38 | 39 | 如果 UEFI 映像是不是 UEFI 操作系统加载程序的 UEFI 应用程序,则该应用程序将执行并返回或调用 EFI 引导服务 `EFI_BOOT_SERVICES.Exit()`。UEFI 应用程序在退出时总是从内存中卸载,并将其返回状态返回给启动该 UEFI 应用程序的组件。 40 | 41 | 如果 UEFI 映像是 UEFI 操作系统加载程序,则 UEFI 操作系统加载程序将执行并返回,调用 EFI 引导服务 `Exit()`,或调用 EFI 引导服务 `EFI_BOOT_SERVICES.ExitBootServices()`。如果 EFI OS Loader 返回或调用 `Exit()`,则 OS 加载失败,EFI OS Loader 从内存中卸载,控制权返回到尝试启动 UEFI OS Loader 的组件。如果调用了 `ExitBootServices()`,那么 UEFI OS Loader 已经控制了平台,EFI 将不会重新获得系统的控制权,直到平台被重置。重置平台的一种方法是通过 EFI 运行时服务 `ResetSystem()`。 42 | 43 | 如果 UEFI 映像是 UEFI 驱动程序,则 UEFI 驱动程序将执行并返回或调用 Boot Service `Exit()`。如果 UEFI 驱动程序返回错误,则驱动程序将从内存中卸载。如果 UEFI 驱动程序返回 `EFI_SUCCESS`,则它会驻留在内存中。如果 UEFI 驱动程序不遵循 UEFI 驱动程序模型,则它会执行任何必需的初始化并在返回之前安装其协议服务。如果驱动程序确实遵循 UEFI 驱动程序模型,则不允许入口点接触任何设备硬件。相反,入口点需要在 UEFI 驱动程序的 `ImageHandle` 上创建和安装 `EFI_DRIVER_BINDING_PROTOCOL`(请参阅第 11.1 节)。如果此过程完成,则返回 `EFI_SUCCESS`。如果资源不可用于完成 UEFI 驱动程序初始化,则返回 `EFI_OUT_OF_RESOURCES`。 44 | 45 | **返回的状态码** 46 | 47 | - `EFI_SUCCESS`:驱动程序已初始化。 48 | - `EFI_OUT_OF_RESOURCES`:由于缺乏资源,请求无法完成。 49 | 50 | ## EFI 表头 51 | 52 | 数据类型 `EFI_TABLE_HEADER` 是所有标准 EFI 表类型之前的数据结构。它包括对每个表类型唯一的签名、可以在扩展添加到 EFI 表类型时更新的表修订以及一个 32 位 CRC,因此 EFI 表类型的消费者可以验证 EFI 表类型的内容 EFI 表。 53 | 54 | **EFI_TABLE_HEADER** 55 | 56 | **概述** 57 | 58 | 在所有标准 EFI 表类型之前的数据结构。 59 | 60 | **原型** 61 | 62 | ```C 63 | typedef struct { 64 | UINT64 Signature; 65 | UINT32 Revision; 66 | UINT32 HeaderSize; 67 | UINT32 CRC32; 68 | UINT32 Reserved; 69 | } EFI_TABLE_HEADER; 70 | ``` 71 | 72 | **参数** 73 | 74 | - **Signature**:标识后面的表类型的 64 位签名。已为 EFI 系统表、EFI 引导服务表和 EFI 运行时服务表生成唯一签名。 75 | - **Revision**:此表符合的 EFI 规范的修订版。该字段的高 16 位包含主修订值,低 16 位包含次修订值。次要修订值是二进制编码的十进制数,并且限制在 00..99 的范围内。 76 | - 当打印或显示时,UEFI 规范修订被称为(主要修订)。(次要修订上位小数)。(次要修订小数下位)或(主要修订)。(次要修订小数点上位)如果次要修订小数下位设置为 0。例如 77 | - 具有修订值 ((2<<16) | (30)) 的规范将称为 2.3 78 | - 具有修订值 ((2<<16) | (31)) 的规范将称为 2.3.1 79 | - **HeaderSize**:整个表的大小(以字节为单位),包括 EFI_TABLE_HEADER。 80 | - **CRC32**:整个表的 32 位 CRC。通过将此字段设置为 0 并计算 HeaderSize 字节的 32 位 CRC 来计算此值 81 | - **Reserved**:必须设置为 0 的保留字段。 82 | 83 | **描述** 84 | 85 | 注:EFI 系统表、运行时表和引导服务表中的功能可能会随时间发生变化。每个表中的第一个字段是 EFI_TABLE_HEADER。当新的能力和功能被添加到表中的功能时,此标头的修订字段会增加。检查功能时,代码应验证 Revision 是否大于或等于将功能添加到 UEFI 规范时表的修订级别。 86 | 87 | 注:除非另有说明,否则 UEFI 使用标准 CCITT32 CRC 算法进行 CRC 计算,其种子多项式值为 `0x04c11db7`。 88 | 89 | 注:系统表、运行时服务表和引导服务表的大小可能会随着时间的推移而增加。始终使用 `EFI_TABLE_HEADER` 的 `HeaderSize` 字段来确定这些表的大小非常重要。 90 | 91 | ## EFI 系统表 92 | 93 | UEFI 使用 EFI 系统表,它包含指向运行时和启动服务表的指针。这个表的定义在下面的代码片段中显示。除了表头,服务表中的所有元素都是指向第 7 节和第 8 节中定义的函数的指针。在调用 `EFI_BOOT_SERVICES.ExitBootServices()` 之前,EFI 系统表的所有字段都是有效的。在操作系统通过调用 `ExitBootServices()` 控制平台后,只有 Hdr、`FirmwareVendor`、`FirmwareRevision`、`RuntimeServices`、`NumberOfTableEntries` 和 `ConfigurationTable` 字段有效 94 | 95 | **EFI_SYSTEM_TABLE** 96 | 97 | **概述** 98 | 99 | 包含指向运行时和引导服务表的指针。 100 | 101 | **相关定义** 102 | 103 | ```C 104 | #define EFI_SYSTEM_TABLE_SIGNATURE 0x5453595320494249 105 | #define EFI_2_90_SYSTEM_TABLE_REVISION ((2<<16) | (90)) 106 | #define EFI_2_80_SYSTEM_TABLE_REVISION ((2<<16) | (80)) 107 | #define EFI_2_70_SYSTEM_TABLE_REVISION ((2<<16) | (70)) 108 | #define EFI_2_60_SYSTEM_TABLE_REVISION ((2<<16) | (60)) 109 | #define EFI_2_50_SYSTEM_TABLE_REVISION ((2<<16) | (50)) 110 | #define EFI_2_40_SYSTEM_TABLE_REVISION ((2<<16) | (40)) 111 | #define EFI_2_31_SYSTEM_TABLE_REVISION ((2<<16) | (31)) 112 | #define EFI_2_30_SYSTEM_TABLE_REVISION ((2<<16) | (30)) 113 | #define EFI_2_20_SYSTEM_TABLE_REVISION ((2<<16) | (20)) 114 | #define EFI_2_10_SYSTEM_TABLE_REVISION ((2<<16) | (10)) 115 | #define EFI_2_00_SYSTEM_TABLE_REVISION ((2<<16) | (00)) 116 | #define EFI_1_10_SYSTEM_TABLE_REVISION ((1<<16) | (10)) 117 | #define EFI_1_02_SYSTEM_TABLE_REVISION ((1<<16) | (02)) 118 | #define EFI_SPECIFICATION_VERSION EFI_SYSTEM_TABLE_REVISION 119 | #define EFI_SYSTEM_TABLE_REVISION EFI_2_90_SYSTEM_TABLE_REVISION 120 | typedef struct { 121 | EFI_TABLE_HEADER Hdr; 122 | CHAR16 *FirmwareVendor; 123 | UINT32 FirmwareRevision; 124 | EFI_HANDLE ConsoleInHandle; 125 | EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; 126 | EFI_HANDLE ConsoleOutHandle; 127 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; 128 | EFI_HANDLE StandardErrorHandle; 129 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; 130 | EFI_RUNTIME_SERVICES *RuntimeServices; 131 | EFI_BOOT_SERVICES *BootServices; 132 | UINTN NumberOfTableEntries; 133 | EFI_CONFIGURATION_TABLE *ConfigurationTable; 134 | } EFI_SYSTEM_TABLE; 135 | ``` 136 | 137 | 参数 138 | 139 | - **Hdr**:EFI 系统表的表头。此标头包含 `EFI_SYSTEM_TABLE_SIGNATURE` 和 `EFI_SYSTEM_TABLE_REVISION` 值以及 `EFI_SYSTEM_TABLE` 结构的大小和 32 位 CRC,以验证 EFI 系统表的内容是否有效 140 | - **FirmwareVendor**:指向空终止字符串的指针,该字符串标识为平台生产系统固件的供应商 141 | - **FirmwareRevision**:固件供应商特定值,用于标识平台的系统固件修订版。 142 | - **ConsoleInHandle**:活动控制台输入设备的句柄。此句柄必须支持 `EFI_SIMPLE_TEXT_INPUT_PROTOCOL` 和 `EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL`。如果没有活动的控制台,这些协议必须仍然存在。 143 | - **ConIn**:指向与 ConsoleInHandle 关联的 `EFI_SIMPLE_TEXT_INPUT_PROTOCOL` 接口的指针。 144 | - **ConsoleOutHandle**:活动控制台输出设备的句柄。此句柄必须支持 `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL`。如果没有活动控制台,则此协议必须仍然存在 145 | - **ConOut**:指向与 ConsoleOutHandle 关联的 `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL` 接口的指针 146 | - **StandardErrorHandle**:活动标准错误控制台设备的句柄。此句柄必须支持 `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL`。如果没有活动控制台,则此协议必须仍然存在 147 | - **StdErr**:指向与 `StandardErrorHandle` 关联的 `EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL` 接口的指针 148 | - **RuntimeServices**:指向 EFI 运行时服务表的指针。请参阅第 4.5 节。 149 | - **BootServices**:指向 EFI 引导服务表的指针。请参阅第 4.4 节。 150 | - **NumberOfTableEntries**:缓冲区 `ConfigurationTable` 中系统配置表的个数 151 | - **ConfigurationTable**:指向系统配置表的指针。表中的条目数是 `NumberOfTableEntries`。 152 | 153 | ## EFI Boot Services Table 154 | 155 | UEFI 使用 EFI 引导服务表,其中包含表头和指向所有引导服务的指针。该表的定义显示在以下代码片段中。除表头外,EFI 引导服务表中的所有元素都是函数指针的原型,指向第 7 节中定义的函数。在操作系统通过调用控制平台后,此表中的函数指针无效 `EFI_BOOT_SERVICES.ExitBootServices()`。 156 | 157 | **EFI_BOOT_SERVICES** 158 | 159 | **概述** 160 | 161 | 包含表头和指向所有引导服务的指针。 162 | 163 | **相关定义** 164 | 165 | ```C 166 | #define EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42 167 | #define EFI_BOOT_SERVICES_REVISION EFI_SPECIFICATION_VERSION 168 | typedef struct { 169 | EFI_TABLE_HEADER Hdr; 170 | // 171 | // Task Priority Services 172 | // 173 | EFI_RAISE_TPL RaiseTPL; // EFI 1.0+ 174 | EFI_RESTORE_TPL RestoreTPL; // EFI 1.0+ 175 | // 176 | // Memory Services 177 | // 178 | EFI_ALLOCATE_PAGES AllocatePages; // EFI 1.0+ 179 | EFI_FREE_PAGES FreePages; // EFI 1.0+ 180 | EFI_GET_MEMORY_MAP GetMemoryMap; // EFI 1.0+ 181 | EFI_ALLOCATE_POOL AllocatePool; // EFI 1.0+ 182 | EFI_FREE_POOL FreePool; // EFI 1.0+ 183 | // 184 | // Event & Timer Services 185 | // 186 | EFI_CREATE_EVENT CreateEvent; // EFI 1.0+ 187 | EFI_SET_TIMER SetTimer; // EFI 1.0+ 188 | EFI_WAIT_FOR_EVENT WaitForEvent; // EFI 1.0+ 189 | EFI_SIGNAL_EVENT SignalEvent; // EFI 1.0+ 190 | EFI_CLOSE_EVENT CloseEvent; // EFI 1.0+ 191 | EFI_CHECK_EVENT CheckEvent; // EFI 1.0+ 192 | // 193 | // Protocol Handler Services 194 | // 195 | EFI_INSTALL_PROTOCOL_INTERFACE InstallProtocolInterface; // EFI 1.0+ 196 | EFI_REINSTALL_PROTOCOL_INTERFACE ReinstallProtocolInterface; // EFI 1.0+ 197 | EFI_UNINSTALL_PROTOCOL_INTERFACE UninstallProtocolInterface; // EFI 1.0+ 198 | EFI_HANDLE_PROTOCOL HandleProtocol; // EFI 1.0+ 199 | VOID* Reserved; // EFI 1.0+ 200 | EFI_REGISTER_PROTOCOL_NOTIFY RegisterProtocolNotify; // EFI 1.0+ 201 | EFI_LOCATE_HANDLE LocateHandle; // EFI 1.0+ 202 | EFI_LOCATE_DEVICE_PATH LocateDevicePath; // EFI 1.0+ 203 | EFI_INSTALL_CONFIGURATION_TABLE InstallConfigurationTable; // EFI 1.0+ 204 | // 205 | // Image Services 206 | // 207 | EFI_IMAGE_LOAD LoadImage; // EFI 1.0+ 208 | EFI_IMAGE_START StartImage; // EFI 1.0+ 209 | EFI_EXIT Exit; // EFI 1.0+ 210 | EFI_IMAGE_UNLOAD UnloadImage; // EFI 1.0+ 211 | EFI_EXIT_BOOT_SERVICES ExitBootServices; // EFI 1.0+ 212 | // 213 | // Miscellaneous Services 214 | // 215 | EFI_GET_NEXT_MONOTONIC_COUNT GetNextMonotonicCount; // EFI 1.0+ 216 | EFI_STALL Stall; // EFI 1.0+ 217 | EFI_SET_WATCHDOG_TIMER SetWatchdogTimer; // EFI 1.0+ 218 | // 219 | // DriverSupport Services 220 | // 221 | EFI_CONNECT_CONTROLLER ConnectController; // EFI 1.1 222 | EFI_DISCONNECT_CONTROLLER DisconnectController;// EFI 1.1+ 223 | // 224 | // Open and Close Protocol Services 225 | // 226 | EFI_OPEN_PROTOCOL OpenProtocol; // EFI 1.1+ 227 | EFI_CLOSE_PROTOCOL CloseProtocol; // EFI 1.1+ 228 | EFI_OPEN_PROTOCOL_INFORMATION OpenProtocolInformation; // EFI 1.1+ 229 | // 230 | // Library Services 231 | // 232 | EFI_PROTOCOLS_PER_HANDLE ProtocolsPerHandle; // EFI 1.1+ 233 | EFI_LOCATE_HANDLE_BUFFER LocateHandleBuffer; // EFI 1.1+ 234 | EFI_LOCATE_PROTOCOL LocateProtocol; // EFI 1.1+ 235 | EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES 236 | InstallMultipleProtocolInterfaces; // EFI 1.1+ 237 | EFI_UNINSTALL_MULTIPLE_PROTOCOL_INTERFACES 238 | UninstallMultipleProtocolInterfaces; // EFI 1.1+ 239 | // 240 | // 32-bit CRC Services 241 | // 242 | EFI_CALCULATE_CRC32 CalculateCrc32; // EFI 1.1+ 243 | // 244 | // Miscellaneous Services 245 | // 246 | EFI_COPY_MEM CopyMem; // EFI 1.1+ 247 | EFI_SET_MEM SetMem; // EFI 1.1+ 248 | EFI_CREATE_EVENT_EX CreateEventEx; // UEFI 2.0+ 249 | } EFI_BOOT_SERVICES; 250 | ``` 251 | 252 | **参数** 253 | 254 | - **Hdr** EFI 引导服务表的表头。此标头包含 `EFI_BOOT_SERVICES_SIGNATURE` 和 `EFI_BOOT_SERVICES_REVISION` 值以及 `EFI_BOOT_SERVICES` 结构的大小和 32 位 CRC,以验证 EFI 引导服务表的内容是否有效。 255 | - **RaiseTPL** 提高任务优先级。 256 | - **RestoreTPL** 恢复/降低任务优先级。 257 | - **AllocatePages** 分配特定类型的页面。 258 | - **FreePages** 释放分配的页面。 259 | - **GetMemoryMap** 返回当前引导服务内存映射和内存映射键。 260 | - **AllocatePool** 分配特定类型的池。 261 | - **FreePool** 释放分配的池。 262 | - **CreateEvent** 创建通用事件结构。 263 | - **SetTimer** 设置在特定时间发出信号的事件。 264 | - **WaitForEvent** 停止执行,直到发出事件信号。 265 | - **SignalEvent** 发出事件信号。 266 | - **CloseEvent** 关闭和释放事件结构。 267 | - **CheckEvent** 检查事件是否处于信号状态。 268 | - **InstallProtocolInterface** 在设备句柄上安装协议接口。 269 | - **ReinstallProtocolInterface** 在设备句柄上重新安装协议接口。 270 | - **UninstallProtocolInterface** 从设备句柄中删除协议接口。 271 | - **HandleProtocol** 查询句柄以确定它是否支持指定的协议。保留 保留。必须为 NULL。 272 | - **RegisterProtocolNotify** 注册一个事件,每当为指定协议安装接口时,该事件就会发出信号。 273 | - **LocateHandle** 返回支持指定协议的句柄数组。 274 | - **LocateDevicePath** 定位设备路径上支持指定协议的所有设备,并将句柄返回到距离该路径最近的设备。 275 | - **InstallConfigurationTable** 在 EFI 系统表中添加、更新或删除配置表。 276 | - **LoadImage** 将 EFI 镜像加载到内存中。 277 | - **StartImage** 将控制转移到加载镜像的入口点。退出 退出镜像的入口点。 278 | - **UnloadImage** 卸载镜像。 279 | - **ExitBootServices** 终止引导服务。 280 | - **GetNextMonotonicCount** 返回平台的单调递增计数。停止处理器。 281 | - **SetWatchdogTimer** 重置和设置引导服务期间使用的看门狗定时器。 282 | - **ConnectController** 使用一组优先规则来找到最佳驱动程序集来管理控制器。 283 | - **DisconnectController** 通知一组驱动程序停止管理控制器。 284 | - **OpenProtocol** 将元素添加到使用协议接口的代理列表中。 285 | - **CloseProtocol** 从使用协议接口的代理列表中删除元素。 286 | - **OpenProtocolInformation** 检索当前使用协议接口的代理列表。 287 | - **ProtocolsPerHandle** 检索句柄上安装的协议列表。返回缓冲区是自动分配的。 288 | - **LocateHandleBuffer** 从句柄数据库中检索满足搜索条件的句柄列表。返回缓冲区是自动分配的。 289 | - **LocateProtocol** 在句柄数据库中查找支持所请求协议的第一个句柄。 290 | - **InstallMultipleProtocolInterfaces** 在句柄上安装一个或多个协议接口。 291 | - **UninstallMultipleProtocolInterfaces** 从句柄中卸载一个或多个协议接口。 292 | - **CalculateCrc32** 计算并返回数据缓冲区的 32 位 CRC。 293 | - **CopyMem** 将一个缓冲区的内容复制到另一个缓冲区。 294 | - **SetMem** 用指定值填充缓冲区。 295 | - **CreateEventEx** 创建事件结构作为事件组的一部分 296 | 297 | ## EFI Runtime Services Table 298 | 299 | UEFI 使用 EFI 运行时服务表,其中包含表头和指向所有运行时服务的指针。该表的定义显示在以下代码片段中。除表头外,EFI 运行时服务表中的所有元素都是指向第 8 节中定义的函数的函数指针的原型。与 EFI 引导服务表不同,此表及其包含的函数指针在 UEFI 操作系统加载程序和操作系统通过调用 `EFI_BOOT_SERVICES.ExitBootServices()`控制平台后有效。如果操作系统调用 `SetVirtualAddressMap()`,则此表中的函数指针将固定为指向新的虚拟映射入口点。 300 | 301 | **EFI_RUNTIME_SERVICES** 302 | 303 | **概述** 304 | 305 | 包含表头和指向所有运行时服务的指针。 306 | 307 | **相关定义** 308 | 309 | ```C 310 | #define EFI_RUNTIME_SERVICES_SIGNATURE 0x56524553544e5552 311 | #define EFI_RUNTIME_SERVICES_REVISION EFI_SPECIFICATION_VERSION 312 | typedef struct { 313 | EFI_TABLE_HEADER Hdr; 314 | // 315 | // Time Services 316 | // 317 | EFI_GET_TIME GetTime; 318 | EFI_SET_TIME SetTime; 319 | EFI_GET_WAKEUP_TIME GetWakeupTime; 320 | EFI_SET_WAKEUP_TIME SetWakeupTime; 321 | // 322 | // Virtual Memory Services 323 | // 324 | EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap; 325 | EFI_CONVERT_POINTER ConvertPointer; 326 | // 327 | // Variable Services 328 | // 329 | EFI_GET_VARIABLE GetVariable; 330 | EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; 331 | EFI_SET_VARIABLE SetVariable; 332 | // 333 | // Miscellaneous Services 334 | // 335 | EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount; 336 | EFI_RESET_SYSTEM ResetSystem; 337 | // 338 | // UEFI 2.0 Capsule Services 339 | // 340 | EFI_UPDATE_CAPSULE UpdateCapsule; 341 | EFI_QUERY_CAPSULE_CAPABILITIES QueryCapsuleCapabilities; 342 | // 343 | // Miscellaneous UEFI 2.0 Service 344 | // 345 | EFI_QUERY_VARIABLE_INFO QueryVariableInfo; 346 | } EFI_RUNTIME_SERVICES; 347 | ``` 348 | 349 | **参数** 350 | 351 | - **Hdr** EFI 运行时服务表的表头。此标头包含 `EFI_RUNTIME_SERVICES_SIGNATURE` 和 `EFI_RUNTIME_SERVICES_REVISION` 值以及 `EFI_RUNTIME_SERVICES` 结构的大小和 32 位 CRC,以验证 EFI 运行时服务表的内容是否有效。 352 | - **GetTime** 返回当前时间和日期,以及平台的计时功能。 353 | - **SetTime** 设置当前本地时间和日期信息。 354 | - **GetWakeupTime** 返回当前的唤醒闹钟设置。 355 | - **SetWakeupTime** 设置系统唤醒闹钟时间。 356 | - **SetVirtualAddressMap** 由 UEFI 操作系统加载程序用于从物理寻址转换为虚拟寻址。 357 | - **ConvertPointer** 由 EFI 组件用来在切换到虚拟寻址时转换内部指针。 358 | - **GetVariable** 返回变量的值。 359 | - **GetNextVariableName** 枚举当前变量名。 360 | - **SetVariable** 设置变量的值。 361 | - **GetNextHighMonotonicCount** 返回平台单调计数器的下一个高 32 位。 362 | - **ResetSystem** 重置整个平台。 363 | - **UpdateCapsule** 将胶囊传递给具有虚拟和物理映射的固件。 364 | - **QueryCapsuleCapabilities** 返回是否可以通过 UpdateCapsule() 支持胶囊。 365 | - **QueryVariableInfo** 返回有关 EFI 变量存储的信息 366 | 367 | ## EFI Configuration Table & Properties Table 368 | 369 | EFI 配置表是 EFI 系统表中的 `ConfigurationTable` 字段。此表包含一组 GUID 指针对。该表的每个元素都由下面的 `EFI_CONFIGURATION_TABLE` 结构描述。配置表的类型数量预计会随着时间的推移而增长。这就是使用 GUID 来标识配置表类型的原因。EFI 配置表最多包含每种表类型的一次实例。 370 | 371 | **EFI_CONFIGURATION_TABLE** 372 | 373 | **概述** 374 | 375 | 包含一组 GUID/指针对,由 EFI 系统表中的 ConfigurationTable 字段组成 376 | 377 | **相关定义** 378 | 379 | ```C 380 | typedef struct{ 381 | EFI_GUID VendorGuid; 382 | VOID *VendorTable; 383 | } EFI_CONFIGURATION_TABLE; 384 | ``` 385 | 386 | **参数** 387 | 388 | - **VendorGuid** 唯一标识系统配置表的 128 位 GUID 值。 389 | - **VendorTable** 指向与 `VendorGuid` 关联的表的指针。用于存储表的内存类型以及该指针在运行时是物理地址还是虚拟地址(当调用 `SetVirtualAddressMap()` 时,表中报告的特定地址是否得到修复)由 `VendorGuid` 确定。除非另有说明,否则表缓冲区的内存类型由第 2 章调用约定部分中规定的指南定义。定义 VendorTable 的规范有责任指定额外的内存类型要求(如果有)以及是否转换表中报告的地址。任何所需的地址转换都是发布相应配置表的驱动程序的责任。指向与 VendorGuid 关联的表的指针。这个指针在运行时是物理地址还是虚拟地址由 `VendorGuid` 决定。与给定 `VendorTable` 指针关联的 `VendorGuid` 定义在调用 `SetVirtualAddressMap()` 时表中报告的特定地址是否得到修复。定义 `VendorTable` 的规范有责任指定是否转换表中报告的地址。 390 | 391 | **行业标准配置表** 392 | 393 | 以下列表显示了一些行业标准中定义的表的 GUID。这些行业标准定义了在基于 UEFI 的系统上作为 UEFI 配置表访问的表。这些表条目中报告的所有地址都将被引用为物理地址,并且在从预引导阶段过渡到运行时阶段时不会被修复。此列表并不详尽,不会显示所有可能的 UEFI 配置表的 GUID。 394 | 395 | ```C 396 | 397 | #define EFI_ACPI_20_TABLE_GUID 398 | \ 399 | { 400 | 0x8868e871, 0xe4f1, 0x11d3,\ 401 | { 402 | 0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81 403 | } 404 | } 405 | #define ACPI_TABLE_GUID \ 406 | { 407 | 0xeb9d2d30, 0x2d88, 0x11d3,\ 408 | { 409 | 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d 410 | } 411 | } 412 | #define SAL_SYSTEM_TABLE_GUID \ 413 | { 414 | 0xeb9d2d32, 0x2d88, 0x11d3,\ 415 | { 416 | 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d 417 | } 418 | } 419 | #define SMBIOS_TABLE_GUID \ 420 | { 421 | 0xeb9d2d31, 0x2d88, 0x11d3,\ 422 | { 423 | 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d 424 | } 425 | } 426 | #define SMBIOS3_TABLE_GUID \ 427 | {0xf2fd1544, 0x9794, 0x4a2c,\ {0x99,0x2e,0xe5,0xbb,0xcf,0x20,0xe3,0x94})#define MPS_TABLE_GUID \ 428 | { 429 | 0xeb9d2d2f, 0x2d88, 0x11d3,\ 430 | { 431 | 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d 432 | } 433 | } //// ACPI 2.0 or newer tables should use EFI_ACPI_TABLE_GUID//#define EFI_ACPI_TABLE_GUID \ 434 | {0x8868e871,0xe4f1,0x11d3,\ 435 | {0xbc,0x22,0x00,0x80,0xc7,0x3c,0x88,0x81}} 436 | #define EFI_ACPI_20_TABLE_GUID EFI_ACPI_TABLE_GUID 437 | #define ACPI_TABLE_GUID \ 438 | \ 439 | { \ 440 | 0xeb9d2d30, 0x2d88, 0x11d3, \ 441 | \ 442 | { \ 443 | 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d \ 444 | } \ 445 | } 446 | #define ACPI_10_TABLE_GUID ACPI_TABLE_GUID 447 | 448 | ``` 449 | 450 | **JSON Configuration Tables** 451 | 452 | 以下列表显示了为向 EFI 配置表报告固件配置数据而定义的表的 GUID,以及用于处理第 23.5 节中定义的 JSON 负载胶囊的表。在由 `EFI_JSON_CAPSULE_DATA_TABLE_GUID` 标识的表条目中报告的地址将被引用为物理地址,并且在从预引导阶段过渡到运行时阶段时不会被修复。这些由 `EFI_JSON_CONFIG_DATA_TABLE_GUID` 和 `EFI_JSON_CAPSULE_RESULT_TABLE_GUID` 标识的表条目中报告的地址将被引用为虚拟地址,并且在从预引导阶段过渡到运行时阶段时将得到修复。 453 | 454 | ```c 455 | # define EFI_JSON_CONFIG_DATA_TABLE_GUID 456 | \ 457 | { 458 | 0x87367f87, 0x1119, 0x41ce, \ 459 | { 460 | 0xaa, 0xec, 0x8b, 0xe0, 0x11, 0x1f, 0x55, 0x8a 461 | } 462 | } 463 | # define EFI_JSON_CAPSULE_DATA_TABLE_GUID \ 464 | { 465 | 0x35e7a725, 0x8dd2, 0x4cac, \ 466 | { 467 | 0x80, 0x11, 0x33, 0xcd, 0xa8, 0x10, 0x90, 0x56 468 | } 469 | } 470 | # define EFI_JSON_CAPSULE_RESULT_TABLE_GUID \ 471 | { 472 | 0xdbc461c3, 0xb3de, 0x422a,\ 473 | { 474 | 0xb9, 0xb4, 0x98, 0x86, 0xfd, 0x49, 0xa1, 0xe5 475 | } 476 | } 477 | ``` 478 | 479 | **Devicetree Tables** 480 | 481 | 以下列表显示了设备树表 (DTB) 的 GUID。有关详细信息,请参阅“设备树规范”标题下的“UEFI 相关文档的链接”()。DTB 必须包含在 EfiACPIReclaimMemory 类型的内存中。此表条目中报告的地址将被引用为物理地址,并且在从预引导阶段过渡到运行时阶段时不会被修复。固件必须将 DTB 驻留在内存中并安装在 EFI 中在执行不属于系统固件映像的任何 UEFI 应用程序或驱动程序之前检查系统表。一旦 DTB 作为配置表安装,系统固件不得对其进行任何修改或引用 DTB 中包含的任何数据。 482 | 483 | 允许 UEFI 应用程序修改或替换加载的 DTB。系统固件不得依赖于 DTB 中包含的任何数据。如果系统固件使用 DTB 进行自己的配置,它应该使用一个单独的私有副本,该副本未安装在 EFI 系统表中或以其他方式暴露给 EFI 应用程序。 484 | 485 | ```C 486 | #define CTEST_MAIN 487 | //// 设备树表,扁平化设备树 Blob (DTB) 格式// 488 | #define EFI_DTB_TABLE_GUID \ 489 | \ \ 490 | { \ 491 | 0xb1b621d5, 0xf19c, 0x41a5, \ \ 492 | { \ 493 | 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 \ 494 | } \ 495 | } 496 | ``` 497 | 498 | **EFI_RT_PROPERTIES_TABLE** 499 | 500 | 如果操作系统调用 ExitBootServices() 后平台不再支持所有 EFI 运行时服务,则该表应由平台发布。请注意,这只是对操作系统的提示,可以随意忽略,因此平台仍然需要提供不受支持的运行时服务的可调用实现,这些服务只返回 EFI_UNSUPPORTED。 501 | 502 | ```C 503 | #define EFI_RT_PROPERTIES_TABLE_GUID \ 504 | { \ 505 | 0xeb66918a, 0x7eef, 0x402a, \ 506 | { \ 507 | 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9 \ 508 | } \ 509 | } 510 | typedef struct { 511 | UINT16 Version; 512 | UINT16 Length; 513 | UINT32 RuntimeServicesSupported; 514 | } EFI_RT_PROPERTIES_TABLE; 515 | ``` 516 | 517 | - **Version**:表的版本,必须是 0x1 518 | - `#define EFI_RT_PROPERTIES_TABLE_VERSION 0x1` 519 | - **Length**:整个 EFI_RT_PROPERTIES_TABLE 的大小(以字节为单位)必须为 8。 520 | - **RuntimeServicesSupported**:支持或不支持调用的位掩码,其中位设置为 1 表示支持该调用,0 表示不支持。 521 | 522 | ```c 523 | #define EFI_RT_SUPPORTED_GET_TIME 0x0001 524 | # define EFI_RT_SUPPORTED_SET_TIME 0x0002 525 | # define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 526 | # define EFI_RT_SUPPORTED_SET_WAKEUP_TIME 0x0008 527 | # define EFI_RT_SUPPORTED_GET_VARIABLE 0x0010 528 | # define EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME 0x0020 529 | # define EFI_RT_SUPPORTED_SET_VARIABLE 0x0040 530 | # define EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP 0x0080 531 | # define EFI_RT_SUPPORTED_CONVERT_POINTER 0x0100 532 | # define EFI_RT_SUPPORTED_GET_NEXT_HIGH_MONOTONIC_COUNT 0x0200 533 | # define EFI_RT_SUPPORTED_RESET_SYSTEM 0x0400 534 | # define EFI_RT_SUPPORTED_UPDATE_CAPSULE 0x0800 535 | # define EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES 0x1000 536 | # define EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO 0x2000 537 | ``` 538 | 539 | 这种类型的 EFI 配置表条目中报告的地址将被引用为物理地址,并且在从预引导过渡到运行时阶段时不会被修复。 540 | 541 | **EFI_PROPERTIES_TABLE (deprecated)** 542 | 543 | 注意:此表已弃用,不应再使用!它将从规范的未来版本中删除。下面描述的 EFI_MEMORY_ATTRIBUTES_TABLE 提供了替代机制来实现运行时内存保护。 544 | 545 | 如果平台满足 MemoryProtectionAttributes 中列出的某些构造要求,则会发布此表。 546 | 547 | ```C 548 | typedef struct { 549 | UINT32 Version; 550 | UINT32 Length; 551 | UINT64 MemoryProtectionAttribute; 552 | } EFI_PROPERTIES_TABLE; 553 | ``` 554 | 555 | - **Version**:这是表的修订。后续版本可能会填充额外的位并增加表的长度。如果是后者,Length 字段会适当调整 556 | - #define EFI_PROPERTIES_TABLE_VERSION 0x00010000 557 | - **Length**:这是整个 EFI_PROPERTIES_TABLE 结构的大小,包括版本。初始版本的长度为 16 558 | - **MemoryProtectionAttribute**:这个字段是一个位掩码。任何未定义的位都应被视为保留位。设置位意味着底层固件已根据给定属性构建。 559 | 560 | ```C 561 | // 562 | // Memory attribute (Not defined bits are reserved) 563 | // 564 | #define EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA 0x1 565 | 566 | // BIT 0 – description – implies the runtime data is separated from the code 567 | ``` 568 | 569 | 该位意味着可执行映像的 UEFI 运行时代码和数据部分是分开的,并且必须按照第 2.3 节中的规定对齐。该位还暗示数据页没有任何可执行代码。 570 | 571 | 建议不要使用此属性,尤其是对于将运行时代码内存映射描述符分解为 UEFI 模块中的底层代码和数据部分的实现。这种拆分会导致与调用 SetVirtualAddress() 的操作系统的互操作性问题,而没有意识到这些运行时描述符之间存在关系。 572 | 573 | **EFI_MEMORY_ATTRIBUTES_TABLE** 574 | 575 | **概述** 576 | 577 | 当由系统固件发布时,EFI_MEMORY_ATTRIBUTES_TABLE 提供有关运行时内存块中区域的附加信息,这些内存块在从 `EFI_BOOT_SERVICES.GetMemoryMap()` 函数返回的 `EFI_MEMORY_DESCRIPTOR` 条目中定义。内存属性表当前用于描述可由操作系统或管理程序应用于 EFI 运行时代码和数据的内存保护。此表的使用者当前必须忽略包含除 `EfiRuntimeServicesData` 和 `EfiRuntimeServicesCode` 之外的任何类型值的条目,以确保与此表的未来使用兼容。属性表可以定义多个条目来描述子区域,这些子区域包含由 `GetMemoryMap()` 返回的单个条目,但是子区域必须合计以完全描述更大的区域,并且不能跨越 `GetMemoryMap()` 报告的条目之间的边界。如果 `GetMemoryMap()` 条目中返回的运行时区域未在内存属性表中描述,则假定该区域与任何内存保护不兼容。 578 | 579 | 只有 `GetMemoryMap()` 返回的整个 EFI_MEMORY_DESCRIPTOR 条目可以传递给 `SetVirtualAddressMap`()。 580 | 581 | 这种类型的 EFI 配置表条目中报告的地址将被引用为物理地址,并且在从预引导阶段过渡到运行时阶段时不会被修复 582 | 583 | **原型** 584 | 585 | ```C 586 | #define EFI_MEMORY_ATTRIBUTES_TABLE_GUID \ 587 | { \ 588 | 0xdcfa911d, 0x26eb, 0x469f, \ 589 | { \ 590 | 0xa2, 0x20, 0x38, 0xb7, 0xdc, 0x46, 0x12, 0x20 \ 591 | } \ 592 | } 593 | ``` 594 | 595 | 具有以下数据结构 596 | 597 | ```C 598 | /***********************************************/ 599 | /* EFI_MEMORY_ATTRIBUTES_TABLE 600 | /***********************************************/ 601 | typedef struct { 602 | UINT32 Version; 603 | UINT32 NumberOfEntries; 604 | UINT32 DescriptorSize; 605 | UINT32 Reserved; 606 | // EFI_MEMORY_DESCRIPTOR Entry [1]; 607 | } EFI_MEMORY_ATTRIBUTES_TABLE; 608 | ``` 609 | 610 | - **Version**:该表的版本。当前版本为 0x00000001 611 | - **NumberOfEntries**:提供的 EFI_MEMORY_DESCRIPTOR 条目计数。这通常是包含 UEFI 运行时和所有 UEFI 运行时数据区域(例如运行时堆)的所有 UEFI 模块中的 PE/COFF 部分的总数。 612 | - **Entry**:EFI_MEMORY_DESCRIPTOR 类型的条目数组。 613 | - **DescriptorSize**:内存描述符的大小 614 | - **Reserved**:保留字节 615 | 616 | **描述** 617 | 618 | 对于每个数组条目,`EFI_MEMORY_DESCRIPTOR.Attribute` 字段可以通知运行时机构,例如操作系统或管理程序,关于可以在内存管理单元中为该条目定义的内存进行什么级别的保护设置。当前属性字段的唯一有效位是 `EFI_MEMORY_RO`、`EFI_MEMORY_XP` 以及 `EFI_MEMORY_RUNTIME`。无论属性隐含的内存保护如何,`EFI_MEMORY_DESCRIPTOR.Type` 字段都应与封闭的 `SetMemoryMap()` 条目中的内存类型相匹配。`PhysicalStart` 必须按照第 2.3 节中的规定对齐。该列表必须按物理起始地址升序排序。`VirtualStart` 字段必须为零并被操作系统忽略,因为它对该表没有任何用处。`NumPages` 必须覆盖保护映射的整个内存区域。`EFI_MEMORY_ATTRIBUTES_TABLE` 中具有属性 `EFI_MEMORY_RUNTIME` 的每个描述符不得与 `EFI_MEMORY_ATTRIBUTES_TABLE` 中具有属性 `EFI_MEMORY_RUNTIME` 的任何其他描述符重叠。此外,`EFI_MEMORY_ATTRIBUTES_TABLE` 中的描述符描述的每个内存区域必须是 `GetMemoryMap()` 生成的表中描述符的子区域或等于。 619 | 620 | ![内存属性定义的使用](../pic/table4-1.jpg "内存属性定义的使用") 621 | 622 | **其他配置表** 623 | 624 | 以下列表显示了本规范中定义的其他配置表: 625 | 626 | - EFI_MEMORY_RANGE_CAPSULE_GUID 627 | - EFI_DEBUG_IMAGE_INFO_TABLE (Section 18.4.3) 628 | - EFI_SYSTEM_RESOURCE_TABLE (Section 23.4) 629 | - EFI_IMAGE_EXECUTION_INFO_TABLE (Section 32.5.3.1) 630 | - User Information Table (Section 36.5) 631 | - HII Database export buffer (Section 33.2.11.1, OS Runtime Utilization) 632 | 633 | ## 镜像入口点示例 634 | 635 | 以下部分中的示例显示了如何在 UEFI 环境中显示各种表示例。 636 | 637 | ### 镜像入口点示例 638 | 639 | 以下示例显示了 UEFI 应用程序的镜像入口点。此应用程序使用 EFI 系统表、EFI 引导服务表和 EFI 运行时服务表 640 | 641 | ```C 642 | EFI_SYSTEM_TABLE *gST; 643 | EFI_BOOT_SERVICES *gBS; 644 | EFI_RUNTIME_SERVICES *gRT; 645 | EfiApplicationEntryPoint(IN EFI_HANDLE ImageHandle, 646 | IN EFI_SYSTEM_TABLE *SystemTable) 647 | { 648 | EFI_STATUS Status; 649 | EFI_TIME *Time; 650 | gST = SystemTable; 651 | gBS = gST->BootServices; 652 | gRT = gST->RuntimeServices; 653 | // 654 | // Use EFI System Table to print “Hello World” to the active console output 655 | // device. 656 | // 657 | Status = gST->ConOut->OutputString(gST->ConOut, L”Hello World\n\r”); 658 | if (EFI_ERROR(Status)) { 659 | return Status; 660 | } 661 | // 662 | // Use EFI Boot Services Table to allocate a buffer to store the current time 663 | // and date. 664 | // 665 | Status = gBS->AllocatePool(EfiBootServicesData, sizeof(EFI_TIME), 666 | (VOID **)&Time); 667 | if (EFI_ERROR(Status)) { 668 | return Status; 669 | } 670 | // 671 | // Use the EFI Runtime Services Table to get the current time and date. 672 | // 673 | Status = gRT->GetTime(Time, NULL) if (EFI_ERROR(Status)) 674 | { 675 | return Status; 676 | } 677 | return Status; 678 | } 679 | ``` 680 | 681 | 以下示例显示了不遵循 UEFI 驱动程序模型的驱动程序的 UEFI 映像入口点。由于此驱动程序返回 `EFI_SUCCESS`,因此它在退出后会一直驻留在内存中。 682 | 683 | ```C 684 | EFI_SYSTEM_TABLE *gST; 685 | EFI_BOOT_SERVICES *gBS; 686 | EFI_RUNTIME_SERVICES *gRT; 687 | EfiDriverEntryPoint(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) 688 | { 689 | gST = SystemTable; 690 | gBS = gST->BootServices; 691 | gRT = gST->RuntimeServices; 692 | // 693 | // Implement driver initialization here. 694 | // 695 | return EFI_SUCCESS; 696 | } 697 | ``` 698 | 699 | 以下示例显示了不遵循 UEFI 驱动程序模型的驱动程序的 UEFI 映像入口点。由于此驱动返回 `EFI_DEVICE_ERROR`,退出后不会常驻内存。 700 | 701 | ```C 702 | EFI_SYSTEM_TABLE *gST; 703 | EFI_BOOT_SERVICES*gBS; 704 | EFI_RUNTIME_SERVICES *gRT; 705 | EfiDriverEntryPoint(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE*SystemTable) 706 | { 707 | gST = SystemTable; 708 | gBS = gST->BootServices; 709 | gRT = gST->RuntimeServices; 710 | // 711 | // Implement driver initialization here. 712 | // 713 | return EFI_DEVICE_ERROR; 714 | } 715 | ``` 716 | 717 | ### UEFI Driver Model Example 718 | 719 | 以下是一个 UEFI 驱动程序模型示例,它显示了 XYZ 总线上 ABC 设备控制器的驱动程序初始化例程。`EFI_DRIVER_BINDING_PROTOCOL` 和 `AbcSupported()`、`AbcStart()` 和 `AbcStop()` 的函数原型在 11.1 节中定义。该函数将驱动程序的镜像句柄和指向 EFI 引导服务表的指针保存在全局变量中,因此其他函数在同一个驱动程序可以访问这些值。然后它创建 `EFI_DRIVER_BINDING_PROTOCOL` 的实例并将其安装到驱动程序的镜像句柄上 720 | 721 | ```C 722 | extern EFI_GUID gEfiDriverBindingProtocolGuid; 723 | EFI_BOOT_SERVICES *gBS; 724 | static EFI_DRIVER_BINDING_PROTOCOL mAbcDriverBinding = { AbcSupported, AbcStart, 725 | AbcStop, 1, 726 | NULL, NULL }; 727 | AbcEntryPoint(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) 728 | { 729 | EFI_STATUS Status; 730 | gBS = SystemTable->BootServices; 731 | mAbcDriverBinding->ImageHandle = ImageHandle; 732 | mAbcDriverBinding->DriverBindingHandle = ImageHandle; 733 | Status = gBS->InstallMultipleProtocolInterfaces( 734 | &mAbcDriverBinding->DriverBindingHandle, &gEfiDriverBindingProtocolGuid, 735 | &mAbcDriverBinding, NULL); 736 | return Status; 737 | } 738 | ``` 739 | 740 | ### UEFI Driver Model Example (Unloadable) 741 | 742 | 下面是与上面相同的 UEFI 驱动程序模型示例,除了它还包含允许通过引导服务 `Unload()` 卸载驱动程序所需的代码。在 `AbcEntryPoint()` 中安装的任何协议或分配的内存都必须在 `AbcUnload()` 中卸载或释放。 743 | 744 | ```C 745 | extern EFI_GUID gEfiLoadedImageProtocolGuid; 746 | extern EFI_GUID gEfiDriverBindingProtocolGuid; 747 | EFI_BOOT_SERVICES *gBS; 748 | static EFI_DRIVER_BINDING_PROTOCOL mAbcDriverBinding = { AbcSupported, AbcStart, 749 | AbcStop, 1, 750 | NULL, NULL }; 751 | EFI_STATUS 752 | AbcUnload(IN EFI_HANDLE ImageHandle); 753 | AbcEntryPoint(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) 754 | { 755 | EFI_STATUS Status; 756 | EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; 757 | gBS = SystemTable->BootServices; 758 | Status = gBS->OpenProtocol(ImageHandle, &gEfiLoadedImageProtocolGuid, 759 | &LoadedImage, ImageHandle, NULL, 760 | EFI_OPEN_PROTOCOL_GET_PROTOCOL); 761 | if (EFI_ERROR(Status)) { 762 | return Status; 763 | } 764 | LoadedImage->Unload = AbcUnload; 765 | mAbcDriverBinding->ImageHandle = ImageHandle; 766 | mAbcDriverBinding->DriverBindingHandle = ImageHandle; 767 | Status = gBS->InstallMultipleProtocolInterfaces( 768 | &mAbcDriverBinding->DriverBindingHandle, &gEfiDriverBindingProtocolGuid, 769 | &mAbcDriverBinding, NULL); 770 | return Status; 771 | } 772 | EFI_STATUS 773 | AbcUnload(IN EFI_HANDLE ImageHandle) 774 | { 775 | EFI_STATUS Status; 776 | Status = gBS->UninstallMultipleProtocolInterfaces( 777 | ImageHandle, &gEfiDriverBindingProtocolGuid, &mAbcDriverBinding, NULL); 778 | return Status; 779 | } 780 | ``` 781 | 782 | ### EFI Driver Model Example (Multiple Instances) 783 | 784 | 以下与第一个 UEFI 驱动程序模型示例相同,只是它生成三个 `EFI_DRIVER_BINDING_PROTOCOL` 实例。第一个安装在驱动程序的镜像句柄上。另外两个安装在新创建的句柄上 785 | 786 | ```C 787 | extern EFI_GUID gEfiDriverBindingProtocolGuid; 788 | EFI_BOOT_SERVICES *gBS; 789 | static EFI_DRIVER_BINDING_PROTOCOL mAbcDriverBindingA = { 790 | AbcSupportedA, AbcStartA, AbcStopA, 1, NULL, NULL 791 | }; 792 | static EFI_DRIVER_BINDING_PROTOCOL mAbcDriverBindingB = { 793 | AbcSupportedB, AbcStartB, AbcStopB, 1, NULL, NULL 794 | }; 795 | static EFI_DRIVER_BINDING_PROTOCOL mAbcDriverBindingC = { 796 | AbcSupportedC, AbcStartC, AbcStopC, 1, NULL, NULL 797 | }; 798 | AbcEntryPoint(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) 799 | { 800 | EFI_STATUS Status; 801 | gBS = SystemTable->BootServices; 802 | // 803 | // Install mAbcDriverBindingA onto ImageHandle 804 | // 805 | mAbcDriverBindingA->ImageHandle = ImageHandle; 806 | mAbcDriverBindingA->DriverBindingHandle = ImageHandle; 807 | Status = gBS->InstallMultipleProtocolInterfaces( 808 | &mAbcDriverBindingA->DriverBindingHandle, 809 | &gEfiDriverBindingProtocolGuid, &mAbcDriverBindingA, NULL); 810 | if (EFI_ERROR(Status)) { 811 | return Status; 812 | } 813 | // 814 | // Install mAbcDriverBindingB onto a newly created handle 815 | // 816 | mAbcDriverBindingB->ImageHandle = ImageHandle; 817 | mAbcDriverBindingB->DriverBindingHandle = NULL; 818 | Status = gBS->InstallMultipleProtocolInterfaces( 819 | &mAbcDriverBindingB->DriverBindingHandle, 820 | &gEfiDriverBindingProtocolGuid, &mAbcDriverBindingB, NULL); 821 | if (EFI_ERROR(Status)) { 822 | return Status; 823 | } 824 | // 825 | // Install mAbcDriverBindingC onto a newly created handle 826 | // 827 | mAbcDriverBindingC->ImageHandle = ImageHandle; 828 | mAbcDriverBindingC->DriverBindingHandle = NULL; 829 | Status = gBS->InstallMultipleProtocolInterfaces( 830 | &mAbcDriverBindingC->DriverBindingHandle, 831 | &gEfiDriverBindingProtocolGuid, &mAbcDriverBindingC, NULL); 832 | return Status; 833 | } 834 | ``` -------------------------------------------------------------------------------- /src/5-GUID-Partition-Table-Disk-Layout.md: -------------------------------------------------------------------------------- 1 | # GUID Partition Table(GPT) Disk Layout 2 | 3 | ## GPT 和 MBR 磁盘布局对比 4 | 5 | 此规范定义了 GUID 分区表 (GPT) 磁盘布局(即分区方案)。下表概述了使用 GPT 磁盘布局相对于传统主引导记录 (MBR) 磁盘布局的优势 6 | 7 | - 逻辑块地址 (LBA) 是 64 位(而不是 32 位)。 8 | - 支持多个分区(而不仅仅是四个主分区)。 9 | - 提供主分区表和备份分区表以实现冗余。 10 | - 使用版本号和大小字段以供将来扩展。 11 | - 使用 CRC32 字段提高数据完整性。 12 | - 定义用于唯一标识每个分区的 GUID。 13 | - 使用 GUID 和属性来定义分区内容类型。 14 | - 每个分区包含一个 36 个字符的人类可读名称。 15 | 16 | ## LBA 0 格式 17 | 18 | 硬盘的 LBA 0(即第一个逻辑块)包含 19 | 20 | - 传统主引导记录 (MBR)(参见第 5.2.1 节) 21 | - 或保护性 MBR(参见第 5.2.3 节)。 22 | 23 | ### 传统主引导记录 (MBR) 24 | 25 | 如果传统 MBR 未使用 GPT 磁盘布局(即,如果它使用 MBR 磁盘布局),它可能位于磁盘的 LBA 0(即第一个逻辑块)。MBR 上的引导代码不由 UEFI 固件执行。 26 | 27 | | 助记符 | 字节偏移 | 字节长度 | 描述 | 28 | | :--------------------: | :------: | :---------------------: | :-----------------------------------------------------------------------------------------------------------------: | 29 | | BootCode | 0 | 424 | x86 代码在非 UEFI 系统上用于选择 MBR 分区记录并加载该分区的第一个逻辑块。此代码不应在 UEFI 系统上执行。 | 30 | | UniqueMBRDiskSignature | 440 | 4 | Unique Disk Signature 这可能被操作系统用来从系统中的其他磁盘中识别磁盘。该值始终由操作系统写入,从不由 EFI 固件写入 | 31 | | Unknown | 444 | 2 | 未知。UEFI 固件不得使用该字段。 | 32 | | PartitionRecord | 446 | 16*4 | 四个遗留 MBR 分区记录的数组(请参阅表 5-2)。 | 33 | | Signature | 510 | 2 | 设置为 0xAA55(即字节 510 包含 0x55,字节 5 11 包含 0xAA)。 | 34 | | Reserved | 512 | Logical BlockSize - 512 | 保留逻辑块的其余部分(如果有的话) | 35 | 36 | MBR 包含四个分区记录(参见表 11),每个记录定义一个分区在磁盘上使用的开始和结束 LBA 37 | | 助记符 | 字节偏移 | 字节长度 | 描述 | 38 | | :-----------: | :------: | :------: | :------------------------------------------------------------------------------------: | 39 | | BootIndicator | 0 | 1 | 0x80 表示这是可引导的传统分区。其他值表示这不是可引导的旧分区。UEFI 固件不得使用该字段 | 40 | | StartingCHS | 1 | 3 | CHS 地址格式的分区开始。UEFI 固件不得使用该字段。 | 41 | | OSType | 4 | 1 | 分区类型。请参阅第 5.2.2 节。 | 42 | | EndingCHS | 5 | 3 | CHS 地址格式的分区结束。UEFI 固件不得使用该字段。 | 43 | | StartingLBA | 8 | 4 | 磁盘上分区的起始 LBA。UEFI 固件使用此字段来确定分区的开始。 | 44 | | SizeInLBA | 12 | 4 | 以逻辑块的 LBA 为单位的分区大小。UEFI 固件使用此字段来确定分区的大小。 | 45 | 46 | 如果 MBR 分区的 OSType 字段为 0xEF(即 UEFI 系统分区),则固件必须使用 InstallProtocolInterface() 将 UEFI 系统分区 GUID 添加到 MBR 分区的句柄中。这允许驱动程序和应用程序(包括操作系统加载程序)轻松搜索代表 UEFI 系统分区的句柄。 47 | 48 | 必须执行以下测试以确定遗留 MBR 是否有效: 49 | 50 | - 签名必须是 0xaa55。 51 | - 包含零 OSType 值或零 SizeInLBA 值的分区记录可能会被忽略。 52 | 53 | 否则: 54 | 55 | - 每个 MBR 分区记录定义的分区必须物理驻留在磁盘上(即不超过磁盘的容量)。 56 | - 每个分区不得与其他分区重叠 57 | 58 | 图 5-1 显示了具有四个分区的 MBR 磁盘布局示例。 59 | 60 | ![](../pic/5-1.jpg) 61 | 62 | 相关定义: 63 | 64 | ```C 65 | #pragma pack(1) 66 | /// 67 | /// MBR Partition Entry 68 | /// 69 | typedef struct { 70 | UINT8 BootIndicator; 71 | UINT8 StartHead; 72 | UINT8 StartSector; 73 | UINT8 StartTrack; 74 | UINT8 OSIndicator; 75 | UINT8 EndHead; 76 | UINT8 EndSector; 77 | UINT8 EndTrack; 78 | UINT8 StartingLBA[4]; 79 | UINT8 SizeInLBA[4]; 80 | } MBR_PARTITION_RECORD; 81 | /// 82 | /// MBR Partition Table 83 | /// 84 | typedef struct { 85 | UINT8 BootStrapCode[440]; 86 | UINT8 UniqueMbrSignature[4]; 87 | UINT8 Unknown[2]; 88 | MBR_PARTITION_RECORD Partition[4]; 89 | UINT16 Signature; 90 | } MASTER_BOOT_RECORD; 91 | #pragma pack() 92 | ``` 93 | 94 | ### 操作系统类型 95 | 96 | 本规范定义的唯一类型(本规范未定义其他值): 97 | 98 | - 0xEF(即 UEFI 系统分区)定义了一个 UEFI 系统分区。 99 | - 0xEE(即 GPT Protective)被保护性 MBR(见 5.2.2)用来定义覆盖整个磁盘的假分区。 100 | 101 | 其他值由遗留操作系统使用,并独立于 UEFI 规范进行分配。 102 | 103 | 注意:Andries Brouwer 的“分区类型”:请参阅“MBR 磁盘布局中使用的操作系统类型值”标题下的“UEFI 相关文档链接”() 104 | 105 | ### Protective MBR 106 | 107 | 对于可引导磁盘,如果它使用 GPT 磁盘布局,则保护性 MBR 必须位于磁盘的 LBA 0(即第一个逻辑块)。保护性 MBR 在 GUID 分区表头之前,以保持与不理解 GPT 分区结构的现有工具的兼容性 108 | 109 | **Table 5-3 Protective MBR** 110 | | 助记符 | 字节偏移 | 字节长度 | 描述 | 111 | | :-----------------------: | :------: | :--------------: | :----------------------------------------------------------------------------------------------------: | 112 | | Boot Code | 0 | 440 | 未被 UEFI 系统使用 | 113 | | Unique MBR Disk Signature | 440 | 4 | 没用过。归零 | 114 | | Unknown | 444 | 2 | 没用过。设置为零。 | 115 | | Partition Record | 446 | 16*4 | 四个 MBR 分区记录的数组。包含: • 一个分区记录,如表 5-4 所定义;和 • 三个分区记录,每个记录都设置为零 | 116 | | Signature | 510 | 2 | 设置为 0xAA55(即字节 510 包含 0x55,字节 511 包含 0xAA) | 117 | | Reserved | 512 | 逻辑块大小 - 512 | 保留逻辑块的其余部分(如果有的话)。归零 | 118 | 119 | 分区记录之一应如表 12 中所定义,在保护性 MBR 本身之后为 GPT 磁盘布局保留磁盘上的整个空间。 120 | 121 | **表 5-4 保护整个磁盘的保护性 MBR 分区记录** 122 | 123 | | 助记符 | 字节偏移 | 字节长度 | 描述 | 124 | | :-----------: | :------: | :------: | :---------------------------------------------------------------------------------------------------------------------: | 125 | | BootIndicator | 0 | 1 | 设置为 0x00 以指示不可引导分区。如果设置为 0x00 以外的任何值,则此标志在非 UEFI 系统上的行为未定义。UEFI 实现必须忽略。 | 126 | | StartingCHS | 1 | 3 | 设置为 0x000200,对应 Starting LBA 字段 | 127 | | OSType | 4 | 1 | 设置为 0xEE(即 GPT 保护) | 128 | | EndingCHS | 5 | 3 | 设置为磁盘上最后一个逻辑块的 CHS 地址。如果无法表示该字段中的值,则设置为 0xFFFFFF | 129 | | StartingLBA | 8 | 4 | 设置为 0x00000001(即 GPT 分区标头的 LBA) | 130 | | SizeInLBA | 12 | 4 | 设置为磁盘大小减一。如果磁盘的大小太大而无法在此字段中表示,则设置为 0xFFFFFFFF | 131 | 132 | 剩余的分区记录应分别设置为零。 133 | 134 | 图 5-2 展示了一个 GPT 磁盘布局的例子,它有四个分区和一个保护性的 MBR。 135 | 136 | ![](../pic/5-2.jpg) 137 | 138 | 图 5-3 显示了具有四个分区的 GPT 磁盘布局示例,其中磁盘容量超过 LBA 0xFFFFFFFF。 139 | 140 | ![](../pic/5-3.jpg) 141 | 142 | ### 分区信息 143 | 144 | 在每个安装了逻辑 EFI_BLOCK_IO_PROTOTOL 的设备句柄上安装 EFI_PARTITION_INFO 协议。 145 | 146 | ## GUID 分区表 (GPT) 磁盘布局 147 | 148 | ### GPT 概述 149 | 150 | GPT 分区方案如图 5-4 所示。GPT 标头(参见第 5.3.2 节)包括一个签名和一个修订号,用于指定分区标头中数据字节的格式。GUID 分区表标头包含一个标头大小字段,用于计算确认 GPT 标头完整性的 CRC32。虽然 GPT 标头的大小在未来可能会增加,但它不能跨越设备上的多个逻辑块。 151 | 152 | LBA 0(即第一个逻辑块)包含一个保护性 MBR(参见第 5.2.3 节)。 153 | 154 | 设备上存储了两个 GPT 标头结构:主要和备份。主 GPT Header 必须位于 LBA 1(即第二个逻辑块),备份 GPT Header 必须位于设备的最后一个 LBA。在 GPT 标头中,我的 LBA 字段包含 GPT 标头本身的 LBA,备用 LBA 字段包含其他 GPT 标头的 LBA。例如,主 GPT 标头的我的 LBA 值将为 1,其备用 LBA 将为设备的最后一个 LBA 的值。备份 GPT 标头的字段将被反转。GPT 标头定义了 GPT 分区条目可用的 LBA 范围。此范围定义为包括逻辑设备上的第一个可用 LBA 到最后一个可用 LBA。存储在卷上的所有数据必须存储在第一个可用 LBA 到最后一个可用 LBA 之间,并且只有 UEFI 定义的用于管理分区的数据结构可以驻留在可用空间之外。Disk GUID 的值是唯一标识整个 GPT Header 及其所有关联存储的 GUID。该值可用于唯一标识磁盘。GPT 分区条目数组的开始位于分区条目 LBA 字段指示的 LBA 处。GUID 分区条目元素的大小在“分区条目大小”字段中定义。GPT 分区条目数组的 32 位 CRC 存储在分区条目数组 CRC32 字段的 GPT 标头中。GPT 分区条目数组的大小是分区条目大小乘以分区条目数。如果 GUID 分区条目数组的大小不是逻辑块大小的偶数倍,则最后一个逻辑块中剩余的任何空间都将保留,并且不会被分区条目数组 CRC32 字段覆盖。更新 GUID 分区条目时,必须更新分区条目数组 CRC32。当 Partition Entry Array CRC32 更新时,GPT Header CRC 也必须更新,因为 Partition Entry Array CRC32 存储在 GPT Header 中。 155 | 156 | ![GUID 分区表 (GPT) 示例](../pic/5-4.jpg "GUID 分区表 (GPT) 示例") 157 | 158 | 主 GPT 分区条目数组必须位于主 GPT 标头之后并在第一个可用 LBA 之前结束。备份 GPT Partition Entry Array 必须位于 Last Usable LBA 之后并在备份 GPT Header 之前结束。 159 | 160 | 因此,主要和备份 GPT 分区 EntryArrays 存储在磁盘上的不同位置。每个 GPT 分区条目定义一个分区,该分区包含在 GPT 标头声明的可用空间内的范围内。GPT 分区条目数组中可能正在使用零个或多个 GPT 分区条目。每个定义的分区不得与任何其他定义的分区重叠。如果 GUID 分区条目的所有字段都为零,则该条目未在使用中。必须为 GPT 分区条目数组保留至少 16,384 字节的空间。 161 | 162 | 如果块大小为 512,则 First Usable LBA 必须大于或等于 34(允许 1 块用于 Protective MBR,1 块用于 Partition Table Header,32 块用于 GPT Partition Entry Array);如果逻辑块大小为 4096,则 First Useable LBA 必须大于或等于 6(允许 1 个块用于 Protective MBR,1 个块用于 GPT Header,4 个块用于 GPT Partition Entry Array)。 163 | 164 | 设备可能会提供一个长度不是 512 字节的逻辑块大小。在 ATA 中,这称为 Long Logical Sector 功能集;ATA 设备在 IDENTIFY DEVICE 数据字 106 位 12 中报告支持此功能集,并在 IDENTIFY DEVICE 数据字 117-118 中报告每个逻辑扇区的字数(即 2 个字节)(请参阅 ATA8-ACS)。SCSI 设备在 READ CAPACITY 参数数据 Block Length In Bytes 字段中报告其逻辑块大小(请参阅 SBC-3)。设备可能呈现小于物理块大小的逻辑块大小(例如,呈现 512 字节的逻辑块大小但实现 4,096 字节的物理块大小)。在 ATA 中,这称为 Long Physical Sector 功能集;ATA 设备在 IDENTIFY DEVICE 数据字 106 位 13 中报告支持此功能集,并在 IDENTIFY DEVICE 数据字 106 位 3-0 中报告物理扇区大小/逻辑扇区大小指数比(请参阅 ATA8-ACS)。SCSI 设备在读取容量 (16) 参数数据逻辑块每个物理块指数字段(请参阅 SBC-3)中报告其逻辑块大小/物理块指数比率。这些字段返回每个物理扇区 2x 逻辑扇区(例如,3 表示 23=每个物理扇区 8 个逻辑扇区)。 165 | 166 | 实现长物理块的设备可能会呈现未与底层物理块边界对齐的逻辑块。ATA 设备在 IDENTIFY DEVICE 数据字 209(参见 ATA8-ACS)中报告物理块内逻辑块的对齐情况。SCSI 设备在 READ CAPACITY (16) 参数数据最低对齐逻辑块地址字段中报告其对齐情况(请参阅 SBC-3)。请注意,ATA 和 SCSI 字段的定义不同(例如,为了使 LBA 63 对齐,ATA 返回值 1 而 SCSI 返回值 7)。在 SCSI 设备中,Block Limits VPD 页面 Optimal Transfer Length Granularity 字段(参见 SBC-3)也可能报告对对齐目的很重要的粒度(例如,RAID 控制器可能会在该字段中返回其 RAID 条带深度)。 167 | 168 | GPT 分区应与以下较大者对齐: 169 | 170 | - a 物理块边界,如果有的话 171 | - b 最佳传输长度粒度,如果有的话。 172 | 173 | 例如: 174 | 175 | - 如果逻辑块大小为 512 字节,物理块大小为 4,096 字节(即 512 字节 x 8 个逻辑块),没有最佳传输长度粒度,并且逻辑块 0 与物理块边界对齐,则每个 GPT 分区应从 8 的倍数的 LBA 开始。 176 | - 如果逻辑块大小为 512 字节,物理块大小为 8,192 字节(即 512 字节 x 16 逻辑块),最佳传输长度粒度为 65,536 字节(即,512 字节 x 128 个逻辑块),并且逻辑块 0 与物理块边界对齐,那么每个 GPT 分区应该从 LBA 开始,该 LBA 是 128 的倍数。 177 | 178 | 为避免需要确定物理块大小和最佳传输长度粒度,软件可能会在明显更大的边界处对齐 GPT 分区。例如,假设逻辑块 0 对齐,它可以使用 2,048 的倍数的 LBA 对齐到 1,048,576 字节 (1 MiB) 的边界,这支持最常见的物理块大小和 RAID 条带大小。 179 | 180 | 参考资料如下: 181 | 182 | ISO/IEC 24739-200 [ANSI INCITS 452-2008] AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)。由 INCITS T13 技术委员会提供。 (参见“UEFI 相关文档的链接”(,标题为“国际信息技术标准委员会 (INCITS)”和“INCITs T13 技术委员会”)。 183 | 184 | ISO/IEC 14776-323 [ T10/1799-D] SCSI Block Commands - 3 (SBC-3)。可从 www.incits.org 获得。由 INCITS T10 技术委员会提供(参见“UEFI 相关文档的链接”( uefi 在“国际信息技术标准委员会 (INCITS)”和“SCSI 块命令”的标题下) 185 | 186 | ### GPT Header 187 | 188 | 表 5-5 定义 GPT Header。 189 | 190 | | 助记符 | 字节偏移 | 字节长度 | 描述 | 191 | | :----------------------: | :------: | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | 192 | | Signature | 0 | 8 | 标识 EFI 兼容的分区表头。该值必须包含 ASCII 字符串“EFI PART”,编码为 64 位常量 0x5452415020494645。 | 193 | | Revision | 8 | 4 | 此标头的修订号。此修订值与 UEFI 规范版本无关。此标头是 1.0 版,因此正确值为 0x00010000 | 194 | | HeaderSize | 12 | 4 | GPT 标头的大小(以字节为单位)。HeaderSize 必须大于或等于 92,并且必须小于或等于逻辑块大小。 | 195 | | HeaderCRC32 | 16 | 4 | GPT 标头结构的 CRC32 校验和。通过将此字段设置为 0 并计算 HeaderSize 字节的 32 位 CRC 来计算此值 s | 196 | | Reserved | 20 | 4 | 必须为零 | 197 | | MyLBA | 24 | 8 | 包含这个数据结构的 LBA | 198 | | AlternateLBA | 32 | 8 | 备用 GPT 头的 LBA 地址 | 199 | | FirstUsableLBA | 40 | 8 | GUID 分区条目描述的分区可能使用的第一个可用逻辑块 | 200 | | LastUsableLBA | 48 | 8 | GUID 分区条目描述的分区可能使用的最后一个可用逻辑块。 | 201 | | DiskGUID | 56 | 16 | 可用于唯一标识磁盘的 GUID。 | 202 | | PartitionEntryLBA | 72 | 8 | GUID 分区条目数组的起始 LBA | 203 | | NumberOfPartitionEntries | 80 | 4 | GUID 分区条目数组中的分区条目数 | 204 | | SizeOfPartitionEntry | 84 | 4 | GUID 分区条目数组中每个 GUID 分区条目结构的大小(以字节为单位)。该字段应设置为 128 x 2n 的值,其中 n 是大于或等于零的整数(例如,128、256、512 等)。注意:本规范的早期版本允许 8.. | 205 | | PartitionEntryArrayCRC32 | 88 | 4 | GUID 分区条目数组的 CRC32。从 PartitionEntryLBA 开始,计算 NumberOfPartitionEntries * SizeOfPartitionEntry 的字节长度。 | 206 | | Reserved | 92 | 4 | 块的其余部分由 UEFI 保留并且必须为零。 | 207 | 208 | 必须执行以下测试以确定 GPT 是否有效: 209 | 210 | - 检查签名 211 | - 检查标头 CRC 212 | - 检查 MyLBA 条目是否指向包含 GUID 分区表的 LBA 213 | - 检查 GUID 分区条目数组的 CRC 如果 GPT 是主表,存储在 LBA 1: 214 | - 检查 AlternateLBA 查看它是否是一个有效的 GPT 215 | 216 | 如果主 GPT 已损坏,软件必须检查设备的最后一个 LBA 以查看它是否具有有效的 GPT 标头并指向有效的 GPT 分区条目阵列。如果它指向一个有效的 GPT 分区条目数组,那么软件应该在平台策略设置允许的情况下恢复主 GPT(例如,平台可能需要用户在恢复表之前提供确认,或者可能允许自动恢复表). 217 | 218 | 软件必须在恢复 GPT 时进行报告。软件应该在恢复主要 GPT 之前询问用户确认,并且必须在它修改媒体以恢复 GPT 时报告。如果 GPT 格式的磁盘被旧版软件重新格式化为旧版 MBR 格式,最后一个逻辑块可能不会被覆盖,并且可能仍包含陈旧的 GPT。如果 GPT 识别软件随后访问磁盘并接受陈旧的 GPT,它会误解磁盘的内容。如果遗留 MBR 包含有效分区而不是保护性 MBR,软件可能会检测到这种情况(请参阅第 5.2.1 节)。 219 | 220 | 更新主 GPT 的任何软件也必须更新备份 GPT。软件可以按任何顺序更新 GPT 标头和 GPT 分区条目数组,因为所有 CRC 都存储在 GPT 标头中。软件必须在主 GPT 之前更新备份 GPT,因此如果设备大小发生变化(例如卷扩​​展)并且更新中断,则备份 GPT 位于磁盘上的正确位置。 221 | 222 | 如果主 GPT 无效,则备份而是使用 GPT,它位于磁盘上的最后一个逻辑块上。如果备份 GPT 有效,则必须使用它来恢复主 GPT。如果主 GPT 有效而备份 GPT 无效,则软件必须恢复备份 GPT。如果主 GPT 和备份 GPT 都已损坏,则此块设备被定义为没有有效的 GUID 分区标头。 223 | 224 | 在尝试增加物理卷的大小之前,主 GPT 和备份 GPT 都必须有效。这是由于 GPT 恢复方案取决于在设备末端定位备份 GPT。将磁盘添加到 RAID 设备时,卷的大小可能会增加。一旦卷大小增加,备份 GPT 必须移动到卷的末尾,并且必须更新主要和备份 GPT 标头以反映新的卷大小。 225 | 226 | ### GPT 分区条目数组 227 | 228 | GPT 分区条目数组包含一个 GPT 分区条目数组。表 5-6 定义了 GPT 分区条目。 229 | 230 | | 助记符 | 字节偏移 | 字节长度 | 描述 | 231 | | :----: | :------: | :------: | :----: | 232 | | PartitionTypeGUID | 0 | 16 | 定义此分区的用途和类型的唯一 ID。零值定义此分区条目未被使用。 | 233 | | UniquePartitionGUID | 16 | 16 | 每个分区条目唯一的 GUID。曾经创建的每个分区都有一个唯一的 GUID。创建 GPT 分区条目时必须分配此 GUID。每当 GPT 标头中的 NumberOfPartitionEntrie 增加以包含更大范围的地址时,就会创建 GPT 分区条目 | 234 | | StartingLBA | 32 | 8 | 此项定义的分区的起始 LBA。 | 235 | | EndingLBA | 40 | 8 | 此条目定义的分区的结束 LBA | 236 | | Attributes | 48 | 8 | 属性位,UEFI 保留的所有位(见表 5-8) | 237 | | PartitionName | 56 | 72 | 包含人类可读的分区名称的以空字符结尾的字符串 | 238 | | Reserved | 128 | SizeOfPartitio 239 | nEntry - 128 | GPT 分区条目的其余部分(如果有)由 UEFI 保留并且必须为零。说明 GUID 值未使用条目 00000000-0000-0000-0000-000000000000 | 240 | 241 | GPT 标头中的 SizeOfPartitionEntry 变量定义了每个 GUID 分区条目的大小。每个分区条目都包含一个唯一分区 GUID 值,该值唯一标识将要创建的每个分区。每当创建一个新的分区条目时,都必须为该分区生成一个新的 GUID,并且保证每个分区都具有唯一的 GUID。分区定义为包括 StartingLBA 和 EndingLBA 在内的所有逻辑块。 242 | 243 | PartitionTypeGUID 字段标识分区的内容。此 GUID 类似于 MBR 中的操作系统类型字段。每个文件系统都必须发布其唯一的 GUID。实用程序可以使用 Attributes 字段对分区的使用进行广泛的推断,并在表 5-7 中定义。 244 | 245 | 固件必须使用 EFI_BOOT_SERVICES.InstallProtocolInterface() 将 PartitionTypeGuid 添加到每个活动 GPT 分区的句柄。这将允许驱动程序和应用程序(包括操作系统加载程序)轻松搜索代表 EFI 系统分区或供应商特定分区类型的句柄。制作 GPT 格式磁盘和分区副本的软件必须在 GPT 标头中生成新的磁盘 GUID 值,并在每个 GPT 分区条目中生成新的唯一分区 GUID 值。如果 GPT 识别软件遇到具有相同 GUID 的两个磁盘或分区,则结果将不确定。 246 | 247 | **Table 5-7 Defined GPT Partition Entry - Partition Type GUIDs** 248 | | 描述 | GUID 值 | 249 | | :----: | :------: | 250 | | EFI System Partition | C12A7328-F81F-11D2-BA4B-00A0C93EC93B | 251 | | Partition containing a legacy MBR | 024DEE41-33E7-11D3-9D69-0008C781F39F | 252 | 253 | 操作系统供应商需要生成他们自己的分区类型 GUID 来标识他们的分区类型。 254 | 255 | **Table 5-8 Defined GPT Partition Entry - Attributes** 256 | 257 | | Bits | Name | 描述 | 258 | | :----: | :------: | :------: | 259 | | Bit 0 | Required 260 | Partition | 如果设置了此位,则平台需要分区才能运行。分区的所有者/创建者表示,删除或修改内容可能会导致平台功能丢失或平台无法启动或运行。如果删除该分区,系统将无法正常运行,应将其视为系统硬件的一部分。如果删除此分区,运行诊断、系统恢复甚至操作系统安装或引导等操作可能会停止工作。除非 OS 软件或固件识别此分区,否则不应删除或修改它,因为 UEFI 固件或平台硬件可能会变得无法正常工作。 | 261 | | Bit 1 | No Block IO 262 | Protocol | 如果设置了该位,则固件不得为该分区生成 EFI_BLOCK_IO_PROTOCOL 设备。有关详细信息,请参阅第 13.3.2 节。通过不生成 EFI_BLOCK_IO_PROTOCOL 分区,不会在 UEFI 中为该分区创建文件系统映射。 | 263 | | Bit 2 | Legacy BIOS 264 | Bootable | 此位由本规范留出,让具有传统 PC-AT BIOS 固件实现的系统通知在这些系统上运行的某些有限的专用软件 GPT 分区可能是可引导的。对于具有符合此规范的固件实现的系统,UEFI 引导管理器(请参阅第 3 章)在选择符合 UEFI 的应用程序时必须忽略此位,例如,操作系统加载程序(请参阅 2.1.3)。因此本规范无需定义该位的确切含义 | 265 | | Bits 3-47 | | 未定义且必须为零。为 UEFI 规范的未来版本扩展而保留 | 266 | | Bits 48-63 | | 为 GUID 特定用途保留。这些位的使用将因 PartitionTypeGUID 而异。只允许 PartitionTypeGUID 的所有者修改这些位。如果修改了位 0-47,则必须保留它们 | 267 | 268 | **相关定义** 269 | 270 | ```C 271 | #pragma pack(1) 272 | /// 273 | /// GPT Partition Entry. 274 | /// 275 | typedef struct { 276 | EFI_GUID PartitionTypeGUID; 277 | EFI_GUID UniquePartitionGUID; 278 | EFI_LBA StartingLBA; 279 | EFI_LBA EndingLBA; 280 | UINT64 Attributes; 281 | CHAR16 PartitionName[36]; 282 | } EFI_PARTITION_ENTRY; 283 | #pragma pack() 284 | ``` -------------------------------------------------------------------------------- /src/6-Block-Translation-Table-Layout.md: -------------------------------------------------------------------------------- 1 | # 区块转换表(BTT)布局 2 | 3 | 本规范定义了块转换表(BTT)元数据布局。以下各小节概述了在媒体上使用的 BTT 格式,涉及的数据结构,以及对 SW 如何解释 BTT 布局的详细描述。 4 | 5 | ## 区块转换表(BTT)背景 6 | 7 | 命名空间定义了非易失性存储器的连续地址范围,概念上类似于 SCSI 逻辑单元(LUN)或 NVM Express 命名空间。 8 | 9 | 任何被用于块存储的命名空间都可能包含一个块转换表(BTT),这是一个布局和一组进行块 I/O 的规则,提供单一区块的电源故障写入原子性。传统的块状存储,包括硬盘和固态硬盘,通常会防止撕裂的扇区,也就是在断电中断时部分写入的扇区。现有的软件,主要是文件系统,依赖于这种行为,往往作者没有意识到这一点。为了使这种软件能够在支持块存储访问的命名空间上正常工作,本文定义的 BTT 布局将一个命名空间细分为一个或多个 BTT 区域 (TODO),这些命名空间的大块区域包含了提供所需写入原子性的元数据。如图 6-1 和图 6-2 所示,这些 BTT A 中的每一个都包含一个元数据布局。 10 | 11 | ![BTT 区域中的 BTT 布局](../pic/6-1.jpg "BTT区域中的BTT布局") 12 | 13 | 每个区域都包含图中所示的布局。BTT 区域中的 BTT 布局,主要信息块、数据区、地图、flog 和一个备份信息块。当命名空间大于 512G 时,BTT 布局需要多个区域,如图 6-2 所示。每个使用 BTT 的命名空间都被划分为尽可能多的 512G 的区域,然后是一个更小的区域,以包含任何适当的剩余空间。最小的区域大小为 16M,所以最后的区域大小应在 16M 和 512G 之间。任何小于 16M 的剩余空间都是未使用的。由于这些区域放置规则,软件可以定位每个主要信息块和每个备份信息块,而无需读取任何元数据,仅基于命名空间大小。 14 | 15 | ![大命名空间里包含多个区域的 BTT](../pic/6-2.jpg "大命名空间里包含多个区域的BTT") 16 | 17 | ## 区块转换表(BTT)数据结构 18 | 19 | 以下各小节概述了与 BTT 布局相关的数据结构。 20 | 21 | ### BTT 信息块 22 | 23 | ```c 24 | // Alignment of all BTT structures 25 | #define EFI_BTT_ALIGNMENT 4096 26 | #define EFI_BTT_INFO_UNUSED_LEN 3968 27 | #define EFI_BTT_INFO_BLOCK_SIG_LEN 16 28 | 29 | // Constants for Flags field 30 | #define EFI_BTT_INFO_BLOCK_FLAGS_ERROR 0x00000001 31 | 32 | // Constants for Major and Minor version fields 33 | #define EFI_BTT_INFO_BLOCK_MAJOR_VERSION 2 34 | #define EFI_BTT_INFO_BLOCK_MINOR_VERSION 0 35 | 36 | typdef struct _EFI_BTT_INFO_BLOCK { 37 | CHAR8 Sig[EFI_BTT_INFO_BLOCK_SIG_LEN]; 38 | EFI_GUID Uuid; 39 | EFI_GUID ParentUuid; 40 | UINT32 Flags; 41 | UINT16 Major; 42 | UINT16 Minor; 43 | UINT32 ExternalLbaSize; 44 | UINT32 ExternalNLba; 45 | UINT32 InternalLbaSize; 46 | UINT32 InternalNLba; 47 | UINT32 NFree; 48 | UINT32 InfoSize; 49 | UINT64 NextOff; 50 | UINT64 DataOff; 51 | UINT64 MapOff; 52 | UINT64 FlogOff; 53 | UINT64 InfoOff; 54 | CHAR8 Unused[EFI_BTT_INFO_UNUSED_LEN]; 55 | UINT64 Checksum; 56 | } EFI_BTT_INFO_BLOCK 57 | ``` 58 | 59 | **Sig** 60 | 61 | BTT 索引块数据结构的签名。应为 `BTT_ARENA_INFO\0\0`。 62 | 63 | **Uuid** 64 | 65 | UUID 识别这个 BTT 实例。每次写入初始 BTT 区域时都会创建一个新的 UUID。这个值在一个命名空间的所有领域内的所有 BTT 信息块中应是相同的。 66 | 67 | **ParentUuid** 68 | 69 | 包含命名空间的 UUID,在验证 BTT 信息块时使用,以确保 BTT 布局的这个实例是为当前周围的命名空间准备的,而不是从使用媒体相同区域的前一个命名空间遗留下来的。这个值在一个命名空间的所有领域内的所有 BTT 信息块中应是相同的。 70 | 71 | **Flags** 72 | 73 | 该 BTT 信息块的布尔属性。参见下面关于标志的使用的补充说明。以下是定义的数值。 74 | `EFI_BTT_INFO_BLOCK_FLAGS_ERROR` - BTT 区域处于错误状态。当 BTT 实现发现不一致的元数据或由于不可恢复的媒体错误而丢失的元数据等问题时,相关区域的错误位应被设置。关于`EFI_BTT_INFO_BLOCK_FLAGS_ERROR`的处理,请参见`BTT Theory of Operation`部分。 75 | 76 | **Major** 77 | 78 | 主要版本号。目前为第 2 版。这个值在一个命名空间的所有区域内的所有 BTT 信息块中应是相同的。 79 | 80 | **Minor** 81 | 82 | 次要版本号。目前为第 0 版。这个值在一个命名空间的所有区域内的所有 BTT 信息块中应是相同的。 83 | 84 | **ExternalLbaSize** 85 | 86 | 公布的 LBA[^1]大小,以字节为单位。I/O请求应在这个大小的块中。这个值在一个命名空间的所有区域内的所有BTT信息块中应是相同的。 87 | [^1]: LBA(logical block address):逻辑块地址 88 | 89 | **ExternalNLba** 90 | 91 | 本区域中公布的 LBA 数量。这个字段的总和,在所有 BTT 区域中,是命名空间中可用 LBA 的总数。 92 | 93 | **InternalLbaSize** 94 | 95 | 内部 LBA 大小应大于或等于 `ExternalLbaSize`,并且不应小于 512 字节。区域数据区的每个块都是这个大小的字节,并且正好包含一个数据块。可以选择,由于 LBA 之间的对齐填充,这可能大于`ExternalLbaSize`。这个值在一个命名空间的所有区域内的所有 BTT 信息块中应是相同的。 96 | 97 | **InternalNLba** 98 | 99 | 区域数据区的内部块数。这应等于 `ExternalNLba + NFree`,因为每个内部 LBA 要么被映射到一个外部 LBA,要么在 flog(TODO) 中显示为自由。 100 | 101 | **NFree** 102 | 103 | 为写到该区域而保持的空闲块的数量。`NFree`应等于`InternalNLba-ExternalNLba`。这个值在一个命名空间的所有区域内的所有 BTT 信息块中应是相同的。 104 | 105 | **InfoSize** 106 | 107 | 该信息块的大小,以字节为单位。这个值在一个命名空间的所有区域内的所有 BTT 信息块中应是相同的。 108 | 109 | **NextOff** 110 | 111 | 相对于这个区域的起点,下一个区域的偏移量。偏移量为 0 表示当前区域后面没有区域。提供这个字段是为了方便,因为每个区域的起点可以根据命名空间的大小来计算,如 `Theory of Operation – Validating BTT Arenas at start-up` 描述中所述。该值在一个区域内的主要和备用 BTT 信息块中应是相同的。 112 | 113 | **DataOff** 114 | 115 | 相对于这个区域的起点,本区域的数据区域的偏移量。内部 LBA number zero(TODO) 指向这个偏移处。该值在一个区域内的主要和备用 BTT 信息块中应是相同的。 116 | 117 | **MapOff** 118 | 119 | 相对于这个区域的起点,本区域的地图的偏移量。该值在一个区域内的主要和备用 BTT 信息块中应是相同的。 120 | 121 | **FlogOff** 122 | 123 | 相对于这个区域的起点,本区域的 flog 偏移。该值在一个区域内的主要和备用 BTT 信息块中应是相同的。 124 | 125 | **InfoOff** 126 | 127 | 相对于这个区域的起点,本区域信息块的备份副本的偏移量。该值在一个区域内的主要和备用 BTT 信息块中应是相同的。 128 | 129 | **Reserved** 130 | 131 | 应为 0。 132 | 133 | **Checksum** 134 | 135 | 所有字段`64-bit Fletcher64`的检查和。在计算校验和时,这个字段被认为是含有零。 136 | 137 | **BTT Info Block Description** 138 | 139 | 一个有效的 BTT 信息块的存在被用来确定一个命名空间是否被用作 BTT 块设备。 140 | 141 | 每个 BTT 区域包含两个 BTT 信息块,主信息块复制到 BTT 区域的开始位置,地址偏移量为 0,最后是一个相同备份 BTT 信息块,位于区域中可用的最高块,以 `EFI_BTT_ALIGNMENT` 为边界对齐。当写入 BTT 布局时,实施方案为将信息块从最高区域写到最低区域,在写主信息块之前写出备份信息块和其他 BTT 数据结构。以这种方式写入布局应确保只有在整个布局被写入后才会检测到一个有效的 BTT 布局。 142 | 143 | ### BTT Map Entry 144 | 145 | ```c 146 | typedef struct _EFI_BTT_MAP_ENTRY { 147 | UINT32 PostMapLba : 30; 148 | UINT32 Error : 1; 149 | UINT32 Zero : 1; 150 | } EFI_BTT_MAP_ENTRY; 151 | ``` 152 | 153 | **PostMapLba** 154 | 155 | 映射后的 LBA 号码(该区域数据区的块号) 156 | 157 | **Error** 158 | 159 | 当被设置且 Zero 未被设置时,对该块的读取会返回一个错误。对该块的写操作会清除该标志。 160 | 161 | **Zero** 162 | 163 | 当设置和 Error 未设置时,对该块的读取会返回整块的零。对该块的写操作会清除该标志。 164 | 165 | **BTT Map Description** 166 | 167 | BTT Map 将索引到区域的 LBA 映射到其实际位置。BTT Map 在区域中的位置要尽可能高,在考虑到备用信息块和 flog(以及任何所需的排列)的空间后。术语*pre-map LBA*和*post-map LBA*被用来描述这种映射的输入和输出值。 168 | 169 | **Error**(TODO) 和**Zero**(TODO) 比特位表示不能同时为真的条件,所以该组合用于表示一个正常的地图 map entry(TODO:地图条目),其中没有错误或归零块的指示。只有当**Error**位被设置且**Zero**位被清除时,才会显示错误状态,零块条件的逻辑类似。当这两种情况都没有显示时,**Error**和**Zero**都被设置为表示 map entry(TODO) 处于正常、非错误状态。这就留下了**Error**和**Zero**都是零的情况,这也是 BTT 布局首次写入时所有 map entry(TODO) 的初始状态。两个位都为零意味着 map entry 包含初始身份映射,其中前映射 LBA 被映射到相同的后映射 LBA。以这种方式定义映射,允许实现方案为利用已知命名空间的初始内容为零的情况,在写布局时不需要写到映射。这可以大大改善布局时间,因为 map 是布局过程中写入的最大 BTT 数据结构。 170 | 171 | ### BTT Flog 172 | 173 | ```c 174 | // Alignment of each flog structure 175 | #define EFI_BTT_FLOG_ENTRY_ALIGNMENT 64 176 | typedef struct _EFI_BTT_FLOG { 177 | UINT32 Lba0; 178 | UINT32 OldMap0; 179 | UINT32 NewMap0; 180 | UINT32 Seq0; 181 | UINT32 Lba1; 182 | UINT32 OldMap1; 183 | UINT32 NewMap1; 184 | UINT32 Seq1; 185 | } EFI_BTT_FLOG 186 | ``` 187 | 188 | **Lba0** 189 | 190 | 最后一次使用此 flog entry(TODO) 写入 pre-map(TODO:预制图) 的 LBA。在更新 BTT map 以完成交易时,这个值被用作 BTT 地图的索引。 191 | 192 | **OldMap0** 193 | 194 | 旧的 post-map(TODO)LBA。这是 map 中的旧 entry,当最后一次使用这个 flog entry(TODO) 的写入发生时。如果交易完成,这个 LBA 现在是与这个 flog entry(TODO) 相关的自由块。 195 | 196 | **NewMap0** 197 | 198 | 新的 post-map(TODO)LBA。最后一次使用该 flog entry 的写发生时分配的块。根据定义,如果 BTT map entry 包含这个值,那么一个写交易就完成了。 199 | 200 | **Seq0** 201 | 202 | 每个 flog entry 中的 Seq0 字段被用来确定两组字段中哪一组较新(Lba0, OldMap0, NewMpa0, Seq0 vs Lba1, Oldmap1, NewMap1, Seq1)。对 flog entry 的更新应总是在较早的一组字段集进行,并应谨慎执行,以便**Seq0**位仅在已知其他字段被提交到 persistence(TODO) 后才被写入。下图显示了**Seq0**位随时间变化的进程,较新的 entry 由较旧的数值的顺时针方向表示。 203 | 204 | ![循环序列号的 Flog entrys](../pic/6-3.jpg "循环序列号的Flog entrys") 205 | 206 | **Lba1** 207 | 208 | 备用 lba entry 209 | 210 | **OldMap1** 211 | 212 | 替代旧 entry 213 | 214 | **NewMap1** 215 | 216 | 备选新 entry 217 | 218 | **Seq1** 219 | 220 | 替代序列 entry 221 | 222 | **BTT Flog Description** 223 | 224 | BTT Flog 如此命名是为了说明它既是一个自由列表,又是一个日志,被卷进了一个数据结构。Flog 的大小是由 BTT 信息块中的 **NFree** 字段决定的,它决定了有多少个这样的 flog entry。在考虑了备份信息块的空间和对齐要求后,flog 位置是场内的最高地址。 225 | 226 | ### BTT 数据区 227 | 228 | 从低位地址开始到高位,BTT 数据区在 BTT 信息块之后立即开始,一直延伸到 BTT Map 数据结构的开始。一个区域可以存储的内部数据块的数量是通过以下方式计算的:首先计算 BTT 信息块、地图和 flog(加上任何需要的对齐)所需的必要空间,从总的区域大小中减去这个数量,然后计算有多少块适合于所产生的空间。 229 | 230 | ### NVDIMM 标签协议地址抽象指南 231 | 232 | 这个版本的 BTT 布局和行为利用这个 GUID 由 `UEFI NVDIMM` 标签协议部分的 `AddressAbstractionGuid` 集体描述: 233 | 234 | ```c 235 | #define EFI_BTT_ABSTRACTION_GUID \ 236 | {0x18633bfc,0x1735,0x4217, 237 | {0x8a,0xc9,0x17,0x23,0x92,0x82,0xd3,0xf8}} 238 | ``` 239 | 240 | ## BTT 操作理论 241 | 242 | 本节概述了 BTT 的操作理论,并描述了任何软件实现应遵循的责任。 243 | 244 | BTT 布局的具体实例取决于命名空间的大小和在创建初始布局时的三个管理选择: 245 | 246 | - **ExternalLbaSize**:所需的区块大小 247 | 248 | - **InternalLbaSize**:块的大小与任何内部填充物 249 | 250 | - **NFree**:布局所支持的并发写的数量 251 | 252 | BTT 数据结构不支持小于 512 字节的**InternalLbaSize**,所以如果**ExternalLbaSize**小于 512 字节,**InternalLbaSize**应被四舍五入为 512。为了提高性能,**InternalLbaSize**可能还包括一些填充字节。例如,一个支持 520 字节块的 BTT 布局可以在内部使用 576 字节的块,以便将大小四舍五入到 64 字节高速缓存行大小的倍数。在这个例子中,对 BTT 软件上的软件来说,**ExternalLbaSize**将是 520 字节,但**InternalLbaSize**将是 576 字节。 253 | 254 | 一旦确定了上述这些管理选择,命名空间就会被划分为区域,如 BTT 区域部分所述,每个区域对**ExternalLbaSize**、**InternalLbaSize**和**Nfree**使用相同的值。 255 | 256 | ### BTT 区域 257 | 258 | 为了减少 BTT 元数据的大小,增加并发更新的可能性,命名空间中的 BTT 布局被划分为若干个区域。一个区域不能大于 512G 或小于 16M。一个命名空间被分为尽可能多的 512G 区域,从偏移量 0 开始,不需要填充就可以打包在一起,如果剩余的空间至少有 16M,那么后面还有一个小于 512G 的区域。如有必要,较小的区域大小将被四舍五入为`EFI_BTT_ALIGNMENT`的倍数。由于这些规则,命名空间中每个 BTT Arena 的位置和大小都可以从命名空间的大小中确定。 259 | 260 | 在一个区域内,用于 Flog 的空间量是每个 Flog entry 所需空间量的**NFree**倍。Flog entry 应在 64 字节的边界上对齐。 261 | 262 | 此外,完整的 BTT Flog 表应在**EFI_BTT_ALIGNMENT**边界上对齐,其大小被填充为**EFI_BTT_ALIGNMENT**的倍数。 263 | 264 | ```c 265 | FlogSize = roundup(NFree * roundup(sizeof(EFI_BTT_FLOG), 266 | EFI_BTT_FLOG_ENTRY_ALIGNMENT), EFI_BTT_ALIGNMENT) 267 | ``` 268 | 269 | 在一个区域内,数据块和相关 Map 的可用空间是区域大小减去用于 BTT 信息块和 Flog 的空间。 270 | 271 | ```c 272 | DataAndMapSize = ArenaSize – 2 * sizeof(EFI_BTT_INFO_BLOCK) - FlogSize 273 | ``` 274 | 275 | 在一个区域内,数据块的数量是通过可用空间**DataAndMapSize**除以**InternalLbaSize**再加上每个块所需的 Map 开销来计算的,并对结果进行四舍五入以确保数据区在**EFI_BTT_ALIGNMENT**边界上对齐。 276 | 277 | ```c 278 | InternalNLba = (DataAndMapSize – EFI_BTT_ALIGNMENT) / (InternalLbaSize + 279 | sizeof(EFI_BTT_MAP_ENTRY) 280 | ``` 281 | 282 | 在知道**InternalNLba**值的情况下,计算外部 LBA 的数量时要减去未公布的自由块池的**NFree**。 283 | 284 | ```c 285 | ExternalNLba = InternalNLba – Nfree 286 | ``` 287 | 288 | 在一个区域内,BTT Map 所需的字节数为每个外部 LBA 的一个 entry,加上为保持整个 Map**EFI_BTT_ALIGNMENT**对齐所需的任何对齐方式。 289 | 290 | ```c 291 | MapSize = roundup(ExternalNLba * sizeof(EFI_BTT_MAP_ENTRY), 292 | EFI_BTT_ALIGNMENT) 293 | ``` 294 | 295 | 一个区域允许的并发写的数量是基于 BTT 布局时选择的**NFree**值。例如,选择 256 的**NFree**意味着 BTT 区域将有 256 个空闲块用于 inflight(TODO) 的写入操作。由于 BTT 区域中每个都有**NFree**空闲块,当有多个区域,且写入分散在多个区域之间时,一个命名空间允许的并发写入数量可能更大。 296 | 297 | ### 区域中数据块的原子性 298 | 299 | BTT 的主要原因是为了在写入数据块时提供故障原子性,因此对单个数据块的任何写入都不会被断电等中断情况所撕裂。BTT 通过维护一个空闲块池来提供这种服务,这些空闲块不属于向 BTT 软件之上的软件层公布的容量。BTT 数据区足够大,可以容纳广告中的容量以及自由块池。BTT 软件将 BTT 数据区的块作为内部*LBAs*的列表进行管理,这些块的编号只在 BTT 软件内部可见。构成广告容量的块编号被称为外部*LBAs*,在任何给定的时间点,这些外部*LBAs*中的每一个都被 BTT Map 映射到 BTT 数据区中的一个块。BTT 软件所做的每一个区块写入都是从分配一个空闲区块开始,将数据写入其中,只有当该区块完全 persistent(TODO)(包括任何需要的刷新)时,才会采取步骤使该区块处于活动状态,如`BTT Theory of Operations – Write Path`部分所述。 300 | 301 | BTT Flog(自由列表和日志的组合)是写块时原子更新的核心。在 BTT Flog 的 "安静 "状态下,当没有 in-flight(TODO) 中的写操作发生,也没有未完成的恢复步骤时,当前可用于写操作的**NFree**块包含在 Flog entries 的 OldMap 字段中。写入应使用这些 Flog entries 之一来寻找一个空闲的块来写入,然后 Flog 中的 Lba 和 NewMap 字段在写入的数据部分完成后被用作 BTT 地图更新的写前日志,如在`Validating the Flog at start-up`部分所述。 302 | 303 | 由 BTT 软件的运行时逻辑来确保一次只使用一个 Flog entry,并且在开始使用该区块的写之前,仍在执行 OldMap entry 所指示的区块上的任何读都已完成。 304 | 305 | ### BTT 数据结构的原子性 306 | 307 | 可字节寻址的 persistent media(TODO) 可能不支持大于 8 字节的原子更新,因此 BTT 中任何大于 8 字节的数据结构都使用软件实现的原子性进行更新。请注意,8 字节写的原子性,即 8 字节存储到 persistent media(TODO) 不会被中断,如断电,是使用本文件中描述的 BTT 的最低要求。 308 | 309 | 在 BTT 中,有四种数据结构: 310 | 311 | - BTT 信息块 312 | 313 | - BTT Map 314 | 315 | - BTT Flog 316 | 317 | - BTT 数据区 318 | 319 | BTT Map entrys 的大小为 4 字节,因此可以用一条存储指令原子式地更新。所有其他的数据结构都是按照本文描述的规则进行更新的,首先更新数据结构的非活动版本,然后是使其原子化的步骤。 320 | 321 | 对于 BTT 信息块来说,原子性是通过总是先写备份信息块来提供的,只有在该更新完全 persistent(TODO) 之后(块的校验正确),主 BTT 信息块才会按照`Writing the initial BTT layout`部分的描述进行更新。 322 | 323 | 对于 BTT Flog,每个 entry 都是双倍大小,每个字段都有两个完整的副本(Lba, OldMap, NewMap, Seq)。活跃的 entry 有更高的序列号,所以更新总是写到不活跃的字段,一旦这些字段完全 persistent,不活跃的 entry 的 Seq 字段就会被更新,使其成为活跃的 entry,这是原子性的。这将在`Validating the Flog at start-up`一节中描述。 324 | 325 | 对于 BTT 数据区,所有的区块写入可以被认为是分配写入,其中一个不活动的区块是从 Flog 维护的空闲列表中选择的,只有在写入该区块的新数据完全 persistent 之后,该区块才会通过更新 Flog 和 Map entry,如`Write Path`部分所述,以原子方式变成活动的。 326 | 327 | ### 编写初始 BTT 布局 328 | 329 | BTT 的整体布局依赖于这样一个事实:除了最后一个区域最小为 16MB 外,所有区域的大小都应是 512GiB。在一个 BTT 的生命周期中,初始化 BTT 的 on-media(TODO) 结构只发生一次,即在它被创建时。这个序列假设软件已经确定需要创建新的 BTT 布局,并且已知命名空间的总原始大小。 330 | 331 | 在创建一个新的 BTT 布局之前,周围命名空间的 UUID 可以被更新为新生成的 UUID。根据 BTT 软件实施的需要,这一可选的步骤具有使命名空间中任何先前的 BTT 信息块无效的效果,并确保在 BTT 布局创建过程被中断时检测到无效的布局。因为父 UUID 字段,所以这种检测是有效的。 332 | 333 | BTT 布局中的 on-media(TODO) 结构可以按任何顺序写出,但 BTT 信息块除外,BTT 信息块应作为布局的最后一步写出,从最后一个 arena(命名空间中的最高偏移量)开始到第一个 arena(命名空间中的最低偏移量),首先在每个 arena 中写入备份 BTT 信息块,然后为该 arena 写入主 BTT 信息块。这允许在执行`Validating BTT Arenas at start-up`部分的算法时检测出不完整的 BTT 布局。 334 | 335 | 由于一个区域内部 LBA 数量超过了**NFree**的外部 LBA 数量,所以有足够的内部 LBA 数量来完全初始化 BTT Map 以及 BTT Flog,其中 BTT Flog 用**NFree**最高的内部 LBA 数量初始化,其余的用于 BTT Map。 336 | 337 | 每个竞技场中的 BTT Map 被初始化为零。地图中的零 entry 表示所有 pre-map 的 LBAs 与相应的 post-map 的 LBAs 的身份映射。这使用了所有的内部 LBA,只有**NFree**的 LBA,留下**Nfree**的 LBA 供 BTT Flog 使用。 338 | 339 | 每个区域的 BTT Flog 的初始化方法是:整个 flog 区域从所有的零开始,将每个 flog 条目中的 Lba0 字段设置为唯一的 pre-map LBA,从 0 到**Free-1**,每个 flog entry 中的**OldMap0**和**NewMap0**字段都设置为剩余的内部 LBA 之一。 340 | 例如,flog entry 0 会将 Lba0 设置为 0,**OldMap0**和**NewMap0**都设置为 Map 中没有代表的第一个内部 LBA(因为 Map 中存在**ExternalNLba**entry,下一个可用的内部 LBA 等于**ExternalNLba**)。 341 | 342 | ### 启动时验证 BTT 区域 343 | 344 | 当软件准备访问命名空间中的 BTT 布局时,第一步是检查 BTT Arenas 的一致性。读取和验证 BTT 区域时依赖于这样一个事实,即所有区域的大小应是 512GiB,除了最后一个竞技场是最小的 16MiB。 345 | 346 | 在软件认为 BTT 布局有效之前,必须通过以下测试: 347 | 348 | - 对于每一个 BTT 区域 349 | 350 | - 读取和验证主 BTT 信息块 351 | 352 | - 如果对主 BTT 信息块读取失败,则转到读取和验证备份 BTT 信息块 353 | 354 | - 如果主 BTT 信息块包含一个不正确的**Sig**字段,则为无效,转到读取和验证备用的 BTT 信息块 355 | 356 | - 如果主 BTT 信息块的父级 Uuid 字段与周围命名空间的 UUID 不匹配,则转到读取和验证备用的 BTT 信息块 357 | 358 | - 如果主 BTT 信息块包含一个不正确的 Checksum(校验和),则为无效,转到读取和验证备用的 BTT 信息块 359 | 360 | - 主 BTT 信息块有效。使用**NextOff**字段找到下一个区域的开始,继续 BTT 信息块的验证,转到读取和验证备用的 BTT 信息块 361 | 362 | - 读取和验证备份 BTT 信息块 363 | 364 | - 确定备份 BTT 信息块的位置 365 | 366 | 1. 所有区域都应是完整的 512G 数据区大小,除了最后一个区域至少是 16MB 367 | 368 | 2. 备份 BTT 信息块是区域中最后一个`EFI_BTT_ALIGNMENT`对齐的块 369 | 370 | - 如果在 BTT 区域的高地址上读取备份 BTT 信息块失败,neither copy could be read(则两个副本都无法读取),软件应假定命名空间没有有效的 BTT 元数据布局 371 | 372 | - 如果备份的 BTT 信息块父级 Uuid 字段与周围命名空间的 UUID 不匹配,则为无效,软件应假定该命名空间没有有效的 BTT 元数据布局。 373 | 374 | - 如果备份的 BTT 信息块包含一个不正确的 Checksum(校验和),则为无效,软件应假定命名空间没有有效的 BTT 元数据布局 375 | 376 | - 备份 BTT 信息块是有效的。由于主副本是坏的,软件应将有效的备份 BTT 信息块的内容复制到主 BTT 信息块中,然后才能成功地完成对所有区域的 BTT 信息块的验证 377 | 378 | ### 启动时验证 Flog Entry 379 | 380 | 在按照`Validating BTT Arenas at start-up`一节所述验证 BTT 信息块后,软件应采取的下一步是验证 BTT Flog entry。当数据块被写入时,如下面`Write Path`部分所述,persistent(持久的)Flog 和 Map 状态不会被更新,直到空闲块被写入新数据。将部分写入的数据保留在一个空闲的块中,这确保了数据传输过程中任何时候的电源故障都是无害的。一旦 Flog 被更新(通过 Flog entry 中的**Seq**位使之成为原子),写入算法就致力于更新,从写入流程中的这一点开始,电源故障应通过在恢复时完成对 BTT Map 的更新来处理。Flog 包含完成 Map entry 更新所需的所有信息。 381 | 382 | 请注意,这里概述的 Flog entry 的恢复是为了在不活动的 BTT 上单线程发生(在 BTT 块命名空间被允许接受 I/O 请求之前)。恢复所需的最大时间由**NFree**决定,但对于发现的每个不完整的写,只需要几次加载和一次存储(以及相应的缓存刷新)。 383 | 384 | 针对每个区域中的每个 flog entry 执行以下步骤,以恢复任何中断的写入,并验证 flog entry 在启动时是否一致。在这些步骤中发现的任何一致性问题都会导致为区域设置错误状态 (`EFI_BTT_INFO_BLOCK_FLAGS_ERROR`) 并终止该区域的 flog 验证过程。 385 | 386 | 1. 针对 flog entry 检查**Seq0**和**Seq1**字段。如果两个字段都为零,或者两个字段彼此相等,则 flog entry 不一致。否则,较高的 Seq 字段表明在接下来的步骤中使用哪一组 flog 字段 (**Lba0**, **OldMap0**, **NewMap0**与**Lba1**, **OldMap1**, **NewMap1**)。从本节的这一点开始,所选字段被称为**Lba**、**OldMap**和**NewMap** 387 | 388 | 2. 如果**OldMap**和**NewMap**相等,这就是一个自 BTT 的初始布局创建以来从未使用过的 Flog entry; 389 | 390 | 3. **Lba**字段被检查以确保它是一个有效的 pre-map(预映射) LBA(范围在 0 到**External NLba 1**)。如果检查失败,flog entry 就不一致了; 391 | 392 | 4. 获取对应 Flog entry Lba 字段中的 BTT Map Entry。由于 Map 中可包含特殊的 Zero Entry 以指示身份映射,因此当遇到零时,将获取的 Entry 调整为相应的内部 LBA(通过将 Entry 解释为与 Flog Entry LBA 字段相同的 LBA); 393 | 394 | 5. 如果上一步调整后的 Map Entry 与 Flog Entry 中的 NewMap 字段不匹配,并且与 OldMap 字段匹配,则检测到 BTT Map 更新中断。恢复步骤是将 NewMap 字段写入 Flog entry Lba 字段索引的 BTT Map entry。 395 | 396 | ### 读取路径 397 | 398 | 以下高级序列描述了在使用 BTT 时读取单个数据块的步骤,如图所示:BTT 读取路径概述如下: 399 | 400 | 1. 如果在 Arena 的 BTT 信息块中设置了 `EFI_BTT_INFO_BLOCK_FLAGS_ERROR`,BTT 软件可能会返回读取错误,或者实现可能会选择继续提供只读访问并继续这些步骤; 401 | 402 | 2. 使用随读取操作提供的外部 LBA 来确定要访问的 BTT Arena。从第一个 Arena(命名空间中的最低偏移量)开始,依次循环遍历 Arena,BTT 信息块中的 **ExternalNLba**字段描述了该区域中有多少外部 LBA。一旦确定了正确的 Arena,就从提供的 LBA 中减去包含在较低、跳过的 Arena 中的外部 LBA,以获得所选 Arena 的 pre-map LBA; 403 | 404 | 3. 使用 pre-map LBA 索引到 Arena 的 BTT Map 并获取 Map Entry; 405 | 406 | 4. 如果在映射条目中设置了零位和错误位,则这表示正常 Entry。Map Entry 中的**PostMapLba**字段用于通过将其乘以**InternalLbaSize**并将结果添加到来自 Arena 的 BTT 信息块的 **DataOff** 字段来索引到 Arena 数据区域。这提供了数据在 Arena 中的位置,然后软件将**ExternalLbaSize**字节复制到提供的缓冲区中以满足读取请求; 407 | 408 | 5. 否则,如果仅在 Map Entry 中设置了错误位,则返回读取错误; 409 | 410 | 6. 否则,如果在 Map Entry 中仅设置了零位,则会将一块**ExternalLbaSize**字节的零复制到提供的缓冲区中以满足读取请求; 411 | 412 | 7. 最后,如果零位和错误位都被清除,这表示初始标识映射,并且 Pre-Map(预映射)LBA 用于通过将其乘以 **InternalLbaSize** 并将结果添加到竞技场 BTT 的 **DataOff**字段来索引到 Arena 数据区域信息块。这提供了数据在 Arena 中的位置,然后软件将 **ExternalLbaSize** 字节复制到提供的缓冲区中以满足读取请求。 413 | 414 | ![BTT Read Path Overview](../pic/6-4.jpg) 415 | 416 | ### 写入路径 417 | 418 | 以下高级序列描述了在使用 BTT 时写入单个数据块的步骤,如下图所示:BTT 写入路径概述: 419 | 420 | 1. 如果在 Arena 的 BTT 信息块中设置了`EFI_BTT_INFO_BLOCK_FLAGS_ERROR`,则 BTT 软件将返回写入错误; 421 | 422 | 2. 使用随写入操作提供的外部 LBA 来确定要访问的 BTT Arena。从第一个 Arena(命名空间中的最小偏移)开始,依次循环遍历 Arena,BTT 信息块中的**ExternalNLba**字段描述了该区域中有多少外部 LBA。一旦识别出正确的 Arena,就从提供的 LBA 中减去包含在较低、跳过的 Arena 中的外部 LBA,以获得所选 Arena 的地图前 LBA。 423 | 424 | 3. BTT 软件在 Arena 中分配一个 Flog Entry 用于此写入。多个并发写入不能共享 Flog Entry。管理 Flog Entry 的独占使用的确切方法取决于 BTT 软件实现。媒体上没有关于 Flog Entry 当前是否分配给写入请求的指示。请注意,**OldMap** 字段中的 Flog Entry 跟踪的空闲块可能仍然有来自运行在其上的相对较慢的线程的读取。BTT 软件实施应确保在进行下一步之前已完成任何此类读取。 425 | 426 | 4. 在接下来的三个步骤中,锁定对与 Pre-Map(预映射)LBA 相关联的 BTT Map 区域的访问。锁定的粒度取决于实现;一个实现可以选择锁定单个 Map Entry、锁定整个 BTT Map 或介于两者之间的东西。 427 | 428 | 5. 使用 Pre-Map LBA 索引到 Arena 的 BTT Map 并获取旧地图 Entry。 429 | 430 | 6. 通过写入非活动的 Flog 字段集(较低的 Seq 编号)来更新 Flog Entry。首先,分别用 Pre-Map LBA、旧 Map Entry 和上面选择的空闲块更新 **LBA**、**OldMap** 和 **NewMap** 字段。一旦这些字段完全持久化(完成任何所需的刷新),Seq 字段就会更新,以使新字段处于活动状态。**Seq** 字段的此更新提交写入 - 在此更新之前,如果操作中断,则写入不应发生。在 **Seq** 字段更新后,即使操作中断,也应进行写入,因为下一步中的 Map 更新将在启动时发生的 BTT 恢复期间进行。 431 | 432 | 7. 使用上面选择的空闲块更新 Map Entry。 433 | 434 | 8. 删除在上述步骤 4 中获取的地图锁。写请求现在得到满足。 435 | 436 | ![BTT Write Path Overview](../pic/6-5.jpg) 437 | -------------------------------------------------------------------------------- /src/8-Services-Runtime-Services.md: -------------------------------------------------------------------------------- 1 | # 运行时服务 2 | 3 | 本节讨论兼容系统中存在的基本服务。这些服务由在 EFI 环境中运行的代码可以使用的接口函数定义。此类代码可能包括管理设备访问或扩展平台功能的协议,以及在预引导环境和 EFI OS 加载程序中运行的应用程序。 4 | 5 | 此处描述了两种类型的服务: 6 | 7 | - 引导服务。在成功调用 `EFI_BOOT_SERVICES.ExitBootServices()` 之前可用的函数。这些功能在第 7 节中描述。 8 | 9 | - 运行时服务。在任何调用 `ExitBootServices()` 之前和之后可用的函数。这些功能在本节中描述。 10 | 11 | 在引导期间,系统资源由固件拥有,并通过引导服务接口函数进行控制。这些功能可以描述为“全局”或“基于句柄”。术语“全局”仅表示功能访问系统服务并且在所有平台上都可用(因为所有平台都支持所有系统服务)。术语“基于句柄”意味着该函数访问特定设备或设备功能,并且在某些平台上可能不可用(因为某些设备在某些平台上不可用)。协议是动态创建的。本节讨论“全局”功能和运行时功能;随后的部分讨论“基于句柄”。 12 | 13 | UEFI 应用程序(包括 UEFI 操作系统加载程序)必须使用引导服务功能来访问设备和分配内存。进入时,图像会提供一个指向系统表的指针,该表包含引导服务分派表和用于访问控制台的默认句柄。所有引导服务功能都可用,直到 UEFI 操作系统加载程序加载了足够多的自身环境以控制系统的持续运行,然后通过调用 `ExitBootServices()` 终止引导服务。 14 | 15 | 原则上,`ExitBootServices()` 调用旨在供操作系统使用,以指示其加载程序已准备好接管平台和所有平台资源管理的控制权。因此,到目前为止,引导服务可用以协助 UEFI OS 加载程序准备引导操作系统。一旦 UEFI 操作系统加载程序控制了系统并完成了操作系统启动过程,就只能调用运行时服务。但是,UEFI 操作系统加载程序以外的代码可能会也可能不会选择调用 `ExitBootServices()`。这种选择可能部分取决于此类代码是否旨在继续使用 EFI 引导服务或引导服务环境。 16 | 17 | 本节的其余部分将讨论各个函数。运行时服务分为以下几类: 18 | 19 | - 运行时规则和限制(第 8.1 节) 20 | 21 | - 可变服务(第 8.1.1 节) 22 | 23 | - 时间服务(第 8.3 节) 24 | 25 | - 虚拟内存服务(第 8.4 节) 26 | 27 | - 杂项服务(第 8.5 节) 28 | 29 | ## 运行时服务规则和限制(Runtime Services Rules and Restrictions) 30 | 31 | 如果需要,可以在启用中断的情况下调用所有运行时服务。当需要保护对硬件资源的访问时,运行时服务功能将在内部禁用中断。 32 | 33 | 对关键硬件资源的访问完成后,中断使能控制位将返回其入口状态。 34 | 35 | 运行时服务的所有调用者都被限制在先前运行时服务调用完成和返回之前调用相同或某些其他运行时服务功能。这些限制适用于: 36 | 37 | - 被中断的运行时服务; 38 | 39 | - 在另一个处理器上处于活动状态的运行时服务。 40 | 41 | 如表 8-1(重新进入运行时服务的规则)中所述,调用者被禁止在中断后使用来自另一个处理器或同一处理器的某些其他服务。对于此表,“忙碌”定义为运行时服务已进入且尚未返回给调用者时的状态。 42 | 43 | 调用者违反这些限制的后果是未定义的,除了下面描述的某些特殊情况。 44 | 45 | ![table8-1](../pic/table8-1.jpg) 46 | 47 | 如果操作系统在运行时不支持使用任何 `EFI_RUNTIME_SERVICES` 调用,则应发布 `EFI_RT_PROPERTIES_TABLE` 配置表,描述在运行时支持哪些运行时服务(请参阅第 4.6 节)。请注意,这只是对操作系统的提示,可以随意忽略,因此平台仍然需要提供不受支持的运行时服务的可调用实现,这些服务只返回 `EFI_UNSUPPORTED`。 48 | 49 | ### Machine Check, INIT and NMI [^1] 异常(Exception for Machine Check, INIT and NMI) 50 | 51 | ^1: NMI(Non-Maskable Interrupt):不可屏蔽中断 52 | 53 | 某些异步事件(例如,`IA-32` 和 `x64` 系统上的 NMI、`Itanium` 系统上的 Machine Check 和 INIT)无法被屏蔽,并且可能在启用任何中断设置的情况下发生。这些事件还可能需要 OS 级处理程序的参与,这可能涉及调用某些运行时服务(见下文)。 54 | 55 | 如果调用了 `SetVirtualAddressMap()`,则在 Machine Check、INIT 或 NMI 之后对运行时服务的所有调用都必须使用该调用设置的虚拟地址映射进行。 56 | 57 | Machine Check 可能中断了运行时服务(见下文)。如果 OS 确定 Machine Check 是可恢复的,则 OS 级别的处理程序必须遵循表 8-1 中的正常限制。 58 | 59 | 如果 OS 确定 Machine Check 是不可恢复的,则 OS 级别的处理程序可能会忽略正常限制,并且可能会调用表 8-2(Machine Check、INIT 和 NMI 之后可能调用的函数)中描述的运行时服务,即使在先前调用繁忙的情况下也是如此。系统固件将接受新的运行时服务调用,并且不保证先前中断的调用的操作。任何中断的运行时函数都不会重新启动。 60 | 61 | INIT 和 NMI 事件遵循相同的限制。 62 | 63 | 注:在 `Itanium` 系统上,操作系统机器检查处理程序不得调用 `ResetSystem()`。如果需要重置,操作系统机器检查处理程序可能会请求 `SAL` [^2] 在返回到 `SAL_CHECK` 时重置。 64 | 65 | ^2: SAL(System Abstraction Layer):系统抽象层 66 | 67 | 平台实现需要清除任何正在进行的运行时服务,以便操作系统处理程序能够调用这些运行时服务,即使在先前的调用繁忙的情况下也是如此。在这种情况下,不能保证原来中断的呼叫的正常运行。 68 | 69 | ![table8-2](../pic/table8-2.jpg) 70 | 71 | ## 可变服务(Variable Services) 72 | 73 | 变量被定义为键/值对,由标识信息加上属性(键)和任意数据(值)组成。变量旨在用作存储在平台中实现的 EFI 环境与 EFI 操作系统加载程序和在 EFI 环境中运行的其他应用程序之间传递的数据的一种方式。 74 | 75 | 虽然本规范没有定义变量存储的实现,但在大多数情况下变量必须是持久的。这意味着平台上的 EFI 实现必须对其进行安排,以便在每次系统启动时保留并可供使用,至少在它们被明确删除或覆盖之前,传递给存储的变量都可以使用。在某些平台上提供这种类型的非易失性存储可能非常有限,因此在无法使用其他通信信息方式的情况下应谨慎使用变量。 76 | 77 | 表 8-3 列出了本节中描述的变量服务函数: 78 | 79 | ![table8-3](../pic/table8-3.jpg) 80 | 81 | **GetVariable()** 82 | 83 | - 概要(Summary) 84 | 85 | 返回变量的值。 86 | 87 | - 原型(Prototype) 88 | 89 | ```c 90 | typedef 91 | EFI_STATUS GetVariable ( 92 | IN CHAR16 *VariableName, 93 | IN EFI_GUID *VendorGuid, 94 | OUT UINT32 *Attributes OPTIONAL, 95 | IN OUT UINTN *DataSize, 96 | OUT VOID *Data OPTIONAL 97 | ); 98 | ``` 99 | 100 | - 参数(Parameters) 101 | 102 | *VariableName*:一个以 `Null` 结尾的字符串,它是供应商变量的名称。 103 | 104 | *VendorGuid*:供应商的唯一标识符。`EFI_GUID` 类型在 `EFI_BOOT_SERVICES.InstallProtocolInterface()` 函数描述中定义。 105 | 106 | *Attributes*:如果不是 `NULL`,则指向内存位置的指针以返回变量的属性位掩码。参见“相关定义”。如果不为 `NULL`,则在返回 `EFI_SUCCESS` 和 `EFI_BUFFER_TOO_SMALL` 时都在输出上设置 *Attributes*。 107 | 108 | *DataSize*:输入时,返回 *data* 缓冲区的大小(以字节为单位)。输出数据中返回的 *data* 大小。 109 | 110 | *Data*:返回变量内容的缓冲区。可以为 `NULL` 且 *DataSize* 为零,以确定所需的缓冲区大小。 111 | 112 | - 相关定义(Related Definitions) 113 | 114 | ```c 115 | //******************************************************* 116 | // Variable Attributes 117 | //******************************************************* 118 | #define EFI_VARIABLE_NON_VOLATILE 0x00000001 119 | #define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 120 | #define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 121 | #define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008 \ 122 | 123 | //This attribute is identified by the mnemonic 'HR' elsewhere 124 | //in this specification. 125 | #define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 126 | //NOTE: EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated 127 | //and should be considered reserved. 128 | #define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS \ 0x00000020 129 | #define EFI_VARIABLE_APPEND_WRITE 0x00000040 130 | #define EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS 0x00000080 131 | //This attribute indicates that the variable payload begins 132 | //with an EFI_VARIABLE_AUTHENTICATION_3 structure, and 133 | //potentially more structures as indicated by fields of this 134 | //structure. See definition below and in SetVariable(). 135 | ``` 136 | 137 | - 描述(Description) 138 | 139 | 每个供应商都可以通过使用唯一的 *VendorGuid* 创建和管理自己的变量,而不会出现名称冲突的风险。设置变量时,将提供其 *Attributes* 以指示系统应如何存储和维护数据变量。这些属性会影响何时可以访问变量和数据的易变性。如果 `EFI_BOOT_SERVICES.ExitBootServices()` 已经执行,没有设置 `EFI_VARIABLE_RUNTIME_ACCESS` 属性的数据变量将对 `GetVariable()` 不可见,并将返回 `EFI_NOT_FOUND` 错误。 140 | 141 | 如果数据缓冲区太小无法容纳变量的内容,则返回错误 `EFI_BUFFER_TOO_SMALL` 并将 *DataSize* 设置为获取数据所需的缓冲区大小。 142 | 143 | `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 和 `EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS` 属性都可以在 `GetVariable()` 调用的返回属性位掩码参数中设置,但应注意 `EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS` 属性已弃用,不应再使用。`EFI_VARIABLE_APPEND_WRITE` 属性永远不会在返回的 *Attributes* 位掩码参数中设置。 144 | 145 | 当调用 `GetVariable()` 时,使用 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性集存储的变量将返回元数据以及变量数据。如果 `GetVariable()` 调用指示设置了此属性,则必须根据元数据标头解释 `GetVariable()` 有效负载。除了 `SetVariable()` 中描述的标头之外,以下标头用于指示当前可能与变量关联的证书。 146 | 147 | ```c 148 | // 149 | // EFI_VARIABLE_AUTHENTICATION_3_CERT_ID descriptor 150 | // 151 | // An extensible structure to identify a unique x509 cert 152 | // associated with a given variable 153 | // 154 | #define EFI_VARIABLE_AUTHENTICATION_3_CERT_ID_SHA256 1 155 | 156 | typedef struct { 157 | UINT8 Type; 158 | UINT32 IdSize 159 | // UINT8 Id[IdSize]; 160 | } EFI_VARIABLE_AUTHENTICATION_3_CERT_ID; 161 | ``` 162 | 163 | *Type*:标识返回的 ID 类型以及应如何解释 ID。 164 | 165 | *IdSize*:指示结构中此字段后面的 Id 缓冲区的大小。 166 | 167 | *Id(Not a formal structure member)*:这是 *Type* 字段定义的关联证书的唯一标识符。对于 `CERT_ID_SHA256`,缓冲区将是证书的 tbsCertificate(x509 中定义的待签名证书数据)数据的 SHA-256 摘要。 168 | 169 | 当设置 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性时,数据缓冲区应解释如下: 170 | 171 | ```c 172 | // NOTE: “||” indicates concatenation. 173 | 174 | // Example: EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE 175 | EFI_VARIABLE_AUTHENTICATION_3 || EFI_TIME || EFI_VARIABLE_AUTHENTICATION_3_CERT_ID || Data 176 | 177 | // Example: EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE 178 | EFI_VARIABLE_AUTHENTICATION_3 || EFI_VARIABLE_AUTHENTICATION_3_NONCE || EFI_VARIABLE_AUTHENTICATION_3_CERT_ID || Data 179 | ``` 180 | 181 | 注:每个示例中的 `EFI_VARIABLE_AUTHENTICATION_3` 结构的 *MetadataSize* 字段不包含任何 `WIN_CERTIFICATE_UEFI_GUID` 结构。这些结构用于 `SetVariable()` 接口,而不是 `GetVariable()`,如上例中所述。 182 | 183 | - 返回的状态码(Status Codes Returned) 184 | 185 | ![table8-3_1](../pic/table8-3_1.jpg) 186 | 187 | **GetNextVariableName()** 188 | 189 | - 概要(Summary) 190 | 191 | 枚举当前变量名称。 192 | 193 | - 原型(Prototype) 194 | 195 | ```c 196 | typedef 197 | EFI_STATUS GetNextVariableName ( 198 | IN OUT UINTN *VariableNameSize, 199 | IN OUT CHAR16 *VariableName, 200 | IN OUT EFI_GUID *VendorGuid 201 | ); 202 | ``` 203 | 204 | - 参数(Parameters) 205 | 206 | *VariableNameSize*:变量名缓冲区的大小。大小必须足够大以适合 *VariableName* 缓冲区中提供的输入字符串。 207 | 208 | *VariableName*:输入时,提供由 `GetNextVariableName()` 返回的最后一个 *VariableName*。在输出时,返回当前变量的 `Null` 终止字符串。 209 | 210 | *VendorGuid*:在输入时,提供由 `GetNextVariableName()` 返回的最后一个 *VendorGuid*。在输出时,返回当前变量的 *VendorGuid*。*EFI_GUID* 类型在 `EFI_BOOT_SERVICES.InstallProtocolInterface()` 函数描述中定义。 211 | 212 | - 描述(Description) 213 | 214 | 多次调用 `GetNextVariableName()` 以检索系统中当前可用的所有变量的 *VariableName* 和 *VendorGuid*。在每次调用 `GetNextVariableName()` 时,先前的结果都会传递到接口中,而在输出时,接口会返回下一个变量名称数据。返回整个变量列表后,将返回错误 `EFI_NOT_FOUND`。 215 | 216 | 请注意,如果返回 `EFI_BUFFER_TOO_SMALL`,则 *VariableName* 缓冲区对于下一个变量来说太小了。发生此类错误时,将更新 *VariableNameSize* 以反映所需缓冲区的大小。在所有情况下,当调用 `GetNextVariableName()` 时,*VariableNameSize* 不得超过为 *VariableName* 分配的实际缓冲区大小。*VariableNameSize* 不得小于在 *VariableName* 缓冲区输入时传递给 `GetNextVariableName()` 的变量名称字符串的大小。 217 | 218 | 要开始搜索,将以 `Null` 结尾的字符串传递到 *VariableName* 中;也就是说,*VariableName* 是指向 `Null` 字符的指针。这始终在首次调用 `GetNextVariableName()` 时完成。当 *VariableName* 是指向 `Null` 字符的指针时,*VendorGuid* 将被忽略。`GetNextVariableName()` 不能用作过滤器以返回具有特定 GUID 的变量名称。相反,必须检索整个变量列表,并且调用者可以选择充当过滤器。在调用 `GetNextVariableName()` 之间调用 `SetVariable()` 可能会产生不可预知的结果。如果输入的 *VariableName* 缓冲区不是以 `Null` 结尾的字符串,则返回 `EFI_INVALID_PARAMETER`。如果 *VariableName* 和 *VendorGuid* 的输入值不是现有变量的名称和 GUID,则返回 `EFI_INVALID_PARAMETER`。 219 | 220 | 一旦执行 `EFI_BOOT_SERVICES.ExitBootServices()`,将不再返回仅在引导服务期间可见的变量。要获取由 `GetNextVariableName()` 返回的变量的数据内容或属性,可使用 `GetVariable()` 接口。 221 | 222 | - 返回的状态码(Status Codes Returned) 223 | 224 | ![table8-3_2](../pic/table8-3_2.jpg) 225 | 226 | **SetVariable()** 227 | 228 | - 概要(Summary) 229 | 230 | 设置变量的值。此服务可用于创建新变量、修改现有变量的值或删除现有变量。 231 | 232 | - 原型(Prototype) 233 | 234 | ```c 235 | typedef EFI_STATUS SetVariable ( 236 | IN CHAR16 *VariableName, 237 | IN EFI_GUID *VendorGuid, 238 | IN UINT32 Attributes, 239 | IN UINTN DataSize, 240 | IN VOID *Data 241 | ); 242 | ``` 243 | 244 | - 参数(Parameters) 245 | 246 | *VariableName*:一个以 `Null` 结尾的字符串,它是供应商变量的名称。每个 *VariableName* 对于每个 *VendorGuid* 都是唯一的。变量名必须包含 1 个或多个字符。如果 *VariableName* 是空字符串,则返回 `EFI_INVALID_PARAMETER`。 247 | 248 | *VendorGuid*:供应商的唯一标识符。`EFI_GUID` 类型在 `EFI_BOOT_SERVICES.InstallProtocolInterface()` 函数描述中定义。 249 | 250 | *Attributes*:为变量设置的属性位掩码。请参阅 `GetVariable()` 函数说明。 251 | 252 | *DataSize*:数据缓冲区的大小(以字节为单位)。除非设置了 `EFI_VARIABLE_APPEND_WRITE`、`EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS`、`EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 或 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性,否则大小为零会导致变量被删除。当设置了 `EFI_VARIABLE_APPEND_WRITE` 属性时,*DataSize* 为零的 `SetVariable()` 调用将不会导致变量值发生任何变化(然而,与变量关联的时间戳可能会更新,即使没有提供新的数据值;请参阅下面对 `EFI_VARIABLE_AUTHENTICATION_2` 描述符的描述)。在这种情况下,*DataSize* 不会为零,因为将填充 `EFI_VARIABLE_AUTHENTICATION_2` 描述符)。 253 | 254 | *Data*:变量的内容。 255 | 256 | - 相关定义(Related Definitions) 257 | 258 | ```c 259 | //******************************************************* 260 | // Variable Attributes 261 | //******************************************************* 262 | 263 | // NOTE: This interface is deprecated and should no longer be used! 264 | // 265 | // EFI_VARIABLE_AUTHENTICATION descriptor 266 | // 267 | // A counter-based authentication method descriptor template 268 | // 269 | typedef struct { 270 | UINT64 MonotonicCount; 271 | WIN_CERTIFICATE_UEFI_GUID AuthInfo 272 | } EFI_VARIABLE_AUTHENTICATION; 273 | ``` 274 | 275 | *MonotonicCount*:包含在 *AuthInfo* 的签名中。用于确保新鲜度/无重播。在每次“写入”访问期间递增。 276 | 277 | *AuthInfo*:提供变量访问权限。它是跨可变数据和单调计数值的签名。调用方使用与通过密钥交换提供的公钥相关联的私钥。 278 | 279 | ```c 280 | // 281 | // EFI_VARIABLE_AUTHENTICATION_2 descriptor 282 | // 283 | // A time-based authentication method descriptor template 284 | // 285 | typedef struct { 286 | EFI_TIME TimeStamp; 287 | WIN_CERTIFICATE_UEFI_GUID AuthInfo 288 | } EFI_VARIABLE_AUTHENTICATION_2; 289 | ``` 290 | 291 | *TimeStamp*:与身份验证描述符关联的时间。对于 *TimeStamp* 值,组件 *Pad1*、*Nanosecond*、*TimeZone*、*Daylight* 和 *Pad2* 应设置为 0。这意味着时间应始终以 GMT [^3] 表示。 292 | 293 | ^3: GMT(Greenwich Mean Time): 格林威治标准时间。 294 | 295 | *AuthInfo*:提供变量访问权限。仅接受 `EFI_CERT_TYPE_PKCS7_GUID` 的 *CertType*。 296 | 297 | ```c 298 | // 299 | // EFI_VARIABLE_AUTHENTICATION_3 descriptor 300 | // 301 | // An extensible implementation of the Variable Authentication 302 | // structure. 303 | // 304 | #define EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE 1 305 | #define EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE 2 306 | 307 | typedef struct { 308 | UINT8 Version; 309 | UINT8 Type; 310 | UINT32 MetadataSize; 311 | UINT32 Flags 312 | } EFI_VARIABLE_AUTHENTICATION_3; 313 | ``` 314 | 315 | *Version*:该字段用于 `EFI_VARIABLE_AUTHENTICATION_3` 结构本身需要更新的情况。目前,它被硬编码为“0x1”。 316 | 317 | *Type*:在可变数据有效负载中声明紧跟在该结构之后的结构。对于 `EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE`,它将是 `EFI_TIME`(对于 *TimeStamp*)的一个实例。对于 `EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE`,该结构将是 `EFI_VARIABLE_AUTHENTICATION_3_NONCE` 的一个实例。该结构定义如下。请注意,这些结构都不包含 `WIN_CERTIFICATE_UEFI_GUID` 结构。有关结构排序的说明,请参见第 8.2.1 节。 318 | 319 | *MetadataSize*:声明所有变量身份验证元数据(与变量身份验证相关的数据本身不是变量数据)的大小,包括此标头结构和特定于类型的结构(例如 `EFI_VARIABLE_AUTHENTICATION_3_NONCE`)和任何 `WIN_CERTIFICATE_UEFI_GUID` 结构。 320 | 321 | *Flags*:指示此调用的任何可选配置的位字段。目前,唯一定义的值是: 322 | 323 | ```c 324 | #define EFI_VARIABLE_ENHANCED_AUTH_FLAG_UPDATE_CERT 0x00000001 325 | ``` 326 | 327 | `SetVariable()` 上此标志的存在表明在特定类型的结构之后有 `WIN_CERTIFICATE_UEFI_GUID` 结构的两个实例。 328 | 第一个实例描述了要设置为变量权限的新证书。 329 | 第二个是授权当前更新的签名数据。 330 | 注意:所有其他位当前在 `SetVariable()` 上保留。 331 | 注意:所有标志都保留在 `GetVariable()` 上。 332 | 333 | ```c 334 | // 335 | // EFI_VARIABLE_AUTHENTICATION_3_NONCE descriptor 336 | // 337 | // A nonce-based authentication method descriptor template. This 338 | // structure will always be followed by a 339 | // WIN_CERTIFICATE_UEFI_GUID structure. 340 | // 341 | typedef struct { 342 | UINT32 NonceSize 343 | // UINT8 Nonce[NonceSize]; 344 | } EFI_VARIABLE_AUTHENTICATION_3_NONCE; 345 | ``` 346 | 347 | *NonceSize*:指示结构中此字段后面的 *Nonce* 缓冲区的大小。不得为 0。 348 | 349 | *Nonce (Not a formal structure member)*:保证签名有效载荷的唯一随机值不能在多台机器或机器系列之间共享。在 `SetVariable()` 上,如果 *Nonce* 字段全为 0,主机将尝试使用内部生成的随机数。如果不可能,将返回 `EFI_UNSUPPORTED`。此外,在 `SetVariable()` 上,如果变量已经存在并且随机数与当前随机数相同,将返回 `EFI_INVALID_PARAMETER`。 350 | 351 | - 描述(Description) 352 | 353 | 变量由固件存储,并且可以在电源循环期间保持它们的值。每个供应商都可以通过使用唯一的 *VendorGuid* 创建和管理自己的变量,而不会出现名称冲突的风险。 354 | 355 | 每个变量都有定义固件如何存储和维护数据值的属性。如果未设置 `EFI_VARIABLE_NON_VOLATILE` 属性,则固件会将变量存储在普通内存中,并且不会在电源循环期间保持不变。这些变量用于将信息从一个组件传递到另一个组件。这方面的一个例子是固件的语言代码支持变量。它是在固件初始化时创建的,供可能需要该信息的 EFI 组件访问,但不需要备份到非易失性存储器。 356 | 357 | `EFI_VARIABLE_NON_VOLATILE` 变量存储在存储容量有限的固定硬件中;有时能力严重受限。软件只应在绝对必要时才使用非易失性变量。此外,如果软件使用非易失性变量,它应该尽可能使用只能在引导服务时访问的变量。 358 | 359 | 变量必须包含一个或多个字节的数据。除非设置了 `EFI_VARIABLE_APPEND_WRITE`、`EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 或 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性(见下文),否则使用 *DataSize* 为零的 `SetVariable()` 将导致删除整个变量。被删除的变量占用的空间可能在下一次电源循环之前不可用。 360 | 361 | 如果具有匹配名称、GUID 和属性的变量已经存在,则更新其值。 362 | 363 | 属性具有以下使用规则: 364 | 365 | - 如果用不同的属性重写一个预先存在的变量,`SetVariable()` 不应修改该变量并应返回 `EFI_INVALID_PARAMETER`。唯一的例外是唯一不同的属性是 `EFI_VARIABLE_APPEND_WRITE`。在这种情况下,调用的成功与否取决于写入的实际值。此规则有两个例外: 366 | 367 | - 如果一个预先存在的变量在没有指定访问属性的情况下被重写,该变量将被删除。 368 | 369 | - `EFI_VARIABLE_APPEND_WRITE` 属性代表了一种特殊情况。可以使用或不使用 `EFI_VARIABLE_APPEND_WRITE` 属性重写变量。 370 | 371 | - 设置没有访问属性的数据变量会导致它被删除。 372 | 373 | - `EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS` 已弃用,不应使用。如果 `SetVariable()` 的调用方指定了此属性,平台应返回 `EFI_UNSUPPORTED`。 374 | 375 | - 除非设置了 `EFI_VARIABLE_APPEND_WRITE`、`EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 或 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_WRITE_ACCESS` 属性,设置指定为零 DataSize 的数据变量会导致其被删除。 376 | 377 | - 对数据变量的运行时访问意味着引导服务访问。设置了 `EFI_VARIABLE_RUNTIME_ACCESS` 的属性也必须设置 `EFI_VARIABLE_BOOTSERVICE_ACCESS`。调用者有责任遵守此规则。 378 | 379 | - 一旦执行了 `EFI_BOOT_SERVICES.ExitBootServices()`,没有设置 `EFI_VARIABLE_RUNTIME_ACCESS` 的数据变量将不再对 `GetVariable()` 可见。 380 | 381 | - 执行 `ExitBootServices()` 后,只有设置了 `EFI_VARIABLE_RUNTIME_ACCESS` 和 `EFI_VARIABLE_NON_VOLATILE` 的变量才能使用 `SetVariable()` 进行设置。执行 `ExitBootServices()` 后,具有运行时访问权限但非非易失性的变量是只读数据变量。 382 | 当在 `SetVariable()` 调用中设置 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性时,身份验证应使用 `EFI_VARIABLE_AUTHENTICATION_3` 描述符,其后将是类型和标志字段中指示的任何描述符。 383 | 384 | - 当在 `SetVariable()` 调用中设置 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性时,验证应使用 `EFI_VARIABLE_AUTHENTICATION_2` 描述符。 385 | 386 | - 如果在 `SetVariable()` 调用中设置了 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 和 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性,则固件必须返回 `EFI_INVALID_PARAMETER`。 387 | 388 | - 如果在 `SetVariable()` 调用中设置了 `EFI_VARIABLE_APPEND_WRITE` 属性,则任何现有变量值都应附加 *Data* 参数的值。如果固件不支持附加操作,则 `SetVariable()` 调用应返回 `EFI_INVALID_PARAMETER`。 389 | 390 | - 如果在 `SetVariable()` 调用中设置了 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性,并且固件不支持包含在 `EFI_VARIABLE_AUTHENTICATION_2` 描述符中的证书的签名类型,则 `SetVariable()` 调用应返回 `EFI_INVALID_PARAMETER`。固件支持的签名类型列表由 *SignatureSupport* 变量定义。证书的签名类型由其摘要和加密算法定义。 391 | 392 | - 如果设置了 `EFI_VARIABLE_HARDWARE_ERROR_RECORD` 属性,*VariableName* 和 *VendorGuid* 必须符合 **Section 8.2.4.2** 和 **附录 P** 中规定的规则。否则,`SetVariable()` 调用应返回 `EFI_INVALID_PARAMETER`。 393 | 394 | - 必须使用 `Boot Manager` 这一章 **Table 3-1** 中定义的属性创建 **Globally Defined Variables**。如果使用错误的属性创建全局定义的变量,则结果是不确定的,并且可能因实现而异。 395 | 396 | - 如果使用 `EFI_VARIABLE_ENHANCED_AUTHETICATED_ACCESS` 接口更新给定变量的证书颁发机构,有效负载的 *Data* 区域为空是有效的。这将在不修改数据本身的情况下更新证书。如果 *Data* 区域为空且未指定 *NewCert*,则将删除该变量(假设已验证所有授权)。 397 | 398 | - 必须使用 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性集创建安全启动策略变量,并且身份验证应使用 `EFI_VARIABLE_AUTHENTICATION_2` 描述符。如果未设置适当的属性位,则固件应返回 `EFI_INVALID_PARAMETER`。 399 | 400 | 固件在保存非易失性变量时必须执行的唯一规则是它在返回 `EFI_SUCCESS` 之前实际上已经保存到非易失性存储中,并且不执行部分保存。如果在调用 `SetVariable()` 期间发生电源故障,则变量可能包含其先前值或新值。此外,没有读取、写入或删除安全保护。 401 | 402 | 要删除使用 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性创建的变量,必须将 *SetVariable* 与与现有变量匹配的属性一起使用,并将 *DataSize* 设置为 *AuthInfo* 描述符的大小。数据缓冲区必须包含 *AuthInfo* 描述符的实例,该实例将根据上面相应部分中参考更新已验证变量的步骤进行验证。尝试删除使用 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性创建的变量时,规定的 *AuthInfo* 验证失败或使用 **0** 值的 *DataSize* 调用时将失败,状态为 `EFI_SECURITY_VIOLATION`。 403 | 404 | 要删除使用 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性创建的变量,必须将 *SetVariable* 与与现有变量匹配的属性一起使用,并将 *DataSize* 设置为整个有效负载的大小,包括所有描述符和证书。数据缓冲区必须包含 `EFI_VARIABLE_AUTHENTICATION_3` 描述符的实例,它将指示如何根据 **Section 8.2.1** 中的描述验证有效负载。尝试删除使用 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性创建的变量时,规定的验证失败或使用 **0** 值的 *DataSize* 调用时将失败,状态为 `EFI_SECURITY_VIOLATION`。 405 | 406 | - 返回的状态码(Status Codes Returned) 407 | 408 | ![table8-3_3](../pic/table8-3_3.jpg) 409 | 410 | **QueryVariableInfo()** 411 | 412 | - 概要(Summary) 413 | 414 | 返回有关 EFI 变量的信息。 415 | 416 | - 原型(Prototype) 417 | 418 | ```c 419 | typedef EFI_STATUS QueryVariableInfo ( 420 | IN UINT32 Attributes, 421 | OUT UINT64 *MaximumVariableStorageSize, 422 | OUT UINT64 *RemainingVariableStorageSize, 423 | OUT UINT64 *MaximumVariableSize 424 | ); 425 | ``` 426 | 427 | - 参数(Parameters) 428 | 429 | *Attributes*:属性位掩码指定返回信息的变量类型。请参阅 `GetVariable()` 函数说明。`EFI_VARIABLE_APPEND_WRITE` 属性,如果在属性位掩码中设置,将被忽略。 430 | 431 | *MaximumVariableStorageSize*:输出时可用于与指定属性关联的 EFI 变量的存储空间的最大大小。 432 | 433 | *RemainingVariableStorageSize*:返回可用于与指定属性关联的 EFI 变量的存储空间的剩余大小。 434 | 435 | *MaximumVariableSize*:返回与指定属性关联的单个 EFI 变量的最大大小。 436 | 437 | - 描述(Description) 438 | 439 | `QueryVariableInfo()` 函数允许调用者获取有关 EFI 变量可用存储空间的最大大小、EFI 变量可用存储空间的剩余大小以及每个单独的 EFI 变量的最大大小的信息,与 指定的属性。 440 | 441 | *MaximumVariableSize* 值将反映与保存单个 EFI 变量相关的开销,与 EFI 变量的字符串名称的长度相关的开销除外。 442 | 443 | 返回的 *MaximumVariableStorageSize*、*RemainingVariableStorageSize*、*MaximumVariableSize* 信息可能会在调用后根据其他运行时活动(包括异步错误事件)立即更改。此外,这些与不同属性关联的值在本质上不是相加的。 444 | 445 | 系统转换到运行时后(调用 `ExitBootServices()` 后),实现可能无法准确返回有关引导服务变量存储的信息。在这种情况下,应返回 `EFI_INVALID_PARAMETER`。 446 | 447 | - 返回的状态码(Status Codes Returned) 448 | 449 | ![table8-3_4](../pic/table8-3_4.jpg) 450 | 451 | ### 使用 EFI_VARIABLE_AUTHENTICATION_3 描述符(Using the EFI_VARIABLE_AUTHENTICATION_3 descriptor) 452 | 453 | 当设置 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性时,有效负载缓冲区(作为“数据”传递到 `SetVariable()` 中)应按如下方式构造: 454 | 455 | ```c 456 | // NOTE: “||” indicates concatenation. 457 | // NOTE: “[]” indicates an optional element. 458 | 459 | // Example: EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE 460 | EFI_VARIABLE_AUTHENTICATION_3 || EFI_TIME || [ NewCert ] || SigningCert || Data 461 | 462 | // Example: EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE 463 | EFI_VARIABLE_AUTHENTICATION_3 || EFI_VARIABLE_AUTHENTICATION_3_NONCE || [ NewCert ] || SigningCert || Data 464 | ``` 465 | 466 | 在此示例中,*NewCert* 和 *SigningCert* 都是 `WIN_CERTIFICATE_UEFI_GUID` 的实例。*NewCert* 的存在由 `EFI_VARIABLE_AUTHENTICATION_3.Flags` 字段指示(请参阅 `SetVariable()` 中的定义)。如果提供 - 并假设有效负载通过所有完整性和安全验证 - 此证书将被设置为基础变量的新权限,即使变量是新创建的。 467 | 468 | *NewCert* 元素必须具有 `EFI_CERT_TYPE_PKCS7_GUID` 的 *CertType*,并且 *CertData* 必须是符合 **PKCS#7 version 1.5 (RFC 2315)** 的 `DER-encoded SignedData` 结构,根据 **PKCS#7 version 1.5**,无论是否使用 `DER-encoded ContentInfo` 结构都应支持该结构。创建 *SignedData* 结构时,应遵循以下步骤: 469 | 470 | 1. 创建 `WIN_CERTIFICATE_UEFI_GUID` 结构,其中 *CertType* 设置为 `EFI_CERT_TYPE_PKCS7_GUID`。 471 | 472 | 2. 使用添加的 **x509** 证书作为新权限来签署自己的 *tbsCertificate* 数据。 473 | 474 | 3. 构造一个 **PKCS#7 version 1.5 SignedData(参见 [RFC2315])**,其签名内容如下: 475 | 476 | - *SignedData.version* 应设置为 1。 477 | 478 | - *SignedData.digestAlgorithms* 应包含准备签名时使用的摘要算法。仅接受 **SHA-256** 的摘要算法。 479 | 480 | - *SignedData.contentInfo.contentType* 应设置为 **id-data**。 481 | 482 | - *SignedData.contentInfo.content* 应该是为新的 **x509** 证书签名的 *tbsCertificate* 数据。 483 | 484 | - *SignedData.certificates* 应至少包含签名者的 `DER-encoded X.509` 证书。 485 | 486 | - *SignedData.crls* 是可选的。 487 | 488 | - *SignedData.signerInfos* 应构造为: 489 | 490 | - *SignerInfo.version* 应设置为 1; 491 | 492 | - *SignerInfo.issuerAndSerial* 应存在并与签名者的证书相同; 493 | 494 | - *SignerInfo.authenticatedAttributes* 不应存在; 495 | 496 | - *SignerInfo.digestEncryptionAlgorithm* 应设置为用于签署数据的算法。仅接受具有 **PKCS #1 v1.5 padding (RSASSA_PKCS1v1_5)** 的 RSA 摘要加密算法; 497 | 498 | - *SignerInfo.encryptedDigest* 应存在; 499 | 500 | - *SignerInfo.unauthenticatedAttributes* 不应存在; 501 | 502 | - 将 *CertData* 字段设置为 `DER-encoded PKCS#7 SignedData` 值。 503 | 504 | 4. 将 *CertData* 字段设置为 `DER-encoded PKCS#7 SignedData` 值。 505 | 506 | 尝试创建、更新或删除带有 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 集的变量的 `SetVariable()` 调用者应执行以下步骤来为 *SigningCert* 创建 *SignedData* 结构: 507 | 508 | 1. 使用以下值创建 `EFI_VARIABLE_AUTHENTICATION_3` 主描述符: 509 | 510 | - 版本应设置为适合正在使用的元数据头的版本(当前为 1); 511 | 512 | - 类型应根据调用者规范进行设置(请参阅 `SetVariable()` 下的 `EFI_VARIABLE_AUTHENTICATION_3` 描述符); 513 | 514 | - *MetadataSize* 暂时可以忽略,会在构建最终 *payload* 时更新; 515 | 516 | - 应根据调用方规范设置标志。 517 | 518 | 2. 可能需要根据类型创建二级描述符: 519 | 520 | - 对于 `EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE` 类型,这将是 `EFI_TIME` 设置为当前时间的一个实例; 521 | 522 | - 对于 `EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE` 类型,这将是 `EFI_VARIABLE_AUTHENTICATION_3_NONCE` 的实例,根据调用者规范(不得为零)设置 *NonceSize* 更新,并将 *Nonce*(非正式结构成员)设置为: 523 | 524 | - 全零请求平台创建随机数; 525 | 526 | - 调用者为预生成的随机数指定值。 527 | 528 | - 散列有效负载的序列化。序列化应按此顺序包含以下元素: 529 | 530 | - *VariableName*、*VendorGuid*、*Attributes* 和 *Secondary* 描述符(如果存在); 531 | 532 | - 变量的新值(即 Data 参数的新变量内容); 533 | 534 | - 如果这是对类型为 `EFI_VARIABLE_AUTHENTICATION_3_NONCE` 的变量的更新或删除,请序列化当前随机数。当前随机数是当前与该变量关联的随机数,而不是二级描述符中的随机数。仅序列化 *nonce* 缓冲区内容,而不序列化大小或任何其他数据。如果这是尝试创建一个新变量(即没有当前随机数),请跳过此步骤; 535 | 536 | - 签署生成的摘要。 537 | 538 | - 创建 `WIN_CERTIFICATE_UEFI_GUID` 结构,其中 *CertType* 设置为 `EFI_CERT_TYPE_PKCS7_GUID`。 539 | 540 | - 按照上面为 *NewCert*(步骤 3)描述的步骤构建一个 `DER-encoded PKCS #7 version 1.5 SignedData`(请参阅 [RFC2315]),但有以下例外: 541 | 542 | - `SignedData.contentInfo.content` 应不存在(内容在 *Data* 参数中提供给 `SetVariable()` 调用)。 543 | 544 | - 根据本节开头对“payload buffer”的描述,构造 `SetVariable()` 的最终 *payload*。 545 | 546 | - 更新 `EFI_VARIABLE_AUTHENTICATION_3.MetadataSize` 字段以包含最终有效负载的所有部分,但“数据”除外。 547 | 548 | 实现 `SetVariable()` 服务并支持 `EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS` 属性的固件应执行以下操作以响应被调用: 549 | 550 | 1. 阅读 `EFI_VARIABLE_AUTHENTICATION_3` 描述符以确定正在执行的身份验证类型以及如何解析其余有效负载。 551 | 552 | 2. 验证 `SigningCert.CertType` 是否为 `EFI_CERT_TYPE_PKCS7_GUID`。 553 | 554 | - 如果 `EFI_VARIABLE_AUTHENTICATION_3.Flags` 字段指示存在 *NewCert*,请验证 `NewCert.CertType` 是否为 `EFI_CERT_TYPE_PKCS7_GUID`; 555 | 556 | - 如果任一失败,则返回 `EFI_INVALID_PARAMETER`。 557 | 558 | 3. 如果该变量已存在,请验证传入类型是否与现有类型匹配。 559 | 560 | 4. 确认任何 `EFI_TIME` 结构都将 *Pad1*、*Nanosecond*、*TimeZone*、*Daylight* 和 *Pad2* 字段设置为零。 561 | 562 | 5. 如果 `EFI_VARIABLE_AUTHENTICATION_3_NONCE_TYPE`: 563 | 564 | - 验证 *NonceSize* 是否大于零。如果为零,则返回 `EFI_INVALID_PARAMETER`; 565 | 566 | - 如果传入的 *nonce* 全为 0,确认平台支持生成随机 *nonce*。如果不受支持,则返回 `EFI_UNSUPPORTED`; 567 | 568 | - 如果指定了 *nonce* 并且变量已经存在,请验证传入的 *nonce* 是否与现有的 *nonce* 不匹配。如果相同,则返回 `EFI_INVALID_PARAMETER`。 569 | 570 | 6. 如果 `EFI_VARIABLE_AUTHENTICATION_3_TIMESTAMP_TYPE` 和变量已经存在,请验证新时间戳在时间上是否大于当前 *Timestamp*。 571 | 572 | 7. 通过以下方式验证有效负载签名: 573 | 574 | - 根据描述符解析整个有效载荷; 575 | 576 | - 使用描述符内容(以及必要时来自现有变量的元数据)构建本节前面描述的序列化(`SetVariable()` 指令的第 3 步); 577 | 578 | - 计算摘要并与将 *SigningCert* 的公钥应用于签名的结果进行比较。 579 | 580 | 8. 如果该变量已存在,请验证 *SigningCert* 权限是否与已与该变量关联的权限相同。 581 | 582 | 9. 如果提供了 *NewCert*,请通过以下方式验证 *NewCert* 签名: 583 | 584 | - 根据描述符解析整个有效载荷; 585 | 586 | - 计算 *NewCert* 中 **x509** 证书的 *tbsCertificate* 摘要,并与将 *NewCert* 的公钥应用于签名的结果进行比较; 587 | 588 | - 如果失败,返回 `EFI_SECURITY_VIOLATION`。 589 | 590 | ### 使用 EFI_VARIABLE_AUTHENTICATION_2 描述符 (Using the EFI_VARIABLE_AUTHENTICATION_2 descriptor) 591 | 592 | 当设置了 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性时,数据缓冲区应以一个完整(和序列化)的实例开始。 593 | 594 | `EFI_VARIABLE_AUTHENTICATION_2` 描述符。描述符后应跟新变量值,*DataSize* 应反映描述符和新变量值的组合大小。身份验证描述符不是变量数据的一部分,并且不会由对 `GetVariable()` 的后续调用返回。 595 | 596 | 使用 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性集调用 `SetVariable()` 服务的调用方应在调用服务之前执行以下操作: 597 | 598 | 1. 创建描述符 599 | 600 | 创建 `EFI_VARIABLE_AUTHENTICATION_2` 描述符,其中: 601 | 602 | - *TimeStamp* 设置为当前时间; 603 | 604 | 注:在某些环境中,可靠的时间源可能不可用。在这种情况下,由于 `EFI_VARIABLE_APPEND_WRITE` 属性在设置时会禁用时间戳验证(见下文),因此实现可能仍会向经过身份验证的变量添加值。在这些情况下,应使用 `EFI_TIME` 结构的每个组件(包括日和月)都设置为 0 的特殊时间值。 605 | 606 | - `AuthInfo.CertType` 设置为 `EFI_CERT_TYPE_PKCS7_GUID`。 607 | 608 | 2. 散列 `SetVariable()` 调用的 *VariableName*、*VendorGuid* 和 *Attributes* 参数值的序列化以及 `EFI_VARIABLE_AUTHENTICATION_2` 描述符的 *TimeStamp* 组件,后跟变量的新值(即 *Data* 参数的新变量内容)。即 `digest = hash(VariableName, VendorGuid, Attributes, TimeStamp, DataNew_variable_content)`。终止 *VariableName* 值的 `NULL` 字符不应包含在散列计算中。 609 | 610 | 3. 使用选定的签名方案(例如 `PKCS #1 v1.5`)对生成的摘要进行签名。 611 | 612 | 4. 根据 `PKCS#7 v1.5 (RFC 2315)` 构造一个 `DER-encoded SignedData` 结构,无论是否使用 `PKCS#7 v1.5` 的 `DER-encoded ContentInfo` 结构都应支持,签名内容如下: 613 | 614 | - `SignedData.version` 应设置为 1; 615 | 616 | - `SignedData.digestAlgorithms` 应包含准备签名时使用的摘要算法。仅接受 **SHA-256** 的摘要算法; 617 | 618 | - `SignedData.contentInfo.contentType` 应设置为 `id-data`; 619 | 620 | - `SignedData.contentInfo.content` 应不存在(内容在 *Data* 参数中提供给 `SetVariable()` 调用); 621 | 622 | - `SignedData.certificates` 应至少包含签名者的 `DER-encoded X.509` 证书; 623 | 624 | - `SignedData.crls` 可选; 625 | 626 | - `SignedData.signerInfos` 应构造为: 627 | 628 | - `SignerInfo.version` 应设置为 1; 629 | 630 | - `SignerInfo.issuerAndSerial` 应存在,并且在签名者的证书中 `SignerInfo.authenticatedAttributes` 不应存在; 631 | 632 | - `SignerInfo.digestEncryptionAlgorithm` 应设置为用于签署数据的算法。仅接受具有 `PKCS #1 v1.5 padding (RSASSA_PKCS1v1_5)` 的 RSA 摘要加密算法; 633 | 634 | - `SignerInfo.encryptedDigest` 应存在; 635 | 636 | - `SignerInfo.unauthenticatedAttributes` 不应存在; 637 | 638 | - 将 `AuthInfo.CertData` 设置为 `DER-encoded PKCS #7 SignedData` 值; 639 | 640 | - 构造数据参数:通过将完整的序列化 `EFI_VARIABLE_AUTHENTICATION_2` 描述符与变量的新值 (`DataNew_variable_content`) 连接起来构造 `SetVariable()` 的数据参数。 641 | 642 | 实现 `SetVariable()` 服务并支持 `EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS` 属性的固件应执行以下操作以响应调用: 643 | 644 | 1. 验证是否使用了正确的 `AuthInfo.CertType (EFI_CERT_TYPE_PKCS7_GUID)` 以及 `AuthInfo.CertData` 值是否正确解析为 `PKCS #7 SignedData` 值; 645 | 646 | 2. 验证 *TimeStamp* 值的 *Pad1*、*Nanosecond*、*TimeZone*、*Daylight* 和 *Pad2* 组件是否设置为零。除非设置了 `EFI_VARIABLE_APPEND_WRITE` 属性,否则验证 *TimeStamp* 值是否晚于与变量关联的当前时间戳值; 647 | 648 | 3. 如果变量 `SetupMode==1`,并且该变量是安全启动策略变量,则固件实现应认为已通过以下步骤 4 和 5 中的检查,并继续更新变量值,如下所述。 649 | 650 | 4. 通过以下方式验证签名: 651 | 652 | - 从数据缓冲区中提取 `EFI_VARIABLE_AUTHENTICATION_2` 描述符; 653 | 654 | - 使用描述符内容和其他参数来:(a) 构造摘要算法的输入;(b) 计算摘要;(c) 将摘要与将签名者的公钥应用于签名的结果进行比较; 655 | 656 | 5. 如果变量是全局 PK 变量或全局 KEK 变量,请验证是否使用当前 `Platform Key` 进行了签名; 657 | 658 | - 如果变量是步骤 3 中提到的“db”、“dbt”、“dbr”或“dbx”变量,请验证签名者的证书链接到密钥交换密钥数据库中的证书(或者签名是使用 当前的平台密钥); 659 | 660 | - 如果变量是步骤 3 中提到的“OsRecoveryOrder”或“OsRecover####”变量,请验证签名者的证书是否链接到“dbr”数据库或密钥交换密钥数据库中的证书,或者签名是使用当前平台密钥进行的; 661 | 662 | - 否则,如果变量不是上述任何一种,则应将其指定为私有验证变量。如果私有身份验证变量不存在,则签名证书的主题的 CN 和顶级颁发者证书的 *tbsCertificate* 的哈希值(如果不存在其他证书或证书链长度为 1,则为签名证书本身) 在 `SignedData.certificates`中注册用于此变量的后续验证。实现可能只存储这两个元素的单个散列以减少存储需求。如果 *Private Authenticated* 变量以前存在,则签名者的证书链接到以前与该变量关联的信息。请注意,由于不存在针对它们的撤销列表,如果证书链的任何成员遭到破坏,则撤销对私有身份验证变量证书的信任的唯一方法是删除该变量,重新颁发链中的所有证书颁发机构,并使用新的证书链重新创建变量。因此,剩下的好处可能是对发起者的强烈识别,或遵守某些证书颁发机构的政策。进一步注意,经过身份验证的变量更新的 PKCS7 包必须包含签名证书链,包括所需信任锚的完整证书。信任锚可能是中级证书或根,但由于它们为不同目的颁发的 CA 数量,许多根可能不适合作为信任锚。一些工具需要非默认参数来包含信任锚证书。 663 | 664 | 只有当所有这些检查都通过时,驱动程序才会更新变量的值。如果任何检查失败,固件必须返回 `EFI_SECURITY_VIOLATION`。仅当设置了 `EFI_VARIABLE_APPEND_WRITE` 属性时,固件才应执行对现有变量值的追加。 665 | 666 | 对于具有 `GUID EFI_IMAGE_SECURITY_DATABASE_GUID` 的变量(即数据缓冲区格式为 `EFI_SIGNATURE_LIST` 的地方),驱动程序不应执行已经是现有变量值的一部分的 `EFI_SIGNATURE_DATA` 值的追加。 667 | 668 | 注意:这种情况不被视为错误,并且本身不会导致返回 `EFI_SUCCESS` 以外的状态代码或不更新与变量关联的时间戳。 669 | 670 | 固件应将新 *TimeStamp* 与更新值相关联(在设置 `EFI_VARIABLE_APPEND_WRITE` 属性的情况下,这仅适用于新时间戳值晚于与变量关联的当前时间戳的情况)。 671 | 672 | 如果该变量以前不存在,并且不是上面第 3 步中列出的变量之一,则固件应将签名者的公钥与该变量相关联,以供将来验证之用。 673 | 674 | ### 使用 EFI_VARIABLE_AUTHENTICATION 描述符(Using the EFI_VARIABLE_AUTHENTICATION descriptor) 675 | 676 | 注意:此接口已弃用,不应再使用!它将从规范的未来版本中删除。 677 | 678 | ### 硬件错误记录持久化(Hardware Error Record Persistence) 679 | 680 | 本节定义如何实现硬件错误记录持久性。通过实现对硬件错误记录持久性的支持,该平台使操作系统能够利用 EFI 变量服务来保存硬件错误记录,因此它们是持久的,并且在操作系统会话中保持可用,直到它们被其创建者明确清除或覆盖。 681 | 682 | #### 硬件错误记录非易失性存储(Hardware Error Record Non-Volatile Store) 683 | 684 | 需要一个实现支持硬件错误记录持久化的平台,以保证一定数量的 NVR 可供 OS 用于保存硬件错误记录。平台通过 *QueryVariableInfo* 例程传达为错误记录分配的空间量,如 **附录 P** 中所述。 685 | 686 | #### 硬件错误记录变量(Hardware Error Record Variables) 687 | 688 | 本节定义了一组具有架构定义含义的硬件错误记录变量。除了定义的数据内容之外,每个这样的变量都有一个体系结构定义的属性,指示何时可以访问数据变量。具有 HR 属性的变量存储在 NVR 分配给错误记录的部分。NV、BS 和 RT 具有第 3.2 节中定义的含义。所有硬件错误记录变量都使用 `EFI_HARDWARE_ERROR_VARIABLE VendorGuid`: 689 | 690 | ```c 691 | #define EFI_HARDWARE_ERROR_VARIABLE\ 692 | {0x414E6BDD,0xE47B,0x47cc,{0xB2,0x44,0xBB,0x61,0x02,0x0C,0xF5,0x16}} 693 | ``` 694 | 695 | ![table8-4 Hardware Error Record Persistence Variables](../pic/table8-4.jpg) 696 | 697 | `HwErrRec####` 变量包含硬件错误记录。每个 `HwErrRec####` 变量都是名称 `“HwErrRec”` 附加一个唯一的 4 位十六进制数。例如 `HwErrRec0001`、`HwErrRec0002`、`HwErrRecF31A` 等。HR 属性表示此变量将存储在分配给错误记录的 NVR 部分中。 698 | 699 | #### 通用平台错误记录格式(Common Platform Error Record Format) 700 | 701 | 使用此接口保留的错误记录变量以通用平台错误记录格式进行编码,该格式在 UEFI 规范的附录 N 中进行了描述。由于使用此接口保存的错误记录符合此标准格式,因此错误信息可能会被 OS 以外的实体使用。 702 | 703 | ## 时间服务(Time Services) 704 | 705 | 本节包含与时间相关的功能的功能定义,操作系统通常需要这些功能在运行时访问管理时间信息和服务的底层硬件。这些接口的目的是为操作系统编写者提供硬件时间设备的抽象,从而减轻直接访问遗留硬件设备的需要。还有一个用于预引导环境的停止功能。表 8-5 列出了本节中描述的时间服务功能: 706 | 707 | ![table8-5 Time Services Functions](../pic/table8-5.jpg) 708 | 709 | **GetTime()** 710 | 711 | - 概要(Summary) 712 | 713 | 返回当前时间和日期信息,以及硬件平台的计时能力。 714 | 715 | - 原型(Prototype) 716 | 717 | ```c 718 | typedef EFI_STATUS GetTime ( 719 | OUT EFI_TIME *Time, 720 | OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL 721 | ); 722 | ``` 723 | 724 | - 参数(Parameters) 725 | 726 | *Time*:指向存储的指针以接收当前时间的快照。`EFI_TIME` 类型在“相关定义”中定义。 727 | 728 | *Capabilities*:指向缓冲区的可选指针,用于接收实时时钟设备的功能。`EFI_TIME_CAPABILITIES` 类型在“相关定义”中定义。 729 | 730 | - 相关定义(Related Definitions) 731 | 732 | ```c 733 | //******************************************************* 734 | //EFI_TIME 735 | //******************************************************* 736 | // This represents the current time information 737 | typedef struct { 738 | UINT16 Year; // 1900 – 9999 739 | UINT8 Month; // 1 – 12 740 | UINT8 Day; // 1 – 31 741 | UINT8 Hour; // 0 – 23 742 | UINT8 Minute; // 0 – 59 743 | UINT8 Second; // 0 – 59 744 | UINT8 Pad1; 745 | UINT32 Nanosecond; // 0 – 999,999,999 746 | INT16 TimeZone; // -1440 to 1440 or 2047 747 | UINT8 Daylight; 748 | UINT8 Pad2 749 | } EFI_TIME; 750 | 751 | //******************************************************* 752 | // Bit Definitions for EFI_TIME.Daylight. See below. 753 | //******************************************************* 754 | #define EFI_TIME_ADJUST_DAYLIGHT 0x01 755 | #define EFI_TIME_IN_DAYLIGHT 0x02 756 | //******************************************************* 757 | // Value Definition for EFI_TIME.TimeZone. See below. 758 | //******************************************************* 759 | #define EFI_UNSPECIFIED_TIMEZONE 0x07FF 760 | ``` 761 | 762 | *Year, Month, Day*:当前本地日期。 763 | 764 | *Hour, Minute, Second, Nanosecond*:当前当地时间。纳秒报告设备中的当前秒数。时间格式为 `hh:mm:ss.nnnnnnnnn`。电池供电的实时时钟设备保持日期和时间。 765 | 766 | *TimeZone*:时间与 UTC 的偏移量(以分钟为单位)。如果值为 `EFI_UNSPECIFIED_TIMEZONE`,则时间被解释为本地时间。*TimeZone* 是本地时间相对于 UTC 的分钟数。要计算 *TimeZone* 值,请遵循以下等式:`Localtime = UTC - TimeZone`。为了进一步说明这一点,下面给出了一个例子:`PST(太平洋标准时间是中午 12 点)= UTC(晚上 8 点)- 8 小时(480 分钟)`。在这种情况下,如果引用 PST,*TimeZone* 的值将为 480。 767 | 768 | *Daylight*:包含时间的夏令时信息的位掩码。`EFI_TIME_ADJUST_DAYLIGHT` 位指示时间是否受夏令时影响。此值并不表示时间已针对夏令时进行了调整。仅表示 `EFI_TIME` 进入夏令时时需要调整。如果设置了 `EFI_TIME_IN_DAYLIGHT`,则时间已针对夏令时进行了调整。所有其他位必须为零。输入夏令时时,如果时间受到影响,但没有调整(DST = 1),则使用新计算: 769 | 770 | 1. 日期/时间适当增加; 771 | 772 | 2. *TimeZone* 应适当减少(例如:从 PST 移动到 PDT 时 +480 变为 +420); 773 | 774 | 3. 日光值变为 3; 775 | 776 | 退出夏令时时,如果时间受到影响并已调整(DST = 3),则使用新计算: 777 | 778 | 1. 日期/时间应适当减少; 779 | 780 | 2. *TimeZone* 要适当增加; 781 | 782 | 3. 日光值变为 1; 783 | 784 | ```c 785 | //******************************************************* 786 | // EFI_TIME_CAPABILITIES 787 | //******************************************************* 788 | // This provides the capabilities of the 789 | // real time clock device as exposed through the EFI interfaces. 790 | typedef struct { 791 | UINT32 Resolution; 792 | UINT32 Accuracy; 793 | BOOLEAN SetsToZero 794 | } EFI_TIME_CAPABILITIES; 795 | ``` 796 | 797 | *Resolution*:提供实时时钟设备每秒计数的报告分辨率。对于普通的 PC-AT CMOS RTC 设备,此值将为 1 Hz 或 1,以指示该设备仅报告 1 秒分辨率的时间。 798 | 799 | *Accuracy*:提供实时时钟的计时精度,误差率为百万分之 1E-6。对于精度为百万分之 50 的时钟,此字段中的值为 50,000,000。 800 | 801 | *SetsToZero*:`TRUE` 表示时间设置操作将设备的时间清除到 *Resolution* 报告级别以下。`FALSE` 表示在设置时间时不清除设备 *Resolution* 级别以下的状态。普通 PC-AT CMOS RTC 设备将此值设置为 `FALSE`。 802 | 803 | - 描述(Description) 804 | 805 | `GetTime()` 函数返回一个在函数调用期间有效的时间。虽然返回的 `EFI_TIME` 结构包含 *TimeZone* 和夏令时信息,但实际时钟不维护这些值。`GetTime()` 返回的当前时区和夏令时信息是上次通过 `SetTime()` 设置的值。 806 | 807 | `GetTime()` 函数在每次调用时应该花费大致相同的时间来读取时间。所有报告的设备功能都将被四舍五入。 808 | 809 | 在运行时期间,如果平台中存在 PC-AT CMOS 设备,则调用者必须在调用 `GetTime()` 之前同步对设备的访问。 810 | 811 | - 返回的状态码(Status Codes Returned) 812 | 813 | ![table8-5_1](../pic/table8-5_1.jpg) 814 | 815 | **SetTime()** 816 | 817 | - 概要(Summary) 818 | 819 | 设置当前本地时间和日期信息。 820 | 821 | - 原型(Prototype) 822 | 823 | ```c 824 | typedef EFI_STATUS SetTime ( 825 | IN EFI_TIME *Time 826 | ); 827 | ``` 828 | 829 | - 参数(Parameters) 830 | 831 | *Time*:指向当前时间的指针。`EFI_TIME` 类型在 `GetTime()` 函数描述中定义。对 `EFI_TIME` 结构的不同字段执行完整的错误检查(有关完整详细信息,请参阅 `GetTime()` 函数描述中的 `EFI_TIME` 定义),如果任何字段超出范围,则返回 `EFI_INVALID_PARAMETER`。 832 | 833 | - 描述(Description) 834 | 835 | `SetTime()` 函数将实时时钟设备设置为提供的时间,并记录当前时区和夏令时信息。`SetTime()` 函数不允许根据当前时间循环。例如,如果设备不支持亚分辨率时间的硬件重置,则代码不会通过等待时间回绕来实现该功能。 836 | 837 | 在运行期间,如果平台中存在 PC-AT CMOS 设备,则调用者必须在调用 `SetTime()` 之前同步对设备的访问。 838 | 839 | - 返回的状态码(Status Codes Returned) 840 | 841 | ![table8-5_2](../pic/table8-5_2.jpg) 842 | 843 | **GetWakeupTime()** 844 | 845 | - 概要(Summary) 846 | 847 | 返回当前唤醒闹钟设置。 848 | 849 | - 原型(Prototype) 850 | 851 | ```c 852 | typedef EFI_STATUS GetWakeupTime ( 853 | OUT BOOLEAN *Enabled, 854 | OUT BOOLEAN *Pending, 855 | OUT EFI_TIME *Time 856 | ); 857 | ``` 858 | 859 | - 参数(Parameters) 860 | 861 | *Enabled*:指示警报当前是启用还是禁用; 862 | 863 | *Pending*:指示警报信号是否未决并需要确认; 864 | 865 | *Time*:当前闹钟设置。`EFI_TIME` 类型在 `GetTime()` 函数描述中定义。 866 | 867 | - 描述(Description) 868 | 869 | 闹钟时间可以从设置的闹钟时间四舍五入到闹钟设备的分辨率内。闹钟设备的分辨率定义为一秒。 870 | 871 | 在运行时期间,如果平台中存在 PC-AT CMOS 设备,则调用者必须在调用 `GetWakeupTime()` 之前同步对设备的访问。 872 | 873 | - 返回的状态码(Status Codes Returned) 874 | 875 | ![table8-5_3](../pic/table8-5_3.jpg) 876 | 877 | **SetWakeupTime()** 878 | 879 | - 概要(Summary) 880 | 881 | 设置系统唤醒闹钟时间。 882 | 883 | - 原型(Prototype) 884 | 885 | ```c 886 | typedef EFI_STATUS SetWakeupTime ( 887 | IN BOOLEAN Enable, 888 | IN EFI_TIME *Time OPTIONAL 889 | ); 890 | ``` 891 | 892 | - 参数(Parameters) 893 | 894 | *Enable*:启用或禁用唤醒警报; 895 | 896 | *Time*:如果 *Enable* 为 `TRUE`,则为设置唤醒警报的时间。`EFI_TIME` 类型在 `GetTime()` 函数描述中定义。如果 *Enable* 为 `FALSE`,则此参数是可选的,并且可以为 `NULL`。 897 | 898 | - 描述(Description) 899 | 900 | 设置系统唤醒警报会使系统在设置的时间唤醒或开机。当警报触发时,警报信号将被锁存,直到通过调用 `SetWakeupTime()` 确认以禁用警报。如果警报在系统进入休眠或关闭状态之前触发,由于警报信号被锁存,系统将立即唤醒。如果在系统关闭且没有足够的电力启动系统时发出警报,系统会在电源恢复时启动。 901 | 902 | 对于 ACPI 感知操作系统,此函数仅处理为所需的唤醒时间编程唤醒警报。操作系统仍然像往常一样通过 ACPI 电源管理寄存器组控制唤醒事件。 903 | 904 | 唤醒警报的分辨率定义为 1 秒。 905 | 906 | 在运行期间,如果平台中存在 PC-AT CMOS 设备,则调用者必须在调用 `SetWakeupTime()` 之前同步对设备的访问。 907 | 908 | - 返回的状态码(Status Codes Returned) 909 | 910 | ![table8-5_4](../pic/table8-3_1.jpg) 911 | 912 | ## 虚拟内存服务(Virtual Memory Services) 913 | 914 | 本节包含操作系统在运行时可选择使用的虚拟内存支持的函数定义。如果操作系统选择以虚拟寻址模式而不是平面物理模式进行 EFI 运行时服务调用,则操作系统必须使用本节中的服务将 EFI 运行时服务从平面物理寻址切换到虚拟寻址。Table 8-6 列出了本节中描述的虚拟内存服务函数。系统固件必须遵循 EFI 内存映射布局中 **Section 2.3.2** 到 **Section 2.3.2** 中概述的特定于处理器的规则,以使操作系统能够进行所需的虚拟映射。 915 | 916 | ![table8-6 Virtual Memory Functions](../pic/table8-6.jpg) 917 | 918 | **SetVirtualAddressMap()** 919 | 920 | - 概要(Summary) 921 | 922 | 将 EFI 固件的运行时寻址模式从物理更改为虚拟。 923 | 924 | - 原型(Prototype) 925 | 926 | ```c 927 | typedef EFI_STATUS SetVirtualAddressMap ( 928 | IN UINTN MemoryMapSize, 929 | IN UINTN DescriptorSize, 930 | IN UINT32 DescriptorVersion, 931 | IN EFI_MEMORY_DESCRIPTOR *VirtualMap 932 | ); 933 | ``` 934 | 935 | - 参数(Parameters) 936 | 937 | *MemoryMapSize*:*VirtualMap* 的字节大小; 938 | 939 | *DescriptorSize*:*VirtualMap* 中 *entry* 的大小(以字节为单位); 940 | 941 | *DescriptorVersion*:*VirtualMap* 中结构 *entry* 的版本; 942 | 943 | *VirtualMap*:包含所有运行时范围的新虚拟地址映射信息的内存描述符数组。`EFI_MEMORY_DESCRIPTOR` 类型在 `EFI_BOOT_SERVICES.GetMemoryMap()` 函数描述中定义。 944 | 945 | - 描述(Description) 946 | 947 | OS 加载程序使用 `SetVirtualAddressMap()` 函数。该函数只能在运行时调用,并由系统内存映射的所有者调用:即调用 `EFI_BOOT_SERVICES.ExitBootServices()` 的组件。所有类型为 `EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE` 的事件都必须在 `SetVirtualAddressMap()` 返回之前发出信号。 948 | 949 | 此调用将 EFI 固件的运行时组件的地址更改为 *VirtualMap* 中提供的新虚拟地址。提供的 *VirtualMap* 必须在 `ExitBootServices()` 处为内存映射中的每个条目提供一个新的虚拟地址,这些条目被标记为需要运行时使用。*VirtualMap* 中的所有虚拟地址字段都必须在 4 KiB 边界上对齐。 950 | 951 | 必须使用物理映射来调用 `SetVirtualAddressMap()`。从此函数成功返回后,系统必须使用新分配的虚拟映射进行任何未来调用。所有地址空间映射都必须根据原始地址映射中指定的可缓存性标志来完成。 952 | 953 | 调用此函数时,将通知所有注册为在地址映射更改时发出信号的事件。收到通知的每个组件必须更新其新地址的任何内部指针。这可以通过 `ConvertPointer()` 函数来完成。通知所有事件后,EFI 固件会重新应用映像“修复”信息,以虚拟方式将所有运行时映像重新定位到它们的新地址。此外,EFI 运行时服务表中除 *SetVirtualAddressMap* 和 *ConvertPointer* 之外的所有字段都必须使用 `ConvertPointer()` 服务从物理指针转换为虚拟指针。`SetVirtualAddressMap()` 和 `ConvertPointer()` 服务只能在物理模式下调用,因此不需要将它们从物理指针转换为虚拟指针。EFI 系统表的几个字段必须使用 `ConvertPointer()` 服务从物理指针转换为虚拟指针。这些字段包括 *FirmwareVendor*、*RuntimeServices* 和 *ConfigurationTable*。由于 EFI 运行时服务表和 EFI 系统表的内容都被该服务修改,因此必须重新计算 EFI 运行时服务表和 EFI 系统表的 32 位 CRC。 954 | 955 | 虚拟地址映射只能应用一次。一旦运行时系统处于虚拟模式,对该函数的调用将返回 `EFI_UNSUPPORTED`。 956 | 957 | - 返回的状态码(Status Codes Returned) 958 | 959 | ![table8-6_1](../pic/table8-6_1.jpg) 960 | 961 | **ConvertPointer()** 962 | 963 | - 概要(Summary) 964 | 965 | 确定要在后续内存访问中使用的新虚拟地址。 966 | 967 | - 原型(Prototype) 968 | 969 | ```c 970 | typedef EFI_STATUS ConvertPointer ( 971 | IN UINTN DebugDisposition, 972 | IN VOID **Address 973 | ); 974 | ``` 975 | 976 | - 参数(Parameters) 977 | 978 | *DebugDisposition*:为正在转换的指针提供类型信息。参见“相关定义”。 979 | 980 | *Address*:指向要固定为正在应用的新虚拟地址映射所需的值的指针的指针。 981 | 982 | - 相关定义(Related Definitions) 983 | 984 | ```c 985 | //******************************************************* 986 | // EFI_OPTIONAL_PTR 987 | //******************************************************* 988 | #define EFI_OPTIONAL_PTR 0x00000001 989 | ``` 990 | 991 | - 描述(Description) 992 | 993 | EFI 组件在 `SetVirtualAddressMap()` 操作期间使用 `ConvertPointer()` 函数。在执行 `SetVirtualAddressMap()` 期间,必须使用物理地址指针调用 `ConvertPointer()`。 994 | 995 | `ConvertPointer()` 函数将 *Address* 指向的当前指针更新为新地址映射的正确值。只有运行时组件需要执行此操作。`EFI_BOOT_SERVICES.CreateEvent()` 函数用于创建一个事件,该事件将在地址映射发生变化时被通知。必须更新组件已分配或指定的所有指针。 996 | 997 | 如果指定了 `EFI_OPTIONAL_PTR` 标志,则允许被转换的指针为 `NULL`。 998 | 999 | 一旦所有组件都收到地址映射更改的通知,固件就会修复嵌入任何运行时映像中的任何编译指针。 1000 | 1001 | - 返回的状态码(Status Codes Returned) 1002 | 1003 | ![table8-6_2](../pic/table8-6_2.jpg) 1004 | 1005 | ## 杂项运行时服务(Miscellaneous Runtime Services) 1006 | 1007 | 本节包含其他地方未定义但需要完成 EFI 环境定义的运行时服务的其余函数定义。Table 8-7 列出了杂项运行时服务。 1008 | 1009 | ![table8-7 Miscellaneous Runtime Services](../pic/table8-7.jpg) 1010 | 1011 | ### 重置系统(Reset System) 1012 | 1013 | **ResetSystem()** 1014 | 1015 | - 概要(Summary) 1016 | 1017 | 重置整个平台。如果平台支持 `EFI_RESET_NOTIFICATION_PROTOCOL`,则在完成平台重置之前,必须调用所有挂起的通知。 1018 | 1019 | - 原型(Prototype) 1020 | 1021 | ```c 1022 | typedef VOID (EFIAPI *EFI_RESET_SYSTEM) ( 1023 | IN EFI_RESET_TYPE ResetType, 1024 | IN EFI_STATUS ResetStatus, 1025 | IN UINTN DataSize, 1026 | IN VOID *ResetData OPTIONAL 1027 | ); 1028 | ``` 1029 | 1030 | - 参数(Parameters) 1031 | 1032 | *ResetType*:要执行的重置类型。`EFI_RESET_TYPE` 类型在下面的“相关定义”中定义; 1033 | 1034 | *ResetStatus*:重置的状态代码。如果系统重置是正常操作的一部分,则状态代码将为 `EFI_SUCCESS`。如果系统重置是由于某种类型的故障,将使用最合适的 EFI 状态代码; 1035 | 1036 | *DataSize*:*ResetData* 的大小(以字节为单位); 1037 | 1038 | *ResetData*:对于 *EfiResetCold*、*EfiResetWarm* 或 *EfiResetShutdown* 的 *ResetType*,数据缓冲区以 `Null` 终止字符串开头,后面可以选择附加二进制数据。该字符串是调用者可以用来进一步指示系统重置原因的描述。对于 *EfiResetPlatformSpecific* 的 *ResetType*,数据缓冲区也以 `Null` 终止的字符串开头,后跟描述要执行的特定重置类型的 `EFI_GUID`。 1039 | 1040 | - 相关定义(Related Definitions) 1041 | 1042 | ```c 1043 | //******************************************************* 1044 | // EFI_RESET_TYPE 1045 | //******************************************************* 1046 | typedef enum { 1047 | EfiResetCold, 1048 | EfiResetWarm, 1049 | EfiResetShutdown, 1050 | EfiResetPlatformSpecific 1051 | } EFI_RESET_TYPE; 1052 | ``` 1053 | 1054 | - 描述(Description) 1055 | 1056 | `ResetSystem()` 函数重置整个平台,包括所有处理器和设备,并重新启动系统。 1057 | 1058 | 使用 *EfiResetCold* 的 *ResetType* 调用此接口会导致系统范围的重置。这会将系统内的所有电路设置为其初始状态。这种类型的复位与系统操作异步,并且在不考虑周期边界的情况下进行操作。*EfiResetCold* 等同于系统电源循环。 1059 | 1060 | 使用 *EfiResetWarm* 的 *ResetType* 调用此接口会导致系统范围的初始化。处理器被设置为它们的初始状态,挂起的周期没有被破坏。如果系统不支持此重置类型,则必须执行 *EfiResetCold*。 1061 | 1062 | 使用 *EfiResetShutdown* 的 *ResetType* 调用此接口会导致系统进入等效于 ACPI G2/S5 或 G3 状态的电源状态。如果系统不支持这种重置类型,那么当系统重新启动时,它应该显示 *EfiResetCold* 属性。 1063 | 1064 | 使用 *EfiResetPlatformSpecific* 的 *ResetType* 调用此接口会导致系统范围的重置。重置的确切类型由 `EFI_GUID` 定义,该 `EFI_GUID` 跟随传递到 *ResetData* 的 `Null` 终止的 *Unicode* 字符串。如果平台无法识别 *ResetData* 中的 `EFI_GUID`,则平台必须选择支持的重置类型来执行。该平台可以选择记录来自发生的任何非正常重置的参数。 1065 | 1066 | `ResetSystem()` 函数不返回。 1067 | 1068 | ### 获取下一个高单调计数(Get Next High Monotonic Count) 1069 | 1070 | 本节介绍 *GetNextHighMonotonicCount* 运行时服务及其关联的数据结构。 1071 | 1072 | **GetNextHighMonotonicCount()** 1073 | 1074 | - 概要(Summary) 1075 | 1076 | 返回平台单调计数器的下一个高 32 位。 1077 | 1078 | - 原型(Prototype) 1079 | 1080 | ```c 1081 | typedef EFI_STATUS GetNextHighMonotonicCount ( 1082 | OUT UINT32 *HighCount 1083 | ); 1084 | ``` 1085 | 1086 | - 参数(Parameters) 1087 | 1088 | *HighCount*:指向返回值的指针。 1089 | 1090 | - 描述(Description) 1091 | 1092 | `GetNextHighMonotonicCount()` 函数返回平台单调计数器的下一个高 32 位。 1093 | 1094 | 该平台的单调计数器由两个 32 位量组成:高 32 位和低 32 位。在引导服务期间,低 32 位值是易变的:它在每次系统重置时重置为零,并在每次调用 `GetNextMonotonicCount()` 时增加 1。高 32 位值是非易失性的,每当系统重置、调用 `GetNextHighMonotonicCount()` 或低 32 位计数(由 `GetNextMonoticCount()` 返回)溢出时都会增加 1。 1095 | 1096 | `EFI_BOOT_SERVICES.GetNextMonotonicCount()` 函数仅在引导服务时可用。如果操作系统希望将平台单调计数器扩展到运行时,它可以通过使用 `GetNextHighMonotonicCount()` 来实现。为此,在调用 `EFI_BOOT_SERVICES.ExitBootServices()` 之前,操作系统将调用 `GetNextMonotonicCount()` 以获取当前平台单调计数。然后操作系统将提供一个接口,通过以下方式返回下一个计数: 1097 | 1098 | 1. 最后一次计数加 1; 1099 | 1100 | 2. 在计数的低 32 位溢出之前,调用 `GetNextHighMonotonicCount()`。这会将平台的单调计数的非易失性部分的高 32 位增加 1。 1101 | 1102 | 该函数只能在运行时调用。 1103 | 1104 | - 返回的状态码(Status Codes Returned) 1105 | 1106 | ![table8-7_1](../pic/table8-7_1.jpg) 1107 | 1108 | ### 更新胶囊(Update Capsule) 1109 | 1110 | 此运行时函数允许调用者将信息传递给固件。*Update Capsule* 通常用于更新固件 FLASH 或操作系统,让信息在系统重置时保持不变。 1111 | 1112 | **UpdateCapsule()** 1113 | 1114 | - 概要(Summary) 1115 | 1116 | 通过虚拟和物理映射将胶囊传递给固件。根据预期消耗量,固件可能会立即处理胶囊。如果有效负载应在系统重置期间持续存在,则必须将 `EFI_QueryCapsuleCapabilities` 返回的重置值传递到 `ResetSystem()` 并将导致胶囊作为重置过程的一部分由固件处理。 1117 | 1118 | - 原型(Prototype) 1119 | 1120 | ```c 1121 | typedef EFI_STATUS UpdateCapsule ( 1122 | IN EFI_CAPSULE_HEADER **CapsuleHeaderArray, 1123 | IN UINTN CapsuleCount, 1124 | IN EFI_PHYSICAL_ADDRESS ScatterGatherList OPTIONAL 1125 | ); 1126 | ``` 1127 | 1128 | - 参数(Parameters) 1129 | 1130 | *CapsuleHeaderArray*:指向被传递到更新胶囊的胶囊的虚拟指针数组的虚拟指针。假定每个胶囊存储在连续的虚拟内存中。*CapsuleHeaderArray* 中的胶囊必须与 *ScatterGatherList* 中的胶囊相同。*CapsuleHeaderArray* 必须具有与 *ScatterGatherList* 相同顺序的胶囊。 1131 | 1132 | *CapsuleCount*:*CapsuleHeaderArray* 中指向 `EFI_CAPSULE_HEADER` 的指针数。 1133 | 1134 | *ScatterGatherList*:指向一组 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 的物理指针,描述一组胶囊在物理内存中的位置。请参阅“相关定义”以了解如何通过此接口传递一个以上的胶囊。*ScatterGatherList* 中的胶囊必须与 *CapsuleHeaderArray* 的顺序相同。仅当 *capsules* 被定义为在系统重置时持续存在时才引用此参数。 1135 | 1136 | - 相关定义(Related Definitions) 1137 | 1138 | ```c 1139 | typedef struct { 1140 | UINT64 Length; 1141 | union { 1142 | EFI_PHYSICAL_ADDRESS DataBlock; 1143 | EFI_PHYSICAL_ADDRESS ContinuationPointer; 1144 | } Union; 1145 | } EFI_CAPSULE_BLOCK_DESCRIPTOR; 1146 | ``` 1147 | 1148 | *Length*:`DataBlock/ContinuationPointer` 指向的数据的字节长度; 1149 | 1150 | *DataBlock*:数据块的物理地址。如果 *Length* 不等于零,则使用该联合成员; 1151 | 1152 | *ContinuationPointer*:另一个 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 结构块的物理地址。如果 *Length* 等于零,则使用该联合成员。如果 *ContinuationPointer* 为零,则此 *entry* 表示列表的末尾。 1153 | 1154 | - 相关定义(Related Definitions) 1155 | 1156 | 此数据结构定义操作系统传递给固件的 *ScatterGatherList* 列表。*ScatterGatherList* 表示一个结构体数组,以长度为 0 且 *DataBlock* 物理地址为 0 的结构体成员结束。如果 *Length* 为 0 且 *DataBlock* 物理地址不为 0,则指定的物理地址称为“连续指针”,它指向 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 结构的进一步列表。连续指针用于允许分散收集列表包含在不连续的物理内存中。它还用于允许一次通过多个胶囊。 1157 | 1158 | ```c 1159 | typedef struct { 1160 | EFI_GUID CapsuleGuid; 1161 | UINT32 HeaderSize; 1162 | UINT32 Flags; 1163 | UINT32 CapsuleImageSize; 1164 | } EFI_CAPSULE_HEADER; 1165 | ``` 1166 | 1167 | *CapsuleGuid*:定义胶囊内容的 GUID; 1168 | 1169 | *HeaderSize*:胶囊标头的大小。这可能大于 `EFI_CAPSULE_HEADER` 的大小,因为 *CapsuleGuid* 可能暗示扩展的标头 *entry*; 1170 | 1171 | *Flags*:标志位[15:0] 由 CapsuleGuid 定义,标志位[31:16] 由本规范定义; 1172 | 1173 | *CapsuleImageSize*:胶囊的字节大小(包括胶囊标头)。 1174 | 1175 | ```c 1176 | #define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000 1177 | #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 1178 | #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 1179 | ``` 1180 | 1181 | 注:具有 `CAPSULE_FLAGS_INITIATE_RESET` 标志的胶囊必须在其标头中也设置 `CAPSULE_FLAGS_PERSIST_ACROSS_RESET`。遇到在其标头中设置了 `CAPSULE_FLAGS_INITIATE_RESET` 标志的胶囊的固件将启动与传入的胶囊请求兼容的平台重置,并且不会返回给调用者。 1182 | 1183 | ```c 1184 | typedef struct { 1185 | UINT32 CapsuleArrayNumber; 1186 | VOID* CapsulePtr[1]; 1187 | } EFI_CAPSULE_TABLE; 1188 | ``` 1189 | 1190 | *CapsuleArrayNumber*:胶囊数组中的 *entry* 数; 1191 | 1192 | *CapsulePtr*:指向包含相同 *CapsuleGuid* 值的胶囊数组的指针。每个 *CapsulePtr* 都指向 `EFI_CAPSULE_HEADER` 的一个实例,胶囊数据连接在其末端。 1193 | 1194 | - 描述(Description) 1195 | 1196 | `UpdateCapsule()` 函数允许操作系统将信息传递给固件。`UpdateCapsule()` 函数支持将操作系统虚拟内存中的胶囊传递回固件。每个 *capsule* 都包含在操作系统的连续虚拟内存范围内,但 *capsules* 的虚拟和物理映射都会传递给固件。 1197 | 1198 | 如果 *capsule* 在其标头设置了 `CAPSULE_FLAGS_PERSIST_ACROSS_RESET` 标志,则固件将在系统重置后处理 *capsules*。调用者必须确保使用从 *QueryCapsuleCapabilities* 获得的所需重置值来重置系统。如果未设置此标志,固件将立即处理胶囊。 1199 | 1200 | 具有 `CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE` 标志的胶囊必须在其标头中也设置 `CAPSULE_FLAGS_PERSIST_ACROSS_RESET`。 1201 | 1202 | 处理在其标头中设置了 CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 标志的胶囊的固件会将胶囊的内容从 *ScatterGatherList* 合并到一个连续的缓冲区中,然后必须在系统重置后在 EFI 系统表中放置指向该合并胶囊的指针。搜索此胶囊的代理将在 `EFI_CONFIGURATION_TABLE` 中查找并搜索胶囊的 GUID 和关联的指针以在重置后检索数据。 1203 | 1204 | ![table8-8 Flag Firmware Behavior](../pic/table8-8.jpg) 1205 | 1206 | EFI 系统表条目必须使用 `EFI_CAPSULE_HEADER` 的 *CapsuleGuid* 字段中的 GUID。EFI 系统表 *entry* 必须指向包含相同 *CapsuleGuid* 值的胶囊数组。该数组必须以表示胶囊数组大小的 UINT32 为前缀。 1207 | 1208 | 这组胶囊由 *ScatterGatherList* 和 *CapsuleHeaderArray* 指向,因此固件将知道操作系统分配的缓冲区的物理地址和虚拟地址。*scatter-gather* 列表支持 *capsule* 的虚拟地址范围是连续的,但物理地址不连续的情况。 1209 | 1210 | 在禁用内存管理单元时,处理器的主内存视图与缓存不一致的架构上,`UpdateCapsule()` 的调用方必须在调用 `UpdateCapsul()` 之前对每个 *ScatterGatherList* 元素的主内存执行缓存维护。此要求仅在操作系统调用 `ExitBootServices()` 后适用。 1211 | 1212 | 如果传入此函数的任何胶囊遇到错误,则不会处理整组胶囊,并将遇到的错误返回给调用者。 1213 | 1214 | - 返回的状态码(Status Codes Returned) 1215 | 1216 | ![table8-8_1](../pic/table8-8_1.jpg) 1217 | 1218 | #### 胶囊定义(Capsule Definition) 1219 | 1220 | 胶囊只是一组以 `EFI_CAPSULE_HEADER` 开头的连续数据。标头中的 *CapsuleGuid* 字段定义了胶囊的格式。 1221 | 1222 | 胶囊内容旨在从存在操作系统的环境与系统固件进行通信。为了允许 *capsule* 在系统重置时持续存在,*capsule* 的描述需要一定程度的间接性,因为操作系统主要使用虚拟内存,而固件在启动时使用物理内存。这个抽象级别是通过 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 完成的。`EFI_CAPSULE_BLOCK_DESCRIPTOR` 允许操作系统分配连续的虚拟地址空间,并将该地址空间描述为一组不连续的物理地址范围给固件。向固件传递物理地址和虚拟地址以及指针来描述胶囊,因此固件可以立即处理胶囊或将胶囊的处理推迟到系统重置之后。 1223 | 1224 | 在大多数指令集和操作系统架构中,物理内存的分配只能在“页面”粒度(范围从 4 KiB 到至少 1 MiB)上进行。`EFI_CAPSULE_BLOCK_DESCRIPTOR` 必须具有以下属性以确保数据的安全和定义良好的转换: 1225 | 1226 | - 每个新胶囊必须从新的内存页开始; 1227 | 1228 | - 除最后一页外,所有页面都必须被胶囊完全填满; 1229 | 1230 | - 填充标头以使其消耗整页数据以允许通过胶囊传递页面对齐的数据结构是合法的。最后一页必须至少有一个字节的胶囊; 1231 | 1232 | - 页面必须自然对齐; 1233 | 1234 | - 页面不能重叠; 1235 | 1236 | - 固件可能永远不会假设操作系统正在使用的页面大小。 1237 | 1238 | 多个胶囊可以连接在一起,并通过一次调用 `UpdateCapsule()` 传递。胶囊的物理地址描述通过将第一个胶囊的终止 `EFI_CAPSULE_BLOCK_DESCRIPTOR` *entry* 转换为一个延续指针,使其指向代表第二个胶囊开始的 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 来连接。只有一个终止 `EFI_CAPSULE_BLOCK_DESCRIPTOR` *entry*,它位于链中最后一个胶囊的末尾。 1239 | 1240 | 必须使用以下算法在单个分散收集列表中查找多个胶囊: 1241 | 1242 | - 查看 *capsule* 标头以确定 *capsule* 的大小; 1243 | 1244 | - 第一个 *Capsule* 标头始终由第一个 `EFI_CAPSULE_BLOCK_DESCRIPTOR` *entry* 指向; 1245 | 1246 | - 遍历 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 列表,保持每个 *entry* 代表的大小的运行计数; 1247 | 1248 | - 如果 `EFI_CAPSULE_BLOCK_DESCRIPTOR` *entry* 是一个延续指针,并且正在运行的当前胶囊大小计数大于或等于当前胶囊的大小,则这是下一个胶囊的开始; 1249 | 1250 | - 使新胶囊成为当前胶囊并重复该算法。 1251 | 1252 | Figure 8-1 显示了描述两个胶囊的 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 结构的 *Scatter-Gather* 列表。图的左侧显示了胶囊的操作系统视图,作为两个独立的连续虚拟内存缓冲区。图中的中心显示了系统内存中数据的布局。该图的右侧显示了传递到固件中的 *ScatterGatherList* 列表。由于有两个胶囊,因此存在两个独立的 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 列表,它们通过第一个列表中的延续指针连接在一起。 1253 | 1254 | ![Figure 8-1 Scatter-Gather List of EFI_CAPSULE_BLOCK_DESCRIPTOR Structures](../pic/8-1.jpg) 1255 | 1256 | **EFI_MEMORY_RANGE_CAPSULE_GUID** 1257 | 1258 | 这种胶囊结构定义提供了一种方法,第三方组件(例如操作系统)可以通过这种方法向固件描述内存中的哪些区域在下一次重置时应该保持不变。 1259 | 1260 | 对此 *capsule* 的支持是可选的。对于支持此胶囊的平台,它们必须使用 `EFI_MEMORY_RANGE_CAPSULE_GUID` 作为 GUID/指针对中的 GUID 在 EFI 配置表中公布 `EFI_MEMORY_RANGE_CAPSULE`。 1261 | 1262 | ```c 1263 | // {0DE9F0EC-88B6-428F-977A-258F1D0E5E72} 1264 | #define EFI_MEMORY_RANGE_CAPSULE_GUID \ 1265 | { 0xde9f0ec, 0x88b6, 0x428f, \ 1266 | { 0x97, 0x7a, 0x25, 0x8f, 0x1d, 0xe, 0x5e, 0x72 } } 1267 | ``` 1268 | 1269 | 内存范围描述符。 1270 | 1271 | ```c 1272 | typedef struct { 1273 | EFI_PHYSICAL_ADDRESS Address; 1274 | UINT64 Length 1275 | } EFI_MEMORY_RANGE; 1276 | ``` 1277 | 1278 | *Address*:正在描述的内存位置的物理地址。 1279 | 1280 | *Length*:以字节为单位的长度。 1281 | 1282 | 描述平台固件应保持不变的内存范围的胶囊描述符。 1283 | 1284 | ```c 1285 | typedef struct { 1286 | EFI_CAPSULE_HEADER Header; 1287 | UINT32 OsRequestedMemoryType; 1288 | UINT64 NumberOfMemoryRanges; 1289 | EFI_MEMORY_RANGE MemoryRanges[] 1290 | } EFI_MEMORY_RANGE_CAPSULE; 1291 | ``` 1292 | 1293 | *Header*:`Header.CapsuleGuid = EFI_MEMORY_RANGE_CAPSULE_GUI`,`Header.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET`。 1294 | 1295 | *OsRequestedMemoryType*:必须在 `0x80000000-0xFFFFFFFF` 范围内。当 UEFI 固件处理胶囊时,MemoryRanges[] 中描述的内容将在 EFI 内存映射中显示为 *OsRequestedMemoryType* 值。 1296 | 1297 | *NumberofMemoryRanges*:MemoryRanges[] *entry* 的数量。必须是 1 或更大的值。 1298 | 1299 | *MemoryRanges[]*:内存范围数组。等同于 MemoryRanges[NumberOfMemoryRanges]。 1300 | 1301 | 对于打算支持 `EFI_MEMORY_RANGE_CAPSULE` 的平台,它必须使用 `EFI_MEMORY_RANGE_CAPSULE_GUID` 作为 GUID/指针对中的 GUID 在 EFI 配置表中公布 `EFI_MEMORY_RANGE_CAPSULE_RESULT`。 1302 | 1303 | ```c 1304 | typedef struct { 1305 | UINT64 FirmwareMemoryRequirement; 1306 | UINT64 NumberOfMemoryRanges 1307 | } EFI_MEMORY_RANGE_CAPSULE_RESULT 1308 | ``` 1309 | 1310 | *FirmwareMemoryRequirement*:UEFI 固件初始化所需的最大内存量(以字节为单位)。 1311 | 1312 | *NumberofMemoryRanges*:如果没有处理 `EFI_MEMORY_RANGE_CAPSULE`,则为 0。如果处理了 `EFI_MEMORY_RANGE_CAPSULE`,则此数字将与 `EFI_MEMORY_RANGE_CAPSULE.NumberOfMemoryRanges` 值相同。 1313 | 1314 | **QueryCapsuleCapabilities()** 1315 | 1316 | - 概要(Summary) 1317 | 1318 | 返回是否可以通过 `UpdateCapsule()` 支持胶囊。 1319 | 1320 | - 原型(Prototype) 1321 | 1322 | ```c 1323 | typedef EFI_STATUS QueryCapsuleCapabilities { 1324 | IN EFI_CAPSULE_HEADER **CapsuleHeaderArray, 1325 | IN UINTN CapsuleCount, 1326 | OUT UINT64 *MaximumCapsuleSize, 1327 | OUT EFI_RESET_TYPE *ResetType 1328 | }; 1329 | ``` 1330 | 1331 | - 参数(Parameters) 1332 | 1333 | *CapsuleHeaderArray*:指向被传递到更新胶囊的胶囊的虚拟指针数组的虚拟指针。假定胶囊存储在连续的虚拟内存中。 1334 | 1335 | *CapsuleCount*:*CapsuleHeaderArray* 中指向 `EFI_CAPSULE_HEADER` 的指针数。 1336 | 1337 | *MaximumCapsuleSize*:在输出时,`UpdateCapsule()` 可以通过 *CapsuleHeaderArray* 和 *ScatterGatherList* 作为 `UpdateCapsule()` 的参数支持的最大大小(以字节为单位)。输入未定义。 1338 | 1339 | *ResetType*:返回胶囊更新所需的重置类型。输入未定义。 1340 | 1341 | - 描述(Description) 1342 | 1343 | `QueryCapsuleCapabilities()` 函数允许调用者测试是否可以通过 `UpdateCapsule()` 更新一个或多个胶囊。检查胶囊标头中的标志值和整个胶囊的大小。 1344 | 1345 | 如果调用者需要查询通用胶囊功能,则可以构造一个伪造的 `EFI_CAPSULE_HEADER`,其中 *CapsuleImageSize* 等于 *HeaderSize*,即等于 sizeof (EFI_CAPSULE_HEADER)。要确定重置要求,应在 `EFI_CAPSULE_HEADER` 的标志字段中设置 `CAPSULE_FLAGS_PERSIST_ACROSS_RESET`。 1346 | 1347 | - 返回的状态码(Status Codes Returned) 1348 | 1349 | ![table8-8_2](../pic/table8-8_2.jpg) 1350 | 1351 | #### 在操作系统和固件之间交换信息(Exchanging information between the OS and Firmware) 1352 | 1353 | 固件和操作系统可以通过 *OsIndicationsSupported* 和 *OSIndications* 变量交换信息,如下所示: 1354 | 1355 | - *OsIndications* 变量返回操作系统拥有的 UINT64 位掩码,用于指示操作系统希望固件启用哪些功能或操作系统希望固件执行哪些操作。操作系统将通过 `SetVariable()` 调用提供此数据; 1356 | 1357 | - *OsIndicationsSupported* 变量返回固件拥有的 UINT64 位掩码,并指示固件支持哪些操作系统指示功能和操作。这个变量在每次启动时由固件重新创建,并且不能被操作系统修改。 1358 | 1359 | 如果固件支持操作系统请求在固件用户界面停止,则固件可以在 *OsIndicationsSupported* 变量中设置 `EFI_OS_INDICATIONS_BOOT_TO_FW_UI` 位。如果操作系统希望固件在下次启动时在固件用户界面停止,则 `EFI_OS_INDICATIONS_BOOT_TO_FW_UI` 位可以由操作系统在 *OsIndications* 变量中设置。一旦固件使用 *OsIndications* 变量中的该位并在固件用户界面停止,固件应从 *OsIndications* 变量中清除该位,以便向操作系统确认信息已被使用,更重要的是,防止固件用户 在后续启动时再次显示界面。 1360 | 1361 | 如果固件支持基于时间戳的撤销和“dbt”授权时间戳数据库变量,则固件可以在 *OSIndicationsSupported* 变量中设置 `EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION` 位。 1362 | 1363 | 如果平台支持处理 **Section 23.2** 中定义的固件管理协议更新胶囊,则 `EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED` 位在 *OsIndicationsSupported* 变量中设置。如果在 *OsIndications* 变量中设置,则 `EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED` 位没有任何功能,并在下次重新启动时被清除。 1364 | 1365 | 如果平台支持根据 **Section 8.5.5** 处理文件胶囊,则设置 *OsIndicationsSupported* 变量中的 `EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED` 位。 1366 | 1367 | 当通过 **Section 8.5.5** 的大容量存储设备方法提交胶囊时,*OsIndications* 变量中的 `EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED` 位必须由提交者设置,以在下次重启时触发提交的胶囊处理。在重启后的处理过程中,系统固件会在所有情况下从 *OsIndications* 中清除该位。 1368 | 1369 | `EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED` 位在 *OsIndicationsSupported* 变量中设置,如果平台支持通过创建 **Section 8.5.6** 中定义的结果变量来报告延迟胶囊处理。如果在 *OsIndications* 中设置,则此位无效。 1370 | 1371 | 如果平台既支持操作系统指示重新启动时应开始操作系统定义的恢复的能力,也支持短格式文件路径媒体设备路径,则在 *OsIndicationsSupported* 变量中设置 `EFI_OS_INDICATIONS_START_OS_REVERY` 位(参见 **Section 3.1.2**)。如果在 *OsIndications* 中设置了该位,则平台固件必须在引导期间绕过 *BootOrder* 变量的处理,并直接跳到操作系统定义的恢复(参见 **Section 3.4.1**),然后是平台定义的恢复(参见 **Section 3.4.2**)。系统固件在启动操作系统定义的恢复时必须清除 *OsIndications* 中的该位。 1372 | 1373 | 如果平台既支持操作系统指示重新启动时应开始平台定义的恢复的能力,也支持短格式文件路径媒体设备路径,则在 *OsIndicationsSupported* 变量中设置 `EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY` 位(参见 **Section 3.1.2**)。如果在 *OsIndications* 中设置了该位,则平台固件必须在引导期间绕过 *BootOrder* 变量的处理,并直接跳到平台定义的恢复(参见 **Section 3.4.2**)。系统固件在启动平台定义的恢复时必须清除 *OsIndications* 中的该位。 1374 | 1375 | 在所有情况下,如果在 *OsIndicationsSupported* 中设置了 `EFI_OS_INDICATIONS_START_OS_RECOVERY` 或 `EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY` 之一,则两者都必须设置并受支持。 1376 | 1377 | `EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH` 位由提交者在 *OsIndications* 变量中设置,以触发收集当前配置并在下次启动时将刷新的数据报告给 EFI 系统配置表。如果未设置,平台将不会收集当前配置,而是将缓存的配置数据报告给 EFI 系统配置表。配置数据应使用 **Section 23.5.2** 中定义的 `EFI_JSON_CAPSULE_CONFIG_DATA` 格式安装到 EFI 系统配置表。一旦报告了刷新的数据,该位将被系统固件从 *OsIndications* 中清除。 1378 | 1379 | 如果在 *OsIndicationsSupported* 变量中设置,则 `EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH` 位没有任何功能,并在下次重新启动时被清除。 1380 | 1381 | - 相关定义(Related Definitions) 1382 | 1383 | ```c 1384 | #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001 1385 | #define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION 0x0000000000000002 1386 | #define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED 0x0000000000000004 1387 | #define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED 0x0000000000000008 1388 | #define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED 0x0000000000000010 1389 | #define EFI_OS_INDICATIONS_START_OS_RECOVERY 0x0000000000000020 1390 | #define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY 0x0000000000000040 1391 | #define EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH 0x0000000000000080 1392 | ``` 1393 | 1394 | #### 通过大容量存储设备上的文件交付胶囊(Delivery of Capsules via file on Mass Storage device) 1395 | 1396 | 作为 `UpdateCapsule()` 运行时 API 的替代方案,平台支持的任何类型的胶囊也可以通过目标为引导的大容量存储设备上的 EFI 系统分区内的文件传送到固件。使用此方法暂存的胶囊将在下次系统重新启动时处理。此方法仅在从使用 GPT 格式化(参见 **Section 5.3**)并且在设备映像中包含 EFI 系统分区的大容量存储设备引导时可用。当 *OsIndications* 中的 `EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED` 位设置时,系统固件将搜索胶囊,如 **Section 8.5.4** 所述。 1397 | 1398 | 活动 EFI 系统分区中的目录 `\EFI\UpdateCapsule`(忽略字母大小写)被定义用于将封装体传送到固件。 1399 | 1400 | 大容量存储设备上的胶囊文件的二进制结构与通过 EFI RunTime API 传送的胶囊内容相同,只是不支持使用 `EFI_CAPSULE_BLOCK_DESCRIPTOR` 进行分段,并且单个胶囊必须存储在以 `EFI_CAPSULE_HEADER` 开头的文件中的连续字节中。文件的大小必须等于 `EFI_CAPSULE_HEADER.CapsuleImageSize` 否则将生成错误并忽略胶囊。在一个文件中只能提交带有单个 `EFI_CAPSULE_HEADER` 的单个胶囊,但在单次重启期间可以提交多个文件,每个文件都包含一个胶囊。 1401 | 1402 | 压缩包的文件名应由提交者使用适合 EFI 系统分区文件系统的 8 位 ASCII 字符选择(第 13.3.1 节)。检查和处理放置在该目录中的文件后,固件将(如果可能)删除该文件。删除在成功处理的情况下执行,也在错误的情况下执行,但未能成功删除本身不是可报告的错误。 1403 | 1404 | 指定目录中可以存储多个胶囊文件,每个胶囊文件包含一个胶囊图像。在多个文件的情况下,系统固件将使用基于文件名字符的 CHAR16 数值排序的字母顺序处理文件,从左到右进行比较。小写字母字符将在比较前转换为大写字母。比较不等长的文件名时,应使用空格字符来填充较短的文件名。如果文件名包含一个或多个句点字符(.),则文件名中最右边的句点和最右边的句点右侧的文本将在比较前被删除。如果文件名在排除最右边的句点之后的任何文本后具有相同的文本,则处理顺序将通过在文件名字符串中最右边的句点右侧找到的任何文本的排序来确定。 1405 | 1406 | 如果胶囊处理因错误而终止,则将正常处理任何剩余的附加胶囊文件。 1407 | 1408 | 目录 `\EFI\UpdateCapsule` 仅在活动引导选项中指定的设备上的 EFI 系统分区内检查胶囊,通过引用 *BootNext* 变量或 *BootOrder* 变量处理确定。活动引导变量是具有最高优先级 *BootNext* 或 *BootOrder* 中的变量,它指的是发现存在的设备。*BootOrder* 中但引用不存在的设备的引导变量在确定活动引导变量时将被忽略。 1409 | 1410 | 要检查 `\EFI\UpdateCapsule` 的设备通过引用所选活动 `Boot####` 变量中的 *FilePathList* 字段来标识。系统固件不需要检查不包含最高引导优先级的引导目标的大容量存储设备,也不需要检查第二个 EFI 系统分区而不是活动引导变量的目标。 1411 | 1412 | 在所有情况下,一个 *capsule* 被识别用于处理,系统在 *capsule* 处理完成后重新启动。如果设置了 *BootNext* 变量,则在执行胶囊处理时清除该变量,而无需指示变量的实际引导。 1413 | 1414 | #### UEFI 变量报告成功或重启后处理胶囊时遇到的任何错误(UEFI variable reporting on the Success or any Errors encountered in processing of capsules after restart) 1415 | 1416 | 如果 *capsule* 的处理是(1)通过调用 `UpdateCapsule()` API 传递,但延迟到下次重新启动,或者(2)当胶囊通过大容量存储设备传递时,固件会创建 UEFI 变量,以向 *capsule* 提供商指示胶囊处理的状态。 1417 | 1418 | 在调用 `UpdateCapsule()` 时传递多个胶囊的情况下,或如 **Section 8.5.5** 所述的磁盘上的多个文件,或如 **Section 23.2** 所述当胶囊包含多个有效负载时,将为处理的每个胶囊有效载荷创建一个单独的结果变量。当计算出的变量名称已经存在时,固件将覆盖结果变量。然而,为了避免不必要地消耗系统变量存储,结果变量应该在检查结果状态后由胶囊提供者删除。 1419 | 1420 | 当整个胶囊处理发生在对 `UpdateCapsule()` 函数的调用中时,将不会使用 UEFI 变量报告。 1421 | 1422 | 报告变量属性将为 `EFI_VARIABLE_NON_VOLATILE + EFI_VARIABLE_BOOTSERVICE_ACCESS + EFI_VARIABLE_RUNTIME_ACCESS`。 1423 | 1424 | 报告变量的供应商 GUID 将为 `EFI_CAPSULE_REPORT_GUID`。报告变量的名称将是 *CapsuleNNNN*,其中 *NNNN* 是由固件选择的 4 位十六进制数。*NNNN* 的值将由固件从 *Capsule0000* 开始递增,一直持续到平台定义的最大值。 1425 | 1426 | 平台将在名为 `EFI_CAPSULE_REPORT_GUID:CapsuleMax` 的只读变量中发布平台最大值。*CapsuleMax* 的内容将是字符串“CapsuleNNNN”,其中 *NNNN* 是平台在滚动到 *Capsule0000* 之前使用的最高值。该平台还将发布在 `EFI_CAPSULE_REPORT_GUID:CapsuleLast` 中创建的最后一个变量的名称。 1427 | 1428 | 创建新的结果变量时,将覆盖任何先前具有相同名称的变量。在变量存储有限的情况下,系统固件可以选择删除最旧的报告变量以创建可用空间。如果无法释放足够的变量空间,则不会创建变量。 1429 | 1430 | ![Table 8-9 Variables Using EFI_CAPSULE_REPORT_GUID](../pic/table8-9.jpg) 1431 | 1432 | **EFI_CAPSULE_REPORT_GUID** 1433 | 1434 | ```c 1435 | // {39B68C46-F7FB-441B-B6EC-16B0F69821F3} 1436 | #define EFI_CAPSULE_REPORT_GUID \ 1437 | { 0x39b68c46, 0xf7fb, 0x441b, \ 1438 | {0xb6, 0xec, 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3 }}; 1439 | ``` 1440 | 1441 | **胶囊处理结果变量的结构(Structure of the Capsule Processing Result Variable)** 1442 | 1443 | 胶囊处理结果变量的内容总是以 `EFI_CAPSULE_RESULT_VARIABLE_HEADER` 结构开头。*CapsuleGuid* 的值决定了结果变量内容实例中可能跟随的任何附加数据。对于 *CapsuleGuid* 的某些值,可能无法定义其他数据。 1444 | 1445 | 如下所述,**VariableTotalSize** 是完整结果变量的大小,包括整个标头和特定 *CapsuleGuid* 类型所需的任何其他数据。 1446 | 1447 | ```c 1448 | typedef struct { 1449 | UINT32 VariableTotalSize; 1450 | UINT32 Reserved; //for alignment 1451 | EFI_GUID CapsuleGuid; 1452 | EFI_TIME CapsuleProcessed; 1453 | EFI_STATUS CapsuleStatus 1454 | } EFI_CAPSULE_RESULT_VARIABLE_HEADER; 1455 | ``` 1456 | 1457 | *VariableTotalSize*:变量的大小(以字节为单位),包括 *CapsuleGuid* 指定的标头之外的任何数据。 1458 | 1459 | *CapsuleGuid*:来自 `EFI_CAPSULE_HEADER` 的引导。 1460 | 1461 | *CapsuleProcessed*:处理完成时使用系统时间的 *TimeStamp*。 1462 | 1463 | *CapsuleStatus*:胶囊处理的结果。任何错误代码的准确解释可能取决于处理的胶囊类型。 1464 | 1465 | **CapsuleGuid 为 EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID 时的附加结构(Additional Structure When CapsuleGuid is EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID)** 1466 | 1467 | 胶囊处理结果变量的内容总是以 `EFI_CAPSULE_RESULT_VARIABLE_HEADER` 开头。当 *CapsuleGuid* 是 `EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID` 时,标头后面是 `EFI_CAPSULE_RESULT_VARIABLE_FMP` 定义的附加数据。 1468 | 1469 | ```c 1470 | typedef struct { 1471 | UINT16 Version; 1472 | UINT8 PayloadIndex; 1473 | UINT8 UpdateImageIndex; 1474 | EFI_GUID UpdateImageTypeId 1475 | // CHAR16 CapsuleFileName[]; 1476 | // CHAR16 CapsuleTarget[]; 1477 | } EFI_CAPSULE_RESULT_VARIABLE_FMP; 1478 | ``` 1479 | 1480 | *Version*:此结构的版本,当前为 `0x00000001`。 1481 | 1482 | *PayloadIndex*:FMP 胶囊中有效载荷的索引,从零开始,经过处理以生成此报告。 1483 | 1484 | *UpdateImageIndex*:来自 `EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER` 的 *UpdateImageIndex*(从 UINT8 无符号转换为 UINT16 后)。 1485 | 1486 | *UpdateImageTypeId*:来自 `EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER` 的 *UpdateImageTypeId Guid*。 1487 | 1488 | *CapsuleFileName*:如果是从磁盘加载的胶囊,则以零结尾的数组包含已处理的胶囊文件名。如果胶囊直接提交给 `UpdateCapsule()`,则没有文件名,并且此字段需要包含一个 16 位零字符,该字符包含在 *VariableTotalSize* 中。 1489 | 1490 | *CapsuleTarget*:该字段将包含一个以零结尾的 CHAR16 字符串,其中包含设备发布固件管理协议(如果存在)的设备路径的文本表示。如果设备路径不存在并且目标不为固件所知,或者有效载荷被策略阻止或跳过,则此字段需要包含一个 16 位零字符,该字符包含在 *VariableTotalSize* 中。 1491 | 1492 | **CapsuleGuid 为 EFI_JSON_CAPSULE_ID_GUID 时的附加结构(Additional Structure When CapsuleGuid is EFI_JSON_CAPSULE_ID_GUID)** 1493 | 1494 | 胶囊处理结果变量的内容总是以 `EFI_CAPSULE_RESULT_VARIABLE_HEADER` 开头。当 *CapsuleGuid* 为 `EFI_JSON_CAPSULE_ID_GUID` 时,标头后跟 `EFI_CAPSULE_RESULT_VARIABLE_JSON` 定义的附加数据。 1495 | 1496 | ```c 1497 | typedef struct { 1498 | UINT32 Version; 1499 | UINT32 CapsuleId; 1500 | UINT32 RespLength; 1501 | UINT8 Resp[] 1502 | } EFI_CAPSULE_RESULT_VARIABLE_JSON; 1503 | ``` 1504 | 1505 | *Version*:此结构的版本,当前为 `0x00000001`。 1506 | 1507 | *CapsuleId*:处理结果记录在该变量中的胶囊的唯一标识符。`0x00000000 – 0xEFFFFFFF` – 实现保留,`0xF0000000 – 0xFFFFFFFF` – 规范保留, 1508 | `#define REDFISH_DEFINED_JSON_SCHEMA 0xF000000`,JSON 负载应符合 Redfish-defined JSON 架构,请参阅 DMTF-Redfish 规范。 1509 | 1510 | *RespLength*:*Resp* 的字节长度。 1511 | 1512 | *Resp*:可变长度缓冲区,其中包含向将 JSON 胶囊传送到系统的调用方回复的 JSON 负载。回复有效负载中使用的 JSON 模式的定义超出了本规范的范围。 1513 | 1514 | **CapsuleStatus 中返回的状态码(Status Codes Returned in CapsuleStatus)** 1515 | 1516 | ![table8-9_1](../pic/table8-9_1.jpg) 1517 | --------------------------------------------------------------------------------