2009年12月14日 星期一

●memtest86+教學 Part16

關於SMP在Memtest86+ V4.00與Memtest86 V3.5 之比較可以發現main.c中多了void initialise_cpus(void),因此先將Memtest86 V3.5 轉成exe檔來執行看看多加入SMP功能後測試時間是否減少.得知結果差異不大,因為其實作多核只是以輪流交替執行權來執行程式碼。我想未來應該會有改進吧,到時後就可以享受多核心CPU降低測試時間的好處了。
注意一點Memtest86 V3.5必須使用gcc 4-2-0以下的版本compile,但Memtest86+ V4.00則使用gcc 4-2-x以上版本。
--> 閱讀更多...

2009年6月10日 星期三

●memtest86+教學 Part15

仔細看一下controller.c之中ㄉstatic struct pci_memory_controller controllers[],你就可以知道目前memtest86+到底支援哪些chipsets,可別小看這ㄍ陣列,我們看一下這ㄍ陣列的元素是ㄍ資料結構如下:
struct pci_memory_controller {
unsigned vendor;
unsigned device;
char *name;
int tested;
void (*poll_fsb)(void);
void (*poll_timings)(void);
void (*setup_ecc)(void);
void (*poll_errors)(void);
};
上面綠色字ㄉ那幾ㄍ是函式指標。這種程式的寫法應該不會陌生吧!一般人很少能接觸這麼多種類的平台,即使接觸到ㄌ也不見得拿得到它們的data sheets,因此能寫出特定chipset的那些函式指標,真是佩服;然而有些還是memtest86作者能力所不及,為什麼ㄋ;例如poll_fsb_nothing,這樣ㄉ函式裡什麼東西也沒有,原因我想當然是沒有specifiction。我想Intel真是大方,它會將所有chipsets的data sheet提供給大家download,其他廠商可不會這樣作。這有關係ㄇ,這個關係可大ㄌ,就好比說show_spd這個routine,它目前只能support ich5以上的南橋晶片,若你的平台不是Intel的晶片組,那麼memtest show SPD code的function就只能看到"SMBus Controller not known",連chipset裡頭的相關Register你都碰不到,我想對該chipset就只能束手無策ㄌ。
--> 閱讀更多...

2009年5月9日 星期六

AMD64 & Intel EM64T


此圖是從intel datasheet:Intel64 and IA-32 Architectures Software Develops Manual Volume3A所截取下來,從這張圖就可知道CPU如何在各模式之間切換;及為什麼可以作到向下相容的架構。
圖二
圖一
AMD公司設計,可以在同一時間內處理64位的整數運算,並兼容于X86-32架構。其中支持64位邏輯定址,同時提供轉換為32位定址選項;但數據操作指令默認為32位和8位,提供轉換成64位和16位的選項;支持常規用途暫存器,如果是32位運算操作,就要將結果擴展成完整的64位。這樣,指令中有“直接執行”和“轉換執行”的區別,其指令字段是8位或32位,可以避免字段過長。
x86-64(也叫AMD64)的產生也並非空穴來風,x86處理器的32bit尋址空間限制在4GB內存,而IA-64的處理器又不能兼容x86。AMD充分考慮顧客的需求,加強x86指令集的功能,使這套指令集可同時支持64位的運算模式,因此AMD把它們的架構稱之為x86-64。在技術上AMD在x86-64架構中為了進行64位運算,AMD為其引入了新增了R8-R15通用暫存器作為原有X86處理器暫存器的擴充,但在而在32位環境下並不完全使用到這些暫存器。原來的暫存器諸如EAX、EBX也由32位擴張至64位。在SSE單元中新加入了8個新暫存器以提供對SSE2的支持。暫存器數量的增加將帶來性能的提升。與此同時,為了同時支持32和64位代碼及暫存器,x86-64架構允許處理器工作在以下兩種模式︰Long Mode(長模式)和Legacy Mode(傳統模式),Long模式又分為兩種子模式(64bit模式和Compatibility mode兼容模式)。該標準已經被引進在AMD伺服器處理器中的Opteron處理器。
而今年也推出了支持64位的EM64T技術,再還沒被正式命為EM64T之前是IA32E,這是英特爾64位擴展技術的名字,用來區別X86指令集。Intel的EM64T支持64位sub-mode,和AMD的X86-64技術類似,採用64位的線性平面尋址,加入8個新的通用暫存器(GPRs),還增加8個暫存器支持SSE指令。與AMD相類似,Intel的64位技術將兼容IA32和IA32E,只有在營運64位作業系統下的時候,才將會採用IA32E。IA32E將由2個sub-mode組成︰64位sub-mode和32位sub-mode,同AMD64一樣是向下兼容的。Intel的EM64T將完全兼容AMD的X86-64技術。現下Nocona處理器已經加入了一些64位技術,Intel的Pentium 4E處理器也支持64位技術。
應該說,這兩者都是兼容x86指令集的64位微處理器架構,但EM64T與AMD64還是有一些不一樣的地方
AMD的K8不是真正的64bit處理器,其和Intel的EM64T處理器一樣,不是真正的64bit處理器。Microsoft的WINXP64bit也不是真正意義上的64bit作業系統。真正64bit系統(硬體和軟體系統)出來的時候,現下這些所謂的64bit全部會淘汰,而這段時間會相當長,至少三年內難以實現;況且配件的更新速度很快,只要用的順手,何必在意以後會怎樣。順便說一下,現下的K8實際上是40bit處理器,而非64bit處理器;EM64T實際上是32bit處理器,只是在物理內存尋址上擴展到36bit。
AMD64 及 EM64T 倒底誰是一顆真正的 64 bit處理器,不用太在意, 這兩者都是由 32bit 處理器改進而來, 前陣子在 bbs 的 hardware 版, 為了AMD64 是不是創新的架構, 爭論了許久. 不過, 實際使用上, AMD64 不論是要當 32bit或 64 bit處理器來用, 算是不錯, 效能好, 溫度又低!

由 Intel EM64T 技術談 64 位元 CPU
由於軟體的功能越來越強大且多元化,所消耗的系統資源相對的也越多,因此在軟體的功能不斷提升的同時,硬體的處理能力也就捉襟見拙。而新技術不斷的發展,除了代表科技的進步外,也因應這必然的趨勢,造就了電腦平台的變革 —DDRⅡ、PCI Express、Hyper Threading 與 EM64T 等。
一、何謂 64 位元 CPU我們知道一個功能強大的軟體其實是由無數行的程式所組合而成,每一行的程式碼均會透過編譯器編譯成數筆資料,送到 CPU 內部的暫存器中等待執行。而所謂的 64 位元也就是在這資料的寬度、暫存器的大小以及執行指令的長度均為 64 位元,另外在記憶體的定址能力同樣也為 64 位元。
二、64 位元 CPU 的發展Intel CPU 的發展從 80386 之後便演進為 32 位元,到 1994 年 Intel 與 HP 共同開發 VLIW(Very Long Instruction Word)架構的 Itanium、Itanium2 的 IA-64 CPU,希望能將 HP 的 PA-RISC、COMPAQ 的 Alpha 以及 x86 架構的 PC 作一整合,以期降低研發高階伺服器處理器成本,並能跨平台的執行不同的作業系統。只是因為 Itanium、Itanium2 的 CPU 使用的是 VLIW 架構,雖然在執行 64 位元的作業系統與應用程式非常順暢,但於 32 位元的作業系統與應用程式中必須透過指令轉譯的方式來執行,導致在 32 位元的環境下效能不彰,因此發展上是叫好不叫座。在 64 位元 CPU 沈潛好一段時間之後,AMD 於 2003 年四月推出 64 位元 x86 架構的處理器 Opteron,以及 Apple 推出 PowerPC G5 的 RISC CPU,既可以執行 64 位元作業系統與應用程式,亦可向下相容於原本 32 位元的作業系統與應用程式,一時間 64 位元處理器的話題又熱絡起來。而 Intel 為了不讓競爭對手專美於前,於 2004 年春季 IDF 論壇中發表了 Intel 64bit Extension Technology 的新技術,並於 4 月份正式定名為 Intel Extended Memory 64 Technology(EM64T),並採用被稱之為 IA-32e 的新操作模式。
三、EM64T 技術Intel EM64T 是為了強化 IA-32 架構所發展的新技術。包含此技術的 IA-32 處理器除了可向下相容於現存的 32 位元軟體,並賦予軟體存取更多記憶體空間。並且允許那些開發於 32 位元定址模式下的軟體擁有執行於 64 位元定址空間的能力。相較於之前 IA32,擁有 EM64T 的 IA32e 最大的不同是將記憶體定址能力由原本 2 的 32 次方提升到 2 的 64 次方,也就是 18446744TB。
四、包含 EM64T 技術處理器的執行模式包含 IA32e 的處理器可執行在 Legacy IA-32 Mode 與 IA-32e Mode。IA-32e Mode 又包含了兩種子模式:1. Compatibility Mode:允許 64 位元的作業系統在不做任何的變動下執行大多數原生的 32 位元軟體。2. 64-bit Mode:允許 64 位元作業系統執行那些存取 64 位元位址空間的應用程式。
五、64-bit Mode 是用來在 64 位元作業系統下執行 64 位元應用程式,包含下列幾項特性:● 64 位元線性定址。● 8 個全新的一般暫存器(General-Purpose Register)。● 8 個全新 Streaming SIMD Extensions(SSE)暫存器支援 SSE、SSE2與SSE3。● 64 位元寬度的 GPR 與指令指標● 相同的 byte-register addressing● 更快速的中斷優化機制● 全新的指令指標相對定址模式64-bit Mode 可透過 64 位元作業系統藉由分割程式碼的準則來啟動。初始定址大小為 64 位元,初始運算元大小為 32 位元。
六、在 Compatibility Mode 中,絕大部分的 16 位元或 32 位元應用程式均可在不重新編譯的狀況下執行於 64 位元作業系統中。相同於 64-bit Mode,Compatibility Mode 可透過 64 位元作業系統藉由分割程式碼的準則來啟動。也就是說在同一時間中,64 位元應用程式也可被執行於 64-bit Mode,而不需重新編譯。
七、Legacy IA-32 Mode 包含保護模式、真實位址模式或是虛擬 8086 模式。針對這些模式撰寫的軟體,EM64T 的處理器均有完整的相容性。
上圖一列出 EM64T 處理器在不同模式下的差異:
上面圖二列出了在不同模式中所支援的暫存器類別:
八、64 位元處理器的迷思是不是購買了 64 位元處理器,相對的執行速度就是原本 32 位元的兩倍?其實以目前的作業系統 Windows XP 或是 Windows 2000 均為 32 位元的作業系統,而應用程式部分亦為 32 位元,也因此在程式碼分割與執行指令上仍為 32 位元寬,而 CPU 資料處理部分仍為 32 位元,也因此與目前的 IA-32 處理器並無差別。在未來 Windows XP 64-bit Edition for Extended System 上市後搭配 64 位元的應用程式,在效能方面才能有所提升。
--> 閱讀更多...

2009年5月6日 星期三

●memtest86+教學 Part14


