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
/ impacts the latency of operations.
/
/ - 160KB: 1 head × 40 cylinders × 8 sectors × 512 = 163,840
/ - 180KB: 1 head × 40 cylinders × 9 sectors × 512 = 184,320
/ - 320KB: 2 heads × 40 cylinders × 8 sectors × 512 = 327,680
/ - 360KB: 2 heads × 40 cylinders × 9 sectors × 512 = 368,640
/ - 720KB: 2 heads × 80 cylinders × 9 sectors × 512 = 737,280
/ - 1.2MB: 2 heads × 80 cylinders × 15 sectors × 512 = 1,228,800
/ - 1.44MB: 2 heads × 80 cylinders × 18 sectors × 512 = 1,474,560
/ - 160KB: 40 cylinders × 1 head × 8 sectors × 512 = 163,840
/ - 180KB: 40 cylinders × 1 head × 9 sectors × 512 = 184,320
/ - 320KB: 40 cylinders × 2 heads × 8 sectors × 512 = 327,680
/ - 360KB: 40 cylinders × 2 heads × 9 sectors × 512 = 368,640
/ - 720KB: 80 cylinders × 2 heads × 9 sectors × 512 = 737,280
/ - 1.2MB: 80 cylinders × 2 heads × 15 sectors × 512 = 1,228,800
/ - 1.44MB: 80 cylinders × 2 heads × 18 sectors × 512 = 1,474,560
/
/ Terminology
/
/ - Cylinder / Tracks should mean the same thing
/ - Heads / Sides / Spindles should mean the same thing
/ - Heads are also known as Tracks
/
/ Disk Base Table
/
@ -341,18 +340,18 @@ pcread: push %ax
pop %cx
pop %ax
jc 9f
mov %es,%si
mov %es,%si # addr += 512
add $512>>4,%si
mov %si,%es
inc %al
inc %al # ++sector
cmp XLM(DRIVE_LAST_SECTOR),%al
jbe 2f
mov $1,%al
inc %cx
cmp XLM(DRIVE_LAST_CYLINDER),%cx
jbe 2f
xor %cx,%cx
inc %dh
inc %dh # ++head
cmp XLM(DRIVE_LAST_HEAD),%cx
jb 2f
xor %dh,%dh
inc %cx # ++cylinder
2: ret
9: push %ax
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) {
__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)_end - IMAGE_BASE_VIRTUAL);
}

View file

