barebox学习笔记之一 -- 系统架构

引导程序的最终目的是将内核引导起来,因此不要忘记关注一下kernel是如何引导起来的!!!
void 
start_barebox (void)
+-- for(initcall = __barebox_initcalls_start; initcall < __barebox_initcalls_end; 
        initcall++){
        result = (*initcall)();//调用各种初始化函数
    } 
+-- display_meminfo();         //显示内存信息
+-- if (envfs_load(default_environment_path, "/env")) {
        envfs_load("/dev/defaultenv", "/env");      //解析并加载环境变量
    }
+-- if (!stat("/env/bin/init", &s)) {
        run_command("source /env/bin/init", 0); //运行初始化脚本
    }
+-- for(;;)
        run_shell();          //循环处理各种命令

//在fs_init阶段,会将ramfs挂到"/"目录,而后devf会被挂到"/dev"目录
static int mount_root(void)
{
        mount("none", "ramfs", "/");
        mkdir("/dev", 0);
        mount("none", "devfs", "/dev");
        return 0;
}
fs_initcall(mount_root);
因此诸多初始化函数调用之后,就可以使用相关文件系统,例如envfs_load().

static int armlinux_register_image_handler(void)
{
        register_image_handler(&barebox_handler); //barebox_handler...
        register_image_handler(&uimage_handler);  //uimage_handler...
        register_image_handler(&rawimage_handler);//rawimage_handler...
        register_image_handler(&zimage_handler);  //zimage_handler...
        if (IS_BUILTIN(CONFIG_CMD_BOOTM_AIMAGE)) {
                register_image_handler(&aimage_handler);
                binfmt_register(&binfmt_aimage_hook);
        }
        binfmt_register(&binfmt_arm_zimage_hook);
        binfmt_register(&binfmt_barebox_hook);

        return 0;
}
late_initcall(armlinux_register_image_handler);



int register_image_handler(struct image_handler *handler)
{
        list_add_tail(&handler->list, &handler_list);//handler_list...
        return 0;
}

//程序会在handler_list中查找合适的handler
static struct image_handler *
bootm_find_handler(enum filetype filetype, struct image_data *data)
{
        struct image_handler *handler;

        list_for_each_entry(handler, &handler_list, list) {
                if (filetype != filetype_uimage &&
                    handler->filetype == filetype)
                        return handler;
                if (filetype == filetype_uimage &&
                    handler->ih_os == data->os->header.ih_os)
                        return handler;
        }

        return NULL;
}

//bootm_find_handler()由do_bootm()调用:
static int do_bootm(int argc, char *argv[])
+-- handler = bootm_find_handler(os_type, &data);

//do_bootm在此处被引用:
BAREBOX_CMD_START(bootm)
        .cmd            = do_bootm,
        .usage          = "boot an application image",
        BAREBOX_CMD_HELP(cmd_bootm_help)
BAREBOX_CMD_END

//两个宏定义如下:
#define BAREBOX_CMD_START(_name)                                                        \
extern const struct command __barebox_cmd_##_name;                                      \
const struct command __barebox_cmd_##_name                                              \
        __attribute__ ((unused,section (".barebox_cmd_" __stringify(_name)))) = {       \
        .name           = #_name,

#define BAREBOX_CMD_END                                 \
};

//由此上面的定义展开为:
extern const struct command __barebox_cmd_bootm;                                      \
const struct command __barebox_cmd_bootm                                              \
        __attribute__ ((unused,section (".barebox_cmd_" __stringify(bootm)))) = {       \
        .name           = "bootm",
        .cmd            = do_bootm,
        .usage          = "boot an application image",
        BAREBOX_CMD_HELP(cmd_bootm_help)
};

//由定义可以看出,该结构被分配在名为".barebox_cmd_"的段里面.

@/arch/arm/lib/barebox.lds.S
        ...
        . = .;
        __barebox_cmd_start = .;
        .barebox_cmd : { BAREBOX_CMDS }
        __barebox_cmd_end = .;
        ...
//因此,代码中可以通过__barebox_cmd_start和__barebox_cmd_end两个变量来找到".barebox_cmd"
//这个section的头和尾.

//[__barebox_cmd_start,__barebox_cmd_end]之间的每个command都会被注册到一个名为
//command_list的链表中去.
static int init_command_list(void)
{
        struct command *cmdtp;
        for (cmdtp = &__barebox_cmd_start; cmdtp != &__barebox_cmd_end;
             cmdtp++)
                register_command(cmdtp);
                //+-- list_add_sort(&cmd->list, &command_list, compare);
        return 0;
}
late_initcall(init_command_list);


