blob: c6787e25b2ef38f2f65b50cf2db69b5d26a2b272 [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
183 if (nlc == npc && nlh == nph && nlspt == npspt)
184 // no logical CHS mapping used, just physical CHS
185 // use Standard Fixed Disk Parameter Table (FDPT)
186 return;
187
188 // complies with Phoenix style Translated Fixed Disk Parameter
189 // Table (FDPT)
190 fdpt->phys_cylinders = npc;
191 fdpt->phys_heads = nph;
192 fdpt->phys_sectors = npspt;
193 fdpt->a0h_signature = 0xa0;
194
195 // Checksum structure.
196 fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
197
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400198 if (hdid == 0)
Kevin O'Connor9f985422009-09-09 11:34:39 -0400199 SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
200 struct extended_bios_data_area_s, fdpt[0])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400201 else
Kevin O'Connor9f985422009-09-09 11:34:39 -0400202 SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
203 struct extended_bios_data_area_s, fdpt[1])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400204}
205
206// Map a drive (that was registered via add_bcv_hd)
207void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400208map_hd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400209{
210 // fill hdidmap
211 u8 hdcount = GET_BDA(hdcount);
212 if (hdcount >= ARRAY_SIZE(Drives.idmap[0]))
213 return;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400214 dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdcount);
215 int driveid = drive_g - Drives.drives;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400216 SET_GLOBAL(Drives.idmap[EXTTYPE_HD][hdcount], driveid);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400217 SET_BDA(hdcount, hdcount + 1);
218
219 // Fill "fdpt" structure.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400220 fill_fdpt(drive_g, hdcount);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400221}
222
Kevin O'Connord254dc22009-11-25 18:49:06 -0500223// Find spot to add a drive
224static void
225add_ordered_drive(u8 *idmap, u8 *count, struct drive_s *drive_g)
226{
227 if (*count >= ARRAY_SIZE(Drives.idmap[0])) {
228 dprintf(1, "No room to map drive %p\n", drive_g);
229 return;
230 }
231 u8 *pos = &idmap[*count];
232 *count = *count + 1;
233 if (CONFIG_THREADS) {
234 // Add to idmap with assured drive order.
235 u8 *end = pos;
236 for (;;) {
237 u8 *prev = pos - 1;
238 if (prev < idmap)
239 break;
240 struct drive_s *prevdrive = &Drives.drives[*prev];
241 if (prevdrive->type < drive_g->type
242 || (prevdrive->type == drive_g->type
243 && prevdrive->cntl_id < drive_g->cntl_id))
244 break;
245 pos--;
246 }
247 if (pos != end)
248 memmove(pos+1, pos, (void*)end-(void*)pos);
249 }
250 *pos = drive_g - Drives.drives;
251}
252
Kevin O'Connorc892b132009-08-11 21:59:37 -0400253// Map a cd
254void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400255map_cd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400256{
Kevin O'Connord254dc22009-11-25 18:49:06 -0500257 dprintf(3, "Mapping cd drive %p\n", drive_g);
258 add_ordered_drive(Drives.idmap[EXTTYPE_CD], &Drives.cdcount, drive_g);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400259}
260
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400261// Map a floppy
262void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400263map_floppy_drive(struct drive_s *drive_g)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400264{
265 // fill idmap
Kevin O'Connord254dc22009-11-25 18:49:06 -0500266 dprintf(3, "Mapping floppy drive %p\n", drive_g);
267 add_ordered_drive(Drives.idmap[EXTTYPE_FLOPPY], &Drives.floppycount
268 , drive_g);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400269
270 // Update equipment word bits for floppy
Kevin O'Connord254dc22009-11-25 18:49:06 -0500271 if (Drives.floppycount == 1) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400272 // 1 drive, ready for boot
273 SETBITS_BDA(equipment_list_flags, 0x01);
274 SET_BDA(floppy_harddisk_info, 0x07);
Kevin O'Connord254dc22009-11-25 18:49:06 -0500275 } else if (Drives.floppycount >= 2) {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400276 // 2 drives, ready for boot
277 SETBITS_BDA(equipment_list_flags, 0x41);
278 SET_BDA(floppy_harddisk_info, 0x77);
279 }
280}
281
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400282// Show a one line description (without trailing newline) of a drive.
283void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400284describe_drive(struct drive_s *drive_g)
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400285{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500286 ASSERT32FLAT();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400287 u8 type = GET_GLOBAL(drive_g->type);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400288 switch (type) {
289 case DTYPE_FLOPPY:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400290 describe_floppy(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400291 break;
292 case DTYPE_ATA:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400293 describe_ata(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400294 break;
295 case DTYPE_ATAPI:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400296 describe_atapi(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400297 break;
298 case DTYPE_RAMDISK:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400299 describe_ramdisk(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400300 break;
301 default:
302 printf("Unknown");
303 break;
304 }
305}
306
Kevin O'Connorc892b132009-08-11 21:59:37 -0400307
308/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400309 * 16bit calling interface
310 ****************************************************************/
311
312// 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'Connor77d227b2009-10-22 21:48:39 -0400316 u8 type = GET_GLOBAL(op->drive_g->type);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400317 switch (type) {
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400318 case DTYPE_FLOPPY:
319 return process_floppy_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400320 case DTYPE_ATA:
321 return process_ata_op(op);
322 case DTYPE_ATAPI:
323 return process_atapi_op(op);
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400324 case DTYPE_RAMDISK:
325 return process_ramdisk_op(op);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400326 case DTYPE_CDEMU:
327 return process_cdemu_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400328 default:
329 op->count = 0;
330 return DISK_RET_EPARAM;
331 }
332}
333
334// Execute a "disk_op_s" request - this runs on a stack in the ebda.
335static int
336__send_disk_op(struct disk_op_s *op_far, u16 op_seg)
337{
338 struct disk_op_s dop;
339 memcpy_far(GET_SEG(SS), &dop
340 , op_seg, op_far
341 , sizeof(dop));
342
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400343 dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
344 , dop.drive_g, (u32)dop.lba, dop.buf_fl
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400345 , dop.count, dop.command);
346
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400347 int status = process_op(&dop);
348
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400349 // Update count with total sectors transferred.
350 SET_FARVAR(op_seg, op_far->count, dop.count);
351
352 return status;
353}
354
355// Execute a "disk_op_s" request by jumping to a stack in the ebda.
356int
357send_disk_op(struct disk_op_s *op)
358{
359 if (! CONFIG_DRIVES)
360 return -1;
Kevin O'Connor372e0712009-09-09 09:51:31 -0400361 ASSERT16();
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400362
363 return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
364}
365
366
367/****************************************************************
Kevin O'Connorc892b132009-08-11 21:59:37 -0400368 * Setup
369 ****************************************************************/
370
371void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500372drive_setup(void)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400373{
374 memset(&Drives, 0, sizeof(Drives));
375 memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
376}