├── 第5章-U-boot代码重定位.md ├── README.md ├── images ├── IVT.jpg ├── DM.xmind ├── GPIO_2.png ├── gpio.jpg ├── uboot.jpg ├── 20E_0234.png ├── GPIO_DR.png ├── GPIO_PSR.png ├── KEY_ROW6.png ├── dm_init.png ├── do_bootm.png ├── initf_dm.png ├── u-boot.xmind ├── GPIO_GDIR.png ├── device_file.png ├── dm_scan_fdt.png ├── dt_struct.png ├── dt_struct_2.jpg ├── dtb_struct.png ├── GPIO_DR_Field.png ├── board_init_f.jpg ├── device_probe.png ├── driver_model.jpg ├── fdtdec_setup.png ├── GPIO_Memory_Map.png ├── Muxing_Options.png ├── Pad_Mux_Register.png ├── dm_scan_platdata.png ├── do_bootm_linux.png ├── memory_mapping.jpg ├── u-boot_makefile.JPG └── Pad_Control_Register.png ├── 第8章-U-boot管脚复用.md ├── 第9章-U-boot编译之二编译过程.md ├── 第4章-U-boot驱动模型之一基本内容.md ├── 第7章-U-boot移植.md ├── 第9章-U-boot编译之一配置过程.md ├── 第4章-U-boot驱动模型之四实例.md ├── 第1章-U-boot启动流程.md ├── 第2章-U-boot设备树.md ├── 第6章-U-boot启动内核之一uImage.md ├── 第6章-U-boot启动内核之二bootm启动内核.md ├── 第0章-U-boot基础.md ├── 第3章-U-boot命令行.md ├── 第4章-U-boot驱动模型之二数据结构.md ├── 第4章-U-boot驱动模型之三流程.md └── LICENSE /第5章-U-boot代码重定位.md: -------------------------------------------------------------------------------- 1 | ## **第5章-U-boot代码重定位** 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # U-boot 2 | U-boot学习笔记 3 | 嵌入式项目,基于imx6q的编译执行 -------------------------------------------------------------------------------- /images/IVT.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/IVT.jpg -------------------------------------------------------------------------------- /images/DM.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/DM.xmind -------------------------------------------------------------------------------- /images/GPIO_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/GPIO_2.png -------------------------------------------------------------------------------- /images/gpio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/gpio.jpg -------------------------------------------------------------------------------- /images/uboot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/uboot.jpg -------------------------------------------------------------------------------- /images/20E_0234.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/20E_0234.png -------------------------------------------------------------------------------- /images/GPIO_DR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/GPIO_DR.png -------------------------------------------------------------------------------- /images/GPIO_PSR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/GPIO_PSR.png -------------------------------------------------------------------------------- /images/KEY_ROW6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/KEY_ROW6.png -------------------------------------------------------------------------------- /images/dm_init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/dm_init.png -------------------------------------------------------------------------------- /images/do_bootm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/do_bootm.png -------------------------------------------------------------------------------- /images/initf_dm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/initf_dm.png -------------------------------------------------------------------------------- /images/u-boot.xmind: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/u-boot.xmind -------------------------------------------------------------------------------- /images/GPIO_GDIR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/GPIO_GDIR.png -------------------------------------------------------------------------------- /images/device_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/device_file.png -------------------------------------------------------------------------------- /images/dm_scan_fdt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/dm_scan_fdt.png -------------------------------------------------------------------------------- /images/dt_struct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/dt_struct.png -------------------------------------------------------------------------------- /images/dt_struct_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/dt_struct_2.jpg -------------------------------------------------------------------------------- /images/dtb_struct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/dtb_struct.png -------------------------------------------------------------------------------- /images/GPIO_DR_Field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/GPIO_DR_Field.png -------------------------------------------------------------------------------- /images/board_init_f.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/board_init_f.jpg -------------------------------------------------------------------------------- /images/device_probe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/device_probe.png -------------------------------------------------------------------------------- /images/driver_model.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/driver_model.jpg -------------------------------------------------------------------------------- /images/fdtdec_setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/fdtdec_setup.png -------------------------------------------------------------------------------- /images/GPIO_Memory_Map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/GPIO_Memory_Map.png -------------------------------------------------------------------------------- /images/Muxing_Options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/Muxing_Options.png -------------------------------------------------------------------------------- /images/Pad_Mux_Register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/Pad_Mux_Register.png -------------------------------------------------------------------------------- /images/dm_scan_platdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/dm_scan_platdata.png -------------------------------------------------------------------------------- /images/do_bootm_linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/do_bootm_linux.png -------------------------------------------------------------------------------- /images/memory_mapping.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/memory_mapping.jpg -------------------------------------------------------------------------------- /images/u-boot_makefile.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/u-boot_makefile.JPG -------------------------------------------------------------------------------- /images/Pad_Control_Register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhaojh329/U-boot-1/HEAD/images/Pad_Control_Register.png -------------------------------------------------------------------------------- /第8章-U-boot管脚复用.md: -------------------------------------------------------------------------------- 1 | ## **第8章-U-boot管脚复用** 2 | > 需要配置寄存器:pad控制器,Mux控制器,select input寄存器和Data寄存器 3 | 配置IOMUX的必备工具:1.芯片原理图;2.芯片软件手册; 3.内核源代码 4 | 5 | ### **1. 查看原理图** 6 | **找到KEY_ROW6对应的GPIO_2** 7 | ![](./images/KEY_ROW6.png) 8 | 9 | ### **2. 找到对应的Pad/Group Registers** 10 | **找到GPIO_2对应的控制寄存器 SW_PAD_CTL_PAD_GPIO02** 11 | ![](./images/GPIO_2.png) 12 | 13 | **SW_PAD_CTL_PAD_GPIO02寄存器: 控制引脚上拉电阻、下拉电阻和电源控制** 14 | ![](./images/Pad_Control_Register.png) 15 | 16 | ### **3. 根据ALT5:GPIO1_IO02查找IOMUXC寄存器** 17 | **a) IOMUXC:IOMUX_SW_MUX_CTL_PAD_GPIO02** 18 | > GPIO1_IO02代表GPIO1组寄存器的第02个寄存器,也就是第3位 19 | 20 | ![](./images/Muxing_Options.png) 21 | 22 | **b) GPIO_2的功能ALT5:GPIO1_IO02用于查找其他寄存器** 23 | > 设置ALT5: 101模式 24 | 25 | ![](./images/Pad_Mux_Register.png) 26 | 27 | ### **4. GPIO编程** 28 | #### **4.1 读操作** 29 | **1. 通过配置IOMUXC控制器使得IOMUX选择GPIO模式** 30 | **2. 配置GPIO direction register为input模式(GPIO_GDIR[GDIR]设置为0)** 31 | **3. 从data寄存器读值** 32 | > While the GPIO direction is set to input (GPIO_GDIR = 0), a read access to GPIO_DR does not return GPIO_DR data. Instead, it returns the GPIO_PSR data, which is the corresponding input signal value.实际上PSR才是读到的值 33 | 34 | ``` 35 | // SET INPUTS TO GPIO MODE. 36 | write sw_mux_ctl____, 32'h00000000 37 | // SET GDIR TO INPUT. 38 | write GDIR[31:4,input3_bit, input2_bit, input1_bit, input0_bit,] 32'hxxxxxxx0 39 | // READ INPUT VALUE FROM DR. 40 | read DR 41 | // READ INPUT VALUE FROM PSR. 42 | read PSR 43 | 44 | ``` 45 | 46 | #### **4.2 写操作** 47 | **1. 通过配置IOMUXC控制器使得IOMUX选择GPIO模式,如果需要通过PSR寄存器读取值,则需要enable SION位(通常置1)** 48 | **2. 配置GPIO direction register为output模式(GPIO_GDIR[GDIR]设置为1)** 49 | **3. 向data寄存器写值(GPIO_DR)** 50 | 51 | ``` 52 | // SET PADS TO GPIO MODE VIA IOMUX. 53 | write sw_mux_ctl_pad_.mux_mode, 54 | // Enable loopback so we can capture pad value into PSR in output mode 55 | write sw_mux_ctl_pad_.sion, 1 56 | // SET GDIR=1 TO OUTPUT BITS. 57 | write GDIR[31:4,output3_bit,output2_bit, output1_bit, output0_bit,] 32'hxxxxxxxF 58 | // WRITE OUTPUT VALUE=4’b0101 TO DR. 59 | write DR, 32'hxxxxxxx5 60 | // READ OUTPUT VALUE FROM PSR ONLY. 61 | read_cmp PSR, 32'hxxxxxxx5 62 | ``` 63 | 64 | ### **5. 配置GDIR, PSR, DR寄存器** 65 | **GPIO1寄存器组基地址** 66 | > GPIO1_IO02: IO02代表第3个寄存器,其他寄存器根据偏移量指定 67 | 68 | ![](./images/GPIO_Memory_Map.png) 69 | 70 | #### **5.1 配置GDIR方向寄存器** 71 | > 0x000 0100 72 | 73 | ![](./images/GPIO_GDIR.png) 74 | 75 | #### **5.2 配置DR数据寄存器** 76 | ![](./images/GPIO_DR.png) 77 | ![](./images/GPIO_DR_Field.png) 78 | 79 | #### **5.3 从PSR寄存器读取值** 80 | ![](./images/GPIO_PSR.png) 81 | 82 | 83 | 84 | ### **6. 实例** 85 | > **点亮上面流程中的LED灯** 86 | 87 | #### **6.1 设置复用功能控制器IOMUX为GPIO模式** 88 | ``` 89 | // 复用控制器地址 90 | volatile uint_32 *p_GPIO02_IOMUX = (uint_32*)0x20E0234u; 91 | // 配置 92 | *p_GPIO02_IOMUX &= 0; // 清MUX位段 93 | *p_GPIO02_IOMUX |= 0x15; // 设置为GPIO模式 94 | 95 | ``` 96 | 97 | #### **6.2 设置方向寄存器GDIR** 98 | ``` 99 | // 基地址 100 | volatile uint_32 *p_BASE = (uint_32*)0x209C000u; 101 | // 方向寄存器地址 102 | volatile uint_32 *p_GPIO_GDIR = p_BASE + 4; 103 | // 1为output输出模式 104 | *p_GPIO_GDIR |= (1 << 2) 105 | // 0位input输入模式 106 | *p_GPIO_GDIR &= ~(1 << 2) 107 | ``` 108 | 109 | #### **6.3 设置数据寄存器DR** 110 | ``` 111 | // 数据寄存器地址 112 | volatile uint_32 *p_GPIO_DR = p_BASE + 0; 113 | // 亮 114 | *p_GPIO_DR |= (1<<2); 115 | // 灭 116 | *p_GPIO_DR &= ~(1<<2); 117 | ``` 118 | 119 | #### **6.4 查看PSR寄存器** 120 | ``` 121 | // PSR寄存器基地址 122 | volatile uint_32 *p_GPIO_PSR = p_BASE + 8; 123 | // 查看PSR内容 124 | *p_GPIO_PSR 125 | ``` 126 | -------------------------------------------------------------------------------- /第9章-U-boot编译之二编译过程.md: -------------------------------------------------------------------------------- 1 | # 第9章-uboot编译之编译过程 2 | [u-boot-2016.09 make编译过程分析(一)](http://blog.csdn.net/guyongqiangx/article/details/52558087) 3 | > Makefile核心是依赖和命令;对于每个目标,会先检查依赖,如果依赖存在则执行命令更新目标;如果依赖不存在,则会以依赖为目标,先生成依赖,依赖生成后,则执行命令 4 | 5 | ## 1. 6 | ``` 7 | %.imx: %.bin 8 | $(Q)$(MAKE) $(build)=arch/arm/imx-common $@ 9 | 10 | 11 | u-boot.imx: u-boot.bin 12 | $(Q) make $(build)=arch/arm/imx-common u-boot.imx 13 | 14 | u-boot.bin: u-boot-dtb.bin FORCE 15 | $(call if_changed,copy) 16 | 17 | u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE 18 | $(call if_changed,cat) 19 | 20 | u-boot-nodtb.bin: u-boot FORCE 21 | $(call if_changed,objcopy) 22 | $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) 23 | $(BOARD_SIZE_CHECK) 24 | 25 | dts/dt.dtb: checkdtc u-boot 26 | $(Q)$(MAKE) $(build)=dts dtbs 27 | 28 | PHONY += dtbs 29 | dtbs: dts/dt.dtb 30 | @: 31 | 32 | u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE 33 | $(call if_changed,u-boot__) 34 | ifeq ($(CONFIG_KALLSYMS),y) 35 | $(call cmd,smap) 36 | $(call cmd,u-boot__) common/system_map.o 37 | endif 38 | 39 | ``` 40 | 41 | ### $(u-boot-init) 42 | ``` 43 | u-boot-init := $(head-y) 44 | head-y := arch/arm/cpu/$(CPU)/start.o 45 | CONFIG_SYS_CPU="armv7" 46 | ``` 47 | 48 | ### $(u-boot-main) 49 | ``` 50 | u-boot-main := $(libs-y) 51 | libs-y += lib/ 52 | libs-y += fs/ 53 | libs-y += net/ 54 | libs-y += disk/ 55 | libs-y += drivers/ 56 | libs-y += drivers/dma/ 57 | libs-y += drivers/gpio/ 58 | libs-y += drivers/i2c/ 59 | libs-y += drivers/mmc/ 60 | libs-y += drivers/mtd/ 61 | libs-y += drivers/mtd/onenand/ 62 | libs-y += drivers/mtd/spi/ 63 | libs-y += drivers/net/ 64 | libs-y += drivers/net/phy/ 65 | libs-y += drivers/pci/ 66 | libs-y += drivers/power/ \ 67 | drivers/power/domain/ \ 68 | drivers/power/fuel_gauge/ \ 69 | drivers/power/mfd/ \ 70 | drivers/power/pmic/ \ 71 | drivers/power/battery/ \ 72 | drivers/power/regulator/ 73 | libs-y += drivers/spi/ 74 | libs-y += drivers/serial/ 75 | libs-y += drivers/usb/dwc3/ 76 | libs-y += drivers/usb/common/ 77 | libs-y += drivers/usb/emul/ 78 | libs-y += drivers/usb/eth/ 79 | libs-y += drivers/usb/gadget/ 80 | libs-y += drivers/usb/gadget/udc/ 81 | libs-y += drivers/usb/host/ 82 | libs-y += drivers/usb/musb/ 83 | libs-y += drivers/usb/musb-new/ 84 | libs-y += drivers/usb/phy/ 85 | libs-y += drivers/usb/ulpi/ 86 | libs-y += cmd/ 87 | libs-y += common/ 88 | libs-y += test/ 89 | libs-y += test/dm/ 90 | 91 | libs-y += board/freescale/mx6qsensorgw 92 | 93 | libs-y := $(sort $(libs-y)) 94 | libs-y := $(patsubst %/, %/built-in.o, $(libs-y)) 95 | xxx/xxx/built-in.o 96 | ``` 97 | 98 | ### u-boot.lds 99 | > ARCH := arm; CPU := armv7; BOARD := mx6qsensorgw; VENDOR := freescale; SOC := mx6 100 | 101 | ``` 102 | u-boot.lds: $(LDSCRIPT) prepare FORCE 103 | $(call if_changed_dep,cpp_lds) 104 | 105 | # 根据.config的配置,以及Makefile文件可以得出 106 | LDSCRIPT := ./arch/arm/cpu/u-boot.lds 107 | 108 | 109 | prepare: prepare0 110 | 111 | 112 | prepare0: archprepare FORCE 113 | $(Q)$(MAKE) $(build)=. 114 | 115 | archprepare: prepare1 scripts_basic 116 | 117 | prepare1: prepare2 $(version_h) $(timestamp_h) include/config/auto.conf 118 | 119 | prepare2: prepare3 outputmakefile 120 | 121 | prepare3: include/config/uboot.release 122 | 123 | outputmakefile: 124 | ifneq ($(KBUILD_SRC),) 125 | $(Q)ln -fsn $(srctree) source 126 | $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ 127 | $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) 128 | endif 129 | 130 | 131 | $(version_h): include/config/uboot.release FORCE 132 | $(call filechk,version.h) 133 | 134 | $(timestamp_h): $(srctree)/Makefile FORCE 135 | $(call filechk,timestamp.h) 136 | 137 | include/config/uboot.release: include/config/auto.conf FORCE 138 | $(call filechk,uboot.release) 139 | 140 | include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd 141 | 142 | 143 | ``` 144 | 145 | ![u-boot.imx目标关系依赖图](./images/u-boot_makefile.JPG) 146 | 147 | ----- 148 | 149 | -------------------------------------------------------------------------------- /第4章-U-boot驱动模型之一基本内容.md: -------------------------------------------------------------------------------- 1 | ## **第4章-U-boot驱动模型之一基本内容** 2 | ### **1. 基本概念** 3 | #### **1.1 全局数据global_data** 4 | 某些情况下,u-boot在某些只读存储器上运行,如ROM,NorFlash等;在其重定位到RAM之前,无法写入数据或者通过全局变量传递数据,而global_data(也称GD)则可以解决这个问题 5 | > 简单来说,u-boot把GD放在RAM区,使用它来存储全局数据, 以解决上述场景中无法使用全局变量的问题 6 | 7 | 8 | 9 | 10 | 11 | **GD数据结构** 12 | ``` 13 | // include/asm-generic/global_data.h 14 | typedef struct global_data { 15 | bd_t *bd; // 保存开发板的相关参数 16 | unsigned long env_addr; // 环境变量地址 17 | unsigned long ram_top; // RAM空间的顶端地址 18 | unsigned long relocaddr; // u-boot重定位后的地址 19 | phys_size_t ram_size; // 物理ram的size 20 | unsigned long irq_sp; // 中断的栈地址 21 | unsigned long start_addr_sp; // stack地址 22 | unsigned long reloc_off; // uboot的relocation的偏移 23 | struct global_data *new_gd; // 重定位后的GD结构体 24 | const void *fdt_blob; // dtb地址 25 | void *new_fdt; // 重定位后dtb地址 26 | unsigned long fdt_size; // dtb的长度 27 | struct udevice *cur_serial_dev; // 当前使用串口设备 28 | ...... 29 | } gd_t; 30 | 31 | ``` 32 | 33 | **初始化GD** 34 | ``` 35 | // crt0.S 36 | // 设置C运行时环境 37 | ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) // 设置栈顶SP指针,只是预设,并不是最终的栈顶地址 38 | bic sp, sp, #7 // 8byte对齐 39 | mov r0, sp // SP放入r0 40 | bl board_init_f_alloc_reserve // 参数为r0,返回后,r0中存放的是GD的地址 41 | mov sp, r0 // r0放入sp中 42 | mov r9, r0 // r0放入r9中,即r0和r9存放的都是GD的地址 43 | bl board_init_f_init_reserve // 对GD初始化,r0为参数 44 | 45 | // 给GD分配空间,传入的是r0,即栈顶地址 46 | ulong board_init_f_alloc_reserve(ulong top) 47 | { 48 | // 自顶向下分配CONFIG_SYS_MALLOC_F_LEN大小内存, 用于在relocation前用于给malloc函数提供内存池(给堆用的) 49 | top -= CONFIG_SYS_MALLOC_F_LEN; 50 | 51 | // 继续向下分配sizeof(struct global_data)大小的内存给GD使用,向下16byte对齐 52 | top = rounddown(top-sizeof(struct global_data), 16); 53 | 54 | // 返回GD地址 55 | return top; 56 | } 57 | 58 | // 初始化GD分配的空间, 即清空global_data区域 59 | // 传入的参数为GD的基地址 60 | void board_init_f_init_reserve(ulong base) 61 | { 62 | // 清零 63 | memset(gd_ptr, '\0', sizeof(*gd)); 64 | 65 | // 获取了early malloc的内存池的地址(给堆使用) 66 | base += roundup(sizeof(struct global_data), 16); 67 | 68 | // 写入到gd->malloc_base中 69 | gd->malloc_base = base; 70 | 71 | // 获取early malloc的内存池的末尾地址 72 | base += CONFIG_SYS_MALLOC_F_LEN; 73 | } 74 | 75 | ``` 76 | 77 | **使用GD** 78 | 根据上面可得GD的基地址存放在r9中,需要GD的时候,直接从r9寄存器中取的其地址即可 79 | ``` 80 | // arch/arm/include/asm/global_data.h 81 | #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9") 82 | 83 | // common/board_r.c 84 | DECLARE_GLOBAL_DATA_PTR 85 | 86 | static int initr_reloc(void) 87 | { 88 | // 直接使用gd变量即可 89 | gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT; 90 | } 91 | 92 | ``` 93 | 94 | #### **1.2 驱动模型引入** 95 | 在U-boot中引入驱动模型(driver model),为驱动的定义和范文接口提供统一的方法,提高驱动间的兼容性以及访问的标准性,u-boot中的驱动模型(DM)和kernel中的设备驱动模型类似,但是也有所区别 96 | > 通过宏定义CONFIG_DM使能驱动模型,对应的实际驱动设备则需要通过使能CONFIG_DM_SERIAL来使能;后面以serial驱动为例 97 | 98 | ##### **1.2.1 uclass/udevice/drivers三者之间的关联** 99 | **uclass**可以理解为具有相同属性的device对外操作的接口, 它与上层接口直接通讯,其驱动为**uclass_driver**,给上层提供接口 100 | **udevice**对具体设备的抽象,对应的驱动是**driver; driver**负责和硬件通讯,为**uclass**提供实际的操作集 101 | **udevice**如何和**uclass**绑定:**udevice**对应的**driver_id**和**uclass**对应的**uclass_driver_id**是否匹配 102 | **hardware**对应的**driver**绑定对应的**udevice,udevice**绑定**uclass**,**uclass**有其对应的**uclass_driver** 103 | > **uclass和udevice是动态生成的** 104 | 1. **udevice**在解析fdt中的设备的时候自动生成,然后**udevice**找到对应的**driver** 105 | 2. **driver**中保存了**uclass_id**, 根据它找到**uclass_driver_id** 106 | 3. 从**uclass**链表中查找对应的**uclass**是否已经生成,若没有生成,则动态生成 107 | 4. **重点是解析设备树,生成udevice, 并找到对应的driver** 108 | 109 | ![u-boot驱动模型](./images/driver_model.jpg) 110 | 111 | **1.2.2 全局数据GD中和DM相关部分** 112 | ``` 113 | typedef struct global_data { 114 | // dts中的根节点,第一个创建的udevice 115 | struct udevice *dm_root; 116 | 117 | // relocation之前的根设备 118 | struct udevice *dm_root_f; 119 | 120 | // uclass的链表, 挂的是有udevice的uclass 121 | struct list_head uclass_root; 122 | } gd_t; 123 | ``` 124 | -------------------------------------------------------------------------------- /第7章-U-boot移植.md: -------------------------------------------------------------------------------- 1 | ## **第7章-U-boot移植步骤** 2 | > 参考自**[链接](http://www.imx6rex.com/open-rex/software/yocto-uboot-how-to-add-support-for-a-custom-board/)** 3 | 4 | ### **1. 复制现有板子目录,创建新板子** 5 | ``` 6 | // 板级目录 7 | xingyanl@yocto:uboot$ cd board/freescale/ 8 | // 创建新板子 9 | xingyanl@yocto:uboot$ cp -R mx6sabresd mx6qsensorgw 10 | ``` 11 | ### **2. 修改相关文件** 12 | 13 | #### **2.1 修改Makefile文件** 14 | ``` 15 | // 修改mx6qsensorgw目录下得到Makefile文件 16 | xingyanl@yocto:mx6qsensorgw$ vi Makefile 17 | xingyanl@yocto:mx6qsensorgw$ cat Makefile 18 | obj-y := mx6qsensorgw.o // 更新内容 19 | ``` 20 | 21 | #### **2.2 重命名.c文件** 22 | ``` 23 | // 更改板级c文件 24 | xingyanl@yocto:mx6qsensorgw$ cp mx6sabresd.c mx6qsensorgw.c 25 | ``` 26 | 27 | #### **2.3 修改Kconfig文件** 28 | **a) 修改/board/freescale/mx6qsensorgw目录下的Kconfig** 29 | ``` 30 | xingyanl@yocto:mx6qsensorgw$ vi Kconfig 31 | xingyanl@yocto:mx6qsensorgw$ cat Kconfig 32 | if TARGET_MX6QSENSORGW // 新板子名字 33 | 34 | config SYS_BOARD 35 | default "mx6qsensorgw" // 新板子名字 36 | 37 | config SYS_VENDOR 38 | default "freescale" 39 | 40 | config SYS_CONFIG_NAME 41 | default "mx6qsensorgw" // 新板子名字 42 | 43 | endif 44 | 45 | ``` 46 | **b) 修改/arch/arm/cpu/armv7/mx6目录下的Kconfig** 47 | ``` 48 | // 添加如下内容 49 | xingyanl@yocto:mx6$ vi Kconfig 50 | ...... 51 | // 添加新板类型 52 | config TARGET_MX6QSENSORGW 53 | bool "Support mx6qsensorgw" 54 | select BOARD_LATE_INIT 55 | select SUPPORT_SPL 56 | select DM 57 | select DM_THERMAL 58 | select BOARD_EARLY_INIT_F 59 | ...... 60 | // source新板子文件Kconfig文件 61 | source "board/freescale/mx6qsensorgw/Kconfig" 62 | ...... 63 | ``` 64 | 65 | #### **2.4 更新include/configs/xxx.h文件** 66 | ``` 67 | xingyanl@yocto:uboot$ cd include/configs 68 | // copy mx6sabresd.h文件 69 | xingyanl@yocto:configs$ cp mx6sabresd.h mx6qsensorgw.h 70 | 71 | // 修改#include "mx6sabre_common.h"为 #include "mx6qsensorgw_common.h" 72 | xingyanl@yocto:configs$ vi mx6qsensorgw.h 73 | #include "mx6qsensorgw_common.h" 74 | 75 | // copy mx6sabre_common.h文件 76 | xingyanl@yocto:configs$ cp mx6sabre_common.h mx6qsensorgw_common.h 77 | 78 | // 一些宏定义 79 | CONFIG_LOADADDR // ZImage内核会load到这个地址引导 80 | CONFIG_SYS_MALLOC_LEN // Heap内存大小 81 | CONFIG_STACKSIZE // stack的大小 82 | CONFIG_NR_DRAM_BANKS // DDR banks的数量 83 | PHYS_SDRAM_SIZE // DDR的大小,以MB为单位 84 | PHYS_SDRAM // DDR的物理地址 85 | fdt_file // 配置宏定义 86 | 87 | "#define CONFIG_DEFAULT_FDT_FILE .dtb" 88 | 89 | // 或者直接修改 90 | "fdt_file=.dtb" 91 | 92 | // Config文件对U-Boot很重要,它决定了u-boot.bin的大小,功能和性能 93 | ``` 94 | 95 | ### **3. 为新板创建配置文件** 96 | 97 | ``` 98 | xingyanl@yocto:uboot$ cd configs/ 99 | xingyanl@yocto:configs$ cp mx6qsabresd_defconfig mx6qsensorgw_defconfig 100 | xingyanl@yocto:configs$ vi mx6qsensorgw_defconfig 101 | // 替换TARGET 102 | # CONFIG_TARGET_MX6SABRESD=y // 删除 103 | CONFIG_TARGET_MX6QSENSORGW=y // 添加 104 | 105 | // 更新cfg文件目录,和内存相关配置文件 106 | # CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6sabresd/mx6q_4x_mt41j128.cfg,MX6Q" 107 | CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6qsensorgw/mx6q_4x_mt41j128.cfg,MX6Q" 108 | 109 | // 更新设备树文件 110 | #CONFIG_DEFAULT_DEVICE_TREE="imx6q-sabresd" 111 | CONFIG_DEFAULT_DEVICE_TREE="imx6q-sensorgw" 112 | ``` 113 | 114 | ### **4. 为新板子指定设备树文件** 115 | ``` 116 | // 添加设备树文件 117 | xingyanl@yocto:uboot$ cd arch/arm/dts/ 118 | xingyanl@yocto:dts$ cp imx6q-sabresd.dts imx6q-sensorgw.dts 119 | ``` 120 | 121 | ### **5. 创建u-boot的build脚本** 122 | **5.1 创建u-boot编译脚本** 123 | ``` 124 | xingyanl@yocto:uboot$ vi u-boot.sh 125 | #!/bin/bash 126 | export ARCH=arm 127 | # 指定交叉编译器目录---重要 128 | export CROSS_COMPILE=/opt/fsl-imx-x11/4.1.15-2.0.0/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi- 129 | make distclean; 130 | make mx6qsensorgw_defconfig 131 | make 132 | ``` 133 | **5.2 编译u-boot** 134 | ``` 135 | // 添加权限 136 | xingyanl@yocto:uboot$ chmod a+x u-boot.sh 137 | 138 | // 编译 139 | xingyanl@yocto:uboot$ ./u-boot.sh 140 | ``` 141 | -------------------------------------------------------------------------------- /第9章-U-boot编译之一配置过程.md: -------------------------------------------------------------------------------- 1 | # 第9章-U-boot编译make配置过程 2 | [参考u-boot-2016.09 make配置过程分析](http://blog.csdn.net/guyongqiangx/article/details/52558087) 3 | ## 1. 概述 4 | u-boot从 **v2014.10版本** 后引入kBuild系统,使得其Makefile变的更加复杂;整个Makefile中,嵌套其他不同用途的Makefile,本文将进行简单的分析. 5 | 6 | ## 2. 执行配置命令, 生成.config文件 7 | > 配置,执行make XXX_defconfig进行各项配置,生成.config文件 8 | 9 | ```make配置 10 | // 执行make,生成.config文件 11 | asb@IoT:uboot$ make mx6qsensorgw_defconfig V=1 12 | ``` 13 | 14 | #### 2.1 编译生成fixdep工具 15 | 16 | a. %@表示所有的目标依赖的值 17 | > 本例子中是mx6qsensorgw_defconfig 18 | 19 | ```分析配置文件: 20 | %config: scripts_basic outputmakefile FORCE 21 | $(Q)$(MAKE) $(build)=scripts/kconfig $@ 22 | ``` 23 | 24 | b. 经过上步转换得出: 25 | 26 | ``` 27 | mx6qsensorgw_defconfig: scripts_basic outputmakefile FORCE 28 | $(Q)$(MAKE) $(build)=scripts/kconfig mx6qsensorgw_defconfig 29 | 30 | // 通过查找可得到build定义在文件中 31 | scripts/Kbuild.include: ; 32 | build := -f $(srctree)/scripts/Makefile.build obj 33 | 34 | # 即: 35 | mx6qsensorgw_defconfig: scripts_basic outputmakefile FORCE 36 | $(Q) make -f ./scripts/Makefile.build obj=scripts/kconfig mx6qsensorgw_defconfig 37 | ``` 38 | 39 | c. 得出三个依赖: 40 | 41 | ``` 42 | // scripts_basic依赖 43 | # Basic helpers built in scripts/ 44 | PHONY += scripts_basic 45 | scripts_basic: 46 | $(Q) make -f $(srctree)/scripts/Makefile.build obj=scripts/basic # make没找到哪里来的,srctree为(./)当前目录 47 | $(Q) rm -f .tmp_quiet_recordmcount 48 | 49 | 50 | // outputmakefile依赖 51 | PHONY += outputmakefile 52 | # outputmakefile generates a Makefile in the output directory, if using a 53 | # separate output directory. This allows convenient use of make in the 54 | # output directory. 55 | # 如果执行make mx6qsensorgw_defconfig O=out,则所有生成目标都会放到out目录,此时会通过outputmakefile导出一个makefile到out目录进行编译 56 | # 因在当前目录编译,则$(KBUILD_SRC)为空,不需要导出makefile文件,outputmakefile为空 57 | outputmakefile: 58 | ifneq ($(KBUILD_SRC),) 59 | $(Q)ln -fsn $(srctree) source 60 | $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ 61 | $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) 62 | endif 63 | 64 | 65 | // FORCE依赖,目前为空 66 | # 若一个目标添加FORCE依赖,每次编译都会去先去执行FORCE(实际上什么都不做),然后运行命令更新目标,从而确保目标每次都会被更新 67 | 68 | ``` 69 | 70 | d. mx6qsensorgw_defconfig的三个依赖scripts_basic outputmakefile FORCE,只有scripts_basic需要执行命令 71 | ``` 72 | scripts_basic: 73 | $(Q) make -f ./scripts/Makefile.build obj=scripts/basic # 执行Makefile.build文件,且文件中的obj替换为scripts/basic 74 | $(Q) rm -f .tmp_quiet_recordmcount 75 | 76 | ``` 77 | 78 | e. ./scripts/Makefile.build文件 79 | ``` 80 | # 赋值src 81 | src := $(patsubst $(prefix)/%,%,$(obj)) # 即src=scripts/basic 82 | 83 | # 搜寻子目录的下的Makefile,并包含进来 84 | kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) 85 | kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) 86 | include $(kbuild-file) 87 | 88 | # 展开后即 89 | include ./scripts/basic/Makefile # 定义编译在主机上执行的工具fixdep,它用于更新每个生成目标的依赖文件*.cmd 90 | ``` 91 | 92 | f. 编译生成fix总结为 93 | ``` 94 | make -f ./scripts/Makefile.build obj=scripts/basic 95 | cc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c 96 | ``` 97 | 98 | #### 2.2 编译生成conf工具 99 | 100 | a. 依赖scripts_basic执行结束以后,继续执行顶层目录的命令 101 | ``` 102 | make -f ./scripts/Makefile.build obj=scripts/kconfig mx6qsensorgw_defconfig 103 | 104 | ``` 105 | 106 | b. ./scripts/Makefile.build文件 107 | ``` 108 | # 赋值src 109 | src := $(patsubst $(prefix)/%,%,$(obj)) # 即src=scripts/kconfig 110 | 111 | # 搜寻子目录的下的Makefile,并包含进来 112 | kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) 113 | kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) 114 | include $(kbuild-file) 115 | 116 | # 展开后即 117 | include ./scripts/kconfig/Makefile # 定义编译在主机上执行的工具fixdep,它用于更新每个生成目标的依赖文件*.cmd 118 | ``` 119 | 120 | c. ./scripts/kconfig/Makefile文件 121 | ``` 122 | # 匹配到如下规则 123 | %_defconfig: $(obj)/conf 124 | $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) 125 | 126 | # 展开为 127 | mx6qsensorgw_defconfig: scripts/kconfig/conf 128 | $(Q)scripts/kconfig/conf --defconfig=arch/../configs/mx6qsensorgw_defconfig Kconfig 129 | ``` 130 | 131 | d. 依赖文件scripts/kconfig/conf文件 132 | ``` 133 | # 生成conf文件 134 | cc -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o 135 | ``` 136 | 137 | e. 最后执行最后的目标命令,生成.conf文件 138 | ``` 139 | scripts/kconfig/conf --defconfig=arch/../configs/mx6qsensorgw_defconfig Kconfig 140 | # 141 | # configuration written to .config 142 | # 143 | ``` 144 | **conf工具从根目录开始树状读取默认的Kconfig文件,分析其配置,并保存在内存中;然后读取指定的xxx_defconfig文件,更新得到最终的符号表,并输出到.config文件中** 145 | -------------------------------------------------------------------------------- /第4章-U-boot驱动模型之四实例.md: -------------------------------------------------------------------------------- 1 | # 实例 2 | 3 | ------ 4 | ## 1. serial驱动 5 | ### 1.1 定义uclass_driver 6 | ``` 7 | UCLASS_DRIVER(serial) = { 8 | .id = UCLASS_SERIAL, //设置对应的uclass id,并且存放在.u_boot_list_2_uclass_2_serial段中 9 | .name = "serial", 10 | .flags = DM_UC_FLAG_SEQ_ALIAS, 11 | .post_probe = serial_post_probe, 12 | .pre_remove = serial_pre_remove, 13 | .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), 14 | }; 15 | ``` 16 | 17 | ### 1.2 dts文件中serial节点 18 | ``` 19 | serial@02020000 { 20 | compatible = "fsl,imx6q-uart", "fsl,imx21-uart"; 21 | reg = <0x2020000 0x4000>; 22 | interrupts = <0x0 0x1a 0x4>; 23 | clocks = <0x3 0xa0 0x3 0xa1>; 24 | clock-names = "ipg", "per"; 25 | dmas = <0x8 0x19 0x4 0x0 0x8 0x1a 0x4 0x0>; 26 | dma-names = "rx", "tx"; 27 | status = "okay"; 28 | pinctrl-names = "default"; 29 | pinctrl-0 = <0xc>; 30 | }; 31 | 32 | ``` 33 | 34 | ### 1.3 定义设备驱动 35 | ``` 36 | U_BOOT_DRIVER(serial_s5p) = { 37 | .name = "serial_s5p", 38 | .id = UCLASS_SERIAL, // 这里的uclass_id和 uclass_driver中的一致 39 | .of_match = s5p_serial_ids, // 设备树种的compatible属性 40 | .ofdata_to_platdata = s5p_serial_ofdata_to_platdata, 41 | .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), 42 | .probe = s5p_serial_probe, 43 | .ops = &s5p_serial_ops, 44 | .flags = DM_FLAG_PRE_RELOC, 45 | }; 46 | 47 | static const struct udevice_id s5p_serial_ids[] = { 48 | { .compatible = "samsung,exynos4210-uart" }, //注意这里的compatible要和设备树中的一致 49 | { } 50 | }; 51 | 52 | ``` 53 | 54 | ### 1.4 udevice和对应uclass的创建 55 | **dm\_scan\_fdt** 56 | ``` 57 | fdtdec_get_chosen_node ------> fdtdec_get_chosen_prop 58 | 59 | chosen { // chosen节点也位于根节点下,该节点用来给内核传递参数(不代表实际硬件) 60 | // 从串口输出 61 | stdout-path = "/soc/aips-bus@02000000/spba-bus@02000000/serial@02020000"; 62 | }; 63 | 64 | 65 | ``` 66 | ### 1.5 udevice和对应uclass的绑定 67 | **核心解析函数lists\_bind\_fdt** 68 | 69 | ### 1.6 对应udevice的probe 70 | ``` 71 | int serial_init(void) 72 | { 73 | // console的serial的初始化 74 | serial_find_console_or_panic(); 75 | gd->flags |= GD_FLG_SERIAL_READY; 76 | } 77 | 78 | static void serial_find_console_or_panic(void) 79 | { 80 | 81 | if (CONFIG_IS_ENABLED(OF_CONTROL) && blob) { 82 | 83 | // 获取指定的serial的dts节点 84 | 85 | // 调用uclass_get_device_by_of_offset,通过dts节点的偏移从uclass的设备链表中获取udevice,并且进行probe。 86 | if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node, 87 | &dev)) { 88 | // 将udevice存储在gd->cur_serial_dev,后续uclass中可以直接通过gd->cur_serial_dev获取到对应的设备并且进行操作 89 | // 但是注意,这种并不是通用做法!!! 90 | gd->cur_serial_dev = dev; 91 | return; 92 | } 93 | } 94 | } 95 | ``` 96 | ### 1.7 uclass的接口调用 97 | ``` 98 | void serial_putc(char ch) 99 | { 100 | if (gd->cur_serial_dev) 101 | // 将console对应的serial的udevice作为参数传入 102 | _serial_putc(gd->cur_serial_dev, ch); 103 | } 104 | 105 | static void _serial_putc(struct udevice *dev, char ch) 106 | { 107 | // 获取设备对应的driver函数的ops操作集 108 | struct dm_serial_ops *ops = serial_get_ops(dev); 109 | int err; 110 | 111 | // 以udevice为参数,调用ops中对应的操作函数putc 112 | do { 113 | err = ops->putc(dev, ch); 114 | } while (err == -EAGAIN); 115 | } 116 | 117 | ``` 118 | 119 | 120 | ------ 121 | ## 2. gpio驱动 122 | ### 2.1 dm-gpio架构 123 | - gpio的驱动模型架构如图所示: 124 | ![gpio驱动模型架构](./images/gpio.jpg) 125 | 126 | #### 2.1.1 gpio core 127 | a) 为上层提供接口 128 | b) 从dts中获取GPIO属性 129 | c) 从gpio uclass的设备链表中获取对应udevice设备,并使用其操作集 130 | 131 | #### 2.1.2 gpio uclass 132 | a) 链接属于该uclass的所有udevice 133 | b) 为udevice的driver提供统一操作集接口 134 | 135 | #### 2.1.3 bank和udevice 136 | a) 在某些平台,把使用同一组寄存器的GPIO构成一个bank,但是不是所有的都有bank的概念,如果每个GPIO都有自己独立的寄存器,则可看成只有一个bank 137 | b) 一个bank对应一个udevice,用bank中的偏移表示具体GPIO号 138 | c) udevice的driver根据bank和offset来操作对应寄存器的bit 139 | 140 | ### 2.2 基本原理 141 | a) 一个bank对应一个udevice,udevice中私有数据中存放着该bank的信息,比如相应寄存器地址等等 142 | b) 上层通过调用gpio core的接口从dtsi获取到GPIO属性对应的gpio_desc描述符,用此描述符描述一个GPIO,它包括该GPIO所属的udevice、在bank内的偏移、以及标志位等 143 | c) 上层使用gpio_desc描述符来作为调用gpio core的操作接口的参数 144 | d) gpio core从gpio_desc描述符提取udevice,并调用其driver中对应的操作集,以bank内的偏移作为其参数(这样driver就能判断出是哪个GPIO了) 145 | e) driver中提取udevice的私有数据中的bank信息,并进行相应的操作 146 | f) 147 | 148 | 149 | ### 2.3 driver 150 | 151 | 152 | ### 2.4 uclass_driver 153 | 154 | 155 | 156 | ### 2.5 uclass 157 | 158 | 159 | 160 | 161 | ### 2.6 udevice 162 | 163 | 164 | 165 | ### 2.7 绑定流程 166 | driver为uclass提供操作集,其中保存了uclass_id 167 | 168 | 169 | 根据uclass_id查询uclass driver列表获得uclass 170 | udevice会放在两个地方,一个是uclass对应的udevice链表 171 | 一个是Device链表中 172 | 173 | driver中有compatibile字段,通过检查它和dts节点中的compatible字段是否相同,获取节点名称,获取driver,生成udevice,uclass,并进行绑定 174 | 175 | ``` 176 | // 以serial为例, driver 177 | U_BOOT_DRIVER(serial_s5p) = { 178 | .name = "serial_s5p", 179 | .id = UCLASS_SERIAL, 180 | .of_match = s5p_serial_ids, 181 | .ofdata_to_platdata = s5p_serial_ofdata_to_platdata, 182 | .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), 183 | .probe = s5p_serial_probe, 184 | .ops = &s5p_serial_ops, 185 | .flags = DM_FLAG_PRE_RELOC, 186 | }; 187 | 188 | // 以serial-uclass为例,uclass driver 189 | UCLASS_DRIVER(serial) = { 190 | .id = UCLASS_SERIAL, 191 | .name = "serial", 192 | .flags = DM_UC_FLAG_SEQ_ALIAS, 193 | .post_probe = serial_post_probe, 194 | .pre_remove = serial_pre_remove, 195 | .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), 196 | }; 197 | 198 | ``` 199 | 200 | if(!a): a是假的执行 201 | if(a): a是真的执行 202 | -------------------------------------------------------------------------------- /第1章-U-boot启动流程.md: -------------------------------------------------------------------------------- 1 | # 第1章:u-boot启动流程 2 | - 板子上电以后,首先执行的是ROM中的一段启动代码。启动代码**根据寄存器/外部管脚配置**,确定是进入下载模式,还是从某介质(Flash/EMMC/SD卡等存储设备)启动u-boot 3 | > ROM中的代码是固化的,无法修改 4 | 5 | **先分析架构,不要开始就进入细节** 6 | ------ 7 | ## 1 从上电到u-boot(EMMC为例) 8 | - 根据IMX6Q数据手册描述: 9 | - 首先启动代码会将EMMC前4K的数据copy到**片内RAM(SOC芯片上的)**. 10 | > 这4K数据中包含了Program Image数据,copy数据长度大小和IVT(镜像向量结构)的偏移量根据**启动设备类型决定**. 11 | 12 | ### 1.1 Program Image 13 | Program Image是freescale定义的一个镜像数据结构,它里面包括镜像向量表IVT,Boot Data,设备配置数据DCD和用户代码等信息,详见数据手册. 14 | 15 | ![IVT偏移和加载大小](./images/IVT.jpg) 16 | 17 | ### 1.2 IVT概念 18 | | Header | entry | dcd | Boot Data | CSF| 19 | | :--------------- | :------------- | :------------- |:------------- | :------------- | 20 | | IVT头部,标识IVT和IVT长度 | 第一条指令的入口地址,也就是U-boot的入口地址 | DCD数据地址,在BOOT Data后面 | Boot Data地址,在IVT后面| CSF地址| 21 | 22 | - 其中Boot Data里面包含: 23 | 24 | | start | length | plugin flag | 25 | | :------------- | :------------- |:------------- | 26 | | 启动数据加载到内存的地址 | 启动数据加载到内存的长度 | 标志位 | 27 | 28 | ### 1.3 Boot Device Memory结构图 29 | 30 | ![Boot Device Memory](./images/memory_mapping.jpg) 31 | 32 | ### 1.4 启动代码拷贝 33 | - 启动代码根据Boot Data的指示,把EMMC中前**Boot Data->length**字节copy到**Boot Data->start地址**的内存位置处. 34 | > 如图所示:其中左边是**启动设备中的数据**,右边是**片上内存中的数据** 35 | 36 | - IVT->entry指向**片上内存中Application的位置(蓝线)**,Application也就是U-boot的镜像,最后跳转到IVT->entry指定的地址,进入U-boot. 37 | 38 | ![Boot Device Memory](./images/memory_mapping.jpg) 39 | 40 | ------ 41 | ## 2 u-boot启动概览 42 | ![初始化流程](./images/uboot.jpg) 43 | 44 | ### 2.1 arch级初始化 45 | - 主要是设置SVC模式,关中断、设置寄存器等芯片级别的初始化 46 | ``` 47 | _start——————>reset——————>设置SVC模式, 关闭中断 48 | ………………………………| 49 | ………………………………————>cpu_init_cp15—————>关闭MMU,TLB 50 | ………………………………| 51 | ………………………………————>cpu_init_crit—————>lowlevel_init—————>关键寄存器的配置和初始化 52 | ………………………………| 53 | ………………………………————>_main—————>进入板级初始化 54 | ``` 55 | 56 | ### 2.2 板级初始化 57 | - 主要是堆栈,外设等初始化 58 | ``` 59 | _main—————>board_init_f_alloc_reserve ——————>堆栈、GD、early malloc空间的分配 60 | …………………………| 61 | …………………………——————>board_init_f ———————>uboot relocate前的板级初始化以及relocate的区域规划 62 | …………………………| 63 | …………………………——————>relocate_code、relocate_vectors——————>进行uboot和异常中断向量表的重定向 64 | …………………………| 65 | …………………………——————>旧堆栈的清空 66 | …………………………| 67 | …………………………——————>board_init_r —————————>uboot relocate后的板级初始化 68 | …………………………| 69 | ……………………………………………………——————>run_main_loop ————————>进入命令行状态,等待终端输入命令以及对命令进行处理 70 | 71 | ``` 72 | 73 | ### 2.3 u-boot.lds链接文件 74 | - u-boot入口文件:/arch/arm/cpu/u-boot.lds; 主要**指示u-boot的入口地址以及ELF各个段的分布** 75 | > b用于**不返回的跳转**,比如跳到xxx标号处,b xxx;bl用于**子程序跳转,要返回地址,返回地址存于LR中;**当发生bl跳转前,会在寄存器R14(即LR)中保存当前PC-4,即bl跳转指令的下一条指令的地址。所以在返回时只要**mov lr, pc** 76 | 77 | ``` 78 | // 入口地址,定义在vectors.S文件中 79 | ENTRY(_start) 80 | 81 | SECTIONS 82 | { 83 | . = 0x00000000; 84 | // 4字节对齐的 85 | . = ALIGN(4); 86 | .text : 87 | { 88 | *(.__image_copy_start) 89 | // 所有可重定位目标文件的vectors段,其中定义了全局的_start入口 90 | *(.vectors) 91 | // start.o文件的.text段,对应的汇编文件是start.S 92 | CPUDIR/start.o (.text*) 93 | // 所有可重定位目标文件的.text段 94 | *(.text*) 95 | } 96 | 97 | // 各个段的描述 98 | ....... 99 | } 100 | ``` 101 | ### 2.4 /arch/arm/lib/vectors.S 102 | ``` 103 | // vectors.S文件定义了全局入口_start 104 | _start: 105 | // 跳转到start.S中定义的reset,正式开始执行u-boot 106 | b reset 107 | // 以下是Exception Handlers Vectors,是芯片规定的,可以查看芯片手册说明 108 | ldr pc, _undefined_instruction 109 | ldr pc, _software_interrupt 110 | ldr pc, _prefetch_abort 111 | ldr pc, _data_abort 112 | ldr pc, _not_used 113 | ldr pc, _irq 114 | ldr pc, _fiq 115 | ``` 116 | 117 | ### 2.5 /arch/arm/cpu/armv7/start.S 118 | ``` 119 | // start.S文件 120 | reset: 121 | /* Allow the board to save important registers */ 122 | b save_boot_params 123 | ..... 124 | // 设置SVC模式,关中断等 125 | mrs r0, cpsr 126 | and r1, r0, #0x1f @ mask mode bits 127 | teq r1, #0x1a @ test for HYP mode 128 | bicne r0, r0, #0x1f @ clear all mode bits 129 | orrne r0, r0, #0x13 @ set SVC mode 130 | orr r0, r0, #0xc0 @ disable FIQ and IRQ 131 | msr cpsr,r0 132 | 133 | // 初始化协处理器CP15,禁用MMU和TLB 134 | bl cpu_init_cp15 135 | // 初始化CPU相关的重要寄存器 136 | bl cpu_init_crit 137 | // crt0.S中的板级初始化_main执行 138 | bl _main <=================== 139 | ...... 140 | 141 | ENTRY(cpu_init_cp15) 142 | // 因为系统栈没有初始化,因此使用lr寄存器来保存返回地址 143 | mov r5, lr @ Store my Caller <=================== 144 | ...... 145 | mov pc, r5 @ back to my caller <=================== 146 | ENDPROC(cpu_init_cp15) 147 | 148 | ENTRY(cpu_init_crit) 149 | // 调用rch/arm/cpu/lowlevel_init.S中通用的lowlevel_init 150 | // 通常是由板级代码自己实现 151 | b lowlevel_init @ go setup pll,mux,memory 152 | ENDPROC(cpu_init_crit) 153 | ``` 154 | 155 | ### 2.6 arch/arm/cpu/lowlevel_init.S 156 | ``` 157 | // 设置临时堆栈等 158 | .... 159 | // 保存返回地址,之后重新回到start.S文件 160 | push {ip, lr} 161 | // 跳转到arch\arm\cpu\armV7\mx6\soc.c中做些操作 162 | bl s_init 163 | // 重新回到start.S文件继续执行_main 164 | pop {ip, pc} 165 | ``` 166 | 167 | ### 2.7 arch\arm\lib\crt0.S 168 | ``` 169 | // 入口及重要函数 170 | ENTRY(_main) 171 | // 设置栈顶指针 172 | ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) 173 | 174 | // 函数位于common/init/board_init.c 175 | bl board_init_f_alloc_reserve 176 | bl board_init_f_init_reserve 177 | 178 | // 板前期初始化,common\board_f.c 179 | bl board_init_f 180 | 181 | // relocate.S中重定位区域规划,代码重定位 182 | ...... 183 | b relocate_code 184 | // 板后期初始化,common\board_r.c 185 | ldr pc, =board_init_r // 进入主函数 186 | ENDPROC(_main) 187 | ``` 188 | 189 | ### 2.8 arch\arm\lib\relocate.S 190 | ``` 191 | ENTRY(relocate_code) 192 | // 对__image_copy_start等section进行重定位 193 | .... 194 | ENDPROC(relocate_code) 195 | ``` 196 | 197 | 至此,大致流程分析完毕** 198 | 199 | ------- 200 | ## 3. 板前期初始化 201 | ### 3.1 202 | ### 3.2 203 | ### 3.3 204 | ### 3.4 205 | ### 3.5 206 | ### 3.6 207 | 208 | ------ 209 | ## 4. 板后期初始化 210 | ### 4.1 211 | ### 4.2 212 | ### 4.3 213 | ### 4.4 214 | ### 4.5 215 | ### 4.6 216 | 217 | ------ 218 | ## 5. u-boot命令解析 219 | ### 5.1 220 | ### 5.2 221 | ### 5.3 222 | ### 5.4 223 | ### 5.5 224 | ### 5.6 225 | -------------------------------------------------------------------------------- /第2章-U-boot设备树.md: -------------------------------------------------------------------------------- 1 | # 设备树 2 | 3 | ------ 4 | ## 1. 基本概念 5 | - **FDT, flatted device tree,**扁平设备树, 简单来说,就是存储部分设备信息结构的文件; U-boot最终会将其编译成dtb文件, 在使用通常中通过解析该dtb文件来获取板级设备的信息 6 | > U-boot的dtb和kernel中的dtb是一致的,有关fdt的详细介绍,参考doc/README.fdt-control 7 | 8 | ### 1.1 dtb结构 9 | - Device tree源文件结构分为**header、fill\_area、dt\_struct和dt_string**四个区域 10 | > header为头信息;fill\_area为填充区域,填充数字0;dt\_struct存储节点数值及名称相关信息;dt\_string存储属性名 11 | 12 | ![dtb结构图](./images/dtb_struct.png) 13 | 14 | ### 1.2 dts文件 15 | - dtb头部存放的是fdt_header的结构体信息 16 | - 然后是填充区,填充大小为off\_dt\_struct-sizeof(struct fdt\_header)填充值为0 17 | - 接着就是struct fdt_property结构体的相关信息 18 | - 最后是dt_string部分 19 | ``` 20 | // 反汇编设备树dtb到dts文件 21 | asb@ubuntu:Iot$ dtc -I dtb -O dts xxx.dtb > xxx.dts 22 | 23 | // 二进制查看,根据前4个字节magic来验证dtb,注意大端和小端的区别 24 | asb@ubuntu:Iot$ hexdump -C imx6q-sabresd.dtb | more 25 | 00000000 d0 0d fe ed 00 00 b5 d4 00 00 00 38 00 00 a9 88 |...........8....| 26 | 00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............| 27 | 00000020 00 00 0c 4c 00 00 a9 50 00 00 00 00 00 00 00 00 |...L...P........| 28 | 00000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................| 29 | 00000040 00 00 00 03 00 00 00 04 00 00 00 00 00 00 00 01 |................| 30 | ``` 31 | 32 | - Device tree文件结构图 33 | ![Device tree文件结构图](./images/device_file.png) 34 | 35 | - dt_struct在device tree中的结构如下 36 | ![dt_struct结构图](./images/dt_struct.png) 37 | ![dt_struct结构图2](./images/dt_struct_2.jpg) 38 | 39 | ### 1.3 dtb在U-boot中的位置 40 | - dtb能够以两种形式编译到U-boot的镜像中 41 | 1. dtb和u-boot的bin文件分离(imx6q中使用的这种方式,在.config文件中可以查看到) 42 | > 通过CONFIG\_OF\_SEPARATE宏定义使能,dtb最后会追加到u-boot的bin文件的最后面,通过u-boot的结束地址符号_end符号来获取dtb的地址 43 | 44 | 2. dtb集成到u-boot的bin文件内部 45 | > 通过CONFIG\_OF\_EMBED宏定义使能,dtb会位于u-boot的.dtb.init.rodata段中,通过\__dtb\_dt_begin符号来获取dtb 46 | 47 | 3. 获取dts文件的地址gd->fdt_blob 48 | 49 | ``` 50 | // 宏用来表示是否把dtb文件放在uboot.bin的文件中 51 | CONFIG_OF_EMBED 52 | 53 | // 单独编译dtb文件 54 | CONFIG_OF_SEPARATE,编译出来的dtb放在uboot.bin的最后面,就是dtb追加到uboot的bin文件后面时,通过_end符号来获取dtb地址 55 | 56 | gd->fdt_blob = (ulong *)&_end; 57 | 58 | // 可以通过fdtcontroladdr环境变量来指定fdt的地址 59 | gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16, 60 | (uintptr_t)gd->fdt_blob); 61 | ``` 62 | 63 | ------ 64 | ## 2. U-boot中支持设备树原理 65 | - 通过配置宏定义CONFIG\_OF\_CONTROL来使能FDT 66 | 67 | ### 2.1 添加dtb文件的流程 68 | TBD 69 | 70 | ------ 71 | ## 3. U-boot中获取设备树 72 | ### 3.1 简介 73 | - 在初始化过程中: 74 | > 总控流程在common/board_f.c 文件中 75 | 76 | ![验证设备树流程](./images/fdtdec_setup.png) 77 | 1. 获取dtb,并且验证其合法性 78 | ``` 79 | // 获取dtb地址,验证其合法性: lib/fdtdec.c----fdtdec_setup函数 80 | int fdtdec_setup(void) 81 | { 82 | gd->fdt_blob = __dtb_dt_begin; // dtb集成到u-boot中,通过__dtb_dt_begin符号表获取dtb地址 83 | gd->fdt_blob = (ulong *)&_end; // dtb集成到u-boot中,通过_end符号获取dtb地址 84 | gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16, 85 | (uintptr_t)gd->fdt_blob); // 通过环境变量fdtcontroladdr来指定dtb的地址。 86 | 87 | return fdtdec_prepare_fdt(); // 最终dtb的地址存储在gd->fdt_blob中,在fdtdec_prepare_fdt中检查fdt的合法性 88 | } 89 | 90 | int fdtdec_prepare_fdt(void) 91 | { 92 | // 判断dtb是否存在,是否四个字节对齐,dtb的magic是否正确 93 | if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) || 94 | fdt_check_header(gd->fdt_blob)) { 95 | puts("No valid device tree binary found - please append one to U-Boot binary, use u-boot-dtb.bin or define CONFIG_OF_EMBED. For sandbox, use -d \n"); 96 | return -1; 97 | } 98 | return 0; 99 | } 100 | 101 | ``` 102 | 103 | 2. 为dtb分配存储空间,并进行relocate 104 | ``` 105 | // 为dtb分配新的内存地址空间: reserve_fdt 106 | static int reserve_fdt(void) 107 | { 108 | // 若dtb集成在u-boot中,重定位u-boot过程会把dtb一起relocate, 否则需要单独的relocate 109 | if (gd->fdt_blob) { 110 | gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32); // 获取dtb的size 111 | gd->start_addr_sp -= gd->fdt_size; 112 | gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size); // 为dtb分配新的内存空间 113 | debug("Reser ving %lu Bytes for FDT at: %08lx\n", 114 | gd->fdt_size, gd->start_addr_sp); 115 | } 116 | return 0; 117 | } 118 | ``` 119 | 120 | 3. relocate重定位以后,重新获取dtb的地址 121 | 122 | ``` 123 | // 重定位dtb: reloc_fdt 124 | static int reloc_fdt(void) 125 | { 126 | if (gd->flags & GD_FLG_SKIP_RELOC) // 检查GD_FLG_SKIP_RELOC标识 127 | return 0; 128 | if (gd->new_fdt) { 129 | memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size); // relocate dtb空间 130 | gd->fdt_blob = gd->new_fdt; // 切换gd->fdt_blob到dtb新的地址空间 131 | } 132 | return 0; 133 | } 134 | 135 | ``` 136 | -------- 137 | 138 | ## 4. U-boot中解析设备树 139 | - 经过以上步骤,gd->fdt_blob已经设置为dtb的地址,fdt提供的接口都是以它作为参数的 140 | 141 | ### 4.1 dtb解析接口 142 | - 定义在lib/fdtdec.c文件中,节点变量node中存放的是偏移地址 143 | 144 | ``` 145 | // 获得dtb下某个节点的路径path的偏移,偏移就代表这个节点 146 | int fdt_path_offset(const void *fdt, const char *path) 147 | eg:node = fdt_path_offset(gd->fdt_blob, “/aliases”); 148 | 149 | // 获得节点node的某个字符串属性值 150 | const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp) 151 | eg: mac = fdt_getprop(gd->fdt_blob, node, “mac-address”, &len); 152 | 153 | // 获得节点node的某个整形数组属性值 154 | int fdtdec_get_int_array(const void *blob, int node, const char *prop_name, u32 *array, int count) 155 | eg: ret = fdtdec_get_int_array(blob, node, “interrupts”, cell, ARRAY_SIZE(cell)); 156 | 157 | // 获得节点node的地址属性值 158 | fdt_addr_t fdtdec_get_addr(const void *blob, int node, const char *prop_name) 159 | eg:fdtdec_get_addr(blob, node, “reg”); 160 | 161 | // 获得config节点下的整形属性、bool属性、字符串等等 162 | fdtdec_get_config_int、fdtdec_get_config_bool、fdtdec_get_config_string 163 | 164 | // 获得chosen下的name节点的偏移 165 | int fdtdec_get_chosen_node(const void *blob, const char *name) 166 | 167 | // 获得chosen下name属性的值 168 | const char *fdtdec_get_chosen_prop(const void *blob, const char *name) 169 | ``` 170 | 171 | - 定义在lib/fdtdec_common.c文件中 172 | 173 | ``` 174 | // 获得节点node的某个整形属性值 175 | int fdtdec_get_int(const void *blob, int node, const char *prop_name, int default_val) 176 | eg: bus->udelay = fdtdec_get_int(blob, node, “i2c-gpio,delay-us”, DEFAULT_UDELAY); 177 | 178 | // 获得节点node的某个无符号整形属性值 179 | fdtdec_get_uint 180 | ``` 181 | -------------------------------------------------------------------------------- /第6章-U-boot启动内核之一uImage.md: -------------------------------------------------------------------------------- 1 | ## **第6章-U-boot启动内核** 2 | > 整理自[uboot启动kernel篇(一)——Legacy-uImage & FIT-uImage](http://blog.csdn.net/ooonebook/article/details/53495002) 3 | 4 | ### **1.uImage** 5 | > 内核编译后会生成Image或者压缩的zImage,但这两种格式缺少足够信息供u-boot进行load、jump或验证操作;因此u-boot提供mkimage工具,用以生成u-boot可以识别的格式,称为uImage 6 | 7 | #### **1.1 Legacy-uImage类型** 8 | **在kernel镜像的基础上,附加64Byte的信息在头部供u-boot使用** 9 | > **优点是**实现简单,长度较小; 10 | **缺点是**使用麻烦,需要在启动kernel命令中额外添加fdt、ramdisk等信息 11 | 12 | #### **1.2 FIT-uImage类型** 13 | **类似FDT模式,把kernel,fdt,ramdisk等镜像打包到一个image file文件中,外加一些额外信息;u-boot获得这个image以后,就可以得到其中各镜像的具体信息和内容** 14 | > **优点是**使用方便,兼容性好 15 | **缺点是**长度较大,需要额外信息较长 16 | 17 | ### **2.Legacy-uImage** 18 | #### **2.1 配置宏,使能** 19 | ``` 20 | // 自动生成autoconf.mk时会自动使能,不需要额外配置 21 | CONFIG_IMAGE_FORMAT_LEGACY=y 22 | ``` 23 | 24 | #### **2.2 制作和使用** 25 | **a) 制作** 26 | ``` 27 | // mkimage工具目录 28 | xingyanl@yocto:~$ ls -lrt uboot/tools/mkimage 29 | -rwxrwxr-x 1 xingyanl xingyanl 164650 Dec 26 15:14 uboot/tools/mkimage 30 | xingyanl@yocto:~$ 31 | 32 | xingyanl@yocto:~$ mkimage -A arm -O linux -C none -T kernel -a 0x20008000 -e 0x20008040 -n Linux_Image -d zImage uImage 33 | 34 | 参数含义: 35 | Usage: mkimage -l image 36 | -l ==> list image header information 37 | mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image 38 | -A ==> set architecture to 'arch' // 体系 39 | -O ==> set operating system to 'os' // 操作系统 40 | -T ==> set image type to 'type' // 镜像类型 41 | -C ==> set compression type 'comp' // 压缩类型 42 | -a ==> set load address to 'addr' (hex) // 加载地址 43 | -e ==> set entry point to 'ep' (hex) // 入口地址 44 | -n ==> set image name to 'name' // 镜像名称,注意不能超过32B 45 | -d ==> use image data from 'datafile' // 输入文件 46 | -x ==> set XIP (execute in place) 47 | ``` 48 | 49 | **b) 使用** 50 | ``` 51 | // 使用bootm命令下载Legacy-uImage到指定位置 52 | bootm Legacy-uImage加载地址 ramdisk加载地址 dtb加载地址 53 | ``` 54 | 55 | #### **2.3 和zImage的区别** 56 | ``` 57 | // 可得uImage比zImage多了64Byte数据 58 | -rw-rw-rw- 1 hlos hlos 1560120 Dec 5 14:46 uImage 59 | -rwxrwxrwx 1 hlos hlos 1560056 Nov 30 15:42 zImage* 60 | 61 | // 通过od命令查看这64Byte的数据,并且加在了zImage的头部 62 | xingyanl@yocto:~$ od -tx1 -tc -Ax -N64 uImage 63 | 000000 27 05 19 56 5a f3 f7 8e 58 45 0d 3d 00 17 cd f8 64 | ' 005 031 V Z 363 367 216 X E \r = \0 027 315 370 65 | 000010 20 00 80 00 20 00 80 40 e2 4b 43 b6 05 02 02 00 66 | \0 200 \0 \0 200 @ 342 K C 266 005 002 002 \0 67 | 000020 4c 69 6e 75 78 5f 49 6d 61 67 65 00 00 00 00 00 68 | L i n u x _ I m a g e \0 \0 \0 \0 \0 69 | 000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 | \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 71 | ``` 72 | 73 | #### **2.4 格式头说明** 74 | 75 | ``` 76 | // u-boot中使用image_header表示legacy-uImage的头部 77 | typedef struct image_header { 78 | __be32 ih_magic; // 魔数头27 05 19 56,用来校验是否是Legacy-uImage 79 | __be32 ih_hcrc; // 头部的CRC校验值 80 | __be32 ih_time; // 镜像创建的时间戳 81 | __be32 ih_size; // 镜像数据长度 82 | __be32 ih_load; // 加载地址 83 | __be32 ih_ep; // 入口地址 84 | __be32 ih_dcrc; // 镜像的CRC校验 85 | uint8_t ih_os; // 操作系统类型 86 | uint8_t ih_arch; // 体系 87 | uint8_t ih_type; // 镜像类型 88 | uint8_t ih_comp; // 压缩类型 89 | uint8_t ih_name[IH_NMLEN]; // 镜像名 90 | } image_header_t; 91 | #define IH_NMLEN 32 // 镜像名最大长度 92 | ``` 93 | 94 | ### **3. FIT-uImage** 95 | **FIT:**flattened image tree,类似于**FDT(flattened device tree)**的一种实现机制,它使用一定语法和格式把需要的镜像(如kernel,dtb等)组合到一起生成一个image文件 96 | > 主要使用四个组件: 97 | a) **its(image source file)文件:**类似于dts,负责描述image的信息,需要自行构造 98 | b) **itb文件: **最终得到的image文件,类似于dtb文件,u-boot直接对其识别和解析 99 | c) **mkimage:**负责dtc的角色,通过解析its生成itb 100 | d) **image data file:**实际使用到的镜像文件 101 | **mkimage将its及对应的image data file打包成一个itb,即uboot可识别的image file(FIT-uImage); 把文件下载到memory中,使用bootm命令就可以执行了** 102 | 103 | 104 | #### **3.1 配置宏,使能** 105 | ``` 106 | // 需要配置使能 107 | CONFIG_FIT=y 108 | ``` 109 | 110 | #### **3.2 制作** 111 | ##### **3.2.1 its文件制作** 112 | ``` 113 | // 制作its文件, 其中需要描述被打包的镜像, 主要是kernel镜像, dtb文件和ramdisk镜像 114 | // 示例 [u-boot FIT image介绍](http://www.wowotech.net/u-boot/fit_image_overview.html) 115 | /dts-v1/; 116 | / { 117 | description = "U-Boot uImage source file for X project"; 118 | #address-cells = <1>; 119 | images { 120 | kernel@tiny210 { 121 | description = "Unify(TODO) Linux kernel for project-x"; 122 | data = /incbin/("/home/hlos/code/xys/temp/project-x/build/out/linux/arch/arm/boot/zImage"); 123 | type = "kernel"; 124 | arch = "arm"; 125 | os = "linux"; 126 | compression = "none"; 127 | load = <0x20008000>; 128 | entry = <0x20008000>; 129 | }; 130 | fdt@tiny210 { 131 | description = "Flattened Device Tree blob for project-x"; 132 | data = /incbin/("/home/hlos/code/xys/temp/project-x/build/out/linux/arch/arm/boot/dts/s5pv210-tiny210.dtb"); 133 | type = "flat_dt"; 134 | arch = "arm"; 135 | compression = "none"; 136 | }; 137 | ramdisk@tiny210 { 138 | description = "Ramdisk for project-x"; 139 | data = /incbin/("/home/hlos/code/xys/temp/project-x/build/out/rootfs/initramfs.gz"); 140 | type = "ramdisk"; 141 | arch = "arm"; 142 | os = "linux"; 143 | compression = "gzip"; 144 | }; 145 | }; 146 | configurations { 147 | default = "conf@tiny210"; 148 | conf@tiny210 { 149 | description = "Boot Linux kernel with FDT blob"; 150 | kernel = "kernel@tiny210"; 151 | fdt = "fdt@tiny210"; 152 | ramdisk = "ramdisk@tiny210"; 153 | }; 154 | }; 155 | }; 156 | ``` 157 | > **可以有多个kernel节点或者fdt节点等, 提高兼容性; 并且可以对kernel、fdt和ramdisk进行多种组合生成多种configurations, 使FIT-uImage可以兼容于多种板子,而无需重新进行编译烧写** 158 | 159 | ##### **3.2.2 itb文件制作** 160 | ``` 161 | // 生成itb文件 162 | xingyanl@yocto:~$ mkimage -f ${UIMAGE_ITS_FILE} ${UIMAGE_ITB_FILE} 163 | ``` 164 | 165 | #### **3.3 使用** 166 | ``` 167 | // 下载FIT-uImage到指定地址 168 | xingyanl@yocto:~$ bootm "指定地址" 169 | ``` 170 | ### **总结:FIT-uImage的格式类似于DTB,uboot通过解析FIT-uImage中的configurations节点,根据节点选择出需要的kernel、dtb、rootfs** 171 | -------------------------------------------------------------------------------- /第6章-U-boot启动内核之二bootm启动内核.md: -------------------------------------------------------------------------------- 1 | ## **第6章-U-boot启动内核之二bootm启动内核** 2 | > 整理自[uboot] uboot启动kernel篇(二)——bootm跳转到kernel的流程](http://blog.csdn.net/ooonebook/article/details/53495021#t1) 3 | 4 | ### **1. bootm简要说明** 5 | > **宏定义使能:** 6 | CONFIG_BOOTM_LINUX=y 7 | CONFIG_CMD_BOOTM=y 8 | 9 | #### **1.1 概要** 10 | **bootm用于启动一个操作系统映像, 它会从映像文件的头部读取一些信息,如:映像文件的的cpu架构、操作系统类型、映像的类型、压缩方式、映像文件在内存中的加载地址、映像文件运行的入口地址、映像文件名等** 11 | 12 | #### **1.2 使用方式** 13 | **a) 对于Legacy-uImage** 14 | ``` 15 | // 假设Legacy-uImage的加载地址是0x20008000,ramdisk的加载地址是0x21000000,fdt的加载地址是0x22000000 16 | 17 | // 只加载kernel 18 | bootm 0x20008000 19 | 20 | // 加载kernel和ramdisk 21 | bootm 0x20008000 0x21000000 22 | 23 | // 加载kernel和fdt 24 | bootm 0x20008000 - 0x22000000 25 | 26 | // 加载kernel、ramdisk、fdt 27 | bootm 0x20008000 0x21000000 0x22000000 28 | ``` 29 | 30 | **b) 对于FIT-uImage** 31 | ``` 32 | // 假设FIT-uImage的加载地址是0x30000000 33 | 34 | // 启动内核 35 | bootm 0x30000000 36 | ``` 37 | 38 | ### **2. 执行流程** 39 | 40 | #### **2.1 bootm函数** 41 | ``` 42 | // 以bootm 0x20008000 0x21000000 0x22000000为例 43 | // 通过U_BOOT_CMD找到对应命令do_bootm 44 | U_BOOT_CMD( 45 | bootm, CONFIG_SYS_MAXARGS, 1, do_bootm, 46 | "boot application image from memory", bootm_help_text 47 | ); 48 | 49 | // cmdtp:_u_boot_list_2_cmd_2_bootm的指针 50 | // argc=4 51 | // argv[0]="bootm", argv[1]=0x20008000, arv[2]=0x21000000, argv[3]=0x22000000 52 | int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 53 | { 54 | // 去掉bootm参数 55 | .... 56 | // 此时argc=3 57 | // argv[0]=0x20008000 , argv[1]=0x21000000, argv[2]=0x22000000 58 | return do_bootm_states(cmdtp, flag, argc, argv, ...., &images, 1); 59 | } 60 | ``` 61 | 62 | #### **2.2 do_bootm_states函数** 63 | ##### **2.2.1 bootm_headers_t数据结构** 64 | > bootm根据参数指向的镜像填充此结构体,然后使用结构体的内容填充kernel的启动信息,进行跳转 65 | 66 | ``` 67 | typedef struct bootm_headers { 68 | image_header_t *legacy_hdr_os; // Legacy-uImage的镜像头 69 | image_header_t legacy_hdr_os_copy; // Legacy-uImage的镜像头备份 70 | ulong legacy_hdr_valid; // Legacy-uImage的镜像头是否存在的标记 71 | #if IMAGE_ENABLE_FIT // 使能FIT 72 | const char *fit_uname_cfg; // 配置节点名 73 | void *fit_hdr_os; // FIT-uImage中kernel镜像头 74 | const char *fit_uname_os; // FIT-uImage中kernel的节点名 75 | int fit_noffset_os; // FIT-uImage中kernel的节点偏移 76 | void *fit_hdr_rd; // FIT-uImage中ramdisk的镜像头 77 | const char *fit_uname_rd; // FIT-uImage中ramdisk的节点名 78 | int fit_noffset_rd; // FIT-uImage中ramdisk的节点偏移 79 | void *fit_hdr_fdt; // FIT-uImage中FDT的镜像头 80 | const char *fit_uname_fdt; // FIT-uImage中FDT的节点名 81 | int fit_noffset_fdt; // FIT-uImage中FDT的节点偏移 82 | #endif 83 | image_info_t os; // 操作系统信息的结构体 84 | ulong ep; // 操作系统的入口地址 85 | ulong rd_start, rd_end; // ramdisk在内存上的起始地址和结束地址 86 | char *ft_addr; // fdt在内存上的地址 87 | ulong ft_len; // fdt在内存上的长度 88 | int state; // 状态标识,用于标识对应的bootm需要做什么操作 89 | 90 | } bootm_headers_t; 91 | ``` 92 | ##### **2.2.2 状态说明** 93 | > do_bootm_states根据states来判断要执行的操作 94 | 95 | ``` 96 | // 开始执行bootm的一些准备动作 97 | #define BOOTM_STATE_START (0x00000001) 98 | // 查找操作系统镜像 99 | #define BOOTM_STATE_FINDOS (0x00000002) 100 | // 查找操作系统镜像外的其他镜像,比如FDT\ramdisk等等 101 | #define BOOTM_STATE_FINDOTHER (0x00000004) 102 | // 加载操作系统 103 | #define BOOTM_STATE_LOADOS (0x00000008) 104 | // 操作ramdisk 105 | #define BOOTM_STATE_RAMDISK (0x00000010) 106 | // 操作FDT 107 | #define BOOTM_STATE_FDT (0x00000020) 108 | // 操作commandline 109 | #define BOOTM_STATE_OS_CMDLINE (0x00000040) 110 | // 跳转到操作系统的前的准备动作 111 | #define BOOTM_STATE_OS_BD_T (0x00000080) 112 | #define BOOTM_STATE_OS_PREP (0x00000100) 113 | // 伪跳转,一般都能直接跳转到kernel中去 114 | #define BOOTM_STATE_OS_FAKE_GO (0x00000200) 115 | // 跳转到kernel中去 116 | #define BOOTM_STATE_OS_GO (0x00000400) 117 | ``` 118 | 119 | #### **2.3 执行流程** 120 | ![执行流程](./images/do_bootm.png) 121 | ``` 122 | // 根据states判断要执行的操作 123 | int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], 124 | int states, bootm_headers_t *images, int boot_progress) 125 | { 126 | // *****************解析uImage***************************// 127 | // 保存states到bootm_headers_t images内部 128 | images->state |= states; 129 | 130 | // BOOTM_STATE_START 131 | if (states & BOOTM_STATE_START) 132 | ret = bootm_start(cmdtp, flag, argc, argv); 133 | 134 | // BOOTM_STATE_FINDOS 135 | if (!ret && (states & BOOTM_STATE_FINDOS)) 136 | ret = bootm_find_os(cmdtp, flag, argc, argv); 137 | 138 | // BOOTM_STATE_FINDOTHER 139 | if (!ret && (states & BOOTM_STATE_FINDOTHER)) { 140 | ret = bootm_find_other(cmdtp, flag, argc, argv); 141 | argc = 0; /* consume the args */ 142 | } 143 | // ********************解析uImage结束************************// 144 | // BOOTM_STATE_LOADOS 145 | if (!ret && (states & BOOTM_STATE_LOADOS)) { 146 | ulong load_end; 147 | iflag = bootm_disable_interrupts(); 148 | ret = bootm_load_os(images, &load_end, 0); 149 | } 150 | 151 | // 是否需要重定向ramdinsk,do_bootm流程不需要 152 | #ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH 153 | if (!ret && (states & BOOTM_STATE_RAMDISK)) { 154 | ulong rd_len = images->rd_end - images->rd_start; 155 | ret = boot_ramdisk_high(&images->lmb, images->rd_start, 156 | rd_len, &images->initrd_start, &images->initrd_end); 157 | } 158 | #endif 159 | 160 | // 是否需要重定向fdt,do_bootm流程不需要 161 | #if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB) 162 | if (!ret && (states & BOOTM_STATE_FDT)) { 163 | boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); 164 | ret = boot_relocate_fdt(&images->lmb, &images->ft_addr, 165 | &images->ft_len); 166 | } 167 | #endif 168 | 169 | // 获取OS启动函数, 如下: 170 | boot_fn = bootm_os_get_boot_func(images->os.os); 171 | 172 | // 跳转到OS前的准备动作,会直接调用启动函数,但是标识是BOOTM_STATE_OS_PREP 173 | if (!ret && (states & BOOTM_STATE_OS_PREP)) 174 | ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); 175 | 176 | // BOOTM_STATE_OS_GO标识,跳转到操作系统中,并且不应该再返回了 177 | if (!ret && (states & BOOTM_STATE_OS_GO)) 178 | ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, 179 | images, boot_fn); 180 | } 181 | 182 | // 根据OS类型获得到对应的操作函数 183 | boot_os_fn *bootm_os_get_boot_func(int os) 184 | { 185 | return boot_os[os]; 186 | } 187 | 188 | // 获取启动函数 189 | static boot_os_fn *boot_os[] = { 190 | // linux内核启动函数 191 | [IH_OS_LINUX] = do_bootm_linux, 192 | .... 193 | } 194 | ``` 195 | **跳转到内核** 196 | 197 | ![do_bootm_linux函数](./images/do_bootm_linux.png) 198 | ``` 199 | int do_bootm_linux(int flag, int argc, char * const argv[], 200 | bootm_headers_t *images) 201 | { 202 | // 实现跳转到linux前的准备动作 203 | boot_prep_linux(images); 204 | // 跳转到linux内核中 205 | boot_jump_linux(images, flag); 206 | } 207 | 208 | // 主要任务是修复LMB,并把它填入到fdt中 209 | // LMB是指Logical Memory blocks,用于表示内存保留区域,主要有fdt的区域,ramdisk区域等 210 | static void boot_prep_linux(bootm_headers_t *images) 211 | { 212 | char *commandline = getenv("bootargs"); 213 | if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { 214 | #ifdef CONFIG_OF_LIBFDT 215 | if (image_setup_linux(images)) { 216 | hang(); 217 | } 218 | #endif 219 | } 220 | 221 | /* Subcommand: GO */ 222 | static void boot_jump_linux(bootm_headers_t *images, int flag) 223 | { 224 | // 从gd中获取machine-id 225 | unsigned long machid = gd->bd->bi_arch_number; 226 | 227 | // kernel入口函数,即kernel的入口地址,对应kernel的_start段地址 228 | void (*kernel_entry)(int zero, int arch, uint params); 229 | 230 | // 伪跳转,并不真正地跳转到kernel中 231 | int fake = (flag & BOOTM_STATE_OS_FAKE_GO); 232 | 233 | // 设置kernel的入口地址 234 | kernel_entry = (void (*)(int, int, uint))images->ep; 235 | 236 | // 把fdt的地址放人r2寄存器中 237 | if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) 238 | r2 = (unsigned long)images->ft_addr; 239 | else 240 | r2 = gd->bd->bi_boot_params; 241 | 242 | // 执行kernel_entry, 跳转到kernel内核中 243 | kernel_entry(0, machid, r2); 244 | 245 | } 246 | ``` 247 | -------------------------------------------------------------------------------- /第0章-U-boot基础.md: -------------------------------------------------------------------------------- 1 | # 基础 2 | 3 | ------ 4 | ## 1. 概念 5 | 6 | ### 1.1 AHB/APB 7 | - AHB主要用于高性能模块(如CPU, DMA和DSP等)之间的连接,是SOC的片上系统总线 8 | - APB主要用于低带宽的周围外设之间的连接,如UART等 9 | 10 | ### 1.2 BIOS基本输入输出系统 11 | - 固化在计算机内主板上一个ROM芯片上的程序,保存计算机最重要的基本输入输出的程序,如系统设置程序,开机自检程序和系统自启动程序;提供最底层,最直接的硬件设置和控制 12 | 13 | ------ 14 | ## 2. 命令 15 | ### 2.1 go 16 | - 跳转到指定地址执行 17 | 18 | ### 2.2 help 19 | ``` 20 | => help 21 | ? - alias for 'help' 22 | base - print or set address offset 23 | bdinfo - print Board Info structure 24 | bmode - sd2|sd3|emmc|normal|usb|sata|ecspi1:0|ecspi1:1|ecspi1:2|ecspi1:3|esdhc1|esdhc2|esdhc3|esdhc4 [noreset] 25 | boot - boot default, i.e., run 'bootcmd' 26 | bootd - boot default, i.e., run 'bootcmd' 27 | bootelf - Boot from an ELF image in memory 28 | bootm - boot application image from memory 29 | bootp - boot image via network using BOOTP/TFTP protocol 30 | bootvx - Boot vxWorks from an ELF image 31 | bootz - boot Linux zImage image from memory 32 | clocks - display clocks 33 | clrlogo - fill the boot logo area with black 34 | cmp - memory compare 35 | coninfo - print console devices and information 36 | cp - memory copy 37 | cpu - Multiprocessor CPU boot manipulation and release 38 | crc32 - checksum calculation 39 | dcache - enable or disable data cache 40 | dfu - Device Firmware Upgrade 41 | dhcp - boot image via network using DHCP/TFTP protocol 42 | dm - Driver model low level access 43 | echo - echo args to console 44 | editenv - edit environment variable 45 | env - environment handling commands 46 | erase - erase FLASH memory 47 | exit - exit script 48 | ext2load- load binary file from a Ext2 filesystem 49 | ext2ls - list files in a directory (default /) 50 | ext4load- load binary file from a Ext4 filesystem 51 | ext4ls - list files in a directory (default /) 52 | ext4size- determine a file's size 53 | ext4write- create a file in the root directory 54 | false - do nothing, unsuccessfully 55 | fatinfo - print information about filesystem 56 | fatload - load binary file from a dos filesystem 57 | fatls - list files in a directory (default /) 58 | fatsize - determine a file's size 59 | fdt - flattened device tree utility commands 60 | flinfo - print FLASH memory information 61 | fstype - Look up a filesystem type 62 | fuse - Fuse sub-system 63 | go - start application at address 'addr' 64 | gpio - query and control gpio pins 65 | help - print command description/usage 66 | i2c - I2C sub-system 67 | icache - enable or disable instruction cache 68 | iminfo - print header information for application image 69 | imxtract- extract a part of a multi-image 70 | itest - return true/false on integer compare 71 | load - load binary file from a filesystem 72 | loadb - load binary file over serial line (kermit mode) 73 | loads - load S-Record file over serial line 74 | loadx - load binary file over serial line (xmodem mode) 75 | loady - load binary file over serial line (ymodem mode) 76 | loop - infinite loop on address range 77 | ls - list files in a directory (default /) 78 | md - memory display 79 | mdio - MDIO utility commands 80 | mii - MII utility commands 81 | mm - memory modify (auto-incrementing address) 82 | mmc - MMC sub system 83 | mmcinfo - display MMC info 84 | mtest - simple RAM read/write test 85 | mw - memory write (fill) 86 | nfs - boot image via network using NFS protocol 87 | nm - memory modify (constant address) 88 | ping - send ICMP ECHO_REQUEST to network host 89 | pmic - PMIC 90 | printenv- print environment variables 91 | protect - enable or disable FLASH write protection 92 | reset - Perform RESET of the CPU 93 | run - run commands in an environment variable 94 | save - save file to a filesystem 95 | saveenv - save environment variables to persistent storage 96 | setenv - set environment variables 97 | setexpr - set environment variable as the result of eval expression 98 | sf - SPI flash sub-system 99 | showvar - print local hushshell variables 100 | size - determine a file's size 101 | sleep - delay execution for some time 102 | source - run script from memory 103 | test - minimal test like /bin/sh 104 | tftpboot- boot image via network using TFTP protocol 105 | true - do nothing, successfully 106 | ums - Use the UMS [USB Mass Storage] 107 | usb - USB sub-system 108 | usbboot - boot from USB device 109 | version - print monitor, compiler and linker version 110 | ``` 111 | 112 | ### 2.3 u-boot环境变量 113 | ``` 114 | Normal Boot 115 | Hit any key to stop autoboot: 0 116 | => print 117 | baudrate=115200 118 | board_name=SABRESD 119 | board_rev=MX6Q 120 | boot_fdt=try 121 | bootcmd=run findfdt;mmc dev ${mmcdev};if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else run netboot; fi 122 | bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr}; 123 | bootdelay=3 124 | bootscript=echo Running bootscript from mmc ...; source 125 | console=ttymxc0 126 | dfu_alt_info=spl raw 0x400 127 | dfu_alt_info_img=u-boot raw 0x10000 128 | dfu_alt_info_spl=spl raw 0x400 129 | dfuspi=dfu 0 sf 0:0:10000000:0 130 | emmcdev=2 131 | epdc_waveform=epdc_splash.bin 132 | ethact=FEC 133 | ethprime=FEC 134 | fdt_addr=0x18000000 135 | fdt_file=undefined 136 | fdt_high=0xffffffff 137 | findfdt=if test $fdt_file = undefined; then if test $board_name = SABREAUTO && test $board_rev = MX6QP; then setenv fdt_file imx6qp-sabreauto.dtb; fi; if test $board_name = SABREAUTO && test $board_rev = MX6Q; then setenv fdt_file imx6q-sabreauto.dtb; fi; if test $board_name = SABREAUTO && test $board_rev = MX6DL; then setenv fdt_file imx6dl-sabreauto.dtb; fi; if test $board_name = SABRESD && test $board_rev = MX6QP; then setenv fdt_file imx6qp-sabresd.dtb; fi; if test $board_name = SABRESD && test $board_rev = MX6Q; then setenv fdt_file imx6q-sabresd.dtb; fi; if test $board_name = SABRESD && test $board_rev = MX6DL; then setenv fdt_file imx6dl-sabresd.dtb; fi; if test $fdt_file = undefined; then echo WARNING: Could not determine dtb to use; fi; fi; 138 | image=zImage 139 | initrd_addr=0x12C00000 140 | initrd_high=0xffffffff 141 | ip_dyn=yes 142 | loadaddr=0x12000000 143 | loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script}; 144 | loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file} 145 | loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image} 146 | mfgtool_args=setenv bootargs console=ttymxc0,115200 rdinit=/linuxrc g_mass_storage.stall=0 g_mass_storage.removable=1 g_mass_storage.file=/fat g_mass_storage.ro=1 g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF g_mass_storage.iSerialNumber="" enable_wait_mode=off 147 | mmcargs=setenv bootargs console=${console},${baudrate} ${smp} root=${mmcroot} 148 | mmcautodetect=yes 149 | mmcboot=echo Booting from mmc ...; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi; 150 | mmcdev=2 151 | mmcpart=1 152 | mmcroot=/dev/mmcblk3p2 rootwait rw 153 | netargs=setenv bootargs console=${console},${baudrate} ${smp} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp 154 | netboot=echo Booting from net ...; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${image}; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then bootz ${loadaddr} - ${fdt_addr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi; 155 | script=boot.scr 156 | update_emmc_firmware=if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; if ${get_cmd} ${update_sd_firmware_filename}; then if mmc dev ${emmcdev} 1; then setexpr fw_sz ${filesize} / 0x200; setexpr fw_sz ${fw_sz} + 1; mmc write ${loadaddr} 0x2 ${fw_sz}; fi; fi 157 | update_sd_firmware=if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; if mmc dev ${mmcdev}; then if ${get_cmd} ${update_sd_firmware_filename}; then setexpr fw_sz ${filesize} / 0x200; setexpr fw_sz ${fw_sz} + 1; mmc write ${loadaddr} 0x2 ${fw_sz}; fi; fi 158 | 159 | Environment size: 3587/8188 bytes 160 | => 161 | ``` 162 | 163 | -------------------------------------------------------------------------------- /第3章-U-boot命令行.md: -------------------------------------------------------------------------------- 1 | ## **第3章-U-boot命令行** 2 | ### **1. 基本概念** 3 | 所谓的**命令行模式**是指u-boot初始化完成以后,等待终端输入命令和处理命令的模式 4 | > 通过宏定义CONFIG_CMDLINE使能; 对应命令要打开对应的宏定义,如:要支持bootm命令,则需要打开CONFIG_CMD_BOOTM宏,还有几个相关的TBD 5 | 6 | 7 | ------- 8 | ### **2. 数据结构** 9 | #### **2.1 基本概念** 10 | U-boot把所有命令的数据结构放在一个表(命令表)中,其中的每一项代表一个命令,类型是**cmd_tbl_t** 11 | **数据结构:** 12 | ``` 13 | struct cmd_tbl_s { 14 | char *name; // 命令名字,对应于执行命令的字符串 15 | int maxargs; // 命令支持最大参数 16 | int repeatable; // 是否需要重复,也就是空回车键是否会不停执行 17 | int (*cmd)(struct cmd_tbl_s *, int, int, char * const []); // 命令处理函数 18 | char *usage; // 帮助(short) 19 | char *help; // 帮助(long),通过CONFIG_SYS_LONGHELP使能 20 | // TBD,根据宏定义使能 21 | int (*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]); 22 | }; 23 | 24 | typedef struct cmd_tbl_s cmd_tbl_t; 25 | 26 | ``` 27 | **以bootm命令为例:** 28 | ``` 29 | // bootm对应数据结构符号: _u_boot_list_2_cmd_2_bootm,起始地址: 0x17881c3c 30 | 17881c3c <_u_boot_list_2_cmd_2_bootm>: 31 | 17881c3c: 17863203 strne r3, [r6, r3, lsl #4] 32 | 17881c40: 00000020 andeq r0, r0, r0, lsr #32 33 | 17881c44: 00000001 andeq r0, r0, r1 34 | 17881c48: 178044bc ; instruction: 0x178044bc 35 | 17881c4c: 1786b1d8 ; instruction: 0x1786b1d8 36 | 17881c50: 17872324 strne r2, [r7, r4, lsr #6] 37 | 17881c54: 00000000 andeq r0, r0, r0 38 | ``` 39 | 40 | #### **2.2 u-boot.map符号表 ** 41 | u-boot.map符号表的定义**TBD** 42 | **cmd相关部分** 43 | ``` 44 | // 命令表定义在_cmd_1和_cmd_3的位置中, 每个占28个字节, 和cmd_tbl_t结构的大小是一致 45 | .u_boot_list 0x00000000178739d0 0x1450 46 | *(SORT(.u_boot_list*)) 47 | .u_boot_list_2_cmd_1 // 链接器生成 48 | 0x0000000017873a00 0x0 cmd/built-in.o 49 | .u_boot_list_2_cmd_1 50 | 0x0000000017873a00 0x0 common/built-in.o 51 | .u_boot_list_2_cmd_2_base 52 | 0x0000000017873a00 0x1c cmd/built-in.o 53 | 0x0000000017873a00 _u_boot_list_2_cmd_2_base 54 | .u_boot_list_2_cmd_2_bdinfo 55 | 0x0000000017873a1c 0x1c cmd/built-in.o 56 | 0x0000000017873a1c _u_boot_list_2_cmd_2_bdinfo 57 | .u_boot_list_2_cmd_2_bmode 58 | 0x0000000017873a38 0x1c arch/arm/imx-common/built-in.o 59 | 0x0000000017873a38 _u_boot_list_2_cmd_2_bmode 60 | .u_boot_list_2_cmd_2_boot 61 | 0x0000000017873a54 0x1c cmd/built-in.o 62 | 0x0000000017873a54 _u_boot_list_2_cmd_2_boot 63 | .u_boot_list_2_cmd_2_bootd 64 | 0x0000000017873a70 0x1c cmd/built-in.o 65 | 0x0000000017873a70 _u_boot_list_2_cmd_2_bootd 66 | ...... 67 | .u_boot_list_2_cmd_3 68 | 0x00000000178743a0 0x0 cmd/built-in.o 69 | .u_boot_list_2_cmd_3 // 链接器生成 70 | 0x00000000178743a0 0x0 common/built-in.o 71 | 72 | ``` 73 | 74 | #### **2.3 定义一个命令 ** 75 | **以bootm为例:** 76 | ``` 77 | U_BOOT_CMD( 78 | bootm, CONFIG_SYS_MAXARGS, 1, do_bootm, 79 | "boot application image from memory", bootm_help_text 80 | ); 81 | // 命令处理函数 82 | int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 83 | ``` 84 | 85 | **U_BOOT_CMD实现** 86 | ``` 87 | // 接口 88 | #define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) 89 | 90 | // 实现在include/common.h文件中 91 | #define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) \ 92 | U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL) 93 | 94 | #define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \ 95 | ll_entry_declare(cmd_tbl_t, _name, cmd) = \ 96 | U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \ 97 | _usage, _help, _comp); 98 | 99 | #define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd, \ 100 | _usage, _help, _comp) \ 101 | { #_name, _maxargs, _rep, _cmd, _usage, \ 102 | _CMD_HELP(_help) _CMD_COMPLETE(_comp) } 103 | 104 | #define ll_entry_declare(_type, _name, _list) \ 105 | _type _u_boot_list_2_##_list##_2_##_name __aligned(4) \ 106 | __attribute__((unused, \ 107 | section(".u_boot_list_2_"#_list"_2_"#_name))) 108 | 109 | ll_entry_declare(cmd_tbl_t, _name, cmd) 110 | 111 | // 最终得到数据结构如下,并保持在.u_boot_list_2_cmd_2_bootm段中: 112 | cmd_tbl_t _u_boot_list_2_cmd_2_bootm= 113 | { 114 | _name=bootm, 115 | _maxargs=CONFIG_SYS_MAXARGS, 116 | _rep=1, 117 | _cmd=do_bootm, 118 | _usage="boot application image from memory", 119 | _help=bootm_help_text, 120 | _comp=NULL, 121 | } 122 | ``` 123 | 124 | ------- 125 | ### **3. 命令行模式** 126 | 命令行模式有两种: 127 | 1. 通用模式:获取数据,解析和处理命令 128 | 2. hush模式:命令的解析和处理使用busybox中的hush工具,对应代码为hush.c 129 | 130 | u-boot执行完所有初始化程序后,调用**run_main_loop**进入主循环,接着进入命令行模式 131 | ``` 132 | // common/board_r.c文件 133 | static int run_main_loop(void) 134 | { 135 | for (;;) 136 | // 进入了主循环,autoboot也在主循环内实现 137 | main_loop(); 138 | return 0; 139 | } 140 | 141 | // common/main.c文件 142 | void main_loop(void) 143 | { 144 | // cli的初始化,主要是hush模式下的初始化 145 | cli_init(); 146 | 147 | // 命令行模式循环模式,不返回 148 | cli_loop(); 149 | panic("No CLI available"); 150 | } 151 | 152 | 153 | // common/cli.c文件 154 | void cli_loop(void) 155 | { 156 | // hush命令模式 157 | parse_file_outer(); 158 | 159 | // 通用命令行模式 160 | cli_simple_loop(); 161 | } 162 | 163 | ``` 164 | 165 | #### **3.1 通用模式** 166 | **通用模式处理流程** 167 | ``` 168 | void cli_simple_loop(void) 169 | { 170 | for (;;) { 171 | // 打印CONFIG_SYS_PROMPT,然后从串口读取一行作为命令,存储在console_buffer中 172 | len = cli_readline(CONFIG_SYS_PROMPT); 173 | 174 | if (len > 0) 175 | // 从console_buffer中copy到lastcommand中 176 | strlcpy(lastcommand, console_buffer, 177 | CONFIG_SYS_CBSIZE + 1); 178 | else if (len == 0) 179 | // 得到换行符,设置重复标识,后续重复执行上条命令 180 | flag |= CMD_FLAG_REPEAT; 181 | 182 | if (len == -1) 183 | // 直接打印中断 184 | puts("\n"); 185 | else 186 | // 执行lastcommand中的命令 187 | rc = run_command_repeatable(lastcommand, flag); 188 | 189 | if (rc <= 0) { 190 | // 命令执行出错时,清空lastcommand,防止下次再执行命令 191 | /* invalid command or not repeatable, forget it */ 192 | lastcommand[0] = 0; 193 | } 194 | } 195 | } 196 | 197 | int run_command_repeatable(const char *cmd, int flag) 198 | { 199 | return cli_simple_run_command(cmd, flag); 200 | } 201 | 202 | int cli_simple_run_command(const char *cmd, int flag) 203 | { 204 | // 把lastcommand中保存的内容转化成argv和argc格式 205 | argc = cli_simple_parse_line(finaltoken, argv); 206 | 207 | // 调用cmd_process处理命令 208 | if (cmd_process(flag, argc, argv, &repeatable, NULL)) 209 | } 210 | 211 | ``` 212 | 213 | #### **3.2 hush模式(**TBD**)** 214 | 215 | ------- 216 | 217 | ### **4. 命令执行流程** 218 | #### **4.1 对以上接收到命令进行处理** 219 | ``` 220 | // common/command.c文件 221 | enum command_ret_t cmd_process(int flag, int argc, char * const argv[], 222 | int *repeatable, ulong *ticks) 223 | { 224 | // 根据命令argv[0],获取它对应的表项cmd_tbl_t 225 | cmdtp = find_cmd(argv[0]); 226 | 227 | // 执行命令表项中的命令,返回0表示成功 228 | rc = cmd_call(cmdtp, flag, argc, argv); 229 | } 230 | 231 | // 获取命令表 232 | cmd_tbl_t *find_cmd(const char *cmd) 233 | { 234 | // 获取命令表的地址,start表示指向命令表的指针 235 | cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd); 236 | 237 | // 获取命令表的长度 238 | const int len = ll_entry_count(cmd_tbl_t, cmd); 239 | 240 | // 查找和cmd匹配的表项,返回给调用者。 241 | return find_cmd_tbl(cmd, start, len); 242 | } 243 | 244 | ``` 245 | 246 | #### **4.2 获取命令表地址和长度** 247 | ``` 248 | // include/linker_lists.h文件 249 | 250 | // _list == cmd,则获取.u_boot_list_2_cmd_1的起始地址 251 | #define ll_entry_start(_type, _list) \ 252 | ({ \ 253 | static char start[0] __aligned(4) __attribute__((unused, \ 254 | section(".u_boot_list_2_"#_list"_1"))); \ 255 | (_type *)&start; \ 256 | }) 257 | 258 | // _list == cmd,则获取.u_boot_list_2_cmd_3的结束地址 259 | #define ll_entry_end(_type, _list) \ 260 | ({ \ 261 | static char end[0] __aligned(4) __attribute__((unused, \ 262 | section(".u_boot_list_2_"#_list"_3"))); \ 263 | (_type *)&end; \ 264 | }) 265 | 266 | // _list == cmd,计算命令表的长度 267 | #define ll_entry_count(_type, _list) \ 268 | ({ \ 269 | _type *start = ll_entry_start(_type, _list); \ 270 | _type *end = ll_entry_end(_type, _list); \ 271 | unsigned int _ll_result = end - start; \ 272 | _ll_result; \ 273 | }) 274 | 275 | // 查找匹配表项common/command.c文件, 参数table和table_len可通过上面计算得到 276 | cmd_tbl_t *find_cmd_tbl(const char *cmd, cmd_tbl_t *table, int table_len) 277 | { 278 | // 查找table中的每一个cmd_tbl_t 279 | for (cmdtp = table; cmdtp != table + table_len; cmdtp++) { 280 | if (strncmp(cmd, cmdtp->name, len) == 0) { 281 | if (len == strlen(cmdtp->name)) 282 | // 命令字符串和表项中的name完全匹配,且长度相等,直接返回 283 | return cmdtp; 284 | 285 | // 若命令字符串和表项中的name的前面部分匹配的话(我们称为部分匹配),则暂时保存下来,并记录有几个这样的表项 286 | // 目的是支持自动补全的功能 287 | cmdtp_temp = cmdtp; 288 | n_found++; 289 | } 290 | } 291 | if (n_found == 1) { 292 | // 如果部分匹配的表项是唯一,则将这个表项返回 293 | // 目的是支持自动补全的功能 294 | return cmdtp_temp; 295 | } 296 | } 297 | ``` 298 | 299 | #### **4.3 执行命令** 300 | ``` 301 | // 执行对应命令表项中的命令 302 | static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 303 | { 304 | // 执行命令表项cmd_tbl_t中的cmd命令处理函数,返回0代表成功 305 | result = (cmdtp->cmd)(cmdtp, flag, argc, argv); 306 | } 307 | 308 | ``` 309 | -------------------------------------------------------------------------------- /第4章-U-boot驱动模型之二数据结构.md: -------------------------------------------------------------------------------- 1 | # 数据结构 2 | 3 | ------ 4 | ## 2. uclass_id 5 | **每种uclass有对应的ID号,定义在其uclass_driver中,这个和ID号和其对应的udevice中的driver中的uclass_id一样** 6 | ``` 7 | // include/dm/uclass-id.h 8 | enum uclass_id { 9 | UCLASS_ROOT = 0, 10 | UCLASS_DEMO, 11 | UCLASS_CLK, 12 | UCLASS_PINCTRL, 13 | UCLASS_SERIAL, 14 | } 15 | ``` 16 | 17 | ------ 18 | ## 3. uclass 19 | ### 3.1 数据结构 20 | ``` 21 | struct uclass { 22 | // uclass的私有数据指针 23 | void *priv; 24 | // 对应的uclass driver 25 | struct uclass_driver *uc_drv; 26 | // 链表头,挂接它所有相关联的udevice 27 | struct list_head dev_head; 28 | // 链表节点,用于把uclass连接到uclass_root链表上 29 | struct list_head sibling_node; 30 | }; 31 | ``` 32 | ### 3.2 生成 33 | 有对应uclass driver并且下面挂有udevice的uclass才会被uboot自动生成 34 | 35 | ### 3.3 存放位置 36 | 所有生成的都会挂接到gd->uclass_root链表上 37 | 38 | ### 3.4 获取对应的API 39 | ``` 40 | // 根据uclass_id遍历gd->ulcass_root链表 41 | int uclass_get(enum uclass_id key, struct uclass **ucp); 42 | ``` 43 | 44 | ------ 45 | ## 4. uclass_driver 46 | ### 4.1 数据结构 47 | ``` 48 | // include/dm/uclass.h,每个uclass都有对应的uclass_driver 49 | struct uclass_driver { 50 | // 该uclass_driver的名字 51 | const char *name; 52 | // 对应的uclass id 53 | enum uclass_id id; 54 | 55 | /* 以下函数指针主要是调用时机的区别 */ 56 | int (*post_bind)(struct udevice *dev); // 在udevice被绑定到该uclass之后调用 57 | int (*pre_unbind)(struct udevice *dev); // 在udevice被解绑出该uclass之前调用 58 | int (*pre_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之前调用 59 | int (*post_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之后调用 60 | int (*pre_remove)(struct udevice *dev);// 在该uclass的一个udevice进行remove之前调用 61 | int (*child_post_bind)(struct udevice *dev); // 在该uclass对应的某个udevice的某个设备被绑定到该udevice之后调用 62 | int (*child_pre_probe)(struct udevice *dev); // 在该uclass对应的某个udevice的某个设备进行probe之前调用 63 | int (*init)(struct uclass *class); // 安装该uclass的时候调用 64 | int (*destroy)(struct uclass *class); // 销毁该uclass的时候调用 65 | int priv_auto_alloc_size; // 需要为对应的uclass分配多少私有数据 66 | const void *ops; //操作集合 67 | }; 68 | 69 | ``` 70 | ### 4.2 生成 71 | - 通过UCLASS\_DRIVER定义uclass\_driver 72 | ``` 73 | // 以serial-uclass为例 74 | UCLASS_DRIVER(serial) = { 75 | .id = UCLASS_SERIAL, 76 | .name = "serial", 77 | .flags = DM_UC_FLAG_SEQ_ALIAS, 78 | .post_probe = serial_post_probe, 79 | .pre_remove = serial_pre_remove, 80 | .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), 81 | }; 82 | ``` 83 | - UCLASS_DRIVER宏定义 84 | 85 | ``` 86 | #define UCLASS_DRIVER(__name) \ 87 | ll_entry_declare(struct uclass_driver, __name, uclass) 88 | 89 | #define ll_entry_declare(_type, _name, _list) \ 90 | _type _u_boot_list_2_##_list##_2_##_name __aligned(4) \ 91 | __attribute__((unused, \ 92 | section(".u_boot_list_2_"#_list"_2_"#_name))) 93 | ``` 94 | - 最终会得到如下结构体,并且存放在.u\_boot\_list\_2\_uclass\_2\_serial段中 95 | 96 | ``` 97 | struct uclass_driver _u_boot_list_2_uclass_2_serial = { 98 | .id = UCLASS_SERIAL, // 设置对应的uclass id 99 | .name = "serial", 100 | .flags = DM_UC_FLAG_SEQ_ALIAS, 101 | .post_probe = serial_post_probe, 102 | .pre_remove = serial_pre_remove, 103 | .per_device_auto_alloc_size = sizeof(struct serial_dev_priv), 104 | } 105 | ``` 106 | 107 | ### 4.3 存放位置 108 | 通过查看u-boot.map可以得到: 109 | ``` 110 | .u_boot_list_2_uclass_1 111 | 0x23e368e0 0x0 drivers/built-in.o 112 | .u_boot_list_2_uclass_2_gpio 113 | 0x23e368e0 0x48 drivers/gpio/built-in.o 114 | 0x23e368e0 _u_boot_list_2_uclass_2_gpio // gpio uclass driver的符号 115 | .u_boot_list_2_uclass_2_root 116 | 0x23e36928 0x48 drivers/built-in.o 117 | 0x23e36928 _u_boot_list_2_uclass_2_root // root uclass drvier的符号 118 | .u_boot_list_2_uclass_2_serial 119 | 0x23e36970 0x48 drivers/serial/built-in.o 120 | 0x23e36970 _u_boot_list_2_uclass_2_serial // serial uclass driver的符号 121 | .u_boot_list_2_uclass_2_simple_bus 122 | 0x23e369b8 0x48 drivers/built-in.o 123 | 0x23e369b8 _u_boot_list_2_uclass_2_simple_bus 124 | .u_boot_list_2_uclass_3 125 | 0x23e36a00 0x0 drivers/built-in.o 126 | 0x23e36a00 . = ALIGN (0x4) 127 | ``` 128 | - 即所有的uclass driver都会放在.u_boot_list_2_uclass_1和.u_boot_list_2_uclass_3的区间中,这个列表也称为uclass_driver table** 129 | 130 | ### 4.4 获取对应的API 131 | - 先获取uclass\_driver table,然后遍历获得对应的uclass\_driver 132 | ``` 133 | // 根据.u_boot_list_2_uclass_1的段地址获得uclass_driver table的地址 134 | struct uclass_driver *uclass = 135 | ll_entry_start(struct uclass_driver, uclass); 136 | 137 | // 获得uclass_driver table的长度 138 | const int n_ents = ll_entry_count(struct uclass_driver, uclass); 139 | 140 | // 根据uclass_id从uclass_driver table中得到相应的uclass_driver 141 | struct uclass_driver *lists_uclass_lookup(enum uclass_id id) 142 | ``` 143 | 144 | ------ 145 | ## 5. udevice 146 | ### 5.1 数据结构 147 | ``` 148 | // include/dm/device.h, 描述的是设备树内容 149 | struct udevice { 150 | const struct driver *driver; // 该udevice对应的driver 151 | const char *name; // 设备名 152 | void *platdata; // 该udevice的平台数据 153 | void *parent_platdata; // 提供给父设备使用的平台数据 154 | void *uclass_platdata; // 提供给所属uclass使用的平台数据 155 | int of_offset; // 该udevice的dtb节点偏移,代表了dtb里面的这个节点node 156 | ulong driver_data; // 驱动数据 157 | struct udevice *parent; // 父设备 158 | void *priv; // 私有数据的指针 159 | struct uclass *uclass; // 所属uclass 160 | void *uclass_priv; // 提供给所属uclass使用的私有数据指针 161 | void *parent_priv; // 提供给其父设备使用的私有数据指针 162 | struct list_head uclass_node; // 挂接到它所属uclass的链表上 163 | struct list_head child_head; // 链表头,挂接它下面的子设备 164 | struct list_head sibling_node; // 挂接到父设备的链表上 165 | uint32_t flags; // 标识 166 | int req_seq; // 请求的seq 167 | int seq; // 分配的seq 168 | }; 169 | ``` 170 | 171 | ### 5.2 生成 172 | uboot解析dtb以后动态生成 173 | 174 | ### 5.3 存放位置 175 | 1. 挂接到对应的uclass上,即挂接到uclass->dev\_head 176 | 2. 挂接到父设备的子设备链表中, 即挂接到udevice->child\_head, 最上层的父设备是gd->dm\_root 177 | 178 | ### 5.4 获取对应的API 179 | - 通过uclass获取对应的udevice 180 | > 不应该是dtb解析获得的吗?的确是通过dtb解析获得 181 | 182 | ``` 183 | int uclass_get_device(enum uclass_id id, int index, struct udevice **devp); // 通过索引从uclass中获取udevice 184 | int uclass_get_device_by_name(enum uclass_id id, const char *name, struct udevice **devp); // 通过设备名从uclass中获取udevice 185 | int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp); 186 | int uclass_get_device_by_of_offset(enum uclass_id id, int node, 187 | struct udevice **devp); 188 | int uclass_resolve_seq(struct udevice *dev); 189 | ``` 190 | 191 | ------ 192 | ## 6. driver 193 | ### 6.1 数据结构 194 | ``` 195 | // include/dm/device.h 196 | struct driver { 197 | char *name; // 驱动名 198 | enum uclass_id id; // 对应的uclass id 199 | const struct udevice_id *of_match; // compatible字符串的匹配表,用于和device tree里面的设备节点匹配 200 | int (*bind)(struct udevice *dev); // 用于绑定目标设备到该driver中 201 | int (*probe)(struct udevice *dev); // 用于probe目标设备激活 202 | int (*remove)(struct udevice *dev); // 用于remove目标设备禁用 203 | int (*unbind)(struct udevice *dev); // 用于解绑目标设备到该driver中 204 | int (*ofdata_to_platdata)(struct udevice *dev); // 在probe之前,解析对应udevice的dts节点,转化成udevice的平台数据 205 | int (*child_post_bind)(struct udevice *dev); // 如果目标设备的一个子设备被绑定之后调用 206 | int (*child_pre_probe)(struct udevice *dev); // 在目标设备的一个子设备被probe之前调用 207 | int (*child_post_remove)(struct udevice *dev); // 在目标设备的一个子设备被remove之后调用 208 | int priv_auto_alloc_size; //需要分配多少空间作为其udevice的私有数据 209 | int platdata_auto_alloc_size; //需要分配多少空间作为其udevice的平台数据 210 | int per_child_auto_alloc_size; // 对于目标设备的每个子设备需要分配多少空间作为父设备的私有数据 211 | int per_child_platdata_auto_alloc_size; // 对于目标设备的每个子设备需要分配多少空间作为父设备的平台数据 212 | const void *ops; /* driver-specific operations */ // 操作集合的指针,提供给uclass使用,没有规定操作集的格式,由具体uclass决定 213 | }; 214 | ``` 215 | 216 | ### 6.2 生成 217 | - 通过U\_BOOT\_DRIVER定义一个driver 218 | ``` 219 | // 以serial为例 220 | U_BOOT_DRIVER(serial_s5p) = { 221 | .name = "serial_s5p", 222 | .id = UCLASS_SERIAL, 223 | // 定义为设备树中的compatible属性 224 | .of_match = s5p_serial_ids, 225 | .ofdata_to_platdata = s5p_serial_ofdata_to_platdata, 226 | .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), 227 | .probe = s5p_serial_probe, 228 | .ops = &s5p_serial_ops, 229 | .flags = DM_FLAG_PRE_RELOC, 230 | }; 231 | ``` 232 | - U\_BOOT\_DRIVER宏定义如下,生成的结构体保存在.u\_boot\_list\_2\_driver\_2\_serial\_s5p段中** 233 | 234 | ``` 235 | // U_BOOT_DRIVER宏定义 236 | #define U_BOOT_DRIVER(__name) \ 237 | ll_entry_declare(struct driver, __name, driver) 238 | 239 | #define ll_entry_declare(_type, _name, _list) \ 240 | _type _u_boot_list_2_##_list##_2_##_name __aligned(4) \ 241 | __attribute__((unused, \ 242 | section(".u_boot_list_2_"#_list"_2_"#_name))) 243 | 244 | // 生成结构体 245 | struct driver _u_boot_list_2_driver_2_serial_s5p= { 246 | .name = "serial_s5p", 247 | .id = UCLASS_SERIAL, 248 | .of_match = s5p_serial_ids, 249 | .ofdata_to_platdata = s5p_serial_ofdata_to_platdata, 250 | .platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata), 251 | .probe = s5p_serial_probe, 252 | .ops = &s5p_serial_ops, 253 | .flags = DM_FLAG_PRE_RELOC, 254 | }; 255 | ``` 256 | 257 | ### 6.3 存放位置 258 | 通过查看u-boot.map可以得到: 259 | ``` 260 | .u_boot_list_2_driver_1 261 | 0x00000000178743a0 0x0 drivers/built-in.o 262 | .u_boot_list_2_driver_2_gpio_mxc 263 | 0x00000000178744b0 0x44 drivers/gpio/built-in.o 264 | 0x00000000178744b0 _u_boot_list_2_driver_2_gpio_mxc 265 | .u_boot_list_2_driver_2_gpio_regulator 266 | 0x00000000178744f4 0x44 drivers/power/regulator/built-in.o 267 | 0x00000000178744f4 _u_boot_list_2_driver_2_gpio_regulator 268 | .u_boot_list_2_driver_2_i2c_generic_chip_drv 269 | 0x0000000017874538 0x44 drivers/i2c/built-in.o 270 | 0x0000000017874538 _u_boot_list_2_driver_2_i2c_generic_chip_drv 271 | .u_boot_list_2_driver_2_i2c_mxc 272 | 0x000000001787457c 0x44 drivers/i2c/built-in.o 273 | 0x000000001787457c _u_boot_list_2_driver_2_i2c_mxc 274 | .u_boot_list_2_driver_2_imx6_pinctrl 275 | 0x00000000178745c0 0x44 drivers/built-in.o 276 | 0x00000000178745c0 _u_boot_list_2_driver_2_imx6_pinctrl 277 | ...... 278 | .u_boot_list_2_driver_3 279 | 0x00000000178748f0 0x0 drivers/built-in.o 280 | ``` 281 | - 即所有driver都会放在.u\_boot\_list\_2\_driver\_1和.u\_boot\_list\_2\_driver\_3的区间中,这个列表也称为driver table 282 | 283 | ### 6.4 获取对应的API 284 | - 先获取driver table,然后遍历获得对应的driver 285 | ``` 286 | // 根据.u_boot_list_2_driver_1的段地址获得driver table的地址 287 | struct driver *drv = 288 | ll_entry_start(struct driver, driver); 289 | 290 | // 获得driver table的长度 291 | const int n_ents = ll_entry_count(struct driver, driver); 292 | 293 | // 根据driver name从driver table中得到相应的driver 294 | struct driver *lists_driver_lookup_name(const char *name) 295 | ``` 296 | 297 | ------ 298 | ## 7. 相关API 299 | ### 7.1 uclass 300 | ``` 301 | // 从gd->uclass_root链表获取对应的uclass 302 | // uclass是从gd->uclass_root链表中获得, 参数有uclass_id 303 | int uclass_get(enum uclass_id key, struct uclass **ucp); 304 | ``` 305 | 306 | ### 7.2 uclass_driver 307 | ``` 308 | // 根据uclass id从uclass_driver table中获取 309 | // uclass_driver是从uclass_driver 中获得,参数是uclass_id 310 | struct uclass_driver *lists_uclass_lookup(enum uclass_id id) 311 | ``` 312 | 313 | ### 7.3 udevice 314 | ``` 315 | #define uclass_foreach_dev(pos, uc) \ 316 | list_for_each_entry(pos, &uc->dev_head, uclass_node) 317 | 318 | #define uclass_foreach_dev_safe(pos, next, uc) \ 319 | list_for_each_entry_safe(pos, next, &uc->dev_head, uclass_node) 320 | 321 | // 通过name获取driver,调用device_bind对udevice初始化,与对应uclass、driver绑定 322 | int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, 323 | const struct driver_info *info, struct udevice **devp) 324 | 325 | // 初始化udevice,与对应uclass、driver绑定 326 | int device_bind(struct udevice *parent, const struct driver *drv, 327 | const char *name, void *platdata, int of_offset, 328 | struct udevice **devp) 329 | 330 | // 在uclass的设备链表中绑定新的udevice 331 | int uclass_bind_device(struct udevice *dev) 332 | { 333 | uc = dev->uclass; 334 | list_add_tail(&dev->uclass_node, &uc->dev_head); 335 | } 336 | 337 | // 通过索引从uclass中获取udevice,注意,在获取的过程中就会对设备进行probe 338 | int uclass_get_device(enum uclass_id id, int index, struct udevice **devp); 339 | // 通过设备名从uclass中获取udevice 340 | int uclass_get_device_by_name(enum uclass_id id, const char *name, 341 | struct udevice **devp); 342 | int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp); 343 | int uclass_get_device_by_of_offset(enum uclass_id id, int node, 344 | struct udevice **devp); 345 | ...... 346 | int uclass_resolve_seq(struct udevice *dev); 347 | ``` 348 | 349 | ### 7.4 driver 350 | ``` 351 | // 根据name从driver table中获取 352 | struct driver *lists_driver_lookup_name(const char *name) 353 | ``` 354 | 355 | 356 | 357 | -------------------------------------------------------------------------------- /第4章-U-boot驱动模型之三流程.md: -------------------------------------------------------------------------------- 1 | # 流程 2 | // 根据驱动跑一遍 3 | ------ 4 | ## 1. DM的初始化 5 | ### 1.1 初始化DM 6 | - 创建根设备root的udevice,放在gd->dm_root中 7 | > 根设备其实是一个虚拟设备,主要为其他设备提供一个挂载点 8 | 9 | - 初始化uclass链表**gd->uclass_root** 10 | ``` 11 | #define DM_UCLASS_ROOT_NON_CONST (gd->uclass_root) 12 | INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); // 初始化uclass 13 | 14 | #define DM_ROOT_NON_CONST (gd->dm_root) dm_root是struct udevice*类型 15 | 16 | ``` 17 | 18 | ### 1.2 udevice和uclass的解析 19 | 1. 创建udevice和uclass 20 | 2. 绑定udevice和uclass 21 | 3. 绑定udevice和driver 22 | 4. 绑定uclass和uclass_driver 23 | 5. 调用部分的driver 24 | 25 | ``` 26 | ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); // dm的根udevice 27 | 28 | // 29 | int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, 30 | const struct driver_info *info, struct udevice **devp) 31 | 32 | /* This is the root driver - all drivers are children of this */ 33 | U_BOOT_DRIVER(root_driver) = { 34 | .name = "root_driver", 35 | .id = UCLASS_ROOT, 36 | .priv_auto_alloc_size = sizeof(struct root_priv), 37 | }; 38 | 39 | /* This is the root uclass */ 40 | UCLASS_DRIVER(root) = { 41 | .name = "root", 42 | .id = UCLASS_ROOT, 43 | }; 44 | 45 | ``` 46 | ------ 47 | ## 2. 入口说明 48 | > dts节点中的**u-boot,dm-pre-reloc**属性,当设置了这个属性时,则表示这个设备在relocate之前就需要使用 49 | 50 | ``` 51 | // 只对带有“u-boot,dm-pre-reloc”属性的节点进行解析,情况少 52 | dm_init_and_scan(true); 53 | 54 | // 对所有节点进行解析,重点说明 55 | dm_init_and_scan(false); 56 | ``` 57 | 58 | ### 2.1 dm\_init\_and\_scan说明 59 | ![initf_dm](./images/initf_dm.png) 60 | 61 | ``` 62 | // driver/core/root.c 63 | int dm_init_and_scan(bool pre_reloc_only) 64 | { 65 | ret = dm_init(); // DM的初始化 66 | 67 | ret = dm_scan_platdata(pre_reloc_only); // 从平台设备中解析udevice和uclass 68 | 69 | if (CONFIG_IS_ENABLED(OF_CONTROL)) { 70 | ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only); // 从dtb中解析udevice和uclass 71 | } 72 | 73 | ret = dm_scan_other(pre_reloc_only); 74 | } 75 | ``` 76 | 77 | ### 2.2 dm_init 78 | - 创建根设备root的udevice,放在gd->dm_root中, 即创建设备链表头 79 | - 初始化uclass链表gd->uclass_root, 即创建uclass链表头 80 | 81 | - **driver数组,从u-boot.map中查找** 82 | ``` 83 | /** 84 | * ll_entry_start() - Point to first entry of linker-generated array 85 | * @_type: Data type of the entry 86 | * @_list: Name of the list in which this entry is placed 87 | * 88 | * This function returns (_type *) pointer to the very first entry of a 89 | * linker-generated array placed into subsection of .u_boot_list section 90 | * specified by _list argument. 91 | * 92 | * Since this macro defines an array start symbol, its leftmost index 93 | * must be 2 and its rightmost index must be 1. 94 | * 95 | * Example: 96 | * struct my_sub_cmd *msc = ll_entry_start(struct my_sub_cmd, cmd_sub); 97 | */ 98 | #define ll_entry_start(_type, _list) 99 | ({ static char start[0] __aligned(4) __attribute__((unused, section(".u_boot_list_2_driver_1"))); (struct driver *)&start;}) 100 | 101 | ll_entry_start(struct driver, driver) 102 | 103 | ``` 104 | 105 | ![dm_init](./images/dm_init.png) 106 | 107 | ``` 108 | // driver/core/root.c 109 | #define DM_ROOT_NON_CONST (((gd_t *)gd)->dm_root) // 宏定义根设备指针gd->dm_root 110 | #define DM_UCLASS_ROOT_NON_CONST (((gd_t *)gd)->uclass_root) // 宏定义gd->uclass_root,uclass的链表 111 | 112 | // root_info就实现了一个.name属性,其他为默认值 113 | static const struct driver_info root_info = { 114 | .name = "root_driver", 115 | }; 116 | 117 | int dm_init(void) 118 | { 119 | // 初始化uclass链表 120 | INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); 121 | 122 | // DM_ROOT_NON_CONST是指根设备udevice,root_info是表示根设备的设备信息 123 | // 查找和设备信息匹配的driver,创建对应的udevice,和对应uclass绑定,并把udevice放在DM_ROOT_NON_CONST链表中 124 | ret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); 125 | 126 | // 对根设备执行probe操作 127 | ret = device_probe(DM_ROOT_NON_CONST); 128 | } 129 | 130 | // 1. 根据driver的name找到driver,其中名字为"root_driver" 131 | int device_bind_by_name(struct udevice *parent(NULL), bool pre_reloc_only(false), 132 | const struct driver_info *info("root_info"), struct udevice **devp(gd->uclass_root)) 133 | { 134 | drv = lists_driver_lookup_name(info->name); 135 | return device_bind_common(parent, drv, info->name, 136 | (void *)info->platdata, 0, -1, platdata_size, devp); 137 | } 138 | 139 | // 2. 查询driver列表,找到root_driver,并且返回root_driver的地址 140 | struct driver *lists_driver_lookup_name(const char *name) 141 | { 142 | // 在u-boot.map文件中找到driver列表的起始地址为.u_boot_list_2_driver_1 143 | struct driver *drv = 144 | ll_entry_start(struct driver, driver); 145 | 146 | .u_boot_list_2_driver_1 147 | 0x00000000178743a0 0x0 drivers/built-in.o 148 | .u_boot_list_2_driver_2_asix_eth 149 | 0x00000000178743a0 0x44 drivers/usb/eth/built-in.o 150 | 0x00000000178743a0 _u_boot_list_2_driver_2_asix_eth 151 | .u_boot_list_2_driver_2_fecmxc_gem 152 | 0x00000000178743e4 0x44 drivers/net/built-in.o 153 | 0x00000000178743e4 _u_boot_list_2_driver_2_fecmxc_gem 154 | .u_boot_list_2_driver_2_fixed_regulator 155 | 0x0000000017874428 0x44 drivers/power/regulator/built-in.o 156 | 0x0000000017874428 _u_boot_list_2_driver_2_fixed_regulator 157 | ....................... 158 | 159 | // 找到driver列表的end地址 160 | .u_boot_list_2_driver_3 161 | 162 | 163 | // 计算出一共有对个驱动 164 | const int n_ents = ll_entry_count(struct driver, driver); 165 | struct driver *entry; 166 | 167 | for (entry = drv; entry != drv + n_ents; entry++) { 168 | if (!strcmp(name, entry->name)) 169 | return entry; 170 | } 171 | } 172 | 173 | ********************************** 174 | 175 | /* This is the root driver - all drivers are children of this */ 176 | U_BOOT_DRIVER(root_driver) = { 177 | .name = "root_driver", 178 | .id = UCLASS_ROOT, 0 179 | .priv_auto_alloc_size = sizeof(struct root_priv), 180 | }; 181 | ********************************** 182 | // 3. 给定udevice parent, 驱动地址,驱动名字,平台数据,驱动数据,偏移,偏移大小和udevice,返回一个int类型 183 | static int device_bind_common(struct udevice *parent, const struct driver *drv, 184 | const char *name, void *platdata, 185 | ulong driver_data, int of_offset, 186 | uint of_platdata_size, struct udevice **devp) 187 | { 188 | // 根据driver中保存的id找uclass,如果不存在就创建一个uclass 189 | ret = uclass_get(drv->id, &uc); 190 | ********************************************************* 191 | 192 | int uclass_get(enum uclass_id id, struct uclass **ucp) 193 | { 194 | struct uclass *uc; 195 | 196 | *ucp = NULL; 197 | uc = uclass_find(id); // 开始是空的,返回0 198 | if (!uc) 199 | return uclass_add(id, ucp); 200 | *ucp = uc; 201 | 202 | return 0; 203 | } 204 | 205 | 206 | 207 | /* This is the root uclass */ 208 | UCLASS_DRIVER(root) = { 209 | .name = "root", 210 | .id = UCLASS_ROOT, 211 | }; 212 | 213 | 214 | // 在uclass driver中找对应的uclass driver 215 | // 根据uclass driver中id和driver中的id对比,如果相等,则返回uclass_driver的起始地址 216 | // 申请1个uclass的内存,这个uc的uc_driver就是刚才返回的uclass_driver的起始地址 217 | // INIT_LIST_HEAD(&uc->sibling_node); 初始化这uc的两个节点为单头结点,指向它自己 218 | // INIT_LIST_HEAD(&uc->dev_head); 219 | // 把uc->sibling_node这个节点插入到gd->uclass_root后面 220 | // 直接返回这个uclass 221 | 222 | .u_boot_list_2_uclass_1 223 | 224 | .u_boot_list_2_uclass_2_root 225 | 226 | 227 | .u_boot_list_2_uclass_3 228 | ********************************************************* 229 | 230 | 231 | // 创建一个udevice,和uclass关联 232 | dev->uclass = uc; 233 | ...... 234 | 235 | // 给udevice一个req_seq 236 | fdtdec_get_alias_seq(gd->fdt_blob, 237 | uc->uc_drv->name, of_offset, 238 | &dev->req_seq) 239 | 240 | // 把udevice链接到uclass中,检查是否需要执行uclass_driver的操作 241 | // 把udevice的uclass_node链接到uclass的dev_head上去 242 | ret = uclass_bind_device(dev); 243 | 244 | // driver和udevice进行绑定, 函数指针,由具体设备指定 245 | // 查看driver是否有需要绑定udevice的需求 246 | ret = drv->bind(dev); 247 | } 248 | 249 | // 绑定后激活 250 | int device_probe(struct udevice *dev) 251 | { 252 | // 根据uclass_id和req_seq,得到dev->seq 253 | seq = uclass_resolve_seq(dev); 254 | 255 | // probe前操作 256 | ret = uclass_pre_probe_device(dev); 257 | 258 | // ************************** 259 | // 函数指针,对应实际的udevice的driver 260 | ret = dev->parent->driver->child_pre_probe(dev); 261 | ret = drv->ofdata_to_platdata(dev); 262 | ret = drv->probe(dev); 263 | // ************************** 264 | 265 | // probe后操作 266 | ret = uclass_post_probe_device(dev); 267 | } 268 | ``` 269 | 270 | ### 2.3 从平台设备中解析udevice和uclass——dm\_scan\_platdata 271 | ![dm_scan_platdata](./images/dm_scan_platdata.png) 272 | TBD 273 | 274 | ### 2.4 dm\_scan\_fdt: 从dtb中解析udevice和uclass 275 | 276 | ![dm_scan_fdt](./images/dm_scan_fdt.png) 277 | 278 | ``` 279 | // 以dtb基地址为参数 280 | int dm_scan_fdt(const void *blob, bool pre_reloc_only) 281 | { 282 | return dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only); 283 | } 284 | 285 | // parent=gd->dm_root,表示以root设备作为父设备开始解析 286 | // blob=gd->fdt_blob,对应dtb入口地址 287 | // offset=0,从偏移0的节点开始扫描 288 | int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset, 289 | bool pre_reloc_only) 290 | { 291 | // 遍历每一个dts节点并且调用lists_bind_fdt对其进行解析 292 | // 获得blob设备树的offset偏移下的节点的第一个子节点 293 | for (offset = fdt_first_subnode(blob, offset); 294 | offset > 0; 295 | // 循环查找下一个子节点 296 | offset = fdt_next_subnode(blob, offset)) { 297 | // 节点状态disable的直接忽略 298 | if (!fdtdec_get_is_enabled(blob, offset)) 299 | // 解析绑定这个节点,dm_scan_fdt的核心 300 | err = lists_bind_fdt(parent, blob, offset, NULL); 301 | } 302 | 303 | // driver/core/lists.c 304 | // 通过blob和offset可以获得对应的设备的dts节点 305 | int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, 306 | struct udevice **devp) 307 | { 308 | // 获取driver table地址 309 | struct driver *driver = ll_entry_start(struct driver, driver); 310 | // 获取driver table长度 311 | const int n_ents = ll_entry_count(struct driver, driver); 312 | 313 | // 遍历driver table中的所有driver 314 | for (entry = driver; entry != driver + n_ents; entry++) { 315 | // 判断driver中的compatibile字段和dts节点是否匹配 316 | ret = driver_check_compatible(blob, offset, entry->of_match, 317 | &id); 318 | 319 | // 获取节点名称 320 | name = fdt_get_name(blob, offset, NULL); 321 | 322 | // 找到对应driver,创建对应udevice和uclass进行绑定 323 | ret = device_bind_with_driver_data(parent, entry, name, 324 | id->data, offset, &dev); 325 | } 326 | } 327 | 328 | int device_bind_with_driver_data(struct udevice *parent, 329 | const struct driver *drv, const char *name, 330 | ulong driver_data, int of_offset, 331 | struct udevice **devp) 332 | { 333 | // 同上 334 | return device_bind_common(parent, drv, name, NULL, driver_data, 335 | of_offset, 0, devp); 336 | } 337 | 338 | int device_bind(struct udevice *parent, const struct driver *drv, 339 | const char *name, void *platdata, int of_offset, 340 | struct udevice **devp) 341 | { 342 | // 同上 343 | return device_bind_common(parent, drv, name, platdata, 0, of_offset, 0, 344 | devp); 345 | } 346 | ``` 347 | 348 | > 以上完成dtb的解析,udevice和uclass的创建,以及各个组成部分的绑定 349 | **注意:这里只是绑定,即调用了driver的bind函数,但是设备还没有真正激活,也就是还没有执行设备的probe函数** 350 | 351 | 352 | **TBD,差一个流程图** 353 | 354 | ------- 355 | ## 3. DM的probe 356 | ### 3.1 device_probe函数 357 | 1. 分配设备的私有数据 358 | 2. 对父设备probe,执行probe device之前uclass需要调用的一些函数 359 | 3. 调用driver的ofdata_to_platdata,将dts信息转化为设备的平台数据 360 | 4. 调用driver的probe函数,执行probe device之后uclass需要调用的一些函数 361 | 362 | ![device_probe](./images/device_probe.png) 363 | ``` 364 | // driver/core/device.c 365 | int device_probe(struct udevice *dev) 366 | { 367 | // 同上 368 | } 369 | 370 | 371 | ************************************************************** 372 | ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, dev->req_seq, 373 | false, &dup); // dup是个空设备,要修改的 374 | 375 | 376 | 377 | ************************************************************** 378 | 379 | ``` 380 | 381 | ### 3.2 通过uclass获取一个udevice并且probe 382 | ``` 383 | // driver/core/uclass.c 384 | //通过索引从uclass的设备链表中获取udevice,进行probe 385 | int uclass_get_device(enum uclass_id id, int index, struct udevice **devp) 386 | 387 | //通过设备名从uclass的设备链表中获取udevice,进行probe 388 | int uclass_get_device_by_name(enum uclass_id id, const char *name, 389 | struct udevice **devp) 390 | //通过序号从uclass的设备链表中获取udevice,进行probe 391 | int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp) 392 | 393 | //通过dts节点的偏移从uclass的设备链表中获取udevice,进行probe 394 | int uclass_get_device_by_of_offset(enum uclass_id id, int node, 395 | struct udevice **devp) 396 | 397 | //通过设备的“phandle”属性从uclass的设备链表中获取udevice,进行probe 398 | int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, 399 | const char *name, struct udevice **devp) 400 | 401 | //从uclass的设备链表中获取第一个udevice,进行probe 402 | int uclass_first_device(enum uclass_id id, struct udevice **devp) 403 | 404 | //从uclass的设备链表中获取下一个udevice,进行probe 405 | int uclass_next_device(struct udevice **devp) 406 | ``` 407 | **以上接口主要是获取设备的方法上有所区别,但是probe设备的方法都是一样的** 408 | 409 | ### 3.3 以uclass\_get\_device为例 410 | ``` 411 | int uclass_get_device(enum uclass_id id, int index, struct udevice **devp) 412 | { 413 | //通过索引从uclass的设备链表中获取对应的udevice 414 | ret = uclass_find_device(id, index, &dev); 415 | 416 | // 获得device,进行probe 417 | return uclass_get_device_tail(dev, ret, devp); 418 | } 419 | 420 | int uclass_get_device_tail(struct udevice *dev, int ret, 421 | struct udevice **devp) 422 | { 423 | // probe设备 424 | ret = device_probe(dev); 425 | } 426 | ``` 427 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | --------------------------------------------------------------------------------