Initial support for booting from USB drives.
[seabios.git] / src / block.c
1 // Disk setup and access
2 //
3 // Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "disk.h" // struct ata_s
9 #include "biosvar.h" // GET_GLOBAL
10 #include "cmos.h" // inb_cmos
11 #include "util.h" // dprintf
12 #include "ata.h" // process_ata_op
13 #include "usb-msc.h" // process_usb_op
14
15 struct drives_s Drives VAR16VISIBLE;
16
17 struct drive_s *
18 getDrive(u8 exttype, u8 extdriveoffset)
19 {
20     if (extdriveoffset >= ARRAY_SIZE(Drives.idmap[0]))
21         return NULL;
22     return RETRIEVE_GLOBAL_PTR(GET_GLOBAL(Drives.idmap[exttype][extdriveoffset]));
23 }
24
25
26 /****************************************************************
27  * Disk geometry translation
28  ****************************************************************/
29
30 static u8
31 get_translation(struct drive_s *drive_g)
32 {
33     u8 type = GET_GLOBAL(drive_g->type);
34     if (! CONFIG_COREBOOT && type == DTYPE_ATA) {
35         // Emulators pass in the translation info via nvram.
36         u8 ataid = GET_GLOBAL(drive_g->cntl_id);
37         u8 channel = ataid / 2;
38         u8 translation = inb_cmos(CMOS_BIOS_DISKTRANSFLAG + channel/2);
39         translation >>= 2 * (ataid % 4);
40         translation &= 0x03;
41         return translation;
42     }
43
44     // Otherwise use a heuristic to determine translation type.
45     u16 heads = GET_GLOBAL(drive_g->pchs.heads);
46     u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders);
47     u16 spt = GET_GLOBAL(drive_g->pchs.spt);
48     u64 sectors = GET_GLOBAL(drive_g->sectors);
49     u64 psectors = (u64)heads * cylinders * spt;
50     if (!heads || !cylinders || !spt || psectors > sectors)
51         // pchs doesn't look valid - use LBA.
52         return TRANSLATION_LBA;
53
54     if (cylinders <= 1024 && heads <= 16 && spt <= 63)
55         return TRANSLATION_NONE;
56     if (cylinders * heads <= 131072)
57         return TRANSLATION_LARGE;
58     return TRANSLATION_LBA;
59 }
60
61 void
62 setup_translation(struct drive_s *drive_g)
63 {
64     u8 translation = get_translation(drive_g);
65     SET_GLOBAL(drive_g->translation, translation);
66
67     u16 heads = GET_GLOBAL(drive_g->pchs.heads);
68     u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders);
69     u16 spt = GET_GLOBAL(drive_g->pchs.spt);
70     u64 sectors = GET_GLOBAL(drive_g->sectors);
71     const char *desc = NULL;
72
73     switch (translation) {
74     default:
75     case TRANSLATION_NONE:
76         desc = "none";
77         break;
78     case TRANSLATION_LBA:
79         desc = "lba";
80         spt = 63;
81         if (sectors > 63*255*1024) {
82             heads = 255;
83             cylinders = 1024;
84             break;
85         }
86         u32 sect = (u32)sectors / 63;
87         heads = sect / 1024;
88         if (heads>128)
89             heads = 255;
90         else if (heads>64)
91             heads = 128;
92         else if (heads>32)
93             heads = 64;
94         else if (heads>16)
95             heads = 32;
96         else
97             heads = 16;
98         cylinders = sect / heads;
99         break;
100     case TRANSLATION_RECHS:
101         desc = "r-echs";
102         // Take care not to overflow
103         if (heads==16) {
104             if (cylinders>61439)
105                 cylinders=61439;
106             heads=15;
107             cylinders = (u16)((u32)(cylinders)*16/15);
108         }
109         // then go through the large bitshift process
110     case TRANSLATION_LARGE:
111         if (translation == TRANSLATION_LARGE)
112             desc = "large";
113         while (cylinders > 1024) {
114             cylinders >>= 1;
115             heads <<= 1;
116
117             // If we max out the head count
118             if (heads > 127)
119                 break;
120         }
121         break;
122     }
123     // clip to 1024 cylinders in lchs
124     if (cylinders > 1024)
125         cylinders = 1024;
126     dprintf(1, "drive %p: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d s=%d\n"
127             , drive_g
128             , drive_g->pchs.cylinders, drive_g->pchs.heads, drive_g->pchs.spt
129             , desc
130             , cylinders, heads, spt
131             , (u32)sectors);
132
133     SET_GLOBAL(drive_g->lchs.heads, heads);
134     SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
135     SET_GLOBAL(drive_g->lchs.spt, spt);
136 }
137
138
139 /****************************************************************
140  * Drive mapping
141  ****************************************************************/
142
143 // Fill in Fixed Disk Parameter Table (located in ebda).
144 static void
145 fill_fdpt(struct drive_s *drive_g, int hdid)
146 {
147     if (hdid > 1)
148         return;
149
150     u16 nlc   = GET_GLOBAL(drive_g->lchs.cylinders);
151     u16 nlh   = GET_GLOBAL(drive_g->lchs.heads);
152     u16 nlspt = GET_GLOBAL(drive_g->lchs.spt);
153
154     u16 npc   = GET_GLOBAL(drive_g->pchs.cylinders);
155     u16 nph   = GET_GLOBAL(drive_g->pchs.heads);
156     u16 npspt = GET_GLOBAL(drive_g->pchs.spt);
157
158     struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid];
159     fdpt->precompensation = 0xffff;
160     fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3);
161     fdpt->landing_zone = npc;
162     fdpt->cylinders = nlc;
163     fdpt->heads = nlh;
164     fdpt->sectors = nlspt;
165
166     if (nlc != npc || nlh != nph || nlspt != npspt) {
167         // Logical mapping present - use extended structure.
168
169         // complies with Phoenix style Translated Fixed Disk Parameter
170         // Table (FDPT)
171         fdpt->phys_cylinders = npc;
172         fdpt->phys_heads = nph;
173         fdpt->phys_sectors = npspt;
174         fdpt->a0h_signature = 0xa0;
175
176         // Checksum structure.
177         fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
178     }
179
180     if (hdid == 0)
181         SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
182                                  struct extended_bios_data_area_s, fdpt[0])));
183     else
184         SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
185                                  struct extended_bios_data_area_s, fdpt[1])));
186 }
187
188 // Map a drive (that was registered via add_bcv_hd)
189 void
190 map_hd_drive(struct drive_s *drive_g)
191 {
192     // fill hdidmap
193     u8 hdcount = GET_BDA(hdcount);
194     if (hdcount >= ARRAY_SIZE(Drives.idmap[0])) {
195         warn_noalloc();
196         return;
197     }
198     dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdcount);
199     Drives.idmap[EXTTYPE_HD][hdcount] = STORE_GLOBAL_PTR(drive_g);
200     SET_BDA(hdcount, hdcount + 1);
201
202     // Fill "fdpt" structure.
203     fill_fdpt(drive_g, hdcount);
204 }
205
206 // Find spot to add a drive
207 static void
208 add_ordered_drive(struct drive_s **idmap, u8 *count, struct drive_s *drive_g)
209 {
210     if (*count >= ARRAY_SIZE(Drives.idmap[0])) {
211         warn_noalloc();
212         return;
213     }
214     struct drive_s **pos = &idmap[*count];
215     *count = *count + 1;
216     if (CONFIG_THREADS) {
217         // Add to idmap with assured drive order.
218         struct drive_s **end = pos;
219         for (;;) {
220             struct drive_s **prev = pos - 1;
221             if (prev < idmap)
222                 break;
223             struct drive_s *prevdrive = *prev;
224             if (prevdrive->type < drive_g->type
225                 || (prevdrive->type == drive_g->type
226                     && prevdrive->cntl_id < drive_g->cntl_id))
227                 break;
228             pos--;
229         }
230         if (pos != end)
231             memmove(pos+1, pos, (void*)end-(void*)pos);
232     }
233     *pos = STORE_GLOBAL_PTR(drive_g);
234 }
235
236 // Map a cd
237 void
238 map_cd_drive(struct drive_s *drive_g)
239 {
240     dprintf(3, "Mapping cd drive %p\n", drive_g);
241     add_ordered_drive(Drives.idmap[EXTTYPE_CD], &Drives.cdcount, drive_g);
242 }
243
244 // Map a floppy
245 void
246 map_floppy_drive(struct drive_s *drive_g)
247 {
248     // fill idmap
249     dprintf(3, "Mapping floppy drive %p\n", drive_g);
250     add_ordered_drive(Drives.idmap[EXTTYPE_FLOPPY], &Drives.floppycount
251                       , drive_g);
252
253     // Update equipment word bits for floppy
254     if (Drives.floppycount == 1) {
255         // 1 drive, ready for boot
256         SETBITS_BDA(equipment_list_flags, 0x01);
257         SET_BDA(floppy_harddisk_info, 0x07);
258     } else if (Drives.floppycount >= 2) {
259         // 2 drives, ready for boot
260         SETBITS_BDA(equipment_list_flags, 0x41);
261         SET_BDA(floppy_harddisk_info, 0x77);
262     }
263 }
264
265 // Show a one line description (without trailing newline) of a drive.
266 void
267 describe_drive(struct drive_s *drive_g)
268 {
269     ASSERT32FLAT();
270     u8 type = GET_GLOBAL(drive_g->type);
271     switch (type) {
272     case DTYPE_FLOPPY:
273         describe_floppy(drive_g);
274         break;
275     case DTYPE_ATA:
276         describe_ata(drive_g);
277         break;
278     case DTYPE_ATAPI:
279         describe_atapi(drive_g);
280         break;
281     case DTYPE_RAMDISK:
282         describe_ramdisk(drive_g);
283         break;
284     case DTYPE_USB:
285         describe_usb(drive_g);
286         break;
287     default:
288         printf("Unknown");
289         break;
290     }
291 }
292
293
294 /****************************************************************
295  * 16bit calling interface
296  ****************************************************************/
297
298 // Execute a disk_op request.
299 int
300 process_op(struct disk_op_s *op)
301 {
302     ASSERT16();
303     u8 type = GET_GLOBAL(op->drive_g->type);
304     switch (type) {
305     case DTYPE_FLOPPY:
306         return process_floppy_op(op);
307     case DTYPE_ATA:
308         return process_ata_op(op);
309     case DTYPE_ATAPI:
310         return process_atapi_op(op);
311     case DTYPE_RAMDISK:
312         return process_ramdisk_op(op);
313     case DTYPE_CDEMU:
314         return process_cdemu_op(op);
315     case DTYPE_USB:
316         return process_usb_op(op);
317     default:
318         op->count = 0;
319         return DISK_RET_EPARAM;
320     }
321 }
322
323 // Execute a "disk_op_s" request - this runs on a stack in the ebda.
324 static int
325 __send_disk_op(struct disk_op_s *op_far, u16 op_seg)
326 {
327     struct disk_op_s dop;
328     memcpy_far(GET_SEG(SS), &dop
329                , op_seg, op_far
330                , sizeof(dop));
331
332     dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
333             , dop.drive_g, (u32)dop.lba, dop.buf_fl
334             , dop.count, dop.command);
335
336     int status = process_op(&dop);
337
338     // Update count with total sectors transferred.
339     SET_FARVAR(op_seg, op_far->count, dop.count);
340
341     return status;
342 }
343
344 // Execute a "disk_op_s" request by jumping to a stack in the ebda.
345 int
346 send_disk_op(struct disk_op_s *op)
347 {
348     ASSERT16();
349     if (! CONFIG_DRIVES)
350         return -1;
351
352     return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
353 }
354
355
356 /****************************************************************
357  * Setup
358  ****************************************************************/
359
360 void
361 drive_setup(void)
362 {
363     memset(&Drives, 0, sizeof(Drives));
364 }