2010年3月4日 星期四

在安裝DOS的硬碟,將MBR讀出,並反組譯



http://www.pczone.com.tw/vbb3/archive/t-51849.html這裡的code比較多,這是用光碟安裝xp時,寫入mbrㄉ狀況;但此連結也有很多可參考的資訊。以下這一小段程式可能比較適合理解mbr的Boot Partition Loader ,這是我用virtualBoxㄉ虛擬dos執行fdisk後將虛擬HD分割為單一顆後,並用spfdisk將磁註0磁頭0磁區1的MBR共512bytes儲存,並反組譯所得到的程式碼如下。
參考以下的CODE必須參照上圖。

.386
seg000 segment byte public '' use16
assume cs:seg000
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
cli ;關閉中斷
xor ax, ax ;ax=0
mov ss, ax ;ss=0
mov sp, 7C00h ;sp=7c00h
mov si, sp ;si=7c00h
push ax
pop es ;將es設為0
push ax
pop ds ;將ds設為0
sti ;啟用中斷
cld ;清除方向旗標,使si di隨位址遞增
mov di, 600h ;設di=600h
mov cx, 100h ;設cx=100h,目的當成計數器
repne movsw ;重覆搬移100h次,每次搬移2bytes,來源ds:si -->0000:7c00h 目的es:di -->0000:0600h 因此共搬移512bytes
jmp far ptr 0:61Dh ;長跳躍到0000:061dh

mov si, 7BEh ;7beh - 600h = 1beh,也就是說si指到位移值 01BE ~ 01CD <-- 第一分割表
mov bl, 4 ;設定bl=4
next:
cmp byte ptr [si], 80h ;比對first_partition第一ㄍbyte是否為80h
jz is_a_boot_partition
cmp byte ptr [si], 0 ;比對first_partition第一ㄍbyte是否為00h
jnz invalid_table
add si, 10h ;將si指到下一ㄍ分割表
dec bl ;bl減1
jnz next
int 18h ; TRANSFER TO ROM BASIC
; causes transfer to ROM-based BASIC (IBM-PC)
; often reboots a compatible; often has no effect at all
is_a_boot_partition:
mov dx, [si] ;設dx=80h ,也就是DH=01 DL=80H 請參考first_partition
mov cx, [si+2] ;設cx=01h ,也就是CH=00 CL=01H 等一下讀取硬諜時會用到,可參考first_partition 分割開始之磁區編號 (6 bits) 最高的 2 個 bits(bit6-7), 為磁柱編號的 bit8-9
mov bp, si ;將bp設為指向first_partition

next_partition:
add si, 10h
dec bl
jz chk_set_OK
cmp byte ptr [si], 0
jz next_partition

invalid_table:
mov si, 68Bh ;68B指到str_invalid

show_msg:
lodsb ;以下幾行code是呼叫int 10h顯示單一字元
cmp al, 0
jz Exit
push si
mov bx, 7
mov ah, 0Eh
int 10h
; - VIDEO - WRITE CHARACTER AND
; AL = character, BH = display page (alpha modes)
; BL = foreground color (graphics modes)
pop si
jmp show_msg
; ---------------------------------------------------------------------------

Exit:
jmp Exit ;無窮迴圈 程式停在此處
; ---------------------------------------------------------------------------

chk_set_OK:
mov di, 5

repeat_5time:
;這個中斷程式的spec:http://lrs.uni-passau.de/support/doc/interrupt-57/RB-0569.HTM
mov bx, 7C00h ;
mov ax, 201h ;讀取1ㄍ磁區
push di
int 13h ; 讀取 磁頭head=1 drive=80H 磁軌track=0 磁區sector=1(*****此處就是dos作業系統INSTALL的啟始位置*****)
; DISK - READ SECTORS INTO MEMORY
; AL = number of sectors to read, CH = track, CL = sector
; DH = head, DL = drive, ES:BX -> buffer to fill
; Return: CF set on error, AH = status, AL = number of sectors read
pop di
jnb drive_read_OK
xor ax, ax
int 13h ; DISK - RESET DISK SYSTEM
; DL = drive (if bit 7 is set both hard disks and floppy disks reset)
dec di
jnz repeat_5time
mov si, 6A3h ;6A3h指向str_err_loading "Error loading operating system"
jmp short show_msg
; ---------------------------------------------------------------------------

drive_read_OK:
mov si, 6C2h ;6c2h指向str_miss_OS "Missing operating system"
mov di, 7DFEh ;7DFEh指向55aa之55位置
cmp word ptr [di], 0AA55h ;比對是否510及511位置是否為55aa
jnz short show_msg ;若不是55AA便顯示"Missing operating system"
mov si, bp
jmp far ptr 0:7C00h ; 轉移控制權到 0000:7C00H 程式到止結束;注意,此時的0000:7C00H已經更新為剛才重新讀取的磁頭head=1 drive=80H 磁軌track=0 磁區sector=1,因此這裡也就是進入dos作業系統的指令。
; ---------------------------------------------------------------------------
str_invalid db 'Invalid partition table',0
str_err_loading db 'Error loading operating system',0
str_miss_OS db 'Missing operating system',0
db 0
db 88h ;
db 74h ;
db 63h ;
db 3Ch ;
db 222 dup(0)
first_partition db 80h,01h,01h,00h,06h,1Fh,0BFh,07h,3Fh,00h,00h,00h,0C1h,0FEh,0Fh, 00h
db 30h dup(0)
db 55h, 0AAh ;55aa標示符號
seg000 ends

