Disclaimer Most information only applies to SurfaceRT, but some yahallo information applies to Surface RT 2 too.
Because nvidia/microsoft didn't implement a check for maximal buffer size when using a smc instruction to register a new shared buffer, it is possible to create a buffer overflow, which is then used to overwrite the area where a BootServices->SetMem uefi call writes its memory.
// Register a new shared memory buffer at 0x4000_0000 (IRAM) with size // 0x6001_e0e0. The following algorithm is used to determine the end address // that the QueryVariable call will write over: // response_area = (request_area + (area_slice >> 1));
Ret = ArmCallSmcHelper(0x03, 0x06, 0x40000000, 0x6001e0e0);
This is from the yahallo source code. We didn't know how to interpret this comment, so we decided to ask the creator of the exploit how to reproduce the smc calls to write to overwrite different memory locations.
First you need to know which values are used from the SMC in the calculation, we don't care about parameters 1 and 2. Parameter 3 is request_area, so in yahallo source 0x40000000. Parameter 4 is area_slice, so in yahallo source 0x6001e0e0.
The calculation has the following formular:
response_area = (request_area + (area_slice >> 1))
0x7000_f070 = (0x4000_0000 + (0x6001_e0e0 >> 1)
0x7000_f010 = (0x4000_0000 + (0x6001_e020 >> 1))
The Tegra 3 Technical Reference Manual (TRM) has important information about virtual addressing and configuration registers in section 18. You can download it here, but you need a Nvidia Developer account to download it.
The SMMU (System Memory Managment Unit) is part of the SOC, and not part of the Cortex-A9 cores, but the CPU cores have a MMU too, you can disable it with a simple ArmDisableMmu() in the edk2 source tree. The CPU has access to all memory (from the TRM), but this seems wrong, because you can't access certain memory locations, like the SMMU Enable Register and why would yahallo make the trustzone 0 MB in size (SMMU side) when you are able to write to everything in memory (when CPU MMU is disabled)?
To disable the SMMU, you need to write 0's to the SMMU Enable Register (0x7000f010). This can only be done when you are in secure mode, so it doesn't work if you just disable the CPU MMU. Because of the yahallo exploit we can write to all memory locations in secure mode, because uefi executes in secure mode. (We use an uefi call for writing to arbitrary locations)
ArmCallSmcHelper(0x03, 0x06, 0x40000000, 0x6001e020);
You can use this line to disable the SMMU, replace it with
ArmCallSmcHelper(0x03, 0x06, 0x40000000, 0x6001e0e0); from Smc.c in yahallo source. Alternatively you can copy the code, and paste it somewhere else with the modified line. How to modify the line is described in Understand the exploit above
Yahallo writes 32 bytes of 0's to 0x7000f070, so it writes the Secure Region Configuration Base register, Secure Region Configuration Bound register and the Protected Region Configuration Bound register.
This makes the Trustzone 0MB in size (SMMU side), so you have access to non-trustzone-secured* memory, when CPU doesn't restrict memory access. The CPU restrictions can be removed by disabling the MMU.
* Some registers are restricted to be only accessible from Trustzone-secured CPU memory access. (e.g. from the SMMU_ENABLE register from TRM: This register can only be accessed by TrustZone-Secured accesses from the CPU.)
We gave up on this, because we realized that we need to modify ARM MMU page tables, which we didn't find out how to do. If you know how that works, please contact @Leander on our discord server.
The MMU is also used for virtual addressing, not only for protected memory. Virtual addressing is necessary for most applications and operating systems. Linux works without it, but most programs don't work without virtual addressing. Because of that we need to enable the MMU for linux, but when MMU is enabled, it blocks access to trustzone memory, so the trustzone memory needs to be unmapped by modifying the page tables of the Cortex-A9 cores.
The advantage of completely eliminating UEFI and TrustZone is, that we don't need to modify the linux source code, only a device tree is needed, which can be mainlined. Without getting this way to work, our linux work probably won't be mainlined ever.