1 /* ----------------------------------------------------------------------- *
3 * Copyright 1998-2007 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
14 * syslinux.c - Linux installer program for SYSLINUX
28 const char *program
= "syslinux"; /* Name of program */
32 # define dprintf printf
34 # define dprintf(...) ((void)0)
37 void __attribute__((noreturn
)) usage(void)
39 puts("Usage: syslinux [-sfma][-d directory] <drive>: [bootsecfile]\n");
43 void unlock_device(int);
45 void __attribute__((noreturn
)) die(const char *msg
)
54 void warning(const char *msg
)
56 puts("syslinux: warning: ");
62 * read/write wrapper functions
64 int creat(const char *filename
, int mode
)
69 dprintf("creat(\"%s\", 0x%x)\n", filename
, mode
);
72 asm volatile("int $0x21 ; setc %0"
73 : "=bcdm" (err
), "+a" (rv
)
74 : "c" (mode
), "d" (filename
));
76 dprintf("rv = %d\n", rv
);
77 die("cannot open ldlinux.sys");
87 dprintf("close(%d)\n", fd
);
89 asm volatile("int $0x21"
93 /* The only error MS-DOS returns for close is EBADF,
94 and we really don't care... */
97 int rename(const char *oldname
, const char *newname
)
99 uint16_t rv
= 0x5600; /* Also support 43FFh? */
102 dprintf("rename(\"%s\", \"%s\")\n", oldname
, newname
);
104 asm volatile("int $0x21 ; setc %0"
105 : "=bcdm" (err
), "+a" (rv
)
106 : "d" (oldname
), "D" (newname
));
109 dprintf("rv = %d\n", rv
);
110 warning("cannot move ldlinux.sys");
117 ssize_t
write_file(int fd
, const void *buf
, size_t count
)
123 dprintf("write_file(%d,%p,%u)\n", fd
, buf
, count
);
127 asm volatile("int $0x21 ; setc %0"
128 : "=abcdm" (err
), "+a" (rv
)
129 : "b" (fd
), "c" (count
), "d" (buf
));
130 if ( err
|| rv
== 0 )
131 die("file write error");
140 static inline __attribute__((const)) uint16_t data_segment(void)
144 asm("movw %%ds,%0" : "=rm" (ds
));
149 uint32_t startsector
;
151 uint16_t bufoffs
, bufseg
;
152 } __attribute__((packed
));
154 void write_device(int drive
, const void *buf
, size_t nsecs
, unsigned int sector
)
159 dprintf("write_device(%d,%p,%u,%u)\n", drive
, buf
, nsecs
, sector
);
161 dio
.startsector
= sector
;
163 dio
.bufoffs
= (uintptr_t)buf
;
164 dio
.bufseg
= data_segment();
166 asm volatile("int $0x26 ; setc %0 ; popfw"
168 : "a" (drive
-1), "b" (&dio
), "c" (-1), "d" (buf
), "m" (dio
));
171 die("sector write error");
174 void read_device(int drive
, const void *buf
, size_t nsecs
, unsigned int sector
)
179 dprintf("read_device(%d,%p,%u,%u)\n", drive
, buf
, nsecs
, sector
);
181 dio
.startsector
= sector
;
183 dio
.bufoffs
= (uintptr_t)buf
;
184 dio
.bufseg
= data_segment();
186 asm volatile("int $0x25 ; setc %0 ; popfw"
188 : "a" (drive
-1), "b" (&dio
), "c" (-1), "d" (buf
), "m" (dio
));
191 die("sector read error");
194 /* Both traditional DOS and FAT32 DOS return this structure, but
195 FAT32 return a lot more data, so make sure we have plenty of space */
196 struct deviceparams
{
202 uint16_t bytespersec
;
206 uint16_t rootdirents
;
210 uint16_t secpertrack
;
213 uint32_t hugesectors
;
214 uint8_t lotsofpadding
[224];
215 } __attribute__((packed
));
217 uint32_t get_partition_offset(int drive
)
221 struct deviceparams dp
;
223 dp
.specfunc
= 1; /* Get current information */
226 asm volatile("int $0x21 ; setc %0"
227 : "=abcdm" (err
), "+a" (rv
), "=m" (dp
)
228 : "b" (drive
), "c" (0x0860), "d" (&dp
));
231 return dp
.hiddensecs
;
234 asm volatile("int $0x21 ; setc %0"
235 : "=abcdm" (err
), "+a" (rv
), "=m" (dp
)
236 : "b" (drive
), "c" (0x4860), "d" (&dp
));
239 return dp
.hiddensecs
;
241 die("could not find partition start offset");
248 uint16_t firstsector
;
250 uint16_t bufferoffset
;
252 } __attribute__((packed
));
254 static struct rwblock mbr
= {
258 .firstsector
= 0, /* MS-DOS, unlike the BIOS, zero-base sectors */
264 void write_mbr(int drive
, const void *buf
)
269 dprintf("write_mbr(%d,%p)\n", drive
, buf
);
271 mbr
.bufferoffset
= (uintptr_t)buf
;
272 mbr
.bufferseg
= data_segment();
275 asm volatile("int $0x21 ; setc %0"
276 : "=abcdm" (err
), "+a" (rv
)
277 : "c" (0x0841), "d" (&mbr
), "b" (drive
), "m" (mbr
));
283 asm volatile("int $0x21 ; setc %0"
284 : "=abcdm" (err
), "+a" (rv
)
285 : "c" (0x4841), "d" (&mbr
), "b" (drive
), "m" (mbr
));
288 die("mbr write error");
291 void read_mbr(int drive
, const void *buf
)
296 dprintf("read_mbr(%d,%p)\n", drive
, buf
);
298 mbr
.bufferoffset
= (uintptr_t)buf
;
299 mbr
.bufferseg
= data_segment();
302 asm volatile("int $0x21 ; setc %0"
303 : "=abcdm" (err
), "+a" (rv
)
304 : "c" (0x0861), "d" (&mbr
), "b" (drive
), "m" (mbr
));
310 asm volatile("int $0x21 ; setc %0"
311 : "=abcdm" (err
), "+a" (rv
)
312 : "c" (0x4861), "d" (&mbr
), "b" (drive
), "m" (mbr
));
315 die("mbr read error");
318 /* This call can legitimately fail, and we don't care, so ignore error return */
319 void set_attributes(const char *file
, int attributes
)
321 uint16_t rv
= 0x4301;
323 dprintf("set_attributes(\"%s\", 0x%02x)\n", file
, attributes
);
325 asm volatile("int $0x21"
327 : "c" (attributes
), "d" (file
));
331 * Version of the read_device function suitable for libfat
333 int libfat_xpread(intptr_t pp
, void *buf
, size_t secsize
, libfat_sector_t sector
)
335 read_device(pp
, buf
, 1, sector
);
339 static inline void get_dos_version(void)
341 uint16_t ver
= 0x3001;
342 asm("int $0x21 ; xchgb %%ah,%%al" : "+a" (ver
) : : "ebx", "ecx");
344 dprintf("DOS version %d.%d\n", (dos_version
>> 8), dos_version
& 0xff);
347 /* The locking interface relies on static variables. A massive hack :( */
348 static uint16_t lock_level
;
350 static inline void set_lock_device(uint8_t device
)
355 void lock_device(int level
)
361 if ( dos_version
< 0x0700 )
362 return; /* Win9x/NT only */
365 /* DOS 7.10 = Win95 OSR2 = first version with FAT32 */
366 lock_call
= (dos_version
>= 0x0710) ? 0x484A : 0x084A;
368 lock_call
= 0x084A; /* MSDN says this is OK for all filesystems */
371 while ( (lock_level
>> 8) < level
) {
372 uint16_t new_level
= lock_level
+ 0x0100;
373 dprintf("Trying lock %04x...\n", new_level
);
375 asm volatile("int $0x21 ; setc %0"
376 : "=abcdm" (err
), "+a" (rv
)
377 : "b" (new_level
), "c" (lock_call
), "d"(0x0001));
379 /* rv == 0x0001 means this call is not supported, if so we
380 assume locking isn't needed (e.g. Win9x in DOS-only mode) */
384 die("could not lock device");
387 lock_level
= new_level
;
392 void unlock_device(int level
)
396 uint16_t unlock_call
;
398 if ( dos_version
< 0x0700 )
399 return; /* Win9x/NT only */
402 /* DOS 7.10 = Win95 OSR2 = first version with FAT32 */
403 unlock_call
= (dos_version
>= 0x0710) ? 0x486A : 0x086A;
405 unlock_call
= 0x086A; /* MSDN says this is OK for all filesystems */
408 while ( (lock_level
>> 8) > level
) {
409 uint16_t new_level
= lock_level
- 0x0100;
411 asm volatile("int $0x21 ; setc %0"
412 : "=abcdm" (err
), "+a" (rv
)
413 : "b" (new_level
), "c" (unlock_call
));
414 lock_level
= new_level
;
420 * This function does any desired MBR manipulation; called with the device lock held.
423 uint8_t active
; /* Active flag */
424 uint8_t bhead
; /* Begin head */
425 uint8_t bsector
; /* Begin sector */
426 uint8_t bcylinder
; /* Begin cylinder */
427 uint8_t filesystem
; /* Filesystem value */
428 uint8_t ehead
; /* End head */
429 uint8_t esector
; /* End sector */
430 uint8_t ecylinder
; /* End cylinder */
431 uint32_t startlba
; /* Start sector LBA */
432 uint32_t sectors
; /* Length in sectors */
433 } __attribute__((packed
));
435 static void adjust_mbr(int device
, int writembr
, int set_active
)
437 static unsigned char sectbuf
[512];
440 if ( !writembr
&& !set_active
)
441 return; /* Nothing to do */
443 read_mbr(device
, sectbuf
);
446 memcpy(sectbuf
, syslinux_mbr
, syslinux_mbr_len
);
447 *(uint16_t *)(sectbuf
+510) = 0xaa55;
451 uint32_t offset
= get_partition_offset(device
);
452 struct mbr_entry
*me
= (struct mbr_entry
*)(sectbuf
+446);
455 for ( i
= 0 ; i
< 4 ; i
++ ) {
456 if ( me
->startlba
== offset
) {
466 die("partition not found");
467 } else if ( found
> 1 ) {
468 die("multiple aliased partitions found");
472 write_mbr(device
, sectbuf
);
475 int main(int argc
, char *argv
[])
477 static unsigned char sectbuf
[512];
479 static char ldlinux_name
[] = "@:\\ldlinux.sys";
481 int force
= 0; /* -f (force) option */
482 struct libfat_filesystem
*fs
;
483 libfat_sector_t s
, *secp
, sectors
[65]; /* 65 is maximum possible */
484 int32_t ldlinux_cluster
;
486 const char *device
= NULL
, *bootsecfile
= NULL
;
489 int writembr
= 0; /* -m (write MBR) option */
490 int set_active
= 0; /* -a (set partition active) option */
491 const char *subdir
= NULL
;
493 dprintf("argv = %p\n", argv
);
494 for ( i
= 0 ; i
<= argc
; i
++ )
495 dprintf("argv[%d] = %p = \"%s\"\n", i
, argv
[i
], argv
[i
]);
497 (void)argc
; /* Unused */
501 for ( argp
= argv
+1 ; *argp
; argp
++ ) {
502 if ( **argp
== '-' ) {
509 case 's': /* Use "safe, slow and stupid" code */
510 syslinux_make_stupid();
512 case 'f': /* Force install */
515 case 'm': /* Write MBR */
518 case 'a': /* Set partition active */
544 * Figure out which drive we're talking to
546 dev_fd
= (device
[0] & ~0x20) - 0x40;
547 if ( dev_fd
< 1 || dev_fd
> 26 || device
[1] != ':' || device
[2] )
550 set_lock_device(dev_fd
);
552 lock_device(2); /* Make sure we can lock the device */
553 read_device(dev_fd
, sectbuf
, 1, 0);
557 * Check to see that what we got was indeed an MS-DOS boot sector/superblock
559 if( (errmsg
= syslinux_check_bootsect(sectbuf
)) ) {
566 ldlinux_name
[0] = dev_fd
| 0x40;
568 set_attributes(ldlinux_name
, 0);
569 fd
= creat(ldlinux_name
, 0x07); /* SYSTEM HIDDEN READONLY */
570 write_file(fd
, syslinux_ldlinux
, syslinux_ldlinux_len
);
574 * Now, use libfat to create a block map. This probably
575 * should be changed to use ioctl(...,FIBMAP,...) since
576 * this is supposed to be a simple, privileged version
580 fs
= libfat_open(libfat_xpread
, dev_fd
);
581 ldlinux_cluster
= libfat_searchdir(fs
, 0, "LDLINUX SYS", NULL
);
584 s
= libfat_clustertosector(fs
, ldlinux_cluster
);
585 while ( s
&& nsectors
< 65 ) {
588 s
= libfat_nextsector(fs
, s
);
593 * If requested, move ldlinux.sys
596 char new_ldlinux_name
[160];
597 char *cp
= new_ldlinux_name
+3;
601 new_ldlinux_name
[0] = dev_fd
| 0x40;
602 new_ldlinux_name
[1] = ':';
603 new_ldlinux_name
[2] = '\\';
605 for (sd
= subdir
; *sd
; sd
++) {
608 if (c
== '/' || c
== '\\') {
620 /* Skip if subdirectory == root */
621 if (cp
> new_ldlinux_name
+3) {
625 memcpy(cp
, "ldlinux.sys", 12);
627 set_attributes(ldlinux_name
, 0);
628 if (rename(ldlinux_name
, new_ldlinux_name
))
629 set_attributes(ldlinux_name
, 0x07);
631 set_attributes(new_ldlinux_name
, 0x07);
636 * Patch ldlinux.sys and the boot sector
638 syslinux_patch(sectors
, nsectors
);
641 * Write the now-patched first sector of ldlinux.sys
644 write_device(dev_fd
, syslinux_ldlinux
, 1, sectors
[0]);
647 * Muck with the MBR, if desired, while we hold the lock
649 adjust_mbr(dev_fd
, writembr
, set_active
);
652 * To finish up, write the boot sector
655 /* Read the superblock again since it might have changed while mounted */
656 read_device(dev_fd
, sectbuf
, 1, 0);
658 /* Copy the syslinux code into the boot sector */
659 syslinux_make_bootsect(sectbuf
);
661 /* Write new boot sector */
664 fd
= creat(bootsecfile
, 0x20); /* ARCHIVE */
665 write_file(fd
, sectbuf
, 512);
668 write_device(dev_fd
, sectbuf
, 1, 0);