/* @(#)boot.c 1.28 16/10/23 Copyright 1999-2016 J. Schilling */ #include #ifndef lint static UConst char sccsid[] = "@(#)boot.c 1.28 16/10/23 Copyright 1999-2016 J. Schilling"; #endif /* * Support for generic boot (sector 0..16) * and to boot Sun sparc and Sun x86 systems. * * Copyright (c) 1999-2016 J. Schilling */ /*@@C@@*/ #include "mkisofs.h" #include #include #include #include #include "sunlabel.h" extern int use_sunx86boot; LOCAL struct sun_label cd_label; LOCAL struct x86_label sx86_label; LOCAL struct pc_part fdisk_part; LOCAL char *boot_files[NDKMAP]; /* Change this for > 8 x86 parts */ LOCAL void init_sparc_label __PR((void)); LOCAL void init_sunx86_label __PR((void)); EXPORT int sparc_boot_label __PR((char *label)); EXPORT int sunx86_boot_label __PR((char *label)); EXPORT int scan_sparc_boot __PR((char *files)); EXPORT int scan_sunx86_boot __PR((char *files)); EXPORT int make_sun_label __PR((void)); EXPORT int make_sunx86_label __PR((void)); LOCAL void dup_sun_label __PR((int part)); LOCAL int sunboot_write __PR((FILE *outfile)); LOCAL int sunlabel_size __PR((UInt32_t starting_extent)); LOCAL int sunlabel_write __PR((FILE *outfile)); LOCAL int genboot_size __PR((UInt32_t starting_extent)); LOCAL int genboot_write __PR((FILE *outfile)); /* * Set the virtual geometry in the disk label. * If we like to make the geometry variable, we may change * dkl_ncyl and dkl_pcyl later. */ LOCAL void init_sparc_label() { i_to_4_byte(cd_label.dkl_vtoc.v_version, V_VERSION); i_to_2_byte(cd_label.dkl_vtoc.v_nparts, NDKMAP); i_to_4_byte(cd_label.dkl_vtoc.v_sanity, VTOC_SANE); i_to_2_byte(cd_label.dkl_rpm, CD_RPM); i_to_2_byte(cd_label.dkl_pcyl, CD_PCYL); i_to_2_byte(cd_label.dkl_apc, CD_APC); i_to_2_byte(cd_label.dkl_intrlv, CD_INTRLV); i_to_2_byte(cd_label.dkl_ncyl, CD_NCYL); i_to_2_byte(cd_label.dkl_acyl, CD_ACYL); i_to_2_byte(cd_label.dkl_nhead, CD_NHEAD); i_to_2_byte(cd_label.dkl_nsect, CD_NSECT); cd_label.dkl_magic[0] = DKL_MAGIC_0; cd_label.dkl_magic[1] = DKL_MAGIC_1; } LOCAL void init_sunx86_label() { li_to_4_byte(sx86_label.dkl_vtoc.v_sanity, VTOC_SANE); li_to_4_byte(sx86_label.dkl_vtoc.v_version, V_VERSION); li_to_2_byte(sx86_label.dkl_vtoc.v_sectorsz, 512); li_to_2_byte(sx86_label.dkl_vtoc.v_nparts, NX86MAP); li_to_4_byte(sx86_label.dkl_pcyl, CD_PCYL); li_to_4_byte(sx86_label.dkl_ncyl, CD_NCYL); li_to_2_byte(sx86_label.dkl_acyl, CD_ACYL); li_to_2_byte(sx86_label.dkl_bcyl, 0); li_to_4_byte(sx86_label.dkl_nhead, CD_NHEAD); li_to_4_byte(sx86_label.dkl_nsect, CD_NSECT); li_to_2_byte(sx86_label.dkl_intrlv, CD_INTRLV); li_to_2_byte(sx86_label.dkl_skew, 0); li_to_2_byte(sx86_label.dkl_apc, CD_APC); li_to_2_byte(sx86_label.dkl_rpm, CD_RPM); li_to_2_byte(sx86_label.dkl_write_reinstruct, 0); li_to_2_byte(sx86_label.dkl_read_reinstruct, 0); li_to_2_byte(sx86_label.dkl_magic, DKL_MAGIC); } /* * For command line parser: set ASCII label. */ EXPORT int sparc_boot_label(label) char *label; { strncpy(cd_label.dkl_ascilabel, label, 127); cd_label.dkl_ascilabel[127] = '\0'; return (1); } EXPORT int sunx86_boot_label(label) char *label; { strncpy(sx86_label.dkl_vtoc.v_asciilabel, label, 127); sx86_label.dkl_vtoc.v_asciilabel[127] = '\0'; return (1); } /* * Parse the command line argument for boot images. */ EXPORT int scan_sparc_boot(files) char *files; { char *p; int i = 1; struct stat statbuf; int status; extern int use_sparcboot; extern int use_sunx86boot; if (use_sunx86boot) comerrno(EX_BAD, _("-sparc-boot and -sunx86-boot are mutual exclusive.\n")); use_sparcboot++; init_sparc_label(); do { if (i >= NDKMAP) comerrno(EX_BAD, _("Too many boot partitions.\n")); boot_files[i++] = files; if ((p = strchr(files, ',')) != NULL) *p++ = '\0'; files = p; } while (p); i_to_2_byte(cd_label.dkl_vtoc.v_part[0].p_tag, V_USR); i_to_2_byte(cd_label.dkl_vtoc.v_part[0].p_flag, V_RONLY); for (i = 0; i < NDKMAP; i++) { p = boot_files[i]; if (p == NULL || *p == '\0') continue; if (strcmp(p, "...") == '\0') break; status = stat_filter(p, &statbuf); if (status < 0 || access(p, R_OK) < 0) comerr(_("Cannot access '%s'.\n"), p); i_to_4_byte(cd_label.dkl_map[i].dkl_nblk, roundup(statbuf.st_size, CD_CYLSIZE)/512); i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_tag, V_ROOT); i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_flag, V_RONLY); } return (1); } EXPORT int scan_sunx86_boot(files) char *files; { char *p; int i = 0; struct stat statbuf; int status; extern int use_sparcboot; extern int use_sunx86boot; if (use_sparcboot) comerrno(EX_BAD, _("-sparc-boot and -sunx86-boot are mutual exclusive.\n")); use_sunx86boot++; init_sunx86_label(); do { if (i >= NDKMAP) comerrno(EX_BAD, _("Too many boot partitions.\n")); boot_files[i++] = files; if ((p = strchr(files, ',')) != NULL) *p++ = '\0'; files = p; } while (p); li_to_2_byte(sx86_label.dkl_vtoc.v_part[0].p_tag, V_ROOT); /* UFS */ li_to_2_byte(sx86_label.dkl_vtoc.v_part[0].p_flag, V_RONLY); li_to_2_byte(sx86_label.dkl_vtoc.v_part[1].p_tag, V_USR); /* ISO */ li_to_2_byte(sx86_label.dkl_vtoc.v_part[1].p_flag, V_RONLY); li_to_2_byte(sx86_label.dkl_vtoc.v_part[2].p_tag, 0); /* ALL */ li_to_2_byte(sx86_label.dkl_vtoc.v_part[2].p_flag, 0); for (i = 0; i < NDKMAP; i++) { p = boot_files[i]; if (p == NULL || *p == '\0') continue; if (i == 1 || i == 2) { comerrno(EX_BAD, _("Partition %d may not have a filename.\n"), i); } status = stat_filter(p, &statbuf); if (status < 0 || access(p, R_OK) < 0) comerr(_("Cannot access '%s'.\n"), p); li_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size, roundup(statbuf.st_size, CD_CYLSIZE)/512); if (i > 2) { li_to_2_byte(sx86_label.dkl_vtoc.v_part[i].p_tag, V_USR); li_to_2_byte(sx86_label.dkl_vtoc.v_part[i].p_flag, V_RONLY); } } return (1); } /* * Finish the Sun disk label and compute the size of the additional data. */ EXPORT int make_sun_label() { int last; int cyl = 0; int nblk; int bsize; int i; char *p; /* * Compute the size of the padding for the iso9660 image * to allow the next partition to start on a cylinder boundary. */ last = roundup(last_extent, (CD_CYLSIZE/SECTOR_SIZE)); i_to_4_byte(cd_label.dkl_map[0].dkl_nblk, last*4); bsize = 0; for (i = 0; i < NDKMAP; i++) { p = boot_files[i]; if (p != NULL && strcmp(p, "...") == '\0') { dup_sun_label(i); break; } if ((nblk = a_to_4_byte(cd_label.dkl_map[i].dkl_nblk)) == 0) continue; i_to_4_byte(cd_label.dkl_map[i].dkl_cylno, cyl); cyl += nblk / (CD_CYLSIZE/512); if (i > 0) bsize += nblk; } bsize /= 4; return (last-last_extent+bsize); } /* * A typical Solaris boot/install CD from a Sun CD set looks * this way: * * UFS Part 0 tag 2 flag 10 start 3839 size 1314560 * ISO Part 1 tag 4 flag 10 start 0 size 3839 * ALL Part 2 tag 0 flag 0 start 0 size 1318400 */ EXPORT int make_sunx86_label() { int last; int cyl = 0; int nblk; int bsize; int i; int partoff = 1; /* The offset of the Solaris 0x82 partition */ /* * Compute the size of the padding for the iso9660 image * to allow the next partition to start on a cylinder boundary. */ last = roundup(last_extent, (CD_CYLSIZE/SECTOR_SIZE)); li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_size, last*4); /* * Note that the Solaris fdisk partition with fdisk signature 0x82 * is created at fixed offset 1 sector == 512 Bytes by this * implementation. * We need subtract this partition offset from all absolute * partition offsets in order to get offsets relative to the * Solaris primary partition. */ bsize = 0; for (i = 0; i < NDKMAP; i++) { if (i == 2) /* Never include the whole disk in */ continue; /* size/offset computations */ if ((nblk = la_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size)) == 0) continue; li_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_start, cyl*(CD_CYLSIZE/512)-partoff); cyl += nblk / (CD_CYLSIZE/512); if (i == 0 || i > 2) bsize += nblk; } li_to_4_byte(sx86_label.dkl_vtoc.v_part[0].p_start, last*4-partoff); li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_start, 0); li_to_4_byte(sx86_label.dkl_vtoc.v_part[1].p_size, last*4-partoff); li_to_4_byte(sx86_label.dkl_vtoc.v_part[2].p_start, 0); li_to_4_byte(sx86_label.dkl_vtoc.v_part[2].p_size, last*4+bsize); fdisk_part.part[0].pr_status = STATUS_ACTIVE; fdisk_part.part[0].pr_type = TYPE_SOLARIS; li_to_4_byte(fdisk_part.part[0].pr_partoff, partoff); li_to_4_byte(fdisk_part.part[0].pr_nsect, last*4+bsize-partoff); fdisk_part.magic[0] = 0x55; fdisk_part.magic[1] = 0xAA; bsize /= 4; return (last-last_extent+bsize); } /* * Duplicate a partition of the Sun disk label until all partitions are filled up. */ LOCAL void dup_sun_label(part) int part; { int cyl; int nblk; int i; if (part < 1 || part >= NDKMAP) part = 1; cyl = a_to_4_byte(cd_label.dkl_map[part-1].dkl_cylno); nblk = a_to_4_byte(cd_label.dkl_map[part-1].dkl_nblk); for (i = part; i < NDKMAP; i++) { i_to_4_byte(cd_label.dkl_map[i].dkl_cylno, cyl); i_to_4_byte(cd_label.dkl_map[i].dkl_nblk, nblk); i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_tag, V_ROOT); i_to_2_byte(cd_label.dkl_vtoc.v_part[i].p_flag, V_RONLY); } } /* * Write out Sun boot partitions. */ LOCAL int sunboot_write(outfile) FILE *outfile; { char buffer[SECTOR_SIZE]; int i; int n; int nblk; int amt; int f; char *p; memset(buffer, 0, sizeof (buffer)); /* * Write padding to the iso9660 image to allow the * boot partitions to start on a cylinder boundary. */ amt = roundup(last_extent_written, (CD_CYLSIZE/SECTOR_SIZE)) - last_extent_written; for (n = 0; n < amt; n++) { xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE); last_extent_written++; } if (use_sunx86boot) i = 0; else i = 1; for (; i < NDKMAP; i++) { if (use_sunx86boot && (i == 1 || i == 2)) continue; p = boot_files[i]; if (p == NULL || *p == '\0') continue; if (p != NULL && strcmp(p, "...") == '\0') break; if (use_sunx86boot) { if ((nblk = la_to_4_byte(sx86_label.dkl_vtoc.v_part[i].p_size)) == 0) continue; } else { if ((nblk = a_to_4_byte(cd_label.dkl_map[i].dkl_nblk)) == 0) continue; } if ((f = open(boot_files[i], O_RDONLY| O_BINARY)) < 0) comerr(_("Cannot open '%s'.\n"), boot_files[i]); amt = nblk / 4; for (n = 0; n < amt; n++) { memset(buffer, 0, sizeof (buffer)); if (read(f, buffer, SECTOR_SIZE) < 0) comerr(_("Read error on '%s'.\n"), boot_files[i]); xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE); last_extent_written++; } close(f); } fprintf(stderr, _("Total extents including %s boot = %u\n"), use_sunx86boot ? "Solaris x86":"sparc", last_extent_written - session_start); return (0); } /* * Do size management for the Sun disk label that is located in the first * sector of a disk. */ LOCAL int sunlabel_size(starting_extent) UInt32_t starting_extent; { if (last_extent != session_start) comerrno(EX_BAD, _("Cannot create sparc boot on offset != 0.\n")); last_extent++; return (0); } /* * Compute the checksum and write a Sun disk label to the first sector * of the disk. * If the -generic-boot option has been specified too, overlay the * Sun disk label on the first 512 bytes of the generic boot code. */ LOCAL int sunlabel_write(outfile) FILE *outfile; { char buffer[SECTOR_SIZE]; register char *p; register short count = (512/2) - 1; int f; memset(buffer, 0, sizeof (buffer)); if (genboot_image) { if ((f = open(genboot_image, O_RDONLY| O_BINARY)) < 0) comerr(_("Cannot open '%s'.\n"), genboot_image); if (read(f, buffer, SECTOR_SIZE) < 0) comerr(_("Read error on '%s'.\n"), genboot_image); close(f); } if (use_sunx86boot) { if (sx86_label.dkl_vtoc.v_asciilabel[0] == '\0') strcpy(sx86_label.dkl_vtoc.v_asciilabel, CD_X86LABEL); p = (char *)&sx86_label; sx86_label.dkl_cksum[0] = 0; sx86_label.dkl_cksum[1] = 0; while (count-- > 0) { sx86_label.dkl_cksum[0] ^= *p++; sx86_label.dkl_cksum[1] ^= *p++; } memcpy(&buffer[0x1BE], fdisk_part.part, sizeof (fdisk_part.part)); p = &buffer[510]; *p++ = 0x55; *p = 0xAA; memcpy(&buffer[1024], &sx86_label, 512); } else { /* * If we don't already have a Sun disk label text * set up the default. */ if (cd_label.dkl_ascilabel[0] == '\0') strcpy(cd_label.dkl_ascilabel, CD_DEFLABEL); p = (char *)&cd_label; cd_label.dkl_cksum[0] = 0; cd_label.dkl_cksum[1] = 0; while (count--) { cd_label.dkl_cksum[0] ^= *p++; cd_label.dkl_cksum[1] ^= *p++; } memcpy(buffer, &cd_label, 512); } xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE); last_extent_written++; return (0); } /* * Do size management for the generic boot code on sectors 0..16. */ LOCAL int genboot_size(starting_extent) UInt32_t starting_extent; { if (last_extent > (session_start + 1)) comerrno(EX_BAD, _("Cannot create generic boot on offset != 0.\n")); last_extent = session_start + 16; return (0); } /* * Write the generic boot code to sectors 0..16. * If there is a Sun disk label, start writing at sector 1. */ LOCAL int genboot_write(outfile) FILE *outfile; { char buffer[SECTOR_SIZE]; int i; int f; if ((f = open(genboot_image, O_RDONLY| O_BINARY)) < 0) comerr(_("Cannot open '%s'.\n"), genboot_image); for (i = 0; i < 16; i++) { memset(buffer, 0, sizeof (buffer)); if (read(f, buffer, SECTOR_SIZE) < 0) comerr(_("Read error on '%s'.\n"), genboot_image); if (i != 0 || last_extent_written == session_start) { xfwrite(buffer, SECTOR_SIZE, 1, outfile, 0, FALSE); last_extent_written++; } } close(f); return (0); } struct output_fragment sunboot_desc = {NULL, NULL, NULL, sunboot_write, "Sun Boot" }; struct output_fragment sunlabel_desc = {NULL, sunlabel_size, NULL, sunlabel_write, "Sun Disk Label" }; struct output_fragment genboot_desc = {NULL, genboot_size, NULL, genboot_write, "Generic Boot" };