Add UEFI support

This is mutually exclusive with Windows support. Documentation for how
to use it has been written in libc/runtime/efimain.c
This commit is contained in:
Justine Tunney 2021-02-21 16:26:36 -08:00 committed by Justine Tunney
parent c6c9b5dfde
commit 537c21338b
24 changed files with 1381 additions and 391 deletions

View file

@ -105,7 +105,7 @@ rlstr: .endobj rlstr,globl,hidden # ←for gdb readibility
αcτµαlly pδrταblε εxεcµταblε § the old technology
*/
#if SupportsWindows() || SupportsXnu()
#if SupportsWindows() || SupportsUefi() || SupportsXnu()
// MZ Literally Executable Header
//
@ -131,14 +131,14 @@ ape_mz: .asciz "MZqFpD='\n" # Mark 'Zibo' Joseph Zbikowski
.ascii "JT" # MZ: OEM identifier
.short 0 # MZ: OEM information
.org 0x40-4 # MZ: bytes reserved for you
#if SupportsWindows()
#if SupportsWindows() || SupportsUefi()
.long RVA(ape_pe) # PE: the new technology
#else
.long 0
#endif
.endfn ape_mz,globl,hidden
#else /* !(SupportsWindows() || SupportsXnu()) */
#else /* !(SupportsWindows() || SupportsUefi() || SupportsXnu()) */
// ELF Literally Executable Header
//
@ -168,7 +168,7 @@ ape_mz: .asciz "MZqFpD='\n" # Mark 'Zibo' Joseph Zbikowski
.short ape_elf_shnum # 3c: e_shnum
.short ape_elf_shstrndx # 3e: e_shstrndx
#endif /* SupportsWindows() || SupportsXnu() */
#endif /* SupportsWindows() || SupportsUefi() || SupportsXnu() */
#if SupportsMetal()
@ -471,7 +471,7 @@ ape_disk:
#if SupportsSystemv()
#if SupportsWindows() || SupportsXnu()
#if SupportsWindows() || SupportsUefi() || SupportsXnu()
apesh: .ascii "'\n#'\"\n" # sixth edition shebang
.ascii "o=\"$(command -v \"$0\")\"\n"
#if SupportsXnu()
@ -526,7 +526,7 @@ apesh: .ascii "'\n#'\"\n" # sixth edition shebang
.ascii "fi\n"
.ascii "exit $R\n"
.endobj apesh
#endif /* SupportsWindows() || SupportsXnu() */
#endif /* SupportsWindows() || SupportsUefi() || SupportsXnu() */
.section .elf.phdrs,"a",@progbits
.long PT_LOAD # text segment
@ -812,7 +812,7 @@ PETEXT = 0b01110000000000000000000001100000
PEDATA = 0b11000000000000000000000011000000
PEIMPS = 0b11000000000000000000000001000000
#if SupportsWindows()
#if SupportsWindows() || SupportsUefi()
.section .pe.header,"a",@progbits
.align __SIZEOF_POINTER__
@ -830,23 +830,27 @@ ape_pe: .ascin "PE",4
.long 0 # SizeOfCode
.long 0 # SizeOfInitializedData
.long 0 # SizeOfUninitializedData
.long RVA(WinMain) # EntryPoint
.long RVA(ape_pe_entry) # EntryPoint
.long 0 # BaseOfCode
.quad IMAGE_BASE_VIRTUAL # ImageBase
.long 4096 # SectionAlignment
.long 4096 # FileAlignment
.short 6 # MajorOperatingSystemVersion
.short v_ntversion # MajorOperatingSystemVersion
.short 0 # MinorOperatingSystemVersion
.short 0 # MajorImageVersion
.short 0 # MinorImageVersion
.short 6 # MajorSubsystemVersion
.short v_ntsubversion # MajorSubsystemVersion
.short 0 # MinorSubsystemVersion
.long 0 # Win32VersionValue
.long RVA(_end) # SizeOfImage
.long RVA(_ehead) # SizeOfHeaders
.long 0 # Checksum
.short v_ntsubsystem # Subsystem: 0=Neutral,2=GUI,3=Console
#if SupportsWindows()
.short DLLEXE # DllCharacteristics
#else
.short 0 # DllCharacteristics
#endif
.quad 0x0000000000100000 # StackReserve
.quad 0x00000000000fc000 # StackCommit
.quad 0 # HeapReserve
@ -854,8 +858,12 @@ ape_pe: .ascin "PE",4
.long 0 # LoaderFlags
.long 16 # NumberOfDirectoryEntries
.long 0,0 # ExportsDirectory
#if SupportsWindows()
.long RVA(ape_idata_idt) # ImportsDirectory
.stub ape_idata_idtsize,long # ImportsDirectorySize
#else
.long 0,0 # ImportsDirectory
#endif
.long 0,0 # ResourcesDirectory
.long 0,0 # ExceptionsDirectory
.long 0,0 # SecurityDirectory
@ -866,8 +874,12 @@ ape_pe: .ascin "PE",4
.long 0,0 # ThreadLocalStorage
.long 0,0 # LoadConfigurationDirectory
.long 0,0 # BoundImportDirectory
#if SupportsWindows()
.long RVA(ape_idata_iat) # ImportAddressDirectory
.stub ape_idata_iatsize,long # ImportAddressDirectorySize
#else
.long 0,0 # ImportAddressDirectory
#endif
.long 0,0 # DelayImportDescriptor
.long 0,0 # ComPlusRuntimeHeader
.long 0,0 # Reserved
@ -900,7 +912,7 @@ ape_pe: .ascin "PE",4
.long PEDATA # Flags
.previous
#endif /* SupportsWindows() */
#endif /* SupportsWindows() || SupportsUefi() */
.section .idata.ro.idt.1,"a",@progbits
.type ape_idata_idtend,@object
@ -1524,22 +1536,21 @@ metal.thunk:
#endif
// 𝑠𝑙𝑖𝑑𝑒
.endfn metal.thunk
metal:
xor %eax,%eax # clear bss
metal: xor %eax,%eax # clear bss
mov $ape_bss_vaddr,%edi
mov $ape_bss_memsz,%ecx
rep stosb
.weak __hostos
0: .weak __hostos
ezlea __hostos,ax
test %rax,%rax
jz 1f
movb $METAL,(%rax)
1: push $0 # auxv
push $0
push $0 # envp
push $0 # auxv
1: push $0 # auxv[0][1]
push $0 # auxv[0][0]
push $0 # envp[0]
push $0 # argv[0]
push $0 # argc
xor %edi,%edi
xor %edi,%edi # not freebsd
jmp _start
.endfn metal

View file

