Skip to content

Commit d4c3ef0

Browse files
authored
Backport esp8266#7514 (part1)
1 parent b10fb54 commit d4c3ef0

File tree

4 files changed

+344
-189
lines changed

4 files changed

+344
-189
lines changed

cores/esp8266/Esp.cpp

+239-30
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
#include "MD5Builder.h"
2727
#include "umm_malloc/umm_malloc.h"
2828
#include "cont.h"
29+
2930
#include "coredecls.h"
31+
#include <pgmspace.h>
3032

3133
extern "C" {
3234
#include "user_interface.h"
@@ -40,11 +42,6 @@ extern struct rst_info resetInfo;
4042
#ifndef PUYA_SUPPORT
4143
#define PUYA_SUPPORT 1
4244
#endif
43-
#ifndef PUYA_BUFFER_SIZE
44-
// Good alternative for buffer size is: SPI_FLASH_SEC_SIZE (= 4k)
45-
// Always use a multiple of flash page size (256 bytes)
46-
#define PUYA_BUFFER_SIZE 256
47-
#endif
4845

4946
/**
5047
* User-defined Literals
@@ -264,13 +261,6 @@ uint8_t EspClass::getBootMode(void)
264261
return system_get_boot_mode();
265262
}
266263

267-
#ifndef F_CPU
268-
uint8_t EspClass::getCpuFreqMHz(void)
269-
{
270-
return system_get_cpu_freq();
271-
}
272-
#endif
273-
274264
uint32_t EspClass::getFlashChipId(void)
275265
{
276266
static uint32_t flash_chip_id = 0;
@@ -673,11 +663,14 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
673663
if (data == nullptr) {
674664
return SPI_FLASH_RESULT_ERR;
675665
}
666+
if (size % 4 != 0) {
667+
return SPI_FLASH_RESULT_ERR;
668+
}
676669
// PUYA flash chips need to read existing data, update in memory and write modified data again.
677670
static uint32_t *flash_write_puya_buf = nullptr;
678671

679672
if (flash_write_puya_buf == nullptr) {
680-
flash_write_puya_buf = (uint32_t*) malloc(PUYA_BUFFER_SIZE);
673+
flash_write_puya_buf = (uint32_t*) malloc(FLASH_PAGE_SIZE);
681674
// No need to ever free this, since the flash chip will never change at runtime.
682675
if (flash_write_puya_buf == nullptr) {
683676
// Memory could not be allocated.
@@ -691,45 +684,261 @@ static SpiFlashOpResult spi_flash_write_puya(uint32_t offset, uint32_t *data, si
691684
uint32_t pos = offset;
692685
while (bytesLeft > 0 && rc == SPI_FLASH_RESULT_OK) {
693686
size_t bytesNow = bytesLeft;
694-
if (bytesNow > PUYA_BUFFER_SIZE) {
695-
bytesNow = PUYA_BUFFER_SIZE;
696-
bytesLeft -= PUYA_BUFFER_SIZE;
687+
if (bytesNow > FLASH_PAGE_SIZE) {
688+
bytesNow = FLASH_PAGE_SIZE;
689+
bytesLeft -= FLASH_PAGE_SIZE;
697690
} else {
698691
bytesLeft = 0;
699692
}
700-
size_t bytesAligned = (bytesNow + 3) & ~3;
701-
rc = spi_flash_read(pos, flash_write_puya_buf, bytesAligned);
693+
rc = spi_flash_read(pos, flash_write_puya_buf, bytesNow);
702694
if (rc != SPI_FLASH_RESULT_OK) {
703695
return rc;
704696
}
705-
for (size_t i = 0; i < bytesAligned / 4; ++i) {
697+
for (size_t i = 0; i < bytesNow / 4; ++i) {
706698
flash_write_puya_buf[i] &= *ptr;
707699
++ptr;
708700
}
709-
rc = spi_flash_write(pos, flash_write_puya_buf, bytesAligned);
701+
rc = spi_flash_write(pos, flash_write_puya_buf, bytesNow);
710702
pos += bytesNow;
711703
}
712704
return rc;
713705
}
714706
#endif
715707

716-
bool EspClass::flashWrite(uint32_t offset, uint32_t *data, size_t size) {
708+
bool EspClass::flashReplaceBlock(uint32_t address, const uint8_t *value, uint32_t byteCount) {
709+
uint32_t alignedAddress = (address & ~3);
710+
uint32_t alignmentOffset = address - alignedAddress;
711+
712+
if (alignedAddress != ((address + byteCount - 1) & ~3)) {
713+
// Only one 4 byte block is supported
714+
return false;
715+
}
716+
#if PUYA_SUPPORT
717+
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
718+
uint8_t tempData[4] __attribute__((aligned(4)));
719+
if (spi_flash_read(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
720+
return false;
721+
}
722+
for (size_t i = 0; i < byteCount; i++) {
723+
tempData[i + alignmentOffset] &= value[i];
724+
}
725+
if (spi_flash_write(alignedAddress, (uint32_t *)tempData, 4) != SPI_FLASH_RESULT_OK) {
726+
return false;
727+
}
728+
}
729+
else
730+
#endif // PUYA_SUPPORT
731+
{
732+
uint32_t tempData;
733+
if (spi_flash_read(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
734+
return false;
735+
}
736+
memcpy((uint8_t *)&tempData + alignmentOffset, value, byteCount);
737+
if (spi_flash_write(alignedAddress, &tempData, 4) != SPI_FLASH_RESULT_OK) {
738+
return false;
739+
}
740+
}
741+
return true;
742+
}
743+
744+
size_t EspClass::flashWriteUnalignedMemory(uint32_t address, const uint8_t *data, size_t size) {
745+
size_t sizeLeft = (size & ~3);
746+
size_t currentOffset = 0;
747+
// Memory is unaligned, so we need to copy it to an aligned buffer
748+
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
749+
// Handle page boundary
750+
bool pageBreak = ((address % 4) != 0) && ((address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
751+
752+
if (pageBreak) {
753+
size_t byteCount = 4 - (address % 4);
754+
755+
if (!flashReplaceBlock(address, data, byteCount)) {
756+
return 0;
757+
}
758+
// We will now have aligned address, so we can cross page boundaries
759+
currentOffset += byteCount;
760+
// Realign size to 4
761+
sizeLeft = (size - byteCount) & ~3;
762+
}
763+
764+
while (sizeLeft) {
765+
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
766+
memcpy(alignedData, data + currentOffset, willCopy);
767+
// We now have address, data and size aligned to 4 bytes, so we can use aligned write
768+
if (!flashWrite(address + currentOffset, alignedData, willCopy))
769+
{
770+
return 0;
771+
}
772+
sizeLeft -= willCopy;
773+
currentOffset += willCopy;
774+
}
775+
776+
return currentOffset;
777+
}
778+
779+
bool EspClass::flashWritePageBreak(uint32_t address, const uint8_t *data, size_t size) {
780+
if (size > 4) {
781+
return false;
782+
}
783+
size_t pageLeft = FLASH_PAGE_SIZE - (address % FLASH_PAGE_SIZE);
784+
size_t offset = 0;
785+
size_t sizeLeft = size;
786+
if (pageLeft > 3) {
787+
return false;
788+
}
789+
790+
if (!flashReplaceBlock(address, data, pageLeft)) {
791+
return false;
792+
}
793+
offset += pageLeft;
794+
sizeLeft -= pageLeft;
795+
// We replaced last 4-byte block of the page, now we write the remainder in next page
796+
if (!flashReplaceBlock(address + offset, data + offset, sizeLeft)) {
797+
return false;
798+
}
799+
return true;
800+
}
801+
802+
bool EspClass::flashWrite(uint32_t address, const uint32_t *data, size_t size) {
717803
SpiFlashOpResult rc = SPI_FLASH_RESULT_OK;
804+
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + size - 1) / FLASH_PAGE_SIZE));
805+
806+
if ((uintptr_t)data % 4 != 0 || size % 4 != 0 || pageBreak) {
807+
return false;
808+
}
718809
#if PUYA_SUPPORT
719810
if (getFlashChipVendorId() == SPI_FLASH_VENDOR_PUYA) {
720-
rc = spi_flash_write_puya(offset, data, size);
811+
rc = spi_flash_write_puya(address, const_cast<uint32_t *>(data), size);
721812
}
722813
else
723-
#endif
814+
#endif // PUYA_SUPPORT
724815
{
725-
rc = spi_flash_write(offset, data, size);
816+
rc = spi_flash_write(address, const_cast<uint32_t *>(data), size);
726817
}
727818
return rc == SPI_FLASH_RESULT_OK;
728819
}
729820

730-
bool EspClass::flashRead(uint32_t offset, uint32_t *data, size_t size) {
731-
auto rc = spi_flash_read(offset, (uint32_t*) data, size);
732-
return rc == SPI_FLASH_RESULT_OK;
821+
bool EspClass::flashWrite(uint32_t address, const uint8_t *data, size_t size) {
822+
if (size == 0) {
823+
return true;
824+
}
825+
826+
size_t sizeLeft = size & ~3;
827+
size_t currentOffset = 0;
828+
829+
if (sizeLeft) {
830+
if ((uintptr_t)data % 4 != 0) {
831+
size_t written = flashWriteUnalignedMemory(address, data, size);
832+
if (!written) {
833+
return false;
834+
}
835+
currentOffset += written;
836+
sizeLeft -= written;
837+
} else {
838+
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
839+
840+
if (pageBreak) {
841+
while (sizeLeft) {
842+
// We cannot cross page boundary, but the write must be 4 byte aligned,
843+
// so this is the maximum amount we can write
844+
size_t pageBoundary = (FLASH_PAGE_SIZE - ((address + currentOffset) % FLASH_PAGE_SIZE)) & ~3;
845+
846+
if (sizeLeft > pageBoundary) {
847+
// Aligned write up to page boundary
848+
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), pageBoundary)) {
849+
return false;
850+
}
851+
currentOffset += pageBoundary;
852+
sizeLeft -= pageBoundary;
853+
// Cross the page boundary
854+
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, 4)) {
855+
return false;
856+
}
857+
currentOffset += 4;
858+
sizeLeft -= 4;
859+
} else {
860+
// We do not cross page boundary
861+
if (!flashWrite(address + currentOffset, (uint32_t *)(data + currentOffset), sizeLeft)) {
862+
return false;
863+
}
864+
currentOffset += sizeLeft;
865+
sizeLeft = 0;
866+
}
867+
}
868+
} else {
869+
// Pointer is properly aligned and write does not cross page boundary,
870+
// so use aligned write
871+
if (!flashWrite(address, (uint32_t *)data, sizeLeft)) {
872+
return false;
873+
}
874+
currentOffset = sizeLeft;
875+
sizeLeft = 0;
876+
}
877+
}
878+
}
879+
sizeLeft = size - currentOffset;
880+
if (sizeLeft > 0) {
881+
// Size was not aligned, so we have some bytes left to write, we also need to recheck for
882+
// page boundary crossing
883+
bool pageBreak = ((address % 4) != 0 && (address / FLASH_PAGE_SIZE) != ((address + sizeLeft - 1) / FLASH_PAGE_SIZE));
884+
885+
if (pageBreak) {
886+
// Cross the page boundary
887+
if (!flashWritePageBreak(address + currentOffset, data + currentOffset, sizeLeft)) {
888+
return false;
889+
}
890+
} else {
891+
// Just write partial block
892+
flashReplaceBlock(address + currentOffset, data + currentOffset, sizeLeft);
893+
}
894+
}
895+
896+
return true;
897+
}
898+
899+
bool EspClass::flashRead(uint32_t address, uint8_t *data, size_t size) {
900+
size_t sizeAligned = size & ~3;
901+
size_t currentOffset = 0;
902+
903+
if ((uintptr_t)data % 4 != 0) {
904+
uint32_t alignedData[FLASH_PAGE_SIZE / sizeof(uint32_t)] __attribute__((aligned(4)));
905+
size_t sizeLeft = sizeAligned;
906+
907+
while (sizeLeft) {
908+
size_t willCopy = std::min(sizeLeft, sizeof(alignedData));
909+
// We read to our aligned buffer and then copy to data
910+
if (!flashRead(address + currentOffset, alignedData, willCopy))
911+
{
912+
return false;
913+
}
914+
memcpy(data + currentOffset, alignedData, willCopy);
915+
sizeLeft -= willCopy;
916+
currentOffset += willCopy;
917+
}
918+
} else {
919+
// Pointer is properly aligned, so use aligned read
920+
if (!flashRead(address, (uint32_t *)data, sizeAligned)) {
921+
return false;
922+
}
923+
currentOffset = sizeAligned;
924+
}
925+
926+
if (currentOffset < size) {
927+
uint32_t tempData;
928+
if (spi_flash_read(address + currentOffset, &tempData, 4) != SPI_FLASH_RESULT_OK) {
929+
return false;
930+
}
931+
memcpy((uint8_t *)data + currentOffset, &tempData, size - currentOffset);
932+
}
933+
934+
return true;
935+
}
936+
937+
bool EspClass::flashRead(uint32_t address, uint32_t *data, size_t size) {
938+
if ((uintptr_t)data % 4 != 0 || size % 4 != 0) {
939+
return false;
940+
}
941+
return (spi_flash_read(address, data, size) == SPI_FLASH_RESULT_OK);
733942
}
734943

735944
String EspClass::getSketchMD5()
@@ -740,17 +949,17 @@ String EspClass::getSketchMD5()
740949
}
741950
uint32_t lengthLeft = getSketchSize();
742951
const size_t bufSize = 512;
743-
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
952+
std::unique_ptr<uint8_t[]> buf(new (std::nothrow) uint8_t[bufSize]);
744953
uint32_t offset = 0;
745954
if(!buf.get()) {
746-
return String();
955+
return emptyString;
747956
}
748957
MD5Builder md5;
749958
md5.begin();
750959
while( lengthLeft > 0) {
751960
size_t readBytes = (lengthLeft < bufSize) ? lengthLeft : bufSize;
752961
if (!flashRead(offset, reinterpret_cast<uint32_t*>(buf.get()), (readBytes + 3) & ~3)) {
753-
return String();
962+
return emptyString;
754963
}
755964
md5.add(buf.get(), readBytes);
756965
lengthLeft -= readBytes;

0 commit comments

Comments
 (0)