====== U-BOOT介紹以及Disk模塊源碼分析 ====== http://http://blog.csdn.net/zht_sir/archive/2007/04/26/1585284.aspx ===== 一. BootLoader簡介 ===== 在專用的嵌入式板子運行GNU/Linux系統已經變得越來越流行。一個嵌入式Linux系統從軟件的角度看通常可以分為四個層次: -引導加載程序。\\ 包括固化在固件(firmware)中的boot代碼(可選),和BootLoader兩大部分。 -Linux內核。\\ 特定於嵌入式板子的定制內核以及內核的啟動參數。 -文件系統。\\ 包括根文件系統和建立於Flash內存設備之上文件系統。通常用ramdisk來作為rootfs。 -用戶應用程序。\\ 特定於用戶的應用程序。有時在用戶應用程序和內核層之間可能還會包括一個嵌入式圖形用戶界面。常用的嵌入式GUI有:MicroWindows和MiniGUI懂。 \\ 引導加載程序是系統加電後運行的第一段軟件代碼。PC機中的引導加載程序由BIOS(其本質就是一段固件程序)和位於硬盤MBR中的OS BootLoader(比如,LILO和GRUB等)一起組成。BIOS在完成硬件檢測和資源分配後,將硬盤MBR中的BootLoader讀到系統的RAM中,然後將控制權交給OS BootLoader。BootLoader的主要運行任務就是將內核映像從硬盤上讀到 RAM 中,然後跳轉到內核的入口點去運行,也即開始啟動操作系統。 而在嵌入式系統中,通常並沒有像BIOS那樣的固件程序(注,有的嵌入式CPU也會內嵌一段短小的啟動程序),因此整個系統的加載啟動任務就完全由BootLoader來完成。比如在一個基於ARM7TDMI core的嵌入式系統中,系統在上電或復位時通常都從地址0x00000000處開始執行,而在這個地址處安排的通常就是系統的BootLoader程序。 簡單地說,BootLoader就是在操作系統內核運行之前運行的一段小程序。通過這段小程序,我們可以初始化硬件設備、建立內存空間的映射圖,從而將系統的軟硬件環境帶到一個合適的狀態,以便為最終調用操作系統內核準備好正確的環境。 通常,BootLoader是嚴重地依賴於硬件而實現的,特別是在嵌入式世界。因此,在嵌入式世界裡建立一個通用的BootLoader幾乎是不可能的。儘管如此,我們仍然可以對BootLoader歸納出一些通用的概念來,以指導用戶特定的BootLoader設計與實現。 ===== 二.U-BOOT介紹 ===== uboot是一個龐大的公開源碼的軟件。他支持一些系列的arm體系,包含常見的外設的驅動,是一個功能強大的板極支持包。其代碼可以從http://sourceforge.net/projects/u-boot下載 U-BOOT是由PPCBOOT發展起來的,是PowerPC、ARM9、Xscale、X86等系統通用的Boot方案,從官方版本0.3.2開始全面支持SC系列單板機。u-boot是一個open source的bootloader,目前版本是0.4.0。u-boot是在ppcboot以及armboot的基礎上發展而來,雖然宣稱是0.4.0版本,卻相當的成熟和穩定,已經在許多嵌入式系統開發過程中被採用。由於其開發源代碼,其支持的開發板眾多。唯一遺憾的是並不支持我們現在學習所用samsung 44B0X的開發板。 為什麼我們需要u-boot?顯然可以將ucLinux直接燒入flash,從而不需要額外的引導裝載程序(bootloader)。但是從軟件升級的角度以及程序修補的來說,軟件的自動更新非常重要。事實上,引導裝載程序(bootloader)的用途不僅如此,但僅從軟件的自動更新的需要就說明我們的開發是必要的。 同時,u-boot移植的過程也是一個對嵌入式系統包括軟硬件以及操作系統加深理解的一個過程。 ===== 三.disk模塊分析 ===== part.c:輸出設備信息\\ part_amiga.c:處理amiga分區\\ part_dos.c:處理dos分區\\ part_iso.c:處理iso分區\\ part_mac.c:處理mac分區\\ Part.c文件: void dev_print (block_dev_desc_t *dev_desc) { //主要用來向用戶報告設備信息 …… if (dev_desc->type==DEV_TYPE_UNKNOWN) {//設備未知 puts ("not available\n"); return; } …… puts (" Type: "); if (dev_desc->removable) //設備可移除 puts ("Removable "); switch (dev_desc->type & 0x1F) { //輸出設備類型 case DEV_TYPE_HARDDISK: puts ("Hard Disk"); break; case DEV_TYPE_CDROM: puts ("CD ROM"); break; case DEV_TYPE_OPDISK: puts ("Optical Device"); break; case DEV_TYPE_TAPE: puts ("Tape"); break; default: printf ("# %02X #", dev_desc->type & 0x1F); break; } puts ("\n"); if ((dev_desc->lba * dev_desc->blksz)>0L) { ulong mb, mb_quot, mb_rem, gb, gb_quot, gb_rem; lbaint_t lba; lba = dev_desc->lba; lba512 = (lba * (dev_desc->blksz/512)); mb = (10 * lba512) / 2048; /* 2048 = (1024 * 1024) / 512 MB */ /* round to 1 digit */ mb_quot = mb / 10; mb_rem = mb - (10 * mb_quot); gb = mb / 1024; gb_quot = gb / 10; gb_rem = gb - (10 * gb_quot); …… } else { puts (" Capacity: not available\n"); } } …… void init_part (block_dev_desc_t * dev_desc) //對分區iso,mac,dos,amiga進行初始化 { #ifdef CONFIG_ISO_PARTITION //對應iso分區 …… #endif #ifdef CONFIG_MAC_PARTITION //對應mac分區 …… #endif #ifdef CONFIG_DOS_PARTITION //對應dos分區 …… #endif #ifdef CONFIG_AMIGA_PARTITION //對應amiga分區 …… #endif } int get_partition_info (block_dev_desc_t *dev_desc, int part, disk_partition_t *info) { //獲得不同分區的分區信息 switch (dev_desc->part_type) { #ifdef CONFIG_MAC_PARTITION //對應mac分區,以下略 …… #endif …… default: break; } return (-1); } static void print_part_header (const char *type, block_dev_desc_t * dev_desc) { //輸出設備、分區類型,如IDE, SCSI, ATAPI, USB, DOC, UNKNOWN puts ("\nPartition Map for "); switch (dev_desc->if_type) { case IF_TYPE_IDE: puts ("IDE"); break; …… default: puts ("UNKNOWN"); break; } printf (" device %d -- Partition Type: %s\n\n", dev_desc->dev, type); } void print_part (block_dev_desc_t * dev_desc) { //輸出分區信息 switch (dev_desc->part_type) { #ifdef CONFIG_MAC_PARTITION …… #endif #ifdef CONFIG_DOS_PARTITION …… #endif #ifdef CONFIG_ISO_PARTITION …… #endif #ifdef CONFIG_AMIGA_PARTITION …… #endif } puts ("## Unknown partition table\n"); } Part_amiga文件: static void bcpl_strcpy(char *to, char *from) { //將bcpl轉換成c string int len = *from++; while (len) { *to++ = *from++; len--; } *to = 0; } static void bstr_print(char *string) { //輸出一個bcpl字符串。Bcpl字符串第一個byte保存了該字符串的長度 …… } int sum_block(struct block_header *header) { //計算一個塊的大小,塊以0結束 …… for (i = 0; i < header->summed_longs; i++) sum += *block++; return (sum != 0); } static void print_disk_type(u32 disk_type) { // 輸出amigaOS 磁盤類型,一般由四個字節表示,例如 // DOS\0表示original file system, // SFS\0表示SmartFileSystem, // DOS\1表示FFS. char buffer[6]; buffer[0] = (disk_type & 0xFF000000)>>24; buffer[1] = (disk_type & 0x00FF0000)>>16; buffer[2] = (disk_type & 0x0000FF00)>>8; buffer[3] = '\\'; buffer[4] = (disk_type & 0x000000FF) + '0'; buffer[5] = 0; printf("%s", buffer); } static void print_part_info(struct partition_block *p) { //輸出給定分區塊內的信息 …… bstr_print(p->drive_name); printf("%6d\t%6d\t", g->low_cyl * g->block_per_track * g->surfaces , (g->high_cyl - g->low_cyl + 1) * g->block_per_track * g->surfaces - 1); print_disk_type(g->dos_type); printf("\t%5d\n", g->boot_priority); } struct rigid_disk_block *get_rdisk(block_dev_desc_t *dev_desc) { //尋找Rigid Disk塊。該塊必須位於設備的最前面的16個塊中 …… if (res == 1) { struct rigid_disk_block *trdb = (struct rigid_disk_block *)block_buffer; if (trdb->id == AMIGA_ID_RDISK) { PRINTF("Rigid disk block suspect at %d, checking checksum\n",i); if (sum_block((struct block_header *)block_buffer) == 0) { PRINTF("FOUND\n"); memcpy(&rdb, trdb, sizeof(struct rigid_disk_block)); return (struct rigid_disk_block *)&rdb; } } } } PRINTF("Done scanning, no RDB found\n"); return NULL; } struct bootcode_block *get_bootcode(block_dev_desc_t *dev_desc) { //尋找啟動代碼,它必須在塊設備前16個塊中,或者在Ridgid塊中 …… PRINTF("Scanning for BOOT from 0 to %d\n", limit); for (i = 0; i < limit; i++) { ulong res = dev_desc->block_read(dev_desc->dev, i, 1, (ulong *)block_buffer); if (res == 1) { struct bootcode_block *boot = (struct bootcode_block *)block_buffer; if (boot->id == AMIGA_ID_BOOT) { PRINTF("BOOT block at %d, checking checksum\n", i); if (sum_block((struct block_header *)block_buffer) == 0) { PRINTF("Found valid bootcode block\n"); memcpy(&bootcode, boot, sizeof(struct bootcode_block)); return &bootcode; } } } } PRINTF("No boot code found on disk\n"); return 0; } int test_part_amiga(block_dev_desc_t *dev_desc) { //測試給定分區是否有amiga分區表/rigid塊 …… PRINTF("test_part_amiga: Testing for an Amiga RDB partition\n"); rdb = get_rdisk(dev_desc); if (rdb) { bootcode = get_bootcode(dev_desc); if (bootcode) PRINTF("test_part_amiga: bootable Amiga disk\n"); else PRINTF("test_part_amiga: non-bootable Amiga disk\n"); return 0; } else { PRINTF("test_part_amiga: no RDB found\n"); return -1; } } static struct partition_block *find_partition(block_dev_desc_t *dev_desc, int partnum) { //尋找指定分區號的分區 …… PRINTF("Trying to find partition block %d\n", partnum); …… while (block != 0xFFFFFFFF) { ulong res = dev_desc->block_read(dev_desc->dev, block, 1, (ulong *)block_buffer); if (res == 1) { p = (struct partition_block *)block_buffer; if (p->id == AMIGA_ID_PART) { PRINTF("PART block suspect at 0x%x, checking checksum\n",block); if (sum_block((struct block_header *)p) == 0) { if (partnum == 0) break; else { partnum--; block = p->next; } } } else block = 0xFFFFFFFF; } else block = 0xFFFFFFFF; } …… return (struct partition_block *)block_buffer; } int get_partition_info_amiga (block_dev_desc_t *dev_desc, int part, disk_partition_t *info) { //獲取一個分區的信息 …… if (!p) return -1; g = (struct amiga_part_geometry *)&(p->environment); info->start = g->low_cyl * g->block_per_track * g->surfaces; info->size = (g->high_cyl - g->low_cyl + 1) * g->block_per_track * g->surfaces - 1; info->blksz = rdb.block_bytes; bcpl_strcpy(info->name, p->drive_name); disk_type = g->dos_type; info->type[0] = (disk_type & 0xFF000000)>>24; info->type[1] = (disk_type & 0x00FF0000)>>16; info->type[2] = (disk_type & 0x0000FF00)>>8; info->type[3] = '\\'; info->type[4] = (disk_type & 0x000000FF) + '0'; info->type[5] = 0; return 0; } void print_part_amiga (block_dev_desc_t *dev_desc) { //輸出分區信息 …… PRINTF("print_part_amiga: Scanning partition list\n"); block = rdb->partition_list; PRINTF("print_part_amiga: partition list at 0x%x\n", block); printf("Summary: DiskBlockSize: %d\n" " Cylinders : %d\n" " Sectors/Track: %d\n" " Heads : %d\n\n", rdb->block_bytes, rdb->cylinders, rdb->sectors, rdb->heads); printf(" First Num. \n" "Nr. Part. Name Block Block Type Boot Priority\n"); …… boot = get_bootcode(dev_desc); if (boot) { printf("Disk is bootable\n"); } } static void print_one_part (dos_partition_t *p, int ext_part_sector, int part_num) { //打印一個分區信息 …… printf ("%5d\t\t%10d\t%10d\t%2x%s\n", part_num, lba_start, lba_size, p->sys_ind, (is_extended (p->sys_ind) ? " Extd" : "")); } static int test_block_type(unsigned char *buffer) { //測試塊類型 if((buffer[DOS_PART_MAGIC_OFFSET + 0] != 0x55) || (buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) ) { return (-1); } //不是dos塊 if(strncmp(&buffer[DOS_PBR_FSTYPE_OFFSET],"FAT",3)==0) return DOS_PBR; /* is PBR */ return DOS_MBR; /* Is MBR */ } int test_part_dos (block_dev_desc_t *dev_desc) { //測試是否為dos塊 unsigned char buffer[DEFAULT_SECTOR_SIZE]; if ((dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *) buffer) != 1) || (buffer[DOS_PART_MAGIC_OFFSET + 0] != 0x55) || (buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) ) { return (-1); } return (0); } static void print_partition_extended (block_dev_desc_t *dev_desc, int ext_part_sector, int relative, int part_num) { //輸出與其擴展分區表有關的分區信息 …… //輸出所有主要/邏輯分區 pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); for (i = 0; i < 4; i++, pt++) {//文件系統不顯示MBR以外的分區 if ((pt->sys_ind != 0) && (ext_part_sector == 0 || !is_extended (pt->sys_ind)) ) { print_one_part (pt, ext_part_sector, part_num); } /* Reverse engr the fdisk part# assignment rule! */ if ((ext_part_sector == 0) || (pt->sys_ind != 0 && !is_extended (pt->sys_ind)) ) { part_num++; } } //處理擴展分區 pt = (dos_partition_t *) (buffer + DOS_PART_TBL_OFFSET); for (i = 0; i < 4; i++, pt++) { if (is_extended (pt->sys_ind)) { int lba_start = le32_to_int (pt->start4) + relative; print_partition_extended (dev_desc, lba_start, ext_part_sector == 0 ? lba_start : relative, part_num); } } return; } static int get_partition_info_extended (block_dev_desc_t *dev_desc, int ext_part_sector, int relative, int part_num, int which_part, disk_partition_t *info) { //獲取與其擴展分區表有關的分區信息 …… } …… Part_iso.c文件: int get_partition_info_iso_verb(block_dev_desc_t * dev_desc, int part_num, disk_partition_t * info, int verb) { //獲得iso設備塊的分區信息 …… //第一部分必須是主要卷 blkaddr=PVD_OFFSET; if (dev_desc->block_read (dev_desc->dev, PVD_OFFSET, 1, (ulong *) tmpbuf) != 1) return (-1); if(ppr->desctype!=0x01) { if(verb) printf ("** First descriptor is NOT a primary desc on %d:%d **\n", dev_desc->dev, part_num); return (-1); } if(strncmp(ppr->stand_ident,"CD001",5)!=0) { if(verb) printf ("** Wrong ISO Ident: %s on %d:%d **\n", ppr->stand_ident,dev_desc->dev, part_num); return (-1); } …… //尋找入口 if(strncmp(pbr->ident_str,"EL TORITO SPECIFICATION",23)!=0) { if(verb) printf ("** Wrong El Torito ident: %s on %d:%d **\n", pbr->ident_str,dev_desc->dev, part_num); return (-1); } bootaddr=le32_to_int(pbr->pointer); PRINTF(" Boot Entry at: %08lX\n",bootaddr); if (dev_desc->block_read (dev_desc->dev, bootaddr, 1, (ulong *) tmpbuf) != 1) { if(verb) printf ("** Can't read Boot Entry at %lX on %d:%d **\n", bootaddr,dev_desc->dev, part_num); return (-1); } …… //找到有效入口,現在尋找分區 entry_num=0; offset=0x20; sprintf (info->type, "U-Boot"); switch(dev_desc->if_type) { case IF_TYPE_IDE: case IF_TYPE_ATAPI: sprintf (info->name, "hd%c%d\n", 'a' + dev_desc->dev, part_num); break; …… default: sprintf (info->name, "xx%c%d\n", 'a' + dev_desc->dev, part_num); break; } //bootcatalog (including validation Entry) 必須小於 2048Bytes while(offset<2048) { pide=(iso_init_def_entry_t *)&tmpbuf[offset]; if ((pide->boot_ind==0x88) || (pide->boot_ind==0x00)) { //默認入口的id頭 if(entry_num==part_num) { //找到分區 goto found; } entry_num++; //記錄分區入口數 offset+=0x20; continue; } if ((pide->boot_ind==0x90) || /* Section Header Entry */ (pide->boot_ind==0x91) || /* Section Header Entry (last) */ (pide->boot_ind==0x44)) { /* Extension Indicator */ offset+=0x20; //跳過不使用的入口 } else { if(verb) printf ("** Partition %d not found on device %d **\n", part_num,dev_desc->dev); return(-1); } } …… newblkaddr=le32_to_int(pide->rel_block_addr); info->start=newblkaddr; PRINTF(" part %d found @ %lx size %lx\n",part_num,newblkaddr,info->size); return 0; } int get_partition_info_iso(block_dev_desc_t * dev_desc, int part_num, disk_partition_t * info) { return(get_partition_info_iso_verb(dev_desc, part_num, info, 1)); } void print_part_iso(block_dev_desc_t * dev_desc) { //打印分區信息 disk_partition_t info; int i; if(get_partition_info_iso_verb(dev_desc,0,&info,0)==-1) { printf("** No boot partition found on device %d **\n",dev_desc->dev); return; } printf("Part Start Sect x Size Type\n"); i=0; do { printf (" %2d %8ld %8ld %6ld %.32s\n", i, info.start, info.size, info.blksz, info.type); i++; } while (get_partition_info_iso_verb(dev_desc,i,&info,0)!=-1); } int test_part_iso (block_dev_desc_t *dev_desc) { 測試是否為iso分區 disk_partition_t info; return(get_partition_info_iso_verb(dev_desc,0,&info,0)); } Part_mac.c文件: int test_part_mac (block_dev_desc_t *dev_desc) { //檢查是否為有效的mac分區 …… if (part_mac_read_ddb (dev_desc, &ddesc)) {//讀塊錯誤,或無有效信息 return (-1); } n = 1; //假設最少有一個分區 for (i=1; i<=n; ++i) { if ((dev_desc->block_read(dev_desc->dev, i, 1, (ulong *)&mpart) != 1) || (mpart.signature != MAC_PARTITION_MAGIC) ) { return (-1); } n = mpart.map_count; 更新分區數 } return (0); } void print_part_mac (block_dev_desc_t *dev_desc) { //輸出mac分區信息 …… if (part_mac_read_ddb (dev_desc, &ddesc)) { /* error reading Driver Desriptor Block, or no valid Signature */ return; } n = ddesc.blk_count; …… printf ("Block Size=%d, Number of Blocks=%d, " "Total Capacity: %ld.%ld MB = %ld.%ld GB\n" "DeviceType=0x%x, DeviceId=0x%x\n\n" " #: type name" " length base (size)\n", ddesc.blk_size, ddesc.blk_count, mb.quot, mb.rem, gb.quot, gb.rem, ddesc.dev_type, ddesc.dev_id ); n = 1; //假設最少有一個分區 for (i=1; i<=n; ++i) { …… printf ("%20.32s %-18.32s %10u @ %-10u (%3ld%c)\n", mpart.type, mpart.name, mpart.block_count, mpart.start_block, bytes, c ); } return; } static int part_mac_read_ddb (block_dev_desc_t *dev_desc, mac_driver_desc_t *ddb_p) { //讀取設備描述塊信息 …… if (ddb_p->signature != MAC_DRIVER_MAGIC) { #if 0 printf ("** Bad Signature: expected 0x%04x, got 0x%04x\n", MAC_DRIVER_MAGIC, ddb_p->signature); #endif return (-1); } return (0); } static int part_mac_read_pdb (block_dev_desc_t *dev_desc, int part, mac_partition_t *pdb_p) { //讀取分區描述塊信息 int n = 1; for (;;) { //必須從第一個分區開始讀描述塊,只有這樣才能知道一共有多少個分區 if (dev_desc->block_read (dev_desc->dev, n, 1, (ulong *)pdb_p) != 1) { printf ("** Can't read Partition Map on %d:%d **\n", dev_desc->dev, n); return (-1); } if (pdb_p->signature != MAC_PARTITION_MAGIC) { printf ("** Bad Signature on %d:%d: " "expected 0x%04x, got 0x%04x\n", dev_desc->dev, n, MAC_PARTITION_MAGIC, pdb_p->signature); return (-1); } if (n == part) return (0); if ((part < 1) || (part > pdb_p->map_count)) { printf ("** Invalid partition %d:%d [%d:1...%d:%d only]\n", dev_desc->dev, part, dev_desc->dev, dev_desc->dev, pdb_p->map_count); return (-1); } n = part; //更新分區數 } } int get_partition_info_mac (block_dev_desc_t *dev_desc, int part, disk_partition_t *info) { //獲取mac分區的信息 mac_driver_desc_t ddesc; mac_partition_t mpart; if (part_mac_read_ddb (dev_desc, &ddesc)) { return (-1); } info->blksz = ddesc.blk_size; if (part_mac_read_pdb (dev_desc, part, &mpart)) { return (-1); } info->start = mpart.start_block; info->size = mpart.block_count; memcpy (info->type, mpart.type, sizeof(info->type)); memcpy (info->name, mpart.name, sizeof(info->name)); return (0); } ===== 四.常用U-BOOT命令介紹 ===== -list: 得到所有命令列表 -help: help usb, 列出USB功能的使用說明 -ping: 註:只能開發板PING別的機器 -setenv: 設置互環境變量: *setenv serverip 192.168.0.1 *setenv ipaddr 192.168.0.56 *setenv bootcmd 『tftp 32000000 vmlinux; kgo 32000000』 -saveenv: 保存環境變量,在設置好環境變量以後, 保存變量值 -tftp: tftp 32000000 vmlinux, 把server(IP=環境變量中設置的serverip)中/tftpdroot/ 下的vmlinux通過TFTP讀入到物理內存32000000處。 -kgo: 起動沒有壓縮的linux內核,kgo 32000000 -bootm: 起動UBOOT TOOLS製作的壓縮LINUX內核, bootm 3200000 -protect: 對FLASH進行寫保護或取消寫保護, protect on 1:0-3(就是對第一塊FLASH的0-3扇區進行保護),protect off 1:0-3取消寫保護 -erase: 刪除FLASH的扇區, erase 1:0-2(就是對每一塊FLASH的0-2扇區進行刪除) -cp: 在內存中複製內容, cp 32000000 0 40000(把內存中0x32000000開始的0x40000字節複製到0x0處) -mw: 對RAM中的內容寫操作, mw 32000000 ff 10000(把內存0x32000000開始的0x10000字節設為0xFF) -md: 修改RAM中的內容, md 32000000(內存的起始地址) -usb: *lusb start: 起動usb 功能 *lusb info: 列出設備 *lusb scan: 掃瞄usb storage(u 盤)設備 -fatls: 列出DOS FAT文件系統, 如:fatls usb 0列出第一塊U盤中的文件 -fatload: 讀入FAT中的一個文件,如:fatload usb 0:0 32000000 aa.txt -把USB中的aa.txt 讀到物理內存0x32000000處! -flinfo: 列出flash的信息 -loadb: 準備用KERMIT協議接收來自kermit或超級終端傳送的文件。 -nfs: nfs 32000000 192.168.0.2:aa.txt , 把192.168.0.2(LINUX 的NFS文件系統)中的NFS文件系統中的aa.txt 讀入內存0x32000000處。