blob: 619db67341ef4b9c53d3d1f7e320f8f0eab6e84b [file] [log] [blame]
Kevin O'Connorc892b132009-08-11 21:59:37 -04001// 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
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -040012#include "ata.h" // process_ata_op
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +010013#include "ahci.h" // process_ahci_op
Kevin O'Connor7149fc82010-02-17 23:24:42 -050014#include "usb-msc.h" // process_usb_op
Gleb Natapov89acfa32010-05-10 11:36:37 +030015#include "virtio-blk.h" // process_virtio_op
Kevin O'Connorc892b132009-08-11 21:59:37 -040016
Kevin O'Connora0842f82010-12-29 11:05:46 -050017u8 FloppyCount VAR16VISIBLE;
18u8 CDCount;
19struct drive_s *IDMap[3][CONFIG_MAX_EXTDRIVE] VAR16VISIBLE;
Kevin O'Connorc892b132009-08-11 21:59:37 -040020
Kevin O'Connor77d227b2009-10-22 21:48:39 -040021struct drive_s *
22getDrive(u8 exttype, u8 extdriveoffset)
23{
Kevin O'Connora0842f82010-12-29 11:05:46 -050024 if (extdriveoffset >= ARRAY_SIZE(IDMap[0]))
Kevin O'Connor77d227b2009-10-22 21:48:39 -040025 return NULL;
Kevin O'Connora0842f82010-12-29 11:05:46 -050026 struct drive_s *drive_gf = GET_GLOBAL(IDMap[exttype][extdriveoffset]);
Kevin O'Connor8f469b92010-02-28 01:28:11 -050027 if (!drive_gf)
28 return NULL;
29 return GLOBALFLAT2GLOBAL(drive_gf);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040030}
31
Gleb Natapov4c90a202010-12-07 13:50:54 +020032int getDriveId(u8 exttype, struct drive_s *drive_g)
33{
34 int i;
Kevin O'Connora0842f82010-12-29 11:05:46 -050035 for (i = 0; i < ARRAY_SIZE(IDMap[0]); i++)
Gleb Natapov4c90a202010-12-07 13:50:54 +020036 if (getDrive(exttype, i) == drive_g)
37 return i;
Gleb Natapov4c90a202010-12-07 13:50:54 +020038 return -1;
39}
Kevin O'Connorc892b132009-08-11 21:59:37 -040040
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -050041
Kevin O'Connorc892b132009-08-11 21:59:37 -040042/****************************************************************
43 * Disk geometry translation
44 ****************************************************************/
45
46static u8
Kevin O'Connor77d227b2009-10-22 21:48:39 -040047get_translation(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -040048{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040049 u8 type = GET_GLOBAL(drive_g->type);
Kevin O'Connorc892b132009-08-11 21:59:37 -040050 if (! CONFIG_COREBOOT && type == DTYPE_ATA) {
51 // Emulators pass in the translation info via nvram.
Kevin O'Connor77d227b2009-10-22 21:48:39 -040052 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connorc892b132009-08-11 21:59:37 -040053 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
Kevin O'Connord2d1de02010-02-17 01:07:36 -050060 // Otherwise use a heuristic to determine translation type.
Kevin O'Connor77d227b2009-10-22 21:48:39 -040061 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);
Kevin O'Connord2d1de02010-02-17 01:07:36 -050064 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;
Kevin O'Connorc892b132009-08-11 21:59:37 -040069
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
Kevin O'Connor697e63c2010-12-27 21:08:53 -050077static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -040078setup_translation(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -040079{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040080 u8 translation = get_translation(drive_g);
81 SET_GLOBAL(drive_g->translation, translation);
Kevin O'Connorc892b132009-08-11 21:59:37 -040082
Kevin O'Connor77d227b2009-10-22 21:48:39 -040083 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);
Kevin O'Connor6f6a74d2009-11-09 19:15:50 -050087 const char *desc = NULL;
Kevin O'Connorc892b132009-08-11 21:59:37 -040088
Kevin O'Connorc892b132009-08-11 21:59:37 -040089 switch (translation) {
Kevin O'Connorc604f2f2009-10-24 19:56:11 -040090 default:
Kevin O'Connorc892b132009-08-11 21:59:37 -040091 case TRANSLATION_NONE:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -040092 desc = "none";
Kevin O'Connorc892b132009-08-11 21:59:37 -040093 break;
94 case TRANSLATION_LBA:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -040095 desc = "lba";
Kevin O'Connorc892b132009-08-11 21:59:37 -040096 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:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400117 desc = "r-echs";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400118 // 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)
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400128 desc = "large";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400129 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;
Kevin O'Connord2d1de02010-02-17 01:07:36 -0500142 dprintf(1, "drive %p: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d s=%d\n"
143 , drive_g
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400144 , drive_g->pchs.cylinders, drive_g->pchs.heads, drive_g->pchs.spt
145 , desc
Kevin O'Connord2d1de02010-02-17 01:07:36 -0500146 , cylinders, heads, spt
147 , (u32)sectors);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400148
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400149 SET_GLOBAL(drive_g->lchs.heads, heads);
150 SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
151 SET_GLOBAL(drive_g->lchs.spt, spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400152}
153
154
155/****************************************************************
156 * Drive mapping
157 ****************************************************************/
158
159// Fill in Fixed Disk Parameter Table (located in ebda).
160static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400161fill_fdpt(struct drive_s *drive_g, int hdid)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400162{
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400163 if (hdid > 1)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400164 return;
165
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400166 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);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400169
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400170 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);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400173
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400174 struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid];
Kevin O'Connorc892b132009-08-11 21:59:37 -0400175 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
Kevin O'Connor085debd2010-01-03 22:24:18 -0500182 if (nlc != npc || nlh != nph || nlspt != npspt) {
183 // Logical mapping present - use extended structure.
Kevin O'Connorc892b132009-08-11 21:59:37 -0400184
Kevin O'Connor085debd2010-01-03 22:24:18 -0500185 // 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;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400191
Kevin O'Connor085debd2010-01-03 22:24:18 -0500192 // Checksum structure.
193 fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
194 }
Kevin O'Connorc892b132009-08-11 21:59:37 -0400195
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400196 if (hdid == 0)
Kevin O'Connor9f985422009-09-09 11:34:39 -0400197 SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
198 struct extended_bios_data_area_s, fdpt[0])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400199 else
Kevin O'Connor9f985422009-09-09 11:34:39 -0400200 SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
201 struct extended_bios_data_area_s, fdpt[1])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400202}
203
Kevin O'Connord254dc22009-11-25 18:49:06 -0500204// Find spot to add a drive
205static void
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500206add_drive(struct drive_s **idmap, u8 *count, struct drive_s *drive_g)
Kevin O'Connord254dc22009-11-25 18:49:06 -0500207{
Kevin O'Connora0842f82010-12-29 11:05:46 -0500208 if (*count >= ARRAY_SIZE(IDMap[0])) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500209 warn_noalloc();
Kevin O'Connord254dc22009-11-25 18:49:06 -0500210 return;
211 }
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500212 idmap[*count] = drive_g;
Kevin O'Connord254dc22009-11-25 18:49:06 -0500213 *count = *count + 1;
Kevin O'Connord254dc22009-11-25 18:49:06 -0500214}
215
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500216// Map a hard drive
217void
218map_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);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500224 add_drive(IDMap[EXTTYPE_HD], &bda->hdcount, drive_g);
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500225
Kevin O'Connor697e63c2010-12-27 21:08:53 -0500226 // Setup disk geometry translation.
227 setup_translation(drive_g);
228
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500229 // Fill "fdpt" structure.
230 fill_fdpt(drive_g, hdid);
231}
232
Kevin O'Connorc892b132009-08-11 21:59:37 -0400233// Map a cd
234void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400235map_cd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400236{
Kevin O'Connord254dc22009-11-25 18:49:06 -0500237 dprintf(3, "Mapping cd drive %p\n", drive_g);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500238 add_drive(IDMap[EXTTYPE_CD], &CDCount, drive_g);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400239}
240
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400241// Map a floppy
242void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400243map_floppy_drive(struct drive_s *drive_g)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400244{
Kevin O'Connord254dc22009-11-25 18:49:06 -0500245 dprintf(3, "Mapping floppy drive %p\n", drive_g);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500246 add_drive(IDMap[EXTTYPE_FLOPPY], &FloppyCount, drive_g);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400247
248 // Update equipment word bits for floppy
Kevin O'Connora0842f82010-12-29 11:05:46 -0500249 if (FloppyCount == 1) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400250 // 1 drive, ready for boot
251 SETBITS_BDA(equipment_list_flags, 0x01);
252 SET_BDA(floppy_harddisk_info, 0x07);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500253 } else if (FloppyCount >= 2) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400254 // 2 drives, ready for boot
255 SETBITS_BDA(equipment_list_flags, 0x41);
256 SET_BDA(floppy_harddisk_info, 0x77);
257 }
258}
259
Kevin O'Connorc892b132009-08-11 21:59:37 -0400260
261/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400262 * 16bit calling interface
263 ****************************************************************/
264
265// Execute a disk_op request.
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400266int
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400267process_op(struct disk_op_s *op)
268{
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500269 ASSERT16();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400270 u8 type = GET_GLOBAL(op->drive_g->type);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400271 switch (type) {
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400272 case DTYPE_FLOPPY:
273 return process_floppy_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400274 case DTYPE_ATA:
275 return process_ata_op(op);
276 case DTYPE_ATAPI:
277 return process_atapi_op(op);
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400278 case DTYPE_RAMDISK:
279 return process_ramdisk_op(op);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400280 case DTYPE_CDEMU:
281 return process_cdemu_op(op);
Kevin O'Connor7149fc82010-02-17 23:24:42 -0500282 case DTYPE_USB:
283 return process_usb_op(op);
Gleb Natapov89acfa32010-05-10 11:36:37 +0300284 case DTYPE_VIRTIO:
285 return process_virtio_op(op);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100286 case DTYPE_AHCI:
287 return process_ahci_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400288 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.
295static 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
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400303 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
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400305 , dop.count, dop.command);
306
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400307 int status = process_op(&dop);
308
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400309 // 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.
316int
317send_disk_op(struct disk_op_s *op)
318{
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500319 ASSERT16();
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400320 if (! CONFIG_DRIVES)
321 return -1;
322
Kevin O'Connorbca07362010-03-20 20:41:38 -0400323 return stack_hop((u32)op, GET_SEG(SS), __send_disk_op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400324}