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