Separate ATA code from generic disk code.
[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
13 struct drives_s Drives VAR16_32;
14
15
16 /****************************************************************
17  * Disk geometry translation
18  ****************************************************************/
19
20 static u8
21 get_translation(int driveid)
22 {
23     u8 type = GET_GLOBAL(Drives.drives[driveid].type);
24     if (! CONFIG_COREBOOT && type == DTYPE_ATA) {
25         // Emulators pass in the translation info via nvram.
26         u8 ataid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
27         u8 channel = ataid / 2;
28         u8 translation = inb_cmos(CMOS_BIOS_DISKTRANSFLAG + channel/2);
29         translation >>= 2 * (ataid % 4);
30         translation &= 0x03;
31         return translation;
32     }
33
34     // On COREBOOT, use a heuristic to determine translation type.
35     u16 heads = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
36     u16 cylinders = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
37     u16 spt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
38
39     if (cylinders <= 1024 && heads <= 16 && spt <= 63)
40         return TRANSLATION_NONE;
41     if (cylinders * heads <= 131072)
42         return TRANSLATION_LARGE;
43     return TRANSLATION_LBA;
44 }
45
46 void
47 setup_translation(int driveid)
48 {
49     u8 translation = get_translation(driveid);
50     SET_GLOBAL(Drives.drives[driveid].translation, translation);
51
52     u8 ataid = GET_GLOBAL(Drives.drives[driveid].cntl_id);
53     u8 channel = ataid / 2;
54     u8 slave = ataid % 2;
55     u16 heads = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
56     u16 cylinders = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
57     u16 spt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
58     u64 sectors = GET_GLOBAL(Drives.drives[driveid].sectors);
59
60     dprintf(1, "ata%d-%d: PCHS=%u/%d/%d translation="
61             , channel, slave, cylinders, heads, spt);
62     switch (translation) {
63     case TRANSLATION_NONE:
64         dprintf(1, "none");
65         break;
66     case TRANSLATION_LBA:
67         dprintf(1, "lba");
68         spt = 63;
69         if (sectors > 63*255*1024) {
70             heads = 255;
71             cylinders = 1024;
72             break;
73         }
74         u32 sect = (u32)sectors / 63;
75         heads = sect / 1024;
76         if (heads>128)
77             heads = 255;
78         else if (heads>64)
79             heads = 128;
80         else if (heads>32)
81             heads = 64;
82         else if (heads>16)
83             heads = 32;
84         else
85             heads = 16;
86         cylinders = sect / heads;
87         break;
88     case TRANSLATION_RECHS:
89         dprintf(1, "r-echs");
90         // Take care not to overflow
91         if (heads==16) {
92             if (cylinders>61439)
93                 cylinders=61439;
94             heads=15;
95             cylinders = (u16)((u32)(cylinders)*16/15);
96         }
97         // then go through the large bitshift process
98     case TRANSLATION_LARGE:
99         if (translation == TRANSLATION_LARGE)
100             dprintf(1, "large");
101         while (cylinders > 1024) {
102             cylinders >>= 1;
103             heads <<= 1;
104
105             // If we max out the head count
106             if (heads > 127)
107                 break;
108         }
109         break;
110     }
111     // clip to 1024 cylinders in lchs
112     if (cylinders > 1024)
113         cylinders = 1024;
114     dprintf(1, " LCHS=%d/%d/%d\n", cylinders, heads, spt);
115
116     SET_GLOBAL(Drives.drives[driveid].lchs.heads, heads);
117     SET_GLOBAL(Drives.drives[driveid].lchs.cylinders, cylinders);
118     SET_GLOBAL(Drives.drives[driveid].lchs.spt, spt);
119 }
120
121
122 /****************************************************************
123  * Drive mapping
124  ****************************************************************/
125
126 // Fill in Fixed Disk Parameter Table (located in ebda).
127 static void
128 fill_fdpt(int driveid)
129 {
130     if (driveid > 1)
131         return;
132
133     u16 nlc   = GET_GLOBAL(Drives.drives[driveid].lchs.cylinders);
134     u16 nlh   = GET_GLOBAL(Drives.drives[driveid].lchs.heads);
135     u16 nlspt = GET_GLOBAL(Drives.drives[driveid].lchs.spt);
136
137     u16 npc   = GET_GLOBAL(Drives.drives[driveid].pchs.cylinders);
138     u16 nph   = GET_GLOBAL(Drives.drives[driveid].pchs.heads);
139     u16 npspt = GET_GLOBAL(Drives.drives[driveid].pchs.spt);
140
141     struct fdpt_s *fdpt = &get_ebda_ptr()->fdpt[driveid];
142     fdpt->precompensation = 0xffff;
143     fdpt->drive_control_byte = 0xc0 | ((nph > 8) << 3);
144     fdpt->landing_zone = npc;
145     fdpt->cylinders = nlc;
146     fdpt->heads = nlh;
147     fdpt->sectors = nlspt;
148
149     if (nlc == npc && nlh == nph && nlspt == npspt)
150         // no logical CHS mapping used, just physical CHS
151         // use Standard Fixed Disk Parameter Table (FDPT)
152         return;
153
154     // complies with Phoenix style Translated Fixed Disk Parameter
155     // Table (FDPT)
156     fdpt->phys_cylinders = npc;
157     fdpt->phys_heads = nph;
158     fdpt->phys_sectors = npspt;
159     fdpt->a0h_signature = 0xa0;
160
161     // Checksum structure.
162     fdpt->checksum -= checksum(fdpt, sizeof(*fdpt));
163
164     if (driveid == 0)
165         SET_IVT(0x41, get_ebda_seg()
166                 , offsetof(struct extended_bios_data_area_s, fdpt[0]));
167     else
168         SET_IVT(0x46, get_ebda_seg()
169                 , offsetof(struct extended_bios_data_area_s, fdpt[1]));
170 }
171
172 // Map a drive (that was registered via add_bcv_hd)
173 void
174 map_hd_drive(int driveid)
175 {
176     // fill hdidmap
177     u8 hdcount = GET_BDA(hdcount);
178     if (hdcount >= ARRAY_SIZE(Drives.idmap[0]))
179         return;
180     dprintf(3, "Mapping hd driveid %d to %d\n", driveid, hdcount);
181     SET_GLOBAL(Drives.idmap[0][hdcount], driveid);
182     SET_BDA(hdcount, hdcount + 1);
183
184     // Fill "fdpt" structure.
185     fill_fdpt(hdcount);
186 }
187
188 // Map a cd
189 void
190 map_cd_drive(int driveid)
191 {
192     // fill cdidmap
193     u8 cdcount = GET_GLOBAL(Drives.cdcount);
194     if (cdcount >= ARRAY_SIZE(Drives.idmap[1]))
195         return;
196     dprintf(3, "Mapping cd driveid %d to %d\n", driveid, cdcount);
197     SET_GLOBAL(Drives.idmap[1][cdcount], driveid);
198     SET_GLOBAL(Drives.cdcount, cdcount+1);
199 }
200
201
202 /****************************************************************
203  * Setup
204  ****************************************************************/
205
206 void
207 drive_setup()
208 {
209     memset(&Drives, 0, sizeof(Drives));
210     memset(&Drives.idmap, 0xff, sizeof(Drives.idmap));
211 }