'我花150元買了一臺龍芯電腦'

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

我花150元買了一臺龍芯電腦

關於這個Bug

在接受指點之後,我意識到這個Bug和I/O DMA一致性有關。

從胡偉武老師的文章《我們的龍芯三號》中,我找到了關於這個Bug的詳細描述:

這個問題是從3B1000到3B1500改版過程中引進的,為了提高性能,處理器核收到多個維護Cache一致性的無效請求時,原來每兩拍才能處理一個,改成可以連續處理,導致清除LL/SC同步指令的同步位llbit時錯了一拍,誤把IO DMA引起的Cache無效請求當作0號處理器核的Cache無效請求(IO DMA的編號剛好為0,與0號處理器核區分不開),通過軟件調整可以規避此問題。經過批量測試,原不穩定現象消失。

龍芯架構下,DMA的一致性是可配置的,也就是說可以從Cached地址空間進行DMA操作,硬件維護DMA一致性;也可以不由硬件控制一致性而從Uncached地址空間操作。

出現問題的環節是I/O DMA的一致性維持,所以我們可以採取不讓硬件維持I/O DMA,走Uncached DMA來在犧牲一些I/O DMA性能的前提下繞過這個Bug。

Lemote在這些主板出貨的時候選擇犧牲兩個Node的0號核的方法來保證I/O DMA性能順便降低一些功耗(沒錯,3B1500是個NUMA架構的處理器,全片八個核心分成兩個Node,每四個核心組成一個SMP Group)。

另外,即使不解決DMA的問題,雖然穩定性不高,但是對於日常折騰也已足矣。更何況我們部分人拿到的3B1500G已經解決了這個Bug,但是還是被關核,實在是不甘心。

SMP/NUMA啟動流程

要找到開核方案,明白除了Bootloader初始運行的那顆核心之外剩餘的核心是如何被Bootloader初始化,控制權如何被移交給內核,是個很重要的過程。

根據我對龍芯多核架構的理解,多核啟動的主要流程是,每個核心被複位的時候,其PC共同指向NMI Exception的入口,也就是0xbfc00000,Kseg1被map到Boot SPI Flash的頭部的位置。之後由Bootloader初始化所有核心的私有緩存,TLB等組件。

然後軟件通過讀取CP0 Ebase的0-10bit CPUNum位判斷自己的核編號,如果自己是Bootloader將要下一步使用的Bootcore,就繼續運行Bootloader核心代碼,如果自己是從核,那麼就跳轉到一段循環代碼,等待Bootcore的控制權移交給內核之後,內核通過向各個核心的MailBox寄存器發送這個核心將要跑的代碼的PC地址來喚醒這個核心。

內核側的實現我大致讀了讀,覺得和開核關係不大,就不在這裡過多的做闡述了。

讓我們來看看PMON下具體的代碼實現,這些代碼均摘錄自龍芯開源的PMON Target bonito.3c780e的start.S。

 .set mips64
mfc0 t0, $15, 1 /* 取CP0 Ebase */
.set mips3
andi t0, t0, 0x3ff /* 取出t0中的CPUNum */
dli a0, 0x9800000000000000 /*取NUMA Base*/
andi t1, t0, 0x3
dsll t2, t1, 18
or a0, t2, a0 /* 256KB offset for the each core */
andi t2, t0, 0xc /* node id */
dsll t2, 42
or a0, t2, a0 /* get the L2 cache address */
dsll t1, t1, 8
or t1, t2, t1
dli t2, NODE0_CORE0_BUF0
or t1, t2, t1
/* 上面主要在判斷自己是哪個NUMA Node */
dli a0, BOOTCORE_ID /* 把BOOTCORE ID加載進a0 */
bne t0, a0, slave_main /* 如果t0(當前核的CPUNum) != a0(BOOTCORE) 那麼跳轉到slave_main */
nop
bal initserial /* 在BOOTCORE上初始化串口 */
nop

slave_main的代碼就不貼了,也是做一些初始化的活,最後是一個等待內核發mailbox的loop。

修改內核此路不通

PMON的Start.S讀的我醉生夢死,大致從頭看到尾,並沒有什麼去關閉那兩個核的代碼,按照我的理解,這兩個核在啟動之後也在運行slave_main的代碼,只不過因為通過“龍芯EFI標準”傳過去的Reserved Core Mask中將那倆核Mask掉了,所以內核沒有去初始化他們,那麼直接在內核層面覆蓋掉傳參就好了嘛。事實證明我還是太Naive,改完內核一跑,屏幕上還是6只小企鵝,串口上的輸出也是那麼直白,CPU0 Failed to Boot, CPU4 Failed to Boot。

不甘心的我手動在內核的Head.S里加了一段彙編餵給CPU0一個Mailbox PC,指望能把它帶回來,然而它根本不鳥我一下。

看來這兩顆核心是真的被關掉了。

此路不通矣。

小插曲

聯繫到一位Lemote的員工願意幫忙編譯一份開核版本的崑崙固件,但是失敗了。。。。。不過還是非常感謝他。。。

Lemote方面也表示不是很方便開放他們的PMON私有代碼。

看來還是要自己動手豐衣足食。

逆向工程一臉懵逼

說起逆向工程,那必是會想到Hex-Rays的IDA工具。IDA有對MIPS架構提供支持,那是再好不過了(其實我本來想用開源的Radare2,但是當時手邊只有一臺Windows環境的電腦,於是心安理得的開始使用盜版軟件)。

手上的PMON是一個bin文件,不要說debug symbol和lable了,就連個Entrypoint都沒有。。好在Bootloader總是從頭開始跑,也沒什麼庫之類的東西。

省略一堆設置IDA的過程。

加載後:

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

我花150元買了一臺龍芯電腦

關於這個Bug

在接受指點之後,我意識到這個Bug和I/O DMA一致性有關。

從胡偉武老師的文章《我們的龍芯三號》中,我找到了關於這個Bug的詳細描述:

這個問題是從3B1000到3B1500改版過程中引進的,為了提高性能,處理器核收到多個維護Cache一致性的無效請求時,原來每兩拍才能處理一個,改成可以連續處理,導致清除LL/SC同步指令的同步位llbit時錯了一拍,誤把IO DMA引起的Cache無效請求當作0號處理器核的Cache無效請求(IO DMA的編號剛好為0,與0號處理器核區分不開),通過軟件調整可以規避此問題。經過批量測試,原不穩定現象消失。

龍芯架構下,DMA的一致性是可配置的,也就是說可以從Cached地址空間進行DMA操作,硬件維護DMA一致性;也可以不由硬件控制一致性而從Uncached地址空間操作。

出現問題的環節是I/O DMA的一致性維持,所以我們可以採取不讓硬件維持I/O DMA,走Uncached DMA來在犧牲一些I/O DMA性能的前提下繞過這個Bug。

Lemote在這些主板出貨的時候選擇犧牲兩個Node的0號核的方法來保證I/O DMA性能順便降低一些功耗(沒錯,3B1500是個NUMA架構的處理器,全片八個核心分成兩個Node,每四個核心組成一個SMP Group)。

另外,即使不解決DMA的問題,雖然穩定性不高,但是對於日常折騰也已足矣。更何況我們部分人拿到的3B1500G已經解決了這個Bug,但是還是被關核,實在是不甘心。

SMP/NUMA啟動流程

要找到開核方案,明白除了Bootloader初始運行的那顆核心之外剩餘的核心是如何被Bootloader初始化,控制權如何被移交給內核,是個很重要的過程。

根據我對龍芯多核架構的理解,多核啟動的主要流程是,每個核心被複位的時候,其PC共同指向NMI Exception的入口,也就是0xbfc00000,Kseg1被map到Boot SPI Flash的頭部的位置。之後由Bootloader初始化所有核心的私有緩存,TLB等組件。

然後軟件通過讀取CP0 Ebase的0-10bit CPUNum位判斷自己的核編號,如果自己是Bootloader將要下一步使用的Bootcore,就繼續運行Bootloader核心代碼,如果自己是從核,那麼就跳轉到一段循環代碼,等待Bootcore的控制權移交給內核之後,內核通過向各個核心的MailBox寄存器發送這個核心將要跑的代碼的PC地址來喚醒這個核心。

