--- /sys/man/8/9boot Tue Jul 30 01:21:33 2013 +++ /sys/man/8/9boot Wed Sep 4 07:17:33 2013 @@ -1,6 +1,6 @@ .TH 9BOOT 8 .SH NAME -9boot, 9bootpbs, 9load, 9loadusb \- PC bootstrap programs +9boot, 9bootpbs, 9load, 9loadusb, pbs \- PC bootstrap programs .SH SYNOPSIS .I none .SH DESCRIPTION @@ -18,7 +18,7 @@ .I 9load and .I 9loadusb -are less commonly used variants +are less-commonly-used variants that reside in a FAT file system under the name .L 9load and bootstrap Plan 9. @@ -66,28 +66,55 @@ .\" .IR 9load , .\" kernel .PP +In summary, +Plan 9 is usually booted on a PC +by using a PXE-capable BIOS to boot +.I 9boot +directly over the ethernet. +File servers that must be able to boot when other machines are down +boot directly from a Plan 9 disk partition +.\" or boot floppy +prepared using +.B format +to install the appropriate files and bootstrap sectors +(see +.IR prep (8)). +.PP Details follow. -.SS "Loading these bootstraps" +.SS Kernel loading .I 9boot is a bootstrap program that loads and starts a program, typically the kernel, on a PC. It is run by the PXE boot ROM of a PC, which loads .I 9boot -at location +at physical address .B 0x7C00 (31K). +When it starts running, +it switches to 32-bit mode. +It then double maps the first 16Mb of physical memory to +virtual addresses +.B 0 +and +.BR 0x80000000 . +Only devices which can be automatically configured, +e.g. most PCI ethernet adapters, +will be recognised. +If the file +.BI /cfg/pxe/ ether +can be located via a DHCP server, +where +.I ether +is the lower-case MAC address of a recognised ethernet adapter, +the contents are obtained by TFTP and used as a +.IR plan9.ini (8). .I 9boot -reads a -.IR plan.ini (8) -file from -.B /cfg/pxe -via PXE, -then loads the named +then loads the .I bootfile -via TFTP, +named within via TFTP, trying each ethernet in sequence, -at the entry address specified by the header, +at the entry address specified by the kernel executable's header, usually virtual .BR 0xF0100020 . After loading, @@ -95,78 +122,136 @@ creates a Gnu Multiboot header in low memory for the benefit of the loaded kernel and -control is passed to the entry location. +control is passed to the entry location +in 32-bit protected mode, even for 64-bit kernels. So far, only .B amd64 kernels expect Multiboot headers. .PP +Some options in +.B plan9.ini +are used by +.IR 9boot : +.TF bootfile=manual +.TP +.B console +.TP +.B baud +Specifies the console device and baud rate if not a display. +.TP +.BI ether n +Ethernet interfaces. These can be used to load the +.I bootfile +over a network. +.TP +.BI bootfile= bootfile +Specifies the +.IR bootfile . +.ig +.TP +.B bootfile=auto +Default. +.TP +.B bootfile=local +Like +.IR auto , +but do not attempt to load over the network. +.. +.TP +.B bootfile=manual +After determining which devices are available for loading from, +enter prompt mode. +.PD +.PP .I 9load is a similar bootstrap program, -run by the PC partition boot sector program (PBS), +loaded by the PC partition boot sector program (PBS), which usually resides in the first -sector of the active partition. -A copy of the Plan 9 PBS is kept in -.BR /386/pbs , -but due to the ``cylinder-head-sector'' (CHS) addressing mode of old BIOSes, it can only -operate up to 8.5GB into the disk. -Plan 9 partitions further into the disk -can only be booted using -.BR /386/pbslba , -and then only if the machine's BIOS supports -linear block addressing (LBA) mode for disk transfers. -.PP -When booting from disk, -.\" or floppy, -the BIOS loads the -first sector of the medium at location -.BR 0x7C00 . -In the case of a disk, it is the master boot record (MBR). -.\" In the case of a floppy, this is the PBS. -The MBR copies itself to address -.BR 0x600 , -finds the active partition and loads its PBS at address -.BR 0x7C00 . -A copy of the Plan 9 MBR is kept in -.BR /386/mbr ; -some commercial MBRs cannot read sectors -past 2GB. -The Plan 9 MBR can read sectors up to 8.5GB into -the disk, and further if the BIOS supports LBA. -The single file -.B /386/mbr -detects whether the BIOS supports LBA and -acts appropriately, defaulting to CHS mode -when LBA is not present. -The PBSs cannot do this due to code size limitations. -The Plan 9 MBR is suitable for booting non-Plan-9 -operating systems, -and (modulo the large disk constraints just described) -non-Plan-9 MBRs are suitable for booting Plan 9. +sector of the active disk partition. +It is initially loaded at physical address +.BR 0x10000 (64K); +it begins execution at virtual address +.BR 0x80010000 . +In order to find configuration information, +.I 9load +searches all units on devices +.\" .BR fd +.\" and +.B sd?[0-9]* +(all +.B sd +devices), +for a file called +.\" .B plan9\eplan9.ini +.\" or +.B plan9.ini +(see +.IR plan9.ini (8)) +on a FAT partition named +.B dos +or +.BR 9fat . +If one is found, searching stops and the file is read into memory +at physical address +.B 0x1200 +where it can be found later by any loaded +.IR bootfile . .PP +When the search for +.B plan9.ini +is done, .I 9load -begins execution at virtual address -.B 0x80010000 -(physical 64K) and -loads the +proceeds to determine which bootfile to load. +If there was no .I bootfile -at the entry address specified by the header, -usually virtual -.BR 0xF0100020 . -After loading, control is passed to the entry location. +option, +.I 9load +searches +.B sd?[0-9]* +FAT partitions for a kernel +(any file named +.BR 9pc* , +.B 9k8* +or +.BR 9k10* ) +and if it finds exactly one kernel in a given FAT partition, +chooses it. +.I 9load +then attempts to load the +.IR bootfile . +.ig +unless +the +.B bootfile=manual +option was given, in which case prompt mode is entered immediately. +.. +.ig +If the default device is +.BR fd , +.I 9load +will prompt the user for input before proceeding with the +default bootfile load after 5 seconds; +this prompt is omitted if a +.I bootfile +option +was given. +.. .PP -In summary, -Plan 9 is usually booted on a PC -by using a PXE-capable BIOS to boot -.I 9boot -directly over the ethernet. -File servers that must be able to boot when other machines are down -boot directly from a Plan 9 disk partition -.\" or boot floppy -prepared using -.B format -to install the appropriate files and bootstrap sectors -(see -.IR prep (8)). +.I 9load +prints the list of available +.IR device s +and +enters prompt mode on encountering any error +or if directed to do so by a +.B bootfile=manual +option. +In prompt mode, the user is required to type +a +.IB bootfile +in response to the +.L Boot +.L from: +prompt. .br .ne 4 .SS Bootfile @@ -299,150 +384,44 @@ that uses .BI bios n to read from a FAT file system. -.SS Kernel loading -.I 9boot -is initially loaded by the PXE BIOS at physical address -.BR 0x7C00 . -When it starts running, -it switches to 32-bit mode. -It then double maps the first 16Mb of physical memory to -virtual addresses -.B 0 -and -.BR 0x80000000 . -Only devices which can be automatically configured, -e.g. most PCI ethernet adapters, -will be recognised. -If the file -.BI /cfg/pxe/ ether -can be located via a DHCP server, -where -.I ether -is the lower-case MAC address of a recognised ethernet adapter, -the contents are obtained by TFTP and used as a -.IR plan9.ini . -.PP -.I 9load -differs slightly in operation from -.IR 9boot . -It is initially loaded by a partition boot sector at physical address -.BR 0x10000 . -In order to find configuration information, -.I 9load -searches all units on devices -.\" .BR fd -.\" and -.B sd?[0-9]* -(all -.B sd -devices), -for a file called -.\" .B plan9\eplan9.ini -.\" or -.B plan9.ini -(see -.IR plan9.ini (8)) -on a FAT partition named -.B dos -or -.BR 9fat . -If one is found, searching stops and the file is read into memory -at physical address -.B 0x1200 -where it can be found later by any loaded -.IR bootfile . -Some options in -.B plan9.ini -are used by -.IR 9boot : -.TF bootfile=manual -.TP -.B console -.TP -.B baud -Specifies the console device and baud rate if not a display. -.TP -.BI ether n -Ethernet interfaces. These can be used to load the -.I bootfile -over a network. -.TP -.BI bootfile= bootfile -Specifies the -.IR bootfile . -This option is overridden by a command-line argument. -.ig -.TP -.B bootfile=auto -Default. -.TP -.B bootfile=local -Like -.IR auto , -but do not attempt to load over the network. -.. -.TP -.B bootfile=manual -After determining which devices are available for loading from, -enter prompt mode. -.PD -.PP -When the search for -.B plan9.ini -is done, -.I 9load -proceeds to determine which bootfile to load. -If there was no -.I bootfile -option, -.I 9load -searches -.B sd?[0-9]* -FAT partitions for a kernel -(any file named -.BR 9pc* , -.B 9k8* -or -.BR 9k10* ) -and if it finds exactly one kernel in a given FAT partition, -chooses it. -.I 9load -then attempts to load the -.IR bootfile . -.ig -unless -the -.B bootfile=manual -option was given, in which case prompt mode is entered immediately. -.. -.ig -If the default device is -.BR fd , -.I 9load -will prompt the user for input before proceeding with the -default bootfile load after 5 seconds; -this prompt is omitted if -a command-line argument or -.I bootfile -option -was given. -.. +.SS Boot Sectors +A copy of the Plan 9 PBS is kept in +.BR /386/pbs , +but due to the ``cylinder-head-sector'' (CHS) addressing mode of old BIOSes, it can only +operate up to 8.5GB into the disk. +Plan 9 partitions further into the disk +can only be booted using +.BR /386/pbslba , +and then only if the machine's BIOS supports +linear block addressing (LBA) mode for disk transfers. .PP -.I 9load -prints the list of available -.IR device s -and -enters prompt mode on encountering any error -or if directed to do so by a -.B bootfile=manual -option. -In prompt mode, the user is required to type -a -.IB bootfile -in response to the -.L Boot -.L from: -prompt. +When booting from disk, +.\" or floppy, +the BIOS loads the +first sector of the medium at location +.BR 0x7C00 . +In the case of a disk, it is the master boot record (MBR). +.\" In the case of a floppy, this is the PBS. +The MBR copies itself to address +.BR 0x600 , +finds the active partition and loads its PBS at address +.BR 0x7C00 . +A copy of the Plan 9 MBR is kept in +.BR /386/mbr ; +some commercial MBRs cannot read sectors +past 2GB. +The Plan 9 MBR can read sectors up to 8.5GB into +the disk, and further if the BIOS supports LBA. +The single file +.B /386/mbr +detects whether the BIOS supports LBA and +acts appropriately, defaulting to CHS mode +when LBA is not present. +The PBSs cannot do this due to code size limitations. +The Plan 9 MBR is suitable for booting non-Plan-9 +operating systems, +and (modulo the large disk constraints just described) +non-Plan-9 MBRs are suitable for booting Plan 9. .br .ne 4 .SS Other facilities and caveats --- /sys/src/9/pc/sdiahci.c Tue Apr 23 21:39:09 2013 +++ /sys/src/9/pc/sdiahci.c Tue Sep 3 20:17:36 2013 @@ -1,10 +1,6 @@ /* * ahci serial ata driver * copyright © 2007-8 coraid, inc. - * - * there was a great deal of locking of single operations (e.g., - * atomic assignments); it's not clear what that locking was intended to - * prevent. */ #include "u.h" @@ -1030,6 +1026,14 @@ } static void +setstate(Drive *d, int state) +{ + ilock(d); + d->state = state; + iunlock(d); +} + +static void resetdisk(Drive *d) { uint state, det, stat; @@ -1057,10 +1061,10 @@ iunlock(d); qlock(&d->portm); - if(p->cmd&Ast && ahciswreset(&d->portc) == -1){ - d->state = Dportreset; /* get a bigger stick. */ - } else { - d->state = Dmissing; + if(p->cmd&Ast && ahciswreset(&d->portc) == -1) + setstate(d, Dportreset); /* get a bigger stick. */ + else { + setstate(d, Dmissing); configdrive(d); } dprint("ahci: %s: resetdisk: %s → %s\n", (d->unit? d->unit->name: nil), @@ -1098,9 +1102,7 @@ if(ahcirecover(c) == -1) goto lose; } - - d->state = Dready; - + setstate(d, Dready); qunlock(c->m); idprint("%s: %sLBA %,llud sectors: %s %s %s %s\n", d->unit->name, @@ -1110,7 +1112,7 @@ lose: idprint("%s: can't be initialized\n", d->unit->name); - d->state = Dnull; + setstate(d, Dnull); qunlock(c->m); return -1; } @@ -1298,9 +1300,12 @@ c->intrs = 0; c->lastintr0 = now; } - if (++c->intrs > Maxintrspertick) - panic("sdiahci: too many intrs per tick for no serviced " - "drive; cause %#lux mport %d", cause, c->mport); + if (++c->intrs > Maxintrspertick) { + iprint("sdiahci: %lud intrs per tick for no serviced " + "drive; cause %#lux mport %d\n", + c->intrs, cause, c->mport); + c->intrs = 0; + } } static void @@ -1313,9 +1318,11 @@ d->intrs = 0; d->lastintr0 = now; } - if (++d->intrs > Maxintrspertick) - panic("sdiahci: too many interrupts per tick for %s", - d->unit->name); + if (++d->intrs > Maxintrspertick) { + iprint("sdiahci: %lud interrupts per tick for %s\n", + d->intrs, d->unit->name); + d->intrs = 0; + } } static void @@ -1646,7 +1653,7 @@ esleep(250); } print("%s: not responding; offline\n", d->unit->name); - d->state = Doffline; + setstate(d, Doffline); return -1; } @@ -2173,7 +2180,9 @@ break; if(i == nelem(modename)) i = 0; + ilock(d); d->mode = i; + iunlock(d); } static void @@ -2202,7 +2211,7 @@ break; if(i == nelem(diskstates)) error(Ebadctl); - d->state = i; + setstate(d, i); } /* --- /sys/src/9/pcboot/pxeload.c Fri Aug 30 22:19:13 2013 +++ /sys/src/9/pcboot/pxeload.c Tue Sep 3 20:12:57 2013 @@ -33,6 +33,10 @@ Prefsegsize = 1400, Maxsegsize = 2048, Bufsz = Maxsegsize + 2, + + Ok = 0, + Err = -1, + Nonexist = -2, }; typedef struct Ethaddr Ethaddr; @@ -73,10 +77,12 @@ }; static char ethernm[] = "ether"; +static uchar myea[Eaddrlen]; +static Pxenetaddr myaddr; /* actually, local ip addr & port */ /* * there can be at most one concurrent tftp session until we move these - * variables into Openeth or some other struct. + * variables into Openeth or some other struct (Tftpstate). */ static ushort tftpport; static int tftpblockno; @@ -84,12 +90,11 @@ static int progress; static int segsize; static Tftp *tftpb; - -static uchar myea[Eaddrlen]; -static Pxenetaddr myaddr; /* actually, local ip addr & port */ static Pxenetaddr tftpserv; /* actually, remote ip addr & port */ static Pxenetaddr bootpserv; +static int tftpconnect(Openeth *, Bootp *); + uchar * etheraddr(Openeth *oe) { @@ -404,15 +409,19 @@ switch((tftp->header[0]<<8)|tftp->header[1]){ case Tftp_ERROR: + if(strstr((char *)tftp->data, "does not exist") != nil){ + print("%s\n", (char*)tftp->data); + return Nonexist; + } print("tftpread1st: error (%d): %s\n", (tftp->header[2]<<8)|tftp->header[3], (char*)tftp->data); - return -1; + return Err; case Tftp_OACK: n = optval("blksize", (char *)tftp->header+2, rlen-2); if (n <= 0) { nak(oe, a, 0, "bad blksize option value", 0); - return -1; + return Err; } segsize = n; /* no bytes stashed in tftp.data */ @@ -426,7 +435,7 @@ if(len != tftpblockno){ print("tftpread1st: block error: %d\n", len); nak(oe, a, 1, "block error", 0); - return -1; + return Err; } rlen -= Tftphdrsz; if(rlen < segsize) @@ -437,12 +446,12 @@ default: print("tftpread1st: unexpected pkt type recv'd\n"); nak(oe, a, 0, "unexpected pkt type recv'd", 0); - return -1; + return Err; } } print("tftpread1st: failed to connect to server (%I!%d)\n", a->ip, oport); - return -1; + return Err; } static int @@ -606,6 +615,9 @@ char buf[128]; static uchar ipv4noaddr[IPv4addrlen]; + if (tftpconnect(oe, rep) < 0) + return Err; + /* * read file from tftp server in bootp answer */ @@ -641,13 +653,15 @@ return tftpread1st(oe, &tftpserv, filename, tftpb); } +/* load the kernel in file via tftp on oe */ int tftpboot(Openeth *oe, char *file, Bootp *rep, Boot *b) { int n; + /* file must exist, else it's an error */ if((n = tftpopen(oe, file, rep)) < 0) - return -1; + return n; progress = 0; /* no more dots; we're on a roll now */ print(" "); /* after "sys (ip!port): kernel ..." */ @@ -661,7 +675,7 @@ else nak(oe, &tftpserv, 3, "ok", 0); /* tftpclose to abort transfer */ bootpass(b, nil, 0); /* boot if possible */ - return -1; + return Err; } /* leave the channel to /net/ipifc/clone open */ @@ -816,9 +830,8 @@ continue; } if(np != nil){ - if(*np > len) { + if(*np > len) return 0; - } *np = len; } return p; @@ -971,13 +984,13 @@ break; if((n = tftpread(oe, &tftpserv, tftpb, segsize)) < 0) - return -1; + return n; } return p-v; } static int -newtftpconn(Openeth *oe, Bootp *rep) +tftpconnect(Openeth *oe, Bootp *rep) { char num[16], dialstr[64]; @@ -1025,31 +1038,20 @@ return 0; } +/* + * use bootp answer (rep) to open cfgpxe. + * reads first pkt of cfgpxe into tftpb->data. + */ static int -getkernname(Openeth *oe, Bootp *rep, Kernname *kp) +rdcfgpxe(Openeth *oe, Bootp *rep, char *cfgpxe) { int n; - char *ini, *p; - char cfgpxe[32], buf[64]; + char *ini; - if (kp->bootfile) { - print("getkernname: already have bootfile %s\n", kp->bootfile); - return 0; - } - if (newtftpconn(oe, rep) < 0) - return -1; - - /* use our mac address instead of relying on a bootp answer */ - snprint(cfgpxe, sizeof cfgpxe, "/cfg/pxe/%E", myea); - /* - * use bootp answer (rep) to open cfgpxe. - * reads first pkt of cfgpxe into tftpb->data. - */ + /* cfgpxe is optional */ n = tftpopen(oe, cfgpxe, rep); - if (n < 0) { - print("\nfailed.\n"); - return -1; - } + if (n < 0) + return n; if (Debug) print("\opened %s\n", cfgpxe); @@ -1059,7 +1061,7 @@ if (n < 0) { print("error reading %s\n", cfgpxe); free(ini); - return -1; + return n; } print(" read %d bytes", n); @@ -1068,17 +1070,17 @@ * thus we can't free ini. */ dotini(ini); - i8250console(); /* configure serial port with defaults */ + return Ok; +} - kp->edev = kp->bootfile = nil; - p = getconf("bootfile"); - if (p) - kstrdup(&kp->bootfile, p); - if (kp->bootfile == nil) - askbootfile(buf, sizeof buf, &kp->bootfile, Promptsecs, - "ether0!/386/9pccpu"); - if (strcmp(kp->bootfile, "manual") == 0) - askbootfile(buf, sizeof buf, &kp->bootfile, 0, ""); +/* + * break kp->bootfile into kp->edev & kp->bootfile, + * copy any args for new kernel to low memory. + */ +static int +parsebootfile(Kernname *kp) +{ + char *p; p = strchr(kp->bootfile, '!'); if (p != nil) { @@ -1088,16 +1090,57 @@ kstrdup(&kp->bootfile, p); if (strncmp(kp->edev, ethernm, sizeof ethernm - 1) != 0) { print("bad ether device %s\n", kp->edev); - return -1; + return Err; } } - /* pass arguments to kernels that can use them */ + /* pass any arguments to kernels that expect them */ strecpy(BOOTLINE, BOOTLINE+BOOTLINELEN, kp->bootfile); p = strchr(kp->bootfile, ' '); if(p != nil) *p = '\0'; - return 0; + return Ok; +} + +static int +getkernname(Openeth *oe, Bootp *rep, Kernname *kp) +{ + int n; + char *p; + char cfgpxe[32], buf[64]; + + if (kp->bootfile) { + /* i think returning here is a bad idea */ + // print("getkernname: already have bootfile %s\n", + // kp->bootfile); + free(kp->bootfile); + // return Ok; + } + kp->edev = kp->bootfile = nil; + i8250console(); /* configure serial port with defaults */ + + /* use our mac address instead of relying on a bootp answer. */ + snprint(cfgpxe, sizeof cfgpxe, "/cfg/pxe/%E", myea); + n = rdcfgpxe(oe, rep, cfgpxe); + switch (n) { + case Ok: + p = getconf("bootfile"); + if (p) + kstrdup(&kp->bootfile, p); + if (kp->bootfile == nil) + askbootfile(buf, sizeof buf, &kp->bootfile, Promptsecs, + "ether0!/386/9pccpu"); + if (strcmp(kp->bootfile, "manual") == 0) + askbootfile(buf, sizeof buf, &kp->bootfile, 0, ""); + break; + case Err: + print("\nfailed.\n"); + return n; + case Nonexist: + askbootfile(buf, sizeof buf, &kp->bootfile, 0, ""); + break; + } + return parsebootfile(kp); } static void @@ -1119,39 +1162,62 @@ * phase 2: load /cfg/pxe, parse it, extract kernel filename. * phase 3: load kernel and jump to it. */ -static void +static int tftpload(Openeth *oe, Kernname *kp) { + int r, n; + char buf[64]; Bootp rep; Boot boot; + r = -1; if(waserror()) { print("tftpload: %s\n", up->errstr); closeudp(oe); unbinddevip(oe); - return; + return r; } memset(&rep, 0, sizeof rep); - if (setipcfg(oe, &rep) >= 0 && - getkernname(oe, &rep, kp) >= 0 && - (!kp->edev || - oe->ctlrno == strtol(kp->edev + sizeof ethernm - 1, 0, 10)) && - newtftpconn(oe, &rep) >= 0) { + if (setipcfg(oe, &rep) < 0) + error("can't set ip config"); + + n = getkernname(oe, &rep, kp); + if (n < 0) { + r = n; /* pass reason back to caller */ + USED(r); + nexterror(); + } + do { + if (kp->edev && + oe->ctlrno != strtol(kp->edev + sizeof ethernm - 1, 0, 10)){ + /* user specified an ether & it's not this one; next! */ + r = Ok; + USED(r); + nexterror(); + } + memset(&boot, 0, sizeof boot); boot.state = INITKERNEL; - tftpboot(oe, kp->bootfile, &rep, &boot); - } + r = tftpboot(oe, kp->bootfile, &rep, &boot); + + /* we failed or bootfile asked for another ether */ + if (r == Nonexist) + do { + askbootfile(buf, sizeof buf, &kp->bootfile, 0, ""); + } while (parsebootfile(kp) != Ok); + } while (r == Nonexist); - /* we failed or bootfile asked for another ether */ poperror(); closeudp(oe); unbinddevip(oe); + return r; } static int etherload(int eth, Kernname *kp) { + int r; Openeth *oe; print("pxe on ether%d ", eth); @@ -1163,11 +1229,11 @@ oe->ctlrno); initbind(oe); - tftpload(oe, kp); + r = tftpload(oe, kp); /* failed to boot; keep going */ unmount(nil, "/net"); - return 0; + return r; } static int