uboot之u盤啓動kernel

AT91SAM9260

U-BOOT

OHCI

 

對於 U 盤啓動 kernel, 先通過了解整個框架,在細說 USB 枚舉(包括 HUB ,OHCI 等內容

 

一.            總體流程

U-BOOT 中, USB HOST 是可以不支持的,如果需要 U 盤啓動內核時,會使用此功能。

U_BOOT_CMD 中有 usb host 的命令結構,具體調用的是 do_usb ,若使用 USB storage U 盤),則同時會有 do_usbboot

使用 U 盤啓動內核的過程一般是,(舉例)

do_usb 函數中添加如下代碼,

if (strcmp(argv[1], "boot") == 0) {

                            char *s;

              s=getenv("boot");

              run_command(s,0);

              return 0;

       }

插上 U 盤之後,進入命令行模式,先寫命令 usb start 啓動 HUB U 盤設備,然後配置環境變量,如 boot=usbboot ,接着寫命令 usb boot ,如上代碼所示,取得環境變量之後,就會調用 do_usbboot 完成 U 盤啓動 kernel image

首先分析 do_usb do_usbboot USB storage 部分分析

do_usb--------usb_init,usb_init 主要調用 usb_lowlevel_init usb_scan_devices

識別設備之後,如果配置了 USB storage ,則會調用 usb_stor_scan 配置 U 盤,接着輸入命令 usb boot 就會調用 do_usbboot

 

二.            Usb_low_init

usb_low_init 主要流程如下,

a)      配置外部時鐘,使能時鐘,寄存器爲 PCER SCER

b)      配置 host controller 相關變量,包括

gohci,ghcca[0],phcca,ohci_dev,gtd,ptd,urb_priv

c)      hc_reset

寫命令到 host controller control 寄存器, reset ,等待 reset 結束 ( 不懂爲啥用兩次 reset)

d)     hc_start

 

三.            Usb device 的識別

1 Usb_scan_devices

usb_scan_devices 主要調用了兩個函數, usb_alloc_new_device usb_new_device

usb_alloc_new_device 初始化 usb_device *dev, 並將初始化成功的 dev 存放在數組 usb_dev 中,便於以後的查找

usb_new_device 先設置 Dev 的地址爲 0 ,然後通過枚舉過程得到新的 USB 設備的地址,設備地址一般從 1 開始分配, roothub 的地址就爲 1 u 盤的地址爲 2

USB 設備枚舉過程,

get device descriptor

set address

get device descriptor

get configuration descriptor

set configuration

get string

由於 root hub 是直接與 host controller 相連的,所以枚舉過程一定存在 ( 至少有一個 USB device)

最後調用 usb_hub_probe 識別連接上的設備是否爲 hub

 

2. 枚舉過程

具體的, scan devices 枚舉包括兩個部分,一個是 ROOT HUB 的枚舉過程,一個 u 盤的枚舉過程

a) ROOT HUB 的枚舉過程: 上述描述的過程識別處 ROOT HUB 這一 USB 設備後,還未確定是否是 HUB 設備,則調用 usb_hub_probe 識別,識別出是 HUB 設備後,調用 usb_hub_configure 配置 ROOT HUB ,由於是 HUB ,會重新進行 HUB 的枚舉過程

如同前面所說的數組 usb_dev 一樣,對於所有的 HUB 設備也有一個數組 hub_dev , usb_hub_configure 中,先分配一個 usb_hub_device *hub, 然後對 hub 進行枚舉,過程包括,(更詳細的枚舉見 uboot USB 枚舉)

usb_get_hub_descriptor 得到 hub descriptor length

usb_get_hub_descriptor 用新得到的 length 獲取對應長的描述符

usb_get_hub_status     獲取 port 數等

usb_set_port_feature 根據 Port 數調用相應次數,對每個 Port 進行 set feature

usb_get_port_status          根據 Port 數調用相應次數,獲取 Port 是否有設備插入的信息等

usb_hub_port_connect_change 對於 Port 端口有設備插入的,調用此函數,對新設備進行檢測枚舉,此函數中又包括如下函數操作,

       aa)usb_clear_port_feature

       ab)disconnect any existing devices under this port    wai_ms(200)

       ac)hub_port_reset 這裏需要 reset 的原因在於, usb 設備速度的識別,對於以前的 1.0 系列 USB ,只有全速和低速,所以無需 reset 也能識別(通過兩根數據線的上拉電阻識別),對於高速和全速,插上之後先識別爲全速設備, hub_port_reset 之後再通過電路時序識別是否是高速設備

       ad) 建立設備的樹結構, dev->children[port] usb->parent 實現

       ae) usb_new_device 這裏繼續調用此函數識別 USB 設備,如果連接到 root hub 上的設備仍然爲 HUB 設備,則會一直延續着調用此函數,直至 usb_hub_probe 識別出連接到 HUB Port 上的設備不爲 HUB ,對於 U 盤啓動,這裏 usb_new_device 在調用一次之後就不再調用, usb_hub_probe 識別出 U 盤設備不爲 HUB 設備

 

b)U 盤的枚舉過程(假設已經連接上): 這裏由於在 usb_hub_port_connect_change 中檢測到 Port 上有 USB device ,所以會調用 usb_new_device 進行枚舉,從而 USB device 得到識別,接着在 調用 usb_stor_scan 配置 U

(具體見 uboot usb 枚舉)

 

四.            USB storage

u-boot 中如果配置 usb storage ,則會調用函數 usb_stor_scan

主要流程如下:

     ( 1 初始化結構 usb_dev_desc[], 該結構爲 block_dev_desc ,包括 target SCSI ID,type of the interface(usb),device number,part_type( 未知 ) ,以及初始化 block_read 方法

       2 )調用函數 usb_get_dev_index 找到 usb_dev[] 數組下標得到 u 盤設備的 usb_device * 結構指針

       3 )調用 usb_storage_probe ,取得 us_data usb_stor[]