int execute_command(int argc, char **argv)
+-- getopt_context_store(&gc);
    //从command_list链表中找到名为argv[0]指向的字符串的command
+-- cmdtp = find_cmd(argv[0]);
    //调用这个command中的cmd()函数
+-- ret = cmdtp->cmd(argc, argv); 
+-- getopt_context_restore(&gc);

于是产生了两个问题:
1. execute_command()被调用的途径是怎么样的?
2. do_bootm()是怎样将内核引导起来的?
先看do_bootm(),在进入do_bootm之前,先看以下image_data这个数据结构:
//struct image_data用来维护os image的相关信息,比如:os的地址,文件名等等
struct image_data {
        //simplest case. barebox has already loaded the os here 
        struct resource *os_res;

        //if os is an uImage this will be provided
        struct uimage_handle *os;
        int os_num;

        //otherwise only the filename will be provided
        char *os_file;

        //The address the user wants to load the os image to.
        //May be UIMAGE_INVALID_ADDRESS to indicate that the
        //user has not specified any address. In this case the
        //handler may choose a suitable address
        unsigned long os_address;

        //entry point to the os. relative to the start of the image
        unsigned long os_entry;

        //if initrd is already loaded this resource will be !NULL
        struct resource *initrd_res;

        //if initrd is an uImage this will be provided
        struct uimage_handle *initrd;
        int initrd_num;

        //otherwise only the filename will be provided
        char *initrd_file;

        unsigned long initrd_address;
        struct fdt_header *oftree;

        int verify;
        int verbose;
};
下面进入do_bootm():
static int do_bootm(int argc, char *argv[])
{
	int opt;
	struct image_handler *handler;
	struct image_data data;
	int ret = 1;
	enum filetype os_type, initrd_type = filetype_unknown;
	const char *oftree = NULL, *initrd_file = NULL, *os_file = NULL;
	int fallback = 0;

	//创建一个struct image_data实例.
	memset(&data, 0, sizeof(struct image_data));

	//初始化对应成员
	data.initrd_address = UIMAGE_SOME_ADDRESS;
	data.os_address = UIMAGE_SOME_ADDRESS;
	data.verify = 0;
	data.verbose = 0;

	oftree = getenv("global.bootm.oftree");
	//从环境变量拿到文件名关联的字符串
	os_file = getenv("global.bootm.image");
	if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD))
		//从环境变量拿到ramdisk文件名关联的字符串
		initrd_file = getenv("global.bootm.initrd");

	//循环读取bootm options,并初始化image_data中相应的域.
	while ((opt = getopt(argc, argv, BOOTM_OPTS)) > 0) {
		switch(opt) {
		case 'c':
			data.verify = 1;
			break;
#ifdef CONFIG_CMD_BOOTM_INITRD
		case 'L':
			data.initrd_address = simple_strtoul(optarg, NULL, 0);
			break;
		case 'r':
			initrd_file = optarg;
			break;
#endif
		case 'a':
			data.os_address = simple_strtoul(optarg, NULL, 0);
			break;
		case 'e':
			data.os_entry = simple_strtoul(optarg, NULL, 0);
			break;
		case 'v':
			data.verbose++;
			break;
		case 'o':
			oftree = optarg;
			break;
		case 'f':
			fallback = 1;
			break;
		default:
			break;
		}
	}

	if (optind != argc)
		os_file = argv[optind];

	if (!os_file || !*os_file) {
		printf("no boot image given\n");
		goto err_out;
	}

	if (initrd_file && !*initrd_file)
		initrd_file = NULL;

	if (oftree && !*oftree)
		oftree = NULL;

	//data.os_file就是os_file去掉@之后的前半部分字符串
	//而data.os_num得到的则是@之后的字符串
	data.os_file = bootm_image_name_and_no(os_file, &data.os_num);
	//打开data.os_file指定的文件,并根据钱512Byte的内容判断os镜像文件的类型
	os_type = file_name_detect_type(data.os_file);
	if ((int)os_type < 0) {
		printf("could not open %s: %s\n", data.os_file,
				strerror(-os_type));
		goto err_out;
	}

	if (!fallback && os_type == filetype_unknown) {
		printf("Unknown OS filetype (try -f)\n");
		goto err_out;
	}

	//如果是uimage,则将其加载到sdram
	if (os_type == filetype_uimage) {
		//将镜像文件加载到sdram
		ret = bootm_open_os_uimage(&data);
		if (ret) {
			printf("loading os image failed with %s\n",
					strerror(-ret));
			goto err_out;
		}
	}
	//如果使能了ramdisk,则获取ramdisk的镜像文件,检测其类型,并
	if (IS_ENABLED(CONFIG_CMD_BOOTM_INITRD) && initrd_file) {
		data.initrd_file = bootm_image_name_and_no(initrd_file, &data.initrd_num);

		initrd_type = file_name_detect_type(data.initrd_file);
		if ((int)initrd_type < 0) {
			printf("could not open %s: %s\n", data.initrd_file,
					strerror(-initrd_type));
			goto err_out;
		}
		if (initrd_type == filetype_uimage) {
			//将image相关的信息保存在data->initrd->header中
			ret = bootm_open_initrd_uimage(&data);
			if (ret) {
				printf("loading initrd failed with %s\n",
						strerror(-ret));
				goto err_out;
			}
		}
	}
	//打印详细信息
	if (bootm_verbose(&data)) {
		printf("\nLoading OS %s '%s'", file_type_to_string(os_type),
				data.os_file);
		if (os_type == filetype_uimage &&
				data.os->header.ih_type == IH_TYPE_MULTI)
			printf(", multifile image %d", data.os_num);
		printf("\n");
		if (data.os_res)
			printf("OS image is at 0x%08x-0x%08x\n",
					data.os_res->start,
					data.os_res->end);
		else
			printf("OS image not yet relocated\n");

		if (data.initrd_file) {
			printf("Loading initrd %s '%s'",
					file_type_to_string(initrd_type),
					data.initrd_file);
			if (initrd_type == filetype_uimage &&
					data.initrd->header.ih_type == IH_TYPE_MULTI)
				printf(", multifile image %d", data.initrd_num);
			printf("\n");
			if (data.initrd_res)
				printf("initrd is at 0x%08x-0x%08x\n",
					data.initrd_res->start,
					data.initrd_res->end);
			else
				printf("initrd image not yet relocated\n");
		}
	}

