mirror of
https://github.com/drasko/codezero.git
synced 2026-01-16 21:03:16 +01:00
Changes to mm0 initialization. mm0 building.
Changed mm0 initialization. Removed all cruft about boot-specific task setup and initialization, mm0 now builds with new changes.
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
#ifndef __BOOT_H__
|
||||
#define __BOOT_H__
|
||||
|
||||
#include <vm_area.h>
|
||||
#include <task.h>
|
||||
|
||||
/* Structures to use when sending new task information to vfs */
|
||||
struct task_data {
|
||||
unsigned long tid;
|
||||
unsigned long shpage_address;
|
||||
};
|
||||
|
||||
struct task_data_head {
|
||||
unsigned long total;
|
||||
struct task_data tdata[];
|
||||
};
|
||||
|
||||
int boottask_setup_regions(struct vm_file *file, struct tcb *task,
|
||||
unsigned long task_start, unsigned long task_end);
|
||||
|
||||
int boottask_mmap_regions(struct tcb *task, struct vm_file *file);
|
||||
|
||||
struct tcb *boottask_exec(struct vm_file *f, unsigned long task_region_start,
|
||||
unsigned long task_region_end, struct task_ids *ids);
|
||||
|
||||
int vfs_send_task_data(struct tcb *vfs);
|
||||
|
||||
#endif /* __BOOT_H__ */
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <l4/lib/list.h>
|
||||
#include <l4lib/types.h>
|
||||
#include <posix/sys/types.h>
|
||||
#include <posix/sys/types.h> /* FIXME: Remove this and refer to internal headers */
|
||||
#include <task.h>
|
||||
|
||||
int vfs_read(unsigned long vnum, unsigned long f_offset, unsigned long npages,
|
||||
|
||||
24
conts/posix/mm0/include/linker.h
Normal file
24
conts/posix/mm0/include/linker.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef __LINKER_H__
|
||||
#define __LINKER_H__
|
||||
|
||||
/*
|
||||
* Linker script-defined memory markers.
|
||||
*/
|
||||
|
||||
extern unsigned long __start_text[];
|
||||
extern unsigned long __end_text[];
|
||||
extern unsigned long __start_data[];
|
||||
extern unsigned long __end_data[];
|
||||
extern unsigned long __start_rodata[];
|
||||
extern unsigned long __end_rodata[];
|
||||
extern unsigned long __start_bss[];
|
||||
extern unsigned long __end_bss[];
|
||||
|
||||
extern unsigned long __start_stack[];
|
||||
extern unsigned long __stack[];
|
||||
|
||||
extern unsigned long __start_init[];
|
||||
extern unsigned long __end_init[];
|
||||
extern unsigned long __end[];
|
||||
|
||||
#endif /* __LINKER_H__ */
|
||||
@@ -26,29 +26,49 @@ ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = virtual_base;
|
||||
_start_text = .;
|
||||
.text : AT (ADDR(.text) - pager_offset) { *(.text.head) *(.text) }
|
||||
/* rodata is needed else your strings will link at physical! */
|
||||
.rodata : AT (ADDR(.rodata) - pager_offset) { *(.rodata) }
|
||||
.rodata1 : AT (ADDR(.rodata1) - pager_offset) { *(.rodata1) }
|
||||
.data : AT (ADDR(.data) - pager_offset) { *(.data) }
|
||||
.bss : AT (ADDR(.bss) - pager_offset) { *(.bss) }
|
||||
|
||||
__start_text = .;
|
||||
.text : AT (ADDR(.text) - pager_offset) {
|
||||
*(.text.head) *(.text)
|
||||
}
|
||||
__end_text = .;
|
||||
|
||||
__start_rodata = .;
|
||||
.rodata : AT (ADDR(.rodata) - pager_offset) {
|
||||
*(.rodata)
|
||||
}
|
||||
.rodata1 : AT (ADDR(.rodata1) - pager_offset) {
|
||||
*(.rodata1)
|
||||
}
|
||||
__end_rodata = .;
|
||||
|
||||
__start_data = .;
|
||||
.data : AT (ADDR(.data) - pager_offset) {
|
||||
*(.data)
|
||||
}
|
||||
__end_data = .;
|
||||
|
||||
__start_bss = .;
|
||||
.bss : AT (ADDR(.bss) - pager_offset) {
|
||||
*(.bss)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
__end_bss = .;
|
||||
. += 0x2000; /* BSS doesnt increment link counter??? */
|
||||
.stack : AT (ADDR(.stack) - pager_offset)
|
||||
{
|
||||
|
||||
__start_stack = .;
|
||||
.stack : AT (ADDR(.stack) - pager_offset) {
|
||||
*(.stack)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
__stack = .; /* This is the preallocated boot stack */
|
||||
|
||||
/* Below part is to be discarded after boot */
|
||||
_start_init = .;
|
||||
.init : AT (ADDR(.init) - pager_offset)
|
||||
{
|
||||
__start_init = .;
|
||||
.init : AT (ADDR(.init) - pager_offset) {
|
||||
*(.init.data)
|
||||
*(.init.bootmem)
|
||||
}
|
||||
_end_init = .;
|
||||
_end = .;
|
||||
__end_init = .;
|
||||
__end = .;
|
||||
}
|
||||
|
||||
@@ -42,24 +42,5 @@ int sys_readdir(struct tcb *sender, int fd, void *buf, int count);
|
||||
int sys_mkdir(struct tcb *sender, const char *pathname, unsigned int mode);
|
||||
int sys_chdir(struct tcb *sender, const char *pathname);
|
||||
|
||||
/* Calls from pager that completes a posix call */
|
||||
int pager_open_bypath(struct tcb *pager, char *pathname);
|
||||
int pager_sys_open(struct tcb *sender, l4id_t opener, int fd);
|
||||
int pager_sys_read(struct tcb *sender, unsigned long vnum, unsigned long f_offset,
|
||||
unsigned long npages, void *pagebuf);
|
||||
|
||||
int pager_sys_write(struct tcb *sender, unsigned long vnum, unsigned long f_offset,
|
||||
unsigned long npages, void *pagebuf);
|
||||
|
||||
int pager_sys_close(struct tcb *sender, l4id_t closer, int fd);
|
||||
int pager_update_stats(struct tcb *sender, unsigned long vnum,
|
||||
unsigned long newsize);
|
||||
|
||||
int pager_notify_fork(struct tcb *sender, l4id_t parentid,
|
||||
l4id_t childid, unsigned long utcb_address,
|
||||
unsigned int flags);
|
||||
|
||||
int pager_notify_exit(struct tcb *sender, l4id_t tid);
|
||||
|
||||
#endif /* __MM0_SYSARGS_H__ */
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <shm.h>
|
||||
#include <mmap.h>
|
||||
#include <test.h>
|
||||
#include <boot.h>
|
||||
|
||||
|
||||
/* Receives all registers and origies back */
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Functions used for running initial tasks during boot.
|
||||
*
|
||||
* Copyright (C) 2008 Bahadir Balban
|
||||
*/
|
||||
#include <boot.h>
|
||||
#include <mmap.h>
|
||||
#include <shm.h>
|
||||
#include <utcb.h>
|
||||
#include <l4/api/thread.h>
|
||||
#include <l4lib/arch/syslib.h>
|
||||
|
||||
int boottask_setup_regions(struct vm_file *file, struct tcb *task,
|
||||
unsigned long task_start, unsigned long task_end)
|
||||
{
|
||||
/*
|
||||
* Set task's main address space boundaries. Not all tasks
|
||||
* run in the default user boundaries, e.g. mm0 pager.
|
||||
*/
|
||||
task->start = task_start;
|
||||
task->end = task_end;
|
||||
|
||||
/* Task stack starts right after the environment. */
|
||||
task->stack_end = task->end;
|
||||
task->stack_start = task->stack_end - DEFAULT_STACK_SIZE;
|
||||
|
||||
/* Prepare environment boundaries. */
|
||||
task->args_end = task->stack_end;
|
||||
task->args_start = task->stack_start - DEFAULT_ENV_SIZE;
|
||||
|
||||
/* Currently RO text and RW data are one region. TODO: Fix this */
|
||||
task->data_start = task->start;
|
||||
task->data_end = task->start + page_align_up(file->length);
|
||||
task->text_start = task->data_start;
|
||||
task->text_end = task->data_end;
|
||||
task->entry = task->text_start;
|
||||
|
||||
/* Task's region available for mmap */
|
||||
task->map_start = task->data_end;
|
||||
task->map_end = task->stack_start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used for mmapping boot task regions. These are slightly different
|
||||
* from a vfs executable file.
|
||||
*/
|
||||
int boottask_mmap_regions(struct tcb *task, struct vm_file *file)
|
||||
{
|
||||
void *mapped;
|
||||
|
||||
/*
|
||||
* mmap each task's physical image to task's address space.
|
||||
* TODO: Map data and text separately when available from bootdesc.
|
||||
*/
|
||||
if (IS_ERR(mapped = do_mmap(file, 0, task, task->text_start,
|
||||
VM_READ | VM_WRITE | VM_EXEC | VMA_PRIVATE,
|
||||
__pfn(page_align_up(task->text_end) -
|
||||
task->text_start)))) {
|
||||
printf("do_mmap: failed with %d.\n", (int)mapped);
|
||||
return (int)mapped;
|
||||
}
|
||||
|
||||
/* mmap each task's stack as anonymous memory. */
|
||||
if (IS_ERR(mapped = do_mmap(0, 0, task, task->stack_start,
|
||||
VM_READ | VM_WRITE |
|
||||
VMA_PRIVATE | VMA_ANONYMOUS,
|
||||
__pfn(task->stack_end -
|
||||
task->stack_start)))) {
|
||||
printf("do_mmap: Mapping stack failed with %d.\n",
|
||||
(int)mapped);
|
||||
return (int)mapped;
|
||||
}
|
||||
|
||||
task_setup_utcb(task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main entry point for the creation, initialisation and
|
||||
* execution of a new task.
|
||||
*/
|
||||
struct tcb *boottask_exec(struct vm_file *f, unsigned long task_region_start,
|
||||
unsigned long task_region_end, struct task_ids *ids)
|
||||
{
|
||||
struct tcb *task;
|
||||
int err;
|
||||
|
||||
if (IS_ERR(task = task_create(0, ids, THREAD_NEW_SPACE,
|
||||
TCB_NO_SHARING)))
|
||||
return task;
|
||||
|
||||
if ((err = boottask_setup_regions(f, task, task_region_start,
|
||||
task_region_end)) < 0)
|
||||
return PTR_ERR(err);
|
||||
|
||||
if ((err = boottask_mmap_regions(task, f)) < 0)
|
||||
return PTR_ERR(err);
|
||||
|
||||
if ((err = task_setup_registers(task, 0, 0, 0)) < 0)
|
||||
return PTR_ERR(err);
|
||||
|
||||
/* Add the task to the global task list */
|
||||
global_add_task(task);
|
||||
|
||||
/* Add the file to global vm lists */
|
||||
global_add_vm_file(f);
|
||||
|
||||
/* Prefault all its regions */
|
||||
if (ids->tid == VFS_TID)
|
||||
task_prefault_regions(task, f);
|
||||
|
||||
/* Start the task */
|
||||
if ((err = task_start(task)) < 0)
|
||||
return PTR_ERR(err);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
#include <bootdesc.h>
|
||||
#include <bootm.h>
|
||||
#include <init.h>
|
||||
#include <linker.h>
|
||||
|
||||
#include <l4lib/arch/syslib.h>
|
||||
|
||||
extern unsigned long _end[];
|
||||
extern unsigned long pager_offset;
|
||||
|
||||
struct svc_image *bootdesc_get_image_byname(char *name)
|
||||
@@ -28,7 +29,7 @@ void read_boot_params()
|
||||
/*
|
||||
* End of the executable image is where bootdesc resides
|
||||
*/
|
||||
bootdesc = (struct bootdesc *)_end;
|
||||
bootdesc = (struct bootdesc *)__end;
|
||||
|
||||
/* Check if bootdesc is on an unmapped page */
|
||||
if (is_page_aligned(bootdesc))
|
||||
@@ -42,5 +43,5 @@ void read_boot_params()
|
||||
bootdesc->desc_size);
|
||||
|
||||
if (npages > 0)
|
||||
l4_unmap_helper((void *)page_align_up(_end), npages);
|
||||
l4_unmap_helper((void *)page_align_up(__end), npages);
|
||||
}
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
#include <file.h>
|
||||
#include <init.h>
|
||||
#include <test.h>
|
||||
#include <boot.h>
|
||||
#include <utcb.h>
|
||||
#include <bootm.h>
|
||||
#include <vfs.h>
|
||||
#include <init.h>
|
||||
#include <memory.h>
|
||||
#include <capability.h>
|
||||
|
||||
|
||||
extern unsigned long _start_init[];
|
||||
extern unsigned long _end_init[];
|
||||
#include <linker.h>
|
||||
#include <mmap.h>
|
||||
#include <file.h>
|
||||
#include <syscalls.h>
|
||||
#include <linker.h>
|
||||
|
||||
/* Kernel data acquired during initialisation */
|
||||
__initdata struct initdata initdata;
|
||||
@@ -59,38 +59,92 @@ void print_pfn_range(int pfn, int size)
|
||||
}
|
||||
|
||||
/*
|
||||
* A specialised function for setting up the task environment of mm0.
|
||||
* Essentially all the memory regions are set up but a new task isn't
|
||||
* created, registers aren't assigned, and thread not started, since
|
||||
* these are all already done by the kernel. But we do need a memory
|
||||
* environment for mm0, hence this function.
|
||||
* This sets up the mm0 task struct and memory environment but omits
|
||||
* bits that are already done such as creating a new thread, setting
|
||||
* registers.
|
||||
*/
|
||||
int mm0_task_init(struct vm_file *f, unsigned long task_start,
|
||||
unsigned long task_end, struct task_ids *ids)
|
||||
int pager_setup_task(void)
|
||||
{
|
||||
int err;
|
||||
struct tcb *task;
|
||||
struct task_ids ids;
|
||||
void *mapped;
|
||||
|
||||
/*
|
||||
* The thread itself is already known by the kernel, so we just
|
||||
* allocate a local task structure.
|
||||
* The thread itself is already known by the kernel,
|
||||
* so we just allocate a local task structure.
|
||||
*/
|
||||
BUG_ON(IS_ERR(task = tcb_alloc_init(TCB_NO_SHARING)));
|
||||
if (IS_ERR(task = tcb_alloc_init(TCB_NO_SHARING))) {
|
||||
printf("FATAL: "
|
||||
"Could not allocate tcb for pager.\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
task->tid = ids->tid;
|
||||
task->spid = ids->spid;
|
||||
task->tgid = ids->tgid;
|
||||
/* Set up own ids */
|
||||
l4_getid(&ids);
|
||||
task->tid = ids.tid;
|
||||
task->spid = ids.spid;
|
||||
task->tgid = ids.tgid;
|
||||
|
||||
/* Initialise vfs specific fields. */
|
||||
task->fs_data->rootdir = vfs_root.pivot;
|
||||
task->fs_data->curdir = vfs_root.pivot;
|
||||
|
||||
if ((err = boottask_setup_regions(f, task,
|
||||
task_start, task_end)) < 0)
|
||||
return err;
|
||||
/* Text markers */
|
||||
task->text_start = (unsigned long)__start_text;
|
||||
task->text_end = (unsigned long)__end_rodata;
|
||||
|
||||
if ((err = boottask_mmap_regions(task, f)) < 0)
|
||||
return err;
|
||||
/* Data markers */
|
||||
task->stack_end = (unsigned long)__stack;
|
||||
task->stack_start = (unsigned long)__start_stack;
|
||||
|
||||
/* Stack markers */
|
||||
task->data_start = (unsigned long)__start_data;
|
||||
task->data_end = (unsigned long)__end_data;
|
||||
|
||||
/* Task's region available for mmap */
|
||||
task->map_start = (unsigned long)__stack;
|
||||
task->map_end = 0xF0000000; /* FIXME: Fix this */
|
||||
|
||||
/*
|
||||
* Map all regions as anonymous
|
||||
* (since no real file could back)
|
||||
*/
|
||||
|
||||
/* Map text */
|
||||
if (IS_ERR(mapped =
|
||||
do_mmap(0, 0, task, task->text_start,
|
||||
VMA_ANONYMOUS | VM_READ |
|
||||
VM_WRITE | VM_EXEC | VMA_PRIVATE,
|
||||
__pfn(page_align_up(task->text_end) -
|
||||
task->text_start)))) {
|
||||
printf("do_mmap: failed with %d.\n", (int)mapped);
|
||||
return (int)mapped;
|
||||
}
|
||||
|
||||
/* Map data */
|
||||
if (IS_ERR(mapped =
|
||||
do_mmap(0, 0, task, task->data_start,
|
||||
VMA_ANONYMOUS | VM_READ |
|
||||
VM_WRITE | VM_EXEC | VMA_PRIVATE,
|
||||
__pfn(page_align_up(task->data_end) -
|
||||
task->data_start)))) {
|
||||
printf("do_mmap: failed with %d.\n", (int)mapped);
|
||||
return (int)mapped;
|
||||
}
|
||||
|
||||
/* Map stack */
|
||||
if (IS_ERR(mapped =
|
||||
do_mmap(0, 0, task, task->stack_start,
|
||||
VMA_ANONYMOUS | VM_READ |
|
||||
VM_WRITE | VMA_PRIVATE,
|
||||
__pfn(task->stack_end -
|
||||
task->stack_start)))) {
|
||||
printf("do_mmap: Mapping stack failed with %d.\n",
|
||||
(int)mapped);
|
||||
return (int)mapped;
|
||||
}
|
||||
|
||||
task_setup_utcb(task);
|
||||
|
||||
/* Set pager as child and parent of itself */
|
||||
list_insert(&task->child_ref, &task->children);
|
||||
@@ -110,9 +164,6 @@ int mm0_task_init(struct vm_file *f, unsigned long task_start,
|
||||
/* Add the task to the global task list */
|
||||
global_add_task(task);
|
||||
|
||||
/* Add the file to global vm lists */
|
||||
global_add_vm_file(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -141,9 +192,11 @@ int read_pager_capabilities()
|
||||
int err;
|
||||
|
||||
/* Read number of capabilities */
|
||||
if ((err = l4_capability_control(CAP_CONTROL_NCAPS, 0, &ncaps)) < 0) {
|
||||
printf("l4_capability_control() reading # of capabilities failed.\n"
|
||||
"Could not complete CAP_CONTROL_NCAPS request.\n");
|
||||
if ((err = l4_capability_control(CAP_CONTROL_NCAPS,
|
||||
0, &ncaps)) < 0) {
|
||||
printf("l4_capability_control() reading # of"
|
||||
" capabilities failed.\n Could not "
|
||||
"complete CAP_CONTROL_NCAPS request.\n");
|
||||
goto error;
|
||||
}
|
||||
total_caps = ncaps;
|
||||
@@ -152,9 +205,11 @@ int read_pager_capabilities()
|
||||
caparray = alloc_bootmem(sizeof(struct capability) * ncaps, 0);
|
||||
|
||||
/* Read all capabilities */
|
||||
if ((err = l4_capability_control(CAP_CONTROL_READ_CAPS, 0, caparray)) < 0) {
|
||||
printf("l4_capability_control() reading of capabilities failed.\n"
|
||||
"Could not complete CAP_CONTROL_READ_CAPS request.\n");
|
||||
if ((err = l4_capability_control(CAP_CONTROL_READ_CAPS,
|
||||
0, caparray)) < 0) {
|
||||
printf("l4_capability_control() reading of "
|
||||
"capabilities failed.\n Could not "
|
||||
"complete CAP_CONTROL_READ_CAPS request.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -172,8 +227,10 @@ int read_pager_capabilities()
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
printf("%s: Error, pager has no physmem capability defined.\n",
|
||||
__TASKNAME__);
|
||||
|
||||
printf("%s: Error, pager has no physmem "
|
||||
"capability defined.\n", __TASKNAME__);
|
||||
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
@@ -201,8 +258,8 @@ void release_initdata()
|
||||
* it remains as if it is a used block
|
||||
*/
|
||||
|
||||
l4_unmap(_start_init,
|
||||
__pfn(page_align_up(_end_init - _start_init)),
|
||||
l4_unmap(__start_init,
|
||||
__pfn(page_align_up(__end_init - __start_init)),
|
||||
self_tid());
|
||||
}
|
||||
|
||||
@@ -378,11 +435,28 @@ void init_execve(char *path)
|
||||
*/
|
||||
void copy_init_process(void)
|
||||
{
|
||||
//int fd = sys_open(find_task(self_tid()), "/test0", O_WRITE, 0)
|
||||
int fd;
|
||||
struct svc_image *init_img;
|
||||
unsigned long img_size;
|
||||
void *init_img_start, *init_img_end;
|
||||
|
||||
//sys_write(find_task(self_tid()), fd, __test0_start, __test0_end - __test0_start);
|
||||
if ((fd = sys_open(find_task(self_tid()),
|
||||
"/test0", O_TRUNC | O_RDWR,
|
||||
0)) < 0) {
|
||||
printf("FATAL: Could not open file "
|
||||
"to write initial task.\n");
|
||||
BUG();
|
||||
}
|
||||
|
||||
//sys_close(find_task(self_tid()), fd);
|
||||
init_img = bootdesc_get_image_byname("test0");
|
||||
img_size = init_img->phys_end - init_img->phys_start;
|
||||
|
||||
init_img_start = l4_map_helper((void *)init_img->phys_start, __pfn(img_size));
|
||||
init_img_end = init_img_start + img_size;
|
||||
|
||||
sys_write(find_task(self_tid()), fd, init_img_start, img_size);
|
||||
|
||||
sys_close(find_task(self_tid()), fd);
|
||||
|
||||
}
|
||||
|
||||
@@ -412,6 +486,8 @@ void init(void)
|
||||
|
||||
vfs_init();
|
||||
|
||||
pager_setup_task();
|
||||
|
||||
start_init_process();
|
||||
|
||||
release_initdata();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <memory.h>
|
||||
#include <file.h>
|
||||
#include <user.h>
|
||||
|
||||
#include <linker.h>
|
||||
|
||||
struct address_pool pager_vaddr_pool;
|
||||
|
||||
@@ -40,9 +40,6 @@ static struct pager_virtual_address_id_pool {
|
||||
.bitlimit = ADDRESS_POOL_256MB * 32,
|
||||
};
|
||||
|
||||
/* End of pager image */
|
||||
extern unsigned char _end[];
|
||||
|
||||
/* For supplying contiguous virtual addresses to pager */
|
||||
int pager_address_pool_init(void)
|
||||
{
|
||||
@@ -55,7 +52,8 @@ int pager_address_pool_init(void)
|
||||
address_pool_init_with_idpool(&pager_vaddr_pool,
|
||||
(struct id_pool *)
|
||||
&pager_virtual_address_id_pool,
|
||||
page_align_up((unsigned long)_end + PAGE_SIZE),
|
||||
page_align_up((unsigned long)__end + PAGE_SIZE),
|
||||
/* FIXME: Fix this! Same as mm0's map_start and map_end */
|
||||
(unsigned long)0xF0000000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <exec.h>
|
||||
#include <shm.h>
|
||||
#include <mmap.h>
|
||||
#include <boot.h>
|
||||
#include <test.h>
|
||||
#include <utcb.h>
|
||||
#include <vfs.h>
|
||||
|
||||
Reference in New Issue
Block a user