blob: a62536caa8707b343852ec4e9c4bccbf07780ffb [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 *
34allocDrive()
35{
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'Connorc892b132009-08-11 21:59:37 -040089
90 dprintf(1, "ata%d-%d: PCHS=%u/%d/%d translation="
91 , channel, slave, cylinders, heads, spt);
92 switch (translation) {
93 case TRANSLATION_NONE:
94 dprintf(1, "none");
95 break;
96 case TRANSLATION_LBA:
97 dprintf(1, "lba");
98 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:
119 dprintf(1, "r-echs");
120 // 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)
130 dprintf(1, "large");
131 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;
144 dprintf(1, " LCHS=%d/%d/%d\n", cylinders, heads, spt);
145
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400146 SET_GLOBAL(drive_g->lchs.heads, heads);
147 SET_GLOBAL(drive_g->lchs.cylinders, cylinders);
148 SET_GLOBAL(drive_g->lchs.spt, spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400149}
150
151
152/****************************************************************
153 * Drive mapping
154 ****************************************************************/
155
156// Fill in Fixed Disk Parameter Table (located in ebda).
157static void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400158fill_fdpt(struct drive_s *drive_g, int hdid)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400159{
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400160 if (hdid > 1)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400161 return;
162
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400163 u16 nlc = GET_GLOBAL(drive_g->lchs.cylinders);
164 u16 nlh = GET_GLOBAL(drive_g->lchs.heads);
165 u16 nlspt = GET_GLOBAL(drive_g->lchs.spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400166
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400167 u16 npc = GET_GLOBAL(drive_g->pchs.cylinders);
168 u16 nph = GET_GLOBAL(drive_g->pchs.heads);
169 u16 npspt = GET_GLOBAL(drive_g->pchs.spt);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400170
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400171 struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[hdid];
Kevin O'Connorc892b132009-08-11 21:59:37 -0400172 fdpt->precompensation = 0xffff;
173 fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3);
174 fdpt->landing_zone = npc;
175 fdpt->cylinders = nlc;
176 fdpt->heads = nlh;
177 fdpt->sectors = nlspt;
178
179 if (nlc == npc && nlh == nph && nlspt == npspt)
180 // no logical CHS mapping used, just physical CHS
181 // use Standard Fixed Disk Parameter Table (FDPT)
182 return;
183
184 // complies with Phoenix style Translated Fixed Disk Parameter
185 // Table (FDPT)
186 fdpt->phys_cylinders = npc;
187 fdpt->phys_heads = nph;
188 fdpt->phys_sectors = npspt;
189 fdpt->a0h_signature = 0xa0;
190
191 // Checksum structure.
192 fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
193
Kevin O'Connor34ec7b02009-09-20 20:04:06 -0400194 if (hdid == 0)
Kevin O'Connor9f985422009-09-09 11:34:39 -0400195 SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
196 struct extended_bios_data_area_s, fdpt[0])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400197 else
Kevin O'Connor9f985422009-09-09 11:34:39 -0400198 SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
199 struct extended_bios_data_area_s, fdpt[1])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400200}
201
202// Map a drive (that was registered via add_bcv_hd)
203void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400204map_hd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400205{
206 // fill hdidmap
207 u8 hdcount = GET_BDA(hdcount);
208 if (hdcount >= ARRAY_SIZE(Drives.idmap[0]))
209 return;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400210 dprintf(3, "Mapping hd drive %p to %d\n", drive_g, hdcount);
211 int driveid = drive_g - Drives.drives;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400212 SET_GLOBAL(Drives.idmap[EXTTYPE_HD][hdcount], driveid);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400213 SET_BDA(hdcount, hdcount + 1);
214
215 // Fill "fdpt" structure.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400216 fill_fdpt(drive_g, hdcount);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400217}
218
219// Map a cd
220void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400221map_cd_drive(struct drive_s *drive_g)
Kevin O'Connorc892b132009-08-11 21:59:37 -0400222{
223 // fill cdidmap
224 u8 cdcount = GET_GLOBAL(Drives.cdcount);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400225 if (cdcount >= ARRAY_SIZE(Drives.idmap[0]))
Kevin O'Connorc892b132009-08-11 21:59:37 -0400226 return;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400227 dprintf(3, "Mapping cd drive %p to %d\n", drive_g, cdcount);
228 int driveid = drive_g - Drives.drives;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400229 SET_GLOBAL(Drives.idmap[EXTTYPE_CD][cdcount], driveid);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400230 SET_GLOBAL(Drives.cdcount, cdcount+1);
231}
232
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400233// Map a floppy
234void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400235map_floppy_drive(struct drive_s *drive_g)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400236{
237 // fill idmap
238 u8 floppycount = GET_GLOBAL(Drives.floppycount);
239 if (floppycount >= ARRAY_SIZE(Drives.idmap[0]))
240 return;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400241 dprintf(3, "Mapping floppy drive %p to %d\n", drive_g, floppycount);
242 int driveid = drive_g - Drives.drives;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400243 SET_GLOBAL(Drives.idmap[EXTTYPE_FLOPPY][floppycount], driveid);
244 floppycount++;
245 SET_GLOBAL(Drives.floppycount, floppycount);
246
247 // Update equipment word bits for floppy
248 if (floppycount == 1) {
249 // 1 drive, ready for boot
250 SETBITS_BDA(equipment_list_flags, 0x01);
251 SET_BDA(floppy_harddisk_info, 0x07);
252 } else {
253 // 2 drives, ready for boot
254 SETBITS_BDA(equipment_list_flags, 0x41);
255 SET_BDA(floppy_harddisk_info, 0x77);
256 }
257}
258
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400259// Show a one line description (without trailing newline) of a drive.
260void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400261describe_drive(struct drive_s *drive_g)
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400262{
263 ASSERT32();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400264 u8 type = GET_GLOBAL(drive_g->type);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400265 switch (type) {
266 case DTYPE_FLOPPY:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400267 describe_floppy(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400268 break;
269 case DTYPE_ATA:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400270 describe_ata(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400271 break;
272 case DTYPE_ATAPI:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400273 describe_atapi(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400274 break;
275 case DTYPE_RAMDISK:
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400276 describe_ramdisk(drive_g);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400277 break;
278 default:
279 printf("Unknown");
280 break;
281 }
282}
283
Kevin O'Connorc892b132009-08-11 21:59:37 -0400284
285/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400286 * 16bit calling interface
287 ****************************************************************/
288
289// Execute a disk_op request.
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400290int
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400291process_op(struct disk_op_s *op)
292{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400293 u8 type = GET_GLOBAL(op->drive_g->type);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400294 switch (type) {
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400295 case DTYPE_FLOPPY:
296 return process_floppy_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400297 case DTYPE_ATA:
298 return process_ata_op(op);
299 case DTYPE_ATAPI:
300 return process_atapi_op(op);
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400301 case DTYPE_RAMDISK:
302 return process_ramdisk_op(op);
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400303 case DTYPE_CDEMU:
304 return process_cdemu_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400305 default:
306 op->count = 0;
307 return DISK_RET_EPARAM;
308 }
309}
310
311// Execute a "disk_op_s" request - this runs on a stack in the ebda.
312static int
313__send_disk_op(struct disk_op_s *op_far, u16 op_seg)
314{
315 struct disk_op_s dop;
316 memcpy_far(GET_SEG(SS), &dop
317 , op_seg, op_far
318 , sizeof(dop));
319
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400320 dprintf(DEBUG_HDL_13, "disk_op d=%p lba=%d buf=%p count=%d cmd=%d\n"
321 , dop.drive_g, (u32)dop.lba, dop.buf_fl
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400322 , dop.count, dop.command);
323
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400324 int status = process_op(&dop);
325
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400326 // Update count with total sectors transferred.
327 SET_FARVAR(op_seg, op_far->count, dop.count);
328
329 return status;
330}
331
332// Execute a "disk_op_s" request by jumping to a stack in the ebda.
333int
334send_disk_op(struct disk_op_s *op)
335{
336 if (! CONFIG_DRIVES)
337 return -1;
Kevin O'Connor372e0712009-09-09 09:51:31 -0400338 ASSERT16();
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400339
340 return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
341}
342
343
344/****************************************************************
Kevin O'Connorc892b132009-08-11 21:59:37 -0400345 * Setup
346 ****************************************************************/
347
348void
349drive_setup()
350{
351 memset(&Drives, 0, sizeof(Drives));
352 memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
353}