{"id":399985,"date":"2024-06-29T14:59:35","date_gmt":"2024-06-29T14:59:35","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=399985"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=399985","title":{"rendered":"<span>Active Restore: Beginning UEFI development<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>This is a translation with minor changes from the <a href=\"https:\/\/habr.com\/ru\/company\/acronis\/blog\/511172\/\">original post<\/a> according to advice from comments.<\/p>\n<p>  <\/p>\n<p>Hello, folks! We have studied the boot sequence of the Windows operating system as part of a project from Acronis with students of Innopolis University. You can find more about the project <a href=\"https:\/\/habr.com\/ru\/company\/acronis\/blog\/496584\/\">here<\/a> and <a href=\"https:\/\/habr.com\/ru\/company\/acronis\/blog\/499466\/\">there<\/a>. There was an idea to execute the logic even before the boot sequence of the OS. Therefore, we tried to write something just to try, for a smooth immersion in UEFI. In this article, we will go through the theory and practice to read and write to disk in a pre-OS environment.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/webt\/35\/ru\/an\/35ruanjzzo52pl4lm-8elaumorm.jpeg\" alt=\"cover\" data-src=\"https:\/\/habrastorage.org\/webt\/35\/ru\/an\/35ruanjzzo52pl4lm-8elaumorm.jpeg\" data-blurred=\"true\"\/><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<p>There are not so many teams in Acronis that deal with UEFI, so I decided to figure it out on my own. Plus, there is a proven way to get tons of accurate advice for free \u2014 just start doing something and post it online. Therefore, comments and recommendations under this post are highly appreciated! The second goal of this post is to collect a small digest of articles about UEFI and help everyone to move in this direction.<\/p>\n<p>  <\/p>\n<h3 id=\"useful-links\">Useful links<\/h3>\n<p>  <\/p>\n<p>First, I want to list sources that helped me a lot. Perhaps they will also help you and answer all your questions.<\/p>\n<p>  <\/p>\n<ul>\n<li>First of all, these are <a href=\"https:\/\/github.com\/tianocore\/tianocore.github.io\/wiki\/UEFI%20EDKII%20Learning%20Dev\">UEFI and EDK II Learning and Development<\/a> lectures from tianocore. A beautifully structured and illustrated course that will help you understand what happens at boot time and what UEFI is. This course is an ideal solution if you are looking for accurate theoretical information on a topic. If you want to quickly move on to writing UEFI drivers, then go straight to <em>Lesson 3<\/em>.<\/li>\n<li>There are articles on habr.com: <a href=\"https:\/\/habr.com\/ru\/post\/338264\/\">one<\/a>, <a href=\"https:\/\/habr.com\/ru\/post\/338404\/\">two<\/a> and <a href=\"https:\/\/habr.com\/ru\/post\/338634\/\">three<\/a>. Many thanks to the author. This is a great, hands-on guide for beginners. I will cite these masterpieces with minor changes. Without these publications, it would be much harder to get started. Sorry they are in Russian.<\/li>\n<li>For those who are already familiar with basics, I recommend <a href=\"https:\/\/habr.com\/ru\/post\/274463\/\">this article<\/a> and others by the same author.<\/li>\n<li>Since we are planning to write a driver, <a href=\"https:\/\/edk2-docs.gitbook.io\/edk-ii-uefi-driver-writer-s-guide\/\">the official guideline for writing a driver<\/a> will help a lot. The most correct advice will be there.<\/li>\n<li>Well, as a last resort, <a href=\"https:\/\/uefi.org\/sites\/default\/files\/resources\/UEFI%20Spec%202.8B%20May%202020.pdf\">the UEFI specification<\/a>.<\/li>\n<\/ul>\n<p>  <\/p>\n<h3 id=\"a-bit-of-theory\">A bit of theory<\/h3>\n<p>  <\/p>\n<p>I would like to remind you of the requirements and goals of the Active Restore project. We plan to prioritize files on the system for more efficient recovery. To do this, you need to start at the earliest stage of the OS boot. To understand our capabilities in the UEFI world, it&#8217;s worth diving into some theory about how the boot cycle goes. The information for this part is taken entirely from <a href=\"https:\/\/github.com\/tianocore\/tianocore.github.io\/wiki\/UEFI%20EDKII%20Learning%20Dev\">this source<\/a>, which I will try to retell popularly.<\/p>\n<p>  <\/p>\n<h4 id=\"uefi\">UEFI<\/h4>\n<p>  <\/p>\n<p>UEFI, or Unified Extensible Firmware Interface, is the evolution of the <em>Legacy BIOS<\/em>. The UEFI model also has a basic I \/ O system for interacting with hardware, however, the system boot process differs. UEFI uses <em>GPT<\/em> (Guid partition table). GPT is closely related to the specification and it is a more advanced model for storing information about disk partitions. The process has changed, but the tasks remain the same: initializing I \/ O devices and transferring control to the operating system. UEFI not only replaces most of the BIOS functionality but also provides a wide range of options for pre-OS development. There is a good comparison between Legacy BIOS and UEFI <a href=\"https:\/\/habr.com\/ru\/post\/404511\/\">here<\/a>.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/_a\/wi\/q0\/_awiq0ui6huczoaxuc_gmifqin8.png\" alt=\"arch\" data-src=\"https:\/\/habrastorage.org\/webt\/_a\/wi\/q0\/_awiq0ui6huczoaxuc_gmifqin8.png\"\/><\/p>\n<p>  <\/p>\n<p>From this point of view, BIOS is a component that provides direct communication with hardware and is the firmware. UEFI is the unification of the hardware interface for the operating system, which makes life much easier for developers.<\/p>\n<p>  <\/p>\n<p>In the UEFI world, we can develop drivers or applications. Also, there is a special subtype of applications \u2014 <em>loaders<\/em>. The only difference is that these applications do not end in the way we are used to (by calling return from the main function). They end with a call to the <a href=\"https:\/\/edk2-docs.gitbook.io\/edk-ii-uefi-driver-writer-s-guide\/5_uefi_services\/readme.3\/5312_exitbootservices\">ExitBootServices()<\/a> function and transfer control to the operating system. To decide which driver you need, I recommend taking a look <a href=\"https:\/\/tianocore-training.github.io\/Lesson-3\/\">here<\/a> to expand your understanding of the protocols and recommendations for their use.<\/p>\n<p>  <\/p>\n<h4 id=\"dev-kits\">Dev kits<\/h4>\n<p>  <\/p>\n<p>A small list of what we will use in our practice:<\/p>\n<p>  <\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/tianocore\/edk2\">EDKII<\/a> (Extensible Firmware Interface Development Kit) is a free project for the development of UEFI applications and drivers, which we will also use. At first, we will not go deep into it.<\/li>\n<li><a href=\"https:\/\/github.com\/ionescu007\/VisualUefi\">VisualUEFI<\/a> is a project that simplifies the development in Visual Studio. You no longer need to bother with .inf files and poke around in 100,500 Python scripts. All this has already been done for you. Inside this project, you can find a <em>QEMU<\/em> to run our code. The project provides examples of applications and drivers. <\/li>\n<li><a href=\"https:\/\/doc.coreboot.org\/index.html\">Coreboot<\/a> is a complex project for the firmware. It helps to develop a solution for starting hardware and transferring control to the payload (for example, UEFI or GRUB), which in turn will load the operating system. We will not be covering coreboot in this article. We&#8217;ll leave it for future experiments when I get my hands on the <em>EDKII<\/em>. Coreboot + Tianocore UEFI + Windows 7 x64 will probably be the right vector of development.<\/li>\n<\/ul>\n<p>  <\/p>\n<h4 id=\"boot-sequence\">Boot sequence<\/h4>\n<p>  <\/p>\n<p>Let&#8217;s briefly analyze what stages our machine goes through before we see the logo of the operating system. To do this, consider the following diagram:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/8b\/ks\/zz\/8bkszzowvmnnclrapkwkhgboa3a.png\" alt=\"boot_seq\" data-src=\"https:\/\/habrastorage.org\/webt\/8b\/ks\/zz\/8bkszzowvmnnclrapkwkhgboa3a.png\"\/><br \/>  <a href=\"https:\/\/tianocore-training.github.io\/Lesson-0\/\">Link<\/a><\/p>\n<p>  <\/p>\n<p><strong>Platform Initialization<\/strong> is the process from the moment you press the power button and until the UEFI interface is fully ready. PI is divided into several phases:<\/p>\n<p>  <\/p>\n<ul>\n<li><strong>Security (SEC)<\/strong> \u2014 depends on the platform and the processor, usually implemented by assembly instructions, performs the initial initialization of temporary memory, checks the rest of the platform for security in various ways.<\/li>\n<li><strong>Pre EFI Initialization (PEI)<\/strong> \u2014 in this phase, the work of the EFI code is starting, the main task is to download the <strong>DXE Foundation<\/strong>, which will start the <em>DXE drivers<\/em> in the next phase. In fact, there is a lot going on here, but what we are planning to develop will not fit here, so we move on.<\/li>\n<li><strong>Driver Execution Environment (DXE)<\/strong> \u2014 at this stage, drivers start their work. The most important phase for us, because our driver will also be launched here. This driver execution environment is the main advantage over Legacy BIOS. This is where the code starts executing in parallel. DXE behaves like an operating system. This allows different companies to implement their drivers. The DXE Foundation, deployed in the previous phase, finds drivers, libraries, and applications one by one, deploys them in memory and executes them.<\/li>\n<li><strong>Boot Device Selection (BDS)<\/strong> phase takes over next. You have probably personally seen this phase. Here there is a choice on which device to search for the operating system loader. After selection, the transition to the operating system begins. DXE boot drivers start unloading from memory. On the contrary, the operating system loader is loaded into memory using the block I \/ O protocol <strong>BLOCK_IO<\/strong>. <\/li>\n<\/ul>\n<p>  <\/p>\n<p>Not all DXE drivers are terminated here. There are so-called <a href=\"https:\/\/edk2-docs.gitbook.io\/edk-ii-uefi-driver-writer-s-guide\/7_driver_entry_point\/711_runtime_drivers\">runtime drivers<\/a>. They will have to mark up the memory they occupy in a notation understandable to the loaded operating system. In other words, <a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A2%D0%B0%D0%B1%D0%BB%D0%B8%D1%86%D0%B0_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86#:~:text=%D0%92%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5%20%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%B0%20%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D1%8E%D1%82%D1%81%D1%8F%20%D0%B2%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D1%8F%D1%8E%D1%89%D0%B8%D0%BC%D1%81%D1%8F%20%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%BE%D0%BC,%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%B0%20%D0%BA%20%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%BC%20%D0%B2%20%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8.\">virtualize<\/a> your addresses into the Windows address space when the <a href=\"https:\/\/edk2-docs.gitbook.io\/edk-ii-uefi-driver-writer-s-guide\/5_uefi_services\/readme.3\/5313_setvirtualaddressmap\">SetVirtualAddressMap()<\/a> function is called. Once the environment is ready, the <em>\u201cMain\u201d<\/em> function of the OS kernel will begin execution, and the EFI phase will end with a call to <a href=\"https:\/\/edk2-docs.gitbook.io\/edk-ii-uefi-driver-writer-s-guide\/5_uefi_services\/readme.3\/5312_exitbootservices\">ExitBootServices()<\/a>. Control is completely transferred to the operating system. Then Windows will decide which and where to load the diver, how to read and write to the disk, and what kind of file system to use. A picture summarize the sequence:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/_w\/y_\/um\/_wy_umtnny6bezvt1glpjzn3cuo.png\" alt=\"image\" data-src=\"https:\/\/habrastorage.org\/webt\/_w\/y_\/um\/_wy_umtnny6bezvt1glpjzn3cuo.png\"\/><br \/>  <a href=\"https:\/\/uefi.org\/sites\/default\/files\/resources\/UEFI-Plugfest-WindowsBootEnvironment.pdf\">Link<\/a><\/p>\n<p>  <\/p>\n<p>You could find a cool story about the loading stages <a href=\"https:\/\/habr.com\/ru\/post\/185764\/\">here<\/a>.<\/p>\n<p>  <\/p>\n<h3 id=\"project-preparation\">Project preparation<\/h3>\n<p>  <\/p>\n<p>It&#8217;s time to set yourself a simple task. We can load our driver in the <em>DXE phase<\/em>, open a file on disk, and write some data to it. The task is simple enough to practice.<\/p>\n<p>  <\/p>\n<p>As I already mentioned, we will use the <strong>VisualUEFI<\/strong> project, but I also recommend trying the methods described <a href=\"https:\/\/habr.com\/ru\/post\/338264\/\">here<\/a>, at least because it is easier to use the debugger in the method described by the link.<\/p>\n<p>  <\/p>\n<p>I will assume that you already have Visual Studio. In my case, I have Visual Studio 2019. First, we clone our <strong>VisualUEFI<\/strong> project:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">git clone --recurse-submodules -j8 https:\/\/github.com\/ionescu007\/VisualUefi.git<\/code><\/pre>\n<p>  <\/p>\n<p>We need <a href=\"https:\/\/www.nasm.us\">NASM<\/a> (<a href=\"https:\/\/www.nasm.us\/pub\/nasm\/releasebuilds\/2.15.02\/win64\/\">https:\/\/www.nasm.us\/pub\/nasm\/releasebuilds\/2.15.02\/win64\/<\/a>). Go ahead and download it. At the time of this writing, the current version is <em>2.15.02<\/em>. After installation, make sure you have <strong>NASM_PREFIX<\/strong> in environment variables which points to the folder where <em>NASM<\/em> was installed. In my case, this is <em>C:\\Program Files\\NASM\\<\/em>.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/gd\/dd\/fq\/gdddfqrvvzwjowis2723o0qbkpm.png\" alt=\"NASM_PREFIX\" data-src=\"https:\/\/habrastorage.org\/webt\/gd\/dd\/fq\/gdddfqrvvzwjowis2723o0qbkpm.png\"\/><\/p>\n<p>  <\/p>\n<p>Let&#8217;s build <em>EDKII<\/em>. To do this, open <strong>EDK-II.sln<\/strong> from <strong>\\VisualUefi\\EDK-II<\/strong>, and just click build on the solution. All projects in the solution should be successfully assembled, and you can proceed to ready-made examples. Opening <strong>samples.sln<\/strong> from <strong>\\VisualUefi\\samples<\/strong>. We press build on the application and driver, after which you can start <strong>QEMU<\/strong> by simply pressing <strong>F5<\/strong>.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/tm\/m1\/rz\/tmm1rza0pabky2u1w_vlloawhxg.png\" alt=\"Shell\" data-src=\"https:\/\/habrastorage.org\/webt\/tm\/m1\/rz\/tmm1rza0pabky2u1w_vlloawhxg.png\"\/><\/p>\n<p>  <\/p>\n<p>We check our <strong>UefiDriver<\/strong> and <strong>UefiApplication<\/strong>. This is how the examples in the <em>samples.sln<\/em> solution are called.<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">Shell> fs1: FS1:\\> load UefiDriver.efi<\/code><\/pre>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/gv\/z1\/cp\/gvz1cpkqtavc-wzzbelz06ri-v4.png\" alt=\"load\" data-src=\"https:\/\/habrastorage.org\/webt\/gv\/z1\/cp\/gvz1cpkqtavc-wzzbelz06ri-v4.png\"\/><\/p>\n<p>  <\/p>\n<p>Great, the driver not only compiled but also loaded successfully. After running the command <em>\u201cdrivers\u201d<\/em>, we will even see it on the list.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/cq\/wq\/lz\/cqwqlzcz3ryh08hpntdxcator9s.png\" alt=\"drivers\" data-src=\"https:\/\/habrastorage.org\/webt\/cq\/wq\/lz\/cqwqlzcz3ryh08hpntdxcator9s.png\"\/><\/p>\n<p>  <\/p>\n<p>If we did not return <strong>EFI_ACCESS_DENIED<\/strong> in the <strong>UefiUnload<\/strong> function in the code, we could even unload our driver by running the command:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">FS1:\\> unload BA<\/code><\/pre>\n<p>  <\/p>\n<p>Now let&#8217;s call our application:<\/p>\n<p>  <\/p>\n<pre><code class=\"plaintext\">FS1:\\> UefiApplication.efi<\/code><\/pre>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/cd\/pl\/qr\/cdplqrx0zmsdw20etvsmjnczhvq.png\" alt=\"app\" data-src=\"https:\/\/habrastorage.org\/webt\/cd\/pl\/qr\/cdplqrx0zmsdw20etvsmjnczhvq.png\"\/><\/p>\n<p>  <\/p>\n<h3 id=\"code\">Code<\/h3>\n<p>  <\/p>\n<p>Let&#8217;s consider the code of the driver provided to us. It all starts with the <strong>UefiMain<\/strong> function, which is located in the <strong>drvmain.c<\/strong> file. We could have named the entry point by a different name if we were writing the driver from scratch, cause this name is specified in the <em>.inf<\/em> file.<\/p>\n<p>  <\/p>\n<p>By the way, speaking about code style. It is better to use the one defined by <em>tianocore<\/em> \u2014 <a href=\"https:\/\/github.com\/tianocore\/tianocore.github.io\/wiki\/Code-Style-C\">C code style<\/a><\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">EFI_STATUS EFIAPI UefiUnload (   IN EFI_HANDLE ImageHandle   ) {   \/\/   \/\/ Do not allow unload   \/\/   return EFI_ACCESS_DENIED; }  EFI_STATUS EFIAPI UefiMain (   IN EFI_HANDLE       ImageHandle,   IN EFI_SYSTEM_TABLE *SystemTable   ) {   EFI_STATUS EfiStatus;    \/\/   \/\/ Install required driver binding components   \/\/   EfiStatus = EfiLibInstallDriverBindingComponentName2(ImageHandle,                                                        SystemTable,                                                        &amp;gDriverBindingProtocol,                                                        ImageHandle,                                                        &amp;gComponentNameProtocol,                                                        &amp;gComponentName2Protocol);   return EfiStatus; }<\/code><\/pre>\n<p>  <\/p>\n<p>In the project, we are not required to register the <em>Unload<\/em> function, since <strong>VisualUEFI<\/strong> already does this \u201cunder the hood\u201d, we just need to declare it. In the example, it is in the same file and the function is called <strong>UefiUnload<\/strong>. In this function, we can write code that will free all the resources we are using, since it will be called when the driver is unloaded. The registration of the <em>Unload<\/em> function in the <strong>VisualUEFI<\/strong> project occurs in the <strong>DriverEntryPoint.c<\/strong> file, in the <em>_ModuleEntryPoint<\/em> function.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">\/\/ _DriverUnloadHandler manages to call UefiUnload Status = gBS->HandleProtocol(ImageHandle,                              &amp;gEfiLoadedImageProtocolGuid,                              (VOID **)&amp;LoadedImage);  \/\/ todo check Status if error  LoadedImage->Unload = _DriverUnloadHandler;<\/code><\/pre>\n<p>  <\/p>\n<p>In our example, in the <strong>UefiMain<\/strong> function, the <strong>EfiLibInstallDriverBindingComponentName2<\/strong> function is called, which registers the name of our driver and the <em>Driver Binding Protocol<\/em>. According to the UEFI driver model, all device drivers must register this protocol in order to provide the controller with <em>Support, Start, Stop<\/em> functions. The <em>Support<\/em> function answers whether our driver can work with this controller. If so, the <em>Start<\/em> function is called. More details about this are well described in the <a href=\"https:\/\/uefi.org\/sites\/default\/files\/resources\/UEFI%20Spec%202.8B%20May%202020.pdf\">specification<\/a> (section Protocols \u2014 UEFI Driver Model). In our example, the <em>Support<\/em>, <em>Start<\/em> and <em>Stop<\/em> functions set up our custom protocol. Its implementation in <em>drvpnp.c<\/em> file:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">\/\/ \/\/ EFI Driver Binding Protocol \/\/ EFI_DRIVER_BINDING_PROTOCOL gDriverBindingProtocol = {   SampleDriverSupported,   SampleDriverStart,   SampleDriverStop,   10,   NULL,   NULL };  \u2026  \/\/ \/\/ Install our custom protocol on top of a new device handle \/\/ EfiStatus = gBS->InstallMultipleProtocolInterfaces(&amp;deviceExtension->DeviceHandle,                                                    &amp;gEfiSampleDriverProtocolGuid,                                                    &amp;deviceExtension->DeviceProtocol,                                                    NULL);  \/\/ todo check error  \/\/ \/\/ Bind the PCI I\/O protocol between our new device handle and the controller \/\/ EfiStatus = gBS->OpenProtocol(Controller,                               &amp;gEfiPciIoProtocolGuid,                               (VOID**)&amp;childPciIo,                               This->DriverBindingHandle,                               deviceExtension->DeviceHandle,                               EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);<\/code><\/pre>\n<p>  <\/p>\n<p>The <strong>EfiLibInstallDriverBindingComponentName2<\/strong> function is implemented in the <em>UefiDriverModel.c<\/em> file and is actually very simple. It calls <strong>InstallMultipleProtocolInterfaces<\/strong> from <em>Boot Services<\/em> (see <a href=\"https:\/\/uefi.org\/sites\/default\/files\/resources\/UEFI%20Spec%202.8B%20May%202020.pdf\">spec<\/a> page 210). This function binds the handle (in our case the <em>ImageHandle<\/em>, which we received at the entry point) and the protocol.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">\/\/ install component name and binding Status = gBS->InstallMultipleProtocolInterfaces(                        &amp;DriverBinding->DriverBindingHandle,                        &amp;gEfiDriverBindingProtocolGuid, DriverBinding,                        &amp;gEfiComponentNameProtocolGuid, ComponentName,                        &amp;gEfiComponentName2ProtocolGuid, ComponentName2,                        NULL);<\/code><\/pre>\n<p>  <\/p>\n<p>Logically, it is possible and necessary, at the time of unloading the driver, to remove the installed components. We will do this in our <em>Unload<\/em> function. Now our driver can be unloaded by calling the command <em>&#171;unload &#171;, or before transferring control to the operating system.<\/em>  <\/p>\n<pre><code class=\"cpp\">EFI_STATUS EFIAPI UefiUnload (   IN EFI_HANDLE ImageHandle   ) {   gBS->UninstallMultipleProtocolInterfaces(     ImageHandle,     &amp;gEfiDriverBindingProtocolGuid, &amp;gDriverBindingProtocol,     &amp;gEfiComponentNameProtocolGuid, &amp;gComponentNameProtocol,     &amp;gEfiComponentName2ProtocolGuid, &amp;gComponentName2Protocol,     NULL   );   \/\/   \/\/ Changed from access denied in order to unload in boot   \/\/   return EFI_SUCCESS; }<\/code><\/pre>\n<p>  <\/p>\n<p>As you can see, in our code we interact with UEFI through the <strong>gBS (global Boot Services)<\/strong> global field. Also, there is <strong>gRT (Global Runtime Services)<\/strong>, and together they are part of the <strong>System Table<\/strong> structure. <a href=\"https:\/\/habr.com\/ru\/post\/338404\/\">Source<\/a>.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">gST = *SystemTable;  gBS = gST->BootServices;  gRT = gST->RuntimeServices;<\/code><\/pre>\n<p>  <\/p>\n<p>To work with files, we need the <strong>Simple File System Protocol<\/strong> (see <a href=\"https:\/\/uefi.org\/sites\/default\/files\/resources\/UEFI%20Spec%202.8B%20May%202020.pdf\">spec<\/a> page 504). By calling the <strong>LocateProtocol<\/strong> function, you can get a pointer to it, but the more correct way to list all handles on the file system devices is using the <strong>LocateHandleBuffer<\/strong> function, and after going through all the <em>Simple File System protocols<\/em>, choose the appropriate one that will allow us to write and read to a file. An example of such code is <a href=\"https:\/\/github.com\/LongSoft\/CrScreenshotDxe\/blob\/master\/CrScreenshotDxe.c\">here<\/a>. We are going to use the easier way. The protocol has only one function and it will allow us to open the volume.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">EFI_STATUS OpenVolume(   OUT EFI_FILE_PROTOCOL** Volume ) {   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* FsProto = NULL;   EFI_STATUS Status;   *Volume = NULL;    \/\/ get file system protocol   Status = gBS->LocateProtocol(     &amp;gEfiSimpleFileSystemProtocolGuid,     NULL,     (VOID**)&amp;FsProto);    if (EFI_ERROR(status)) {     return Status;   }    Status = FsProto->OpenVolume(FsProto, Volume);    return Status; }<\/code><\/pre>\n<p>  <\/p>\n<p>Next, we need to be able to create a file and close it. Let&#8217;s use <strong>EFI_FILE_PROTOCOL<\/strong>, which has functions for working with the file system (see [spec]<a href=\"https:\/\/uefi.org\/sites\/default\/files\/resources\/UEFI%20Spec%202.8B%20May%202020.pdf\">https:\/\/uefi.org\/sites\/default\/files\/resources\/UEFI%20Spec%202.8B%20May%202020.pdf<\/a>) page 506).<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">EFI_STATUS OpenFile(   IN  EFI_FILE_PROTOCOL*   Volume,   OUT EFI_FILE_PROTOCOL**  File,   IN  CHAR16*              Path   ) {   EFI_STATUS Status;   *File = NULL;    \/\/  from root file we open file specified by path   Status = Volume->Open(     Volume,     File,     Path,     EFI_FILE_MODE_CREATE |     EFI_FILE_MODE_WRITE |     EFI_FILE_MODE_READ,     0);    return status; }  EFI_STATUS CloseFile(   IN EFI_FILE_PROTOCOL* File   ) {   \/\/  flush unwritten data   File->Flush(File);   \/\/  close file   File->Close(File);    return EFI_SUCCESS; }<\/code><\/pre>\n<p>  <\/p>\n<p>To write to a file, we will have to manually move the carriage. To do this, we will ask the file size using the <strong>GetInfo<\/strong> function.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\"> #define REQUIRE_NOERR(stat, label) if (EFI_ERROR(stat)) goto label;  EFI_STATUS WriteDataToFile(   IN VOID*              Buffer,   IN UINTN              BufferSize,   IN EFI_FILE_PROTOCOL* File ) {   UINTN InfoBufferSize = 0;   EFI_FILE_INFO* FileInfo = NULL;    \/\/  retrieve file info to know it size   EFI_STATUS Status = File->GetInfo(     File,     &amp;gEfiFileInfoGuid,     &amp;InfoBufferSize,     (VOID*)FileInfo);    if (EFI_BUFFER_TOO_SMALL != Status) {     return Status;   }    FileInfo = AllocatePool(InfoBufferSize);    if (NULL == fileInfo) {     Status = EFI_OUT_OF_RESOURCES;     return Status;   }    \/\/    we need to know file size   Status = File->GetInfo(     File,     &amp;gEfiFileInfoGuid,     &amp;InfoBufferSize,     (VOID*)FileInfo   );    REQUIRE_NOERR(Status, FINALIZE);    \/\/    we move carriage to the end of the file   Status = File->SetPosition(File, FileInfo->FileSize);    REQUIRE_NOERR(Status, FINALIZE);    \/\/    write buffer   Status = File->Write(File, &amp;BufferSize, Buffer);    REQUIRE_NOERR(Status, FINALIZE);    \/\/    flush data   Status = File->Flush(File);  FINALIZE:    if (NULL != FileInfo) {     FreePool(FileInfo);   }    return Status; }<\/code><\/pre>\n<p>  <\/p>\n<p>We call our functions and write random data to our file.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">EFI_STATUS WriteToFile(   VOID ) {   CHAR16 Path[] = L\"\\\\example.txt\";   EFI_FILE_PROTOCOL* File = NULL;   EFI_FILE_PROTOCOL* Volume = NULL;   CHAR16 Something[] = L\"Hello from UEFI driver\";    \/\/   \/\/  Open file   \/\/   EFI_STATUS Status = OpenVolume(&amp;Volume);    if (EFI_ERROR(Status)) {     return Status;   }    Status = OpenFile(Volume, &amp;File, Path);    if (EFI_ERROR(Status)) {     CloseFile(Volume);     return Status;   }    Status = WriteDataToFile(Something, sizeof(Something), File);    CloseFile(File);   CloseFile(Volume);    return Status; }<\/code><\/pre>\n<p>  <\/p>\n<p>There is an alternative way to accomplish our task. The VisualUEFI project has already implemented what we wrote above. We can simply include the <strong>ShellLib.h<\/strong> header file and call the <strong>ShellInitialize<\/strong> function at the very beginning. All the necessary protocols for working with the file system will be open, and the <strong>ShellOpenFileByName<\/strong>, <strong>ShellWrite<\/strong>, and <strong>ShellRead<\/strong> functions are implemented in almost the same way as we have.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">#include &lt;Library\/ShellLib.h>  EFI_STATUS WriteToFile2(   VOID ) {   SHELL_FILE_HANDLE FileHandle = NULL;   CHAR16 Path[] = L\"fs1:\\\\example2.txt\";   CHAR16 Something[] = L\"Hello from UEFI driver\";   UINTN WriteSize = sizeof(Something);    EFI_STATUS Status = ShellInitialize();    if (EFI_ERROR(Status)) {     return Status;   }    Status = ShellOpenFileByName(Path,     &amp;FileHandle,     EFI_FILE_MODE_CREATE |     EFI_FILE_MODE_WRITE |     EFI_FILE_MODE_READ,     0);    if (EFI_ERROR(Status)) {     return Status;   }    Status = ShellWriteFile(FileHandle, &amp;WriteSize, Something);    ShellCloseFile(&amp;FileHandle);   return Status; }<\/code><\/pre>\n<p>  <\/p>\n<p>The result:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/webt\/_s\/k3\/de\/_sk3desxw2wnpbexcucasgt7j2g.jpeg\" alt=\"result\" data-src=\"https:\/\/habrastorage.org\/webt\/_s\/k3\/de\/_sk3desxw2wnpbexcucasgt7j2g.jpeg\" data-blurred=\"true\"\/><\/p>\n<p>  <\/p>\n<p>\u2192 The code for this example is on <a href=\"https:\/\/github.com\/Dabudabot\/hello-uefi\">github<\/a><\/p>\n<p>  <\/p>\n<p>If we want to switch to <em>VMWare<\/em>, then the most correct would be to modify the firmware using <a href=\"https:\/\/github.com\/LongSoft\/UEFITool\">UEFITool<\/a>. <a href=\"https:\/\/github.com\/pbatard\/efifs\/wiki\/Adding-a-driver-to-a-UEFI-firmware\">Here<\/a> you could find a demo on how to add an NTFS driver to UEFI.<\/p>\n<p>  <\/p>\n<h3 id=\"conclusion\">Conclusion<\/h3>\n<p>  <\/p>\n<p>To complicate the idea of our driver and bring it closer to the requirements of the Active Restore project, we can do the following: open the <strong>BLOCK_IO<\/strong> protocol, replace the read functions on the disk with our functions, which will write the data read from the disk to the log and then call the original functions. This can be done as follows:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">\/\/ just pseudo code ...  \/\/ open protocol to replace callbacks gBS->OpenProtocol(       Controller,       Guid,       (VOID**)&amp;protocol,       DriverBindingHandle,       Controller,       EFI_OPEN_PROTOCOL_GET_PROTOCOL     );  \/\/ raise Task Priority Level to max avaliable gBS->RaiseTPL(TPL_NOTIFY);  VOID** ProtocolBase = EFI_FIELD_BY_OFFSET(VOID**, FilterContainer, 0); VOID** OldCallback = EFI_FIELD_BY_OFFSET(VOID**, *ProtocolBase, OldCallbackOffset); VOID** OriginalCallback = EFI_FIELD_BY_OFFSET(VOID**, FilterContainer, OriginalCallbackOffset);  \/\/  yes, I know that it is not super obvious \/\/  but if first and third is equal (placeholder and function) \/\/  then the first one is not the function it is offset! \/\/  and function itself is by offset of third one if ((UINTN) NewCallback == OriginalCallbackOffset) {   NewCallback = *OriginalCallback; }  PRINT_DEBUG(DEBUG_INFO, L\"[UefiMonitor] 0x%x -> 0x%x\\n\", *OldCallback, NewCallback);  \/\/saving original functions *OriginalCallback = *OldCallback; \/\/replacing them by filter function *OldCallback = NewCallback;  \/\/ restore TPL gBS->RestoreTPL(OldTpl);<\/code><\/pre>\n<p>  <\/p>\n<p>In addition, we have to subscribe to <strong>ExitBootServices()<\/strong> to get the pointers back in place. After the file system filter in Windows is ready, the minifilter will continue to log reads from the disk.<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">\/\/ event on exit gBS->CreateEvent(       EVT_SIGNAL_EXIT_BOOT_SERVICES,       TPL_NOTIFY,       ExitBootServicesNotifyCallback,       NULL,       &amp;mExitBootServicesEvent     );<\/code><\/pre>\n<p>  <\/p>\n<p>But these are already ideas for future articles. Thanks for your attention.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><\/p>\n<div class=\"tm-article-poll-container\"><!--[--><\/p>\n<div class=\"tm-article-poll tm-article-poll_variant-bordered\">\n<div class=\"tm-notice tm-notice_positive tm-article-poll__notice\"><!----><\/p>\n<div class=\"tm-notice__inner\"><!----><\/p>\n<div class=\"tm-notice__content\" data-test-id=\"notice-content\"><!--[--><span>\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u043f\u0440\u043e\u0441\u0435. <a rel=\"nofollow\" href=\"\/kek\/v1\/auth\/habrahabr\/?back=\/ru\/companies\/acronis\/articles\/535848\/&#038;hl=ru\">\u0412\u043e\u0439\u0434\u0438\u0442\u0435<\/a>, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430.<\/span><!--]--><\/div>\n<\/div>\n<\/div>\n<p><!--[--><\/p>\n<div class=\"tm-article-poll__header\">Do you think UEFI development (and embedded development in general) is difficult and impossible for you?<\/div>\n<div class=\"tm-article-poll__answers\"><!--[--><\/p>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">0% <\/span><span class=\"tm-article-poll__answer-label\">Yes, it&#8217;s too hard for me<\/span><span class=\"tm-article-poll__answer-votes\">0<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 0%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">50% <\/span><span class=\"tm-article-poll__answer-label\">You can always find materials that explain simply about the complex<\/span><span class=\"tm-article-poll__answer-votes\">1<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 50%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent tm-article-poll__answer-percent_winning\">50% <\/span><span class=\"tm-article-poll__answer-label\">No, I&#8217;m used to living with it<\/span><span class=\"tm-article-poll__answer-votes\">1<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress tm-article-poll__answer-progress_winning\" style=\"width: 50%\"><\/div>\n<\/div>\n<\/div>\n<p><!--]--><\/div>\n<div class=\"tm-article-poll__stats\"> \u041f\u0440\u043e\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043b\u0438 2 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.    \u0412\u043e\u0437\u0434\u0435\u0440\u0436\u0430\u0432\u0448\u0438\u0445\u0441\u044f \u043d\u0435\u0442. <\/div>\n<p><!--]--><\/div>\n<p><!--]--><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/535848\/\"> https:\/\/habr.com\/ru\/articles\/535848\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>This is a translation with minor changes from the <a href=\"https:\/\/habr.com\/ru\/company\/acronis\/blog\/511172\/\">original post<\/a> according to advice from comments.<\/p>\n<p>  <\/p>\n<p>Hello, folks! We have studied the boot sequence of the Windows operating system as part of a project from Acronis with students of Innopolis University. You can find more about the project <a href=\"https:\/\/habr.com\/ru\/company\/acronis\/blog\/496584\/\">here<\/a> and <a href=\"https:\/\/habr.com\/ru\/company\/acronis\/blog\/499466\/\">there<\/a>. There was an idea to execute the logic even before the boot sequence of the OS. Therefore, we tried to write something just to try, for a smooth immersion in UEFI. In this article, we will go through the theory and practice to read and write to disk in a pre-OS environment.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/webt\/35\/ru\/an\/35ruanjzzo52pl4lm-8elaumorm.jpeg\" alt=\"cover\" data-src=\"https:\/\/habrastorage.org\/webt\/35\/ru\/an\/35ruanjzzo52pl4lm-8elaumorm.jpeg\" data-blurred=\"true\"\/><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-399985","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/399985","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=399985"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/399985\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=399985"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=399985"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=399985"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}