raburton / rboot

An open source bootloader for the ESP8266
https://richard.burtons.org/tag/rboot/?order=ASC
MIT License
299 stars 72 forks source link

rboot + uart #52

Open Bertje opened 5 years ago

Bertje commented 5 years ago

Hi,

First of all; many thanks for releasing rboot!

I'm trying to get (ESP8266_NONOS_SDK-3.0) UART working on rboot, as I'd like to define how to boot depending on what's coming in on UART. However, I'm a bit puzzled on how to achieve that in combination with your Makefile and esptool2. I keep on hitting on undefined reference errors when compiling, so I guess it's something to do with linking the objects together somehow. As I'm not a Makefile/compiler master, I would appreciate your thoughts on this, i.e., how would you approach this?

raburton commented 5 years ago

Can you give me more information on the error? Maybe the build log so I can get an idea of what's failing. Being able to see the code would probably help too, if that's an option.

Bertje commented 5 years ago

Thanks for the swift response here!

In essence, what I was trying to do is to re-use/copy the SDK's driver library into rboot, such that I could start using the UART functions. This, however, resulted in a nice deepdive into Espressif's wonderful Makefiles.

Anyway, here's my (current) plan - making use of (a copy of) the ESP NONOS 3.0 SDK's driver library, like I did for the main application (which works):

  1. In rboot.c: Add:

    #include "/absolute/path/to/ESP8266_NONOS_SDK-3.0/driver_lib/include/driver/uart.h"
    #include "/absolute/path/to/ESP8266_NONOS_SDK-3.0/driver_lib/include/driver/uart_register.h"

    In call_user_start(), add:

    uart_init(BIT_RATE_115200,BIT_RATE_115200);
  2. Edit rboot's Makefile, to match more with the main application's Makefile's LDFLAGS, particularly, adding -ldriver to the flags, as that's the one containing UART functionality from how I understand it: As LDFLAGS is used in two recipes and I think/assume that for my purpose I can't use the same for both, I create two, i.e., rename definition LDFLAGS to LDFLAGS1 and rename LDFLAGS to 'LDFLAGS1' at the recipe for rboot-stage2a.elf: Create a new variable named LDFLAGS2 and define it as follows. Additionally, rename LDFLAGS to LDFLAGS2 at the recipe for %.elf. There's probably some unnecessary libraries included here, but I didn't dare to throw too many out (I figured that would be a problem for later). Also, do I understand correctly that -static makes it a stand-alone "executable"/binary, meaning, linked functions such as those for UART are copied into this, rather than linked to via the eagle linker script?:

    LDFLAGS2 = \
    -L$(SDK_BASE)/lib        \
    -nostdlib   \
    -Wl,--no-check-sections \
    -Wl,--gc-sections   \
     -u call_user_start \
    -Wl,-static                     \
    -Wl,--start-group                   \
    -lc                 \
    -lgcc                   \
    -lhal                   \
    -lphy   \
    -lpp    \
    -llwip  \
    -lmain  \
    -ldriver \
    $(DEP_LIBS_eagle.app.v6)                    \
    -Wl,--end-group
  3. Edit the recipe for rboot.o to:

    $(RBOOT_BUILD_BASE)/rboot.o: rboot.c rboot-private.h rboot.h $(RBOOT_BUILD_BASE)/rboot-hex2a.h $(SDK_BASE)/driver_lib/include/driver/uart_register.h $(SDK_BASE)/driver_lib/include/driver/uart.h
    @echo "CC $< $(CFLAGS) -I$(RBOOT_BUILD_BASE) -I$(SDK_BASE)/include"
    $(Q) $(CC) $(CFLAGS) -I$(RBOOT_BUILD_BASE) -I$(SDK_BASE)/include -I$(SDK_BASE)/driver_lib/include/driver -I -c $< -o $@
  4. Replace rboot's eagle.app.v6.ld and eagle.rom.addr.v6.ld with the SDK's:

    $mv eagle.app.v6.ld eagle.app.v6.ld.rbootsample
    $cp /absolute/path/to/ESP8266_NONOS_SDK-3.0/ld/eagle.app.v6.ld .
    $cp /absolute/path/to/ESP8266_NONOS_SDK-3.0/ld/eagle.rom.addr.v6.ld .
  5. set environment variable SDK_BASE to SDK's root folder

  6. I know I have to change the linker script to fit my memory map needs, but I assumed/presumed/hoped that would be a worry for later, i.e., first the compiling should stop throwing errors :)

