]>
Commit | Line | Data |
---|---|---|
55e303ae | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved. |
55e303ae | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
55e303ae | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
55e303ae A |
27 | */ |
28 | ||
29 | #include <vc.h> | |
55e303ae | 30 | #include <console/video_console.h> |
0c530ab8 | 31 | #include <libkern/OSByteOrder.h> |
55e303ae A |
32 | #include <kdp/kdp_udp.h> |
33 | #include <kern/debug.h> | |
91447636 A |
34 | #include <mach/mach_time.h> |
35 | #include <sys/errno.h> | |
36 | #include <string.h> | |
2d21ac55 | 37 | #include <machine/machlimits.h> |
b7266188 | 38 | #include <pexpert/pexpert.h> |
55e303ae A |
39 | |
40 | extern struct vc_info vinfo; | |
41 | extern boolean_t panicDialogDesired; | |
42 | ||
91447636 A |
43 | #include "panic_image.c" |
44 | ||
45 | void panic_ui_initialize(const unsigned char * system_clut); | |
46 | int panic_dialog_set_image( const unsigned char * ptr, unsigned int size ); | |
2d21ac55 | 47 | void panic_dialog_get_image(const unsigned char **ptr, unsigned int *size); |
91447636 A |
48 | void draw_panic_dialog( void ); |
49 | void panic_dialog_test( void ); | |
50 | ||
51 | static int panic_dialog_verify( const struct panicimage * data, unsigned int size ); | |
52 | static int pixels_needed_to_blit_digit( int digit ); | |
55e303ae | 53 | static void blit_digit( int digit ); |
2d21ac55 | 54 | static const char * strnstr(const char * s, const char * find, size_t slen); |
2d21ac55 A |
55 | static void panic_blit_rect(unsigned int x, unsigned int y, unsigned int width, |
56 | unsigned int height, int transparent, | |
57 | const unsigned char * dataPtr); | |
91447636 A |
58 | |
59 | static int panic_info_x; | |
60 | static int panic_info_y; | |
61 | ||
62 | static const unsigned char * active_clut = NULL; /* This is a copy of the active clut */ | |
63 | ||
55e303ae A |
64 | static boolean_t panicDialogDrawn = FALSE; |
65 | ||
91447636 A |
66 | static const struct panicimage * panic_dialog = NULL; /* the active panic dialog */ |
67 | static const unsigned char * panic_dialog_data = NULL; /* where the image data starts */ | |
68 | static const unsigned char * panic_dialog_clut = NULL; /* where the clut used for the image starts */ | |
55e303ae | 69 | |
2d21ac55 A |
70 | static const unsigned char *curr_image_ptr; /* If NULL, the default panic |
71 | dialog is active */ | |
91447636 | 72 | static unsigned int curr_image_size = 0; |
55e303ae | 73 | |
91447636 A |
74 | #define FONT_WIDTH 8 |
75 | #define FONT_HEIGHT 16 | |
76 | static unsigned short rendered_font[FONT_HEIGHT][FONT_WIDTH]; | |
55e303ae | 77 | |
2d21ac55 A |
78 | #define VERSIONBUF_LEN 20 |
79 | static char versionbuf[VERSIONBUF_LEN]; /* ####.###~###\0 */ | |
55e303ae | 80 | |
91447636 | 81 | #define isdigit(d) ((d) >= '0' && (d) <= '9') |
55e303ae | 82 | |
91447636 A |
83 | #define CLUT_ENTRIES 256 |
84 | #define CLUT_SIZE (CLUT_ENTRIES * 3) | |
85 | ||
86 | ||
87 | /* | |
88 | * This routine sets up the default panic dialog | |
89 | */ | |
90 | ||
91 | extern unsigned char iso_font[]; | |
92 | extern const char version[]; | |
91447636 A |
93 | |
94 | void | |
55e303ae A |
95 | panic_ui_initialize(const unsigned char * system_clut) |
96 | { | |
2d21ac55 | 97 | char vstr[VERSIONBUF_LEN]; |
91447636 A |
98 | |
99 | panic_dialog_set_image( NULL, 0 ); | |
100 | ||
101 | active_clut = system_clut; | |
102 | ||
2d21ac55 | 103 | strlcpy(vstr, "custom", VERSIONBUF_LEN); |
91447636 A |
104 | |
105 | /* Convert xnu-####.###.obj~### into ####.###~### */ | |
106 | ||
cf7d32b8 | 107 | if (version[0]) { |
2d21ac55 | 108 | const char *versionpos = strnstr(version, "xnu-", VERSIONBUF_LEN); |
91447636 A |
109 | |
110 | if (versionpos) { | |
111 | int len, i; | |
112 | ||
113 | vstr[0] = '\0'; | |
114 | ||
2d21ac55 | 115 | for (i = 0, len = 4; len < VERSIONBUF_LEN; len++) { |
91447636 A |
116 | if (isdigit(versionpos[len]) || versionpos[len] == '.') { /* extract ####.###. */ |
117 | vstr[i++] = versionpos[len]; | |
118 | continue; | |
119 | } | |
120 | break; | |
121 | } | |
122 | ||
123 | if ( versionpos[len-1] == '.' ) /* remove trailing period if present */ | |
124 | i--; | |
125 | ||
2d21ac55 | 126 | for (; len < VERSIONBUF_LEN; len++) { /* skip to next digit if present */ |
91447636 A |
127 | if ( !isdigit(versionpos[len]) ) |
128 | continue; | |
129 | break; | |
130 | } | |
131 | ||
132 | if ( versionpos[len-1] == '~' ) { /* extract ~### if present */ | |
133 | vstr[i++] = versionpos[len-1]; | |
2d21ac55 | 134 | for (; len < VERSIONBUF_LEN; len++) { /* extract ### */ |
91447636 A |
135 | if ( isdigit(versionpos[len]) ) { |
136 | vstr[i++] = versionpos[len]; | |
137 | continue; | |
138 | } | |
139 | break; | |
140 | } | |
141 | } | |
142 | ||
143 | vstr[i] = '\0'; | |
144 | } | |
145 | } | |
146 | ||
2d21ac55 | 147 | strlcpy(versionbuf, vstr, VERSIONBUF_LEN); |
91447636 A |
148 | } |
149 | ||
150 | ||
151 | ||
152 | void | |
153 | panic_dialog_test( void ) | |
154 | { | |
155 | boolean_t o_panicDialogDrawn = panicDialogDrawn; | |
156 | boolean_t o_panicDialogDesired = panicDialogDesired; | |
157 | unsigned int o_logPanicDataToScreen = logPanicDataToScreen; | |
2d21ac55 | 158 | unsigned long o_panic_caller = panic_caller; |
91447636 A |
159 | unsigned int o_panicDebugging = panicDebugging; |
160 | ||
91447636 | 161 | panicDebugging = TRUE; |
2d21ac55 | 162 | panic_caller = (unsigned long)(char *)__builtin_return_address(0); |
91447636 A |
163 | logPanicDataToScreen = FALSE; |
164 | panicDialogDesired = TRUE; | |
165 | panicDialogDrawn = FALSE; | |
166 | ||
167 | draw_panic_dialog(); | |
168 | ||
169 | panicDebugging = o_panicDebugging; | |
170 | panic_caller = o_panic_caller; | |
171 | logPanicDataToScreen = o_logPanicDataToScreen; | |
172 | panicDialogDesired = o_panicDialogDesired; | |
173 | panicDialogDrawn = o_panicDialogDrawn; | |
55e303ae A |
174 | } |
175 | ||
91447636 | 176 | |
55e303ae A |
177 | void |
178 | draw_panic_dialog( void ) | |
179 | { | |
91447636 A |
180 | if (!panicDialogDrawn && panicDialogDesired) { |
181 | if ( !logPanicDataToScreen ) { | |
182 | int pd_x, pd_y; | |
183 | int count, nibble, indx; | |
184 | struct ether_addr kdp_mac_addr; | |
185 | unsigned int panic_dialog_count, ip_addr; | |
186 | char panic_num_chars[13+8+1], mac_addr_chars[17+1], ip_addr_chars[15+1]; | |
187 | struct { | |
188 | int pixels; | |
189 | char * chars; | |
190 | } panic_dialog_info[3]; | |
191 | ||
55e303ae A |
192 | |
193 | /* dim the screen 50% before putting up panic dialog */ | |
194 | dim_screen(); | |
195 | ||
196 | /* set up to draw background box */ | |
91447636 A |
197 | /* by locating where the upper left corner is placed */ |
198 | ||
b0d623f7 A |
199 | pd_x = (int)((vinfo.v_width/2) - panic_dialog->pd_width/2); |
200 | pd_y = (int)((vinfo.v_height/2) - panic_dialog->pd_height/2); | |
55e303ae | 201 | |
91447636 | 202 | /* draw panic dialog at pd_x/pd_y */ |
2d21ac55 A |
203 | panic_blit_rect(pd_x, pd_y, panic_dialog->pd_width, |
204 | panic_dialog->pd_height, 0, | |
205 | panic_dialog_data); | |
55e303ae | 206 | |
91447636 A |
207 | panic_dialog_count = 0; /* number of info items to display at the bottom of dialog */ |
208 | ||
209 | if (panicDebugging) { | |
210 | int x1, x2; | |
211 | ||
212 | /* | |
213 | * PANIC CALLER | |
214 | * | |
215 | * don't display the panic caller if it is 0 | |
216 | * | |
217 | */ | |
218 | ||
219 | if ( panic_caller != 0 ) { | |
220 | /* Calculate the pixels need to generate the panic number */ | |
221 | panic_dialog_info[panic_dialog_count].pixels = 0; | |
222 | ||
223 | for ( indx=1, count=0; count < 13; count++ ) { | |
224 | if ( versionbuf[count] == '\0' ) | |
225 | break; | |
226 | ||
227 | panic_num_chars[indx++] = versionbuf[count]; | |
228 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( versionbuf[count] ); | |
229 | } | |
230 | ||
231 | panic_num_chars[indx++] = ':'; | |
232 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( ':' ); | |
233 | ||
234 | for ( count=8; count != 0; count-- ) { | |
b0d623f7 | 235 | nibble = (int)((panic_caller >> ((count-1)<<2)) &0xF); |
91447636 A |
236 | panic_num_chars[indx++] = nibble; |
237 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( nibble ); | |
238 | } | |
239 | ||
240 | panic_num_chars[0] = indx; | |
241 | panic_dialog_info[panic_dialog_count].chars = panic_num_chars; | |
242 | panic_dialog_count++; | |
243 | } | |
55e303ae | 244 | |
91447636 A |
245 | /* |
246 | * MAC ADDRESS | |
247 | * | |
248 | * if the mac address is not available, then use ff:ff:ff:ff:ff:ff | |
249 | * | |
250 | */ | |
251 | ||
252 | kdp_mac_addr = kdp_get_mac_addr(); | |
55e303ae | 253 | |
91447636 A |
254 | /* If no mac_addr has been set, then force to -1 */ |
255 | if( ! (kdp_mac_addr.ether_addr_octet[0] || kdp_mac_addr.ether_addr_octet[1] || kdp_mac_addr.ether_addr_octet[2] | |
256 | || kdp_mac_addr.ether_addr_octet[3] || kdp_mac_addr.ether_addr_octet[4] || kdp_mac_addr.ether_addr_octet[5])) { | |
55e303ae | 257 | for (count = 0; count < 6; count++ ) |
91447636 | 258 | kdp_mac_addr.ether_addr_octet[count] = -1; |
55e303ae | 259 | } |
91447636 A |
260 | |
261 | panic_dialog_info[panic_dialog_count].pixels = 0; | |
262 | ||
263 | for (indx=1, count=0; count < 6; count++ ) { | |
264 | nibble = (kdp_mac_addr.ether_addr_octet[count] & 0xf0) >> 4; | |
265 | mac_addr_chars[indx++] = nibble; | |
266 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( nibble ); | |
267 | ||
268 | nibble = kdp_mac_addr.ether_addr_octet[count] & 0xf; | |
269 | mac_addr_chars[indx++] = nibble; | |
270 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( nibble ); | |
271 | ||
272 | if( count < 5 ) { | |
273 | mac_addr_chars[indx++] = ':'; | |
274 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( ':' ); | |
55e303ae A |
275 | } |
276 | } | |
91447636 A |
277 | |
278 | mac_addr_chars[0] = indx; | |
279 | panic_dialog_info[panic_dialog_count].chars = mac_addr_chars; | |
280 | panic_dialog_count++; | |
281 | ||
282 | /* | |
283 | * IP ADDRESS | |
284 | * | |
285 | * do not display the ip addresses if the machine isn't attachable. | |
286 | * there's no sense in possibly confusing people. | |
287 | */ | |
288 | ||
289 | if ( (ip_addr = (unsigned int) ntohl(kdp_get_ip_address())) != 0 ) { | |
290 | int d1, d2, d3; | |
291 | ||
292 | panic_dialog_info[panic_dialog_count].pixels = 0; | |
293 | ||
294 | for ( indx=1, count=0; count < 4; count++ ) { | |
55e303ae A |
295 | nibble = (ip_addr & 0xff000000 ) >> 24; |
296 | ||
91447636 A |
297 | d3 = (nibble % 10) ; nibble = nibble / 10; |
298 | d2 = (nibble % 10) ; nibble = nibble / 10; | |
299 | d1 = (nibble % 10) ; | |
55e303ae | 300 | |
91447636 A |
301 | if( d1 != 0 ) { |
302 | ip_addr_chars[indx++] = d1; | |
303 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( d1 ); | |
304 | } | |
305 | ||
306 | ip_addr_chars[indx++] = d2; | |
307 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( d2 ); | |
308 | ||
309 | ip_addr_chars[indx++] = d3; | |
310 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( d3 ); | |
311 | ||
312 | if ( count < 3 ) { | |
313 | ip_addr_chars[indx++] = '.'; | |
314 | panic_dialog_info[panic_dialog_count].pixels += pixels_needed_to_blit_digit( '.' ); | |
315 | } | |
55e303ae A |
316 | |
317 | d1= d2 = d3 = 0; | |
318 | ip_addr = ip_addr << 8; | |
319 | } | |
91447636 A |
320 | |
321 | ip_addr_chars[0] = indx; | |
322 | panic_dialog_info[panic_dialog_count].chars = ip_addr_chars; | |
323 | panic_dialog_count++; | |
55e303ae | 324 | } |
91447636 A |
325 | |
326 | ||
327 | /* vertical alignment for information to be displayed */ | |
b0d623f7 | 328 | panic_info_y = (int)((vinfo.v_height/2) + panic_dialog->pd_height/2 - (panic_dialog->pd_info_height)); |
91447636 A |
329 | |
330 | /* blit out all the information we gathered */ | |
331 | ||
332 | switch ( panic_dialog_count ) { | |
333 | case 1 : /* one item is centered */ | |
b0d623f7 | 334 | panic_info_x = (int)((vinfo.v_width/2) - (panic_dialog_info[0].pixels/2)); |
91447636 A |
335 | for (indx=1; indx < panic_dialog_info[0].chars[0]; indx++) |
336 | blit_digit(panic_dialog_info[0].chars[indx]); | |
337 | ||
338 | break; | |
339 | ||
340 | case 2 : /* left centered and right centered */ | |
341 | x1 = ((panic_dialog->pd_width/2) - panic_dialog_info[0].pixels)/2; | |
b0d623f7 | 342 | panic_info_x = (int)(((vinfo.v_width/2) - (panic_dialog->pd_width/2)) + x1); |
91447636 A |
343 | |
344 | for (indx=1; indx < panic_dialog_info[0].chars[0]; indx++) | |
345 | blit_digit(panic_dialog_info[0].chars[indx]); | |
346 | ||
347 | x2 = ((panic_dialog->pd_width/2) - panic_dialog_info[1].pixels)/2; | |
b0d623f7 | 348 | panic_info_x = (int)((vinfo.v_width/2) + x2); |
91447636 A |
349 | |
350 | for (indx=1; indx < panic_dialog_info[1].chars[0]; indx++) | |
351 | blit_digit(panic_dialog_info[1].chars[indx]); | |
352 | ||
353 | break; | |
354 | ||
355 | case 3 : /* left centered, middle and right centered */ | |
356 | x1 = ((panic_dialog->pd_width/2) - panic_dialog_info[0].pixels - (panic_dialog_info[1].pixels/2))/2; | |
b0d623f7 | 357 | panic_info_x = (int)(((vinfo.v_width/2) - (panic_dialog->pd_width/2)) + x1); |
91447636 A |
358 | |
359 | for (indx=1; indx < panic_dialog_info[0].chars[0]; indx++) | |
360 | blit_digit(panic_dialog_info[0].chars[indx]); | |
361 | ||
b0d623f7 | 362 | panic_info_x = (int)((vinfo.v_width/2) - (panic_dialog_info[1].pixels/2)); |
91447636 A |
363 | |
364 | for (indx=1; indx < panic_dialog_info[1].chars[0]; indx++) | |
365 | blit_digit(panic_dialog_info[1].chars[indx]); | |
366 | ||
367 | x2 = ((panic_dialog->pd_width/2) - panic_dialog_info[2].pixels - (panic_dialog_info[1].pixels/2))/2; | |
b0d623f7 | 368 | panic_info_x = (int)((vinfo.v_width/2) + x2 + (panic_dialog_info[1].pixels/2)); |
91447636 A |
369 | |
370 | for (indx=1; indx < panic_dialog_info[2].chars[0]; indx++) | |
371 | blit_digit(panic_dialog_info[2].chars[indx]); | |
372 | ||
373 | break; | |
374 | ||
375 | default : /* nothing */ | |
376 | break; | |
377 | ||
378 | } /* switch */ | |
379 | } /* if panic_deugging */ | |
380 | } /* if ! logPanicDataToScreen */ | |
381 | } /* if ! panicDialogDrawn && ! panicDialogDesired */ | |
382 | ||
55e303ae A |
383 | panicDialogDrawn = TRUE; |
384 | panicDialogDesired = FALSE; | |
91447636 A |
385 | } |
386 | ||
387 | ||
388 | /* | |
389 | * This routine installs a new panic dialog | |
390 | * If ptr is NULL, then the default "built-in" panic dialog will be installed. | |
391 | * note: It is the caller that must take care of deallocating memory used for the previous panic dialog | |
392 | */ | |
393 | ||
394 | int | |
395 | panic_dialog_set_image( const unsigned char * ptr, unsigned int size ) | |
396 | { | |
397 | int error; | |
398 | unsigned int newsize; | |
399 | const struct panicimage * newimage; | |
400 | ||
401 | /* if ptr is NULL, restore panic image to built-in default */ | |
402 | if ( ptr == NULL ) { | |
403 | newimage = &panic_dialog_default; | |
b0d623f7 | 404 | newsize = (unsigned int)(sizeof(struct panicimage) + newimage->pd_dataSize); |
91447636 A |
405 | } |
406 | else { | |
2d21ac55 | 407 | newimage = (const struct panicimage *)ptr; |
91447636 A |
408 | newsize = size; |
409 | } | |
410 | ||
411 | if ( (error = panic_dialog_verify( newimage, newsize )) ) | |
412 | return (error); | |
413 | ||
414 | panic_dialog = newimage; | |
415 | panic_dialog_data = &panic_dialog->data[0]; | |
416 | panic_dialog_clut = &panic_dialog->data[panic_dialog->pd_dataSize-CLUT_SIZE]; | |
417 | ||
2d21ac55 | 418 | curr_image_ptr = ptr; |
91447636 A |
419 | curr_image_size = size; |
420 | ||
421 | return (0); | |
422 | } | |
423 | ||
424 | ||
425 | /* | |
426 | * This routines returns the current address of the panic dialog | |
427 | * If the default panic dialog is active, then *ptr will be NULL | |
428 | */ | |
429 | ||
430 | void | |
2d21ac55 | 431 | panic_dialog_get_image(const unsigned char ** ptr, unsigned int * size ) |
91447636 A |
432 | { |
433 | *ptr = curr_image_ptr; | |
434 | *size = curr_image_size; | |
435 | } | |
436 | ||
437 | ||
438 | /* | |
439 | * This routine verifies the panic dialog image is valid. | |
440 | */ | |
441 | ||
442 | static int | |
443 | panic_dialog_verify( const struct panicimage * newimage, unsigned int size ) | |
444 | { | |
445 | unsigned int sum, i; | |
446 | ||
447 | if ( size < (sizeof(struct panicimage) + newimage->pd_dataSize) ) | |
448 | return EINVAL; | |
449 | ||
b0d623f7 | 450 | if ( newimage->pd_tag != 0x524E4D70 /* 'RNMp' */ ) |
91447636 A |
451 | return EINVAL; |
452 | ||
453 | size = newimage->pd_dataSize-CLUT_SIZE; | |
454 | for (sum=0,i=0; i<size; i++) { | |
455 | sum += newimage->data[i]; | |
456 | sum <<= sum&1; | |
457 | } | |
55e303ae | 458 | |
91447636 A |
459 | if ( sum != newimage->pd_sum ) |
460 | return EINVAL; | |
461 | ||
462 | return 0; | |
463 | } | |
464 | ||
465 | ||
466 | /* | |
467 | * Service Routines for managing the panic dialog | |
468 | */ | |
469 | ||
470 | ||
471 | static const struct rendered_num * find_rendered_digit( int digit ); | |
2d21ac55 A |
472 | static void panic_blit_rect_8(unsigned int x, unsigned int y, |
473 | unsigned int width, unsigned int height, | |
474 | int transparent, const unsigned char *dataPtr); | |
475 | static void panic_blit_rect_16(unsigned int x, unsigned int y, | |
476 | unsigned int width, unsigned int height, | |
477 | int transparent, const unsigned char *dataPtr); | |
478 | static void panic_blit_rect_32(unsigned int x, unsigned int y, | |
479 | unsigned int width, unsigned int height, | |
480 | int transparent, const unsigned char *dataPtr); | |
b0d623f7 A |
481 | static void panic_blit_rect_30(unsigned int x, unsigned int y, |
482 | unsigned int width, unsigned int height, | |
483 | int transparent, const unsigned char *dataPtr); | |
2d21ac55 A |
484 | static int decode_rle(const unsigned char *dataPtr, |
485 | unsigned int *quantity, unsigned int *depth, | |
486 | const unsigned char **value); | |
91447636 A |
487 | |
488 | ||
489 | /* Utilities to convert 8 bit/gray */ | |
490 | static unsigned int make24bitcolor( unsigned int index, const unsigned char * clut ); | |
491 | static unsigned char findIndexMatch( unsigned char index ); | |
492 | static unsigned char color24togray8( unsigned int color24 ); | |
493 | static unsigned char findbestgray( unsigned int color24 ); | |
494 | static int isActiveClutOK( void ); | |
495 | ||
496 | static int | |
2d21ac55 | 497 | pixels_needed_to_blit_digit(__unused int digit ) |
91447636 A |
498 | { |
499 | return FONT_WIDTH; | |
55e303ae A |
500 | } |
501 | ||
91447636 A |
502 | |
503 | static const struct rendered_num * | |
504 | find_rendered_digit( int digit ) | |
505 | { | |
506 | //extern unsigned char iso_font[]; | |
507 | const struct rendered_num *digitPtr; | |
508 | ||
509 | if ( digit < 16 ) { | |
510 | if ( digit < 10 ) | |
511 | digit += 0x30; | |
512 | else | |
513 | digit += 0x37; | |
514 | } | |
515 | ||
516 | digitPtr = (const struct rendered_num *) &iso_font[digit * 16]; | |
517 | return digitPtr; | |
518 | } | |
519 | ||
520 | ||
55e303ae A |
521 | static void |
522 | blit_digit( int digit ) | |
523 | { | |
2d21ac55 A |
524 | const unsigned char *raw_data = |
525 | (const unsigned char *)find_rendered_digit(digit); | |
91447636 A |
526 | unsigned width = FONT_WIDTH, height = FONT_HEIGHT; |
527 | int row; | |
528 | ||
529 | for (row=0; row<FONT_HEIGHT; row++) { | |
530 | int j; | |
531 | unsigned char bits; | |
532 | ||
533 | bits = raw_data[row]; | |
534 | for( j=FONT_WIDTH-1; j>=0; j--) { | |
535 | ||
536 | if ( bits & 0x80 ) | |
0c530ab8 | 537 | rendered_font[row][j] = OSSwapBigToHostInt16(0x0100 | panic_dialog->pd_info_color[0]); |
91447636 | 538 | else |
0c530ab8 | 539 | rendered_font[row][j] = OSSwapBigToHostInt16(0x0100 | panic_dialog->pd_info_color[1]); |
91447636 | 540 | bits <<= 1; |
55e303ae | 541 | } |
55e303ae | 542 | } |
91447636 A |
543 | |
544 | panic_blit_rect( panic_info_x, panic_info_y , width, height, 255, (unsigned char *) rendered_font); | |
545 | panic_info_x += width; | |
55e303ae A |
546 | } |
547 | ||
91447636 | 548 | |
55e303ae | 549 | static void |
2d21ac55 A |
550 | panic_blit_rect(unsigned int x, unsigned int y, unsigned int width, |
551 | unsigned int height, int transparent, | |
552 | const unsigned char *dataPtr) | |
55e303ae A |
553 | { |
554 | if(!vinfo.v_depth) | |
555 | return; | |
556 | ||
557 | switch( vinfo.v_depth) { | |
558 | case 8: | |
559 | panic_blit_rect_8( x, y, width, height, transparent, dataPtr); | |
560 | break; | |
561 | case 16: | |
562 | panic_blit_rect_16( x, y, width, height, transparent, dataPtr); | |
563 | break; | |
564 | case 32: | |
565 | panic_blit_rect_32( x, y, width, height, transparent, dataPtr); | |
566 | break; | |
b0d623f7 A |
567 | case 30: |
568 | panic_blit_rect_30( x, y, width, height, transparent, dataPtr); | |
569 | break; | |
55e303ae A |
570 | } |
571 | } | |
572 | ||
91447636 A |
573 | /* |
574 | * panic_blit_rect_8 decodes the RLE encoded image data on the fly, looks up the | |
575 | * color by indexing into the clut, or attempts to find the best index. | |
576 | */ | |
55e303ae | 577 | |
55e303ae | 578 | static void |
2d21ac55 A |
579 | panic_blit_rect_8(unsigned int x, unsigned int y, unsigned int width, |
580 | unsigned int height, __unused int transparent, | |
581 | const unsigned char * dataPtr) | |
55e303ae A |
582 | { |
583 | volatile unsigned char * dst; | |
91447636 A |
584 | unsigned int line, col, i; |
585 | static int clutOK = -1; | |
586 | unsigned int data, quantity, depth; | |
2d21ac55 | 587 | const unsigned char *value; |
55e303ae | 588 | |
91447636 A |
589 | |
590 | if ( clutOK == -1 ) | |
591 | clutOK = isActiveClutOK(); | |
592 | ||
55e303ae | 593 | dst = (volatile unsigned char *) (vinfo.v_baseaddr + |
91447636 A |
594 | (y * vinfo.v_rowbytes) + |
595 | x); | |
55e303ae A |
596 | |
597 | quantity = 0; | |
91447636 | 598 | i = 0; |
55e303ae A |
599 | |
600 | for( line = 0; line < height; line++) { | |
601 | for( col = 0; col < width; col++) { | |
91447636 | 602 | |
55e303ae | 603 | if (quantity == 0) { |
91447636 A |
604 | dataPtr += decode_rle(dataPtr, &quantity, &depth, &value); |
605 | i = 0; | |
55e303ae A |
606 | } |
607 | ||
91447636 A |
608 | if ( clutOK ) |
609 | data = value[i++]; | |
610 | else | |
611 | data = findIndexMatch( value[i++] ); | |
612 | ||
55e303ae | 613 | *(dst + col) = data; |
91447636 A |
614 | |
615 | if ( i == depth ) { | |
616 | i = 0; | |
617 | quantity--; | |
618 | } | |
55e303ae A |
619 | } |
620 | ||
b0d623f7 | 621 | dst = (volatile unsigned char *) (((uintptr_t)dst) + vinfo.v_rowbytes); |
55e303ae A |
622 | } |
623 | } | |
624 | ||
91447636 A |
625 | /* |
626 | * panic_blit_rect_16 decodes the RLE encoded image data on the fly, looks up the | |
627 | * color by indexing into the clut, uses the top 5 bits to fill in each of the three | |
628 | * pixel values (RGB) and writes each pixel to the screen. | |
629 | */ | |
630 | ||
2d21ac55 A |
631 | static void |
632 | panic_blit_rect_16(unsigned int x, unsigned int y, unsigned int width, | |
633 | unsigned int height, __unused int transparent, | |
634 | const unsigned char *dataPtr) | |
635 | { | |
55e303ae | 636 | |
91447636 A |
637 | volatile unsigned short * dst; |
638 | unsigned int line, col, i; | |
639 | unsigned int quantity, index, data, depth; | |
2d21ac55 | 640 | const unsigned char *value; |
55e303ae | 641 | |
91447636 A |
642 | dst = (volatile unsigned short *) (vinfo.v_baseaddr + |
643 | (y * vinfo.v_rowbytes) + | |
644 | (x * 2)); | |
55e303ae | 645 | |
91447636 A |
646 | quantity = 0; |
647 | i = 0; | |
55e303ae | 648 | |
91447636 A |
649 | for( line = 0; line < height; line++) { |
650 | for( col = 0; col < width; col++) { | |
55e303ae | 651 | |
91447636 A |
652 | if (quantity == 0) { |
653 | dataPtr += decode_rle(dataPtr, &quantity, &depth, &value); | |
654 | i = 0; | |
655 | } | |
55e303ae | 656 | |
91447636 A |
657 | index = value[i++] * 3; |
658 | ||
659 | data = ( (unsigned short) (0xf8 & (panic_dialog_clut[index + 0])) << 7) | |
660 | | ( (unsigned short) (0xf8 & (panic_dialog_clut[index + 1])) << 2) | |
661 | | ( (unsigned short) (0xf8 & (panic_dialog_clut[index + 2])) >> 3); | |
55e303ae | 662 | |
91447636 | 663 | *(dst + col) = data; |
55e303ae | 664 | |
91447636 A |
665 | if ( i == depth ) { |
666 | i = 0; | |
667 | quantity--; | |
668 | } | |
669 | } | |
55e303ae | 670 | |
b0d623f7 | 671 | dst = (volatile unsigned short *) (((uintptr_t)dst) + vinfo.v_rowbytes); |
91447636 | 672 | } |
55e303ae A |
673 | } |
674 | ||
91447636 A |
675 | /* |
676 | * panic_blit_rect_32 decodes the RLE encoded image data on the fly, and fills | |
677 | * in each of the three pixel values from the clut (RGB) for each pixel and | |
678 | * writes it to the screen. | |
55e303ae | 679 | */ |
91447636 | 680 | |
2d21ac55 A |
681 | static void |
682 | panic_blit_rect_32(unsigned int x, unsigned int y, unsigned int width, | |
683 | unsigned int height, __unused int transparent, | |
684 | const unsigned char *dataPtr) | |
685 | { | |
91447636 A |
686 | volatile unsigned int * dst; |
687 | unsigned int line, col, i; | |
688 | unsigned int quantity, index, data, depth; | |
2d21ac55 | 689 | const unsigned char *value; |
55e303ae | 690 | |
55e303ae A |
691 | |
692 | dst = (volatile unsigned int *) (vinfo.v_baseaddr + | |
91447636 A |
693 | (y * vinfo.v_rowbytes) + |
694 | (x * 4)); | |
55e303ae A |
695 | |
696 | quantity = 0; | |
91447636 | 697 | i = 0; |
55e303ae A |
698 | |
699 | for( line = 0; line < height; line++) { | |
700 | for( col = 0; col < width; col++) { | |
91447636 | 701 | |
55e303ae | 702 | if (quantity == 0) { |
91447636 A |
703 | dataPtr += decode_rle(dataPtr, &quantity, &depth, &value); |
704 | i = 0; | |
55e303ae | 705 | } |
91447636 A |
706 | |
707 | index = value[i++] * 3; | |
55e303ae | 708 | |
91447636 A |
709 | data = ( (unsigned int) panic_dialog_clut[index + 0] << 16) |
710 | | ( (unsigned int) panic_dialog_clut[index + 1] << 8) | |
711 | | ( (unsigned int) panic_dialog_clut[index + 2]); | |
55e303ae A |
712 | |
713 | *(dst + col) = data; | |
91447636 A |
714 | |
715 | if ( i == depth ) { | |
716 | i = 0; | |
717 | quantity--; | |
718 | } | |
55e303ae A |
719 | } |
720 | ||
b0d623f7 | 721 | dst = (volatile unsigned int *) (((uintptr_t)dst) + vinfo.v_rowbytes); |
55e303ae A |
722 | } |
723 | } | |
724 | ||
b0d623f7 A |
725 | /* |
726 | * panic_blit_rect_30 decodes the RLE encoded image data on the fly, and fills | |
727 | * in each of the three pixel values from the clut (RGB) for each pixel and | |
728 | * writes it to the screen. | |
729 | */ | |
730 | ||
731 | static void | |
732 | panic_blit_rect_30(unsigned int x, unsigned int y, unsigned int width, | |
733 | unsigned int height, __unused int transparent, | |
734 | const unsigned char *dataPtr) | |
735 | { | |
736 | volatile unsigned int * dst; | |
737 | unsigned int line, col, i; | |
738 | unsigned int quantity, index, data, depth; | |
739 | const unsigned char *value; | |
740 | unsigned int in; | |
741 | ||
742 | dst = (volatile unsigned int *) (vinfo.v_baseaddr + | |
743 | (y * vinfo.v_rowbytes) + | |
744 | (x * 4)); | |
745 | ||
746 | quantity = 0; | |
747 | i = 0; | |
748 | ||
749 | for( line = 0; line < height; line++) { | |
750 | for( col = 0; col < width; col++) { | |
751 | ||
752 | if (quantity == 0) { | |
753 | dataPtr += decode_rle(dataPtr, &quantity, &depth, &value); | |
754 | i = 0; | |
755 | } | |
756 | ||
757 | index = value[i++] * 3; | |
758 | in = panic_dialog_clut[index + 0]; | |
759 | data = (in << 2) | (in >> 6); | |
760 | ||
761 | in = panic_dialog_clut[index + 1]; | |
762 | data |= (in << (2 + 10)) | ((3 << 10) & (in << 4)); | |
763 | ||
764 | in = panic_dialog_clut[index + 2]; | |
765 | data |= (in << (2 + 20)) | ((3 << 20) & (in << 14)); | |
766 | ||
767 | *(dst + col) = data; | |
768 | ||
769 | if ( i == depth ) { | |
770 | i = 0; | |
771 | quantity--; | |
772 | } | |
773 | } | |
774 | ||
775 | dst = (volatile unsigned int *) (((uintptr_t)dst) + vinfo.v_rowbytes); | |
776 | } | |
777 | } | |
778 | ||
779 | ||
55e303ae | 780 | /* |
91447636 A |
781 | decode_rle decodes a single quantity/value run of a "modified-RLE" encoded |
782 | image. The encoding works as follows: | |
783 | ||
784 | The run is described in the first byte. If the MSB is zero, then the next seven bits | |
785 | are the quantity of bytes that follow that make up the run of value bytes. (see case 0) | |
786 | ||
787 | If the MSB is set, bits 0-3 are the quantity's least significant 4 bits. If bit 5 is set, | |
788 | then the quantity is further described in the next byte, where an additional 7 bits (4-10) | |
789 | worth of quantity will be found. If the MSB of this byte is set, then an additional | |
790 | 7 bits (11-17) worth of quantity will be found in the next byte. This repeats until the MSB of | |
791 | a quantity byte is zero, thus ending the run of quantity bytes. | |
792 | ||
793 | Bits 5/6 of the first byte, describe the number of bytes in the value run following the quantity run. | |
794 | These bits describe value runs of 1 to 4 bytes. And the quantity describe the number of value runs. | |
795 | (see cases 1-4) | |
796 | ||
797 | encodings are: (q = quantity, v = value, c = quantity continues) | |
798 | ||
799 | case 0: [ 0 q6-q0 ] [ v7-v0 ] ... [ v7-v0 ] | |
800 | case 1: [ 1 0 0 c q3-q0 ] [ c q10-q4 ] [ c q17-q11 ] [ q24-q18 ] [ v7-v0 ] | |
801 | case 2: [ 1 0 1 c q3-q0 ] [ c q10-q4 ] [ c q17-q11 ] [ q24-q18 ] [ v7-v0 ] [ v7-v0 ] | |
802 | case 3: [ 1 1 0 c q3-q0 ] [ c q10-q4 ] [ c q17-q11 ] [ q24-q18 ] [ v7-v0 ] [ v7-v0 ] [ v7-v0 ] | |
803 | case 4: [ 1 1 1 c q3-q0 ] [ c q10-q4 ] [ c q17-q11 ] [ q24-q18 ] [ v7-v0 ] [ v7-v0 ] [ v7-v0 ] [ v7-v0 ] | |
55e303ae | 804 | */ |
91447636 | 805 | |
55e303ae | 806 | static int |
2d21ac55 A |
807 | decode_rle(const unsigned char *dataPtr, unsigned int *quantity, |
808 | unsigned int *depth, const unsigned char **value ) | |
55e303ae | 809 | { |
91447636 A |
810 | unsigned int mask; |
811 | int i, runlen, runsize; | |
812 | ||
813 | i = 0; | |
814 | mask = dataPtr[i] & 0xF0; | |
815 | ||
816 | if ( mask & 0x80 ) { | |
817 | runsize = ((mask & 0x60) >> 5) + 1; | |
818 | runlen = dataPtr[i++] & 0x0F; | |
819 | ||
820 | if ( mask & 0x10 ) { | |
821 | int shift = 4; | |
822 | ||
823 | do { | |
824 | mask = dataPtr[i] & 0x80; | |
825 | runlen |= ((dataPtr[i++] & 0x7F) << shift); | |
826 | shift+=7; | |
827 | } while (mask); | |
828 | } | |
55e303ae | 829 | } else { |
91447636 A |
830 | runlen = 1; |
831 | runsize = dataPtr[i++]; | |
55e303ae | 832 | } |
91447636 A |
833 | |
834 | *depth = runsize; | |
835 | *quantity = runlen; | |
836 | *value = &dataPtr[i]; | |
837 | ||
838 | return i+runsize; | |
55e303ae A |
839 | } |
840 | ||
91447636 | 841 | |
91447636 | 842 | /* From user mode Libc - this ought to be in a library */ |
2d21ac55 | 843 | static const char * |
91447636 A |
844 | strnstr(const char * s, const char * find, size_t slen) |
845 | { | |
846 | char c, sc; | |
847 | size_t len; | |
848 | ||
849 | if ((c = *find++) != '\0') { | |
850 | len = strlen(find); | |
851 | do { | |
852 | do { | |
853 | if ((sc = *s++) == '\0' || slen-- < 1) | |
854 | return (NULL); | |
855 | } while (sc != c); | |
856 | if (len > slen) | |
857 | return (NULL); | |
858 | } while (strncmp(s, find, len) != 0); | |
859 | s--; | |
860 | } | |
2d21ac55 | 861 | return s; |
91447636 A |
862 | } |
863 | ||
864 | /* | |
865 | * these routines are for converting a color into grayscale | |
866 | * in 8-bit mode, if the active clut is different than the | |
867 | * clut used to create the panic dialog, then we must convert to gray | |
868 | */ | |
869 | ||
870 | static unsigned int | |
871 | make24bitcolor( unsigned int index, const unsigned char * clut ) | |
872 | { | |
873 | unsigned int color24 = 0; | |
874 | int i = index * 3; | |
875 | ||
876 | color24 |= clut[i+0] << 16; | |
877 | color24 |= clut[i+1] << 8; | |
878 | color24 |= clut[i+2]; | |
879 | ||
880 | return color24; | |
881 | } | |
882 | ||
883 | ||
884 | static unsigned char | |
885 | findbestgray( unsigned int color24 ) | |
886 | { | |
887 | unsigned int c24, rel, bestindex=-1, bestgray = -1; | |
888 | unsigned char gray8, c8; | |
889 | int i; | |
890 | #define abs(v) ((v) > 0)?(v):-(v) | |
55e303ae | 891 | |
91447636 A |
892 | gray8 = color24togray8( color24 ); /* convert the original color into grayscale */ |
893 | ||
894 | for (i=0; i<CLUT_ENTRIES; i++) { | |
895 | c24 = make24bitcolor( i, active_clut ); | |
896 | if ( (((c24>>16)&0xff) != ((c24>>8)&0xff)) || ((c24>>8)&0xff) != (c24 & 0xff) ) | |
897 | continue; /* only match against grays */ | |
898 | ||
899 | c8 = c24 & 0xFF; /* isolate the gray */ | |
900 | ||
901 | /* find the gray with the smallest difference */ | |
902 | rel = abs( gray8 - c8 ); | |
903 | if ( rel < bestgray ) { | |
904 | bestgray = rel; | |
905 | bestindex = i; | |
906 | } | |
55e303ae A |
907 | } |
908 | ||
91447636 | 909 | /* Did we fail to find any grays ? */ |
2d21ac55 | 910 | if (ULONG_MAX == bestindex) { |
91447636 A |
911 | /* someday we should look for the best color match */ |
912 | /* but for now just return the gray as the index */ | |
913 | /* at least there might be something readble on the display */ | |
914 | ||
915 | bestindex = gray8; | |
916 | } | |
917 | ||
918 | return bestindex; | |
919 | #undef abs | |
55e303ae A |
920 | } |
921 | ||
91447636 A |
922 | |
923 | static unsigned char | |
924 | color24togray8( unsigned int color24 ) | |
925 | { | |
0c530ab8 A |
926 | int R, G, B; |
927 | int Gray; | |
91447636 A |
928 | unsigned char gray8; |
929 | ||
930 | R = (color24 & 0xFF0000) >> 16 ; | |
931 | G = (color24 & 0xFF00) >> 8 ; | |
932 | B = (color24 & 0xFF); | |
933 | ||
0c530ab8 A |
934 | Gray = (R*30) + (G*59) + (B*11); |
935 | gray8 = (unsigned char) ((Gray + 50) / 100); | |
91447636 | 936 | return gray8; |
2d21ac55 | 937 | } |
91447636 A |
938 | |
939 | ||
940 | static unsigned char | |
941 | findIndexMatch( unsigned char index ) | |
55e303ae | 942 | { |
91447636 A |
943 | static unsigned int last_in_index = -1; |
944 | static unsigned char last_index; | |
945 | unsigned int sc; | |
55e303ae | 946 | |
91447636 A |
947 | if ( index == last_in_index ) |
948 | return last_index; | |
55e303ae | 949 | |
91447636 A |
950 | last_in_index = index; |
951 | sc = make24bitcolor( index, panic_dialog_clut ); | |
952 | last_index = findbestgray( sc ); /* find the nearest matching gray in the active clut */ | |
55e303ae | 953 | |
91447636 A |
954 | return last_index; |
955 | } | |
55e303ae | 956 | |
91447636 A |
957 | static int |
958 | isActiveClutOK( void ) | |
959 | { | |
960 | int i; | |
961 | int r = 1; /* assume OK */ | |
55e303ae | 962 | |
91447636 A |
963 | for (i=0; i<CLUT_ENTRIES; i++) { |
964 | if ( panic_dialog_clut[i] == active_clut[i] ) continue; | |
965 | r = 0; | |
966 | break; | |
55e303ae A |
967 | } |
968 | ||
91447636 | 969 | return r; |
55e303ae | 970 | } |