/* * syslinux.c * * Creates a Linux bootable MS-DOS floppy * * Copyright (C) 1994-96 H. Peter Anvin * */ #include #include #include #include extern char boot_sector[]; extern char ldlinux_sys[]; extern unsigned int ldlinux_len; #define BOOTSECT_LEN 512 /* Length of the boot sector */ #define RETRIES 4 /* Make 4 read/write attempts */ typedef unsigned int word; typedef unsigned char byte; typedef unsigned long dword; struct bootsector { byte jump[3]; byte oemname[8]; word bytespersec; byte secperclust; word ressectors; byte fats; word rootdirents; word sectors; byte media; word fatsecs; word secpertrack; word heads; dword hiddensecs; dword hugesectors; byte drivenumber; byte reserved1; byte bootsignature; dword volumeid; byte volumelabel[11]; byte filesystype[8]; byte bootstrap_code[448]; word boot_magic; }; struct diskio { unsigned long sector; unsigned count; void far *buffer; }; /* * read_boot_sect: read the boot sector of the specified drive into the * given buffer, using the DOS function INT 25h */ int read_boot_sect(int drive, void *buffer, int huge_disk) { int retry_ctr, err; struct diskio huge_buf; unsigned sectors; if ( huge_disk ) { huge_buf.buffer = buffer; huge_buf.sector = 0; huge_buf.count = 1; buffer = &huge_buf; sectors = 0xFFFF; } else sectors = 1; /* Read one sector */ err = 0; for ( retry_ctr = RETRIES ; retry_ctr ; retry_ctr-- ) { _BX = (unsigned) buffer; _CX = sectors; _DX = 0; /* Sector zero = boot sector */ _AL = drive; geninterrupt(0x25); asm pop ax; /* Flags left on the stack */ asm sti; /* Make sure interrupts are on */ asm jnc read_ok; } err = 1; /* Operation failed */ read_ok: return err; } /* * write_boot_sect: read the boot sector of the specified drive into the * given buffer, using the DOS function INT 26h */ int write_boot_sect(int drive, void *buffer, int huge_disk) { int retry_ctr, err, flg; struct diskio huge_buf; unsigned sectors; if ( huge_disk ) { huge_buf.buffer = buffer; huge_buf.sector = 0; huge_buf.count = 1; buffer = &huge_buf; sectors = 0xFFFF; } else sectors = 1; /* Write one sector */ err = 0; for ( retry_ctr = RETRIES ; retry_ctr ; retry_ctr-- ) { _BX = (unsigned) buffer; _CX = sectors; _DX = 0; /* Sector zero = boot sector */ _AL = drive; geninterrupt(0x26); asm pop ax; /* Flags left on the stack */ asm sti; /* Make sure interrupts are on */ asm jnc write_ok; } err = 1; /* Operation failed */ write_ok: return err; } /* * disk_is_huge: Check if a disk is larger than 64K sectors (32 Mb) */ int disk_is_huge(int drive, int *sectsize) { unsigned secperclust, bytespersec, clusters; unsigned long sectors; _DL = drive+1; _AH = 0x36; geninterrupt(0x21); *sectsize = _CX; asm mul dx; return _DX; } /* * usage: Complain to the user, and exit */ void usage(void) { fprintf(stderr, "Usage:\n" " SYSLINUX drive:\n"); exit(1); } /* * Main program * */ int main(int argc, char *argv[]) { int drive, huge_disk, sectsize; char *cmd_ptr; int cmd_maxlen, cmd_len; struct bootsector *orig_sect; static char *ld_name = "?:\LDLINUX.SYS"; FILE *ld; int i; unsigned dosattr; /* If no drive specified */ if ( argc < 2 || strlen(argv[1]) != 2 || argv[1][1] != ':' ) usage(); drive = argv[1][0] | 0x20; if ( drive < 'a' || drive > 'z' ) usage(); if ( argc > 2 ) { fprintf(stderr, "Warning: specifying the default command on the SYSLINUX command\n" " line is no longer supported. Create a SYSLINUX.CFG file\n" " with a \"default\" line instead. The default kernel name\n" " in the absence of a SYSLINUX.CFG file is now always\n" " \"linux\".\n"); } drive -= 'a'; huge_disk = disk_is_huge(drive, §size); orig_sect = malloc(sectsize); if ( !orig_sect ) { fprintf(stderr, "Error: could not malloc %d bytes\n", sectsize); exit(1); } /* Read in the original boot sector */ if ( read_boot_sect(drive, orig_sect, huge_disk) ) { fprintf(stderr, "Error: could not read original boot sector\n"); exit(1); } /* Make sure we're dealing with something sane here */ if ( orig_sect->bytespersec != 512 ) { fprintf(stderr, "Error: Bytes per sector is not 512\n"); exit(1); } if ( orig_sect->boot_magic != 0xAA55 ) { fprintf(stderr, "Error: Boot sector signature not found on disk\n"); exit(1); } if ( orig_sect->bootsignature == 0x29 && memcmp(orig_sect->filesystype, "FAT12 ", 8) ) { fprintf(stderr, "Error: Filesystem is not type FAT12 (or " "ancient format program)\n"); exit(1); } /* Very well, copy the thing */ memcpy( &boot_sector[0x0B], &(orig_sect->bytespersec), 0x33 ); /* Write out the new boot sector */ if ( write_boot_sect(drive, boot_sector, huge_disk) ) { fprintf(stderr, "Error: could not write new boot sector\n"); exit(1); } /* Get full name of LDLINUX.SYS */ *ld_name = drive+'A'; /* Clear readonly, hidden, system flags (but remember them) */ /* If these call fail, assume it is because the file doesn't exist */ if (_dos_getfileattr(ld_name, &dosattr)) dosattr = FA_ARCH; /* default attributes */ else { dosattr &= ~(FA_RDONLY|FA_HIDDEN|FA_SYSTEM); _dos_setfileattr(ld_name, dosattr); } unlink(ld_name); /* If it exists, delete it */ ld = fopen(ld_name, "wb"); if ( !ld ) { perror("Writing LDLINUX.SYS"); exit(1); } if ( fwrite(ldlinux_sys,1,ldlinux_len,ld) < ldlinux_len ) { perror("Writing LDLINUX.SYS"); fclose(ld); exit(1); } fclose(ld); /* Set readonly flag - if you want to have the hidden flag set by default, you can add FA_HIDDEN here. I suggest not setting FA_SYSTEM since it implies a file that must not be moved, which is not the case for LDLINUX.SYS. */ _dos_setfileattr(ld_name, dosattr|FA_ARCH|FA_RDONLY); fprintf(stderr, "The Linux boot system was successfully installed.\n"); return 0; }