#ifdef CONFIG_OFTREE
	if (oftree) {
		int oftree_num;

		oftree = bootm_image_name_and_no(oftree, &oftree_num);

		ret = bootm_open_oftree(&data, oftree, oftree_num);
		if (ret)
			goto err_out;
	}
#endif
	if (data.os_address == UIMAGE_SOME_ADDRESS)
		data.os_address = UIMAGE_INVALID_ADDRESS;
	if (data.initrd_address == UIMAGE_SOME_ADDRESS)
		data.initrd_address = UIMAGE_INVALID_ADDRESS;

	//在handler_list中找到os_type对应的handeler
	handler = bootm_find_handler(os_type, &data); 
	if (!handler) {
		printf("no image handler found for image type %s\n",
			file_type_to_string(os_type));
		if (os_type == filetype_uimage)
			printf("and os type: %d\n", data.os->header.ih_os);
		goto err_out;
	}

	if (bootm_verbose(&data))
		printf("Passing control to %s handler\n", handler->name);

	//利用找到的handler去引导该os(initrd和os image已经被加载到内存,对应信息已经保存在data中)
	ret = handler->bootm(&data);

	printf("handler failed with %s\n", strerror(-ret));

err_out:
	free(data.initrd_file);
	free(data.os_file);
	if (data.os_res)
		release_sdram_region(data.os_res);
	if (data.initrd_res)
		release_sdram_region(data.initrd_res);
	if (data.initrd && data.initrd != data.os)
		uimage_close(data.initrd);
	if (data.os)
		uimage_close(data.os);
	return 1;
}

因此,下面就去看看handler->bootm().
@arch/arm/lib/bootm.c
static struct image_handler zimage_handler = {
        .name = "ARM zImage",
        .bootm = do_bootz_linux,
        .filetype = filetype_arm_zimage,
};


