grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Rewrite of grub-mkdevicemap


From: Marco Gerards
Subject: Re: Rewrite of grub-mkdevicemap
Date: Sun, 05 Feb 2006 17:10:43 +0100
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

"Yoshinori K. Okuji" <address@hidden> writes:

> On Thursday 02 February 2006 14:31, Lubomir Kundrak wrote:
>> I am currently working on BSD port of GRUB, so I first need
>> the basic utilities to work.  I decided to rewrite
>> grub-mkdevicemap, because the old version was hard to modify,
>> and actually didn't use all the information provided by the
>> kernel.
>
> I don't know why you thought it is difficult to modify. grub-mkdevicemap.c is 
> based on lib/device.c in GRUB Legacy, which works well with *BSD.
>
>> Changelog entry and uuencoded source file is bellow
>
> I could not record it correctly. Can you attach it as a plain text?

Lubomir had problems with inline emails before, so I assume he can't.
Here it is, copied from Lubomir's email.

--
Marco


2006-01-02  Lubomir Kundrak  <address@hidden>

     * util/i386/pc/grub-mkdevicemap.c: Complete rewrite


/*
 * grub-mkdevicemap -- generate BIOS-to-native disk map
 * Copyright (C) 2006  Lubomir Kundrak
 *
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,  
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#define DEFAULT_DIRECTORY       "/grub"
#define DEFAULT_DEVICE_MAP      DEFAULT_DIRECTORY "/device.map"

#define PROGNAME        "grub-mkdevicemap"

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <argp.h>
#include <grub/util/misc.h>
#include <sys/param.h>

#if defined (BSD)
# include <sys/types.h>
# include <sys/sysctl.h>
#endif /* BSD */

#if defined (__NetBSD__)
# include <machine/cpu.h>
# include <util.h>
#endif /* __NetBSD__ */

#if defined (__OpenBSD__)
# include <machine/cpu.h>
# include <dirent.h>
# include <sys/stat.h>
# include <machine/biosvar.h>
#endif /* __OpenBSD__ */

#define BIOS_DISKS      0xff
#define BIOS_HD         0x80    /* Number of first hard-disk device.  */

#define GRUB_DISK_TYPE_NONE     0
#define GRUB_DISK_TYPE_HD       1
#define GRUB_DISK_TYPE_FD       2

/* The device map.  */
struct grub_disk
{
  char file_name[MAXPATHLEN];
  int matches;
  int type;             /* See defines above.  */
} grub_device_map[BIOS_DISKS];

/* Command line arguments.  */
struct arguments
{ 
  char *device_map;
};

static struct argp_option options[] =
  {
    {"device-map", 'm', "FILE", 0,
     "Output device map to FILE [default=" DEFAULT_DEVICE_MAP "]", 0},
    {"verbose",    'v', 0,      0, "Print verbose messages", 0},
    { 0, 0, 0, 0, 0, 0 }
  };

const char *argp_program_version = PROGNAME
                                   " ("PACKAGE_NAME") "
                                   PACKAGE_VERSION;
const char *argp_program_bug_address = PACKAGE_BUGREPORT;
static char doc[] = "Generate a device map file automatically.";

char *progname;

int grub_last_hd = BIOS_HD - 1;

/* Not OpenBSD-specific, but used only in OpenBSD code.  */
#if defined (__OpenBSD__)

/* Scans directory specified by root recursively until in finds the file of
   block device dev.  In case device is found, its name is copied to fname,
   otherwise fname is untouched. */

int
get_dev_fname (char *fname, dev_t dev, char *root)
{
  DIR *dir;
  struct dirent *ent;

  grub_util_info ("searching for device 0x%04x in %s", dev, root);

  dir = opendir (root);
  if (dir == NULL)
    {
      grub_util_info ("unable to open %s: %s", root, strerror (errno));
      return -1;
    }

  while ((ent = readdir (dir)))
    {
      struct stat st;
      char path[MAXPATHLEN] = "";

      strncat (path, root, MAXPATHLEN);
      strncat (path, "/", MAXPATHLEN);
      strncat (path, ent->d_name, MAXPATHLEN);

      if (stat (path, &st) > 0)
        {
          grub_util_info ("unable to stat %s: %s", path, strerror (errno));
          continue;
        }

      if (S_ISDIR (st.st_mode) && ent->d_name[0] != '.')
        get_dev_fname (fname, dev, path);

      if (S_ISBLK (st.st_mode) && dev == st.st_rdev)
        strncpy (fname, path, MAXPATHLEN);

      if (fname[0])
        break;
    }

    closedir (dir);
    return 0;
  }

