1. 程式人生 > >linux核心裝置樹及編譯

linux核心裝置樹及編譯

1、裝置樹的概念

        在核心原始碼中,存在大量對板級細節資訊描述的程式碼。這些程式碼充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目錄,對核心而言這些platform裝置、resource、i2c_board_info、spi_board_info以及各種硬體的platform_data絕大多數純屬垃圾冗餘程式碼。為了解決這一問題,ARM核心版本3.x之後引入了原先在Power PC等其他體系架構已經使用的Flattened Device Tree。

        開源文件中對裝置樹的描述是,一種描述硬體資源的資料結構,它通過bootloader將硬體資源傳給核心,使得核心和硬體資源描述相對獨立。

         Device Tree可以描述的資訊包括CPU的數量和類別、記憶體基地址和大小、匯流排和橋、外設連線、中斷控制器和中斷使用情況、GPIO控制器和GPIO使用情況、Clock控制器和Clock使用情況

         另外,裝置樹對於可熱插拔的裝置不進行具體描述,它只描述用於控制該熱插拔裝置的控制器

         裝置樹的主要優勢:對於同一SOC的不同主機板,只需更換裝置樹檔案.dtb即可實現不同主機板的無差異支援,而無需更換核心檔案

注:要使得3.x之後的核心支援使用裝置樹,除了核心編譯時需要開啟相對應的選項外,bootloader也需要支援將裝置樹的資料結構傳給核心

2、裝置樹的組成和使用

        裝置樹包含DTC(device tree compiler),DTS(device tree source和DTB(device tree blob)。其對應關係如下圖所示:

                                                               

2.1 DTS和DTSI(原始檔)

        .dts檔案是一種ASCII文字對Device Tree的描述,放置在核心的/arch/arm/boot/dts目錄。一般而言,一個.dts檔案對應一個ARM的machine。

         由於一個SOC可能有多個不同的電路板(  .dts檔案為板級定義, .dtsi檔案為SoC級定義),而每個電路板擁有一個 .dts。這些dts勢必會存在許多共同部分,為了減少程式碼的冗餘,裝置樹將這些共同部分提煉儲存在.dtsi檔案中,供不同的dts共同使用

。.dtsi的使用方法,類似於C語言的標頭檔案,在dts檔案中需要進行include .dtsi檔案。當然,dtsi本身也支援include 另一個dtsi檔案。

2.2 DTC (編譯工具)

        DTC為編譯工具,dtc編譯器可以把dts檔案編譯成為dtb,也可把dtb編譯成為dts檔案。在3.x核心版本中,DTC的原始碼位於核心的scripts/dtc目錄,核心選中CONFIG_OF,編譯核心的時候,主機可執行程式DTC就會被編譯出來。 即scripts/dtc/Makefile中

  1. hostprogs-:= dtc
  2. always := $(hostprogs-y) 

        在核心的arch/arm/boot/dts/Makefile中,若選中某種SOC,則與其對應相關的所有dtb檔案都將編譯出來。在linux下,make dtbs可單獨編譯dtb。以下截取了TEGRA平臺的一部分。

  1. ifeq ($(CONFIG_OF),y)
  2. dtb-$(CONFIG_ARCH_TEGRA) += tegra20-harmony.dtb \
  3. tegra30-beaver.dtb \
  4. tegra114-dalmore.dtb \
  5. tegra124-ardbeg.dtb 
        在2.6.x版本核心中,只在powerpc架構下使用了裝置樹,DTC的原始碼位於核心的arch/powerpc/boot/dtc-src目錄,編譯核心後,可將DTC編譯出來,DTC編譯工具位於arch/powerpc/boot目錄下。

2.3 DTB (二進位制檔案)

       DTC編譯.dts生成的二進位制檔案(.dtb),bootloader在引導核心時,會預先讀取.dtb到記憶體,進而由核心解析

        在2.6.x版本核心中,在powerpc架構下,dtb檔案可以單獨進行編譯,編譯命令格式如下:

dtc [-I input-format] [-O output-format][-o output-filename] [-V output_version] input_filename

引數說明

input-format:

- “dtb”: “blob” format

- “dts”: “source” format.

- “fs” format.

output-format:

- “dtb”: “blob” format

- “dts”: “source” format

- “asm”: assembly language file

output_version:

定義”blob”的版本,在dtb檔案的欄位中有表示,支援1 2 3和16,預設是3,在16版本上有許多特性改變

(1)  Dts編譯生成dtb

./dtc -I dts -O dtb -o B_dtb.dtb A_dts.dts

把A_dts.dts編譯生成B_dtb.dtb

(2)  Dtb編譯生成dts

./dtc -I dtb -O dts -o A_dts.dts A_dtb.dtb

把A_dtb.dtb反編譯生成為A_dts.dts

        在linux 3.x核心中,可以使用make的方式進行編譯。

2.4 Bootloader(boottloader支援)

    Bootloader需要將裝置樹在記憶體中的地址傳給核心。在ARM中通過bootm或bootz命令來進行傳遞。    

    bootm [kernel_addr] [initrd_address] [dtb_address],其中kernel_addr為核心映象的地址,initrd為initrd的地址dtb_address為dtb所在的地址。若initrd_address為空,則用“-”來代替。

3、linux核心對硬體的描述方式

       在以前的核心版本中:
1)核心包含了對硬體的全部描述;
2)bootloader會載入一個二進位制的核心映象,並執行它,比如uImage或者zImage;
3)bootloader會提供一些額外的資訊,成為ATAGS,它的地址會通過r2暫存器傳給核心;
    ATAGS包含了記憶體大小和地址,kernel command line等等;