And here's the first part of the compiler output:

CC rboot-stage2a.c
LD build/rboot-stage2a.elf
E2 build/rboot-hex2a.h
CC rboot.c -Os -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals  -D__ets__ -DICACHE_FLASH -I. -Ibuild -
I/mnt/hgfs/bert/Share/ESP8266_NONOS_SDK-3.0/include
/home/bert/esp/crosstool-NG/builds/xtensa-lx106-elf/lib/gcc/xtensa-lx106-elf/4.8.5/../../../../xtensa-lx106-elf/bin/ld: warning: cannot find entry symbol _start;
defaulting to 0000000000400054
/tmp/ccxzUnTF.o:(.text+0x0): undefined reference to `SPIRead'
/tmp/ccxzUnTF.o: In function `check_image$part$0':
rboot.c:(.text+0x20): undefined reference to `SPIRead'
rboot.c:(.text+0x57): undefined reference to `SPIRead'
rboot.c:(.text+0x77): undefined reference to `SPIRead'
rboot.c:(.text+0x9e): undefined reference to `SPIRead'
/tmp/ccxzUnTF.o:rboot.c:(.text+0xd6): more undefined references to `SPIRead' follow
/tmp/ccxzUnTF.o: In function `check_image$part$0':
rboot.c:(.text+0x188): undefined reference to `ets_printf'
rboot.c:(.text+0x18c): undefined reference to `ets_memset'
rboot.c:(.text+0x190): undefined reference to `SPIEraseSector'
rboot.c:(.text+0x194): undefined reference to `SPIWrite'
rboot.c:(.text+0x198): undefined reference to `ets_memcpy'
rboot.c:(.text+0x1b2): undefined reference to `ets_printf'
rboot.c:(.text+0x1be): undefined reference to `SPIRead'
rboot.c:(.text+0x1c7): undefined reference to `ets_printf'
rboot.c:(.text+0x1e2): undefined reference to `ets_printf'
/tmp/ccxzUnTF.o: In function `find_image':
rboot.c:(.text+0x235): undefined reference to `ets_printf'
rboot.c:(.text+0x244): undefined reference to `ets_printf'
rboot.c:(.text+0x250): undefined reference to `ets_printf'
/tmp/ccxzUnTF.o:rboot.c:(.text+0x283): more undefined references to `ets_printf' follow
/tmp/ccxzUnTF.o: In function `find_image':
rboot.c:(.text+0x2da): undefined reference to `SPIRead'
rboot.c:(.text+0x2ef): undefined reference to `ets_printf'
rboot.c:(.text+0x2fb): undefined reference to `ets_memset'
rboot.c:(.text+0x31f): undefined reference to `SPIEraseSector'
rboot.c:(.text+0x32b): undefined reference to `SPIWrite'
rboot.c:(.text+0x343): undefined reference to `ets_printf'

Do you have any idea on:

  1. My approach is even going to be fruitful, i.e., is it even possible to (re-)use the SDK's driver/uart library this way?
  2. In line with 1., the bootloader should have its own copy of the SDK's driver library, as the bootloader is intended to overwrite the application's code (via UART).
  3. Do you have a nice reference which explains Espressif's Makefile? That stuff is like black magic to me (I see variables which I nowhere see again in the whole SDK, e.g., DEPENDS_eagle.app.v6)
  4. How to help me forward (:

Again, I appreciate your time spent on this one 👍🙏

raburton commented 5 years ago

rBoot cannot be linked against the sdk library or, consequently, make use of any normal sdk functions or code. A bootloader if not a normal app, it is a tiny bit of baremetal code, that can only use simple rom based functions (or it's own code). To link it against the SDK library it would go from <1k to hundreds and, more importantly, would be unable to chain load a user rom.

Bertje commented 5 years ago

Indeed, bootloaders are typically small and non-sophisticated. I just hoped that “just” adding UART functionality wouldn’t crank up its total size too much.

Anyway, looking forward, I guess I’d go for the following setup then: 1) rboot (boots either rom1 or rom2 depending on some flag in pre-defined memory location (set by rom1)) 2) rom1 (seconds stage “bootloader”, built with SDK, which downloads the new firmware over uart to rom2 memory location) 3) rom2 (application)

Thoughts?

raburton commented 5 years ago

You could (probably) still use uart in rBoot, after all it is able to print to the serial port. But you'd need to work out how to do that, and couldn't use the SDK functions. If you decompile the parts of the sdk that do the functions you want to use you may be able to see what lower level calls are made and replicate these in rBoot. In theory basic serial interaction shouldn't be too complex (compared with wifi, for example), but as nothing at this level is documented it might take a lot of figuring out. Your way of having another rom specifically for performing the update is a reasonable option. You could also use the gpio boot option to have a button to force booting of this "recovery" rom.

someburner commented 5 years ago

You could look at the esptool.py stub flasher code, here. AFAIK it is not compiled against the SDK and just uses the definitions from rom_functions.h.

Also -just my 2 cents- I'd recommend against multiple stages of bootloader. It complicates everything. Just pack it into a single rboot.

raburton commented 5 years ago

Good tip, that looks about right.

Bertje commented 5 years ago

Thanks for the great thoughts here @raburton and @someburner! I'm following your recommendations on trying to get UART working in rboot the same way as the stub_flasher does.

Now, I've spent a bit of time mimicking what happens in stub_flasher.c (especially from this location). Stuff compiles, however, I'm getting when running the code.

Fatal exception (0): epc1=0x40240010, epc2=0x00000000, epc3=0x00000000, excvaddr=0x00000000, depc=0x00000000

I've dived into where it goes wrong, and it goes wrong with the following calls (regardless in what order):

ets_isr_attach(ETS_UART0_INUM, uart0_rx_intr_handler, NULL);
SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RX_INTS);
ets_isr_unmask(1 << ETS_UART0_INUM);

I assume it has something to do with the linking and memory mapping, so here's the map for, e.g., ets_isr_attach, as defined in the rom.ld file:

PROVIDE ( ets_isr_attach = 0x40000f88 );

The app.ld file is as follows:

dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40240000, len = 0x3C000

Current generated rboot.bin is 2.3K large. Not sure if the CFLAGS which are set for stub_flasher are of importance here as well.

Thoughts?

raburton commented 5 years ago

There is no flash memory mapping here, so you need to keep your code in the iram segment. If it's trying to run code from 0x40240010 then that code must have found it's way into the wrong segment.

Bertje commented 5 years ago

Awesome, thanks for the hint; I had to remove ICACHE_FLASH_ATTR from the function definitions.

Now I'm trying to remove the need / get a similar workaround for functions os_malloc() and system_get_free_heap_size(), as used in SDK's uart.c for enqueuing and dequeuing UART buffers. Obviously I (could) know the sizes of those buffers, but I'll keep that for plan B.

raburton commented 5 years ago

There are some memory management functions in rom, I don't remember using them but I imagine they work pretty much as you'd expect them to. See: https://github.com/raburton/rboot/blob/master/eagle.rom.addr.v6.ld#L190

Bertje commented 4 years ago

Tad of a late reply here, but thanks again for the hints!

Now, as rboot.c has grown a bit too much, I’d like to split the code into multiple source files, i.e.., add my own uart.c and uart.h to it as well. However, I only know some basic Makefile stuff and I do not fully understand what your esptool2 is doing, so, perhaps you could help me here @raburton?

raburton commented 4 years ago

I can't really give you a makefile tutorial, but as a starter try creating some additional targets like the one for $(RBOOT_BUILD_BASE)/rboot.o:, for your new uart files. esptool2 is used for 2 things: 1) extract the compiled stage2.5 loader and produce the header file to import it into rBoot main code 2) create the flashable rBoot rom from the linked elf file. You shouldn't need to do anything with these bits of the make file.

Bertje commented 4 years ago

Thanks for the guidance (and notes on esptool2). I misunderstood the usage of % sign in Makefiles (assumptions..). When explicitly adding each object file to rule for making the final .elf file, it works!