GFX_CONTEXT *_kgfx_new_context(int pid) { int context = -1; //check for existing context for this pid for (int i = 0; i < MAX_CONTEXTS; i++) { if (context_pid[i] == pid) { context = i; break; } } if (context == -1) { //take first unused context for (int i = 0; i < MAX_CONTEXTS; i++) { if (!context_pid[i]) { num_contexts++; context_pid[i] = pid; context = i; break; } } } _kmemclr(contexts[context].backbuffer->data, VGA_SIZE); _kmemcpy((uint8_t *)&palettes[context], (uint8_t *)&default_palette, PALETTE_SIZE); return &contexts[context]; }
ext2_write_status ext2_raw_write (struct ext2_filesystem_context *context, const char *path, const void *buffer, Uint32 *bytes_written, Uint32 start, Uint32 nbytes) { if( !bytes_written ) { Uint32 throwaway = 0; bytes_written = &throwaway; } *bytes_written = 0; if( path[0] != DIRECTORY_SEPARATOR ) { return EXT2_WRITE_NO_LEADING_SLASH; } // Split up the path into the filename and dirname components Uint path_length = _kstrlen( path ); const char *filename = _kstrrchr( path, DIRECTORY_SEPARATOR ) + 1; // Set up the directory name, keep the trailing slash, ignore filename. char dirname[path_length+1]; _kmemcpy( dirname, path, path_length+1 ); serial_printf( __FILE__ ":" CPP_STRINGIFY_RESULT(__LINE__) ", dirname before excluding filename: %s\n\r", dirname ); ((char *)_kstrrchr( dirname, DIRECTORY_SEPARATOR ))[1] = '\0'; serial_printf( __FILE__ ":" CPP_STRINGIFY_RESULT(__LINE__) ", dirname after excluding filename: %s\n\r", dirname ); ((char *)_kstrrchr( dirname, DIRECTORY_SEPARATOR ))[1] = '\0'; struct ext2_directory_entry* file = get_file_from_dir_path( context, dirname, filename ); if( file ) { *bytes_written = ext2_write_file_by_inode(context, file, buffer, start, nbytes); return EXT2_WRITE_SUCCESS; } else { return EXT2_WRITE_FILE_NOT_FOUND; } }
Uint32 ext2_write_file_by_inode (struct ext2_filesystem_context *context, struct ext2_directory_entry *file, void *buffer, Uint32 start, Uint32 nbytes) { Uint32 inode_num = file->inode_number; struct ext2_inode *fp = get_inode( context, inode_num ); Uint32 block_size = get_block_size( context->sb ); Uint32 indirect_block_size = block_size * block_size / sizeof(Uint32); // If we're going to write off of the end of a file, we need to // allocate a new block for the inode. if( fp->size < (nbytes+start) ) { // We can't have a gap in our file; don't start writing past // the end of the file. if( start > fp->size ) { return EXT2_WRITE_NON_CONTIGUOUS; } else { increase_inode_size ( context, fp, nbytes+start-(fp->size) ); } } // At this point, we know that we have enough blocks to store the data. Uint32 remaining_bytes = nbytes; // Skip ahead to the block that we need to write Uint32 first_inode_block = start / block_size; // Literal inode block indexes Uint32 indirect_inode_block_idx; Uint32 direct_inode_block_idx; if( first_inode_block >= EXT2_INODE_INDIRECT_BLOCK_IDX ) { direct_inode_block_idx = EXT2_INODE_INDIRECT_BLOCK_IDX; indirect_inode_block_idx = first_inode_block - EXT2_INODE_INDIRECT_BLOCK_IDX; } else { direct_inode_block_idx = first_inode_block; indirect_inode_block_idx = 0; } // The number of bytes into the first block that we need to skip Uint32 block_offset = start % block_size; for (Uint32 inode_block_idx = direct_inode_block_idx; inode_block_idx < EXT2_INODE_TOTAL_BLOCKS; inode_block_idx++) { Uint32 block_number = fp->blocks[inode_block_idx]; PRINT_VARIABLE( block_number ); Uint32 direct_inode_block_idx = first_inode_block % EXT2_INODE_TOTAL_BLOCKS; const char *data = (const char*) (block_offset + ((void*) block_number_to_address( context, block_number ))); // Pointer to an indirect block data area (can't be declared // inside of the `switch' statement. const void *indirect_block; #if DEBUG_FILESYSTEM PRINT_VARIABLE( block_offset ); serial_printf("inode_block_idx: %d\n\r", inode_block_idx ); PRINT_VARIABLE( indirect_inode_block_idx ); PRINT_VARIABLE( dub_indirect_inode_block_idx ); PRINT_VARIABLE( trip_indirect_inode_block_idx ); #endif // TODO: Support dub. indirect, trip. indirect blocks switch( inode_block_idx ) { case EXT2_INODE_TRIP_INDIRECT_BLOCK_IDX: case EXT2_INODE_DUB_INDIRECT_BLOCK_IDX: _kpanic( "ext2", __FILE__ ":" CPP_STRINGIFY_RESULT(__LINE__) " No support for double or tripple indirect blocks", FEATURE_UNIMPLEMENTED ); case EXT2_INODE_INDIRECT_BLOCK_IDX: // Dont' factor in the block offset when getting the // indirect data block. indirect_block = (const void*) (block_number_to_address( context, block_number )); // Don't let the inode block index advance unless // we've used all of the blocks in the indirect data // block. if( indirect_inode_block_idx < indirect_data_block_size(context) ) { --inode_block_idx; } data = (const void*) block_number_to_address( context, ((Uint32*) indirect_block)[indirect_inode_block_idx] ); data += block_offset; serial_printf("data: %x\n\r", data ); ++indirect_inode_block_idx; } if( remaining_bytes < block_size ) { _kmemcpy( data, buffer, remaining_bytes ); // We've copied everything we need to. Time to leave. remaining_bytes = 0; break; } else { _kmemcpy( data, buffer, block_size-block_offset ); buffer += (block_size-block_offset); // There's more to read remaining_bytes -= (block_size-block_offset); } // Reset the block offset -- we only use it once! block_offset = 0; } return nbytes-remaining_bytes; }
Status _elf_load_from_file(Pcb* pcb, const char* file_name) { // Need to copy the file_name into kernel land...because we're killing userland! const char* temp = file_name; file_name = (const char *)__kmalloc(_kstrlen(temp) + 1); _kmemcpy((void *)file_name, (void *)temp, _kstrlen(temp)+1); // Copy the null terminator as well serial_printf("---Elf: attempting to open: %s\n", file_name); if (pcb == NULL || file_name == NULL) { return BAD_PARAM; } // Try to open the file Elf32_Ehdr* elf32_hdr = (Elf32_Ehdr *)__kmalloc(sizeof(Elf32_Ehdr)); serial_printf("ELF header location: %x\n", elf32_hdr); Uint bytes_read = 0; VFSStatus vfs_status = raw_read(file_name, (void *)elf32_hdr, &bytes_read, 0, sizeof(Elf32_Ehdr)); if (vfs_status != FS_E_OK /* Couldn't read the file */ || bytes_read < sizeof(Elf32_Ehdr) /* Clearly not an ELF file */ || elf32_hdr->e_magic != ELF_MAGIC_NUM /* Need the magic number! */ || elf32_hdr->e_type != ET_EXEC /* Don't support relocatable or dynamic files yet */ || elf32_hdr->e_machine != EM_386 /* Make sure it's for our architecture */ || elf32_hdr->e_entry == 0x0 /* Need an entry point */ || elf32_hdr->e_version != EV_CURRENT /* We don't support extensions right now */ || elf32_hdr->e_phoff == 0 /* If there are no program headers, what do we load? */ || elf32_hdr->e_phnum == 0) /* ... */ // || elf32_hdr->e_ehsize != sizeof(Elf32_Ehdr)) /* The header size should match our struct */ { if (vfs_status != FS_E_OK) { serial_printf("RETURN VALUE: %x\n", vfs_status); _kpanic("ELF", "Failed to open file successfully\n", 0); } if (bytes_read < sizeof(Elf32_Ehdr)) _kpanic("ELF", "Read too small of a file!\n", 0); if (elf32_hdr->e_magic != ELF_MAGIC_NUM) _kpanic("ELF", "Bad magic number!\n", 0); if (elf32_hdr->e_type != ET_EXEC) _kpanic("ELF", "Not an executable ELF!\n", 0); if (elf32_hdr->e_machine != EM_386) _kpanic("ELF", "Not a i386 ELF!\n", 0); if (elf32_hdr->e_entry == 0x0) _kpanic("ELF", "Bad entry point!\n", 0); if (elf32_hdr->e_version != EV_CURRENT) _kpanic("ELF", "Don't support non-current versions!\n", 0); if (elf32_hdr->e_phoff == 0) _kpanic("ELF", "No program headers found!\n", 0); if (elf32_hdr->e_phnum == 0) _kpanic("ELF", "Zero program headers!\n", 0); _kpanic("ELF", "Couldn't open file!\n", 0); // Problem opening the file __kfree(elf32_hdr); return BAD_PARAM; } if (sizeof(Elf32_Phdr) != elf32_hdr->e_phentsize) { _kpanic("ELF", "program header size is different!\n", 0); } /* Okay lets start reading in and setting up the ELF file */ // We need a new buffer of size of (e_phentsize * e_phnum) Uint32 pheader_tbl_size = sizeof(Elf32_Phdr) * elf32_hdr->e_phnum; Elf32_Phdr* pheaders = (Elf32_Phdr *)__kmalloc(pheader_tbl_size); serial_printf("---ELF: program headers location: %x\n", pheaders); serial_printf("ELF: Reading program headers\n"); vfs_status = raw_read(file_name, (void *)pheaders, &bytes_read, elf32_hdr->e_phoff, pheader_tbl_size); if (vfs_status != FS_E_OK || bytes_read < pheader_tbl_size) { _kpanic("ELF", "error reading file!\n", 0); __kfree(pheaders); __kfree(elf32_hdr); return BAD_PARAM; } serial_printf("ELF: resetting page directory\n"); // Cleanup the old processes page directory, we're replacing everything __virt_reset_page_directory(); serial_printf("ELF: About to read the program sections\n"); /* We need to load all of the program sections now */ for (Int32 i = 0; i < elf32_hdr->e_phnum; ++i) { Elf32_Phdr* cur_phdr = &(pheaders[i]); if (cur_phdr->p_type == PT_LOAD) { if (cur_phdr->p_vaddr >= KERNEL_LINK_ADDR || cur_phdr->p_vaddr < 0x100000) { _kpanic("ELF", "An ELF with bad addresses loaded", 0); } serial_printf("\tELF: loading program section: %d at %x size: %x\n", i, cur_phdr->p_vaddr, cur_phdr->p_memsz); if (cur_phdr->p_memsz == 0) { serial_printf("\tELF: empty section, skipping\n"); continue; } // This is a loadable section //if (cur_phdr->p_align > 1) // _kpanic("ELF", "ELF loader doesn't support aligned program segments\n", 0); // Map these pages into memory! void* start_address = (void *)cur_phdr->p_vaddr; void* end_address = (void *)(start_address + cur_phdr->p_memsz); for (; start_address < end_address; start_address += PAGE_SIZE) { Uint32 flags = PG_USER; if ((cur_phdr->p_flags & PF_WRITE) > 0) { flags |= PG_READ_WRITE; } serial_printf("Checking address: %x\n", __virt_get_phys_addr(start_address)); serial_printf("Start address: %x\n", start_address); if (__virt_get_phys_addr(start_address) == (void *)0xFFFFFFFF) { serial_printf("ELF: Mapping page: %x - flags: %x\n", start_address, flags); __virt_map_page(__phys_get_free_4k(), start_address, flags); serial_printf("ELF: Done mapping page\n"); } else { serial_printf("Address: %x already mapped\n", start_address); } } serial_printf("ELF: about to memcpy program section: %x of size %d\n", cur_phdr->p_vaddr, cur_phdr->p_memsz); // Lets zero it out, we only need to zero the remaining bytes, p_filesz // may be zero for data sections, in this case the memory should be zeroed _kmemclr((void *)(cur_phdr->p_vaddr + (cur_phdr->p_memsz - cur_phdr->p_filesz)), cur_phdr->p_memsz - cur_phdr->p_filesz); serial_printf("ELF: done memory copying: %s\n", file_name); // Now we have to read it in from the file if (cur_phdr->p_filesz > 0) { serial_printf("\tAt offset: %x\n", cur_phdr->p_offset); vfs_status = raw_read(file_name, (void *)cur_phdr->p_vaddr, &bytes_read, cur_phdr->p_offset, cur_phdr->p_filesz); serial_printf("Read: %d - File size: %d\n", bytes_read, cur_phdr->p_filesz); if (bytes_read != cur_phdr->p_filesz) { _kpanic("ELF", "Failed to read data from the filesystem", 0); } if (vfs_status != FS_E_OK) { // TODO - cleanup if error _kpanic("ELF", "failed to read program section\n", 0); } //asm volatile("hlt"); } } else { serial_printf("\tELF: Non-loadable section: %d at %x size: %x type: %d\n", i, cur_phdr->p_vaddr, cur_phdr->p_memsz, cur_phdr->p_type); } } // Setup the PCB information // Allocate a stack and map some pages for it #define USER_STACK_LOCATION 0x2000000 #define USER_STACK_SIZE 0x4000 /* 16 KiB */ serial_printf("ELF: Allocating stack\n"); void* stack_start = (void *)USER_STACK_LOCATION; void* stack_end = (void *)(USER_STACK_LOCATION + USER_STACK_SIZE); for (; stack_start < stack_end; stack_start += PAGE_SIZE) { __virt_map_page(__phys_get_free_4k(), stack_start, PG_READ_WRITE | PG_USER); } _kmemclr((void *)USER_STACK_LOCATION, USER_STACK_SIZE); // Throw exit as the return address as a safe guard serial_printf("ELF: setting up context\n"); // Setup the context Context* context = ((Context *)(USER_STACK_LOCATION+USER_STACK_SIZE-4)) - 1; serial_printf("Context location: %x\n", context); pcb->context = context; context->esp = (Uint32)(((Uint32 *)context) - 1); context->ebp = (USER_STACK_LOCATION+USER_STACK_SIZE)-4; context->cs = GDT_CODE; context->ss = GDT_STACK; context->ds = GDT_DATA; context->es = GDT_DATA; context->fs = GDT_DATA; context->gs = GDT_DATA; serial_printf("ELF: setting entry point: %x\n", elf32_hdr->e_entry); // Entry point context->eip = elf32_hdr->e_entry; // Setup the rest of the PCB pcb->context->eflags = DEFAULT_EFLAGS; serial_printf("ELF: about to return\n"); __kfree(pheaders); __kfree(elf32_hdr); __kfree((void *)file_name); return SUCCESS; }
pcb_t *_create_process( pcb_t *curr ) { pcb_t *pcb; stack_t *stack; int offset; uint32_t *ptr; // allocate the new structures pcb = _pcb_alloc(); if( pcb == NULL ) { return( NULL ); } stack = _stack_alloc(); if( stack == NULL ) { _pcb_free(pcb); return( NULL ); } /* ** The PCB argument will be NULL if this function is called ** from the system initialization, and non-NULL if called ** from the fork() implementation. ** ** In the former case, we initialize the new data structures for ** a brand-new process. ** ** In the latter case, we replicate the information from the ** existing process whose PCB was passed in. */ if( curr != NULL ) { // called from fork() // duplicate the PCB and stack contents _kmemcpy( (void *) pcb, (void *) curr, sizeof(pcb_t) ); _kmemcpy( (void *) stack, (void *) curr->stack, sizeof(stack_t) ); // update the entries which should be changed in the PCB pcb->pid = _next_pid++; pcb->ppid = curr->pid; pcb->stack = stack; /* ** We duplicated the original stack contents, which ** means that the context pointer and ESP and EBP values ** in the new stack are still pointing into the original ** stack. We need to correct all of these. ** ** We have to change EBP because that's how the compiled ** code for the user process accesses its local variables. ** If we didn't change this, as soon as the new process ** was dispatched, it would start to stomp on the local ** variables in the original process' stack. We also ** have to fix the EBP chain in the child process. ** ** None of this would be an issue if we were doing "real" ** virtual memory, as we would be talking about virtual ** addresses here rather than physical addresses, and all ** processes would share the same virtual address space ** layout. ** ** First, determine the distance (in bytes) between the ** two stacks. This is the adjustment value we must add ** to the three pointers to correct them. Note that this ** distance may be positive or negative, depending on the ** relative placement of the two stacks in memory. */ offset = (void *) (pcb->stack) - (void *) (curr->stack); // modify the context pointer for the new process pcb->context = (context_t *)( (void *) (pcb->context) + offset ); // now, change ESP and EBP in the new process (easy to // do because they're just uint32_t values, not really // pointers) pcb->context->esp += offset; pcb->context->ebp += offset; /* ** Next, we must fix the EBP chain in the new stack. This ** is necessary in the situation where the fork() occurred ** in a nested function call sequence; we fixed EBP, but ** the "saved" EBP in the stack frame is pointing to the ** calling function's frame in the original stack, not the ** new stack. ** ** We are guaranteed that the chain of frames ends at the ** frame for the original process' main() routine, because ** exec() will initialize EBP for the process to 0, and the ** entry prologue code in main() routine will push EBP, ** ensuring a NULL pointer in the chain. */ // start at the current frame ptr = (uint32_t *) pcb->context->ebp; // follow the chain of frame pointers to its end while( *ptr != 0 ) { // update the link from this frame to the previous *ptr += offset; // follow the updated link ptr = (uint32_t *) *ptr; } } else { // called from init pcb->pid = pcb->ppid = PID_INIT; pcb->stack = stack; } // all done - return the new PCB return( pcb ); }