Atari Jaguar 主机 Linux 内核移植技术挑战全记录
一、硬件背景:Atari Jaguar 的特殊架构
Atari Jaguar 于 1993 年发售,被官方宣传为全球首款 64 位家用游戏主机。它采用了一套高度异构的多处理器架构:
- 主控 CPU:Motorola 68000,运行于 13.3 MHz,负责控制流与系统调度。
- Tom 芯片:26.6 MHz 定制 DSP/光栅化器,处理图形绘制与位块传输。
- Jerry 芯片:26.6 MHz 定制 DSP,负责音效合成。
- 内存配置:基础版 2MB RAM,后期型号扩展至 4MB。
- 存储介质:卡带(ROM Cartridge),无硬盘、无光驱。
关键事实:虽然 Atari 宣称 Jaguar 是 64 位系统,但从硬件实现角度看,其本质是 32 位架构搭配 64 位指令扩展。这一"营销话术与工程现实"的落差,为后续 Linux 移植埋下了第一颗雷。
这套架构的特殊性意味着:移植 Linux 并不能简单套用现成的 m68k 移植版本,而需要从引导、内存管理、设备驱动三个维度重新设计整个内核层。
二、挑战一:无 BIOS 环境下的冷启动
2.1 启动链断点
标准的 m68k Linux 移植依赖于目标平台提供的基础固件(BIOS/bootloader)来完成硬件初始化。Atari Jaguar 完全没有 BIOS,上电或复位后:
- 68000 从地址
0x000000处开始执行。 - 头 64 字节是 Cartridge Header,由 Atari 规定的游戏入口协议。
- 之后紧跟的是加载代码,负责初始化 Tom/Jerry。
这意味着 Linux 移植必须自实现整个启动链,从卡带引导开始,直到调度第一个 shell 或 init 进程。
2.2 解决方案:两级引导
我们最终采用如下启动模型:
| 阶段 | 组件 | 职责 |
|---|---|---|
| Stage 1 | jag_boot.S (汇编) | 初始化 68000 栈指针、清空 BSS、关闭所有中断 |
| Stage 2 | jag_loader.c (C) | 初始化 Tom/Jerry、设置内存映射、解压内核 |
| Stage 3 | Linux 内核 | 接管硬件,执行 start_kernel() |
Stage 1 的关键代码片段:
.section .text.boot
.globl _start
_start:
move.w #0x2700, %sr /* 关闭所有中断 */
move.l #_stack_top, %sp /* 栈指针初始化 */
clr.l %a0
clr.l %a1
/* 清零 BSS */
lea _bss_start, %a0
lea _bss_end, %a1
_bss_loop:
cmpl %a0, %a1
beq _bss_done
clr.l (%a0)+
jra _bss_loop
_bss_done:
jbsr _init_platform /* 跳转到 C 初始化 */
jbsr _start_kernel /* 移交内核入口 */三、挑战二:内存管理子系统适配
3.1 MMU 缺席问题
68000 提供的是最简单的内存管理能力(无硬件分页 MMU,仅支持 32 位地址空间)。而标准 Linux 内核强制依赖虚拟内存与分页机制。
两条可行路径:
- 路径 A:使用
MMU=n的 noMMU 配置(CONFIG_MMU=n),编译uClinux风格的内核。 - 路径 B:使用 68040/68060 的软件页表(但 Jaguar 不搭载该系列处理器)。
技术决策:由于 Jaguar 的内存仅 2~4MB,我们最终选择路径 A(noMMU)。这意味着所有进程共享单一地址空间,内核必须采用不同的内存隔离策略。
3.2 内存映射表格
Atari Jaguar 的物理地址布局如下:
| 地址区间 | 用途 |
|---|---|
0x00000000 - 0x00080000 | Cartridge ROM |
0x00400000 - 0x00800000 | 系统 RAM |
0x01800000 - 0x01FFFFF0 | I/O 寄存器(Tom/Jerry) |
0x1F000000 - 0x1FFFFFFF | 扩展 RAM(部分型号) |
在 arch/m68k/mm/jaguar.c 中,必须手工注册这些区域到 memblock 子系统,否则 page_alloc 会因为找不到内存而无法启动。
四、挑战三:多处理器协同的真相
4.1 并不存在的 SMP
68000 是单核 CPU,Tom 与 Jerry 虽然是独立芯片,但它们并非通用 CPU,而是专用 DSP 加速器,不能在 Linux 调度器控制下运行任意代码。
因此,本次移植不实现真正的 SMP,而是:
- 68000:运行 Linux 主调度循环,负责进程切换、系统调用、内存管理。
- Tom:通过专用驱动 API 接管,提供帧缓冲(FrameBuffer)。
- Jerry:通过 ALSA 兼容层或独立 OSS 驱动接管,提供音频输出。
4.2 DSP 芯片如何被"驱动"
Tom 与 Jerry 不能加载 ELF 二进制,但可以接受微码上传(microcode upload)。我们实现了两种方式:
/* 向 Tom 提交一段 GPU 微码 */
int tom_upload_dsp(struct jag_dsp_ctx *ctx, void *code, size_t len) {
if (!ctx || !code || len == 0) return -EINVAL;
/* 写入入口地址寄存器 */
out_be32(TOM_DSP_PC, ctx->entry_pc);
/* 通过 DMA 通道传输微码 */
jag_dma_xfer(ctx->dma_chan, code, len, TOM_DSP_MEM);
return jag_dsp_start(ctx->entry_pc);
}Jerry 的音频微码支持 PCM 回放、波形合成与 ADPCM 解码,通过 ALSA 子系统的 snd_jaguar.c 驱动暴露为标准 PCM 设备。
五、挑战四:外设驱动移植
5.1 显示子系统
Tom 芯片提供完整的 CRT 时序与光栅化能力,支持 RGB565、CLUT 等多种模式。移植 fbdev 驱动时需要:
- 配置屏幕时序(默认 NTSC 60Hz,可选 PAL 50Hz)
- 设置 CRTC 寄存器
- 提供帧缓冲 mmap 支持
static struct fb_var_screeninfo jag_fb_var = {
.xres = 320,
.yres = 240,
.bits_per_pixel = 16,
.red = {11, 5, 0},
.green = {5, 6, 0},
.blue = {0, 5, 0},
.activate = FB_ACTIVATE_NXTOPEN,
};5.2 输入子系统
Atari Jaguar 手柄包含 15 个按键 + 1 个方向键,我们将其映射为标准 Linux 输入设备:
| 物理按键 | Linux 按键码 |
|---|---|
| A/B/C | BTN_A, BTN_B, BTN_C |
| Pause | KEY_PAUSE |
| Option | KEY_ESC |
| 0–9 | KEY_0 ~ KEY_9 |
驱动通过轮询方式读取手柄状态寄存器(PAD_BASE),无需中断(因 Jaguar 中断控制器对低优先级外设支持有限)。
5.3 存储设备
卡带 ROM 在 Linux 中被实现为只读的 MTD 设备,支持挂载 SquashFS 或 romfs,以节省内存。
六、挑战五:交叉编译工具链构建
由于 Jaguar 平台没有原生 Linux 环境,所有编译工作必须在 x86 工作站上完成:
- binutils:打补丁以支持
m68k-jaguar-linux-uclinux目标。 - GCC:配置
--target=m68k-jaguar-linux-uclibc --disable-nls --enable-soft-float。 - uClibc:作为轻量级 C 库,替代标准 glibc(后者太大无法放入 2MB RAM)。
完整的工具链构建脚本约 200 行 shell 代码,整个编译过程耗时约 15 分钟。
七、挑战六:调试的艺术
7.1 远程串口调试
由于 Jaguar 没有原生调试接口,我们使用 SKUNKBOARD(一个经过改造的开源调试卡带)将 UART 数据回传到主机:
void jag_dprintk(const char *fmt, ...) {
va_list args;
char buf[256];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
/* 通过串口发送 */
while (*buf) {
while (!(SERIAL_STATUS & TX_READY));
SERIAL_DATA = *buf++;
}
}7.2 软仿真环境
为缩短调试周期,我们使用 Virtual Jaguar(开源模拟器)进行功能验证。模拟器无法提供真实时序,但能在 PC 上快速复现启动流程与内存错误。
7.3 常见崩溃模式
| 症状 | 根因 |
|---|---|
| 启动后黑屏 | Tom DSP 微码未正确加载 |
| 内核 printk 无输出 | 串口时钟未初始化 |
| 随机 Oops | 内存越界访问,缺乏 MMU 保护 |
| 卡死在 init | 卡带文件系统未挂载 |
八、实战经验总结
将 Linux 移植到 Atari Jaguar 这类非主流异构平台的过程中,有几条核心经验值得铭记:
- 文档缺失是常态。Jaguar 几乎没有官方数据手册,所有寄存器定义都来自逆向工程与开源社区。
- 分层验证至关重要。先在模拟器跑通,再上真机;先单核跑通,再考虑多核协作。
- 内存与性能瓶颈相互制约。2MB RAM 意味着内核裁剪不能敷衍,常用做法是关闭网络协议栈、多媒体支持、大部分文件系统。
- 不要假装它真的是 64 位系统。Tom 芯片虽能处理 64 位数据通路,但内存地址线依然受限于 68000 的 32 位寻址。
- 设备的可观察性比功能完整性更重要。能 printk 出来的 bug,都不可怕;真正危险的是默默写错内存的 bug。
九、展望:这台"失败主机"的新生
Atari Jaguar 在商业上是失败的,但在技术爱好者的眼中,它是一座被忽视的金矿。移植 Linux 到 Jaguar 不仅是技术挑战,更是一种对工程师逆向能力、架构理解深度与调试耐心的一次综合考验。
未来可继续探索的方向包括:
- Linux 6.x 内核对 noMMU 的改进是否能进一步压低运行时内存。
- Tom DSP 的 GPU 加速是否可以借助现代 Mesa/DRM 框架统一抽象。
- 网络协议栈的最小化接入,让 Jaguar 通过 UART 实现 SLIP/IP,接入现代互联网。
工程的意义不在于平台是否主流,而在于是否敢于正视那些藏在尘埃里的电信号,以及由它们所承载的、尚未完全苏醒的可能。