Boot Assist Module(BAM)

对于绝大多数mpu/cpu来说,上电之后pc都会进到一个约定俗成的地址,这个地址一般由芯片设计厂商设置,但是也有支持设备集成厂商修改的mpu/cpu,如ppc的e200z7就可通过p_rstbase[0:29]设置上电地址。

对于一般的板级设备开发,使用cpu/mpu默认的地址就好了;但是也有比较高级的设备,它在设备集成的时候内建了一个boot程序,cpu上电后进入到约定地址,即boot程序地址,然后再由boot程序加载操作系统。这就是当前pc机的做法,而对于简单的嵌入式设备一般是不会内建boot程序的。

BAM(Boot Assist Module)就是这样的一个boot程序,我在QEMU中实现mpc5675的时候所遇到的一个问题就是无法确定加载程序的entry地址,而在其他固定上电地址的板级设备中则可确定entry地址为默认上电地址。

1、分析

参考e500中获取entry地址的方式,在qemu/hw/ppc/e500.c:ppce500_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
if (bios_name == NULL) {
    if (machine->kernel_filename) {
        bios_name = machine->kernel_filename;
    } else {
        bios_name = "u-boot.e500";
    }
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_nam
bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL,
                     1, PPC_ELF_MACHINE, 0, 0);
if (bios_size < 0) {
    /*
     * Hrm. No ELF image? Try a uImage, maybe someone is giving us an
     * ePAPR compliant kernel
     */
    kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL,
                              NULL, NULL);
    if (kernel_size < 0) {
        fprintf(stderr, "qemu: could not load firmware '%s'\n", filename);
        exit(1);
    }
}
g_free(filenam
/* Reserve space for dtb */
dt_base = (loadaddr + bios_size + DTC_LOAD_PAD) & ~DTC_PAD_MA
dt_size = ppce500_prep_device_tree(machine, params, dt_base,
                                   initrd_base, initrd_size,
                                   kernel_base, kernel_size);
if (dt_size < 0) {
    fprintf(stderr, "couldn't load device tree\n");
    exit(1);
}
assert(dt_size < DTB_MAX_SIZ
boot_info = env->load_info;
boot_info->entry = bios_entry;
boot_info->dt_base = dt_base;
boot_info->dt_size = dt_size;

e500通过elf文件加载时读取elf文件头文件信息,获取entry。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void ppce500_cpu_reset(void *opaque)
{
    PowerPCCPU *cpu = opaque;
    CPUState *cs = CPU(cpu);
    CPUPPCState *env = &cpu->env;
    struct boot_info *bi = env->load_info;

    cpu_reset(cs);

    /* Set initial guest state. */
    cs->halted = 0;
    env->gpr[1] = (16<<20) - 8;
    env->gpr[3] = bi->dt_base;
    env->gpr[4] = 0;
    env->gpr[5] = 0;
    env->gpr[6] = EPAPR_MAGIC;
    env->gpr[7] = mmubooke_initial_mapsize(env);
    env->gpr[8] = 0;
    env->gpr[9] = 0;
    env->nip = bi->entry;
    mmubooke_create_initial_mapping(env);
}

在cpu_reset之后,手动设置env->nip = bi->entry。但是在加载bin类型文件时却没有办法读取entry信息。

2、BAM实现

查看BAM实现,看BAM如何找到程序entry,是否采用指定到固定地址的方式。

2.1、概述

BAM在一块只读内存上,它包含了VLE指定代码,会根据设备的不同启动模式选择执行。

BAM通过以下两个协议下载代码到SRAM,并且执行它:

  • FlexCAN
  • LINFlexD-UART

BAM的目的是通过一个串口下载代码到SRAM,下载完成后进行检验确保下载数据的完整性。

2.2、启动模式

mpc5675支持两种启动模式:

  • SC(Single Chip)设备从flash main array的第一个可执行的段启动;
  • SBL(Serial Boot)设备从FlexCAN和LINFlex下载同时运行它。

如果没有可用的启动方式,那么就会进入“Static mode”。

2.3、内存映射

BAM代码在8KB的ROM上,映射地址为0xFFFF_C000。

BAM将代码加载到SRAM上,起始地址为0x4000_0100。

2.4 、进入启动模式

BAM会读取SSCM_STATUS[BMODE]位判断进入那个模式

  • 000 Reserved
  • 001 FlexCAN
  • 010 LINFlexD
  • 011 Single Chip

2.5、内存映射

用户代码加载到板子上之后,会在板子FLASH上添加RCHW和Start address。调试时直接打印内存0x0 0x4可看到。

valid boot identifier为0x5A,后面的0x6c为application start address。该地址即为我想要寻找的entry地址。

3、解决方法

在板级设备中添加BAM。