Hi Rudolf,
that's an impressive piece of code!
On 01.02.2009 17:02, Rudolf Marek wrote:
Hello,
Following patch adds dynamic ACPI AML code generator which can be used to generate run-time ACPI ASL code like this:
Name (XXX, 0xXX)
Or:
Scope (\_SB.PCI0) {
Name (BUSN, Package (0x04) { 0x11111111, 0x22222222, 0x33333333, 0x44444444 })
Moreover it demonstrates its use on Asus M2V-MX SE where the SSDT table is generated by new function k8acpi_write_vars (technically similar to update_ssdt). But lot of nicer ;)
The ACPI binary code generator will be also useful for P-states generation, without ugly run-time patching of DSDT.
The old SSDT of K8 will be removed and rest of boards converted in next patch.
Tested on that board. It generates same table as the static SSDT patched by amdk8_acpi. Windows still boots too ;)
Signed-off-by: Rudolf Marek r.marek@assembler.cz
The following review is rather short and to-the-point. No offense intended.
A few comments about the general design: The global variable gencurrent is a bit irritating. How do you handle running on multiple cores? Same for len_stack. Besides that, both variables are in the global namespace and could use some acpi_ prefix or suffix.
Index: coreboot-v2/src/arch/i386/boot/acpi.c
--- coreboot-v2.orig/src/arch/i386/boot/acpi.c 2008-12-23 17:31:37.000000000 +0100 +++ coreboot-v2/src/arch/i386/boot/acpi.c 2009-02-01 11:05:03.171807310 +0100 @@ -24,6 +24,7 @@ #include <console/console.h> #include <string.h> #include <arch/acpi.h> +#include <arch/acpigen.h> #include <device/pci.h>
u8 acpi_checksum(u8 *table, u32 length) @@ -180,6 +181,34 @@ header->checksum = acpi_checksum((void *)mcfg, header->length); }
+/* this can be overriden by platform ACPI setup code,
- if it calls acpi_create_ssdt_generator */
+unsigned long __attribute__((weak)) acpi_fill_ssdt_generator(unsigned long current,
I think weak symbols are something that makes code harder to follow.
char *oem_table_id) {
The { belongs on another line.
- return current;
+}
+void acpi_create_ssdt_generator(acpi_header_t *ssdt, char *oem_table_id)
Strange function name.
+{
- unsigned long current=(unsigned long)ssdt+sizeof(acpi_header_t);
- memset((void *)ssdt, 0, sizeof(acpi_header_t));
- memcpy(&ssdt->signature, SSDT_NAME, 4);
Maybe strncpy instead?
- ssdt->revision = 2;
- memcpy(&ssdt->oem_id, OEM_ID, 6);
- memcpy(&ssdt->oem_table_id, oem_table_id, 8);
- ssdt->oem_revision = 42;
- memcpy(&ssdt->asl_compiler_id, "GENAML", 4);
- ssdt->asl_compiler_revision = 42;
Heh.
- ssdt->length = sizeof(acpi_header_t);
- acpigen_set_current((unsigned char *) current);
- current = acpi_fill_ssdt_generator(current, oem_table_id);
- /* recalculate length */
- ssdt->length = current - (unsigned long)ssdt;
- ssdt->checksum = acpi_checksum((void *)ssdt, ssdt->length);
+}
int acpi_create_srat_lapic(acpi_srat_lapic_t *lapic, u8 node, u8 apic) { lapic->type=0; Index: coreboot-v2/src/arch/i386/boot/acpigen.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ coreboot-v2/src/arch/i386/boot/acpigen.c 2009-02-01 11:04:09.927808334 +0100 @@ -0,0 +1,138 @@ +/*
- This file is part of the coreboot project.
- Copyright (C) 2009 Rudolf Marek r.marek@assembler.cz
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License v2 as published by
- the Free Software Foundation.
- This program 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 General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
+/* how many nesting we support */
"how many nesting levels do we support"
+#define ACPIGEN_LENSTACK_SIZE 10
+/* if you need to change this, change the acpigen_write_f and
- acpigen_patch_len */
Can you remove the empty line above?
+#define ACPIGEN_MAXLEN 0xfff
+#include <string.h> +#include <arch/acpigen.h> +#include <console/console.h>
+static char *gencurrent;
+char *len_stack[ACPIGEN_LENSTACK_SIZE]; +int ltop = 0;
These global variables need better names and some comments explaining what they do.
+static int acpigen_write_len_f()
Needs a doxygen comment.
+{
- ASSERT(ltop < (ACPIGEN_LENSTACK_SIZE - 1))
- len_stack[ltop++] = gencurrent;
- acpigen_emit_byte(0);
- acpigen_emit_byte(0);
- return 2;
+}
+void acpigen_patch_len(int len)
Needs a doxygen comment.
+{
- ASSERT(len <= ACPIGEN_MAXLEN)
- ASSERT(ltop > 0)
- char *p = len_stack[--ltop];
- /* generate store length for 0xfff max */
- p[0] = (0x40 | (len & 0xf));
- p[1] = (len >> 4 & 0xff);
+}
+void acpigen_set_current(char *curr) {
Needs a doxygen comment.
- gencurrent = curr;
+}
+char *acpigen_get_current(void) {
Needs a doxygen comment.
- return gencurrent;
+}
+int acpigen_emit_byte(unsigned char b) +{
- (*gencurrent++) = b;
- return 1;
+}
+int acpigen_write_package(int nr_el) +{
- int len;
- /* package op */
- acpigen_emit_byte(0x12);
- len = acpigen_write_len_f();
- acpigen_emit_byte(nr_el);
- return len + 2;
+}
+int acpigen_write_byte(unsigned int data) +{
- /* byte op */
- acpigen_emit_byte(0xa);
- acpigen_emit_byte(data & 0xff);
- return 2;
+}
+int acpigen_write_dword(unsigned int data) +{
- /* dword op */
- acpigen_emit_byte(0xc);
- acpigen_emit_byte(data & 0xff);
- acpigen_emit_byte((data >> 8) & 0xff);
- acpigen_emit_byte((data >> 16) & 0xff);
- acpigen_emit_byte((data >> 24) & 0xff);
- return 5;
+}
+int acpigen_write_name_byte(char *name, uint8_t val) {
- int len;
- len = acpigen_write_name(name);
- len += acpigen_write_byte(val);
- return len;
+}
+int acpigen_write_name_dword(char *name, uint32_t val) {
- int len;
- len = acpigen_write_name(name);
- len += acpigen_write_dword(val);
- return len;
+}
+int acpigen_emit_stream(char *data, int size) {
- int i;
- for (i = 0; i < size; i++) {
acpigen_emit_byte(data[i]);
- }
- return size;
+}
+int acpigen_write_name(char *name) +{
- int len = strlen(name);
- /* name op */
- acpigen_emit_byte(0x8);
- acpigen_emit_stream(name, len);
- return len + 1;
+}
+int acpigen_write_scope(char *name) +{
- int len;
- /* scope op */
- acpigen_emit_byte(0x10);
- len = acpigen_write_len_f();
- return len + acpigen_emit_stream(name, strlen(name)) + 1;
+} Index: coreboot-v2/src/arch/i386/boot/Config.lb =================================================================== --- coreboot-v2.orig/src/arch/i386/boot/Config.lb 2009-02-01 10:25:41.491806925 +0100 +++ coreboot-v2/src/arch/i386/boot/Config.lb 2009-02-01 10:30:56.756876765 +0100 @@ -13,5 +13,5 @@ end if HAVE_ACPI_TABLES object acpi.o +object acpigen.o end
Index: coreboot-v2/src/arch/i386/include/arch/acpigen.h
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ coreboot-v2/src/arch/i386/include/arch/acpigen.h 2009-02-01 10:30:56.758807179 +0100 @@ -0,0 +1,37 @@ +/*
- This file is part of the coreboot project.
- Copyright (C) 2009 Rudolf Marek r.marek@assembler.cz
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License v2 as published by
- the Free Software Foundation.
- This program 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 General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
+#ifndef LIBACPI_H +#define LIBACPI_H +#include <assert.h> +#include <stdlib.h> +#include <stdint.h>
+void acpigen_patch_len(int len); +void acpigen_set_current(char *curr); +char *acpigen_get_current(void); +int acpigen_write_package(int nr_el); +int acpigen_write_byte(unsigned int data); +int acpigen_emit_byte(unsigned char data); +int acpigen_emit_stream(char *data, int size); +int acpigen_write_dword(unsigned int data); +int acpigen_write_name(char *name); +int acpigen_write_name_dword(char *name, uint32_t val); +int acpigen_write_name_byte(char *name, uint8_t val); +int acpigen_write_scope(char *name); +#endif Index: coreboot-v2/src/arch/i386/include/arch/acpi.h =================================================================== --- coreboot-v2.orig/src/arch/i386/include/arch/acpi.h 2008-12-23 17:38:41.000000000 +0100 +++ coreboot-v2/src/arch/i386/include/arch/acpi.h 2009-02-01 10:52:25.478808600 +0100 @@ -29,6 +29,7 @@ #define MCFG_NAME "MCFG" #define SRAT_NAME "SRAT" #define SLIT_NAME "SLIT" +#define SSDT_NAME "SSDT"
#define RSDT_TABLE "RSDT " #define HPET_TABLE "AMD64 " @@ -293,6 +294,8 @@ unsigned long acpi_fill_madt(unsigned long current); unsigned long acpi_fill_mcfg(unsigned long current); unsigned long acpi_fill_srat(unsigned long current); +unsigned long acpi_fill_ssdt_generator(unsigned long current, char *oem_table_id); +void acpi_create_ssdt_generator(acpi_header_t *ssdt, char *oem_table_id);
The two functions above have rather similar names and it's not immedately ofvious how they differ.
void acpi_create_fadt(acpi_fadt_t *fadt,acpi_facs_t *facs,void *dsdt);
/* These can be used by the target port */ Index: coreboot-v2/src/mainboard/asus/m2v-mx_se/acpi_tables.c =================================================================== --- coreboot-v2.orig/src/mainboard/asus/m2v-mx_se/acpi_tables.c 2008-12-23 17:46:56.000000000 +0100 +++ coreboot-v2/src/mainboard/asus/m2v-mx_se/acpi_tables.c 2009-02-01 11:23:05.142807423 +0100 @@ -30,9 +30,9 @@ #include <device/pci_ids.h> #include <../../../southbridge/via/vt8237r/vt8237r.h> #include <../../../southbridge/via/k8t890/k8t890.h> +#include <../../../northbridge/amd/amdk8/amdk8_acpi.h>
extern unsigned char AmlCode[]; -extern unsigned char AmlCode_ssdt[];
unsigned long acpi_fill_mcfg(unsigned long current) { @@ -81,6 +81,12 @@ return current; }
+unsigned long acpi_fill_ssdt_generator(unsigned long current, char *oem_table_id) {
- k8acpi_write_vars();
- /* put PSTATES generator call here */
- return (unsigned long) (acpigen_get_current());
acpigen_get_current is a really confusingly named function.
+}
unsigned long write_acpi_tables(unsigned long start) { unsigned long current; @@ -175,13 +181,10 @@ /* SSDT */ printk_debug("ACPI: * SSDT\n"); ssdt = (acpi_header_t *)current;
- current += ((acpi_header_t *)AmlCode_ssdt)->length;
- memcpy((void *)ssdt, (void *)AmlCode_ssdt, ((acpi_header_t *)AmlCode_ssdt)->length);
- update_ssdt((void*)ssdt);
/* recalculate checksum */
ssdt->checksum = 0;
ssdt->checksum = acpi_checksum((unsigned char *)ssdt,ssdt->length);
- acpi_add_table(rsdt,ssdt);
- acpi_create_ssdt_generator(ssdt, "DYNADATA");
"COREBOOT" please. That way, people can actually find out where the table came from. The fact that the data are generated dynamically is already visible from the compiler ID.
current += ssdt->length;
acpi_add_table(rsdt, ssdt);
printk_info("ACPI: done.\n"); return current;
Index: coreboot-v2/src/northbridge/amd/amdk8/amdk8_acpi.c
--- coreboot-v2.orig/src/northbridge/amd/amdk8/amdk8_acpi.c 2008-12-23 17:31:36.000000000 +0100 +++ coreboot-v2/src/northbridge/amd/amdk8/amdk8_acpi.c 2009-02-01 11:26:00.102959065 +0100 @@ -259,6 +259,80 @@ } }
+static int k8acpi_write_HT(void) {
- device_t dev;
- uint32_t dword;
- int len, lenp, i;
- len = acpigen_write_name("HCLK");
- lenp = acpigen_write_package(HC_POSSIBLE_NUM);
- for(i=0;i<sysconf.hc_possible_num;i++) {
lenp += acpigen_write_dword(sysconf.pci1234[i]);
- }
- for(i=sysconf.hc_possible_num; i<HC_POSSIBLE_NUM; i++) { // in case we set array size to other than 8
Comment on a separate line, please.
lenp += acpigen_write_dword(0x0);
- }
- acpigen_patch_len(lenp - 1);
acpigen_patch_len is a name which does not really reveal function purpose.
- len += lenp;
- len += acpigen_write_name("HCDN");
- lenp = acpigen_write_package(HC_POSSIBLE_NUM);
- for(i=0;i<sysconf.hc_possible_num;i++) {
lenp += acpigen_write_dword(sysconf.hcdn[i]);
- }
- for(i=sysconf.hc_possible_num; i<HC_POSSIBLE_NUM; i++) { // in case we set array size to other than 8
lenp += acpigen_write_dword(0x20202020);
- }
- acpigen_patch_len(lenp - 1);
- len += lenp;
- return len;
+}
+static int k8acpi_write_pci_data(int dlen, char *name, int offset) {
- device_t dev;
- uint32_t dword;
- int len, lenp, i;
- dev = dev_find_slot(0, PCI_DEVFN(0x18, 1));
- len = acpigen_write_name(name);
- lenp = acpigen_write_package(dlen);
- for(i=0; i<dlen; i++) {
dword = pci_read_config32(dev, offset+i*4);
lenp += acpigen_write_dword(dword);
- }
- // minus the opcode
- acpigen_patch_len(lenp - 1);
- return len + lenp;
+}
+int k8acpi_write_vars(void) +{
- int lens;
Maybe use len instead of lens?
- msr_t msr;
- char pscope[] = "\._SB_PCI0";
- lens = acpigen_write_scope(pscope);
- lens += k8acpi_write_pci_data(4, "BUSN", 0xe0);
- lens += k8acpi_write_pci_data(8, "PCIO", 0xc0);
- lens += k8acpi_write_pci_data(16, "MMIO", 0x80);
- lens += acpigen_write_name_byte("SBLK", sysconf.sblk);
- lens += acpigen_write_name_byte("CBST",
((sysconf.pci1234[0] >> 12) & 0xff) ? 0xf : 0x0);
- lens += acpigen_write_name_dword("SBDN", sysconf.sbdn);
- msr = rdmsr(TOP_MEM);
- lens += acpigen_write_name_dword("TOM1", msr.lo);
I know the original code did not have it, but can you please add TOM2?
- lens += k8acpi_write_HT();
- //minus opcode
- acpigen_patch_len(lens - 1);
- return lens;
+}
// used by acpi_tables.h
Index: coreboot-v2/src/northbridge/amd/amdk8/amdk8_acpi.h
--- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ coreboot-v2/src/northbridge/amd/amdk8/amdk8_acpi.h 2009-02-01 11:28:13.236202005 +0100 @@ -0,0 +1,25 @@ +/*
- This file is part of the coreboot project.
- Copyright (C) 2009 Rudolf Marek r.marek@assembler.cz
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License v2 as published by
- the Free Software Foundation.
- This program 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 General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
+#ifndef AMDK8_ACPI_H +#define AMDK8_ACPI_H +#include <arch/acpigen.h>
+int k8acpi_write_vars(void);
+#endif
Regards, Carl-Daniel