內核側的實現我大致讀了讀,覺得和開核關係不大,就不在這裡過多的做闡述了。

讓我們來看看PMON下具體的代碼實現,這些代碼均摘錄自龍芯開源的PMON Target bonito.3c780e的start.S。

 .set mips64
mfc0 t0, $15, 1 /* 取CP0 Ebase */
.set mips3
andi t0, t0, 0x3ff /* 取出t0中的CPUNum */
dli a0, 0x9800000000000000 /*取NUMA Base*/
andi t1, t0, 0x3
dsll t2, t1, 18
or a0, t2, a0 /* 256KB offset for the each core */
andi t2, t0, 0xc /* node id */
dsll t2, 42
or a0, t2, a0 /* get the L2 cache address */
dsll t1, t1, 8
or t1, t2, t1
dli t2, NODE0_CORE0_BUF0
or t1, t2, t1
/* 上面主要在判斷自己是哪個NUMA Node */
dli a0, BOOTCORE_ID /* 把BOOTCORE ID加載進a0 */
bne t0, a0, slave_main /* 如果t0(當前核的CPUNum) != a0(BOOTCORE) 那麼跳轉到slave_main */
nop
bal initserial /* 在BOOTCORE上初始化串口 */
nop

slave_main的代碼就不貼了,也是做一些初始化的活,最後是一個等待內核發mailbox的loop。

修改內核此路不通

PMON的Start.S讀的我醉生夢死,大致從頭看到尾,並沒有什麼去關閉那兩個核的代碼,按照我的理解,這兩個核在啟動之後也在運行slave_main的代碼,只不過因為通過“龍芯EFI標準”傳過去的Reserved Core Mask中將那倆核Mask掉了,所以內核沒有去初始化他們,那麼直接在內核層面覆蓋掉傳參就好了嘛。事實證明我還是太Naive,改完內核一跑,屏幕上還是6只小企鵝,串口上的輸出也是那麼直白,CPU0 Failed to Boot, CPU4 Failed to Boot。

不甘心的我手動在內核的Head.S里加了一段彙編餵給CPU0一個Mailbox PC,指望能把它帶回來,然而它根本不鳥我一下。

看來這兩顆核心是真的被關掉了。

此路不通矣。

小插曲

聯繫到一位Lemote的員工願意幫忙編譯一份開核版本的崑崙固件,但是失敗了。。。。。不過還是非常感謝他。。。

Lemote方面也表示不是很方便開放他們的PMON私有代碼。

看來還是要自己動手豐衣足食。

逆向工程一臉懵逼

說起逆向工程,那必是會想到Hex-Rays的IDA工具。IDA有對MIPS架構提供支持,那是再好不過了(其實我本來想用開源的Radare2,但是當時手邊只有一臺Windows環境的電腦,於是心安理得的開始使用盜版軟件)。

手上的PMON是一個bin文件,不要說debug symbol和lable了,就連個Entrypoint都沒有。。好在Bootloader總是從頭開始跑,也沒什麼庫之類的東西。

省略一堆設置IDA的過程。

加載後:

我花150元買了一臺龍芯電腦

截圖是後來補上的,剛載入進去肯定是沒有註釋,lable什麼的

撲面而來一堆彙編,我一臉懵逼,好吧,挑燈苦讀。窗外滿天星斗,一嘆星搖搖,長夜殊未央。

而且,不得不說反彙編得到的代碼和實際Start.S彙編差距很大,一是因為assembler進行了預處理,二是因為Lemote PMON的代碼基似乎和龍芯開源的版本也有一些差距。

總之,對照著開源版本的Start.S又是一番痛苦的分析。

同時發現了一個定位PMON代碼用途的小技巧,PMON源代碼裡的PIRINTSTR宏實際上是用stringserial函數配上往.data section寫入要打印字符實現的,也就說,只要找到stringserial的地址,就能找到在哪裡call的這個函數,以及傳給函數的.data section 地址/offset,進而得到PRINTSTR出來的內容。所以我得以標註出start.S中各個函數的入口點lable。

然而,重點檢查了判斷CPUNum的代碼和slave_main裡執行的代碼,並沒有發現更多和核心判斷相關的內容。

鬱悶至極,望向窗外,此時天已拂曉,第二天還有很多事情要幹,不得不鬱郁而眠。

又到了第二天晚上,繼續去鑽進這些代碼裡,再次檢查代碼,還是沒有線索。鬱悶至極,隨手翻開3B1500的用戶手冊尋找靈感。

一頁頁翻過去,突然看到 “2.6 芯片配置、採樣及 PLL 相關寄存器”,猛地一想,是不是單個核的時鐘被關閉了。。。仔細讀了讀這一張,果然,後面有提到 “芯片處理器核軟件分頻設置寄存器” ,這些寄存器可以控制單個核時鐘的使能與否。

他的物理地址是0x1fe001d0,換算到Start.S運行的kseg1裡就是0xbfe001d0。

全局搜索了一下這個地址,果然,在SHUT_SLAVE和WAKE_CORES兩個階段都被寫了一下,對照手冊看了看寫入的值,果然。CPU0和CPU4的時鐘都被直接關掉了。

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

我花150元買了一臺龍芯電腦

關於這個Bug

在接受指點之後,我意識到這個Bug和I/O DMA一致性有關。

從胡偉武老師的文章《我們的龍芯三號》中,我找到了關於這個Bug的詳細描述:

這個問題是從3B1000到3B1500改版過程中引進的,為了提高性能,處理器核收到多個維護Cache一致性的無效請求時,原來每兩拍才能處理一個,改成可以連續處理,導致清除LL/SC同步指令的同步位llbit時錯了一拍,誤把IO DMA引起的Cache無效請求當作0號處理器核的Cache無效請求(IO DMA的編號剛好為0,與0號處理器核區分不開),通過軟件調整可以規避此問題。經過批量測試,原不穩定現象消失。

龍芯架構下,DMA的一致性是可配置的,也就是說可以從Cached地址空間進行DMA操作,硬件維護DMA一致性;也可以不由硬件控制一致性而從Uncached地址空間操作。

出現問題的環節是I/O DMA的一致性維持,所以我們可以採取不讓硬件維持I/O DMA,走Uncached DMA來在犧牲一些I/O DMA性能的前提下繞過這個Bug。

Lemote在這些主板出貨的時候選擇犧牲兩個Node的0號核的方法來保證I/O DMA性能順便降低一些功耗(沒錯,3B1500是個NUMA架構的處理器,全片八個核心分成兩個Node,每四個核心組成一個SMP Group)。

另外,即使不解決DMA的問題,雖然穩定性不高,但是對於日常折騰也已足矣。更何況我們部分人拿到的3B1500G已經解決了這個Bug,但是還是被關核,實在是不甘心。

SMP/NUMA啟動流程

要找到開核方案,明白除了Bootloader初始運行的那顆核心之外剩餘的核心是如何被Bootloader初始化,控制權如何被移交給內核,是個很重要的過程。

根據我對龍芯多核架構的理解,多核啟動的主要流程是,每個核心被複位的時候,其PC共同指向NMI Exception的入口,也就是0xbfc00000,Kseg1被map到Boot SPI Flash的頭部的位置。之後由Bootloader初始化所有核心的私有緩存,TLB等組件。

然後軟件通過讀取CP0 Ebase的0-10bit CPUNum位判斷自己的核編號,如果自己是Bootloader將要下一步使用的Bootcore,就繼續運行Bootloader核心代碼,如果自己是從核,那麼就跳轉到一段循環代碼,等待Bootcore的控制權移交給內核之後,內核通過向各個核心的MailBox寄存器發送這個核心將要跑的代碼的PC地址來喚醒這個核心。

內核側的實現我大致讀了讀,覺得和開核關係不大,就不在這裡過多的做闡述了。

讓我們來看看PMON下具體的代碼實現,這些代碼均摘錄自龍芯開源的PMON Target bonito.3c780e的start.S。

 .set mips64
