uboot/lib/acpi/acpi_table.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Generic code used to generate ACPI tables
   4 *
   5 * Copyright 2019 Google LLC
   6 */
   7
   8#include <common.h>
   9#include <dm.h>
  10#include <cpu.h>
  11#include <log.h>
  12#include <mapmem.h>
  13#include <tables_csum.h>
  14#include <timestamp.h>
  15#include <version.h>
  16#include <acpi/acpi_table.h>
  17#include <asm/global_data.h>
  18#include <dm/acpi.h>
  19
  20/*
  21 * OEM_REVISION is 32-bit unsigned number. It should be increased only when
  22 * changing software version. Therefore it should not depend on build time.
  23 * U-Boot calculates it from U-Boot version and represent it in hexadecimal
  24 * notation. As U-Boot version is in form year.month set low 8 bits to 0x01
  25 * to have valid date. So for U-Boot version 2021.04 OEM_REVISION is set to
  26 * value 0x20210401.
  27 */
  28#define OEM_REVISION ((((U_BOOT_VERSION_NUM / 1000) % 10) << 28) | \
  29                      (((U_BOOT_VERSION_NUM / 100) % 10) << 24) | \
  30                      (((U_BOOT_VERSION_NUM / 10) % 10) << 20) | \
  31                      ((U_BOOT_VERSION_NUM % 10) << 16) | \
  32                      (((U_BOOT_VERSION_NUM_PATCH / 10) % 10) << 12) | \
  33                      ((U_BOOT_VERSION_NUM_PATCH % 10) << 8) | \
  34                      0x01)
  35
  36int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags)
  37{
  38        struct acpi_table_header *header = &dmar->header;
  39        struct cpu_info info;
  40        struct udevice *cpu;
  41        int ret;
  42
  43        ret = uclass_first_device(UCLASS_CPU, &cpu);
  44        if (ret)
  45                return log_msg_ret("cpu", ret);
  46        ret = cpu_get_info(cpu, &info);
  47        if (ret)
  48                return log_msg_ret("info", ret);
  49        memset((void *)dmar, 0, sizeof(struct acpi_dmar));
  50
  51        /* Fill out header fields. */
  52        acpi_fill_header(&dmar->header, "DMAR");
  53        header->length = sizeof(struct acpi_dmar);
  54        header->revision = acpi_get_table_revision(ACPITAB_DMAR);
  55
  56        dmar->host_address_width = info.address_width - 1;
  57        dmar->flags = flags;
  58
  59        return 0;
  60}
  61
  62int acpi_get_table_revision(enum acpi_tables table)
  63{
  64        switch (table) {
  65        case ACPITAB_FADT:
  66                return ACPI_FADT_REV_ACPI_3_0;
  67        case ACPITAB_MADT:
  68                return ACPI_MADT_REV_ACPI_3_0;
  69        case ACPITAB_MCFG:
  70                return ACPI_MCFG_REV_ACPI_3_0;
  71        case ACPITAB_TCPA:
  72                /* This version and the rest are open-coded */
  73                return 2;
  74        case ACPITAB_TPM2:
  75                return 4;
  76        case ACPITAB_SSDT: /* ACPI 3.0 upto 6.3: 2 */
  77                return 2;
  78        case ACPITAB_SRAT: /* ACPI 2.0: 1, ACPI 3.0: 2, ACPI 4.0 to 6.3: 3 */
  79                return 1; /* TODO Should probably be upgraded to 2 */
  80        case ACPITAB_DMAR:
  81                return 1;
  82        case ACPITAB_SLIT: /* ACPI 2.0 upto 6.3: 1 */
  83                return 1;
  84        case ACPITAB_SPMI: /* IMPI 2.0 */
  85                return 5;
  86        case ACPITAB_HPET: /* Currently 1. Table added in ACPI 2.0 */
  87                return 1;
  88        case ACPITAB_VFCT: /* ACPI 2.0/3.0/4.0: 1 */
  89                return 1;
  90        case ACPITAB_IVRS:
  91                return IVRS_FORMAT_FIXED;
  92        case ACPITAB_DBG2:
  93                return 0;
  94        case ACPITAB_FACS: /* ACPI 2.0/3.0: 1, ACPI 4.0 to 6.3: 2 */
  95                return 1;
  96        case ACPITAB_RSDT: /* ACPI 1.0 upto 6.3: 1 */
  97                return 1;
  98        case ACPITAB_XSDT: /* ACPI 2.0 upto 6.3: 1 */
  99                return 1;
 100        case ACPITAB_RSDP: /* ACPI 2.0 upto 6.3: 2 */
 101                return 2;
 102        case ACPITAB_HEST:
 103                return 1;
 104        case ACPITAB_NHLT:
 105                return 5;
 106        case ACPITAB_BERT:
 107                return 1;
 108        case ACPITAB_SPCR:
 109                return 2;
 110        default:
 111                return -EINVAL;
 112        }
 113}
 114
 115void acpi_fill_header(struct acpi_table_header *header, char *signature)
 116{
 117        memcpy(header->signature, signature, 4);
 118        memcpy(header->oem_id, OEM_ID, 6);
 119        memcpy(header->oem_table_id, OEM_TABLE_ID, 8);
 120        header->oem_revision = OEM_REVISION;
 121        memcpy(header->aslc_id, ASLC_ID, 4);
 122}
 123
 124void acpi_align(struct acpi_ctx *ctx)
 125{
 126        ctx->current = (void *)ALIGN((ulong)ctx->current, 16);
 127}
 128
 129void acpi_align64(struct acpi_ctx *ctx)
 130{
 131        ctx->current = (void *)ALIGN((ulong)ctx->current, 64);
 132}
 133
 134void acpi_inc(struct acpi_ctx *ctx, uint amount)
 135{
 136        ctx->current += amount;
 137}
 138
 139void acpi_inc_align(struct acpi_ctx *ctx, uint amount)
 140{
 141        ctx->current += amount;
 142        acpi_align(ctx);
 143}
 144
 145/**
 146 * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length
 147 * and checksum.
 148 */
 149int acpi_add_table(struct acpi_ctx *ctx, void *table)
 150{
 151        int i, entries_num;
 152        struct acpi_rsdt *rsdt;
 153        struct acpi_xsdt *xsdt;
 154
 155        /* The RSDT is mandatory while the XSDT is not */
 156        rsdt = ctx->rsdt;
 157
 158        /* This should always be MAX_ACPI_TABLES */
 159        entries_num = ARRAY_SIZE(rsdt->entry);
 160
 161        for (i = 0; i < entries_num; i++) {
 162                if (rsdt->entry[i] == 0)
 163                        break;
 164        }
 165
 166        if (i >= entries_num) {
 167                log_err("ACPI: Error: too many tables\n");
 168                return -E2BIG;
 169        }
 170
 171        /* Add table to the RSDT */
 172        rsdt->entry[i] = map_to_sysmem(table);
 173
 174        /* Fix RSDT length or the kernel will assume invalid entries */
 175        rsdt->header.length = sizeof(struct acpi_table_header) +
 176                                (sizeof(u32) * (i + 1));
 177
 178        /* Re-calculate checksum */
 179        rsdt->header.checksum = 0;
 180        rsdt->header.checksum = table_compute_checksum((u8 *)rsdt,
 181                                                       rsdt->header.length);
 182
 183        /*
 184         * And now the same thing for the XSDT. We use the same index as for
 185         * now we want the XSDT and RSDT to always be in sync in U-Boot
 186         */
 187        xsdt = ctx->xsdt;
 188
 189        /* Add table to the XSDT */
 190        xsdt->entry[i] = map_to_sysmem(table);
 191
 192        /* Fix XSDT length */
 193        xsdt->header.length = sizeof(struct acpi_table_header) +
 194                                (sizeof(u64) * (i + 1));
 195
 196        /* Re-calculate checksum */
 197        xsdt->header.checksum = 0;
 198        xsdt->header.checksum = table_compute_checksum((u8 *)xsdt,
 199                                                       xsdt->header.length);
 200
 201        return 0;
 202}
 203
 204void acpi_create_dbg2(struct acpi_dbg2_header *dbg2,
 205                      int port_type, int port_subtype,
 206                      struct acpi_gen_regaddr *address, u32 address_size,
 207                      const char *device_path)
 208{
 209        uintptr_t current;
 210        struct acpi_dbg2_device *device;
 211        u32 *dbg2_addr_size;
 212        struct acpi_table_header *header;
 213        size_t path_len;
 214        const char *path;
 215        char *namespace;
 216
 217        /* Fill out header fields. */
 218        current = (uintptr_t)dbg2;
 219        memset(dbg2, '\0', sizeof(struct acpi_dbg2_header));
 220        header = &dbg2->header;
 221
 222        header->revision = acpi_get_table_revision(ACPITAB_DBG2);
 223        acpi_fill_header(header, "DBG2");
 224        header->aslc_revision = ASL_REVISION;
 225
 226        /* One debug device defined */
 227        dbg2->devices_offset = sizeof(struct acpi_dbg2_header);
 228        dbg2->devices_count = 1;
 229        current += sizeof(struct acpi_dbg2_header);
 230
 231        /* Device comes after the header */
 232        device = (struct acpi_dbg2_device *)current;
 233        memset(device, 0, sizeof(struct acpi_dbg2_device));
 234        current += sizeof(struct acpi_dbg2_device);
 235
 236        device->revision = 0;
 237        device->address_count = 1;
 238        device->port_type = port_type;
 239        device->port_subtype = port_subtype;
 240
 241        /* Base Address comes after device structure */
 242        memcpy((void *)current, address, sizeof(struct acpi_gen_regaddr));
 243        device->base_address_offset = current - (uintptr_t)device;
 244        current += sizeof(struct acpi_gen_regaddr);
 245
 246        /* Address Size comes after address structure */
 247        dbg2_addr_size = (uint32_t *)current;
 248        device->address_size_offset = current - (uintptr_t)device;
 249        *dbg2_addr_size = address_size;
 250        current += sizeof(uint32_t);
 251
 252        /* Namespace string comes last, use '.' if not provided */
 253        path = device_path ? : ".";
 254        /* Namespace string length includes NULL terminator */
 255        path_len = strlen(path) + 1;
 256        namespace = (char *)current;
 257        device->namespace_string_length = path_len;
 258        device->namespace_string_offset = current - (uintptr_t)device;
 259        strncpy(namespace, path, path_len);
 260        current += path_len;
 261
 262        /* Update structure lengths and checksum */
 263        device->length = current - (uintptr_t)device;
 264        header->length = current - (uintptr_t)dbg2;
 265        header->checksum = table_compute_checksum(dbg2, header->length);
 266}
 267