aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/kernel/Makefile2
-rw-r--r--src/kernel/include/pager.h73
-rw-r--r--src/kernel/include/process.h15
-rw-r--r--src/kernel/include/sys.h7
-rw-r--r--src/kernel/include/vfs.h4
-rw-r--r--src/kernel/user/pager.c183
-rw-r--r--src/kernel/user/process.c93
7 files changed, 321 insertions, 56 deletions
diff --git a/src/kernel/Makefile b/src/kernel/Makefile
index f7cee23..1b01f13 100644
--- a/src/kernel/Makefile
+++ b/src/kernel/Makefile
@@ -3,7 +3,7 @@ OBJ = core/loader.o core/dbglog.o \
core/gdt.o core/idt.o core/interrupt.o core/context_switch.o core/thread.o \
core/frame.o core/paging.o core/freemem.o core/region.o core/kmalloc.o \
core/worker.o core/prng.o \
- user/vfs.o user/nullfs.o user/process.o user/elf.o user/syscall.o user/ipc.o \
+ user/vfs.o user/nullfs.o user/process.o user/elf.o user/syscall.o user/ipc.o user/pager.o \
dev/pci.o dev/pciide.o \
fs/iso9660.o
diff --git a/src/kernel/include/pager.h b/src/kernel/include/pager.h
new file mode 100644
index 0000000..5491d10
--- /dev/null
+++ b/src/kernel/include/pager.h
@@ -0,0 +1,73 @@
+#pragma once
+
+#include <hashtbl.h>
+#include <mutex.h>
+
+#include <paging.h>
+
+#define PAGE_PINNED 0x01
+#define PAGE_ACCESSED 0x02
+#define PAGE_DIRTY 0x04
+
+typedef struct pager pager_t;
+typedef struct user_region user_region_t;
+typedef struct fs_node fs_node_t;
+
+typedef struct {
+ size_t (*read)(fs_node_t* data, size_t offset, size_t len, char* ptr);
+ size_t (*write)(fs_node_t* data, size_t offset, size_t len, const char* ptr);
+ bool (*resize)(fs_node_t* data, size_t new_size);
+} vfs_pager_ops_t;
+
+typedef struct {
+ void (*page_in)(pager_t *p, size_t offset, size_t len); // allocates the pages
+ void (*page_commit)(pager_t *p, size_t offset, size_t len); // writes pages back to storage (if applicable)
+ void (*page_out)(pager_t *p, size_t offset, size_t len); // frees the pages (no need to write back)
+ void (*page_release)(pager_t *p, size_t offset, size_t len); // called on delete/resize
+} pager_ops_t;
+
+typedef struct pager {
+ pager_ops_t *ops;
+
+ union {
+ struct {
+ fs_node_t* vfs_data;
+ vfs_pager_ops_t *vfs_ops;
+ } vfs_pager;
+ struct {
+ size_t phys_offset;
+ } device_pager;
+ };
+
+ size_t size;
+
+ hashtbl_t *pages;
+ mutex_t lock;
+
+ user_region_t *maps;
+} pager_t;
+
+pager_t* new_swap_pager(size_t size);
+pager_t* new_vfs_pager(size_t size, fs_node_t* vfs_node, vfs_pager_ops_t *vfs_ops);
+pager_t* new_dev_pager(size_t size, size_t phys_offset);
+
+void delete_pager(pager_t *p);
+
+bool pager_resize(pager_t *p, size_t newsize);
+
+// void pager_pin_region(pager_t *pager, size_t offset, size_t len); // implement later
+// void pager_unpin_region(pager_t *pager, size_t offset, size_t len);
+
+void pager_access(pager_t *p, size_t offset, size_t len, bool accessed, bool dirty);
+void pager_commit_dirty(pager_t *p);
+
+int pager_get_frame(pager_t *p, size_t offset);
+
+size_t pager_read(pager_t *p, size_t offset, size_t len, char* buf);
+size_t pager_write(pager_t *p, size_t offset, size_t len, const char* buf);
+
+void pager_add_map(pager_t *p, user_region_t *reg);
+void pager_rm_map(pager_t *p, user_region_t *reg);
+
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/include/process.h b/src/kernel/include/process.h
index 8dfa274..8cdfe85 100644
--- a/src/kernel/include/process.h
+++ b/src/kernel/include/process.h
@@ -15,9 +15,9 @@
#include <thread.h>
#include <vfs.h>
+#include <pager.h>
-#include <mmap.h>
-
+#include <mmap.h> // common header for mmaps
#include <proc.h> // common header defining process statuses
#define USERSTACK_ADDR 0xB8000000
@@ -31,12 +31,15 @@ typedef struct user_region {
void* addr;
size_t size;
- int mode;
+ fs_handle_t *file;
+ pager_t *pager;
+ size_t offset;
+ bool own_pager;
- fs_handle_t *file; // null if not mmaped-file
- size_t file_offset;
+ int16_t mode;
- struct user_region *next;
+ struct user_region *next_in_proc;
+ struct user_region *next_for_pager;
} user_region_t;
typedef struct process {
diff --git a/src/kernel/include/sys.h b/src/kernel/include/sys.h
index 944df96..61bfc59 100644
--- a/src/kernel/include/sys.h
+++ b/src/kernel/include/sys.h
@@ -75,9 +75,10 @@ static inline void invlpg(void* addr) {
#define PAGE_SIZE 0x1000
#define PAGE_MASK 0xFFFFF000
-#define PAGE_ALIGN_DOWN(x) (((size_t)x) & PAGE_MASK)
-#define PAGE_ALIGN_UP(x) ((((size_t)x)&(~PAGE_MASK)) == 0 ? ((size_t)x) : (((size_t)x) & PAGE_MASK) + PAGE_SIZE)
-#define PAGE_ID(x) (((size_t)x) / PAGE_SIZE)
+#define PAGE_ALIGNED(x) ((((size_t)(x)) & (~PAGE_MASK)) == 0)
+#define PAGE_ALIGN_DOWN(x) (((size_t)(x)) & PAGE_MASK)
+#define PAGE_ALIGN_UP(x) ((((size_t)(x))&(~PAGE_MASK)) == 0 ? ((size_t)x) : (((size_t)x) & PAGE_MASK) + PAGE_SIZE)
+#define PAGE_ID(x) (((size_t)(x)) / PAGE_SIZE)
#define PAGE_SHIFT 12
#define PT_SHIFT 10
// PAGE_SHIFT + PT_SHIFT + PT_SHIFT = 32
diff --git a/src/kernel/include/vfs.h b/src/kernel/include/vfs.h
index cc6d11c..cdd9e8d 100644
--- a/src/kernel/include/vfs.h
+++ b/src/kernel/include/vfs.h
@@ -8,6 +8,8 @@
#include <fs.h> // common header
+#include <pager.h>
+
// How to use :
// - When using a filesystem : never call the operations in fs_*_ops_t directly, use
// the functions defined bellow in section "public functions";
@@ -108,6 +110,8 @@ typedef struct fs_node {
// These fields are filled by the FS's specific walk() code
fs_node_ops_t *ops;
fs_node_ptr data;
+
+ pager_t *pager;
} fs_node_t;
// -------------------------------------------
diff --git a/src/kernel/user/pager.c b/src/kernel/user/pager.c
new file mode 100644
index 0000000..c892599
--- /dev/null
+++ b/src/kernel/user/pager.c
@@ -0,0 +1,183 @@
+#include <string.h>
+
+#include <pager.h>
+#include <vfs.h>
+#include <process.h>
+#include <frame.h>
+
+#define ENT_FRAME_SHIFT 12
+
+
+// ========== //
+// SWAP PAGER //
+// ========== //
+
+static void swap_page_in(pager_t *p, size_t offset, size_t len);
+static void swap_page_out(pager_t *p, size_t offset, size_t len);
+static void swap_page_release(pager_t *p, size_t offset, size_t len);
+
+static pager_ops_t swap_pager_ops = {
+ .page_in = swap_page_in,
+ .page_commit = 0,
+ .page_out = swap_page_out,
+ .page_release = swap_page_release,
+};
+
+pager_t *new_swap_pager(size_t size) {
+ pager_t *p = (pager_t*)malloc(sizeof(pager_t));
+ if (p == 0) return 0;
+
+ p->pages = create_hashtbl(id_key_eq_fun, id_hash_fun, 0);
+ if (p->pages == 0) goto error;
+
+ p->ops = &swap_pager_ops;
+ p->size = size;
+ p->lock = MUTEX_UNLOCKED;
+ p->maps = 0;
+
+ return p;
+
+error:
+ if (p) free(p);
+ return 0;
+}
+
+void swap_page_in(pager_t *p, size_t offset, size_t len) {
+ ASSERT(PAGE_ALIGNED(offset));
+ size_t npages = PAGE_ALIGN_UP(len) / PAGE_SIZE;
+
+ void *region = region_alloc(PAGE_SIZE, "Page zero area", 0);
+ if (region == 0) return;
+
+ for (size_t i = 0; i < npages; i++) {
+ size_t page = offset + (i * PAGE_SIZE);
+ if (hashtbl_find(p->pages, (void*)page) == 0) {
+ uint32_t frame = frame_alloc(1);
+ if (frame != 0) {
+ pd_map_page(region, frame, true);
+ memset(region, 0, PAGE_SIZE);
+ pd_unmap_page(region);
+ }
+ if (!hashtbl_add(p->pages, (void*)page, (void*)(frame << ENT_FRAME_SHIFT))) {
+ frame_free(frame, 1);
+ }
+ }
+ }
+
+ region_free(region);
+}
+
+void swap_page_out(pager_t *p, size_t offset, size_t len) {
+ // later : save to swap file...
+}
+
+void swap_page_release(pager_t *p, size_t offset, size_t len) {
+ ASSERT(PAGE_ALIGNED(offset));
+ size_t npages = PAGE_ALIGN_UP(len) / PAGE_SIZE;
+
+ for (size_t i = 0; i < npages; i++) {
+ size_t page = offset + (i * PAGE_SIZE);
+ uint32_t frame = (uint32_t)hashtbl_find(p->pages, (void*)page) >> ENT_FRAME_SHIFT;
+ if (frame != 0) {
+ hashtbl_remove(p->pages, (void*)page);
+ frame_free(frame, 1);
+ }
+ }
+}
+
+// ======================= //
+// GENERIC PAGER FUNCTIONS //
+// ======================= //
+
+void delete_pager(pager_t *p) {
+ ASSERT(p->maps == 0);
+
+ pager_commit_dirty(p);
+
+ mutex_lock(&p->lock);
+
+ p->ops->page_release(p, 0, p->size);
+ delete_hashtbl(p->pages);
+ free(p);
+}
+
+void pager_access(pager_t *p, size_t offset, size_t len, bool accessed, bool dirty) {
+ mutex_lock(&p->lock);
+
+ ASSERT(PAGE_ALIGNED(offset));
+ size_t npages = PAGE_ALIGN_UP(len) / PAGE_SIZE;
+
+ for (size_t i = 0; i < npages; i++) {
+ size_t page = offset + (i * PAGE_SIZE);
+ uint32_t v = (uint32_t)hashtbl_find(p->pages, (void*)page);
+ if (v != 0) {
+ hashtbl_change(p->pages, (void*)page,
+ (void*)(v | (accessed ? PAGE_ACCESSED : 0) | (dirty ? PAGE_DIRTY : 0)));
+ }
+ }
+
+ mutex_unlock(&p->lock);
+}
+
+void pager_commit_dirty(pager_t *p) {
+ if (!p->ops->page_commit) return;
+
+ mutex_lock(&p->lock);
+
+ for (size_t i = 0; i < p->size; i += PAGE_SIZE) {
+ uint32_t v = (uint32_t)hashtbl_find(p->pages, (void*)i);
+ if (v & PAGE_DIRTY) {
+ p->ops->page_commit(p, i, PAGE_SIZE);
+ hashtbl_change(p->pages, (void*)i, (void*)(v & ~PAGE_DIRTY));
+ }
+
+ }
+
+ mutex_unlock(&p->lock);
+}
+
+int pager_get_frame(pager_t *p, size_t offset) {
+ mutex_lock(&p->lock);
+
+ ASSERT(PAGE_ALIGNED(offset));
+
+ uint32_t v = (uint32_t)hashtbl_find(p->pages, (void*)offset);
+ if (v == 0) {
+ p->ops->page_in(p, offset, PAGE_SIZE);
+ }
+
+ uint32_t ret = (uint32_t)hashtbl_find(p->pages, (void*)offset) >> ENT_FRAME_SHIFT;
+
+ mutex_unlock(&p->lock);
+
+ return ret;
+}
+
+void pager_add_map(pager_t *p, user_region_t *reg) {
+ mutex_lock(&p->lock);
+
+ reg->next_for_pager = p->maps;
+ p->maps = reg;
+
+ mutex_unlock(&p->lock);
+}
+
+void pager_rm_map(pager_t *p, user_region_t *reg) {
+ mutex_lock(&p->lock);
+
+ if (p->maps == reg) {
+ p->maps = reg->next_for_pager;
+ } else {
+ for (user_region_t *it = p->maps; it->next_for_pager != 0; it = it->next_for_pager) {
+ if (it->next_for_pager == reg) {
+ it->next_for_pager = reg->next_for_pager;
+ break;
+ }
+ }
+ }
+ reg->next_for_pager = 0;
+
+ mutex_unlock(&p->lock);
+}
+
+/* vim: set ts=4 sw=4 tw=0 noet :*/
diff --git a/src/kernel/user/process.c b/src/kernel/user/process.c
index 2812fc6..1b3013b 100644
--- a/src/kernel/user/process.c
+++ b/src/kernel/user/process.c
@@ -489,32 +489,38 @@ bool mmap(process_t *proc, void* addr, size_t size, int mode) {
if ((uint32_t)addr & (~PAGE_MASK)) return false;
+ if (addr >= (void*)K_HIGHHALF_ADDR || addr + size > (void*)K_HIGHHALF_ADDR
+ || addr + size <= addr || size == 0) return false;
+
user_region_t *r = (user_region_t*)malloc(sizeof(user_region_t));
if (r == 0) return false;
+ r->proc = proc;
r->addr = addr;
- r->size = PAGE_ALIGN_UP(size);
+ r->size = size;
- if (r->addr >= (void*)K_HIGHHALF_ADDR || r->addr + r->size > (void*)K_HIGHHALF_ADDR
- || r->addr + r->size <= r->addr || r->size == 0) {
- free(r);
- return false;
- }
+ r->own_pager = true;
+ r->pager = new_swap_pager(size);
+ if (r->pager == 0) goto error;
+ r->offset = 0;
bool add_ok = btree_add(proc->regions_idx, r->addr, r);
- if (!add_ok) {
- free(r);
- return false;
- }
+ if (!add_ok) goto error;
r->mode = mode;
r->file = 0;
- r->file_offset = 0;
- r->next = proc->regions;
+ r->next_in_proc = proc->regions;
proc->regions = r;
+ pager_add_map(r->pager, r);
+
return true;
+
+error:
+ if (r && r->pager) delete_pager(r->pager);
+ if (r) free(r);
+ return false;
}
bool mmap_file(process_t *proc, fs_handle_t *h, size_t offset, void* addr, size_t size, int mode) {
@@ -527,34 +533,38 @@ bool mmap_file(process_t *proc, fs_handle_t *h, size_t offset, void* addr, size_
if (!(fmode & FM_MMAP) || !(fmode & FM_READ)) return false;
if ((mode & MM_WRITE) && !(fmode & FM_WRITE)) return false;
+ ASSERT(h->node->pager != 0);
+
+ if (addr >= (void*)K_HIGHHALF_ADDR || addr + size > (void*)K_HIGHHALF_ADDR
+ || addr + size <= addr || size == 0) return false;
+
user_region_t *r = (user_region_t*)malloc(sizeof(user_region_t));
if (r == 0) return false;
r->addr = addr;
- r->size = PAGE_ALIGN_UP(size);
-
- if (r->addr >= (void*)K_HIGHHALF_ADDR || r->addr + r->size > (void*)K_HIGHHALF_ADDR
- || r->addr + r->size <= r->addr || r->size == 0) {
- free(r);
- return false;
- }
+ r->size = size;
bool add_ok = btree_add(proc->regions_idx, r->addr, r);
- if (!add_ok) {
- free(r);
- return false;
- }
+ if (!add_ok) goto error;
ref_file(h);
+ r->file = h;
r->mode = mode;
- r->file = h;
- r->file_offset = offset;
+ r->offset = offset;
+ r->pager = h->node->pager;
+ r->own_pager = false;
- r->next = proc->regions;
+ pager_add_map(r->pager, r);
+
+ r->next_in_proc = proc->regions;
proc->regions = r;
return true;
+
+error:
+ if (r) free(r);
+ return false;
}
bool mchmap(process_t *proc, void* addr, int mode) {
@@ -592,11 +602,11 @@ bool munmap(process_t *proc, void* addr) {
if (r == 0) return false;
if (proc->regions == r) {
- proc->regions = r->next;
+ proc->regions = r->next_in_proc;
} else {
- for (user_region_t *it = proc->regions; it != 0; it = it->next) {
- if (it->next == r) {
- it->next = r->next;
+ for (user_region_t *it = proc->regions; it != 0; it = it->next_in_proc) {
+ if (it->next_in_proc == r) {
+ it->next_in_proc = r->next_in_proc;
break;
}
}
@@ -609,22 +619,20 @@ bool munmap(process_t *proc, void* addr) {
switch_pagedir(proc->pd);
for (void* it = r->addr; it < r->addr + r->size; it += PAGE_SIZE) {
uint32_t ent = pd_get_entry(it);
- uint32_t frame = pd_get_frame(it);
if (ent & PTE_PRESENT) {
- if ((ent & PTE_DIRTY) && (r->mode & MM_WRITE) && r->file != 0) {
- // TODO COMMIT PAGE!!
- }
+ pager_access(r->pager,
+ it - r->addr + r->offset, PAGE_SIZE,
+ (ent & PTE_ACCESSED), (ent & PTE_DIRTY));
pd_unmap_page(it);
- if (r->file != 0) {
- // frame is owned by process
- frame_free(frame, 1);
- }
}
}
switch_pagedir(save_pd);
+ pager_rm_map(r->pager, r);
+
if (r->file != 0) unref_file(r->file);
+ if (r->own_pager) delete_pager(r->pager);
free(r);
@@ -663,12 +671,7 @@ static void proc_usermem_pf(void* p, registers_t *regs, void* addr) {
uint32_t frame;
do {
- if (r->file == 0) {
- frame = frame_alloc(1);
- } else {
- PANIC("Not implemented mmap (AWFUL TODO)");
- // Here we must get the page from the cache
- }
+ frame = pager_get_frame(r->pager, addr - r->addr + r->offset);
if (frame == 0) {
free_some_memory();
}
@@ -677,8 +680,6 @@ static void proc_usermem_pf(void* p, registers_t *regs, void* addr) {
while(!pd_map_page(addr, frame, (r->mode & MM_WRITE) != 0)) {
free_some_memory();
}
-
- if (r->file == 0) memset(addr, 0, PAGE_SIZE); // zero out
}
void probe_for_read(const void* addr, size_t len) {