小時候拆解Roland ,Korg,Yamaha生產的電子合成器.發現PCB主控最右上Intel C8095-90 ,DAC是最左邊中間的PCM54, 看到幾個Roland字樣的IC
心裡覺得 挖靠 量這樣小好了不起 ,音樂公司自己Tape out 一堆特製ASIC.
後來才知道 以Roland MT-32為例子,原創 ASIC 為R15229896 LA32(LA合成IC ),其他長條的Roland IC只是Hitachi 27C256 EPROM

這幾年來x86性能舉崛起,沒想到我才發現這類音樂客製化合成算法IC很多早已不見.而是以X86模擬….
之前閱讀到這篇文章 因為實在太有意思了 因此先簡單翻譯一下.
翻譯與修改自:https://marcan.st/2016/06/hacking-and-upgrading-the-korg-kronos/
原作者:Marcan / Hector Martin
破解並升級 Korg Kronos
從 1.66GHz Atom 到 3.9GHz Skylake
Korg Kronos 是一台很有意思的怪物。它是 Korg 的旗艦合成器,市場上稱為 music workstation;但拆開來看,裡面其實是一張標準 x86 主機板,加上一些 Korg 自製的 I/O 硬體。而且它跑的是 Linux。
這種東西根本就是在邀請人去 hack,不是嗎? 🙂
我擁有這台機器好幾年了,之前曾經研究過它的軟體,但後來覺得無聊,沒有真的做出什麼有趣的事(說真的,原廠軟體本身已經夠複雜了)。最近我又決定重新看一遍,結果意外發現一個已經寫了一年的部落格:kronoshacker。
這位匿名 hacker 一直在記錄 Kronos 軟體裡各種細節,從安全機制、混淆手法到效能問題都有。他把 Kronos 原本老舊的 Atom 主機板與 CPU 換成當時很新的 Skylake CPU,大幅提升了機器效能。我也決定照著做一次,並把整個過程記錄下來。
Kronos 硬體概觀

