blob: c24e11828c8daa0abf7ace66e541ea55004159cc [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
16
17/****************************************************************
18 * Disk geometry translation
19 ****************************************************************/
20
21static u8
22get_translation(int driveid)
23{
24 u8 type = GET_GLOBAL(Drives.drives[driveid].type);
25 if (! CONFIG_COREBOOT && type == DTYPE_ATA) {
26 // Emulators pass in the translation info via nvram.
27 u8 ataid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
28 u8 channel = ataid / 2;
29 u8 translation = inb_cmos(CMOS_BIOS_DISKTRANSFLAG + channel/2);
30 translation >>= 2 * (ataid % 4);
31 translation &= 0x03;
32 return translation;
33 }
34
35 // On COREBOOT, use a heuristic to determine translation type.
36 u16 heads = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
37 u16 cylinders = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
38 u16 spt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
39
40 if (cylinders <= 1024 && heads <= 16 && spt <= 63)
41 return TRANSLATION_NONE;
42 if (cylinders * heads <= 131072)
43 return TRANSLATION_LARGE;
44 return TRANSLATION_LBA;
45}
46
47void
48setup_translation(int driveid)
49{
50 u8 translation = get_translation(driveid);
51 SET_GLOBAL(Drives.drives[driveid].translation, translation);
52
53 u8 ataid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
54 u8 channel = ataid / 2;
55 u8 slave = ataid % 2;
56 u16 heads = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
57 u16 cylinders = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
58 u16 spt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
59 u64 sectors = GET_GLOBAL(Drives.drives[driveid].sectors);
60
61 dprintf(1, "ata%d-%d: PCHS=%u/%d/%d translation="
62 , channel, slave, cylinders, heads, spt);
63 switch (translation) {
64 case TRANSLATION_NONE:
65 dprintf(1, "none");
66 break;
67 case TRANSLATION_LBA:
68 dprintf(1, "lba");
69 spt = 63;
70 if (sectors > 63*255*1024) {
71 heads = 255;
72 cylinders = 1024;
73 break;
74 }
75 u32 sect = (u32)sectors / 63;
76 heads = sect / 1024;
77 if (heads>128)
78 heads = 255;
79 else if (heads>64)
80 heads = 128;
81 else if (heads>32)
82 heads = 64;
83 else if (heads>16)
84 heads = 32;
85 else
86 heads = 16;
87 cylinders = sect / heads;
88 break;
89 case TRANSLATION_RECHS:
90 dprintf(1, "r-echs");
91 // Take care not to overflow
92 if (heads==16) {
93 if (cylinders>61439)
94 cylinders=61439;
95 heads=15;
96 cylinders = (u16)((u32)(cylinders)*16/15);
97 }
98 // then go through the large bitshift process
99 case TRANSLATION_LARGE:
100 if (translation == TRANSLATION_LARGE)
101 dprintf(1, "large");
102 while (cylinders > 1024) {
103 cylinders >>= 1;
104 heads <<= 1;
105
106 // If we max out the head count
107 if (heads > 127)
108 break;
109 }
110 break;
111 }
112 // clip to 1024 cylinders in lchs
113 if (cylinders > 1024)
114 cylinders = 1024;
115 dprintf(1, " LCHS=%d/%d/%d\n", cylinders, heads, spt);
116
117 SET_GLOBAL(Drives.drives[driveid].lchs.heads, heads);
118 SET_GLOBAL(Drives.drives[driveid].lchs.cylinders, cylinders);
119 SET_GLOBAL(Drives.drives[driveid].lchs.spt, spt);
120}
121
122
123/****************************************************************
124 * Drive mapping
125 ****************************************************************/
126
127// Fill in Fixed Disk Parameter Table (located in ebda).
128static void
129fill_fdpt(int driveid)
130{
131 if (driveid > 1)
132 return;
133
134 u16 nlc = GET_GLOBAL(Drives.drives[driveid].lchs.cylinders);
135 u16 nlh = GET_GLOBAL(Drives.drives[driveid].lchs.heads);
136 u16 nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
137
138 u16 npc = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
139 u16 nph = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
140 u16 npspt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
141
142 struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[driveid];
143 fdpt->precompensation = 0xffff;
144 fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3);
145 fdpt->landing_zone = npc;
146 fdpt->cylinders = nlc;
147 fdpt->heads = nlh;
148 fdpt->sectors = nlspt;
149
150 if (nlc == npc && nlh == nph && nlspt == npspt)
151 // no logical CHS mapping used, just physical CHS
152 // use Standard Fixed Disk Parameter Table (FDPT)
153 return;
154
155 // complies with Phoenix style Translated Fixed Disk Parameter
156 // Table (FDPT)
157 fdpt->phys_cylinders = npc;
158 fdpt->phys_heads = nph;
159 fdpt->phys_sectors = npspt;
160 fdpt->a0h_signature = 0xa0;
161
162 // Checksum structure.
163 fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
164
165 if (driveid == 0)
Kevin O'Connor9f985422009-09-09 11:34:39 -0400166 SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
167 struct extended_bios_data_area_s, fdpt[0])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400168 else
Kevin O'Connor9f985422009-09-09 11:34:39 -0400169 SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
170 struct extended_bios_data_area_s, fdpt[1])));
Kevin O'Connorc892b132009-08-11 21:59:37 -0400171}
172
173// Map a drive (that was registered via add_bcv_hd)
174void
175map_hd_drive(int driveid)
176{
177 // fill hdidmap
178 u8 hdcount = GET_BDA(hdcount);
179 if (hdcount >= ARRAY_SIZE(Drives.idmap[0]))
180 return;
181 dprintf(3, "Mapping hd driveid %d to %d\n", driveid, hdcount);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400182 SET_GLOBAL(Drives.idmap[EXTTYPE_HD][hdcount], driveid);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400183 SET_BDA(hdcount, hdcount + 1);
184
185 // Fill "fdpt" structure.
186 fill_fdpt(hdcount);
187}
188
189// Map a cd
190void
191map_cd_drive(int driveid)
192{
193 // fill cdidmap
194 u8 cdcount = GET_GLOBAL(Drives.cdcount);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400195 if (cdcount >= ARRAY_SIZE(Drives.idmap[0]))
Kevin O'Connorc892b132009-08-11 21:59:37 -0400196 return;
197 dprintf(3, "Mapping cd driveid %d to %d\n", driveid, cdcount);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400198 SET_GLOBAL(Drives.idmap[EXTTYPE_CD][cdcount], driveid);
Kevin O'Connorc892b132009-08-11 21:59:37 -0400199 SET_GLOBAL(Drives.cdcount, cdcount+1);
200}
201
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400202// Map a floppy
203void
204map_floppy_drive(int driveid)
205{
206 // fill idmap
207 u8 floppycount = GET_GLOBAL(Drives.floppycount);
208 if (floppycount >= ARRAY_SIZE(Drives.idmap[0]))
209 return;
210 dprintf(3, "Mapping floppy driveid %d to %d\n", driveid, floppycount);
211 SET_GLOBAL(Drives.idmap[EXTTYPE_FLOPPY][floppycount], driveid);
212 floppycount++;
213 SET_GLOBAL(Drives.floppycount, floppycount);
214
215 // Update equipment word bits for floppy
216 if (floppycount == 1) {
217 // 1 drive, ready for boot
218 SETBITS_BDA(equipment_list_flags, 0x01);
219 SET_BDA(floppy_harddisk_info, 0x07);
220 } else {
221 // 2 drives, ready for boot
222 SETBITS_BDA(equipment_list_flags, 0x41);
223 SET_BDA(floppy_harddisk_info, 0x77);
224 }
225}
226
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400227// Show a one line description (without trailing newline) of a drive.
228void
229describe_drive(int driveid)
230{
231 ASSERT32();
232 u8 type = GET_GLOBAL(Drives.drives[driveid].type);
233 switch (type) {
234 case DTYPE_FLOPPY:
235 describe_floppy(driveid);
236 break;
237 case DTYPE_ATA:
238 describe_ata(driveid);
239 break;
240 case DTYPE_ATAPI:
241 describe_atapi(driveid);
242 break;
243 case DTYPE_RAMDISK:
244 describe_ramdisk(driveid);
245 break;
246 default:
247 printf("Unknown");
248 break;
249 }
250}
251
Kevin O'Connorc892b132009-08-11 21:59:37 -0400252
253/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400254 * 16bit calling interface
255 ****************************************************************/
256
257// Execute a disk_op request.
258static int
259process_op(struct disk_op_s *op)
260{
261 u8 type = GET_GLOBAL(Drives.drives[op->driveid].type);
262 switch (type) {
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400263 case DTYPE_FLOPPY:
264 return process_floppy_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400265 case DTYPE_ATA:
266 return process_ata_op(op);
267 case DTYPE_ATAPI:
268 return process_atapi_op(op);
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400269 case DTYPE_RAMDISK:
270 return process_ramdisk_op(op);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400271 default:
272 op->count = 0;
273 return DISK_RET_EPARAM;
274 }
275}
276
277// Execute a "disk_op_s" request - this runs on a stack in the ebda.
278static int
279__send_disk_op(struct disk_op_s *op_far, u16 op_seg)
280{
281 struct disk_op_s dop;
282 memcpy_far(GET_SEG(SS), &dop
283 , op_seg, op_far
284 , sizeof(dop));
285
286 dprintf(DEBUG_HDL_13, "disk_op d=%d lba=%d buf=%p count=%d cmd=%d\n"
287 , dop.driveid, (u32)dop.lba, dop.buf_fl
288 , dop.count, dop.command);
289
290 irq_enable();
291
292 int status = process_op(&dop);
293
294 irq_disable();
295
296 // Update count with total sectors transferred.
297 SET_FARVAR(op_seg, op_far->count, dop.count);
298
299 return status;
300}
301
302// Execute a "disk_op_s" request by jumping to a stack in the ebda.
303int
304send_disk_op(struct disk_op_s *op)
305{
306 if (! CONFIG_DRIVES)
307 return -1;
Kevin O'Connor372e0712009-09-09 09:51:31 -0400308 ASSERT16();
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400309
310 return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
311}
312
313
314/****************************************************************
Kevin O'Connorc892b132009-08-11 21:59:37 -0400315 * Setup
316 ****************************************************************/
317
318void
319drive_setup()
320{
321 memset(&Drives, 0, sizeof(Drives));
322 memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
323}