static int do_bootz_linux(struct image_data *data)
{
	int fd, ret, swap = 0;
	struct zimage_header __header, *header;
	void *zimage;
	u32 end;
	unsigned long load_address = data->os_address;
        
        //如果data->os_address还未指定,则重新获取os将要加载的目标地址
	if (load_address == UIMAGE_INVALID_ADDRESS) {
		struct memory_bank *bank = list_first_entry(&memory_banks,
                                                    struct memory_bank, list);
		data->os_address = bank->start + SZ_8M;
		load_address = data->os_address;
		if (bootm_verbose(data))
			printf("no os load address, defaulting to 0x%08lx\n",
				load_address);
	}
        //打开os镜像文件 
	fd = open(data->os_file, O_RDONLY);
	if (fd < 0) {
		perror("open");
		return 1;
	}
        
        //将os镜像文件的header读取到__header中来
	header = &__header;
	ret = read(fd, header, sizeof(*header));
	if (ret < sizeof(*header)) {
		printf("could not read %s\n", data->os_file);
		goto err_out;
	}

        //根据MAGIC WORD判断字节序是否符合当前处理器
	switch (header->magic) {
	case swab32(ZIMAGE_MAGIC):
		swap = 1;
		/* fall through */
	case ZIMAGE_MAGIC:
		break;
	default:
		printf("invalid magic 0x%08x\n", header->magic);
		ret = -EINVAL;
		goto err_out;
	}

	end = header->end;

	if (swap)
		end = swab32(end);

        //申请sdram空间资源,并将load_address和end分别作为起始地址和size赋值给resource结构
	data->os_res = request_sdram_region("zimage", load_address, end);
	if (!data->os_res) {
		pr_err("bootm/zImage: failed to request memory at 0x%lx to 0x%lx (%d).\n",
		       load_address, load_address + end, end);
		ret = -ENOMEM;
		goto err_out;
	}

        //从申请的resource结构中拿到起始地址,其实就是load_address
	zimage = (void *)data->os_res->start;

        //将header拷贝到该起始地址为起点的内存中
	memcpy(zimage, header, sizeof(*header));

        //从文件中读出os镜像的其余部分
	ret = read_full(fd, zimage + sizeof(*header), end - sizeof(*header));
	if (ret < 0)
		goto err_out;
	if (ret < end - sizeof(*header)) {
		printf("premature end of image\n");
		ret = -EIO;
		goto err_out;
	}
        
        //字节序不一致的话改变一下字节序 
	if (swap) {
		void *ptr;
		for (ptr = zimage; ptr < zimage + end; ptr += 4)
			*(u32 *)ptr = swab32(*(u32 *)ptr);
	}
       
        //这个oftree相关的暂时没看懂
	ret = do_bootz_linux_fdt(fd, data);
	if (ret && ret != -ENXIO)
		return ret;
        //从这里开始引导内核,如果顺利的话就不会再返回了.
	return __do_bootm_linux(data, swap);

err_out:
	close(fd);

	return ret;
}


static int do_bootz_linux(struct image_data *data)
+-- __do_bootm_linux(data, swap);
    +-- kernel = data->os_res->start + data->os_entry;
    +-- start_linux((void *)kernel, swap, initrd_start, initrd_size, data->oftree);
        +-- void (*kernel)(int zero, int arch, void *params) = adr;
        +-- kernel(0, architecture, params);//将cpu控制权转移给kernel image


void start_linux(void *adr, int swap, unsigned long initrd_address,
                unsigned long initrd_size, void *oftree)
{
        void (*kernel)(int zero, int arch, void *params) = adr;
        void *params = NULL;
        int architecture;
        
        //传给kernel的启动参数,供kernel startup的时候使用
        if (oftree) {
                printf("booting Linux kernel with devicetree\n");
                params = oftree;
        } else {
                setup_tags(initrd_address, initrd_size, swap);
                params = armlinux_bootparams; //armlinux_bootparams...
        }

        //get the devices into a clean state
        shutdown_barebox();
        if (swap) {
                u32 reg;
                __asm__ __volatile__("mrc p15, 0, %0, c1, c0" : "=r" (reg));
                reg ^= CR_B; /* swap big-endian flag */
                __asm__ __volatile__("mcr p15, 0, %0, c1, c0" :: "r" (reg));
        }

        //拿到ARM的架构信息
        architecture = armlinux_get_architecture();

        //转交CPU控制权给kernel
#ifdef CONFIG_THUMB2_BAREBOX
        __asm__ __volatile__ (
                "mov r0, #0\n"
                "mov r1, %0\n"
                "mov r2, %1\n"
                "bx %2\n"
                :
                : "r" (architecture), "r" (params), "r" (kernel)
                : "r0", "r1", "r2"
        );
#else
        kernel(0, architecture, params);
#endif
}

这里又产生了一个新的问题:
architecture和armlinux_bootparams是怎么来的?
//下面这两个函数设置了architecture和armlinux_bootparams
void armlinux_set_architecture(int architecture)
{
#ifdef CONFIG_ENVIRONMENT_VARIABLES
        export_env_ull("armlinux_architecture", architecture);
#else
        armlinux_architecture = architecture;
#endif
}

void armlinux_set_bootparams(void *params)
{
        armlinux_bootparams = params;
}

device_initcall(a9m2410_devices_init);
+-- static int a9m2410_devices_init(void)
    +-- armlinux_set_bootparams((void*)S3C_SDRAM_BASE + 0x100);
    +-- armlinux_set_architecture(MACH_TYPE_A9M2410);
