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