Update appfs, tests

This commit is contained in:
Renze Nicolai 2022-01-23 01:27:22 +01:00
parent 932246024c
commit 32637a8676
17 changed files with 494 additions and 12931 deletions

View file

@ -1,4 +1,3 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(main) project(main)

View file

@ -0,0 +1 @@
../components/appfs

View file

@ -0,0 +1,17 @@
idf_component_register(SRCS "bootloader_start.c" "appfs_flashfunctions_wrapper.c"
REQUIRES bootloader bootloader_support appfs)
idf_build_get_property(target IDF_TARGET)
# Use the linker script files from the actual bootloader
set(scripts "${IDF_PATH}/components/bootloader/subproject/main/ld/${target}/bootloader.ld"
"${IDF_PATH}/components/bootloader/subproject/main/ld/${target}/bootloader.rom.ld")
target_linker_script(${COMPONENT_LIB} INTERFACE "${scripts}")
target_link_libraries(${COMPONENT_LIB} INTERFACE
"-Wl,--wrap=bootloader_flash_read"
"-Wl,--wrap=bootloader_mmap"
"-Wl,--wrap=bootloader_munmap"
"-Wl,--wrap=bootloader_console_deinit"
)

View file

@ -0,0 +1,159 @@
/*
These functions wrap the flash read and mmap functions. The idea is that normally, they will run the real
functions. However, after appfs_wrapper_init is called with an appfs file and a flash range, any
call to these functions in that range will be redirected to the appfs functions that do the same.
The idea is that this changes the 'view' of that flash range from an (for the rest of the bootloader)
ununderstandable appfs struct mess, to one that looks the same as it would when the selected file
would be directly flashed to the partition. The nice thing here is that we can use the rest of the
bootloader verbatim, without having to modify it.
Not we assume the ovl_start and ovl_end match the position and size of the appfs partition; we use that
if we actually boot an app.
Note that IRAM_ATTR is used here to make sure the functions that are used when/after the app loadable
segments are loaded, won't be overwritten. The IRAM_ATTR in the bootloader code dumps the function
in the loader segment instead of in random IRAM.
*/
#ifdef BOOTLOADER_BUILD
#include "appfs.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_app_format.h"
#include "soc/soc_memory_types.h"
#include "soc/soc_caps.h"
#include <string.h>
static const char *TAG="appfs_wrapper";
static appfs_handle_t file_handle=APPFS_INVALID_FD;
static size_t ovl_start, ovl_size;
void appfs_wrapper_init(appfs_handle_t handle, size_t part_start, size_t part_size) {
file_handle=handle;
ovl_start=part_start;
ovl_size=part_size;
}
void appfs_wrapper_deinit() {
file_handle=APPFS_INVALID_FD;
}
//These are the actual functions.
esp_err_t __real_bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt);
const void *__real_bootloader_mmap(uint32_t src_addr, uint32_t size);
void __real_bootloader_munmap(const void *mapping);
void __real_bootloader_console_deinit();
static bool was_mmapped_to_appfs=false;
IRAM_ATTR const void *__wrap_bootloader_mmap(uint32_t src_addr, uint32_t size) {
if (file_handle!=APPFS_INVALID_FD && src_addr>=ovl_start && src_addr+size<ovl_start+ovl_size) {
ESP_LOGI(TAG, "__wrap_bootloader_mmap: redirecting map to 0x%X", src_addr);
uint8_t *f=appfsBlMmap(file_handle);
return &f[src_addr-ovl_start];
} else {
return __real_bootloader_mmap(src_addr, size);
}
}
IRAM_ATTR void __wrap_bootloader_munmap(const void *mapping) {
if (file_handle!=APPFS_INVALID_FD && was_mmapped_to_appfs) {
appfsBlMunmap();
was_mmapped_to_appfs=false;
} else {
__real_bootloader_munmap(mapping);
}
}
IRAM_ATTR esp_err_t __wrap_bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt) {
if (file_handle!=APPFS_INVALID_FD && src_addr>=ovl_start && src_addr+size<ovl_start+ovl_size) {
uint8_t *f=appfsBlMmap(file_handle);
ESP_LOGI(TAG, "__wrap_bootloader_flash_read: 0x%X->0x%X, %d bytes", src_addr, (int)dest, size);
memcpy(dest, &f[src_addr-ovl_start], size);
appfsBlMunmap();
return ESP_OK;
} else {
return __real_bootloader_flash_read(src_addr, dest, size, allow_decrypt);
}
}
IRAM_ATTR static bool should_map(uint32_t load_addr) {
return (load_addr >= SOC_IROM_LOW && load_addr < SOC_IROM_HIGH)
|| (load_addr >= SOC_DROM_LOW && load_addr < SOC_DROM_HIGH);
}
//Note: when this is called, everything to verify and load the app has already been done *EXCEPT* the MMU
//mapping. That is done, but with wrong addresses. We need to re-do that here and then call into
//the app.
static IRAM_ATTR void mmap_and_start_app() {
ESP_LOGI(TAG, "mmap_and_start_app()");
//Undo bootloader mapping. If we don't call this, the rest of the code thinks there's still
//something mapped. Note that for now the address doesn't matter, we feed it 0.
__real_bootloader_munmap(0);
//Appfs is not gonna like that its metadata is not mmapped. We call this routine as what it does
//'under the hood' is clear the mmu and reset it to mmap only the appfs meta info.
appfsBlMunmap();
//Map the executable file so we can read its header.
uint8_t *appBytes=appfsBlMmap(file_handle);
const esp_image_header_t *hdr=(const esp_image_header_t*)appBytes;
uint32_t entry_addr=hdr->entry_addr;
AppfsBlRegionToMap mapRegions[8];
int noMaps=0;
uint8_t *pstart=appBytes+sizeof(esp_image_header_t);
uint8_t *p=pstart;
for (int i=0; i<hdr->segment_count; i++) {
esp_image_segment_header_t *shdr=(esp_image_segment_header_t*)p;
p+=sizeof(esp_image_segment_header_t);
if (should_map(shdr->load_addr)) {
mapRegions[noMaps].fileAddr=p-appBytes;
mapRegions[noMaps].mapAddr=shdr->load_addr;
mapRegions[noMaps].length=shdr->data_len;
noMaps++;
ESP_LOGI(TAG, "Segment %d: map to %X size %X", i, shdr->load_addr, shdr->data_len);
} else {
ESP_LOGI(TAG, "Segment %d: ignore (addr %X) size %X", i, shdr->load_addr, shdr->data_len);
}
int l=(shdr->data_len+3)&(~3);
p+=l;
}
ESP_LOGI(TAG, "Unmap");
appfsBlMunmap();
appfsBlMapRegions(file_handle, mapRegions, noMaps);
ESP_LOGD(TAG, "start: 0x%08x", entry_addr);
typedef void (*entry_t)(void);
entry_t entry = ((entry_t) entry_addr);
(*entry)();
}
//Before the app is started, the bootloader manually sets up the cache. We can't easily intercept
//that in order to do the transformation from fake partition offsets to appfs file contents,
//however the bootloader does have a call that it calls just before it starts up the app. We hook
//that here, manually set the cache regions to the actual app.
IRAM_ATTR void __wrap_bootloader_console_deinit() {
if (file_handle!=APPFS_INVALID_FD) {
mmap_and_start_app();
}
//Actual partition selected. Simply call the actual function.
__real_bootloader_console_deinit();
}
//These functions are used by appfs to access the flash: these should always use unwrapped calls.
IRAM_ATTR const void* appfs_bootloader_mmap(uint32_t src_addr, uint32_t size) {
return __real_bootloader_mmap(src_addr, size);
}
IRAM_ATTR void appfs_bootloader_munmap(const void *mapping) {
return __real_bootloader_munmap(mapping);
}
#endif

