docs: add the VitePress documentation site

Add English and Simplified Chinese documentation under docs/, covering the boot chain, build, boot, debugging, porting, drivers, XNU 4 KiB pages, macOS and device notes. Add the Pages workflow and CNAME. Rename the boot manager from CrabApple to UniXNU across the package, symbols, paths, configuration file and logs. Update README with the documentation badge and CONTRIBUTING link.
This commit is contained in:
UniXNU Docs committed 2026-10-19 22:10:43 +08:00
1 parent 1223fba59c
commit 5c92d4048f
161 files changed
+15089 -1007

No files matched your search

+47
View File
@@ -0,0 +1,47 @@
name: Docs
on:
push:
branches: [main]
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: docs/package-lock.json
- run: npm ci
working-directory: docs
- run: npm run build
working-directory: docs
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
+5
View File
@@ -8,6 +8,11 @@
/.local*/
/Mu_Basecore/
# Documentation site
/docs/node_modules/
/docs/.vitepress/dist/
/docs/.vitepress/cache/
# Local development files
*.log
*.terminal
+90
View File
@@ -0,0 +1,90 @@
# Contributing to UniXNU
Read the [documentation](https://unixnu.com) before changing the build driver, the kernel patches or a device port. The [repository layout](https://unixnu.com/contributing/repository) page describes the module system.
## Repository scope
| Repository | Changes |
| --- | --- |
| `UniXNU` | Build driver, UEFI boot manager, DarwinBoot, generic drivers, kits, patches and documentation |
| `UniXNU-<device>` | Board file, defconfig, device scripts, device drivers and package |
| `uefi-<device>` | Mu-Silicium UEFI port |
A change that applies to one board belongs in the device repository. A change that applies to the stack belongs in this repository.
## Development
```sh
git clone https://github.com/UniXNU/UniXNU
cd UniXNU
./unixnu defconfig <name> -D ../UniXNU-<device>
./unixnu fetch
./unixnu build kernel package firmware payload
./unixnu bootimg --uefi ../uefi-<device> --dtb <board>.dtb
```
Use `--local-module` or `--local` to test a module from a working directory without committing it.
## Change guidelines
- State the current behavior in the code and documentation.
- Keep one subject per commit.
- Add a test when it prevents a failure a future edit would introduce. A behavior visible on every run needs no assertion.
- Put device-specific values in the device repository.
- Put stack-wide behavior in `UniXNU`.
- Keep the English and Simplified Chinese documentation pages in sync.
## Commit messages
```
feat(<area>): <summary>
fix(<area>): <summary>
docs: <summary>
build(<area>): <summary>
chore: <summary>
```
The message describes the change. The documentation describes the resulting state.
## Kernel patches
1. Edit `.cache/xnu-work` after a first kernel build.
2. Generate the patch with `git diff`.
3. Name it so the apply order is correct.
4. Rebuild the kernel and the prelink.
5. Record the kernelcache pin if the ABI changed.
Late patches use the `zzy-` and `zzz-` prefixes.
## Drivers
- Built-in XNU drivers compile against the recorded XNU flags in `*.cpo.json`.
- UEFI drivers use the EDK II module layout.
- Kext bundles keep the standard bundle layout.
- A driver that touches hardware reports each stage through a boot argument while it is under bring-up.
## Documentation
The documentation source is in `docs/`. The site is bilingual:
| Locale | Path |
| --- | --- |
| English | `docs/` |
| Simplified Chinese | `docs/zh/` |
```sh
cd docs
npm install
npm run dev
npm run build
```
Add a page to both locales and to the sidebar in `docs/.vitepress/config.mts`.
## Security
Report a security issue privately to the maintainers. Do not open a public issue for a trust cache, code signing or boot policy defect.
## License
Contributions are licensed under BSD-2-Clause-Patent. The `LICENSE` file in each directory applies to that directory.
+30 -8
View File
@@ -7,20 +7,42 @@
</p>
<p align="center">
<a href="https://unixnu.com"><img alt="Documentation" src="https://img.shields.io/badge/docs-unixnu.com-2A5BD7?logo=readthedocs&logoColor=white"></a>
<a href="https://t.me/unixnu"><img alt="Telegram" src="https://img.shields.io/badge/Telegram-@unixnu-26A5E4?logo=telegram&logoColor=white"></a>
<a href="https://x.com/unixnu"><img alt="X" src="https://img.shields.io/badge/X-@unixnu-000000?logo=x&logoColor=white"></a>
</p>
# UniXNU
An independent project to run Darwin/XNU userland on non-Apple ARM64 hardware through UEFI.
An independent project to run Darwin and XNU userland on non-Apple ARM64 hardware through UEFI.
Work in progress. The `unixnu` driver builds the kernel, the UniXNU package, the device firmware and the
boot image (developed on macOS, other hosts untested), see [docs/building.md](docs/building.md). The boot payload (kernel, device tree and
config) and the userdata image for the device are built by the same driver. The target IPSW can be a local file or
a URL.
## Documentation
Device adaptations are separate projects:
The documentation is at [unixnu.com](https://unixnu.com). It covers the boot chain, the build driver, device porting, driver development, the 4 KiB XNU page patches, boot arguments and per-device notes.
- [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng): Xiaomi Pad 6S Pro (sheng)
- [uefi-sheng](https://github.com/UniXNU/uefi-sheng): UEFI firmware for sheng (Mu-Silicium fork)
- [Overview](https://unixnu.com/guide/overview)
- [Build](https://unixnu.com/building)
- [Boot the image](https://unixnu.com/guide/boot)
- [Debugging and logs](https://unixnu.com/guide/debugging)
- [Porting](https://unixnu.com/porting/)
- [Drivers](https://unixnu.com/drivers/overview)
- [XNU and 4 KiB pages](https://unixnu.com/xnu/four-k-page-tables)
- [Devices](https://unixnu.com/devices/)
## Repositories
| Repository | Contents |
| --- | --- |
| [UniXNU](https://github.com/UniXNU/UniXNU) | Build driver, UEFI boot manager, DarwinBoot, generic drivers, kits and patches |
| [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng) | Device repository for the Xiaomi Pad 6S Pro |
| [uefi-sheng](https://github.com/UniXNU/uefi-sheng) | UEFI firmware port for the Xiaomi Pad 6S Pro |
Device repositories use the same layout as this repository. Their modules override modules with the same name, and their `package/` becomes a kit in the build tree.
## Contributing
Read [CONTRIBUTING.md](CONTRIBUTING.md) for the repository scope, the development workflow, the commit format and the documentation rules.
## License
BSD-2-Clause-Patent. See [LICENSE](LICENSE).
+318
View File
@@ -0,0 +1,318 @@
import { defineConfig } from 'vitepress'
export default defineConfig({
lang: 'en-US',
title: 'UniXNU',
description: 'Run Darwin and XNU userland on non-Apple ARM64 hardware through UEFI.',
cleanUrls: true,
lastUpdated: true,
locales: {
root: {
label: 'English',
lang: 'en-US',
},
zh: {
label: '简体中文',
lang: 'zh-CN',
link: '/zh/',
},
},
head: [
['link', { rel: 'icon', type: 'image/svg+xml', href: '/logo-light.svg' }],
['meta', { name: 'theme-color', content: '#0b0c0f' }],
['meta', { property: 'og:type', content: 'website' }],
['meta', { property: 'og:title', content: 'UniXNU' }],
['meta', { property: 'og:description', content: 'Run Darwin and XNU userland on non-Apple ARM64 hardware through UEFI.' }],
],
themeConfig: {
logo: {
light: '/logo-light.svg',
dark: '/logo-dark.svg',
alt: 'UniXNU',
},
siteTitle: 'UniXNU',
nav: [
{ text: 'Guide', link: '/guide/overview', activeMatch: '^/(guide/|building)' },
{ text: 'Porting', link: '/porting/', activeMatch: '^/porting/' },
{ text: 'Drivers', link: '/drivers/overview', activeMatch: '^/drivers/' },
{ text: 'XNU', link: '/xnu/source-and-patches', activeMatch: '^/xnu/' },
{ text: 'Devices', link: '/devices/', activeMatch: '^/devices/' },
{ text: 'Reference', link: '/reference/cli', activeMatch: '^/reference/' },
{ text: 'Contributing', link: '/contributing/documentation', activeMatch: '^/contributing/' },
],
sidebar: {
'/guide/': [
{
text: 'Guide',
items: [
{ text: 'Overview', link: '/guide/overview' },
{ text: 'Requirements', link: '/guide/requirements' },
{ text: 'Build', link: '/building' },
{ text: 'Build the kernel', link: '/guide/build-kernel' },
{ text: 'Boot the image', link: '/guide/boot' },
{ text: 'Debugging and logs', link: '/guide/debugging' },
{ text: 'Userdata image', link: '/guide/userdata' },
],
},
],
'/building': [
{
text: 'Guide',
items: [
{ text: 'Overview', link: '/guide/overview' },
{ text: 'Requirements', link: '/guide/requirements' },
{ text: 'Build', link: '/building' },
{ text: 'Build the kernel', link: '/guide/build-kernel' },
{ text: 'Boot the image', link: '/guide/boot' },
{ text: 'Debugging and logs', link: '/guide/debugging' },
{ text: 'Userdata image', link: '/guide/userdata' },
],
},
],
'/concepts/': [
{
text: 'Concepts',
items: [
{ text: 'Boot chain', link: '/concepts/boot-chain' },
{ text: 'Boot payload', link: '/concepts/payload' },
{ text: 'Kernel prelink', link: '/concepts/prelink' },
{ text: 'Userdata layout', link: '/concepts/userdata' },
{ text: 'Integrity and boot policy', link: '/concepts/security' },
],
},
],
'/porting/': [
{
text: 'Porting',
items: [
{ text: 'Porting overview', link: '/porting/' },
{ text: 'Device repository', link: '/porting/device-repository' },
{ text: 'UEFI firmware', link: '/porting/uefi-firmware' },
{ text: 'Device tree', link: '/porting/device-tree' },
{ text: 'Bring-up order', link: '/porting/bring-up' },
],
},
],
'/drivers/': [
{
text: 'Drivers',
items: [
{ text: 'Driver layers', link: '/drivers/overview' },
{ text: 'UEFI drivers', link: '/drivers/uefi-drivers' },
{ text: 'XNU drivers', link: '/drivers/xnu-drivers' },
{ text: 'Kext bundles', link: '/drivers/kexts' },
{ text: 'Device tree matching', link: '/drivers/device-tree-matching' },
],
},
],
'/xnu/': [
{
text: 'XNU',
items: [
{ text: 'Source and patches', link: '/xnu/source-and-patches' },
{ text: 'Four-kilobyte pages', link: '/xnu/four-k-page-tables' },
{ text: 'Patch reference', link: '/xnu/patch-reference' },
{ text: 'Boot arguments', link: '/xnu/boot-arguments' },
],
},
],
'/devices/': [
{
text: 'Devices',
items: [
{ text: 'Supported devices', link: '/devices/' },
{ text: 'Xiaomi Pad 6S Pro (sheng)', link: '/devices/sheng' },
{ text: 'Touch driver example', link: '/devices/sheng-touch-driver' },
],
},
],
'/reference/': [
{
text: 'Reference',
items: [
{ text: 'Command line', link: '/reference/cli' },
{ text: 'Device configuration', link: '/reference/configuration' },
{ text: 'Boot configuration', link: '/reference/boot-configuration' },
{ text: 'Output layout', link: '/reference/output-layout' },
{ text: 'QEMU testing', link: '/reference/qemu' },
{ text: 'Troubleshooting', link: '/reference/troubleshooting' },
{ text: 'Glossary', link: '/reference/glossary' },
],
},
],
'/contributing/': [
{
text: 'Contributing',
items: [
{ text: 'Documentation site', link: '/contributing/documentation' },
{ text: 'Repository layout', link: '/contributing/repository' },
],
},
],
},
search: {
provider: 'local',
},
socialLinks: [
{ icon: 'github', link: 'https://github.com/UniXNU/UniXNU' },
{ icon: 'x', link: 'https://x.com/unixnu' },
],
footer: {
message: 'Released under the BSD-2-Clause-Patent License.',
copyright: 'Copyright 2026 UniXNU contributors',
},
editLink: {
pattern: 'https://github.com/UniXNU/UniXNU/edit/main/docs/:path',
text: 'Edit this page on GitHub',
},
docFooter: {
prev: 'Previous',
next: 'Next',
},
outline: {
level: [2, 3],
label: 'On this page',
},
returnToTopLabel: 'Back to top',
sidebarMenuLabel: 'Menu',
darkModeSwitchLabel: 'Appearance',
lightModeSwitchTitle: 'Use light theme',
darkModeSwitchTitle: 'Use dark theme',
locales: {
zh: {
label: '简体中文',
selectText: '选择语言',
nav: [
{ text: '指南', link: '/zh/guide/overview', activeMatch: '^/zh/(guide/|building)' },
{ text: '移植', link: '/zh/porting/', activeMatch: '^/zh/porting/' },
{ text: '驱动', link: '/zh/drivers/overview', activeMatch: '^/zh/drivers/' },
{ text: 'XNU', link: '/zh/xnu/source-and-patches', activeMatch: '^/zh/xnu/' },
{ text: '设备', link: '/zh/devices/', activeMatch: '^/zh/devices/' },
{ text: '参考', link: '/zh/reference/cli', activeMatch: '^/zh/reference/' },
{ text: '参与贡献', link: '/zh/contributing/documentation', activeMatch: '^/zh/contributing/' },
],
sidebar: {
'/zh/guide/': [
{
text: '指南',
items: [
{ text: '总览', link: '/zh/guide/overview' },
{ text: '构建环境', link: '/zh/guide/requirements' },
{ text: '构建', link: '/zh/building' },
{ text: '构建内核', link: '/zh/guide/build-kernel' },
{ text: '启动镜像', link: '/zh/guide/boot' },
{ text: '调试与日志', link: '/zh/guide/debugging' },
{ text: 'userdata 镜像', link: '/zh/guide/userdata' },
],
},
],
'/zh/building': [
{
text: '指南',
items: [
{ text: '总览', link: '/zh/guide/overview' },
{ text: '构建环境', link: '/zh/guide/requirements' },
{ text: '构建', link: '/zh/building' },
{ text: '构建内核', link: '/zh/guide/build-kernel' },
{ text: '启动镜像', link: '/zh/guide/boot' },
{ text: '调试与日志', link: '/zh/guide/debugging' },
{ text: 'userdata 镜像', link: '/zh/guide/userdata' },
],
},
],
'/zh/concepts/': [
{
text: '原理',
items: [
{ text: '启动链', link: '/zh/concepts/boot-chain' },
{ text: '启动负载', link: '/zh/concepts/payload' },
{ text: '内核预链接', link: '/zh/concepts/prelink' },
{ text: 'userdata 布局', link: '/zh/concepts/userdata' },
{ text: '完整性与启动策略', link: '/zh/concepts/security' },
],
},
],
'/zh/porting/': [
{
text: '移植',
items: [
{ text: '移植总览', link: '/zh/porting/' },
{ text: '设备仓库', link: '/zh/porting/device-repository' },
{ text: 'UEFI 固件', link: '/zh/porting/uefi-firmware' },
{ text: '设备树', link: '/zh/porting/device-tree' },
{ text: 'bring-up 顺序', link: '/zh/porting/bring-up' },
],
},
],
'/zh/drivers/': [
{
text: '驱动',
items: [
{ text: '驱动分层', link: '/zh/drivers/overview' },
{ text: 'UEFI 驱动', link: '/zh/drivers/uefi-drivers' },
{ text: 'XNU 驱动', link: '/zh/drivers/xnu-drivers' },
{ text: 'kext 包', link: '/zh/drivers/kexts' },
{ text: '设备树匹配', link: '/zh/drivers/device-tree-matching' },
],
},
],
'/zh/xnu/': [
{
text: 'XNU',
items: [
{ text: '源码与补丁', link: '/zh/xnu/source-and-patches' },
{ text: '4 KiB 页表', link: '/zh/xnu/four-k-page-tables' },
{ text: '补丁参考', link: '/zh/xnu/patch-reference' },
{ text: '启动参数', link: '/zh/xnu/boot-arguments' },
],
},
],
'/zh/devices/': [
{
text: '设备',
items: [
{ text: '支持的设备', link: '/zh/devices/' },
{ text: '小米平板 6S Pro(sheng)', link: '/zh/devices/sheng' },
{ text: '触控驱动示例', link: '/zh/devices/sheng-touch-driver' },
],
},
],
'/zh/reference/': [
{
text: '参考',
items: [
{ text: '命令行', link: '/zh/reference/cli' },
{ text: '设备配置', link: '/zh/reference/configuration' },
{ text: '启动配置', link: '/zh/reference/boot-configuration' },
{ text: '输出布局', link: '/zh/reference/output-layout' },
{ text: 'QEMU 测试', link: '/zh/reference/qemu' },
{ text: '故障排查', link: '/zh/reference/troubleshooting' },
{ text: '术语表', link: '/zh/reference/glossary' },
],
},
],
'/zh/contributing/': [
{
text: '参与贡献',
items: [
{ text: '文档站', link: '/zh/contributing/documentation' },
{ text: '仓库布局', link: '/zh/contributing/repository' },
],
},
],
},
outline: { level: [2, 3], label: '本页内容' },
docFooter: { prev: '上一页', next: '下一页' },
returnToTopLabel: '回到顶部',
sidebarMenuLabel: '菜单',
darkModeSwitchLabel: '外观',
lightModeSwitchTitle: '切换到浅色主题',
darkModeSwitchTitle: '切换到深色主题',
editLink: {
pattern: 'https://github.com/UniXNU/UniXNU/edit/main/docs/:path',
text: '在 GitHub 上编辑此页',
},
},
},
},
})
+71
View File
@@ -0,0 +1,71 @@
:root {
--vp-home-hero-name-color: var(--vp-c-text-1);
--vp-home-hero-name-background: none;
--vp-home-hero-image-background-image: none;
--vp-home-hero-image-filter: none;
--vp-c-brand-1: #2a5bd7;
--vp-c-brand-2: #1f4ec4;
--vp-c-brand-3: #1a43a9;
--vp-c-brand-soft: rgba(42, 91, 215, 0.12);
}
.dark {
--vp-c-brand-1: #7fa2ff;
--vp-c-brand-2: #6b91f5;
--vp-c-brand-3: #5a80e8;
--vp-c-brand-soft: rgba(127, 162, 255, 0.14);
}
.VPHero .VPImage {
filter: none;
content: url('/logo-light.svg');
width: 260px;
height: 260px;
}
.dark .VPHero .VPImage {
content: url('/logo-dark.svg');
}
.VPHero .name,
.VPHero .text,
.VPHero .tagline {
background: none;
}
.VPButton {
border-radius: 6px;
}
:root {
--docsearch-key-gradient: #ebedf0;
}
.dark {
--docsearch-key-gradient: #31355b;
}
.aside-curtain {
background: var(--vp-c-bg);
}
.excerpt-gradient-bottom,
.excerpt-gradient-top {
background: var(--vp-local-search-result-bg);
}
.vp-doc table {
display: table;
width: 100%;
}
.vp-doc code {
white-space: pre-wrap;
}
@media (min-width: 960px) {
.vp-doc table {
width: auto;
min-width: 60%;
}
}
+4
View File
@@ -0,0 +1,4 @@
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default DefaultTheme
+156 -191
View File
@@ -1,61 +1,6 @@
# Building UniXNU
# Build
This describes the `unixnu` build driver. The project was released in a hurry: the driver is new,
only the boot image step has been checked end to end, and the kernel, package and firmware steps
call scripts that still carry assumptions from the old single repository. Expect to read logs.
The driver is developed and tested on macOS. Whether another host works is not known: the kernel step builds
with Xcode (`xcodebuild`, the iphoneos SDK) and `--skip-setup` uses `hdiutil`, so those two need macOS. The firmware,
payload and boot image steps use Python, git, LLVM, EDK II, mtools and dtc, and have only been run on macOS.
Boot images are only ever started with `fastboot boot`. Nothing here flashes a device.
## Requirements
- macOS (see above), Python 3.11 or newer (standard library only, nothing to install with pip)
- Xcode for the kernel step, `hdiutil` for `--skip-setup`, `mtools` for the payload
- `git`, `make`, `cmake`, `ninja`, `unifdef`
- LLVM and lld from Homebrew (`clang`, `llvm-objcopy`, `llvm-nm`, `ld.lld`). The keg-only
`/opt/homebrew/opt/llvm/bin` is found without being on `PATH`.
- `dtc` (provides `fdtput` and `fdtget`)
- Network access for `unixnu fetch`
You also provide inputs that are never part of a repository:
| Input | Used by | Option |
| --- | --- | --- |
| A Mu-Silicium checkout for the device (for sheng: uefi-sheng) | firmware, bootimg | `--uefi DIR` or `UNIXNU_UEFI` |
| The target IPSW, a local file or an https URL | kernel (prelink), userdata | `--ipsw PATH_OR_URL` or `UNIXNU_IPSW`; defaults to the url of `board.toml` |
| The decompressed kernelcache (optional, taken from the IPSW when not given) | kernel (prelink) | `--kernelcache FILE` or `UNIXNU_KERNELCACHE` |
| A compiled device tree | bootimg | `--dtb FILE` or `UNIXNU_DTB` |
| A trust cache bundle (optional) | payload | `--trust-cache FILE` |
| A Data ramdisk seed (optional) | payload | `--data-ramdisk FILE` |
| The platform seed of the device (not stored in any repository) | payload | `--unique-chip-id HEX` or `UNIXNU_UNIQUE_CHIP_ID` |
| Vendor firmware inputs of the board | firmware | board preflight scripts |
| An edk2 tree (Mu_Basecore) | package, firmware | fetched by default, or `--edk2 DIR` / `UNIXNU_EDK2` |
## Layout
```
UniXNU/ this repository: the driver, generic modules and the package
unixnu entry point
scripts/unixnu/ the driver
package/ UniXNU itself (CrabApple, an EDK II package)
drivers/ kits/ patches/ scripts/
feeds.toml external sources, pinned by commit
examples/device/ a sample device repository (marked as an example)
UniXNU-sheng/ a device repository
board.toml board description
configs/*_defconfig kernel configuration
feeds.toml optional extra sources
drivers/ kits/ patches/ scripts/ package/
```
A device repository has the same folder layout as this repository. Its modules override modules of the
same name here. Its `package/` becomes `kits/<board.kit>` in the build tree.
All output goes under `out/` (ignored by Git): `out/<board>/` for the board and `out/feeds/` for fetched
sources. Nothing is written into the repositories, except `unixnu.lock` in the device repository.
`unixnu` is the build driver. It reads a device repository, resolves modules, assembles a build tree and runs each step.
## Quick start
@@ -65,164 +10,184 @@ git clone https://github.com/UniXNU/UniXNU-sheng
git clone https://github.com/UniXNU/uefi-sheng
cd UniXNU
./unixnu defconfig sheng_defconfig -D ../UniXNU-sheng # the device is remembered afterwards
./unixnu fetch # shallow, sparse clones, writes unixnu.lock
./unixnu ipsw fetch # kernelcache, device tree and trust caches from the IPSW
./unixnu defconfig sheng_defconfig -D ../UniXNU-sheng
./unixnu fetch
./unixnu ipsw fetch
./unixnu build kernel
./unixnu build package
./unixnu build firmware --uefi ../uefi-sheng
./unixnu bootimg --uefi ../uefi-sheng --dtb /path/to/sheng.dtb
./unixnu bootimg --uefi ../uefi-sheng --dtb /path/to/device.dtb
```
The boot image is `out/sheng/boot/boot.img`, with a `boot.json` report next to it. Options go after
the subcommand.
The device repository is remembered in `out/state.json` after the first `-D`.
The boot image is `out/<board>/boot/boot.img` with `out/<board>/boot/boot.json` next to it. Options follow the subcommand.
## Commands
| Command | What it does |
| Command | Action |
| --- | --- |
| `defconfig [NAME]` | Lists `configs/*_defconfig` of the device repository, or selects one into `out/<board>/.config`. |
| `fetch [NAME...] [--update]` | Fetches git sources and records the commits in `unixnu.lock`. `--update` ignores the lock. |
| `tree` | Assembles `out/<board>/tree`, the layout the build scripts expect. |
| `ipsw info\|fetch [COMPONENT...]` | Lists the components of the target IPSW, or extracts them into `out/ipsw/<build>/`. |
| `build [kernel] [package] [firmware] [payload] [prelink]` | Builds the steps (default: all four). `prelink` repeats only the prelink. |
| `build userdata [--dry-run]` | Builds the raw image for the userdata partition (large, only when named). |
| `bootimg` | Packs BootShim, the firmware volume and the device tree into an Android boot image. |
| `status` | Shows the device, the configuration, and where every module comes from. |
| `clean` | Removes the build output of the board and keeps fetched sources. |
| `defconfig [NAME]` | Lists `configs/*_defconfig` of the device repository, or copies one to `out/<board>/.config`. |
| `fetch [NAME...] [--update]` | Fetches git sources and writes `unixnu.lock` in the device repository. `--update` ignores the lock. |
| `tree` | Resolves modules, fetches the references the steps need, and assembles `out/<board>/tree`. |
| `ipsw info` | Prints the product, build and every component of the target IPSW. |
| `ipsw fetch [COMPONENT...]` | Extracts the named components into `out/ipsw/<build>/`. |
| `build [kernel] [package] [firmware] [payload] [prelink]` | Runs the named steps. The default is all four steps. `prelink` repeats the prelink alone. |
| `build userdata [--dry-run]` | Builds the raw userdata image. This step is large and runs only when named. |
| `bootimg` | Packs BootShim, the firmware volume, the device tree and the optional payload into an Android boot image. |
| `status` | Prints the device, configuration and the origin of every module. |
| `clean` | Removes the board output and keeps fetched sources. |
Common options: `-D DIR` device repository, `--out DIR` output root, `-j N` jobs, `--target DEBUG|RELEASE`,
`--uefi`, `--edk2`, `--kernelcache`, and the local source options below.
Environment: `UNIXNU_DEVICE`, `UNIXNU_OUT`, `UNIXNU_UEFI`, `UNIXNU_EDK2`, `UNIXNU_KERNELCACHE`,
`UNIXNU_DTB`, `JOBS`.
## Common options
| Option | Meaning |
| --- | --- |
| `-D DIR` | Device repository. Remembered after the first use. |
| `--out DIR` | Output root. Default `out/` in this repository. |
| `-j N` | Parallel jobs. Default is `JOBS` or the CPU count. |
| `--target DEBUG\|RELEASE` | EDK II build target. Default `DEBUG`. |
| `--uefi DIR` | Mu-Silicium checkout for the device. |
| `--edk2 DIR` | Mu_Basecore checkout. |
| `--kernelcache FILE` | Original decompressed kernelcache. |
| `--kernel FILE` | Prelinked kernel Mach-O for the payload. |
| `--kernel-arg NAME[=VALUE]` | Extra kernel argument. Repeatable. |
| `--trust-cache FILE` | Trust cache bundle. |
| `--data-ramdisk FILE` | Data ramdisk seed. |
| `--skip-setup` | Marks Setup as done in a copy of the Data seed. Requires `--data-ramdisk`. |
| `--unique-chip-id HEX` | Platform seed for the device tree. |
| `--ipsw PATH_OR_URL` | Target IPSW, local or remote. |
| `--skip-preflight` | Skips the board preflight scripts of the firmware step. |
| `--dry-run` | Userdata only: check sizes and pins, write nothing. |
The environment variables `UNIXNU_DEVICE`, `UNIXNU_OUT`, `UNIXNU_UEFI`, `UNIXNU_EDK2`, `UNIXNU_KERNELCACHE`, `UNIXNU_DTB`, `UNIXNU_IPSW`, `UNIXNU_UNIQUE_CHIP_ID` and `JOBS` supply the same values.
## Steps
**ipsw** is not a step but the source of the original OS files. `--ipsw` takes a local file or an https URL.
A remote IPSW is never downloaded as a whole: the zip directory and each needed member are read with HTTP range
requests. Members are cached in `out/ipsw/<build>/` and checked against the sha256 pins of `board.toml [ipsw.sha256]`.
The image must match `[ipsw]` (product, version, build, device class, chip and board id).
### defconfig
**kernel** applies `patches/xnu` and the prelink patches to Apple's xnu-10063.141.1, builds the kernel with
the switches of the defconfig, then prelinks it against the original kernelcache with
`kits/NativePrelink`. The kernelcache is taken from the IPSW (the LZFSE payload of its IM4P, arm64 slice, checked against
`kernelcache_sha256`) unless `--kernelcache` gives one; decoding it uses macOS `compression_tool`. The result is `out/<board>/kernel/kernel.macho`. If the board lists a
`kernelcache_sha256`, any other kernelcache is refused. The XNU sources, libplatform, libdispatch and
AvailabilityVersions are fetched on demand.
`defconfig` copies `configs/<name>_defconfig` from the device repository to `out/<board>/.config`. It parses the file to reject a malformed line before the build starts.
**package** builds `package/` (UniXNU itself) with EDK II into `out/<board>/package/Build/Out`.
### fetch
**firmware** builds the device firmware from the Mu-Silicium checkout. It runs the preflight scripts of the
board, applies the board's patches to edk2 for the build only (they are always reverted), and runs `build` with
the board's DSC and defines. `FD_BASE`, `FD_SIZE` and `FD_BLOCKS` come from the Mu-Silicium device config.
`fetch` resolves every module and fetches the git sources. It uses a shallow, blobless clone with a cone sparse checkout. One clone exists per repository and ref. The commit of each source is written to `unixnu.lock` in the device repository. A later build uses the locked commit.
**payload** builds the ramdisk of the boot image: a FAT volume with the CrabApple config, the prelinked kernel
(`--kernel FILE`, default the kernel step output; optionally with the panic screen patched into a copy), the
device tree sidecar generated by the board script, and the optional trust cache and Data seed. The kernel command
line comes from `board.toml` plus any `--kernel-arg NAME=VALUE`. The result is `out/<board>/payload/payload.img`
with a `payload.json` report. The volume is written with mtools without mounting. The device tree contains a
random seed, so the payload differs between runs. The loader (DarwinBoot) is part of the firmware volume, and the
IPSW is not read by this step: its inputs are the kernel, which comes from the original kernelcache, and the
files you provide. `--skip-setup` marks Setup as done in a copy of the Data seed given with `--data-ramdisk`, so the Hello pages are skipped.
The Data seed itself is an input.
### tree
**userdata** builds the raw image for the `userdata` partition: the unchanged OS and SystemOS disk images of the
IPSW, byte for byte, behind a manifest (`SHNGROOT`). The disk images are extracted from the IPSW (about 8 GiB) and
attached read-only with `hdiutil`. The result is `out/<board>/userdata/userdata-root.img` with a report. Nothing is
flashed: the report prints the `fastboot flash userdata` command for you to run. `--dry-run` only checks the IPSW and
the sizes.
`tree` assembles `out/<board>/tree` with the layout the scripts expect:
**bootimg** compiles the BootShim, appends the firmware volume, compresses it with gzip, appends the device
tree (after adding the board's `dtb_node` entries), and runs `mkbootimg.py` with the parameters of the
Mu-Silicium device config. It unpacks the result and compares every part with its input, so a mismatch fails
the build. The output is deterministic. The ramdisk is the output of the payload step when it exists, or `--ramdisk FILE`,
or empty with `--ramdisk none`.
## Local development
Any module can come from a local directory instead of the repository or git. The first match wins:
1. `--local-module [KIND:]NAME=PATH`, one module
2. `--local-drivers|kits|patches|scripts|package DIR`, one category (each subdirectory is a module; for
`package` the directory is the package itself)
3. `--local DIR`, a directory laid out like this repository
4. a `local = "DIR"` entry in a `feeds.toml`, or an ignored `local.toml` in the device repository
(see `examples/device/local.toml.example`)
5. the device repository
6. this repository
7. a git source of a `feeds.toml`
Local modules are linked into the build tree, not copied, so an edit is used by the next build. A missing
directory is an error. `unixnu status` marks local modules and warns that they are not pinned by the lock, so
a build that uses them is not reproducible.
## Configuration files
### board.toml (device repository)
| Section | Keys |
| --- | --- |
| `[board]` | `name`, `soc`, `description`, `kit` (folder name of the device package under `kits/`), `example` |
| `[ipsw]` | `url` (default remote source), `product`, `version`, `build`, `device_class`, `chip_id`, `board_id`, `fetch` (components `ipsw fetch` extracts by default) |
| `[ipsw.sha256]` | sha256 pins by member file name |
| `[userdata]` | `first_lba`, `last_lba`, `lba_bytes` (the GPT entry of the partition) and `windows` (name and IPSW component of each disk image) |
| `[kernel]` | `defconfig` |
| `[kernel.prelink]` | `tool`, `kernel`, `slide`, `roots` (list), `early_security_init`, `kernelcache`, `kernelcache_sha256`, `extra_args` |
| `[firmware]` | `config` (name of `Resources/Configs/<name>.toml`), `dsc`, `fd` (path of the result, `{target}` is replaced), `packages` (`{uefi}`, `{edk2}`, `{tree}` are replaced), `preflight`, `patches` |
| `[firmware.defines]` | extra `-D` definitions |
| `[bootimg.image]` | overrides of the image parameters (`base`, `kernel_offset`, `tags_offset`, `pagesize`, `header_version`) |
| `[payload]` | `volume`, `size_mib`, `loader` (`firmware`), `title`, `dram_size`, `panic_screen`, `trust_cache_sha256` (accepted bundles), `kernel_args` |
| `[payload.adt]` | `tool`, `chip_id`, `owned_memory_size`, `root_match_volume_uuid`, `cpu_regs`, `psci_method`, `packed_data_ramdisk`, `ipad_identity`, `unique_chip_id = "required"` |
| `[[bootimg.dtb_node]]` | `path` and `props` (`name`, `type`, `value`) added to the device tree |
`examples/device` is a complete sample. It is marked `example = true`, and `build` and `bootimg` refuse to run
on a repository with that mark.
### configs/NAME_defconfig
Lines of the form `CONFIG_NAME=y`, `=n`, `="text"` or `=123`, and `# CONFIG_NAME is not set`. Each
`CONFIG_XNU_<NAME>` is passed to the kernel build as `XNU_<NAME>`, with `y` as 1 and `n` as 0. `{tree}` in a
string is replaced by the build tree.
### feeds.toml
```toml
[[source]]
name = "SomeDriver"
kind = "driver" # driver, kit, patch, package, refer
git = "https://github.com/ORG/REPO"
ref = "main"
path = "drivers/SomeDriver" # optional: fetch only this directory
commit = "..." # optional pin
local = "~/Code/dev/x" # optional: use a local directory instead
```
tree/
drivers/ kits/ patches/ refer/
scripts/ package/
build/ .cache/
```
Fetching uses `git fetch --depth 1 --filter=blob:none` and a cone sparse checkout, one clone per
repository and ref. UniXNU repositories do not use submodules; the `submodules` key exists only for upstream
trees such as edk2. `unixnu.lock` in the device repository records the commit of every fetched source.
Modules are symbolic links to their sources. Scripts are copied. The device `package/` appears as `kits/<board.kit>`.
## Status
### ipsw
Observed on the Xiaomi Pad 6S Pro with `fastboot boot` of an image built by the driver (firmware compiled from
source, payload with a prelinked kernel, trust cache and Data seed supplied as inputs):
The IPSW is read member by member. A remote IPSW is never downloaded as a whole. The zip directory and each needed member are read with HTTP range requests. Extracted members are cached in `out/ipsw/<build>/` and checked against the sha256 pins in `board.toml [ipsw.sha256]`.
| Stage | State |
The image must match `[ipsw]`: product, version, build, device class, chip ID and board ID.
### kernel
The kernel step:
1. Applies `patches/xnu` and `kits/NativePrelink/patches` to the pinned XNU source.
2. Runs `prepare-xnu-source.py` to restore omitted DriverKit headers.
3. Builds XNU for `ARM64`, machine `BCM2837`, configuration `DEVELOPMENT`, iOS 17.
4. Prelinks the native kernel against the original kernelcache with `kits/NativePrelink`.
The kernelcache comes from the IPSW unless `--kernelcache` supplies one. The LZFSE payload of the `KernelCache` IM4P is decoded with macOS `compression_tool`, the arm64 slice is selected, and the result is checked against `[kernel.prelink] kernelcache_sha256`.
The output is `out/<board>/kernel/kernel.macho`.
### package
The package step builds `package/` with EDK II. The output is `out/<board>/package/Build/Out`. It contains `UniXNU.efi`, `UniXNUDxe.efi`, `HelloPayload.efi`, `SampleDriver.efi` and `XnuTest.efi`.
### firmware
The firmware step builds the device firmware from the Mu-Silicium checkout:
1. Runs the board preflight scripts.
2. Applies the board patches to the shared edk2 tree for the build, then reverts them.
3. Runs EDK II `build` with the board DSC and defines.
`FD_BASE`, `FD_SIZE` and `FD_BLOCKS` come from the Mu-Silicium device configuration. The firmware volume must match `[uefi_fd] size` from that configuration.
### payload
The payload step writes a FAT volume with:
- `EFI/UniXNU/unixnu.toml`
- `EFI/UniXNU/Payloads/kernel.macho`
- `EFI/UniXNU/Payloads/kernel.macho.adt`
- the optional `kernel.macho.trustcache`
- the optional `scratch-data.img`
The kernel command line comes from `[payload] kernel_args` plus every `--kernel-arg`. The volume is written with mtools without mounting. The output is `out/<board>/payload/payload.img` with `payload.json`.
`--skip-setup` marks Setup as done in a copy of the Data seed supplied with `--data-ramdisk`.
### userdata
The userdata step builds the raw image for the `userdata` partition. It holds the unchanged `OS` and `SystemOS` disk images of the IPSW behind the `SHNGROOT` manifest. The disk images are attached read-only with `hdiutil`. The output is `out/<board>/userdata/userdata-root.img` with `userdata-root.json`.
The report prints the `fastboot flash userdata` command.
### bootimg
The boot image step:
1. Compiles BootShim for the firmware base and size.
2. Appends the firmware volume.
3. Compresses the pair with gzip.
4. Appends the device tree after applying `[[bootimg.dtb_node]]`.
5. Packs the result with `mkbootimg.py` and the image parameters of the Mu-Silicium device configuration.
6. Unpacks the image and compares every part with its input.
The ramdisk is the payload output when it exists, `--ramdisk FILE`, or an empty ramdisk with `--ramdisk none`.
## Local modules
A module can come from a local directory. The first match wins:
1. `--local-module [KIND:]NAME=PATH`
2. `--local-drivers|kits|patches|scripts|package DIR`
3. `--local DIR`, a directory with this repository's layout
4. a `local = "DIR"` entry in `feeds.toml`, or `local.toml` in the device repository
5. the device repository
6. this repository
7. a git source in `feeds.toml`
Local modules are linked into the build tree. `unixnu status` marks them and reports that they are not pinned by the lock.
## Output
All output goes to `out/`:
| Path | Contents |
| --- | --- |
| Stock ABL loads the boot image, BootShim starts the UEFI firmware | works |
| UEFI (Silicium) and CrabApple | works; Silicium stalls at random and a retry of the same image starts it |
| CrabApple finds and loads the payload volume (ramdisk) | works |
| XNU and userland | reached with images from the earlier workflow; not yet recorded for driver-built images |
| `out/state.json` | Remembered device repository |
| `out/feeds/` | Fetched sources |
| `out/ipsw/<build>/` | Extracted IPSW members |
| `out/<board>/.config` | Selected defconfig |
| `out/<board>/tree/` | Assembled build tree |
| `out/<board>/logs/` | Step logs |
| `out/<board>/kernel/kernel.macho` | Prelinked kernel |
| `out/<board>/package/Build/Out/` | EFI binaries |
| `out/<board>/payload/` | Payload image and report |
| `out/<board>/boot/` | Boot image and report |
| `out/<board>/userdata/` | Userdata image and report |
## Limits
## Reproducibility
- The kernel step needs prebuilt static libraries (corecrypto, ArmCpu, NativeUfs, NativeAES, NativeAdreno and
others) that separate scripts produce. Running them from the driver is planned. Meanwhile pass an existing
prelinked kernel with `--kernel`.
- The kernel and userdata steps read the IPSW. Only the iPad7,1 iPadOS 17.7.11 (21H461) image is described by a board.
- The firmware step needs the vendor binaries of the board (`Binaries/<board>`), and the board preflight script
still expects the old layout, so use `--skip-preflight`.
- The firmware volume differs between two builds from the same sources. The boot image is deterministic for the
same firmware volume, device tree and ramdisk.
- Many scripts in `scripts/` still use the old layout (`build/`, `.cache/`, `refer/`) and are being generalized.
- The Data seed of the boot payload is an input.
- The userdata image reproduced a previously built image byte for byte. It has not been flashed from this driver.
`unixnu.lock` pins every fetched source. The firmware build sets `SOURCE_DATE_EPOCH` to the Mu-Silicium commit time, so EDK II does not embed the build time. The boot image is deterministic for a given firmware volume, device tree and ramdisk. The payload contains a random seed, so two payload builds differ.
## Next steps
- [Build the kernel](/guide/build-kernel) describes the kernel flags and archives.
- [Boot the image](/guide/boot) describes the fastboot commands.
- [Reference](/reference/output-layout) lists every report field.
+121
View File
@@ -0,0 +1,121 @@
# Boot chain
The boot chain has four handoffs. Each handoff has a fixed input, a validation step and a failure mode that returns to the previous stage.
## 1. Bootloader to BootShim
The stock Android bootloader reads the boot image. The kernel field is a gzip stream. The first part is BootShim, the second part is the UEFI firmware volume. The device tree follows the gzip stream.
BootShim is assembled for the firmware base and size from the Mu-Silicium device configuration:
```sh
clang --target=aarch64-none-elf -c \
-DFD_BASE=0xA7100000 -DFD_SIZE=0x300000 \
-DREQUIRES_KERNEL_HEADER=1 BootShim.S -o shim.o
llvm-objcopy -O binary shim.o shim.bin
```
The shim contains an ARM64 image header when the device configuration requires one. The header describes the shim plus the firmware volume, so the bootloader can load the pair at the requested address.
BootShim relocates the firmware volume and enters UEFI.
## 2. UEFI to UniXNU
The firmware volume contains the UniXNU boot manager and its driver. `UniXNUDxe` registers `UniXNU.efi` as a `Boot####` option with the load option data `unixnu:firmware`. BDS starts the application, or the user starts it from the firmware menu.
The boot manager scans the volume it was started from, then every other volume, for `\EFI\UniXNU\unixnu.toml`. It parses the file and applies the configuration.
Before the menu, UniXNU:
1. Publishes every `[[block_slice]]`.
2. Loads and starts every `[[driver]]`.
3. Installs every ACPI table.
4. Merges every kit whose `[match]` section matches.
5. Builds the menu.
## 3. UniXNU to DarwinBoot
An entry of type `xnu` is passed to the loader protocol:
```c
typedef struct {
EFI_HANDLE Volume;
CONST CHAR16 *KernelPath;
CONST CHAR16 *BootArgs;
VOID *DeviceTree;
UINTN DeviceTreeSize;
VOID *Ramdisk;
UINTN RamdiskSize;
CONST UNIXNU_LOADER_KEXT *Kexts;
UINTN KextCount;
} UNIXNU_LOADER_BOOT_REQUEST;
```
UniXNU prepares the device tree for the entry:
1. It takes the firmware DTB, a file DTB, or no DTB.
2. It copies the tree with 64 KiB of slack.
3. It applies global patches from the configuration and kits.
4. It applies the entry patches.
5. It applies `[entry.chosen]` properties last.
6. It installs the tree as `EFI_DTB_TABLE`.
The loader receives the tree, the ramdisk, the boot arguments and any staged kexts.
## 4. DarwinBoot to XNU
DarwinBoot validates and loads the kernel:
1. Read `kernel.macho` from the entry directory.
2. Parse the Mach-O header. It requires `MH_MAGIC_64`, `CPU_TYPE_ARM64`, `MH_EXECUTE`, one `LC_MAIN` and at least one `LC_SEGMENT_64`.
3. Check that the entry point lies in an executable segment.
4. Check that every segment is page aligned, non-overlapping and within the 256 MiB window.
5. Read `kernel.macho.adt`.
6. Read `kernel.macho.trustcache` when present.
7. Validate the trust cache: header count, offsets, entry count and sorted hashes.
8. Read the framebuffer information from GOP.
9. Allocate a contiguous physical window.
10. Copy the segments, the ADT, the trust cache and the ramdisk.
11. Build `DARWIN_BOOT_ARGS`.
12. Exit boot services.
13. Disable the MMU and caches.
14. Jump to the entry point in EL1.
The `DARWIN_BOOT_ARGS` layout matches Darwin 23:
| Offset | Field |
| --- | --- |
| 0x00 | revision, version, padding |
| 0x08 | virtual base |
| 0x10 | physical base |
| 0x18 | memory size |
| 0x20 | top of kernel data |
| 0x28 | video, six 64-bit values |
| 0x60 | device tree address |
| 0x68 | device tree length |
| 0x6c | command line, 1024 bytes |
| 0x478 | memory size actual |
## 5. XNU to userland
XNU starts at `start.s`. It installs the bootstrap page tables, enters the C boot path, initializes the platform expert and publishes the IOKit registry.
The platform expert matches the ADT root. The generic `ArmDTPlatform` requires `/cpus`, `/arm-io`, `/defaults`, `/options` and a CHRP NVRAM proxy in `/chosen`. It publishes CPU nubs and every `arm-io` child.
The storage driver publishes the userdata partition. The partition scheme publishes `OS` and `SystemOS`. XNU matches the root volume by UUID, mounts it, and starts `launchd` from the selected userland.
## Failure behavior
| Stage | Failure | Result |
| --- | --- | --- |
| Bootloader | Header mismatch | Image refused, device stays in bootloader |
| Firmware | No GOP or no input | UniXNU runs headless or in text mode |
| UniXNU | No config | Built-in configuration and removable loaders |
| DarwinBoot | Invalid kernel, ADT or trust cache | `EFI_COMPROMISED_DATA`, firmware retained |
| XNU | Missing platform or storage driver | Kernel panic with the missing provider in the log |
## Next steps
- [Payload](/concepts/payload) describes the FAT volume.
- [Prelink](/concepts/prelink) describes the kernel and kernelcache binding.
- [Integrity and boot policy](/concepts/security) describes trust caches and AMFI.
+113
View File
@@ -0,0 +1,113 @@
# Boot payload
The payload is a FAT volume that becomes the ramdisk of the Android boot image. UniXNU reads the configuration from it, and DarwinBoot reads the kernel, the ADT and the optional trust cache from it.
## Volume layout
```
/EFI/UniXNU/unixnu.toml
/EFI/UniXNU/Payloads/kernel.macho
/EFI/UniXNU/Payloads/kernel.macho.adt
/EFI/UniXNU/Payloads/kernel.macho.trustcache
/EFI/UniXNU/Payloads/scratch-data.img
```
The payload step writes the volume with `mformat` and `mcopy`. Timestamps are fixed at 1980-01-01, so the volume is reproducible for the same inputs. The ADT contains a random seed, so two payload builds differ.
## Configuration
The payload step writes this configuration:
```toml
version = 1
[boot]
timeout = 3
default = "xnu"
[kits]
autoload = false
[[entry]]
id = "xnu"
title = "UniXNU"
type = "xnu"
path = "Payloads/kernel.macho"
initrd = "Payloads/scratch-data.img"
args = "-v serial=3"
```
`initrd` is present when a Data seed is supplied. `args` is the joined `[payload] kernel_args` and `--kernel-arg` values.
## ADT sidecar
The ADT uses Apple's flattened node encoding. The board script named in `[payload.adt] tool` generates it. The payload step calls the tool with:
| Option | Source |
| --- | --- |
| `--output` | `Payloads/kernel.macho.adt` |
| `--dram-size` | `[payload] dram_size` |
| `--owned-memory-size` | `[payload.adt] owned_memory_size` |
| `--root-match-volume-uuid` | `[payload.adt] root_match_volume_uuid` |
| `--chip-id` | `[payload.adt] chip_id` |
| `--cpu-regs` | `[payload.adt] cpu_regs` |
| `--psci-method` | `[payload.adt] psci_method` |
| `--unique-chip-id` | `--unique-chip-id` or `UNIXNU_UNIQUE_CHIP_ID` |
| `--packed-data-ramdisk` | `[payload.adt] packed_data_ramdisk` |
| `--data-ramdisk` | a Data seed without packed delivery |
| `--trust-cache` | a trust cache bundle |
| `--ipad-identity` | `[payload.adt] ipad_identity` |
The ADT must reserve the following properties when the matching input exists:
| Property | Purpose |
| --- | --- |
| `/chosen/memory-map/RAMDisk` | DarwinBoot writes the ramdisk address and size |
| `/chosen/memory-map/PackedRAMDisk` | DarwinBoot writes the packed ramdisk address and size |
| `/chosen/memory-map/TrustCache` | DarwinBoot writes the trust cache address and size |
| `/chosen/darwinboot-memory-size` | Requests a contiguous window larger than 256 MiB |
DarwinBoot requires the placeholder to exist for every input it receives. A mismatch is `EFI_COMPROMISED_DATA`.
## Kernel arguments
The payload checks the command line before writing the volume:
- Every argument matches `[A-Za-z0-9_.\-]+` with an optional `=value` of `[A-Za-z0-9_.,\-]+`.
- The joined line is at most 1000 bytes. DarwinBoot copies it into a 1024-byte buffer.
The command line is stored in `unixnu.toml` as `args`. DarwinBoot copies it into `boot_args` without modification.
## Trust cache
The trust cache bundle is validated by DarwinBoot:
- A 32-bit count from 1 to 128.
- One offset table entry per cache.
- Every cache starts with version 2.
- Every entry is 24 bytes.
- Every hash type is 1 to 4.
- Hashes are sorted in ascending order.
The bundle is copied into the kernel window and its address and size replace the `TrustCache` placeholder.
## Data ramdisk
A Data seed can be delivered in two forms:
| Form | ADT placeholder | Behavior |
| --- | --- | --- |
| Dense | `RAMDisk` | DarwinBoot copies the whole image |
| Packed | `PackedRAMDisk` | DarwinBoot validates the `DBRDISK1` extent table and copies the extents |
The packed form carries a header with the magic `DBRDISK1`, version 1, the page size, the expanded size and an extent table of `(offset, length)` pairs. Omitted ranges are zeroed RAM.
## Determinism
The boot image is deterministic for a given firmware volume, device tree and ramdisk. The ADT random seed makes each payload build differ. The payload report records the kernel, ADT, trust cache, Data seed and payload hashes.
## Next steps
- [Boot chain](/concepts/boot-chain) describes how the payload is consumed.
- [Boot configuration](/reference/boot-configuration) lists every `unixnu.toml` key.
- [Boot arguments](/xnu/boot-arguments) lists the kernel arguments.
+98
View File
@@ -0,0 +1,98 @@
# Kernel prelink
The prelinked kernel combines a kernel built from source with the original Apple kernelcache. The source kernel provides the code that UniXNU changes. The original kernelcache provides the Apple kexts and platform services, which the source tree leaves to the kernelcache.
## Inputs
| Input | Source |
| --- | --- |
| Native kernel | `out/<board>/tree/build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf` |
| Original kernelcache | IPSW `KernelCache`, or `--kernelcache FILE` |
| Component list | `[kernel.prelink] roots` |
| Slide | `[kernel.prelink] slide` |
| Early security initializer | `[kernel.prelink] early_security_init` |
The kernelcache is the LZFSE payload of the `KernelCache` IM4P. The arm64 slice is selected and checked against `kernelcache_sha256`.
## Prelink steps
`kits/NativePrelink/emit_prelink.py` performs these steps:
1. Parse the native and original Mach-O images.
2. Read `__PRELINK_INFO` from the original kernelcache.
3. Select the components named in `roots` plus the kernel interfaces they import.
4. Copy the selected component segments into the native kernel's empty coalesced segments.
5. Rebind every pointer that references a selected component.
6. Apply the slide to every selected address.
7. Rewrite the module headers and kmod entries.
8. Build a sparse prelink index.
9. Write `kernel.macho`.
The output keeps the original component code unchanged. The native kernel supplies the platform code, the CPU code, the storage code and the device drivers selected in the defconfig.
## Coalesced segments
The original kernelcache places kext segments in coalesced ranges:
| Original segment | Native segment |
| --- | --- |
| `__PRELINK_TEXT` | `__TEXT` |
| `__PLK_TEXT_EXEC` | `__TEXT_EXEC` |
| `__PLK_DATA_CONST` | `__DATA_CONST` |
| `__PRELINK_DATA` | `__DATA` |
The prelink copies each component segment into the matching range. The ranges keep their original virtual addresses, so pointers inside the original code stay valid after the slide.
## Pointer rebinding
The native kernel imports symbols from the original components. The prelink resolves each import to the component export and writes the slid address at the import site.
The plan records every binding:
```json
{
"bindings": [
{ "new_site": "0xfffffff007123456", "new_target": "0xfffffff004abcdef" }
]
}
```
The report is written next to the output. It also records the component list, the instruction inventory and the module entry points.
## Early security initialization
`early_security_init` selects an original security extension and an address in its executable segment. The prelink slides the address and records it for the kernel's early security path. The selected component must carry `AppleSecurityExtension = true` in the prelink metadata.
## Kernelcache pin
A board can pin the kernelcache:
```toml
[kernel.prelink]
kernelcache_sha256 = "..."
```
The prelink refuses a different kernelcache. This ties the native kernel to the original component ABI it was qualified against.
## Sparsity and capacity
The prelink emits a sparse index. The kernel patch `native-4k-bootstrap-capacity` increases the bootstrap page table capacity for native 4 KiB mappings. The boot progress patch adds checkpoint output around the prelink path.
## Report
`emit_prelink.py` writes a JSON report next to the output:
| Field | Meaning |
| --- | --- |
| `components` | Original bundle identifiers included |
| `kernel_interfaces` | Components that export kernel interfaces |
| `bindings` | Import sites and slid targets |
| `instruction_inventory` | PC-relative encodings and inline data |
| `module_entrypoints` | Module load addresses after the slide |
| `early_security_initializers` | Selected early initializer addresses |
## Next steps
- [Build the kernel](/guide/build-kernel) lists the kernel flags.
- [Four-kilobyte pages](/xnu/four-k-page-tables) explains the page size patches.
- [Integrity and boot policy](/concepts/security) explains the trust cache path.
+87
View File
@@ -0,0 +1,87 @@
# Integrity and boot policy
UniXNU keeps the original Apple security components and supplies the inputs they check. The source kernel adds the platform code those components need, and the boot payload supplies the trust caches and device tree properties that describe the device.
## Inputs
| Input | Supplier |
| --- | --- |
| Trust cache bundle | `--trust-cache FILE` |
| Trust cache pins | `[payload] trust_cache_sha256` |
| Platform seed | `--unique-chip-id HEX` |
| Device tree identity | `[payload.adt] ipad_identity` and the board script |
| PMGR integration | `XNU_SHENG_PMGR` on boards that need it |
| Root policy | `XNU_VERIFIED_ROOT_PROBE`, `XNU_OWNER_ROOT_AUDIT`, `XNU_OWNER_ROOT_TRUST` |
## Trust cache
DarwinBoot validates the structure of the trust cache bundle and copies it into the kernel window. The ADT `/chosen/memory-map/TrustCache` placeholder receives the address and size.
The kernel patch `native-trust-cache-runtime` initializes the runtime trust cache path on `BCM2837`. The original AppleImage4 and AMFI components validate code against the cache.
The payload step accepts a trust cache bundle only when its SHA-256 is listed in `[payload] trust_cache_sha256`.
## Code signing at 4 KiB
The original CodeDirectory uses a 16 KiB hash page. A 4 KiB kernel validates 4 KiB pages. The patch `native-4k-signature-hash-cache`:
1. Reads every original 16 KiB block through `vn_rdwr`.
2. Checks the block against the original CodeDirectory hash.
3. Derives one hash per 4 KiB page from the verified block.
4. Stores the derived hashes in read-only kernel memory.
5. Serves `cs_validate_page` from the derived table.
A block is published only after the original 16 KiB hash matches. A failed block leaves the blob without a derived table, and the page validator returns `CS_VALIDATE_TAINTED`.
## AMFI policy
The ADT property `/chosen/amfi-only-platform-code` keeps AMFI in its platform code policy. The `early_security_init` entry in `[kernel.prelink]` runs the original `AppleMobileFileIntegrity` initializer at the selected address.
The kernel patches `offline-owner-root-*` add the owner root checks used by the first boot path:
| Patch | Function |
| --- | --- |
| `offline-owner-root-binding-audit` | Checks the root binding against the boot manifest |
| `offline-owner-root-crypto-probe` | Checks the owner crypto provider |
| `offline-owner-boot-manifest` | Records the boot manifest hash |
| `offline-owner-cache-carrier` | Selects the code signing cache carrier |
| `offline-owner-systemos-mount` | Mounts the SystemOS cryptex |
| `offline-owner-z-air-init` | Initializes the Z air path for the owner root |
## Verified root
`kits/VerifiedRoot` supplies a signed policy bundle. The verified block driver checks every read against the policy. The policy can be selected per capacity with `--verified-root-bundle`.
The kit provides:
| Tool | Function |
| --- | --- |
| `verified_root.c` | Policy parsing and read authentication |
| `xnu_crypto.c` | XNU crypto adapter |
| `manifest.py` | Policy bundle builder |
| `verify_image.py` | Image verification |
## Storage integrity
`NativeVirtioBlock` and `NativeRamBlock` share the verified block policy. The build script checks that both transports import the same original storage bindings and that the compiled object matches the recorded hash.
## Reporting
The payload report records:
| Field | Meaning |
| --- | --- |
| `kernel_sha256` | Input kernel |
| `payload_kernel_sha256` | Kernel in the payload, after the optional panic screen patch |
| `adt_sha256` | ADT sidecar |
| `trust_cache_sha256` | Trust cache bundle |
| `data_ramdisk_sha256` | Data seed |
| `payload_sha256` | Payload image |
The boot manager log records the staged kexts, the installed ACPI tables and the injected drivers.
## Next steps
- [Payload](/concepts/payload) describes the trust cache and Data inputs.
- [Patch reference](/xnu/patch-reference) lists the security patches.
- [Boot arguments](/xnu/boot-arguments) lists the security related arguments.
+89
View File
@@ -0,0 +1,89 @@
# Userdata layout
The userdata partition carries the Darwin root file system. UniXNU keeps the original disk images unchanged and adds a manifest that maps each image to a window in the partition.
## Manifest
```
struct shngroot_header {
char magic[8]; /* "SHNGROOT" */
uint32_t version; /* 1 */
uint32_t count; /* window count */
uint64_t image_bytes; /* total image size */
struct {
char name[32];
uint64_t offset;
uint64_t size;
uint32_t block_size; /* 512 */
uint32_t flags; /* bit 0: read-only */
uint8_t sha256[32];
uint8_t reserved[40];
} window[count];
uint8_t reserved[32];
uint8_t manifest_sha256[32];
};
```
The header occupies 4096 bytes and is zero padded to 1 MiB. The first window starts at 0x100000. Every window is aligned to 1 MiB.
## Windows
| Window | IPSW component | Contents |
| --- | --- | --- |
| `OS` | `OS` | The system volume |
| `SystemOS` | `Cryptex1,SystemOS` | The cryptex system volume |
The images are copied byte for byte from the disk images in the IPSW. The userdata step attaches each disk image read-only with `hdiutil` and reads the raw device.
## Capacity
The partition capacity is:
```
(last_lba - first_lba + 1) * lba_bytes
```
The step refuses an image larger than the capacity. The report records the capacity and the image size.
## Root matching
XNU selects the root volume through `/chosen/root-matching`:
```xml
<dict>
<key>IOProviderClass</key><string>IOMedia</string>
<key>IOPropertyMatch</key>
<dict>
<key>UUID</key><string>00000000-0000-0000-0000-000000000000</string>
</dict>
</dict>
```
The UUID is the volume UUID of the OS window. The native block driver publishes the userdata partition, the partition scheme publishes the volumes, and XNU matches the UUID.
## Data volume
The writable Data volume is separate from the userdata image. It comes from the Data ramdisk seed in the payload. The seed is an input to the payload step, and the payload report records its hash.
`--skip-setup` writes a copy of the seed with the Setup preferences marked as done. The copy is cached by the seed hash.
## Report
`userdata-root.json` records:
| Field | Meaning |
| --- | --- |
| `image_bytes` | Total image size |
| `image_sha256` | Image hash |
| `manifest_sha256` | Manifest hash |
| `windows` | Name, offset, size and hash of each window |
| `userdata_bytes` | Partition capacity |
| `flash_command` | `fastboot flash userdata <image>` |
| `source` | Local or remote IPSW |
| `hardware_tested` | Qualification state |
## Next steps
- [Userdata image](/guide/userdata) describes the build and flash commands.
- [Device configuration](/reference/configuration) lists the `[userdata]` keys.
- [Boot chain](/concepts/boot-chain) describes how the volume is mounted.
+132
View File
@@ -0,0 +1,132 @@
# Documentation site
The documentation is a VitePress site in `docs/`. It is published to `https://unixnu.com`.
## Requirements
| Tool | Version |
| --- | --- |
| Node.js | 22 or newer |
| npm | 10 or newer |
## Local development
```sh
cd docs
npm install
npm run dev
```
The site runs at the URL printed by VitePress. `npm run build` writes the static site to `docs/.vitepress/dist`. `npm run preview` serves the built site.
## Structure
```
docs/
.vitepress/
config.mts site configuration
theme/ custom CSS
public/
CNAME custom domain
logo-light.svg
logo-dark.svg
guide/ getting started and build
concepts/ boot chain, payload, prelink, userdata, security
porting/ device porting
drivers/ UEFI, XNU and kext drivers
xnu/ source, patches, pages, boot arguments
devices/ device pages and examples
reference/ command line, configuration, outputs, QEMU, glossary
contributing/ this section
zh/ Simplified Chinese locale
```
## Add a page
1. Create the Markdown file under the matching section.
2. Add the page to the sidebar in `docs/.vitepress/config.mts`.
3. Add the Chinese page under `docs/zh/` and to the Chinese sidebar.
4. Run `npm run build` and fix broken links.
5. Add the language switcher entry when a new section starts.
## Language editions
The site has two locales:
| Locale | Path | Language |
| --- | --- | --- |
| Root | `/` | English |
| `zh` | `/zh/` | Simplified Chinese |
Both editions carry the same pages. A change to one page updates both editions in the same pull request.
## Writing rules
- Use standard technical terms.
- State the current behavior directly.
- Avoid em-dashes and en-dashes.
- Avoid metaphors.
- Use neutral headings.
- Keep the page focused on one subject.
- Use tables for references and lists for steps.
- Put device-specific values in the device page.
- Put a concrete command in every procedure.
## Deploy
The workflow `.github/workflows/docs.yml` builds the site on every push to `main` that touches `docs/` and deploys it with GitHub Pages.
The site needs:
1. GitHub Pages enabled with `GitHub Actions` as the source.
2. The custom domain `unixnu.com` in the repository Pages settings.
3. DNS records for the domain.
`docs/public/CNAME` contains:
```
unixnu.com
```
DNS for an apex domain:
| Type | Name | Value |
| --- | --- | --- |
| A | `@` | `185.199.108.153` |
| A | `@` | `185.199.109.153` |
| A | `@` | `185.199.110.153` |
| A | `@` | `185.199.111.153` |
| CNAME | `www` | `unixnu.github.io` |
GitHub Pages also serves the site over IPv6:
| Type | Name | Value |
| --- | --- | --- |
| AAAA | `@` | `2606:50c0:8000::153` |
| AAAA | `@` | `2606:50c0:8001::153` |
| AAAA | `@` | `2606:50c0:8002::153` |
| AAAA | `@` | `2606:50c0:8003::153` |
Enable `Enforce HTTPS` after the certificate is issued.
## Badge
The README uses:
```markdown
[![Documentation](https://img.shields.io/badge/docs-unixnu.com-2A5BD7?logo=readthedocs&logoColor=white)](https://unixnu.com)
```
## Link check
```sh
cd docs
npm run build
```
VitePress fails the build on a broken internal link.
## Next steps
- [Repository layout](/contributing/repository)
- [Overview](/guide/overview)
+85
View File
@@ -0,0 +1,85 @@
# Repository layout
The base repository and every device repository share the same layout.
```
UniXNU/
unixnu entry point
scripts/unixnu/ build driver
package/ UniXNU EDK II package
drivers/ built-in XNU drivers
kits/ kernel libraries
patches/ XNU, libpthread and Mesa patches
scripts/ build and verification scripts
docs/ this site
assets/ logos
feeds.toml pinned external sources
examples/device/ sample device repository
```
## Module kinds
| Kind | Directory | Contents |
| --- | --- | --- |
| driver | `drivers/<Name>` | XNU driver source and reference plist |
| kit | `kits/<Name>` | Kernel library |
| patch | `patches/<Name>` | Patch set |
| script | `scripts/` | Build and verification scripts |
| package | `package/` | UniXNU package |
| refer | fetched tree | Apple source or edk2 |
A device repository can supply any kind. Its module overrides the base module with the same name.
## Resolution order
1. `--local-module [KIND:]NAME=PATH`
2. `--local-<kind> DIR`
3. `--local DIR`
4. a `local` key in `feeds.toml` or `local.toml`
5. the device repository
6. the base repository
7. a git source in `feeds.toml`
## Build tree
`unixnu tree` writes `out/<board>/tree`:
```
tree/
drivers/ kits/ patches/ refer/
scripts/ package/
build/ .cache/
```
Symbolic links point at the selected modules. Scripts are copied. The device `package/` appears as `kits/<board.kit>`.
## Scripts
The build scripts locate the tree from their own path. A script in `scripts/` can read `ROOT`, `build/`, `.cache/`, `refer/`, `drivers/`, `kits/`, `patches/` and `package/`.
## Add a module
1. Create the directory under the matching kind.
2. Add the build or verification logic.
3. Reference it from the board file or the kernel script.
4. Document the module in the matching section.
## Commit scope
A commit changes one subject:
| Prefix | Subject |
| --- | --- |
| `feat(<area>)` | New capability |
| `fix(<area>)` | Defect repair |
| `docs` | Documentation |
| `build(<area>)` | Build system |
| `chore` | Cleanup |
The commit message describes the change. The documentation describes the current state.
## Next steps
- [Device repository](/porting/device-repository)
- [Documentation site](/contributing/documentation)
- [Build](/building)
+37
View File
@@ -0,0 +1,37 @@
# Supported devices
A device entry is a pair of repositories and the values that make the generic stack run on that board. The table records the ports with a public repository. Boards with a similar SoC need their own port and their own entry.
| Device | SoC | Device repository | Firmware |
| --- | --- | --- | --- |
| Xiaomi Pad 6S Pro (sheng) | SM8550 | [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng) | [uefi-sheng](https://github.com/UniXNU/uefi-sheng) |
## What a device page records
| Item | Example |
| --- | --- |
| Board identity | Product name, SoC, codename |
| IPSW | Product, version, build, device class, chip and board ID |
| Partition geometry | `first_lba`, `last_lba`, block size |
| Kernel prelink | Slide, roots, early security initializer, kernelcache pin |
| Firmware | Mu-Silicium config, DSC, firmware volume path, defines |
| Payload | Volume label, size, DRAM size, kernel arguments |
| ADT | Generator script, identity, CPU registers, PSCI method |
| Device tree additions | Reserved memory nodes for the stock bootloader |
| Build commands | The exact driver commands |
| Boot commands | The exact fastboot commands |
| Observed state | Which stages have run on the device |
## Adding a device
1. Follow [Porting](/porting/).
2. Create the device repository and the UEFI repository.
3. Build and bring up in stages.
4. Add a page under `docs/devices/` with the values and commands.
5. Add the device to the table above.
Device pages are the place for board specific values. The general guide uses placeholders and links here.
## sheng
The [sheng page](/devices/sheng) records the Xiaomi Pad 6S Pro port. It is one device entry. The [touch driver example](/devices/sheng-touch-driver) uses the sheng touch driver as a worked example of a built-in XNU driver.
+145
View File
@@ -0,0 +1,145 @@
# Touch driver example
The sheng device repository contains a complete touch driver for the Novatek NT36532E controller. It shows the structure a new touch driver can follow.
The source lives in [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng):
| Path | Contents |
| --- | --- |
| `drivers/NativeNovatekTouch/NvtTouchCore.c` | Pure touch processing |
| `drivers/NativeNovatekTouch/NvtProtocol.c` | Controller protocol |
| `drivers/NativeNovatekTouch/NvtProtocol.h` | Protocol constants and bus interface |
| `drivers/NativeGeniSpi/GeniSpi.c` | GENI SPI engine |
| `package/TouchBoard.h` | Board constants and clock helpers |
| `package/TouchProbe.cpp` | Kernel probe and frame loop |
| `package/TouchBoardTests.c` | Host tests for the board helpers |
| `drivers/NativeHidInput/NativeHidInput.cpp` | HID digitizer source |
## Two layers
The driver separates pure logic from hardware access.
```
NvtTouchCore.c matrix to contacts, no allocation, no I/O
NvtProtocol.c chip identification, firmware, frames, through nvt_bus
TouchProbe.cpp MMIO, clocks, pins, DMA, SMMU, threads
NativeHidInput.cpp contacts to HID reports
```
The pure core and the protocol take a `struct nvt_bus`:
```c
struct nvt_bus {
void *ctx;
int (*xfer)(void *ctx, const uint8_t *tx, uint8_t *rx, size_t len);
void (*delay)(void *ctx, unsigned ms);
};
```
The host tests supply a fake bus. The kernel probe supplies the GENI SPI transfer.
## Touch processing
`NvtTouchCore.c` processes a 40 by 60 matrix of 16-bit values:
1. Collect 72 startup frames.
2. Form a per-node reference from the median of the startup frames.
3. Subtract the reference to get a delta matrix.
4. Label nodes above the noise floor by steepest ascent.
5. Find local maxima and form blobs.
6. Reject blobs above the palm node count.
7. Compute a weighted centroid per blob.
8. Apply edge compensation.
9. Track contacts across frames with a radius and a down/up threshold.
10. Report contacts from the second frame of a slot.
The core has no allocation. Its scratch arrays live in `struct nvt_touch_core`, which the probe allocates once. The comments record why: kernel thread stacks are 16 KiB and the scratch is larger.
## Controller protocol
`NvtProtocol.c` provides:
| Function | Purpose |
| --- | --- |
| `nvt_identify` | Read the chip id and select the map |
| `nvt_fw_parse` | Parse the firmware image into ILM and DLM parts |
| `nvt_download_firmware` | Reset the bootloader, write the parts, verify the SRAM |
| `nvt_start_scanning` | Read the firmware version and matrix geometry |
| `nvt_peek_frame_header` | Read the 32-byte THP header |
| `nvt_read_frame` | Read a complete THP frame |
| `nvt_frame_header_valid` | Reject a torn frame |
| `nvt_frame_matrix` | Decode the matrix |
The transfer length, read maximum and frame length are constants from the vendor protocol.
## Board constants
`TouchBoard.h` holds every address and bit with a source comment:
| Group | Examples |
| --- | --- |
| QUP wrapper and serial engine | Base addresses and sizes |
| SMMU | Stream ID and apps SMMU base |
| GCC | Vote registers, halt registers, DFS levels |
| TLMM | GPIO numbers, function, drive strength |
| SPI | Mode and maximum frequency |
The clock helpers are pure functions. `tb_choose_clock` selects the DFS level and divider that give the highest rate not above the target. The host tests cover the divider arithmetic.
## Probe stages
The probe is off by default. The boot argument `sheng-touch=<n>` selects a stage:
| Stage | Work |
| --- | --- |
| 1 | Read GCC branches, DFS levels, pins and the serial engine identity |
| 2 | Vote and wait for the QUP clocks, configure the pins |
| 3 | Initialize GENI SPI, select the clock, identify the chip |
| 4 | Download the firmware, start scanning, process frames |
Each stage reports on the diagnostic page. The first failure stops the sequence.
## Hardware path
1. Vote for GPLL0 and the QUP branches in GCC.
2. Wait for the branch clocks to leave the off state.
3. Configure the SPI pins in TLMM.
4. Map the QUP wrapper and the serial engine.
5. Initialize GENI SPI.
6. Select the DFS level and divider.
7. Allocate a physically contiguous DMA buffer.
8. Map the buffer through the apps SMMU with a bypass stream.
9. Maintain the cache around each transfer.
10. Reset the touch bootloader and download the firmware.
11. Poll the THP header, read complete frames and publish contacts.
The SMMU mapping follows the same bypass model as the UFS driver. The DMA buffer is cached, and the driver performs explicit clean and invalidate operations around each transfer.
## HID publication
The probe publishes contacts through globals:
```c
volatile uint32_t native_touch_seq;
volatile uint32_t native_touch_count;
volatile int native_touch_xs[NVT_SLOTS];
volatile int native_touch_ys[NVT_SLOTS];
volatile int native_touch_ids[NVT_SLOTS];
```
The writer increments the sequence before and after the update, so the reader detects a torn snapshot. `NativeHidInput` reads the snapshot and emits digitizer reports.
## Adapting the example
1. Keep the pure processing layer separate from the bus.
2. Put every board constant in a header with its source.
3. Write host tests for the pure helpers before the hardware probe.
4. Add a staged boot argument so each hardware step is observable.
5. Publish contacts through a sequence counter and a fixed size snapshot.
6. Keep the driver off by default until a boot log shows each stage.
## Next steps
- [XNU drivers](/drivers/xnu-drivers)
- [Device tree matching](/drivers/device-tree-matching)
- [sheng device page](/devices/sheng)
+282
View File
@@ -0,0 +1,282 @@
# Xiaomi Pad 6S Pro (sheng)
sheng is the Xiaomi Pad 6S Pro with the SM8550 SoC. It is one device port among the devices listed in [Supported devices](/devices/).
| Repository | Contents |
| --- | --- |
| [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng) | Board file, defconfig, device scripts, touch driver and package |
| [uefi-sheng](https://github.com/UniXNU/uefi-sheng) | Mu-Silicium UEFI port |
## Board
```toml
[board]
name = "sheng"
soc = "SM8550"
description = "Xiaomi Pad 6S Pro"
kit = "sheng"
```
The output directory is `out/sheng/`.
## IPSW
```toml
[ipsw]
url = "https://updates.cdn-apple.com/2025SpringFCS/fullrestores/122-77166/DCA6CFE8-90E9-4B92-880D-3DE171798E40/iPad_Pro_HFR_17.7.11_21H461_Restore.ipsw"
product = "iPad7,1"
version = "17.7.11"
build = "21H461"
device_class = "j120ap"
chip_id = 0x8011
board_id = 12
fetch = ["KernelCache", "DeviceTree", "StaticTrustCache", "Cryptex1,SystemTrustCache"]
```
The board uses the unchanged iPad7,1 iPadOS 17.7.11 restore image. The member pins:
```toml
[ipsw.sha256]
"044-76040-038.dmg" = "3f99b859c9728379110b66657c7b15285bcb1925ad8b5726542e2cdcf8b1028d"
"044-76034-038.dmg" = "b4908cde41d23e6bc05e6af51a2c9eeb00cf0f863a6a18b1600c96a3fd70414f"
```
## Userdata
```toml
[userdata]
first_lba = 0x2f3840
last_lba = 0x3d0abfa
lba_bytes = 4096
windows = [
{ name = "OS", component = "OS" },
{ name = "SystemOS", component = "Cryptex1,SystemOS" },
]
```
The LBAs come from the GPT entry named `userdata` on the device.
## Kernel
```toml
[kernel]
defconfig = "sheng_defconfig"
[kernel.prelink]
tool = "kits/NativePrelink/emit_prelink.py"
kernel = "build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf"
kernelcache_sha256 = "f18b4df865405f2a287b84977d7aca745e056d656d9fc9df1662970ce2969de7"
slide = "-0x1000000"
early_security_init = "com.apple.driver.AppleMobileFileIntegrity=0xfffffff005ab4ae0"
```
The prelink roots:
```
com.apple.AppleFSCompression.AppleFSCompressionTypeZlib
com.apple.driver.AppleARMPlatform
com.apple.driver.AppleARMWatchdogTimer
com.apple.driver.AppleAudioClockLibs
com.apple.driver.AppleEffaceableStorage
com.apple.driver.AppleIISController
com.apple.driver.AppleM2ScalerCSCDriver
com.apple.driver.AppleMobileFileIntegrity
com.apple.driver.ApplePMGR
com.apple.driver.IODARTFamily
com.apple.filesystems.apfs
com.apple.iokit.CoreAnalyticsFamily
com.apple.iokit.IOAVFamily
com.apple.iokit.IOAudio2Family
com.apple.iokit.IOCECFamily
com.apple.iokit.IOCryptoAcceleratorFamily
com.apple.iokit.IOGPUFamily
com.apple.iokit.IOHDCPFamily
com.apple.iokit.IOMobileGraphicsFamily
com.apple.iokit.IOReportFamily
com.apple.iokit.IOSlowAdaptiveClockingFamily
com.apple.iokit.IOStorageFamily
com.apple.iokit.IOSurface
com.apple.kec.Libm
com.apple.kec.corecrypto
com.apple.kext.AppleMatch
com.apple.kext.CoreTrust
com.apple.security.AppleImage4
com.apple.security.sandbox
com.apple.iokit.IOHIDFamily
com.apple.iokit.IOHIDEventDriver
```
## Defconfig
`sheng_defconfig` enables:
```
CONFIG_XNU_SOURCE_CORECRYPTO14=y
CONFIG_XNU_SOURCE_PTHREAD=y
CONFIG_XNU_ARM_DT_PLATFORM=y
CONFIG_XNU_ARMCPU=y
CONFIG_XNU_NATIVE_UFS=y
CONFIG_XNU_NATIVE_AES=y
CONFIG_XNU_NATIVE_VIRTIO_BLOCK=y
CONFIG_XNU_NATIVE_RAM_BLOCK=y
CONFIG_XNU_TRUST_CACHE_RUNTIME=y
CONFIG_XNU_SOURCE_MOCKFS=y
CONFIG_XNU_NATIVE_CS_HASH_CACHE=y
CONFIG_XNU_VERIFIED_ROOT_PROBE=y
CONFIG_XNU_OWNER_ROOT_AUDIT=y
CONFIG_XNU_OWNER_ROOT_TRUST=y
CONFIG_XNU_SHENG_PMGR=y
CONFIG_XNU_OWNER_CACHE_MOUNT=y
CONFIG_XNU_OWNER_CACHE_STARTUP=y
CONFIG_XNU_OWNER_BOOT_MANIFEST=y
CONFIG_XNU_OWNER_DATA_MOUNT=y
CONFIG_XNU_OWNER_UNION_SYSTEMOS=y
CONFIG_XNU_DT_PANIC_REGION=y
CONFIG_XNU_ZONE_PGZ_ALIGNMENT_PROBE=y
CONFIG_XNU_NATIVE_ARCH_TIMER=y
CONFIG_XNU_NATIVE_PAN=y
CONFIG_XNU_BOOT_PROGRESS=y
CONFIG_XNU_NATIVE_LEGACY_DISPLAY=y
CONFIG_XNU_NATIVE_ADRENO=y
CONFIG_XNU_CRYPTO_BOOTSTRAP="prelinked"
```
The board sets the scratch Data volume UUID:
```
CONFIG_XNU_OWNER_DATA_VOLUME_UUID="18AB5557-DC2D-469B-8758-855E60F5B0DF"
```
## Firmware
```toml
[firmware]
config = "sheng"
dsc = "Platforms/Xiaomi/shengPkg/sheng.dsc"
fd = "Build/shengPkg/{target}_CLANGPDB/FV/SILICIUM_UEFI.fd"
preflight = ["scripts/check-sheng.py"]
submodules = ["Common/Mu", "Common/Mu_OEM_Sample", "Silicium-ACPI", "Silicon/Silicium/OpensslPkg/Library/OpensslLib/openssl"]
patches = ["Resources/MuPatches/Gic-Spurious.patch"]
```
The package path list covers the Mu-Silicium tree, the UniXNU package, DarwinBoot, edk2, Mu, Mu OEM samples, Qualcomm, Silicium and ACPI.
The defines:
```toml
[firmware.defines]
ENABLE_SECUREBOOT = "1"
DEVICE_MODEL = "sheng"
MEMORY_PROTECTION = "TRUE"
SHIP_MODE = "FALSE"
BUILDID_STRING = "sheng-research"
```
## Payload
```toml
[payload]
volume = "SHENGESP"
size_mib = 192
loader = "firmware"
title = "sheng first boot: source 4K XNU to console"
dram_size = "0x80000000"
skip_setup_tool = "scripts/seed-data-skip-setup.py"
panic_screen = true
trust_cache_sha256 = [
"7416ca4f8377146626bb284e2133c76bee6cb4e3ee0af5d648b186235a7323a7",
"8900d6a93d7a59ce1a872a5c081e559f4240a218f97e12d44d22dda46644d68f",
]
```
The kernel arguments cover the platform, storage, display, input, GPU, diagnostics and owner root paths. The full list is in the board file.
The ADT:
```toml
[payload.adt]
tool = "scripts/make-sheng-adt.py"
chip_id = "207"
owned_memory_size = "0x130000000"
root_match_volume_uuid = "04769D26-2CA0-4825-B443-E2E5F88300F2"
cpu_regs = "0,0x100,0x200,0x300,0x400,0x500,0x600,0x700"
packed_data_ramdisk = true
ipad_identity = true
unique_chip_id = "required"
```
The boot image adds a splash region that the stock ABL expects:
```toml
[[bootimg.dtb_node]]
path = "/reserved-memory/splash_region"
props = [
{ name = "label", type = "s", value = "cont_splash_region" },
{ name = "reg", type = "x", value = ["0x0", "0xb8000000", "0x0", "0x2b00000"] },
]
```
## Build
```sh
git clone https://github.com/UniXNU/UniXNU
git clone https://github.com/UniXNU/UniXNU-sheng
git clone https://github.com/UniXNU/uefi-sheng
cd UniXNU
./unixnu defconfig sheng_defconfig -D ../UniXNU-sheng
./unixnu fetch
./unixnu ipsw fetch
./unixnu build kernel
./unixnu build package
./unixnu build firmware --uefi ../uefi-sheng
./unixnu build payload --unique-chip-id <hex> --trust-cache <bundle> --data-ramdisk <seed>
./unixnu bootimg --uefi ../uefi-sheng --dtb <sheng.dtb>
```
The platform seed, trust cache and Data seed are device inputs. The board reports the accepted trust cache hashes.
## Boot
```sh
fastboot boot out/sheng/boot/boot.img
```
The first boot uses the userdata image:
```sh
./unixnu build userdata
fastboot flash userdata out/sheng/userdata/userdata-root.img
fastboot reboot bootloader
fastboot boot out/sheng/boot/boot.img
```
## macOS
The board file points at an iPadOS restore image. To boot macOS on the same board, point `[ipsw]` at a macOS restore image and update the product, version, build, device class, chip ID, board ID and member pins. Set `[userdata] windows` to `OS` and `Cryptex1,SystemOS`. The kernel, payload, boot image and userdata steps stay the same.
## Observed state
| Stage | State |
| --- | --- |
| Stock ABL loads the boot image and BootShim starts the UEFI firmware | works |
| Silicium UEFI and UniXNU start | works; Silicium stalls at random and a retry of the same image starts it |
| UniXNU reads the payload volume | works |
| XNU and userland | reached with images from the earlier workflow |
| Touch driver | staged probe with the NT36532E controller |
The touch driver is documented in [Touch driver example](/devices/sheng-touch-driver).
## Device specifics
- The stock ABL powers the panel off when the device tree has no splash region. The boot image adds `/reserved-memory/splash_region`.
- The platform seed lives outside the repositories. Supply it with `--unique-chip-id`.
- The `sheng-pmgr=2` argument is required for the native UFS root path. The payload step refuses a kernel without the linked PMGR integration when `native-ufs-root=1`.
- The trust cache bundle must be one of the two accepted hashes.
- The Data seed is an input to the payload step.
## Next steps
- [Porting](/porting/)
- [Touch driver example](/devices/sheng-touch-driver)
- [Troubleshooting](/reference/troubleshooting)
+165
View File
@@ -0,0 +1,165 @@
# Device tree matching
A driver binds to a provider through an IOKit personality. The personality is a dictionary of match keys.
## Personality keys
| Key | Meaning |
| --- | --- |
| `IOClass` | Driver class |
| `IOProviderClass` | Provider class |
| `IONameMatch` | Provider name or `compatible` value |
| `IOPropertyMatch` | Dictionary of exact provider property values |
| `IOProbeScore` | Higher scores probe first |
| `IOMatchCategory` | Match category, which allows a second driver on the same provider |
| `IOResourceMatch` | Resource that must be published before probing |
| `IOProviderMergeProperties` | Properties added to the provider |
A personality without `IONameMatch` or `IOPropertyMatch` matches every provider of the named class. `IOProbeScore` decides which candidate wins.
## Provider classes
| Provider | Published by | Use |
| --- | --- | --- |
| `IOPlatformExpertDevice` | Platform expert | Platform identity, product and buttons |
| `IOPlatformDevice` | `ArmDTPlatform` for every `arm-io` child | SoC devices |
| `IOResources` | IOKit | Global resources such as AES or RAM block |
| `IOMedia` | Partition and file system stack | Root volume matching |
| `IOBlockStorageDevice` | Storage driver | Block device clients |
`ArmDTPlatform` attaches a nub to the platform expert for each `arm-io` child. The nub keeps the device tree parent, so `reg` and `ranges` resolve through `arm-io`.
## Matching examples
### Platform device by compatible
```c
" ,{"
" 'IOClass' = NativeUfs;"
" 'IOProviderClass' = IOPlatformDevice;"
" 'IONameMatch' = 'qcom,sm8550-ufshc';"
" 'IOProbeScore' = 1000:32;"
" }"
```
`IONameMatch` compares against the provider name and the `compatible` string list.
### Global resource
```c
" ,{"
" 'IOClass' = NativeAES;"
" 'IOProviderClass' = IOResources;"
" 'IOResourceMatch' = IOBSD;"
" 'IOProbeScore' = 1000:32;"
" }"
```
`IOResourceMatch = IOBSD` waits until the BSD subsystem publishes its resource.
### Root volume by UUID
```xml
<key>IOProviderClass</key><string>IOMedia</string>
<key>IOPropertyMatch</key>
<dict>
<key>UUID</key><string>00000000-0000-0000-0000-000000000000</string>
</dict>
```
This dictionary is stored in `/chosen/root-matching`.
## Device tree nodes
### Device node
```c
ufshc {
name = "ufshc";
compatible = "qcom,sm8550-ufshc";
reg = <0x03ff0000 0x3000>;
interrupts = <0 0x100 4>;
status = "okay";
}
```
The platform expert publishes a nub with these properties. The driver reads `reg` and `interrupts` from the provider.
### Bus node
```c
arm-io {
name = "arm-io";
device_type = "arm-io";
#address-cells = <2>;
#size-cells = <2>;
ranges = <0 0x17000000 0x08000000>;
intc {
name = "intc";
compatible = "arm,gic-v3";
reg = <0x0 0x10000 0x0 0x10000>;
};
}
```
A child `reg` is an offset inside the `ranges` window. The platform expert resolves the physical address.
## Reading properties
```cpp
OSData *data = OSDynamicCast(OSData, provider->getProperty("reg"));
if (data == nullptr) {
return false;
}
const uint32_t *cells = (const uint32_t *)data->getBytesNoCopy();
uint32_t count = data->getLength() / sizeof(uint32_t);
```
The number of cells comes from the parent node. A node under `#address-cells = <2>` and `#size-cells = <2>` uses four cells per range.
A string property:
```cpp
OSData *compatible = OSDynamicCast(OSData, provider->getProperty("compatible"));
const char *value = (const char *)compatible->getBytesNoCopy();
```
The string list is a sequence of NUL-terminated strings.
## Overlays and patches
A kit can apply a device tree overlay:
```toml
[[overlay]]
file = "board.dtbo"
```
A configuration can patch a property:
```toml
[[dt_patch]]
op = "set"
node = "/arm-io/intc"
prop = "status"
value = "disabled"
```
Patches apply to EFI and Linux entries. The Darwin path reads the `.adt` sidecar.
## Probe order
1. The platform expert publishes the provider.
2. IOKit collects personalities whose `IOProviderClass` matches.
3. Personalities whose `IONameMatch` or `IOPropertyMatch` fails are skipped.
4. `IOResourceMatch` providers must be published.
5. The remaining candidates are ordered by `IOProbeScore`.
6. Each candidate runs `probe()`.
7. The highest scoring candidate that starts owns the provider.
## Next steps
- [XNU drivers](/drivers/xnu-drivers)
- [Device tree](/porting/device-tree)
- [Kext bundles](/drivers/kexts)
+156
View File
@@ -0,0 +1,156 @@
# Kext bundles
A kext bundle is a standard macOS or iOS kernel extension directory. UniXNU stages the bundle files and passes them to an XNU loader through `UNIXNU_LOADER_PROTOCOL`.
## Bundle layout
```
MyDriver.kext/
Contents/
Info.plist
MacOS/
MyDriver
```
The bundle name ends with `.kext`. The plist path defaults to `Contents/Info.plist`. The executable path defaults to the value in `CFBundleExecutable`, relative to the bundle.
A codeless kext has a plist and no executable. It can declare dependencies or personalities that reference another binary.
## Info.plist
```xml
<dict>
<key>CFBundleIdentifier</key><string>org.example.MyDriver</string>
<key>CFBundleName</key><string>MyDriver</string>
<key>CFBundleExecutable</key><string>MyDriver</string>
<key>CFBundlePackageType</key><string>KEXT</string>
<key>CFBundleVersion</key><string>1.0.0</string>
<key>OSBundleRequired</key><string>Root</string>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.kpi.iokit</key><string>23.6.0</string>
</dict>
<key>IOKitPersonalities</key>
<dict>
<key>MyDevice</key>
<dict>
<key>IOClass</key><string>MyDriver</string>
<key>IOProviderClass</key><string>IOPlatformDevice</string>
<key>IONameMatch</key><string>my-device</string>
<key>IOProbeScore</key><integer>1000</integer>
</dict>
</dict>
</dict>
```
## Register with UniXNU
An entry scoped kext:
```toml
[[entry]]
id = "xnu"
type = "xnu"
path = "Payloads/kernel.macho"
[[entry.kext]]
bundle = "Kexts/MyDriver.kext"
```
A kit scoped kext names the entry:
```toml
[[kext]]
bundle = "MyDriver.kext"
entry = "xnu"
plist = "Contents/Info.plist"
executable = "Contents/MacOS/MyDriver"
enabled = true
```
| Key | Meaning |
| --- | --- |
| `bundle` | Bundle directory, relative to the configuration or kit |
| `plist` | Plist path inside the bundle |
| `executable` | Executable path inside the bundle. Empty for a codeless kext |
| `entry` | Entry id. Required for kit kexts |
| `enabled` | `false` skips the bundle |
Entry kexts and kit kexts are staged in configuration order, with kit kexts first, then entry kexts. A duplicate bundle path is refused.
## Validation by UniXNU
UniXNU validates the bundle before staging:
- The bundle name ends with `.kext`.
- Every path is relative and contains no `.` or `..` segment.
- The plist is not empty.
- The executable, when present, is a thin AArch64 `MH_KEXT_BUNDLE`:
- Magic `0xfeedfacf`.
- CPU type `0x0100000c`.
- File type `11`.
- Subtype `0` or `2` (arm64 or arm64e).
- Load commands fit inside the file.
- A kit kext names an entry.
- An entry kext does not name an entry.
The staged data is the unmodified plist and executable. The loader validates signatures, resolves dependencies and injects the code.
## Loader protocol
The loader receives:
```c
typedef struct {
CONST CHAR16 *BundlePath;
CONST CHAR16 *PlistPath;
CONST CHAR16 *ExecutablePath;
CONST VOID *InfoPlist;
UINTN InfoPlistSize;
CONST VOID *Executable;
UINTN ExecutableSize;
} UNIXNU_LOADER_KEXT;
```
The buffers belong to UniXNU and stay valid for the duration of `Boot()`.
A loader that accepts kexts implements protocol revision 2. The shipped DarwinBoot accepts kernels, the ADT sidecar, the trust cache and the optional ramdisk, and refuses a request with staged kexts.
## Build a kext
Build the bundle with Xcode against the iOS kernel SDK:
```sh
xcodebuild -target MyDriver -sdk iphoneos -configuration Release \
ARCHS=arm64 ONLY_ACTIVE_ARCH=YES
```
The target uses `MODULE_TYPE = KEXT` and links the kernel frameworks:
| Setting | Value |
| --- | --- |
| `MACH_O_TYPE` | `mh_kext_bundle` |
| `PRODUCT_TYPE` | `com.apple.product-type.kernel-extension` |
| `SDKROOT` | `iphoneos` |
| `ARCHS` | `arm64` or `arm64e` |
| `KERNEL_FRAMEWORKS` | `Kernel` |
The executable must match the kernel ABI of `xnu-10063.141.1` and the iOS 17 deployment target.
## Signing and trust
AMFI validates the kext against the trust cache and the code signature. A kext that is not trusted fails to load with a code signing error.
The trust cache bundle passed to the payload must include the kext CD hash. The payload step accepts the bundle only when its hash is listed in `[payload] trust_cache_sha256`.
## Order and dependencies
UniXNU stages bundles in configuration order. The loader resolves `OSBundleLibraries` and starts the kexts in dependency order.
A codeless kext can sit in the list to express a dependency or to attach personalities to an existing binary.
## Next steps
- [XNU drivers](/drivers/xnu-drivers)
- [Integrity and boot policy](/concepts/security)
- [Boot configuration](/reference/boot-configuration)
+68
View File
@@ -0,0 +1,68 @@
# Driver layers
UniXNU has three driver layers. Each layer runs in a different environment and has a different build and load path.
| Layer | Runs in | Built as | Loaded by |
| --- | --- | --- | --- |
| UEFI driver | UEFI boot services | EDK II `.efi` | Firmware dispatch, UniXNU `[[driver]]`, or a kit |
| Built-in XNU driver | XNU kernel | Static archive linked into the kernel | The kernel at boot |
| Kext bundle | XNU kernel, staged by UEFI | `.kext` bundle | An XNU loader that implements the protocol |
## Choosing a layer
| Need | Layer |
| --- | --- |
| Display, input or storage available before XNU | UEFI driver |
| Platform expert, CPU, interrupt controller, storage, display or GPU inside XNU | Built-in XNU driver |
| A bundle that an external loader validates and injects at runtime | Kext bundle |
A UEFI driver can provide the framebuffer and input for the UniXNU menu. It can also publish a protocol that another UEFI driver consumes.
A built-in XNU driver has full kernel API access. It is compiled against the exact XNU build ABI and force-loaded into the kernel.
A kext bundle keeps the standard bundle layout. UniXNU stages the plist and the executable and passes them to the loader. The loader owns validation, dependency resolution and injection.
## Matching
UEFI drivers bind through the UEFI driver model or run as applications.
XNU drivers bind through `IOKitPersonalities`. A personality names the provider class, the driver class, an optional name match and a probe score.
| Key | Meaning |
| --- | --- |
| `IOClass` | Driver class |
| `IOProviderClass` | Provider class |
| `IONameMatch` | Provider name or `compatible` value |
| `IOProbeScore` | Higher scores probe first |
| `IOMatchCategory` | Allows a second driver to match the same provider |
| `IOResourceMatch` | Waits for a named resource, for example `IOBSD` |
Built-in personalities live in `iokit/KernelConfigTables.cpp`. Kext personalities live in the bundle `Info.plist`.
## Device tree
The ADT describes the hardware. The platform expert publishes a nub for each `arm-io` child. A driver reads its resources from the provider:
```cpp
OSData *reg = OSDynamicCast(OSData, provider->getProperty("reg"));
```
The `compatible` property selects a driver. `IONameMatch` compares against the provider name.
## Driver directories
| Directory | Contents |
| --- | --- |
| `drivers/<Name>` | Built-in XNU driver source and reference `Info.plist` |
| `kits/<Name>` | Kernel library linked into a driver or the kernel |
| `package/UniXNUPkg/Driver` | UEFI drivers |
| `package/UniXNUPkg/Test` | UEFI test drivers and payloads |
A device repository can override any of these directories with its own version.
## Next steps
- [UEFI drivers](/drivers/uefi-drivers)
- [XNU drivers](/drivers/xnu-drivers)
- [Kext bundles](/drivers/kexts)
- [Device tree matching](/drivers/device-tree-matching)
+205
View File
@@ -0,0 +1,205 @@
# UEFI drivers
A UEFI driver is an EDK II module built as a `.efi` image. It runs in boot services and can publish protocols, bind devices and prepare hardware for the next stage.
## Module layout
```
MyDriver/
MyDriver.c
MyDriver.inf
MyProtocol.h
```
`MyDriver.inf`:
```ini
[Defines]
INF_VERSION = 0x00010019
BASE_NAME = MyDriver
FILE_GUID = 00000000-0000-0000-0000-000000000000
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 0.1
ENTRY_POINT = MyDriverEntry
[Sources]
MyDriver.c
[Packages]
MdePkg/MdePkg.dec
UniXNUPkg/UniXNUPkg.dec
[LibraryClasses]
MemoryAllocationLib
UefiBootServicesTableLib
UefiDriverEntryPoint
[Protocols]
gMyProtocolGuid ## PRODUCES
gEfiLoadedImageProtocolGuid ## CONSUMES
```
`MyDriver.c`:
```c
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/MyProtocol.h>
STATIC MY_PROTOCOL mProtocol = { MY_SIGNATURE, NULL };
EFI_STATUS
EFIAPI
MyDriverEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_LOADED_IMAGE_PROTOCOL *Loaded;
EFI_HANDLE Handle = NULL;
if (!EFI_ERROR (gBS->HandleProtocol (
ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&Loaded)) &&
(Loaded->LoadOptions != NULL))
{
mProtocol.Options = AllocateCopyPool (
Loaded->LoadOptionsSize, Loaded->LoadOptions);
}
return gBS->InstallMultipleProtocolInterfaces (
&Handle, &gMyProtocolGuid, &mProtocol, NULL);
}
```
The driver receives its `LoadOptions` from the UniXNU `args` field.
## Build
Add the INF to a DSC:
```
[Components]
MyPkg/MyDriver/MyDriver.inf
```
Build with EDK II:
```sh
build -p MyPkg/MyPkg.dsc -a AARCH64 -b DEBUG -t CLANGPDB -n 8
```
The result is `Build/MyPkg/DEBUG_CLANGPDB/AARCH64/MyDriver.efi`.
The UniXNU package DSC contains the standalone build:
```
package/UniXNUPkg/UniXNUPkg.dsc
```
`package/build.sh` copies the EFI binaries to `Build/Out/AARCH64-DEBUG`.
## Load from UniXNU
Copy the driver to the payload volume and add an entry to `unixnu.toml`:
```toml
[[driver]]
path = "Drivers/MyDriver.efi"
args = "board=1"
connect = true
enabled = true
```
| Key | Meaning |
| --- | --- |
| `path` | Path relative to `\EFI\UniXNU`, or `fv:<GUID>` |
| `args` | Load options string |
| `connect` | Run `ConnectController` on every handle after the driver starts |
| `enabled` | `false` skips the entry |
A kit can add the driver:
```toml
[[driver]]
file = "MyDriver.efi"
args = "from-kit"
```
The kit path resolves relative to the kit directory.
## Firmware volume path
`fv:<GUID>` loads a file from any firmware volume by file GUID:
```toml
[[driver]]
path = "fv:00000000-0000-0000-0000-000000000000"
```
This loads a driver already embedded in the firmware without copying it to the payload.
## Firmware volume inclusion
To embed a driver in the firmware:
1. Add the INF to the platform DSC under `[Components]`.
2. Add the INF to the FDF under `[FV.<name>] INF`.
3. Rebuild the firmware.
The firmware dispatcher starts DXE drivers during dispatch. A UEFI application in the FV is started by UniXNUDxe or by a `Boot####` option.
## Driver model
A UEFI driver that follows the driver model implements `EFI_DRIVER_BINDING_PROTOCOL`:
| Function | Role |
| --- | --- |
| `Supported` | Reports whether the driver can handle a controller |
| `Start` | Installs child handles or protocols |
| `Stop` | Removes them |
UniXNU calls `ConnectController` on every handle when `connect = true` and `[boot] connect_all` is true. This binds driver-model drivers to new devices.
## Protocols
A protocol is a GUID and a function table:
```c
typedef struct {
UINT64 Signature;
EFI_STATUS (EFIAPI *DoWork)(IN struct _MY_PROTOCOL *This);
} MY_PROTOCOL;
extern EFI_GUID gMyProtocolGuid;
```
The producer installs it with `InstallMultipleProtocolInterfaces`. A consumer locates it with `LocateProtocol` or `HandleProtocol`.
## Example modules
| Module | Purpose |
| --- | --- |
| `UniXNUPkg/Test/SampleDriver` | Installs `UNIXNU_SAMPLE_PROTOCOL` and records its load options |
| `UniXNUPkg/Test/HelloPayload` | Test application for injection |
| `UniXNUPkg/Driver/UniXNUDxe` | Registers the UniXNU application as a boot option |
| `drivers/DarwinBoot` | Installs `UNIXNU_LOADER_PROTOCOL` |
## Logging
UniXNU logs every driver result:
```
UNIXNU: driver <path> (<origin>): started
UNIXNU: driver <path> (<origin>): load failed: <status>
UNIXNU: driver <path> (<origin>): start failed: <status>
```
The menu shows the loaded and failed counts and the first failure reason.
## Next steps
- [XNU drivers](/drivers/xnu-drivers)
- [Kext bundles](/drivers/kexts)
- [UEFI firmware](/porting/uefi-firmware)
+256
View File
@@ -0,0 +1,256 @@
# XNU drivers
An XNU driver in UniXNU is a C++ IOKit class compiled into the kernel. The kernel links the driver as a static archive and starts it through a personality in `KernelConfigTables.cpp`.
## Source layout
```
drivers/MyDriver/
MyDriver.cpp
MyDriver.h
Info.plist
```
`Info.plist` records the personality for reference. The kernel uses the personality compiled into `iokit/KernelConfigTables.cpp`.
## Driver class
```cpp
#include <IOKit/IOService.h>
#include <IOKit/IOLib.h>
#include <libkern/c++/OSData.h>
class MyDriver : public IOService {
OSDeclareDefaultStructors(MyDriver)
public:
bool start(IOService *provider) override;
void stop(IOService *provider) override;
bool probe(IOService *provider, SInt32 *score) override;
};
OSDefineMetaClassAndStructors(MyDriver, IOService)
bool MyDriver::probe(IOService *provider, SInt32 *score)
{
if (!super::probe(provider, score)) {
return false;
}
*score = 1000;
return true;
}
bool MyDriver::start(IOService *provider)
{
if (!super::start(provider)) {
return false;
}
OSData *reg = OSDynamicCast(OSData, provider->getProperty("reg"));
if (reg == nullptr || reg->getLength() != 4 * sizeof(uint32_t)) {
IOLog("MyDriver: provider has no usable reg property\n");
return false;
}
registerService();
IOLog("MyDriver: started\n");
return true;
}
void MyDriver::stop(IOService *provider)
{
IOLog("MyDriver: stopped\n");
super::stop(provider);
}
```
`start()` gets the provider, reads its properties, allocates resources, installs interrupt handlers and calls `registerService()`.
## Compile against the kernel ABI
The driver must use the same compiler flags as XNU. The kernel build records the exact command for every object in a `.cpo.json` file:
```
build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/iokit/DEVELOPMENT/IOPlatformExpert.cpo.json
```
A build script reads that record, replaces the source path and the output path, adds the driver include paths, and runs the compiler:
```python
import json, subprocess
from pathlib import Path
record = json.loads(Path(
"build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/iokit/DEVELOPMENT/IOPlatformExpert.cpo.json"
).read_text())
command = []
i = 0
while i < len(record["arguments"]):
value = record["arguments"][i]
if value == record["file"]:
command.append("drivers/MyDriver/MyDriver.cpp")
elif value == "-o":
command += ["-o", "build/my-driver/MyDriver.o"]
i += 1
elif value == "-Xclang" and record["arguments"][i + 1].startswith("-W"):
i += 1
elif not value.startswith("-W"):
command.append(value)
i += 1
command += ["-Wall", "-Wextra", "-Werror", "-I", "build/my-driver"]
subprocess.run(command, cwd=record["directory"], check=True)
```
The resulting object must be:
| Field | Value |
| --- | --- |
| Magic | `0xfeedfacf` |
| CPU type | `0x0100000c` (ARM64) |
| File type | `1` (MH_OBJECT) |
| Subtype | `0` |
| Build version | platform `2` (iOS), minimum `0x110000` (17.0) |
The compile record carries the flags that make these values match the kernel.
## Archive
```sh
xcrun ar rcs build/my-driver/libMyDriver.a build/my-driver/MyDriver.o
```
## Link into the kernel
Add the archive to `scripts/build-xnu-4k-baseline.sh`:
```sh
if [ "${XNU_MY_DRIVER:-0}" = 1 ]; then
MY_DRIVER_ARCHIVE=$ROOT/build/my-driver/libMyDriver.a
test -f "$MY_DRIVER_ARCHIVE"
KERNEL_EXTRA_ARCHIVES="$KERNEL_EXTRA_ARCHIVES -Wl,-force_load,$MY_DRIVER_ARCHIVE"
KERNEL_ARCHIVE_DEPENDENCIES="$KERNEL_ARCHIVE_DEPENDENCIES $MY_DRIVER_ARCHIVE"
fi
```
`-force_load` keeps every object, including classes the linker would otherwise drop. Add the archive to `KERNEL_ARCHIVE_DEPENDENCIES` so a changed archive relinks the kernel.
Enable it in the defconfig:
```
CONFIG_XNU_MY_DRIVER=y
```
## Add a personality
Add the match dictionary to `iokit/KernelConfigTables.cpp` through a patch:
```c
#if defined(XNU_MY_DRIVER) && XNU_MY_DRIVER
" ,{"
" 'IOClass' = MyDriver;"
" 'IOProviderClass' = IOPlatformDevice;"
" 'IONameMatch' = 'my-device';"
" 'IOProbeScore' = 1000:32;"
" }"
#endif
```
The build script passes `-DXNU_MY_DRIVER=1` to `KernelConfigTables.cpo`:
```sh
EXTRA_MAKE_ARGS+=("KernelConfigTables.cpo_CFLAGS_ADD=-DXNU_MY_DRIVER=$MY_DRIVER")
```
The personality is a property list string in the kernel's IOKit configuration table. `IOClass` names the C++ class. `IOProviderClass` names the provider class. `IONameMatch` matches the provider name or a `compatible` value. `IOProbeScore` orders multiple candidates.
## Provider properties
The ADT node becomes an `IOPlatformDevice` nub when the platform expert matches it. Common properties:
| Property | Encoding | Use |
| --- | --- | --- |
| `reg` | 32-bit or 64-bit cells | Register window |
| `interrupts` | 32-bit cells | Interrupt numbers |
| `compatible` | string list | Driver match |
| `name` | string | Node name |
| `clock-frequency` | 32-bit | Input clock |
| `power-domains` | phandles | Power dependencies |
`provider->getProperty("reg")` returns an `OSData`. The number of address and size cells comes from the parent node.
## Memory
| API | Use |
| --- | --- |
| `IOMalloc`, `IOFree` | Kernel virtual memory |
| `IOMallocContiguous` | Physically contiguous memory |
| `IOBufferMemoryDescriptor` | DMA buffers with a physical address |
| `IODMACommand` | Map a buffer for a device |
A DMA buffer must be mapped through the device's IOMMU or SMMU context. The `ArmSmmu4K` kit provides 4 KiB SMMU tables.
## Interrupts
Use an interrupt event source from the provider or the platform:
```cpp
IOInterruptEventSource *source = IOInterruptEventSource::interruptEventSource(
this, OSMemberFunctionCast(IOInterruptEventSource::Action,
this, &MyDriver::handleInterrupt), provider, 0);
if (source == nullptr || !workLoop->addEventSource(source)) {
return false;
}
source->enable();
```
The provider must publish an interrupt controller or an interrupt resource. `ArmCpu` supplies the GIC path on the generic ARM64 platform.
## Device tree resources
The platform expert publishes one nub per `arm-io` child. A driver that serves a device below the `arm-io` range takes its physical addresses from board constants.
A driver that matches `/product` or `/buttons` uses `IOPlatformExpertDevice` as the provider.
## Runtime behavior
| Stage | Call |
| --- | --- |
| Match | `probe()` |
| Start | `start()` |
| Publish | `registerService()` |
| Stop | `stop()` |
A driver that fails `start()` releases its resources and returns `false`.
## Testing
| Test | Method |
| --- | --- |
| Compile | `-Wall -Wextra -Werror` against the recorded flags |
| Object ABI | Check magic, CPU type, file type, subtype and build version |
| Host logic | Extract pure functions and compile a host test |
| Kernel boot | QEMU with the driver enabled and a boot progress log |
| Hardware | Boot the device and read the driver log |
## Reference drivers
| Driver | Demonstrates |
| --- | --- |
| `ArmDTPlatform` | Platform expert and device nubs |
| `ArmCpu` | CPU, GIC and timer |
| `NativeUfs` | Storage, SMMU and command queues |
| `NativeAES` | Accelerator subclass with original ABI |
| `NativeAdreno` | GPU power, firmware, rings and user client |
## Worked example
The [sheng touch driver](/devices/sheng-touch-driver) shows the full structure: a pure processing core, a controller protocol behind a bus interface, board constants with sources, staged probing and HID publication.
## Next steps
- [Kext bundles](/drivers/kexts)
- [Device tree matching](/drivers/device-tree-matching)
- [Build the kernel](/guide/build-kernel)
+118
View File
@@ -0,0 +1,118 @@
# Boot the image
The boot image runs from RAM. The workflow starts the image and leaves the installed boot partition unchanged.
## Prerequisites
- An unlocked bootloader with `fastboot`.
- A device with a UEFI port that matches the board configuration.
- A built boot image with a payload, or an empty ramdisk for the boot manager smoke test.
- A serial console when the board exposes a UART.
## Start the image
```sh
fastboot devices
fastboot boot out/<board>/boot/boot.img
```
`fastboot boot` transfers the image and starts it one time. The stock boot partition and the installed OS stay unchanged.
The boot image contains:
| Part | Contents |
| --- | --- |
| Kernel | gzip of BootShim plus the UEFI firmware volume |
| Ramdisk | UniXNU payload FAT volume |
| Appended data | Device tree |
The stock Android bootloader loads the image, verifies the ARM64 header in BootShim, and jumps to the shim. BootShim starts the firmware volume.
## Boot manager
UniXNU prints its version and configuration summary to the serial port and draws the menu. The default configuration uses a timeout, a default entry and a countdown.
| Action | Key |
| --- | --- |
| Move the highlight | Up and Down |
| Boot the selected entry | Enter |
| Stop the countdown | Any key |
The menu entries come from `\EFI\UniXNU\unixnu.toml`. An entry can start an EFI image, a Linux image with an EFI stub, an action, or a Darwin kernel through the loader protocol.
If UniXNU finds no configuration, it scans every volume for `\EFI\BOOT\BOOTAA64.EFI` and adds those entries in front of the built-in actions.
## Darwin boot
For an entry of type `xnu`, DarwinBoot reads these files from the entry directory:
| File | Required | Contents |
| --- | --- | --- |
| `kernel.macho` | yes | Prelinked XNU kernel |
| `kernel.macho.adt` | yes | Apple-format device tree sidecar |
| `kernel.macho.trustcache` | no | Trust cache bundle |
| the entry `initrd` | no | Data ramdisk seed |
DarwinBoot copies the kernel segments into a contiguous physical window, builds `boot_args`, installs the ADT and the trust cache addresses in the `/chosen/memory-map` placeholders, and exits boot services.
The default window is 256 MiB. The ADT property `/chosen/darwinboot-memory-size` can request a larger window.
## First boot
The first boot needs a userdata image. The image supplies the OS and SystemOS volumes and the root volume UUID that the kernel matches.
```sh
fastboot flash userdata out/<board>/userdata/userdata-root.img
fastboot reboot bootloader
fastboot boot out/<board>/boot/boot.img
```
The userdata image is large. Build it with `unixnu build userdata` and flash it from the device repository report.
The Data seed supplies the writable Data volume. The `--skip-setup` option marks Setup as done in a copy of the seed, so the Hello pages do not run.
## Logs
The UEFI firmware logs to `EFI_SERIAL_IO_PROTOCOL` when the board exposes a UART. UniXNU prefixes its lines with `UNIXNU:`.
The kernel logs to the serial port when the command line selects one, for example `serial=3`. The boot progress layer writes checkpoints to the framebuffer and the serial port.
Useful logs:
| Source | Location |
| --- | --- |
| Build driver | `out/<board>/logs/*.log` |
| Boot manager | Serial console, lines with `UNIXNU:` |
| DarwinBoot | Serial console, lines with `DARWINBOOT:` |
| XNU | Serial console, kernel output |
## Recovery
The image runs from RAM. Reset the device to return to the stock bootloader and the installed OS.
```sh
fastboot reboot bootloader
```
If the firmware stalls before the menu, reset the device and start the same image again. A firmware stall leaves the device in the bootloader; a second `fastboot boot` uses the same image.
## macOS
The boot pipeline is driven by the IPSW. A macOS restore image provides `KernelCache`, `DeviceTree`, `OS`, `Cryptex1,SystemOS`, `StaticTrustCache` and `Cryptex1,SystemTrustCache` in the same member layout.
To boot macOS:
1. Point `[ipsw]` in the board file at a macOS IPSW and set the product, version, build, device class, chip ID and board ID of that image.
2. Pin the member hashes in `[ipsw.sha256]`.
3. Set `[userdata] windows` to `OS` and `Cryptex1,SystemOS`.
4. Build the kernel, payload, boot image and userdata image.
5. Flash the userdata image and start the boot image.
The device tree and the platform drivers supply the hardware description. The userland comes from the macOS disk images.
## Next steps
- [Userdata image](/guide/userdata) describes the partition image.
- [Boot arguments](/xnu/boot-arguments) lists the kernel arguments.
- [Troubleshooting](/reference/troubleshooting) lists failure conditions and checks.
- [Devices](/devices/) lists device specific commands.
+120
View File
@@ -0,0 +1,120 @@
# Build the kernel
The kernel step builds Apple's XNU source with UniXNU patches, then prelinks the result against the original kernelcache.
## Source and tree
| Path | Contents |
| --- | --- |
| `out/<board>/tree/refer/xnu` | Pinned XNU source |
| `out/<board>/tree/.cache/xnu-work` | Working clone with patches applied |
| `out/<board>/tree/build/xnu/ios-obj` | Object files and compile command records |
| `out/<board>/tree/build/xnu/ios-sym` | Symbols |
| `out/<board>/tree/build/xnu/ios-dst` | Install headers |
| `out/<board>/tree/.cache/fakeroot` | Kernel SDK root with restored headers |
| `out/<board>/kernel/kernel.macho` | Prelinked kernel |
The source version comes from `feeds.toml`:
```toml
[[source]]
name = "xnu"
kind = "refer"
git = "https://github.com/apple-oss-distributions/xnu.git"
ref = "xnu-10063.141.1"
commit = "d8b80295118ef25ac3a784134bcf95cd8e88109f"
darwin = "23.6.0"
version = "10063.141.1"
driverkit_tag = "xnu-10063.121.3"
trustcache_tag = "xnu-8796.101.5"
```
`unixnu build kernel` passes these values to `scripts/build-xnu-4k-baseline.sh` as `UNIXNU_XNU_TAG`, `UNIXNU_XNU_COMMIT`, `UNIXNU_XNU_DARWIN`, `UNIXNU_XNU_VERSION`, `UNIXNU_DRIVERKIT_TAG` and `UNIXNU_TRUSTCACHE_TAG`.
The build target is:
```
SDKROOT=iphoneos
ARCH_CONFIGS=ARM64
MACHINE_CONFIGS=BCM2837
KERNEL_CONFIGS=DEVELOPMENT
ARM64_LINK_ADDR=0xfffffff007004000
ARM64_PLKSEG_ADDR=0xfffffff004004000
DEPLOYMENT_TARGET_FLAGS="-miphoneos-version-min=17.0 -DXNU_TARGET_OS_IOS"
```
`BCM2837` is the XNU machine configuration that carries the 4 KiB page layout. The kernel still targets iOS 17 so the prelink ABI matches the iPadOS kernelcache.
## Defconfig flags
Each `CONFIG_XNU_<NAME>` line becomes `XNU_<NAME>` in the kernel build environment. `y` becomes `1`, `n` becomes `0`, quoted strings are passed as text, and `{tree}` in a string is replaced by the build tree path.
| Flag | Effect | Archive |
| --- | --- | --- |
| `XNU_SOURCE_CORECRYPTO14` | Builds and links the source corecrypto provider | `build/corecrypto-2023/libcorecrypto_kernel_noasm-namespaced.a` |
| `XNU_SOURCE_PTHREAD` | Builds and registers the source pthread component | `build/libpthread-kernel/libpthread_kernel.a` |
| `XNU_ARM_DT_PLATFORM` | Links the Apple-format device tree platform expert | `build/armdt-platform/libArmDTPlatform.a` |
| `XNU_ARMVIRT_PLATFORM` | Links the QEMU virt platform expert | `build/armvirt-platform/libArmVirtPlatform.a` |
| `XNU_ARMCPU` | Links the architectural CPU and interrupt provider | `build/armcpu/libArmCpu.a` |
| `XNU_NATIVE_UFS` | Links the UFS storage driver | `build/native-ufs/libNativeUfs.a` |
| `XNU_NATIVE_AES` | Links the AES accelerator driver | `build/native-aes/libNativeAES.a` |
| `XNU_NATIVE_VIRTIO_BLOCK` | Links the VirtIO block driver | `XNU_NATIVE_VIRTIO_BLOCK_ARCHIVE` or `build/native-virtio-block/libNativeVirtioBlock.a` |
| `XNU_NATIVE_RAM_BLOCK` | Links the RAM block driver | `build/native-ram-block/libNativeRamBlock.a` |
| `XNU_NATIVE_VIRTIO_CONSOLE` | Links the VirtIO console byte transport | `build/native-virtio-console/libNativeVirtioConsole.a` |
| `XNU_NATIVE_MOBILE_DISPLAY` | Links the mobile display provider | `build/native-mobile-display/libNativeMobileDisplay.late.a` |
| `XNU_NATIVE_LEGACY_DISPLAY` | Links the legacy display provider | `build/native-legacy-display/libNativeLegacyDisplay.late.a` |
| `XNU_NATIVE_ADRENO` | Links the Adreno GPU driver and kits | `build/native-adreno/libNativeAdreno.a` |
| `XNU_NATIVE_OFFLINE_GPU` | Links the offline GPU broker | `build/native-offline-gpu/libNativeOfflineGPU.late.a` |
| `XNU_VERIFIED_ROOT_PROBE` | Links the verified root provider | `XNU_VERIFIED_ROOT_ARCHIVE` or `build/verified-root-xnu/libVerifiedRootProbe.a` |
| `XNU_NATIVE_CS_HASH_CACHE` | Derives 4 KiB code signature hashes from verified 16 KiB blocks | none |
| `XNU_NATIVE_ARCH_TIMER` | Uses the architectural timer entry | none |
| `XNU_NATIVE_PAN` | Enables ARMv8.1 PAN support | none |
| `XNU_DT_PANIC_REGION` | Reads the panic region from the device tree | none |
| `XNU_TRUST_CACHE_RUNTIME` | Initializes the trust cache runtime | none |
| `XNU_BOOT_PROGRESS` | Adds the boot progress layer | `build/boot-progress/libBootProgress.a` |
| `XNU_SOURCE_MOCKFS` | Adds the mockfs test file | `build/mockfs-fixture/libmockfs_fixture.a` |
The device repository owns the scripts that produce its archives. Run them after the first kernel build produces the compile command records, then run `unixnu build kernel` again.
## Build sequence
1. `unixnu fetch` clones XNU and the Apple references.
2. `unixnu build kernel` applies patches and builds XNU. When `XNU_BOOT_PROGRESS=1` and `startup.o.json` is absent, the step runs once with boot progress disabled, then again with it enabled.
3. Build the device archives. Each device script compiles against the actual XNU compile flags recorded in `build/xnu/ios-obj/.../*.cpo.json`.
4. `unixnu build kernel` links the archives into the kernel.
5. `unixnu build prelink` produces `out/<board>/kernel/kernel.macho`.
The prelink reads `[kernel.prelink]` from `board.toml`:
```toml
[kernel.prelink]
tool = "kits/NativePrelink/emit_prelink.py"
kernel = "build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf"
kernelcache_sha256 = "..."
slide = "-0x1000000"
early_security_init = "com.apple.driver.AppleMobileFileIntegrity=0xfffffff005ab4ae0"
roots = [
"com.apple.driver.AppleARMPlatform",
"com.apple.filesystems.apfs",
]
```
`roots` lists the original kernelcache components the native kernel uses. `early_security_init` maps a component identifier to an address that runs during prelink security initialization.
## Adding a kernel flag
1. Add `CONFIG_XNU_<NAME>=y` to the device defconfig.
2. Read `XNU_<NAME>` in `scripts/build-xnu-4k-baseline.sh`.
3. Add the object flags with `EXTRA_MAKE_ARGS+=("<file>.o_CFLAGS_ADD=-DXNU_<NAME>=$VALUE")`.
4. Add the archive to `KERNEL_EXTRA_ARCHIVES` and `KERNEL_ARCHIVE_DEPENDENCIES`.
5. Add the personality to `iokit/KernelConfigTables.cpp` through a patch.
## Prebuilt kernel
`unixnu build payload --kernel FILE` packs an existing prelinked kernel. This path builds the boot image without rebuilding XNU.
## Next steps
- [Four-kilobyte pages](/xnu/four-k-page-tables) explains the page size work.
- [Patch reference](/xnu/patch-reference) lists every patch.
- [Prelink](/concepts/prelink) explains the kernelcache binding.
+287
View File
@@ -0,0 +1,287 @@
# Debugging and logs
A bring-up produces four kinds of evidence: serial output, the on-screen diagnostic page, the panic screen and build reports. Collect them together.
## Log sources
| Source | Transport | Use |
| --- | --- | --- |
| UEFI serial | UART | Firmware, UniXNU and DarwinBoot |
| Kernel serial | UART selected by `serial=` | XNU console and panic output |
| Diagnostic page | Framebuffer | Live state after the console stops |
| Pulse strip | Framebuffer bottom row | Heartbeat while the diagnostic page owns the panel |
| Panic screen | Framebuffer | Panic text and registers |
| QEMU log | File | UniXNU and loader tests |
| Build reports | JSON | Input hashes and stage results |
## Serial console
Add the arguments to the payload:
```toml
[payload]
kernel_args = ["-v", "serial=3", "debug=0x14e"]
```
The UniXNU boot manager writes to the first `EFI_SERIAL_IO_PROTOCOL` instance. Its lines start with `UNIXNU:`. DarwinBoot lines start with `DARWINBOOT:`.
Capture the port:
```sh
tio -b 115200 -d 8 -p none -s 1 /dev/tty.usbserial-0001 -L out/serial.log
```
Other tools:
```sh
picocom -b 115200 /dev/tty.usbserial-0001
screen -L /dev/tty.usbserial-0001 115200
minicom -D /dev/tty.usbserial-0001 -b 115200
```
The board page records the UART pins and the baud rate. A board with USB device mode can expose a CDC ACM port instead.
Useful early output:
```
UNIXNU: UniXNU 0.1.0 starting as firmware boot option
UNIXNU: config: loaded \EFI\UniXNU\unixnu.toml
UNIXNU: driver ...: started
UNIXNU: menu: shown 2 entries, default 0
DARWINBOOT: loader v2 installed EL1
DARWINBOOT: ARM64 va=... pa=... args=... entry=... owned=...
DARWINBOOT: inputs validated; exiting boot services
```
## On-screen diagnostic page
The diagnostic page is a full-screen text overlay drawn into the XNU framebuffer mapping. Enable it with:
```
boot-progress=1
```
Level 1 arms the monitor. The page freezes when no stage or storage activity appears for 30 seconds, or at the 120 second observation limit, and keeps the screen for a photograph.
```
boot-progress=2
```
Level 2 runs continuously. Deadlines are reported and the OS keeps running.
The page uses 48 rows of 120 columns. On a panel at least 2160 by 1400 the scale is 3.
| Row group | Contents |
| --- | --- |
| Title | Build tag, live or stop state, reason |
| 1 | Time, idle, IRQ count, monitor count, root state, process count, free pages |
| 2 | Storage requests, verified blocks, failures, integrity counters |
| 3 to 30 | Stage history, process states, storage detail, last PC and registers |
| 31 to 47 | Cached storage output, UFS timing, memory, process sampling, UI trace, touch rows |
| 47 | Independent pulse strip |
Label the image so a photograph identifies the build:
```
sheng-build-tag=ks-001
```
The title bar then starts with `[ks-001]`.
When userland takes over the display and stops scanning out, take the panel back:
```
boot-diag-resume=10
```
After 10 seconds without a scanout, the page redraws. A new validated scanout yields the panel again.
The page reports:
| Field | Meaning |
| --- | --- |
| `time` | Seconds since the monitor started |
| `idle` | Seconds since the last stage or I/O progress |
| `IRQ` | Interrupt entries counted by the monitor |
| `monitor` | Monitor thread iterations |
| `lost` | Dropped recorder updates |
| `root` | Owner root acceptance |
| `procs` | Process count |
| `free` | Free 4 KiB pages |
| `requests` | Storage requests |
| `verified4K` | Verified 4 KiB blocks |
| `failures` | Storage failures |
| `integrity` | Storage integrity failures |
| `last PC` | Last interrupt entry PC |
| `TCR`, `TTBR`, `SCTLR` | Translation control and table base registers |
## Pulse strip
The pulse strip is a 32-pixel strip at the bottom of the panel, drawn at 2x scale in green. It runs on a separate high-priority monitor thread at one second intervals.
```
P<turn> I<irq> M<monitor>/<seconds> B<bind>:<result> d<depth> @<base> lock<busy>/<render> PC<pc>
```
| Token | Meaning |
| --- | --- |
| `P` | Pulse turn |
| `I` | Interrupt entries |
| `M` | Monitor count and seconds since it advanced |
| `B` | Framebuffer bind count, result and depth |
| `@` | Framebuffer base |
| `lock` | Recorder and renderer busy flags |
| `PC` | Last interrupt entry PC |
The strip survives while the diagnostic page owns the panel. It closes when the OS takes over the display. `boot-diag-resume` reopens the page and the strip.
## Panic screen
The payload can patch a panic screen into the kernel copy:
```toml
[payload]
panic_screen = true
```
The payload step runs:
```sh
python3 scripts/patch-xnu-entry-probe.py <kernel> --panic-screen --no-reset
```
The panic screen writes the formatted panic message and registers to the framebuffer and keeps the image visible. `--no-reset` stops the automatic reset, so the screen stays for a photograph.
The first panic message is also copied to `boot_progress_panic_text`. A panic on a secondary core parks that core; the live banner keeps the text after the screen is repainted.
The device tree panic region is available with:
```
CONFIG_XNU_DT_PANIC_REGION=y
```
The kernel reads `/chosen` `embedded-panic-log-size` and the region `reg` property. The region is validated before it is mapped.
## User fault evidence
The fault probe records EL0 breakpoint and abort state before exception delivery:
| Field | Meaning |
| --- | --- |
| `pid`, `name` | Process |
| `PC`, `LR`, `SP`, `FP` | Registers at the fault |
| `ESR`, `FAR` | Exception syndrome and fault address |
| `slide` | Shared region slide |
| `x0` to `x28` | General registers |
| frames | Up to 12 return addresses |
The capture runs in the trap path without allocation. A matching process exit prints the record with the signal. An uncaught exception reason is searched in the writable malloc regions of the exiting process and printed with the crash row.
## UI stack trace
The UI trace samples the selected processes and prints PC, LR, FP and stack slices:
```
boot-ui-focus=launchd
boot-ui-focus2=SpringBoard
boot-ui-focus3=backboardd
boot-ui-focus4=Setup
boot-ui-chrono=1
```
The address tokens use a radix-64 alphabet to fit in the row width. Feed the transcribed rows to the decoder:
```sh
python3 scripts/decode-boot-ui-trace.py trace.txt
```
The decoder prints one JSON object per thread group with `pc`, `lr`, frames and error codes.
The trace rotates one page every 8 seconds. Take three or four photographs several seconds apart to capture every page.
## Taking photographs
The diagnostic page is built to be photographed. Use this sequence:
1. Set `sheng-build-tag` to a short image label.
2. Enable `boot-progress=1` for a frozen page, or `boot-progress=2` for a live page.
3. Set `boot-diag-resume` when userland blanks the panel.
4. Place the device on a stand. Keep the camera parallel to the panel.
5. Turn off the flash. Light the panel from the side to avoid a reflection.
6. Lock focus and exposure on the title bar.
7. Fill the frame with the panel and include all four corners.
8. Photograph the title bar, then the full page.
9. For UI trace pages, take three or four photos 8 seconds apart.
10. Photograph the bottom pulse strip.
11. Record the boot image SHA-256 and the capture time.
| Check | Why |
| --- | --- |
| Title bar readable | Build tag and state identify the image |
| No glare over text | Rows 3 to 30 carry the stage history |
| All four corners visible | Confirms the full panel and scale |
| Pulse strip visible | Shows the heartbeat at capture time |
| Two or more frames | A changing counter shows a live screen |
A camera photo is evidence of what the panel showed. Keep the original file. Transcribe only the rows you need, and keep the photo with the transcription.
## QEMU capture
```sh
python3 package/Scripts/qemu-run.py out/esp \
--log out/qemu.log --screenshot out/qemu.png \
--expect 'UNIXNU: menu: shown'
```
The runner writes the ANSI-stripped log to `out/qemu.clean.log`. `--screenshot` captures the panel when the menu appears. `--keys` sends QEMU key codes after the menu.
The stop helper records registers and memory at a symbol:
```sh
python3 scripts/qemu-entry/gdb-stop.py --help
```
## Debug arguments
| Argument | Use |
| --- | --- |
| `-v` | Verbose kernel output |
| `serial=3` | Serial console |
| `debug=0x14e` | Debug output mask |
| `kextlog=0xfff` | Kext logging |
| `boot-progress=1` | Diagnostic page with a 30 second stall freeze |
| `boot-progress=2` | Diagnostic page with continuous operation |
| `sheng-build-tag=<tag>` | Image label in the title bar |
| `boot-diag-resume=<seconds>` | Take the panel back after the OS stops scanning out |
| `sheng-platform-stage=<n>` | Stop the platform expert at a stage |
| `sheng-touch=<n>` | Touch probe stage |
| `offline-legacy-phase=<n>` | Display probe stage |
| `-no_slto_panic` | Suppress the SLTO panic |
| `cs_debug=<mask>` | Code signing output |
## Evidence bundle
Collect:
| Item | Source |
| --- | --- |
| Serial log | UART capture file |
| Diagnostic photos | Title bar, full page, trace pages, pulse strip |
| Panic photo | Panic screen |
| Boot report | `out/<board>/boot/boot.json` |
| Payload report | `out/<board>/payload/payload.json` |
| Build logs | `out/<board>/logs/` |
Record the boot image hash with every photo:
```sh
shasum -a 256 out/<board>/boot/boot.img
jq . out/<board>/boot/boot.json
```
## Next steps
- [Boot arguments](/xnu/boot-arguments)
- [Troubleshooting](/reference/troubleshooting)
- [QEMU testing](/reference/qemu)
- [sheng device page](/devices/sheng)
+129
View File
@@ -0,0 +1,129 @@
# Overview
UniXNU starts a Darwin-based operating system on an ARM64 device from a UEFI environment. The stack has six layers. Each layer has a repository, a build output and a handoff contract.
## Layer model
```
Android bootloader (ABL)
|
| fastboot boot boot.img
v
BootShim + UEFI firmware volume + device tree
|
| UEFI BDS starts UniXNU
v
UniXNU (UEFI boot manager)
|
| UNIXNU_LOADER_PROTOCOL
v
DarwinBoot (XNU loader)
|
| boot_args, ADT, trust cache, ramdisk
v
Patched XNU kernel
|
| IOKit, BSD, launchd
v
Userland from the IPSW (OS and SystemOS)
```
## Components
### Boot image
The boot image is an Android boot image with header version 0. Its kernel field holds gzip data with two parts: the compiled BootShim and the UEFI firmware volume. The device tree follows the gzip stream. The ramdisk field holds the UniXNU payload.
The stock Android bootloader loads the image at the address declared by the Mu-Silicium device configuration, verifies the ARM64 image header, and jumps to the shim. BootShim relocates the firmware volume and enters UEFI.
### UEFI firmware
The firmware comes from a Mu-Silicium checkout for the device. It provides the UEFI services the boot manager needs:
- `EFI_GRAPHICS_OUTPUT_PROTOCOL` for the menu and the kernel framebuffer.
- `EFI_SIMPLE_TEXT_IN_PROTOCOL` for menu input.
- `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` and `EFI_BLOCK_IO_PROTOCOL` for the payload volume and disk partitions.
- `EFI_SERIAL_IO_PROTOCOL` for logs when the board exposes a UART.
The `unixnu build firmware` step applies the board patches to the shared edk2 tree, builds the board DSC and returns `SILICIUM_UEFI.fd`.
### Boot manager
The UniXNU boot manager is a UEFI application built from `package/UniXNUPkg`. It reads `\EFI\UniXNU\unixnu.toml` from the volume it was started from, then from every other volume. The configuration declares menu entries, drivers, device tree patches, ACPI tables, block slices and kits.
The boot manager performs five jobs before the menu:
1. Publishes every `[[block_slice]]` as a block device.
2. Loads and starts every configured UEFI driver.
3. Installs every configured ACPI table.
4. Merges every kit whose `[match]` section is satisfied.
5. Prepares the device tree for the selected entry.
An entry of type `xnu` is handed to a loader that implements `UNIXNU_LOADER_PROTOCOL`.
### DarwinBoot
DarwinBoot is a UEFI driver in the firmware volume. It installs the loader protocol at `EL2` or `EL1`. On an `xnu` entry it performs these steps:
1. Reads the kernel Mach-O from the payload volume.
2. Reads the `.adt` sidecar next to the kernel.
3. Reads the optional `.trustcache` sidecar.
4. Validates the Mach-O segments, the ADT and the trust cache structure.
5. Allocates a physical window for the kernel and its data.
6. Copies the kernel segments, the ADT, the trust cache and the ramdisk.
7. Builds a Darwin `boot_args` structure.
8. Exits boot services, disables the MMU and caches, and jumps to the kernel entry point in EL1.
The loader requires a single ARM64 Mach-O with a `__TEXT` segment, a `LC_MAIN` entry point and a base address aligned to the firmware page size.
### XNU kernel
The kernel is built from Apple's `xnu-10063.141.1` source with the patches in `patches/xnu`. The BCM2837 machine configuration supplies the 4 KiB page layout for non-Apple ARM64 boards.
The native kernel is prelinked against the original kernelcache from the target IPSW. The prelink keeps the original Apple kexts and binds the native kernel to their symbols. The result is a single `kernel.macho` that contains the native kernel segments and the selected original components.
### Userland
The userdata image holds the unchanged `OS` and `Cryptex1,SystemOS` disk images from the IPSW. A 4 KiB manifest at the start of the partition describes both windows. The native UFS or block driver publishes the partition, and XNU mounts the root volume by UUID.
## Repositories
| Repository | Purpose |
| --- | --- |
| `UniXNU` | Build driver, UEFI boot manager, DarwinBoot, generic drivers, kits, patches and documentation |
| `UniXNU-<device>` | Board description, kernel defconfig, device scripts, device drivers and package |
| `uefi-<device>` | Mu-Silicium UEFI port for the device |
A device repository has the same directory layout as `UniXNU`. Its `drivers/`, `kits/`, `patches/`, `scripts/` and `package/` override the generic modules with the same name.
## Build outputs
Everything is written under `out/`:
```
out/
state.json remembered device repository
feeds/ fetched git sources
ipsw/<build>/ extracted IPSW members
<board>/
.config selected defconfig
tree/ assembled build tree
logs/ one log per build step
kernel/kernel.macho prelinked kernel
package/Build/Out/ UniXNU and test EFI binaries
payload/payload.img FAT payload volume
payload/payload.json payload report
boot/boot.img Android boot image
boot/boot.json boot image report
userdata/userdata-root.img
userdata/userdata-root.json
```
The reports record input hashes and the commands that produce the next step. The boot image is deterministic for a given firmware volume, device tree and ramdisk. The payload contains a random seed in the ADT, so two payload builds differ.
## Next steps
- [Requirements](/guide/requirements) lists the host tools and external inputs.
- [Build](/building) walks through the driver commands.
- [Porting](/porting/) describes how to add a device.
- [Devices](/devices/) lists device repositories. The sheng page records one device; the general guide uses placeholders.
+55
View File
@@ -0,0 +1,55 @@
# Requirements
## Host
The build driver runs on macOS. The kernel step invokes `xcodebuild` with the `iphoneos` SDK, and the `--skip-setup` option invokes `hdiutil`.
| Tool | Used by | Install |
| --- | --- | --- |
| macOS with Xcode | kernel, skip-setup | App Store or developer.apple.com |
| Python 3.11 or newer | every step | python.org or Homebrew |
| git, make | fetch, firmware | Xcode command line tools or Homebrew |
| cmake, ninja | kernel references | `brew install cmake ninja` |
| unifdef | libdispatch headers | `brew install unifdef` |
| LLVM with lld | kernel, firmware, boot image | `brew install llvm` |
| mtools | payload volume | `brew install mtools` |
| dtc | device tree properties | `brew install dtc` |
| QEMU and AAVMF | optional smoke tests | `brew install qemu` |
The driver finds the keg-only Homebrew LLVM at `/opt/homebrew/opt/llvm/bin` without `PATH` changes. Other tools must be on `PATH`.
Network access is required for `unixnu fetch`, which fetches Apple source references and a Mu_Basecore tree.
## External inputs
These inputs live outside the UniXNU repositories.
| Input | Used by | Option | Environment |
| --- | --- | --- | --- |
| Mu-Silicium checkout for the device | firmware, boot image | `--uefi DIR` | `UNIXNU_UEFI` |
| Target IPSW, local file or HTTPS URL | kernel prelink, userdata | `--ipsw PATH_OR_URL` | `UNIXNU_IPSW` |
| Decompressed kernelcache | kernel prelink | `--kernelcache FILE` | `UNIXNU_KERNELCACHE` |
| Compiled device tree | boot image | `--dtb FILE` | `UNIXNU_DTB` |
| Trust cache bundle | payload | `--trust-cache FILE` | |
| Data ramdisk seed | payload | `--data-ramdisk FILE` | |
| Platform seed | payload | `--unique-chip-id HEX` | `UNIXNU_UNIQUE_CHIP_ID` |
| Prebuilt static archives | kernel | `--kernel FILE` for the payload, archives in the build tree | |
| edk2 or Mu_Basecore tree | package, firmware | `--edk2 DIR` | `UNIXNU_EDK2` |
The device repository supplies the board description, the kernel defconfig and the scripts that produce device-specific archives.
## Device
The device needs:
- An unlockable bootloader that accepts `fastboot boot`.
- A UEFI port that provides a framebuffer, input, block I/O and file system access.
- A UART or USB transport for logs during bring-up.
- Enough RAM for the kernel window, the payload and the userdata image.
- The target OS restore image and a matching device identity.
## Next steps
- [Build](/building) describes the commands that consume these inputs.
- [Device repository](/porting/device-repository) describes the board file.
- [Devices](/devices/) lists device repositories and their specific inputs.
+94
View File
@@ -0,0 +1,94 @@
# Userdata image
The userdata image supplies the root file system to XNU. It holds the unchanged `OS` and `Cryptex1,SystemOS` disk images from the target IPSW behind a manifest that the kernel reads.
## Build
```sh
./unixnu build userdata --ipsw /path/to/restore.ipsw
```
The step extracts the IPSW components named in `[userdata] windows`, reads each disk image with `hdiutil`, and writes a raw image to `out/<board>/userdata/userdata-root.img`.
The report `userdata-root.json` records the window offsets, sizes and hashes, the partition capacity and the flash command.
A dry run checks the IPSW and the partition size without writing the image:
```sh
./unixnu build userdata --dry-run
```
## Partition
`board.toml` describes the GPT entry:
```toml
[userdata]
first_lba = 0x2f3840
last_lba = 0x3d0abfa
lba_bytes = 4096
windows = [
{ name = "OS", component = "OS" },
{ name = "SystemOS", component = "Cryptex1,SystemOS" },
]
```
`first_lba` and `last_lba` come from the GPT entry named `userdata` on the device. `lba_bytes` is the logical block size of the partition.
The image size is the sum of the windows, each aligned to 1 MiB, plus the 1 MiB header region. The step rejects an image larger than the partition.
## Layout
```
0x000000 SHNGROOT manifest, 4096 bytes, zero padded to 1 MiB
0x100000 OS window, whole disk image
.... SystemOS window, whole disk image
```
The manifest is little endian:
| Offset | Field |
| --- | --- |
| 0 | `SHNGROOT` |
| 8 | version, 32-bit, value 1 |
| 12 | window count, 32-bit |
| 16 | image size, 64-bit |
| 24 | window entries, 128 bytes each |
| 4064 | SHA-256 of bytes 0 to 4063 |
Each window entry contains a 32-byte name, a 64-bit offset, a 64-bit size, a 32-bit block size, a 32-bit flags field, a 32-byte SHA-256 and 40 reserved bytes.
## Flash
```sh
fastboot flash userdata out/<board>/userdata/userdata-root.img
```
The driver prints the command in the report. The device repository can provide a wrapper script that checks the image and partition before flashing.
## Root selection
The kernel mounts the root volume by UUID:
```toml
[payload.adt]
root_match_volume_uuid = "00000000-0000-0000-0000-000000000000"
```
The ADT generator writes a `/chosen/root-matching` dictionary with `IOProviderClass = IOMedia` and the UUID. The native block driver publishes the userdata partition, the partition scheme publishes the volumes, and XNU selects the IOMedia with the matching UUID.
## Data volume
The writable Data volume comes from the Data ramdisk seed in the payload. The seed is an input to the payload step:
```sh
./unixnu build payload --data-ramdisk data.img
```
`--skip-setup` marks Setup as done in a copy of the seed. The payload report records the seed hash and the resulting image hash.
## Next steps
- [Userdata layout](/concepts/userdata) explains the manifest and the disk windows.
- [Boot the image](/guide/boot) covers the first boot.
- [Device configuration](/reference/configuration) lists every `[userdata]` key.
+58
View File
@@ -0,0 +1,58 @@
---
layout: home
hero:
name: UniXNU
text: Darwin and XNU userland on non-Apple ARM64 hardware
tagline: A UEFI boot manager, a device tree platform, a patched XNU kernel and a complete boot image.
image:
src: /logo-light.svg
alt: UniXNU logo
actions:
- theme: brand
text: Start here
link: /guide/overview
- theme: alt
text: Port a device
link: /porting/
- theme: alt
text: Device notes
link: /devices/
---
::: warning Documentation status
The documentation is under active maintenance. Some pages may be incomplete or contain inaccurate details. Check the source repository and the build reports when a detail matters.
:::
## What UniXNU provides
UniXNU is a build system and runtime stack for starting Darwin-based operating systems on ARM64 devices whose firmware is UEFI or can be made UEFI.
| Layer | Component | Role |
| --- | --- | --- |
| Boot image | BootShim, UEFI firmware volume, device tree | Loaded by the stock Android bootloader with `fastboot boot` |
| Boot manager | UniXNU | Loads drivers, applies device tree patches, publishes block slices, runs the menu |
| Loader | DarwinBoot | Validates the kernel Mach-O, ADT and trust cache, exits boot services, enters EL1 |
| Kernel | Patched XNU | Built from Apple source, prelinked against the original kernelcache |
| Devices | XNU drivers and kits | Platform expert, CPU, storage, display, GPU and board power |
| Userland | IPSW disk images | Unchanged OS and SystemOS images on the userdata partition |
## Documentation map
- [Overview](/guide/overview): the stack, the repositories and the build model.
- [Build](/building): clone, configure, fetch, build and pack a boot image.
- [Boot the image](/guide/boot): `fastboot boot`, the menu, serial logs and recovery.
- [Porting](/porting/): add a device repository and a UEFI firmware port.
- [Drivers](/drivers/overview): UEFI drivers, built-in XNU drivers and kext bundles.
- [XNU](/xnu/source-and-patches): source version, patches, four-kilobyte pages and boot arguments.
- [Devices](/devices/): the support model and per-device notes. The sheng page is one device entry.
- [Reference](/reference/cli): every command, configuration key and output file.
## Repositories
| Repository | Contents |
| --- | --- |
| [UniXNU](https://github.com/UniXNU/UniXNU) | Build driver, UEFI boot manager, DarwinBoot, generic drivers, kits and patches |
| [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng) | Device repository for the Xiaomi Pad 6S Pro |
| [uefi-sheng](https://github.com/UniXNU/uefi-sheng) | UEFI firmware port for the Xiaomi Pad 6S Pro |
Device repositories follow the same layout as this repository. Their modules override modules with the same name, and their `package/` becomes a kit in the build tree. The generic documentation applies to every device. Device-specific values live in the repository and in the [Devices](/devices/) section.
+2561
View File
File diff suppressed because it is too large. Load diff
+13
View File
@@ -0,0 +1,13 @@
{
"name": "unixnu-docs",
"private": true,
"type": "module",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
},
"devDependencies": {
"vitepress": "1.6.4"
}
}
+144
View File
@@ -0,0 +1,144 @@
# Bring-up order
Bring-up proceeds in stages. Each stage has a visible result and a log line. Isolate one stage at a time.
## Stage 1: UEFI
Goal: the firmware reaches a UEFI shell, a boot menu or the UniXNU application.
Checks:
- The firmware volume size matches `[uefi_fd] size`.
- The boot image header matches the bootloader requirement.
- `fastboot boot boot.img` starts the firmware without a reset.
- The framebuffer shows output, or the serial port shows UEFI output.
- Block devices and the payload volume are visible.
Tools:
```sh
fastboot boot out/<board>/boot/boot.img
```
A firmware stall leaves the device in the bootloader. Start the same image again.
## Stage 2: UniXNU
Goal: the menu appears and the configuration is parsed.
Checks:
- Serial lines start with `UNIXNU:`.
- The log reports the entry, driver, patch, ACPI, slice and kit counts.
- The menu lists the expected entries.
- Driver failures show a count and the first failure reason.
Minimal test: an empty ramdisk. UniXNU falls back to its built-in configuration and lists removable loaders.
```sh
./unixnu bootimg --uefi /path/to/Mu-Silicium --dtb board.dtb --ramdisk none
```
## Stage 3: Payload
Goal: UniXNU reads `unixnu.toml` and the payload files.
Checks:
- The log reports the configuration path.
- The `xnu` entry is present.
- The payload report hashes match the files in the volume.
- The ADT sidecar is next to the kernel.
A payload error is visible before DarwinBoot starts.
## Stage 4: DarwinBoot
Goal: DarwinBoot validates the kernel, the ADT and the trust cache, then exits boot services.
Checks:
- Serial lines start with `DARWINBOOT:`.
- The loader prints the virtual base, physical base, arguments address and entry address.
- The printed output shows the framebuffer base, width, height and depth.
- The exception level is EL1.
- The bootstrap page table path reaches the kernel entry.
A refusal prints `DARWINBOOT: boot refused: <status>; firmware retained`. The firmware stays in control, so the device returns to the menu.
## Stage 5: Kernel entry
Goal: XNU starts and reaches the platform expert.
Checks:
- The kernel prints its version and boot arguments.
- The bootstrap page tables map the kernel window.
- The platform expert matches the ADT root.
- `/cpus`, `/arm-io`, `/defaults`, `/options` and the NVRAM proxy are present.
A missing platform provider produces a panic that names the provider. Add the driver or correct the ADT.
## Stage 6: Storage and root
Goal: the block driver publishes the userdata partition, the volumes appear, and XNU mounts the root.
Checks:
- The storage driver logs the link speed and the command queue.
- The partition scheme publishes `OS` and `SystemOS`.
- The root volume UUID matches `root_match_volume_uuid`.
- The kernel mounts the root and starts `launchd`.
A root match failure leaves the kernel without a root device. Check the UUID, the window offsets and the partition capacity.
## Stage 7: Userland
Goal: `launchd` runs and the first user process reaches a visible state.
Checks:
- `launchd` starts from the root volume.
- The Data volume mounts.
- The display server or the console presents output.
- The boot progress layer records the last checkpoint.
Use `boot-progress=2` and the diagnostic boot arguments to see the last completed checkpoint.
## QEMU before hardware
The QEMU fixture tests UniXNU, DarwinBoot and the kernel entry without a device:
```sh
python3 scripts/qemu-entry/make-dt.py --armvirt --armcpu --smp 2 \
--owned-memory-size 0x20000000 --output build/qemu.dtb
python3 package/Scripts/stage-esp.py -o out/esp \
package/UniXNUPkg/Test/Qemu/esp \
--put out/<board>/package/Build/Out/UniXNU.efi=EFI/BOOT/BOOTAA64.EFI
python3 package/Scripts/qemu-run.py out/esp \
--log out/qemu.log --screenshot out/qemu.png \
--expect "UNIXNU: menu: shown" --expect "DARWINBOOT:"
```
The QEMU runner boots an AArch64 `virt` machine with AAVMF, captures the serial log and can send keys to the emulated keyboard.
## Failure isolation
| Symptom | First check |
| --- | --- |
| No firmware output | Boot image header, firmware base and size, bootloader log |
| Firmware output, no menu | GOP driver, UniXNU driver path, `unixnu.toml` |
| Menu, no payload entry | Payload volume label, FAT driver, entry path |
| Payload entry, no DarwinBoot log | Loader protocol, DarwinBoot in the firmware volume |
| DarwinBoot refusal | Kernel Mach-O, ADT, trust cache, placeholder |
| Kernel entry, no log | Boot arguments, serial selection, framebuffer address |
| Panic naming a provider | ADT node, driver archive, kernel flag |
| No root | Userdata image, UUID, partition capacity |
## Next steps
- [Troubleshooting](/reference/troubleshooting)
- [QEMU testing](/reference/qemu)
- [Boot arguments](/xnu/boot-arguments)
+298
View File
@@ -0,0 +1,298 @@
# Device repository
A device repository describes one board and supplies the modules that differ from the generic tree.
## Layout
```
UniXNU-<device>/
board.toml board description
configs/<name>_defconfig kernel configuration
feeds.toml optional extra sources
local.toml optional local source overrides, ignored by Git
drivers/ device XNU drivers
kits/ device kernel libraries
patches/ device patches
scripts/ build and bring-up scripts
package/ UniXNU kit contents
unixnu.lock written by unixnu fetch
```
The directory layout matches `UniXNU`. A module in the device repository overrides the module with the same name in the base repository.
## board.toml
### Board
```toml
[board]
name = "<device>"
soc = "<SoC>"
description = "<device description>"
kit = "<kit>"
# example = true
```
| Key | Meaning |
| --- | --- |
| `name` | Output directory name under `out/` |
| `soc` | SoC identifier for reports and scripts |
| `description` | Human readable description |
| `kit` | Folder name of the device package under `kits/` |
| `example` | Marks a sample repository. `build` and `bootimg` refuse it |
### IPSW
```toml
[ipsw]
url = "https://example.invalid/restore.ipsw"
product = "<product>"
version = "<version>"
build = "<build>"
device_class = "<device class>"
chip_id = 0x0000
board_id = 0
fetch = ["KernelCache", "DeviceTree", "StaticTrustCache", "Cryptex1,SystemTrustCache"]
[ipsw.sha256]
"<member file>" = "<sha256>"
```
`url` is the default source. `--ipsw` or `UNIXNU_IPSW` overrides it. The image must match the product, version, build, device class, chip ID and board ID.
`fetch` lists the components that `unixnu ipsw fetch` extracts with no arguments.
`[ipsw.sha256]` pins member hashes by member file name. The IPSW reader checks every extracted member against its pin.
### Userdata
```toml
[userdata]
first_lba = 0x0
last_lba = 0x0
lba_bytes = 4096
windows = [
{ name = "OS", component = "OS" },
{ name = "SystemOS", component = "Cryptex1,SystemOS" },
]
```
`first_lba` and `last_lba` come from the GPT entry named `userdata`. `lba_bytes` is the logical block size. Each window names a disk image component from the IPSW.
### Kernel
```toml
[kernel]
defconfig = "<name>_defconfig"
[kernel.prelink]
tool = "kits/NativePrelink/emit_prelink.py"
kernel = "build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf"
kernelcache_sha256 = "..."
slide = "-0x1000000"
early_security_init = "com.apple.driver.AppleMobileFileIntegrity=0xfffffff005ab4ae0"
roots = [
"com.apple.driver.AppleARMPlatform",
"com.apple.filesystems.apfs",
]
extra_args = []
```
| Key | Meaning |
| --- | --- |
| `defconfig` | File name under `configs/` |
| `tool` | Prelink tool path in the build tree |
| `kernel` | Native kernel path in the build tree |
| `kernelcache_sha256` | Pins the original kernelcache |
| `slide` | Address slide applied to the selected components |
| `early_security_init` | Component identifier and address for the early initializer |
| `roots` | Bundle identifiers kept from the original kernelcache |
| `extra_args` | Additional arguments for the prelink tool |
### Firmware
```toml
[firmware]
config = "<name>"
dsc = "Platforms/<Vendor>/<device>Pkg/<device>.dsc"
fd = "Build/<device>Pkg/{target}_CLANGPDB/FV/SILICIUM_UEFI.fd"
preflight = ["scripts/check-<device>.py"]
submodules = []
patches = ["Resources/MuPatches/<patch>.patch"]
packages = [
"{uefi}",
"{tree}/package",
"{tree}/drivers/DarwinBoot",
"{edk2}",
]
[firmware.defines]
DEVICE_MODEL = "<device>"
```
| Key | Meaning |
| --- | --- |
| `config` | `Resources/Configs/<name>.toml` in the Mu-Silicium checkout |
| `dsc` | DSC path, relative to the checkout |
| `fd` | Firmware volume path. `{target}` becomes `DEBUG` or `RELEASE` |
| `preflight` | Scripts in the build tree, run before the firmware build |
| `submodules` | Mu-Silicium submodules the board needs |
| `patches` | Patches applied to the shared edk2 tree for the build, then reverted |
| `packages` | `PACKAGES_PATH` entries. `{uefi}`, `{edk2}` and `{tree}` are replaced |
| `defines` | Extra `-D` definitions for the EDK II build |
### Boot image
```toml
[bootimg.image]
base = 0x0
kernel_offset = 0x8000
tags_offset = 0x1e00000
pagesize = 4096
header_version = 0
[[bootimg.dtb_node]]
path = "/reserved-memory/<region>"
props = [
{ name = "label", type = "s", value = "<label>" },
{ name = "reg", type = "x", value = ["0x0", "0x80000000", "0x0", "0x100000"] },
]
```
The image parameters default to the Mu-Silicium device configuration. `[bootimg.image]` overrides them. Each `[[bootimg.dtb_node]]` adds a node or property to the UEFI device tree before it is appended to the boot image. The stock bootloader sometimes requires a splash region or another reserved-memory node.
### Payload
```toml
[payload]
volume = "<label>"
size_mib = 192
loader = "firmware"
title = "UniXNU"
dram_size = "0x80000000"
panic_screen = true
skip_setup_tool = "scripts/seed-data-skip-setup.py"
trust_cache_sha256 = ["<sha256>"]
timeout = 3
kernel_args = ["-v", "serial=3"]
[payload.adt]
tool = "scripts/make-<device>-adt.py"
chip_id = "0"
owned_memory_size = "0x10000000"
root_match_volume_uuid = "00000000-0000-0000-0000-000000000000"
cpu_regs = "0,0x100"
psci_method = "smc"
packed_data_ramdisk = true
ipad_identity = true
unique_chip_id = "required"
```
| Key | Meaning |
| --- | --- |
| `volume` | FAT volume label, at most 11 characters |
| `size_mib` | Volume size |
| `loader` | `firmware` when DarwinBoot is in the firmware volume |
| `title` | Menu entry title |
| `dram_size` | DRAM size passed to the ADT tool |
| `panic_screen` | Patches the panic screen into the payload copy of the kernel |
| `skip_setup_tool` | Script used by `--skip-setup` |
| `trust_cache_sha256` | Accepted trust cache bundle hashes |
| `timeout` | Menu timeout in seconds |
| `kernel_args` | Default kernel command line |
The `[payload.adt]` keys are passed to the ADT tool. The tool contract is device specific. The generic keys are documented in [Device tree](/porting/device-tree).
## Defconfig
`configs/<name>_defconfig` uses the same syntax as a Linux defconfig:
```
CONFIG_XNU_ARM_DT_PLATFORM=y
CONFIG_XNU_ARMCPU=y
CONFIG_XNU_NATIVE_UFS=y
CONFIG_XNU_NATIVE_CS_HASH_CACHE=y
# CONFIG_XNU_ARMVIRT_PLATFORM is not set
CONFIG_XNU_CRYPTO_BOOTSTRAP="prelinked"
CONFIG_XNU_OWNER_DATA_VOLUME_UUID="00000000-0000-0000-0000-000000000000"
```
Values can be `y`, `n`, a number or a quoted string. Each `CONFIG_XNU_<NAME>` becomes `XNU_<NAME>` in the kernel build. `{tree}` in a string becomes the build tree path.
The file name is selected by `[kernel] defconfig`.
## feeds.toml
A device repository can add sources:
```toml
[[source]]
name = "DeviceKit"
kind = "kit"
git = "https://github.com/ORG/REPO"
ref = "main"
path = "kits/DeviceKit"
commit = "..."
local = "~/Code/device-kit"
```
Kinds are `driver`, `kit`, `patch`, `package` and `refer`. `path` fetches only a subdirectory. `local` uses a local directory.
The base `feeds.toml` supplies `xnu`, `libplatform`, `libdispatch`, `AvailabilityVersions` and `edk2`.
## Local development
The first match wins:
1. `--local-module [KIND:]NAME=PATH`
2. `--local-<kind> DIR`
3. `--local DIR`
4. a `local` key in `feeds.toml` or an entry in `local.toml`
5. the device repository
6. the base repository
7. a git source in `feeds.toml`
A local module is linked into `out/<board>/tree`. `unixnu status` marks it and reports that the lock does not pin it.
## Package and kits
The device `package/` becomes `kits/<board.kit>` in the build tree. The package can contain a `unixnu.toml`, kit directories, drivers, device tree overlays and kext bundles.
A kit directory has a `Kit.toml`:
```toml
schema = 1
[kit]
name = "<kit>"
version = "1.0.0"
description = "<description>"
[match]
arch = "AARCH64"
fdt_compatible = ["<compatible>"]
firmware_vendor = ["<vendor>"]
requires_protocol = ["<guid>"]
[[driver]]
file = "<driver>.efi"
args = "<load options>"
[[overlay]]
file = "<overlay>.dtbo"
[[kext]]
bundle = "<name>.kext"
entry = "<entry id>"
```
The `[match]` keys are all required. Within one key, any listed value matches. A kit that does not match is skipped and counted in the menu summary.
## Next steps
- [UEFI firmware](/porting/uefi-firmware)
- [Device tree](/porting/device-tree)
- [Device configuration reference](/reference/configuration)
- [Devices](/devices/)
+208
View File
@@ -0,0 +1,208 @@
# Device tree
A device port deals with two trees:
| Tree | Format | Consumer |
| --- | --- | --- |
| UEFI device tree | Standard flattened device tree (FDT) | UEFI firmware, EFI and Linux entries |
| Apple device tree (ADT) | Apple node encoding | XNU platform expert and drivers |
The payload step generates the ADT as `kernel.macho.adt` next to the kernel. DarwinBoot reads it from the entry directory.
## Apple device tree encoding
An Apple device tree uses a compact node encoding:
```
u32 property_count
u32 child_count
for each property:
char name[32]
u32 length
u8 value[length]
padding to 4 bytes
for each child:
node
```
Every node carries a `name` property with its own name and a terminating NUL.
The encoding is Apple's own. `dtc` compiles FDT sources, and libfdt functions read FDT blobs, so both apply to the UEFI tree only.
## Required nodes
The generic `ArmDTPlatform` requires these top level nodes:
| Node | Required contents |
| --- | --- |
| `/chosen` | `random-seed`, CHRP NVRAM proxy, DRAM description, `osenvironment` |
| `/cpus` | One `cpuN` child per core |
| `/arm-io` | Bus node with `#address-cells`, `#size-cells`, `ranges` |
| `/defaults` | Platform defaults |
| `/options` | Platform options |
The platform expert fails `configure()` when one of these nodes is absent. It also fails when `/chosen` has no usable NVRAM proxy.
### NVRAM proxy
XNU publishes the real `IODTNVRAM` from `/chosen`. The proxy uses the CHRP bank format:
| Property | Contents |
| --- | --- |
| `nvram-bank-size` | Bank size, 32-bit |
| `nvram-bank-count` | Number of banks, 32-bit |
| `nvram-current-bank` | Current bank index, 32-bit |
| `nvram-proxy-data` | The bank image |
The bank image contains a `nvram` header, a `common` header with a two kilobyte data area, and a `free` header. The `common` data area has an Adler-32 checksum at offset 16.
An empty proxy is enough for a device with no persistent NVRAM controller.
### Chosen properties
| Property | Type | Purpose |
| --- | --- | --- |
| `random-seed` | 64 bytes | Kernel entropy seed |
| `unique-chip-id` | 64-bit | Seeds `IOPlatformUUID` with `chip-id` |
| `chip-id` | 32-bit | Platform identity seed |
| `osenvironment` | string | `main`, `recoveryos` or `ramdisk` |
| `dram-base` | 64-bit | Physical DRAM base |
| `dram-size` | 64-bit | Physical DRAM size |
| `crypto-hash-method` | string | Image4 hash method, for example `sha2-384` |
| `root-matching` | dictionary | Root volume match dictionary |
| `ephemeral-storage` | 32-bit | Marks the writable Data volume as ephemeral |
| `darwinboot-memory-size` | 64-bit | Contiguous window request, 256 MiB to 8 GiB in 2 MiB steps |
| `amfi-only-platform-code` | 32-bit | Keeps AMFI in its platform code policy |
| `boot-manifest-hash` | bytes | Boot manifest hash for the owner path |
`unique-chip-id` and `chip-id` seed `IOPlatformUUID`. Without them, callers that pass a zero timeout to `gethostuuid()` wait indefinitely.
### Memory map
`/chosen/memory-map` holds the placeholders DarwinBoot fills:
| Property | Length | Filled with |
| --- | --- | --- |
| `RAMDisk` | 16 bytes | Address and size of the dense Data image |
| `PackedRAMDisk` | 16 bytes | Address and size of the packed Data image |
| `TrustCache` | 16 bytes | Address and size of the trust cache |
A placeholder is required for every input. A missing placeholder makes DarwinBoot return `EFI_COMPROMISED_DATA`.
### CPUs
```c
cpu0 {
name = "cpu0";
state = "running";
reg = <0>;
device_type = "cpu";
compatible = "arm,cortex-a715";
timebase-frequency = <19200000>;
}
```
The first CPU is the boot CPU. Its `reg` value is the MPIDR affinity value. More than one CPU adds a `/psci` node with `compatible = "arm,psci-1.0"` and `method = "smc"` or `"hvc"`.
### arm-io
```c
arm-io {
name = "arm-io";
device_type = "arm-io";
#address-cells = <2>;
#size-cells = <2>;
ranges = <0 0x17000000 0x08000000>;
intc {
name = "intc";
compatible = "arm,gic-v3";
reg = <...>;
};
}
```
`ranges` maps the bus window. Child `reg` values use offsets inside that window. A device below the window base cannot be expressed as a child; the driver then takes its physical addresses from board constants.
### product and buttons
`/product` supplies the product name and model. `/buttons` supplies the button names and `home-button-type`. MobileGestalt and the platform code read these nodes.
### Platform defaults
`/defaults` supplies platform options. For example, `sleep-disabled = <1>` prevents the CPU from parking when suspend is not implemented.
## ADT tool contract
`[payload.adt] tool` names a script in the device repository. The payload step calls it with the options listed in [Payload](/concepts/payload).
The tool writes the tree to `Payloads/kernel.macho.adt`. It must:
1. Generate valid Apple node encoding.
2. Add `random-seed` with a fresh 64-byte value.
3. Add the memory map placeholders for the inputs.
4. Add `unique-chip-id` and `chip-id` when a seed is supplied.
5. Add `root-matching` when a UUID is supplied.
6. Add `ephemeral-storage` for a Data seed.
7. Add `darwinboot-memory-size` when the window override is supplied.
## UEFI device tree
The firmware exposes its own FDT through `EFI_DTB_TABLE`. UniXNU can patch it and pass it to EFI or Linux entries.
An entry selects a tree:
```toml
[[entry]]
id = "linux"
type = "linux"
path = "Payloads/Image"
dtb = "DeviceTree/board.dtb"
```
`dtb = "firmware"` uses the firmware tree. A path uses a file on the payload volume. The tree is copied with 64 KiB of slack, global patches are applied, then entry patches, then `[entry.chosen]`.
Patches support `set`, `delete-prop`, `add-node`, `delete-node` and `overlay`:
```toml
[[dt_patch]]
op = "set"
node = "/chosen"
prop = "bootargs"
value = "console=ttyAMA0"
[[dt_patch]]
op = "overlay"
file = "DeviceTree/board.dtbo"
```
DarwinBoot reads the `.adt` sidecar. An `xnu` entry that sets `dtb` passes a tree in the loader request, and the shipped DarwinBoot refuses a request with a non-null device tree.
## Boot image device tree
The boot image carries a device tree after the gzip stream. The stock bootloader may require nodes that the firmware tree does not have.
```toml
[[bootimg.dtb_node]]
path = "/reserved-memory/splash_region"
props = [
{ name = "label", type = "s", value = "cont_splash_region" },
{ name = "reg", type = "x", value = ["0x0", "0xb8000000", "0x0", "0x2b00000"] },
]
```
`unixnu bootimg` applies these nodes with `fdtput` and verifies integer properties round-trip.
## Reference generators
| Generator | Purpose |
| --- | --- |
| `scripts/qemu-entry/make-dt.py` | QEMU fixture with a single CPU and optional GICv3, VirtIO and NVRAM |
| A device repository `make-<device>-adt.py` | Device tree with board addresses and identity |
The QEMU generator is the smallest complete example. The device page in [Devices](/devices/) records the values for a real board.
## Next steps
- [Payload](/concepts/payload)
- [Device tree matching](/drivers/device-tree-matching)
- [Bring-up order](/porting/bring-up)
+53
View File
@@ -0,0 +1,53 @@
# Porting overview
A device port has two repositories and one board configuration.
| Repository | Contents |
| --- | --- |
| `UniXNU-<device>` | `board.toml`, kernel defconfig, device scripts, drivers, kits, patches and device package |
| `uefi-<device>` | Mu-Silicium UEFI port: device configuration, DSC/FDF, preflight and vendor inputs |
`UniXNU` supplies the build driver, the UEFI boot manager, DarwinBoot and the generic drivers. A device repository overrides any module with the same name.
## Porting checklist
1. [Port the UEFI firmware](/porting/uefi-firmware) so the device reaches a menu, a framebuffer and a payload volume.
2. [Create the device repository](/porting/device-repository) with `board.toml`, a defconfig and the package directory.
3. Select the target IPSW and its identity.
4. [Generate the ADT](/porting/device-tree) with the nodes and properties XNU consumes.
5. Build the payload and the boot image.
6. [Bring up in stages](/porting/bring-up): firmware, menu, payload, kernel entry, platform expert, storage, root, userland.
7. Add device drivers and kits for display, storage, input and power.
8. Build the userdata image and flash it.
## What stays generic
These parts come from `UniXNU` and usually need no device copy:
- The build driver and module resolution.
- UniXNU and its configuration schema.
- DarwinBoot and the loader protocol.
- NativePrelink.
- The 4 KiB XNU patches.
- The generic platform expert, CPU, VirtIO and block drivers.
## What is device specific
- The UEFI firmware port and its vendor binaries.
- The firmware base and size.
- The device tree and the ADT generator.
- The kernel defconfig and the archives it selects.
- The board power, display, touch and GPU drivers.
- The IPSW identity and the trust caches.
- The userdata partition geometry.
## Device pages
The [Devices](/devices/) section lists device repositories. Each page records the values for that device. The porting guide uses placeholders and links to the device pages for concrete values.
## Next steps
- [Device repository](/porting/device-repository)
- [UEFI firmware](/porting/uefi-firmware)
- [Device tree](/porting/device-tree)
- [Bring-up order](/porting/bring-up)
+194
View File
@@ -0,0 +1,194 @@
# UEFI firmware
The UEFI firmware port comes from Mu-Silicium. It supplies the services UniXNU and DarwinBoot use.
## Required services
| Service | Consumer |
| --- | --- |
| `EFI_GRAPHICS_OUTPUT_PROTOCOL` | UniXNU menu, DarwinBoot framebuffer handoff, XNU console |
| `EFI_SIMPLE_TEXT_IN_PROTOCOL` | UniXNU menu input |
| `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` | Configuration, payload and kernel files |
| `EFI_BLOCK_IO_PROTOCOL` | Userdata partition and block slices |
| `EFI_SERIAL_IO_PROTOCOL` | Logs on boards with a UART |
| `EFI_ACPI_TABLE_PROTOCOL` | Optional ACPI table injection |
| `EFI_FIRMWARE_VOLUME2_PROTOCOL` | UniXNUDxe boot option registration |
A board that lacks one of these services limits the stages it can run. Add the driver to the firmware volume or to the UniXNU payload before the dependent stage.
## Mu-Silicium layout
```
Mu-Silicium/
BootShim/
BootShim.S
Resources/
Configs/<device>.toml
Scripts/mkbootimg.py
Platforms/<Vendor>/<device>Pkg/
<device>.dsc
<device>.fdf
Silicon/
Common/
```
The UniXNU driver reads `Resources/Configs/<name>.toml` and `<device>.dsc`. It uses `Resources/Scripts/mkbootimg.py` for the boot image.
## Device configuration
`Resources/Configs/<device>.toml` supplies the addresses and image parameters:
```toml
[boot_shim]
requires_kernel_header = true
[uefi_fd]
base = 0x00000000
size = 0x000000
[boot_image_kernel]
kernel_compression = "gzip"
append_dtb = true
[boot_image]
header_version = 0
base = 0x00000000
kernel_offset = 0x8000
tags_offset = 0x1e00000
pagesize = 4096
```
| Key | Meaning |
| --- | --- |
| `boot_shim.requires_kernel_header` | The bootloader checks the ARM64 header at the start of the image |
| `uefi_fd.base` | Physical address where the firmware volume is loaded |
| `uefi_fd.size` | Exact firmware volume size |
| `boot_image_kernel.kernel_compression` | Compression of the shim and firmware volume |
| `boot_image_kernel.append_dtb` | Append the device tree to the kernel field |
| `boot_image.*` | Android boot image header parameters |
The firmware base and size are compiled into BootShim. They are also checked when the boot image is packed.
## DSC and FDF
The DSC lists the packages, libraries and components. The FDF places the components in the firmware volume.
UniXNU packages use `PACKAGES_PATH`:
```
{uefi}:{tree}/package:{tree}/drivers/DarwinBoot:{edk2}:...
```
`{uefi}` is the Mu-Silicium checkout, `{tree}` is the assembled build tree, and `{edk2}` is the Mu_Basecore checkout. The board file lists every package path.
UniXNU and DarwinBoot are included through their INFs:
```
UniXNUPkg/Application/UniXNU/UniXNU.inf
UniXNUPkg/Driver/UniXNUDxe/UniXNUDxe.inf
DarwinBootPkg/Driver/DarwinBoot.inf
```
The firmware FDF decides whether they are embedded in the firmware volume or copied to the ESP.
## Preflight
`[firmware] preflight` lists scripts in the build tree that run before the EDK II build. A preflight script checks vendor inputs, firmware partitions or board files. `--skip-preflight` skips them.
## Vendor inputs
A Mu-Silicium port needs the vendor binaries for the board, for example:
- UEFI DXE drivers for storage, display, input or power.
- ACPI tables.
- Firmware partition images.
- Configuration data.
The board preflight script validates the files and their hashes.
## Submodules and patches
Mu-Silicium uses submodules for shared trees. The board file lists the ones it needs:
```toml
[firmware]
submodules = ["Common/Mu", "Silicon/Silicium", "Silicon/Qualcomm"]
patches = ["Resources/MuPatches/Gic-Spurious.patch"]
```
A patch in `patches` is applied to the shared edk2 tree for the build and reverted afterwards. The driver checks each patch with `git apply --check` before applying it.
## Build
With the device repository selected:
```sh
./unixnu build firmware --uefi /path/to/Mu-Silicium
```
The driver:
1. Runs the preflight scripts.
2. Initializes the listed submodules.
3. Builds BaseTools when `GenFw` is absent.
4. Applies the board patches.
5. Runs EDK II `build` with the DSC, `AARCH64`, `CLANGPDB` and the board defines.
6. Reverts the patches.
7. Checks the firmware volume size and path.
The output path comes from `[firmware] fd`:
```toml
fd = "Build/<device>Pkg/{target}_CLANGPDB/FV/SILICIUM_UEFI.fd"
```
## Standalone build
The Mu-Silicium checkout can be built with its own script:
```sh
cd Mu-Silicium
python3 build_uefi.py -d <device>
```
The UniXNU driver performs the same build with the board defines and the shared edk2 patches.
## Boot image integration
`unixnu bootimg` compiles BootShim with the firmware base and size:
```sh
clang --target=aarch64-none-elf -c \
-DFD_BASE=<base> -DFD_SIZE=<size> \
-DREQUIRES_KERNEL_HEADER=<0 or 1> \
BootShim/BootShim.S -o shim.o
```
It appends the firmware volume, compresses the pair with gzip, appends the device tree and packs the Android boot image.
## Verification
| Check | Method |
| --- | --- |
| Firmware volume size | The driver compares the output size with `[uefi_fd] size` |
| Shim header | The driver checks `ARM\x64` and the base and size fields |
| Boot image parts | The driver unpacks the image and compares every part |
| Menu | The serial log contains `UNIXNU:` lines |
| Payload | The menu lists the `xnu` entry and DarwinBoot logs the handoff |
## Adding a UEFI driver
A driver can live in the firmware volume or in the payload.
| Location | Build | Load |
| --- | --- | --- |
| Firmware volume | Add the INF to the DSC and FDF | Firmware dispatches it, or UniXNUDxe starts the application |
| Payload | Build a standalone `.efi` | `[[driver]]` in `unixnu.toml` or a kit |
See [UEFI drivers](/drivers/uefi-drivers) for the INF, entry point and injection details.
## Next steps
- [Device tree](/porting/device-tree)
- [Bring-up order](/porting/bring-up)
- [UEFI drivers](/drivers/uefi-drivers)
+1
View File
@@ -0,0 +1 @@
unixnu.com
+21
View File
@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="1000" height="1000">
<title>UniXNU</title>
<g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round">
<path stroke-width="24" d="M515 224C460 204 420 192 360 194C280 197 232 272 226 372C220 477 260 582 320 652C360 697 420 700 460 686C490 676 540 676 570 686C610 700 665 697 700 652C728 614 753 564 770 518C712 492 680 436 682 376C684 324 710 286 756 260C722 222 665 198 602 202C566 205 542 214 515 224Z"/>
<path stroke-width="22" d="M501 188C503 148 524 118 554 90L528 64L648 48L636 152L609 126C584 156 554 182 501 188Z"/>
<path stroke-width="22" d="M455 363L545 363C563.778 363 579 378.222 579 397L579 487C579 505.778 563.778 521 545 521L455 521C436.222 521 421 505.778 421 487L421 397C421 378.222 436.222 363 455 363Z"/>
<path stroke-width="16" d="M456 320L456 352"/>
<path stroke-width="16" d="M456 532L456 564"/>
<path stroke-width="16" d="M500 320L500 352"/>
<path stroke-width="16" d="M500 532L500 564"/>
<path stroke-width="16" d="M544 320L544 352"/>
<path stroke-width="16" d="M544 532L544 564"/>
<path stroke-width="16" d="M378 398L410 398"/>
<path stroke-width="16" d="M590 398L622 398"/>
<path stroke-width="16" d="M378 442L410 442"/>
<path stroke-width="16" d="M590 442L622 442"/>
<path stroke-width="16" d="M378 486L410 486"/>
<path stroke-width="16" d="M590 486L622 486"/>
</g>
<path fill="#fff" fill-rule="evenodd" d="M158.1 949.0C154.7 948.8 150.2 948.2 148.0 947.6C147.2 947.4 145.9 947.1 145.1 947.0C144.3 946.8 143.2 946.6 142.6 946.4C142.1 946.2 140.8 945.8 139.8 945.5C138.7 945.2 137.4 944.8 136.8 944.5C136.1 944.2 135.3 943.9 135.0 943.9C132.9 943.3 124.8 939.1 123.0 937.8C122.5 937.3 122.0 937.0 121.9 937.0C120.9 937.0 112.4 929.0 109.6 925.5C106.5 921.7 103.4 915.9 101.4 910.5C101.0 909.5 100.6 908.4 100.5 908.2C100.3 907.8 99.1 903.0 98.5 900.2C97.1 893.6 97.2 896.3 97.1 836.9C97.0 798.6 97.1 782.4 97.3 782.0C97.5 781.5 97.8 781.5 114.5 781.5C127.8 781.5 131.5 781.6 131.7 781.8C131.8 782.0 131.9 806.3 132.0 835.9L132.1 889.6L132.7 892.4C134.2 899.7 137.2 905.3 142.5 910.5C146.7 914.6 150.8 916.8 157.6 918.5C161.3 919.5 174.6 919.4 178.0 918.4C186.1 916.2 191.3 912.8 195.5 907.2C196.9 905.4 200.0 899.4 200.0 898.7C200.0 898.5 200.2 897.7 200.5 896.9C202.3 891.2 202.2 891.6 202.2 833.7C202.2 805.4 202.3 782.1 202.4 781.9C202.5 781.5 204.3 781.5 219.5 781.5C236.2 781.5 236.5 781.5 236.7 782.0C236.9 782.3 237.0 799.0 236.9 837.4C236.8 891.9 236.8 892.4 236.3 895.8C235.6 900.1 234.6 905.4 234.3 906.0C234.1 906.3 234.0 906.7 234.0 907.0C234.0 907.3 233.8 908.1 233.5 908.7C233.3 909.3 232.8 910.5 232.5 911.4C227.5 924.5 218.2 935.0 205.9 941.2C203.4 942.5 198.5 944.4 196.2 945.1C189.1 947.3 186.8 947.8 180.1 948.7C176.9 949.1 162.8 949.3 158.1 949.0ZM832.5 948.7C829.2 948.5 826.4 948.2 822.2 947.5C803.7 944.3 789.2 933.9 781.7 918.5C780.1 915.1 779.8 914.4 779.1 912.2C778.9 911.6 778.6 910.7 778.4 910.2C777.7 908.4 776.1 901.9 775.9 899.9C775.8 899.0 775.6 898.1 775.5 897.8C774.9 896.2 774.8 887.5 774.7 836.3C774.7 796.0 774.8 782.1 775.0 781.8C775.4 781.4 787.6 781.3 787.9 781.8C788.0 782.0 788.2 806.3 788.3 835.9C788.4 892.9 788.4 892.9 789.6 898.0C789.8 898.9 790.1 900.1 790.2 900.8C790.7 902.8 791.9 906.6 792.7 908.2C794.7 912.7 796.0 915.0 798.3 918.0C807.7 930.2 820.8 935.9 839.4 935.9C847.4 935.9 856.2 934.5 860.6 932.5C861.2 932.2 861.8 932.0 862.0 932.0C862.1 932.0 863.4 931.5 864.7 930.8C874.0 926.3 882.0 917.8 885.4 908.9C885.7 907.9 886.2 906.7 886.4 906.2C886.9 905.0 887.8 901.1 888.8 895.5C889.0 894.3 889.1 878.4 889.2 838.0C889.2 797.5 889.3 782.1 889.5 781.8C889.8 781.6 891.2 781.5 896.3 781.5C902.5 781.5 902.7 781.5 903.0 782.0C903.5 782.9 903.2 894.0 902.7 897.2C902.0 901.4 901.0 906.3 900.3 908.4C899.5 910.7 898.5 913.8 898.1 914.9C897.6 916.2 895.0 921.2 894.3 922.2C894.0 922.6 893.8 923.0 893.7 923.2C893.0 925.1 886.4 932.7 882.7 935.8C881.7 936.7 877.3 939.7 876.6 940.0C876.4 940.2 875.7 940.5 875.1 940.9C874.6 941.2 874.0 941.6 873.8 941.7C873.5 941.7 872.4 942.2 871.3 942.8C870.2 943.3 869.2 943.8 869.1 943.8C869.0 943.8 868.6 943.9 868.2 944.1C867.2 944.6 864.9 945.3 861.1 946.2C852.5 948.5 842.2 949.4 832.5 948.7ZM263.1 946.1C262.7 945.8 262.5 945.4 262.4 943.9C262.0 939.5 262.2 825.2 262.5 824.5C262.8 824.0 263.4 824.0 279.1 824.0C292.4 824.0 295.5 824.1 295.7 824.3C295.9 824.6 296.0 827.5 296.1 833.5C296.1 840.0 296.2 842.4 296.4 842.4C296.6 842.5 296.8 842.3 296.9 842.0C297.1 841.1 300.3 836.7 301.8 834.9C307.6 828.5 314.0 824.7 322.9 822.6C330.6 820.8 343.9 821.3 350.4 823.6C357.9 826.3 360.5 828.0 365.5 833.0C370.3 837.8 373.0 842.1 375.2 848.6C378.5 858.0 378.8 863.8 378.7 905.0C378.6 936.8 378.4 945.4 377.8 946.0C377.2 946.8 346.0 946.7 345.3 946.0C344.8 945.6 344.8 944.0 344.8 910.2C344.7 876.9 344.7 874.7 344.2 871.3C344.0 869.3 343.6 867.2 343.4 866.6C343.2 866.1 343.0 865.4 343.0 865.2C343.0 864.6 341.3 860.5 340.9 860.0C340.7 859.8 340.4 859.3 340.2 858.8C339.1 856.8 335.1 853.2 332.5 851.8C324.3 847.6 313.0 848.8 306.2 854.6C302.1 858.1 298.7 863.2 297.9 867.0C297.8 867.5 297.6 868.0 297.5 868.1C297.4 868.3 297.2 869.2 297.0 870.2C296.8 871.3 296.5 873.0 296.3 874.1C296.1 875.7 296.0 882.2 296.0 910.9C296.0 942.9 296.0 945.7 295.6 946.0C294.9 946.6 263.9 946.7 263.1 946.1ZM403.0 946.3C402.8 946.1 402.6 945.3 402.5 943.9C402.1 939.9 402.2 824.6 402.6 824.3C402.8 824.1 408.6 824.0 419.2 823.9C434.1 823.7 435.6 823.7 436.1 824.1L436.8 824.5L436.7 885.0C436.6 941.8 436.6 945.5 436.2 945.9C435.8 946.3 434.5 946.3 419.6 946.4C409.2 946.5 403.2 946.4 403.0 946.3ZM455.4 946.1C455.3 945.9 455.8 945.0 456.4 944.1C457.1 943.2 458.0 941.9 458.4 941.3C458.9 940.6 459.8 939.4 460.4 938.5C461.0 937.6 462.5 935.5 463.6 933.9C464.8 932.2 466.6 929.7 467.7 928.2C468.8 926.7 469.9 925.1 470.1 924.8C470.4 924.4 471.2 923.3 471.8 922.3C472.5 921.4 474.1 919.1 475.4 917.3C476.8 915.5 478.4 913.3 479.0 912.4C479.6 911.5 480.7 909.9 481.5 908.9C483.2 906.6 484.5 904.8 485.6 903.1C486.1 902.4 487.2 900.9 488.1 899.7C488.9 898.4 489.8 897.2 490.0 896.9C490.2 896.6 491.3 895.1 492.5 893.5C493.7 891.9 494.8 890.3 495.0 890.0C495.2 889.6 496.4 888.0 497.6 886.3C498.8 884.7 500.8 881.8 502.2 879.9C503.5 878.0 504.9 876.0 505.3 875.5C505.7 875.0 506.7 873.6 507.6 872.2C508.5 870.9 509.8 869.2 510.3 868.4C510.9 867.5 511.9 866.1 512.6 865.1C513.3 864.2 513.8 863.3 513.8Line truncated
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

+21
View File
@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" width="1000" height="1000">
<title>UniXNU</title>
<g fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round">
<path stroke-width="24" d="M515 224C460 204 420 192 360 194C280 197 232 272 226 372C220 477 260 582 320 652C360 697 420 700 460 686C490 676 540 676 570 686C610 700 665 697 700 652C728 614 753 564 770 518C712 492 680 436 682 376C684 324 710 286 756 260C722 222 665 198 602 202C566 205 542 214 515 224Z"/>
<path stroke-width="22" d="M501 188C503 148 524 118 554 90L528 64L648 48L636 152L609 126C584 156 554 182 501 188Z"/>
<path stroke-width="22" d="M455 363L545 363C563.778 363 579 378.222 579 397L579 487C579 505.778 563.778 521 545 521L455 521C436.222 521 421 505.778 421 487L421 397C421 378.222 436.222 363 455 363Z"/>
<path stroke-width="16" d="M456 320L456 352"/>
<path stroke-width="16" d="M456 532L456 564"/>
<path stroke-width="16" d="M500 320L500 352"/>
<path stroke-width="16" d="M500 532L500 564"/>
<path stroke-width="16" d="M544 320L544 352"/>
<path stroke-width="16" d="M544 532L544 564"/>
<path stroke-width="16" d="M378 398L410 398"/>
<path stroke-width="16" d="M590 398L622 398"/>
<path stroke-width="16" d="M378 442L410 442"/>
<path stroke-width="16" d="M590 442L622 442"/>
<path stroke-width="16" d="M378 486L410 486"/>
<path stroke-width="16" d="M590 486L622 486"/>
</g>
<path fill="#000" fill-rule="evenodd" d="M158.1 949.0C154.7 948.8 150.2 948.2 148.0 947.6C147.2 947.4 145.9 947.1 145.1 947.0C144.3 946.8 143.2 946.6 142.6 946.4C142.1 946.2 140.8 945.8 139.8 945.5C138.7 945.2 137.4 944.8 136.8 944.5C136.1 944.2 135.3 943.9 135.0 943.9C132.9 943.3 124.8 939.1 123.0 937.8C122.5 937.3 122.0 937.0 121.9 937.0C120.9 937.0 112.4 929.0 109.6 925.5C106.5 921.7 103.4 915.9 101.4 910.5C101.0 909.5 100.6 908.4 100.5 908.2C100.3 907.8 99.1 903.0 98.5 900.2C97.1 893.6 97.2 896.3 97.1 836.9C97.0 798.6 97.1 782.4 97.3 782.0C97.5 781.5 97.8 781.5 114.5 781.5C127.8 781.5 131.5 781.6 131.7 781.8C131.8 782.0 131.9 806.3 132.0 835.9L132.1 889.6L132.7 892.4C134.2 899.7 137.2 905.3 142.5 910.5C146.7 914.6 150.8 916.8 157.6 918.5C161.3 919.5 174.6 919.4 178.0 918.4C186.1 916.2 191.3 912.8 195.5 907.2C196.9 905.4 200.0 899.4 200.0 898.7C200.0 898.5 200.2 897.7 200.5 896.9C202.3 891.2 202.2 891.6 202.2 833.7C202.2 805.4 202.3 782.1 202.4 781.9C202.5 781.5 204.3 781.5 219.5 781.5C236.2 781.5 236.5 781.5 236.7 782.0C236.9 782.3 237.0 799.0 236.9 837.4C236.8 891.9 236.8 892.4 236.3 895.8C235.6 900.1 234.6 905.4 234.3 906.0C234.1 906.3 234.0 906.7 234.0 907.0C234.0 907.3 233.8 908.1 233.5 908.7C233.3 909.3 232.8 910.5 232.5 911.4C227.5 924.5 218.2 935.0 205.9 941.2C203.4 942.5 198.5 944.4 196.2 945.1C189.1 947.3 186.8 947.8 180.1 948.7C176.9 949.1 162.8 949.3 158.1 949.0ZM832.5 948.7C829.2 948.5 826.4 948.2 822.2 947.5C803.7 944.3 789.2 933.9 781.7 918.5C780.1 915.1 779.8 914.4 779.1 912.2C778.9 911.6 778.6 910.7 778.4 910.2C777.7 908.4 776.1 901.9 775.9 899.9C775.8 899.0 775.6 898.1 775.5 897.8C774.9 896.2 774.8 887.5 774.7 836.3C774.7 796.0 774.8 782.1 775.0 781.8C775.4 781.4 787.6 781.3 787.9 781.8C788.0 782.0 788.2 806.3 788.3 835.9C788.4 892.9 788.4 892.9 789.6 898.0C789.8 898.9 790.1 900.1 790.2 900.8C790.7 902.8 791.9 906.6 792.7 908.2C794.7 912.7 796.0 915.0 798.3 918.0C807.7 930.2 820.8 935.9 839.4 935.9C847.4 935.9 856.2 934.5 860.6 932.5C861.2 932.2 861.8 932.0 862.0 932.0C862.1 932.0 863.4 931.5 864.7 930.8C874.0 926.3 882.0 917.8 885.4 908.9C885.7 907.9 886.2 906.7 886.4 906.2C886.9 905.0 887.8 901.1 888.8 895.5C889.0 894.3 889.1 878.4 889.2 838.0C889.2 797.5 889.3 782.1 889.5 781.8C889.8 781.6 891.2 781.5 896.3 781.5C902.5 781.5 902.7 781.5 903.0 782.0C903.5 782.9 903.2 894.0 902.7 897.2C902.0 901.4 901.0 906.3 900.3 908.4C899.5 910.7 898.5 913.8 898.1 914.9C897.6 916.2 895.0 921.2 894.3 922.2C894.0 922.6 893.8 923.0 893.7 923.2C893.0 925.1 886.4 932.7 882.7 935.8C881.7 936.7 877.3 939.7 876.6 940.0C876.4 940.2 875.7 940.5 875.1 940.9C874.6 941.2 874.0 941.6 873.8 941.7C873.5 941.7 872.4 942.2 871.3 942.8C870.2 943.3 869.2 943.8 869.1 943.8C869.0 943.8 868.6 943.9 868.2 944.1C867.2 944.6 864.9 945.3 861.1 946.2C852.5 948.5 842.2 949.4 832.5 948.7ZM263.1 946.1C262.7 945.8 262.5 945.4 262.4 943.9C262.0 939.5 262.2 825.2 262.5 824.5C262.8 824.0 263.4 824.0 279.1 824.0C292.4 824.0 295.5 824.1 295.7 824.3C295.9 824.6 296.0 827.5 296.1 833.5C296.1 840.0 296.2 842.4 296.4 842.4C296.6 842.5 296.8 842.3 296.9 842.0C297.1 841.1 300.3 836.7 301.8 834.9C307.6 828.5 314.0 824.7 322.9 822.6C330.6 820.8 343.9 821.3 350.4 823.6C357.9 826.3 360.5 828.0 365.5 833.0C370.3 837.8 373.0 842.1 375.2 848.6C378.5 858.0 378.8 863.8 378.7 905.0C378.6 936.8 378.4 945.4 377.8 946.0C377.2 946.8 346.0 946.7 345.3 946.0C344.8 945.6 344.8 944.0 344.8 910.2C344.7 876.9 344.7 874.7 344.2 871.3C344.0 869.3 343.6 867.2 343.4 866.6C343.2 866.1 343.0 865.4 343.0 865.2C343.0 864.6 341.3 860.5 340.9 860.0C340.7 859.8 340.4 859.3 340.2 858.8C339.1 856.8 335.1 853.2 332.5 851.8C324.3 847.6 313.0 848.8 306.2 854.6C302.1 858.1 298.7 863.2 297.9 867.0C297.8 867.5 297.6 868.0 297.5 868.1C297.4 868.3 297.2 869.2 297.0 870.2C296.8 871.3 296.5 873.0 296.3 874.1C296.1 875.7 296.0 882.2 296.0 910.9C296.0 942.9 296.0 945.7 295.6 946.0C294.9 946.6 263.9 946.7 263.1 946.1ZM403.0 946.3C402.8 946.1 402.6 945.3 402.5 943.9C402.1 939.9 402.2 824.6 402.6 824.3C402.8 824.1 408.6 824.0 419.2 823.9C434.1 823.7 435.6 823.7 436.1 824.1L436.8 824.5L436.7 885.0C436.6 941.8 436.6 945.5 436.2 945.9C435.8 946.3 434.5 946.3 419.6 946.4C409.2 946.5 403.2 946.4 403.0 946.3ZM455.4 946.1C455.3 945.9 455.8 945.0 456.4 944.1C457.1 943.2 458.0 941.9 458.4 941.3C458.9 940.6 459.8 939.4 460.4 938.5C461.0 937.6 462.5 935.5 463.6 933.9C464.8 932.2 466.6 929.7 467.7 928.2C468.8 926.7 469.9 925.1 470.1 924.8C470.4 924.4 471.2 923.3 471.8 922.3C472.5 921.4 474.1 919.1 475.4 917.3C476.8 915.5 478.4 913.3 479.0 912.4C479.6 911.5 480.7 909.9 481.5 908.9C483.2 906.6 484.5 904.8 485.6 903.1C486.1 902.4 487.2 900.9 488.1 899.7C488.9 898.4 489.8 897.2 490.0 896.9C490.2 896.6 491.3 895.1 492.5 893.5C493.7 891.9 494.8 890.3 495.0 890.0C495.2 889.6 496.4 888.0 497.6 886.3C498.8 884.7 500.8 881.8 502.2 879.9C503.5 878.0 504.9 876.0 505.3 875.5C505.7 875.0 506.7 873.6 507.6 872.2C508.5 870.9 509.8 869.2 510.3 868.4C510.9 867.5 511.9 866.1 512.6 865.1C513.3 864.2 513.8 863.3 513.8Line truncated
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

+278
View File
@@ -0,0 +1,278 @@
# Boot configuration
The UniXNU boot manager reads `\EFI\UniXNU\unixnu.toml` from the volume it was started from, then from every other volume. A kit directory contains `Kit.toml` with the same match and injection sections.
## unixnu.toml
```toml
version = 1
[boot]
timeout = 5
rotate = 0
fallback_timeout = 10
connect_all = true
default = "xnu"
[ui]
title = "UniXNU"
scale = 0
logo = true
console = "auto"
```
### [boot]
| Key | Default | Meaning |
| --- | --- | --- |
| `timeout` | 5 | Seconds. Negative waits for input |
| `rotate` | 0 | Idle highlight rotation in seconds. 0 disables it |
| `fallback_timeout` | 10 | Timeout when no input device exists |
| `connect_all` | true | Calls `ConnectController` on every handle |
| `default` | first entry | Entry id or index |
### [ui]
| Key | Default | Meaning |
| --- | --- | --- |
| `title` | `UniXNU` | Menu title |
| `scale` | 0 | Glyph scale. 0 selects automatically |
| `logo` | true | Draws the logo |
| `console` | `auto` | `auto`, `graphics`, `text` or `none` |
### [[driver]]
```toml
[[driver]]
path = "Drivers/MyDriver.efi"
args = "board=1"
connect = true
enabled = true
```
| Key | Default | Meaning |
| --- | --- | --- |
| `path` | required | File path or `fv:<GUID>` |
| `args` | | Load options |
| `connect` | true | Connects controllers after the driver starts |
| `enabled` | true | `false` skips the entry |
A kit driver uses `file` instead of `path`.
### [[dt_patch]]
```toml
[[dt_patch]]
op = "set"
node = "/chosen"
prop = "bootargs"
value = "console=ttyAMA0"
type = "string"
```
| Key | Meaning |
| --- | --- |
| `op` | `set`, `delete-prop`, `add-node`, `delete-node` or `overlay` |
| `node` | Absolute node path |
| `prop` | Property name for `set` and `delete-prop` |
| `value` | Value for `set` |
| `type` | `string`, `stringlist`, `u32`, `u64`, `bytes` or `empty` |
| `file` | Overlay file for `overlay` |
Patches apply to EFI and Linux entries. The Darwin path uses the `.adt` sidecar.
### [[block_slice]]
```toml
[[block_slice]]
partition = "<GPT name>"
name = "<slice name>"
offset = 0
length = 0
writable = false
enabled = true
```
| Key | Meaning |
| --- | --- |
| `partition` | GPT partition name |
| `name` | Label in the device path |
| `offset` | Byte offset from the partition start |
| `length` | Byte length. 0 means to the end |
| `writable` | Publishes a writable block device |
| `enabled` | `false` skips the slice |
The slice appears as a whole disk with its own device path. A child partition scheme or file system driver can bind to it.
### ACPI
```toml
[acpi]
add = ["Tables/DSDT.aml", "Tables/SSDT.aml"]
```
Each table is installed through `EFI_ACPI_TABLE_PROTOCOL`. The header length must match the file size.
### Kits
```toml
[[kit]]
path = "Kits/MyKit.kit"
enabled = true
[kits]
autoload = true
directory = "Kits"
```
Explicit kits load first. With `autoload`, every `*.kit` directory under `directory` loads in name order.
### [[entry]]
```toml
[[entry]]
id = "xnu"
title = "UniXNU"
type = "xnu"
path = "Payloads/kernel.macho"
args = "-v serial=3"
initrd = "Payloads/scratch-data.img"
enabled = true
```
| Key | Meaning |
| --- | --- |
| `id` | Entry identifier |
| `title` | Menu title |
| `type` | `efi`, `linux`, `action` or `xnu` |
| `path` | EFI image, Linux image or kernel path |
| `args` | Load options or kernel command line |
| `dtb` | `firmware`, a file path, or absent |
| `initrd` | Initrd path |
| `action` | `reset`, `shutdown` or `exit` for `action` entries |
| `enabled` | `false` skips the entry |
The built-in configuration has `exit`, `reset` and `shutdown` entries. When no file is found, removable loaders are added in front of them.
### [entry.chosen]
```toml
[entry.chosen]
bootargs = "console=ttyAMA0"
```
Every key becomes a `/chosen` property. These patches apply after the entry patches.
### [[entry.kext]]
```toml
[[entry.kext]]
bundle = "Kexts/MyDriver.kext"
plist = "Contents/Info.plist"
executable = "Contents/MacOS/MyDriver"
enabled = true
```
Entry kexts apply only to the entry that declares them.
### [[entry.variable]]
```toml
[[entry.variable]]
guid = "00000000-0000-0000-0000-000000000000"
name = "MyVariable"
value = "hello"
type = "ascii"
volatile = true
```
| Key | Meaning |
| --- | --- |
| `guid` | Variable GUID |
| `name` | Variable name |
| `value` | Value |
| `type` | `ascii`, `ucs2`, `u8`, `u16`, `u32`, `u64` or `bytes` |
| `volatile` | `false` adds `EFI_VARIABLE_NON_VOLATILE` |
The variables are set before the entry starts.
### [[entry.dt_patch]]
The entry accepts its own `[[dt_patch]]` array. Entry patches apply after global patches.
## Kit.toml
```toml
schema = 1
[kit]
name = "MyKit"
version = "1.0.0"
description = "Board support"
[match]
arch = "AARCH64"
fdt_compatible = ["vendor,board"]
firmware_vendor = ["Vendor"]
requires_protocol = ["00000000-0000-0000-0000-000000000000"]
[[driver]]
file = "MyDriver.efi"
args = "from-kit"
[[overlay]]
file = "board.dtbo"
[[dt_patch]]
op = "set"
node = "/chosen"
prop = "from-kit"
value = "1"
type = "u32"
[[acpi]]
file = "DSDT.aml"
[[kext]]
bundle = "MyDriver.kext"
entry = "xnu"
```
| Section | Meaning |
| --- | --- |
| `[kit]` | Name, version and description |
| `[match]` | All present keys must match |
| `[[driver]]` | Driver file relative to the kit |
| `[[overlay]]` | Device tree overlay relative to the kit |
| `[[dt_patch]]` | Device tree patch |
| `[[acpi]]` | ACPI table relative to the kit |
| `[[kext]]` | Kext bundle scoped to an entry id |
Match keys:
| Key | Meaning |
| --- | --- |
| `arch` | `AARCH64` |
| `fdt_compatible` | Strings in the firmware root `compatible` list |
| `firmware_vendor` | Substring of `gST->FirmwareVendor` |
| `requires_protocol` | GUIDs that must be installed |
Within a key, any listed value matches. Across keys, every key must match.
## Menu behavior
| Condition | Result |
| --- | --- |
| Input device present | Countdown runs to the timeout |
| No input device | `fallback_timeout` applies |
| Negative timeout with input | Waits for a key |
| Negative timeout without input | `fallback_timeout` applies |
| Failed boot | Returns to the menu without a countdown |
The menu shows the configuration source, the entry list, the injection summary and the first error.
## Next steps
- [Payload](/concepts/payload)
- [Kext bundles](/drivers/kexts)
- [Device configuration](/reference/configuration)
+145
View File
@@ -0,0 +1,145 @@
# Command line
```
./unixnu <command> [options]
```
Options follow the command. The driver reads a device repository from `-D`, `UNIXNU_DEVICE` or `out/state.json`.
## Commands
### defconfig
```
unixnu defconfig [NAME]
```
Lists `configs/*_defconfig` in the device repository. With a name, copies the file to `out/<board>/.config` and parses it.
### fetch
```
unixnu fetch [NAME...] [--update]
```
Fetches git sources. With names, fetches only those sources. `--update` ignores `unixnu.lock` and moves to the newest ref. Writes the commits to `unixnu.lock` in the device repository.
### tree
```
unixnu tree
```
Resolves modules, fetches the references the steps need, and assembles `out/<board>/tree`.
### ipsw
```
unixnu ipsw info
unixnu ipsw fetch [COMPONENT...]
```
`info` prints the product, build, device class and every manifest component with its size and path. `fetch` extracts the named components, or the `[ipsw] fetch` list when no component is named.
### build
```
unixnu build [kernel] [package] [firmware] [payload] [prelink] [userdata]
```
Runs the named steps in this order: kernel, prelink, package, payload, firmware. With no step, runs kernel, package, firmware and payload. `userdata` runs alone; `all` covers the other four.
`build prelink` repeats the prelink without rebuilding XNU.
`build userdata --dry-run` checks the IPSW and the partition size.
### bootimg
```
unixnu bootimg [--fd FILE] [--dtb FILE] [--ramdisk FILE|none] [-o FILE]
```
Packs the boot image. The firmware volume defaults to the output under `--uefi`. The ramdisk defaults to the payload output when it exists.
### status
```
unixnu status
```
Prints the device, output root, configuration path and the origin of every module. Marks local modules and reports that they are not pinned.
### clean
```
unixnu clean
```
Removes `tree`, `logs`, `kernel`, `package`, `boot` and `edk2-conf` under the board output. Keeps fetched sources.
## Options
| Option | Meaning |
| --- | --- |
| `-D DIR`, `--device DIR` | Device repository |
| `--out DIR` | Output root |
| `-j N`, `--jobs N` | Parallel jobs |
| `--local DIR` | Local tree with this repository's layout |
| `--local-drivers DIR` | Local drivers directory |
| `--local-kits DIR` | Local kits directory |
| `--local-patches DIR` | Local patches directory |
| `--local-scripts DIR` | Local scripts directory |
| `--local-package DIR` | Local package directory |
| `--local-module [KIND:]NAME=PATH` | One local module. Repeatable |
| `--uefi DIR` | Mu-Silicium checkout |
| `--edk2 DIR` | Mu_Basecore checkout |
| `--kernelcache FILE` | Original decompressed kernelcache |
| `--skip-preflight` | Skips the board preflight scripts |
| `--kernel FILE` | Prelinked kernel for the payload |
| `--kernel-arg NAME[=VALUE]` | Extra kernel argument. Repeatable |
| `--trust-cache FILE` | Trust cache bundle |
| `--skip-setup` | Marks Setup as done in a copy of the Data seed |
| `--data-ramdisk FILE` | Data ramdisk seed |
| `--unique-chip-id HEX` | Platform seed for the ADT |
| `--ipsw PATH_OR_URL` | Target IPSW |
| `--dry-run` | Userdata dry run |
| `--target DEBUG\|RELEASE` | EDK II target |
| `--fd FILE` | Firmware volume for bootimg |
| `--dtb FILE` | Compiled device tree for bootimg |
| `--ramdisk FILE\|none` | Ramdisk for bootimg |
| `-o FILE`, `--output FILE` | Boot image output |
## Environment
| Variable | Equivalent |
| --- | --- |
| `UNIXNU_DEVICE` | `-D` |
| `UNIXNU_OUT` | `--out` |
| `UNIXNU_UEFI` | `--uefi` |
| `UNIXNU_EDK2` | `--edk2` |
| `UNIXNU_KERNELCACHE` | `--kernelcache` |
| `UNIXNU_DTB` | `--dtb` |
| `UNIXNU_IPSW` | `--ipsw` |
| `UNIXNU_UNIQUE_CHIP_ID` | `--unique-chip-id` |
| `JOBS` | `-j` |
## Exit behavior
| Condition | Result |
| --- | --- |
| Missing device repository | Error with the `-D` hint |
| Missing configuration for a kernel build | Error with the `defconfig` hint |
| Missing Mu-Silicium checkout for firmware | Error before fetching |
| IPSW identity mismatch | Error naming the expected and actual image |
| sha256 pin mismatch | Error naming the member |
| Firmware volume size mismatch | Error naming the size |
| Payload larger than `size_mib` | Error with the size to raise |
| Invalid kernel argument | Error naming the argument |
| Missing platform seed | Error with the `--unique-chip-id` hint |
| Example device repository | Error before build or bootimg |
## Next steps
- [Device configuration](/reference/configuration)
- [Output layout](/reference/output-layout)
- [Troubleshooting](/reference/troubleshooting)
+205
View File
@@ -0,0 +1,205 @@
# Device configuration
A device repository has four configuration files: `board.toml`, `configs/<name>_defconfig`, `feeds.toml` and `local.toml`.
## board.toml
### [board]
| Key | Type | Meaning |
| --- | --- | --- |
| `name` | string | Output directory name |
| `soc` | string | SoC identifier |
| `description` | string | Human readable description |
| `kit` | string | Device package folder under `kits/` |
| `example` | boolean | Marks a sample. Build and bootimg refuse it |
### [ipsw]
| Key | Type | Meaning |
| --- | --- | --- |
| `url` | string | Default remote IPSW |
| `product` | string | Product type, for example `iPad7,1` |
| `version` | string | Product version |
| `build` | string | Product build |
| `device_class` | string | Device class from the manifest |
| `chip_id` | integer | `ApChipID` |
| `board_id` | integer | `ApBoardID` |
| `fetch` | array | Components extracted by `ipsw fetch` |
### [ipsw.sha256]
| Key | Type | Meaning |
| --- | --- | --- |
| member file | string | Expected SHA-256 of the extracted member |
### [userdata]
| Key | Type | Meaning |
| --- | --- | --- |
| `first_lba` | integer | First LBA of the userdata GPT entry |
| `last_lba` | integer | Last LBA of the userdata GPT entry |
| `lba_bytes` | integer | Logical block size |
| `windows` | array | `{ name, component }` entries |
### [kernel]
| Key | Type | Meaning |
| --- | --- | --- |
| `defconfig` | string | File name under `configs/` |
### [kernel.prelink]
| Key | Type | Meaning |
| --- | --- | --- |
| `tool` | string | Prelink tool path in the build tree |
| `kernel` | string | Native kernel path in the build tree |
| `slide` | string | Address slide |
| `roots` | array | Bundle identifiers kept from the kernelcache |
| `early_security_init` | string | `identifier=address` |
| `kernelcache` | string | Optional path to a local kernelcache |
| `kernelcache_sha256` | string | Expected kernelcache hash |
| `extra_args` | array | Extra prelink arguments |
### [firmware]
| Key | Type | Meaning |
| --- | --- | --- |
| `config` | string | Mu-Silicium `Resources/Configs/<name>.toml` |
| `dsc` | string | DSC path in the checkout |
| `fd` | string | Firmware volume path, `{target}` replaced |
| `packages` | array | `PACKAGES_PATH` entries |
| `preflight` | array | Preflight scripts in the build tree |
| `patches` | array | Patches applied to edk2 for the build |
| `submodules` | array | Mu-Silicium submodules to initialize |
### [firmware.defines]
| Key | Type | Meaning |
| --- | --- | --- |
| any | string | `-D NAME=VALUE` for the EDK II build |
### [bootimg.image]
| Key | Type | Meaning |
| --- | --- | --- |
| `base` | integer | Image base address |
| `kernel_offset` | integer | Kernel offset |
| `tags_offset` | integer | Tags offset |
| `pagesize` | integer | Image page size |
| `header_version` | integer | Android boot image header version |
### [[bootimg.dtb_node]]
| Key | Type | Meaning |
| --- | --- | --- |
| `path` | string | Device tree node path |
| `props` | array | `{ name, type, value }` |
Types are `s` for string, `x` for hex cells, `i` for decimal cells, `u` for unsigned cells and `empty`.
### [payload]
| Key | Type | Meaning |
| --- | --- | --- |
| `volume` | string | FAT label, at most 11 characters |
| `size_mib` | integer | Volume size |
| `loader` | string | `firmware` or a driver path mode |
| `title` | string | Menu title |
| `dram_size` | string | DRAM size for the ADT |
| `panic_screen` | boolean | Patch the panic screen into the payload kernel |
| `trust_cache_sha256` | array | Accepted trust cache hashes |
| `kernel_args` | array | Default kernel arguments |
| `timeout` | integer | Menu timeout |
| `skip_setup_tool` | string | Script used by `--skip-setup` |
### [payload.adt]
| Key | Type | Meaning |
| --- | --- | --- |
| `tool` | string | ADT generator path |
| `chip_id` | string | Chip ID for the ADT |
| `owned_memory_size` | string | Contiguous window request |
| `root_match_volume_uuid` | string | Root volume UUID |
| `cpu_regs` | string | CPU MPIDR values, comma separated |
| `psci_method` | string | `smc` or `hvc` |
| `packed_data_ramdisk` | boolean | Use the packed Data format |
| `ipad_identity` | boolean | Write the Apple device identity properties |
| `unique_chip_id` | string | `required` when the seed is mandatory |
## defconfig
```
CONFIG_NAME=y
CONFIG_NAME=n
CONFIG_NAME="text"
CONFIG_NAME=123
# CONFIG_NAME is not set
```
Each `CONFIG_XNU_<NAME>` becomes `XNU_<NAME>`. `{tree}` in a string becomes the build tree path.
## feeds.toml
```toml
[[source]]
name = "Name"
kind = "driver" # driver, kit, patch, package, refer
git = "https://github.com/ORG/REPO"
ref = "main"
path = "drivers/Name"
commit = "..."
local = "/path/to/local"
submodules = []
```
| Key | Meaning |
| --- | --- |
| `name` | Module name |
| `kind` | Module kind |
| `git` | Source repository |
| `ref` | Branch, tag or commit to fetch |
| `path` | Optional subdirectory, fetched sparse |
| `commit` | Optional pin |
| `local` | Local directory instead of git |
| `submodules` | Upstream submodules to initialize |
## local.toml
An ignored file in the device repository:
```toml
[local]
drivers = "/path/to/drivers"
kits = "/path/to/kits"
patches = "/path/to/patches"
scripts = "/path/to/scripts"
package = "/path/to/package"
```
## unixnu.lock
```toml
[source."Name"]
commit = "0123456789abcdef"
```
Written by `unixnu fetch`. A build uses the locked commit. The lock records git sources only.
## Module resolution
1. `--local-module [KIND:]NAME=PATH`
2. `--local-<kind> DIR`
3. `--local DIR`
4. a `local` key in `feeds.toml` or `local.toml`
5. the device repository
6. the base repository
7. a git source in `feeds.toml`
The first match wins.
## Next steps
- [Boot configuration](/reference/boot-configuration)
- [Device repository](/porting/device-repository)
- [Command line](/reference/cli)
+43
View File
@@ -0,0 +1,43 @@
# Glossary
| Term | Meaning |
| --- | --- |
| ADT | Apple device tree. The compact node encoding XNU reads from the `.adt` sidecar |
| AMFI | Apple Mobile File Integrity. The original code signing and entitlement policy |
| ABL | Android bootloader. The stock bootloader that loads the boot image |
| BootShim | The small AArch64 shim that starts the UEFI firmware volume |
| UniXNU | The UEFI boot manager |
| DarwinBoot | The UniXNU XNU loader |
| Defconfig | The `CONFIG_*` file that selects kernel, package and driver options |
| Device repository | The repository with `board.toml` and the device modules |
| ADT sidecar | `kernel.macho.adt`, the device tree next to the kernel |
| Feed | A git source declared in `feeds.toml` |
| Firmware volume | The EDK II `SILICIUM_UEFI.fd` image |
| IOMedia | The IOKit object that represents a disk or volume |
| IPSW | Apple restore image. The source of the kernelcache, device tree, disk images and trust caches |
| Kernelcache | The original prelinked Apple kernel with its kexts |
| Kext | A kernel extension bundle |
| Kit | A UniXNU bundle with drivers, patches, overlays, ACPI tables and kexts |
| Module | A driver, kit, patch, package or script selected by name |
| Personality | The IOKit match dictionary for a driver |
| PGZ | The zone allocator secondary page |
| Prelink | The step that combines the native kernel with the original kernelcache |
| SMMU | The ARM System Memory Management Unit |
| Trust cache | The Apple list of trusted code hashes |
| Userdata | The partition image with the OS and SystemOS windows |
| SHNGROOT | The manifest at the start of the userdata image |
| SystemOS | The cryptex system volume from the IPSW |
| DSC | The EDK II platform description file |
| FDF | The EDK II firmware description file |
| INF | The EDK II module description file |
| DXE | The UEFI driver execution environment |
| BDS | The UEFI boot device selection phase |
| GOP | The UEFI graphics output protocol |
| CHRP | The NVRAM bank format used by the Apple device tree |
| PGZ probe | The zone allocator alignment instrument |
## Next steps
- [Overview](/guide/overview)
- [Command line](/reference/cli)
- [Devices](/devices/)
+151
View File
@@ -0,0 +1,151 @@
# Output layout
All build output goes under `out/`. The driver writes nothing into the repositories except `unixnu.lock` in the device repository.
## Tree
```
out/
state.json
feeds/
ipsw/<build>/
<board>/
.config
tree/
logs/
kernel/
kernel.macho
package/
Build/Out/
payload/
payload.img
payload.json
boot/
boot.img
boot.json
userdata/
userdata-root.img
userdata-root.json
```
## state.json
```json
{
"device": "/path/to/device/repository"
}
```
The driver reads the device repository from this file when `-D` and `UNIXNU_DEVICE` are absent.
## feeds
A clone per git repository and ref:
```
out/feeds/<repo>-<hash>/
```
The hash covers the URL and the ref. The clone is shallow, blobless and sparse when the source declares a path.
## ipsw
```
out/ipsw/<build>/
<member file>
<member file>.json
```
The sidecar records the component, member path, size, SHA-256 and source. A cached member is reused when its size and hash match the pin.
## tree
The assembled build tree:
```
tree/
drivers/
kits/
patches/
refer/
scripts/
package/
build/
.cache/
```
Module directories are symbolic links. `scripts/` is copied. The device package appears as `kits/<board.kit>`.
## logs
One log per step:
| File | Step |
| --- | --- |
| `kernel.log` | XNU build |
| `kernel-bootstrap.log` | First pass without boot progress |
| `prelink.log` | Prelink |
| `package.log` | EDK II package build |
| `firmware-preflight.log` | Board preflight |
| `firmware.log` | EDK II firmware build |
| `basetools.log` | EDK II BaseTools build |
| `payload.log` | Payload volume |
| `payload-adt.log` | ADT generator |
| `payload-patch.log` | Panic screen patch |
| `payload-skip-setup.log` | Setup skip |
| `userdata.log` | Userdata image |
## boot.json
| Field | Meaning |
| --- | --- |
| `kernel_sha256` | Kernel field hash |
| `ramdisk_bytes` | Ramdisk size |
| `header_version` | Android boot image header version |
| `kernel_address` | Kernel load address |
| `tags_address` | Tags load address |
| `image_sha256` | Boot image hash |
| `image_bytes` | Boot image size |
| `fd_sha256` | Firmware volume hash |
| `dtb_sha256` | Device tree hash |
| `shim_bytes` | BootShim size |
| `hardware_tested` | Qualification state |
## payload.json
| Field | Meaning |
| --- | --- |
| `kernel_sha256` | Input kernel |
| `payload_kernel_sha256` | Kernel in the payload |
| `adt_sha256` | ADT sidecar |
| `kernel_args` | Command line |
| `trust_cache_sha256` | Trust cache bundle |
| `data_ramdisk_sha256` | Data seed |
| `skip_setup` | Setup skip applied |
| `panic_screen` | Panic screen patch applied |
| `payload_sha256` | Payload image hash |
| `payload_bytes` | Payload image size |
| `hardware_tested` | Qualification state |
## userdata-root.json
| Field | Meaning |
| --- | --- |
| `image_bytes` | Image size |
| `image_sha256` | Image hash |
| `manifest_sha256` | Manifest hash |
| `windows` | Name, offset, size and hash per window |
| `userdata_bytes` | Partition capacity |
| `flash_command` | Fastboot command |
| `source` | Local or remote IPSW |
| `hardware_tested` | Qualification state |
## Clean
`unixnu clean` removes `tree`, `logs`, `kernel`, `package`, `boot` and `edk2-conf` under the board output. Fetched sources and extracted IPSW members stay.
## Next steps
- [Build](/building)
- [Command line](/reference/cli)
- [Troubleshooting](/reference/troubleshooting)
+122
View File
@@ -0,0 +1,122 @@
# QEMU testing
QEMU tests UniXNU, the injection paths, DarwinBoot and the kernel entry without a device.
## Requirements
| Tool | Purpose |
| --- | --- |
| `qemu-system-aarch64` | AArch64 virtual machine |
| AAVMF or EDK2 AArch64 firmware | UEFI environment |
| `dtc` | Device tree compilation |
| `mtools` or `pyfatfs` | FAT image for the block slice test |
The QEMU runner looks for the firmware in the common Homebrew and distribution paths, or takes `--firmware`.
## Stage an ESP
```sh
python3 package/Scripts/stage-esp.py -o out/esp \
package/UniXNUPkg/Test/Qemu/esp \
--put out/<board>/package/Build/Out/UniXNU.efi=EFI/BOOT/BOOTAA64.EFI \
--put out/<board>/package/Build/Out/HelloPayload.efi=EFI/UniXNU/Payloads/HelloPayload.efi \
--put out/<board>/package/Build/Out/SampleDriver.efi=EFI/UniXNU/Kits/SampleDriver.kit/SampleDriver.efi
```
`stage-esp.py` copies the tree, compiles `.dts` to `.dtb` and `.dtso` to `.dtbo`, and places each `--put` file at its destination.
## Run
```sh
python3 package/Scripts/qemu-run.py out/esp \
--log out/qemu.log --screenshot out/menu.png \
--expect 'UNIXNU: menu: shown' \
--expect 'UNIXNU-HELLO: RESULT PASS'
```
| Option | Meaning |
| --- | --- |
| `--firmware FILE` | AAVMF code image |
| `--log FILE` | Serial log |
| `--screenshot FILE` | PNG captured when the menu appears |
| `--keys LIST` | QEMU key codes sent after the menu |
| `--expect REGEX` | Pattern that must match the serial log |
| `--reject REGEX` | Pattern that must not match |
| `--no-gop` | No display device, text console only |
| `--disk FILE` | Extra read-only raw disk |
| `--timeout SECONDS` | Run timeout |
| `--cpus N` | Virtual CPU count |
| `--finish-on-expect` | Stop two seconds after all expectations match |
| `--menu-marker TEXT` | Log marker that triggers the screenshot and keys |
The runner exposes the ESP read-only through QEMU `vvfat`, captures the serial log, and talks to QEMU over QMP for screenshots and keys.
## Smoke test
```sh
JOBS=8 bash package/Scripts/smoke.sh
```
The script builds the package and runs five scenarios:
| Scenario | Checks |
| --- | --- |
| Default entry | Device tree patches, overlay, kit driver, initrd media, entry variable |
| Keyboard | Down and Enter select the second entry |
| Degraded | No GOP and no configuration, built-in entries, text console |
| Block slice | Configuration on a FAT partition head, tail published as a block device |
| Kext staging | Revision 2 handoff, revision 1 refusal, invalid bundles, duplicates |
Logs and screenshots go to `package/Build/Smoke/`.
## XNU entry fixture
The QEMU entry generator builds an Apple-format device tree for the kernel:
```sh
python3 scripts/qemu-entry/make-dt.py \
--armvirt --armcpu --smp 2 \
--owned-memory-size 0x20000000 \
--output build/qemu-test.adt
```
| Option | Meaning |
| --- | --- |
| `--armvirt` | Describe QEMU virt devices |
| `--armcpu` | Add the GICv3 distributor and redistributor |
| `--smp N` | CPU count and PSCI |
| `--owned-memory-size` | Contiguous DarwinBoot window |
| `--data-ramdisk` | Scratch Data placeholder |
| `--trust-cache` | Trust cache placeholder |
| `--nvram-proxy` | Volatile CHRP NVRAM proxy |
| `--root-match-volume-uuid` | Root volume match |
| `--osenvironment` | `main`, `recoveryos` or `ramdisk` |
| `--qemu-vmm-present` | Report the VMM in `/defaults` |
| `--crypto-hash-method` | Image4 hash method |
The generator describes QEMU and uses QEMU values only.
## HVF
On Apple Silicon macOS, QEMU can use HVF:
```sh
python3 scripts/qemu-entry/make-dt.py --armvirt --armcpu --qemu-vmm-present \
--qemu-accel hvf --output build/qemu-hvf.adt
```
The generator reads the host counter frequency for the CPU timebase. The bound fixture is used to observe the actual `TCR_EL1`, `SCTLR_EL1` and `TTBR` registers at the kernel stop point.
## Stop point
```sh
python3 scripts/qemu-entry/gdb-stop.py --help
```
The helper stops QEMU at a symbol and records registers, memory and the last checkpoint. The output is a JSON report.
## Next steps
- [Bring-up order](/porting/bring-up)
- [Four-kilobyte pages](/xnu/four-k-page-tables)
- [Troubleshooting](/reference/troubleshooting)
+83
View File
@@ -0,0 +1,83 @@
# Troubleshooting
## Build
| Symptom | Check | Action |
| --- | --- | --- |
| `no device repository` | `-D` or `UNIXNU_DEVICE` | Pass the device repository |
| `has no board.toml` | Device repository layout | Create `board.toml` |
| `is an EXAMPLE device repository` | `[board] example` | Copy the sample and set a real name |
| `no configuration` | `out/<board>/.config` | Run `unixnu defconfig NAME` |
| `no Mu-Silicium checkout` | `--uefi` path | Pass a checkout with `Resources/Scripts/mkbootimg.py` |
| `is not a Mu-Silicium checkout` | Path | Point `--uefi` at the root of the checkout |
| `no edk2 tree` | `--edk2` or fetched edk2 | Run `unixnu fetch` or pass a Mu_Basecore tree |
| Missing archive in the kernel step | `build/<component>/lib<Name>.a` | Run the device archive scripts |
| `kernelcache not found` | `--kernelcache` or IPSW | Fetch the IPSW or pass the file |
| `does not match the kernelcache` | `[kernel.prelink] kernelcache_sha256` | Use the pinned image |
| Firmware volume size mismatch | `[uefi_fd] size` | Rebuild the firmware or correct the config |
| Payload larger than `size_mib` | `[payload] size_mib` | Raise the size or remove files |
| `unsupported kernel argument` | Argument characters | Use letters, digits, `_`, `.`, `-` |
| `command line is ... bytes` | Argument count | Shorten the line to 1000 bytes |
## IPSW
| Symptom | Check | Action |
| --- | --- | --- |
| `this IPSW is ...` | Product, version, build | Use the image the board describes |
| `no Erase build identity` | Device class, chip ID, board ID | Correct the identity values |
| `does not support range requests` | Remote URL | Use a server with range support or a local file |
| `differs from the pinned sha256` | `[ipsw.sha256]` | Use the pinned member or update the pin |
| `expected exactly one arm64 slice` | Kernelcache | Use an ARM64 kernelcache |
## Boot
| Symptom | Check | Action |
| --- | --- | --- |
| `fastboot` does not see the device | Driver, cable, bootloader mode | Install the USB driver and reboot to bootloader |
| Image starts and resets | Boot image header, base and size | Check the Mu-Silicium config and BootShim flags |
| Firmware stalls | Random Silicium stall | Start the same image again |
| No display | GOP driver | Use `console = "text"` or a serial console |
| No menu | Configuration path | Place `unixnu.toml` at `\EFI\UniXNU\unixnu.toml` |
| No boot entry | Payload volume | Check the FAT label, the entry path and the volume driver |
| Driver failed | First failure reason in the menu | Fix the driver path or its dependencies |
| `no loader protocol installed` | DarwinBoot in the firmware volume | Add DarwinBoot to the FDS or the payload |
| `loader revision 1 cannot accept kexts` | Loader revision | Use a revision 2 loader or remove the kexts |
| `boot refused: ...; firmware retained` | DarwinBoot log | Check the kernel, ADT, trust cache and placeholders |
| Kernel panics at entry | Page tables, kernel window, boot arguments | Use the QEMU fixture and the boot progress log |
| Panic names a provider | Device tree node or driver archive | Add the node or enable the driver |
| No root device | Userdata image, UUID, capacity | Flash the image, check the UUID and window offsets |
| Display assertion in userland | Display provider and boot arguments | Check the display driver and the focus arguments |
## Logs
| Source | Location |
| --- | --- |
| Build | `out/<board>/logs/` |
| Boot manager | Serial console, `UNIXNU:` lines |
| Loader | Serial console, `DARWINBOOT:` lines |
| Kernel | Serial console |
| QEMU | `--log` file and the `.clean.log` companion |
## Inspect a report
```sh
jq . out/<board>/boot/boot.json
jq . out/<board>/payload/payload.json
jq . out/<board>/userdata/userdata-root.json
```
The reports record the input hashes. Compare them with the files you supplied.
## Module origin
```sh
./unixnu status
```
The output lists every module and its origin. A local module is marked and reported as not pinned.
## Next steps
- [Build](/building)
- [Boot the image](/guide/boot)
- [Bring-up order](/porting/bring-up)
+102
View File
@@ -0,0 +1,102 @@
# Boot arguments
The kernel command line comes from `[payload] kernel_args` in the board file plus every `--kernel-arg`. The payload step writes the joined line into `unixnu.toml`, and DarwinBoot copies it into `boot_args`.
## Format
An argument matches:
```
[A-Za-z0-9_.-]+(=[A-Za-z0-9_.,-]+)?
```
The joined line is at most 1000 bytes. DarwinBoot copies it into a 1024-byte buffer.
```sh
./unixnu build payload --kernel-arg serial=3 --kernel-arg debug=0x14e
```
## Common arguments
| Argument | Purpose |
| --- | --- |
| `-v` | Verbose kernel output |
| `serial=3` | Selects the serial console |
| `debug=0x14e` | Enables the debug output mask |
| `kextlog=0xfff` | Verbose kext logging |
| `-apfs_shared_datavolume` | Uses the shared APFS data volume |
| `-no_slto_panic` | Suppresses the SLTO panic |
| `kt_fixed_zones=<n>` | Fixes the kernel trace zone count |
| `boot-progress=<n>` | Selects the boot progress display level |
| `cs_debug=<mask>` | Code signing debug output |
| `amfi_get_out_of_my_way=1` | Runs without AMFI enforcement |
| `pmap_debug=<mask>` | Pmap debug output |
| `zlog=<name>` | Selects the zone log |
Use `amfi_get_out_of_my_way=1` only in a diagnostic boot. It changes the security policy.
## Platform arguments
| Argument | Purpose |
| --- | --- |
| `sheng-platform-stage=<n>` | Stops the platform expert at a fixed stage |
| `sheng-pmgr=<n>` | Selects the board PMGR integration |
| `native-ufs-root=1` | Uses the native UFS root path |
| `native-ufs-stats-delay=<n>` | Delays the UFS statistics read |
| `armcpu-diagnostics=1` | Prints the CPU and interrupt state |
| `armcpu-spi-ipi=1` | Uses the SPI path for inter-processor interrupts |
| `sheng-smp=1` | Starts the secondary CPUs |
| `sheng-cpufreq=<n>` | Selects the CPU frequency path |
| `sheng-touch=<n>` | Runs the touch probe stage |
| `sheng-touch-hz=<n>` | Selects the touch SPI clock |
| `sheng-touch-mode=<n>` | Forces the touch SPI mode |
| `sheng-touch-hid=1` | Publishes touch contacts to HID |
| `sheng-aks=1` | Enables the AKS path |
| `sheng-gpu-power=<n>` | Selects the GPU power sequence |
| `sheng-gpu-metal=1` | Enables the Metal path |
| `sheng-gpu-addon=1` | Mounts the GPU addon content |
| `sheng-disable-watchdogd=1` | Stops the watchdog daemon |
| `sheng-disable-jobs=<list>` | Skips the named launch jobs |
| `offline-legacy-*=1` | Enables the legacy display and input probes |
| `offline-owner-root-check=1` | Starts the verified root check |
| `offline-owner-root-audit=1` | Enables the root binding audit |
| `offline-apfs-load=1` | Loads APFS at the offline checkpoint |
| `offline-mobile-graphics-*=1` | Enables the mobile graphics probes |
| `boot-ui-focus=<name>` | Selects the process for the boot UI trace |
The list above includes board specific arguments. Check the device page and the board file for the set a device uses.
## Adding an argument
1. Add the default to `[payload] kernel_args`.
2. Add a one-off value with `--kernel-arg`.
3. Read the value in the kernel with `PE_parse_boot_argn`:
```c
unsigned stage = 0;
if (PE_parse_boot_argn("my-driver-stage", &stage, sizeof(stage))) {
...
}
```
4. Document the argument in the device page.
## Debugging
| Need | Arguments |
| --- | --- |
| Early kernel output | `-v serial=3 debug=0x14e` |
| Kext load detail | `kextlog=0xfff` |
| Boot progress | `boot-progress=2` |
| Platform stop | `sheng-platform-stage=<n>` |
| Storage detail | `native-ufs-root=1 native-ufs-stats-delay=30` |
| Display detail | `offline-legacy-gop=1 offline-legacy-phase=<n>` |
| Code signing detail | `cs_debug=<mask>` |
A panic log records the last kernel output. The embedded panic region is read from the device tree when `XNU_DT_PANIC_REGION` is set.
## Next steps
- [Troubleshooting](/reference/troubleshooting)
- [Boot the image](/guide/boot)
- [Devices](/devices/)
+196
View File
@@ -0,0 +1,196 @@
# Four-kilobyte pages
Apple ARM64 kernels use a 16 KiB page on most SoCs. Non-Apple ARM64 SoCs commonly use a 4 KiB granule. UniXNU builds XNU for the BCM2837 machine configuration, which carries the 4 KiB layout, and patches the places where XNU assumes 16 KiB.
## Granule constants
| Machine | `ARM_PGSHIFT` | `ARM_PGBYTES` | `TCR_EL1_BOOT` TG0 | TG1 |
| --- | --- | --- | --- | --- |
| `BCM2837` | 12 | 4096 | 0, 4 KiB | 2, 4 KiB |
| `VMAPPLE` | 14 | 16384 | 2, 16 KiB | 1, 16 KiB |
`scripts/check-xnu-granules.py` compiles `TCR_EL1_BOOT` for both profiles and checks the TG0 and TG1 fields.
The kernel is compiled for 4 KiB. The page size is part of the machine configuration and the build flags. A 16 KiB kernel and a 4 KiB kernel are separate builds.
## Boot page tables
`osfmk/arm64/start.s` builds the bootstrap page tables before the MMU is enabled. The table pool is sized by `BOOTSTRAP_TABLE_SIZE`.
The patch `native-4k-bootstrap-capacity` adds a non-Apple 4 KiB budget:
```c
#elif !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
/* Two roots plus up to seven L2 pages per 6 GiB mapping. */
#define BOOTSTRAP_TABLE_SIZE (ARM_PGBYTES * 16)
```
The same patch adds a capacity check before a new L2 table is allocated:
```asm
add $6, x25, #(BOOTSTRAP_TABLE_BYTES >> 12), lsl #12
cmp $4, $6
b.hs Lbootstrap_tables_exhausted
```
The exhaustion label stops in a loop with the cursor and end address preserved in registers. The MMU is still off at that point, so the debugger can inspect the registers without a mapped stack.
The patch also clears the whole reserved pool instead of four 16 KiB pages:
```asm
mov x2, #(BOOTSTRAP_TABLE_BYTES / ARM_PGBYTES)
lsl x2, x2, #(ARM_PGSHIFT - TTE_SHIFT)
```
## Runtime page size
`osfmk/arm/arm_init.c` selects the page size from the RAM size on Apple boards. The patch `native-4k-user-abi` makes the native 4 KiB build use the hardware page size at every RAM size:
```c
#if !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
PAGE_SHIFT_CONST = ARM_PGSHIFT;
page_shift_user32 = ARM_PGSHIFT;
#else
...
#endif
```
`scripts/check-native-4k-policy.py` extracts this policy from the patched source and executes it at RAM boundaries:
| Profile | RAM | Kernel shift | User 32-bit shift |
| --- | --- | --- | --- |
| native 4K | any | 12 | 12 |
| Apple 4K | up to 1 GiB | 12 | 14 |
| Apple 4K | above 1 GiB | 14 | 14 |
| Apple 16K | any | 14 | 14 |
The Apple 4K profile switches to 16 KiB above 1 GiB. The native build keeps 4 KiB at every size.
## User address space
The patch updates the user map policy:
| File | Change |
| --- | --- |
| `bsd/kern/mach_loader.c` | 64-bit processes get `FOURK_PAGE_SHIFT` |
| `osfmk/arm/commpage/commpage.c` | 64-bit commpage shifts are 12 |
| `osfmk/vm/vm_shared_region.c` | The shared region submap uses 4 KiB entries |
| `osfmk/arm/arm_init.c` | `PAGE_SHIFT_CONST` and `page_shift_user32` follow `ARM_PGSHIFT` |
A 4 KiB userspace expects 4 KiB `mmap` alignment, 4 KiB commpage shifts and a 4 KiB dyld shared cache layout. A process that reads the commpage receives 12 for both the legacy and current 64-bit shift fields.
## Code signing
The original CodeDirectory in the kernelcache uses a 16 KiB hash page. A 4 KiB kernel validates 4 KiB pages. The patch `native-4k-signature-hash-cache` bridges the two:
1. Accept a CodeDirectory with `pageSize = 14` on a 12-bit kernel.
2. Read each original 16 KiB block through `vn_rdwr`.
3. Compare the block with the original CodeDirectory hash.
4. Derive one digest per 4 KiB page from the verified block.
5. Store the derived digests in read-only kernel memory.
6. Serve `cs_validate_page` from the derived table.
The derived table is attached to the `cs_blob`:
```c
const uint8_t *csb_native4k_hashes;
vm_size_t csb_native4k_hashes_size;
vm_size_t csb_native4k_hash_count;
```
A page validation checks the index, the offset alignment and the table bounds. A miss returns `CS_VALIDATE_TAINTED`. A block that fails the original 16 KiB hash leaves the blob without a table.
The build flag is `XNU_NATIVE_CS_HASH_CACHE`. `scripts/check-code-signing-granules.py` compiles the actual `cs_validate_codedirectory` gate and checks eight combinations of kernel shift, signature shift and cache mode.
## Allocator alignment
The zone allocator stores metadata on a secondary page. The patch `pgz-aligned-validation` decodes the PGZ address before checking the page mask:
```c
elem = pgz_decode((vm_offset_t)addr, 1);
offs = (elem & PAGE_MASK) - zone_elem_inner_offs(zone);
if (from_zone_map(elem, 1)) {
```
The check then uses the decoded element address for the metadata lookup.
## Physical addresses above 4 GiB
`pmap_sync_page_attributes_phys` originally shifted the physical page number with a 32-bit shift:
```c
flush_dcache((vm_offset_t)(pp << PAGE_SHIFT), PAGE_SIZE, TRUE);
```
The patch `zzx-pmap-sync-64bit-pa` uses `ptoa()`:
```c
flush_dcache((vm_offset_t)ptoa(pp), PAGE_SIZE, TRUE);
```
`ptoa()` widens the page number before the shift. A device whose DRAM starts above 4 GiB keeps the high address bits.
## Non-Apple platform guards
Several Apple-only paths need guards on a non-Apple platform:
| Patch | Change |
| --- | --- |
| `apple-only-cpu-tunables` | Apply Apple CPU tunables only on `APPLE_ARM64_ARCH_FAMILY` |
| `non-apple-cache-monitor` | Call `ml_enable_monitor` only under `XNU_MONITOR`; export a cache clean symbol |
| `non-apple-iommu-bootstrap` | Guard UAT and SART bootstrap and PPL code |
| `non-apple-pmap-field` | Rename the reserved field to match the non-PAC layout |
| `source-machine-layer` | Add `CONFIG_APPLE_AMCC` and drop Apple-only config entries |
| `native-arm-pan` | Enable PAN on ARMv8.1 when `XNU_NATIVE_PAN` is set |
## SMMU and 4 KiB tables
The `ArmSmmu4K` kit builds stage 1 and stage 2 tables with 4 KiB granules. The kit provides:
| File | Function |
| --- | --- |
| `ArmSmmu4KProgram.c` | Table programming |
| `ArmSmmu4KContext.c` | Context descriptors |
| `ArmSmmu4KStreams.c` | Stream match groups |
| `ArmSmmu4KLive.c` | Live stream mapping |
| `ArmSmmu4KTableMemory.c` | Table memory |
A device driver maps DMA buffers through the SMMU with 4 KiB tables. The touch driver example uses the apps SMMU bypass path.
## Verification
| Check | Tool |
| --- | --- |
| Granule constants | `scripts/check-xnu-granules.py` |
| Page size policy | `scripts/check-native-4k-policy.py` |
| Code signing gate | `scripts/check-code-signing-granules.py` |
| PGZ alignment | `scripts/check-pgz-alignment-checkpoints.py` |
| UFS VA64 evidence | `scripts/check-ufs-va64-evidence.py` |
| QEMU HVF observation | `scripts/qemu-entry/gdb-stop.py` with the native 4K fixture |
The QEMU observation records:
| Register | Expected |
| --- | --- |
| `TCR_EL1` TG0 | 0, 4 KiB |
| `TCR_EL1` TG1 | 2, 4 KiB |
| `SCTLR_EL1` M | 1, MMU enabled |
| `TTBR0_EL1`, `TTBR1_EL1` | Nonzero |
| `_PAGE_SHIFT_CONST`, `_page_shift`, `_page_shift_user32`, `_page_size` | 12, 12, 12, 4096 |
## Rules for new code
1. Use `PAGE_SIZE` and `PAGE_SHIFT`. Use `ARM_PGBYTES` and `ARM_PGSHIFT` in assembly.
2. Keep the code signing hash shift separate from the kernel page shift.
3. Set the user map page shift for every new user map.
4. Align DMA buffers and descriptors to `PAGE_SIZE`.
5. Use 4 KiB SMMU tables for new IOMMU contexts.
6. Decode PGZ addresses before applying page masks.
7. Use `ptoa()` and `phystokv()` for physical addresses above 4 GiB.
8. Budget bootstrap page tables for every mapped GiB.
## Next steps
- [Patch reference](/xnu/patch-reference)
- [SMMU kit](/drivers/overview)
- [Build the kernel](/guide/build-kernel)
+119
View File
@@ -0,0 +1,119 @@
# Patch reference
Patches apply in name order from `patches/xnu/*.patch` and `kits/NativePrelink/patches/*.patch`. The late patches `zzy-*` and `zzz-*` apply last.
## Build and toolchain
| Patch | Purpose |
| --- | --- |
| `apple-only-cpu-tunables` | Applies Apple CPU tunables only on `APPLE_ARM64_ARCH_FAMILY` |
| `cxx-flag-dependencies` | Passes `CFLAGS` to C++ object builds in the Makefile templates |
| `external-archive-dependencies` | Makes external archives relink dependencies of the kernel |
| `kernel-sdk-root` | Separates `KERNEL_SDKROOT` from `SDKROOT` and adds include paths |
| `local-availability` | Allows `XNU_AVAILABILITY_PL` to point at a local `availability.pl` |
| `public-entitlements-header` | Adds the web browser entitlement names and uses `CoreEntitlements` |
| `source-machine-layer` | Adds `CONFIG_APPLE_AMCC` and adjusts the BCM2837 config and file list |
| `disabled-deferred-reclaim` | Removes an unused parameter that breaks the build |
| `published-freezer-condition` | Fixes the preprocessor condition for the freezer default |
## Platform and CPU
| Patch | Purpose |
| --- | --- |
| `architectural-timer-entry` | Skips the BCM2837 timer routing when the native architectural timer is enabled |
| `armvirt-platform-personality` | Adds `ArmCpu`, `ArmVirtPlatform` and `ArmDTPlatform` personalities |
| `native-smp-secondary` | Raises `MAX_CPUS` to 8 and uses MPIDR Affinity1 for BCM2837 secondary CPUs |
| `native-arm-pan` | Enables ARMv8.1 PAN when `XNU_NATIVE_PAN` is set |
| `native-dt-panic-region` | Reads the panic log region from the device tree and validates it |
## Four-kilobyte pages
| Patch | Purpose |
| --- | --- |
| `native-4k-bootstrap-capacity` | Sizes the bootstrap page table pool for 4 KiB mappings and stops before overflow |
| `native-4k-user-abi` | Sets the 4 KiB page shift for user maps, commpage and shared region |
| `native-4k-signature-hash-cache` | Derives 4 KiB code signing hashes from verified 16 KiB blocks |
| `pgz-aligned-validation` | Decodes the PGZ address before the alignment check |
| `zzx-pmap-sync-64bit-pa` | Uses `ptoa()` to keep physical addresses above 4 GiB |
| `non-apple-pmap-field` | Matches the non-PAC pmap field layout |
| `non-apple-iommu-bootstrap` | Guards UAT and SART bootstrap and PPL code |
| `non-apple-cache-monitor` | Guards the monitor enable and exports the cache clean symbol |
## Security and owner root
| Patch | Purpose |
| --- | --- |
| `native-trust-cache-runtime` | Initializes the trust cache runtime on BCM2837 |
| `source-corecrypto14-bootstrap` | Registers the source corecrypto provider at early boot |
| `source-pthread-bootstrap` | Registers the source pthread component at early boot |
| `native-aes-personality` | Adds the AES, VirtIO block, VirtIO console, RAM block and UFS personalities |
| `offline-owner-root-crypto-probe` | Starts the verified root probe when `offline-owner-root-check` is set |
| `offline-owner-root-binding-audit` | Compares the mounted root device with the verified root device |
| `offline-owner-boot-manifest` | Publishes the owner boot manifest hash from the root and cache devices |
| `offline-owner-cache-carrier` | Selects the owner dyld cache carrier and validates its device and path |
| `offline-owner-systemos-mount` | Mounts the SystemOS cryptex and composes the root with the owner cache |
| `offline-owner-z-air-init` | Initializes the Z air path for the owner root |
| `offline-legacy-start` | Starts the legacy display probe after `IOKitBSDInit` |
| `offline-component-load-probes` | Loads APFS, IOGPU and mobile graphics components with checkpoints |
| `offline-native-gpu-broker` | Publishes the offline GPU broker after DTrace initialization |
| `sheng-platform-power` | Runs the board PMGR integration before the calendar initialization |
## Graphics and display
| Patch | Purpose |
| --- | --- |
| `offline-native-gpu-broker` | Starts the offline GPU broker |
| `zzy-native-gpu-addon-mount` | Mounts read-only union views for GPU addon content and launch jobs |
| `native-aes-personality` | Registers the storage and console personalities |
## Diagnostics
| Patch | Purpose |
| --- | --- |
| `offline-pgz-alignment-probe` | Adds the PGZ allocation probe and checkpoints |
| `native-work-interval-trace` | Records the last work interval results for the boot progress page |
| `zzz-boot-progress` | Adds boot progress checkpoints across the kernel startup path |
## Behavior fixes
| Patch | Purpose |
| --- | --- |
| `native-priv-work-interval` | Grants `PRIV_WORK_INTERVAL` before policy evaluation |
| `native-work-interval-soft-fail` | Soft-fails a refused CA client work interval |
| `native-union-getattrlistbulk` | Continues a bulk directory read in the lower union directory |
| `null-root-media` | Handles a null root in `IOService::setRootMedia` |
| `published-freezer-condition` | Fixes the freezer default condition |
## Mockfs
| Patch | Purpose |
| --- | --- |
| `mockfs-intermediate-lookup` | Allows lookups that are not the last component |
| `mockfs-memory-device-width` | Uses the device path when the legacy ioctl cannot express a 64-bit address |
| `mockfs-statfs` | Implements `vfs_getattr` |
| `mockfs-typed-allocation` | Uses the struct type for `kalloc_type` and `kfree_type` |
| `mockfs-test-file` | Adds `launchd` and `probe.bin` to the test file system |
## Prelink patches
`kits/NativePrelink/patches/`:
| Patch | Purpose |
| --- | --- |
| `xnu-10063.141.1-security-init.patch` | Adds the early security initializer entry point |
| `xnu-10063.141.1-sparse-index.patch` | Adds the sparse prelink index ABI |
## Add a patch
1. Edit `.cache/xnu-work`.
2. Generate the diff with `git diff`.
3. Place it in `patches/xnu/` with a name that orders it correctly.
4. Rebuild the kernel.
A patch that depends on another patch applies after it. A patch that changes a region touched by `zzy` or `zzz` applies before them.
## Next steps
- [Source and patches](/xnu/source-and-patches)
- [Four-kilobyte pages](/xnu/four-k-page-tables)
- [Build the kernel](/guide/build-kernel)
+148
View File
@@ -0,0 +1,148 @@
# Source and patches
UniXNU builds XNU from the pinned Apple source and applies its patches in name order.
## Source
`feeds.toml` pins the source:
| Source | Tag | Purpose |
| --- | --- | --- |
| `xnu` | `xnu-10063.141.1` | Kernel source |
| `libplatform` | `libplatform-316.100.10` | Platform headers |
| `libdispatch` | `libdispatch-1477.100.9` | Dispatch headers |
| `AvailabilityVersions` | `AvailabilityVersions-141.1` | Availability macros |
| `edk2` | `release/202608` | EDK II base for the package and firmware |
The XNU entry also carries:
```toml
darwin = "23.6.0"
version = "10063.141.1"
driverkit_tag = "xnu-10063.121.3"
trustcache_tag = "xnu-8796.101.5"
```
`driverkit_tag` supplies `iokit/DriverKit` headers that the current tag omits. `trustcache_tag` supplies `EXTERNAL_HEADERS/TrustCache`.
## Patch application
`scripts/build-xnu-4k-baseline.sh` applies patches to `.cache/xnu-work`:
1. Reverse the two late patches when they are present:
- `patches/xnu/zzz-boot-progress.patch`
- `patches/xnu/zzy-native-gpu-addon-mount.patch`
2. Apply every `patches/xnu/*.patch` and `kits/NativePrelink/patches/*.patch` in name order.
3. Skip a patch that is already applied.
4. Run `prepare-xnu-source.py` to restore omitted headers.
The late patches edit regions that earlier patches also touch. They carry the `zzz` and `zzy` prefixes so they apply last. The reverse step lets a new build start from a clean context for those regions.
A patch is idempotent:
```sh
git -C .cache/xnu-work apply --reverse --check "$PATCH" && skip
git -C .cache/xnu-work apply --check "$PATCH"
git -C .cache/xnu-work apply "$PATCH"
```
`--check` stops the build before a partial application.
## Patch layout
| Directory | Contents |
| --- | --- |
| `patches/xnu/` | Kernel patches |
| `patches/libpthread/` | Source pthread patches |
| `patches/mesa/` | Turnip and Mesa build patches |
| `kits/NativePrelink/patches/` | Prelink ABI patches |
A device repository can add `patches/` modules. The build tree links them under `tree/patches/`, and the kernel script reads `$ROOT/patches/xnu/*.patch`, which includes device patches.
## Add a patch
1. Build the kernel once so `.cache/xnu-work` exists.
2. Edit the source in `.cache/xnu-work`.
3. Generate the patch:
```sh
git -C .cache/xnu-work diff -- <paths> > patches/xnu/my-change.patch
```
4. Run `unixnu build kernel` again.
A patch that adds a build option follows the pattern in `native-aes-personality.patch`:
```c
#if defined(XNU_MY_OPTION) && XNU_MY_OPTION
" ,{"
" 'IOClass' = MyDriver;"
" 'IOProviderClass' = IOPlatformDevice;"
" }"
#endif
```
The matching `-DXNU_MY_OPTION=1` comes from `EXTRA_MAKE_ARGS` in the kernel script.
## Build flags
The kernel build uses these switches:
| Switch | Value |
| --- | --- |
| `SDKROOT` | `iphoneos` |
| `ARCH_CONFIGS` | `ARM64` |
| `MACHINE_CONFIGS` | `BCM2837` |
| `KERNEL_CONFIGS` | `DEVELOPMENT` |
| `ARM64_LINK_ADDR` | `0xfffffff007004000` |
| `ARM64_PLKSEG_ADDR` | `0xfffffff004004000` |
| `DEPLOYMENT_TARGET_FLAGS` | `-miphoneos-version-min=17.0 -DXNU_TARGET_OS_IOS` |
| `BUILD_LTO` | `0` |
| `BOUND_CHECKS` | `0` |
`BCM2837` carries the 4 KiB page layout. `BUILD_LTO=0` keeps the object files and the compile records readable and link-compatible with the external archives.
## Defconfig mapping
Each `CONFIG_XNU_<NAME>` becomes `XNU_<NAME>`:
| Defconfig value | Build value |
| --- | --- |
| `y` | `1` |
| `n` | `0` |
| `"text"` | `text` |
| `123` | `123` |
| `{tree}` in a string | Build tree path |
The kernel script reads the variables and adds object flags, archives and link inputs.
## Source tree
```
out/<board>/tree/
refer/xnu/ pinned source
.cache/xnu-work/ working clone
.cache/fakeroot/ kernel SDK root
.cache/sources/ restored header manifests
build/xnu/ios-obj/ objects and compile records
build/xnu/ios-sym/ symbols
build/xnu/ios-dst/ install headers
patches/xnu/ linked patches
kits/NativePrelink/ prelink tool and patches
```
## Updating the source
1. Set a new `ref` and `commit` in `feeds.toml`.
2. Update `darwin`, `version`, `driverkit_tag` and `trustcache_tag`.
3. Rebase the patches onto the new tag.
4. Rebuild the device archives against the new compile records.
5. Rebuild the prelink and re-qualify the kernelcache pin.
The prelink output depends on the original kernelcache ABI. A new source version can require a new kernelcache pin and new roots.
## Next steps
- [Four-kilobyte pages](/xnu/four-k-page-tables)
- [Patch reference](/xnu/patch-reference)
- [Build the kernel](/guide/build-kernel)
+191
View File
@@ -0,0 +1,191 @@
# 构建
`unixnu` 是构建驱动。它读取设备仓库、解析模块、组装构建树,并按步骤执行。
## 快速开始
```sh
git clone https://github.com/UniXNU/UniXNU
git clone https://github.com/UniXNU/UniXNU-sheng
git clone https://github.com/UniXNU/uefi-sheng
cd UniXNU
./unixnu defconfig sheng_defconfig -D ../UniXNU-sheng
./unixnu fetch
./unixnu ipsw fetch
./unixnu build kernel
./unixnu build package
./unixnu build firmware --uefi ../uefi-sheng
./unixnu bootimg --uefi ../uefi-sheng --dtb /path/to/device.dtb
```
第一次使用 `-D` 后,设备仓库会记录在 `out/state.json` 中。
启动镜像位于 `out/<board>/boot/boot.img`,旁边的 `boot.json` 是报告。选项写在子命令之后。
## 命令
| 命令 | 作用 |
| --- | --- |
| `defconfig [NAME]` | 列出设备仓库的 `configs/*_defconfig`,或把选中的文件复制到 `out/<board>/.config`。 |
| `fetch [NAME...] [--update]` | 拉取 git 源码并把 commit 写入设备仓库的 `unixnu.lock`。`--update` 忽略锁文件。 |
| `tree` | 解析模块、拉取步骤需要的参考源码,并组装 `out/<board>/tree`。 |
| `ipsw info` | 打印目标 IPSW 的产品、build 和全部组件。 |
| `ipsw fetch [COMPONENT...]` | 把指定组件解压到 `out/ipsw/<build>/`。 |
| `build [kernel] [package] [firmware] [payload] [prelink]` | 执行指定步骤。默认执行四个步骤。`prelink` 只重做预链接。 |
| `build userdata [--dry-run]` | 构建 userdata 原始镜像。该步骤较大,只在显式指定时执行。 |
| `bootimg` | 把 BootShim、固件卷、设备树和可选负载打包为 Android 启动镜像。 |
| `status` | 打印设备、配置和每个模块的来源。 |
| `clean` | 删除板级输出,保留已拉取源码。 |
## 常用选项
| 选项 | 含义 |
| --- | --- |
| `-D DIR` | 设备仓库,首次使用后会记住。 |
| `--out DIR` | 输出根目录,默认是本仓库的 `out/`。 |
| `-j N` | 并行任务数,默认取 `JOBS` 或 CPU 数量。 |
| `--target DEBUG\|RELEASE` | EDK II 构建目标,默认 `DEBUG`。 |
| `--uefi DIR` | 设备的 Mu-Silicium 仓库。 |
| `--edk2 DIR` | Mu_Basecore 仓库。 |
| `--kernelcache FILE` | 原始解压 kernelcache。 |
| `--kernel FILE` | 负载使用的预链接内核。 |
| `--kernel-arg NAME[=VALUE]` | 追加内核参数,可重复。 |
| `--trust-cache FILE` | trust cache 包。 |
| `--data-ramdisk FILE` | Data ramdisk 种子。 |
| `--skip-setup` | 在 Data 种子副本中把 Setup 标记为完成,需要 `--data-ramdisk`。 |
| `--unique-chip-id HEX` | 设备树平台种子。 |
| `--ipsw PATH_OR_URL` | 目标 IPSW,本地或远程。 |
| `--skip-preflight` | 跳过固件步骤的板级预检脚本。 |
| `--dry-run` | 仅 userdata:检查大小和哈希,不写文件。 |
环境变量 `UNIXNU_DEVICE`、`UNIXNU_OUT`、`UNIXNU_UEFI`、`UNIXNU_EDK2`、`UNIXNU_KERNELCACHE`、`UNIXNU_DTB`、`UNIXNU_IPSW`、`UNIXNU_UNIQUE_CHIP_ID` 和 `JOBS` 提供相同取值。
## 步骤
### defconfig
`defconfig` 把 `configs/<name>_defconfig` 复制到 `out/<board>/.config`,并在构建开始前解析文件,拒绝格式错误的行。
### fetch
`fetch` 解析模块并拉取 git 源码。它使用浅克隆、blobless 过滤和 cone sparse checkout,每个仓库与 ref 一个克隆。每个源码的 commit 写入设备仓库的 `unixnu.lock`,后续构建使用锁定的 commit。
### tree
`tree` 按脚本期望的布局组装 `out/<board>/tree`:
```
tree/
drivers/ kits/ patches/ refer/
scripts/ package/
build/ .cache/
```
模块是指向源码的符号链接,脚本会复制。设备 `package/` 出现在 `kits/<board.kit>`。
### ipsw
IPSW 按成员读取,远程 IPSW 不会整体下载。zip 目录和需要的成员通过 HTTP range 请求读取。解压的成员缓存在 `out/ipsw/<build>/`,并按 `board.toml [ipsw.sha256]` 的哈希校验。
镜像必须匹配 `[ipsw]` 的产品、版本、build、device class、chip ID 和 board ID。
### kernel
内核步骤:
1. 把 `patches/xnu` 和 `kits/NativePrelink/patches` 应用到固定版本的 XNU 源码。
2. 运行 `prepare-xnu-source.py` 恢复缺失的 DriverKit 头文件。
3. 为 `ARM64`、机型 `BCM2837`、配置 `DEVELOPMENT`、iOS 17 构建 XNU。
4. 用 `kits/NativePrelink` 把原生内核与原始 kernelcache 预链接。
kernelcache 来自 IPSW,除非 `--kernelcache` 指定文件。`KernelCache` IM4P 的 LZFSE 负载由 macOS `compression_tool` 解码,选出 arm64 切片,并按 `[kernel.prelink] kernelcache_sha256` 校验。
输出为 `out/<board>/kernel/kernel.macho`。
### package
package 步骤用 EDK II 构建 `package/`,输出到 `out/<board>/package/Build/Out`,包含 `UniXNU.efi`、`UniXNUDxe.efi`、`HelloPayload.efi`、`SampleDriver.efi` 和 `XnuTest.efi`。
### firmware
固件步骤从 Mu-Silicium 仓库构建设备固件:
1. 运行板级预检脚本。
2. 把板级补丁应用到共享 edk2 树,构建后还原。
3. 用板级 DSC 和 defines 运行 EDK II `build`。
`FD_BASE`、`FD_SIZE`、`FD_BLOCKS` 来自 Mu-Silicium 设备配置。固件卷大小必须匹配 `[uefi_fd] size`。
### payload
负载步骤写一个 FAT 卷,包含:
- `EFI/UniXNU/unixnu.toml`
- `EFI/UniXNU/Payloads/kernel.macho`
- `EFI/UniXNU/Payloads/kernel.macho.adt`
- 可选的 `kernel.macho.trustcache`
- 可选的 `scratch-data.img`
内核命令行来自 `[payload] kernel_args` 和每个 `--kernel-arg`。卷用 mtools 写入,不挂载。输出为 `out/<board>/payload/payload.img` 和 `payload.json`。
`--skip-setup` 在 `--data-ramdisk` 提供的 Data 种子副本中把 Setup 标记为完成。
### userdata
userdata 步骤构建 `userdata` 分区的原始镜像,保存 IPSW 中未修改的 `OS` 和 `SystemOS` 磁盘镜像,前置 `SHNGROOT` manifest。磁盘镜像用 `hdiutil` 只读挂载。输出为 `out/<board>/userdata/userdata-root.img` 和 `userdata-root.json`,报告会打印 `fastboot flash userdata` 命令。
### bootimg
启动镜像步骤:
1. 按固件基址和大小编译 BootShim。
2. 追加固件卷。
3. 用 gzip 压缩。
4. 应用 `[[bootimg.dtb_node]]` 后追加设备树。
5. 用 Mu-Silicium 设备配置中的参数调用 `mkbootimg.py`。
6. 解包镜像并逐部分与输入比较。
ramdisk 优先使用负载输出,也可以用 `--ramdisk FILE`,或用 `--ramdisk none` 使用空 ramdisk。
## 本地模块
模块可以来自本地目录,第一个匹配生效:
1. `--local-module [KIND:]NAME=PATH`
2. `--local-drivers|kits|patches|scripts|package DIR`
3. `--local DIR`,目录布局与本仓库相同
4. `feeds.toml` 中的 `local = "DIR"`,或设备仓库的 `local.toml`
5. 设备仓库
6. 本仓库
7. `feeds.toml` 中的 git 源码
本地模块以符号链接进入构建树。`unixnu status` 会标记它们,并报告它们没有锁文件固定。
## 输出
全部输出位于 `out/`:
| 路径 | 内容 |
| --- | --- |
| `out/state.json` | 记住的设备仓库 |
| `out/feeds/` | 拉取的源码 |
| `out/ipsw/<build>/` | 解压的 IPSW 成员 |
| `out/<board>/.config` | 选中的 defconfig |
| `out/<board>/tree/` | 组装后的构建树 |
| `out/<board>/logs/` | 步骤日志 |
| `out/<board>/kernel/kernel.macho` | 预链接内核 |
| `out/<board>/package/Build/Out/` | EFI 程序 |
| `out/<board>/payload/` | 负载镜像与报告 |
| `out/<board>/boot/` | 启动镜像与报告 |
| `out/<board>/userdata/` | userdata 镜像与报告 |
## 可复现性
`unixnu.lock` 固定每个拉取的源码。固件构建把 `SOURCE_DATE_EPOCH` 设为 Mu-Silicium 的 commit 时间,EDK II 不会写入构建时间。给定固件卷、设备树和 ramdisk 时启动镜像确定。负载包含随机种子,两次构建不同。
## 下一步
- [构建内核](/zh/guide/build-kernel)介绍内核开关和静态库。
- [启动镜像](/zh/guide/boot)介绍 fastboot 命令。
- [参考](/zh/reference/output-layout)列出报告字段。
+121
View File
@@ -0,0 +1,121 @@
# 启动链
启动链有四次交接。每次交接都有固定输入、校验步骤和失败后的返回路径。
## 1. bootloader 到 BootShim
Android bootloader 读取启动镜像。kernel 字段是 gzip 流,前半部分是 BootShim,后半部分是 UEFI 固件卷。gzip 流之后是设备树。
BootShim 按 Mu-Silicium 设备配置中的固件基址和大小汇编:
```sh
clang --target=aarch64-none-elf -c \
-DFD_BASE=0xA7100000 -DFD_SIZE=0x300000 \
-DREQUIRES_KERNEL_HEADER=1 BootShim.S -o shim.o
llvm-objcopy -O binary shim.o shim.bin
```
设备配置要求时,shim 包含 ARM64 image header,描述 shim 加固件卷,bootloader 可以把两者加载到指定地址。
BootShim 重定位固件卷并进入 UEFI。
## 2. UEFI 到 UniXNU
固件卷包含 UniXNU 启动管理器和它的驱动。`UniXNUDxe` 把 `UniXNU.efi` 注册为 `Boot####` 选项,load option 数据为 `unixnu:firmware`。BDS 启动该应用,用户也可以从固件菜单启动。
启动管理器先扫描启动所在卷,再扫描其他卷,寻找 `\EFI\UniXNU\unixnu.toml`,解析并应用配置。
菜单出现前,UniXNU:
1. 发布每个 `[[block_slice]]`。
2. 加载并启动每个 `[[driver]]`。
3. 安装 ACPI 表。
4. 合并 `[match]` 条件满足的 kit。
5. 构造菜单。
## 3. UniXNU 到 DarwinBoot
类型为 `xnu` 的菜单项交给加载器协议:
```c
typedef struct {
EFI_HANDLE Volume;
CONST CHAR16 *KernelPath;
CONST CHAR16 *BootArgs;
VOID *DeviceTree;
UINTN DeviceTreeSize;
VOID *Ramdisk;
UINTN RamdiskSize;
CONST UNIXNU_LOADER_KEXT *Kexts;
UINTN KextCount;
} UNIXNU_LOADER_BOOT_REQUEST;
```
UniXNU 为菜单项准备设备树:
1. 取固件 DTB、文件 DTB 或无 DTB。
2. 复制树并预留 64 KiB。
3. 应用配置和 kit 的全局补丁。
4. 应用菜单项补丁。
5. 最后应用 `[entry.chosen]` 属性。
6. 把树安装为 `EFI_DTB_TABLE`。
加载器收到树、ramdisk、启动参数和暂存的 kext。
## 4. DarwinBoot 到 XNU
DarwinBoot 校验并加载内核:
1. 从菜单项目录读取 `kernel.macho`。
2. 解析 Mach-O header,要求 `MH_MAGIC_64`、`CPU_TYPE_ARM64`、`MH_EXECUTE`、一个 `LC_MAIN` 和至少一个 `LC_SEGMENT_64`。
3. 检查入口位于可执行段。
4. 检查每个段页对齐、不重叠并位于 256 MiB 窗口内。
5. 读取 `kernel.macho.adt`。
6. 存在时读取 `kernel.macho.trustcache`。
7. 校验 trust cache:头计数、偏移、条目数和排序。
8. 从 GOP 读取 framebuffer 信息。
9. 分配连续物理窗口。
10. 复制段、ADT、trust cache 和 ramdisk。
11. 构造 `DARWIN_BOOT_ARGS`。
12. 退出 boot services。
13. 关闭 MMU 和缓存。
14. 在 EL1 跳转到入口。
`DARWIN_BOOT_ARGS` 布局匹配 Darwin 23:
| 偏移 | 字段 |
| --- | --- |
| 0x00 | revision、version、padding |
| 0x08 | 虚拟基址 |
| 0x10 | 物理基址 |
| 0x18 | 内存大小 |
| 0x20 | 内核数据末端 |
| 0x28 | video,六个 64 位值 |
| 0x60 | 设备树地址 |
| 0x68 | 设备树长度 |
| 0x6c | 命令行,1024 字节 |
| 0x478 | 实际内存大小 |
## 5. XNU 到用户态
XNU 从 `start.s` 开始,安装 bootstrap 页表,进入 C 启动路径,初始化平台专家并发布 IOKit registry。
平台专家匹配 ADT 根节点。通用 `ArmDTPlatform` 要求 `/cpus`、`/arm-io`、`/defaults`、`/options` 和 `/chosen` 中的 CHRP NVRAM proxy。它发布 CPU nub 和每个 `arm-io` 子节点。
存储驱动发布 userdata 分区,分区方案发布 `OS` 和 `SystemOS`。XNU 按 UUID 匹配根卷,挂载并启动用户态中的 `launchd`。
## 失败行为
| 阶段 | 失败 | 结果 |
| --- | --- | --- |
| bootloader | header 不匹配 | 拒绝镜像,设备留在 bootloader |
| 固件 | 无 GOP 或无输入 | UniXNU 以 headless 或文本模式运行 |
| UniXNU | 无配置 | 使用内置配置和可移动加载器 |
| DarwinBoot | 内核、ADT 或 trust cache 无效 | `EFI_COMPROMISED_DATA`,固件继续运行 |
| XNU | 缺少平台或存储驱动 | panic 日志给出缺失的 provider |
## 下一步
- [启动负载](/zh/concepts/payload)
- [预链接](/zh/concepts/prelink)
- [完整性与启动策略](/zh/concepts/security)
+113
View File
@@ -0,0 +1,113 @@
# 启动负载
负载是一个 FAT 卷,作为 Android 启动镜像的 ramdisk。UniXNU 从中读取配置,DarwinBoot 从中读取内核、ADT 和可选 trust cache。
## 卷布局
```
/EFI/UniXNU/unixnu.toml
/EFI/UniXNU/Payloads/kernel.macho
/EFI/UniXNU/Payloads/kernel.macho.adt
/EFI/UniXNU/Payloads/kernel.macho.trustcache
/EFI/UniXNU/Payloads/scratch-data.img
```
负载步骤用 `mformat` 和 `mcopy` 写卷。时间戳固定为 1980-01-01,相同输入得到相同卷。ADT 包含随机种子,两次负载构建不同。
## 配置
负载步骤写入:
```toml
version = 1
[boot]
timeout = 3
default = "xnu"
[kits]
autoload = false
[[entry]]
id = "xnu"
title = "UniXNU"
type = "xnu"
path = "Payloads/kernel.macho"
initrd = "Payloads/scratch-data.img"
args = "-v serial=3"
```
提供 Data 种子时存在 `initrd`。`args` 是 `[payload] kernel_args` 和 `--kernel-arg` 合并后的命令行。
## ADT sidecar
ADT 使用 Apple 的扁平节点编码。`[payload.adt] tool` 指定的板级脚本生成它。负载步骤传入:
| 选项 | 来源 |
| --- | --- |
| `--output` | `Payloads/kernel.macho.adt` |
| `--dram-size` | `[payload] dram_size` |
| `--owned-memory-size` | `[payload.adt] owned_memory_size` |
| `--root-match-volume-uuid` | `[payload.adt] root_match_volume_uuid` |
| `--chip-id` | `[payload.adt] chip_id` |
| `--cpu-regs` | `[payload.adt] cpu_regs` |
| `--psci-method` | `[payload.adt] psci_method` |
| `--unique-chip-id` | `--unique-chip-id` 或 `UNIXNU_UNIQUE_CHIP_ID` |
| `--packed-data-ramdisk` | `[payload.adt] packed_data_ramdisk` |
| `--data-ramdisk` | 非 packed Data 种子 |
| `--trust-cache` | trust cache 包 |
| `--ipad-identity` | `[payload.adt] ipad_identity` |
ADT 必须为对应输入预留以下属性:
| 属性 | 用途 |
| --- | --- |
| `/chosen/memory-map/RAMDisk` | DarwinBoot 写入 ramdisk 地址和大小 |
| `/chosen/memory-map/PackedRAMDisk` | DarwinBoot 写入 packed ramdisk 地址和大小 |
| `/chosen/memory-map/TrustCache` | DarwinBoot 写入 trust cache 地址和大小 |
| `/chosen/darwinboot-memory-size` | 请求大于 256 MiB 的连续窗口 |
DarwinBoot 要求每个输入都有对应占位符。不匹配时返回 `EFI_COMPROMISED_DATA`。
## 内核参数
写卷前会检查命令行:
- 每个参数匹配 `[A-Za-z0-9_.\-]+`,可选 `=value`,值匹配 `[A-Za-z0-9_.,\-]+`。
- 合并后的行不超过 1000 字节。DarwinBoot 把它复制到 1024 字节缓冲区。
命令行以 `args` 写入 `unixnu.toml`,DarwinBoot 原样复制到 `boot_args`。
## trust cache
DarwinBoot 校验 trust cache 包:
- 32 位计数为 1 到 128。
- 每个 cache 一个偏移表项。
- 每个 cache 以版本 2 开头。
- 每个条目 24 字节。
- 哈希类型为 1 到 4。
- 哈希按升序排列。
包被复制到内核窗口,地址和大小替换 `TrustCache` 占位符。
## Data ramdisk
Data 种子有两种交付形式:
| 形式 | ADT 占位符 | 行为 |
| --- | --- | --- |
| Dense | `RAMDisk` | DarwinBoot 复制整个镜像 |
| Packed | `PackedRAMDisk` | DarwinBoot 校验 `DBRDISK1` extent 表并复制片段 |
Packed 形式包含 magic `DBRDISK1`、版本 1、页大小、展开大小和 `(offset, length)` extent 表。未列出的范围是零填充 RAM。
## 确定性
给定固件卷、设备树和 ramdisk 时启动镜像确定。ADT 的随机种子使每次负载构建不同。负载报告记录内核、ADT、trust cache、Data 种子和负载哈希。
## 下一步
- [启动链](/zh/concepts/boot-chain)
- [启动配置](/zh/reference/boot-configuration)
- [启动参数](/zh/xnu/boot-arguments)
+98
View File
@@ -0,0 +1,98 @@
# 内核预链接
预链接内核把源码构建的内核和原始 Apple kernelcache 合并。源码内核提供 UniXNU 修改的代码,原始 kernelcache 提供 Apple kext 和平台服务,源码树把这些留给 kernelcache。
## 输入
| 输入 | 来源 |
| --- | --- |
| 原生内核 | `out/<board>/tree/build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf` |
| 原始 kernelcache | IPSW `KernelCache`,或 `--kernelcache FILE` |
| 组件列表 | `[kernel.prelink] roots` |
| slide | `[kernel.prelink] slide` |
| 早期安全初始化器 | `[kernel.prelink] early_security_init` |
kernelcache 是 `KernelCache` IM4P 的 LZFSE 负载。选出 arm64 切片,并按 `kernelcache_sha256` 校验。
## 预链接步骤
`kits/NativePrelink/emit_prelink.py`:
1. 解析原生和原始 Mach-O。
2. 读取原始 kernelcache 的 `__PRELINK_INFO`。
3. 选择 `roots` 中的组件及其导入的内核接口。
4. 把选中的组件段复制到原生内核的空合并段。
5. 重绑定引用选中组件的指针。
6. 对所有选中地址应用 slide。
7. 重写模块头和 kmod 条目。
8. 构建稀疏预链接索引。
9. 写出 `kernel.macho`。
输出保留原始组件代码不变。原生内核提供平台、CPU、存储和 defconfig 选中的设备驱动。
## 合并段
原始 kernelcache 把 kext 段放在合并范围:
| 原始段 | 原生段 |
| --- | --- |
| `__PRELINK_TEXT` | `__TEXT` |
| `__PLK_TEXT_EXEC` | `__TEXT_EXEC` |
| `__PLK_DATA_CONST` | `__DATA_CONST` |
| `__PRELINK_DATA` | `__DATA` |
预链接把每个组件段复制到对应范围。范围保留原始虚拟地址,slide 后原始代码中的指针仍然有效。
## 指针重绑定
原生内核从原始组件导入符号。预链接把每个导入解析为组件导出,并在导入位置写入 slide 后的地址。
计划记录每个绑定:
```json
{
"bindings": [
{ "new_site": "0xfffffff007123456", "new_target": "0xfffffff004abcdef" }
]
}
```
报告写在输出旁边,包含组件列表、指令清单和模块入口。
## 早期安全初始化
`early_security_init` 选择一个原始安全扩展及其可执行段中的地址。预链接对地址应用 slide,并记录给内核的早期安全路径。组件在预链接元数据中必须带 `AppleSecurityExtension = true`。
## kernelcache 固定
板级文件可以固定 kernelcache:
```toml
[kernel.prelink]
kernelcache_sha256 = "..."
```
预链接拒绝不同的 kernelcache。这样原生内核与它验证过的原始组件 ABI 绑定。
## 稀疏与容量
预链接输出稀疏索引。补丁 `native-4k-bootstrap-capacity` 增加原生 4 KiB 映射的 bootstrap 页表容量。启动进度补丁在预链接路径周围加入检查点输出。
## 报告
`emit_prelink.py` 在输出旁边写 JSON 报告:
| 字段 | 含义 |
| --- | --- |
| `components` | 包含的原始 bundle identifier |
| `kernel_interfaces` | 导出内核接口的组件 |
| `bindings` | 导入位置和 slide 后的目标 |
| `instruction_inventory` | PC 相对编码和内联数据 |
| `module_entrypoints` | slide 后的模块加载地址 |
| `early_security_initializers` | 选中的早期初始化器地址 |
## 下一步
- [构建内核](/zh/guide/build-kernel)
- [4 KiB 页表](/zh/xnu/four-k-page-tables)
- [完整性与启动策略](/zh/concepts/security)
+85
View File
@@ -0,0 +1,85 @@
# 完整性与启动策略
UniXNU 保留原始 Apple 安全组件,并提供它们检查的输入。源码内核加入这些组件所需的平台代码,启动负载提供描述设备的 trust cache 和设备树属性。
## 输入
| 输入 | 提供方 |
| --- | --- |
| trust cache 包 | `--trust-cache FILE` |
| trust cache 哈希 | `[payload] trust_cache_sha256` |
| 平台种子 | `--unique-chip-id HEX` |
| 设备树身份 | `[payload.adt] ipad_identity` 和板级脚本 |
| PMGR 集成 | 需要时使用 `XNU_SHENG_PMGR` |
| 根策略 | `XNU_VERIFIED_ROOT_PROBE`、`XNU_OWNER_ROOT_AUDIT`、`XNU_OWNER_ROOT_TRUST` |
## trust cache
DarwinBoot 校验 trust cache 包结构,并把它复制到内核窗口。ADT 的 `/chosen/memory-map/TrustCache` 占位符接收地址和大小。
补丁 `native-trust-cache-runtime` 在 `BCM2837` 上初始化运行时 trust cache 路径。原始 AppleImage4 和 AMFI 组件按缓存校验代码。
负载步骤只接受 SHA-256 位于 `[payload] trust_cache_sha256` 的 trust cache 包。
## 4 KiB 下的代码签名
原始 CodeDirectory 使用 16 KiB 哈希页。4 KiB 内核校验 4 KiB 页。补丁 `native-4k-signature-hash-cache`:
1. 通过 `vn_rdwr` 读取每个原始 16 KiB 块。
2. 用原始 CodeDirectory 哈希校验该块。
3. 从已验证块派生每个 4 KiB 页的哈希。
4. 把派生哈希存入只读内核内存。
5. 让 `cs_validate_page` 使用派生表。
只有原始 16 KiB 哈希匹配后,块才会发布。校验失败的块不产生派生表,页校验返回 `CS_VALIDATE_TAINTED`。
## AMFI 策略
ADT 属性 `/chosen/amfi-only-platform-code` 让 AMFI 保持在平台代码策略。`[kernel.prelink] early_security_init` 在指定地址运行原始 `AppleMobileFileIntegrity` 初始化器。
`offline-owner-root-*` 补丁为首次启动加入 owner root 检查:
| 补丁 | 功能 |
| --- | --- |
| `offline-owner-root-binding-audit` | 用启动 manifest 检查根绑定 |
| `offline-owner-root-crypto-probe` | 检查 owner crypto provider |
| `offline-owner-boot-manifest` | 记录启动 manifest 哈希 |
| `offline-owner-cache-carrier` | 选择代码签名缓存载体 |
| `offline-owner-systemos-mount` | 挂载 SystemOS cryptex |
| `offline-owner-z-air-init` | 初始化 owner root 的 Z air 路径 |
## verified root
`kits/VerifiedRoot` 提供签名的策略包。verified block 驱动按策略校验每次读取。策略可以按容量用 `--verified-root-bundle` 选择。
| 工具 | 功能 |
| --- | --- |
| `verified_root.c` | 策略解析和读取校验 |
| `xnu_crypto.c` | XNU crypto 适配 |
| `manifest.py` | 策略包构建 |
| `verify_image.py` | 镜像校验 |
## 存储完整性
`NativeVirtioBlock` 和 `NativeRamBlock` 共用 verified block 策略。构建脚本检查两个传输层导入相同的原始存储绑定,并检查编译对象与记录哈希一致。
## 报告
负载报告记录:
| 字段 | 含义 |
| --- | --- |
| `kernel_sha256` | 输入内核 |
| `payload_kernel_sha256` | 负载中的内核,经过可选 panic 屏幕补丁 |
| `adt_sha256` | ADT sidecar |
| `trust_cache_sha256` | trust cache 包 |
| `data_ramdisk_sha256` | Data 种子 |
| `payload_sha256` | 负载镜像 |
启动管理器日志记录暂存的 kext、安装的 ACPI 表和注入的驱动。
## 下一步
- [启动负载](/zh/concepts/payload)
- [补丁参考](/zh/xnu/patch-reference)
- [启动参数](/zh/xnu/boot-arguments)
+87
View File
@@ -0,0 +1,87 @@
# userdata 布局
userdata 分区承载 Darwin 根文件系统。UniXNU 保留原始磁盘镜像不变,并添加 manifest 把每个镜像映射到分区窗口。
## manifest
```
struct shngroot_header {
char magic[8]; /* "SHNGROOT" */
uint32_t version; /* 1 */
uint32_t count; /* 窗口数 */
uint64_t image_bytes; /* 镜像总大小 */
struct {
char name[32];
uint64_t offset;
uint64_t size;
uint32_t block_size; /* 512 */
uint32_t flags; /* bit 0: 只读 */
uint8_t sha256[32];
uint8_t reserved[40];
} window[count];
uint8_t reserved[32];
uint8_t manifest_sha256[32];
};
```
头部占 4096 字节,零填充到 1 MiB。第一个窗口从 0x100000 开始,每个窗口按 1 MiB 对齐。
## 窗口
| 窗口 | IPSW 组件 | 内容 |
| --- | --- | --- |
| `OS` | `OS` | 系统卷 |
| `SystemOS` | `Cryptex1,SystemOS` | cryptex 系统卷 |
镜像按字节从 IPSW 磁盘镜像复制。userdata 步骤用 `hdiutil` 只读挂载每个磁盘镜像并读取原始设备。
## 容量
```
(last_lba - first_lba + 1) * lba_bytes
```
步骤拒绝超过容量的镜像。报告记录容量和镜像大小。
## 根卷匹配
XNU 通过 `/chosen/root-matching` 选择根卷:
```xml
<dict>
<key>IOProviderClass</key><string>IOMedia</string>
<key>IOPropertyMatch</key>
<dict>
<key>UUID</key><string>00000000-0000-0000-0000-000000000000</string>
</dict>
</dict>
```
UUID 是 OS 窗口的卷 UUID。原生块驱动发布 userdata 分区,分区方案发布卷,XNU 匹配 UUID。
## Data 卷
可写 Data 卷独立于 userdata 镜像,来自负载中的 Data ramdisk 种子。种子是负载步骤的输入,负载报告记录其哈希。
`--skip-setup` 写出把 Setup 标记为完成的种子副本,副本按种子哈希缓存。
## 报告
`userdata-root.json` 记录:
| 字段 | 含义 |
| --- | --- |
| `image_bytes` | 镜像总大小 |
| `image_sha256` | 镜像哈希 |
| `manifest_sha256` | manifest 哈希 |
| `windows` | 每个窗口的名称、偏移、大小和哈希 |
| `userdata_bytes` | 分区容量 |
| `flash_command` | `fastboot flash userdata <image>` |
| `source` | 本地或远程 IPSW |
| `hardware_tested` | 验证状态 |
## 下一步
- [userdata 镜像](/zh/guide/userdata)
- [设备配置](/zh/reference/configuration)
- [启动链](/zh/concepts/boot-chain)
+130
View File
@@ -0,0 +1,130 @@
# 文档站
文档是 `docs/` 下的 VitePress 站点,发布到 `https://unixnu.com`。
## 环境
| 工具 | 版本 |
| --- | --- |
| Node.js | 22 或更新 |
| npm | 10 或更新 |
## 本地开发
```sh
cd docs
npm install
npm run dev
```
VitePress 会打印本地地址。`npm run build` 把静态站点写到 `docs/.vitepress/dist`,`npm run preview` 提供构建结果。
## 结构
```
docs/
.vitepress/
config.mts 站点配置
theme/ 自定义 CSS
public/
CNAME 自定义域名
logo-light.svg
logo-dark.svg
guide/ 入门与构建
concepts/ 启动链、负载、预链接、userdata、安全
porting/ 设备移植
drivers/ UEFI、XNU 和 kext 驱动
xnu/ 源码、补丁、页表、启动参数
devices/ 设备页面和示例
reference/ 命令行、配置、输出、QEMU、术语
contributing/ 本节
zh/ 简体中文
```
## 新增页面
1. 在对应章节创建 Markdown 文件。
2. 在 `docs/.vitepress/config.mts` 的 sidebar 中加入页面。
3. 在 `docs/zh/` 下创建中文页面,并加入中文 sidebar。
4. 运行 `npm run build`,修复坏链。
5. 新章节需要加入语言切换配置。
## 语言版本
| 语言 | 路径 | 语言代码 |
| --- | --- | --- |
| 英文 | `/` | `en-US` |
| 简体中文 | `/zh/` | `zh-CN` |
两个版本包含相同页面。修改一个页面时,在同一个 pull request 中更新另一个版本。
## 写作规则
- 使用标准术语。
- 直接陈述当前行为。
- 不使用长破折号和短破折号。
- 不使用比喻。
- 标题使用中性名词。
- 一个页面只讲一个主题。
- 参考内容用表格,操作步骤用列表。
- 设备相关数值放在设备页面。
- 每个操作步骤给出具体命令。
## 部署
`.github/workflows/docs.yml` 在 `docs/` 变化时构建并部署到 GitHub Pages。
需要:
1. 在仓库 Pages 设置中选择 `GitHub Actions` 作为来源。
2. 在 Pages 设置中填写自定义域名 `unixnu.com`。
3. 配置域名的 DNS 记录。
`docs/public/CNAME` 内容:
```
unixnu.com
```
apex 域名的 DNS:
| 类型 | 名称 | 值 |
| --- | --- | --- |
| A | `@` | `185.199.108.153` |
| A | `@` | `185.199.109.153` |
| A | `@` | `185.199.110.153` |
| A | `@` | `185.199.111.153` |
| CNAME | `www` | `unixnu.github.io` |
IPv6:
| 类型 | 名称 | 值 |
| --- | --- | --- |
| AAAA | `@` | `2606:50c0:8000::153` |
| AAAA | `@` | `2606:50c0:8001::153` |
| AAAA | `@` | `2606:50c0:8002::153` |
| AAAA | `@` | `2606:50c0:8003::153` |
证书签发后启用 `Enforce HTTPS`。
## 徽章
README 使用:
```markdown
[![Documentation](https://img.shields.io/badge/docs-unixnu.com-2A5BD7?logo=readthedocs&logoColor=white)](https://unixnu.com)
```
## 链接检查
```sh
cd docs
npm run build
```
VitePress 在内部链接断开时构建失败。
## 下一步
- [仓库布局](/zh/contributing/repository)
- [总览](/zh/guide/overview)
+85
View File
@@ -0,0 +1,85 @@
# 仓库布局
基础仓库和每个设备仓库使用相同布局。
```
UniXNU/
unixnu 入口
scripts/unixnu/ 构建驱动
package/ UniXNU EDK II package
drivers/ 内置 XNU 驱动
kits/ 内核库
patches/ XNU、libpthread、Mesa 补丁
scripts/ 构建和校验脚本
docs/ 本文档站
assets/ 标志
feeds.toml 固定的外部源码
examples/device/ 示例设备仓库
```
## 模块类型
| 类型 | 目录 | 内容 |
| --- | --- | --- |
| driver | `drivers/<Name>` | XNU 驱动源码和参考 plist |
| kit | `kits/<Name>` | 内核库 |
| patch | `patches/<Name>` | 补丁集 |
| script | `scripts/` | 构建和校验脚本 |
| package | `package/` | UniXNU package |
| refer | 拉取的树 | Apple 源码或 edk2 |
设备仓库可以提供任意类型,同名模块覆盖基础模块。
## 解析顺序
1. `--local-module [KIND:]NAME=PATH`
2. `--local-<kind> DIR`
3. `--local DIR`
4. `feeds.toml` 的 `local` 键或 `local.toml`
5. 设备仓库
6. 基础仓库
7. `feeds.toml` 的 git 源码
## 构建树
`unixnu tree` 写 `out/<board>/tree`:
```
tree/
drivers/ kits/ patches/ refer/
scripts/ package/
build/ .cache/
```
符号链接指向选中的模块,脚本被复制,设备 `package/` 出现在 `kits/<board.kit>`。
## 脚本
构建脚本从自身路径定位构建树。`scripts/` 中的脚本可以读取 `ROOT`、`build/`、`.cache/`、`refer/`、`drivers/`、`kits/`、`patches/` 和 `package/`。
## 新增模块
1. 在对应类型下创建目录。
2. 加入构建或校验逻辑。
3. 从板级文件或内核脚本引用。
4. 在对应文档章节记录。
## 提交范围
一个提交只改一个主题:
| 前缀 | 主题 |
| --- | --- |
| `feat(<area>)` | 新能力 |
| `fix(<area>)` | 缺陷修复 |
| `docs` | 文档 |
| `build(<area>)` | 构建系统 |
| `chore` | 清理 |
提交信息描述变化,文档描述当前状态。
## 下一步
- [设备仓库](/zh/porting/device-repository)
- [文档站](/zh/contributing/documentation)
- [构建](/zh/building)
+37
View File
@@ -0,0 +1,37 @@
# 支持的设备
一个设备条目由两个仓库和一组让通用栈在该板子上运行的数值组成。下表记录有公开仓库的移植。采用类似 SoC 的板子需要自己的移植和条目。
| 设备 | SoC | 设备仓库 | 固件 |
| --- | --- | --- | --- |
| 小米平板 6S Pro(sheng) | SM8550 | [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng) | [uefi-sheng](https://github.com/UniXNU/uefi-sheng) |
## 设备页面记录什么
| 项目 | 示例 |
| --- | --- |
| 板级身份 | 产品名、SoC、代号 |
| IPSW | 产品、版本、build、device class、chip 和 board ID |
| 分区几何 | `first_lba`、`last_lba`、块大小 |
| 内核预链接 | slide、roots、早期安全初始化器、kernelcache 固定 |
| 固件 | Mu-Silicium 配置、DSC、固件卷路径、defines |
| 负载 | 卷标、大小、DRAM 大小、内核参数 |
| ADT | 生成脚本、身份、CPU 寄存器、PSCI 方法 |
| 设备树追加 | 给原有 bootloader 的 reserved-memory 节点 |
| 构建命令 | 实际驱动命令 |
| 启动命令 | 实际 fastboot 命令 |
| 观察状态 | 已在设备上运行过的阶段 |
## 新增设备
1. 按[移植](/zh/porting/)操作。
2. 创建设备仓库和 UEFI 仓库。
3. 分阶段构建和 bring-up。
4. 在 `docs/zh/devices/` 下新增页面,记录数值和命令。
5. 把设备加入上表。
设备页面是板级数值的位置。通用指南使用占位符并链接到这里。
## sheng
[sheng 页面](/zh/devices/sheng)记录小米平板 6S Pro 移植。它是一个设备条目。[触控驱动示例](/zh/devices/sheng-touch-driver)把 sheng 的触控驱动作为内置 XNU 驱动的完整示例。
+145
View File
@@ -0,0 +1,145 @@
# 触控驱动示例
sheng 设备仓库包含一个完整的 Novatek NT36532E 触控驱动,展示新触控驱动的结构。
源码位于 [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng):
| 路径 | 内容 |
| --- | --- |
| `drivers/NativeNovatekTouch/NvtTouchCore.c` | 纯触控处理 |
| `drivers/NativeNovatekTouch/NvtProtocol.c` | 控制器协议 |
| `drivers/NativeNovatekTouch/NvtProtocol.h` | 协议常量和总线接口 |
| `drivers/NativeGeniSpi/GeniSpi.c` | GENI SPI 引擎 |
| `package/TouchBoard.h` | 板级常量和时钟辅助函数 |
| `package/TouchProbe.cpp` | 内核探针和帧循环 |
| `package/TouchBoardTests.c` | 板级辅助函数的主机测试 |
| `drivers/NativeHidInput/NativeHidInput.cpp` | HID digitizer 源 |
## 两层结构
驱动把纯逻辑和硬件访问分开。
```
NvtTouchCore.c 矩阵到触点,不分配,不做 I/O
NvtProtocol.c 芯片识别、固件、帧,通过 nvt_bus
TouchProbe.cpp MMIO、时钟、引脚、DMA、SMMU、线程
NativeHidInput.cpp 触点到 HID 报告
```
纯核心和协议接收 `struct nvt_bus`:
```c
struct nvt_bus {
void *ctx;
int (*xfer)(void *ctx, const uint8_t *tx, uint8_t *rx, size_t len);
void (*delay)(void *ctx, unsigned ms);
};
```
主机测试提供假总线,内核探针提供 GENI SPI 传输。
## 触控处理
`NvtTouchCore.c` 处理 40 乘 60 的 16 位矩阵:
1. 收集 72 帧启动数据。
2. 用启动帧的中位数形成每个节点的参考值。
3. 减去参考值得到 delta 矩阵。
4. 对高于噪声阈值的节点按最陡上升标记。
5. 找局部极大值并形成 blob。
6. 拒绝超过手掌节点数的 blob。
7. 计算每个 blob 的加权质心。
8. 应用边缘补偿。
9. 用半径和 down/up 阈值跨帧跟踪触点。
10. 从槽位第二帧开始报告触点。
核心不分配内存。临时数组位于 `struct nvt_touch_core`,探针只分配一次。注释记录原因:内核线程栈为 16 KiB,而临时数据更大。
## 控制器协议
`NvtProtocol.c` 提供:
| 函数 | 用途 |
| --- | --- |
| `nvt_identify` | 读取芯片 id 并选择映射 |
| `nvt_fw_parse` | 把固件镜像解析为 ILM 和 DLM 部分 |
| `nvt_download_firmware` | 复位 bootloader、写入部分、校验 SRAM |
| `nvt_start_scanning` | 读取固件版本和矩阵几何 |
| `nvt_peek_frame_header` | 读取 32 字节 THP 头 |
| `nvt_read_frame` | 读取完整 THP 帧 |
| `nvt_frame_header_valid` | 拒绝被撕裂的帧 |
| `nvt_frame_matrix` | 解码矩阵 |
传输长度、读取上限和帧长度来自厂商协议常量。
## 板级常量
`TouchBoard.h` 保存每个地址和位,并注明来源:
| 组 | 示例 |
| --- | --- |
| QUP wrapper 和 serial engine | 基址和大小 |
| SMMU | stream ID 和 apps SMMU 基址 |
| GCC | vote 寄存器、halt 寄存器、DFS 级 |
| TLMM | GPIO 编号、功能、驱动强度 |
| SPI | 模式和最高频率 |
时钟辅助函数是纯函数。`tb_choose_clock` 选择不超过目标频率的最高 DFS 级和分频。主机测试覆盖分频计算。
## 探针阶段
探针默认关闭。启动参数 `sheng-touch=<n>` 选择阶段:
| 阶段 | 工作 |
| --- | --- |
| 1 | 读取 GCC 分支、DFS 级、引脚和 serial engine 身份 |
| 2 | vote 并等待 QUP 时钟,配置引脚 |
| 3 | 初始化 GENI SPI,选择时钟,识别芯片 |
| 4 | 下载固件,开始扫描,处理帧 |
每个阶段在诊断页报告结果,首次失败停止后续阶段。
## 硬件路径
1. 在 GCC 中 vote GPLL0 和 QUP 分支。
2. 等待分支时钟离开 off 状态。
3. 在 TLMM 配置 SPI 引脚。
4. 映射 QUP wrapper 和 serial engine。
5. 初始化 GENI SPI。
6. 选择 DFS 级和分频。
7. 分配物理连续 DMA 缓冲。
8. 用 bypass stream 通过 apps SMMU 映射缓冲。
9. 每次传输前后维护缓存。
10. 复位触控 bootloader 并下载固件。
11. 轮询 THP 头,读取完整帧并发布触点。
SMMU 映射沿用 UFS 驱动的 bypass 模型。DMA 缓冲是 cached 的,驱动在每次传输前后显式 clean 和 invalidate。
## HID 发布
探针通过全局变量发布触点:
```c
volatile uint32_t native_touch_seq;
volatile uint32_t native_touch_count;
volatile int native_touch_xs[NVT_SLOTS];
volatile int native_touch_ys[NVT_SLOTS];
volatile int native_touch_ids[NVT_SLOTS];
```
写入方在更新前后递增序列号,读取方据此检测撕裂快照。`NativeHidInput` 读取快照并发出 digitizer 报告。
## 适配这个示例
1. 把纯处理层与总线分开。
2. 把每个板级常量放入头文件并注明来源。
3. 在硬件探针前为纯辅助函数写主机测试。
4. 加入分阶段启动参数,让每个硬件步骤可观察。
5. 通过序列计数器和固定大小快照发布触点。
6. 在启动日志显示每个阶段前保持驱动默认关闭。
## 下一步
- [XNU 驱动](/zh/drivers/xnu-drivers)
- [设备树匹配](/zh/drivers/device-tree-matching)
- [sheng 设备页](/zh/devices/sheng)
+283
View File
@@ -0,0 +1,283 @@
# 小米平板 6S Pro(sheng)
sheng 是搭载 SM8550 SoC 的小米平板 6S Pro。它是[支持的设备](/zh/devices/)中的一个设备移植。
| 仓库 | 内容 |
| --- | --- |
| [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng) | 板级文件、defconfig、设备脚本、触控驱动和 package |
| [uefi-sheng](https://github.com/UniXNU/uefi-sheng) | Mu-Silicium UEFI 移植 |
## 板级
```toml
[board]
name = "sheng"
soc = "SM8550"
description = "Xiaomi Pad 6S Pro"
kit = "sheng"
```
输出目录为 `out/sheng/`。
## IPSW
```toml
[ipsw]
url = "https://updates.cdn-apple.com/2025SpringFCS/fullrestores/122-77166/DCA6CFE8-90E9-4B92-880D-3DE171798E40/iPad_Pro_HFR_17.7.11_21H461_Restore.ipsw"
product = "iPad7,1"
version = "17.7.11"
build = "21H461"
device_class = "j120ap"
chip_id = 0x8011
board_id = 12
fetch = ["KernelCache", "DeviceTree", "StaticTrustCache", "Cryptex1,SystemTrustCache"]
```
板级使用未修改的 iPad7,1 iPadOS 17.7.11 恢复镜像。成员哈希:
```toml
[ipsw.sha256]
"044-76040-038.dmg" = "3f99b859c9728379110b66657c7b15285bcb1925ad8b5726542e2cdcf8b1028d"
"044-76034-038.dmg" = "b4908cde41d23e6bc05e6af51a2c9eeb00cf0f863a6a18b1600c96a3fd70414f"
```
## userdata
```toml
[userdata]
first_lba = 0x2f3840
last_lba = 0x3d0abfa
lba_bytes = 4096
windows = [
{ name = "OS", component = "OS" },
{ name = "SystemOS", component = "Cryptex1,SystemOS" },
]
```
LBA 来自设备上名为 `userdata` 的 GPT 条目。
## 内核
```toml
[kernel]
defconfig = "sheng_defconfig"
[kernel.prelink]
tool = "kits/NativePrelink/emit_prelink.py"
kernel = "build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf"
kernelcache_sha256 = "f18b4df865405f2a287b84977d7aca745e056d656d9fc9df1662970ce2969de7"
slide = "-0x1000000"
early_security_init = "com.apple.driver.AppleMobileFileIntegrity=0xfffffff005ab4ae0"
```
预链接 roots:
```
com.apple.AppleFSCompression.AppleFSCompressionTypeZlib
com.apple.driver.AppleARMPlatform
com.apple.driver.AppleARMWatchdogTimer
com.apple.driver.AppleAudioClockLibs
com.apple.driver.AppleEffaceableStorage
com.apple.driver.AppleIISController
com.apple.driver.AppleM2ScalerCSCDriver
com.apple.driver.AppleMobileFileIntegrity
com.apple.driver.ApplePMGR
com.apple.driver.IODARTFamily
com.apple.filesystems.apfs
com.apple.iokit.CoreAnalyticsFamily
com.apple.iokit.IOAVFamily
com.apple.iokit.IOAudio2Family
com.apple.iokit.IOCECFamily
com.apple.iokit.IOCryptoAcceleratorFamily
com.apple.iokit.IOGPUFamily
com.apple.iokit.IOHDCPFamily
com.apple.iokit.IOMobileGraphicsFamily
com.apple.iokit.IOReportFamily
com.apple.iokit.IOSlowAdaptiveClockingFamily
com.apple.iokit.IOStorageFamily
com.apple.iokit.IOSurface
com.apple.kec.Libm
com.apple.kec.corecrypto
com.apple.kext.AppleMatch
com.apple.kext.CoreTrust
com.apple.security.AppleImage4
com.apple.security.sandbox
com.apple.iokit.IOHIDFamily
com.apple.iokit.IOHIDEventDriver
```
## defconfig
`sheng_defconfig` 启用:
```
CONFIG_XNU_SOURCE_CORECRYPTO14=y
CONFIG_XNU_SOURCE_PTHREAD=y
CONFIG_XNU_ARM_DT_PLATFORM=y
CONFIG_XNU_ARMCPU=y
CONFIG_XNU_NATIVE_UFS=y
CONFIG_XNU_NATIVE_AES=y
CONFIG_XNU_NATIVE_VIRTIO_BLOCK=y
CONFIG_XNU_NATIVE_RAM_BLOCK=y
CONFIG_XNU_TRUST_CACHE_RUNTIME=y
CONFIG_XNU_SOURCE_MOCKFS=y
CONFIG_XNU_NATIVE_CS_HASH_CACHE=y
CONFIG_XNU_VERIFIED_ROOT_PROBE=y
CONFIG_XNU_OWNER_ROOT_AUDIT=y
CONFIG_XNU_OWNER_ROOT_TRUST=y
CONFIG_XNU_SHENG_PMGR=y
CONFIG_XNU_OWNER_CACHE_MOUNT=y
CONFIG_XNU_OWNER_CACHE_STARTUP=y
CONFIG_XNU_OWNER_BOOT_MANIFEST=y
CONFIG_XNU_OWNER_DATA_MOUNT=y
CONFIG_XNU_OWNER_UNION_SYSTEMOS=y
CONFIG_XNU_DT_PANIC_REGION=y
CONFIG_XNU_ZONE_PGZ_ALIGNMENT_PROBE=y
CONFIG_XNU_NATIVE_ARCH_TIMER=y
CONFIG_XNU_NATIVE_PAN=y
CONFIG_XNU_BOOT_PROGRESS=y
CONFIG_XNU_NATIVE_LEGACY_DISPLAY=y
CONFIG_XNU_NATIVE_ADRENO=y
CONFIG_XNU_CRYPTO_BOOTSTRAP="prelinked"
```
板级设置 scratch Data 卷 UUID:
```
CONFIG_XNU_OWNER_DATA_VOLUME_UUID="18AB5557-DC2D-469B-8758-855E60F5B0DF"
```
## 固件
```toml
[firmware]
config = "sheng"
dsc = "Platforms/Xiaomi/shengPkg/sheng.dsc"
fd = "Build/shengPkg/{target}_CLANGPDB/FV/SILICIUM_UEFI.fd"
preflight = ["scripts/check-sheng.py"]
submodules = ["Common/Mu", "Common/Mu_OEM_Sample", "Silicium-ACPI", "Silicon/Silicium/OpensslPkg/Library/OpensslLib/openssl"]
patches = ["Resources/MuPatches/Gic-Spurious.patch"]
```
package 路径覆盖 Mu-Silicium 树、UniXNU package、DarwinBoot、edk2、Mu、Mu OEM、Qualcomm、Silicium 和 ACPI。
defines:
```toml
[firmware.defines]
ENABLE_SECUREBOOT = "1"
DEVICE_MODEL = "sheng"
MEMORY_PROTECTION = "TRUE"
SHIP_MODE = "FALSE"
BUILDID_STRING = "sheng-research"
```
## 负载
```toml
[payload]
volume = "SHENGESP"
size_mib = 192
loader = "firmware"
title = "sheng first boot: source 4K XNU to console"
dram_size = "0x80000000"
skip_setup_tool = "scripts/seed-data-skip-setup.py"
panic_screen = true
trust_cache_sha256 = [
"7416ca4f8377146626bb284e2133c76bee6cb4e3ee0af5d648b186235a7323a7",
"8900d6a93d7a59ce1a872a5c081e559f4240a218f97e12d44d22dda46644d68f",
]
```
内核参数覆盖平台、存储、显示、输入、GPU、诊断和 owner root 路径。完整列表在板级文件中。
ADT:
```toml
[payload.adt]
tool = "scripts/make-sheng-adt.py"
chip_id = "207"
owned_memory_size = "0x130000000"
root_match_volume_uuid = "04769D26-2CA0-4825-B443-E2E5F88300F2"
cpu_regs = "0,0x100,0x200,0x300,0x400,0x500,0x600,0x700"
packed_data_ramdisk = true
ipad_identity = true
unique_chip_id = "required"
```
启动镜像加入原有 ABL 需要的 splash 区域:
```toml
[[bootimg.dtb_node]]
path = "/reserved-memory/splash_region"
props = [
{ name = "label", type = "s", value = "cont_splash_region" },
{ name = "reg", type = "x", value = ["0x0", "0xb8000000", "0x0", "0x2b00000"] },
]
```
## 构建
```sh
git clone https://github.com/UniXNU/UniXNU
git clone https://github.com/UniXNU/UniXNU-sheng
git clone https://github.com/UniXNU/uefi-sheng
cd UniXNU
./unixnu defconfig sheng_defconfig -D ../UniXNU-sheng
./unixnu fetch
./unixnu ipsw fetch
./unixnu build kernel
./unixnu build package
./unixnu build firmware --uefi ../uefi-sheng
./unixnu build payload --unique-chip-id <hex> --trust-cache <bundle> --data-ramdisk <seed>
./unixnu bootimg --uefi ../uefi-sheng --dtb <sheng.dtb>
```
平台种子、trust cache 和 Data 种子是设备输入。板级文件报告接受的 trust cache 哈希。
## 启动
```sh
fastboot boot out/sheng/boot/boot.img
```
首次启动使用 userdata 镜像:
```sh
./unixnu build userdata
fastboot flash userdata out/sheng/userdata/userdata-root.img
fastboot reboot bootloader
fastboot boot out/sheng/boot/boot.img
```
## macOS
板级文件指向 iPadOS 恢复镜像。要在同一设备上启动 macOS,把 `[ipsw]` 指向 macOS 恢复镜像,并更新产品、版本、build、device class、chip ID、board ID 和成员哈希。把 `[userdata] windows` 设为 `OS` 和 `Cryptex1,SystemOS`。内核、负载、启动镜像和 userdata 步骤保持不变。
## 观察状态
| 阶段 | 状态 |
| --- | --- |
| 原有 ABL 加载启动镜像,BootShim 启动 UEFI 固件 | 工作 |
| Silicium UEFI 和 UniXNU 启动 | 工作;Silicium 偶尔停住,再次启动同一镜像可继续 |
| UniXNU 读取负载卷 | 工作 |
| XNU 与用户态 | 早期工作流的镜像已到达 |
| 触控驱动 | NT36532E 控制器的分阶段探针 |
触控驱动见[触控驱动示例](/zh/devices/sheng-touch-driver)。
## 设备细节
- 设备树没有 splash 区域时,原有 ABL 会关闭面板。启动镜像加入 `/reserved-memory/splash_region`。
- 平台种子位于仓库之外,用 `--unique-chip-id` 提供。
- 原生 UFS 根路径需要 `sheng-pmgr=2`。`native-ufs-root=1` 时,负载步骤拒绝没有链接 PMGR 集成的内核。
- trust cache 包必须是两个接受哈希之一。
- Data 种子是负载步骤的输入。
## 下一步
- [移植](/zh/porting/)
- [触控驱动示例](/zh/devices/sheng-touch-driver)
- [调试与日志](/zh/guide/debugging)
- [故障排查](/zh/reference/troubleshooting)
+165
View File
@@ -0,0 +1,165 @@
# 设备树匹配
驱动通过 IOKit personality 绑定到 provider。personality 是匹配键字典。
## personality 键
| 键 | 含义 |
| --- | --- |
| `IOClass` | 驱动类 |
| `IOProviderClass` | Provider 类 |
| `IONameMatch` | Provider 名称或 `compatible` 值 |
| `IOPropertyMatch` | Provider 属性精确匹配字典 |
| `IOProbeScore` | 分数越高越先 probe |
| `IOMatchCategory` | 匹配类别,允许第二个驱动匹配同一 provider |
| `IOResourceMatch` | probe 前必须发布的资源 |
| `IOProviderMergeProperties` | 加到 provider 的属性 |
没有 `IONameMatch` 或 `IOPropertyMatch` 的 personality 匹配该类的所有 provider,`IOProbeScore` 决定候选顺序。
## Provider 类
| Provider | 发布方 | 用途 |
| --- | --- | --- |
| `IOPlatformExpertDevice` | 平台专家 | 平台身份、product、buttons |
| `IOPlatformDevice` | `ArmDTPlatform` 为每个 `arm-io` 子节点发布 | SoC 设备 |
| `IOResources` | IOKit | AES、RAM 块等全局资源 |
| `IOMedia` | 分区和文件系统栈 | 根卷匹配 |
| `IOBlockStorageDevice` | 存储驱动 | 块设备客户端 |
`ArmDTPlatform` 把 nub 附加到平台专家上。nub 保留设备树父节点,`reg` 和 `ranges` 通过 `arm-io` 解析。
## 匹配示例
### 按 compatible 匹配平台设备
```c
" ,{"
" 'IOClass' = NativeUfs;"
" 'IOProviderClass' = IOPlatformDevice;"
" 'IONameMatch' = 'qcom,sm8550-ufshc';"
" 'IOProbeScore' = 1000:32;"
" }"
```
`IONameMatch` 与 provider 名称和 `compatible` 字符串列表比较。
### 全局资源
```c
" ,{"
" 'IOClass' = NativeAES;"
" 'IOProviderClass' = IOResources;"
" 'IOResourceMatch' = IOBSD;"
" 'IOProbeScore' = 1000:32;"
" }"
```
`IOResourceMatch = IOBSD` 等到 BSD 子系统发布资源。
### 按 UUID 匹配根卷
```xml
<key>IOProviderClass</key><string>IOMedia</string>
<key>IOPropertyMatch</key>
<dict>
<key>UUID</key><string>00000000-0000-0000-0000-000000000000</string>
</dict>
```
该字典存放在 `/chosen/root-matching`。
## 设备树节点
### 设备节点
```c
ufshc {
name = "ufshc";
compatible = "qcom,sm8550-ufshc";
reg = <0x03ff0000 0x3000>;
interrupts = <0 0x100 4>;
status = "okay";
}
```
平台专家发布带这些属性的 nub,驱动从 provider 读取 `reg` 和 `interrupts`。
### 总线节点
```c
arm-io {
name = "arm-io";
device_type = "arm-io";
#address-cells = <2>;
#size-cells = <2>;
ranges = <0 0x17000000 0x08000000>;
intc {
name = "intc";
compatible = "arm,gic-v3";
reg = <0x0 0x10000 0x0 0x10000>;
};
}
```
子节点 `reg` 是 `ranges` 窗口内的偏移,平台专家解析物理地址。
## 读取属性
```cpp
OSData *data = OSDynamicCast(OSData, provider->getProperty("reg"));
if (data == nullptr) {
return false;
}
const uint32_t *cells = (const uint32_t *)data->getBytesNoCopy();
uint32_t count = data->getLength() / sizeof(uint32_t);
```
cell 数量来自父节点。`#address-cells = <2>` 和 `#size-cells = <2>` 的节点每个范围使用四个 cell。
字符串属性:
```cpp
OSData *compatible = OSDynamicCast(OSData, provider->getProperty("compatible"));
const char *value = (const char *)compatible->getBytesNoCopy();
```
字符串列表是连续的 NUL 结尾字符串。
## overlay 与补丁
kit 可以应用设备树 overlay:
```toml
[[overlay]]
file = "board.dtbo"
```
配置可以修改属性:
```toml
[[dt_patch]]
op = "set"
node = "/arm-io/intc"
prop = "status"
value = "disabled"
```
补丁应用于 EFI 和 Linux 菜单项。Darwin 路径读取 `.adt` sidecar。
## probe 顺序
1. 平台专家发布 provider。
2. IOKit 收集 `IOProviderClass` 匹配的 personality。
3. `IONameMatch` 或 `IOPropertyMatch` 不匹配的项被跳过。
4. `IOResourceMatch` 对应的 provider 必须已发布。
5. 剩余候选按 `IOProbeScore` 排序。
6. 每个候选运行 `probe()`。
7. 启动成功的最高分候选获得 provider。
## 下一步
- [XNU 驱动](/zh/drivers/xnu-drivers)
- [设备树](/zh/porting/device-tree)
- [kext 包](/zh/drivers/kexts)
+156
View File
@@ -0,0 +1,156 @@
# kext 包
kext 包是标准 macOS 或 iOS 内核扩展目录。UniXNU 暂存包文件,并通过 `UNIXNU_LOADER_PROTOCOL` 交给 XNU 加载器。
## 包布局
```
MyDriver.kext/
Contents/
Info.plist
MacOS/
MyDriver
```
包名以 `.kext` 结尾。plist 默认路径为 `Contents/Info.plist`,可执行文件路径默认取 `CFBundleExecutable`,相对包目录。
无代码 kext 只有 plist,可以声明依赖或引用其他二进制的 personality。
## Info.plist
```xml
<dict>
<key>CFBundleIdentifier</key><string>org.example.MyDriver</string>
<key>CFBundleName</key><string>MyDriver</string>
<key>CFBundleExecutable</key><string>MyDriver</string>
<key>CFBundlePackageType</key><string>KEXT</string>
<key>CFBundleVersion</key><string>1.0.0</string>
<key>OSBundleRequired</key><string>Root</string>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.kpi.iokit</key><string>23.6.0</string>
</dict>
<key>IOKitPersonalities</key>
<dict>
<key>MyDevice</key>
<dict>
<key>IOClass</key><string>MyDriver</string>
<key>IOProviderClass</key><string>IOPlatformDevice</string>
<key>IONameMatch</key><string>my-device</string>
<key>IOProbeScore</key><integer>1000</integer>
</dict>
</dict>
</dict>
```
## 注册到 UniXNU
菜单项级 kext:
```toml
[[entry]]
id = "xnu"
type = "xnu"
path = "Payloads/kernel.macho"
[[entry.kext]]
bundle = "Kexts/MyDriver.kext"
```
kit 级 kext 指定菜单项:
```toml
[[kext]]
bundle = "MyDriver.kext"
entry = "xnu"
plist = "Contents/Info.plist"
executable = "Contents/MacOS/MyDriver"
enabled = true
```
| 键 | 含义 |
| --- | --- |
| `bundle` | 相对配置或 kit 的包目录 |
| `plist` | 包内 plist 路径 |
| `executable` | 包内可执行文件路径,无代码 kext 留空 |
| `entry` | 菜单项 id,kit kext 必需 |
| `enabled` | `false` 跳过 |
菜单项 kext 和 kit kext 按配置顺序暂存,kit kext 在前,菜单项 kext 在后。重复包路径会被拒绝。
## UniXNU 校验
UniXNU 暂存前校验:
- 包名以 `.kext` 结尾。
- 所有路径相对且不含 `.` 或 `..` 段。
- plist 非空。
- 可执行文件存在时为 thin AArch64 `MH_KEXT_BUNDLE`:
- Magic `0xfeedfacf`。
- CPU type `0x0100000c`。
- File type `11`。
- Subtype `0` 或 `2`(arm64 或 arm64e)。
- load commands 位于文件内。
- kit kext 指定菜单项。
- 菜单项 kext 不指定菜单项。
暂存的是未修改的 plist 和可执行文件。加载器校验签名、解析依赖并注入代码。
## 加载器协议
加载器收到:
```c
typedef struct {
CONST CHAR16 *BundlePath;
CONST CHAR16 *PlistPath;
CONST CHAR16 *ExecutablePath;
CONST VOID *InfoPlist;
UINTN InfoPlistSize;
CONST VOID *Executable;
UINTN ExecutableSize;
} UNIXNU_LOADER_KEXT;
```
缓冲区属于 UniXNU,在 `Boot()` 期间有效。
接受 kext 的加载器实现协议 revision 2。发布版 DarwinBoot 接受内核、ADT sidecar、trust cache 和可选 ramdisk,拒绝携带暂存 kext 的请求。
## 构建 kext
用 Xcode 和 iOS kernel SDK 构建:
```sh
xcodebuild -target MyDriver -sdk iphoneos -configuration Release \
ARCHS=arm64 ONLY_ACTIVE_ARCH=YES
```
目标使用 `MODULE_TYPE = KEXT` 并链接 kernel frameworks:
| 设置 | 值 |
| --- | --- |
| `MACH_O_TYPE` | `mh_kext_bundle` |
| `PRODUCT_TYPE` | `com.apple.product-type.kernel-extension` |
| `SDKROOT` | `iphoneos` |
| `ARCHS` | `arm64` 或 `arm64e` |
| `KERNEL_FRAMEWORKS` | `Kernel` |
可执行文件必须匹配 `xnu-10063.141.1` 的内核 ABI 和 iOS 17 deployment target。
## 签名与信任
AMFI 按 trust cache 和代码签名校验 kext。不受信任的 kext 加载失败并给出代码签名错误。
负载的 trust cache 包必须包含 kext 的 CD hash。负载步骤只接受 SHA-256 位于 `[payload] trust_cache_sha256` 的包。
## 顺序与依赖
UniXNU 按配置顺序暂存。加载器解析 `OSBundleLibraries`,按依赖顺序启动 kext。
无代码 kext 可以用于表达依赖,或把 personality 附到已有二进制上。
## 下一步
- [XNU 驱动](/zh/drivers/xnu-drivers)
- [完整性与启动策略](/zh/concepts/security)
- [启动配置](/zh/reference/boot-configuration)
+68
View File
@@ -0,0 +1,68 @@
# 驱动分层
UniXNU 有三层驱动,分别运行在不同环境,构建和加载方式不同。
| 层 | 运行环境 | 构建产物 | 加载方 |
| --- | --- | --- | --- |
| UEFI 驱动 | UEFI boot services | EDK II `.efi` | 固件 dispatch、UniXNU `[[driver]]` 或 kit |
| 内置 XNU 驱动 | XNU 内核 | 链接进内核的静态库 | 内核启动时 |
| kext 包 | XNU 内核,由 UEFI 暂存 | `.kext` 包 | 实现协议的 XNU 加载器 |
## 选择层次
| 需求 | 层次 |
| --- | --- |
| XNU 启动前可用的显示、输入或存储 | UEFI 驱动 |
| XNU 内的平台专家、CPU、中断控制器、存储、显示或 GPU | 内置 XNU 驱动 |
| 由外部加载器在运行时校验并注入的包 | kext 包 |
UEFI 驱动可以为 UniXNU 菜单提供 framebuffer 和输入,也可以发布供其他 UEFI 驱动使用的协议。
内置 XNU 驱动可以使用完整内核 API,按实际 XNU 构建 ABI 编译,并强制链接进内核。
kext 包保持标准 bundle 布局。UniXNU 暂存 plist 和可执行文件并交给加载器,校验、依赖解析和注入由加载器负责。
## 匹配
UEFI 驱动通过 UEFI driver model 绑定,或作为应用运行。
XNU 驱动通过 `IOKitPersonalities` 绑定。personality 指定 provider 类、驱动类、可选名称匹配和 probe 分数。
| 键 | 含义 |
| --- | --- |
| `IOClass` | 驱动类 |
| `IOProviderClass` | Provider 类 |
| `IONameMatch` | Provider 名称或 `compatible` 值 |
| `IOProbeScore` | 分数越高越先 probe |
| `IOMatchCategory` | 允许第二个驱动匹配同一 provider |
| `IOResourceMatch` | 等待命名资源,例如 `IOBSD` |
内置 personality 位于 `iokit/KernelConfigTables.cpp`,kext personality 位于 bundle 的 `Info.plist`。
## 设备树
ADT 描述硬件。平台专家为每个 `arm-io` 子节点发布 nub。驱动从 provider 读取资源:
```cpp
OSData *reg = OSDynamicCast(OSData, provider->getProperty("reg"));
```
`compatible` 属性用于选择驱动,`IONameMatch` 与 provider 名称比较。
## 驱动目录
| 目录 | 内容 |
| --- | --- |
| `drivers/<Name>` | 内置 XNU 驱动源码和参考 `Info.plist` |
| `kits/<Name>` | 链接进驱动或内核的库 |
| `package/UniXNUPkg/Driver` | UEFI 驱动 |
| `package/UniXNUPkg/Test` | UEFI 测试驱动和负载 |
设备仓库可以覆盖其中任何目录。
## 下一步
- [UEFI 驱动](/zh/drivers/uefi-drivers)
- [XNU 驱动](/zh/drivers/xnu-drivers)
- [kext 包](/zh/drivers/kexts)
- [设备树匹配](/zh/drivers/device-tree-matching)
+201
View File
@@ -0,0 +1,201 @@
# UEFI 驱动
UEFI 驱动是构建为 `.efi` 的 EDK II 模块,运行在 boot services,可以发布协议、绑定设备,为下一阶段准备硬件。
## 模块布局
```
MyDriver/
MyDriver.c
MyDriver.inf
MyProtocol.h
```
`MyDriver.inf`:
```ini
[Defines]
INF_VERSION = 0x00010019
BASE_NAME = MyDriver
FILE_GUID = 00000000-0000-0000-0000-000000000000
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 0.1
ENTRY_POINT = MyDriverEntry
[Sources]
MyDriver.c
[Packages]
MdePkg/MdePkg.dec
UniXNUPkg/UniXNUPkg.dec
[LibraryClasses]
MemoryAllocationLib
UefiBootServicesTableLib
UefiDriverEntryPoint
[Protocols]
gMyProtocolGuid ## PRODUCES
gEfiLoadedImageProtocolGuid ## CONSUMES
```
`MyDriver.c`:
```c
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/MyProtocol.h>
STATIC MY_PROTOCOL mProtocol = { MY_SIGNATURE, NULL };
EFI_STATUS
EFIAPI
MyDriverEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_LOADED_IMAGE_PROTOCOL *Loaded;
EFI_HANDLE Handle = NULL;
if (!EFI_ERROR (gBS->HandleProtocol (
ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&Loaded)) &&
(Loaded->LoadOptions != NULL))
{
mProtocol.Options = AllocateCopyPool (
Loaded->LoadOptionsSize, Loaded->LoadOptions);
}
return gBS->InstallMultipleProtocolInterfaces (
&Handle, &gMyProtocolGuid, &mProtocol, NULL);
}
```
驱动通过 UniXNU 的 `args` 字段获得 `LoadOptions`。
## 构建
把 INF 加入 DSC:
```
[Components]
MyPkg/MyDriver/MyDriver.inf
```
```sh
build -p MyPkg/MyPkg.dsc -a AARCH64 -b DEBUG -t CLANGPDB -n 8
```
输出为 `Build/MyPkg/DEBUG_CLANGPDB/AARCH64/MyDriver.efi`。
UniXNU package 的独立构建 DSC 是:
```
package/UniXNUPkg/UniXNUPkg.dsc
```
`package/build.sh` 把 EFI 程序复制到 `Build/Out/AARCH64-DEBUG`。
## 从 UniXNU 加载
把驱动复制到负载卷,并在 `unixnu.toml` 中加入:
```toml
[[driver]]
path = "Drivers/MyDriver.efi"
args = "board=1"
connect = true
enabled = true
```
| 键 | 含义 |
| --- | --- |
| `path` | 相对 `\EFI\UniXNU` 的路径,或 `fv:<GUID>` |
| `args` | load options 字符串 |
| `connect` | 驱动启动后对所有 handle 运行 `ConnectController` |
| `enabled` | `false` 跳过 |
kit 可以加入驱动:
```toml
[[driver]]
file = "MyDriver.efi"
args = "from-kit"
```
kit 路径相对 kit 目录解析。
## 固件卷路径
`fv:<GUID>` 按文件 GUID 从任意固件卷加载:
```toml
[[driver]]
path = "fv:00000000-0000-0000-0000-000000000000"
```
这样无需把驱动复制到负载即可加载固件中的驱动。
## 加入固件卷
1. 把 INF 加入平台 DSC 的 `[Components]`。
2. 把 INF 加入 FDF 的 `[FV.<name>] INF`。
3. 重新构建固件。
固件 dispatcher 启动 DXE 驱动。FV 中的 UEFI 应用由 UniXNUDxe 或 `Boot####` 选项启动。
## Driver model
遵循 driver model 的 UEFI 驱动实现 `EFI_DRIVER_BINDING_PROTOCOL`:
| 函数 | 作用 |
| --- | --- |
| `Supported` | 报告能否处理控制器 |
| `Start` | 安装子 handle 或协议 |
| `Stop` | 移除 |
`connect = true` 且 `[boot] connect_all` 为 true 时,UniXNU 对所有 handle 调用 `ConnectController`。
## 协议
协议是 GUID 加函数表:
```c
typedef struct {
UINT64 Signature;
EFI_STATUS (EFIAPI *DoWork)(IN struct _MY_PROTOCOL *This);
} MY_PROTOCOL;
extern EFI_GUID gMyProtocolGuid;
```
生产者用 `InstallMultipleProtocolInterfaces` 安装,消费者用 `LocateProtocol` 或 `HandleProtocol` 查找。
## 示例模块
| 模块 | 用途 |
| --- | --- |
| `UniXNUPkg/Test/SampleDriver` | 安装 `UNIXNU_SAMPLE_PROTOCOL` 并记录 load options |
| `UniXNUPkg/Test/HelloPayload` | 注入测试应用 |
| `UniXNUPkg/Driver/UniXNUDxe` | 把 UniXNU 注册为启动选项 |
| `drivers/DarwinBoot` | 安装 `UNIXNU_LOADER_PROTOCOL` |
## 日志
UniXNU 记录每个驱动结果:
```
UNIXNU: driver <path> (<origin>): started
UNIXNU: driver <path> (<origin>): load failed: <status>
UNIXNU: driver <path> (<origin>): start failed: <status>
```
菜单显示加载和失败数量,以及第一条失败原因。
## 下一步
- [XNU 驱动](/zh/drivers/xnu-drivers)
- [kext 包](/zh/drivers/kexts)
- [UEFI 固件](/zh/porting/uefi-firmware)
+256
View File
@@ -0,0 +1,256 @@
# XNU 驱动
UniXNU 的 XNU 驱动是编译进内核的 C++ IOKit 类。内核把驱动作为静态库链接,并通过 `KernelConfigTables.cpp` 中的 personality 启动它。
## 源码布局
```
drivers/MyDriver/
MyDriver.cpp
MyDriver.h
Info.plist
```
`Info.plist` 记录 personality 供参考。内核使用编译进 `iokit/KernelConfigTables.cpp` 的 personality。
## 驱动类
```cpp
#include <IOKit/IOService.h>
#include <IOKit/IOLib.h>
#include <libkern/c++/OSData.h>
class MyDriver : public IOService {
OSDeclareDefaultStructors(MyDriver)
public:
bool start(IOService *provider) override;
void stop(IOService *provider) override;
bool probe(IOService *provider, SInt32 *score) override;
};
OSDefineMetaClassAndStructors(MyDriver, IOService)
bool MyDriver::probe(IOService *provider, SInt32 *score)
{
if (!super::probe(provider, score)) {
return false;
}
*score = 1000;
return true;
}
bool MyDriver::start(IOService *provider)
{
if (!super::start(provider)) {
return false;
}
OSData *reg = OSDynamicCast(OSData, provider->getProperty("reg"));
if (reg == nullptr || reg->getLength() != 4 * sizeof(uint32_t)) {
IOLog("MyDriver: provider has no usable reg property\n");
return false;
}
registerService();
IOLog("MyDriver: started\n");
return true;
}
void MyDriver::stop(IOService *provider)
{
IOLog("MyDriver: stopped\n");
super::stop(provider);
}
```
`start()` 获取 provider,读取属性,分配资源,安装中断处理并调用 `registerService()`。
## 按内核 ABI 编译
驱动必须使用与 XNU 相同的编译参数。内核构建把每个目标文件的完整命令记录在 `.cpo.json`:
```
build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/iokit/DEVELOPMENT/IOPlatformExpert.cpo.json
```
构建脚本读取记录,替换源码和输出路径,加入驱动 include 路径,然后运行编译器:
```python
import json, subprocess
from pathlib import Path
record = json.loads(Path(
"build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/iokit/DEVELOPMENT/IOPlatformExpert.cpo.json"
).read_text())
command = []
i = 0
while i < len(record["arguments"]):
value = record["arguments"][i]
if value == record["file"]:
command.append("drivers/MyDriver/MyDriver.cpp")
elif value == "-o":
command += ["-o", "build/my-driver/MyDriver.o"]
i += 1
elif value == "-Xclang" and record["arguments"][i + 1].startswith("-W"):
i += 1
elif not value.startswith("-W"):
command.append(value)
i += 1
command += ["-Wall", "-Wextra", "-Werror", "-I", "build/my-driver"]
subprocess.run(command, cwd=record["directory"], check=True)
```
目标文件必须满足:
| 字段 | 值 |
| --- | --- |
| Magic | `0xfeedfacf` |
| CPU type | `0x0100000c`(ARM64) |
| File type | `1`(MH_OBJECT) |
| Subtype | `0` |
| Build version | platform `2`(iOS),minimum `0x110000`(17.0) |
编译记录包含使这些值匹配内核的参数。
## 静态库
```sh
xcrun ar rcs build/my-driver/libMyDriver.a build/my-driver/MyDriver.o
```
## 链接进内核
在 `scripts/build-xnu-4k-baseline.sh` 中加入:
```sh
if [ "${XNU_MY_DRIVER:-0}" = 1 ]; then
MY_DRIVER_ARCHIVE=$ROOT/build/my-driver/libMyDriver.a
test -f "$MY_DRIVER_ARCHIVE"
KERNEL_EXTRA_ARCHIVES="$KERNEL_EXTRA_ARCHIVES -Wl,-force_load,$MY_DRIVER_ARCHIVE"
KERNEL_ARCHIVE_DEPENDENCIES="$KERNEL_ARCHIVE_DEPENDENCIES $MY_DRIVER_ARCHIVE"
fi
```
`-force_load` 保留每个目标文件,包括链接器会丢弃的类。把静态库加入 `KERNEL_ARCHIVE_DEPENDENCIES`,库变化时会重新链接内核。
在 defconfig 中启用:
```
CONFIG_XNU_MY_DRIVER=y
```
## 加入 personality
通过补丁把匹配字典加入 `iokit/KernelConfigTables.cpp`:
```c
#if defined(XNU_MY_DRIVER) && XNU_MY_DRIVER
" ,{"
" 'IOClass' = MyDriver;"
" 'IOProviderClass' = IOPlatformDevice;"
" 'IONameMatch' = 'my-device';"
" 'IOProbeScore' = 1000:32;"
" }"
#endif
```
构建脚本把 `-DXNU_MY_DRIVER=1` 传给 `KernelConfigTables.cpo`:
```sh
EXTRA_MAKE_ARGS+=("KernelConfigTables.cpo_CFLAGS_ADD=-DXNU_MY_DRIVER=$MY_DRIVER")
```
personality 是内核 IOKit 配置表中的属性列表字符串。`IOClass` 是 C++ 类名,`IOProviderClass` 是 provider 类,`IONameMatch` 匹配 provider 名称或 `compatible` 值,`IOProbeScore` 决定多个候选的顺序。
## Provider 属性
ADT 节点在平台专家匹配后成为 `IOPlatformDevice` nub。常用属性:
| 属性 | 编码 | 用途 |
| --- | --- | --- |
| `reg` | 32 位或 64 位 cell | 寄存器窗口 |
| `interrupts` | 32 位 cell | 中断号 |
| `compatible` | 字符串列表 | 驱动匹配 |
| `name` | 字符串 | 节点名 |
| `clock-frequency` | 32 位 | 输入时钟 |
| `power-domains` | phandle | 电源依赖 |
`provider->getProperty("reg")` 返回 `OSData`,地址和大小 cell 数量来自父节点。
## 内存
| API | 用途 |
| --- | --- |
| `IOMalloc`、`IOFree` | 内核虚拟内存 |
| `IOMallocContiguous` | 物理连续内存 |
| `IOBufferMemoryDescriptor` | 带物理地址的 DMA 缓冲 |
| `IODMACommand` | 为设备映射缓冲 |
DMA 缓冲必须通过设备的 IOMMU 或 SMMU 上下文映射。`ArmSmmu4K` kit 提供 4 KiB SMMU 表。
## 中断
使用 provider 或平台的中断事件源:
```cpp
IOInterruptEventSource *source = IOInterruptEventSource::interruptEventSource(
this, OSMemberFunctionCast(IOInterruptEventSource::Action,
this, &MyDriver::handleInterrupt), provider, 0);
if (source == nullptr || !workLoop->addEventSource(source)) {
return false;
}
source->enable();
```
Provider 需要发布中断控制器或中断资源。通用 ARM64 平台上 `ArmCpu` 提供 GIC 路径。
## 设备树资源
平台专家为每个 `arm-io` 子节点发布 nub。服务 `arm-io` 范围以下设备的驱动从板级常量取得物理地址。
匹配 `/product` 或 `/buttons` 的驱动使用 `IOPlatformExpertDevice` 作为 provider。
## 运行时行为
| 阶段 | 调用 |
| --- | --- |
| 匹配 | `probe()` |
| 启动 | `start()` |
| 发布 | `registerService()` |
| 停止 | `stop()` |
`start()` 失败的驱动释放资源并返回 `false`。
## 测试
| 测试 | 方法 |
| --- | --- |
| 编译 | 按记录参数使用 `-Wall -Wextra -Werror` |
| 对象 ABI | 检查 magic、CPU type、file type、subtype、build version |
| 主机逻辑 | 抽出纯函数并编译主机测试 |
| 内核启动 | QEMU 启用驱动并读取启动进度日志 |
| 硬件 | 启动设备并读取驱动日志 |
## 参考驱动
| 驱动 | 展示内容 |
| --- | --- |
| `ArmDTPlatform` | 平台专家和设备 nub |
| `ArmCpu` | CPU、GIC 和定时器 |
| `NativeUfs` | 存储、SMMU 和命令队列 |
| `NativeAES` | 带原始 ABI 的加速器子类 |
| `NativeAdreno` | GPU 电源、固件、ring 和 user client |
## 完整示例
[sheng 触控驱动](/zh/devices/sheng-touch-driver)展示完整结构:纯处理核心、总线接口后的控制器协议、带来源的板级常量、分阶段探针和 HID 发布。
## 下一步
- [kext 包](/zh/drivers/kexts)
- [设备树匹配](/zh/drivers/device-tree-matching)
- [构建内核](/zh/guide/build-kernel)
+116
View File
@@ -0,0 +1,116 @@
# 启动镜像
启动镜像从内存运行。流程启动镜像,并保持已安装的 boot 分区不变。
## 前提
- bootloader 已解锁并支持 `fastboot`。
- 设备的 UEFI 移植与板级配置匹配。
- 已构建带负载的启动镜像,或用空 ramdisk 做启动管理器冒烟测试。
- 板级 UART 可用时准备串口。
## 启动
```sh
fastboot devices
fastboot boot out/<board>/boot/boot.img
```
`fastboot boot` 传输镜像并启动一次。设备原有的 boot 分区和已安装系统保持不变。
启动镜像包含:
| 部分 | 内容 |
| --- | --- |
| kernel | BootShim 和 UEFI 固件卷的 gzip 数据 |
| ramdisk | UniXNU 负载 FAT 卷 |
| 追加数据 | 设备树 |
Android bootloader 加载镜像,校验 BootShim 的 ARM64 header,然后跳转到 shim。BootShim 启动固件卷。
## 启动管理器
UniXNU 把版本和配置摘要写到串口并绘制菜单。默认配置包含超时、默认项和倒计时。
| 操作 | 按键 |
| --- | --- |
| 移动高亮 | 上、下 |
| 启动选中项 | Enter |
| 停止倒计时 | 任意键 |
菜单项来自 `\EFI\UniXNU\unixnu.toml`。菜单项可以启动 EFI 程序、带 EFI stub 的 Linux、执行动作,或通过加载器协议启动 Darwin 内核。
UniXNU 找不到配置时,会扫描每个卷的 `\EFI\BOOT\BOOTAA64.EFI`,把这些菜单项加到内置动作之前。
## Darwin 启动
类型为 `xnu` 的菜单项由 DarwinBoot 读取以下文件:
| 文件 | 必需 | 内容 |
| --- | --- | --- |
| `kernel.macho` | 是 | 预链接 XNU 内核 |
| `kernel.macho.adt` | 是 | Apple 格式设备树 sidecar |
| `kernel.macho.trustcache` | 否 | trust cache 包 |
| 菜单项 `initrd` | 否 | Data ramdisk 种子 |
DarwinBoot 把内核段复制到连续物理窗口,构造 `boot_args`,把 ADT 和 trust cache 地址写入 `/chosen/memory-map` 占位符,然后退出 boot services。
默认窗口为 256 MiB。ADT 属性 `/chosen/darwinboot-memory-size` 可以请求更大的窗口。
## 首次启动
首次启动需要 userdata 镜像。镜像提供 OS 与 SystemOS 卷,以及内核匹配的根卷 UUID。
```sh
fastboot flash userdata out/<board>/userdata/userdata-root.img
fastboot reboot bootloader
fastboot boot out/<board>/boot/boot.img
```
userdata 镜像较大。用 `unixnu build userdata` 构建,并按设备仓库报告中的命令刷写。
Data 种子提供可写 Data 卷。`--skip-setup` 在种子副本中把 Setup 标记为完成,跳过 Hello 页面。
## 日志
板级 UART 可用时,UEFI 固件写到 `EFI_SERIAL_IO_PROTOCOL`,UniXNU 的行以 `UNIXNU:` 开头。
内核在命令行选择串口时写日志,例如 `serial=3`。启动进度层把检查点写到 framebuffer 和串口。
| 来源 | 位置 |
| --- | --- |
| 构建驱动 | `out/<board>/logs/*.log` |
| 启动管理器 | 串口,`UNIXNU:` 行 |
| DarwinBoot | 串口,`DARWINBOOT:` 行 |
| XNU | 串口内核输出 |
## 恢复
镜像从内存运行。复位设备即可回到原有 bootloader 和已安装系统。
```sh
fastboot reboot bootloader
```
固件在菜单前停住时,复位设备并再次启动同一镜像。固件停住后设备仍在 bootloader,第二次 `fastboot boot` 使用同一镜像。
## macOS
启动流程由 IPSW 驱动。macOS 恢复镜像按相同成员布局提供 `KernelCache`、`DeviceTree`、`OS`、`Cryptex1,SystemOS`、`StaticTrustCache` 和 `Cryptex1,SystemTrustCache`。
启动 macOS:
1. 把板级文件的 `[ipsw]` 指向 macOS IPSW,并填入该镜像的产品、版本、build、device class、chip ID 和 board ID。
2. 在 `[ipsw.sha256]` 中固定成员哈希。
3. 把 `[userdata] windows` 设为 `OS` 和 `Cryptex1,SystemOS`。
4. 构建内核、负载、启动镜像和 userdata 镜像。
5. 刷写 userdata 镜像并启动启动镜像。
设备树和平台驱动提供硬件描述,用户态来自 macOS 磁盘镜像。
## 下一步
- [调试与日志](/zh/guide/debugging)
- [userdata 镜像](/zh/guide/userdata)
- [启动参数](/zh/xnu/boot-arguments)
- [故障排查](/zh/reference/troubleshooting)
+120
View File
@@ -0,0 +1,120 @@
# 构建内核
内核步骤从 Apple XNU 源码构建,并打上 UniXNU 补丁,然后与原始 kernelcache 预链接。
## 源码与构建树
| 路径 | 内容 |
| --- | --- |
| `out/<board>/tree/refer/xnu` | 固定版本的 XNU 源码 |
| `out/<board>/tree/.cache/xnu-work` | 应用补丁的工作克隆 |
| `out/<board>/tree/build/xnu/ios-obj` | 目标文件和编译命令记录 |
| `out/<board>/tree/build/xnu/ios-sym` | 符号 |
| `out/<board>/tree/build/xnu/ios-dst` | 安装头文件 |
| `out/<board>/tree/.cache/fakeroot` | 内核 SDK 根 |
| `out/<board>/kernel/kernel.macho` | 预链接内核 |
源码版本来自 `feeds.toml`:
```toml
[[source]]
name = "xnu"
kind = "refer"
git = "https://github.com/apple-oss-distributions/xnu.git"
ref = "xnu-10063.141.1"
commit = "d8b80295118ef25ac3a784134bcf95cd8e88109f"
darwin = "23.6.0"
version = "10063.141.1"
driverkit_tag = "xnu-10063.121.3"
trustcache_tag = "xnu-8796.101.5"
```
`unixnu build kernel` 把这些值作为 `UNIXNU_XNU_TAG`、`UNIXNU_XNU_COMMIT`、`UNIXNU_XNU_DARWIN`、`UNIXNU_XNU_VERSION`、`UNIXNU_DRIVERKIT_TAG` 和 `UNIXNU_TRUSTCACHE_TAG` 传给 `scripts/build-xnu-4k-baseline.sh`。
构建目标:
```
SDKROOT=iphoneos
ARCH_CONFIGS=ARM64
MACHINE_CONFIGS=BCM2837
KERNEL_CONFIGS=DEVELOPMENT
ARM64_LINK_ADDR=0xfffffff007004000
ARM64_PLKSEG_ADDR=0xfffffff004004000
DEPLOYMENT_TARGET_FLAGS="-miphoneos-version-min=17.0 -DXNU_TARGET_OS_IOS"
```
`BCM2837` 携带 4 KiB 页布局。内核仍以 iOS 17 为目标,以匹配 iPadOS kernelcache 的预链接 ABI。
## defconfig 开关
每个 `CONFIG_XNU_<NAME>` 变成构建环境中的 `XNU_<NAME>`。`y` 变成 `1`,`n` 变成 `0`,字符串按文本传入,字符串中的 `{tree}` 替换为构建树路径。
| 开关 | 作用 | 静态库 |
| --- | --- | --- |
| `XNU_SOURCE_CORECRYPTO14` | 构建并链接源码 corecrypto | `build/corecrypto-2023/libcorecrypto_kernel_noasm-namespaced.a` |
| `XNU_SOURCE_PTHREAD` | 构建并注册源码 pthread | `build/libpthread-kernel/libpthread_kernel.a` |
| `XNU_ARM_DT_PLATFORM` | 链接 Apple 格式设备树平台专家 | `build/armdt-platform/libArmDTPlatform.a` |
| `XNU_ARMVIRT_PLATFORM` | 链接 QEMU virt 平台专家 | `build/armvirt-platform/libArmVirtPlatform.a` |
| `XNU_ARMCPU` | 链接 CPU 与中断控制器 | `build/armcpu/libArmCpu.a` |
| `XNU_NATIVE_UFS` | 链接 UFS 存储驱动 | `build/native-ufs/libNativeUfs.a` |
| `XNU_NATIVE_AES` | 链接 AES 加速器驱动 | `build/native-aes/libNativeAES.a` |
| `XNU_NATIVE_VIRTIO_BLOCK` | 链接 VirtIO 块驱动 | `XNU_NATIVE_VIRTIO_BLOCK_ARCHIVE` 或 `build/native-virtio-block/libNativeVirtioBlock.a` |
| `XNU_NATIVE_RAM_BLOCK` | 链接 RAM 块驱动 | `build/native-ram-block/libNativeRamBlock.a` |
| `XNU_NATIVE_VIRTIO_CONSOLE` | 链接 VirtIO 控制台 | `build/native-virtio-console/libNativeVirtioConsole.a` |
| `XNU_NATIVE_MOBILE_DISPLAY` | 链接移动显示驱动 | `build/native-mobile-display/libNativeMobileDisplay.late.a` |
| `XNU_NATIVE_LEGACY_DISPLAY` | 链接旧式显示驱动 | `build/native-legacy-display/libNativeLegacyDisplay.late.a` |
| `XNU_NATIVE_ADRENO` | 链接 Adreno GPU 驱动 | `build/native-adreno/libNativeAdreno.a` |
| `XNU_NATIVE_OFFLINE_GPU` | 链接离线 GPU broker | `build/native-offline-gpu/libNativeOfflineGPU.late.a` |
| `XNU_VERIFIED_ROOT_PROBE` | 链接 verified root | `XNU_VERIFIED_ROOT_ARCHIVE` 或 `build/verified-root-xnu/libVerifiedRootProbe.a` |
| `XNU_NATIVE_CS_HASH_CACHE` | 从已验证 16 KiB 块派生 4 KiB 签名哈希 | 无 |
| `XNU_NATIVE_ARCH_TIMER` | 使用架构定时器入口 | 无 |
| `XNU_NATIVE_PAN` | 启用 ARMv8.1 PAN | 无 |
| `XNU_DT_PANIC_REGION` | 从设备树读取 panic 区域 | 无 |
| `XNU_TRUST_CACHE_RUNTIME` | 初始化 trust cache 运行时 | 无 |
| `XNU_BOOT_PROGRESS` | 加入启动进度层 | `build/boot-progress/libBootProgress.a` |
| `XNU_SOURCE_MOCKFS` | 加入 mockfs 测试文件 | `build/mockfs-fixture/libmockfs_fixture.a` |
设备仓库负责生成自己的静态库。先构建一次内核得到编译命令记录,再运行设备脚本,然后重新构建内核。
## 构建顺序
1. `unixnu fetch` 克隆 XNU 和 Apple 参考源码。
2. `unixnu build kernel` 应用补丁并构建 XNU。当 `XNU_BOOT_PROGRESS=1` 且 `startup.o.json` 不存在时,先用关闭进度层的配置构建一次,再开启进度层构建。
3. 构建设备静态库。脚本使用 `build/xnu/ios-obj/.../*.cpo.json` 中记录的实际编译参数。
4. `unixnu build kernel` 把静态库链接进内核。
5. `unixnu build prelink` 生成 `out/<board>/kernel/kernel.macho`。
预链接读取 `board.toml` 的 `[kernel.prelink]`:
```toml
[kernel.prelink]
tool = "kits/NativePrelink/emit_prelink.py"
kernel = "build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf"
kernelcache_sha256 = "..."
slide = "-0x1000000"
early_security_init = "com.apple.driver.AppleMobileFileIntegrity=0xfffffff005ab4ae0"
roots = [
"com.apple.driver.AppleARMPlatform",
"com.apple.filesystems.apfs",
]
```
`roots` 列出原生内核使用的原始 kernelcache 组件。`early_security_init` 指定在预链接安全初始化期间运行的组件地址。
## 新增内核开关
1. 在设备 defconfig 中加入 `CONFIG_XNU_<NAME>=y`。
2. 在 `scripts/build-xnu-4k-baseline.sh` 中读取 `XNU_<NAME>`。
3. 用 `EXTRA_MAKE_ARGS+=("<file>.o_CFLAGS_ADD=-DXNU_<NAME>=$VALUE")` 加入目标文件开关。
4. 把静态库加入 `KERNEL_EXTRA_ARCHIVES` 和 `KERNEL_ARCHIVE_DEPENDENCIES`。
5. 通过补丁把 personality 加入 `iokit/KernelConfigTables.cpp`。
## 使用已有内核
`unixnu build payload --kernel FILE` 用已有的预链接内核打包,跳过 XNU 构建。
## 下一步
- [4 KiB 页表](/zh/xnu/four-k-page-tables)
- [补丁参考](/zh/xnu/patch-reference)
- [预链接](/zh/concepts/prelink)
+287
View File
@@ -0,0 +1,287 @@
# 调试与日志
bring-up 需要四类证据:串口输出、屏幕诊断页、panic 屏幕和构建报告。把它们一起收集。
## 日志来源
| 来源 | 通道 | 用途 |
| --- | --- | --- |
| UEFI 串口 | UART | 固件、UniXNU、DarwinBoot |
| 内核串口 | 由 `serial=` 选择的 UART | XNU 控制台和 panic 输出 |
| 诊断页 | framebuffer | 控制台停止后的实时状态 |
| 脉冲条 | framebuffer 底部 | 诊断页占用面板时的心跳 |
| panic 屏幕 | framebuffer | panic 文本和寄存器 |
| QEMU 日志 | 文件 | UniXNU 和加载器测试 |
| 构建报告 | JSON | 输入哈希和阶段结果 |
## 串口
把参数加入负载:
```toml
[payload]
kernel_args = ["-v", "serial=3", "debug=0x14e"]
```
UniXNU 启动管理器写到第一个 `EFI_SERIAL_IO_PROTOCOL`,行以 `UNIXNU:` 开头。DarwinBoot 的行以 `DARWINBOOT:` 开头。
抓取串口:
```sh
tio -b 115200 -d 8 -p none -s 1 /dev/tty.usbserial-0001 -L out/serial.log
```
其他工具:
```sh
picocom -b 115200 /dev/tty.usbserial-0001
screen -L /dev/tty.usbserial-0001 115200
minicom -D /dev/tty.usbserial-0001 -b 115200
```
设备页面记录 UART 引脚和波特率。支持 USB device 模式的板子可以暴露 CDC ACM 串口。
常用的早期输出:
```
UNIXNU: UniXNU 0.1.0 starting as firmware boot option
UNIXNU: config: loaded \EFI\UniXNU\unixnu.toml
UNIXNU: driver ...: started
UNIXNU: menu: shown 2 entries, default 0
DARWINBOOT: loader v2 installed EL1
DARWINBOOT: ARM64 va=... pa=... args=... entry=... owned=...
DARWINBOOT: inputs validated; exiting boot services
```
## 屏幕诊断页
诊断页是绘制到 XNU framebuffer 映射上的全屏文本覆盖层。启用方式:
```
boot-progress=1
```
级别 1 启动监视器。30 秒没有阶段或存储活动,或达到 120 秒观察上限时,页面冻结并保留屏幕,便于拍照。
```
boot-progress=2
```
级别 2 连续运行。超过时限只报告,系统继续运行。
页面使用 48 行、120 列。面板至少 2160x1400 时缩放为 3。
| 行区域 | 内容 |
| --- | --- |
| 标题 | 构建标签、live 或 stop 状态、原因 |
| 1 | 时间、空闲、IRQ 计数、监视器计数、root 状态、进程数、空闲页 |
| 2 | 存储请求、已验证块、失败、完整性计数 |
| 3 到 30 | 阶段历史、进程状态、存储细节、最后 PC 和寄存器 |
| 31 到 47 | 缓存存储输出、UFS 计时、内存、进程采样、UI trace、触控行 |
| 47 | 独立脉冲条 |
给镜像加标签,拍照后可以确认构建:
```
sheng-build-tag=ks-001
```
标题栏会以 `[ks-001]` 开头。
用户态接管显示并停止扫描输出后,可以收回面板:
```
boot-diag-resume=10
```
10 秒没有 scanout 后页面重绘。新的有效 scanout 会再次让出面板。
页面字段:
| 字段 | 含义 |
| --- | --- |
| `time` | 监视器启动后的秒数 |
| `idle` | 距上次阶段或 I/O 进展的秒数 |
| `IRQ` | 监视器统计的中断入口数 |
| `monitor` | 监视器线程迭代数 |
| `lost` | 丢弃的记录更新数 |
| `root` | owner root 接受状态 |
| `procs` | 进程数 |
| `free` | 空闲 4 KiB 页 |
| `requests` | 存储请求 |
| `verified4K` | 已验证 4 KiB 块 |
| `failures` | 存储失败 |
| `integrity` | 存储完整性失败 |
| `last PC` | 最后中断入口 PC |
| `TCR`、`TTBR`、`SCTLR` | 翻译控制和页表基址寄存器 |
## 脉冲条
脉冲条位于面板底部,高 32 像素,2 倍缩放,绿色文字,由独立高优先级监视线程每秒更新。
```
P<turn> I<irq> M<monitor>/<seconds> B<bind>:<result> d<depth> @<base> lock<busy>/<render> PC<pc>
```
| 字段 | 含义 |
| --- | --- |
| `P` | 脉冲轮次 |
| `I` | 中断入口数 |
| `M` | 监视器计数和距上次推进的秒数 |
| `B` | framebuffer 绑定次数、结果和深度 |
| `@` | framebuffer 基址 |
| `lock` | 记录器和渲染器忙碌标志 |
| `PC` | 最后中断入口 PC |
诊断页占用面板时脉冲条持续存在。OS 接管显示后它关闭。`boot-diag-resume` 会重新打开诊断页和脉冲条。
## panic 屏幕
负载可以把 panic 屏幕打进内核副本:
```toml
[payload]
panic_screen = true
```
负载步骤运行:
```sh
python3 scripts/patch-xnu-entry-probe.py <kernel> --panic-screen --no-reset
```
panic 屏幕把格式化后的 panic 消息和寄存器写到 framebuffer,并保持显示。`--no-reset` 停止自动复位,屏幕保留到拍照完成。
第一条 panic 消息也会复制到 `boot_progress_panic_text`。secondary core 上的 panic 只停住该 core,实时横幅在屏幕重绘后仍保留文本。
设备树 panic 区域需要:
```
CONFIG_XNU_DT_PANIC_REGION=y
```
内核读取 `/chosen` 的 `embedded-panic-log-size` 和区域的 `reg` 属性,映射前先校验。
## 用户态故障证据
故障探针在异常投递前记录 EL0 breakpoint 和 abort 状态:
| 字段 | 含义 |
| --- | --- |
| `pid`、`name` | 进程 |
| `PC`、`LR`、`SP`、`FP` | 故障时寄存器 |
| `ESR`、`FAR` | 异常综合征和故障地址 |
| `slide` | shared region slide |
| `x0` 到 `x28` | 通用寄存器 |
| frames | 最多 12 个返回地址 |
捕获在 trap 路径中完成,不分配内存。匹配的进程退出时,记录随信号打印。未捕获异常的原因会从退出进程的可写 malloc 区域中搜索并打印到崩溃行。
## UI 栈追踪
UI trace 采样指定进程,打印 PC、LR、FP 和栈切片:
```
boot-ui-focus=launchd
boot-ui-focus2=SpringBoard
boot-ui-focus3=backboardd
boot-ui-focus4=Setup
boot-ui-chrono=1
```
地址使用 64 进制字母表编码,以适配行宽。把抄录的行交给解码器:
```sh
python3 scripts/decode-boot-ui-trace.py trace.txt
```
解码器为每个线程组输出一个 JSON 对象,包含 `pc`、`lr`、帧和错误码。
trace 每 8 秒轮换一页。间隔几秒拍三到四张照片,覆盖全部页面。
## 拍照
诊断页按拍照场景设计。建议流程:
1. 把 `sheng-build-tag` 设为简短的镜像标签。
2. 冻结页面用 `boot-progress=1`,实时页面用 `boot-progress=2`。
3. 用户态会清屏时设置 `boot-diag-resume`。
4. 把设备放在支架上,相机与面板平行。
5. 关闭闪光灯,从侧面打光,避免反射。
6. 对标题栏锁定焦点和曝光。
7. 让面板填满画面,包含四个角。
8. 先拍标题栏,再拍整页。
9. UI trace 页面间隔 8 秒拍三到四张。
10. 拍底部脉冲条。
11. 记录启动镜像 SHA-256 和拍摄时间。
| 检查项 | 原因 |
| --- | --- |
| 标题栏可读 | 构建标签和状态标识镜像 |
| 文字区域无眩光 | 第 3 到 30 行承载阶段历史 |
| 四角可见 | 确认完整面板和缩放 |
| 脉冲条可见 | 显示拍摄时的心跳 |
| 至少两张 | 变化的计数器说明屏幕在实时更新 |
相机照片是面板显示内容的证据。保留原始文件。只抄录需要的行,并把照片与抄录一起保存。
## QEMU 抓取
```sh
python3 package/Scripts/qemu-run.py out/esp \
--log out/qemu.log --screenshot out/qemu.png \
--expect 'UNIXNU: menu: shown'
```
运行器把去掉 ANSI 的日志写到 `out/qemu.clean.log`。`--screenshot` 在菜单出现时截屏。`--keys` 在菜单后发送 QEMU 键码。
停止辅助脚本在指定符号处记录寄存器和内存:
```sh
python3 scripts/qemu-entry/gdb-stop.py --help
```
## 调试参数
| 参数 | 用途 |
| --- | --- |
| `-v` | 详细内核输出 |
| `serial=3` | 串口控制台 |
| `debug=0x14e` | 调试输出掩码 |
| `kextlog=0xfff` | kext 日志 |
| `boot-progress=1` | 诊断页,30 秒停滞后冻结 |
| `boot-progress=2` | 诊断页连续运行 |
| `sheng-build-tag=<tag>` | 标题栏镜像标签 |
| `boot-diag-resume=<seconds>` | OS 停止 scanout 后收回面板 |
| `sheng-platform-stage=<n>` | 在平台专家阶段停住 |
| `sheng-touch=<n>` | 触控探针阶段 |
| `offline-legacy-phase=<n>` | 显示探针阶段 |
| `-no_slto_panic` | 关闭 SLTO panic |
| `cs_debug=<mask>` | 代码签名输出 |
## 证据包
收集:
| 项目 | 来源 |
| --- | --- |
| 串口日志 | UART 抓取文件 |
| 诊断照片 | 标题栏、整页、trace 页、脉冲条 |
| panic 照片 | panic 屏幕 |
| 启动报告 | `out/<board>/boot/boot.json` |
| 负载报告 | `out/<board>/payload/payload.json` |
| 构建日志 | `out/<board>/logs/` |
每张照片都记录启动镜像哈希:
```sh
shasum -a 256 out/<board>/boot/boot.img
jq . out/<board>/boot/boot.json
```
## 下一步
- [启动参数](/zh/xnu/boot-arguments)
- [故障排查](/zh/reference/troubleshooting)
- [QEMU 测试](/zh/reference/qemu)
- [sheng 设备页](/zh/devices/sheng)
+129
View File
@@ -0,0 +1,129 @@
# 总览
UniXNU 在 ARM64 设备上从 UEFI 环境启动 Darwin 系操作系统。整个栈分为六层,每一层都有对应的仓库、构建产物和交接约定。
## 分层模型
```
Android bootloader (ABL)
|
| fastboot boot boot.img
v
BootShim + UEFI 固件卷 + 设备树
|
| UEFI BDS 启动 UniXNU
v
UniXNU(UEFI 启动管理器)
|
| UNIXNU_LOADER_PROTOCOL
v
DarwinBoot(XNU 加载器)
|
| boot_args、ADT、trust cache、ramdisk
v
打过补丁的 XNU 内核
|
| IOKit、BSD、launchd
v
来自 IPSW 的用户态(OS 与 SystemOS)
```
## 组件
### 启动镜像
启动镜像是 header version 0 的 Android boot image。kernel 字段存放 gzip 数据,由两部分组成:编译后的 BootShim 和 UEFI 固件卷。gzip 流之后是设备树。ramdisk 字段存放 UniXNU 负载。
设备原有的 Android bootloader 按 Mu-Silicium 设备配置中的地址加载镜像,校验 ARM64 image header,然后跳转到 shim。BootShim 重定位固件卷并进入 UEFI。
### UEFI 固件
固件来自设备的 Mu-Silicium 仓库,提供启动管理器所需的服务:
- `EFI_GRAPHICS_OUTPUT_PROTOCOL`:菜单和内核 framebuffer。
- `EFI_SIMPLE_TEXT_IN_PROTOCOL`:菜单输入。
- `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` 与 `EFI_BLOCK_IO_PROTOCOL`:负载卷和磁盘分区。
- `EFI_SERIAL_IO_PROTOCOL`:板级 UART 日志。
`unixnu build firmware` 会把板级补丁应用到共享 edk2 树,构建板级 DSC,并返回 `SILICIUM_UEFI.fd`。
### 启动管理器
UniXNU 启动管理器是由 `package/UniXNUPkg` 构建的 UEFI 应用。它先从启动所在卷读取 `\EFI\UniXNU\unixnu.toml`,再扫描其他卷。配置声明菜单项、驱动、设备树补丁、ACPI 表、块设备切片和 kit。
菜单出现前,启动管理器完成五件事:
1. 把每个 `[[block_slice]]` 发布为块设备。
2. 加载并启动配置中的 UEFI 驱动。
3. 安装配置中的 ACPI 表。
4. 合并 `[match]` 条件满足的 kit。
5. 为选中项准备设备树。
类型为 `xnu` 的菜单项会交给实现 `UNIXNU_LOADER_PROTOCOL` 的加载器。
### DarwinBoot
DarwinBoot 是固件卷中的 UEFI 驱动,在 EL2 或 EL1 安装加载器协议。处理 `xnu` 菜单项时执行以下步骤:
1. 从负载卷读取内核 Mach-O。
2. 读取内核旁边的 `.adt` sidecar。
3. 读取可选的 `.trustcache` sidecar。
4. 校验 Mach-O 段、ADT 和 trust cache 结构。
5. 为内核及其数据分配物理窗口。
6. 复制内核段、ADT、trust cache 和 ramdisk。
7. 构造 Darwin `boot_args`。
8. 退出 boot services,关闭 MMU 和缓存,在 EL1 跳转到内核入口。
加载器要求单个 ARM64 Mach-O,包含 `__TEXT` 段、`LC_MAIN` 入口,基地址按固件页大小对齐。
### XNU 内核
内核由 Apple `xnu-10063.141.1` 源码加上 `patches/xnu` 中的补丁构建。BCM2837 机型配置提供非 Apple ARM64 板所需的 4 KiB 页布局。
构建出的原生内核会与目标 IPSW 的原始 kernelcache 预链接。预链接保留原始 Apple kext,并把原生内核绑定到它们的符号。结果是单个 `kernel.macho`,包含原生内核段和选中的原始组件。
### 用户态
userdata 镜像保存 IPSW 中未修改的 `OS` 和 `Cryptex1,SystemOS` 磁盘镜像。分区开头有一个 4 KiB manifest,描述两个窗口。原生 UFS 或块驱动发布分区,XNU 按 UUID 挂载根卷。
## 仓库
| 仓库 | 用途 |
| --- | --- |
| `UniXNU` | 构建驱动、UEFI 启动管理器、DarwinBoot、通用驱动、kit、补丁和文档 |
| `UniXNU-<device>` | 板级描述、内核 defconfig、设备脚本、设备驱动和 package |
| `uefi-<device>` | 设备的 Mu-Silicium UEFI 移植 |
设备仓库的目录布局与 `UniXNU` 相同。它的 `drivers/`、`kits/`、`patches/`、`scripts/` 和 `package/` 会覆盖同名通用模块。
## 构建产物
所有输出位于 `out/`:
```
out/
state.json 记住的设备仓库
feeds/ 拉取的 git 源码
ipsw/<build>/ 解压的 IPSW 成员
<board>/
.config 选中的 defconfig
tree/ 组装后的构建树
logs/ 每一步的日志
kernel/kernel.macho 预链接内核
package/Build/Out/ UniXNU 和测试 EFI 程序
payload/payload.img FAT 负载卷
payload/payload.json 负载报告
boot/boot.img Android 启动镜像
boot/boot.json 启动镜像报告
userdata/userdata-root.img
userdata/userdata-root.json
```
报告记录输入哈希和生成下一步的命令。给定固件卷、设备树和 ramdisk 时,启动镜像是确定性的。ADT 包含随机种子,因此两次负载构建的结果不同。
## 下一步
- [构建环境](/zh/guide/requirements)列出主机工具和外部输入。
- [构建](/zh/building)介绍驱动命令。
- [移植](/zh/porting/)介绍如何新增设备。
- [设备](/zh/devices/)列出设备仓库。sheng 页面记录一个设备,通用指南使用占位符。
+55
View File
@@ -0,0 +1,55 @@
# 构建环境
## 主机
构建驱动在 macOS 上运行。内核步骤调用带 `iphoneos` SDK 的 `xcodebuild`,`--skip-setup` 调用 `hdiutil`。
| 工具 | 用途 | 安装方式 |
| --- | --- | --- |
| macOS 与 Xcode | 内核、skip-setup | App Store 或 developer.apple.com |
| Python 3.11 或更新 | 所有步骤 | python.org 或 Homebrew |
| git、make | 拉取、固件 | Xcode command line tools 或 Homebrew |
| cmake、ninja | 内核参考源码 | `brew install cmake ninja` |
| unifdef | libdispatch 头文件 | `brew install unifdef` |
| LLVM 与 lld | 内核、固件、启动镜像 | `brew install llvm` |
| mtools | 负载卷 | `brew install mtools` |
| dtc | 设备树属性 | `brew install dtc` |
| QEMU 与 AAVMF | 可选冒烟测试 | `brew install qemu` |
驱动会直接找到 Homebrew 的 `/opt/homebrew/opt/llvm/bin`,无需修改 `PATH`。其他工具需要在 `PATH` 中。
`unixnu fetch` 需要网络,用于拉取 Apple 源码参考和 Mu_Basecore 树。
## 外部输入
以下输入位于 UniXNU 仓库之外。
| 输入 | 使用步骤 | 选项 | 环境变量 |
| --- | --- | --- | --- |
| 设备的 Mu-Silicium 仓库 | 固件、启动镜像 | `--uefi DIR` | `UNIXNU_UEFI` |
| 目标 IPSW,本地文件或 HTTPS URL | 内核预链接、userdata | `--ipsw PATH_OR_URL` | `UNIXNU_IPSW` |
| 解压后的 kernelcache | 内核预链接 | `--kernelcache FILE` | `UNIXNU_KERNELCACHE` |
| 编译后的设备树 | 启动镜像 | `--dtb FILE` | `UNIXNU_DTB` |
| trust cache 包 | 负载 | `--trust-cache FILE` | |
| Data ramdisk 种子 | 负载 | `--data-ramdisk FILE` | |
| 平台种子 | 负载 | `--unique-chip-id HEX` | `UNIXNU_UNIQUE_CHIP_ID` |
| 预编译静态库 | 内核 | 负载可用 `--kernel FILE` | |
| edk2 或 Mu_Basecore 树 | package、固件 | `--edk2 DIR` | `UNIXNU_EDK2` |
设备仓库提供板级描述、内核 defconfig 和生成设备静态库的脚本。
## 设备
设备需要满足:
- bootloader 可解锁并接受 `fastboot boot`。
- UEFI 移植提供 framebuffer、输入、块 I/O 和文件系统。
- bring-up 期间有 UART 或 USB 日志通道。
- 内存足以容纳内核窗口、负载和 userdata 镜像。
- 目标系统恢复镜像和匹配的设备身份。
## 下一步
- [构建](/zh/building)介绍使用这些输入的命令。
- [设备仓库](/zh/porting/device-repository)介绍板级文件。
- [设备](/zh/devices/)列出设备仓库和各自的输入。
+94
View File
@@ -0,0 +1,94 @@
# userdata 镜像
userdata 镜像为 XNU 提供根文件系统。它保存目标 IPSW 中未修改的 `OS` 和 `Cryptex1,SystemOS` 磁盘镜像,并在前面加一个 manifest。
## 构建
```sh
./unixnu build userdata --ipsw /path/to/restore.ipsw
```
步骤按 `[userdata] windows` 提取 IPSW 组件,用 `hdiutil` 读取每个磁盘镜像,写出原始镜像到 `out/<board>/userdata/userdata-root.img`。
报告 `userdata-root.json` 记录窗口偏移、大小、哈希、分区容量和刷写命令。
只检查不写文件:
```sh
./unixnu build userdata --dry-run
```
## 分区
`board.toml` 描述 GPT 条目:
```toml
[userdata]
first_lba = 0x2f3840
last_lba = 0x3d0abfa
lba_bytes = 4096
windows = [
{ name = "OS", component = "OS" },
{ name = "SystemOS", component = "Cryptex1,SystemOS" },
]
```
`first_lba` 和 `last_lba` 来自设备上名为 `userdata` 的 GPT 条目,`lba_bytes` 是分区逻辑块大小。
镜像大小为各窗口按 1 MiB 对齐后的总和,加上 1 MiB 头部区域。步骤拒绝超过分区容量的镜像。
## 布局
```
0x000000 SHNGROOT manifest,4096 字节,零填充到 1 MiB
0x100000 OS 窗口,完整磁盘镜像
.... SystemOS 窗口,完整磁盘镜像
```
manifest 为小端:
| 偏移 | 字段 |
| --- | --- |
| 0 | `SHNGROOT` |
| 8 | 版本,32 位,值 1 |
| 12 | 窗口数,32 位 |
| 16 | 镜像大小,64 位 |
| 24 | 窗口条目,每条 128 字节 |
| 4064 | 字节 0 到 4063 的 SHA-256 |
每个窗口条目包含 32 字节名称、64 位偏移、64 位大小、32 位块大小、32 位标志、32 字节 SHA-256 和 40 字节保留。
## 刷写
```sh
fastboot flash userdata out/<board>/userdata/userdata-root.img
```
驱动把命令写入报告。设备仓库可以提供封装脚本,在刷写前检查镜像和分区。
## 根卷选择
内核按 UUID 挂载根卷:
```toml
[payload.adt]
root_match_volume_uuid = "00000000-0000-0000-0000-000000000000"
```
ADT 生成器写入 `/chosen/root-matching`,包含 `IOProviderClass = IOMedia` 和该 UUID。原生块驱动发布 userdata 分区,分区方案发布卷,XNU 选择 UUID 匹配的 IOMedia。
## Data 卷
可写 Data 卷来自负载中的 Data ramdisk 种子:
```sh
./unixnu build payload --data-ramdisk data.img
```
`--skip-setup` 在种子副本中把 Setup 标记为完成。负载报告记录种子哈希和结果镜像哈希。
## 下一步
- [userdata 布局](/zh/concepts/userdata)
- [启动镜像](/zh/guide/boot)
- [设备配置](/zh/reference/configuration)
+58
View File
@@ -0,0 +1,58 @@
---
layout: home
hero:
name: UniXNU
text: 在非 Apple ARM64 硬件上运行 Darwin 与 XNU 用户态
tagline: UEFI 启动管理器、设备树平台、打过补丁的 XNU 内核和完整启动镜像。
image:
src: /logo-light.svg
alt: UniXNU 标志
actions:
- theme: brand
text: 从这里开始
link: /zh/guide/overview
- theme: alt
text: 移植设备
link: /zh/porting/
- theme: alt
text: 设备说明
link: /zh/devices/
---
::: warning 文档状态
文档仍在维护中,部分页面可能不完整或存在不准确之处。涉及具体细节时,请以源码仓库和构建报告为准。
:::
## UniXNU 提供什么
UniXNU 是一套构建系统和运行时栈,用于在固件为 UEFI 或可移植为 UEFI 的 ARM64 设备上启动 Darwin 系操作系统。
| 层次 | 组件 | 作用 |
| --- | --- | --- |
| 启动镜像 | BootShim、UEFI 固件卷、设备树 | 由设备原有的 Android bootloader 通过 `fastboot boot` 加载 |
| 启动管理器 | UniXNU | 加载驱动、应用设备树补丁、发布块设备切片、运行菜单 |
| 加载器 | DarwinBoot | 校验内核 Mach-O、ADT 和 trust cache,退出 boot services,进入 EL1 |
| 内核 | 打过补丁的 XNU | 从 Apple 源码构建,并与原始 kernelcache 预链接 |
| 设备 | XNU 驱动与 kit | 平台专家、CPU、存储、显示、GPU 和板级电源 |
| 用户态 | IPSW 磁盘镜像 | userdata 分区中未修改的 OS 与 SystemOS 镜像 |
## 文档地图
- [总览](/zh/guide/overview):整体栈、仓库和构建模型。
- [构建](/zh/building):克隆、配置、拉取、构建和打包启动镜像。
- [启动镜像](/zh/guide/boot):`fastboot boot`、菜单、串口日志和恢复。
- [移植](/zh/porting/):新增设备仓库和 UEFI 固件移植。
- [驱动](/zh/drivers/overview):UEFI 驱动、内置 XNU 驱动和 kext 包。
- [XNU](/zh/xnu/source-and-patches):源码版本、补丁、4 KiB 页和启动参数。
- [设备](/zh/devices/):支持模型和各设备说明。sheng 页面只是其中一个设备条目。
- [参考](/zh/reference/cli):全部命令、配置项和输出文件。
## 仓库
| 仓库 | 内容 |
| --- | --- |
| [UniXNU](https://github.com/UniXNU/UniXNU) | 构建驱动、UEFI 启动管理器、DarwinBoot、通用驱动、kit 和补丁 |
| [UniXNU-sheng](https://github.com/UniXNU/UniXNU-sheng) | 小米平板 6S Pro 的设备仓库 |
| [uefi-sheng](https://github.com/UniXNU/uefi-sheng) | 小米平板 6S Pro 的 UEFI 固件移植 |
设备仓库采用与本仓库相同的目录布局。同名模块会覆盖基础仓库的模块,设备仓库的 `package/` 在构建树中成为 kit。通用文档适用于所有设备,设备相关的具体数值放在仓库和 [设备](/zh/devices/) 章节中。
+143
View File
@@ -0,0 +1,143 @@
# bring-up 顺序
bring-up 按阶段推进。每个阶段都有可见结果和日志行,一次隔离一个问题。
## 阶段 1:UEFI
目标:固件进入 UEFI shell、启动菜单或 UniXNU。
检查:
- 固件卷大小匹配 `[uefi_fd] size`。
- 启动镜像 header 满足 bootloader 要求。
- `fastboot boot boot.img` 启动固件,不复位。
- framebuffer 有输出,或串口有 UEFI 输出。
- 块设备和负载卷可见。
```sh
fastboot boot out/<board>/boot/boot.img
```
固件停住后设备仍在 bootloader,再次启动同一镜像。
## 阶段 2:UniXNU
目标:菜单出现,配置解析成功。
检查:
- 串口行以 `UNIXNU:` 开头。
- 日志报告 entry、driver、patch、ACPI、slice 和 kit 数量。
- 菜单列出预期项。
- 驱动失败显示数量和第一条失败原因。
最小测试:空 ramdisk。UniXNU 使用内置配置并列出可移动加载器。
```sh
./unixnu bootimg --uefi /path/to/Mu-Silicium --dtb board.dtb --ramdisk none
```
## 阶段 3:负载
目标:UniXNU 读取 `unixnu.toml` 和负载文件。
检查:
- 日志报告配置路径。
- `xnu` 菜单项存在。
- 负载报告哈希与卷中文件一致。
- ADT sidecar 位于内核旁边。
负载错误在 DarwinBoot 启动前可见。
## 阶段 4:DarwinBoot
目标:DarwinBoot 校验内核、ADT 和 trust cache,退出 boot services。
检查:
- 串口行以 `DARWINBOOT:` 开头。
- 打印虚拟基址、物理基址、参数地址、入口地址。
- 打印 framebuffer 基址、宽、高和深度。
- 异常级别为 EL1。
- bootstrap 页表路径到达内核入口。
拒绝时打印 `DARWINBOOT: boot refused: <status>; firmware retained`,固件继续运行并回到菜单。
## 阶段 5:内核入口
目标:XNU 启动并到达平台专家。
检查:
- 内核打印版本和启动参数。
- bootstrap 页表映射内核窗口。
- 平台专家匹配 ADT 根节点。
- `/cpus`、`/arm-io`、`/defaults`、`/options` 和 NVRAM proxy 存在。
缺少平台 provider 时 panic 会给出 provider 名称。加入驱动或修正 ADT。
## 阶段 6:存储与根卷
目标:块驱动发布 userdata 分区,卷出现,XNU 挂载根卷。
检查:
- 存储驱动打印链路速度和命令队列。
- 分区方案发布 `OS` 和 `SystemOS`。
- 根卷 UUID 匹配 `root_match_volume_uuid`。
- 内核挂载根卷并启动 `launchd`。
根卷匹配失败时内核没有根设备。检查 UUID、窗口偏移和分区容量。
## 阶段 7:用户态
目标:`launchd` 运行,首个用户进程到达可见状态。
检查:
- `launchd` 从根卷启动。
- Data 卷挂载。
- 显示服务或控制台有输出。
- 启动进度层记录最后检查点。
使用 `boot-progress=2` 和诊断启动参数查看最后完成的检查点。
## 先在 QEMU 验证
QEMU fixture 在没有设备时测试 UniXNU、DarwinBoot 和内核入口:
```sh
python3 scripts/qemu-entry/make-dt.py --armvirt --armcpu --smp 2 \
--owned-memory-size 0x20000000 --output build/qemu.dtb
python3 package/Scripts/stage-esp.py -o out/esp \
package/UniXNUPkg/Test/Qemu/esp \
--put out/<board>/package/Build/Out/UniXNU.efi=EFI/BOOT/BOOTAA64.EFI
python3 package/Scripts/qemu-run.py out/esp \
--log out/qemu.log --screenshot out/qemu.png \
--expect "UNIXNU: menu: shown" --expect "DARWINBOOT:"
```
QEMU 运行器启动 AArch64 `virt` 机器和 AAVMF,抓取串口日志,并可以向模拟键盘发送按键。
## 失败隔离
| 现象 | 首先检查 |
| --- | --- |
| 固件无输出 | 启动镜像 header、固件基址和大小、bootloader 日志 |
| 固件有输出,无菜单 | GOP 驱动、UniXNU 路径、`unixnu.toml` |
| 菜单存在,无负载项 | 负载卷标、FAT 驱动、菜单项路径 |
| 有负载项,无 DarwinBoot 日志 | 加载器协议、固件卷中的 DarwinBoot |
| DarwinBoot 拒绝 | 内核 Mach-O、ADT、trust cache、占位符 |
| 内核入口无日志 | 启动参数、串口选择、framebuffer 地址 |
| panic 给出 provider | ADT 节点、驱动静态库、内核开关 |
| 无根卷 | userdata 镜像、UUID、分区容量 |
## 下一步
- [故障排查](/zh/reference/troubleshooting)
- [QEMU 测试](/zh/reference/qemu)
- [启动参数](/zh/xnu/boot-arguments)
- [调试与日志](/zh/guide/debugging)
+294
View File
@@ -0,0 +1,294 @@
# 设备仓库
设备仓库描述一块板子,并提供与通用树不同的模块。
## 布局
```
UniXNU-<device>/
board.toml 板级描述
configs/<name>_defconfig 内核配置
feeds.toml 可选外部源码
local.toml 可选本地覆盖,Git 忽略
drivers/ 设备 XNU 驱动
kits/ 设备内核库
patches/ 设备补丁
scripts/ 构建和 bring-up 脚本
package/ UniXNU kit 内容
unixnu.lock unixnu fetch 写入
```
目录布局与 `UniXNU` 相同。设备仓库中的同名模块覆盖基础仓库模块。
## board.toml
### 板级
```toml
[board]
name = "<device>"
soc = "<SoC>"
description = "<device description>"
kit = "<kit>"
# example = true
```
| 键 | 含义 |
| --- | --- |
| `name` | `out/` 下的输出目录名 |
| `soc` | 报告和脚本使用的 SoC 标识 |
| `description` | 可读描述 |
| `kit` | 设备 package 在 `kits/` 下的目录名 |
| `example` | 标记示例仓库,`build` 和 `bootimg` 会拒绝 |
### IPSW
```toml
[ipsw]
url = "https://example.invalid/restore.ipsw"
product = "<product>"
version = "<version>"
build = "<build>"
device_class = "<device class>"
chip_id = 0x0000
board_id = 0
fetch = ["KernelCache", "DeviceTree", "StaticTrustCache", "Cryptex1,SystemTrustCache"]
[ipsw.sha256]
"<member file>" = "<sha256>"
```
`url` 是默认来源,`--ipsw` 或 `UNIXNU_IPSW` 覆盖。镜像必须匹配产品、版本、build、device class、chip ID 和 board ID。
`fetch` 列出 `unixnu ipsw fetch` 无参数时提取的组件。`[ipsw.sha256]` 按成员文件名固定哈希。
### userdata
```toml
[userdata]
first_lba = 0x0
last_lba = 0x0
lba_bytes = 4096
windows = [
{ name = "OS", component = "OS" },
{ name = "SystemOS", component = "Cryptex1,SystemOS" },
]
```
`first_lba` 和 `last_lba` 来自名为 `userdata` 的 GPT 条目,`lba_bytes` 是逻辑块大小。每个窗口指定 IPSW 磁盘镜像组件。
### 内核
```toml
[kernel]
defconfig = "<name>_defconfig"
[kernel.prelink]
tool = "kits/NativePrelink/emit_prelink.py"
kernel = "build/xnu/ios-obj/DEVELOPMENT_ARM64_BCM2837/mach.development.bcm2837.unstripped.noctf"
kernelcache_sha256 = "..."
slide = "-0x1000000"
early_security_init = "com.apple.driver.AppleMobileFileIntegrity=0xfffffff005ab4ae0"
roots = [
"com.apple.driver.AppleARMPlatform",
"com.apple.filesystems.apfs",
]
extra_args = []
```
| 键 | 含义 |
| --- | --- |
| `defconfig` | `configs/` 下的文件名 |
| `tool` | 构建树中的预链接工具 |
| `kernel` | 构建树中的原生内核 |
| `kernelcache_sha256` | 固定原始 kernelcache |
| `slide` | 选中组件的地址 slide |
| `early_security_init` | 早期初始化器的组件标识和地址 |
| `roots` | 保留的原始 kernelcache bundle identifier |
| `extra_args` | 预链接工具的额外参数 |
### 固件
```toml
[firmware]
config = "<name>"
dsc = "Platforms/<Vendor>/<device>Pkg/<device>.dsc"
fd = "Build/<device>Pkg/{target}_CLANGPDB/FV/SILICIUM_UEFI.fd"
preflight = ["scripts/check-<device>.py"]
submodules = []
patches = ["Resources/MuPatches/<patch>.patch"]
packages = [
"{uefi}",
"{tree}/package",
"{tree}/drivers/DarwinBoot",
"{edk2}",
]
[firmware.defines]
DEVICE_MODEL = "<device>"
```
| 键 | 含义 |
| --- | --- |
| `config` | Mu-Silicium 中的 `Resources/Configs/<name>.toml` |
| `dsc` | 相对仓库根目录的 DSC |
| `fd` | 固件卷路径,`{target}` 替换为 `DEBUG` 或 `RELEASE` |
| `preflight` | 构建树中的预检脚本 |
| `submodules` | 板级需要的 Mu-Silicium 子模块 |
| `patches` | 构建期间应用到共享 edk2 树、之后还原的补丁 |
| `packages` | `PACKAGES_PATH` 条目,`{uefi}`、`{edk2}`、`{tree}` 会被替换 |
| `defines` | EDK II 额外 `-D` 定义 |
### 启动镜像
```toml
[bootimg.image]
base = 0x0
kernel_offset = 0x8000
tags_offset = 0x1e00000
pagesize = 4096
header_version = 0
[[bootimg.dtb_node]]
path = "/reserved-memory/<region>"
props = [
{ name = "label", type = "s", value = "<label>" },
{ name = "reg", type = "x", value = ["0x0", "0x80000000", "0x0", "0x100000"] },
]
```
镜像参数默认来自 Mu-Silicium 设备配置,`[bootimg.image]` 覆盖。每个 `[[bootimg.dtb_node]]` 在设备树追加到启动镜像前加入节点或属性。原有 bootloader 有时需要 splash 或 reserved-memory 节点。
### 负载
```toml
[payload]
volume = "<label>"
size_mib = 192
loader = "firmware"
title = "UniXNU"
dram_size = "0x80000000"
panic_screen = true
skip_setup_tool = "scripts/seed-data-skip-setup.py"
trust_cache_sha256 = ["<sha256>"]
timeout = 3
kernel_args = ["-v", "serial=3"]
[payload.adt]
tool = "scripts/make-<device>-adt.py"
chip_id = "0"
owned_memory_size = "0x10000000"
root_match_volume_uuid = "00000000-0000-0000-0000-000000000000"
cpu_regs = "0,0x100"
psci_method = "smc"
packed_data_ramdisk = true
ipad_identity = true
unique_chip_id = "required"
```
| 键 | 含义 |
| --- | --- |
| `volume` | FAT 卷标,最多 11 字符 |
| `size_mib` | 卷大小 |
| `loader` | DarwinBoot 在固件卷中时用 `firmware` |
| `title` | 菜单标题 |
| `dram_size` | 传给 ADT 工具的 DRAM 大小 |
| `panic_screen` | 把 panic 屏幕打进负载内核副本 |
| `skip_setup_tool` | `--skip-setup` 使用的脚本 |
| `trust_cache_sha256` | 接受的 trust cache 哈希 |
| `timeout` | 菜单超时 |
| `kernel_args` | 默认内核命令行 |
`[payload.adt]` 的键传给 ADT 工具。工具契约由设备决定,通用键见[设备树](/zh/porting/device-tree)。
## defconfig
`configs/<name>_defconfig` 使用 Linux defconfig 语法:
```
CONFIG_XNU_ARM_DT_PLATFORM=y
CONFIG_XNU_ARMCPU=y
CONFIG_XNU_NATIVE_UFS=y
CONFIG_XNU_NATIVE_CS_HASH_CACHE=y
# CONFIG_XNU_ARMVIRT_PLATFORM is not set
CONFIG_XNU_CRYPTO_BOOTSTRAP="prelinked"
CONFIG_XNU_OWNER_DATA_VOLUME_UUID="00000000-0000-0000-0000-000000000000"
```
取值可以是 `y`、`n`、数字或带引号字符串。每个 `CONFIG_XNU_<NAME>` 变成内核构建的 `XNU_<NAME>`,字符串中的 `{tree}` 替换为构建树路径。
## feeds.toml
设备仓库可以追加源码:
```toml
[[source]]
name = "DeviceKit"
kind = "kit"
git = "https://github.com/ORG/REPO"
ref = "main"
path = "kits/DeviceKit"
commit = "..."
local = "~/Code/device-kit"
```
kind 为 `driver`、`kit`、`patch`、`package` 或 `refer`。`path` 只拉取子目录,`local` 使用本地目录。
基础 `feeds.toml` 提供 `xnu`、`libplatform`、`libdispatch`、`AvailabilityVersions` 和 `edk2`。
## 本地开发
第一个匹配生效:
1. `--local-module [KIND:]NAME=PATH`
2. `--local-<kind> DIR`
3. `--local DIR`
4. `feeds.toml` 的 `local` 键或 `local.toml` 条目
5. 设备仓库
6. 基础仓库
7. `feeds.toml` 的 git 源码
本地模块以符号链接进入 `out/<board>/tree`。`unixnu status` 会标记它们并报告锁文件未固定。
## package 与 kit
设备 `package/` 在构建树中成为 `kits/<board.kit>`。package 可以包含 `unixnu.toml`、kit 目录、驱动、设备树 overlay 和 kext 包。
kit 目录包含 `Kit.toml`:
```toml
schema = 1
[kit]
name = "<kit>"
version = "1.0.0"
description = "<description>"
[match]
arch = "AARCH64"
fdt_compatible = ["<compatible>"]
firmware_vendor = ["<vendor>"]
requires_protocol = ["<guid>"]
[[driver]]
file = "<driver>.efi"
args = "<load options>"
[[overlay]]
file = "<overlay>.dtbo"
[[kext]]
bundle = "<name>.kext"
entry = "<entry id>"
```
`[match]` 的键全部需要满足。同一个键内任意值匹配。不匹配的 kit 会被跳过并计入菜单摘要。
## 下一步
- [UEFI 固件](/zh/porting/uefi-firmware)
- [设备树](/zh/porting/device-tree)
- [设备配置参考](/zh/reference/configuration)
- [设备](/zh/devices/)
+208
View File
@@ -0,0 +1,208 @@
# 设备树
设备移植涉及两棵树:
| 树 | 格式 | 使用方 |
| --- | --- | --- |
| UEFI 设备树 | 标准扁平设备树(FDT) | UEFI 固件、EFI 和 Linux 菜单项 |
| Apple 设备树(ADT) | Apple 节点编码 | XNU 平台专家和驱动 |
负载步骤在内核旁边生成 `kernel.macho.adt`,DarwinBoot 从菜单项目录读取它。
## Apple 设备树编码
Apple 设备树使用紧凑节点编码:
```
u32 property_count
u32 child_count
每个属性:
char name[32]
u32 length
u8 value[length]
填充到 4 字节
每个子节点:
node
```
每个节点带有自己的 `name` 属性和结尾 NUL。
这棵树使用 Apple 自己的编码。`dtc` 编译 FDT 源码,libfdt 读取 FDT blob,两者只适用于 UEFI 树。
## 必需节点
通用 `ArmDTPlatform` 要求以下顶层节点:
| 节点 | 必需内容 |
| --- | --- |
| `/chosen` | `random-seed`、CHRP NVRAM proxy、DRAM 描述、`osenvironment` |
| `/cpus` | 每个核心一个 `cpuN` 子节点 |
| `/arm-io` | 带 `#address-cells`、`#size-cells`、`ranges` 的总线节点 |
| `/defaults` | 平台默认值 |
| `/options` | 平台选项 |
缺少任一节点时平台专家 `configure()` 失败。`/chosen` 没有可用 NVRAM proxy 时同样失败。
### NVRAM proxy
XNU 从 `/chosen` 发布真实 `IODTNVRAM`。proxy 使用 CHRP bank 格式:
| 属性 | 内容 |
| --- | --- |
| `nvram-bank-size` | bank 大小,32 位 |
| `nvram-bank-count` | bank 数量,32 位 |
| `nvram-current-bank` | 当前 bank,32 位 |
| `nvram-proxy-data` | bank 镜像 |
bank 镜像包含 `nvram` 头、带两 KiB 数据区的 `common` 头和 `free` 头。`common` 数据区偏移 16 处有 Adler-32 校验。
没有持久 NVRAM 控制器的设备使用空 proxy 即可。
### chosen 属性
| 属性 | 类型 | 用途 |
| --- | --- | --- |
| `random-seed` | 64 字节 | 内核熵种子 |
| `unique-chip-id` | 64 位 | 与 `chip-id` 一起生成 `IOPlatformUUID` |
| `chip-id` | 32 位 | 平台身份种子 |
| `osenvironment` | 字符串 | `main`、`recoveryos` 或 `ramdisk` |
| `dram-base` | 64 位 | 物理 DRAM 基址 |
| `dram-size` | 64 位 | 物理 DRAM 大小 |
| `crypto-hash-method` | 字符串 | Image4 哈希方法,例如 `sha2-384` |
| `root-matching` | 字典 | 根卷匹配字典 |
| `ephemeral-storage` | 32 位 | 标记可写 Data 卷为临时 |
| `darwinboot-memory-size` | 64 位 | 连续窗口请求,256 MiB 到 8 GiB,2 MiB 步进 |
| `amfi-only-platform-code` | 32 位 | 保持 AMFI 平台代码策略 |
| `boot-manifest-hash` | 字节 | owner 路径的启动 manifest 哈希 |
`unique-chip-id` 和 `chip-id` 生成 `IOPlatformUUID`。缺少它们时,以零超时调用 `gethostuuid()` 的进程会一直等待。
### memory-map
`/chosen/memory-map` 保存 DarwinBoot 填充的占位符:
| 属性 | 长度 | 填充内容 |
| --- | --- | --- |
| `RAMDisk` | 16 字节 | dense Data 镜像地址和大小 |
| `PackedRAMDisk` | 16 字节 | packed Data 镜像地址和大小 |
| `TrustCache` | 16 字节 | trust cache 地址和大小 |
每个输入都需要占位符。缺少占位符时 DarwinBoot 返回 `EFI_COMPROMISED_DATA`。
### CPU
```c
cpu0 {
name = "cpu0";
state = "running";
reg = <0>;
device_type = "cpu";
compatible = "arm,cortex-a715";
timebase-frequency = <19200000>;
}
```
第一个 CPU 是启动 CPU,`reg` 是 MPIDR affinity 值。多于一个 CPU 时加入 `/psci` 节点,`compatible = "arm,psci-1.0"`,`method` 为 `"smc"` 或 `"hvc"`。
### arm-io
```c
arm-io {
name = "arm-io";
device_type = "arm-io";
#address-cells = <2>;
#size-cells = <2>;
ranges = <0 0x17000000 0x08000000>;
intc {
name = "intc";
compatible = "arm,gic-v3";
reg = <...>;
};
}
```
`ranges` 映射总线窗口。子节点 `reg` 使用窗口内偏移。低于窗口基址的设备无法作为子节点表达,驱动改用板级常量中的物理地址。
### product 与 buttons
`/product` 提供产品名和型号,`/buttons` 提供按钮名称和 `home-button-type`。MobileGestalt 和平台代码读取这些节点。
### 平台默认值
`/defaults` 提供平台选项。例如 `sleep-disabled = <1>` 在未实现 suspend 时阻止 CPU 停驻。
## ADT 工具契约
`[payload.adt] tool` 指定设备仓库中的脚本。负载步骤按[启动负载](/zh/concepts/payload)列出的选项调用它。
工具把树写到 `Payloads/kernel.macho.adt`,并保证:
1. 生成合法 Apple 节点编码。
2. 加入新的 64 字节 `random-seed`。
3. 为输入加入 memory-map 占位符。
4. 提供种子时加入 `unique-chip-id` 和 `chip-id`。
5. 提供 UUID 时加入 `root-matching`。
6. Data 种子存在时加入 `ephemeral-storage`。
7. 提供窗口覆盖值时加入 `darwinboot-memory-size`。
## UEFI 设备树
固件通过 `EFI_DTB_TABLE` 暴露自己的 FDT。UniXNU 可以修改它并传给 EFI 或 Linux 菜单项。
菜单项选择设备树:
```toml
[[entry]]
id = "linux"
type = "linux"
path = "Payloads/Image"
dtb = "DeviceTree/board.dtb"
```
`dtb = "firmware"` 使用固件树,路径使用负载卷上的文件。树复制时预留 64 KiB,先应用全局补丁,再应用菜单项补丁,最后应用 `[entry.chosen]`。
补丁支持 `set`、`delete-prop`、`add-node`、`delete-node` 和 `overlay`:
```toml
[[dt_patch]]
op = "set"
node = "/chosen"
prop = "bootargs"
value = "console=ttyAMA0"
[[dt_patch]]
op = "overlay"
file = "DeviceTree/board.dtbo"
```
DarwinBoot 读取 `.adt` sidecar。`xnu` 菜单项设置 `dtb` 时会在加载器请求中携带设备树,发布版 DarwinBoot 会拒绝带非空设备树的请求。
## 启动镜像设备树
启动镜像在 gzip 流后携带设备树。原有 bootloader 可能需要固件树中没有的节点。
```toml
[[bootimg.dtb_node]]
path = "/reserved-memory/splash_region"
props = [
{ name = "label", type = "s", value = "cont_splash_region" },
{ name = "reg", type = "x", value = ["0x0", "0xb8000000", "0x0", "0x2b00000"] },
]
```
`unixnu bootimg` 用 `fdtput` 应用这些节点,并校验整数属性往返一致。
## 参考生成器
| 生成器 | 用途 |
| --- | --- |
| `scripts/qemu-entry/make-dt.py` | 单 CPU、可选 GICv3、VirtIO 和 NVRAM 的 QEMU fixture |
| 设备仓库的 `make-<device>-adt.py` | 带板级地址和身份的 ADT |
QEMU 生成器是最小完整示例。真实板级的数值记录在[设备](/zh/devices/)页面。
## 下一步
- [启动负载](/zh/concepts/payload)
- [设备树匹配](/zh/drivers/device-tree-matching)
- [bring-up 顺序](/zh/porting/bring-up)
+53
View File
@@ -0,0 +1,53 @@
# 移植总览
一个设备移植包含两个仓库和一份板级配置。
| 仓库 | 内容 |
| --- | --- |
| `UniXNU-<device>` | `board.toml`、内核 defconfig、设备脚本、驱动、kit、补丁和设备 package |
| `uefi-<device>` | Mu-Silicium UEFI 移植:设备配置、DSC/FDF、预检和厂商输入 |
`UniXNU` 提供构建驱动、UEFI 启动管理器、DarwinBoot 和通用驱动。设备仓库会覆盖同名模块。
## 移植清单
1. [移植 UEFI 固件](/zh/porting/uefi-firmware),让设备能进入菜单、使用 framebuffer 和负载卷。
2. [创建设备仓库](/zh/porting/device-repository),包含 `board.toml`、defconfig 和 package。
3. 选择目标 IPSW 和设备身份。
4. [生成 ADT](/zh/porting/device-tree),提供 XNU 读取的节点和属性。
5. 构建负载和启动镜像。
6. [按阶段 bring-up](/zh/porting/bring-up):固件、菜单、负载、内核入口、平台专家、存储、根卷、用户态。
7. 为显示、存储、输入和电源加入设备驱动和 kit。
8. 构建 userdata 镜像并刷写。
## 通用部分
以下部分来自 `UniXNU`,通常无需设备副本:
- 构建驱动和模块解析。
- UniXNU 和配置 schema。
- DarwinBoot 和加载器协议。
- NativePrelink。
- 4 KiB XNU 补丁。
- 通用平台专家、CPU、VirtIO 和块驱动。
## 设备相关部分
- UEFI 固件移植和厂商二进制。
- 固件基址和大小。
- 设备树和 ADT 生成器。
- 内核 defconfig 及其选择的静态库。
- 板级电源、显示、触控和 GPU 驱动。
- IPSW 身份和 trust cache。
- userdata 分区几何。
## 设备页面
[设备](/zh/devices/)章节列出设备仓库。每个页面记录该设备的数值。移植指南使用占位符,具体数值链接到设备页面。
## 下一步
- [设备仓库](/zh/porting/device-repository)
- [UEFI 固件](/zh/porting/uefi-firmware)
- [设备树](/zh/porting/device-tree)
- [bring-up 顺序](/zh/porting/bring-up)
+192
View File
@@ -0,0 +1,192 @@
# UEFI 固件
UEFI 固件移植来自 Mu-Silicium,提供 UniXNU 和 DarwinBoot 使用的服务。
## 必需服务
| 服务 | 使用方 |
| --- | --- |
| `EFI_GRAPHICS_OUTPUT_PROTOCOL` | UniXNU 菜单、DarwinBoot framebuffer 交接、XNU 控制台 |
| `EFI_SIMPLE_TEXT_IN_PROTOCOL` | UniXNU 菜单输入 |
| `EFI_SIMPLE_FILE_SYSTEM_PROTOCOL` | 配置、负载和内核文件 |
| `EFI_BLOCK_IO_PROTOCOL` | userdata 分区和块设备切片 |
| `EFI_SERIAL_IO_PROTOCOL` | 有 UART 的板级日志 |
| `EFI_ACPI_TABLE_PROTOCOL` | 可选 ACPI 表注入 |
| `EFI_FIRMWARE_VOLUME2_PROTOCOL` | UniXNUDxe 启动选项注册 |
缺少某项服务会限制对应阶段。把驱动加入固件卷或 UniXNU 负载后再运行依赖阶段。
## Mu-Silicium 布局
```
Mu-Silicium/
BootShim/
BootShim.S
Resources/
Configs/<device>.toml
Scripts/mkbootimg.py
Platforms/<Vendor>/<device>Pkg/
<device>.dsc
<device>.fdf
Silicon/
Common/
```
UniXNU 驱动读取 `Resources/Configs/<name>.toml` 和 `<device>.dsc`,启动镜像使用 `Resources/Scripts/mkbootimg.py`。
## 设备配置
`Resources/Configs/<device>.toml` 提供地址和镜像参数:
```toml
[boot_shim]
requires_kernel_header = true
[uefi_fd]
base = 0x00000000
size = 0x000000
[boot_image_kernel]
kernel_compression = "gzip"
append_dtb = true
[boot_image]
header_version = 0
base = 0x00000000
kernel_offset = 0x8000
tags_offset = 0x1e00000
pagesize = 4096
```
| 键 | 含义 |
| --- | --- |
| `boot_shim.requires_kernel_header` | bootloader 检查镜像开头的 ARM64 header |
| `uefi_fd.base` | 固件卷加载物理地址 |
| `uefi_fd.size` | 固件卷精确大小 |
| `boot_image_kernel.kernel_compression` | shim 和固件卷的压缩方式 |
| `boot_image_kernel.append_dtb` | 把设备树追加到 kernel 字段 |
| `boot_image.*` | Android 启动镜像 header 参数 |
固件基址和大小编译进 BootShim,并在打包启动镜像时检查。
## DSC 与 FDF
DSC 列出 package、库和组件,FDF 把组件放入固件卷。
UniXNU 使用 `PACKAGES_PATH`:
```
{uefi}:{tree}/package:{tree}/drivers/DarwinBoot:{edk2}:...
```
`{uefi}` 是 Mu-Silicium 仓库,`{tree}` 是组装后的构建树,`{edk2}` 是 Mu_Basecore。板级文件列出全部 package 路径。
UniXNU 和 DarwinBoot 通过 INF 加入:
```
UniXNUPkg/Application/UniXNU/UniXNU.inf
UniXNUPkg/Driver/UniXNUDxe/UniXNUDxe.inf
DarwinBootPkg/Driver/DarwinBoot.inf
```
FDF 决定它们嵌入固件卷还是复制到 ESP。
## 预检
`[firmware] preflight` 列出 EDK II 构建前运行的构建树脚本,用于检查厂商输入、固件分区或板级文件。`--skip-preflight` 跳过。
## 厂商输入
Mu-Silicium 移植需要板级厂商二进制,例如:
- 存储、显示、输入或电源的 UEFI DXE 驱动。
- ACPI 表。
- 固件分区镜像。
- 配置数据。
板级预检脚本校验文件和哈希。
## 子模块与补丁
Mu-Silicium 使用子模块:
```toml
[firmware]
submodules = ["Common/Mu", "Silicon/Silicium", "Silicon/Qualcomm"]
patches = ["Resources/MuPatches/Gic-Spurious.patch"]
```
`patches` 中的补丁在构建期间应用到共享 edk2 树,之后还原。驱动在应用前运行 `git apply --check`。
## 构建
```sh
./unixnu build firmware --uefi /path/to/Mu-Silicium
```
驱动:
1. 运行预检脚本。
2. 初始化列出的子模块。
3. 缺少 `GenFw` 时构建 BaseTools。
4. 应用板级补丁。
5. 用 DSC、`AARCH64`、`CLANGPDB` 和板级 defines 运行 EDK II `build`。
6. 还原补丁。
7. 检查固件卷大小和路径。
输出路径来自 `[firmware] fd`:
```toml
fd = "Build/<device>Pkg/{target}_CLANGPDB/FV/SILICIUM_UEFI.fd"
```
## 独立构建
Mu-Silicium 也可以用自己的脚本构建:
```sh
cd Mu-Silicium
python3 build_uefi.py -d <device>
```
UniXNU 驱动使用板级 defines 和共享 edk2 补丁执行同样的构建。
## 启动镜像集成
`unixnu bootimg` 用固件基址和大小编译 BootShim:
```sh
clang --target=aarch64-none-elf -c \
-DFD_BASE=<base> -DFD_SIZE=<size> \
-DREQUIRES_KERNEL_HEADER=<0 or 1> \
BootShim/BootShim.S -o shim.o
```
它追加固件卷,用 gzip 压缩,追加设备树,再打包 Android 启动镜像。
## 验证
| 检查 | 方法 |
| --- | --- |
| 固件卷大小 | 驱动与 `[uefi_fd] size` 比较 |
| shim header | 检查 `ARM\x64` 及基址和大小字段 |
| 启动镜像部分 | 解包并逐部分比较 |
| 菜单 | 串口出现 `UNIXNU:` 行 |
| 负载 | 菜单列出 `xnu` 项,DarwinBoot 打印交接信息 |
## 添加 UEFI 驱动
驱动可以放在固件卷或负载中。
| 位置 | 构建 | 加载 |
| --- | --- | --- |
| 固件卷 | 把 INF 加入 DSC 和 FDF | 固件 dispatch,或 UniXNUDxe 启动应用 |
| 负载 | 构建独立 `.efi` | `unixnu.toml` 的 `[[driver]]` 或 kit |
INF、入口和注入细节见 [UEFI 驱动](/zh/drivers/uefi-drivers)。
## 下一步
- [设备树](/zh/porting/device-tree)
- [bring-up 顺序](/zh/porting/bring-up)
- [UEFI 驱动](/zh/drivers/uefi-drivers)
+278
View File
@@ -0,0 +1,278 @@
# 启动配置
UniXNU 启动管理器先从启动所在卷读取 `\EFI\UniXNU\unixnu.toml`,再扫描其他卷。kit 目录中的 `Kit.toml` 使用相同的 match 和注入段。
## unixnu.toml
```toml
version = 1
[boot]
timeout = 5
rotate = 0
fallback_timeout = 10
connect_all = true
default = "xnu"
[ui]
title = "UniXNU"
scale = 0
logo = true
console = "auto"
```
### [boot]
| 键 | 默认 | 含义 |
| --- | --- | --- |
| `timeout` | 5 | 秒数,负数等待输入 |
| `rotate` | 0 | 空闲高亮轮换秒数,0 关闭 |
| `fallback_timeout` | 10 | 无输入设备时的超时 |
| `connect_all` | true | 对所有 handle 调用 `ConnectController` |
| `default` | 第一项 | 菜单项 id 或索引 |
### [ui]
| 键 | 默认 | 含义 |
| --- | --- | --- |
| `title` | `UniXNU` | 菜单标题 |
| `scale` | 0 | 字形缩放,0 自动 |
| `logo` | true | 绘制标志 |
| `console` | `auto` | `auto`、`graphics`、`text` 或 `none` |
### [[driver]]
```toml
[[driver]]
path = "Drivers/MyDriver.efi"
args = "board=1"
connect = true
enabled = true
```
| 键 | 默认 | 含义 |
| --- | --- | --- |
| `path` | 必需 | 文件路径或 `fv:<GUID>` |
| `args` | | load options |
| `connect` | true | 驱动启动后连接控制器 |
| `enabled` | true | `false` 跳过 |
kit 驱动使用 `file` 代替 `path`。
### [[dt_patch]]
```toml
[[dt_patch]]
op = "set"
node = "/chosen"
prop = "bootargs"
value = "console=ttyAMA0"
type = "string"
```
| 键 | 含义 |
| --- | --- |
| `op` | `set`、`delete-prop`、`add-node`、`delete-node` 或 `overlay` |
| `node` | 绝对节点路径 |
| `prop` | `set` 和 `delete-prop` 的属性名 |
| `value` | `set` 的值 |
| `type` | `string`、`stringlist`、`u32`、`u64`、`bytes` 或 `empty` |
| `file` | `overlay` 的文件 |
补丁应用于 EFI 和 Linux 菜单项。Darwin 路径使用 `.adt` sidecar。
### [[block_slice]]
```toml
[[block_slice]]
partition = "<GPT name>"
name = "<slice name>"
offset = 0
length = 0
writable = false
enabled = true
```
| 键 | 含义 |
| --- | --- |
| `partition` | GPT 分区名 |
| `name` | 设备路径中的标签 |
| `offset` | 相对分区起始的字节偏移 |
| `length` | 字节长度,0 表示到末尾 |
| `writable` | 发布可写块设备 |
| `enabled` | `false` 跳过 |
切片以整盘形式出现,子分区方案或文件系统驱动可以绑定。
### ACPI
```toml
[acpi]
add = ["Tables/DSDT.aml", "Tables/SSDT.aml"]
```
每个表通过 `EFI_ACPI_TABLE_PROTOCOL` 安装,header 长度必须匹配文件大小。
### kit
```toml
[[kit]]
path = "Kits/MyKit.kit"
enabled = true
[kits]
autoload = true
directory = "Kits"
```
显式 kit 先加载。`autoload` 为 true 时,`directory` 下每个 `*.kit` 按名称顺序加载。
### [[entry]]
```toml
[[entry]]
id = "xnu"
title = "UniXNU"
type = "xnu"
path = "Payloads/kernel.macho"
args = "-v serial=3"
initrd = "Payloads/scratch-data.img"
enabled = true
```
| 键 | 含义 |
| --- | --- |
| `id` | 菜单项标识 |
| `title` | 菜单标题 |
| `type` | `efi`、`linux`、`action` 或 `xnu` |
| `path` | EFI 程序、Linux 镜像或内核路径 |
| `args` | load options 或内核命令行 |
| `dtb` | `firmware`、文件路径或省略 |
| `initrd` | initrd 路径 |
| `action` | `action` 类型的 `reset`、`shutdown` 或 `exit` |
| `enabled` | `false` 跳过 |
内置配置包含 `exit`、`reset` 和 `shutdown`。找不到文件时,可移动加载器会加在它们之前。
### [entry.chosen]
```toml
[entry.chosen]
bootargs = "console=ttyAMA0"
```
每个键成为 `/chosen` 属性,在菜单项补丁之后应用。
### [[entry.kext]]
```toml
[[entry.kext]]
bundle = "Kexts/MyDriver.kext"
plist = "Contents/Info.plist"
executable = "Contents/MacOS/MyDriver"
enabled = true
```
菜单项 kext 只作用于声明它的菜单项。
### [[entry.variable]]
```toml
[[entry.variable]]
guid = "00000000-0000-0000-0000-000000000000"
name = "MyVariable"
value = "hello"
type = "ascii"
volatile = true
```
| 键 | 含义 |
| --- | --- |
| `guid` | 变量 GUID |
| `name` | 变量名 |
| `value` | 值 |
| `type` | `ascii`、`ucs2`、`u8`、`u16`、`u32`、`u64` 或 `bytes` |
| `volatile` | `false` 加入 `EFI_VARIABLE_NON_VOLATILE` |
变量在菜单项启动前设置。
### [[entry.dt_patch]]
菜单项接受自己的 `[[dt_patch]]` 数组,在全局补丁之后应用。
## Kit.toml
```toml
schema = 1
[kit]
name = "MyKit"
version = "1.0.0"
description = "Board support"
[match]
arch = "AARCH64"
fdt_compatible = ["vendor,board"]
firmware_vendor = ["Vendor"]
requires_protocol = ["00000000-0000-0000-0000-000000000000"]
[[driver]]
file = "MyDriver.efi"
args = "from-kit"
[[overlay]]
file = "board.dtbo"
[[dt_patch]]
op = "set"
node = "/chosen"
prop = "from-kit"
value = "1"
type = "u32"
[[acpi]]
file = "DSDT.aml"
[[kext]]
bundle = "MyDriver.kext"
entry = "xnu"
```
| 段 | 含义 |
| --- | --- |
| `[kit]` | 名称、版本、描述 |
| `[match]` | 所有存在的键必须匹配 |
| `[[driver]]` | 相对 kit 的驱动文件 |
| `[[overlay]]` | 相对 kit 的设备树 overlay |
| `[[dt_patch]]` | 设备树补丁 |
| `[[acpi]]` | 相对 kit 的 ACPI 表 |
| `[[kext]]` | 限定菜单项 id 的 kext 包 |
match 键:
| 键 | 含义 |
| --- | --- |
| `arch` | `AARCH64` |
| `fdt_compatible` | 固件根 `compatible` 列表中的字符串 |
| `firmware_vendor` | `gST->FirmwareVendor` 的子串 |
| `requires_protocol` | 必须已安装的 GUID |
同一个键内任意值匹配,跨键必须全部匹配。
## 菜单行为
| 条件 | 结果 |
| --- | --- |
| 有输入设备 | 倒计时到超时 |
| 无输入设备 | 使用 `fallback_timeout` |
| 负超时且有输入 | 等待按键 |
| 负超时且无输入 | 使用 `fallback_timeout` |
| 启动失败 | 返回菜单,不再倒计时 |
菜单显示配置来源、菜单项、注入摘要和第一条错误。
## 下一步
- [启动负载](/zh/concepts/payload)
- [kext 包](/zh/drivers/kexts)
- [设备配置](/zh/reference/configuration)
+145
View File
@@ -0,0 +1,145 @@
# 命令行
```
./unixnu <command> [options]
```
选项写在命令之后。驱动从 `-D`、`UNIXNU_DEVICE` 或 `out/state.json` 读取设备仓库。
## 命令
### defconfig
```
unixnu defconfig [NAME]
```
列出设备仓库的 `configs/*_defconfig`。指定名称时复制到 `out/<board>/.config` 并解析。
### fetch
```
unixnu fetch [NAME...] [--update]
```
拉取 git 源码。指定名称时只拉取这些源码。`--update` 忽略 `unixnu.lock` 并移到最新 ref。commit 写入设备仓库的 `unixnu.lock`。
### tree
```
unixnu tree
```
解析模块、拉取步骤需要的参考源码,并组装 `out/<board>/tree`。
### ipsw
```
unixnu ipsw info
unixnu ipsw fetch [COMPONENT...]
```
`info` 打印产品、build、device class 和 manifest 中每个组件的大小与路径。`fetch` 提取指定组件,未指定时使用 `[ipsw] fetch` 列表。
### build
```
unixnu build [kernel] [package] [firmware] [payload] [prelink] [userdata]
```
按 kernel、prelink、package、payload、firmware 顺序执行指定步骤。未指定时执行 kernel、package、firmware 和 payload。`userdata` 单独执行,`all` 覆盖其他四个步骤。
`build prelink` 只重做预链接。
`build userdata --dry-run` 检查 IPSW 和分区大小。
### bootimg
```
unixnu bootimg [--fd FILE] [--dtb FILE] [--ramdisk FILE|none] [-o FILE]
```
打包启动镜像。固件卷默认取 `--uefi` 下的输出,ramdisk 默认取负载输出。
### status
```
unixnu status
```
打印设备、输出根、配置路径和每个模块来源。标记本地模块并报告未固定的模块。
### clean
```
unixnu clean
```
删除板级输出下的 `tree`、`logs`、`kernel`、`package`、`boot` 和 `edk2-conf`,保留已拉取源码。
## 选项
| 选项 | 含义 |
| --- | --- |
| `-D DIR`、`--device DIR` | 设备仓库 |
| `--out DIR` | 输出根 |
| `-j N`、`--jobs N` | 并行任务 |
| `--local DIR` | 与本仓库布局相同的本地树 |
| `--local-drivers DIR` | 本地 drivers 目录 |
| `--local-kits DIR` | 本地 kits 目录 |
| `--local-patches DIR` | 本地 patches 目录 |
| `--local-scripts DIR` | 本地 scripts 目录 |
| `--local-package DIR` | 本地 package 目录 |
| `--local-module [KIND:]NAME=PATH` | 单个本地模块,可重复 |
| `--uefi DIR` | Mu-Silicium 仓库 |
| `--edk2 DIR` | Mu_Basecore 仓库 |
| `--kernelcache FILE` | 原始解压 kernelcache |
| `--skip-preflight` | 跳过板级预检 |
| `--kernel FILE` | 负载使用的预链接内核 |
| `--kernel-arg NAME[=VALUE]` | 追加内核参数,可重复 |
| `--trust-cache FILE` | trust cache 包 |
| `--skip-setup` | 在 Data 种子副本中标记 Setup 完成 |
| `--data-ramdisk FILE` | Data ramdisk 种子 |
| `--unique-chip-id HEX` | ADT 平台种子 |
| `--ipsw PATH_OR_URL` | 目标 IPSW |
| `--dry-run` | userdata 试运行 |
| `--target DEBUG\|RELEASE` | EDK II 目标 |
| `--fd FILE` | bootimg 的固件卷 |
| `--dtb FILE` | bootimg 的编译设备树 |
| `--ramdisk FILE\|none` | bootimg 的 ramdisk |
| `-o FILE`、`--output FILE` | 启动镜像输出 |
## 环境变量
| 变量 | 等价选项 |
| --- | --- |
| `UNIXNU_DEVICE` | `-D` |
| `UNIXNU_OUT` | `--out` |
| `UNIXNU_UEFI` | `--uefi` |
| `UNIXNU_EDK2` | `--edk2` |
| `UNIXNU_KERNELCACHE` | `--kernelcache` |
| `UNIXNU_DTB` | `--dtb` |
| `UNIXNU_IPSW` | `--ipsw` |
| `UNIXNU_UNIQUE_CHIP_ID` | `--unique-chip-id` |
| `JOBS` | `-j` |
## 退出行为
| 条件 | 结果 |
| --- | --- |
| 缺少设备仓库 | 提示 `-D` |
| 内核构建缺少配置 | 提示先运行 `defconfig` |
| 固件缺少 Mu-Silicium 仓库 | 拉取前报错 |
| IPSW 身份不匹配 | 报错并给出期望和实际镜像 |
| sha256 不匹配 | 报错并给出成员名 |
| 固件卷大小不匹配 | 报错并给出大小 |
| 负载超过 `size_mib` | 报错并给出需要提高的大小 |
| 内核参数非法 | 报错并给出参数 |
| 缺少平台种子 | 提示 `--unique-chip-id` |
| 示例设备仓库 | `build` 或 `bootimg` 前报错 |
## 下一步
- [设备配置](/zh/reference/configuration)
- [输出布局](/zh/reference/output-layout)
- [故障排查](/zh/reference/troubleshooting)
+205
View File
@@ -0,0 +1,205 @@
# 设备配置
设备仓库有四个配置文件:`board.toml`、`configs/<name>_defconfig`、`feeds.toml` 和 `local.toml`。
## board.toml
### [board]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `name` | 字符串 | 输出目录名 |
| `soc` | 字符串 | SoC 标识 |
| `description` | 字符串 | 可读描述 |
| `kit` | 字符串 | 设备 package 在 `kits/` 下的目录 |
| `example` | 布尔 | 标记示例,build 和 bootimg 会拒绝 |
### [ipsw]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `url` | 字符串 | 默认远程 IPSW |
| `product` | 字符串 | 产品类型,例如 `iPad7,1` |
| `version` | 字符串 | 产品版本 |
| `build` | 字符串 | 产品 build |
| `device_class` | 字符串 | manifest 中的 device class |
| `chip_id` | 整数 | `ApChipID` |
| `board_id` | 整数 | `ApBoardID` |
| `fetch` | 数组 | `ipsw fetch` 提取的组件 |
### [ipsw.sha256]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| 成员文件 | 字符串 | 解压成员的期望 SHA-256 |
### [userdata]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `first_lba` | 整数 | userdata GPT 条目起始 LBA |
| `last_lba` | 整数 | userdata GPT 条目结束 LBA |
| `lba_bytes` | 整数 | 逻辑块大小 |
| `windows` | 数组 | `{ name, component }` 条目 |
### [kernel]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `defconfig` | 字符串 | `configs/` 下的文件名 |
### [kernel.prelink]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `tool` | 字符串 | 构建树中的预链接工具 |
| `kernel` | 字符串 | 构建树中的原生内核 |
| `slide` | 字符串 | 地址 slide |
| `roots` | 数组 | 保留的 kernelcache bundle identifier |
| `early_security_init` | 字符串 | `identifier=address` |
| `kernelcache` | 字符串 | 可选本地 kernelcache 路径 |
| `kernelcache_sha256` | 字符串 | 期望 kernelcache 哈希 |
| `extra_args` | 数组 | 预链接额外参数 |
### [firmware]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `config` | 字符串 | Mu-Silicium `Resources/Configs/<name>.toml` |
| `dsc` | 字符串 | 仓库中的 DSC 路径 |
| `fd` | 字符串 | 固件卷路径,`{target}` 替换 |
| `packages` | 数组 | `PACKAGES_PATH` 条目 |
| `preflight` | 数组 | 构建树预检脚本 |
| `patches` | 数组 | 构建期间应用到 edk2 的补丁 |
| `submodules` | 数组 | 需要初始化的 Mu-Silicium 子模块 |
### [firmware.defines]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| 任意 | 字符串 | EDK II 构建的 `-D NAME=VALUE` |
### [bootimg.image]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `base` | 整数 | 镜像基址 |
| `kernel_offset` | 整数 | 内核偏移 |
| `tags_offset` | 整数 | tags 偏移 |
| `pagesize` | 整数 | 镜像页大小 |
| `header_version` | 整数 | Android 启动镜像 header 版本 |
### [[bootimg.dtb_node]]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `path` | 字符串 | 设备树节点路径 |
| `props` | 数组 | `{ name, type, value }` |
类型为 `s` 字符串、`x` 十六进制 cell、`i` 十进制 cell、`u` 无符号 cell、`empty`。
### [payload]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `volume` | 字符串 | FAT 卷标,最多 11 字符 |
| `size_mib` | 整数 | 卷大小 |
| `loader` | 字符串 | `firmware` 或驱动加载模式 |
| `title` | 字符串 | 菜单标题 |
| `dram_size` | 字符串 | ADT 使用的 DRAM 大小 |
| `panic_screen` | 布尔 | 把 panic 屏幕打进负载内核 |
| `trust_cache_sha256` | 数组 | 接受的 trust cache 哈希 |
| `kernel_args` | 数组 | 默认内核参数 |
| `timeout` | 整数 | 菜单超时 |
| `skip_setup_tool` | 字符串 | `--skip-setup` 使用的脚本 |
### [payload.adt]
| 键 | 类型 | 含义 |
| --- | --- | --- |
| `tool` | 字符串 | ADT 生成器路径 |
| `chip_id` | 字符串 | ADT 芯片 ID |
| `owned_memory_size` | 字符串 | 连续窗口请求 |
| `root_match_volume_uuid` | 字符串 | 根卷 UUID |
| `cpu_regs` | 字符串 | CPU MPIDR 值,逗号分隔 |
| `psci_method` | 字符串 | `smc` 或 `hvc` |
| `packed_data_ramdisk` | 布尔 | 使用 packed Data 格式 |
| `ipad_identity` | 布尔 | 写入 Apple 设备身份属性 |
| `unique_chip_id` | 字符串 | 种子必需时写 `required` |
## defconfig
```
CONFIG_NAME=y
CONFIG_NAME=n
CONFIG_NAME="text"
CONFIG_NAME=123
# CONFIG_NAME is not set
```
每个 `CONFIG_XNU_<NAME>` 变成 `XNU_<NAME>`,字符串中的 `{tree}` 替换为构建树路径。
## feeds.toml
```toml
[[source]]
name = "Name"
kind = "driver" # driver、kit、patch、package、refer
git = "https://github.com/ORG/REPO"
ref = "main"
path = "drivers/Name"
commit = "..."
local = "/path/to/local"
submodules = []
```
| 键 | 含义 |
| --- | --- |
| `name` | 模块名 |
| `kind` | 模块类型 |
| `git` | 源码仓库 |
| `ref` | 拉取的分支、tag 或 commit |
| `path` | 可选子目录,sparse 拉取 |
| `commit` | 可选固定 commit |
| `local` | 使用本地目录 |
| `submodules` | 初始化的上游子模块 |
## local.toml
设备仓库中被 Git 忽略的文件:
```toml
[local]
drivers = "/path/to/drivers"
kits = "/path/to/kits"
patches = "/path/to/patches"
scripts = "/path/to/scripts"
package = "/path/to/package"
```
## unixnu.lock
```toml
[source."Name"]
commit = "0123456789abcdef"
```
由 `unixnu fetch` 写入。构建使用锁定的 commit,锁只记录 git 源码。
## 模块解析
1. `--local-module [KIND:]NAME=PATH`
2. `--local-<kind> DIR`
3. `--local DIR`
4. `feeds.toml` 的 `local` 键或 `local.toml`
5. 设备仓库
6. 基础仓库
7. `feeds.toml` 的 git 源码
第一个匹配生效。
## 下一步
- [启动配置](/zh/reference/boot-configuration)
- [设备仓库](/zh/porting/device-repository)
- [命令行](/zh/reference/cli)
+43
View File
@@ -0,0 +1,43 @@
# 术语表
| 术语 | 含义 |
| --- | --- |
| ADT | Apple 设备树,XNU 从 `.adt` sidecar 读取的紧凑节点编码 |
| AMFI | Apple Mobile File Integrity,原始代码签名和 entitlement 策略 |
| ABL | Android bootloader,加载启动镜像的原有 bootloader |
| BootShim | 启动 UEFI 固件卷的小型 AArch64 shim |
| UniXNU | UEFI 启动管理器 |
| DarwinBoot | UniXNU 的 XNU 加载器 |
| defconfig | 选择内核、package 和驱动选项的 `CONFIG_*` 文件 |
| 设备仓库 | 含 `board.toml` 和设备模块的仓库 |
| ADT sidecar | `kernel.macho.adt`,内核旁边的设备树 |
| feed | `feeds.toml` 中声明的 git 源码 |
| 固件卷 | EDK II `SILICIUM_UEFI.fd` 镜像 |
| IOMedia | IOKit 中表示磁盘或卷的对象 |
| IPSW | Apple 恢复镜像,提供 kernelcache、设备树、磁盘镜像和 trust cache |
| kernelcache | 原始预链接 Apple 内核及其 kext |
| kext | 内核扩展包 |
| kit | 含驱动、补丁、overlay、ACPI 表和 kext 的 UniXNU 包 |
| 模块 | 按名称选择的驱动、kit、补丁、package 或脚本 |
| personality | 驱动的 IOKit 匹配字典 |
| PGZ | zone 分配器的 secondary page |
| 预链接 | 把原生内核与原始 kernelcache 合并的步骤 |
| SMMU | ARM System Memory Management Unit |
| trust cache | Apple 的可信代码哈希列表 |
| userdata | 含 OS 和 SystemOS 窗口的分区镜像 |
| SHNGROOT | userdata 镜像开头的 manifest |
| SystemOS | IPSW 中的 cryptex 系统卷 |
| DSC | EDK II 平台描述文件 |
| FDF | EDK II 固件描述文件 |
| INF | EDK II 模块描述文件 |
| DXE | UEFI driver execution environment |
| BDS | UEFI boot device selection 阶段 |
| GOP | UEFI graphics output protocol |
| CHRP | Apple 设备树使用的 NVRAM bank 格式 |
| PGZ 探针 | zone 分配器对齐探针 |
## 下一步
- [总览](/zh/guide/overview)
- [命令行](/zh/reference/cli)
- [设备](/zh/devices/)
+147
View File
@@ -0,0 +1,147 @@
# 输出布局
全部构建输出位于 `out/`。驱动只在设备仓库写入 `unixnu.lock`。
## 目录
```
out/
state.json
feeds/
ipsw/<build>/
<board>/
.config
tree/
logs/
kernel/
kernel.macho
package/
Build/Out/
payload/
payload.img
payload.json
boot/
boot.img
boot.json
userdata/
userdata-root.img
userdata-root.json
```
## state.json
```json
{
"device": "/path/to/device/repository"
}
```
没有 `-D` 和 `UNIXNU_DEVICE` 时,驱动从这里读取设备仓库。
## feeds
每个 git 仓库和 ref 一个克隆:
```
out/feeds/<repo>-<hash>/
```
哈希覆盖 URL 和 ref。源码声明 `path` 时使用浅克隆、blobless 和 sparse checkout。
## ipsw
```
out/ipsw/<build>/
<member file>
<member file>.json
```
sidecar 记录组件、成员路径、大小、SHA-256 和来源。缓存成员的大小和哈希匹配固定值时复用。
## tree
```
tree/
drivers/
kits/
patches/
refer/
scripts/
package/
build/
.cache/
```
模块目录是符号链接,`scripts/` 是副本。设备 package 出现在 `kits/<board.kit>`。
## logs
| 文件 | 步骤 |
| --- | --- |
| `kernel.log` | XNU 构建 |
| `kernel-bootstrap.log` | 关闭进度层的首轮构建 |
| `prelink.log` | 预链接 |
| `package.log` | EDK II package 构建 |
| `firmware-preflight.log` | 板级预检 |
| `firmware.log` | EDK II 固件构建 |
| `basetools.log` | EDK II BaseTools 构建 |
| `payload.log` | 负载卷 |
| `payload-adt.log` | ADT 生成 |
| `payload-patch.log` | panic 屏幕补丁 |
| `payload-skip-setup.log` | Setup 跳过 |
| `userdata.log` | userdata 镜像 |
## boot.json
| 字段 | 含义 |
| --- | --- |
| `kernel_sha256` | kernel 字段哈希 |
| `ramdisk_bytes` | ramdisk 大小 |
| `header_version` | Android 启动镜像 header 版本 |
| `kernel_address` | 内核加载地址 |
| `tags_address` | tags 加载地址 |
| `image_sha256` | 启动镜像哈希 |
| `image_bytes` | 启动镜像大小 |
| `fd_sha256` | 固件卷哈希 |
| `dtb_sha256` | 设备树哈希 |
| `shim_bytes` | BootShim 大小 |
| `hardware_tested` | 验证状态 |
## payload.json
| 字段 | 含义 |
| --- | --- |
| `kernel_sha256` | 输入内核 |
| `payload_kernel_sha256` | 负载中的内核 |
| `adt_sha256` | ADT sidecar |
| `kernel_args` | 命令行 |
| `trust_cache_sha256` | trust cache 包 |
| `data_ramdisk_sha256` | Data 种子 |
| `skip_setup` | 是否跳过 Setup |
| `panic_screen` | 是否打入 panic 屏幕 |
| `payload_sha256` | 负载镜像哈希 |
| `payload_bytes` | 负载镜像大小 |
| `hardware_tested` | 验证状态 |
## userdata-root.json
| 字段 | 含义 |
| --- | --- |
| `image_bytes` | 镜像大小 |
| `image_sha256` | 镜像哈希 |
| `manifest_sha256` | manifest 哈希 |
| `windows` | 每个窗口的名称、偏移、大小和哈希 |
| `userdata_bytes` | 分区容量 |
| `flash_command` | fastboot 命令 |
| `source` | 本地或远程 IPSW |
| `hardware_tested` | 验证状态 |
## 清理
`unixnu clean` 删除板级输出下的 `tree`、`logs`、`kernel`、`package`、`boot` 和 `edk2-conf`,保留已拉取源码和解压的 IPSW 成员。
## 下一步
- [构建](/zh/building)
- [命令行](/zh/reference/cli)
- [故障排查](/zh/reference/troubleshooting)
+122
View File
@@ -0,0 +1,122 @@
# QEMU 测试
QEMU 在没有设备时测试 UniXNU、注入路径、DarwinBoot 和内核入口。
## 前提
| 工具 | 用途 |
| --- | --- |
| `qemu-system-aarch64` | AArch64 虚拟机 |
| AAVMF 或 EDK2 AArch64 固件 | UEFI 环境 |
| `dtc` | 设备树编译 |
| `mtools` 或 `pyfatfs` | 块切片测试的 FAT 镜像 |
运行器在常见 Homebrew 和发行版路径查找固件,也可以用 `--firmware` 指定。
## 准备 ESP
```sh
python3 package/Scripts/stage-esp.py -o out/esp \
package/UniXNUPkg/Test/Qemu/esp \
--put out/<board>/package/Build/Out/UniXNU.efi=EFI/BOOT/BOOTAA64.EFI \
--put out/<board>/package/Build/Out/HelloPayload.efi=EFI/UniXNU/Payloads/HelloPayload.efi \
--put out/<board>/package/Build/Out/SampleDriver.efi=EFI/UniXNU/Kits/SampleDriver.kit/SampleDriver.efi
```
`stage-esp.py` 复制树,把 `.dts` 编译为 `.dtb`,`.dtso` 编译为 `.dtbo`,并把每个 `--put` 文件放到目标位置。
## 运行
```sh
python3 package/Scripts/qemu-run.py out/esp \
--log out/qemu.log --screenshot out/menu.png \
--expect 'UNIXNU: menu: shown' \
--expect 'UNIXNU-HELLO: RESULT PASS'
```
| 选项 | 含义 |
| --- | --- |
| `--firmware FILE` | AAVMF code 镜像 |
| `--log FILE` | 串口日志 |
| `--screenshot FILE` | 菜单出现时的 PNG |
| `--keys LIST` | 菜单后发送的 QEMU 键码 |
| `--expect REGEX` | 必须匹配串口日志 |
| `--reject REGEX` | 必须不匹配 |
| `--no-gop` | 无显示设备,只用文本控制台 |
| `--disk FILE` | 额外只读 raw 磁盘 |
| `--timeout SECONDS` | 运行超时 |
| `--cpus N` | 虚拟 CPU 数量 |
| `--finish-on-expect` | 所有期望匹配后两秒停止 |
| `--menu-marker TEXT` | 触发截图和按键的日志标记 |
运行器通过 QEMU `vvfat` 只读暴露 ESP,抓取串口日志,并通过 QMP 截图和发送按键。
## 冒烟测试
```sh
JOBS=8 bash package/Scripts/smoke.sh
```
脚本构建 package 并运行五个场景:
| 场景 | 检查 |
| --- | --- |
| 默认项 | 设备树补丁、overlay、kit 驱动、initrd、菜单项变量 |
| 键盘 | 下和 Enter 选择第二项 |
| 降级 | 无 GOP、无配置、内置菜单项、文本控制台 |
| 块切片 | FAT 分区头部的配置,尾部发布为块设备 |
| kext 暂存 | revision 2 交接、revision 1 拒绝、非法包、重复 |
日志和截图位于 `package/Build/Smoke/`。
## XNU 入口 fixture
QEMU 入口生成器为内核构建 Apple 格式设备树:
```sh
python3 scripts/qemu-entry/make-dt.py \
--armvirt --armcpu --smp 2 \
--owned-memory-size 0x20000000 \
--output build/qemu-test.adt
```
| 选项 | 含义 |
| --- | --- |
| `--armvirt` | 描述 QEMU virt 设备 |
| `--armcpu` | 加入 GICv3 distributor 和 redistributor |
| `--smp N` | CPU 数量和 PSCI |
| `--owned-memory-size` | DarwinBoot 连续窗口 |
| `--data-ramdisk` | scratch Data 占位符 |
| `--trust-cache` | trust cache 占位符 |
| `--nvram-proxy` | 易失 CHRP NVRAM proxy |
| `--root-match-volume-uuid` | 根卷匹配 |
| `--osenvironment` | `main`、`recoveryos` 或 `ramdisk` |
| `--qemu-vmm-present` | 在 `/defaults` 中报告 VMM |
| `--crypto-hash-method` | Image4 哈希方法 |
生成器描述 QEMU,只使用 QEMU 数值。
## HVF
Apple Silicon macOS 上 QEMU 可以使用 HVF:
```sh
python3 scripts/qemu-entry/make-dt.py --armvirt --armcpu --qemu-vmm-present \
--qemu-accel hvf --output build/qemu-hvf.adt
```
生成器读取主机计数器频率作为 CPU timebase。bound fixture 用于在内核停点观察实际 `TCR_EL1`、`SCTLR_EL1` 和 `TTBR`。
## 停点
```sh
python3 scripts/qemu-entry/gdb-stop.py --help
```
辅助脚本在符号处停住 QEMU,记录寄存器、内存和最后检查点,输出 JSON 报告。
## 下一步
- [bring-up 顺序](/zh/porting/bring-up)
- [4 KiB 页表](/zh/xnu/four-k-page-tables)
- [故障排查](/zh/reference/troubleshooting)
+84
View File
@@ -0,0 +1,84 @@
# 故障排查
## 构建
| 现象 | 检查 | 处理 |
| --- | --- | --- |
| `no device repository` | `-D` 或 `UNIXNU_DEVICE` | 传入设备仓库 |
| `has no board.toml` | 设备仓库布局 | 创建 `board.toml` |
| `is an EXAMPLE device repository` | `[board] example` | 复制示例并设置真实名称 |
| `no configuration` | `out/<board>/.config` | 运行 `unixnu defconfig NAME` |
| `no Mu-Silicium checkout` | `--uefi` 路径 | 传入含 `Resources/Scripts/mkbootimg.py` 的仓库 |
| `is not a Mu-Silicium checkout` | 路径 | 指向仓库根目录 |
| `no edk2 tree` | `--edk2` 或已拉取 edk2 | 运行 `unixnu fetch` 或传入 Mu_Basecore |
| 内核步骤缺少静态库 | `build/<component>/lib<Name>.a` | 运行设备静态库脚本 |
| `kernelcache not found` | `--kernelcache` 或 IPSW | 拉取 IPSW 或传入文件 |
| `does not match the kernelcache` | `[kernel.prelink] kernelcache_sha256` | 使用固定镜像 |
| 固件卷大小不匹配 | `[uefi_fd] size` | 重新构建固件或修正配置 |
| 负载超过 `size_mib` | `[payload] size_mib` | 提高大小或移除文件 |
| `unsupported kernel argument` | 参数字符 | 使用字母、数字、`_`、`.`、`-` |
| `command line is ... bytes` | 参数数量 | 缩短到 1000 字节 |
## IPSW
| 现象 | 检查 | 处理 |
| --- | --- | --- |
| `this IPSW is ...` | 产品、版本、build | 使用板级描述的镜像 |
| `no Erase build identity` | device class、chip ID、board ID | 修正身份值 |
| `does not support range requests` | 远程 URL | 使用支持 range 的服务器或本地文件 |
| `differs from the pinned sha256` | `[ipsw.sha256]` | 使用固定成员或更新固定值 |
| `expected exactly one arm64 slice` | kernelcache | 使用 ARM64 kernelcache |
## 启动
| 现象 | 检查 | 处理 |
| --- | --- | --- |
| `fastboot` 看不到设备 | 驱动、线缆、bootloader 模式 | 安装 USB 驱动并重启到 bootloader |
| 镜像启动后复位 | 启动镜像 header、基址和大小 | 检查 Mu-Silicium 配置和 BootShim 参数 |
| 固件停住 | Silicium 随机停顿 | 再次启动同一镜像 |
| 无显示 | GOP 驱动 | 使用 `console = "text"` 或串口 |
| 无菜单 | 配置路径 | 把 `unixnu.toml` 放到 `\EFI\UniXNU\unixnu.toml` |
| 无启动项 | 负载卷 | 检查 FAT 卷标、菜单项路径和卷驱动 |
| 驱动失败 | 菜单中的第一条失败原因 | 修正驱动路径或依赖 |
| `no loader protocol installed` | 固件卷中的 DarwinBoot | 把 DarwinBoot 加入 FDS 或负载 |
| `loader revision 1 cannot accept kexts` | 加载器 revision | 使用 revision 2 或移除 kext |
| `boot refused: ...; firmware retained` | DarwinBoot 日志 | 检查内核、ADT、trust cache 和占位符 |
| 内核入口 panic | 页表、内核窗口、启动参数 | 用 QEMU fixture 和启动进度日志 |
| panic 给出 provider | 设备树节点或驱动静态库 | 加入节点或启用驱动 |
| 无根设备 | userdata 镜像、UUID、容量 | 刷写镜像,检查 UUID 和窗口偏移 |
| 用户态显示断言 | 显示 provider 和启动参数 | 检查显示驱动和 focus 参数 |
## 日志
| 来源 | 位置 |
| --- | --- |
| 构建 | `out/<board>/logs/` |
| 启动管理器 | 串口 `UNIXNU:` 行 |
| 加载器 | 串口 `DARWINBOOT:` 行 |
| 内核 | 串口 |
| QEMU | `--log` 文件和 `.clean.log` |
## 检查报告
```sh
jq . out/<board>/boot/boot.json
jq . out/<board>/payload/payload.json
jq . out/<board>/userdata/userdata-root.json
```
报告记录输入哈希,与提供的文件比较。
## 模块来源
```sh
./unixnu status
```
输出列出每个模块和来源。本地模块会被标记并报告为未固定。
## 下一步
- [构建](/zh/building)
- [启动镜像](/zh/guide/boot)
- [调试与日志](/zh/guide/debugging)
- [bring-up 顺序](/zh/porting/bring-up)
+103
View File
@@ -0,0 +1,103 @@
# 启动参数
内核命令行来自板级文件的 `[payload] kernel_args` 和每个 `--kernel-arg`。负载步骤把合并后的行写入 `unixnu.toml`,DarwinBoot 复制到 `boot_args`。
## 格式
参数匹配:
```
[A-Za-z0-9_.-]+(=[A-Za-z0-9_.,-]+)?
```
合并后的行最多 1000 字节。DarwinBoot 把它复制到 1024 字节缓冲区。
```sh
./unixnu build payload --kernel-arg serial=3 --kernel-arg debug=0x14e
```
## 常用参数
| 参数 | 用途 |
| --- | --- |
| `-v` | 详细内核输出 |
| `serial=3` | 选择串口控制台 |
| `debug=0x14e` | 调试输出掩码 |
| `kextlog=0xfff` | kext 详细日志 |
| `-apfs_shared_datavolume` | 使用 APFS 共享 Data 卷 |
| `-no_slto_panic` | 关闭 SLTO panic |
| `kt_fixed_zones=<n>` | 固定 kernel trace zone 数量 |
| `boot-progress=<n>` | 选择启动进度显示级别 |
| `cs_debug=<mask>` | 代码签名调试输出 |
| `amfi_get_out_of_my_way=1` | 关闭 AMFI 强制执行 |
| `pmap_debug=<mask>` | pmap 调试输出 |
| `zlog=<name>` | 选择 zone 日志 |
`amfi_get_out_of_my_way=1` 只用于诊断启动,它会改变安全策略。
## 平台参数
| 参数 | 用途 |
| --- | --- |
| `sheng-platform-stage=<n>` | 在平台专家固定阶段停住 |
| `sheng-pmgr=<n>` | 选择板级 PMGR 集成 |
| `native-ufs-root=1` | 使用原生 UFS 根路径 |
| `native-ufs-stats-delay=<n>` | 延迟 UFS 统计读取 |
| `armcpu-diagnostics=1` | 打印 CPU 和中断状态 |
| `armcpu-spi-ipi=1` | 使用 SPI 路径发送核间中断 |
| `sheng-smp=1` | 启动 secondary CPU |
| `sheng-cpufreq=<n>` | 选择 CPU 频率路径 |
| `sheng-touch=<n>` | 运行触控探针阶段 |
| `sheng-touch-hz=<n>` | 选择触控 SPI 时钟 |
| `sheng-touch-mode=<n>` | 强制触控 SPI 模式 |
| `sheng-touch-hid=1` | 把触控点发布到 HID |
| `sheng-aks=1` | 启用 AKS 路径 |
| `sheng-gpu-power=<n>` | 选择 GPU 电源序列 |
| `sheng-gpu-metal=1` | 启用 Metal 路径 |
| `sheng-gpu-addon=1` | 挂载 GPU addon 内容 |
| `sheng-disable-watchdogd=1` | 停止 watchdog daemon |
| `sheng-disable-jobs=<list>` | 跳过指定 launch job |
| `offline-legacy-*=1` | 启用 legacy 显示和输入探针 |
| `offline-owner-root-check=1` | 启动 verified root 检查 |
| `offline-owner-root-audit=1` | 启用根绑定审计 |
| `offline-apfs-load=1` | 在离线检查点加载 APFS |
| `offline-mobile-graphics-*=1` | 启用 mobile graphics 探针 |
| `boot-ui-focus=<name>` | 选择启动 UI trace 的进程 |
上表包含板级参数。设备使用的集合以设备页面和板级文件为准。
## 新增参数
1. 把默认值加入 `[payload] kernel_args`。
2. 用 `--kernel-arg` 加入一次性取值。
3. 内核中用 `PE_parse_boot_argn` 读取:
```c
unsigned stage = 0;
if (PE_parse_boot_argn("my-driver-stage", &stage, sizeof(stage))) {
...
}
```
4. 在设备页面记录该参数。
## 调试
| 需求 | 参数 |
| --- | --- |
| 早期内核输出 | `-v serial=3 debug=0x14e` |
| kext 加载细节 | `kextlog=0xfff` |
| 启动进度 | `boot-progress=2` |
| 平台停点 | `sheng-platform-stage=<n>` |
| 存储细节 | `native-ufs-root=1 native-ufs-stats-delay=30` |
| 显示细节 | `offline-legacy-gop=1 offline-legacy-phase=<n>` |
| 代码签名细节 | `cs_debug=<mask>` |
panic 日志记录最后的内核输出。设置 `XNU_DT_PANIC_REGION` 时,内嵌 panic 区域从设备树读取。
## 下一步
- [故障排查](/zh/reference/troubleshooting)
- [启动镜像](/zh/guide/boot)
- [调试与日志](/zh/guide/debugging)
- [设备](/zh/devices/)
+196
View File
@@ -0,0 +1,196 @@
# 4 KiB 页表
Apple ARM64 内核在多数 SoC 上使用 16 KiB 页。非 Apple ARM64 SoC 常用 4 KiB granule。UniXNU 为携带 4 KiB 布局的 BCM2837 机型配置构建 XNU,并修改 XNU 中假设 16 KiB 的位置。
## granule 常量
| 机型 | `ARM_PGSHIFT` | `ARM_PGBYTES` | `TCR_EL1_BOOT` TG0 | TG1 |
| --- | --- | --- | --- | --- |
| `BCM2837` | 12 | 4096 | 0,4 KiB | 2,4 KiB |
| `VMAPPLE` | 14 | 16384 | 2,16 KiB | 1,16 KiB |
`scripts/check-xnu-granules.py` 为两个 profile 编译 `TCR_EL1_BOOT` 并检查 TG0 和 TG1 字段。
内核按 4 KiB 编译。页大小属于机型配置和构建参数,16 KiB 内核和 4 KiB 内核是两次独立构建。
## 启动页表
`osfmk/arm64/start.s` 在 MMU 开启前构建 bootstrap 页表。页表池大小由 `BOOTSTRAP_TABLE_SIZE` 决定。
补丁 `native-4k-bootstrap-capacity` 加入非 Apple 4 KiB 预算:
```c
#elif !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
/* 两个根,加上每 6 GiB 映射最多七个 L2 页。 */
#define BOOTSTRAP_TABLE_SIZE (ARM_PGBYTES * 16)
```
同一补丁在分配新 L2 表前检查容量:
```asm
add $6, x25, #(BOOTSTRAP_TABLE_BYTES >> 12), lsl #12
cmp $4, $6
b.hs Lbootstrap_tables_exhausted
```
耗尽标签在循环中停住,游标和结束地址保留在寄存器中。此时 MMU 未开启,调试器可以直接查看寄存器。
补丁还把整个保留池清零,而不是只清四个 16 KiB 页:
```asm
mov x2, #(BOOTSTRAP_TABLE_BYTES / ARM_PGBYTES)
lsl x2, x2, #(ARM_PGSHIFT - TTE_SHIFT)
```
## 运行时页大小
`osfmk/arm/arm_init.c` 在 Apple 板上按 RAM 大小选择页大小。补丁 `native-4k-user-abi` 让原生 4 KiB 构建在任何 RAM 大小下使用硬件页大小:
```c
#if !defined(APPLE_ARM64_ARCH_FAMILY) && !__ARM_16K_PG__
PAGE_SHIFT_CONST = ARM_PGSHIFT;
page_shift_user32 = ARM_PGSHIFT;
#else
...
#endif
```
`scripts/check-native-4k-policy.py` 从打补丁后的源码提取策略,并在 RAM 边界执行:
| Profile | RAM | 内核 shift | 用户 32 位 shift |
| --- | --- | --- | --- |
| native 4K | 任意 | 12 | 12 |
| Apple 4K | 1 GiB 以内 | 12 | 14 |
| Apple 4K | 1 GiB 以上 | 14 | 14 |
| Apple 16K | 任意 | 14 | 14 |
Apple 4K profile 在 1 GiB 以上切换到 16 KiB,native 构建在任何大小下保持 4 KiB。
## 用户地址空间
补丁修改用户 map 策略:
| 文件 | 修改 |
| --- | --- |
| `bsd/kern/mach_loader.c` | 64 位进程使用 `FOURK_PAGE_SHIFT` |
| `osfmk/arm/commpage/commpage.c` | 64 位 commpage shift 为 12 |
| `osfmk/vm/vm_shared_region.c` | shared region 子 map 使用 4 KiB 项 |
| `osfmk/arm/arm_init.c` | `PAGE_SHIFT_CONST` 和 `page_shift_user32` 跟随 `ARM_PGSHIFT` |
4 KiB 用户态需要 4 KiB `mmap` 对齐、4 KiB commpage shift 和 4 KiB dyld shared cache 布局。读取 commpage 的进程在 legacy 和当前 64 位 shift 字段都得到 12。
## 代码签名
原始 CodeDirectory 使用 16 KiB 哈希页,4 KiB 内核校验 4 KiB 页。补丁 `native-4k-signature-hash-cache` 连接两者:
1. 在 12 位内核上接受 `pageSize = 14` 的 CodeDirectory。
2. 通过 `vn_rdwr` 读取每个原始 16 KiB 块。
3. 用原始 CodeDirectory 哈希校验该块。
4. 从已验证块派生每 4 KiB 页的摘要。
5. 把派生摘要存入只读内核内存。
6. 让 `cs_validate_page` 使用派生表。
派生表挂在 `cs_blob`:
```c
const uint8_t *csb_native4k_hashes;
vm_size_t csb_native4k_hashes_size;
vm_size_t csb_native4k_hash_count;
```
页校验检查索引、偏移对齐和表边界。未命中返回 `CS_VALIDATE_TAINTED`。原始 16 KiB 哈希失败的块不会产生表。
构建开关为 `XNU_NATIVE_CS_HASH_CACHE`。`scripts/check-code-signing-granules.py` 编译实际的 `cs_validate_codedirectory` gate,并检查内核 shift、签名 shift 和 cache 模式的八种组合。
## 分配器对齐
zone 分配器把元数据放在 secondary page。补丁 `pgz-aligned-validation` 在页掩码检查前解码 PGZ 地址:
```c
elem = pgz_decode((vm_offset_t)addr, 1);
offs = (elem & PAGE_MASK) - zone_elem_inner_offs(zone);
if (from_zone_map(elem, 1)) {
```
后续元数据查找使用解码后的元素地址。
## 4 GiB 以上的物理地址
`pmap_sync_page_attributes_phys` 原先用 32 位 shift 处理物理页号:
```c
flush_dcache((vm_offset_t)(pp << PAGE_SHIFT), PAGE_SIZE, TRUE);
```
补丁 `zzx-pmap-sync-64bit-pa` 改用 `ptoa()`:
```c
flush_dcache((vm_offset_t)ptoa(pp), PAGE_SIZE, TRUE);
```
`ptoa()` 在 shift 前扩展页号。DRAM 从 4 GiB 以上开始的设备保留高位地址。
## 非 Apple 平台保护
若干 Apple 专属路径在非 Apple 平台上需要保护:
| 补丁 | 修改 |
| --- | --- |
| `apple-only-cpu-tunables` | 仅在 `APPLE_ARM64_ARCH_FAMILY` 应用 Apple CPU tunables |
| `non-apple-cache-monitor` | 仅在 `XNU_MONITOR` 下调用 `ml_enable_monitor`,并导出 cache clean 符号 |
| `non-apple-iommu-bootstrap` | 保护 UAT 和 SART bootstrap 及 PPL 代码 |
| `non-apple-pmap-field` | 按非 PAC 布局重命名保留字段 |
| `source-machine-layer` | 加入 `CONFIG_APPLE_AMCC`,去掉 Apple 专属配置项 |
| `native-arm-pan` | 设置 `XNU_NATIVE_PAN` 时在 ARMv8.1 启用 PAN |
## SMMU 与 4 KiB 表
`ArmSmmu4K` kit 用 4 KiB granule 构建 stage 1 和 stage 2 表:
| 文件 | 功能 |
| --- | --- |
| `ArmSmmu4KProgram.c` | 表编程 |
| `ArmSmmu4KContext.c` | context descriptor |
| `ArmSmmu4KStreams.c` | stream match group |
| `ArmSmmu4KLive.c` | live stream 映射 |
| `ArmSmmu4KTableMemory.c` | 表内存 |
设备驱动用 4 KiB 表映射 DMA 缓冲。触控驱动示例使用 apps SMMU bypass 路径。
## 验证
| 检查 | 工具 |
| --- | --- |
| granule 常量 | `scripts/check-xnu-granules.py` |
| 页大小策略 | `scripts/check-native-4k-policy.py` |
| 代码签名 gate | `scripts/check-code-signing-granules.py` |
| PGZ 对齐 | `scripts/check-pgz-alignment-checkpoints.py` |
| UFS VA64 证据 | `scripts/check-ufs-va64-evidence.py` |
| QEMU HVF 观察 | `scripts/qemu-entry/gdb-stop.py` 与 native 4K fixture |
QEMU 观察记录:
| 寄存器 | 期望 |
| --- | --- |
| `TCR_EL1` TG0 | 0,4 KiB |
| `TCR_EL1` TG1 | 2,4 KiB |
| `SCTLR_EL1` M | 1,MMU 开启 |
| `TTBR0_EL1`、`TTBR1_EL1` | 非零 |
| `_PAGE_SHIFT_CONST`、`_page_shift`、`_page_shift_user32`、`_page_size` | 12、12、12、4096 |
## 新代码规则
1. 使用 `PAGE_SIZE` 和 `PAGE_SHIFT`,汇编使用 `ARM_PGBYTES` 和 `ARM_PGSHIFT`。
2. 代码签名哈希 shift 与内核页 shift 分开。
3. 每个新用户 map 设置页 shift。
4. DMA 缓冲和 descriptor 按 `PAGE_SIZE` 对齐。
5. 新 IOMMU 上下文使用 4 KiB SMMU 表。
6. 应用页掩码前解码 PGZ 地址。
7. 4 GiB 以上物理地址使用 `ptoa()` 和 `phystokv()`。
8. 为每个映射的 GiB 预算 bootstrap 页表。
## 下一步
- [补丁参考](/zh/xnu/patch-reference)
- [驱动分层](/zh/drivers/overview)
- [构建内核](/zh/guide/build-kernel)
+119
View File
@@ -0,0 +1,119 @@
# 补丁参考
补丁按名称顺序应用 `patches/xnu/*.patch` 和 `kits/NativePrelink/patches/*.patch`。后置补丁 `zzy-*` 和 `zzz-*` 最后应用。
## 构建与工具链
| 补丁 | 作用 |
| --- | --- |
| `apple-only-cpu-tunables` | 仅在 `APPLE_ARM64_ARCH_FAMILY` 应用 Apple CPU tunables |
| `cxx-flag-dependencies` | Makefile 模板把 `CFLAGS` 传给 C++ 目标 |
| `external-archive-dependencies` | 外部静态库变化时重新链接内核 |
| `kernel-sdk-root` | 分离 `KERNEL_SDKROOT` 与 `SDKROOT` 并加入 include 路径 |
| `local-availability` | 允许 `XNU_AVAILABILITY_PL` 指向本地 `availability.pl` |
| `public-entitlements-header` | 加入浏览器 entitlement 名称并使用 `CoreEntitlements` |
| `source-machine-layer` | 加入 `CONFIG_APPLE_AMCC` 并调整 BCM2837 配置和文件列表 |
| `disabled-deferred-reclaim` | 去掉破坏构建的未使用参数 |
| `published-freezer-condition` | 修正 freezer 默认值的预处理条件 |
## 平台与 CPU
| 补丁 | 作用 |
| --- | --- |
| `architectural-timer-entry` | 启用原生架构定时器时跳过 BCM2837 定时器路由 |
| `armvirt-platform-personality` | 加入 `ArmCpu`、`ArmVirtPlatform` 和 `ArmDTPlatform` personality |
| `native-smp-secondary` | `MAX_CPUS` 提高到 8,BCM2837 secondary CPU 使用 MPIDR Affinity1 |
| `native-arm-pan` | 设置 `XNU_NATIVE_PAN` 时启用 ARMv8.1 PAN |
| `native-dt-panic-region` | 从设备树读取并校验 panic 日志区域 |
## 4 KiB 页
| 补丁 | 作用 |
| --- | --- |
| `native-4k-bootstrap-capacity` | 为 4 KiB 映射调整 bootstrap 页表池,并在溢出前停止 |
| `native-4k-user-abi` | 为用户 map、commpage 和 shared region 设置 4 KiB shift |
| `native-4k-signature-hash-cache` | 从已验证 16 KiB 块派生 4 KiB 代码签名哈希 |
| `pgz-aligned-validation` | 对齐检查前解码 PGZ 地址 |
| `zzx-pmap-sync-64bit-pa` | 用 `ptoa()` 保留 4 GiB 以上物理地址 |
| `non-apple-pmap-field` | 匹配非 PAC pmap 字段布局 |
| `non-apple-iommu-bootstrap` | 保护 UAT 和 SART bootstrap 及 PPL 代码 |
| `non-apple-cache-monitor` | 保护 monitor enable 并导出 cache clean 符号 |
## 安全与 owner root
| 补丁 | 作用 |
| --- | --- |
| `native-trust-cache-runtime` | 在 BCM2837 初始化 trust cache 运行时 |
| `source-corecrypto14-bootstrap` | 早期启动时注册源码 corecrypto |
| `source-pthread-bootstrap` | 早期启动时注册源码 pthread |
| `native-aes-personality` | 加入 AES、VirtIO 块、VirtIO 控制台、RAM 块和 UFS personality |
| `offline-owner-root-crypto-probe` | 设置 `offline-owner-root-check` 时启动 verified root 探针 |
| `offline-owner-root-binding-audit` | 比较挂载根设备和 verified root 设备 |
| `offline-owner-boot-manifest` | 从根和 cache 设备发布 owner 启动 manifest 哈希 |
| `offline-owner-cache-carrier` | 选择 owner dyld cache 载体并校验设备和路径 |
| `offline-owner-systemos-mount` | 挂载 SystemOS cryptex 并用 owner cache 组合根 |
| `offline-owner-z-air-init` | 初始化 owner root 的 Z air 路径 |
| `offline-legacy-start` | 在 `IOKitBSDInit` 后启动 legacy display 探针 |
| `offline-component-load-probes` | 加载 APFS、IOGPU 和 mobile graphics 组件并记录检查点 |
| `offline-native-gpu-broker` | DTrace 初始化后发布离线 GPU broker |
| `sheng-platform-power` | 日历初始化前运行板级 PMGR 集成 |
## 图形与显示
| 补丁 | 作用 |
| --- | --- |
| `offline-native-gpu-broker` | 启动离线 GPU broker |
| `zzy-native-gpu-addon-mount` | 为 GPU addon 内容和启动任务挂载只读 union 视图 |
| `native-aes-personality` | 注册存储和控制台 personality |
## 诊断
| 补丁 | 作用 |
| --- | --- |
| `offline-pgz-alignment-probe` | 加入 PGZ 分配探针和检查点 |
| `native-work-interval-trace` | 记录最近的 work interval 结果供启动进度页显示 |
| `zzz-boot-progress` | 在内核启动路径加入启动进度检查点 |
## 行为修复
| 补丁 | 作用 |
| --- | --- |
| `native-priv-work-interval` | 在策略评估前授予 `PRIV_WORK_INTERVAL` |
| `native-work-interval-soft-fail` | 被拒绝的 CA client work interval 软失败 |
| `native-union-getattrlistbulk` | 批量目录读取继续到 union 下层目录 |
| `null-root-media` | 处理 `IOService::setRootMedia` 的空 root |
| `published-freezer-condition` | 修正 freezer 默认条件 |
## Mockfs
| 补丁 | 作用 |
| --- | --- |
| `mockfs-intermediate-lookup` | 允许非最后分量的查找 |
| `mockfs-memory-device-width` | legacy ioctl 无法表达 64 位地址时使用设备路径 |
| `mockfs-statfs` | 实现 `vfs_getattr` |
| `mockfs-typed-allocation` | `kalloc_type` 和 `kfree_type` 使用结构体类型 |
| `mockfs-test-file` | 在测试文件系统中加入 `launchd` 和 `probe.bin` |
## 预链接补丁
`kits/NativePrelink/patches/`:
| 补丁 | 作用 |
| --- | --- |
| `xnu-10063.141.1-security-init.patch` | 加入早期安全初始化入口 |
| `xnu-10063.141.1-sparse-index.patch` | 加入稀疏预链接索引 ABI |
## 新增补丁
1. 编辑 `.cache/xnu-work`。
2. 用 `git diff` 生成补丁。
3. 以正确的顺序命名并放入 `patches/xnu/`。
4. 重新构建内核。
依赖其他补丁的补丁排在其后。修改 `zzy` 或 `zzz` 区域的补丁排在其前。
## 下一步
- [源码与补丁](/zh/xnu/source-and-patches)
- [4 KiB 页表](/zh/xnu/four-k-page-tables)
- [构建内核](/zh/guide/build-kernel)
+146
View File
@@ -0,0 +1,146 @@
# 源码与补丁
UniXNU 从固定版本的 Apple 源码构建 XNU,并按名称顺序应用补丁。
## 源码
`feeds.toml` 固定源码:
| 源码 | tag | 用途 |
| --- | --- | --- |
| `xnu` | `xnu-10063.141.1` | 内核源码 |
| `libplatform` | `libplatform-316.100.10` | 平台头文件 |
| `libdispatch` | `libdispatch-1477.100.9` | dispatch 头文件 |
| `AvailabilityVersions` | `AvailabilityVersions-141.1` | availability 宏 |
| `edk2` | `release/202608` | package 和固件的 EDK II 基础 |
XNU 条目还包含:
```toml
darwin = "23.6.0"
version = "10063.141.1"
driverkit_tag = "xnu-10063.121.3"
trustcache_tag = "xnu-8796.101.5"
```
`driverkit_tag` 提供当前 tag 缺失的 `iokit/DriverKit` 头文件,`trustcache_tag` 提供 `EXTERNAL_HEADERS/TrustCache`。
## 补丁应用
`scripts/build-xnu-4k-baseline.sh` 对 `.cache/xnu-work` 应用补丁:
1. 存在时反向应用两个后置补丁:
- `patches/xnu/zzz-boot-progress.patch`
- `patches/xnu/zzy-native-gpu-addon-mount.patch`
2. 按名称顺序应用 `patches/xnu/*.patch` 和 `kits/NativePrelink/patches/*.patch`。
3. 已应用的补丁跳过。
4. 运行 `prepare-xnu-source.py` 恢复缺失头文件。
后置补丁修改的区域与更早的补丁重叠,`zzz` 和 `zzy` 前缀让它们最后应用。反向步骤让新构建从这些区域的干净上下文开始。
补丁是幂等的:
```sh
git -C .cache/xnu-work apply --reverse --check "$PATCH" && skip
git -C .cache/xnu-work apply --check "$PATCH"
git -C .cache/xnu-work apply "$PATCH"
```
`--check` 在部分应用前停止构建。
## 补丁目录
| 目录 | 内容 |
| --- | --- |
| `patches/xnu/` | 内核补丁 |
| `patches/libpthread/` | 源码 pthread 补丁 |
| `patches/mesa/` | Turnip 和 Mesa 构建补丁 |
| `kits/NativePrelink/patches/` | 预链接 ABI 补丁 |
设备仓库可以加入 `patches/` 模块,构建树把它链接到 `tree/patches/`,内核脚本读取 `$ROOT/patches/xnu/*.patch`,其中包含设备补丁。
## 新增补丁
1. 构建一次内核,生成 `.cache/xnu-work`。
2. 编辑 `.cache/xnu-work` 中的源码。
3. 生成补丁:
```sh
git -C .cache/xnu-work diff -- <paths> > patches/xnu/my-change.patch
```
4. 再次运行 `unixnu build kernel`。
新增构建选项的补丁参考 `native-aes-personality.patch`:
```c
#if defined(XNU_MY_OPTION) && XNU_MY_OPTION
" ,{"
" 'IOClass' = MyDriver;"
" 'IOProviderClass' = IOPlatformDevice;"
" }"
#endif
```
对应的 `-DXNU_MY_OPTION=1` 来自内核脚本的 `EXTRA_MAKE_ARGS`。
## 构建开关
| 开关 | 值 |
| --- | --- |
| `SDKROOT` | `iphoneos` |
| `ARCH_CONFIGS` | `ARM64` |
| `MACHINE_CONFIGS` | `BCM2837` |
| `KERNEL_CONFIGS` | `DEVELOPMENT` |
| `ARM64_LINK_ADDR` | `0xfffffff007004000` |
| `ARM64_PLKSEG_ADDR` | `0xfffffff004004000` |
| `DEPLOYMENT_TARGET_FLAGS` | `-miphoneos-version-min=17.0 -DXNU_TARGET_OS_IOS` |
| `BUILD_LTO` | `0` |
| `BOUND_CHECKS` | `0` |
`BCM2837` 携带 4 KiB 页布局。`BUILD_LTO=0` 保持目标文件和编译记录可读,并与外部静态库链接兼容。
## defconfig 映射
每个 `CONFIG_XNU_<NAME>` 变成 `XNU_<NAME>`:
| defconfig 值 | 构建值 |
| --- | --- |
| `y` | `1` |
| `n` | `0` |
| `"text"` | `text` |
| `123` | `123` |
| 字符串中的 `{tree}` | 构建树路径 |
内核脚本读取这些变量并加入目标文件开关、静态库和链接输入。
## 源码树
```
out/<board>/tree/
refer/xnu/ 固定源码
.cache/xnu-work/ 工作克隆
.cache/fakeroot/ 内核 SDK 根
.cache/sources/ 恢复头文件清单
build/xnu/ios-obj/ 目标文件和编译记录
build/xnu/ios-sym/ 符号
build/xnu/ios-dst/ 安装头文件
patches/xnu/ 链接的补丁
kits/NativePrelink/ 预链接工具和补丁
```
## 更新源码
1. 在 `feeds.toml` 中设置新的 `ref` 和 `commit`。
2. 更新 `darwin`、`version`、`driverkit_tag` 和 `trustcache_tag`。
3. 把补丁 rebase 到新 tag。
4. 按新的编译记录重建设备静态库。
5. 重建预链接并重新验证 kernelcache 固定。
预链接输出依赖原始 kernelcache ABI。新的源码版本可能需要新的 kernelcache 固定和 roots。
## 下一步
- [4 KiB 页表](/zh/xnu/four-k-page-tables)
- [补丁参考](/zh/xnu/patch-reference)
- [构建内核](/zh/guide/build-kernel)
@@ -8,7 +8,7 @@
#include <Library/UefiBootServicesTableLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiLib.h>
#include <Protocol/CrabAppleXnuLoader.h>
#include <Protocol/UniXNULoader.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/GraphicsOutput.h>
#include <Protocol/SimpleFileSystem.h>
@@ -445,8 +445,8 @@ STATIC
EFI_STATUS
EFIAPI
Boot (
IN CRABAPPLE_XNU_LOADER_PROTOCOL *This,
IN CONST CRABAPPLE_XNU_BOOT_REQUEST *Request
IN UNIXNU_LOADER_PROTOCOL *This,
IN CONST UNIXNU_LOADER_BOOT_REQUEST *Request
)
{
EFI_STATUS Status;
@@ -658,14 +658,14 @@ Fail:
return Status;
}
STATIC CRABAPPLE_XNU_LOADER_PROTOCOL mLoader = { CRABAPPLE_XNU_LOADER_REVISION, Boot };
STATIC UNIXNU_LOADER_PROTOCOL mLoader = { UNIXNU_LOADER_REVISION, Boot };
STATIC
EFI_STATUS
EFIAPI
Unload (EFI_HANDLE ImageHandle)
{
return gBS->UninstallMultipleProtocolInterfaces (ImageHandle, &gCrabAppleXnuLoaderProtocolGuid, &mLoader, NULL);
return gBS->UninstallMultipleProtocolInterfaces (ImageHandle, &gUniXNULoaderProtocolGuid, &mLoader, NULL);
}
EFI_STATUS
@@ -677,12 +677,12 @@ DarwinBootEntry (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
VOID *Existing;
(VOID)SystemTable;
Status = gBS->LocateProtocol (&gCrabAppleXnuLoaderProtocolGuid, NULL, &Existing);
Status = gBS->LocateProtocol (&gUniXNULoaderProtocolGuid, NULL, &Existing);
if (!EFI_ERROR (Status)) { return EFI_ALREADY_STARTED; }
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&Loaded);
if (EFI_ERROR (Status)) { return Status; }
mImage = ImageHandle;
Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle, &gCrabAppleXnuLoaderProtocolGuid, &mLoader, NULL);
Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle, &gUniXNULoaderProtocolGuid, &mLoader, NULL);
if (!EFI_ERROR (Status)) {
Loaded->Unload = Unload;
Print (L"DARWINBOOT: loader v2 installed EL%u\n", (UINT32)(DarwinCurrentEl () >> 2));
@@ -17,7 +17,7 @@
[Packages]
MdePkg/MdePkg.dec
CrabApplePkg/CrabApplePkg.dec
UniXNUPkg/UniXNUPkg.dec
[LibraryClasses]
BaseLib
@@ -30,7 +30,7 @@
UefiLib
[Protocols]
gCrabAppleXnuLoaderProtocolGuid ## PRODUCES
gUniXNULoaderProtocolGuid ## PRODUCES
gEfiSimpleFileSystemProtocolGuid ## CONSUMES
gEfiLoadedImageProtocolGuid ## CONSUMES
gEfiGraphicsOutputProtocolGuid ## CONSUMES
+11 -11
View File
@@ -4,7 +4,7 @@
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/CrabAppleXnuLoader.h>
#include <Protocol/UniXNULoader.h>
#include <Protocol/LoadedImage.h>
#include "../DarwinBootPkg/Driver/BootVideo.h"
@@ -63,8 +63,8 @@ ValidationEntry (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
L"bad-entry", L"bad-adt", L"missing-adt" };
CHAR16 Path[128];
EFI_LOADED_IMAGE_PROTOCOL *Loaded;
CRABAPPLE_XNU_LOADER_PROTOCOL *Loader;
CRABAPPLE_XNU_BOOT_REQUEST Request;
UNIXNU_LOADER_PROTOCOL *Loader;
UNIXNU_LOADER_BOOT_REQUEST Request;
UINTN Index;
UINT8 RamdiskByte = 0x5a;
CHAR16 *RamdiskPaths[] = { L"missing-ramdisk-adt", L"stale-ramdisk-adt",
@@ -79,11 +79,11 @@ ValidationEntry (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
(VOID)SystemTable;
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&Loaded);
if (EFI_ERROR (Status)) { return Status; }
Status = gBS->LocateProtocol (&gCrabAppleXnuLoaderProtocolGuid, NULL, (VOID **)&Loader);
Status = gBS->LocateProtocol (&gUniXNULoaderProtocolGuid, NULL, (VOID **)&Loader);
if (EFI_ERROR (Status)) { return Status; }
ZeroMem (&Request, sizeof (Request));
Request.Volume = Loaded->DeviceHandle;
Request.KernelPath = L"\\EFI\\CrabApple\\Payloads\\kernel.macho";
Request.KernelPath = L"\\EFI\\UniXNU\\Payloads\\kernel.macho";
if (Loader->Boot (Loader, NULL) != EFI_INVALID_PARAMETER) { goto Failed; }
Request.KextCount = 1;
if (Loader->Boot (Loader, &Request) != EFI_UNSUPPORTED) { goto Failed; }
@@ -100,11 +100,11 @@ ValidationEntry (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
if (Loader->Boot (Loader, &Request) != EFI_BAD_BUFFER_SIZE) { goto Failed; }
Request.RamdiskSize = 1;
for (Index = 0; Index < ARRAY_SIZE (RamdiskPaths); Index++) {
UnicodeSPrint (Path, sizeof (Path), L"\\EFI\\CrabApple\\Payloads\\%s.macho", RamdiskPaths[Index]);
UnicodeSPrint (Path, sizeof (Path), L"\\EFI\\UniXNU\\Payloads\\%s.macho", RamdiskPaths[Index]);
Request.KernelPath = Path;
if (Loader->Boot (Loader, &Request) != EFI_COMPROMISED_DATA) { goto Failed; }
}
Request.KernelPath = L"\\EFI\\CrabApple\\Payloads\\kernel.macho";
Request.KernelPath = L"\\EFI\\UniXNU\\Payloads\\kernel.macho";
Request.Ramdisk = NULL;
Request.RamdiskSize = 0;
if (Loader->Boot (Loader, &Request) != EFI_UNSUPPORTED) { goto Failed; }
@@ -112,19 +112,19 @@ ValidationEntry (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
if (Loader->Boot (Loader, &Request) != EFI_BAD_BUFFER_SIZE) { goto Failed; }
Request.BootArgs = NULL;
for (Index = 0; Index < ARRAY_SIZE (TrustPaths); Index++) {
UnicodeSPrint (Path, sizeof (Path), L"\\EFI\\CrabApple\\Payloads\\%s.macho", TrustPaths[Index]);
UnicodeSPrint (Path, sizeof (Path), L"\\EFI\\UniXNU\\Payloads\\%s.macho", TrustPaths[Index]);
Request.KernelPath = Path;
if (Loader->Boot (Loader, &Request) != EFI_COMPROMISED_DATA) { goto Failed; }
}
Request.KernelPath = L"\\EFI\\CrabApple\\Payloads\\unavailable-memory-window.macho";
Request.KernelPath = L"\\EFI\\UniXNU\\Payloads\\unavailable-memory-window.macho";
if (Loader->Boot (Loader, &Request) != EFI_OUT_OF_RESOURCES) { goto Failed; }
for (Index = 0; Index < ARRAY_SIZE (MemoryPaths); Index++) {
UnicodeSPrint (Path, sizeof (Path), L"\\EFI\\CrabApple\\Payloads\\%s.macho", MemoryPaths[Index]);
UnicodeSPrint (Path, sizeof (Path), L"\\EFI\\UniXNU\\Payloads\\%s.macho", MemoryPaths[Index]);
Request.KernelPath = Path;
if (Loader->Boot (Loader, &Request) != EFI_COMPROMISED_DATA) { goto Failed; }
}
for (Index = 0; Index < ARRAY_SIZE (Paths); Index++) {
UnicodeSPrint (Path, sizeof (Path), L"\\EFI\\CrabApple\\Payloads\\%s.macho", Paths[Index]);
UnicodeSPrint (Path, sizeof (Path), L"\\EFI\\UniXNU\\Payloads\\%s.macho", Paths[Index]);
Request.KernelPath = Path;
Status = Loader->Boot (Loader, &Request);
if (Status != Expected[Index]) {
+2 -2
View File
@@ -12,7 +12,7 @@
[Packages]
MdePkg/MdePkg.dec
CrabApplePkg/CrabApplePkg.dec
UniXNUPkg/UniXNUPkg.dec
[LibraryClasses]
BaseMemoryLib
@@ -22,5 +22,5 @@
UefiLib
[Protocols]
gCrabAppleXnuLoaderProtocolGuid
gUniXNULoaderProtocolGuid
gEfiLoadedImageProtocolGuid
-47
View File
@@ -1,47 +0,0 @@
## @file
# UniXNU: a device-agnostic UEFI boot manager.
#
# Copyright (c) 2026, UniXNU contributors. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent
##
[Defines]
DEC_SPECIFICATION = 0x00010019
PACKAGE_NAME = CrabApplePkg
PACKAGE_GUID = E2C7EB96-33AF-4BCD-BA70-26A6BAB3719D
PACKAGE_VERSION = 0.1
[Includes]
Include
[LibraryClasses]
## @libraryclass The boot manager core shared by the application and the
# DXE driver builds.
CrabAppleLib|Include/Library/CrabAppleLib.h
[Guids]
gCrabApplePkgTokenSpaceGuid = { 0x13c7e6d7, 0x043c, 0x475b, { 0xba, 0x77, 0x0e, 0x79, 0xd5, 0xad, 0x88, 0x98 } }
## Vendor namespace for UEFI variables that UniXNU itself defines.
gCrabAppleVariableGuid = { 0xf50548ac, 0xb469, 0x47c6, { 0x89, 0xea, 0xc1, 0x73, 0x8b, 0x81, 0x21, 0x65 } }
## Vendor media node of block devices published by [[block_slice]]
# (Include/Guid/CrabAppleBlockSlice.h).
gCrabAppleBlockSliceGuid = { 0x63e1d76a, 0x715f, 0x4b8c, { 0x85, 0x0a, 0xe1, 0x58, 0xd1, 0x27, 0x0f, 0x9f } }
[Protocols]
## Optional loader for `type = "xnu"` entries. UniXNU only defines the
# interface; it never ships an implementation (see Docs/Boundaries.md).
gCrabAppleXnuLoaderProtocolGuid = { 0x0f0a492e, 0x5a6a, 0x49f6, { 0xa1, 0xbf, 0x74, 0x3c, 0x87, 0xab, 0x1c, 0xfe } }
[PcdsFixedAtBuild]
## DEBUG() level used for UniXNU's own log lines. Platforms whose debug
# output shares the screen with the menu can lower it to DEBUG_VERBOSE.
gCrabApplePkgTokenSpaceGuid.PcdCrabAppleDebugLevel|0x00000040|UINT32|0x00000001
## Path of the configuration file on every scanned volume.
gCrabApplePkgTokenSpaceGuid.PcdCrabAppleConfigPath|L"\\EFI\\CrabApple\\crabapple.toml"|VOID*|0x00000002
## FV file name (GUID) of the UniXNU application that CrabAppleDxe
# registers as a boot option. Defaults to CrabApple.inf's FILE_GUID.
gCrabApplePkgTokenSpaceGuid.PcdCrabAppleApplicationFile|{ GUID("9B665204-A133-47A2-9396-9E2B370A4E25") }|VOID*|0x00000003
@@ -1 +0,0 @@
CRABAPPLE-INITRD-OK this text stands in for an initramfs
+2 -2
View File
@@ -10,7 +10,7 @@ curves with round joins and caps.
Outputs:
../assets/logo-dark.svg, ../assets/logo-light.svg the vector logo, transparent
CrabApplePkg/Library/CrabAppleLib/LogoShape.h the same strokes, flattened
UniXNUPkg/Library/UniXNULib/LogoShape.h the same strokes, flattened
to integer polylines for the
firmware renderer (Logo.c)
"""
@@ -164,7 +164,7 @@ def main():
'STATIC CONST UINT16 mLogoStrokes[][3] = {']
header += [' { %d, %d, %d },' % s for s in strokes]
header += ['};', '', '#endif', '']
(ROOT / 'CrabApplePkg/Library/CrabAppleLib/LogoShape.h').write_text('\n'.join(header))
(ROOT / 'UniXNUPkg/Library/UniXNULib/LogoShape.h').write_text('\n'.join(header))
print('logo: %d strokes, %d points' % (len(strokes), at))
+4 -4
View File
@@ -14,7 +14,7 @@ def bundle(root: Path, name: str, code: bool = True) -> Path:
path.mkdir(parents=True, exist_ok=True)
(path / "Info.plist").write_text(
'<plist version="1.0"><dict><key>CFBundleIdentifier</key>'
f'<string>org.crabapple.test.{name}</string></dict></plist>\n'
f'<string>org.unixnu.test.{name}</string></dict></plist>\n'
)
executable = path / "MacOS" / "Test"
if code:
@@ -39,11 +39,11 @@ def main() -> None:
)
for variant in variants:
esp = args.out / variant
ca = esp / "EFI" / "CrabApple"
ca = esp / "EFI" / "UniXNU"
ca.mkdir(parents=True, exist_ok=True)
boot = esp / "EFI" / "BOOT"
boot.mkdir(parents=True, exist_ok=True)
shutil.copy2(args.binaries / "CrabApple.efi", boot / "BOOTAA64.EFI")
shutil.copy2(args.binaries / "UniXNU.efi", boot / "BOOTAA64.EFI")
shutil.copy2(args.binaries / "XnuTest.efi", ca / "XnuTest.efi")
code = bundle(ca / "Kexts", "Code")
bundle(ca / "Kexts", "Codeless", False)
@@ -95,7 +95,7 @@ def main() -> None:
config = config.replace('executable = "Contents/MacOS/Test"', 'executable = "../outside"')
elif variant == "bad-type":
config = config.replace('executable = "Contents/MacOS/Test"', 'executable = false')
(ca / "crabapple.toml").write_text(config)
(ca / "unixnu.toml").write_text(config)
if __name__ == "__main__":
+1 -1
View File
@@ -102,7 +102,7 @@ def main() -> int:
parser.add_argument("--misc", type=int, default=8, help="first partition size, MiB")
parser.add_argument("--fat", type=int, default=64, help="FAT volume size, MiB (>= 33)")
parser.add_argument("--name", default="userdata", help="GPT name of the second partition")
parser.add_argument("--marker", default="CRABAPPLE-TAIL-MARKER", help="text at the start of the tail")
parser.add_argument("--marker", default="UNIXNU-TAIL-MARKER", help="text at the start of the tail")
args = parser.parse_args()
if args.fat < 33 or args.misc + args.fat + 2 >= args.size:
+2 -2
View File
@@ -111,7 +111,7 @@ def main() -> int:
parser.add_argument("--cpus", type=int, default=2, help="number of virtual CPUs (default: 2)")
parser.add_argument("--finish-on-expect", action="store_true",
help="finish two seconds after all expected patterns match")
parser.add_argument("--menu-marker", default=r"CRABAPPLE: menu: shown")
parser.add_argument("--menu-marker", default=r"UNIXNU: menu: shown")
args = parser.parse_args()
if args.cpus < 1:
parser.error("--cpus must be positive")
@@ -123,7 +123,7 @@ def main() -> int:
sys.exit("error: qemu-system-aarch64 not found")
code, vars_template = find_firmware(args.firmware)
work = Path(tempfile.mkdtemp(prefix="crabapple-qemu-"))
work = Path(tempfile.mkdtemp(prefix="unixnu-qemu-"))
qmp_path = work / "qmp.sock"
args.log.parent.mkdir(parents=True, exist_ok=True)
if args.log.exists():
Loaded 100 of 161 files, more files were not shown because too many files have changed in this diff. Show more