Kernel runs in the higher address space

This commit is contained in:
Taku Shimosawa
2011-11-07 00:48:22 +09:00
parent 83a17650b9
commit 2773606466
11 changed files with 224 additions and 6 deletions

27
kboot/Makefile Normal file
View File

@ -0,0 +1,27 @@
DEST=$(O)/kboot
OBJS=$(DEST)/main.o $(DEST)/data.o
CFLAGS=-mno-sse -mno-mmx -mno-sse2 -mno-3dnow
$(if $(O),,$(error Specify the target directory))
$(DEST)/kboot.elf: $(DEST) $(DEST)/kernel.lds $(OBJS)
@$(LD) $(LDFLAGS) -o $@ -T $(DEST)/kernel.lds -nostdlib $(OBJS)
$(DEST)/%.o: %.c
@$(CC) $(CFLAGS) -c -o $@ -O3 $<
$(DEST)/data.o: data.S
@$(CC) -c -o $@ -O3 -DKIMAGE='"$(KIMAGE)"' $^
$(DEST)/kernel.lds: kernel.lds.S
$(if $(LOAD_PA),,$(error Specify the loading physical address))
@$(CC) -E -P -DLOAD_PA=$(LOAD_PA) -o $@ $<
$(DEST):
@mkdir -p $(DEST)
clean:
@$(RM) $(DEST)/*
.PHONY: clean

9
kboot/data.S Normal file
View File

@ -0,0 +1,9 @@
#ifndef KIMAGE
#error "No kernel image is specified"
#endif
.data
.globl data_start
data_start:
.incbin KIMAGE

34
kboot/kernel.lds.S Normal file
View File

@ -0,0 +1,34 @@
ENTRY(main)
PHDRS
{
text PT_LOAD FLAGS(5);
data PT_LOAD FLAGS(7);
}
SECTIONS
{
. = LOAD_PA;
_head = .;
.text : {
*(.text);
} : text
. = ALIGN(4096);
.data : {
*(.data)
*(.data.*)
} :data
.rodata : {
*(.rodata .rodata.*)
} :data
.bss : {
*(.bss .bss.*)
}
. = ALIGN(4096);
data_end = .;
/DISCARD/ : {
*(.eh_frame)
*(.note.gnu.build-id)
}
}

132
kboot/main.c Normal file
View File

@ -0,0 +1,132 @@
#include <elf.h>
extern char data_start[], data_end[];
#define LARGE_PAGE_SIZE (1UL << 21)
#define LARGE_PAGE_MASK (~((unsigned long)LARGE_PAGE_SIZE - 1))
#define MAP_ST_START 0xffff800000000000UL
#define MAP_KERNEL_START 0xffffffff80000000UL
#define PTL4_SHIFT 39
#define PTL3_SHIFT 30
#define PTL2_SHIFT 21
unsigned long page_tables[3][512] __attribute__((aligned(4096)));
static void *memcpy(void *dest, void *src, unsigned long len)
{
char *d = dest, *s = src;
for ( ; len ; len--) {
*(d++) = *(s++);
}
return d;
}
static void *memset(void *dest, int v, unsigned long len)
{
char *d = dest;
for ( ; len ; len--) {
*(d++) = (char)v;
}
return d;
}
void memzerol(unsigned long *p, unsigned long size)
{
unsigned long i;
size /= sizeof(unsigned long);
for (i = 0; i < size; i++) {
p[i] = 0;
}
}
static unsigned long load_programs(unsigned char *image, Elf64_Phdr *hdrs,
int nhdr, unsigned long offset)
{
int i;
unsigned long end = MAP_KERNEL_START;
for (i = 0; i < nhdr; i++) {
if (hdrs[i].p_type == PT_LOAD) {
memcpy((void *)(hdrs[i].p_vaddr - offset),
image + hdrs[i].p_offset,
hdrs[i].p_filesz);
if (hdrs[i].p_filesz < hdrs[i].p_memsz) {
memset((void *)(hdrs[i].p_vaddr +
hdrs[i].p_filesz - offset), 0,
hdrs[i].p_memsz - hdrs[i].p_filesz);
}
if (end < hdrs[i].p_vaddr + hdrs[i].p_memsz) {
end = hdrs[i].p_vaddr + hdrs[i].p_memsz;
}
}
}
return end;
}
/*
* Return value: If success, the entry point address. Otherwise, 0.
*/
unsigned long load_elf(unsigned char *image, unsigned long offset)
{
Elf64_Ehdr *hdr = (Elf64_Ehdr *)image;
if (hdr->e_ident[0] != 0x7f || hdr->e_ident[1] != 'E'
|| hdr->e_ident[2] != 'L' || hdr->e_ident[3] != 'F') {
return 0;
}
/* TODO: We may overlap. So copying should be more sophisticated */
if (!hdr->e_phoff || hdr->e_phentsize != sizeof(Elf64_Phdr)) {
return 0;
}
return load_programs(image,
(Elf64_Phdr *)(image + hdr->e_phoff), hdr->e_phnum,
offset);
}
void main(unsigned long param)
{
/* Assume phys == virt */
unsigned long load_address, end, *org_cr3;
unsigned long i, n;
Elf64_Ehdr *hdr;
void (*entry)(unsigned long param, unsigned long load_address);
load_address = (unsigned long)data_end;
load_address = (load_address + LARGE_PAGE_SIZE - 1) & LARGE_PAGE_MASK;
asm volatile("movq %%cr3, %0" : "=r"(org_cr3));
memzerol((unsigned long *)page_tables, sizeof(page_tables));
page_tables[0][0] = org_cr3[0];
page_tables[0][(MAP_ST_START >> PTL4_SHIFT) & 511] = org_cr3[0];
page_tables[0][(MAP_KERNEL_START >> PTL4_SHIFT) & 511] =
((unsigned long)page_tables[1]) | 3;
page_tables[1][(MAP_KERNEL_START >> PTL3_SHIFT) & 511] =
((unsigned long)page_tables[2]) | 3;
end = load_elf(data_start, MAP_KERNEL_START - load_address);
/* map 4MB more in case */
n = (end - MAP_KERNEL_START + (1 << PTL2_SHIFT) - 1) >> PTL2_SHIFT;
n += 2;
for (i = 0; i < n; i++) {
page_tables[2][i] = (load_address + (i << PTL2_SHIFT)) | 0x83;
}
hdr = (Elf64_Ehdr *)data_start;
asm volatile("movq %0, %%cr3" : : "r"(page_tables) : "memory");
entry = (void *)hdr->e_entry;
entry(param, load_address);
}