mfc0 t0, $15, 1 /* 取CP0 Ebase */
.set mips3
andi t0, t0, 0x3ff /* 取出t0中的CPUNum */
dli a0, 0x9800000000000000 /*取NUMA Base*/
andi t1, t0, 0x3
dsll t2, t1, 18
or a0, t2, a0 /* 256KB offset for the each core */
andi t2, t0, 0xc /* node id */
dsll t2, 42
or a0, t2, a0 /* get the L2 cache address */
dsll t1, t1, 8
or t1, t2, t1
dli t2, NODE0_CORE0_BUF0
or t1, t2, t1
/* 上面主要在判斷自己是哪個NUMA Node */
dli a0, BOOTCORE_ID /* 把BOOTCORE ID加載進a0 */
bne t0, a0, slave_main /* 如果t0(當前核的CPUNum) != a0(BOOTCORE) 那麼跳轉到slave_main */
nop
bal initserial /* 在BOOTCORE上初始化串口 */
nop

slave_main的代碼就不貼了,也是做一些初始化的活,最後是一個等待內核發mailbox的loop。

修改內核此路不通

PMON的Start.S讀的我醉生夢死,大致從頭看到尾,並沒有什麼去關閉那兩個核的代碼,按照我的理解,這兩個核在啟動之後也在運行slave_main的代碼,只不過因為通過“龍芯EFI標準”傳過去的Reserved Core Mask中將那倆核Mask掉了,所以內核沒有去初始化他們,那麼直接在內核層面覆蓋掉傳參就好了嘛。事實證明我還是太Naive,改完內核一跑,屏幕上還是6只小企鵝,串口上的輸出也是那麼直白,CPU0 Failed to Boot, CPU4 Failed to Boot。

不甘心的我手動在內核的Head.S里加了一段彙編餵給CPU0一個Mailbox PC,指望能把它帶回來,然而它根本不鳥我一下。

看來這兩顆核心是真的被關掉了。

此路不通矣。

小插曲

聯繫到一位Lemote的員工願意幫忙編譯一份開核版本的崑崙固件,但是失敗了。。。。。不過還是非常感謝他。。。

Lemote方面也表示不是很方便開放他們的PMON私有代碼。

看來還是要自己動手豐衣足食。

逆向工程一臉懵逼

說起逆向工程,那必是會想到Hex-Rays的IDA工具。IDA有對MIPS架構提供支持,那是再好不過了(其實我本來想用開源的Radare2,但是當時手邊只有一臺Windows環境的電腦,於是心安理得的開始使用盜版軟件)。

手上的PMON是一個bin文件,不要說debug symbol和lable了,就連個Entrypoint都沒有。。好在Bootloader總是從頭開始跑,也沒什麼庫之類的東西。

省略一堆設置IDA的過程。

加載後:

我花150元買了一臺龍芯電腦

截圖是後來補上的,剛載入進去肯定是沒有註釋,lable什麼的

撲面而來一堆彙編,我一臉懵逼,好吧,挑燈苦讀。窗外滿天星斗,一嘆星搖搖,長夜殊未央。

而且,不得不說反彙編得到的代碼和實際Start.S彙編差距很大,一是因為assembler進行了預處理,二是因為Lemote PMON的代碼基似乎和龍芯開源的版本也有一些差距。

總之,對照著開源版本的Start.S又是一番痛苦的分析。

同時發現了一個定位PMON代碼用途的小技巧,PMON源代碼裡的PIRINTSTR宏實際上是用stringserial函數配上往.data section寫入要打印字符實現的,也就說,只要找到stringserial的地址,就能找到在哪裡call的這個函數,以及傳給函數的.data section 地址/offset,進而得到PRINTSTR出來的內容。所以我得以標註出start.S中各個函數的入口點lable。

然而,重點檢查了判斷CPUNum的代碼和slave_main裡執行的代碼,並沒有發現更多和核心判斷相關的內容。

鬱悶至極,望向窗外,此時天已拂曉,第二天還有很多事情要幹,不得不鬱郁而眠。

又到了第二天晚上,繼續去鑽進這些代碼裡,再次檢查代碼,還是沒有線索。鬱悶至極,隨手翻開3B1500的用戶手冊尋找靈感。

一頁頁翻過去,突然看到 “2.6 芯片配置、採樣及 PLL 相關寄存器”,猛地一想,是不是單個核的時鐘被關閉了。。。仔細讀了讀這一張,果然,後面有提到 “芯片處理器核軟件分頻設置寄存器” ,這些寄存器可以控制單個核時鐘的使能與否。

他的物理地址是0x1fe001d0,換算到Start.S運行的kseg1裡就是0xbfe001d0。

全局搜索了一下這個地址,果然,在SHUT_SLAVE和WAKE_CORES兩個階段都被寫了一下,對照手冊看了看寫入的值,果然。CPU0和CPU4的時鐘都被直接關掉了。

我花150元買了一臺龍芯電腦

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

我花150元買了一臺龍芯電腦

關於這個Bug

在接受指點之後,我意識到這個Bug和I/O DMA一致性有關。

從胡偉武老師的文章《我們的龍芯三號》中,我找到了關於這個Bug的詳細描述:

這個問題是從3B1000到3B1500改版過程中引進的,為了提高性能,處理器核收到多個維護Cache一致性的無效請求時,原來每兩拍才能處理一個,改成可以連續處理,導致清除LL/SC同步指令的同步位llbit時錯了一拍,誤把IO DMA引起的Cache無效請求當作0號處理器核的Cache無效請求(IO DMA的編號剛好為0,與0號處理器核區分不開),通過軟件調整可以規避此問題。經過批量測試,原不穩定現象消失。

龍芯架構下,DMA的一致性是可配置的,也就是說可以從Cached地址空間進行DMA操作,硬件維護DMA一致性;也可以不由硬件控制一致性而從Uncached地址空間操作。

出現問題的環節是I/O DMA的一致性維持,所以我們可以採取不讓硬件維持I/O DMA,走Uncached DMA來在犧牲一些I/O DMA性能的前提下繞過這個Bug。

Lemote在這些主板出貨的時候選擇犧牲兩個Node的0號核的方法來保證I/O DMA性能順便降低一些功耗(沒錯,3B1500是個NUMA架構的處理器,全片八個核心分成兩個Node,每四個核心組成一個SMP Group)。

另外,即使不解決DMA的問題,雖然穩定性不高,但是對於日常折騰也已足矣。更何況我們部分人拿到的3B1500G已經解決了這個Bug,但是還是被關核,實在是不甘心。

SMP/NUMA啟動流程

要找到開核方案,明白除了Bootloader初始運行的那顆核心之外剩餘的核心是如何被Bootloader初始化,控制權如何被移交給內核,是個很重要的過程。

根據我對龍芯多核架構的理解,多核啟動的主要流程是,每個核心被複位的時候,其PC共同指向NMI Exception的入口,也就是0xbfc00000,Kseg1被map到Boot SPI Flash的頭部的位置。之後由Bootloader初始化所有核心的私有緩存,TLB等組件。

然後軟件通過讀取CP0 Ebase的0-10bit CPUNum位判斷自己的核編號,如果自己是Bootloader將要下一步使用的Bootcore,就繼續運行Bootloader核心代碼,如果自己是從核,那麼就跳轉到一段循環代碼,等待Bootcore的控制權移交給內核之後,內核通過向各個核心的MailBox寄存器發送這個核心將要跑的代碼的PC地址來喚醒這個核心。

內核側的實現我大致讀了讀,覺得和開核關係不大,就不在這裡過多的做闡述了。

讓我們來看看PMON下具體的代碼實現,這些代碼均摘錄自龍芯開源的PMON Target bonito.3c780e的start.S。

 .set mips64
mfc0 t0, $15, 1 /* 取CP0 Ebase */
.set mips3
andi t0, t0, 0x3ff /* 取出t0中的CPUNum */
dli a0, 0x9800000000000000 /*取NUMA Base*/
andi t1, t0, 0x3
dsll t2, t1, 18
or a0, t2, a0 /* 256KB offset for the each core */
andi t2, t0, 0xc /* node id */
dsll t2, 42
or a0, t2, a0 /* get the L2 cache address */
dsll t1, t1, 8
or t1, t2, t1
dli t2, NODE0_CORE0_BUF0
or t1, t2, t1
/* 上面主要在判斷自己是哪個NUMA Node */
dli a0, BOOTCORE_ID /* 把BOOTCORE ID加載進a0 */
bne t0, a0, slave_main /* 如果t0(當前核的CPUNum) != a0(BOOTCORE) 那麼跳轉到slave_main */
nop
bal initserial /* 在BOOTCORE上初始化串口 */
nop

