xref: /haiku/src/system/kernel/arch/x86/32/bios.cpp (revision 5c1f231967bbf06af56728b86ad70f266c99f64d)
14ebc6dfaSAlex Smith /*
24ebc6dfaSAlex Smith  * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
34ebc6dfaSAlex Smith  * Distributed under the terms of the MIT License.
44ebc6dfaSAlex Smith  */
54ebc6dfaSAlex Smith 
64ebc6dfaSAlex Smith 
74ebc6dfaSAlex Smith #include <KernelExport.h>
84ebc6dfaSAlex Smith #include <vm/vm_types.h>
94ebc6dfaSAlex Smith 
104ebc6dfaSAlex Smith #include <arch/x86/bios.h>
114ebc6dfaSAlex Smith 
124ebc6dfaSAlex Smith 
134ebc6dfaSAlex Smith //#define TRACE_BIOS
144ebc6dfaSAlex Smith #ifdef TRACE_BIOS
154ebc6dfaSAlex Smith #	define TRACE(x) dprintf x
164ebc6dfaSAlex Smith #else
174ebc6dfaSAlex Smith #	define TRACE(x) ;
184ebc6dfaSAlex Smith #endif
194ebc6dfaSAlex Smith 
204ebc6dfaSAlex Smith 
214ebc6dfaSAlex Smith struct smbios {
224ebc6dfaSAlex Smith 	uint32		anchor_string;
234ebc6dfaSAlex Smith 	uint8		entry_point_checksum;
244ebc6dfaSAlex Smith 	uint8		entry_point_length;
254ebc6dfaSAlex Smith 	struct {
264ebc6dfaSAlex Smith 		uint8	major;
274ebc6dfaSAlex Smith 		uint8	minor;
284ebc6dfaSAlex Smith 	} version;
294ebc6dfaSAlex Smith 	uint16		maximum_size;
304ebc6dfaSAlex Smith 	uint8		formatted_area[5];
314ebc6dfaSAlex Smith 
324ebc6dfaSAlex Smith 	// this part is the legacy DMI compatible structure
334ebc6dfaSAlex Smith 	uint8		dmi_anchor_string[5];
344ebc6dfaSAlex Smith 	uint8		intermediate_checksum;
354ebc6dfaSAlex Smith 	uint16		structure_table_size;
364ebc6dfaSAlex Smith 	uint32		structure_table;
374ebc6dfaSAlex Smith 	uint16		num_structures;
384ebc6dfaSAlex Smith 	uint8		bcd_revision;
394ebc6dfaSAlex Smith } _PACKED;
404ebc6dfaSAlex Smith 
414ebc6dfaSAlex Smith struct bios32 {
424ebc6dfaSAlex Smith 	uint32		anchor_string;
434ebc6dfaSAlex Smith 	uint32		service_directory;
444ebc6dfaSAlex Smith 	uint8		revision;
454ebc6dfaSAlex Smith 	uint8		size;	// in "paragraph" (16 byte) units
464ebc6dfaSAlex Smith 	uint8		checksum;
474ebc6dfaSAlex Smith 	uint8		_reserved[5];
484ebc6dfaSAlex Smith };
494ebc6dfaSAlex Smith 
504ebc6dfaSAlex Smith enum {
514ebc6dfaSAlex Smith 	BIOS32	= '_23_',
524ebc6dfaSAlex Smith 	SMBIOS	= '_MS_',
534ebc6dfaSAlex Smith 	DMI		= '_IMD',
544ebc6dfaSAlex Smith };
554ebc6dfaSAlex Smith 
564ebc6dfaSAlex Smith 
574ebc6dfaSAlex Smith addr_t gBiosBase;
584ebc6dfaSAlex Smith static addr_t sBios32ServiceDirectory;
594ebc6dfaSAlex Smith 
604ebc6dfaSAlex Smith 
614ebc6dfaSAlex Smith static bool
check_checksum(addr_t base,size_t length)624ebc6dfaSAlex Smith check_checksum(addr_t base, size_t length)
634ebc6dfaSAlex Smith {
644ebc6dfaSAlex Smith 	uint8 *bytes = (uint8 *)base;
654ebc6dfaSAlex Smith 	uint8 sum = 0;
664ebc6dfaSAlex Smith 
674ebc6dfaSAlex Smith 	for (uint32 i = 0; i < length; i++)
684ebc6dfaSAlex Smith 		sum += bytes[i];
694ebc6dfaSAlex Smith 
704ebc6dfaSAlex Smith 	return sum == 0;
714ebc6dfaSAlex Smith }
724ebc6dfaSAlex Smith 
734ebc6dfaSAlex Smith 
744ebc6dfaSAlex Smith //	#pragma mark -
754ebc6dfaSAlex Smith //	public functions
764ebc6dfaSAlex Smith 
774ebc6dfaSAlex Smith 
784ebc6dfaSAlex Smith /**	This function fills the provided bios32_service structure with
794ebc6dfaSAlex Smith  *	the values that identify BIOS service.
804ebc6dfaSAlex Smith  *	Returns B_OK on successful completion, otherwise B_ERROR if
814ebc6dfaSAlex Smith  *	the BIOS32 service directory is not available, or B_BAD_VALUE
824ebc6dfaSAlex Smith  *	in case the identifier is not known or present.
834ebc6dfaSAlex Smith  */
844ebc6dfaSAlex Smith 
854ebc6dfaSAlex Smith extern "C" status_t
get_bios32_service(uint32 identifier,struct bios32_service * service)864ebc6dfaSAlex Smith get_bios32_service(uint32 identifier, struct bios32_service *service)
874ebc6dfaSAlex Smith {
884ebc6dfaSAlex Smith 	TRACE(("get_bios32_service(identifier = %#lx)\n", identifier));
894ebc6dfaSAlex Smith 
904ebc6dfaSAlex Smith 	if (sBios32ServiceDirectory == 0)
914ebc6dfaSAlex Smith 		return B_ERROR;
924ebc6dfaSAlex Smith 
934ebc6dfaSAlex Smith 	uint32 eax = 0, ebx = 0, ecx = 0, edx = 0;
944ebc6dfaSAlex Smith 
954ebc6dfaSAlex Smith 	asm("movl	%4, %%eax;		"	// set service parameters
964ebc6dfaSAlex Smith 		"xorl	%%ebx, %%ebx;	"
974ebc6dfaSAlex Smith 		"movl	%5, %%ecx;		"
984ebc6dfaSAlex Smith 		"pushl	%%cs;			"	// emulate far call by register
994ebc6dfaSAlex Smith 		"call	*%%ecx;			"
1004ebc6dfaSAlex Smith 		"movl	%%eax, %0;		"	// save return values
1014ebc6dfaSAlex Smith 		"movl	%%ebx, %1;		"
1024ebc6dfaSAlex Smith 		"movl	%%ecx, %2;		"
1034ebc6dfaSAlex Smith 		"movl	%%edx, %3;		"
1044ebc6dfaSAlex Smith 		: "=m" (eax), "=m" (ebx), "=m" (ecx), "=m" (edx)
1054ebc6dfaSAlex Smith 		: "m" (identifier), "m" (sBios32ServiceDirectory)
1064ebc6dfaSAlex Smith 		: "eax", "ebx", "ecx", "edx", "memory");
1074ebc6dfaSAlex Smith 
1084ebc6dfaSAlex Smith 	if ((eax & 0xff) != 0)
1094ebc6dfaSAlex Smith 		return B_BAD_VALUE;
1104ebc6dfaSAlex Smith 
1114ebc6dfaSAlex Smith 	service->base = ebx;
1124ebc6dfaSAlex Smith 	service->size = ecx;
1134ebc6dfaSAlex Smith 	service->offset = edx;
1144ebc6dfaSAlex Smith 
1154ebc6dfaSAlex Smith 	return B_OK;
1164ebc6dfaSAlex Smith }
1174ebc6dfaSAlex Smith 
1184ebc6dfaSAlex Smith 
1194ebc6dfaSAlex Smith extern "C" status_t
bios_init(void)1204ebc6dfaSAlex Smith bios_init(void)
1214ebc6dfaSAlex Smith {
1224ebc6dfaSAlex Smith 	// map BIOS area 0xe0000 - 0xfffff
1234ebc6dfaSAlex Smith 	area_id biosArea = map_physical_memory("pc bios", 0xe0000, 0x20000,
124*5c1f2319SAugustin Cavalier 		B_ANY_KERNEL_ADDRESS | B_WRITE_BACK_MEMORY,
1254ebc6dfaSAlex Smith 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&gBiosBase);
1264ebc6dfaSAlex Smith 	if (biosArea < 0)
1274ebc6dfaSAlex Smith 		return biosArea;
1284ebc6dfaSAlex Smith 
1294ebc6dfaSAlex Smith 	TRACE(("PC BIOS mapped to %p\n", (void *)gBiosBase));
1304ebc6dfaSAlex Smith 
1314ebc6dfaSAlex Smith 	// ToDo: add driver settings support to disable the services below
1324ebc6dfaSAlex Smith 
1334ebc6dfaSAlex Smith 	// search for available BIOS services
1344ebc6dfaSAlex Smith 
1354ebc6dfaSAlex Smith 	addr_t base = gBiosBase;
1364ebc6dfaSAlex Smith 	addr_t end = base + 0x20000;
1374ebc6dfaSAlex Smith 
1384ebc6dfaSAlex Smith 	while (base < end) {
1394ebc6dfaSAlex Smith 		switch (*(uint32 *)base) {
1404ebc6dfaSAlex Smith 			case BIOS32:
1414ebc6dfaSAlex Smith 				if (check_checksum(base, sizeof(struct bios32))) {
1424ebc6dfaSAlex Smith 					struct bios32 *bios32 = (struct bios32 *)base;
1434ebc6dfaSAlex Smith 
1444ebc6dfaSAlex Smith 					TRACE(("bios32 revision %d\n", bios32->revision));
1454ebc6dfaSAlex Smith 					TRACE(("bios32 service directory at: %lx\n", bios32->service_directory));
1464ebc6dfaSAlex Smith 
1474ebc6dfaSAlex Smith 					if (bios32->service_directory >= 0xe0000
1484ebc6dfaSAlex Smith 						&& bios32->service_directory <= 0xfffff)
1494ebc6dfaSAlex Smith 						sBios32ServiceDirectory = gBiosBase - 0xe0000 + bios32->service_directory;
1504ebc6dfaSAlex Smith 				}
1514ebc6dfaSAlex Smith 				break;
1524ebc6dfaSAlex Smith 			case SMBIOS:
1534ebc6dfaSAlex Smith 				// SMBIOS contains the legacy DMI structure, so we have to
1544ebc6dfaSAlex Smith 				// make sure it won't be found
1554ebc6dfaSAlex Smith 				base += 16;
1564ebc6dfaSAlex Smith 				TRACE(("probably found SMBIOS structure.\n"));
1574ebc6dfaSAlex Smith 				break;
1584ebc6dfaSAlex Smith 			case DMI:
1594ebc6dfaSAlex Smith 				TRACE(("probably found DMI legacy structure.\n"));
1604ebc6dfaSAlex Smith 				break;
1614ebc6dfaSAlex Smith 		}
1624ebc6dfaSAlex Smith 
1634ebc6dfaSAlex Smith 		// get on to next "paragraph"
1644ebc6dfaSAlex Smith 		base += 16;
1654ebc6dfaSAlex Smith 	}
1664ebc6dfaSAlex Smith 
1674ebc6dfaSAlex Smith 	return B_OK;
1684ebc6dfaSAlex Smith }
1694ebc6dfaSAlex Smith 
170