[metal] Get zipos working again for legacy BIOS boot

examples/hellolua.c now runs again when booted as a legacy BIOS
disk image.

- output number of sectors to load as a patchable variable;
  for now the patching is done by tool/build/zipcopy.c
- remove references to _ezip
- add tweaks to metal mmap()
- implement lseek() & fstat() for metal memory files
This commit is contained in:
tkchia 2023-08-26 22:23:17 +00:00
parent 4c74f09393
commit 44dc43fcb5
13 changed files with 160 additions and 55 deletions

View file

@ -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

View file

@ -436,7 +436,6 @@ SECTIONS {
/*END: NT FORK COPYING */
_edata = .;
PROVIDE(edata = .);
_ezip = .; /* <-- very deprecated */
} :Ram
. = ALIGN(CONSTANT(COMMONPAGESIZE));
@ -632,12 +631,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

View file

@ -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__));

Binary file not shown.

View file

@ -17,14 +17,17 @@
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) {
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;
@ -32,7 +35,15 @@ int sys_fstat_metal(int fd, struct stat *st) {
st->st_mode = S_IFCHR | 0600;
st->st_blksize = 1;
return 0;
} else {
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();
}
}

View file

@ -43,6 +43,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) */

50
libc/calls/lseek-metal.c Normal file
View file

@ -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();
}
}

View file

@ -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()) {

View file

@ -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,

View file

@ -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) */

View file

@ -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);

View file

@ -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"

View file

@ -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");
}