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