185f26688SStefano Ceccherini/*
273df2f20SRene Gollent * Copyright 2011, Rene Gollent, rene@gollent.com.
385f26688SStefano Ceccherini * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved.
485f26688SStefano Ceccherini * Copyright 2007, Michael Lotz, mmlr@mlotz.ch
585f26688SStefano Ceccherini * Copyright 2004-2005, Axel D��rfler, axeld@pinc-software.de.
685f26688SStefano Ceccherini * Distributed under the terms of the MIT License.
785f26688SStefano Ceccherini *
885f26688SStefano Ceccherini * Copyright 2001, Travis Geiselbrecht. All rights reserved.
985f26688SStefano Ceccherini * Distributed under the terms of the NewOS License.
1085f26688SStefano Ceccherini*/
1185f26688SStefano Ceccherini
1285f26688SStefano Ceccherini
1385f26688SStefano Ceccherini#include "acpi.h"
1485f26688SStefano Ceccherini#include "mmu.h"
1585f26688SStefano Ceccherini
1612378618SAxel Dörfler#include <string.h>
1712378618SAxel Dörfler
1885f26688SStefano Ceccherini#include <KernelExport.h>
1985f26688SStefano Ceccherini
2085f26688SStefano Ceccherini#include <arch/x86/arch_acpi.h>
2185f26688SStefano Ceccherini
2285f26688SStefano Ceccherini
2385f26688SStefano Ceccherini//#define TRACE_ACPI
2485f26688SStefano Ceccherini#ifdef TRACE_ACPI
2585f26688SStefano Ceccherini#	define TRACE(x) dprintf x
2685f26688SStefano Ceccherini#else
2785f26688SStefano Ceccherini#	define TRACE(x) ;
2885f26688SStefano Ceccherini#endif
2985f26688SStefano Ceccherini
3085f26688SStefano Ceccherinistatic struct scan_spots_struct acpi_scan_spots[] = {
3185f26688SStefano Ceccherini	{ 0x0, 0x400, 0x400 - 0x0 },
3285f26688SStefano Ceccherini	{ 0xe0000, 0x100000, 0x100000 - 0xe0000 },
3385f26688SStefano Ceccherini	{ 0, 0, 0 }
3485f26688SStefano Ceccherini};
3585f26688SStefano Ceccherini
3643e738bdSStefano Ceccherinistatic acpi_descriptor_header* sAcpiRsdt; // System Description Table
3773df2f20SRene Gollentstatic acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table
3833e11f7cSStefano Ceccherinistatic int32 sNumEntries = -1;
3985f26688SStefano Ceccherini
4043e738bdSStefano Ceccherini
4173df2f20SRene Gollentstatic status_t
4273df2f20SRene Gollentacpi_validate_rsdp(acpi_rsdp* rsdp)
4373df2f20SRene Gollent{
4473df2f20SRene Gollent	const char* data = (const char*)rsdp;
4573df2f20SRene Gollent	unsigned char checksum = 0;
4673df2f20SRene Gollent	for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++)
4773df2f20SRene Gollent		checksum += data[i];
4873df2f20SRene Gollent
4973df2f20SRene Gollent	if ((checksum & 0xff) != 0) {
5073df2f20SRene Gollent		TRACE(("acpi: rsdp failed basic checksum\n"));
5173df2f20SRene Gollent		return B_BAD_DATA;
5273df2f20SRene Gollent	}
5373df2f20SRene Gollent
5473df2f20SRene Gollent	// for ACPI 2.0+ we need to also validate the extended checksum
5573df2f20SRene Gollent	if (rsdp->revision > 0) {
5673df2f20SRene Gollent		for (uint32 i = sizeof(acpi_rsdp_legacy);
5773df2f20SRene Gollent			i < sizeof(acpi_rsdp_extended); i++) {
5873df2f20SRene Gollent				checksum += data[i];
5973df2f20SRene Gollent		}
6073df2f20SRene Gollent
6173df2f20SRene Gollent		if ((checksum & 0xff) != 0) {
6273df2f20SRene Gollent			TRACE(("acpi: rsdp failed extended checksum\n"));
6373df2f20SRene Gollent			return B_BAD_DATA;
6473df2f20SRene Gollent		}
6573df2f20SRene Gollent	}
6673df2f20SRene Gollent
6773df2f20SRene Gollent	return B_OK;
6873df2f20SRene Gollent}
6973df2f20SRene Gollent
7073df2f20SRene Gollent
71c4b692daSRene Gollentstatic status_t
72c4b692daSRene Gollentacpi_validate_rsdt(acpi_descriptor_header* rsdt)
73c4b692daSRene Gollent{
74c4b692daSRene Gollent	const char* data = (const char*)rsdt;
75c4b692daSRene Gollent	unsigned char checksum = 0;
76c4b692daSRene Gollent	for (uint32 i = 0; i < rsdt->length; i++)
77c4b692daSRene Gollent		checksum += data[i];
78c4b692daSRene Gollent
79c4b692daSRene Gollent	return checksum == 0 ? B_OK : B_BAD_DATA;
80c4b692daSRene Gollent}
81c4b692daSRene Gollent
82c4b692daSRene Gollent
8385f26688SStefano Ceccherinistatic status_t
8443e738bdSStefano Ceccheriniacpi_check_rsdt(acpi_rsdp* rsdp)
8585f26688SStefano Ceccherini{
8673df2f20SRene Gollent	if (acpi_validate_rsdp(rsdp) != B_OK)
8773df2f20SRene Gollent		return B_BAD_DATA;
8873df2f20SRene Gollent
8973df2f20SRene Gollent	bool usingXsdt = false;
9073df2f20SRene Gollent
91c4b692daSRene Gollent	TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n",
92b047e413SStefano Ceccherini		rsdp, rsdp->oem_id, rsdp->revision));
9385f26688SStefano Ceccherini	TRACE(("acpi: rsdp points to rsdt at 0x%lx\n", rsdp->rsdt_address));
9485f26688SStefano Ceccherini
9573df2f20SRene Gollent	uint32 length = 0;
9673df2f20SRene Gollent	acpi_descriptor_header* rsdt = NULL;
9773df2f20SRene Gollent	if (rsdp->revision > 0) {
9873df2f20SRene Gollent		length = rsdp->xsdt_length;
9973df2f20SRene Gollent		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
10073df2f20SRene Gollent			(uint32)rsdp->xsdt_address, rsdp->xsdt_length, kDefaultPageFlags);
10173df2f20SRene Gollent		if (rsdt != NULL
10273df2f20SRene Gollent			&& strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) {
10373df2f20SRene Gollent			mmu_free(rsdt, rsdp->xsdt_length);
10473df2f20SRene Gollent			rsdt = NULL;
10573df2f20SRene Gollent			TRACE(("acpi: invalid extended system description table\n"));
10673df2f20SRene Gollent		} else
10773df2f20SRene Gollent			usingXsdt = true;
10873df2f20SRene Gollent	}
10973df2f20SRene Gollent
11073df2f20SRene Gollent	// if we're ACPI v1 or we fail to map the XSDT for some reason,
11173df2f20SRene Gollent	// attempt to use the RSDT instead.
11273df2f20SRene Gollent	if (rsdt == NULL) {
11373df2f20SRene Gollent		// map and validate the root system description table
11473df2f20SRene Gollent		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
11573df2f20SRene Gollent			rsdp->rsdt_address, sizeof(acpi_descriptor_header),
11673df2f20SRene Gollent			kDefaultPageFlags);
1170994532dSJérôme Duval		if (rsdt == NULL) {
1180994532dSJérôme Duval			TRACE(("acpi: couldn't map rsdt header\n"));
1190994532dSJérôme Duval			return B_ERROR;
1200994532dSJérôme Duval		}
1210994532dSJérôme Duval		if (strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) {
122c4b692daSRene Gollent			mmu_free(rsdt, sizeof(acpi_descriptor_header));
12373df2f20SRene Gollent			rsdt = NULL;
12473df2f20SRene Gollent			TRACE(("acpi: invalid root system description table\n"));
12573df2f20SRene Gollent			return B_ERROR;
12673df2f20SRene Gollent		}
12773df2f20SRene Gollent
12873df2f20SRene Gollent		length = rsdt->length;
12973df2f20SRene Gollent		// Map the whole table, not just the header
13073df2f20SRene Gollent		TRACE(("acpi: rsdt length: %lu\n", length));
13173df2f20SRene Gollent		mmu_free(rsdt, sizeof(acpi_descriptor_header));
13273df2f20SRene Gollent		rsdt = (acpi_descriptor_header*)mmu_map_physical_memory(
13373df2f20SRene Gollent			rsdp->rsdt_address, length, kDefaultPageFlags);
13485f26688SStefano Ceccherini	}
13585f26688SStefano Ceccherini
13673df2f20SRene Gollent	if (rsdt != NULL) {
13773df2f20SRene Gollent		if (acpi_validate_rsdt(rsdt) != B_OK) {
13873df2f20SRene Gollent			TRACE(("acpi: rsdt failed checksum validation\n"));
13973df2f20SRene Gollent			mmu_free(rsdt, length);
140c4b692daSRene Gollent			return B_ERROR;
14173df2f20SRene Gollent		} else {
14273df2f20SRene Gollent			if (usingXsdt)
14373df2f20SRene Gollent				sAcpiXsdt = rsdt;
14473df2f20SRene Gollent			else
14573df2f20SRene Gollent				sAcpiRsdt = rsdt;
14673df2f20SRene Gollent			TRACE(("acpi: found valid %s at %p\n",
14773df2f20SRene Gollent				usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE,
14873df2f20SRene Gollent				rsdt));
149c4b692daSRene Gollent		}
150c4b692daSRene Gollent	} else
151c4b692daSRene Gollent		return B_ERROR;
152c4b692daSRene Gollent
15385f26688SStefano Ceccherini	return B_OK;
15485f26688SStefano Ceccherini}
15585f26688SStefano Ceccherini
15685f26688SStefano Ceccherini
15773df2f20SRene Gollenttemplate<typename PointerType>
15843e738bdSStefano Ceccheriniacpi_descriptor_header*
15973df2f20SRene Gollentacpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt)
16085f26688SStefano Ceccherini{
16173df2f20SRene Gollent	if (acpiSdt == NULL)
16285f26688SStefano Ceccherini		return NULL;
16385f26688SStefano Ceccherini
16443e738bdSStefano Ceccherini	if (sNumEntries == -1) {
16573df2f20SRene Gollent		// if using the xsdt, our entries are 64 bits wide.
16673df2f20SRene Gollent		sNumEntries = (acpiSdt->length
16773df2f20SRene Gollent			- sizeof(acpi_descriptor_header))
16873df2f20SRene Gollent				/ sizeof(PointerType);
16943e738bdSStefano Ceccherini	}
170f3eff1caSMichael Lotz
17133e11f7cSStefano Ceccherini	if (sNumEntries <= 0) {
17285f26688SStefano Ceccherini		TRACE(("acpi: root system description table is empty\n"));
17385f26688SStefano Ceccherini		return NULL;
17485f26688SStefano Ceccherini	}
17585f26688SStefano Ceccherini
17633e11f7cSStefano Ceccherini	TRACE(("acpi: searching %ld entries for table '%.4s'\n", sNumEntries,
17712378618SAxel Dörfler		signature));
17812378618SAxel Dörfler
17973df2f20SRene Gollent	PointerType* pointer = (PointerType*)((uint8*)acpiSdt
18012378618SAxel Dörfler		+ sizeof(acpi_descriptor_header));
18112378618SAxel Dörfler
182fb764e7fSStefano Ceccherini	acpi_descriptor_header* header = NULL;
18333e11f7cSStefano Ceccherini	for (int32 j = 0; j < sNumEntries; j++, pointer++) {
184fb764e7fSStefano Ceccherini		header = (acpi_descriptor_header*)
18573df2f20SRene Gollent			mmu_map_physical_memory((uint32)*pointer,
18673df2f20SRene Gollent				sizeof(acpi_descriptor_header), kDefaultPageFlags);
18773df2f20SRene Gollent
18843e738bdSStefano Ceccherini		if (header == NULL
18943e738bdSStefano Ceccherini			|| strncmp(header->signature, signature, 4) != 0) {
19085f26688SStefano Ceccherini			// not interesting for us
191f3eff1caSMichael Lotz			TRACE(("acpi: Looking for '%.4s'. Skipping '%.4s'\n",
192f3eff1caSMichael Lotz				signature, header != NULL ? header->signature : "null"));
193f3eff1caSMichael Lotz
1946e70e32dSanevilyak			if (header != NULL) {
195ad12a198SStefano Ceccherini				mmu_free(header, sizeof(acpi_descriptor_header));
1966e70e32dSanevilyak				header = NULL;
1976e70e32dSanevilyak			}
19873df2f20SRene Gollent
19985f26688SStefano Ceccherini			continue;
20085f26688SStefano Ceccherini		}
201f3eff1caSMichael Lotz
2027d2739a0SStefano Ceccherini		TRACE(("acpi: Found '%.4s' @ %p\n", signature, pointer));
203fb764e7fSStefano Ceccherini		break;
20485f26688SStefano Ceccherini	}
20585f26688SStefano Ceccherini
20673df2f20SRene Gollent
207fb764e7fSStefano Ceccherini	if (header == NULL)
208fb764e7fSStefano Ceccherini		return NULL;
209fb764e7fSStefano Ceccherini
210fb764e7fSStefano Ceccherini	// Map the whole table, not just the header
211fb764e7fSStefano Ceccherini	uint32 length = header->length;
212fb764e7fSStefano Ceccherini	mmu_free(header, sizeof(acpi_descriptor_header));
213fb764e7fSStefano Ceccherini
21473df2f20SRene Gollent	return (acpi_descriptor_header*)mmu_map_physical_memory(
21573df2f20SRene Gollent		(uint32)*pointer, length, kDefaultPageFlags);
21673df2f20SRene Gollent}
21773df2f20SRene Gollent
21873df2f20SRene Gollent
21973df2f20SRene Gollentacpi_descriptor_header*
22073df2f20SRene Gollentacpi_find_table(const char* signature)
22173df2f20SRene Gollent{
22273df2f20SRene Gollent	if (sAcpiRsdt != NULL)
22373df2f20SRene Gollent		return acpi_find_table_generic<uint32>(signature, sAcpiRsdt);
22473df2f20SRene Gollent	else if (sAcpiXsdt != NULL)
22573df2f20SRene Gollent		return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
22673df2f20SRene Gollent
22773df2f20SRene Gollent	return NULL;
22885f26688SStefano Ceccherini}
22985f26688SStefano Ceccherini
23085f26688SStefano Ceccherini
23185f26688SStefano Ceccherinivoid
23243e738bdSStefano Ceccheriniacpi_init()
23385f26688SStefano Ceccherini{
23485f26688SStefano Ceccherini	// Try to find the ACPI RSDP.
23585f26688SStefano Ceccherini	for (int32 i = 0; acpi_scan_spots[i].length > 0; i++) {
23643e738bdSStefano Ceccherini		acpi_rsdp* rsdp = NULL;
23712378618SAxel Dörfler
23812378618SAxel Dörfler		TRACE(("acpi_init: entry base 0x%lx, limit 0x%lx\n",
23912378618SAxel Dörfler			acpi_scan_spots[i].start, acpi_scan_spots[i].stop));
24012378618SAxel Dörfler
24143e738bdSStefano Ceccherini		for (char* pointer = (char*)acpi_scan_spots[i].start;
24285f26688SStefano Ceccherini		     (uint32)pointer < acpi_scan_spots[i].stop; pointer += 16) {
24385f26688SStefano Ceccherini			if (strncmp(pointer, ACPI_RSDP_SIGNATURE, 8) == 0) {
24443e738bdSStefano Ceccherini				TRACE(("acpi_init: found ACPI RSDP signature at %p\n",
24543e738bdSStefano Ceccherini					pointer));
24643e738bdSStefano Ceccherini				rsdp = (acpi_rsdp*)pointer;
24785f26688SStefano Ceccherini			}
24885f26688SStefano Ceccherini		}
24912378618SAxel Dörfler
25012378618SAxel Dörfler		if (rsdp != NULL && acpi_check_rsdt(rsdp) == B_OK)
25185f26688SStefano Ceccherini			break;
25285f26688SStefano Ceccherini	}
25385f26688SStefano Ceccherini}