Monday, February 1, 2021

Building XNU for macOS 11.2 (Intel + Apple Silicon)

 The macOS Big Sur 11.2 kernel (XNU) source has been released here: source tarball.

My previous post on building XNU for macOS 11.0.1 described the method for compiling open source XNU for Intel Macs. This post details how to compile XNU for both Intel and Apple silicon Macs, and how to boot the custom kernel on both platforms. Note that it is not possible to build or boot a custom XNU on Apple silicon Macs before macOS 11.2.
Building XNU requires some patience, and some open source dependencies which are not pre-installed. This post walks through all the steps necessary to build the open source version of XNU on supported Apple hardware. Some of the steps have changed slightly from the previous process of building for Intel only. Please read carefully!


TL;DR

I have updated the makefile which automates the downloading and building of all prerequisites. The file now supports Big Sur Intel and Apple silicon kernels, and the new default macOS version is 11.2. It will build the prerequisites necessary to build both the Intel and Apple silicon kernel. You can manually grab it like:
  • curl https://jeremya.com/sw/Makefile.xnudeps > Makefile.xnudeps
and invoke it like:
  • make -f Makefile.xnudeps
This makefile will automatically detect the correct versions of source code to download based on the version of macOS you specify. By default, the version is 11.2, however you can select a different version like:
  • make -f Makefile.xnudeps macos_version=10.15.3 xnudeps
You might need a different version of Xcode installed when building for previous OS versions. You can also see other features of the Makefile using the  help  target.

After building the prerequisites, you can compile XNU. On Apple silicon Macs, this now requires some bits from the Kernel Debug Kit (KDK) which is available for download from developer.appple.com. Building XNU for Apple silicon Macs requires specifying the platform name. See below for a table of supported platforms.


Setup Xcode

If you have not downloaded and installed Xcode, you will first need to do a small bit of setup to be able to use the command line tools. The steps to install and setup a specific version of Xcode are as follows:
  1. Download the xip package from developer.apple.com/downloads/more.  The particular version you select may depend on what version of XNU you want to compile. You will need a developer account to download Xcode this way.
  2. Unpack the xip package by double clicking on it, or using the command line:
    $ xip --expand Xcode_{ver}.xip

  3. Select the new xcode:
    $ sudo xcode-select -s path/to/Xcode.app/Contents/Developer

  4. Agree to the license:
    $ sudo xcodebuild -license

  5. Make sure it works:
    $ xcrun -sdk macosx -show-sdk-path
    $ clang -v


Manual XNU Building

