Skip to content

Commit

Permalink
kernel: add linux compatibility layer
Browse files Browse the repository at this point in the history
  • Loading branch information
willdurand committed Apr 3, 2022
1 parent 3f32af7 commit 6c4ffe3
Show file tree
Hide file tree
Showing 23 changed files with 838 additions and 65 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ ifeq ($(CONFIG_USE_FAKE_CLOCK), 1)
libk_c_files += $(kernel_src_dir)/time/fake_clock.c
endif

ifeq ($(CONFIG_LINUX_COMPAT), 1)
KERNEL_CONFIG += -DCONFIG_LINUX_COMPAT
LIBC_CONFIG += -DCONFIG_LINUX_COMPAT
endif

# This file exists in a Docker container because we copy it in `Dockerfile`.
in_docker = $(wildcard /tmp/install-linux-deps)
ifneq ($(in_docker),)
Expand Down Expand Up @@ -434,6 +439,7 @@ what: ## display some information about the current configuration
echo "OS_NAME : $(OS_NAME)"
echo "ARCH : $(ARCH)"
echo ""
echo "CONFIG_LINUX_COMPAT = $(CONFIG_LINUX_COMPAT)"
echo "CONFIG_SEMIHOSTING = $(CONFIG_SEMIHOSTING)"
echo "CONFIG_USE_DLMALLOC = $(CONFIG_USE_DLMALLOC)"
echo "CONFIG_USE_FAKE_CLOCK = $(CONFIG_USE_FAKE_CLOCK)"
Expand Down
2 changes: 2 additions & 0 deletions Makefile-cfg.include
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ LLVM_SUFFIX ?=

# When set to 1, enable the Undefined Behavior SANitizer.
UBSAN ?=
# When set to 1, enable Linux (binary) compatibility.
CONFIG_LINUX_COMPAT ?=
# When set to 1, enable semi-hosting mode (QEMU, mainly).
CONFIG_SEMIHOSTING ?=
# When set to 1, use dlmalloc for malloc/free/realloc (instead of liballoc).
Expand Down
1 change: 1 addition & 0 deletions data/initrd/etc/hosts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
140.82.121.4 github
1 change: 1 addition & 0 deletions data/initrd/etc/passwd
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root:x:0:0::/:none
8 changes: 8 additions & 0 deletions include/kernel/proc/descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
#ifndef PROC_DESCRIPTOR_H
#define PROC_DESCRIPTOR_H

#include <arpa/inet.h>
#include <fs/vfs.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/types.h>

#define STDIN 0
Expand All @@ -23,6 +25,8 @@ typedef struct descriptor
uint32_t type;
uint32_t protocol;
uint16_t port;
struct sockaddr_in addr;
socklen_t addr_len;
} descriptor_t;

/**
Expand Down Expand Up @@ -82,4 +86,8 @@ int descriptor_udp_lookup(uint16_t port);
*/
bool is_protocol_supported(uint32_t type, uint32_t protocol);

void duplicate_descriptor(int oldfd, int newfd);

int descriptor_raw_lookup(uint32_t protocol, in_addr_t src_addr);

#endif
108 changes: 108 additions & 0 deletions include/libc/sys/linux_compat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// https://chromium.googlesource.com/chromiumos/docs/+/HEAD/constants/syscalls.md
#ifndef SYS_LINUX_COMPAT_H
#define SYS_LINUX_COMPAT_H

#ifdef __x86_64__

#define SYSCALL_READ 0
#define SYSCALL_WRITE 1
#define SYSCALL_OPEN 2
#define SYSCALL_CLOSE 3
#define SYSCALL_FSTAT 5
#define SYSCALL_POLL 7
#define SYSCALL_LSEEK 8
#define SYSCALL_MMAP 9
#define SYSCALL_BRK 12
#define SYSCALL_RT_SIGPROCMASK 14
#define SYSCALL_IOCTL 16
#define SYSCALL_WRITEV 20
#define SYSCALL_DUP2 33
#define SYSCALL_NANOSLEEP 35
#define SYSCALL_GETPID 39
#define SYSCALL_SOCKET 41
#define SYSCALL_CONNECT 42
#define SYSCALL_SENDTO 44
#define SYSCALL_RECVFROM 45
#define SYSCALL_BIND 49
#define SYSCALL_GETSOCKNAME 51
#define SYSCALL_SETSOCKOPT 54
#define SYSCALL_EXECV 59
#define SYSCALL_EXIT 60
#define SYSCALL_UNAME 63
#define SYSCALL_FCNTL 72
#define SYSCALL_GETCWD 79
#define SYSCALL_GETTIMEOFDAY 96
#define SYSCALL_SETUID 105
#define SYSCALL_SETGID 106
#define SYSCALL_GETEUID 107
#define SYSCALL_ARCH_PRCTL 158
#define SYSCALL_REBOOT 169
#define SYSCALL_SET_TID_ADDR 218
#define SYSCALL_EXIT_GROUP 231
#define SYSCALL_OPENAT 257

