software/liblitesdcard: switch to FatFs for sdcardboot.

This commit is contained in:
Florent Kermarrec 2020-06-05 20:09:02 +02:00
parent f972c8e45e
commit f9b43c81cc
8 changed files with 46 additions and 411 deletions

View File

@ -21,6 +21,7 @@
- Add CV32E40P CPU support (ex RI5CY).
- JTAG UART with uart_name=jtag_uart (validated on Spartan6, 7-Series, Ultrascale(+)).
- Add Symbiflow experimental support on Arty.
- Add SDCard boot from FAT/exFAT filesystems with FatFs.
[> API changes/Deprecation
--------------------------

View File

@ -30,7 +30,7 @@
#include <liblitesdcard/spisdcard.h>
#include <liblitesdcard/sdcard.h>
#include <liblitesdcard/fat16.h>
#include <liblitesdcard/ff.h>
extern void boot_helper(unsigned long r1, unsigned long r2, unsigned long r3, unsigned long addr);
@ -536,8 +536,33 @@ void romboot(void)
#if defined(CSR_SPISDCARD_BASE) || defined(CSR_SDCORE_BASE)
static int copy_image_from_sdcard_to_ram(const char * filename, unsigned int ram_address)
{
FRESULT fr;
FIL file;
UINT br;
UINT offset;
fr = f_open(&file, filename, FA_READ);
if (fr == FR_OK){
printf("Copying %d bytes from %s to 0x%08x...\n", f_size(&file), filename, ram_address);
offset = 0;
for (;;) {
fr = f_read(&file, (void *) ram_address + offset, 512, &br);
if (br == 0) break;
offset += br;
}
} else {
printf("%s file not found.\n", filename);
return 0;
}
f_close(&file);
return 1;
}
void sdcardboot(void)
{
FATFS FatFs;
unsigned int result;
printf("Booting from SDCard...\n");
@ -556,36 +581,26 @@ void sdcardboot(void)
return;
}
/* Read MBR */
result = fat16_read_mbr();
if (result == 0) {
printf("SDCard MBR read failed.\n");
return;
}
/* Copy files to RAM */
#if defined(CONFIG_CPU_TYPE_VEXRISCV) && defined(CONFIG_CPU_VARIANT_LINUX)
result = fat16_read_file("IMAGE", "", MAIN_RAM_BASE + KERNEL_IMAGE_RAM_OFFSET);
f_mount(&FatFs, "", 0);
printf("Loading Linux images from SDCard to RAM...\n");
result = copy_image_from_sdcard_to_ram("rv32.dtb", MAIN_RAM_BASE + DEVICE_TREE_IMAGE_RAM_OFFSET);
if (result)
result &= fat16_read_file("ROOTFS~1", "CPI", MAIN_RAM_BASE + ROOTFS_IMAGE_RAM_OFFSET);
result &= copy_image_from_sdcard_to_ram("emulator.bin", MAIN_RAM_BASE + EMULATOR_IMAGE_RAM_OFFSET);
if (result)
result &= fat16_read_file("RV32", "DTB", MAIN_RAM_BASE + DEVICE_TREE_IMAGE_RAM_OFFSET);
result &= copy_image_from_sdcard_to_ram("Image", MAIN_RAM_BASE + KERNEL_IMAGE_RAM_OFFSET);
if (result)
result &= copy_image_from_sdcard_to_ram("ROOTFS", MAIN_RAM_BASE + ROOTFS_IMAGE_RAM_OFFSET); /* FIXME should be rootfs.cpio */
f_mount(0, "", 0);
if (result)
result &= fat16_read_file("EMULATOR", "BIN", MAIN_RAM_BASE + EMULATOR_IMAGE_RAM_OFFSET);
if(result) {
boot(0, 0, 0, MAIN_RAM_BASE + EMULATOR_IMAGE_RAM_OFFSET);
return;
}
printf("Unable to load all Linux images, falling back to boot.bin...\n");
#endif
result = fat16_read_file("BOOT", "BIN", MAIN_RAM_BASE);
result = copy_image_from_sdcard_to_ram("boot.bin", MAIN_RAM_BASE);
if(result)
boot(0, 0, 0, MAIN_RAM_BASE);
else
printf("SDCard boot failed\n");
printf("SDCard boot failed.\n");
}
#endif

