blob: 243428e7c7ccb36221482d56b122dfac1b189159 [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
Paolo Bonzini0e7fb5f2011-11-16 13:02:55 +010014#include "virtio-blk.h" // process_virtio_blk_op
Paolo Bonzini1e749c82011-11-16 13:02:53 +010015#include "blockcmd.h" // cdb_*
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;
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +020020u8 *bounce_buf_fl VAR16VISIBLE;
Kevin O'Connord3140832012-05-13 22:46:12 -040021struct dpte_s DefaultDPTE VARLOW;
Kevin O'Connorc892b132009-08-11 21:59:37 -040022
Kevin O'Connor77d227b2009-10-22 21:48:39 -040023struct drive_s *
24getDrive(u8 exttype, u8 extdriveoffset)
25{
Kevin O'Connora0842f82010-12-29 11:05:46 -050026 if (extdriveoffset >= ARRAY_SIZE(IDMap[0]))
Kevin O'Connor77d227b2009-10-22 21:48:39 -040027 return NULL;
Kevin O'Connora0842f82010-12-29 11:05:46 -050028 struct drive_s *drive_gf = GET_GLOBAL(IDMap[exttype][extdriveoffset]);
Kevin O'Connor8f469b92010-02-28 01:28:11 -050029 if (!drive_gf)
30 return NULL;
31 return GLOBALFLAT2GLOBAL(drive_gf);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040032}
33
Gleb Natapov4c90a202010-12-07 13:50:54 +020034int getDriveId(u8 exttype, struct drive_s *drive_g)
35{
36 int i;
Kevin O'Connora0842f82010-12-29 11:05:46 -050037 for (i = 0; i < ARRAY_SIZE(IDMap[0]); i++)
Gleb Natapov4c90a202010-12-07 13:50:54 +020038 if (getDrive(exttype, i) == drive_g)
39 return i;
Gleb Natapov4c90a202010-12-07 13:50:54 +020040 return -1;
41}
Kevin O'Connorc892b132009-08-11 21:59:37 -040042
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +020043int bounce_buf_init(void)
44{
45 if (bounce_buf_fl)
46 return 0;
47
48 u8 *buf = malloc_low(CDROM_SECTOR_SIZE);
49 if (!buf) {
50 warn_noalloc();
51 return -1;
52 }
53 bounce_buf_fl = buf;
54 return 0;
55}
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -050056
Kevin O'Connorc892b132009-08-11 21:59:37 -040057/****************************************************************
58 * Disk geometry translation
59 ****************************************************************/
60
61static u8
Kevin O'Connor77d227b2009-10-22 21:48:39 -040062get_translation(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -040063{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040064 u8 type = GET_GLOBAL(drive_g->type);
Kevin O'Connorc892b132009-08-11 21:59:37 -040065 if (! CONFIG_COREBOOT && type == DTYPE_ATA) {
66 // Emulators pass in the translation info via nvram.
Kevin O'Connor77d227b2009-10-22 21:48:39 -040067 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connorc892b132009-08-11 21:59:37 -040068 u8 channel = ataid / 2;
69 u8 translation = inb_cmos(CMOS_BIOS_DISKTRANSFLAG + channel/2);
70 translation >>= 2 * (ataid % 4);
71 translation &= 0x03;
72 return translation;
73 }
74
Kevin O'Connord2d1de02010-02-17 01:07:36 -050075 // Otherwise use a heuristic to determine translation type.
Kevin O'Connor77d227b2009-10-22 21:48:39 -040076 u16 heads = GET_GLOBAL(drive_g->pchs.heads);
77 u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders);
78 u16 spt = GET_GLOBAL(drive_g->pchs.spt);
Kevin O'Connord2d1de02010-02-17 01:07:36 -050079 u64 sectors = GET_GLOBAL(drive_g->sectors);
80 u64 psectors = (u64)heads * cylinders * spt;
81 if (!heads || !cylinders || !spt || psectors > sectors)
82 // pchs doesn't look valid - use LBA.
83 return TRANSLATION_LBA;
Kevin O'Connorc892b132009-08-11 21:59:37 -040084
85 if (cylinders <= 1024 && heads <= 16 && spt <= 63)
86 return TRANSLATION_NONE;
87 if (cylinders * heads <= 131072)
88 return TRANSLATION_LARGE;
89 return TRANSLATION_LBA;
90}
91
Kevin O'Connor697e63c2010-12-27 21:08:53 -050092static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -040093setup_translation(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -040094{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040095 u8 translation = get_translation(drive_g);
96 SET_GLOBAL(drive_g->translation, translation);
Kevin O'Connorc892b132009-08-11 21:59:37 -040097
Kevin O'Connor77d227b2009-10-22 21:48:39 -040098 u16 heads = GET_GLOBAL(drive_g->pchs.heads);
99 u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders);
100 u16 spt = GET_GLOBAL(drive_g->pchs.spt);
101 u64 sectors = GET_GLOBAL(drive_g->sectors);
Kevin O'Connor6f6a74d2009-11-09 19:15:50 -0500102 const char *desc = NULL;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400103
Kevin O'Connorc892b132009-08-11 21:59:37 -0400104 switch (translation) {
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400105 default:
Kevin O'Connorc892b132009-08-11 21:59:37 -0400106 case TRANSLATION_NONE:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400107 desc = "none";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400108 break;
109 case TRANSLATION_LBA:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400110 desc = "lba";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400111 spt = 63;
112 if (sectors > 63*255*1024) {
113 heads = 255;
114 cylinders = 1024;
115 break;
116 }
117 u32 sect = (u32)sectors / 63;
118 heads = sect / 1024;
119 if (heads>128)
120 heads = 255;
121 else if (heads>64)
122 heads = 128;
123 else if (heads>32)
124 heads = 64;
125 else if (heads>16)
126 heads = 32;
127 else
128 heads = 16;
129 cylinders = sect / heads;
130 break;
131 case TRANSLATION_RECHS:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400132 desc = "r-echs";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400133 // Take care not to overflow
134 if (heads==16) {
135 if (cylinders>61439)
136 cylinders=61439;
137 heads=15;
138 cylinders = (u16)((u32)(cylinders)*16/15);
139 }
140 // then go through the large bitshift process
141 case TRANSLATION_LARGE:
142 if (translation == TRANSLATION_LARGE)
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400143 desc = "large";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400144 while (cylinders > 1024) {
145 cylinders >>= 1;
146 heads <<= 1;
147
148 // If we max out the head count
149 if (heads > 127)
150 break;
151 }
152 break;
153 }
154 // clip to 1024 cylinders in lchs
155 if (cylinders > 1024)
156 cylinders = 1024;
Kevin O'Connord2d1de02010-02-17 01:07:36 -0500157 dprintf(1, "drive %p: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d s=%d\n"
158 , drive_g
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400159 , drive_g->pchs.cylinders, drive_g->pchs.heads, drive_g->pchs.spt
160 , desc
Kevin O'Connord2d1de02010-02-17 01:07:36 -0500161 , cylinders, heads, spt
162 , (u32)sectors);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400163
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400164 SET_GLOBAL(drive_g->lchs.heads, heads);
165 SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
166 SET_GLOBAL(drive_g->lchs.spt, spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400167}
168
169
170/****************************************************************
171 * Drive mapping
172 ****************************************************************/
173
174// Fill in Fixed Disk Parameter Table (located in ebda).
175static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400176fill_fdpt(struct drive_s *drive_g, int hdid)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400177{
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400178 if (hdid > 1)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400179 return;
180
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400181 u16 nlc = GET_GLOBAL(drive_g->lchs.cylinders);
182 u16 nlh = GET_GLOBAL(drive_g->lchs.heads);
183 u16 nlspt = GET_GLOBAL(drive_g->lchs.spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400184
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400185 u16 npc = GET_GLOBAL(drive_g->pchs.cylinders);
186 u16 nph = GET_GLOBAL(drive_g->pchs.heads);
187 u16 npspt = GET_GLOBAL(drive_g->pchs.spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400188
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400189 struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid];
Kevin O'Connorc892b132009-08-11 21:59:37 -0400190 fdpt->precompensation = 0xffff;
191 fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3);
192 fdpt->landing_zone = npc;
193 fdpt->cylinders = nlc;
194 fdpt->heads = nlh;
195 fdpt->sectors = nlspt;
196
Kevin O'Connor085debd2010-01-03 22:24:18 -0500197 if (nlc != npc || nlh != nph || nlspt != npspt) {
198 // Logical mapping present - use extended structure.
Kevin O'Connorc892b132009-08-11 21:59:37 -0400199
Kevin O'Connor085debd2010-01-03 22:24:18 -0500200 // complies with Phoenix style Translated Fixed Disk Parameter
201 // Table (FDPT)
202 fdpt->phys_cylinders = npc;
203 fdpt->phys_heads = nph;
204 fdpt->phys_sectors = npspt;
205 fdpt->a0h_signature = 0xa0;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400206
Kevin O'Connor085debd2010-01-03 22:24:18 -0500207 // Checksum structure.
208 fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
209 }
Kevin O'Connorc892b132009-08-11 21:59:37 -0400210
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400211 if (hdid == 0)
Kevin O'Connor9f985422009-09-09 11:34:39 -0400212 SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
213 struct extended_bios_data_area_s, fdpt[0])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400214 else
Kevin O'Connor9f985422009-09-09 11:34:39 -0400215 SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
216 struct extended_bios_data_area_s, fdpt[1])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400217}
218
Kevin O'Connord254dc22009-11-25 18:49:06 -0500219// Find spot to add a drive
220static void
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500221add_drive(struct drive_s **idmap, u8 *count, struct drive_s *drive_g)
Kevin O'Connord254dc22009-11-25 18:49:06 -0500222{
Kevin O'Connora0842f82010-12-29 11:05:46 -0500223 if (*count >= ARRAY_SIZE(IDMap[0])) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500224 warn_noalloc();
Kevin O'Connord254dc22009-11-25 18:49:06 -0500225 return;
226 }
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500227 idmap[*count] = drive_g;
Kevin O'Connord254dc22009-11-25 18:49:06 -0500228 *count = *count + 1;
Kevin O'Connord254dc22009-11-25 18:49:06 -0500229}
230
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500231// Map a hard drive
232void
233map_hd_drive(struct drive_s *drive_g)
234{
235 ASSERT32FLAT();
236 struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0);
237 int hdid = bda->hdcount;
238 dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdid);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500239 add_drive(IDMap[EXTTYPE_HD], &bda->hdcount, drive_g);
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500240
Kevin O'Connor697e63c2010-12-27 21:08:53 -0500241 // Setup disk geometry translation.
242 setup_translation(drive_g);
243
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500244 // Fill "fdpt" structure.
245 fill_fdpt(drive_g, hdid);
246}
247
Kevin O'Connorc892b132009-08-11 21:59:37 -0400248// Map a cd
249void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400250map_cd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400251{
Kevin O'Connord254dc22009-11-25 18:49:06 -0500252 dprintf(3, "Mapping cd drive %p\n", drive_g);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500253 add_drive(IDMap[EXTTYPE_CD], &CDCount, drive_g);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400254}
255
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400256// Map a floppy
257void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400258map_floppy_drive(struct drive_s *drive_g)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400259{
Kevin O'Connord254dc22009-11-25 18:49:06 -0500260 dprintf(3, "Mapping floppy drive %p\n", drive_g);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500261 add_drive(IDMap[EXTTYPE_FLOPPY], &FloppyCount, drive_g);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400262
263 // Update equipment word bits for floppy
Kevin O'Connora0842f82010-12-29 11:05:46 -0500264 if (FloppyCount == 1) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400265 // 1 drive, ready for boot
Kevin O'Connore51316d2012-06-10 09:09:22 -0400266 set_equipment_flags(0x41, 0x01);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400267 SET_BDA(floppy_harddisk_info, 0x07);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500268 } else if (FloppyCount >= 2) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400269 // 2 drives, ready for boot
Kevin O'Connore51316d2012-06-10 09:09:22 -0400270 set_equipment_flags(0x41, 0x41);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400271 SET_BDA(floppy_harddisk_info, 0x77);
272 }
273}
274
Kevin O'Connorc892b132009-08-11 21:59:37 -0400275
276/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400277 * 16bit calling interface
278 ****************************************************************/
279
Laszlo Ersek26d31442012-02-09 16:55:30 +0100280static int
Paolo Bonzini1e749c82011-11-16 13:02:53 +0100281process_scsi_op(struct disk_op_s *op)
282{
Paolo Bonzini1e749c82011-11-16 13:02:53 +0100283 switch (op->command) {
284 case CMD_READ:
285 return cdb_read(op);
286 case CMD_WRITE:
287 return cdb_write(op);
288 case CMD_FORMAT:
289 case CMD_RESET:
290 case CMD_ISREADY:
291 case CMD_VERIFY:
292 case CMD_SEEK:
293 return DISK_RET_SUCCESS;
294 default:
295 op->count = 0;
296 return DISK_RET_EPARAM;
297 }
298}
299
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400300static int
301process_atapi_op(struct disk_op_s *op)
302{
303 switch (op->command) {
304 case CMD_WRITE:
305 case CMD_FORMAT:
306 return DISK_RET_EWRITEPROTECT;
307 default:
308 return process_scsi_op(op);
309 }
310}
311
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400312// Execute a disk_op request.
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400313int
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400314process_op(struct disk_op_s *op)
315{
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500316 ASSERT16();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400317 u8 type = GET_GLOBAL(op->drive_g->type);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400318 switch (type) {
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400319 case DTYPE_FLOPPY:
320 return process_floppy_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400321 case DTYPE_ATA:
322 return process_ata_op(op);
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400323 case DTYPE_RAMDISK:
324 return process_ramdisk_op(op);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400325 case DTYPE_CDEMU:
326 return process_cdemu_op(op);
Paolo Bonzini0e7fb5f2011-11-16 13:02:55 +0100327 case DTYPE_VIRTIO_BLK:
328 return process_virtio_blk_op(op);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100329 case DTYPE_AHCI:
Kevin O'Connor7a08ae72012-03-14 21:11:39 -0400330 return process_ahci_op(op);
Kevin O'Connorbd6afe52012-07-21 12:01:12 -0400331 case DTYPE_ATA_ATAPI:
332 case DTYPE_AHCI_ATAPI:
333 return process_atapi_op(op);
Paolo Bonzini1e749c82011-11-16 13:02:53 +0100334 case DTYPE_USB:
Gerd Hoffmanne53e30d2012-07-20 10:59:24 +0200335 case DTYPE_UAS:
Paolo Bonzinic5c488f2012-02-27 17:22:23 +0100336 case DTYPE_VIRTIO_SCSI:
Gerd Hoffmann9d6bac12012-07-20 10:59:25 +0200337 case DTYPE_LSI_SCSI:
Paolo Bonzini7a39e722012-08-06 13:15:06 +0200338 case DTYPE_ESP_SCSI:
Paolo Bonzini1e749c82011-11-16 13:02:53 +0100339 return process_scsi_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400340 default:
341 op->count = 0;
342 return DISK_RET_EPARAM;
343 }
344}
345
Kevin O'Connor46b82622012-05-13 12:10:30 -0400346// Execute a "disk_op_s" request - this runs on the extra stack.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400347static int
348__send_disk_op(struct disk_op_s *op_far, u16 op_seg)
349{
350 struct disk_op_s dop;
351 memcpy_far(GET_SEG(SS), &dop
352 , op_seg, op_far
353 , sizeof(dop));
354
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400355 dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
356 , dop.drive_g, (u32)dop.lba, dop.buf_fl
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400357 , dop.count, dop.command);
358
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400359 int status = process_op(&dop);
360
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400361 // Update count with total sectors transferred.
362 SET_FARVAR(op_seg, op_far->count, dop.count);
363
364 return status;
365}
366
Kevin O'Connor46b82622012-05-13 12:10:30 -0400367// Execute a "disk_op_s" request by jumping to the extra 16bit stack.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400368int
369send_disk_op(struct disk_op_s *op)
370{
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500371 ASSERT16();
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400372 if (! CONFIG_DRIVES)
373 return -1;
374
Kevin O'Connorbca07362010-03-20 20:41:38 -0400375 return stack_hop((u32)op, GET_SEG(SS), __send_disk_op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400376}