@ -229,7 +229,7 @@ SECTIONS {
. += 1;
KEEP(*(.ape.pad.head))
. = ALIGN(SupportsWindows() ? PAGESIZE : 16);
. = ALIGN(SupportsWindows() || SupportsUefi() ? PAGESIZE : 16);
HIDDEN(_ehead = .);
} :Head
@ -500,15 +500,21 @@ SHSTUB2(ape_macho_dd_skip, RVA(ape_macho) / 8);
SHSTUB2(ape_macho_dd_count, (ape_macho_end - ape_macho) / 8);
#endif
#if SupportsWindows()
#if SupportsWindows() || SupportsUefi()
#define LINK_WINDOWS (SupportsWindows() && !DEFINED(EfiMain))
PFSTUB4(ape_pe_offset, ape_pe - ape_mz);
HIDDEN(ape_pe_optsz = ape_pe_sections - (ape_pe + 24));
HIDDEN(ape_pe_shnum = (ape_pe_sections_end - ape_pe_sections) / 40);
HIDDEN(ape_idata_idtsize = ape_idata_idtend - ape_idata_idt);
HIDDEN(ape_idata_iatsize = ape_idata_iatend - ape_idata_iat);
HIDDEN(v_ntsubsystem = (DEFINED(GetMessage)
HIDDEN(v_ntversion = LINK_WINDOWS ? 6 : 1);
HIDDEN(v_ntsubversion = LINK_WINDOWS ? 6 : 5);
HIDDEN(v_ntsubsystem = (LINK_WINDOWS
? (DEFINED(GetMessage)
? kNtImageSubsystemWindowsGui
: kNtImageSubsystemWindowsCui));
: kNtImageSubsystemWindowsCui)
: kNtImageSubsystemEfiApplication));
HIDDEN(ape_pe_entry = LINK_WINDOWS ? WinMain : EfiMain);
#endif
#if SupportsMetal()
@ -603,7 +609,7 @@ CHURN(v_ape_realsectors);
#if SupportsXnu()
CHURN(ape_macho);
#endif
#if SupportsWindows()
#if SupportsWindows() || SupportsUefi()
CHURN(ape_mz);
CHURN(ape_pe);
CHURN(ape_pe_offset);

View file

@ -95,7 +95,7 @@ CONFIG_COPTS += \
$(SANITIZER)
TARGET_ARCH ?= \
-msse3
-march=native
OVERRIDE_CCFLAGS += \
-fno-pie

View file

@ -91,7 +91,6 @@ FTRACE = \
-pg
SANITIZER = \
-fsanitize=leak \
-fsanitize=address
NO_MAGIC = \

View file

@ -1,7 +1,7 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- 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
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
@ -16,30 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/runtime/internal.h"
#include "libc/macros.h"
.privileged
#include "libc/calls/efi.h"
#include "libc/nt/efi.h"
// Terminates process, ignoring destructors and atexit() handlers.
//
// @param edi is exit code ∈ [0,256)
// @asyncsignalsafe
// @vforksafe
// @noreturn
_Exit: push %rbp
mov %rsp,%rbp
#if SupportsWindows()
testb IsWindows()
jz 1f
sub $32,%rsp
movzbl %dil,%ecx # %ERRORLEVEL% is limitless
call *__imp_ExitProcess(%rip)
#endif
1: mov __NR_exit_group(%rip),%eax
syscall
#if SupportsMetal()
call triplf
#endif
.endfn _Exit,globl,protected
.hidden __NR_exit_group
EFI_HANDLE __efi_image_handle;
EFI_SYSTEM_TABLE *__efi_system_table;

12
libc/calls/efi.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_EFI_H_
#define COSMOPOLITAN_LIBC_CALLS_EFI_H_
#include "libc/nt/efi.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern EFI_HANDLE __efi_image_handle;
extern EFI_SYSTEM_TABLE *__efi_system_table;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_EFI_H_ */

View file

