Atari Jaguar 主机 Linux 内核移植技术挑战全记录

引言

Atari Jaguar 是 1993 年由 Atari 公司推出的第五代家用游戏机,搭载独特的 Tom/Jerry 双芯片架构与 Motorola 68000 主控 CPU。虽然生命周期短暂,但其异构多处理器设计至今仍是嵌入式与复古硬件爱好者研究的热门对象。将 Linux 内核移植到 Jaguar 这种无 MMU、内存极小、I/O 完全定制的平台,涉及从引导加载到设备驱动的全栈重构,本文将系统梳理整个移植过程中遇到的典型技术挑战与解决方案。


一、硬件架构概览

理解 Jaguar 的硬件结构是移植工作的前提。其核心由两块 ASIC 芯片构成:

  • Tom 芯片:集成主 68000 CPU(26.591 MHz)、Blitter、色彩 DAC 以及视频时序逻辑
  • Jerry 芯片:集成 GPU、DSP(内置另一个 68000 核心)以及 I/O 控制器

整套系统的关键参数如下:

项目参数
主 CPUMotorola 68000 @ 26.591 MHz
DSP 内核68000 @ 26.591 MHz
GPU64-bit 专用图形处理器
内存2 MB(基础)/ 4 MB(扩展)
存储介质cartridge(只读)
MMU
⚠️ 注意:Jaguar 没有内存管理单元,这意味着 Linux 必须运行在 m68k nommu 子架构下,无法启用常规分页与进程隔离。

二、移植动因与技术目标

将 Linux 移植到 Jaguar 并非出于实用性考虑,而是一次极致的工程验证:

  1. 验证 m68k nommu 内核在极端受限环境下的可裁剪性
  2. 打通异构芯片驱动框架,为后续 DSP/GPU 子系统调度积累经验
  3. 建立 Skunkboard 等开发板的统一引导接口
  4. 沉淀逆向工程方法论,覆盖 ROM 分析、芯片手册还原、寄存器级调试

目标产物是一个能在真实 Jaguar 硬件或 Skunkboard 上启动 Shell 的最小化系统镜像。


三、Bootloader 启动链设计

3.1 Cartridge 引导格式

Jaguar 上电后,Tom 芯片从 cartridge ROM 的 0x800000 地址开始执行 68000 代码。ROM 前 4 字节必须包含合法指令,否则硬件直接进入死锁状态。

3.2 多级加载策略

受限于 cartridge 体积与 RAM 容量,采用 三级引导链

Stage1 (Cartridge, 4KB)   →  初始化 Tom/Jerry 寄存器
Stage2 (加载到 RAM, 64KB) →  设置栈、初始化 SDRAM 控制器
Stage3 (Linux uImage)     →  通过串口或 Skunkboard 加载内核

Stage1 通过修改 JOYSTICKTIMER 寄存器,使 Jerry 进入托管模式;Stage2 完成 UART 初始化后即可接收内核镜像;Stage3 跳转到 start_kernel 入口。

3.3 关键陷阱

  • 68000 在 Jaguar 上的中断向量表基址为 0x000000,与 m68k 标准 Linux 不一致,需要在 head.S 中重映射。
  • cartridge ROM 起始地址的内存属性被硬件标记为只读,需在 MMU 关闭情况下手动刷新 M68k 缓存(CPUSH 指令)。

四、内存管理挑战

4.1 无 MMU 下的内存模型

Linux m68k nommu 使用平坦内存模型,所有地址直接对应物理 RAM。Jaguar 仅有 2–4 MB,需要严格控制内核镜像尺寸:

  • 关闭所有可加载模块机制
  • 裁剪网络协议栈,仅保留 AF_UNIX
  • 使用 tiny C 库替代 glibc

4.2 共享内存协调

Tom 与 Jerry 通过 64-bit 共享总线访问同一块 RAM,这导致:

  • GPU 操作显存时 68000 不能同时访问,否则会产生数据冲突
  • 必须实现 jaguar_memory_barrier(),在 GPU 命令队列前后插入同步原语
  • 借鉴 m68k 的 m68k_set_mr() 例程,使用 TOM_CTRL 寄存器的 BUS arbitration 位

4.3 DMA 一致性

由于无硬件缓存一致性保障,所有 DMA 缓冲区必须经过 dma_map_single() 显式刷写。Jaguar 的 Blitter 寄存器窗口位于 0xF00000,写入顺序必须遵循:

writel(data, BLITTER_SRC);
writel(addr, BLITTER_DST);
writel(BLITTER_GO, BLITTER_CTRL);

五、Tom/Jerry 芯片驱动开发

5.1 Jerry GPU 子系统

Jerry 的 GPU 是 64-bit 专用处理器,通过对象列表(Object List)机制渲染多边形。Linux 移植中并未实现完整 3D 驱动,但完成了 framebuffer 子系统

static struct fb_ops jaguar_fb_ops = {
    .owner        = THIS_MODULE,
    .fb_setcolreg = jaguar_setcolreg,
    .fb_pan_display = jaguar_pan_display,
    .fb_fillrect  = cfb_fillrect,
    .fb_copyarea  = cfb_copyarea,
    .fb_imageblit = cfb_imageblit,
};