View File

@ -1,7 +1,7 @@
include ../include/generated/variables.mak
include $(SOC_DIRECTORY)/software/common.mak
OBJECTS=fat16.o sdcard.o spisdcard.o
OBJECTS=ff.o sdcard.o spisdcard.o
all: liblitesdcard.a

View File

@ -1,331 +0,0 @@
// This file is Copyright (c) 2020 Rob Shelton <rob.s.ng15@googlemail.com>
// License: BSD
//
// SD CARD code for loading files from a FAT16 formatted partition into memory
#include <generated/csr.h>
#include <generated/soc.h>
#include <generated/mem.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <system.h>
#include "spisdcard.h"
#include "sdcard.h"
#include "fat16.h"
// Return values
#define SUCCESS 0x01
#define FAILURE 0x00
#if defined(CSR_SPISDCARD_BASE) || defined(CSR_SDCORE_BASE)
#if defined(CSR_SPISDCARD_BASE)
#define read_block spisdcard_read_block
#endif
#if defined(CSR_SDCORE_BASE)
#define read_block sdcard_read_block
#endif
// FAT16 Specific code starts here
// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
// Structure to store SD CARD partition table
typedef struct {
uint8_t first_byte;
uint8_t start_chs[3];
uint8_t partition_type;
uint8_t end_chs[3];
uint32_t start_sector;
uint32_t length_sectors;
} __attribute((packed)) PartitionTable;
PartitionTable sdCardPartition;
// Structure to store SD CARD FAT16 Boot Sector (boot code is ignored, provides layout of the FAT16 partition on the SD CARD)
typedef struct {
uint8_t jmp[3];
uint8_t oem[8];
uint16_t sector_size;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t number_of_fats;
uint16_t root_dir_entries;
uint16_t total_sectors_short; // if zero, later field is used
uint8_t media_descriptor;
uint16_t fat_size_sectors;
uint16_t sectors_per_track;
uint16_t number_of_heads;
uint32_t hidden_sectors;
uint32_t total_sectors_long;
uint8_t drive_number;
uint8_t current_head;
uint8_t boot_signature;
uint32_t volume_id;
uint8_t volume_label[11];
uint8_t fs_type[8];
uint8_t boot_code[448];
uint16_t boot_sector_signature;
} __attribute((packed)) Fat16BootSector;
Fat16BootSector sdCardFatBootSector;
// Structure to store SD CARD FAT16 Root Directory Entries
// Allocated to MAIN RAM - hence pointer
typedef struct {
uint8_t filename[8];
uint8_t ext[3];
uint8_t attributes;
uint8_t reserved[10];
uint16_t modify_time;
uint16_t modify_date;
uint16_t starting_cluster;
uint32_t file_size;
} __attribute((packed)) Fat16Entry;
Fat16Entry *sdCardFat16RootDir;
// Structure to store SD CARD FAT16 Entries
// Array of uint16_tS (16bit integers)
uint16_t *sdCardFatTable;
// Calculated sector numbers on the SD CARD for the FAT16 Entries and ROOT DIRECTORY
uint32_t fatSectorStart, rootDirSectorStart;
// Storage for SECTOR read from SD CARD
uint8_t sdCardSector[512];
// SPI_SDCARD_READMBR
// Function exposed to BIOS to retrieve FAT16 partition details, FAT16 Entry Table, FAT16 Root Directory
// MBR = Master Boot Record - Sector 0x00000000 on SD CARD - Contains Partition 1 details at 0x1be
//
// FIXME only checks partition 1 out of 4
//
// Return 0 success, 1 failure
//
// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
uint8_t fat16_read_mbr(void)
{
int i, n;
// Read Sector 0x00000000
printf("Reading MBR\n");
if( read_block(0x00000000, sdCardSector)==SUCCESS ) {
// Copy Partition 1 Entry from byte 0x1be
// FIXME should check 0x55 0xaa at end of sector
memcpy(&sdCardPartition, &sdCardSector[0x1be], sizeof(PartitionTable));
// Check Partition 1 is valid, FIRST_BYTE=0x00 or 0x80
// Check Partition 1 has type 4, 6 or 14 (FAT16 of various sizes)
printf("Partition 1 Information: Active=0x%02x, Type=0x%02x, LBAStart=0x%08x\n", sdCardPartition.first_byte, sdCardPartition.partition_type, sdCardPartition.start_sector);
if( (sdCardPartition.first_byte!=0x80) && (sdCardPartition.first_byte!=0x00) ) {
printf("Partition 1 Not Valid\n");
return FAILURE;
}
if( (sdCardPartition.partition_type==4) || (sdCardPartition.partition_type==6) || (sdCardPartition.partition_type==14) ) {
printf("Partition 1 is FAT16\n");
}
else {
printf("Partition 1 Not FAT16\n");
return FAILURE;
}
}
else {
printf("Failed to read MBR\n");
return FAILURE;
}
// Read Parition 1 Boot Sector - Found from Partion Table
printf("\nRead FAT16 Boot Sector\n");
if( read_block(sdCardPartition.start_sector, sdCardSector)==SUCCESS ) {
memcpy(&sdCardFatBootSector, &sdCardSector, sizeof(Fat16BootSector));
}
else {
printf("Failed to read FAT16 Boot Sector\n");
return FAILURE;
}
// Print details of Parition 1
printf(" Jump Code: 0x%02x 0x%02x 0x%02x\n",sdCardFatBootSector.jmp[0],sdCardFatBootSector.jmp[1],sdCardFatBootSector.jmp[2]);
printf(" OEM Code: [");
for(n=0; n<8; n++)
printf("%c",sdCardFatBootSector.oem[n]);
printf("]\n");
printf(" Sector Size: %d\n",sdCardFatBootSector.sector_size);
printf(" Sectors Per Cluster: %d\n",sdCardFatBootSector.sectors_per_cluster);
printf(" Reserved Sectors: %d\n",sdCardFatBootSector.reserved_sectors);
printf(" Number of Fats: %d\n",sdCardFatBootSector.number_of_fats);
printf(" Root Dir Entries: %d\n",sdCardFatBootSector.root_dir_entries);
printf(" Total Sectors Short: %d\n",sdCardFatBootSector.total_sectors_short);
printf(" Media Descriptor: 0x%02x\n",sdCardFatBootSector.media_descriptor);
printf(" Fat Size Sectors: %d\n",sdCardFatBootSector.fat_size_sectors);
printf(" Sectors Per Track: %d\n",sdCardFatBootSector.sectors_per_track);
printf(" Number of Heads: %d\n",sdCardFatBootSector.number_of_heads);
printf(" Hidden Sectors: %d\n",sdCardFatBootSector.hidden_sectors);
printf(" Total Sectors Long: %d\n",sdCardFatBootSector.total_sectors_long);
printf(" Drive Number: 0x%02x\n",sdCardFatBootSector.drive_number);
printf(" Current Head: 0x%02x\n",sdCardFatBootSector.current_head);
printf(" Boot Signature: 0x%02x\n",sdCardFatBootSector.boot_signature);
printf(" Volume ID: 0x%08x\n",sdCardFatBootSector.volume_id);
printf(" Volume Label: [");
for(n=0; n<11; n++)
printf("%c",sdCardFatBootSector.volume_label[n]);
printf("]\n");
printf(" Volume Label: [");
for(n=0; n<8; n++)
printf("%c",sdCardFatBootSector.fs_type[n]);
printf("]\n");
printf(" Boot Sector Signature: 0x%04x\n\n",sdCardFatBootSector.boot_sector_signature);
// Check Partition 1 is valid, not 0 length
if(sdCardFatBootSector.total_sectors_long==0) {
printf("Error reading FAT16 Boot Sector\n");
return FAILURE;
}
// Read in FAT16 File Allocation Table, array of 16bit unsinged integers
// Calculate Storage from TOP of MAIN RAM
sdCardFatTable = (uint16_t *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors);
printf("sdCardFatTable = 0x%08x Reading Fat16 Table (%d Sectors Long)\n\n",sdCardFatTable,sdCardFatBootSector.fat_size_sectors);
// Calculate Start of FAT16 File Allocation Table (start of partition plus reserved sectors)
fatSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors;
for(n=0; n<sdCardFatBootSector.fat_size_sectors; n++) {
if( read_block(fatSectorStart+n, (uint8_t *)((uint8_t*)sdCardFatTable)+sdCardFatBootSector.sector_size*n)==FAILURE ) {
printf("Error reading FAT16 table - sector %d\n",n);
return FAILURE;
}
}
// Read in FAT16 Root Directory
// Calculate Storage from TOP of MAIN RAM
sdCardFat16RootDir= (Fat16Entry *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors-sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry));
printf("sdCardFat16RootDir = 0x%08x Reading Root Directory (%d Sectors Long)\n\n",sdCardFat16RootDir,sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry)/sdCardFatBootSector.sector_size);
// Calculate Start of FAT ROOT DIRECTORY (start of partition plues reserved sectors plus size of File Allocation Table(s))
rootDirSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors+sdCardFatBootSector.number_of_fats*sdCardFatBootSector.fat_size_sectors;
for(n=0; n<sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry)/sdCardFatBootSector.sector_size; n++) {
if( read_block(rootDirSectorStart+n, (uint8_t *)(sdCardFatBootSector.sector_size*n+(uint8_t *)(sdCardFat16RootDir)))==FAILURE ) {
printf("Error reading Root Dir - sector %d\n",n);
return FAILURE;
}
}
// Print out Root Directory
// Alternates between valid and invalid directory entries for SIMPLE 8+3 file names, extended filenames in other entries
// Only print valid characters
printf("\nRoot Directory\n");
for(n=0; n<sdCardFatBootSector.root_dir_entries; n++) {
if( (sdCardFat16RootDir[n].filename[0]!=0) && (sdCardFat16RootDir[n].file_size>0)) {
printf(" File %d [",n);
for( i=0; i<8; i++) {
if( (sdCardFat16RootDir[n].filename[i]>31) && (sdCardFat16RootDir[n].filename[i]<127) )
printf("%c",sdCardFat16RootDir[n].filename[i]);
else
printf(" ");
}
printf(".");
for( i=0; i<3; i++) {
if( (sdCardFat16RootDir[n].ext[i]>31) && (sdCardFat16RootDir[n].ext[i]<127) )
printf("%c",sdCardFat16RootDir[n].ext[i]);
else
printf(" ");
}
printf("] @ Cluster %d for %d bytes\n",sdCardFat16RootDir[n].starting_cluster,sdCardFat16RootDir[n].file_size);
}
}
printf("\n");
return SUCCESS;
}
// SPI_SDCARD_READFILE
// Function exposed to BIOS to retrieve FILENAME+EXT into ADDRESS
//
// FIXME only checks UPPERCASE 8+3 filenames
//
// Return 0 success, 1 failure
//
// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
uint8_t fat16_read_file(char *filename, char *ext, unsigned long address)
{
int i, n, sector;
uint16_t fileClusterStart;
uint32_t fileLength, bytesRemaining, clusterSectorStart;
uint16_t nameMatch;
printf("Reading File [%s.%s] into 0x%08x : ",filename, ext, address);
// Find FILENAME+EXT in Root Directory
// Indicate FILE found by setting the starting cluster number
fileClusterStart=0; n=0;
while( (fileClusterStart==0) && (n<sdCardFatBootSector.root_dir_entries) ) {
nameMatch=0;
if( sdCardFat16RootDir[n].filename[0]!=0 ) {
nameMatch=1;
for(i=0; i<strlen(filename); i++)
if(sdCardFat16RootDir[n].filename[i]!=filename[i]) nameMatch=0;
for(i=0; i<strlen(ext); i++)
if(sdCardFat16RootDir[n].ext[i]!=ext[i]) nameMatch=0;
}
if(nameMatch==1) {
fileClusterStart=sdCardFat16RootDir[n].starting_cluster;
fileLength=sdCardFat16RootDir[n].file_size;
} else {
n++;
}
}
// If starting cluster number is still 0 then file not found
if(fileClusterStart==0) {
printf("File not found\n");
return FAILURE;
}
printf("File starts at Cluster %d length %d\n",fileClusterStart,fileLength);
// ZERO Length file are automatically assumed to have been read SUCCESS
if( fileLength==0 ) return SUCCESS;
// Read each cluster sector by sector, i being number of clusters
bytesRemaining=fileLength;
// Calculate number of clusters (always >1)
for(i=0; i<1+((fileLength/sdCardFatBootSector.sectors_per_cluster)/sdCardFatBootSector.sector_size); i++) {
printf("\rCluster: %d",fileClusterStart);
// Locate start of cluster on SD CARD and read appropraite number of sectors
clusterSectorStart=rootDirSectorStart+(fileClusterStart-1)*sdCardFatBootSector.sectors_per_cluster;
for(sector=0; sector<sdCardFatBootSector.sectors_per_cluster; sector++) {
// Read Sector from SD CARD
// If whole sector to be read, read directly into memory
// Otherwise, read to sdCardSector buffer and transfer appropriate number of bytes
if(bytesRemaining>sdCardFatBootSector.sector_size) {
if( read_block(clusterSectorStart+sector,(uint8_t *)address) == FAILURE ) {
printf("\nRead Error\n");
return FAILURE;
}
bytesRemaining=bytesRemaining-sdCardFatBootSector.sector_size;
address=address+sdCardFatBootSector.sector_size;
} else {
if( read_block(clusterSectorStart+sector,sdCardSector) == FAILURE ) {
printf("\nRead Error\n");
return FAILURE;
}
memcpy((uint8_t *)address, sdCardSector, bytesRemaining);
bytesRemaining=0;
}
}
// Move to next cluster
fileClusterStart=sdCardFatTable[fileClusterStart];
}
printf("\n\n");
return SUCCESS;
}
#endif

