more user space stuff, including files and mouse packets
This commit is contained in:
parent
32524106e8
commit
b539b6302c
35 changed files with 1476 additions and 139 deletions
BIN
disk/calcite/apps/hello/pointer.pam
Normal file
BIN
disk/calcite/apps/hello/pointer.pam
Normal file
Binary file not shown.
30
include/calcite/file-streams.h
Normal file
30
include/calcite/file-streams.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/* Calcite, include/calcite/file-streams.h
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kernel-public/files.h>
|
||||
|
||||
struct file_stream;
|
||||
|
||||
enum fs_access_result open_file_stream(const char *path, struct file_stream **file_stream_out);
|
||||
void close_file_stream(struct file_stream *file_stream);
|
||||
|
||||
enum fs_access_result read_file_stream(struct file_stream *file_stream, void *buffer, uint64_t bytes);
|
||||
|
||||
//returns next byte casted to int. on error or eof, returns -1.
|
||||
int read_file_stream_byte(struct file_stream *file_stream);
|
||||
28
include/calcite/memory.h
Normal file
28
include/calcite/memory.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* Calcite, include/calcite/memory.h
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void *heap_alloc(uint64_t bytes);
|
||||
|
||||
//start should be something returned from heap_alloc,
|
||||
//unless this is called (carefully) from inside memory.c
|
||||
void heap_dealloc(void *start, uint64_t bytes);
|
||||
|
||||
void memcpy(void *to, const void *from, uint64_t bytes);
|
||||
38
include/calcite/syscalls.h
Normal file
38
include/calcite/syscalls.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/* Calcite, include/calcite/syscalls.h
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kernel-public/framebuffer.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <kernel-public/input.h>
|
||||
|
||||
[[noreturn]] void end_thread();
|
||||
|
||||
void map_framebuffer(struct framebuffer_info *info_out);
|
||||
|
||||
enum fs_access_result open_file(const char *path, file_handle_t *handle_out);
|
||||
void close_file(file_handle_t handle);
|
||||
|
||||
enum fs_access_result get_file_size(file_handle_t handle, uint64_t *bytes_out);
|
||||
|
||||
enum fs_access_result read_file(struct read_file_parameter *parameter);
|
||||
enum fs_access_result read_file_splat(file_handle_t handle, void *buffer, uint64_t start, uint64_t bytes);
|
||||
|
||||
void wait_for_mouse_packet(struct mouse_packet *packet_out);
|
||||
|
||||
void *map_pages(uint64_t count);
|
||||
40
include/kernel-public/files.h
Normal file
40
include/kernel-public/files.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* Calcite, include/kernel-public/files.h
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
enum fs_access_result {
|
||||
FAR_SUCCESS,
|
||||
FAR_HARDWARE_ERROR,
|
||||
FAR_FORMAT_ERROR,
|
||||
FAR_UNKNOWN_FS_TYPE,
|
||||
FAR_NOT_FOUND,
|
||||
FAR_OUT_OF_BOUNDS,
|
||||
FAR_BAD_HANDLE
|
||||
};
|
||||
|
||||
typedef uint64_t file_handle_t;
|
||||
|
||||
struct read_file_parameter {
|
||||
file_handle_t handle;
|
||||
void *buffer;
|
||||
//in bytes
|
||||
uint64_t start;
|
||||
uint64_t bytes;
|
||||
};
|
||||
23
include/kernel-public/input.h
Normal file
23
include/kernel-public/input.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* Calcite, include/kernel-public/input.h
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct mouse_packet {
|
||||
int x_change;
|
||||
int y_change;
|
||||
};
|
||||
|
|
@ -19,5 +19,11 @@
|
|||
|
||||
enum {
|
||||
SYSCALL_END_THREAD,
|
||||
SYSCALL_MAP_FRAMEBUFFER
|
||||
SYSCALL_MAP_FRAMEBUFFER,
|
||||
SYSCALL_OPEN_FILE,
|
||||
SYSCALL_CLOSE_FILE,
|
||||
SYSCALL_GET_FILE_SIZE,
|
||||
SYSCALL_READ_FILE,
|
||||
SYSCALL_WAIT_FOR_MOUSE_PACKET,
|
||||
SYSCALL_MAP_PAGES
|
||||
};
|
||||
|
|
|
|||
36
include/silver/image.h
Normal file
36
include/silver/image.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* Calcite, include/silver/image.h
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct pixel {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
|
||||
struct image {
|
||||
int width;
|
||||
int height;
|
||||
struct pixel *pixels;
|
||||
};
|
||||
|
||||
void create_image(int width, int height, struct image **image_out);
|
||||
void destroy_image(struct image *image);
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/* Calcite, include/calcite/calcite.h
|
||||
/* Calcite, include/silver/pam.h
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
|
@ -17,8 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <kernel-public/framebuffer.h>
|
||||
#include <silver/image.h>
|
||||
|
||||
[[noreturn]] void end_thread();
|
||||
|
||||
void map_framebuffer(struct framebuffer_info *info_out);
|
||||
//returns 0 on failure, 1 on success.
|
||||
int load_pam(const char *path, struct image **image_out);
|
||||
|
|
@ -75,13 +75,13 @@ app_deps=""
|
|||
for dir in src/user-apps/*; do
|
||||
|
||||
if [ -e $dir/libraries.txt ]; then
|
||||
lib_paths=$(cat $dir/libraries.txt | sed -e 's/^.*$/build\/user-libs\/\0\/lib\0.o/')
|
||||
lib_paths=$(cat $dir/libraries.txt | sed -e 's/^.*$/build\/user-libs\/\0\/lib\0.o/' | tr '\n' ' ')
|
||||
else
|
||||
lib_paths=""
|
||||
fi
|
||||
|
||||
app_name=$(echo $dir | sed -e 's/^src\/user-apps\///')
|
||||
build_all $dir user_cc user_app_ld $app_name.elf $lib_paths
|
||||
build_all $dir user_cc user_app_ld $app_name.elf "$lib_paths"
|
||||
|
||||
build_elf=$(echo $dir | sed -e 's/^src/build/')/$app_name.elf
|
||||
disk_dir=build/disk/calcite/apps/$app_name
|
||||
|
|
@ -103,7 +103,7 @@ echo " command =" \
|
|||
"cp dependencies/limine/limine-bios.sys build/disk/limine/ &&"\
|
||||
"mkdir -p build/disk/EFI/BOOT &&" \
|
||||
"cp dependencies/limine/BOOTX64.EFI build/disk/EFI/BOOT/ &&" \
|
||||
"mkdir build/disk/calcite &&" \
|
||||
"mkdir -p build/disk/calcite &&" \
|
||||
"cp build/kernel/kernel.elf build/disk/calcite/ &&" \
|
||||
$cp_apps \
|
||||
"xorriso -as mkisofs -R -r -J -b limine/limine-bios-cd.bin -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus -apm-block-size 2048 --efi-boot limine/limine-uefi-cd.bin -efi-boot-part --efi-boot-image --protective-msdos-label build/disk -o build/disk.iso &&" \
|
||||
|
|
|
|||
1
qemu.gdb
1
qemu.gdb
|
|
@ -1,5 +1,6 @@
|
|||
target remote | qemu-system-x86_64 -gdb stdio -cdrom build/disk.iso -boot d
|
||||
symbol-file build/kernel/kernel.elf
|
||||
add-symbol-file build/user-apps/hello/hello.elf
|
||||
set disassembly-flavor intel
|
||||
set print asm-demangle on
|
||||
layout src
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ struct drive_info {
|
|||
uint64_t block_count;
|
||||
const void *driver_info;
|
||||
|
||||
//start and count are both in blocks
|
||||
//start and count are both in blocks. bounds-checking is caller responsibility.
|
||||
enum drive_access_result (*read_blocks)(
|
||||
const struct drive_info *info, void *buffer, uint64_t start, uint64_t count);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <kernel-public/syscall-numbers.h>
|
||||
#include "framebuffer.h"
|
||||
#include "interrupts.h"
|
||||
#include "scheduler.h"
|
||||
|
|
@ -24,12 +23,14 @@
|
|||
#include "utility.h"
|
||||
#include "drives.h"
|
||||
#include "paging.h"
|
||||
#include "input.h"
|
||||
#include "panic.h"
|
||||
#include "heap.h"
|
||||
#include "pata.h"
|
||||
#include "ps2.h"
|
||||
#include "fs.h"
|
||||
|
||||
#include <kernel-public/syscall-numbers.h>
|
||||
#include <limine.h>
|
||||
|
||||
LIMINE_BASE_REVISION(3)
|
||||
|
|
@ -252,6 +253,12 @@ static const char *cmdline_look_up(const char *key) {
|
|||
init_syscalls();
|
||||
register_syscall(SYSCALL_END_THREAD, (void *)&syscall_end_thread);
|
||||
register_syscall(SYSCALL_MAP_FRAMEBUFFER, (void *)&syscall_map_framebuffer);
|
||||
register_syscall(SYSCALL_OPEN_FILE, (void *)&syscall_open_file);
|
||||
register_syscall(SYSCALL_CLOSE_FILE, (void *)&syscall_close_file);
|
||||
register_syscall(SYSCALL_GET_FILE_SIZE, (void *)&syscall_get_file_size);
|
||||
register_syscall(SYSCALL_READ_FILE, (void *)&syscall_read_file);
|
||||
register_syscall(SYSCALL_WAIT_FOR_MOUSE_PACKET, (void *)&syscall_wait_for_mouse_packet);
|
||||
register_syscall(SYSCALL_MAP_PAGES, (void *)&syscall_map_pages);
|
||||
|
||||
//probe for drives
|
||||
|
||||
|
|
@ -269,26 +276,19 @@ static const char *cmdline_look_up(const char *key) {
|
|||
const char *root_fs_type = cmdline_look_up("root-fs");
|
||||
if (!root_fs_type)
|
||||
panic("root fs missing from cmdline")
|
||||
struct fs_info root_fs;
|
||||
if (create_fs_info(root_drive, &root_fs, root_fs_type) != FAR_SUCCESS)
|
||||
struct fs_info *root_fs = heap_alloc(sizeof(struct fs_info));
|
||||
if (create_fs_info(root_drive, root_fs, root_fs_type) != FAR_SUCCESS)
|
||||
panic("could not create root file system info")
|
||||
|
||||
set_fs("root", root_fs);
|
||||
|
||||
//load hello and start it
|
||||
|
||||
init_scheduler();
|
||||
|
||||
void *hello_node;
|
||||
if ((*root_fs.look_up_file)(
|
||||
&root_fs, &hello_node,
|
||||
"calcite/apps/hello/hello.elf") != FAR_SUCCESS)
|
||||
panic("could not look up hello.elf")
|
||||
|
||||
if (!start_elf(&root_fs, hello_node))
|
||||
if (!start_elf("root://calcite/apps/hello/hello.elf"))
|
||||
panic("could not start hello.elf")
|
||||
|
||||
if ((*root_fs.free_node)(&root_fs, hello_node) != FAR_SUCCESS)
|
||||
panic("could not free hello.elf node")
|
||||
|
||||
resume_next_continuation();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,12 @@
|
|||
|
||||
#include "iso9660.h"
|
||||
#include "utility.h"
|
||||
#include "panic.h"
|
||||
#include "heap.h"
|
||||
#include "fs.h"
|
||||
|
||||
#include <kernel-public/files.h>
|
||||
|
||||
enum fs_access_result create_fs_info(
|
||||
const struct drive_info *drive, struct fs_info *fs_out, const char *fs_type) {
|
||||
|
||||
|
|
@ -28,3 +32,74 @@ enum fs_access_result create_fs_info(
|
|||
return FAR_UNKNOWN_FS_TYPE;
|
||||
|
||||
}
|
||||
|
||||
struct set_fs {
|
||||
const char *name;
|
||||
const struct fs_info *fs;
|
||||
};
|
||||
|
||||
struct set_fs *set_fses;
|
||||
int set_fses_buffer_length = 0;
|
||||
int set_fses_count = 0;
|
||||
|
||||
#define SET_FSES_INITIAL_LENGTH 16
|
||||
|
||||
void set_fs(const char *name, const struct fs_info *fs) {
|
||||
|
||||
assert(get_fs(name) == 0)
|
||||
|
||||
if (set_fses_buffer_length == 0) {
|
||||
set_fses = heap_alloc(SET_FSES_INITIAL_LENGTH * sizeof(struct set_fs));
|
||||
set_fses_buffer_length = SET_FSES_INITIAL_LENGTH;
|
||||
}
|
||||
|
||||
else if (set_fses_count == set_fses_buffer_length) {
|
||||
struct set_fs *new_set_fses = heap_alloc(2 * set_fses_buffer_length * sizeof(struct set_fs));
|
||||
memcpy(new_set_fses, set_fses, set_fses_count * sizeof(struct set_fs));
|
||||
heap_dealloc(set_fses, set_fses_count * sizeof(struct set_fs));
|
||||
set_fses = new_set_fses;
|
||||
set_fses_buffer_length *= 2;
|
||||
}
|
||||
|
||||
set_fses[set_fses_count].name = name;
|
||||
set_fses[set_fses_count].fs = fs;
|
||||
++set_fses_count;
|
||||
|
||||
}
|
||||
|
||||
const struct fs_info *get_fs(const char *name) {
|
||||
for (int i = 0; i < set_fses_count; ++i)
|
||||
if (strequ(set_fses[i].name, name))
|
||||
return set_fses[i].fs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum fs_access_result look_up_file(
|
||||
const char *uri, const struct fs_info **fs_out, void **node_out) {
|
||||
|
||||
int colon = 0;
|
||||
while (1) {
|
||||
if (uri[colon] == ':')
|
||||
break;
|
||||
if (uri[colon] == 0)
|
||||
panic("bad uri")
|
||||
++colon;
|
||||
}
|
||||
|
||||
if (uri[colon + 1] != '/' || uri[colon + 2] != '/')
|
||||
panic("bad uri")
|
||||
|
||||
char *uri_copy = heap_alloc(colon + 1);
|
||||
memcpy(uri_copy, uri, colon);
|
||||
uri_copy[colon] = 0;
|
||||
|
||||
*fs_out = get_fs(uri_copy);
|
||||
|
||||
heap_dealloc(uri_copy, colon + 1);
|
||||
|
||||
if (!*fs_out)
|
||||
return FAR_NOT_FOUND;
|
||||
|
||||
return (*(*fs_out)->look_up_file)(*fs_out, node_out, &uri[colon + 3]);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,16 +19,9 @@
|
|||
|
||||
#include "drives.h"
|
||||
|
||||
#include <kernel-public/files.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum fs_access_result {
|
||||
FAR_SUCCESS,
|
||||
FAR_HARDWARE_ERROR,
|
||||
FAR_FORMAT_ERROR,
|
||||
FAR_UNKNOWN_FS_TYPE,
|
||||
FAR_NOT_FOUND
|
||||
};
|
||||
|
||||
struct fs_stat {
|
||||
uint64_t bytes;
|
||||
};
|
||||
|
|
@ -47,7 +40,7 @@ struct fs_info {
|
|||
enum fs_access_result (*stat_file)(
|
||||
const struct fs_info *info, void *node, struct fs_stat *stat_out);
|
||||
|
||||
//start is in bytes
|
||||
//start is in bytes. bounds-checking is caller responsibility.
|
||||
enum fs_access_result (*read_file)(
|
||||
const struct fs_info *info, void *node,
|
||||
void *buffer, uint64_t start, uint64_t bytes);
|
||||
|
|
@ -56,3 +49,10 @@ struct fs_info {
|
|||
|
||||
enum fs_access_result create_fs_info(
|
||||
const struct drive_info *drive, struct fs_info *fs_out, const char *fs_type);
|
||||
|
||||
void set_fs(const char *name, const struct fs_info *fs);
|
||||
|
||||
//returns 0 if no fs is set with that name.
|
||||
const struct fs_info *get_fs(const char *name);
|
||||
|
||||
enum fs_access_result look_up_file(const char *uri, const struct fs_info **fs_out, void **node_out);
|
||||
|
|
|
|||
68
src/kernel/input.c
Normal file
68
src/kernel/input.c
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* Calcite, src/kernel/input.c
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "scheduler.h"
|
||||
#include "process.h"
|
||||
#include "input.h"
|
||||
#include "panic.h"
|
||||
|
||||
static int is_somebody_waiting_for_mouse_packet = 0;
|
||||
static struct continuation_info waiting_continuation;
|
||||
static struct mouse_packet *resume_by_reporting_to;
|
||||
|
||||
static int total_unreported_x_movement = 0;
|
||||
static int total_unreported_y_movement = 0;
|
||||
|
||||
void add_mouse_movement(int x, int y) {
|
||||
|
||||
total_unreported_x_movement += x;
|
||||
total_unreported_y_movement += y;
|
||||
|
||||
if (is_somebody_waiting_for_mouse_packet) {
|
||||
resume_by_reporting_to->x_change = total_unreported_x_movement;
|
||||
resume_by_reporting_to->y_change = total_unreported_y_movement;
|
||||
add_to_queue(&ready_continuations, &waiting_continuation);
|
||||
total_unreported_x_movement = 0;
|
||||
total_unreported_y_movement = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out) {
|
||||
|
||||
assert(running_thread != 0);
|
||||
|
||||
//TODO: handle these
|
||||
if (is_somebody_waiting_for_mouse_packet ||
|
||||
!is_mapped_writable(
|
||||
running_thread->process, packet_out, sizeof(struct mouse_packet)))
|
||||
panic("TODO")
|
||||
|
||||
if (total_unreported_x_movement != 0 || total_unreported_y_movement != 0) {
|
||||
packet_out->x_change = total_unreported_x_movement;
|
||||
packet_out->y_change = total_unreported_y_movement;
|
||||
total_unreported_x_movement = 0;
|
||||
total_unreported_y_movement = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
is_somebody_waiting_for_mouse_packet = 1;
|
||||
resume_by_reporting_to = packet_out;
|
||||
yield(&waiting_continuation);
|
||||
is_somebody_waiting_for_mouse_packet = 0;
|
||||
|
||||
}
|
||||
24
src/kernel/input.h
Normal file
24
src/kernel/input.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/* Calcite, src/kernel/input.h
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <kernel-public/input.h>
|
||||
|
||||
void add_mouse_movement(int x, int y);
|
||||
|
||||
void syscall_wait_for_mouse_packet(struct mouse_packet *packet_out);
|
||||
|
|
@ -187,3 +187,19 @@ uint64_t take_free_physical_page() {
|
|||
}
|
||||
panic("out of physical memory");
|
||||
}
|
||||
|
||||
#define SYSCALL_STACK_BYTES (1 << 15)
|
||||
|
||||
void *create_syscall_stack() {
|
||||
void *vma = find_free_kernel_region(SYSCALL_STACK_BYTES);
|
||||
for (uint64_t i = 0; i < SYSCALL_STACK_BYTES; i += 4096) {
|
||||
uint64_t pma = take_free_physical_page();
|
||||
map_in_kernel_page_table(pma, vma + i * 4096, 1, 0);
|
||||
}
|
||||
return vma + SYSCALL_STACK_BYTES;
|
||||
}
|
||||
|
||||
void destroy_syscall_stack(void *stack_top) {
|
||||
for (uint64_t i = 0; i < SYSCALL_STACK_BYTES; i += 4096)
|
||||
unmap_and_free_kernel_page(stack_top - SYSCALL_STACK_BYTES + i * 4096);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,3 +48,7 @@ uint64_t take_free_physical_page();
|
|||
|
||||
//implemented in paging.asm. the continuation should be noreturn.
|
||||
[[noreturn]] void switch_to_kernel_page_tables(void (*continuation)());
|
||||
|
||||
//returns the top
|
||||
void *create_syscall_stack();
|
||||
void destroy_syscall_stack(void *stack_top);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "framebuffer.h"
|
||||
#include "scheduler.h"
|
||||
#include "syscalls.h"
|
||||
#include "process.h"
|
||||
#include "utility.h"
|
||||
#include "paging.h"
|
||||
|
|
@ -24,6 +25,14 @@
|
|||
#include "heap.h"
|
||||
#include "fs.h"
|
||||
|
||||
#include <kernel-public/files.h>
|
||||
|
||||
struct process_file_info *get_file_info(struct process *process, file_handle_t handle) {
|
||||
if (handle > (uint64_t)process->files_buffer_size || process->files[handle].fs == 0)
|
||||
return 0;
|
||||
return &process->files[handle];
|
||||
}
|
||||
|
||||
void create_process(struct process *process_out) {
|
||||
|
||||
process_out->p4_physical_base = take_free_physical_page();
|
||||
|
|
@ -51,7 +60,8 @@ void create_process(struct process *process_out) {
|
|||
process_out->p1_virtual_bases[i] = 0;
|
||||
}
|
||||
|
||||
process_out->n_threads = 0;
|
||||
process_out->n_threads = 0;
|
||||
process_out->files = 0;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +132,7 @@ void unmap_page_for_process(
|
|||
|
||||
}
|
||||
|
||||
int is_mapped_writable(struct process *process, void *start, uint64_t length) {
|
||||
static int is_mapped_with_flags(struct process *process, const void *start, uint64_t length, uint64_t flags) {
|
||||
|
||||
uint64_t vma_start = (uint64_t)start;
|
||||
uint64_t vma_end = vma_start + length;
|
||||
|
|
@ -141,7 +151,7 @@ int is_mapped_writable(struct process *process, void *start, uint64_t length) {
|
|||
if (!process->p1_virtual_bases[p3i] ||
|
||||
!process->p1_virtual_bases[p3i][p2i] ||
|
||||
!process->p1_virtual_bases[p3i][p2i][p1i] ||
|
||||
!(process->p1_virtual_bases[p3i][p2i][p1i] & 0x2))
|
||||
(process->p1_virtual_bases[p3i][p2i][p1i] & flags) != flags)
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
|
@ -150,6 +160,30 @@ int is_mapped_writable(struct process *process, void *start, uint64_t length) {
|
|||
|
||||
}
|
||||
|
||||
int is_mapped_writable(struct process *process, const void *start, uint64_t length) {
|
||||
return is_mapped_with_flags(process, start, length, 0x7);
|
||||
}
|
||||
|
||||
int is_mapped_readable(struct process *process, const void *start, uint64_t length) {
|
||||
return is_mapped_with_flags(process, start, length, 0x5);
|
||||
}
|
||||
|
||||
int is_mapped_readable_string(struct process *process, const char *start) {
|
||||
|
||||
if (!is_mapped_readable(process, start, 1))
|
||||
return 0;
|
||||
|
||||
while (1) {
|
||||
if ((uint64_t)start % 4096 == 0)
|
||||
if (!is_mapped_readable(process, start, 1))
|
||||
return 0;
|
||||
if (*start == 0)
|
||||
return 1;
|
||||
++start;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void *find_free_process_region(
|
||||
struct process *process, uint64_t page_count) {
|
||||
|
||||
|
|
@ -293,18 +327,24 @@ int load_elf(
|
|||
// all other registers zeroed
|
||||
extern uint8_t thread_start;
|
||||
|
||||
int start_elf(const struct fs_info *fs_info, void *fs_node) {
|
||||
int start_elf(const char *uri) {
|
||||
|
||||
struct process *process = heap_alloc(sizeof(struct process));
|
||||
create_process(process);
|
||||
|
||||
const struct fs_info *fs_info;
|
||||
void *fs_node;
|
||||
uint64_t entry;
|
||||
|
||||
if (!load_elf(process, &entry, fs_info, fs_node)) {
|
||||
if (look_up_file(uri, &fs_info, &fs_node) != FAR_SUCCESS ||
|
||||
!load_elf(process, &entry, fs_info, fs_node)) {
|
||||
destroy_process(process);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((*fs_info->free_node)(fs_info, fs_node) != FAR_SUCCESS)
|
||||
panic("TODO")
|
||||
|
||||
struct thread *thread = heap_alloc(sizeof(struct thread));
|
||||
create_thread(process, thread);
|
||||
|
||||
|
|
@ -315,12 +355,21 @@ int start_elf(const struct fs_info *fs_info, void *fs_node) {
|
|||
ci.rsp = (uint64_t)thread->stack_top;
|
||||
ci.r12 = entry;
|
||||
|
||||
add_ready_continuation(&ci);
|
||||
add_to_queue(&ready_continuations, &ci);
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
void destroy_process(struct process *process) {
|
||||
|
||||
if (process->files) {
|
||||
for (int i = 0; i < process->files_buffer_size; ++i)
|
||||
if (process->files[i].fs != 0)
|
||||
(*process->files[i].fs->free_node)(process->files[i].fs, process->files[i].node);
|
||||
heap_dealloc(process->files, process->files_buffer_size * sizeof(struct process_file_info));
|
||||
}
|
||||
|
||||
for (int p3i = 0; p3i < 512; ++p3i)
|
||||
if (process->p3_virtual_base[p3i]) {
|
||||
for (int p2i = 0; p2i < 512; ++p2i)
|
||||
|
|
@ -341,6 +390,7 @@ void destroy_process(struct process *process) {
|
|||
unmap_and_free_kernel_page(process->p3_virtual_base);
|
||||
unmap_and_free_kernel_page(process->p4_virtual_base);
|
||||
heap_dealloc(process, sizeof(struct process));
|
||||
|
||||
}
|
||||
|
||||
void destroy_thread(struct thread *thread) {
|
||||
|
|
@ -393,21 +443,32 @@ void create_thread(struct process *process, struct thread *thread_out) {
|
|||
|
||||
}
|
||||
|
||||
[[noreturn]] static void syscall_illegal_args() {
|
||||
panic("bad syscall")
|
||||
}
|
||||
|
||||
struct thread *running_thread = 0;
|
||||
|
||||
[[noreturn]] static void syscall_end_thread_with_temporary_stack() {
|
||||
destroy_syscall_stack(most_recent_syscall_stack);
|
||||
resume_next_continuation();
|
||||
}
|
||||
|
||||
[[noreturn]] void syscall_end_thread() {
|
||||
assert(running_thread != 0)
|
||||
destroy_thread(running_thread);
|
||||
running_thread = 0;
|
||||
resume_next_continuation();
|
||||
with_temporary_stack(&syscall_end_thread_with_temporary_stack);
|
||||
}
|
||||
|
||||
void syscall_map_framebuffer(struct framebuffer_info *info_out) {
|
||||
|
||||
assert(running_thread != 0)
|
||||
|
||||
if (!is_mapped_writable(
|
||||
running_thread->process,
|
||||
info_out, sizeof(struct framebuffer_info)))
|
||||
panic("bad syscall");
|
||||
syscall_illegal_args();
|
||||
|
||||
uint64_t pages_needed = (fb_pitch * fb_height - 1) / 4096 + 1;
|
||||
|
||||
|
|
@ -425,3 +486,128 @@ void syscall_map_framebuffer(struct framebuffer_info *info_out) {
|
|||
info_out->fb_width = fb_width;
|
||||
|
||||
}
|
||||
|
||||
#define INITIAL_FILE_HANDLE_COUNT 128
|
||||
|
||||
enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_out) {
|
||||
|
||||
assert(running_thread != 0)
|
||||
|
||||
if (!is_mapped_readable_string(running_thread->process, path) ||
|
||||
!is_mapped_writable(running_thread->process, handle_out, sizeof(file_handle_t)))
|
||||
syscall_illegal_args();
|
||||
|
||||
const struct fs_info *fs;
|
||||
void *node;
|
||||
enum fs_access_result result = look_up_file(path, &fs, &node);
|
||||
if (result != FAR_SUCCESS)
|
||||
return result;
|
||||
|
||||
struct fs_stat stat;
|
||||
result = (*fs->stat_file)(fs, node, &stat);
|
||||
if (result != FAR_SUCCESS) {
|
||||
(*fs->free_node)(fs, node);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (running_thread->process->files == 0) {
|
||||
running_thread->process->files =
|
||||
heap_alloc(INITIAL_FILE_HANDLE_COUNT * sizeof(struct process_file_info));
|
||||
running_thread->process->files_buffer_size = INITIAL_FILE_HANDLE_COUNT;
|
||||
for (int i = 0; i < INITIAL_FILE_HANDLE_COUNT; ++i)
|
||||
running_thread->process->files[i].fs = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < running_thread->process->files_buffer_size; ++i)
|
||||
if (running_thread->process->files[i].fs == 0) {
|
||||
running_thread->process->files[i].fs = fs;
|
||||
running_thread->process->files[i].node = node;
|
||||
memcpy(&running_thread->process->files[i].stat, &stat, sizeof(struct fs_stat));
|
||||
*handle_out = i;
|
||||
return FAR_SUCCESS;
|
||||
}
|
||||
|
||||
struct process_file_info *old_buffer = running_thread->process->files;
|
||||
int old_size = running_thread->process->files_buffer_size;
|
||||
|
||||
struct process_file_info *new_buffer = heap_alloc(2 * old_size * sizeof(struct process_file_info));
|
||||
memcpy(new_buffer, old_buffer, old_size * sizeof(struct process_file_info));
|
||||
heap_dealloc(old_buffer, old_size * sizeof(struct process_file_info));
|
||||
|
||||
new_buffer[old_size].fs = fs;
|
||||
new_buffer[old_size].node = node;
|
||||
memcpy(&new_buffer[old_size].stat, &stat, sizeof(struct fs_stat));
|
||||
for (int i = old_size + 1; i < old_size * 2; ++i)
|
||||
new_buffer[i].fs = 0;
|
||||
|
||||
running_thread->process->files = new_buffer;
|
||||
running_thread->process->files_buffer_size *= 2;
|
||||
|
||||
*handle_out = old_size;
|
||||
return FAR_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
void syscall_close_file(file_handle_t handle) {
|
||||
|
||||
assert(running_thread != 0)
|
||||
|
||||
struct process_file_info *file = get_file_info(running_thread->process, handle);
|
||||
if (file != 0) {
|
||||
(*file->fs->free_node)(file->fs, file->node);
|
||||
file->fs = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum fs_access_result syscall_get_file_size(file_handle_t handle, uint64_t *bytes_out) {
|
||||
|
||||
assert(running_thread != 0)
|
||||
|
||||
struct process_file_info *file = get_file_info(running_thread->process, handle);
|
||||
if (file == 0)
|
||||
return FAR_BAD_HANDLE;
|
||||
|
||||
*bytes_out = file->stat.bytes;
|
||||
return FAR_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
enum fs_access_result syscall_read_file(struct read_file_parameter *parameter) {
|
||||
|
||||
assert(running_thread != 0)
|
||||
|
||||
if (!is_mapped_readable(
|
||||
running_thread->process, parameter, sizeof(struct read_file_parameter)) ||
|
||||
!is_mapped_writable(
|
||||
running_thread->process, parameter->buffer, parameter->bytes))
|
||||
syscall_illegal_args();
|
||||
|
||||
struct process_file_info *file = get_file_info(running_thread->process, parameter->handle);
|
||||
|
||||
if (file == 0)
|
||||
return FAR_BAD_HANDLE;
|
||||
|
||||
if (parameter->start + parameter->bytes > file->stat.bytes)
|
||||
return FAR_OUT_OF_BOUNDS;
|
||||
|
||||
return (*file->fs->read_file)(file->fs, file->node, parameter->buffer, parameter->start, parameter->bytes);
|
||||
|
||||
}
|
||||
|
||||
void *syscall_map_pages(uint64_t count) {
|
||||
|
||||
assert(running_thread != 0)
|
||||
|
||||
void *vma = find_free_process_region(running_thread->process, count);
|
||||
for (uint64_t i = 0; i < count; ++i) {
|
||||
uint64_t pma = take_free_physical_page();
|
||||
map_page_for_process(
|
||||
running_thread->process,
|
||||
pma, vma + i * 4096,
|
||||
1, 0);
|
||||
}
|
||||
|
||||
return vma;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,17 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <kernel-public/framebuffer.h>
|
||||
#include "fs.h"
|
||||
|
||||
#include <kernel-public/framebuffer.h>
|
||||
#include <kernel-public/files.h>
|
||||
|
||||
struct process_file_info {
|
||||
const struct fs_info *fs;
|
||||
void *node;
|
||||
struct fs_stat stat;
|
||||
};
|
||||
|
||||
struct process {
|
||||
|
||||
uint64_t p4_physical_base;
|
||||
|
|
@ -29,12 +37,21 @@ struct process {
|
|||
|
||||
int n_threads;
|
||||
|
||||
//handles are indices into this.
|
||||
//0 for fs means unused handle.
|
||||
struct process_file_info *files;
|
||||
int files_buffer_size;
|
||||
|
||||
//0 for missing levels. just bottom p3 of address space.
|
||||
uint64_t *p2_virtual_bases[512];
|
||||
uint64_t **p1_virtual_bases[512];
|
||||
|
||||
};
|
||||
|
||||
//returns 0 if that handle is not used.
|
||||
//return value might be invalidated by future allocation of file handles for this process.
|
||||
struct process_file_info *get_file_info(struct process *process, file_handle_t handle);
|
||||
|
||||
struct thread {
|
||||
|
||||
struct process *process;
|
||||
|
|
@ -74,14 +91,27 @@ int load_elf(
|
|||
//creates a process and a thread in that process, loads the elf into the process,
|
||||
//and schedules a ready task that sets the running thread to the new thread and
|
||||
//starts user mode at the elf's entry point.
|
||||
int start_elf(const struct fs_info *fs_info, void *fs_node);
|
||||
int start_elf(const char *uri);
|
||||
|
||||
void destroy_process(struct process *process);
|
||||
void destroy_thread(struct thread *thread);
|
||||
|
||||
//returs 1 if [start, start + length) is writable by process, otherwise 0.
|
||||
int is_mapped_writable(struct process *process, void *start, uint64_t length);
|
||||
int is_mapped_writable(struct process *process, const void *start, uint64_t length);
|
||||
|
||||
//returs 1 if [start, start + length) is readable by process, otherwise 0.
|
||||
int is_mapped_readable(struct process *process, const void *start, uint64_t length);
|
||||
|
||||
//return 1 if the entire null-terminated string starting at start is readable by process, otherwise 0.
|
||||
int is_mapped_readable_string(struct process *process, const char *start);
|
||||
|
||||
[[noreturn]] void syscall_end_thread();
|
||||
|
||||
void syscall_map_framebuffer(struct framebuffer_info *info_out);
|
||||
|
||||
enum fs_access_result syscall_open_file(const char *path, file_handle_t *handle_out);
|
||||
void syscall_close_file(file_handle_t handle);
|
||||
enum fs_access_result syscall_get_file_size(file_handle_t handle, uint64_t *bytes_out);
|
||||
enum fs_access_result syscall_read_file(struct read_file_parameter *parameter);
|
||||
|
||||
void *syscall_map_pages(uint64_t count);
|
||||
|
|
|
|||
|
|
@ -15,10 +15,11 @@
|
|||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "framebuffer.h"
|
||||
#include "panic.h"
|
||||
#include "input.h"
|
||||
#include "ps2.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//defined in ps2.asm
|
||||
//returns -1 if no byte available
|
||||
int read_ps2_byte();
|
||||
|
|
@ -36,9 +37,6 @@ void on_keyboard_irq() {
|
|||
static uint8_t mouse_packet[3];
|
||||
static int mouse_packet_length = 0;
|
||||
|
||||
static int total_x = 0;
|
||||
static int total_y = 0;
|
||||
|
||||
void on_mouse_irq() {
|
||||
|
||||
int byte = read_ps2_byte();
|
||||
|
|
@ -52,29 +50,14 @@ void on_mouse_irq() {
|
|||
}
|
||||
mouse_packet_length = 0;
|
||||
|
||||
int x = mouse_packet[1];
|
||||
int x_change = mouse_packet[1];
|
||||
if (mouse_packet[0] & 0x10)
|
||||
x -= 256;
|
||||
x_change -= 256;
|
||||
|
||||
int y = mouse_packet[2];
|
||||
int y_change = mouse_packet[2];
|
||||
if (mouse_packet[0] & 0x20)
|
||||
y -= 256;
|
||||
y_change -= 256;
|
||||
|
||||
total_x += x;
|
||||
total_y -= y;
|
||||
|
||||
if (total_x < 0)
|
||||
total_x = 0;
|
||||
if (total_x >= fb_width)
|
||||
total_x = fb_width - 1;
|
||||
|
||||
if (total_y < 0)
|
||||
total_y = 0;
|
||||
if (total_y >= fb_height)
|
||||
total_y = fb_height - 1;
|
||||
|
||||
fb_base[total_y * fb_pitch + total_x * 4] = 0xff;
|
||||
fb_base[total_y * fb_pitch + total_x * 4 + 1] = 0xff;
|
||||
fb_base[total_y * fb_pitch + total_x * 4 + 2] = 0xff;
|
||||
add_mouse_movement(x_change, y_change);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
bits 64
|
||||
|
||||
extern resume_next_continuation
|
||||
|
||||
;referenced in scheduler.c
|
||||
global resume_continuation
|
||||
resume_continuation:
|
||||
|
|
@ -31,3 +33,22 @@ resume_continuation:
|
|||
mov r15, qword [rdi + 56]
|
||||
|
||||
jmp rax
|
||||
|
||||
ret:
|
||||
ret
|
||||
|
||||
global yield
|
||||
yield:
|
||||
mov qword [rdi], ret
|
||||
mov qword [rdi + 8], rbx
|
||||
mov qword [rdi + 16], rbp
|
||||
mov qword [rdi + 24], rsp
|
||||
mov qword [rdi + 32], r12
|
||||
mov qword [rdi + 40], r13
|
||||
mov qword [rdi + 48], r14
|
||||
mov qword [rdi + 56], r15
|
||||
|
||||
jmp resume_next_continuation
|
||||
|
||||
.ret:
|
||||
ret
|
||||
|
|
|
|||
|
|
@ -19,69 +19,69 @@
|
|||
#include "utility.h"
|
||||
#include "heap.h"
|
||||
|
||||
static struct continuation_info *ready_continuations = 0;
|
||||
static int rc_buffer_length = 0;
|
||||
static int rc_read_ptr = 0;
|
||||
static int rc_count = 0;
|
||||
|
||||
#define INITIAL_RC_BUFFER_LENGTH 128
|
||||
struct continuation_queue ready_continuations;
|
||||
|
||||
void init_scheduler() {
|
||||
ready_continuations = heap_alloc(INITIAL_RC_BUFFER_LENGTH * sizeof(struct continuation_info));
|
||||
rc_buffer_length = INITIAL_RC_BUFFER_LENGTH;
|
||||
}
|
||||
|
||||
void add_ready_continuation(struct continuation_info *info) {
|
||||
|
||||
if (rc_count == rc_buffer_length) {
|
||||
|
||||
struct continuation_info *new_rc_buffer =
|
||||
heap_alloc(2 * rc_buffer_length * sizeof(struct continuation_info));
|
||||
|
||||
memcpy(
|
||||
new_rc_buffer,
|
||||
ready_continuations + rc_read_ptr,
|
||||
(rc_buffer_length - rc_read_ptr) * sizeof(struct continuation_info));
|
||||
|
||||
memcpy(
|
||||
new_rc_buffer + rc_buffer_length - rc_read_ptr,
|
||||
ready_continuations,
|
||||
rc_read_ptr * sizeof(struct continuation_info));
|
||||
|
||||
heap_dealloc(ready_continuations, rc_buffer_length * sizeof(struct continuation_info));
|
||||
|
||||
memcpy(&new_rc_buffer[rc_buffer_length], info, sizeof(struct continuation_info));
|
||||
|
||||
ready_continuations = new_rc_buffer;
|
||||
rc_buffer_length *= 2;
|
||||
rc_read_ptr = 0;
|
||||
++rc_count;
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
memcpy(
|
||||
&ready_continuations[(rc_read_ptr + rc_count) % rc_buffer_length],
|
||||
info, sizeof(struct continuation_info));
|
||||
++rc_count;
|
||||
}
|
||||
|
||||
create_queue(&ready_continuations, 128);
|
||||
}
|
||||
|
||||
//defined in scheduler.asm
|
||||
[[noreturn]] void resume_continuation(struct continuation_info *info);
|
||||
[[noreturn]] void resume_continuation(const struct continuation_info *info);
|
||||
|
||||
[[noreturn]] void resume_next_continuation() {
|
||||
|
||||
while (rc_count == 0)
|
||||
struct continuation_info ci;
|
||||
|
||||
while (!take_from_queue(&ready_continuations, &ci))
|
||||
__asm__ ("hlt");
|
||||
|
||||
struct continuation_info info;
|
||||
memcpy(&info, &ready_continuations[rc_read_ptr], sizeof(struct continuation_info));
|
||||
|
||||
rc_read_ptr = (rc_read_ptr + 1) % rc_buffer_length;
|
||||
--rc_count;
|
||||
|
||||
resume_continuation(&info);
|
||||
resume_continuation(&ci);
|
||||
|
||||
}
|
||||
|
||||
void create_queue(struct continuation_queue *queue, int buffer_size) {
|
||||
queue->cis = heap_alloc(buffer_size * sizeof(struct continuation_info));
|
||||
queue->next_read_index = 0;
|
||||
queue->buffer_size = buffer_size;
|
||||
queue->count = 0;
|
||||
}
|
||||
|
||||
void add_to_queue(struct continuation_queue *queue, struct continuation_info *ci) {
|
||||
|
||||
if (queue->count == queue->buffer_size) {
|
||||
|
||||
struct continuation_info *new_buffer =
|
||||
heap_alloc(2 * queue->buffer_size * sizeof(struct continuation_info));
|
||||
|
||||
memcpy(
|
||||
new_buffer, &queue->cis[queue->next_read_index],
|
||||
(queue->buffer_size - queue->next_read_index) * sizeof(struct continuation_info));
|
||||
memcpy(
|
||||
&new_buffer[queue->buffer_size - queue->next_read_index],
|
||||
queue->cis, queue->next_read_index * sizeof(struct continuation_info));
|
||||
|
||||
heap_dealloc(queue->cis, queue->buffer_size * sizeof(struct continuation_info));
|
||||
queue->cis = new_buffer;
|
||||
queue->buffer_size *= 2;
|
||||
queue->next_read_index = 0;
|
||||
|
||||
}
|
||||
|
||||
memcpy(
|
||||
&queue->cis[(queue->next_read_index + queue->count) % queue->buffer_size],
|
||||
ci, sizeof(struct continuation_info));
|
||||
++queue->count;
|
||||
|
||||
}
|
||||
|
||||
int take_from_queue(struct continuation_queue *queue, struct continuation_info *ci) {
|
||||
|
||||
if (queue->count == 0)
|
||||
return 0;
|
||||
|
||||
memcpy(ci, &queue->cis[queue->next_read_index], sizeof(struct continuation_info));
|
||||
queue->next_read_index = (queue->next_read_index + 1) % queue->buffer_size;
|
||||
--queue->count;
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,5 +34,23 @@ void init_scheduler();
|
|||
|
||||
[[noreturn]] void resume_next_continuation();
|
||||
|
||||
struct continuation_queue {
|
||||
struct continuation_info *cis;
|
||||
int next_read_index;
|
||||
int buffer_size;
|
||||
int count;
|
||||
};
|
||||
|
||||
extern struct continuation_queue ready_continuations;
|
||||
|
||||
void create_queue(struct continuation_queue *queue, int buffer_size);
|
||||
|
||||
//ci is copied
|
||||
void add_ready_continuation(struct continuation_info *ci);
|
||||
void add_to_queue(struct continuation_queue *queue, struct continuation_info *ci);
|
||||
|
||||
//if queue is empty, returns 0. otherwise, copies next read to ci, advances read, and returns 1.
|
||||
int take_from_queue(struct continuation_queue *queue, struct continuation_info *ci);
|
||||
|
||||
//saves a continuation that returns from this function to save_current_continuation_to, then resumes
|
||||
//the next ready continuation (thus doesn't return until save_current_continuation_to is resumed).
|
||||
void yield(struct continuation_info *save_current_continuation_to);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,22 @@
|
|||
|
||||
bits 64
|
||||
|
||||
extern destroy_syscall_stack
|
||||
extern create_syscall_stack
|
||||
extern syscall_entry_c
|
||||
|
||||
section .bss
|
||||
|
||||
;this should have guard pages blah blah blah
|
||||
resb 16 << 20
|
||||
syscall_stack_top:
|
||||
resb 1 << 15
|
||||
temp_syscall_stack:
|
||||
|
||||
available_syscall_stack_count equ 128
|
||||
available_syscall_stacks:
|
||||
resq available_syscall_stack_count
|
||||
|
||||
global most_recent_syscall_stack
|
||||
most_recent_syscall_stack:
|
||||
resq 1
|
||||
|
||||
section .text
|
||||
|
||||
|
|
@ -31,8 +40,48 @@ section .text
|
|||
;system call arguments are in rdi, rsi, rdx.
|
||||
;system call returns a value in rax.
|
||||
syscall_entry:
|
||||
mov qword [syscall_stack_top - 8], rsp
|
||||
mov rsp, syscall_stack_top - 8
|
||||
|
||||
xor r8, r8
|
||||
.find_stack_loop:
|
||||
|
||||
mov r9, qword [available_syscall_stacks + r8 * 8]
|
||||
test r9, r9
|
||||
jnz .got_syscall_stack
|
||||
|
||||
inc r8
|
||||
cmp r8, available_syscall_stack_count
|
||||
jne .find_stack_loop
|
||||
|
||||
mov qword [temp_syscall_stack - 8], rsp
|
||||
mov rsp, temp_syscall_stack - 8
|
||||
push r11
|
||||
push rcx
|
||||
push rax
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
|
||||
call create_syscall_stack
|
||||
mov r9, rax
|
||||
|
||||
pop rdx
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rax
|
||||
pop rcx
|
||||
pop r11
|
||||
pop rsp
|
||||
|
||||
jmp .common
|
||||
|
||||
.got_syscall_stack:
|
||||
mov qword [available_syscall_stacks + r8 * 8], 0
|
||||
|
||||
.common:
|
||||
mov qword [r9 - 8], rsp
|
||||
mov rsp, r9
|
||||
mov qword [most_recent_syscall_stack], rsp
|
||||
sub rsp, 8
|
||||
push r11
|
||||
push rcx
|
||||
|
||||
|
|
@ -41,11 +90,59 @@ syscall_entry:
|
|||
|
||||
pop rcx
|
||||
pop r11
|
||||
|
||||
xor r8, r8
|
||||
.find_place_to_put_stack:
|
||||
|
||||
mov r9, qword [available_syscall_stacks + r8 * 8]
|
||||
test r9, r9
|
||||
jz .got_place
|
||||
|
||||
inc r8
|
||||
cmp r8, available_syscall_stack_count
|
||||
jne .find_stack_loop
|
||||
|
||||
mov rdi, rsp
|
||||
add rdi, 8
|
||||
|
||||
pop rsp
|
||||
|
||||
mov qword [temp_syscall_stack - 8], rsp
|
||||
mov rsp, temp_syscall_stack - 8
|
||||
push rax
|
||||
push rcx
|
||||
push r11
|
||||
|
||||
call destroy_syscall_stack
|
||||
|
||||
pop r11
|
||||
pop rcx
|
||||
pop rax
|
||||
jmp .common_2
|
||||
|
||||
.got_place:
|
||||
mov r9, rsp
|
||||
add r9, 8
|
||||
mov qword [available_syscall_stacks + r8 * 8], r9
|
||||
|
||||
.common_2:
|
||||
pop rsp
|
||||
o64 sysret
|
||||
|
||||
global init_syscalls
|
||||
init_syscalls:
|
||||
sub rsp, 8
|
||||
mov qword [rsp], 0
|
||||
|
||||
.create_stack_loop:
|
||||
call create_syscall_stack
|
||||
mov rdi, qword [rsp]
|
||||
mov qword [available_syscall_stacks + rdi * 8], rax
|
||||
inc qword [rsp]
|
||||
cmp rdi, available_syscall_stack_count - 1
|
||||
jne .create_stack_loop
|
||||
|
||||
add rsp, 8
|
||||
|
||||
mov ecx, 0xc0000080
|
||||
rdmsr
|
||||
|
|
@ -69,3 +166,8 @@ init_syscalls:
|
|||
wrmsr
|
||||
|
||||
ret
|
||||
|
||||
global with_temporary_stack
|
||||
with_temporary_stack:
|
||||
mov rsp, temp_syscall_stack
|
||||
jmp rdi
|
||||
|
|
|
|||
|
|
@ -25,3 +25,11 @@ void init_syscalls();
|
|||
void register_syscall(
|
||||
int number,
|
||||
uint64_t (*handler)(uint64_t arg1, uint64_t arg2, uint64_t arg3));
|
||||
|
||||
//the top of the syscall stack of the most recently started syscall.
|
||||
//kind of a hack, used in the end_thread syscall to deallocate that stack.
|
||||
extern void *most_recent_syscall_stack;
|
||||
|
||||
//also kind of a hack. switches to temporary stack and runs f with that stack.
|
||||
//f should not return. this does not touch most_recent_syscall_stack.
|
||||
[[noreturn]] void with_temporary_stack(void (*f)());
|
||||
|
|
|
|||
|
|
@ -15,18 +15,88 @@
|
|||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <calcite/calcite.h>
|
||||
#include <calcite/syscalls.h>
|
||||
#include <silver/pam.h>
|
||||
|
||||
static struct framebuffer_info fb_info;
|
||||
static struct image *fbb;
|
||||
|
||||
static struct image *cursor_image;
|
||||
|
||||
static void draw_bg(int startx, int starty, int width, int height) {
|
||||
if (startx + width > fbb->width)
|
||||
width = fbb->width - startx;
|
||||
if (starty + height > fbb->height)
|
||||
height = fbb->height - starty;
|
||||
for (int y = starty; y < starty + height; ++y)
|
||||
for (int x = startx; x < startx + width; ++x) {
|
||||
struct pixel *pixel = &fbb->pixels[y * fbb->width + x];
|
||||
pixel->r = 0;
|
||||
pixel->g = y * 256 / fb_info.fb_height;
|
||||
pixel->b = x * 256 / fb_info.fb_width;
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_cursor(int x, int y) {
|
||||
for (int yy = 0; yy < cursor_image->height && y + yy < fbb->height; ++yy)
|
||||
for (int xx = 0; xx < cursor_image->width && x + xx < fbb->width; ++xx) {
|
||||
struct pixel *fb_pixel = &fbb->pixels[(y + yy) * fbb->width + (x + xx)];
|
||||
struct pixel *ci_pixel = &cursor_image->pixels[yy * cursor_image->width + xx];
|
||||
fb_pixel->r = (fb_pixel->r * (255 - ci_pixel->a) + ci_pixel->r * ci_pixel->a) / 255;
|
||||
fb_pixel->g = (fb_pixel->g * (255 - ci_pixel->a) + ci_pixel->g * ci_pixel->a) / 255;
|
||||
fb_pixel->b = (fb_pixel->b * (255 - ci_pixel->a) + ci_pixel->b * ci_pixel->a) / 255;
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_frame() {
|
||||
for (int y = 0; y < fbb->height; ++y)
|
||||
for (int x = 0; x < fbb->width; ++x) {
|
||||
uint8_t *fb_pixel = &fb_info.fb_base[y * fb_info.fb_pitch + x * 4];
|
||||
struct pixel *fbb_pixel = &fbb->pixels[y * fbb->width + x];
|
||||
fb_pixel[0] = fbb_pixel->b;
|
||||
fb_pixel[1] = fbb_pixel->g;
|
||||
fb_pixel[2] = fbb_pixel->r;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
struct framebuffer_info fb_info;
|
||||
map_framebuffer(&fb_info);
|
||||
if (!load_pam("root://calcite/apps/hello/pointer.pam", &cursor_image))
|
||||
return;
|
||||
|
||||
map_framebuffer(&fb_info);
|
||||
create_image(fb_info.fb_width, fb_info.fb_height, &fbb);
|
||||
|
||||
int cursor_x = fb_info.fb_width / 2;
|
||||
int cursor_y = fb_info.fb_height / 2;
|
||||
|
||||
draw_bg(0, 0, fbb->width, fbb->height);
|
||||
draw_cursor(cursor_x, cursor_y);
|
||||
copy_frame();
|
||||
|
||||
while (1) {
|
||||
|
||||
struct mouse_packet packet;
|
||||
wait_for_mouse_packet(&packet);
|
||||
|
||||
draw_bg(cursor_x, cursor_y, cursor_image->width, cursor_image->height);
|
||||
|
||||
cursor_x += packet.x_change;
|
||||
cursor_y -= packet.y_change;
|
||||
|
||||
if (cursor_x < 0)
|
||||
cursor_x = 0;
|
||||
if (cursor_x >= fbb->width)
|
||||
cursor_x = fbb->width - 1;
|
||||
|
||||
if (cursor_y < 0)
|
||||
cursor_y = 0;
|
||||
if (cursor_y >= fbb->height)
|
||||
cursor_y = fbb->height - 1;
|
||||
|
||||
draw_cursor(cursor_x, cursor_y);
|
||||
copy_frame();
|
||||
|
||||
for (int y = 0; y < fb_info.fb_height; ++y)
|
||||
for (int x = 0; x < fb_info.fb_width; ++x) {
|
||||
uint8_t *pixel = fb_info.fb_base + y * fb_info.fb_pitch + x * 4;
|
||||
pixel[0] = x * 256 / fb_info.fb_width;
|
||||
pixel[1] = y * 256 / fb_info.fb_height;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
calcite
|
||||
silver
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
* with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <calcite/calcite.h>
|
||||
#include <calcite/syscalls.h>
|
||||
|
||||
void main();
|
||||
|
||||
|
|
|
|||
150
src/user-libs/calcite/file-streams.c
Normal file
150
src/user-libs/calcite/file-streams.c
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/* Calcite, src/user-libs/calcite/file-streams.c
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <calcite/file-streams.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <calcite/syscalls.h>
|
||||
#include <calcite/memory.h>
|
||||
|
||||
static uint64_t min(uint64_t a, uint64_t b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
//in bytes
|
||||
#define FS_BUFFER_SIZE 8192
|
||||
|
||||
struct file_stream {
|
||||
|
||||
file_handle_t handle;
|
||||
|
||||
//both in bytes
|
||||
uint64_t length;
|
||||
uint64_t pointer;
|
||||
|
||||
int is_there_buffer;
|
||||
//in bytes
|
||||
uint64_t buffer_start;
|
||||
uint8_t buffer[FS_BUFFER_SIZE];
|
||||
|
||||
};
|
||||
|
||||
enum fs_access_result open_file_stream(const char *path, struct file_stream **file_stream_out) {
|
||||
|
||||
file_handle_t handle;
|
||||
enum fs_access_result result = open_file(path, &handle);
|
||||
if (result != FAR_SUCCESS)
|
||||
return result;
|
||||
|
||||
uint64_t length;
|
||||
result = get_file_size(handle, &length);
|
||||
if (result != FAR_SUCCESS) {
|
||||
close_file(handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
*file_stream_out = heap_alloc(sizeof(struct file_stream));
|
||||
(*file_stream_out)->handle = handle;
|
||||
(*file_stream_out)->length = length;
|
||||
(*file_stream_out)->pointer = 0;
|
||||
(*file_stream_out)->is_there_buffer = 0;
|
||||
return FAR_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
void close_file_stream(struct file_stream *file_stream) {
|
||||
close_file(file_stream->handle);
|
||||
heap_dealloc(file_stream, sizeof(struct file_stream));
|
||||
}
|
||||
|
||||
enum fs_access_result get_in_buffer(struct file_stream *file_stream, uint64_t pointer) {
|
||||
|
||||
uint64_t bstart = (pointer / FS_BUFFER_SIZE) * FS_BUFFER_SIZE;
|
||||
uint64_t bbytes = min(FS_BUFFER_SIZE, file_stream->length - bstart);
|
||||
|
||||
if (file_stream->is_there_buffer && file_stream->buffer_start == bstart)
|
||||
return FAR_SUCCESS;
|
||||
|
||||
enum fs_access_result result =
|
||||
read_file_splat(file_stream->handle, file_stream->buffer, bstart, bbytes);
|
||||
|
||||
if (result == FAR_SUCCESS) {
|
||||
file_stream->is_there_buffer = 1;
|
||||
file_stream->buffer_start = bstart;
|
||||
return FAR_SUCCESS;
|
||||
}
|
||||
|
||||
file_stream->is_there_buffer = 0;
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
enum fs_access_result read_file_stream(struct file_stream *file_stream, void *buffer, uint64_t bytes) {
|
||||
|
||||
if (bytes == 0)
|
||||
return FAR_SUCCESS;
|
||||
|
||||
if (file_stream->pointer + bytes > file_stream->length)
|
||||
return FAR_OUT_OF_BOUNDS;
|
||||
|
||||
enum fs_access_result result = get_in_buffer(file_stream, file_stream->pointer);
|
||||
if (result != FAR_SUCCESS)
|
||||
return result;
|
||||
|
||||
uint64_t in_buffer = min(bytes, FS_BUFFER_SIZE - (file_stream->pointer - file_stream->buffer_start));
|
||||
memcpy(
|
||||
buffer,
|
||||
file_stream->buffer + file_stream->pointer - file_stream->buffer_start,
|
||||
in_buffer);
|
||||
|
||||
uint64_t pointer = file_stream->pointer + in_buffer;
|
||||
uint64_t left = bytes - in_buffer;
|
||||
void *buffer_pointer = buffer + in_buffer;
|
||||
|
||||
while (left >= FS_BUFFER_SIZE) {
|
||||
enum fs_access_result result =
|
||||
read_file_splat(file_stream->handle, buffer_pointer, pointer, FS_BUFFER_SIZE);
|
||||
if (result != FAR_SUCCESS)
|
||||
return result;
|
||||
pointer += FS_BUFFER_SIZE;
|
||||
left -= FS_BUFFER_SIZE;
|
||||
buffer_pointer += FS_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
if (left > 0) {
|
||||
enum fs_access_result result = get_in_buffer(file_stream, pointer);
|
||||
if (result != FAR_SUCCESS)
|
||||
return result;
|
||||
memcpy(buffer_pointer, file_stream->buffer, left);
|
||||
}
|
||||
|
||||
file_stream->pointer += bytes;
|
||||
return FAR_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
int read_file_stream_byte(struct file_stream *file_stream) {
|
||||
|
||||
if (file_stream->pointer == file_stream->length)
|
||||
return -1;
|
||||
|
||||
enum fs_access_result result = get_in_buffer(file_stream, file_stream->pointer);
|
||||
if (result != FAR_SUCCESS)
|
||||
return -1;
|
||||
|
||||
return file_stream->buffer[file_stream->pointer++ - file_stream->buffer_start];
|
||||
|
||||
}
|
||||
191
src/user-libs/calcite/memory.c
Normal file
191
src/user-libs/calcite/memory.c
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/* Calcite, src/user-libs/calcite/memory.c
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <calcite/syscalls.h>
|
||||
#include <calcite/memory.h>
|
||||
|
||||
struct block_list_entry {
|
||||
struct block_list_entry *prev;
|
||||
struct block_list_entry *next;
|
||||
};
|
||||
|
||||
struct block_list {
|
||||
struct block_list_entry *first_entry;
|
||||
struct block_list_entry *last_entry;
|
||||
};
|
||||
|
||||
#define BASE_BLOCK_SIZE 16ULL
|
||||
#define BLOCK_LEVELS 32
|
||||
//BASE_BLOCK_SIZE << MIN_MAP_BLOCK_LEVEL must be at least 4096
|
||||
#define MIN_MAP_BLOCK_LEVEL 16
|
||||
|
||||
//block_lists[n] is a list of free blocks of size BASE_BLOCK_SIZE * 2^n bytes
|
||||
static struct block_list block_lists[BLOCK_LEVELS];
|
||||
|
||||
void *heap_alloc(uint64_t bytes) {
|
||||
|
||||
bytes = ((bytes - 1) / BASE_BLOCK_SIZE + 1) * BASE_BLOCK_SIZE;
|
||||
|
||||
//TODO: handle this case?
|
||||
if (bytes > (BASE_BLOCK_SIZE << (BLOCK_LEVELS - 1)))
|
||||
return 0;
|
||||
|
||||
int block_n_minimum = 0;
|
||||
uint64_t block_size_minimum = BASE_BLOCK_SIZE;
|
||||
while (block_size_minimum < bytes) {
|
||||
++block_n_minimum;
|
||||
block_size_minimum *= 2;
|
||||
}
|
||||
|
||||
int block_n = block_n_minimum;
|
||||
uint64_t block_size = block_size_minimum;
|
||||
while (1) {
|
||||
if (block_n == BLOCK_LEVELS) {
|
||||
block_n = -1;
|
||||
break;
|
||||
}
|
||||
if (block_lists[block_n].first_entry != 0)
|
||||
break;
|
||||
++block_n;
|
||||
block_size *= 2;
|
||||
}
|
||||
|
||||
if (block_n == -1) {
|
||||
block_n = block_n_minimum > MIN_MAP_BLOCK_LEVEL ? block_n_minimum : MIN_MAP_BLOCK_LEVEL;
|
||||
block_size = BASE_BLOCK_SIZE << block_n;
|
||||
void *start = map_pages(block_size / 4096);
|
||||
if (block_size != bytes)
|
||||
heap_dealloc(start + bytes, block_size - bytes);
|
||||
return start;
|
||||
}
|
||||
|
||||
struct block_list_entry *e = block_lists[block_n].first_entry;
|
||||
if (e->next)
|
||||
e->next->prev = 0;
|
||||
else
|
||||
block_lists[block_n].last_entry = 0;
|
||||
block_lists[block_n].first_entry = e->next;
|
||||
|
||||
void *start = e;
|
||||
if (block_size != bytes)
|
||||
heap_dealloc(start + bytes, block_size - bytes);
|
||||
|
||||
return start;
|
||||
|
||||
}
|
||||
|
||||
static void heap_dealloc_aligned(void *start, uint64_t block_n, uint64_t block_size) {
|
||||
recurse:
|
||||
|
||||
if (block_size != BLOCK_LEVELS - 1) {
|
||||
|
||||
void *buddy = (void *)((uint64_t)start ^ block_size);
|
||||
|
||||
for (struct block_list_entry *e = block_lists[block_n].first_entry; e != 0; e = e->next)
|
||||
if (e == buddy) {
|
||||
if (e->next)
|
||||
e->next->prev = e->prev;
|
||||
else
|
||||
block_lists[block_n].last_entry = e->prev;
|
||||
if (e->prev)
|
||||
e->prev->next = e->next;
|
||||
else
|
||||
block_lists[block_n].first_entry = e->next;
|
||||
start = (void *)((uint64_t)start & ~block_size);
|
||||
++block_n;
|
||||
block_size *= 2;
|
||||
goto recurse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (block_lists[block_n].first_entry == 0) {
|
||||
struct block_list_entry *e = start;
|
||||
e->next = 0;
|
||||
e->prev = 0;
|
||||
block_lists[block_n].first_entry = e;
|
||||
block_lists[block_n].last_entry = e;
|
||||
return;
|
||||
}
|
||||
|
||||
if (start < (void *)block_lists[block_n].first_entry) {
|
||||
struct block_list_entry *e = start;
|
||||
e->next = block_lists[block_n].first_entry;
|
||||
e->prev = 0;
|
||||
block_lists[block_n].first_entry->prev = e;
|
||||
block_lists[block_n].first_entry = e;
|
||||
return;
|
||||
}
|
||||
|
||||
if (start > (void *)block_lists[block_n].last_entry) {
|
||||
struct block_list_entry *e = start;
|
||||
e->next = 0;
|
||||
e->prev = block_lists[block_n].last_entry;
|
||||
block_lists[block_n].last_entry->next = e;
|
||||
block_lists[block_n].last_entry = e;
|
||||
return;
|
||||
}
|
||||
|
||||
struct block_list_entry *after = block_lists[block_n].first_entry;
|
||||
while ((void *)after < start)
|
||||
after = after->next;
|
||||
struct block_list_entry *before = after->prev;
|
||||
struct block_list_entry *e = start;
|
||||
|
||||
e->next = after;
|
||||
e->prev = before;
|
||||
after->prev = e;
|
||||
before->next = e;
|
||||
|
||||
}
|
||||
|
||||
void heap_dealloc(void *start, uint64_t bytes) {
|
||||
|
||||
bytes = ((bytes - 1) / BASE_BLOCK_SIZE + 1) * BASE_BLOCK_SIZE;
|
||||
//bytes is guaranteed to be at most BASE_BLOCK_SIZE << (BLOCK_LEVELS - 1)
|
||||
|
||||
int block_n = 0;
|
||||
uint64_t block_size = BASE_BLOCK_SIZE;
|
||||
|
||||
while (block_size <= bytes) {
|
||||
if ((uint64_t)start & block_size) {
|
||||
heap_dealloc_aligned(start, block_n, block_size);
|
||||
start += block_size;
|
||||
bytes -= block_size;
|
||||
}
|
||||
++block_n;
|
||||
block_size *= 2;
|
||||
}
|
||||
|
||||
while (block_n >= 0) {
|
||||
if (block_size <= bytes) {
|
||||
heap_dealloc_aligned(start, block_n, block_size);
|
||||
start += block_size;
|
||||
bytes -= block_size;
|
||||
}
|
||||
--block_n;
|
||||
block_size /= 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void memcpy(void *to, const void *from, uint64_t bytes) {
|
||||
uint8_t *to8 = to;
|
||||
const uint8_t *from8 = from;
|
||||
for (uint64_t i = 0; i < bytes; ++i)
|
||||
to8[i] = from8[i];
|
||||
}
|
||||
|
|
@ -16,7 +16,8 @@
|
|||
*/
|
||||
|
||||
#include <kernel-public/syscall-numbers.h>
|
||||
#include <calcite/calcite.h>
|
||||
#include <kernel-public/files.h>
|
||||
#include <calcite/syscalls.h>
|
||||
|
||||
//defined in syscalls.asm
|
||||
uint64_t do_syscall
|
||||
|
|
@ -30,3 +31,32 @@ uint64_t do_syscall
|
|||
void map_framebuffer(struct framebuffer_info *info_out) {
|
||||
do_syscall((uint64_t)info_out, 0, 0, SYSCALL_MAP_FRAMEBUFFER);
|
||||
}
|
||||
|
||||
enum fs_access_result open_file(const char *path, file_handle_t *handle_out) {
|
||||
return do_syscall((uint64_t)path, (uint64_t)handle_out, 1, SYSCALL_OPEN_FILE);
|
||||
}
|
||||
|
||||
void close_file(file_handle_t handle) {
|
||||
do_syscall(handle, 0, 0, SYSCALL_CLOSE_FILE);
|
||||
}
|
||||
|
||||
enum fs_access_result get_file_size(file_handle_t handle, uint64_t *bytes_out) {
|
||||
return do_syscall(handle, (uint64_t)bytes_out, 0, SYSCALL_GET_FILE_SIZE);
|
||||
}
|
||||
|
||||
enum fs_access_result read_file(struct read_file_parameter *parameter) {
|
||||
return do_syscall((uint64_t)parameter, 0, 0, SYSCALL_READ_FILE);
|
||||
}
|
||||
|
||||
enum fs_access_result read_file_splat(file_handle_t handle, void *buffer, uint64_t start, uint64_t bytes) {
|
||||
struct read_file_parameter parameter = { .handle = handle, .buffer = buffer, .start = start, .bytes = bytes };
|
||||
return do_syscall((uint64_t)¶meter, 0, 0, SYSCALL_READ_FILE);
|
||||
}
|
||||
|
||||
void wait_for_mouse_packet(struct mouse_packet *packet_out) {
|
||||
do_syscall((uint64_t)packet_out, 0, 0, SYSCALL_WAIT_FOR_MOUSE_PACKET);
|
||||
}
|
||||
|
||||
void *map_pages(uint64_t count) {
|
||||
return (void *)do_syscall(count, 0, 0, SYSCALL_MAP_PAGES);
|
||||
}
|
||||
|
|
|
|||
31
src/user-libs/silver/image.c
Normal file
31
src/user-libs/silver/image.c
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* Calcite, src/user-libs/silver/image.c
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <calcite/memory.h>
|
||||
#include <silver/image.h>
|
||||
|
||||
void create_image(int width, int height, struct image **image_out) {
|
||||
*image_out = heap_alloc(sizeof(struct image));
|
||||
(*image_out)->width = width;
|
||||
(*image_out)->height = height;
|
||||
(*image_out)->pixels = heap_alloc(width * height * sizeof(struct pixel));
|
||||
}
|
||||
|
||||
void destroy_image(struct image *image) {
|
||||
heap_dealloc(image->pixels, image->width * image->height * sizeof(struct pixel));
|
||||
heap_dealloc(image, sizeof(struct image));
|
||||
}
|
||||
128
src/user-libs/silver/pam.c
Normal file
128
src/user-libs/silver/pam.c
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/* Calcite, src/user-libs/silver/pam.c
|
||||
* Copyright 2025 Benji Dial
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <calcite/file-streams.h>
|
||||
#include <silver/pam.h>
|
||||
|
||||
static int starts_with(const char *big_string, const char *little_string) {
|
||||
while (*little_string) {
|
||||
if (*big_string != *little_string)
|
||||
return 0;
|
||||
++big_string;
|
||||
++little_string;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int strequ(const char *str1, const char *str2) {
|
||||
while (1) {
|
||||
if (!*str1 && !*str2)
|
||||
return 1;
|
||||
if (*str1 != *str2)
|
||||
return 0;
|
||||
++str1;
|
||||
++str2;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_uint(const char *str) {
|
||||
int i = 0;
|
||||
while (*str)
|
||||
i = i * 10 + *(str++) - '0';
|
||||
return i;
|
||||
}
|
||||
|
||||
enum pam_tupltype {
|
||||
PTT_RGB_ALPHA
|
||||
};
|
||||
|
||||
#define MAX_LINE_LENGTH 1023
|
||||
|
||||
int load_pam(const char *path, struct image **image_out) {
|
||||
|
||||
struct file_stream *stream;
|
||||
if (open_file_stream(path, &stream) != FAR_SUCCESS)
|
||||
return 0;
|
||||
|
||||
if (read_file_stream_byte(stream) != 'P' ||
|
||||
read_file_stream_byte(stream) != '7' ||
|
||||
read_file_stream_byte(stream) != '\n') {
|
||||
close_file_stream(stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int width = -1, height = -1, depth = -1, maxval = -1, tupltype = -1;
|
||||
|
||||
while (1) {
|
||||
|
||||
char line[MAX_LINE_LENGTH + 1];
|
||||
int line_length = 0;
|
||||
while (1) {
|
||||
int c = read_file_stream_byte(stream);
|
||||
if (c == -1) {
|
||||
close_file_stream(stream);
|
||||
return 0;
|
||||
}
|
||||
if (c == '\n') {
|
||||
line[line_length] = 0;
|
||||
break;
|
||||
}
|
||||
if (line_length == MAX_LINE_LENGTH) {
|
||||
close_file_stream(stream);
|
||||
return 0;
|
||||
}
|
||||
line[line_length++] = c;
|
||||
}
|
||||
|
||||
if (line[0] == '#')
|
||||
continue;
|
||||
|
||||
if (starts_with(line, "WIDTH "))
|
||||
width = parse_uint(&line[6]);
|
||||
else if (starts_with(line, "HEIGHT "))
|
||||
height = parse_uint(&line[7]);
|
||||
else if (starts_with(line, "DEPTH "))
|
||||
depth = parse_uint(&line[6]);
|
||||
else if (starts_with(line, "MAXVAL "))
|
||||
maxval = parse_uint(&line[7]);
|
||||
else if (strequ(line, "TUPLTYPE RGB_ALPHA"))
|
||||
tupltype = PTT_RGB_ALPHA;
|
||||
else if (strequ(line, "ENDHDR"))
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (width == -1 || height == -1 || depth != 4 || maxval != 255 || tupltype != PTT_RGB_ALPHA) {
|
||||
close_file_stream(stream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
create_image(width, height, image_out);
|
||||
|
||||
if (read_file_stream(
|
||||
stream,
|
||||
(*image_out)->pixels,
|
||||
width * height * 4) != FAR_SUCCESS) {
|
||||
close_file_stream(stream);
|
||||
destroy_image(*image_out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
close_file_stream(stream);
|
||||
return 1;
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue