Imported Debian patch 0.5.3-1
[debian/efibootmgr] / src / lib / disk.c
1 /*
2   disk.[ch]
3  
4   Copyright (C) 2001 Dell Computer Corporation <Matt_Domsch@dell.com>
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdint.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include "disk.h"
30 #include "scsi_ioctls.h"
31 #include "gpt.h"
32 #include "efibootmgr.h"
33
34 #define BLKSSZGET  _IO(0x12,104)        /* get block device sector size */
35
36 int
37 disk_info_from_fd(int fd, 
38                   int *interface_type,
39                   unsigned int *controllernum, 
40                   unsigned int *disknum,
41                   unsigned char *part)
42 {
43         struct stat buf;
44         int rc;
45         uint64_t major;
46         unsigned char minor;
47         memset(&buf, 0, sizeof(struct stat));
48         rc = fstat(fd, &buf);
49         if (rc == -1) {
50                 perror("stat");
51                 return 1;
52         }
53         if (!(S_ISBLK(buf.st_mode) || S_ISREG(buf.st_mode))) {
54                 printf("Cannot stat non-block or non-regular file\n");
55                 return 1;
56         }
57         major = buf.st_dev >> 8;
58         minor = buf.st_dev && 0xFF;
59
60         /* IDE disks can have up to 64 partitions, or 6 bits worth,
61          * and have one bit for the disk number.
62          * This leaves an extra bit at the top.
63          */
64         if (major == 3) {
65                 *disknum = (minor >> 6) & 1;
66                 *controllernum = (major - 3 + 0) + *disknum;
67                 *interface_type = ata;
68                 *part    = minor & 0x3F;
69                 return 0;
70         }
71         else if (major == 22) {
72                 *disknum = (minor >> 6) & 1;
73                 *controllernum = (major - 22 + 2) + *disknum;
74                 *interface_type = ata;
75                 *part    = minor & 0x3F;
76                 return 0;
77         }
78         else if (major >= 33 && major <= 34) {
79                 *disknum = (minor >> 6) & 1;
80                 *controllernum = (major - 33 + 4) + *disknum;
81                 *interface_type = ata;
82                 *part    = minor & 0x3F;
83                 return 0;
84         }
85         else if (major >= 56 && major <= 57) {
86                 *disknum = (minor >> 6) & 1;
87                 *controllernum = (major - 56 + 8) + *disknum;
88                 *interface_type = ata;
89                 *part    = minor & 0x3F;
90                 return 0;
91         }
92         else if (major >= 88 && major <= 91) {
93                 *disknum = (minor >> 6) & 1;
94                 *controllernum = (major - 88 + 12) + *disknum;
95                 *interface_type = ata;
96                 *part    = minor & 0x3F;
97                 return 0;
98         }
99         
100         /* I2O disks can have up to 16 partitions, or 4 bits worth. */
101         if (major >= 80 && major <= 87) {
102                 *interface_type = i2o;
103                 *disknum = 16*(major-80) + (minor >> 4);
104                 *part    = (minor & 0xF);
105                 return 0;
106         }
107
108         /* SCSI disks can have up to 16 partitions, or 4 bits worth
109          * and have one bit for the disk number.
110          */
111         if (major == 8) {
112                 *interface_type = scsi;
113                 *disknum = (minor >> 4);
114                 *part    = (minor & 0xF);
115                 return 0;
116         }
117         else  if ( major >= 65 && major <= 71) {
118                 *interface_type = scsi;
119                 *disknum = 16*(major-64) + (minor >> 4);
120                 *part    = (minor & 0xF);
121                 return 0;
122         }
123             
124         printf("Unknown interface type.\n");
125         return 1;
126 }
127
128 static int
129 disk_get_scsi_pci(int fd, 
130              unsigned char *bus,
131              unsigned char *device,
132              unsigned char *function)
133 {
134         int rc, usefd=fd;
135         struct stat buf;
136         char slot_name[8];
137         unsigned int b=0,d=0,f=0;
138         memset(&buf, 0, sizeof(buf));
139         rc = fstat(fd, &buf);
140         if (rc == -1) {
141                 perror("stat");
142                 return 1;
143         }
144         if (S_ISREG(buf.st_mode)) {
145                 /* can't call ioctl() on this file and have it succeed.  
146                  * instead, need to open the block device
147                  * from /dev/.
148                  */
149                 fprintf(stderr, "You must call this program with "
150                         "a file name such as /dev/sda.\n");
151                 return 1;
152         }
153
154         rc = get_scsi_pci(usefd, slot_name);
155         if (rc) {
156                 perror("get_scsi_pci");
157                 return rc;
158         }
159         rc = sscanf(slot_name, "%x:%x.%x", &b,&d,&f);
160         if (rc != 3) {
161                 printf("sscanf failed\n");
162                 return 1;
163         }
164         *bus      = b & 0xFF;
165         *device   = d & 0xFF;
166         *function = f & 0xFF;
167         return 0;
168 }
169
170 /*
171  * The PCI interface treats multi-function devices as independent
172  * devices.  The slot/function address of each device is encoded
173  * in a single byte as follows:
174  *
175  *      7:3 = slot
176  *      2:0 = function
177  *
178  *  pci bus 00 device 39 vid 8086 did 7111 channel 1
179  *               00:07.1
180  */
181 #define PCI_DEVFN(slot,func)    ((((slot) & 0x1f) << 3) | ((func) & 0x07))
182 #define PCI_SLOT(devfn)         (((devfn) >> 3) & 0x1f)
183 #define PCI_FUNC(devfn)         ((devfn) & 0x07)
184
185 static int
186 disk_get_ide_pci(int fd,
187              unsigned char *bus,
188              unsigned char *device,
189              unsigned char *function)
190 {
191         int num_scanned, procfd;
192         unsigned int b=0,d=0,disknum=0, controllernum=0;
193         unsigned char part=0;
194         char procname[80], infoline[80];
195         size_t read_count;
196         int interface_type;
197         int rc;
198         
199         rc = disk_info_from_fd(fd, &interface_type, &controllernum,
200                                &disknum, &part);
201         if (rc) return rc;
202
203
204         sprintf(procname, "/proc/ide/ide%d/config", controllernum);
205         
206         procfd = open(procname, O_RDONLY);
207         if (!procfd) {
208                 perror("opening /proc/ide/ide*/config");
209                 return 1;
210         }
211         read_count = read(procfd, infoline, sizeof(infoline)-1);
212         close(procfd);
213         
214         num_scanned = sscanf(infoline,
215                              "pci bus %x device %x vid %*x did %*x channel %*x",
216                              &b, &d);
217         
218         if (num_scanned == 2) {
219                 *bus      = b;
220                 *device   = PCI_SLOT(d);
221                 *function = PCI_FUNC(d);
222         }
223         return 0;
224 }
225
226
227
228 #if 0
229 /* this is a list of devices */
230 static int
231 disk_get_md_parts(int fd)
232 {
233         return 0;
234 }
235 #endif
236
237 int
238 disk_get_pci(int fd,
239              unsigned char *bus,
240              unsigned char *device,
241              unsigned char *function)
242 {
243         int interface_type=interface_type_unknown;
244         unsigned int controllernum=0, disknum=0;
245         unsigned char part=0;
246         
247         disk_info_from_fd(fd,
248                           &interface_type,
249                           &controllernum, 
250                           &disknum,
251                           &part);
252         switch (interface_type) {
253         case ata:
254                 return disk_get_ide_pci(fd, bus, device, function);
255                 break;
256         case scsi:
257                 return disk_get_scsi_pci(fd, bus, device, function);
258                 break;
259         case i2o:
260                 break;
261         case md:
262                 break;
263         default:
264                 break;
265         }
266         return 1;
267 }       
268
269 int
270 disk_get_size(int fd, long *size)
271 {
272         return ioctl(fd, BLKGETSIZE, size);
273 }
274
275 /**
276  * is_mbr_valid(): test MBR for validity
277  * @mbr: pointer to a legacy mbr structure
278  *
279  * Description: Returns 1 if MBR is valid, 0 otherwise.
280  * Validity depends on one thing:
281  *  1) MSDOS signature is in the last two bytes of the MBR
282  */
283 static int
284 is_mbr_valid(legacy_mbr *mbr)
285 {
286         if (!mbr)
287                 return 0;
288         return (mbr->signature == MSDOS_MBR_SIGNATURE);
289 }
290
291 /************************************************************
292  * msdos_disk_get_extended partition_info()
293  * Requires:
294  *  - open file descriptor fd
295  *  - start, size
296  * Modifies: all these
297  * Returns:
298  *  0 on success
299  *  non-zero on failure
300  *
301  ************************************************************/
302
303 static int
304 msdos_disk_get_extended_partition_info (int fd, legacy_mbr *mbr,
305                                         uint32_t num,
306                                         uint64_t *start, uint64_t *size)
307 {
308         /* Until I can handle these... */
309         fprintf(stderr, "Extended partition info not supported.\n");
310         return 1;
311 }
312
313 /************************************************************
314  * msdos_disk_get_partition_info()
315  * Requires:
316  *  - mbr
317  *  - open file descriptor fd (for extended partitions)
318  *  - start, size, signature, mbr_type, signature_type
319  * Modifies: all these
320  * Returns:
321  *  0 on success
322  *  non-zero on failure
323  *
324  ************************************************************/
325
326 static int
327 msdos_disk_get_partition_info (int fd, legacy_mbr *mbr,
328                                uint32_t num,
329                                uint64_t *start, uint64_t *size,
330                                char *signature,
331                                uint8_t *mbr_type, uint8_t *signature_type)
332 {       
333         int rc;
334         long disk_size=0;
335         struct stat stat;
336         struct timeval tv;
337         
338         if (!mbr) return 1;
339         if (!is_mbr_valid(mbr)) return 1;
340
341         *mbr_type = 0x01;
342         *signature_type = 0x01;
343
344         if (!mbr->unique_mbr_signature && !opts.write_signature) {
345                 
346                 printf("\n\n******************************************************\n");
347                 printf("Warning! This MBR disk does not have a unique signature.\n");
348                 printf("If this is not the first disk found by EFI, you may not be able\n");
349                 printf("to boot from it without a unique signature.\n");
350                 printf("Run efibootmgr with the -w flag to write a unique signature\n");
351                 printf("to the disk.\n");
352                 printf("******************************************************\n\n");
353                 
354         }
355         else if (opts.write_signature) {
356                 
357                 /* MBR Signatures must be unique for the 
358                    EFI Boot Manager
359                    to find the right disk to boot from */
360                 
361                 rc = fstat(fd, &stat);
362                 if (rc == -1) {
363                         perror("stat disk");
364                 }
365
366                 rc = gettimeofday(&tv, NULL);
367                 if (rc == -1) {
368                         perror("gettimeofday");
369                 }
370                 
371                 /* Write the device type to the signature.
372                    This should be unique per disk per system */
373                 mbr->unique_mbr_signature =  tv.tv_usec << 16;
374                 mbr->unique_mbr_signature |= stat.st_rdev & 0xFFFF;
375                         
376                 /* Write it to the disk */
377                 lseek(fd, 0, SEEK_SET);
378                 write(fd, mbr, sizeof(*mbr));
379
380         }
381         *(uint32_t *)signature = mbr->unique_mbr_signature;
382                 
383                 
384         if (num > 4) {
385                 /* Extended partition */
386                 return msdos_disk_get_extended_partition_info(fd, mbr, num,
387                                                               start, size);
388         }
389         else if (num == 0) {
390                 /* Whole disk */
391                 *start = 0;
392                 disk_get_size(fd, &disk_size);
393                 *size = disk_size;
394         }
395         else if (num >= 1 && num <= 4) {
396                 /* Primary partition */
397                 *start = mbr->partition[num-1].starting_lba;
398                 *size  = mbr->partition[num-1].size_in_lba;
399                 
400         }
401         return 0;
402 }
403
404 /************************************************************
405  * get_sector_size
406  * Requires:
407  *  - filedes is an open file descriptor, suitable for reading
408  * Modifies: nothing
409  * Returns:
410  *  sector size, or 512.
411  ************************************************************/
412 int
413 get_sector_size(int filedes)
414 {
415         int rc, sector_size = 512;
416
417         rc = ioctl(filedes, BLKSSZGET, &sector_size);
418         if (rc)
419                 sector_size = 512;
420         return sector_size;
421 }
422
423 /**
424  * disk_get_partition_info()
425  *  @fd - open file descriptor to disk
426  *  @num   - partition number (1 is first partition on the disk)
427  *  @start - partition starting sector returned
428  *  @size  - partition size (in sectors) returned
429  *  @signature - partition signature returned
430  *  @mbr_type  - partition type returned
431  *  @signature_type - signature type returned
432  * 
433  *  Description: Finds partition table info for given partition on given disk.
434  *               Both GPT and MSDOS partition tables are tested for.
435  *  Returns 0 on success, non-zero on failure
436  */
437 int
438 disk_get_partition_info (int fd, 
439                          uint32_t num,
440                          uint64_t *start, uint64_t *size,
441                          char *signature,
442                          uint8_t *mbr_type, uint8_t *signature_type)
443 {
444         legacy_mbr *mbr;
445         void *mbr_unaligned;
446         off_t offset;
447         int this_bytes_read = 0;
448         int gpt_invalid=0, mbr_invalid=0;
449         int rc=0;
450         int sector_size = get_sector_size(fd);
451
452         if (sizeof(*mbr) != sector_size)
453                 return 1;
454         mbr_unaligned = malloc(sizeof(*mbr)+sector_size-1);
455         mbr = (legacy_mbr *)
456                 (((unsigned long)mbr_unaligned + sector_size - 1) &
457                  ~(unsigned long)(sector_size-1));
458         memset(mbr, 0, sizeof(*mbr));
459         offset = lseek(fd, 0, SEEK_SET);
460         this_bytes_read = read(fd, mbr, sizeof(*mbr));
461         if (this_bytes_read < sizeof(*mbr)) {
462                 rc=1;
463                 goto error_free_mbr;
464         }
465         gpt_invalid = gpt_disk_get_partition_info(fd, num,
466                                                   start, size,
467                                                   signature,
468                                                   mbr_type,
469                                                   signature_type);
470         if (gpt_invalid) {
471                 mbr_invalid = msdos_disk_get_partition_info(fd, mbr, num,
472                                                             start, size,
473                                                             signature,
474                                                             mbr_type,
475                                                             signature_type);
476                 if (mbr_invalid) {
477                         rc=1;
478                         goto error_free_mbr;
479                 }
480         }
481  error_free_mbr:
482         free(mbr_unaligned);
483         return rc;
484 }
485
486 #ifdef DISK_EXE
487 int
488 main (int argc, char *argv[])
489 {
490         int fd, rc;
491         unsigned char bus=0,device=0,func=0;
492         if (argc <= 1) return 1;
493         fd = open(argv[1], O_RDONLY|O_DIRECT);
494         rc = disk_get_pci(fd, &bus, &device, &func);
495         if (!rc) {
496                 printf("PCI %02x:%02x.%02x\n", bus, device, func);
497         }
498         return rc;
499 }
500 #endif