@ -31,9 +31,9 @@ int fstat(int fd, struct stat *st) {
if (__isfdkind(fd, kFdZip)) {
return weaken(__zipos_fstat)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, st);
} else if (!IsWindows() && !IsMetal()) {
} else if (!IsWindows() && !IsMetal() && !IsUefi()) {
return sys_fstat(fd, st);
} else if (IsMetal()) {
} else if (IsMetal() || IsUefi()) {
return fstat_metal(fd, st);
} else {
if (!__isfdkind(fd, kFdFile)) return ebadf();

View file

@ -30,7 +30,7 @@ hidden textstartup void InitializeFileDescriptors(void) {
fds = VEIL("r", &g_fds);
pushmov(&fds->n, ARRAYLEN(fds->__init_p));
fds->p = fds->__init_p;
if (IsMetal()) {
if (IsMetal() || IsUefi()) {
pushmov(&fds->f, 3ull);
fds->__init_p[STDIN_FILENO].kind = pushpop(kFdSerial);
fds->__init_p[STDOUT_FILENO].kind = pushpop(kFdSerial);

View file

@ -199,6 +199,7 @@ u32 sys_gettid(void) hidden;
u32 sys_getuid(void) hidden;
void *sys_mmap(void *, u64, u32, u32, i64, i64, i64) hidden;
void *sys_mremap(void *, u64, u64, i32, void *) hidden;
void sys_exit(int) hidden;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » system five » support
@ -294,6 +295,12 @@ unsigned __wincrash_nt(struct NtExceptionPointers *);
int fstat_metal(int, struct stat *);
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » uefi
*/
int sys_nanosleep_uefi(const struct timespec *, struct timespec *);
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § syscalls » drivers
*/

View file

@ -0,0 +1,35 @@
/*-*- 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/calls.h"
#include "libc/calls/efi.h"
#include "libc/nt/efi.h"
#include "libc/sysv/errfuns.h"
int sys_nanosleep_uefi(const struct timespec *req, struct timespec *rem) {
if (__efi_system_table->BootServices->Stall(
req->tv_sec * 1000000 + req->tv_nsec / 1000) == EFI_SUCCESS) {
if (rem) {
rem->tv_sec = 0;
rem->tv_nsec = 0;
}
return 0;
} else {
return eintr();
}
}

View file

@ -26,10 +26,15 @@
*/
int nanosleep(const struct timespec *req, struct timespec *rem) {
if (!req) return efault();
if (!IsWindows() && !IsMetal() && !IsXnu()) {
if (req->tv_sec < 0 || !(0 <= req->tv_nsec && req->tv_nsec <= 999999999)) {
return einval();
}
if (!IsWindows() && !IsMetal() && !IsUefi() && !IsXnu()) {
return sys_nanosleep(req, rem);
} else if (IsXnu()) {
return sys_nanosleep_xnu(req, rem);
} else if (IsUefi()) {
return sys_nanosleep_uefi(req, rem);
} else if (IsMetal()) {
return enosys(); /* TODO: Sleep on Metal */
} else {

View file

@ -36,7 +36,8 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
return weaken(__zipos_read)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, -1);
} else if (SupportsMetal() && fd < g_fds.n && g_fds.p[fd].kind == kFdSerial) {
} else if ((SupportsMetal() || SupportsUefi()) && fd < g_fds.n &&
g_fds.p[fd].kind == kFdSerial) {
return readv_serial(&g_fds.p[fd], iov, iovlen);
} else if (!IsWindows()) {
return sys_readv(fd, iov, iovlen);

View file

@ -144,7 +144,7 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) {
sizeof(struct sigaction) > sizeof(struct sigaction_freebsd) &&
sizeof(struct sigaction) > sizeof(struct sigaction_openbsd) &&
sizeof(struct sigaction) > sizeof(struct sigaction_netbsd));
if (IsMetal()) return enosys(); /* TODO: Signals on Metal */
if (IsMetal() || IsUefi()) return enosys(); /* TODO: Signals on Metal */
if (!(0 < sig && sig < NSIG)) return einval();
if (sig == SIGKILL || sig == SIGSTOP) return einval();
if (!act) {

View file

@ -39,7 +39,8 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
return weaken(__zipos_write)(
(struct ZiposHandle *)(intptr_t)g_fds.p[fd].handle, iov, iovlen, -1);
} else if (SupportsMetal() && fd < g_fds.n && g_fds.p[fd].kind == kFdSerial) {
} else if ((SupportsMetal() || SupportsUefi()) && fd < g_fds.n &&
g_fds.p[fd].kind == kFdSerial) {
return writev_serial(&g_fds.p[fd], iov, iovlen);
} else if (!IsWindows()) {
return sys_writev(fd, iov, iovlen);

View file

@ -20,6 +20,7 @@
#define OPENBSD 16
#define FREEBSD 32
#define NETBSD 64
#define UEFI 128
#ifdef NDEBUG
#define NoDebug() 1
@ -72,6 +73,7 @@
#define SupportsLinux() ((SUPPORT_VECTOR & LINUX) == LINUX)
#define SupportsMetal() ((SUPPORT_VECTOR & METAL) == METAL)
#define SupportsWindows() ((SUPPORT_VECTOR & WINDOWS) == WINDOWS)
#define SupportsUefi() ((SUPPORT_VECTOR & UEFI) == UEFI)
#define SupportsXnu() ((SUPPORT_VECTOR & XNU) == XNU)
#define SupportsFreebsd() ((SUPPORT_VECTOR & FREEBSD) == FREEBSD)
#define SupportsOpenbsd() ((SUPPORT_VECTOR & OPENBSD) == OPENBSD)
@ -84,6 +86,7 @@
#define IsLinux() (SupportsLinux() && (__hostos & LINUX))
#define IsMetal() (SupportsMetal() && (__hostos & METAL))
#define IsWindows() (SupportsWindows() && (__hostos & WINDOWS))
#define IsUefi() (SupportsUefi() && (__hostos & UEFI))
#define IsXnu() (SupportsXnu() && (__hostos & XNU))
#define IsFreebsd() (SupportsFreebsd() && (__hostos & FREEBSD))
#define IsOpenbsd() (SupportsOpenbsd() && (__hostos & OPENBSD))
@ -94,6 +97,7 @@
#define IsLinux() $LINUX,__hostos(%rip)
#define IsMetal() $METAL,__hostos(%rip)
#define IsWindows() $WINDOWS,__hostos(%rip)
#define IsUefi() $UEFI,__hostos(%rip)
#define IsBsd() $XNU|FREEBSD|OPENBSD|NETBSD,__hostos(%rip)
#define IsXnu() $XNU,__hostos(%rip)
#define IsFreebsd() $FREEBSD,__hostos(%rip)

View file

@ -59,7 +59,7 @@
#define _KCPUIDS_VENDOR() \
(((kCpuids[KCPUIDS_0H][KCPUIDS_EBX] >> 000) & 0xff) ^ \
((kCpuids[KCPUIDS_0H][KCPUIDS_EDX] >> 010) & 0xff))
((kCpuids[KCPUIDS_0H][KCPUIDS_ECX] >> 010) & 0xff))
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_NEXGEN32E_VENDOR_H_ */

424
libc/nt/efi.h Normal file
View file

@ -0,0 +1,424 @@
#ifndef COSMOPOLITAN_LIBC_NT_EFI_H_
#define COSMOPOLITAN_LIBC_NT_EFI_H_
/*
αcτµαlly pδrταblε εxεcµταblε § the unified extensible firmware interface
*/
#define EFI_SUCCESS 0x8000000000000000
#define EFI_LOAD_ERROR 0x8000000000000001
#define EFI_INVALID_PARAMETER 0x8000000000000002
#define EFI_UNSUPPORTED 0x8000000000000003
#define EFI_BAD_BUFFER_SIZE 0x8000000000000004
#define EFI_BUFFER_TOO_SMALL 0x8000000000000005
#define EFI_NOT_READY 0x8000000000000006
#define EFI_DEVICE_ERROR 0x8000000000000007
#define EFI_WRITE_PROTECTED 0x8000000000000008
#define EFI_OUT_OF_RESOURCES 0x8000000000000009
#define EFI_VOLUME_CORRUPTED 0x800000000000000a
#define EFI_VOLUME_FULL 0x800000000000000b
#define EFI_NO_MEDIA 0x800000000000000c
#define EFI_MEDIA_CHANGED 0x800000000000000d
#define EFI_NOT_FOUND 0x800000000000000e
#define EFI_ACCESS_DENIED 0x800000000000000f
#define EFI_NO_RESPONSE 0x8000000000000010
#define EFI_NO_MAPPING 0x8000000000000011
#define EFI_TIMEOUT 0x8000000000000012
#define EFI_NOT_STARTED 0x8000000000000013
#define EFI_ALREADY_STARTED 0x8000000000000014
#define EFI_ABORTED 0x8000000000000015
#define EFI_ICMP_ERROR 0x8000000000000016
#define EFI_TFTP_ERROR 0x8000000000000017
#define EFI_PROTOCOL_ERROR 0x8000000000000018
#define EFI_VARIABLE_NON_VOLATILE 0x00000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008
#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010
#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020
#define EFI_VARIABLE_APPEND_WRITE 0x00000040
#define EFI_MEMORY_UC 0x0000000000000001U
#define EFI_MEMORY_WC 0x0000000000000002U
#define EFI_MEMORY_WT 0x0000000000000004U
#define EFI_MEMORY_WB 0x0000000000000008U
#define EFI_MEMORY_UCE 0x0000000000000010U
#define EFI_MEMORY_WP 0x0000000000001000U
#define EFI_MEMORY_RP 0x0000000000002000U
#define EFI_MEMORY_XP 0x0000000000004000U
#define EFI_MEMORY_RO 0x0000000000020000U
#define EFI_MEMORY_NV 0x0000000000008000U
#define EFI_MEMORY_MORE_RELIABLE 0x0000000000010000U
#define EFI_MEMORY_RUNTIME 0x8000000000000000U
#define EFI_OPTIONAL_PTR 0x00000001
#define EFI_SCAN_NULL 0x0000
#define EFI_SCAN_UP 0x0001
#define EFI_SCAN_DOWN 0x0002
#define EFI_SCAN_RIGHT 0x0003
#define EFI_SCAN_LEFT 0x0004
#define EFI_SCAN_HOME 0x0005
#define EFI_SCAN_END 0x0006
#define EFI_SCAN_INSERT 0x0007
#define EFI_SCAN_DELETE 0x0008
#define EFI_SCAN_PAGE_UP 0x0009
#define EFI_SCAN_PAGE_DOWN 0x000A
#define EFI_SCAN_F1 0x000B
#define EFI_SCAN_F2 0x000C
#define EFI_SCAN_F3 0x000D
#define EFI_SCAN_F4 0x000E
#define EFI_SCAN_F5 0x000F
#define EFI_SCAN_F6 0x0010
#define EFI_SCAN_F7 0x0011
#define EFI_SCAN_F8 0x0012
#define EFI_SCAN_F9 0x0013
#define EFI_SCAN_F10 0x0014
#define EFI_SCAN_ESC 0x0017
#define EFI_EVT_TIMER 0x80000000
#define EFI_EVT_RUNTIME 0x40000000
#define EFI_EVT_NOTIFY_WAIT 0x00000100
#define EFI_EVT_NOTIFY_SIGNAL 0x00000200
#define EFI_EVT_SIGNAL_EXIT_BOOT_SERVICES 0x00000201
#define EFI_EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE 0x60000202
#define EFI_EVT_RUNTIME_CONTEXT 0x20000000
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define EFIAPI __attribute__((__ms_abi__))
#define EFI_STATUS uint64_t
#define EFI_EVENT uintptr_t
#define EFI_HANDLE uintptr_t
typedef struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL EFI_SIMPLE_TEXT_INPUT_PROTOCOL;
typedef struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL;
typedef enum {
EfiReservedMemoryType,
EfiLoaderCode,
EfiLoaderData,
EfiBootServicesCode,
EfiBootServicesData,
EfiRuntimeServicesCode,
EfiRuntimeServicesData,
EfiConventionalMemory,
EfiUnusableMemory,
EfiACPIReclaimMemory,
EfiACPIMemoryNVS,
EfiMemoryMappedIO,
EfiMemoryMappedIOPortSpace,
EfiPalCode,
EfiPersistentMemory,
EfiMaxMemoryType
} EFI_MEMORY_TYPE;
typedef enum {
EfiResetCold,
EfiResetWarm,
EfiResetShutdown,
EfiResetPlatformSpecific
} EFI_RESET_TYPE;
typedef enum {
AllocateAnyPages,
AllocateMaxAddress,
AllocateAddress,
MaxAllocateType
} EFI_ALLOCATE_TYPE;
typedef enum {
TimerCancel,
TimerPeriodic,
TimerRelative,
} EFI_TIMER_DELAY;
typedef struct {
uint32_t Resolution;
uint32_t Accuracy;
bool SetsToZero;
} EFI_TIME_CAPABILITIES;
typedef struct {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} EFI_GUID;
typedef struct {
uint16_t Year;
uint8_t Month;
uint8_t Day;
uint8_t Hour;
uint8_t Minute;
uint8_t Second;
uint8_t Pad1;
uint32_t Nanosecond;
int16_t TimeZone;
uint8_t Daylight;
uint8_t Pad2;
} EFI_TIME;
typedef struct {
uint32_t Type;
uint64_t PhysicalStart;
uint64_t VirtualStart;
uint64_t NumberOfPages;
uint64_t Attribute;
} EFI_MEMORY_DESCRIPTOR;
typedef struct {
EFI_GUID VendorGuid;
void *VendorTable;
} EFI_CONFIGURATION_TABLE;
typedef struct {
EFI_GUID CapsuleGuid;
uint32_t HeaderSize;
uint32_t Flags;
uint32_t CapsuleImageSize;
} EFI_CAPSULE_HEADER;
typedef struct {
uint16_t ScanCode;
char16_t UnicodeChar;
} EFI_INPUT_KEY;
typedef struct {
int32_t MaxMode;
int32_t Mode;
int32_t Attribute;
int32_t CursorColumn;
int32_t CursorRow;
bool CursorVisible;
} EFI_SIMPLE_TEXT_OUTPUT_MODE;
typedef struct {
uint64_t Signature;
uint32_t Revision;
uint32_t HeaderSize;
uint32_t CRC32;
uint32_t Reserved;
} EFI_TABLE_HEADER;
typedef EFI_STATUS(EFIAPI *EFI_EXIT)(EFI_HANDLE ImageHandle,
EFI_STATUS ExitStatus,
uintptr_t ExitDataSize,
char16_t *opt_ExitData);
typedef EFI_STATUS(EFIAPI *EFI_GET_VARIABLE)(char16_t *VariableName,
EFI_GUID *VendorGuid,
uint32_t *outopt_Attributes,
uintptr_t *inout_DataSize,
void *outopt_Data);
typedef EFI_STATUS(EFIAPI *EFI_SET_VARIABLE)(char16_t *VariableName,
EFI_GUID *VendorGuid,
uint32_t Attributes,
uintptr_t DataSize, void *Data);
typedef EFI_STATUS(EFIAPI *EFI_GET_NEXT_VARIABLE_NAME)(
uintptr_t *inout_VariableNameSize, char16_t *inout_VariableName,
EFI_GUID *inout_VendorGuid);
typedef EFI_STATUS(EFIAPI *EFI_QUERY_VARIABLE_INFO)(
uint32_t Attributes, uint64_t *out_MaximumVariableStorageSize,
uint64_t *out_RemainingVariableStorageSize,
uint64_t *out_MaximumVariableSize);
typedef EFI_STATUS(EFIAPI *EFI_ALLOCATE_PAGES)(EFI_ALLOCATE_TYPE Type,
EFI_MEMORY_TYPE MemoryType,
uintptr_t Pages,
uint64_t *inout_Memory);
typedef EFI_STATUS(EFIAPI *EFI_FREE_PAGES)(uint64_t Memory, uintptr_t Pages);
typedef EFI_STATUS(EFIAPI *EFI_GET_MEMORY_MAP)(
uintptr_t *inout_MemoryMapSize, EFI_MEMORY_DESCRIPTOR *inout_MemoryMap,
uintptr_t *out_MapKey, uintptr_t *out_DescriptorSize,
uint32_t *out_DescriptorVersion);
typedef EFI_STATUS(EFIAPI *EFI_CHECK_EVENT)(EFI_EVENT Event);
typedef EFI_STATUS(EFIAPI *EFI_CLOSE_EVENT)(EFI_EVENT Event);
typedef EFI_STATUS(EFIAPI *EFI_SIGNAL_EVENT)(EFI_EVENT Event);
typedef EFI_STATUS(EFIAPI *EFI_WAIT_FOR_EVENT)(uintptr_t NumberOfEvents,
EFI_EVENT *Events,
uintptr_t *out_Index);
typedef EFI_STATUS(EFIAPI *EFI_SET_TIMER)(EFI_EVENT Event, EFI_TIMER_DELAY Type,
uint64_t TriggerTime);
typedef void(EFIAPI *EFI_EVENT_NOTIFY)(EFI_EVENT Event, void *Context);
typedef EFI_STATUS(EFIAPI *EFI_CREATE_EVENT)(uint32_t Type, uintptr_t NotifyTpl,
EFI_EVENT_NOTIFY NotifyFunction,
void *NotifyContext,
EFI_EVENT *out_Event);
typedef EFI_STATUS(EFIAPI *EFI_CREATE_EVENT_EX)(
uint32_t Type, uintptr_t NotifyTpl, EFI_EVENT_NOTIFY opt_NotifyFunction,
const void *opt_NotifyContext, const EFI_GUID *opt_EventGroup,
EFI_EVENT *out_Event);
typedef EFI_STATUS(EFIAPI *EFI_UPDATE_CAPSULE)(
EFI_CAPSULE_HEADER **CapsuleHeaderArray, uintptr_t CapsuleCount,
uint64_t opt_ScatterGatherList);
typedef EFI_STATUS(EFIAPI *EFI_QUERY_CAPSULE_CAPABILITIES)(
EFI_CAPSULE_HEADER **CapsuleHeaderArray, uintptr_t CapsuleCount,
uint64_t *out_MaximumCapsuleSize, EFI_RESET_TYPE *out_ResetType);
typedef EFI_STATUS(EFIAPI *EFI_GET_WAKEUP_TIME)(bool *out_Enabled,
bool *out_Pending,
EFI_TIME *out_Time);
typedef EFI_STATUS(EFIAPI *EFI_SET_WAKEUP_TIME)(bool Enable,
EFI_TIME *opt_Time);
typedef EFI_STATUS(EFIAPI *EFI_SET_WATCHDOG_TIMER)(uintptr_t Timeout,
uint64_t WatchdogCode,
uintptr_t DataSize,
char16_t *opt_WatchdogData);
typedef EFI_STATUS(EFIAPI *EFI_SET_TIME)(EFI_TIME *Time);
typedef EFI_STATUS(EFIAPI *EFI_GET_TIME)(
EFI_TIME *out_Time, EFI_TIME_CAPABILITIES *outopt_Capabilities);
typedef EFI_STATUS(EFIAPI *EFI_GET_NEXT_HIGH_MONO_COUNT)(
uint32_t *out_HighCount);
typedef EFI_STATUS(EFIAPI *EFI_STALL)(uintptr_t Microseconds);
typedef EFI_STATUS(EFIAPI *EFI_GET_NEXT_MONOTONIC_COUNT)(uint64_t *out_Count);
typedef EFI_STATUS(EFIAPI *EFI_SET_VIRTUAL_ADDRESS_MAP)(
uintptr_t MemoryMapSize, uintptr_t DescriptorSize,
uint32_t DescriptorVersion, EFI_MEMORY_DESCRIPTOR *VirtualMap);
typedef void(EFIAPI *EFI_RESET_SYSTEM)(EFI_RESET_TYPE ResetType,
EFI_STATUS ResetStatus,
uintptr_t DataSize, void *opt_ResetData);
typedef EFI_STATUS(EFIAPI *EFI_CONVERT_POINTER)(uintptr_t DebugDisposition,
void **inout_Address);
typedef EFI_STATUS(EFIAPI *EFI_INPUT_RESET)(
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, bool ExtendedVerification);
typedef EFI_STATUS(EFIAPI *EFI_INPUT_READ_KEY)(
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This, EFI_INPUT_KEY *out_Key);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_RESET)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, bool ExtendedVerification);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_STRING)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, char16_t *String);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_TEST_STRING)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, char16_t *String);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_QUERY_MODE)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, uint64_t ModeNumber,
uint64_t *out_Columns, uint64_t *out_Rows);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_SET_MODE)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, uint64_t ModeNumber);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_SET_ATTRIBUTE)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, uint64_t Attribute);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_CLEAR_SCREEN)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_SET_CURSOR_POSITION)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, uint64_t Column, uint64_t Row);
typedef EFI_STATUS(EFIAPI *EFI_TEXT_ENABLE_CURSOR)(
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This, bool Visible);
typedef struct {
EFI_TABLE_HEADER Hdr;
EFI_GET_TIME GetTime;
EFI_SET_TIME SetTime;
EFI_GET_WAKEUP_TIME GetWakeupTime;
EFI_SET_WAKEUP_TIME SetWakeupTime;
EFI_SET_VIRTUAL_ADDRESS_MAP SetVirtualAddressMap;
EFI_CONVERT_POINTER ConvertPointer;
EFI_GET_VARIABLE GetVariable;
EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName;
EFI_SET_VARIABLE SetVariable;
EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount;
EFI_RESET_SYSTEM ResetSystem;
EFI_UPDATE_CAPSULE UpdateCapsule;
EFI_QUERY_CAPSULE_CAPABILITIES QueryCapsuleCapabilities;
EFI_QUERY_VARIABLE_INFO QueryVariableInfo;
} EFI_RUNTIME_SERVICES;
typedef struct {
EFI_TABLE_HEADER Hdr;
void *RaiseTPL;
void *RestoreTPL;
EFI_ALLOCATE_PAGES AllocatePages;
EFI_FREE_PAGES FreePages;
EFI_GET_MEMORY_MAP GetMemoryMap;
void *AllocatePool;
void *FreePool;
EFI_CREATE_EVENT CreateEvent;
EFI_SET_TIMER SetTimer;
EFI_WAIT_FOR_EVENT WaitForEvent;
EFI_SIGNAL_EVENT SignalEvent;
EFI_CLOSE_EVENT CloseEvent;
EFI_CHECK_EVENT CheckEvent;
void *InstallProtocolInterface;
void *ReinstallProtocolInterface;
void *UninstallProtocolInterface;
void *HandleProtocol;
void *Reserved;
void *RegisterProtocolNotify;
void *LocateHandle;
void *LocateDevicePath;
void *InstallConfigurationTable;
void *LoadImage;
void *StartImage;
EFI_EXIT Exit;
void *UnloadImage;
void *ExitBootServices;
EFI_GET_NEXT_MONOTONIC_COUNT GetNextMonotonicCount;
EFI_STALL Stall;
EFI_SET_WATCHDOG_TIMER SetWatchdogTimer;
void *ConnectController;
void *DisconnectController;
void *OpenProtocol;
void *CloseProtocol;
void *OpenProtocolInformation;
void *ProtocolsPerHandle;
void *LocateHandleBuffer;
void *LocateProtocol;
void *InstallMultipleProtocolInterfaces;
void *UninstallMultipleProtocolInterfaces;
void *CalculateCrc32;
void *CopyMem;
void *SetMem;
EFI_CREATE_EVENT_EX CreateEventEx;
} EFI_BOOT_SERVICES;
typedef struct {
EFI_TABLE_HEADER Hdr;
char16_t *FirmwareVendor;
uint32_t FirmwareRevision;
EFI_HANDLE ConsoleInHandle;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
EFI_HANDLE ConsoleOutHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut;
EFI_HANDLE StandardErrorHandle;
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;
EFI_RUNTIME_SERVICES *RuntimeServices;
EFI_BOOT_SERVICES *BootServices;
uintptr_t NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigurationTable;
} EFI_SYSTEM_TABLE;
struct _EFI_SIMPLE_TEXT_INPUT_PROTOCOL {
EFI_INPUT_RESET Reset;
EFI_INPUT_READ_KEY ReadKeyStroke;
EFI_EVENT WaitForKey;
};
struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
EFI_TEXT_RESET Reset;
EFI_TEXT_STRING OutputString;
EFI_TEXT_TEST_STRING TestString;
EFI_TEXT_QUERY_MODE QueryMode;
EFI_TEXT_SET_MODE SetMode;
EFI_TEXT_SET_ATTRIBUTE SetAttribute;
EFI_TEXT_CLEAR_SCREEN ClearScreen;
EFI_TEXT_SET_CURSOR_POSITION SetCursorPosition;
EFI_TEXT_ENABLE_CURSOR EnableCursor;
EFI_SIMPLE_TEXT_OUTPUT_MODE *Mode;
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_NT_EFI_H_ */

