~ubuntu-branches/ubuntu/utopic/grub/utopic

« back to all changes in this revision

Viewing changes to stage2/bios.c

  • Committer: Bazaar Package Importer
  • Author(s): Jason Thomas
  • Date: 2002-02-04 15:35:01 UTC
  • Revision ID: james.westby@ubuntu.com-20020204153501-neulkag77r3a2m0v
Tags: upstream-0.91
ImportĀ upstreamĀ versionĀ 0.91

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* bios.c - implement C part of low-level BIOS disk input and output */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 1999,2000  Free Software Foundation, Inc.
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 */
 
20
 
 
21
#include "shared.h"
 
22
 
 
23
 
 
24
/* These are defined in asm.S, and never be used elsewhere, so declare the
 
25
   prototypes here.  */
 
26
extern int biosdisk_int13_extensions (int ah, int drive, void *dap);
 
27
extern int biosdisk_standard (int ah, int drive,
 
28
                              int coff, int hoff, int soff,
 
29
                              int nsec, int segment);
 
30
extern int check_int13_extensions (int drive);
 
31
extern int get_diskinfo_int13_extensions (int drive, void *drp);
 
32
extern int get_diskinfo_standard (int drive,
 
33
                                  unsigned long *cylinders,
 
34
                                  unsigned long *heads,
 
35
                                  unsigned long *sectors);
 
36
#if 0
 
37
extern int get_diskinfo_floppy (int drive,
 
38
                                unsigned long *cylinders,
 
39
                                unsigned long *heads,
 
40
                                unsigned long *sectors);
 
41
#endif
 
42
 
 
43
 
 
44
/* Read/write NSEC sectors starting from SECTOR in DRIVE disk with GEOMETRY
 
45
   from/into SEGMENT segment. If READ is BIOSDISK_READ, then read it,
 
46
   else if READ is BIOSDISK_WRITE, then write it. If an geometry error
 
47
   occurs, return BIOSDISK_ERROR_GEOMETRY, and if other error occurs, then
 
48
   return the error number. Otherwise, return 0.  */
 
49
int
 
50
biosdisk (int read, int drive, struct geometry *geometry,
 
51
          int sector, int nsec, int segment)
 
52
{
 
53
  int err;
 
54
  
 
55
  if (geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION)
 
56
    {
 
57
      struct disk_address_packet
 
58
      {
 
59
        unsigned char length;
 
60
        unsigned char reserved;
 
61
        unsigned short blocks;
 
62
        unsigned long buffer;
 
63
        unsigned long long block;
 
64
      } dap;
 
65
 
 
66
      /* XXX: Don't check the geometry by default, because some buggy
 
67
         BIOSes don't return the number of total sectors correctly,
 
68
         even if they have working LBA support. Hell.  */
 
69
#ifdef NO_BUGGY_BIOS_IN_THE_WORLD
 
70
      if (sector >= geometry->total_sectors)
 
71
        return BIOSDISK_ERROR_GEOMETRY;
 
72
#endif /* NO_BUGGY_BIOS_IN_THE_WORLD */
 
73
 
 
74
      /* FIXME: sizeof (DAP) must be 0x10. Should assert that the compiler
 
75
         can't add any padding.  */
 
76
      dap.length = sizeof (dap);
 
77
      dap.block = sector;
 
78
      dap.blocks = nsec;
 
79
      dap.reserved = 0;
 
80
      /* This is undocumented part. The address is formated in
 
81
         SEGMENT:ADDRESS.  */
 
82
      dap.buffer = segment << 16;
 
83
      
 
84
      err = biosdisk_int13_extensions (read + 0x42, drive, &dap);
 
85
 
 
86
/* #undef NO_INT13_FALLBACK */
 
87
#ifndef NO_INT13_FALLBACK
 
88
      if (err)
 
89
        {
 
90
          geometry->flags &= ~BIOSDISK_FLAG_LBA_EXTENSION;
 
91
          geometry->total_sectors = (geometry->cylinders
 
92
                                     * geometry->heads
 
93
                                     * geometry->sectors);
 
94
          return biosdisk (read, drive, geometry, sector, nsec, segment);
 
95
        }
 
96
#endif /* ! NO_INT13_FALLBACK */
 
97
      
 
98
    }
 
99
  else
 
100
    {
 
101
      int cylinder_offset, head_offset, sector_offset;
 
102
      int head;
 
103
 
 
104
      /* SECTOR_OFFSET is counted from one, while HEAD_OFFSET and
 
105
         CYLINDER_OFFSET are counted from zero.  */
 
106
      sector_offset = sector % geometry->sectors + 1;
 
107
      head = sector / geometry->sectors;
 
108
      head_offset = head % geometry->heads;
 
109
      cylinder_offset = head / geometry->heads;
 
110
      
 
111
      if (cylinder_offset >= geometry->cylinders)
 
112
        return BIOSDISK_ERROR_GEOMETRY;
 
113
 
 
114
      err = biosdisk_standard (read + 0x02, drive,
 
115
                               cylinder_offset, head_offset, sector_offset,
 
116
                               nsec, segment);
 
117
    }
 
118
 
 
119
  return err;
 
120
}
 
