Edit online

SDFAT32 加载

25 Dec 2024
Read time: 3 minute(s)

SDFAT32 启动方式更为简单,不需要专业的烧录工具进行烧写,只需要将镜像文件复制到 SD 卡即可。



1. MMC 与 SD 卡启动差异
SDFAT32 是基于 MMC 建立的,加载程序也在 spl_mmc.c 中实现:
  1. 使用下列宏参数将 spl_mmc_load_image 函数添加到 .u_boot_list_2_spl_image_loader_* 段。
    SPL_LOAD_IMAGE_METHOD(“MMC2”, 0, BOOT_DEVICE_MMC2, spl_mmc_load_image);
  2. board_init_r(gd_t *dummy1, ulong dummy2) 函数中,调用 boot_from_devices 从 device 中选择启动介质,通过启动介质选择对应的加载程序,获取启动镜像。
    // source/uboot-2021.10/common/spl/spl.c
    boot_from_devices
    |   // 这里会传递一个空的 info 结构体,以及传递一个启动设备的列表,这里为第一个值为 BOOT_DEVICE_MMC2
    |->spl_ll_find_loader(bootdev); // bootdev=BOOT_DEVICE_MMC2,根据 bootdev 查找 spl_load_image 指针
    |->spl_load_image(spl_image, loader); // 这里已经获取到对应启动设备的加载器
            |->loader->load_image(spl_image, &bootdev); // 调用加载器的实现函数 spl_mmc_load_image
  3. 进入到对应启动介质的加载程序中,通过 NAME 指定启动镜像配置文件,从中获取镜像在 SD 卡中的实际地址。
    // source/uboot-2021.10/common/spl/spl_fat.c
    spl_mmc_load_image
    |->spl_mmc_load(spl_image, bootdev,...); // 这里 CONFIG_SPL_FS_LOAD_PAYLOAD_NAME=bootcfg.txt
        |->spl_mmc_find_device(&mmc, bootdev->boot_device); // 先找 mmc 设备
        |->mmc_init(mmc); // 再进行 mmc 设备的初始化
        |->spl_mmc_boot_mode(bootdev->boot_device);
        |       // 选择启动方式,这里获取到的是 FS 方式启动
        |->spl_mmc_do_fs_boot(spl_image, mmc, filename); // filename 为 bootcfg.txt
            |->aic_spl_load_image_fat(spl_image, mmc_get_blk_desc(mmc), filename);
                |->blk_dread(cur_dev, 0, 1, header);
                |       // 读取 SD 卡的第 0 块。
                |->check_identifier(header); // 检测 SD 卡的分区类型选择启动方式
                |->load_image_from_mbr_part_fat32(header, filename); // 以 MBR 分区格式去加载镜像
                    |->blk_dread(cur_dev, part.lba_start, 1, header);
                    |   // 读取 LBA 起始地址
                    |->check_identifier(header); // 检测 SD 卡分区类型是否为 FAT32
                    |->load_image_from_raw_volume_fat32(part.lba_start, header, filename);
                    |   // 获取 BPB 内容
                        |->load_image_file(header, filename);
                            |->aic_fat_read_file(filename, (u8 *)header, 0, 1024, &actread);
                            |   // 查找并读取 txt 文件内容到 header 地址
                            |->boot_cfg_parse_file((u8 *)header, actread, "boot1", ...);
                            |   // 解析 txt 信息,获取 image 名称,解析 Boot 的大小,以及相对
                            |   // image 文件中 Boot 所在的偏移地址
                            |->aic_fat_read_file(imgname, header, ...); //读取 image 文件的 header
                            |->spl_parse_image_header(spl_image, header);
                            |   // 解析 Boot 中前 64 个字节内容,设置加载地址
                            |->aic_fat_read_file(imgname, (u8 *)spl_image->load_addr, ...);
                            |   // 读取 Boot 镜像到 spl_image->load_addr 地址。
                            |->board_init_r(); // 跳转回 board_init_r()继续执行
  4. 读取 Boot 镜像到内存后,跳转回 board_init_r()继续执行
    // source/uboot-2021.10/common/spl/spl.c
    board_init_r()
    |->spl_perform_fixups // vendor hook,用于修改 tree 传递参数
    |->jump_to_image_no_args(&spl_image);
        |->image_entry = (image_entry_withargs_t)spl_image->entry_point; // 获取 Boot 加载地址
        |->set_boot_device(boot_param, aic_get_boot_device()); // 设置启动介质
        |->set_boot_reason(boot_param, aic_get_boot_reason()); // 设置启动原因(冷启动或者热启动)
        |->image_entry(boot_param, cur_tm); // 进入 Boot 执行,并传递一些参数