1/*-
2 * Copyright (c) 2004 Nate Lawson (SDG)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/bus.h>
32
33#include <contrib/dev/acpica/include/acpi.h>
34
35#include <dev/acpica/acpivar.h>
36
37enum ops_t {
38	OP_NONE,
39	OP_LEQ,
40	OP_GEQ,
41	OP_EQL,
42};
43
44enum val_t {
45	OEM,
46	OEM_REV,
47	CREATOR,
48	CREATOR_REV,
49};
50
51struct acpi_q_rule {
52    char	sig[ACPI_NAME_SIZE];	/* Table signature to match */
53    enum val_t	val;
54    union {
55	char	*id;
56	enum ops_t op;
57    } x;
58    union {
59	char	*tid;
60	int	rev;
61    } y;
62};
63
64struct acpi_q_entry {
65    const struct acpi_q_rule *match;
66    int		quirks;
67};
68
69#include "acpi_quirks.h"
70
71static int	aq_revcmp(int revision, enum ops_t op, int value);
72static int	aq_strcmp(char *actual, char *possible);
73static int	aq_match_header(ACPI_TABLE_HEADER *hdr,
74		    const struct acpi_q_rule *match);
75
76static int
77aq_revcmp(int revision, enum ops_t op, int value)
78{
79    switch (op) {
80    case OP_LEQ:
81	if (revision <= value)
82	    return (TRUE);
83	break;
84    case OP_GEQ:
85	if (revision >= value)
86	    return (TRUE);
87	break;
88    case OP_EQL:
89	if (revision == value)
90	    return (TRUE);
91	break;
92    case OP_NONE:
93	return (TRUE);
94    default:
95	panic("aq_revcmp: invalid op %d", op);
96    }
97
98    return (FALSE);
99}
100
101static int
102aq_strcmp(char *actual, char *possible)
103{
104    if (actual == NULL || possible == NULL)
105	return (TRUE);
106    return (strncmp(actual, possible, strlen(possible)) == 0);
107}
108
109static int
110aq_match_header(ACPI_TABLE_HEADER *hdr, const struct acpi_q_rule *match)
111{
112    int result;
113
114    result = FALSE;
115    switch (match->val) {
116    case OEM:
117	if (aq_strcmp(hdr->OemId, match->x.id) &&
118	    aq_strcmp(hdr->OemTableId, match->y.tid))
119	    result = TRUE;
120	break;
121    case CREATOR:
122	if (aq_strcmp(hdr->AslCompilerId, match->x.id))
123	    result = TRUE;
124	break;
125    case OEM_REV:
126	if (aq_revcmp(hdr->OemRevision, match->x.op, match->y.rev))
127	    result = TRUE;
128	break;
129    case CREATOR_REV:
130	if (aq_revcmp(hdr->AslCompilerRevision, match->x.op, match->y.rev))
131	    result = TRUE;
132	break;
133    }
134
135    return (result);
136}
137
138int
139acpi_table_quirks(int *quirks)
140{
141    const struct acpi_q_entry *entry;
142    const struct acpi_q_rule *match;
143    ACPI_TABLE_HEADER fadt, dsdt, xsdt, *hdr;
144    int done;
145
146    /* First, allow the machdep system to set its idea of quirks. */
147    KASSERT(quirks != NULL, ("acpi quirks ptr is NULL"));
148    acpi_machdep_quirks(quirks);
149
150    if (ACPI_FAILURE(AcpiGetTableHeader(ACPI_SIG_FADT, 0, &fadt)))
151	bzero(&fadt, sizeof(fadt));
152    if (ACPI_FAILURE(AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &dsdt)))
153	bzero(&dsdt, sizeof(dsdt));
154    if (ACPI_FAILURE(AcpiGetTableHeader(ACPI_SIG_XSDT, 0, &xsdt)))
155	bzero(&xsdt, sizeof(xsdt));
156
157    /* Then, override the quirks with any matched from table signatures. */
158    for (entry = acpi_quirks_table; entry->match; entry++) {
159	done = TRUE;
160	for (match = entry->match; match->sig[0] != '\0'; match++) {
161	    if (!strncmp(match->sig, "FADT", ACPI_NAME_SIZE))
162		hdr = &fadt;
163	    else if (!strncmp(match->sig, ACPI_SIG_DSDT, ACPI_NAME_SIZE))
164		hdr = &dsdt;
165	    else if (!strncmp(match->sig, ACPI_SIG_XSDT, ACPI_NAME_SIZE))
166		hdr = &xsdt;
167	    else
168		panic("invalid quirk header\n");
169
170	    /* If we don't match any, skip to the next entry. */
171	    if (aq_match_header(hdr, match) == FALSE) {
172		done = FALSE;
173		break;
174	    }
175	}
176
177	/* If all entries matched, update the quirks and return. */
178	if (done) {
179	    *quirks = entry->quirks;
180	    break;
181	}
182    }
183
184    return (0);
185}
186