| #include <stdio.h> |
| #include <stdbool.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <signal.h> |
| #include <setjmp.h> |
| |
| typedef enum exit_codes_ { |
| |
| #if defined(VGA_ppc32) || defined(VGA_ppc64) |
| /* If the insn that got queried for: exists */ |
| POWER_INSN_AVAILABLE = 0, |
| /* If the insn that got queried for: does not exist on this platform */ |
| POWER_INSN_UNAVAILABLE = 1, |
| /* If the insn that got queried for: does not exist in the vocabulary of this program */ |
| POWER_INSN_UNRECOGNIZED = 2, |
| |
| /* Note: Please keep USAGE_ERROR last. */ |
| USAGE_ERROR |
| #else |
| /* When not on a POWER system: */ |
| NOT_POWER_ARCH = 255, |
| #endif |
| |
| } exit_code; |
| |
| #if defined(VGA_ppc32) || defined(VGA_ppc64) |
| /* Signal Handling support for unsupported instructions. */ |
| static jmp_buf unsup_insn_env; |
| static void unsup_insn_handler(int signal_number) |
| { |
| if (signal_number == SIGILL) |
| longjmp(unsup_insn_env, 1); |
| return; |
| } |
| static struct sigaction unsup_insn_action = (struct sigaction) { |
| .sa_handler = &unsup_insn_handler, |
| }; |
| |
| /* Instruction existence tests. */ |
| static bool dcbzl_available(void) |
| { |
| #define MAX_DCBZL_SZB (128) /* largest known effect of dcbzl */ |
| char *test_block = NULL; |
| register char *rb asm ("r14"); |
| int err; |
| bool dcbzl_exists = false; |
| |
| err = posix_memalign ((void **)&test_block, MAX_DCBZL_SZB, 4 * MAX_DCBZL_SZB); |
| if (err) { |
| fprintf(stderr, "posix_memalign() failed (err = %d [%s])\n", err, strerror(err)); |
| return err; |
| } |
| |
| rb = test_block; |
| |
| if (setjmp(unsup_insn_env) != 0) |
| dcbzl_exists = false; |
| else { |
| sigaction(SIGILL, &unsup_insn_action, NULL); |
| asm volatile ("dcbzl 0, %[RB]" : : [RB] "r" (rb)); |
| dcbzl_exists = true; |
| } |
| |
| free(test_block); |
| return dcbzl_exists; |
| } |
| #endif |
| |
| /* main() */ |
| int main(int argc, char **argv) |
| { |
| exit_code status; |
| |
| #if defined(VGA_ppc32) || defined(VGA_ppc64) |
| char *insn; |
| if (argc != 2) { |
| fprintf(stderr, "usage: power_insn_available <insn>\n" ); |
| exit(USAGE_ERROR); |
| } |
| |
| insn = argv[1]; |
| if (strcmp (insn, "dcbzl") == 0) |
| status = ((dcbzl_available ()) ? POWER_INSN_AVAILABLE : POWER_INSN_UNAVAILABLE); |
| else |
| /* power_insn_available has not been taught anything about this insn yet. */ |
| status = POWER_INSN_UNRECOGNIZED; |
| #else |
| status = NOT_POWER_ARCH; |
| #endif |
| return status; |
| } |