xref: /illumos-kvm-cmd/hw/acpi.c (revision 68396ea9)
1 /*
2  * ACPI implementation
3  *
4  * Copyright (c) 2006 Fabrice Bellard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License version 2 as published by the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see <http://www.gnu.org/licenses/>
17  */
18 #include "hw.h"
19 #include "pc.h"
20 #include "acpi.h"
21 #include "kvm.h"
22 #include "qemu-kvm.h"
23 #include "string.h"
24 
25 struct acpi_table_header
26 {
27     char signature [4];    /* ACPI signature (4 ASCII characters) */
28     uint32_t length;          /* Length of table, in bytes, including header */
29     uint8_t revision;         /* ACPI Specification minor version # */
30     uint8_t checksum;         /* To make sum of entire table == 0 */
31     char oem_id [6];       /* OEM identification */
32     char oem_table_id [8]; /* OEM table identification */
33     uint32_t oem_revision;    /* OEM revision number */
34     char asl_compiler_id [4]; /* ASL compiler vendor ID */
35     uint32_t asl_compiler_revision; /* ASL compiler revision number */
36 } __attribute__((packed));
37 
38 char *acpi_tables;
39 size_t acpi_tables_len;
40 
acpi_checksum(const uint8_t * data,int len)41 static int acpi_checksum(const uint8_t *data, int len)
42 {
43     int sum, i;
44     sum = 0;
45     for(i = 0; i < len; i++)
46         sum += data[i];
47     return (-sum) & 0xff;
48 }
49 
acpi_table_add(const char * t)50 int acpi_table_add(const char *t)
51 {
52     static const char *dfl_id = "QEMUQEMU";
53     char buf[1024], *p, *f;
54     struct acpi_table_header acpi_hdr;
55     unsigned long val;
56     uint32_t length;
57     struct acpi_table_header *acpi_hdr_p;
58     size_t off;
59 
60     memset(&acpi_hdr, 0, sizeof(acpi_hdr));
61 
62     if (get_param_value(buf, sizeof(buf), "sig", t)) {
63         strncpy(acpi_hdr.signature, buf, 4);
64     } else {
65         strncpy(acpi_hdr.signature, dfl_id, 4);
66     }
67     if (get_param_value(buf, sizeof(buf), "rev", t)) {
68         val = strtoul(buf, &p, 10);
69         if (val > 255 || *p != '\0')
70             goto out;
71     } else {
72         val = 1;
73     }
74     acpi_hdr.revision = (int8_t)val;
75 
76     if (get_param_value(buf, sizeof(buf), "oem_id", t)) {
77         strncpy(acpi_hdr.oem_id, buf, 6);
78     } else {
79         strncpy(acpi_hdr.oem_id, dfl_id, 6);
80     }
81 
82     if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) {
83         strncpy(acpi_hdr.oem_table_id, buf, 8);
84     } else {
85         strncpy(acpi_hdr.oem_table_id, dfl_id, 8);
86     }
87 
88     if (get_param_value(buf, sizeof(buf), "oem_rev", t)) {
89         val = strtol(buf, &p, 10);
90         if(*p != '\0')
91             goto out;
92     } else {
93         val = 1;
94     }
95     acpi_hdr.oem_revision = cpu_to_le32(val);
96 
97     if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) {
98         strncpy(acpi_hdr.asl_compiler_id, buf, 4);
99     } else {
100         strncpy(acpi_hdr.asl_compiler_id, dfl_id, 4);
101     }
102 
103     if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) {
104         val = strtol(buf, &p, 10);
105         if(*p != '\0')
106             goto out;
107     } else {
108         val = 1;
109     }
110     acpi_hdr.asl_compiler_revision = cpu_to_le32(val);
111 
112     if (!get_param_value(buf, sizeof(buf), "data", t)) {
113          buf[0] = '\0';
114     }
115 
116     length = sizeof(acpi_hdr);
117 
118     f = buf;
119     while (buf[0]) {
120         struct stat s;
121         char *n = strchr(f, ':');
122         if (n)
123             *n = '\0';
124         if(stat(f, &s) < 0) {
125             fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno));
126             goto out;
127         }
128         length += s.st_size;
129         if (!n)
130             break;
131         *n = ':';
132         f = n + 1;
133     }
134 
135     if (!acpi_tables) {
136         acpi_tables_len = sizeof(uint16_t);
137         acpi_tables = qemu_mallocz(acpi_tables_len);
138     }
139     acpi_tables = qemu_realloc(acpi_tables,
140                                acpi_tables_len + sizeof(uint16_t) + length);
141     p = acpi_tables + acpi_tables_len;
142     acpi_tables_len += sizeof(uint16_t) + length;
143 
144     *(uint16_t*)p = cpu_to_le32(length);
145     p += sizeof(uint16_t);
146     memcpy(p, &acpi_hdr, sizeof(acpi_hdr));
147     off = sizeof(acpi_hdr);
148 
149     f = buf;
150     while (buf[0]) {
151         struct stat s;
152         int fd;
153         char *n = strchr(f, ':');
154         if (n)
155             *n = '\0';
156         fd = open(f, O_RDONLY);
157 
158         if(fd < 0)
159             goto out;
160         if(fstat(fd, &s) < 0) {
161             close(fd);
162             goto out;
163         }
164 
165         /* off < length is necessary because file size can be changed
166            under our foot */
167         while(s.st_size && off < length) {
168             int r;
169             r = read(fd, p + off, s.st_size);
170             if (r > 0) {
171                 off += r;
172                 s.st_size -= r;
173             } else if ((r < 0 && errno != EINTR) || r == 0) {
174                 close(fd);
175                 goto out;
176             }
177         }
178 
179         close(fd);
180         if (!n)
181             break;
182         f = n + 1;
183     }
184     if (off < length) {
185         /* don't pass random value in process to guest */
186         memset(p + off, 0, length - off);
187     }
188 
189     acpi_hdr_p = (struct acpi_table_header*)p;
190     acpi_hdr_p->length = cpu_to_le32(length);
191     acpi_hdr_p->checksum = acpi_checksum((uint8_t*)p, length);
192     /* increase number of tables */
193     (*(uint16_t*)acpi_tables) =
194 	    cpu_to_le32(le32_to_cpu(*(uint16_t*)acpi_tables) + 1);
195     return 0;
196 out:
197     if (acpi_tables) {
198         qemu_free(acpi_tables);
199         acpi_tables = NULL;
200     }
201     return -1;
202 }
203