Re: [RFC/PATCH 1/2] kvm tools, seabios: Add "--bios" option to "vm run"

Anthony Liguori
2012-02-24T15:24:23Z

On 02/24/2012 09:05 AM, Pekka Enberg wrote:
> This patch adds a "--bios" command line option to "vm run". You can use this to
> try to boot with SeaBIOS, for example:
>
> ./vm run --bios=/usr/share/seabios/bios.bin \
> --disk $HOME/images/debian_lenny_amd64_standard.qcow2
>
> This doesn't boot yet for obvious reasons but at least people can now start to
> play with external BIOS images easily.

You may want to call it firmware as other platforms also have firmware. For
instance, it may be desirable to use the same interface to load SLOF with spapr.

>
> Cc: Yang Bai<hamo.by@gmail.com>
> Cc: Matt Evans<matt@ozlabs.org>
> Cc: Ron Minnich<rminnich@gmail.com>
> Cc: Anthony Liguori<aliguori@us.ibm.com>
> Cc: John Floren<john@jfloren.net>
> Cc: Sasha Levin<levinsasha928@gmail.com>
> Cc: Cyrill Gorcunov<gorcunov@openvz.org>
> Cc: Asias He<asias.hejun@gmail.com>
> Cc: Ingo Molnar<mingo@elte.hu>
> Signed-off-by: Pekka Enberg<penberg@kernel.org>
> ---
> tools/kvm/Makefile | 2 +
> tools/kvm/builtin-run.c | 32 +++++++++++++++++++----------
> tools/kvm/include/kvm/kvm.h | 1 +
> tools/kvm/powerpc/boot.c | 8 +++++++
> tools/kvm/x86/boot.c | 41 ++++++++++++++++++++++++++++++++++++++
> tools/kvm/x86/include/kvm/bios.h | 3 +-
> 6 files changed, 75 insertions(+), 12 deletions(-)
> create mode 100644 tools/kvm/powerpc/boot.c
> create mode 100644 tools/kvm/x86/boot.c
>
> diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
> index cfa5547..0a9c2cc 100644
> --- a/tools/kvm/Makefile
> +++ b/tools/kvm/Makefile
> @@ -113,6 +113,7 @@ LIBFDT_OBJS = $(patsubst %,../../scripts/dtc/libfdt/%,$(LIBFDT_SRC))
> #x86
> ifeq ($(ARCH),x86)
> DEFINES += -DCONFIG_X86
> + OBJS += x86/boot.o
> OBJS += x86/cpuid.o
> OBJS += x86/interrupt.o
> OBJS += x86/ioport.o
> @@ -129,6 +130,7 @@ endif
> # POWER/ppc: Actually only support ppc64 currently.
> ifeq ($(uname_M), ppc64)
> DEFINES += -DCONFIG_PPC
> + OBJS += powerpc/boot.o
> OBJS += powerpc/ioport.o
> OBJS += powerpc/irq.o
> OBJS += powerpc/kvm.o
> diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c
> index 466169e..f96c581 100644
> --- a/tools/kvm/builtin-run.c
> +++ b/tools/kvm/builtin-run.c
> @@ -76,6 +76,7 @@ static const char *kernel_cmdline;
> static const char *kernel_filename;
> static const char *vmlinux_filename;
> static const char *initrd_filename;
> +static const char *bios_filename;
> static const char *image_filename[MAX_DISK_IMAGES];
> static const char *console;
> static const char *dev;
> @@ -458,6 +459,8 @@ static const struct option options[] = {
> "Initial RAM disk image"),
> OPT_STRING('p', "params",&kernel_cmdline, "params",
> "Kernel command line arguments"),
> + OPT_STRING('b', "bios",&bios_filename, "bios",
> + "BIOS to boot in virtual machine"),
>
> OPT_GROUP("Networking options:"),
> OPT_CALLBACK_DEFAULT('n', "network", NULL, "network params",
> @@ -1110,14 +1113,16 @@ static int kvm_cmd_run_init(int argc, const char **argv)
> printf(" # %s run -k %s -m %Lu -c %d --name %s\n", KVM_BINARY_NAME,
> kernel_filename, ram_size / 1024 / 1024, nrcpus, guest_name);
>
> - if (!kvm__load_kernel(kvm, kernel_filename, initrd_filename,
> - real_cmdline, vidmode))
> - die("unable to load kernel %s", kernel_filename);
> + if (!bios_filename) {
> + if (!kvm__load_kernel(kvm, kernel_filename,
> + initrd_filename, real_cmdline, vidmode))
> + die("unable to load kernel %s", kernel_filename);
>
> - kvm->vmlinux = vmlinux_filename;
> - r = symbol_init(kvm);
> - if (r< 0)
> - pr_debug("symbol_init() failed with error %d\n", r);
> + kvm->vmlinux = vmlinux_filename;
> + r = symbol_init(kvm);
> + if (r< 0)
> + pr_debug("symbol_init() failed with error %d\n", r);
> + }
>
> ioport__setup_arch();
>
> @@ -1218,10 +1223,15 @@ static int kvm_cmd_run_init(int argc, const char **argv)
>
> kvm__start_timer(kvm);
>
> - kvm__arch_setup_firmware(kvm);
> - if (r< 0) {
> - pr_err("kvm__arch_setup_firmware() failed with error %d\n", r);
> - goto fail;
> + if (bios_filename) {
> + if (!kvm__load_bios(kvm, bios_filename))
> + die("unable to load bios %s: %s", bios_filename, strerror(errno));
> + } else {
> + kvm__arch_setup_firmware(kvm);
> + if (r< 0) {
> + pr_err("kvm__arch_setup_firmware() failed with error %d\n", r);
> + goto fail;
> + }
> }
>
> for (i = 0; i< nrcpus; i++) {
> diff --git a/tools/kvm/include/kvm/kvm.h b/tools/kvm/include/kvm/kvm.h
> index 7870118..258d11a 100644
> --- a/tools/kvm/include/kvm/kvm.h
> +++ b/tools/kvm/include/kvm/kvm.h
> @@ -39,6 +39,7 @@ int kvm__recommended_cpus(struct kvm *kvm);
> int kvm__max_cpus(struct kvm *kvm);
> void kvm__init_ram(struct kvm *kvm);
> int kvm__exit(struct kvm *kvm);
> +bool kvm__load_bios(struct kvm *kvm, const char *bios_filename);
> bool kvm__load_kernel(struct kvm *kvm, const char *kernel_filename,
> const char *initrd_filename, const char *kernel_cmdline, u16 vidmode);
> void kvm__start_timer(struct kvm *kvm);
> diff --git a/tools/kvm/powerpc/boot.c b/tools/kvm/powerpc/boot.c
> new file mode 100644
> index 0000000..8c99831
> --- /dev/null
> +++ b/tools/kvm/powerpc/boot.c
> @@ -0,0 +1,8 @@
> +#include "kvm/kvm.h"
> +
> +#include<stdbool.h>
> +
> +bool kvm__load_bios(struct kvm *kvm, const char *bios_filename)
> +{
> + return false;
> +}
> diff --git a/tools/kvm/x86/boot.c b/tools/kvm/x86/boot.c
> new file mode 100644
> index 0000000..383d9f7
> --- /dev/null
> +++ b/tools/kvm/x86/boot.c
> @@ -0,0 +1,41 @@
> +#include "kvm/kvm.h"
> +
> +#include "kvm/util.h"
> +
> +#include<sys/types.h>
> +#include<sys/stat.h>
> +#include<stdbool.h>
> +#include<fcntl.h>
> +
> +#define BIOS_SELECTOR 0xf000
> +#define BIOS_IP 0xfff0
> +#define BIOS_SP 0x8000
> +
> +bool kvm__load_bios(struct kvm *kvm, const char *bios_filename)
> +{
> + struct stat st;
> + void *p;
> + int fd;
> + int nr;
> +
> + fd = open(bios_filename, O_RDONLY);
> + if (fd< 0)
> + return false;
> +
> + if (fstat(fd,&st))
> + return false;
> +
> + if (st.st_size> MB_BIOS_SIZE)
> + die("BIOS image %s is too big to fit in memory (%lu KB).\n", bios_filename, st.st_size / 1024);
> +
> + p = guest_flat_to_host(kvm, MB_BIOS_BEGIN);
> +
> + while ((nr = read(fd, p, st.st_size))> 0)
> + p += nr;
> +
> + kvm->boot_selector = BIOS_SELECTOR;
> + kvm->boot_ip = BIOS_IP;
> + kvm->boot_sp = BIOS_SP;
> +
> + return true;
> +}
> diff --git a/tools/kvm/x86/include/kvm/bios.h b/tools/kvm/x86/include/kvm/bios.h
> index de569bc..9d677ae 100644
> --- a/tools/kvm/x86/include/kvm/bios.h
> +++ b/tools/kvm/x86/include/kvm/bios.h
> @@ -26,8 +26,9 @@
>
> #define E820_MAP_START EBDA_START
>
> -#define MB_BIOS_BEGIN 0x000f0000
> +#define MB_BIOS_BEGIN 0x000e0000
> #define MB_BIOS_END 0x000fffff
> +#define MB_BIOS_SIZE (MB_BIOS_END - MB_BIOS_BEGIN + 1)
>
> #define VGA_RAM_BEGIN 0x000a0000
> #define VGA_RAM_END 0x000bffff

FYI, this will work with SeaBIOS because it's currently 128k. But the normal
thing is to load the last 128k of the BIOS ROM at 1MB - 128k, with the full ROM
mapped at 4GB - sizeof(ROM).

SeaBIOS certainly expects the ROM to be located in both places.

Also note that the area at 1MB - 128k is special. There's two distinct regions
mapped here, normal RAM and then the BIOS ROM. Depending on the settings of the
PAM registers, a guest can chose to see either piece of memory. In fact, it can
control read and write access independently such that writes go to RAM and reads
come from the BIOS area.

This is used to implement shadowing. SeaBIOS does special case this under KVM
since it's not possible to execute out of MMIO under KVM.

Regards,

Anthony Liguori