4)bootloader會告訴核心載入哪一款board,通過r1暫存器存放的machine type integer;
5)U-Boot的核心啟動命令:bootm <kernel img addr>
6)Barebox變數:bootm.image (?)


現今的核心版本使用了Device Tree:
1)核心不再包含對硬體的描述,它以二進位制的形式單獨儲存在另外的位置:the device tree blob
2)bootloader需要載入兩個二進位制檔案:核心映象和DTB
    核心映象仍然是uImage或者zImage;
    DTB檔案在arch/arm/boot/dts中,每一個board對應一個dts檔案;
3)bootloader通過r2暫存器來傳遞DTB地址,通過修改DTB可以修改記憶體資訊,kernel command line,以及潛在的其它資訊;
4)不再有machine type;
5)U-Boot的核心啟動命令:bootm <kernel img addr> - <dtb addr>
6)Barebox變數:bootm.image,bootm.oftree


        有些bootloader不支援Device Tree,或者有些專門給特定裝置寫的版本太老了,也不包含。為了解決這個問題,CONFIG_ARM_APPENDED_DTB被引進。 
    它告訴核心,在緊跟著核心的地址裡查詢DTB檔案;
    由於沒有built-in Makefile rule來產生這樣的核心,因此需要手動操作:
        cat arch/arm/boot/zImage arch/arm/boot/dts/myboard.dtb > my-zImage
        mkimage ... -d my-zImage my-uImage
    (cat這個命令,還能夠直接合並兩個mp3檔案哦!so easy!)
另外,CONFIG_ARM_ATAG_DTB_COMPAT選項告訴核心去bootloader裡面讀取ATAGS,並使用它們升級DT。

4、DTB載入及解析過程

    先從uboot裡的do_bootm出發,根據之前描述,DTB在記憶體中的地址通過bootm命令進行傳遞。在bootm中,它會根據所傳進來的DTB地址,對DTB所在記憶體做一系列操作,為核心解析DTB提供保證。上圖為對應的函式呼叫關係圖。

    在do_bootm中,主要呼叫函式為do_bootm_states,第四個引數為bootm所要處理的階段和狀態。 

    在do_bootm_states中,bootm_start會對lmb進行初始化操作,lmb所管理的實體記憶體塊有三種方式獲取。起始地址,優先順序從上往下:

  1.  環境變數“bootm_low”
  2.  巨集CONFIG_SYS_SDRAM_BASE(在tegra124中為0x80000000)
  3.  gd->bd->bi_dram[0].start

大小:

  1.  環境變數“bootm_size”
  2.  gd->bd->bi_dram[0].size

    經過初始化之後,這塊記憶體就歸lmb所管轄。接著,呼叫bootm_find_os進行kernel映象的相關操作,這裡不具體闡述。

    還記得之前講過bootm的三個引數麼,第一個引數核心地址已經被bootm_find_os處理,而接下來的兩個引數會在bootm_find_other中執行操作。

    首先,bootm_find_other根據第二個引數找到ramdisk的地址,得到ramdisk的映象;然後根據第三個引數得到DTB映象,同檢查kernel和ramdisk映象一樣,檢查DTB映象也會進行一系列的校驗工作,如果校驗錯誤,將無法正常啟動核心。另外,uboot在確認DTB映象無誤之後,會將該地址儲存在環境變數“fdtaddr”中。

    接著,uboot會把DTB映象reload一次,使得DTB映象所在的實體記憶體歸lmb所管理:    

  • ①boot_fdt_add_mem_rsv_regions會將原先的記憶體DTB映象所在的記憶體置為reserve,保證該段記憶體不會被其他非法使用,保證接下來的reload資料是正確的;
  • ②boot_relocate_fdt會在bootmap區域中申請一塊未被使用的記憶體,接著將DTB映象內容複製到這塊區域(即歸lmb所管理的區域)

