/* FELOAD.C - PDP-10 boot loader routines */ /* $Id: feload.c,v 2.3 2001/11/10 21:28:59 klh Exp $ */ /* Copyright © 1992, 1993, 2001 Kenneth L. Harrenstien ** All Rights Reserved ** ** This file is part of the KLH10 Distribution. Use, modification, and ** re-distribution is permitted subject to the terms in the file ** named "LICENSE", which contains the full text of the legal notices ** and should always accompany this Distribution. ** ** This software is provided "AS IS" with NO WARRANTY OF ANY KIND. ** ** This notice (including the copyright and warranty disclaimer) ** must be included in all copies or derivations of this software. */ /* * $Log: feload.c,v $ * Revision 2.3 2001/11/10 21:28:59 klh * Final 2.0 distribution checkin * */ /* Loads executables into physical memory. */ #include "cenv.h" #include #include /* Malloc and friends */ #include #include #include #include "klh10.h" /* For overall config defs */ #include "word10.h" #include "kn10ops.h" #include "wfio.h" #include "feload.h" #ifdef RCSID RCSID(feload_c,"$Id: feload.c,v 2.3 2001/11/10 21:28:59 klh Exp $") #endif /* Commonly needed vector value */ #define LH_JRST ((h10_t)I_JRST<<9) /* DEC sharable save format - block IDs */ #define DECSSF_DIR 01776 #define DECSSF_EV 01775 #define DECSSF_PDV 01774 #define DECSSF_END 01777 static int load_typefind(WFILE *, struct loadinfo *); static int load_sblk(WFILE *, struct loadinfo *); static int dump_dsblk(WFILE *, struct loadinfo *, int); static int load_decsav(WFILE *, struct loadinfo *); static int load_decexe(WFILE *, struct loadinfo *); int fe_load(register WFILE *wf, register struct loadinfo *lp) { /* Initialize result variables of loadinfo struct */ lp->ldi_allerr = 0; lp->ldi_loaddr = (uint18)1 << 18; lp->ldi_hiaddr = 0; LRHSET(lp->ldi_startwd, 0, 0); lp->ldi_ndata = 0; lp->ldi_nsyms = 0; lp->ldi_cksumerr = 0; lp->ldi_aibgot = 0; lp->ldi_evlen = -1; lp->ldi_evloc = 0; /* See if load format is already known. If not, try to determine ** from initial contents of file. */ if (lp->ldi_type == LOADT_UNKNOWN) { lp->ldi_type = load_typefind(wf, lp); wf_rewind(wf); /* Back up to start */ } switch (lp->ldi_type) { case LOADT_UNKNOWN: lp->ldi_typname = "Unknown"; break; case LOADT_PDUMP: lp->ldi_typname = "ITS-PDUMP"; fprintf(stderr, "Loading aborted, unable to load ITS PDUMP format.\n"); break; case LOADT_SBLK: lp->ldi_typname = "ITS-SBLK"; return load_sblk(wf, lp); case LOADT_DECSAV: lp->ldi_typname = "DEC-CSAV"; return load_decsav(wf, lp); case LOADT_DECEXE: lp->ldi_typname = "DEC-PEXE"; return load_decexe(wf, lp); } return 0; } /* LDVM_MAP - Do loader-specific mapping of addresses to physical memory ** locations. For now, assumes only phys mapping in effect, no virtual ** funnies. ** Also, permit addresses larger than 18 bits, so caller must be sure ** arg is OK! */ static vmptr_t ldvm_map(paddr_t pa) { if (pa <= AC_17) return &cpu.acblk.cur[pa]; /* Use currently active AC block */ return vm_physmap(pa); } /* FE_DUMP - Dump out memory in a loadable format. ** The loadinfo struct must be properly set up beforehand: ** ldi_type = desired dump format ** ldi_loaddr, ldi_hiaddr = range to dump ** ldi_startwd = start address in RH if any ** (other members may become significant with other formats) */ int fe_dump(register WFILE *wf, register struct loadinfo *lp) { int res; /* Initialize result variables of loadinfo struct */ lp->ldi_allerr = 0; lp->ldi_ndata = 0; lp->ldi_nsyms = 0; lp->ldi_cksumerr = 0; /* See if load format is already known. If not, try to determine ** from current system type. */ if (lp->ldi_type == LOADT_UNKNOWN) { #if KLH10_SYS_ITS lp->ldi_type = LOADT_SBLK; #else lp->ldi_type = LOADT_DECSAV; #endif } switch (lp->ldi_type) { case LOADT_SBLK: case LOADT_DECSAV: lp->ldi_typname = (lp->ldi_type == LOADT_SBLK) ? "ITS-SBLK" : "DEC-CSAV"; res = dump_dsblk(wf, lp, lp->ldi_type); break; default: fprintf(stderr, "fe_dump: unknown format %d\n", lp->ldi_type); lp->ldi_allerr++; return 0; } wf_flush(wf); return res; } static int load_typefind(register WFILE *wf, struct loadinfo *lp) { w10_t w, w2; /* Read first word (or two) to help determine format type */ if (wf_get(wf, &w) <= 0) { fprintf(stderr, "Loading aborted, initial read failed\n"); return LOADT_UNKNOWN; /* Ugh return */ } if (op10m_skipge(w)) { /* First word is positive */ if (op10m_skipe(w)) /* If 1st word is 0 */ return LOADT_PDUMP; /* assume ITS PDUMP */ if (LHGET(w) == DECSSF_DIR) /* If special EXE value */ return LOADT_DECEXE; /* assume DEC sharable */ return LOADT_SBLK; /* Else must be SBLK with no RIM */ } /* First word is negative, check for special values */ if (RHGET(w)) /* If RH non-zero, */ return LOADT_DECSAV; /* can't be any of the others */ if (LHGET(w) != 0710440 /* If not DATAI PTR,0 (RIM) */ && LHGET(w) != 0777761) /* or -17,,0 (RIM10) */ return LOADT_DECSAV; /* then also can't be RIM/SBLK */ /* We might be looking at a RIM or RIM10 preface to a SBLK file. ** Check the second word to be doubly sure. If it isn't a CONO PTR,60 ** then give up and default to DEC unsharable format. */ if (wf_get(wf, &w2) <= 0) { fprintf(stderr, "Loading aborted, read of 2nd word failed\n"); return LOADT_UNKNOWN; /* Ugh return */ } if (LHGET(w2) == 0710600 && RHGET(w2) == 060) /* CONO PTR,60 */ return LOADT_SBLK; return LOADT_DECSAV; } /* ITS SBLK format loading code */ /* SBLK format: [] : This block contains 1 to 16 (RIM10) or more (RIM) words, where the first word must be non-zero and the last word (which may be the first word, and which marks the end of the block) is JRST 1. The RIM (Read-In Mode) loader starts with: 0: 710440,, 0 DATAI PTR, 1: 710600,, 60 CONO PTR,60 The RIM10 loader starts with: 0: -017,,0 1: CONO PTR,60 : -<# wds>,, : A non-negative word seen while looking for a simple block is interpreted as the start address. (normally, a JRST ). This may be 0 if there is no start address. : Exactly like simple blocks, but with zero s. A duplicate start-address indicates there are no more symtab blocks. */ #define DDTLOC 0774000 /* Loc of ITS Exec DDT in phys mem */ static int ld_bget(WFILE *, struct loadinfo *, w10_t *); static int load_sblk(register WFILE *wf, register struct loadinfo *lp) { register int i; register paddr_t addr; register w10_t *wp; #define WBUFLEN 16000+2 /* Make room for big blocks (ITS symtab) */ register w10_t *wbuf; /* Get a buffer for reading blocks into. This is done with malloc ** instead of using the stack because at least one platform (Mac MPW C) ** silently generates incorrect code for stack frames > 32Kb. */ wbuf = (w10_t *)malloc(sizeof(w10_t) * WBUFLEN); if (!wbuf) { fprintf(stderr, "Loader couldn't allocate buffer - malloc failed.\n"); return 0; } /* First skip over RIM10 loader */ for (;;) { if (wf_get(wf, wbuf) <= 0) { fprintf(stderr, "SBLK load aborted before data blocks\n"); free(wbuf); return 0; /* Ugh return */ } if ((LHGET(wbuf[0]) == LH_JRST) && (RHGET(wbuf[0]) == 1)) break; } if (lp->ldi_debug) printf(" RIM10 Loader skipped.\n"); /* Now read simple data blocks and store them in ** PDP-10 physical memory. This could be done with a memcpy if ** phys mem is identical to the buffer format. */ while ((i = ld_bget(wf, lp, wbuf)) > 1) { addr = RHGET(wbuf[0]); /* Find addr of first word */ if (addr < lp->ldi_loaddr) lp->ldi_loaddr = addr; lp->ldi_ndata += (i -= 2); for (wp = wbuf+1; --i >= 0; ++wp, ++addr) vm_pset(ldvm_map(addr & H10MASK), *wp); if (addr > lp->ldi_hiaddr) lp->ldi_hiaddr = addr-1; } if (i <= 0) { fprintf(stderr, "SBLK aborted before first start address\n"); free(wbuf); return 0; } /* Now have a positive word in wbuf, assume it's the start addr */ lp->ldi_startwd = wbuf[0]; /* Remember it */ if (lp->ldi_debug) printf(" Start address = %lo\n", (long)RHGET(wbuf[0])); /* Now gobble up symtab blocks. */ while ((i = ld_bget(wf, lp, wbuf)) > 1) { register w10_t sptr; int nsym = i - 2; lp->ldi_nsyms += nsym; addr = RHGET(wbuf[0]); /* Find addr of first word */ if (addr) { int j; if (addr != 3) { /* Understand Misc Info blocks */ fprintf(stderr, "Symtab block with nonzero RH: %#lo\n", (long)addr); continue; /* Ignore it */ } if (i <= 0) continue; /* We only understand Type 1 subblocks (Assembly Info) */ if (LHGET(wbuf[1]) != ((-6)&H10MASK) || RHGET(wbuf[1]) != 1) { fprintf(stderr, "Unknown subblock in MiscInfo blk: %lo,,%lo\n", (long)LHGET(wbuf[1]), (long)RHGET(wbuf[1])); continue; /* Ignore it */ } if (--i <= 0) continue; /* Have at least one word of data for info. ** Copy them into array... unspecified words remain zero. */ lp->ldi_aibgot = i = ((i > 6) ? 6 : i); for (j = 0; j < i; ++j) lp->ldi_asminf[j] = wbuf[j+2]; continue; /* And that's all for now */ } if (nsym <= 0) continue; /* Add this batch of syms to Exec DDT's symtab. ** Perhaps add a flag to make this optional. */ sptr = vm_pget(ldvm_map((paddr_t)DDTLOC-1)); if (RHGET(sptr) != DDTLOC-2) { /* Check for DDT */ printf("No EXEC DDT? Ignoring block of %d syms.\n", nsym); continue; /* Not there */ } sptr = vm_pget(ldvm_map((paddr_t)DDTLOC-2)); LHSET(sptr, (LHGET(sptr)-nsym)&H10MASK); RHSET(sptr, (RHGET(sptr)-nsym)&H10MASK); addr = RHGET(sptr); /* Copy sym data to this loc */ for (wp = wbuf+1; --i >= 0; ++wp, ++addr) vm_pset(ldvm_map(addr & H10MASK), *wp); /* Now tell DDT about it */ vm_pset(ldvm_map((paddr_t)DDTLOC-2), sptr); /* Store new symtab ptr */ vm_psetlh(ldvm_map((paddr_t)DDTLOC-1), H10MASK); /* Tell DDT symtab munged */ vm_pset(ldvm_map((paddr_t)DDTLOC-4), lp->ldi_startwd); /* Set start addr */ printf("Added %d syms to DDT, total %ld\n", nsym, (long) -(LHGET(sptr)|~MASK18)); } if (i <= 0) { fprintf(stderr, "SBLK aborted before final start address\n"); free(wbuf); return 0; } /* Last check -- should be duplicate of first start word */ if (LHGET(wbuf[0]) != LHGET(lp->ldi_startwd) || RHGET(wbuf[0]) != RHGET(lp->ldi_startwd)) { fprintf(stderr, "SBLK start address mismatch: %#lo,,%#lo != %#lo,,%#lo\n", (long) LHGET(lp->ldi_startwd), (long) RHGET(lp->ldi_startwd), (long) LHGET(wbuf[0]), (long) RHGET(wbuf[0])); free(wbuf); return 0; } free(wbuf); return 1; /* Won! */ } /* Read in simple block of up to WBUFLEN words, including header & checksum. ** Returns positive # words read, if all's well. ** Returns # <= 0 if error, where # gives words read so far. */ static int ld_bget(register WFILE *wf, register struct loadinfo *lp, register w10_t *aw) { register int i, cnt; /* Get first word */ if (wf_get(wf, aw) <= 0) return 0; if (op10m_skipge(*aw)) return 1; /* Not a simple block, stop now */ /* Simple block, read in rest of words! */ i = -(LHGET(*aw) | -H10SIGN); /* Extend sign and negate to get pos */ if (i > (WBUFLEN-2)) { fprintf(stderr, "Block size too large: %d (max %d)\n", i, WBUFLEN-2); return -1; } if (lp->ldi_debug) printf(" ------ Starting block of %d. words, addr=%lo ------\n", i, (long)RHGET(*aw)); for (cnt = 1; --i >= 0; ++cnt) { if (wf_get(wf, ++aw) <= 0) { fprintf(stderr, "Unexpected EOF while reading block.\n"); return -cnt; } /* Could compute checksum here */ } /* Block data read in, now get trailing checksum */ if (wf_get(wf, ++aw) <= 0) { fprintf(stderr, "Unexpected EOF while reading checksum.\n"); return -cnt; } /* Could compare checksum here */ return cnt+1; } /* ITS SBLK (and DEC SAV) dumping code ** Per definition of SBLK, no zero words are ever dumped. */ static int sblk_out(WFILE *, paddr_t, int, w10_t *); static int dsav_out(WFILE *, paddr_t, int, w10_t *); static int dump_dsblk(register WFILE *wf, register struct loadinfo *lp, int typ) /* A LOADT_xxx type */ { register w10_t *wp; register paddr_t addr; register int blen; /* Block length */ paddr_t baddr; /* Block start addr */ #define WBOLEN 128 w10_t wbuf[WBOLEN]; if (typ == LOADT_SBLK) { LRHSET(wbuf[0], LH_JRST, 1); /* First word is JRST 1 */ if (wf_put(wf, wbuf[0]) <= 0) return 0; } addr = lp->ldi_loaddr; for (; addr <= lp->ldi_hiaddr;) { blen = 0; wp = wbuf; /* Scan for nonzero word */ for (; addr <= lp->ldi_hiaddr; ++addr) { *wp = vm_pget(ldvm_map(addr)); if (op10m_skipn(*wp)) { blen = 1; baddr = addr; break; } } if (!blen) break; /* Have 1st wd in buffer. ** Now scan for first zero word, or until buffer full */ for (; ++addr <= lp->ldi_hiaddr;) { if (blen >= WBOLEN) break; *++wp = vm_pget(ldvm_map(addr)); if (op10m_skipe(*wp)) break; ++blen; } /* Block done. addr points to next word (may be zero), ** blen has # of words in buffer/block. */ if ( (typ == LOADT_SBLK) ? (sblk_out(wf, baddr, blen, wbuf) <= 0) : ((typ == LOADT_DECSAV) ? (dsav_out(wf, baddr, blen, wbuf) <= 0) : 1)) { return 0; /* Failure of some kind */ } lp->ldi_ndata += blen; /* Remember # words */ } /* Data blocks done, now add start address (or entry vector) */ if (typ == LOADT_SBLK) { /* Build start address to mark end of simple blocks */ LRHSET(wbuf[0], LH_JRST, RHGET(lp->ldi_startwd)); wf_put(wf, wbuf[0]); /* Symbols should go here, if we ever remember them */ /* Now use duplicate start address to mark end of symbols */ LRHSET(wbuf[0], LH_JRST, RHGET(lp->ldi_startwd)); return wf_put(wf, wbuf[0]); } else if (typ == LOADT_DECSAV) { /* Build entry vector to mark end of blocks */ switch (lp->ldi_evlen) { case -1: case 0: case LH_JRST: LRHSET(wbuf[0], LH_JRST, RHGET(lp->ldi_startwd)); break; default: LRHSET(wbuf[0], lp->ldi_evlen, lp->ldi_evloc); } return wf_put(wf, wbuf[0]); } return 0; } static int sblk_out(register WFILE *wf, register paddr_t addr, register int len, register w10_t *wp) { register w10_t w; LRHSET(w, (-len)&H10MASK, addr & H10MASK); /* -,, */ if (wf_put(wf, w) <= 0) return 0; for (; --len >= 0; ++wp) if (wf_put(wf, *wp) <= 0) return 0; LRHSET(w, 0, 0); /* Bogus checksum for now */ return wf_put(wf, w); } /* DEC SAV non-sharable SAVE format loading code */ /* DEC nonsharable save format: : -<# wds>,, : ,, Vector word 0 is instr to execute to start program. Vector word 1 is instr to execute to reenter program. Vector word 2 contains program version # info. BUT if LH is 254000 then: start addr = RH(120) reenter addr = RH(124) version info in 137 Actually it appears that the RH may be the start address. If it's non-zero, let's use that. */ static int load_decsav(register WFILE *wf, register struct loadinfo *lp) { register paddr_t addr; register int32 cnt; w10_t wdata; lp->ldi_evlen = -1; /* Init entry vector length in case fail */ for (;;) { /* Read first word of block, should be an IOWD */ if ((cnt = wf_get(wf, &wdata)) <= 0) { return cnt ? 0 : 1; } if (op10m_skipge(wdata)) /* If word is positive, done! */ break; /* Read in and load data words for one block */ cnt = -(LHGET(wdata) | ~MASK18); /* Get positive count */ addr = RHGET(wdata)+1; /* Find 1st loc to load into */ if (lp->ldi_debug) printf(" ------ Starting block of %ld. words, addr=%lo ------\n", (long)cnt, (long)addr); if (addr < lp->ldi_loaddr) lp->ldi_loaddr = addr; lp->ldi_ndata += cnt; for (; --cnt >= 0; ++addr) { if (wf_get(wf, &wdata) <= 0) { fprintf(stderr, "Loading aborted, read failed\n"); return 0; } vm_pset(ldvm_map(addr & H10MASK), wdata); } if (addr > lp->ldi_hiaddr) lp->ldi_hiaddr = addr-1; } /* Positive header word seen, assume entry vector */ if (lp->ldi_debug) printf(" ------ Entry vector word %#lo,,%lo ------\n", (long)LHGET(wdata), (long)RHGET(wdata)); lp->ldi_evlen = LHGET(wdata); lp->ldi_evloc = RHGET(wdata); addr = lp->ldi_evloc; /* Set up probable start addr */ if ((lp->ldi_evlen == LH_JRST) && !addr) addr = vm_pgetrh(ldvm_map((paddr_t)0120)); LRHSET(lp->ldi_startwd, 0, addr & H10MASK); return 1; } /* DEC SAV dumping code ** Similar to SBLK but a little simpler. */ static int dsav_out(register WFILE *wf, register paddr_t addr, register int len, register w10_t *wp) { register w10_t w; LRHSET(w, (-len)&H10MASK, (addr-1) & H10MASK); /* -,, */ if (wf_put(wf, w) <= 0) return 0; for (; --len >= 0; ++wp) if (wf_put(wf, *wp) <= 0) return 0; /* No checksum word in DEC SAV format */ return 1; } /* DEC EXE sharable SAVE format loading code */ /* DEC sharable SAVE format: : : data pages Each block has this general format: ,,<# words in block, including this word> <#-1 remaining words> : 1776,,<#> : <27-bit page # in file, 0 if none> <9-bit repeat cnt><27-bit page # in process> Access bits: B1 - pages are sharable B2 - pages are writable Repeat count: # of pages (minus 1) in group. : ; Optional in TOPS-10 1775,,3 <# words in entry vector> : ; optional, basically ignore this 1774,,<#> <#-1 words> : 1777,,1 Entry vector contents are the same as for SAV (non-sharable) format. TOPS-10 EXEs appear to leave out the entry vector block; in that case, the contents are taken from start addr = RH(120) reenter addr = RH(124) version info in 137 Again, just as for SAV format. */ #define DEC_MAXPHYSPGS ((paddr_t)1<<(PAG_PABITS-9)) /* # DEC pages on machine */ static void decld_clrpag(paddr_t); static int decld_rdpag(struct wfile *, paddr_t, paddr_t, int); static int load_decexe(register WFILE *wf, register struct loadinfo *lp) { register paddr_t addr; register int32 cnt; register int i; w10_t wdata; register w10_t *wp; #define DBUFLEN (1+(512*3)) /* Make room for directory area blocks */ w10_t wbuf[DBUFLEN]; /* For loading block data */ lp->ldi_evlen = -1; /* Init entry vector length in case fail */ wp = wbuf; if (wf_get(wf, wp) <= 0) { fprintf(stderr, "Loading aborted, first read failed\n"); return 0; } if (LHGET(*wp) != DECSSF_DIR) { fprintf(stderr, "1st word not directory section: %#lo\n", (long)LHGET(*wp)); return 0; } if ((cnt = RHGET(*wp)-1) > DBUFLEN) { fprintf(stderr, "Directory section too large: %ld words\n", (long)cnt+1); return 0; } if (cnt & 01) fprintf(stderr, "Warning, dir block has non-pair word count: %ld\n", (long)cnt+1); /* Read in directory section */ if (lp->ldi_debug) printf(" DIR section = %#lo wds\n", (long)cnt); for (i = cnt; --i >= 0;) { if (wf_get(wf, ++wp) <= 0) { fprintf(stderr, "Loading aborted, read failed in dir block\n"); return 0; } } /* Gobbled directory block, now get entry vector */ if (wf_get(wf, &wdata) <= 0) { fprintf(stderr, "Loading aborted, read failed after dir block\n"); return 0; } if (LHGET(wdata) != DECSSF_EV || RHGET(wdata) != 3) { if (lp->ldi_debug) fprintf(stderr, "No entvec block, word: %#lo,,%#lo\n", (long) LHGET(wdata), (long) RHGET(wdata)); lp->ldi_evlen = 0; lp->ldi_evloc = 0; } else { if (wf_get(wf, &wdata) <= 0) { fprintf(stderr, "Loading aborted, read failed in entvec block\n"); return 0; } lp->ldi_evlen = RHGET(wdata); if (wf_get(wf, &wdata) <= 0) { fprintf(stderr, "Loading aborted, read failed in entvec block\n"); return 0; } lp->ldi_evloc = ((paddr_t)LHGET(wdata) << 18) | RHGET(wdata); if (lp->ldi_debug) printf(" ENTVEC section = %#lo wds at %#lo\n", (long)lp->ldi_evlen, (long)lp->ldi_evloc); } /* At this point we could also scan for the PDV and END blocks, but ** why bother? */ /* Now grovel over the directory section, loading in all the pages it ** knows about. ** Since we're loading physical memory, the access bits are ignored. ** If the file page # is 0, the page group has its memory cleared; this may ** or may not be what the DEC bootstrap does, but seems useful. */ for (wp = wbuf+1; cnt > 0; cnt -= 2, wp += 2) { register paddr_t fpag = RHGET(wp[0]); register paddr_t ppag = RHGET(wp[1]); i = 1 + ((LHGET(wp[1]) >> 9) & 0777); /* High 9 bits are rpt cnt */ if (!fpag) { for (; --i >= 0; ++ppag) { if (lp->ldi_debug) printf(" Page %#lo: clear\n", (long)ppag); decld_clrpag(ppag); } } else { if (lp->ldi_debug) printf(" Page %#lo: file page %#lo (n=%d.)\n", (long)fpag, (long)ppag, i); decld_rdpag(wf, fpag, ppag, i); } } addr = lp->ldi_evloc; /* Set up probable start addr */ if (!addr && ( (lp->ldi_evlen == LH_JRST) /* Old vector? */ || (lp->ldi_evlen == 0))) { /* or no vector? */ addr = vm_pgetrh(ldvm_map((paddr_t)0120)); /* Use .JBSA */ } LRHSET(lp->ldi_startwd, 0, addr & H10MASK); return 1; } static void decld_clrpag(register paddr_t pag) { register int i = 512; register w10_t wz; if (pag >= DEC_MAXPHYSPGS) { fprintf(stderr, "Loader warning: trying to clear non-ex page %d\n", pag); return; } pag <<= 9; op10m_setz(wz); for (; --i >= 0; ++pag) vm_pset(ldvm_map(pag), wz); } static int decld_rdpag(register struct wfile *wf, paddr_t fpag, paddr_t ppag, int pcnt) { register paddr_t addr; register int i; w10_t w; if (!wf_seek(wf, (long)fpag<<9)) { return 0; } addr = ppag << 9; for (; --pcnt >= 0; ++ppag, ++fpag) { if (ppag >= DEC_MAXPHYSPGS) { fprintf(stderr, "Loading aborted, trying to load non-ex page %d\n", ppag); return 0; } for (i = 512; --i >= 0; ++addr) { if (wf_get(wf, &w) <= 0) { fprintf(stderr, "Loading aborted, read failed for file page %d, proc page %d\n", fpag, ppag); return 0; } vm_pset(ldvm_map(addr), w); } } return 1; }