| // Copyright 2016 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <assert.h> |
| #include <dev/udisplay.h> |
| #include <vm/vm_aspace.h> |
| #include <vm/vm_object.h> |
| #include <lib/debuglog.h> |
| #include <lib/gfxconsole.h> |
| #include <lib/io.h> |
| #include <platform.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <trace.h> |
| |
| #include <qrcodegen/qrcode.h> |
| |
| #define LOCAL_TRACE 0 |
| |
| constexpr uint kFramebufferArchMmuFlags = ARCH_MMU_FLAG_WRITE_COMBINING | ARCH_MMU_FLAG_PERM_READ | |
| ARCH_MMU_FLAG_PERM_WRITE; |
| |
| static qrcodegen::QrCode qrcode; |
| |
| #define MAX_QRCODE_DATA (2953u) |
| static char qrlogbuf[MAX_QRCODE_DATA]; |
| static size_t qrlogptr; |
| |
| static void qrcode_print_callback(print_callback_t* cb, const char* str, size_t len) { |
| if (len > (MAX_QRCODE_DATA - qrlogptr)) |
| len = (MAX_QRCODE_DATA - qrlogptr); |
| |
| memcpy(qrlogbuf + qrlogptr, str, len); |
| qrlogptr += len; |
| } |
| |
| static print_callback_t qrcode_cb = { |
| .entry = {}, |
| .print = qrcode_print_callback, |
| .context = nullptr |
| }; |
| |
| struct udisplay_info { |
| void* framebuffer_virt; |
| size_t framebuffer_size; |
| struct display_info info; |
| }; |
| |
| static struct udisplay_info g_udisplay; |
| |
| status_t udisplay_init(void) { |
| return MX_OK; |
| } |
| |
| void dlog_bluescreen_halt(void) { |
| platform_stow_crashlog(qrlogbuf, qrlogptr); |
| |
| if (g_udisplay.framebuffer_virt == 0) |
| return; |
| |
| if (qrcode.encodeBinary(qrlogbuf, qrlogptr, qrcodegen::Ecc::LOW)) { |
| printf("cannot create qrcode\n"); |
| return; |
| } |
| |
| int w = g_udisplay.info.width; |
| int h = g_udisplay.info.height; |
| |
| // qrcode.pixel() returns infinite white pixels if you |
| // access outside of the body of the qrcode, which we |
| // take advantage of to draw a border around the qrcode |
| // (necessary for good recogniation) by overshooting 3 |
| // "pixels" in every direction. |
| int sz = qrcode.size() + 6; |
| int px = 1; |
| |
| // Scale up a bit if there's room, but don't go crazy |
| // (no more than 5x5 pixel scaling) |
| while (((sz * (px + 1)) < (w / 2)) && (px < 5)) |
| px++; |
| |
| w -= sz * px; |
| h -= sz * px; |
| |
| for (int y = 0; y < sz; y++) { |
| for (int x = 0; x < sz; x++) { |
| uint32_t color = qrcode.pixel(x - 3, y - 3) ? 0xFF000000 : 0xFFFFFFFF; |
| for (int yy = 0; yy < px; yy++) { |
| for (int xx = 0; xx < px; xx++) { |
| gfxconsole_putpixel(w + x * px + xx, h + y * px + yy, color); |
| } |
| } |
| } |
| } |
| |
| } |
| |
| status_t udisplay_set_framebuffer(paddr_t fb_phys, size_t fb_size) { |
| g_udisplay.framebuffer_size = fb_size; |
| |
| // map the framebuffer |
| status_t result = VmAspace::kernel_aspace()->AllocPhysical( |
| "udisplay_fb", |
| g_udisplay.framebuffer_size, |
| &g_udisplay.framebuffer_virt, |
| PAGE_SIZE_SHIFT, |
| fb_phys, |
| 0 /* vmm flags */, |
| kFramebufferArchMmuFlags); |
| |
| if (result) |
| g_udisplay.framebuffer_virt = 0; |
| |
| return MX_OK; |
| } |
| |
| status_t udisplay_set_framebuffer_vmo(fbl::RefPtr<VmObject> vmo) { |
| g_udisplay.framebuffer_size = 0; |
| g_udisplay.framebuffer_virt = 0; |
| |
| const size_t size = vmo->size(); |
| fbl::RefPtr<VmMapping> mapping; |
| status_t status = VmAspace::kernel_aspace()->RootVmar()->CreateVmMapping( |
| 0 /* ignored */, size, 0 /* align pow2 */, 0 /* vmar flags */, |
| fbl::move(vmo), 0, kFramebufferArchMmuFlags, "framebuffer_vmo", &mapping); |
| if (status != MX_OK) |
| return status; |
| |
| status = mapping->MapRange(0, size, true); |
| if (status != MX_OK) { |
| mapping->Destroy(); |
| return status; |
| } |
| |
| g_udisplay.framebuffer_virt = reinterpret_cast<void*>(mapping->base()); |
| g_udisplay.framebuffer_size = size; |
| return MX_OK; |
| } |
| |
| status_t udisplay_set_display_info(struct display_info* display) { |
| memcpy(&g_udisplay.info, display, sizeof(struct display_info)); |
| return MX_OK; |
| } |
| |
| status_t udisplay_bind_gfxconsole(void) { |
| if (g_udisplay.framebuffer_virt == 0) |
| return MX_ERR_NOT_FOUND; |
| |
| register_print_callback(&qrcode_cb); |
| |
| // bind the display to the gfxconsole |
| g_udisplay.info.framebuffer = g_udisplay.framebuffer_virt; |
| g_udisplay.info.flags = DISPLAY_FLAG_HW_FRAMEBUFFER | DISPLAY_FLAG_CRASH_FRAMEBUFFER; |
| gfxconsole_bind_display(&g_udisplay.info, nullptr); |
| |
| return MX_OK; |
| } |