View file

@ -0,0 +1,18 @@
#pragma once
#ifdef BOOTLOADER_BUILD
/*
The appfs wrapper layer wraps (using thr gcc linker wrapping function, see the CMakeFiles.txt
file in this directory) the calls that the rest of the bootloader uses, and maps accesses to
the appfs partition to transparently map to access to the selected appfs file instead.
*/
//Initialize the wrapper. Handle is a handle to the appfs file, part_start and part_size
//must refer to the offset and the size of the appfs partition.
void appfs_wrapper_init(appfs_handle_t handle, size_t part_start, size_t part_size);
//Un-initialize the wrapper. Flash access will always access raw flash after this.
void appfs_wrapper_deinit();
#endif

View file

@ -0,0 +1,138 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef BOOTLOADER_BUILD
#include <stdbool.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "bootloader_init.h"
#include "bootloader_utility.h"
#include "bootloader_common.h"
#include "appfs.h"
#include "bootloader_flash_priv.h"
#include "appfs_flashfunctions_wrapper.h"
static const char *TAG="bootloader";
//Copied from kchal, which we don't want to link inhere.
//See 8bkc-hal/kchal.c for explanation of the bits in the store0 register
static int appfs_get_new_app() {
uint32_t r=REG_READ(RTC_CNTL_STORE0_REG);
ESP_LOGI(TAG, "RTC store0 reg: %x", r);
if ((r&0xFF000000)!=0xA5000000) return -1;
return r&0xff;
}
//Find the position/size of the appfs partition
static bool find_appfs_part(size_t *pos, size_t *len) {
const esp_partition_info_t *partitions = bootloader_mmap(ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
if (!partitions) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
return false;
}
ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_OFFSET, (intptr_t)partitions);
int num_partitions;
esp_err_t err = esp_partition_table_verify(partitions, true, &num_partitions);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify partition table");
return false;
}
bool found=false;
for (int i = 0; i < num_partitions; i++) {
const esp_partition_info_t *partition = &partitions[i];
if (partition->type==APPFS_PART_TYPE && partition->subtype==APPFS_PART_SUBTYPE) {
*pos=partition->pos.offset;
*len=partition->pos.size;
found=true;
}
}
bootloader_munmap(partitions);
return found;
}
/*
* We arrive here after the ROM bootloader finished loading this second stage bootloader from flash.
* The hardware is mostly uninitialized, flash cache is down and the app CPU is in reset.
* We do have a stack, so we can do the initialization in C.
*/
void __attribute__((noreturn)) call_start_cpu0(void)
{
// Hardware initialization
if (bootloader_init() != ESP_OK) {
bootloader_reset();
}
bootloader_state_t bs = {0};
if (!bootloader_utility_load_partition_table(&bs)) {
ESP_LOGE(TAG, "load partition table error!");
bootloader_reset();
}
size_t appfs_pos, appfs_len;
if (!find_appfs_part(&appfs_pos, &appfs_len)) {
ESP_LOGE(TAG, "No appfs found!");
goto error;
}
//We have an appfs
ESP_LOGI(TAG, "AppFs found @ offset 0x%X", appfs_pos);
esp_err_t err=appfsBlInit(appfs_pos, appfs_len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "AppFs initialization failed");
appfsBlDeinit();
goto error;
}
ESP_LOGI(TAG, "AppFs initialized");
int app=appfs_get_new_app();
appfs_handle_t handle=0;
if (app<0) {
//Load default app
handle=appfsOpen("chooser.app");
} else {
handle=app;
}
if (handle==APPFS_INVALID_FD) {
ESP_LOGE(TAG, "Couldn't open app (%d)!", app);
appfsBlDeinit();
goto error;
}
ESP_LOGI(TAG, "Wrapping flash functions and booting app...");
appfs_wrapper_init(handle, appfs_pos, appfs_len);
//Note that the rest of the bootloader code has no clue about appfs, and as such won't try
//to boot it. We 'fix' that by chucking the appfs partition (which is now wrapped so the rest
//of the bootloader reads from the selected file when it thinks it loads from the app) into
//the top OTA slot.
bs.ota[0].offset=appfs_pos;
bs.ota[0].size=appfs_len;
bs.app_count=1;
//And bingo bango, we can now boot from appfs as if it is the first ota partition.
bootloader_utility_load_boot_image(&bs, 0);
//Still here? Must be an error.
error:
//Try to fallback to factory part
bootloader_utility_load_boot_image(&bs, -1);
ESP_LOGE(TAG, "Bootloader end");
bootloader_reset();
}
// Return global reent struct if any newlib functions are linked to bootloader
struct _reent *__getreent(void)
{
return _GLOBAL_REENT;
}
#endif