slave_main的代碼就不貼了,也是做一些初始化的活,最後是一個等待內核發mailbox的loop。

修改內核此路不通

PMON的Start.S讀的我醉生夢死,大致從頭看到尾,並沒有什麼去關閉那兩個核的代碼,按照我的理解,這兩個核在啟動之後也在運行slave_main的代碼,只不過因為通過“龍芯EFI標準”傳過去的Reserved Core Mask中將那倆核Mask掉了,所以內核沒有去初始化他們,那麼直接在內核層面覆蓋掉傳參就好了嘛。事實證明我還是太Naive,改完內核一跑,屏幕上還是6只小企鵝,串口上的輸出也是那麼直白,CPU0 Failed to Boot, CPU4 Failed to Boot。

不甘心的我手動在內核的Head.S里加了一段彙編餵給CPU0一個Mailbox PC,指望能把它帶回來,然而它根本不鳥我一下。

看來這兩顆核心是真的被關掉了。

此路不通矣。

小插曲

聯繫到一位Lemote的員工願意幫忙編譯一份開核版本的崑崙固件,但是失敗了。。。。。不過還是非常感謝他。。。

Lemote方面也表示不是很方便開放他們的PMON私有代碼。

看來還是要自己動手豐衣足食。

逆向工程一臉懵逼

說起逆向工程,那必是會想到Hex-Rays的IDA工具。IDA有對MIPS架構提供支持,那是再好不過了(其實我本來想用開源的Radare2,但是當時手邊只有一臺Windows環境的電腦,於是心安理得的開始使用盜版軟件)。

手上的PMON是一個bin文件,不要說debug symbol和lable了,就連個Entrypoint都沒有。。好在Bootloader總是從頭開始跑,也沒什麼庫之類的東西。

省略一堆設置IDA的過程。

加載後:

我花150元買了一臺龍芯電腦

截圖是後來補上的,剛載入進去肯定是沒有註釋,lable什麼的

撲面而來一堆彙編,我一臉懵逼,好吧,挑燈苦讀。窗外滿天星斗,一嘆星搖搖,長夜殊未央。

而且,不得不說反彙編得到的代碼和實際Start.S彙編差距很大,一是因為assembler進行了預處理,二是因為Lemote PMON的代碼基似乎和龍芯開源的版本也有一些差距。

總之,對照著開源版本的Start.S又是一番痛苦的分析。

同時發現了一個定位PMON代碼用途的小技巧,PMON源代碼裡的PIRINTSTR宏實際上是用stringserial函數配上往.data section寫入要打印字符實現的,也就說,只要找到stringserial的地址,就能找到在哪裡call的這個函數,以及傳給函數的.data section 地址/offset,進而得到PRINTSTR出來的內容。所以我得以標註出start.S中各個函數的入口點lable。

然而,重點檢查了判斷CPUNum的代碼和slave_main裡執行的代碼,並沒有發現更多和核心判斷相關的內容。

鬱悶至極,望向窗外,此時天已拂曉,第二天還有很多事情要幹,不得不鬱郁而眠。

又到了第二天晚上,繼續去鑽進這些代碼裡,再次檢查代碼,還是沒有線索。鬱悶至極,隨手翻開3B1500的用戶手冊尋找靈感。

一頁頁翻過去,突然看到 “2.6 芯片配置、採樣及 PLL 相關寄存器”,猛地一想,是不是單個核的時鐘被關閉了。。。仔細讀了讀這一張,果然,後面有提到 “芯片處理器核軟件分頻設置寄存器” ,這些寄存器可以控制單個核時鐘的使能與否。

他的物理地址是0x1fe001d0,換算到Start.S運行的kseg1裡就是0xbfe001d0。

全局搜索了一下這個地址,果然,在SHUT_SLAVE和WAKE_CORES兩個階段都被寫了一下,對照手冊看了看寫入的值,果然。CPU0和CPU4的時鐘都被直接關掉了。

我花150元買了一臺龍芯電腦

我花150元買了一臺龍芯電腦

之前之所以忽視了這裡,是因為龍芯開源的PMON考慮到了6核版本和8和版本共用同一固件的兼容性,並沒有關掉這兩個核的時鐘,所以在逆向的時候,找到了這段代碼的用途,就誤以為作用和開源版本的一致,疏忽了。

然後梳理了一下需要修改的地方,主要是把Bootcore改成0,在SHUT_SLAVE的時候把一號核關掉,0號核打開,WAKE_CORES的時候

啟用所有八個核,就好了,PMON的代碼幾乎不動,只是改動幾個寫入寄存器的值。

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

我花150元買了一臺龍芯電腦

關於這個Bug

在接受指點之後,我意識到這個Bug和I/O DMA一致性有關。

從胡偉武老師的文章《我們的龍芯三號》中,我找到了關於這個Bug的詳細描述:

這個問題是從3B1000到3B1500改版過程中引進的,為了提高性能,處理器核收到多個維護Cache一致性的無效請求時,原來每兩拍才能處理一個,改成可以連續處理,導致清除LL/SC同步指令的同步位llbit時錯了一拍,誤把IO DMA引起的Cache無效請求當作0號處理器核的Cache無效請求(IO DMA的編號剛好為0,與0號處理器核區分不開),通過軟件調整可以規避此問題。經過批量測試,原不穩定現象消失。

龍芯架構下,DMA的一致性是可配置的,也就是說可以從Cached地址空間進行DMA操作,硬件維護DMA一致性;也可以不由硬件控制一致性而從Uncached地址空間操作。

出現問題的環節是I/O DMA的一致性維持,所以我們可以採取不讓硬件維持I/O DMA,走Uncached DMA來在犧牲一些I/O DMA性能的前提下繞過這個Bug。

Lemote在這些主板出貨的時候選擇犧牲兩個Node的0號核的方法來保證I/O DMA性能順便降低一些功耗(沒錯,3B1500是個NUMA架構的處理器,全片八個核心分成兩個Node,每四個核心組成一個SMP Group)。

另外,即使不解決DMA的問題,雖然穩定性不高,但是對於日常折騰也已足矣。更何況我們部分人拿到的3B1500G已經解決了這個Bug,但是還是被關核,實在是不甘心。

SMP/NUMA啟動流程

要找到開核方案,明白除了Bootloader初始運行的那顆核心之外剩餘的核心是如何被Bootloader初始化,控制權如何被移交給內核,是個很重要的過程。

根據我對龍芯多核架構的理解,多核啟動的主要流程是,每個核心被複位的時候,其PC共同指向NMI Exception的入口,也就是0xbfc00000,Kseg1被map到Boot SPI Flash的頭部的位置。之後由Bootloader初始化所有核心的私有緩存,TLB等組件。

然後軟件通過讀取CP0 Ebase的0-10bit CPUNum位判斷自己的核編號,如果自己是Bootloader將要下一步使用的Bootcore,就繼續運行Bootloader核心代碼,如果自己是從核,那麼就跳轉到一段循環代碼,等待Bootcore的控制權移交給內核之後,內核通過向各個核心的MailBox寄存器發送這個核心將要跑的代碼的PC地址來喚醒這個核心。

內核側的實現我大致讀了讀,覺得和開核關係不大,就不在這裡過多的做闡述了。

讓我們來看看PMON下具體的代碼實現,這些代碼均摘錄自龍芯開源的PMON Target bonito.3c780e的start.S。

 .set mips64
mfc0 t0, $15, 1 /* 取CP0 Ebase */
.set mips3
andi t0, t0, 0x3ff /* 取出t0中的CPUNum */
dli a0, 0x9800000000000000 /*取NUMA Base*/
andi t1, t0, 0x3
dsll t2, t1, 18
or a0, t2, a0 /* 256KB offset for the each core */
andi t2, t0, 0xc /* node id */
dsll t2, 42
or a0, t2, a0 /* get the L2 cache address */
dsll t1, t1, 8
or t1, t2, t1
dli t2, NODE0_CORE0_BUF0
or t1, t2, t1
/* 上面主要在判斷自己是哪個NUMA Node */
dli a0, BOOTCORE_ID /* 把BOOTCORE ID加載進a0 */
bne t0, a0, slave_main /* 如果t0(當前核的CPUNum) != a0(BOOTCORE) 那麼跳轉到slave_main */
nop
bal initserial /* 在BOOTCORE上初始化串口 */
nop

