技术笔记2023076 rBoot学习7

技术笔记2023076 rBoot学习7

  继续之前的学习。

代码分析:函数find_image()

// prevent this function being placed inline with main
// to keep main's stack size as small as possible
// don't mark as static or it'll be optimised out when
// using the assembler stub

  首先看一下作者给这个函数写下的注释。为了避免此函数内嵌入main函数而占用main函数的栈空间,此函数被关键字NOINLINE修饰。为了避免此函数在使用汇编存根(stub)时被优化,不能使用static来修饰此函数。

	uint8_t  flag;
	uint32_t  loadAddr;
	uint32_t  flashsize;
	int32_t  romToBoot;
	uint8_t  updateConfig  =  0;
	uint8_t  buffer[SECTOR_SIZE];
	rboot_config  *romconf  = (rboot_config*)buffer;
	rom_header  *header  = (rom_header*)buffer;

  此函数先声明了一些变量,其中的变量buffer数组的大小是SECTOR_SIZE,也就是Flash一个扇区的大小,此处为4KB

#ifdef BOOT_BAUDRATE
	// soft reset doesn't reset PLL/divider, so leave as configured
	if (get_reset_reason() != REASON_SOFT_RESTART) {
		uart_div_modify( 0, UART_CLK_FREQ / BOOT_BAUDRATE);
	}
#endif

  此处建议在Makefile中定义变量RBOOT_BAUDRATE,因为ESP-12F模组上电默认的74880波特率太过奇怪。此处我将变量RBOOT_BAUDRATE定义在了变量RBOOT_FW_BASE的后面,并赋值为115200。

#if  defined  BOOT_DELAY_MICROS  &&  BOOT_DELAY_MICROS  >  0
	// delay to slow boot (help see messages when debugging)
	ets_delay_us(BOOT_DELAY_MICROS);
