ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_sd_format.c
Go to the documentation of this file.
1
16
17#include <string.h>
18
21
22/* ----------------------------------------------------------------------------
23 * Blank-card creation (--sd-new): format an empty FAT volume in sparse storage so a
24 * size/format can be set up with no pre-built image. The BPB is complete enough
25 * to satisfy a host `fsck_msdos` and the firmware's ra8_fs mount alike.
26 * --------------------------------------------------------------------------*/
27
29typedef enum : uint8_t {
39
52RA8_INTERNAL static void internal_sd_put16(uint8_t* p, uint16_t v)
53{
54 p[k_le_byte0] = (uint8_t)(v & (uint16_t)k_le_byte_mask);
55 p[k_le_byte1] = (uint8_t)((v >> (uint16_t)k_le_shift_b1) & (uint16_t)k_le_byte_mask);
56}
57
70RA8_INTERNAL static void internal_sd_put32(uint8_t* p, uint32_t v)
71{
72 p[k_le_byte0] = (uint8_t)(v & (uint32_t)k_le_byte_mask);
73 p[k_le_byte1] = (uint8_t)((v >> (uint32_t)k_le_shift_b1) & (uint32_t)k_le_byte_mask);
74 p[k_le_byte2] = (uint8_t)((v >> (uint32_t)k_le_shift_b2) & (uint32_t)k_le_byte_mask);
75 p[k_le_byte3] = (uint8_t)((v >> (uint32_t)k_le_shift_b3) & (uint32_t)k_le_byte_mask);
76}
77
112
152
165RA8_INTERNAL static void internal_sd_boot_frame(uint8_t* img, const char* oem)
166{
167 img[k_bpb_jmp0] = (uint8_t)k_bpb_jmp_b0;
168 img[k_bpb_jmp1] = (uint8_t)k_bpb_jmp_b1;
169 img[k_bpb_jmp2] = (uint8_t)k_bpb_jmp_b2;
170 (void)memcpy(&img[k_bpb_oem], oem, (size_t)k_bpb_oem_len);
171 img[k_bpb_sig_lo] = (uint8_t)k_bpb_sig_byte_lo;
172 img[k_bpb_sig_hi] = (uint8_t)k_bpb_sig_byte_hi;
173}
174
176void priv_board_sd_label_field(uint8_t* dst, const char* label)
177{
178 bool past_end = (label == nullptr);
179 for (uint32_t i = 0U; i < (uint32_t)k_fmt_label_len; i++) {
180 if (!past_end && (label[i] == '\0')) {
181 past_end = true; /* stop reading at the NUL; pad the rest with spaces. */
182 }
183 dst[i] = past_end ? (uint8_t)' ' : (uint8_t)label[i];
184 }
185}
186
187/* @brief Format an empty FAT16 volume; returns the chosen sectors-per-cluster. */
212RA8_INTERNAL static uint32_t
213internal_fat16_solve_geometry(uint32_t total_sectors, uint32_t root_sectors, uint32_t* fatsz)
214{
215 uint32_t spc = 1U;
216 for (;;) {
217 const uint32_t tmp1 = total_sectors - ((uint32_t)k_fmt_resv_f16 + root_sectors);
218 const uint32_t tmp2 = ((uint32_t)k_bpb_fat16_div * spc) +
219 (uint32_t)k_bpb_num_fats_val; /* 256 = 512 / 2-byte ent. */
220 *fatsz = (tmp1 + tmp2 - 1U) / tmp2;
221 const uint32_t data = total_sectors - (uint32_t)k_fmt_resv_f16 - (2U * *fatsz) - root_sectors;
222 const uint32_t clus = data / spc;
223 if (clus <= (uint32_t)k_fmt_fat16_max) {
224 break;
225 }
226 spc *= 2U;
227 if (spc >= (uint32_t)k_fmt_spc_max) {
228 spc = (uint32_t)k_fmt_spc_max;
229 break;
230 }
231 }
232 return spc;
233}
234
254RA8_INTERNAL static void internal_fat16_write_bpb(uint8_t* img,
255 uint32_t total_sectors,
256 uint32_t spc,
257 uint32_t fatsz,
258 const char* label)
259{
260 internal_sd_boot_frame(img, "MSDOS5.0");
262 img[k_bpb_sec_per_clus] = (uint8_t)spc;
264 img[k_bpb_num_fats] = (uint8_t)k_bpb_num_fats_val;
266 if (total_sectors < (uint32_t)k_fmt_totsec16_max) {
267 internal_sd_put16(&img[k_bpb_totsec16], (uint16_t)total_sectors);
268 } else {
269 internal_sd_put32(&img[k_bpb_totsec32], total_sectors);
270 }
271 img[k_bpb_media] = (uint8_t)k_bpb_media_fixed;
272 internal_sd_put16(&img[k_bpb_fatsz16], (uint16_t)fatsz);
276 img[k_bpb16_boot_sig] = (uint8_t)k_bpb_ext_boot_sig;
277 internal_sd_put32(&img[k_bpb16_volid], (uint32_t)k_fmt_volid | total_sectors);
279 (void)memcpy(&img[k_bpb16_fstype], "FAT16 ", (size_t)k_bpb_fstype_len);
280}
281
298RA8_INTERNAL static bool internal_write_sector(int image_fd, uint32_t sector, const uint8_t* data)
299{
300 const uint64_t offset = (uint64_t)sector * (uint64_t)k_fmt_sec_bytes;
301 const emu_io_result_t result =
302 priv_emu_io_pwrite_exact(image_fd, data, (size_t)k_fmt_sec_bytes, (off_t)offset);
303 return result.status == k_emu_io_ok;
304}
305
307 uint32_t total_sectors,
308 const char* label,
309 uint32_t* out_spc)
310{
311 const uint32_t root_sectors = (((uint32_t)k_fmt_root_ents * (uint32_t)k_bpb_dir_ent_size) +
312 ((uint32_t)k_fmt_sec_bytes - 1U)) /
313 (uint32_t)k_fmt_sec_bytes;
314 uint32_t fatsz = 1U;
315 const uint32_t spc = internal_fat16_solve_geometry(total_sectors, root_sectors, &fatsz);
316
317 uint8_t boot[k_fmt_sec_bytes] = {};
318 uint8_t fat[k_fmt_sec_bytes] = {};
319 internal_fat16_write_bpb(boot, total_sectors, spc, fatsz, label);
322
323 if (!internal_write_sector(image_fd, 0U, boot)) {
324 return false;
325 }
326 for (uint32_t f = 0U; f < (uint32_t)k_bpb_num_fats_val; ++f) {
327 if (!internal_write_sector(image_fd, (uint32_t)k_fmt_resv_f16 + (f * fatsz), fat)) {
328 return false;
329 }
330 }
331 *out_spc = spc;
332 return true;
333}
334
359RA8_INTERNAL static uint32_t
360internal_sd_fat32_geometry(uint32_t total_sectors, uint32_t* out_fatsz, uint32_t* out_clusters)
361{
362 uint32_t spc = 1U;
363 uint32_t fatsz = 1U;
364 uint32_t clusters = 0U;
365 for (;;) {
366 const uint32_t tmp1 = total_sectors - (uint32_t)k_fmt_resv_f32;
367 /* Microsoft FAT32 FAT-size divisor: ((256*spc) + NumFATs) / 2 = 128*spc + 1.
368 * (Using +2 can undersize the FAT by a sector on large volumes.) */
369 const uint32_t tmp2 = ((uint32_t)k_bpb_fat32_div * spc) + 1U;
370 fatsz = (tmp1 + tmp2 - 1U) / tmp2;
371 const uint32_t data = total_sectors - (uint32_t)k_fmt_resv_f32 - (2U * fatsz);
372 clusters = data / spc;
373 /* Grow the cluster size for big cards so the FAT stays bounded and the
374 * cluster size is realistic (8 KiB at ~2 GiB, 32 KiB at ~8 GiB+), capped at
375 * the FAT32 cluster ceiling. k_fmt_fat32_pref keeps small cards at 1 spc. */
376 if (clusters <= (uint32_t)k_fmt_fat32_pref) {
377 break;
378 }
379 spc *= 2U;
380 if (spc >= (uint32_t)k_fmt_spc_max) {
381 spc = (uint32_t)k_fmt_spc_max;
382 break;
383 }
384 }
385 *out_fatsz = fatsz;
386 *out_clusters = clusters;
387 return spc;
388}
389
406RA8_INTERNAL static void internal_fat32_write_bpb(uint8_t* img,
407 uint32_t total_sectors,
408 uint32_t spc,
409 uint32_t fatsz,
410 const char* label)
411{
412 internal_sd_boot_frame(img, "MSDOS5.0");
414 img[k_bpb_sec_per_clus] = (uint8_t)spc;
416 img[k_bpb_num_fats] = (uint8_t)k_bpb_num_fats_val;
417 internal_sd_put16(&img[k_bpb_root_ents], 0U); /* root entries: 0 for FAT32 */
418 internal_sd_put16(&img[k_bpb_totsec16], 0U); /* totsec16: 0, use totsec32 */
419 img[k_bpb_media] = (uint8_t)k_bpb_media_fixed;
420 internal_sd_put16(&img[k_bpb_fatsz16], 0U); /* fatsz16: 0 for FAT32 */
423 internal_sd_put32(&img[k_bpb_totsec32], total_sectors);
424 internal_sd_put32(&img[k_bpb_fatsz32], fatsz); /* BPB_FATSz32 */
429 img[k_bpb32_boot_sig] = (uint8_t)k_bpb_ext_boot_sig;
430 internal_sd_put32(&img[k_bpb32_volid], (uint32_t)k_fmt_volid | total_sectors);
432 (void)memcpy(&img[k_bpb32_fstype], "FAT32 ", (size_t)k_bpb_fstype_len);
433}
434
436 uint32_t total_sectors,
437 const char* label,
438 uint32_t* out_spc)
439{
440 uint32_t fatsz = 0U;
441 uint32_t clusters = 0U;
442 const uint32_t spc = internal_sd_fat32_geometry(total_sectors, &fatsz, &clusters);
443 uint8_t boot[k_fmt_sec_bytes] = {};
444 uint8_t fsi[k_fmt_sec_bytes] = {};
445 uint8_t fat[k_fmt_sec_bytes] = {};
446 internal_fat32_write_bpb(boot, total_sectors, spc, fatsz, label);
450 (clusters > 0U) ? (clusters - 1U) : (uint32_t)k_fsi_free_unknown);
456
457 if (!internal_write_sector(image_fd, 0U, boot) ||
458 !internal_write_sector(image_fd, (uint32_t)k_fat32_fsinfo_sec, fsi) ||
459 !internal_write_sector(image_fd, (uint32_t)k_fat32_bkboot_sec, boot)) {
460 return false;
461 }
462 for (uint32_t f = 0U; f < (uint32_t)k_bpb_num_fats_val; f++) {
463 if (!internal_write_sector(image_fd, (uint32_t)k_fmt_resv_f32 + (f * fatsz), fat)) {
464 return false;
465 }
466 }
467 *out_spc = spc;
468 return true;
469}
sd_bpb_val_t
FAT BPB literal field values + the FSInfo signatures.
@ k_bpb_jmp_b0
Boot jump byte 0 (JMP short).
@ k_bpb_sec_per_trk_val
Sectors per track.
@ k_fsi_free_unknown
Free-cluster count unknown.
@ k_bpb_num_heads_val
Number of heads.
@ k_fat32_ent2_off
FAT32 entry-2 byte offset.
@ k_bpb_num_fats_val
Number of FAT copies.
@ k_bpb_sig_byte_lo
Boot signature low byte.
@ k_bpb_jmp_b1
Boot jump byte 1 (offset).
@ k_fat32_eoc
FAT32 end-of-chain marker.
@ k_fsi_off_lead
FSInfo lead-sig offset.
@ k_bpb_fstype_len
FilSysType field length.
@ k_fat32_ent1_off
FAT32 entry-1 byte offset.
@ k_fat16_eoc_clus0
FAT16 entry 0: media + EOC bits.
@ k_fsi_off_struc
FSInfo struct-sig offset.
@ k_bpb_media_fixed
Media descriptor: fixed disk.
@ k_bpb_fat16_div
512-byte sector / 2-byte FAT16 ent.
@ k_fsi_off_free
FSInfo free-cluster-count off.
@ k_fat32_fsinfo_sec
FSInfo sector index.
@ k_bpb_sig_byte_hi
Boot signature high byte.
@ k_fat32_bkboot_sec
Backup boot-sector index.
@ k_fat32_eoc_media
FAT32 entry 0: media + EOC.
@ k_fsi_lead_sig
FSInfo lead signature "RRaA".
@ k_bpb_fat32_div
FAT32 FAT-size divisor multiplier.
@ k_fsi_trail_sig
FSInfo trailing signature.
@ k_bpb_ext_boot_sig
Extended boot signature present.
@ k_fat_ent1_off
FAT16 entry-1 byte offset.
@ k_fsi_struc_sig
FSInfo struct signature "rrAa".
@ k_bpb_oem_len
OEM-name field length.
@ k_fat32_root_clus
FAT32 root directory cluster.
@ k_bpb_drive_num_val
Drive number (hard disk).
@ k_fsi_nxt_free
Next-free cluster hint.
@ k_fsi_off_nxtfree
FSInfo next-free-cluster off.
@ k_fsi_off_trail
FSInfo trailing-sig offset.
@ k_bpb_dir_ent_size
Bytes per directory entry.
@ k_bpb_jmp_b2
Boot jump byte 2 (NOP).
@ k_fat16_eoc_clus1
FAT16 entry 1: end-of-chain.
static RA8_INTERNAL uint32_t internal_sd_fat32_geometry(uint32_t total_sectors, uint32_t *out_fatsz, uint32_t *out_clusters)
Compute FAT32 volume geometry for a card of total_sectors.
static RA8_INTERNAL uint32_t internal_fat16_solve_geometry(uint32_t total_sectors, uint32_t root_sectors, uint32_t *fatsz)
Solve the FAT16 cluster size and FAT length for total_sectors.
sd_le_const_t
Little-endian byte offsets, shifts, and mask for image serialization.
@ k_le_byte2
Third byte offset.
@ k_le_shift_b2
Shift to extract byte 2.
@ k_le_byte3
Most-significant byte offset.
@ k_le_shift_b3
Shift to extract byte 3.
@ k_le_byte0
Least-significant byte offset.
@ k_le_shift_b1
Shift to extract byte 1.
@ k_le_byte1
Second byte offset.
@ k_le_byte_mask
Low-byte mask.
static RA8_INTERNAL void internal_sd_put32(uint8_t *p, uint32_t v)
Store one 32-bit image field in little-endian order.
static RA8_INTERNAL bool internal_write_sector(int image_fd, uint32_t sector, const uint8_t *data)
Write one complete sector at its checked positioned offset.
static RA8_INTERNAL void internal_fat32_write_bpb(uint8_t *img, uint32_t total_sectors, uint32_t spc, uint32_t fatsz, const char *label)
Serialize one deterministic FAT32 BIOS Parameter Block.
static RA8_INTERNAL void internal_sd_put16(uint8_t *p, uint16_t v)
Store one 16-bit image field in little-endian order.
static RA8_INTERNAL void internal_fat16_write_bpb(uint8_t *img, uint32_t total_sectors, uint32_t spc, uint32_t fatsz, const char *label)
Write the FAT16 BIOS Parameter Block into sector 0 of img.
bool priv_board_sd_format_fat16(int image_fd, uint32_t total_sectors, const char *label, uint32_t *out_spc)
Format the attached sparse backend as a FAT16 volume.
sd_bpb_off_t
FAT BPB byte offsets within a sector (FAT16 + FAT32 layouts).
@ k_bpb_media
BPB_Media.
@ k_bpb_num_heads
BPB_NumHeads.
@ k_bpb_bytes_per_sec
BPB_BytsPerSec.
@ k_bpb16_drive_num
BS_DrvNum (FAT16).
@ k_bpb_resv_sec
BPB_RsvdSecCnt.
@ k_bpb_oem
OEM name (8 bytes).
@ k_bpb_sec_per_clus
BPB_SecPerClus.
@ k_bpb32_volid
BS_VolID (FAT32).
@ k_bpb16_boot_sig
BS_BootSig (FAT16).
@ k_bpb_fatsz32
BPB_FATSz32 (FAT32 only).
@ k_bpb_totsec16
BPB_TotSec16.
@ k_bpb_sec_per_trk
BPB_SecPerTrk.
@ k_bpb_num_fats
BPB_NumFATs.
@ k_bpb32_fsinfo_sec
BPB_FSInfo (FAT32).
@ k_bpb32_bkboot_sec
BPB_BkBootSec (FAT32).
@ k_bpb16_vol_label
BS_VolLab (FAT16, 11 bytes).
@ k_bpb32_drive_num
BS_DrvNum (FAT32).
@ k_bpb16_volid
BS_VolID (FAT16).
@ k_bpb32_root_clus
BPB_RootClus (FAT32).
@ k_bpb_root_ents
BPB_RootEntCnt.
@ k_bpb_totsec32
BPB_TotSec32.
@ k_bpb32_vol_label
BS_VolLab (FAT32, 11 bytes).
@ k_bpb16_fstype
BS_FilSysType (FAT16, 8 bytes).
@ k_bpb32_boot_sig
BS_BootSig (FAT32).
@ k_bpb32_fstype
BS_FilSysType (FAT32, 8 bytes).
static RA8_INTERNAL void internal_sd_boot_frame(uint8_t *img, const char *oem)
Write the shared boot prologue and trailing boot signature.
void priv_board_sd_label_field(uint8_t *dst, const char *label)
Pad an ASCII volume label to the 11-byte 8.3 field (space-filled).
bool priv_board_sd_format_fat32(int image_fd, uint32_t total_sectors, const char *label, uint32_t *out_spc)
Format the attached sparse backend as a FAT32 volume.
Module-private FAT formatter shared by the board_periph_sd TUs.
@ k_fmt_fat32_pref
FAT32: preferred max clusters (bumps spc).
@ k_fmt_totsec16_max
Above this, use the 32-bit TotSec.
@ k_fmt_root_ents
FAT16 root-directory entries.
@ k_fmt_volid
Arbitrary volume serial base.
@ k_fmt_fat16_max
Max FAT16 data clusters.
@ k_fmt_sec_bytes
Bytes per sector.
Bounded raw-descriptor I/O seam for the RA8 emulator.
@ k_emu_io_ok
The complete operation succeeded.
emu_io_result_t priv_emu_io_pwrite_exact(int fd, const void *buf, size_t count, off_t offset)
Write exactly count bytes at offset without changing the cursor.
@ k_bpb_jmp2
BS_jmpBoot[2]: NOP.
@ k_bpb_jmp0
BS_jmpBoot[0]: short jump opcode.
@ k_bpb_jmp1
BS_jmpBoot[1]: jump displacement.
@ k_bpb_fatsz16
Sectors per FAT.
Definition mkfontimg.c:46
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
@ k_bpb_sig_hi
Bpb sig hi.
@ k_bpb_sig_lo
Bpb sig lo.
@ k_fmt_label_len
Volume-label field width (bytes).
@ k_fmt_resv_f16
FAT12/16 reserved (boot) sectors.
@ k_fmt_spc_max
Largest sectors-per-cluster we set.
@ k_fmt_resv_f32
FAT32 reserved sectors.
Complete, caller-visible result of a raw host-I/O operation.
emu_io_status_t status
Semantic completion status.