slave_main的代碼就不貼了,也是做一些初始化的活,最後是一個等待內核發mailbox的loop。

修改內核此路不通

PMON的Start.S讀的我醉生夢死,大致從頭看到尾,並沒有什麼去關閉那兩個核的代碼,按照我的理解,這兩個核在啟動之後也在運行slave_main的代碼,只不過因為通過“龍芯EFI標準”傳過去的Reserved Core Mask中將那倆核Mask掉了,所以內核沒有去初始化他們,那麼直接在內核層面覆蓋掉傳參就好了嘛。事實證明我還是太Naive,改完內核一跑,屏幕上還是6只小企鵝,串口上的輸出也是那麼直白,CPU0 Failed to Boot, CPU4 Failed to Boot。

不甘心的我手動在內核的Head.S里加了一段彙編餵給CPU0一個Mailbox PC,指望能把它帶回來,然而它根本不鳥我一下。

看來這兩顆核心是真的被關掉了。

此路不通矣。

小插曲

聯繫到一位Lemote的員工願意幫忙編譯一份開核版本的崑崙固件,但是失敗了。。。。。不過還是非常感謝他。。。

Lemote方面也表示不是很方便開放他們的PMON私有代碼。

看來還是要自己動手豐衣足食。

逆向工程一臉懵逼

說起逆向工程,那必是會想到Hex-Rays的IDA工具。IDA有對MIPS架構提供支持,那是再好不過了(其實我本來想用開源的Radare2,但是當時手邊只有一臺Windows環境的電腦,於是心安理得的開始使用盜版軟件)。

手上的PMON是一個bin文件,不要說debug symbol和lable了,就連個Entrypoint都沒有。。好在Bootloader總是從頭開始跑,也沒什麼庫之類的東西。

省略一堆設置IDA的過程。

加載後:

我花150元買了一臺龍芯電腦

截圖是後來補上的,剛載入進去肯定是沒有註釋,lable什麼的

撲面而來一堆彙編,我一臉懵逼,好吧,挑燈苦讀。窗外滿天星斗,一嘆星搖搖,長夜殊未央。

而且,不得不說反彙編得到的代碼和實際Start.S彙編差距很大,一是因為assembler進行了預處理,二是因為Lemote PMON的代碼基似乎和龍芯開源的版本也有一些差距。

總之,對照著開源版本的Start.S又是一番痛苦的分析。

同時發現了一個定位PMON代碼用途的小技巧,PMON源代碼裡的PIRINTSTR宏實際上是用stringserial函數配上往.data section寫入要打印字符實現的,也就說,只要找到stringserial的地址,就能找到在哪裡call的這個函數,以及傳給函數的.data section 地址/offset,進而得到PRINTSTR出來的內容。所以我得以標註出start.S中各個函數的入口點lable。

然而,重點檢查了判斷CPUNum的代碼和slave_main裡執行的代碼,並沒有發現更多和核心判斷相關的內容。

鬱悶至極,望向窗外,此時天已拂曉,第二天還有很多事情要幹,不得不鬱郁而眠。

又到了第二天晚上,繼續去鑽進這些代碼裡,再次檢查代碼,還是沒有線索。鬱悶至極,隨手翻開3B1500的用戶手冊尋找靈感。

一頁頁翻過去,突然看到 “2.6 芯片配置、採樣及 PLL 相關寄存器”,猛地一想,是不是單個核的時鐘被關閉了。。。仔細讀了讀這一張,果然,後面有提到 “芯片處理器核軟件分頻設置寄存器” ,這些寄存器可以控制單個核時鐘的使能與否。

他的物理地址是0x1fe001d0,換算到Start.S運行的kseg1裡就是0xbfe001d0。

全局搜索了一下這個地址,果然,在SHUT_SLAVE和WAKE_CORES兩個階段都被寫了一下,對照手冊看了看寫入的值,果然。CPU0和CPU4的時鐘都被直接關掉了。

我花150元買了一臺龍芯電腦

我花150元買了一臺龍芯電腦

之前之所以忽視了這裡,是因為龍芯開源的PMON考慮到了6核版本和8和版本共用同一固件的兼容性,並沒有關掉這兩個核的時鐘,所以在逆向的時候,找到了這段代碼的用途,就誤以為作用和開源版本的一致,疏忽了。

然後梳理了一下需要修改的地方,主要是把Bootcore改成0,在SHUT_SLAVE的時候把一號核關掉,0號核打開,WAKE_CORES的時候

啟用所有八個核,就好了,PMON的代碼幾乎不動,只是改動幾個寫入寄存器的值。

我花150元買了一臺龍芯電腦

好吧,剛想改就給我刺激的,好在對MIPS的指令編碼我也還算有些瞭解,直接做HexEdit級別的Patch也沒什麼問題。

改完已經是凌晨一點了,又XJB一改內核,編程器燒寫了一波,刺激的點亮時間到。

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

我花150元買了一臺龍芯電腦

關於這個Bug

在接受指點之後,我意識到這個Bug和I/O DMA一致性有關。

從胡偉武老師的文章《我們的龍芯三號》中,我找到了關於這個Bug的詳細描述:

這個問題是從3B1000到3B1500改版過程中引進的,為了提高性能,處理器核收到多個維護Cache一致性的無效請求時,原來每兩拍才能處理一個,改成可以連續處理,導致清除LL/SC同步指令的同步位llbit時錯了一拍,誤把IO DMA引起的Cache無效請求當作0號處理器核的Cache無效請求(IO DMA的編號剛好為0,與0號處理器核區分不開),通過軟件調整可以規避此問題。經過批量測試,原不穩定現象消失。

龍芯架構下,DMA的一致性是可配置的,也就是說可以從Cached地址空間進行DMA操作,硬件維護DMA一致性;也可以不由硬件控制一致性而從Uncached地址空間操作。

出現問題的環節是I/O DMA的一致性維持,所以我們可以採取不讓硬件維持I/O DMA,走Uncached DMA來在犧牲一些I/O DMA性能的前提下繞過這個Bug。

Lemote在這些主板出貨的時候選擇犧牲兩個Node的0號核的方法來保證I/O DMA性能順便降低一些功耗(沒錯,3B1500是個NUMA架構的處理器,全片八個核心分成兩個Node,每四個核心組成一個SMP Group)。

另外,即使不解決DMA的問題,雖然穩定性不高,但是對於日常折騰也已足矣。更何況我們部分人拿到的3B1500G已經解決了這個Bug,但是還是被關核,實在是不甘心。

SMP/NUMA啟動流程

要找到開核方案,明白除了Bootloader初始運行的那顆核心之外剩餘的核心是如何被Bootloader初始化,控制權如何被移交給內核,是個很重要的過程。

根據我對龍芯多核架構的理解,多核啟動的主要流程是,每個核心被複位的時候,其PC共同指向NMI Exception的入口,也就是0xbfc00000,Kseg1被map到Boot SPI Flash的頭部的位置。之後由Bootloader初始化所有核心的私有緩存,TLB等組件。

然後軟件通過讀取CP0 Ebase的0-10bit CPUNum位判斷自己的核編號,如果自己是Bootloader將要下一步使用的Bootcore,就繼續運行Bootloader核心代碼,如果自己是從核,那麼就跳轉到一段循環代碼,等待Bootcore的控制權移交給內核之後,內核通過向各個核心的MailBox寄存器發送這個核心將要跑的代碼的PC地址來喚醒這個核心。

內核側的實現我大致讀了讀,覺得和開核關係不大,就不在這裡過多的做闡述了。

讓我們來看看PMON下具體的代碼實現,這些代碼均摘錄自龍芯開源的PMON Target bonito.3c780e的start.S。

 .set mips64
