2010-07-23 16:52:35 +00:00
|
|
|
#include "param.h"
|
|
|
|
#include "types.h"
|
2011-07-29 11:31:27 +00:00
|
|
|
#include "memlayout.h"
|
2010-07-23 16:52:35 +00:00
|
|
|
#include "elf.h"
|
2019-05-31 13:45:59 +00:00
|
|
|
#include "riscv.h"
|
|
|
|
#include "defs.h"
|
2019-05-31 15:45:42 +00:00
|
|
|
#include "fs.h"
|
Checkpoint port of xv6 to x86-64. Passed usertests on 2 processors a few times.
The x86-64 doesn't just add two levels to page tables to support 64 bit
addresses, but is a different processor. For example, calling conventions,
system calls, and segmentation are different from 32-bit x86. Segmentation is
basically gone, but gs/fs in combination with MSRs can be used to hold a
per-core pointer. In general, x86-64 is more straightforward than 32-bit
x86. The port uses code from sv6 and the xv6 "rsc-amd64" branch.
A summary of the changes is as follows:
- Booting: switch to grub instead of xv6's bootloader (pass -kernel to qemu),
because xv6's boot loader doesn't understand 64bit ELF files. And, we don't
care anymore about booting.
- Makefile: use -m64 instead of -m32 flag for gcc, delete boot loader, xv6.img,
bochs, and memfs. For now dont' use -O2, since usertests with -O2 is bigger than
MAXFILE!
- Update gdb.tmpl to be for i386 or x86-64
- Console/printf: use stdarg.h and treat 64-bit addresses different from ints
(32-bit)
- Update elfhdr to be 64 bit
- entry.S/entryother.S: add code to switch to 64-bit mode: build a simple page
table in 32-bit mode before switching to 64-bit mode, share code for entering
boot processor and APs, and tweak boot gdt. The boot gdt is the gdt that the
kernel proper also uses. (In 64-bit mode, the gdt/segmentation and task state
mostly disappear.)
- exec.c: fix passing argv (64-bit now instead of 32-bit).
- initcode.c: use syscall instead of int.
- kernel.ld: load kernel very high, in top terabyte. 64 bits is a lot of
address space!
- proc.c: initial return is through new syscall path instead of trapret.
- proc.h: update struct cpu to have some scratch space since syscall saves less
state than int, update struct context to reflect x86-64 calling conventions.
- swtch: simplify for x86-64 calling conventions.
- syscall: add fetcharg to handle x86-64 calling convetions (6 arguments are
passed through registers), and fetchaddr to read a 64-bit value from user space.
- sysfile: update to handle pointers from user space (e.g., sys_exec), which are
64 bits.
- trap.c: no special trap vector for sys calls, because x86-64 has a different
plan for system calls.
- trapasm: one plan for syscalls and one plan for traps (interrupt and
exceptions). On x86-64, the kernel is responsible for switching user/kernel
stacks. To do, xv6 keeps some scratch space in the cpu structure, and uses MSR
GS_KERN_BASE to point to the core's cpu structure (using swapgs).
- types.h: add uint64, and change pde_t to uint64
- usertests: exit() when fork fails, which helped in tracking down one of the
bugs in the switch from 32-bit to 64-bit
- vectors: update to make them 64 bits
- vm.c: use bootgdt in kernel too, program MSRs for syscalls and core-local
state (for swapgs), walk 4 levels in walkpgdir, add DEVSPACETOP, use task
segment to set kernel stack for interrupts (but simpler than in 32-bit mode),
add an extra argument to freevm (size of user part of address space) to avoid
checking all entries till KERNBASE (there are MANY TB before the top 1TB).
- x86: update trapframe to have 64-bit entries, which is what the processor
pushes on syscalls and traps. simplify lgdt and lidt, using struct desctr,
which needs the gcc directives packed and aligned.
TODO:
- use int32 instead of int?
- simplify curproc(). xv6 has per-cpu state again, but this time it must have it.
- avoid repetition in walkpgdir
- fix validateint() in usertests.c
- fix bugs (e.g., observed one a case of entering kernel with invalid gs or proc
2018-09-23 12:24:42 +00:00
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
/*
|
|
|
|
* the kernel's page table.
|
|
|
|
*/
|
|
|
|
pagetable_t kernel_pagetable;
|
Checkpoint port of xv6 to x86-64. Passed usertests on 2 processors a few times.
The x86-64 doesn't just add two levels to page tables to support 64 bit
addresses, but is a different processor. For example, calling conventions,
system calls, and segmentation are different from 32-bit x86. Segmentation is
basically gone, but gs/fs in combination with MSRs can be used to hold a
per-core pointer. In general, x86-64 is more straightforward than 32-bit
x86. The port uses code from sv6 and the xv6 "rsc-amd64" branch.
A summary of the changes is as follows:
- Booting: switch to grub instead of xv6's bootloader (pass -kernel to qemu),
because xv6's boot loader doesn't understand 64bit ELF files. And, we don't
care anymore about booting.
- Makefile: use -m64 instead of -m32 flag for gcc, delete boot loader, xv6.img,
bochs, and memfs. For now dont' use -O2, since usertests with -O2 is bigger than
MAXFILE!
- Update gdb.tmpl to be for i386 or x86-64
- Console/printf: use stdarg.h and treat 64-bit addresses different from ints
(32-bit)
- Update elfhdr to be 64 bit
- entry.S/entryother.S: add code to switch to 64-bit mode: build a simple page
table in 32-bit mode before switching to 64-bit mode, share code for entering
boot processor and APs, and tweak boot gdt. The boot gdt is the gdt that the
kernel proper also uses. (In 64-bit mode, the gdt/segmentation and task state
mostly disappear.)
- exec.c: fix passing argv (64-bit now instead of 32-bit).
- initcode.c: use syscall instead of int.
- kernel.ld: load kernel very high, in top terabyte. 64 bits is a lot of
address space!
- proc.c: initial return is through new syscall path instead of trapret.
- proc.h: update struct cpu to have some scratch space since syscall saves less
state than int, update struct context to reflect x86-64 calling conventions.
- swtch: simplify for x86-64 calling conventions.
- syscall: add fetcharg to handle x86-64 calling convetions (6 arguments are
passed through registers), and fetchaddr to read a 64-bit value from user space.
- sysfile: update to handle pointers from user space (e.g., sys_exec), which are
64 bits.
- trap.c: no special trap vector for sys calls, because x86-64 has a different
plan for system calls.
- trapasm: one plan for syscalls and one plan for traps (interrupt and
exceptions). On x86-64, the kernel is responsible for switching user/kernel
stacks. To do, xv6 keeps some scratch space in the cpu structure, and uses MSR
GS_KERN_BASE to point to the core's cpu structure (using swapgs).
- types.h: add uint64, and change pde_t to uint64
- usertests: exit() when fork fails, which helped in tracking down one of the
bugs in the switch from 32-bit to 64-bit
- vectors: update to make them 64 bits
- vm.c: use bootgdt in kernel too, program MSRs for syscalls and core-local
state (for swapgs), walk 4 levels in walkpgdir, add DEVSPACETOP, use task
segment to set kernel stack for interrupts (but simpler than in 32-bit mode),
add an extra argument to freevm (size of user part of address space) to avoid
checking all entries till KERNBASE (there are MANY TB before the top 1TB).
- x86: update trapframe to have 64-bit entries, which is what the processor
pushes on syscalls and traps. simplify lgdt and lidt, using struct desctr,
which needs the gcc directives packed and aligned.
TODO:
- use int32 instead of int?
- simplify curproc(). xv6 has per-cpu state again, but this time it must have it.
- avoid repetition in walkpgdir
- fix validateint() in usertests.c
- fix bugs (e.g., observed one a case of entering kernel with invalid gs or proc
2018-09-23 12:24:42 +00:00
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
extern char etext[]; // kernel.ld sets this to end of kernel code.
|
Checkpoint port of xv6 to x86-64. Passed usertests on 2 processors a few times.
The x86-64 doesn't just add two levels to page tables to support 64 bit
addresses, but is a different processor. For example, calling conventions,
system calls, and segmentation are different from 32-bit x86. Segmentation is
basically gone, but gs/fs in combination with MSRs can be used to hold a
per-core pointer. In general, x86-64 is more straightforward than 32-bit
x86. The port uses code from sv6 and the xv6 "rsc-amd64" branch.
A summary of the changes is as follows:
- Booting: switch to grub instead of xv6's bootloader (pass -kernel to qemu),
because xv6's boot loader doesn't understand 64bit ELF files. And, we don't
care anymore about booting.
- Makefile: use -m64 instead of -m32 flag for gcc, delete boot loader, xv6.img,
bochs, and memfs. For now dont' use -O2, since usertests with -O2 is bigger than
MAXFILE!
- Update gdb.tmpl to be for i386 or x86-64
- Console/printf: use stdarg.h and treat 64-bit addresses different from ints
(32-bit)
- Update elfhdr to be 64 bit
- entry.S/entryother.S: add code to switch to 64-bit mode: build a simple page
table in 32-bit mode before switching to 64-bit mode, share code for entering
boot processor and APs, and tweak boot gdt. The boot gdt is the gdt that the
kernel proper also uses. (In 64-bit mode, the gdt/segmentation and task state
mostly disappear.)
- exec.c: fix passing argv (64-bit now instead of 32-bit).
- initcode.c: use syscall instead of int.
- kernel.ld: load kernel very high, in top terabyte. 64 bits is a lot of
address space!
- proc.c: initial return is through new syscall path instead of trapret.
- proc.h: update struct cpu to have some scratch space since syscall saves less
state than int, update struct context to reflect x86-64 calling conventions.
- swtch: simplify for x86-64 calling conventions.
- syscall: add fetcharg to handle x86-64 calling convetions (6 arguments are
passed through registers), and fetchaddr to read a 64-bit value from user space.
- sysfile: update to handle pointers from user space (e.g., sys_exec), which are
64 bits.
- trap.c: no special trap vector for sys calls, because x86-64 has a different
plan for system calls.
- trapasm: one plan for syscalls and one plan for traps (interrupt and
exceptions). On x86-64, the kernel is responsible for switching user/kernel
stacks. To do, xv6 keeps some scratch space in the cpu structure, and uses MSR
GS_KERN_BASE to point to the core's cpu structure (using swapgs).
- types.h: add uint64, and change pde_t to uint64
- usertests: exit() when fork fails, which helped in tracking down one of the
bugs in the switch from 32-bit to 64-bit
- vectors: update to make them 64 bits
- vm.c: use bootgdt in kernel too, program MSRs for syscalls and core-local
state (for swapgs), walk 4 levels in walkpgdir, add DEVSPACETOP, use task
segment to set kernel stack for interrupts (but simpler than in 32-bit mode),
add an extra argument to freevm (size of user part of address space) to avoid
checking all entries till KERNBASE (there are MANY TB before the top 1TB).
- x86: update trapframe to have 64-bit entries, which is what the processor
pushes on syscalls and traps. simplify lgdt and lidt, using struct desctr,
which needs the gcc directives packed and aligned.
TODO:
- use int32 instead of int?
- simplify curproc(). xv6 has per-cpu state again, but this time it must have it.
- avoid repetition in walkpgdir
- fix validateint() in usertests.c
- fix bugs (e.g., observed one a case of entering kernel with invalid gs or proc
2018-09-23 12:24:42 +00:00
|
|
|
|
2019-06-01 09:33:38 +00:00
|
|
|
extern char trampout[]; // trampoline.S
|
2010-07-23 16:52:35 +00:00
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
/*
|
|
|
|
* create a direct-map page table for the kernel and
|
|
|
|
* turn on paging. called early, in supervisor mode.
|
|
|
|
* the page allocator is already initialized.
|
|
|
|
*/
|
2010-09-02 20:23:15 +00:00
|
|
|
void
|
2019-05-31 13:45:59 +00:00
|
|
|
kvminit()
|
2010-09-02 20:23:15 +00:00
|
|
|
{
|
2019-05-31 13:45:59 +00:00
|
|
|
kernel_pagetable = (pagetable_t) kalloc();
|
|
|
|
memset(kernel_pagetable, 0, PGSIZE);
|
Checkpoint port of xv6 to x86-64. Passed usertests on 2 processors a few times.
The x86-64 doesn't just add two levels to page tables to support 64 bit
addresses, but is a different processor. For example, calling conventions,
system calls, and segmentation are different from 32-bit x86. Segmentation is
basically gone, but gs/fs in combination with MSRs can be used to hold a
per-core pointer. In general, x86-64 is more straightforward than 32-bit
x86. The port uses code from sv6 and the xv6 "rsc-amd64" branch.
A summary of the changes is as follows:
- Booting: switch to grub instead of xv6's bootloader (pass -kernel to qemu),
because xv6's boot loader doesn't understand 64bit ELF files. And, we don't
care anymore about booting.
- Makefile: use -m64 instead of -m32 flag for gcc, delete boot loader, xv6.img,
bochs, and memfs. For now dont' use -O2, since usertests with -O2 is bigger than
MAXFILE!
- Update gdb.tmpl to be for i386 or x86-64
- Console/printf: use stdarg.h and treat 64-bit addresses different from ints
(32-bit)
- Update elfhdr to be 64 bit
- entry.S/entryother.S: add code to switch to 64-bit mode: build a simple page
table in 32-bit mode before switching to 64-bit mode, share code for entering
boot processor and APs, and tweak boot gdt. The boot gdt is the gdt that the
kernel proper also uses. (In 64-bit mode, the gdt/segmentation and task state
mostly disappear.)
- exec.c: fix passing argv (64-bit now instead of 32-bit).
- initcode.c: use syscall instead of int.
- kernel.ld: load kernel very high, in top terabyte. 64 bits is a lot of
address space!
- proc.c: initial return is through new syscall path instead of trapret.
- proc.h: update struct cpu to have some scratch space since syscall saves less
state than int, update struct context to reflect x86-64 calling conventions.
- swtch: simplify for x86-64 calling conventions.
- syscall: add fetcharg to handle x86-64 calling convetions (6 arguments are
passed through registers), and fetchaddr to read a 64-bit value from user space.
- sysfile: update to handle pointers from user space (e.g., sys_exec), which are
64 bits.
- trap.c: no special trap vector for sys calls, because x86-64 has a different
plan for system calls.
- trapasm: one plan for syscalls and one plan for traps (interrupt and
exceptions). On x86-64, the kernel is responsible for switching user/kernel
stacks. To do, xv6 keeps some scratch space in the cpu structure, and uses MSR
GS_KERN_BASE to point to the core's cpu structure (using swapgs).
- types.h: add uint64, and change pde_t to uint64
- usertests: exit() when fork fails, which helped in tracking down one of the
bugs in the switch from 32-bit to 64-bit
- vectors: update to make them 64 bits
- vm.c: use bootgdt in kernel too, program MSRs for syscalls and core-local
state (for swapgs), walk 4 levels in walkpgdir, add DEVSPACETOP, use task
segment to set kernel stack for interrupts (but simpler than in 32-bit mode),
add an extra argument to freevm (size of user part of address space) to avoid
checking all entries till KERNBASE (there are MANY TB before the top 1TB).
- x86: update trapframe to have 64-bit entries, which is what the processor
pushes on syscalls and traps. simplify lgdt and lidt, using struct desctr,
which needs the gcc directives packed and aligned.
TODO:
- use int32 instead of int?
- simplify curproc(). xv6 has per-cpu state again, but this time it must have it.
- avoid repetition in walkpgdir
- fix validateint() in usertests.c
- fix bugs (e.g., observed one a case of entering kernel with invalid gs or proc
2018-09-23 12:24:42 +00:00
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// uart registers
|
|
|
|
mappages(kernel_pagetable, UART0, PGSIZE,
|
|
|
|
UART0, PTE_R | PTE_W);
|
|
|
|
|
2019-06-13 10:57:38 +00:00
|
|
|
// virtio mmio disk interface
|
2019-06-13 13:40:17 +00:00
|
|
|
mappages(kernel_pagetable, VIRTIO0, PGSIZE,
|
|
|
|
VIRTIO0, PTE_R | PTE_W);
|
2019-06-13 10:49:02 +00:00
|
|
|
|
2019-06-04 18:20:37 +00:00
|
|
|
// CLINT
|
|
|
|
mappages(kernel_pagetable, CLINT, 0x10000,
|
|
|
|
CLINT, PTE_R | PTE_W);
|
|
|
|
|
2019-06-03 18:13:07 +00:00
|
|
|
// PLIC
|
|
|
|
mappages(kernel_pagetable, PLIC, 0x4000000,
|
|
|
|
PLIC, PTE_R | PTE_W);
|
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// map kernel text executable and read-only.
|
|
|
|
mappages(kernel_pagetable, KERNBASE, (uint64)etext-KERNBASE,
|
|
|
|
KERNBASE, PTE_R | PTE_X);
|
2019-06-04 14:43:45 +00:00
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// map kernel data and the physical RAM we'll make use of.
|
|
|
|
mappages(kernel_pagetable, (uint64)etext, PHYSTOP-(uint64)etext,
|
|
|
|
(uint64)etext, PTE_R | PTE_W);
|
|
|
|
|
|
|
|
// map the trampoline for trap entry/exit to
|
|
|
|
// the highest virtual address in the kernel.
|
|
|
|
mappages(kernel_pagetable, TRAMPOLINE, PGSIZE,
|
2019-06-01 09:33:38 +00:00
|
|
|
(uint64)trampout, PTE_R | PTE_X);
|
2019-06-05 18:05:46 +00:00
|
|
|
}
|
Checkpoint port of xv6 to x86-64. Passed usertests on 2 processors a few times.
The x86-64 doesn't just add two levels to page tables to support 64 bit
addresses, but is a different processor. For example, calling conventions,
system calls, and segmentation are different from 32-bit x86. Segmentation is
basically gone, but gs/fs in combination with MSRs can be used to hold a
per-core pointer. In general, x86-64 is more straightforward than 32-bit
x86. The port uses code from sv6 and the xv6 "rsc-amd64" branch.
A summary of the changes is as follows:
- Booting: switch to grub instead of xv6's bootloader (pass -kernel to qemu),
because xv6's boot loader doesn't understand 64bit ELF files. And, we don't
care anymore about booting.
- Makefile: use -m64 instead of -m32 flag for gcc, delete boot loader, xv6.img,
bochs, and memfs. For now dont' use -O2, since usertests with -O2 is bigger than
MAXFILE!
- Update gdb.tmpl to be for i386 or x86-64
- Console/printf: use stdarg.h and treat 64-bit addresses different from ints
(32-bit)
- Update elfhdr to be 64 bit
- entry.S/entryother.S: add code to switch to 64-bit mode: build a simple page
table in 32-bit mode before switching to 64-bit mode, share code for entering
boot processor and APs, and tweak boot gdt. The boot gdt is the gdt that the
kernel proper also uses. (In 64-bit mode, the gdt/segmentation and task state
mostly disappear.)
- exec.c: fix passing argv (64-bit now instead of 32-bit).
- initcode.c: use syscall instead of int.
- kernel.ld: load kernel very high, in top terabyte. 64 bits is a lot of
address space!
- proc.c: initial return is through new syscall path instead of trapret.
- proc.h: update struct cpu to have some scratch space since syscall saves less
state than int, update struct context to reflect x86-64 calling conventions.
- swtch: simplify for x86-64 calling conventions.
- syscall: add fetcharg to handle x86-64 calling convetions (6 arguments are
passed through registers), and fetchaddr to read a 64-bit value from user space.
- sysfile: update to handle pointers from user space (e.g., sys_exec), which are
64 bits.
- trap.c: no special trap vector for sys calls, because x86-64 has a different
plan for system calls.
- trapasm: one plan for syscalls and one plan for traps (interrupt and
exceptions). On x86-64, the kernel is responsible for switching user/kernel
stacks. To do, xv6 keeps some scratch space in the cpu structure, and uses MSR
GS_KERN_BASE to point to the core's cpu structure (using swapgs).
- types.h: add uint64, and change pde_t to uint64
- usertests: exit() when fork fails, which helped in tracking down one of the
bugs in the switch from 32-bit to 64-bit
- vectors: update to make them 64 bits
- vm.c: use bootgdt in kernel too, program MSRs for syscalls and core-local
state (for swapgs), walk 4 levels in walkpgdir, add DEVSPACETOP, use task
segment to set kernel stack for interrupts (but simpler than in 32-bit mode),
add an extra argument to freevm (size of user part of address space) to avoid
checking all entries till KERNBASE (there are MANY TB before the top 1TB).
- x86: update trapframe to have 64-bit entries, which is what the processor
pushes on syscalls and traps. simplify lgdt and lidt, using struct desctr,
which needs the gcc directives packed and aligned.
TODO:
- use int32 instead of int?
- simplify curproc(). xv6 has per-cpu state again, but this time it must have it.
- avoid repetition in walkpgdir
- fix validateint() in usertests.c
- fix bugs (e.g., observed one a case of entering kernel with invalid gs or proc
2018-09-23 12:24:42 +00:00
|
|
|
|
2019-06-05 18:05:46 +00:00
|
|
|
// Switch h/w page table register to the kernel's page table,
|
|
|
|
// and enable paging.
|
|
|
|
void
|
|
|
|
kvminithart()
|
|
|
|
{
|
2019-05-31 13:45:59 +00:00
|
|
|
w_satp(MAKE_SATP(kernel_pagetable));
|
2010-09-02 20:23:15 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// Return the address of the PTE in page table pagetable
|
2011-08-16 00:21:14 +00:00
|
|
|
// that corresponds to virtual address va. If alloc!=0,
|
2010-08-05 16:10:54 +00:00
|
|
|
// create any required page table pages.
|
2019-05-31 13:45:59 +00:00
|
|
|
//
|
|
|
|
// The risc-v Sv39 scheme has three levels of page table
|
|
|
|
// pages. A page table page contains 512 64-bit PTEs.
|
|
|
|
// A 64-bit virtual address is split into five fields:
|
|
|
|
// 39..63 -- must be zero.
|
|
|
|
// 30..38 -- 9 bits of level-2 index.
|
|
|
|
// 21..39 -- 9 bits of level-1 index.
|
|
|
|
// 12..20 -- 9 bits of level-0 index.
|
|
|
|
// 0..12 -- 12 bits of byte offset within the page.
|
2010-07-23 16:52:35 +00:00
|
|
|
static pte_t *
|
2019-05-31 16:43:20 +00:00
|
|
|
walk(pagetable_t pagetable, uint64 va, int alloc)
|
2010-07-23 16:52:35 +00:00
|
|
|
{
|
2019-05-31 16:43:20 +00:00
|
|
|
if(va >= MAXVA)
|
2019-05-31 13:45:59 +00:00
|
|
|
panic("walk");
|
|
|
|
|
|
|
|
for(int level = 2; level > 0; level--) {
|
|
|
|
pte_t *pte = &pagetable[PX(level, va)];
|
|
|
|
if(*pte & PTE_V) {
|
|
|
|
pagetable = (pagetable_t)PTE2PA(*pte);
|
|
|
|
} else {
|
|
|
|
if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
|
2018-10-02 11:37:49 +00:00
|
|
|
return 0;
|
2019-05-31 13:45:59 +00:00
|
|
|
memset(pagetable, 0, PGSIZE);
|
|
|
|
*pte = PA2PTE(pagetable) | PTE_V;
|
2018-10-02 11:37:49 +00:00
|
|
|
}
|
Checkpoint port of xv6 to x86-64. Passed usertests on 2 processors a few times.
The x86-64 doesn't just add two levels to page tables to support 64 bit
addresses, but is a different processor. For example, calling conventions,
system calls, and segmentation are different from 32-bit x86. Segmentation is
basically gone, but gs/fs in combination with MSRs can be used to hold a
per-core pointer. In general, x86-64 is more straightforward than 32-bit
x86. The port uses code from sv6 and the xv6 "rsc-amd64" branch.
A summary of the changes is as follows:
- Booting: switch to grub instead of xv6's bootloader (pass -kernel to qemu),
because xv6's boot loader doesn't understand 64bit ELF files. And, we don't
care anymore about booting.
- Makefile: use -m64 instead of -m32 flag for gcc, delete boot loader, xv6.img,
bochs, and memfs. For now dont' use -O2, since usertests with -O2 is bigger than
MAXFILE!
- Update gdb.tmpl to be for i386 or x86-64
- Console/printf: use stdarg.h and treat 64-bit addresses different from ints
(32-bit)
- Update elfhdr to be 64 bit
- entry.S/entryother.S: add code to switch to 64-bit mode: build a simple page
table in 32-bit mode before switching to 64-bit mode, share code for entering
boot processor and APs, and tweak boot gdt. The boot gdt is the gdt that the
kernel proper also uses. (In 64-bit mode, the gdt/segmentation and task state
mostly disappear.)
- exec.c: fix passing argv (64-bit now instead of 32-bit).
- initcode.c: use syscall instead of int.
- kernel.ld: load kernel very high, in top terabyte. 64 bits is a lot of
address space!
- proc.c: initial return is through new syscall path instead of trapret.
- proc.h: update struct cpu to have some scratch space since syscall saves less
state than int, update struct context to reflect x86-64 calling conventions.
- swtch: simplify for x86-64 calling conventions.
- syscall: add fetcharg to handle x86-64 calling convetions (6 arguments are
passed through registers), and fetchaddr to read a 64-bit value from user space.
- sysfile: update to handle pointers from user space (e.g., sys_exec), which are
64 bits.
- trap.c: no special trap vector for sys calls, because x86-64 has a different
plan for system calls.
- trapasm: one plan for syscalls and one plan for traps (interrupt and
exceptions). On x86-64, the kernel is responsible for switching user/kernel
stacks. To do, xv6 keeps some scratch space in the cpu structure, and uses MSR
GS_KERN_BASE to point to the core's cpu structure (using swapgs).
- types.h: add uint64, and change pde_t to uint64
- usertests: exit() when fork fails, which helped in tracking down one of the
bugs in the switch from 32-bit to 64-bit
- vectors: update to make them 64 bits
- vm.c: use bootgdt in kernel too, program MSRs for syscalls and core-local
state (for swapgs), walk 4 levels in walkpgdir, add DEVSPACETOP, use task
segment to set kernel stack for interrupts (but simpler than in 32-bit mode),
add an extra argument to freevm (size of user part of address space) to avoid
checking all entries till KERNBASE (there are MANY TB before the top 1TB).
- x86: update trapframe to have 64-bit entries, which is what the processor
pushes on syscalls and traps. simplify lgdt and lidt, using struct desctr,
which needs the gcc directives packed and aligned.
TODO:
- use int32 instead of int?
- simplify curproc(). xv6 has per-cpu state again, but this time it must have it.
- avoid repetition in walkpgdir
- fix validateint() in usertests.c
- fix bugs (e.g., observed one a case of entering kernel with invalid gs or proc
2018-09-23 12:24:42 +00:00
|
|
|
}
|
2019-05-31 13:45:59 +00:00
|
|
|
return &pagetable[PX(0, va)];
|
2010-07-23 16:52:35 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 16:43:20 +00:00
|
|
|
// Look up a virtual address, return the physical address,
|
|
|
|
// Can only be used to look up user pages.
|
|
|
|
// or 0 if not mapped.
|
|
|
|
uint64
|
|
|
|
walkaddr(pagetable_t pagetable, uint64 va)
|
|
|
|
{
|
|
|
|
pte_t *pte;
|
|
|
|
uint64 pa;
|
|
|
|
|
|
|
|
pte = walk(pagetable, va, 0);
|
|
|
|
if(pte == 0)
|
|
|
|
return 0;
|
|
|
|
if((*pte & PTE_V) == 0)
|
|
|
|
return 0;
|
|
|
|
if((*pte & PTE_U) == 0)
|
|
|
|
return 0;
|
|
|
|
pa = PTE2PA(*pte);
|
|
|
|
return pa;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-08-16 19:47:22 +00:00
|
|
|
// Create PTEs for virtual addresses starting at va that refer to
|
|
|
|
// physical addresses starting at pa. va and size might not
|
2010-08-05 20:00:59 +00:00
|
|
|
// be page-aligned.
|
2019-05-31 13:45:59 +00:00
|
|
|
void
|
|
|
|
mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
|
2010-07-23 16:52:35 +00:00
|
|
|
{
|
2019-05-31 16:43:20 +00:00
|
|
|
uint64 a, last;
|
2011-01-11 18:01:13 +00:00
|
|
|
pte_t *pte;
|
2016-08-25 13:13:00 +00:00
|
|
|
|
2019-05-31 16:43:20 +00:00
|
|
|
a = PGROUNDDOWN(va);
|
|
|
|
last = PGROUNDDOWN(va + size - 1);
|
2011-01-11 18:01:13 +00:00
|
|
|
for(;;){
|
2019-05-31 13:45:59 +00:00
|
|
|
if((pte = walk(pagetable, a, 1)) == 0)
|
|
|
|
panic("mappages: walk");
|
|
|
|
if(*pte & PTE_V)
|
2010-08-05 16:10:54 +00:00
|
|
|
panic("remap");
|
2019-05-31 13:45:59 +00:00
|
|
|
*pte = PA2PTE(pa) | perm | PTE_V;
|
2010-08-05 20:00:59 +00:00
|
|
|
if(a == last)
|
|
|
|
break;
|
|
|
|
a += PGSIZE;
|
|
|
|
pa += PGSIZE;
|
2010-07-23 16:52:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// Remove mappings from a page table. The mappings in
|
|
|
|
// the given range must exist. Optionally free the
|
|
|
|
// physical memory.
|
|
|
|
void
|
|
|
|
unmappages(pagetable_t pagetable, uint64 va, uint64 size, int do_free)
|
2010-09-02 20:23:15 +00:00
|
|
|
{
|
2019-05-31 16:43:20 +00:00
|
|
|
uint64 a, last;
|
2019-05-31 13:45:59 +00:00
|
|
|
pte_t *pte;
|
|
|
|
uint64 pa;
|
2010-07-23 16:52:35 +00:00
|
|
|
|
2019-05-31 16:43:20 +00:00
|
|
|
a = PGROUNDDOWN(va);
|
|
|
|
last = PGROUNDDOWN(va + size - 1);
|
2019-05-31 13:45:59 +00:00
|
|
|
for(;;){
|
|
|
|
if((pte = walk(pagetable, a, 0)) == 0)
|
|
|
|
panic("unmappages: walk");
|
2019-06-04 15:31:50 +00:00
|
|
|
if((*pte & PTE_V) == 0){
|
|
|
|
printf("va=%p pte=%p\n", a, *pte);
|
2019-05-31 13:45:59 +00:00
|
|
|
panic("unmappages: not mapped");
|
2019-06-04 15:31:50 +00:00
|
|
|
}
|
2019-05-31 13:45:59 +00:00
|
|
|
if(PTE_FLAGS(*pte) == PTE_V)
|
|
|
|
panic("unmappages: not a leaf");
|
|
|
|
if(do_free){
|
|
|
|
pa = PTE2PA(*pte);
|
|
|
|
kfree((void*)pa);
|
2017-02-05 10:44:56 +00:00
|
|
|
}
|
2019-05-31 13:45:59 +00:00
|
|
|
*pte = 0;
|
|
|
|
if(a == last)
|
|
|
|
break;
|
|
|
|
a += PGSIZE;
|
|
|
|
pa += PGSIZE;
|
Checkpoint port of xv6 to x86-64. Passed usertests on 2 processors a few times.
The x86-64 doesn't just add two levels to page tables to support 64 bit
addresses, but is a different processor. For example, calling conventions,
system calls, and segmentation are different from 32-bit x86. Segmentation is
basically gone, but gs/fs in combination with MSRs can be used to hold a
per-core pointer. In general, x86-64 is more straightforward than 32-bit
x86. The port uses code from sv6 and the xv6 "rsc-amd64" branch.
A summary of the changes is as follows:
- Booting: switch to grub instead of xv6's bootloader (pass -kernel to qemu),
because xv6's boot loader doesn't understand 64bit ELF files. And, we don't
care anymore about booting.
- Makefile: use -m64 instead of -m32 flag for gcc, delete boot loader, xv6.img,
bochs, and memfs. For now dont' use -O2, since usertests with -O2 is bigger than
MAXFILE!
- Update gdb.tmpl to be for i386 or x86-64
- Console/printf: use stdarg.h and treat 64-bit addresses different from ints
(32-bit)
- Update elfhdr to be 64 bit
- entry.S/entryother.S: add code to switch to 64-bit mode: build a simple page
table in 32-bit mode before switching to 64-bit mode, share code for entering
boot processor and APs, and tweak boot gdt. The boot gdt is the gdt that the
kernel proper also uses. (In 64-bit mode, the gdt/segmentation and task state
mostly disappear.)
- exec.c: fix passing argv (64-bit now instead of 32-bit).
- initcode.c: use syscall instead of int.
- kernel.ld: load kernel very high, in top terabyte. 64 bits is a lot of
address space!
- proc.c: initial return is through new syscall path instead of trapret.
- proc.h: update struct cpu to have some scratch space since syscall saves less
state than int, update struct context to reflect x86-64 calling conventions.
- swtch: simplify for x86-64 calling conventions.
- syscall: add fetcharg to handle x86-64 calling convetions (6 arguments are
passed through registers), and fetchaddr to read a 64-bit value from user space.
- sysfile: update to handle pointers from user space (e.g., sys_exec), which are
64 bits.
- trap.c: no special trap vector for sys calls, because x86-64 has a different
plan for system calls.
- trapasm: one plan for syscalls and one plan for traps (interrupt and
exceptions). On x86-64, the kernel is responsible for switching user/kernel
stacks. To do, xv6 keeps some scratch space in the cpu structure, and uses MSR
GS_KERN_BASE to point to the core's cpu structure (using swapgs).
- types.h: add uint64, and change pde_t to uint64
- usertests: exit() when fork fails, which helped in tracking down one of the
bugs in the switch from 32-bit to 64-bit
- vectors: update to make them 64 bits
- vm.c: use bootgdt in kernel too, program MSRs for syscalls and core-local
state (for swapgs), walk 4 levels in walkpgdir, add DEVSPACETOP, use task
segment to set kernel stack for interrupts (but simpler than in 32-bit mode),
add an extra argument to freevm (size of user part of address space) to avoid
checking all entries till KERNBASE (there are MANY TB before the top 1TB).
- x86: update trapframe to have 64-bit entries, which is what the processor
pushes on syscalls and traps. simplify lgdt and lidt, using struct desctr,
which needs the gcc directives packed and aligned.
TODO:
- use int32 instead of int?
- simplify curproc(). xv6 has per-cpu state again, but this time it must have it.
- avoid repetition in walkpgdir
- fix validateint() in usertests.c
- fix bugs (e.g., observed one a case of entering kernel with invalid gs or proc
2018-09-23 12:24:42 +00:00
|
|
|
}
|
2011-07-29 11:31:27 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// create an empty user page table.
|
|
|
|
pagetable_t
|
|
|
|
uvmcreate()
|
2010-09-02 20:23:15 +00:00
|
|
|
{
|
2019-05-31 13:45:59 +00:00
|
|
|
pagetable_t pagetable;
|
|
|
|
pagetable = (pagetable_t) kalloc();
|
|
|
|
if(pagetable == 0)
|
|
|
|
panic("uvmcreate: out of memory");
|
|
|
|
memset(pagetable, 0, PGSIZE);
|
|
|
|
return pagetable;
|
2010-07-23 16:52:35 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// Load the user initcode into address 0 of pagetable,
|
|
|
|
// for the very first process.
|
2010-09-02 20:39:55 +00:00
|
|
|
// sz must be less than a page.
|
2010-09-02 20:23:15 +00:00
|
|
|
void
|
2019-06-05 18:31:13 +00:00
|
|
|
uvminit(pagetable_t pagetable, uchar *src, uint sz)
|
2010-09-02 20:23:15 +00:00
|
|
|
{
|
2011-01-11 18:01:13 +00:00
|
|
|
char *mem;
|
2016-08-25 13:13:00 +00:00
|
|
|
|
2011-01-11 18:01:13 +00:00
|
|
|
if(sz >= PGSIZE)
|
2010-09-02 20:23:15 +00:00
|
|
|
panic("inituvm: more than a page");
|
2011-01-11 18:01:13 +00:00
|
|
|
mem = kalloc();
|
2010-09-02 20:23:15 +00:00
|
|
|
memset(mem, 0, PGSIZE);
|
2019-05-31 13:45:59 +00:00
|
|
|
mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
|
|
|
|
memmove(mem, src, sz);
|
2010-07-23 16:52:35 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 16:43:20 +00:00
|
|
|
// Allocate PTEs and physical memory to grow process from oldsz to
|
|
|
|
// newsz, which need not be page aligned. Returns new size or 0 on error.
|
|
|
|
uint64
|
|
|
|
uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
|
|
|
|
{
|
|
|
|
char *mem;
|
|
|
|
uint64 a;
|
|
|
|
|
|
|
|
if(newsz < oldsz)
|
|
|
|
return oldsz;
|
|
|
|
|
2019-06-04 15:31:50 +00:00
|
|
|
oldsz = PGROUNDUP(oldsz);
|
|
|
|
a = oldsz;
|
2019-05-31 16:43:20 +00:00
|
|
|
for(; a < newsz; a += PGSIZE){
|
|
|
|
mem = kalloc();
|
|
|
|
if(mem == 0){
|
2019-06-04 14:43:45 +00:00
|
|
|
uvmdealloc(pagetable, a, oldsz);
|
2019-05-31 16:43:20 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
memset(mem, 0, PGSIZE);
|
2019-06-01 09:33:38 +00:00
|
|
|
mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U);
|
2019-05-31 16:43:20 +00:00
|
|
|
}
|
|
|
|
return newsz;
|
|
|
|
}
|
|
|
|
|
2010-09-02 22:28:36 +00:00
|
|
|
// Deallocate user pages to bring the process size from oldsz to
|
|
|
|
// newsz. oldsz and newsz need not be page-aligned, nor does newsz
|
|
|
|
// need to be less than oldsz. oldsz can be larger than the actual
|
|
|
|
// process size. Returns the new process size.
|
2019-05-31 16:43:20 +00:00
|
|
|
uint64
|
2019-05-31 13:45:59 +00:00
|
|
|
uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
|
2010-08-10 21:08:41 +00:00
|
|
|
{
|
2011-01-11 18:01:13 +00:00
|
|
|
if(newsz >= oldsz)
|
|
|
|
return oldsz;
|
2019-05-31 13:45:59 +00:00
|
|
|
unmappages(pagetable, newsz, oldsz - newsz, 1);
|
2011-01-11 18:01:13 +00:00
|
|
|
return newsz;
|
2010-08-10 21:08:41 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// Recursively free page table pages.
|
|
|
|
// All leaf mappings must already have been removed.
|
|
|
|
static void
|
|
|
|
freewalk(pagetable_t pagetable)
|
|
|
|
{
|
|
|
|
// there are 2^9 = 512 PTEs in a page table.
|
|
|
|
for(int i = 0; i < 512; i++){
|
|
|
|
pte_t pte = pagetable[i];
|
|
|
|
if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
|
|
|
|
// this PTE points to a lower-level page table.
|
|
|
|
uint64 child = PTE2PA(pte);
|
|
|
|
freewalk((pagetable_t)child);
|
|
|
|
pagetable[i] = 0;
|
|
|
|
} else if(pte & PTE_V){
|
|
|
|
panic("freewalk: leaf");
|
2018-10-02 12:12:01 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-31 13:45:59 +00:00
|
|
|
kfree((void*)pagetable);
|
2018-10-02 12:12:01 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// Free user memory pages,
|
|
|
|
// then free page table pages.
|
2010-07-23 16:52:35 +00:00
|
|
|
void
|
2019-05-31 13:45:59 +00:00
|
|
|
uvmfree(pagetable_t pagetable, uint64 sz)
|
2010-07-23 16:52:35 +00:00
|
|
|
{
|
2019-05-31 13:45:59 +00:00
|
|
|
unmappages(pagetable, 0, sz, 1);
|
|
|
|
freewalk(pagetable);
|
2010-07-23 16:52:35 +00:00
|
|
|
}
|
|
|
|
|
2019-05-31 13:45:59 +00:00
|
|
|
// Given a parent process's page table, copy
|
|
|
|
// its memory into a child's page table.
|
|
|
|
// Copies both the page table and the
|
|
|
|
// physical memory.
|
2011-09-02 18:35:58 +00:00
|
|
|
void
|
2019-05-31 13:45:59 +00:00
|
|
|
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
|
2010-07-23 16:52:35 +00:00
|
|
|
{
|
|
|
|
pte_t *pte;
|
Checkpoint port of xv6 to x86-64. Passed usertests on 2 processors a few times.
The x86-64 doesn't just add two levels to page tables to support 64 bit
addresses, but is a different processor. For example, calling conventions,
system calls, and segmentation are different from 32-bit x86. Segmentation is
basically gone, but gs/fs in combination with MSRs can be used to hold a
per-core pointer. In general, x86-64 is more straightforward than 32-bit
x86. The port uses code from sv6 and the xv6 "rsc-amd64" branch.
A summary of the changes is as follows:
- Booting: switch to grub instead of xv6's bootloader (pass -kernel to qemu),
because xv6's boot loader doesn't understand 64bit ELF files. And, we don't
care anymore about booting.
- Makefile: use -m64 instead of -m32 flag for gcc, delete boot loader, xv6.img,
bochs, and memfs. For now dont' use -O2, since usertests with -O2 is bigger than
MAXFILE!
- Update gdb.tmpl to be for i386 or x86-64
- Console/printf: use stdarg.h and treat 64-bit addresses different from ints
(32-bit)
- Update elfhdr to be 64 bit
- entry.S/entryother.S: add code to switch to 64-bit mode: build a simple page
table in 32-bit mode before switching to 64-bit mode, share code for entering
boot processor and APs, and tweak boot gdt. The boot gdt is the gdt that the
kernel proper also uses. (In 64-bit mode, the gdt/segmentation and task state
mostly disappear.)
- exec.c: fix passing argv (64-bit now instead of 32-bit).
- initcode.c: use syscall instead of int.
- kernel.ld: load kernel very high, in top terabyte. 64 bits is a lot of
address space!
- proc.c: initial return is through new syscall path instead of trapret.
- proc.h: update struct cpu to have some scratch space since syscall saves less
state than int, update struct context to reflect x86-64 calling conventions.
- swtch: simplify for x86-64 calling conventions.
- syscall: add fetcharg to handle x86-64 calling convetions (6 arguments are
passed through registers), and fetchaddr to read a 64-bit value from user space.
- sysfile: update to handle pointers from user space (e.g., sys_exec), which are
64 bits.
- trap.c: no special trap vector for sys calls, because x86-64 has a different
plan for system calls.
- trapasm: one plan for syscalls and one plan for traps (interrupt and
exceptions). On x86-64, the kernel is responsible for switching user/kernel
stacks. To do, xv6 keeps some scratch space in the cpu structure, and uses MSR
GS_KERN_BASE to point to the core's cpu structure (using swapgs).
- types.h: add uint64, and change pde_t to uint64
- usertests: exit() when fork fails, which helped in tracking down one of the
bugs in the switch from 32-bit to 64-bit
- vectors: update to make them 64 bits
- vm.c: use bootgdt in kernel too, program MSRs for syscalls and core-local
state (for swapgs), walk 4 levels in walkpgdir, add DEVSPACETOP, use task
segment to set kernel stack for interrupts (but simpler than in 32-bit mode),
add an extra argument to freevm (size of user part of address space) to avoid
checking all entries till KERNBASE (there are MANY TB before the top 1TB).
- x86: update trapframe to have 64-bit entries, which is what the processor
pushes on syscalls and traps. simplify lgdt and lidt, using struct desctr,
which needs the gcc directives packed and aligned.
TODO:
- use int32 instead of int?
- simplify curproc(). xv6 has per-cpu state again, but this time it must have it.
- avoid repetition in walkpgdir
- fix validateint() in usertests.c
- fix bugs (e.g., observed one a case of entering kernel with invalid gs or proc
2018-09-23 12:24:42 +00:00
|
|
|
uint64 pa, i;
|
|
|
|
uint flags;
|
2010-07-23 16:52:35 +00:00
|
|
|
char *mem;
|
|
|
|
|
2010-09-01 04:41:25 +00:00
|
|
|
for(i = 0; i < sz; i += PGSIZE){
|
2019-05-31 16:43:20 +00:00
|
|
|
if((pte = walk(old, i, 0)) == 0)
|
2011-02-20 02:17:55 +00:00
|
|
|
panic("copyuvm: pte should exist");
|
2019-05-31 13:45:59 +00:00
|
|
|
if((*pte & PTE_V) == 0)
|
2011-02-20 02:17:55 +00:00
|
|
|
panic("copyuvm: page not present");
|
2019-05-31 13:45:59 +00:00
|
|
|
pa = PTE2PA(*pte);
|
2013-03-04 21:16:54 +00:00
|
|
|
flags = PTE_FLAGS(*pte);
|
2011-01-11 18:01:13 +00:00
|
|
|
if((mem = kalloc()) == 0)
|
2019-05-31 13:45:59 +00:00
|
|
|
panic("uvmcopy: kalloc failed");
|
|
|
|
memmove(mem, (char*)pa, PGSIZE);
|
|
|
|
mappages(new, i, PGSIZE, (uint64)mem, flags);
|
2010-07-23 16:52:35 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-31 16:43:20 +00:00
|
|
|
|
2019-06-01 09:33:38 +00:00
|
|
|
// Copy from kernel to user.
|
2019-05-31 16:43:20 +00:00
|
|
|
// Copy len bytes from src to virtual address dstva in a given page table.
|
|
|
|
// Return 0 on success, -1 on error.
|
|
|
|
int
|
|
|
|
copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
|
|
|
|
{
|
|
|
|
uint64 n, va0, pa0;
|
|
|
|
|
|
|
|
while(len > 0){
|
|
|
|
va0 = (uint)PGROUNDDOWN(dstva);
|
|
|
|
pa0 = walkaddr(pagetable, va0);
|
|
|
|
if(pa0 == 0)
|
|
|
|
return -1;
|
|
|
|
n = PGSIZE - (dstva - va0);
|
|
|
|
if(n > len)
|
|
|
|
n = len;
|
|
|
|
memmove((void *)(pa0 + (dstva - va0)), src, n);
|
|
|
|
|
|
|
|
len -= n;
|
|
|
|
src += n;
|
|
|
|
dstva = va0 + PGSIZE;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2019-06-01 09:33:38 +00:00
|
|
|
|
|
|
|
// Copy from user to kernel.
|
|
|
|
// Copy len bytes to dst from virtual address srcva in a given page table.
|
|
|
|
// Return 0 on success, -1 on error.
|
|
|
|
int
|
|
|
|
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
|
|
|
|
{
|
|
|
|
uint64 n, va0, pa0;
|
|
|
|
|
|
|
|
while(len > 0){
|
|
|
|
va0 = (uint)PGROUNDDOWN(srcva);
|
|
|
|
pa0 = walkaddr(pagetable, va0);
|
|
|
|
if(pa0 == 0)
|
|
|
|
return -1;
|
|
|
|
n = PGSIZE - (srcva - va0);
|
|
|
|
if(n > len)
|
|
|
|
n = len;
|
|
|
|
memmove(dst, (void *)(pa0 + (srcva - va0)), n);
|
|
|
|
|
|
|
|
len -= n;
|
|
|
|
dst += n;
|
|
|
|
srcva = va0 + PGSIZE;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-06-04 09:57:47 +00:00
|
|
|
// Copy a null-terminated string from user to kernel.
|
2019-06-01 09:33:38 +00:00
|
|
|
// Copy bytes to dst from virtual address srcva in a given page table,
|
|
|
|
// until a '\0', or max.
|
|
|
|
// Return 0 on success, -1 on error.
|
|
|
|
int
|
|
|
|
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
|
|
|
|
{
|
|
|
|
uint64 n, va0, pa0;
|
|
|
|
int got_null = 0;
|
|
|
|
|
|
|
|
while(got_null == 0 && max > 0){
|
|
|
|
va0 = (uint)PGROUNDDOWN(srcva);
|
|
|
|
pa0 = walkaddr(pagetable, va0);
|
|
|
|
if(pa0 == 0)
|
|
|
|
return -1;
|
|
|
|
n = PGSIZE - (srcva - va0);
|
|
|
|
if(n > max)
|
|
|
|
n = max;
|
|
|
|
|
|
|
|
char *p = (char *) (pa0 + (srcva - va0));
|
|
|
|
while(n > 0){
|
|
|
|
if(*p == '\0'){
|
|
|
|
*dst = '\0';
|
|
|
|
got_null = 1;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
*dst = *p;
|
|
|
|
}
|
|
|
|
--n;
|
|
|
|
--max;
|
|
|
|
p++;
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
|
|
|
|
srcva = va0 + PGSIZE;
|
|
|
|
}
|
|
|
|
if(got_null){
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|