NetBSD/sbin/bim/bim.c
1995-03-18 12:28:06 +00:00

665 lines
17 KiB
C

/* $NetBSD: bim.c,v 1.2 1995/03/18 12:28:10 cgd Exp $ */
/*
* Copyright (c) 1994 Philip A. Nelson.
* All rights reserved.
*
* 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 by Philip A. Nelson.
* 4. The name of Philip A. Nelson may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY PHILIP NELSON ``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 PHILIP NELSON 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.
*/
/*
* Boot Image Manager
*
* (First copy called "hdsetup" and was written under Minix.)
*
* Phil Nelson
* Sept 30, 1990
*/
#include <sys/types.h>
#include <sys/param.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <a.out.h>
#include <string.h>
#include <ctype.h>
#define DKTYPENAMES
#include <sys/disklabel.h>
#include "images.h"
#define TRUE 1
#define FALSE 0
#define MAXARGCMDS 20
#define BLOCK_SIZE 1024
#define DEFAULT_DEVICE "/dev/sd0c"
/* Global data.... */
char disk_info [BLOCK_SIZE];
int disk_fd; /* The file descriptor for the disk. */
struct disklabel *dk_label = (struct disklabel *) &disk_info[LABELOFFSET];
struct imageinfo *im_table =
(struct imageinfo *) (&disk_info[LABELOFFSET] + sizeof(struct disklabel));
char *prog_name;
int label_changed = FALSE;
int images_changed = FALSE;
int secsize;
/* Utility routines... */
/***********************/
void usage ()
{
printf ("usage: %s [-c command [-c command ...]] [device]\n",prog_name);
printf (" Maximum of %d commands\n", MAXARGCMDS);
exit (-2);
}
error (s1, s2)
char *s1, *s2;
{
printf ("%s: %s%s\n",prog_name,s1,s2);
exit (3);
}
void getlf ( inchar )
char inchar;
{
while ( inchar != '\n') inchar = getchar();
}
void getstr (str, size)
char *str;
int size;
{
char inchar;
int count;
count = 0;
inchar = getchar();
while (count < size-1 && inchar != '\n') {
*str++ = inchar;
count++;
inchar = getchar();
}
*str++ = 0;
getlf (inchar);
}
/* Checksum a disk label */
unsigned short
dkcksum(lp)
struct disklabel *lp;
{
register unsigned short *start, *end, sum = 0;
start = (unsigned short *)lp;
end = (unsigned short*)&lp->d_partitions[lp->d_npartitions];
while (start < end) sum ^= *start++;
return sum;
}
void save_images ()
{
int count;
int start;
start = LABELOFFSET + sizeof(struct disklabel);
count = lseek (disk_fd, start, SEEK_SET);
if (count != start)
error ("lseek error in saving image info.","");
count = write (disk_fd, (char *) im_table, sizeof(struct imageinfo));
if (count != sizeof(struct imageinfo))
error ("write error in saveing image info.","");
sync ();
}
/* Get a number using the prompt routine. */
GetInt (num, prompt_str)
int *num;
char *prompt_str;
{
char answer[80];
prompt (answer, 80, prompt_str);
while (!Str2Int (answer, num))
{
printf ("Bad number.\n");
prompt (answer, 80, prompt_str);
}
}
/* This function will initialize the image information . */
void init_images (badmagic)
char badmagic;
{
char answer[80];
int index;
if (badmagic) {
printf ("Image information has improper magic number.\n");
while (TRUE) {
prompt (answer,3,"Do you want the images initialized? (y or n) ");
if (answer[0] == 'y') break;
if (answer[0] == 'n') error ("images not initialized.","");
}
}
/* Initialize the image table. */
im_table->ii_magic = IMAGE_MAGIC;
im_table->ii_boot_partition = -1;
for (index = 0; index < dk_label->d_npartitions; index++)
if (dk_label->d_partitions[index].p_fstype == FS_BOOT)
{
im_table->ii_boot_partition = index;
break;
}
im_table->ii_boot_count = MAXIMAGES;
im_table->ii_boot_used = 0;
im_table->ii_boot_default = -1;
images_changed = TRUE;
}
/* Print out the header and other information about the disk. */
int display_part(num, args, syntax)
int num;
char **args;
char *syntax;
{
int count;
printf ("\nDisk: %s Type: %s\n", dk_label->d_packname,
dk_label->d_typename);
printf ("Physical Sector Size = %d\n", dk_label->d_secsize);
printf ("Disk Size = %ld\n", dk_label->d_secperunit);
/* Disk Partitions. */
printf (" partition type sector start length in sectors\n");
for (count = 0; count < dk_label->d_npartitions; count++) {
if (dk_label->d_partitions[count].p_fstype != FS_UNUSED) {
printf (" %5c ", 'a'+count);
printf ("%14s", fstypenames[dk_label->d_partitions[count].p_fstype]);
printf ("%14ld %17ld\n",
dk_label->d_partitions[count].p_offset,
dk_label->d_partitions[count].p_size);
}
}
printf ("\n");
return FALSE;
}
int display_image(num, args, syntax)
int num;
char **args;
char *syntax;
{
int count;
/* Boot Images. */
if (im_table->ii_boot_partition != -1)
printf ("Boot partition = %c\n", 'a'+im_table->ii_boot_partition);
if (im_table->ii_boot_default != -1)
printf ("Default boot image = %d\n", im_table->ii_boot_default);
printf ("Boot Images: total of %d\n",im_table->ii_boot_count);
printf (" (image address and size in sectors.)\n");
printf ("Image address size load addr run addr name\n");
for (count = 0; count < im_table->ii_boot_used; count++) {
printf ("%5d %8lx %6lx %9lx %9lx %s\n", count,
im_table->ii_images[count].boot_address/secsize,
im_table->ii_images[count].boot_size/secsize,
im_table->ii_images[count].boot_load_adr,
im_table->ii_images[count].boot_run_adr,
im_table->ii_images[count].boot_name );
}
printf ("\n");
return FALSE;
}
int display_head(num, args, syntax)
int num;
char **args;
char *syntax;
{
printf ("\nDisk: %s Type: %s\n", dk_label->d_packname,
dk_label->d_typename);
printf ("Physical Sector Size = %d\n", dk_label->d_secsize);
printf ("Disk Size = %ld\n", dk_label->d_secperunit);
return FALSE;
}
/* Utility routine for moving boot images. These are byte addresses
relative to the start of the files. */
int copy_bytes (from_fd, from_adr, to_fd, to_adr, number)
int from_fd, from_adr, to_fd, to_adr, number;
{
int count;
int index;
int index1;
int left;
int xfer_size;
char buffer [BLOCK_SIZE];
/* Check the parameters. */
if (to_adr > from_adr && from_fd == to_fd)
{
printf ("There is a system error. (copy_bytes)\n");
return 0;
}
/* Do the copy. */
for (index = 0; index < number; index += BLOCK_SIZE)
{
count = lseek (from_fd, from_adr+index, SEEK_SET);
if (count != from_adr+index)
{
printf ("Error in copying (seek from)\n");
return 0;
}
count = read (from_fd, buffer, BLOCK_SIZE);
if (count != BLOCK_SIZE)
{
if (index != number-1 || count < 0)
{
printf ("Error in copying (read from)\n");
return 0;
}
else
{
while (count < BLOCK_SIZE)
buffer[count++] = 0;
}
}
count = lseek (to_fd, to_adr+index, SEEK_SET);
if (count != to_adr+index)
{
printf ("Error in copying (seek to)\n");
return 0;
}
count = write (to_fd, buffer, BLOCK_SIZE);
if (count != BLOCK_SIZE)
{
printf ("Error in copying (write to)\n");
return 0;
}
}
/* Success. */
return 1;
}
/* Add a boot image. */
int
add_image (num, args, syntax)
int num;
char **args;
char *syntax;
{
struct exec im_exec; /* Information about a new image. */
int im_file;
int im_size; /* Size of text and data in bytes. Rounded up to a full
block in both text and data. */
int which_image; /* Which image is to be operated upon. */
int count; /* read/write counts. */
int index; /* temporary variable for loops. */
int part_size; /* The total size of the boot partition (in bytes). */
int total_size; /* The total size of all images (in bytes). */
int boot_start; /* Byte address of start of boot partition. */
int new_size; /* The new total size of all images. */
int im_addr; /* Byte address of the new boot image. */
unsigned int im_load_adr; /* The memory load address. */
unsigned int im_run_adr; /* The memory run address. */
char *nptr; /* Pointer for makeing name lower case. */
/* Check argument numbers. */
if (num != 2 && num !=3)
{
printf ("Syntax: %s\n", syntax);
return FALSE;
}
/* Check for a boot partition. */
if (im_table->ii_boot_partition == -1)
{
printf ("There is no boot partition.\n");
return FALSE;
}
/* Any free images? */
which_image = im_table->ii_boot_used;
if (which_image == im_table->ii_boot_count)
{
printf ("No more boot image slots available.\n");
return FALSE;
}
/* Open the file. */
im_file = open (args[1], O_RDONLY);
if (im_file < 0)
{
printf ("Could not open %s\n", args[1]);
return FALSE;
}
/* check the exec header. */
count = read (im_file, (char *) &im_exec, sizeof(struct exec));
if (count != sizeof(struct exec))
{
printf ("Read problems for file %s\n", args[1]);
close (im_file);
return FALSE;
}
if (N_GETMAGIC(im_exec) != ZMAGIC || N_GETMID(im_exec) != MID_MACHINE)
{
printf ("%s is not a a pc532 executable file.\n", args[1]);
close (im_file);
return FALSE;
}
if (im_exec.a_entry < 0x2000)
{
printf ("%s has a load address less than 0x2000.\n", args[1]);
close (im_file);
return;
}
im_load_adr = im_exec.a_entry & ~(__LDPGSZ-1);
im_run_adr = im_exec.a_entry;
if (im_load_adr > 0xFFFFFF)
{
im_load_adr = im_load_adr & 0xFFFFFF;
im_run_adr = im_run_adr & 0xFFFFFF;
printf ("%s has a load address greater than 0xFFFFFF.\n", args[1]);
printf (
"using the address:\n\tload address = 0x%x\n\trun address = 0x%x\n",
im_load_adr, im_run_adr);
}
/* Check the sizes. */
boot_start = dk_label->d_partitions[im_table->ii_boot_partition].p_offset
* secsize;
part_size = dk_label->d_partitions[im_table->ii_boot_partition].p_size
* secsize;
total_size = 0;
for (index = 0; index < im_table->ii_boot_used; index++)
total_size = total_size + im_table->ii_images [index].boot_size;
/* Calculate other things. */
im_size = im_exec.a_text + im_exec.a_data;
/* Final check. */
new_size = total_size + im_size;
if (new_size > part_size)
{
printf ("Image too big to fit in boot partition.\n");
close(im_file);
}
/* Add the image. */
im_addr = (total_size+secsize-1)/secsize * secsize;
im_table->ii_images [which_image].boot_address = im_addr;
im_table->ii_images [which_image].boot_size = im_size;
im_table->ii_images [which_image].boot_load_adr = im_load_adr;
im_table->ii_images [which_image].boot_run_adr = im_run_adr;
if (num == 3)
strncpy (im_table->ii_images [which_image].boot_name, args[2], 15);
else
strncpy (im_table->ii_images [which_image].boot_name, args[1], 15);
if (copy_bytes (im_file,0,disk_fd,boot_start+im_addr,im_size))
{
im_table->ii_boot_used++;
/* Make name lowercase and report on image. */
for (nptr = im_table->ii_images[which_image].boot_name;
*nptr != 0;
nptr++)
if (isupper(*nptr)) *nptr = tolower (*nptr);
printf ("added image %d (%s).\n", which_image,
im_table->ii_images[which_image].boot_name);
close (im_file);
}
else
{
printf ("Problems in installing image.\n");
close (im_file);
return FALSE;
}
/* Save the changes. */
save_images ();
return FALSE;
}
/* Delete a boot image. */
int
delete_image (num, args, syntax)
int num;
char **args;
char *syntax;
{
int which_image; /* Which image is to be operated upon. */
int index; /* temporary variable for loops. */
int boot_start; /* Zone number of start of boot partition. */
int del_size; /* Size of the deleted image. */
/* Check arguments. */
if (num != 2)
{
printf ("Syntax: %s\n", syntax);
return FALSE;
}
/* Find the image. */
which_image = -1;
for (index = which_image; index < im_table->ii_boot_used; index++)
if (strcmp(im_table->ii_images[index].boot_name,args[1]) == 0)
{
which_image = index;
break;
}
if (which_image == -1)
if (!Str2Int(args[1],&which_image))
{
printf ("Syntax: %s\n", syntax);
return FALSE;
}
if (which_image < 0 || which_image >= im_table->ii_boot_used)
{
printf ("Delete: No such image (%s)\n", args[1]);
return FALSE;
}
/* Report on image we are deleteing. */
printf ("deleting image %d (%s).\n", which_image,
im_table->ii_images[which_image].boot_name);
/* Do the delete. */
boot_start = dk_label->d_partitions[im_table->ii_boot_partition].p_offset
* dk_label->d_secsize;
del_size = im_table->ii_images[which_image].boot_size;
for (index = which_image; index < im_table->ii_boot_used-1; index++)
{
copy_bytes (
disk_fd, boot_start+im_table->ii_images[index+1].boot_address,
disk_fd, boot_start+im_table->ii_images[index+1].boot_address-del_size,
im_table->ii_images[index+1].boot_size);
im_table->ii_images[index] = im_table->ii_images[index+1];
im_table->ii_images[index].boot_address -= del_size;
}
im_table->ii_boot_used--;
if (which_image == im_table->ii_boot_default)
im_table->ii_boot_default = -1;
else if (which_image < im_table->ii_boot_default)
im_table->ii_boot_default--;
/* Save the changes. */
save_images ();
return FALSE;
}
/* Set the default boot image. */
int
set_default_image (num, args, syntax)
int num;
char **args;
char *syntax;
{
int which_image;
if (num != 2 || !Str2Int(args[1],&which_image))
{
printf ("Syntax: %s\n", syntax);
return FALSE;
}
if (which_image >= im_table->ii_boot_used)
{
printf ("No such image.\n");
return FALSE;
}
im_table->ii_boot_default = which_image;
images_changed = TRUE;
return FALSE;
}
/* Initialize the disk or just the image portion. */
int
initialize (num, args, syntax)
int num;
char **args;
char *syntax;
{
/* Check the args */
if ( num > 1)
{
printf ("Syntax: %s\n", syntax);
return FALSE;
}
init_images (FALSE);
return FALSE;
}
/* Write the disk header and exit. */
int write_exit (num, args, syntax)
int num;
char **args;
char *syntax;
{
if (images_changed) save_images ();
return TRUE;
}
/* The main program! */
/*********************/
main (argc, argv)
int argc;
char *argv[];
{
int count; /* Used by reads. */
char answer, *fname;
int cmdscnt; /* Number of argument line commands. */
char *argcmds[MAXARGCMDS];
extern int optind, opterr;
extern char *optarg;
char optchar;
int index;
/* Check the parameters. */
prog_name = argv[0];
cmdscnt = 0;
opterr = TRUE;
fname = DEFAULT_DEVICE;
while ((optchar = getopt (argc, argv, "c:")) != EOF)
switch (optchar) {
case 'c': if (cmdscnt == MAXARGCMDS) usage();
argcmds[cmdscnt++] = optarg;
break;
case '?': usage ();
}
if (argc - optind > 1) usage();
if (optind < argc) fname = argv[optind];
disk_fd = open(fname, O_RDWR);
if (disk_fd < 0) error("Could not open ", fname);
/* Read the disk information block. */
count = read (disk_fd, disk_info, BLOCK_SIZE);
if (count != BLOCK_SIZE) error("Could not read info block on ", fname);
/* Check for correct information and set up pointers. */
if (dk_label->d_magic != DISKMAGIC)
error ("Could not find a disk label on", fname);
if (im_table->ii_magic != IMAGE_MAGIC) init_images (TRUE);
if (dkcksum (dk_label) != 0)
printf ("Warning: bad checksum in disk label.\n");
/* initialize secsize. */
secsize = dk_label->d_secsize;
/* do the commands.... */
if (cmdscnt > 0)
{
/* Process the argv commands. */
for (index = 0; index < cmdscnt; index++)
one_command (argcmds[index]);
}
else
{
/* Interactive command loop. */
display_part (0,NULL,NULL);
display_image (0,NULL,NULL);
command_loop ();
}
}