Beispiel #1
0
/*
 * This function implements the brk(2) system call.
 *
 * This routine manages the calling process's "break" -- the ending address
 * of the process's "dynamic" region (often also referred to as the "heap").
 * The current value of a process's break is maintained in the 'p_brk' member
 * of the proc_t structure that represents the process in question.
 *
 * The 'p_brk' and 'p_start_brk' members of a proc_t struct are initialized
 * by the loader. 'p_start_brk' is subsequently never modified; it always
 * holds the initial value of the break. Note that the starting break is
 * not necessarily page aligned!
 *
 * 'p_start_brk' is the lower limit of 'p_brk' (that is, setting the break
 * to any value less than 'p_start_brk' should be disallowed).
 *
 * The upper limit of 'p_brk' is defined by the minimum of (1) the
 * starting address of the next occuring mapping or (2) USER_MEM_HIGH.
 * That is, growth of the process break is limited only in that it cannot
 * overlap with/expand into an existing mapping or beyond the region of
 * the address space allocated for use by userland. (note the presence of
 * the 'vmmap_is_range_empty' function).
 *
 * The dynamic region should always be represented by at most ONE vmarea.
 * Note that vmareas only have page granularity, you will need to take this
 * into account when deciding how to set the mappings if p_brk or p_start_brk
 * is not page aligned.
 *
 * You are guaranteed that the process data/bss region is non-empty.
 * That is, if the starting brk is not page-aligned, its page has
 * read/write permissions.
 *
 * If addr is NULL, you should NOT fail as the man page says. Instead,
 * "return" the current break. We use this to implement sbrk(0) without writing
 * a separate syscall. Look in user/libc/syscall.c if you're curious.
 *
 * Also, despite the statement on the manpage, you MUST support combined use
 * of brk and mmap in the same process.
 *
 * Note that this function "returns" the new break through the "ret" argument.
 * Return 0 on success, -errno on failure.
 */
int
do_brk(void *addr, void **ret)
{
    if (addr == NULL) {
        *ret = curproc->p_brk;
        return 0;
    }

    uintptr_t start_brk = (uintptr_t)curproc->p_start_brk;
    uintptr_t brk = (uintptr_t)curproc->p_brk;
    uintptr_t vaddr = (uintptr_t)addr;
    uint32_t lopage = ADDR_TO_PN(PAGE_ALIGN_DOWN(start_brk));

    if (vaddr < start_brk) {
        return -ENOMEM;
    }

    if (vaddr >= USER_MEM_HIGH) {
        return -ENOMEM;
    }

    if (vaddr == brk) {
        *ret = addr;
        KASSERT(curproc->p_brk == addr);
        curproc->p_brk = addr;
        return 0;
    }

    KASSERT(start_brk <= brk);

    vmarea_t *area = vmmap_lookup(curproc->p_vmmap, lopage);

    if (area == NULL) {
        panic("panic for now\n");
        return -1;
    } else {
        KASSERT(area);

        uint32_t hiaddr = (uint32_t)(vaddr - 1);
        uint32_t hipage = ADDR_TO_PN(hiaddr);
        if (hipage < area->vma_end) {
            area->vma_end = hipage + 1;
            *ret = addr;
            curproc->p_brk = addr;
            return 0;
        } else {
            if (vmmap_is_range_empty(curproc->p_vmmap, area->vma_end,
                                        hipage - area->vma_end + 1)) {
                area->vma_end = hipage + 1;
                *ret = addr;
                curproc->p_brk = addr;
                return 0;
            } else {
                return -ENOMEM;
            }
        }
    }
        /*NOT_YET_IMPLEMENTED("VM: do_brk");*/
        /*return 0;*/
}
Beispiel #2
0
/*
 * This function implements the brk(2) system call.
 *
 * This routine manages the calling process's "break" -- the ending address
 * of the process's "dynamic" region (often also referred to as the "heap").
 * The current value of a process's break is maintained in the 'p_brk' member
 * of the proc_t structure that represents the process in question.
 *
 * The 'p_brk' and 'p_start_brk' members of a proc_t struct are initialized
 * by the loader. 'p_start_brk' is subsequently never modified; it always
 * holds the initial value of the break. Note that the starting break is
 * not necessarily page aligned!
 *
 * 'p_start_brk' is the lower limit of 'p_brk' (that is, setting the break
 * to any value less than 'p_start_brk' should be disallowed).
 *
 * The upper limit of 'p_brk' is defined by the minimum of (1) the
 * starting address of the next occuring mapping or (2) USER_MEM_HIGH.
 * That is, growth of the process break is limited only in that it cannot
 * overlap with/expand into an existing mapping or beyond the region of
 * the address space allocated for use by userland. (note the presence of
 * the 'vmmap_is_range_empty' function).
 *
 * The dynamic region should always be represented by at most ONE vmarea.
 * Note that vmareas only have page granularity, you will need to take this
 * into account when deciding how to set the mappings if p_brk or p_start_brk
 * is not page aligned.
 *
 * You are guaranteed that the process data/bss region is non-empty.
 * That is, if the starting brk is not page-aligned, its page has
 * read/write permissions.
 *
 * If addr is NULL, you should NOT fail as the man page says. Instead,
 * "return" the current break. We use this to implement sbrk(0) without writing
 * a separate syscall. Look in user/libc/syscall.c if you're curious.
 *
 * Also, despite the statement on the manpage, you MUST support combined use
 * of brk and mmap in the same process.
 *
 * Note that this function "returns" the new break through the "ret" argument.
 * Return 0 on success, -errno on failure.
 */