原本的 Kronos 主機板
Kronos 基本上由兩個主要部分組成:x86 和 NKS4。x86 是一張相當標準的 Intel D510MO 主機板,搭載 1.66GHz 雙核心 Atom D510。它跑 Linux,所有合成與訊號處理都在這裡完成。不過這張主機板本身的 I/O 幾乎沒被使用,它真正的角色比較像整台機器的大腦。軟體、取樣音色和使用者資料存放在內部 SATA SSD,原廠容量是 30GB。主機板原本配 2GB RAM,官方可升級到 3GB。
由於 CPU 和軟體都是 32 位元,使用超過 4GB RAM 是不可能的。系統周邊會保留部分位址空間;不過裝 4GB RAM 時,合成器仍然能比 3GB 機器多認出幾百 MB 的記憶體。
大多數 I/O 都由 NKS4 處理。NKS4 是 Korg 自製的板子,核心是一顆 TI AM1806 處理器。它負責觸控螢幕(framebuffer 與輸入)、所有按鈕與 LED,以及音訊 I/O。它跑一個相當簡單的韌體,並透過 USB 連到 x86。這個韌體可以升級,但不是由 Kronos 內部自行升級;要升級時,必須在開機時按住 ENTER + PAUSE,讓它進入 bootloader 模式。
接著把 USB device port 接到 PC,執行 Korg 提供的工具來更新韌體(看起來是透過 USB MIDI 介面)。這設計有點奇怪,因為 Kronos 更新系統韌體時順便從內部更新 NKS4 似乎更合理;但也就這樣了。
琴鍵床另外透過 RS232 連到 x86。它由一顆獨立的微控制器控制。也許 Korg 決定用 RS232 是為了比透過 NKS4 再走 USB 傳琴鍵資料更低延遲。
軟體上的小把戲
Kronos 使用標準 BIOS,正常開機,用的是老派 GRUB,沒有 secure boot。不過 kernel 有點特別:它包含 realtime patches(RTAI)以及一些 Korg 自己修改的部分。
我就先跳過 Korg 違反 GPL 的抱怨了。簡單講,他們在不少地方都有這種問題:未公開的 kernel patches、把 GPL-only symbols 重新匯出成非 GPL 的 shim module、帶有 patch 卻沒有釋出 source 的 GPL userspace apps。Korg 確實有做一點形式上的努力,把 kernel source 和不完整的 patches 放在隨機器附的 DVD 裡,但這裡就不展開了。
Kernel 會開進一個精簡過的 Linux 發行版(看起來像 RedHat/Fedora 系,但留下的身份特徵很少),只有最基本的 userspace 和 init。接著做一些基本初始化,然後開始載入 kernel modules 與啟動 userspace daemons。Kronos runtime 主要有兩個元件:OA.ko 和 Eva。OA.ko 是主要的合成器 kernel module,實際的音訊處理都在 kernelspace 裡完成(使用 RTAI 提供的 realtime services)。
Eva 是使用者介面,跑在 userspace。UI 透過 NKS4 經 USB 匯出的 framebuffer 顯示,裝置是 /dev/fb1。
其他 userspace daemons 處理雜項,例如內建 FTP server 用的 vsftpd、mDNS 用的 avahi,以及處理網路介面的 ifplugd。還有一個很好笑的 fanctrld;就我看來,它除了 daemonize 後在迴圈裡跑 sleep(1) 以外什麼都沒做。我猜它前世可能真的是風扇控制 daemon,後來某個時候被換成 no-op 了……
合成器 modules、userspace 軟體和部分 samples 其實存放在加密的 filesystem images 裡,這是 Kronos 安全系統的一部分。這些 images 的 keys 存放在 /.pairFact3 檔案中,而且它們本身也被加密。解開後的 keys 是透過一顆 Atmel AT88SC0204CA CryptoMemory chip 做 key exchange 取得;這顆 chip 連在 NKS4 上,並由 NKS4 提供存取。
loadmod.ko kernel module 負責這件事,也包含其他一些混淆和安全檢查,尤其是它會做 filesystem integrity check;如果偵測到被竄改,就拒絕運作。
這些 filesystem images 其實只是標準 cryptoloop images,使用 AES-256-CBC 加密……雖然它們實際上是把 124-bit key 轉成 ASCII hexadecimal 形式,再當成 256-bit AES key 使用。沒錯,是 124-bit,不是 128-bit,因為有個 bug 會吃掉原本 128-bit keys 的最後一個 nybble。三個 images 和各自的 mount points 如下:
/korg/ro/Eva.img → /korg/Eva /korg/ro/Mod.img → /korg/Mod /korg/ro/WaveMotion.img → /korg/rw/PCM/WaveMotion
整套安全機制其實相當沒意義,因為取得 root shell 並不難(後面會看到),一旦有 root,就可以好好地叫 loadmod.ko 掛載那些加密 filesystem,然後把內容複製出來。取得 image encryption keys(每台 Kronos 都一樣)也很容易,有幾種方法可行(其中一種甚至可以塞進一則 tweet,而且還能塞兩次)。原文作者本來把這部分留給讀者當練習,只說 keys 是 31 字元的十六進位字串;但 kronoshacker 在這篇文章發布前後幾乎同時更新了他的 blog,公開了 keys 和其中一種取得方法,所以想看 keys 的話照那個連結即可。他用的是 kernel patch,但我有兩個更簡單的方法:一個是在 userspace 使用 LOOP_GET_STATUS64 ioctl(它會很樂意把 key 給你);另一個,如果你希望 key dumper 短到能塞進 tweet,在掛載 filesystem 後可以這樣做:
# busybox mknod /dev/mem c 1 1
# busybox strings /dev/mem | grep -E '^[0-9a-f]{31}$' | busybox tail -n 3
參考資料
kronoshacker 已經把不少主題寫得很好,如果你想看細節,應該去讀他的 blog:
- 安全晶片
- Kernel 小把戲與混淆
/sbin/loadoa- 韌體更新
- Filesystem integrity check
- 編譯新的 kernel
loadmod.ko(大多數安全/混淆內容都在這裡)
警告
如果你照著這些步驟對自己的 Kronos 動手,結果搞壞了,那是你的責任,不是我的。如果你沒有至少合理程度的 Linux 系統管理知識,不要照做。請先備份 SSD 內容。如果你的 Kronos 停止工作、著火、開始放 dubstep,或變成 Roland,我不負責。
這篇文章假設你至少知道 kernel 是什麼、如何使用標準 Linux command-line tools,以及 busybox 是什麼、如何用它替代缺少的系統工具。
開始:Root Kronos
kronoshacker 已經提供了一個現成的 update file,可以 root Kronos 並安裝 SSH daemon。不過他沒有解釋它怎麼運作,所以我來說明 :-)。
loadmod.ko 會執行 filesystem integrity check,其中一個效果是:如果你試圖修改 /etc/passwd 或 /etc/inittab 這類檔案,它會拒絕工作,阻止你用簡單方式改變開機流程。不過它只檢查既有、預期檔案清單的完整性;額外新增的檔案它不知道,也不在乎。它也不檢查 /boot 底下的任何東西,因為正常情況下那裡不會被 mount。
因此 kronoshacker 的方法很簡單:不要修改既有 init system,而是安裝一套平行的 init system。更新包會安裝 busybox,並把它 symlink 成 /bin/init(相對於原本的 /sbin/init)。Busybox 被 patch 成使用 /etc/inittab.busybox,而不是 /etc/inittab。由於 GRUB config 不會被檢查,所以可以讓 kernel 改用 init=/bin/init,啟動這套平行 init system。原始檔案沒有被動到,loadmod.ko 也就不會察覺。
SSH daemon 是 dropbear,並且被 patch 成使用 Kronos 的 FTP credentials 登入,而不是 /etc/passwd。這樣你就能很方便地從 UI 修改 root SSH 密碼。
拔掉 Kronos 安全機制的牙齒:loadmod.ko 與加密
loadmod.ko 的主要工作是進行 crypto exchange、計算加密 loopback images 的 keys,然後把它們 mount 起來。因此,只要把內容複製出來,我們理論上就能擺脫它,避開 filesystem integrity check,並加快開機。
但是,移除 loadmod.ko 會讓 OA.ko 的音訊合成壞掉。這同樣是混淆的一部分。loadmod.ko 會把一個 magic value pokes 到某個 magic kernel state(這是未公開 Korg kernel patches 的一部分),而 OA.ko 會檢查它。如果 OA.ko 找不到正確值,就會故意讓 audio synth 失能。
這件事是用一個有點好笑的方式做的:module 內含兩個 hardcoded 128-bit SSE float constants,{1., 1., 1., 1.} 和 {-1., -1., -1., -1.},名字也很直接,叫 allPlusOne 和 allMinusOne(沒錯,他們發佈 module 時還保留完整 symbols……)。這些常數在整個 synth 裡到處被使用。如果 magic value 檢查失敗,這些值會分別被覆寫成 {0.7, 0.7, 0.7, 0.7} 和 {-0.2, -0.2, -0.2, -0.2},不用說,這會讓音訊發生非常糟糕的事。
kronoshacker 已經處理好這個問題:他建了一個自訂 kernel,把 magic value 放在正確位置,讓 OA.ko 滿意。他預先編譯好的 kernel、patchset 和解釋可以在 這裡 找到。
安裝這個 kernel 很簡單:mount /boot、把 kernel copy 進去,然後更新 GRUB config(/boot/grub/grub.conf)讓它使用新 kernel:
default=0
timeout=3
title kronos-bzImage-20160505 + busybox
root (hd0,0)
kernel /kronos-bzImage-20160505 root=/dev/sda2 max_loop=16 fbcon=map:0 memmap=384m vga=0x0303 loglevel=0 fastboot Single raid=noautodetect elevator=noop init=/bin/init
title Linux (2.6.32.11)-320m STG 8x6 + busybox
root (hd0,0)
kernel /bzImage root=/dev/sda2 max_loop=16 fbcon=map:0 memmap=384m vga=0x0303 loglevel=0 fastboot Single raid=noautodetect elevator=noop init=/bin/init
title Linux (2.6.32.11)-320m STG 8x6
root (hd0,0)
kernel /bzImage root=/dev/sda2 max_loop=16 fbcon=map:0 memmap=384m vga=0x0303 loglevel=8 fastboot Single raid=noautodetect elevator=noop
做完後,我們就可以安全地移除 loadmod.ko。要做到這點,先把加密 images 內容複製出來。從已開機的 Kronos 執行:
# mount /korg/Mod # mount -o remount,rw /korg/ro # cp -vr /korg/Mod /korg/Eva /korg/ro/ # cp -vr /korg/rw/PCM/WaveMotion /korg/rw/PCM/WaveMotion2
/korg/ro 預設可用空間不足,所以我決定把 WaveMotion 資料放到 /korg/rw,就在原本 mount 位置旁邊。
注意,/korg/Eva 只能 mount 一次(loadmod.ko 第一次 mount 後會清掉它的 encryption key),但其他 images 可以反覆 mount / unmount。相反地,/korg/Mod 在載入它包含的 kernel modules 後會被 unmount,但可以自由重新 mount;而 /korg/Eva 必須維持 mount,userspace 軟體才能運作。這讓人不禁懷疑 Korg 原本是不是想把單次 mount 保護加在 /korg/Mod 上,結果弄錯了……
接下來,我們要替換 /sbin/loadoa。它其實只是一個試圖偽裝成 shell script 的 C 程式(真的,它裡面滿是對 shell pipelines 的 popen 呼叫之類的東西……)。我們要把它換成真正的 shell script,並修改成不載入 loadmod.ko,而是把複製出來的資料 bind mount 到最後的 mount points。我寫了一個 /sbin/loadoa.sh 來做這件事。
把它 copy 進 Kronos,確認執行 chmod 775 /sbin/loadoa.sh,然後編輯 /etc/OA.clonos.rc(這是 kronoshacker root pack 安裝的 /etc/OA.rc 替代版本),讓它呼叫 /sbin/loadoa.sh,而不是 /sbin/loadoa。
計畫
麻煩的部分處理完後,是時候規劃硬體升級了。kronoshacker 記錄了 他的硬體選擇,我用它作為基準。我的配置如下:
CPU 和 kronoshacker 使用的是同一顆(他也為它準備了現成的 CostProfile,讓 OA.ko 知道 CPU 效能,以便正確執行 voice stealing)。kronoshacker 用的 Supermicro 主機板很貴,而且在我住的地方不好買,所以我選了一張較常見的消費級 ASUS 主機板。
ASUS 主機板有 serial port header(其實大多數現代主機板仍然有,只是沒有實體 D-SUB9 接頭),所以 Kronos 的 serial port cable 應該可以直接接上(kronoshacker 則必須把它焊到 D-SUB 上)。
我在 serial port 這點上小賭了一把。照 kronoshacker 的說法,OA.ko 會直接和 serial hardware(Super I/O chip)溝通,而且期待特定種類的 chip,並不只是標準 16550 registers。kronoshacker 已經 確認相容 Winbond W83627(原始 Kronos)、Nuvoton NCT6627UD(Kronos 2),以及 Nuvoton NCT6776(他的替換主機板)。我的主機板也有 Nuvoton chip,但型號不同(NCT5539D)。我不確定它是否能開箱即用,還是得 patch OA.ko 支援。幸好,它真的直接能用。
8GB RAM 有點過度;32-bit OS 正常幾乎用不到超過 3GB。不過我想試試看 PAE kernel 或其他高記憶體實驗(把 Kronos 虛擬化,有人要嗎?),而且現在買少於 8GB RAM 感覺也有點蠢。
拆開它
這時我才發現 Kronos 的 PSU 沒有 ATX12V 接頭。不過 PSU 端的接頭上有一個未接線的 pin,剛好是另一條 12V 線,所以我決定拿它來用。我從舊 PSU 上拆了一個 ATX12V 接頭與線,又從更老的 PSU 的 AUX 接頭拆出需要的 pin,剛好能塞進 Kronos 的接頭。
沒有未接線的 ground pin,而我也不想太大幅度破壞原本線路,所以我決定用 screw terminal,把 ground 接到 PSU 的其中一顆螺絲上(那些螺絲是接地的)。

