[Top][All Lists]
[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;
}