diff mbox

[v2] ACPI: Add -acpifadt to allow FADT revision changes

Message ID a0a25b78b9a593b7cf3c73b54342ee9b331688da.1470643610.git.lv.zheng@intel.com
State New
Headers show

Commit Message

Lv Zheng Aug. 8, 2016, 8:16 a.m. UTC
This patch allows FADT to be built with different revisions. When the revision
is greater than 1.0, 64-bit address fields may also be filled.

Note that FADT revision 2 has never been defined by the ACPI specification. So
this patch only adds an option -acpifadt to allow revision 1,3,5 to be
configured by the users.

1. Tested by booting a linux image, the 64-bit addresses are correctly filled
   in the dumped FADT.
2. Tested by booting a Windows image, no boot failure can be seen.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
History:

v2: Fix coding style issues.
---
 hw/i386/acpi-build.c   |   62 ++++++++++++++++++++++++++++++++++++++++++------
 include/hw/acpi/acpi.h |    1 +
 qemu-options.hx        |    9 +++++++
 vl.c                   |   23 ++++++++++++++++++
 4 files changed, 88 insertions(+), 7 deletions(-)

Comments

Paolo Bonzini Aug. 8, 2016, 9:01 a.m. UTC | #1
On 08/08/2016 10:16, Lv Zheng wrote:
> This patch allows FADT to be built with different revisions. When the revision
> is greater than 1.0, 64-bit address fields may also be filled.
> 
> Note that FADT revision 2 has never been defined by the ACPI specification. So
> this patch only adds an option -acpifadt to allow revision 1,3,5 to be
> configured by the users.
> 
> 1. Tested by booting a linux image, the 64-bit addresses are correctly filled
>    in the dumped FADT.
> 2. Tested by booting a Windows image, no boot failure can be seen.
> 
> Signed-off-by: Lv Zheng <lv.zheng@intel.com>

Hi, please make this a suboption of -acpitable instead.  We try not to
add new command-line options without supporting them in QemuOpts too.

Paolo