該函數包括三個重要數據結構 usb_device usb_interface_descriptor us_data ,主要目的就是根據 usb_device usb_interface_descriptor 來填充 usb_data 結構,如下圖所示,

 

     這裏一些賦值操作主要還是枚舉中獲得的接口信息,所以熟悉枚舉過程是很必要的。

       4 )最後調用 usb_stor_get_info 取得 block_dev_desc_t usb_dev_desc[]

       該函數主要是 U 盤的一些操作,如 INQUIRY,READ CAPACITY ,要來填充結構 static block_dev_desc_t usb_dev_desc[USB_MAX_STOR_DEV] ,便於後面引導 kernel 中所需的信息。(這裏許多 UFI 命令可以參考 USB 枚舉過程) , 這裏涉及的 CCB 結構就不講了,枚舉中會細說

       流程大致如下,

a) 對於一些特定的 USB 設備,不需要 transport_reset, 否則調用 transport_reset 方法。

b) 發送 USB_INQUIRY 命令,獲取 U 盤基本信息 ( 這裏涉及的可以看 U-BOOT USB 枚舉 ) (調用 transport 方法)

c) 調用 usb_test_unit_ready 查看設備是否準備就緒(調用 transport 方法)

d) 調用 usb_read_capacity 讀取 U 盤容量(返回信息包括塊數目,塊大小,設備類型,例如 DOS 盤等)(調用 transport 方法)

e) 調用 init_part 初始化分區,對於 DOS 盤,則調用 test_part_dos 讀取分區頭信息,檢測是否正確(調用 block_read 方法) , 對於 DOS 盤,讀取出的第一塊的信息,要求 0x1fe 偏移處值爲 0x55,0x1ff 0xaa

下面主要講講涉及到的三個方法, transport_reset transport block_read

    i)                    tranport_reset

    transport_reset 指向的函數爲 usb_stor_BBB_reset reset 的步驟需要嚴格支持以下步驟,

  /*

    * Reset recovery (5.3.4 in Universal Serial Bus Mass Storage Class)

    *

    * For Reset Recovery the host shall issue in the following order:

    * a) a Bulk-Only Mass Storage Reset

    * b) a Clear Feature HALT to the Bulk-In endpoint

    * c) a Clear Feature HALT to the Bulk-Out endpoint

    *

    * This is done in 3 steps.

    *

    * If the reset doesn't succeed, the device should be port reset.

    *

    * This comment stolen from FreeBSD's /sys/dev/usb/umass.c.

    */

所以調用的分別是 usb_control_msg

usb_clear_halt, usb_clear_hatl

 

    ii)                  transport

transport 指向的函數 usb_stor_bbb_transport ,該函數主要調用函數 usb_stor_BBB_comdat 填充 CBW ,接着調用 usb_bulk_msg 發送 CBW, 調用 usb_bulk_msg 返回數據,調用 usb_bulk_msg 接收 CSW ,進行結構 CSW 解析,查看是否發送成功,如果有誤,則 reset ,即 usb_stor_BBB_reset

    iii)                block_read

該部分實際調用的是 UFI 命令,實際上仍然是 transport 方法,讀取指定塊數據,略

 

五.            USB 引導 kernel

在最後一步,做 U 盤引導 kernel 時,最好格式化 U 盤,然後將 kernel.img 存入 U 盤進行引導。

這部分的原理與 UBOOT 最後檢查 img 的合法性,然後調用 do_bootm 方法實現引導 kernel 原理一致(只不過多了一步分析 U 盤的文件系統的步驟)

當然這裏所說的啓動 kernel 的方式還使用命令行,實際上稍微修改下代碼是可以實現自動檢測 kernel ,如果存在且合法則啓動,否則使用默認 kernel

該部分調用函數 do_usbboot, 首先通過環境變量 bootdevice 得到引導設備的下標,從而找到引導設備。

這裏可以設置 bootdevice=0, 則爲獲取得的 block_dev_desc * stor_dev 指針 .

具體的流程爲,(這裏主要調用的方法是 block_read

a)      get_partition_info 獲取分區信息,分析分區信息

b)      讀取 img img_head ,分析合法性,如果合法,則調用 do_bootm 引導 kernel (這裏設置 autostart=yes 這一環境變量則合法直接引導)

該函數的重點在於分析 U 盤的文件系統。事實上, uboot 中的對於 u 盤啓動 kernel 的方法是不完善的(支持引導 DOS 盤),特別是對與普通的 U 盤。(查看源代碼可以發現這一不足,本人用 U 盤引導 kernel 時也出現一些問題,主要是文件系統方面的)

下面對 FAT32 文件系統簡單分析( FAT16 類似,如果需要完善 U 盤引導 kernel 這部分,需要對大多數的 U 盤支持,所以最好對大多數的文件系統熟悉,並且對需要用到的信息封裝結構體,這樣在便於實現的同時也實現對不同文件系統的支持)

這裏只分析與引導 kernel 部分有關的,具體的可以參照《 4.5 萬字透視 FAT32 文件系統》

由於 U 盤是經過格式化的,且一般 img 的大小不會超過第一分區的大小,所以通過計算 img 起始位置,然後讀取 img 對應的塊數數據即可

下圖是 FAT 文件系統的部分結構圖

 

一般而言只要分析出 DBR 中相關數據,即可計算起始位置

下圖是 U 盤的 DBR 截圖(用的是 WINHEX

可以看到該 U 盤使用的是 FAT32 文件系統

參照 FAT32 BPB 說明,根據下圖信息即可找到對應的起始地址,具體的就分析略。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章