NetBSD/distrib/utils/sysinst/mbr.c
fvdl 902ae34ae6 When converting from relsecs to chs, if the value exceeds the maxmimum
that can be specified with CHS, truncate it to the maximum values that
the BIOS provided, not 1023*255*63. Some BIOSs get awfully cranky when
you do that.
2000-05-27 18:41:36 +00:00

632 lines
15 KiB
C

/* $NetBSD: mbr.c,v 1.22 2000/05/27 18:41:36 fvdl Exp $ */
/*
* Copyright 1997 Piermont Information Systems Inc.
* All rights reserved.
*
* Written by Philip A. Nelson for Piermont Information Systems Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the NetBSD Project by
* Piermont Information Systems Inc.
* 4. The name of Piermont Information Systems Inc. may not be used to endorse
* or promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* Following applies to the geometry guessing code
*/
/*
* Mach Operating System
* Copyright (c) 1992 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/* mbr.c -- DOS Master Boot Record editing code */
#include <sys/param.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <util.h>
#include "defs.h"
#include "mbr.h"
#include "md.h"
#include "msg_defs.h"
#include "menu_defs.h"
#include "endian.h"
struct part_id {
int id;
char *name;
} part_ids[] = {
{0, "unused"},
{MBR_PTYPE_FAT12, "DOS FAT12"},
{MBR_PTYPE_FAT16S, "DOS FAT16, <32M"},
{MBR_PTYPE_EXT, "Extended partition"},
{MBR_PTYPE_FAT16B, "DOS FAT16, >32M"},
{MBR_PTYPE_NTFS, "NTFS"},
{MBR_PTYPE_FAT32, "Windows FAT32"},
{MBR_PTYPE_FAT32L, "Windows FAT32, LBA"},
{MBR_PTYPE_FAT16L, "Windows FAT16, LBA"},
{MBR_PTYPE_EXT_LBA, "Extended partition, LBA"},
{MBR_PTYPE_LNXSWAP, "Linux swap"},
{MBR_PTYPE_LNXEXT2, "Linux native"},
{MBR_PTYPE_NTFSVOL, "NTFS volume set"},
{MBR_PTYPE_386BSD, "old NetBSD/FreeBSD/386BSD"},
{MBR_PTYPE_NETBSD, "NetBSD"},
{-1, "Unknown"},
};
int dosptyp_nbsd = MBR_PTYPE_NETBSD;
static int get_mapping __P((struct mbr_partition *, int, int *, int *, int *,
long *absolute));
static void convert_mbr_chs __P((int, int, int, u_int8_t *, u_int8_t *,
u_int8_t *, u_int32_t));
/*
* First, geometry stuff...
*/
int
check_geom()
{
return bcyl <= 1024 && bsec < 64 && bcyl > 0 && bhead > 0 && bsec > 0;
}
/*
* get C/H/S geometry from user via menu interface and
* store in globals.
*/
void
set_bios_geom(cyl, head, sec)
int cyl, head, sec;
{
char res[80];
msg_display_add(MSG_setbiosgeom);
snprintf(res, 80, "%d", cyl);
msg_prompt_add(MSG_cylinders, res, res, 80);
bcyl = atoi(res);
snprintf(res, 80, "%d", head);
msg_prompt_add(MSG_heads, res, res, 80);
bhead = atoi(res);
snprintf(res, 80, "%d", sec);
msg_prompt_add(MSG_sectors, res, res, 80);
bsec = atoi(res);
}
void
disp_cur_geom()
{
msg_display_add(MSG_realgeom, dlcyl, dlhead, dlsec);
msg_display_add(MSG_biosgeom, bcyl, bhead, bsec);
}
/*
* Then, the partition stuff...
*/
int
otherpart(id)
int id;
{
return (id != 0 && id != MBR_PTYPE_386BSD && id != MBR_PTYPE_NETBSD);
}
int
ourpart(id)
int id;
{
return (id == MBR_PTYPE_386BSD || id == MBR_PTYPE_NETBSD);
}
/*
* Let user change incore Master Boot Record partitions via menu.
*/
int
edit_mbr(partition)
struct mbr_partition *partition;
{
int i, j;
/* Ask full/part */
/* XXX this sucks ("part" is used in menus, no param passing there) */
part = partition;
msg_display(MSG_fullpart, diskdev);
process_menu(MENU_fullpart);
/* DOS fdisk label checking and value setting. */
if (usefull) {
int otherparts = 0;
int ourparts = 0;
int i;
/* Count nonempty, non-BSD partitions. */
for (i = 0; i < NMBRPART; i++) {
otherparts += otherpart(part[i].mbrp_typ);
/* check for dualboot *bsd too */
ourparts += ourpart(part[i].mbrp_typ);
}
/* Ask if we really want to blow away non-NetBSD stuff */
if (otherparts != 0 || ourparts > 1) {
msg_display(MSG_ovrwrite);
process_menu(MENU_noyes);
if (!yesno) {
if (logging)
(void)fprintf(log, "User answered no to destroy other data, aborting.\n");
return 0;
}
}
/* Set the partition information for full disk usage. */
part[0].mbrp_typ = part[0].mbrp_flag = 0;
part[0].mbrp_start = part[0].mbrp_size = 0;
part[1].mbrp_typ = part[0].mbrp_flag = 0;
part[1].mbrp_start = part[0].mbrp_size = 0;
part[2].mbrp_typ = part[0].mbrp_flag = 0;
part[2].mbrp_start = part[0].mbrp_size = 0;
part[3].mbrp_typ = dosptyp_nbsd;
part[3].mbrp_size = bsize - bsec;
part[3].mbrp_start = bsec;
part[3].mbrp_flag = 0x80;
ptstart = bsec;
ptsize = bsize - bsec;
fsdsize = dlsize;
fsptsize = dlsize - bsec;
fsdmb = fsdsize / MEG;
activepart = 3;
} else {
int numbsd, overlap;
int numfreebsd, freebsdpart; /* dual-boot */
/* Ask for sizes, which partitions, ... */
ask_sizemult(bcylsize);
bsdpart = freebsdpart = -1;
activepart = -1;
for (i = 0; i<4; i++)
if (part[i].mbrp_flag != 0) {
activepart = i;
part[i].mbrp_flag = 0;
}
do {
process_menu (MENU_editparttable);
numbsd = 0;
bsdpart = -1;
freebsdpart = -1;
numfreebsd = 0;
overlap = 0;
yesno = 0;
for (i=0; i<4; i++) {
/* Count 386bsd/FreeBSD/NetBSD(old) partitions */
if (part[i].mbrp_typ == MBR_PTYPE_386BSD) {
freebsdpart = i;
numfreebsd++;
}
/* Count NetBSD-only partitions */
if (part[i].mbrp_typ == MBR_PTYPE_NETBSD) {
bsdpart = i;
numbsd++;
}
for (j = i+1; j<4; j++)
if (partsoverlap(part, i,j))
overlap = 1;
}
/* If no new-NetBSD partition, use 386bsd instead */
if (numbsd == 0 && numfreebsd > 0) {
numbsd = numfreebsd;
bsdpart = freebsdpart;
/* XXX check partition type? */
}
/* Check for overlap or multiple native partitions */
if (overlap || numbsd != 1) {
msg_display(MSG_reeditpart);
process_menu(MENU_yesno);
}
} while (yesno && (numbsd != 1 || overlap));
if (numbsd == 0) {
msg_display(MSG_nobsdpart);
process_menu(MENU_ok);
return 0;
}
if (numbsd > 1) {
msg_display(MSG_multbsdpart, bsdpart);
process_menu(MENU_ok);
}
if (activepart == -1) {
msg_display(MSG_noactivepart);
process_menu(MENU_yesno);
if (yesno)
part[bsdpart].mbrp_flag = 0x80;
} else
part[activepart].mbrp_flag = 0x80;
if (bsdpart == freebsdpart) {
msg_display(MSG_upgradeparttype);
process_menu(MENU_yesno);
if (yesno)
part[bsdpart].mbrp_typ = dosptyp_nbsd;
}
ptstart = part[bsdpart].mbrp_start;
ptsize = part[bsdpart].mbrp_size;
fsdsize = dlsize;
if (ptstart + ptsize < bsize)
fsptsize = ptsize;
else
fsptsize = dlsize - ptstart;
fsdmb = fsdsize / MEG;
/* Ask if a boot selector is wanted. XXXX */
}
/* Compute minimum NetBSD partition sizes (in sectors). */
minfsdmb = (80 + 4*rammb) * (MEG / sectorsize);
return 1;
}
int
partsoverlap(part, i, j)
struct mbr_partition *part;
int i;
int j;
{
if (part[i].mbrp_size == 0 || part[j].mbrp_size == 0)
return 0;
return
(part[i].mbrp_start < part[j].mbrp_start &&
part[i].mbrp_start + part[i].mbrp_size > part[j].mbrp_start)
||
(part[i].mbrp_start > part[j].mbrp_start &&
part[i].mbrp_start < part[j].mbrp_start + part[j].mbrp_size)
||
(part[i].mbrp_start == part[j].mbrp_start);
}
char *
get_partname(i)
int i;
{
int j;
for (j = 0; part_ids[j].id != -1 &&
part_ids[j].id != part[i].mbrp_typ; j++);
return part_ids[j].name;
}
void
disp_cur_part(part, sel, disp)
struct mbr_partition *part;
int sel;
int disp;
{
int i, start, stop, rsize, rend;
if (disp < 0)
start = 0, stop = 4;
else
start = disp, stop = disp+1;
msg_table_add(MSG_part_header, multname, multname, multname);
for (i = start; i < stop; i++) {
if (sel == i)
msg_standout();
if (part[i].mbrp_size == 0 && part[i].mbrp_start == 0)
msg_table_add(MSG_part_row_start_unused, i);
else {
rsize = part[i].mbrp_size / sizemult;
if (part[i].mbrp_size % sizemult)
rsize++;
rend = (part[i].mbrp_start + part[i].mbrp_size) / sizemult;
if ((part[i].mbrp_size + part[i].mbrp_size) % sizemult)
rend++;
msg_table_add(MSG_part_row_start_used, i,
part[i].mbrp_start / sizemult, rsize, rend);
}
msg_table_add(MSG_part_row_end, get_partname(i));
if (sel == i)
msg_standend();
}
}
int
read_mbr(disk, buf, len)
char *disk, *buf;
int len;
{
char diskpath[MAXPATHLEN];
int fd, i;
struct mbr_partition *mbrp;
/* Open the disk. */
fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
if (fd < 0)
return -1;
if (lseek(fd, MBR_BBSECTOR * MBR_SECSIZE, SEEK_SET) < 0) {
close(fd);
return -1;
}
if (read(fd, buf, len) < len) {
close(fd);
return -1;
}
if (valid_mbr(buf)) {
mbrp = (struct mbr_partition *)&buf[MBR_PARTOFF];
for (i = 0; i < NMBRPART; i++) {
if (mbrp[i].mbrp_typ != 0) {
mbrp[i].mbrp_start =
le_to_native32(mbrp[i].mbrp_start);
mbrp[i].mbrp_size =
le_to_native32(mbrp[i].mbrp_size);
}
}
}
(void)close(fd);
return 0;
}
int
write_mbr(disk, buf, len, convert)
char *disk, *buf;
int len;
int convert;
{
char diskpath[MAXPATHLEN];
int fd, i, ret = 0;
struct mbr_partition *mbrp;
u_int32_t pstart, psize;
/* Open the disk. */
fd = opendisk(disk, O_WRONLY, diskpath, sizeof(diskpath), 0);
if (fd < 0)
return -1;
if (lseek(fd, MBR_BBSECTOR * MBR_SECSIZE, SEEK_SET) < 0) {
close(fd);
return -1;
}
mbrp = (struct mbr_partition *)&buf[MBR_PARTOFF];
for (i = 0; i < NMBRPART; i++) {
if (mbrp[i].mbrp_start == 0 &&
mbrp[i].mbrp_size == 0) {
mbrp[i].mbrp_scyl = 0;
mbrp[i].mbrp_shd = 0;
mbrp[i].mbrp_ssect = 0;
mbrp[i].mbrp_ecyl = 0;
mbrp[i].mbrp_ehd = 0;
mbrp[i].mbrp_esect = 0;
} else {
pstart = mbrp[i].mbrp_start;
psize = mbrp[i].mbrp_size;
mbrp[i].mbrp_start = native_to_le32(pstart);
mbrp[i].mbrp_size = native_to_le32(psize);
if (convert) {
convert_mbr_chs(bcyl, bhead, bsec,
&mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd,
&mbrp[i].mbrp_ssect, pstart);
convert_mbr_chs(bcyl, bhead, bsec,
&mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd,
&mbrp[i].mbrp_esect, pstart + psize - 1);
}
}
}
if (write(fd, buf, len) < 0)
ret = -1;
(void)close(fd);
return ret;
}
int
valid_mbr(buf)
char *buf;
{
u_int16_t magic;
magic = *((u_int16_t *)&buf[MBR_MAGICOFF]);
return (le_to_native16(magic) == MBR_MAGIC);
}
static void
convert_mbr_chs(cyl, head, sec, cylp, headp, secp, relsecs)
int cyl, head, sec;
u_int8_t *cylp, *headp, *secp;
u_int32_t relsecs;
{
unsigned int tcyl, temp, thead, tsec;
temp = cyl * head * sec - 1;
if (relsecs >= temp)
relsecs = temp;
temp = head * sec;
tcyl = relsecs / temp;
relsecs %= temp;
thead = relsecs / sec;
tsec = (relsecs % sec) + 1;
*cylp = MBR_PUT_LSCYL(tcyl);
*headp = thead;
*secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec);
}
/*
* This function is ONLY to be used as a last resort to provide a
* hint for the user. Ports should provide a more reliable way
* of getting the BIOS geometry. The i386 code, for example,
* uses the BIOS geometry as passed on from the bootblocks,
* and only uses this as a hint to the user when that information
* is not present, or a match could not be made with a NetBSD
* device.
*/
int
guess_biosgeom_from_mbr(buf, cyl, head, sec)
char *buf;
int *cyl, *head, *sec;
{
struct mbr_partition *parts = (struct mbr_partition *)&buf[MBR_PARTOFF];
int cylinders = -1, heads = -1, sectors = -1, i, j;
int c1, h1, s1, c2, h2, s2;
long a1, a2;
quad_t num, denom;
*cyl = *head = *sec = -1;
/* Try to deduce the number of heads from two different mappings. */
for (i = 0; i < NMBRPART * 2; i++) {
if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
continue;
for (j = 0; j < 8; j++) {
if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
continue;
num = (quad_t)h1*(a2-s2) - (quad_t)h2*(a1-s1);
denom = (quad_t)c2*(a1-s1) - (quad_t)c1*(a2-s2);
if (denom != 0 && num % denom == 0) {
heads = num / denom;
break;
}
}
if (heads != -1)
break;
}
if (heads == -1)
return -1;
/* Now figure out the number of sectors from a single mapping. */
for (i = 0; i < NMBRPART * 2; i++) {
if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
continue;
num = a1 - s1;
denom = c1 * heads + h1;
if (denom != 0 && num % denom == 0) {
sectors = num / denom;
break;
}
}
if (sectors == -1)
return -1;
/*
* Estimate the number of cylinders.
* XXX relies on get_disks having been called.
*/
cylinders = disk->dd_totsec / heads / sectors;
/* Now verify consistency with each of the partition table entries.
* Be willing to shove cylinders up a little bit to make things work,
* but translation mismatches are fatal. */
for (i = 0; i < NMBRPART * 2; i++) {
if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
continue;
if (sectors * (c1 * heads + h1) + s1 != a1)
return -1;
if (c1 >= cylinders)
cylinders = c1 + 1;
}
/* Everything checks out. Reset the geometry to use for further
* calculations. */
*cyl = cylinders;
*head = heads;
*sec = sectors;
return 0;
}
static int
get_mapping(parts, i, cylinder, head, sector, absolute)
struct mbr_partition *parts;
int i, *cylinder, *head, *sector;
long *absolute;
{
struct mbr_partition *part = &parts[i / 2];
if (part->mbrp_typ == 0)
return -1;
if (i % 2 == 0) {
*cylinder = MBR_PCYL(part->mbrp_scyl, part->mbrp_ssect);
*head = part->mbrp_shd;
*sector = MBR_PSECT(part->mbrp_ssect) - 1;
*absolute = part->mbrp_start;
} else {
*cylinder = MBR_PCYL(part->mbrp_ecyl, part->mbrp_esect);
*head = part->mbrp_ehd;
*sector = MBR_PSECT(part->mbrp_esect) - 1;
*absolute = part->mbrp_start + part->mbrp_size - 1;
}
return 0;
}