NOTE: these steps can be done from either an Apple silicon Mac or an Intel Mac. The Xcode toolchain will happily cross-compile for the architecture(s) you specify!
  1. Download and Install Xcode
    • Make sure you have at least Xcode 12.4 installed. You can install it via the App Store, or by manual download here:  https://developer.apple.com/download/more/
    • NOTE: for older versions of macOS, you may need older versions of Xcode which are only available via download from developer.apple.com. You need a developer account to download from that site.
  2. Download and Install the KDK
    • Building an open source kernel for an Apple silicon Mac requires content from the Kernel Debug Kit. If you are building only for Intel macs, you can skip this step
    • Download the KDK package here (you will need a developer account). Or you can visit  https://developer.apple.com/download/more/, search for "Kernel Debug Kit", and download/install the package corresponding to the version of macOS Big Sur you are compiling (ensure the version is at least 11.2), e.g. "Kernel Debug Kit 11.2 build 20D64".
    • Once complete, the KDK should be installed to: /Library/Developer/KDKs/KDK_{ver}_{build}.kdk where {ver} is the OS version, e.g., 11.2, and {build} is the build number, e.g., 20D64.
    • You can save this path for later, e.g.,
      export KDK=/Library/Developer/KDKs/KDK_11.2_20D64.kdk
  3. Download the source
    • export TARBALLS=https://opensource.apple.com/tarballs
    • curl -O ${TARBALLS}/dtrace/dtrace-370.40.1.tar.gz
    • curl -O ${TARBALLS}/AvailabilityVersions/AvailabilityVersions-70.tar.gz
    • curl -O ${TARBALLS}/libdispatch/libdispatch-1271.40.12.tar.gz
    • curl -O ${TARBALLS}/xnu/xnu-7195.81.3.tar.gz
  4. Build CTF tools from dtrace
    • tar zxf dtrace-370.40.1.tar.gz
    • cd dtrace-370.40.1
    • xcodebuild install -sdk macosx -target ctfconvert \
      -target ctfdump -target ctfmerge \
      ARCHS='x86_64 arm64' VALID_ARCHS='x86_64 arm64' DSTROOT=$PWD/dst
    • export TOOLCHAIN=`cd $(xcrun -sdk macosx -show-sdk-platform-path)/../../Toolchains/XcodeDefault.xctoolchain && pwd`
    • sudo ditto "$PWD/dst/$TOOLCHAIN" "$TOOLCHAIN"
    • cd ..
  5. Install AvailabilityVersions
    • tar zxf AvailabilityVersions-70.tar.gz
    • cd AvailabilityVersions-70
    • make install
    • sudo ditto "$PWD/dst/usr/local/libexec" \
      "$(xcrun -sdk macosx -show-sdk-path)/usr/local/libexec"
    • cd ..
  6. Install XNU headers
    • tar zxf xnu-7195.81.3.tar.gz
    • cd xnu-7195.81.3
    • make SDKROOT=macosx ARCH_CONFIGS="X86_64 ARM64" installhdrs
    • sudo ditto "$PWD/BUILD/dst" "$(xcrun -sdk macosx -show-sdk-path)"
    • cd ..
  7. Build libfirehose from libdispatch
    • tar zxf libdispatch-1271.40.12.tar.gz
    • cd libdispatch-1271.40.12
    • xcodebuild install -sdk macosx ARCHS='x86_64 arm64e' \
      VALID_ARCHS='x86_64 arm64e' -target libfirehose_kernel \
      PRODUCT_NAME=firehose_kernel DSTROOT=$PWD/dst
    • sudo ditto "$PWD/dst/usr/local" \
      "$(xcrun -sdk macosx -show-sdk-path)/usr/local"
    • cd ..
  8. Build XNU for Intel Macs
    • cd xnu-7195.81.3
    • make SDKROOT=macosx ARCH_CONFIGS=X86_64 KERNEL_CONFIGS=RELEASE
    • NOTE that this make invocation is equivalent:
      make SDKROOT=macosx TARGET_CONFIGS="RELEASE X86_64 NONE"
  9. Build XNU for Apple silicon
    • cd xnu-7195.81.3
    • make SDKROOT=macosx KDKROOT=path/to/your/KDK \
      TARGET_CONFIGS="RELEASE ARM64 {PLATFORM}"
    • You can use the ${KDK} environment variable you saved from the KDK installation, or a manual path which should look like:
      export KDK=/Library/Developer/KDKs/KDK_11.2_20D64.kdk
    • The {PLATFORM} should be set based on your machine. The supported platforms are:
      {PLATFORM}Mac Model
      T8020 Developer Transition Kit
      (ADP3,2)
      T8101 MacBookPro17,1
      MacBookAir10,1
      Macmini9,1
    • e.g. to build for the MacBookAir10,1, you would use:
      make SDKROOT=macosx TARGET_CONFIGS="RELEASE ARM64 T8101"
  10. Additional build options (for both Intel and Apple silicon)
    • Speed up the link by adding BUILD_LTO=0 to your xnu make invocation
    • Build the development kernel by replacing RELEASE with  DEVELOPMENT in your xnu make invocation
    • See colorful build output by adding LOGCOLORS=y to your xnu make invocation
    • Make the build output stay on a single line by adding CONCISE=1 to your xnu make invocation


Install and Run XNU

SECURITY WARNING: On Big Sur, you will need to lower your system's security in order to install and boot from a custom kernel.

On Intel Macs, you will need to  disable System Integrity Protection, set the machine's Secure Boot security setting to "No Security," and  disable the authenticated root volume.

On Apple silicon Macs you will need to boot into Recovery Mode (hold down the power button while booting and select "Options"). Using the Startup Security Utility, change your system's security policy to "Reduced Security." The system security will be further downgraded to "Permissive" via a command line tool that's also run from Recovery Mode.


NOTE: while the building of XNU (and supporting tools/libraries) can be done on any Mac, the final step of building a bootable Kext Collection (KC) must be done on the device on which you will boot the newly compiled kernel.
There are significant differences in the way in which a kernel is installed on an Intel Mac vs. an Apple silicon Mac. Separate instructions are given for each platform below.


Intel Mac: Install and Run

