* don't display a share connection error when listing share
[open-ps2-loader.git] / src / hdd.c
blob24a4d9530c4600f68fe596edf62d0a0e7d7631df
1 #include "include/usbld.h"
2 #include "include/ioman.h"
3 #include "include/hddsupport.h"
5 //#define TEST_WRITES
7 typedef struct // size = 1024
9 u32 checksum; // HDL uses 0xdeadfeed magic here
10 u32 magic;
11 char gamename[160];
12 u8 hdl_compat_flags;
13 u8 ops2l_compat_flags;
14 u8 dma_type;
15 u8 dma_mode;
16 char startup[60];
17 u32 layer1_start;
18 u32 discType;
19 int num_partitions;
20 struct {
21 u32 part_offset; // in MB
22 u32 data_start; // in sectors
23 u32 part_size; // in KB
24 } part_specs[65];
25 } hdl_apa_header;
28 // DEVCTL commands
30 #define APA_DEVCTL_MAX_SECTORS 0x00004801 // max partition size(in sectors)
31 #define APA_DEVCTL_TOTAL_SECTORS 0x00004802
32 #define APA_DEVCTL_IDLE 0x00004803
33 #define APA_DEVCTL_FLUSH_CACHE 0x00004804
34 #define APA_DEVCTL_SWAP_TMP 0x00004805
35 #define APA_DEVCTL_DEV9_SHUTDOWN 0x00004806
36 #define APA_DEVCTL_STATUS 0x00004807
37 #define APA_DEVCTL_FORMAT 0x00004808
38 #define APA_DEVCTL_SMART_STAT 0x00004809
39 #define APA_DEVCTL_GETTIME 0x00006832
40 #define APA_DEVCTL_SET_OSDMBR 0x00006833// arg = hddSetOsdMBR_t
41 #define APA_DEVCTL_GET_SECTOR_ERROR 0x00006834
42 #define APA_DEVCTL_GET_ERROR_PART_NAME 0x00006835// bufp = namebuffer[0x20]
43 #define APA_DEVCTL_ATA_READ 0x00006836// arg = hddAtaTransfer_t
44 #define APA_DEVCTL_ATA_WRITE 0x00006837// arg = hddAtaTransfer_t
45 #define APA_DEVCTL_SCE_IDENTIFY_DRIVE 0x00006838// bufp = buffer for atadSceIdentifyDrive
46 #define APA_DEVCTL_IS_48BIT 0x00006840
47 #define APA_DEVCTL_SET_TRANSFER_MODE 0x00006841
49 static apa_partition_table_t *ptable = NULL;
51 // chunks_map
52 #define MAP_AVAIL '.'
53 #define MAP_MAIN 'M'
54 #define MAP_SUB 's'
55 #define MAP_COLL 'x'
56 #define MAP_ALLOC '*'
58 static u8 hdd_buf[16384];
60 //-------------------------------------------------------------------------
61 int hddCheck(void)
63 register int ret;
65 fileXioInit();
66 ret = fileXioDevctl("hdd0:", APA_DEVCTL_STATUS, NULL, 0, NULL, 0);
68 if ((ret >= 3) || (ret < 0))
69 return -1;
71 return ret;
74 //-------------------------------------------------------------------------
75 u32 hddGetTotalSectors(void)
77 return fileXioDevctl("hdd0:", APA_DEVCTL_TOTAL_SECTORS, NULL, 0, NULL, 0);
80 //-------------------------------------------------------------------------
81 int hddIs48bit(void)
83 return fileXioDevctl("hdd0:", APA_DEVCTL_IS_48BIT, NULL, 0, NULL, 0);
86 //-------------------------------------------------------------------------
87 int hddSetTransferMode(int type, int mode)
89 u8 args[16];
91 *(u32 *)&args[0] = type;
92 *(u32 *)&args[4] = mode;
94 return fileXioDevctl("hdd0:", APA_DEVCTL_SET_TRANSFER_MODE, args, 8, NULL, 0);
97 //-------------------------------------------------------------------------
98 int hddSetIdleTimeout(int timeout)
100 // From hdparm man:
101 // A value of zero means "timeouts are disabled": the
102 // device will not automatically enter standby mode. Values from 1
103 // to 240 specify multiples of 5 seconds, yielding timeouts from 5
104 // seconds to 20 minutes. Values from 241 to 251 specify from 1 to
105 // 11 units of 30 minutes, yielding timeouts from 30 minutes to 5.5
106 // hours. A value of 252 signifies a timeout of 21 minutes. A
107 // value of 253 sets a vendor-defined timeout period between 8 and
108 // 12 hours, and the value 254 is reserved. 255 is interpreted as
109 // 21 minutes plus 15 seconds. Note that some older drives may
110 // have very different interpretations of these values.
112 u8 args[16];
114 *(u32 *)&args[0] = timeout & 0xff;
116 return fileXioDevctl("hdd0:", APA_DEVCTL_IDLE, args, 4, NULL, 0);
119 //-------------------------------------------------------------------------
120 static int hddReadSectors(u32 lba, u32 nsectors, void *buf, int bufsize)
122 u8 args[16];
124 *(u32 *)&args[0] = lba;
125 *(u32 *)&args[4] = nsectors;
127 if (fileXioDevctl("hdd0:", APA_DEVCTL_ATA_READ, args, 8, buf, bufsize) != 0)
128 return -1;
130 return 0;
133 //-------------------------------------------------------------------------
134 static int hddWriteSectors(u32 lba, u32 nsectors, void *buf)
136 int argsz;
137 u8 *args = (u8 *)hdd_buf;
139 *(u32 *)&args[0] = lba;
140 *(u32 *)&args[4] = nsectors;
141 memcpy(&args[8], buf, nsectors << 9);
143 argsz = 8 + (nsectors << 9);
145 if (fileXioDevctl("hdd0:", APA_DEVCTL_ATA_WRITE, args, argsz, NULL, 0) != 0)
146 return -1;
148 return 0;
151 //-------------------------------------------------------------------------
152 static int hddFlushCache(void)
154 return fileXioDevctl("hdd0:", APA_DEVCTL_FLUSH_CACHE, NULL, 0, NULL, 0);
157 //-------------------------------------------------------------------------
158 int hddGetFormat(void)
160 return fileXioDevctl("hdd0:", APA_DEVCTL_FORMAT, NULL, 0, NULL, 0);
163 //-------------------------------------------------------------------------
164 static int apaCheckSum(apa_header *header)
166 u32 *ptr=(u32 *)header;
167 u32 sum=0;
168 int i;
170 for(i=1; i < 256; i++)
171 sum+=ptr[i];
172 return sum;
175 //-------------------------------------------------------------------------
176 static int apaAddPartition(apa_partition_table_t *table, apa_header *header, int existing)
178 register u32 nbytes;
180 if (table->part_count == table->part_alloc_) {
182 // grow buffer of 16 parts
183 nbytes = (table->part_alloc_ + 16) * sizeof(apa_partition_t);
184 apa_partition_t *parts = malloc(nbytes);
185 if(parts != NULL) {
187 memset(parts, 0, nbytes);
189 // copy existing parts
190 if (table->parts != NULL)
191 memcpy(parts, table->parts, table->part_count * sizeof(apa_partition_t));
193 // free old parts mem
194 free(table->parts);
195 table->parts = parts;
196 table->part_alloc_ += 16;
198 else
199 return -2;
202 // copy the part to its buffer
203 memcpy(&table->parts[table->part_count].header, header, sizeof(apa_header));
204 table->parts[table->part_count].existing = existing;
205 table->parts[table->part_count].modified = !existing;
206 table->parts[table->part_count].linked = 1;
207 table->part_count++;
209 return 0;
212 //-------------------------------------------------------------------------
213 static void apaFreePartitionTable(apa_partition_table_t *table)
215 register int i;
217 if (table != NULL) {
218 for (i=0; i<table->part_count; i++) {
219 if (table->parts)
220 free(table->parts);
223 if (table->chunks_map)
224 free(table->chunks_map);
228 //-------------------------------------------------------------------------
229 static int apaSetPartitionTableStats(apa_partition_table_t *table)
231 register int i;
232 char *chunks_map;
234 table->total_chunks = table->device_size_in_mb / 128;
235 chunks_map = malloc(table->total_chunks);
236 if (chunks_map == NULL)
237 return -1;
239 *chunks_map = MAP_AVAIL;
240 for (i=0; i<table->total_chunks; i++)
241 chunks_map[i] = MAP_AVAIL;
243 // build occupided/available space map
244 table->allocated_chunks = 0;
245 table->free_chunks = table->total_chunks;
246 for (i=0; i<table->part_count; i++) {
247 apa_header *part_hdr = (apa_header *)&table->parts[i].header;
248 int part_num = part_hdr->start / ((128 * 1024 * 1024) / 512);
249 int num_parts = part_hdr->length / ((128 * 1024 * 1024) / 512);
251 // "alloc" num_parts starting at part_num
252 while (num_parts) {
253 if (chunks_map[part_num] == MAP_AVAIL)
254 chunks_map[part_num] = (part_hdr->main == 0 ? MAP_MAIN : MAP_SUB);
255 else
256 chunks_map[part_num] = MAP_COLL; // collision
257 part_num++;
258 num_parts--;
259 table->allocated_chunks++;
260 table->free_chunks--;
264 if (table->chunks_map)
265 free(table->chunks_map);
267 table->chunks_map = (u8 *)chunks_map;
269 return 0;
272 //-------------------------------------------------------------------------
273 static int apaReadPartitionTable(apa_partition_table_t **table)
275 register u32 nsectors, lba;
276 register int ret;
278 // get number of sectors on device
279 nsectors = hddGetTotalSectors();
281 // if not already done allocate space for the partition table
282 if (*table == NULL) {
283 *table = malloc(sizeof(apa_partition_table_t));
284 if (*table == NULL) {
285 return -2;
288 else // if partition table was already allocated, Free all
289 apaFreePartitionTable(*table);
291 // clear the partition table
292 memset(*table, 0, sizeof(apa_partition_table_t));
294 // read the partition table
295 lba = 0;
296 do {
297 apa_header apaHeader;
298 // read partition header
299 ret = hddReadSectors(lba, sizeof(apa_header) >> 9, &apaHeader, sizeof(apa_header));
300 if (ret == 0) {
301 // check checksum & header magic
302 if ((apaCheckSum(&apaHeader) == apaHeader.checksum) && (apaHeader.magic == APA_MAGIC)) {
303 if (apaHeader.start < nsectors) {
304 //if ((apaHeader.flags == 0) && (apaHeader.type == 0x1337))
305 ret = apaAddPartition(*table, &apaHeader, 1);
306 if (ret == 0)
307 lba = apaHeader.next;
309 else {
310 ret = 7; // data behind end-of-HDD
311 break;
314 else ret = 1;
317 if ((*table)->part_count > 10000)
318 ret = 7;
320 } while (ret == 0 && lba != 0);
322 if (ret == 0) {
323 (*table)->device_size_in_mb = nsectors >> 11;
324 ret = apaSetPartitionTableStats(*table);
326 else {
327 apaFreePartitionTable(*table);
328 ret = 20000 + (*table)->part_count;
331 return ret;
334 //-------------------------------------------------------------------------
335 static int apaCheckLinkedList(apa_partition_table_t *table, int flag)
338 register int i;
340 for (i=0; i<table->part_count; i++) {
341 apa_partition_t *prev = (apa_partition_t *)&table->parts[(i > 0 ? i - 1 : table->part_count - 1)];
342 apa_partition_t *curr = (apa_partition_t *)&table->parts[i];
343 apa_partition_t *next = (apa_partition_t *)&table->parts[(i + 1 < table->part_count ? i + 1 : 0)];
345 if (curr->header.prev != prev->header.start) {
346 if (!flag)
347 return -1;
348 else {
349 curr->modified = 1;
350 curr->header.prev = prev->header.start;
353 if (curr->header.next != next->header.start) {
354 if (!flag)
355 return -1;
356 else {
357 curr->modified = 1;
358 curr->header.next = next->header.start;
361 if ((flag) && (curr->modified))
362 curr->header.checksum = apaCheckSum(&curr->header);
365 return 0;
368 //-------------------------------------------------------------------------
369 static int apaCheckPartitionTable(apa_partition_table_t *table)
371 register int i, j, k;
373 u32 total_sectors = table->device_size_in_mb * 1024 * 2;
375 for (i=0; i<table->part_count; i++) {
376 apa_header *part_hdr = &table->parts[i].header;
378 // check checksum
379 if (part_hdr->checksum != apaCheckSum(part_hdr))
380 return -1;
382 // check for data behind end of HDD
383 if (!((part_hdr->start < total_sectors) && (part_hdr->start + part_hdr->length <= total_sectors)))
384 return -2;
386 // check partition length is multiple of 128 MB
387 if ((part_hdr->length % ((128 * 1024 * 1024) / 512)) != 0)
388 return -3;
390 // check partition start is multiple of partion length
391 if ((part_hdr->start % part_hdr->length) != 0)
392 return -4;
394 // check all subs partitions
395 if ((part_hdr->main == 0) && (part_hdr->flags == 0) && (part_hdr->start != 0)) {
396 int count = 0;
398 for (j=0; j<table->part_count; j++) {
399 apa_header *part2_hdr = &table->parts[j].header;
400 if (part2_hdr->main == part_hdr->start) { // sub-partition of current main partition
401 int found;
402 if (part2_hdr->flags != APA_FLAG_SUB)
403 return -5;
405 found = 0;
406 for (k=0; k<part_hdr->nsub; k++) {
407 if (part_hdr->subs[k].start == part2_hdr->start) { // in list
408 if (part_hdr->subs[k].length != part2_hdr->length)
409 return -6;
410 found = 1;
411 break;
414 if (!found)
415 return -7; // not found in the list
417 count++;
420 if (count != part_hdr->nsub)
421 return -8; // wrong number of sub-partitions
425 // verify double-linked list
426 if (apaCheckLinkedList(table, 0) < 0)
427 return -9; // bad links
429 LOG("apaCheckPartitionTable OK!\n");
431 return 0;
434 //-------------------------------------------------------------------------
435 static int apaWritePartitionTable(apa_partition_table_t *table)
437 register int ret, i;
439 LOG("apaWritePartitionTable\n");
441 ret = apaCheckPartitionTable(table);
442 if (ret < 0)
443 return ret;
445 for (i=0; i<table->part_count; i++) {
447 if (table->parts[i].modified) {
448 apa_header *part_hdr = &table->parts[i].header;
449 LOG("writing 2 sectors at sector 0x%X\n", part_hdr->start);
450 #ifndef TEST_WRITES
451 ret = hddWriteSectors(part_hdr->start, 2, (void *)part_hdr);
452 if (ret < 0)
453 return -10;
454 #endif
458 return 0;
461 //-------------------------------------------------------------------------
462 static int apaFindPartition(apa_partition_table_t *table, char *partname)
464 register int ret, i;
466 ret = -1;
468 for (i=0; i<table->part_count; i++) {
470 apa_header *part_hdr = &table->parts[i].header;
472 // if part found, return its index
473 if ((part_hdr->main == 0) && (!strcmp(part_hdr->id, partname))) {
474 ret = i;
475 break;
479 return ret;
482 //-------------------------------------------------------------------------
483 static int apaDeletePartition(apa_partition_table_t *table, char *partname)
485 register int i, part_index;
486 register int count = 1;
487 u32 pending_deletes[APA_MAXSUB];
489 LOG("apaDeletePartition %s\n", partname);
491 // retrieve part index
492 part_index = apaFindPartition(table, partname);
493 if (part_index < 0)
494 return -1;
496 apa_header *part_hdr = &table->parts[part_index].header;
498 // Do not delete system partitions
499 if (part_hdr->type == 1)
500 return -2;
502 // preserve a list of starting sectors of partitions to be deleted
503 pending_deletes[0] = part_hdr->start;
504 LOG("apaDeletePartition: found part at %d \n", part_hdr->start / 262144);
505 for (i=0; i<part_hdr->nsub; i++) {
506 LOG("apaDeletePartition: found subpart at %d \n", part_hdr->subs[i].start / 262144);
507 pending_deletes[count++] = part_hdr->subs[i].start;
510 LOG("apaDeletePartition: number of subpartitions=%d count=%d\n", part_hdr->nsub, count);
512 // remove partitions from the double-linked list
513 i = 0;
514 while (i<table->part_count) {
515 int j;
516 int found = 0;
518 for (j=0; j<count; j++) {
519 if (table->parts[i].header.start == pending_deletes[j]) {
520 found = 1;
521 break;
525 if (found) {
526 // remove this partition
527 int part_num = table->parts[i].header.start / 262144; // 262144 sectors == 128M
528 int num_parts = table->parts[i].header.length / 262144;
530 LOG("apaDeletePartition: partition found! num_parts=%d part_num=%d\n", num_parts, part_num);
532 memmove((void *)&table->parts[i], (void *)&table->parts[i+1], sizeof(apa_partition_t) * (table->part_count-i-1));
533 table->part_count--;
535 // "free" num_parts starting at part_num
536 while (num_parts) {
537 table->chunks_map[part_num] = MAP_AVAIL;
538 part_num++;
539 num_parts--;
540 table->allocated_chunks--;
541 table->free_chunks++;
544 else
545 i++;
548 apaCheckLinkedList(table, 1);
550 return 0;
553 //-------------------------------------------------------------------------
554 static int hddGetHDLGameInfo(apa_header *header, hdl_game_info_t *ginfo)
556 u32 i, size;
557 const u32 offset = 0x101000;
558 char buf[1024];
559 register int ret;
561 u32 start_sector = header->start + offset / 512;
563 ret = hddReadSectors(start_sector, 2, buf, 2 * 512);
564 if (ret == 0) {
566 hdl_apa_header *hdl_header = (hdl_apa_header *)buf;
568 // calculate total size
569 size = header->length;
571 for (i = 0; i < header->nsub; i++)
572 size += header->subs[i].length;
574 memcpy(ginfo->partition_name, header->id, APA_IDMAX);
575 ginfo->partition_name[APA_IDMAX] = 0;
576 strncpy(ginfo->name, hdl_header->gamename, HDL_GAME_NAME_MAX);
577 strncpy(ginfo->startup, hdl_header->startup, 12);
578 ginfo->hdl_compat_flags = hdl_header->hdl_compat_flags;
579 ginfo->ops2l_compat_flags = hdl_header->ops2l_compat_flags;
580 ginfo->dma_type = hdl_header->dma_type;
581 ginfo->dma_mode = hdl_header->dma_mode;
582 ginfo->layer_break = hdl_header->layer1_start;
583 ginfo->disctype = hdl_header->discType;
584 ginfo->start_sector = start_sector;
585 ginfo->total_size_in_kb = size / 2;
587 else ret = -1;
589 return ret;
592 //-------------------------------------------------------------------------
593 int hddGetHDLGamelist(hdl_games_list_t **game_list)
595 register int ret;
597 ret = apaReadPartitionTable(&ptable);
598 if (ret == 0) {
600 u32 i, count = 0;
601 void *tmp;
603 if (*game_list != NULL) {
604 for (i = 0; i < (*game_list)->count; i++) {
605 if ((*game_list)->games != NULL)
606 free((*game_list)->games);
610 for (i = 0; i < ptable->part_count; i++)
611 count += ((ptable->parts[i].header.flags == 0) && (ptable->parts[i].header.type == 0x1337));
613 tmp = malloc(sizeof(hdl_game_info_t) * count);
614 if (tmp != NULL) {
616 memset(tmp, 0, sizeof(hdl_game_info_t) * count);
617 *game_list = malloc(sizeof(hdl_games_list_t));
618 if (*game_list != NULL) {
620 u32 index = 0;
621 memset(*game_list, 0, sizeof(hdl_games_list_t));
622 (*game_list)->count = count;
623 (*game_list)->games = tmp;
624 (*game_list)->total_chunks = ptable->total_chunks;
625 (*game_list)->free_chunks = ptable->free_chunks;
627 for (i = 0; ((ret == 0) && (i < ptable->part_count)); i++) {
628 apa_header *header = &ptable->parts[i].header;
629 if ((header->flags == 0) && (header->type == 0x1337))
630 ret = hddGetHDLGameInfo(header, (*game_list)->games + index++);
633 if (ret != 0)
634 free(*game_list);
635 } else
636 ret = -2;
638 if (ret != 0)
639 free(tmp);
641 } else ret = -2;
644 return ret;
647 //-------------------------------------------------------------------------
648 int hddFreeHDLGamelist(hdl_games_list_t *game_list)
650 register int i;
652 apaFreePartitionTable(ptable);
654 if (game_list != NULL) {
655 for (i = 0; i < game_list->count; i++) {
656 if (game_list->games != NULL)
657 free(game_list->games);
660 free(game_list);
663 return 0;
666 //-------------------------------------------------------------------------
667 int hddSetHDLGameInfo(hdl_game_info_t *ginfo)
669 const u32 offset = 0x101000;
670 char buf[1024];
671 int part_index;
673 if (ptable == NULL)
674 return -1;
676 // retrieve part index
677 part_index = apaFindPartition(ptable, ginfo->partition_name);
678 if (part_index < 0)
679 return -1;
681 apa_header *header = &ptable->parts[part_index].header;
682 if (header == NULL)
683 return -2;
685 u32 start_sector = header->start + offset / 512;
687 if (hddReadSectors(start_sector, 2, buf, 2 * 512) != 0)
688 return -3;
690 hdl_apa_header *hdl_header = (hdl_apa_header *)buf;
692 // checking deadfeed magic & PS2 game magic
693 if (hdl_header->checksum != 0xdeadfeed)
694 return -4;
696 // just change game name and compat flags !!!
697 strncpy(hdl_header->gamename, ginfo->name, 160);
698 hdl_header->gamename[159] = '\0';
699 //hdl_header->hdl_compat_flags = ginfo->hdl_compat_flags;
700 hdl_header->ops2l_compat_flags = ginfo->ops2l_compat_flags;
701 hdl_header->dma_type = ginfo->dma_type;
702 hdl_header->dma_mode = ginfo->dma_mode;
704 if (hddWriteSectors(start_sector, 2, buf) != 0)
705 return -5;
707 hddFlushCache();
709 return 0;
712 //-------------------------------------------------------------------------
713 int hddDeleteHDLGame(hdl_game_info_t *ginfo)
715 register int ret;
717 LOG("hddDeleteHDLGame() game name='%s'\n", ginfo->name);
719 if (ptable == NULL)
720 return -1;
722 ret = apaDeletePartition(ptable, ginfo->partition_name);
723 if (ret < 0)
724 return -2;
726 ret = apaWritePartitionTable(ptable);
727 if (ret < 0)
728 return -3;
730 hddFlushCache();
732 LOG("hddDeleteHDLGame: '%s' deleted!\n", ginfo->name);
734 return 0;