aboutsummaryrefslogtreecommitdiffstats
path: root/memdisk/dskprobe.c
blob: 460bf647f9d28305e68c79919c1dbcefd13255a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/* ----------------------------------------------------------------------- *
 *
 *   Copyright 2009 Shao Miller - All Rights Reserved
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
 *   Boston MA 02111-1307, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/*
 * dskprobe.c
 *
 * Routines for probing BIOS disk drives
 */

/* Change to 1 for debugging */
#define DBG_DSKPROBE 0

#include <stdint.h>
#include "memdisk.h"
#include "bda.h"
#include "conio.h"

/* Function type for printf() */
typedef int (f_printf) (const char *, ...);

/* Dummy printf() that does nothing */
static f_printf no_printf;
static f_printf *dskprobe_printfs[] = { no_printf, printf };

#define dskprobe_printf (dskprobe_printfs[DBG_DSKPROBE])

static void dskprobe_pause(com32sys_t *);

/* Probe routine function type */
typedef int (f_probe) (uint8_t, com32sys_t *);
static f_probe probe_int13h_08h, probe_int13h_15h, probe_int13h_41h;

/* We will probe a BIOS drive number using INT 0x13, AH == func */
static void probe_any(uint8_t func, uint8_t drive, com32sys_t * regs)
{
    regs->eax.b[1] = func;	/* AH == sub-function for probe */
    regs->edx.b[0] = drive;	/* DL == drive number to probe */
    intcall(0x13, regs, regs);
    return;
}

/**
 * Determine if the return from probe_int13h_01h indicates a failure; a
 * return of zero indicates no known failure.
 */
static int probe_int13h_01h_fail(int istatus)
{
    int status = 0;

    if (istatus >= 256)
	status = istatus;
    else
	switch (istatus) {
	case 1: status = istatus;
	}
    return status;
}

/**
 * INT 0x13, AH == 0x01: Get status of last command.
 */
static int probe_int13h_01h(uint8_t drive)
{
    int status;
    com32sys_t regs;

    memset(&regs, 0, sizeof regs);
    probe_any(0x01, drive, &regs);
    status = (regs.eflags.l & 1) * 256 + regs.eax.b[1];
    dskprobe_printf("  AH01: CF%d AH%02x", regs.eflags.l & 1, regs.eax.b[1]);
    return status;
}

/**
 * INT 0x13, AH == 0x08: Get drive parameters.
 */
static int probe_int13h_08h(uint8_t drive, com32sys_t * regs)
{
    int present;
    int status;

    memset(regs, 0, sizeof *regs);
    probe_any(0x08, drive, regs);
    dskprobe_printf("  AH08: CF%d AH%02x AL%02x BL%02x DL%02x    ",
		    regs->eflags.l & 1, regs->eax.b[1], regs->eax.b[0],
		    regs->ebx.b[0], regs->edx.b[0]);
    present = !(regs->eflags.l & 1) && !regs->eax.b[1];
    status = probe_int13h_01h(drive);
    present = present && !(probe_int13h_01h_fail(status));
    dskprobe_printf("  P%d\n",  present);
    return present;
}

/**
 * INT 0x13, AH == 0x15: Get disk type.
 */
static int probe_int13h_15h(uint8_t drive, com32sys_t * regs)
{
    int present;
    int status;

    memset(regs, 0, sizeof *regs);
    probe_any(0x15, drive, regs);
    dskprobe_printf("  AH15: CF%d AH%02x AL%02x CX%04x DX%04x",
		    regs->eflags.l & 1, regs->eax.b[1], regs->eax.b[0],
		    regs->ecx.w[0], regs->edx.w[0]);
    present = !(regs->eflags.l & 1) && regs->eax.b[1];
    status = probe_int13h_01h(drive);
    present = present && !(probe_int13h_01h_fail(status));
    dskprobe_printf("  P%d\n",  present);
    return present;
}

/**
 * INT 0x13, AH == 0x41: INT 0x13 extensions installation check.
 */
static int probe_int13h_41h(uint8_t drive, com32sys_t * regs)
{
    int present;
    int status;

    memset(regs, 0, sizeof *regs);
    regs->ebx.w[0] = 0x55AA;	/* BX == 0x55AA */
    probe_any(0x41, drive, regs);
    dskprobe_printf("  AH41: CF%d AH%02x BX%04x CX%04x DH%02x",
		    regs->eflags.l & 1, regs->eax.b[1], regs->ebx.w[0],
		    regs->ecx.w[0], regs->edx.b[1]);
    present = !(regs->eflags.l & 1) && (regs->ebx.w[0] == 0xAA55);
    status = probe_int13h_01h(drive);
    present = present && !(probe_int13h_01h_fail(status));
    dskprobe_printf("  P%d\n",  present);
    return present;
}

/*
 * We will probe the BIOS Data Area and count the drives found there.
 * This heuristic then assumes that all drives of 'drive's type are
 * found in a contiguous range, and returns 1 if the probed drive
 * is less than or equal to the BDA count.
 * This particular function's code is derived from code in setup.c by
 * H. Peter Anvin.  Please respect that file's copyright for this function
 */
int probe_bda_drive(uint8_t drive)
{
    int bios_drives;
    int err;

    if (drive & 0x80) {
	bios_drives = rdz_8(BIOS_HD_COUNT);	/* HDD count */
    } else {
	uint8_t equip = rdz_8(BIOS_EQUIP);
	if (equip & 1)
	    bios_drives = (equip >> 6) + 1;	/* Floppy count */
	else
	    bios_drives = 0;
    }
    err = (drive - (drive & 0x80)) >= bios_drives ? 0 : 1;
    dskprobe_printf("BDA drive %02x? %d, total count: %d\n", drive, err,
		    bios_drives);
    return err;
}

/*
 * We will probe a drive with a few different methods, returning
 * the count of succesful probes
 */
int multi_probe_drive(uint8_t drive)
{
    int c = 0;
    com32sys_t regs;

    dskprobe_printf("INT 13 DL%02x:\n", drive);
    /* Only probe the BDA for floppies */
    if (drive & 0x80) {

	c += probe_int13h_08h(drive, &regs);
	c += probe_int13h_15h(drive, &regs);
	c += probe_int13h_41h(drive, &regs);
    }
    c += probe_bda_drive(drive);
    dskprobe_pause(&regs);
    return c;
}

/*
 * We will probe a contiguous range of BIOS drive, starting with drive
 * number 'start'.  We probe with a few different methods, and return
 * the first drive which doesn't respond to any of the probes.
 */
uint8_t probe_drive_range(uint8_t start)
{
    uint8_t drive = start;
    while (multi_probe_drive(drive)) {
	drive++;
	/* Check for passing the floppy/HDD boundary */
	if ((drive & 0x7F) == 0)
	    break;
    }
    return drive;
}

/* Dummy printf() that does nothing */
static int no_printf(const char *ignored, ...)
{
    (void)ignored;
    return 0;
}

/* Pause if we are in debug-mode */
static void dskprobe_pause(com32sys_t * regs)
{
    if (!DBG_DSKPROBE)
	return;
    dskprobe_printf("Press a key to continue...\n");
    memset(regs, 0, sizeof *regs);
    regs->eax.w[0] = 0;
    intcall(0x16, regs, NULL);
    return;
}