xref: /haiku/src/add-ons/kernel/generic/bios/bios.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright 2012, Alex Smith, alex@alex-smith.me.uk.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <drivers/bios.h>
8 
9 #include <KernelExport.h>
10 
11 #include <AutoDeleter.h>
12 
13 #include <arch/x86/arch_cpu.h>
14 #include <vm/vm.h>
15 #include <vm/VMAddressSpace.h>
16 
17 #include "x86emu.h"
18 
19 
20 struct BIOSState {
21 	BIOSState()
22 		:
23 		mapped_address(0),
24 		bios_area(-1),
25 		ram_area(-1),
26 		allocated_size(0)
27 	{
28 	}
29 
30 	~BIOSState()
31 	{
32 		if (bios_area >= 0)
33 			delete_area(bios_area);
34 		if (ram_area >= 0)
35 			delete_area(ram_area);
36 	}
37 
38 	addr_t		mapped_address;
39 	area_id		bios_area;
40 	area_id		ram_area;
41 	size_t		allocated_size;
42 };
43 
44 
45 // BIOS memory layout definitions.
46 static const uint32 kBDABase = 0;
47 static const uint32 kBDASize = 0x1000;
48 static const uint32 kEBDABase = 0x90000;
49 static const uint32 kEBDASize = 0x70000;
50 static const uint32 kRAMBase = 0x1000;
51 static const uint32 kRAMSize = 0x8f000;
52 static const uint32 kStackSize = 0x1000;
53 static const uint32 kTotalSize = 0x100000;
54 
55 
56 static sem_id sBIOSLock;
57 static BIOSState* sCurrentBIOSState;
58 
59 
60 //	#pragma mark - X86EMU hooks.
61 
62 
63 static x86emuu8
64 x86emu_pio_inb(X86EMU_pioAddr port)
65 {
66 	return in8(port);
67 }
68 
69 
70 static x86emuu16
71 x86emu_pio_inw(X86EMU_pioAddr port)
72 {
73 	return in16(port);
74 }
75 
76 
77 static x86emuu32
78 x86emu_pio_inl(X86EMU_pioAddr port)
79 {
80 	return in32(port);
81 }
82 
83 
84 static void
85 x86emu_pio_outb(X86EMU_pioAddr port, x86emuu8 data)
86 {
87 	out8(data, port);
88 }
89 
90 
91 static void
92 x86emu_pio_outw(X86EMU_pioAddr port, x86emuu16 data)
93 {
94 	out16(data, port);
95 }
96 
97 
98 static void
99 x86emu_pio_outl(X86EMU_pioAddr port, x86emuu32 data)
100 {
101 	out32(data, port);
102 }
103 
104 
105 static X86EMU_pioFuncs x86emu_pio_funcs = {
106 	x86emu_pio_inb,
107 	x86emu_pio_inw,
108 	x86emu_pio_inl,
109 	x86emu_pio_outb,
110 	x86emu_pio_outw,
111 	x86emu_pio_outl,
112 };
113 
114 
115 static x86emuu8
116 x86emu_mem_rdb(x86emuu32 addr)
117 {
118 	return *(x86emuu8*)((addr_t)addr + sCurrentBIOSState->mapped_address);
119 }
120 
121 
122 static x86emuu16
123 x86emu_mem_rdw(x86emuu32 addr)
124 {
125 	return *(x86emuu16*)((addr_t)addr + sCurrentBIOSState->mapped_address);
126 }
127 
128 
129 static x86emuu32
130 x86emu_mem_rdl(x86emuu32 addr)
131 {
132 	return *(x86emuu32*)((addr_t)addr + sCurrentBIOSState->mapped_address);
133 }
134 
135 
136 static void
137 x86emu_mem_wrb(x86emuu32 addr, x86emuu8 val)
138 {
139 	*(x86emuu8*)((addr_t)addr + sCurrentBIOSState->mapped_address) = val;
140 }
141 
142 
143 static void
144 x86emu_mem_wrw(x86emuu32 addr, x86emuu16 val)
145 {
146 	*(x86emuu16*)((addr_t)addr + sCurrentBIOSState->mapped_address) = val;
147 }
148 
149 
150 static void
151 x86emu_mem_wrl(x86emuu32 addr, x86emuu32 val)
152 {
153 	*(x86emuu32*)((addr_t)addr + sCurrentBIOSState->mapped_address) = val;
154 }
155 
156 
157 static X86EMU_memFuncs x86emu_mem_funcs = {
158 	x86emu_mem_rdb,
159 	x86emu_mem_rdw,
160 	x86emu_mem_rdl,
161 	x86emu_mem_wrb,
162 	x86emu_mem_wrw,
163 	x86emu_mem_wrl,
164 };
165 
166 
167 //	#pragma mark -
168 
169 
170 static void*
171 bios_allocate_mem(bios_state* state, size_t size)
172 {
173 	// Simple allocator for memory to pass to the BIOS. No need for a complex
174 	// allocator here, there is only a few allocations per BIOS usage.
175 
176 	size = ROUNDUP(size, 4);
177 
178 	if (state->allocated_size + size > kRAMSize)
179 		return NULL;
180 
181 	void* address
182 		= (void*)(state->mapped_address + kRAMBase + state->allocated_size);
183 	state->allocated_size += size;
184 
185 	return address;
186 }
187 
188 
189 static uint32
190 bios_physical_address(bios_state* state, void* virtualAddress)
191 {
192 	return (uint32)((addr_t)virtualAddress - state->mapped_address);
193 }
194 
195 
196 static void*
197 bios_virtual_address(bios_state* state, uint32 physicalAddress)
198 {
199 	return (void*)((addr_t)physicalAddress + state->mapped_address);
200 }
201 
202 
203 static status_t
204 bios_prepare(bios_state** _state)
205 {
206 	status_t status;
207 
208 	BIOSState* state = new(std::nothrow) BIOSState;
209 	if (state == NULL)
210 		return B_NO_MEMORY;
211 	ObjectDeleter<BIOSState> stateDeleter(state);
212 
213 	// Reserve a chunk of address space to map at.
214 	status = vm_reserve_address_range(VMAddressSpace::KernelID(),
215 		(void**)&state->mapped_address, B_ANY_KERNEL_ADDRESS,
216 		kTotalSize, 0);
217 	if (status != B_OK)
218 		return status;
219 
220 	// Map RAM for for the BIOS.
221 	state->ram_area = create_area("bios ram", (void**)&state->mapped_address,
222 		B_EXACT_ADDRESS, kBDASize + kRAMSize, B_NO_LOCK, B_KERNEL_READ_AREA
223 			| B_KERNEL_WRITE_AREA);
224 	if (state->ram_area < B_OK) {
225 		vm_unreserve_address_range(VMAddressSpace::KernelID(),
226 			(void*)state->mapped_address, kTotalSize);
227 		return state->ram_area;
228 	}
229 
230 	// Copy the interrupt vectors and the BIOS data area.
231 	status = vm_memcpy_from_physical((void*)state->mapped_address, kBDABase,
232 		kBDASize, false);
233 	if (status != B_OK) {
234 		vm_unreserve_address_range(VMAddressSpace::KernelID(),
235 			(void*)state->mapped_address, kTotalSize);
236 		return status;
237 	}
238 
239 	// Map the extended BIOS data area and VGA memory.
240 	void* address = (void*)(state->mapped_address + kEBDABase);
241 	state->bios_area = map_physical_memory("bios", kEBDABase, kEBDASize,
242 		B_EXACT_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, &address);
243 	if (state->bios_area < B_OK) {
244 		vm_unreserve_address_range(VMAddressSpace::KernelID(),
245 			(void*)state->mapped_address, kTotalSize);
246 		return state->bios_area;
247 	}
248 
249 	// Attempt to acquire exclusive access to the BIOS.
250 	acquire_sem(sBIOSLock);
251 
252 	stateDeleter.Detach();
253 	*_state = state;
254 	return B_OK;
255 }
256 
257 
258 static status_t
259 bios_interrupt(bios_state* state, uint8 vector, bios_regs* regs)
260 {
261 	sCurrentBIOSState = state;
262 
263 	// Allocate a stack.
264 	void* stack = bios_allocate_mem(state, kStackSize);
265 	if (stack == NULL)
266 		return B_NO_MEMORY;
267 	uint32 stackTop = bios_physical_address(state, stack) + kStackSize;
268 
269 	// X86EMU finishes when it encounters a HLT instruction, allocate a
270 	// byte to store one in and set the return address of the interrupt to
271 	// point to it.
272 	void* halt = bios_allocate_mem(state, 1);
273 	if (halt == NULL)
274 		return B_NO_MEMORY;
275 	*(uint8*)halt = 0xF4;
276 
277 	// Copy in the registers.
278 	memset(&M, 0, sizeof(M));
279 	M.x86.R_EAX = regs->eax;
280 	M.x86.R_EBX = regs->ebx;
281 	M.x86.R_ECX = regs->ecx;
282 	M.x86.R_EDX = regs->edx;
283 	M.x86.R_EDI = regs->edi;
284 	M.x86.R_ESI = regs->esi;
285 	M.x86.R_EBP = regs->ebp;
286 	M.x86.R_EFLG = regs->eflags | X86_EFLAGS_INTERRUPT | X86_EFLAGS_RESERVED1;
287 	M.x86.R_EIP = bios_physical_address(state, halt);
288 	M.x86.R_CS = 0x0;
289 	M.x86.R_DS = regs->ds;
290 	M.x86.R_ES = regs->es;
291 	M.x86.R_FS = regs->fs;
292 	M.x86.R_GS = regs->gs;
293 	M.x86.R_SS = stackTop >> 4;
294 	M.x86.R_ESP = stackTop - (M.x86.R_SS << 4);
295 
296 	/* Run the interrupt. */
297 	X86EMU_setupPioFuncs(&x86emu_pio_funcs);
298 	X86EMU_setupMemFuncs(&x86emu_mem_funcs);
299 	X86EMU_prepareForInt(vector);
300 	X86EMU_exec();
301 
302 	// Copy back modified data.
303 	regs->eax = M.x86.R_EAX;
304 	regs->ebx = M.x86.R_EBX;
305 	regs->ecx = M.x86.R_ECX;
306 	regs->edx = M.x86.R_EDX;
307 	regs->edi = M.x86.R_EDI;
308 	regs->esi = M.x86.R_ESI;
309 	regs->ebp = M.x86.R_EBP;
310 	regs->eflags = M.x86.R_EFLG;
311 	regs->ds = M.x86.R_DS;
312 	regs->es = M.x86.R_ES;
313 	regs->fs = M.x86.R_FS;
314 	regs->gs = M.x86.R_GS;
315 
316 	return B_OK;
317 }
318 
319 
320 static void
321 bios_finish(bios_state* state)
322 {
323 	release_sem(sBIOSLock);
324 
325 	delete state;
326 }
327 
328 
329 static status_t
330 std_ops(int32 op, ...)
331 {
332 	switch (op) {
333 		case B_MODULE_INIT:
334 			sBIOSLock = create_sem(1, "bios lock");
335 			if (sBIOSLock < B_OK)
336 				return sBIOSLock;
337 
338 			return B_OK;
339 		case B_MODULE_UNINIT:
340 			delete_sem(sBIOSLock);
341 			return B_OK;
342 		default:
343 			return B_ERROR;
344 	}
345 }
346 
347 
348 static bios_module_info sBIOSModule = {
349 	{
350 		B_BIOS_MODULE_NAME,
351 		0,
352 		std_ops
353 	},
354 
355 	bios_prepare,
356 	bios_interrupt,
357 	bios_finish,
358 	bios_allocate_mem,
359 	bios_physical_address,
360 	bios_virtual_address
361 };
362 
363 
364 module_info *modules[] = {
365 	(module_info*)&sBIOSModule,
366 	NULL
367 };
368