From a00bd89522e749aee5f7214b7c860eebe13d94ec Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 27 Jan 2023 13:15:37 +0400 Subject: [PATCH 1/8] Port bootmain.c from xv6-public. --- Makefile | 13 ++++-- bootmain.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/port.h | 10 ++--- elf.h | 41 ++++++++++++++++++ mbr.S | 97 +------------------------------------------ tools/mbrpad.c | 21 ++++++++++ 6 files changed, 188 insertions(+), 104 deletions(-) create mode 100644 bootmain.c create mode 100644 elf.h create mode 100644 tools/mbrpad.c diff --git a/Makefile b/Makefile index 4c8b27a..002227f 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,10 @@ image.bin: mbr.bin fs.img cat $^ >$@ kernel.bin: kernel.o console.o drivers/vga.o drivers/uart.o - $(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^ + $(LD) $(LDFLAGS) -o $@ -Ttext 0x9000 $^ + +bootmain.o: bootmain.c + $(CC) $(CFLAGS) -Os -c $< -o $@ %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ @@ -75,10 +78,14 @@ kernel.bin: kernel.o console.o drivers/vga.o drivers/uart.o %.o: %.S $(CC) -m32 -ffreestanding -c -g $^ -o $@ -mbr.bin: mbr.o +mbr.bin: mbr.raw tools/mbrpad + cp $< $@ + tools/mbrpad $@ + +mbr.raw: mbr.o bootmain.o $(LD) -m elf_i386 -Ttext=0x7c00 --oformat=binary $^ -o $@ -mbr.elf: mbr.o +mbr.elf: mbr.o bootmain.o $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: diff --git a/bootmain.c b/bootmain.c new file mode 100644 index 0000000..95a10fd --- /dev/null +++ b/bootmain.c @@ -0,0 +1,110 @@ +// Boot loader. +// +// Part of the boot block, along with mbr.S, which calls bootmain(). +// mbr.S has put the processor into protected 32-bit mode. +// bootmain() loads an ELF kernel image from the disk starting at +// sector 3 and then jumps to the kernel entry routine. + +#include "elf.h" +#include "drivers/port.h" + +#define SECTSIZE 512 + +typedef unsigned char uchar; +typedef unsigned int uint; + +void readseg(uchar*, uint, uint); + +inline void stosb(uchar* addr, uchar byte, uint count) { + asm volatile("cld; rep stosb" : "+D"(addr), "+a"(byte) : "c"(count) : "cc"); +} + +void +bootmain(void) +{ + Elf32_Ehdr *elf; + Elf32_Phdr *ph, *eph; + void (*entry)(void); + uchar* pa; + + elf = (Elf32_Ehdr*)0x10000; // scratch space + + // Read 1st page off disk + readseg((uchar*)elf, 4096, 0); + + // Is this an ELF executable? + if(elf->magic != ELF_MAGIC) + return; // let bootasm.S handle error + + // Load each program segment (ignores ph flags). + ph = (Elf32_Phdr*)((uchar*)elf + elf->e_phoff); + eph = ph + elf->e_phnum; + for(; ph < eph; ph++) { + pa = (uchar*)ph->p_paddr; + readseg(pa, ph->p_filesz, ph->p_offset); + if(ph->p_memsz > ph->p_filesz) + stosb(pa + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz); + } + + // Call the entry point from the ELF header. + // Does not return! + entry = (void(*)(void))(elf->e_entry); + entry(); +} + +void +waitdisk(void) +{ + // Wait for disk ready. + while((port_byte_in(0x1F7) & 0xC0) != 0x40) + ; +} + +static inline void +insl(int port, void *addr, int cnt) +{ + asm volatile("cld; rep insl" : + "=D" (addr), "=c" (cnt) : + "d" (port), "0" (addr), "1" (cnt) : + "memory", "cc"); +} + +// Read a single sector at offset into dst. +void +readsect(void *dst, uint offset) +{ + // Issue command. + waitdisk(); + port_byte_out(0x1F2, 1); // count = 1 + port_byte_out(0x1F3, offset); + port_byte_out(0x1F4, offset >> 8); + port_byte_out(0x1F5, offset >> 16); + port_byte_out(0x1F6, (offset >> 24) | 0xE0); + port_byte_out(0x1F7, 0x20); // cmd 0x20 - read sectors + + // Read data. + waitdisk(); + insl(0x1F0, dst, SECTSIZE/4); +} + +// Read 'count' bytes at 'offset' from kernel into physical address 'pa'. +// Might copy more than asked. +void +readseg(uchar* pa, uint count, uint offset) +{ + uchar* epa; + + epa = pa + count; + + // Round down to sector boundary. + pa -= offset % SECTSIZE; + + // Translate from bytes to sectors; kernel starts at sector 3. + offset = (offset / SECTSIZE) + 2; + + // If this is too slow, we could read lots of sectors at a time. + // We'd write more to memory than asked, but it doesn't matter -- + // we load in increasing order. + for(; pa < epa; pa += SECTSIZE, offset++) + readsect(pa, offset); +} diff --git a/drivers/port.h b/drivers/port.h index 380272a..5d2174a 100644 --- a/drivers/port.h +++ b/drivers/port.h @@ -2,24 +2,24 @@ static inline unsigned char port_byte_in(unsigned short port) { unsigned char result; - __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); + asm volatile("in %%dx, %%al" : "=a" (result) : "d" (port)); return result; } static inline unsigned short port_word_in(unsigned short port) { unsigned short result; - __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); + asm volatile("in %%dx, %%ax" : "=a" (result) : "d" (port)); return result; } static inline void port_byte_out(unsigned short port, unsigned char data) { - __asm__("outb %%al, %%dx" : : "a" (data), "d" (port)); + asm volatile("outb %%al, %%dx" : : "a" (data), "d" (port)); } static inline void port_word_out(unsigned short port, unsigned short data) { - __asm__("outw %%ax, %%dx" : : "a" (data), "d" (port)); + asm volatile("outw %%ax, %%dx" : : "a" (data), "d" (port)); } static inline void port_long_out(unsigned short port, unsigned int data) { - __asm__("outl %%eax, %%dx" : : "a" (data), "d" (port)); + asm volatile("outl %%eax, %%dx" : : "a" (data), "d" (port)); } diff --git a/elf.h b/elf.h new file mode 100644 index 0000000..efa86a0 --- /dev/null +++ b/elf.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +enum { + EI_NIDENT = 16, +}; + +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; + +#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian + +typedef struct { + uint32_t magic; + unsigned char e_ident[EI_NIDENT - 4]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + uint32_t p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +} Elf32_Phdr; diff --git a/mbr.S b/mbr.S index 20b5fe3..d05f6ee 100644 --- a/mbr.S +++ b/mbr.S @@ -7,101 +7,11 @@ _start: mov $banner, %si call print_string - call get_drive_geometry - call load_kernel call switch_to_32bit hlt jmp . // loop forever -get_drive_geometry: - mov $8, %ah - mov boot_drive, %dl - int $0x13 - inc %dh // number of heads - mov %dh, disk_heads - and 0x3f, %cl - mov %cl, sectors_per_track - ret - - -.equ ELF32_ENTRY_OFFSET, 0x18 -.equ ELF32_PHDR_OFFSET, 0x1c -.equ ELF32_PHENTSIZE_OFFSET, ELF32_PHDR_OFFSET + 14 -.equ ELF32_PHNUM_OFFSET, ELF32_PHENTSIZE_OFFSET + 2 -.equ ELF32_PHDR_P_OFFSET, 4 -.equ ELF32_PHDR_PTYPE_OFFSET, 0 -.equ ELF32_PHDR_FILESZ_OFFSET, 4*4 -.equ KERNEL_OFFSET, 0x1000 - -.equ PT_LOAD, 1 - -.equ MBR_SECTORS, 2 -.equ SECTOR_BASE, 1 -.equ ELFHDR_SECTORS, 8 - -.equ SECTOR_SIZE, 512 -.equ SECTOR_SHIFT, 9 - -load_kernel: - mov $1, %al // sectors to read - mov $SECTOR_BASE + MBR_SECTORS, %cl // start after MBR - call bios_disk_read - - mov KERNEL_OFFSET + ELF32_ENTRY_OFFSET, %si - mov %si, entry // store entry point - - mov KERNEL_OFFSET + ELF32_PHNUM_OFFSET, %si -read_segment: - dec %si // no offset to the first entry - mov %si, %ax - mulb KERNEL_OFFSET + ELF32_PHENTSIZE_OFFSET - mov %ax, %di - add KERNEL_OFFSET + ELF32_PHDR_OFFSET, %di - // now di holds offset to the phentry - mov KERNEL_OFFSET + ELF32_PHDR_PTYPE_OFFSET(%di), %ax - cmp $PT_LOAD, %ax - jnz read_segment // not a PT_LOAD segment - mov KERNEL_OFFSET + ELF32_PHDR_FILESZ_OFFSET(%di), %ax - test %ax, %ax - jz read_segment // empty segment - - // now di holds offset to the last phentry loaded from file, ax its filesz - - add KERNEL_OFFSET + ELF32_PHDR_P_OFFSET(%di), %ax - sub $0x1000, %ax // we won't load the header - add $SECTOR_SIZE - 1, %ax - shr $SECTOR_SHIFT, %ax // round up to sector count - - mov $SECTOR_BASE + MBR_SECTORS + ELFHDR_SECTORS, %cl //start after ELF header - call bios_disk_read - ret - -bios_disk_read: - // expects %al to specify number of sectors, %cl the initial sector - xor %ah, %ah - mov %ax, %si - mov $0, %ch // cylinder 0 - mov $0, %dh // head 0 - mov $KERNEL_OFFSET, %bx // bx -> destination - mov boot_drive, %dl // dl -> disk - mov $1, %al - -1: - mov $2, %ah // read mode - int $0x13 - jc fail - add $SECTOR_SIZE, %bx - inc %cl - dec %si - jnz 1b - ret - -fail: - mov $read_error, %si - call print_string - hlt - jmp . switch_to_32bit: mov $2, %al @@ -127,8 +37,7 @@ init_32bit: mov $KERN_STACK_BASE, %ebp // 6. setup stack mov %ebp, %esp - movzwl entry, %esi - call *%esi // 7. jump to the kernel + call bootmain // 7. load and run kernel jmp . // 8. loop forever @@ -146,7 +55,6 @@ repeat: done: ret - . = _start + 256 # pad to 256 bytes boot_drive: .byte 0 banner: @@ -173,6 +81,3 @@ gdt_end: gdt_descriptor: .word gdt_end - gdt_start - 1 // size (16 bit) .int gdt_start // address (32 bit) - - . = _start + 510 # pad to 510 bytes - .byte 0x55, 0xaa # boot sector magic value diff --git a/tools/mbrpad.c b/tools/mbrpad.c new file mode 100644 index 0000000..267b8a8 --- /dev/null +++ b/tools/mbrpad.c @@ -0,0 +1,21 @@ +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s mbr.bin\n", argv[0]); + return 1; + } + const char* filename = argv[1]; + int fd = open(filename, O_RDWR); + off_t length = lseek(fd, 0, SEEK_END); + if (length > 510) { + fprintf(stderr, "file %s is larger than 510 bytes (size: %llu)\n", + filename, length); + return 1; + } + lseek(fd, 510, SEEK_SET); + write(fd, "\x55\xaa", 2); + return 0; +} From ae91bed93ff3334d7ef2e779f79d80f230020a49 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 27 Jan 2023 12:25:39 +0000 Subject: [PATCH 2/8] Fix Linux build. --- Makefile | 12 +++++++----- tools/mbrpad.c | 5 +++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 002227f..7e69da5 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,12 @@ GDB=gdb +OBJCOPY=objcopy ifeq ($(shell uname -s),Darwin) AS=x86_64-elf-as LD=x86_64-elf-ld CC=x86_64-elf-gcc GDB=x86_64-elf-gdb +OBJCOPY=x86_64-elf-objcopy endif CFLAGS = -fno-pic -ffreestanding -static -fno-builtin -fno-strict-aliasing \ @@ -78,18 +80,18 @@ bootmain.o: bootmain.c %.o: %.S $(CC) -m32 -ffreestanding -c -g $^ -o $@ -mbr.bin: mbr.raw tools/mbrpad - cp $< $@ +mbr.bin: mbr.elf tools/mbrpad + $(OBJCOPY) -S -O binary -j .text $< $@ tools/mbrpad $@ mbr.raw: mbr.o bootmain.o - $(LD) -m elf_i386 -Ttext=0x7c00 --oformat=binary $^ -o $@ + $(LD) -N -m elf_i386 -Ttext=0x7c00 --oformat=binary $^ -o $@ mbr.elf: mbr.o bootmain.o - $(LD) -m elf_i386 -Ttext=0x7c00 $^ -o $@ + $(LD) -N -m elf_i386 -Ttext=0x7c00 $^ -o $@ clean: - rm -f *.elf *.img *.bin *.o */*.o tools/mkfs ejudge.sh + rm -f *.elf *.img *.bin *.raw *.o */*.o tools/mkfs ejudge.sh tools/%: tools/%.c gcc -Wall -Werror -g $^ -o $@ diff --git a/tools/mbrpad.c b/tools/mbrpad.c index 267b8a8..7a42dce 100644 --- a/tools/mbrpad.c +++ b/tools/mbrpad.c @@ -1,6 +1,7 @@ #include #include #include +#include int main(int argc, char* argv[]) { if (argc != 2) { @@ -11,8 +12,8 @@ int main(int argc, char* argv[]) { int fd = open(filename, O_RDWR); off_t length = lseek(fd, 0, SEEK_END); if (length > 510) { - fprintf(stderr, "file %s is larger than 510 bytes (size: %llu)\n", - filename, length); + fprintf(stderr, "file %s is larger than 510 bytes (size: %ju)\n", + filename, (uintmax_t)length); return 1; } lseek(fd, 510, SEEK_SET); From 5c3656adaca7d06ce1944256e36cbc62221d58e8 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 27 Jan 2023 12:51:04 +0000 Subject: [PATCH 3/8] Fix LLVM build. --- mbr.S | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/mbr.S b/mbr.S index d05f6ee..227677c 100644 --- a/mbr.S +++ b/mbr.S @@ -3,7 +3,6 @@ .code16 .global _start _start: - mov %dl, boot_drive mov $banner, %si call print_string @@ -55,20 +54,8 @@ repeat: done: ret -boot_drive: - .byte 0 banner: - .asciz "YABLOKO bootloader started\n\r" -read_error: - .asciz "Read error\n\r" - - .balign 2 -entry: - .word 0 -disk_heads: - .byte 0 -sectors_per_track: - .byte 0 + .asciz "Loader \n" .balign 4 gdt_start: From 9963c3f6a03a2aacad10f297e38f5ba67a131723 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 27 Jan 2023 16:55:45 +0400 Subject: [PATCH 4/8] Add LLVM build. --- .github/workflows/build.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7779ef..e40434f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,10 +8,14 @@ on: jobs: build: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v3 - name: build disk image run: make image.bin + llvm: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: LLVM build + run: make LLVM=on image.bin From 3344730568318974a42e4b9d1299d5c45b7ecffb Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Fri, 27 Jan 2023 16:58:37 +0400 Subject: [PATCH 5/8] Install lld for the LLVM build. --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e40434f..ed11560 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,5 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - name: Install lld + run: sudo apt-get install -y lld - name: LLVM build run: make LLVM=on image.bin From 7d1b88ec00d790a86124ef193fcd8a55d0882ea7 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Sat, 28 Jan 2023 13:22:17 +0400 Subject: [PATCH 6/8] Extract the OBJECTS variable. --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index aca2acb..5825871 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,9 @@ ASMFLAGS = -target elf-i386 -ffreestanding -c -g LDKERNELFLAGS = --script=script.ld endif +OBJECTS = kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \ + cpu/idt.o cpu/vectors.o lib/mem.o + run: image.bin qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio @@ -84,8 +87,7 @@ user/%: user/%.o user/crt.o image.bin: mbr.bin fs.img cat $^ >$@ -kernel.bin: kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \ - cpu/idt.o cpu/vectors.o lib/mem.o +kernel.bin: $(OBJECTS) $(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0x9000 $^ bootmain.o: bootmain.c From 1679518a28ac41e208a6ad17730ad99c65b49ca5 Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 31 Jan 2023 13:11:09 +0400 Subject: [PATCH 7/8] Lecture 3: running binaries in userspace. --- Makefile | 7 ++--- console.h | 2 +- cpu/gdt.c | 65 +++++++++++++++++++++++++++++++++++++++++++++ cpu/idt.c | 24 +++++++++++++++++ cpu/swtch.S | 20 ++++++++++++++ fs/fs.c | 12 +++++++++ kernel.c | 34 ++++++++++++++++++++---- lib/string.c | 20 ++++++++++++++ lib/string.h | 7 +++++ proc.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ proc.h | 4 +++ syscall.h | 9 +++++++ user/crt.c | 18 +++++++++++++ user/div0.c | 5 ++++ user/false.c | 3 +++ user/greet.c | 7 +++++ 16 files changed, 303 insertions(+), 9 deletions(-) create mode 100644 cpu/gdt.c create mode 100644 cpu/swtch.S create mode 100644 fs/fs.c create mode 100644 lib/string.c create mode 100644 lib/string.h create mode 100644 proc.c create mode 100644 proc.h create mode 100644 syscall.h create mode 100644 user/crt.c create mode 100644 user/div0.c create mode 100644 user/false.c create mode 100644 user/greet.c diff --git a/Makefile b/Makefile index 5825871..5a10743 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,8 @@ LDKERNELFLAGS = --script=script.ld endif OBJECTS = kernel.o console.o drivers/vga.o drivers/uart.o drivers/keyboard.o \ - cpu/idt.o cpu/vectors.o lib/mem.o + cpu/idt.o cpu/gdt.o cpu/swtch.o cpu/vectors.o lib/mem.o proc.o lib/string.o \ + fs/fs.o run: image.bin qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio @@ -76,8 +77,8 @@ debug-nox: image.bin -ex "break _start" \ -ex "continue" -fs.img: kernel.bin tools/mkfs - tools/mkfs $@ $< +fs.img: kernel.bin tools/mkfs user/false user/greet user/div0 + tools/mkfs $@ $< user/false user/greet user/div0 LDFLAGS=-m elf_i386 diff --git a/console.h b/console.h index 2e5c22a..272fad2 100644 --- a/console.h +++ b/console.h @@ -1,4 +1,4 @@ #pragma once void printk(const char* msg); -void panic(const char* msg); +_Noreturn void panic(const char* msg); diff --git a/cpu/gdt.c b/cpu/gdt.c new file mode 100644 index 0000000..f511ef0 --- /dev/null +++ b/cpu/gdt.c @@ -0,0 +1,65 @@ +#include "gdt.h" +#include "../lib/string.h" + +#include + +struct seg_desc_t { + uint16_t lim_15_0; // Low bits of segment limit + uint16_t base_15_0; // Low bits of segment base address + uint8_t base_23_16; // Middle bits of segment base address + uint8_t type : 4; // Segment type (see STA_ constants) + uint8_t s : 1; // 0 = system, 1 = application + uint8_t dpl : 2; // Descriptor Privilege Level + uint8_t p : 1; // Present + uint8_t lim_19_16 : 4; // High bits of segment limit + uint8_t avl : 1; // Unused (available for software use) + uint8_t rsv1 : 1; // Reserved + uint8_t db : 1; // 0 = 16-bit segment, 1 = 32-bit segment + uint8_t g : 1; // Granularity: limit scaled by 4K when set + uint8_t base_31_24; // High bits of segment base address +} __attribute__((packed)); + +#define SEG(type, base, lim, dpl) (struct seg_desc_t) \ +{ ((lim) >> 12) & 0xffff, (uint)(base) & 0xffff, \ + ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ + (uint)(lim) >> 28, 0, 0, 1, 1, (uint)(base) >> 24 } +#define SEG16(type, base, lim, dpl) (struct seg_desc_t) \ +{ (lim) & 0xffff, (uint)(base) & 0xffff, \ + ((uint)(base) >> 16) & 0xff, type, 1, dpl, 1, \ + (uint)(lim) >> 16, 0, 0, 1, 0, (uint)(base) >> 24 } + +struct seg_desc_t seg_desc[6]; + +void init_seg_desc() { + seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0); + seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); + seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); + seg_desc[SEG_UDATA] = SEG(STA_W, USER_BASE, 0xffffffff - USER_BASE, DPL_USER); +} + +struct gdt_desc_t { + uint16_t size; + void* ptr; +} __attribute__((packed)); + +void load_gdt() { + init_seg_desc(); + + struct gdt_desc_t gdt_desc; + gdt_desc.size = sizeof(seg_desc) - 1; + gdt_desc.ptr = seg_desc; + asm("lgdt %0": : "m"(gdt_desc)); +} + +void switchuvm(struct taskstate *tss, void* esp) { + memset(tss, 0, sizeof(*tss)); + seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, 0); + seg_desc[SEG_TSS].s = 0; + tss->ss0 = SEG_KDATA << 3; + tss->esp0 = (uint)esp; + // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit + // forbids I/O instructions (e.g., inb and outb) from user space + tss->iomb = (ushort) 0xFFFF; + + asm("ltr %0": : "r"((ushort)(SEG_TSS << 3))); +} diff --git a/cpu/idt.c b/cpu/idt.c index 78f41f0..4b4137c 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -1,5 +1,7 @@ #include "isr.h" #include "gdt.h" +#include "../syscall.h" +#include "../proc.h" #include "../drivers/port.h" #include "../console.h" @@ -47,6 +49,7 @@ void init_idt() { for (int i = 0; i < IDT_HANDLERS; i++) { set_idt_gate(i, 0, default_handlers[i], 0); } + set_idt_gate(T_SYSCALL, 1, default_handlers[T_SYSCALL], DPL_USER); } const char * const exception_messages[] = { @@ -82,6 +85,26 @@ void register_interrupt_handler(uint8_t i, isr_t handler) { } void trap(registers_t *r) { + if (r->int_no == T_SYSCALL) { + switch (r->eax) { + case SYS_exit: + if (r->ebx == 0) { + printk("* success\n"); + } else { + printk("* failure\n"); + } + killproc(); + case SYS_greet: + printk("Hello world!\n"); + r->eax = 0; + break; + default: + printk("Unknown syscall\n"); + r->eax = -1; + } + return; + } + if (r->int_no < 32) { const char* msg = "Reserved"; if (r->int_no < ARRLEN(exception_messages)) { @@ -90,6 +113,7 @@ void trap(registers_t *r) { panic(msg); } + /* Handle the interrupt in a more modular way */ if (interrupt_handlers[r->int_no] != 0) { isr_t handler = interrupt_handlers[r->int_no]; handler(r); diff --git a/cpu/swtch.S b/cpu/swtch.S new file mode 100644 index 0000000..db6b56e --- /dev/null +++ b/cpu/swtch.S @@ -0,0 +1,20 @@ + // swtch(void** oldstack, void* newstack) + .global swtch +swtch: + mov 4(%esp), %eax // eax holds "oldstack" + mov 8(%esp), %ecx + + push %ebx + push %ebp + push %esi + push %edi + + mov %esp, (%eax) // save stack ptr to "oldstack" + mov %ecx, %esp // use "newstack" as stack ptr + + pop %edi + pop %esi + pop %ebp + pop %ebx + + ret diff --git a/fs/fs.c b/fs/fs.c new file mode 100644 index 0000000..5f594ce --- /dev/null +++ b/fs/fs.c @@ -0,0 +1,12 @@ +#include "fs.h" +#include "../lib/string.h" +#include "../drivers/ata.h" +#include "../console.h" + +int stat(const char* name, struct stat *buf) { + panic("stat not implemented"); +} + +int read_file(const char* name, void* buf, uint32_t bufsize) { + panic("read_file not implemented"); +} diff --git a/kernel.c b/kernel.c index 481c60b..5e7441f 100644 --- a/kernel.c +++ b/kernel.c @@ -1,19 +1,43 @@ +asm(".asciz \"kernel start\\n\""); + #include "console.h" -#include "drivers/vga.h" -#include "drivers/uart.h" -#include "drivers/keyboard.h" #include "cpu/isr.h" +#include "cpu/gdt.h" +#include "drivers/keyboard.h" +#include "drivers/vga.h" +#include "drivers/ata.h" +#include "drivers/misc.h" +#include "drivers/uart.h" +#include "fs/fs.h" +#include "lib/string.h" +#include "proc.h" void _start() { - uartinit(); + load_gdt(); init_keyboard(); + uartinit(); load_idt(); sti(); vga_clear_screen(); - printk("\nYABLOKO\n> "); + printk("YABLOKO\n"); + + printk("\n> "); while (1) { + if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') { + if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) { + qemu_shutdown(); + } else if (!strncmp("run ", kbd_buf, 4)) { + kbd_buf[kbd_buf_size-1] = '\0'; + const char* cmd = kbd_buf + 4; + run_elf(cmd); + } else { + printk("unknown command, try: halt | run CMD"); + } + kbd_buf_size = 0; + printk("\n> "); + } asm("hlt"); } } diff --git a/lib/string.c b/lib/string.c new file mode 100644 index 0000000..fab905b --- /dev/null +++ b/lib/string.c @@ -0,0 +1,20 @@ +#include "string.h" + +int strncmp(const char* s1, const char* s2, size_t size) { + while (size && *s1 && *s2 && *s1 == *s2) { + size--; + s1++; + s2++; + } + if (!size) { + return 0; + } + return (unsigned char)(*s1) - (unsigned char)(*s2); +} + +void memset(void* b, char c, size_t len) { + char* p = b; + for (size_t i = 0; i < len; ++i) { + p[i] = c; + } +} diff --git a/lib/string.h b/lib/string.h new file mode 100644 index 0000000..6184c33 --- /dev/null +++ b/lib/string.h @@ -0,0 +1,7 @@ +#pragma once + +typedef unsigned size_t; + +void kmemmove(char* dst, char* src, size_t size); +int strncmp(const char* s1, const char* s2, size_t size); +void memset(void* b, char c, size_t len); diff --git a/proc.c b/proc.c new file mode 100644 index 0000000..6ec0971 --- /dev/null +++ b/proc.c @@ -0,0 +1,75 @@ +#include "elf.h" +#include "proc.h" +#include "fs/fs.h" +#include "cpu/gdt.h" +#include "cpu/isr.h" +#include "lib/mem.h" +#include "lib/string.h" +#include "console.h" + +struct context { + // matches the behavior of swtch() + uint32_t edi, esi, ebp, ebx; + uint32_t eip; // return address for swtch() +}; + +struct kstack { + uint32_t space[400]; + struct context context; + registers_t trapframe; + char bottom[]; +}; + +struct task { + struct taskstate tss; + struct kstack stack; +}; + +struct vm { + void *kernel_thread; + void *user_thread; + struct task *user_task; +} *vm; + +void trapret(); +void swtch(void** oldstack, void* newstack); + +void run_elf(const char* name) { + if (!vm) { + vm = kmalloc(sizeof(struct vm)); + vm->user_task = kmalloc(sizeof(struct task)); + switchuvm(&vm->user_task->tss, vm->user_task->stack.bottom); + } + if (read_file(name, (void*)USER_BASE, 100 << 20) <= 0) { + printk(name); + printk(": file not found\n"); + return; + } + Elf32_Ehdr *hdr = (void*)USER_BASE; + + struct kstack *u = &vm->user_task->stack; + memset(u, 0, sizeof(*u)); + u->context.eip = (uint32_t)trapret; + + registers_t *tf = &u->trapframe; + tf->eip = hdr->e_entry; + tf->cs = (SEG_UCODE << 3) | DPL_USER; + tf->ds = (SEG_UDATA << 3) | DPL_USER; + tf->es = tf->ds; + tf->fs = tf->ds; + tf->gs = tf->ds; + tf->ss = tf->ds; + tf->eflags = FL_IF; + tf->useresp = USER_STACK_BASE; + + // initialization done, now switch to the process + swtch(&vm->kernel_thread, &u->context); + + // process has finished +} + +_Noreturn void killproc() { + void* task_stack; + swtch(&task_stack, vm->kernel_thread); + __builtin_unreachable(); +} diff --git a/proc.h b/proc.h new file mode 100644 index 0000000..54ede88 --- /dev/null +++ b/proc.h @@ -0,0 +1,4 @@ +#pragma once + +void run_elf(const char* name); +_Noreturn void killproc(); diff --git a/syscall.h b/syscall.h new file mode 100644 index 0000000..4c9ba03 --- /dev/null +++ b/syscall.h @@ -0,0 +1,9 @@ +#pragma once + +enum { + T_SYSCALL = 0x84, + SYS_exit = 0, + SYS_greet = 1, +}; + +int syscall(int call, int arg); diff --git a/user/crt.c b/user/crt.c new file mode 100644 index 0000000..5032810 --- /dev/null +++ b/user/crt.c @@ -0,0 +1,18 @@ +#include "../syscall.h" + +int main(); + +int syscall(int call, int arg) { + asm("int $0x84": "+a"(call) : "b"(arg)); + return call; +} + +_Noreturn +void _exit(int exit_status) { + syscall(SYS_exit, exit_status); + __builtin_unreachable(); +} + +void _start() { + _exit(main()); +} diff --git a/user/div0.c b/user/div0.c new file mode 100644 index 0000000..76c2038 --- /dev/null +++ b/user/div0.c @@ -0,0 +1,5 @@ +int main(void) { + volatile int x = 1, y = 0; + x /= y; + return 0; +} diff --git a/user/false.c b/user/false.c new file mode 100644 index 0000000..6346e2d --- /dev/null +++ b/user/false.c @@ -0,0 +1,3 @@ +int main() { + return 1; +} diff --git a/user/greet.c b/user/greet.c new file mode 100644 index 0000000..28b539b --- /dev/null +++ b/user/greet.c @@ -0,0 +1,7 @@ +#include "../syscall.h" + +int main(void) { + syscall(SYS_greet, 0); + syscall(SYS_greet, 0); + return 0; +} From 4de207cd42aeb9c7977dfca932b8849b5fe7aa1f Mon Sep 17 00:00:00 2001 From: Alexander Myltsev Date: Tue, 31 Jan 2023 15:28:29 +0400 Subject: [PATCH 8/8] Fix LLVM build. --- user/div0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/div0.c b/user/div0.c index 76c2038..e633708 100644 --- a/user/div0.c +++ b/user/div0.c @@ -1,5 +1,5 @@ int main(void) { volatile int x = 1, y = 0; x /= y; - return 0; + return x; }