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