Friday, April 19, 2013

UEFI and ARM

Introduction

UEFI [Universal Extensible Firmware Interface] is a standard for implementing the bootloaders and the interface between the bootloader and the OS.

UEFI defines a standard for executable images (second-stage loaders which load the OS - like rEFIt for OS X, bootx64.efi for Windows, grub-efi and elilo for linux). Which is actually a PE/COFF (windows "MZ" exe files).

Advantages

  • Fixed API and ABI
  • Extended type annotations [like, IN/OUT/INOUT for function arguments]. This can theoretically help spot some coding mistakes at compilation time.
  • Providing IO range and IRQ descriptions (like ACPI) for ARM systems

Disadvantages

  • It was designed by Microsoft and Intel (therefore, unnecessary code bloat)
  • All the code runs in the same address space, MMIO access is not protected by capabilities or any other security mechanism. While the situation is the same with other bootloaders and using the one-to-one memory mapping shared between all components [processes or libraries] allows to use the bootloader in the systems without MMU, UEFI was devised to provide a secure chain  of trust for the bootup process and 

UEFI Services

Since UEFI services are PE/COFF binaries, they export the symbols via the import/export tables. This allows to lookup the needed functions in the binary modules. UEFI defines a number of services. For example, the initial bootloader can rely on the UEFI bootloader for reading data from the disk. The part which confuses me is that most of these services are destroyed with the ExitBootServices() call, and the only usable service available at runtime is the RTC/Timer service. Since the OS cannot piggy-back on the bootloader for all its driver routines, why introduce a complex bootloader at all? It does deliver potential vulnerabilities but does not have advantages over BIOS in terms of hardware initialization.

TianoCore EDK2 and UEFI on ARM

TianoCore EDK2 is a reference UEFI implementation from Intel. It comes with various interesting packages and can be used to build both UEFI bootloaders and standalone applications for systems already running UEFI (like, most consumer-grade motherboards and laptops available on the market)

  • ArmPkg - contains the Linux Loader and the drivers for CPUS (Cortex A8, A9, A15) - cache, interrupts (GIC), Timer
  • ArmPlatformPkg - contains the Uart, GPIO and Nor drivers for the ARM reference platforms, TrustZone setup routines, Exception handling and stack switching code
  • CryptoPkg - contains the wrappers for OpenSSL to allow using cryptography (for example, for UEFI Secure Boot)
  • DuetPkg - the package to test UEFI on an X86 computer
  • EdkShellPkg - the UEFI shell which allows browsing mass storage driver, booting custom images and interacting with drivers via the configurable variables
  • MdePkg - contains the runtime services and HAL (Hardware Abstraction Layer) for PCI and other busses.
  • NetworkPkg - contains the support for IPv6, DHCP, TFTP and SCSI (obviously, for network booting)
  • Nt32Pkg - Bootloader services for Windows 7 Embedded
  • OvmfPkg - ACPI emulation and Virtio
  • PcAtChipsetPkg - obviously the x86 support - Timer, PIC, HPET and PCI bridge drivers.
  • StdLib - EFI library and sockets


I think this particular implementation sucks. I could bear with the unreadable code, the fact that you have to put all definitions in like three files (platform, board files and .ini files). But. The reference implementation only works for ARM BeagleBoard. For PandaBoard, the source code clearly states that "the display driver is broken and uncommenting it leads to mysterious freeze". I've spent some time hacking on it but still didn't manage to get the display working. Neither the MMC. On PandaBoard, it just failed to work. On Galaxy Nexus, it started working after I switched it from the block mode to the "unsupported" streaming mode in the generic mmc host code. Weird.

Windows RT Bootup Process

Windows RT is a port of Windows 8 to the ARM architecture. Here, the UEFI is used to emulate an X86 setup on ARM: the ACPI tables and power states. Besides, UEFI is used to query the IO ranges for peripheral controllers (it is known that most ARM SoCs use memory-mapped IO for accessing peripherals and the only things an OS has to care about are the IO range and the IRQ pin. This is what UEFI provides). So, the advantage of the UEFI is that it allows to compile the OS kernel and drivers once and use that on various boards.

For Windows RT and Windows Phone 8, the boot process starts with the UEFI bootloader loading the bootarm.efi file. It then tries to read the BCD (Boot Configuration Data) table and mount the root partition. It does rely on UEFI for reading the drive at this stage. Next, control is transferred to the Windows NT kernel which calls the ExitBootServices() routine and loads the native block driver. So far, I've managed to partition the usb thumb drive properly and boot the bootarm.efi and make it recognize the partition on qemu emulating the BeagleBoard, but that's about all.

Alternatives

Linux kernel has recently also gained the support for building mutiple SoCs in one kernel. You can have a single kernel that boots on OMAP, Tegra and what not. The advantage is that you can have a kernel-side board driver that will initialize the drivers with needed data eliminating the need for ACPI emulation and complex binary table parsers.

