2015-01-08

Easily create UEFI applications using Visual Studio 2015

As pointed out before, Visual Studio is now essentially free for all development, and its solid IDE of course makes is very desirable as the environment to use to develop UEFI applications on Windows.

Now, you might have read that, short of using the oh-so-daunting EDK2, and the intricate voodoo magic you'll have to spend days on, to make it play nice with the Visual Studio IDE, there is no salvation in the UEFI world. However, this couldn't be further from the truth.

Enters UEFI:SIMPLE.

The thing is, Visual Studio can already compile EFI applications without having to rely on any external tools, and even if you want an EDK2 like environment, with the common EFI API calls that it provides, you can totally do away with the super heavy installation and setup of the EDK, and instead use the lightweight and straightforward GNU-EFI library, that provides about the same level of functionality (as far as building standalone EFI applications or drivers are concerned, which is what we are interested in).

So really, if you want to craft an EFI application in no time at all, all you need to do is:

  1. Install Visual Studio 2015, which is totally free and which, no matter who you work for or what restrictions your corporate IT department tries to impose, you are 100% legally entitled to when it comes to trying to compile and test UEFI:SIMPLE.
  2. As suggested by the Visual Studio installer, install a git client such as msys-git (or TortoiseGit + msys-git). Now, you're going to wonder why, with git support being an integral part of Visual Studio 2015, we actually need an external client, but one problem is that Microsoft decided to strip their embedded git client of critical functionality, such as git submodule support, which we'll need.
  3. Because you'd be a fool not to want to test your EFI application or driver in a virtual environment, and, thanks to QEMU, this is so exceedingly simple to achieve that UEFI:SIMPLE will do it for you, you should download and install QEMU, preferably the 64 bit version (you can find a 64 bit qemu installer here), and preferably to its default of C:\Program Files\qemu.
  4. Clone the UEFI:SIMPLE git project, using the URI https://github.com/pbatard/uefi-simple.git. For this part, you can either use the embedded git client from Visual Studio or your external client.
  5. Now, using your external git client, navigate to your uefi-simple directory and issue the following commands:
    git submodule init
    git submodule update
    This will fetch the gnu-efi library source, which we rely on to build our application.
  6. Open the solution file in Visual Studio and just click the "Local Windows Debugger" button to both compile and run our "Hello, World"-type application in QEMU.
    Through its debug.vbs script, which can be found under the "Resource File" category, the UEFI:SIMPLE solution will take of setting everything up for you, including downloading the OVMF UEFI firmware for QEMU.
    Note that if you didn't install QEMU into C:\Program Files\qemu\ you will need to edit debug.vbs to modify the path.
  7. Finally, because the UEFI:SIMPLE source is public domain, you can now use it as a starting point to build your own UEFI application, whilst relying on the standard EFI API calls that one expects, and, more importantly, with an easy way to test your module at your fingertips.
Oh and I should point out that UEFI:SIMPLE also has ARM support, and can also be compiled on Linux, or using MinGW if you don't want to use Visual Studio on Windows. Also, if you want real-life examples of fully fledged UEFI applications, that were built using UEFI:SIMPLE as their starting point, you should look no further than efifs, a project that builds whole slew of EFI file system drivers, or UEFI:NTFS, which allows seamless EFI boot of NTFS partition.