圖一
上一篇我們已經知道頁目錄如何建立,並且要另外建立一個指向4個頁目錄基底位址的表格,名稱叫作pdp(page-directory-pointer-table)它非常重要,因為我們現在討論的這種2M分頁方式無論如何一定要建立4GB的頁目錄表,並區分成4等分,每一等分佔用4KByte(4096)的空間,也就是說每一等分負責1GB容量的映射(Mapping);這樣的說法不如來作一張圖(如圖一)會更容易理解。雖然圖中例子是共16G記憶體,但可支援到64G的Mapping。
現在我們來看看誰在使用pdp及頁目錄:
int map_page(unsigned long page)
{
unsigned long i;
struct pde
{
unsigned long addr_lo;
unsigned long addr_hi;
};
extern unsigned char pdp[];
extern struct pde pd2[];
unsigned long window = page >> 19;
if (FLAT (window == mapped_window)) { return 0; }
if (window == 0) { return 0; }
if (!v->pae (window >= 32)) {
/* Fail either we don't have pae support
* or we want an address that is out of bounds
* even for pae. */
return -1; }
/* Compute the page table entries... */
//由於左移符號會無法顯示在文章中,因此我把左移使用shl來表示,右移shr,小於lss
for(i = 0; i lss 1024; i++)
{
pd2[i].addr_lo = ((window & 1) shl 31) + ((i & 0x3ff) shl 21) + 0xE3;
pd2[i].addr_hi = (window shr 1);
}
/*以上這個for迴圈會重新建立pd2~pd3的頁目錄表*/
paging_off();
if (window gtr 1) { paging_on(pdp); }
mapped_window = window;
return 0;
}
這個函式可以說是memtest86分頁技術中最經典的routineㄌ。我們來看誰調用它:
有init.c中的cacheable():
/* Ensure the default set of pages are mapped */
map_page(0);
map_page(0x80000);
及main.c中的do_test():
map_page(v->map[0].pbase_addr)
在cacheable()調用它共2個;第一個map_page(0);代入0結果return 0,而且不會去啟用paging_on(pdp);第二個map_page(0x80000);代入0x80000結果也是return 0,而且不會去啟用paging_on(pdp);然而這只是程式的初始,當然不會去啟用分頁;所以真正會不斷調用它的便是map_page(v->map[0].pbase_addr);而且你會發現pd0~pd3好像只有pd2每次都會重新被計算,其實應該說是pd2及pd3每次都被重新計算,因為它的for迴圈是1024次,然而每個pd佔用512個頁目錄表項。所以我們可以開始推論:假設我們主機板插上6GB的記憶體;當測試0~4G時,並不會啟動分頁模式,當測試範圍為4~6G時;便會進入分頁,並且把pd2~pd3對應到4~6G;也就是說此時cpu access的位址雖然是2~4G,但實際上access 到的卻是實體物理位址4~6G。這樣ㄉ推論沒有說服力;所以一定要實際驗證才行。
順便來看一下paging_on:
static void paging_on(void *pdp)
{
if (!v->pae) return;
__asm__ __volatile__(
/* Load the page table address 此處的%0就是pdp的位址 */
"movl %0, %%cr3\n\t"
/* Enable pae cr4的bit5就是pae位元設為1*/
"movl %%cr4, %%eax\n\t"
"orl $0x00000020, %%eax\n\t"
"movl %%eax, %%cr4\n\t"
/* Enable paging cr0的PG位元設為1*/
"movl %%cr0, %%eax\n\t"
"orl $0x80000000, %%eax\n\t"
"movl %%eax, %%cr0\n\t"
:
: "r" (pdp)
: "ax"
);
}
P.S.
試想,為什麼要分頁,而且還分不同的分頁模式,為什麼要把事情搞的那麼複雜ㄋ,難不成就是要存心搞死我們這些人,難道就沒更好的辦法嗎?回顧歷史,32位元CPU已問世多年,我記得Win95那個時候我的系統記憶體也不過才32MB,但是CPU已經可以Access到4G的空間;所以那時RAM遠比CPU定址能力少得多,為了實現虛擬記憶體,讓作業系統可以多工,每個程式都擁有自己的4G位址空間,所以才搞分頁,因為記憶體容量有限,所以要把很多已執行但卻不會馬上用到的行程(Process)所佔用的空間置換(swap)到硬碟(或其他儲存媒體裝置),使正在執行的行程能有足夠的記體體使用權。曾幾何時,CPU定址仍然是32位元,但DRAM價跌容量升級,使很多使用者的安裝於系統的記憶體容量甚至大於4G,因此時代變了分頁技術也要跟著變,就像我們現在所探討的memtest86,就是因為要能符合時代須求所以要測試比4G容量更大的記憶體,且CPU也已經支援這種技術,所以我們才能借助對memtest86的了解,得知這樣的分頁模式。總之這樣的分頁模式和現行OS所實作的分頁稍有不同,就是因為須求不同的原因或許未來OS設計會將資訊全數載入記憶體,便不需要swap到硬碟,透過PAE這樣的分頁方式來取得資訊,我想一定能更加速系統的運作,但先決條件是DRAM還要更便宜,容量再加大;純假設。
待續‧‧‧


--> 閱讀更多...

●memtest86+教學 Part13






分頁我想應該是memtest86的精華,若能真的把這部分搞懂,我想對IA32(x86)的認識又進入到更深一層的境界了。若你想對整個IA-32的分頁做作通盤認識你可以去參考Intel 64 and IA-32 Architectures Software Developer's Manual - Volume 3A System Programming Guide.pdf。由於一般書籍講到的分頁都是使用4kbyte的分頁模式,而且只講到CR3(或稱 PDBR,page directory base register),然而IA-32的分頁方式還分成好幾種模式;而且支援4K、2M、4M的分頁大小;並可定址到64G (36-BIT PHYSICAL ADDRESSING USING THE PAE PAGING MECHANISM);說的好像很複雜,其實就是在搞CR3、CR4這兩個暫存器(register)。然而其實也沒那麼簡單,無論如何,由於memtest86是採用2M分頁(page size extensions)及支援PAE paging mechanism使可定址到64G;因此我們至少要對這種分頁方式作一番說明:


首先我們先來看看爛豬腳(head.S)如何實作頁目錄,你可能會問不是還要有頁表嗎?這個問題非常好但是我不想在此解答,你直接參考我上面說的那份資料以及Paging Extensions for the Pentium Pro Processor 就可知道為何。

在head.S有這麼一段code:注意;在巨集中使用參數必須前面加上"\"符號
.macro ptes64 start, count=64
.quad \start + 0x0000000 + 0xE3 ;為什麼是E3,下面會解說
.quad \start + 0x0200000 + 0xE3
.quad \start + 0x0400000 + 0xE3
.quad \start + 0x0600000 + 0xE3
.quad \start + 0x0800000 + 0xE3
.quad \start + 0x0A00000 + 0xE3
.quad \start + 0x0C00000 + 0xE3
.quad \start + 0x0E00000 + 0xE3
.if \count-1ptes64 "(\start+0x01000000)",\count-1
.endif
.endm
.macro maxdepth depth=1
.if \depth-1maxdepth \depth-1
.endif
.endm
maxdepth
.balign 4096
.globl pd0
pd0: ptes64 0x0000000000000000
.balign 4096
.globl pd1
pd1: ptes64 0x0000000040000000
.balign 4096
.globl pd2
pd2: ptes64 0x0000000080000000
.balign 4096
.globl pd3
pd3: ptes64 0x00000000C0000000
.balign 4096
.globl pdp
pdp:
.long pd0 + 1
.long 0
.long pd1 + 1
.long 0
.long pd2 + 1
.long 0
.long pd3 + 1
.long 0
上面這段code最重要的就是ptes64那個巨集(macro);若你把pd0:、pd1:、pd2:、pd3:後面的ptes64巨集展開,便會得到0~4GB的頁目錄表,而且每個表項相差2MB。差別在於其每一個頁目錄表項佔用一個quad(8Byte),這和我之前介紹的那本"自己動手寫作業系統"所談到的分頁採用long為頁目錄,相差4個bytes;-而且書面說的也不搞pdp原因如下:
Figure 3-21 shows the format for the page-directory-pointer-table and page-directory entries when 2-MByte pages and extended physicaladdresses are being used.
The major differences in these entries are as follows:
•A page-directory-pointer(pdp)-table entry is added.
•The size of the entries are increased from 32 bits to 64 bits
•The maximum number of entries in a page directory or page table is 512.
•The base physical address field in each entry is extended to 24 bits for 36-bit physical addressing (or extended to MAXPHYADDR-12 bits if MAXPHYADDR is different than 36).
另外針對"至少在各式各樣的Pentium簡介中有四個2M頁[1,2,3,4]"這句話的意思,答案如下:
Figure 3-19 shows how a page-directory-pointer table and page directories can be used to map linear addresses to 2-MByte pages when the PAE paging mechanism enabled. This paging method can be used to map up to 2048 pages (4 page-directory-pointer(pdp)-table entries times 512 page-directory entries) into a 4-GByte linear address space.也就是Figure 3-19中的bit30和bit31。
/*-----------------參考init.c map_page()--------------- *
0xE3 --
* Bit 0 = Present bit. 1 = PDE is present
* Bit 1 = Read/Write. 1 = memory is writable
* Bit 2 = Supervisor/User. 0 = Supervisor only (CPL 0-2)
* Bit 3 = Writethrough. 0 = writeback cache policy
* Bit 4 = Cache Disable. 0 = page level cache enabled
* Bit 5 = Accessed. 1 = memory has been accessed.
* Bit 6 = Dirty. 1 = memory has been written to.
* Bit 7 = Page Size. 1 = page size is 2 MBytes
* --------------------------------------------------*/
透過以上的認知我們要來看其他的code如何來實作分頁‧‧‧待續‧‧‧
--> 閱讀更多...

2009年5月4日 星期一

●memtest86+教學 Part12

今天我想把int 15h eax=E820h作個總結;首先我們看一下head.S:
/* Don't disable the a20 line */
/* Load 16bit data segments, to ensure the segment limits are set */
movl $REAL_DS, %eax
movl %eax, %ds
movl %eax, %es
movl %eax, %ss
movl %eax, %fs
movl %eax, %gs
/* Compute the stack base */
leal stack@GOTOFF(%ebx), %ecx
/* Compute the address of meminfo*/
leal mem_info@GOTOFF(%ebx), %edi #取得mem_info標籤的絕對位址並存到edi
/* switch to 16bit mode */
ljmp $REAL_CS, $1f - RSTART #這個ljmp就是跳到.code16,也就是下一行;因為ljmp的格式是 segment:offset ;segment就是$REAL_CS;offset就是$1f - RSTART(下一行1:標籤減掉RSTART;因為有這個定義#define RSTART startup_32就是head.S的頂端;所以$1f - RSTART就等於1:標籤相對於head.S的偏移位址)
1:
.code16
/* Disable Paging and protected mode */
/* clear the PG & PE bits of CR0 */
movl %cr0,%eax
andl $~((1 << 31)(1<<0)),%eax #關閉分頁及保護模式,回到真實模式
movl %eax,%cr0
/* make intersegment jmp to flush the processor pipeline * and reload %cs:%eip (to clear upper 16 bits of %eip). */
ljmp *(realptr - RSTART)
#這樣的語法通常是這種32位元和16位元的混合區段才會用到(切記)
#這樣的語法類似nasm的 jmp dword selector:offset;
#然而masm卻沒有這樣的機制語法;所以masm會如下這樣搞:
# db 0eah
# dw ofsfset
# dw segment
#以上三行是JMP到16bit時的用法,雖等同於 jmp dword ptr label(不支援),但masm好像只
#支援 jmp fword ptr label(從16bit跳到32bit),然而跳到32bit也可以這樣搞:
# db 0eah
# dw ofsfset , 0h
# dw segment

#也就是說從32bit跳到16bit會去提取2ㄍbyte當offset及2ㄍbyte當segment
#當從16bit跳到32bit會去提取4ㄍbyte當offset及2ㄍbyte當segment
real:
/* we are in real mode now
* set up the real mode segment registers : %ds, %ss, %es, %gs, %fs
*/
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
/* Adjust the stack pointer */
movl %ecx, %eax
shrl $4, %eax #取得ss值
movw %ax, %ss
subl %ecx, %esp
/* Save my base pointer */
pushl %ebx
/* Setup %ds to point to my data area */
shrl $4, %edi
movl %edi, %ds
/* Enable interrupts or BIOS's go crazy */
sti
# Get memory size (extended mem, kB)
#define SMAP 0x534d4150
xorl %eax, %eax
movl %eax, (E88) #0x00
movl %eax, (E801) #0x04
movl %eax, (E820NR) #0x08
# Try three different memory detection schemes. First, try
# e820h, which lets us assemble a memory map, then try e801h,
# which returns a 32-bit memory size, and finally 88h, which
# returns 0-64m
# method E820H:
# the memory map from hell. e820h returns memory classified into
# a whole bunch of different types, and allows memory holes and
# everything. We scan through this memory map and build a list
# of the first 32 memory areas, which we return at [E820MAP].
# This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm
#int 15h eax=0e820h使用說明:
#INPUT:eax=0e820h ebx=0(第一次呼叫必須為0)
#INPUT:es:di=指向一個可容納32*20byte的位址空間
#INPUT:ecx=20 edx=0534d4150h('SMAP')
#OUTPUT:CF(C旗標=0表示成功,否則失敗)
#OUTPUT:eax=0534d4150h('SMAP') return value
#OUTPUT:ecx=20
#OUTPUT:ebx如果等於0且cf=0,表示已經是最後一個位址範圍描述器
meme820:
xorl %ebx, %ebx # continuation counter
movw $E820MAP, %di # point into the whitelist
# so we can have the bios
# directly write into it.
jmpe820:
movl $0x0000e820, %eax # e820, upper word zeroed
movl $SMAP, %edx # ascii 'SMAP'
movl $20, %ecx # size of the e820rec
pushw %ds # data record.
popw %es #這ㄍpush pop很重要,因為得到ㄉ資料是存放在es:di
int $0x15 # make the call
jc bail820 # fall to e801 if it fails
cmpl $SMAP, %eax # check the return is `SMAP'
jne bail820 # fall to e801 if it fails
# cmpl $1, 16(%di) # is this usable memory?
# jne again820
# If this is usable memory, we save it by simply advancing %di by
# sizeof(e820rec).
good820:
movb (E820NR), %al # up to 32 entries
cmpb $E820MAX, %al
jnl bail820
incb (E820NR)
movw %di, %ax
addw $E820ENTRY_SIZE, %ax #define E820ENTRY_SIZE 20
movw %ax, %di
again820:
cmpl $0, %ebx # check to see if
jne jmpe820 # %ebx is set to EOF
bail820:
# method E801H:
# memory size is in 1k chunksizes, to avoid confusing loadlin.
# we store the 0xe801 memory size in a completely different place,
# because it will most likely be longer than 16 bits.
#以下是0xe801及E88我們不討論,因為現在用的主機板根本不會去呼叫它們
meme801:
‧‧‧
‧‧

/* O.k. the BIOS query is done switch back to protected mode */
cli
/* Restore my saved variables */
popl %ebx
/* Get an convinient %ds */
movw %cs, %ax
movw %ax, %ds
/* Load the global descriptor table */
addr32 lgdt gdt_descr - RSTART
/* Turn on protected mode */
/* Set the PE bit in CR0 */
movl %cr0,%eax
orl $(1 SHL 0),%eax #回到保護模式
movl %eax,%cr0
/* flush the prefetch queue, and relaod %cs:%eip */
data32 ljmp *(protptr - RSTART)
prot:
.code32
‧‧‧
‧‧

