From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:47671) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UhohF-0000Sy-N1 for qemu-devel@nongnu.org; Wed, 29 May 2013 18:18:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UhogY-0007BU-Ti for qemu-devel@nongnu.org; Wed, 29 May 2013 18:18:17 -0400 Received: from vms173017pub.verizon.net ([206.46.173.17]:24533) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UhogY-0007Ac-Ge for qemu-devel@nongnu.org; Wed, 29 May 2013 18:17:34 -0400 Received: from wf-rch.minyard.home ([unknown] [173.74.121.95]) by vms173017.mailsrvcs.net (Sun Java(tm) System Messaging Server 7u2-7.02 32bit (built Apr 16 2009)) with ESMTPA id <0MNK00IVPZ8D4G50@vms173017.mailsrvcs.net> for qemu-devel@nongnu.org; Wed, 29 May 2013 17:17:04 -0500 (CDT) From: minyard@acm.org Date: Wed, 29 May 2013 17:08:12 -0500 Message-id: <1369865296-19584-17-git-send-email-minyard@acm.org> In-reply-to: <1369865296-19584-1-git-send-email-minyard@acm.org> References: <1369865296-19584-1-git-send-email-minyard@acm.org> Subject: [Qemu-devel] [PATCH 16/20] acpi: Add table construction tools List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: Corey Minyard , openipmi-developer@lists.sourceforge.net From: Corey Minyard Add a set of functions to allow construction of ACPI elements dynamically. Signed-off-by: Corey Minyard --- hw/acpi/Makefile.objs | 2 +- hw/acpi/acpi-elements.c | 351 +++++++++++++++++++++++++++++++++++++++ include/hw/acpi/acpi-elements.h | 77 +++++++++ 3 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 hw/acpi/acpi-elements.c create mode 100644 include/hw/acpi/acpi-elements.h diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index a0b63b5..abfdc31 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -1,2 +1,2 @@ -common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o +common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o acpi-elements.o diff --git a/hw/acpi/acpi-elements.c b/hw/acpi/acpi-elements.c new file mode 100644 index 0000000..08d3f40 --- /dev/null +++ b/hw/acpi/acpi-elements.c @@ -0,0 +1,351 @@ +/* + * Dynamically construct ACPI elements + * + * Copyright (C) 2012 Corey Minyard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "hw/acpi/acpi-elements.h" +#include + +#define acpi_add_byte(data, val) do { **data = val; (*data)++; } while(0) + +static int +acpi_add_Pkglen(char **data, int dlen, int length) +{ + int pkglen; + + /* + * The funny length values following are because we include the length + * bytes in the full length. + */ + if (length <= 0x3e) { + if (dlen > 0) { + acpi_add_byte(data, length + 1); + } + return 1; + } else if (length <= 0xffd) { + pkglen = 2; + } else if (length <= 0xffffc) { + pkglen = 3; + } else if (length <= 0xffffffb) { + pkglen = 4; + } else { + return -1; + } + length += pkglen; + if (pkglen <= dlen) { + acpi_add_byte(data, ((pkglen - 1) << 6) | (length & 0xf)); + length >>= 4; + pkglen--; + while (pkglen > 0) { + acpi_add_byte(data, length & 0xff); + length >>= 8; + pkglen--; + } + } + return pkglen; +} + +static int +acpi_add_NameString(char **data, int dlen, const char *name) +{ + int length = strlen(name); + int i; + + if (dlen >= 4) { + i = 0; + while ((i < 4) && (i < length)) { + acpi_add_byte(data, *name++); + i++; + } + while (i < 4) { + acpi_add_byte(data, '_'); + i++; + } + } + return 4; +} + +int +acpi_add_Device(char **data, int dlen, const char *name, + contained_acpi_elem e, void *opaque) +{ + int length, plen, totlen; + + length = e(NULL, 0, opaque); + if (length < 0) + return length; + + plen = acpi_add_Pkglen(NULL, 0, length); + if (plen < 0) + return plen; + totlen = length + plen + 6; + if (dlen >= totlen) { + acpi_add_byte(data, 0x5b); + acpi_add_byte(data, 0x82); + dlen -= 2; + dlen -= acpi_add_Pkglen(data, dlen, length); + dlen -= acpi_add_NameString(data, dlen, name); + dlen -= e(data, dlen, opaque); + } + + return totlen; +} + +int +acpi_add_Name(char **data, int dlen, const char *name, + contained_acpi_elem e, void *opaque) +{ + int rv; + + if (dlen >= 5) { + acpi_add_byte(data, 0x8); + dlen -= 1; + dlen -= acpi_add_NameString(data, dlen, name); + } + rv = e(data, dlen, opaque); + if (rv < 0) + return rv; + return rv + 5; +} + +int +acpi_add_Method(char **data, int dlen, const char *name, uint8_t flags, + contained_acpi_elem e, void *opaque) +{ + int elen, plen; + + elen = e(NULL, 0, opaque); + if (elen < 0) + return elen; + + plen = acpi_add_Pkglen(NULL, 0, elen + 5); + if (plen < 0) + return plen; + if (plen + elen + 6 <= dlen) { + acpi_add_byte(data, 0x14); + dlen -= 1; + dlen -= acpi_add_Pkglen(data, dlen, elen + 5); + dlen -= acpi_add_NameString(data, dlen, name); + acpi_add_byte(data, flags); + dlen -= 1; + dlen -= e(data, dlen, opaque); + } + return plen + elen + 6; +} + +int +acpi_add_Integer(char **data, int dlen, void *vval) +{ + uint64_t val = *((uint64_t *) vval); + int length, i; + unsigned char op; + + if ((val == 0) || (val == 1)) { + /* ZeroOp or OneOp */ + if (dlen > 0) { + acpi_add_byte(data, val); + } + return 1; + } if (val <= 0xff) { + length = 1; + op = 0x0a; + } else if (val <= 0xffff) { + length = 2; + op = 0x0b; + } else if (val <= 0xffffffff) { + length = 4; + op = 0x0c; + } else { + length = 8; + op = 0x0e; + } + + if (dlen >= length + 1) { + acpi_add_byte(data, op); + for (i = 0; i < length; i++) { + acpi_add_byte(data, val & 0xff); + val >>= 8; + } + } + return length + 1; +} + +/* + * A compressed EISA ID has the top bit reserved, the next 15 bits as + * compressed ASCII upper case letters, and the bottom 16 bits as four + * hex digits. + */ +int +acpi_add_EISAID(char **data, int dlen, void *val) +{ + char *str = val; + uint32_t ival = 0; + int i; + + if (dlen >= 5) { + acpi_add_byte(data, 0xc); /* dword */ + if (strlen(val) != 7) + return -1; + for (i = 0; i < 3; i++) { + if (str[i] < 'A' || str[i] > 'Z') + return -1; + ival = (ival << 5) | (str[i] - 0x40); + } + for (; i < 7; i++) { + int v; + if (str[i] >= '0' && str[i] <= '9') + v = str[i] - '0'; + else if (str[i] >= 'A' && str[i] <= 'F') + v = str[i] - 'A' + 10; + else + return -1; + ival = (ival << 4) | v; + } + /* Note that for some reason this is big endian */ + for (i = 0; i < 4; i++) { + acpi_add_byte(data, (ival >> 24) & 0xff); + ival <<= 8; + } + } + + return 5; +} + +int +acpi_add_BufferOp(char **data, int dlen, + contained_acpi_elem e, void *opaque) +{ + int blen, slen, plen, tlen; + uint64_t val; + + blen = e(NULL, 0, opaque); + if (blen < 0) + return blen; + + val = blen; + slen = acpi_add_Integer(NULL, 0, &val); + if (slen < 0) + return slen; + plen = acpi_add_Pkglen(NULL, 0, slen + blen); + if (plen < 0) + return plen; + tlen = blen + slen + plen + 1; + if (tlen <= dlen) { + acpi_add_byte(data, 0x11); + dlen--; + dlen -= acpi_add_Pkglen(data, dlen, slen + blen); + dlen -= acpi_add_Integer(data, dlen, &val); + dlen -= e(data, dlen, opaque); + } + return tlen; +} + +int +acpi_add_Return(char **data, int dlen, void *val) +{ + int blen; + + blen = acpi_add_Integer(NULL, 0, val); + if (blen + 1 <= dlen) { + acpi_add_byte(data, 0xa4); + dlen -= 1; + dlen -= acpi_add_Integer(data, dlen, val); + } + return blen + 1; +} + +/* + * Note that str is void*, not char*, so it can be passed as a + * contained element. + */ +static int +unicode_helper(char **data, int dlen, void *vstr) +{ + char *str = vstr; + int len = strlen(str) + 1; + + if (len * 2 <= dlen) { + while (*str) { + acpi_add_byte(data, *str++); + acpi_add_byte(data, 0); + } + acpi_add_byte(data, 0); + acpi_add_byte(data, 0); + } + return len * 2; +} +int +acpi_add_Unicode(char **data, int dlen, void *vstr) +{ + int len; + + len = acpi_add_BufferOp(NULL, 0, unicode_helper, vstr); + if (len < 0) + return len; + if (len <= dlen) { + acpi_add_BufferOp(data, dlen, unicode_helper, vstr); + } + return len; +} + +int +acpi_add_IO16(char **data, int dlen, + uint16_t minaddr, uint16_t maxaddr, + uint8_t align, uint8_t range) +{ + if (dlen >= 8) { + acpi_add_byte(data, 0x47); + acpi_add_byte(data, 1); + acpi_add_byte(data, minaddr & 0xff); + acpi_add_byte(data, minaddr >> 8); + acpi_add_byte(data, maxaddr & 0xff); + acpi_add_byte(data, maxaddr >> 8); + acpi_add_byte(data, align); + acpi_add_byte(data, range); + } + return 8; +} + +int +acpi_add_Interrupt(char **data, int dlen, int irq, + int consumer, int mode, int polarity, int sharing) +{ + if (dlen >= 9) { + acpi_add_byte(data, 0x89); + acpi_add_byte(data, 6); + acpi_add_byte(data, 0); + acpi_add_byte(data, consumer | (mode << 1) | (polarity << 2) | (sharing << 3)); + acpi_add_byte(data, 1); /* Only 1 irq */ + acpi_add_byte(data, irq); + acpi_add_byte(data, 0); + acpi_add_byte(data, 0); + acpi_add_byte(data, 0); + } + return 9; +} + +int +acpi_add_EndResource(char **data, int dlen) +{ + if (dlen >= 2) { + acpi_add_byte(data, 0x79); + acpi_add_byte(data, 0); + } + return 2; +} diff --git a/include/hw/acpi/acpi-elements.h b/include/hw/acpi/acpi-elements.h new file mode 100644 index 0000000..68ce151 --- /dev/null +++ b/include/hw/acpi/acpi-elements.h @@ -0,0 +1,77 @@ +/* + * Dynamically construct ACPI elements + * + * Copyright (C) 2012 Corey Minyard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef __ACPI_ELEMENTS_H +#define __ACPI_ELEMENTS_H + +#include + +typedef int (*contained_acpi_elem)(char **data, int length, + void *opaque); + +int acpi_add_Device(char **data, int dlen, const char *name, + contained_acpi_elem e, void *opaque); + +int acpi_add_Name(char **data, int dlen, const char *name, + contained_acpi_elem e, void *opaque); + +int acpi_add_Method(char **data, int dlen, const char *name, uint8_t flags, + contained_acpi_elem e, void *opaque); + +/* Pass in a pointer to a u64 */ +int acpi_add_Integer(char **data, int dlen, void *val); + +/* Pass in a pointer to a string */ +int acpi_add_EISAID(char **data, int dlen, void *val); + +int acpi_add_BufferOp(char **data, int dlen, + contained_acpi_elem e, void *opaque); + +/* Pass in a pointer to a u64 */ +int acpi_add_Return(char **data, int dlen, void *); + +/* + * Note that str is void*, not char*, so it can be passed as a + * contained element. + */ +int acpi_add_Unicode(char **data, int dlen, void *vstr); + +int acpi_add_IO16(char **data, int dlen, + uint16_t minaddr, uint16_t maxaddr, + uint8_t align, uint8_t range); + +#define ACPI_RESOURCE_PRODUCER 0 +#define ACPI_RESOURCE_CONSUMER 1 +#define ACPI_INTERRUPT_MODE_LEVEL 0 +#define ACPI_INTERRUPT_MODE_EDGE 1 +#define ACPI_INTERRUPT_POLARITY_ACTIVE_HIGH 0 +#define ACPI_INTERRUPT_POLARITY_ACTIVE_LOW 1 +#define ACPI_INTERRUPT_EXCLUSIVE 0 +#define ACPI_INTERRUPT_SHARED 1 +#define ACPI_INTERRUPT_EXCLUSIVE_WAKE 2 +#define ACPI_INTERRUPT_SHARED_WAKE 3 + +int acpi_add_Interrupt(char **data, int dlen, int irq, + int consumer, int mode, int polarity, int sharing); + +int acpi_add_EndResource(char **data, int dlen); + +#endif -- 1.7.9.5