#endif /* __OpenBSD__ */

#if defined (__i386__)
# if defined (__NetBSD__) && defined (MAX_BIOSDISKS)

int
getrawpartition () /* XXX: -lutil */
{
  return 3;
}

/* This function retrieves information from the NetBSD/i386 kernel to
   construct device map.  It returns number of disks matched.
 
   The NetBSD/i386 native bootloader passes checksum of MBR of each BIOS
   disk and kernel tries to match it with its devices.  It exports the
   results via "machdep.diskinfo" sysctl.  See bottom of <sys/cpu.h> for
   more details.
 
   Note 1:
     GRUB doesn't (but imho should) pass anything like that, so when
     running kernel was booted by GRUB, this function always returns 0.
 
   Note 2:
     Diskinfo contains information only about hard disks.  */

int
netbsd_get_diskinfo ()
{
  struct disklist *disks;
  size_t size;
  int mib[2];
  int ndisks = 0;
  int i;

  mib[0] = CTL_MACHDEP;
  mib[1] = CPU_DISKINFO;

  if (sysctl (mib, 2, NULL, &size, NULL, 0) == -1)
    return -1;

  disks = (struct disklist *) malloc (size);
  if (! disks)
    {
      grub_util_info ("can't allocate %i B of memory for diskinfo struct",
                      size); 
      return -1;
    }

  if (sysctl (mib, 2, disks, &size, NULL, 0) == -1)
    return -1;

  /* BIOS hard disk devices */

  for (i = 0; i < disks->dl_nbiosdisks; i++)
    {
      struct biosdisk_info *this = &disks->dl_biosdisks[i];

      grub_device_map[this->bi_dev].type = GRUB_DISK_TYPE_HD;
      if (this->bi_dev > grub_last_hd)
        grub_last_hd = this->bi_dev;
    }

  /* Native hard disk devices */

  for (i = 0; i < disks->dl_nnativedisks; i++)
    {
      struct nativedisk_info *this = &disks->dl_nativedisks[i];
      int match = 0;
      int i;

      if (this->ni_nmatches > 1)
        {
          grub_util_info ("%s corresponds to more than one BIOS disk.",
                          this->ni_devname);
        }

      for (i = 0; i < this->ni_nmatches; i++)
        {
          int bi_dev = disks->dl_biosdisks[this->ni_biosmatches[i]].bi_dev;
 
          grub_device_map[bi_dev].matches++;

          if ((! match) && (grub_device_map[bi_dev].file_name[0] == 0))
            {
              match = bi_dev;
              ndisks++;
              snprintf (grub_device_map[bi_dev].file_name, MAXPATHLEN,
                       "/dev/%s%c", this->ni_devname,
                       getrawpartition () + 'a');
            }
        }

      if (! match)
        {
          /* Probably it's removable USB drive or whatever.  */
          grub_util_info ("unable to guess BIOS disk number for device %s\n",
                          this->ni_devname);
        }
    }

  free (disks);
  return ndisks;
}

# endif /* !__NetBSD__ */

# if defined (__OpenBSD__)

/* Does things similar to what netbsd_get_diskinfo() does for NetBSD
   As OpenBSD is just obsolete NetBSD, it is little less elegant and
   little less accurate :)  (No offense, just kidding.  Really :o) */ 

int
openbsd_get_diskinfo ()
{
  int mib[4];
  int ndisks = 0;
  int i;

  mib[0] = CTL_MACHDEP;
  mib[1] = CPU_BIOS;
  mib[2] = BIOS_DISKINFO;

  for (i = 0; i < BIOS_DISKS; i++)
    {
      bios_diskinfo_t disk;
      size_t size = sizeof (bios_diskinfo_t);
      char path[MAXPATHLEN] = "";
      dev_t rdev;

      mib[3] = i;

      if (sysctl (mib, 4, &disk, &size, NULL, 0) == -1)
        continue;

      /* switch major/minor number */
      rdev = (disk.bsd_dev & 0x00ff) << 8;
      rdev |= (disk.bsd_dev & 0xff00) >> 8;

      get_dev_fname (path, rdev, "/dev");

      if (path[0])
        {
          int devno = disk.bios_number;

          ndisks++;
          grub_device_map[devno].matches++;
          strncpy (grub_device_map[devno].file_name, path, MAXPATHLEN);
          grub_device_map[devno].type = devno < BIOS_HD
                                        ? GRUB_DISK_TYPE_FD
                                        : GRUB_DISK_TYPE_HD;
        }
    }

  return 0;
}

