1/*
2 * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <KernelExport.h>
8#include <vm/vm_types.h>
9
10#include <arch/x86/bios.h>
11
12
13//#define TRACE_BIOS
14#ifdef TRACE_BIOS
15#	define TRACE(x) dprintf x
16#else
17#	define TRACE(x) ;
18#endif
19
20
21struct smbios {
22	uint32		anchor_string;
23	uint8		entry_point_checksum;
24	uint8		entry_point_length;
25	struct {
26		uint8	major;
27		uint8	minor;
28	} version;
29	uint16		maximum_size;
30	uint8		formatted_area[5];
31
32	// this part is the legacy DMI compatible structure
33	uint8		dmi_anchor_string[5];
34	uint8		intermediate_checksum;
35	uint16		structure_table_size;
36	uint32		structure_table;
37	uint16		num_structures;
38	uint8		bcd_revision;
39} _PACKED;
40
41struct bios32 {
42	uint32		anchor_string;
43	uint32		service_directory;
44	uint8		revision;
45	uint8		size;	// in "paragraph" (16 byte) units
46	uint8		checksum;
47	uint8		_reserved[5];
48};
49
50enum {
51	BIOS32	= '_23_',
52	SMBIOS	= '_MS_',
53	DMI		= '_IMD',
54};
55
56
57addr_t gBiosBase;
58static addr_t sBios32ServiceDirectory;
59
60
61static bool
62check_checksum(addr_t base, size_t length)
63{
64	uint8 *bytes = (uint8 *)base;
65	uint8 sum = 0;
66
67	for (uint32 i = 0; i < length; i++)
68		sum += bytes[i];
69
70	return sum == 0;
71}
72
73
74//	#pragma mark -
75//	public functions
76
77
78/**	This function fills the provided bios32_service structure with
79 *	the values that identify BIOS service.
80 *	Returns B_OK on successful completion, otherwise B_ERROR if
81 *	the BIOS32 service directory is not available, or B_BAD_VALUE
82 *	in case the identifier is not known or present.
83 */
84
85extern "C" status_t
86get_bios32_service(uint32 identifier, struct bios32_service *service)
87{
88	TRACE(("get_bios32_service(identifier = %#lx)\n", identifier));
89
90	if (sBios32ServiceDirectory == 0)
91		return B_ERROR;
92
93	uint32 eax = 0, ebx = 0, ecx = 0, edx = 0;
94
95	asm("movl	%4, %%eax;		"	// set service parameters
96		"xorl	%%ebx, %%ebx;	"
97		"movl	%5, %%ecx;		"
98		"pushl	%%cs;			"	// emulate far call by register
99		"call	*%%ecx;			"
100		"movl	%%eax, %0;		"	// save return values
101		"movl	%%ebx, %1;		"
102		"movl	%%ecx, %2;		"
103		"movl	%%edx, %3;		"
104		: "=m" (eax), "=m" (ebx), "=m" (ecx), "=m" (edx)
105		: "m" (identifier), "m" (sBios32ServiceDirectory)
106		: "eax", "ebx", "ecx", "edx", "memory");
107
108	if ((eax & 0xff) != 0)
109		return B_BAD_VALUE;
110
111	service->base = ebx;
112	service->size = ecx;
113	service->offset = edx;
114
115	return B_OK;
116}
117
118
119extern "C" status_t
120bios_init(void)
121{
122	// map BIOS area 0xe0000 - 0xfffff
123	area_id biosArea = map_physical_memory("pc bios", 0xe0000, 0x20000,
124		B_ANY_KERNEL_ADDRESS | B_MTR_WB,
125		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&gBiosBase);
126	if (biosArea < 0)
127		return biosArea;
128
129	TRACE(("PC BIOS mapped to %p\n", (void *)gBiosBase));
130
131	// ToDo: add driver settings support to disable the services below
132
133	// search for available BIOS services
134
135	addr_t base = gBiosBase;
136	addr_t end = base + 0x20000;
137
138	while (base < end) {
139		switch (*(uint32 *)base) {
140			case BIOS32:
141				if (check_checksum(base, sizeof(struct bios32))) {
142					struct bios32 *bios32 = (struct bios32 *)base;
143
144					TRACE(("bios32 revision %d\n", bios32->revision));
145					TRACE(("bios32 service directory at: %lx\n", bios32->service_directory));
146
147					if (bios32->service_directory >= 0xe0000
148						&& bios32->service_directory <= 0xfffff)
149						sBios32ServiceDirectory = gBiosBase - 0xe0000 + bios32->service_directory;
150				}
151				break;
152			case SMBIOS:
153				// SMBIOS contains the legacy DMI structure, so we have to
154				// make sure it won't be found
155				base += 16;
156				TRACE(("probably found SMBIOS structure.\n"));
157				break;
158			case DMI:
159				TRACE(("probably found DMI legacy structure.\n"));
160				break;
161		}
162
163		// get on to next "paragraph"
164		base += 16;
165	}
166
167	return B_OK;
168}
169
170