]> git.proxmox.com Git - grub2.git/blame - grub-core/commands/acpihalt.c
probe: Support probing for partition UUID with --part-uuid
[grub2.git] / grub-core / commands / acpihalt.c
CommitLineData
41cf1ca3
VS
1/*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010 Free Software Foundation, Inc.
4 *
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * GRUB 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
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
17 */
18
c32b51c9
VS
19#ifdef GRUB_DSDT_TEST
20#include <stdio.h>
21#include <unistd.h>
22#include <stdlib.h>
23#include <stdint.h>
24#include <string.h>
4fd42c53 25#include <errno.h>
c32b51c9
VS
26
27#define grub_dprintf(cond, args...) printf ( args )
28#define grub_printf printf
19554a60
AB
29#define grub_util_fopen fopen
30#define grub_memcmp memcmp
c32b51c9
VS
31typedef uint64_t grub_uint64_t;
32typedef uint32_t grub_uint32_t;
33typedef uint16_t grub_uint16_t;
34typedef uint8_t grub_uint8_t;
35
36#endif
37
41cf1ca3 38#include <grub/acpi.h>
463dcadc 39#ifndef GRUB_DSDT_TEST
6e0632e2 40#include <grub/i18n.h>
463dcadc
VS
41#else
42#define _(x) x
43#define N_(x) x
44#endif
c32b51c9
VS
45
46#ifndef GRUB_DSDT_TEST
33028f4c 47#include <grub/mm.h>
41cf1ca3 48#include <grub/misc.h>
0354b867 49#include <grub/time.h>
41cf1ca3 50#include <grub/cpu/io.h>
c32b51c9 51#endif
41cf1ca3
VS
52
53static inline grub_uint32_t
54decode_length (const grub_uint8_t *ptr, int *numlen)
55{
56 int num_bytes, i;
57 grub_uint32_t ret;
58 if (*ptr < 64)
59 {
60 if (numlen)
61 *numlen = 1;
62 return *ptr;
63 }
64 num_bytes = *ptr >> 6;
65 if (numlen)
66 *numlen = num_bytes + 1;
67 ret = *ptr & 0xf;
68 ptr++;
69 for (i = 0; i < num_bytes; i++)
70 {
71 ret |= *ptr << (8 * i + 4);
72 ptr++;
73 }
74 return ret;
75}
76
77static inline grub_uint32_t
78skip_name_string (const grub_uint8_t *ptr, const grub_uint8_t *end)
79{
80 const grub_uint8_t *ptr0 = ptr;
81
82 while (ptr < end && (*ptr == '^' || *ptr == '\\'))
83 ptr++;
84 switch (*ptr)
85 {
86 case '.':
87 ptr++;
88 ptr += 8;
89 break;
90 case '/':
91 ptr++;
92 ptr += 1 + (*ptr) * 4;
93 break;
94 case 0:
95 ptr++;
96 break;
97 default:
98 ptr += 4;
99 break;
100 }
101 return ptr - ptr0;
102}
103
104static inline grub_uint32_t
105skip_data_ref_object (const grub_uint8_t *ptr, const grub_uint8_t *end)
106{
107 grub_dprintf ("acpi", "data type = 0x%x\n", *ptr);
108 switch (*ptr)
109 {
110 case GRUB_ACPI_OPCODE_PACKAGE:
463dcadc 111 case GRUB_ACPI_OPCODE_BUFFER:
41cf1ca3
VS
112 return 1 + decode_length (ptr + 1, 0);
113 case GRUB_ACPI_OPCODE_ZERO:
114 case GRUB_ACPI_OPCODE_ONES:
115 case GRUB_ACPI_OPCODE_ONE:
116 return 1;
117 case GRUB_ACPI_OPCODE_BYTE_CONST:
118 return 2;
119 case GRUB_ACPI_OPCODE_WORD_CONST:
120 return 3;
121 case GRUB_ACPI_OPCODE_DWORD_CONST:
122 return 5;
463dcadc
VS
123 case GRUB_ACPI_OPCODE_STRING_CONST:
124 {
125 const grub_uint8_t *ptr0 = ptr;
126 for (ptr++; ptr < end && *ptr; ptr++);
127 if (ptr == end)
128 return 0;
129 return ptr - ptr0 + 1;
130 }
41cf1ca3
VS
131 default:
132 if (*ptr == '^' || *ptr == '\\' || *ptr == '_'
133 || (*ptr >= 'A' && *ptr <= 'Z'))
134 return skip_name_string (ptr, end);
135 grub_printf ("Unknown opcode 0x%x\n", *ptr);
136 return 0;
137 }
138}
139
0f1f95c7
VD
140static inline grub_uint32_t
141skip_term (const grub_uint8_t *ptr, const grub_uint8_t *end)
142{
143 grub_uint32_t add;
144 const grub_uint8_t *ptr0 = ptr;
145
146 switch(*ptr)
147 {
148 case GRUB_ACPI_OPCODE_ADD:
149 case GRUB_ACPI_OPCODE_AND:
150 case GRUB_ACPI_OPCODE_CONCAT:
151 case GRUB_ACPI_OPCODE_CONCATRES:
152 case GRUB_ACPI_OPCODE_DIVIDE:
153 case GRUB_ACPI_OPCODE_INDEX:
154 case GRUB_ACPI_OPCODE_LSHIFT:
155 case GRUB_ACPI_OPCODE_MOD:
156 case GRUB_ACPI_OPCODE_MULTIPLY:
157 case GRUB_ACPI_OPCODE_NAND:
158 case GRUB_ACPI_OPCODE_NOR:
159 case GRUB_ACPI_OPCODE_OR:
160 case GRUB_ACPI_OPCODE_RSHIFT:
161 case GRUB_ACPI_OPCODE_SUBTRACT:
162 case GRUB_ACPI_OPCODE_TOSTRING:
163 case GRUB_ACPI_OPCODE_XOR:
164 /*
165 * Parameters for these opcodes: TermArg, TermArg Target, see ACPI
166 * spec r5.0, page 828f.
167 */
168 ptr++;
169 ptr += add = skip_term (ptr, end);
170 if (!add)
171 return 0;
172 ptr += add = skip_term (ptr, end);
173 if (!add)
174 return 0;
175 ptr += skip_name_string (ptr, end);
176 break;
177 default:
178 return skip_data_ref_object (ptr, end);
179 }
180 return ptr - ptr0;
181}
182
41cf1ca3
VS
183static inline grub_uint32_t
184skip_ext_op (const grub_uint8_t *ptr, const grub_uint8_t *end)
185{
186 const grub_uint8_t *ptr0 = ptr;
187 int add;
188 grub_dprintf ("acpi", "Extended opcode: 0x%x\n", *ptr);
189 switch (*ptr)
190 {
191 case GRUB_ACPI_EXTOPCODE_MUTEX:
192 ptr++;
193 ptr += skip_name_string (ptr, end);
194 ptr++;
195 break;
33028f4c
CW
196 case GRUB_ACPI_EXTOPCODE_EVENT_OP:
197 ptr++;
198 ptr += skip_name_string (ptr, end);
199 break;
41cf1ca3
VS
200 case GRUB_ACPI_EXTOPCODE_OPERATION_REGION:
201 ptr++;
202 ptr += skip_name_string (ptr, end);
203 ptr++;
0f1f95c7 204 ptr += add = skip_term (ptr, end);
41cf1ca3
VS
205 if (!add)
206 return 0;
0f1f95c7 207 ptr += add = skip_term (ptr, end);
41cf1ca3
VS
208 if (!add)
209 return 0;
210 break;
211 case GRUB_ACPI_EXTOPCODE_FIELD_OP:
33028f4c
CW
212 case GRUB_ACPI_EXTOPCODE_DEVICE_OP:
213 case GRUB_ACPI_EXTOPCODE_PROCESSOR_OP:
214 case GRUB_ACPI_EXTOPCODE_POWER_RES_OP:
215 case GRUB_ACPI_EXTOPCODE_THERMAL_ZONE_OP:
e19b016b 216 case GRUB_ACPI_EXTOPCODE_INDEX_FIELD_OP:
33028f4c 217 case GRUB_ACPI_EXTOPCODE_BANK_FIELD_OP:
41cf1ca3
VS
218 ptr++;
219 ptr += decode_length (ptr, 0);
220 break;
221 default:
222 grub_printf ("Unexpected extended opcode: 0x%x\n", *ptr);
223 return 0;
224 }
225 return ptr - ptr0;
226}
227
0f1f95c7 228
41cf1ca3 229static int
33028f4c
CW
230get_sleep_type (grub_uint8_t *table, grub_uint8_t *ptr, grub_uint8_t *end,
231 grub_uint8_t *scope, int scope_len)
41cf1ca3 232{
33028f4c 233 grub_uint8_t *prev = table;
41cf1ca3 234
33028f4c
CW
235 if (!ptr)
236 ptr = table + sizeof (struct grub_acpi_table_header);
41cf1ca3
VS
237 while (ptr < end && prev < ptr)
238 {
239 int add;
240 prev = ptr;
2419f17a
VS
241 grub_dprintf ("acpi", "Opcode 0x%x\n", *ptr);
242 grub_dprintf ("acpi", "Tell %x\n", (unsigned) (ptr - table));
41cf1ca3
VS
243 switch (*ptr)
244 {
245 case GRUB_ACPI_OPCODE_EXTOP:
246 ptr++;
247 ptr += add = skip_ext_op (ptr, end);
248 if (!add)
249 return -1;
250 break;
aa7bb460 251 case GRUB_ACPI_OPCODE_CREATE_DWORD_FIELD:
463dcadc
VS
252 case GRUB_ACPI_OPCODE_CREATE_WORD_FIELD:
253 case GRUB_ACPI_OPCODE_CREATE_BYTE_FIELD:
254 {
463dcadc
VS
255 ptr += 5;
256 ptr += add = skip_data_ref_object (ptr, end);
257 if (!add)
258 return -1;
259 ptr += 4;
260 break;
261 }
41cf1ca3
VS
262 case GRUB_ACPI_OPCODE_NAME:
263 ptr++;
8063ce19
VS
264 if ((!scope || grub_memcmp (scope, "\\", scope_len) == 0) &&
265 (grub_memcmp (ptr, "_S5_", 4) == 0 || grub_memcmp (ptr, "\\_S5_", 4) == 0))
41cf1ca3
VS
266 {
267 int ll;
268 grub_uint8_t *ptr2 = ptr;
6c8d3002
VS
269 grub_dprintf ("acpi", "S5 found\n");
270 ptr2 += skip_name_string (ptr, end);
41cf1ca3
VS
271 if (*ptr2 != 0x12)
272 {
273 grub_printf ("Unknown opcode in _S5: 0x%x\n", *ptr2);
274 return -1;
275 }
276 ptr2++;
277 decode_length (ptr2, &ll);
278 ptr2 += ll;
279 ptr2++;
280 switch (*ptr2)
281 {
282 case GRUB_ACPI_OPCODE_ZERO:
4fd42c53 283 return 0;
41cf1ca3 284 case GRUB_ACPI_OPCODE_ONE:
4fd42c53 285 return 1;
41cf1ca3 286 case GRUB_ACPI_OPCODE_BYTE_CONST:
4fd42c53 287 return ptr2[1];
41cf1ca3
VS
288 default:
289 grub_printf ("Unknown data type in _S5: 0x%x\n", *ptr2);
290 return -1;
291 }
292 }
293 ptr += add = skip_name_string (ptr, end);
294 if (!add)
295 return -1;
296 ptr += add = skip_data_ref_object (ptr, end);
297 if (!add)
298 return -1;
299 break;
0f1f95c7
VD
300 case GRUB_ACPI_OPCODE_ALIAS:
301 ptr++;
302 /* We need to skip two name strings */
303 ptr += add = skip_name_string (ptr, end);
304 if (!add)
305 return -1;
306 ptr += add = skip_name_string (ptr, end);
307 if (!add)
308 return -1;
309 break;
310
41cf1ca3 311 case GRUB_ACPI_OPCODE_SCOPE:
33028f4c
CW
312 {
313 int scope_sleep_type;
314 int ll;
315 grub_uint8_t *name;
316 int name_len;
317
318 ptr++;
319 add = decode_length (ptr, &ll);
320 name = ptr + ll;
321 name_len = skip_name_string (name, ptr + add);
322 if (!name_len)
323 return -1;
324 scope_sleep_type = get_sleep_type (table, name + name_len,
325 ptr + add, name, name_len);
326 if (scope_sleep_type != -2)
327 return scope_sleep_type;
328 ptr += add;
329 break;
330 }
41cf1ca3
VS
331 case GRUB_ACPI_OPCODE_IF:
332 case GRUB_ACPI_OPCODE_METHOD:
333 {
334 ptr++;
335 ptr += decode_length (ptr, 0);
336 break;
337 }
463dcadc
VS
338 default:
339 grub_printf ("Unknown opcode 0x%x\n", *ptr);
340 return -1;
41cf1ca3
VS
341 }
342 }
343
4fd42c53 344 return -2;
41cf1ca3
VS
345}
346
c32b51c9
VS
347#ifdef GRUB_DSDT_TEST
348int
349main (int argc, char **argv)
350{
351 FILE *f;
352 size_t len;
353 unsigned char *buf;
354 if (argc < 2)
355 printf ("Usage: %s FILE\n", argv[0]);
bb338aaf 356 f = grub_util_fopen (argv[1], "rb");
c32b51c9
VS
357 if (!f)
358 {
359 printf ("Couldn't open file\n");
360 return 1;
361 }
362 fseek (f, 0, SEEK_END);
363 len = ftell (f);
364 fseek (f, 0, SEEK_SET);
365 buf = malloc (len);
366 if (!buf)
367 {
d22840ec 368 printf (_("error: %s.\n"), _("out of memory"));
c32b51c9
VS
369 fclose (f);
370 return 2;
371 }
372 if (fread (buf, 1, len, f) != len)
373 {
4fd42c53 374 printf (_("cannot read `%s': %s"), argv[1], strerror (errno));
c32b51c9
VS
375 free (buf);
376 fclose (f);
377 return 2;
378 }
379
33028f4c 380 printf ("Sleep type = %d\n", get_sleep_type (buf, NULL, buf + len, NULL, 0));
c32b51c9
VS
381 free (buf);
382 fclose (f);
383 return 0;
384}
385
386#else
387
41cf1ca3
VS
388void
389grub_acpi_halt (void)
390{
391 struct grub_acpi_rsdp_v20 *rsdp2;
392 struct grub_acpi_rsdp_v10 *rsdp1;
33028f4c
CW
393 struct grub_acpi_table_header *rsdt;
394 grub_uint32_t *entry_ptr;
395 grub_uint32_t port = 0;
396 int sleep_type = -1;
41cf1ca3
VS
397
398 rsdp2 = grub_acpi_get_rsdpv2 ();
399 if (rsdp2)
400 rsdp1 = &(rsdp2->rsdpv1);
401 else
402 rsdp1 = grub_acpi_get_rsdpv1 ();
403 grub_dprintf ("acpi", "rsdp1=%p\n", rsdp1);
404 if (!rsdp1)
405 return;
406
2419f17a 407 rsdt = (struct grub_acpi_table_header *) (grub_addr_t) rsdp1->rsdt_addr;
41cf1ca3
VS
408 for (entry_ptr = (grub_uint32_t *) (rsdt + 1);
409 entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt)
410 + rsdt->length);
411 entry_ptr++)
412 {
2419f17a 413 if (grub_memcmp ((void *) (grub_addr_t) *entry_ptr, "FACP", 4) == 0)
41cf1ca3 414 {
41cf1ca3 415 struct grub_acpi_fadt *fadt
2419f17a 416 = ((struct grub_acpi_fadt *) (grub_addr_t) *entry_ptr);
41cf1ca3 417 struct grub_acpi_table_header *dsdt
2419f17a 418 = (struct grub_acpi_table_header *) (grub_addr_t) fadt->dsdt_addr;
33028f4c 419 grub_uint8_t *buf = (grub_uint8_t *) dsdt;
41cf1ca3
VS
420
421 port = fadt->pm1a;
422
423 grub_dprintf ("acpi", "PM1a port=%x\n", port);
424
425 if (grub_memcmp (dsdt->signature, "DSDT",
4fd42c53
VS
426 sizeof (dsdt->signature)) == 0
427 && sleep_type < 0)
33028f4c
CW
428 sleep_type = get_sleep_type (buf, NULL, buf + dsdt->length,
429 NULL, 0);
430 }
4fd42c53
VS
431 else if (grub_memcmp ((void *) (grub_addr_t) *entry_ptr, "SSDT", 4) == 0
432 && sleep_type < 0)
33028f4c
CW
433 {
434 struct grub_acpi_table_header *ssdt
435 = (struct grub_acpi_table_header *) (grub_addr_t) *entry_ptr;
436 grub_uint8_t *buf = (grub_uint8_t *) ssdt;
41cf1ca3 437
33028f4c 438 grub_dprintf ("acpi", "SSDT = %p\n", ssdt);
41cf1ca3 439
33028f4c
CW
440 sleep_type = get_sleep_type (buf, NULL, buf + ssdt->length, NULL, 0);
441 }
442 }
41cf1ca3 443
4fd42c53 444 grub_dprintf ("acpi", "SLP_TYP = %d, port = 0x%x\n", sleep_type, port);
33028f4c 445 if (port && sleep_type >= 0 && sleep_type < 8)
4fd42c53
VS
446 grub_outw (GRUB_ACPI_SLP_EN | (sleep_type << GRUB_ACPI_SLP_TYP_OFFSET),
447 port & 0xffff);
41cf1ca3 448
0354b867
VS
449 grub_millisleep (1500);
450
40211ab8 451 /* TRANSLATORS: It's computer shutdown using ACPI, not disabling ACPI. */
6e0632e2 452 grub_puts_ (N_("ACPI shutdown failed"));
41cf1ca3 453}
c32b51c9 454#endif