⛏️ index : haiku.git

/*
 * Copyright 2004-2006, Axel DΓΆrfler, axeld@pinc-software.de. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include <KernelExport.h>
#include <vm/vm_types.h>

#include <arch/x86/bios.h>


//#define TRACE_BIOS
#ifdef TRACE_BIOS
#	define TRACE(x) dprintf x
#else
#	define TRACE(x) ;
#endif


struct smbios {
	uint32		anchor_string;
	uint8		entry_point_checksum;
	uint8		entry_point_length;
	struct {
		uint8	major;
		uint8	minor;
	} version;
	uint16		maximum_size;
	uint8		formatted_area[5];

	// this part is the legacy DMI compatible structure
	uint8		dmi_anchor_string[5];
	uint8		intermediate_checksum;
	uint16		structure_table_size;
	uint32		structure_table;
	uint16		num_structures;
	uint8		bcd_revision;
} _PACKED;

struct bios32 {
	uint32		anchor_string;
	uint32		service_directory;
	uint8		revision;
	uint8		size;	// in "paragraph" (16 byte) units
	uint8		checksum;
	uint8		_reserved[5];
};

enum {
	BIOS32	= '_23_',
	SMBIOS	= '_MS_',
	DMI		= '_IMD',
};


addr_t gBiosBase;
static addr_t sBios32ServiceDirectory;


static bool
check_checksum(addr_t base, size_t length)
{
	uint8 *bytes = (uint8 *)base;
	uint8 sum = 0;

	for (uint32 i = 0; i < length; i++)
		sum += bytes[i];

	return sum == 0;
}


//	#pragma mark -
//	public functions


/**	This function fills the provided bios32_service structure with
 *	the values that identify BIOS service.
 *	Returns B_OK on successful completion, otherwise B_ERROR if
 *	the BIOS32 service directory is not available, or B_BAD_VALUE
 *	in case the identifier is not known or present.
 */

extern "C" status_t
get_bios32_service(uint32 identifier, struct bios32_service *service)
{
	TRACE(("get_bios32_service(identifier = %#lx)\n", identifier));

	if (sBios32ServiceDirectory == 0)
		return B_ERROR;

	uint32 eax = 0, ebx = 0, ecx = 0, edx = 0;

	asm("movl	%4, %%eax;		"	// set service parameters
		"xorl	%%ebx, %%ebx;	"
		"movl	%5, %%ecx;		"
		"pushl	%%cs;			"	// emulate far call by register
		"call	*%%ecx;			"
		"movl	%%eax, %0;		"	// save return values
		"movl	%%ebx, %1;		"
		"movl	%%ecx, %2;		"
		"movl	%%edx, %3;		"
		: "=m" (eax), "=m" (ebx), "=m" (ecx), "=m" (edx)
		: "m" (identifier), "m" (sBios32ServiceDirectory)
		: "eax", "ebx", "ecx", "edx", "memory");

	if ((eax & 0xff) != 0)
		return B_BAD_VALUE;

	service->base = ebx;
	service->size = ecx;
	service->offset = edx;

	return B_OK;
}


extern "C" status_t
bios_init(void)
{
	// map BIOS area 0xe0000 - 0xfffff
	area_id biosArea = map_physical_memory("pc bios", 0xe0000, 0x20000,
		B_ANY_KERNEL_ADDRESS | B_WRITE_BACK_MEMORY,
		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&gBiosBase);
	if (biosArea < 0)
		return biosArea;

	TRACE(("PC BIOS mapped to %p\n", (void *)gBiosBase));

	// ToDo: add driver settings support to disable the services below

	// search for available BIOS services

	addr_t base = gBiosBase;
	addr_t end = base + 0x20000;

	while (base < end) {
		switch (*(uint32 *)base) {
			case BIOS32:
				if (check_checksum(base, sizeof(struct bios32))) {
					struct bios32 *bios32 = (struct bios32 *)base;

					TRACE(("bios32 revision %d\n", bios32->revision));
					TRACE(("bios32 service directory at: %lx\n", bios32->service_directory));

					if (bios32->service_directory >= 0xe0000
						&& bios32->service_directory <= 0xfffff)
						sBios32ServiceDirectory = gBiosBase - 0xe0000 + bios32->service_directory;
				}
				break;
			case SMBIOS:
				// SMBIOS contains the legacy DMI structure, so we have to
				// make sure it won't be found
				base += 16;
				TRACE(("probably found SMBIOS structure.\n"));
				break;
			case DMI:
				TRACE(("probably found DMI legacy structure.\n"));
				break;
		}

		// get on to next "paragraph"
		base += 16;
	}

	return B_OK;
}