xref: /minix/minix/kernel/arch/i386/acpi.c (revision 7f5f010b)
1 
2 #include <string.h>
3 
4 #include "kernel/kernel.h"
5 #include "acpi.h"
6 #include "arch_proto.h"
7 
8 typedef int ((* acpi_read_t)(phys_bytes addr, void * buff, size_t size));
9 
10 struct acpi_rsdp acpi_rsdp;
11 
12 static acpi_read_t read_func;
13 
14 #define MAX_RSDT	35 /* ACPI defines 35 signatures */
15 #define SLP_EN_CODE	(1 << 13) /* ACPI SLP_EN_CODE code */
16 #define AMI_PACKAGE_OP_CODE (0x12)
17 #define AMI_NAME_OP_CODE (0x8)
18 #define AMI_BYTE_PREFIX_CODE (0xA)
19 #define AMI_PACKAGE_LENGTH_ENCODING_BITS_MASK (0xC0)
20 #define AMI_PACKAGE_LENGTH_ENCODING_BITS_SHIFT (6)
21 #define AMI_MIN_PACKAGE_LENGTH (1)
22 #define AMI_NUM_ELEMENTS_LENGTH (1)
23 #define AMI_SLP_TYPA_SHIFT (10)
24 #define AMI_SLP_TYPB_SHIFT (10)
25 #define AMI_S5_NAME_OP_OFFSET_1 (-1)
26 #define AMI_S5_NAME_OP_OFFSET_2 (-2)
27 #define AMI_S5_PACKAGE_OP_OFFSET (4)
28 #define AMI_S5_PACKET_LENGTH_OFFSET (5)
29 
30 static struct acpi_rsdt {
31 	struct acpi_sdt_header	hdr;
32 	u32_t			data[MAX_RSDT];
33 } rsdt;
34 
35 static struct {
36 	char	signature [ACPI_SDT_SIGNATURE_LEN + 1];
37 	size_t	length;
38 } sdt_trans[MAX_RSDT];
39 
40 static int sdt_count;
41 static u16_t pm1a_cnt_blk = 0;
42 static u16_t pm1b_cnt_blk = 0;
43 static u16_t slp_typa = 0;
44 static u16_t slp_typb = 0;
45 
46 static int acpi_check_csum(struct acpi_sdt_header * tb, size_t size)
47 {
48 	u8_t total = 0;
49 	int i;
50 	for (i = 0; i < size; i++)
51 		total += ((unsigned char *)tb)[i];
52 	return total == 0 ? 0 : -1;
53 }
54 
55 static int acpi_check_signature(const char * orig, const char * match)
56 {
57 	return strncmp(orig, match, ACPI_SDT_SIGNATURE_LEN);
58 }
59 
60 static u32_t acpi_phys2vir(u32_t p)
61 {
62 	if(!vm_running) {
63 		printf("acpi: returning 0x%lx as vir addr\n", p);
64 		return p;
65 	}
66 	panic("acpi: can't get virtual address of arbitrary physical address");
67 }
68 
69 static int acpi_phys_copy(phys_bytes phys, void *target, size_t len)
70 {
71 	if(!vm_running) {
72 		memcpy(target, (void *) phys, len);
73 		return 0;
74 	}
75 	panic("can't acpi_phys_copy with vm");
76 }
77 
78 static int acpi_read_sdt_at(phys_bytes addr,
79 				struct acpi_sdt_header * tb,
80 				size_t size,
81 				const char * name)
82 {
83 	struct acpi_sdt_header hdr;
84 
85 	/* if NULL is supplied, we only return the size of the table */
86 	if (tb == NULL) {
87 		if (read_func(addr, &hdr, sizeof(struct acpi_sdt_header))) {
88 			printf("ERROR acpi cannot read %s header\n", name);
89 			return -1;
90 		}
91 
92 		return hdr.length;
93 	}
94 
95 	if (read_func(addr, tb, sizeof(struct acpi_sdt_header))) {
96 		printf("ERROR acpi cannot read %s header\n", name);
97 		return -1;
98 	}
99 
100 	if (acpi_check_signature(tb->signature, name)) {
101 		printf("ERROR acpi %s signature does not match\n", name);
102 		return -1;
103 	}
104 
105 	if (size < tb->length) {
106 		printf("ERROR acpi buffer too small for %s\n", name);
107 		return -1;
108 	}
109 
110 	if (read_func(addr, tb, size)) {
111 		printf("ERROR acpi cannot read %s\n", name);
112 		return -1;
113 	}
114 
115 	if (acpi_check_csum(tb, tb->length)) {
116 		printf("ERROR acpi %s checksum does not match\n", name);
117 		return -1;
118 	}
119 
120 	return tb->length;
121 }
122 
123 phys_bytes acpi_get_table_base(const char * name)
124 {
125 	int i;
126 
127 	for(i = 0; i < sdt_count; i++) {
128 		if (strncmp(name, sdt_trans[i].signature,
129 					ACPI_SDT_SIGNATURE_LEN) == 0)
130 			return (phys_bytes) rsdt.data[i];
131 	}
132 
133 	return (phys_bytes) NULL;
134 }
135 
136 size_t acpi_get_table_length(const char * name)
137 {
138 	int i;
139 
140 	for(i = 0; i < sdt_count; i++) {
141 		if (strncmp(name, sdt_trans[i].signature,
142 					ACPI_SDT_SIGNATURE_LEN) == 0)
143 			return sdt_trans[i].length;
144 	}
145 
146 	return 0;
147 }
148 
149 static void * acpi_madt_get_typed_item(struct acpi_madt_hdr * hdr,
150 					unsigned char type,
151 					unsigned idx)
152 {
153 	u8_t * t, * end;
154 	int i;
155 
156 	t = (u8_t *) hdr + sizeof(struct acpi_madt_hdr);
157 	end = (u8_t *) hdr + hdr->hdr.length;
158 
159 	i = 0;
160 	while(t < end) {
161 		if (type == ((struct acpi_madt_item_hdr *) t)->type) {
162 			if (i == idx)
163 				return t;
164 			else
165 				i++;
166 		}
167 		t += ((struct acpi_madt_item_hdr *) t)->length;
168 	}
169 
170 	return NULL;
171 }
172 
173 #if 0
174 static void * acpi_madt_get_item(struct acpi_madt_hdr * hdr,
175 				unsigned idx)
176 {
177 	u8_t * t, * end;
178 	int i;
179 
180 	t = (u8_t *) hdr + sizeof(struct acpi_madt_hdr);
181 	end = (u8_t *) hdr + hdr->hdr.length;
182 
183 	for(i = 0 ; i <= idx && t < end; i++) {
184 		if (i == idx)
185 			return t;
186 		t += ((struct acpi_madt_item_hdr *) t)->length;
187 	}
188 
189 	return NULL;
190 }
191 #endif
192 
193 static int acpi_rsdp_test(void * buff)
194 {
195 	struct acpi_rsdp * rsdp = (struct acpi_rsdp *) buff;
196 
197 	if (!platform_tbl_checksum_ok(buff, 20))
198 		return 0;
199 	if (strncmp(rsdp->signature, "RSD PTR ", 8))
200 		return 0;
201 
202 	return 1;
203 }
204 
205 static int get_acpi_rsdp(void)
206 {
207 	u16_t ebda;
208 	/*
209 	 * Read 40:0Eh - to find the starting address of the EBDA.
210 	 */
211 	acpi_phys_copy (0x40E, &ebda, sizeof(ebda));
212 	if (ebda) {
213 		ebda <<= 4;
214 		if(platform_tbl_ptr(ebda, ebda + 0x400, 16, &acpi_rsdp,
215 					sizeof(acpi_rsdp), &machine.acpi_rsdp,
216 					acpi_rsdp_test))
217 			return 1;
218 	}
219 
220 	/* try BIOS read only mem space */
221 	if(platform_tbl_ptr(0xE0000, 0x100000, 16, &acpi_rsdp,
222 				sizeof(acpi_rsdp), &machine.acpi_rsdp,
223 				acpi_rsdp_test))
224 		return 1;
225 
226 	machine.acpi_rsdp = 0; /* RSDP cannot be found at this address therefore
227 				  it is a valid negative value */
228 	return 0;
229 }
230 
231 static void acpi_init_poweroff(void)
232 {
233 	u8_t *ptr = NULL;
234 	u8_t *start = NULL;
235 	u8_t *end = NULL;
236 	struct acpi_fadt_header *fadt_header = NULL;
237 	struct acpi_rsdt * dsdt_header = NULL;
238 	char *msg = NULL;
239 
240 	/* Everything used here existed since ACPI spec 1.0 */
241 	/* So we can safely use them */
242 	fadt_header = (struct acpi_fadt_header *)
243 		acpi_phys2vir(acpi_get_table_base("FACP"));
244 	if (fadt_header == NULL) {
245 		msg = "Could not load FACP";
246 		goto exit;
247 	}
248 
249 	dsdt_header = (struct acpi_rsdt *)
250 		acpi_phys2vir((phys_bytes) fadt_header->dsdt);
251 	if (dsdt_header == NULL) {
252 		msg = "Could not load DSDT";
253 		goto exit;
254 	}
255 
256 	pm1a_cnt_blk = fadt_header->pm1a_cnt_blk;
257 	pm1b_cnt_blk = fadt_header->pm1b_cnt_blk;
258 
259 	ptr = start = (u8_t *) dsdt_header->data;
260 	end = start + dsdt_header->hdr.length - 4;
261 
262 	/* See http://forum.osdev.org/viewtopic.php?t=16990 */
263 	/* for layout of \_S5 */
264 	while (ptr < end && memcmp(ptr, "_S5_", 4) != 0)
265 		ptr++;
266 
267 	msg = "Could not read S5 data. Use default SLP_TYPa and SLP_TYPb";
268 	if (ptr >= end || ptr == start)
269 		goto exit;
270 
271 	/* validate AML structure */
272 	if (*(ptr + AMI_S5_PACKAGE_OP_OFFSET) != AMI_PACKAGE_OP_CODE)
273 		goto exit;
274 
275 	if ((ptr < start + (-AMI_S5_NAME_OP_OFFSET_2) ||
276 		(*(ptr + AMI_S5_NAME_OP_OFFSET_2) != AMI_NAME_OP_CODE ||
277 		 *(ptr + AMI_S5_NAME_OP_OFFSET_2 + 1) != '\\')) &&
278 		*(ptr + AMI_S5_NAME_OP_OFFSET_1) != AMI_NAME_OP_CODE)
279 		goto exit;
280 
281 	ptr += AMI_S5_PACKET_LENGTH_OFFSET;
282 	if (ptr >= end)
283 		goto exit;
284 
285 	/* package length */
286 	ptr += ((*ptr & AMI_PACKAGE_LENGTH_ENCODING_BITS_MASK) >>
287 		AMI_PACKAGE_LENGTH_ENCODING_BITS_SHIFT) +
288 		AMI_MIN_PACKAGE_LENGTH + AMI_NUM_ELEMENTS_LENGTH;
289 	if (ptr >= end)
290 		goto exit;
291 
292 	if (*ptr == AMI_BYTE_PREFIX_CODE)
293 		ptr++; /* skip byte prefix */
294 
295 	slp_typa = (*ptr) << AMI_SLP_TYPA_SHIFT;
296 
297 	ptr++; /* move to SLP_TYPb */
298 	if (*ptr == AMI_BYTE_PREFIX_CODE)
299 		ptr++; /* skip byte prefix */
300 
301 	slp_typb = (*ptr) << AMI_SLP_TYPB_SHIFT;
302 
303 	msg = "poweroff initialized";
304 
305 exit:
306 	if (msg) {
307 		printf("acpi: %s\n", msg);
308 	}
309 }
310 
311 void acpi_init(void)
312 {
313 	int s, i;
314 	read_func = acpi_phys_copy;
315 
316 	if (!get_acpi_rsdp()) {
317 		printf("WARNING : Cannot configure ACPI\n");
318 		return;
319 	}
320 
321 	s = acpi_read_sdt_at(acpi_rsdp.rsdt_addr, (struct acpi_sdt_header *) &rsdt,
322 			sizeof(struct acpi_rsdt), ACPI_SDT_SIGNATURE(RSDT));
323 
324 	sdt_count = (s - sizeof(struct acpi_sdt_header)) / sizeof(u32_t);
325 
326 	for (i = 0; i < sdt_count; i++) {
327 		struct acpi_sdt_header hdr;
328 		int j;
329 		if (read_func(rsdt.data[i], &hdr, sizeof(struct acpi_sdt_header))) {
330 			printf("ERROR acpi cannot read header at 0x%x\n",
331 								rsdt.data[i]);
332 			return;
333 		}
334 
335 		for (j = 0 ; j < ACPI_SDT_SIGNATURE_LEN; j++)
336 			sdt_trans[i].signature[j] = hdr.signature[j];
337 		sdt_trans[i].signature[ACPI_SDT_SIGNATURE_LEN] = '\0';
338 		sdt_trans[i].length = hdr.length;
339 	}
340 
341 	acpi_init_poweroff();
342 }
343 
344 struct acpi_madt_ioapic * acpi_get_ioapic_next(void)
345 {
346 	static unsigned idx = 0;
347 	static struct acpi_madt_hdr * madt_hdr;
348 
349 	struct acpi_madt_ioapic * ret;
350 
351 	if (idx == 0) {
352 		madt_hdr = (struct acpi_madt_hdr *)
353 			acpi_phys2vir(acpi_get_table_base("APIC"));
354 		if (madt_hdr == NULL)
355 			return NULL;
356 	}
357 
358 	ret = (struct acpi_madt_ioapic *)
359 		acpi_madt_get_typed_item(madt_hdr, ACPI_MADT_TYPE_IOAPIC, idx);
360 	if (ret)
361 		idx++;
362 
363 	return ret;
364 }
365 
366 struct acpi_madt_lapic * acpi_get_lapic_next(void)
367 {
368 	static unsigned idx = 0;
369 	static struct acpi_madt_hdr * madt_hdr;
370 
371 	struct acpi_madt_lapic * ret;
372 
373 	if (idx == 0) {
374 		madt_hdr = (struct acpi_madt_hdr *)
375 			acpi_phys2vir(acpi_get_table_base("APIC"));
376 		if (madt_hdr == NULL)
377 			return NULL;
378 	}
379 
380 	for (;;) {
381 		ret = (struct acpi_madt_lapic *)
382 			acpi_madt_get_typed_item(madt_hdr,
383 					ACPI_MADT_TYPE_LAPIC, idx);
384 		if (!ret)
385 			break;
386 
387 		idx++;
388 
389 		/* report only usable CPUs */
390 		if (ret->flags & 1)
391 			break;
392 	}
393 
394 	return ret;
395 }
396 
397 void __k_unpaged_acpi_poweroff(void)
398 {
399 	/* NO OP poweroff symbol*/
400 }
401 
402 void acpi_poweroff(void)
403 {
404 	if (pm1a_cnt_blk == 0) {
405 		return;
406 	}
407 	outw(pm1a_cnt_blk, slp_typa | SLP_EN_CODE);
408 	if (pm1b_cnt_blk != 0) {
409 		outw(pm1b_cnt_blk, slp_typb | SLP_EN_CODE);
410 	}
411 }
412