> For the complete documentation index, see [llms.txt](https://freebsd-journal-cn.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://freebsd-journal-cn.bsdcn.org/20230506-freebsd-san-shi-zhou-nian-ji-nian-te-kan/ports.md).

# 早期的 FreeBSD 移植

* 原文：[Early FreeBSD Ports](https://freebsdfoundation.org/wp-content/uploads/2023/06/rabson_early_ports.pdf)
* 作者：DOUG RABSON
* 译者：basebyte

自诞生之初，FreeBSD 便专注于为 i386 架构提供稳固的支持。PC 平台普遍易得且相对廉价，将现有资源集中于此有助于使 FreeBSD 在 i386 上保持稳定和高性能。但是，几年后，我们决定扩大支持范围；第一个目标是 DEC 的 Alpha 平台，因为它是 64 位架构的平台，是不错的选择。几年之后，我们又增加了对 IA-64 的支持，当时 Intel 将其定位为 i386 的继任者。

## 移植 FreeBSD 到 Alpha 平台

在 1997 年 5 月初，Jordan Hubbard 征求志愿者来将 FreeBSD 移植到 DEC Alpha 平台。Alpha 是 64 位的加载/存储体系结构，与 i386 截然不同，它拥有更多的寄存器和 RISC 指令集。当时的硬件使用了我们熟悉的 PCI 和 ISA 总线接口。

我自愿参加了这个项目。DEC 的 Clem Cole 借给了我们一些硬件，这些硬件于 1997 年 7 月运抵（除了 Peter Wemm 的机器，我记得他的机器好像在海关丢失了）。此时，我开始逐渐意识到这个项目需要多少工作量，许多不同领域都需要修改。

### 设备驱动程序

在 FreeBSD 中，特别是针对 ISA 的设备驱动程序，需要修改以支持新的 Alpha 架构，因为硬件访问方式有很大的不同。为了使这项工作顺利推进，并在 i386 和 Alpha 平台之间尽可能共享代码，我们需要抽象层。

我对此有一些想法，这些想法最终演变成了 FreeBSD 的 newbus 框架。我的计划是从顶层系统设备总线开始自动发现设备，对于 i386 和 Alpha 来说，顶层系统设备总线通常是指 PCI 和 ISA。内核将动态构建设备树，然后将这些设备与可用的驱动程序匹配。我还希望能够将这个功能与早先开发的内核链接器（KLD）结合起来，以允许系统在初始引导后添加驱动程序。我在 1997 年末为此开发了原型，但并没有进展到支持任何“真实”的硬件驱动程序的程度。

### 文件格式

在 1997 年，FreeBSD 仍在使用 a.out 文件格式，这是非常简单的 32 位格式。而当时大多数其他类 Unix 系统在使用 ELF 格式，它提供了更多的灵活性，并且已经支持包括 Alpha 在内的 64 位平台。

改用新格式需要修改编译系统，以支持新格式，并支持动态链接，动态链接是通过运行时链接器（RTLD）在用户空间实现的。在 1998 年初的几个月里，包括 John Polstra、Jordan Hubbard、Peter Wemm 和我在内的许多人都在为此而努力。

### 启动

最初这是该项目的不确定领域。Alpha 的“预引导”环境非常分散，Digital Unix（DUX）和 VMS 使用 SRM 控制台，NT 使用 AlphaBIOS，Linux 则同时使用这两者以及它们自己的 MILO。

在 Alpha 上，任何操作系统都需要称为 PALcode 的东西来处理虚拟页转换、缓存、中断以及用户模式和内核模式之间的转换。DUX、NT 和 VMS 都有各自的 PALcode 变体。尽管 DUX 的 PALcode 可能是我们最好的选择，但我们并不清楚能否在没有昂贵许可证的情况下使用它。结果，我们支持的所有 Alpha 硬件都附带有支持 DUX PALcode 的 SRM 控制台，因此这一问题得以解决。

i386 引导启动代码的大小限制在 7.5 KB。这个限制来自 UFS 文件系统格式，它为引导程序保留了 8 KB 的区域，而我们需要其中的 512 字节的扇区来让 PC BIOS 引导。这只够将 a.out 格式的内核文件从根文件系统读入内存并启动它。

Alpha 也有类似的限制；SRM 控制台使用磁盘的第一个扇区来标识要加载的一系列连续扇区，这样与 i386 一样，UFS 引导区只剩下 7.5 KB 的空间。在 Alpha 上，由于 RISC 架构的代码密度较低以及 ELF 格式所需的额外复杂性，受限的 7.5 KB 空间可能不足以装下完整的引导程序。最终我们重新编写了引导程序，使得这 7.5 KB 的引导区能够加载更大的引导程序（在现代 FreeBSD 系统中称为 **/boot/loader**）。这给了我们足够的灵活性来完全支持在 i386 和 Alpha 上启动 ELF 格式的内核，以及其他新特性，如预加载内核模块、网络启动等等。Mike Smith 负责多阶段引导，Peter Wemm 实现了内核模块预加载。不知从什么时候开始，Forth 解释器被添加进来了——我想这是 Jordan Hubbard 的功劳。

### 用户空间

在我们开始这个项目时，FreeBSD 的源代码树并未针对便捷的交叉编译配置。这使得构建用户空间的实用程序略显挑战性。John Birrell 致力于在 NetBSD 主机上构建大部分 FreeBSD 源代码树，并成功运行了 NetBSD 内核系统，具有相当完整的 FreeBSD 用户空间。

NetBSD 的系统调用接口与 FreeBSD 略有不同，因此 John 早期的用户空间程序开发工作使用了 NetBSD 的 ABI。原生的 FreeBSD 内核需要使用 FreeBSD ABI 的实用程序，但这是重要的进展。待我们有了可用的内核，从混合的 NetBSD/FreeBSD 系统迁移到完整的原生 FreeBSD 系统就相对比较简单了。

### 内核

这可能是整个项目中最大的一部分，它涉及填充内核中的所有与机器相关的部分，为虚拟内存、中断处理、进程上下文切换等提供底层支持。

我通过构建 Alpha 交叉编译器，尝试构建内核，看哪些部分无法编译通过，然后填补这些空白，要么使用空的存根，要么从 NetBSD 中导入代码，因为那些代码与 FreeBSD 的代码非常相似。这是相当繁琐的过程，花了好几天时间，但最终却得到了无法运行的二进制内核文件。

接下来，花费更长时间的移植工作是尝试运行这个内核，看它运行到哪个阶段出现问题，然后解决问题后再进行下一次尝试。为了运行每个测试，我使用了名为 SimOS 的工具。它模拟了基于 Alpha 的计算机，并配有磁盘和串口等模拟硬件。SimOS 支持使用 gdb 调试模拟内核；这非常有帮助，因为我可以逐步执行非常早期的内核初始化过程，比如设置内核虚拟内存等，然后再进入与机器无关的初始化序列。

为了缩短移植过程，我在适当的地方使用了 NetBSD/alpha 的代码。不幸的是，在一些地方我忽略了 NetBSD 的版权信息。我不得不在提交代码后，在公开场合更正这些问题，这让我感到相当尴尬。这也是 FreeBSD 提交历史被修改的极少数情况之一——我们删除了版权信息不正确的修订。

在虚拟内存支持方面，使用 NetBSD 的代码是行不通的，因为 FreeBSD 在这方面有很大的不同。Alpha 的页表与 i386 类似，都是采用基于树的三层结构（而 i386 当时使用的是两层结构）。我复制了 i386 的代码并加以修改，以增加额外的层级。

对 Alpha 的初步支持于 1998 年 7 月提交，随后几个月里又添加了对 SimOS 仿真器和实际硬件的支持。FreeBSD 3.0 的发布说明中提到了这一点：“对 DEC Alpha 架构的移植已进入‘ALPHA’（haha）状态。”

## 移植 FreeBSD 到 IA-64 平台

这个项目始于 2000 年，当时 Paul Saab 在 Usenix ATC 年度技术会议上带来了一套 IA-64 的文档，并询问我是否有兴趣将 FreeBSD 移植到这个新平台。当时，雅虎是 i386 上 FreeBSD 的大规模用户，他们的一些工作负载正遇到 32 位平台的限制。

IA-64 架构在多个方面都非常有趣。其指令编码采用 128 位指令束，每个指令束最多包含三条可同时执行的 41 位指令。这使得它具有大型寄存器集，包括 128 个通用寄存器和 128 个浮点寄存器。

通用寄存器分为两组——32 个“静态”寄存器和 96 个“堆叠”寄存器。处理器会从大型寄存器池中分配这些寄存器，并在函数调用和返回时自动保存和恢复。每个寄存器是 64 位，外加用于推测执行的 NaT（“Not a Thing”）位。

条件执行通过 64 个谓词寄存器实现，每个寄存器都是一位，保存比较指令的结果。每条指令都可以根据谓词寄存器的值条件执行。

使用 8 个分支寄存器支持间接跳转（例如函数指针），这有助于分支预测。

虚拟内存管理由虚拟哈希页表（VHPT）控制，该表包含可能的虚拟地址到物理地址映射的子集。软件 TLB 缺失处理程序用于查找 VHPT 中不存在的映射。VHPT 支持两种格式，一种是“短”格式，可用于模拟传统的树形页表；另一种是“长”格式，即包含冲突链的简单哈希表。

### 启动

IA-64 硬件使用了 EFI 预引导环境。我在多阶段引导加载器中添加了对 EFI 的基本支持。在 IA-64 的 EFI 环境中，程序是可重定位的；这需要在 EFI 程序本身中处理，而这在调试时可能会很困难。

### 用户空间

移植 FreeBSD 用户空间工具和实用程序的工作相当简单，因为这个时候 FreeBSD 构建系统支持了交叉编译，只需要添加 IA-64 版本的底层库代码，比如字符串比较、内存复制和系统调用等。

### 内核

我们很幸运能够使用 HP IA-64 仿真器（SKI），Marcel Moolenaar 利用它完成了 FreeBSD 的移植。该移植包括指令级调试器，对于调试内核早期初始化和陷阱处理非常有帮助。

内核移植比 Alpha 稍微困难一些。这次，没有其他可供参考的 BSD 移植版本，所以所有的底层支持都是新代码。IA-64 架构需要两个堆栈，一个用于寄存器，另一个用于常规数据。由于额外的寄存器状态，以及推测执行和堆叠寄存器的复杂性，陷阱处理比大多数其他架构要困难得多。

长格式的 VHPT 最终成为 FreeBSD 的虚拟内存系统的合理选择。机器无关的虚拟内存系统通过向平台的 pmap 系统发出请求来建立虚拟地址到物理地址的映射。这些映射就是直接添加到 VHPT 中的。

### 32 位兼容性

在移植过程中，我们使用 Perforce 做源代码控制，当时只有 i386 平台的二进制版本可用。

我希望能在移植过程中在目标平台上使用这个二进制文件，因此我最终实现了 i386 兼容性，利用了 IA-64 处理器中内置的 i386 支持。这建立在早期 Linux 和 SVR4 仿真工作的基础上，这些工作明确地将系统调用的 ABI 和实现分开。

### 传统

Alpha 的移植工作促使了大量必要的配套开发，这些开发帮助形成了现代 FreeBSD 内核。从 a.out 到 ELF 格式的过渡是 Alpha 移植的必要步骤，但由于 ELF 迅速成为事实上的标准，所有平台上都不再使用 a.out，这避免了我们花费大量精力来支持和扩展过时的格式。事实证明，多阶段引导加载器是灵活的平台，它使新的架构移植变得更容易，并支持从现代文件系统（如 OpenZFS）引导。newbus 设备框架促进了驱动程序在不同架构之间的兼容性，并支持动态设备发现，这在设备可随时添加或移除的现代系统中是必需的。

增加对 Alpha 的支持迫使我们解决了内核和用户空间的 64 位兼容性问题。加载/存储架构还暴露了一些其他问题，例如假设对内存做读-改-写操作以设置标志或递增计数器的操作不会受到硬件中断的影响。为了解决这个问题，我们在内核中添加了一组“原子”操作。John Baldwin 扩展了原子框架，以支持 IA-64 的获取/释放语义，该框架广泛用于支持多 CPU 平台。

IA-64 移植的灵感源于一种需要：摆脱 32 位 i386 平台的限制，同时保留与传统软件的兼容性。虽然这些目标得以实现，但该平台本身并没有达到更简单的 i386 架构的性价比。IA-64 最终在大规模超级计算中找到了自己的定位，但它并不适合大多数 FreeBSD 工作负载，并被 AMD 对 i386 架构的 x86-64 扩展所取代，后者在现代计算环境中无处不在。

此后，FreeBSD 移除了对这两个平台的支持。Alpha 架构是 Digital 与 Compaq 合并后的牺牲品，尽管它继续作为产品提供，直至 2007 年。FreeBSD 于 2006 年移除了对 Alpha 平台的支持。对 IA-64 的支持持续时间稍长一些；Marcel Moolenaar 多年来改进了对这个平台的多处理器和 NUMA 变体的支持。2014 年，FreeBSD 移除了对 IA-64 平台的支持，而该平台于 2021 年停产。

***

**DOUG RABSON** 是软件工程师，拥有超过三十年的经验，历经上世纪 80 年代的 8 位文本冒险游戏到本世纪 20 年代的每秒 TB 级分布式日志聚合系统。自 1994 年以来，他一直是 FreeBSD 项目的成员和贡献者，目前正致力于改进 FreeBSD 对现代容器编排系统的支持，如 podman 和 kubernetes。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://freebsd-journal-cn.bsdcn.org/20230506-freebsd-san-shi-zhou-nian-ji-nian-te-kan/ports.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