View file

@ -1,5 +1,6 @@
idf_component_register(
INCLUDE_DIRS "include" idf_component_register(SRC_DIRS .
SRCS "appfs.c" REQUIRES log spi_flash bootloader_support
REQUIRES spi_flash INCLUDE_DIRS . )
)
target_compile_options(${COMPONENT_LIB} PRIVATE -DPROJECT_NAME="${CMAKE_PROJECT_NAME}")

View file

@ -1,12 +1,3 @@
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
/* /*
Theory of operation: Theory of operation:
An appfs filesystem is meant to store executable applications (=ESP32 programs) alongside other An appfs filesystem is meant to store executable applications (=ESP32 programs) alongside other
@ -58,13 +49,13 @@ is implemented as a singleton.
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <alloca.h> #include <alloca.h>
#include "esp32/rom/crc.h" #include <rom/crc.h>
#include "esp32/rom/cache.h"
#include "esp_spi_flash.h" #include "esp_spi_flash.h"
#include "esp_partition.h" #include "esp_partition.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_err.h" #include "esp_err.h"
#include "appfs.h" #include "appfs.h"
#include "rom/cache.h"
#include "sdkconfig.h" #include "sdkconfig.h"
@ -177,7 +168,9 @@ static esp_err_t findActiveMeta() {
return ESP_OK; return ESP_OK;
} }
#ifdef BOOTLOADER_BUILD
IRAM_ATTR
#endif
static int appfsGetFirstPageFor(const char *filename) { static int appfsGetFirstPageFor(const char *filename) {
for (int j=0; j<APPFS_PAGES; j++) { for (int j=0; j<APPFS_PAGES; j++) {
if (appfsMeta[appfsActiveMeta].page[j].used==APPFS_USE_DATA && strcmp(appfsMeta[appfsActiveMeta].page[j].name, filename)==0) { if (appfsMeta[appfsActiveMeta].page[j].used==APPFS_USE_DATA && strcmp(appfsMeta[appfsActiveMeta].page[j].name, filename)==0) {
@ -241,19 +234,34 @@ size_t appfsGetFreeMem() {
#ifdef BOOTLOADER_BUILD #ifdef BOOTLOADER_BUILD
#include "bootloader_flash.h" /*
Note that IRAM_ATTR is used here to make sure the functions that are used when/after the app loadable
segments are loaded, won't be overwritten. The IRAM_ATTR in the bootloader code dumps the function
in the loader segment instead of in random IRAM.
*/
#include "bootloader_flash_priv.h"
#include "soc/soc.h" #include "soc/soc.h"
#include "soc/cpu.h" #include "esp_cpu.h"
#include "soc/rtc.h" #include "soc/rtc.h"
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
//These can be overridden if we need a custom implementation of bootloader_mmap/munmap.
IRAM_ATTR __attribute__ ((weak)) const void *appfs_bootloader_mmap(uint32_t src_addr, uint32_t size) {
return bootloader_mmap(src_addr, size);
}
IRAM_ATTR __attribute__ ((weak)) void appfs_bootloader_munmap(const void *mapping) {
bootloader_munmap(mapping);
}
esp_err_t appfsBlInit(uint32_t offset, uint32_t len) { esp_err_t appfsBlInit(uint32_t offset, uint32_t len) {
//Compile-time sanity check on size of structs //Compile-time sanity check on size of structs
_Static_assert(sizeof(AppfsHeader)==APPFS_META_DESC_SZ, "sizeof AppfsHeader != 128bytes"); _Static_assert(sizeof(AppfsHeader)==APPFS_META_DESC_SZ, "sizeof AppfsHeader != 128bytes");
_Static_assert(sizeof(AppfsPageInfo)==APPFS_META_DESC_SZ, "sizeof AppfsPageInfo != 128bytes"); _Static_assert(sizeof(AppfsPageInfo)==APPFS_META_DESC_SZ, "sizeof AppfsPageInfo != 128bytes");
_Static_assert(sizeof(AppfsMeta)==APPFS_META_SZ, "sizeof AppfsMeta != APPFS_META_SZ"); _Static_assert(sizeof(AppfsMeta)==APPFS_META_SZ, "sizeof AppfsMeta != APPFS_META_SZ");
//Map meta page //Map meta page
appfsMeta=bootloader_mmap(offset, APPFS_SECTOR_SZ); appfsMeta=appfs_bootloader_mmap(offset, APPFS_SECTOR_SZ);
if (!appfsMeta) return ESP_ERR_NOT_FOUND; if (!appfsMeta) return ESP_ERR_NOT_FOUND;
if (findActiveMeta()!=ESP_OK) { if (findActiveMeta()!=ESP_OK) {
//No valid metadata half-sector found. Initialize the first sector. //No valid metadata half-sector found. Initialize the first sector.
@ -266,15 +274,15 @@ esp_err_t appfsBlInit(uint32_t offset, uint32_t len) {
} }
void appfsBlDeinit() { void appfsBlDeinit() {
bootloader_munmap(appfsMeta); ESP_LOGI(TAG, "Appfs deinit");
appfs_bootloader_munmap(appfsMeta);
} }
#define MMU_BLOCK0_VADDR 0x3f400000 #define MMU_BLOCK0_VADDR 0x3f400000
#define MMU_BLOCK50_VADDR 0x3f720000 #define MMU_BLOCK50_VADDR 0x3f720000
#define MMU_FLASH_MASK 0xffff0000
#define MMU_BLOCK_SIZE 0x00010000
esp_err_t appfsBlMapRegions(int fd, AppfsBlRegionToMap *regions, int noRegions) { IRAM_ATTR esp_err_t appfsBlMapRegions(int fd, AppfsBlRegionToMap *regions, int noRegions) {
if (appfsMeta==NULL) ESP_LOGE(TAG, "EEK! appfsBlMapRegions called without meta mapped");
uint8_t pages[255]; uint8_t pages[255];
int pageCt=0; int pageCt=0;
int page=fd; int page=fd;
@ -283,7 +291,7 @@ esp_err_t appfsBlMapRegions(int fd, AppfsBlRegionToMap *regions, int noRegions)
page=appfsMeta[appfsActiveMeta].page[page].next; page=appfsMeta[appfsActiveMeta].page[page].next;
} while (page!=0); } while (page!=0);
//Okay, we have our info. //Okay, we have our info.
bootloader_munmap(appfsMeta); appfs_bootloader_munmap(appfsMeta);
Cache_Read_Disable( 0 ); Cache_Read_Disable( 0 );
Cache_Flush( 0 ); Cache_Flush( 0 );
@ -314,9 +322,10 @@ esp_err_t appfsBlMapRegions(int fd, AppfsBlRegionToMap *regions, int noRegions)
return ESP_OK; return ESP_OK;
} }
void* appfsBlMmap(int fd) { IRAM_ATTR void* appfsBlMmap(int fd) {
//We want to mmap() the pages of the file into memory. However, to do that we need to kill the mmap for the //We want to mmap() the pages of the file into memory. However, to do that we need to kill the mmap for the
//meta info. To do this, we collect the pages before unmapping the meta info. //meta info. To do this, we collect the pages before unmapping the meta info.
if (appfsMeta==NULL) ESP_LOGE(TAG, "EEK! appfsBlMmap called without meta mapped");
uint8_t pages[255]; uint8_t pages[255];
int pageCt=0; int pageCt=0;
int page=fd; int page=fd;
@ -324,7 +333,7 @@ void* appfsBlMmap(int fd) {
pages[pageCt++]=page; pages[pageCt++]=page;
page=appfsMeta[appfsActiveMeta].page[page].next; page=appfsMeta[appfsActiveMeta].page[page].next;
} while (page!=0); } while (page!=0);
ESP_LOGI(TAG, "File %d has %d pages.", fd, pageCt); // ESP_LOGI(TAG, "File %d has %d pages.", fd, pageCt);
if (pageCt>50) { if (pageCt>50) {
ESP_LOGE(TAG, "appfsBlMmap: file too big to mmap"); ESP_LOGE(TAG, "appfsBlMmap: file too big to mmap");
@ -332,14 +341,14 @@ void* appfsBlMmap(int fd) {
} }
//Okay, we have our info. //Okay, we have our info.
bootloader_munmap(appfsMeta); appfs_bootloader_munmap(appfsMeta);
//Bootloader_mmap only allows mapping of one consecutive memory range. We need more than that, so we essentially //Bootloader_mmap only allows mapping of one consecutive memory range. We need more than that, so we essentially
//replicate the function here. //replicate the function here.
Cache_Read_Disable(0); Cache_Read_Disable(0);
Cache_Flush(0); Cache_Flush(0);
for (int i=0; i<pageCt; i++) { for (int i=0; i<pageCt; i++) {
ESP_LOGI(TAG, "Mapping flash addr %X to mem addr %X for page %d", appfsPartOffset+((pages[i]+1)*APPFS_SECTOR_SZ), MMU_BLOCK0_VADDR+(i*APPFS_SECTOR_SZ), pages[i]); // ESP_LOGI(TAG, "Mapping flash addr %X to mem addr %X for page %d", appfsPartOffset+((pages[i]+1)*APPFS_SECTOR_SZ), MMU_BLOCK0_VADDR+(i*APPFS_SECTOR_SZ), pages[i]);
int e = cache_flash_mmu_set(0, 0, MMU_BLOCK0_VADDR+(i*APPFS_SECTOR_SZ), int e = cache_flash_mmu_set(0, 0, MMU_BLOCK0_VADDR+(i*APPFS_SECTOR_SZ),
appfsPartOffset+((pages[i]+1)*APPFS_SECTOR_SZ), 64, 1); appfsPartOffset+((pages[i]+1)*APPFS_SECTOR_SZ), 64, 1);
if (e != 0) { if (e != 0) {
@ -352,13 +361,13 @@ void* appfsBlMmap(int fd) {
return (void *)(MMU_BLOCK0_VADDR); return (void *)(MMU_BLOCK0_VADDR);
} }
void appfsBlMunmap() { IRAM_ATTR void appfsBlMunmap() {
/* Full MMU reset */ /* Full MMU reset */
Cache_Read_Disable(0); Cache_Read_Disable(0);
Cache_Flush(0); Cache_Flush(0);
mmu_init(0); mmu_init(0);
//Map meta page //Map meta page
appfsMeta=bootloader_mmap(appfsPartOffset, APPFS_SECTOR_SZ); appfsMeta=appfs_bootloader_mmap(appfsPartOffset, APPFS_SECTOR_SZ);
} }
#else //so if !BOOTLOADER_BUILD #else //so if !BOOTLOADER_BUILD

View file

@ -1,13 +1,5 @@
/* #ifndef APPFS_H
* ---------------------------------------------------------------------------- #define APPFS_H
* "THE BEER-WARE LICENSE" (Revision 42):
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain
* this notice you can do whatever you want with this stuff. If we meet some day,
* and you think this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
#pragma once
#include "esp_err.h" #include "esp_err.h"
#include <stdint.h> #include <stdint.h>
@ -230,7 +222,7 @@ void appfsDump();
#ifdef BOOTLOADER_BUILD #ifdef BOOTLOADER_BUILD
#include "bootloader_flash.h" #include "bootloader_flash_priv.h"
/** /**
* @brief Appfs bootloader support: struct to hold a region of a file to map * @brief Appfs bootloader support: struct to hold a region of a file to map
@ -287,3 +279,6 @@ esp_err_t appfsBlMapRegions(int fd, AppfsBlRegionToMap *regions, int noRegions);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif

View file

@ -1,9 +0,0 @@
#
# Component Makefile. By default,
# this will take the sources in this directory, compile them and link them into
# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable,
# please read the SDK documents if you need to do this.
#
COMPONENT_ADD_INCLUDEDIRS := include

View file

@ -3,7 +3,6 @@
#include <esp_log.h> #include <esp_log.h>
#include <driver/gpio.h> #include <driver/gpio.h>
#include "managed_i2c.h" #include "managed_i2c.h"
#include "logo.h"
#include "sdcard.h" #include "sdcard.h"
static const char *TAG = "hardware"; static const char *TAG = "hardware";
@ -121,13 +120,6 @@ esp_err_t hardware_init() {
return res; return res;
} }
// Hack: show logo while the other hardware components initialize
res = ili9341_write(&dev_ili9341, logo);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to write logo to LCD");
return res;
}
// BNO055 sensor on system I2C bus // BNO055 sensor on system I2C bus
res = bno055_init(&dev_bno055, I2C_BUS_SYS, BNO055_ADDR, GPIO_INT_BNO055, true); res = bno055_init(&dev_bno055, I2C_BUS_SYS, BNO055_ADDR, GPIO_INT_BNO055, true);
@ -136,8 +128,6 @@ esp_err_t hardware_init() {
return res; return res;
} }
//res = mount_sd(SD_CMD, SD_CLK, SD_D0, SD_PWR, "/sd", false, 5);
return res; return res;
} }

File diff suppressed because it is too large Load diff

View file

@ -30,43 +30,47 @@ bno055_vector_t rotation_offset = {.x = 0, .y = 0, .z = 0};
bno055_vector_t acceleration, magnetism, orientation, rotation, linear_acceleration, gravity; bno055_vector_t acceleration, magnetism, orientation, rotation, linear_acceleration, gravity;
uint8_t selected_item = 0;
void button_handler(uint8_t pin, bool value) { void button_handler(uint8_t pin, bool value) {
switch(pin) { switch(pin) {
case PCA9555_PIN_BTN_JOY_LEFT: case PCA9555_PIN_BTN_JOY_LEFT:
printf("Joystick horizontal %s\n", value ? "left" : "center"); printf("Joystick horizontal %s\n", value ? "left" : "center");
ili9341_set_partial_scanning(ili9341, 60, ILI9341_WIDTH - 61); //ili9341_set_partial_scanning(ili9341, 60, ILI9341_WIDTH - 61);
break; break;
case PCA9555_PIN_BTN_JOY_PRESS: case PCA9555_PIN_BTN_JOY_PRESS:
printf("Joystick %s\n", value ? "pressed" : "released"); printf("Joystick %s\n", value ? "pressed" : "released");
break; break;
case PCA9555_PIN_BTN_JOY_DOWN: case PCA9555_PIN_BTN_JOY_DOWN:
printf("Joystick vertical %s\n", value ? "down" : "center"); printf("Joystick vertical %s\n", value ? "down" : "center");
ili9341_set_partial_scanning(ili9341, 0, ILI9341_WIDTH / 2 - 1); //ili9341_set_partial_scanning(ili9341, 0, ILI9341_WIDTH / 2 - 1);
if (value) selected_item += 1;
break; break;
case PCA9555_PIN_BTN_JOY_UP: case PCA9555_PIN_BTN_JOY_UP:
printf("Joy vertical %s\n", value ? "up" : "center"); printf("Joy vertical %s\n", value ? "up" : "center");
ili9341_set_partial_scanning(ili9341, ILI9341_WIDTH / 2, ILI9341_WIDTH - 1); //ili9341_set_partial_scanning(ili9341, ILI9341_WIDTH / 2, ILI9341_WIDTH - 1);
if (value) selected_item -= 1;
break; break;
case PCA9555_PIN_BTN_JOY_RIGHT: case PCA9555_PIN_BTN_JOY_RIGHT:
printf("Joy horizontal %s\n", value ? "right" : "center"); printf("Joy horizontal %s\n", value ? "right" : "center");
ili9341_set_partial_scanning(ili9341, 0, ILI9341_WIDTH - 1); //ili9341_set_partial_scanning(ili9341, 0, ILI9341_WIDTH - 1);
break; break;
case PCA9555_PIN_BTN_HOME: case PCA9555_PIN_BTN_HOME:
printf("Home button %s\n", value ? "pressed" : "released"); printf("Home button %s\n", value ? "pressed" : "released");
ili9341_set_tearing_effect_line(ili9341, true); //ili9341_set_tearing_effect_line(ili9341, true);
break; break;
case PCA9555_PIN_BTN_MENU: case PCA9555_PIN_BTN_MENU:
printf("Menu button %s\n", value ? "pressed" : "released"); printf("Menu button %s\n", value ? "pressed" : "released");
ili9341_set_tearing_effect_line(ili9341, false); //ili9341_set_tearing_effect_line(ili9341, false);
break; break;
case PCA9555_PIN_BTN_START: { case PCA9555_PIN_BTN_START: {
printf("Start button %s\n", value ? "pressed" : "released"); printf("Start button %s\n", value ? "pressed" : "released");
ili9341_set_idle_mode(ili9341, true); //ili9341_set_idle_mode(ili9341, true);
break; break;
} }
case PCA9555_PIN_BTN_SELECT: { case PCA9555_PIN_BTN_SELECT: {
printf("Select button %s\n", value ? "pressed" : "released"); printf("Select button %s\n", value ? "pressed" : "released");
ili9341_set_idle_mode(ili9341, false); //ili9341_set_idle_mode(ili9341, false);
break; break;
} }
case PCA9555_PIN_BTN_BACK: case PCA9555_PIN_BTN_BACK:
@ -174,42 +178,78 @@ void bno055_task(BNO055* bno055) {
} }
} }
void draw_cursor(pax_buf_t* buffer, float x, float y) {
uint64_t millis = esp_timer_get_time() / 1000;
pax_col_t color = pax_col_hsv(millis * 255 / 8000, 255, 255);
pax_push_2d(buffer);
pax_apply_2d(buffer, matrix_2d_translate(x, y));
pax_apply_2d(buffer, matrix_2d_scale(10, 10));
pax_draw_tri(buffer, color, -1, -1, -1, 1, 1, 0);
pax_pop_2d(buffer);
}
void draw_menu_item(pax_buf_t* buffer, uint8_t position, bool selected, char* text) {
float y = 24 + position * 20;
if (selected) draw_cursor(buffer, 15, y + 9);
pax_draw_text(buffer, pax_col_rgb(0,0,0), PAX_FONT_DEFAULT, 18, 24, y, text);
}
esp_err_t draw_menu(pax_buf_t* buffer) { esp_err_t draw_menu(pax_buf_t* buffer) {
pax_push_2d(buffer); pax_push_2d(buffer);
pax_simple_line(buffer, pax_col_rgb(255,0,0), 0, 16, 320, 16); //pax_apply_2d(buffer, matrix_2d_translate(0, 0));
pax_draw_text(buffer, pax_col_rgb(255,255,0), PAX_FONT_DEFAULT, 0.2, 0, 0, "Hello world!"); //pax_apply_2d(buffer, matrix_2d_scale(1, 1));
pax_simple_line(buffer, pax_col_rgb(0,0,0), 0, 20, 320, 20);
pax_draw_text(buffer, pax_col_rgb(0,0,0), PAX_FONT_DEFAULT, 18, 0, 0, "Launcher");
draw_menu_item(buffer, 0, (selected_item == 0), "Test 1");
draw_menu_item(buffer, 1, (selected_item == 1), "Hey, this almost looks like");
draw_menu_item(buffer, 2, (selected_item == 2), "a menu list?!");
draw_menu_item(buffer, 3, (selected_item == 3), "Woooow!");
draw_menu_item(buffer, 4, (selected_item == 4), "Blahblah");
draw_menu_item(buffer, 5, (selected_item == 5), "8=======D~~~~~~");
draw_menu_item(buffer, 6, (selected_item == 6), "Does this fit on the screen or not?");
pax_pop_2d(buffer); pax_pop_2d(buffer);
return ESP_OK; return ESP_OK;
} }
pax_col_t regenboogkots(pax_col_t tint, int x, int y, float u, float v, void *args) {
return pax_col_hsv(x / 50.0 * 255.0 + y / 150.0 * 255.0, 255, 255);
}
pax_shader_t kots = {
.callback = regenboogkots
};
esp_err_t graphics_task(pax_buf_t* buffer, ILI9341* ili9341, uint8_t* framebuffer) { esp_err_t graphics_task(pax_buf_t* buffer, ILI9341* ili9341, uint8_t* framebuffer) {
uint64_t millis = esp_timer_get_time() / 1000; pax_background(buffer, 0xFFFFFF);
pax_background(buffer, 0x000000); //pax_shade_rect(buffer, 0, &kots, NULL, 0, 0, 320, 240);
pax_col_t color0 = pax_col_hsv(millis * 255 / 8000, 255, 255);
pax_col_t color1 = pax_col_hsv(millis * 255 / 8000 + 127, 255, 255);
float a0 = millis / 3000.0 * M_PI;//0;//
float a1 = fmodf(a0, M_PI * 4) - M_PI * 2;//rotation.y * (M_PI / 180.0);//
pax_draw_arc(buffer, color0, 0, 0, 1, a0, a0 + a1);
pax_push_2d(buffer); pax_push_2d(buffer);
pax_apply_2d(buffer, matrix_2d_translate(buffer->width / 2.0, buffer->height / 2.0 + 10));
pax_apply_2d(buffer, matrix_2d_rotate(a0)); pax_apply_2d(buffer, matrix_2d_scale(50, 50));
pax_push_2d(buffer); uint64_t millis = esp_timer_get_time() / 1000;
pax_apply_2d(buffer, matrix_2d_translate(1, 0)); pax_col_t color0 = pax_col_hsv(millis * 255 / 8000, 255, 255);
pax_draw_rect(buffer, color1, -0.25, -0.25, 0.5, 0.5); //pax_col_t color1 = pax_col_hsv(millis * 255 / 8000 + 127, 255, 255);
pax_pop_2d(buffer); float a0 = rotation.y * (M_PI / 360.0);//millis / 3000.0 * M_PI;//0;//
//printf("%f from %f\n", a0, rotation.y);
pax_apply_2d(buffer, matrix_2d_rotate(a1)); //float a1 = fmodf(a0, M_PI * 4) - M_PI * 2;////
pax_push_2d(buffer); //pax_draw_arc(buffer, color0, 0, 0, 2, a0 + M_PI, a0);
pax_apply_2d(buffer, matrix_2d_translate(1, 0)); /*pax_push_2d(buffer);
pax_apply_2d(buffer, matrix_2d_rotate(-a0 - a1 + M_PI * 0.5)); pax_apply_2d(buffer, matrix_2d_rotate(a0));
pax_draw_tri(buffer, color1, 0.25, 0, -0.125, 0.2165, -0.125, -0.2165); pax_push_2d(buffer);
pax_pop_2d(buffer); pax_apply_2d(buffer, matrix_2d_translate(1, 0));
pax_draw_rect(buffer, color1, -0.25, -0.25, 0.5, 0.5);
pax_pop_2d(buffer);
pax_apply_2d(buffer, matrix_2d_rotate(a1));
pax_push_2d(buffer);
pax_apply_2d(buffer, matrix_2d_translate(1, 0));
pax_apply_2d(buffer, matrix_2d_rotate(-a0 - a1 + M_PI * 0.5));
pax_draw_tri(buffer, color1, 0.25, 0, -0.125, 0.2165, -0.125, -0.2165);
pax_pop_2d(buffer);
pax_pop_2d(buffer);*/
pax_pop_2d(buffer); pax_pop_2d(buffer);
draw_menu(buffer); draw_menu(buffer);
driver_framebuffer_print(NULL, "Hello world", 0, 0, 1, 1, 0xFF00FF, &ocra_22pt7b); //driver_framebuffer_print(NULL, "Hello world", 0, 0, 1, 1, 0xFF00FF, &ocra_22pt7b);
return ili9341_write(ili9341, framebuffer); return ili9341_write(ili9341, framebuffer);
} }
@ -418,6 +458,9 @@ void appfs_store_app(void) {
ESP_LOGE(TAG, "Failed to load application into RAM"); ESP_LOGE(TAG, "Failed to load application into RAM");
return; return;
} }
ESP_LOGI(TAG, "Application size %d", app_size);
res = appfsCreateFile("gnuboy", app_size, &handle); res = appfsCreateFile("gnuboy", app_size, &handle);
if (res != ESP_OK) { if (res != ESP_OK) {
ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res); ESP_LOGE(TAG, "Failed to create file on AppFS (%d)", res);
@ -448,6 +491,7 @@ void appfs_boot_app(int fd) {
} }
void appfs_test(bool sdcard_ready) { void appfs_test(bool sdcard_ready) {
appfs_store_app();
appfsDump(); appfsDump();
// Try booting the app from appfs // Try booting the app from appfs
@ -460,7 +504,7 @@ void appfs_test(bool sdcard_ready) {
appfs_test(false); // Recursive, but who cares :D appfs_test(false); // Recursive, but who cares :D
} }
} else { } else {
ESP_LOGE(TAG, "booting gnuboy from appfs"); ESP_LOGE(TAG, "booting gnuboy from appfs (%d)", fd);
appfs_boot_app(fd); appfs_boot_app(fd);
} }
} }
@ -468,18 +512,6 @@ void appfs_test(bool sdcard_ready) {
void app_main(void) { void app_main(void) {
esp_err_t res; esp_err_t res;
res = hardware_init();
if (res != ESP_OK) {
printf("Failed to initialize hardware!\n");
restart();
}
ili9341 = get_ili9341();
ice40 = get_ice40();
bno055 = get_bno055();
print_chip_info();
uint8_t* framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT); uint8_t* framebuffer = heap_caps_malloc(ILI9341_BUFFER_SIZE, MALLOC_CAP_8BIT);
if (framebuffer == NULL) { if (framebuffer == NULL) {
ESP_LOGE(TAG, "Failed to allocate framebuffer"); ESP_LOGE(TAG, "Failed to allocate framebuffer");
@ -489,11 +521,21 @@ void app_main(void) {
pax_buf_t buffer; pax_buf_t buffer;
pax_buf_init(&buffer, framebuffer, ILI9341_WIDTH, ILI9341_HEIGHT, PAX_BUF_16_565RGB); pax_buf_init(&buffer, framebuffer, ILI9341_WIDTH, ILI9341_HEIGHT, PAX_BUF_16_565RGB);
pax_apply_2d(&buffer, matrix_2d_translate(buffer.width / 2.0, buffer.height / 2.0));
pax_apply_2d(&buffer, matrix_2d_scale(50, 50));
driver_framebuffer_init(framebuffer); driver_framebuffer_init(framebuffer);
res = hardware_init();
if (res != ESP_OK) {
printf("Failed to initialize hardware!\n");
restart();
}
ili9341 = get_ili9341();
ice40 = get_ice40();
bno055 = get_bno055();
//print_chip_info();
button_init(); button_init();
res = appfs_init(); res = appfs_init();
@ -506,19 +548,22 @@ void app_main(void) {
res = mount_sd(SD_CMD, SD_CLK, SD_D0, SD_PWR, "/sd", false, 5); res = mount_sd(SD_CMD, SD_CLK, SD_D0, SD_PWR, "/sd", false, 5);
bool sdcard_ready = (res == ESP_OK); bool sdcard_ready = (res == ESP_OK);
ili9341_deinit(ili9341);
ili9341_select(ili9341, true);
if (sdcard_ready) { if (sdcard_ready) {
ESP_LOGI(TAG, "SD card mounted"); ESP_LOGI(TAG, "SD card mounted");
//fpga_test(); //fpga_test();
} }
//appfs_test(sdcard_ready); appfs_test(sdcard_ready);
// //
while (1) { /*while (1) {
bno055_task(bno055); bno055_task(bno055);
graphics_task(&buffer, ili9341, framebuffer); graphics_task(&buffer, ili9341, framebuffer);
} }*/
/* /*
uint8_t data_out, data_in; uint8_t data_out, data_in;

View file

@ -1,8 +1,6 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000, # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
otadata, data, ota, 0xd000, 0x2000 nvs, data, nvs, 0xD000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, 0xF000, 0x1000,
ota_0, app, ota_0, 0x10000, 1536K factory, app, factory, 0x10000, 512K,
ota_1, app, ota_1, , 1536K appfs, 0x43, 0x3, , 3M,
appfs, 0x43, 3, , 13248K

1 # ESP-IDF Partition Table # Name, Type, SubType, Offset, Size, Flags
# ESP-IDF Partition Table
1 # Name, Type, SubType, Offset, Size, Flags # Name, Type, SubType, Offset, Size, Flags
2 nvs, data, nvs, 0x9000, 0x4000, # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 otadata, data, ota, 0xd000, 0x2000 nvs, data, nvs, 0xD000, 0x2000,
4 phy_init, data, phy, 0xf000, 0x1000, phy_init, data, phy, 0xF000, 0x1000,
5 ota_0, app, ota_0, 0x10000, 1536K factory, app, factory, 0x10000, 512K,
6 ota_1, app, ota_1, , 1536K appfs, 0x43, 0x3, , 3M,
appfs, 0x43, 3, , 13248K

View file

@ -0,0 +1,9 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0xA000, 12K,
otadata, data, ota, 0xD000, 8K,
phy_init, data, phy, 0xF000, 4K,
ota_0, 0, ota_0, 0x10000, 1600K,
ota_1, 0, ota_1, 0x1A0000, 1600K,
appfs, 0x43, 3, 0x330000, 8000K,
locfd, data, fat, 0xB00000, 5120K,

BIN
factory_test/partitions.ods Normal file

Binary file not shown.

View file

@ -265,7 +265,7 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200
CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_OFFSET=0x9000
CONFIG_PARTITION_TABLE_MD5=y CONFIG_PARTITION_TABLE_MD5=y
# end of Partition Table # end of Partition Table