blob: ccf4ee6d39e0a1e136ed4148acf5686783a940a6 [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'Connorc892b132009-08-11 21:59:37 -040021
Kevin O'Connor77d227b2009-10-22 21:48:39 -040022struct drive_s *
23getDrive(u8 exttype, u8 extdriveoffset)
24{
Kevin O'Connora0842f82010-12-29 11:05:46 -050025 if (extdriveoffset >= ARRAY_SIZE(IDMap[0]))
Kevin O'Connor77d227b2009-10-22 21:48:39 -040026 return NULL;
Kevin O'Connora0842f82010-12-29 11:05:46 -050027 struct drive_s *drive_gf = GET_GLOBAL(IDMap[exttype][extdriveoffset]);
Kevin O'Connor8f469b92010-02-28 01:28:11 -050028 if (!drive_gf)
29 return NULL;
30 return GLOBALFLAT2GLOBAL(drive_gf);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040031}
32
Gleb Natapov4c90a202010-12-07 13:50:54 +020033int getDriveId(u8 exttype, struct drive_s *drive_g)
34{
35 int i;
Kevin O'Connora0842f82010-12-29 11:05:46 -050036 for (i = 0; i < ARRAY_SIZE(IDMap[0]); i++)
Gleb Natapov4c90a202010-12-07 13:50:54 +020037 if (getDrive(exttype, i) == drive_g)
38 return i;
Gleb Natapov4c90a202010-12-07 13:50:54 +020039 return -1;
40}
Kevin O'Connorc892b132009-08-11 21:59:37 -040041
Gerd Hoffmannd7a7cf32011-08-04 19:36:27 +020042int 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}
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -050055
Kevin O'Connorc892b132009-08-11 21:59:37 -040056/****************************************************************
57 * Disk geometry translation
58 ****************************************************************/
59
60static u8
Kevin O'Connor77d227b2009-10-22 21:48:39 -040061get_translation(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -040062{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040063 u8 type = GET_GLOBAL(drive_g->type);
Kevin O'Connorc892b132009-08-11 21:59:37 -040064 if (! CONFIG_COREBOOT && type == DTYPE_ATA) {
65 // Emulators pass in the translation info via nvram.
Kevin O'Connor77d227b2009-10-22 21:48:39 -040066 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connorc892b132009-08-11 21:59:37 -040067 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
Kevin O'Connord2d1de02010-02-17 01:07:36 -050074 // Otherwise use a heuristic to determine translation type.
Kevin O'Connor77d227b2009-10-22 21:48:39 -040075 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);
Kevin O'Connord2d1de02010-02-17 01:07:36 -050078 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;
Kevin O'Connorc892b132009-08-11 21:59:37 -040083
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
Kevin O'Connor697e63c2010-12-27 21:08:53 -050091static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -040092setup_translation(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -040093{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040094 u8 translation = get_translation(drive_g);
95 SET_GLOBAL(drive_g->translation, translation);
Kevin O'Connorc892b132009-08-11 21:59:37 -040096
Kevin O'Connor77d227b2009-10-22 21:48:39 -040097 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);
Kevin O'Connor6f6a74d2009-11-09 19:15:50 -0500101 const char *desc = NULL;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400102
Kevin O'Connorc892b132009-08-11 21:59:37 -0400103 switch (translation) {
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400104 default:
Kevin O'Connorc892b132009-08-11 21:59:37 -0400105 case TRANSLATION_NONE:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400106 desc = "none";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400107 break;
108 case TRANSLATION_LBA:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400109 desc = "lba";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400110 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:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400131 desc = "r-echs";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400132 // 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)
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400142 desc = "large";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400143 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;
Kevin O'Connord2d1de02010-02-17 01:07:36 -0500156 dprintf(1, "drive %p: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d s=%d\n"
157 , drive_g
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400158 , drive_g->pchs.cylinders, drive_g->pchs.heads, drive_g->pchs.spt
159 , desc
Kevin O'Connord2d1de02010-02-17 01:07:36 -0500160 , cylinders, heads, spt
161 , (u32)sectors);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400162
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400163 SET_GLOBAL(drive_g->lchs.heads, heads);
164 SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
165 SET_GLOBAL(drive_g->lchs.spt, spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400166}
167
168
169/****************************************************************
170 * Drive mapping
171 ****************************************************************/
172
173// Fill in Fixed Disk Parameter Table (located in ebda).
174static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400175fill_fdpt(struct drive_s *drive_g, int hdid)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400176{
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400177 if (hdid > 1)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400178 return;
179
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400180 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);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400183
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400184 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);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400187
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400188 struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid];
Kevin O'Connorc892b132009-08-11 21:59:37 -0400189 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
Kevin O'Connor085debd2010-01-03 22:24:18 -0500196 if (nlc != npc || nlh != nph || nlspt != npspt) {
197 // Logical mapping present - use extended structure.
Kevin O'Connorc892b132009-08-11 21:59:37 -0400198
Kevin O'Connor085debd2010-01-03 22:24:18 -0500199 // 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;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400205
Kevin O'Connor085debd2010-01-03 22:24:18 -0500206 // Checksum structure.
207 fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
208 }
Kevin O'Connorc892b132009-08-11 21:59:37 -0400209
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400210 if (hdid == 0)
Kevin O'Connor9f985422009-09-09 11:34:39 -0400211 SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
212 struct extended_bios_data_area_s, fdpt[0])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400213 else
Kevin O'Connor9f985422009-09-09 11:34:39 -0400214 SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
215 struct extended_bios_data_area_s, fdpt[1])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400216}
217
Kevin O'Connord254dc22009-11-25 18:49:06 -0500218// Find spot to add a drive
219static void
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500220add_drive(struct drive_s **idmap, u8 *count, struct drive_s *drive_g)
Kevin O'Connord254dc22009-11-25 18:49:06 -0500221{
Kevin O'Connora0842f82010-12-29 11:05:46 -0500222 if (*count >= ARRAY_SIZE(IDMap[0])) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500223 warn_noalloc();
Kevin O'Connord254dc22009-11-25 18:49:06 -0500224 return;
225 }
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500226 idmap[*count] = drive_g;
Kevin O'Connord254dc22009-11-25 18:49:06 -0500227 *count = *count + 1;
Kevin O'Connord254dc22009-11-25 18:49:06 -0500228}
229
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500230// Map a hard drive
231void
232map_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);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500238 add_drive(IDMap[EXTTYPE_HD], &bda->hdcount, drive_g);
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500239
Kevin O'Connor697e63c2010-12-27 21:08:53 -0500240 // Setup disk geometry translation.
241 setup_translation(drive_g);
242
Kevin O'Connor3c5e0e12010-12-27 08:37:38 -0500243 // Fill "fdpt" structure.
244 fill_fdpt(drive_g, hdid);
245}
246
Kevin O'Connorc892b132009-08-11 21:59:37 -0400247// Map a cd
248void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400249map_cd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400250{
Kevin O'Connord254dc22009-11-25 18:49:06 -0500251 dprintf(3, "Mapping cd drive %p\n", drive_g);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500252 add_drive(IDMap[EXTTYPE_CD], &CDCount, drive_g);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400253}
254
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400255// Map a floppy
256void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400257map_floppy_drive(struct drive_s *drive_g)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400258{
Kevin O'Connord254dc22009-11-25 18:49:06 -0500259 dprintf(3, "Mapping floppy drive %p\n", drive_g);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500260 add_drive(IDMap[EXTTYPE_FLOPPY], &FloppyCount, drive_g);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400261
262 // Update equipment word bits for floppy
Kevin O'Connora0842f82010-12-29 11:05:46 -0500263 if (FloppyCount == 1) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400264 // 1 drive, ready for boot
265 SETBITS_BDA(equipment_list_flags, 0x01);
266 SET_BDA(floppy_harddisk_info, 0x07);
Kevin O'Connora0842f82010-12-29 11:05:46 -0500267 } else if (FloppyCount >= 2) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400268 // 2 drives, ready for boot
269 SETBITS_BDA(equipment_list_flags, 0x41);
270 SET_BDA(floppy_harddisk_info, 0x77);
271 }
272}
273
Kevin O'Connorc892b132009-08-11 21:59:37 -0400274
275/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400276 * 16bit calling interface
277 ****************************************************************/
278
Laszlo Ersek26d31442012-02-09 16:55:30 +0100279static int
Paolo Bonzini1e749c82011-11-16 13:02:53 +0100280process_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
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400301// Execute a disk_op request.
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400302int
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400303process_op(struct disk_op_s *op)
304{
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500305 ASSERT16();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400306 u8 type = GET_GLOBAL(op->drive_g->type);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400307 switch (type) {
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400308 case DTYPE_FLOPPY:
309 return process_floppy_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400310 case DTYPE_ATA:
311 return process_ata_op(op);
312 case DTYPE_ATAPI:
313 return process_atapi_op(op);
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400314 case DTYPE_RAMDISK:
315 return process_ramdisk_op(op);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400316 case DTYPE_CDEMU:
317 return process_cdemu_op(op);
Paolo Bonzini0e7fb5f2011-11-16 13:02:55 +0100318 case DTYPE_VIRTIO_BLK:
319 return process_virtio_blk_op(op);
Gerd Hoffmannd52fdf62010-11-29 09:42:13 +0100320 case DTYPE_AHCI:
321 return process_ahci_op(op);
Paolo Bonzini1e749c82011-11-16 13:02:53 +0100322 case DTYPE_USB:
323 return process_scsi_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400324 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.
331static 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
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400339 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
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400341 , dop.count, dop.command);
342
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400343 int status = process_op(&dop);
344
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400345 // 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.
352int
353send_disk_op(struct disk_op_s *op)
354{
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500355 ASSERT16();
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400356 if (! CONFIG_DRIVES)
357 return -1;
358
Kevin O'Connorbca07362010-03-20 20:41:38 -0400359 return stack_hop((u32)op, GET_SEG(SS), __send_disk_op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400360}