Developer Quickstart - AsahiLinux/docs Wiki

Developer Quick Start Guide

So you want to help us bring Linux to M1 Macs? Read on! This guide describes the recommended set-up for developing for Linux on an M1.

This guide is meant for developers who want to hack on Asahi Linux. It is not representative of what the install process for end-users will look like. It is meant for experienced Linux developers who may not know anything about Macs or M1 machines.

It is extremely unlikely that you will damage your computer by following these steps, but we can make no guarantees at this time. Please proceed at your own risk.

Introduction

Objective

We will be setting up a dual-boot M1 computer with two OSes: a vanilla macOS install (optionally with reduced/permissive security), and a "stub" macOS install with permissive security that will serve as a boot wrapper for Linux (or anything else we want to run through m1n1). You will also learn about what options are available to get a hardware serial console.

Prerequisites

Highly recommended:

TL;DR on M1 Mac Boot

Storage: the internal SSD is a GPT partitioned disk containing (initially) APFS containers, each of which contains multiple APFS volumes.

The boot process:

  1. SecureROM (Boot ROM) in M1 silicon starts
    • Either enters DFU mode (USB recovery) or
    • Boots iBoot1 from NOR (SPI) flash
  2. iBoot1 (global) looks for boot policies in iSC Preboot and tries to boot the active one from its OS APFS Preboot volume (as defined in NVRAM)
    • If the power button is held down, boots 1TR instead
    • This is what initializes the screen and shows the Apple logo
  3. iBoot2 (per-OS copy; ~= OS loader) boots the OS
    • Loads auxiliary CPU engine firmwares from OS APFS Preboot partition
    • Then loads a mach-o kernel wrapped in an img4 container from the Preboot partition
    • This is in charge of adjusting and passing in the ADT (Apple Device Tree)
  4. The OS kernel (XNU/Darwin) boots
    • Mounts the root filesystem, etc

Read [[SW:Boot]] and [[M1 vs. PC Boot]] for more info.

Mini glossary:

Notes:

Further Reading

If things go wrong

M1 Macs are effectively unbrickable as long as you do not write to SPI Flash (we highly discourage declaring the NOR Flash in the Linux devicetree: we are not aware of any reason to touch it at this point, so all it does is massively increase the risk of catastrophe; we will proceed with caution and safeguards if it ever becomes necessary to access directly). However, they do rely on software installed on the internal SSD storage to boot normally and into 1TR.

If you end up with your Mac in an unbootable state, follow apple's DFU recovery instructions. You will need another Mac running macOS to perform this process (Intel ones are fine). This should not happen if you follow the steps on this guide normally, but may happen if you end up accidentally writing to internal storage incorrectly (e.g. wiping the entire block device or screwing up the GPT partition table).

Eventually, it should be possible to use idevicerestore to perform this same process from a Linux machine, but this isn't ready yet.

While DFU boot works with a totally corrupted NOR flash, vital product identification and calibration data is stored there and you cannot restore this without Apple's help; you will have to send the machine in for repairs so they can re-provision its identity and run manufacturing testing and calibration. So, stay away from touching NOR flash.

Setup

Step 0: Upgrade macOS

You need at least macOS 11.2. Don't bother with older versions, they won't work.

You will need to have gone through the macOS first-setup, including having created an administrator user with a password.

Step 1: Partitioning

You are starting here:

We want to get here:

Note that storage is not yet supported in our tree or bootloader, but we are targeting this partition layout a priori. disk0s3 will be our "stub" macOS, which at this time will be used to hold a full macOS install (eventually we will provide tooling to allow this to be a minimal partition containing only boot files, to avoid wasted space, but this is not ready yet).

Assuming you have a 500GB model, follow these commands (note that your machine may freeze for several minutes while these commands are running):

# diskutil apfs resizeContainer disk0s2 200GB
# diskutil addPartition disk0s2 APFS Linux 80GB
# diskutil addPartition disk0s5 FAT32 LB 1GB
# diskutil addPartition disk0s4 FAT32 LR 0