關於 ATX12V 的小岔路。
我也決定加上外接 HDMI 和 Ethernet,這樣蓋上外殼時仍然能控制開機過程。Kronos 正常是用外接 USB Ethernet adapter 作為選配網路,但內部 port(通常沒有接出來)其實也能用,而且會從網路取得 DHCP。
原始 kernel 可以在新硬體上開機。不過那個 kernel 沒有可用的 USB xHCI 支援,所以永遠無法在新硬體上跑 synth。即便如此,如果使用 PS/2 keyboard,它仍然會給你一個可用 terminal。kronoshacker 的 kernel 包含 backport 的 xHCI driver,運作得很好。ASUS 主機板的 Ethernet chip 和原本 Atom 主機板一樣屬於類似 r8169 的等級,所以不需要額外 modules 就能正常運作。
有一個 BIOS 設定很重要:CPU C states 高於 C2 的狀態必須關閉,否則載入 RTAI modules 時會隨機 hang 住。我也順手把 BIOS 升到最新版。
換上新硬體後,Kronos 的效能比原本強好幾倍,幾乎任何可能的組合都會在 CPU 滿載前先碰到 200 voices 的硬上限。
不過有一個令人失望的地方:換新硬體後,Kronos 可用的 sample memory 反而比舊硬體稍微少一點。這是怎麼回事?
TOLUD,還是不要 TOLUD
為什麼新硬體會進一步限制記憶體?在 32-bit 系統上,kernel 只能使用 4GB 以下可見的記憶體。但系統周邊和其他特殊位址範圍也必須映射到 4GB 以下。為了做到這點,BIOS 會從 address space 頂端切走一塊 memory,然後依 BIOS 設定停用它或把它 remap 到 4GB 以上,再用相對應的空間給周邊與其他系統範圍使用。
這由 chipset config 裡的 TOLUD register 控制,而這個問題也因此得名。
我的主機板預設把 TOLUD 設成 3 GiB,在一些 ACPI overhead 之後,最大可用 RAM address 是 2974 MiB:
BIOS-provided physical RAM map: BIOS-e820: 0000000000000000 - 000000000009c800 (usable) BIOS-e820: 000000000009c800 - 00000000000a0000 (reserved) BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved) BIOS-e820: 0000000000100000 - 00000000b9ea6000 (usable) BIOS-e820: 00000000b9ea6000 - 00000000b9ea7000 (ACPI NVS) [...] BIOS-e820: 00000000bffff000 - 00000000c0000000 (usable)
不幸的是,雖然 kronoshacker 的主機板有可由使用者設定的 TOLUD 選項,ASUS 這張沒有,而且它的預設設定比原本主機板保留了更多記憶體。這看起來像是需要 BIOS patch 才能修,但幸好有更簡單的方法。要看清楚發生什麼事,我們得檢查 BIOS image。
UEFI BIOS 相當複雜,包含許多 modules 和巢狀資料結構。像 UEFITool 這類工具,可以讓處理 UEFI BIOS images 容易許多。
把 ASUS BIOS 載入 UEFITool 後,我們可以抽出 Setup module 的 PEI image。UEFI setup screens 使用一種類似 bytecode 的語言描述。幸好也有工具可以自動反編譯;我使用的是 Universal IFR Extractor。
在反編譯出的 Setup IFR 裡搜尋 TOLUD,可以發現設定其實存在:
Suppress If: {0A 82}
True {46 02}
Setting: Max TOLUD, Variable: 0x483 {05 91 FC 04 FD 04 20 06 01 00 83 04 10 10 00 0B 00}
Default: 8 Bit, Value: 0x0 {5B 06 00 00 00 00}
Option: Dynamic, Value: 0x0 {09 07 FE 04 00 00 00}
Option: 1 GB, Value: 0x1 {09 07 09 05 00 00 01}
Option: 1.25 GB, Value: 0x2 {09 07 08 05 00 00 02}
Option: 1.5 GB, Value: 0x3 {09 07 07 05 00 00 03}
Option: 1.75 GB, Value: 0x4 {09 07 06 05 00 00 04}
Option: 2 GB, Value: 0x5 {09 07 05 05 00 00 05}
Option: 2.25 GB, Value: 0x6 {09 07 04 05 00 00 06}
Option: 2.5 GB, Value: 0x7 {09 07 03 05 00 00 07}
Option: 2.75 GB, Value: 0x8 {09 07 02 05 00 00 08}
Option: 3 GB, Value: 0x9 {09 07 01 05 00 00 09}
Option: 3.25 GB, Value: 0xA {09 07 00 05 00 00 0A}
Option: 3.5 GB, Value: 0xB {09 07 FF 04 00 00 0B}
End of Options {29 02}
但它被永久 suppress(也就是隱藏)了:Suppress If: True。真可惜。
雖然我們不能從 Setup 畫面存取這個選項來改 TOLUD 設定,但這個選項與對應的 config variable 確實存在。因此,我們可以不透過 Setup Utility,而是手動修改這個選項的值。這些值存放在 Setup UEFI variable 裡:
Default Store: 0x1 {5C 06 02 00 01 00}
Var Store: 0x1[3597] (Setup) {24 1C 43 D6 87 EC A4 EB B5 4B A1 E5 3F 3E 36 B2 0D A9 01 00 0D 0E 53 65 74 75 70 00}
修改 UEFI variables 的簡單方法之一,是以 UEFI 模式開進支援 UEFI 的 Linux kernel,然後使用 efivarfs。我用的是可靠的 SystemRescueCD USB stick(它支援 UEFI 模式)。開進相容 kernel 後,就可以看到 Setup variable:
% cd /sys/firmware/efi/efivars % ls -al Setup-* -rw-r--r-- 1 root root 3601 May 31 21:16 Setup-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9
稍微手動做 sanity check 後可以發現,IFR data 裡指定的 variable offsets 並不直接對應 variable 裡的 byte offsets,而是從 offset 4 開始算。因此我們要改的 variable 位於 offset 0x483 + 4。我們想寫入的值是 0xb(3.5 GiB):
% cp Setup-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9 ~/Setup % cd % cp Setup Setup.bak % echo -ne '\x0b' | dd of=Setup bs=1 seek=$((0x483 + 4)) conv=notrunc % cat Setup >/sys/firmware/efi/efivars/Setup-ec87d643-eba4-4bb5-a1e5-3f3e36b20da9
重新開回 Kronos 系統後,確認修正有效:
BIOS-provided physical RAM map: BIOS-e820: 0000000000000000 - 000000000009c800 (usable) BIOS-e820: 000000000009c800 - 00000000000a0000 (reserved) BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved) BIOS-e820: 0000000000100000 - 00000000d0d53000 (usable) BIOS-e820: 00000000d0d53000 - 00000000d0d54000 (ACPI NVS) [...] BIOS-e820: 00000000d7fff000 - 00000000d8000000 (usable)
這實際上是 3.375 GiB 的 TOLUD 設定,不是 3.5 GiB,但總比沒有好。這應該讓 Kronos 有 3341 MiB 記憶體。然而……可用記憶體只稍微增加,沒有預期多。事實上,我們原本預期大約會有 2957 MiB 可用 heap(3341 MiB – 384 MiB),但 OA.ko 回報大約只拿到 2619 MiB:
5b000000..feb53000 is ioremapped memory, 2746560512 bytes, physicalBankStart @ 0x18800000
OA.ko 記憶體管理
要理解如何最佳化可用的合成器記憶體,我們必須看看 Kronos 如何做記憶體管理。Kronos 並沒有使用標準 Linux kernel 機制管理大部分 RAM,而是設定成開機時只啟用低位址的 384 MiB RAM(透過 memmap=384M kernel command line option 指定)。接著 OA.ko 取得剩餘可用記憶體,把它映射成 kernel space 裡一大塊 physically contiguous block,並自行管理。
為了做到這點,kernel 使用相當非標準的 1G/3G user/kernel memory split 建置。也就是說,在 32-bit 系統可用的 4 GiB address space 裡,前 1 GiB 配給 userspace,後 3 GiB 配給 kernelspace。這讓它可以把接近 3GiB 的 synth heap 映射進 kernelspace。
我完全不知道他們為什麼要這樣做。通常只有不支援 scatter-gather DMA 的笨硬體才需要 physically-contiguous memory,但在這個情況下,記憶體實際上只由 x86 自己使用。使用 physically contiguous memory 相比標準 paged memory 幾乎沒有好處(反正映射時還是要經過 page tables)。
我唯一的猜測是,OA synth 是其他 Korg synth 產品的後代;那些產品可能直接跑在沒有 MMU 的 bare metal 上,設計時就是直接處理 physical memory,而 Korg 只是把這套架構延續到 Kronos 上。
不管原因是什麼,結果就是 kernel virtual address space 橫跨 0x40000000–0xffffffff 這 3 GiB 範圍。不過 kernel 預設會把所有可用 RAM 映射在自己 address space 的底部。由於 Kronos 告訴 kernel 使用 384MiB RAM,所以留下 3 GiB – 384 MiB = 2688 MiB 的可用 kernel address space 來映射 synthesizer heap。這就是我們記憶體限制的來源。
Kernel 不需要隨時映射所有 physical memory。事實上,在更典型的 3G/1G split(kernel 只有 1GiB)且 RAM 超過 1GiB 的系統上,這根本不可能。因此 kernel 支援「high memory」概念,也就是不直接映射的記憶體。可以用 vmalloc command line option 指示 kernel 為 high memory mappings 保留更多空間。讓我們只給 kernel 64 MiB 的 directly mapped physical memory。這代表 3072 MiB – 64 MiB = 3008 MiB 的 vmalloc space。再扣掉 16 MiB overhead,剩下 2992 MiB。
把 vmalloc=2992M 加進 kernel command line 會得到:
320MB HIGHMEM available. 63MB LOWMEM available. [...] found a suitable region at 0x18800000 of size 0xb8553000 (3092590592). Our size request is 0xcd000000 (3439329280) sPhysicalBankStart = 0x18800000, physMemSize = 0xb7b53000 (3082104832), sIORemapBase = 0x47000000 47000000..feb53000 is ioremapped memory, 3082104832 bytes, physicalBankStart @ 0x18800000
3072 MiB kernel virtual address space 裡有 2939 MiB 被用於 synthesizer heap,UI 現在回報可用 user sample memory 高達 2277 MiB(扣掉其他 synth memory 使用量之後)。

