From: John Tsiombikas Date: Sun, 1 Oct 2023 23:56:00 +0000 (+0300) Subject: porting over pcboot codebase X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=commitdiff_plain;h=f41dfaf1cf4f2338a0f1a4ab4b1b520c9408a5da;p=com32 porting over pcboot codebase --- diff --git a/.gitignore b/.gitignore index dc5c1f6..5300cea 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ RUN disasm dosbox.conf +*.log +tofloppy diff --git a/Makefile b/Makefile index a3411a3..1872bbf 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,9 @@ bin = test.com warn = -pedantic -Wall inc = -Isrc -Isrc/libc +AS = nasm + +ASFLAGS = -Isrc/ CFLAGS = -m32 -march=i386 $(warn) $(opt) $(dbg) -fno-pic -ffreestanding -nostdinc $(inc) $(def) LDFLAGS = -m elf_i386 -nostdlib -T com32.ld -Map test.map @@ -16,7 +19,7 @@ $(bin): $(obj) -include $(dep) %.o: %.asm - nasm -o $@ -f elf $< + $(AS) -o $@ -f elf $(ASFLAGS) $< %.s: %.c $(CC) $(CFLAGS) -S $< -o $@ diff --git a/src/asmops.h b/src/asmops.h new file mode 100644 index 0000000..6a25f15 --- /dev/null +++ b/src/asmops.h @@ -0,0 +1,81 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef ASMOPS_H_ +#define ASMOPS_H_ + +#include + +#define enable_intr() asm volatile("sti") +#define disable_intr() asm volatile("cli") +#define halt_cpu() asm volatile("hlt") + +#define CALLER_EIP(eip) \ + asm volatile( \ + "mov (%%esp), %0\n\t" \ + : "=a" ((uint32_t)eip)) + +static inline uint8_t inp(uint16_t port) +{ + uint8_t res; + asm volatile ( + "inb %1, %0\n\t" + : "=a" (res) + : "dN" (port)); + return res; +} + +static inline uint16_t inpw(uint16_t port) +{ + uint16_t res; + asm volatile ( + "inw %1, %0\n\t" + : "=a" (res) + : "dN" (port)); + return res; +} + +static inline uint32_t inpd(uint16_t port) +{ + uint32_t res; + asm volatile ( + "inl %1, %0\n\t" + : "=a" (res) + : "dN" (port)); + return res; +} + +#define outp(port, src) \ + asm volatile( \ + "outb %0, %1\n\t" \ + :: "a" ((uint8_t)(src)), "dN" ((uint16_t)(port))) + +#define outpw(port, src) \ + asm volatile( \ + "outw %0, %1\n\t" \ + :: "a" ((uint16_t)(src)), "dN" ((uint16_t)(port))) + +#define outpd(port, src) \ + asm volatile( \ + "outl %0, %1\n\t" \ + :: "a" ((uint32_t)(src)), "dN" ((uint16_t)(port))) + +/* delay for about 1us */ +#define iodelay() outp(0x80, 0) + + +#endif /* ASMOPS_H_ */ diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..c83bb90 --- /dev/null +++ b/src/config.h @@ -0,0 +1,27 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2023 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef PCBOOT_CONFIG_H_ +#define PCBOOT_CONFIG_H_ + +/* frequency of generated timer ticks in hertz */ +#define TICK_FREQ_HZ 250 + +#define CON_TEXTMODE +#define CON_SERIAL + +#endif /* PCBOOT_CONFIG_H_ */ diff --git a/src/contty.c b/src/contty.c new file mode 100644 index 0000000..ce1115c --- /dev/null +++ b/src/contty.c @@ -0,0 +1,286 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include "contty.h" +#include "serial.h" +#include "asmops.h" +#include "config.h" + +#define VIRT_ROWS 200 + +#define NCOLS 80 +#define NROWS 25 +#define TEXT_ADDR ((char*)0xb8000) + +#define CRTC_ADDR 0x3d4 +#define CRTC_DATA 0x3d5 + +#define CRTC_REG_CURSTART 0x0a +#define CRTC_REG_CUREND 0x0b +#define CRTC_REG_START_H 0x0c +#define CRTC_REG_START_L 0x0d +#define CRTC_REG_CURLOC_H 0x0e +#define CRTC_REG_CURLOC_L 0x0f + +#define VMEM_CHAR(c, attr) \ + (((uint16_t)(c) & 0xff) | ((uint16_t)(attr) << 8)) + +static void scroll(void); +static void crtc_cursor(int x, int y); +static void crtc_setstart(int y); +static inline unsigned char crtc_read(int reg); +static inline void crtc_write(int reg, unsigned char val); + +extern int cursor_x, cursor_y; +static unsigned char txattr = 0x07; +static int start_line; +static unsigned char cy0, cy1; +static int curvis; +static int scr_on = 1; + +int con_init(void) +{ +#ifdef CON_SERIAL + ser_open(1, 9600, SER_8N1); +#endif + +#ifdef CON_TEXTMODE + cy0 = crtc_read(CRTC_REG_CURSTART); + curvis = cy0 & 0x20 ? 1 : 0; + cy0 &= 0x1f; + cy1 = crtc_read(CRTC_REG_CUREND) & 0x1f; + + con_show_cursor(1); + crtc_setstart(0); + crtc_cursor(cursor_x, cursor_y); + scr_on = 1; +#endif + + return 0; +} + +void con_scr_enable(void) +{ + scr_on = 1; +} + +void con_scr_disable(void) +{ + scr_on = 0; +} + +void con_show_cursor(int show) +{ +#ifdef CON_TEXTMODE + unsigned char val = cy0 & 0x1f; + if(!show) { + val |= 0x20; + } + crtc_write(CRTC_REG_CURSTART, val); + curvis = show; +#endif +} + +void con_cursor(int x, int y) +{ +#ifdef CON_TEXTMODE + cursor_x = x; + cursor_y = y; + crtc_cursor(x, y); +#endif +} + +void con_curattr(int shape, int blink) +{ +#ifdef CON_TEXTMODE + unsigned char start; + cy0 = (shape == CON_CURSOR_LINE) ? 0xd : 0; + cy1 = 0xe; + + start = cy0; + if(curvis) { + start |= 0x20; + } + + crtc_write(CRTC_REG_CURSTART, start); + crtc_write(CRTC_REG_CUREND, cy0); +#endif +} + +void con_fgcolor(int c) +{ + txattr = (txattr & 0xf0) | c; +} + +void con_bgcolor(int c) +{ + txattr = (txattr & 0x0f) | (c << 4); +} + +void con_setattr(unsigned char attr) +{ + txattr = attr; +} + +unsigned char con_getattr(void) +{ + return txattr; +} + +void con_clear(void) +{ +#ifdef CON_TEXTMODE + memset16(TEXT_ADDR, VMEM_CHAR(' ', txattr), NCOLS * NROWS); + + start_line = 0; + crtc_setstart(0); + + cursor_x = cursor_y = 0; + crtc_cursor(0, 0); +#endif +} + +static inline void linefeed(void) +{ + if(++cursor_y >= NROWS) { + scroll(); + --cursor_y; + } +} + +void con_putchar(int c) +{ +#ifdef CON_TEXTMODE + if(scr_on) { + switch(c) { + case '\n': + linefeed(); + case '\r': + cursor_x = 0; + crtc_cursor(cursor_x, cursor_y); + break; + + case '\t': + cursor_x = (cursor_x & 0x7) + 8; + if(cursor_x >= NCOLS) { + linefeed(); + cursor_x = 0; + } + crtc_cursor(cursor_x, cursor_y); + break; + + case '\b': + if(cursor_x > 0) cursor_x--; + con_putchar_scr(cursor_x, cursor_y, ' '); + crtc_cursor(cursor_x, cursor_y); + break; + + default: + con_putchar_scr(cursor_x, cursor_y, c); + + if(++cursor_x >= NCOLS) { + linefeed(); + cursor_x = 0; + } + crtc_cursor(cursor_x, cursor_y); + } + } +#endif + +#ifdef CON_SERIAL + ser_putchar(c); +#endif +} + +void con_putchar_scr(int x, int y, int c) +{ +#ifdef CON_TEXTMODE + uint16_t *ptr = (uint16_t*)TEXT_ADDR; + ptr[(y + start_line) * NCOLS + x] = VMEM_CHAR(c, txattr); +#endif +} + +int con_printf(int x, int y, const char *fmt, ...) +{ +#ifdef CON_TEXTMODE + va_list ap; + char buf[81]; + char *ptr = buf; + + va_start(ap, fmt); + vsnprintf(buf, 80, fmt, ap); + va_end(ap); + + while(*ptr && x < 80) { + con_putchar_scr(x++, y, *ptr++); + } + return ptr - buf; +#else + return 0; +#endif +} + +static void scroll(void) +{ + int new_line; + + if(++start_line > VIRT_ROWS - NROWS) { + /* The bottom of the visible range reached the end of our text buffer. + * Copy the rest of the lines to the top and reset start_line. + */ + memcpy(TEXT_ADDR, TEXT_ADDR + start_line * NCOLS, (NROWS - 1) * NCOLS * 2); + start_line = 0; + } + + /* clear the next line that will be revealed by scrolling */ + new_line = start_line + NROWS - 1; + memset16(TEXT_ADDR + new_line * NCOLS * 2, VMEM_CHAR(' ', txattr), NCOLS); + crtc_setstart(start_line); +} + +static void crtc_cursor(int x, int y) +{ + unsigned int addr; + + addr = (y + start_line) * NCOLS + x; + + crtc_write(CRTC_REG_CURLOC_L, addr); + crtc_write(CRTC_REG_CURLOC_H, addr >> 8); +} + +static void crtc_setstart(int y) +{ + unsigned int addr = y * NCOLS; + + crtc_write(CRTC_REG_START_L, addr); + crtc_write(CRTC_REG_START_H, addr >> 8); +} + +static inline unsigned char crtc_read(int reg) +{ + outp(CRTC_ADDR, reg); + return inp(CRTC_DATA); +} + +static inline void crtc_write(int reg, unsigned char val) +{ + outp(CRTC_ADDR, reg); + outp(CRTC_DATA, val); +} diff --git a/src/contty.h b/src/contty.h new file mode 100644 index 0000000..fcc6e37 --- /dev/null +++ b/src/contty.h @@ -0,0 +1,87 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef CONTTY_H_ +#define CONTTY_H_ + +enum { + CON_CURSOR_LINE, + CON_CURSOR_BLOCK +}; + +enum { + BLACK, + BLUE, + GREEN, + CYAN, + RED, + MAGENTA, + BROWN, + LTGREY, + GREY, + LTBLUE, + LTGREEN, + LTCYAN, + LTRED, + LTMAGENTA, + YELLOW, + WHITE +}; +#define BRIGHT 8 +#define FG_BRIGHT 0x08 +#define BG_BRIGHT 0x80 + +enum { + G_DIAMOND = 0x04, + G_CHECKER = 0xb1, + G_LR_CORNER = 0xd9, + G_UR_CORNER = 0xbf, + G_UL_CORNER = 0xda, + G_LL_CORNER = 0xc0, + G_CROSS = 0xc5, + G_HLINE = 0xc4, + G_L_TEE = 0xc3, + G_R_TEE = 0xb4, + G_B_TEE = 0xc1, + G_T_TEE = 0xc2, + G_VLINE = 0xb3, + G_CDOT = 0xf8, + + G_HDBL = 0xcd, + G_UL_HDBL = 0xd5, + G_UR_HDBL = 0xb8, + G_T_HDBL_TEE = 0xd1 +}; + + +int con_init(void); +void con_scr_enable(void); +void con_scr_disable(void); +void con_show_cursor(int show); +void con_cursor(int x, int y); +void con_curattr(int shape, int blink); +void con_fgcolor(int c); +void con_bgcolor(int c); +void con_setattr(unsigned char attr); +unsigned char con_getattr(void); +void con_clear(void); +void con_putchar(int c); + +void con_putchar_scr(int x, int y, int c); +int con_printf(int x, int y, const char *fmt, ...); + +#endif /* CONTTY_H_ */ diff --git a/src/desc.h b/src/desc.h new file mode 100644 index 0000000..d581f94 --- /dev/null +++ b/src/desc.h @@ -0,0 +1,27 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef DESC_H_ +#define DESC_H_ + +#include + +typedef struct { + uint16_t d[4]; +} desc_t; + +#endif /* DESC_H_ */ diff --git a/src/intr.c b/src/intr.c new file mode 100644 index 0000000..5a351c8 --- /dev/null +++ b/src/intr.c @@ -0,0 +1,277 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include "config.h" +#include "intr.h" +#include "desc.h" +#include "segm.h" +#include "asmops.h" +#include "panic.h" + +#define SYSCALL_INT 0x80 + +/* IDT gate descriptor bits */ +#define GATE_TASK (5 << 8) +#define GATE_INTR (6 << 8) +#define GATE_TRAP (7 << 8) +#define GATE_DEFAULT (1 << 11) +#define GATE_PRESENT (1 << 15) + +/* PIC command and data ports */ +#define PIC1_CMD 0x20 +#define PIC1_DATA 0x21 +#define PIC2_CMD 0xa0 +#define PIC2_DATA 0xa1 + +/* PIC initialization command word 1 bits */ +#define ICW1_ICW4_NEEDED (1 << 0) +#define ICW1_SINGLE (1 << 1) +#define ICW1_INTERVAL4 (1 << 2) +#define ICW1_LEVEL (1 << 3) +#define ICW1_INIT (1 << 4) +/* PIC initialization command word 4 bits */ +#define ICW4_8086 (1 << 0) +#define ICW4_AUTO_EOI (1 << 1) +#define ICW4_BUF_SLAVE (1 << 3) /* 1000 */ +#define ICW4_BUF_MASTER (3 << 2) /* 1100 */ +#define ICW4_SPECIAL (1 << 4) + +/* PIC operation command word 2 bits */ +#define OCW2_EOI (1 << 5) + + +void init_pic(void); +static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type); + +/* defined in intr_asm.S */ +void set_idt(uint32_t addr, uint16_t limit); +void intr_entry_default(void); +void irq7_entry_check_spurious(void); +void irq15_entry_check_spurious(void); + +/* the IDT (interrupt descriptor table) */ +static desc_t idt[256] __attribute__((aligned(8))); + +/* table of handler functions for all interrupts */ +static intr_func_t intr_func[256]; + +static struct intr_frame *cur_intr_frame; +static int eoi_pending; + +#define INTR_ENTRY_EC(n, name) \ + void intr_entry_##name(void); \ + set_intr_entry(n, intr_entry_##name); +#define INTR_ENTRY_NOEC(n, name) INTR_ENTRY_EC(n, name) + +void init_intr(void) +{ + int i; + + set_idt((uint32_t)idt, sizeof idt - 1); + + /* initialize all entry points and interrupt handlers */ + for(i=0; i<256; i++) { + set_intr_entry(i, intr_entry_default); + interrupt(i, 0); + } + + /* by including intrtab.h here the series of INTR_ENTRY_* macros will be + * expanded to a series of function prototypes for all interrupt entry + * points and the corresponding calls to set_intr_entry to set up the IDT + * slots + */ +#include "intrtab.h" + + /* change irq7 and irq15 to special entry points which first + * make sure we didn't get a spurious interrupt before proceeding + */ + set_intr_entry(IRQ_TO_INTR(7), irq7_entry_check_spurious); + set_intr_entry(IRQ_TO_INTR(15), irq15_entry_check_spurious); + + /* initialize the programmable interrupt controller + * setting up the maping of IRQs [0, 15] to interrupts [32, 47] + */ + init_pic(); + eoi_pending = 0; +} + +/* retrieve the current interrupt frame. + * returns 0 when called during init. + */ +struct intr_frame *get_intr_frame(void) +{ + return cur_intr_frame; +} + +/* set an interrupt handler function for a particular interrupt */ +void interrupt(int intr_num, intr_func_t func) +{ + int iflag = get_intr_flag(); + disable_intr(); + intr_func[intr_num] = func; + set_intr_flag(iflag); +} + +/* this function is called from all interrupt entry points + * it calls the appropriate interrupt handlers if available and handles + * sending an end-of-interrupt command to the PICs when finished. + */ +void dispatch_intr(struct intr_frame frm) +{ + cur_intr_frame = &frm; + + if(IS_IRQ(frm.inum)) { + eoi_pending = frm.inum; + } + + if(intr_func[frm.inum]) { + intr_func[frm.inum](frm.inum); + } else { + if(frm.inum < 32) { + panic("unhandled exception %d, error code: %d\n", frm.inum, frm.err); + } + printf("unhandled interrupt %d\n", frm.inum); + } + + disable_intr(); + if(eoi_pending) { + end_of_irq(INTR_TO_IRQ(eoi_pending)); + } +} + +void init_pic(void) +{ + prog_pic(IRQ_OFFSET); +} + +void prog_pic(int offs) +{ + /* send ICW1 saying we'll follow with ICW4 later on */ + outp(PIC1_CMD, ICW1_INIT | ICW1_ICW4_NEEDED); + outp(PIC2_CMD, ICW1_INIT | ICW1_ICW4_NEEDED); + /* send ICW2 with IRQ remapping */ + outp(PIC1_DATA, offs); + outp(PIC2_DATA, offs + 8); + /* send ICW3 to setup the master/slave relationship */ + /* ... set bit3 = 3rd interrupt input has a slave */ + outp(PIC1_DATA, 4); + /* ... set slave ID to 2 */ + outp(PIC2_DATA, 2); + /* send ICW4 to set 8086 mode (no calls generated) */ + outp(PIC1_DATA, ICW4_8086); + outp(PIC2_DATA, ICW4_8086); + /* done, just reset the data port to 0 */ + outp(PIC1_DATA, 0); + outp(PIC2_DATA, 0); +} + +static void gate_desc(desc_t *desc, uint16_t sel, uint32_t addr, int dpl, int type) +{ + /* first 16bit part is the low 16bits of the entry address */ + desc->d[0] = addr & 0xffff; + /* second 16bit part is the segment selector for the entry code */ + desc->d[1] = sel; + /* third 16bit part has the privilege level, type, and present bit */ + desc->d[2] = ((dpl & 3) << 13) | type | GATE_DEFAULT | GATE_PRESENT; + /* last 16bit part is the high 16bits of the entry address */ + desc->d[3] = (addr & 0xffff0000) >> 16; +} + +#define IS_TRAP(n) ((n) >= 32 && !IS_IRQ(n)) +void set_intr_entry(int num, void (*handler)(void)) +{ + int type = IS_TRAP(num) ? GATE_TRAP : GATE_INTR; + + /* the syscall interrupt has to have a dpl of 3 otherwise calling it from + * user space will raise a general protection exception. All the rest should + * have a dpl of 0 to disallow user programs to execute critical interrupt + * handlers and possibly crashing the system. + */ + int dpl = (num == SYSCALL_INT) ? 3 : 0; + + gate_desc(idt + num, selector(SEGM_KCODE, 0), (uint32_t)handler, dpl, type); +} + +void set_pic_mask(int pic, unsigned char mask) +{ + outp(pic > 0 ? PIC2_DATA : PIC1_DATA, mask); +} + +unsigned char get_pic_mask(int pic) +{ + return inp(pic > 0 ? PIC2_DATA : PIC1_DATA); +} + +void mask_irq(int irq) +{ + int port; + unsigned char mask; + + if(irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + + mask = inp(port) | (1 << irq); + outp(port, mask); +} + +void unmask_irq(int irq) +{ + int port; + unsigned char mask; + + if(irq < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + irq -= 8; + } + + mask = inp(port) & ~(1 << irq); + outp(port, mask); +} + + +void end_of_irq(int irq) +{ + int intr_state = get_intr_flag(); + disable_intr(); + + if(!eoi_pending) { + set_intr_flag(intr_state); + return; + } + eoi_pending = 0; + + if(irq > 7) { + outp(PIC2_CMD, OCW2_EOI); + } + outp(PIC1_CMD, OCW2_EOI); + + set_intr_flag(intr_state); +} + +#ifdef ENABLE_GDB_STUB +void exceptionHandler(int id, void (*func)()) +{ + set_intr_entry(id, func); +} +#endif diff --git a/src/intr.h b/src/intr.h new file mode 100644 index 0000000..f33ec23 --- /dev/null +++ b/src/intr.h @@ -0,0 +1,89 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef INTR_H_ +#define INTR_H_ + +#include +#include "asmops.h" + +/* offset used to remap IRQ numbers (+32) */ +#define IRQ_OFFSET 32 +/* conversion macros between IRQ and interrupt numbers */ +#define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET) +#define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET) +/* checks whether a particular interrupt is an remapped IRQ */ +#define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16) + +/* general purpose registers as they are pushed by pusha */ +struct registers { + uint32_t edi, esi, ebp, esp; + uint32_t ebx, edx, ecx, eax; +} __attribute__ ((packed)); + +/* structure used to pass the interrupt stack frame from the + * entry points to the C dispatch function. + */ +struct intr_frame { + /* registers pushed by pusha in intr_entry_* */ + struct registers regs; + /* data segment selectors */ + /* XXX removed: not needed unless we do dpl3 transitions + uint32_t ds, es, fs, gs; + */ + /* interrupt number and error code pushed in intr_entry_* */ + uint32_t inum, err; + /* pushed by CPU during interrupt entry */ + uint32_t eip, cs, eflags; + /* pushed by CPU during interrupt entry from user space */ + /* XXX removed: again, not doing user space currently + uint32_t esp, ss; + */ +} __attribute__ ((packed)); + + + +typedef void (*intr_func_t)(int); + + +void init_intr(void); + +struct intr_frame *get_intr_frame(void); + +/* install high level interrupt callback */ +void interrupt(int intr_num, intr_func_t func); + +/* install low-level interrupt vector in IDT + * must be able to handle EOI and return with iret + */ +void set_intr_entry(int num, void (*handler)(void)); + +void prog_pic(int offs); +void set_pic_mask(int pic, unsigned char mask); +unsigned char get_pic_mask(int pic); +void mask_irq(int irq); +void unmask_irq(int irq); + +/* defined in intr_asm.S */ +int get_intr_flag(void); +void set_intr_flag(int onoff); + +void intr_ret(struct intr_frame ifrm); + +void end_of_irq(int irq); + +#endif /* INTR_H_ */ diff --git a/src/intr_s.asm b/src/intr_s.asm new file mode 100644 index 0000000..cdae52a --- /dev/null +++ b/src/intr_s.asm @@ -0,0 +1,161 @@ +; pcboot - bootable PC demo/game kernel +; Copyright (C) 2018-2023 John Tsiombikas +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY, without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + section .data + align 4 + dw 0 +idtr_desc: +lim: dw 0 +addr: dd 0 + + section .text +; void set_idt(uint32_t addr, uint16_t limit) + global set_idt +set_idt: + mov eax, [esp + 4] + mov [addr], eax + mov ax, [esp + 8] + mov [lim], ax + lidt [idtr_desc] + ret + +; int get_intr_flag() + global get_intr_flag +get_intr_flag: + pushf + pop eax + ; bit 9 of eflags is IF + shr eax, 9 + and eax, 1 + ret + +; void set_intr_flag(int onoff) + global set_intr_flag +set_intr_flag: + cmp dword [esp + 4], 0 + jz .off + sti + ret +.off: cli + ret + +; interrupt entry with error code macro +; this macro generates an interrupt entry point for the +; exceptions which include error codes in the stack frame +%macro ientry_err 2 + global intr_entry_%2 +intr_entry_%2: + push dword %1 + jmp intr_entry_common +%endmacro + +; interrupt entry without error code macro +; this macro generates an interrupt entry point for the interrupts +; and exceptions which do not include error codes in the stack frame +; it pushes a dummy error code (0), to make the stack frame identical +%macro ientry_noerr 2 + global intr_entry_%2 +intr_entry_%2: + push dword 0 + push dword %1 + jmp intr_entry_common +%endmacro + +; common code used by all entry points. calls dispatch_intr() +; defined in intr.c + extern dispatch_intr +intr_entry_common: + ; save general purpose registers + pusha + call dispatch_intr +intr_ret_local: + ; restore general purpose registers + popa + ; remove error code and intr num from stack + add esp, 8 + iret + +; special case for the timer interrupt, to avoid all the overhead of +; going through the C interrupt dispatcher 250 times each second + extern nticks + global intr_entry_fast_timer +intr_entry_fast_timer: + inc dword [nticks] + ; signal end of interrupt + push eax + mov al, 20h + out 20h, al + pop eax + iret + +; special case for IRQ 7 and IRQ 15, to catch spurious interrupts +PIC1_CMD equ 0x20 +PIC2_CMD equ 0xa0 +OCW2_EOI equ 0x20 +OCW3_ISR equ 0x0b + + global irq7_entry_check_spurious +irq7_entry_check_spurious: + push eax + mov al, OCW3_ISR + out PIC1_CMD, al + in al, PIC1_CMD + and al, 80h + pop eax + jnz intr_entry_irq7 + iret + + global irq15_entry_check_spurious +irq15_entry_check_spurious: + push eax + mov al, OCW3_ISR + out PIC2_CMD, al + in al, PIC2_CMD + and al, 80h + jnz .legit + ; it was spurious, send EOI to master PIC and iret + mov al, OCW2_EOI + out PIC1_CMD, al + pop eax + iret +.legit: pop eax + jmp intr_entry_irq15 + + +; XXX not necessary for now, just leaving it in in case it's useful +; down the road. +; +; intr_ret is called by context_switch to return from the kernel +; to userspace. The argument is a properly formed intr_frame +; structure with the saved context of the new task. +; +; First thing to do is remove the return address pointing back +; to context_switch, which then leaves us with a proper interrupt +; stack frame, so we can jump right in the middle of the regular +; interrupt return code above. + global intr_ret +intr_ret: + add esp, $4 + jmp intr_ret_local + +; by including interrupts.h with ASM defined, the macros above +; are expanded to generate all required interrupt entry points +%define INTR_ENTRY_EC(n, name) ientry_err n, name +%define INTR_ENTRY_NOEC(n, name) ientry_noerr n, name + +%include 'intrtab.h' + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/intrtab.h b/src/intrtab.h new file mode 100644 index 0000000..34af6fa --- /dev/null +++ b/src/intrtab.h @@ -0,0 +1,38 @@ +INTR_ENTRY_NOEC(0, div) +INTR_ENTRY_NOEC(1, debug) +INTR_ENTRY_NOEC(2, nmi) +INTR_ENTRY_NOEC(3, bp) +INTR_ENTRY_NOEC(4, overflow) +INTR_ENTRY_NOEC(5, bound) +INTR_ENTRY_NOEC(6, ill) +INTR_ENTRY_NOEC(7, nodev) +INTR_ENTRY_EC(8, dfault) +INTR_ENTRY_NOEC(9, copseg) +INTR_ENTRY_EC(10, tss) +INTR_ENTRY_EC(11, segpres) +INTR_ENTRY_EC(12, stack) +INTR_ENTRY_EC(13, prot) +INTR_ENTRY_EC(14, page) +INTR_ENTRY_NOEC(15, reserved) +INTR_ENTRY_NOEC(16, fpu) +INTR_ENTRY_EC(17, align) +INTR_ENTRY_NOEC(18, mce) +INTR_ENTRY_NOEC(19, sse) +INTR_ENTRY_NOEC(32, irq0) +INTR_ENTRY_NOEC(33, irq1) +INTR_ENTRY_NOEC(34, irq2) +INTR_ENTRY_NOEC(35, irq3) +INTR_ENTRY_NOEC(36, irq4) +INTR_ENTRY_NOEC(37, irq5) +INTR_ENTRY_NOEC(38, irq6) +INTR_ENTRY_NOEC(39, irq7) +INTR_ENTRY_NOEC(40, irq8) +INTR_ENTRY_NOEC(41, irq9) +INTR_ENTRY_NOEC(42, irq10) +INTR_ENTRY_NOEC(43, irq11) +INTR_ENTRY_NOEC(44, irq12) +INTR_ENTRY_NOEC(45, irq13) +INTR_ENTRY_NOEC(46, irq14) +INTR_ENTRY_NOEC(47, irq15) +INTR_ENTRY_NOEC(128, syscall) +INTR_ENTRY_NOEC(255, default) diff --git a/src/libc/alloca.h b/src/libc/alloca.h new file mode 100644 index 0000000..ae27a71 --- /dev/null +++ b/src/libc/alloca.h @@ -0,0 +1,23 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef ALLOCA_H_ +#define ALLOCA_H_ + +#define alloca(x) __builtin_alloca(x) + +#endif /* ALLOCA_H_ */ diff --git a/src/libc/assert.h b/src/libc/assert.h new file mode 100644 index 0000000..7180c97 --- /dev/null +++ b/src/libc/assert.h @@ -0,0 +1,28 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef ASSERT_H_ +#define ASSERT_H_ + +#include "panic.h" + +#define assert(x) \ + if(!(x)) { \ + panic("Kernel assertion failed at " __FILE__ ":%d: " #x "\n", __LINE__); \ + } + +#endif /* ASSERT_H_ */ diff --git a/src/libc/ctype.c b/src/libc/ctype.c new file mode 100644 index 0000000..89e7b93 --- /dev/null +++ b/src/libc/ctype.c @@ -0,0 +1,73 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "ctype.h" + +int isalnum(int c) +{ + return isalpha(c) || isdigit(c); +} + +int isalpha(int c) +{ + return isupper(c) || islower(c); +} + +int isblank(int c) +{ + return c == ' ' || c == '\t'; +} + +int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +int isupper(int c) +{ + return c >= 'A' && c <= 'Z'; +} + +int islower(int c) +{ + return c >= 'a' && c <= 'z'; +} + +int isgraph(int c) +{ + return c > ' ' && c <= '~'; +} + +int isprint(int c) +{ + return isgraph(c) || c == ' '; +} + +int isspace(int c) +{ + return isblank(c) || c == '\f' || c == '\n' || c == '\r' || c == '\v'; +} + +int toupper(int c) +{ + return islower(c) ? (c + ('A' - 'a')) : c; +} + +int tolower(int c) +{ + return isupper(c) ? (c - ('A' - 'a')) : c; +} diff --git a/src/libc/ctype.h b/src/libc/ctype.h new file mode 100644 index 0000000..79595c8 --- /dev/null +++ b/src/libc/ctype.h @@ -0,0 +1,34 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef CTYPE_H_ +#define CTYPE_H_ + +int isalnum(int c); +int isalpha(int c); +#define isascii(c) ((c) < 128) +int isblank(int c); +int isdigit(int c); +int isupper(int c); +int islower(int c); +int isprint(int c); +int isspace(int c); + +int toupper(int c); +int tolower(int c); + +#endif /* CTYPE_H_ */ diff --git a/src/libc/errno.h b/src/libc/errno.h new file mode 100644 index 0000000..8c152de --- /dev/null +++ b/src/libc/errno.h @@ -0,0 +1,41 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef ERRNO_H_ +#define ERRNO_H_ + +#define EFOO 1 +#define EAGAIN 2 +#define EINVAL 3 +#define ECHILD 4 +#define EBUSY 5 +#define ENOMEM 6 +#define EIO 7 +#define ENOENT 8 +#define ENAMETOOLONG 9 +#define ENOSPC 10 +#define EPERM 11 +#define ENOTDIR 12 +#define EISDIR 13 +#define EEXIST 14 +#define ERANGE 34 + +#define EBUG 127 /* for missing features and known bugs */ + +extern int errno; + +#endif /* ERRNO_H_ */ diff --git a/src/libc/float.h b/src/libc/float.h new file mode 100644 index 0000000..073bf6d --- /dev/null +++ b/src/libc/float.h @@ -0,0 +1,24 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef FLOAT_H_ +#define FLOAT_H_ + +#define FLT_MIN __FLT_MIN__ +#define FLT_MAX __FLT_MAX__ + +#endif /* FLOAT_H_ */ diff --git a/src/libc/inttypes.h b/src/libc/inttypes.h new file mode 100644 index 0000000..39d948a --- /dev/null +++ b/src/libc/inttypes.h @@ -0,0 +1,31 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef INTTYPES_H_ +#define INTTYPES_H_ + +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif /* INTTYPES_H_ */ diff --git a/src/libc/limits.h b/src/libc/limits.h new file mode 100644 index 0000000..b3abdad --- /dev/null +++ b/src/libc/limits.h @@ -0,0 +1,36 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef LIMITS_H_ +#define LIMITS_H_ + +#define CHAR_BIT 8 + +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 +#define INT_MIN (-2147483648) +#define INT_MAX 2147483647 +#define LONG_MIN (-2147483648) +#define LONG_MAX 2147483647 + +#define USHRT_MAX 65535 +#define UINT_MAX 0xffffffff +#define ULONG_MAX 0xffffffff + +#define PATH_MAX 256 + +#endif /* LIMITS_H_ */ diff --git a/src/libc/malloc.c b/src/libc/malloc.c new file mode 100644 index 0000000..c064915 --- /dev/null +++ b/src/libc/malloc.c @@ -0,0 +1,429 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#include "mem.h" +#include "panic.h" + +#define SINGLE_POOL + +struct mem_desc { + size_t size; + uint32_t magic; +#ifdef MALLOC_DEBUG + uint32_t dbg; +#endif + struct mem_desc *next; +}; + +#ifdef MALLOC_DEBUG +static void check_cycles(struct mem_desc *mem); +static void print_pool(void); +#endif + +#define MAGIC_USED 0xdeadf00d +#define MAGIC_FREE 0x1ee7d00d + +#define DESC_PTR(b) ((void*)((struct mem_desc*)(b) + 1)) +#define PTR_DESC(p) ((struct mem_desc*)(p) - 1) + +#ifdef SINGLE_POOL +static struct mem_desc *pool; +#else + +#define NUM_POOLS 16 +#define FIRST_POOL_POW2 5 +/* 2**(x+5) size pools: 0->32, 1->64, 2->128 .. 15->1048576 */ +#define POOL_SIZE(x) (1 << ((x) + FIRST_POOL_POW2)) +#define MAX_POOL_SIZE (1 << ((NUM_POOLS - 1) + FIRST_POOL_POW2)) +#define MAX_POOL_PAGES BYTES_TO_PAGES(MAX_POOL_SIZE) +static struct mem_desc *pools[NUM_POOLS]; + +static int add_to_pool(struct mem_desc *b); + +static int pool_index(int sz) +{ + int x = 0; + --sz; + while(sz) { + sz >>= 1; + ++x; + } + return x - FIRST_POOL_POW2; +} +#endif /* !SINGLE_POOL */ + + +#ifdef SINGLE_POOL +#define MIN_BLOCK_SIZE (sizeof(struct mem_desc) * 2) + +void *malloc(size_t sz) +{ + int pg0, npages; + size_t total_sz, rest_sz; + struct mem_desc *mem, *rest, *prev, dummy; + int found = 0; + + total_sz = (sz + sizeof(struct mem_desc) + 3) & 0xfffffffc; + + dummy.next = pool; + prev = &dummy; + while(prev->next) { + mem = prev->next; + /* give the whole block to the allocation if mem->size == total_sz or + * if it's larger, but not large enough to fit another mem_desc in there + * for the new block that we're going to split off + some reasonable + * amount of memory for the new block. + */ + if(mem->size >= total_sz && mem->size < total_sz + MIN_BLOCK_SIZE) { + prev->next = mem->next; + found = 1; + break; + } + /* if we have enough space, split the block and give the upper part + * to the allocation + */ + if(mem->size > total_sz) { + void *ptr = (char*)mem + mem->size - total_sz; + mem->size -= total_sz; + mem = ptr; + found = 1; + break; + } + prev = prev->next; + } + pool = dummy.next; + + if(found) { + mem->size = total_sz; + mem->magic = MAGIC_USED; + mem->next = 0; + return DESC_PTR(mem); + } + + /* did not find a free block, grab a new one */ + npages = BYTES_TO_PAGES(total_sz); + if((pg0 = alloc_ppages(npages, 0)) == -1) { + errno = ENOMEM; + return 0; + } + mem = PAGE_TO_PTR(pg0); + mem->size = total_sz; + mem->next = 0; + mem->magic = MAGIC_USED; + + /* add the rest of the block to pool */ + rest_sz = npages * 4096 - total_sz; + if(rest_sz > 0) { + rest = (struct mem_desc*)((char*)mem + total_sz); + rest->size = rest_sz; + rest->next = 0; + rest->magic = MAGIC_USED; + free(DESC_PTR(rest)); + } + + return DESC_PTR(mem); +} + +void free(void *p) +{ + struct mem_desc *mem, *prev, *cur, dummy; + char *end; + + if(!p) return; + mem = PTR_DESC(p); + + if(mem->magic != MAGIC_USED) { + if(mem->magic == MAGIC_FREE) { + panic("free(%p): double-free\n", p); + } else { + panic("free(%p): corrupted magic (%x)!\n", p, mem->magic); + } + } + mem->magic = MAGIC_FREE; + mem->next = 0; + + /* nothing in the pool, just add this one */ + if(!pool) { + pool = mem; + return; + } + + end = (char*)mem + mem->size; + + dummy.next = pool; + prev = &dummy; + + while(prev->next) { + cur = prev->next; + + /* block starts right at the end of mem: coalesce */ + if((char*)cur == end) { + mem->size += cur->size; + mem->next = cur->next; + cur->magic = 0; + prev->next = mem; + goto done; + } + + /* block starts *after* the end of mem: add in front */ + if((char*)cur > end) { + mem->next = cur; + prev->next = mem; + goto done; + } + + prev = prev->next; + } + + /* our block starts at the end of the last block in the pool: coalesce */ + if((char*)prev + prev->size == (char*)mem) { + mem->magic = 0; + prev->size += mem->size; + goto done; + } + + /* so our block starts after the end of the last block: append */ + prev->next = mem; + +done: + pool = dummy.next; + +#ifdef MALLOC_DEBUG + print_pool(); +#endif +} + +#else /* !SINGLE_POOL */ + +void *malloc(size_t sz) +{ + int pidx, pg0; + size_t total_sz, halfsz; + struct mem_desc *mem, *other; + + total_sz = sz + sizeof(struct mem_desc); + + if(total_sz > MAX_POOL_SIZE) { + /*printf(" allocation too big, hitting sys_alloc directly\n");*/ + if((pg0 = alloc_ppages(BYTES_TO_PAGES(total_sz), 0)) == -1) { + errno = ENOMEM; + return 0; + } + mem = PAGE_TO_PTR(pg0); + mem->magic = MAGIC_USED; + mem->size = total_sz; + mem->next = 0; + return DESC_PTR(mem); + } + + pidx = pool_index(total_sz); + while(pidx < NUM_POOLS) { + if(pools[pidx]) { + /* found a pool with a free block that fits */ + mem = pools[pidx]; + pools[pidx] = mem->next; + mem->next = 0; + + /*printf(" using pool %d (size %ld)\n", pidx, (unsigned long)mem->size);*/ + + /* while the mem size is larger than twice the allocation, split it */ + while(pidx-- > 0 && total_sz <= (halfsz = mem->size / 2)) { + /*printf(" splitting %ld block in half and ", (unsigned long)mem->size);*/ + other = (struct mem_desc*)((char*)mem + halfsz); + mem->size = other->size = halfsz; + + add_to_pool(other); + } + + if(mem->magic != MAGIC_FREE) { + panic("Trying to allocate range surrounded by an aura of wrong MAGIC\n"); + } + mem->magic = MAGIC_USED; + return DESC_PTR(mem); + } + + ++pidx; + } + + /* did not find a free block, add a new one */ + pidx = NUM_POOLS - 1; + if((pg0 = alloc_ppages(MAX_POOL_PAGES, 0)) == -1) { + errno = ENOMEM; + return 0; + } + mem = PAGE_TO_PTR(pg0); + mem->size = MAX_POOL_SIZE; + mem->next = pools[pidx]; + mem->magic = MAGIC_FREE; + pools[pidx] = mem; + + /* try again now that there is a free block */ + return malloc(sz); +} + + +void free(void *p) +{ + int pg0; + struct mem_desc *mem; + + if(!p) return; + + mem = PTR_DESC(p); + if(mem->magic != MAGIC_USED) { + if(mem->magic == MAGIC_FREE) { + panic("free(%p): double-free\n", p); + } else { + panic("free(%p): corrupted magic (%x)!\n", p, mem->magic); + } + } + + if(mem->size > MAX_POOL_SIZE) { + /*printf("foo_free(%p): %ld bytes. block too large for pools, returning to system\n", + (void*)p, (unsigned long)mem->size);*/ + pg0 = ADDR_TO_PAGE(mem); + free_ppages(pg0, BYTES_TO_PAGES(mem->size)); + return; + } + + /*printf("foo_free(%p): block of %ld bytes and ", (void*)p, (unsigned long)mem->size);*/ + add_to_pool(mem); +} + +#endif /* !def SINGLE_POOL */ + + +void *calloc(size_t num, size_t size) +{ + void *ptr = malloc(num * size); + if(ptr) { + memset(ptr, 0, num * size); + } + return ptr; +} + +void *realloc(void *ptr, size_t size) +{ + struct mem_desc *mem; + void *newp; + + if(!ptr) { + return malloc(size); + } + + mem = PTR_DESC(ptr); + if(mem->size >= size) { + return ptr; /* TODO: shrink */ + } + + if(!(newp = malloc(size))) { + return 0; + } + memcpy(newp, ptr, mem->size); + free(ptr); + return newp; +} + +#ifdef MALLOC_DEBUG +static void check_cycles(struct mem_desc *mem) +{ + static uint32_t dbg = 42; + uint32_t cur_dbg = dbg++; + + while(mem) { + if(mem->magic != MAGIC_FREE) { + panic("check_cycles: NON-FREE MAGIC!\n"); + } + if(mem->dbg == cur_dbg) { + panic("CYCLE DETECTED\n"); + } + mem->dbg = cur_dbg; + mem = mem->next; + } +} + +static void print_pool(void) +{ + struct mem_desc *mem = pool; + + printf("DBG: malloc pool:\n"); + while(mem) { + printf(" %p (%ld) [%x]\n", mem, mem->size, mem->magic); + mem = mem->next; + + assert((uint32_t)mem != MAGIC_USED); + } +} +#endif /* MALLOC_DEBUG */ + +#ifndef SINGLE_POOL +static int add_to_pool(struct mem_desc *mem) +{ + int pidx; + struct mem_desc head; + struct mem_desc *iter, *pnode; + + pidx = pool_index(mem->size); + + /*printf("adding %ld block to pool %d\n", (unsigned long)mem->size, pidx);*/ + + iter = &head; + head.next = pools[pidx]; + + while(iter->next) { + pnode = iter->next; + if(mem->size == pnode->size) { /* only coalesce same-sized blocks */ + size_t size = mem->size; + + if((char*)mem == (char*)pnode - size) { + iter->next = pnode->next; /* unlink pnode */ + pools[pidx] = head.next; + mem->next = 0; + mem->size += size; + + /*printf(" coalescing %p with %p and ", (void*)mem, (void*)pnode);*/ + return add_to_pool(mem); + } + if((char*)mem == (char*)pnode + size) { + iter->next = pnode->next; /* unlink pnode */ + pools[pidx] = head.next; + pnode->next = 0; + pnode->size += size; + + /*printf(" coalescing %p with %p and ", (void*)mem, (void*)pnode);*/ + return add_to_pool(pnode); + } + } + iter = iter->next; + } + + /* otherwise just add it to the pool */ + mem->magic = MAGIC_FREE; + mem->next = pools[pidx]; + pools[pidx] = mem; + +#ifdef MALLOC_DEBUG + check_cycles(pools[pidx]); +#endif + return 0; +} +#endif /* !def SINGLE_POOL */ diff --git a/src/libc/math.c b/src/libc/math.c new file mode 100644 index 0000000..3f3e5de --- /dev/null +++ b/src/libc/math.c @@ -0,0 +1,55 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "math.h" + +static double calc_pow(double x, double y, double precision); + +double pow(double x, double y) +{ + if(y == 0.0 || y == -0.0) { + return 1.0; + } + if(y == 1.0) { + return x; + } + if(y == -INFINITY) { + return fabs(x) < 1.0 ? INFINITY : 0.0; + } + if(y == INFINITY) { + return fabs(x) < 1.0 ? 0.0 : INFINITY; + } + return calc_pow(x, y, 1e-6); +} + +static double calc_pow(double x, double y, double precision) +{ + if(y < 0.0) { + return 1.0 / calc_pow(x, -y, precision); + } + if(y >= 10.0) { + double p = calc_pow(x, y / 2.0, precision / 2.0); + return p * p; + } + if(y >= 1.0) { + return x * calc_pow(x, y - 1.0, precision); + } + if(precision >= 1) { + return __builtin_sqrt(x); + } + return __builtin_sqrt(calc_pow(x, y * 2.0, precision * 2.0)); +} diff --git a/src/libc/math.h b/src/libc/math.h new file mode 100644 index 0000000..e3b0f23 --- /dev/null +++ b/src/libc/math.h @@ -0,0 +1,36 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef MATH_H_ +#define MATH_H_ + +#define INFINITY __builtin_inff() +#define NAN __builtin_nanf + +#define M_PI 3.141592653589793 + +#define sin(x) __builtin_sin(x) +#define cos(x) __builtin_cos(x) +#define tan(x) __builtin_tan(x) +#define fabs(x) __builtin_fabs(x) +#define fmod(x, y) __builtin_fmod(x, y) +#define sqrt(x) __builtin_sqrt(x) +#define atan2(y, x) __builtin_atan2(y, x) + +double pow(double x, double y); + +#endif /* MATH_H_ */ diff --git a/src/libc/rand.c b/src/libc/rand.c new file mode 100644 index 0000000..de159c2 --- /dev/null +++ b/src/libc/rand.c @@ -0,0 +1,65 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +/* random number generator, based on this description of the algorithm + * used by the GNU libc: https://www.mathstat.dal.ca/~selinger/random + */ +#include +#include + +static int init_done; +static int32_t rng[34]; +static int32_t *ptr0, *ptr1; + +int rand(void) +{ + int res; + + if(!init_done) { + srand(1); + } + + *ptr1 += *ptr0; + res = (uint32_t)*ptr1 >> 1; + if(++ptr0 >= rng + 34) ptr0 = rng; + if(++ptr1 >= rng + 34) ptr1 = rng; + + return res; +} + +void srand(unsigned int seed) +{ + int i; + + init_done = 1; + if(seed == 0) seed = 1; + + rng[0] = seed; + for(i=1; i<31; i++) { + rng[i] = (16807 * rng[i - 1]) % RAND_MAX; + if(rng[i] < 0) rng[i] += RAND_MAX; + } + for(i=31; i<34; i++) { + rng[i] = rng[i - 31]; + } + ptr0 = rng + 3; + ptr1 = rng + 31; + + for(i=34; i<344; i++) { + rand(); + } +} diff --git a/src/libc/setjmp.c b/src/libc/setjmp.c new file mode 100644 index 0000000..517563b --- /dev/null +++ b/src/libc/setjmp.c @@ -0,0 +1,28 @@ +/* +256boss - bootable launcher for 256byte intros +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include + +int setjmp(jmp_buf buf) +{ + return __builtin_setjmp(buf); +} + +void longjmp(jmp_buf buf, int val) +{ + __builtin_longjmp(buf, 1); +} diff --git a/src/libc/setjmp.h b/src/libc/setjmp.h new file mode 100644 index 0000000..c92e93d --- /dev/null +++ b/src/libc/setjmp.h @@ -0,0 +1,26 @@ +/* +256boss - bootable launcher for 256byte intros +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef SETJMP_H_ +#define SETJMP_H_ + +typedef unsigned long jmp_buf[5]; + +int setjmp(jmp_buf buf); +void longjmp(jmp_buf buf, int val); + +#endif /* SETJMP_H_ */ diff --git a/src/libc/stdarg.h b/src/libc/stdarg.h new file mode 100644 index 0000000..2d0bb92 --- /dev/null +++ b/src/libc/stdarg.h @@ -0,0 +1,29 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef STDARG_H_ +#define STDARG_H_ + +/* Assumes that arguments are passed on the stack 4-byte aligned */ + +typedef int* va_list; + +#define va_start(ap, last) ((ap) = (int*)&(last) + 1) +#define va_arg(ap, type) (*(type*)(ap)++) +#define va_end(ap) + +#endif /* STDARG_H_ */ diff --git a/src/libc/stddef.h b/src/libc/stddef.h new file mode 100644 index 0000000..9ec6bda --- /dev/null +++ b/src/libc/stddef.h @@ -0,0 +1,32 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef STDDEF_H_ +#define STDDEF_H_ + +#include + +typedef int32_t ssize_t; +typedef uint32_t size_t; +typedef int wchar_t; + +typedef int32_t ptrdiff_t; +typedef uint32_t intptr_t; + +#define NULL 0 + +#endif /* STDDEF_H_ */ diff --git a/src/libc/stdio.c b/src/libc/stdio.c new file mode 100644 index 0000000..1f12fd6 --- /dev/null +++ b/src/libc/stdio.c @@ -0,0 +1,407 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include "contty.h" +#include "serial.h" +#include "panic.h" + +enum { + OUT_DEF, + OUT_BUF, + OUT_SCR, + OUT_SER +}; + +extern void pcboot_putchar(int c); + +static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list ap); +static int intern_scanf(const char *instr, FILE *infile, const char *fmt, va_list ap); +static void bwrite(int out, char *buf, size_t buf_sz, char *str, int sz); +/*static int readchar(const char *str, FILE *fp);*/ + +int putchar(int c) +{ + con_putchar(c); + return c; +} + +int puts(const char *s) +{ + while(*s) { + putchar(*s++); + } + putchar('\n'); + return 0; +} + +/* -- printf and friends -- */ + +int printf(const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = intern_printf(OUT_DEF, 0, 0, fmt, ap); + va_end(ap); + return res; +} + +int vprintf(const char *fmt, va_list ap) +{ + return intern_printf(OUT_DEF, 0, 0, fmt, ap); +} + +int sprintf(char *buf, const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = intern_printf(OUT_BUF, buf, 0, fmt, ap); + va_end(ap); + return res; +} + +int vsprintf(char *buf, const char *fmt, va_list ap) +{ + return intern_printf(OUT_BUF, buf, 0, fmt, ap); +} + +int snprintf(char *buf, size_t sz, const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = intern_printf(OUT_BUF, buf, sz, fmt, ap); + va_end(ap); + return res; +} + +int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap) +{ + return intern_printf(OUT_BUF, buf, sz, fmt, ap); +} + +int fprintf(FILE *fp, const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = vfprintf(fp, fmt, ap); + va_end(ap); + return res; +} + +int vfprintf(FILE *fp, const char *fmt, va_list ap) +{ + if(fp == stdout || fp == stderr) { + return vprintf(fmt, ap); + } + + panic("*fprintf for anything other than stdout/stderr, not implemented yet\n"); + return 0; +} + +int ser_printf(const char *fmt, ...) +{ + int res; + va_list ap; + + va_start(ap, fmt); + res = intern_printf(OUT_SER, 0, 0, fmt, ap); + va_end(ap); + return res; +} + +int ser_vprintf(const char *fmt, va_list ap) +{ + return intern_printf(OUT_SER, 0, 0, fmt, ap); +} + +void perror(const char *s) +{ + printf("%s: %s\n", s, strerror(errno)); +} + +/* intern_printf provides all the functionality needed by all the printf + * variants. + * - buf: optional buffer onto which the formatted results are written. If null + * then the output goes to the terminal through putchar calls. This is used + * by the (v)sprintf variants which write to an array of char. + * - sz: optional maximum size of the output, 0 means unlimited. This is used + * by the (v)snprintf variants to avoid buffer overflows. + * The rest are obvious, format string and variable argument list. + */ +static char *convc = "dioxXucsfeEgGpn%"; + +#define IS_CONV(c) strchr(convc, c) + +#define BUF(x) ((x) ? (x) + cnum : (x)) +#define SZ(x) ((x) ? (x) - cnum : (x)) + +static int intern_printf(int out, char *buf, size_t sz, const char *fmt, va_list ap) +{ + char conv_buf[32]; + char *str; + int i, slen; + const char *fstart = 0; + + /* state */ + int cnum = 0; + int base = 10; + int alt = 0; + int fwidth = 0; + int padc = ' '; + int sign = 0; + int left_align = 0; + int hex_caps = 0; + int unsig = 0; + int num, unum; + + while(*fmt) { + if(*fmt == '%') { + fstart = fmt++; + continue; + } + + if(fstart) { + if(IS_CONV(*fmt)) { + switch(*fmt) { + case 'X': + hex_caps = 1; + + case 'x': + case 'p': + base = 16; + + if(alt) { + bwrite(out, BUF(buf), SZ(sz), "0x", 2); + cnum += 2; + } + + case 'u': + unsig = 1; + + if(0) { + case 'o': + base = 8; + + if(alt) { + bwrite(out, BUF(buf), SZ(sz), "0", 1); + cnum++; + } + } + + case 'd': + case 'i': + if(unsig) { + unum = va_arg(ap, unsigned int); + utoa(unum, conv_buf, base); + } else { + num = va_arg(ap, int); + itoa(num, conv_buf, base); + } + if(hex_caps) { + for(i=0; conv_buf[i]; i++) { + conv_buf[i] = toupper(conv_buf[i]); + } + } + + slen = strlen(conv_buf); + + if(left_align) { + if(!unsig && sign && num >= 0) { + bwrite(out, BUF(buf), SZ(sz), "+", 1); + cnum++; + } + bwrite(out, BUF(buf), SZ(sz), conv_buf, slen); + cnum += slen; + padc = ' '; + } + for(i=slen; i= 0) { + bwrite(out, BUF(buf), SZ(sz), "+", 1); + cnum++; + } + bwrite(out, BUF(buf), SZ(sz), conv_buf, slen); + cnum += slen; + } + break; + + case 'c': + { + char c = va_arg(ap, int); + bwrite(out, BUF(buf), SZ(sz), &c, 1); + cnum++; + } + break; + + case 's': + str = va_arg(ap, char*); + slen = strlen(str); + + if(left_align) { + bwrite(out, BUF(buf), SZ(sz), str, slen); + cnum += slen; + padc = ' '; + } + for(i=slen; i + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef STDIO_H_ +#define STDIO_H_ + +#include +#include + +typedef struct FILE FILE; + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define EOF (-1) + +#define stdin ((FILE*)0) +#define stdout ((FILE*)1) +#define stderr ((FILE*)2) + +int putchar(int c); +int puts(const char *s); + +int printf(const char *fmt, ...); +int vprintf(const char *fmt, va_list ap); + +int sprintf(char *buf, const char *fmt, ...); +int vsprintf(char *buf, const char *fmt, va_list ap); + +int snprintf(char *buf, size_t sz, const char *fmt, ...); +int vsnprintf(char *buf, size_t sz, const char *fmt, va_list ap); + +/* TODO */ +int fprintf(FILE *fp, const char *fmt, ...); +int vfprintf(FILE *fp, const char *fmt, va_list ap); + +/* TODO +int fscanf(FILE *fp, const char *fmt, ...); +int vfscanf(FILE *fp, const char *fmt, va_list ap); + +int sscanf(const char *str, const char *fmt, ...); +int vsscanf(const char *ptr, const char *fmt, va_list ap); +*/ + +/* printf to the serial port */ +int ser_printf(const char *fmt, ...); +int ser_vprintf(const char *fmt, va_list ap); + +void perror(const char *s); + + +/* FILE I/O */ +FILE *fopen(const char *path, const char *mode); +int fclose(FILE *fp); + +long filesize(FILE *fp); +int fseek(FILE *fp, long offset, int from); +void rewind(FILE *fp); +long ftell(FILE *fp); + +size_t fread(void *buf, size_t size, size_t count, FILE *fp); +size_t fwrite(const void *buf, size_t size, size_t count, FILE *fp); + +int fgetc(FILE *fp); +char *fgets(char *buf, int size, FILE *fp); + +int fputc(int c, FILE *fp); + +int fflush(FILE *fp); + +int feof(FILE *fp); +int ferror(FILE *fp); +void clearerr(FILE *fp); + +#endif /* STDIO_H_ */ diff --git a/src/libc/stdlib.c b/src/libc/stdlib.c new file mode 100644 index 0000000..6b130ed --- /dev/null +++ b/src/libc/stdlib.c @@ -0,0 +1,272 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include +#include +#include + +int atoi(const char *str) +{ + return strtol(str, 0, 10); +} + +long atol(const char *str) +{ + return strtol(str, 0, 10); +} + +long strtol(const char *str, char **endp, int base) +{ + long acc = 0; + int sign = 1; + int valid = 0; + const char *start = str; + + while(isspace(*str)) str++; + + if(base == 0) { + if(str[0] == '0') { + if(str[1] == 'x' || str[1] == 'X') { + base = 16; + } else { + base = 8; + } + } else { + base = 10; + } + } + + if(*str == '+') { + str++; + } else if(*str == '-') { + sign = -1; + str++; + } + + while(*str) { + long val = LONG_MAX; + char c = tolower(*str); + + if(isdigit(c)) { + val = *str - '0'; + } else if(c >= 'a' && c <= 'f') { + val = 10 + c - 'a'; + } else { + break; + } + if(val >= base) { + break; + } + valid = 1; + + acc = acc * base + val; + str++; + } + + if(endp) { + *endp = (char*)(valid ? str : start); + } + + return sign > 0 ? acc : -acc; +} + +void itoa(int val, char *buf, int base) +{ + static char rbuf[16]; + char *ptr = rbuf; + int neg = 0; + + if(val < 0) { + neg = 1; + val = -val; + } + + if(val == 0) { + *ptr++ = '0'; + } + + while(val) { + int digit = val % base; + *ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a'); + val /= base; + } + + if(neg) { + *ptr++ = '-'; + } + + ptr--; + + while(ptr >= rbuf) { + *buf++ = *ptr--; + } + *buf = 0; +} + +void utoa(unsigned int val, char *buf, int base) +{ + static char rbuf[16]; + char *ptr = rbuf; + + if(val == 0) { + *ptr++ = '0'; + } + + while(val) { + unsigned int digit = val % base; + *ptr++ = digit < 10 ? (digit + '0') : (digit - 10 + 'a'); + val /= base; + } + + ptr--; + + while(ptr >= rbuf) { + *buf++ = *ptr--; + } + *buf = 0; +} + +double atof(const char *str) +{ + return strtod(str, 0); +} + + +double strtod(const char *str, char **endp) +{ + char *ep; + const char *start = str; + int valid = 0; + long ival = 0, dval = 0; + int ddig = 0; + double res; + + /* integer part */ + ival = strtol(str, &ep, 10); + if(ep == str && *str != '.') { + if(endp) *endp = (char*)str; + return 0.0; + } + if(ep != str) valid = 1; + str = *ep == '.' ? ep + 1 : ep; + if(!isdigit(*str)) { + goto done; + } + valid = 1; + + dval = strtol(str, &ep, 10); + assert(dval >= 0); + ddig = ep - str; + str = ep; + +done: + if(*endp) { + *endp = (char*)(valid ? str : start); + } + + res = (double)ival; + if(ddig) { + double d = (double)dval; + while(ddig-- > 0) { + d /= 10.0; + } + res += d; + } + return res; +} + +int atexit(void (*func)(void)) +{ + /* there's no concept of exiting at the moment, so this does nothing */ + return 0; +} + +void abort(void) +{ + panic("Aborted\n"); +} + +#define QSORT_THRESHOLD 4 +#define ITEM(idx) ((char*)arr + (idx) * itemsz) + +#define SWAP(p, q) \ + do { \ + int nn = itemsz; \ + char *pp = (p); \ + char *qq = (q); \ + do { \ + char tmp = *pp; \ + *pp++ = *qq; \ + *qq++ = tmp; \ + } while(--nn > 0); \ + } while(0) + +static void ins_sort(void *arr, size_t count, size_t itemsz, int (*cmp)(const void*, const void*)) +{ + int i; + char *it, *a, *b; + + if(count <= 1) return; + + it = (char*)arr + itemsz; + for(i=1; i (char*)arr && cmp(a, (b = a - itemsz)) < 0) { + SWAP(a, b); + a -= itemsz; + } + } +} + +void qsort(void *arr, size_t count, size_t itemsz, int (*cmp)(const void*, const void*)) +{ + char *ma, *mb, *mc, *left, *right; + size_t sepidx, nleft, nright; + + if(count <= 1) return; + + if(count < QSORT_THRESHOLD) { + ins_sort(arr, count, itemsz, cmp); + return; + } + + ma = arr; + mb = ITEM(count / 2); + mc = ITEM(count - 1); + if(cmp(ma, mb) < 0) SWAP(ma, mb); + if(cmp(mc, ma) < 0) SWAP(mc, ma); + + left = ma + itemsz; + right = mc - itemsz; + for(;;) { + while(cmp(left, ma) < 0) left += itemsz; + while(cmp(ma, right) < 0) right -= itemsz; + if(left >= right) break; + SWAP(left, right); + } + SWAP(ma, right); + sepidx = (right - (char*)arr) / itemsz; + nleft = sepidx; + nright = count - nleft - 1; + + qsort(ma, nleft, itemsz, cmp); + qsort(right + itemsz, nright, itemsz, cmp); +} diff --git a/src/libc/stdlib.h b/src/libc/stdlib.h new file mode 100644 index 0000000..981cf69 --- /dev/null +++ b/src/libc/stdlib.h @@ -0,0 +1,53 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef STDLIB_H_ +#define STDLIB_H_ + +#include + +#define RAND_MAX 2147483647 + +#define abs(x) __builtin_abs(x) + +int atoi(const char *str); +long atol(const char *str); +long strtol(const char *str, char **endp, int base); + +void itoa(int val, char *buf, int base); +void utoa(unsigned int val, char *buf, int base); + +double atof(const char *str); +double strtod(const char *str, char **endp); + +int atexit(void (*func)(void)); + +void abort(void); + +void qsort(void *arr, size_t count, size_t size, int (*cmp)(const void*, const void*)); + +int rand(void); +int rand_r(unsigned int *seedp); +void srand(unsigned int seed); + +/* defined in malloc.c */ +void *malloc(size_t sz); +void *calloc(size_t num, size_t sz); +void *realloc(void *ptr, size_t sz); +void free(void *ptr); + +#endif /* STDLIB_H_ */ diff --git a/src/libc/string.c b/src/libc/string.c new file mode 100644 index 0000000..5afe839 --- /dev/null +++ b/src/libc/string.c @@ -0,0 +1,259 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include + +int errno; + +void *memmove(void *dest, const void *src, size_t n) +{ + int i; + char *dptr; + const char *sptr; + + if(dest <= src) { + /* forward copy */ + dptr = dest; + sptr = src; + for(i=0; i= 4) { + if(*a32 != *b32) break; + a32++; + b32++; + n -= 4; + } + + /* update byte pointers to contine with the tail */ + a = (unsigned char*)a32; + b = (unsigned char*)b32; + } + + /* we're here both for the tail-end of same-alignment buffers, or for the + * whole length of mis-aligned buffers. + */ + while(n-- > 0) { + if((diff = *a++ - *b++) != 0) { + return diff; + } + } + return 0; +} + +size_t strlen(const char *s) +{ + size_t len = 0; + while(*s++) len++; + return len; +} + +char *strchr(const char *s, int c) +{ + while(*s) { + if(*s == c) { + return (char*)s; + } + s++; + } + return 0; +} + +char *strrchr(const char *s, int c) +{ + const char *ptr = s; + + /* find the end */ + while(*ptr) ptr++; + + /* go back checking for c */ + while(--ptr >= s) { + if(*ptr == c) { + return (char*)ptr; + } + } + return 0; +} + +char *strstr(const char *str, const char *substr) +{ + while(*str) { + const char *s1 = str; + const char *s2 = substr; + + while(*s1 && *s1 == *s2) { + s1++; + s2++; + } + if(!*s2) { + return (char*)str; + } + str++; + } + return 0; +} + +char *strcasestr(const char *str, const char *substr) +{ + while(*str) { + const char *s1 = str; + const char *s2 = substr; + + while(*s1 && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + if(!*s2) { + return (char*)str; + } + str++; + } + return 0; +} + +int strcmp(const char *s1, const char *s2) +{ + while(*s1 && *s1 == *s2) { + s1++; + s2++; + } + return *s1 - *s2; +} + +int strcasecmp(const char *s1, const char *s2) +{ + while(*s1 && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + return tolower(*s1) - tolower(*s2); +} + +int strncmp(const char *s1, const char *s2, int n) +{ + if(n <= 0) return 0; + + while(n-- > 0 && *s1 && *s2 && *s1 == *s2) { + s1++; + s2++; + } + + if(n <= 0) return 0; + return *s1 - *s2; +} + +int strncasecmp(const char *s1, const char *s2, int n) +{ + if(n <= 0) return 0; + + while(n-- > 0 && *s1 && *s2 && tolower(*s1) == tolower(*s2)) { + s1++; + s2++; + } + + if(n <= 0) return 0; + return tolower(*s1) - tolower(*s2); +} + +char *strcpy(char *dest, const char *src) +{ + char *dptr = dest; + while((*dptr++ = *src++)); + return dest; +} + +char *strcat(char *dest, const char *src) +{ + strcpy(dest + strlen(dest), src); + return dest; +} + +char *strncpy(char *dest, const char *src, int n) +{ + char *dptr = dest; + while(n-- > 0 && (*dptr++ = *src++)); + return dest; +} + + +static const char *errstr[] = { + "Success", + "Foo", + "Interrupted", + "Invalid", + "Child", + "Timeout", + "Out of memory", + "I/O error", + "Not found", + "Name too long", + "No space left on device", + "Permission denied", + "Not a directory", + "Is a directory", + "Does not exist", + 0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0, + "Bug" +}; + +char *strerror(int err) +{ + if(err < 0 || err > sizeof errstr / sizeof *errstr || !errstr[err]) { + return "Unknown"; + } + return (char*)errstr[err]; +} diff --git a/src/libc/string.h b/src/libc/string.h new file mode 100644 index 0000000..85490b2 --- /dev/null +++ b/src/libc/string.h @@ -0,0 +1,52 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018-2019 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef STRING_H_ +#define STRING_H_ + +#include + +void *memset(void *s, int c, size_t n); +void *memset16(void *s, int c, size_t n); + +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); + +int memcmp(void *aptr, void *bptr, size_t n); + +size_t strlen(const char *s); + +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); + +char *strstr(const char *str, const char *substr); +char *strcasestr(const char *str, const char *substr); + +int strcmp(const char *s1, const char *s2); +int strcasecmp(const char *s1, const char *s2); + +int strncmp(const char *s1, const char *s2, int n); +int strncasecmp(const char *s1, const char *s2, int n); + +char *strcpy(char *dest, const char *src); +char *strcat(char *dest, const char *src); + +char *strncpy(char *dest, const char *src, int n); + +char *strerror(int err); + +#endif /* STRING_H_ */ diff --git a/src/libc/string_s.asm b/src/libc/string_s.asm new file mode 100644 index 0000000..4545d16 --- /dev/null +++ b/src/libc/string_s.asm @@ -0,0 +1,174 @@ +; pcboot - bootable PC demo/game kernel +; Copyright (C) 2018 John Tsiombikas +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY, without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + ; standard C memset + global memset +memset: + push ebp + mov ebp, esp + push edi + + mov edi, [ebp + 8] + push edi + mov al, [ebp + 12] + mov ah, al + mov cx, ax + shl eax, 16 + mov ax, cx + mov ecx, [ebp + 16] + + test ecx, ecx + jz .done + + ; write 1, 2, or 3 times until we reache a 32bit-aligned dest address + mov edx, edi + and edx, 3 + jz .main + jmp [.pre_tab + ecx * 4] + +.pre_tab dd .main, .pre1, .pre2, .pre3 +.pre3: stosb + dec ecx +.pre2: stosb + dec ecx +.pre1: stosb + dec ecx + jz .done + + ; edi is 32bit-aligned here, write ecx>>2 32bit values +.main: + push ecx + shr ecx, 2 + rep stosd + pop ecx + + ; write any trailing bytes + and ecx, 3 + jmp [.post_tab + ecx * 4] + +.post_tab dd .done, .post1, .post2, .post3 +.post3: stosb +.post2: stosb +.post1: stosb + +.done: pop eax + pop edi + pop ebp + ret + + + ; same as memset but copies 16bit values, and the count is the number + ; of 16bit values to copy, not bytes. + global memset16 +memset16: + push ebp + mov ebp, esp + push edi + + mov edi, [ebp + 8] + push edi + mov ax, [ebp + 12] + shl eax, 16 + mov ax, [ebp + 12] + mov ecx, [ebp + 16] + + test ecx, ecx + jz .done + + mov edx, edi + and edx, 3 + jz .main + jmp [.pre_tab + edx * 4] + +.pre_tab dd .main, .pre1, .pre2, .pre3 +.pre3: + ; unaligned by +3: + ; same as next one, but jump to ms16main instead of falling through + mov [edi], al + mov [edi + ecx * 2 - 1], ah + rol eax, 8 + inc edi + dec ecx + jz .done + jmp .main +.pre1: + ; unaligned by +1: + ; - write low byte at edi + ; - write high byte at the end + ; - rotate by 8 for the rest + ; - decrement ecx + mov [edi], al + mov [edi + ecx * 2 - 1], ah + rol eax, 8 + inc edi + dec ecx + jz .done +.pre2: + ; unaligned by +2 + stosw + dec ecx + jz .done + +.main: + push ecx + shr ecx, 1 + rep stosd + pop ecx + + and ecx, 1 + jz .done + stosw +.done: + pop eax + pop edi + pop ebp + ret + + + ; standard C memcpy + global memcpy +memcpy: + push ebp + mov ebp, esp + push edi + push esi + + mov edi, [ebp + 8] + mov esi, [ebp + 12] + mov ecx, [ebp + 16] + + test ecx, ecx + jz .done + + mov edx, ecx + shr ecx, 2 + rep movsd + + and edx, 3 + jmp [.post_tab + edx * 4] + +.post_tab dd .done, .post1, .post2, .post3 +.post3: movsb +.post2: movsb +.post1: movsb + +.done: + pop esi + pop edi + pop ebp + ret + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/loader.asm b/src/loader.asm index b0c13d6..68504f8 100644 --- a/src/loader.asm +++ b/src/loader.asm @@ -1,11 +1,12 @@ section .loader extern startup - extern _ldr_main_start extern _main_start extern _main_size +%include 'macros.inc' + [bits 16] global _start _start: @@ -31,6 +32,7 @@ _start: jmp exit .notvm86: + call setup_serial call enable_a20 ; calculate GDT linear address @@ -84,6 +86,14 @@ _start: call 8:startup + PMPRINT 'X' + PMPRINT 'Y' + PMPRINT 'Z' + PMPRINT 'Z' + PMPRINT 'Y' + PMPRINT 13 + PMPRINT 10 + ; return to real mode jmp 28h:.rm @@ -102,6 +112,9 @@ _start: exit: mov ax, 4c00h int 21h +dbg_inpm db 'DBG: in pmode',10,0 +dbg_retrm db 'DBG: returning to real mode...',10,0 + str_errvm86 db 'Error: memory manager detected! Stop it and try again (e.g. emm386 off)',10,0 str_enterpm db 'Entering 32bit protected mode ...',10,0 @@ -189,6 +202,45 @@ kbc_wait_write: jnz kbc_wait_write ret +UART_BASE equ 2f8h ; COM1: 3f8, COM2: 2f8 +UART_DIVISOR equ 115200 / 9600 ; 9600 baud +setup_serial: + ; set clock divisor + mov dx, UART_BASE + 3 ; LCTL + mov al, 80h ; DLAB + out dx, al + mov dx, UART_BASE ; DIVLO + mov al, UART_DIVISOR & 0xff + out dx, al + inc dx ; DIVHI + mov al, UART_DIVISOR >> 8 + out dx, al + mov dx, UART_BASE + 3 ; LCTL + mov al, 3 ; 8n1 + out dx, al + inc dx ; MCTL + mov al, 0xb ; DTR/RTS/OUT2 + out dx, al + ret + +ser_putchar: + SER_PUTCHAR + ret + +ser_printstr: + lodsb + test al, al + jz .end + cmp al, 10 + jnz .nolf + push ax + mov al, 13 + call ser_putchar + pop ax +.nolf: call ser_putchar + jmp ser_printstr +.end: ret + printstr: lodsb test al, al @@ -196,17 +248,39 @@ printstr: cmp al, 10 ; check for line-feed and insert CR before it jnz .nolf push ax + mov al, 13 + call ser_putchar mov dl, 13 mov ah, 2 int 21h pop ax -.nolf: mov ah, 2 +.nolf: call ser_putchar + mov ah, 2 mov dl, al int 21h jmp printstr .end: ret + [bits 32] +ser_putchar_pmode: + SER_PUTCHAR + ret + +ser_putstr_pmode: + lodsb + test al, al + jz .end + cmp al, 10 + jnz .nolf + push ax + mov al, 13 + call ser_putchar_pmode + pop ax +.nolf: call ser_putchar_pmode + jmp ser_putstr_pmode +.end: ret + align 4 enterpm dd 0xbad00d ; space for linear address for far jump to pmode enterpm_sel dw 8 ; selector for far jump to protected mode diff --git a/src/macros.inc b/src/macros.inc new file mode 100644 index 0000000..03613dc --- /dev/null +++ b/src/macros.inc @@ -0,0 +1,30 @@ +UART_BASE equ 2f8h ; COM1: 3f8, COM2: 2f8 +UART_DIVISOR equ 115200 / 9600 ; 9600 baud + +%macro WAITKEY 0 +%%waitkey: + in al, 64h + test al, 1 + jz %%waitkey + in al, 60h +%endmacro + +%macro SER_PUTCHAR 0 + push dx + push ax + mov dx, UART_BASE + 5 ; LSTAT +%%wait: in al, dx + test al, 20h ; TRIG_EMPTY + jz %%wait + mov dx, UART_BASE + pop ax + out dx, al + pop dx +%endmacro + +%macro PMPRINT 1 + mov al, %1 + SER_PUTCHAR +%endmacro + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..93cbfd5 --- /dev/null +++ b/src/main.c @@ -0,0 +1,14 @@ +void putchar(int c); /* in startup.asm */ + +static void printstr(const char *s) +{ + while(*s) { + putchar(*s++); + } +} + +int main(void) +{ + printstr("hello from C\n"); + return 0; +} diff --git a/src/mem.c b/src/mem.c new file mode 100644 index 0000000..a013607 --- /dev/null +++ b/src/mem.c @@ -0,0 +1,314 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include "config.h" +#include "panic.h" +#include "mem.h" +#include "intr.h" + +#define FREE 0 +#define USED 1 + +#define BM_IDX(pg) ((pg) / 32) +#define BM_BIT(pg) ((pg) & 0x1f) + +#define IS_FREE(pg) ((bitmap[BM_IDX(pg)] & (1 << BM_BIT(pg))) == 0) + +#define MEM_START ((uint32_t)&_mem_start) + + +struct mem_range { + uint32_t start; + uint32_t size; +}; + +void move_stack(uint32_t newaddr); /* defined in startup.s */ + +static void mark_page(int pg, int used); +static void add_memory(uint32_t start, size_t size); + +#define MAX_MAP_SIZE 16 +extern struct mem_range boot_mem_map[MAX_MAP_SIZE]; +extern int boot_mem_map_size; + +/* linker supplied symbol, points to the end of the kernel image */ +extern uint32_t _mem_start; + + +/* A bitmap is used to track which physical memory pages are used, and which + * are available for allocation by alloc_ppage. + * + * last_alloc_idx keeps track of the last 32bit element in the bitmap array + * where a free page was found. It's guaranteed that all the elements before + * this have no free pages, but it doesn't imply that there will be another + * free page there. So it's used as a starting point for the search. + */ +static uint32_t *bitmap; +static int bmsize, last_alloc_idx; + + + +void init_mem(void) +{ + int i, pg, max_used_pg, end_pg = 0; + uint32_t used_end, start, end, sz, total = 0, rem; + const char *suffix[] = {"bytes", "KB", "MB", "GB"}; + + last_alloc_idx = 0; + + /* the allocation bitmap starts right at the end of the kernel image */ + bitmap = (uint32_t*)&_mem_start; + + /* start by marking all posible pages (2**20) as used. We do not "reserve" + * all this space. Pages beyond the end of the useful bitmap area + * ((char*)bitmap + bmsize), which will be determined after we traverse the + * memory map, are going to be marked as available for allocation. + */ + memset(bitmap, 0xff, 1024 * 1024 / 8); + + + if(boot_mem_map_size <= 0 || boot_mem_map_size > MAX_MAP_SIZE) { + panic("invalid memory map size reported by the boot loader: %d\n", boot_mem_map_size); + } + + printf("Memory map:\n"); + for(i=0; i 1024) { + rem = total & 0x3ff; + total >>= 10; + ++i; + } + printf("Total usable RAM: %u.%u %s\n", total, 100 * rem / 1024, suffix[i]); + + /* size of the useful part of the bitmap in bytes padded to 4-byte + * boundaries to allow 32bit at a time operations. + */ + bmsize = (end_pg / 32) * 4; + + /* mark all pages occupied by the bitmap as used */ + used_end = (uint32_t)bitmap + bmsize - 1; + + max_used_pg = ADDR_TO_PAGE(used_end); + printf("marking pages up to %x (page: %d) as used\n", used_end, max_used_pg); + for(i=0; i<=max_used_pg; i++) { + mark_page(i, USED); + } + +#ifdef MOVE_STACK_RAMTOP + /* allocate space for the stack at the top of RAM and move it there */ + if((pg = alloc_ppages(STACK_PAGES, MEM_STACK)) != -1) { + printf("moving stack-top to: %x (%d pages)\n", PAGE_TO_ADDR(end_pg) - 4, STACK_PAGES); + move_stack(PAGE_TO_ADDR(pg + STACK_PAGES) - 4); + } +#endif +} + +int alloc_ppage(int area) +{ + return alloc_ppages(1, area); +} + +/* free_ppage marks the physical page, free in the allocation bitmap. + * + * CAUTION: no checks are done that this page should actually be freed or not. + * If you call free_ppage with the address of some part of memory that was + * originally reserved due to it being in a memory hole or part of the kernel + * image or whatever, it will be subsequently allocatable by alloc_ppage. + */ +void free_ppage(int pg) +{ + int bmidx = BM_IDX(pg); + + int intr_state = get_intr_flag(); + disable_intr(); + + if(IS_FREE(pg)) { + panic("free_ppage(%d): I thought that was already free!\n", pg); + } + + mark_page(pg, FREE); + if(bmidx < last_alloc_idx) { + last_alloc_idx = bmidx; + } + + set_intr_flag(intr_state); +} + + +int alloc_ppages(int count, int area) +{ + int i, dir, pg, idx, max, intr_state, found_free = 0; + + intr_state = get_intr_flag(); + disable_intr(); + + if(area == MEM_STACK) { + idx = (bmsize - 1) / 4; + max = -1; + dir = -1; + } else { + idx = last_alloc_idx; + max = bmsize / 4; + dir = 1; + } + + while(idx != max) { + /* if at least one bit is 0 then we have at least + * one free page. find it and try to allocate a range starting from there + */ + if(bitmap[idx] != 0xffffffff) { + pg = idx * 32; + if(dir < 0) pg += 31; + + for(i=0; i<32; i++) { + if(IS_FREE(pg)) { + if(!found_free && dir > 0) { + last_alloc_idx = idx; + found_free = 1; + } + + if(alloc_ppage_range(pg, count) != -1) { + set_intr_flag(intr_state); + return pg; + } + } + pg += dir; + } + } + idx += dir; + } + + set_intr_flag(intr_state); + return -1; +} + +void free_ppages(int pg0, int count) +{ + int i; + + for(i=0; i + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef MEM_H_ +#define MEM_H_ + +#define ADDR_TO_PAGE(x) ((uint32_t)(x) >> 12) +#define PAGE_TO_ADDR(x) ((uint32_t)(x) << 12) +#define PAGE_TO_PTR(x) ((void*)PAGE_TO_ADDR(x)) + +#define BYTES_TO_PAGES(x) (((uint32_t)(x) + 4095) >> 12) + +/* for alloc_ppage/alloc_ppages */ +enum { + MEM_HEAP = 0, /* start searching from the bottom */ + MEM_STACK = 1 /* start searching from the top */ +}; + +void init_mem(void); + +int alloc_ppage(int area); +void free_ppage(int pg); + +/* allocate a number of consecutive pages */ +int alloc_ppages(int count, int area); +void free_ppages(int pg0, int count); + +/* allocate a specific range of pages. + * Fails (and returns -1) if any page in the requested range is not free. + */ +int alloc_ppage_range(int start, int size); +int free_ppage_range(int start, int size); + +#endif /* MEM_H_ */ diff --git a/src/panic.c b/src/panic.c new file mode 100644 index 0000000..3daff29 --- /dev/null +++ b/src/panic.c @@ -0,0 +1,65 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include +#include "asmops.h" + +struct all_registers { + uint32_t eax, ebx, ecx, edx; + uint32_t esp, ebp, esi, edi; + uint32_t eflags; + uint32_t cs, ss, ds, es, fs, gs; + uint32_t cr0, cr1, cr2, cr3; +}; + +/* defined in regs.S */ +void get_regs(struct all_registers *regs); + +void panic(const char *fmt, ...) +{ + va_list ap; + struct all_registers regs; + uint32_t eip; + + disable_intr(); + + memset(®s, 0, sizeof regs); + get_regs(®s); + + CALLER_EIP(eip); + + printf("~~~~~ pcboot panic ~~~~~\n"); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + printf("\nRegisters:\n"); + printf("eax: %x, ebx: %x, ecx: %x, edx: %x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); + printf("esp: %x, ebp: %x, esi: %x, edi: %x\n", regs.esp, regs.ebp, regs.esi, regs.edi); + printf("eip: %x, eflags: %x\n", eip, regs.eflags); + printf("cr0: %x, cr1: %x, cr2: %x, cr3: %x\n", regs.cr0, regs.cr1, regs.cr2, regs.cr3); + printf("cs: %x (%d|%d)\n", regs.cs, regs.cs >> 3, regs.cs & 3); + printf("ss: %x (%d|%d)\n", regs.ss, regs.ss >> 3, regs.ss & 3); + printf("ds: %x (%d|%d)\n", regs.ds, regs.ds >> 3, regs.ds & 3); + printf("es: %x (%d|%d)\n", regs.es, regs.es >> 3, regs.es & 3); + printf("fs: %x (%d|%d)\n", regs.fs, regs.fs >> 3, regs.fs & 3); + printf("gs: %x (%d|%d)\n", regs.gs, regs.gs >> 3, regs.gs & 3); + + for(;;) halt_cpu(); +} diff --git a/src/panic.h b/src/panic.h new file mode 100644 index 0000000..012eecb --- /dev/null +++ b/src/panic.h @@ -0,0 +1,23 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef PANIC_H_ +#define PANIC_H_ + +void panic(const char *fmt, ...) __attribute__((noreturn)); + +#endif /* PANIC_H_ */ diff --git a/src/regs.asm b/src/regs.asm new file mode 100644 index 0000000..5fb93d1 --- /dev/null +++ b/src/regs.asm @@ -0,0 +1,57 @@ + section .text + align 4 + + global get_regs +get_regs: + push ebp + mov ebp, esp + + push edx + mov edx, [ebp + 8] + + mov [edx], eax + mov [edx + 4], ebx + mov [edx + 8], ecx + + ; juggle edx + mov eax, edx + pop edx + mov [eax + 12], edx + push edx + mov edx, eax + + ; those two are pointless in a function + mov [edx + 16], esp + mov [edx + 20], ebp + + mov [edx + 24], esi + mov [edx + 28], edx + + pushf + pop eax + mov [edx + 32], eax + + mov [edx + 36], cs + mov [edx + 40], ss + mov [edx + 44], ds + mov [edx + 48], es + mov [edx + 52], fs + mov [edx + 56], gs + + push ebx + mov ebx, cr0 + mov [edx + 60], ebx + ;mov ebx, cr1 + ;mov [edx + 64], ebx + mov ebx, cr2 + mov [edx + 68], ebx + mov ebx, cr3 + mov [edx + 72], ebx + pop ebx + + pop edx + pop ebp + ret + + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/segm.c b/src/segm.c new file mode 100644 index 0000000..c6baffd --- /dev/null +++ b/src/segm.c @@ -0,0 +1,159 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include "segm.h" +#include "desc.h" +#include "tss.h" + +/* bits for the 3rd 16bt part of the descriptor */ +#define BIT_ACCESSED (1 << 8) +#define BIT_WR (1 << 9) +#define BIT_RD (1 << 9) +#define BIT_EXP_DOWN (1 << 10) +#define BIT_CONFORMING (1 << 10) +#define BIT_CODE (1 << 11) +#define BIT_NOSYS (1 << 12) +#define BIT_PRESENT (1 << 15) +/* TSS busy bit */ +#define BIT_BUSY (1 << 9) + +/* bits for the last 16bit part of the descriptor */ +#define BIT_BIG (1 << 6) +#define BIT_DEFAULT (1 << 6) +#define BIT_GRAN (1 << 7) + +enum {TYPE_DATA, TYPE_CODE}; + +/* we need the following bit pattern at the 8th bit excluding the busy bit: 1001 */ +#define TSS_TYPE_BITS (9 << 8) + +static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type); +static void segm_desc16(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type); +static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl); + +/* these functions are implemented in segm-asm.S */ +void setup_selectors(uint16_t code, uint16_t data); +void set_gdt(uint32_t addr, uint16_t limit); +void set_task_reg(uint16_t tss_selector); + + +/* our global descriptor table */ +static desc_t gdt[NUM_SEGMENTS] __attribute__((aligned(8))); + + +void init_segm(void) +{ + memset(gdt, 0, sizeof gdt); + segm_desc(gdt + SEGM_KCODE, 0, 0xffffffff, 0, TYPE_CODE); + segm_desc(gdt + SEGM_KDATA, 0, 0xffffffff, 0, TYPE_DATA); + segm_desc(gdt + SEGM_UCODE, 0, 0xffffffff, 3, TYPE_CODE); + segm_desc(gdt + SEGM_UDATA, 0, 0xffffffff, 3, TYPE_DATA); + segm_desc16(gdt + SEGM_CODE16, 0, 0xffff, 0, TYPE_CODE); + + set_gdt((uint32_t)gdt, sizeof gdt - 1); + + setup_selectors(selector(SEGM_KCODE, 0), selector(SEGM_KDATA, 0)); +} + +/* constructs a GDT selector based on index and priviledge level */ +uint16_t selector(int idx, int rpl) +{ + return (idx << 3) | (rpl & 3); +} + +void set_tss(uint32_t addr) +{ + task_desc(gdt + SEGM_TASK, addr, sizeof(struct task_state) - 1, 3); + set_task_reg(selector(SEGM_TASK, 0)); +} + +static void segm_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type) +{ + desc->d[0] = limit & 0xffff; /* low order 16bits of limit */ + desc->d[1] = base & 0xffff; /* low order 16bits of base */ + + /* third 16bit part contains the last 8 bits of base, the 2 priviledge + * level bits starting on bit 13, present flag on bit 15, and type bits + * starting from bit 8 + */ + desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT | + BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE)); + + /* last 16bit part contains the last nibble of limit, the last byte of + * base, and the granularity and deafult/big flags in bits 23 and 22 resp. + */ + desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN | BIT_BIG; +} + +static void segm_desc16(desc_t *desc, uint32_t base, uint32_t limit, int dpl, int type) +{ + desc->d[0] = limit & 0xffff; /* low order 16bits of limit */ + desc->d[1] = base & 0xffff; /* low order 16bits of base */ + + /* third 16bit part contains the last 8 bits of base, the 2 priviledge + * level bits starting on bit 13, present flag on bit 15, and type bits + * starting from bit 8 + */ + desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT | + BIT_NOSYS | (type == TYPE_DATA ? BIT_WR : (BIT_RD | BIT_CODE)); + + /* 16bit descriptors have the upper word 0 */ + desc->d[3] = 0; +} + +static void task_desc(desc_t *desc, uint32_t base, uint32_t limit, int dpl) +{ + desc->d[0] = limit & 0xffff; + desc->d[1] = base & 0xffff; + + desc->d[2] = ((base >> 16) & 0xff) | ((dpl & 3) << 13) | BIT_PRESENT | + TSS_TYPE_BITS; /* XXX busy ? */ + desc->d[3] = ((limit >> 16) & 0xf) | ((base >> 16) & 0xff00) | BIT_GRAN; +} +/* +static void dbg_print_gdt(void) +{ + int i; + + printf("Global Descriptor Table\n"); + printf("-----------------------\n"); + + for(i=0; i<6; i++) { + print_desc(gdt + i); + } +} + +static void print_desc(desc_t *desc) +{ + uint32_t base, limit; + int dpl, g, db, l, avl, p, s, type; + char *type_str; + + base = (uint32_t)desc->d[1] | ((uint32_t)(desc->d[2] & 0xff) << 16) | ((uint32_t)(desc->d[3] >> 8) << 24); + limit = (uint32_t)desc->d[0] | ((uint32_t)(desc->d[3] & 0xf) << 16); + dpl = (desc->d[2] >> 13) & 3; + type = (desc->d[2] >> 8) & 0xf; + g = (desc->d[3] >> 23) & 1; + db = (desc->d[3] >> 22) & 1; + l = (desc->d[3] >> 21) & 1; + avl = (desc->d[3] >> 20) & 1; + + p = (desc->d[2] >> 15) & 1; + s = (desc->d[2] >> 12) & 1; +} +*/ diff --git a/src/segm.h b/src/segm.h new file mode 100644 index 0000000..b57375b --- /dev/null +++ b/src/segm.h @@ -0,0 +1,39 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef SEGM_H_ +#define SEGM_H_ + +enum { + SEGM_KCODE = 1, + SEGM_KDATA = 2, + SEGM_UCODE, + SEGM_UDATA, + SEGM_TASK, + SEGM_CODE16, + + NUM_SEGMENTS +}; + +void init_segm(void); + +uint16_t selector(int idx, int rpl); + +void set_tss(uint32_t addr); + + +#endif /* SEGM_H_ */ diff --git a/src/segm_s.asm b/src/segm_s.asm new file mode 100644 index 0000000..6448c04 --- /dev/null +++ b/src/segm_s.asm @@ -0,0 +1,64 @@ +; pcboot - bootable PC demo/game kernel +; Copyright (C) 2018-2023 John Tsiombikas +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY, without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + section .data + align 4 +; memory reserved for setup_selectors +off: dd 0 +segm: dw 0 +; memory reserved for set_gdt +lim: dw 0 +addr: dd 0 + + section .text +; setup_selectors(uint16_t code, uint16_t data) +; loads the requested selectors to all the selector registers + global setup_selectors +setup_selectors: + ; set data selectors directly + mov eax, [esp + 8] + mov ss, ax + mov es, ax + mov ds, ax + mov gs, ax + ;mov fs, ax ; XXX don't touch fs, we use it to store initial seg + ; set cs using a long jump + mov eax, [esp + 4] + mov [segm], ax + mov dword [off], .ldcs + jmp [off] +.ldcs: ret + +; set_gdt(uint32_t addr, uint16_t limit) +; loads the GDTR with the new address and limit for the GDT + global set_gdt +set_gdt: + mov eax, [esp + 4] + mov [addr], eax + mov ax, [esp + 8] + mov [lim], ax + lgdt [lim] + ret + +; set_task_reg(uint16_t tss_selector) +; loads the TSS selector in the task register + global set_task_reg +set_task_reg: + mov eax, [esp + 4] + ltr [esp + 4] + ret + +; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..f9ee14c --- /dev/null +++ b/src/serial.c @@ -0,0 +1,351 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include +#include +#include "config.h" +#include "serial.h" +#include "asmops.h" +#include "intr.h" +#include "panic.h" + +#define UART1_BASE 0x3f8 +#define UART2_BASE 0x2f8 +#define UART1_IRQ 4 +#define UART2_IRQ 3 + +#define UART_DATA 0 +#define UART_INTR 1 +#define UART_DIVLO 0 +#define UART_DIVHI 1 +#define UART_FIFO 2 +#define UART_IID 2 +#define UART_LCTL 3 +#define UART_MCTL 4 +#define UART_LSTAT 5 +#define UART_MSTAT 6 + +/* interrupt enable register bits */ +#define INTR_RECV 1 +#define INTR_SEND 2 +#define INTR_LSTAT 4 +#define INTR_DELTA 8 + +/* fifo control register bits */ +#define FIFO_ENABLE 0x01 +#define FIFO_RECV_CLEAR 0x02 +#define FIFO_SEND_CLEAR 0x04 +#define FIFO_DMA 0x08 +#define FIFO_TRIG_4 0x40 +#define FIFO_TRIG_8 0x80 +#define FIFO_TRIG_14 0xc0 + +/* interrupt id register bits */ +#define IID_PENDING 0x01 +#define IID_ID0 0x02 +#define IID_ID1 0x04 +#define IID_ID2 0x08 +#define IID_FIFO_EN 0xc0 + +#define IID_SOURCE 0xe + +#define IID_DELTA 0 +#define IID_SEND 0x2 +#define IID_RECV 0x4 +#define IID_FIFO 0xc +#define IID_STATUS 0x6 + +/* line control register bits */ +#define LCTL_BITS_8 0x03 +#define LCTL_STOP_2 0x04 +#define LCTL_DLAB 0x80 +#define LCTL_8N1 LCTL_BITS_8 +#define LCTL_8N2 (LCTL_BITS_8 | LCTL_STOP_2) + +/* modem control register bits */ +#define MCTL_DTR 0x01 +#define MCTL_RTS 0x02 +#define MCTL_OUT1 0x04 +#define MCTL_OUT2 0x08 +#define MCTL_LOOP 0x10 + +/* line status register bits */ +#define LST_DRDY 0x01 +#define LST_ERR_OVER 0x02 +#define LST_ERR_PARITY 0x04 +#define LST_ERR_FRAME 0x08 +#define LST_ERR_BRK 0x10 +#define LST_TREG_EMPTY 0x20 +#define LST_TIDLE 0x40 +#define LST_ERROR 0x80 + +/* modem status register bits */ +#define MST_DELTA_CTS 0x01 +#define MST_DELTA_DSR 0x02 +#define MST_TERI 0x04 +#define MST_DELTA_DCD 0x08 +#define MST_CTS 0x10 +#define MST_DSR 0x20 +#define MST_RING 0x40 +#define MST_DCD 0x80 + +/* interrupt controller stuff */ +#define PIC1_CMD_PORT 0x20 +#define PIC1_DATA_PORT 0x21 +#define PIC2_CMD_PORT 0xa0 +#define PIC2_DATA_PORT 0xa1 +#define OCW2_EOI 0x20 + +#define COM_FMT_8N1 LCTL_8N1 +#define COM_FMT_8N2 LCTL_8N2 + +struct serial_port { + int base, intr; + int blocking; + + char inbuf[256]; + int inbuf_ridx, inbuf_widx; +}; + +#define BNEXT(x) (((x) + 1) & 0xff) +#define BEMPTY(b) (b##_ridx == b##_widx) + +static int have_recv(int base); +static void recv_intr(); + +static struct serial_port ports[2]; +static int num_open; + +static int uart_base[] = {UART1_BASE, UART2_BASE}; +static int uart_irq[] = {UART1_IRQ, UART2_IRQ}; + +int ser_open(int pidx, int baud, unsigned int mode) +{ + unsigned short div = 115200 / baud; + int base, intr; + unsigned int fmt; + + if(pidx < 0 || pidx > 1) { + printf("ser_open: invalid serial port: %d\n", pidx); + return -1; + } + + if(ports[pidx].base) { + printf("ser_open: port %d already open!\n", pidx); + return -1; + } + memset(ports + pidx, 0, sizeof ports[pidx]); + + base = uart_base[pidx]; + intr = uart_irq[pidx]; + + if(mode & SER_8N2) { + fmt = COM_FMT_8N2; + } else { + fmt = COM_FMT_8N1; + } + + interrupt(IRQ_TO_INTR(uart_irq[pidx]), recv_intr); + + outp(base + UART_LCTL, LCTL_DLAB); + outp(base + UART_DIVLO, div & 0xff); + outp(base + UART_DIVHI, (div >> 8) & 0xff); + outp(base + UART_LCTL, fmt); /* fmt should be LCTL_8N1, LCTL_8N2 etc */ + outp(base + UART_FIFO, FIFO_ENABLE | FIFO_SEND_CLEAR | FIFO_RECV_CLEAR); + outp(base + UART_MCTL, MCTL_DTR | MCTL_RTS | MCTL_OUT2); + outp(base + UART_INTR, INTR_RECV); + + ports[pidx].base = base; + ports[pidx].intr = intr; + ports[pidx].blocking = 1; + ++num_open; + return pidx; +} + +void ser_close(int fd) +{ + if(--num_open == 0) { + outp(ports[fd].base + UART_INTR, 0); + outp(ports[fd].base + UART_MCTL, 0); + } + + ports[fd].base = 0; +} + +int ser_block(int fd) +{ + ports[fd].blocking = 1; + return 0; +} + +int ser_nonblock(int fd) +{ + ports[fd].blocking = 0; + return 0; +} + +int ser_pending(int fd) +{ + return !BEMPTY(ports[fd].inbuf); +} + +/* if msec < 0: wait for ever */ +int ser_wait(int fd, long msec) +{ + int res; + while(!(res = ser_pending(fd))) { + /* TODO timeout */ + } + return res; +} + +static int can_send(int fd) +{ + int base = ports[fd].base; + return inp(base + UART_LSTAT) & LST_TREG_EMPTY; +} + +void ser_putc(int fd, char c) +{ + int base = ports[fd].base; + + if(c == '\n') { + ser_putc(fd, '\r'); + } + + while(!can_send(fd)); + /*while((inp(base + UART_MSTAT) & MST_CTS) == 0);*/ + outp(base + UART_DATA, c); +} + +int ser_getc(int fd) +{ + struct serial_port *p = ports + fd; + int have, c = -1; + + if(p->blocking) { + while(!(have = ser_pending(fd))); + } else { + have = ser_pending(fd); + } + + if(have) { + c = p->inbuf[p->inbuf_ridx]; + p->inbuf_ridx = BNEXT(p->inbuf_ridx); + } + return c; +} + +int ser_write(int fd, const char *buf, int count) +{ + int n = count; + while(n--) { + ser_putc(fd, *buf++); + } + return count; +} + +int ser_read(int fd, char *buf, int count) +{ + int c, n = 0; + while(n < count && (c = ser_getc(fd)) != -1) { + *buf++ = c; + ++n; + } + return n; +} + +char *ser_getline(int fd, char *buf, int bsz) +{ + static char linebuf[512]; + static int widx; + int i, rd, size, offs; + + size = sizeof linebuf - widx; + while(size && (rd = ser_read(fd, linebuf + widx, size)) > 0) { + widx += rd; + size -= rd; + } + + linebuf[widx] = 0; + + for(i=0; i= bsz ? bsz - 1 : i; + memcpy(buf, linebuf, size); + buf[size] = 0; + + offs = i + 1; + memmove(linebuf, linebuf + offs, widx - offs); + widx -= offs; + return buf; + } + } + return 0; +} + +static int have_recv(int base) +{ + unsigned short stat = inp(base + UART_LSTAT); + if(stat & LST_ERROR) { + panic("serial receive error\n"); + } + return stat & LST_DRDY; +} + +static void recv_intr() +{ + int i, idreg, c; + + for(i=0; i<2; i++) { + int base = uart_base[i]; + struct serial_port *p = ports + i; + + while(((idreg = inp(base + UART_IID)) & IID_PENDING) == 0) { + while(have_recv(base)) { + c = inp(base + UART_DATA); + +#ifdef ENABLE_GDB_STUB + if(c == 3 && i == GDB_SERIAL_PORT) { + asm("int $3"); + continue; + } +#endif + + p->inbuf[p->inbuf_widx] = c; + p->inbuf_widx = BNEXT(p->inbuf_widx); + + if(p->inbuf_widx == p->inbuf_ridx) { + /* we overflowed, drop the oldest */ + p->inbuf_ridx = BNEXT(p->inbuf_ridx); + } + } + } + } +} + +#ifdef ENABLE_GDB_STUB +void putDebugChar(int c) +{ + ser_putc(GDB_SERIAL_PORT, c); +} + +int getDebugChar(void) +{ + return ser_getc(GDB_SERIAL_PORT); +} +#endif diff --git a/src/serial.h b/src/serial.h new file mode 100644 index 0000000..fb2c17e --- /dev/null +++ b/src/serial.h @@ -0,0 +1,46 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef SERIAL_H_ +#define SERIAL_H_ + +#define SER_8N1 0 +#define SER_8N2 1 +#define SER_HWFLOW 2 + +int ser_open(int pidx, int baud, unsigned int mode); +void ser_close(int fd); + +int ser_block(int fd); +int ser_nonblock(int fd); + +int ser_pending(int fd); +/* if msec < 0: wait for ever */ +int ser_wait(int fd, long msec); + +void ser_putc(int fd, char c); +int ser_getc(int fd); + +int ser_write(int fd, const char *buf, int count); +int ser_read(int fd, char *buf, int count); + +#define ser_putchar(c) ser_putc(0, c) + +char *ser_getline(int fd, char *buf, int bsz); + + +#endif /* SERIAL_H_ */ diff --git a/src/startup.asm b/src/startup.asm index 56e25b4..16e08fa 100644 --- a/src/startup.asm +++ b/src/startup.asm @@ -1,10 +1,20 @@ bits 32 section .text +%include 'macros.inc' + + extern main + global startup startup: - mov ebx, 0xb8000 + mov ebx, 0xb8000 + 156 mov byte [ebx], '@' + mov byte [ebx + 2], '@' + mov byte [ebx + 160], '@' + mov byte [ebx + 162], '@' + + call main + .waitkey: in al, 64h test al, 1 @@ -12,4 +22,16 @@ startup: in al, 60h ret + global putchar +putchar: + mov eax, [esp + 4] + cmp al, 10 + jnz .nonl + push eax + mov al, 13 + SER_PUTCHAR + pop eax +.nonl: SER_PUTCHAR + ret + ; vi:set ts=8 sts=8 sw=8 ft=nasm: diff --git a/src/tss.h b/src/tss.h new file mode 100644 index 0000000..e9c6943 --- /dev/null +++ b/src/tss.h @@ -0,0 +1,38 @@ +/* +pcboot - bootable PC demo/game kernel +Copyright (C) 2018 John Tsiombikas + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY, without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef TSS_H_ +#define TSS_H_ + +#include + +struct task_state { + uint32_t prev_task; + uint32_t esp0, ss0; /* we only ever set these two values */ + uint32_t esp1, ss1; + uint32_t esp2, ss2; + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax, ecx, edx, ebx; + uint32_t esp, ebp, esi, edi; + uint32_t es, cs, ss, ds, fs, gs; + uint32_t ldt_sel; + uint16_t trap, iomap_addr; +} __attribute__((packed)); + +#endif /* TSS_H_ */