You need at least 80GB for the stub partition, because the macOS install/update process is very inefficient. This will not be necessary in the future, once we have our own setup process.

Shrinking the OS APFS partition can be done on-line.

Each invocation of diskutil addPartition needs to be passed the partition node of the partition prior to the new partition to be created. This can vary, so run diskutil list prior to every instance and pick the device name of the last created partition. In the example above, Linux ended up as disk0s5 and LB ended up as disk0s4. Go figure. This gets reset after a reboot.

The volume name of the stub partition will determine the OS name in the boot picker, so call it "Linux".

We can change the FS and GPT type of the true Linux root and boot partitions later.

Step 2: Install macOS in the stub partition

Shut down the machine. Boot with the power button held down, until "Loading startup options..." appears. This enters 1TR, the OS recovery environment.

Select "Options" in the boot picker. You may be prompted to enter your credentials. These are the login credential for the main macOS installation.

In the main recovery menu, select "Reinstall macOS Big Sur".

Follow the prompts, and select "Linux" as the target volume.

Wait a while. This will reboot the machine into the new OS automatically.

Step 3: Go through initial setup

Go through OS first-time setup. Create an administrator user and password.

Check the OS version. If it is not 11.2, you should update to at least this version.

Shut down the machine again.

Step 4: Downgrade security

Boot into 1TR again ("Options" entry in the boot picker).

In the top menu bar, go to Utilities → Terminal. This brings up a root shell.

Find the volume group ID of your stub partition:

# diskutil apfs listVolumeGroups | grep -B3 Linux | grep Group

Then downgrade security:

# bputil -nkcas

Select the matching OS install ID, and follow the prompts. You will need to enter the credentials that you created in step 3.

Further disable SIP for good measure. This one is nice enough to ask you to pick the OS by name:

# csrutil disable

Note: If you get strange permission errors such as Failed to create local policy you may have to reset the system security policy first via

# csrutil clear

At this point you are ready to use custom kernels. Congrats!

Getting a serial console

Having a serial console is indispensable for a fast development cycle and to debug low-level issues.

M1 Macs expose their debug serial port over one of their Type C ports (the same one used for DFU). On MacBooks, this is the rear left port. On the Mac Mini, this is the port closest to the power plug.

The target machine can also be hard-rebooted using USB-PD VDM commands, making for a quick test cycle (no holding down power buttons).

See [[HW:USB-PD]] for details on the USB-PD VDM commands and what you can do with them.

The serial port is a UART using 1.2V logic levels, and requires vendor-specific USB-PD VDM commands to enable. Thus, making a compatible cable is nontrivial. You have the following options.

Using an M1 machine

If you have two M1 boxes, this is the simplest solution. Just grab macvdmtool, connect both machines with a standard Type C SuperSpeed cable using the DFU port on both machines, and that's it!

IMPORTANT NOTE: The cable needs to be the USB 3 / SuperSpeed type. USB 2 only cables won't work, the charging cable that comes with the MacBook/MacBook Air will not work and neither will most other cheap cables or cables marketed for their charging capacity. If it doesn't say "SuperSpeed" or "USB3.0" in the package it almost certainly won't work. If it's thin and bendy it is almost certainly not a SuperSpeed cable. Cables that work need to have upwards of 15 wires inside them; if it doesn't feel like it could possibly have that many wires inside, it is not the cable you need. If you are sure your cable is SuperSpeed capable (i.e. devices do enumerate as SuperSpeed through it) and it still doesn't work, then it is non-compliant and the manufacturer deserves to be shamed on their Amazon reviews page, because it means they didn't connect the SBU1/2 pins.

$ xcode-select --install
$ git clone https://github.com/AsahiLinux/macvdmtool
$ cd macvdmtool
$ make
$ sudo ./macvdmtool reboot serial

You should see something like this:

Mac type: J313AP
Looking for HPM devices...
Found: IOService:/AppleARMPE/[email protected]/AppleT810xIO/[email protected]/AppleS5L8940XI2CController/[email protected]/AppleHPMBusController/hpm0/AppleHPMARM
Connection: Sink
Status: APP 
Unlocking... OK
Entering DBMa mode... Status: DBMa
Rebooting target into normal mode... OK
Waiting for connection........ Connected
Putting target into serial mode... OK
Putting local end into serial mode... OK
Exiting DBMa mode... OK

