diff mbox

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

Message ID 20110401065221.GC8401@valinux.co.jp
State New
Headers show

Commit Message

Isaku Yamahata April 1, 2011, 6:52 a.m. UTC
Hi. SLIC table can be fed dynamically by utilize the existing fw_cfg interface.
Something like this. (This requires your qemu patch.)
This is just for showing the idea.

thanks,



On Thu, Mar 31, 2011 at 05:00:08PM +0400, Michael Tokarev wrote:
> 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[] = {/' >
>      src/acpi-slic.hex
> 
>   this produces file src/acpi-slic.hex with content like
>   this:
> 
>     static char SLIC[] = {
>      0x53, 0x4c, 0x49, 0x43, 0x76, 0x01, ...
>     };
> 
> 4) apply the following patch to src/acpi.c:
> 
> ============= cut =============
> --- a/src/acpi.c
> +++ b/src/acpi.c
> @@ -199,4 +199,9 @@ struct srat_memory_affinity
>  #include "acpi-dsdt.hex"
> 
> +#define CONFIG_OEM_SLIC
> +#ifdef CONFIG_OEM_SLIC
> +#include "acpi-slic.hex"
> +#endif
> +
>  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);
> +#ifdef CONFIG_OEM_SLIC
> +    if (sig == RSDT_SIGNATURE)	// only RSDT is checked by win7 & vista
> +      memcpy(h->oem_id, ((struct acpi_table_header*)SLIC)->oem_id, 14);
> +#endif
>      h->checksum -= checksum(h, len);
>  }
> @@ -627,4 +636,15 @@ acpi_bios_init(void)
>      ACPI_INIT_TABLE(build_srat());
> 
> +#ifdef CONFIG_OEM_SLIC
> +    { void *buf = malloc_high(sizeof(SLIC));
> +      if (!buf)
> +        warn_noalloc();
> +      else {
> +        memcpy(buf, SLIC, sizeof(SLIC));
> +        ACPI_INIT_TABLE(buf);
> +      }
> +    }
> +#endif
> +
>      u16 i, external_tables = qemu_cfg_acpi_additional_tables();
> 
> ============= 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
> acpi.c:
> 
> 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) ?
> 
> Thanks!
> 
> /mjt
>
diff mbox

Patch

diff --git a/src/acpi.c b/src/acpi.c
index 6428d9c..e0815bd 100644
--- a/src/acpi.c
+++ b/src/acpi.c
@@ -627,6 +627,7 @@  acpi_bios_init(void)
     ACPI_INIT_TABLE(build_srat());
 
     u16 i, external_tables = qemu_cfg_acpi_additional_tables();
+    bool slic = false;
 
     for(i = 0; i < external_tables; i++) {
         u16 len = qemu_cfg_next_acpi_table_len();
@@ -635,7 +636,12 @@  acpi_bios_init(void)
             warn_noalloc();
             continue;
         }
-        ACPI_INIT_TABLE(qemu_cfg_next_acpi_table_load(addr, len));
+        struct acpi_table_header *header =
+            qemu_cfg_next_acpi_table_load(addr, len);
+        if (header->signature == SLIC_SIGNATURE) {
+            slic = true;
+        }
+        ACPI_INIT_TABLE(header);
         if (tbl_idx == MAX_ACPI_TABLES) {
             warn_noalloc();
             break;
@@ -654,6 +660,9 @@  acpi_bios_init(void)
     memcpy(rsdt->table_offset_entry, tables, sizeof(u32) * tbl_idx);
 
     build_header((void*)rsdt, RSDT_SIGNATURE, rsdt_len, 1);
+    if (slic) {
+        // fix up rsdt->oem_id and check sum
+    }
 
     // Build rsdp pointer table
     memset(rsdp, 0, sizeof(*rsdp));