diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:56:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 02:56:35 +0000 |
commit | eba0cfa6b0bef4f2e73c8630a7efa3944df8b0f8 (patch) | |
tree | 74c37eede1f0634cc5de1c63c934edaa1630c6bc /util_lib | |
parent | Initial commit. (diff) | |
download | kexec-tools-7bbe6b040c9123991377749057cfc0356c289ceb.tar.xz kexec-tools-7bbe6b040c9123991377749057cfc0356c289ceb.zip |
Adding upstream version 1:2.0.27.upstream/1%2.0.27upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'util_lib')
-rw-r--r-- | util_lib/Makefile | 24 | ||||
-rw-r--r-- | util_lib/compute_ip_checksum.c | 102 | ||||
-rw-r--r-- | util_lib/elf_info.c | 1284 | ||||
-rw-r--r-- | util_lib/include/elf_info.h | 36 | ||||
-rw-r--r-- | util_lib/include/ip_checksum.h | 8 | ||||
-rw-r--r-- | util_lib/include/sha256.h | 22 | ||||
-rw-r--r-- | util_lib/sha256.c | 368 |
7 files changed, 1844 insertions, 0 deletions
diff --git a/util_lib/Makefile b/util_lib/Makefile new file mode 100644 index 0000000..4a6213a --- /dev/null +++ b/util_lib/Makefile @@ -0,0 +1,24 @@ +# +# Utility function library +# +UTIL_LIB_SRCS += +UTIL_LIB_SRCS += util_lib/compute_ip_checksum.c +UTIL_LIB_SRCS += util_lib/elf_info.c +UTIL_LIB_SRCS += util_lib/sha256.c +UTIL_LIB_OBJS =$(call objify, $(UTIL_LIB_SRCS)) +UTIL_LIB_DEPS =$(call depify, $(UTIL_LIB_OBJS)) +UTIL_LIB = libutil.a + +-include $(UTIL_LIB_DEPS) + +dist += util_lib/Makefile $(UTIL_LIB_SRCS) \ + util_lib/include/elf_info.h util_lib/include/sha256.h \ + util_lib/include/ip_checksum.h +clean += $(UTIL_LIB_OBJS) $(UTIL_LIB_DEPS) $(UTIL_LIB) + +$(UTIL_LIB): CPPFLAGS += -I$(srcdir)/util_lib/include + +$(UTIL_LIB): $(UTIL_LIB_OBJS) + @$(MKDIR) -p $(@D) + $(AR) rs $(UTIL_LIB) $(UTIL_LIB_OBJS) + diff --git a/util_lib/compute_ip_checksum.c b/util_lib/compute_ip_checksum.c new file mode 100644 index 0000000..39a371b --- /dev/null +++ b/util_lib/compute_ip_checksum.c @@ -0,0 +1,102 @@ +/* + * kexec: Linux boots Linux + * + * Copyright (C) 2003,2004 Eric Biederman (ebiederm@xmission.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * This program is distributed in the hope that 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <stdint.h> +#include <ip_checksum.h> +#include <string.h> + +unsigned long compute_ip_checksum(void *addr, unsigned long length) +{ + uint16_t *ptr; + unsigned long sum; + unsigned long len; + unsigned long laddr; + /* compute an ip style checksum */ + laddr = (unsigned long )addr; + sum = 0; + if (laddr & 1) { + uint16_t buffer; + unsigned char *ptr; + /* copy the first byte into a 2 byte buffer. + * This way automatically handles the endian question + * of which byte (low or high) the last byte goes in. + */ + buffer = 0; + ptr = addr; + memcpy(&buffer, ptr, 1); + sum += buffer; + if (sum > 0xFFFF) + sum -= 0xFFFF; + length -= 1; + addr = ptr +1; + + } + len = length >> 1; + ptr = addr; + while (len--) { + sum += *(ptr++); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + addr = ptr; + if (length & 1) { + uint16_t buffer; + unsigned char *ptr; + /* copy the last byte into a 2 byte buffer. + * This way automatically handles the endian question + * of which byte (low or high) the last byte goes in. + */ + buffer = 0; + ptr = addr; + memcpy(&buffer, ptr, 1); + sum += buffer; + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + return (~sum) & 0xFFFF; + +} + +unsigned long add_ip_checksums(unsigned long offset, unsigned long sum, unsigned long new) +{ + unsigned long checksum; + sum = ~sum & 0xFFFF; + new = ~new & 0xFFFF; + if (offset & 1) { + /* byte swap the sum if it came from an odd offset + * since the computation is endian independant this + * works. + */ + new = ((new >> 8) & 0xff) | ((new << 8) & 0xff00); + } + checksum = sum + new; + if (checksum > 0xFFFF) { + checksum -= 0xFFFF; + } + return (~checksum) & 0xFFFF; +} + +unsigned long negate_ip_checksum(unsigned long sum) +{ + sum = ~sum & 0xFFFF; + + sum = 0xFFFF - sum; + + return ~sum & 0xFFFF; +} diff --git a/util_lib/elf_info.c b/util_lib/elf_info.c new file mode 100644 index 0000000..ce71c60 --- /dev/null +++ b/util_lib/elf_info.c @@ -0,0 +1,1284 @@ +/* + * elf_info.c: Architecture independent code for parsing elf files. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation (version 2 of the License). + * + * This program is distributed in the hope that 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, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <elf_info.h> + +/* The 32bit and 64bit note headers make it clear we don't care */ +typedef Elf32_Nhdr Elf_Nhdr; + +const char *fname; +static Elf64_Ehdr ehdr; +static Elf64_Phdr *phdr; +static int num_pt_loads; + +static char osrelease[4096]; + +/* VMCOREINFO symbols for lockless printk ringbuffer */ +static loff_t prb_vaddr; +static size_t printk_ringbuffer_sz; +static size_t prb_desc_sz; +static size_t printk_info_sz; +static uint64_t printk_ringbuffer_desc_ring_offset; +static uint64_t printk_ringbuffer_text_data_ring_offset; +static uint64_t prb_desc_ring_count_bits_offset; +static uint64_t prb_desc_ring_descs_offset; +static uint64_t prb_desc_ring_infos_offset; +static uint64_t prb_data_ring_size_bits_offset; +static uint64_t prb_data_ring_data_offset; +static uint64_t prb_desc_ring_head_id_offset; +static uint64_t prb_desc_ring_tail_id_offset; +static uint64_t atomic_long_t_counter_offset; +static uint64_t prb_desc_state_var_offset; +static uint64_t prb_desc_info_offset; +static uint64_t prb_desc_text_blk_lpos_offset; +static uint64_t prb_data_blk_lpos_begin_offset; +static uint64_t prb_data_blk_lpos_next_offset; +static uint64_t printk_info_seq_offset; +static uint64_t printk_info_caller_id_offset; +static uint64_t printk_info_ts_nsec_offset; +static uint64_t printk_info_level_offset; +static uint64_t printk_info_text_len_offset; + +static loff_t log_buf_vaddr; +static loff_t log_end_vaddr; +static loff_t log_buf_len_vaddr; +static loff_t logged_chars_vaddr; + +/* record format logs */ +static loff_t log_first_idx_vaddr; +static loff_t log_next_idx_vaddr; + +/* struct printk_log (or older log) size */ +static uint64_t log_sz; + +/* struct printk_log (or older log) field offsets */ +static uint64_t log_offset_ts_nsec = UINT64_MAX; +static uint16_t log_offset_len = UINT16_MAX; +static uint16_t log_offset_text_len = UINT16_MAX; + +static uint64_t phys_offset = UINT64_MAX; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define ELFDATANATIVE ELFDATA2LSB +#elif __BYTE_ORDER == __BIG_ENDIAN +#define ELFDATANATIVE ELFDATA2MSB +#else +#error "Unknown machine endian" +#endif + +static uint16_t file16_to_cpu(uint16_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_16(val); + return val; +} + +static uint32_t file32_to_cpu(uint32_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_32(val); + return val; +} + +static uint64_t file64_to_cpu(uint64_t val) +{ + if (ehdr.e_ident[EI_DATA] != ELFDATANATIVE) + val = bswap_64(val); + return val; +} + +static uint64_t vaddr_to_offset(uint64_t vaddr) +{ + /* Just hand the simple case where kexec gets + * the virtual address on the program headers right. + */ + ssize_t i; + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdr[i].p_vaddr > vaddr) + continue; + if ((phdr[i].p_vaddr + phdr[i].p_memsz) <= vaddr) + continue; + return (vaddr - phdr[i].p_vaddr) + phdr[i].p_offset; + } + fprintf(stderr, "No program header covering vaddr 0x%llxfound kexec bug?\n", + (unsigned long long)vaddr); + exit(30); +} + +static unsigned machine_pointer_bits(void) +{ + uint8_t bits = 0; + + /* Default to the size of the elf class */ + switch(ehdr.e_ident[EI_CLASS]) { + case ELFCLASS32: bits = 32; break; + case ELFCLASS64: bits = 64; break; + } + + /* Report the architectures pointer size */ + switch(ehdr.e_machine) { + case EM_386: bits = 32; break; + } + + return bits; +} + +void read_elf32(int fd) +{ + Elf32_Ehdr ehdr32; + Elf32_Phdr *phdr32; + size_t phdrs32_size; + ssize_t ret, i; + + ret = pread(fd, &ehdr32, sizeof(ehdr32), 0); + if (ret != sizeof(ehdr32)) { + fprintf(stderr, "Read of Elf header from %s failed: %s\n", + fname, strerror(errno)); + exit(10); + } + + ehdr.e_type = file16_to_cpu(ehdr32.e_type); + ehdr.e_machine = file16_to_cpu(ehdr32.e_machine); + ehdr.e_version = file32_to_cpu(ehdr32.e_version); + ehdr.e_entry = file32_to_cpu(ehdr32.e_entry); + ehdr.e_phoff = file32_to_cpu(ehdr32.e_phoff); + ehdr.e_shoff = file32_to_cpu(ehdr32.e_shoff); + ehdr.e_flags = file32_to_cpu(ehdr32.e_flags); + ehdr.e_ehsize = file16_to_cpu(ehdr32.e_ehsize); + ehdr.e_phentsize = file16_to_cpu(ehdr32.e_phentsize); + ehdr.e_phnum = file16_to_cpu(ehdr32.e_phnum); + ehdr.e_shentsize = file16_to_cpu(ehdr32.e_shentsize); + ehdr.e_shnum = file16_to_cpu(ehdr32.e_shnum); + ehdr.e_shstrndx = file16_to_cpu(ehdr32.e_shstrndx); + + if (ehdr.e_version != EV_CURRENT) { + fprintf(stderr, "Bad Elf header version %u\n", + ehdr.e_version); + exit(11); + } + if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { + fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", + ehdr.e_phentsize, sizeof(Elf32_Phdr)); + exit(12); + } + phdrs32_size = ehdr.e_phnum * sizeof(Elf32_Phdr); + phdr32 = calloc(ehdr.e_phnum, sizeof(Elf32_Phdr)); + if (!phdr32) { + fprintf(stderr, "Calloc of %u phdrs32 failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(14); + } + phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr) { + fprintf(stderr, "Calloc of %u phdrs failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(15); + } + ret = pread(fd, phdr32, phdrs32_size, ehdr.e_phoff); + if (ret < 0 || (size_t)ret != phdrs32_size) { + fprintf(stderr, "Read of program header @ 0x%llu for %zu bytes failed: %s\n", + (unsigned long long)ehdr.e_phoff, phdrs32_size, strerror(errno)); + exit(16); + } + for (i = 0; i < ehdr.e_phnum; i++) { + phdr[i].p_type = file32_to_cpu(phdr32[i].p_type); + phdr[i].p_offset = file32_to_cpu(phdr32[i].p_offset); + phdr[i].p_vaddr = file32_to_cpu(phdr32[i].p_vaddr); + phdr[i].p_paddr = file32_to_cpu(phdr32[i].p_paddr); + phdr[i].p_filesz = file32_to_cpu(phdr32[i].p_filesz); + phdr[i].p_memsz = file32_to_cpu(phdr32[i].p_memsz); + phdr[i].p_flags = file32_to_cpu(phdr32[i].p_flags); + phdr[i].p_align = file32_to_cpu(phdr32[i].p_align); + + if (phdr[i].p_type == PT_LOAD) + num_pt_loads++; + } + free(phdr32); +} + +void read_elf64(int fd) +{ + Elf64_Ehdr ehdr64; + Elf64_Phdr *phdr64; + size_t phdrs_size; + ssize_t ret, i; + + ret = pread(fd, &ehdr64, sizeof(ehdr64), 0); + if (ret < 0 || (size_t)ret != sizeof(ehdr)) { + fprintf(stderr, "Read of Elf header from %s failed: %s\n", + fname, strerror(errno)); + exit(10); + } + + ehdr.e_type = file16_to_cpu(ehdr64.e_type); + ehdr.e_machine = file16_to_cpu(ehdr64.e_machine); + ehdr.e_version = file32_to_cpu(ehdr64.e_version); + ehdr.e_entry = file64_to_cpu(ehdr64.e_entry); + ehdr.e_phoff = file64_to_cpu(ehdr64.e_phoff); + ehdr.e_shoff = file64_to_cpu(ehdr64.e_shoff); + ehdr.e_flags = file32_to_cpu(ehdr64.e_flags); + ehdr.e_ehsize = file16_to_cpu(ehdr64.e_ehsize); + ehdr.e_phentsize = file16_to_cpu(ehdr64.e_phentsize); + ehdr.e_phnum = file16_to_cpu(ehdr64.e_phnum); + ehdr.e_shentsize = file16_to_cpu(ehdr64.e_shentsize); + ehdr.e_shnum = file16_to_cpu(ehdr64.e_shnum); + ehdr.e_shstrndx = file16_to_cpu(ehdr64.e_shstrndx); + + if (ehdr.e_version != EV_CURRENT) { + fprintf(stderr, "Bad Elf header version %u\n", + ehdr.e_version); + exit(11); + } + if (ehdr.e_phentsize != sizeof(Elf64_Phdr)) { + fprintf(stderr, "Bad Elf progra header size %u expected %zu\n", + ehdr.e_phentsize, sizeof(Elf64_Phdr)); + exit(12); + } + phdrs_size = ehdr.e_phnum * sizeof(Elf64_Phdr); + phdr64 = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr64) { + fprintf(stderr, "Calloc of %u phdrs64 failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(14); + } + phdr = calloc(ehdr.e_phnum, sizeof(Elf64_Phdr)); + if (!phdr) { + fprintf(stderr, "Calloc of %u phdrs failed: %s\n", + ehdr.e_phnum, strerror(errno)); + exit(15); + } + ret = pread(fd, phdr64, phdrs_size, ehdr.e_phoff); + if (ret < 0 || (size_t)ret != phdrs_size) { + fprintf(stderr, "Read of program header @ %llu for %zu bytes failed: %s\n", + (unsigned long long)(ehdr.e_phoff), phdrs_size, strerror(errno)); + exit(16); + } + for (i = 0; i < ehdr.e_phnum; i++) { + phdr[i].p_type = file32_to_cpu(phdr64[i].p_type); + phdr[i].p_flags = file32_to_cpu(phdr64[i].p_flags); + phdr[i].p_offset = file64_to_cpu(phdr64[i].p_offset); + phdr[i].p_vaddr = file64_to_cpu(phdr64[i].p_vaddr); + phdr[i].p_paddr = file64_to_cpu(phdr64[i].p_paddr); + phdr[i].p_filesz = file64_to_cpu(phdr64[i].p_filesz); + phdr[i].p_memsz = file64_to_cpu(phdr64[i].p_memsz); + phdr[i].p_align = file64_to_cpu(phdr64[i].p_align); + + if (phdr[i].p_type == PT_LOAD) + num_pt_loads++; + } + free(phdr64); +} + +int get_pt_load(int idx, + unsigned long long *phys_start, + unsigned long long *phys_end, + unsigned long long *virt_start, + unsigned long long *virt_end) +{ + Elf64_Phdr *pls; + + if (num_pt_loads <= idx) + return 0; + + pls = &phdr[idx]; + + if (phys_start) + *phys_start = pls->p_paddr; + if (phys_end) + *phys_end = pls->p_paddr + pls->p_memsz; + if (virt_start) + *virt_start = pls->p_vaddr; + if (virt_end) + *virt_end = pls->p_vaddr + pls->p_memsz; + + return 1; +} + +#define NOT_FOUND_LONG_VALUE (-1) + +void (*arch_scan_vmcoreinfo)(char *pos); + +void scan_vmcoreinfo(char *start, size_t size) +{ + char *last = start + size - 1; + char *pos, *eol; + char temp_buf[1024]; + bool last_line = false; + char *str, *endp; + +#define SYMBOL(sym) { \ + .str = "SYMBOL(" #sym ")=", \ + .name = #sym, \ + .len = sizeof("SYMBOL(" #sym ")=") - 1, \ + .vaddr = & sym ## _vaddr, \ + } + static struct symbol { + const char *str; + const char *name; + size_t len; + loff_t *vaddr; + } symbol[] = { + SYMBOL(prb), + SYMBOL(log_buf), + SYMBOL(log_end), + SYMBOL(log_buf_len), + SYMBOL(logged_chars), + SYMBOL(log_first_idx), + SYMBOL(log_next_idx), + }; + + for (pos = start; pos <= last; pos = eol + 1) { + size_t len, i; + /* Find the end of the current line */ + for (eol = pos; (eol <= last) && (*eol != '\n') ; eol++) + ; + if (eol > last) { + /* + * We did not find \n and note ended. Currently kernel + * is appending last field CRASH_TIME without \n. It + * is ugly but handle it. + */ + eol = last; + len = eol - pos + 1; + if (len >= sizeof(temp_buf)) + len = sizeof(temp_buf) - 1; + strncpy(temp_buf, pos, len); + temp_buf[len + 1] = '\0'; + + pos = temp_buf; + len = len + 1; + eol = pos + len -1; + last_line = true; + } else { + len = eol - pos + 1; + } + + /* Stomp the last character so I am guaranteed a terminating null */ + *eol = '\0'; + /* Copy OSRELEASE if I see it */ + if ((len >= 10) && (memcmp("OSRELEASE=", pos, 10) == 0)) { + size_t to_copy = len - 10; + if (to_copy >= sizeof(osrelease)) + to_copy = sizeof(osrelease) - 1; + memcpy(osrelease, pos + 10, to_copy); + osrelease[to_copy] = '\0'; + } + /* See if the line is mentions a symbol I am looking for */ + for (i = 0; i < sizeof(symbol)/sizeof(symbol[0]); i++ ) { + unsigned long long vaddr; + if (symbol[i].len >= len) + continue; + if (memcmp(symbol[i].str, pos, symbol[i].len) != 0) + continue; + /* Found a symbol now decode it */ + vaddr = strtoull(pos + symbol[i].len, NULL, 16); + /* Remember the virtual address */ + *symbol[i].vaddr = vaddr; + } + + str = "SIZE(printk_ringbuffer)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_ringbuffer_sz = strtoull(pos + strlen(str), + NULL, 10); + + str = "SIZE(prb_desc)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_sz = strtoull(pos + strlen(str), NULL, 10); + + str = "SIZE(printk_info)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_sz = strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_ringbuffer.desc_ring)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_ringbuffer_desc_ring_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_ringbuffer.text_data_ring)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_ringbuffer_text_data_ring_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.count_bits)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_count_bits_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.descs)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_descs_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.infos)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_infos_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_data_ring.size_bits)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_data_ring_size_bits_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_data_ring.data)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_data_ring_data_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.head_id)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_head_id_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc_ring.tail_id)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_ring_tail_id_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(atomic_long_t.counter)="; + if (memcmp(str, pos, strlen(str)) == 0) + atomic_long_t_counter_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc.state_var)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_state_var_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc.info)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_info_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_desc.text_blk_lpos)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_desc_text_blk_lpos_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_data_blk_lpos.begin)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_data_blk_lpos_begin_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(prb_data_blk_lpos.next)="; + if (memcmp(str, pos, strlen(str)) == 0) + prb_data_blk_lpos_next_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.seq)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_seq_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.caller_id)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_caller_id_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.ts_nsec)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_ts_nsec_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.level)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_level_offset = + strtoull(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_info.text_len)="; + if (memcmp(str, pos, strlen(str)) == 0) + printk_info_text_len_offset = + strtoull(pos + strlen(str), NULL, 10); + + /* Check for "SIZE(printk_log)" or older "SIZE(log)=" */ + str = "SIZE(log)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_sz = strtoull(pos + strlen(str), NULL, 10); + + str = "SIZE(printk_log)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_sz = strtoull(pos + strlen(str), NULL, 10); + + /* Check for struct printk_log (or older log) field offsets */ + str = "OFFSET(log.ts_nsec)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_ts_nsec = strtoull(pos + strlen(str), NULL, + 10); + str = "OFFSET(printk_log.ts_nsec)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_ts_nsec = strtoull(pos + strlen(str), NULL, + 10); + + str = "OFFSET(log.len)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_len = strtoul(pos + strlen(str), NULL, 10); + + str = "OFFSET(printk_log.len)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_len = strtoul(pos + strlen(str), NULL, 10); + + str = "OFFSET(log.text_len)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_text_len = strtoul(pos + strlen(str), NULL, + 10); + str = "OFFSET(printk_log.text_len)="; + if (memcmp(str, pos, strlen(str)) == 0) + log_offset_text_len = strtoul(pos + strlen(str), NULL, + 10); + + /* Check for PHYS_OFFSET number */ + str = "NUMBER(PHYS_OFFSET)="; + if (memcmp(str, pos, strlen(str)) == 0) { + phys_offset = strtoul(pos + strlen(str), &endp, + 10); + if (strlen(endp) != 0) + phys_offset = strtoul(pos + strlen(str), &endp, 16); + if ((phys_offset == LONG_MAX) || strlen(endp) != 0) { + fprintf(stderr, "Invalid data %s\n", + pos); + break; + } + } + + if (arch_scan_vmcoreinfo != NULL) + (*arch_scan_vmcoreinfo)(pos); + + if (last_line) + break; + } +} + +static int scan_notes(int fd, loff_t start, loff_t lsize) +{ + char *buf, *last, *note, *next; + size_t size; + ssize_t ret; + + if (lsize > SSIZE_MAX) { + fprintf(stderr, "Unable to handle note section of %llu bytes\n", + (unsigned long long)lsize); + exit(20); + } + + size = lsize; + buf = malloc(size); + if (!buf) { + fprintf(stderr, "Cannot malloc %zu bytes\n", size); + exit(21); + } + last = buf + size - 1; + ret = pread(fd, buf, size, start); + if (ret != (ssize_t)size) { + fprintf(stderr, "Cannot read note section @ 0x%llx of %zu bytes: %s\n", + (unsigned long long)start, size, strerror(errno)); + exit(22); + } + + for (note = buf; (note + sizeof(Elf_Nhdr)) < last; note = next) + { + Elf_Nhdr *hdr; + char *n_name, *n_desc; + size_t n_namesz, n_descsz, n_type; + + hdr = (Elf_Nhdr *)note; + n_namesz = file32_to_cpu(hdr->n_namesz); + n_descsz = file32_to_cpu(hdr->n_descsz); + n_type = file32_to_cpu(hdr->n_type); + + n_name = note + sizeof(*hdr); + n_desc = n_name + ((n_namesz + 3) & ~3); + next = n_desc + ((n_descsz + 3) & ~3); + + if (next > (last + 1)) + break; + + if ((memcmp(n_name, "VMCOREINFO", 11) != 0) || (n_type != 0)) + continue; + scan_vmcoreinfo(n_desc, n_descsz); + } + + if ((note + sizeof(Elf_Nhdr)) == last) { + free(buf); + return -1; + } + + free(buf); + + return 0; +} + +static int scan_note_headers(int fd) +{ + int i, ret = 0; + + for (i = 0; i < ehdr.e_phnum; i++) { + if (phdr[i].p_type != PT_NOTE) + continue; + ret = scan_notes(fd, phdr[i].p_offset, phdr[i].p_filesz); + } + + return ret; +} + +static uint64_t read_file_pointer(int fd, uint64_t addr) +{ + uint64_t result; + ssize_t ret; + + if (machine_pointer_bits() == 64) { + uint64_t scratch; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(40); + } + result = file64_to_cpu(scratch); + } else { + uint32_t scratch; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read pointer @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(40); + } + result = file32_to_cpu(scratch); + } + return result; +} + +static uint32_t read_file_u32(int fd, uint64_t addr) +{ + uint32_t scratch; + ssize_t ret; + ret = pread(fd, &scratch, sizeof(scratch), addr); + if (ret != sizeof(scratch)) { + fprintf(stderr, "Failed to read value @ 0x%llx: %s\n", + (unsigned long long)addr, strerror(errno)); + exit(41); + } + return file32_to_cpu(scratch); +} + +static int32_t read_file_s32(int fd, uint64_t addr) +{ + return read_file_u32(fd, addr); +} + +static void dump_dmesg_legacy(int fd, void (*handler)(char*, unsigned int)) +{ + uint64_t log_buf, log_buf_offset; + unsigned log_end, logged_chars, log_end_wrapped; + int log_buf_len, to_wrap; + char *buf; + ssize_t ret; + + if (!log_buf_vaddr) { + fprintf(stderr, "Missing the log_buf symbol\n"); + exit(50); + } + if (!log_end_vaddr) { + fprintf(stderr, "Missing the log_end symbol\n"); + exit(51); + } + if (!log_buf_len_vaddr) { + fprintf(stderr, "Missing the log_bug_len symbol\n"); + exit(52); + } + if (!logged_chars_vaddr) { + fprintf(stderr, "Missing the logged_chars symbol\n"); + exit(53); + } + + log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); + log_end = read_file_u32(fd, vaddr_to_offset(log_end_vaddr)); + log_buf_len = read_file_s32(fd, vaddr_to_offset(log_buf_len_vaddr)); + logged_chars = read_file_u32(fd, vaddr_to_offset(logged_chars_vaddr)); + + log_buf_offset = vaddr_to_offset(log_buf); + + buf = calloc(1, log_buf_len); + if (!buf) { + fprintf(stderr, "Failed to malloc %d bytes for the logbuf: %s\n", + log_buf_len, strerror(errno)); + exit(51); + } + + log_end_wrapped = log_end % log_buf_len; + to_wrap = log_buf_len - log_end_wrapped; + + ret = pread(fd, buf, to_wrap, log_buf_offset + log_end_wrapped); + if (ret != to_wrap) { + fprintf(stderr, "Failed to read the first half of the log buffer: %s\n", + strerror(errno)); + exit(52); + } + ret = pread(fd, buf + to_wrap, log_end_wrapped, log_buf_offset); + if (ret != log_end_wrapped) { + fprintf(stderr, "Faield to read the second half of the log buffer: %s\n", + strerror(errno)); + exit(53); + } + + /* + * To collect full dmesg including the part before `dmesg -c` is useful + * for later debugging. Use same logic as what crash utility is using. + */ + logged_chars = log_end < log_buf_len ? log_end : log_buf_len; + + if (handler) + handler(buf + (log_buf_len - logged_chars), logged_chars); +} + +static inline uint16_t struct_val_u16(char *ptr, unsigned int offset) +{ + return(file16_to_cpu(*(uint16_t *)(ptr + offset))); +} + +static inline uint32_t struct_val_u32(char *ptr, unsigned int offset) +{ + return(file32_to_cpu(*(uint32_t *)(ptr + offset))); +} + +static inline uint64_t struct_val_u64(char *ptr, unsigned int offset) +{ + return(file64_to_cpu(*(uint64_t *)(ptr + offset))); +} + +/* Read headers of log records and dump accordingly */ +static void dump_dmesg_structured(int fd, void (*handler)(char*, unsigned int)) +{ +#define OUT_BUF_SIZE 4096 + uint64_t log_buf, log_buf_offset, ts_nsec; + uint32_t log_buf_len, log_first_idx, log_next_idx, current_idx, len = 0, i; + char *buf, out_buf[OUT_BUF_SIZE]; + bool has_wrapped_around = false; + ssize_t ret; + char *msg; + uint16_t text_len; + imaxdiv_t imaxdiv_sec, imaxdiv_usec; + + if (!log_buf_vaddr) { + fprintf(stderr, "Missing the log_buf symbol\n"); + exit(60); + } + + if (!log_buf_len_vaddr) { + fprintf(stderr, "Missing the log_bug_len symbol\n"); + exit(61); + } + + if (!log_first_idx_vaddr) { + fprintf(stderr, "Missing the log_first_idx symbol\n"); + exit(62); + } + + if (!log_next_idx_vaddr) { + fprintf(stderr, "Missing the log_next_idx symbol\n"); + exit(63); + } + + if (!log_sz) { + fprintf(stderr, "Missing the struct log size export\n"); + exit(64); + } + + if (log_offset_ts_nsec == UINT64_MAX) { + fprintf(stderr, "Missing the log.ts_nsec offset export\n"); + exit(65); + } + + if (log_offset_len == UINT16_MAX) { + fprintf(stderr, "Missing the log.len offset export\n"); + exit(66); + } + + if (log_offset_text_len == UINT16_MAX) { + fprintf(stderr, "Missing the log.text_len offset export\n"); + exit(67); + } + + log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr)); + log_buf_len = read_file_s32(fd, vaddr_to_offset(log_buf_len_vaddr)); + + log_first_idx = read_file_u32(fd, vaddr_to_offset(log_first_idx_vaddr)); + log_next_idx = read_file_u32(fd, vaddr_to_offset(log_next_idx_vaddr)); + + log_buf_offset = vaddr_to_offset(log_buf); + + buf = calloc(1, log_sz); + if (!buf) { + fprintf(stderr, "Failed to malloc %" PRId64 " bytes for the log:" + " %s\n", log_sz, strerror(errno)); + exit(64); + } + + /* Parse records and write out data at standard output */ + + current_idx = log_first_idx; + len = 0; + while (current_idx != log_next_idx) { + uint16_t loglen; + + ret = pread(fd, buf, log_sz, log_buf_offset + current_idx); + if (ret != log_sz) { + fprintf(stderr, "Failed to read log of size %" PRId64 " bytes:" + " %s\n", log_sz, strerror(errno)); + exit(65); + } + ts_nsec = struct_val_u64(buf, log_offset_ts_nsec); + imaxdiv_sec = imaxdiv(ts_nsec, 1000000000); + imaxdiv_usec = imaxdiv(imaxdiv_sec.rem, 1000); + + len += sprintf(out_buf + len, "[%5llu.%06llu] ", + (long long unsigned int)imaxdiv_sec.quot, + (long long unsigned int)imaxdiv_usec.quot); + + /* escape non-printable characters */ + text_len = struct_val_u16(buf, log_offset_text_len); + msg = calloc(1, text_len); + if (!msg) { + fprintf(stderr, "Failed to malloc %u bytes for log text:" + " %s\n", text_len, strerror(errno)); + exit(64); + } + + ret = pread(fd, msg, text_len, log_buf_offset + current_idx + log_sz); + if (ret != text_len) { + fprintf(stderr, "Failed to read log text of size %u bytes:" + " %s\n", text_len, strerror(errno)); + exit(65); + } + for (i = 0; i < text_len; i++) { + unsigned char c = msg[i]; + + if (!isprint(c) && !isspace(c)) + len += sprintf(out_buf + len, "\\x%02x", c); + else + out_buf[len++] = c; + + if (len >= OUT_BUF_SIZE - 64) { + if (handler) + handler(out_buf, len); + len = 0; + } + } + + out_buf[len++] = '\n'; + free(msg); + /* + * A length == 0 record is the end of buffer marker. Wrap around + * and read the message at the start of the buffer. + */ + loglen = struct_val_u16(buf, log_offset_len); + if (!loglen) { + if (has_wrapped_around) { + if (len && handler) + handler(out_buf, len); + fprintf(stderr, "Cycle when parsing dmesg detected.\n"); + fprintf(stderr, "The prink log_buf is most likely corrupted.\n"); + fprintf(stderr, "log_buf = 0x%lx, idx = 0x%x\n", + log_buf, current_idx); + exit(68); + } + current_idx = 0; + has_wrapped_around = true; + } else { + /* Move to next record */ + current_idx += loglen; + if(current_idx > log_buf_len - log_sz) { + if (len && handler) + handler(out_buf, len); + fprintf(stderr, "Index outside log_buf detected.\n"); + fprintf(stderr, "The prink log_buf is most likely corrupted.\n"); + fprintf(stderr, "log_buf = 0x%lx, idx = 0x%x\n", + log_buf, current_idx); + exit(69); + } + } + } + free(buf); + if (len && handler) + handler(out_buf, len); +} + +/* convenience struct for passing many values to helper functions */ +struct prb_map { + char *prb; + + char *desc_ring; + unsigned long desc_ring_count; + char *descs; + + char *infos; + + char *text_data_ring; + unsigned long text_data_ring_size; + char *text_data; +}; + +/* + * desc_state and DESC_* definitions taken from kernel source: + * + * kernel/printk/printk_ringbuffer.h + * + * DESC_* definitions modified to provide 32-bit and 64-bit variants. + */ + +/* The possible responses of a descriptor state-query. */ +enum desc_state { + desc_miss = -1, /* ID mismatch (pseudo state) */ + desc_reserved = 0x0, /* reserved, in use by writer */ + desc_committed = 0x1, /* committed by writer, could get reopened */ + desc_finalized = 0x2, /* committed, no further modification allowed */ + desc_reusable = 0x3, /* free, not yet used by any writer */ +}; + +#define DESC_SV_BITS (sizeof(uint64_t) * 8) +#define DESC_FLAGS_SHIFT (DESC_SV_BITS - 2) +#define DESC_FLAGS_MASK (3ULL << DESC_FLAGS_SHIFT) +#define DESC_STATE(sv) (3ULL & (sv >> DESC_FLAGS_SHIFT)) +#define DESC_ID_MASK (~DESC_FLAGS_MASK) +#define DESC_ID(sv) ((sv) & DESC_ID_MASK) + +#define DESC32_SV_BITS (sizeof(uint32_t) * 8) +#define DESC32_FLAGS_SHIFT (DESC32_SV_BITS - 2) +#define DESC32_FLAGS_MASK (3UL << DESC32_FLAGS_SHIFT) +#define DESC32_STATE(sv) (3UL & (sv >> DESC32_FLAGS_SHIFT)) +#define DESC32_ID_MASK (~DESC32_FLAGS_MASK) +#define DESC32_ID(sv) ((sv) & DESC32_ID_MASK) + +/* + * get_desc_state() taken from kernel source: + * + * kernel/printk/printk_ringbuffer.c + * + * get_desc32_state() added as 32-bit variant. + */ + +/* Query the state of a descriptor. */ +static enum desc_state get_desc_state(unsigned long id, + uint64_t state_val) +{ + if (id != DESC_ID(state_val)) + return desc_miss; + + return DESC_STATE(state_val); +} + +static enum desc_state get_desc32_state(unsigned long id, + uint64_t state_val) +{ + if (id != DESC32_ID(state_val)) + return desc_miss; + + return DESC32_STATE(state_val); +} + +static bool record_committed(unsigned long id, uint64_t state_var) +{ + enum desc_state state; + + if (machine_pointer_bits() == 32) + state = get_desc32_state(id, state_var); + else + state = get_desc_state(id, state_var); + + return (state == desc_committed || state == desc_finalized); +} + +static uint64_t id_inc(uint64_t id) +{ + id++; + + if (machine_pointer_bits() == 32) + return (id & DESC32_ID_MASK); + + return (id & DESC_ID_MASK); +} + +static uint64_t get_ulong(char *addr) +{ + if (machine_pointer_bits() == 32) + return struct_val_u32(addr, 0); + return struct_val_u64(addr, 0); +} + +static uint64_t sizeof_ulong(void) +{ + return (machine_pointer_bits() >> 3); +} + +static void dump_record(struct prb_map *m, unsigned long id, + void (*handler)(char*, unsigned int)) +{ +#define OUT_BUF_SIZE 4096 + char out_buf[OUT_BUF_SIZE]; + imaxdiv_t imaxdiv_usec; + imaxdiv_t imaxdiv_sec; + uint32_t offset = 0; + unsigned short len; + uint64_t state_var; + uint64_t ts_nsec; + uint64_t begin; + uint64_t next; + char *info; + char *text; + char *desc; + int i; + + desc = m->descs + ((id % m->desc_ring_count) * prb_desc_sz); + info = m->infos + ((id % m->desc_ring_count) * printk_info_sz); + + /* skip non-committed record */ + state_var = get_ulong(desc + prb_desc_state_var_offset + + atomic_long_t_counter_offset); + if (!record_committed(id, state_var)) + return; + + begin = get_ulong(desc + prb_desc_text_blk_lpos_offset + + prb_data_blk_lpos_begin_offset) % + m->text_data_ring_size; + next = get_ulong(desc + prb_desc_text_blk_lpos_offset + + prb_data_blk_lpos_next_offset) % + m->text_data_ring_size; + + ts_nsec = struct_val_u64(info, printk_info_ts_nsec_offset); + imaxdiv_sec = imaxdiv(ts_nsec, 1000000000); + imaxdiv_usec = imaxdiv(imaxdiv_sec.rem, 1000); + + offset += sprintf(out_buf + offset, "[%5llu.%06llu] ", + (long long unsigned int)imaxdiv_sec.quot, + (long long unsigned int)imaxdiv_usec.quot); + + /* skip data-less text blocks */ + if (begin == next) + goto out; + + len = struct_val_u16(info, printk_info_text_len_offset); + + /* handle wrapping data block */ + if (begin > next) + begin = 0; + + /* skip over descriptor ID */ + begin += sizeof_ulong(); + + /* handle truncated messages */ + if (next - begin < len) + len = next - begin; + + text = m->text_data + begin; + + /* escape non-printable characters */ + for (i = 0; i < len; i++) { + unsigned char c = text[i]; + + if (!isprint(c) && !isspace(c)) + offset += sprintf(out_buf + offset, "\\x%02x", c); + else + out_buf[offset++] = c; + + if (offset >= OUT_BUF_SIZE - 64) { + if (handler) + handler(out_buf, offset); + offset = 0; + } + } +out: + out_buf[offset++] = '\n'; + + if (offset && handler) + handler(out_buf, offset); +} + +/* + * Handle the lockless printk_ringbuffer. + */ +static void dump_dmesg_lockless(int fd, void (*handler)(char*, unsigned int)) +{ + struct prb_map m; + uint64_t head_id; + uint64_t tail_id; + uint64_t kaddr; + uint64_t id; + int ret; + + /* setup printk_ringbuffer */ + kaddr = read_file_pointer(fd, vaddr_to_offset(prb_vaddr)); + m.prb = calloc(1, printk_ringbuffer_sz); + if (!m.prb) { + fprintf(stderr, "Failed to malloc %zu bytes for prb: %s\n", + printk_ringbuffer_sz, strerror(errno)); + exit(64); + } + ret = pread(fd, m.prb, printk_ringbuffer_sz, vaddr_to_offset(kaddr)); + if (ret != printk_ringbuffer_sz) { + fprintf(stderr, "Failed to read prb of size %zu bytes: %s\n", + printk_ringbuffer_sz, strerror(errno)); + exit(65); + } + + /* setup descriptor ring */ + m.desc_ring = m.prb + printk_ringbuffer_desc_ring_offset; + m.desc_ring_count = 1 << struct_val_u32(m.desc_ring, + prb_desc_ring_count_bits_offset); + kaddr = get_ulong(m.desc_ring + prb_desc_ring_descs_offset); + m.descs = calloc(1, prb_desc_sz * m.desc_ring_count); + if (!m.descs) { + fprintf(stderr, "Failed to malloc %lu bytes for descs: %s\n", + prb_desc_sz * m.desc_ring_count, strerror(errno)); + exit(64); + } + ret = pread(fd, m.descs, prb_desc_sz * m.desc_ring_count, + vaddr_to_offset(kaddr)); + if (ret != prb_desc_sz * m.desc_ring_count) { + fprintf(stderr, + "Failed to read descs of size %lu bytes: %s\n", + prb_desc_sz * m.desc_ring_count, strerror(errno)); + exit(65); + } + + /* setup info ring */ + kaddr = get_ulong(m.prb + prb_desc_ring_infos_offset); + m.infos = calloc(1, printk_info_sz * m.desc_ring_count); + if (!m.infos) { + fprintf(stderr, "Failed to malloc %lu bytes for infos: %s\n", + printk_info_sz * m.desc_ring_count, strerror(errno)); + exit(64); + } + ret = pread(fd, m.infos, printk_info_sz * m.desc_ring_count, + vaddr_to_offset(kaddr)); + if (ret != printk_info_sz * m.desc_ring_count) { + fprintf(stderr, + "Failed to read infos of size %lu bytes: %s\n", + printk_info_sz * m.desc_ring_count, strerror(errno)); + exit(65); + } + + /* setup text data ring */ + m.text_data_ring = m.prb + printk_ringbuffer_text_data_ring_offset; + m.text_data_ring_size = 1 << struct_val_u32(m.text_data_ring, + prb_data_ring_size_bits_offset); + kaddr = get_ulong(m.text_data_ring + prb_data_ring_data_offset); + m.text_data = calloc(1, m.text_data_ring_size); + if (!m.text_data) { + fprintf(stderr, + "Failed to malloc %lu bytes for text_data: %s\n", + m.text_data_ring_size, strerror(errno)); + exit(64); + } + ret = pread(fd, m.text_data, m.text_data_ring_size, + vaddr_to_offset(kaddr)); + if (ret != m.text_data_ring_size) { + fprintf(stderr, + "Failed to read text_data of size %lu bytes: %s\n", + m.text_data_ring_size, strerror(errno)); + exit(65); + } + + /* ready to go */ + + tail_id = get_ulong(m.desc_ring + prb_desc_ring_tail_id_offset + + atomic_long_t_counter_offset); + head_id = get_ulong(m.desc_ring + prb_desc_ring_head_id_offset + + atomic_long_t_counter_offset); + + for (id = tail_id; id != head_id; id = id_inc(id)) + dump_record(&m, id, handler); + + /* dump head record */ + dump_record(&m, id, handler); + + free(m.text_data); + free(m.infos); + free(m.descs); + free(m.prb); +} + +void dump_dmesg(int fd, void (*handler)(char*, unsigned int)) +{ + if (prb_vaddr) + dump_dmesg_lockless(fd, handler); + else if (log_first_idx_vaddr) + dump_dmesg_structured(fd, handler); + else + dump_dmesg_legacy(fd, handler); +} + +int read_elf(int fd) +{ + int ret; + + ret = pread(fd, ehdr.e_ident, EI_NIDENT, 0); + if (ret != EI_NIDENT) { + fprintf(stderr, "Read of e_ident from %s failed: %s\n", + fname, strerror(errno)); + return 3; + } + if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { + fprintf(stderr, "Missing elf signature\n"); + return 4; + } + if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { + fprintf(stderr, "Bad elf version\n"); + return 5; + } + if ((ehdr.e_ident[EI_CLASS] != ELFCLASS32) && + (ehdr.e_ident[EI_CLASS] != ELFCLASS64)) + { + fprintf(stderr, "Unknown elf class %u\n", + ehdr.e_ident[EI_CLASS]); + return 6; + } + if ((ehdr.e_ident[EI_DATA] != ELFDATA2LSB) && + (ehdr.e_ident[EI_DATA] != ELFDATA2MSB)) + { + fprintf(stderr, "Unkown elf data order %u\n", + ehdr.e_ident[EI_DATA]); + return 7; + } + if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) + read_elf32(fd); + else + read_elf64(fd); + + ret = scan_note_headers(fd); + if (ret < 0) + return ret; + + return 0; +} + +int read_phys_offset_elf_kcore(int fd, long *phys_off) +{ + int ret; + + *phys_off = ULONG_MAX; + + ret = read_elf(fd); + if (!ret) { + /* If we have a valid 'PHYS_OFFSET' by now, + * return it to the caller now. + */ + if (phys_offset != UINT64_MAX) { + *phys_off = phys_offset; + return ret; + } + } + + return 2; +} diff --git a/util_lib/include/elf_info.h b/util_lib/include/elf_info.h new file mode 100644 index 0000000..fdf4c3d --- /dev/null +++ b/util_lib/include/elf_info.h @@ -0,0 +1,36 @@ +#ifndef ELF_INFO_H +#define ELF_INFO_H + +#define _XOPEN_SOURCE 700 +#define _GNU_SOURCE +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + +#include <endian.h> +#include <byteswap.h> +#include <stdio.h> +#include <stdint.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <elf.h> +#include <stdbool.h> +#include <inttypes.h> +#include <ctype.h> + +int get_pt_load(int idx, + unsigned long long *phys_start, + unsigned long long *phys_end, + unsigned long long *virt_start, + unsigned long long *virt_end); +int read_phys_offset_elf_kcore(int fd, long *phys_off); +int read_elf(int fd); +void dump_dmesg(int fd, void (*handler)(char*, unsigned int)); +extern void (*arch_scan_vmcoreinfo)(char *pos); + +#endif /* ELF_INFO_H */ diff --git a/util_lib/include/ip_checksum.h b/util_lib/include/ip_checksum.h new file mode 100644 index 0000000..5559fa4 --- /dev/null +++ b/util_lib/include/ip_checksum.h @@ -0,0 +1,8 @@ +#ifndef IP_CHECKSUM_H +#define IP_CHECKSUM_H + +unsigned long compute_ip_checksum(void *addr, unsigned long length); +unsigned long add_ip_checksums(unsigned long offset, unsigned long sum, unsigned long new); +unsigned long negate_ip_checksum(unsigned long sum); + +#endif /* IP_CHECKSUM_H */ diff --git a/util_lib/include/sha256.h b/util_lib/include/sha256.h new file mode 100644 index 0000000..467fb22 --- /dev/null +++ b/util_lib/include/sha256.h @@ -0,0 +1,22 @@ +#ifndef SHA256_H +#define SHA256_H + +#include <sys/types.h> +#include <stdint.h> + +typedef struct +{ + size_t total[2]; + uint32_t state[8]; + uint8_t buffer[64]; +} +sha256_context; + +typedef uint8_t sha256_digest_t[32]; + +void sha256_starts( sha256_context *ctx ); +void sha256_update( sha256_context *ctx, const uint8_t *input, size_t length ); +void sha256_finish( sha256_context *ctx, sha256_digest_t digest ); + + +#endif /* SHA256_H */ diff --git a/util_lib/sha256.c b/util_lib/sha256.c new file mode 100644 index 0000000..2e61a31 --- /dev/null +++ b/util_lib/sha256.c @@ -0,0 +1,368 @@ +/* + * FIPS-180-2 compliant SHA-256 implementation + * + * Copyright (C) 2001-2003 Christophe Devine + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <string.h> + +#include "sha256.h" + +#define GET_UINT32(n,b,i) \ +{ \ + (n) = ( (uint32_t) (b)[(i) ] << 24 ) | \ + ( (uint32_t) (b)[(i) + 1] << 16 ) | \ + ( (uint32_t) (b)[(i) + 2] << 8 ) | \ + ( (uint32_t) (b)[(i) + 3] ); \ +} + +#define PUT_UINT32(n,b,i) \ +{ \ + (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uint8_t) ( (n) ); \ +} + +void sha256_starts( sha256_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +void sha256_process( sha256_context *ctx, const uint8_t data[64] ) +{ + uint32_t temp1, temp2, W[64]; + uint32_t A, B, C, D, E, F, G, H; + + GET_UINT32( W[0], data, 0 ); + GET_UINT32( W[1], data, 4 ); + GET_UINT32( W[2], data, 8 ); + GET_UINT32( W[3], data, 12 ); + GET_UINT32( W[4], data, 16 ); + GET_UINT32( W[5], data, 20 ); + GET_UINT32( W[6], data, 24 ); + GET_UINT32( W[7], data, 28 ); + GET_UINT32( W[8], data, 32 ); + GET_UINT32( W[9], data, 36 ); + GET_UINT32( W[10], data, 40 ); + GET_UINT32( W[11], data, 44 ); + GET_UINT32( W[12], data, 48 ); + GET_UINT32( W[13], data, 52 ); + GET_UINT32( W[14], data, 56 ); + GET_UINT32( W[15], data, 60 ); + +#define SHR(x,n) ((x & 0xFFFFFFFF) >> n) +#define ROTR(x,n) (SHR(x,n) | (x << (32 - n))) + +#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3)) +#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10)) + +#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22)) +#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25)) + +#define F0(x,y,z) ((x & y) | (z & (x | y))) +#define F1(x,y,z) (z ^ (x & (y ^ z))) + +#define R(t) \ +( \ + W[t] = S1(W[t - 2]) + W[t - 7] + \ + S0(W[t - 15]) + W[t - 16] \ +) + +#define P(a,b,c,d,e,f,g,h,x,K) \ +{ \ + temp1 = h + S3(e) + F1(e,f,g) + K + x; \ + temp2 = S2(a) + F0(a,b,c); \ + d += temp1; h = temp1 + temp2; \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + F = ctx->state[5]; + G = ctx->state[6]; + H = ctx->state[7]; + + P( A, B, C, D, E, F, G, H, W[ 0], 0x428A2F98 ); + P( H, A, B, C, D, E, F, G, W[ 1], 0x71374491 ); + P( G, H, A, B, C, D, E, F, W[ 2], 0xB5C0FBCF ); + P( F, G, H, A, B, C, D, E, W[ 3], 0xE9B5DBA5 ); + P( E, F, G, H, A, B, C, D, W[ 4], 0x3956C25B ); + P( D, E, F, G, H, A, B, C, W[ 5], 0x59F111F1 ); + P( C, D, E, F, G, H, A, B, W[ 6], 0x923F82A4 ); + P( B, C, D, E, F, G, H, A, W[ 7], 0xAB1C5ED5 ); + P( A, B, C, D, E, F, G, H, W[ 8], 0xD807AA98 ); + P( H, A, B, C, D, E, F, G, W[ 9], 0x12835B01 ); + P( G, H, A, B, C, D, E, F, W[10], 0x243185BE ); + P( F, G, H, A, B, C, D, E, W[11], 0x550C7DC3 ); + P( E, F, G, H, A, B, C, D, W[12], 0x72BE5D74 ); + P( D, E, F, G, H, A, B, C, W[13], 0x80DEB1FE ); + P( C, D, E, F, G, H, A, B, W[14], 0x9BDC06A7 ); + P( B, C, D, E, F, G, H, A, W[15], 0xC19BF174 ); + P( A, B, C, D, E, F, G, H, R(16), 0xE49B69C1 ); + P( H, A, B, C, D, E, F, G, R(17), 0xEFBE4786 ); + P( G, H, A, B, C, D, E, F, R(18), 0x0FC19DC6 ); + P( F, G, H, A, B, C, D, E, R(19), 0x240CA1CC ); + P( E, F, G, H, A, B, C, D, R(20), 0x2DE92C6F ); + P( D, E, F, G, H, A, B, C, R(21), 0x4A7484AA ); + P( C, D, E, F, G, H, A, B, R(22), 0x5CB0A9DC ); + P( B, C, D, E, F, G, H, A, R(23), 0x76F988DA ); + P( A, B, C, D, E, F, G, H, R(24), 0x983E5152 ); + P( H, A, B, C, D, E, F, G, R(25), 0xA831C66D ); + P( G, H, A, B, C, D, E, F, R(26), 0xB00327C8 ); + P( F, G, H, A, B, C, D, E, R(27), 0xBF597FC7 ); + P( E, F, G, H, A, B, C, D, R(28), 0xC6E00BF3 ); + P( D, E, F, G, H, A, B, C, R(29), 0xD5A79147 ); + P( C, D, E, F, G, H, A, B, R(30), 0x06CA6351 ); + P( B, C, D, E, F, G, H, A, R(31), 0x14292967 ); + P( A, B, C, D, E, F, G, H, R(32), 0x27B70A85 ); + P( H, A, B, C, D, E, F, G, R(33), 0x2E1B2138 ); + P( G, H, A, B, C, D, E, F, R(34), 0x4D2C6DFC ); + P( F, G, H, A, B, C, D, E, R(35), 0x53380D13 ); + P( E, F, G, H, A, B, C, D, R(36), 0x650A7354 ); + P( D, E, F, G, H, A, B, C, R(37), 0x766A0ABB ); + P( C, D, E, F, G, H, A, B, R(38), 0x81C2C92E ); + P( B, C, D, E, F, G, H, A, R(39), 0x92722C85 ); + P( A, B, C, D, E, F, G, H, R(40), 0xA2BFE8A1 ); + P( H, A, B, C, D, E, F, G, R(41), 0xA81A664B ); + P( G, H, A, B, C, D, E, F, R(42), 0xC24B8B70 ); + P( F, G, H, A, B, C, D, E, R(43), 0xC76C51A3 ); + P( E, F, G, H, A, B, C, D, R(44), 0xD192E819 ); + P( D, E, F, G, H, A, B, C, R(45), 0xD6990624 ); + P( C, D, E, F, G, H, A, B, R(46), 0xF40E3585 ); + P( B, C, D, E, F, G, H, A, R(47), 0x106AA070 ); + P( A, B, C, D, E, F, G, H, R(48), 0x19A4C116 ); + P( H, A, B, C, D, E, F, G, R(49), 0x1E376C08 ); + P( G, H, A, B, C, D, E, F, R(50), 0x2748774C ); + P( F, G, H, A, B, C, D, E, R(51), 0x34B0BCB5 ); + P( E, F, G, H, A, B, C, D, R(52), 0x391C0CB3 ); + P( D, E, F, G, H, A, B, C, R(53), 0x4ED8AA4A ); + P( C, D, E, F, G, H, A, B, R(54), 0x5B9CCA4F ); + P( B, C, D, E, F, G, H, A, R(55), 0x682E6FF3 ); + P( A, B, C, D, E, F, G, H, R(56), 0x748F82EE ); + P( H, A, B, C, D, E, F, G, R(57), 0x78A5636F ); + P( G, H, A, B, C, D, E, F, R(58), 0x84C87814 ); + P( F, G, H, A, B, C, D, E, R(59), 0x8CC70208 ); + P( E, F, G, H, A, B, C, D, R(60), 0x90BEFFFA ); + P( D, E, F, G, H, A, B, C, R(61), 0xA4506CEB ); + P( C, D, E, F, G, H, A, B, R(62), 0xBEF9A3F7 ); + P( B, C, D, E, F, G, H, A, R(63), 0xC67178F2 ); + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; + ctx->state[5] += F; + ctx->state[6] += G; + ctx->state[7] += H; +} + +void sha256_update( sha256_context *ctx, const uint8_t *input, size_t length ) +{ + size_t left, fill; + + if( ! length ) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += length; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < length ) + ctx->total[1]++; + + if( left && length >= fill ) + { + memcpy( ctx->buffer + left, input, fill ); + sha256_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + sha256_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy(ctx->buffer + left, input, length); + } +} + +static uint8_t sha256_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void sha256_finish( sha256_context *ctx, sha256_digest_t digest ) +{ + uint32_t last, padn; + uint32_t high, low; + uint8_t msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32( high, msglen, 0 ); + PUT_UINT32( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha256_update( ctx, sha256_padding, padn ); + sha256_update( ctx, msglen, 8 ); + + PUT_UINT32( ctx->state[0], digest, 0 ); + PUT_UINT32( ctx->state[1], digest, 4 ); + PUT_UINT32( ctx->state[2], digest, 8 ); + PUT_UINT32( ctx->state[3], digest, 12 ); + PUT_UINT32( ctx->state[4], digest, 16 ); + PUT_UINT32( ctx->state[5], digest, 20 ); + PUT_UINT32( ctx->state[6], digest, 24 ); + PUT_UINT32( ctx->state[7], digest, 28 ); +} + +#ifdef TEST + +#include <stdlib.h> +#include <stdio.h> + +/* + * those are the standard FIPS-180-2 test vectors + */ + +static char *msg[] = +{ + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + NULL +}; + +static char *val[] = +{ + "ba7816bf8f01cfea414140de5dae2223" \ + "b00361a396177a9cb410ff61f20015ad", + "248d6a61d20638b8e5c026930c3e6039" \ + "a33ce45964ff2167f6ecedd419db06c1", + "cdc76e5c9914fb9281a1c7e284d73e67" \ + "f1809a48a497200e046d39ccc7112cd0" +}; + +int main( int argc, char *argv[] ) +{ + FILE *f; + int i, j; + char output[65]; + sha256_context ctx; + unsigned char buf[1000]; + unsigned char sha256sum[32]; + + if( argc < 2 ) + { + printf( "\n SHA-256 Validation Tests:\n\n" ); + + for( i = 0; i < 3; i++ ) + { + printf( " Test %d ", i + 1 ); + + sha256_starts( &ctx ); + + if( i < 2 ) + { + sha256_update( &ctx, (uint8_t *) msg[i], + strlen( msg[i] ) ); + } + else + { + memset( buf, 'a', 1000 ); + + for( j = 0; j < 1000; j++ ) + { + sha256_update( &ctx, (uint8_t *) buf, 1000 ); + } + } + + sha256_finish( &ctx, sha256sum ); + + for( j = 0; j < 32; j++ ) + { + sprintf( output + j * 2, "%02x", sha256sum[j] ); + } + + if( memcmp( output, val[i], 64 ) ) + { + printf( "failed!\n" ); + return( 1 ); + } + + printf( "passed.\n" ); + } + + printf( "\n" ); + } + else + { + if( ! ( f = fopen( argv[1], "rb" ) ) ) + { + perror( "fopen" ); + return( 1 ); + } + + sha256_starts( &ctx ); + + while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + { + sha256_update( &ctx, buf, i ); + } + + sha256_finish( &ctx, sha256sum ); + + for( j = 0; j < 32; j++ ) + { + printf( "%02x", sha256sum[j] ); + } + + printf( " %s\n", argv[1] ); + } + + return( 0 ); +} + +#endif + |