16 comments:

  1. Pete, it appears your site and the rufus download page are serving up malicious ads designed to appear as download buttons. You may want to take it up with google asd services.

    If you can't see it yourself due to your location, I can provide screenshots. Reach out to me via email: DAVID#DCBARRY#COM. (first hashtag @, second "dot")

    ReplyDelete
    Replies
    1. Please post comments under a relevant topic. It's very rude to use comments from the first blog post you find, regardless of its content, to try to communicate with the author of a site, especially when my e-mail address is easily accessible from both the Rufus application and the Rufus web page...

      Now, about Google ads, yeah, it seems Google is pretty loose with regards to the kind of ads they serve. But I do filter ad categories as much as I can, to remove the more obnoxious ones (but there again, Google imposes an arbitrary limit, so I can't filter as much as I can, in which case I'd only have the "Technology" category allowed), and I made sure I place the ads in a location (right sidebar), where it should be fairly obvious that they are just ads.

      So I'm a bit tow minds here: I've done what I could with Google, but it's hard to fight them when they obviously have a vested and very obvious interest in allowing more than site owners would like to serve (and are DELIBERATELY preventing them from filtering them all). And I also have to say that trying to babysit people about deceptive ads is not going to make the world any better: at one stage, everybody on the internet needs to be able to tell what is a deceptive ad and what isn't, which, really, shouldn't be that difficult. Otherwise, they are going to fall prey to many more deceptive practices...

      Delete
  2. What I do wrong? I just download project and try compile it. Log:
    1>------ Build started: Project: uefi-simple, Configuration: Debug x64 ------
    1> main.c
    //some warning
    1>c:\users\артём\desktop\uefi-simple-master\gnu-efi\inc\efipoint.h(70): warning C4091: 'typedef ': ignored on left of '_EFI_ABSOLUTE_POINTER_PROTOCOL' when no variable is declared
    1>LINK : error LNK2001: unresolved external symbol EfiMain
    1>libcmtd.lib(init.obj) : error LNK2019: unresolved external symbol _CrtDbgReport referenced in function _CRT_RTC_INIT
    1>libcmtd.lib(init.obj) : error LNK2019: unresolved external symbol _CrtDbgReportW referenced in function _CRT_RTC_INITW
    1>libcmtd.lib(error.obj) : error LNK2001: unresolved external symbol __C_specific_handler
    1>libcmtd.lib(pdblkup.obj) : error LNK2019: unresolved external symbol wcscpy_s referenced in function "int __cdecl GetPdbDllPathFromFilePath(wchar_t const *,wchar_t *,unsigned __int64)" (?GetPdbDllPathFromFilePath@@YAHPEB_WPEA_W_K@Z)
    1>libcmtd.lib(pdblkup.obj) : error LNK2019: unresolved external symbol _wmakepath_s referenced in function "int __cdecl GetPdbDllPathFromFilePath(wchar_t const *,wchar_t *,unsigned __int64)" (?GetPdbDllPathFromFilePath@@YAHPEB_WPEA_W_K@Z)
    1>libcmtd.lib(pdblkup.obj) : error LNK2019: unresolved external symbol _wsplitpath_s referenced in function "int __cdecl GetPdbDllPathFromFilePath(wchar_t const *,wchar_t *,unsigned __int64)" (?GetPdbDllPathFromFilePath@@YAHPEB_WPEA_W_K@Z)
    1>libcmtd.lib(gs_report.obj) : error LNK2019: unresolved external symbol __vcrt_CaptureCurrentContext referenced in function __report_securityfailure
    1>libcmtd.lib(gs_report.obj) : error LNK2019: unresolved external symbol __vcrt_CapturePreviousContext referenced in function __report_gsfailure
    1>libcmtd.lib(gs_report.obj) : error LNK2019: unresolved external symbol __vcrt_TerminateProcess referenced in function __raise_securityfailure
    1>libcmtd.lib(gs_report.obj) : error LNK2019: unresolved external symbol __vcrt_UnhandledException referenced in function __raise_securityfailure
    1>gnu-efi.lib(print.obj) : error LNK2019: unresolved external symbol _vacopy referenced in function DbgPrint
    1>C:\Users\Артём\Desktop\uefi-simple-master\x86_64\Debug\uefi-simple.efi : fatal error LNK1120: 12 unresolved externals
    ========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========

    ReplyDelete
    Replies
    1. Please try to use the github tracker to report issues - it's A LOT easier for everyone, and I only check blog comments on a very infrequent basis.

      I've re-cloned the project from scratch from github, re-issued git submodule init & git submodule update, and I don't see any issue.
      Either your installation of Visual Studio 2013 is missing something, or not set with default settings for C compilation, or you modified some settings. Judging from the error, it would look to me like your VS 2013 is set to force C++ compilation always (and I'm not sure how you did that). Please make sure it is set to compile pure C too.

      Delete
  3. It doesnt seem that easy to locate a 64bit QEMU binary copy for Windows.

    actually we can find some links here


    http://wiki.qemu.org/Links


    and for 64 bit, here


    http://qemu.weilnetz.de/


    it may take some time for beginners of QEMU to understand what they want is not in the download page.

    ReplyDelete
  4. Sometimes one may want to install OVMF manaully, the download URL is

    ftp://ftp.heanet.ie/pub/download.sourceforge.net/pub/sourceforge/e/ed/edk2/OVMF/OVMF-X64-r15214.zip

    or

    ftp://ftp.heanet.ie/pub/download.sourceforge.net/pub/sourceforge/e/ed/edk2/OVMF/OVMF-IA32-r15214.zip

    ReplyDelete
    Replies
    1. after you have download the zip file,
      extract the .fd file and
      rename it to
      OVMF_x86_32.fd
      or
      OVMF_x86_64.fd

      Delete
    2. The README from the project also details how you can update the files manually. At the very end it says:

      "In case the download fails, you can download the latest from: http://tianocore.sourceforge.net/wiki/OVMF and extract the OVMF.fd as OVMF_x86_32.fd or OVMF_x86_64.fdin the top directory."

      Delete
  5. Very much thanks to your creative works where you save us from edk2.

    ReplyDelete
  6. Hi Pete, I feel sorry to consult you but I really wish to compile x64 assembly within UEFI:SIMPLE.
    I can see two or more .S files in gnu-efi, does it indicate there is a way to compile ATT syntax assembly with vs although there is no support for inline assembly?

    ReplyDelete
  7. To get this running in VS 2013:
    - For both projects in the Solution Explorer, right-click and choose properties. Under Navigation Properties->General change the value of the option 'Platform Toolset' to Visual Studio 2013 (v120)

    - For project uefi-simple, right-click and choose properties. Under Linker->Input remove the second and third lib file from the option 'Additional Dependencies'. The names of these files will be slightly different for Release and Debug builds (libucrt.lib/libvcruntime.lib and libucrtd.lib/libvcruntimed.lib respectively).

    ReplyDelete
  8. Hello, I am trying to build this UEFI_Simple in Visual Studio 2015 Enterprise, but it doesn't launch anything. First debug.vbs says "Unsupported debug target: -", which I resolved by manually saying Target = "x64".

    And now it gives me "Unhandled exception"... The line giving me error is:
    Call fso.CopyFile(WScript.Arguments(0), "image\efi\boot\" & BOOT_NAME, True)

    Any help?

    ReplyDelete
    Replies
    1. Please open a github issue at: https://github.com/pbatard/uefi-simple/issues and I will help you there.

      Delete
    2. Pete, fur helping future newbies like me, in Project Options, under Debugging --> Command Arguments:
      Change them to:
      //d .msvc\debug.vbs "$(TargetPath)" "$(PlatformShortName)"

      Pretty much add quotes around TargetPath and PlatformShortName ... A lot of users have spaces in paths they use...forlders... Like me. This will make their lives easier.

      Delete
    3. Done. Submitted the issue/solution to your github.

      Delete
    4. Note that the project has now been fixed with the solution proposed by Armin. Thanks!

      Delete