3fb5e91da9ecb446d6c394f0920339b0d67e0da6
[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()
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         // no logical CHS mapping used, just physical CHS
185         // use Standard Fixed Disk Parameter Table (FDPT)
186         return;
187
188     // complies with Phoenix style Translated Fixed Disk Parameter
189     // Table (FDPT)
190     fdpt->phys_cylinders = npc;
191     fdpt->phys_heads = nph;
192     fdpt->phys_sectors = npspt;
193     fdpt->a0h_signature = 0xa0;
194
195     // Checksum structure.
196     fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
197
198     if (hdid == 0)
199         SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
200                                  struct extended_bios_data_area_s, fdpt[0])));
201     else
202         SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
203                                  struct extended_bios_data_area_s, fdpt[1])));
204 }
205
206 // Map a drive (that was registered via add_bcv_hd)
207 void
208 map_hd_drive(struct drive_s *drive_g)
209 {
210     // fill hdidmap
211     u8 hdcount = GET_BDA(hdcount);
212     if (hdcount >= ARRAY_SIZE(Drives.idmap[0]))
213         return;
214     dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdcount);
215     int driveid = drive_g - Drives.drives;
216     SET_GLOBAL(Drives.idmap[EXTTYPE_HD][hdcount], driveid);
217     SET_BDA(hdcount, hdcount + 1);
218
219     // Fill "fdpt" structure.
220     fill_fdpt(drive_g, hdcount);
221 }
222
223 // Find spot to add a drive
224 static void
225 add_ordered_drive(u8 *idmap, u8 *count, struct drive_s *drive_g)
226 {
227     if (*count >= ARRAY_SIZE(Drives.idmap[0])) {
228         dprintf(1, "No room to map drive %p\n", drive_g);
229         return;
230     }
231     u8 *pos = &idmap[*count];
232     *count = *count + 1;
233     if (CONFIG_THREADS) {
234         // Add to idmap with assured drive order.
235         u8 *end = pos;
236         for (;;) {
237             u8 *prev = pos - 1;
238             if (prev < idmap)
239                 break;
240             struct drive_s *prevdrive = &Drives.drives[*prev];
241             if (prevdrive->type < drive_g->type
242                 || (prevdrive->type == drive_g->type
243                     && prevdrive->cntl_id < drive_g->cntl_id))
244                 break;
245             pos--;
246         }
247         if (pos != end)
248             memmove(pos+1, pos, (void*)end-(void*)pos);
249     }
250     *pos = drive_g - Drives.drives;
251 }
252
253 // Map a cd
254 void
255 map_cd_drive(struct drive_s *drive_g)
256 {
257     dprintf(3, "Mapping cd drive %p\n", drive_g);
258     add_ordered_drive(Drives.idmap[EXTTYPE_CD], &Drives.cdcount, drive_g);
259 }
260
261 // Map a floppy
262 void
263 map_floppy_drive(struct drive_s *drive_g)
264 {
265     // fill idmap
266     dprintf(3, "Mapping floppy drive %p\n", drive_g);
267     add_ordered_drive(Drives.idmap[EXTTYPE_FLOPPY], &Drives.floppycount
268                       , drive_g);
269
270     // Update equipment word bits for floppy
271     if (Drives.floppycount == 1) {
272         // 1 drive, ready for boot
273         SETBITS_BDA(equipment_list_flags, 0x01);
274         SET_BDA(floppy_harddisk_info, 0x07);
275     } else if (Drives.floppycount >= 2) {
276         // 2 drives, ready for boot
277         SETBITS_BDA(equipment_list_flags, 0x41);
278         SET_BDA(floppy_harddisk_info, 0x77);
279     }
280 }
281
282 // Show a one line description (without trailing newline) of a drive.
283 void
284 describe_drive(struct drive_s *drive_g)
285 {
286     ASSERT32FLAT();
287     u8 type = GET_GLOBAL(drive_g->type);
288     switch (type) {
289     case DTYPE_FLOPPY:
290         describe_floppy(drive_g);
291         break;
292     case DTYPE_ATA:
293         describe_ata(drive_g);
294         break;
295     case DTYPE_ATAPI:
296         describe_atapi(drive_g);
297         break;
298     case DTYPE_RAMDISK:
299         describe_ramdisk(drive_g);
300         break;
301     default:
302         printf("Unknown");
303         break;
304     }
305 }
306
307
308 /****************************************************************
309  * 16bit calling interface
310  ****************************************************************/
311
312 // Execute a disk_op request.
313 int
314 process_op(struct disk_op_s *op)
315 {
316     u8 type = GET_GLOBAL(op->drive_g->type);
317     switch (type) {
318     case DTYPE_FLOPPY:
319         return process_floppy_op(op);
320     case DTYPE_ATA:
321         return process_ata_op(op);
322     case DTYPE_ATAPI:
323         return process_atapi_op(op);
324     case DTYPE_RAMDISK:
325         return process_ramdisk_op(op);
326     case DTYPE_CDEMU:
327         return process_cdemu_op(op);
328     default:
329         op->count = 0;
330         return DISK_RET_EPARAM;
331     }
332 }
333
334 // Execute a "disk_op_s" request - this runs on a stack in the ebda.
335 static int
336 __send_disk_op(struct disk_op_s *op_far, u16 op_seg)
337 {
338     struct disk_op_s dop;
339     memcpy_far(GET_SEG(SS), &dop
340                , op_seg, op_far
341                , sizeof(dop));
342
343     dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
344             , dop.drive_g, (u32)dop.lba, dop.buf_fl
345             , dop.count, dop.command);
346
347     int status = process_op(&dop);
348
349     // Update count with total sectors transferred.
350     SET_FARVAR(op_seg, op_far->count, dop.count);
351
352     return status;
353 }
354
355 // Execute a "disk_op_s" request by jumping to a stack in the ebda.
356 int
357 send_disk_op(struct disk_op_s *op)
358 {
359     if (! CONFIG_DRIVES)
360         return -1;
361     ASSERT16();
362
363     return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
364 }
365
366
367 /****************************************************************
368  * Setup
369  ****************************************************************/
370
371 void
372 drive_setup()
373 {
374     memset(&Drives, 0, sizeof(Drives));
375     memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
376 }