freebsd-dev/sys/boot/i386/common/cons.c

153 lines
2.5 KiB
C
Raw Normal View History

- Split code shared by almost any boot loader into separate files and clean up most layering violations: sys/boot/i386/common/rbx.h: RBX_* defines OPT_SET() OPT_CHECK() sys/boot/common/util.[ch]: memcpy() memset() memcmp() bcpy() bzero() bcmp() strcmp() strncmp() [new] strcpy() strcat() strchr() strlen() printf() sys/boot/i386/common/cons.[ch]: ioctrl putc() xputc() putchar() getc() xgetc() keyhit() [now takes number of seconds as an argument] getstr() sys/boot/i386/common/drv.[ch]: struct dsk drvread() drvwrite() [new] drvsize() [new] sys/boot/common/crc32.[ch] [new] sys/boot/common/gpt.[ch] [new] - Teach gptboot and gptzfsboot about new files. I haven't touched the rest, but there is still a lot of code duplication to be removed. - Implement full GPT support. Currently we just read primary header and partition table and don't care about checksums, etc. After this change we verify checksums of primary header and primary partition table and if there is a problem we fall back to backup header and backup partition table. - Clean up most messages to use prefix of boot program, so in case of an error we know where the error comes from, eg.: gptboot: unable to read primary GPT header - If we can't boot, print boot prompt only once and not every five seconds. - Honour newly added GPT attributes: bootme - this is bootable partition bootonce - try to boot from this partition only once bootfailed - we failed to boot from this partition - Change boot order of gptboot to the following: 1. Try to boot from all the partitions that have both 'bootme' and 'bootonce' attributes one by one. 2. Try to boot from all the partitions that have only 'bootme' attribute one by one. 3. If there are no partitions with 'bootme' attribute, boot from the first UFS partition. - The 'bootonce' functionality is implemented in the following way: 1. Walk through all the partitions and when 'bootonce' attribute is found without 'bootme' attribute, remove 'bootonce' attribute and set 'bootfailed' attribute. 'bootonce' attribute alone means that we tried to boot from this partition, but boot failed after leaving gptboot and machine was restarted. 2. Find partition with both 'bootme' and 'bootonce' attributes. 3. Remove 'bootme' attribute. 4. Try to execute /boot/loader or /boot/kernel/kernel from that partition. If succeeded we stop here. 5. If execution failed, remove 'bootonce' and set 'bootfailed'. 6. Go to 2. If whole boot succeeded there is new /etc/rc.d/gptboot script coming that will log all partitions that we failed to boot from (the ones with 'bootfailed' attribute) and will remove this attribute. It will also find partition with 'bootonce' attribute - this is the partition we booted from successfully. The script will log success and remove the attribute. All the GPT updates we do here goes to both primary and backup GPT if they are valid. We don't touch headers or partition tables when checksum doesn't match. Reviewed by: arch (Message-ID: <20100917234542.GE1902@garage.freebsd.pl>) Obtained from: Wheel Systems Sp. z o.o. http://www.wheelsystems.com MFC after: 2 weeks
2010-09-24 19:49:12 +00:00
/*-
* Copyright (c) 1998 Robert Nordier
* All rights reserved.
*
* Redistribution and use in source and binary forms are freely
* permitted provided that the above copyright notice and this
* paragraph and the following disclaimer are duplicated in all
* such forms.
*
* This software is provided "AS IS" and without any express or
* implied warranties, including, without limitation, the implied
* warranties of merchantability and fitness for a particular
* purpose.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <machine/psl.h>
#include <btxv86.h>
#include "lib.h"
#include "rbx.h"
#include "util.h"
#include "cons.h"
#define V86_ZR(x) ((x) & PSL_Z)
#define SECOND 18 /* Circa that many ticks in a second. */
uint8_t ioctrl = IO_KEYBOARD;
void
putc(int c)
{
v86.addr = 0x10;
v86.eax = 0xe00 | (c & 0xff);
v86.ebx = 0x7;
v86int();
}
void
xputc(int c)
{
if (ioctrl & IO_KEYBOARD)
putc(c);
if (ioctrl & IO_SERIAL)
sio_putc(c);
}
void
putchar(int c)
{
if (c == '\n')
xputc('\r');
xputc(c);
}
int
getc(int fn)
{
/*
* The extra comparison against zero is an attempt to work around
* what appears to be a bug in QEMU and Bochs. Both emulators
* sometimes report a key-press with scancode one and ascii zero
* when no such key is pressed in reality. As far as I can tell,
* this only happens shortly after a reboot.
*/
v86.ctl = V86_FLAGS;
v86.addr = 0x16;
v86.eax = fn << 8;
v86int();
return fn == 0 ? v86.eax & 0xff : (!V86_ZR(v86.efl) && (v86.eax & 0xff));
}
int
xgetc(int fn)
{
if (OPT_CHECK(RBX_NOINTR))
return (0);
for (;;) {
if (ioctrl & IO_KEYBOARD && getc(1))
return (fn ? 1 : getc(0));
if (ioctrl & IO_SERIAL && sio_ischar())
return (fn ? 1 : sio_getc());
if (fn)
return (0);
}
/* NOTREACHED */
}
int
keyhit(unsigned int secs)
{
uint32_t t0, t1;
if (OPT_CHECK(RBX_NOINTR))
return (0);
secs *= SECOND;
t0 = 0;
for (;;) {
if (xgetc(1))
return (1);
if (secs > 0) {
t1 = *(uint32_t *)PTOV(0x46c);
if (!t0)
t0 = t1;
if (t1 < t0 || t1 >= t0 + secs)
return (0);
}
}
/* NOTREACHED */
}
void
getstr(char *cmdstr, size_t cmdstrsize)
{
char *s;
int c;
s = cmdstr;
for (;;) {
switch (c = xgetc(0)) {
case 0:
break;
case '\177':
case '\b':
if (s > cmdstr) {
s--;
printf("\b \b");
}
break;
case '\n':
case '\r':
*s = 0;
return;
default:
if (s - cmdstr < cmdstrsize - 1)
*s++ = c;
putchar(c);
break;
}
}
}