mfc0 t0, $15, 1 /* 取CP0 Ebase */
.set mips3
andi t0, t0, 0x3ff /* 取出t0中的CPUNum */
dli a0, 0x9800000000000000 /*取NUMA Base*/
andi t1, t0, 0x3
dsll t2, t1, 18
or a0, t2, a0 /* 256KB offset for the each core */
andi t2, t0, 0xc /* node id */
dsll t2, 42
or a0, t2, a0 /* get the L2 cache address */
dsll t1, t1, 8
or t1, t2, t1
dli t2, NODE0_CORE0_BUF0
or t1, t2, t1
/* 上面主要在判斷自己是哪個NUMA Node */
dli a0, BOOTCORE_ID /* 把BOOTCORE ID加載進a0 */
bne t0, a0, slave_main /* 如果t0(當前核的CPUNum) != a0(BOOTCORE) 那麼跳轉到slave_main */
nop
bal initserial /* 在BOOTCORE上初始化串口 */
nop

slave_main的代碼就不貼了,也是做一些初始化的活,最後是一個等待內核發mailbox的loop。

修改內核此路不通

PMON的Start.S讀的我醉生夢死,大致從頭看到尾,並沒有什麼去關閉那兩個核的代碼,按照我的理解,這兩個核在啟動之後也在運行slave_main的代碼,只不過因為通過“龍芯EFI標準”傳過去的Reserved Core Mask中將那倆核Mask掉了,所以內核沒有去初始化他們,那麼直接在內核層面覆蓋掉傳參就好了嘛。事實證明我還是太Naive,改完內核一跑,屏幕上還是6只小企鵝,串口上的輸出也是那麼直白,CPU0 Failed to Boot, CPU4 Failed to Boot。

不甘心的我手動在內核的Head.S里加了一段彙編餵給CPU0一個Mailbox PC,指望能把它帶回來,然而它根本不鳥我一下。

看來這兩顆核心是真的被關掉了。

此路不通矣。

小插曲

聯繫到一位Lemote的員工願意幫忙編譯一份開核版本的崑崙固件,但是失敗了。。。。。不過還是非常感謝他。。。

Lemote方面也表示不是很方便開放他們的PMON私有代碼。

看來還是要自己動手豐衣足食。

逆向工程一臉懵逼

說起逆向工程,那必是會想到Hex-Rays的IDA工具。IDA有對MIPS架構提供支持,那是再好不過了(其實我本來想用開源的Radare2,但是當時手邊只有一臺Windows環境的電腦,於是心安理得的開始使用盜版軟件)。

手上的PMON是一個bin文件,不要說debug symbol和lable了,就連個Entrypoint都沒有。。好在Bootloader總是從頭開始跑,也沒什麼庫之類的東西。

省略一堆設置IDA的過程。

加載後:

我花150元買了一臺龍芯電腦

截圖是後來補上的,剛載入進去肯定是沒有註釋,lable什麼的

撲面而來一堆彙編,我一臉懵逼,好吧,挑燈苦讀。窗外滿天星斗,一嘆星搖搖,長夜殊未央。

而且,不得不說反彙編得到的代碼和實際Start.S彙編差距很大,一是因為assembler進行了預處理,二是因為Lemote PMON的代碼基似乎和龍芯開源的版本也有一些差距。

總之,對照著開源版本的Start.S又是一番痛苦的分析。

同時發現了一個定位PMON代碼用途的小技巧,PMON源代碼裡的PIRINTSTR宏實際上是用stringserial函數配上往.data section寫入要打印字符實現的,也就說,只要找到stringserial的地址,就能找到在哪裡call的這個函數,以及傳給函數的.data section 地址/offset,進而得到PRINTSTR出來的內容。所以我得以標註出start.S中各個函數的入口點lable。

然而,重點檢查了判斷CPUNum的代碼和slave_main裡執行的代碼,並沒有發現更多和核心判斷相關的內容。

鬱悶至極,望向窗外,此時天已拂曉,第二天還有很多事情要幹,不得不鬱郁而眠。

又到了第二天晚上,繼續去鑽進這些代碼裡,再次檢查代碼,還是沒有線索。鬱悶至極,隨手翻開3B1500的用戶手冊尋找靈感。

一頁頁翻過去,突然看到 “2.6 芯片配置、採樣及 PLL 相關寄存器”,猛地一想,是不是單個核的時鐘被關閉了。。。仔細讀了讀這一張,果然,後面有提到 “芯片處理器核軟件分頻設置寄存器” ,這些寄存器可以控制單個核時鐘的使能與否。

他的物理地址是0x1fe001d0,換算到Start.S運行的kseg1裡就是0xbfe001d0。

全局搜索了一下這個地址,果然,在SHUT_SLAVE和WAKE_CORES兩個階段都被寫了一下,對照手冊看了看寫入的值,果然。CPU0和CPU4的時鐘都被直接關掉了。

我花150元買了一臺龍芯電腦

我花150元買了一臺龍芯電腦

之前之所以忽視了這裡,是因為龍芯開源的PMON考慮到了6核版本和8和版本共用同一固件的兼容性,並沒有關掉這兩個核的時鐘,所以在逆向的時候,找到了這段代碼的用途,就誤以為作用和開源版本的一致,疏忽了。

然後梳理了一下需要修改的地方,主要是把Bootcore改成0,在SHUT_SLAVE的時候把一號核關掉,0號核打開,WAKE_CORES的時候

啟用所有八個核,就好了,PMON的代碼幾乎不動,只是改動幾個寫入寄存器的值。

我花150元買了一臺龍芯電腦

好吧,剛想改就給我刺激的,好在對MIPS的指令編碼我也還算有些瞭解,直接做HexEdit級別的Patch也沒什麼問題。

改完已經是凌晨一點了,又XJB一改內核,編程器燒寫了一波,刺激的點亮時間到。

我花150元買了一臺龍芯電腦

八隻小企鵝!瞬間淚流滿面

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

我花150元買了一臺龍芯電腦

關於這個Bug

在接受指點之後,我意識到這個Bug和I/O DMA一致性有關。

從胡偉武老師的文章《我們的龍芯三號》中,我找到了關於這個Bug的詳細描述:

這個問題是從3B1000到3B1500改版過程中引進的,為了提高性能,處理器核收到多個維護Cache一致性的無效請求時,原來每兩拍才能處理一個,改成可以連續處理,導致清除LL/SC同步指令的同步位llbit時錯了一拍,誤把IO DMA引起的Cache無效請求當作0號處理器核的Cache無效請求(IO DMA的編號剛好為0,與0號處理器核區分不開),通過軟件調整可以規避此問題。經過批量測試,原不穩定現象消失。

龍芯架構下,DMA的一致性是可配置的,也就是說可以從Cached地址空間進行DMA操作,硬件維護DMA一致性;也可以不由硬件控制一致性而從Uncached地址空間操作。

出現問題的環節是I/O DMA的一致性維持,所以我們可以採取不讓硬件維持I/O DMA,走Uncached DMA來在犧牲一些I/O DMA性能的前提下繞過這個Bug。

Lemote在這些主板出貨的時候選擇犧牲兩個Node的0號核的方法來保證I/O DMA性能順便降低一些功耗(沒錯,3B1500是個NUMA架構的處理器,全片八個核心分成兩個Node,每四個核心組成一個SMP Group)。

另外,即使不解決DMA的問題,雖然穩定性不高,但是對於日常折騰也已足矣。更何況我們部分人拿到的3B1500G已經解決了這個Bug,但是還是被關核,實在是不甘心。

SMP/NUMA啟動流程

要找到開核方案,明白除了Bootloader初始運行的那顆核心之外剩餘的核心是如何被Bootloader初始化,控制權如何被移交給內核,是個很重要的過程。

根據我對龍芯多核架構的理解,多核啟動的主要流程是,每個核心被複位的時候,其PC共同指向NMI Exception的入口,也就是0xbfc00000,Kseg1被map到Boot SPI Flash的頭部的位置。之後由Bootloader初始化所有核心的私有緩存,TLB等組件。

然後軟件通過讀取CP0 Ebase的0-10bit CPUNum位判斷自己的核編號,如果自己是Bootloader將要下一步使用的Bootcore,就繼續運行Bootloader核心代碼,如果自己是從核,那麼就跳轉到一段循環代碼,等待Bootcore的控制權移交給內核之後,內核通過向各個核心的MailBox寄存器發送這個核心將要跑的代碼的PC地址來喚醒這個核心。

內核側的實現我大致讀了讀,覺得和開核關係不大,就不在這裡過多的做闡述了。

