Unified Extensible Firmware Interface(UEFI) is the new standard that is being developed in order to replace BIOS. UEFI was first developed by Intel and was later joined by other organizations. All the open source components of Intel’s implementation of UEFI is currently hosted at tianocore website. EDK II is a modern, feature-rich, cross-platform firmware development environment for the UEFI and PI specifications.
In this article, we will be going over how to setup edk2 for beagleboard on Ubuntu 14.04. I will be using qemu in order to simulate beagleboard. Finally we will develop an osloader that can load a FreeRTOS application.
Creating SdCard Image
In order to boot beagleboard, we need the sdcard image with u-boot in it. We will be using linaro image creator to create the image file. Linaro group maintains a ppa from which we can install these tools.
user@bash:$ sudo add-apt-repository ppa:linaro-maintainers/tools
user@bash:$ sudo apt-get update
user@bash:$ sudo apt-get install linaro-image-tools
We also need a few additional files to create the image.
user@bash:$ wget http://releases.linaro.org/platform/linaro-m/hwpacks/final/hwpack_linaro-omap3_20101109-1_armel_supported.tar.gz
user@bash:$ wget http://releases.linaro.org/platform/linaro-m/headless/release-candidate/linaro-m-headless-tar-20101101-0.tar.gz
Now we can create the sdcard image file with the following commands
user@bash:$ sudo linaro-media-create --image_file beagle_sd.img --dev beagle --binary linaro-m-headless-tar-20101101-0.tar.gz --hwpack hwpack_linaro-omap3_20101109-1_armel_supported.tar.gz
This will create a beagle_sd.img
image file in your current directory.
Mounting SdCard Image
I spent some time trying to figure out how to mount the sdcard to access the files. I have listed the commands used to mount the sdcard.
user@bash:$ fdisk -l beagle_sd.img
Device Boot Start End Blocks Id System
beagle_sd.img1 * 63 106494 53216 c W95 FAT32 (LBA)
beagle_sd.img2 106496 6291455 3092480 83 Linux
user@bash:$ mkdir /tmp/beagle
user@bash:$ sudo mount -o loop,offset=$[63*512] beagle_sd.img /tmp/beagle
You will notice that the value I used in $[63*512]
is the same value I get from fdisk
command under Start
. So you should replace 63 by the value you get.
The mounted sd card has the following files
boot.ini boot.scr boot.txt MLO u-boot.bin uImage uInitrd
Install Qemu
Installing Qemu in Ubuntu is very easy as it exists in the system repository. But the main qemu branch does not support beagleboard. Linaro has forked qemu and they have added support for beagle board in their release. I was unable to find a ppa for that release. We need to install it from the source, but if you install the latest release which is currently at 0677e6e
, you will be able to boot the system but you will be unable to browse the contents of the sdcard which is essential if you need to run an UEFI application.
I found that a older version of the code works and you can download the zip file from here. Extract the file and then use the below commands to install qemu. If you directly use the below commands, the build will fail with an error related to fdt32_t
. So you have to patch include/libfdt_env.h
file by adding the below lines after line 22.
#ifdef __CHECKER__
#define __force __attribute__((force))
#define __bitwise __attribute__((bitwise))
#else
#define __force
#define __bitwise
#endif
typedef uint16_t __bitwise fdt16_t;
typedef uint32_t __bitwise fdt32_t;
typedef uint64_t __bitwise fdt64_t;
user@bash:$ sudo apt-get build-dep qemu
user@bash:$ ./configure --target-list=arm-softmmu --prefix=$HOME/qemu
user@bash:$ make
user@bash:$ make install
The above commands will install qemu into qemu
folder in your home directory. Now add the directory to you path by editing your .bashrc
file.
PATH=$PATH:~/qemu/bin
Now qemu-system-arm
command should be available at prompt.
user@bash:$ qemu-system-arm --version
QEMU emulator version 1.5.0 (qemu-linaro from git), Copyright (c) 2003-2008 Fabrice Bellard
Testing Qemu
At this point, we can test whether qemu is working with the sd card image we generated. When you run the below command you should get the u-boot prompt.
user@bash:$ qemu-system-arm -M beagle -sd beagle_sd.img -serial stdio -clock unix
Texas Instruments X-Loader 1.4.4ss (Sep 30 2010 - 14:44:32)
Beagle Rev C4
Reading boot sector
Loading u-boot.bin from mmc
Building UEFI
In order to build UEFI, we need the Arm toolchain. Follow the simple instructions below and you should be able to generate the file without any trouble.
user@bash:$ sudo apt-get install gcc-arm-linux-gnueabi
user@bash:$ gcc-arm-linux-gnueabi --version
arm-linux-gnueabi-gcc (Ubuntu/Linaro 4.7.3-12ubuntu1) 4.7.3
user@bash:$ git clone https://github.com/tianocore/edk2.git
user@bash:$ cd edk2
user@bash:$ source edksetup.sh
user@bash:$ cd BeagleBoardPkg
user@bash:$ ./build.sh
user@bash:$ ls ../Build/BeagleBoard/DEBUG_ARMLINUXGCC/FV/
Now you should be able to see BEAGLEBOARD_EFI.FD
which is the file we will be using to boot UEFI.
Mount the sdcard using the commands that were given in the beginning. Now we need to replace the u-boot.bin
file with the BEAGLEBOARD_EFI.fd
file in the sdcard. Lets now start UEFI on the beagle board.
user@bash:$ qemu-system-arm -M beagle -sd beagle_sd.img -serial stdio -clock unix
This will start the qemu and after that it prints some gibberish and then you should get the boot selection option of UEFI.
The default boot selection will start in 5 seconds
[1] Linux from SD
......
[2] Shell
[3] Boot Manager
Start:
If you get some lines saying SD: CMD12 in a wrong state
just ignore them. We need to enter the shell, so enter 2
to select the shell. This will output the Mapping table and the important thing to note here is that you should have a FS0:
entry. If you did not get that then you are doing something wrong.
I had some problem with the number of lines that the shell could print, so you can use mode 100 31
to fix this. You can use cls
to clear the screen.
Building FreeRTOS and OSLoader
FreeRTOS is a simple real time operating system suited for small and medium sized micro-controllers. I was not the one who ported FreeRTOS on to beagleboard, here I will be only going over the changes I had to make in order for it work with UEFI.
UEFI defines a simple set of instructions to build a osloader.
- Load the application in to the memory.
- Call
GetMemoryMap()
to get the memory map and the key - Call
ExitBootServices()
to stop the boot time services provided by UEFI. - Now transfer control to the application
So it is as simple as that.
We need to install CodeSourcery toolchain to be able to compile the FreeRTOS code. I downloaded the lite version and installed it and added it to my path. If you did this right at the end you should be able to run the below command.
user@bash:$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (Sourcery CodeBench 2014.11-36) 4.9.1
Copyright (C) 2014 Free Software Foundation, Inc.
I have put up the source code for this project on github. I also have added the instructions needed to compile the FreeRTOS and UEFI application in the readme.
Loading Application in to memory
Compiling FreeRTOS will generate an elf
file and we need to load this file in to the memory. Elf file has a very simple file format and readelf
is command that is very handy. Elf has a main header that holds the gist of the overall file and it also holds the pointers to other sections. Elf has different kinds of sections but we are interested only in the program section. The osloader just loops over all the program headers and loads the content starting at the virtual address. The virtual address value can obtained from p_vaddr
variable in the elf header. The header also has one more important parameter and that is the entry point (e_entry
). This points to the location from which the code should start executing.
UEFI has two kinds of services boot time and run time services. The OS cannot use the boot time services but it can make use of the run time services that are provided. So we need to provide a pointer to the structure.
/** Function pointer to FreeRTOS main */
typedef VOID (*freertos_elf)(EFI_RUNTIME_SERVICES* runtime);
/** Inside main */
/** declare function ptr */
freertos_elf start_elf;
....
/** Jump to the entry point */
start_elf = (freertos_elf)EntryPoint;
start_elf(gRT);
The above code should be self explanatory. The 1st lines defines a function pointer to the main with a pointer to the UEFI runtime services as a parameter. The start address of the FreeRTOS application is held by the Entrypoint
variable. Finally you just call the function and this should start the FreeRTOS app.
FreeRTOS Modifications
I had to make a few modifications to the FreeRTOS application for it to work.
You need to include UEFI.h
and modify the main function declaration so that it can accept the UEFI runtime services pointer.
#include <Uefi.h>
EFI_RUNTIME_SERVICES *uefi_services;
int main(void *pointer) {
uefi_services = (EFI_RUNTIME_SERVICES*)pointer;
ARM has two modes of operation ARM and Thumb mode. When you jump from UEFI it seems to be in thumb mode whereas our application was compiled in arm mode. So we basically check and then make a switch to arm mode if needed. The pointer value also needs to stored as we will be using the registers for other operations.
/** Application code starts here */
start:
_start:r
_mainCRTStartup:
.thumb
thumb_entry_point:
blx arm_entry_point
.arm
arm_entry_point:
/* store runtime pointer in r10 */
mov r10, r0
....
/* Load the pointer back to r0 */
mov r0, r10
mov r1, #0 /* no argv either */
bl main
The exception vector table that is responsible for handling interrupts in ARM can be moved around. UEFI uses a different location and in order for our interrupts to work we need to relocate the vector table address.
ldr r0, =0x40014000
mcr p15, 0, r0, c12, c0, 0
mrc p15, 0, r1, c12, c0, 0
Debugging UEFI
We will be using arm-none-eabi-gdb
to debug UEFI code on qemu. Gdb needs UEFI core application symbols and also you need add those of the application you developed. When you start qemu you will get a log at the beginning that looks like this.
add-symbol-file path-to-edk2/Build/BeagleBoard/DEBUG_ARMLINUXGCC/ARM/Omap35xxPkg/MmcHostDxe/MmcHostDxe/DEBUG/MMC.dll 0x87AD0240
You should see a number of lines that show the path and address for different dll
files. You can either copy all those lines to a file and run the below command to load it or else you can just copy and paste it at the gdb prompt.
(gdb) source filename
Finally you need to add the symbol for your UEFI and freertos application. You can load the freertos symbols using
(gdb) add-symbol-file path-to-file/rtosdemo.elf 0x80300000
Copy rtosdemo.elf
and osloader.efi
file to the sdcard and then start qemu but now add -s
to enable debugging using gdb. Connect gdb to qemu with the below command
(gdb) target remote :1234
You should get the UEFI shell prompt as was shown above. Now we just need to run the osloader.
Shell> fs0:
FS0:\> osloader.efi
add-symbol-file path-to-file/osloader.dll 0x86E04240
Copy the above line and paste it in to gdb and you should be able set the breakpoints and step in and out of the code.
Conclusion
I wrote this long post hoping that it will help anybody the trouble I went though to get to this point. When I started, I had problems getting qemu to work, after that point I did not have any idea about how to get gdb to debug my code. Finally I had problems with ARM FreeRTOS application, mainly with regards to the thumb mode and the vector table address.
Please send me a mail if you need any additional info or if you find any mistakes.