diff --git a/Makefile.example b/Makefile.example index da2779da..17346871 100644 --- a/Makefile.example +++ b/Makefile.example @@ -1,5 +1,6 @@ TOPDIR = $(shell pwd) ARCH = x86 +# For 64bit support, you have define BIT as 64 BIT=32 NAME = metalsvm LWIPDIRS = lwip/src/arch lwip/src/api lwip/src/core lwip/src/core/ipv4 lwip/src/netif @@ -32,23 +33,29 @@ READELF_FOR_TARGET = $(CROSSCOMPREFIX)readelf MAKE = make RM = rm -rf NASM = nasm +# For 64bit code, you have to use qemu-system-x86_64 QEMU = qemu-system-i386 EMU = qemu GDB = gdb +# For 64bit support, you have to define -felf64 instead of -felf32 NASMFLAGS = -felf32 -g -i$(TOPDIR)/include/metalsvm/ INCLUDE = -I$(TOPDIR)/include -I$(TOPDIR)/arch/$(ARCH)/include -I$(TOPDIR)/lwip/src/include -I$(TOPDIR)/lwip/src/include/ipv4 -I$(TOPDIR)/drivers +# For 64bit support, you have to define "-m64 -mno-red-zone" instead of "-m32 -march=i586" # Compiler options for final code -CFLAGS = -g -m32 -march=i586 -Wall -O2 -fno-builtin -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc $(INCLUDE) $(STACKPROT) +CFLAGS = -g -m32 -march=i586 -Wall -O2 -fstrength-reduce -fomit-frame-pointer -finline-functions -ffreestanding $(INCLUDE) $(STACKPROT) # Compiler options for debuging -#CFLAGS = -g -O -m32 -march=i586 -Wall -fno-builtin -DWITH_FRAME_POINTER -nostdinc $(INCLUDE) $(STACKPROT) +#CFLAGS = -g -O -m32 -march=i586 -Wall -DWITH_FRAME_POINTER -ffreestanding $(INCLUDE) $(STACKPROT) ARFLAGS = rsv LDFLAGS = -T link$(BIT).ld -z max-page-size=4096 --defsym __BUILD_DATE=$(shell date +'%Y%m%d') --defsym __BUILD_TIME=$(shell date +'%H%M%S') STRIP_DEBUG = --strip-debug KEEP_DEBUG = --only-keep-debug OUTPUT_FORMAT = -O elf32-i386 +# For 64bit support, you have to define -m64 instead of "-m32 -march=i586" CFLAGS_FOR_NEWLIB = -m32 -march=i586 -O2 $(STACKPROT) +# For 64bit support, you have to define -m64 instead of "-m32 -march=i586" LDFLAGS_FOR_NEWLIB = -m32 -march=i586 +# For 64bit support, you have to define -felf64 instead of -felf32 NASMFLAGS_FOR_NEWLIB = -felf32 # Prettify output diff --git a/arch/x86/include/asm/gdt.h b/arch/x86/include/asm/gdt.h index fb4bd4c1..8df3115f 100644 --- a/arch/x86/include/asm/gdt.h +++ b/arch/x86/include/asm/gdt.h @@ -36,35 +36,37 @@ extern "C" { #endif /// This segment is a data segment -#define GDT_FLAG_DATASEG 0x02 +#define GDT_FLAG_DATASEG 0x02 /// This segment is a code segment -#define GDT_FLAG_CODESEG 0x0a -#define GDT_FLAG_TSS 0x09 +#define GDT_FLAG_CODESEG 0x0a +#define GDT_FLAG_TSS 0x09 #define GDT_FLAG_TSS_BUSY 0x02 -#define GDT_FLAG_SEGMENT 0x10 +#define GDT_FLAG_SEGMENT 0x10 /// Privilege level: Ring 0 -#define GDT_FLAG_RING0 0x00 +#define GDT_FLAG_RING0 0x00 /// Privilege level: Ring 1 -#define GDT_FLAG_RING1 0x20 +#define GDT_FLAG_RING1 0x20 /// Privilege level: Ring 2 -#define GDT_FLAG_RING2 0x40 +#define GDT_FLAG_RING2 0x40 /// Privilege level: Ring 3 -#define GDT_FLAG_RING3 0x60 +#define GDT_FLAG_RING3 0x60 /// Segment is present -#define GDT_FLAG_PRESENT 0x80 +#define GDT_FLAG_PRESENT 0x80 /** * @brief Granularity of segment limit * - set: segment limit unit is 4 KB (page size) * - not set: unit is bytes */ -#define GDT_FLAG_4K_GRAN 0x80 +#define GDT_FLAG_4K_GRAN 0x80 /** * @brief Default operand size * - set: 32 bit * - not set: 16 bit */ -#define GDT_FLAG_32_BIT 0x40 +#define GDT_FLAG_16_BIT 0x00 +#define GDT_FLAG_32_BIT 0x40 +#define GDT_FLAG_64_BIT 0x20 /** @brief Defines a GDT entry * @@ -74,17 +76,17 @@ extern "C" { */ typedef struct { /// Lower 16 bits of limit range - unsigned short limit_low; + uint16_t limit_low; /// Lower 16 bits of base address - unsigned short base_low; + uint16_t base_low; /// middle 8 bits of base address - unsigned char base_middle; + uint8_t base_middle; /// Access bits - unsigned char access; + uint8_t access; /// Granularity bits - unsigned char granularity; + uint8_t granularity; /// Higher 8 bits of base address - unsigned char base_high; + uint8_t base_high; } __attribute__ ((packed)) gdt_entry_t; /** @brief defines the GDT pointer structure @@ -93,9 +95,9 @@ typedef struct { */ typedef struct { /// Size of the table in bytes (not the number of entries!) - unsigned short limit; + uint16_t limit; /// Address of the table - unsigned int base; + size_t base; } __attribute__ ((packed)) gdt_ptr_t; /// Defines the maximum number of GDT entries @@ -115,16 +117,6 @@ typedef struct { */ void gdt_install(void); -/** @brief Configures and returns a GDT descriptor with chosen attributes - * - * Just feed this function with address, limit and the flags - * you have seen in idt.h - * - * @return a preconfigured gdt descriptor - */ -gdt_entry_t configure_gdt_entry(unsigned long base, unsigned long limit, - unsigned char access, unsigned char gran); - #ifdef __cplusplus } #endif diff --git a/arch/x86/include/asm/idt.h b/arch/x86/include/asm/idt.h index a30cddb1..f8665792 100644 --- a/arch/x86/include/asm/idt.h +++ b/arch/x86/include/asm/idt.h @@ -71,15 +71,21 @@ extern "C" { */ typedef struct { /// Handler function's lower 16 address bits - unsigned short base_lo; + uint16_t base_lo; /// Handler function's segment selector. - unsigned short sel; + uint16_t sel; /// These bits are reserved by Intel - unsigned char always0; + uint8_t always0; /// These 8 bits contain flags. Exact use depends on the type of interrupt gate. - unsigned char flags; + uint8_t flags; /// Higher 16 bits of handler function's base address - unsigned short base_hi; + uint16_t base_hi; +#ifdef CONFIG_X86_64 + /// In 64 bit mode, the "highest" 32 bits of the handler function's base address + uint32_t base_hi64; + /// resvered entries + uint32_t reserved; +#endif } __attribute__ ((packed)) idt_entry_t; /** @brief Defines the idt pointer structure. @@ -89,9 +95,9 @@ typedef struct { */ typedef struct { /// Size of the IDT in bytes (not the number of entries!) - unsigned short limit; + uint16_t limit; /// Base address of the IDT - unsigned int base; + size_t base; } __attribute__ ((packed)) idt_ptr_t; /** @brief Installs IDT @@ -113,16 +119,6 @@ void idt_install(void); void idt_set_gate(unsigned char num, size_t base, unsigned short sel, unsigned char flags); -/** @brief Configures and returns a IDT entry with chosen attributes - * - * Just feed this function with base, selector and the flags - * you have seen in idt.h - * - * @return a preconfigured idt descriptor - */ -idt_entry_t configure_idt_entry(size_t base, unsigned short sel, - unsigned char flags); - #ifdef __cplusplus } #endif diff --git a/arch/x86/include/asm/page.h b/arch/x86/include/asm/page.h index 5c5d5960..d02242fd 100644 --- a/arch/x86/include/asm/page.h +++ b/arch/x86/include/asm/page.h @@ -93,7 +93,7 @@ /** @brief Page table structure * * This structure keeps page table entries.\n - * A page table consists of 1024 entries. + * On a 32bit system, a page table consists normally of 1024 entries. */ typedef struct page_table { @@ -104,7 +104,7 @@ typedef struct page_table /** @brief Page directory structure * * This structure keeps page directory entries.\ - * A page directory consists of 1024 entries. + * On a 32bit system, a page directory consists normally of 1024 entries. */ typedef struct page_dir { diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index e55f5c10..cfa6544e 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -335,7 +335,9 @@ uint32_t read_eip(void); inline static int system_init(void) { gdt_install(); +#ifdef CONFIG_X86_32 apic_init(); +#endif #ifdef CONFIG_PCI pci_init(); #endif diff --git a/arch/x86/include/asm/stddef.h b/arch/x86/include/asm/stddef.h index 50d3258c..e83032e1 100644 --- a/arch/x86/include/asm/stddef.h +++ b/arch/x86/include/asm/stddef.h @@ -85,33 +85,79 @@ typedef unsigned int wint_t; * All the interrupt handler routines use this type for their only parameter. */ struct state { +#ifdef CONFIG_X86_32 /// EDI register - unsigned int edi; + uint32_t edi; /// ESI register - unsigned int esi; + uint32_t esi; /// EBP register - unsigned int ebp; + uint32_t ebp; /// ESP register - unsigned int esp; + uint32_t esp; /// EBX register - unsigned int ebx; + uint32_t ebx; /// EDX register - unsigned int edx; + uint32_t edx; /// ECX register - unsigned int ecx; + uint32_t ecx; /// EAX register - unsigned int eax; /* pushed by 'pusha' */ + uint32_t eax; /* pushed by 'pusha' */ /// Interrupt number - unsigned int int_no; + uint32_t int_no; // pushed by the processor automatically - unsigned int error; - unsigned int eip; - unsigned int cs; - unsigned int eflags; - unsigned int useresp; - unsigned int ss; + uint32_t error; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t useresp; + uint32_t ss; +#elif defined(CONFIG_X86_64) + /// R15 register + uint64_t r15; + /// R14 register + uint64_t r14; + /// R13 register + uint64_t r13; + /// R12 register + uint64_t r12; + /// R11 register + uint64_t r11; + /// R10 register + uint64_t r10; + /// R9 register + uint64_t r9; + /// R8 register + uint64_t r8; + /// RDI register + uint64_t rdi; + /// RSI register + uint64_t rsi; + /// RBP register + uint64_t rbp; + /// (pseudo) RSP register + uint64_t rsp; + /// RBX register + uint64_t rbx; + /// RDX register + uint64_t rdx; + /// RCX register + uint64_t rcx; + /// RAX register + uint64_t rax; + + /// Interrupt number + uint64_t int_no; + + // pushed by the processor automatically + uint64_t error; + uint64_t rip; + uint64_t cs; + uint64_t rflags; + uint64_t userrsp; + uint64_t ss; +#endif }; uint32_t apic_cpu_id(void); diff --git a/arch/x86/include/asm/string.h b/arch/x86/include/asm/string.h index 66f5e617..ee39ff4a 100644 --- a/arch/x86/include/asm/string.h +++ b/arch/x86/include/asm/string.h @@ -96,11 +96,12 @@ inline static void *memcpy(void *dest, const void *src, size_t count) */ inline static void *memcpy(void* dest, const void *src, size_t count) { - int32_t i, j, k; + size_t i, j, k; if (BUILTIN_EXPECT(!dest || !src, 0)) return dest; +#ifdef CONFIG_X86_32 asm volatile ( "cld; rep movsl\n\t" "movl %4, %%ecx\n\t" @@ -108,6 +109,15 @@ inline static void *memcpy(void* dest, const void *src, size_t count) "rep movsb\n\t" : "=&c"(i), "=&D"(j), "=&S"(k) : "0"(count/4), "g"(count), "1"(dest), "2"(src) : "memory","cc"); +#elif defined(CONFIG_X86_64) + asm volatile ( + "cld; rep movsq\n\t" + "movq %4, %%rcx\n\t" + "andq $7, %%rcx\n\t" + "rep movsb\n\t" + : "=&c"(i), "=&D"(j), "=&S"(k) + : "0"(count/8), "g"(count), "1"(dest), "2"(src) : "memory","cc"); +#endif return dest; } @@ -128,7 +138,7 @@ inline static void *memcpy(void* dest, const void *src, size_t count) */ inline static void *memset(void* dest, int val, size_t count) { - int32_t i, j; + size_t i, j; if (BUILTIN_EXPECT(!dest, 0)) return dest; @@ -155,7 +165,7 @@ inline static void *memset(void* dest, int val, size_t count) */ inline static void *memset(void* dest, int val, size_t count) { - int32_t i, j; + size_t i, j; if (BUILTIN_EXPECT(!dest, 0)) return dest; @@ -184,15 +194,22 @@ inline static void *memset(void* dest, int val, size_t count) inline static size_t strlen(const char* str) { size_t len = 0; - uint32_t i, j; + size_t i, j; if (BUILTIN_EXPECT(!str, 0)) return len; +#ifdef CONFIG_X86_32 asm volatile("not %%ecx; cld; repne scasb; not %%ecx; dec %%ecx" : "=&c"(len), "=&D"(i), "=&a"(j) : "2"(0), "1"(str), "0"(len) : "memory","cc"); +#elif defined(CONFIG_X86_64) + asm volatile("not %%rcx; cld; repne scasb; not %%rcx; dec %%rcx" + : "=&c"(len), "=&D"(i), "=&a"(j) + : "2"(0), "1"(str), "0"(len) + : "memory","cc"); +#endif return len; } diff --git a/arch/x86/include/asm/tasks.h b/arch/x86/include/asm/tasks.h index a5367512..6761b240 100644 --- a/arch/x86/include/asm/tasks.h +++ b/arch/x86/include/asm/tasks.h @@ -95,11 +95,11 @@ static inline int jump_to_user_code(uint32_t ep, uint32_t stack) asm volatile ("mov %0, %%ds; mov %0, %%fs; mov %0, %%gs; mov %0, %%es" :: "r"(0x23)); asm volatile ("push $0x23; push %0; push $0x1B; push %1" :: "r"(stack), "r"(ep)); asm volatile ("lret" ::: "cc"); -#else -#warning Currently, not supported! -#endif return 0; +#else + return -22; +#endif } /** @brief determines the stack of a specific task diff --git a/arch/x86/include/asm/tss.h b/arch/x86/include/asm/tss.h index 9e905449..17b2e8ab 100644 --- a/arch/x86/include/asm/tss.h +++ b/arch/x86/include/asm/tss.h @@ -35,6 +35,7 @@ extern "C" { /** @brief The tast state segment structure */ typedef struct { +#ifdef CONFIG_X86_32 uint16_t backlink, __blh; uint32_t esp0; uint16_t ss0, __ss0h; @@ -55,6 +56,24 @@ typedef struct { uint16_t gs, __gsh; uint16_t ldt, __ldth; uint16_t trace, bitmap; +#endif +#ifdef CONFIG_X86_64 + uint16_t res0, res1; // reserved entries + uint64_t rsp0; + uint64_t rsp1; + uint64_t rsp2; + uint32_t res2, res3; // reserved entries + uint64_t ist_rsp1; + uint64_t ist_rsp2; + uint64_t ist_rsp3; + uint64_t ist_rsp4; + uint64_t ist_rsp5; + uint64_t ist_rsp6; + uint64_t ist_rsp7; + uint32_t res4; // reserved entries + uint32_t res5; // reserved entries + uint16_t res6, bitmap; +#endif } __attribute__ ((packed)) tss_t; #ifdef __cplusplus diff --git a/arch/x86/kernel/entry32.asm b/arch/x86/kernel/entry32.asm index c9c0f4dc..49e30351 100644 --- a/arch/x86/kernel/entry32.asm +++ b/arch/x86/kernel/entry32.asm @@ -66,11 +66,13 @@ ALIGN 4 stublet: ; initialize stack pointer. mov esp, [default_stack_pointer] +; save pointer to the multiboot structure + push ebx ; initialize cpu features call cpu_init ; interpret multiboot information extern multiboot_init - push ebx +; pointer to the multiboot structure is already pushed call multiboot_init add esp, 4 @@ -769,8 +771,8 @@ switch_context: pushf ; EFLAGS push DWORD 0x8 ; CS push DWORD rollback ; EIP - push DWORD 0 ; Interrupt number - push DWORD 0xc0edbabe ; Error code + push DWORD 0x0 ; Interrupt number + push DWORD 0x00edbabe ; Error code pusha ; Registers... jmp common_switch diff --git a/arch/x86/kernel/entry64.asm b/arch/x86/kernel/entry64.asm index bb06c3b1..5a35099a 100644 --- a/arch/x86/kernel/entry64.asm +++ b/arch/x86/kernel/entry64.asm @@ -91,17 +91,17 @@ SECTION .data ; create default page tables for the 64bit kernel global boot_pgd ; aka PML4 ALIGN 4096 ; of course, the page tables have to be page aligned -NOPTS equ 2 +NOPTS equ 512 boot_pgd times 512 DQ 0 -pdpt times 512 DQ 0 -pd times 512 DQ 0 -pt times (NOPTS*512) DQ 0 +boot_pdpt times 512 DQ 0 +boot_pd times 512 DQ 0 +boot_pt times (NOPTS*512) DQ 0 SECTION .text ALIGN 4 stublet: mov esp, startup_stack-4 - push ebx ; save pointer to multiboot structure + push ebx ; save pointer to the multiboot structure mov eax, cr0 ; enable caching, disable paging and fpu emulation and eax, 0x1ffffffb @@ -144,13 +144,13 @@ stublet: mov cr3, edi ; So lets make PML4T[0] point to the PDPT and so on: - mov DWORD [edi], pdpt ; Set the double word at the destination index to pdpt. + mov DWORD [edi], boot_pdpt ; Set the double word at the destination index to pdpt. or DWORD [edi], 0x00000003 ; Set present and writeable bit - mov edi, pdpt - mov DWORD [edi], pd ; Set the double word at the destination index to pd. + mov edi, boot_pdpt + mov DWORD [edi], boot_pd ; Set the double word at the destination index to pd. or DWORD [edi], 0x00000003 ; Set present and writeable bit - mov edi, pd - mov ebx, pt + mov edi, boot_pd + mov ebx, boot_pt mov ecx, NOPTS L0: mov DWORD [edi], ebx ; Set the double word at the destination index to pt. @@ -162,16 +162,26 @@ L0: ; map the VGA address into the virtual address space mov edi, 0xB8000 shr edi, 9 ; (edi >> 12) * 8 - add edi, pt + add edi, boot_pt mov ebx, 0xB8000 or ebx, 0x00000003 mov DWORD [edi], ebx + ; map multiboot structure into the virtual address space + mov edi, [esp] + and edi, 0xFFFFF000 + shr edi, 9 ; (edi >> 12) * 8 + add edi, boot_pt + mov ebx, [esp] + and ebx, 0xFFFFF000 + or ebx, 0x00000003 + mov DWORD [edi], ebx + extern kernel_start ; defined in linker script extern kernel_end mov edi, kernel_start shr edi, 9 ; (kernel_start >> 12) * 8 - add edi, pt + add edi, boot_pt mov ebx, kernel_start or ebx, 0x00000003 mov ecx, kernel_end ; determine kernel size in number of pages @@ -246,22 +256,12 @@ cpu_init: ; mov cr4, eax ; ret -; This will set up our new segment registers. We need to do -; something special in order to set CS. We do what is called a -; far jump. A jump that includes a segment as well as an offset. -; This is declared in C as 'extern void gdt_flush();' +; This will set up our new segment registers and is declared in +; C as 'extern void gdt_flush();' global gdt_flush extern gp gdt_flush: -; lgdt [gp] -; mov ax, 0x10 -; mov ds, ax -; mov es, ax -; mov fs, ax -; mov gs, ax -; mov ss, ax -; jmp 0x08:flush2 -flush2: + lgdt [gp] ret ; determines the current instruction pointer (after the jmp) @@ -603,7 +603,7 @@ isrsyscall: push rdi push rsi push rbp - push Qword 0 + push rsp push rbx push rdx push rcx @@ -616,7 +616,7 @@ isrsyscall: pop rcx pop rdx pop rbx - add rsp, 1*8 + add rsp, 8 pop rbp pop rsi pop rdi @@ -627,7 +627,7 @@ isrsyscall: pop r12 pop r13 pop r14 - iret + iretq global irq0 global irq1 @@ -926,17 +926,20 @@ ALIGN 8 switch_context: ; create on the stack a pseudo interrupt ; afterwards, we switch to the task with iret - mov rax, [rdi] ; rdi contains the address to store the old rsp - pushf ; EFLAGS - push QWORD 0x8 ; CS + mov rax, rdi ; rdi contains the address to store the old rsp + push QWORD 0x10 ; SS + push rsp ; RSP + add QWORD [rsp], 8*1 + pushf ; RFLAGS + push QWORD 0x08 ; CS push QWORD rollback ; RIP - push QWORD 0 ; Interrupt number - push QWORD 0x0edbabe ; Error code + push QWORD 0x00 ; Interrupt number + push QWORD 0x00edbabe ; Error code push rax push rcx push rdx push rbx - push Qword 0 + push rsp push rbp push rsi push rdi @@ -961,7 +964,7 @@ common_stub: push rcx push rdx push rbx - push Qword 0 + push rsp push rbp push rsi push rdi @@ -994,7 +997,7 @@ common_switch: %else xor rax, rax %endif - add eax, task_state_segments + add rax, task_state_segments ; set rsp0 in TSS mov [rax+4], rsp @@ -1013,13 +1016,13 @@ no_context_switch: pop rdi pop rsi pop rbp - add rsp, 1*8 + add rsp, 8 pop rbx pop rdx pop rcx pop rax add rsp, 16 - iret + iretq SECTION .note.GNU-stack noalloc noexec nowrite progbits diff --git a/arch/x86/kernel/gdt.c b/arch/x86/kernel/gdt.c index 84251c3b..ebd73536 100644 --- a/arch/x86/kernel/gdt.c +++ b/arch/x86/kernel/gdt.c @@ -61,8 +61,9 @@ size_t get_stack(uint32_t id) return (size_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t); } -int register_task(task_t* task) { - uint16_t sel; +int register_task(task_t* task) +{ + uint16_t sel; sel = (CORE_ID+5) << 3; asm volatile ("mov %0, %%ax; ltr %%ax" : : "ir"(sel) : "%eax"); @@ -128,8 +129,9 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg) { uint16_t cs = 0x08; uint32_t id; - uint32_t *stack; + size_t *stack; struct state *stptr; + size_t state_size; if (BUILTIN_EXPECT(!task, 0)) return -EINVAL; @@ -137,17 +139,18 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg) memset(kstacks[id], 0xCD, KERNEL_STACK_SIZE); -#ifdef CONFIG_X86_32 /* The difference between setting up a task for SW-task-switching * and not for HW-task-switching is setting up a stack and not a TSS. * This is the stack which will be activated and popped off for iret later. */ - stack = (uint32_t*) (kstacks[id] + KERNEL_STACK_SIZE - sizeof(uint32_t)); + stack = (size_t*) (kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t)); /* The next three things on the stack are a marker for debugging purposes, ... */ *stack-- = 0xDEADBEEF; +#ifdef CONFIG_X86_32 /* the first-function-to-be-called's arguments, ... */ *stack-- = (size_t) arg; +#endif /* and the "caller" we shall return to. * This procedure cleans the task after exit. */ *stack = (size_t) leave_kernel_task; @@ -156,58 +159,71 @@ int create_default_frame(task_t* task, entry_point_t ep, void* arg) * The stack must look like the stack of a task which was * scheduled away previously. */ - stack = (uint32_t*) ((size_t) stack - sizeof(struct state) + 2*sizeof(size_t)); + /* In 64bit mode, he stack pointer (SS:RSP) is pushed unconditionally on interrupts. + * In legacy modes, this push is conditional and based on a change in current privilege level (CPL).*/ +#ifdef CONFIG_X86_32 + state_size = sizeof(struct state) - 2*sizeof(size_t); +#else + state_size = sizeof(struct state); +#endif + stack = (size_t*) ((size_t) stack - state_size); stptr = (struct state *) stack; - memset(stptr, 0x00, sizeof(struct state) - 2*sizeof(size_t)); - stptr->esp = (size_t)stack + sizeof(struct state) - 2*sizeof(size_t); + memset(stptr, 0x00, state_size); +#ifdef CONFIG_X86_32 + stptr->esp = (size_t)stack + state_size; +#else + stptr->rsp = (size_t)stack + state_size; + /* the first-function-to-be-called's arguments, ... */ + stptr->rdi = (size_t) arg; +#endif stptr->int_no = 0xB16B00B5; stptr->error = 0xC03DB4B3; /* The instruction pointer shall be set on the first function to be called * after IRETing */ - stptr->eip = (uint32_t)ep; +#ifdef CONFIG_X86_32 + stptr->eip = (size_t)ep; +#else + stptr->rip = (size_t)ep; +#endif stptr->cs = cs; +#ifdef CONFIG_X86_32 stptr->eflags = 0x1202; // the creation of a kernel tasks didn't change the IOPL level // => useresp & ss is not required - +#else + stptr->rflags = 0x1202; + stptr->ss = 0x10; + stptr->userrsp = stptr->rsp; +#endif /* Set the task's stack pointer entry to the stack we have crafted right now. */ task->stack = (size_t*)stack; -#else -#warning Currently, not supported - return -1; -#endif return 0; } -/* Setup a descriptor in the Global Descriptor Table */ +/** @brief Configures GDT descriptor with chosen attributes + * + * Just feed this function with address, limit and the flags + * you have seen in gdt.h + */ static void gdt_set_gate(int num, unsigned long base, unsigned long limit, - unsigned char access, unsigned char gran) + unsigned char access, unsigned char gran) { - gdt[num] = configure_gdt_entry(base, limit, access, gran); -} - -gdt_entry_t configure_gdt_entry(unsigned long base, unsigned long limit, - unsigned char access, unsigned char gran) -{ - gdt_entry_t desc; /* Setup the descriptor base address */ - desc.base_low = (base & 0xFFFF); - desc.base_middle = (base >> 16) & 0xFF; - desc.base_high = (base >> 24) & 0xFF; + gdt[num].base_low = (base & 0xFFFF); + gdt[num].base_middle = (base >> 16) & 0xFF; + gdt[num].base_high = (base >> 24) & 0xFF; /* Setup the descriptor limits */ - desc.limit_low = (limit & 0xFFFF); - desc.granularity = ((limit >> 16) & 0x0F); + gdt[num].limit_low = (limit & 0xFFFF); + gdt[num].granularity = ((limit >> 16) & 0x0F); /* Finally, set up the granularity and access flags */ - desc.granularity |= (gran & 0xF0); - desc.access = access; - - return desc; + gdt[num].granularity |= (gran & 0xF0); + gdt[num].access = access; } /* @@ -219,13 +235,21 @@ gdt_entry_t configure_gdt_entry(unsigned long base, unsigned long limit, */ void gdt_install(void) { - unsigned int i; + unsigned int i, mode; memset(task_state_segments, 0x00, MAX_CORES*sizeof(tss_t)); +#ifdef CONFIG_X86_32 + mode = GDT_FLAG_32_BIT; +#elif defined(CONFIG_X86_64) + mode = GDT_FLAG_64_BIT; +#else +#error invalid mode +#endif + /* Setup the GDT pointer and limit */ gp.limit = (sizeof(gdt_entry_t) * GDT_ENTRIES) - 1; - gp.base = (unsigned int) &gdt; + gp.base = (size_t) &gdt; /* Our NULL descriptor */ gdt_set_gate(0, 0, 0, 0, 0); @@ -237,7 +261,7 @@ void gdt_install(void) */ gdt_set_gate(1, 0, 0xFFFFFFFF, GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, - GDT_FLAG_4K_GRAN | GDT_FLAG_32_BIT); + GDT_FLAG_4K_GRAN | mode); /* * The third entry is our Data Segment. It's EXACTLY the @@ -246,33 +270,37 @@ void gdt_install(void) */ gdt_set_gate(2, 0, 0xFFFFFFFF, GDT_FLAG_RING0 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, - GDT_FLAG_4K_GRAN | GDT_FLAG_32_BIT); + GDT_FLAG_4K_GRAN | mode); /* * Create code segement for userspace applications (ring 3) */ gdt_set_gate(3, 0, 0xFFFFFFFF, GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_CODESEG | GDT_FLAG_PRESENT, - GDT_FLAG_4K_GRAN | GDT_FLAG_32_BIT); + GDT_FLAG_4K_GRAN | mode); /* * Create data segement for userspace applications (ring 3) */ gdt_set_gate(4, 0, 0xFFFFFFFF, GDT_FLAG_RING3 | GDT_FLAG_SEGMENT | GDT_FLAG_DATASEG | GDT_FLAG_PRESENT, - GDT_FLAG_4K_GRAN | GDT_FLAG_32_BIT); + GDT_FLAG_4K_GRAN | mode); /* * Create TSS for each task at ring0 (we use these segments for task switching) */ for(i=0; i> 16) & 0xFFFF; - - /* The segment or 'selector' that this IDT entry will use - * is set here, along with any access flags */ - desc.sel = sel; - desc.always0 = 0; - desc.flags = flags; - - return desc; -} - /* * Use this function to set an entry in the IDT. Alot simpler * than twiddling with the GDT ;) @@ -67,7 +49,19 @@ idt_entry_t configure_idt_entry(size_t base, unsigned short sel, void idt_set_gate(unsigned char num, size_t base, unsigned short sel, unsigned char flags) { - idt[num] = configure_idt_entry(base, sel, flags); + /* The interrupt routine's base address */ + idt[num].base_lo = (base & 0xFFFF); + idt[num].base_hi = (base >> 16) & 0xFFFF; +#ifdef CONFIG_X86_64 + idt[num].base_hi64 = (base >> 32) & 0xFFFFFFFF; + idt[num].reserved = 0x0; +#endif + + /* The segment or 'selector' that this IDT entry will use + * is set here, along with any access flags */ + idt[num].sel = sel; + idt[num].always0 = 0x0; + idt[num].flags = flags; } extern void isrsyscall(void); @@ -82,7 +76,7 @@ void idt_install(void) /* Sets the special IDT pointer up, just like in 'gdt.c' */ idtp.limit = (sizeof(idt_entry_t) * 256) - 1; - idtp.base = (unsigned int)&idt; + idtp.base = (size_t)&idt; /* Add any new ISRs to the IDT here using idt_set_gate */ idt_set_gate(INT_SYSCALL, (size_t)isrsyscall, KERNEL_CODE_SELECTOR, diff --git a/arch/x86/kernel/isrs.c b/arch/x86/kernel/isrs.c index 73f1e203..5d61321a 100644 --- a/arch/x86/kernel/isrs.c +++ b/arch/x86/kernel/isrs.c @@ -230,9 +230,14 @@ static void fault_handler(struct state *s) { if (s->int_no < 32) { kputs(exception_messages[s->int_no]); +#ifdef CONFIG_X86_32 kprintf(" Exception (%d) at 0x%x:0x%x on core %u, error code 0x%x, eflags 0x%x\n", s->int_no, s->cs, s->eip, CORE_ID, s->error, s->eflags); - +#elif defined(CONFIG_X86_64) + kprintf(" Exception (%d) at 0x%llx:0x%llx on core %u, error code 0x%llx, rflags 0x%llx\n", + s->int_no, s->cs, s->rip, CORE_ID, s->error, s->rflags); +#endif + /* Now, we signalize that we have handled the interrupt */ if (apic_is_enabled()) apic_eoi(); diff --git a/arch/x86/kernel/string64.asm b/arch/x86/kernel/string64.asm index f2a25f92..5dc5d855 100644 --- a/arch/x86/kernel/string64.asm +++ b/arch/x86/kernel/string64.asm @@ -8,20 +8,11 @@ ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ; -; TODO -%if 0 - -[BITS 32] +[BITS 64] SECTION .text global strcpy strcpy: - push ebp - mov ebp, esp - push edi - push esi - - mov esi, [ebp+12] - mov edi, [ebp+8] + push rdi L1: lodsb @@ -29,25 +20,16 @@ L1: test al, al jne L1 - mov eax, [ebp+8] - pop esi - pop edi - pop ebp + pop rax ret global strncpy strncpy: - push ebp - mov ebp, esp - push edi - push esi - - mov ecx, [ebp+16] - mov esi, [ebp+12] - mov edi, [ebp+8] + push rdi + mov rcx, rdx L2: - dec ecx + dec rcx js L3 lodsb stosb @@ -57,12 +39,10 @@ L2: stosb L3: - mov eax, [ebp+8] - pop esi - pop edi - pop ebp + pop rax ret +%if 0 ; The following function is derived from JamesM's kernel development tutorials ; (http://www.jamesmolloy.co.uk/tutorial_html/) global copy_page_physical diff --git a/arch/x86/mm/page32.c b/arch/x86/mm/page32.c index 6b8ec587..010dec20 100644 --- a/arch/x86/mm/page32.c +++ b/arch/x86/mm/page32.c @@ -78,7 +78,6 @@ inline static size_t copy_page_table(task_t* task, uint32_t pgd_index, page_tabl page_table_t* new_pgt; size_t phyaddr; -#ifdef CONFIG_X86_32 if (BUILTIN_EXPECT(!pgt, 0)) return 0; @@ -89,7 +88,7 @@ inline static size_t copy_page_table(task_t* task, uint32_t pgd_index, page_tabl if (counter) (*counter)++; - for(i=0; i<1024; i++) { + for(i=0; ientries[i] & PAGE_MASK) { if (!(pgt->entries[i] & PG_USER)) { // Kernel page => copy only page entries @@ -114,10 +113,6 @@ inline static size_t copy_page_table(task_t* task, uint32_t pgd_index, page_tabl phyaddr = virt_to_phys((size_t)new_pgt); return phyaddr; -#else -#warning Currently, not supported - return 0; -#endif } int create_pgd(task_t* task, int copy) @@ -154,7 +149,7 @@ int create_pgd(task_t* task, int copy) spinlock_lock(&kslock); - for(i=0; i<1024; i++) { + for(i=0; ientries[i] = boot_pgd.entries[i]; // only kernel entries will be copied if (pgd->entries[i] && !(pgd->entries[i] & PG_USER)) @@ -211,7 +206,7 @@ int drop_pgd(void) spinlock_lock(&task->pgd_lock); - for(i=0; i<1024; i++) { + for(i=0; ientries[i] & PG_USER) { put_page(pgd->entries[i] & PAGE_MASK); pgd->entries[i] = 0; @@ -310,9 +305,9 @@ size_t map_region(size_t viraddr, size_t phyaddr, uint32_t npages, uint32_t flag // set the new page table into the directory if (flags & MAP_USER_SPACE) - task->pgd->entries[index] = (size_t)pgt|USER_TABLE; + task->pgd->entries[index] = (uint32_t)pgt|USER_TABLE; else - task->pgd->entries[index] = (size_t)pgt|KERN_TABLE; + task->pgd->entries[index] = (uint32_t)pgt|KERN_TABLE; // if paging is already enabled, we need to use the virtual address if (paging_enabled) @@ -392,7 +387,7 @@ size_t map_region(size_t viraddr, size_t phyaddr, uint32_t npages, uint32_t flag int change_page_permissions(size_t start, size_t end, uint32_t flags) { uint32_t index1, index2, newflags; - size_t viraddr = start & PAGE_MASK; + size_t viraddr = start & 0xFFFFF000; size_t phyaddr; page_table_t* pgt; page_dir_t* pgd; @@ -708,7 +703,7 @@ int arch_paging_init(void) // now, we create a self reference per_core(current_task)->pgd->entries[index1] = (((size_t) pgt) & PAGE_MASK)|KERN_TABLE; - pgt->entries[index2] = ((size_t) pgt & PAGE_MASK)|KERN_PAGE; + pgt->entries[index2] = ((size_t) pgt & 0xFFFFF000)|KERN_PAGE; // create the other PGTs for the kernel space for(i=0; imods_count * sizeof(multiboot_module_t) >> PAGE_SHIFT; if (mb_info->mods_count * sizeof(multiboot_module_t) & (PAGE_SIZE-1)) npages++; - map_region((size_t) (mb_info->mods_addr), (size_t) (mb_info->mods_addr), npages, MAP_KERNEL_SPACE); + map_region((size_t) mb_info->mods_addr & PAGE_MASK, (size_t) mb_info->mods_addr & PAGE_MASK, npages, MAP_KERNEL_SPACE); for(i=0; imods_count; i++, mmodule++) { // map physical address to the same virtual address npages = (mmodule->mod_end - mmodule->mod_start) >> PAGE_SHIFT; if (mmodule->mod_end & (PAGE_SIZE-1)) npages++; - map_region((size_t) (mmodule->mod_start), (size_t) (mmodule->mod_start), npages, MAP_KERNEL_SPACE); + kprintf("Map module %s at 0x%x (%u pages)\n", (char*) mmodule->cmdline, mmodule->mod_start, npages); + map_region((size_t) mmodule->mod_start & PAGE_MASK, (size_t) mmodule->mod_start & PAGE_MASK, npages, MAP_KERNEL_SPACE); } } #endif @@ -814,7 +810,7 @@ int arch_paging_init(void) #endif /* enable paging */ - write_cr3((size_t) &boot_pgd); + write_cr3((uint32_t) &boot_pgd); i = read_cr0(); i = i | (1 << 31); write_cr0(i); diff --git a/arch/x86/mm/page64.c b/arch/x86/mm/page64.c index 5312b413..9f9ba41b 100644 --- a/arch/x86/mm/page64.c +++ b/arch/x86/mm/page64.c @@ -44,9 +44,8 @@ * * 0x00000000 - 0x000FFFFF: reserved for IO devices (16MB) * 0x00100000 - 0x0DEADFFF: Kernel (size depends on the configuration) (221MB) - * 0x0DEAE000 - 0x3FFFEFFF: Kernel heap (801MB) - * 0x3FFFF000 - 0x3FFFFFFF: Page Tables are mapped in this region (4KB) - * (The last 256 entries belongs to kernel space) + * 0x0DEAE000 - 0x3FFFFFFF: Kernel heap + * */ /* @@ -57,147 +56,24 @@ extern const void kernel_start; extern const void kernel_end; // boot task's page directory and page directory lock -static page_dir_t boot_pgd = {{[0 ... PGT_ENTRIES-1] = 0}}; +extern page_dir_t boot_pgd; static spinlock_t kslock = SPINLOCK_INIT; -//static int paging_enabled = 0; +static int paging_enabled = 0; page_dir_t* get_boot_pgd(void) { return &boot_pgd; } -#if 0 -/* - * TODO: We create a full copy of the current task. Copy-On-Access will be the better solution. - * - * No PGD locking is needed because onls create_pgd use this function and holds already the - * PGD lock. - */ -inline static size_t copy_page_table(task_t* task, uint32_t pgd_index, page_table_t* pgt, int* counter) -{ - uint32_t i; - page_table_t* new_pgt; - size_t phyaddr; - -#ifdef CONFIG_X86_32 - if (BUILTIN_EXPECT(!pgt, 0)) - return 0; - - new_pgt = kmalloc(sizeof(page_table_t)); - if (!new_pgt) - return 0; - memset(new_pgt, 0x00, sizeof(page_table_t)); - if (counter) - (*counter)++; - - for(i=0; i<1024; i++) { - if (pgt->entries[i] & PAGE_MASK) { - if (!(pgt->entries[i] & PG_USER)) { - // Kernel page => copy only page entries - new_pgt->entries[i] = pgt->entries[i]; - continue; - } - - phyaddr = get_page(); - if (!phyaddr) - continue; - if (counter) - (*counter)++; - - copy_page_physical((void*)phyaddr, (void*) (pgt->entries[i] & PAGE_MASK)); - - new_pgt->entries[i] = phyaddr | (pgt->entries[i] & 0xFFF); - - atomic_int32_inc(&task->user_usage); - } - } - - phyaddr = virt_to_phys((size_t)new_pgt); - - return phyaddr; -#else -#warning Currently, not supported - return 0; -#endif -} -#endif - int create_pgd(task_t* task, int copy) { -#if 0 - page_dir_t* pgd; - page_table_t* pgt; - page_table_t* pgt_container; - uint32_t i; - uint32_t index1, index2; - size_t viraddr, phyaddr; - int counter = 0; - task_t* curr_task = per_core(current_task); + // Currently, we support only kernel tasks + // => all tasks are able to use the same pgd if (BUILTIN_EXPECT(!paging_enabled, 0)) return -EINVAL; - // we already know the virtual address of the "page table container" - // (see file header) - pgt_container = (page_table_t*) ((KERNEL_SPACE - PAGE_SIZE) & PAGE_MASK); - - // create new page directory for the new task - pgd = kmalloc(sizeof(page_dir_t)); - if (!pgd) - return -ENOMEM; - memset(pgd, 0x00, sizeof(page_dir_t)); - - // create a new "page table container" for the new task - pgt = kmalloc(sizeof(page_table_t)); - if (!pgt) { - kfree(pgd, sizeof(page_dir_t)); - return -ENOMEM; - } - memset(pgt, 0x00, sizeof(page_table_t)); - - spinlock_lock(&kslock); - - for(i=0; i<1024; i++) { - pgd->entries[i] = boot_pgd.entries[i]; - // only kernel entries will be copied - if (pgd->entries[i] && !(pgd->entries[i] & PG_USER)) - pgt->entries[i] = pgt_container->entries[i]; - } - - spinlock_unlock(&kslock); - - // map page table container at the end of the kernel space - viraddr = (KERNEL_SPACE - PAGE_SIZE) & PAGE_MASK; - index1 = viraddr >> 22; - index2 = (viraddr >> 12) & 0x3FF; - - // now, we create a self reference - pgd->entries[index1] = ((size_t) virt_to_phys((size_t) pgt) & PAGE_MASK)|KERN_TABLE; - pgt->entries[index2] = ((size_t) virt_to_phys((size_t) pgt) & PAGE_MASK)|KERN_PAGE; - - task->pgd = pgd; - - if (copy) { - spinlock_lock(&curr_task->pgd_lock); - - for (i=KERNEL_SPACE/(1024*PAGE_SIZE); i<1024; i++) { - if (!(curr_task->pgd->entries[i])) - continue; - if (!(curr_task->pgd->entries[i] & PG_USER)) - continue; - - phyaddr = copy_page_table(task, i, (page_table_t*) ((KERNEL_SPACE - 1024*PAGE_SIZE + i*PAGE_SIZE) & PAGE_MASK), &counter); - if (phyaddr) { - pgd->entries[i] = (phyaddr & PAGE_MASK) | (curr_task->pgd->entries[i] & 0xFFF); - pgt->entries[i] = (phyaddr & PAGE_MASK) | KERN_PAGE; - } - } - - spinlock_unlock(&curr_task->pgd_lock); - } - - return counter; -#endif + task->pgd = get_boot_pgd(); return 0; } @@ -238,9 +114,11 @@ int drop_pgd(void) size_t virt_to_phys(size_t viraddr) { -#if 0 task_t* task = per_core(current_task); - uint32_t index1, index2; + uint16_t idx_pd4 = (viraddr >> 39) & 0x1FF; + uint16_t idx_dirp = (viraddr >> 30) & 0x1FF; + uint16_t idx_dir = (viraddr >> 21) & 0x1FF; + uint16_t idx_table = (viraddr >> 12) & 0x1FF; page_table_t* pgt; size_t ret = 0; @@ -252,17 +130,24 @@ size_t virt_to_phys(size_t viraddr) spinlock_lock(&task->pgd_lock); - index1 = viraddr >> 22; - index2 = (viraddr >> 12) & 0x3FF; - - if (!(task->pgd->entries[index1] & PAGE_MASK)) + // Currently, we allocate pages only in kernel space. + // => physical address of the page table is identical of the virtual address + pgt = (page_table_t*) (task->pgd->entries[idx_pd4] & PAGE_MASK); + if (!pgt) goto out; - pgt = (page_table_t*) ((KERNEL_SPACE - 1024*PAGE_SIZE + index1*PAGE_SIZE) & PAGE_MASK); - if (!pgt || !(pgt->entries[index2])) + pgt = (page_table_t*) (pgt->entries[idx_dirp] & PAGE_MASK); + if (!pgt) + goto out; + + pgt = (page_table_t*) (pgt->entries[idx_dir] & PAGE_MASK); + if (!pgt) + goto out; + + ret = (size_t) (pgt->entries[idx_table] & PAGE_MASK); + if (!ret) goto out; - ret = pgt->entries[index2] & PAGE_MASK; // determine page frame ret = ret | (viraddr & 0xFFF); // add page offset out: //kprintf("vir %p to phy %p\n", viraddr, ret); @@ -270,18 +155,14 @@ out: spinlock_unlock(&task->pgd_lock); return ret; -#endif - return 0; } size_t map_region(size_t viraddr, size_t phyaddr, uint32_t npages, uint32_t flags) { -#if 0 task_t* task = per_core(current_task); spinlock_t* pgd_lock; page_table_t* pgt; - size_t index, i; - size_t ret; + size_t i, ret; if (BUILTIN_EXPECT(!task || !task->pgd, 0)) return 0; @@ -300,95 +181,64 @@ size_t map_region(size_t viraddr, size_t phyaddr, uint32_t npages, uint32_t flag viraddr = vm_alloc(npages, flags); if (BUILTIN_EXPECT(!viraddr, 0)) { spinlock_unlock(pgd_lock); - kputs("map_adress: found no valid virtual address\n"); + kputs("map_region: found no valid virtual address\n"); return 0; } } ret = viraddr; - //kprintf("map %d pages from %p to %p\n", npages, phyaddr, ret); for(i=0; i> 22; + uint16_t idx_pd4 = (viraddr >> 39) & 0x1FF; + uint16_t idx_dirp = (viraddr >> 30) & 0x1FF; + uint16_t idx_dir = (viraddr >> 21) & 0x1FF; + uint16_t idx_table = (viraddr >> 12) & 0x1FF; - if (!(task->pgd->entries[index])) { - page_table_t* pgt_container; + pgt = (page_table_t*) (task->pgd->entries[idx_pd4] & PAGE_MASK); + if (!pgt) { + spinlock_unlock(pgd_lock); + kputs("map_region: out of memory\n"); + return 0; + } + + pgt = (page_table_t*) (pgt->entries[idx_dirp] & PAGE_MASK); + if (!pgt) { + spinlock_unlock(pgd_lock); + kputs("map_region: out of memory\n"); + return 0; + } - pgt = (page_table_t*) get_pages(1); - if (BUILTIN_EXPECT(!pgt, 0)) { - spinlock_unlock(pgd_lock); - kputs("map_address: out of memory\n"); - return 0; - } - - // set the new page table into the directory - if (flags & MAP_USER_SPACE) - task->pgd->entries[index] = (size_t)pgt|USER_TABLE; - else - task->pgd->entries[index] = (size_t)pgt|KERN_TABLE; - - // if paging is already enabled, we need to use the virtual address - if (paging_enabled) - // we already know the virtual address of the "page table container" - // (see file header) - pgt_container = (page_table_t*) ((KERNEL_SPACE - PAGE_SIZE) & PAGE_MASK); - else - pgt_container = (page_table_t*) (task->pgd->entries[(KERNEL_SPACE - PAGE_SIZE) >> 22] & PAGE_MASK); - - if (BUILTIN_EXPECT(!pgt_container, 0)) { - spinlock_unlock(pgd_lock); - kputs("map_address: internal error\n"); - return 0; - } - - // map the new table into the address space of the kernel space - pgt_container->entries[index] = ((size_t) pgt)|KERN_PAGE; - - // clear the page table - if (paging_enabled) - memset((void*) ((KERNEL_SPACE - 1024*PAGE_SIZE + index*PAGE_SIZE) & PAGE_MASK), 0x00, PAGE_SIZE); - else - memset(pgt, 0x00, PAGE_SIZE); - } else pgt = (page_table_t*) (task->pgd->entries[index] & PAGE_MASK); + pgt = (page_table_t*) (pgt->entries[idx_dir] & PAGE_MASK); + if (!pgt) { + spinlock_unlock(pgd_lock); + kputs("map_region: out of memory\n"); + return 0; + } /* convert physical address to virtual */ - if (paging_enabled) - pgt = (page_table_t*) ((KERNEL_SPACE - 1024*PAGE_SIZE + index*PAGE_SIZE) & PAGE_MASK); + // Currently, we allocate pages only in kernel space. + // => physical address of the page table is identical of the virtual address + //if (paging_enabled) + // pgt = (page_table_t*) ((KERNEL_SPACE - 1024*PAGE_SIZE + index*PAGE_SIZE) & PAGE_MASK); - index = (viraddr >> 12) & 0x3FF; - if (pgt->entries[index] && !(flags & MAP_REMAP)) { + if (pgt->entries[idx_table] && !(flags & MAP_REMAP)) { spinlock_unlock(pgd_lock); kprintf("0x%x is already mapped\n", viraddr); return 0; } if (flags & MAP_USER_SPACE) - pgt->entries[index] = USER_PAGE|(phyaddr & PAGE_MASK); + pgt->entries[idx_table] = USER_PAGE|(phyaddr & PAGE_MASK); else - pgt->entries[index] = KERN_PAGE|(phyaddr & PAGE_MASK); + pgt->entries[idx_table] = KERN_PAGE|(phyaddr & PAGE_MASK); if (flags & MAP_NO_CACHE) - pgt->entries[index] |= PG_PCD; -#ifdef CONFIG_ROCKCREEK - if (flags & MAP_MPE) - pgt->entries[index] |= PG_MPE; -#endif - if (flags & MAP_SVM_STRONG) -#ifndef SVM_WB - pgt->entries[index] |= PG_SVM_STRONG|PG_PWT; -#else - pgt->entries[index] |= PG_SVM; -#endif - if (flags & MAP_SVM_LAZYRELEASE) - pgt->entries[index] |= PG_SVM_LAZYRELEASE|PG_PWT; - - if (flags & MAP_SVM_INIT) - pgt->entries[index] |= PG_SVM_INIT; + pgt->entries[idx_table] |= PG_PCD; if (flags & MAP_NO_ACCESS) - pgt->entries[index] &= ~PG_PRESENT; + pgt->entries[idx_table] &= ~PG_PRESENT; if (flags & MAP_WT) - pgt->entries[index] |= PG_PWT; + pgt->entries[idx_table] |= PG_PWT; if (flags & MAP_USER_SPACE) atomic_int32_inc(&task->user_usage); @@ -399,9 +249,6 @@ size_t map_region(size_t viraddr, size_t phyaddr, uint32_t npages, uint32_t flag spinlock_unlock(pgd_lock); return ret; -#endif - - return 0; } int change_page_permissions(size_t start, size_t end, uint32_t flags) @@ -469,7 +316,7 @@ int change_page_permissions(size_t start, size_t end, uint32_t flags) spinlock_unlock(&task->pgd_lock); #endif - return 0; + return -EINVAL; } /* @@ -479,11 +326,9 @@ int change_page_permissions(size_t start, size_t end, uint32_t flags) */ size_t vm_alloc(uint32_t npages, uint32_t flags) { -#if 0 task_t* task = per_core(current_task); spinlock_t* pgd_lock; - uint32_t index1, index2, j; - size_t viraddr, i, ret = 0; + size_t viraddr, i, j, ret = 0; size_t start, end; page_table_t* pgt; @@ -508,11 +353,35 @@ size_t vm_alloc(uint32_t npages, uint32_t flags) viraddr = i = start; j = 0; do { - index1 = i >> 22; - index2 = (i >> 12) & 0x3FF; + uint16_t idx_pd4 = (viraddr >> 39) & 0x1FF; + uint16_t idx_dirp = (viraddr >> 30) & 0x1FF; + uint16_t idx_dir = (viraddr >> 21) & 0x1FF; + uint16_t idx_table = (viraddr >> 12) & 0x1FF; - pgt = (page_table_t*) ((KERNEL_SPACE - 1024*PAGE_SIZE + index1*PAGE_SIZE) & PAGE_MASK); - if (!pgt || !(pgt->entries[index2])) { + // Currently, we allocate pages only in kernel space. + // => physical address of the page table is identical of the virtual address + pgt = (page_table_t*) (task->pgd->entries[idx_pd4] & PAGE_MASK); + if (!pgt) { + i += (size_t)PGT_ENTRIES*PGT_ENTRIES*PGT_ENTRIES*PAGE_SIZE; + j += PGT_ENTRIES*PGT_ENTRIES*PGT_ENTRIES; + continue; + } + + pgt = (page_table_t*) (pgt->entries[idx_dirp] & PAGE_MASK); + if (!pgt) { + i += PGT_ENTRIES*PGT_ENTRIES*PAGE_SIZE; + j += PGT_ENTRIES*PGT_ENTRIES; + continue; + } + + pgt = (page_table_t*) (pgt->entries[idx_dir] & PAGE_MASK); + if (!pgt) { + i += PGT_ENTRIES*PAGE_SIZE; + j += PGT_ENTRIES; + continue; + } + + if (!(pgt->entries[idx_table])) { i+=PAGE_SIZE; j++; } else { @@ -529,21 +398,19 @@ size_t vm_alloc(uint32_t npages, uint32_t flags) spinlock_unlock(pgd_lock); return ret; -#endif - return 0; } int unmap_region(size_t viraddr, uint32_t npages) { -#if 0 task_t* task = per_core(current_task); spinlock_t* pgd_lock; - uint32_t i; - uint32_t index1, index2; page_table_t* pgt; + size_t i; + uint16_t idx_pd4, idx_dirp; + uint16_t idx_dir, idx_table; if (BUILTIN_EXPECT(!task || !task->pgd || !paging_enabled, 0)) - return -EINVAL; + return -EINVAL; if (viraddr <= KERNEL_SPACE) pgd_lock = &kslock; @@ -552,16 +419,43 @@ int unmap_region(size_t viraddr, uint32_t npages) spinlock_lock(pgd_lock); - for(i=0; i> 22; - index2 = (viraddr >> 12) & 0x3FF; + idx_pd4 = (viraddr >> 39) & 0x1FF; + idx_dirp = (viraddr >> 30) & 0x1FF; + idx_dir = (viraddr >> 21) & 0x1FF; + idx_table = (viraddr >> 12) & 0x1FF; - pgt = (page_table_t*) ((KERNEL_SPACE - 1024*PAGE_SIZE + index1*PAGE_SIZE) & PAGE_MASK); - if (!pgt) + // Currently, we allocate pages only in kernel space. + // => physical address of the page table is identical of the virtual address + pgt = (page_table_t*) (task->pgd->entries[idx_pd4] & PAGE_MASK); + if (!pgt) { + viraddr += (size_t) PGT_ENTRIES*PGT_ENTRIES*PGT_ENTRIES*PAGE_SIZE; + i += PGT_ENTRIES*PGT_ENTRIES*PGT_ENTRIES; continue; - pgt->entries[index2] &= ~PG_PRESENT; + } + pgt = (page_table_t*) (pgt->entries[idx_dirp] & PAGE_MASK); + if (!pgt) { + viraddr += PGT_ENTRIES*PGT_ENTRIES*PAGE_SIZE; + i += PGT_ENTRIES*PGT_ENTRIES; + continue; + } + + pgt = (page_table_t*) (pgt->entries[idx_dir] & PAGE_MASK); + if (!pgt) { + viraddr += PGT_ENTRIES*PAGE_SIZE; + i += PGT_ENTRIES; + continue; + } + + if (pgt->entries[idx_table]) + pgt->entries[idx_table] &= ~PG_PRESENT; + + viraddr +=PAGE_SIZE; + i++; + if (viraddr > KERNEL_SPACE) atomic_int32_dec(&task->user_usage); @@ -569,19 +463,18 @@ int unmap_region(size_t viraddr, uint32_t npages) } spinlock_unlock(pgd_lock); -#endif return 0; } int vm_free(size_t viraddr, uint32_t npages) { -#if 0 task_t* task = per_core(current_task); spinlock_t* pgd_lock; - uint32_t i; - uint32_t index1, index2; page_table_t* pgt; + size_t i; + uint16_t idx_pd4, idx_dirp; + uint16_t idx_dir, idx_table; if (BUILTIN_EXPECT(!task || !task->pgd || !paging_enabled, 0)) return -EINVAL; @@ -593,61 +486,47 @@ int vm_free(size_t viraddr, uint32_t npages) spinlock_lock(pgd_lock); - for(i=0; i> 22; - index2 = (viraddr >> 12) & 0x3FF; + idx_pd4 = (viraddr >> 39) & 0x1FF; + idx_dirp = (viraddr >> 30) & 0x1FF; + idx_dir = (viraddr >> 21) & 0x1FF; + idx_table = (viraddr >> 12) & 0x1FF; - pgt = (page_table_t*) ((KERNEL_SPACE - 1024*PAGE_SIZE + index1*PAGE_SIZE) & PAGE_MASK); - if (!pgt) + // Currently, we allocate pages only in kernel space. + // => physical address of the page table is identical of the virtual address + pgt = (page_table_t*) (task->pgd->entries[idx_pd4] & PAGE_MASK); + if (!pgt) { + viraddr += (size_t) PGT_ENTRIES*PGT_ENTRIES*PGT_ENTRIES*PAGE_SIZE; + i += PGT_ENTRIES*PGT_ENTRIES*PGT_ENTRIES; continue; - pgt->entries[index2] = 0; + } + + pgt = (page_table_t*) (pgt->entries[idx_dirp] & PAGE_MASK); + if (!pgt) { + viraddr += PGT_ENTRIES*PGT_ENTRIES*PAGE_SIZE; + i += PGT_ENTRIES*PGT_ENTRIES; + continue; + } + + pgt = (page_table_t*) (pgt->entries[idx_dir] & PAGE_MASK); + if (!pgt) { + viraddr += PGT_ENTRIES*PAGE_SIZE; + i += PGT_ENTRIES; + continue; + } + + if (pgt->entries[idx_table]) + pgt->entries[idx_table] = 0; + + viraddr +=PAGE_SIZE; + i++; tlb_flush_one_page(viraddr); } spinlock_unlock(pgd_lock); -#endif - - return 0; -} - -#if 0 -int print_paging_tree(size_t viraddr) -{ - task_t* task = per_core(current_task); - uint32_t index1, index2; - page_dir_t* pgd = NULL; - page_table_t* pgt = NULL; - - if (BUILTIN_EXPECT(!viraddr, 0)) - return -EINVAL; - - index1 = viraddr >> 22; - index2 = (viraddr >> 12) & 0x3FF; - - spinlock_lock(&task->pgd_lock); - - kprintf("Paging dump of address 0x%x\n", viraddr); - pgd = task->pgd; - kprintf("\tPage directory entry %u: ", index1); - if (pgd) { - kprintf("0x%0x\n", pgd->entries[index1]); - pgt = (page_table_t*) (pgd->entries[index1] & PAGE_MASK); - } else - kputs("invalid page directory\n"); - - /* convert physical address to virtual */ - if (paging_enabled && pgt) - pgt = (page_table_t*) (KERNEL_SPACE - 1024*PAGE_SIZE + index1*PAGE_SIZE); - - kprintf("\tPage table entry %u: ", index2); - if (pgt) - kprintf("0x%x\n", pgt->entries[index2]); - else - kputs("invalid page table\n"); - - spinlock_unlock(&task->pgd_lock); return 0; } @@ -655,14 +534,12 @@ int print_paging_tree(size_t viraddr) static void pagefault_handler(struct state *s) { task_t* task = per_core(current_task); - page_dir_t* pgd = task->pgd; - page_table_t* pgt = NULL; + //page_dir_t* pgd = task->pgd; + //page_table_t* pgt = NULL; size_t viraddr = read_cr2(); - size_t phyaddr; -#ifdef CONFIG_ROCKCREEK - uint32_t index1, index2; -#endif + //size_t phyaddr; +#if 0 if ((viraddr >= task->start_heap) && (viraddr <= task->end_heap) && (viraddr > KERNEL_SPACE)) { viraddr = viraddr & PAGE_MASK; @@ -678,87 +555,28 @@ static void pagefault_handler(struct state *s) kprintf("Could not map 0x%x at 0x%x\n", phyaddr, viraddr); put_page(phyaddr); } - -#ifdef CONFIG_ROCKCREEK - // does our SVM system need to handle this page fault? - index1 = viraddr >> 22; - index2 = (viraddr >> 12) & 0x3FF; - if (!pgd || !(pgd->entries[index1] & PAGE_MASK)) - goto default_handler; - pgt = (page_table_t*) ((KERNEL_SPACE - 1024*PAGE_SIZE + index1*PAGE_SIZE) & PAGE_MASK); - if (!pgt || !(pgt->entries[index2])) - goto default_handler; - if (pgt->entries[index2] & PG_SVM_INIT) { - if (BUILTIN_EXPECT(!svm_alloc_page(viraddr, pgt), 1)) - return; - else - goto default_handler; - } - if (pgt->entries[index2] & PG_SVM_STRONG) - if (BUILTIN_EXPECT(!svm_access_request(viraddr), 1)) - return; #endif default_handler: - kprintf("PAGE FAULT: Task %u got page fault at %p (irq %d, cs:eip 0x%x:0x%x)\n", task->id, viraddr, s->int_no, s->cs, s->eip); - kprintf("Register state: eax = 0x%x, ebx = 0x%x, ecx = 0x%x, edx = 0x%x, edi = 0x%x, esi = 0x%x, ebp = 0x%x, esp = 0x%x\n", - s->eax, s->ebx, s->ecx, s->edx, s->edi, s->esi, s->ebp, s->esp); + kprintf("PAGE FAULT: Task %u got page fault at %p (irq %llu, cs:rip 0x%llx:0x%llx)\n", task->id, viraddr, s->int_no, s->cs, s->rip); + kprintf("Register state: rax = 0x%llx, rbx = 0x%llx, rcx = 0x%llx, rdx = 0x%llx, rdi = 0x%llx, rsi = 0x%llx, rbp = 0x%llx, rsp = 0x%llx\n", + s->rax, s->rbx, s->rcx, s->rdx, s->rdi, s->rsi, s->rbp, s->rsp); +while(1); irq_enable(); abort(); } -#endif int arch_paging_init(void) { -#if 0 - uint32_t i, npages, index1, index2; - page_table_t* pgt; - size_t viraddr; + uint32_t i, npages; // uninstall default handler and install our own irq_uninstall_handler(14); irq_install_handler(14, pagefault_handler); - // Create a page table to reference to the other page tables - pgt = (page_table_t*) get_page(); - if (!pgt) { - kputs("arch_paging_init: Not enough memory!\n"); - return -ENOMEM; - } - memset(pgt, 0, PAGE_SIZE); - - // map this table at the end of the kernel space - viraddr = KERNEL_SPACE - PAGE_SIZE; - index1 = viraddr >> 22; - index2 = (viraddr >> 12) & 0x3FF; - - // now, we create a self reference - per_core(current_task)->pgd->entries[index1] = (((size_t) pgt) & PAGE_MASK)|KERN_TABLE; - pgt->entries[index2] = ((size_t) pgt & PAGE_MASK)|KERN_PAGE; - - // create the other PGTs for the kernel space - for(i=0; ipgd->entries[i] = (phyaddr & PAGE_MASK)|KERN_TABLE; - pgt->entries[i] = (phyaddr & PAGE_MASK)|KERN_PAGE; - } - - /* - * Set the page table and page directory entries for the kernel. We map the kernel's physical address - * to the same virtual address. - */ - npages = ((size_t) &kernel_end - (size_t) &kernel_start) >> PAGE_SHIFT; - if ((size_t)&kernel_end & (PAGE_SIZE-1)) - npages++; - map_region((size_t)&kernel_start, (size_t)&kernel_start, npages, MAP_KERNEL_SPACE); + // kernel is already maped into the kernel space (see entry64.asm) + // this includes .data, .bss, .text, video memory and the multiboot structure #if MAX_CORES > 1 // Reserve page for smp boot code @@ -768,18 +586,7 @@ int arch_paging_init(void) } #endif -#ifdef CONFIG_VGA - // map the video memory into the kernel space - map_region(VIDEO_MEM_ADDR, VIDEO_MEM_ADDR, 1, MAP_KERNEL_SPACE|MAP_NO_CACHE); -#endif - #ifdef CONFIG_MULTIBOOT - /* - * of course, mb_info has to map into the kernel space - */ - if (mb_info) - map_region((size_t) mb_info & PAGE_MASK, (size_t) mb_info & PAGE_MASK, 1, MAP_KERNEL_SPACE); - #if 0 /* * Map reserved memory regions into the kernel space @@ -817,46 +624,15 @@ int arch_paging_init(void) npages = (mmodule->mod_end - mmodule->mod_start) >> PAGE_SHIFT; if (mmodule->mod_end & (PAGE_SIZE-1)) npages++; + //kprintf("Map module %s at 0x%x (%u pages)\n", (char*) mmodule->cmdline, mmodule->mod_start, npages); map_region((size_t) (mmodule->mod_start), (size_t) (mmodule->mod_start), npages, MAP_KERNEL_SPACE); } } #endif -#ifdef CONFIG_ROCKCREEK - // map SCC's bootinfo - viraddr = map_region(SCC_BOOTINFO, SCC_BOOTINFO, 1, MAP_KERNEL_SPACE); - kprintf("Map SCC's bootinfos at 0x%x\n", viraddr); - - // map SCC's configuration registers - viraddr = map_region(CRB_X0_Y0, CRB_X0_Y0, (CRB_OWN-CRB_X0_Y0+16*1024*1024) >> PAGE_SHIFT, MAP_KERNEL_SPACE|MAP_NO_CACHE); - kprintf("Map configuration registers at 0x%x\n", viraddr); - - // map SCC's message passing buffers - viraddr = map_region(MPB_X0_Y0, MPB_X0_Y0, (MPB_OWN-MPB_X0_Y0+16*1024*1024) >> PAGE_SHIFT, MAP_KERNEL_SPACE|MAP_MPE); - kprintf("Map message passing buffers at 0x%x\n", viraddr); - - // map the FPGA registers - viraddr = map_region(FPGA_BASE, FPGA_BASE, 0x10000 >> PAGE_SHIFT, MAP_KERNEL_SPACE|MAP_NO_CACHE); - kprintf("Map FPGA regsiters at 0x%x\n", viraddr); -#endif - - /* enable paging */ - write_cr3((size_t) &boot_pgd); - i = read_cr0(); - i = i | (1 << 31); - write_cr0(i); + /* signalize that we are able to use paging */ paging_enabled = 1; -#ifdef CONFIG_ROCKCREEK - // map the initial ramdisk - npages = bootinfo->size >> PAGE_SHIFT; - if (bootinfo->size & (PAGE_SIZE-1)) - npages++; - viraddr = map_region(0, bootinfo->addr, npages, MAP_KERNEL_SPACE); - kprintf("Map initrd from 0x%x to 0x%x (size %u bytes)\n", bootinfo->addr, viraddr, bootinfo->size); - bootinfo->addr = viraddr; -#endif - /* * we turned on paging * => now, we are able to register our task for Task State Switching @@ -865,7 +641,6 @@ int arch_paging_init(void) // APIC registers into the kernel address space map_apic(); -#endif return 0; } diff --git a/fs/initrd.c b/fs/initrd.c index 3ecfa20c..fd889269 100644 --- a/fs/initrd.c +++ b/fs/initrd.c @@ -456,8 +456,8 @@ int initrd_init(void) initrd_file_desc_t* file_desc; vfs_node_t* new_node; - if (header->magic != INITRD_MAGIC_NUMBER) { - kprintf("Invalid magic number for a init ram disk\n"); + if (BUILTIN_EXPECT(header->magic != INITRD_MAGIC_NUMBER, 0)) { + kprintf("Invalid magic number for a init ram disk: 0x%x\n", header->magic); continue; } diff --git a/kernel/main.c b/kernel/main.c index f1017ef1..62753e41 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -62,17 +62,12 @@ int main(void) { tid_t id; - -vga_init(); -vga_puts("aaa"); - //lowlevel_init(); + lowlevel_init(); pushbg(COL_BLUE); kprintf("This is MetalSVM %s Build %u, %u\n", METALSVM_VERSION, &__BUILD_DATE, &__BUILD_TIME); popbg(); - -while(1); system_init(); irq_init(); timer_init(); diff --git a/kernel/tasks.c b/kernel/tasks.c index d283e55d..866a4a43 100644 --- a/kernel/tasks.c +++ b/kernel/tasks.c @@ -719,7 +719,6 @@ invalid: return -EINVAL; #else -#warning Currently, not supported! return -EINVAL; #endif } @@ -786,7 +785,6 @@ int create_user_task(tid_t* id, const char* fname, char** argv) while ((*dest++ = *src++) != 0); } - /* create new task */ return create_task(id, user_entry, load_args, NORMAL_PRIO); } diff --git a/link32.ld b/link32.ld index ad03b8f5..af270aa6 100644 --- a/link32.ld +++ b/link32.ld @@ -20,8 +20,8 @@ SECTIONS .data ALIGN(4096) : AT(ADDR(.data)) { *(.data) } - bss_start = .; .bss ALIGN(4096) : AT(ADDR(.bss)) { + bss_start = .; *(.bss) } bss_end = .; diff --git a/link64.ld b/link64.ld index 2794f82c..b2cb05d8 100644 --- a/link64.ld +++ b/link64.ld @@ -20,8 +20,8 @@ SECTIONS .data ALIGN(4096) : AT(ADDR(.data)) { *(.data) } - bss_start = .; .bss ALIGN(4096) : AT(ADDR(.bss)) { + bss_start = .; *(.bss) } bss_end = .; diff --git a/mm/memory.c b/mm/memory.c index eed59d5d..1c7c89a2 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -96,7 +96,7 @@ int mmu_init(void) size_t kernel_size; unsigned int i; size_t addr; - int ret; + int ret = 0; // at first, set default value of the bitmap memset(bitmap, 0xFF, sizeof(uint8_t)*BITMAP_SIZE); @@ -128,35 +128,6 @@ int mmu_init(void) HALT; } } - - /* - * Modules like the init ram disk are already loaded. - * Therefore, we set these pages as used. - */ - if (mb_info && (mb_info->flags & MULTIBOOT_INFO_MODS)) { - multiboot_module_t* mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr); - - /* - * Mark the mb_info as used. - */ - page_set_mark((size_t)mb_info >> PAGE_SHIFT); - atomic_int32_inc(&total_allocated_pages); - atomic_int32_dec(&total_available_pages); - - for(addr = mb_info->mods_addr; addr < mb_info->mods_addr + mb_info->mods_count * sizeof(multiboot_module_t); addr += PAGE_SIZE) { - page_set_mark(addr >> PAGE_SHIFT); - atomic_int32_inc(&total_allocated_pages); - atomic_int32_dec(&total_available_pages); - } - - for(i=0; imods_count; i++, mmodule++) { - for(addr=mmodule->mod_start; addrmod_end; addr+=PAGE_SIZE) { - page_set_mark(addr >> PAGE_SHIFT); - atomic_int32_inc(&total_allocated_pages); - atomic_int32_dec(&total_available_pages); - } - } - } #elif defined(CONFIG_ROCKCREEK) /* of course, the first slots belong to the private memory */ for(addr=0x00; addr<1*0x1000000; addr+=PAGE_SIZE) { @@ -210,8 +181,39 @@ int mmu_init(void) atomic_int32_sub(&total_available_pages, 1); #endif ret = paging_init(); + if (ret) + return ret; -#ifdef CONFIG_ROCKCREEK +#ifdef CONFIG_MULTIBOOT + /* + * Modules like the init ram disk are already loaded. + * Therefore, we set these pages as used. + */ + if (mb_info && (mb_info->flags & MULTIBOOT_INFO_MODS)) { + multiboot_module_t* mmodule = (multiboot_module_t*) ((size_t) mb_info->mods_addr); + + /* + * Mark the mb_info as used. + */ + page_set_mark((size_t)mb_info >> PAGE_SHIFT); + atomic_int32_inc(&total_allocated_pages); + atomic_int32_dec(&total_available_pages); + + for(addr = mb_info->mods_addr; addr < mb_info->mods_addr + mb_info->mods_count * sizeof(multiboot_module_t); addr += PAGE_SIZE) { + page_set_mark(addr >> PAGE_SHIFT); + atomic_int32_inc(&total_allocated_pages); + atomic_int32_dec(&total_available_pages); + } + + for(i=0; imods_count; i++, mmodule++) { + for(addr=mmodule->mod_start; addrmod_end; addr+=PAGE_SIZE) { + page_set_mark(addr >> PAGE_SHIFT); + atomic_int32_inc(&total_allocated_pages); + atomic_int32_dec(&total_available_pages); + } + } + } +#elif defined(CONFIG_ROCKCREEK) /* * Now, we are able to read the FPGA registers and to * determine the number of slots for private memory.