Recent Apple MacBook Pro models are usually equipped with long OLED touch display that officially named “Touch Bar” that substitutes the traditional Function (Fn) key row. Internally, this thing is called Dynamic Function Row (DFR) according to the naming prefix of related system components. In Boot Camp Windows installations, the Touch Bar acts as basic media and function keys without advanced graphical capabilities.
Given the fact that the Touch Bar is implemented by either Apple T1 or T2 chip, it is certain that one or more connection technology such as USB are utilized for the host communication. However, the communication protocol remains unknown for a long time due to the availability(cost) of device and the nature of typical technology users.
Third-party developers have developed applications that simulates Touch Bar on the host (x86) system. A quick inspection through the source code reveals a library that generates the continuous stream that contains the graphical content on the Touch Bar. It suggests that the content on Touch Bar is pre-rendered on the x86 system and gets transferred to the ARM side (T1/T2). Since T1 and T2 are both connected on the USB bus, packet captures were conducted over the USB bus.
During packet capture sessions, the pattern of periodic large packet transfers were observed. However, the size is not fully deterministic, but mostly it is larger than 54000 bytes. More image with certain patterns (single color, rainbow, etc.) were transferred to help analysis the binary structure. With sufficient samples, the following characteristics and structs were concluded:
A semi-static header is present for each frame update.
Sometimes it is not feasible to get UART access on consumer blackbox devices (e.g. Lumia 950XL). In the case of ARM ACPI debugging, the lack of UART access may make early boot debug incredibly difficult.
Starting from Linux Kernel 5.0, it is now possible to enable FrameBuffer-based early kernel display. All you need to do is:
Enable Earlyprintk and Earlycon support. By default it is on.
Enable EFI FrameBuffer display device.
Enable EFI FrameBuffer Earlycon device.
(Optional) Enable PSCI checker to verify PSCI functionality.
This UEFI project will be finalized on March or April, then I will transfer the ownership to LumiaWoA organization.
Mainline Linux & Android
Lumia950XLPkg makes it possible to run mainline Linux on Lumia 950 XL. So far I’ve brought up main components including touchscreen and Bluetooth. Wi-Fi will be available once I figure out the way to declare firmware-initialized PCIe bus in device tree.
Freedreno is also possible. However, it may takes significant time to figure out proper MIPI DSI commands for display panel enablement.
There are other people working on Android-side project for Lumia 950 XL, but I am unable to disclosure the progress at this moment.
Joining Microsoft / LinkedIn
I am excited to announce that I am joining Microsoft / LinkedIn in the coming summer. But the employment may have potential CoI (conflict of Interest) on projects that I am currently working on. I wish I can continue on making the next big thing 😛
Windows on ARM is not a new topic. There are some guys attempted to bring up Windows RT and Windows 10 on Qemu (ARM/AArch64 target). It even runs on Raspberry Pi 3. Obviously it is not a Snapdragon 835-only thing. We can give it a hand on our own Single Board Computers.
This article covers some important details in Dragonboard 410c SBC’s aa64 UEFI implementation.
Windows Boot Requirements
Bootstrapping your own EDK2/TianoCore UEFI
Memory Allocation / Memory Management Unit
UEFI Flash Definition
First-stage Bootloader (Little Kernel)
Persistent NVRAM Support
A “Working” RTC
Multi-processor startup (PSCI)
Windows Boot Requirements (AArch64)
AArch64 architecture processor. It seems that AArch64 cryptography extension is required too (Raspberry Pi 3 randomly throws UNSUPPORTED_PROCESSOR bugcheck, rs4 fixed the issue). The bugcheck is raised in Errata Check (a hardcoded ID check).
A working interrupt controller. Most AArch64 SoC cores include ARM GIC, so there’s little work to do here. The only exception I know is BCM2837. Windows has inbox Broadcom interrupt controller support (for the sake of Raspberry Pi). But if your SoC has additional third party interrupt controller, you need to supply your own HAL extension library. There is few documentation for this available though…
A working processor timer. If not, supply your own HAL extension library.
These requirements are fairly similar to ARM SBBR certification requirements. If your SBC has a working EDK2/TianoCore UEFI, then you are probably good to go. Bootstrapping your own EDK2 is pretty easy too.
Bootstrapping your own EDK2/TianoCore
The board I used (DragonBoard 410c) doesn’t have a known EDK2/TianoCore implementation. So I have to build my own. This repository for Raspberry Pi 3 is a good start point and reference for you.
You need to do these things in UEFI:
Initialize serial output (for debugging) and Memory Management Unit (MMU). Refer to your platform datasheet for device memory address allocation.
Retrieve required information from pre-UEFI environment and build Hand-off Blocks (HOB) for DXE phase
Initialize processor (exception vector, etc.) in DXE phase.
Initialize UEFI services (variable services) in DXE phase.
Jump to BDS phase, start Windows Boot Manager or something else.
Memory Allocation / Memory Management Unit
Memory allocation is a platform-specific thing. Check your platform HRD to get some idea about MMU and memory allocation. For Snapdragon 410, check out Qualcomm LM80-P0436-13.
UEFI Flash Definition
Our UEFI FD starts at 0x80200000. Update your tokens in platform definition and flash definition:
And the first piece code should be your SEC initialization code (without relocation).
Little Kernel (mentioned below) will be responsible for jumping into UEFI FD at 0x80200000 and handing off execution. If you want, you can actually removes Android-specific header and device tree validation in LK (apps/aboot.c).
First-stage bootloader (Little Kernel)
DragonBoard 410c uses ARM Secure Monitor Call to switch to AArch64 mode (See Qualcomm LM80-P0436-1 for more information). The stock close-sourced SBL doesn not recognize AArch64 ELF files (later model should). LK performs basic platform initialization (UART, eMMC, MMU, etc.) A modified variant LK also initializes FrameBuffer for U-Boot. We can make it work for our UEFI too.
Windows requires UEFI provide a BGRA FrameBuffer. To achieve this, we need to modify pixel unpack pattern in platform/msm_shared/mdp5.c:
/* Windows requires a BGRA FB */
writel(0x000236FF, pipe_base + PIPE_SSPP_SRC_FORMAT);
writel(0x03020001, pipe_base + PIPE_SSPP_SRC_UNPACK_PATTERN);
You can either specify a hard-coded address for FrameBuffer, or have a random piece of memory block to transfer information (pixel format, width, height, etc.) to UEFI. UEFI SEC phase retrieve the information, allocate HOB block and transfer information to DXE phase. A simple FrameBuffer driver retrieve information from HOB block, initializes UEFI Graphics Output Protocol. For optimal performance, initialize this piece of memory block as write-through cache memory in MMU initialization.
Persistent NVRAM Support
For persistent NVRAM support, it’s a good idea to use eMMC as storage device. This implementation demonstrates how to simulate NVRAM using eMMC and a piece of memory. I slightly modified it make it work for Qualcomm devices:
If eMMC NVRAM region is corrupted or uninitialized, provision it and perform a platform warm reset so I don’t get a synchronous exception in volatile variable initialization phase.
Modify dependency relationship to prevent “device not found” error in BlockRamVariable DXE initialization.
Windows Boot Manager depends on a “working” Real Time Clock for miscellaneous purposes. APQ8016/MSM8916 has a RTC on its PMIC processor PM8916. To access RTC services, read/write SPMI registers (see Qualcomm LM80-P0436-36). If you are lazy, just use Xen fake RTC in ArmVirtPkg.
To enable PM8916 RTC, set SPMI register 0x6046 to enabled state, then read 0x6048 and three following bits.
Note: I implemented my own PMIC protocol called PM8916Protocol that read/writes PMIC register on SPMI bus, slave #0. This RTC library is based on Xen face RTC library from ArmVirtPkg.
4KB / 64KB Page Table
Revised: On certain SoC platforms, runtime memory allocations are not comply with 64KB alignment requirements. There are two solutions, either round these memory regions to 64KB alignments, or go to MdePkg/Include/AArch64/ProcessorBind.h:
/// The stack alignment required for AARCH64
#define CPU_STACK_ALIGNMENT 16
/// For the sake of our SBCs
#define RUNTIME_PAGE_ALLOCATION_GRANULARITY (0x1000)
I randomly hit crashes (synchronous exception) during my UEFI development. After some investigation, it seems that the problem is related to load/store commands. (See ARM Errata 835769, 843419) To prevent random crashes, add these two flags to your GCC compiler:
Multi-Processor Startup (PSCI)
For platforms that implement ARM PSCI, indicate PSCI support in ACPI FADT table:
Typically you don’t need HVC call for PSCI. If you did so (and your platform doesn’t support HVC call for PSCI), you will get a INTERNAL_POWER_ERROR bugcheck with first parameter of 0x0000BEEF.
If you indicates PSCI support, you don’t have to provide parking protocol version in your ACPI MADT table. Simply set it to 0. Here’s one example:
Windows 8 and Windows 8.1 has a minimum screen resolution constraint for Windows Store Apps (aka. Metro Apps or whatever). If the screen resolution doesn’t meet requirement, user will see a prompt indicating the resolution is too low for these applications.
However, on certain platforms (like phones and single-board computers), it is not convenient to change resolution. Recently I am trying Windows RT 8.1 on Lumia 640 XL. Qualcomm has the resolution hard-coded in platform configuration, so I was unable to change the resolution. 1280 * 720 is not sufficient for Store Apps.
But there was an exception – the PC settings (aka. Immersive Control Panel) app. It always opens regardless of current resolution settings. So how can I force other applications to launch?
Let’s turn to TwinUI.dll. It’s one of the core components of shell infrastructure. Start IDA Pro, load TwinUI with symbols from Microsoft. Go ahead and search the existence of PC settings app. All Windows Store Apps are associated with a package family identifier. Let’s search it. In this case, it’s windows.immersivecontrolpanel_cw5n1h2txyewy.
Bingo. We found it in some functions.
By checking it’s references, we learned that layout checking routine verifies whether it is a desktop application, or PC settings app when resolution doesn’t meet requirements. Either you can patch layout checking routine or PC settings PFN verification routine. I decided to patch the second one, however patching the first is probably a better idea.
On ARMv7-A platform, I simply patched initial register store operation and the branch. Instruction BLX call was replaced with a simple NOP(MOV R0, R0).
There are two version of the PC settings check routines, so I need to patch both. The other one is similar to this one. Patching the layout verification routine (actually a better idea, as this patch will have some trouble when launch files from desktop) / patching on other architectures should be similar to this one.
When Microsoft decided to adopt MSBuild on .NET Core platform, project.json was not dropped immediately until first toolchain RTM arrives. Dotnet Development on Universal Windows Platform Development leverages .NET Core too, but the depreciation progress is significantly slower than other .NET Core platforms due to historical reasons. UWP uses project.json for package management and MSBuild for builds.
In Visual Studio 2017 April Update, Microsoft finally migrates new UWP projects to full MSBuild-based project system. But our projects, which creates on early 2015, doesn’t get an auto migration as expected. Hence we decided to migrate them manually for additional benefits like better toolchain stability and advanced customization features.
Reminder: Do not attempt to use “dotnet migrate” CLI command, it won’t work for UWP projects.
Notify all your team members. Make sure everyone has Visual Studio 2017 with April update installed.
If you have continuous integration environment configured, make sure build agents have NuGet 4.1 or higher installed (3.5 or 4.0 won’t work).
Lock VCS during migration to prevent additional incidents. (We’re using TFVC for source management so that it will be easy)
Clean up all projects (including bin and obj directories)
Iterate all project directories
Find C# project file, open with your favorite editor.
Add following property group before project file lists:
First of all, Windows “Gatekeeper” doesn’t block the execution of applications that don’t require installation. I tried to run PuTTY, a popular tool on Windows and it works.
Secondly, Windows “Gatekeeper” is based on Microsoft SmartScreen, which means disabling SmartScreen will turn it off too. Prior to application execution, SmartScreen will send file hash and publisher information(including certificate thumbprint) to Microsoft’s server, then SmartScreen server send back metadata including application reputation. Response is signed with a specific key that will be checked in client side for message integrity.
Unlike macOS, attempt to start application from console(e.g. Command Prompt and PowerShell) will trigger “Gatekeeper”.
The window is web-based. Although you can’t modify the response directly(no one wants to deal with sha256RSA unless the key leaks), you can attach a debugger to have some fun with it.
Microsoft claims that this feature is opt-in for most Windows SKUs (except Windows 10 Cloud AFAIK), and it is not revalent to UMCI (User-mode Code Integrity), which is enforced in Windows 10 Cloud.
This step is absolutely easy on Ubuntu/Debian and other officially supported distributions. If you are using Archlinux, go to AUR for .NET Core runtime.
Step 2: Prepare files.
In local Visual Studio or other development environment, publish project to a folder. (dotnet build -c Release)
Step 3: Setting services
I wrote a simple systemd service for my application:
Description=A certain ASP.NET Core application
ExecStart=/usr/bin/dotnet /opt/imbushuo/somepath/App.dll --server.urls=http://localhost:5050
Step 4: Setting up reverse proxy
Refer to your frontend server documentation for details.
If you are using per-environment configuration file, make sure the configuration starts with capital letter, like appsettings.Production.json . Otherwise, the Startup class will not load the settings file.
If you want to run multiple applications, make sure add configuration class in Program class and apply it. Then, pass server.urls parameter with address and port, like what I wrote above.
The following Program.cs has been modified to enable parameter support. Feel free to copy it:
public class Program
public static void Main(string args)
var configuration = new ConfigurationBuilder()
var host = new WebHostBuilder()