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