Pass 'drive_s' pointer instead of driveid.
[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
90     dprintf(1, "ata%d-%d: PCHS=%u/%d/%d translation="
91             , channel, slave, cylinders, heads, spt);
92     switch (translation) {
93     case TRANSLATION_NONE:
94         dprintf(1, "none");
95         break;
96     case TRANSLATION_LBA:
97         dprintf(1, "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         dprintf(1, "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             dprintf(1, "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, " LCHS=%d/%d/%d\n", cylinders, heads, spt);
145
146     SET_GLOBAL(drive_g->lchs.heads, heads);
147     SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
148     SET_GLOBAL(drive_g->lchs.spt, spt);
149 }
150
151
152 /****************************************************************
153  * Drive mapping
154  ****************************************************************/
155
156 // Fill in Fixed Disk Parameter Table (located in ebda).
157 static void
158 fill_fdpt(struct drive_s *drive_g, int hdid)
159 {
160     if (hdid > 1)
161         return;
162
163     u16 nlc   = GET_GLOBAL(drive_g->lchs.cylinders);
164     u16 nlh   = GET_GLOBAL(drive_g->lchs.heads);
165     u16 nlspt = GET_GLOBAL(drive_g->lchs.spt);
166
167     u16 npc   = GET_GLOBAL(drive_g->pchs.cylinders);
168     u16 nph   = GET_GLOBAL(drive_g->pchs.heads);
169     u16 npspt = GET_GLOBAL(drive_g->pchs.spt);
170
171     struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid];
172     fdpt->precompensation = 0xffff;
173     fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3);
174     fdpt->landing_zone = npc;
175     fdpt->cylinders = nlc;
176     fdpt->heads = nlh;
177     fdpt->sectors = nlspt;
178
179     if (nlc == npc && nlh == nph && nlspt == npspt)
180         // no logical CHS mapping used, just physical CHS
181         // use Standard Fixed Disk Parameter Table (FDPT)
182         return;
183
184     // complies with Phoenix style Translated Fixed Disk Parameter
185     // Table (FDPT)
186     fdpt->phys_cylinders = npc;
187     fdpt->phys_heads = nph;
188     fdpt->phys_sectors = npspt;
189     fdpt->a0h_signature = 0xa0;
190
191     // Checksum structure.
192     fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
193
194     if (hdid == 0)
195         SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
196                                  struct extended_bios_data_area_s, fdpt[0])));
197     else
198         SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
199                                  struct extended_bios_data_area_s, fdpt[1])));
200 }
201
202 // Map a drive (that was registered via add_bcv_hd)
203 void
204 map_hd_drive(struct drive_s *drive_g)
205 {
206     // fill hdidmap
207     u8 hdcount = GET_BDA(hdcount);
208     if (hdcount >= ARRAY_SIZE(Drives.idmap[0]))
209         return;
210     dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdcount);
211     int driveid = drive_g - Drives.drives;
212     SET_GLOBAL(Drives.idmap[EXTTYPE_HD][hdcount], driveid);
213     SET_BDA(hdcount, hdcount + 1);
214
215     // Fill "fdpt" structure.
216     fill_fdpt(drive_g, hdcount);
217 }
218
219 // Map a cd
220 void
221 map_cd_drive(struct drive_s *drive_g)
222 {
223     // fill cdidmap
224     u8 cdcount = GET_GLOBAL(Drives.cdcount);
225     if (cdcount >= ARRAY_SIZE(Drives.idmap[0]))
226         return;
227     dprintf(3, "Mapping cd drive %p to %d\n", drive_g, cdcount);
228     int driveid = drive_g - Drives.drives;
229     SET_GLOBAL(Drives.idmap[EXTTYPE_CD][cdcount], driveid);
230     SET_GLOBAL(Drives.cdcount, cdcount+1);
231 }
232
233 // Map a floppy
234 void
235 map_floppy_drive(struct drive_s *drive_g)
236 {
237     // fill idmap
238     u8 floppycount = GET_GLOBAL(Drives.floppycount);
239     if (floppycount >= ARRAY_SIZE(Drives.idmap[0]))
240         return;
241     dprintf(3, "Mapping floppy drive %p to %d\n", drive_g, floppycount);
242     int driveid = drive_g - Drives.drives;
243     SET_GLOBAL(Drives.idmap[EXTTYPE_FLOPPY][floppycount], driveid);
244     floppycount++;
245     SET_GLOBAL(Drives.floppycount, floppycount);
246
247     // Update equipment word bits for floppy
248     if (floppycount == 1) {
249         // 1 drive, ready for boot
250         SETBITS_BDA(equipment_list_flags, 0x01);
251         SET_BDA(floppy_harddisk_info, 0x07);
252     } else {
253         // 2 drives, ready for boot
254         SETBITS_BDA(equipment_list_flags, 0x41);
255         SET_BDA(floppy_harddisk_info, 0x77);
256     }
257 }
258
259 // Show a one line description (without trailing newline) of a drive.
260 void
261 describe_drive(struct drive_s *drive_g)
262 {
263     ASSERT32();
264     u8 type = GET_GLOBAL(drive_g->type);
265     switch (type) {
266     case DTYPE_FLOPPY:
267         describe_floppy(drive_g);
268         break;
269     case DTYPE_ATA:
270         describe_ata(drive_g);
271         break;
272     case DTYPE_ATAPI:
273         describe_atapi(drive_g);
274         break;
275     case DTYPE_RAMDISK:
276         describe_ramdisk(drive_g);
277         break;
278     default:
279         printf("Unknown");
280         break;
281     }
282 }
283
284
285 /****************************************************************
286  * 16bit calling interface
287  ****************************************************************/
288
289 // Execute a disk_op request.
290 int
291 process_op(struct disk_op_s *op)
292 {
293     u8 type = GET_GLOBAL(op->drive_g->type);
294     switch (type) {
295     case DTYPE_FLOPPY:
296         return process_floppy_op(op);
297     case DTYPE_ATA:
298         return process_ata_op(op);
299     case DTYPE_ATAPI:
300         return process_atapi_op(op);
301     case DTYPE_RAMDISK:
302         return process_ramdisk_op(op);
303     case DTYPE_CDEMU:
304         return process_cdemu_op(op);
305     default:
306         op->count = 0;
307         return DISK_RET_EPARAM;
308     }
309 }
310
311 // Execute a "disk_op_s" request - this runs on a stack in the ebda.
312 static int
313 __send_disk_op(struct disk_op_s *op_far, u16 op_seg)
314 {
315     struct disk_op_s dop;
316     memcpy_far(GET_SEG(SS), &dop
317                , op_seg, op_far
318                , sizeof(dop));
319
320     dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
321             , dop.drive_g, (u32)dop.lba, dop.buf_fl
322             , dop.count, dop.command);
323
324     irq_enable();
325
326     int status = process_op(&dop);
327
328     irq_disable();
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 }