acpi, fix mboot memory map, fix kalloc, fix virtalloc node allocator, add kprintf, other changes

This commit is contained in:
Freya Murphy 2024-02-01 12:48:55 -05:00
parent 7a59ef37b8
commit 192a4ccd6b
Signed by: freya
GPG key ID: 744AB800E383AE52
17 changed files with 836 additions and 137 deletions

View file

@ -52,5 +52,7 @@ run: all
qemu-system-x86_64 \
-cdrom $(BUILD_DIR)/$(ISO_NAME) \
-serial stdio \
-display gtk,show-menubar=off,zoom-to-fit=on \
-m 4G \
-enable-kvm \
-name corn

View file

@ -1,5 +1,6 @@
#pragma once
#include <stdarg.h>
#include <stddef.h>
/**
@ -109,7 +110,7 @@ long long int atoll(const char* s);
/**
* Converts a integer to asci inside a string with a given radix (base).
* @param n - the number to conver
* @param n - the number to conver
* @param buffer - the string buffer
* @param radix - the base to convert
*/
@ -117,15 +118,23 @@ char *itoa(int n, char *buffer, int radix);
/**
* Converts a long to asci inside a string with a given radix (base).
* @param n - the number to conver
* @param n - the number to conver
* @param buffer - the string buffer
* @param radix - the base to convert
*/
char *ltoa(long int n, char *buffer, int radix);
/**
* Converts a long long to asci inside a string with a given radix (base).
* @param n - the number to conver
* @param buffer - the string buffer
* @param radix - the base to convert
*/
char *lltoa(long long int n, char *buffer, int radix);
/**
* Converts a unsigned integer to asci inside a string with a given radix (base).
* @param n - the number to conver
* @param n - the number to conver
* @param buffer - the string buffer
* @param radix - the base to convert
*/
@ -133,12 +142,20 @@ char *utoa(unsigned int n, char *buffer, int radix);
/**
* Converts a unsigned long to asci inside a string with a given radix (base).
* @param n - the number to conver
* @param n - the number to conver
* @param buffer - the string buffer
* @param radix - the base to convert
*/
char *ultoa(unsigned long int n, char *buffer, int radix);
/**
* Converts a unsigned long long to asci inside a string with a given radix (base).
* @param n - the number to conver
* @param buffer - the string buffer
* @param radix - the base to convert
*/
char *ulltoa(unsigned long long int n, char *buffer, int radix);
/**
* Converts the string in str to an int value based on the given base.
* The endptr is updated to where the string was no longer valid.
@ -168,3 +185,30 @@ long int strtol(const char *str, char **endptr, int base);
* @returns 0 on error or success, error if endptr is still equal to str
*/
long long int strtoll(const char *str, char **endptr, int base);
/**
* Prints out a char
* @param c - the char
*/
void kputc(char c);
/**
* Prints out a null terminated string
* @param s - the string
*/
void kputs(const char *s);
/**
* Prints out a formatted string
* @param format - the format string
* @param ... - variable args for the format
*/
void kvprintf(const char *format, va_list args);
/**
* Prints out a formatted string
* @param format - the format string
* @param ... - variable args for the format
*/
__attribute__((format(printf, 1, 2)))
void kprintf(const char *format, ...);

View file

@ -5,4 +5,4 @@
int serial_init(void);
uint8_t serial_in(void);
void serial_out(uint8_t ch);
void serial_out_str(char *str);
void serial_out_str(const char *str);

View file