> ---
> History:
> 
> v2: Fix coding style issues.
> ---
>  hw/i386/acpi-build.c   |   62 ++++++++++++++++++++++++++++++++++++++++++------
>  include/hw/acpi/acpi.h |    1 +
>  qemu-options.hx        |    9 +++++++
>  vl.c                   |   23 ++++++++++++++++++
>  4 files changed, 88 insertions(+), 7 deletions(-)
> 
> diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
> index ce7cbc5..4479695 100644
> --- a/hw/i386/acpi-build.c
> +++ b/hw/i386/acpi-build.c
> @@ -276,8 +276,22 @@ build_facs(GArray *table_data, BIOSLinker *linker)
>      facs->length = cpu_to_le32(sizeof(*facs));
>  }
>  
> +/* GAS */
> +static void
> +build_gas(struct AcpiGenericAddress *gas, uint8_t space_id,
> +          uint8_t bit_width, uint8_t bit_offset,
> +          uint8_t access_width, uint64_t address)
> +{
> +    gas->space_id = space_id;
> +    gas->bit_width = bit_width;
> +    gas->bit_offset = bit_offset;
> +    gas->access_width = access_width;
> +    gas->address = address;
> +}
> +
>  /* Load chipset information in FADT */
> -static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm)
> +static void fadt_setup(AcpiFadtDescriptorRev5_1 *fadt, AcpiPmInfo *pm,
> +                       uint8_t revision)
>  {
>      fadt->model = 1;
>      fadt->reserved1 = 0;
> @@ -309,6 +323,25 @@ static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm)
>          fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL);
>      }
>      fadt->century = RTC_CENTURY;
> +
> +    /* Build ACPI 2.0 (FADT version 3+) GAS fields */
> +    if (revision >= 3) {
> +        /* EVT, CNT, TMR register matches hw/acpi/core.c */
> +        build_gas(&fadt->xpm1a_event_block, AML_SYSTEM_IO,
> +                  32, 0, 0, cpu_to_le64(pm->io_base));
> +        build_gas(&fadt->xpm1a_control_block, AML_SYSTEM_IO,
> +                  16, 0, 0, cpu_to_le64(pm->io_base + 0x04));
> +        build_gas(&fadt->xpm_timer_block, AML_SYSTEM_IO,
> +                  32, 0, 0, cpu_to_le64(pm->io_base + 0x08));
> +        build_gas(&fadt->xgpe0_block, AML_SYSTEM_IO,
> +                  pm->gpe0_blk_len << 3, 0, 0, cpu_to_le64(pm->gpe0_blk));
> +    }
> +
> +    /* Build dummy ACPI 5.0 fields */
> +    if (revision >= 5) {
> +        build_gas(&fadt->sleep_control, AML_SYSTEM_MEMORY, 0, 0, 0, 0);
> +        build_gas(&fadt->sleep_status, AML_SYSTEM_MEMORY, 0, 0, 0, 0);
> +    }
>  }
>  
>  
> @@ -316,9 +349,9 @@ static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm)
>  static void
>  build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm,
>             unsigned facs_tbl_offset, unsigned dsdt_tbl_offset,
> -           const char *oem_id, const char *oem_table_id)
> +           const char *oem_id, const char *oem_table_id, uint8_t revision)
>  {
> -    AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
> +    AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
>      unsigned fw_ctrl_offset = (char *)&fadt->firmware_ctrl - table_data->data;
>      unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data;
>  
> @@ -328,13 +361,28 @@ build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm,
>          ACPI_BUILD_TABLE_FILE, facs_tbl_offset);
>  
>      /* DSDT address to be filled by Guest linker */
> -    fadt_setup(fadt, pm);
>      bios_linker_loader_add_pointer(linker,
>          ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt),
>          ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
>  
> -    build_header(linker, table_data,
> -                 (void *)fadt, "FACP", sizeof(*fadt), 1, oem_id, oem_table_id);
> +    if (revision > 2) {
> +        fw_ctrl_offset = (char *)&fadt->Xfacs - table_data->data;
> +        dsdt_entry_offset = (char *)&fadt->Xdsdt - table_data->data;
> +
> +        /* FACS address to be filled by Guest linker */
> +        bios_linker_loader_add_pointer(linker,
> +            ACPI_BUILD_TABLE_FILE, fw_ctrl_offset, sizeof(fadt->Xfacs),
> +            ACPI_BUILD_TABLE_FILE, facs_tbl_offset);
> +
> +        /* DSDT address to be filled by Guest linker */
> +        bios_linker_loader_add_pointer(linker,
> +            ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->Xdsdt),
> +            ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
> +    }
> +
> +    fadt_setup(fadt, pm, revision);
> +    build_header(linker, table_data, (void *)fadt, "FACP", sizeof(*fadt),
> +                 revision, oem_id, oem_table_id);
>  }
>  
>  void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
> @@ -2681,7 +2729,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine)
>      fadt = tables_blob->len;
>      acpi_add_table(table_offsets, tables_blob);
>      build_fadt(tables_blob, tables->linker, &pm, facs, dsdt,
> -               slic_oem.id, slic_oem.table_id);
> +               slic_oem.id, slic_oem.table_id, acpi_fadt_rev);
>      aml_len += tables_blob->len - fadt;
>  
>      acpi_add_table(table_offsets, tables_blob);
> diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h
> index 7b3d93c..63df38d 100644
> --- a/include/hw/acpi/acpi.h
> +++ b/include/hw/acpi/acpi.h
> @@ -173,6 +173,7 @@ void acpi_update_sci(ACPIREGS *acpi_regs, qemu_irq irq);
>  
>  /* acpi.c */
>  extern int acpi_enabled;
> +extern uint8_t acpi_fadt_rev;
>  extern char unsigned *acpi_tables;
>  extern size_t acpi_tables_len;
>  
> diff --git a/qemu-options.hx b/qemu-options.hx
> index a71aaf8..cff34f6 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1492,6 +1492,15 @@ STEXI
>  Disable HPET support.
>  ETEXI
>  
> +DEF("acpifadt", HAS_ARG, QEMU_OPTION_acpifadt, \
> +    "-acpifadt  n    configure FADT revision number (1, 3, 5)\n",
> +    QEMU_ARCH_ALL)
> +STEXI
> +@item -acpifadt @var{n}
> +@findex -acpifadt
> +Configure FADT revision number, default is 1.
> +ETEXI
> +
>  DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable,
>      "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,{data|file}=file1[:file2]...]\n"
>      "                ACPI table description\n", QEMU_ARCH_I386)
> diff --git a/vl.c b/vl.c
> index e7c2c62..220e36d 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -64,6 +64,7 @@ int main(int argc, char **argv)
>  #include "hw/bt.h"
>  #include "sysemu/watchdog.h"
>  #include "hw/smbios/smbios.h"
> +#include "hw/acpi/acpi.h"
>  #include "hw/xen/xen.h"
>  #include "hw/qdev.h"
>  #include "hw/loader.h"
> @@ -158,6 +159,7 @@ int max_cpus = 1;
>  int smp_cores = 1;
>  int smp_threads = 1;
>  int acpi_enabled = 1;
> +uint8_t acpi_fadt_rev = 1;
>  int no_hpet = 0;
>  int fd_bootchk = 1;
>  static int no_reboot;
> @@ -2301,6 +2303,24 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp)
>      return 0;
>  }
>  
> +static void fadt_parse(const char *arg)
> +{
> +    unsigned long rev;
> +    int err;
> +
> +    err = qemu_strtoul(arg, NULL, 10, &rev);
> +    if (err) {
> +        error_printf("Invalid FADT revision.\n");
> +        exit(1);
> +    }
> +    if (rev != 1 && rev != 3 && rev != 5) {
> +        error_printf("Unsupported FADT revision %lu, "
> +                     "please specify 1,3,5.\n", rev);
> +        exit(1);
> +    }
> +    acpi_fadt_rev = rev;
> +}
> +
>  static int device_help_func(void *opaque, QemuOpts *opts, Error **errp)
>  {
>      return qdev_device_help(opts);
> @@ -3622,6 +3642,9 @@ int main(int argc, char **argv, char **envp)
>                  qdev_prop_register_global(&slew_lost_ticks);
>                  break;
>              }
> +            case QEMU_OPTION_acpifadt:
> +                fadt_parse(optarg);
> +                break;
>              case QEMU_OPTION_acpitable:
>                  opts = qemu_opts_parse_noisily(qemu_find_opts("acpi"),
>                                                 optarg, true);
>
Lv Zheng Aug. 9, 2016, 9:06 p.m. UTC | #2
Hi,