end
--> 閱讀更多...

2010年3月3日 星期三

學習u-boot

http://blog.chinaunix.net/u1/34474/showart.php?id=1877493
http://blog.chinaunix.net/u2/75270/showart_1779196.html
http://blog.chinaunix.net/u3/98913/article.html
這兩個連結算是我最近search到最棒的u-boot介紹:
以下內容便是轉載於第二個連結,由於這資料非常寶貴,因此我當然要post一份到自己的blog;以免萬一原文超連結遺失。
u-boot2008.10非nand_leagcy移植mini2440,支持yaffs,附源碼
這篇文章寫於2008.12.28日,主要記錄了我移植u-boot-2008.10的過程,並附上了移植好的patch檔。移植好的u-boot-2008.10適用友善公司的mini2440和陽初公司的yc2410。其他的開發板,可能要根據相應的電路配置做稍許修改。我的移植是使用非nand-leagcy方法的,移植好的u-boot-2008.10功能除了基本功能外,加上了yaffs1映射的寫入功能,加入了從nand flash啟動的功能,改善了一些操作感受,往nand寫入資料時,可以顯示進度。在使用上要注意的是使用nand wirte.yaffs 命令寫入yaffs1映射時,文件長度參數一定要與yaffs1映射的大小完全一致,否則有可能產生假壞塊。我的開發環境是vmware,kubuntu8.04。交叉編譯是用crosstool0.43編譯生成的arm-linux-gcc 4.1.0,libc 2.3.2。
(本人也成功移植了linux2.6.27.9到mini2440開發板上,有需要源碼的,可以和我聯繫。EMAIL:nanjinrat@sohu.com,映射文件在此下載http://blog.chinaunix.net/u2/75270/showart.php?id=1796658 映射的使用方法見我的另一篇文章。http://blog.chinaunix.net/u2/75270/showart.php?id=1836713)一直想自已移植一套u-boot,但因為工作忙,一直都沒有做,最近時間比較多,買了一套友善之臂的mini2440開發板,此板電路與該公司之前的QQ2440基本一至。而該開發板自帶的u-boot的移植的還不很完善,於是下決心自已移植u-boot。要移植就用最新版的u-boot移植,於是決定在u-boot.2008.10版上進行移植,此版是2008年10月的新版,於之前的版本有較大改動,所以版本號也沒有延續以前的編號方式,而改為2008.10。我的移植目的,是要能同時持S3C2440,S3C2410(手中還有一塊陽初公司出的S3C2410開發板),從nand flash啟動u-boot,因為目前大多數應用都是只有nand flash的,所以沒打算從nor flash啟動。支援tftp的使用,也就說要移植網卡的驅動,mini2440和陽初的s3c2410自帶的linux都是使用yaffs檔系統作為根檔系統的,因此,u-boot還要能支援yaffs映射的燒寫。在此我將移植過程記錄下來,以方便大家在移植此版u-boot時參考。為了方便整個移植過程中的調試,我把移植過程分為0~6共7個階段。每個階段完成時,u-boot都是可以正常運行的,因此,你可以根據自已的要求,決定移植工作做到哪一階段。為了同時支持S3C2440和S3C2410,我在移植時,同時加入兩種代碼,使用config_s3c2440,config_s3c2410這樣的巨集定義來決定編譯哪種代碼。如果你不需要同時支援兩個CPU,你可以只加入一種代碼。所有代碼不在此列出,大家可以看我上傳的移植好的u-boot。我在下面給出針對S3C2440移植的詳細說明,S3C2410的移植比較簡單,參考2440的移植即可,不另說明了。不同階段的詳細代碼可以閱讀patch檔。
第0階段:本階段任務,是在u-boot系統中,建立起自已的開發板體系。我先建立mini2440板的體系。方法如下。hugerat是我的網名,你可以根據需要更改。1 打開u-boot主目錄下的makefile,找到smdk2410_config,在其下,仿照它的格式加入如下語句rat2440_config : unconfig @$(MKCONFIG) $(@:_config=) arm arm920t rat2440 hugerat s3c24x0
各項的意思如下:arm: CPU的架構(ARCH)arm920t: CPU的類型(CPU),其對應於cpu/arm920t子目錄。rat2440: 開發板的型號(BOARD),對應於board/hugerat/rat2440目錄。hugerat: 開發者/或經銷商(vender)。s3c24x0: 片上系統(SOC)。
此步是為了加入自已的開發板,非必須也可以在現有的開發板基礎上修改。
2修改CROSS_COMPILE為自已的arm gcc編譯器,我使用的是系統默認的arm-linux-gcc,故不必再修改相應的CROSS_COMPILE項。
3 在/board子目錄中建立自己的開發板rat2440目錄由於我在上一步板子的開發者/或經銷商(vender)中填了 hugerat ,所以開發板rat2440目錄一定要建在/board子目錄中的hugerat
目錄下 ,否則編譯會出錯。然後,將smdk2410目錄下的檔考入此目錄中。並將其中的smdk2410.c改名為rat2440.c 還要記得修改自己的開發板rat2440目錄下的Makefile檔,不然編譯時會出錯:
COBJS := rat2440.o flash.o
4 在include/configs/中建立配置頭檔將smdk2410的相應頭檔複製一份在相同目錄下。並改名為rat2440.h
5 回到u-boot主目錄,make rat2440_config,再make,編譯生成u-boot.bin成功。
建立S3C410板的方法同上,僅將2440改為2410即可。不重複了。
第0階段完成。
第1階段本版u-boot依然沒有提供對S3C2440的支援,因此本階段任務是加入S3C2440相關的代碼,使得u-boot可以在s3c2440上正常工作。但沒有增加任何附加功能。在下列文件中加入標注為/*by hugerat,phase 1---*/的代碼。其中,被注釋掉的代碼為原代碼。/cpu/arm920t/start.s (加入S3C2440的時鐘相關的寄存器定義,加入時鐘初始化代碼,以使S3C2440工作在405MHz)board/hugerat/rat2440/lowlevel_init.s(加入S3C2440記憶體控制寄存器的定義)board/hugerat/rat2440/rat2440.c (修改GPIO,PLL的設置,應注意GPBCON的設置,不要讓蜂鳴器響)include/configs/rat2440.h(設定環境變數)以下檔主要加入CONFIG_S3C2440巨集定義以使得編譯一些S3C2410的代碼,和加入led燈的控制。
以指示u-boot程式進程。/inlcude/s3c24x0.hcpu/arm920t/s3c24x0/interrupts.c(這裏還要加入CONFIG_rat2440和CONFIG_rat2410)/cpu/arm920t/s3c24x0/serial.ccpu/arm920t/s3c24x0/speed.ccpu/arm920t/s3c24x0/usb_ohci.c/cpu/arm920t/s3c24x0/usb.c cpu/arm920t/s3c24x0/i2c.cdrivers/usb/usb_ohci.cdrivers/rtc/s3c24x0_rtc.c/lib_arm/board.c
編譯成功。將u-boot.bin燒入nor-flash即可運行。但是為方便使用,同時,也是方便u-boot的調試因此,我要將此u-boot代碼再做一修改,使其可以在記憶體中運行。這樣,可以用開發板自帶的vivi將其下載到記憶體中,再在記憶體中運行u-boot。要想它在記憶體中運行,方法很簡單,將u-boot/cpu/arm920t中的start.s
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
此段代碼中的bl cpu_init_crit注釋掉,即不進行CPU的初始化工作(此工作,當前在板子上運行的vivi已完成,故不能再次進行)
,即改為
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
@bl cpu_init_crit
#endif
修改/board/hugerat/rat2440/config.mk中text_base 值為0x33000000
使用vivi命令load ram 0x33000000 0x17ea8 x(0x17ea8 是載入檔案的size單位byte , x表示xmodem u表示usb)將u-boot.bin裝入記憶體。再用go 0x33000000命令,即可。
至此,第1階段工作完成。
第2階段本階段任務,是給u-boot移植dm9000的網卡驅動。u-boot自帶網卡驅動,所以只要做些設置即可。在下列檔中加入標誌為/*by hugerat,phase 2----*/的代碼./include/configs/rat2440.h (加入dm9000定義,加入ping命令定義)此時,編譯通過,但ping時,報`ethaddr' not set錯誤。研究原代碼,發現#define CONFIG_ETHADDR 08:00:3e:26:0a:5b被注釋掉,恢復,並改變原來默認的IP位址。重新編譯後,可以ping通網路了。(也可以在u-boot啟動後,修改相關參數,但因為現階段還沒有支持nandflash,參數無法保存,故在此改變較為方便)又發現,ping是能ping通了,但報could not establish link(不能建立鏈結)的錯。不影響使用。參考網上的資料,發現這是因為在網卡驅動中,/home/lijin/u-boot-rat/drivers/net/dm9000.c,有一段程式試圖連接網卡的MII介面,而實際上MII介面並未使用,所以有十秒的等待時間,且報錯,將此段程式注釋掉即可。到此,本階段任務完成。
第3階段本階段任務,移植nand-flash驅動,注意此階段工作僅是讓u-boot可以操作讀寫nand flash。還不能讓它從nand flash啟動。
首先,要說明一下CFG_NAND_LEGACY的使用。在u-boot的/drivers/mtd/下有兩個目錄,分別是nand和nand_legacy。在nand目錄下的是nand的初始化函數和nand的操作讀寫函數,是使用linux的mtd構架的。此目錄下的檔,只有在定義了CFG_CMD_NAND宏和沒有定義CFG_NAND_LEGACY巨集的情況下才會被編譯。在nand_leagcy目錄下的檔也是是實現nand相關操作命令,如read,write等命令的功能,但不是使用linux的mtd構架。此目錄下的檔,只有在定義了CFG_CMD_NAND和定義了CFG_NAND_LEGACY巨集的情況下才會定義。此目錄下的檔u-boot組織已不推薦使用。事實上,此版中,S3C2410構架已不支持對nand_leagcy,因此,我在移植中,是用的不定義CFG_NAND_LEGACY的方式,即非nand_leagcy方式。
在/include/configs/rat2440.h中加標注為by hugerat,phase3的代碼段,這段代碼是讓u-boot啟用其自帶nand flash驅動,並設置相應的nand flash參數。此時,試編譯一下,通過。要說明的是,此版的u-boot已自帶board_nand_init(),此函數在/cpu/arm920t/s3c24x0/nand.c中實現。並且此版已不支援定義CFG_NAND_LEGACY,如定義此宏,則編譯是會報 #error "U-Boot legacy NAND support not available
for S3C2410"的錯誤。故此版已不能使用網上流傳非nand_leagcy方式的自加nand flash初始化函數的方法,只能用其自帶的初始化函數下載到記憶體中運行,報None nand devices!!!,這是當然的。因為S3C2410和S3C2440在FLASH控制器上,差別較大,必須改寫代碼。
改寫主要在/cpu/arm920t/s3c24x0/nand.c中進行。(此檔內是與晶片緊密相關的代碼),一是board_nand_init函數,一是s3c2410_hwcontrol,參照2440的手冊,改寫相關代碼。還要在此檔的開始部分加入S3C2440 nand flash控制器相關寄存器的定義。改寫的代碼標注同上。改寫完畢後,u-boot可以識別出nand flash晶片是64MB的,但還不能識別是什麼晶片。
在/driver/mtd/nand/nand_base.c中的nand_get_flash_type函數結尾,修改MTDDEBUG語句,改為printf,再編譯,可以正常顯示芯片了。但nand write功能不正常,即沒報錯,實際上也沒有寫進去。
探討原因後發現,原來是u-boot自帶的nand-flash驅動(不定義nand_leagcy),是基於mtd驅動的。在默認情況下,不進行寫入正確與否的校驗。要定義CONFIG_MTD_NAND_VERIFY_WRITE宏才能進行寫入校驗。關於ECC校驗,mtd驅動默認是用sotf_ecc的。加入宏定義後,u-boot報write error了。在此我卡了很久,最後才發現是u-boot.2008.10自帶的S3C2410的s3c2410_hwcontrol函數有錯。在此函數中,把chip->IO_ADDR_W值改寫了,導致在寫資料時出現錯誤。將此錯誤修正後,nand write正常了。修正方法是使用一總體變數替代chip->IO_ADDR_W。
接下來,在rat2440.h中加入#define CONFIG_ENV_IS_IN_NAND 1注掉原來的#define CONFIG_ENV_IS_IN_FLASH 1,加入
#define CONFIG_ENV_OFFSET 0x30000 注掉原來的#define CFG_ENV_OFFSET 0x30000。編譯。saveenv功能也正常了。至此,nand-flash驅動移植完成。
測試,nand write 0x30000000 0x40000 0x40000時,成功。用nand read也成功讀出。此處要說明的,如果用vivi燒寫資訊到nand
中,再用u-boot讀取,會報錯,應該是ECC校驗不是由同一軟體產生所至。此階段完成。
第4階段本階段任務是將u-boot改寫為從nand-flash啟動。首先,將/cpu/arm920t/start.s中第一階段中為了從記憶體中啟動而遮罩掉的語句恢復。加入標注為/*by hugerat,phase 4----*/
語句,為了相容S3C2410,同時也加入S3C2410 nand boot 相關代碼。
同時,在/board/hugerat/rat2440/lower_init.s中,根據開發板電路,對記憶體相關的幾個寄存器定義進行調整。整個啟動代碼參考了vivi的代碼。因此,將vivi的nand_read.c(支持S3C2440
的vivi,此檔中的讀nand函數是直接操作nand flash的,與u-boot自帶,適合用於啟動時用。)考入/board/hugerat/rat2440目錄下。並修改目錄下的makefile,使nand_read.c被編譯。編譯,成功,將u-boot燒入nand flash,能成功從nand啟動了。
第5階段本階段任務是實現u-boot通過nfs引導友善提供的linux2.6.13的內核。友善的linux並沒有提供make uImage命令,需要添加。將u-boot的tools目錄下的mkimage拷到開發機linux系統下的/usr/bin目錄下
。在linux的/arch/arm/makefile中和/arch/arm/boot/makefile中,可以找到zImage的項目,仿照它添加uImage項目。
這個問題解決後,就是要用u-boot引導linux內核了,我用的是揚創提供的linux2.6.13的內核,揚創提供的內核,loadaddress和entry都是30008000,我用u-boot1.2中的bootcmd參數,u-boot1.3.1 bootm卻不能引導,報bad magic number,看了bootm的相關資料,得知,如果我們沒用mkimage對內核進行處理的話,那直接把內核下載到0x30008000再運行就行,內核會自解壓運行(不過內核運行需要一個tag來傳遞參數,而這個tag建議是由bootloader提供的,在u-boot下默認是由bootm命令建立的)。 2)如果使用mkimage生成內核鏡像檔的話,會在內核的前頭加上了64byte的資訊,供建立tag之用。bootm命令會首先判斷bootm xxxx 這個指定的位址xxxx是否與-a指定的載入位址相同。(1)如果不同的話會從這個位址開始提取出這個64byte的頭部,對其進行分析,然後把去掉頭部的內核複製到-a指定的load地址中去運行之(2)如果相同的話那就讓其原封不同的放在那,但-e指定的入口地址會推後64byte,以跳過這64byte的頭部。
Bootm在沒有參數時,是採用rat2440.h中的#define CFG_LOAD_ADDR 的位址的,而我用bootm就是沒有使用參數,所以出錯了。正確的做法應該是用nand read命令將內核從nand flash中讀到記憶體的某一位址中(注意不要與其他已分配的記憶體衝突),然後再用bootm 加位址參數,即可引導,也可以在上述的檔中,將CFG_LOAD_ADDR的位址定義為此位址,再用bootm就可以了.我設定bootcmd環境變數為tftp 0x31000000 uImage; bootm 0x31000000,注意位址不能為0x30008000,否則報錯.
做完這此,內核可以引導了,但卻停在starting kernel不動了,好在我以前做過vivi+linux2.6.22的移植,知道此問題多半是由於mach_type不同而造成的。在u-boot中,此mach_type是由rat2440.c中的這段代碼定義的
#if defined(CONFIG_S3C2440)
/* arch number of S3C2440 -Board */
//gd->bd->bi_arch_number = 5244 ; //改為和內核的MACH_TYPE一至
gd->bd->bi_arch_number = MACH_TYPE_MINI2440 ;
#endif
你可以直接在這裏改數位,也可以在include/asm-arm/mach_types.h的文件中,改MACH_TYPE_S3C2440的數值。將數值改為和內核的
mach_type一至。至於內核的mach_type可以在內核linux源代碼下的arch/arm/tools中的mach_types檔查看到。
此時,又發現內核不能通過nfs引導。查資料後得知是因為u-boot沒有傳遞參數給內核。原來是rat2440.h中少定義了幾個宏。補上
。#define CONFIG_CMDLINE_TAG 1 /* enable passing of ATAGs */
#define CONFIG_SETUP_MEMORY_TAGS 1
#define CONFIG_INITRD_TAG 1
至此,u-boot已能正常引導linux啟動了。第5階段完成。
第6階段本階段任務,是給u-boot加入燒寫yaffs映射檔的功能。網上有很多文章已經有介紹如何加入此功能,方法也不複雜。但我移植的u-boot.2008.10版,在nand flash的操作上與之前的版本
有了很大的區別。這些文章介紹的方法已不能在此版中應用。於是我只好自已根據u-boot的源代碼,yaffs映射檔的格式,自已編修改了u-boot.2008.10的代碼,讓它可以實現yaffs的燒寫,因為我用的開發板的nand flash是k9f1208,只能使用yaffs1,所以我的修改代碼暫時只能支援yaffs1格式的映射。(要支持yaffs2也不難,但因為無法驗證,所以就沒有做嘗試。)修改方法如下:在/common/com_nand.c中do_nand函數中,加入三段/*by hugerat,phase6---*/標注的代碼,實現對nand write.yaffs命令的支援。
此代碼中,要用到mtd_info結構中的兩個變數,這兩個變數本來是沒有的,所以要在include/linux/mtd/mtd.h的mtd_info結構體定
義中加入。在/drivers/mtd/nand/nand_util.c的nand_write_skip_bad函數中,兩段程式,一段是為了計算正常資料的長度,一段是為了在寫入一段資料後,資料指標能正常跳到下一段資料。在/drivers/mtd/nand/nand_base.c的nand_write函數中,加入一段把正常資料與oob資料分離的代碼,再加入頁寫時的模式設置為MTD_OOB_RAW,使頁寫時,不進行ECC的校驗和加入。(ECC的校驗在yaffs的oob資料中已自帶了,不能重寫。)此模式下,寫入正常資料後,會把oob資料緩存的資料寫入nand的oob區。至此,u-boot.2008.10的移植工作全部完成。
最後說明一下,關於陽初2410的移植,因為上面的代碼中已加入S3C2410相關代碼,移植時,只需參考S3C2440,新建一個開發板體系,新建時,將rat2440目錄下的檔拷貝到rat2410下,將重命名S3C2440.c為S3C2410.c,修改makefile,加上s3c2410.o 去掉s3c2440.o,拷貝rat2440.h為rat2410.h,將其中的CONFIG_S3C2440注掉,加上CONFIG_S3C2410。在u-boot總目錄下的makefile中rat2440_config下添加rat2410_config : unconfig @$(MKCONFIG) $(@:_config=) arm arm920t rat2410 hugerat s3c24x0因為陽初的板用的是cs8900,因此要在rat2410.h中注掉DM9000的定義,添加CS8900的。再make rat2410_config,再make,即可。
文章最後,為方便網友,我把patch檔附上。patch文件使用方法是在linux下建立u-boot-rat.patch檔,將patch檔的內容拷入u-boot-rat.patch文件中。下載u-boot-2008.10.tar.bz2,在這裏下載ftp://ftp.denx.de/pub/u-boot/,用tar -xvf u-bot-2008.19.tar.bz2解壓到u-boot-2008.10目錄,把u-boot-rat.patch檔拷貝到此目錄所在同級目錄下。執行patch -p0 小於符號 u-boot-rat.patch。完成後,進u-boot-2008.10目錄,執行make rat2440_config,再make即可生成u-boot.bin,如是2410,則執行make rat2410_config即可,我在mini2440,陽初的2410上試過,功能正常。其他開發板的也可一試。 附patch文件: 可能發貼字數有限制,下面的補丁檔不全。可以在此下載全部補丁。 文件:u-boot-rat.rar 大小:20KB 下載
--> 閱讀更多...

2010年3月1日 星期一

Fast A20 和92H,及8042到底是什麼關係



關於A20 Gate
本來想直接寫一篇關於保護模式的文章,因為有一位讀者不斷地問我這個問題,隨著問題的深入,在評論上回答這個問題實在是太困難了,動起筆來,發現涉及的事情太多,免不了又是長篇大論惹人煩,而且要寫很長時間,不知道我能不能把它寫完,所以乾脆把一些問題分離出來寫,或許還可以堅持寫出來。
在許多PC的CMOS設置裏,都有一項叫做“A20 Enable“的設置,不知道大家是否就此設置困惑過,這個A20是什麼呢?
說A20則不得不說PC機的記憶體,我儘量用簡短的語言說明白PC機記憶體的一些獨有的術語,在我剛接觸PC機的時候(1985年前後),如果你的機器有一塊10M或者20M的硬碟,那已經是一台不錯的機器,如果你的機器有256K的記憶體,那絕對是高配置,那時候還沒有3.5"的軟碟,都是使用5"的容量僅360K的軟碟,所以那個時候,設計IBM PC的IBM公司非常自信地認為,1M記憶體是一個根本達不到的天文數字,我想由於這種思想的作怪(我猜的),IBM非常愚蠢地把PC機1M存儲空間的最上面的384K用作了ROM和系統設備,這種設計給現在的PC機的記憶體結構埋下了麻煩的伏筆,如果當初IBM把PC機的1M存儲空間的最下面的384K用作ROM和系統設備,可能現在就會少好多麻煩,這個後面說。
寫到這裏的時候,我想起了許多事,大家是否知道我國的第一台電子電腦是什麼型號?是什麼樣子?是1958年生產的103機,後來又有了全電晶體的電子電腦,很榮幸的是,在我工作過的單位裏,曾經有過這麼一台全電晶體的電子電腦,就是那種使用穿孔紙帶輸入電腦程式,占地好幾百平方米,一旦開動需要幾十人進行維護的傢伙,我到這個單位的時候,這台機器早已不再運轉的,80年代的時候,我有幸參加了這台機器的拆解工作,給我印象最深的是,我終於看到了當時在教科書裏說的“磁鼓”(估計現在的教科書裏也沒有了),一種看上去像篩子一樣的東西,那就是當年的記憶體,用於在紙帶上打孔進行程式輸入的穿孔機我用過,樣子很像老式的英文打字機。還有那種8英寸的軟碟,估計現在也很少有人見過了。
好了言歸正傳,大家都知道,8088/8086只有20位位址線,按理它的定址空間是2^20,應該是1024KB,但PC機的定址結構是segment:offset,segment和offset都是16位的寄存器,最大值是0ffffh,換算成物理位址的計算方法是把segment左移4位,再加上offset,所以segment:offset所能表達的定址空間最大應為0ffff0h + 0ffffh = 10ffefh(前面的0ffffh是segment=0ffffh並向左移動4位的結果,後面的0ffffh是可能的最大offset),這個計算出的10ffefh是多大呢?大約是1088KB,就是說,segment:offset的位址表達能力,超過了20位元位址線的物理定址能力,你說這是不是有點麻煩。在早先,由於所有的機器都沒有那麼大的記憶體,加上位址線只有20位,所以當你用segment:offset的方式企圖定址100000h這個位址時,由於沒有實際的第21位位址線,你實際定址的記憶體是00000h的位置,如果你企圖定址100001h這個位址時,你實際得到的內容是位址00001h上的內容,所以這個事對實際使用幾乎沒有任何影響,但是後來就不行了,出現了80286,地址線達到了24位,使segment:offset定址100000h--10ffefh這將近64K的記憶體成為可能,為了保持向下相容,於是出現了A20 Gate,這是後話,我們後面再細說。
我們可能經常聽到一些只有在PC機上才有的一些關於記憶體的專有名詞,包括:常規記憶體(Conventional Memory)、上位記憶體區(Upper Memory Area)、高端記憶體區(High Memory Area)和擴展記憶體(Extended Memory),我儘量把這幾個東東說明白,這需要上面這張著名的圖。

這張圖很清楚地說明了問題,大家都知道,DOS下的“常規記憶體”只有640K,這640K就是從0--A0000H這段位址空間;所謂“上位記憶體區”,指的就是20位位址線所能定址到的1M位址空間的上面384K空間,就是從A0001H--100000H這段位址空間,也就是我們說的用於ROM和系統設備的位址區域,這384K空間和常規記憶體的640K空間加起來就是20位位址線所能定址的完整空間1024KB;由於80286和80386的出現使PC機的位址線從20位變成24位又變成32位,定址能力極大地增加,1M以上的記憶體定址空間,我們統稱為“擴展記憶體”;這裏面絕大部分記憶體區域只能在保護模式下才能定址到,但有一部分既可以在保護模式下,也可以在實模式下定址,這就是我們前面提到過的位址100000h--10ffefh之間的這塊記憶體,為了表明其特殊性,我們把這塊有趣的記憶體區叫做“高端記憶體”。
前面我們提過由於IBM的愚蠢設計給PC機的記憶體結構埋下了麻煩的伏筆,現在我們來說說這個麻煩。我們都見過PC機上的記憶體條,但是由於上位記憶體區的存在,這個記憶體條上的位址居然不能連續,就是說,這個記憶體條上要有0--A0000H的位址空間,還要有100000h--最大記憶體容量的位址空間,中間的384K位址空間必須留出來給ROM用,在現如今一個晶片就好幾兆的情況下,你說這個記憶體條應該怎麼做,當然我相信一定是可以做出來的,但肯定很麻煩,如果當初IBM把這個“上位記憶體區”放在位址低端,就是0--6000h這一部分,豈不是這個麻煩就沒有了?!
但是,實際的記憶體條上位址都是連續的,並沒有人把這段位址空間留出來給ROM使用,原因很簡單,採用技術手段把這段位址空間空出來,比浪費這384K記憶體的成本還要高,所以在這個位址區域就出現了很奇怪的現象,ROM和RAM的地址重疊。 實際上,往往ROM並不能完全覆蓋整個384K區域,這樣就會有一些位址沒有被ROM佔用,那麼這部分位址上的RAM仍然是可以使用的。實際上,和ROM重疊的這384K RAM一般也不會被浪費,說到這裏,不得不說所謂的ROM Shadowing了,RAM和ROM的性能是有很大差異的,RAM的存取速度要遠遠大於ROM,而且RAM可以32位存取,ROM通常只能16位,所以目前的PC機對這塊RAM和ROM重疊的區域的處理採用一種ROM Shadowing的技術方式,當機器加電後,先讓ROM有效,RAM無效,然後讀出ROM內容,再讓ROM無效,RAM有效,把讀出的ROM內容放到相同位址的RAM中,並把相應位置的RAM設定為唯讀,這樣就把ROM搬到了RAM中,位址完全一樣,只是性能比使用ROM要高些,這塊RAM就好像ROM的Shadow一樣。
回到我們的主題A20 Gate,出現80286以後,為了保持和8086的相容,PC機在設計上在第21條位址線(也就是A20)上做了一個開關,當這個開關打開時,這條位址線和其他位址線一樣可以使用,當這個開關關閉時,第21條位址線(A20)恒為0,這個開關就叫做A20 Gate,很顯然,在實模式下要訪問高端記憶體區,這個開關必須打開,在保護模式下,由於使用32位位址線,如果A20恒等於0,那麼系統只能訪問奇數兆的記憶體,即只能訪問0--1M、2-3M、4-5M......,這顯然是不行的,所以在保護模式下,這個開關也必須打開。
下面我們來看一下PC機是怎麼實現A20 Gate的。
早期的PC機,控制鍵盤有一個單獨的單片機8042,現如今這個晶片已經給集成到了其他大片子中,但其功能和使用方法還是一樣,當PC機剛剛出現A20 Gate的時候,估計實在找不到控制它的地方了,同時為這點小事也不值得增加晶片,於是工程師使用這個8042鍵盤控制器來控制A20 Gate,但A20 Gate真的和鍵盤一點關係也沒有,我們先從軟體的角度簡單介紹一下8042這個晶片。
8042有4個寄存器
· 1個8-bit長的Input buffer;Write-Only;
· 1個8-bit長的Output buffer; Read-Only;
· 1個8-bit長的Status Register;Read-Only;
· 1個8-bit長的Control Register;Read/Write。
有兩個埠位址:60h和64h。
· 讀60h埠,讀output buffer
· 寫60h埠,寫input buffer
· 讀64h埠,讀Status Register
對Control Register的操作相對要複雜一些,首先要向64h埠寫一個命令(20h為讀命令,60h為寫命令),然後根據命令從60h埠讀出Control Register的資料或者向60h埠寫入Control Register的資料(64h埠還可以接受許多其他的命令)。
先來看看Status Register的定義,我們後面要用bit 0和bit 1:
bit meaning ----------------------------------------------------------------------- 0 output register (60h) 中有數據

1 input register (60h/64h) 有數據

2 系統標誌(上電重定後被置為0)

3 data in input register is command (1) or data (0)

4 1=keyboard enabled, 0=keyboard disabled (via switch)

5 1=transmit timeout (data transmit not complete)

6 1=receive timeout (data transmit not complete)

7 1=even parity rec'd, 0=odd parity rec'd (should be odd)
除了這些資源外,8042還有3個內部埠:Input Port、Outport Port和Test Port,這三個埠的操作都是通過向64h發送命令,然後在60h進行讀寫的方式完成,其中本文要操作的A20 Gate被定義在Output Port的bit 1上,所以我們有必要對Outport Port的操作及埠定義做一個說明。
· 讀Output Port向64h發送0d0h命令,然後從60h讀取Output Port的內容
· 寫Output Port向64h發送0d1h命令,然後向60h寫入Output Port的資料
另外我們還應該介紹兩個命令:
· 禁止鍵盤操作命令向64h發送0adh
· 打開鍵盤操作命令向64h發送0aeh
有了這些命令和知識,我們可以考慮操作A20 Gate了,有關8042晶片更詳細的資料,請參考該晶片的Data Sheet。
如何打開和關閉A20 Gate。
理論上講,我們只要操作8042晶片的輸出埠(64h)的bit 1,就可以控制A20 Gate,但實際上,當你準備向8042的輸入緩衝區裏寫資料時,可能裏面還有其他資料沒有處理,所以,我們要首先禁止鍵盤操作,同時等待資料緩衝區中沒有資料以後,才能真正地去操作8042打開或者關閉A20 Gate。打開A20 Gate的具體步驟大致如下:
1.關閉中斷; 2.等待8042 Input buffer為空; 3.發送禁止鍵盤操作命令; 4.等待8042 Input buffer為空; 5.發送讀取8042 Output Port命令; 6.等待8042 Output buffer有數據; 7.讀取8042 Output buffer,並保存得到的位元組; 8.等待8042 Input buffer為空; 9.發送Write 8042 Output Port命令到8042 Input buffer; 10.等待8042 Input buffer為空; 11.將從8042 Output Port得到的位元組的第2位置1(或清0),然後寫入8042 Input buffer; 12.等待,直到8042 Input buffer為空為止; 13.發送允許鍵盤操作命令到8042 Input buffer; 14. 打開中斷。
下面是完成打開A20 Gate的代碼:
A20Enable:

cli ;1.關閉中斷

call WaitInbufEmpty ;2.等待8042 Input buffer為空;

mov al, 0adh

mov dx, 64h

out dx, al ;3.發送禁止鍵盤操作命令

call WaitInbufEmpty ;4.等待8042 Input buffer為空;

mov al, 0d0h

mov dx, 64h

out dx, al ;5.發送讀取8042 Output Port命令;

call WaitOutbufFull ;6.等待8042 Output buffer有數據;

mov dx, 60h

in al, dx ;7.讀取8042 Output buffer

push ax ;保存讀取的資料

call WaitInbufEmpty ;8.等待8042 Input buffer為空;

mov al, 0d1h

mov dx, 64h

out dx, al ;9.發送寫 8042 Output Port命令

call WaitInbufEmpty ;10.等待8042 Input buffer為空

pop ax

or al, 02h ;11.將從8042 Output Port得到的位元組的bit 1置1

mov dx, 60h

out dx, al ;寫入Output Port

call WaitInbufEmpty ;12.等待8042 Input buffer為空

mov al, 0aeh

mov dx, 64h

out dx, al ;13.發送允許鍵盤操作命令

sti ;開中斷

ret

WaitInbufEmpty:

mov dx, 64h

in al, dx ;讀取Status Register

test al, 02h

jnz WaitInbufEmpty

ret

WaitOutbufFull:

mov dx, 64h

in al, dx

test al, 01 ;讀取Status Register

jz WaitOutbufFull

ret
後來,由於感覺使用8042控制A20運行太慢了(確實,那麼長的代碼,中間還要若干次的wait),所以後來又出現了所謂的Fast A20,實際上,現在的大多數機器都是Fast A20,Fast A20使用92h埠控制A20,同時BIOS裏提供了一個軟中斷來控制A20:
入口:ah=24h al=0 關閉A20 1 打開A20 2 讀取A20狀態 int 15h
返回:如果BIOS支援此功能,CF=0,否則CF=1 CF=0時,AX返回當前A20狀態,1=打開,0=關閉
像8042中的Output Port中的定義一樣,92h埠的bit 1控制著A20,為1時打開,為0時關閉,從92h中讀一個byte可以看a20的當前狀態,所以對92h的操作如下:
· 讀A20狀態mov dx, 92hin al, dx如果al的bit 1為1表示a20打開,否則為0
· 打開A20mov dx, 92hmov al, 02out dx, al
· 關閉A20mov dx, 92hmov al, 0out dx, al
特別要注意的是,大家從這篇文章的文字中可能也能感覺到,A20 Gate的設計本身就讓人感覺很彆扭,不是那麼流暢,所以和A20有關的事情就難免也會有相同的感覺,很奇怪的是,上面介紹的三種方法並不是在每台機器上都適用,所以如果你要做一個商業軟體其中要操作A20,那一定要三種方式聯合使用才比較穩妥,否則會有意想不到的結果,LINUX公開的啟動代碼中就是這麼做的
在DOS下有時我們會在config.sys中寫上一句:dos=high,這句就會把駐留的DOS放到高端記憶體區域去,怎麼放的呢?關鍵點就是打開A20,然後把DOS從常規記憶體搬到100000h起始的區域去,並把在常規記憶體中佔用的記憶體釋放掉,當然說起來容易,實際做的時候還有很多細節要處理。
說到DOS=HIGH,就不得不提醒大家另一件事,如果在config.sys中有dos=high這一句,那麼恐怕92h的方法和BIOS的方法都會不完全靈驗(至於操作8042的方法靈不靈我沒有試),這是DOS做了手腳,因為DOS被放到了高端記憶體中,為了保證DOS能正常運行,它不允許你把A20給關掉,遇到這種情況不要驚慌,不是我寫得不對,確實是DOS太狡猾了。
還有最後一點要特別注意,92h的bit 0是給機器發重定信號的(8042 Output Port的bit 0也是),所以在向92h寫資料時,千萬不要讓bit 0為1,否則機器會重新啟動,如果你的應用程式需要重新啟動機器,這也是方法之一,比jmp 0ffffh:0來的還要乾脆。
在其他我們介紹保護模式的文章中,我們會用到上面提到的打開A20的方法,屆時可能就不會做更多的解釋了。
另外,涉及操作A20的資料其實很少,有些資料我手裏也很缺乏,比如92h除bit 0和bit 1以外的定義是什麼我至今也不知道,包括本文中的一些內容也是摸索所得,並沒有資料予以佐證,所以萬一有不對的地方,不要見怪,並懇請指出。
--> 閱讀更多...