1 /* ----------------------------------------------------------------------- *
3 * Copyright 2009 Pierre-Alexandre Meyer
5 * Some parts borrowed from chain.c32:
7 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
8 * Copyright 2009 Intel Corporation; author: H. Peter Anvin
10 * This file is part of Syslinux, and is made available under
11 * the terms of the GNU General Public License version 2.
13 * ----------------------------------------------------------------------- */
18 #include <disk/geom.h>
23 * lba_to_chs - split given lba into cylinders/heads/sectors
25 void lba_to_chs(const struct driveinfo
*drive_info
, const int lba
,
26 unsigned int *cylinder
, unsigned int *head
,
31 /* Use EDD, if valid */
32 if (drive_info
->edd_params
.sectors_per_track
> 0 &&
33 drive_info
->edd_params
.heads
> 0) {
34 *cylinder
= (lba
% drive_info
->edd_params
.sectors_per_track
) + 1;
35 track
= lba
/ drive_info
->edd_params
.sectors_per_track
;
36 *head
= track
% drive_info
->edd_params
.heads
;
37 *sector
= track
/ drive_info
->edd_params
.heads
;
38 } else if (drive_info
->cbios
) {
39 *cylinder
= (lba
% drive_info
->legacy_sectors_per_track
) + 1;
40 track
= lba
/ drive_info
->legacy_sectors_per_track
;
41 *head
= track
% (drive_info
->legacy_max_head
+ 1);
42 *sector
= track
/ (drive_info
->legacy_max_head
+ 1);
47 * detect_extensions - detect if we can use extensions
49 * INT 13 - IBM/MS INT 13 Extensions - INSTALLATION CHECK
52 * DL = drive (80h-FFh)
54 * Return: CF set on error (extensions not supported)
55 * AH = 01h (invalid function)
56 * CF clear if successful
57 * BX = AA55h if installed
58 * AH = major version of extensions
64 * CX = API subset support bitmap (see #00271)
65 * DH = extension version (v2.0+ ??? -- not present in 1.x)
67 * Note: the Phoenix Enhanced Disk Drive Specification v1.0 uses version 2.0 of
68 * the INT 13 Extensions API
70 * Bitfields for IBM/MS INT 13 Extensions API support bitmap:
71 * Bit(s) Description (Table 00271)
72 * 0 extended disk access functions (AH=42h-44h,47h,48h) supported
73 * 1 removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h)
75 * 2 enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported
76 * extended drive parameter table is valid (see #00273,#00278)
79 static int detect_extensions(struct driveinfo
*drive_info
)
81 com32sys_t getebios
, ebios
;
83 memset(&getebios
, 0, sizeof getebios
);
84 memset(&ebios
, 0, sizeof ebios
);
86 getebios
.eflags
.b
[0] = 0x3; /* CF set */
87 getebios
.ebx
.w
[0] = 0x55aa;
88 getebios
.edx
.b
[0] = drive_info
->disk
;
89 getebios
.eax
.b
[1] = 0x41;
91 __intcall(0x13, &getebios
, &ebios
);
93 if (!(ebios
.eflags
.l
& EFLAGS_CF
) && ebios
.ebx
.w
[0] == 0xaa55) {
94 drive_info
->ebios
= 1;
95 drive_info
->edd_version
= ebios
.eax
.b
[1];
96 drive_info
->edd_functionality_subset
= ebios
.ecx
.w
[0];
99 return -1; /* Drive does not exist? */
103 * get_drive_parameters_with_extensions - retrieve disk parameters via AH=48h
105 * INT 13 - IBM/MS INT 13 Extensions - GET DRIVE PARAMETERS
107 * DL = drive (80h-FFh)
108 * DS:SI -> buffer for drive parameters
109 * Return: CF clear if successful
111 * DS:SI buffer filled
113 * AH = error code (see #00234)
114 * BUG: several different Compaq BIOSes incorrectly report high-numbered
115 * drives (such as 90h, B0h, D0h, and F0h) as present, giving them the
116 * same geometry as drive 80h; as a workaround, scan through disk
117 * numbers, stopping as soon as the number of valid drives encountered
118 * equals the value in 0040h:0075h
120 static int get_drive_parameters_with_extensions(struct driveinfo
*drive_info
)
122 com32sys_t inreg
, outreg
;
123 struct edd_device_parameters
*dp
= __com32
.cs_bounce
;
125 memset(&inreg
, 0, sizeof inreg
);
128 * The caller shall set this value to the maximum Result Buffer
129 * length, in bytes. If the length of this buffer is less than 30
130 * bytes, this function shall not return the pointer to Drive Parameter
131 * Table (DPT) extension. If the buffer length is 30 or greater on
132 * entry, it shall be set to 30 on exit. If the buffer length is
133 * between 26 and 29, it shall be set to 26 on exit.
134 * If the buffer length is less than 26 on entry an error shall be
137 dp
->len
= sizeof(struct edd_device_parameters
);
139 inreg
.esi
.w
[0] = OFFS(dp
);
141 inreg
.edx
.b
[0] = drive_info
->disk
;
142 inreg
.eax
.b
[1] = 0x48;
144 __intcall(0x13, &inreg
, &outreg
);
146 /* CF set on error */
147 if (outreg
.eflags
.l
& EFLAGS_CF
)
148 return outreg
.eax
.b
[1];
150 memcpy(&drive_info
->edd_params
, dp
, sizeof drive_info
->edd_params
);
156 * get_drive_parameters_without_extensions - retrieve drive parameters via AH=08h
158 * INT 13 - DISK - GET DRIVE PARAMETERS (PC,XT286,CONV,PS,ESDI,SCSI)
160 * DL = drive (bit 7 set for hard disk)
162 * Return: CF set on error
163 * AH = status (07h) (see #00234)
164 * CF clear if successful
166 * AL = 00h on at least some BIOSes
167 * BL = drive type (AT/PS2 floppies only) (see #00242)
168 * CH = low eight bits of maximum cylinder number
169 * CL = maximum sector number (bits 5-0)
170 * high two bits of maximum cylinder number (bits 7-6)
171 * DH = maximum head number
172 * DL = number of drives
173 * ES:DI -> drive parameter table (floppies only)
176 * - may return successful even though specified drive is greater than the
177 * number of attached drives of that type (floppy/hard); check DL to
179 * - for systems predating the IBM AT, this call is only valid for hard
180 * disks, as it is implemented by the hard disk BIOS rather than the
182 * - Toshiba laptops with HardRAM return DL=02h when called with DL=80h,
183 * but fail on DL=81h. The BIOS data at 40h:75h correctly reports 01h.
184 * may indicate only two drives present even if more are attached; to
185 * ensure a correct count, one can use AH=15h to scan through possible
187 * - for BIOSes which reserve the last cylinder for testing purposes, the
188 * cylinder count is automatically decremented
189 * on PS/1s with IBM ROM DOS 4, nonexistent drives return CF clear,
190 * BX=CX=0000h, and ES:DI = 0000h:0000h
191 * - the PC-Tools PCFORMAT program requires that AL=00h before it will
192 * proceed with the formatting
194 * BUG: several different Compaq BIOSes incorrectly report high-numbered
195 * drives (such as 90h, B0h, D0h, and F0h) as present, giving them the
196 * same geometry as drive 80h; as a workaround, scan through disk
197 * numbers, stopping as soon as the number of valid drives encountered
198 * equals the value in 0040h:0075h
200 * SeeAlso: AH=06h"Adaptec",AH=13h"SyQuest",AH=48h,AH=15h,INT 1E
201 * SeeAlso: INT 41"HARD DISK 0"
203 static int get_drive_parameters_without_extensions(struct driveinfo
*drive_info
)
205 com32sys_t getparm
, parm
;
207 memset(&getparm
, 0, sizeof getparm
);
208 memset(&parm
, 0, sizeof parm
);
210 /* Ralf Brown recommends setting ES:DI to 0:0 */
211 getparm
.esi
.w
[0] = 0;
213 getparm
.edx
.b
[0] = drive_info
->disk
;
214 getparm
.eax
.b
[1] = 0x08;
216 __intcall(0x13, &getparm
, &parm
);
218 /* CF set on error */
219 if (parm
.eflags
.l
& EFLAGS_CF
)
220 return parm
.eax
.b
[1];
222 /* DL contains the maximum drive number (it starts at 0) */
223 drive_info
->legacy_max_drive
= parm
.edx
.b
[0];
226 /* Drive specified greater than the bumber of attached drives */
227 //if (drive_info->disk > drive_info->drives)
230 drive_info
->legacy_type
= parm
.ebx
.b
[0];
232 /* DH contains the maximum head number (it starts at 0) */
233 drive_info
->legacy_max_head
= parm
.edx
.b
[1];
235 /* Maximum sector number (bits 5-0) per track */
236 drive_info
->legacy_sectors_per_track
= parm
.ecx
.b
[0] & 0x3f;
239 * Maximum cylinder number:
240 * CH = low eight bits of maximum cylinder number
241 * CL = high two bits of maximum cylinder number (bits 7-6)
243 drive_info
->legacy_max_cylinder
= parm
.ecx
.b
[1] +
244 ((parm
.ecx
.b
[0] & 0xc0) << 2);
246 if (drive_info
->legacy_sectors_per_track
> 0)
247 drive_info
->cbios
= 1; /* Valid geometry */
253 * get_drive_parameters - retrieve drive parameters
254 * @drive_info: driveinfo structure to fill
256 int get_drive_parameters(struct driveinfo
*drive_info
)
260 if (detect_extensions(drive_info
))
263 return_code
= get_drive_parameters_without_extensions(drive_info
);
265 /* If geometry isn't valid, no need to try to get more info about the drive */
266 /* Looks like in can confuse some optical drives */
267 if (drive_info
->ebios
&& drive_info
->cbios
)
268 get_drive_parameters_with_extensions(drive_info
);