Ported linux to relocator framework

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-01-11 14:59:01 +01:00
parent cb1b2ad7e0
commit 55b40bc68a
5 changed files with 83 additions and 78 deletions

View File

@ -31,6 +31,7 @@ struct grub_relocator32_state
grub_uint32_t ecx;
grub_uint32_t edx;
grub_uint32_t eip;
grub_uint32_t esi;
};
grub_err_t grub_relocator32_boot (struct grub_relocator *rel,

View File

@ -47,6 +47,7 @@ extern grub_uint32_t grub_relocator32_ecx;
extern grub_uint32_t grub_relocator32_edx;
extern grub_uint32_t grub_relocator32_eip;
extern grub_uint32_t grub_relocator32_esp;
extern grub_uint32_t grub_relocator32_esi;
#define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start)
@ -126,6 +127,7 @@ grub_relocator32_boot (struct grub_relocator *rel,
grub_relocator32_edx = state.edx;
grub_relocator32_eip = state.eip;
grub_relocator32_esp = state.esp;
grub_relocator32_esi = state.esi;
grub_memmove (src, &grub_relocator32_start, RELOCATOR_SIZEOF (32));

View File

@ -93,6 +93,13 @@ VARIABLE(grub_relocator32_esp)
movl %eax, %esp
/* mov imm32, %eax */
.byte 0xb8
VARIABLE(grub_relocator32_esi)
.long 0
movl %eax, %esi
/* mov imm32, %eax */
.byte 0xb8
VARIABLE(grub_relocator32_eax)

View File

@ -99,6 +99,7 @@ get_best_header (struct grub_relocator *rel,
hb = h;
hbp = hp;
*best_addr = addr;
grub_dprintf ("relocator", "picked %p/%x\n", hb, addr);
}
}
else
@ -141,6 +142,7 @@ get_best_header (struct grub_relocator *rel,
hb = h;
hbp = hp;
*best_addr = addr;
grub_dprintf ("relocator", "picked %p/%x\n", hb, addr);
}
}
}
@ -188,8 +190,12 @@ malloc_in_range (struct grub_relocator *rel,
grub_mm_region_t r, rp;
for (rp = NULL, r = grub_mm_base; r; rp = r, r = r->next)
{
if ((grub_addr_t) r + r->size + sizeof (*r) > start
&& (grub_addr_t) r <= end && r->size + sizeof (*r) >= size
grub_dprintf ("relocator", "region %p. %d %d %d\n", r,
(grub_addr_t) r + r->size + sizeof (*r) >= start,
(grub_addr_t) r < end && r->size + sizeof (*r) >= size,
(rb == NULL || (from_low_priv ? rb > r : rb < r)));
if ((grub_addr_t) r + r->size + sizeof (*r) >= start
&& (grub_addr_t) r < end && r->size + sizeof (*r) >= size
&& (rb == NULL || (from_low_priv ? rb > r : rb < r)))
{
rb = r;
@ -221,7 +227,7 @@ malloc_in_range (struct grub_relocator *rel,
}
/* Special case: relocating region start. */
if (best_addr < (grub_addr_t) hbp)
if (best_addr < (grub_addr_t) hb)
{
grub_addr_t newreg_start, newreg_raw_start = best_addr + size;
grub_addr_t newreg_size, newreg_presize;
@ -320,7 +326,8 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
/* Keep chunks in memory in the same order as they'll be after relocation. */
for (chunk = rel->chunks; chunk; chunk = chunk->next)
{
if (chunk->target > target && chunk->src > max_addr)
if (chunk->target > target && chunk->src < max_addr
&& chunk->src < rel->postchunks)
max_addr = chunk->src;
if (chunk->target + chunk->size <= target
&& chunk->src + chunk->size > min_addr
@ -345,10 +352,10 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
/* A trick to improve Linux allocation. */
#if defined (__i386__) || defined (__x86_64__)
if (target < 0x100000)
if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 0,
size, &start, 1, 0))
if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
size, &start, 0, 1))
{
if (rel->postchunks < start)
if (rel->postchunks > start)
rel->postchunks = start;
break;
}
@ -356,7 +363,7 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
if (malloc_in_range (rel, target, max_addr, 1, size, &start, 1, 0))
break;
if (malloc_in_range (rel, min_addr, target, 0, size, &start, 1, 0))
if (malloc_in_range (rel, min_addr, target, 1, size, &start, 0, 0))
break;
grub_dprintf ("relocator", "not allocated\n");
@ -436,7 +443,8 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
relocation. */
for (chunk = rel->chunks; chunk; chunk = chunk->next)
{
if (chunk->target > max_addr && chunk->src > max_addr2)
if (chunk->target > max_addr && chunk->src > max_addr2
&& chunk->src < rel->postchunks)
max_addr2 = chunk->src;
if (chunk->target + chunk->size <= min_addr
&& chunk->src + chunk->size < min_addr2

View File

@ -34,6 +34,7 @@
#include <grub/command.h>
#include <grub/i386/pc/vbe.h>
#include <grub/i386/pc/console.h>
#include <grub/i386/relocator.h>
#include <grub/i18n.h>
#define GRUB_LINUX_CL_OFFSET 0x1000
@ -44,36 +45,18 @@ static grub_dl_t my_mod;
static grub_size_t linux_mem_size;
static int loaded;
static void *real_mode_mem;
static grub_addr_t real_mode_target;
static void *prot_mode_mem;
static grub_addr_t prot_mode_target;
static void *initrd_mem;
static grub_addr_t initrd_mem_target;
static grub_uint32_t real_mode_pages;
static grub_uint32_t prot_mode_pages;
static grub_uint32_t initrd_pages;
static struct grub_relocator *relocator = NULL;
static grub_uint8_t gdt[] __attribute__ ((aligned(16))) =
{
/* NULL. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Reserved. */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* Code segment. */
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00,
/* Data segment. */
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
};
struct gdt_descriptor
{
grub_uint16_t limit;
void *base;
} __attribute__ ((packed));
static struct gdt_descriptor gdt_desc =
{
sizeof (gdt) - 1,
gdt
};
/* FIXME */
#if 0
struct idt_descriptor
{
grub_uint16_t limit;
@ -85,6 +68,7 @@ static struct idt_descriptor idt_desc =
0,
0
};
#endif
#ifdef GRUB_MACHINE_PCBIOS
struct linux_vesafb_res
@ -290,15 +274,19 @@ find_mmap_size (void)
static void
free_pages (void)
{
grub_relocator_unload (relocator);
relocator = NULL;
real_mode_mem = prot_mode_mem = initrd_mem = 0;
real_mode_target = prot_mode_target = initrd_mem_target = 0;
}
/* Allocate pages for the real mode code and the protected mode code
for linux as well as a memory map buffer. */
static int
static grub_err_t
allocate_pages (grub_size_t prot_size)
{
grub_size_t real_size, mmap_size;
grub_err_t err;
/* Make sure that each size is aligned to a page boundary. */
real_size = GRUB_LINUX_CL_END_OFFSET;
@ -316,6 +304,13 @@ allocate_pages (grub_size_t prot_size)
/* Initialize the memory pointers with NULL for convenience. */
free_pages ();
relocator = grub_relocator_new ();
if (!relocator)
{
err = grub_errno;
goto fail;
}
/* FIXME: Should request low memory from the heap when this feature is
implemented. */
@ -339,32 +334,42 @@ allocate_pages (grub_size_t prot_size)
if (real_size + mmap_size > size)
return 0;
real_mode_mem =
(void *) (grub_size_t) ((addr + size) - (real_size + mmap_size));
real_mode_target = ((addr + size) - (real_size + mmap_size));
return 1;
}
return 0;
}
grub_mmap_iterate (hook);
if (! real_mode_mem)
if (! real_mode_target)
{
grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
goto fail;
}
prot_mode_mem = (void *) 0x100000;
err = grub_relocator_alloc_chunk_addr (relocator, &real_mode_mem,
real_mode_target,
(real_size + mmap_size));
if (err)
goto fail;
prot_mode_target = 0x100000;
err = grub_relocator_alloc_chunk_addr (relocator, &prot_mode_mem,
prot_mode_target, prot_size);
if (err)
goto fail;
grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
"prot_mode_mem = %lx, prot_mode_pages = %x\n",
(unsigned long) real_mode_mem, (unsigned) real_mode_pages,
(unsigned long) prot_mode_mem, (unsigned) prot_mode_pages);
return 1;
return GRUB_ERR_NONE;
fail:
free_pages ();
return 0;
return err;
}
static void
@ -460,16 +465,12 @@ grub_linux_boot (void)
int e820_num;
grub_err_t err = 0;
char *modevar, *tmp;
struct grub_relocator32_state state;
params = real_mode_mem;
grub_dprintf ("linux", "code32_start = %x, idt_desc = %lx, gdt_desc = %lx\n",
(unsigned) params->code32_start,
(unsigned long) &(idt_desc.limit),
(unsigned long) &(gdt_desc.limit));
grub_dprintf ("linux", "idt = %x:%lx, gdt = %x:%lx\n",
(unsigned) idt_desc.limit, (unsigned long) idt_desc.base,
(unsigned) gdt_desc.limit, (unsigned long) gdt_desc.base);
grub_dprintf ("linux", "code32_start = %x\n",
(unsigned) params->code32_start);
auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type)
@ -568,31 +569,12 @@ grub_linux_boot (void)
}
}
#ifdef __x86_64__
grub_memcpy ((char *) prot_mode_mem + (prot_mode_pages << 12),
grub_linux_trampoline_start,
grub_linux_trampoline_end - grub_linux_trampoline_start);
((void (*) (unsigned long, void *)) ((char *) prot_mode_mem
+ (prot_mode_pages << 12)))
(params->code32_start, real_mode_mem);
#else
/* Hardware interrupts are not safe any longer. */
asm volatile ("cli" : : );
/* Load the IDT and the GDT for the bootstrap. */
asm volatile ("lidt %0" : : "m" (idt_desc));
asm volatile ("lgdt %0" : : "m" (gdt_desc));
/* Enter Linux. */
asm volatile ("jmp *%2" : : "b" (0), "S" (real_mode_mem), "g" (params->code32_start));
#endif
/* Never reach here. */
return GRUB_ERR_NONE;
/* FIXME. */
/* asm volatile ("lidt %0" : : "m" (idt_desc)); */
state.ebx = 0;
state.esi = real_mode_target;
state.eip = params->code32_start;
return grub_relocator32_boot (relocator, state);
}
static grub_err_t
@ -678,7 +660,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
if (! allocate_pages (prot_size))
if (allocate_pages (prot_size))
goto fail;
params = (struct linux_kernel_params *) real_mode_mem;
@ -701,7 +683,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
params->cl_magic = GRUB_LINUX_CL_MAGIC;
params->cl_offset = 0x1000;
params->cmd_line_ptr = (unsigned long) real_mode_mem + 0x1000;
params->cmd_line_ptr = real_mode_target + 0x1000;
params->ramdisk_image = 0;
params->ramdisk_size = 0;
@ -910,6 +892,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
grub_ssize_t size;
grub_addr_t addr_min, addr_max;
grub_addr_t addr;
grub_err_t err;
struct linux_kernel_header *lh;
if (argc == 0)
@ -957,7 +940,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
addr_max -= 0x10000;
/* Usually, the compression ratio is about 50%. */
addr_min = (grub_addr_t) prot_mode_mem + ((prot_mode_pages * 3) << 12)
addr_min = (grub_addr_t) prot_mode_target + ((prot_mode_pages * 3) << 12)
+ page_align (size);
if (addr_max > grub_os_area_addr + grub_os_area_size)
@ -972,7 +955,11 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
goto fail;
}
initrd_mem = (void *) addr;
err = grub_relocator_alloc_chunk_align (relocator, &initrd_mem,
&initrd_mem_target,
addr_min, addr, size, 0x1000);
if (err)
return err;
if (grub_file_read (file, initrd_mem, size) != size)
{
@ -983,7 +970,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
(unsigned) addr, (unsigned) size);
lh->ramdisk_image = addr;
lh->ramdisk_image = initrd_mem_target;
lh->ramdisk_size = size;
lh->root_dev = 0x0100; /* XXX */