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