The Pedigree Project  0.1
vbe.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "VbeDisplay.h"
21 #include "modules/Module.h"
22 #include "modules/system/config/Config.h"
23 #include "pedigree/kernel/Log.h"
24 #include "pedigree/kernel/Service.h"
25 #include "pedigree/kernel/ServiceFeatures.h"
26 #include "pedigree/kernel/ServiceManager.h"
27 #include "pedigree/kernel/graphics/GraphicsService.h"
28 #include "pedigree/kernel/machine/Device.h"
29 #include "pedigree/kernel/machine/Display.h"
30 #include "pedigree/kernel/machine/Framebuffer.h"
31 #include "pedigree/kernel/machine/x86_common/Bios.h"
32 #include "pedigree/kernel/processor/MemoryRegion.h"
33 #include "pedigree/kernel/processor/PhysicalMemoryManager.h"
34 #include "pedigree/kernel/processor/VirtualAddressSpace.h"
35 #include "pedigree/kernel/processor/types.h"
36 #include "pedigree/kernel/utilities/Iterator.h"
37 #include "pedigree/kernel/utilities/List.h"
38 #include "pedigree/kernel/utilities/String.h"
39 #include "pedigree/kernel/utilities/Vector.h"
40 #include "pedigree/kernel/utilities/utility.h"
41 
42 #ifdef CRIPPLE_HDD
43 #pragma GCC diagnostic ignored "-Wunreachable-code"
44 #endif
45 
46 extern "C" void vbeModeChangedCallback(char *pId, char *pModeId);
47 
48 #define REALMODE_PTR(x) ((x[1] << 4) + x[0])
49 
50 static VbeDisplay *g_pDisplays[4];
51 static size_t g_nDisplays = 0;
52 
54 {
55  char signature[4]; // == "VESA"
56  short version; // == 0x0300 for VBE 3.0
57  short oemString[2]; // isa vbeFarPtr
58  unsigned char capabilities[4];
59  unsigned short videomodes[2]; // isa vbeFarPtr
60  short totalMemory; // as # of 64KB blocks
61 } __attribute__((packed));
62 
64 {
65  short attributes;
66  char winA, winB;
67  short granularity;
68  short winsize;
69  short segmentA, segmentB;
70  unsigned short realFctPtr[2];
71  short pitch; // chars per scanline
72 
73  unsigned short Xres, Yres;
74  unsigned char Wchar, Ychar, planes, bpp, banks;
75  uint8_t memory_model, bank_size, image_pages;
76  char reserved0;
77 
78  char red_mask, red_position;
79  char green_mask, green_position;
80  char blue_mask, blue_position;
81  char rsv_mask, rsv_position;
82  char directcolor_attributes;
83 
84  // --- VBE 2.0 ---
85  unsigned int framebuffer;
86  unsigned int offscreen;
87  short sz_offscreen; // In KB.
88 } __attribute__((packed));
89 
91 {
92  public:
94  VbeFramebuffer(Display *pDisplay);
95  virtual ~VbeFramebuffer();
96 
97  virtual void hwRedraw(
98  size_t x = ~0UL, size_t y = ~0UL, size_t w = ~0UL, size_t h = ~0UL);
99  virtual void setFramebuffer(uintptr_t p);
100 
101  private:
102  Display *m_pDisplay;
103  char *m_pBackbuffer;
104  size_t m_nBackbufferBytes;
105 
106  MemoryRegion *m_pFramebufferRegion;
107 
108  Display::ScreenMode m_Mode;
109 };
110 
111 extern "C" void vbeModeChangedCallback(char *pId, char *pModeId)
112 {
113  size_t id = StringToUnsignedLong(pId, 0, 10);
114  size_t mode_id = StringToUnsignedLong(pModeId, 0, 10);
115 
116  if (id >= g_nDisplays)
117  return;
118 
119  if (g_pDisplays[id]->getModeId() != mode_id)
120  {
121  g_pDisplays[id]->setScreenMode(mode_id);
122  }
123 }
124 
125 static bool entry()
126 {
127 #ifdef NOGFX
128  NOTICE("Not starting VBE module, NOGFX is defined.");
129  return false;
130 #endif
131 
133 
134  // Allocate some space for the information structure and prepare for a BIOS
135  // call.
136  vbeControllerInfo *info = reinterpret_cast<vbeControllerInfo *>(
137  Bios::instance().malloc(/*sizeof(vbeControllerInfo)*/ 256));
138  vbeModeInfo *mode = reinterpret_cast<vbeModeInfo *>(
139  Bios::instance().malloc(/*sizeof(vbeModeInfo)*/ 256));
140  QuadWordSet(info, 0, 256 / 8);
141  QuadWordSet(mode, 0, 256 / 8);
142  StringCopyN(info->signature, "VBE2", 4);
143  Bios::instance().setAx(0x4F00);
144  Bios::instance().setEs(0x0000);
146  static_cast<uint16_t>(reinterpret_cast<uintptr_t>(info) & 0xFFFF));
147 
148  uint16_t ax = Bios::instance().getAx();
149  uint16_t bx = Bios::instance().getBx();
150  uint16_t cx = Bios::instance().getCx();
151  uint16_t dx = Bios::instance().getDx();
152  uint16_t di = Bios::instance().getDi();
153  NOTICE("abcd: " << Hex << ax << ", " << bx << ", " << cx << ", " << dx);
154  NOTICE("di: " << Hex << di);
155 
157 
158  // Check the signature.
159  ax = Bios::instance().getAx();
160  bx = Bios::instance().getBx();
161  cx = Bios::instance().getCx();
162  dx = Bios::instance().getDx();
163  di = Bios::instance().getDi();
164  if (ax != 0x004F || StringCompareN(info->signature, "VESA", 4) != 0)
165  {
166  ERROR(
167  "VBE: VESA not supported (ax=" << Hex << ax << ", signature="
168  << info->signature << ")!");
169  NOTICE("abcd: " << Hex << ax << ", " << bx << ", " << cx << ", " << dx);
170  NOTICE("di: " << Hex << di);
171  Bios::instance().free(reinterpret_cast<uintptr_t>(info));
172  Bios::instance().free(reinterpret_cast<uintptr_t>(mode));
173  return false;
174  }
175 
176  VbeDisplay::VbeVersion vbeVersion;
177  switch (info->version)
178  {
179  case 0x0102:
180  vbeVersion = VbeDisplay::Vbe1_2;
181  break;
182  case 0x0200:
183  vbeVersion = VbeDisplay::Vbe2_0;
184  break;
185  case 0x0300:
186  vbeVersion = VbeDisplay::Vbe3_0;
187  break;
188  default:
189  ERROR("VBE: Unrecognised VESA version: " << Hex << info->version);
190  Bios::instance().free(reinterpret_cast<uintptr_t>(info));
191  Bios::instance().free(reinterpret_cast<uintptr_t>(mode));
192  return false;
193  }
194 
195  size_t maxWidth = 0;
196  size_t maxHeight = 0;
197  size_t maxBpp = 0;
198 
199  size_t maxTextWidth = 0;
200  size_t maxTextHeight = 0;
201 
202  uintptr_t fbAddr = 0;
203  uint16_t *modes =
204  reinterpret_cast<uint16_t *>(REALMODE_PTR(info->videomodes));
205  for (int i = 0; modes[i] != 0xFFFF; i++)
206  {
207  Bios::instance().setAx(0x4F01);
208  Bios::instance().setCx(modes[i]);
209  Bios::instance().setEs(0x0000);
211  static_cast<uint16_t>(reinterpret_cast<uintptr_t>(mode) & 0xFFFF));
212 
214 
215  ax = Bios::instance().getAx();
216  if (ax != 0x004F)
217  {
218  WARNING(
219  "Testing for mode " << Hex << modes[i] << " failed, ax=" << ax);
220  continue;
221  }
222 
223  // graphics/text mode bit
224  bool isGraphicsMode = mode->attributes & 0x10;
225  bool hasLFB = mode->attributes & 0x80;
226 
227  if (isGraphicsMode)
228  {
229  // We only want graphics modes with LFB support.
230  if (!hasLFB)
231  {
232  continue;
233  }
234  // Check if this is a packed pixel or direct colour mode.
235  else if (mode->memory_model != 4 && mode->memory_model != 6)
236  {
237  continue;
238  }
239  }
240 
241  // Add this pixel mode.
243  pSm->id = modes[i];
244  pSm->width = mode->Xres;
245  pSm->height = mode->Yres;
246  pSm->refresh = 0;
247  pSm->framebuffer = mode->framebuffer;
248  pSm->textMode = !isGraphicsMode;
249  fbAddr = mode->framebuffer;
250  pSm->pf.mRed = mode->red_mask;
251  pSm->pf.pRed = mode->red_position;
252  pSm->pf.mGreen = mode->green_mask;
253  pSm->pf.pGreen = mode->green_position;
254  pSm->pf.mBlue = mode->blue_mask;
255  pSm->pf.pBlue = mode->blue_position;
256  pSm->pf.nBpp = mode->bpp;
257  pSm->pf.nPitch = mode->pitch;
258  modeList.pushBack(pSm);
259 
260  if (isGraphicsMode)
261  {
262  if (mode->Xres > maxWidth)
263  maxWidth = mode->Xres;
264  if (mode->Yres > maxHeight)
265  maxHeight = mode->Yres;
266  }
267  else
268  {
269  if (mode->Xres > maxTextWidth)
270  maxTextWidth = mode->Xres;
271  if (mode->Yres > maxTextHeight)
272  maxTextHeight = mode->Yres;
273  }
274  if (mode->bpp > maxBpp)
275  maxBpp = mode->bpp;
276  }
277 
278  // Total video memory, in bytes.
279  size_t totalMemory = info->totalMemory * 64 * 1024;
280 
281  Bios::instance().free(reinterpret_cast<uintptr_t>(info));
282  Bios::instance().free(reinterpret_cast<uintptr_t>(mode));
283 
284  NOTICE("VBE: Detected compatible display modes:");
285 
286  for (List<Display::ScreenMode *>::Iterator it = modeList.begin();
287  it != modeList.end(); it++)
288  {
289  Display::ScreenMode *pSm = *it;
290  NOTICE(
291  Hex << pSm->id << "\t " << Dec << pSm->width << "x" << pSm->height
292  << "x" << pSm->pf.nBpp << "\t " << Hex << pSm->framebuffer);
293  if (!pSm->textMode)
294  {
295  NOTICE(
296  " " << pSm->pf.mRed << "<<" << pSm->pf.pRed << " "
297  << pSm->pf.mGreen << "<<" << pSm->pf.pGreen << " "
298  << pSm->pf.mBlue << "<<" << pSm->pf.pBlue);
299  }
300  else
301  {
302  NOTICE(" text mode");
303  }
304  }
305  NOTICE("VBE: End of compatible display modes.");
306 
307  // Now that we have a framebuffer address, we can (hopefully) find the
308  // device in the device tree that owns that address.
309  Device *pDevice = 0;
310  auto searchNode = [&pDevice, fbAddr](Device *pDev) {
311  if (pDevice)
312  return pDev;
313 
314  // Get its addresses, and search for fbAddr.
315  for (unsigned int j = 0; j < pDev->addresses().count(); j++)
316  {
317  if (pDev->getPciClassCode() == 0x03 &&
318  pDev->addresses()[j]->m_Address <= fbAddr &&
319  (pDev->addresses()[j]->m_Address +
320  pDev->addresses()[j]->m_Size) > fbAddr)
321  {
322  pDevice = pDev;
323  break;
324  }
325  }
326 
327  return pDev;
328  };
329  auto f = pedigree_std::make_callable(searchNode);
330  Device::foreach (f, 0);
331  if (!pDevice)
332  {
333  ERROR(
334  "VBE: Device mapped to framebuffer address '" << Hex << fbAddr
335  << "' not found.");
336  return false;
337  }
338 
339  // Create a new VbeDisplay device node.
340  VbeDisplay *pDisplay =
341  new VbeDisplay(pDevice, vbeVersion, modeList, totalMemory, g_nDisplays);
342 
343  g_pDisplays[g_nDisplays] = pDisplay;
344 
345  // Does the display already exist in the database?
346  bool bDelayedInsert = false;
347  size_t mode_id = 0;
348  String str;
349  str.Format(
350  "SELECT * FROM displays WHERE pointer=%d",
351  reinterpret_cast<uintptr_t>(pDisplay));
352  Config::Result *pResult = Config::instance().query(str);
353  if (!pResult)
354  {
355  ERROR("vbe: Got no result when selecting displays");
356  }
357  else if (pResult->succeeded() && pResult->rows() == 1)
358  {
359  mode_id = pResult->getNum(0, "mode_id");
360  delete pResult;
361  str.Format(
362  "UPDATE displays SET id=%d WHERE pointer=%d", g_nDisplays,
363  reinterpret_cast<uintptr_t>(pDisplay));
364  pResult = Config::instance().query(str);
365  if (!pResult->succeeded())
366  FATAL("Display update failed: " << pResult->errorMessage());
367  delete pResult;
368  }
369  else if (pResult->succeeded() && pResult->rows() > 1)
370  {
371  delete pResult;
372  FATAL(
373  "Multiple displays for pointer `"
374  << reinterpret_cast<uintptr_t>(pDisplay) << "'");
375  }
376  else if (pResult->succeeded())
377  {
378  delete pResult;
379  bDelayedInsert = true;
380  }
381  else
382  {
383  FATAL("Display select failed: " << pResult->errorMessage());
384  delete pResult;
385  }
386 
387  g_nDisplays++;
388 
389  VbeFramebuffer *pFramebuffer = new VbeFramebuffer(pDisplay);
390  pDisplay->setLogicalFramebuffer(pFramebuffer);
391 
394  pProvider->pDisplay = pDisplay;
395  pProvider->pFramebuffer = pFramebuffer;
396  pProvider->maxWidth = maxWidth;
397  pProvider->maxHeight = maxHeight;
398  pProvider->maxTextWidth = maxTextWidth;
399  pProvider->maxTextHeight = maxTextHeight;
400  pProvider->maxDepth = maxBpp;
401  pProvider->bHardwareAccel = false;
402  pProvider->bTextModes = true;
403 
404  // Register with the graphics service
405  ServiceFeatures *pFeatures =
406  ServiceManager::instance().enumerateOperations(String("graphics"));
407  Service *pService =
408  ServiceManager::instance().getService(String("graphics"));
409  if (pFeatures->provides(ServiceFeatures::touch))
410  if (pService)
411  pService->serve(
412  ServiceFeatures::touch, reinterpret_cast<void *>(pProvider),
413  sizeof(*pProvider));
414 
415  // Replace pDev with pDisplay.
416  pDisplay->setParent(pDevice->getParent());
417  pDevice->getParent()->replaceChild(pDevice, pDisplay);
418 
419  return true;
420 }
421 
422 static void exit()
423 {
424 }
425 
426 MODULE_INFO("vbe", &entry, &exit, "pci", "config");
427 
428 VbeFramebuffer::VbeFramebuffer()
429  : Framebuffer(), m_pDisplay(0), m_pBackbuffer(0), m_nBackbufferBytes(0),
430  m_pFramebufferRegion(0), m_Mode()
431 {
432 }
433 
434 VbeFramebuffer::VbeFramebuffer(Display *pDisplay)
435  : Framebuffer(), m_pDisplay(pDisplay), m_pBackbuffer(0),
436  m_nBackbufferBytes(0), m_pFramebufferRegion(0), m_Mode()
437 {
438 }
439 
440 void VbeFramebuffer::hwRedraw(size_t x, size_t y, size_t w, size_t h)
441 {
442  if (x == ~0UL)
443  x = 0;
444  if (y == ~0UL)
445  y = 0;
446  if (w == ~0UL)
447  w = m_Mode.width;
448  if (h == ~0UL)
449  h = m_Mode.height;
450 
451  if (x == 0 && y == 0 && w >= m_Mode.width && h >= m_Mode.height)
452  {
453  // Full-screen refresh.
454  MemoryCopy(
455  m_pDisplay->getFramebuffer(), m_pBackbuffer, m_nBackbufferBytes);
456  return;
457  }
458 
459  // We have a smaller copy than the entire screen.
460  size_t bytesPerRow = w * m_Mode.bytesPerPixel;
461  size_t xOffset = x * m_Mode.bytesPerPixel;
462  size_t yOffset = y * m_Mode.bytesPerLine;
463 
464  void *firstRowTarget =
465  adjust_pointer(m_pDisplay->getFramebuffer(), yOffset + xOffset);
466  void *firstRowBackbuffer = adjust_pointer(m_pBackbuffer, yOffset + xOffset);
467  for (size_t yy = 0; yy < h; ++yy)
468  {
469  void *targetRow =
470  adjust_pointer(firstRowTarget, yy * m_Mode.bytesPerLine);
471  void *backbufferRow =
472  adjust_pointer(firstRowBackbuffer, yy * m_Mode.bytesPerLine);
473  MemoryCopy(targetRow, backbufferRow, bytesPerRow);
474  }
475 }
476 
477 VbeFramebuffer::~VbeFramebuffer()
478 {
479 }
480 
481 void VbeFramebuffer::setFramebuffer(uintptr_t p)
482 {
483  ByteSet(&m_Mode, 0, sizeof(Display::ScreenMode));
484  if (!m_pDisplay->getCurrentScreenMode(m_Mode))
485  {
486  ERROR("VBE: setting screen mode failed.");
487  return;
488  }
489  m_nBackbufferBytes = m_Mode.bytesPerLine * m_Mode.height;
490  if (m_nBackbufferBytes)
491  {
492  m_pBackbuffer = 0;
493 
494  if (m_pFramebufferRegion)
495  {
496  delete m_pFramebufferRegion;
497  }
498 
499  size_t nPages = (m_nBackbufferBytes +
502 
503  m_pFramebufferRegion = new MemoryRegion("VBE Backbuffer");
504  if (!PhysicalMemoryManager::instance().allocateRegion(
505  *m_pFramebufferRegion, nPages,
507  {
508  delete m_pFramebufferRegion;
509  m_pFramebufferRegion = 0;
510  }
511  else
512  {
513  NOTICE(
514  "VBE backbuffer is at " << reinterpret_cast<uintptr_t>(
515  m_pFramebufferRegion->virtualAddress()));
516  m_pBackbuffer = reinterpret_cast<char *>(
517  m_pFramebufferRegion->virtualAddress());
518  }
519 
520  Framebuffer::setFramebuffer(reinterpret_cast<uintptr_t>(m_pBackbuffer));
521  }
522 }
void setCx(int n)
Definition: Bios.cc:219
void pushBack(const T &value)
Definition: List.h:232
size_t rows()
Returns the number of rows.
uint8_t mRed
Red mask.
Definition: Display.h:57
static PhysicalMemoryManager & instance()
virtual bool setScreenMode(Display::ScreenMode sm)
Definition: VbeDisplay.cc:163
int getDi()
Definition: Bios.cc:252
void setAx(int n)
Definition: Bios.cc:211
void executeInterrupt(int interrupt)
Definition: Bios.cc:172
Definition: String.h:49
virtual bool provides(Type service)
void replaceChild(Device *src, Device *dest)
Definition: Device.cc:168
virtual void hwRedraw(size_t x=~0UL, size_t y=~0UL, size_t w=~0UL, size_t h=~0UL)
Inherited by drivers that provide a hardware redraw function.
Definition: vbe.cc:440
uint8_t pGreen
Position of green field.
Definition: Display.h:60
Definition: Device.h:43
uint8_t nBpp
Bits per pixel (total).
Definition: Display.h:65
#define WARNING(text)
Definition: Log.h:78
size_t getNum(size_t row, size_t col)
Returns the value in column &#39;col&#39; of the result, in number form.
Result * query(const char *sql)
#define NOTICE(text)
Definition: Log.h:74
Special memory entity in the kernel&#39;s virtual address space.
Definition: MemoryRegion.h:35
Definition: Log.h:136
bool succeeded()
Returns true if the result is valid, false if there was an error.
static Bios & instance()
Definition: Bios.h:32
Iterator begin()
Definition: List.h:123
uint32_t nPitch
Bytes per scanline.
Definition: Display.h:66
int getAx()
Definition: Bios.cc:236
Abstracts the system&#39;s framebuffer offering.
void setParent(Device *p)
Definition: Device.h:154
static void foreach(Callback callback, Device *root=0)
Definition: Device.cc:94
int getDx()
Definition: Bios.cc:248
int getBx()
Definition: Bios.cc:240
Service * getService(const String &serviceName)
virtual bool serve(ServiceFeatures::Type type, void *pData, size_t dataLen)=0
uintptr_t malloc(int n)
Definition: Bios.cc:165
uint8_t pBlue
Position of blue field.
Definition: Display.h:62
#define ERROR(text)
Definition: Log.h:82
uint8_t mGreen
Green mask.
Definition: Display.h:59
Definition: Log.h:138
int getCx()
Definition: Bios.cc:244
void setDi(int n)
Definition: Bios.cc:227
#define FATAL(text)
Definition: Log.h:89
std::string errorMessage(size_t buffSz=256)
Returns the error message.
uint8_t mBlue
Blue mask.
Definition: Display.h:61
Iterator end()
Definition: List.h:135
ServiceFeatures * enumerateOperations(const String &serviceName)
Device * getParent() const
Definition: Device.h:149
uint8_t pRed
Position of red field.
Definition: Display.h:58
void setEs(int n)
Definition: Bios.cc:231