关于这一类的问题,不同的平台可能它的代码出处不一样,但大体结构是一样的。
一、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://*.com/questions/10261300/invalid-constant-after-fixup
三、kernel how to get ID
waiting .................