See the macvdmtool page or just run it without arguments for a list of supported commands. Your serial port device is /dev/cu.debug-console. You can try picocom with: sudo picocom -q --omap crlf --imap lfcrlf -b 115200 /dev/tty.debug-console.

NOTE: if you have enabled serial debug output on your host machine (via nvram settings), the serial port device will be in use by the kernel. You need to turn that off to use it as a generic serial port.

DIY Arduino-based USB-PD interface

You can build a DIY USB-PD interface with the following parts:

Note that most FUSB302 breakout boards will not usefully break out the Type C pins you need, so it's best to use a separate full breakout board.

Go to the vdmtool repository for more information and a wiring list. Documentation is a bit sparse at the moment. You can ask us on IRC (OFTC/#asahi) if you need help.

1.2V compatible UART interfaces are relatively rare. 1.8V ones will usually work for input (RX); you can use a resistor divider to step down the TX voltage (TX -- 470Ω -+- 220Ω -- GND will step down 1.8V TX to 1.22V at the + point).

The default vdmtool code will put serial on the SBU1/SBU2 pins. At the device side connector (no cable), TX (output from the Mac) will be on the SBU1 pin on the connector side that has the active CC line (you should only connect one), and RX (input to the Mac) will be directly opposite the CC line.

This is all rather rudimentary because it's a stop-gap for the proper solution, which is...

Flexible USB-PD Debug Interface (project name TBD)

In the coming weeks we'll be designing an open hardware interface for interfacing to M1 serial ports, and more (supporting other debug pinsets on Apple devices, as well as UARTs on other devices such as certain Android phones, etc). Stay tuned for more information. Established kernel developers who want to get an early prototype when they become available should contact marcan.

USB gadget mode using a standard USB cable

m1n1 now supports exposing its debug console and proxy interface via a standard USB CDC-ACM device. All you need is a standard USB cable (C to C or A to C, as appropriate for your host machine). USB Type-C  to Type A cable

This interface is much faster than a serial port, and is the preferred way of using m1n1 remotely. However, a serial console is still recommended in addition to this for low-level debugging and development.

After booting m1n1, you will now see two CDC-ACM device appear (e.g. as /dev/ttyACM0 and /dev/ttyACM1 on Linux or /dev/tty.usbmodemP_01 and /dev/tty.usbmodemP_03 on macOS):

[977820.091168] usb 2-2: USB disconnect, device number 95
[977820.832569] usb 2-2: new high-speed USB device number 96 using xhci_hcd
[977821.069287] usb 2-2: New USB device found, idVendor=1209, idProduct=316d, bcdDevice= 1.00
[977821.069296] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[977821.069300] usb 2-2: Product: m1n1 uartproxy da44067
[977821.069303] usb 2-2: Manufacturer: Asahi Linux
[977821.069305] usb 2-2: SerialNumber: P-0
[977821.070574] cdc_acm 2-2:1.0: ttyACM0: USB ACM device
[977821.070825] probe of 2-2:1.0 returned 1 after 323 usecs
[977821.071036] cdc_acm 2-2:1.2: ttyACM1: USB ACM device
[977821.071099] probe of 0000:00:02.0 returned 0 after 19 usecs
[977821.071262] probe of 2-2:1.2 returned 1 after 284 usecs
[977821.071375] probe of 2-2 returned 1 after 1838 usecs
[977821.071428] probe of 0000:00:02.0 returned 0 after 9 usecs

When running the python proxyclient commands proxyclient/tools/{shell,chainload,linux}.py as described below (N.B.: see below for python version and installation requirements) you must specify the USB ACM serial device. e.g. for shell.py:

$ M1N1DEVICE=/dev/ttyACM0
$ export M1N1DEVICE
$ python3 proxyclient/tools/shell.py
picocom /dev/ttyACM1

Note that this method cannot (yet) be used as an earlycon for Linux, and USB gadget support is not yet in our main Linux tree either.

Using m1n1

m1n1 is our initial bootloader, which is in charge of pretending to be a XNU kernel and performing Apple-specific initialization.

Currently, m1n1 works as a serial "proxy" server, controlled via Python scripts from a host. In this way, you can load kernels via serial or USB and also explore the hardware interactively, with a Python console.

Debian build dependencies

$ sudo apt install -y gcc-aarch64-linux-gnu libc6-dev-arm64-cross device-tree-compiler imagemagick

To run the python proxy clients you need a newer version of python3 than default - 3.9 works

python3.9 -m pip install pyserial construct serial.tool

Building

You need an aarch64-linux-gnu-gcc cross-compiler toolchain (or a native one, if running on ARM64). You also need dtc (the devicetree compiler) and convert (from ImageMagick) for the boot logos.

$ git clone --recursive https://github.com/AsahiLinux/m1n1.git
$ cd m1n1
$ make

The output will be in build/m1n1.macho.

Installation

Boot your Mac into 1TR, then pull up the terminal. You can use the network to copy files over - either SSH from the Mac or just use curl.

For example, on your build machine:

$ cd m1n1/build
$ python3 -m http.server

And on your target machine:

# curl buildmachine.lan:8000/m1n1.macho > m1n1.macho

To install m1n1 into the stub partition as a custom kernelcache:

# kmutil configure-boot -c m1n1.macho -C -v /Volumes/Linux

Type in your credentials. If everything went well, you can restart and boot into m1n1!

Direct payload booting

m1n1 supports running payloads embedded directly in the binary. This allows you to boot Linux (or a subsequent bootloader stage) without the need for serial interaction. Currently, debug output is over serial only; framebuffer debug console support is coming soon.

To merge m1n1 with payloads, just concatenate them:

$ cat build/m1n1.macho Image.gz build/dtb/apple-j274.dtb initramfs.cpio.gz > m1n1-payload.macho

The maximum cumulative payload size is currently 64 MiB. You can change this at the top of m1n1.ld if you need more. Bear in mind this incurs a fixed runtime memory cost when using m1n1 interactively, even with no payload (though this memory can be reclaimed by the next stage), and chainloading m1n1 will currently "waste" this memory (the next iteration is loaded above this region and cannot use memory "under" it).

Supported payload file formats:

Supported compression formats:

Host setup

m1n1 currently requires a serial console for interactive usage or to get debug info. Go to the getting a serial console section to learn more about available options.

NOTE(2021-02-06): we will add a framebuffer console to make debugging easier without serial soon. USB OTG support is also planned in the future, so you will be able to interact with m1n1 with a standard USB C cable and load kernels faster.

To use the m1n1 proxyclient, you need the Python packages pyserial and construct. If your distribution does not provide them for you, you can use:

$ pip install --user pyserial construct

If you are on macOS, install python3.9 using Homebrew. The OS version of Python has problems with readline history files (?):

$ arch -x86_64 brew install python
$ pip3.9 install --user pyserial construct

You can also use macports to install python and pip and then install the dependencies using pip as root. Macports has a native build for M1.

$ sudo su -
$ port install python39 py39-pip
$ pip install pyserial construct

Usage

To use the proxyclient scripts, you need to set the envar M1N1DEVICE to your serial port device. When using another M1 mac as the host, use export M1N1DEVICE=/dev/cu.debug-console. For a Linux host connecting via USB, use something like export M1N1DEVICE=/dev/ttyACM0.

Things to do:

Playground shell

Run python shell.py for an interactive debug shell.

>>> read32(0x800000000) # Read the first word of RAM
0x484f6666
>>> memset32(fb, 0x000003ff, 1920 * 1080 * 4) # turn the framebuffer blue
>>> mrs(HCR_EL2) # only some registers supported, feel free to add more in utils.py
0x30488000000
>>> d = readmem(base, 0x10) # read and hexdump the first 16 bytes of m1n1 (mach-o header)
>>> chexdump(d)
00000000  cf fa ed fe 0c 00 00 01  02 00 00 00 0c 00 00 00  |................|
# allocate a buffer, read the AIC registers, and dump them
# using a buffer is recommended for registers because `readmem` will read
# byte-by-byte twice and complain if the data changes (checksum).
>>> AIC = 0x23b100000
>>> data = malloc(0x8000) # this is python-side malloc, allocs vanish when the script exits
>>> memcpy32(data, AIC, 0x8000)
>>> chexdump32(d)
00000000  00000007 000a0380 00180018 00000000 e0477971 00000000 000000f0 00000000
00000020  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000040  *
00003320  00000000 00000000 00000000 00000001 00000000 00000000 00000000 00000000
[...]
>>> u.msr(DAIF, u.mrs(DAIF) & ~0x3c0) # Enable IRQs             
>>> mon.add(AIC, 0x8000) # Monitor the AIC registers
[...]
>>> write32(AIC + 0x2008, 1) # Fire off an IPI to ourselves
TTY> Exception: IRQ          # These debug messages are from m1n1's IRQ handler
TTY>  type: 4 num: 1
# Then the register monitor prints changed registers automatically,
# with color coding we can't show here 
000000023b100020 00000000 00000000 00000000 00000000 00000001 00000000 00000000 00000000 
000000023b102000 00000000 00000000 00000001 00000001 00000000 00000000 00000000 00000000 
000000023b102020 00000000 00000001 00000001 00000000 00000000 00000000 00000000 00000000 
000000023b105000 00000000 00000000 00000001 00000001 00000000 00000000 00000000 00000000 
[...]
# There is support for recovering from simple faults
>>> read64(0)
TTY> Exception: SYNC
TTY> Running in EL2
TTY> MPIDR: 0x80000000
TTY> Registers: (@0x81368bde0)
TTY>   x0-x3: 0000000000000000 000000081360c004 0000000000000002 000000081360c004
TTY>   x4-x7: 000000081368bfa8 0000000000007a69 000000081360c004 0000000000000000
TTY>  x8-x11: 0000000000000000 0000000000000000 0000000000000000 00000000aaaaaaab
TTY> x12-x15: 000000000000002c 00000008135b5488 000000081368bb80 0000000000000000
TTY> x16-x19: 00000008135aaabc 0000000000000000 0000000000000000 000000081368bf88
TTY> x20-x23: 000000081368bfb0 0000000813608000 0000000000000000 0000000002aa55ff
TTY> x24-x27: 0000000003aa55ff 000000081360c000 0000000001aa55ff 000000081368bfb0
TTY> x28-x30: 0000000000000000 000000081368bee0 00000008135abb98
TTY> PC:       0x8135aaa9c (rel: 0xaa9c)
TTY> SPSEL:    0x1
TTY> SP:       0x81368bee0
TTY> SPSR_EL2: 0x80000009
TTY> FAR_EL2:  0x0
TTY> ESR_EL2:  0x96000018 (data abort (current))
TTY> L2C_ERR_STS: 0x11000ffc00000080
TTY> L2C_ERR_ADR: 0x300000000000000
TTY> L2C_ERR_INF: 0x1
TTY> SYS_E_LSU_ERR_STS: 0x0
TTY> SYS_E_FED_ERR_STS: 0x0
TTY> SYS_E_MMU_ERR_STS: 0x0
TTY> Recovering from exception (ELR=0x8135aaaa0)
0xacce5515abad1dea

Note: If you get an error that says proxy.ProxyCommandError: Reply error: Bad Command you most likely need to upgrade the m1n1 on the M1. A quick way around this is to just upload the newest m1n1.macho via the chainload.py as described below:

Loading another m1n1

Useful to test changes without using kmutil again.

$ python chainload.py ../build/m1n1.macho 
Base at: 0x803c5c000
FB at: 0x9e0df8000
Loading 442368 bytes to 0x812ce0000
....................................................
Jumping to 0x812ce4800
TTY> m1n1
TTY> sc: Initializing
TTY> CPU init... CPU: M1 Icestorm
[...]
TTY> m1n1 vf14489f
TTY> Copyright (C) 2021 The Asahi Linux Contributors
TTY> Licensed under the MIT license
[...]
Proxy is alive again

Boot a Linux kernel

This is what you're here for, right? :-). See [[SW:Linux]] for full instructions.

$ python linux.py -b 'earlycon console=ttySAC0,1500000 console=tty0 debug' Image.gz apple-j274.dtb initramfs.cpio.gz

If you are using USB to talk to m1n1 but would like to get a serial console to Linux, use the --tty argument. For example, to use /dev/ttyACM0 for m1n1 and /dev/ttyUSB0 for Linux, use:

$ export M1N1DEVICE=/dev/ttyACM0
$ python linux.py --tty /dev/ttyUSB0 -b 'earlycon console=ttySAC0,1500000 console=tty0 debug' Image.gz apple-j274.dtb initramfs.cpio.gz
Base at: 0x815300000
FB at: 0x9e0df8000
Loading 2046483 bytes to 0x8214fc000..0x8216efa13...
..........................................................................................................................................................................................................................................................
Loading DTB to 0x8216efa40...
Kernel_base: 0x821800000
Loading 953623 initramfs bytes to 0x821700000...
.....................................................................................................................
TTY> Starting secondary CPUs...
TTY> Starting CPU 1 (0:1)... OK
TTY>   Stack base: 0x8153ac040
TTY>   MPIDR: 0x80000001
TTY>   CPU: M1 Icestorm
TTY>   Index: 1 (table: 0x8153c4080)
TTY> 
TTY> Starting CPU 2 (0:2)... OK
TTY>   Stack base: 0x8153b0040
TTY>   MPIDR: 0x80000002
TTY>   CPU: M1 Icestorm
TTY>   Index: 2 (table: 0x8153c40c0)
TTY> 
TTY> Starting CPU 3 (0:3)... OK
TTY>   Stack base: 0x8153b4040
TTY>   MPIDR: 0x80000003
TTY>   CPU: M1 Icestorm
TTY>   Index: 3 (table: 0x8153c4100)
TTY> 
TTY> Starting CPU 4 (1:0)... OK
TTY>   Stack base: 0x8153b8040
TTY>   MPIDR: 0x80010100
TTY>   CPU: M1 Firestorm
TTY>   Index: 4 (table: 0x8153c4140)
TTY> 
TTY> Starting CPU 5 (1:1)... OK
TTY>   Stack base: 0x8153bc040
TTY>   MPIDR: 0x80010101
TTY>   CPU: M1 Firestorm
TTY>   Index: 5 (table: 0x8153c4180)
TTY> 
TTY> Starting CPU 6 (1:2)... OK
TTY>   Stack base: 0x8153c0040
TTY>   MPIDR: 0x80010102
TTY>   CPU: M1 Firestorm
TTY>   Index: 6 (table: 0x8153c41c0)
TTY> 
TTY> Starting CPU 7 (1:3)... OK
TTY>   Stack base: 0x8153c4040
TTY>   MPIDR: 0x80010103
TTY>   CPU: M1 Firestorm
TTY>   Index: 7 (table: 0x8153c4200)
TTY> 
TTY> FDT: initrd at 0x821700000 size 0xe8d17
TTY> FDT: [email protected] base 0x9e0df8000 size 0x7e9000
TTY> FDT: DRAM at 0x800000000 size 0x200000000
TTY> FDT: Usable memory is 0x80134c000..0x9db5e0000 (0x1da294000)
TTY> FDT: CPU 0 MPIDR=0x0 release-addr=0x8153c4050
TTY> FDT: CPU 1 MPIDR=0x1 release-addr=0x8153c4090
TTY> FDT: CPU 2 MPIDR=0x2 release-addr=0x8153c40d0
TTY> FDT: CPU 3 MPIDR=0x3 release-addr=0x8153c4110
TTY> FDT: CPU 4 MPIDR=0x10100 release-addr=0x8153c4150
TTY> FDT: CPU 5 MPIDR=0x10101 release-addr=0x8153c4190
TTY> FDT: CPU 6 MPIDR=0x10102 release-addr=0x8153c41d0
TTY> FDT: CPU 7 MPIDR=0x10103 release-addr=0x8153c4210
TTY> FDT prepared at 0x8193f0000
Uncompressing...
5758984
Decompress OK...
Ready to boot
DAIF: 3c0
MMU: shutting down...
MMU: shutdown successful, clearing caches
Booting kernel at 0x821800000 with fdt at 0x8193f0000
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x611f0221]
[    0.000000] Linux version 5.11.0-rc6-00015-gdc1ce163506d ([email protected]) (aarch64-linux-gnu-gcc (Gentoo 10.2.0-r5 p6) 10.2.0, GNU ld (Gentoo 2.35.1 p2) 2.35.1) #201 SMP PREEMPT Fri Feb 5 03:15:40 JST 2021
[    0.000000] Machine model: Apple Mac Mini M1 2020
[    0.000000] earlycon: s5l0 at MMIO32 0x0000000235200000 (options '1500000')
[    0.000000] printk: bootconsole [s5l0] enabled
[    0.000000] Zone ranges:
[    0.000000]   DMA      [mem 0x000000080134c000-0x00000009db5dffff]
[    0.000000]   DMA32    empty
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x000000080134c000-0x00000009db5dffff]
[    0.000000] Zeroed struct page in unavailable ranges: 648 pages
[    0.000000] Initmem setup node 0 [mem 0x000000080134c000-0x00000009db5dffff]
[    0.000000] On node 0 totalpages: 485541
[    0.000000]   DMA zone: 1897 pages used for memmap
[    0.000000]   DMA zone: 0 pages reserved
[    0.000000]   DMA zone: 485541 pages, LIFO batch:15
[    0.000000] percpu: Embedded 6 pages/cpu s57888 r0 d40416 u98304
[    0.000000] pcpu-alloc: s57888 r0 d40416 u98304 alloc=6*16384
[    0.000000] pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3 [0] 4 [0] 5 [0] 6 [0] 7 
[    0.000000] Detected VIPT I-cache on CPU0
[    0.000000] CPU features: detected: Virtualization Host Extensions
[    0.000000] CPU features: kernel page table isolation disabled by kernel configuration
[    0.000000] CPU features: detected: Spectre-v4
[    0.000000] CPU features: detected: Address authentication (IMP DEF algorithm)
[    0.000000] CPU features: detected: FIQs
[    0.000000] alternatives: patching kernel code
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 483644
[    0.000000] Kernel command line: earlycon debug
[    0.000000] Dentry cache hash table entries: 1048576 (order: 9, 8388608 bytes, linear)
[    0.000000] Inode-cache hash table entries: 524288 (order: 8, 4194304 bytes, linear)
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 7715584K/7768656K available (3136K kernel code, 1144K rwdata, 640K rodata, 512K init, 455K bss, 53072K reserved, 0K cma-reserved)
[    0.000000] random: get_random_u64 called from __kmem_cache_create+0x30/0x4b4 with crng_init=0
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
[    0.000000] rcu: Preemptible hierarchical RCU implementation.
[    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=16 to nr_cpu_ids=8.
[    0.000000]  Trampoline variant of Tasks RCU enabled.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 100 jiffies.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=8
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] aic_of_ic_init: AIC: initialized with 896 IRQs, 2 FIQs, 2 IPIs, 32 vIPIs
[    0.000000] arch_timer: cp15 timer(s) running at 24.00MHz (phys).
[    0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x588fe9dc0, max_idle_ns: 440795202592 ns
[    0.000000] sched_clock: 56 bits at 24MHz, resolution 41ns, wraps every 4398046511097ns
[    0.000650] Console: colour dummy device 80x25
[    0.000958] printk: console [tty0] enabled
[    0.001273] printk: bootconsole [s5l0] disabled

BusyBox v1.30.1 (Debian 1:1.30.1-6+b1) built-in shell (ash)
Enter 'help' for a list of built-in commands.

/bin/sh: can't access tty; job control turned off
/ #

The script currently supports input and output for the TTY passthrough.