realptr:
.word real - RSTART #仔細觀察一下realptr: 這個標籤後面這兩個.word
.word 0x0000 #因為ljmp 後面是先offset值,然後才是segment值
protptr:
.long 0
.long KERNEL_CS #其實這ㄍlong只要定義為word就可以ㄌ
idt_real:

.word 0x400 - 1 # idt limit ( 256 entries)
.word 0, 0 # idt base = 0L
‧‧‧
.globl mem_info #匯出這個符號給c語言使用
mem_info: #以下這裡就是存放位址範圍描述器的地方
. = . + MEMINFO_SIZE #0x28c=(E88+E801+E820NR=0ch)+(20*32)
‧‧‧
stack:
. = . + 4096
stack_top:

由以上的代碼我們已經得到n個位址範圍描述器,接下來我們要回到init.c得知接下來是呼叫memsize.c中的mem_size();
由於test.h有這樣的定義:extern struct mem_info_t mem_info;
struct e820entry
{ unsigned long long addr; /* start of memory segment */
unsigned long long size; /* size of memory segment */
unsigned long type; /* type of memory segment */
};

struct mem_info_t
{ unsigned long e88_mem_k; /* 0x00 */
unsigned long e801_mem_k; /* 0x04 */
unsigned long e820_nr; /* 0x08 */
struct e820entry e820[E820MAX]; /* 0x0c */
/* 0x28c */};

所以就可將這些位址範圍描述器整理如下:
if (e820_nr == 0 && alt_mem_k == 0 && ext_mem_k == 0){
ext_mem_k = mem_info.e88_mem_k;
alt_mem_k = mem_info.e801_mem_k;
e820_nr = mem_info.e820_nr;
for (i=0; i< mem_info.e820_nr; i++) {
e820[i].addr = mem_info.e820[i].addr;
e820[i].size = mem_info.e820[i].size;
e820[i].type = mem_info.e820[i].type; }
}
--> 閱讀更多...

2009年4月21日 星期二

●memtest86+教學 Part11

圖二
圖一

我想在●memtest86+教學 Part9 中ㄉ介紹觀念還是很模糊,所以我貼出上面這張圖一及圖二,雖然我以visual PC 128MB作說明,但道理是一樣ㄉ。當然看這張圖必須參考memtest86+教學 Part6 中ㄉ圖PMAP-1。
--> 閱讀更多...

2009年4月19日 星期日

●基礎2-計算機表單設計

圖二
圖一

根據上圖一說明各物件name屬性:這些屬性可自己定義
數字按鈕1:Num_Button1
數字按鈕2:Num_Button2
‧‧‧
數字按鈕9:Num_Button9
數字按鈕0:Num_Button0
加法按鈕:add_btn
減法按鈕:sub_btn
乘法按鈕:mul_btn
除法按鈕:div_btn
清除按鈕:clear_btn
等號按鈕:equ_btn
square根號按鈕:square_btn
N階層按鈕:norder_btn
Hex按鈕:Hex_btn
Binary按鈕:Binary_btn
文字方塊:Display_Text
以下是部分代碼:
Private Sub Num_Button8_Click()
If (mode = 0) Then Num1 = Num1 + "8"
Display_Text = Num1 Else Num2 = Num2 + "8"
Display_Text = Num2
End IfEnd Sub

Private Sub Num_Button9_Click()
If (mode = 0) Then Num1 = Num1 + "9"
Display_Text = Num1 Else Num2 = Num2 + "9"
Display_Text = Num2
End IfEnd Sub

Private Sub square_btn_Click()
Display_Text = Str(Sqr(Val(Display_Text.Text)))
End Sub

Private Sub sub_btn_Click()
Display_Text = ""
mode = 2
End Sub

Private Sub UserForm_Initialize()
mode = 0
End Sub
我們依照上一篇ㄉ作法在EXCEL開啟時能夠馬上秀出這個計算機表單。實際作法由於我只列出部分程式碼,所以若有興趣您就下載去看看。我們在未來所用範例都是依照Office 2007,若您使用Office 2003,請自行修改成2003的格式。但是這個code,我故意留下N階層Binary沒作,就留給大家發揮一下。完成畫面如圖二。
--> 閱讀更多...

2009年4月15日 星期三

●memtest86+教學 Part10

圖一
上幾回我有提過memtest86+可以透過uart將畫面透過com port傳送到console;其畫面如圖一。今天我們先來介紹一些重要的變數:
struct vars variables = {};
struct vars * const v = &variables;
別小看這個"v",它運行於整個memtest86,可以說是核心資料結構;所以我們先從了解它著手:
struct vars {
int test; //目前所選取或正在測試中的pattern
int pass; //已經passㄉloop數
unsigned long *eadr; //沒用到,所以不知道宣告這ㄍ做什麼,真正用到ㄉ是在struct err_info erri裡面。
unsigned long exor; //同上
int msg_line; //要印出文字或數字ㄉ列數
int ecount; //錯誤數量
int ecc_ecount; //ecc錯誤數量,這應該也沒用到,因為我們不是測server dimm
int msegs; //從呼叫int 15h ax=e820h所得到ㄉ實體記憶體的segment數量
int testsel; //目前所選取ㄉpattern
int scroll_start; //看這個v->scroll_start = v->msg_line * 160;就知道意思
int rdtsc; //是否開始計時的變數(這ㄍ變數和assembly 指令一模一樣,不要搞混)
int pae; //判斷cpu是否支援分頁ㄉ變數
int pass_ticks; //目前的滴答數
int total_ticks; //總滴答數
int pptr; //顯示"#"總進度Progress的目前位置資訊
int tptr; //顯示"#"目前測試pattern進度Progress的目前位置資訊
int beepmode; //是否啟用pc喇叭
struct err_info erri; //這ㄍ結構底下會介紹
struct pmap pmap[MAX_MEM_SEGMENTS]; //以下4ㄍ這裡很難--
struct mmap map[MAX_MEM_SEGMENTS]; //用幾ㄍ字說清楚,--
ulong plim_lower; //我已另有--
ulong plim_upper; //文章說明。
ulong clks_msec; //CPU時脈
ulong starth; //開始計時ㄉ滴答數高32bits
ulong startl; //開始計時ㄉ滴答數低32bits
ulong snaph; //因為這ㄍ#undef TEST_TIMES所以沒用到此變數
ulong snapl; //因為這ㄍ#undef TEST_TIMES所以沒用到此變數
ulong extclock; //這也是CPU時脈,不知道為什麼要搞死大家
int printmode; //顯示fail的模式:預設值是PRINTMODE_ADDRESSES1
int numpatn; //當fail顯示模式為PRINTMODE_PATTERNS2時,要知道有幾個pattern fail
struct pair patn [BADRAM_MAXPATNS]; //還沒研究
ulong test_pages; //所插記憶體pmap總數
ulong selected_pages; //所插記憶體pmap總數(所以真ㄉ要搞死大家)
ulong reserved_pages; //type2及type4的pmap總數
};

struct err_info {
struct xadr low_addr;
struct xadr high_addr;
unsigned long ebits;
long tbits;
short min_bits;
short max_bits;
unsigned long maxl;
unsigned long eadr;
unsigned long exor;
unsigned long cor_err;
short hdr_flag;
};
以上這ㄍstruct在init.c中有初始化如下
v->erri.low_addr.page = 0x7fffffff;
v->erri.low_addr.offset = 0xfff;
v->erri.high_addr.page = 0;
v->erri.high_addr.offset = 0;
v->erri.min_bits = 32;
v->erri.max_bits = 0;
v->erri.min_bits = 32;
v->erri.max_bits = 0;
v->erri.maxl = 0;
v->erri.cor_err = 0;
v->erri.ebits = 0;
v->erri.hdr_flag = 0;
v->erri.tbits = 0;
當然要研究這ㄍstruct就是要搞懂error.c。
--> 閱讀更多...

●基礎1-EXCEL VBA初體驗

圖一
自定表單/工具箱
屬性視窗

專案視窗
我開使接觸VBA(visual basic application )是從office 2000開始,結果現在2009年office也release到office 2007,而且office也增加ㄌ更多的應用。但綜觀使用者的使用頻率,好像excel、word、power point、Access,才是大家最常用的。連linux也搞open office,但它好像不支援巨集(macro)功能,至少以前不支援,現在我就不知道ㄌ,雖然open office格式相容於MS Office但相容性有限。好ㄌ少說廢話,進入主題ㄅ,我又扯遠ㄌ。反正標題就是要說excel vba,其它就不講ㄌ:
首先學會如何開啟VBA功能,接著就來點簡單ㄉㄅ。以下我們所談內容都表示你已經開啟vba編輯視窗。要會VBA首先要認識專案視窗及屬性視窗;
專案視窗中:This Workbook指ㄉ就是你現在已經開啟ㄉexcel活頁簿,sheet1~sheet3指ㄉ就是活頁簿中的那三個工作表。專案視窗中左上角ㄉ按鈕(檢視程式碼)(檢視物件)便是切換編輯畫面的功能鍵。
今天我們先來和vba表單見個面ㄅ,因為我ㄉ習慣是若excel沒表單,那它就沒有讓我愛不釋手ㄉ理由ㄌ。在插入/自訂表單就會代出如上圖片。我們透過工具箱/Label,產生一個文字標籤,如上圖一,由屬性視窗我們知道,表單ㄉname屬性是UserForm1(可自行定義);接著我們今天來寫一行code如下。點選This Workbook,再點選(檢視程式碼),在程試碼編輯區上方有下拉視窗,目前應該顯示為"一般",改選為"Workbook";便會自動帶出兩行code如下,我在中間加入一行:
Private Sub Workbook_Open()
UserForm1.Show
End Sub
大功告成,儲存檔案。記得回excel(2007)設定開發人員/巨集安全性/巨集設定/啟用所有巨集。若是Excel(2003)則是工具/巨集/安全性/安全性層級/
如此你已完程第一個vba程式,並且會在這ㄍexcel檔開啟時也開啟那個表單視窗。
--> 閱讀更多...

2009年4月14日 星期二

●符合SMBIOS規範的電腦系統訊息獲取方法

以下這篇有關smbiosㄉ文章寫ㄉ很詳細,貼在自己ㄉ部落格是因為工作會常用到,怕說以後會找不到這份資料。
符合SMBIOS規範的電腦系統訊息獲取方法

作者:ramble(如需轉載請注明作者)
SMBIOS (System Management BIOS, SMBIOS),它是一種定出主機板及系統廠商如何以標準的格式顯示產品管理資訊的規格。SMBIOS 及 DMI(Desktop Management Interface)規格兩者皆是由 Desktop Management Task Force (DMTF) 所草擬的,它是一個由業界所領導,實行技術規格以確認開放性標準的組織。
對於符合SMBIOS規範的電腦,可以通過訪問SMBIOS的結構獲得系統訊息,共有兩種辦法可以訪問:
1.通過即插即用功能接口訪問SMBIOS結構,這個在SMBIOS2.0標准裏定義了,從SMBIOS2.1開始這個訪問方法不再被推薦使用。
2.基於表結構的方法,表内容是tableentry point的數據,這個訪問方法從SMBIOS2.1以後開始被使用,從2.1開始,以後的版本都推薦使用這種訪問方式。在2.1版本中允許支援這兩種方法中的任意一種和兩種都支援,但在2.2已經以後的版本,必須支援方法2。
鑒於市場上計算機已經均支援SMBIOS2.3標准,所以只考慮方法2,基於表結構的訪問方式。
基於表結構訪問SMBIOS的過程是先找到EntryPoint Structure(EPS)表,然後通過EntryPoint Structure(EPS)表的數據找到SMBIOS數據表。
訪問SMBIOSEPS表的操作過程如下:
1.從物理内存0xF0000-0xFFFFF間尋找關鍵字“_SM_”
2.找到後再向後16個字節,看後面5個BYTE是否是關鍵字“_DMI_”,如果是,EPS表即找到。
注:按照SMBIOS規範説明,找到關鍵字”_SM_”後就可以確定此處就是EPS表結構,但我在實際操作中發現有為數不少的電腦的指定64K内存中有不只一個“_SM_”,所以不能只用找到”_SM_”來確定,需要繼續判斷16個字節後是否是“_DMI_”。
SMBIOSEPS表結構如下:
位置 名稱 長度 描述
00H 關鍵字 4 byte 固定是“_SM_”
04H Check Sum 1 byte 用於檢查數據
05H 表結構長度 1 byte Entry Point Structure表的長度
06H Major版本號碼 1 byte 用於判斷SMBIOS版本
07H Minor版本號碼 1 byte 用於判斷SMBIOS版本
08H 表結構大小 2 byte 用於即插即用接口方法獲得數據表結構長度
0AH EPS修正 1 byte
0B-0FH 格式區域 5 byte 存放解釋EPS修正的訊息
10H 關鍵字 5 byte 固定為“_DMI_”
15H Check Sum 1 byte Intermediate Entry Point Structure(IEPS)的Check Sum
16H 數據表長度 2 byte SMBIOS數據表長度
18H 數據表位址 4 byte SMBIOS數據表的真實內存位置
1CH 數據表結構數目 2 byte SMBIOS數據表的結構數目
1EH SMBIOS BCD修正 1 byte
通過EPS表結構中的12H以及14H處(這裡說ㄉ應該是16h及18h,但我還是將原文呈現比較好),得出數據表長度和數據表地址,即可通過地址訪問結構表。從EPS表中的1CH處可得知結構表結構的總數,其中TYPE0結構就是BIOSinformation,TYPE1結構就是SYSTEMInformation。
每個結構的頭部是相同的,格式如下:
位置 名稱 長度 描述
00H Type number 1 byte 結構的type number
01H 長度 1 byte 本結的長度,就此type number的結構而言
02H Handle 2 byte 用於獲得SMBIOS結構,使用方法未知
每個結構都分為格式區域和字符串區域,格式區域就是一些本結構的信息,字符串區域是緊隨在格式區域後的一個區域。結構01H處標識的結構長度僅是格式區域的長度,字符串區域的長度是不固定的。
下面以TYPE0(BIOSinformation)為例説明格式區域和字符串區域的關系