#endif

	ets_printf("\r\nrBoot v1.4.2 - richardaburton@gmail.com\r\n");

  此处建议去掉rboot.h//#define BOOT_DELAY_MICROS 2000000的注释去掉,并给宏定义设置一个合适的值,我将其设置为20000(单位为us)。这样做可以做到等待系统稳定再进行后面的操作。如果我们想设置rBoot阶段串口输出的波特率,就需要在这里等待系统串口稳定。

	// read rom header
	SPIRead(0, header, sizeof(rom_header));

	// print and get flash size
	ets_printf("Flash Size: ");

	flag  =  header->flags2  >>  4;
	if (flag  ==  0) {
		ets_printf("4 Mbit\r\n");
		flashsize  =  0x80000;
	} else  if (flag  ==  1) {
		ets_printf("2 Mbit\r\n");
		flashsize  =  0x40000;
	} else  if (flag  ==  2) {
		ets_printf("8 Mbit\r\n");
		flashsize  =  0x100000;
	} else  if (flag  ==  3  ||  flag  ==  5) {
		ets_printf("16 Mbit\r\n");
#ifdef BOOT_BIG_FLASH
		flashsize =  0x200000;
#else
		flashsize  =  0x100000; // limit to 8Mbit
#endif
	} else  if (flag  ==  4  ||  flag  ==  6) {
		ets_printf("32 Mbit\r\n");
#ifdef BOOT_BIG_FLASH
		flashsize =  0x400000;
#else
		flashsize  =  0x100000; // limit to 8Mbit
#endif
	} else  if (flag  ==  8) {
		ets_printf("64 Mbit\r\n");
#ifdef BOOT_BIG_FLASH
		flashsize =  0x800000;
#else
		flashsize  =  0x100000; // limit to 8Mbit
#endif
	} else  if (flag  ==  9) {
		ets_printf("128 Mbit\r\n");
#ifdef BOOT_BIG_FLASH
		flashsize =  0x1000000;
#else
		flashsize  =  0x100000; // limit to 8Mbit
#endif
	} else {
		ets_printf("unknown\r\n");
		// assume at least 4mbit
		flashsize  =  0x80000;
	}

	// print spi mode
	ets_printf("Flash Mode: ");

	if (header->flags1  ==  0) {
		ets_printf("QIO\r\n");
	} else  if (header->flags1  ==  1) {
		ets_printf("QOUT\r\n");
	} else  if (header->flags1  ==  2) {
		ets_printf("DIO\r\n");
	} else  if (header->flags1  ==  3) {
		ets_printf("DOUT\r\n");
	} else {
		ets_printf("unknown\r\n");
	}

	// print spi speed
	ets_printf("Flash Speed: ");

	flag  =  header->flags2  &  0x0f;
	if (flag  ==  0) ets_printf("40 MHz\r\n");
	else  if (flag  ==  1) ets_printf("26.7 MHz\r\n");
	else  if (flag  ==  2) ets_printf("20 MHz\r\n");
	else  if (flag  ==  0x0f) ets_printf("80 MHz\r\n");
	else  ets_printf("unknown\r\n");

  这里使用SPI接口将Flash上的头部信息读出并保存在变量header中。其中包含有Flash大小、模式和速度信息。这些信息我们都可以在Makefile中设置,设置好以后会被编译进bin文件中。对于ESP-12F来说,应该这样设置:SPI_SIZE ?= 4MSPIMODE ?= DOUTSPI_SPEED ?= 40

	// read boot config
	SPIRead(BOOT_CONFIG_SECTOR  *  SECTOR_SIZE, buffer, SECTOR_SIZE);

	// fresh install or old version?
	if (romconf->magic  !=  BOOT_CONFIG_MAGIC  ||  romconf->version  !=  BOOT_CONFIG_VERSION) 
	{
		// create a default config for a standard 2 rom setup
		ets_printf("Writing default boot config.\r\n");
		
		ets_memset(romconf, 0x00, sizeof(rboot_config));
		romconf->magic  =  BOOT_CONFIG_MAGIC;
		romconf->version  =  BOOT_CONFIG_VERSION;
		default_config(romconf, flashsize);
		
		// write new config sector
		SPIEraseSector(BOOT_CONFIG_SECTOR);
		SPIWrite(BOOT_CONFIG_SECTOR  *  SECTOR_SIZE, buffer, SECTOR_SIZE);
	}

	// try rom selected in the config, unless overriden by gpio/temp boot
	romToBoot  =  romconf->current_rom;

  这里先将Flash第一个扇区的数据读入缓冲区buffer中,而前面romconf指向了buffer。那么此时romconf就读出了rBoot的配置结构体信息。接着,我们比较magicversion是否与之前的宏定义相同。按照rBoot项目的readme中说的,我们在进行boot版本迭代时,可以通过修改version来区分新旧版本。如果此处if为真,则说明Flash中存在新的boot,此时要将Flash的第一个扇区覆盖。之后获取当前需要加载的rom的索引。

	// check valid rom number
	// gpio/temp boots will have already validated this
	if (romconf->current_rom  >=  romconf->count) {
		// if invalid rom selected try rom 0
		ets_printf("Invalid rom selected, defaulting to 0.\r\n");
		romToBoot  =  0;
		romconf->current_rom  =  0;
		updateConfig  =  1;
	}

	// check rom is valid
	loadAddr  =  check_image(romconf->roms[romToBoot]);
	
	// check we have a good rom
	while (loadAddr  ==  0) {
		ets_printf("Rom %d at %x is bad.\r\n", romToBoot, romconf->roms[romToBoot]);
		// for normal mode try each previous rom
		// until we find a good one or run out
		updateConfig  =  1;
		romToBoot--;
		if (romToBoot  <  0) romToBoot  =  romconf->count  -  1;
		if (romToBoot  ==  romconf->current_rom) {
			// tried them all and all are bad!
			ets_printf("No good rom available.\r\n");
			return  0;
		}
		loadAddr  =  check_image(romconf->roms[romToBoot]);
}

  之前拿到了当前需要加载的rom的索引。如果之前拿到的索引是否超出rom总数,则认为此索引是无效的,并默认加载第一个rom。之后再去获取rom的地址。如果获取rom地址失败,则说明此rom已经被损坏了。如果所有rom都已经损坏了,那么只能直接返回0,代表没有找到image了。

	// re-write config, if required
	if (updateConfig) {
		romconf->current_rom  =  romToBoot;
		SPIEraseSector(BOOT_CONFIG_SECTOR);
		SPIWrite(BOOT_CONFIG_SECTOR  *  SECTOR_SIZE, buffer, SECTOR_SIZE);
	}

	ets_printf("Booting rom %d at %x, load addr %x.\r\n", romToBoot, romconf->roms[romToBoot], loadAddr);
	// copy the loader to top of iram
	ets_memcpy((void*)_text_addr, _text_data, _text_len);
	// return address to load from
	return  loadAddr;

  获取到rom的地址后,将当前的rom的索引写入配置结构体中。随后将buffer写入boot所在的扇区,覆盖了之前的配置,在下次启动时将使用当前的配置。然后,将_text_data位置,长度为_text_len的数据加载到_text_addr中。最后返回rom的地址,find_image()函数就结束了。