View file

@ -119,7 +119,7 @@
#define kNtImageSubsystemPosixCui 7
#define kNtImageSubsystemNativeWindows 8
#define kNtImageSubsystemWindowsCeGui 9
#define kNtImageSubsystemEfiApplication 1
#define kNtImageSubsystemEfiApplication 10
#define kNtImageSubsystemEfiBootServiceDriver 11
#define kNtImageSubsystemEfiRuntimeDriver 12
#define kNtImageSubsystemEfiRom 13

72
libc/runtime/efimain.c Normal file
View file

@ -0,0 +1,72 @@
/*-*- 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/efi.h"
#include "libc/dce.h"
#include "libc/nt/efi.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/runtime.h"
/**
* EFI Application Entrypoint.
*
* This entrypoint is mutually exclusive from WinMain since
* Windows apps and EFI apps use the same PE binary format.
* By default, we build binaries to support Windows. If you
* want to your APE executable to boot on UEFI instead then
* you need to run the following build command:
*
* make -j8 CPPFLAGS=-DSUPPORT_VECTOR=251
*
* That'll remove all the Windows code and turn EFI on. You
* can also remove by BIOS code too, by changing 251 to 249
* but it shouldn't matter. Here's how to emulate EFI apps:
*
* qemu-system-x86_64 \
* -bios OVMF.fd \
* -serial stdio \
* -net none \
* -drive format=raw,file=fat:rw:o/tool/viz
* FS0:
* deathstar.com
*
* If you're using the amalgamated release binaries then it
* should be possible to enable UEFI mode by having this at
* the top of your main source file to hint the APE linker:
*
* STATIC_YOINK("EfiMain");
* int main() { ... }
*
* @see libc/dce.h
*/
__msabi noasan EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable) {
extern char os asm("__hostos");
os = UEFI;
__efi_image_handle = ImageHandle;
__efi_system_table = SystemTable;
asm("push\t$0\n\t"
"push\t$0\n\t"
"push\t$0\n\t"
"push\t$0\n\t"
"push\t$0\n\t"
"xor\t%edi,%edi\n\t"
".weak\t_start\n\t"
"jmp\t_start");
unreachable;
}

