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