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