@ -2,7 +2,8 @@
#include <memory.h>
#define CMDLINE_MAX 32
#define CMDLINE_MAX 32
#define MMAP_MAX_ENTRY 64
struct memory_segment {
uint64_t addr;
@ -12,8 +13,7 @@ struct memory_segment {
struct memory_map {
uint32_t entry_count;
uint32_t entry_length;
struct memory_segment *entries;
struct memory_segment entries[MMAP_MAX_ENTRY];
};
struct boot_info {

View file

@ -31,7 +31,7 @@ struct rsdp {
char oemid[6];
uint8_t revision;
uint32_t rsdt_addr;
} __attribute__((packed));
};
// eXtended system descriptor pointer
// ACPI 2.0
@ -46,7 +46,7 @@ struct xsdp {
uint64_t xsdt_addr;
uint8_t extendeid_checksum;
uint8_t reserved[3];
} __attribute__((packed));
};
// root system descriptor table
// ACPI 1.0
@ -161,10 +161,8 @@ static bool checksum(uint8_t *data, size_t len) {
}
static int read_s5_addr(struct acpi_state *state) {
serial_out_str("a");
uintptr_t ptr = state->fadt.dsdt;
char *s5_addr = (void*) (ptr + 36);
serial_out_str("a");
int dsdt_len = *((int*) (ptr+1)) - 36;
while (0 < dsdt_len--) {
@ -256,6 +254,7 @@ int acpi_init_rsdt(struct rsdt *rsdt) {
int acpi_init_xsdt(struct xsdt *xsdt) {
xsdt = mmap(xsdt, sizeof(struct xsdt));
return -1;
state.dst.xsdt = xsdt;
state.version = 2;

View file

@ -90,6 +90,8 @@ start:
push DWORD 0
push ebx
push DWORD 0
push eax
mov edi, 0x1000
mov cr3, edi
@ -143,8 +145,10 @@ code64:
xor rbp, rbp ; set ebp to 0 so we know where to end stack traces
pop rdi
pop rsi
call amd64_shim
mov rdi, rax
xor rsi, rsi
sti
call kmain

38
src/arch/amd64/linker.ld Normal file
View file

@ -0,0 +1,38 @@
ENTRY(start)
SECTIONS {
. = 1M;
kernel_start = .;
.boot BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
text_start = .;
.text BLOCK(4K) : ALIGN(4K)
{
*(.text)
}
text_end = .;
.bss BLOCK(4K) : ALIGN(4K)
{
*(.bss)
}
kernel_end = .;
}

View file

@ -1,17 +1,25 @@
#include "mboot.h"
#include "serial.h"
#include "shim.h"
#include <shim.h>
#include <lib.h>
#include <stdint.h>
#include <panic.h>
#include "mboot.h"
#define MBOOT_HEADER_MAGIC 0x36D76289
#define MBOOT_CMDLINE 1
#define MBOOT_MEMORY_MAP 6
#define MBOOT_ELF_SYMBOLS 9
#define MBOOT_OLD_RSDP 14
#define MBOOT_NEW_RSDP 15
extern char symtab;
#define kaddr(addr) ((uintptr_t)(&addr))
typedef unsigned char mboot_uint8_t;
typedef unsigned short mboot_uint16_t;
typedef unsigned int mboot_uint32_t;
typedef unsigned long long mboot_uint64_t;
typedef uint8_t mboot_uint8_t;
typedef uint16_t mboot_uint16_t;
typedef uint32_t mboot_uint32_t;
typedef uint64_t mboot_uint64_t;
struct mboot_info {
mboot_uint32_t total_size;
@ -25,24 +33,17 @@ struct mboot_tag {
char data[];
};
enum mboot_tag_type {
MBOOT_CMDLINE = 0,
MBOOT_MEMORYMAP = 6,
MBOOT_SYMBOLS = 9,
MBOOT_RSDP = 14,
MBOOT_XSDP = 15,
};
struct mboot_elf_header_layout {
struct mboot_tag_elf_sections {
mboot_uint32_t type;
mboot_uint32_t size;
mboot_uint32_t num;
mboot_uint32_t entsize;
mboot_uint32_t shndx;
char elf_section_headers[];
mboot_uint16_t num;
mboot_uint16_t entsize;
mboot_uint16_t shndx;
mboot_uint16_t reserved;
char sections[];
};
struct mboot_elf_section_header {
struct mboot_tag_elf_sections_entry {
mboot_uint32_t sh_name;
mboot_uint32_t sh_type;
mboot_uint64_t sh_flags;
@ -55,45 +56,46 @@ struct mboot_elf_section_header {
mboot_uint64_t sh_entsize;
};
struct mboot_memory_segment {
struct mboot_mmap_entry {
mboot_uint64_t addr;
mboot_uint64_t len;
mboot_uint32_t type;
mboot_uint32_t reserved;
mboot_uint32_t zero;
};
struct mboot_memory_map {
mboot_uint32_t tag;
struct mboot_tag_mmap {
mboot_uint32_t type;
mboot_uint32_t size;
mboot_uint32_t entry_size;
mboot_uint32_t entry_version;
struct memory_segment entries[];
struct mboot_mmap_entry entries[];
};
struct mboot_rsdp {
mboot_uint32_t tag;
struct mboot_tag_old_rsdp {
mboot_uint32_t type;
mboot_uint32_t size;
mboot_uint8_t rsdp[];
};
struct mboot_xsdp {
mboot_uint32_t tag;
struct mboot_tag_new_rsdp {
mboot_uint32_t type;
mboot_uint32_t size;
mboot_uint8_t xsdp[];
mboot_uint8_t rsdp[];
};
struct mboot_cmdline {
mboot_uint32_t tag;
struct mboot_tag_cmdline {
mboot_uint32_t type;
mboot_uint32_t size;
char cmdline[];
mboot_uint8_t cmdline[];
};
static void read_symbols(
struct boot_info *shim_info,
struct mboot_elf_header_layout *layout
struct mboot_tag_elf_sections *sections
) {
shim_info->symbol_table = layout->elf_section_headers;
shim_info->symbol_table = sections->sections;
// struct mboot_elf_section_header *section =
// (struct mboot_elf_section_header *) (layout->elf_section_headers);
@ -130,7 +132,7 @@ static void read_symbols(
static void read_cmdline(
struct boot_info *shim_info,
struct mboot_cmdline *cmdline
struct mboot_tag_cmdline *cmdline
) {
mboot_uint32_t size = cmdline->size - 8;
if (size >= CMDLINE_MAX)
@ -139,39 +141,49 @@ static void read_cmdline(
shim_info->cmdline[size] = '\0';
}
static void read_memorymap(
static void read_memory_map(
struct boot_info *shim_info,
struct mboot_memory_map *map
struct mboot_tag_mmap *map
) {
int size = map->size - sizeof(mboot_uint32_t) * 4;
int count = size / map->entry_size;
shim_info->map.entry_count = count;
shim_info->map.entry_length = map->entry_size;
shim_info->map.entries = map->entries;
int idx = 0;
uintptr_t i = (uintptr_t)map->entries;
for ( ;
i < (uintptr_t)map->entries + map->size;
i += map->entry_size, idx++
) {
struct mboot_mmap_entry *seg = (struct mboot_mmap_entry *) i;
shim_info->map.entries[idx].addr = seg->addr;
shim_info->map.entries[idx].type = seg->type;
shim_info->map.entries[idx].len = seg->len;
}
shim_info->map.entry_count = idx;
}
static void read_rsdp(
static void read_old_rsdp(
struct boot_info *shim_info,
struct mboot_rsdp *rsdp
struct mboot_tag_old_rsdp *rsdp
) {
if (shim_info->acpi_table != NULL)
return; // xsdp is newer and has been loaded
shim_info->acpi_table = rsdp->rsdp;
}
static void read_xsdp(
static void read_new_rsdp(
struct boot_info *shim_info,
struct mboot_xsdp *xsdp
struct mboot_tag_new_rsdp *rsdp
) {
shim_info->acpi_table = xsdp->xsdp;
shim_info->acpi_table = rsdp->rsdp;
}
void mboot_load_info(
long mboot_magic,
const void *mboot_data_ptr,
struct boot_info *shim_info
) {
if (mboot_magic != MBOOT_HEADER_MAGIC)
panic("invalid multiboot magic");
memset(shim_info, 0, sizeof(struct boot_info));
struct mboot_info *mboot_info = (struct mboot_info *) mboot_data_ptr;
@ -186,31 +198,31 @@ void mboot_load_info(
case MBOOT_CMDLINE:
read_cmdline(
shim_info,
(struct mboot_cmdline *) tag
(struct mboot_tag_cmdline *) tag
);
break;
case MBOOT_MEMORYMAP:
read_memorymap(
case MBOOT_MEMORY_MAP:
read_memory_map(
shim_info,
(struct mboot_memory_map *) tag
(struct mboot_tag_mmap *) tag
);
break;
case MBOOT_SYMBOLS:
case MBOOT_ELF_SYMBOLS:
read_symbols(
shim_info,
(struct mboot_elf_header_layout *) tag
(struct mboot_tag_elf_sections *) tag
);
break;
case MBOOT_RSDP:
read_rsdp(
case MBOOT_OLD_RSDP:
read_old_rsdp(
shim_info,
(struct mboot_rsdp *) tag
(struct mboot_tag_old_rsdp *) tag
);
break;
case MBOOT_XSDP:
read_xsdp(
case MBOOT_NEW_RSDP:
read_new_rsdp(
shim_info,
(struct mboot_xsdp *) tag
(struct mboot_tag_new_rsdp *) tag
);
break;
default:

View file

@ -8,4 +8,4 @@
* @param mboot_info - the pointer passed from multiboot2
* @param shim_info - the info to be collected by shim
*/
void mboot_load_info(const void *mboot_info, struct boot_info *shim_info);
void mboot_load_info(long mboot_magic, const void *mboot_info, struct boot_info *shim_info);

View file

@ -16,7 +16,7 @@ int serial_init(void) {
// outb(port + 4, 0x0B); // TODO copied this from osdev wiki but i don't think you need it here
outb(PORT + 4, 0x1E); // set in loopback mode for test
outb(PORT + 0, 0xAE); // test by sending 0xAE
uint8_t response = inb(PORT + 0);
if(response != 0xAE) {
// TODO panic here?
@ -40,7 +40,7 @@ void serial_out(uint8_t ch) {
outb(PORT, ch);
}
void serial_out_str(char *str) {
void serial_out_str(const char *str) {
for(; *str != '\0'; str++) {
serial_out(*str);
}

View file

@ -11,14 +11,13 @@
static struct boot_info boot_info;
void* amd64_shim(void *mboot_data_ptr) {
void* amd64_shim(long mboot_magic, void *mboot_data_ptr) {
serial_init();
paging_init();
pic_remap();
idt_init();
mboot_load_info(mboot_data_ptr, &boot_info);
mboot_load_info(mboot_magic, mboot_data_ptr, &boot_info);
return &boot_info;
}

View file

@ -10,11 +10,13 @@ void kmain(struct boot_info *info) {
//acpi_init(info->acpi_table);
//fb_init(1024, 768);
serial_out_str("entered kmain\n");
kprintf("enterd kmain\n");
*(char*)(0xB8000 + 0x144) = 'h';
*(char*)(0xB8000 + 0x146) = 'i';
while (1) {
//kprintf("ret: 0x%p\n", kalloc(2));
// loop so we dont halt
// this allows interrupts to fire
}

View file

@ -1,5 +1,5 @@
#include "memory.h"
#include <lib.h>
#include <stddef.h>
int memcmp(const void *restrict vl, const void *restrict vr, unsigned long n) {
@ -31,6 +31,7 @@ void *memmove(void *dest, const void *src, unsigned long n) {
}
void *memset(void *restrict dest, int c, unsigned long n) {
unsigned char *d = dest;
for (; n; n--) *d++ = c;
return dest;
@ -149,6 +150,7 @@ int ctoi(char c) {
UXTOA(int, utoa)
UXTOA(long int, ultoa)
UXTOA(long long int, ulltoa)
#define XTOA(type, name) \
char *name(type n, char* buffer, int radix) { \
@ -176,6 +178,7 @@ UXTOA(long int, ultoa)
XTOA(int, itoa)
XTOA(long int, ltoa)
XTOA(long long int, lltoa)
#define STRTOX(name, type) \
type name(const char *s, char **endptr, int radix) { \

View file

@ -1,10 +1,8 @@
#include "serial.h"
#include <memory.h>
#include <stdint.h>
#include <lib.h>
#ifdef MEMORY_PANIC
#include <panic.h>
#endif
#define MAGIC 0xBEEFCAFE
@ -18,8 +16,8 @@ struct page_header {
};
static const size_t header_len = sizeof(struct page_header);
struct page_header *start_header = NULL;
struct page_header *end_header = NULL;
static struct page_header *start_header = NULL;
static struct page_header *end_header = NULL;
struct page_header* get_header(void *ptr) {
struct page_header *header =
@ -36,6 +34,7 @@ struct page_header* get_header(void *ptr) {
void *kalloc_new(size_t size) {
size_t pages = ((size + header_len) / PAGE_SIZE) + 1;
void *addr = alloc_pages(pages);
void *mem = (char *)addr + header_len;
@ -54,20 +53,20 @@ void *kalloc_new(size_t size) {
}
struct page_header *header = addr;
header->magic = MAGIC;
header->magic = 0xBEEFCAFE;
header->used = size;
header->free = free;
header->prev = end_header;
header->next = NULL;
header->node_number = node;
if (end_header == NULL) {
if (start_header == NULL) {
start_header = header;
} else {
end_header->next = header;
}
end_header = header;
if (end_header != NULL) {
end_header->next = header;
}
return mem;
}
@ -97,6 +96,9 @@ void *kalloc(size_t size) {
for (; header != NULL; header = header->next) {
size_t free = header->free;
if (free < header_len) {
continue;
}
if (size <= (free - header_len)) { // we must be able to fit data + header
break;
}
@ -129,21 +131,13 @@ void *krealloc(void *src, size_t dst_len) {
header = get_header(src);
if (header == NULL) {
#ifdef MEMORY_PANIC
panic("attempted to realloc on a invalid ptr");
#else
return NULL; // invalid pointer passed
#endif
}
src_len = header->used;
if (src_len == 0) {
#ifdef MEMORY_PANIC
panic("attempted to realloc on an empty ptr");
#else
return NULL; // likely double free :(
#endif
}
dst = kalloc(dst_len);
@ -166,11 +160,7 @@ void kfree(void *ptr) {
header = get_header(ptr);
if (header == NULL || header->used == 0) {
#ifdef MEMORY_PANIC
panic("attempted to kfree invalid pointer");
#else
return;
#endif
}
header->free += header->used;

View file

@ -1,3 +1,4 @@
#include "serial.h"
#include <memory.h>
#include <stdint.h>
#include <lib.h>
@ -63,6 +64,10 @@ static inline bool bitmap_get(int i) {
}
static inline void bitmap_set(int i, bool v) {
if (v)
free_memory -= PAGE_SIZE;
else
free_memory += PAGE_SIZE;
int idx = i / 64;
bitmap[idx] &= ~(1 << i % 64);
bitmap[idx] |= (v << i % 64);
@ -115,11 +120,11 @@ void free_phys_pages(void *ptr, int pages) {
}
static bool segment_invalid(const struct memory_segment *segment) {
if (segment->type != 1) return false;
if (segment->addr < kaddr(kernel_start)) return false;
if (segment->addr + segment->len < memory_start) return false;
if (segment->addr + segment->len < kaddr(kernel_start)) return false;
return true;
if (segment->type != 1) return true;
if (segment->addr < kaddr(kernel_start)) return true;
if (segment->addr + segment->len < memory_start) return true;
if (segment->addr + segment->len < kaddr(kernel_start)) return true;
return false;
}
static struct memory_area segment_to_area(const struct memory_segment *segment) {
@ -195,18 +200,28 @@ void memory_init(struct memory_map *map) {
struct memory_area *area = page_start;
kprintf("MEMORY MAP\n");
for(uint32_t i = 0; i < map->entry_count; i++) {
struct memory_segment *segment = &map->entries[i];
if (segment_invalid(segment))
continue;
kprintf("addr: 0x%16p\tlen: %ld\n", (void *)segment->addr, segment->len);
struct memory_area temp = segment_to_area(segment);
*area = temp;
area++;
}
total_memory = page_count * PAGE_SIZE;
page_count -= bitmap_pages;
free_memory = page_count * PAGE_SIZE;
kprintf("\nMEMORY USAGE\n");
kprintf("mem total: %ld\n", memory_total());
kprintf("mem free: %ld\n", memory_free());
kprintf("mem used: %ld\n\n", memory_used());
memory_unlock();

View file

@ -1,6 +1,9 @@
#include "lib.h"
#include "panic.h"
#include <stdint.h>
#include <stddef.h>
#include <memory.h>
#include <string.h>
#define MEMORY_INTERNAL
#include <memory/virtalloc.h>
@ -10,34 +13,60 @@ struct addr_node {
uintptr_t end;
struct addr_node *next;
struct addr_node *prev;
uint8_t is_alloc;
uint8_t is_bss;
uint8_t is_alloc; // if node is storing allocated data
uint8_t is_used; // if node is in use by virtalloc
};
#define BOOTSTRAP_BSS_NODES 16
static uint8_t bss_nodes = 0;
static struct addr_node nodes[BOOTSTRAP_BSS_NODES];
#define BSS_NODES 64
static struct addr_node bootstrap_nodes[BSS_NODES];
static struct addr_node *alloc_nodes = NULL;
static size_t free_node_start = 0;
static size_t alloc_node_count = 0;
static size_t used_node_count = 0;
static bool is_allocating = false;
static struct addr_node *start_node;
static struct addr_node *start_node = NULL;
static struct addr_node *alloc_node(void) {
struct addr_node *node = NULL;
if (bss_nodes >= BOOTSTRAP_BSS_NODES) {
node = kalloc(sizeof(struct addr_node));
if (node == NULL)
return NULL;
node->is_bss = false;
static struct addr_node *get_node_idx(int idx) {
if (idx < BSS_NODES) {
return &bootstrap_nodes[idx];
} else {
node = &nodes[bss_nodes];
bss_nodes += 1;
node->is_bss = true;
return &alloc_nodes[idx - BSS_NODES];
}
return node;
}
static struct addr_node *get_node(void) {
size_t count = BSS_NODES + alloc_node_count;
if (!is_allocating && used_node_count + 16 >= count) {
is_allocating = true;
int new_alloc = alloc_node_count * 2;
if (new_alloc < 8)
new_alloc = 8;
int allocated = new_alloc - alloc_node_count;
int old = new_alloc - allocated;
alloc_nodes = krealloc(alloc_nodes, sizeof(struct addr_node) * new_alloc);
memset(alloc_nodes + old, 0, sizeof(struct addr_node) + allocated);;
alloc_node_count = new_alloc;
is_allocating = false;
count = BSS_NODES + alloc_node_count;
}
size_t idx = free_node_start;
for (; idx < count; idx++) {
struct addr_node *node = get_node_idx(idx);
if (!node->is_used) {
used_node_count++;
return node;
}
}
panic("could not get virtaddr node");
}
static void free_node(struct addr_node *node) {
if (!node->is_bss)
kfree(node);
node->is_used = false;
used_node_count--;
}
void virtaddr_init(void) {
@ -47,11 +76,11 @@ void virtaddr_init(void) {
.next = NULL,
.prev = NULL,
.is_alloc = false,
.is_bss = true,
.is_used = true,
};
nodes[0] = init;
start_node = &nodes[0];
bss_nodes++;
memset(bootstrap_nodes, 0, sizeof(bootstrap_nodes));
bootstrap_nodes[0] = init;
start_node = &bootstrap_nodes[0];
}
void *virtaddr_alloc(int n_pages) {
@ -69,10 +98,7 @@ void *virtaddr_alloc(int n_pages) {
long length = node->end - node->start;
if (length >= n_length) {
struct addr_node *new = alloc_node();
if (node == NULL) {
return NULL;
}
struct addr_node *new = get_node();
if (node->prev != NULL) {
node->prev->next = new;
}
@ -82,6 +108,7 @@ void *virtaddr_alloc(int n_pages) {
new->end = new->start + n_length;
node->start = new->end;
new->is_alloc = true;
new->is_used = true;
new->next = node;
return (void *) new->start;
}

564
src/print.c Normal file
View file

@ -0,0 +1,564 @@
#include <lib.h>
#include <serial.h>
#include <stdarg.h>
void kputc(char c) {
serial_out(c);
}
void kputs(const char *s) {
serial_out_str(s);
}
enum format_flag {
FLG_NONE = 0x00,
FLG_ALTERNATE = 0x01,
FLG_ZERO = 0x02,
FLG_LEFT_ALIGN = 0x04,
FLG_ADD_SIGN = 0x08
};
struct format_width {
bool defined;
bool varys;
int value;
};
struct format_precision {
bool defined;
bool varys;
int value;
};
enum format_modifier {
MOD_NONE,
MOD_INVALID,
MOD_HALF_HALF,
MOD_HALF,
MOD_LONG_LONG,
MOD_LONG,
};
enum format_conversion {
FMT_INT,
FMT_UINT,
FMT_OCT,
FMT_HEX,
FMT_HEX_UPPER,
FMT_CHAR,
FMT_STR,
FMT_PTR,
FMT_PTR_UPPER,
FMT_PERCENT,
FMT_INVALID
};
static enum format_flag read_flag(const char *format, const char **end) {
enum format_flag flag = FLG_NONE;
for (; *format != '\0'; format++) {
switch (*format) {
case '#':
flag |= FLG_ALTERNATE;
break;
case '0':
flag |= FLG_ZERO;
break;
case '-':
flag |= FLG_LEFT_ALIGN;
break;
case '+':
flag |= FLG_ADD_SIGN;
break;
default:
*end = format;
return flag;
}
}
*end = format;
return flag;
}
static struct format_width read_width(const char *format, const char **end) {
struct format_width width;
width.defined = false;
width.varys = false;
width.value = 0;
int value = 0;
for (; *format != '\0'; format++) {
char c = *format;
if (c == '*' && width.defined == false) {
width.defined = true;
width.varys = true;
break;
}
if (!isdigit(c))
break;
int i = c - '0';
value *= 10;
value += i;
width.value = value;
width.defined = true;
width.varys = false;
}
*end = format;
return width;
}
static struct format_precision read_precision(const char *format, const char **end) {
struct format_precision precision;
precision.varys = false;
precision.defined = false;
precision.value = 0;
if (*format != '.') {
*end = format;
return precision;
}
format++;
int value = 0;
for (; *format != '\0'; format++) {
char c = *format;
if (c == '*' && precision.defined == false) {
precision.defined = true;
precision.varys = true;
break;
}
if (!isdigit(c))
break;
int i = c - '0';
value *= 10;
value += i;
precision.value = value;
precision.defined = true;
precision.varys = false;
}
*end = format;
return precision;
}
static enum format_modifier read_modifier(const char *format, const char **end) {
enum format_modifier mod = MOD_NONE;
for (; *format != '\0'; format++) {
*end = format;
switch (*format) {
case 'l':
if (mod == MOD_NONE)
mod = MOD_LONG;
else if (mod == MOD_LONG)
return MOD_LONG_LONG;
else
return MOD_INVALID;
break;
case 'L':
if (mod == MOD_NONE)
return MOD_LONG_LONG;
else
return MOD_INVALID;
break;
case 'h':
if (mod == MOD_NONE)
mod = MOD_HALF;
else if (mod == MOD_HALF)
return MOD_HALF_HALF;
else
return MOD_INVALID;
break;
case 'H':
if (mod == MOD_NONE)
return MOD_HALF_HALF;
else
return MOD_INVALID;
break;
default:
return mod;
}
}
return MOD_INVALID;
}
static enum format_conversion read_conversion(const char *format, const char **end) {
*end = format + 1;
switch (*format) {
case 'd':
case 'i':
return FMT_INT;
case 'o':
return FMT_OCT;
case 'u':
return FMT_UINT;
case 'x':
return FMT_HEX;
case 'X':
return FMT_HEX_UPPER;
case 'c':
return FMT_CHAR;
case 's':
return FMT_STR;
case 'p':
return FMT_PTR;
case 'P':
return FMT_PTR_UPPER;
case '%':
return FMT_PERCENT;
default:
return FMT_INVALID;
}
}
static void print_string(
const char *str,
enum format_flag flag,
struct format_width width,
struct format_precision precision
) {
size_t max_len = 0;
size_t min_len = 0;
size_t len = 0;
if (width.defined)
min_len = width.value;
if (precision.defined) {
max_len = precision.value;
len = max_len;
if (max_len < min_len)
min_len = max_len;
} else {
len = strlen(str);
}
if (!(flag & FLG_LEFT_ALIGN) && len < min_len) {
for (size_t i = 0; i < (min_len - len); i++) {
kputc(' ');
}
}
for (size_t i = 0; i < len; i++) {
kputc(str[i]);
}
if ((flag & FLG_LEFT_ALIGN) && len < min_len) {
for (size_t i = 0; i < (min_len - len); i++) {
kputc(' ');
}
}
}
static char get_letter(
char c,
char base
) {
if (c >= 0 && c <= 9)
return c + '0';
c -= 10;
return c + base;
}
static char *get_decimal(
long long num,
char *buf,
char sign,
int radix,
char base
) {
*buf = '\0';
buf--;
if (num == 0) {
*buf = '0';
buf--;
}
while (num != 0) {
char i = num % radix;
char c = get_letter(i, base);
*buf = c;
buf--;
num /= radix;
}
if (sign) {
*buf = sign;
buf--;
}
buf++;
return buf;
}
static void print_unum(
unsigned long long num,
enum format_flag flag,
struct format_width width,
struct format_precision precision,
bool isneg,
int radix,
char base
) {
size_t max_len = 0;
size_t min_len = 0;
size_t len = 0;
char sign = 0;
if (isneg)
sign = '-';
else if (flag & FLG_ADD_SIGN)
sign = '+';
char buf[1024];
char *str = get_decimal(
num,
buf,
sign,
radix,
base
);
bool space_pre = (flag & FLG_LEFT_ALIGN) || !(flag & FLG_ZERO);
if (space_pre && radix == 16 && flag & FLG_ALTERNATE) {
char x = base + ('x' - 'a');
serial_out('0');
serial_out(x);
}
if (width.defined)
min_len = width.value;
if (precision.defined) {
max_len = precision.value;
len = max_len;
if (max_len < min_len)
min_len = max_len;
} else {
len = strlen(str);
}
bool zero_padded = false;
if (!(flag & FLG_LEFT_ALIGN) && len < min_len) {
for (size_t i = 0; i < (min_len - len); i++) {
(flag & FLG_ZERO) ? kputc('0') : kputc(' ');
}
if (flag & FLG_ZERO)
zero_padded = true;
}
kputs(str);
if (!zero_padded && (flag & FLG_ALTERNATE) && radix == 8)
kputc('0');
if ((flag & FLG_LEFT_ALIGN) && len < min_len) {
for (size_t i = 0; i < (min_len - len); i++) {
(flag & FLG_ZERO) ? kputc('0') : kputc(' ');
}
}
}
static void print_num(
long long num,
enum format_flag flag,
struct format_width width,
struct format_precision precision,
int radix,
char base
) {
bool isneg = false;
if (num < 0) {
num = ~num;
isneg = true;
}
print_unum(
num,
flag,
width,
precision,
isneg,
radix,
base
);
}
void kvprintf(const char *format, va_list args) {
for (; *format != '\0'; format++) {
char c = *format;
if (c == '%') {
enum format_flag flag;
struct format_width width;
struct format_precision precision;
enum format_modifier modifier;
enum format_conversion conversion;
const char *ptr = format + 1;
flag = read_flag(ptr, &ptr);
width = read_width(ptr, &ptr);
precision = read_precision(ptr, &ptr);
modifier = read_modifier(ptr, &ptr);
if (modifier == MOD_INVALID) {
kputc('%');
continue;
}
conversion = read_conversion(ptr, &ptr);
if (conversion == FMT_INVALID) {
kputc('%');
continue;
}
union {
unsigned long long u;
long long l;
char c;
const char *str;
void *ptr;
} data;
int radix = 0;
char base = 0;
switch (conversion) {
case FMT_INT:
if (modifier == MOD_NONE)
data.l = va_arg(args, int);
else if (modifier == MOD_HALF)
data.l = (short) va_arg(args, int);
else if (modifier == MOD_HALF_HALF)
data.l = (char) va_arg(args, int);
else if (modifier == MOD_LONG)
data.l = va_arg(args, long);
else if (modifier == MOD_LONG_LONG)
data.l = va_arg(args, long long);
radix = 10;
goto printnum;
case FMT_UINT:
case FMT_OCT:
case FMT_HEX_UPPER:
case FMT_HEX:
if (modifier == MOD_NONE)
data.u = va_arg(args, unsigned int);
else if (modifier == MOD_HALF)
data.u = (unsigned short) va_arg(args, unsigned int);
else if (modifier == MOD_HALF_HALF)
data.u = (unsigned char) va_arg(args, unsigned int);
else if (modifier == MOD_LONG)
data.u = va_arg(args, unsigned long);
else if (modifier == MOD_LONG_LONG)
data.u = va_arg(args, unsigned long long);
if (conversion == FMT_UINT) {
radix = 10;
} else if (conversion == FMT_OCT) {
radix = 8;
} else if (conversion == FMT_HEX) {
radix = 16;
base = 'a';
} else if (conversion == FMT_HEX_UPPER) {
radix = 16;
base = 'A';
}
goto printunum;
case FMT_PTR:
case FMT_PTR_UPPER:
flag |= FLG_ZERO;
data.u = va_arg(args, size_t);
radix = 16;
if (conversion == FMT_PTR)
base = 'a';
else
base = 'A';
goto printunum;
printnum:
print_num(
data.l,
flag,
width,
precision,
radix,
base
);
break;
printunum:
print_unum(
data.u,
flag,
width,
precision,
false,
radix,
base
);
break;
case FMT_CHAR: {
char buf[2];
buf[0] = (char) va_arg(args, int);
buf[1] = '\0';
print_string(
buf,
flag,
width,
precision
);
break;
}
case FMT_STR:
data.str = va_arg(args, const char*);
print_string(
data.str,
flag,
width,
precision
);
break;
case FMT_PERCENT:
kputc('%');
break;
case FMT_INVALID:
break;
}
format = ptr - 1;
} else {
kputc(c);
}
}
}
void kprintf(const char *format, ...) {
va_list args;
va_start(args, format);
kvprintf(format, args);
va_end(args);
}