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,上电或复位后:

  1. 68000 从地址 0x000000 处开始执行。
  2. 头 64 字节是 Cartridge Header,由 Atari 规定的游戏入口协议。
  3. 之后紧跟的是加载代码,负责初始化 Tom/Jerry。

这意味着 Linux 移植必须自实现整个启动链,从卡带引导开始,直到调度第一个 shell 或 init 进程。

2.2 解决方案:两级引导

我们最终采用如下启动模型:

阶段组件职责
Stage 1jag_boot.S (汇编)初始化 68000 栈指针、清空 BSS、关闭所有中断
Stage 2jag_loader.c (C)初始化 Tom/Jerry、设置内存映射、解压内核
Stage 3Linux 内核接管硬件,执行 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 - 0x00080000Cartridge ROM
0x00400000 - 0x00800000系统 RAM
0x01800000 - 0x01FFFFF0I/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/CBTN_A, BTN_B, BTN_C
PauseKEY_PAUSE
OptionKEY_ESC
0–9KEY_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 这类非主流异构平台的过程中,有几条核心经验值得铭记:

  1. 文档缺失是常态。Jaguar 几乎没有官方数据手册,所有寄存器定义都来自逆向工程与开源社区。
  2. 分层验证至关重要。先在模拟器跑通,再上真机;先单核跑通,再考虑多核协作。
  3. 内存与性能瓶颈相互制约。2MB RAM 意味着内核裁剪不能敷衍,常用做法是关闭网络协议栈、多媒体支持、大部分文件系统。
  4. 不要假装它真的是 64 位系统。Tom 芯片虽能处理 64 位数据通路,但内存地址线依然受限于 68000 的 32 位寻址。
  5. 设备的可观察性比功能完整性更重要。能 printk 出来的 bug,都不可怕;真正危险的是默默写错内存的 bug。

九、展望:这台"失败主机"的新生

Atari Jaguar 在商业上是失败的,但在技术爱好者的眼中,它是一座被忽视的金矿。移植 Linux 到 Jaguar 不仅是技术挑战,更是一种对工程师逆向能力、架构理解深度与调试耐心的一次综合考验。

未来可继续探索的方向包括:

  • Linux 6.x 内核对 noMMU 的改进是否能进一步压低运行时内存。
  • Tom DSP 的 GPU 加速是否可以借助现代 Mesa/DRM 框架统一抽象。
  • 网络协议栈的最小化接入,让 Jaguar 通过 UART 实现 SLIP/IP,接入现代互联网。
工程的意义不在于平台是否主流,而在于是否敢于正视那些藏在尘埃里的电信号,以及由它们所承载的、尚未完全苏醒的可能。