Fix metal bugs so deathstar.com runs in qemu (#20)

- Remove XD bit in page tables
- Fix cylinder+head+sector arithmetic
- Implement fstat() for serial file descriptors on metal

Here's how to boot an Actually Portable Executable in QEMU:

    make -j12 o//tool/viz/deathstar.com
    qemu-system-x86_64 -serial stdio -fda o//tool/viz/deathstar.com

Here's a screenshot of DEATHSTAR.COM booted in QEMU:
https://justine.lol/cosmopolitan/cosmo-metal-qemu.png

Thus metal support is in much better shape now, but still incomplete.
Only a few system calls have been polyfilled. To figure out which ones
your program needs, simply boot it in the blinkenlights emulator with a
breakpoint, and press CTRL-C to continue to the system call breakpoint.
If it doesn't break then you should be good. (Note: to emulate normally
you can press 'c' and use CTRL-T and ALT-T to tune the speed.)

    m=tiny
    make -j12 SILENT=0 MODE=$m          \
      o/$m/tool/build/blinkenlights.com \
      o/$m/tool/viz/deathstar.com
    o/$m/tool/build/blinkenlights.com   \
      -r -t -b systemfive.linux         \
      o/$m/tool/viz/deathstar.com

Thank @Theldus for the bug report that made this change possible.
Fixes #20 which explains this change further.
This commit is contained in:
Justine Tunney 2021-01-16 17:52:15 -08:00
parent 58d9659d53
commit f0600a898c
13 changed files with 182 additions and 119 deletions

View file

@ -239,18 +239,17 @@ pc: cld
/ can have an understanding of physical locality, which deeply / can have an understanding of physical locality, which deeply
/ impacts the latency of operations. / impacts the latency of operations.
/ /
/ - 160KB: 1 head × 40 cylinders × 8 sectors × 512 = 163,840 / - 160KB: 40 cylinders × 1 head × 8 sectors × 512 = 163,840
/ - 180KB: 1 head × 40 cylinders × 9 sectors × 512 = 184,320 / - 180KB: 40 cylinders × 1 head × 9 sectors × 512 = 184,320
/ - 320KB: 2 heads × 40 cylinders × 8 sectors × 512 = 327,680 / - 320KB: 40 cylinders × 2 heads × 8 sectors × 512 = 327,680
/ - 360KB: 2 heads × 40 cylinders × 9 sectors × 512 = 368,640 / - 360KB: 40 cylinders × 2 heads × 9 sectors × 512 = 368,640
/ - 720KB: 2 heads × 80 cylinders × 9 sectors × 512 = 737,280 / - 720KB: 80 cylinders × 2 heads × 9 sectors × 512 = 737,280
/ - 1.2MB: 2 heads × 80 cylinders × 15 sectors × 512 = 1,228,800 / - 1.2MB: 80 cylinders × 2 heads × 15 sectors × 512 = 1,228,800
/ - 1.44MB: 2 heads × 80 cylinders × 18 sectors × 512 = 1,474,560 / - 1.44MB: 80 cylinders × 2 heads × 18 sectors × 512 = 1,474,560
/ /
/ Terminology / Terminology
/ /
/ - Cylinder / Tracks should mean the same thing / - Heads are also known as Tracks
/ - Heads / Sides / Spindles should mean the same thing
/ /
/ Disk Base Table / Disk Base Table
/ /
@ -341,18 +340,18 @@ pcread: push %ax
pop %cx pop %cx
pop %ax pop %ax
jc 9f jc 9f
mov %es,%si mov %es,%si # addr += 512
add $512>>4,%si add $512>>4,%si
mov %si,%es mov %si,%es
inc %al inc %al # ++sector
cmp XLM(DRIVE_LAST_SECTOR),%al cmp XLM(DRIVE_LAST_SECTOR),%al
jbe 2f jbe 2f
mov $1,%al mov $1,%al
inc %cx inc %dh # ++head
cmp XLM(DRIVE_LAST_CYLINDER),%cx cmp XLM(DRIVE_LAST_HEAD),%cx
jbe 2f jb 2f
xor %cx,%cx xor %dh,%dh
inc %dh inc %cx # ++cylinder
2: ret 2: ret
9: push %ax 9: push %ax
xor %ax,%ax # try disk reset on error xor %ax,%ax # try disk reset on error

