diff --git a/kernel/Makefile b/kernel/Makefile index 2923250..87b8ae2 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -44,7 +44,7 @@ all: $(OBJS) depend: makedepend -- $(CPPFLAGS) -- $(CSRC) $(CXXSRC) -boot.o: boot.asm +boot.o: boot.asm idt.inc gdt.inc $(NASM) $(NASM_FLAGS) -l boot.lst boot.asm -o boot.o lang/lang_a.o: lang/lang.asm diff --git a/kernel/boot.asm b/kernel/boot.asm index a9e1a42..66f0294 100644 --- a/kernel/boot.asm +++ b/kernel/boot.asm @@ -9,7 +9,7 @@ %define VIRT_OFFSET 0xC0000000 ;3gb virtual address %define PHYS_START 0x00100000 ;1mb physical address -%define VIRT_STACK_TOP 0xCFFFFFFC ;3gb+256mb virtual address +%define VIRT_STACK_TOP 0xD0000000 ;3gb+256mb virtual address %define GDT_P PHYS_START ;1mb physical - Global Descriptor Table space %define GDT_V GDT_P+VIRT_OFFSET %define IDT_P PHYS_START+0x2000 ;1mb+8kb - Interrupt Descriptor Table space @@ -25,8 +25,9 @@ %define KERNEL_P PHYS_START+0x8000 ;1mb+32kb - the kernel's physical address %define KERNEL_V KERNEL_P+VIRT_OFFSET ;3gb+1mb+32kb, the virtual address of the kernel -extern _k_init, _isr, _k_mbsave, _end, _rm_params, _initrd - +extern _k_init, _isr, _k_mbsave, _end, _rm_params, _initrd, _tss0, _kprintf +extern _proc_new_esp, _proc_new_ss + [bits 32] [global _start] @@ -126,7 +127,6 @@ fill_lopt_loop: ;fill the page table or eax, 0x80000000 ;set Page Enable bit mov cr0, eax ;now paging is active! - mov edi, GDT_V mov esi, gdt mov ecx, gdt_end-gdt @@ -166,13 +166,33 @@ newgdtcontinue: lidt [idtr] ;load idt mov [PDBR_V], dword 0 ;unmap 0x0, we are running completely paged at 0xC000_0000 + mov edi, GDT_V + TSS0_SEG + 2 ; copy _tss0 base address + mov eax, _tss0 ; into the GDT descrtiptor + stosw ; for TSS0 + shr eax, 16 + stosb + add edi, 2 + shr eax, 8 + stosb + call _k_init ;C kernel initialization + mov ax, TSS0_SEG + ltr ax + + push esp + push msg1 + call _kprintf + add esp, 8 + idle_loop: ; system idle loop sti hlt jmp idle_loop +msg1: + db "esp before idle_loop: 0x%x", 10, 0 + ;------------------------------------------------------- gdtrbs32: dw gdt_endbs32-gdtbs32-1 diff --git a/kernel/display/kout.c b/kernel/display/kout.c index 5858233..07ee911 100644 --- a/kernel/display/kout.c +++ b/kernel/display/kout.c @@ -8,6 +8,7 @@ #include "lang/conv.h" #include "devices.h" #include "char/misc_char.h" +#include "functions.h" char buffer[64]; // for hex/oct/dec/ascii conversion @@ -15,7 +16,10 @@ char buffer[64]; // for hex/oct/dec/ascii conversion void putc(int c) { #ifdef PARALLEL_DEBUG - char_write(MAJOR_MISC_CHAR, MISC_CHAR_LP0, c); + outportb(0x37a, 0xc); + outportb(0x378, c); + outportb(0x37a, 0x1); + // char_write(MAJOR_MISC_CHAR, MISC_CHAR_LP0, c); #endif char_write(MAJOR_VCONSOLE, KERNEL_MSG_CONSOLE, c); // write to vconsole with minor 1, first allocated } diff --git a/kernel/gdt.inc b/kernel/gdt.inc index 12a137a..43524c3 100644 --- a/kernel/gdt.inc +++ b/kernel/gdt.inc @@ -43,6 +43,13 @@ USER_DATA equ $-gdt db 0xCF ;flags ([G][D/B][0][0]) / limit 19:16 db 0x00 ;base 31:24 +TSS0_SEG equ $-gdt + dw 0x67 ;limit 15:0 + dw 0 ;base 15:0 + db 0 ;base 23:16 + db 0xE9 ;access ([P][DPL][0][1][0][Busy][1]) + db 0x00 ;flags ([G][0][0][AVL]) / limit 19:16 + db 0 ;base 31:24 gdt_end: diff --git a/kernel/hos_defines.h b/kernel/hos_defines.h index 986d6cf..5acecf7 100644 --- a/kernel/hos_defines.h +++ b/kernel/hos_defines.h @@ -13,6 +13,8 @@ #define VIRT_OFFSET 0xC0000000 #define PHYS_LOAD 0x00108000 +#define PDBR 0x00104000 +#define VIRT_STACK_TOP 0xD0000000 #define HEAP_START 0xD0000000 #define HEAP_LENGTH 0x20000000 @@ -20,6 +22,12 @@ #define BIOS_CHAR_MAP 0xC00FFA6E #define LFB_MEMORY 0xF0000000 +#define SEG_KERNEL_CODE 8 +#define SEG_KERNEL_DATA 16 +#define SEG_USER_CODE 24 +#define SEG_USER_DATA 32 +#define SEG_TSS0 40 + #define MAX_MODULES 16 #define MAX_MMAP 16 diff --git a/kernel/idt.inc b/kernel/idt.inc index d4612dc..098ce2f 100644 --- a/kernel/idt.inc +++ b/kernel/idt.inc @@ -12,6 +12,14 @@ idtr: isr_%1: push eax mov eax, %1 + + push eax + push esp + push msg + call _kprintf + add esp, 12 +; hlt + jmp isr_main %endmacro @@ -67,30 +75,40 @@ isr_label 48 isr_label 49 isr_main: - pusha + push ebx + push ecx + push edx + push edi + push esi + push ebp push ds push es push fs push gs + push esp - push eax ;interrupt number + push eax ;interrupt number call _isr add esp, 8 + mov eax, [_proc_new_ss] + mov ebx, [_proc_new_esp] + mov ss, ax + mov esp, ebx + pop gs pop fs pop es pop ds - popa + pop ebp + pop esi + pop edi + pop edx + pop ecx + pop ebx pop eax ;original saved eax iret - - - - - - - - +msg: + db "esp is 0x%x after int %d", 10, 0 diff --git a/kernel/kernel.c b/kernel/kernel.c index db70f4d..1d038c7 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -36,6 +36,7 @@ int criticalCounter; // semaphore for if interrupts are disabled u32_t timer; // number of IRQ 0's extern u32_t mm_freepages; +extern u32_t proc_new_esp; /* This function runs in segmented memory - 0xC000_0000 is mapped to 0x0 but 0x0 itself is an invalid linear address. Therefore, the multiboot information addresses @@ -133,12 +134,13 @@ void k_init() k_check(pci_init(), "pci_init() failed!"); k_check(vfs_init(), "vfs_init() failed!"); + k_check(proc_init(), "proc_init() failed!"); int i; for (i = 0; i < mb_info_block.mods_count; i++) { - kprintf("Loaded kernel module %d: 0x%x - 0x%x (%d bytes)\n", i, mb_modules[i].mod_start, mb_modules[i].mod_end, mb_modules[i].mod_end - mb_modules[i].mod_start); - if (((mb_modules[i].mod_end - mb_modules[i].mod_start) > 1024) && + kprintf("Loaded kernel module %d: 0x%x - 0x%x (%d bytes, type %d)\n", i, mb_modules[i].mod_start, mb_modules[i].mod_end, mb_modules[i].mod_end - mb_modules[i].mod_start, ((hos_module_header_t*)mb_modules[i].mod_start)->mod_type); + if (((mb_modules[i].mod_end - mb_modules[i].mod_start) > 2048) && ((ext2_super_block_t *)(mb_modules[i].mod_start + 1024))->s_magic == EXT2_MAGIC) { // we found an initrd @@ -146,9 +148,10 @@ void k_init() kprintf("initrd (%dkb) loaded\n", (mb_modules[i].mod_end - mb_modules[i].mod_start) >> 10); k_check(vfs_mount(DEV(MAJOR_RAMDISK, initrd_minor), "ext2", "/"), "Kernel panic: Could not mount initrd to /!"); } - if (((hos_module_header_t*)(mb_modules[i].mod_start))->mod_type == 2) + else if (((hos_module_header_t*)(mb_modules[i].mod_start))->mod_type == 2) { - create_task(mb_modules[i].mod_start, + kprintf("Creating a task\n"); + create_task((void *)mb_modules[i].mod_start, mb_modules[i].mod_end - mb_modules[i].mod_end, 0, ((hos_module_header_t*)(mb_modules[i].mod_start))->init); @@ -197,14 +200,14 @@ void k_init() criticalCounter--; } -void isr(u32_t num, int_stack_t *stack_frame) +void isr(u32_t num, int_stack_t *int_stack) { criticalCounter++; switch (num) { case 0x20: // timer timer++; - proc_sched(stack_frame); + (*(u16_t *)CONSOLE_MEMORY)++; pic_eoi(); break; case 0x21: // keyboard @@ -212,13 +215,15 @@ void isr(u32_t num, int_stack_t *stack_frame) pic_eoi(); break; case 0x30: - if (stack_frame->eax == 1) - kputc(stack_frame->ebx); + if (int_stack->eax == 1) + putc(int_stack->ebx); break; default: kprintf("Unhandled interrupt #%d, CR2 = 0x%x!\n", num, read_cr2()); halt(); } + if ( (num == 0x20) || (proc_new_esp == 0) ) + proc_sched(int_stack); criticalCounter--; } diff --git a/kernel/kernel.h b/kernel/kernel.h index a8d3406..aade35d 100644 --- a/kernel/kernel.h +++ b/kernel/kernel.h @@ -17,28 +17,24 @@ typedef struct u32_t bpp; // bits per pixel - 15/16/24/32 } __attribute__ ((packed)) real_mode_param_t; - pusha - push ds - push es - push fs - push gs - push esp - typedef struct { u32_t gs; u32_t fs; u32_t es; u32_t ds; - u32_t edi; /* pusha */ - u32_t esi; u32_t ebp; - u32_t esp; - u32_t ebx; + u32_t esi; + u32_t edi; u32_t edx; u32_t ecx; - u32_t int_num; /* The eax that pusha pushed is int # */ - u32_t eax; /* We saved the real eax manually */ + u32_t ebx; + u32_t eax; + u32_t eip; + u32_t cs; + u32_t eflags; + u32_t ss; /* present if ring3->ring0 transition ?? */ + u32_t esp; } int_stack_t; /* returns true to callee if we should jump to a real mode module */ diff --git a/kernel/lang/lang.asm b/kernel/lang/lang.asm index daf50d2..73e8348 100644 --- a/kernel/lang/lang.asm +++ b/kernel/lang/lang.asm @@ -11,6 +11,13 @@ %endmacro +;returns the value in the CR0 register +;extern dword read_cr0(); +[global _read_cr0] +_read_cr0: + mov eax, cr0; + ret + ;stores the parameter to the CR0 register ;extern dword write_cr0(dword cr0); [global _write_cr0] @@ -22,25 +29,6 @@ _write_cr0: pop ebp ret -;returns the value in the CR0 register -;extern dword read_cr0(); -[global _read_cr0] -_read_cr0: - mov eax, cr0; - ret - -;stores the parameter to the CR3 register -;extern dword write_cr3(dword cr3); -[global _write_cr3] -_write_cr3: - push ebp - mov ebp, esp - mov eax, [ebp+8] - mov cr3, eax - pop ebp - ret - - ;returns the value in the CR2 register ;extern dword read_cr2(); [global _read_cr2] @@ -58,6 +46,24 @@ _read_cr3: ret +;stores the parameter to the CR3 register +;extern dword write_cr3(dword cr3); +[global _write_cr3] +_write_cr3: + push ebp + mov ebp, esp + mov eax, [ebp+8] + mov cr3, eax + pop ebp + ret + + +; read ss register +[global _read_ss] +_read_ss: + mov ax, ss + ret + ;copies a string from the source to the destination parameter ;extern void strcpy(char *dest, char *src); diff --git a/kernel/lang/lang.h b/kernel/lang/lang.h index 65d1ed2..500f667 100644 --- a/kernel/lang/lang.h +++ b/kernel/lang/lang.h @@ -9,11 +9,12 @@ #include "hos_defines.h" /* lang.asm */ -u32_t write_cr0(u32_t cr0); u32_t read_cr0(); -u32_t write_cr3(u32_t cr3); +u32_t write_cr0(u32_t cr0); u32_t read_cr2(); u32_t read_cr3(); +u32_t write_cr3(u32_t cr3); +u32_t read_ss(); void writeCursorPosition(u32_t pos); u32_t getCursorPosition(); void strcpy(char *dest, const char *src); diff --git a/kernel/mm/mm.c b/kernel/mm/mm.c index 4762bbe..dae7ecc 100644 --- a/kernel/mm/mm.c +++ b/kernel/mm/mm.c @@ -1,7 +1,7 @@ // mm.c // Author: Josh Holtrop // Created: 09/01/03 -// Modified: 07/12/04 +// Modified: 08/19/04 #include "kernel.h" #include "mm/mm.h" @@ -48,7 +48,7 @@ void mm_init() int i; for (i = 0; i < mb_info_block.mods_count; i++) //reserve module memory { - mm_preserven(mb_modules[i].mod_start - VIRT_OFFSET, (mb_modules[i].mod_end - mb_modules[i].mod_start) >> 12); + mm_preserven(mb_modules[i].mod_start - VIRT_OFFSET, (mb_modules[i].mod_end - mb_modules[i].mod_start + 4095) >> 12); } mm_megabytes = (mm_totalmem >> 20) + ((mm_totalmem % 0x100000) ? 1 : 0); } diff --git a/kernel/mm/vmm.c b/kernel/mm/vmm.c index 521450f..bbfa7ee 100644 --- a/kernel/mm/vmm.c +++ b/kernel/mm/vmm.c @@ -72,6 +72,8 @@ int vmm_map_addr(void *virt, u32_t *phys_addr) // This function maps a virtual address to a physical address using the page directory / page table int vmm_map1(u32_t virt, u32_t physical) { + virt &= 0xFFFFF000; + physical &= 0xFFFFF000; u32_t pde = virt >> 22; u32_t pte = (virt & 0x003FF000) >> 12; u32_t *pageTables = (u32_t *)0xFFFFF000; //this is the location of the page directory @@ -85,7 +87,7 @@ int vmm_map1(u32_t virt, u32_t physical) invlpg_(virt); //in case it was cached, so we can fill page table safely memsetd((void*)(0xFFC00000 | (pde << 12)), 0, 1024); //zero out new page table } - *(u32_t *)(0xFFC00000 | (pde << 12) | (pte << 2)) = (physical & 0xFFFFF000) | 0x03; + *(u32_t *)(0xFFC00000 | (pde << 12) | (pte << 2)) = physical | 0x03; invlpg_((0xFFC00000 | (pde << 12) | (pte << 2))); invlpg_(virt); return 0; diff --git a/kernel/module.h b/kernel/module.h index b906f25..d816c6d 100644 --- a/kernel/module.h +++ b/kernel/module.h @@ -2,8 +2,8 @@ #ifndef __HOS_MODULE_H__ #define __HOS_MODULE_H__ __HOS_MODULE_H__ +#define MOD_INITRD 0 #define MOD_REAL_MODE 1 -#define MOD_INITRD 2 typedef struct { unsigned int mod_magic; // "HOSM" = dword value 0x4D534F48 diff --git a/kernel/proc/proc.c b/kernel/proc/proc.c index 611d8d6..3f6e045 100644 --- a/kernel/proc/proc.c +++ b/kernel/proc/proc.c @@ -11,52 +11,77 @@ extern int mm_freepages; #include "mm/vmm.h" #include "lang/lang.h" #include "kernel.h" +#include "functions.h" //halt() + +extern u32_t idle_loop; u32_t cur_task = 0; u32_t n_processes = 0; process_t *processes[3]; +tss_t tss0; +u32_t proc_new_esp; +u32_t proc_new_ss; int proc_init() { + /* initialize tss0 */ + memset(&tss0, 0, sizeof(tss_t)); + tss0.ss0 = SEG_KERNEL_DATA; + tss0.esp0 = VIRT_STACK_TOP; + /* tss0.pdbr = read_cr3(); + tss0.eip = &idle_loop; + tss0.eflags = 0x0202; + tss0.esp = VIRT_STACK_TOP-32; // idle_loop won't use -- a good thing + tss0.es = tss0.ds = tss0.fs = tss0.gs = tss0.ss = SEG_KERNEL_DATA; + tss0.cs = SEG_KERNEL_CODE; */ + processes[0] = New(process_t); + processes[0]->p_page_dir = read_cr3(); + processes[0]->page_dir = (void *)0xFFFFF000; return 0; } -void proc_sched(int_stack_t *stack_frame) +void proc_sched(int_stack_t *int_stack) { - cur_task = (cur_task + 1) & 1; - write_cr3(processes[cur_task] + processes[cur_task]->esp = (u32_t) int_stack; + processes[cur_task]->ss = read_ss(); + cur_task = (cur_task + 1) % 3; + proc_new_esp = processes[cur_task]->esp; + proc_new_ss = processes[cur_task]->ss; + write_cr3(processes[cur_task]->p_page_dir); } -u32_t create_task(void *base, u32_t image_size, u32_t bss_size, void *entry); +u32_t create_task(void *base, u32_t image_size, u32_t bss_size, void *entry) { - process_t *proc = create_process(vase, image_size, bss_size, entry); - processes[n_processes] = proc; - return n_processes++; + processes[++n_processes] = create_process(base, image_size, bss_size, entry); + return n_processes; } -process_t *create_process(void *base, u32_t image_size, u32_t bss_size, void *entry); +/* Create process_t struct for a new process + * image_size should be a multiple of 4096 + */ +process_t *create_process(void *base, u32_t image_size, u32_t bss_size, void *entry) { - if (mm_freepages < pages + bsspages + 25) + if (mm_freepages < ((image_size + bss_size) >> 12) + 25) return 0; process_t *process = New(process_t); /* Allocate process_t struct */ create_address_space(process); - create_process_stack(process); + create_process_stack(process, entry); int i; u32_t *ptr32 = process->v_page_dir = vmm_palloc(); for (i = 0; i < 1024; i++) *ptr32++ = 0; - u32_t code_data_pages = (image_size >> 12) + - ((image_size & 0x3FF) ? 1 : 0); - u32_t bss_pages = (bss_size >> 12) + ((bss_size & 0x3FF) ? 1 : 0); + u32_t code_data_pages = (image_size + 4095) >> 12; + u32_t bss_pages = (bss_size + 4095) >> 12; /* Load program at address 0 */ copy_into_address_space(0, base, code_data_pages, process); zero_address_space(code_data_pages << 12, bss_pages, process); ptr32 = process->v_page_dir; for (i = 0; i < 1024; i++) if (*ptr32) - vmm_unmapp(*ptr32); + vmm_unmapp((void *)*ptr32); vmm_unmapp(process->v_page_dir); process->v_page_dir = 0; + process->size = image_size + bss_size; return process; } @@ -68,23 +93,41 @@ void create_address_space(process_t *p) u32_t *ptr32 = p->page_dir; for (i = 0; i < 768; i++) /* zero 3 gigs */ *ptr32++ = 0; - memcpyd(ptr32, (void *)0xFFFFF000, 256); /* 1 gig kernel mem */ + memcpyd(ptr32, (void *)0xFFFFFC00, 256); /* 1 gig kernel mem */ } -void create_process_stack(process_t *p) +void create_process_stack(process_t *p, void *entry) { u32_t p_stack_table, p_stack; u32_t *v_stack_table = vmm_palloc_addr(&p_stack_table); - void *v_stack = vmm_palloc_addr(&p_stack); + u32_t *v_stack = vmm_palloc_addr(&p_stack); int i; u32_t *ptr32 = v_stack_table; - for (int i = 0; i < 1023; i++) + for (i = 0; i < 1023; i++) *ptr32++ = 0; v_stack_table[1032] = p_stack | 0x7; /* user permissions */ - p->page_dir[511] = p_stack_table | 0x7; + p->page_dir[511] = p_stack_table | 0x7; /* stack at 2GB */ + i = 1023; /* top of stack */ + v_stack[i--] = 0x20000000; + v_stack[i--] = SEG_USER_DATA | 0x3; + v_stack[i--] = 0x0202; + v_stack[i--] = SEG_USER_CODE | 0x3; + v_stack[i--] = (u32_t) entry; + v_stack[i--] = 0; + v_stack[i--] = 0; + v_stack[i--] = 0; + v_stack[i--] = 0; + v_stack[i--] = 0; + v_stack[i--] = 0; + v_stack[i--] = 0; + v_stack[i--] = SEG_USER_DATA | 0x3; + v_stack[i--] = SEG_USER_DATA | 0x3; + v_stack[i--] = SEG_USER_DATA | 0x3; + v_stack[i--] = SEG_USER_DATA | 0x3; vmm_unmapp(v_stack_table); vmm_unmapp(v_stack); - p->esp = 0x80000000; /* Stack at 2GB */ + p->ss = SEG_USER_DATA | 0x3; + p->esp = 0x20000000 - sizeof(int_stack_t); } /* Copy pages into new address space (v_page_dir must be set up) */ @@ -103,9 +146,9 @@ void copy_into_address_space(u32_t dest_addr, if (!p->v_page_dir[pde]) { /* Time for a new page table & page directory entry! */ - p->v_page_dir[pde] = vmm_palloc_addr(&p_page_addr); + p->v_page_dir[pde] = (u32_t)vmm_palloc_addr(&p_page_addr); p->page_dir[pde] = p_page_addr | 0x7; - vmm_unmapp(p->v_page_dir[pde]); + vmm_unmapp((void *)p->v_page_dir[pde]); } page = vmm_palloc_addr(&p_page_addr); ((u32_t *)(p->v_page_dir[pde]))[pte] = p_page_addr | 0x7; @@ -130,9 +173,9 @@ void zero_address_space(u32_t dest_addr, u32_t pages, process_t *p) if (!p->v_page_dir[pde]) { /* Time for a new page table & page directory entry! */ - p->v_page_dir[pde] = vmm_palloc_addr(&p_page_addr); + p->v_page_dir[pde] = (u32_t)vmm_palloc_addr(&p_page_addr); p->page_dir[pde] = p_page_addr | 0x7; - vmm_unmapp(p->v_page_dir[pde]); + vmm_unmapp((void *)p->v_page_dir[pde]); } page = vmm_palloc_addr(&p_page_addr); ((u32_t *)(p->v_page_dir[pde]))[pte] = p_page_addr | 0x7; diff --git a/kernel/proc/proc.h b/kernel/proc/proc.h index 6084a94..46baa32 100644 --- a/kernel/proc/proc.h +++ b/kernel/proc/proc.h @@ -15,16 +15,58 @@ typedef struct u32_t *v_page_dir; /* Virtual page table addresses for init */ u32_t p_page_dir; /* Physical address of page directory */ u32_t size; /* Process size, bytes from 0 through bss */ - u32_t esp; /* Process stack pointer */ - int_stack_frame_t stack_frame; + u32_t ss; + u32_t esp; } process_t; -int proc_init(int_stack_t *stack_frame); -void proc_sched(); +typedef struct +{ + u16_t backlink; + u16_t reserved0; + u32_t esp0; + u16_t ss0; + u16_t _ss0; + u32_t esp1; + u16_t ss1; + u16_t _ss1; + u32_t esp2; + u16_t ss2; + u16_t _ss2; + u32_t pdbr; + u32_t eip; + u32_t eflags; + u32_t eax; + u32_t ecx; + u32_t edx; + u32_t ebx; + u32_t esp; + u32_t ebp; + u32_t esi; + u32_t edi; + u16_t es; + u16_t _es; + u16_t cs; + u16_t _cs; + u16_t ss; + u16_t _ss; + u16_t ds; + u16_t _ds; + u16_t fs; + u16_t _fs; + u16_t gs; + u16_t _gs; + u16_t ldt; + u16_t _ldt; + u16_t trap; + u16_t iomap; +} __attribute__((packed)) tss_t; + +int proc_init(); +void proc_sched(int_stack_t *int_stack); u32_t create_task(void *base, u32_t image_size, u32_t bss_size, void *entry); process_t *create_process(void *base, u32_t image_size, u32_t bss_size, void *entry); void create_address_space(process_t *p); -void create_process_stack(process_t *p); +void create_process_stack(process_t *p, void *entry); void copy_into_address_space(u32_t dest_addr, void *src_addr, u32_t pages,