mips的U-Boot分析及移植

news/2024/6/29 11:50:08 标签: cache, cmd, 编译器, alignment, makefile, 工作
 
mips的U-Boot分析及移植
要注意mips具有流水线可见性,所以跟在跳转指令后的下一条指令,在执行跳转到的地方前,都会执行,这个叫分支延迟。但是编译器会隐藏该特性,但可以通过设置”.set noreorder”来禁止编译器重新组织代码顺序。

每个板子都有自己的lds文件。这个主要是用来说明编译生成的指令,及运行过程中用到的数据放置的位置。这个可以参考ld的手册。比如board/dbau1×00/u-boot.lds。

OUTPUT_FORMAT(“elf32-tradbigmips”, “elf32-tradbigmips”, “elf32-tradbigmips”)
/* 这里是生成格式为elf。大端,mips */
OUTPUT_ARCH(mips)
/* 平台为mips */
ENTRY(_start) /* 入口点为_start */
SECTIONS
{
. = 0×00000000;

. = ALIGN(4);
.text : /* 这个是程序存放的地方 */
{
*(.text)
}

. = ALIGN(4); /* 表示以4字节对齐 */
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

. = ALIGN(4);
.data : { *(.data) }

. = .;
_gp = ALIGN(16) + 0×7ff0;

.got : {
__got_start = .; /* 表示该处地址的值给__got_start */
*(.got)
__got_end = .;
}

.sdata : { *(.sdata) }

.u_boot_cmd : {
__u_boot_cmd_start = .;
*(.u_boot_cmd)
__u_boot_cmd_end = .;
}

uboot_end_data = .;
num_got_entries = (__got_end – __got_start) >> 2;

. = ALIGN(4);
.sbss (NOLOAD) : { *(.sbss) }
.bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
uboot_end = .;
}

下面来分析cpu/mips/start.S
首先从_start开始。前面是128个字(不是字节),是留给异常入口点的。
1. 最前面两个分别是硬复位和软复位,这两个都跳到reset处。
2. 下面就是清一些CP0(协处理器0,mips对CPU的控制都是通过它实现的)的一些主要位。
3. 然后是关闭cache
4. 下面这个比较有意思。为什么还非要跳一下呢?这样就可以知道代码的位置,而不是标号值。比如可能在RAM中或ROM中。
bal 1f
nop
.word _gp
1:
lw gp, 0(ra)
5. 这里执行lowlevel_init。这是第一个需要我们自己定义的函数。由于没有初始化堆栈,这里只能用汇编。我们看到在jalr后跟了个nop,这就是分支延迟槽了,在这里什么也没有执行。
6. 下面执行了mips_cache_reset,它会来清理数据和指令的cache,并设置为正确的值。然后就可以打开cache了。
7. 由于我们的内存可能还没有始初化(有些人会有lowlevel_init中初始化,但有的人没有这样做)。但我们使用C函数的话,就需要堆栈,所以需要一 个内存空间。于是这里执行了mips_cache_lock,将cache的地址锁定,就是将cache当内存用了。然后我们将堆栈的地址设定在我们锁定 的cache的最高地址(因为堆栈是向下生长的)。这时我们就可以用C函数了,当然你还用不了malloc,也不可以太多的浪费堆栈。
8. 这里就跑到C的初始化函数中去了--board_init_f。

对于mips,board_init_f在lib_mips/board.c下。在board_init_f()函数中,主要完成了一些功能初始化,和划分RAM。
/* 所以最后RAM中是这样子的
—RAM 0×0000,0000—
…………
— ^ SP ^ —
— boot params — CONFIG_SYS_BOOTPARAMS_LEN bd->bi_boot_params
— Global Data — sizeof(gd_t) gd
— Board Info — sizeof(bd_t) gd->bd = bd
— mallco(+env) — CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE
— uboot code — 16kB
—RAM end —
*/
再看一下都初始化了什么功能。初始化的函数都在init_fnc_t *init_sequence[]里。
init_fnc_t *init_sequence[] = {
board_early_init_f, /* 一些必要,需在之前做的初始化,如想使用需定义CONFIG_BOARD_EARLY_INIT_F */
timer_init, /* 初始化时钟计数,cp0的 */
env_init, /* 环境变量保存在flash中 */
#ifdef CONFIG_INCA_IP
incaip_set_cpuclk, /* 根据cpuclk环境变量设定CPU主频 */
#endif
init_baudrate, /* 根据baudrate环境变量设定gd->baudrate */
serial_init, /* 设定串口速率,需要我们自己写(包括其它serial的) */
console_init_f, /* 设置gd->have_console=1,有CONFIG_SILENT_CONSOLE则查看silent */
display_banner, /* 打印uboot信息 */
checkboard, /* 检测板子,可以在这打印设备信息,需要我们自己写 */
init_func_ram, /* 设置gd->ram_size,initdram需要我们自己写 */
NULL, /* 最后这个空必须留着,检查结束 */
};
最后这个函数调用了relocate_code (addr_sp, id, addr)。注意,这个函数,准确的说不是函数(因为不能返回),是不返回的。

现在我们又回到start.S中了。我们可以看到,这里和C语言传递参数是用a0,a1,a2。relocate_code的工作就是将代码搬移到RAM中执行。这里做的工作是:
1. 移动gp指针
2. 复制代码到RAM中
3. 刷新一下cache
4. 跳到RAM代码当中去(in_ram)