//结合文章开始的代码可以发现,设备的architecture是在设备初始化的时候写死的(例如,这里直接写成MACH_TYPE_A9M2410),
//而传给内核的参数则是放在sdram的固定地址的(例如,这里是放在S3C_SDRAM_BASE+0x100开始的SDRAM中的).
barebox编译系统分析
//@顶层Makefile
# Build barebox
# ---------------------------------------------------------------------------
# barebox is built from the objects selected by $(barebox-init) and
# $(barebox-main). Most are built-in.o files from top-level directories
# in the kernel tree, others are specified in arch/$(ARCH)Makefile.
# Ordering when linking is important, and $(barebox-init) must be first.
#
# barebox由$(barebox-init)和$(barebox-main)选中的对象构建而来.这些对象多数是来自源码树
# 中顶层目录的built-in.o文件,其他的对象由arch/$(ARCH)/Makefile指定.
# 链接的顺序是重要的,并且$(barebox-init)必须是第一个.
#
# FIXME: This picture is wrong for barebox. We have no init, driver, mm
# FIXME: 这幅图对于barebox是错误的.我们没有init,driver,mm 
#
# barebox
#   ^
#   |
#   +-< $(barebox-init)
#   |   +--< init/version.o + more
#   |
#   +--< $(barebox-main)
#   |    +--< driver/built-in.o mm/built-in.o + more
#   |
#   +-< kallsyms.o (see description in CONFIG_KALLSYMS section)
#
# barebox version cannot be updated during normal
# descending-into-subdirs phase since we do not yet know if we need to
# update barebox.
# barebox 版本不能够在进入底层目录的时候修改因为我们还不知道是否有必要修改barebox
#
# System.map is generated to document addresses of all kernel symbols

barebox.bin: barebox FORCE
        $(call if_changed,objcopy)
        $(call cmd,check_file_size,$(CONFIG_BAREBOX_MAX_IMAGE_SIZE))

# barebox image
barebox: $(barebox-lds) $(barebox-head) $(barebox-common) $(kallsyms.o) FORCE
        $(call barebox-modpost)
        $(call if_changed_rule,barebox__)
        $(Q)rm -f .old_version

//cmd_barebox__的定义如下:
cmd_barebox__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_barebox) -o $@ \
      -T $(barebox-lds) $(barebox-head)                         \
      --start-group $(barebox-common) --end-group                  \
      $(filter-out $(barebox-lds) $(barebox-common) FORCE ,$^)


//根据前面主Makefile中的注释:
# barebox由$(barebox-init)和$(barebox-main)选中的对象构建而来.这些对象多数是来自源码树
# 中顶层目录的built-in.o文件,其他的对象由arch/$(ARCH)/Makefile指定.
//barebox应该主要由两部分构建而来:
//一部分是源码树顶层目录中的built-in.o文件; 另一部分就是由arch/$(ARCH)/Makefile指定的built-in.o文件.

//barebox-head的定义没有找到,估计命令行如果没定义的话应该为空(当然,也可能是用脚本之类指定的,以后留意找一下).
[zrlean@e4]$grep -irn "barebox-head" ./*
./Makefile:520:      -T $(barebox-lds) $(barebox-head)                         \
./Makefile:707:barebox: $(barebox-lds) $(barebox-head) $(barebox-common) $(kallsyms.o) FORCE
./Makefile:717:$(sort $(barebox-head) $(barebox-common) ) $(barebox-lds): $(barebox-dirs) ;

//但是barebox-common的定义是可以找到的:
@Makefile
common-y := common/ drivers/ commands/ lib/ crypto/ net/ fs/
include $(srctree)/arch/$(ARCH)/Makefile
+-- BOARD := arch/arm/boards/$(board-y)/
+-- MACH := arch/arm/mach-$(machine-y)/
+-- common-y += $(BOARD) $(MACH)
+-- common-y += arch/arm/lib/ arch/arm/cpu/
+-- lds-y   := arch/arm/lib/barebox.lds
common-y := $(patsubst %/, %/built-in.o, $(common-y))
barebox-common := $(common-y)
barebox-all    := $(barebox-common)
barebox-lds    := $(lds-y)

//由此 barebox-common实际上就是
//所谓的"顶层目录中的built-in.o" 
common/built-in.o drivers/built-in.o commands/built-in.o lib/built-in.o crypto/built-in.o net/built-in.o fs/built-in.o
//加上"由arch/$(ARCH)/Makefile指定的built-in.o" 
arch/arm/boards/$(board-y)/built-in.o arch/arm/mach-$(machine-y)/built-in.o
//./arch/arm/lib/Makefile:23:extra-y += barebox.lds
@arch/arm/lib/barebox.lds.S
#include <asm-generic/barebox.lds.h>

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(start)
SECTIONS
{
        . = TEXT_BASE;

        PRE_IMAGE

        . = ALIGN(4);
        .text      :
        {
                _stext = .;
                _text = .;
                *(.text_entry*)        /*这里应该是整个代码的入口点*/
                __ll_return = .;
                *(.text_ll_return*)
                __bare_init_start = .;
                *(.text_bare_init*)
                __bare_init_end = .;
                __exceptions_start = .;
                KEEP(*(.text_exceptions*))
                __exceptions_stop = .;
                *(.text*)
        }
        BAREBOX_BARE_INIT_SIZE

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

