Fix bug in fdpt filling - driveid is not hdid.
[seabios.git] / src / block.c
1 // 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
12 #include "ata.h" // process_ata_op
13
14 struct drives_s Drives VAR16VISIBLE;
15
16
17 /****************************************************************
18  * Disk geometry translation
19  ****************************************************************/
20
21 static u8
22 get_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
47 void
48 setup_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).
128 static void
129 fill_fdpt(int driveid, int hdid)
130 {
131     if (hdid > 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[hdid];
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 (hdid == 0)
166         SET_IVT(0x41, SEGOFF(get_ebda_seg(), offsetof(
167                                  struct extended_bios_data_area_s, fdpt[0])));
168     else
169         SET_IVT(0x46, SEGOFF(get_ebda_seg(), offsetof(
170                                  struct extended_bios_data_area_s, fdpt[1])));
171 }
172
173 // Map a drive (that was registered via add_bcv_hd)
174 void
175 map_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);
182     SET_GLOBAL(Drives.idmap[EXTTYPE_HD][hdcount], driveid);
183     SET_BDA(hdcount, hdcount + 1);
184
185     // Fill "fdpt" structure.
186     fill_fdpt(driveid, hdcount);
187 }
188
189 // Map a cd
190 void
191 map_cd_drive(int driveid)
192 {
193     // fill cdidmap
194     u8 cdcount = GET_GLOBAL(Drives.cdcount);
195     if (cdcount >= ARRAY_SIZE(Drives.idmap[0]))
196         return;
197     dprintf(3, "Mapping cd driveid %d to %d\n", driveid, cdcount);
198     SET_GLOBAL(Drives.idmap[EXTTYPE_CD][cdcount], driveid);
199     SET_GLOBAL(Drives.cdcount, cdcount+1);
200 }
201
202 // Map a floppy
203 void
204 map_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
227 // Show a one line description (without trailing newline) of a drive.
228 void
229 describe_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
252
253 /****************************************************************
254  * 16bit calling interface
255  ****************************************************************/
256
257 // Execute a disk_op request.
258 int
259 process_op(struct disk_op_s *op)
260 {
261     u8 type = GET_GLOBAL(Drives.drives[op->driveid].type);
262     switch (type) {
263     case DTYPE_FLOPPY:
264         return process_floppy_op(op);
265     case DTYPE_ATA:
266         return process_ata_op(op);
267     case DTYPE_ATAPI:
268         return process_atapi_op(op);
269     case DTYPE_RAMDISK:
270         return process_ramdisk_op(op);
271     case DTYPE_CDEMU:
272         return process_cdemu_op(op);
273     default:
274         op->count = 0;
275         return DISK_RET_EPARAM;
276     }
277 }
278
279 // Execute a "disk_op_s" request - this runs on a stack in the ebda.
280 static int
281 __send_disk_op(struct disk_op_s *op_far, u16 op_seg)
282 {
283     struct disk_op_s dop;
284     memcpy_far(GET_SEG(SS), &dop
285                , op_seg, op_far
286                , sizeof(dop));
287
288     dprintf(DEBUG_HDL_13, "disk_op d=%d lba=%d buf=%p count=%d cmd=%d\n"
289             , dop.driveid, (u32)dop.lba, dop.buf_fl
290             , dop.count, dop.command);
291
292     irq_enable();
293
294     int status = process_op(&dop);
295
296     irq_disable();
297
298     // Update count with total sectors transferred.
299     SET_FARVAR(op_seg, op_far->count, dop.count);
300
301     return status;
302 }
303
304 // Execute a "disk_op_s" request by jumping to a stack in the ebda.
305 int
306 send_disk_op(struct disk_op_s *op)
307 {
308     if (! CONFIG_DRIVES)
309         return -1;
310     ASSERT16();
311
312     return stack_hop((u32)op, GET_SEG(SS), 0, __send_disk_op);
313 }
314
315
316 /****************************************************************
317  * Setup
318  ****************************************************************/
319
320 void
321 drive_setup()
322 {
323     memset(&Drives, 0, sizeof(Drives));
324     memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
325 }