> From: Paolo Bonzini [mailto:paolo.bonzini@gmail.com] On Behalf Of Paolo

> Bonzini

> Subject: Re: [PATCH v2] ACPI: Add -acpifadt to allow FADT revision changes

> 

> 

> 

> On 08/08/2016 10:16, Lv Zheng wrote:

> > This patch allows FADT to be built with different revisions. When the

> revision

> > is greater than 1.0, 64-bit address fields may also be filled.

> >

> > Note that FADT revision 2 has never been defined by the ACPI

> specification. So

> > this patch only adds an option -acpifadt to allow revision 1,3,5 to be

> > configured by the users.

> >

> > 1. Tested by booting a linux image, the 64-bit addresses are correctly

> filled

> >    in the dumped FADT.

> > 2. Tested by booting a Windows image, no boot failure can be seen.

> >

> > Signed-off-by: Lv Zheng <lv.zheng@intel.com>

> 

> Hi, please make this a suboption of -acpitable instead.  We try not to

> add new command-line options without supporting them in QemuOpts

> too.

[Lv Zheng] 
OK. I'll prepare a v3 patch to address this.
Thanks for the review.

Cheers
-Lv

> 

> Paolo

> 

> > ---

> > History:

> >

> > v2: Fix coding style issues.

> > ---

> >  hw/i386/acpi-build.c   |   62

> ++++++++++++++++++++++++++++++++++++++++++------

> >  include/hw/acpi/acpi.h |    1 +

> >  qemu-options.hx        |    9 +++++++

> >  vl.c                   |   23 ++++++++++++++++++

> >  4 files changed, 88 insertions(+), 7 deletions(-)

> >

> > diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c

> > index ce7cbc5..4479695 100644

> > --- a/hw/i386/acpi-build.c

> > +++ b/hw/i386/acpi-build.c

> > @@ -276,8 +276,22 @@ build_facs(GArray *table_data, BIOSLinker

> *linker)

> >      facs->length = cpu_to_le32(sizeof(*facs));

> >  }

> >

> > +/* GAS */

> > +static void