注:若環境變數中,指定“fdt_high”引數,則會根據該值,呼叫lmb_alloc_base函式來分配DTB映象reload的地址空間。若分配失敗,則會停止bootm操作。因而,不建議設定fdt_high引數。

    接下來,do_bootm會根據核心的型別呼叫對應的啟動函式。與linux對應的是do_bootm_linux。

  • ① boot_prep_linux

        為啟動後的kernel準備引數

  • ② boot_jump_linux

    以上是boot_jump_linux的片段程式碼,可以看出:若使用DTB,則原先用來儲存ATAG的暫存器R2,將會用來儲存.dtb映象地址。

    boot_jump_linux最後將呼叫kernel_entry,將.dtb映象地址傳給核心。

    下面我們來看下核心的處理部分:

    在arch/arm/kernel/head.S中,有這樣一段:

    _vet_atags定義在/arch/arm/kernel/head-common.S中,它主要對DTB映象做了一個簡單的校驗。

    真正解析處理dbt的開始部分,是setup_arch->setup_machine_fdt。這部分的處理在第五部分的machine_mdesc中有提及。

    如圖,是setup_machine_fdt中的解析過程。

  •     解析chosen節點將對boot_command_line進行初始化。
  •     解析根節點的{size,address}將對dt_root_size_cells,dt_root_addr_cells進行初始化。為之後解析memory等其他節點提供依據。
  •     解析memory節點,將會把節點中描述的記憶體,加入memory的bank。為之後的記憶體初始化提供條件。
  •     解析裝置樹在函式unflatten_device_tree中完成,它將.dtb解析成device_node結構(第五部分有其定義),並構成單項鍊表,以供OF的API介面使用。

下面主要結合程式碼分析:/drivers/of/fdt.c

 

 

總的歸納為:

    ① kernel入口處獲取到uboot傳過來的.dtb映象的基地址

    ② 通過early_init_dt_scan()函式來獲取kernel初始化時需要的bootargs和cmd_line等系統引導引數。

    ③ 呼叫unflatten_device_tree函式來解析dtb檔案,構建一個由device_node結構連線而成的單向連結串列,並使用全域性變數of_allnodes儲存這個連結串列的頭指標。

    ④ 核心呼叫OF的API介面,獲取of_allnodes連結串列資訊來初始化核心其他子系統、裝置等。


相關推薦

linux核心裝置編譯

1、裝置樹的概念         在核心原始碼中,存在大量對板級細節資訊描述的程式碼。這些程式碼充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目錄,對核心而言這些pl

轉載:Linux核心 裝置操作常用API

Linux裝置樹語法詳解一文中介紹了裝置樹的語法,這裡主要介紹核心中提供的操作裝置樹的API,這些API通常都在"include/of.h"中宣告。 device_node,核心中用下面的這個結構描述裝置樹中的一個節點,後面的API都需要一個device_nod

linux kernel裝置編譯和反編譯