const  uint32_t  entry_addr  =  0x4010fcb4;

const  uint32_t  _text_addr  =  0x4010fc00;
const  uint32_t  _text_len  =  192;
const  uint8_t  _text_data[]  = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x1c, 0x4b, 0x00, 0x40, 0x12, 0xc1, 0xc0, 0xc9, 0xe1, 0x8b, 0x31, 0xcd,
0x02, 0x0c, 0x84, 0xe9, 0xc1, 0xf9, 0xb1, 0x09, 0xf1, 0xd9, 0xd1, 0xc2, 0xcc, 0x08, 0x01, 0xf9,
0xff, 0xc0, 0x00, 0x00, 0xf8, 0x31, 0xe2, 0x01, 0x09, 0x86, 0x10, 0x00, 0x2d, 0x0c, 0x3d, 0x01,
0x0c, 0x84, 0x01, 0xf4, 0xff, 0xc0, 0x00, 0x00, 0x8b, 0xcc, 0x78, 0x01, 0xd8, 0x11, 0x46, 0x09,
0x00, 0x21, 0xef, 0xff, 0x5d, 0x0d, 0xd7, 0xb2, 0x02, 0x20, 0x52, 0x20, 0x2d, 0x0c, 0x3d, 0x07,
0x4d, 0x05, 0x59, 0x51, 0x79, 0x41, 0x01, 0xeb, 0xff, 0xc0, 0x00, 0x00, 0x58, 0x51, 0x78, 0x41,
0x5a, 0xcc, 0x5a, 0x77, 0x50, 0xdd, 0xc0, 0x56, 0x6d, 0xfd, 0x0b, 0x6e, 0x60, 0xe0, 0x74, 0x56,
0x9e, 0xfb, 0x08, 0xf1, 0x2d, 0x0f, 0xc8, 0xe1, 0xd8, 0xd1, 0xe8, 0xc1, 0xf8, 0xb1, 0x12, 0xc1,
0x40, 0x0d, 0xf0, 0x00, 0xfd, 0x00, 0x05, 0xf8, 0xff, 0x0d, 0x0f, 0xa0, 0x02, 0x00, 0x0d, 0xf0,
};

  _text_data_text_len_text_addr等变量都可以在build目录下的rboot-hex2a.h中找到。令人意外的是,此文件竟然处于build目录下,这就说明它不是写出来的,而是通过某种方式生成的。

$(RBOOT_BUILD_BASE)/rboot-hex2a.h: $(RBOOT_BUILD_BASE)/rboot-stage2a.elf
	@echo "E2 $@"
	$(Q)  $(ESPTOOL2) -quiet -header $<  $@ .text

  通过分析Makefile,我们可以看到,rboot-hex2a.h依赖于rboot-stage2a.elf,并通过esptool2生成的。关于生成rboot-hex2a.h的具体过程,可以参考这篇文章:ESP8266 Bootloader开源代码解析之rboot(一)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值