int do_brk(void *addr, void **ret) {
	/*NOT_YET_IMPLEMENTED("VM: do_brk");*/
	vmarea_t *vmarea;
	/*If addr is NULL, "return" the current break.*/
	dbg(DBG_PRINT, "(GRADING3D 3)\n");
	if (addr == NULL || addr == curproc->p_brk) {
		dbg(DBG_PRINT, "(GRADING3D 3)\n");
		*ret = curproc->p_brk;
		return 0;
	}

	/*check for the address range*/
	if (((uint32_t) addr > USER_MEM_HIGH) || (curproc->p_start_brk > addr)) {
		dbg(DBG_PRINT, "(GRADING3D 3)\n");
		return -ENOMEM;
	}

	/*if p_brk and addr are not page aligned*/
	if (ADDR_TO_PN(
			PAGE_ALIGN_UP(curproc->p_brk)) != ADDR_TO_PN(PAGE_ALIGN_UP(addr))) {
		dbg(DBG_PRINT, "(GRADING3D 3)\n");
		if (addr > curproc->p_brk) {
			dbg(DBG_PRINT, "(GRADING3D 3)\n");
			if (!vmmap_is_range_empty(curproc->p_vmmap,
					ADDR_TO_PN(PAGE_ALIGN_UP(curproc->p_brk)),
					ADDR_TO_PN(
							PAGE_ALIGN_UP(addr)) -ADDR_TO_PN(PAGE_ALIGN_UP(curproc->p_brk)))) {
				dbg(DBG_PRINT, "(GRADING3D 3)\n");
				return -ENOMEM;
			}
			vmarea = vmmap_lookup(curproc->p_vmmap,
					ADDR_TO_PN(PAGE_ALIGN_UP(curproc->p_start_brk)));
			if (vmarea != NULL) {
				dbg(DBG_PRINT, "(GRADING3D 3)\n");
				vmarea->vma_end = ADDR_TO_PN(PAGE_ALIGN_UP(addr));
			} else {
				dbg(DBG_PRINT, "(GRADING3D 3)\n");
				vmmap_map(curproc->p_vmmap,
				NULL, ADDR_TO_PN(PAGE_ALIGN_UP(curproc->p_start_brk)),
						ADDR_TO_PN(
								PAGE_ALIGN_UP(addr)) - ADDR_TO_PN(PAGE_ALIGN_UP(curproc->p_start_brk)),
						PROT_READ | PROT_WRITE, MAP_PRIVATE, 0, VMMAP_DIR_LOHI,
						&vmarea);
			}
		} else {
			dbg(DBG_PRINT, "(GRADING3D 3)\n");
			vmmap_remove(curproc->p_vmmap, ADDR_TO_PN(PAGE_ALIGN_UP(addr)),
					ADDR_TO_PN(
							PAGE_ALIGN_UP(curproc->p_brk)) -ADDR_TO_PN(PAGE_ALIGN_UP(addr)));
		}

		curproc->p_brk = addr;
		*ret = addr;
	} else {
		dbg(DBG_PRINT, "(GRADING3D 3)\n");
		curproc->p_brk = addr;
		*ret = addr;
		return 0;
	}
	return 0;
}
Beispiel #3
0
/* Helper function for the ELF loader. Maps the specified segment
 * of the program header from the given file in to the given address
 * space with the given memory offset (in pages). On success returns 0, otherwise
 * returns a negative error code for the ELF loader to return.
 * Note that since any error returned by this function should
 * cause the ELF loader to give up, it is acceptable for the
 * address space to be modified after returning an error.
 * Note that memoff can be negative */