在使用Nvidia TX2平臺時使用到裝置樹的編譯和反編譯命令,記錄如下:1.裝置樹的編譯命令有以下兩種方式:(1)將裝置樹檔案拷貝到核心原始碼的arch/*(處理器平臺)/boot/dts/*(廠家)

linux 裝置節點引用

說了這麼半天,跟引入裝置樹有什麼關係呢?華清教學使用的開發板(A8/A9)都使用DM9000網絡卡晶片。DM9000驅動是開源的,在主線核心原始碼中就有。我們每次基於A8/A9板子移植的時候,DM9000驅動並沒有修改過,僅僅是選配了下,主要的工作是在板級檔案中添加了裝置資訊。DM9000驅動使用的是plat

Linux系統移植——裝置檔案編譯與反編譯

裝置樹檔案編譯與反編譯 一、裝置樹編譯 有兩種方式 1、將裝置樹檔案拷貝到核心原始碼的arch/*(處理器平臺)/boot/dts/*(廠家)/目錄下,    執行make dtbs 2、dtc -I dts -O dtb  *.dts > my.dtb 二、裝置

CUDA在Windows/Linux平臺的配置編譯

text ref gpu加速 spa ron linux平臺 opencv3 v3.4 c++ 前段時間,在TX2上裝了OpenCV3.4,TX2更新源失敗的問題,OpenCV內部很多函數都已經實現了GPU加速,但是我們手動寫的函數,想要通過GPU加速就需要手動調用CU

linux 核心模組程式設計之編譯多個原始檔(三)

編譯擁有多個原始檔的核心模組的方式和編譯一個原始檔的方式差不多,我們先來看下我們需要的檔案都有哪些。 首先是main.c檔案 #include <linux/module.h> #include <linux/init.h> MODULE_LICENSE

wlh- beagle bone 通過uboot tftp 載入zImage 裝置 nfs 掛載根檔案系統

首先重啟Ubuntu 伺服器的 tftp 和nfs  sudo /etc/init.d/xinetd restart 命令              重啟 xinetd  tftp服務 sudo&nbs

Linux獲取裝置資源

Linux 獲取裝置樹原始檔(DTS)裡的資源 韓大衛@吉林師範大學 在linux使用platform_driver_register() 註冊 platform_driver 時, 需要在 platform_driver 的probe() 裡面知道裝置的中斷號,

Linux DTS 裝置

原文:http://blog.csdn.net/woshidahuaidan2011/article/details/52948732 裝置樹就是有一些屬性和節點組成的一種資料結構,屬性一般會賦予一些屬性值,而節點則可能是是由屬性跟其下的子節點構成。下面是一個

Linux 獲取裝置原始檔(DTS)裡的資源

韓大衛@吉林師範大學 在linux使用platform_driver_register() 註冊 platform_driver 時, 需要在 platform_driver 的probe() 裡面知道裝置的中斷號, 記憶體地址等資源。 這些資源的描述資訊存放在 resource 資料結構中, 相同的

H3 Linux4.11核心裝置裝置驅動開發1

AllWinnerH3 linux4.11版本的bsp下載: https://pan.baidu.com/s/1mhU4a8K 密碼: b375 H3-linux4.11_bsp目錄就是所需的原始碼及編譯工具. 下載後, 先安裝tools目錄裡的dtc工具

linux驅動-裝置

裝置樹 -小白總結,謹慎參考 裝置樹是從軟體的角度描述硬體,DTS是裝置樹原始檔。DTC是負責將DTS轉換成DTB,DTB是DTS的二進位制形式,供機器使用。 裝置樹,首先是一個樹形結構,除了根節點外其他子節點都有唯一的父節點,節點下可以有子節點和屬性。屬性由名字和值組成。

linux 根據裝置註冊裝置

/platform.c  (1) int of_platform_populate(struct device_node *root, const struct of_device_id *matches,const struct of_dev_auxdata *looku

面向物件地分析Linux核心裝置驅動(2)——Linux核心裝置模型與匯流排

Linux核心裝置模型與匯流排 -         核心版本 Linux Kernel 2.6.34, 與 Robert.Love的《Linux Kernel Development》(第三版)所講述的核心版本一樣 1.      Linux核心裝置模型分析 1)  

linux核心外部驅動模組編譯報錯ERROR: "******" [drivers/**.ko] undefined!

我往linux核心裡新增一個驅動模組,根據《linux裝置驅動》第三版,修改Makefile如下: obj-m := GobiNet.o GobiNet-objs := GobiUSBNet.o QMIDevice.o QMI.o 但是出現瞭如下錯誤:

經典linux核心面試題答案

1) Linux中主要有哪幾種核心鎖? 2) Linux中的使用者模式和核心模式是什麼含意? 3) 怎樣申請大塊核心記憶體? 4) 使用者程序間通訊主要哪幾種方式? 5) 通過夥伴系統申請核心記憶體的函式有哪些? 6) 通過slab分配器申請核心記憶體的函式有?

Linux 核心裝置驅動之GPIO驅動之GPIO sysfs支援

需要核心配置CONFIG_GPIO_SYSFS int gpiochip_sysfs_register(struct gpio_device *gdev) {  struct device *dev;  struct device *parent;  struct gpi

深入探究Linux/VxWorks裝置視訊教程-宋寶華-專題視訊課程

深入探究Linux/VxWorks裝置樹視訊教程—5116人已學習 課程介紹        很多人看了很Linux多裝置樹的資料後,還是不會用裝置樹。該Linux裝置樹視訊課程假設一個全新的電路板,上面有4個ARM核,一片中斷控制器、一個UART、一個GPIO控制器、一個I2

Linux核心原始碼目錄

arch:包含和硬體體系結構相關的程式碼,每種平臺佔一個相應的目錄。和32位PC相關的程式碼存放在i386目錄下,其中比較重要的包括kernel(核心核心部分)、mm(記憶體管理)、math-emu(浮點單元模擬)、lib(硬體相關工具函式)、boot(載入程式)、pci(