> > +build_gas(struct AcpiGenericAddress *gas, uint8_t space_id,

> > +          uint8_t bit_width, uint8_t bit_offset,

> > +          uint8_t access_width, uint64_t address)

> > +{

> > +    gas->space_id = space_id;

> > +    gas->bit_width = bit_width;

> > +    gas->bit_offset = bit_offset;

> > +    gas->access_width = access_width;

> > +    gas->address = address;

> > +}

> > +

> >  /* Load chipset information in FADT */

> > -static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm)

> > +static void fadt_setup(AcpiFadtDescriptorRev5_1 *fadt, AcpiPmInfo

> *pm,

> > +                       uint8_t revision)

> >  {

> >      fadt->model = 1;

> >      fadt->reserved1 = 0;

> > @@ -309,6 +323,25 @@ static void fadt_setup(AcpiFadtDescriptorRev1

> *fadt, AcpiPmInfo *pm)

> >          fadt->flags |= cpu_to_le32(1 <<

> ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL);

> >      }

> >      fadt->century = RTC_CENTURY;

> > +

> > +    /* Build ACPI 2.0 (FADT version 3+) GAS fields */

> > +    if (revision >= 3) {

> > +        /* EVT, CNT, TMR register matches hw/acpi/core.c */

> > +        build_gas(&fadt->xpm1a_event_block, AML_SYSTEM_IO,

> > +                  32, 0, 0, cpu_to_le64(pm->io_base));

> > +        build_gas(&fadt->xpm1a_control_block, AML_SYSTEM_IO,

> > +                  16, 0, 0, cpu_to_le64(pm->io_base + 0x04));

> > +        build_gas(&fadt->xpm_timer_block, AML_SYSTEM_IO,

> > +                  32, 0, 0, cpu_to_le64(pm->io_base + 0x08));

> > +        build_gas(&fadt->xgpe0_block, AML_SYSTEM_IO,

> > +                  pm->gpe0_blk_len << 3, 0, 0, cpu_to_le64(pm->gpe0_blk));

> > +    }

> > +

> > +    /* Build dummy ACPI 5.0 fields */

> > +    if (revision >= 5) {

> > +        build_gas(&fadt->sleep_control, AML_SYSTEM_MEMORY, 0, 0, 0,

> 0);

> > +        build_gas(&fadt->sleep_status, AML_SYSTEM_MEMORY, 0, 0, 0, 0);

> > +    }

> >  }

> >

> >

> > @@ -316,9 +349,9 @@ static void fadt_setup(AcpiFadtDescriptorRev1

> *fadt, AcpiPmInfo *pm)

> >  static void

> >  build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm,

> >             unsigned facs_tbl_offset, unsigned dsdt_tbl_offset,

> > -           const char *oem_id, const char *oem_table_id)

> > +           const char *oem_id, const char *oem_table_id, uint8_t revision)

> >  {

> > -    AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data,

> sizeof(*fadt));

> > +    AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data,

> sizeof(*fadt));

> >      unsigned fw_ctrl_offset = (char *)&fadt->firmware_ctrl - table_data-

> >data;

> >      unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data;

> >

> > @@ -328,13 +361,28 @@ build_fadt(GArray *table_data, BIOSLinker

> *linker, AcpiPmInfo *pm,

> >          ACPI_BUILD_TABLE_FILE, facs_tbl_offset);

> >

> >      /* DSDT address to be filled by Guest linker */

> > -    fadt_setup(fadt, pm);

> >      bios_linker_loader_add_pointer(linker,

> >          ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt),

> >          ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);

> >

> > -    build_header(linker, table_data,

> > -                 (void *)fadt, "FACP", sizeof(*fadt), 1, oem_id, oem_table_id);

> > +    if (revision > 2) {

> > +        fw_ctrl_offset = (char *)&fadt->Xfacs - table_data->data;

> > +        dsdt_entry_offset = (char *)&fadt->Xdsdt - table_data->data;

> > +

> > +        /* FACS address to be filled by Guest linker */

> > +        bios_linker_loader_add_pointer(linker,

> > +            ACPI_BUILD_TABLE_FILE, fw_ctrl_offset, sizeof(fadt->Xfacs),

> > +            ACPI_BUILD_TABLE_FILE, facs_tbl_offset);

> > +

> > +        /* DSDT address to be filled by Guest linker */

> > +        bios_linker_loader_add_pointer(linker,

> > +            ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->Xdsdt),

> > +            ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);

