在 PyCon 2015,Intel 工程師 Josh Triplett 展示了驚人成果:將 CPython 2.7 移植到 GRUB 開機載入器,使 Python 能在**完全沒有作業系統**的裸機環境(BIOS/EFI)直接運行。這不僅是技術展示,更為韌體測試、硬體驗證、嵌入式開發開啟全新範式。本文深度解析移植架構、核心挑戰、硬體存取 API 與現場 Demo 細節。
| 挑戰 | 原因 | 解決方案 |
| 無作業系統系統呼叫 | CPython 依賴 open/read/write/stat/mmap 等 POSIX API | 實作最小檔案描述符表、簡易 VFS (Virtual File System) 掛載到 GRUB 裝置 |
| 無標準 C 庫 | printf/sprintf/qsort/ungetc/浮點運算等缺失 | 手寫 pyconfig.h 禁用多數功能;移植 FDLIBM 軟體浮點庫;自製 qsort/ungetc/printf %%-fix |
| 啟動速度極慢 | GRUB 無磁碟快取,每次 import 觸發大量 stat()/read() 直存磁碟 | 預編譯 .pyc 字節碼;支援 zipimport 將整個 stdlib 打包單一 ZIP 避免 stat |
| 無終端機/Readline | GNU Readline 需 POSIX terminal | 用純 Python 實作 readline 替代品,掛載到 PyOS_ReadlineFunctionPointer |
| 無動態載入器 | GRUB 無 dlopen,無法載入 .so 擴充模組 | 將所有擴充模組靜態連結進 GRUB 核心 |
關鍵架構:Python 在 GRUB 的啟動流程
# GRUB 核心僅需呼叫一個函數即進入 REPL
PyRun_InteractiveLoop(stdin, ""); // 處理完整 REPL:解析、執行、行編輯、歷史
啟動前須完成:
- 建立
pyconfig.h(手工定義 HAVE_* 巨集,多為「無」)。
- 實作
_PyOS_Readline (Python 版 readline)。
- 註冊 GRUB 裝置到 Python VFS:
(hd0), (cd), (python) (記憶體虛擬檔案系統)。
- 靜態連結所有 C 擴充模組(_socket, _ssl, ctypes, _ctypes_test 等)。
硬體存取 API:bits、acpi、efi、cpu 模組
專案新增 C 擴充模組,透過 ctypes 暴露給 Python:
1. bits.cpuid - CPU 識別與特性
>>> import bits
>>> from ctypes import *
>>> c = bits.cpuid(0, 0) # CPU 0, leaf 0
>>> c
cpuid_result(eax=0x13, ebx=0x756e6547, ecx=0x6c65746e, edx=0x49656e69)
>>> buf = (c_uint32*3)(c.ebx, c.edx, c.ecx)
>>> (c_char*12).from_buffer(buf).value
b'GenuineIntel'
2. bits.msrd - 模型特定暫存器 (MSR) 讀寫
>>> msr = bits.msrd(0x1A0) # IA32_MISC_ENABLE
>>> print(hex(msr.eax), hex(msr.edx))
3. bits.io / bits.mem - I/O 埠與記憶體映射 I/O
>>> bits.outb(0xCF8, 0x80000000) # PCI 配置空間位址埠
>>> val = bits.inl(0xCFC) # 讀取資料埠
>>> # 記憶體映射 I/O
>>> mmio = bits.memmap(0xFED00000, 4096) # 對應 APIC 基址
>>> mmio[0:4] = b'\x00\x00\x00\x00'
4. acpi - ACPI 表格解析與方法執行
>>> import acpi
>>> print(acpi.dump('_HID')) # 列出所有裝置硬體 ID
# 輸出: PNP0C01, PNP0A08, INT3400, ...
>>> # 呼叫 ACPI 方法
>>> result = acpi.eval('_SB.PCI0.LPCB.EC0._Q10', [0x10])
5. efi - EFI 協議存取 (GOP 圖形、檔案系統、變數)
>>> import efi
>>> conout = efi.system_table.ConOut.contents
>>> conout.ClearScreen(conout)
>>> conout.OutputString(conout, 'Hello from Python on EFI!\r\n')
# 寫入 EFI 檔案系統 (GRUB 唯讀,EFI 可寫)
>>> fs = efi.simple_file_system_protocol
>>> fh = fs.OpenVolume(fs).Open(fh, "test.txt", efi.EFI_FILE_MODE_WRITE, 0)
>>> fh.Write(fh, len(b"data"), b"data")
# 圖形輸出協議 (GOP) 直接寫入 framebuffer
>>> gop = efi.graphics_output_protocol
>>> mode = gop.Mode
>>> fb = mode.FrameBufferBase
>>> # 繪製 Mandelbrot 集合 (現場 Demo)
現場 Demo 重現:EFI GOP 繪製 Mandelbrot
# 僅 8 行 Python,直接操作 framebuffer
import efi
gop = efi.graphics_output_protocol
mode = gop.Mode
fb = (c_uint32 * (mode.Info.HorizontalResolution * mode.Info.VerticalResolution)).from_address(mode.FrameBufferBase)
for y in range(mode.Info.VerticalResolution):
for x in range(mode.Info.HorizontalResolution):
# 簡化 Mandelbrot 計算 (軟體浮點,約 15 秒完成 400x400)
c = complex((x-200)/100.0, (y-200)/100.0)
z = 0
for i in range(50):
z = z*z + c
if z.real*z.real + z.imag*z.imag > 4: break
fb[y*mode.Info.HorizontalResolution + x] = i * 0x010101 # 灰階
效能與限制
- 啟動時間:從 GRUB 選單到 REPL 約 3-5 秒 (含 .pyc 載入);未優化前超過 30 秒。
- 浮點運算:純軟體模擬 (FDLIBM),無硬體加速,Mandelbrot 400x400 需 15 秒。
- 記憶體限制:GRUB 預設 32-bit 環境,可用記憶體 < 1GB;64-bit EFI 可突破。
- 無多執行緒/中斷:單執行緒輪詢,無法處理即時硬體中斷 (可加 hooks 改善)。
- 無網路堆疊:後續規劃移植 EFI TCP + Python socket 模組,實現網路 REPL / HTTP 服務。
意義與未來展望
- 韌體測試自動化:工程師可撰寫 Python 腳本批次驗證 ACPI 表格、PCIe 連結速率、CPU P-state 轉換,取代手動 C 程式編譯燒錄。
- 嵌入式開發新範式:在無 OS 的微控制器/SoC 上直接運行 Python 進行硬體帶測、校準、診斷。
- 安全研究:在 Ring 0 / SMM 等級直接操作硬體,分析 CPU 微代碼、SMRAM、Intel ME/AMD PSP 行為。
- 教育價值:讓學生在裸機理解「作業系統提供了什麼服務」(檔案系統、記憶體管理、驅動模型、中斷處理)。
相關資源
這項工作證明了 Python **不需作業系統即可成為系統級程式語言**,透過極少量平台適配層,即可在韌體層獲得完整 Python 生態系能力,為硬體驗證、嵌入式開發、安全研究帶來革命性工具鏈。
- 上一篇文章: 使用 HTTP 安全標頭防禦 Web 攻擊:X-XSS-Protection 實戰指南
- 下一篇文章: Python 函數屬性實現「全域變數」的優雅模式
Copyright ©
程式碼之家 All Rights Reserved