讓我們來看看PMON下具體的代碼實現,這些代碼均摘錄自龍芯開源的PMON Target bonito.3c780e的start.S。

 .set mips64
mfc0 t0, $15, 1 /* 取CP0 Ebase */
.set mips3
andi t0, t0, 0x3ff /* 取出t0中的CPUNum */
dli a0, 0x9800000000000000 /*取NUMA Base*/
andi t1, t0, 0x3
dsll t2, t1, 18
or a0, t2, a0 /* 256KB offset for the each core */
andi t2, t0, 0xc /* node id */
dsll t2, 42
or a0, t2, a0 /* get the L2 cache address */
dsll t1, t1, 8
or t1, t2, t1
dli t2, NODE0_CORE0_BUF0
or t1, t2, t1
/* 上面主要在判斷自己是哪個NUMA Node */
dli a0, BOOTCORE_ID /* 把BOOTCORE ID加載進a0 */
bne t0, a0, slave_main /* 如果t0(當前核的CPUNum) != a0(BOOTCORE) 那麼跳轉到slave_main */
nop
bal initserial /* 在BOOTCORE上初始化串口 */
nop

slave_main的代碼就不貼了,也是做一些初始化的活,最後是一個等待內核發mailbox的loop。

修改內核此路不通

PMON的Start.S讀的我醉生夢死,大致從頭看到尾,並沒有什麼去關閉那兩個核的代碼,按照我的理解,這兩個核在啟動之後也在運行slave_main的代碼,只不過因為通過“龍芯EFI標準”傳過去的Reserved Core Mask中將那倆核Mask掉了,所以內核沒有去初始化他們,那麼直接在內核層面覆蓋掉傳參就好了嘛。事實證明我還是太Naive,改完內核一跑,屏幕上還是6只小企鵝,串口上的輸出也是那麼直白,CPU0 Failed to Boot, CPU4 Failed to Boot。

不甘心的我手動在內核的Head.S里加了一段彙編餵給CPU0一個Mailbox PC,指望能把它帶回來,然而它根本不鳥我一下。

看來這兩顆核心是真的被關掉了。

此路不通矣。

小插曲

聯繫到一位Lemote的員工願意幫忙編譯一份開核版本的崑崙固件,但是失敗了。。。。。不過還是非常感謝他。。。

Lemote方面也表示不是很方便開放他們的PMON私有代碼。

看來還是要自己動手豐衣足食。

逆向工程一臉懵逼

說起逆向工程,那必是會想到Hex-Rays的IDA工具。IDA有對MIPS架構提供支持,那是再好不過了(其實我本來想用開源的Radare2,但是當時手邊只有一臺Windows環境的電腦,於是心安理得的開始使用盜版軟件)。

手上的PMON是一個bin文件,不要說debug symbol和lable了,就連個Entrypoint都沒有。。好在Bootloader總是從頭開始跑,也沒什麼庫之類的東西。

省略一堆設置IDA的過程。

加載後:

我花150元買了一臺龍芯電腦

截圖是後來補上的,剛載入進去肯定是沒有註釋,lable什麼的

撲面而來一堆彙編,我一臉懵逼,好吧,挑燈苦讀。窗外滿天星斗,一嘆星搖搖,長夜殊未央。

而且,不得不說反彙編得到的代碼和實際Start.S彙編差距很大,一是因為assembler進行了預處理,二是因為Lemote PMON的代碼基似乎和龍芯開源的版本也有一些差距。

總之,對照著開源版本的Start.S又是一番痛苦的分析。

同時發現了一個定位PMON代碼用途的小技巧,PMON源代碼裡的PIRINTSTR宏實際上是用stringserial函數配上往.data section寫入要打印字符實現的,也就說,只要找到stringserial的地址,就能找到在哪裡call的這個函數,以及傳給函數的.data section 地址/offset,進而得到PRINTSTR出來的內容。所以我得以標註出start.S中各個函數的入口點lable。

然而,重點檢查了判斷CPUNum的代碼和slave_main裡執行的代碼,並沒有發現更多和核心判斷相關的內容。

鬱悶至極,望向窗外,此時天已拂曉,第二天還有很多事情要幹,不得不鬱郁而眠。

又到了第二天晚上,繼續去鑽進這些代碼裡,再次檢查代碼,還是沒有線索。鬱悶至極,隨手翻開3B1500的用戶手冊尋找靈感。

一頁頁翻過去,突然看到 “2.6 芯片配置、採樣及 PLL 相關寄存器”,猛地一想,是不是單個核的時鐘被關閉了。。。仔細讀了讀這一張,果然,後面有提到 “芯片處理器核軟件分頻設置寄存器” ,這些寄存器可以控制單個核時鐘的使能與否。

他的物理地址是0x1fe001d0,換算到Start.S運行的kseg1裡就是0xbfe001d0。

全局搜索了一下這個地址,果然,在SHUT_SLAVE和WAKE_CORES兩個階段都被寫了一下,對照手冊看了看寫入的值,果然。CPU0和CPU4的時鐘都被直接關掉了。

我花150元買了一臺龍芯電腦

我花150元買了一臺龍芯電腦

之前之所以忽視了這裡,是因為龍芯開源的PMON考慮到了6核版本和8和版本共用同一固件的兼容性,並沒有關掉這兩個核的時鐘,所以在逆向的時候,找到了這段代碼的用途,就誤以為作用和開源版本的一致,疏忽了。

然後梳理了一下需要修改的地方,主要是把Bootcore改成0,在SHUT_SLAVE的時候把一號核關掉,0號核打開,WAKE_CORES的時候

啟用所有八個核,就好了,PMON的代碼幾乎不動,只是改動幾個寫入寄存器的值。

我花150元買了一臺龍芯電腦

好吧,剛想改就給我刺激的,好在對MIPS的指令編碼我也還算有些瞭解,直接做HexEdit級別的Patch也沒什麼問題。

改完已經是凌晨一點了,又XJB一改內核,編程器燒寫了一波,刺激的點亮時間到。

我花150元買了一臺龍芯電腦

八隻小企鵝!瞬間淚流滿面

我花150元買了一臺龍芯電腦

回頭看看對PMON做的修改,也就那麼7個byte而已,但是其中的艱辛,也算是隻有自己明白。

Kernel

於是,這個兩個核被解放了出來,接下來就是要讓內核初始化並且使用上這兩個核,還要讓內核在適當的情況下決定自己的DMA一致性。之前我粗暴無禮的硬編碼了bootcpu編號和reserved core mask,這並不是一個優雅的實現,也會讓內核失去通用性。

"

前言

文中出現的3B1500處理器是龍芯2012年的產品,目前龍芯最新的CPU是3A3000,性能要比文中的處理器高至少三倍。這也是國人設計的第一款國產八核處理器,就算是垃圾,也是很有紀念意義的垃圾。

我花150元買了一臺龍芯電腦

150元的電腦主板

最近有一批龍芯3B1500主板150元低價處理,型號是Lemote-A1310,我也買了塊。

美中不足的是由於芯片一個硬件設計Bug,默認由Bootloader屏蔽了2個核心,也就是說只有6個核心開放給用戶使用。

經過一些調查,我認為通過軟件繞過硬件的限制,將那兩個核心開放是可行的,也因為群裡呼聲巨大,所以我開始著手於解放這些核。

我花150元買了一臺龍芯電腦

關於這個Bug

在接受指點之後,我意識到這個Bug和I/O DMA一致性有關。

從胡偉武老師的文章《我們的龍芯三號》中,我找到了關於這個Bug的詳細描述:

這個問題是從3B1000到3B1500改版過程中引進的,為了提高性能,處理器核收到多個維護Cache一致性的無效請求時,原來每兩拍才能處理一個,改成可以連續處理,導致清除LL/SC同步指令的同步位llbit時錯了一拍,誤把IO DMA引起的Cache無效請求當作0號處理器核的Cache無效請求(IO DMA的編號剛好為0,與0號處理器核區分不開),通過軟件調整可以規避此問題。經過批量測試,原不穩定現象消失。

龍芯架構下,DMA的一致性是可配置的,也就是說可以從Cached地址空間進行DMA操作,硬件維護DMA一致性;也可以不由硬件控制一致性而從Uncached地址空間操作。

