Skip to content

Commit

Permalink
Backport esp8266#7514 (part1)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason2866 committed Oct 19, 2020
1 parent b10fb54 commit d4c3ef0
Show file tree
Hide file tree
Showing 4 changed files with 344 additions and 189 deletions.
269 changes: 239 additions & 30 deletions cores/esp8266/Esp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
#include "MD5Builder.h"
#include "umm_malloc/umm_malloc.h"
#include "cont.h"

#include "coredecls.h"
#include <pgmspace.h>

extern "C" {
#include "user_interface.h"
Expand All @@ -40,11 +42,6 @@ extern struct rst_info resetInfo;
#ifndef PUYA_SUPPORT
#define PUYA_SUPPORT 1
#endif
#ifndef PUYA_BUFFER_SIZE
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
// Always use a multiple of flash page size (256 bytes)
#define PUYA_BUFFER_SIZE 256
#endif

/**
* User-defined Literals
Expand Down Expand Up @@ -264,13 +261,6 @@ uint8_t EspClass::getBootMode(void)
return system_get_boot_mode();
}

#ifndef F_CPU
uint8_t EspClass::getCpuFreqMHz(void)
{
return system_get_cpu_freq();
}
#endif

uint32_t EspClass::getFlashChipId(void)
{
static uint32_t flash_chip_id = 0;
Expand Down Expand Up @@ -673,11 +663,14 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
if (data == nullptr) {
return SPI_FLASH_RESULT_ERR;
}
if (size % 4 != 0) {
return SPI_FLASH_RESULT_ERR;
}
// PUYA flash chips need to read existing data, update in memory and write modified data again.
static uint32_t *flash_write_puya_buf = nullptr;

if (flash_write_puya_buf == nullptr) {
flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE);
flash_write_puya_buf = (uint32_t*) malloc(FLASH_PAGE_SIZE);
// No need to ever free this, since the flash chip will never change at runtime.
if (flash_write_puya_buf == nullptr) {
// Memory could not be allocated.
Expand All @@ -691,45 +684,261 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
uint32_t pos = offset;
while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
size_t bytesNow = bytesLeft;
if (bytesNow > PUYA_BUFFER_SIZE) {
bytesNow = PUYA_BUFFER_SIZE;
bytesLeft -= PUYA_BUFFER_SIZE;
if (bytesNow > FLASH_PAGE_SIZE) {
bytesNow = FLASH_PAGE_SIZE;
bytesLeft -= FLASH_PAGE_SIZE;
} else {
bytesLeft = 0;
}
size_t bytesAligned = (bytesNow + 3) & ~3;
rc = spi_flash_read(pos, flash_write_puya_buf, bytesAligned);
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
if (rc != SPI_FLASH_RESULT_OK) {
return rc;
}
for (size_t i = 0; i < bytesAligned / 4; ++i) {
for (size_t i = 0; i < bytesNow / 4; ++i) {
flash_write_puya_buf[i] &= *ptr;
++ptr;
}
rc = spi_flash_write(pos, flash_write_puya_buf, bytesAligned);
rc = spi_flash_write(pos, flash_write_puya_buf, bytesNow);
pos += bytesNow;
}
return rc;
}
#endif

bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
bool EspClass::flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount) {
uint32_t alignedAddress = (address & ~3);
uint32_t alignmentOffset = address - alignedAddress;

if (alignedAddress != ((address + byteCount - 1) & ~3)) {
// Only one 4 byte block is supported
return false;
}
#if PUYA_SUPPORT
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
uint8_t tempData[4] __attribute__((aligned(4)));
if (spi_flash_read(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
for (size_t i = 0; i < byteCount; i++) {
tempData[i + alignmentOffset] &= value[i];
}
if (spi_flash_write(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
}
else
#endif // PUYA_SUPPORT
{
uint32_t tempData;
if (spi_flash_read(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
memcpy((uint8_t *)&tempData + alignmentOffset, value, byteCount);
if (spi_flash_write(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
}
return true;
}

size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size) {
size_t sizeLeft = (size & ~3);
size_t currentOffset = 0;
// Memory is unaligned, so we need to copy it to an aligned buffer
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
// Handle page boundary
bool pageBreak = ((address % 4) != 0) && ((address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));

if (pageBreak) {
size_t byteCount = 4 - (address % 4);

if (!flashReplaceBlock(address, data, byteCount)) {
return 0;
}
// We will now have aligned address, so we can cross page boundaries
currentOffset += byteCount;
// Realign size to 4
sizeLeft = (size - byteCount) & ~3;
}

while (sizeLeft) {
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
memcpy(alignedData, data + currentOffset, willCopy);
// We now have address, data and size aligned to 4 bytes, so we can use aligned write
if (!flashWrite(address + currentOffset, alignedData, willCopy))
{
return 0;
}
sizeLeft -= willCopy;
currentOffset += willCopy;
}

return currentOffset;
}

bool EspClass::flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size) {
if (size > 4) {
return false;
}
size_t pageLeft = FLASH_PAGE_SIZE - (address % FLASH_PAGE_SIZE);
size_t offset = 0;
size_t sizeLeft = size;
if (pageLeft > 3) {
return false;
}

if (!flashReplaceBlock(address, data, pageLeft)) {
return false;
}
offset += pageLeft;
sizeLeft -= pageLeft;
// We replaced last 4-byte block of the page, now we write the remainder in next page
if (!flashReplaceBlock(address + offset, data + offset, sizeLeft)) {
return false;
}
return true;
}

bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) {
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + size - 1) / FLASH_PAGE_SIZE));

if ((uintptr_t)data % 4 != 0 || size % 4 != 0 || pageBreak) {
return false;
}
#if PUYA_SUPPORT
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
rc = spi_flash_write_puya(offset, data, size);
rc = spi_flash_write_puya(address, const_cast<uint32_t *>(data), size);
}
else
#endif
#endif // PUYA_SUPPORT
{
rc = spi_flash_write(offset, data, size);
rc = spi_flash_write(address, const_cast<uint32_t *>(data), size);
}
return rc == SPI_FLASH_RESULT_OK;
}

bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
auto rc = spi_flash_read(offset, (uint32_t*) data, size);
return rc == SPI_FLASH_RESULT_OK;
bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) {
if (size == 0) {
return true;
}

size_t sizeLeft = size & ~3;
size_t currentOffset = 0;

if (sizeLeft) {
if ((uintptr_t)data % 4 != 0) {
size_t written = flashWriteUnalignedMemory(address, data, size);
if (!written) {
return false;
}
currentOffset += written;
sizeLeft -= written;
} else {
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));

if (pageBreak) {
while (sizeLeft) {
// We cannot cross page boundary, but the write must be 4 byte aligned,
// so this is the maximum amount we can write
size_t pageBoundary = (FLASH_PAGE_SIZE - ((address + currentOffset) % FLASH_PAGE_SIZE)) & ~3;

if (sizeLeft > pageBoundary) {
// Aligned write up to page boundary
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), pageBoundary)) {
return false;
}
currentOffset += pageBoundary;
sizeLeft -= pageBoundary;
// Cross the page boundary
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, 4)) {
return false;
}
currentOffset += 4;
sizeLeft -= 4;
} else {
// We do not cross page boundary
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), sizeLeft)) {
return false;
}
currentOffset += sizeLeft;
sizeLeft = 0;
}
}
} else {
// Pointer is properly aligned and write does not cross page boundary,
// so use aligned write
if (!flashWrite(address, (uint32_t *)data, sizeLeft)) {
return false;
}
currentOffset = sizeLeft;
sizeLeft = 0;
}
}
}
sizeLeft = size - currentOffset;
if (sizeLeft > 0) {
// Size was not aligned, so we have some bytes left to write, we also need to recheck for
// page boundary crossing
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));

if (pageBreak) {
// Cross the page boundary
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, sizeLeft)) {
return false;
}
} else {
// Just write partial block
flashReplaceBlock(address + currentOffset, data + currentOffset, sizeLeft);
}
}

return true;
}

bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) {
size_t sizeAligned = size & ~3;
size_t currentOffset = 0;

if ((uintptr_t)data % 4 != 0) {
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
size_t sizeLeft = sizeAligned;

while (sizeLeft) {
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
// We read to our aligned buffer and then copy to data
if (!flashRead(address + currentOffset, alignedData, willCopy))
{
return false;
}
memcpy(data + currentOffset, alignedData, willCopy);
sizeLeft -= willCopy;
currentOffset += willCopy;
}
} else {
// Pointer is properly aligned, so use aligned read
if (!flashRead(address, (uint32_t *)data, sizeAligned)) {
return false;
}
currentOffset = sizeAligned;
}

if (currentOffset < size) {
uint32_t tempData;
if (spi_flash_read(address + currentOffset, &tempData, 4) != SPI_FLASH_RESULT_OK) {
return false;
}
memcpy((uint8_t *)data + currentOffset, &tempData, size - currentOffset);
}

return true;
}

bool EspClass::flashRead(uint32_t address, uint32_t *data, size_t size) {
if ((uintptr_t)data % 4 != 0 || size % 4 != 0) {
return false;
}
return (spi_flash_read(address, data, size) == SPI_FLASH_RESULT_OK);
}

String EspClass::getSketchMD5()
Expand All @@ -740,17 +949,17 @@ String EspClass::getSketchMD5()
}
uint32_t lengthLeft = getSketchSize();
const size_t bufSize = 512;
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
std::unique_ptr<uint8_t[]> buf(new (std::nothrow) uint8_t[bufSize]);
uint32_t offset = 0;
if(!buf.get()) {
return String();
return emptyString;
}
MD5Builder md5;
md5.begin();
while( lengthLeft > 0) {
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
if (!flashRead(offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) {
return String();
return emptyString;
}
md5.add(buf.get(), readBytes);
lengthLeft -= readBytes;
Expand Down
Loading

0 comments on commit d4c3ef0

Please sign in to comment.