Newer
Older
Atmosphere / stratosphere / fatal / source / fatal_task_screen.cpp
@Michael Scire Michael Scire on 14 Nov 2018 12 KB fatal: Reorder error message lines.
/*
 * Copyright (c) 2018 Atmosphère-NX
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
#include <switch.h>

#include <atmosphere/version.h>

#include "fatal_task_screen.hpp"
#include "fatal_config.hpp"
#include "fatal_font.hpp"
#include "ams_logo.hpp"

static constexpr u32 FatalScreenWidth = 1280;
static constexpr u32 FatalScreenHeight = 720;
static constexpr u32 FatalScreenBpp = 2;

static constexpr u32 FatalScreenWidthAlignedBytes = (FatalScreenWidth * FatalScreenBpp + 63) & ~63;
static constexpr u32 FatalScreenWidthAligned = FatalScreenWidthAlignedBytes / FatalScreenBpp;

u32 GetPixelOffset(uint32_t x, uint32_t y)
{
    u32 tmp_pos;

    tmp_pos = ((y & 127) / 16) + (x/32*8) + ((y/16/8)*(((FatalScreenWidthAligned/2)/16*8)));
    tmp_pos *= 16*16 * 4;

    tmp_pos += ((y%16)/8)*512 + ((x%32)/16)*256 + ((y%8)/2)*64 + ((x%16)/8)*32 + (y%2)*16 + (x%8)*2;//This line is a modified version of code from the Tegra X1 datasheet.

    return tmp_pos / 2;
}

Result ShowFatalTask::SetupDisplayInternal() {
    Result rc;
    ViDisplay display;
    /* Try to open the display. */
    if (R_FAILED((rc = viOpenDisplay("Internal", &display)))) {
        if (rc == 0xE72) {
            return 0;
        } else {
            return rc;
        }
    }
    /* Guarantee we close the display. */
    ON_SCOPE_EXIT { viCloseDisplay(&display); };
    
    /* Turn on the screen. */
    if (R_FAILED((rc = viSetDisplayPowerState(&display, ViPowerState_On)))) {
        return rc;
    }
    
    /* Set alpha to 1.0f. */
    if (R_FAILED((rc = viSetDisplayAlpha(&display, 1.0f)))) {
        return rc;
    }
    
    return rc;
}

Result ShowFatalTask::SetupDisplayExternal() {
    Result rc;
    ViDisplay display;
    /* Try to open the display. */
    if (R_FAILED((rc = viOpenDisplay("External", &display)))) {
        if (rc == 0xE72) {
            return 0;
        } else {
            return rc;
        }
    }
    /* Guarantee we close the display. */
    ON_SCOPE_EXIT { viCloseDisplay(&display); };
    
    /* Set alpha to 1.0f. */
    if (R_FAILED((rc = viSetDisplayAlpha(&display, 1.0f)))) {
        return rc;
    }
    
    return rc;
}

Result ShowFatalTask::PrepareScreenForDrawing() {
    Result rc = 0;
    
    /* Connect to vi. */
    if (R_FAILED((rc = viInitialize(ViServiceType_Manager)))) {
        return rc;
    }
    
    /* Close other content. */
    viSetContentVisibility(false);
    
    /* Setup the two displays. */
    if (R_FAILED((rc = SetupDisplayInternal())) || R_FAILED((rc = SetupDisplayExternal()))) {
        return rc;
    }
    
    /* Open the default display. */
    if (R_FAILED((rc = viOpenDefaultDisplay(&this->display)))) {
        return rc;
    }
    
    /* Reset the display magnification to its default value. */
    u32 display_width, display_height;
    if (R_FAILED((rc = viGetDisplayLogicalResolution(&this->display, &display_width, &display_height)))) {
        return rc;
    }
    if (R_FAILED((rc = viSetDisplayMagnification(&this->display, 0, 0, display_width, display_height)))) {
        return rc;
    }
    
    /* Create layer to draw to. */
    if (R_FAILED((rc = viCreateLayer(&this->display, &this->layer)))) {
        return rc;
    }
    
    /* Setup the layer. */
    {
        /* Display a layer of 1280 x 720 at 1.5x magnification */
        /* NOTE: N uses 2 (770x400) RGBA4444 buffers (tiled buffer + linear). */
        /* We use a single 1280x720 tiled RGB565 buffer. */
        constexpr u32 raw_width = FatalScreenWidth;
        constexpr u32 raw_height = FatalScreenHeight;
        constexpr u32 layer_width = ((raw_width) * 3) / 2;
        constexpr u32 layer_height = ((raw_height) * 3) / 2;
        
        const float layer_x = static_cast<float>((display_width - layer_width) / 2);
        const float layer_y = static_cast<float>((display_height - layer_height) / 2);
        u64 layer_z;
        
        if (R_FAILED((rc = viSetLayerSize(&this->layer, layer_width, layer_height)))) {
            return rc;
        }
        
        /* Set the layer's Z at display maximum, to be above everything else .*/
        /* NOTE: Fatal hardcodes 100 here. */
        if (R_SUCCEEDED((rc = viGetDisplayMaximumZ(&this->display, &layer_z)))) {
            if (R_FAILED((rc = viSetLayerZ(&this->layer, layer_z)))) {
                return rc;
            }
        }
        
        /* Center the layer in the screen. */
        if (R_FAILED((rc = viSetLayerPosition(&this->layer, layer_x, layer_y)))) {
            return rc;
        }
    
        /* Create framebuffer. */
        if (R_FAILED(rc = nwindowCreateFromLayer(&this->win, &this->layer))) {
            return rc;
        }
        if (R_FAILED(rc = framebufferCreate(&this->fb, &this->win, raw_width, raw_height, PIXEL_FORMAT_RGB_565, 1))) {
            return rc;
        }
    }
    

    return rc;
}

