summaryrefslogtreecommitdiff
path: root/src/libpart/prep.c
blob: b3bcdb9d82edecb7fa2f77a8bbbc38341c0ba622 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * <prep.c>
 *
 * Open Hack'Ware PREP BIOS partition type management
 * 
 * Copyright (c) 2004-2005 Jocelyn Mayer
 * 
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License V2
 *   as published by the Free Software Foundation
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdlib.h>
#include <stdio.h>
#include "bios.h"
#include "libpart.h"

/* PREP image management */
typedef struct MSDOS_part_t MSDOS_part_t;
struct MSDOS_part_t {
    uint8_t  boot_ind;
    uint8_t  start_head;
    uint8_t  start_sect;
    uint8_t  start_cyl;
    uint8_t  sys_ind;
    uint8_t  end_head;
    uint8_t  end_sect;
    uint8_t  end_cyl;
    uint32_t LBA_start;
    uint32_t LBA_end;
}  __attribute__ ((packed));

part_t *PREP_find_partition (bloc_device_t *bd)
{
    MSDOS_part_t *p;
    part_t *part;
    uint8_t *buffer;
    uint32_t boot_offset, boot_size;
    int i;

    part = NULL;
    buffer = malloc(0x200);
    bd_seek(bd, 0, 0);
    if (bd_read(bd, buffer, 0x200) < 0) {
        ERROR("Unable to read boot sector from boot device. Aborting...\n");
        goto error;
    }
    if (buffer[0x1FE] != 0x55 || buffer[0x1FF] != 0xAA) {
        ERROR("No MSDOS signature (%x %x %x %x)\n",
              buffer[0x000], buffer[0x001], buffer[0x1FE], buffer[0x1FF]);
        goto error;
    }
    for (i = 0; i < 4; i++) {
        p = (void *)(&buffer[0x1BE + (0x10 * i)]);
        DPRINTF("partition %d: %x is %sbootable - ", i, p->boot_ind,
                (p->boot_ind & 0x80) ? "" : "not ");
        DPRINTF("start %0x end %0x type %x\n",
                get_le32(&p->LBA_start), get_le32(&p->LBA_end), p->sys_ind);
#if 0
        if (p->boot_ind != 0x80)
            continue;
#endif
        switch (p->sys_ind) {
        case 0x07: /* HPFS/NTFS */
            goto register_nonboot;
        case 0x08: /* AIX */
            goto register_nonboot;
        case 0x09: /* AIX bootable */
            /* Not supported by now */
            break;
        case 0x0A: /* OS/2 boot manager */
            /* Not supported by now */
            break;
        case 0x41: /* PREP boot */
            part = malloc(sizeof(part_t));
            memset(part, 0, sizeof(part_t));
            part->bd = bd;
            part_set_blocsize(bd, part, 0x200);
            /* Convert start and size into LBA */
            if ((p->start_head != 0 || p->start_cyl != 0 ||
                 p->start_sect != 0) && p->LBA_start == 0) {
                DPRINTF("start: use CHS\n");
                part->start = bd_CHS2sect(bd, p->start_cyl,
                                          p->start_head,
                                          p->start_sect);
            } else {
                DPRINTF("start: use LBA\n");
                part->start = get_le32(&p->LBA_start);
            }
            if ((p->end_head != 0 || p->end_cyl != 0 ||
                 p->end_sect != 0) && p->LBA_end == 0) {
                DPRINTF("end: use CHS\n");
                part->size = bd_CHS2sect(bd, p->end_cyl,
                                         p->end_head, p->end_sect);
            } else {
                DPRINTF("end: use LBA\n");
                part->size = get_le32(&p->LBA_end);
            }
            /* XXX: seems that some (AIX !)
             * code the size here instead of partition end
             */
            if (part->size > part->start)
                part->size -= part->start;
            DPRINTF("LBA: start %0x size: %0x\n", part->start, part->size);
            /* Now, find and check boot record */
            part_seek(part, 0, 0);
            if (bd_read(bd, buffer, part->bloc_size) < 0) {
                ERROR("%s sector_read failed (%d)\n", __func__, i);
                freep(&part);
                goto error;
            }
#if 0
            if (buffer[0x1FE] != 0x55 || buffer[0x1FF] != 0xAA) {
                ERROR("No MSDOS signature on PREP boot record\n");
                freep(&part);
                goto error;
            }
#endif
            boot_offset = get_le32(buffer);
            boot_size =  get_le32(buffer + 4);
            if ((boot_offset & 3) || /*(boot_size & 3) ||*/
                boot_offset == 0 || boot_size == 0) {
                DPRINTF("Suspicious PREP boot parameters: %08x %08x %08x %08x\n",
                    part->start, part->start * 0x200, boot_offset, boot_size);
#if 0
                freep(&part);
                goto error;
#else
                /* IBM boot blocs respect the norm better than others... */
                part->start++;
                part_seek(part, 0, 0);
                if (bd_read(bd, buffer, part->bloc_size) < 0) {
                    ERROR("%s sector_read failed (%d)\n", __func__, i);
                    freep(&part);
                    goto error;
                }
                boot_offset = get_le32(buffer);
                boot_size =  get_le32(buffer + 4);
#endif
            }
            DPRINTF("PREP boot parameters: %08x %08x %08x %08x\n",
                    part->start, part->start * 0x200, boot_offset, boot_size);
            if (boot_size > (part->size * part->bloc_size)) {
                ERROR("PREP boot image greater than boot partition: %0x %0x\n",
                      boot_size, part->size * part->bloc_size);
#if 0
                freep(&part);
                goto error;
#endif
            }
            part->boot_start.bloc = 0;
            part->boot_start.offset = 0;
            part->boot_size.bloc = boot_size / part->bloc_size;
            part->boot_size.offset = boot_size % part->bloc_size;
            part->boot_load = 0;
            part->boot_entry = boot_offset - part->bloc_size;
            part->flags = PART_TYPE_PREP | PART_FLAG_BOOT;
            part_register(bd, part, "PREP boot");
            fs_raw_set_bootfile(part, part->boot_start.bloc,
                                part->boot_start.offset,
                                part->boot_size.bloc,
                                part->boot_size.offset);
            break;
        case 0x63: /* GNU Hurd */
            goto register_nonboot;
        case 0x83: /* Linux */
            goto register_nonboot;
        case 86 ... 87: /* NFTS volume set */
            /* Not supported by now */
            break;
        case 0x8E: /* Linux LVM */
            /* Not supported by now */
            break;
        case 0x96: /* AIX seems to use this to identify ISO 9660 'partitions' */
            break;
        case 0xA5: /* FreeBSD */
            goto register_nonboot;
        case 0xA6: /* OpenBSD */
            goto register_nonboot;
        case 0xA7: /* NeXTSTEP */
            goto register_nonboot;
        case 0xA8: /* Darwin UFS */
            goto register_nonboot;
        case 0xA9: /* NetBSD */
            goto register_nonboot;
        case 0xAB: /* Darwin boot */
            /* Not supported by now */
            break;
        case 0xBE: /* Solaris boot */
            /* Not supported by now */
            break;
        case 0xEB: /* BeOS fs */
            goto register_nonboot;
        case 0xFD: /* Linux RAID */
            /* Not supported by now */
            break;
        default:
            break;
        register_nonboot:
            break;
        }
    }
 error:
    free(buffer);

    return part;
}