1 /*
2 * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include "vesa_private.h"
8 #include "vesa.h"
9
10 #include <boot_item.h>
11 #include <driver_settings.h>
12 #include <frame_buffer_console.h>
13 #include <util/kernel_cpp.h>
14 #include <vm/vm.h>
15
16 #include "driver.h"
17 #include "utility.h"
18 #include "vesa_info.h"
19
20
21 #define LINEAR_ADDRESS(segment, offset) \
22 (((addr_t)(segment) << 4) + (addr_t)(offset))
23 #define SEGMENTED_TO_LINEAR(segmented) \
24 LINEAR_ADDRESS((addr_t)(segmented) >> 16, (addr_t)(segmented) & 0xffff)
25
26
27 bios_module_info* sBIOSModule;
28
29
30 /*! Loads the BIOS module and sets up a state for it. The BIOS module is only
31 loaded when we need it, as it is quite a large module.
32 */
33 status_t
vbe_call_prepare(bios_state ** state)34 vbe_call_prepare(bios_state** state)
35 {
36 status_t status;
37
38 status = get_module(B_BIOS_MODULE_NAME, (module_info**)&sBIOSModule);
39 if (status != B_OK) {
40 dprintf(DEVICE_NAME ": failed to get BIOS module: %s\n",
41 strerror(status));
42 return status;
43 }
44
45 status = sBIOSModule->prepare(state);
46 if (status != B_OK) {
47 dprintf(DEVICE_NAME ": failed to prepare BIOS state: %s\n",
48 strerror(status));
49 put_module(B_BIOS_MODULE_NAME);
50 }
51
52 return status;
53 }
54
55
56 void
vbe_call_finish(bios_state * state)57 vbe_call_finish(bios_state* state)
58 {
59 sBIOSModule->finish(state);
60 put_module(B_BIOS_MODULE_NAME);
61 }
62
63
64 static status_t
find_graphics_card(addr_t frameBuffer,addr_t & base,size_t & size)65 find_graphics_card(addr_t frameBuffer, addr_t& base, size_t& size)
66 {
67 // TODO: when we port this over to the new driver API, this mechanism can be
68 // used to find the right device_node
69 pci_module_info* pci;
70 if (get_module(B_PCI_MODULE_NAME, (module_info**)&pci) != B_OK)
71 return B_ERROR;
72
73 pci_info info;
74 for (int32 index = 0; pci->get_nth_pci_info(index, &info) == B_OK; index++) {
75 if (info.class_base != PCI_display)
76 continue;
77
78 // check PCI BARs
79 for (uint32 i = 0; i < 6; i++) {
80 if (info.u.h0.base_registers[i] <= frameBuffer
81 && info.u.h0.base_registers[i] + info.u.h0.base_register_sizes[i]
82 > frameBuffer) {
83 // found it!
84 base = info.u.h0.base_registers[i];
85 size = info.u.h0.base_register_sizes[i];
86
87 put_module(B_PCI_MODULE_NAME);
88 return B_OK;
89 }
90 }
91 }
92
93 put_module(B_PCI_MODULE_NAME);
94 return B_ENTRY_NOT_FOUND;
95 }
96
97
98 uint32
get_color_space_for_depth(uint32 depth)99 get_color_space_for_depth(uint32 depth)
100 {
101 switch (depth) {
102 case 1:
103 return B_GRAY1;
104 case 4:
105 return B_GRAY8;
106 // the app_server is smart enough to translate this to VGA mode
107 case 8:
108 return B_CMAP8;
109 case 15:
110 return B_RGB15;
111 case 16:
112 return B_RGB16;
113 case 24:
114 return B_RGB24;
115 case 32:
116 return B_RGB32;
117 }
118
119 return 0;
120 }
121
122
123 status_t
vbe_get_mode_info(bios_state * state,uint16 mode,struct vbe_mode_info * modeInfo)124 vbe_get_mode_info(bios_state* state, uint16 mode, struct vbe_mode_info* modeInfo)
125 {
126 void* vbeModeInfo = sBIOSModule->allocate_mem(state,
127 sizeof(struct vbe_mode_info));
128 if (vbeModeInfo == NULL)
129 return B_NO_MEMORY;
130 memset(vbeModeInfo, 0, sizeof(vbe_mode_info));
131
132 uint32 physicalAddress = sBIOSModule->physical_address(state, vbeModeInfo);
133 bios_regs regs = {};
134 regs.eax = 0x4f01;
135 regs.ecx = mode;
136 regs.es = physicalAddress >> 4;
137 regs.edi = physicalAddress - (regs.es << 4);
138
139 status_t status = sBIOSModule->interrupt(state, 0x10, ®s);
140 if (status != B_OK) {
141 dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): BIOS failed: %s\n", mode,
142 strerror(status));
143 return status;
144 }
145
146 if ((regs.eax & 0xffff) != 0x4f) {
147 dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): BIOS returned "
148 "0x%04" B_PRIx32 "\n", mode, regs.eax & 0xffff);
149 return B_ENTRY_NOT_FOUND;
150 }
151
152 memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info));
153 return B_OK;
154 }
155
156
157 status_t
vbe_set_mode(bios_state * state,uint16 mode)158 vbe_set_mode(bios_state* state, uint16 mode)
159 {
160 bios_regs regs = {};
161 regs.eax = 0x4f02;
162 regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
163
164 status_t status = sBIOSModule->interrupt(state, 0x10, ®s);
165 if (status != B_OK) {
166 dprintf(DEVICE_NAME ": vbe_set_mode(%u): BIOS failed: %s\n", mode,
167 strerror(status));
168 return status;
169 }
170
171 if ((regs.eax & 0xffff) != 0x4f) {
172 dprintf(DEVICE_NAME ": vbe_set_mode(%u): BIOS returned 0x%04" B_PRIx32
173 "\n", mode, regs.eax & 0xffff);
174 return B_ERROR;
175 }
176
177 return B_OK;
178 }
179
180
181 static uint32
vbe_to_system_dpms(uint8 vbeMode)182 vbe_to_system_dpms(uint8 vbeMode)
183 {
184 uint32 mode = 0;
185 if ((vbeMode & (DPMS_OFF | DPMS_REDUCED_ON)) != 0)
186 mode |= B_DPMS_OFF;
187 if ((vbeMode & DPMS_STANDBY) != 0)
188 mode |= B_DPMS_STAND_BY;
189 if ((vbeMode & DPMS_SUSPEND) != 0)
190 mode |= B_DPMS_SUSPEND;
191
192 return mode;
193 }
194
195
196 static status_t
vbe_get_dpms_capabilities(bios_state * state,uint32 & vbeMode,uint32 & mode)197 vbe_get_dpms_capabilities(bios_state* state, uint32& vbeMode, uint32& mode)
198 {
199 // we always return a valid mode
200 vbeMode = 0;
201 mode = B_DPMS_ON;
202
203 bios_regs regs = {};
204 regs.eax = 0x4f10;
205 regs.ebx = 0;
206 regs.esi = 0;
207 regs.edi = 0;
208
209 status_t status = sBIOSModule->interrupt(state, 0x10, ®s);
210 if (status != B_OK) {
211 dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS failed: %s\n",
212 strerror(status));
213 return status;
214 }
215
216 if ((regs.eax & 0xffff) != 0x4f) {
217 dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS returned "
218 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
219 return B_ERROR;
220 }
221
222 vbeMode = regs.ebx >> 8;
223 mode = vbe_to_system_dpms(vbeMode);
224 return status;
225 }
226
227
228 status_t
vbe_set_bits_per_gun(bios_state * state,vesa_info & info,uint8 bits)229 vbe_set_bits_per_gun(bios_state* state, vesa_info& info, uint8 bits)
230 {
231 info.bits_per_gun = 6;
232
233 bios_regs regs = {};
234 regs.eax = 0x4f08;
235 regs.ebx = (bits << 8) | 1;
236
237 status_t status = sBIOSModule->interrupt(state, 0x10, ®s);
238 if (status != B_OK) {
239 dprintf(DEVICE_NAME ": vbe_set_bits_per_gun(): BIOS failed: %s\n",
240 strerror(status));
241 return status;
242 }
243
244 if ((regs.eax & 0xffff) != 0x4f) {
245 dprintf(DEVICE_NAME ": vbe_set_bits_per_gun(): BIOS returned "
246 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
247 return B_ERROR;
248 }
249
250 info.bits_per_gun = regs.ebx >> 8;
251 return B_OK;
252 }
253
254
255 // We used to read the existing mode set by the bootsplash to avoid setting the same mode
256 // when entering app_server, but this has been disabled. So now there is always a modeset
257 // done when app_server starts.
258 #if 0
259 static status_t
260 vbe_get_vesa_info(bios_state* state, vesa_info& info)
261 {
262 vbe_info_block* infoHeader = (vbe_info_block*)sBIOSModule->allocate_mem(state, 256);
263 phys_addr_t physicalAddress = sBIOSModule->physical_address(state, infoHeader);
264
265 bios_regs regs = {};
266 regs.eax = 0x4f00;
267 regs.es = physicalAddress >> 4;
268 regs.edi = physicalAddress - (regs.es << 4);
269
270 status_t status = sBIOSModule->interrupt(state, 0x10, ®s);
271 if (status != B_OK) {
272 dprintf(DEVICE_NAME ": vbe_get_vesa_info(): BIOS failed: %s\n",
273 strerror(status));
274 return status;
275 }
276
277 if ((regs.eax & 0xffff) != 0x4f) {
278 dprintf(DEVICE_NAME ": vbe_get_vesa_info(): BIOS returned "
279 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
280 return B_ERROR;
281 }
282
283 info.shared_info->vram_size = infoHeader->total_memory * 65536;
284 strlcpy(info.shared_info->name, (char*)sBIOSModule->virtual_address(state,
285 SEGMENTED_TO_LINEAR(infoHeader->oem_string)), 32);
286
287 return status;
288 }
289 #endif
290
291
292 /*! Remaps the frame buffer if necessary; if we've already mapped the complete
293 frame buffer, there is no need to map it again.
294 */
295 status_t
remap_frame_buffer(vesa_info & info,addr_t physicalBase,uint32 width,uint32 height,int8 depth,uint32 bytesPerRow,bool initializing)296 remap_frame_buffer(vesa_info& info, addr_t physicalBase, uint32 width,
297 uint32 height, int8 depth, uint32 bytesPerRow, bool initializing)
298 {
299 vesa_shared_info& sharedInfo = *info.shared_info;
300 addr_t frameBuffer = info.frame_buffer;
301
302 if (!info.complete_frame_buffer_mapped) {
303 addr_t base = physicalBase;
304 size_t size = bytesPerRow * height;
305 bool remap = !initializing;
306
307 if (info.physical_frame_buffer_size != 0) {
308 // we can map the complete frame buffer
309 base = info.physical_frame_buffer;
310 size = info.physical_frame_buffer_size;
311 remap = true;
312 }
313
314 if (remap) {
315 area_id area = map_physical_memory("vesa frame buffer", base,
316 size, B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA,
317 (void**)&frameBuffer);
318 if (area < 0)
319 return area;
320
321 if (initializing) {
322 // We need to manually update the kernel's frame buffer address,
323 // since this frame buffer remapping has not been issued by the
324 // app_server (which would otherwise take care of this)
325 frame_buffer_update(frameBuffer, width, height, depth,
326 bytesPerRow);
327 }
328
329 delete_area(info.shared_info->frame_buffer_area);
330
331 info.frame_buffer = frameBuffer;
332 sharedInfo.frame_buffer_area = area;
333
334 // Turn on write combining for the area
335 vm_set_area_memory_type(area, base, B_WRITE_COMBINING_MEMORY);
336
337 if (info.physical_frame_buffer_size != 0)
338 info.complete_frame_buffer_mapped = true;
339 }
340 }
341
342 if (info.complete_frame_buffer_mapped)
343 frameBuffer += physicalBase - info.physical_frame_buffer;
344
345 // Update shared frame buffer information
346 sharedInfo.frame_buffer = (uint8*)frameBuffer;
347 sharedInfo.physical_frame_buffer = (uint8*)physicalBase;
348 sharedInfo.bytes_per_row = bytesPerRow;
349
350 return B_OK;
351 }
352
353
354 // #pragma mark -
355
356
357 status_t
vesa_init(vesa_info & info)358 vesa_init(vesa_info& info)
359 {
360 frame_buffer_boot_info* bufferInfo
361 = (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
362 if (bufferInfo == NULL)
363 return B_ERROR;
364
365 info.vbe_capabilities = bufferInfo->vesa_capabilities;
366 info.complete_frame_buffer_mapped = false;
367
368 // Find out which PCI device we belong to, so that we know its frame buffer
369 // size
370 find_graphics_card(bufferInfo->physical_frame_buffer,
371 info.physical_frame_buffer, info.physical_frame_buffer_size);
372
373 size_t modesSize = 0;
374 vesa_mode* modes = (vesa_mode*)get_boot_item(VESA_MODES_BOOT_INFO,
375 &modesSize);
376 info.modes = modes;
377
378 size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
379
380 info.shared_area = create_area("vesa shared info",
381 (void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
382 ROUND_TO_PAGE_SIZE(sharedSize + modesSize), B_FULL_LOCK,
383 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
384 if (info.shared_area < 0)
385 return info.shared_area;
386
387 vesa_shared_info& sharedInfo = *info.shared_info;
388
389 memset(&sharedInfo, 0, sizeof(vesa_shared_info));
390
391 if (modes != NULL) {
392 sharedInfo.vesa_mode_offset = sharedSize;
393 sharedInfo.vesa_mode_count = modesSize / sizeof(vesa_mode);
394
395 memcpy((uint8*)&sharedInfo + sharedSize, modes, modesSize);
396 }
397
398 sharedInfo.frame_buffer_area = bufferInfo->area;
399
400 remap_frame_buffer(info, bufferInfo->physical_frame_buffer,
401 bufferInfo->width, bufferInfo->height, bufferInfo->depth,
402 bufferInfo->bytes_per_row, true);
403 // Does not matter if this fails - the frame buffer was already mapped
404 // before.
405
406 sharedInfo.current_mode.virtual_width = bufferInfo->width;
407 sharedInfo.current_mode.virtual_height = bufferInfo->height;
408 sharedInfo.current_mode.space = get_color_space_for_depth(
409 bufferInfo->depth);
410
411 edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
412 NULL);
413 if (edidInfo != NULL) {
414 sharedInfo.has_edid = true;
415 memcpy(&sharedInfo.edid_info, edidInfo, sizeof(edid1_info));
416 }
417
418 bios_state* state;
419 status_t status = vbe_call_prepare(&state);
420 if (status != B_OK)
421 return status;
422
423 void* settings = load_driver_settings("vesa");
424 bool patchingAllowed = false;
425 if (settings != NULL) {
426 patchingAllowed = get_driver_boolean_parameter(settings, "bios_patching",
427 patchingAllowed, true);
428 unload_driver_settings(settings);
429 }
430
431 if (patchingAllowed)
432 vesa_identify_bios(state, &sharedInfo);
433 else
434 sharedInfo.bios_type = kUnknownBiosType;
435
436 vbe_get_dpms_capabilities(state, info.vbe_dpms_capabilities,
437 sharedInfo.dpms_capabilities);
438 if (bufferInfo->depth <= 8)
439 vbe_set_bits_per_gun(state, info, 8);
440
441 vbe_call_finish(state);
442
443 dprintf(DEVICE_NAME ": vesa_init() completed successfully!\n");
444 return B_OK;
445 }
446
447
448 void
vesa_uninit(vesa_info & info)449 vesa_uninit(vesa_info& info)
450 {
451 dprintf(DEVICE_NAME": vesa_uninit()\n");
452
453 delete_area(info.shared_info->frame_buffer_area);
454 delete_area(info.shared_area);
455 }
456
457
458 status_t
vesa_set_display_mode(vesa_info & info,uint32 mode)459 vesa_set_display_mode(vesa_info& info, uint32 mode)
460 {
461 if (mode >= info.shared_info->vesa_mode_count)
462 return B_ENTRY_NOT_FOUND;
463
464 // Prepare BIOS environment
465 bios_state* state;
466 status_t status = vbe_call_prepare(&state);
467 if (status != B_OK)
468 return status;
469
470 // Get mode information
471 struct vbe_mode_info modeInfo;
472 status = vbe_get_mode_info(state, info.modes[mode].mode, &modeInfo);
473 if (status != B_OK) {
474 dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n");
475 goto out;
476 }
477
478 // Set mode
479 status = vbe_set_mode(state, info.modes[mode].mode);
480 if (status != B_OK) {
481 dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n");
482 goto out;
483 }
484
485 if (info.modes[mode].bits_per_pixel <= 8)
486 vbe_set_bits_per_gun(state, info, 8);
487
488 // Map new frame buffer if necessary
489
490 status = remap_frame_buffer(info, modeInfo.physical_base, modeInfo.width,
491 modeInfo.height, modeInfo.bits_per_pixel, modeInfo.bytes_per_row,
492 false);
493 if (status == B_OK) {
494 // Update shared frame buffer information
495 info.shared_info->current_mode.virtual_width = modeInfo.width;
496 info.shared_info->current_mode.virtual_height = modeInfo.height;
497 info.shared_info->current_mode.space = get_color_space_for_depth(
498 modeInfo.bits_per_pixel);
499 }
500
501 out:
502 vbe_call_finish(state);
503 return status;
504 }
505
506
507 status_t
vesa_get_dpms_mode(vesa_info & info,uint32 & mode)508 vesa_get_dpms_mode(vesa_info& info, uint32& mode)
509 {
510 mode = B_DPMS_ON;
511 // we always return a valid mode
512
513 // Prepare BIOS environment
514 bios_state* state;
515 status_t status = vbe_call_prepare(&state);
516 if (status != B_OK)
517 return status;
518
519 bios_regs regs = {};
520 regs.eax = 0x4f10;
521 regs.ebx = 2;
522 regs.esi = 0;
523 regs.edi = 0;
524
525 status = sBIOSModule->interrupt(state, 0x10, ®s);
526 if (status != B_OK) {
527 dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS failed: %s\n",
528 strerror(status));
529 goto out;
530 }
531
532 if ((regs.eax & 0xffff) != 0x4f) {
533 dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS returned "
534 "0x%" B_PRIx32 "\n", regs.eax & 0xffff);
535 status = B_ERROR;
536 goto out;
537 }
538
539 mode = vbe_to_system_dpms(regs.ebx >> 8);
540
541 out:
542 vbe_call_finish(state);
543 return status;
544 }
545
546
547 status_t
vesa_set_dpms_mode(vesa_info & info,uint32 mode)548 vesa_set_dpms_mode(vesa_info& info, uint32 mode)
549 {
550 // Only let supported modes through
551 mode &= info.shared_info->dpms_capabilities;
552
553 uint8 vbeMode = 0;
554 if ((mode & B_DPMS_OFF) != 0)
555 vbeMode |= DPMS_OFF | DPMS_REDUCED_ON;
556 if ((mode & B_DPMS_STAND_BY) != 0)
557 vbeMode |= DPMS_STANDBY;
558 if ((mode & B_DPMS_SUSPEND) != 0)
559 vbeMode |= DPMS_SUSPEND;
560
561 vbeMode &= info.vbe_dpms_capabilities;
562
563 // Prepare BIOS environment
564 bios_state* state;
565 status_t status = vbe_call_prepare(&state);
566 if (status != B_OK)
567 return status;
568
569 bios_regs regs = {};
570 regs.eax = 0x4f10;
571 regs.ebx = (vbeMode << 8) | 1;
572 regs.esi = 0;
573 regs.edi = 0;
574
575 status = sBIOSModule->interrupt(state, 0x10, ®s);
576 if (status != B_OK) {
577 dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS failed: %s\n",
578 strerror(status));
579 goto out;
580 }
581
582 if ((regs.eax & 0xffff) != 0x4f) {
583 dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS returned "
584 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
585 status = B_ERROR;
586 goto out;
587 }
588
589 out:
590 vbe_call_finish(state);
591 return status;
592 }
593
594
595 status_t
vesa_set_indexed_colors(vesa_info & info,uint8 first,uint8 * colors,uint16 count)596 vesa_set_indexed_colors(vesa_info& info, uint8 first, uint8* colors,
597 uint16 count)
598 {
599 bios_regs regs = {};
600 uint32 shift, physicalAddress;
601
602 if (first + count > 256)
603 count = 256 - first;
604
605 // Prepare BIOS environment
606 bios_state* state;
607 status_t status = vbe_call_prepare(&state);
608 if (status != B_OK)
609 return status;
610
611 uint8* palette = (uint8*)sBIOSModule->allocate_mem(state, 256 * 4);
612 if (palette == NULL) {
613 status = B_NO_MEMORY;
614 goto out;
615 }
616
617 shift = 8 - info.bits_per_gun;
618
619 // convert colors to VESA palette
620 for (int32 i = first; i < count; i++) {
621 uint8 color[3];
622 if (user_memcpy(color, &colors[i * 3], 3) < B_OK) {
623 status = B_BAD_ADDRESS;
624 goto out;
625 }
626
627 // order is BGR-
628 palette[i * 4 + 0] = color[2] >> shift;
629 palette[i * 4 + 1] = color[1] >> shift;
630 palette[i * 4 + 2] = color[0] >> shift;
631 palette[i * 4 + 3] = 0;
632 }
633
634 // set palette
635 physicalAddress = sBIOSModule->physical_address(state, palette);
636 regs.eax = 0x4f09;
637 regs.ebx = 0;
638 regs.ecx = count;
639 regs.edx = first;
640 regs.es = physicalAddress >> 4;
641 regs.edi = physicalAddress - (regs.es << 4);
642
643 status = sBIOSModule->interrupt(state, 0x10, ®s);
644 if (status != B_OK) {
645 dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS failed: %s\n",
646 strerror(status));
647 goto out;
648 }
649
650 if ((regs.eax & 0xffff) != 0x4f) {
651 dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS returned "
652 "0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
653 status = B_ERROR;
654 }
655
656 out:
657 vbe_call_finish(state);
658 return status;
659 }
660