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