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 2, 2022
1 parent bdccf41 commit a50c764
Show file tree
Hide file tree
Showing 23 changed files with 701 additions and 46 deletions.
8 changes: 7 additions & 1 deletion 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 @@ -192,7 +197,7 @@ LIBC_CFLAGS += $(LIBC_CONFIG)
KERNEL_INCLUDES += $(INCLUDES)
KERNEL_INCLUDES += -I$(include_dir)/kernel/ -I$(arch_src)/
KERNEL_INCLUDES += $(addprefix -I$(external_dir)/,$(addsuffix /, $(external_deps)))
KERNEL_ASM_FLAGS +=
KERNEL_ASM_FLAGS += $(KERNEL_CONFIG)
# We need to have -fno-omit-frame-pointer or the kernel stack backtrace won't
# get the stack.
KERNEL_CFLAGS += -fno-omit-frame-pointer
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
95 changes: 95 additions & 0 deletions include/libc/sys/linux_compat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// 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_LSEEK 8
#define SYSCALL_BRK 12
#define SYSCALL_IOCTL 16
#define SYSCALL_WRITEV 20
#define SYSCALL_DUP2 33
#define SYSCALL_GETPID 39
#define SYSCALL_SOCKET 41
#define SYSCALL_SENDTO 44
#define SYSCALL_RECVFROM 45
#define SYSCALL_EXECV 49
#define SYSCALL_EXIT 60
#define SYSCALL_GETTIMEOFDAY 96
#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

#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
3 changes: 2 additions & 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 All @@ -51,6 +51,7 @@ libk_c_files += $(wildcard $(arch_src)/drivers/*.c)
libk_c_files += $(wildcard $(arch_src)/fs/dev/*.c)
libk_c_files += $(wildcard $(arch_src)/fs/proc/*.c)
libk_c_files += $(wildcard $(arch_src)/mmu/*.c)
libk_c_files += $(wildcard $(arch_src)/proc/*.c)
libk_c_files += $(wildcard $(arch_src)/sys/*.c)
libk_asm_files += $(wildcard $(arch_src)/asm/*.asm)
libc_test_files += $(patsubst $(tests_dir)/%.c, %, $(shell find $(tests_dir)/libc -name '*.c'))
Expand Down
26 changes: 26 additions & 0 deletions 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

; enables the instructions RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE
mov eax, cr4
or eax, 1 << 16
mov cr4, eax
%endif

jmp gdt64.kernel_code:long_mode_start

; 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
21 changes: 21 additions & 0 deletions src/kernel/arch/x86_64/asm/k_syscall.asm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ global syscall_handler
syscall_handler:
sti

%ifdef CONFIG_LINUX_COMPAT
push rsp
%endif

push rcx ; save the return address
push r11 ; save the flags
push rbp
Expand Down Expand Up @@ -39,4 +43,21 @@ syscall_handler:
pop rcx

cli

; TODO: 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).
%ifdef CONFIG_LINUX_COMPAT
pop rbp

push 0x10
push rbp
push r11
push 0x08
push rcx

iretq
%else
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 do_start

do_start:
mov rsp, rsi
jmp rdi
%endif
3 changes: 2 additions & 1 deletion src/kernel/arch/x86_64/core/elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ void load_segment(uint8_t* data, elf_program_header_t* program_header)
uint32_t flags =
PAGING_FLAG_PRESENT | PAGING_FLAG_USER_ACCESSIBLE | PAGING_FLAG_WRITABLE;

if (!(program_header->flags & ELF_PROGRAM_FLAG_EXECUTE)) {
if ((program_header->flags & ELF_PROGRAM_FLAG_EXECUTE) !=
ELF_PROGRAM_FLAG_EXECUTE) {
flags |= PAGING_FLAG_NO_EXECUTE;
}

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
9 changes: 9 additions & 0 deletions src/kernel/net/ipv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#include <net/icmpv4.h>
#include <net/logging.h>
#include <net/udp.h>
#include <proc/descriptor.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>

ipv4_header_t ipv4_create_header(uint8_t src_ip[4],
in_addr_t dst_addr,
Expand Down Expand Up @@ -36,6 +38,13 @@ void ipv4_receive_packet(net_interface_t* interface,
src_ip[3],
interface->id);

int sockfd = descriptor_raw_lookup(header.proto, header.src_addr);
if (sockfd >= 0) {
// Handle SOCK_RAW.
write(sockfd, data, len);
return;
}

switch (header.proto) {
case IPV4_PROTO_ICMP:
icmpv4_receive_packet(interface, data, &header);
Expand Down
2 changes: 1 addition & 1 deletion src/kernel/net/udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ void udp_receive_packet(net_interface_t* interface,
int sockfd = descriptor_udp_lookup(udp_header.dst_port);
NET_DEBUG("got sockfd=%d for dst_port=%d", sockfd, udp_header.dst_port);

if (sockfd > 0) {
if (sockfd >= 0) {
write(sockfd, udp_data, udp_header.len - sizeof(udp_header_t));
return;
}
Expand Down
Loading

0 comments on commit a50c764

Please sign in to comment.