> > +    }

> > +

> > +    fadt_setup(fadt, pm, revision);

> > +    build_header(linker, table_data, (void *)fadt, "FACP", sizeof(*fadt),

> > +                 revision, oem_id, oem_table_id);

> >  }

> >

> >  void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,

> > @@ -2681,7 +2729,7 @@ void acpi_build(AcpiBuildTables *tables,

> MachineState *machine)

> >      fadt = tables_blob->len;

> >      acpi_add_table(table_offsets, tables_blob);

> >      build_fadt(tables_blob, tables->linker, &pm, facs, dsdt,

> > -               slic_oem.id, slic_oem.table_id);

> > +               slic_oem.id, slic_oem.table_id, acpi_fadt_rev);

> >      aml_len += tables_blob->len - fadt;

> >

> >      acpi_add_table(table_offsets, tables_blob);

> > diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h

> > index 7b3d93c..63df38d 100644

> > --- a/include/hw/acpi/acpi.h

> > +++ b/include/hw/acpi/acpi.h

> > @@ -173,6 +173,7 @@ void acpi_update_sci(ACPIREGS *acpi_regs,

> qemu_irq irq);

> >

> >  /* acpi.c */

> >  extern int acpi_enabled;

> > +extern uint8_t acpi_fadt_rev;

> >  extern char unsigned *acpi_tables;

> >  extern size_t acpi_tables_len;

> >

> > diff --git a/qemu-options.hx b/qemu-options.hx

> > index a71aaf8..cff34f6 100644

> > --- a/qemu-options.hx

> > +++ b/qemu-options.hx

> > @@ -1492,6 +1492,15 @@ STEXI

> >  Disable HPET support.

> >  ETEXI

> >

> > +DEF("acpifadt", HAS_ARG, QEMU_OPTION_acpifadt, \

> > +    "-acpifadt  n    configure FADT revision number (1, 3, 5)\n",

> > +    QEMU_ARCH_ALL)

> > +STEXI

> > +@item -acpifadt @var{n}

> > +@findex -acpifadt

> > +Configure FADT revision number, default is 1.

> > +ETEXI

> > +

> >  DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable,

> >      "-acpitable

> [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compi

> ler_id=str][,asl_compiler_rev=n][,{data|file}=file1[:file2]...]\n"

> >      "                ACPI table description\n", QEMU_ARCH_I386)

> > diff --git a/vl.c b/vl.c

> > index e7c2c62..220e36d 100644

> > --- a/vl.c

> > +++ b/vl.c

> > @@ -64,6 +64,7 @@ int main(int argc, char **argv)

> >  #include "hw/bt.h"

> >  #include "sysemu/watchdog.h"

> >  #include "hw/smbios/smbios.h"

> > +#include "hw/acpi/acpi.h"

> >  #include "hw/xen/xen.h"

> >  #include "hw/qdev.h"

> >  #include "hw/loader.h"

> > @@ -158,6 +159,7 @@ int max_cpus = 1;

> >  int smp_cores = 1;

> >  int smp_threads = 1;

> >  int acpi_enabled = 1;

> > +uint8_t acpi_fadt_rev = 1;

> >  int no_hpet = 0;

> >  int fd_bootchk = 1;

> >  static int no_reboot;

> > @@ -2301,6 +2303,24 @@ static int parse_fw_cfg(void *opaque,

> QemuOpts *opts, Error **errp)

> >      return 0;

> >  }

> >

> > +static void fadt_parse(const char *arg)

> > +{

> > +    unsigned long rev;

> > +    int err;

> > +

> > +    err = qemu_strtoul(arg, NULL, 10, &rev);

> > +    if (err) {

> > +        error_printf("Invalid FADT revision.\n");

> > +        exit(1);

> > +    }

> > +    if (rev != 1 && rev != 3 && rev != 5) {

> > +        error_printf("Unsupported FADT revision %lu, "

> > +                     "please specify 1,3,5.\n", rev);

> > +        exit(1);

> > +    }

> > +    acpi_fadt_rev = rev;

> > +}

> > +