# endif /* __OpenBSD__ */
#endif  /* __i386__ */

#if defined (BSD)

/* All BSD variants provide list of disk devices they recognize.  So they might 
be used
   as alternative information source for device map construction.
 
   FreeBSD/Dragonfly:   kern.disks
   NetBSD/OpenBSD:      hw.disknames */

int
bsd_get_disknames (disktypes)
  char *disktypes[];
{
  char **p;
  char *disknames;
  size_t size;
  int mib[2];

#if defined (__FreeBSD__) || defined (__DragonFly__)

  /* XXX: any way for systems without sysctlnametomib()?
     I failed to find constants for kern.disks in headers.
     Grrrr.  I am very sorry about that.  Anyways, do they change at all? */

  size = 2;
  if (sysctlnametomib("kern.disks", mib, &size) == -1)
    return -1;

#else   /* !__FreeBSD__ && !__DragonFly__ */

  mib[0] = CTL_HW;
  mib[1] = HW_DISKNAMES;

#endif

  if (sysctl (mib, 2, NULL, &size, NULL, 0) == -1)
    return -1;

  disknames = (char *) malloc (size);
  if (! disknames)
    {
      grub_util_info ("can't allocate %i B of memory for disk list", size); 
      return -1;
    }

  if (sysctl (mib, 2, disknames, &size, NULL, 0) == -1)
    return -1;

  for (p = &disktypes[0]; *p; p++)
    {
      char *disks = disknames;
        while ((disks = strstr (disks, *p)))
          {
            int disknum;

            disknum = atoi (&disks[2]);
            switch (*p[0])
              {
              case 'f':
                grub_device_map[disknum].matches++;
                grub_device_map[disknum].type = GRUB_DISK_TYPE_FD;
                snprintf (grub_device_map[disknum].file_name,
                         MAXPATHLEN, "/dev/fd%ia", disknum);
                break;
              case 'w':
                grub_last_hd++;
                grub_device_map[grub_last_hd].matches++;
                grub_device_map[grub_last_hd].type = GRUB_DISK_TYPE_HD;
                snprintf (grub_device_map[grub_last_hd].file_name,
                         MAXPATHLEN, "/dev/hd%ia", disknum);
                break;
              case 's':
                grub_last_hd++;
                grub_device_map[grub_last_hd].matches++;
                grub_device_map[grub_last_hd].type = GRUB_DISK_TYPE_HD;
                snprintf (grub_device_map[grub_last_hd].file_name,
                        MAXPATHLEN, "/dev/sd%ia", disknum);
                break;
              }
            disks++;
          }
    }

  free (disknames);
  return 0;
}

#endif  /* BSD */

#if defined (__linux__)

/* Routine for getting information about disks from Linux's procfs
  
   1.) Using /proc/ide and /proc/scsi would be a good idea if all
       SCSI and SCSI-SATA drivers implemented /proc/scsi interface. 
       So we don't use this.

   2.) EDD bios support would be great. 2.6 Linux supports it.
       Not yet implemented here.

   3.) We can dig names of disks from /proc/partitions.
       Everything that does not end with number is considered to be
       a disk. */

int
linux_get_hds ()
{
  int ndisks = 0;
  FILE *partitions;
  char dev[MAXPATHLEN];

  partitions = fopen ("/proc/partitions", "r");
  if (partitions == NULL)
    {
      grub_util_info ("/proc/partitions: %s", strerror (errno));
      return -1;
    }

  while (getc (partitions) != '\n');    /* skip header */
  while (fscanf (partitions, "%s%s%s%s\n", dev, dev, dev, dev) != EOF)
    { 
      if (!isdigit (dev[strlen (dev)-1]))
        {
          grub_last_hd++;
          ndisks++;
          grub_device_map[grub_last_hd].matches++;
          grub_device_map[grub_last_hd].type = GRUB_DISK_TYPE_HD;
          snprintf (grub_device_map[grub_last_hd].file_name,
                   MAXPATHLEN, "/dev/%s", dev);
        }
    }

  fclose (partitions);
  return ndisks;
}