#ifdef CONFIG_ARM_UNWIND
        /*
         * Stack unwinding tables
         */
        . = ALIGN(8);
        .ARM.unwind_idx : {
                __start_unwind_idx = .;
                *(.ARM.exidx*)
                __stop_unwind_idx = .;
        }
        .ARM.unwind_tab : {
                __start_unwind_tab = .;
                *(.ARM.extab*)
                __stop_unwind_tab = .;
        }
#endif
        _etext = .;             /* End of text and rodata section */

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

        . = .;
        __barebox_cmd_start = .;
        .barebox_cmd : { BAREBOX_CMDS }
        __barebox_cmd_end = .;

        __barebox_magicvar_start = .;
        .barebox_magicvar : { BAREBOX_MAGICVARS }
        __barebox_magicvar_end = .;

        __barebox_initcalls_start = .;
        .barebox_initcalls : { INITCALLS }
        __barebox_initcalls_end = .;

        __usymtab_start = .;
        __usymtab : { BAREBOX_SYMS }
        __usymtab_end = .;

        . = ALIGN(4);
        __bss_start = .;
        .bss : { *(.bss*) }
        __bss_stop = .;
        _end = .;
        _barebox_image_size = __bss_start - TEXT_BASE;
}


@arch/arm/Makefile
TEXT_BASE = $(CONFIG_TEXT_BASE)

//CONFIG_TEXT_BASE是在make menuconfig的时候写入的.

#[zrlean@e4]$grep -irn ".text_entry" ./*
#...
#./arch/arm/cpu/start.c:31:void __naked __section(.text_entry) start(void)
#./arch/arm/lib/barebox.lds.S:41:                *(.text_entry*)
#...

@./arch/arm/cpu/start.c
void __naked __section(.text_entry) start(void)
{
        barebox_arm_head();
}

@arch/arm/include/asm/barebox-arm-head.h
static inline void barebox_arm_head(void)
{
        __asm__ __volatile__ (
#ifdef CONFIG_THUMB2_BAREBOX
                ".arm\n"
                "adr r9, 1f + 1\n"
                "bx r9\n"
                ".thumb\n"
                "1:\n"
                "bl reset\n"     //这里跳转到reset()
                ".rept 10\n"
                "1: b 1b\n"
                ".endr\n"
#else
                "b reset\n"      //这里跳转到reset()
                "1: b 1b\n"
                "1: b 1b\n"
                "1: b 1b\n"
                "1: b 1b\n"
                "1: b 1b\n"
                "1: b 1b\n"
                "1: b 1b\n"
#endif
                ".asciz \"barebox\"\n"
                ".word _text\n"                         /* text base. If copied there,
                                                         * barebox can skip relocation
                                                         */
                ".word _barebox_image_size\n"           /* image size to copy */
        );
}


@arch/arm/cpu/start.c
//The actual reset vector. This code is position independent and usually
//does not run at the address it's linked at.
void __naked __bare_init reset(void)
{
        uint32_t r;

        /* set the cpu to SVC32 mode */
        __asm__ __volatile__("mrs %0, cpsr":"=r"(r));
        r &= ~0x1f;
        r |= 0xd3;
        __asm__ __volatile__("msr cpsr, %0" : : "r"(r));

#ifdef CONFIG_ARCH_HAS_LOWLEVEL_INIT
        arch_init_lowlevel();
#endif

        /* disable MMU stuff and caches */
        r = get_cr();
        r &= ~(CR_M | CR_C | CR_B | CR_S | CR_R | CR_V);
        r |= CR_I;

#if __LINUX_ARM_ARCH__ >= 6
        r |= CR_U;
#else
        r |= CR_A;
#endif

#ifdef __ARMEB__
        r |= CR_B;
#endif
        set_cr(r);

#ifdef CONFIG_MACH_DO_LOWLEVEL_INIT
        board_init_lowlevel();
#endif
        board_init_lowlevel_return(); //这里跳转到start_barebox()...
}