出現問題的環節是I/O DMA的一致性維持,所以我們可以採取不讓硬件維持I/O DMA,走Uncached DMA來在犧牲一些I/O DMA性能的前提下繞過這個Bug。

Lemote在這些主板出貨的時候選擇犧牲兩個Node的0號核的方法來保證I/O DMA性能順便降低一些功耗(沒錯,3B1500是個NUMA架構的處理器,全片八個核心分成兩個Node,每四個核心組成一個SMP Group)。

另外,即使不解決DMA的問題,雖然穩定性不高,但是對於日常折騰也已足矣。更何況我們部分人拿到的3B1500G已經解決了這個Bug,但是還是被關核,實在是不甘心。

SMP/NUMA啟動流程

要找到開核方案,明白除了Bootloader初始運行的那顆核心之外剩餘的核心是如何被Bootloader初始化,控制權如何被移交給內核,是個很重要的過程。

根據我對龍芯多核架構的理解,多核啟動的主要流程是,每個核心被複位的時候,其PC共同指向NMI Exception的入口,也就是0xbfc00000,Kseg1被map到Boot SPI Flash的頭部的位置。之後由Bootloader初始化所有核心的私有緩存,TLB等組件。

然後軟件通過讀取CP0 Ebase的0-10bit CPUNum位判斷自己的核編號,如果自己是Bootloader將要下一步使用的Bootcore,就繼續運行Bootloader核心代碼,如果自己是從核,那麼就跳轉到一段循環代碼,等待Bootcore的控制權移交給內核之後,內核通過向各個核心的MailBox寄存器發送這個核心將要跑的代碼的PC地址來喚醒這個核心。

內核側的實現我大致讀了讀,覺得和開核關係不大,就不在這裡過多的做闡述了。

讓我們來看看PMON下具體的代碼實現,這些代碼均摘錄自龍芯開源的PMON Target bonito.3c780e的start.S。

 .set mips64
mfc0 t0, $15, 1 /* 取CP0 Ebase */
.set mips3
andi t0, t0, 0x3ff /* 取出t0中的CPUNum */
dli a0, 0x9800000000000000 /*取NUMA Base*/
andi t1, t0, 0x3
dsll t2, t1, 18
or a0, t2, a0 /* 256KB offset for the each core */
andi t2, t0, 0xc /* node id */
dsll t2, 42
or a0, t2, a0 /* get the L2 cache address */
dsll t1, t1, 8
or t1, t2, t1
dli t2, NODE0_CORE0_BUF0
or t1, t2, t1
/* 上面主要在判斷自己是哪個NUMA Node */
dli a0, BOOTCORE_ID /* 把BOOTCORE ID加載進a0 */
bne t0, a0, slave_main /* 如果t0(當前核的CPUNum) != a0(BOOTCORE) 那麼跳轉到slave_main */
nop
bal initserial /* 在BOOTCORE上初始化串口 */
nop

slave_main的代碼就不貼了,也是做一些初始化的活,最後是一個等待內核發mailbox的loop。

修改內核此路不通

PMON的Start.S讀的我醉生夢死,大致從頭看到尾,並沒有什麼去關閉那兩個核的代碼,按照我的理解,這兩個核在啟動之後也在運行slave_main的代碼,只不過因為通過“龍芯EFI標準”傳過去的Reserved Core Mask中將那倆核Mask掉了,所以內核沒有去初始化他們,那麼直接在內核層面覆蓋掉傳參就好了嘛。事實證明我還是太Naive,改完內核一跑,屏幕上還是6只小企鵝,串口上的輸出也是那麼直白,CPU0 Failed to Boot, CPU4 Failed to Boot。

不甘心的我手動在內核的Head.S里加了一段彙編餵給CPU0一個Mailbox PC,指望能把它帶回來,然而它根本不鳥我一下。

看來這兩顆核心是真的被關掉了。

此路不通矣。

小插曲

聯繫到一位Lemote的員工願意幫忙編譯一份開核版本的崑崙固件,但是失敗了。。。。。不過還是非常感謝他。。。

Lemote方面也表示不是很方便開放他們的PMON私有代碼。

看來還是要自己動手豐衣足食。

逆向工程一臉懵逼

說起逆向工程,那必是會想到Hex-Rays的IDA工具。IDA有對MIPS架構提供支持,那是再好不過了(其實我本來想用開源的Radare2,但是當時手邊只有一臺Windows環境的電腦,於是心安理得的開始使用盜版軟件)。

手上的PMON是一個bin文件,不要說debug symbol和lable了,就連個Entrypoint都沒有。。好在Bootloader總是從頭開始跑,也沒什麼庫之類的東西。

省略一堆設置IDA的過程。

加載後:

我花150元買了一臺龍芯電腦

截圖是後來補上的,剛載入進去肯定是沒有註釋,lable什麼的

撲面而來一堆彙編,我一臉懵逼,好吧,挑燈苦讀。窗外滿天星斗,一嘆星搖搖,長夜殊未央。

而且,不得不說反彙編得到的代碼和實際Start.S彙編差距很大,一是因為assembler進行了預處理,二是因為Lemote PMON的代碼基似乎和龍芯開源的版本也有一些差距。

總之,對照著開源版本的Start.S又是一番痛苦的分析。

同時發現了一個定位PMON代碼用途的小技巧,PMON源代碼裡的PIRINTSTR宏實際上是用stringserial函數配上往.data section寫入要打印字符實現的,也就說,只要找到stringserial的地址,就能找到在哪裡call的這個函數,以及傳給函數的.data section 地址/offset,進而得到PRINTSTR出來的內容。所以我得以標註出start.S中各個函數的入口點lable。

然而,重點檢查了判斷CPUNum的代碼和slave_main裡執行的代碼,並沒有發現更多和核心判斷相關的內容。

鬱悶至極,望向窗外,此時天已拂曉,第二天還有很多事情要幹,不得不鬱郁而眠。

又到了第二天晚上,繼續去鑽進這些代碼裡,再次檢查代碼,還是沒有線索。鬱悶至極,隨手翻開3B1500的用戶手冊尋找靈感。

一頁頁翻過去,突然看到 “2.6 芯片配置、採樣及 PLL 相關寄存器”,猛地一想,是不是單個核的時鐘被關閉了。。。仔細讀了讀這一張,果然,後面有提到 “芯片處理器核軟件分頻設置寄存器” ,這些寄存器可以控制單個核時鐘的使能與否。

他的物理地址是0x1fe001d0,換算到Start.S運行的kseg1裡就是0xbfe001d0。

全局搜索了一下這個地址,果然,在SHUT_SLAVE和WAKE_CORES兩個階段都被寫了一下,對照手冊看了看寫入的值,果然。CPU0和CPU4的時鐘都被直接關掉了。

我花150元買了一臺龍芯電腦

我花150元買了一臺龍芯電腦

之前之所以忽視了這裡,是因為龍芯開源的PMON考慮到了6核版本和8和版本共用同一固件的兼容性,並沒有關掉這兩個核的時鐘,所以在逆向的時候,找到了這段代碼的用途,就誤以為作用和開源版本的一致,疏忽了。

然後梳理了一下需要修改的地方,主要是把Bootcore改成0,在SHUT_SLAVE的時候把一號核關掉,0號核打開,WAKE_CORES的時候

啟用所有八個核,就好了,PMON的代碼幾乎不動,只是改動幾個寫入寄存器的值。

我花150元買了一臺龍芯電腦

好吧,剛想改就給我刺激的,好在對MIPS的指令編碼我也還算有些瞭解,直接做HexEdit級別的Patch也沒什麼問題。

改完已經是凌晨一點了,又XJB一改內核,編程器燒寫了一波,刺激的點亮時間到。

我花150元買了一臺龍芯電腦

八隻小企鵝!瞬間淚流滿面

我花150元買了一臺龍芯電腦

回頭看看對PMON做的修改,也就那麼7個byte而已,但是其中的艱辛,也算是隻有自己明白。

Kernel

於是,這個兩個核被解放了出來,接下來就是要讓內核初始化並且使用上這兩個核,還要讓內核在適當的情況下決定自己的DMA一致性。之前我粗暴無禮的硬編碼了bootcpu編號和reserved core mask,這並不是一個優雅的實現,也會讓內核失去通用性。

我花150元買了一臺龍芯電腦

"

相關推薦

推薦中...