Fix compiler warning in setup_translation().
[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 // Map a cd
224 void
225 map_cd_drive(struct drive_s *drive_g)
226 {
227     // fill cdidmap
228     u8 cdcount = GET_GLOBAL(Drives.cdcount);
229     if (cdcount >= ARRAY_SIZE(Drives.idmap[0]))
230         return;
231     dprintf(3, "Mapping cd drive %p to %d\n", drive_g, cdcount);
232     int driveid = drive_g - Drives.drives;
233     SET_GLOBAL(Drives.idmap[EXTTYPE_CD][cdcount], driveid);
234     SET_GLOBAL(Drives.cdcount, cdcount+1);
235 }
236
237 // Map a floppy
238 void
239 map_floppy_drive(struct drive_s *drive_g)
240 {
241     // fill idmap
242     u8 floppycount = GET_GLOBAL(Drives.floppycount);
243     if (floppycount >= ARRAY_SIZE(Drives.idmap[0]))
244         return;
245     dprintf(3, "Mapping floppy drive %p to %d\n", drive_g, floppycount);
246     int driveid = drive_g - Drives.drives;
247     SET_GLOBAL(Drives.idmap[EXTTYPE_FLOPPY][floppycount], driveid);
248     floppycount++;
249     SET_GLOBAL(Drives.floppycount, floppycount);
250
251     // Update equipment word bits for floppy
252     if (floppycount == 1) {
253         // 1 drive, ready for boot
254         SETBITS_BDA(equipment_list_flags, 0x01);
255         SET_BDA(floppy_harddisk_info, 0x07);
256     } else {
257         // 2 drives, ready for boot
258         SETBITS_BDA(equipment_list_flags, 0x41);
259         SET_BDA(floppy_harddisk_info, 0x77);
260     }
261 }
262
263 // Show a one line description (without trailing newline) of a drive.
264 void
265 describe_drive(struct drive_s *drive_g)
266 {
267     ASSERT32();
268     u8 type = GET_GLOBAL(drive_g->type);
269     switch (type) {
270     case DTYPE_FLOPPY:
271         describe_floppy(drive_g);
272         break;
273     case DTYPE_ATA:
274         describe_ata(drive_g);
275         break;
276     case DTYPE_ATAPI:
277         describe_atapi(drive_g);
278         break;
279     case DTYPE_RAMDISK:
280         describe_ramdisk(drive_g);
281         break;
282     default:
283         printf("Unknown");
284         break;
285     }
286 }
287
288
289 /****************************************************************
290  * 16bit calling interface
291  ****************************************************************/
292
293 // Execute a disk_op request.
294 int
295 process_op(struct disk_op_s *op)
296 {
297     u8 type = GET_GLOBAL(op->drive_g->type);
298     switch (type) {
299     case DTYPE_FLOPPY:
300         return process_floppy_op(op);
301     case DTYPE_ATA:
302         return process_ata_op(op);
303     case DTYPE_ATAPI:
304         return process_atapi_op(op);
305     case DTYPE_RAMDISK:
306         return process_ramdisk_op(op);
307     case DTYPE_CDEMU:
308         return process_cdemu_op(op);
309     default:
310         op->count = 0;
311         return DISK_RET_EPARAM;
312     }
313 }
314
315 // Execute a "disk_op_s" request - this runs on a stack in the ebda.
316 static int
317 __send_disk_op(struct disk_op_s *op_far, u16 op_seg)
318 {
319     struct disk_op_s dop;
320     memcpy_far(GET_SEG(SS), &dop
321                , op_seg, op_far
322                , sizeof(dop));
323
324     dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
325             , dop.drive_g, (u32)dop.lba, dop.buf_fl
326             , dop.count, dop.command);
327
328     int status = process_op(&dop);
329
330     // Update count with total sectors transferred.
331     SET_FARVAR(op_seg, op_far->count, dop.count);
332
333     return status;
334 }
335
336 // Execute a "disk_op_s" request by jumping to a stack in the ebda.
337 int
338 send_disk_op(struct disk_op_s *op)
339 {
340     if (! CONFIG_DRIVES)
341         return -1;
342     ASSERT16();
343
344     return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
345 }
346
347
348 /****************************************************************
349  * Setup
350  ****************************************************************/
351
352 void
353 drive_setup()
354 {
355     memset(&Drives, 0, sizeof(Drives));
356     memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
357 }