@arch/arm/cpu/start.c
//Board code can jump here by either returning from board_init_lowlevel
//or by calling this function directly.
void __naked __section(.text_ll_return) board_init_lowlevel_return(void)
{
        uint32_t r, addr, offset;

        /*
         * Get runtime address of this function. Do not
         * put any code above this.
         */
        __asm__ __volatile__("1: adr %0, 1b":"=r"(addr));

        /* Setup the stack */
        r = STACK_BASE + STACK_SIZE - 16;
        __asm__ __volatile__("mov sp, %0" : : "r"(r));

        /* Get offset between linked address and runtime address */
        offset = (uint32_t)__ll_return - addr;

        /* relocate to link address if necessary */
        if (offset)
                memcpy((void *)_text, (void *)(_text - offset),
                                __bss_start - _text);

        /* clear bss */
        memset(__bss_start, 0, __bss_stop - __bss_start);

        /* flush I-cache before jumping to the copied binary */
        __asm__ __volatile__("mcr p15, 0, %0, c7, c5, 0" : : "r" (0));

        /* call start_barebox with its absolute address */
        r = (unsigned int)&start_barebox;
        __asm__ __volatile__("mov pc, %0" : : "r"(r));      //从这里跳转到start_bare_box()函数的执行.
}

初始化脚本

#[zrlean@e4]$find -iname "init"
#./arch/arm/boards/a9m2410/env/bin/init
#...
#./defaultenv/bin/init
#...
//默认的初始化脚本
#init
#+-- ...
#+-- boot
#    +-- _boot_help

//boot文件建立起bootargs,然后boot系统
@defaultenv/bin/init
#!/bin/sh

PATH=/env/bin
export PATH

//执行配置脚本
. /env/config

if [ -e /dev/nor0 -a -n "$nor_parts" ]; then
        addpart /dev/nor0 $nor_parts
fi

if [ -e /dev/disk0 -a -n "$disk_parts" ]; then
        addpart /dev/disk0 $disk_parts
fi

if [ -e /dev/nand0 -a -n "$nand_parts" ]; then
        addpart /dev/nand0 $nand_parts
        nand -a /dev/nand0.*
fi

if [ -f /env/bin/init_board ]; then
        . /env/bin/init_board
fi

if [ -f /env/bin/boot_board ]; then
        . /env/bin/boot_board
elif [ -n $autoboot_timeout ]; then
        echo
        echo -n "Hit any key to stop autoboot: "
        timeout -a $autoboot_timeout
        if [ $? != 0 ]; then
                exit
        fi

        boot  //调用defaultenv/bin/boot脚本
fi

//默认的配置脚本
@defaultenv/config
#!/bin/sh

hostname=FIXME
if [ -z "$user" ]; then
#       user=
fi

# Enter MAC address here if not retrieved automatically
#eth0.ethaddr=de:ad:be:ef:00:00

# use 'dhcp' to do dhcp in barebox and in kernel
# use 'none' if you want to skip kernel ip autoconfiguration
ip=dhcp
dhcp_vendor_id=barebox

# or set your networking parameters here
#eth0.ipaddr=a.b.c.d
#eth0.netmask=a.b.c.d
#eth0.serverip=a.b.c.d
#eth0.gateway=a.b.c.d

# can be either 'tftp', 'nfs', 'nand', 'nor' or 'disk'
kernel_loc=tftp
# can be either 'net', 'nand', 'nor', 'disk' or 'initrd'
rootfs_loc=net
# can be either 'tftp', 'nfs', 'nand', 'nor', 'disk' or none
oftree_loc=tftp

# for flash based rootfs: 'jffs2' or 'ubifs'
# in case of disk any regular filesystem like 'ext2', 'ext3', 'reiserfs'
rootfs_type=ubifs
# where is the rootfs in case of 'rootfs_loc=disk' (linux name)
rootfs_part_linux_dev=mmcblk0p4
rootfsimage=rootfs-${hostname}.$rootfs_type

# where is the kernel image in case of 'kernel_loc=disk'
kernel_part=disk0.2

kernelimage=zImage-$hostname
#kernelimage=uImage-$hostname
#kernelimage=Image-$hostname
#kernelimage=Image-$hostname.lzo

bareboximage=barebox-${hostname}.bin
bareboxenvimage=barebox-${hostname}.bin