in_ram的主要工作是:更新GOT;清空BSS段;最后跳到board_init_r。我们可以看到board_init_r最后一个参数是在分支延迟槽中赋值的。
这其实这里主要说一下GOTs(global offset tables)这个东东,这是uboot能跳转到不同空间运行的原理。uboot编译时用到了PIC(position-independent code)(也可以说成position-independent executable (PIE))。这个其实是很早之前,在没有MMU的年代引进来的东西。为了在没有MMU时,不同进程也能同时运行,就需要他们的运行地址可以改变。 GOTs用来保存所有的全局变量地址,所以我们只要改变GOTs的值就可以了。gp就是指向GOTs位置的指针。这个功能需要在gcc编译时指定 -fpic。然后就像我们看到的,我们只要改变GOTs里的值,加上地址偏移就可以了。

下面再看一下board_init_r。这里的工作
1. 复制cmd段的信息过来。这里只复制了cmd,name,usage,usage。帮助信息的字符串还在flash中。
2. 然后是初始化malloc功能。注意这里env有malloc的方式分配到了空间,并复制到RAM。
3. 再就是stdio,串口,入口函数,以及全局变最根据env的初始化了。
4. 再接着就是网络的初始化。eth_initialize(gd->bd)。对于mips,如果设了CONFIG_NET_MULTI。我们需要自己写board_eth_init和cpu_eth_init两个函数。 只有前者返回值小于0时,我们才需要执行后者。
5. 最后进入main_loop()。

以太网驱动移植
对于mips,如果设了CONFIG_NET_MULTI。会用到board_eth_init()和cpu_eth_init()两个函数。只有前者返 回值小于0时,才会执行后者。这里要初始化结构体eth_device,然后使用eth_register()注册网络设备。主要要设定 init,halt,send,recv四个函数,及其它一些变量。如支持mii读写命令还需使用miiphy_register()注册mii读写的两 个函数。
当注册完成后,U-Boot不会自动调用init,而是只有当用到网络设备时,才后调用。每次使用网络接口时,是先调用halt之后,才调用init。

当我们使用命令make (型号)_config时,会产生以下效果。
(1) 将include/asm软链接到include/asm-(ARCH)。
(2) 修改include/config.h文件,增加#include <configs/(型号).h>和#include <asm/config.h>。
(3) 在include/config.mk设置变量ARCH,CPU,BOARD,VENDOR。
编译时,Makefile会包含进几个目录的config.mk文件。board/xxx/(xxx/)config.mk在前面已经介绍过了。在 lib_xxx/config.mk中会指定交叉编译器,及增加平台相关的编译参数。在cpu/xxx/config.mk会指定体系结构的相关参数,如 大小端


http://www.niftyadmin.cn/n/1116946.html

相关文章

Ubuntu Server下安装Discuz7.0

1&#xff0c;安装数据库和Web服务器$sudo apt-get install apache2 mysql-server php5 php5-mysql php5-gd phpmyadmin2&#xff0c;安装Zend框架$sudo apt-get install zend-framework3,下载Discuz安装包$cd ~$mkdir discus$wget http://download2.comsenz.com/Discuz/7.0.0/…

redis主从 哨兵

entinel是redis高可用的解决方案&#xff0c;sentinel系统&#xff08;N个sentinel实例&#xff0c;N > 1&#xff09;可以监视一个或者多个redis master服务&#xff0c;以及这些master服务的所有从服务&#xff1b;当某个master服务下线时&#xff0c;自动将该master下的某…

ios命令行打IPA包(shenzhen)

利用github上一个开源项目&#xff1a;https://github.com/nomad/shenzhen可以在命令行为ios项目进行打包并发布。 具体安装步骤如下&#xff1a; gem install shenzhen 如果安装过程出现错误有可能是ruby的源找不到&#xff0c;可以到http://ruby.taobao.org/改变ruby源 如果还…

点击除本身之外的任何地方 都能使该div隐藏

源文件&#xff1a;http://www.jb51.net/article/43372.htm 思路一 第一种思路分两步 第一步&#xff1a;对document的click事件绑定事件处理程序&#xff0c;使其隐藏该div 第二步&#xff1a;对div的click事件绑定事件处理程序&#xff0c;阻止事件冒泡&#xff0c;防止其冒泡…

crontab设置每五秒执行一次程序的方法

事实上知道crontab设置的最小时间为每分钟&#xff0c;那么要实现以秒为单位进行定时执行任务&#xff0c;该怎么处理&#xff0c;实例说明&#xff0c;以每五秒执行一次任务为说明&#xff1a; 方法一&#xff1a; */1 * * * * /homemytest/test.sh */1 * * * * sleep 5 &am…

数据库系统基本组成

1&#xff09;数据库-----数据库是用于存储数据的存储空间。 2&#xff09;数据库管理系统-----DBMS 主要是进行数据的创建&#xff08;Create&#xff09;、读取&#xff08;Read&#xff09;、更新&#xff08;Update&#xff09; 以及删除&#xff08;Delete&#xff09;等…

about system (pause) in cpp

Which is best way to pause the console in C programs? using cin.get()or using system("pause")or using C functions like getch() or getchar()?Is it true that use of system("pause") leads to non portable code and cant work in UNIX? Is c…

java简单的单文件服务器

该服务器的功能&#xff1a;无论接受到何种请求&#xff0c;都始终发送同一个文件。这个服务器命名为SingleFileHTTPServer&#xff0c;文件名、本地端口和内容编码方式从命令行读取。如果缺省端口&#xff0c;则假定端口号为80。如果缺省编码方式&#xff0c;则假定为ASCII。 …