#elif __arm__

#define SYSCALL_EXIT 1
#define SYSCALL_READ 3
#define SYSCALL_WRITE 4
#define SYSCALL_OPEN 5
#define SYSCALL_CLOSE 6
#define SYSCALL_EXECV 11
#define SYSCALL_LSEEK 19
#define SYSCALL_GETPID 20
#define SYSCALL_BRK 45
#define SYSCALL_GETEUID 49
#define SYSCALL_IOCTL 54
#define SYSCALL_DUP2 63
#define SYSCALL_GETTIMEOFDAY 78
#define SYSCALL_REBOOT 88
#define SYSCALL_FSTAT 108
#define SYSCALL_WRITEV 146
#define SYSCALL_EXIT_GROUP 248
#define SYSCALL_SET_TID_ADDR 256
#define SYSCALL_SOCKET 281
#define SYSCALL_SENDTO 290
#define SYSCALL_RECVFROM 292
#define SYSCALL_OPENAT 322

// Not available on AArch32:
//
// - SYSCALL_ARCH_PRCTL

#elif __aarch64__

#define SYSCALL_IOCTL 29
#define SYSCALL_OPENAT 56
#define SYSCALL_CLOSE 57
#define SYSCALL_LSEEK 62
#define SYSCALL_READ 63
#define SYSCALL_WRITE 64
#define SYSCALL_WRITEV 66
#define SYSCALL_FSTAT 80
#define SYSCALL_EXIT 93
#define SYSCALL_EXIT_GROUP 94
#define SYSCALL_SET_TID_ADDR 96
#define SYSCALL_REBOOT 142
#define SYSCALL_GETTIMEOFDAY 169
#define SYSCALL_GETPID 172
#define SYSCALL_GETEUID 175
#define SYSCALL_SOCKET 198
#define SYSCALL_SENDTO 206
#define SYSCALL_RECVFROM 207
#define SYSCALL_BRK 214
#define SYSCALL_EXECV 221

// Not available on AArch64:
//
// - SYSCALL_OPEN
// - SYSCALL_DUP2
// - SYSCALL_ARCH_PRCTL

#endif

// Not available outside ArvernOS:
#define SYSCALL_TEST 348
#define SYSCALL_GETHOSTBYNAME2 349

#endif
8 changes: 8 additions & 0 deletions include/libc/sys/syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
#include <sys/time.h>
#include <sys/types.h>

#ifdef CONFIG_LINUX_COMPAT

#include <sys/linux_compat.h>

#else // CONFIG_LINUX_COMPAT

#define SYSCALL_TEST 1
#define SYSCALL_WRITE 2
#define SYSCALL_READ 3
Expand All @@ -29,6 +35,8 @@
#define SYSCALL_EXIT 16
#define SYSCALL_OPENAT 17

#endif // CONFIG_LINUX_COMPAT