static int _elf32_map_segment(vmmap_t *map, vnode_t *file, int32_t memoff, const Elf32_Phdr *segment)
{
        uintptr_t addr;
        if (memoff < 0) {
                KASSERT(ADDR_TO_PN(segment->p_vaddr) > (uint32_t) -memoff);
                addr = (uintptr_t)segment->p_vaddr - (uintptr_t)PN_TO_ADDR(-memoff);
        } else {
                addr = (uintptr_t)segment->p_vaddr + (uintptr_t)PN_TO_ADDR(memoff);
        }
        uint32_t off = segment->p_offset;
        uint32_t memsz = segment->p_memsz;
        uint32_t filesz = segment->p_filesz;

        dbg(DBG_ELF, "Mapping program segment: type %#x, offset %#08x,"
            " vaddr %#08x, filesz %#x, memsz %#x, flags %#x, align %#x\n",
            segment->p_type, segment->p_offset, segment->p_vaddr,
            segment->p_filesz, segment->p_memsz, segment->p_flags,
            segment->p_align);

        /* check for bad data in the segment header */
        if (PAGE_SIZE != segment->p_align) {
                dbg(DBG_ELF, "ERROR: segment does not have correct alignment\n");
                return -ENOEXEC;
        } else if (filesz > memsz) {
                dbg(DBG_ELF, "ERROR: segment file size is greater than memory size\n");
                return -ENOEXEC;
        } else if (PAGE_OFFSET(addr) != PAGE_OFFSET(off)) {
                dbg(DBG_ELF, "ERROR: segment address and offset are not aligned correctly\n");
                return -ENOEXEC;
        }

        int perms = 0;
        if (PF_R & segment->p_flags) {
                perms |= PROT_READ;
        }
        if (PF_W & segment->p_flags) {
                perms |= PROT_WRITE;
        }
        if (PF_X & segment->p_flags) {
                perms |= PROT_EXEC;
        }

        if (0 < filesz) {
                /* something needs to be mapped from the file */
                /* start from the starting address and include enough pages to
                 * map all filesz bytes of the file */
                uint32_t lopage = ADDR_TO_PN(addr);
                uint32_t npages = ADDR_TO_PN(addr + filesz - 1) - lopage + 1;
                off_t fileoff = (off_t)PAGE_ALIGN_DOWN(off);

                int ret;
                if (!vmmap_is_range_empty(map, lopage, npages)) {
                        dbg(DBG_ELF, "ERROR: ELF file contains overlapping segments\n");
                        return -ENOEXEC;
                } else if (0 > (ret = vmmap_map(map, file, lopage, npages, perms,
                                                MAP_PRIVATE | MAP_FIXED, fileoff,
                                                0, NULL))) {
                        return ret;
                }
        }

        if (memsz > filesz) {
                /* there is left over memory in the segment which must
                 * be initialized to 0 (anonymously mapped) */
                uint32_t lopage = ADDR_TO_PN(addr + filesz);
                uint32_t npages = ADDR_TO_PN(PAGE_ALIGN_UP(addr + memsz)) - lopage;

                int ret;
                if (npages > 1 && !vmmap_is_range_empty(map, lopage + 1, npages - 1)) {
                        dbg(DBG_ELF, "ERROR: ELF file contains overlapping segments\n");
                        return -ENOEXEC;
                } else if (0 > (ret = vmmap_map(map, NULL, lopage, npages, perms,
                                                MAP_PRIVATE | MAP_FIXED, 0, 0, NULL))) {
                        return ret;
                } else if (!PAGE_ALIGNED(addr + filesz) && filesz > 0) {
                        /* In this case, we have accidentally zeroed too much of memory, as
                         * we zeroed all memory in the page containing addr + filesz.
                         * However, the remaining part of the data is not a full page, so we
                         * should not just map in another page (as there could be garbage
                         * after addr+filesz). For instance, consider the data-bss boundary
                         * (c.f. Intel x86 ELF supplement pp. 82).
                         * To fix this, we need to read in the contents of the file manually
                         * and put them at that user space addr in the anon map we just
                         * added. */
                        void *buf;
                        if (NULL == (buf = page_alloc()))
                                return -ENOMEM;
                        if (!(0 > (ret = file->vn_ops->read(file, (off_t) PAGE_ALIGN_DOWN(off + filesz),
                                                            buf, PAGE_OFFSET(addr + filesz))))) {
                                ret = vmmap_write(map, PAGE_ALIGN_DOWN(addr + filesz),
                                                  buf, PAGE_OFFSET(addr + filesz));
                        }
                        page_free(buf);
                        return ret;
                }
        }
        return 0;
}