121
 
 
122
/* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return
 
123
   non-zero, otherwise zero.  */
 
124
int
 
125
get_diskinfo (int drive, struct geometry *geometry)
 
126
{
 
127
  int err;
 
128
 
 
129
  /* Clear the flags.  */
 
130
  geometry->flags = 0;
 
131
  
 
132
  if (drive & 0x80)
 
133
    {
 
134
      /* hard disk */
 
135
      int version;
 
136
      unsigned long total_sectors = 0;
 
137
      
 
138
      version = check_int13_extensions (drive);
 
139
      if (version)
 
140
        {
 
141
          struct drive_parameters
 
142
          {
 
143
            unsigned short size;
 
144
            unsigned short flags;
 
145
            unsigned long cylinders;
 
146
            unsigned long heads;
 
147
            unsigned long sectors;
 
148
            unsigned long long total_sectors;
 
149
            unsigned short bytes_per_sector;
 
150
            /* ver 2.0 or higher */
 
151
            unsigned long EDD_configuration_parameters;
 
152
            /* ver 3.0 or higher */
 
153
            unsigned short signature_dpi;
 
154
            unsigned char length_dpi;
 
155
            unsigned char reserved[3];
 
156
            unsigned char name_of_host_bus[4];
 
157
            unsigned char name_of_interface_type[8];
 
158
            unsigned char interface_path[8];
 
159
            unsigned char device_path[8];
 
160
            unsigned char reserved2;
 
161
            unsigned char checksum;
 
162
 
 
163
            /* XXX: This is necessary, because the BIOS of Thinkpad X20
 
164
               writes a garbage to the tail of drive parameters,
 
165
               regardless of a size specified in a caller.  */
 
166
            unsigned char dummy[16];
 
167
          } __attribute__ ((packed)) drp;
 
168
 
 
169
          /* It is safe to clear out DRP.  */
 
170
          grub_memset (&drp, 0, sizeof (drp));
 
171
          
 
172
          drp.size = sizeof (drp);
 
173
          err = get_diskinfo_int13_extensions (drive, &drp);
 
174
          if (! err)
 
175
            {
 
176
              /* Set the LBA flag.  */
 
177
              geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
 
178
 
 
179
              /* I'm not sure if GRUB should check the bit 1 of DRP.FLAGS,
 
180
                 so I omit the check for now. - okuji  */
 
181
              /* if (drp.flags & (1 << 1)) */
 
182
               
 
183
              /* FIXME: when the 2TB limit becomes critical, we must
 
184
                 change the type of TOTAL_SECTORS to unsigned long
 
185
                 long.  */
 
186
              if (drp.total_sectors)
 
187
                total_sectors = drp.total_sectors & ~0L;
 
188
              else
 
189
                /* Some buggy BIOSes doesn't return the total sectors
 
190
                   correctly but returns zero. So if it is zero, compute
 
191
                   it by C/H/S returned by the LBA BIOS call.  */
 
192
                total_sectors = drp.cylinders * drp.heads * drp.sectors;
 
193
            }
 
194
        }
 
195
 
 
196
      /* Don't pass GEOMETRY directly, but pass each element instead,
 
197
         so that we can change the structure easily.  */
 
198
      err = get_diskinfo_standard (drive,
 
199
                                   &geometry->cylinders,
 
200
                                   &geometry->heads,
 
201
                                   &geometry->sectors);
 
202
      if (err)
 
203
        return err;
 
204
 
 
205
      if (! total_sectors)
 
206
        {
 
207
          total_sectors = (geometry->cylinders
 
208
                           * geometry->heads
 
209
                           * geometry->sectors);
 
210
        }
 
211
      geometry->total_sectors = total_sectors;
 
212
    }
 
213
  else
 
214
    {
 
215
      /* floppy disk */
 
216
 
 
217
      /* First, try INT 13 AH=8h call.  */
 
218
      err = get_diskinfo_standard (drive,
 
219
                                   &geometry->cylinders,
 
220
                                   &geometry->heads,
 
221
                                   &geometry->sectors);
 
222
 
 
223
#if 0
 
224
      /* If fails, then try floppy-specific probe routine.  */
 
225
      if (err)
 
226
        err = get_diskinfo_floppy (drive,
 
227
                                   &geometry->cylinders,
 
228
                                   &geometry->heads,
 
229
                                   &geometry->sectors);
 
230
#endif
 
231
      
 
232
      if (err)
 
233
        return err;
 
234
 
 
235
      geometry->total_sectors = (geometry->cylinders
 
236
                                 * geometry->heads
 
237
                                 * geometry->sectors);
 
238
    }
 
239
 
 
240
  return 0;
 
241
}