Sometimes doing detailed RE research on large complex device drivers might not be feasible: naming fields and fix all data structures might take a few days or a few weeks. and sometimes decompilers produce incorrect result due to compiler optimization. But we know most device drivers only consume public kernel routines, and Windows Driver Framework is now open sourced. Technically speaking, it is possible to host drivers in a special environment.
Hosting UMDF Drivers
It turns out not difficult to host User-mode drivers (UMDF) with specialized host process. The host process needs to load the driver using normal DLL load procedure, then fills in the WDF implementation using WDF Bind functions. The driver will run happily then once you have the way to handle IO request and send them to the actual target. Lumia 950/Lumia 950 XL’s Type-C controller driver is a notable example on this approach.
But how about kernel-mode drivers? In fact, a substantial portion of kernel-mode drivers can be ported to user-mode with few lines of code change, if:
IO requests are completed using WDF primitives.
No direct memory register access. Technically register regions can be mapped twice (PA -> km VA -> um VA), but I am crossing it out here for convenience.
Code can live well in passive IRQL level. No processing logic should exist in other IRQLs. UMDF code all runs at passive IRQL.
Given these constraints, I found out that the ANX7816 HDMI/DPI transmitter driver is a fitting example to start with.
Write Your PE/COFF Loader
Since there’s no official way to load a kernel-mode driver into user-mode process, a customized PE/COFF Loader is required. Modern drivers have ASLR (dynamic base) and NX features enabled, so it is important to take care about relocation and page permissions. Thumb-2 code relocations need to be specially handled. Each MOV32(T) relocation comes with two instructions, the first MOVW sets the lower 16bit of the relocation address, and the second MOVT sets the higher 16bit.
In addition, the cookie canary needs to be initialized to some cryptography-safe random number. The canary is in .data + 0x4, and it is 4 bytes long. Otherwise you will get a quick qfastfail:
Once code is loaded into the memory, just invoke the standard DriverEntry function. The address is exactly the entry point address of the PE/COFF file.
Please also consider visiting the project for more information, including the reference source code.
Running Windows on a consumer calculator was a dream until the appearance of HP Prime G2 Graphing Calculator. This calculator has the most superior hardware specification among the market so far. And more importantly, HP switched to ARMv7-A on the calculator (!)
According to TI-Planet, the calculator comes with a lot of test pins including SD/MMC, JTAG and UART.
Sarah (winocm) explored the possibility of running Windows RT on Qemu platform. Some information has been changed as of 2019, but the post is still worth reading.
Microsoft also partnered with NXP to enable Windows 10 IoT on iMX SoCs. Given these facts, this calculator should be technically capable of running Windows 10 IoT (or generically saying Windows on ARM.)
Pushing the limit of Windows System Requirements
Microsoft Docs site outlined the system requirements for the Windows 10 family. To run the small footprint (still large compared to Linux), the system should have a x86/x64/ARMv7/AArch64 processor, at least 256MB memory and 2GB storage. The device should also has a functional UEFI firmware and full ACPI & SMBIOS tables.
There isn’t sufficient onboard storage in this case (and Windows doesn’t support raw SLC NAND flash either), but USB device can be attached to the calculator and acts as a boot device.
Sarah has summarized ARMv7 architectural requirements for running Windows. The requirements have changed a bit across years, so here is my new summary on this:
System processor: ARMv7-A compatible processor with VFPv3+VFP HalfPrec, VFPv4 is also okay
Memory: At least 256MB of RAM (can probably do less)
- System timer (either architectural or vendor-specific)
- ARM Generic Interrupt Controller (GIC) or Broadcom Interrupt Controller on BCM2835/2836/2837, both CPU and Distributor Interface. GICv2 or higher is required if GIC is present and declared.
- Supported UART, either NS16550, BCM283x or some vendor-specific IP (in the case of Qualcomm and NXP)
- Firmware: UEFI 2.3 or higher
In this case the calculator meets those hard requirements. However the SoC runs in 396MHz by default, significantly slower than the 400MHz/1GHz baseline. Also I only report about 250MB memory to the system while the minimum requirement is 512MB with user interface. However, it boots!
I think we can probably further push the limit. If I recall correctly, Windows Server Nano requires about 150MB memory to boot on amd64 systems.
Using a compiler with hardware float support is also helpful for speed up the boot speeed (30s -> 4s.) Also Windows mandates VFPv3+ support on ARMv7 platforms.
The screen initialization as well as some I/O mux assignments are completed in U-Boot. The current U-Boot does not support 24/32bpp Serial RGB LCD interface operation, so I added the Serial RGB support by referring the implementation of iMX FrameBuffer in Linux Kernel. The code has not yet been upstreamed to the master but I should do this later.
UEFI just picks up the FrameBuffer allocated by U-Boot and registers the Graphics Output Protocol. Unfortunately the screen resolution cannot satisfy the minimum 80*25 console requirements, so I patched Console DXE components by adding a new 40*12 mode.
Prior to the hack in console components I did some trick by reporting 640*480 output in the UEFI GOP protocol. The trick also helped me getting diagnostics information from Windows Boot Manager because 320*240 isn’t sufficient to display the error code. I periodically run block-transfer (BLT) from FrameBuffer to the filesystem and saved BMP files on a USB drive.
As for USB support, fortunately the USB IP in iMX6 is fully standard-compliant and in-box support is included in recent Windows versions. Therefore once I configured the OTG controller into host mode and pass the MMIO region to UEFI and Windows, it works magically without problems. However, Wenting and me have not yet figured out the VBus source on the OTG port (we assume some DC-DC circuit that controlled by SoC or PMIC but this hypothesis has not yet been verified) so the port has to be powered using some external voltage source for proper functionalities.
Make sure you properly report memory size and address regions in SMBIOS, or Windows will do some incredibly weird things (TM).
Timer and HAL Extensions
Windows will fail to boot if no Interrupt Controller or System Timer is found. A system time can be registered using either GTDT table (ARM architectural table) or HAL extensions via CSRT table.
iMX6ULL comes with three timer complexes: iMX GPT (Generic Purpose Timer), EPIT (Enhanced Periodic Timer) and ARM Architectural Timer. ARM Architectural Timer is new to iMX6UL/ULL since earlier iMX6 SoCs use cores such as Cortex A15 which lacks proper Architectural Timer support. Microsoft has implemented a EPIT HAL extension for the system timer, but it was not loaded at first due to a hardware ID mismatch in CSRT table and system image (I downloaded an earlier FFU for iMX6 Solo.) So in the case when no timer is available, Windows will desperately attempt to initialize an assumed timer and the system crashes due to invalid memory access.
The iMX6ULL datasheet does mention the presence of architectural timer but doesn’t say much about the initialization procedure. The EDK2 code can initialize the timer counter but GIC PPI (Per Processor Interrupt) does not work properly. However, Windows successfully initializes and utilizes the timer via GTDT table.
At this moment the UEFI utilizes EPIT timer and turns it off upon the hand-off to Windows. Windows picks up the generic timer and initializes it then. At some later point the UEFI might switch to generic timer too, with the proper initialization procedure.
A few peripherals needs the Smart DMA (SDMA) controller HAL extension and it isn’t too hard to load it.
Show me that in action!
[Security] 3rd party image can be loaded after EndOfDxe: VenHw(0D51905B-B77E-452A-A2C0-ECA0CC8D514A,004118020000000000)/USB(0x0,0x0)/USB(0x2,0x0)/HD(1,GPT,F9ADAEA9-E8DE-4291-B191-5DABA6DC1215,0x800,0x100000)/\efi\boot\bootarm.efi.
InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 8F257028
ConvertPages: failed to find range 10000000 - 100EEFFF
Loading driver at 0x0008E9D6000 EntryPoint=0x0008E9E7511 bootmgfw.efi
InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 8F28EB10
ProtectUefiImageCommon - 0x8F257028
0x000000008E9D6000 - 0x00000000000EF000
InstallProtocolInterface: 752F3136-4E16-4FDC-A22A-E5F46812F4CA 8FBFF88C
ConvertPages: failed to find range 102000 - 102FFF
Disabling EPIT timer on ExitBootServicesEventSetUefiImageMemoryAttributes - 0x000000008F78A000 - 0x0000000000003000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F787000 - 0x0000000000003000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F784000 - 0x0000000000003000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F77F000 - 0x0000000000005000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F77C000 - 0x0000000000003000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F779000 - 0x0000000000003000 (0x0000000000000008)
Where’s Secure Boot and TPM?
There are not really necessary. However, since OP-TEE has iMX6/7/8 support, you can run a secure monitor in TrustZone (TZ) and provide those services via Secure Monitor Calls from EL1/PL1.
In fact the official iMX Windows IoT implementation comes with OP-TEE bundled. I skipped it due to the concern of memory utilization.
But what about drivers?
Windows 10 IoT BSP repository have a lot of available iMX6/7/8 driver sources available. USB works out of box as mentioned. The calculator touchscreen and keypad drivers need to be implemented.
The touchscreen and keypad drivers are available in the Linux kernel tree so it should not be too hard to port them on Windows.
Can it boot Windows RT 8.1?
Maybe. Update: Windows RT 8.1 won’t boot, but later versions do. Windows PE won’t boot in ramdisk mode because 256MB memory isn’t sufficient. I haven’t got flat boot mode work, it enters some loop with no further initialization once devices are enumerated (including the USB drive I used.)
But I want to boot Linux too!
You have two options:
Just use U-Boot to load zImage, device tree and initrd
Move FD, MpPark and FrameBuffer memory regions to the top of the system memory region and leave the lower 128MB memory unoccupied
Load Linux via either GRUB or directly from EFISTUB. It boots and here’s a snippet of the boot log:
EFI stub: Exiting boot services and installing virtual address map…
Disabling EPIT timer on ExitBootServicesEventSetUefiImageMemoryAttributes - 0x000000008F97B000 - 0x0000000000003000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F978000 - 0x0000000000003000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F973000 - 0x0000000000005000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F970000 - 0x0000000000003000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F96D000 - 0x0000000000003000 (0x0000000000000008)
SetUefiImageMemoryAttributes - 0x000000008F96A000 - 0x0000000000003000 (0x0000000000000008)
Booting Linux on physical CPU 0x0
Linux version 4.14.98-g371433a62906-dirty (imbushuo@bc-macbookpro) (gcc version 7.4.1 20181213 [linaro-7.4-2019.02 revision 56ec6f6b99cc167ff0c2f8e1a2eed33b1edc85d4] (Linaro GCC 7.4-2019.02)) #2 PREEMPT Thu Nov 14 03:10:29 EST 2019
CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c53c7d
CPU: div instructions available: patching division code
CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
OF: fdt: Machine model: HP Prime G2 Calculator
Memory policy: Data cache writeback
efi: Getting EFI parameters from FDT:
efi: EFI v2.70 by EDK II
efi: ACPI 2.0=0x8f49b000 SMBIOS=0x8f9a8000 SMBIOS 3.0=0x8f9a6000
OF: reserved mem: failed to allocate memory for node 'linux,cma'
CPU: All CPU(s) started in SVC mode.
Built 1 zonelists, mobility grouping on. Total pages: 64516
Kernel command line: zImage.efi root=/dev/ram0 rw initrd=/rootfs.cpio.gz dtb=/imx6ull-14x14-prime.dtb
If you are using the NXP Linux configuration, it will eventually panic because it reads initrd memory address from device tree or some predefined configuration settings and of course the initrd is loaded as somewhere else in UEFI. You need to remove those settings and make the configuration more generic.
People talk about running Windows on random devices so I want to troll them by the end of the year.
How about other projects? What’s the next thing?
They will continue. So far, I need to implement more drivers, enable UEFI boot from on-board NAND, implement a calculator-friendly UEFI boot user interface and explore the possibility of booting stock HP PPL OS from my UEFI.
This project is partially derived from Microsoft’s Windows 10 IoT BSP for NXP SoC platforms. I would also like to thank Wenting for sending me his Prime G2 with UART lines soldered for debugging.
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.
While this article applies to most ARM SoCs, the following content will use Tegra as the example. NVIDIA developed a few solutions for Windows on ARM in Windows 8 era: Tegra 3 (Tegra30) and 4 (Tegra114). No further model have official Windows BSP (Board Support Package) released publicly due to low market acceptance of those Windows RT products.
Despite of that, general AArch64 processors are capable to run Windows 10 without additional HAL extension library if the following conditions are satisfied:
Architecture Timer with ACPI GTDT table description. Either CP15 or MMIO clock is okay.
Generic Interrupt Controller v2/v3 (we are not yet aware of v4 support) with ACPI MADT (APIC) table description, or Broadcom Interrupt Controller
AArch64 instruction set (crypto extension is not required)
ARM Performance Monitor Unit with ACPI MADT (APIC) table description
One noticeable exception the initial generation of Qualcomm Kryo (Snapdragon 820, 821) due to the faulty cache design in large core cluster. Windows removed the required erratum for it due to the complication of patch maintenance.
In the case of Tegra X1, it satisfied all conditions outlined above. I used an old-bootrom Nintendo Switch as my experiment platform since it is much cheaper than Jetson TX1. Additionally, there is verified CoreBoot and U-Boot source code for these Tegra X1 devices including Nintendo Switch.
I assume you are familiar with the NVIDIA RCM Exploit (Fusee-Gelee) as well as Tegra Boot flow. If you are not familiar with Tegra Boot flow, please refer to Tegra Technical Reference Manual available on NVIDIA developer site.
Port U-Boot Code to EDK2
There are a few environment assumptions that need to be addressed while porting U-Boot device/driver code to EDK2:
While U-Boot runs in AArch64 context, it only utilizes little amount of memory at the memory bottom in most circumstances. EDK2/TianoCore loads everything as high as possible per UEFI specification. Certain peripheral operations are not 64-bit addressing aware. It’s okay to force converting 64-Bit pointers to 32-Bit without data loss in the U-Boot assumption, but in EDK2 this might lead to issues. One case is SDMA (single operation DMA). Tegra SDHCI controller SDMA operations are not 64-bit addressing aware. To address the issue, I slightly modified the DMA bounce buffer allocation library (also ported from U-Boot) to allocate bottom memory instead.
Syntax styles. U-Boot observes the Linux naming convention for functions and types; EDK2 observes the Windows style. It might be a good idea to write a shim to provide functions like readl/writel as well as udelay/mdelay.
There is probably no need for porting generic classes (e.g. udevice). You might not need them in EDK2 context.
To save myself some time bootstrapping the microSD slot, I ported the clock and device framework from U-Boot to EDK2. Here are a few suggestions while porting U-Boot code to EDK2:
Address issues mentioned above.
Put device specific definitions into “Include” directory, use PCD database when necessary.
Install these code services as DXE driver whenever possible. Invoke them using protocols.
For board/machine-dependent code library (e.g. mach-tegra), depends on the usage to integrate them with driver or use additional library instead.
From Device Tree to ACPI
Device Tree is the de-facto standard in ARM to describe the system and peripheral hierarchy. Windows RT introduces the intensive use of ACPI on ARM platforms. I will cover some required tables for a success Windows startup on ARM platforms. For tables such as CSRT and DSDT, check out the Microsoft documentation.
GTDT (Generic Timer Description Table)
For SoC with architecture timer, ARM defines GTDT table to describe platform timer information. In the device tree, an architectural timer may looks like this:
If your platform does not have MMIO architectural timer, write the address as 0xFFFFFFFFFFFFFFFF.
If you boot from EL2, you are required to supply all timer values. Otherwise only EL1 timers are needed.
PPI starts at 16. Plus 16 for all interrupt numbers you have in the device tree. The four interrupts are Secure EL1, Non-secure EL1, virtual timer and hypervisor in sequence.
You may have platform watchdog, supply it in the GTDT table too (see Qualcomm example). It is not mandatory for booting Windows though.
MADT (Multiple APIC Description Table)
Most AArch64 SoC systems have one or more GIC-compatible interrupt controllers. Windows has inbox GIC support, all needed is supplying proper information in the MADT table. The table also describes ARM Performance Monitor Unit information for system’s reference. In device tree, GIC and PMU look like this:
In MADT table, each processor core havean table entry. Make sure you have the same CPU object in DSDT table, with identical and unique UID and CPU interface ID.
If your platform supports ARM PSCI, parking address field can be ignored.
The four registers in GIC device tree are GIC distributor, GIC base address, hypervisor GIC base address and virtual GIC base address.
You might need to supply GIC redistributor address on GICv3 architecture.
SPI interrupt number starts at 32. Plus 32 for all performance interrupt number in MADT table.
MPIDR value needs to be referred from platform resources.
DBG2 (Microsoft Debug Table 2)
Microsoft defines DBG2 table for ARM platforms. Although Microsoft docs mark DBG2 table info as mandatory, you do not need to supply debug device information if you just want to boot Windows as a proof-of-concept :P. An empty DBG2 table is enough for booting.
For debug purposes, it is necessary to define at least one debug device (8250/16550 serial or USB) in DSDT and DBG2 table. More information can be found at here.
FADT (Fixed ACPI Description Table)
Indicates PSCI support and Hardware-reduced ACPI mode, then you are good to go.
With much effort, Windows on ARM can run on a variety of AArch64 devices. There’s still much work between “just-booted” and “usable”, and it may cost you countless nights to achieve your marvel, even if there are always guys ask you “why”:
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.
.1526 21:47:43 (1) !! ERROR: WMI CONNECTION errors occured for the following namespaces: ………………………………………….. 20 ERROR(S)!
.1527 21:47:43 (0) ** - Root, 0x80010114 - The requested object does not exist..
.1528 21:47:43 (0) ** - Root, 0x80010114 - The requested object does not exist..
.1529 21:47:43 (0) ** - Root/subscription, 0x80010114 - The requested object does not exist..
.1530 21:47:43 (0) ** - Root/DEFAULT, 0x80010114 - The requested object does not exist..
.1531 21:47:43 (0) ** - Root/CIMV2, 0x80010114 - The requested object does not exist..
.1532 21:47:43 (0) ** - Root/CIMV2/Security, 0x80010114 - The requested object does not exist..
.1533 21:47:43 (0) ** - Root/CIMV2/TerminalServices, 0x80010114 - The requested object does not exist..
.1534 21:47:43 (0) ** - Root/nap, 0x80010114 - The requested object does not exist..
.1535 21:47:43 (0) ** - Root/SECURITY, 0x80010114 - The requested object does not exist..
.1536 21:47:43 (0) ** - Root/STANDARDCIMV2, 0x80010114 - The requested object does not exist..
.1537 21:47:43 (0) ** - Root/RSOP, 0x80010114 - The requested object does not exist..
.1538 21:47:43 (0) ** - Root/RSOP/User, 0x80010114 - The requested object does not exist..
.1539 21:47:43 (0) ** - Root/RSOP/Computer, 0x80010114 - The requested object does not exist..
.1540 21:47:43 (0) ** - Root/WMI, 0x80010114 - The requested object does not exist..
.1541 21:47:43 (0) ** - Root/directory, 0x80010114 - The requested object does not exist..
.1542 21:47:43 (0) ** - Root/directory/LDAP, 0x80010114 - The requested object does not exist..
.1543 21:47:43 (0) ** - Root/Policy, 0x80010114 - The requested object does not exist..
.1544 21:47:43 (0) ** - Root/Microsoft, 0x80010114 - The requested object does not exist..
.1545 21:47:43 (0) ** - Root/Microsoft/HomeNet, 0x80010114 - The requested object does not exist..
.1546 21:47:43 (0) ** - Root/aspnet, 0x80010114 - The requested object does not exist..
The documentation suggested performing a metadata registration. The following script is utilized for the metadata repair:
cd /d c:\temp
if not exist %windir%\system32\wbem goto TryInstall
cd /d %windir%\system32\wbem
net stop winmgmt
if exist Rep_bak rd Rep_bak /s /q
rename Repository Rep_bak
for %%i in (*.dll) do RegSvr32 -s %%i
for %%i in (*.exe) do call :FixSrv %%i
for %%i in (*.mof,*.mfl) do Mofcomp %%i
net start winmgmt
if /I (%1) == (wbemcntl.exe) goto SkipSrv
if /I (%1) == (wbemtest.exe) goto SkipSrv
if /I (%1) == (mofcomp.exe) goto SkipSrv
if not exist wmicore.exe goto End
net start winmgmt
It will throw some errors. Ignore them. Then reboot the server.
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 😛
I got a Nintendo Switch from my friend (for a research project). Meanwhile, I enjoyed the game “Overcooked” on Switch. In this game, you control cooks to perform variety tasks and then deliver orders in time. If orders are delivered in advance, some tip will be given. 1-4 players can play the game simultaneously.
It’s clear that you have to do everything as quick as possible to achieve high score in the game. Every task (e.g. cutting meat) need some time to complete. Certain task (e.g. frying) depends on other tasks. To eliminate unnecessary time cost (i.e. waiting for cutting to complete), I use the following strategies:
Minimize workers’ stall time (doing nothing). For example, it is not necessary for workers to wait for frying process (polling is not efficient). Like interrupts in modern machines, they can do something else like washing dishes and cutting meats while waiting for cooking. Once interrupt signals (frying completes), they enter interrupt servicing routine: get the food put into a plate. In most cases, the food is ready to serve then. Finally, they returned to what they were doing.
Again, make sure everything is doing something. This is especially important if you are playing with your friends. You had better analyze the dependency chain and discuss strategies with your friend before starting the game. Of course you should issue instructions to your friend during game if necessary.
Not all kitchens are easy to deal with. Some have dynamic arrangements – contents may change their location during the game session. Some kitchens have no constant light source. Other have isolated workspaces with conveyor belts or tables for swapping materials (I call it a “bus”).
Conveyor belts are high-latency bus, but they have relatively high bandwidth (Hey DDR4, I am looking at you). In some kitchen scenarios (e.g. making burgers), you can put everything on the belt in batches and fetch in batches too.
Some conveyor belts connect to trash can, which means materials must be fetched before the expiration. But some cook utilities will appear again if you put them into the trash can. In this way, you can prioritize the transfer of contents on the conveyor belt.
Try achieve full-duplex transfer and prefetch to save time. Consider the following scenario: you have a pot that cooks rice at once side, and food materials (rice and flour tortilla) on the other side. For the first time, you get rice and put them into pot. Once rice finishes, you carry cooked rice to the other side and wrap them with tortilla. Don’t get tortilla separately in another transfer. If you really have to do that, you can instruct other cooks (if exist) to prefetch some for you.
Prefetch might not work for all kitchens. In the case of cooking soup, mice will steal your food if it is unattended for a while. But you can secure processed food in pots so it won’t get stolen.
Get familiar with your kitchen and good luck! (Well, it is a bit boring if you have learned Machine Architecture and Operating System internals).