View file

@ -1,7 +1,7 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
/*-*- 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
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
@ -16,15 +16,16 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.h"
.privileged
.source __FILE__
#include "libc/runtime/runtime.h"
// Terminates process, ignoring destructors and atexit() handlers.
//
// @param edi is exit code ∈ [0,256)
// @asyncsignalsafe
// @vforksafe
// @noreturn
_exit: jmp _Exit
.endfn _exit,globl,protected
/**
* Terminates process, ignoring destructors and atexit() handlers.
*
* @param rc is exit code [0,256)
* @asyncsignalsafe
* @vforksafe
* @noreturn
*/
wontreturn void _exit(int rc) {
_Exit(rc);
}

44
libc/runtime/exit3.c Normal file
View file

@ -0,0 +1,44 @@
/*-*- 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/efi.h"
#include "libc/calls/internal.h"
#include "libc/dce.h"
#include "libc/nexgen32e/vendor.internal.h"
#include "libc/nt/runtime.h"
#include "libc/runtime/runtime.h"
/**
* Terminates process, ignoring destructors and atexit() handlers.
*
* @param rc is exit code [0,256)
* @asyncsignalsafe
* @vforksafe
* @noreturn
*/
wontreturn void _Exit(int rc) {
if ((!IsWindows() && !IsMetal() && !IsUefi()) ||
(IsMetal() && IsGenuineCosmo())) {
sys_exit(rc);
} else if (IsUefi()) {
__efi_system_table->BootServices->Exit(__efi_image_handle, rc, 0, NULL);
} else if (IsWindows()) {
ExitProcess(rc & 0xff);
}
triplf();
}