if [ -n $user ]; then
        bareboximage="$user"-"$bareboximage"
        bareboxenvimage="$user"-"$bareboxenvimage"
        kernelimage="$user"-"$kernelimage"
        rootfsimage="$user"-"$rootfsimage"
        nfsroot="/home/$user/nfsroot/$hostname"
else
        nfsroot="/path/to/nfs/root"
fi

autoboot_timeout=3

bootargs="console=ttyFIXME,115200"

nor_parts="256k(barebox)ro,128k(bareboxenv),3M(kernel),-(root)"
rootfs_mtdblock_nor=3

nand_parts="256k(barebox)ro,128k(bareboxenv),3M(kernel),-(root)"
nand_device="FIXME"
rootfs_mtdblock_nand=7

# set a fancy prompt (if support is compiled in)
PS1="\e[1;32mbarebox@\e[1;31m\h:\w\e[0m "


barebox 命令的声明和注册
//命令的声明:
BAREBOX_CMD_START(addpart)
        .cmd = do_addpart,
        .usage = "adds a partition table to a device",
        BAREBOX_CMD_HELP(cmd_addpart_help)
BAREBOX_CMD_END


#define BAREBOX_CMD_START(_name)                                                        \
extern const struct command __barebox_cmd_##_name;                                      \
const struct command __barebox_cmd_##_name                                              \
        __attribute__ ((unused,section (".barebox_cmd_" __stringify(_name)))) = {       \
        .name           = #_name,

#define BAREBOX_CMD_END                                 \
};

因此,addpart命令的实际定义为:
extern const struct command __barebox_cmd_addpart;                                      
const struct command __barebox_cmd_addpart                                              
        __attribute__ ((unused,section (".barebox_cmd_addpart"))) = {       
        .name           = addpart,
        };


struct command {
        const char      *name;                                            /* Command Name                 */
        const char      **aliases;                                        /* Implementation function      */
        int             (*cmd)(int, char *[]);
        int             (*complete)(struct string_list *sl, char *instr);
        const char      *usage;                                           /* Usage message        (short) */

        struct list_head list;                                            /* List of commands             */
#ifdef  CONFIG_LONGHELP
        const char      *help;                                            /* Help  message        (long)  */
#endif
}

        

@barebox.lds.S
#__barebox_cmd_start = .;  
#.barebox_cmd : { BAREBOX_CMDS }  
#__barebox_cmd_end = .;  


#define BAREBOX_CMDS    KEEP(*(SORT_BY_NAME(.barebox_cmd*)))
KEEP是ld script的关键字,作用是"强制连接器保留一些特定的 section"(refer to <gnu-ld>)
也就是说.barebox_cmd段实际上是由N多名为.barebox_cmd*的subsection组成的,而.barebox_cmd_addpart段就是其中之一.

//命令的注册
int register_command(struct command *cmd)
{
        //We do not check if the command already exists. This allows us to overwrite a 
        //builtin command with a module.
        debug("register command %s\n", cmd->name);
        //将command结构连入command_list链表.
        list_add_sort(&cmd->list, &command_list, compare); 

        if (cmd->aliases) {
                char **aliases = (char**)cmd->aliases;
                while(*aliases) {
                        char *usage = "alias for ";
                        struct command *c = xzalloc(sizeof(struct command));
                        memcpy(c, cmd, sizeof(struct command));

                        c->name = *aliases;
                        c->usage = xmalloc(strlen(usage) + strlen(cmd->name) + 1);
                        sprintf((char*)c->usage, "%s%s", usage, cmd->name);

                        c->aliases = NULL;
                        register_command(c);
                        aliases++;
                }
        }
        return 0;
}
EXPORT_SYMBOL(register_command);

//有两种情况会涉及到命令的注册,一种是模块加载的时候,一种是系统初始化的时候:

//加载模块的时候会像command_list注册模块中的相关命令.
BAREBOX_CMD_START(insmod)
        .cmd            = do_insmod,
        .usage          = "insert a module",
        BAREBOX_CMD_HELP(cmd_insmod_help)
BAREBOX_CMD_END

static int do_insmod(int argc, char * argv[])
+-- buf = read_file(argv[1], &len);
+-- module = load_module(buf, len);
    +-- register_command(cmd);     //模块本身支持的命令...
+-- free(buf);
+-- module->init();


//初始化的时候会将command section中的命令全部注册到command_list中去.
static int init_command_list(void){
        struct command *cmdtp;
        for (cmdtp = &__barebox_cmd_start; cmdtp != &__barebox_cmd_end; cmdtp++)
                register_command(cmdtp);
        return 0;
}
late_initcall(init_command_list);  //初始化的时候调用


barebox 的设备和驱动
barebox 的文件系统
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值