#include #include #include #include #include #include #include #include #include #include #include #define PAGEMAP_ENTRY 8 #define GET_BIT(X,Y) (X & ((uint64_t)1<> Y #define GET_PFN(X) X & 0x7FFFFFFFFFFFFF #define ALLOC_PAGES 1 const int __endian_bit = 1; #define is_bigendian() ( (*(char*)&__endian_bit) == 0 ) /* * Set the early kill mode reaction state to MCE error. */ static void early_reaction() { printf("Setting Early kill... "); if (prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0) == 0) printf("Ok\n"); else printf("Failure !\n"); } /* * Generate an error on the given page. */ static void memory_error_advise(void* virtual_page) { int ret; ret = madvise(virtual_page, 4096, MADV_HWPOISON); if (ret) printf("Poisoning failed - madvise: %s", strerror(errno)); } /* * Return the physical address associated to a given local virtual address, * or -1 in case of an error. */ static uint64_t physical_address(uint64_t virt_addr) { char path_buf [0x100]; FILE * f; uint64_t read_val, file_offset, pfn = 0; long pgsz; unsigned char c_buf[PAGEMAP_ENTRY]; pid_t my_pid = getpid(); int status, i; sprintf(path_buf, "/proc/%u/pagemap", my_pid); f = fopen(path_buf, "rb"); if(!f){ printf("Error! Cannot open %s\n", path_buf); return (uint64_t)-1; } //Shifting by virt-addr-offset number of bytes //and multiplying by the size of an address //(the size of an entry in pagemap file) pgsz = sysconf(_SC_PAGESIZE); file_offset = virt_addr / (uint64_t)pgsz * PAGEMAP_ENTRY; status = fseek(f, (long)file_offset, SEEK_SET); if(status){ perror("Failed to do fseek!"); fclose(f); return (uint64_t)-1; } for(i=0; i < PAGEMAP_ENTRY; i++){ int c = getc(f); if(c==EOF){ fclose(f); return (uint64_t)-1; } if(is_bigendian()) c_buf[i] = (unsigned char)c; else c_buf[PAGEMAP_ENTRY - i - 1] = (unsigned char)c; } fclose(f); read_val = 0; for(i=0; i < PAGEMAP_ENTRY; i++){ read_val = (read_val << 8) + c_buf[i]; } if(GET_BIT(read_val, 63)) { // Bit 63 page present pfn = GET_PFN(read_val); } else { printf("Page not present !\n"); } if(GET_BIT(read_val, 62)) // Bit 62 page swapped printf("Page swapped\n"); if (pfn == 0) return (uint64_t)-1; return pfn * (uint64_t)pgsz; } /* * SIGBUS handler to display the given information. */ static void sigbus_action(int signum, siginfo_t *siginfo, void *ctx) { printf("Signal %d received: ", signum); printf("%s on vaddr: %llx\n", (siginfo->si_code == 4? "BUS_MCEERR_AR":"BUS_MCEERR_AO"), siginfo->si_addr); if (siginfo->si_code == 4) { /* BUS_MCEERR_AR */ fprintf(stderr, "Exit from the signal handler on BUS_MCEERR_AR\n"); _exit(1); } } int main(int argc, char ** argv) { int opt, early_react = 0, madvise_error=0, wait_time=5, i; struct sigaction my_sigaction; uint64_t virt_addr = 0, phys_addr; void *local_pnt; // Need to have the CAP_SYS_ADMIN capability to get PFNs values in pagemap. if (getuid() != 0) { fprintf(stderr, "Usage: %s needs to run as root\n", argv[0]); exit(EXIT_FAILURE); } while ((opt = getopt(argc, argv, "emw:")) != -1) { switch (opt) { case 'e': early_react = 1; break; case 'm': madvise_error=1; break; case 'w': wait_time=atoi(optarg); break; default: /* '?' */ fprintf(stderr, "Usage: %s [-e] [-m] [-w seconds]\n", argv[0]); exit(EXIT_FAILURE); } } // attach our SIGBUS handler. my_sigaction.sa_sigaction = sigbus_action; my_sigaction.sa_flags = SA_SIGINFO | SA_NODEFER | SA_SIGINFO; if (sigaction(SIGBUS, &my_sigaction, NULL) == -1) { perror("Signal handler attach failed"); exit(EXIT_FAILURE); } if (early_react) early_reaction(); // Allocate nx4K private pages. local_pnt = mmap(NULL, ALLOC_PAGES*4096, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if (local_pnt == MAP_FAILED) { fprintf(stderr, "Memory Allocation failed !\n"); exit(EXIT_FAILURE); } virt_addr = (uint64_t)local_pnt; // Dirty / map the pages. for (i=0; i < ALLOC_PAGES; i++) { sprintf(((char *)local_pnt + i*4096), "My page number %d\n", i); } phys_addr = physical_address(virt_addr); if (phys_addr == -1) { fprintf(stderr, "Virtual address translation 0x%llx failed\n", (unsigned long long)virt_addr); exit(EXIT_FAILURE); } printf("\nData pages at 0x%llx physically 0x%llx\n", (unsigned long long)virt_addr, (unsigned long long)phys_addr); fflush(stdout); // Explicit error if (madvise_error) memory_error_advise((void*) virt_addr); // Now Wait ! if (wait_time > 0) { sleep((unsigned int)wait_time); } else { printf("\nPress ENTER to continue with page reading\n"); i = fgetc(stdin); } // read the strings at the beginning of each page. for (i=0; i < ALLOC_PAGES; i++) { printf("%s", ((char *)local_pnt + i*4096)); } phys_addr = physical_address(virt_addr); if (phys_addr == -1) { fprintf(stderr, "Virtual address translation 0x%llx failed\n", (unsigned long long)virt_addr); } else { printf("\nData pages at 0x%llx physically 0x%llx\n", (unsigned long long)virt_addr, (unsigned long long)phys_addr); } return 0; }