2277 MiB。數給你看。
這已經是我們能做到的極限。把 kernel direct map 再往下降也許可行,但收益會遞減(最多再多 64 MiB),而且我們已經碰到 BIOS 提供的 3.26 GiB low memory 了(別忘了 userspace 會吃掉其中 384 MiB)。
若想再多拿到高達 512 MiB 的 sample memory,需要一個使用非標準 0.5G/3.5G virtual address space split 的自訂 kernel,啟用 PAE 讓 userspace 使用 high memory,並對 OA.ko 做幾個 patches 才能處理。但需要的 patch 是否可行還不確定,因為 PAE 很可能造成 binary module compatibility problems。
還有幾個小改進可以做(現在 kernel 在重疊的 PCI address space 裡浪費了 8 MiB RAM),但不值得花力氣修。就目前而言,這已經差不多是極限了。
剩下待處理的事
升級後的 Kronos 運作得很好,但我還有幾個問題想看看。
至少有一個 Combi 有問題:如果 polyphony 爬太高,I-A010 Phantasies 會讓整台 Kronos crash(正常彈奏時就很容易發生,不需要亂按一堆鍵)。使用原始 CostProfile data 時不會發生,因為它會限制 polyphony 避免 CPU 過載。我的猜測是這是一個 synth bug,只是原始硬體上永遠不會觸發,因為 voice stealer 會更早介入並丟掉 voices。
至少恢復 CostProfile 檔案可以繞過這個問題(雖然會把實際 synth performance 限制回原始硬體等級),而且切換這個檔案很容易,所以沒有什麼重大損失。
我懷疑新硬體上的 CPU pinning 可能是錯的。舊 CPU 和新 CPU 都是雙核心、支援 hyperthreading 的 CPU,但 CPU 順序不同:Atom 上 CPU 0,1 和 2,3 是 thread pairs;Skylake 上則是 0,2 和 1,3。這可能代表單一核心最後會在兩個 threads 上同時承擔 synth 和 effects load。話說回來,新 CPU 強大很多,也許這根本不重要……
到目前為止,我一直使用 kronoshacker 預先編譯好的 kernel,因為它看起來運作良好。不過等有時間時,我打算自己編譯,並在 GitHub 上放出自己的 patchset branch。
原文發布時間:2016-06-01 06:40










