revert between 56095 -> 55830 in arch
[AROS.git] / arch / ppc-sam440 / boot / parthenope / src / cdrom.c
blobabb99afd1866ff33f4df658700a91e894259bc25
1 /*
2 * $Id$
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17 * MA 02111-1307 USA
20 #define DEBUG 0
22 #include "context.h"
23 #include "device.h"
24 #include "uboot.h"
25 /* #include <debug.h> */
27 /* The iso9660 code from GRUB2 */
28 #define ISO9660_FSTYPE_DIR 0040000
29 #define ISO9660_FSTYPE_REG 0100000
30 #define ISO9660_FSTYPE_SYMLINK 0120000
31 #define ISO9660_FSTYPE_MASK 0170000
33 #define ISO9660_LOG2_BLKSZ 2
34 #define ISO9660_BLKSZ 2048
36 #define ISO9660_RR_DOT 2
37 #define ISO9660_RR_DOTDOT 4
39 #define ISO9660_MAX_DEPTH 8
41 /* The head of a volume descriptor. */
42 struct iso9660_voldesc {
43 uint8_t type;
44 uint8_t magic[5];
45 uint8_t version;
46 } __attribute__ ((packed));
48 /* A directory entry. */
49 struct iso9660_dir {
50 uint8_t len;
51 uint8_t ext_sectors;
52 uint32_t first_sector;
53 uint32_t first_sector_be;
54 uint32_t size;
55 uint32_t size_be;
56 uint8_t unused1[7];
57 uint8_t flags;
58 uint8_t unused2[6];
59 uint8_t namelen;
60 } __attribute__ ((packed));
62 /* The primary volume descriptor. Only little endian is used. */
63 struct iso9660_primary_voldesc {
64 struct iso9660_voldesc voldesc;
65 uint8_t unused1[33];
66 uint8_t volname[32];
67 uint8_t unused2[60];
68 uint32_t path_table_size;
69 uint8_t unused3[4];
70 uint32_t path_table;
71 uint8_t unused4[12];
72 struct iso9660_dir rootdir;
73 } __attribute__ ((packed));
75 /* A single entry in the path table. */
76 struct iso9660_path {
77 uint8_t len;
78 uint8_t sectors;
79 uint32_t first_sector;
80 uint16_t parentdir;
81 uint8_t name[0];
82 } __attribute__ ((packed));
84 /* An entry in the System Usage area of the directory entry. */
85 struct iso9660_susp_entry {
86 uint8_t sig[2];
87 uint8_t len;
88 uint8_t version;
89 uint8_t data[0];
90 } __attribute__ ((packed));
92 /* The CE entry. This is used to describe the next block where data
93 can be found. */
94 struct iso9660_susp_ce {
95 struct iso9660_susp_entry entry;
96 uint32_t blk;
97 uint32_t blk_be;
98 uint32_t off;
99 uint32_t off_be;
100 uint32_t len;
101 uint32_t len_be;
102 } __attribute__ ((packed));
104 typedef struct cdrom_boot_dev {
105 boot_dev_t dev;
106 block_dev_desc_t *phys;
107 context_t *ctx;
108 struct iso9660_primary_voldesc voldesc;
109 struct iso9660_dir rootdir;
110 uint8_t tmpbuf[2048];
111 uint32_t sector_in_buf;
112 uint8_t rr;
113 uint8_t susp_skip;
114 } cdrom_boot_dev_t;
116 static int cdrom_read(cdrom_boot_dev_t * boot, uint32_t block, uint32_t offset,
117 uint32_t length, char *dest)
119 uint32_t len;
120 uint32_t transferred = 0;
122 while (offset >= 2048) {
123 block++;
124 offset -= 2048;
127 if (offset) {
128 if (boot->sector_in_buf != block) {
129 if (!boot->phys->
130 block_read(boot->phys->dev, block, 1, boot->tmpbuf))
131 return -1;
132 boot->sector_in_buf = block;
135 len = 2048 - offset;
136 if (length < len)
137 len = length;
139 memcpy(dest, &boot->tmpbuf[offset], len);
140 offset = 0;
141 length -= len;
142 dest += len;
143 block++;
144 transferred += len;
147 if (length / 2048) {
148 uint32_t blocks_read = length / 2048;
150 if (!boot->phys->
151 block_read(boot->phys->dev, block, blocks_read, dest))
152 return -1;
154 transferred += 2048 * blocks_read;
155 block += blocks_read;
156 dest += 2048 * blocks_read;
157 length -= 2048 * blocks_read;
160 if (length) {
161 if (boot->sector_in_buf != block) {
162 if (!boot->phys->
163 block_read(boot->phys->dev, block, 1, boot->tmpbuf))
164 return -1;
166 boot->sector_in_buf = block;
169 len = 2048 - offset;
170 if (length < len)
171 len = length;
173 memcpy(dest, boot->tmpbuf, len);
174 transferred += len;
177 return 0;
180 static int iterate_entries(cdrom_boot_dev_t * dev, uint32_t block,
181 uint32_t offset, uint32_t size,
182 int (*iter_func) (struct iso9660_susp_entry *,
183 void *userdata), void *userdata)
185 char *sua = malloc(size);
186 char *ptr;
188 if (!sua)
189 return -1;
191 /* read the SUA into memory */
192 if (cdrom_read(dev, block, offset, size, sua))
193 return -1;
195 ptr = sua;
196 do {
197 struct iso9660_susp_entry *e = (struct iso9660_susp_entry *)ptr;
199 /* In case of CE entry get the new SUA and try again */
200 while (e->sig[0] == 'C' && e->sig[1] == 'E') {
201 struct iso9660_susp_ce *ce =
202 (struct iso9660_susp_ce *)e;
203 block = ce->blk_be;
204 offset = ce->off_be;
205 size = ce->len_be;
207 free(sua);
208 sua = malloc(size);
210 if (!sua)
211 return -1;
212 if (cdrom_read(dev, block, offset, size, sua))
213 return -1;
214 ptr = sua;
215 e = (struct iso9660_susp_entry *)ptr;
218 /* ST entry means STOP NOW */
219 if (e->sig[0] == 'S' && e->sig[1] == 'T') {
220 free(sua);
221 return 0;
224 /* Call the user function. If it returns non-zero value, break iteration */
225 if (iter_func(e, userdata)) {
226 free(sua);
227 return 0;
230 ptr += e->len;
231 } while (ptr < sua + size - 1);
233 free(sua);
235 return 0;
238 struct tmp_data {
239 context_t *ctx;
240 struct iso9660_susp_entry *entry;
241 uint8_t sig[2];
244 static int my_iteration(struct iso9660_susp_entry *e, struct tmp_data *d)
246 if (e->sig[0] == d->sig[0] && e->sig[1] == d->sig[1]) {
247 d->entry = malloc(e->len);
248 memcpy(d->entry, e, e->len);
249 return 1;
250 } else
251 return 0;
254 static struct iso9660_susp_entry *get_entry(cdrom_boot_dev_t * dev,
255 uint32_t block, uint32_t offset,
256 uint32_t size, uint8_t sig[2])
258 struct tmp_data d;
259 context_t *ctx = dev->ctx;
261 d.ctx = ctx;
262 d.entry = NULL;
263 d.sig[0] = sig[0];
264 d.sig[1] = sig[1];
266 iterate_entries(dev, block, offset, size,
267 (int (*)(struct iso9660_susp_entry *, void *userdata))
268 my_iteration, &d);
270 return d.entry;
273 static int fs_cdrom_find_file(cdrom_boot_dev_t * this, char *filename,
274 uint32_t * fblock, uint32_t * fsize)
276 char *local_filename;
277 char *elements[ISO9660_MAX_DEPTH + 1];
278 int i, j, depth = 0;
279 int block = 0;
280 int offset = 0;
281 int size = 0;
282 char *fname = NULL;
283 int fname_alloc = 0;
284 int found = 0;
286 struct iso9660_dir dirent;
288 if (filename[0] == '/')
289 filename++;
291 local_filename = malloc(strlen(filename) + 1);
292 memcpy(local_filename, filename, strlen(filename) + 1);
294 /* Split the filename into separate elements */
295 elements[depth++] = local_filename;
296 for (i = 0; i < strlen(filename) - 1; i++) {
297 if (local_filename[i] == '/') {
298 local_filename[i] = 0;
299 elements[depth++] = &local_filename[i + 1];
303 block = this->voldesc.rootdir.first_sector_be;
304 size = this->voldesc.rootdir.size_be;
306 for (j = 0; j < depth; j++) {
307 found = 0;
309 while (!found && offset < size) {
310 if (cdrom_read(this, block, offset, sizeof(dirent),
311 (char *)&dirent))
312 return 0;
314 if (!dirent.len) {
315 offset = (offset / 2048 + 1) * 2048;
316 continue;
320 char *name = NULL;
321 int nameoffset = offset + sizeof(dirent);
322 int sua_off =
323 (sizeof(dirent) + dirent.namelen + 1 -
324 (dirent.namelen % 2));
325 int sua_size = dirent.len - sua_off;
327 sua_off += offset + this->susp_skip;
329 fname = NULL;
330 fname_alloc = 0;
332 if (this->rr) {
333 struct iso9660_susp_entry *entry =
334 get_entry(this, block,
335 sua_off,
336 sua_size,
337 (unsigned char *)"NM");
338 if (entry) {
339 if (entry->
340 data[0] & ISO9660_RR_DOT)
341 fname = ".";
342 else if (entry->
343 data[0] &
344 ISO9660_RR_DOTDOT)
345 fname = "..";
346 else {
347 if (entry->len > 5) {
348 int __size =
349 entry->len -
351 fname =
352 malloc
353 (__size +
355 if (!fname) {
356 return
359 fname_alloc = 1;
360 memcpy(fname,
361 &entry->
362 data[1],
363 __size);
364 fname[__size] =
365 '\0';
368 free(entry);
372 if (!fname) {
373 name = malloc(dirent.namelen + 1);
375 if (cdrom_read
376 (this, block, nameoffset,
377 dirent.namelen, name))
378 return 0;
380 name[dirent.namelen] = '\0';
381 for (i = dirent.namelen; i; i--) {
382 if (name[i] == ';') {
383 name[i] = '\0';
384 break;
388 if (dirent.namelen == 1 && name[0] == 0)
389 fname = ".";
390 else if (dirent.namelen == 1
391 && name[0] == 1)
392 fname = "..";
393 else
394 fname = name;
398 if (!strcasecmp(elements[j], fname)) {
399 int type = dirent.flags & 3;
401 if ((j + 1 < depth && type == 2)
402 || (j + 1 == depth && type != 2)) {
403 found = 1;
407 if (fname_alloc && fname)
408 free(fname);
410 if (name)
411 free(name);
414 offset += dirent.len;
417 if (found) {
418 /* Entry found. Go to the next level */
419 block = dirent.first_sector_be;
420 size = dirent.size_be;
421 offset = 0;
425 free(local_filename);
427 if (found) {
428 *fblock = block;
429 *fsize = size;
430 // cdrom_read(this, block, 0, size, buffer);
431 //while(1);
432 return size;
435 return 0;
438 #define D(x) x
440 static int fs_cdrom_destroy(cdrom_boot_dev_t * this)
442 free(this);
443 return 0;
446 static int fs_cdrom_load_file(cdrom_boot_dev_t * this, char *filename,
447 void *buffer)
449 uint32_t block;
450 uint32_t size = 0;
452 if (fs_cdrom_find_file(this, filename, &block, &size)) {
453 cdrom_read(this, block, 0, size, buffer);
456 return size;
459 static block_dev_desc_t *get_dev()
461 block_dev_desc_t *bdev = NULL;
462 SCAN_HANDLE hnd;
463 uint32_t blocksize;
464 void *scan_list = get_scan_list();
466 if(scan_list == NULL)
467 return NULL;
469 for (hnd = start_unit_scan(scan_list, &blocksize);
470 hnd != NULL; hnd = next_unit_scan(hnd, &blocksize)) {
471 if (hnd->ush_device.type == DEV_TYPE_CDROM) {
472 bdev = malloc(sizeof(block_dev_desc_t));
473 memmove(bdev, &hnd->ush_device,
474 sizeof(block_dev_desc_t));
475 break;
479 end_unit_scan(hnd);
480 end_global_scan();
482 return bdev;
485 boot_dev_t *cdrom_create()
487 char *sp;
488 block_dev_desc_t *dev = get_dev();
490 if (dev == NULL)
491 return NULL;
493 cdrom_boot_dev_t *boot = malloc(sizeof(cdrom_boot_dev_t));
495 asm volatile ("mr %0,%%r1":"=r" (sp));
497 if (boot) {
498 int sua_pos;
499 int sua_size;
500 char *sua;
501 struct iso9660_susp_entry *entry;
503 char huge_array[1000];
504 huge_array[1] = 1;
505 huge_array[999] = 2;
506 (void)huge_array; /* Placeholder to eat stack */
508 boot->phys = dev;
509 boot->dev.load_file =
510 (int (*)(void *, char *, void *))fs_cdrom_load_file;
511 boot->dev.destroy = (void (*)(void *))fs_cdrom_destroy;
512 boot->rr = 0;
514 if (cdrom_read(boot, 16, 0, sizeof(boot->voldesc),
515 (char *)&boot->voldesc)) {
516 goto fail;
518 // if (!phys->block_read(phys->dev, 16, 1, boot->tmpbuf))
519 // {
520 // D(printf("[BOOT:CD] Cannot read device!\n"));
521 // goto fail;
522 // }
523 // boot->sector_in_buf = 16;
524 // memcpy(&boot->voldesc, boot->tmpbuf, sizeof(boot->voldesc));
526 if (strncmp((char *)boot->voldesc.voldesc.magic, "CD001", 5)) {
527 goto fail;
530 /* Read the system use area and test it to see if SUSP is
531 supported. */
532 if (cdrom_read(boot, boot->voldesc.rootdir.first_sector_be, 0,
533 sizeof(boot->rootdir), (char *)&boot->rootdir)) {
534 goto fail;
536 // if (!phys->block_read(phys->dev, boot->voldesc.rootdir.first_sector_be, 1, boot->tmpbuf))
537 // {
538 // D(printf("[BOOT:CD] Not an iso9660 filesystem\n"));
539 // goto fail;
540 // }
541 // boot->sector_in_buf = boot->voldesc.rootdir.first_sector_be;
542 // memcpy(&boot->rootdir, boot->tmpbuf, sizeof(boot->rootdir));
544 sua_pos = (sizeof(boot->rootdir) + boot->rootdir.namelen
545 + (boot->rootdir.namelen % 2) - 1);
546 sua_size = boot->rootdir.len - sua_pos;
548 sua = malloc(sua_size);
549 // memcpy(sua, &boot->tmpbuf[sua_pos], sua_size);
550 cdrom_read(boot, boot->voldesc.rootdir.first_sector_be, sua_pos,
551 sua_size, sua);
553 entry = (struct iso9660_susp_entry *)sua;
555 if (entry->sig[0] == 'S' && entry->sig[1] == 'P') {
557 boot->susp_skip = entry->data[2];
558 entry = (struct iso9660_susp_entry *)(sua + entry->len);
560 /* Iterate through the SUSP entries and look for the RockRidge marker */
562 entry =
563 get_entry(boot,
564 boot->voldesc.rootdir.first_sector_be,
565 sua_pos, sua_size, (unsigned char *)"RR");
566 if (entry) {
567 boot->rr = 1;
568 free(entry);
571 // do
572 // {
573 // D(printf("%c%c(%d) ", entry->sig[0], entry->sig[1], entry->len));
575 // if (entry->sig[0] == 'R' && entry->sig[1] == 'R')
576 // {
577 // boot->rr = 1;
578 // break;
579 // }
581 // if (!strncmp(entry->sig, "ST", 2))
582 // break;
584 // if (!strncmp(entry->sig, "CE", 2))
585 // {
586 // struct iso9660_susp_ce *ce = (struct iso9660_susp_ce *)entry;
587 // sua_size = ce->len_be;
588 // sua_pos = ce->off_be;
589 // free(sua);
590 // sua = malloc(sua_size);
591 // phys->block_read(phys->dev, ce->blk_be, 1, boot->tmpbuf);
592 // memcpy(sua, &boot->tmpbuf[sua_pos], sua_size);
593 // entry = (struct iso9660_susp_entry *)sua;
594 // }
595 // else
596 // entry = (struct iso9660_susp_entry *)((char*)entry + entry->len);
597 // } while ((char *)entry < (char*)sua + sua_size - 1);
599 /* if (boot->rr) */
600 /* D(printf("[BOOT:CD] The CD supports RockRidge extensions\n")); */
602 free(sua);
605 return &boot->dev;
607 fail:
608 boot->dev.destroy(boot);
609 return NULL;