Added remaining bus functions.
[people/xl0/gpxe.git] / src / arch / i386 / drivers / bus / bios_disks.c
1 #include "realmode.h"
2 #include "isa_ids.h"
3 #include "bios_disks.h"
4
5 #define CF ( 1 << 0 )
6 #define BIOS_DISK_NONE 0
7
8 /*
9  * Ensure that there is sufficient space in the shared dev_bus
10  * structure for a struct bios_disk_device.
11  *
12  */
13 DEV_BUS( struct bios_disk_device, bios_disk_dev );
14 static char bios_disk_magic[0]; /* guaranteed unique symbol */
15
16 /*
17  * Reset the disk system using INT 13,0.  Forces both hard disks and
18  * floppy disks to seek back to track 0.
19  *
20  */
21 void bios_disk_init ( void ) {
22         REAL_EXEC ( rm_bios_disk_init,
23                     "sti\n\t"
24                     "xorw %%ax,%%ax\n\t"
25                     "movb $0x80,%%dl\n\t"
26                     "int $0x13\n\t"
27                     "cli\n\t",
28                     0,
29                     OUT_CONSTRAINTS (),
30                     IN_CONSTRAINTS (),
31                     CLOBBER ( "eax", "ebx", "ecx", "edx",
32                               "ebp", "esi", "edi" ) );
33 }
34
35 /*
36  * Read a single sector from a disk using INT 13,2.
37  *
38  * Returns the BIOS status code (%ah) - 0 indicates success.
39  *
40  */
41 unsigned int bios_disk_read_once ( struct bios_disk_device *bios_disk,
42                                    unsigned int cylinder,
43                                    unsigned int head,
44                                    unsigned int sector,
45                                    struct bios_disk_sector *buf ) {
46         uint16_t basemem_buf, status, flags;
47         int discard_c, discard_d;
48
49         basemem_buf = BASEMEM_PARAMETER_INIT ( *buf );
50         REAL_EXEC ( rm_bios_disk_read,
51                     "sti\n\t"
52                     "movw $0x0201, %%ax\n\t" /* Read a single sector */
53                     "int $0x13\n\t"
54                     "pushfw\n\t"
55                     "popw %%bx\n\t"
56                     "cli\n\t",
57                     4,
58                     OUT_CONSTRAINTS ( "=a" ( status ), "=b" ( flags ),
59                                       "=c" ( discard_c ),
60                                       "=d" ( discard_d ) ),
61                     IN_CONSTRAINTS ( "c" ( ( ( cylinder & 0xff ) << 8 ) |
62                                            ( ( cylinder >> 8 ) & 0x3 ) |
63                                            sector ),
64                                      "d" ( ( head << 8 ) | bios_disk->drive ),
65                                      "b" ( basemem_buf ) ),
66                     CLOBBER ( "ebp", "esi", "edi" ) );
67         BASEMEM_PARAMETER_DONE ( *buf );
68
69         return ( flags & CF ) ? ( status >> 8 ) : 0; 
70 }
71
72 /*
73  * Fill in parameters for a BIOS disk device based on drive number
74  *
75  */
76 static int fill_bios_disk_device ( struct bios_disk_device *bios_disk ) {
77         uint16_t type, flags;
78        
79         REAL_EXEC ( rm_bios_disk_exists,
80                     "sti\n\t"
81                     "movb $0x15, %%ah\n\t"
82                     "int $0x13\n\t"
83                     "pushfw\n\t"
84                     "popw %%dx\n\t"
85                     "cli\n\t",
86                     2,
87                     OUT_CONSTRAINTS ( "=a" ( type ), "=d" ( flags ) ),
88                     IN_CONSTRAINTS ( "d" ( bios_disk->drive ) ),
89                     CLOBBER ( "ebx", "ecx", "esi", "edi", "ebp" ) );
90
91         if ( ( flags & CF ) ||
92              ( ( type >> 8 ) == BIOS_DISK_NONE ) )
93                 return 0;
94
95         DBG ( "BIOS disk found valid drive %hhx\n", bios_disk->drive );
96         return 1;
97 }
98
99 /*
100  * Find a BIOS disk device matching the specified driver
101  *
102  */
103 int find_bios_disk_device ( struct bios_disk_device *bios_disk,
104                             struct bios_disk_driver *driver ) {
105
106         /* Initialise struct bios_disk if it's the first time it's been used.
107          */
108         if ( bios_disk->magic != bios_disk_magic ) {
109                 memset ( bios_disk, 0, sizeof ( *bios_disk ) );
110                 bios_disk->magic = bios_disk_magic;
111         }
112
113         /* Iterate through all possible BIOS drives, starting where we
114          * left off
115          */
116         DBG ( "BIOS disk searching for device matching driver %s\n",
117               driver->name );
118         do {
119                 /* If we've already used this device, skip it */
120                 if ( bios_disk->already_tried ) {
121                         bios_disk->already_tried = 0;
122                         continue;
123                 }
124
125                 /* Fill in device parameters */
126                 if ( ! fill_bios_disk_device ( bios_disk ) ) {
127                         continue;
128                 }
129
130                 /* Compare against driver's valid ID range */
131                 if ( ( bios_disk->drive >= driver->min_drive ) &&
132                      ( bios_disk->drive <= driver->max_drive ) ) {
133                         driver->fill_drive_name ( bios_disk->drive,
134                                                   bios_disk->name );
135                         DBG ( "BIOS_DISK found drive %hhx (\"%s\") "
136                               "matching driver %s\n",
137                               bios_disk->drive, bios_disk->name,
138                               driver->name );
139                         bios_disk->already_tried = 1;
140                         return 1;
141                 }
142         } while ( ++bios_disk->drive );
143
144         /* No device found */
145         DBG ( "BIOS disk found no device matching driver %s\n", driver->name );
146         return 0;
147 }
148
149 /*
150  * Find the next MCA device that can be used to boot using the
151  * specified driver.
152  *
153  */
154 int find_bios_disk_boot_device ( struct dev *dev,
155                                  struct bios_disk_driver *driver ) {
156         struct bios_disk_device *bios_disk
157                 = ( struct bios_disk_device * ) dev->bus;
158
159         if ( ! find_bios_disk_device ( bios_disk, driver ) )
160                 return 0;
161
162         dev->name = bios_disk->name;
163         dev->devid.bus_type = ISA_BUS_TYPE;
164         dev->devid.vendor_id = ISA_VENDOR ( 'D', 'S', 'K' );
165         dev->devid.device_id = bios_disk->drive;
166
167         return 1;
168 }