+#define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */\r
+#define GPT_ALIGN 0x100000 /* Alignment of partitions in GPT [byte] (>=128KB) */\r
+#define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */\r
+\r
+\r
+/* Create partitions on the physical drive in format of MBR or GPT */\r
+\r
+static FRESULT create_partition (\r
+ BYTE drv, /* Physical drive number */\r
+ const LBA_t plst[], /* Partition list */\r
+ BYTE sys, /* System ID for each partition (for only MBR) */\r
+ BYTE *buf /* Working buffer for a sector */\r
+)\r
+{\r
+ UINT i, cy;\r
+ LBA_t sz_drv;\r
+ DWORD sz_drv32, nxt_alloc32, sz_part32;\r
+ BYTE *pte;\r
+ BYTE hd, n_hd, sc, n_sc;\r
+\r
+ /* Get physical drive size */\r
+ if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR;\r
+\r
+#if FF_LBA64\r
+ if (sz_drv >= FF_MIN_GPT) { /* Create partitions in GPT format */\r
+ WORD ss;\r
+ UINT sz_ptbl, pi, si, ofs;\r
+ DWORD bcc, rnd, align;\r
+ QWORD nxt_alloc, sz_part, sz_pool, top_bpt;\r
+ static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};\r
+\r
+#if FF_MAX_SS != FF_MIN_SS\r
+ if (disk_ioctl(drv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; /* Get sector size */\r
+ if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;\r
+#else\r
+ ss = FF_MAX_SS;\r
+#endif\r
+ rnd = (DWORD)sz_drv + GET_FATTIME(); /* Random seed */\r
+ align = GPT_ALIGN / ss; /* Partition alignment for GPT [sector] */\r
+ sz_ptbl = GPT_ITEMS * SZ_GPTE / ss; /* Size of partition table [sector] */\r
+ top_bpt = sz_drv - sz_ptbl - 1; /* Backup partition table start sector */\r
+ nxt_alloc = 2 + sz_ptbl; /* First allocatable sector */\r
+ sz_pool = top_bpt - nxt_alloc; /* Size of allocatable area */\r
+ bcc = 0xFFFFFFFF; sz_part = 1;\r
+ pi = si = 0; /* partition table index, size table index */\r
+ do {\r
+ if (pi * SZ_GPTE % ss == 0) memset(buf, 0, ss); /* Clean the buffer if needed */\r
+ if (sz_part != 0) { /* Is the size table not termintated? */\r
+ nxt_alloc = (nxt_alloc + align - 1) & ((QWORD)0 - align); /* Align partition start */\r
+ sz_part = plst[si++]; /* Get a partition size */\r
+ if (sz_part <= 100) { /* Is the size in percentage? */\r
+ sz_part = sz_pool * sz_part / 100;\r
+ sz_part = (sz_part + align - 1) & ((QWORD)0 - align); /* Align partition end (only if in percentage) */\r
+ }\r
+ if (nxt_alloc + sz_part > top_bpt) { /* Clip the size at end of the pool */\r
+ sz_part = (nxt_alloc < top_bpt) ? top_bpt - nxt_alloc : 0;\r
+ }\r
+ }\r
+ if (sz_part != 0) { /* Add a partition? */\r
+ ofs = pi * SZ_GPTE % ss;\r
+ memcpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16); /* Set partition GUID (Microsoft Basic Data) */\r
+ rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16); /* Set unique partition GUID */\r
+ st_qword(buf + ofs + GPTE_FstLba, nxt_alloc); /* Set partition start sector */\r
+ st_qword(buf + ofs + GPTE_LstLba, nxt_alloc + sz_part - 1); /* Set partition end sector */\r
+ nxt_alloc += sz_part; /* Next allocatable sector */\r
+ }\r
+ if ((pi + 1) * SZ_GPTE % ss == 0) { /* Write the buffer if it is filled up */\r
+ for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ; /* Calculate table check sum */\r
+ if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to primary table */\r
+ if (disk_write(drv, buf, top_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR; /* Write to secondary table */\r
+ }\r
+ } while (++pi < GPT_ITEMS);\r
+\r
+ /* Create primary GPT header */\r
+ memset(buf, 0, ss);\r
+ memcpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */\r
+ st_dword(buf + GPTH_PtBcc, ~bcc); /* Table check sum */\r
+ st_qword(buf + GPTH_CurLba, 1); /* LBA of this header */\r
+ st_qword(buf + GPTH_BakLba, sz_drv - 1); /* LBA of secondary header */\r
+ st_qword(buf + GPTH_FstLba, 2 + sz_ptbl); /* LBA of first allocatable sector */\r
+ st_qword(buf + GPTH_LstLba, top_bpt - 1); /* LBA of last allocatable sector */\r
+ st_dword(buf + GPTH_PteSize, SZ_GPTE); /* Size of a table entry */\r
+ st_dword(buf + GPTH_PtNum, GPT_ITEMS); /* Number of table entries */\r
+ st_dword(buf + GPTH_PtOfs, 2); /* LBA of this table */\r
+ rnd = make_rand(rnd, buf + GPTH_DskGuid, 16); /* Disk GUID */\r
+ for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */\r
+ st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */\r
+ if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR;\r
+\r
+ /* Create secondary GPT header */\r
+ st_qword(buf + GPTH_CurLba, sz_drv - 1); /* LBA of this header */\r
+ st_qword(buf + GPTH_BakLba, 1); /* LBA of primary header */\r
+ st_qword(buf + GPTH_PtOfs, top_bpt); /* LBA of this table */\r
+ st_dword(buf + GPTH_Bcc, 0);\r
+ for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ; /* Calculate header check sum */\r
+ st_dword(buf + GPTH_Bcc, ~bcc); /* Header check sum */\r
+ if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return FR_DISK_ERR;\r
+\r
+ /* Create protective MBR */\r
+ memset(buf, 0, ss);\r
+ memcpy(buf + MBR_Table, gpt_mbr, 16); /* Create a GPT partition */\r
+ st_word(buf + BS_55AA, 0xAA55);\r
+ if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;\r
+\r
+ } else\r
+#endif\r
+ { /* Create partitions in MBR format */\r
+ sz_drv32 = (DWORD)sz_drv;\r
+ n_sc = N_SEC_TRACK; /* Determine drive CHS without any consideration of the drive geometry */\r
+ for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ;\r
+ if (n_hd == 0) n_hd = 255; /* Number of heads needs to be <256 */\r
+\r
+ memset(buf, 0, FF_MAX_SS); /* Clear MBR */\r
+ pte = buf + MBR_Table; /* Partition table in the MBR */\r
+ for (i = 0, nxt_alloc32 = n_sc; i < 4 && nxt_alloc32 != 0 && nxt_alloc32 < sz_drv32; i++, nxt_alloc32 += sz_part32) {\r
+ sz_part32 = (DWORD)plst[i]; /* Get partition size */\r
+ if (sz_part32 <= 100) sz_part32 = (sz_part32 == 100) ? sz_drv32 : sz_drv32 / 100 * sz_part32; /* Size in percentage? */\r
+ if (nxt_alloc32 + sz_part32 > sz_drv32 || nxt_alloc32 + sz_part32 < nxt_alloc32) sz_part32 = sz_drv32 - nxt_alloc32; /* Clip at drive size */\r
+ if (sz_part32 == 0) break; /* End of table or no sector to allocate? */\r
+\r
+ st_dword(pte + PTE_StLba, nxt_alloc32); /* Start LBA */\r
+ st_dword(pte + PTE_SizLba, sz_part32); /* Number of sectors */\r
+ pte[PTE_System] = sys; /* System type */\r
+\r
+ cy = (UINT)(nxt_alloc32 / n_sc / n_hd); /* Start cylinder */\r
+ hd = (BYTE)(nxt_alloc32 / n_sc % n_hd); /* Start head */\r
+ sc = (BYTE)(nxt_alloc32 % n_sc + 1); /* Start sector */\r
+ pte[PTE_StHead] = hd;\r
+ pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc);\r
+ pte[PTE_StCyl] = (BYTE)cy;\r
+\r
+ cy = (UINT)((nxt_alloc32 + sz_part32 - 1) / n_sc / n_hd); /* End cylinder */\r
+ hd = (BYTE)((nxt_alloc32 + sz_part32 - 1) / n_sc % n_hd); /* End head */\r
+ sc = (BYTE)((nxt_alloc32 + sz_part32 - 1) % n_sc + 1); /* End sector */\r
+ pte[PTE_EdHead] = hd;\r
+ pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc);\r
+ pte[PTE_EdCyl] = (BYTE)cy;\r
+\r
+ pte += SZ_PTE; /* Next entry */\r
+ }\r
+\r
+ st_word(buf + BS_55AA, 0xAA55); /* MBR signature */\r
+ if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */\r
+ }\r
+\r
+ return FR_OK;\r
+}\r
+\r
+\r
+\r