After the final build step, you should have a new kernel built in {xnu}/BUILD/obj/kernel[.development]. In order to run this kernel on Big Sur (running on an Intel Mac), you will need to rebuild the kext collection (KC) artifacts. KC artifacts can be generated using the kmutil command, specifically the create subcommand, which allows you to generate custom KCs necessary to boot the system. For more information on the KC boot artifact format, check out the kmutil man page:  man kmutil

Installing a kernel could potentially render your system un-bootable, so trying this out in a VM first is recommended!

Install and Run Your kernel
  1. Build the KCs
    • cd xnu-7195.81.3
    • kmutil create -a x86_64 -Z -n boot sys \
      -B BUILD/BootKernelExtensions.kc \
      -S BUILD/SystemKernelExtensions.kc \
      -k BUILD/obj/kernel \
      --elide-identifier com.apple.driver.AppleIntelTGLGraphicsFramebuffer


      (the AppleIntelTGLGraphicsFramebuffer will not link against the open source xnu kernel)
  2. Mount a live view of the filesystem
    • mkdir BUILD/mnt
    • sudo mount -o nobrowse -t apfs /dev/diskMsN $PWD/BUILD/mnt

      (diskMsN can be found by running mount, looking for the root mount's device, and chopping off the last "s", e.g. if your root is /dev/disk1s2s3, you'll mount /dev/disk1s2)
  3. Place the kernel+KCs into the live volume
    • sudo ditto BUILD/BootKernelExtensions.kc "$PWD/BUILD/mnt/System/Library/KernelCollections/BootKernelExtensions.kc.development"
    • sudo ditto BUILD/SystemKernelExtensions.kc "$PWD/BUILD/mnt/System/Library/KernelCollections/SystemKernelExtensions.kc.development"
    • sudo ditto BUILD/obj/kernel "$PWD/BUILD/mnt/System/Library/Kernels/kernel.development"

      (NOTE: "development" can be replaced with any short string, e.g., "usr". I recommend using a suffix in order to easily maintain a fallback from which you can recover your system)
  4. Bless the new KCs (copy them to the appropriate Preboot volume)
    • sudo bless --folder $PWD/BUILD/mnt/System/Library/CoreServices \
      --bootefi --create-snapshot
  5. Setup boot-args to select the new KC
    • sudo nvram boot-args="kcsuffix=development wlan.skywalk.enable=0"
      (replace development with your chosen suffix)
    • NOTE: the wlan.skywalk.enable=0 boot-arg is necessary to disable the use of skywalk in the WLAN driver as Skywalk is not part of the open source kernel.
    • NOTE: If you run into panics related to Skywalk, you may also need to add the dk=0 boot-arg to disable DriverKit drivers. Networking DriverKit drivers are Skywalk clients.
  6. Reboot!
    • NOTE: due to missing network (Skywalk) and power management (XCPM) functionality, your machine will be missing some features. For example, sleep/wake will not work.

There are a few things to be aware of in the new kernel / kext management world on Intel Macs.
  1. If your machine becomes un-bootable, you can boot into Recovery Mode (by holding down the option key during boot and pressing Cmd-r at the boot picker prompt), opening up terminal, and setting your kcsuffix boot-arg to release. If you used a suffix for your KCs, this will boot from the KCs originally supplied by Apple.
  2. The filenames are important. The booter will load the BootKernelExtensions.kc[.suffix] file into memory, and the kernelmanagerd user space daemon will attempt to mmap the SystemKernelExtensions.kc[.suffix] file corresponding to the booted KC.
  3. The boot and system KCs must be generated together because the system KC is linked against the boot KC. The system will panic if there is a mis-match.
  4. By disabling the authenticated root volume, and booting from a new snapshot, you open your system up to evil maid attacks. In order to re-enable all of the security on the machine, you will need to go through a software update (an update to the current system version should work).
It may also be useful to use the following boot-args to see serial output from the kernel as it boots:
  • serial=3 -v


Apple silicon Mac: Install and Run

Installing a custom kernel on an Apple silicon Mac is a significantly different process than on an Intel Mac. Similarly to an Intel Mac, you will need to build a kext collection, however, the contents of the collection need to be exactly specified and the collection can only be installed in Recovery Mode via a special kmutil command.