> >  static int device_help_func(void *opaque, QemuOpts *opts, Error

> **errp)

> >  {

> >      return qdev_device_help(opts);

> > @@ -3622,6 +3642,9 @@ int main(int argc, char **argv, char **envp)

> >                  qdev_prop_register_global(&slew_lost_ticks);

> >                  break;

> >              }

> > +            case QEMU_OPTION_acpifadt:

> > +                fadt_parse(optarg);

> > +                break;

> >              case QEMU_OPTION_acpitable:

> >                  opts = qemu_opts_parse_noisily(qemu_find_opts("acpi"),

> >                                                 optarg, true);

> >
diff mbox

Patch

diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index ce7cbc5..4479695 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -276,8 +276,22 @@  build_facs(GArray *table_data, BIOSLinker *linker)
     facs->length = cpu_to_le32(sizeof(*facs));
 }
 
+/* GAS */
+static void
+build_gas(struct AcpiGenericAddress *gas, uint8_t space_id,
+          uint8_t bit_width, uint8_t bit_offset,
+          uint8_t access_width, uint64_t address)
+{
+    gas->space_id = space_id;
+    gas->bit_width = bit_width;
+    gas->bit_offset = bit_offset;
+    gas->access_width = access_width;
+    gas->address = address;
+}
+
 /* Load chipset information in FADT */
-static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm)
+static void fadt_setup(AcpiFadtDescriptorRev5_1 *fadt, AcpiPmInfo *pm,
+                       uint8_t revision)
 {
     fadt->model = 1;
     fadt->reserved1 = 0;
@@ -309,6 +323,25 @@  static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm)
         fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_FORCE_APIC_CLUSTER_MODEL);
     }
     fadt->century = RTC_CENTURY;
+
+    /* Build ACPI 2.0 (FADT version 3+) GAS fields */
+    if (revision >= 3) {
+        /* EVT, CNT, TMR register matches hw/acpi/core.c */
+        build_gas(&fadt->xpm1a_event_block, AML_SYSTEM_IO,
+                  32, 0, 0, cpu_to_le64(pm->io_base));
+        build_gas(&fadt->xpm1a_control_block, AML_SYSTEM_IO,
+                  16, 0, 0, cpu_to_le64(pm->io_base + 0x04));
+        build_gas(&fadt->xpm_timer_block, AML_SYSTEM_IO,
+                  32, 0, 0, cpu_to_le64(pm->io_base + 0x08));
+        build_gas(&fadt->xgpe0_block, AML_SYSTEM_IO,
+                  pm->gpe0_blk_len << 3, 0, 0, cpu_to_le64(pm->gpe0_blk));
+    }
+
+    /* Build dummy ACPI 5.0 fields */
+    if (revision >= 5) {
+        build_gas(&fadt->sleep_control, AML_SYSTEM_MEMORY, 0, 0, 0, 0);
+        build_gas(&fadt->sleep_status, AML_SYSTEM_MEMORY, 0, 0, 0, 0);
+    }
 }
 
 
@@ -316,9 +349,9 @@  static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm)
 static void
 build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm,
            unsigned facs_tbl_offset, unsigned dsdt_tbl_offset,
-           const char *oem_id, const char *oem_table_id)
+           const char *oem_id, const char *oem_table_id, uint8_t revision)
 {
-    AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
+    AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
     unsigned fw_ctrl_offset = (char *)&fadt->firmware_ctrl - table_data->data;
     unsigned dsdt_entry_offset = (char *)&fadt->dsdt - table_data->data;
 
@@ -328,13 +361,28 @@  build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm,
         ACPI_BUILD_TABLE_FILE, facs_tbl_offset);
 
     /* DSDT address to be filled by Guest linker */
-    fadt_setup(fadt, pm);
     bios_linker_loader_add_pointer(linker,
         ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->dsdt),
         ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
 