View File

@ -1,10 +0,0 @@
// This file is Copyright (c) 2020 Rob Shelton <rob.s.ng15@googlemail.com>
// License: BSD
#ifndef __FAT16_H
#define __FAT16_H
uint8_t fat16_read_mbr(void);
uint8_t fat16_read_file(char *, char *, unsigned long);
#endif /* __FAT16_H */

View File

@ -8,7 +8,7 @@
/ Function Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_READONLY 0
#define FF_FS_READONLY 1
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
@ -68,7 +68,7 @@
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE 932
#define FF_CODE_PAGE 437
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect code page setting can cause a file open failure.
/
@ -221,7 +221,7 @@
/ System Configurations
/---------------------------------------------------------------------------*/
#define FF_FS_TINY 1
#define FF_FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/ Instead of private sector buffer eliminated from the file object, common sector

View File

@ -43,7 +43,6 @@
static void spi_set_clk_freq(uint32_t clk_freq) {
uint32_t divider;
divider = CONFIG_CLOCK_FREQUENCY/clk_freq + 1;
printf("divider: %d\n", divider);
if (divider >= 65535) /* 16-bit hardware divider */
divider = 65535;
if (divider <= 2) /* At least half CPU speed */
@ -416,45 +415,6 @@ DRESULT disk_read (
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0) */
const BYTE *buff, /* Pointer to the data to be written */
LBA_t sector, /* Start sector number (LBA) */
UINT count /* Sector count (1..128) */
)
{
DWORD sect = (DWORD)sector;
//FIXME if (disk_status(drv) & STA_NOINIT) return RES_NOTRDY;
if (!(CardType & CT_BLOCK)) sect *= 512; /* Convert LBA to byte address if needed */
if (count == 1) { /* Single block write */
if ((send_cmd(CMD24, sect) == 0) /* WRITE_BLOCK */
&& xmit_datablock(buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (CardType & CT_SDC) send_cmd(ACMD23, count);
if (send_cmd(CMD25, sect) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/

View File

@ -1,5 +1,5 @@
// This file is Copyright (c) 2020 Rob Shelton <rob.s.ng15@googlemail.com>
// This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
// This file is Copyright (c) 2020 Rob Shelton <rob.s.ng15@googlemail.com>
// License: BSD
#ifndef __SPISDCARD_H