After the final build step, you should have a new kernel built in {xnu}/BUILD/obj/kernel[.development].{platform}. For example, {xnu}/BUILD/obj/kernel.development.T8101. In order to run this kernel on your Apple silicon Mac, you will need to build a kext collection (KC) that exactly mirrors the collection on your running machine. The set of kexts currently built into the running KC can be listed using the kmutil inspect command, and a new KC can be linked using the kmutil create command. For more information on the boot artifact format, check out the kmutil man page:  man kmutil. On Apple silicon Macs, only one single KC is used there is no SystemKernelExtensions.kc file as there is on Intel Macs.


Install and Run Your kernel
  1. Build the KC (assuming T8101 development kernel)
    • cd xnu-7195.81.3
    • kmutil create -a arm64e -z -V development -n boot \
      -B BUILD/OpenSource.kc \
      -k BUILD/obj/kernel.development.t8101 \
      -r /System/Library/Extensions \
      -r /System/Library/DriverExtensions \
      -x \
      $(kmutil inspect -V release --no-header \
        | grep -v "SEPHiber" | awk '{print " -b "$1; }')
    • NOTE: this command uses a nested kmutil invocation to gather the list of drivers built into the KC your system has booted. The kmutil inspect utility will list all the driver bundle IDs in the running / booted KC. The first piped command filters out the SEP hibernation driver because that won't link with the open source kernel. Then we pass the set of bundle IDs to awk which prepends -b to each. This constructs the explicit command-line list of all bundle IDs for the outer kmutil invocation.
    • NOTE: the file name of the KC does not matter. Here I've chosen OpenSource.kc, but because of the way this KC is booted on Apple silicon Macs, the name does not matter in the same way it does on an Intel Mac.
    • NOTE: you can replace the first -V development with -V release if you built the release kernel, however you shouldn't replace the -V release in the kmutil inspect invocation because it may cause unwanted output to stdout that results in a failed call to kmutil create.
  2. Boot into Recovery Mode
    • Shutdown the computer
    • Power on the computer by clicking the power button, then hold the power button until you see "Loading startup options..."
    • Select "Options" and then "Continue"
    • From the "Utilities" menu, select "Terminal"
  3. Disable SIP and Set boot-args (one time operation)
    • From the Recovery mode terminal run:
      • csrutil disable
        (follow the prompts and enter your password)
      • bputil -a
        (this enables custom boot-args to be sent to the kernel)
    • Reboot
    • From the Terminal in the main OS run:
      • sudo nvram boot-args="wlan.skywalk.enable=0 dk=0"
        (Note that dk=0 disables DriverKit)
    • Boot back into Recovery Mode (see step 2).
  4. Install the new KC
    • From the Recovery mode terminal run:
      • cd /Volumes/Macintosh\ HD/path/to/xnu
        (change directories to the place where you compiled XNU - replace "Macintosh HD" with the name of your hard disk, and append the path to the xnu source, e.g.
        cd /Volumes/Macintosh\ HD/Users/jeremy/sw/xnu-7195.81.3)
      • kmutil configure-boot -v /Volumes/Macintosh\ HD -c BUILD/OpenSource.kc
        (replace "Macintosh HD" with the name of your hard disk, and use the same path to the KC you built earlier)
  5. Reboot!
  6. CAVEAT: the kmutil configure-boot process will only work on the first macOS volume. If you have multiple bootable volumes, you will only be able to boot a custom kernel on the first installation.
  7. NOTE: due to some missing functionality in the open source XNU, your machine will be missing some features. For example, hibernate will not work, and apps run under Rosetta will not work.

There are a few things to be aware of in the new kernel / kext management world on Apple silicon Macs.
  1. Because of the way Apple silicon Macs load and boot the kernel, you do not need to disable the Authenticated Root Volume in order to boot a custom kernel.
  2. If your machine becomes un-bootable, the only way to recover is to boot into Recovery Mode and upgrade your Security to either "Reduced Security" or "Full Security." Boot-args to control which kernel / KC boots do not work on Apple silicon Macs.
  3. The open source XNU release does not contain kernel support for Rosetta. This means that while running an open source kernel on an Apple silicon Mac, you will not be able to run x86_64 binaries.
  4. Booting a new kernel/KC will always require a reboot into Recovery mode and an invocation of kmutil configure-boot. The other security settings only need to be changed the first time you boot a custom KC.

Building XNU for macOS 11.2 (Intel + Apple Silicon)

 The macOS Big Sur 11.2 kernel (XNU) source has been released here:  source ,  tarball . My previous post on building XNU for macOS 1...