-    build_header(linker, table_data,
-                 (void *)fadt, "FACP", sizeof(*fadt), 1, oem_id, oem_table_id);
+    if (revision > 2) {
+        fw_ctrl_offset = (char *)&fadt->Xfacs - table_data->data;
+        dsdt_entry_offset = (char *)&fadt->Xdsdt - table_data->data;
+
+        /* FACS address to be filled by Guest linker */
+        bios_linker_loader_add_pointer(linker,
+            ACPI_BUILD_TABLE_FILE, fw_ctrl_offset, sizeof(fadt->Xfacs),
+            ACPI_BUILD_TABLE_FILE, facs_tbl_offset);
+
+        /* DSDT address to be filled by Guest linker */
+        bios_linker_loader_add_pointer(linker,
+            ACPI_BUILD_TABLE_FILE, dsdt_entry_offset, sizeof(fadt->Xdsdt),
+            ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
+    }
+
+    fadt_setup(fadt, pm, revision);
+    build_header(linker, table_data, (void *)fadt, "FACP", sizeof(*fadt),
+                 revision, oem_id, oem_table_id);
 }
 
 void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
@@ -2681,7 +2729,7 @@  void acpi_build(AcpiBuildTables *tables, MachineState *machine)
     fadt = tables_blob->len;
     acpi_add_table(table_offsets, tables_blob);
     build_fadt(tables_blob, tables->linker, &pm, facs, dsdt,
-               slic_oem.id, slic_oem.table_id);
+               slic_oem.id, slic_oem.table_id, acpi_fadt_rev);
     aml_len += tables_blob->len - fadt;
 
     acpi_add_table(table_offsets, tables_blob);
diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h
index 7b3d93c..63df38d 100644
--- a/include/hw/acpi/acpi.h
+++ b/include/hw/acpi/acpi.h
@@ -173,6 +173,7 @@  void acpi_update_sci(ACPIREGS *acpi_regs, qemu_irq irq);
 
 /* acpi.c */
 extern int acpi_enabled;
+extern uint8_t acpi_fadt_rev;
 extern char unsigned *acpi_tables;
 extern size_t acpi_tables_len;
 
diff --git a/qemu-options.hx b/qemu-options.hx
index a71aaf8..cff34f6 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1492,6 +1492,15 @@  STEXI
 Disable HPET support.
 ETEXI
 
+DEF("acpifadt", HAS_ARG, QEMU_OPTION_acpifadt, \
+    "-acpifadt  n    configure FADT revision number (1, 3, 5)\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -acpifadt @var{n}
+@findex -acpifadt
+Configure FADT revision number, default is 1.
+ETEXI
+
 DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable,
     "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,{data|file}=file1[:file2]...]\n"
     "                ACPI table description\n", QEMU_ARCH_I386)
diff --git a/vl.c b/vl.c
index e7c2c62..220e36d 100644
--- a/vl.c
+++ b/vl.c
@@ -64,6 +64,7 @@  int main(int argc, char **argv)
 #include "hw/bt.h"
 #include "sysemu/watchdog.h"
 #include "hw/smbios/smbios.h"
+#include "hw/acpi/acpi.h"
 #include "hw/xen/xen.h"
 #include "hw/qdev.h"
 #include "hw/loader.h"
@@ -158,6 +159,7 @@  int max_cpus = 1;
 int smp_cores = 1;
 int smp_threads = 1;
 int acpi_enabled = 1;
+uint8_t acpi_fadt_rev = 1;
 int no_hpet = 0;
 int fd_bootchk = 1;
 static int no_reboot;
@@ -2301,6 +2303,24 @@  static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp)
     return 0;
 }
 
+static void fadt_parse(const char *arg)
+{
+    unsigned long rev;
+    int err;
+
+    err = qemu_strtoul(arg, NULL, 10, &rev);
+    if (err) {
+        error_printf("Invalid FADT revision.\n");
+        exit(1);
+    }
+    if (rev != 1 && rev != 3 && rev != 5) {
+        error_printf("Unsupported FADT revision %lu, "
+                     "please specify 1,3,5.\n", rev);
+        exit(1);
+    }
+    acpi_fadt_rev = rev;
+}
+
 static int device_help_func(void *opaque, QemuOpts *opts, Error **errp)
 {
     return qdev_device_help(opts);
@@ -3622,6 +3642,9 @@  int main(int argc, char **argv, char **envp)
                 qdev_prop_register_global(&slew_lost_ticks);
                 break;
             }
+            case QEMU_OPTION_acpifadt:
+                fadt_parse(optarg);
+                break;
             case QEMU_OPTION_acpitable:
                 opts = qemu_opts_parse_noisily(qemu_find_opts("acpi"),
                                                optarg, true);