#define SYSCALL_SET_ERRNO() \
if (retval < 0) { \
errno = -retval; \
Expand Down
2 changes: 1 addition & 1 deletion src/kernel/arch/x86_64/Makefile.include
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ QEMU = qemu-system-x86_64
# Options for the different tools
###############################################################################

QEMU_OPTIONS += -m 512M
QEMU_OPTIONS += -m 512M -cpu IvyBridge
QEMU_OPTIONS += -serial file:$(log_file)
QEMU_OPTIONS += -netdev user,id=u1,ipv6=off,dhcpstart=10.0.2.20
QEMU_OPTIONS += -device rtl8139,netdev=u1
Expand Down
28 changes: 27 additions & 1 deletion src/kernel/arch/x86_64/asm/boot.asm
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,37 @@ start:
; load the 64-bit GDT
lgdt [gdt64.pointer]

%ifdef CONFIG_LINUX_COMPAT
call enable_sse

; enable the following instructions: rdfsbase, rdgsbase, wrfsbase, wrgsbase
mov eax, cr4
or eax, 1 << 16
mov cr4, eax
%endif

jmp gdt64.kernel_code:long_mode_start

; Should not be reached.
; should not be reached
hlt

enable_sse:
mov eax, 0x1 ; check for SSE
cpuid
test edx, 1 << 25
jz .no_sse ; after this, SSE can be enabled
mov eax, cr0
and ax, 0xFFFB ; clear coprocessor emulation CR0.EM
or ax, 0x2 ; set coprocessor monitoring CR0.MP
mov cr0, eax
mov eax, cr4
or ax, 3 << 9 ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
mov cr4, eax
ret

.no_sse:
ret

; -----------------------------------------------------------------------------
; make sure the kernel was really loaded by a Multiboot compliant bootloader
%define MULTIBOOT2_MAGIC_VALUE 0x36d76289
Expand Down
60 changes: 46 additions & 14 deletions src/kernel/arch/x86_64/asm/k_syscall.asm
Original file line number Diff line number Diff line change
@@ -1,42 +1,74 @@
global syscall_handler

%define RSP0 0

syscall_handler:
sti

push rcx ; save the return address
push r11 ; save the flags
%ifdef CONFIG_LINUX_COMPAT
; Save the current stack pointer to be used in the iret frame at the end.
mov gs:[RSP0], rsp
%endif

; Save the return address, flags, base pointer for sysret.
push rcx
push r11
push rbp

; Save callee-saved registers.
push rbx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r12
push r13
push r14
push r15

; Save the syscall params.
push rdi
push rsi
push rdx
push r10
push r8
push r9

mov rcx, r10 ; fix 3rd syscall arg

extern syscall_handlers
call [rax * 8 + syscall_handlers]

pop r15
pop r14
pop r13
pop r12
pop r10
pop r9
pop r8
pop r10
pop rdx
pop rsi
pop rdi
pop rdx

pop r15
pop r14
pop r13
pop r12
pop rbx

pop rbp
pop r11
pop rcx

; This is needed because usermode has been broken recently (when the multi-
; tasking code has been introduced). Therefore, we only have kernel mode (ring
; 0). `sysret` always returns to ring 3 so we cannot use it when a syscall is
; done from ring 0 (which happens with Linux unmodified binaries). That's why
; we use `iretq`.
%ifdef CONFIG_LINUX_COMPAT
push 0x10
push qword gs:[RSP0]
push r11
push 0x08
push rcx

cli

iretq
%else
cli

o64 sysret
%endif
8 changes: 8 additions & 0 deletions src/kernel/arch/x86_64/asm/proc.asm
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ ret_from_fork:
call task_schedule_tail

call r12

%ifdef CONFIG_LINUX_COMPAT
global linux_compat_start

linux_compat_start:
mov rsp, rsi
jmp rdi
%endif
15 changes: 15 additions & 0 deletions src/kernel/arch/x86_64/kmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ void load_modules(multiboot_info_t* mbi);
void load_network_config(inish_config_t* kernel_cfg, net_driver_t* driver);
void load_system_config(inish_config_t* kernel_cfg);

#ifdef CONFIG_LINUX_COMPAT

typedef struct cpu_vars
{
uint64_t rsp0;
} cpu_vars_t;

static cpu_vars_t cpu_vars = { 0 };

#endif // CONFIG_LINUX_COMPAT

static uintptr_t initrd_addr = 0;

void load_modules(multiboot_info_t* mbi)
Expand Down Expand Up @@ -117,6 +128,10 @@ void kmain(uintptr_t addr)

kmain_early_start();

#ifdef CONFIG_LINUX_COMPAT
__asm__("wrgsbase %0" ::"r"(&cpu_vars));
#endif

tss_init();
frame_init();
paging_init();
Expand Down
10 changes: 3 additions & 7 deletions src/kernel/kmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,12 @@ void kinit()
argv[_argc] = NULL;
free(_cmdline);

INFO("kmain: loading %s...", argv[0]);

if (strcmp(argv[0], "kshell") == 0) {
INFO("kmain: loading %s...", argv[0]);
kshell(argc, argv);
} else {
// TODO: create task

// INFO("kmain: switching to usermode... (%s)", argv[0]);
//
// k_execv(argv[0], argv);
WARN("cannot execute: %s", saved_cmdline);
k_execv(argv[0], argv);
}

k_exit(EXIT_FAILURE);
Expand Down
Loading

0 comments on commit 6c4ffe3

Please sign in to comment.