TYPE0(BIOSinformation)格式區域如下:
位置 名稱 長度 描述
00H Type number 1 byte 結構的type number,此處是0
01H 長度 1 byte Type 0格式區域的長度,一般為14H,也有13H
02H Handle 2 byte 一般為0000H
04H BIOS廠商訊息 1 byte 此處是BIOS賣方的訊息,可能是OEM廠商名,一般為01H,代表緊隨格式區域後的字串區域的第一個字串
05H BIOS版本 1 byte BIOS版本號,一般為02H,代表字串區域的第二個字串
06H BIOS開始位址段 2 byte 用於計算常駐BIOS鏡像大小的計算,方法為(10000H-BIOS開始位址段)*16
08H BIOS發佈日期 1 byte 一般為04H,表示字串區第三個字串
09H BIOS ROM Size 1 byte 計算方法為(n+1)*64K,n為此處讀出的數值
0AH BIOS特徵 8 byte BIOS的功能支援特徵,如PCI,PCMCIA,FLASH等等
12H BIOS特徵擴展 不定
緊隨TYPE0(BIOSinformation)結構區域之後的就是TYPE0(BIOSinformation)字符串區域,如下所示:
db‘System BIOS Vendor Name’,0 ; 字符串以零結尾,第一個字符串:賣方訊息
db‘4.04’,0 ; 第二個:BIOS版本
db‘00/00/0000’,0 ; 第三個:發布日期
db0 ; 以0為整個字符區域的結尾,所以要找下一個TYPE,只要在字符區域找到連續的0000H即可
注:當有EPS表中得到結構表的開始地址後,可以直接按結構來尋找相應的TYPE號,找到後直接讀取就是該TYPE對應的結構的格式區域訊息,然後向後移動結構區域長度(結構區域長度由該結構的01H處讀出)個BYTE,即是該TYPE機構的字符串區域。
由上面介紹可知,獲得BIOS訊息的辦法就是:
1.通過EPS表的12H和14H數據找到TYPE結構表,然後找到TYPE0的内存地址。(不一定是首個)
2.由TYPE0結構區域中得出相應BIOS訊息是否存在(存在則是上面所述的 01H,02H,03H依次排布,不存在則是相應的位置上為00H)。
3.如存在訊息,則從字符串區域中讀取對應BIOS訊息。
獲得SYSTEM信息方法同上,只是TYPE結構區域有所不同,請參照SMBIOSReference Specification

在Linux環境裏可以使用dmidecode這支程式去dump SMBIOS的資料
dmidecode主要功能:
桌面管理介面提供標準化的電腦硬體描述,包括硬體的特性,比如 BIOS 的序號、與硬體連接線。dmidecode 提供由 BIOS 輸出 DMI 的資料,通常被其他硬體偵測程式當作後端工具使用。
轉載來源:
http://cupidsd.bokee.com/4052760.html\
其它參考資料:
System Management BIOS (SMBIOS) Specification
Desktop Management Interface (DMI) Standards
一篇世新的碩論-SMBIOS的研究與實作 寫得算蠻完整的唷
CodeGuru SMBIOS Demystified Windows VC++6.0
張貼者: 史丹利 位於 下午 9:05
--> 閱讀更多...

2009年4月9日 星期四

●有關32位元作業系統記憶體4G位址空間的局限

這篇文章網路有很多,但是對玩記憶體的我來說,卻不可不知。
有關32位元作業系統記憶體4G位址空間的局限:
4GB位址空間的局限:
首先我們還必須要先瞭解兩個概念
其一是“實體記憶體”。大家常說的實體記憶體就是指安裝在主板上的記憶體條,其實不然,在電腦的系統中,實體記憶體不僅包括裝在主板上的記憶體條(RAM),還應該包括主板BIOS晶片的ROM,顯卡上的顯存(RAM)和BIOS(ROM),以及各種PCI、PCI-E設備上的RAM和ROM。
其二是“位址空間”。位址空間就是對實體記憶體編碼(位址編碼)的範圍。
所謂編碼就是對每一個物理存儲單元(一個位元組)分配一個唯一的位址號 碼,這個過程又叫做“編址”或者“位址映射”。這個過程就好像在日常生活中我們給每家每戶分配一個位址門牌號。與編碼相對應的是“定址”過程——分配一個 位址號碼給一個存儲單元的目的是為了便於找到它,完成資料的讀寫,這就是“定址”,因此位址空間有時候又被稱作“定址空間”。系統不僅要給主板上的記憶體條 編址,還要給上述的其他實體記憶體編址;它們都被編在同一個位址空間內,編址後的實體記憶體就可以被系統資源使用或佔用。
從Pentium Pro處理器開始,CPU的 位址匯流排已經升級到36位元,定址能力達到64GB,按理說CPU支援4GB的記憶體是沒有問題的;因此,晶片組(北橋—MCH)位址匯流排的數量就成了決定物 理記憶體位址空間大小的決定性因素。在Intel 945系列和945以前的晶片組,nForce 550系列和550以前的晶片組都只有32條位址線,為系統提供4GB的位址空間,即最高可以安裝4GB的記憶體條。
雖然可以安裝4GB記憶體條,但這4GB的記憶體空間不能全部紛配給記憶體,因為從4GB空間的頂端位址(FFFF_FFFFh)開始向下要有400MB-1GB的位址空間要分配給主板上的其他實體記憶體。

我們可以看到4GB的位址空間可以分為兩大部分,0MB~實體記憶體頂端 的地址分配給主板上安裝的實體記憶體,4GB到實體記憶體頂端的位址分配給BIOS(ROM)和PCI/PCI-E設備的記憶體。由於這些記憶體基本上是用於 系統的輸入和輸出,所以Intel又把這段位址空間稱之為“MMIO”(Memory-Mapped I/O—I/O記憶體映射)。當系統安裝3GB以下的記憶體時,MMIO區域不會與實體記憶體條的位址空間相重疊,作業系統可以訪問幾乎全部的實體記憶體,而操 作系統屬性裏顯示的實體記憶體基本接近實際記憶體的容量。
而當系統安裝上4GB記憶體時,問題出現了。由於位於4GB下面的部分位址空間要優先分配給MMIO,記憶體條上對應的這段區間就得不到編址,所以作業系統就不能使用。
嚴格意義上來說,即使安裝2GB記憶體時作業系統也不可能使用到全部的內 存容量,諸如傳統DOS的UMA區就有部分被佔用的位址空間,但因為被佔用的容量相比之下實在太少,所以就被很多讀者忽略了。MMIO佔用的位址空間在 256MB~1GB之間,這麼大的“浪費”大家肯定不能“熟視無睹”。
因為受4GB晶片組位址空間的限制(32條位址線的限制),Intel 945系列及以前的晶片組、NVIDIA nForce 550及以前的晶片組都沒有辦法繞過這個限制。具體原因有三方面:其一是晶片組沒有剩餘空間分配來供作業系統來調配;其二是實體記憶體的編址必須是連續的, 不能被割斷;其三是系統開機時必需先從4GB的頂端位址(FFFF_FFFFh)讀取BIOS資料, 這是IA32架構和4GB位址空間的局限.
所以建議使用這些晶片組主板的用戶不要安裝4GB的記憶體,這樣會有部分記憶體容量不能被作業系統所使用。而解決4GB記憶體限制的唯一辦法就是擴展位址空間。

二、支援大於4GB記憶體的晶片組和“記憶體重映射”技術
面對原有晶片組4GB記憶體的局限,Intel和NVIDIA早就開始未雨綢繆,他們對傳統的32位元位址匯流排進行了 調整,將其升級到36位,並推出了一系列可以突破4GB記憶體限制的晶片組,這就是Intel的965系列以及975系列、NVIDIA的nForce 570/590以及680系列

注:AMD的64位元Socket AM2 CPU把記憶體控制器放到CPU中,提供40bit的物理位址匯流排,位址空間可達到1000GB。具體支援的位址空間和記憶體量取決於晶片組及主板的匯流排設計。
從上面的晶片組參數來看,位址匯流排從32位元提升到36位,位址空間達到 64GB,支持安裝8GB的實體記憶體。但由於IA32架構的規則是開機時必須從4GB的FFFF_FFFFh位址讀取BIOS資訊,儘管晶片組支援的位址 空間變大了,且最大支援的實體記憶體容量也達到了8GB(或以上),但從本質上來說仍然不能解決MMIO位址佔用4GB記憶體編址的問題。這要怎麼辦呢?
36位元位址匯流排最大可以支援64GB的位址空間,這就為移動MMIO地 址區提供了條件。現在解決這個問題的辦法就是“記憶體重映射”技術——就是在IA32架構的基礎上,把BIOS(ROM)和PCI/PCI-E設備佔用的 MMIO位址區段重新映射到記憶體條頂端位址以上 (例如4GB以上)的位址空間,從而把IA32架構規定的這一段作業系統不可使用的、位於4GB下面的MMIO位址空間回收給實體記憶體使用,保證實體記憶體 編址的連續性。

三、BIOS必須支援“記憶體重映射”
“記憶體重映射”技術必須通過BIOS完成。所以BIOS必須具有支援記憶體重映射的功能模組,以便根據用戶安裝的 記憶體容量來確定是否需要啟用記憶體重映射功能。同時,在BIOS的設置選單中也要有“Memory Re-Mapping”的設置選項,使用4GB或者4GB以上記憶體的用戶一定要將此項設置設為“Enable”。

四、解決4GB記憶體問題還需要作業系統支援
我們常使用的桌面作業系統是32位元的,支援4GB的位址空間。前面我們介紹了解決4GB問題的晶片組是支持 64GB位址空間的,在這樣的晶片組主板上安裝32位元的作業系統,就只能使用4GB的位址空間,因此安裝4GB記憶體不能使用僅支援4GB位址空間的32位元 的作業系統。應該使用支援大於4GB位址空間的32位元作業系統或64位元的作業系統。

五、小結.
1、由於iA32架構要求BIOS(ROM)晶片的位址, PCI、PCI-E記憶體位址、APCI中斷路由位址等必須佔用從4GB開始以下的256M-1GB空間。這段MMIO位址區不能分配給記憶體條。4GB的記憶體條有256MB-1GB的容量不能編址而浪費。
2、使用4GB或者以上的記憶體條,必須使用位址(編址)空間64GB的晶片組主板。
3、記憶體重映射就是把被MMIO佔用的位址移到記憶體條容量以上的位址空間。)
4、BIOS應具有支援“記憶體重映射”功能,設置項裏有 Memory Remap Feature 選項,並設置為Enable。
5、必須安裝定址空間大於4GB的作業系統。比如Windows 2000高級伺服器版,以及64位元作業系統。
6、所有位址空間為4GB的晶片組(Intel945和nForce550之前的),和32位元作業系統均不能利用“記憶體重映射”技術解決4GB記憶體問題。

跟這有關ㄉ討論還有這ㄍ連結
--> 閱讀更多...

2009年4月6日 星期一

●memtest86+教學 Part9





今天我們要談mem_size();這ㄍroutine;還記得上一章我們已經呼叫完int 15h ax=e820h;接下來我們介紹2ㄍstruct:來將我們呼叫15h中斷所得到ㄉ資料儲存到這裡來
struct mem_info_t {
unsigned long e88_mem_k; /* 0x00呼叫int15h時 eax=e88h,結果存放於此 */
unsigned long e801_mem_k; /* 0x04呼叫int15h時 eax=e801h,結果存放於此 */
unsigned long e820_nr; /* 0x08呼叫int15h時 eax=e820h,e820map總數存放於此 */
struct e820entry e820[E820MAX]; /* 0x0c 開始存放以20bytes為單位的e820 的structure*/
/* 0x28c此address是最多32個structure的end位址 */
};
struct e820entry {
/*此結構專門用來存放呼叫int15h時 eax=e820h時所得到的資訊,以便以c語言來處理這些資料*/
unsigned long long addr; /* start of memory segment long long是8個bytes */
unsigned long long size; /* size of memory segment long long是8個bytes */
unsigned long type; /* type of memory segment long是4個bytes 以上便是一個20byte的資料結構*/
};
主程序呼叫子程序以後以”=>”這個符號來表示呼叫由以上兩個structure來說明下面的程式架構: Init()=>query_pcbios()因此進入head.S並且切換cpu進入real mode,執行int15h中斷eax=e820h,因此獲得
memory map數量,最多32個,其實只有3個到5個;依照你所插入ㄉ記憶體容量及各家主機板BIOS實作方法不同而定。接著Init()=>mem_size()並將memory map配置給e820entry。 然後mem_size()=>memsize_bios(0)=>memsize_820(0)=>sanitize_e820_map(e820, nm, e820_nr, res)。
sanitize_e820_map就是整理所有memory map的routine(他(這ㄍROUTINE)比較繁雜,我都看不下去)。在此先說明memsize_bios(),它只是去判斷我們是透過什麼方式取得memory map,我們都知道目前應該都會是query_pcbios()來取得,所以代入參數0。
至於memsize_820(res)也是代入0為參數,那麼此routine做了些什麼,我們來看看:

