#include <vc.h>
#include <console/video_console.h>
#include <libkern/OSByteOrder.h>
#include <kdp/kdp_udp.h>
#include <kern/debug.h>
#include <mach/mach_time.h>
#include <sys/errno.h>
#include <string.h>
#include <machine/machlimits.h>
extern struct vc_info vinfo;
extern boolean_t panicDialogDesired;
#include "panic_image.c"
void panic_ui_initialize(const unsigned char * system_clut);
int panic_dialog_set_image( const unsigned char * ptr, unsigned int size );
void panic_dialog_get_image(const unsigned char **ptr, unsigned int *size);
void draw_panic_dialog( void );
void panic_dialog_test( void );
static int panic_dialog_verify( const struct panicimage * data, unsigned int size );
static int pixels_needed_to_blit_digit( int digit );
static void blit_digit( int digit );
static const char * strnstr(const char * s, const char * find, size_t slen);
void dim_screen(void);
static void panic_blit_rect(unsigned int x, unsigned int y, unsigned int width,
unsigned int height, int transparent,
const unsigned char * dataPtr);
static int panic_info_x;
static int panic_info_y;
static const unsigned char * active_clut = NULL;
static boolean_t panicDialogDrawn = FALSE;
static const struct panicimage * panic_dialog = NULL;
static const unsigned char * panic_dialog_data = NULL;
static const unsigned char * panic_dialog_clut = NULL;
static const unsigned char *curr_image_ptr;
static unsigned int curr_image_size = 0;
#define FONT_WIDTH 8
#define FONT_HEIGHT 16
static unsigned short rendered_font[FONT_HEIGHT][FONT_WIDTH];
#define VERSIONBUF_LEN 20
static char versionbuf[VERSIONBUF_LEN];
#define isdigit(d) ((d) >= '0' && (d) <= '9')
#define CLUT_ENTRIES 256
#define CLUT_SIZE (CLUT_ENTRIES * 3)
extern unsigned char iso_font[];
extern const char version[];
void
panic_ui_initialize(const unsigned char * system_clut)
{
char vstr[VERSIONBUF_LEN];
panic_dialog_set_image( NULL, 0 );
active_clut = system_clut;
strlcpy(vstr, "custom", VERSIONBUF_LEN);
if (version[0]) {
const char *versionpos = strnstr(version, "xnu-", VERSIONBUF_LEN);
if (versionpos) {
int len, i;
vstr[0] = '\0';
for (i = 0, len = 4; len < VERSIONBUF_LEN; len++) {
if (isdigit(versionpos[len]) || versionpos[len] == '.') {
vstr[i++] = versionpos[len];
continue;
}
break;
}
if ( versionpos[len-1] == '.' )
i--;
for (; len < VERSIONBUF_LEN; len++) {
if ( !isdigit(versionpos[len]) )
continue;
break;
}
if ( versionpos[len-1] == '~' ) {
vstr[i++] = versionpos[len-1];
for (; len < VERSIONBUF_LEN; len++) {
if ( isdigit(versionpos[len]) ) {
vstr[i++] = versionpos[len];
continue;
}
break;
}
}
vstr[i] = '\0';
}
}
strlcpy(versionbuf, vstr, VERSIONBUF_LEN);
}
void
panic_dialog_test( void )
{
boolean_t o_panicDialogDrawn = panicDialogDrawn;
boolean_t o_panicDialogDesired = panicDialogDesired;
unsigned int o_logPanicDataToScreen = logPanicDataToScreen;
unsigned long o_panic_caller = panic_caller;
unsigned int o_panicDebugging = panicDebugging;
panicDebugging = TRUE;
panic_caller = (unsigned long)(char *)__builtin_return_address(0);
logPanicDataToScreen = FALSE;
panicDialogDesired = TRUE;
panicDialogDrawn = FALSE;
draw_panic_dialog();
panicDebugging = o_panicDebugging;
panic_caller = o_panic_caller;
logPanicDataToScreen = o_logPanicDataToScreen;
panicDialogDesired = o_panicDialogDesired;
panicDialogDrawn = o_panicDialogDrawn;
}
void
draw_panic_dialog( void )
{
if (!panicDialogDrawn && panicDialogDesired) {
if ( !logPanicDataToScreen ) {
int pd_x, pd_y;
int count, nibble, indx;
struct ether_addr kdp_mac_addr;
unsigned int panic_dialog_count, ip_addr;
char panic_num_chars[13+8+1], mac_addr_chars[17+1], ip_addr_chars[15+1];
struct {
int pixels;
char * chars;
} panic_dialog_info[3];
dim_screen();
pd_x = (int)((vinfo.v_width/2) - panic_dialog->pd_width/2);
pd_y = (int)((vinfo.v_height/2) - panic_dialog->pd_height/2);
panic_blit_rect(pd_x, pd_y, panic_dialog->pd_width,
panic_dialog->pd_height, 0,
panic_dialog_data);
panic_dialog_count = 0;
if (panicDebugging) {
int x1, x2;
if ( panic_caller != 0 ) {
panic_dialog_info[panic_dialog_count].pixels = 0;
for ( indx=1, count=0; count < 13; count++ ) {
if ( versionbuf[count] == '\0' )
break;
panic_num_chars[indx++] = versionbuf[count];
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( versionbuf[count] );
}
panic_num_chars[indx++] = ':';
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( ':' );
for ( count=8; count != 0; count-- ) {
nibble = (int)((panic_caller >> ((count-1)<<2)) &0xF);
panic_num_chars[indx++] = nibble;
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( nibble );
}
panic_num_chars[0] = indx;
panic_dialog_info[panic_dialog_count].chars = panic_num_chars;
panic_dialog_count++;
}
kdp_mac_addr = kdp_get_mac_addr();
if( ! (kdp_mac_addr.ether_addr_octet[0] || kdp_mac_addr.ether_addr_octet[1] || kdp_mac_addr.ether_addr_octet[2]
|| kdp_mac_addr.ether_addr_octet[3] || kdp_mac_addr.ether_addr_octet[4] || kdp_mac_addr.ether_addr_octet[5])) {
for (count = 0; count < 6; count++ )
kdp_mac_addr.ether_addr_octet[count] = -1;
}
panic_dialog_info[panic_dialog_count].pixels = 0;
for (indx=1, count=0; count < 6; count++ ) {
nibble = (kdp_mac_addr.ether_addr_octet[count] & 0xf0) >> 4;
mac_addr_chars[indx++] = nibble;
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( nibble );
nibble = kdp_mac_addr.ether_addr_octet[count] & 0xf;
mac_addr_chars[indx++] = nibble;
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( nibble );
if( count < 5 ) {
mac_addr_chars[indx++] = ':';
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( ':' );
}
}
mac_addr_chars[0] = indx;
panic_dialog_info[panic_dialog_count].chars = mac_addr_chars;
panic_dialog_count++;
if ( (ip_addr = (unsigned int) ntohl(kdp_get_ip_address())) != 0 ) {
int d1, d2, d3;
panic_dialog_info[panic_dialog_count].pixels = 0;
for ( indx=1, count=0; count < 4; count++ ) {
nibble = (ip_addr & 0xff000000 ) >> 24;
d3 = (nibble % 10) ; nibble = nibble / 10;
d2 = (nibble % 10) ; nibble = nibble / 10;
d1 = (nibble % 10) ;
if( d1 != 0 ) {
ip_addr_chars[indx++] = d1;
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( d1 );
}
ip_addr_chars[indx++] = d2;
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( d2 );
ip_addr_chars[indx++] = d3;
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( d3 );
if ( count < 3 ) {
ip_addr_chars[indx++] = '.';
panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( '.' );
}
d1= d2 = d3 = 0;
ip_addr = ip_addr << 8;
}
ip_addr_chars[0] = indx;
panic_dialog_info[panic_dialog_count].chars = ip_addr_chars;
panic_dialog_count++;
}
panic_info_y = (int)((vinfo.v_height/2) + panic_dialog->pd_height/2 - (panic_dialog->pd_info_height));
switch ( panic_dialog_count ) {
case 1 :
panic_info_x = (int)((vinfo.v_width/2) - (panic_dialog_info[0].pixels/2));
for (indx=1; indx < panic_dialog_info[0].chars[0]; indx++)
blit_digit(panic_dialog_info[0].chars[indx]);
break;
case 2 :
x1 = ((panic_dialog->pd_width/2) - panic_dialog_info[0].pixels)/2;
panic_info_x = (int)(((vinfo.v_width/2) - (panic_dialog->pd_width/2)) + x1);
for (indx=1; indx < panic_dialog_info[0].chars[0]; indx++)
blit_digit(panic_dialog_info[0].chars[indx]);
x2 = ((panic_dialog->pd_width/2) - panic_dialog_info[1].pixels)/2;
panic_info_x = (int)((vinfo.v_width/2) + x2);
for (indx=1; indx < panic_dialog_info[1].chars[0]; indx++)
blit_digit(panic_dialog_info[1].chars[indx]);
break;
case 3 :
x1 = ((panic_dialog->pd_width/2) - panic_dialog_info[0].pixels - (panic_dialog_info[1].pixels/2))/2;
panic_info_x = (int)(((vinfo.v_width/2) - (panic_dialog->pd_width/2)) + x1);
for (indx=1; indx < panic_dialog_info[0].chars[0]; indx++)
blit_digit(panic_dialog_info[0].chars[indx]);
panic_info_x = (int)((vinfo.v_width/2) - (panic_dialog_info[1].pixels/2));
for (indx=1; indx < panic_dialog_info[1].chars[0]; indx++)
blit_digit(panic_dialog_info[1].chars[indx]);
x2 = ((panic_dialog->pd_width/2) - panic_dialog_info[2].pixels - (panic_dialog_info[1].pixels/2))/2;
panic_info_x = (int)((vinfo.v_width/2) + x2 + (panic_dialog_info[1].pixels/2));
for (indx=1; indx < panic_dialog_info[2].chars[0]; indx++)
blit_digit(panic_dialog_info[2].chars[indx]);
break;
default :
break;
}
}
}
}
panicDialogDrawn = TRUE;
panicDialogDesired = FALSE;
}
int
panic_dialog_set_image( const unsigned char * ptr, unsigned int size )
{
int error;
unsigned int newsize;
const struct panicimage * newimage;
if ( ptr == NULL ) {
newimage = &panic_dialog_default;
newsize = (unsigned int)(sizeof(struct panicimage) + newimage->pd_dataSize);
}
else {
newimage = (const struct panicimage *)ptr;
newsize = size;
}
if ( (error = panic_dialog_verify( newimage, newsize )) )
return (error);
panic_dialog = newimage;
panic_dialog_data = &panic_dialog->data[0];
panic_dialog_clut = &panic_dialog->data[panic_dialog->pd_dataSize-CLUT_SIZE];
curr_image_ptr = ptr;
curr_image_size = size;
return (0);
}
void
panic_dialog_get_image(const unsigned char ** ptr, unsigned int * size )
{
*ptr = curr_image_ptr;
*size = curr_image_size;
}
static int
panic_dialog_verify( const struct panicimage * newimage, unsigned int size )
{
unsigned int sum, i;
if ( size < (sizeof(struct panicimage) + newimage->pd_dataSize) )
return EINVAL;
if ( newimage->pd_tag != 0x524E4D70 )
return EINVAL;
size = newimage->pd_dataSize-CLUT_SIZE;
for (sum=0,i=0; i<size; i++) {
sum += newimage->data[i];
sum <<= sum&1;
}
if ( sum != newimage->pd_sum )
return EINVAL;
return 0;
}
static const struct rendered_num * find_rendered_digit( int digit );
static void panic_blit_rect_8(unsigned int x, unsigned int y,
unsigned int width, unsigned int height,
int transparent, const unsigned char *dataPtr);
static void panic_blit_rect_16(unsigned int x, unsigned int y,
unsigned int width, unsigned int height,
int transparent, const unsigned char *dataPtr);
static void panic_blit_rect_32(unsigned int x, unsigned int y,
unsigned int width, unsigned int height,
int transparent, const unsigned char *dataPtr);
static void panic_blit_rect_30(unsigned int x, unsigned int y,
unsigned int width, unsigned int height,
int transparent, const unsigned char *dataPtr);
static int decode_rle(const unsigned char *dataPtr,
unsigned int *quantity, unsigned int *depth,
const unsigned char **value);
static unsigned int make24bitcolor( unsigned int index, const unsigned char * clut );
static unsigned char findIndexMatch( unsigned char index );
static unsigned char color24togray8( unsigned int color24 );
static unsigned char findbestgray( unsigned int color24 );
static int isActiveClutOK( void );
static int
pixels_needed_to_blit_digit(__unused int digit )
{
return FONT_WIDTH;
}
static const struct rendered_num *
find_rendered_digit( int digit )
{
const struct rendered_num *digitPtr;
if ( digit < 16 ) {
if ( digit < 10 )
digit += 0x30;
else
digit += 0x37;
}
digitPtr = (const struct rendered_num *) &iso_font[digit * 16];
return digitPtr;
}
static void
blit_digit( int digit )
{
const unsigned char *raw_data =
(const unsigned char *)find_rendered_digit(digit);
unsigned width = FONT_WIDTH, height = FONT_HEIGHT;
int row;
for (row=0; row<FONT_HEIGHT; row++) {
int j;
unsigned char bits;
bits = raw_data[row];
for( j=FONT_WIDTH-1; j>=0; j--) {
if ( bits & 0x80 )
rendered_font[row][j] = OSSwapBigToHostInt16(0x0100 | panic_dialog->pd_info_color[0]);
else
rendered_font[row][j] = OSSwapBigToHostInt16(0x0100 | panic_dialog->pd_info_color[1]);
bits <<= 1;
}
}
panic_blit_rect( panic_info_x, panic_info_y , width, height, 255, (unsigned char *) rendered_font);
panic_info_x += width;
}
static void
panic_blit_rect(unsigned int x, unsigned int y, unsigned int width,
unsigned int height, int transparent,
const unsigned char *dataPtr)
{
if(!vinfo.v_depth)
return;
switch( vinfo.v_depth) {
case 8:
panic_blit_rect_8( x, y, width, height, transparent, dataPtr);
break;
case 16:
panic_blit_rect_16( x, y, width, height, transparent, dataPtr);
break;
case 32:
panic_blit_rect_32( x, y, width, height, transparent, dataPtr);
break;
case 30:
panic_blit_rect_30( x, y, width, height, transparent, dataPtr);
break;
}
}
static void
panic_blit_rect_8(unsigned int x, unsigned int y, unsigned int width,
unsigned int height, __unused int transparent,
const unsigned char * dataPtr)
{
volatile unsigned char * dst;
unsigned int line, col, i;
static int clutOK = -1;
unsigned int data, quantity, depth;
const unsigned char *value;
if ( clutOK == -1 )
clutOK = isActiveClutOK();
dst = (volatile unsigned char *) (vinfo.v_baseaddr +
(y * vinfo.v_rowbytes) +
x);
quantity = 0;
i = 0;
for( line = 0; line < height; line++) {
for( col = 0; col < width; col++) {
if (quantity == 0) {
dataPtr += decode_rle(dataPtr, &quantity, &depth, &value);
i = 0;
}
if ( clutOK )
data = value[i++];
else
data = findIndexMatch( value[i++] );
*(dst + col) = data;
if ( i == depth ) {
i = 0;
quantity--;
}
}
dst = (volatile unsigned char *) (((uintptr_t)dst) + vinfo.v_rowbytes);
}
}
static void
panic_blit_rect_16(unsigned int x, unsigned int y, unsigned int width,
unsigned int height, __unused int transparent,
const unsigned char *dataPtr)
{
volatile unsigned short * dst;
unsigned int line, col, i;
unsigned int quantity, index, data, depth;
const unsigned char *value;
dst = (volatile unsigned short *) (vinfo.v_baseaddr +
(y * vinfo.v_rowbytes) +
(x * 2));
quantity = 0;
i = 0;
for( line = 0; line < height; line++) {
for( col = 0; col < width; col++) {
if (quantity == 0) {
dataPtr += decode_rle(dataPtr, &quantity, &depth, &value);
i = 0;
}
index = value[i++] * 3;
data = ( (unsigned short) (0xf8 & (panic_dialog_clut[index + 0])) << 7)
| ( (unsigned short) (0xf8 & (panic_dialog_clut[index + 1])) << 2)
| ( (unsigned short) (0xf8 & (panic_dialog_clut[index + 2])) >> 3);
*(dst + col) = data;
if ( i == depth ) {
i = 0;
quantity--;
}
}
dst = (volatile unsigned short *) (((uintptr_t)dst) + vinfo.v_rowbytes);
}
}
static void
panic_blit_rect_32(unsigned int x, unsigned int y, unsigned int width,
unsigned int height, __unused int transparent,
const unsigned char *dataPtr)
{
volatile unsigned int * dst;
unsigned int line, col, i;
unsigned int quantity, index, data, depth;
const unsigned char *value;
dst = (volatile unsigned int *) (vinfo.v_baseaddr +
(y * vinfo.v_rowbytes) +
(x * 4));
quantity = 0;
i = 0;
for( line = 0; line < height; line++) {
for( col = 0; col < width; col++) {
if (quantity == 0) {
dataPtr += decode_rle(dataPtr, &quantity, &depth, &value);
i = 0;
}
index = value[i++] * 3;
data = ( (unsigned int) panic_dialog_clut[index + 0] << 16)
| ( (unsigned int) panic_dialog_clut[index + 1] << 8)
| ( (unsigned int) panic_dialog_clut[index + 2]);
*(dst + col) = data;
if ( i == depth ) {
i = 0;
quantity--;
}
}
dst = (volatile unsigned int *) (((uintptr_t)dst) + vinfo.v_rowbytes);
}
}
static void
panic_blit_rect_30(unsigned int x, unsigned int y, unsigned int width,
unsigned int height, __unused int transparent,
const unsigned char *dataPtr)
{
volatile unsigned int * dst;
unsigned int line, col, i;
unsigned int quantity, index, data, depth;
const unsigned char *value;
unsigned int in;
dst = (volatile unsigned int *) (vinfo.v_baseaddr +
(y * vinfo.v_rowbytes) +
(x * 4));
quantity = 0;
i = 0;
for( line = 0; line < height; line++) {
for( col = 0; col < width; col++) {
if (quantity == 0) {
dataPtr += decode_rle(dataPtr, &quantity, &depth, &value);
i = 0;
}
index = value[i++] * 3;
in = panic_dialog_clut[index + 0];
data = (in << 2) | (in >> 6);
in = panic_dialog_clut[index + 1];
data |= (in << (2 + 10)) | ((3 << 10) & (in << 4));
in = panic_dialog_clut[index + 2];
data |= (in << (2 + 20)) | ((3 << 20) & (in << 14));
*(dst + col) = data;
if ( i == depth ) {
i = 0;
quantity--;
}
}
dst = (volatile unsigned int *) (((uintptr_t)dst) + vinfo.v_rowbytes);
}
}
static int
decode_rle(const unsigned char *dataPtr, unsigned int *quantity,
unsigned int *depth, const unsigned char **value )
{
unsigned int mask;
int i, runlen, runsize;
i = 0;
mask = dataPtr[i] & 0xF0;
if ( mask & 0x80 ) {
runsize = ((mask & 0x60) >> 5) + 1;
runlen = dataPtr[i++] & 0x0F;
if ( mask & 0x10 ) {
int shift = 4;
do {
mask = dataPtr[i] & 0x80;
runlen |= ((dataPtr[i++] & 0x7F) << shift);
shift+=7;
} while (mask);
}
} else {
runlen = 1;
runsize = dataPtr[i++];
}
*depth = runsize;
*quantity = runlen;
*value = &dataPtr[i];
return i+runsize;
}
void
dim_screen(void)
{
unsigned int *p, *endp, *row;
int col, rowline, rowlongs;
register unsigned int mask;
if(!vinfo.v_depth)
return;
if ( vinfo.v_depth == 32 )
mask = 0x007F7F7F;
else if ( vinfo.v_depth == 30 )
mask = (0x1ff<<20) | (0x1ff<<10) | 0x1ff;
else if ( vinfo.v_depth == 16 )
mask = 0x3DEF3DEF;
else
return;
rowline = (int)(vinfo.v_rowscanbytes / 4);
rowlongs = (int)(vinfo.v_rowbytes / 4);
p = (unsigned int*) vinfo.v_baseaddr;
endp = p + (rowlongs * vinfo.v_height);
for (row = p ; row < endp ; row += rowlongs) {
for (p = &row[0], col = 0; col < rowline; col++) {
*p = (*p >> 1) & mask;
++p;
}
}
}
static const char *
strnstr(const char * s, const char * find, size_t slen)
{
char c, sc;
size_t len;
if ((c = *find++) != '\0') {
len = strlen(find);
do {
do {
if ((sc = *s++) == '\0' || slen-- < 1)
return (NULL);
} while (sc != c);
if (len > slen)
return (NULL);
} while (strncmp(s, find, len) != 0);
s--;
}
return s;
}
static unsigned int
make24bitcolor( unsigned int index, const unsigned char * clut )
{
unsigned int color24 = 0;
int i = index * 3;
color24 |= clut[i+0] << 16;
color24 |= clut[i+1] << 8;
color24 |= clut[i+2];
return color24;
}
static unsigned char
findbestgray( unsigned int color24 )
{
unsigned int c24, rel, bestindex=-1, bestgray = -1;
unsigned char gray8, c8;
int i;
#define abs(v) ((v) > 0)?(v):-(v)
gray8 = color24togray8( color24 );
for (i=0; i<CLUT_ENTRIES; i++) {
c24 = make24bitcolor( i, active_clut );
if ( (((c24>>16)&0xff) != ((c24>>8)&0xff)) || ((c24>>8)&0xff) != (c24 & 0xff) )
continue;
c8 = c24 & 0xFF;
rel = abs( gray8 - c8 );
if ( rel < bestgray ) {
bestgray = rel;
bestindex = i;
}
}
if (ULONG_MAX == bestindex) {
bestindex = gray8;
}
return bestindex;
#undef abs
}
static unsigned char
color24togray8( unsigned int color24 )
{
int R, G, B;
int Gray;
unsigned char gray8;
R = (color24 & 0xFF0000) >> 16 ;
G = (color24 & 0xFF00) >> 8 ;
B = (color24 & 0xFF);
Gray = (R*30) + (G*59) + (B*11);
gray8 = (unsigned char) ((Gray + 50) / 100);
return gray8;
}
static unsigned char
findIndexMatch( unsigned char index )
{
static unsigned int last_in_index = -1;
static unsigned char last_index;
unsigned int sc;
if ( index == last_in_index )
return last_index;
last_in_index = index;
sc = make24bitcolor( index, panic_dialog_clut );
last_index = findbestgray( sc );
return last_index;
}
static int
isActiveClutOK( void )
{
int i;
int r = 1;
for (i=0; i<CLUT_ENTRIES; i++) {
if ( panic_dialog_clut[i] == active_clut[i] ) continue;
r = 0;
break;
}
return r;
}