@ -11,6 +11,7 @@
#include "libc/calls/struct/stat.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/x/x.h"
@ -23,32 +24,29 @@
void PrintFileMetadata(const char *pathname, struct stat *st) {
printf("\n%s:", pathname);
if (stat(pathname, st) != -1) {
printf(
"\n"
"%-32s%,ld\n"
"%-32s%,ld\n"
"%-32s%#lx\n"
"%-32s%#lx\n"
"%-32s%ld\n"
"%-32s%#o\n"
"%-32s%d\n"
"%-32s%d\n"
"%-32s%d\n"
"%-32s%ld\n"
"%-32s%s\n"
"%-32s%s\n"
"%-32s%s\n",
"bytes in file", st->st_size, "physical bytes", st->st_blocks * 512,
"device id w/ file", st->st_dev, "inode", st->st_ino, "hard link count",
st->st_nlink, "mode / permissions", st->st_mode, "owner id", st->st_uid,
"group id", st->st_gid, "device id (if special)", st->st_rdev,
"block size", st->st_blksize, "access time", gc(xiso8601(&st->st_atim)),
"modified time", gc(xiso8601(&st->st_mtim)), "c[omplicated]time",
gc(xiso8601(&st->st_ctim)));
} else {
printf(" %s\n", strerror(errno));
}
CHECK_NE(-1, stat(pathname, st));
printf("\n"
"%-32s%,ld\n"
"%-32s%,ld\n"
"%-32s%#lx\n"
"%-32s%#lx\n"
"%-32s%ld\n"
"%-32s%#o\n"
"%-32s%d\n"
"%-32s%d\n"
"%-32s%d\n"
"%-32s%ld\n"
"%-32s%s\n"
"%-32s%s\n"
"%-32s%s\n",
"bytes in file", st->st_size, "physical bytes", st->st_blocks * 512,
"device id w/ file", st->st_dev, "inode", st->st_ino,
"hard link count", st->st_nlink, "mode / permissions", st->st_mode,
"owner id", st->st_uid, "group id", st->st_gid,
"device id (if special)", st->st_rdev, "block size", st->st_blksize,
"access time", gc(xiso8601(&st->st_atim)), "modified time",
gc(xiso8601(&st->st_mtim)), "c[omplicated]time",
gc(xiso8601(&st->st_ctim)));
}
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;
struct NtFileCompressionInfo fci;
struct NtByHandleFileInformation wst;
if (GetFileInformationByHandle(handle, &wst)) {
if ((filetype = GetFileType(handle))) {
memset(st, 0, sizeof(*st));
filetype = GetFileType(handle);
st->st_mode =
(S_IRUSR | S_IXUSR |
(!(wst.dwFileAttributes & kNtFileAttributeReadonly) ? S_IWUSR : 0) |
((wst.dwFileAttributes & kNtFileAttributeNormal) ? S_IFREG : 0) |
((wst.dwFileAttributes & kNtFileFlagOpenReparsePoint) ? S_IFLNK : 0) |
((wst.dwFileAttributes & kNtFileAttributeDirectory)
? S_IFDIR
: (((filetype == kNtFileTypeDisk) ? S_IFBLK : 0) |
((filetype == kNtFileTypeChar) ? S_IFCHR : 0) |
((filetype == kNtFileTypePipe) ? S_IFIFO : 0))));
st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
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;
switch (filetype) {
case kNtFileTypeChar:
st->st_mode = S_IFCHR | 0600;
break;
case kNtFileTypePipe:
st->st_mode = S_IFIFO | 0600;
break;
case kNtFileTypeDisk:
if (GetFileInformationByHandle(handle, &wst)) {
dprintf(1, "handle = %ld\n", handle);
st->st_mode =
(S_IRUSR | S_IXUSR |
(!(wst.dwFileAttributes & kNtFileAttributeReadonly) ? S_IWUSR
: 0) |
((wst.dwFileAttributes & kNtFileAttributeNormal) ? S_IFREG : 0) |
((wst.dwFileAttributes & kNtFileFlagOpenReparsePoint) ? S_IFLNK
: 0) |
((wst.dwFileAttributes & kNtFileAttributeDirectory) ? S_IFDIR
: 0));
st->st_atim = FileTimeToTimeSpec(wst.ftLastAccessFileTime);
st->st_mtim = FileTimeToTimeSpec(wst.ftLastWriteFileTime);
st->st_ctim = FileTimeToTimeSpec(wst.ftCreationFileTime);
st->st_size = (uint64_t)wst.nFileSizeHigh << 32 | wst.nFileSizeLow;
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;
} else {
return __winerr();

View file

@ -32,7 +32,11 @@ int fstat(int fd, struct stat *st) {
return weaken(__zipos_fstat)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st);
} else if (!IsWindows()) {
return fstat$sysv(fd, st);
if (!IsMetal()) {
return fstat$sysv(fd, st);
} else {
return fstat$metal(fd, st);
}
} else {
if (!__isfdkind(fd, kFdFile)) return ebadf();
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 __mkntpath2(const char *, char16_t[hasatleast PATH_MAX - 16], int) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » metal
*/
int fstat$metal(int, struct stat *);
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » drivers
*/

View file

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

View file

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

View file

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

View file

@ -141,32 +141,36 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
return efault();
}
if (!IsWindows()) {
if (act) {
memcpy(&copy, act, sizeof(copy));
ap = &copy;
if (IsXnu()) {
ap->sa_restorer = (void *)&xnutrampoline;
ap->sa_handler = (void *)&xnutrampoline;
} else {
if (IsLinux()) {
if (!(ap->sa_flags & SA_RESTORER)) {
ap->sa_flags |= SA_RESTORER;
ap->sa_restorer = &__restore_rt;
if (!IsMetal()) {
if (act) {
memcpy(&copy, act, sizeof(copy));
ap = &copy;
if (IsXnu()) {
ap->sa_restorer = (void *)&xnutrampoline;
ap->sa_handler = (void *)&xnutrampoline;
} else {
if (IsLinux()) {
if (!(ap->sa_flags & SA_RESTORER)) {
ap->sa_flags |= SA_RESTORER;
ap->sa_restorer = &__restore_rt;
}
}
if (rva >= kSigactionMinRva) {
ap->sa_sigaction = (sigaction_f)__sigenter;
}
}
if (rva >= kSigactionMinRva) {
ap->sa_sigaction = (sigaction_f)__sigenter;
}
sigaction$cosmo2native((union metasigaction *)ap);
} 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 {
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 {
if (oldact) {
memset(oldact, 0, sizeof(*oldact));

View file

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

View file

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