OEM Windows in qemu/kvm again (mini-howto)

Message ID 4D947AD8.5060205@msgid.tls.msk.ru
State New
Headers show

Commit Message

Michael Tokarev March 31, 2011, 1 p.m.
Here's a quick-n-dirty howto about running OEM version
of windows7 or vista in qemu/kvm, assuming you own a
computer with pre-installed OEM version of such operating
system so you have rights to run it there.

Windows 7 & Vista OEM activation are based on a special
OEM marker in BIOS in a form of one ACPI table named SLIC.
The idea is to take content of that table from your computer
and present it in the virtual machine.

This table can be taken directly from /sys/firmware/acpi/tables/SLIC
(it's a small binary file), and hopefully qemu will be able
to use that directly (I submitted a patch to do that a few
days ago).

But there's on caveat still: windows apparently checks not only
the SLIC table/marker, but also verifies that the Vendor ID
fields in SLIC and RSDT tables matches each other.  So just
presenting SLIC into a VM isn't enough, one have to modify
at least two fields in RSDT table too.

And since there's no way currently to do that using qemu
command line, I took another approach and modified seabios
code a bit.  Here's a small howto to do that "at home".

1) First of all, you need seabios source code appropriate
   for your qemu/kvm.  See http://www.seabios.org/ for that.
   Also you need tools to recompile it, obviously.

2) after getting and extracting seabios sources, cd to
   the source directory.

3) You need to build slic table to be used by a C compiler,
   like this:

    xxd -i /sys/firmware/acpi/tables/SLIC |
     grep -v len |
     sed 's/unsigned char.*/static char SLIC[] = {/' >

  this produces file src/acpi-slic.hex with content like

    static char SLIC[] = {
     0x53, 0x4c, 0x49, 0x43, 0x76, 0x01, ...

4) apply the following patch to src/acpi.c:

============= cut =============

  The patch above arranges for your SLIC table to be
  included inside the ("virtual") bios (that table does
  exactly nothing for the bios itself and for other
  system components) and also modifies OEM identification
  information in RSDT table to match SLIC table.

5) build the bios as usual by issuing `make' in the top
   source directory.  You'll get out/bios.bin file.
   Place it somewhere like /usr/share/qemu/bios-asus.bin
   or anywhere like you want.

6) and finally, specify that bios file when running your
  preinstalled windows:

   qemu -hda /dev/sda -bios /usr/share/qemu/bios-asus.bin
    [...other options...]

  (But be careful and choose appropriate boot entry, do
   not boot linux itself in your virtual machine :)

Something like that.  This is formed as a quick-n-dirty
howto because there's quite alot of questions lately about
this very stuff (how to run activated windows in qemu),
and I wanted to write an instruction so that less
expirienced people will be able to do that.

Kevin, do you remember why this code is present in

build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev)
    memcpy(h->oem_id, CONFIG_APPNAME6, 6);
    memcpy(h->oem_table_id, CONFIG_APPNAME4, 4);
    memcpy(h->asl_compiler_id, CONFIG_APPNAME4, 4);
    memcpy(h->oem_table_id + 4, (void*)&sig, 4);     <=== this

To me it looks like we need another #define, like
CONFIG_APPNAME8, to be used for whole oem_table_id
field.  And also, maybe made all this stuff configurable
somehow (starting from the build config in out/autoconf.h
etc) ?




============= cut =============
--- a/src/acpi.c
+++ b/src/acpi.c
@@ -199,4 +199,9 @@  struct srat_memory_affinity
 #include "acpi-dsdt.hex"

+#include "acpi-slic.hex"
 static void
 build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev)
@@ -211,4 +216,8 @@  build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev)
     h->oem_revision = cpu_to_le32(1);
     h->asl_compiler_revision = cpu_to_le32(1);
+    if (sig == RSDT_SIGNATURE)	// only RSDT is checked by win7 & vista
+      memcpy(h->oem_id, ((struct acpi_table_header*)SLIC)->oem_id, 14);
     h->checksum -= checksum(h, len);
@@ -627,4 +636,15 @@  acpi_bios_init(void)

+    { void *buf = malloc_high(sizeof(SLIC));
+      if (!buf)
+        warn_noalloc();
+      else {
+        memcpy(buf, SLIC, sizeof(SLIC));
+        ACPI_INIT_TABLE(buf);
+      }
+    }
     u16 i, external_tables = qemu_cfg_acpi_additional_tables();