/* 4.) Floppies don't have partitions.UGet number of floppies
       from /proc/devices  */

int
linux_get_fds ()
{
  int ndisks = 0;
  FILE *devices;
  char dev[MAXPATHLEN];

  devices = fopen ("/proc/devices", "r");
  if (devices == NULL)
    {
      grub_util_info ("/proc/devices: %s", strerror (errno));
      return -1;
    }

  while (fscanf (devices, "%s%s\n", dev, dev) != EOF)
    {
      if (strcmp (dev, "fd") == 0)
        {
          grub_device_map[ndisks].matches++;
          grub_device_map[ndisks].type = GRUB_DISK_TYPE_HD;
          snprintf (grub_device_map[ndisks].file_name,
                   MAXPATHLEN, "/dev/fd%i", ndisks);
          ndisks++;
        }
    }

  fclose (devices);
  return ndisks;
}

#endif  /* __linux__ */

/* Calls OS/machine dependent routines to construct the map, ordered by
   result accuracy. */

void
get_map ()
{

#if defined (BSD)
# if defined (__NetBSD__)

  if (netbsd_get_diskinfo () > 0)
    
            MAXPATHLEN, {
      char *disktypes[] =
        {
          "fd", /* floppy */
          0
        };
      bsd_get_disknames (disktypes);
    }
  else
    {
      char *disktypes[] =
        {
          "fd", /* floppy */
          "wd", /* IDE */
          "sd", /* SCSI */
          0
        };
      bsd_get_disknames (disktypes);
    }

# elif defined (__OpenBSD__)

  if (openbsd_get_diskinfo () == 0)
    {
      char *disktypes[] =
        {
          "fd", /* floppy */
          "wd", /* IDE */
          "sd", /* SCSI */
          0
        };
      bsd_get_disknames (disktypes);
    }

# else  /* !__NetBSD__ && !__OpenBSD__ */

  {
    char *disktypes[] =
      {
        "fd", /* floppy */
        "ad", /* IDE */
        "da", /* IDE */
        "sd", /* SCSI */
        0
      };
      bsd_get_disknames (disktypes);
  }

#  warning "This BSD variant is not supported yet"
# endif
#elif defined (__linux__)

  linux_get_hds ();
  linux_get_fds ();

#else
# error "This OS is not supported"
#endif

}

/* Outputs the map constructed by get_map() to a device map file. */

void
dump_map (struct arguments *args)
{
  FILE *output;
  int i;

  if (strcmp (args->device_map, "-") == 0)
    {
      output = stdout;
    }
  else
    {
      output = fopen (args->device_map, "w");
      if (output == NULL)
        {
          grub_util_error ("unable to open %s: %s",
                           args->device_map, strerror (errno));
        }
    }

  for (i = 0; i < BIOS_DISKS; i++)
    {
      if (grub_device_map[i].type)
        {
          fprintf (output, "(%s%i)\t%s\n",
                   grub_device_map[i].type == GRUB_DISK_TYPE_HD ? "hd" :
                   grub_device_map[i].type == GRUB_DISK_TYPE_FD ? "fd" :
                   "???",
                   i < BIOS_HD ? i : i - BIOS_HD,
                   grub_device_map[i].file_name);
        }
  }
}

static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{ 
  struct arguments *args = state->input;
 
  switch (key)
    {
    case 'm':
      args->device_map = arg;
      break;
    case 'v':
      verbosity++;
      break;
    default:
      return ARGP_ERR_UNKNOWN;
    }
  return 0;
}
 
static struct argp argp = {options, parse_opt, NULL, doc, 0, 0, 0};

int
main (int argc, char *argv[])
{
  struct arguments args =
    {
      .device_map = DEFAULT_DEVICE_MAP,
    };

  progname = PROGNAME;
  argp_parse (&argp, argc, argv, 0, 0, &args);

  get_map ();
  dump_map (&args);

  return 0;
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]