blob: 3a9a68d2e338f90e9bb0c6d9c4594b851c2dcac2 [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
Kevin O'Connorc892b132009-08-11 21:59:37 -040013
Kevin O'Connor372e0712009-09-09 09:51:31 -040014struct drives_s Drives VAR16VISIBLE;
Kevin O'Connorc892b132009-08-11 21:59:37 -040015
Kevin O'Connor77d227b2009-10-22 21:48:39 -040016struct drive_s *
17getDrive(u8 exttype, u8 extdriveoffset)
18{
19 // basic check : device has to be defined
20 if (extdriveoffset >= ARRAY_SIZE(Drives.idmap[0]))
21 return NULL;
22
23 // Get the ata channel
24 u8 driveid = GET_GLOBAL(Drives.idmap[exttype][extdriveoffset]);
25
26 // basic check : device has to be valid
27 if (driveid >= ARRAY_SIZE(Drives.drives))
28 return NULL;
29
30 return &Drives.drives[driveid];
31}
32
33struct drive_s *
Kevin O'Connor1ca05b02010-01-03 17:43:37 -050034allocDrive(void)
Kevin O'Connor77d227b2009-10-22 21:48:39 -040035{
36 int driveid = Drives.drivecount;
37 if (driveid >= ARRAY_SIZE(Drives.drives))
38 return NULL;
39 Drives.drivecount++;
40 struct drive_s *drive_g = &Drives.drives[driveid];
41 memset(drive_g, 0, sizeof(*drive_g));
42 return drive_g;
43}
44
Kevin O'Connorc892b132009-08-11 21:59:37 -040045
46/****************************************************************
47 * Disk geometry translation
48 ****************************************************************/
49
50static u8
Kevin O'Connor77d227b2009-10-22 21:48:39 -040051get_translation(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -040052{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040053 u8 type = GET_GLOBAL(drive_g->type);
Kevin O'Connorc892b132009-08-11 21:59:37 -040054 if (! CONFIG_COREBOOT && type == DTYPE_ATA) {
55 // Emulators pass in the translation info via nvram.
Kevin O'Connor77d227b2009-10-22 21:48:39 -040056 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connorc892b132009-08-11 21:59:37 -040057 u8 channel = ataid / 2;
58 u8 translation = inb_cmos(CMOS_BIOS_DISKTRANSFLAG + channel/2);
59 translation >>= 2 * (ataid % 4);
60 translation &= 0x03;
61 return translation;
62 }
63
64 // On COREBOOT, use a heuristic to determine translation type.
Kevin O'Connor77d227b2009-10-22 21:48:39 -040065 u16 heads = GET_GLOBAL(drive_g->pchs.heads);
66 u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders);
67 u16 spt = GET_GLOBAL(drive_g->pchs.spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -040068
69 if (cylinders <= 1024 && heads <= 16 && spt <= 63)
70 return TRANSLATION_NONE;
71 if (cylinders * heads <= 131072)
72 return TRANSLATION_LARGE;
73 return TRANSLATION_LBA;
74}
75
76void
Kevin O'Connor77d227b2009-10-22 21:48:39 -040077setup_translation(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -040078{
Kevin O'Connor77d227b2009-10-22 21:48:39 -040079 u8 translation = get_translation(drive_g);
80 SET_GLOBAL(drive_g->translation, translation);
Kevin O'Connorc892b132009-08-11 21:59:37 -040081
Kevin O'Connor77d227b2009-10-22 21:48:39 -040082 u8 ataid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connorc892b132009-08-11 21:59:37 -040083 u8 channel = ataid / 2;
84 u8 slave = ataid % 2;
Kevin O'Connor77d227b2009-10-22 21:48:39 -040085 u16 heads = GET_GLOBAL(drive_g->pchs.heads);
86 u16 cylinders = GET_GLOBAL(drive_g->pchs.cylinders);
87 u16 spt = GET_GLOBAL(drive_g->pchs.spt);
88 u64 sectors = GET_GLOBAL(drive_g->sectors);
Kevin O'Connor6f6a74d2009-11-09 19:15:50 -050089 const char *desc = NULL;
Kevin O'Connorc892b132009-08-11 21:59:37 -040090
Kevin O'Connorc892b132009-08-11 21:59:37 -040091 switch (translation) {
Kevin O'Connorc604f2f2009-10-24 19:56:11 -040092 default:
Kevin O'Connorc892b132009-08-11 21:59:37 -040093 case TRANSLATION_NONE:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -040094 desc = "none";
Kevin O'Connorc892b132009-08-11 21:59:37 -040095 break;
96 case TRANSLATION_LBA:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -040097 desc = "lba";
Kevin O'Connorc892b132009-08-11 21:59:37 -040098 spt = 63;
99 if (sectors > 63*255*1024) {
100 heads = 255;
101 cylinders = 1024;
102 break;
103 }
104 u32 sect = (u32)sectors / 63;
105 heads = sect / 1024;
106 if (heads>128)
107 heads = 255;
108 else if (heads>64)
109 heads = 128;
110 else if (heads>32)
111 heads = 64;
112 else if (heads>16)
113 heads = 32;
114 else
115 heads = 16;
116 cylinders = sect / heads;
117 break;
118 case TRANSLATION_RECHS:
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400119 desc = "r-echs";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400120 // Take care not to overflow
121 if (heads==16) {
122 if (cylinders>61439)
123 cylinders=61439;
124 heads=15;
125 cylinders = (u16)((u32)(cylinders)*16/15);
126 }
127 // then go through the large bitshift process
128 case TRANSLATION_LARGE:
129 if (translation == TRANSLATION_LARGE)
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400130 desc = "large";
Kevin O'Connorc892b132009-08-11 21:59:37 -0400131 while (cylinders > 1024) {
132 cylinders >>= 1;
133 heads <<= 1;
134
135 // If we max out the head count
136 if (heads > 127)
137 break;
138 }
139 break;
140 }
141 // clip to 1024 cylinders in lchs
142 if (cylinders > 1024)
143 cylinders = 1024;
Kevin O'Connorc604f2f2009-10-24 19:56:11 -0400144 dprintf(1, "ata%d-%d: PCHS=%u/%d/%d translation=%s LCHS=%d/%d/%d\n"
145 , channel, slave
146 , drive_g->pchs.cylinders, drive_g->pchs.heads, drive_g->pchs.spt
147 , desc
148 , cylinders, heads, spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400149
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400150 SET_GLOBAL(drive_g->lchs.heads, heads);
151 SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
152 SET_GLOBAL(drive_g->lchs.spt, spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400153}
154
155
156/****************************************************************
157 * Drive mapping
158 ****************************************************************/
159
160// Fill in Fixed Disk Parameter Table (located in ebda).
161static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400162fill_fdpt(struct drive_s *drive_g, int hdid)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400163{
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400164 if (hdid > 1)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400165 return;
166
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400167 u16 nlc = GET_GLOBAL(drive_g->lchs.cylinders);
168 u16 nlh = GET_GLOBAL(drive_g->lchs.heads);
169 u16 nlspt = GET_GLOBAL(drive_g->lchs.spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400170
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400171 u16 npc = GET_GLOBAL(drive_g->pchs.cylinders);
172 u16 nph = GET_GLOBAL(drive_g->pchs.heads);
173 u16 npspt = GET_GLOBAL(drive_g->pchs.spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400174
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400175 struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid];
Kevin O'Connorc892b132009-08-11 21:59:37 -0400176 fdpt->precompensation = 0xffff;
177 fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3);
178 fdpt->landing_zone = npc;
179 fdpt->cylinders = nlc;
180 fdpt->heads = nlh;
181 fdpt->sectors = nlspt;
182
Kevin O'Connor085debd2010-01-03 22:24:18 -0500183 if (nlc != npc || nlh != nph || nlspt != npspt) {
184 // Logical mapping present - use extended structure.
Kevin O'Connorc892b132009-08-11 21:59:37 -0400185
Kevin O'Connor085debd2010-01-03 22:24:18 -0500186 // complies with Phoenix style Translated Fixed Disk Parameter
187 // Table (FDPT)
188 fdpt->phys_cylinders = npc;
189 fdpt->phys_heads = nph;
190 fdpt->phys_sectors = npspt;
191 fdpt->a0h_signature = 0xa0;
Kevin O'Connorc892b132009-08-11 21:59:37 -0400192
Kevin O'Connor085debd2010-01-03 22:24:18 -0500193 // Checksum structure.
194 fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
195 }
Kevin O'Connorc892b132009-08-11 21:59:37 -0400196
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400197 if (hdid == 0)
Kevin O'Connor9f985422009-09-09 11:34:39 -0400198 SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
199 struct extended_bios_data_area_s, fdpt[0])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400200 else
Kevin O'Connor9f985422009-09-09 11:34:39 -0400201 SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
202 struct extended_bios_data_area_s, fdpt[1])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400203}
204
205// Map a drive (that was registered via add_bcv_hd)
206void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400207map_hd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400208{
209 // fill hdidmap
210 u8 hdcount = GET_BDA(hdcount);
211 if (hdcount >= ARRAY_SIZE(Drives.idmap[0]))
212 return;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400213 dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdcount);
214 int driveid = drive_g - Drives.drives;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400215 SET_GLOBAL(Drives.idmap[EXTTYPE_HD][hdcount], driveid);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400216 SET_BDA(hdcount, hdcount + 1);
217
218 // Fill "fdpt" structure.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400219 fill_fdpt(drive_g, hdcount);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400220}
221
Kevin O'Connord254dc22009-11-25 18:49:06 -0500222// Find spot to add a drive
223static void
224add_ordered_drive(u8 *idmap, u8 *count, struct drive_s *drive_g)
225{
226 if (*count >= ARRAY_SIZE(Drives.idmap[0])) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500227 warn_noalloc();
Kevin O'Connord254dc22009-11-25 18:49:06 -0500228 return;
229 }
230 u8 *pos = &idmap[*count];
231 *count = *count + 1;
232 if (CONFIG_THREADS) {
233 // Add to idmap with assured drive order.
234 u8 *end = pos;
235 for (;;) {
236 u8 *prev = pos - 1;
237 if (prev < idmap)
238 break;
239 struct drive_s *prevdrive = &Drives.drives[*prev];
240 if (prevdrive->type < drive_g->type
241 || (prevdrive->type == drive_g->type
242 && prevdrive->cntl_id < drive_g->cntl_id))
243 break;
244 pos--;
245 }
246 if (pos != end)
247 memmove(pos+1, pos, (void*)end-(void*)pos);
248 }
249 *pos = drive_g - Drives.drives;
250}
251
Kevin O'Connorc892b132009-08-11 21:59:37 -0400252// Map a cd
253void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400254map_cd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400255{
Kevin O'Connord254dc22009-11-25 18:49:06 -0500256 dprintf(3, "Mapping cd drive %p\n", drive_g);
257 add_ordered_drive(Drives.idmap[EXTTYPE_CD], &Drives.cdcount, drive_g);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400258}
259
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400260// Map a floppy
261void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400262map_floppy_drive(struct drive_s *drive_g)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400263{
264 // fill idmap
Kevin O'Connord254dc22009-11-25 18:49:06 -0500265 dprintf(3, "Mapping floppy drive %p\n", drive_g);
266 add_ordered_drive(Drives.idmap[EXTTYPE_FLOPPY], &Drives.floppycount
267 , drive_g);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400268
269 // Update equipment word bits for floppy
Kevin O'Connord254dc22009-11-25 18:49:06 -0500270 if (Drives.floppycount == 1) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400271 // 1 drive, ready for boot
272 SETBITS_BDA(equipment_list_flags, 0x01);
273 SET_BDA(floppy_harddisk_info, 0x07);
Kevin O'Connord254dc22009-11-25 18:49:06 -0500274 } else if (Drives.floppycount >= 2) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400275 // 2 drives, ready for boot
276 SETBITS_BDA(equipment_list_flags, 0x41);
277 SET_BDA(floppy_harddisk_info, 0x77);
278 }
279}
280
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400281// Show a one line description (without trailing newline) of a drive.
282void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400283describe_drive(struct drive_s *drive_g)
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400284{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500285 ASSERT32FLAT();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400286 u8 type = GET_GLOBAL(drive_g->type);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400287 switch (type) {
288 case DTYPE_FLOPPY:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400289 describe_floppy(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400290 break;
291 case DTYPE_ATA:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400292 describe_ata(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400293 break;
294 case DTYPE_ATAPI:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400295 describe_atapi(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400296 break;
297 case DTYPE_RAMDISK:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400298 describe_ramdisk(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400299 break;
300 default:
301 printf("Unknown");
302 break;
303 }
304}
305
Kevin O'Connorc892b132009-08-11 21:59:37 -0400306
307/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400308 * 16bit calling interface
309 ****************************************************************/
310
311// Execute a disk_op request.
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400312int
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400313process_op(struct disk_op_s *op)
314{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400315 u8 type = GET_GLOBAL(op->drive_g->type);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400316 switch (type) {
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400317 case DTYPE_FLOPPY:
318 return process_floppy_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400319 case DTYPE_ATA:
320 return process_ata_op(op);
321 case DTYPE_ATAPI:
322 return process_atapi_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);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400327 default:
328 op->count = 0;
329 return DISK_RET_EPARAM;
330 }
331}
332
333// Execute a "disk_op_s" request - this runs on a stack in the ebda.
334static int
335__send_disk_op(struct disk_op_s *op_far, u16 op_seg)
336{
337 struct disk_op_s dop;
338 memcpy_far(GET_SEG(SS), &dop
339 , op_seg, op_far
340 , sizeof(dop));
341
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400342 dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
343 , dop.drive_g, (u32)dop.lba, dop.buf_fl
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400344 , dop.count, dop.command);
345
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400346 int status = process_op(&dop);
347
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400348 // Update count with total sectors transferred.
349 SET_FARVAR(op_seg, op_far->count, dop.count);
350
351 return status;
352}
353
354// Execute a "disk_op_s" request by jumping to a stack in the ebda.
355int
356send_disk_op(struct disk_op_s *op)
357{
358 if (! CONFIG_DRIVES)
359 return -1;
Kevin O'Connor372e0712009-09-09 09:51:31 -0400360 ASSERT16();
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400361
362 return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
363}
364
365
366/****************************************************************
Kevin O'Connorc892b132009-08-11 21:59:37 -0400367 * Setup
368 ****************************************************************/
369
370void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500371drive_setup(void)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400372{
373 memset(&Drives, 0, sizeof(Drives));
374 memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
375}