diff --git a/ape/ape.S b/ape/ape.S index 348f21912..ecbc5834b 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -152,10 +152,13 @@ ape_mz: .short 0x0800 // MZ: increases cs load lower bound .short 0x0040 // MZ: reloc table offset .short 0 // MZ: overlay number + .ascii "'<<'@'\n" .org 0x24 // MZ: bytes reserved for you .ascii "JT" // MZ: OEM identifier - .short 0 // MZ: OEM information - .ascii "' <<'@'\n" + .short 1 // MZ: OEM information +__ape_com_sectors: + .short v_ape_allsectors // total number of sectors to load; + .endobj __ape_com_sectors,globl // to be patched by Cosmo tools .org 0x40-4 // MZ: bytes reserved for you #if SupportsWindows() || SupportsMetal() .long RVA(ape_pe) // PE: the new technology @@ -220,7 +223,7 @@ stub: mov $0x40,%dl // *literally* dos nop // system five bootpoint .org 0x48,0x90 // note ⌂ELF means JG 47 jmp 3f // MZ also means pop r10 -2: sub $8,%rsp // a.k.a. dec %ax sub %sp +2: push %rax // a.k.a. push %ax xor %edx,%edx // MZ ate BIOS drive code 3: .byte 0xbd,0,0 // a.k.a. mov imm,%bp jmp pc // real mode, is real @@ -291,7 +294,11 @@ pc: cld mov $1,%al // current sector xor %cx,%cx // current cylinder xor %dh,%dh // current head - mov $v_ape_realsectors,%di // total sectors + mov $v_ape_maxrealsectors,%di // no. sectors for base memory + mov REAL(__ape_com_sectors),%bp + cmp %bp,%di + jb 3f + mov %bp,%di 3: call pcread mov %es,%si // addr += 512 add $512>>4,%si @@ -417,13 +424,12 @@ pcread: push %ax ror %cl or %al,%cl xor %bx,%bx // es:bx is destination addr - mov $1,%al // read only one disk sector - mov $2,%ah // read disk sectors ordinal + mov $0x0201,%ax // read only one disk sector int $0x13 pop %cx pop %ax jc 9f - inc %al // ++sector + inc %ax // ++sector cmp mm+"struct mman::pc_drive_last_sector",%al jbe 2f mov $1,%al @@ -480,7 +486,7 @@ sinit4: mov $4,%cx jz 1f push %cx push %si - xchg %ax,%di + xchg %ax,%dx mov $REAL(sconf),%si call sinit pop %si @@ -491,18 +497,16 @@ sinit4: mov $4,%cx // Initializes Serial Line Communications 8250 UART 16550A // -// @param word di tty port +// @param word dx tty port // @param char (*{es:,e,r}si)[4] register initial values +// @clob dx // @mode long,legacy,real // @see www.lammertbies.nl/comm/info/serial-uart.html -sinit: mov %di,%dx - test %dx,%dx +sinit: test %dx,%dx jz 2f push %dx push %si - xor %cx,%cx - mov $UART_LCR,%cl - add %cx,%dx + add $UART_LCR,%dx lodsb %ds:(%si),%al pop %si or $UART_DLAB,%al @@ -1515,7 +1519,11 @@ cpyhi: push %es call unreal mov $IMAGE_BASE_REAL,%esi mov $IMAGE_BASE_PHYSICAL,%edi - mov $v_ape_realdwords,%ecx + mov $v_ape_maxrealsectors,%ecx + cmp %bp,%cx + jb 1f + mov %bp,%cx +1: shl $9-2,%ecx // convert sector count to dword count cld rep movsl %ds:(%esi),%es:(%edi) sti @@ -1545,13 +1553,16 @@ unreal: push %ds pop %ds ret -// Reads any remaining program pages into memory which have not yet -// been read by the boot sector. +// Reads any remaining program & data sectors into memory which have not +// yet been read by the boot sector. // -// @clob eax, ecx, dx, esi, edi, bp -loadhi: mov $v_ape_highsectors,%bp - test %bp,%bp - jz 9f +// @return ebp physical address after end of loaded sectors +// @clob eax, ecx, dx, esi, edi +loadhi: +#define SEG 0x79000 + mov $IMAGE_BASE_PHYSICAL+v_ape_maxrealbytes-SEG,%edi + sub $v_ape_maxrealsectors,%bp + jbe 9f mov $mm+"struct mman::pc_drive",%si cld lodsb //← pc_drive @@ -1565,8 +1576,6 @@ loadhi: mov $v_ape_highsectors,%bp xchg %ax,%cx mov (%si),%dh #← pc_drive_next_head push %es -#define SEG 0x79000 - mov $IMAGE_BASE_PHYSICAL+v_ape_realbytes-SEG,%edi push $SEG>>4 pop %es 0: call pcread @@ -1584,7 +1593,8 @@ loadhi: mov $v_ape_highsectors,%bp dec %bp jnz 0b pop %es -9: ret +9: lea SEG(%edi),%ebp + ret // Initializes long mode paging. pinit: push %ds @@ -1638,6 +1648,8 @@ golong: cli .endfn golong // Long mode is long. +// +// @param ebp physical address after end of loaded sectors .code64 long: movabs $BANE+PHYSICAL(0f),%rax jmp *%rax @@ -1653,12 +1665,12 @@ long: movabs $BANE+PHYSICAL(0f),%rax xor %r13d,%r13d xor %r14d,%r14d xor %r15d,%r15d - xor %ebx,%ebx - xor %ebp,%ebp mov $mm,%rdi mov %cr3,%rsi mov $IMAGE_BASE_PHYSICAL,%edx - lea v_ape_allbytes(%rdx),%ecx + mov %ebp,%ecx + xor %ebx,%ebx + xor %ebp,%ebp call __map_phdrs push $0x037f fldcw (%rsp) @@ -1792,9 +1804,9 @@ kernel: movabs $ape_stack_vaddr,%rsp .endm .ldsvar _end .ldsvar _etext - .ldsvar v_ape_realsectors - .ldsvar v_ape_realbytes - .ldsvar v_ape_highsectors + .ldsvar v_ape_maxrealsectors + .ldsvar v_ape_maxrealbytes + .ldsvar v_ape_allsectors .ldsvar ape_idata_ro .ldsvar ape_piro .ldsvar ape_piro_end diff --git a/ape/ape.lds b/ape/ape.lds index f08f9fce9..1348e75de 100644 --- a/ape/ape.lds +++ b/ape/ape.lds @@ -436,7 +436,6 @@ SECTIONS { /*END: NT FORK COPYING */ _edata = .; PROVIDE(edata = .); - _ezip = .; /* <-- very deprecated */ } :Ram . = ALIGN(CONSTANT(COMMONPAGESIZE)); @@ -633,12 +632,10 @@ APE_DECLARE_FIXED_DECIMAL(IDENTITY, blink_xnu_aarch64_size); #endif /* APE_IS_SHELL_SCRIPT */ #if SupportsMetal() -v_ape_realsectors = MIN(0x70000 - IMAGE_BASE_REAL, ROUNDUP(RVA(_ezip), 512)) / 512; -v_ape_realbytes = v_ape_realsectors * 512; -v_ape_realdwords = v_ape_realsectors * (512 / 4); -v_ape_allsectors = ROUNDUP(RVA(_ezip), 512) / 512; -v_ape_allbytes = v_ape_allsectors * 512; -v_ape_highsectors = MIN(0xffff, v_ape_allsectors - v_ape_realsectors); +v_ape_maxrealbytes = ROUNDUP(0x70000 - IMAGE_BASE_REAL, 512); +v_ape_maxrealsectors = v_ape_maxrealbytes / 512; +v_ape_allbytes = ROUNDUP(_edata - __executable_start, 512); +v_ape_allsectors = v_ape_allbytes / 512; TSSDESCSTUB2(_tss, _tss, _tss_end ? _tss_end - _tss - 1 : 0); #endif diff --git a/ape/sections.internal.h b/ape/sections.internal.h index cec1745dd..ae2193a7a 100644 --- a/ape/sections.internal.h +++ b/ape/sections.internal.h @@ -9,7 +9,6 @@ extern unsigned char __privileged_start[] __attribute__((__weak__)); extern unsigned char _ehead[] __attribute__((__weak__)); extern unsigned char _etext[] __attribute__((__weak__)); extern unsigned char _edata[] __attribute__((__weak__)); -extern unsigned char _ezip[] __attribute__((__weak__)); extern unsigned char _end[] __attribute__((__weak__)); extern unsigned char _ereal[] __attribute__((__weak__)); extern unsigned char _tdata_start[] __attribute__((__weak__)); diff --git a/build/bootstrap/zipcopy.com b/build/bootstrap/zipcopy.com index a448c6cf8..ae8cdc8ae 100755 Binary files a/build/bootstrap/zipcopy.com and b/build/bootstrap/zipcopy.com differ diff --git a/libc/calls/fstat-metal.c b/libc/calls/fstat-metal.c index 66732d78e..ffd782a42 100644 --- a/libc/calls/fstat-metal.c +++ b/libc/calls/fstat-metal.c @@ -17,22 +17,33 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/calls/metalfile.internal.h" #include "libc/calls/struct/stat.h" #include "libc/str/str.h" #include "libc/sysv/consts/s.h" #include "libc/sysv/errfuns.h" int sys_fstat_metal(int fd, struct stat *st) { - if (fd < 0) return einval(); - if (fd < g_fds.n && g_fds.p[fd].kind == kFdSerial) { - bzero(st, sizeof(*st)); - st->st_dev = g_fds.p[fd].handle; - st->st_rdev = g_fds.p[fd].handle; - st->st_nlink = 1; - st->st_mode = S_IFCHR | 0600; - st->st_blksize = 1; - return 0; - } else { - return ebadf(); + struct MetalFile *file; + if (fd < 0 || fd >= g_fds.n) return einval(); + switch (g_fds.p[fd].kind) { + case kFdSerial: + bzero(st, sizeof(*st)); + st->st_dev = g_fds.p[fd].handle; + st->st_rdev = g_fds.p[fd].handle; + st->st_nlink = 1; + st->st_mode = S_IFCHR | 0600; + st->st_blksize = 1; + return 0; + case kFdFile: + file = (struct MetalFile *)g_fds.p[fd].handle; + bzero(st, sizeof(*st)); + st->st_nlink = 1; + st->st_size = file->size; + st->st_mode = S_IFREG | 0600; + st->st_blksize = 1; + return 0; + default: + return ebadf(); } } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 0b8af5aea..54f2b1c34 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -44,6 +44,7 @@ forceinline bool __isfdkind(int fd, int kind) { int _check_interrupts(int); int sys_close_nt(struct Fd *, int); int sys_openat_metal(int, const char *, int, unsigned); +int64_t sys_lseek_metal(struct Fd *, int64_t, int); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/lseek-metal.c b/libc/calls/lseek-metal.c new file mode 100644 index 000000000..a004b7e35 --- /dev/null +++ b/libc/calls/lseek-metal.c @@ -0,0 +1,50 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/metalfile.internal.h" +#include "libc/calls/struct/fd.internal.h" +#include "libc/sysv/errfuns.h" + +int64_t sys_lseek_metal(struct Fd *fd, int64_t offset, int whence) { + size_t new_pos; + struct MetalFile *file; + switch (fd->kind) { + case kFdFile: + file = (struct MetalFile *)fd->handle; + switch (whence) { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos = file->pos + offset; + break; + case SEEK_END: + new_pos = file->size + offset; + break; + default: + return einval(); + } + if (new_pos > file->size) return einval(); + file->pos = new_pos; + return new_pos; + default: + return ebadf(); + } +} diff --git a/libc/calls/lseek.c b/libc/calls/lseek.c index fff3b7efe..71bf8f36c 100644 --- a/libc/calls/lseek.c +++ b/libc/calls/lseek.c @@ -77,9 +77,11 @@ */ int64_t lseek(int fd, int64_t offset, int whence) { int64_t rc; - if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { + if (fd >= 0 && fd < g_fds.n && g_fds.p[fd].kind == kFdZip) { rc = _weaken(__zipos_seek)( (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, offset, whence); + } else if (fd >= 0 && fd < g_fds.n && IsMetal()) { + rc = sys_lseek_metal(g_fds.p + fd, offset, whence); } else if (IsLinux() || IsXnu() || IsFreebsd() || IsOpenbsd()) { rc = sys_lseek(fd, offset, whence, 0); } else if (IsNetbsd()) { diff --git a/libc/calls/metalfile.c b/libc/calls/metalfile.c index 3b46de96e..a657b0b3c 100644 --- a/libc/calls/metalfile.c +++ b/libc/calls/metalfile.c @@ -53,7 +53,7 @@ void *__ape_com_base; size_t __ape_com_size = 0; textstartup dontasan void InitializeMetalFile(void) { - if (IsMetal()) { + if (IsMetal() && _weaken(__ape_com_sectors)) { /* * Copy out a pristine image of the program — before the program might * decide to modify its own .data section. @@ -63,8 +63,7 @@ textstartup dontasan void InitializeMetalFile(void) { * The zipos code will automatically arrange to do this. Alternatively, * user code can __static_yoink this symbol. */ - size_t size = ROUNDUP(_ezip - __executable_start, 4096); - // TODO(jart): Restore support for ZIPOS on metal. + size_t size = ROUNDUP((size_t)__ape_com_sectors * 512, 4096); void *copied_base; struct DirectMap dm; dm = sys_mmap_metal(NULL, size, PROT_READ | PROT_WRITE, diff --git a/libc/calls/metalfile.internal.h b/libc/calls/metalfile.internal.h index a7586c85d..5a59c8c98 100644 --- a/libc/calls/metalfile.internal.h +++ b/libc/calls/metalfile.internal.h @@ -11,6 +11,7 @@ struct MetalFile { extern void *__ape_com_base; extern size_t __ape_com_size; +extern uint16_t __ape_com_sectors; // ape/ape.S COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/intrin/directmap-metal.c b/libc/intrin/directmap-metal.c index 463ac7637..11eb13ed9 100644 --- a/libc/intrin/directmap-metal.c +++ b/libc/intrin/directmap-metal.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/metalfile.internal.h" @@ -46,6 +47,10 @@ dontasan struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot, struct mman *mm; struct DirectMap res; uint64_t addr, faddr = 0, page, e, *pte, *fdpte, *pml4t; + if ((uintptr_t)BANE + size < (uintptr_t)BANE) { + assert(false); + return bad_mmap(); + } mm = __get_mm(); pml4t = __get_pml4t(); size = ROUNDUP(size, 4096); @@ -69,11 +74,14 @@ dontasan struct DirectMap sys_mmap_metal(void *vaddr, size_t size, int prot, } else { addr = ROUNDUP(addr, 4096); } - for (i = 0; i < size; i += 4096) { + i = 0; + while (i < size) { pte = __get_virtual(mm, pml4t, addr + i, false); if (pte && (*pte & (PAGE_V | PAGE_RSRV))) { addr = MAX(addr, sys_mmap_metal_break) + i + 4096; i = 0; + } else { + i += 4096; } } sys_mmap_metal_break = MAX(addr + size, sys_mmap_metal_break); diff --git a/libc/intrin/interrupts.S b/libc/intrin/interrupts.S index 933278725..557ca2b2c 100644 --- a/libc/intrin/interrupts.S +++ b/libc/intrin/interrupts.S @@ -25,6 +25,7 @@ │ OTHER DEALINGS IN THE SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" +#include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/runtime/pc.internal.h" diff --git a/tool/build/zipcopy.c b/tool/build/zipcopy.c index a54b66a2c..b9480ff4c 100644 --- a/tool/build/zipcopy.c +++ b/tool/build/zipcopy.c @@ -23,11 +23,15 @@ #include "libc/elf/struct/shdr.h" #include "libc/errno.h" #include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/newbie.h" #include "libc/limits.h" +#include "libc/nt/struct/imagedosheader.internal.h" +#include "libc/runtime/pc.internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" #include "libc/zip.internal.h" @@ -109,6 +113,25 @@ static void GetOpts(int argc, char *argv[]) { outpath = argv[optind + 1]; } +static void BiosZipFix(unsigned long cdest) { + struct NtImageDosHeader head; + size_t hd_sz = sizeof(head); + size_t e_oemid = offsetof(struct NtImageDosHeader, e_oemid); + size_t e_oeminfo = offsetof(struct NtImageDosHeader, e_oeminfo); + if (outsize < hd_sz) return; + if (pread(outfd, &head, hd_sz, 0) != hd_sz) return; + if (READ64LE((uint8_t *)&head) != READ64LE("MZqFpD='") && + READ64LE((uint8_t *)&head) != READ64LE("jartsr='")) return; + if (le16toh(head.e_oemid) != READ16LE("JT") || + le16toh(head.e_oeminfo) != 1) return; + // patch executable head so that it will load the right count of sectors + // when run as a legacy BIOS disk image + head.e_res2[0] = htole16(ROUNDUP(cdest, 0x200) / 0x200); + if (pwrite(outfd, &head, hd_sz, 0) != hd_sz) { + SysDie(outpath, "head pwrite"); + } +} + static void CopyZip(void) { char *secstrs; int rela, recs; @@ -176,7 +199,7 @@ static void CopyZip(void) { } // write output - if ((outfd = open(outpath, O_WRONLY | O_CREAT, 0644)) == -1) { + if ((outfd = open(outpath, O_RDWR | O_CREAT, 0644)) == -1) { SysDie(outpath, "open"); } if ((outsize = lseek(outfd, 0, SEEK_END)) == -1) { @@ -205,6 +228,7 @@ static void CopyZip(void) { if (pwrite(outfd, eocd, length, cdest) != length) { SysDie(outpath, "eocd pwrite"); } + BiosZipFix(cdest); if (close(outfd)) { SysDie(outpath, "close"); }