View file

@ -73,12 +73,6 @@ void *sbrk(intptr_t);
int brk(void *);
int NtGetVersion(void);
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § runtime » optimizations
*/
#define _exit(RC) _Exit(RC)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_RUNTIME_H_ */

View file

@ -104,7 +104,7 @@ __systemfive:
.privileged
.Lanchorpoint:
#if SupportsLinux() || SupportsMetal()
#if SupportsLinux() || SupportsMetal() || SupportsUefi()
systemfive_linux:
and $0xfff,%eax
cmp $0xfff,%eax
@ -194,14 +194,18 @@ systemfive_xnu:
.init.start 300,_init_systemfive
push %rbx
push %rsi
#if SupportsMetal()
testb $METAL,(%rdi) # @see ape/ape.S
jnz _init_systemfive_metal
#endif
#if SupportsXnu()
testb $XNU,(%rdi) # @see libc/crt/crt.S
jnz _init_systemfive_xnu
#endif
#if SupportsUefi()
testb $UEFI,(%rdi) # @see ape/ape.S
jnz _init_systemfive_uefi
#endif
#if SupportsMetal()
testb $METAL,(%rdi) # @see ape/ape.S
jnz _init_systemfive_metal
#endif
#if SupportsFreebsd()
testb $FREEBSD,(%rdi) # @see libc/crt/crt.S
jnz _init_systemfive_freebsd
@ -222,7 +226,7 @@ systemfive_xnu:
lea 2(%eax),%eax
jnz 0b
#endif
#if SupportsLinux() || SupportsMetal()
#if SupportsLinux()
_init_systemfive_linux:
pushb systemfive_linux-.Lanchorpoint
push $LINUX
@ -236,6 +240,13 @@ _init_systemfive_metal:
ezlea syscon_linux,si
jmp _init_systemfive_os
#endif
#if SupportsUefi()
_init_systemfive_uefi:
pushb systemfive_linux-.Lanchorpoint
push $UEFI
ezlea syscon_linux,si
jmp _init_systemfive_os
#endif
#if SupportsWindows()
_init_systemfive_windows:
pushb systemfive_enosys-.Lanchorpoint
@ -312,8 +323,8 @@ _init_systemfive_magnums:
// 𝑠𝑙𝑖𝑑𝑒
#if SupportsSystemv() && !defined(TINY)
_init_systemfive_stack: # determinism ftw!
#if SupportsWindows()
testb IsWindows() # already did this
#if SupportsWindows() || SupportsMetal() || SupportsUefi()
testb $WINDOWS|METAL|UEFI,__hostos(%rip)
jnz _init_systemfive_done
#endif
push %rdi
@ -403,7 +414,7 @@ syscon_end:
.type syscon_end,@object
.globl syscon_start
.globl syscon_end
#if SupportsLinux() || SupportsMetal()
#if SupportsLinux() || SupportsMetal() || SupportsUefi()
.section .sort.rodata.syscon.linux.1,"a",@progbits
.align 1
syscon_linux:/*

385
tool/viz/printansi.c Normal file
View file

@ -0,0 +1,385 @@
/*-*- 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 "dsp/core/half.h"
#include "dsp/core/twixt8.h"
#include "dsp/scale/scale.h"
#include "dsp/tty/quant.h"
#include "dsp/tty/tty.h"
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/calls/calls.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/winsize.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
#include "libc/limits.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/math.h"
#include "libc/mem/mem.h"
#include "libc/rand/rand.h"
#include "libc/runtime/gc.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/madv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/termios.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.h"
#include "third_party/stb/stb_image.h"
#include "tool/viz/lib/graphic.h"
#define SQR(x) ((x) * (x))
static struct Flags {
const char *out;
bool invert;
bool subpixel;
bool unsharp;
bool dither;
bool ruler;
bool trailingnewline;
long half;
bool full;
long width;
long height;
enum TtyBlocksSelection blocks;
enum TtyQuantizationAlgorithm quant;
} g_flags;
static wontreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "Usage: %s%s", program_invocation_name, "\
[FLAGS] [PATH]\n\
\n\
FLAGS\n\
\n\
-w INT width\n\
-h INT height\n\
-i invert\n\
-? shows this information\n\
\n\
EXAMPLES\n\
\n\
printansi.com -w80 -h40 logo.png\n\
\n\
\n");
exit(rc);
}
static int ParseNumberOption(const char *arg) {
long x;
x = strtol(arg, NULL, 0);
if (!(1 <= x && x <= INT_MAX)) {
fprintf(stderr, "invalid flexidecimal: %s\n\n", arg);
exit(EXIT_FAILURE);
}
return x;
}
static void GetOpts(int *argc, char *argv[]) {
int opt;
struct winsize ws;
g_flags.quant = kTtyQuantTrue;
g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode;
if (*argc == 2 &&
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) {
PrintUsage(EXIT_SUCCESS, stdout);
}
while ((opt = getopt(*argc, argv, "?ivpfrtxads234o:w:h:")) != -1) {
switch (opt) {
case 'o':
g_flags.out = optarg;
break;
case 'd':
g_flags.dither = true;
break;
case 's':
g_flags.unsharp = true;
break;
case 'w':
g_flags.trailingnewline = true;
g_flags.width = ParseNumberOption(optarg);
break;
case 'h':
g_flags.trailingnewline = true;
g_flags.height = ParseNumberOption(optarg);
break;
case 'f':
g_flags.full = true;
break;
case 'i':
g_flags.invert = true;
break;
case '2':
g_flags.half = true;
break;
case 'r':
g_flags.ruler = true;
break;
case 'p':
g_flags.subpixel = true;
break;
case 'a':
g_flags.quant = kTtyQuantAnsi;
break;
case 'x':
g_flags.quant = kTtyQuantXterm256;
break;
case 't':
g_flags.quant = kTtyQuantTrue;
break;
case '3':
g_flags.blocks = kTtyBlocksCp437;
break;
case '4':
g_flags.blocks = kTtyBlocksUnicode;
break;
case 'v':
++g_loglevel;
break;
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
if (optind == *argc) {
if (!g_flags.out) g_flags.out = "-";
argv[(*argc)++] = "-";
}
if (!g_flags.full && (!g_flags.width || !g_flags.width)) {
ws.ws_col = 80;
ws.ws_row = 24;
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 ||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
g_flags.width = ws.ws_col * (1 + !g_flags.half);
g_flags.height = ws.ws_row * 2;
}
}
ttyquantsetup(g_flags.quant, kTtyQuantRgb, g_flags.blocks);
}
static unsigned char ChessBoard(unsigned y, unsigned x, unsigned char a,
unsigned char b) {
return !((y ^ x) & (1u << 2)) ? a : b;
}
static unsigned char AlphaBackground(unsigned y, unsigned x) {
return ChessBoard(y, x, 255, 200);
}
static void *Deblinterlace(long dyn, long dxn, unsigned char dst[3][dyn][dxn],
long syn, long sxn, long scn,
const unsigned char src[syn][sxn][scn], long y0,
long yn, long x0, long xn) {
long y, x;
unsigned char c;
for (y = y0; y < yn; ++y) {
for (x = x0; x < xn; ++x) {
switch (scn) {
case 1:
c = src[y][x][0];
dst[0][y][x] = c;
dst[1][y][x] = c;
dst[2][y][x] = c;
break;
case 2:
c = twixt8(AlphaBackground(y, x), src[y][x][0], src[y][x][1]);
dst[0][y][x] = c;
dst[1][y][x] = c;
dst[2][y][x] = c;
break;
case 3:
dst[0][y][x] = src[y][x][0];
dst[1][y][x] = src[y][x][1];
dst[2][y][x] = src[y][x][2];
break;
case 4:
c = AlphaBackground(y, x);
dst[0][y][x] = twixt8(c, src[y][x][0], src[y][x][3]);
dst[1][y][x] = twixt8(c, src[y][x][1], src[y][x][3]);
dst[2][y][x] = twixt8(c, src[y][x][2], src[y][x][3]);
break;
}
}
}
return dst;
}
static void *DeblinterlaceSubpixelBgr(long dyn, long dxn,
unsigned char dst[3][dyn][dxn][3],
long syn, long sxn,
const unsigned char src[syn][sxn][4],
long y0, long yn, long x0, long xn) {
long y, x;
for (y = y0; y < yn; ++y) {
for (x = x0; x < xn; ++x) {
dst[0][y][x][0] = 0;
dst[1][y][x][0] = 0;
dst[2][y][x][0] = src[y][x][2];
dst[0][y][x][1] = 0;
dst[1][y][x][1] = src[y][x][1];
dst[2][y][x][1] = 0;
dst[0][y][x][2] = src[y][x][0];
dst[1][y][x][2] = 0;
dst[2][y][x][2] = 0;
}
}
return dst;
}
struct Block {
char16_t c;
unsigned char b[4][2];
} kBlocks[] = {
{u' ', {{0000, 0000}, {0000, 0000}, {0000, 0000}, {0000, 0000}}}, //
{u'', {{0060, 0060}, {0060, 0060}, {0060, 0060}, {0060, 0060}}}, //
{u'', {{0140, 0140}, {0140, 0140}, {0140, 0140}, {0140, 0140}}}, //
{u'', {{0300, 0300}, {0300, 0300}, {0300, 0300}, {0300, 0300}}}, //
{u'', {{0377, 0377}, {0377, 0377}, {0377, 0377}, {0377, 0377}}}, //
{u'', {{0000, 0000}, {0000, 0000}, {0377, 0377}, {0377, 0377}}}, //
{u'', {{0377, 0000}, {0377, 0000}, {0377, 0000}, {0377, 0000}}}, //
{u'', {{0000, 0377}, {0000, 0377}, {0000, 0377}, {0000, 0377}}}, //
{u'', {{0377, 0377}, {0377, 0377}, {0000, 0000}, {0000, 0000}}}, //
};
static void *Raster(long yn, long xn, unsigned char Y[yn][xn]) {
long y, x, i, j, k, s, bi, bs;
for (y = 0; y + 4 <= yn; y += 4) {
if (y) fputc('\n', stdout);
for (x = 0; x + 2 <= xn; x += 2) {
bi = 0;
bs = LONG_MAX;
for (k = 0; k < ARRAYLEN(kBlocks); ++k) {
s = 0;
for (i = 0; i < 4; ++i) {
for (j = 0; j < 2; ++j) {
s += SQR(Y[y + i][x + j] - kBlocks[k].b[i][j]);
}
}
if (s < bs) {
bi = k;
bs = s;
}
}
fputwc(kBlocks[bi].c, stdout);
}
}
fputc('\n', stdout);
return Y;
}
static void *Invert(long yn, long xn, unsigned char Y[yn][xn]) {
long y, x;
if (!g_flags.invert) {
for (y = 0; y < yn; ++y) {
for (x = 0; x < xn; ++x) {
Y[y][x] = 255 - Y[y][x];
}
}
}
return Y;
}
static void *Grayify(long yn, long xn, unsigned char Y[yn][xn],
const unsigned char RGB[3][yn][xn]) {
long y, x;
for (y = 0; y < yn; ++y) {
for (x = 0; x < xn; ++x) {
Y[y][x] = RGB[0][y][x] * .299 + RGB[1][y][x] * .587 + RGB[2][y][x] * .114;
}
}
return Y;
}
static void ProcessImage(long yn, long xn, unsigned char RGB[3][yn][xn]) {
Raster(yn, xn,
Invert(yn, xn, Grayify(yn, xn, gc(memalign(32, yn * xn)), RGB)));
}
void WithImageFile(const char *path,
void fn(long yn, long xn, unsigned char RGB[3][yn][xn])) {
struct stat st;
void *map, *data, *data2;
int fd, yn, xn, cn, dyn, dxn, syn, sxn;
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
CHECK_NE(-1, fstat(fd, &st));
CHECK_GT(st.st_size, 0);
CHECK_LE(st.st_size, INT_MAX);
fadvise(fd, 0, 0, MADV_WILLNEED | MADV_SEQUENTIAL);
CHECK_NE(MAP_FAILED,
(map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)));
CHECK_NOTNULL(
(data = gc(stbi_load_from_memory(map, st.st_size, &xn, &yn, &cn, 0))),
"%s", path);
CHECK_NE(-1, munmap(map, st.st_size));
CHECK_NE(-1, close(fd));
if (g_flags.subpixel) {
data = DeblinterlaceSubpixelBgr(yn, xn, gc(memalign(32, yn * xn * 4 * 3)),
yn, xn, data, 0, yn, 0, xn);
xn *= 3;
} else {
data = Deblinterlace(yn, xn, gc(memalign(32, yn * xn * 4)), yn, xn, cn,
data, 0, yn, 0, xn);
cn = 3;
}
if (g_flags.height && g_flags.width) {
syn = yn;
sxn = xn;
dyn = g_flags.height * 4;
dxn = g_flags.width * 2;
while (HALF(syn) > dyn || HALF(sxn) > dxn) {
if (HALF(sxn) > dxn) {
Magikarp2xX(yn, xn, data, syn, sxn);
Magikarp2xX(yn, xn, (char *)data + yn * xn, syn, sxn);
Magikarp2xX(yn, xn, (char *)data + yn * xn * 2, syn, sxn);
sxn = HALF(sxn);
}
if (HALF(syn) > dyn) {
Magikarp2xY(yn, xn, data, syn, sxn);
Magikarp2xY(yn, xn, (char *)data + yn * xn, syn, sxn);
Magikarp2xY(yn, xn, (char *)data + yn * xn * 2, syn, sxn);
syn = HALF(syn);
}
}
data = EzGyarados(3, dyn, dxn, gc(memalign(32, dyn * dxn * 3)), cn, yn, xn,
data, 0, cn, dyn, dxn, syn, sxn, 0, 0, 0, 0);
yn = dyn;
xn = dxn;
}
fn(yn, xn, data);
}
int main(int argc, char *argv[]) {
int i;
GetOpts(&argc, argv);
stbi_set_unpremultiply_on_load(true);
for (i = optind; i < argc; ++i) {
WithImageFile(argv[i], ProcessImage);
}
return 0;
}