View file

@ -30,7 +30,7 @@ textreal static void __map_segment(uint64_t k, uint64_t a, uint64_t b) {
textreal void __map_image(void) { textreal void __map_image(void) {
__map_segment(PAGE_V | PAGE_U, 0, (uintptr_t)_etext - IMAGE_BASE_VIRTUAL); __map_segment(PAGE_V | PAGE_U, 0, (uintptr_t)_etext - IMAGE_BASE_VIRTUAL);
__map_segment(PAGE_V | PAGE_U | PAGE_RW | PAGE_XD, __map_segment(PAGE_V | PAGE_U | PAGE_RW,
(uintptr_t)_etext - IMAGE_BASE_VIRTUAL, (uintptr_t)_etext - IMAGE_BASE_VIRTUAL,
(uintptr_t)_end - IMAGE_BASE_VIRTUAL); (uintptr_t)_end - IMAGE_BASE_VIRTUAL);
} }

View file

@ -11,6 +11,7 @@
#include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/fmt.h" #include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/runtime/gc.h" #include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h" #include "libc/stdio/stdio.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -23,32 +24,29 @@
void PrintFileMetadata(const char *pathname, struct stat *st) { void PrintFileMetadata(const char *pathname, struct stat *st) {
printf("\n%s:", pathname); printf("\n%s:", pathname);
if (stat(pathname, st) != -1) { CHECK_NE(-1, stat(pathname, st));
printf( printf("\n"
"\n" "%-32s%,ld\n"
"%-32s%,ld\n" "%-32s%,ld\n"
"%-32s%,ld\n" "%-32s%#lx\n"
"%-32s%#lx\n" "%-32s%#lx\n"
"%-32s%#lx\n" "%-32s%ld\n"
"%-32s%ld\n" "%-32s%#o\n"
"%-32s%#o\n" "%-32s%d\n"
"%-32s%d\n" "%-32s%d\n"
"%-32s%d\n" "%-32s%d\n"
"%-32s%d\n" "%-32s%ld\n"
"%-32s%ld\n" "%-32s%s\n"
"%-32s%s\n" "%-32s%s\n"
"%-32s%s\n" "%-32s%s\n",
"%-32s%s\n", "bytes in file", st->st_size, "physical bytes", st->st_blocks * 512,
"bytes in file", st->st_size, "physical bytes", st->st_blocks * 512, "device id w/ file", st->st_dev, "inode", st->st_ino,
"device id w/ file", st->st_dev, "inode", st->st_ino, "hard link count", "hard link count", st->st_nlink, "mode / permissions", st->st_mode,
st->st_nlink, "mode / permissions", st->st_mode, "owner id", st->st_uid, "owner id", st->st_uid, "group id", st->st_gid,
"group id", st->st_gid, "device id (if special)", st->st_rdev, "device id (if special)", st->st_rdev, "block size", st->st_blksize,
"block size", st->st_blksize, "access time", gc(xiso8601(&st->st_atim)), "access time", gc(xiso8601(&st->st_atim)), "modified time",
"modified time", gc(xiso8601(&st->st_mtim)), "c[omplicated]time", gc(xiso8601(&st->st_mtim)), "c[omplicated]time",
gc(xiso8601(&st->st_ctim))); gc(xiso8601(&st->st_ctim)));
} else {
printf(" %s\n", strerror(errno));
}
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {

38
libc/calls/fstat-metal.c Normal file
View file

@ -0,0 +1,38 @@
/*-*- 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 2021 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/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 fstat$metal(int fd, struct stat *st) {
if (fd < 0) return einval();
if (fd < g_fds.n && g_fds.p[fd].kind == kFdSerial) {
memset(st, 0, 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();
}
}

View file

@ -35,34 +35,47 @@ textwindows int fstat$nt(int64_t handle, struct stat *st) {
uint64_t actualsize; uint64_t actualsize;
struct NtFileCompressionInfo fci; struct NtFileCompressionInfo fci;
struct NtByHandleFileInformation wst; struct NtByHandleFileInformation wst;
if (GetFileInformationByHandle(handle, &wst)) { if ((filetype = GetFileType(handle))) {
memset(st, 0, sizeof(*st)); memset(st, 0, sizeof(*st));
filetype = GetFileType(handle); switch (filetype) {
st->st_mode = case kNtFileTypeChar:
(S_IRUSR | S_IXUSR | st->st_mode = S_IFCHR | 0600;
(!(wst.dwFileAttributes & kNtFileAttributeReadonly) ? S_IWUSR : 0) | break;
((wst.dwFileAttributes & kNtFileAttributeNormal) ? S_IFREG : 0) | case kNtFileTypePipe:
((wst.dwFileAttributes & kNtFileFlagOpenReparsePoint) ? S_IFLNK : 0) | st->st_mode = S_IFIFO | 0600;
((wst.dwFileAttributes & kNtFileAttributeDirectory) break;
? S_IFDIR case kNtFileTypeDisk:
: (((filetype == kNtFileTypeDisk) ? S_IFBLK : 0) | if (GetFileInformationByHandle(handle, &wst)) {
((filetype == kNtFileTypeChar) ? S_IFCHR : 0) | dprintf(1, "handle = %ld\n", handle);
((filetype == kNtFileTypePipe) ? S_IFIFO : 0)))); st->st_mode =
st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime); (S_IRUSR | S_IXUSR |
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime); (!(wst.dwFileAttributes & kNtFileAttributeReadonly) ? S_IWUSR
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime); : 0) |
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow; ((wst.dwFileAttributes & kNtFileAttributeNormal) ? S_IFREG : 0) |
st->st_blksize = PAGESIZE; ((wst.dwFileAttributes & kNtFileFlagOpenReparsePoint) ? S_IFLNK
st->st_dev = wst.dwVolumeSerialNumber; : 0) |
st->st_ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow; ((wst.dwFileAttributes & kNtFileAttributeDirectory) ? S_IFDIR
st->st_nlink = wst.nNumberOfLinks; : 0));
if (GetFileInformationByHandleEx(handle, kNtFileCompressionInfo, &fci, st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
sizeof(fci))) { st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
actualsize = fci.CompressedFileSize; st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
} else { st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
actualsize = st->st_size; st->st_blksize = PAGESIZE;
st->st_dev = wst.dwVolumeSerialNumber;
st->st_ino = (uint64_t)wst.nFileIndexHigh << 32 | wst.nFileIndexLow;
st->st_nlink = wst.nNumberOfLinks;
if (GetFileInformationByHandleEx(handle, kNtFileCompressionInfo, &fci,
sizeof(fci))) {
actualsize = fci.CompressedFileSize;
} else {
actualsize = st->st_size;
}
st->st_blocks = roundup(actualsize, PAGESIZE) / 512;
}
break;
default:
break;
} }
st->st_blocks = roundup(actualsize, PAGESIZE) / 512;
return 0; return 0;
} else { } else {
return __winerr(); return __winerr();

View file

@ -32,7 +32,11 @@ int fstat(int fd, struct stat *st) {
return weaken(__zipos_fstat)( return weaken(__zipos_fstat)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st); (struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st);
} else if (!IsWindows()) { } else if (!IsWindows()) {
return fstat$sysv(fd, st); if (!IsMetal()) {
return fstat$sysv(fd, st);
} else {
return fstat$metal(fd, st);
}
} else { } else {
if (!__isfdkind(fd, kFdFile)) return ebadf(); if (!__isfdkind(fd, kFdFile)) return ebadf();
return fstat$nt(g_fds.p[fd].handle, st); return fstat$nt(g_fds.p[fd].handle, st);

View file

@ -267,6 +267,12 @@ int64_t __winerr(void) nocallback privileged;
int __mkntpath(const char *, char16_t[hasatleast PATH_MAX - 16]) hidden; int __mkntpath(const char *, char16_t[hasatleast PATH_MAX - 16]) hidden;
int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden; int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » metal
*/
int fstat$metal(int, struct stat *);
/*───────────────────────────────────────────────────────────────────────────│─╗ /*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » drivers cosmopolitan § syscalls » drivers
*/ */

View file

@ -30,16 +30,11 @@
textstartup bool32 ischardev(int fd) { textstartup bool32 ischardev(int fd) {
int olderr; int olderr;
struct stat st; struct stat st;
if (!IsWindows()) { olderr = errno;
olderr = errno; if (fstat(fd, &st) != -1) {
if (fstat$sysv(fd, &st) != -1) { return S_ISCHR(st.st_mode);
return S_ISCHR(st.st_mode);
} else {
errno = olderr;
return false;
}
} else { } else {
return __isfdkind(fd, kFdFile) && errno = olderr;
GetFileType(g_fds.p[fd].handle) == kNtFileTypeChar; return false;
} }
} }

View file

@ -31,7 +31,7 @@ forceinline typeof(PrefetchVirtualMemory) *GetPrefetchVirtualMemory(void) {
if (!once) { if (!once) {
once = true; once = true;
PrefetchVirtualMemory_ = /* win8.1+ */ PrefetchVirtualMemory_ = /* win8.1+ */
GetProcAddressModule("KernelBase.dll", "PrefetchVirtualMemory"); GetProcAddressModule("Kernel32.dll", "PrefetchVirtualMemory");
} }
return PrefetchVirtualMemory_; return PrefetchVirtualMemory_;
} }
@ -42,7 +42,7 @@ forceinline typeof(OfferVirtualMemory) *GetOfferVirtualMemory(void) {
if (!once) { if (!once) {
once = true; once = true;
OfferVirtualMemory_ = /* win8.1+ */ OfferVirtualMemory_ = /* win8.1+ */
GetProcAddressModule("KernelBase.dll", "OfferVirtualMemory"); GetProcAddressModule("Kernel32.dll", "OfferVirtualMemory");
} }
return OfferVirtualMemory_; return OfferVirtualMemory_;
} }

View file

@ -27,10 +27,14 @@
int nanosleep(const struct timespec *req, struct timespec *rem) { int nanosleep(const struct timespec *req, struct timespec *rem) {
if (!req) return efault(); if (!req) return efault();
if (!IsWindows()) { if (!IsWindows()) {
if (!IsXnu()) { if (!IsMetal()) {
return nanosleep$sysv(req, rem); if (!IsXnu()) {
return nanosleep$sysv(req, rem);
} else {
return nanosleep$xnu(req, rem);
}
} else { } else {
return nanosleep$xnu(req, rem); return enosys(); /* TODO: Sleep on Metal */
} }
} else { } else {
return nanosleep$nt(req, rem); return nanosleep$nt(req, rem);

View file

@ -141,32 +141,36 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
return efault(); return efault();
} }
if (!IsWindows()) { if (!IsWindows()) {
if (act) { if (!IsMetal()) {
memcpy(&copy, act, sizeof(copy)); if (act) {
ap = &copy; memcpy(&copy, act, sizeof(copy));
if (IsXnu()) { ap = &copy;
ap->sa_restorer = (void *)&xnutrampoline; if (IsXnu()) {
ap->sa_handler = (void *)&xnutrampoline; ap->sa_restorer = (void *)&xnutrampoline;
} else { ap->sa_handler = (void *)&xnutrampoline;
if (IsLinux()) { } else {
if (!(ap->sa_flags & SA_RESTORER)) { if (IsLinux()) {
ap->sa_flags |= SA_RESTORER; if (!(ap->sa_flags & SA_RESTORER)) {
ap->sa_restorer = &__restore_rt; ap->sa_flags |= SA_RESTORER;
ap->sa_restorer = &__restore_rt;
}
}
if (rva >= kSigactionMinRva) {
ap->sa_sigaction = (sigaction_f)__sigenter;
} }
} }
if (rva >= kSigactionMinRva) { sigaction$cosmo2native((union metasigaction *)ap);
ap->sa_sigaction = (sigaction_f)__sigenter; } else {
} ap = NULL;
} }
sigaction$cosmo2native((union metasigaction *)ap); rc = sigaction$sysv(
sig, ap, oldact,
(!IsXnu() ? 8 /* or linux whines */
: (int64_t)(intptr_t)oldact /* from go code */));
if (rc != -1) sigaction$native2cosmo((union metasigaction *)oldact);
} else { } else {
ap = NULL; return enosys(); /* TODO: Signals on Metal */
} }
rc = sigaction$sysv(
sig, ap, oldact,
(!IsXnu() ? 8 /* or linux whines */
: (int64_t)(intptr_t)oldact /* from go code */));
if (rc != -1) sigaction$native2cosmo((union metasigaction *)oldact);
} else { } else {
if (oldact) { if (oldact) {
memset(oldact, 0, sizeof(*oldact)); memset(oldact, 0, sizeof(*oldact));

View file

@ -28,7 +28,10 @@ program_invocation_short_name:
.init.start 400,_init_program_invocation_short_name .init.start 400,_init_program_invocation_short_name
push %rsi push %rsi
mov (%r13),%rsi xor %eax,%eax
test %r12d,%r12d # argc
jz 2f
mov (%r13),%rsi # argv[0]
mov %rsi,%rcx mov %rsi,%rcx
1: lodsb 1: lodsb
cmp $'/,%al cmp $'/,%al
@ -39,5 +42,5 @@ program_invocation_short_name:
jnz 1b jnz 1b
xchg %rcx,%rax xchg %rcx,%rax
pop %rsi pop %rsi
stosq 2: stosq
.init.end 400,_init_program_invocation_short_name .init.end 400,_init_program_invocation_short_name

View file

@ -1789,14 +1789,14 @@ static void OnDiskServiceBadCommand(void) {
} }
static void OnDiskServiceGetParams(void) { static void OnDiskServiceGetParams(void) {
size_t lastsector, lasttrack, lasthead; size_t lastsector, lastcylinder, lasthead;
lasthead = GetLastIndex(elf->mapsize, 512 * 63 * 1024, 0, 255); lastcylinder = GetLastIndex(elf->mapsize, 512 * 63 * 255, 0, 1023);
lasttrack = GetLastIndex(elf->mapsize, 512 * 63, 0, 1023); lasthead = GetLastIndex(elf->mapsize, 512 * 63, 0, 255);
lastsector = GetLastIndex(elf->mapsize, 512, 1, 63); lastsector = GetLastIndex(elf->mapsize, 512, 1, 63);
m->dx[0] = 1; m->dx[0] = 1;
m->dx[1] = lasthead; m->dx[1] = lasthead;
m->cx[0] = lasttrack >> 8 << 6 | lastsector; m->cx[0] = lastcylinder >> 8 << 6 | lastsector;
m->cx[1] = lasttrack; m->cx[1] = lastcylinder;
m->ax[1] = 0; m->ax[1] = 0;
Write64(m->es, 0); Write64(m->es, 0);
Write16(m->di, 0); Write16(m->di, 0);
@ -1806,18 +1806,17 @@ static void OnDiskServiceGetParams(void) {
static void OnDiskServiceReadSectors(void) { static void OnDiskServiceReadSectors(void) {
static int x; static int x;
uint64_t addr, size; uint64_t addr, size;
int64_t sectors, drive, head, track, sector, offset; int64_t sectors, drive, head, cylinder, sector, offset;
sectors = m->ax[0]; sectors = m->ax[0];
drive = m->dx[0]; drive = m->dx[0];
head = m->dx[1]; head = m->dx[1];
track = (m->cx[0] & 0b11000000) << 2 | m->cx[1]; cylinder = (m->cx[0] & 0b11000000) << 2 | m->cx[1];
sector = (m->cx[0] & 0b00111111) - 1; sector = (m->cx[0] & 0b00111111) - 1;
offset = head * track * sector * 512;
size = sectors * 512; size = sectors * 512;
offset = sector * 512 + track * 512 * 63 + head * 512 * 63 * 1024; offset = sector * 512 + head * 512 * 63 + cylinder * 512 * 63 * 255;
VERBOSEF("bios read sectors %d " VERBOSEF("bios read sectors %d "
"@ sector %ld track %ld head %ld drive %ld offset %#lx", "@ sector %ld cylinder %ld head %ld drive %ld offset %#lx",
sectors, sector, track, head, drive, offset); sectors, sector, cylinder, head, drive, offset);
if (0 <= sector && offset + size <= elf->mapsize) { if (0 <= sector && offset + size <= elf->mapsize) {
addr = Read64(m->es) + Read16(m->bx); addr = Read64(m->es) + Read16(m->bx);
if (addr + size <= m->real.n) { if (addr + size <= m->real.n) {