Suppose you're an evil hardware manufacturer wanting to hide the details about your board and not contribute code to linux. What do you do then? Right, use the DTS or Device Tree. Originated on the PowerPC MACs, the flattened device tree was subsequently ported to ARM and is now supported by the u-boot bootloader and both Linux and FreeBSD kernels. It allows to describe all peripherals and driver parameters in a hierarchial text format which will then either be passed to kernel as is or compiled to a human-unreadable binary format (which is security by obscurity of course but that's what proprietary developers think is cool).

Let's take a look at the FTD file for a tegra board taken from u-boot. I find it neat that you can specify everything - IO ranges, gpio and irq pins. If the BSP code is written properly, you can get away with writing no board code, only a declarative IO map description.

/dts-v1/;

#include "tegra114.dtsi"

/ {
 model = "NVIDIA Dalmore";
 compatible = "nvidia,dalmore", "nvidia,tegra114";

 aliases {
  i2c0 = "/i2c@7000d000";
  i2c1 = "/i2c@7000c000";
  i2c2 = "/i2c@7000c400";
  i2c3 = "/i2c@7000c500";
  i2c4 = "/i2c@7000c700";
  sdhci0 = "/sdhci@78000600";
  sdhci1 = "/sdhci@78000400";
 };

 memory {
  device_type = "memory";
  reg = <0x80000000 0x80000000>;
 };

 i2c@7000c000 {
  status = "okay";
  clock-frequency = <100000>;
 };

 i2c@7000c400 {
  status = "okay";
  clock-frequency = <100000>;
 };

 i2c@7000c500 {
  status = "okay";
  clock-frequency = <100000>;
 };

 i2c@7000c700 {
  status = "okay";
  clock-frequency = <100000>;
 };

 i2c@7000d000 {
  status = "okay";
  clock-frequency = <400000>;
 };

 spi@7000da00 {
  status = "okay";
  spi-max-frequency = <25000000>;
 };

 sdhci@78000400 {
  cd-gpios = <&gpio 170 1>; /* gpio PV2 */
  bus-width = <4>;
  status = "okay";
 };

 sdhci@78000600 {
  bus-width = <8>;
  status = "okay";
 };
};

Conclusion

UEFI is evil. It does not solve any particular problems on X86 and brings bug-ridden ACPI to ARM. Besides, on ARM Microsoft devices, Secure Boot cannot be turned off to boot custom images. This effectively locks the user out of their device. In other words, it is a typical DRM (digital rights management) system with a chain of trust (or, rather, a chain of distrust) which is aimed at preventing the "bad" user from running custom software. This allows the vendor to remove all the freeware content from the application market, and users will be forced to buy it because they have no alternative.

On X86, most vendors have not implemented the support for choosing the UEFI image to load. While this does not prevent you from renaming your binary to windozish bootx64.efi, it does show the attitude and the direction we're heading into.

Mac OS X - an unusable UNIX

Introduction

So I've always been interested in Mac OS X. It is quite a nice combination of the BSD-like userland. The Darwin kernel and IOKit framework are quite nice examples of how a stable kernel ABI can be combined with dynamic configuration via property lists.

Since I needed an X86 box at home anyway (besides my laptop) and wanted an EFI-enabled machine, I got myself a Mac Mini 6.1. Which is a nice little piece of hardware featuring an Intel Core i5 CPU. I'll just describe my brief user experience below. It's not my first experience with OS X. I've previously used OS X 10.5.6 on my old desktop (which is a single-core AMD Athlon 64 without SSE3 support which is why the Voodoo XNU kernel with software SSE3 emulation was used).

