20 |
20 |
#include "pc.h"
|
21 |
21 |
#include "acpi.h"
|
22 |
22 |
|
23 |
|
struct acpi_table_header
|
24 |
|
{
|
25 |
|
char signature [4]; /* ACPI signature (4 ASCII characters) */
|
|
23 |
struct acpi_table_header {
|
|
24 |
uint16_t _length; /* our length, not actual part of the hdr */
|
|
25 |
/* XXX why we have 2 length fields here? */
|
|
26 |
char sig[4]; /* ACPI signature (4 ASCII characters) */
|
26 |
27 |
uint32_t length; /* Length of table, in bytes, including header */
|
27 |
28 |
uint8_t revision; /* ACPI Specification minor version # */
|
28 |
29 |
uint8_t checksum; /* To make sum of entire table == 0 */
|
29 |
|
char oem_id [6]; /* OEM identification */
|
30 |
|
char oem_table_id [8]; /* OEM table identification */
|
|
30 |
char oem_id[6]; /* OEM identification */
|
|
31 |
char oem_table_id[8]; /* OEM table identification */
|
31 |
32 |
uint32_t oem_revision; /* OEM revision number */
|
32 |
|
char asl_compiler_id [4]; /* ASL compiler vendor ID */
|
|
33 |
char asl_compiler_id[4]; /* ASL compiler vendor ID */
|
33 |
34 |
uint32_t asl_compiler_revision; /* ASL compiler revision number */
|
34 |
35 |
} __attribute__((packed));
|
35 |
36 |
|
|
37 |
#define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header)
|
|
38 |
#define ACPI_TABLE_PFX_SIZE sizeof(uint16_t) /* size of the extra prefix */
|
|
39 |
|
|
40 |
static const char dfl_hdr[ACPI_TABLE_HDR_SIZE] =
|
|
41 |
"\0\0" /* fake _length (2) */
|
|
42 |
"QEMU\0\0\0\0\1\0" /* sig (4), len(4), revno (1), csum (1) */
|
|
43 |
"QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */
|
|
44 |
"QEMU\1\0\0\0" /* ASL compiler ID (4), version (4) */
|
|
45 |
;
|
|
46 |
|
36 |
47 |
char *acpi_tables;
|
37 |
48 |
size_t acpi_tables_len;
|
38 |
49 |
|
... | ... | |
40 |
51 |
{
|
41 |
52 |
int sum, i;
|
42 |
53 |
sum = 0;
|
43 |
|
for(i = 0; i < len; i++)
|
|
54 |
for (i = 0; i < len; i++) {
|
44 |
55 |
sum += data[i];
|
|
56 |
}
|
45 |
57 |
return (-sum) & 0xff;
|
46 |
58 |
}
|
47 |
59 |
|
|
60 |
/* like strncpy() but zero-fills the tail of destination */
|
|
61 |
static void strzcpy(char *dst, const char *src, size_t size)
|
|
62 |
{
|
|
63 |
size_t len = strlen(src);
|
|
64 |
if (len >= size) {
|
|
65 |
len = size;
|
|
66 |
} else {
|
|
67 |
memset(dst + len, 0, size - len);
|
|
68 |
}
|
|
69 |
memcpy(dst, src, len);
|
|
70 |
}
|
|
71 |
|
|
72 |
/* XXX fixme: this function uses obsolete argument parsing interface */
|
48 |
73 |
int acpi_table_add(const char *t)
|
49 |
74 |
{
|
50 |
|
static const char *dfl_id = "QEMUQEMU";
|
51 |
75 |
char buf[1024], *p, *f;
|
52 |
|
struct acpi_table_header acpi_hdr;
|
53 |
76 |
unsigned long val;
|
54 |
|
uint32_t length;
|
55 |
|
struct acpi_table_header *acpi_hdr_p;
|
56 |
|
size_t off;
|
|
77 |
size_t len, start, allen;
|
|
78 |
bool has_header;
|
|
79 |
int changed;
|
|
80 |
int r;
|
|
81 |
struct acpi_table_header hdr;
|
|
82 |
|
|
83 |
r = 0;
|
|
84 |
r |= get_param_value(buf, sizeof(buf), "data", t) ? 1 : 0;
|
|
85 |
r |= get_param_value(buf, sizeof(buf), "file", t) ? 2 : 0;
|
|
86 |
switch (r) {
|
|
87 |
case 0:
|
|
88 |
buf[0] = '\0';
|
|
89 |
/* fallthrough for default behavior */
|
|
90 |
case 1:
|
|
91 |
has_header = false;
|
|
92 |
break;
|
|
93 |
case 2:
|
|
94 |
has_header = true;
|
|
95 |
break;
|
|
96 |
default:
|
|
97 |
fprintf(stderr, "acpitable: both data and file are specified\n");
|
|
98 |
return -1;
|
|
99 |
}
|
57 |
100 |
|
58 |
|
memset(&acpi_hdr, 0, sizeof(acpi_hdr));
|
59 |
|
|
60 |
|
if (get_param_value(buf, sizeof(buf), "sig", t)) {
|
61 |
|
strncpy(acpi_hdr.signature, buf, 4);
|
|
101 |
if (!acpi_tables) {
|
|
102 |
allen = sizeof(uint16_t);
|
|
103 |
acpi_tables = qemu_mallocz(allen);
|
62 |
104 |
} else {
|
63 |
|
strncpy(acpi_hdr.signature, dfl_id, 4);
|
|
105 |
allen = acpi_tables_len;
|
64 |
106 |
}
|
|
107 |
|
|
108 |
start = allen;
|
|
109 |
acpi_tables = qemu_realloc(acpi_tables, start + ACPI_TABLE_HDR_SIZE);
|
|
110 |
allen += has_header ? ACPI_TABLE_PFX_SIZE : ACPI_TABLE_HDR_SIZE;
|
|
111 |
|
|
112 |
/* now read in the data files, reallocating buffer as needed */
|
|
113 |
|
|
114 |
for (f = strtok(buf, ":"); f; f = strtok(NULL, ":")) {
|
|
115 |
int fd = open(f, O_RDONLY);
|
|
116 |
|
|
117 |
if (fd < 0) {
|
|
118 |
fprintf(stderr, "can't open file %s: %s\n", f, strerror(errno));
|
|
119 |
return -1;
|
|
120 |
}
|
|
121 |
|
|
122 |
for (;;) {
|
|
123 |
char data[8192];
|
|
124 |
r = read(fd, data, sizeof(data));
|
|
125 |
if (r == 0) {
|
|
126 |
break;
|
|
127 |
} else if (r > 0) {
|
|
128 |
acpi_tables = qemu_realloc(acpi_tables, allen + r);
|
|
129 |
memcpy(acpi_tables + allen, data, r);
|
|
130 |
allen += r;
|
|
131 |
} else if (errno != EINTR) {
|
|
132 |
fprintf(stderr, "can't read file %s: %s\n",
|
|
133 |
f, strerror(errno));
|
|
134 |
close(fd);
|
|
135 |
return -1;
|
|
136 |
}
|
|
137 |
}
|
|
138 |
|
|
139 |
close(fd);
|
|
140 |
}
|
|
141 |
|
|
142 |
/* now fill in the header fields */
|
|
143 |
|
|
144 |
f = acpi_tables + start; /* start of the table */
|
|
145 |
changed = 0;
|
|
146 |
|
|
147 |
/* copy the header to temp place to align the fields */
|
|
148 |
memcpy(&hdr, has_header ? f : dfl_hdr, ACPI_TABLE_HDR_SIZE);
|
|
149 |
|
|
150 |
/* length of the table minus our prefix */
|
|
151 |
len = allen - start - ACPI_TABLE_PFX_SIZE;
|
|
152 |
|
|
153 |
hdr._length = cpu_to_le16(len);
|
|
154 |
|
|
155 |
if (get_param_value(buf, sizeof(buf), "sig", t)) {
|
|
156 |
strzcpy(hdr.sig, buf, sizeof(hdr.sig));
|
|
157 |
++changed;
|
|
158 |
}
|
|
159 |
|
|
160 |
/* length of the table including header, in bytes */
|
|
161 |
if (has_header) {
|
|
162 |
/* check if actual length is correct */
|
|
163 |
val = le32_to_cpu(hdr.length);
|
|
164 |
if (val != len) {
|
|
165 |
fprintf(stderr,
|
|
166 |
"warning: acpitable has wrong length,"
|
|
167 |
" header says %lu, actual size %zu bytes\n",
|
|
168 |
val, len);
|
|
169 |
++changed;
|
|
170 |
}
|
|
171 |
}
|
|
172 |
/* we may avoid putting length here if has_header is true */
|
|
173 |
hdr.length = cpu_to_le32(len);
|
|
174 |
|
65 |
175 |
if (get_param_value(buf, sizeof(buf), "rev", t)) {
|
66 |
|
val = strtoul(buf, &p, 10);
|
67 |
|
if (val > 255 || *p != '\0')
|
68 |
|
goto out;
|
69 |
|
} else {
|
70 |
|
val = 1;
|
|
176 |
val = strtoul(buf, &p, 0);
|
|
177 |
if (val > 255 || *p) {
|
|
178 |
fprintf(stderr, "acpitable: \"rev=%s\" is invalid\n", buf);
|
|
179 |
return -1;
|
|
180 |
}
|
|
181 |
hdr.revision = (uint8_t)val;
|
|
182 |
++changed;
|
71 |
183 |
}
|
72 |
|
acpi_hdr.revision = (int8_t)val;
|
73 |
184 |
|
74 |
185 |
if (get_param_value(buf, sizeof(buf), "oem_id", t)) {
|
75 |
|
strncpy(acpi_hdr.oem_id, buf, 6);
|
76 |
|
} else {
|
77 |
|
strncpy(acpi_hdr.oem_id, dfl_id, 6);
|
|
186 |
strzcpy(hdr.oem_id, buf, sizeof(hdr.oem_id));
|
|
187 |
++changed;
|
78 |
188 |
}
|
79 |
189 |
|
80 |
190 |
if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) {
|
81 |
|
strncpy(acpi_hdr.oem_table_id, buf, 8);
|
82 |
|
} else {
|
83 |
|
strncpy(acpi_hdr.oem_table_id, dfl_id, 8);
|
|
191 |
strzcpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id));
|
|
192 |
++changed;
|
84 |
193 |
}
|
85 |
194 |
|
86 |
195 |
if (get_param_value(buf, sizeof(buf), "oem_rev", t)) {
|
87 |
|
val = strtol(buf, &p, 10);
|
88 |
|
if(*p != '\0')
|
89 |
|
goto out;
|
90 |
|
} else {
|
91 |
|
val = 1;
|
|
196 |
val = strtol(buf, &p, 0);
|
|
197 |
if (*p) {
|
|
198 |
fprintf(stderr, "acpitable: \"oem_rev=%s\" is invalid\n", buf);
|
|
199 |
return -1;
|
|
200 |
}
|
|
201 |
hdr.oem_revision = cpu_to_le32(val);
|
|
202 |
++changed;
|
92 |
203 |
}
|
93 |
|
acpi_hdr.oem_revision = cpu_to_le32(val);
|
94 |
204 |
|
95 |
205 |
if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) {
|
96 |
|
strncpy(acpi_hdr.asl_compiler_id, buf, 4);
|
97 |
|
} else {
|
98 |
|
strncpy(acpi_hdr.asl_compiler_id, dfl_id, 4);
|
|
206 |
strzcpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id));
|
|
207 |
++changed;
|
99 |
208 |
}
|
100 |
209 |
|
101 |
210 |
if (get_param_value(buf, sizeof(buf), "asl_compiler_rev", t)) {
|
102 |
|
val = strtol(buf, &p, 10);
|
103 |
|
if(*p != '\0')
|
104 |
|
goto out;
|
105 |
|
} else {
|
106 |
|
val = 1;
|
107 |
|
}
|
108 |
|
acpi_hdr.asl_compiler_revision = cpu_to_le32(val);
|
109 |
|
|
110 |
|
if (!get_param_value(buf, sizeof(buf), "data", t)) {
|
111 |
|
buf[0] = '\0';
|
112 |
|
}
|
113 |
|
|
114 |
|
length = sizeof(acpi_hdr);
|
115 |
|
|
116 |
|
f = buf;
|
117 |
|
while (buf[0]) {
|
118 |
|
struct stat s;
|
119 |
|
char *n = strchr(f, ':');
|
120 |
|
if (n)
|
121 |
|
*n = '\0';
|
122 |
|
if(stat(f, &s) < 0) {
|
123 |
|
fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno));
|
124 |
|
goto out;
|
|
211 |
val = strtol(buf, &p, 0);
|
|
212 |
if (*p) {
|
|
213 |
fprintf(stderr, "acpitable: \"%s=%s\" is invalid\n",
|
|
214 |
"asl_compiler_rev", buf);
|
|
215 |
return -1;
|
125 |
216 |
}
|
126 |
|
length += s.st_size;
|
127 |
|
if (!n)
|
128 |
|
break;
|
129 |
|
*n = ':';
|
130 |
|
f = n + 1;
|
|
217 |
hdr.asl_compiler_revision = cpu_to_le32(val);
|
|
218 |
++changed;
|
131 |
219 |
}
|
132 |
220 |
|
133 |
|
if (!acpi_tables) {
|
134 |
|
acpi_tables_len = sizeof(uint16_t);
|
135 |
|
acpi_tables = qemu_mallocz(acpi_tables_len);
|
|
221 |
if (!has_header && !changed) {
|
|
222 |
fprintf(stderr, "warning: acpitable: no table headers are specified\n");
|
136 |
223 |
}
|
137 |
|
acpi_tables = qemu_realloc(acpi_tables,
|
138 |
|
acpi_tables_len + sizeof(uint16_t) + length);
|
139 |
|
p = acpi_tables + acpi_tables_len;
|
140 |
|
acpi_tables_len += sizeof(uint16_t) + length;
|
141 |
|
|
142 |
|
*(uint16_t*)p = cpu_to_le32(length);
|
143 |
|
p += sizeof(uint16_t);
|
144 |
|
memcpy(p, &acpi_hdr, sizeof(acpi_hdr));
|
145 |
|
off = sizeof(acpi_hdr);
|
146 |
|
|
147 |
|
f = buf;
|
148 |
|
while (buf[0]) {
|
149 |
|
struct stat s;
|
150 |
|
int fd;
|
151 |
|
char *n = strchr(f, ':');
|
152 |
|
if (n)
|
153 |
|
*n = '\0';
|
154 |
|
fd = open(f, O_RDONLY);
|
155 |
|
|
156 |
|
if(fd < 0)
|
157 |
|
goto out;
|
158 |
|
if(fstat(fd, &s) < 0) {
|
159 |
|
close(fd);
|
160 |
|
goto out;
|
161 |
|
}
|
162 |
224 |
|
163 |
|
/* off < length is necessary because file size can be changed
|
164 |
|
under our foot */
|
165 |
|
while(s.st_size && off < length) {
|
166 |
|
int r;
|
167 |
|
r = read(fd, p + off, s.st_size);
|
168 |
|
if (r > 0) {
|
169 |
|
off += r;
|
170 |
|
s.st_size -= r;
|
171 |
|
} else if ((r < 0 && errno != EINTR) || r == 0) {
|
172 |
|
close(fd);
|
173 |
|
goto out;
|
174 |
|
}
|
175 |
|
}
|
176 |
225 |
|
177 |
|
close(fd);
|
178 |
|
if (!n)
|
179 |
|
break;
|
180 |
|
f = n + 1;
|
181 |
|
}
|
182 |
|
if (off < length) {
|
183 |
|
/* don't pass random value in process to guest */
|
184 |
|
memset(p + off, 0, length - off);
|
|
226 |
/* now calculate checksum of the table, complete with the header */
|
|
227 |
/* we may as well leave checksum intact if has_header is true */
|
|
228 |
/* alternatively there may be a way to set cksum to a given value */
|
|
229 |
hdr.checksum = 0; /* for checksum calculation */
|
|
230 |
|
|
231 |
/* put header back */
|
|
232 |
memcpy(f, &hdr, sizeof(hdr));
|
|
233 |
|
|
234 |
if (changed || !has_header || 1) {
|
|
235 |
((struct acpi_table_header *)f)->checksum =
|
|
236 |
acpi_checksum((uint8_t *)f + ACPI_TABLE_PFX_SIZE, len);
|
185 |
237 |
}
|
186 |
238 |
|
187 |
|
acpi_hdr_p = (struct acpi_table_header*)p;
|
188 |
|
acpi_hdr_p->length = cpu_to_le32(length);
|
189 |
|
acpi_hdr_p->checksum = acpi_checksum((uint8_t*)p, length);
|
190 |
239 |
/* increase number of tables */
|
191 |
|
(*(uint16_t*)acpi_tables) =
|
192 |
|
cpu_to_le32(le32_to_cpu(*(uint16_t*)acpi_tables) + 1);
|
|
240 |
(*(uint16_t *)acpi_tables) =
|
|
241 |
cpu_to_le32(le32_to_cpu(*(uint16_t *)acpi_tables) + 1);
|
|
242 |
|
|
243 |
acpi_tables_len = allen;
|
193 |
244 |
return 0;
|
194 |
|
out:
|
195 |
|
if (acpi_tables) {
|
196 |
|
qemu_free(acpi_tables);
|
197 |
|
acpi_tables = NULL;
|
198 |
|
}
|
199 |
|
return -1;
|
|
245 |
|
200 |
246 |
}
|
201 |
247 |
|
202 |
248 |
/* ACPI PM1a EVT */
|