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