Linux for Macintosh 68K Port
为 Macintosh 68K 移植 Linux 存在几个障碍。首先是 Apple 不希望其机器上运行其他操作系统。虽然几乎所有 PC 的工作原理都可以从书中了解到,但关于 Apple Macintosh 的资料却几乎没有。有时 Macintosh 的规格和技术说明会填补空白;但在其他时候,则需要进行大量的猜测和实验来弄清楚硬件。
第二个障碍是人为的。大多数 Macintosh 机器并非销售给技术市场,而且普通 Macintosh 用户对为其计算机安装“真正的操作系统”并不十分感兴趣。然而,确实存在一个相当大的、技术导向的 Macintosh 用户社区,并且拥有大量的 Macintosh 硬件(可能比任何其他非 Intel Linux 平台都多)。Apple 也提供了另一个理由,他们对 68K 机器用户的古怪建议现在似乎是“购买一台新电脑”。
Linux 移植的第三个障碍不太明显,并且隐藏在缺乏文档之中。某些人推测,Apple Computer 发布如此少的文档的主要原因是尴尬。总的来说,Macintosh 平台的设计特点非常落后。例如,Macintosh II 上的中断控制器是一对 6522 VIA 芯片,旨在与 8 位 6502 处理器一起使用。糟糕的硬件设计会导致性能不佳,除非经过仔细处理。完全缺乏 DMA(直接内存访问)更是无济于事。Apple 似乎认为在大多数机器上没有 DMA 是一项功能,并且实际上有一份技术说明写道“我曾经是一个十几岁的 DMA 瘾君子”,这似乎是对他们相当滑稽的硬件设计的一种辩解。
要开始移植,首先需要的是硬件。我拥有了大部分硬件(一台 5MB MacII,从办公室淘汰下来的,因为它太慢而无法实际使用)。最初,我觉得帮助制定 Linux 移植的方向是安全的,因为该系统缺少 MMU(内存管理单元),因此无法运行任何提议的 Linux 移植。
Rob Pelkey 开始进行一些针对 Macintosh 的非常基础的 Linux 工作,但需要一个引导加载程序来加载 Linux 操作系统并启动它。在 LinuxNet IRC 网络上的 #linux 频道中,Jes Sorensen(Linux68K 的维护者)、我和其他几个人就移植以及需要什么进行了一些讨论。经过大量的挖掘,我们设法收集了一些关于 Macintosh68K 的基本信息,然后通过调查 OpenBSD/Macintosh 团队在让 BSD 在 Macintosh 机器上勉强运行方面所做的出色侦探工作,填补了进一步的领域。进一步的信息来自 Apple 赞助的 OSF Mach 上的 Linux 移植。我们发现 Apple 继续使用相同的 8 位微控制器或其仿真,并且没有为新处理器实质性地重新设计系统。
一切似乎都非常好。我有一台 Macintosh 盒子可以嘲笑(我们偶尔会用它来重现 Macintosh 用户在使用 CymruNet 时遇到的问题失败),我们可以讨论想法,而且我的 Macintosh 中没有 MMU,所以我不可能帮助编写任何代码。
到这时,Rob 的努力已经严重停滞,因为他没有时间编写运行 Linux 所需的引导加载程序,并且正在努力通过课程和其他杂项。不用担心——要么最终有人会接管这个项目,要么他会完成他的课程。然后 Frank Neuman 给我的 MacII 发送了一个 MMU,另一个人捐赠了一对以太网卡——哎呀,没有更多借口了。
在将 MMU 安装到 Macintosh 上而没有炸毁它之后,我尝试让 MacOS 在虚拟内存下运行。这应该很简单——点击内存工具并选择 32 位,虚拟内存开启。但是,我的内存控制中没有 32 位选项,更不用说虚拟内存选项了。我盯着看了一会儿,然后在楼下更现代的 Mac 上检查以确保我有正确的屏幕。另一台运行相同 MacOS 版本的 Macintosh 具有所需的选项;我的没有。
这是我第一次体验到 Mac 的恐怖之处。虽然 UNIX 会说“很抱歉,您不能这样做”,但 MacOS 只有两条错误消息。要么发出“eep?”的声音,要么设置框根本不存在,直到安装了其他 12 个未识别的项目并完成了三个明显不相关的对话框。我的是后一类错误。
Apple 在出厂时就为 MacII 配备了升级以包含 MMU 芯片的能力;因此,他们明智地出厂了系统 ROM,该 ROM 无法在启用 MMU 的情况下运行。太棒了——请不要设计任何关键任务的东西。幸运的是,Apple 在他们的网站上隐藏了一个小工具,它可以修补 ROM 入口点,使其可以在 32 位模式下运行。
好的,所以我所要做的就是下载该工具,安装它并完成——没那么简单。要获得该程序,我需要以太网工作。我最终使用 kermit 将 700KB 的以太网安装程序传输到 Macintosh 上。在与完全陌生的 Macintosh 归档工具搏斗了四个小时后,我让这台机器使用 Netatalk 与 Linux 盒子进行了 AppleTalk 共享,并且也洞悉了为什么 Macintosh 用户第一次遇到 PC 时看起来好像他们刚刚发现了外星生命。
一个小时后,我弄清楚了如何解压缩 Macbin 文件,并且 Macintosh 处于 32 位模式,并承认 MMU 存在且功能正常。
操作的下一阶段是弄清楚如何在 Macintosh 上启动 Linux 内核镜像。NetBSD 和 OpenBSD 使用一个引导加载程序,该程序将 a.out 格式的可执行文件加载到 Macintosh 的内存中,关闭 Macintosh,将其移动到地址 0 并跳转到它。我很快决定我不想编写引导加载程序。OpenBSD 加载程序几乎是纯粹的 MacOS 魔术,其水平远远超出我的能力。不用担心——很快就明显的是,OpenBSD 加载程序也可以被说服加载 Linux。真正的加载程序可以等待。
下一个问题是构建一个 Linux 内核镜像,该镜像可以链接并且(虽然最有可能什么都不做)至少可以作为 OpenBSD 引导程序的东西。Linux 是使用 GNU 工具链构建的,该工具链支持交叉编译器的构建。因此,可以在普通的基于 Intel 的 PC 上编译和构建 680x0 二进制文件。花了几个构建才能让 gcc 和 GNU binutils 生成几乎正确的代码。Linux a.out 可执行文件的两字节头与 OpenBSD 的不同,OpenBSD 引导加载程序会检查这些字节。与其再次重建整个工具链,不如我编写了一个简单的工具来修复头。
大多数 Linux/M68K 都很乐意为 Macintosh 目标构建。我用虚拟例程填补了所有抱怨的地方——用于 Mac 键盘、鼠标、显示器等,直到所有内容都编译完成。由于 Linux/M68K 内核中设计良好的抽象层,这非常容易做到。我现在有了一个完全无用的、什么都不做的 Macintosh 内核,OpenBSD 加载程序会加载它,然后正如我预期的那样,它会立即使机器崩溃。
Linux/M68K 项目在我参与之前就已经面临着在同一端口内支持多种类型的基于 680x0 的计算机的挑战。由于需要支持 Amiga 和 Atari 系统,因此存在清晰的抽象层。添加额外的 M68K 目标主要包括填写特定于平台的空白字段。移植到全新的处理器将比这次更具挑战性。
对于 Macintosh 情况,我填写了各种主要为空的函数处理程序。在最终使它链接后,我最终得到一个内核,该内核是为具有 FPU 和显示器在 0xF9000000 的 5MB 基于 68020 的 Macintosh 硬编码的。它没有中断控制器、没有磁盘控制器、没有键盘也没有鼠标。我可以找到的任何其他东西也被硬编码了。但是,它链接了,这才是重要的。在阅读了一些关于控制台驱动程序内部结构(以及对 Jes 的大量盘问)之后,我为 Macintosh 上的通用控制台驱动程序编写了一个相当简单的后端。事实证明,这种非常简单的方法反映了我拥有的 Macintosh 硬件,这是一个完全未加速的位图显示器,支持 640x480 4 位颜色。
Linux 68K 内核从一段部分共享的初始化代码开始,该代码是用 680x0 汇编程序编写的,并且几乎使用了该架构的所有最哥特式和最奇特的特性。此初始化代码还设置内存管理和缓存,并触及没有人通常知道的所有内容。Macintosh II 中使用的 68020、68851、68881 芯片组合已经过时,Motorola 没有提供关于此设备的文档。我确实知道两件事,理论上,这两件事足以调试并弄清楚发生了什么。首先,我知道屏幕内存的基地址;其次,我知道代码将开始执行的地址。我放在启动代码中的第一个例程将屏幕涂成令人作呕的蓝色。在启动大约 15 次并盯着源代码看了一会儿后,我得到了一台启动到蓝色屏幕的 Macintosh,等待了一会儿,然后崩溃了。
在许多方面,这是最难启动的项目。当处理一个完全未知的系统环境,并且不知道代码周围有什么时,调试非常棘手。真正的商业硬件人员使用逻辑分析仪——我没有这个选项。我在此过程中学到了几件事;值得注意的是,Macintosh 屏幕内存的位置不是硬件声称的位置,直到设置了 MMU。我还惊奇地发现,Macintosh 显示器上的圆角是用软件绘制的。
在接下来的几周里,Macintosh 经历了各种调试条纹和彩色图案,因为我每次在初始化汇编程序代码中一点一点地移动几行,一点一点地修复它,并逐渐映射到所需的硬件。最终,内核在没有崩溃的情况下命中了 C 代码中的神奇的 start_kernel 函数。
命中 start_kernel 是简单道路的开始;至少在 PC 上,现在存在文本模式控制台而不是条纹。因此,理论上,在 Macintosh 上命中 start_kernel 应该意味着让内核初始化文本控制台并开始显示有用的调试信息已经很接近了。事实远非如此。
在多次尝试启动控制台后,我编写了一些例程来在屏幕上打印企鹅和 Macintosh 徽标(这比文本更容易)。内核到达的每个重要点都会在显示器上添加一只企鹅,而在控制台启动之前失败点会打印给定数量的燃烧的 Macintosh 徽标。虽然远不如打印语句好,但这足以快速定位引导加载程序传递的选项处理中的几个错误(小事情,例如显然拥有 0KB 内存会扰乱 Linux 内存初始化)。代码会到达控制台设置的开头并死掉。
要通过这一点,我必须填写对 Apple Macintosh “Toby”显示卡使用的 4 位打包像素显示器的支持。用于 680x0 端口的通用位图控制台驱动程序支持各种像素格式,并且自然地排除了我需要的格式。
如果当时我知道,我可以简单地在显示首选项中将机器切换到单色模式,但我不知道该操作会在物理上将卡切换到单色模式。添加 4 位打包像素并不太困难。我将有点吓人的 2 位打包像素支持留到以后,希望其他人会编写它。控制台代码在 680x0 上也非常模块化,这些控制台层(abscon、fbcon)现在被大多数非 Intel 端口使用。有理由假设它将在 2.3 内核系列中驱动所有端口。
机器仍然神秘地崩溃,所有证据都指向一个结构被盖章。我在它的两侧放置了保护值,并检查了它们是否没有被覆盖;我在内存中移动了该结构;我尝试了我能想到的一切,以阻止它显然被损坏。(没有乐趣,没有变化。)在挠了挠头之后,我添加了代码以检查这些值在启动时和每个子系统初始化时是否正常。该值在 C 代码的开头是错误的;在汇编程序的开头也是错误的。
这看起来好像引导加载程序正在损坏数据,但这没有道理,因为加载程序会损坏相同的位置,而不是选择特定的变量,无论它可能位于何处。最终,我使用 GNU objdump 工具查看我正在加载的二进制文件。事实证明,GNU 链接器有缺陷,并且在某些地方为重定位加载了一个完全错误的地址。
一个新的链接器和神奇的词语“校准 Bogomips”出现在屏幕上,然后是挂起,然后是欢呼雀跃。在许多方面,浪费在链接器错误上的时间并没有那么糟糕。在代码中寻找神秘错误时,我修复了大约二十或三十个其他严重错误,徒劳地试图找到虚幻的真正错误。
我不太担心 Bogomip 校准挂起。在中断例程(特别是定时器中断例程)编写完成之前,很难校准时间。我注释掉了它,过了一会儿,其余代码启动到说“Panic:无法挂载根文件系统”的地步。一个合理的情况,因为它除了屏幕之外没有任何设备支持。
让机器达到所有内容似乎都启动的地步绝不意味着移植项目的最初步骤的完成。在这个阶段,您最终会意识到真正的问题和剩余工作量的规模。
要填补的最重要的项目是那些处理最基本的系统资源的项目:中断、内存和 I/O 总线。中断和几个 I/O 子系统由一对 6522 VIA 芯片处理,这是来自石器时代的 8 位控制器。这些芯片本身是有文档记录的,并且它们的位置是已知的,即使它们与 I/O 引脚的一些连接是一个谜。一定数量的映射工作和其他侦探信息表明,VIA 芯片提供了至关重要的系统定时器滴答声,以极低的(当时未破译的)级别处理键盘,并为来自总线控制器的外部中断提供了接口。
其他几个引脚似乎可以执行诸如关闭 Macintosh 之类的操作。即使现在,我们也不知道 VIA 芯片上的所有东西都做什么,也不知道所有引脚是否都有实际用途。事实证明,我得到了轻松的一端。后来的 Macintosh 机器用称为 RBV(基于 RAM 的视频)的设备替换了第二个 VIA,该设备在一个胶合逻辑中包含 VIA 芯片的错误仿真和各种其他组件。
Macintosh 上的基本中断处理相对干净。已经非常重视使需要快速响应的中断的优先级高于耗时的进程。这在 MacOS 下运行良好,但 Linux 往往对中断采取过于二元的观点,尤其是在驱动程序中。某些中断以奇怪的方式连接,大概是为了节省组件;例如,SCSI 中断通过 VIA 连接,但与其他中断源相比,实际上是颠倒的。Apple 通过使用 VIA 可以处理状态变化任一方向的事实作为中断信号,从而节省了一个反相器。
我最终得到了两个中断处理层,它们大部分是硬编码的。与 PC 不同,Macintosh 中断是硬连线的。只有 Nubus(插件)卡更改位置,并且它们都共享一个中断,该中断在 VIA 寄存器中设置位以指示真正的中断源。
事实证明 Nubus 非常有趣。文档很薄弱,并且是从为 Macintosh 构建卡的人的角度编写的。大约花了一周时间,启动代码才会扫描并报告哪些 Nubus 插槽被占用以及设备的名称列表。一旦它工作了,Nubus 就被证明是一个设计非常精良的系统,具有许多类似于 PCI 的功能。每个插槽都分配了一组内存资源,并且可以引发中断。ROM 允许操作系统读取每个设备以获取识别和驱动程序信息。ROM 还包含其他“有用的”数据,包括设备的图标。目前,这些在 Linux 下不可见,但目的是在未来的某个时候支持 /proc/nubus/[slot]/icon.xpm。
我收到的 Daynaport 卡与几种 PC 设计非常相似。其上的 8390 以太网芯片和 RAM 块清楚地表明了这一点。但是,在每个 Nubus 插槽空间内,芯片和内存可能有 224 个可能的位置。
找出设备隐藏在哪里需要构建一系列内核,这些内核搜索 24 位地址空间以查找两件事。首先,它查找可以读取和写入的内存区域;其次,它查找具有附加属性的区域,这些区域在读回时会给出不同的结果。8390 芯片有几个控制寄存器;通过玩弄这些,可以可靠地识别芯片。(相同的代码用于探测 PC 的 Linux 中的 NE2000 和 WD80x3 卡。)在 Macintosh 上,RAM 很容易找到,但 8390 没有显示出来。
在玩弄了一点 RAM 行为之后,我发现内存被映射到其地址空间中的备用 16 位。也就是说,如果您想读取它,您必须读取两个字节,跳过两个字节,读取两个字节,等等。进一步的实验表明,以太网控制器寄存器每四个字节出现一次,RAM 每隔一对字节出现一次,并且是 16 位宽,并且以太网控制器将 16 位宽的内存视为 8 位宽。
这种技术适用于映射大量设备和地址空间,并有助于发现 Apple I/O 空间中其他设备的位置。我们仍然不够了解以驱动 Apple 声卡和“集成 Woz 机器”(软盘控制器),但我们确实知道它们位于何处。
当您需要通过启动到用户空间来开始测试系统时,您需要一个文件系统。NFS 根文件系统对此非常有吸引力,并且已用于大多数端口。NFS(网络文件系统)在文件级别而不是磁盘块级别发出事务请求。这样做的好处是,新端口中的错误会导致事务被拒绝。如果您同时尝试调试新端口和 SCSI 控制器驱动程序,那么您将花费大量时间重新格式化和重新安装您尝试从中启动的磁盘。使用 NFS 限制了出错的可能性,并且使您在尝试使机器工作时更容易添加和编辑文件。
最初的安装是使用一组 tar 文件完成的,这些文件被称为 M68K 的“watchtower”。Watchtower 非常过时,但体积小且易于解压缩。由于目标是获得 shell 提示符,因此二进制文件的年代久远并不是一个严重的问题。Watchtower 还展示了 Linux/M68K 的另一个优势——所有端口都运行相同的二进制文件。我不必为 Macintosh 交叉编译和调试所有二进制文件,而是解压缩和启动为 Commodore Amiga 上安装设置的文件系统。
通过对驱动程序进行一些修改并对内核代码进行一些小的错误修复,应用程序开始运行。由于我们需要为新的 M68K 平台添加的大部分代码都是驱动程序和设置代码,因此一旦事情开始工作,大多数应用程序就会焕发生机。花了一些调整才使浮点始终表现良好,但一旦完成,我就能够完全多用户启动机器,但没有键盘、鼠标或硬盘支持。
几乎过了一个月,才有人在自己的机器上启动内核。大量的调试消除了代码清理中“逃脱”的一些相当糟糕的假设。渐渐地,其他 MacLinux 68K 机器开始出现。对于任何项目来说,这都是极其重要的一步,因为它允许其他人有效地做出贡献。Michael Schmitz 编写了 SCSI 驱动程序以及大部分键盘和鼠标支持。他现在正在添加 IDE。许多其他人已经在许多种类的 Macintosh 上测试和调试了代码,甚至使其在某些 Macintosh 上工作。
虽然任何新端口都很困难,但 Linux M68K 内核树的结构设计得非常好,并且实现了其允许 M68K 目标之间轻松移植的意图。此代码的几个部分理所当然地被跨架构和跨平台使用。
使免费软件端口工作似乎在于少数愿意将项目完成一半的人。一旦达到这一点,即使它是像 Macintosh II 上的 Linux 这样毫无意义的东西,该项目也会自行聚集动力。
缺乏文档只是一个障碍。它不会阻止决心坚定的人行使其使用和操作他们购买和现在拥有的财产的基本权利。相反,它会给试图成为滋扰的供应商带来负面影响。如果关于键盘接口的唯一文档标题是“太空外星人吃了我的鼠标”,仍然会有人找到它。
始终成为第二个移植到未记录平台的操作系统。OpenBSD/Macintosh 团队所做的杰出工作对 Linux 项目有巨大的帮助。我也很高兴地说,即使世界上一半的人可能将时间花在 Usenet 倡导团体上争论不休,但 Linux 和 BSD Macintosh 团队之间的关系始终是相互合作的关系。我们共同推进我们对 Macintosh 平台的侦探工作和知识,以造福所有被 Apple 抛弃和遗弃的 Macintosh 用户。
