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;
}
待續‧‧‧
--> 閱讀更多...