First, User Experience


  • Well, the setup screen welcomes us and asks to set up WiFi. Which is nice. However, keyboard layout is not shown, wifi password is hidden, and I bet I've entered it correctly 3 times but got refused. Had to skip that one and set up networking after logging into the system.
  • Next, I had to set up my Apple ID. I've created one like 5 years ago and entered the fake birth year of 1900. Apple have since updated the setup screen to allow selecting the year from 1902 and above. Consequently, my profile had the year "-1". Ok, another example of the "intuitive interface".
  • Now, font smoothing can't be switched off globally. And OSX font smoothing does suck. Proponents of it say it looks exactly as if it were printed on a paper. I don't care at all. The problem is that my eyes get extremely tired of any antialiased fonts and this is not going to change until we all switch to high-resolution displays. By that I mean around 350-400DPI, a bit higher than Apple's "Retina" screens. I've used the 311DPI Sony Ericsson X1 phone, the 256DPI B&N Nook HD+ tablet and had a look iPhone 4. And antialiased fonts there do not hurt my eyes as badly as they do on a typical 96-DPI desktop screen.
  • The OS is very slow. Launching apps takes at least 3 seconds, with that annoying bouncing cursor spinning around. Ok, I realize I do have the HDD and "only" 4 gig ram, but that's incredible. A typical linux distro or even Windows 7 would be faster (ok, I have to admint Windows Vista indeed compares to OS X in terms of slowness)
  • I have the non-apple USB keyboard which I've used on a dozen of computers for the last 4 years. And this Mac Mini is the only piece of hardware which makes computing so much more fun again. The box does wake up from sleep when a key is pressed, but. The keyboard does not work after wakeup and needs to be replugged;  sometimes not even once.
  • Keyboard again. Dunno if it is somehow related to the fact that I ain't using an Apple keyboard, but I've never managed to have boot menu by pressing the Alt (Option) key.
  • There are two installer formats for applications. One is ".app" files which are actually installed into separate containers and can be updated via the App Store. The good thing is that they can be easily uninstalled. The other one is ".pkg" which contain Apple Installer packages. They are bad. Really bad. They're like windows installers. If that does not tell you anything, then read on. The problem is that these packages can overwrite any system file, and multiple packages from different vendors can lead to conflicts essentially leaving you with a non-upgradeable system. So windowsish. In most cases you shoud avoid such items but some shitty software (namely, Microsoft MSDN downloader, Oracle VirtualBox and others) are packaged in, well, .pkg.
  • App store contains little software. Even less free, open or at least non-paid software. There are more new games than for Linux, but prices are higher than on Windows or PS3. I guess with Steam coming to Linux, the situation will be quite the opposite in a year or so. At least the free Garage Band coming preinstalled recognized my MIDI keyboard. Still, multimedia support is very poor on OS X. The audio API is more complex than both ALSA and OSS, the latencies are higher, and on Linux, mplayer and ffmpeg with a variety of codecs comes with any distro for free. Hmm, there may be more video editing software, but for the home user (i.e., the consumer) that does not make much difference - it still won't play most videos downloaded from torrents out of the box. Heck, it does not even come with a torrent client.
  • For installing typical free software like nginx, qt4 and vim (like on linux), there exist various package managers. The most widely known, supported by Apple and containing the largest number of apps is MacPorts. I think MacPorts are good (and it's quite rare that I call anything good). They are between BSD's ports (where you have to put weird port-specific defines for flags in make.conf) and Gentoo where you can specify flags in separate file per ports and the flags have the same name for multiple ports. Besides, some software comes precompiled. Overall, it allows to build quite a usable UNIX system, although package querying capabilities and the number of precompiled software are not as good as in Debian.
  • XCode is more or less nice. I don't like IDEs (a makefile-based project is much more portable, and not tied to a particular editor), but I must admit that having out-of-the-box OpenCL integration is nice.

OSX vs Linux vs Windows

So, would I recommend OS X?
I began from Windows, and having hacked on it, I've learnt many things. Some of them are windows-specific (registry,   driver architecture, kernel and userland API, PE/COFF executable format), but it gave me the understanding of the concepts. Besides, some of this stuff can be reused in some other software (for example, the UEFI specification is very  windows-centric).
Next, I switched to Linux and learnt some BSD (namely, FreeBSD and NetBSD). Free software is a miracle. Lots of documentation and code. Compared to the windows world, where you have to dig knowledge from the darkest corners, all the C APIs are open. Besides, there are fewer abstraction layers on top of hardware and while hacking on linux drivers, I've learnt about the computer architecture and various hardware busses (I2C, SMBUS, USB protocol).
OS X allows you to use most free unix software you can use on Linux, but it has some nice proprietary software (like Photoshop and M$ Office).

Compared to Windows:
  • Runs UNIX software and comes with a POSIX software natively
  • Provides typical UNIX facilities - make and toolchain. This allows to easily link your app to any library. Much easier than setting up a Visual Studio project with hardcoded paths.
  • Comes with Ruby and Perl
  • Most WEB frameworks (Rails, Django) have UNIX as the primary target. The same is with libraries and language interpreters (like PHP) and web servers (NGINX). Which means that UNIX-like OSs get newer fresh versions of software faster and compiling from source code is easier
  • App Store and MacPorts allow updating all the software simultaneously. While in the windows-world every app comes with its own updater implementation and updating the whole system is inconvenient. Of course M$ adepts will come bragging about Active Directory and Group Policy, but please show me anyone who'd like to set that up at home.
  • Has a nice one-click firewall and comes with an SSH server.
  • Most software is more expensive than on Windows.

Compared to GNU/Linux:
  • Has more games and multimedia/office software
  • MacPorts is less flexible and has fewer software than either of Debian, Fedora or Gentoo
  • Hardware is hidden behind IOKit. While an average user doesn't care, the Linux's sysfs itself is like a debugger shell where you can interactively debug all the kernel drivers. I do care because I'm developing embedded software.
  • UI is less customizable
  • I hate closed-source code. With free software, it's so nice that you can share your changes and discuss the development on IRC and mailing lists.
So, I'll surely stick to GNU/Linux and other free OSs. But if I were to choose between Windows and OSX for my parents' computer, I would opt for OSX. Because it's UNIX, it has no registry, no DLL hell, all apps can be updated, and the security settings can be locked down with a few clicks instead of hundreds of settings in the Group Policy.