{"id":875,"date":"2025-04-03T15:10:21","date_gmt":"2025-04-03T13:10:21","guid":{"rendered":"https:\/\/www.codiax.se\/?post_type=kunskapsbank&#038;p=875"},"modified":"2025-04-30T10:39:12","modified_gmt":"2025-04-30T08:39:12","slug":"booting-into-uefi-shell-2","status":"publish","type":"kunskapsbank","link":"https:\/\/www.codiax.se\/en\/knowledge-article\/booting-into-uefi-shell-2\/","title":{"rendered":"Booting into UEFI shell with GRUB"},"content":{"rendered":"\n<p>I recently had a need to update the UEFI (Unified Extensible Firmware Interface) firmware of an x86 module that one of our customers is using in their embedded device with Linux based on the Yocto project. The hardware manufacturer had provided us with a bunch of files for doing the update: The firmware binary, <code>fw.bin<\/code>, a UEFI shell script, <code>GO.nsh<\/code>, that can be called from the shell and that performs the update, a UEFI application, <code>AfuEfix64.efi<\/code>, used by <code>GO.nsh<\/code> to perform the actual programming, and a checksum file for verification.<\/p>\n\n\n\n<p>Until now I have been used to there being a built in shell in the UEFI firmware\nthat can be started during boot (commonly by pressing <code>F7<\/code> early in the boot\nprocess), and from which a script such as <code>GO.nsh<\/code> can be executed. But that is\nnot always the case. When there is no built in shell the UEFI shell binary needs\nto be provided at boot time, and started by the bootloader.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>NOTE: An alternative way of starting the UEFI shell on boot this is most\nprobably to use the UEFI boot support and bypass GRUB entirely, but I needed a\nquick fix for this GRUB booted system.<\/p>\n<\/blockquote>\n\n\n\n<p>This is a short writeup of how I solved it with GRUB and a tailored USB image. I\ndid not want to spend time creating a full blown image recipe in Yocto, with a\nwic image and all, so I took a shortcut using an already existing bootable USB\nwith a FAT32 filesystem on the first partition. It looked something like this\nfrom the start<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\n\u251c\u2500\u2500 EFI\n\u2502   \u2514\u2500\u2500 BOOT\n\u2502       \u251c\u2500\u2500 bootx64.efi\n\u2502       \u2514\u2500\u2500 grub.cfg\n\u251c\u2500\u2500 bzImage\n\u251c\u2500\u2500 initramfs-image.cpio.gz\n\u2514\u2500\u2500 microcode.cpio\n<\/code><\/pre>\n\n\n\n<p>For this purpose however, we do not need any Linux kernel (<code>bzImage<\/code>), initramfs\nor <code>microcode.cpio<\/code> files, so I removed those and kept just the <code>.\/EFI\/BOOT<\/code>\ntree and placed the files from the hardware manufacturer on the top. The\n<code>.\/EFI\/BOOT<\/code> tree includes the grub-efi binary, <code>bootx64.efi<\/code>, and the GRUB\nconfiguration file. With UEFI, if there is a file <code>\/EFI\/BOOT\/bootx64.efi<\/code> on the\npartition, and the partition is marked as bootable (active) and has some sort of\nFAT filesystem, then the <code>bootx64.efi<\/code> will be loaded and executed on boot.<\/p>\n\n\n\n<p>The layout of the USB was now<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\n\u251c\u2500\u2500 EFI\n\u2502   \u2514\u2500\u2500 BOOT\n\u2502       \u251c\u2500\u2500 bootx64.efi\n\u2502       \u2514\u2500\u2500 grub.cfg\n\u251c\u2500\u2500 AfuEfix64.efi\n\u251c\u2500\u2500 Checksum.efi\n\u251c\u2500\u2500 GO.nsh\n\u2514\u2500\u2500 fw.bin\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-uefi-shell-binary-shellx64efi\">The UEFI shell binary, <code>Shellx64.efi<\/code><\/h2>\n\n\n\n<p>Next, I needed the UEFI shell binary that can be loaded by GRUB and that in its\nturn can be used to load the <code>GO.nsh<\/code> script. The <a href=\"https:\/\/www.tianocore.org\/\" target=\"_blank\" rel=\"noopener\">Tianocore\nproject<\/a> provides an open source implementation of\nUEFI, and while it can be built locally following the instructions on\n<a href=\"https:\/\/github.com\/tianocore\/edk2\" target=\"_blank\" rel=\"noopener\">Tianocore&#8217;s github<\/a>, I decided to just\ndownload the binary <code>Shell.efi<\/code> file <a href=\"https:\/\/github.com\/tianocore\/edk2\/blob\/UDK2018\/ShellBinPkg\/UefiShell\/X64\/Shell.efi\" target=\"_blank\" rel=\"noopener\">from\nhere<\/a>,\nand try it out. The file was renamed to <code>Shellx64.efi<\/code> and copied alongsied the\nother files on the top level of the USB.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"add-chainloader-command-to-grub\">Add chainloader command to GRUB<\/h2>\n\n\n\n<p>To load a file like <code>Shellx64.efi<\/code> from GRUB, we need GRUB&#8217;s <code>chainloader<\/code>\ncommand. Our current grub-efi build was not set up to include that command, so I\nbuilt a new one using my normal Yocto build setup. I added this line to our\nbbappend file for grub-efi, <code>grub-efi_2.06.bbappend<\/code>. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GRUB_BUILDIN += \"chain\" \n<\/code><\/pre>\n\n\n\n<p>As an alternative, this addition could have been made in e.g. <code>local.conf<\/code> as\n<code>GRUB_BUILDIN:pn-grub-efi += \"chain\"<\/code>.<\/p>\n\n\n\n<p>When the new <code>grub-efi<\/code> had been built, the resulting file\n<code>build\/tmp\/deploy\/images\/${MACHINE}\/grub-efi-bootx64.efi<\/code> was copied to the USB\nas <code>EFI\/BOOT\/bootx64.efi<\/code>, replacing the original file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"modify-grubcfg-to-load-the-uefi-shell\">Modify <code>grub.cfg<\/code> to load the UEFI shell<\/h2>\n\n\n\n<p>Next I modified the <code>grub.cfg<\/code> file on the USB to contain only one menuentry<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\ntimeout=3\n\nmenuentry 'Install new firmware'{\n    search --set=root --file \/Shellx64.efi\n    chainloader \/Shellx64.efi\n}\n<\/code><\/pre>\n\n\n\n<p>The <code>menuentry<\/code> first issues a search for the partition containing\n<code>Shellx64.efi<\/code> and sets it to GRUB&#8217;s root such that the <code>chainloader<\/code> on the\nnext line finds the file it needs to load.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"booting-into-the-uefi-shell\">Booting into the UEFI shell<\/h2>\n\n\n\n<p>By plugging in this USB to the system and booting from it, we get into the UEFI\nshell, loaded by GRUB. To update the firmware we could do<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shell&gt; fs0:\nfs0:&gt; GO.nsh\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"automatic-firmware-update-on-boot\">Automatic firmware update on boot<\/h2>\n\n\n\n<p>Since we had 50+ systems that needed a firmware update, I wanted an automatic\nway to run this script directly on boot, so I renamed the script <code>GO.nsh<\/code> to\n<code>startup.nsh<\/code>, such that it is started automatically by the UEFI shell, and\nmodified the contents of the script a bit, from being:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AfuEfix64.efi fw.bin \/P \/N \/R \/X \/SHUTDOWN\n<\/code><\/pre>\n\n\n\n<p>to <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if exist fs0:startup.nsh then\n   fs0:\nelse\n   fs1:\nendif\n\nAfuEfix64.efi fw.bin \/P \/N \/R \/X \/SHUTDOWN\n<\/code><\/pre>\n\n\n\n<p>This starts by selecting the correct device <code>fs0:<\/code> or <code>fs1:<\/code> such that the\n<code>AfuEfix64.efi<\/code> application and the <code>fw.bin<\/code> files are found. It also handles\nthe case when the USB gets mapped to <code>fs1:<\/code> instead of <code>fs0:<\/code>, which happens\nwhen the eMMC on the system is already populated with a filesystem. This\nspecific trick works for this system, but is of course not universally useful.<\/p>\n\n\n\n<p>The final layout of the USB filesystem is<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\n\u251c\u2500\u2500 EFI\n\u2502   \u2514\u2500\u2500 BOOT\n\u2502       \u251c\u2500\u2500 bootx64.efi\n\u2502       \u2514\u2500\u2500 grub.cfg\n\u251c\u2500\u2500 AfuEfix64.efi\n\u251c\u2500\u2500 Shellx64.efi\n\u251c\u2500\u2500 startup.nsh\n\u2514\u2500\u2500 fw.bin\n<\/code><\/pre>\n","protected":false},"featured_media":0,"template":"","class_list":["post-875","kunskapsbank","type-kunskapsbank","status-publish","hentry","kunskapskategori-guider"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.codiax.se\/en\/wp-json\/wp\/v2\/kunskapsbank\/875","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.codiax.se\/en\/wp-json\/wp\/v2\/kunskapsbank"}],"about":[{"href":"https:\/\/www.codiax.se\/en\/wp-json\/wp\/v2\/types\/kunskapsbank"}],"wp:attachment":[{"href":"https:\/\/www.codiax.se\/en\/wp-json\/wp\/v2\/media?parent=875"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}