关键点在于调色板寄存器的初始化序列:必须在 PIXEL_FORMAT 设置后至少等待 3 个水平扫描周期,否则颜色失真。

5.2 DSP 子系统

Jerry 内部 DSP 是一个 68000 软核,可执行用户上传的微码。移植中实现了 dsp_load_firmware() 接口,允许将 DSP 程序作为内核模块动态加载,通信通过双口 RAM(0xF1B0000xF1BFFF)完成。

5.3 Blitter 与输入设备

Blitter 寄存器在 Linux 中被抽象为 miscdev,通过 ioctl(BLIT_GO, &blit_desc) 接口暴露给用户态。游戏手柄读取则通过轮询 JOYSTICK 寄存器的 bit0–bit9 实现,使用 input_polled_dev 子系统上报按键事件。


六、中断与 I/O 子系统

6.1 中断控制器

Jaguar 没有标准的 68000 autovector,中断源全部来自 Tom/Jerry 寄存器:

  • IRQ 1:VBlank
  • IRQ 2:DSP 中断
  • IRQ 3:GPU 渲染完成
  • IRQ 4:外部 UART

移植中重写了 arch/m68k/kernel/ints.c,将中断向量表改为查询式(polled)分发,并通过 request_irq() 暴露给驱动层。

6.2 时钟源

Tom 内部的 16-bit 计时器作为系统 tick,时钟频率由 PIT 预分频器决定。内核采用 100 Hz 配置,需要在 time_init() 中显式声明:

void __init time_init(void)
{
    jaguar_timer_setup(100);
    m68k_set_irq_type(JAGUAR_IRQ_TIMER, IRQ_FAST);
}

6.3 串口控制台

通过 Skunkboard 提供的 FTDI 兼容通道实现 115200 bps 串口,使用 early_printk 机制在控制台初始化前输出调试信息。


七、工具链与构建系统

7.1 交叉编译环境

  • binutils:2.32,支持 m68k-linux-elf 目标
  • GCC:7.5.0,启用 --enable-multilib--with-cpu=68000
  • Linux 内核:4.14.y m68k nommu 分支
  • uClibc-ng:1.0.33,静态链接 BusyBox

7.2 关键编译参数

ARCH := m68k
CROSS_COMPILE := m68k-linux-elf-
KBUILD_CFLAGS += -mcpu=68000 -mno-align-int
KBUILD_AFLAGS += -mcpu=68000

7.3 镜像生成流程

make bin2cart 工具将 ELF 内核转换为 Jaguar cartridge ROM 格式,注入 Stage1 引导头并写入 cartridge 头部校验和。


八、性能瓶颈与优化

在 26 MHz 的 68000 上运行 Linux,本身就是对内核裁剪水平的极限测试。优化重点包括:

  1. 进程调度:使用 SLAB 预分配模型,避免运行时碎片
  2. 中断合并:VBlank 中断作为主要时间基准,减少 DSP 中断频率
  3. 内存池:所有 DMA 缓冲区来自预分配 64 KB 池,避免 kmalloc 抖动
  4. 指令缓存优化:将 IRQ 入口放入 .text.aligned 段,避免 68000 缓存行失效

实测在 4 MB Jaguar 上,Shell 启动耗时约 7 秒,连续 ls / 调用响应时间约 220 ms。


九、调试与仿真环境

9.1 仿真器验证

主要使用 Virtual Jaguar 进行功能验证,通过 GDB stub 接入 m68k 寄存器上下文。

9.2 真实硬件调试

借助 Skunkboard 开发板(基于 Jaguar 兼容硬件 + FPGA),使用 BDM 接口连接硬件调试器,实时观察:

  • 68000 寄存器状态
  • Tom/Jerry 内部寄存器
  • 总线事务时序

9.3 典型故障排查

  • 黑屏但 UART 输出正常:检查 GPU 对象列表基址寄存器 OLP 是否被 bootloader 覆盖
  • 内核 panic "No init":核对 rd.dyn 段是否被正确搬移到 SDRAM
  • 中断丢失:确认 Tom 芯片 IRQ_MASK 寄存器未被意外清除

十、经验总结与展望

本次移植工作的核心收获可以归纳为三点:

  1. m68k nommu 内核在严格内存预算下仍可提供完整 POSIX 子集,但需要规避一切动态分配
  2. 异构多处理器调度必须从引导阶段就开始规划,硬件仲裁逻辑无法通过软件完全替代
  3. 逆向工程方法论比代码本身更宝贵:芯片手册还原、ROM dump 分析、寄存器枚举构成了整个移植的基石

未来工作将集中在:

  • 集成 ALSA DSP 音频后端,利用 Jerry DSP 实现低延迟音频
  • 实现 GPU 2D 加速子系统,基于 DRM/KMS 暴露给用户态
  • 探索 RISC-V 软核替代 68000 的可能性,验证完全开放的硬件现代化路径

Atari Jaguar 的 Linux 移植并非为了实际部署,而是对 Linux 内核可移植性与复古硬件极限的一次双向验证。它证明了在 1993 年的游戏机硬件上,Linux 内核仍然能够以最小化的形态存活——这本身就是对 Linus 最初那句 "Just for fun" 的最佳致敬。