关于这一类的问题,不同的平台可能它的代码出处不一样,但大体结构是一样的。
一、Details
输出现下面的一行打印后,就再也没有输出信息,
Uncompressing Linux... done, booting the kernel.//如果没有打开kernel的DEBUG_LL选项,就会hang在这,没有下面的信息,所以一般在调试阶段我们会打开这个项
Error: unrecognized/unsupported machine ID (r1 = 0x1fb694ec).
Available machine support:
ID (hex) NAME
00000d32 Xilinx Zynq Platform
Please check your kernel config and/or bootloader.
arch/arm/boot/compressed/misc.c
ulg
decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,
int arch_id)
{
output_data = (uch *)output_start; /* Points to kernel start */
free_mem_ptr = free_mem_ptr_p;
free_mem_ptr_end= free_mem_ptr_end_p;
__machine_arch_type= arch_id;
arch_decomp_setup();
makecrc();
putstr("Uncompressing Linux...");
gunzip();
putstr(" done, booting the kernel.\n");
return output_ptr;
}
在上面的函数解压完kernel后,就会退出,这时是没有问题的。在退回到arch/arm/boot/compressed/head.s后,最终会执行下面的一段汇编:
call_cache_fn: adrr12, proc_types
#ifdef CONFIG_CPU_CP15
mrc p15, 0, r6, c0, c0@ get processor ID //获取CPU的ID号到ARM寄存器R6里面
#else
ldr r6, =CONFIG_PROCESSOR_ID
#endif
1: ldr r1, [r12, #0] @ get value
ldr r2, [r12, #4]@ get mask
eor r1, r1, r6@ (real ^ match)
tst r1, r2@ & mask
addeq pc, r12, r3@ call cache function
add r12, r12, #4*5
b 1b //不相等则再找下一个。
关于如何修改machine ID在网上很多,在这就不说了。
如果不改uboot,只改kernel,下面的在个文件,只要改动一个就可以了。
include/asm-arm/mach-types.h---------一般是在这个文件中改动,找到相应板子的定义,直接改成uboot转过来的ID值就可以了。在3.X的版本后,它的地址变了是在include/generated/下,不过你也可以用find . -name "mach-types*" -ls来找。
arch/arm/tools/mach-types
arch/arm/kernel/head.s
对于uboot所支持的各种machine_id是定义在arch/arm/include/asm/mach-types.h下。
二、uboot how to pass machine id
在2014.1版本的uboot,引导linux的函数是int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images),在这里会读取之machine ID,同时传给kernel
如:
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
void (*kernel_entry)(void *fdt_addr);
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(void *fdt_addr))images->ep;
debug("## Transferring control to Linux (at address %lx)...\n",
(ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (!fake)
kernel_entry(images->ft_addr);
#else
unsigned long machid = gd->bd->bi_arch_number;//从gd中读取之前在board_init中初始化过的ID,不同的平台,可能初始化位置不一样。
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(int, int, uint))images->ep;//函数指针指向内核映像的入口地址
s = getenv("machid");//这里设备ID号可以从环境变量中获得!如果环境变量中有,就会覆盖之前赋值过的设备ID(最终通过r1传递给内核)。
if (s) {
strict_strtoul(s, 16, &machid);
printf("Using machid 0x%lx from environment\n", machid);
}
debug("## Transferring control to Linux (at address %08lx)" \
"...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
r2 = (unsigned long)images->ft_addr;
else
r2 = gd->bd->bi_boot_params;
if (!fake)
kernel_entry(0, machid, r2);//跳入内核入口地址:r1=0、r1=machid、r2=启动参数指针
#endif
}
对于arm平台,gd定义是在global_data.c中完成的:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9")
这个声明告诉编译器使用寄存器r8来存储gd_t类型的指针gd,即这个定义声明了一个指针,并且指明了它的存储位置。
register表示变量放在机器的寄存器
volatile用于指定变量的值可以由外部过程异步修改
并且这个指针在start_armboot()(board.c)中被初始化:
/* Pointer is writable since we allocated a register for it */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
这样,gd就指向的一个可用的内存地址了。
Note:
如果kernel是用go命令来引导,这时转给kernel的参数格式是不对的,从r1中读取到的machine Id是一个argv的地址,对于这种情况,一般是在head.s中加强制给r1赋值。同时要注意,当ID的值大于0x100时,最好用ldr指令,不要用mov,因为对于ARM指令中的立即数是循环右移偶数位的,具体原因可以看一下ARM指令关于立即数的说明。给一个网址,可以看下外外的说法:http://stackoverflow.com/questions/10261300/invalid-constant-after-fixup
三、kernel how to get ID
waiting .................