static void memsize_820(int res) {
int i, n, nr;
struct e820entry nm[E820MAX];

/* Clean up, adjust and copy the BIOS-supplied E820-map. */
/* If the res arg is true reclassify reserved memory as E820_RAM */
nr = sanitize_e820_map(e820, nm, e820_nr, res);

/* If there is not a good 820 map use the BIOS 801/88 info */
if (nr <> E820MAX) {
memsize_801();
return;
}
/* Build the memory map for testing */
n = 0;
for ( i=0 ; ( i < nr ) ; i++ ) { if (nm[i].type == E820_RAM) { unsigned long long start; unsigned long long end; start = nm[i].addr; end = start + nm[i].size;
/* Don't ever use memory between 640 and 1024k */
if (start > RES_START && start < RES_END){
if (end < RES_END){
continue;
}
start = RES_END;
}
if ( end > RES_START && end < RES_END){
end = RES_START;
}

此顏色區域目的是要避開640k~1024k因此只要任何memory map 有涵蓋此區域其start或end就會被設定到640k為end或設定1024k 為start。(這部分是dos作業系統及bios專用空間,當然也包括我們程式要做顯示畫面0xb8000到0xb8fff這塊記憶體,所以這裡測試程式永遠都不會來測這一小塊。)
v->pmap[n].start = (start + 4095) >> 12;
v->pmap[n].end = end >> 12;
v->test_pages += v->pmap[n].end - v->pmap[n].start;
n++;
/*pmap+0xfff(4095)只是作4k為單位前的無條件進位。*/

/*v->pmap[n].end - v->pmap[n].start//便是求算出以4k為單位的總頁數。*/
}
else {
/* If this is reserved memory starting at the top
* of memory then don't count it as reserved, since
* it is very unlikely to be real memory.
*/
if (nm[i].addr <>reserved_pages += nm[i].size >> 12 }
}
v->msegs = n;
if (res) { cprint(LINE_INFO, COL_MMAP, "e820-All"); }
else { cprint(LINE_INFO, COL_MMAP, "e820-Std"); } }
●以上程式碼要看n平方次才會懂ㄉ,尤其是那ㄍ
sanitize_e820_map
--> 閱讀更多...

2009年4月5日 星期日

●Linux ubuntu簡介


由於從前安裝linux都是使用一棵hd,安裝多作業系統的方式,但是每次要到其他os工作就必須reboot;這樣其實對我ㄍ人來說極為不便;更何況自從有ㄌvmware及virtual PC或Sun virtual Box我個人就不再玩多作業系統於單一hd。而且還有samba 支援,做起事來可以在單機游走於windows及linux,真是快活。
linux:當然Fedora及Red_hat都不錯,但我想debian更多人用ㄅ。ubuntu也是源自於debian。

首先我還是說一下版本問題及喜好:ubuntu-8.10-desktop-i386.iso、ubuntu-8.10-alternate-i386.iso,若你想安裝最新版ubuntu 9.04應該快ㄌ,現在只有beta版,所有我還是,建議使用8.10版。ubuntu-8.10-desktop-i386.iso這ㄍ安裝是圖形介面所以建議虛擬機器至少配置512mbㄉ記憶體;安裝過程可能會有些螢幕小問題要解決如下。ubuntu-8.10-alternate-i386.iso這是installs on systems with less than about 256MB of RAM版很好用,虛擬機器至少配置256mb跑起來就很好很順ㄌ。

Desktop版本我使用virtial pc及Sun xVM virtualBox安裝都沒問題。安裝完若覺得螢幕解析度不符合您ㄉ須求可透過以下設定:上圖是我在(桌機)安裝好ㄉ圖例(我nb是裝virtual pc一樣沒問題)。若您安裝ㄉ板本較舊(7.04或7.10或8.04)可能相對問題會增加。至於音效說明請往下看#vim /etc/X11/xorg.conf

Section “Device”Identifier “Configured Video Device”
Driver “vesa”
Option “UseFBDev” “true”
EndSection

Section “Monitor”
Identifier “Configured Monitor”
Option “DPMS”
HorizSync 30-70
VertRefresh 50-160

EndSection

Section “Screen”
Identifier “Default Screen”
Monitor “Configured Monitor”
Device “Configured Video Device”
DefaultDepth 16
SubSection “Display”
Depth 16
Modes "1280×1024" "1024×768" "800×600"
EndSubSection

EndSection


--> 閱讀更多...

2009年4月2日 星期四

●memtest86+教學 Part8

static void display_init(void)
{
int i;
volatile char *pp;
/*只要是印出資料一定會宣告這個指標,volatile表示可變ㄉ,若不加沒事,但compile可能會發出警告。*/

serial_echo_init();
serial_echo_print("\x1B[LINE_SCROLL;24r"); /* Set scroll area row 7-23 */
serial_echo_print("\x1B[H\x1B[2J"); /* Clear Screen */
serial_echo_print("\x1B[37m\x1B[44m");
serial_echo_print("\x1B[0m");
serial_echo_print("\x1B[37m\x1B[44m");
/*以上6行是com port的輸出到(uart),若你在config.h將#define SERIAL_CONSOLE_DEFAULT 0改成1,並接上跳線,從你的os開起一個console 並設好鮑率,你就會接收看到一ㄍ長像和memtest一樣ㄉ單色畫面,聰明的你應該就知道上面那些長像奇特ㄉ鳥東西一定是字元控制碼,可能和uart輸出位置或什麼有關,但是很sorry,我也找不到這些控制字元的specifiction,若是你有,煩請mail一份給我。反正現在重點不是這ㄍ,只要遇到serial_echo_print你可以先不理他*/
for(i=0, pp=(char *)(SCREEN_ADR); i lss 80*24; i++) { *pp++ = ' '; *pp++ = 0x17; } /* 以上是清除畫面;0x17是藍底白色字屬性;但印出空白所以看不到白色字 */ /* Determine the memory map */ if ((firmware == FIRMWARE_UNKNOWN) && (memsz_mode != SZ_MODE_PROBE)) { if (query_linuxbios()) {
/*linuxbios目前support這種架構ㄉ主機板雖不多,但會不會成為主流也不知道,畢竟memtest是GPL,所以一定會挺同門師兄弟,將這個routine加進來。但畢竟所有主機板目前還是會支援正統ㄉbios,所以這ㄍroutine呼叫失敗也沒關係,下面那ㄍquery_pcbios一定會成功;什麼,不成功會怎樣,就什麼都別玩了,關機去休息吧。*/
firmware = FIRMWARE_LINUXBIOS;
}
else if (query_pcbios()) {
firmware = FIRMWARE_PCBIOS;
}
}
/*query_pcbios()你不會要問我這ㄍroutine在哪裡ㄅ;在爛豬腳裡;所以豬腳飯才那麼好吃;但是你會想我們不是好不容易才走出組合語言的陰影ㄇ,現在又要進去"拉d賽",沒辦法誰叫它的名字叫"X86",因為我們要去呼叫int 15h,但是中斷偏偏又只能在真實模式,所以只好又要重新出、進保護模式*/
.globl query_pcbios//匯出到符號表;這是一定要ㄉ,否則C呼叫不到Assembly的函式
query_pcbios:
‧‧‧
‧‧中間這一段我之前也trace很辛苦,所以先不說,
‧但不是我喜歡藏功夫,而是又一言難盡。改天再續‧‧

/* switch to 16bit mode */切到真實模式
ljmp $REAL_CS, $1f - RSTART
1:
.code16
/* Disable Paging and protected mode */
/* clear the PG & PE bits of CR0 */
movl %cr0,%eax
andl $~((1 SHL 31)(1 SHL 0)),%eax #關閉分頁及保護模式,回到真實模式
#Try three different memory detection schemes. First, try
# e820h, which lets us assemble a memory map, then try e801h,
# which returns a 32-bit memory size, and finally 88h, which
# returns 0-64m

# method E820H:
# the memory map from hell. e820h returns memory classified into
# a whole bunch of different types, and allows memory holes and
# everything. We scan through this memory map and build a list
# of the first 32 memory areas, which we return at [E820MAP].
# This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm
取得記憶體的分佈圖,以上介紹ㄉ是ax=e820h,int 15h中斷和e801h88h,由於歷史因素,記憶體在系統啟動後分佈並不是連續性的,因此我們必須非常確定它ㄉ分佈狀況,下面ㄉ代碼便是實作這三種方法。而且後兩者是較舊的主機板不支援e820時ㄉ應變之道,況且這些機種早就沒人用ㄌ,多舊?486ㄅ及更早;所以主流一定會support e820h,你放心。下面ㄉcode e820h 網路上一定找ㄉ到說明。我就不說,什麼,小氣;再說又要叫你買書ㄌ,我又不是"37仔"。
meme820:
xorl %ebx, %ebx # continuation counter
movw $E820MAP, %di # point into the whitelist
# so we can have the bios
# directly write into it.

jmpe820:
movl $0x0000e820, %eax # e820, upper word zeroed
movl $SMAP, %edx # ascii 'SMAP'
movl $20, %ecx # size of the e820rec
pushw %ds # data record.
popw %es
int $0x15 # make the call
jc bail820 # fall to e801 if it fails

cmpl $SMAP, %eax # check the return is `SMAP'
jne bail820 # fall to e801 if it fails

# cmpl $1, 16(%di) # is this usable memory?
# jne again820

# If this is usable memory, we save it by simply advancing %di by
# sizeof(e820rec).
#
good820:
movb (E820NR), %al # up to 32 entries
cmpb $E820MAX, %al
jnl bail820

incb (E820NR)
movw %di, %ax
addw $E820ENTRY_SIZE, %ax
movw %ax, %di
again820:
cmpl $0, %ebx # check to see if
jne jmpe820 # %ebx is set to EOF
bail820:


# method E801H:
# memory size is in 1k chunksizes, to avoid confusing loadlin.
# we store the 0xe801 memory size in a completely different place,
# because it will most likely be longer than 16 bits.

meme801:
stc # fix to work around buggy
xorw %cx,%cx # BIOSes which dont clear/set
xorw %dx,%dx # carry on pass/error of
# e801h memory size call
# or merely pass cx,dx though
# without changing them.
movw $0xe801, %ax
int $0x15
jc mem88

cmpw $0x0, %cx # Kludge to handle BIOSes
jne e801usecxdx # which report their extended
cmpw $0x0, %dx # memory in AX/BX rather than
jne e801usecxdx # CX/DX. The spec I have read
movw %ax, %cx # seems to indicate AX/BX
movw %bx, %dx # are more reasonable anyway...

e801usecxdx:
andl $0xffff, %edx # clear sign extend
shll $6, %edx # and go from 64k to 1k chunks
movl %edx, (E801) # store extended memory size
andl $0xffff, %ecx # clear sign extend
addl %ecx, (E801) # and add lower memory into
# total size.

# Ye Olde Traditional Methode. Returns the memory size (up to 16mb or
# 64mb, depending on the bios) in ax.
mem88:

movb $0x88, %ah
int $0x15
movw %ax, (E88)

#ifdef APM_OFF
# check for APM BIOS
movw $0x5300, %ax # APM BIOS installation check
xorw %bx, %bx
int $0x15
jc done_apm_bios # error -> no APM BIOS

cmpw $0x504d, %bx # check for "PM" signature
jne done_apm_bios # no signature -> no APM BIOS

movw $0x5304, %ax # Disconnect first just in case
xorw %bx, %bx
int $0x15 # ignore return code

movw $0x5301, %ax # Real Mode connect
xorw %bx, %bx
int $0x15
jc done_apm_bios # error

movw $0x5308, %ax # Disable APM
mov $0xffff, %bx
xorw %cx, %cx
int $0x15

done_apm_bios:
#endif
‧‧‧
‧‧

/* Turn on protected mode */切回保護模式
/* Set the PE bit in CR0 */
movl %cr0,%eax
orl $(1 shl 0),%eax #回到保護模式
movl %eax,%cr0
data32 ljmp *(protptr - RSTART)/*跳到下一行的32bits code繼續執行。這種語法未來會介紹。*/
prot:
.code32
/* Reload other segment registers */
movl $KERNEL_DS, %eax /*32bits code*/
movl %eax, %ds
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
movl %eax, %ss

/* Adjust the stack pointer */
leal stack@GOTOFF(%ebx), %eax
addl %eax, %esp

/* Restore the caller saved registers */
popl %ebp
popl %edi
popl %esi
popl %ebx
movl $1, %eax
ret //query_pcbios()到此結束
這ㄍret就是query_pcbios()ㄉ返回,你看它反回前作ㄌ一件事,就是eax設為1,所以,以下這ㄍif條件成立,因為c編譯器預設就是將eax當成return code。
else if (query_pcbios()) {
firmware = FIRMWARE_PCBIOS;
}
待續‧‧‧
--> 閱讀更多...

2009年4月1日 星期三

●老是記不住ㄉ指令


P.S.:上圖是給使用vim及viㄉ使用者一ㄍ很好記指令ㄉ圖檔。你也可以將它印出來隨時用隨時查。
在virtual PC安裝ubuntu linux text mode,由於要讓samba啟用必須作一些設定,其實很簡單,只要確定安裝linux時有選取samba,因此進入ubuntu之後盡可能不要用root身份來操作。
1新增使用者:指令adduser
EX:adduser -a 名稱
2修改user為root權限:需修改/etc/sudoers這ㄍ檔案
3設定samba指令:smbpasswd -a 名稱
並修改/etc/samba/smb.conf
4修改電腦名稱:要改2ㄍ地方,/etc/hostname及/etc/hosts
5(非必要)另外若同一網段MAC ADDR衝突也必須作設定:
/etc/udev/rules.d/70-persistent-net.rules
6(非必要)鎖住或關閉其他用戶ㄉ指令:passwd -L 名稱

以root身份執行工作,所以必須切換為rootㄉ指令:sudo -i

tar壓縮/解壓縮指令如下
檔案格式 壓縮 解壓縮 範例
bz2 -cjf -xjf tar -xjf xxx.bz2
gz -czf -xzf tar -xzf xxx.gz
選項與參數說明:指令說明是參考鳥哥的私房菜
-c:建立打包檔案,可搭配 -v 來察看過程中被打包的檔名(filename)。
-t:察看打包檔案的內容含有哪些檔名,重點在察看『檔名』就是了;
-x:解打包或解壓縮的功能,可以搭配 -C (大寫) 在特定目錄解開特別留意的是, -c, -t, -x 不可同時出現在一串指令列中。
-j:透過 bzip2 的支援進行壓縮/解壓縮:此時檔名最好為 *.tar.bz2-z :透過 gzip 的支援進行壓縮/解壓縮:此時檔名最好為 *.tar.gz。
-v:在壓縮/解壓縮的過程中,將正在處理的檔名顯示出來!
-f filename:-f 後面要立刻接要被處理的檔名!建議 -f 單獨寫一個選項!
-C 目錄:這個選項用在解壓縮,若要在特定目錄解壓縮,可以使用這個選項。
其他後續練習會使用到的選項介紹
-p :保留備份資料的原本權限與屬性,常用於備份(-c)重要的設定檔。
-P :保留絕對路徑,亦即允許備份資料中含有根目錄存在之意。
--exclude=FILE:在壓縮的過程中,不要將 FILE 打包!

/*****************************************************/

/************* SecureCRT Serial com port設定*************/

/*****************************************************/
Session Option:
Serial/Flow Control:RTS/CTS
Modes:initial mode:new line mode 及Application krypad
current mode:和initial mode一樣

Global Option:預設值
--> 閱讀更多...

2009年3月31日 星期二

●DJGPP和保護模式

本文轉貼於http://hengch.blog.163.com/
DOS不能工作在保護模式
讓CPU工作在保護模式很容易,但CPU工作在保護模式時你無法調用DOS和BIOS服務,為什麼呢?因為DOS和BIOS的代碼是按照在真實模式下運行的方式寫的,不符合保護模式程式的規範,比如,在真實模式下,DOS下的程式碼可以把任意數值放到段寄存器(CS、DS、ES、SS)中,只要不超過64K就可以,但在保護模式下,段寄存器只能放一個已經存在的selector的值,任何其它的值都將引起一個“General Protection Fault”錯誤異常。
所以,如果程式讓CPU進入保護模式,並且調用DOS服務,比如列印一行資訊,將馬上使系統崩潰,如果你不明白這一點,恐怕連一個最簡單的“Hello World”程式都寫不出來。
更糟糕的是,雖然應用程式不能調用DOS和BIOS服務,但DOS和BIOS卻必須要運行,比如時鐘晶片產生的硬體中斷,每秒18.2次的時鐘中斷等,時鐘中斷是BIOS的一部分,工作在真實模式。
所以,哪怕程式不調用任何真實模式代碼,一些非同步的系統事件仍然會發生,機器仍然會很快崩潰,所以,要在DOS下進入保護模式,必須首先解決DOS/BIOS和保護模式之間的這種衝突。
DOS Extender允許DOS和保護模式共存
如果你不想寫一個保護模式下的作業系統來完全取代DOS和BIOS,解決衝突的辦法是在你的應用程式和DOS/BIOS之間加入一個軟體層,這個軟體層可以視情況讓CPU在真實模式和保護模式之間切換,這個軟體層叫做DOS Extender。
在DOS Extender下,當保護模式下的程式要調用真實模式下的服務時,Extender給這個調用設置一個陷阱,把CPU切換到真實模式,再重新發出 這個調用,等待其完成調用,然戶再切換回保護模式,並返回到那個調用真實模式代碼的應用程式中,像時鐘中斷、鍵盤這樣的硬體中斷,也會被Extender設 置一個陷阱,並產生保護模式到真實模式的切換和返回。
你可能會想,這種方式會使應用程式運行變的很慢,然而在實際應用中,大多數程式並不會頻繁地調用OS的服務,即使調用很多,由於大多數的OS服務都是存取外部設備,比如硬碟,而這些設備的速度比起CPU而言是非常慢的,所以很少有人注意到模式切換上的系統開銷。
DJGPP v1.X和go32 Extender
在DJGPP v1.x中使用的go32程式,就是這樣一個DOS Extender,每個程式啟動時都會自動地裝載go32,go32除了完成DOS Extender的任務外,還接管下面一些與DJGPP相關的任務。
• 裝載應用程式,並為運行做好準備
由於DJGPP的執行檔使用COFF格式,DOS看不懂這種格式,go32負責讀取 COFF頭並初始化代碼、資料和其它檔頭中的分段
• 提供UNIX形式的命令列擴展
• 保護模式下浮點運算模擬
• 圖形支援
go32中有一些特殊的代碼,使其能夠適應各種各樣的進入保護模式的方法並管理擴展記憶體,所以他可以工作在任何DOS配置下,但這也使其產生一些不能忽視的缺陷,就是Extender必須被裝入常設記憶體,並且每個程式實例大約要佔用130KB的記憶體,大多數情況下DOS啟動以後都會有500-600K剩餘的常設記憶體,這意味著一個DJGPP程式只能有3-4級的嵌套。這是一個很嚴重的限制,DJGPP v2.x已經解決了這個問題。
DJGPP v2.X與DPMI服務
DJGPP v2.x放棄了Extender,取而代之的是需要一個已經運行的DPMI服務,DPMI:DOS Protected-Mode Interface的縮寫,是一個特殊的API,它允許保護模式的應用程式在DOS的上層運行,他定義了 一些函數,使保護模式下的程式(稱為DPMI客戶)可以做一些諸如:進入保護模式、分配記憶體和段描述符、調用真實模式的服務、連接中斷等等,許多使用 Intel CPU的作業系統都有DPMI服務,包括windows的所有版本,OS/2,以及LINUX DOS模擬器都是著名的例子,還有一些持有專 利的DOS下的DPMI伺服器,通常和DOS的記憶體管理器捆綁在一起,比如QEMM和386MAX,FreeDOS 也包含有一個DPMI伺服器作為缺省設置的一部分,對於那些沒有DPMI伺服器的系統,DJGPP v2.x提供一個免費的DPMI伺服器,叫做 CWSDPMI,CWSDPMI很多地方使用了go32的代碼,DJGPP啟動代碼檢查DPMI服務,如果沒有,將自動搜索並載入 cwsdpmi.exe----CWSDPMI伺服器。
DPMI伺服器(又稱為DPMI HOST)可以解決運行在DOS上層的保護模式程式的大部分問題,餘下的那些在1.x版本中由go32完成的函數,在v2.x中被DJGPP的啟動代碼接管並放在低級函式程式庫中,下面簡短地說一下DJGPP啟動代碼的兩個特點。
DJGPP v2.x的啟動代碼
DJGPP v2.x啟動代碼包括兩部分:短小的裝載程式和庫啟動模組,前者是由組合語言寫成,是一段有特殊作用的組合語言程式,叫做djasm,它是一段 16-bit的DOS可執行程式,這個短小的裝載程式會被連結到每一個DJGPP程式中,是唯一一部分可以被DOS識別的部分,其餘部分----COFF 可執行文檔----在DOS看來只是一些奇怪的資料。
第二部分是一個庫模組,它包括許多模組,有些用C寫成,有些用彙編寫成,當裝載程式把程式調入並初始化完成後,這裡就是COFF格式程式的入口點。
裝載程式完成以下工作:
• 為傳輸緩衝區申請記憶體
這個緩衝區用於在DOS服務和程式之間傳送資料。
• 檢查是否DPMI服務已經運行
以下兩種情況說明DPMI已經啟動
(1)有一個常駐記憶體的DPMI伺服器,比如windows中內置的DPMI伺服器
(2)當前程式是一個嵌套的DJGPP程式,他的父程式已經啟動了CWSDPMI
如果DPMI服務還沒有啟動,則裝入CWSDPMI。首先在目前的目錄下搜索cwsdpmi.Exe, 然後到環境變數PATH指定的目錄下去查找。
• 把COFF可執行部分的檔頭裝入記憶體
需要知道要為DJGPP程式申請多少記憶體
• 調用DPMI host提供的入口點,把CPU切換到保護模式
注意:裝入程式的其餘部分運行在保護模式下
• 為程式碼和資料申請記憶體空間
通過DPMI的功能調用可以為代碼和資料申請段描述符和記憶體空間,並設置基底位址、 界限和許可權。
• 把COFF格式的可執行部分調入記憶體
通過DPMI服務調用DOS(主要指檔操作)服務,把代碼、資料和BSS節讀入上 面申請的記憶體中,DPMI服務允許你從保護模式下調用真實模式下的服務。
• 跳轉到COFF鏡像的入口點執行
這個入口點在上面提到過的庫啟動模組中。
下面是庫啟動代碼部分完成的工作
• 生成一個不受約束的空頁
這將產生一個NULL Pointer dereference的錯誤,並將觸發一個異常處理,程式 會收到SIGSEGV信號,但這個功能並非基本DPMI 0.9規範中的一部分,所以 windows和其它許多有專利的DPMI伺服器並不支持這個功能,但CWSDPMI支援這 一功能。
• 改變申請記憶體的資料段的大小
這個聽起來簡單,但由於DPMI記憶體調用的特殊性,實際上是非常複雜的,例如: 它需要把一段真實模式下的16-bit代碼調入常設記憶體的緩衝區並運行。
• 設置程式的堆疊
DJGPP程式堆疊的缺省大小是512KB,但應用程式或改變設置都可以改變堆疊大小
• 為存取常設記憶體申請selector
與DOS/BIOS函數之間傳遞資料,或者像視頻界面上的顯示緩衝區那種使用記憶體 映射的設備,許多DOS程式需要存取常設記憶體,但由於在缺省情況下,常設記憶體並 沒有映射在程式的資料段中,為了存取常設記憶體,使用了一個特殊的selector---- _dos_ds。
• 初始化信號管理
需要連結一些硬體中斷,例如:按下CTRL-時產生的SIGINT信號。還有時鐘中 斷產生的SIGPROF信號等。
• 拷貝程式的環境變數到environ[]陣列中
• 讀出定義了DJGPP附加環境變數的檔
• 獲得並解釋命令列參數
• 如果需要,設置x87 FPU並載入浮點運算模擬器
• 調用靜態構造函數
• 調用用用程式的主函數
--> 閱讀更多...

博客心情

博客大陸人稱呼部落格為博客,不知為何寫博客會上癮,可能是我這ㄍ宅男實在太無趣ㄌ,所以才透過文字抒發心情,然而每次寫完一篇就會覺得心情好過些,就好像每天早上ㄉ那杯咖啡,我目前的工作整天就是在跟記憶體測試軟體打交道,雖說不上心得滿滿,但從剛開始的牙牙學語,到現在一年半載,至少該懂的應該也有七八成,Debug也成為我的嗜好之一,一開始抱持著以前寫ap都搞不懂(應該說領域不同,所以沒機會接觸更深入)的,這回有機會一定要以頃城之力來回報自己內心的不踏實。雖然我很想跳出這個dram產業去接觸更多‧‧‧,但其實不那麼迫切,因為我覺的自己還有成長的空間,學習x86這一塊,讓我感觸良多,畢竟這是整個IT產業的最大宗,然而我相信搞好這一塊,要跳到其它嵌入式系統應該熟悉度會加快。雖說技術一日千里,但只要你夠耐心及細心,我想你也可以日進萬里(內心ㄉ成長)。
--> 閱讀更多...

2009年3月30日 星期一

●分解BIOS

注意:本網站討論的部分內容不詳,還沒瞭解透,定義為:不清昕,可能有錯誤的。

這裡主要以Intel平臺的BIOS檔討論,輔助參考AMD平臺的BIOS文件。要分解的BIOS檔選了當下較新的X38晶片組平臺的ex38dq6.f2 這個BIOS檔,而BIOS的檔是ma79xds4.f4,這是AMD的最新的7系晶片組其中的790X晶片組平臺。好,下面開始進行分解ex38dq6.f2這個BIOS檔。

一、工具的使用
1、Ex38dq6.f2是Award Bios,有一個圖形化的BIOS編輯軟體awdbedit,可以很方便的將BIOS的元件分解出來。
2、通用的BIOS編輯軟體cbrom,這裡使用的是cbrom182版本。下面是使用cbrom182顯示BIOS元件的清單,命令列下使用:Cbrom182 ex38dq6.f2 /D,結果如下(部分):

******** ex38dq6.f2 BIOS component ********
No. Item-Name Original-Size Compressed-Size Original-File-Name
================================================================================
0. System BIOS 20000h(128.00K)15478h(85.12K)ex38dq6.BIN
1. XGROUP CODE 0FC40h(63.06K)0B0ECh(44.23K)awardext.rom
2. ACPI table 04E16h(19.52K)0193Ch(6.31K)ACPITBL.BIN
3. EPA LOGO 0168Ch(5.64K)0030Dh(0.76K)AwardBmp.bmp
4. GROUP ROM[18] 031D0h(12.45K)0225Ah(8.59K)ggroup.bin
5. YGROUP ROM 0C180h(48.38K)066E4h(25.72K)awardeyt.rom
6. GROUP ROM[ 0] 08210h(32.52K)0303Dh(12.06K)_EN_CODE.BIN
7. PCI ROM[A] 10000h(64.00K)09DBEh(39.44K)ICH9RAID.BIN
8. PCI ROM 03600h(13.50K)02553h(9.33K)ICH8AHCI.BIN
9. PCI ROM[C] 07A00h(30.50K)04479h(17.12K)JMB59.BIN
10. MINIT 08220h(32.53K)0824Fh(32.58K)DDR2_MRC.X38
11. PCI ROM[D] 0C800h(50.00K)079FDh(30.50K)rtegrom.lom
12. LOGO1 ROM 00B64h(2.85K)00520h(1.28K)dbios.bmp
13. LOGO BitMap 4B30Ch(300.76K)07EEEh(31.73K)x48dq6.bmp
14. GV3 01EFDh(7.75K)00B66h(2.85K)PPMINIT.ROM
15. OEM0 CODE 028ABh(10.17K)01E1Bh(7.53K)SBF.BIN
(SP) NCPUCODE 1D000h(116.00K)1D000h(116.00K)NCPUCODE.BIN

Total compress code space = E5000h(916.00K)
Total compressed code size = 75C8Dh(471.14K)
Remain compress code space = 6F373h(444.86K)

清單: 2.1

整個ex38dq6.f2 檔1M大小,包含了16個元件,最後的NCPUCODE.BIN元件,是虛擬的或者說物理上不存在,用awdbedit軟體分解不包括這個元件,實際上只有15個真實元件,這些元件全都是經過壓縮的。第2列是元件的名字,第3列是元件真實的大小,第4列是元件中部分壓縮的資料在ex38dq6.f2檔中的大小,最後1列是分解後元件存在磁片上的物理檔案名,以ex38dq6.bin為例,這個元件真實的大小為128K,其中85.12K是壓縮部分,其餘的以純代碼形式分佈在FE000 ~ FFFFF區域,典型地:第一條far jmp就分佈在這個區域。
ex38dq6.BIN 是BIOS的主體組件。
awardext.rom、awardeyt.rom 是BIOS的擴展部分。
ACPITBL.BIN 是供ACPI所使用的低級部件,可供作業系統使用。
PCI ROM 是 PCI 設備的一些元件。
還有一些顯示的BMP圖片
其餘組件不詳,有待瞭解
3、使用cbrom182來分解元件的方法:
Cbrom182 ex38dq6.f2 /XGROUP extract 分解出 awardext.rom
Cbrom182 ex38dq6.f2 /ACPI extract 分解出 ACPITBL.BIN
如此類推,可以逐步分解出各個元件,但是,SYSTEM BIOS元件,也即是 ex38dq6.bin 這個元件,我怎麼試也沒分解出來,所以用以下推薦的方法分解。

4、推薦分解BIOS元件的方法
使用圖形化的BIOS編輯軟體awdbedit可以很方便簡單分解全部的元件。運行awdbedit軟體,打開ex38dq6.f2,忽略掉一些警告資訊,進入後,選擇 [Actions] –> [Extract All] 就可以分解出全部的元件。

二、BIOS元件位置分析
1、ex38dq6.f2檔共1M大小,除了包含各個BIOS元件外,還充斥著大量的“填充碼”,這些“填充碼”是FF位元組以及00位元組,主要用來分隔各個元件,以及填充檔。
2、壓縮元件是以LZH形式壓縮,每個壓縮元件以“-lh5-”開頭,十六進位碼形式為 2D 6C 68 35 2D,這是壓縮組件的戳記,因此,在BIOS檔中只要尋找到這個戳記就可以區分開每個元件。
seg000:0000 24 F7 2D 6C 68 35 2D 50 54 01 00 00 00 02 00 00 $?lh5-PT ... ..
seg000:0010 00 00 50 20 01 0B 65 78 33 38 64 71 36 2E 42 49 ..P
ex38dq6.BI
seg000:0020 4E 24 D3 20 00 00 2D 20 8F 77 BF 74 89 29 BB AA N$?..- 弚縯?華
seg000:0030 7F 33 33 37 37 4D 07 73 55 45 55 78 35 91 D5 66 3377MsUEUx5懻f
seg000:0040 85 B7 54 49 34 52 21 0E 9B A5 10 91 11 BC 1D 28 叿TI4R!
洢 ??(
seg000:0050 B1 2A 66 A0 DD 5B BB BA 9C 0D 51 0C C5 17 AA F2 ?f犦[緩?Q
?
seg000:0060 FB DD BC AC AD 34 F1 55 DB 53 CC 03 DD A6 86 30 棘?馯跾?葒?
seg000:0070 2A CF 42 B5 DC 53 52 22 43 F0 75 84 66 40 00 77 *螧弟SR"C饀刦@.w
seg000:0080 7F FE 66 83 37 77 79 E7 9E BC F6 FF BD 7A FD EE �?wy鐬薦�絲
seg000:0090 BE FF 04 3E F7 76 B2 49 1B 6D C9 D1 4B 2D 15 A0 ? >鱲睮 m裳K- ?
seg000:00A0 AE 84 C4 52 58 5F FF CF ED 24 AC C1 42 64 1F F0 畡腞X_�享$Bd­?
seg000:00B0 BF 45 55 49 0A A2 CE C2 97 58 58 AF 0E 62 22 84 縀UI⑽聴XX?b"?
seg000:00C0 7E CF 94 2F E7 24 F7 E3 CE 0F 55 B8 E0 94 E0 D5 ~蠑/?縻?U膏斷?
seg000:00D0 D1 BE E0 9E FB 99 C1 F8 3B 86 C5 B8 86 6C 6B 85 丫酁麢柳;喤竼lk?
seg000:00E0 88 B3 F7 05 5A F0 BA CB C3 2E 5F 89 F8 AF ED B2 埑?Z鷙嗣._夬?
seg000:00F0 91 9C 42 50 B7 CA 60 34 B6 4A 55 8C 65 D3 8E EA 憸BP肥`4禞U宔訋?
seg000:0100 6A 5D E1 4F 7E DB 97 7F 4C A0 AE 9E 15 B7 8E 86 j]酧~蹢L牣?穾?

清單 2.2

3、以ex38dq6.f2為例,用十六制編輯軟打開,從00000000開始到000FFFFF共1M的大小,每個壓縮元件在ex38dq6.f2的物理位置如下:
0. 0 ~ 15477: System Bios (ex38dq6.bin)
1.15478 ~ 20563: XGROUP CODE(awardext.rom)
2.20564 ~ 21E9F: ACPI table(ACPITBL.BIN)
3. 21EA0 ~ 221AC: EPA LOGO(awardBmp.bmp)
4.221AD ~ 24406: GROUP ROM[18](ggroup.bin)
5.24407 ~ 2AAEA: YGROUP ROM(awardeyt.rom)
6.2AAEB ~ 2DB27: GROUP ROM[0](_EN_CODE.BIN)
7.2DB28 ~ 378E5: PCI ROM[A](ICH9RAID.BIN)
8.378E6 ~ 39E38: PCI ROM(ICH8AHCI.BIN)
9.10. 39E39 ~ 46500: PCI ROM[C]、MINIT(JMB59.BIN、DDR2_MRC.X38)
11.46501 ~ 4DEFD: PCI ROM[D](rtegrom.lom)
12.4DEFE ~ 4E41D: LOGO1 ROM(dbios.bmp)
13.4E41E ~ 5630B: LOGO BIGMAP(X48DQ6.bmp)
14.5630C ~ 56E71: GV3(PPMINIT.ROM)
15.56872 ~ 58C8C: OME0 CODE(SBF.BIN)

以上是各個壓縮元件在BIOS檔中的物理位置,從58C8D ~ FDFFF 這段區間中混合著一些資料,還在大量充斥著“填充碼”,沒有什麼實際的意義,或者說:沒看到什麼實際意義。從 FE000 ~ FFFFF 這段區間中,包含一些非壓縮的純二進位碼,其中有重要的BOOTBLOCK,以及一些初始化代碼。也包含著大量的“填充碼”。這些純代碼分散分佈在這個區間,純代碼與部分壓縮元件的混合在一起,幾乎很難區分哪些是純代碼,哪些是壓縮資料,指令:jmp far ptr 0F000:0E05B 與其它資料混合在一起,如下清單2.2所示:
seg000:FFB25 db 0C3h ; ?
seg000:FFB26 db 66h ; f
seg000:FFB27 db 0EFh ; ?
seg000:FFB28 db 8Bh ; ?
seg000:FFB29 db 0D7h ; ?
seg000:FFB2A db 8Eh ; ?
seg000:FFB2B db 0D9h ; ?
seg000:FFB2C ; ---------------------------------------------------------------------------
seg000:FFB2C jmp far ptr 0F000h:0E05Bh
seg000:FFB2C ; ---------------------------------------------------------------------------
seg000:FFB31 db 0
seg000:FFB32 db 0
seg000:FFB33 db 0
seg000:FFB34 db 0
seg000:FFB35 db 0
seg000:FFB36 db 0
seg000:FFB37 db 0
seg000:FFB38 db 0
seg000:FFB39 db 0

清單 2.3

4、BIOS的主體文件 ex38dq6.BIN的大小是128K,正好映射到系統位址空間的FFFE_0000 ~ FFFF_FFFF(E_0000 ~ F_FFFF)共128K的空間上。
當分解出BIOS主體元件ex38dq6.BIN後,這條指令就在F000:FFF0 的位置上,如下清單2.3所示:

seg000:FFFEC db 80h ; €
seg000:FFFED db 1
seg000:FFFEE db 0Ch
seg000:FFFEF db 89h ; ?
seg000:FFFF0 ; ---------------------------------------------------------------------------
seg000:FFFF0 jmp far ptr 0F000h:0E05Bh
seg000:FFFF0 ; ---------------------------------------------------------------------------
seg000:FFFF5 db 30h ; 0
seg000:FFFF6 db 32h ; 2
seg000:FFFF7 db 2Fh ; /
seg000:FFFF8 db 32h ; 2
seg000:FFFF9 db 37h ; 7
seg000:FFFFA db 2Fh ; /
seg000:FFFFB db 30h ; 0
seg000:FFFFC db 38h ; 8
seg000:FFFFD db 0
seg000:FFFFE db 0FCh ; ?
seg000:FFFFF db 0B3h ; ?
seg000:FFFFF seg000 ends

清單 2.4

在ex38dq6.BIN 檔中的FFFF0位置對應著物理FFFFFFF0這個位址上,第1條指令是far jmp,跳轉到BOOTBLOCK中,通常在這條指令的周圍是些有意議的字元描述,如:02/27/08這是BIOS日期,在far jmp 下麵處於BIOS尾端。

三、不同BIOS檔之間的異同

1、結構不同,AMI和award的BIOS是有差別的。
2、BIOS檔的大小不同,一般的BIOS檔大小為512K,以ma79xds4.f4為例,它是512K,ex38dq6.f2是1M,但結構上差什麼差異,下面是ma79xds4.f4的結構:

No. Item-Name Original-Size Compressed-Size Original-File-Name
================================================================================
0. System BIOS 20000h(128.00K)13944h(78.32K)ma79xds4.BIN
1. XGROUP CODE 0F7D0h(61.95K)0AB7Bh(42.87K)awardext.rom
2. ACPI table 06391h(24.89K)02B35h(10.80K)ACPITBL.BIN
3. EPA LOGO 0168Ch(5.64K)0030Dh(0.76K)AwardBmp.bmp
4. GROUP ROM[18] 03340h(12.81K)02339h(8.81K)ggroup.bin
5. YGROUP ROM 0B310h(44.77K)05023h(20.03K)awardeyt.rom
6. GROUP ROM[ 0] 07100h(28.25K)02C90h(11.14K)_EN_CODE.BIN
7. PCI ROM[A] 0C800h(50.00K)0AC37h(43.05K)sata22.bin
8. OEM1 CODE 0AE4Fh(43.58K)06B6Dh(26.86K)ui22.bin
9. PCI ROM[B] 0A800h(42.00K)06007h(24.01K)RTLGPXE.LOM
10. LOGO1 ROM 00B64h(2.85K)00520h(1.28K)dbios.bmp
11. OEM0 CODE 028ABh(10.17K)01E1Bh(7.53K)SBF.BIN
12. GV3 088C6h(34.19K)026FBh(9.75K)AGESACPU.ROM
13. MINIT 11B80h(70.88K)11BB3h(70.92K)MEMINIT.BIN
14. HTINIT 04BC0h(18.94K)04BF0h(18.98K)HT.DLL
15. 2 PE32 in MB 00552h(1.33K)00582h(1.38K)HT32GATE.BIN
(SP) NCPUCODE 04000h(16.00K)04000h(16.00K)NCPUCODE.BIN

Total compress code space = 63000h(396.00K)
Total compressed code size = 621F3h(392.49K)
Remain compress code space = 00E0Dh(3.51K)

清單 2.5

上面清單所示,與ex38dq6.f2的結構一樣,每個元件都有一部分是經過壓縮的。
--> 閱讀更多...

●跟著流程走(一):far jmp後發生什麼?




跟著流程走(一):far jmp後發生什麼?
一、所需材料
1、主要BIOS :ex38dq6.f2 :技嘉主機板上Intel X38 MCH + ICH9 平臺。
備用BIOS:ma79xds4.f4 : 技嘉主機板上AMD 790X 北橋 + SB600 南橋平臺。
2、cbrom:這是一個BIOS編輯工具,這裡所用的是cbrom182版本
3、lha2.55:LHA格式的解壓工具。
4、awdbedit:award bios 的圖形化編輯工具,方便簡單。
5、hex workshop:一個十六進位編輯工具,簡單小巧。
6、IDA:一個反彙編工具,這裡使用的是IDA 5.2版本

二、所需知識
1、組合語言:這是必備的知識,彙編掌握的程度和理解能力成正比。
2、機器語言:這個不是必需的,但推薦能夠讀懂機器語言,某些場合下當組合語言也陷入窘境時,機器語言是唯一的解釋手段。
3、x86體系知識:具體可以查看相關的Intel 或 AMD 手冊
4、ISA/PCI 匯流排知識:可以查看相應的 ISA/PCI Specification
5、north/south bridge 知識:Intel 現在以MCH代稱north bridge,ICH代稱south bridge,可以查看相應的 datasheet


接下來用IDA pro打開ex38dq6.BIN觀察,這個是BIOS的主體檔,跟著流程走,看看far jmp後BIOS做什麼工作。
1、第一條指令 jmp far ptr F000:E05B 經過幾個跳轉,跳到F000:F46C處
2、以下是F000:F46C的代碼:
seg000:FF46C cli
seg000:FF46D cld
seg000:FF46E xchg bx, bx
seg000:FF470 smsw ax
seg000:FF473 test al, 1
seg000:FF475 jz short near ptr 0F480h
seg000:FF477 cli
seg000:FF478 mov al, 0FEh ; '?
seg000:FF47A out 64h, al ; AT Keyboard controller 8042.
seg000:FF47A ; Resend the last transmission
seg000:FF47C cli
seg000:FF47D hlt
--------------------------------------------------------------
取機器狀態字,也就是CR0寄存器,測試CR0.PE是否為1,判斷CPU是否處於真實模式狀態,若不是則停機。
若處於真實模式轉到F000:F480繼續處理。
3、轉到F000:F480又經過一道跳轉,來到F000:E043進行處理
4、下麵是F000:E043的代碼:

seg000:FE043 mov al, 8Fh ; '? ; disable NMI# and get 0Fh offset register
seg000:FE045 out 70h, al ; CMOS Memory:
seg000:FE045 ;
seg000:FE047 out 0EBh, al
seg000:FE049 in al, 71h ; get OFh offset register data
seg000:FE04B out 0EBh, al
seg000:FE04D or al, al ; is RESET ?
seg000:FE04F jmp near ptr 0F483h

在這裡,取 CMOS RAM 中位於0F處1個位元組的資料,通過測試這個位元組是否為0,判斷是否屬正常啟動。
5、正常啟動的話,調用F000:54DE這個子過程進行處理,否則跳到F000:3468。
二、下面看看F000:54DE的處理,以下是第二張流程圖:
下面proc_F54DE的代碼:

seg000:F54DE mov ax, 0
seg000:F54E1 mov es, ax
seg000:F54E3 cmp word ptr es:472h, 1234h
seg000:F54EA jnz short near ptr 54F8h
seg000:F54EC mov al, 8Fh ; '?
seg000:F54EE out 70h, al ; CMOS Memory:
seg000:F54EE ;
seg000:F54F0 out 0EBh, al
seg000:F54F2 mov al, 0AAh ; '?
seg000:F54F4 out 71h, al ; CMOS Memory:
seg000:F54F4 ;
seg000:F54F6 out 0EBh, al
seg000:F54F8 mov dx, 3C4h
seg000:F54FB mov al, 1
seg000:F54FD out dx, al ; EGA: sequencer address reg
seg000:F54FD ; clocking mode. Data bits:
seg000:F54FD ; 0: 1=8 dots/char; 0=9 dots/char
seg000:F54FD ; 1: CRT bandwidth: 1=low; 0=high
seg000:F54FD ; 2: 1=shift every char; 0=every 2nd char
seg000:F54FD ; 3: dot clock: 1=halved
seg000:F54FE inc dl
seg000:F5500 in al, dx ; EGA port: sequencer data register
seg000:F5501 or al, 20h
seg000:F5503 out dx, al ; EGA port: sequencer data register
seg000:F5504 call near ptr 76FBh
seg000:F5507 retn

1、在BIOS資料區的0472處存放著一個重定標誌:
seg000:F54E3 cmp word ptr es:472h, 1234h
通過比較 [0472] 是否1234h,標誌1234h是一個暖開機標誌位元,機器暖開機時,例如:按下CTRL+ALT+DEL 三個鍵時,由鍵盤中斷處理常式在[0472]處寫標誌1234h。
2、是暖開機的話,將寫入AA標誌到CMOS RAM 的0F處。
3、接著設置EGA相應的工作狀態。
4、在proc_F76FB過程裡置計時器1的狀態。
5、最後調用過程proc_F2941進行晶片組的初始化。

三、下面是本站節的重點,初始化某部分晶片組,下面是流程圖:
1、下面重點理解 write_pci_byte這個BIOS提供的rontine,代碼如下:

seg000:FF798 xchg ax, cx ; write_byte routine
seg000:FF799 shl ecx, 10h
seg000:FF79D xchg ax, cx
seg000:FF79E mov ax, 8000h ; Bus 0
seg000:FF7A1 shl eax, 10h
seg000:FF7A5 mov ax, cx
seg000:FF7A7 and al, 0FCh
seg000:FF7A9 mov dx, 0CF8h ; config_address register
seg000:FF7AC out dx, eax
seg000:FF7AE add dl, 4 ; config_data register
seg000:FF7B1 mov al, cl
seg000:FF7B3 and al, 3
seg000:FF7B5 add dl, al
seg000:FF7B7 mov eax, ecx
seg000:FF7BA shr eax, 10h
seg000:FF7BE out dx, al
seg000:FF7BF retn

將這個routine功能簡化為C代碼形式來看比較直觀:
void wirte_pci_byte(int offset_number, int mask)
{
if (number == -1)
jmp_7666();

do_wirte_pci_byte(offset_number, mask);
}

這段routine固定寫PCI的Bus0,Device0,Function0,offset 值放在cx中,由調用者傳來,置什麼值放在al寄存器,這是1個位元組的值。Bus0,Dev0,Fun0是hostbrige控制器(NorthBridge),也即是DRAM控制器的地址所在。這段代碼是典型的寫PCI設置的手法。PCI設置位址送入config_address_register中,然後往config_data_register裡寫資料,這個PCI設備位址將映射到PCI設備的寄存器,如前面介紹的位址空間圖所示,PCI設備位址範圍是E000_0000 ~ EFFF_FFFF,這段空間提交到相應的PCI設備。
2、現在回過頭來看調用者,cx=95,al=33 這個參數傳給 write_pci_byte。Offset是95,mask碼是33。Offset 95在write_pci_byte將被置為94,這將是DRAM控制器的PAM4寄存器,PAM4寄存器控制D_8000 ~ D_FFFF記憶體空間的屬性。寫入33,結果是:將這段空間置為read/write屬性,這將是所有訪問這段空間的操作會提交到DRAM。而不再是ROM。

3、Offset 96的結果和offset 95一樣,在write_pci_byte的遮罩中被置為offset 94。
--> 閱讀更多...

●memtest86+教學 Part7

這一次我想從簡單部份說起;學code得基本原則就是debug,因此要debug就必需將我們不懂的地方把它show到螢幕的畫面上。所以我們先介紹以下這個函式:
/*
* Print characters on screen
*/
void cprint(int y, int x, const char *text)
{
register int i;
char *dptr;

dptr = (char *)(SCREEN_ADR + (160*y) + (2*x));
for (i=0; text[i]; i++) {
*dptr = text[i];
dptr += 2;
}
tty_print_line(y, x, text);//印出的UART
}
這是一個印出文字(字串)到螢幕上的Routine,相信它應該非常簡單,SCREEN_ADR0xb8000,這個位址就是bios映射到vga的彩色文字資料區;對這一區的記憶體讀寫就等同於對螢目frame buffer讀寫;相關資訊可參考這個網址。
接著我們去回想一下do_test有呼叫一個routine:init()
void init(void)
{
int i;

outb(0x8, 0x3f2); /* Kill Floppy Motor */

/* Turn on cache */
set_cache(1);

/* Setup the display */
display_init();

/* Determine the memory map */
if ((firmware == FIRMWARE_UNKNOWN) &&
(memsz_mode != SZ_MODE_PROBE)) {
if (query_linuxbios()) {
firmware = FIRMWARE_LINUXBIOS;
}
else if (query_pcbios()) {
firmware = FIRMWARE_PCBIOS;
}
}

mem_size();

/* setup pci */
pci_init();

/* setup beep mode */
beepmode = BEEP_MODE;

v->test = 0;
v->pass = 0;
v->msg_line = 0;
v->ecount = 0;
v->ecc_ecount = 0;
v->testsel = -1;
v->msg_line = LINE_SCROLL-1;
v->scroll_start = v->msg_line * 160;
v->erri.low_addr.page = 0x7fffffff;
v->erri.low_addr.offset = 0xfff;
v->erri.high_addr.page = 0;
v->erri.high_addr.offset = 0;
v->erri.min_bits = 32;
v->erri.max_bits = 0;
v->erri.min_bits = 32;
v->erri.max_bits = 0;
v->erri.maxl = 0;
v->erri.cor_err = 0;
v->erri.ebits = 0;
v->erri.hdr_flag = 0;
v->erri.tbits = 0;
for (i=0; tseq[i].msg != NULL; i++) {
tseq[i].errors = 0;
}
if (dmi_initialized) {
for (i=0; i <> 0) {
dmi_err_cnts[i] = 0;
}
}
}

cprint(LINE_CPU+1, 0, "L1 Cache: Unknown ");
cprint(LINE_CPU+2, 0, "L2 Cache: Unknown ");
cprint(LINE_CPU+3, 0, "Memory : ");
aprint(LINE_CPU+3, 10, v->test_pages);
cprint(LINE_CPU+4, 0, "Chipset : ");

cpu_type();

/* Find the memory controller (inverted from standard) */
find_controller();

if (v->rdtsc) {
cacheable();
cprint(LINE_TIME, COL_TIME+4, ": :");
}
cprint(0, COL_MID,"Pass %");
cprint(1, COL_MID,"Test %");
cprint(2, COL_MID,"Test #");
cprint(3, COL_MID,"Testing: ");
cprint(4, COL_MID,"Pattern: ");
cprint(LINE_INFO-2, 0, " WallTime Cached RsvdMem MemMap Cache ECC Test Pass Errors ECC Errs");
cprint(LINE_INFO-1, 0, " --------- ------ ------- -------- ----- --- ---- ---- ------ --------");
cprint(LINE_INFO, COL_TST, " Std");
cprint(LINE_INFO, COL_PASS, " 0");
cprint(LINE_INFO, COL_ERR, " 0");
cprint(LINE_INFO+1, 0, " -----------------------------------------------------------------------------");

for(i=0; i < style="font-weight: bold;">cprint(i, COL_MID-2, " ");
}
footer();
// Default Print Mode
// v->printmode=PRINTMODE_SUMMARY;
v->printmode=PRINTMODE_ADDRESSES;
v->numpatn=0;
find_ticks();
}
這些粗體字就是我會講解的重點;首先說明set_cache
void set_cache(int val)
{
extern struct cpu_ident cpu_id;//cpu_id這個資料結構在head.S中已初始化完成
/* 386's don't have a cache */
if ((cpu_id.cpuid lss 1) && (cpu_id.type == 3))
{
cprint(LINE_INFO, COL_CACHE, "none");
return;
}
switch(val)
{
case 0:
cache_off();
cprint(LINE_INFO, COL_CACHE, "off");
break;
case 1:
cache_on();
cprint(LINE_INFO, COL_CACHE, " on");
break;
}
}

static inline void cache_on(void)
{
asm(
"push %eax\n\t"
"movl %cr0,%eax\n\t"
"andl $0x9fffffff,%eax\n\t" /* Clear CD and NW */
"movl %eax,%cr0\n\t"
"pop %eax\n\t");
}
這個組合語言是GCC-Inline-Assembly,請自行參考語法說明。打開CACHE快取才可使CPU存取RAM的速度加快;因為匯流排,即使是跑DUAL CHENNEL,也不會比CPU快,因此快取越大,更能提升CPU效能。但要知道一點,這軟體主要是測試記憶體,萬一CPU快取本身有問題,就很難去測試記憶體真正的PASS或FAIL。
--> 閱讀更多...