mirror of
https://git.stationery.faith/corn/corn.git
synced 2024-11-24 21:00:03 +00:00
acpi, fix mboot memory map, fix kalloc, fix virtalloc node allocator, add kprintf, other changes
This commit is contained in:
parent
7a59ef37b8
commit
192a4ccd6b
17 changed files with 836 additions and 137 deletions
2
Makefile
2
Makefile
|
@ -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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
|
@ -123,6 +124,14 @@ char *itoa(int n, char *buffer, int radix);
|
|||
*/
|
||||
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
|
||||
|
@ -139,6 +148,14 @@ char *utoa(unsigned int n, char *buffer, int radix);
|
|||
*/
|
||||
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, ...);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
38
src/arch/amd64/linker.ld
Normal 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 = .;
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) { \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
564
src/print.c
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue