我有一 block 基于 MIPS 的主板 (MediaTek mt7620),带有专有且封闭的引导加载程序 (u-boot),其中很多功能被切断,例如启动独立应用程序
。 (我不允许更改/重新刷新这个引导加载程序,因为很多客户使用这个板,如果我们正在更新引导加载程序并且它失败了,我们将面临很多问题 :)(引导加载程序支持 boot通过 tftp
协议(protocol)) )
我正在尝试将板制造商提供的引导顺序更改为:ROM 引导加载程序 --> 闪存引导加载程序 (u-boot) --> linux 内核为:ROM 引导加载程序 --> 闪存引导加载程序 (u-boot) --> 引导陷阱 --> linux 内核。对于另一个带有 u-boot 的开发板 (mt7621),它支持 booting a standalone application
我能够创建一些 boot trap
u-boot 镜像(独立应用程序)并放置它进入闪存而不是 linux 内核(我将 linux 内核向下移动)。 (这个 boot trap
使用 u-boot 通过跳转表提供的 ranand_read/do_bootm
函数进行一些工作,加载和引导 linux 内核。
正如我提到的,用于基于 mt7620 的开发板的 u-boot 不支持启动独立应用程序
,因此我决定创建一个boot trap
作为u-boot linux 镜像(不是作为 u-boot 独立应用程序镜像)保持 boot trap
的代码几乎相同(我只更改了函数签名:对于独立应用程序 u-boot 镜像 2 个参数被传递,对于 linux u-boot 镜像 - 4;和加载/入口地址)。
这是来自串口的日志, 对于基于 mt7621 的开发板:
## Booting image at bc140000 ...
Image Name: Boot trap
Image Type: MIPS U-Boot Standalone Program (uncompressed)
Data Size: 524 Bytes = 0.5 kB
Load Address: a0600000
Entry Point: a0600000
Verifying Checksum ... OK
OK
boot trap: got a control
boot trap: load a real kernel...
.......................
boot trap: boot a real kernel...
## Booting image at 88000000 ...
Image Name: OpenWrt Linux-3.10.14
Image Type: MIPS Linux Kernel Image (lzma compressed)
Data Size: 1494179 Bytes = 1.4 MB
Load Address: 80001000
Entry Point: 80001000
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
No initrd
## Transferring control to Linux (at address 80001000) ...
## Giving linux memsize in MB, 256
Starting kernel ...
LINUX started...
对于基于 mt7620 的开发板:
## Booting image at bc140000 ...
Image Name: Boot trap
Image Type: MIPS Linux Kernel Image (lzma compressed)
Data Size: 345 Bytes = 0.3 kB
Load Address: 80001000
Entry Point: 80001000
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
No initrd
## Transferring control to Linux (at address 80001000) ...
## Giving linux memsize in MB, 256
Starting kernel ...
如您所见,“引导陷阱”(作为 linux u-boot 镜像)中的代码卡住了,我没有 JTAG
调试器,所以我只能猜测发生了一些异常并且板卡在该异常处理程序中的无限循环中。
我在基于 mt7621 的开发板的 u-boot 源代码中看到(我有它们:))这样的代码行:
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
...
switch (hdr->ih_type) {
case IH_TYPE_STANDALONE:
appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
(*appl)(argc-1, &argv[1]); // <--- pass a control to a standalone app
return 0;
...
switch (hdr->ih_os) {
default:
case IH_OS_LINUX:
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;
...
和下一行:
void do_bootm_linux (cmd_tbl_t * cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong * len_ptr, int verify)
{
...
void (*theKernel) (int, char **, char **, int *);
theKernel = (void (*)(int, char **, char **, int *)) ntohl (hdr->ih_ep);
...
theKernel (linux_argc, linux_argv, linux_env, 0); // <--- pass a control to a linux kernel
}
所以问题是启动 Linux 内核和独立应用程序之间有什么区别?如果我们已经有一个有效的 C 环境,为什么 linux 在获得控制权 (linux/arch/mips/kernel/head.S) 后必须设置一个堆栈指针?为什么 linux 在将控制权传递给自身第一个 C 函数之前必须做这么多工作(在独立应用程序中,我们能够将 C 函数代码直接放入地址,u-boot 将控制权传递给)?
为什么不可能将代码放在 u-boot 将控制权传递到的地址中并让它像我为独立应用程序所做的那样运行?
最佳答案
Linux 内核设计用于在从小型嵌入式设备到大型服务器的数千种不同系统上运行。在内核中包含每种可能的体系结构、电路板或配置的代码是不切实际的,因此内核定义了一组在运行之前必须满足的启动要求。设置适当的环境并满足这些要求通常是引导加载程序的任务。
相比之下,“独立应用程序”通常是为特定目标系统(架构、电路板、配置)编写的,并负责自行设置大部分内容。
这就是 u-boot 以不同方式处理这两种情况的原因。如果您正在引导 Linux 内核,那么 u-boot 会按照内核期望的方式设置所有内容。如果您正在启动一个独立的应用程序,则假定该应用程序将执行所需的任何操作。
关于linux - 启动 linux 内核和独立应用程序有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56380347/