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 {
BIOSStateBIOSState21 BIOSState()
22 :
23 mapped_address(0),
24 area(-1),
25 allocated_size(0)
26 {
27 }
28
~BIOSStateBIOSState29 ~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
x86emu_pio_inb(X86EMU_pioAddr port)72 x86emu_pio_inb(X86EMU_pioAddr port)
73 {
74 return in8(port);
75 }
76
77
78 static x86emuu16
x86emu_pio_inw(X86EMU_pioAddr port)79 x86emu_pio_inw(X86EMU_pioAddr port)
80 {
81 return in16(port);
82 }
83
84
85 static x86emuu32
x86emu_pio_inl(X86EMU_pioAddr port)86 x86emu_pio_inl(X86EMU_pioAddr port)
87 {
88 return in32(port);
89 }
90
91
92 static void
x86emu_pio_outb(X86EMU_pioAddr port,x86emuu8 data)93 x86emu_pio_outb(X86EMU_pioAddr port, x86emuu8 data)
94 {
95 out8(data, port);
96 }
97
98
99 static void
x86emu_pio_outw(X86EMU_pioAddr port,x86emuu16 data)100 x86emu_pio_outw(X86EMU_pioAddr port, x86emuu16 data)
101 {
102 out16(data, port);
103 }
104
105
106 static void
x86emu_pio_outl(X86EMU_pioAddr port,x86emuu32 data)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
x86emu_mem_rdb(x86emuu32 addr)124 x86emu_mem_rdb(x86emuu32 addr)
125 {
126 return *(x86emuu8*)((addr_t)addr + sCurrentBIOSState->mapped_address);
127 }
128
129
130 static x86emuu16
x86emu_mem_rdw(x86emuu32 addr)131 x86emu_mem_rdw(x86emuu32 addr)
132 {
133 return *(x86emuu16*)((addr_t)addr + sCurrentBIOSState->mapped_address);
134 }
135
136
137 static x86emuu32
x86emu_mem_rdl(x86emuu32 addr)138 x86emu_mem_rdl(x86emuu32 addr)
139 {
140 return *(x86emuu32*)((addr_t)addr + sCurrentBIOSState->mapped_address);
141 }
142
143
144 static void
x86emu_mem_wrb(x86emuu32 addr,x86emuu8 val)145 x86emu_mem_wrb(x86emuu32 addr, x86emuu8 val)
146 {
147 *(x86emuu8*)((addr_t)addr + sCurrentBIOSState->mapped_address) = val;
148 }
149
150
151 static void
x86emu_mem_wrw(x86emuu32 addr,x86emuu16 val)152 x86emu_mem_wrw(x86emuu32 addr, x86emuu16 val)
153 {
154 *(x86emuu16*)((addr_t)addr + sCurrentBIOSState->mapped_address) = val;
155 }
156
157
158 static void
x86emu_mem_wrl(x86emuu32 addr,x86emuu32 val)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*
bios_allocate_mem(bios_state * state,size_t size)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
bios_physical_address(bios_state * state,void * virtualAddress)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*
bios_virtual_address(bios_state * state,uint32 physicalAddress)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
bios_prepare(bios_state ** _state)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
bios_interrupt(bios_state * state,uint8 vector,bios_regs * regs)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
bios_finish(bios_state * state)329 bios_finish(bios_state* state)
330 {
331 release_sem(sBIOSLock);
332
333 delete state;
334 }
335
336
337 static status_t
std_ops(int32 op,...)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