vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / accelerants / intel_extreme / accelerant.cpp
blob37762aceaf923e0581c8ffd3c57abe76e9ebe706
1 /*
2 * Copyright 2006-2016, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 */
10 #include "accelerant_protos.h"
11 #include "accelerant.h"
13 #include "utility.h"
15 #include <Debug.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <syslog.h>
22 #include <new>
24 #include <AGP.h>
27 #undef TRACE
28 #define TRACE_ACCELERANT
29 #ifdef TRACE_ACCELERANT
30 # define TRACE(x...) _sPrintf("intel_extreme: " x)
31 #else
32 # define TRACE(x...)
33 #endif
35 #define ERROR(x...) _sPrintf("intel_extreme: " x)
36 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
39 struct accelerant_info* gInfo;
40 uint32 gDumpCount;
43 class AreaCloner {
44 public:
45 AreaCloner();
46 ~AreaCloner();
48 area_id Clone(const char* name, void** _address,
49 uint32 spec, uint32 protection,
50 area_id sourceArea);
51 status_t InitCheck()
52 { return fArea < 0 ? (status_t)fArea : B_OK; }
53 void Keep();
55 private:
56 area_id fArea;
60 AreaCloner::AreaCloner()
62 fArea(-1)
67 AreaCloner::~AreaCloner()
69 if (fArea >= 0)
70 delete_area(fArea);
74 area_id
75 AreaCloner::Clone(const char* name, void** _address, uint32 spec,
76 uint32 protection, area_id sourceArea)
78 fArea = clone_area(name, _address, spec, protection, sourceArea);
79 return fArea;
83 void
84 AreaCloner::Keep()
86 fArea = -1;
90 // #pragma mark -
93 // intel_reg --mmio=ie-0001.bin --devid=27a2 dump
94 void
95 dump_registers()
97 char filename[255];
99 sprintf(filename, "/boot/system/cache/tmp/ie-%04" B_PRId32 ".bin",
100 gDumpCount);
102 ERROR("%s: Taking register dump #%" B_PRId32 "\n", __func__, gDumpCount);
104 int fd = open(filename, O_CREAT | O_WRONLY, 0644);
105 uint32 data = 0;
106 if (fd >= 0) {
107 for (int32 i = 0; i < 0x80000; i += sizeof(data)) {
108 //char line[512];
109 //int length = sprintf(line, "%05" B_PRIx32 ": "
110 // "%08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 " %08" B_PRIx32 "\n",
111 // i, read32(i), read32(i + 4), read32(i + 8), read32(i + 12));
112 data = read32(i);
113 write(fd, &data, sizeof(data));
115 close(fd);
116 sync();
119 gDumpCount++;
123 /*! This is the common accelerant_info initializer. It is called by
124 both, the first accelerant and all clones.
126 static status_t
127 init_common(int device, bool isClone)
129 // initialize global accelerant info structure
131 // Number of register dumps we have... taken.
132 gDumpCount = 0;
134 gInfo = (accelerant_info*)malloc(sizeof(accelerant_info));
135 if (gInfo == NULL)
136 return B_NO_MEMORY;
138 memset(gInfo, 0, sizeof(accelerant_info));
140 gInfo->is_clone = isClone;
141 gInfo->device = device;
143 // get basic info from driver
145 intel_get_private_data data;
146 data.magic = INTEL_PRIVATE_DATA_MAGIC;
148 if (ioctl(device, INTEL_GET_PRIVATE_DATA, &data,
149 sizeof(intel_get_private_data)) != 0) {
150 free(gInfo);
151 return B_ERROR;
154 AreaCloner sharedCloner;
155 gInfo->shared_info_area = sharedCloner.Clone("intel extreme shared info",
156 (void**)&gInfo->shared_info, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA,
157 data.shared_info_area);
158 status_t status = sharedCloner.InitCheck();
159 if (status < B_OK) {
160 free(gInfo);
161 return status;
164 AreaCloner regsCloner;
165 gInfo->regs_area = regsCloner.Clone("intel extreme regs",
166 (void**)&gInfo->registers, B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA,
167 gInfo->shared_info->registers_area);
168 status = regsCloner.InitCheck();
169 if (status < B_OK) {
170 free(gInfo);
171 return status;
174 sharedCloner.Keep();
175 regsCloner.Keep();
177 // The overlay registers, hardware status, and cursor memory share
178 // a single area with the shared_info
180 if (gInfo->shared_info->overlay_offset != 0) {
181 gInfo->overlay_registers = (struct overlay_registers*)
182 (gInfo->shared_info->graphics_memory
183 + gInfo->shared_info->overlay_offset);
186 if (gInfo->shared_info->device_type.InGroup(INTEL_GROUP_96x)) {
187 // allocate some extra memory for the 3D context
188 if (intel_allocate_memory(INTEL_i965_3D_CONTEXT_SIZE,
189 B_APERTURE_NON_RESERVED, gInfo->context_base) == B_OK) {
190 gInfo->context_offset = gInfo->context_base
191 - (addr_t)gInfo->shared_info->graphics_memory;
195 gInfo->pipe_count = 0;
197 // Allocate all of our pipes
198 for (int i = 0; i < MAX_PIPES; i++) {
199 switch (i) {
200 case 0:
201 gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_A);
202 break;
203 case 1:
204 gInfo->pipes[i] = new(std::nothrow) Pipe(INTEL_PIPE_B);
205 break;
206 default:
207 ERROR("%s: Unknown pipe %d\n", __func__, i);
209 if (gInfo->pipes[i] == NULL)
210 ERROR("%s: Error allocating pipe %d\n", __func__, i);
211 else
212 gInfo->pipe_count++;
215 return B_OK;
219 /*! Clean up data common to both primary and cloned accelerant */
220 static void
221 uninit_common(void)
223 intel_free_memory(gInfo->context_base);
225 delete_area(gInfo->regs_area);
226 delete_area(gInfo->shared_info_area);
228 gInfo->regs_area = gInfo->shared_info_area = -1;
230 // close the file handle ONLY if we're the clone
231 if (gInfo->is_clone)
232 close(gInfo->device);
234 free(gInfo);
238 static void
239 dump_ports()
241 if (gInfo->port_count == 0) {
242 TRACE("%s: No ports connected\n", __func__);
243 return;
246 TRACE("%s: Connected ports: (port_count: %" B_PRIu32 ")\n", __func__,
247 gInfo->port_count);
249 for (uint32 i = 0; i < gInfo->port_count; i++) {
250 Port* port = gInfo->ports[i];
251 if (!port) {
252 TRACE("port %" B_PRIu32 ":: INVALID ALLOC!\n", i);
253 continue;
255 TRACE("port %" B_PRIu32 ": %s %s\n", i, port->PortName(),
256 port->IsConnected() ? "connected" : "disconnected");
261 static bool
262 has_connected_port(port_index portIndex, uint32 type)
264 for (uint32 i = 0; i < gInfo->port_count; i++) {
265 Port* port = gInfo->ports[i];
266 if (type != INTEL_PORT_TYPE_ANY && port->Type() != type)
267 continue;
268 if (portIndex != INTEL_PORT_ANY && port->PortIndex() != portIndex)
269 continue;
271 return true;
274 return false;
278 static status_t
279 probe_ports()
281 // Try to determine what ports to use. We use the following heuristic:
282 // * Check for DisplayPort, these can be more or less detected reliably.
283 // * Check for HDMI, it'll fail on devices not having HDMI for us to fall
284 // back to DVI.
285 // * Assume DVI B if no HDMI and no DisplayPort is present, confirmed by
286 // reading EDID in the IsConnected() call.
287 // * Check for analog if possible (there's a detection bit on PCH),
288 // otherwise the assumed presence is confirmed by reading EDID in
289 // IsConnected().
291 TRACE("adpa: %08" B_PRIx32 "\n", read32(INTEL_ANALOG_PORT));
292 TRACE("dova: %08" B_PRIx32 ", dovb: %08" B_PRIx32
293 ", dovc: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_PORT_A),
294 read32(INTEL_DIGITAL_PORT_B), read32(INTEL_DIGITAL_PORT_C));
295 TRACE("lvds: %08" B_PRIx32 "\n", read32(INTEL_DIGITAL_LVDS_PORT));
297 bool foundLVDS = false;
299 gInfo->port_count = 0;
300 for (int i = INTEL_PORT_A; i <= INTEL_PORT_D; i++) {
301 Port* displayPort = new(std::nothrow) DisplayPort((port_index)i);
302 if (displayPort == NULL)
303 return B_NO_MEMORY;
305 if (displayPort->IsConnected())
306 gInfo->ports[gInfo->port_count++] = displayPort;
307 else
308 delete displayPort;
311 // Digital Display Interface
312 if (gInfo->shared_info->device_type.HasDDI()) {
313 for (int i = INTEL_PORT_A; i <= INTEL_PORT_E; i++) {
314 Port* ddiPort
315 = new(std::nothrow) DigitalDisplayInterface((port_index)i);
317 if (ddiPort == NULL)
318 return B_NO_MEMORY;
320 if (ddiPort->IsConnected())
321 gInfo->ports[gInfo->port_count++] = ddiPort;
322 else
323 delete ddiPort;
327 // Ensure DP_A isn't already taken (or DDI)
328 if (!has_connected_port((port_index)INTEL_PORT_A, INTEL_PORT_TYPE_ANY)) {
329 // also always try eDP, it'll also just fail if not applicable
330 Port* eDPPort = new(std::nothrow) EmbeddedDisplayPort();
331 if (eDPPort == NULL)
332 return B_NO_MEMORY;
333 if (eDPPort->IsConnected())
334 gInfo->ports[gInfo->port_count++] = eDPPort;
335 else
336 delete eDPPort;
339 for (int i = INTEL_PORT_B; i <= INTEL_PORT_D; i++) {
340 if (has_connected_port((port_index)i, INTEL_PORT_TYPE_ANY)) {
341 // Ensure port not already claimed by something like DDI
342 continue;
345 Port* hdmiPort = new(std::nothrow) HDMIPort((port_index)i);
346 if (hdmiPort == NULL)
347 return B_NO_MEMORY;
349 if (hdmiPort->IsConnected())
350 gInfo->ports[gInfo->port_count++] = hdmiPort;
351 else
352 delete hdmiPort;
355 if (!has_connected_port(INTEL_PORT_ANY, INTEL_PORT_TYPE_ANY)) {
356 // there's neither DisplayPort nor HDMI so far, assume DVI B
357 Port* dviPort = new(std::nothrow) DigitalPort(INTEL_PORT_B);
358 if (dviPort == NULL)
359 return B_NO_MEMORY;
361 if (dviPort->IsConnected()) {
362 gInfo->ports[gInfo->port_count++] = dviPort;
363 gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
364 } else
365 delete dviPort;
368 // always try the LVDS port, it'll simply fail if not applicable
369 Port* lvdsPort = new(std::nothrow) LVDSPort();
370 if (lvdsPort == NULL)
371 return B_NO_MEMORY;
372 if (lvdsPort->IsConnected()) {
373 foundLVDS = true;
374 gInfo->ports[gInfo->port_count++] = lvdsPort;
375 gInfo->head_mode |= HEAD_MODE_LVDS_PANEL;
376 gInfo->head_mode |= HEAD_MODE_A_ANALOG;
377 // FIXME this should not be set, but without it, LVDS modesetting
378 // doesn't work on SandyBridge. Find out why it makes a difference.
379 gInfo->head_mode |= HEAD_MODE_B_DIGITAL;
380 } else
381 delete lvdsPort;
383 // then finally always try the analog port
384 Port* analogPort = new(std::nothrow) AnalogPort();
385 if (analogPort == NULL)
386 return B_NO_MEMORY;
387 if (analogPort->IsConnected()) {
388 gInfo->ports[gInfo->port_count++] = analogPort;
389 gInfo->head_mode |= HEAD_MODE_A_ANALOG;
390 } else
391 delete analogPort;
393 if (gInfo->port_count == 0)
394 return B_ERROR;
396 // Activate reference clocks if needed
397 if (gInfo->shared_info->pch_info == INTEL_PCH_IBX
398 || gInfo->shared_info->pch_info == INTEL_PCH_CPT) {
399 // XXX: Is LVDS the same as Panel?
400 refclk_activate_ilk(foundLVDS);
403 } else if (gInfo->shared_info->pch_info == INTEL_PCH_LPT) {
404 // TODO: Some kind of stepped bend thing?
405 // only needed for vga
406 refclk_activate_lpt(foundLVDS);
410 return B_OK;
414 static status_t
415 assign_pipes()
417 // TODO: At some point we should "group" ports to pipes with the same mode.
418 // You can drive multiple ports from a single pipe as long as the mode is
419 // the same. For the moment we could get displays with the wrong pipes
420 // assigned when the count is > 1;
422 uint32 current = 0;
423 for (uint32 i = 0; i < gInfo->port_count; i++) {
424 if (gInfo->ports[i] == NULL)
425 continue;
427 pipe_index preference = gInfo->ports[i]->PipePreference();
428 if (preference != INTEL_PIPE_ANY) {
429 // Some ports *really* need to be assigned a pipe due to
430 // implementation bugs.
431 int index = (preference == INTEL_PIPE_B) ? 1 : 0;
432 gInfo->ports[i]->SetPipe(gInfo->pipes[index]);
433 continue;
436 if (gInfo->ports[i]->IsConnected()) {
437 if (current >= gInfo->pipe_count) {
438 ERROR("%s: No pipes left to assign to port %s!\n", __func__,
439 gInfo->ports[i]->PortName());
440 continue;
443 gInfo->ports[i]->SetPipe(gInfo->pipes[current]);
444 current++;
448 return B_OK;
452 // #pragma mark - public accelerant functions
455 /*! Init primary accelerant */
456 status_t
457 intel_init_accelerant(int device)
459 CALLED();
461 status_t status = init_common(device, false);
462 if (status != B_OK)
463 return status;
465 intel_shared_info &info = *gInfo->shared_info;
467 init_lock(&info.accelerant_lock, "intel extreme accelerant");
468 init_lock(&info.engine_lock, "intel extreme engine");
470 setup_ring_buffer(info.primary_ring_buffer, "intel primary ring buffer");
472 TRACE("pipe control for: 0x%" B_PRIx32 " 0x%" B_PRIx32 "\n",
473 read32(INTEL_PIPE_CONTROL), read32(INTEL_PIPE_CONTROL));
475 // Probe all ports
476 status = probe_ports();
478 // On TRACE, dump ports and states
479 dump_ports();
481 if (status != B_OK)
482 ERROR("Warning: zero active displays were found!\n");
484 status = assign_pipes();
486 if (status != B_OK)
487 ERROR("Warning: error while assigning pipes!\n");
489 status = create_mode_list();
490 if (status != B_OK) {
491 uninit_common();
492 return status;
495 return B_OK;
499 ssize_t
500 intel_accelerant_clone_info_size(void)
502 CALLED();
503 // clone info is device name, so return its maximum size
504 return B_PATH_NAME_LENGTH;
508 void
509 intel_get_accelerant_clone_info(void* info)
511 CALLED();
512 ioctl(gInfo->device, INTEL_GET_DEVICE_NAME, info, B_PATH_NAME_LENGTH);
516 status_t
517 intel_clone_accelerant(void* info)
519 CALLED();
521 // create full device name
522 char path[B_PATH_NAME_LENGTH];
523 strcpy(path, "/dev/");
524 #ifdef __HAIKU__
525 strlcat(path, (const char*)info, sizeof(path));
526 #else
527 strcat(path, (const char*)info);
528 #endif
530 int fd = open(path, B_READ_WRITE);
531 if (fd < 0)
532 return errno;
534 status_t status = init_common(fd, true);
535 if (status != B_OK)
536 goto err1;
538 // get read-only clone of supported display modes
539 status = gInfo->mode_list_area = clone_area(
540 "intel extreme cloned modes", (void**)&gInfo->mode_list,
541 B_ANY_ADDRESS, B_READ_AREA, gInfo->shared_info->mode_list_area);
542 if (status < B_OK)
543 goto err2;
545 return B_OK;
547 err2:
548 uninit_common();
549 err1:
550 close(fd);
551 return status;
555 /*! This function is called for both, the primary accelerant and all of
556 its clones.
558 void
559 intel_uninit_accelerant(void)
561 CALLED();
563 // delete accelerant instance data
564 delete_area(gInfo->mode_list_area);
565 gInfo->mode_list = NULL;
567 intel_shared_info &info = *gInfo->shared_info;
569 uninit_lock(&info.accelerant_lock);
570 uninit_lock(&info.engine_lock);
572 uninit_ring_buffer(info.primary_ring_buffer);
574 uninit_common();
578 status_t
579 intel_get_accelerant_device_info(accelerant_device_info* info)
581 CALLED();
583 info->version = B_ACCELERANT_VERSION;
585 DeviceType* type = &gInfo->shared_info->device_type;
587 if (type->InFamily(INTEL_FAMILY_7xx) || type->InFamily(INTEL_FAMILY_8xx))
588 strcpy(info->name, "Intel Extreme");
589 else if (type->InFamily(INTEL_FAMILY_9xx))
590 strcpy(info->name, "Intel GMA");
591 else if (type->InFamily(INTEL_FAMILY_POVR))
592 strcpy(info->name, "Intel PowerVR");
593 else if (type->InFamily(INTEL_FAMILY_SOC0))
594 strcpy(info->name, "Intel Atom");
595 else if (type->InFamily(INTEL_FAMILY_SER5))
596 strcpy(info->name, "Intel HD/Iris");
597 else
598 strcpy(info->name, "Intel");
600 strcpy(info->chipset, gInfo->shared_info->device_identifier);
601 strcpy(info->serial_no, "None");
603 info->memory = gInfo->shared_info->graphics_memory_size;
604 info->dac_speed = gInfo->shared_info->pll_info.max_frequency;
606 return B_OK;
610 sem_id
611 intel_accelerant_retrace_semaphore()
613 CALLED();
614 return gInfo->shared_info->vblank_sem;