Result ShowFatalTask::ShowFatal() {
    Result rc = 0;
    const FatalConfig *config = GetFatalConfig();

    if (R_FAILED((rc = PrepareScreenForDrawing()))) {
        *(volatile u32 *)(0xCAFEBABE) = rc;
        return rc;
    }
    
    /* Dequeue a buffer. */
    u16 *tiled_buf = reinterpret_cast<u16 *>(framebufferBegin(&this->fb, NULL));
    if (tiled_buf == nullptr) {
        return FatalResult_NullGfxBuffer;
    }
    
    /* Let the font manager know about our framebuffer. */
    FontManager::ConfigureFontFramebuffer(tiled_buf, GetPixelOffset);
    FontManager::SetFontColor(0xFFFF);
    
    /* Draw a background. */
    for (size_t i = 0; i < this->fb.fb_size / sizeof(*tiled_buf); i++) {
        tiled_buf[i] = 0x39C9;
    }
    
    /* Draw the atmosphere logo in the bottom right corner. */
    for (size_t y = 0; y < AMS_LOGO_HEIGHT; y++) {
        for (size_t x = 0; x < AMS_LOGO_WIDTH; x++) {
            tiled_buf[GetPixelOffset(FatalScreenWidth - AMS_LOGO_WIDTH - 32 + x, 32 + y)] = AMS_LOGO_BIN[y * AMS_LOGO_WIDTH + x];
        }
    }
    
    /* TODO: Actually draw meaningful shit here. */
    FontManager::SetPosition(32, 64);
    FontManager::SetFontSize(16.0f);
    FontManager::PrintFormat(config->error_msg, R_MODULE(this->ctx->error_code), R_DESCRIPTION(this->ctx->error_code), this->ctx->error_code);
    FontManager::AddSpacingLines(0.5f);
    FontManager::PrintFormatLine("Title: %016lx", this->title_id);
    FontManager::AddSpacingLines(0.5f);
    FontManager::PrintFormatLine(u8"Firmware: %s (Atmosphère %u.%u.%u-%s)", GetFatalConfig()->firmware_version.display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision());
    FontManager::AddSpacingLines(1.5f);
    FontManager::Print(config->error_desc);
    
    /* Add a line. */
    for (size_t x = 32; x < FatalScreenWidth - 32; x++) {
        tiled_buf[GetPixelOffset(x, FontManager::GetY())] = 0xFFFF;
    }
    
    
    FontManager::AddSpacingLines(1.5f);
    
    u32 backtrace_y = FontManager::GetY();
    u32 backtrace_x = 0;
    
    /* Print GPRs. */
    FontManager::SetFontSize(14.0f);
    FontManager::PrintLine("General Purpose Registers");
    FontManager::AddSpacingLines(0.5f);
    if (this->ctx->cpu_ctx.is_aarch32) {
        for (size_t i = 0; i < (NumAarch32Gprs / 2); i++) {
            u32 x = FontManager::GetX();
            FontManager::PrintFormat("%s:", Aarch32GprNames[i]);
            FontManager::SetPosition(x + 47, FontManager::GetY());
            FontManager::PrintFormat("0x%08x  ", this->ctx->cpu_ctx.aarch32_ctx.r[i]);
            x = FontManager::GetX();
            FontManager::PrintFormat("%s:", Aarch32GprNames[i + (NumAarch32Gprs / 2)]);
            FontManager::SetPosition(x + 47, FontManager::GetY());
            FontManager::PrintFormat("0x%08x  ", this->ctx->cpu_ctx.aarch32_ctx.r[i]);

            
            FontManager::PrintLine("");
            FontManager::SetPosition(32, FontManager::GetY());
        }
    } else {
        for (size_t i = 0; i < NumAarch64Gprs / 2; i++) {
            u32 x = FontManager::GetX();
            FontManager::PrintFormat("%s:", Aarch64GprNames[i]);
            FontManager::SetPosition(x + 47, FontManager::GetY());
            FontManager::PrintFormat("0x%016lx ", this->ctx->cpu_ctx.aarch64_ctx.x[i]);
            x = FontManager::GetX();
            FontManager::PrintFormat("%s:", Aarch64GprNames[i + (NumAarch64Gprs / 2)]);
            FontManager::SetPosition(x + 47, FontManager::GetY());
            FontManager::PrintFormat("0x%016lx  ", this->ctx->cpu_ctx.aarch64_ctx.x[i]);

            if (i == (NumAarch64Gprs / 2) - 1) {
                FontManager::Print("    ");
                backtrace_x = FontManager::GetX();
            }
            
            FontManager::PrintLine("");
            FontManager::SetPosition(32, FontManager::GetY());
        }
    }
    
    /* Print Backtrace. */
    FontManager::SetPosition(backtrace_x, backtrace_y);
    if (this->ctx->cpu_ctx.is_aarch32) {
        FontManager::PrintFormatLine("Backtrace (Start Address = 0x%08x)", this->ctx->cpu_ctx.aarch32_ctx.start_address);
        FontManager::AddSpacingLines(0.5f);
        for (u32 i = 0; i < Aarch32CpuContext::MaxStackTraceDepth / 2; i++) {
            u32 bt_cur = 0, bt_next = 0;
            if (i < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) {
                bt_cur = this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i];
            }
            if (i + Aarch32CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) {
                bt_next = this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i + Aarch32CpuContext::MaxStackTraceDepth / 2];
            }
            
            if (i < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) {
                u32 x = FontManager::GetX();
                FontManager::PrintFormat("BT[%02X]: ", i);
                FontManager::SetPosition(x + 76, FontManager::GetY());
                FontManager::PrintFormat("0x%08x  ", bt_cur);
            }
            
            if (i + Aarch32CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) {
                u32 x = FontManager::GetX();
                FontManager::PrintFormat("BT[%02X]: ", i + Aarch32CpuContext::MaxStackTraceDepth / 2);
                FontManager::SetPosition(x + 76, FontManager::GetY());
                FontManager::PrintFormat("0x%08x  ", bt_next);
            }
            
            FontManager::PrintLine("");
            FontManager::SetPosition(backtrace_x, FontManager::GetY());
        }
    } else { 
        FontManager::PrintFormatLine("Backtrace (Start Address = 0x%016lx)", this->ctx->cpu_ctx.aarch64_ctx.start_address);
        FontManager::AddSpacingLines(0.5f);
        for (u32 i = 0; i < Aarch64CpuContext::MaxStackTraceDepth / 2; i++) {
            u64 bt_cur = 0, bt_next = 0;
            if (i < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) {
                bt_cur = this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i];
            }
            if (i + Aarch64CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) {
                bt_next = this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i + Aarch64CpuContext::MaxStackTraceDepth / 2];
            }
            
            if (i < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) {
                u32 x = FontManager::GetX();
                FontManager::PrintFormat("BT[%02X]: ", i);
                FontManager::SetPosition(x + 76, FontManager::GetY());
                FontManager::PrintFormat("0x%016lx  ", bt_cur);
            }
            
            if (i + Aarch64CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) {
                u32 x = FontManager::GetX();
                FontManager::PrintFormat("BT[%02X]: ", i + Aarch64CpuContext::MaxStackTraceDepth / 2);
                FontManager::SetPosition(x + 76, FontManager::GetY());
                FontManager::PrintFormat("0x%016lx  ", bt_next);
            }
            
            FontManager::PrintLine("");
            FontManager::SetPosition(backtrace_x, FontManager::GetY());
        }
    }    
    
    /* Enqueue the buffer. */
    framebufferEnd(&fb);
    
    return rc;
}

Result ShowFatalTask::Run() {
    /* Don't show the fatal error screen until we've verified the battery is okay. */
    eventWait(this->battery_event, U64_MAX);

    return ShowFatal();
}

void BacklightControlTask::TurnOnBacklight() {
    lblSwitchBacklightOn(0);
}

Result BacklightControlTask::Run() {
    TurnOnBacklight();
    return 0;
}