]> git.proxmox.com Git - grub2.git/blob - grub-core/commands/acpihalt.c
* grub-core/commands/ls.c (grub_ls_list_devices): Disable
[grub2.git] / grub-core / commands / acpihalt.c
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
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>
25
26 #define grub_dprintf(cond, args...) printf ( args )
27 #define grub_printf printf
28 typedef uint64_t grub_uint64_t;
29 typedef uint32_t grub_uint32_t;
30 typedef uint16_t grub_uint16_t;
31 typedef uint8_t grub_uint8_t;
32
33 #endif
34
35 #include <grub/acpi.h>
36 #ifndef GRUB_DSDT_TEST
37 #include <grub/i18n.h>
38 #else
39 #define _(x) x
40 #define N_(x) x
41 #endif
42
43 #ifndef GRUB_DSDT_TEST
44 #include <grub/misc.h>
45 #include <grub/time.h>
46 #include <grub/cpu/io.h>
47 #endif
48
49 static inline grub_uint32_t
50 decode_length (const grub_uint8_t *ptr, int *numlen)
51 {
52 int num_bytes, i;
53 grub_uint32_t ret;
54 if (*ptr < 64)
55 {
56 if (numlen)
57 *numlen = 1;
58 return *ptr;
59 }
60 num_bytes = *ptr >> 6;
61 if (numlen)
62 *numlen = num_bytes + 1;
63 ret = *ptr & 0xf;
64 ptr++;
65 for (i = 0; i < num_bytes; i++)
66 {
67 ret |= *ptr << (8 * i + 4);
68 ptr++;
69 }
70 return ret;
71 }
72
73 static inline grub_uint32_t
74 skip_name_string (const grub_uint8_t *ptr, const grub_uint8_t *end)
75 {
76 const grub_uint8_t *ptr0 = ptr;
77
78 while (ptr < end && (*ptr == '^' || *ptr == '\\'))
79 ptr++;
80 switch (*ptr)
81 {
82 case '.':
83 ptr++;
84 ptr += 8;
85 break;
86 case '/':
87 ptr++;
88 ptr += 1 + (*ptr) * 4;
89 break;
90 case 0:
91 ptr++;
92 break;
93 default:
94 ptr += 4;
95 break;
96 }
97 return ptr - ptr0;
98 }
99
100 static inline grub_uint32_t
101 skip_data_ref_object (const grub_uint8_t *ptr, const grub_uint8_t *end)
102 {
103 grub_dprintf ("acpi", "data type = 0x%x\n", *ptr);
104 switch (*ptr)
105 {
106 case GRUB_ACPI_OPCODE_PACKAGE:
107 case GRUB_ACPI_OPCODE_BUFFER:
108 return 1 + decode_length (ptr + 1, 0);
109 case GRUB_ACPI_OPCODE_ZERO:
110 case GRUB_ACPI_OPCODE_ONES:
111 case GRUB_ACPI_OPCODE_ONE:
112 return 1;
113 case GRUB_ACPI_OPCODE_BYTE_CONST:
114 return 2;
115 case GRUB_ACPI_OPCODE_WORD_CONST:
116 return 3;
117 case GRUB_ACPI_OPCODE_DWORD_CONST:
118 return 5;
119 case GRUB_ACPI_OPCODE_STRING_CONST:
120 {
121 const grub_uint8_t *ptr0 = ptr;
122 for (ptr++; ptr < end && *ptr; ptr++);
123 if (ptr == end)
124 return 0;
125 return ptr - ptr0 + 1;
126 }
127 default:
128 if (*ptr == '^' || *ptr == '\\' || *ptr == '_'
129 || (*ptr >= 'A' && *ptr <= 'Z'))
130 return skip_name_string (ptr, end);
131 grub_printf ("Unknown opcode 0x%x\n", *ptr);
132 return 0;
133 }
134 }
135
136 static inline grub_uint32_t
137 skip_ext_op (const grub_uint8_t *ptr, const grub_uint8_t *end)
138 {
139 const grub_uint8_t *ptr0 = ptr;
140 int add;
141 grub_dprintf ("acpi", "Extended opcode: 0x%x\n", *ptr);
142 switch (*ptr)
143 {
144 case GRUB_ACPI_EXTOPCODE_MUTEX:
145 ptr++;
146 ptr += skip_name_string (ptr, end);
147 ptr++;
148 break;
149 case GRUB_ACPI_EXTOPCODE_OPERATION_REGION:
150 ptr++;
151 ptr += skip_name_string (ptr, end);
152 ptr++;
153 ptr += add = skip_data_ref_object (ptr, end);
154 if (!add)
155 return 0;
156 ptr += add = skip_data_ref_object (ptr, end);
157 if (!add)
158 return 0;
159 break;
160 case GRUB_ACPI_EXTOPCODE_FIELD_OP:
161 case GRUB_ACPI_EXTOPCODE_INDEX_FIELD_OP:
162 ptr++;
163 ptr += decode_length (ptr, 0);
164 break;
165 default:
166 grub_printf ("Unexpected extended opcode: 0x%x\n", *ptr);
167 return 0;
168 }
169 return ptr - ptr0;
170 }
171
172 static int
173 get_sleep_type (grub_uint8_t *table, grub_uint8_t *end)
174 {
175 grub_uint8_t *ptr, *prev = table;
176 int sleep_type = -1;
177
178 ptr = table + sizeof (struct grub_acpi_table_header);
179 while (ptr < end && prev < ptr)
180 {
181 int add;
182 prev = ptr;
183 grub_dprintf ("acpi", "Opcode 0x%x\n", *ptr);
184 grub_dprintf ("acpi", "Tell %x\n", (unsigned) (ptr - table));
185 switch (*ptr)
186 {
187 case GRUB_ACPI_OPCODE_EXTOP:
188 ptr++;
189 ptr += add = skip_ext_op (ptr, end);
190 if (!add)
191 return -1;
192 break;
193 case GRUB_ACPI_OPCODE_CREATE_WORD_FIELD:
194 case GRUB_ACPI_OPCODE_CREATE_BYTE_FIELD:
195 {
196 ptr += 5;
197 ptr += add = skip_data_ref_object (ptr, end);
198 if (!add)
199 return -1;
200 ptr += 4;
201 break;
202 }
203 case GRUB_ACPI_OPCODE_NAME:
204 ptr++;
205 if (memcmp (ptr, "_S5_", 4) == 0 || memcmp (ptr, "\\_S5_", 4) == 0)
206 {
207 int ll;
208 grub_uint8_t *ptr2 = ptr;
209 grub_dprintf ("acpi", "S5 found\n");
210 ptr2 += skip_name_string (ptr, end);
211 if (*ptr2 != 0x12)
212 {
213 grub_printf ("Unknown opcode in _S5: 0x%x\n", *ptr2);
214 return -1;
215 }
216 ptr2++;
217 decode_length (ptr2, &ll);
218 ptr2 += ll;
219 ptr2++;
220 switch (*ptr2)
221 {
222 case GRUB_ACPI_OPCODE_ZERO:
223 sleep_type = 0;
224 break;
225 case GRUB_ACPI_OPCODE_ONE:
226 sleep_type = 1;
227 break;
228 case GRUB_ACPI_OPCODE_BYTE_CONST:
229 sleep_type = ptr2[1];
230 break;
231 default:
232 grub_printf ("Unknown data type in _S5: 0x%x\n", *ptr2);
233 return -1;
234 }
235 }
236 ptr += add = skip_name_string (ptr, end);
237 if (!add)
238 return -1;
239 ptr += add = skip_data_ref_object (ptr, end);
240 if (!add)
241 return -1;
242 break;
243 case GRUB_ACPI_OPCODE_SCOPE:
244 case GRUB_ACPI_OPCODE_IF:
245 case GRUB_ACPI_OPCODE_METHOD:
246 {
247 ptr++;
248 ptr += decode_length (ptr, 0);
249 break;
250 }
251 default:
252 grub_printf ("Unknown opcode 0x%x\n", *ptr);
253 return -1;
254 }
255 }
256
257 grub_dprintf ("acpi", "TYP = %d\n", sleep_type);
258 return sleep_type;
259 }
260
261 #ifdef GRUB_DSDT_TEST
262 int
263 main (int argc, char **argv)
264 {
265 FILE *f;
266 size_t len;
267 unsigned char *buf;
268 if (argc < 2)
269 printf ("Usage: %s FILE\n", argv[0]);
270 f = fopen (argv[1], "rb");
271 if (!f)
272 {
273 printf ("Couldn't open file\n");
274 return 1;
275 }
276 fseek (f, 0, SEEK_END);
277 len = ftell (f);
278 fseek (f, 0, SEEK_SET);
279 buf = malloc (len);
280 if (!buf)
281 {
282 printf ("Couldn't malloc buffer\n");
283 fclose (f);
284 return 2;
285 }
286 if (fread (buf, 1, len, f) != len)
287 {
288 printf ("Read failed\n");
289 free (buf);
290 fclose (f);
291 return 2;
292 }
293
294 printf ("Sleep type = %d\n", get_sleep_type (buf, buf + len));
295 free (buf);
296 fclose (f);
297 return 0;
298 }
299
300 #else
301
302 void
303 grub_acpi_halt (void)
304 {
305 struct grub_acpi_rsdp_v20 *rsdp2;
306 struct grub_acpi_rsdp_v10 *rsdp1;
307 struct grub_acpi_table_header *rsdt;
308 grub_uint32_t *entry_ptr;
309
310 rsdp2 = grub_acpi_get_rsdpv2 ();
311 if (rsdp2)
312 rsdp1 = &(rsdp2->rsdpv1);
313 else
314 rsdp1 = grub_acpi_get_rsdpv1 ();
315 grub_dprintf ("acpi", "rsdp1=%p\n", rsdp1);
316 if (!rsdp1)
317 return;
318
319 rsdt = (struct grub_acpi_table_header *) (grub_addr_t) rsdp1->rsdt_addr;
320 for (entry_ptr = (grub_uint32_t *) (rsdt + 1);
321 entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt)
322 + rsdt->length);
323 entry_ptr++)
324 {
325 if (grub_memcmp ((void *) (grub_addr_t) *entry_ptr, "FACP", 4) == 0)
326 {
327 grub_uint32_t port;
328 struct grub_acpi_fadt *fadt
329 = ((struct grub_acpi_fadt *) (grub_addr_t) *entry_ptr);
330 struct grub_acpi_table_header *dsdt
331 = (struct grub_acpi_table_header *) (grub_addr_t) fadt->dsdt_addr;
332 int sleep_type = -1;
333
334 port = fadt->pm1a;
335
336 grub_dprintf ("acpi", "PM1a port=%x\n", port);
337
338 if (grub_memcmp (dsdt->signature, "DSDT",
339 sizeof (dsdt->signature)) != 0)
340 break;
341
342 sleep_type = get_sleep_type ((grub_uint8_t *) dsdt,
343 (grub_uint8_t *) dsdt + dsdt->length);
344
345 if (sleep_type < 0 || sleep_type >= 8)
346 break;
347
348 grub_dprintf ("acpi", "SLP_TYP = %d, port = 0x%x\n",
349 sleep_type, port);
350
351 grub_outw (GRUB_ACPI_SLP_EN
352 | (sleep_type << GRUB_ACPI_SLP_TYP_OFFSET), port & 0xffff);
353 }
354 }
355
356 grub_millisleep (1500);
357
358 /* TRANSLATORS: It's computer shutdown using ACPI, not disabling ACPI. */
359 grub_puts_ (N_("ACPI shutdown failed"));
360 }
361 #endif