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