kvm: Use proper segment + offset in e820 bios

This patch fixes E820 BIOS emulation on AMD CPUs that, unlike Intel ones,
require proper 16-bit segment/offset addressing in virtualized 16-bit mode.

Reported-by: Asias He <asias.hejun@gmail.com>
Tested-by: Asias He <asias.hejun@gmail.com>
Acked-by: Cyrill Gorcunov <gorcunov@gmail.com>
Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi>
This commit is contained in:
Pekka Enberg
2010-07-16 15:44:26 +03:00
committed by Will Deacon
parent c89b656f19
commit c2d4a49fa4
3 changed files with 31 additions and 5 deletions
-3
View File
@@ -79,9 +79,6 @@ ENTRY(bios_int15)
pushl %ebx
pushl %eax
xor %ax, %ax
mov %ax, %fs
movl %esp, %eax
call e820_query_map
+15 -2
View File
@@ -1,8 +1,14 @@
#include "kvm/e820.h"
#include "kvm/segment.h"
#include "kvm/bios.h"
#include "kvm/util.h"
static inline void set_fs(uint16_t seg)
{
asm volatile("movw %0,%%fs" : : "rm" (seg));
}
static inline uint8_t rdfs8(unsigned long addr)
{
uint8_t v;
@@ -15,23 +21,30 @@ static inline uint8_t rdfs8(unsigned long addr)
void e820_query_map(struct e820_query *query)
{
uint8_t map_size;
uint16_t fs_seg;
uint32_t ndx;
fs_seg = flat_to_seg16(E820_MAP_SIZE);
set_fs(fs_seg);
ndx = query->ebx;
map_size = rdfs8(E820_MAP_SIZE);
map_size = rdfs8(flat_to_off16(E820_MAP_SIZE, fs_seg));
if (ndx < map_size) {
unsigned long start;
unsigned int i;
uint8_t *p;
fs_seg = flat_to_seg16(E820_MAP_START);
set_fs(fs_seg);
start = E820_MAP_START + sizeof(struct e820_entry) * ndx;
p = (void *) query->edi;
for (i = 0; i < sizeof(struct e820_entry); i++)
*p++ = rdfs8(start + i);
*p++ = rdfs8(flat_to_off16(start + i, fs_seg));
}
query->eax = SMAP;
+16
View File
@@ -0,0 +1,16 @@
#ifndef KVM_SEGMENT_H
#define KVM_SEGMENT_H
#include <stdint.h>
static inline uint16_t flat_to_seg16(uint32_t address)
{
return address >> 4;
}
static inline uint16_t flat_to_off16(uint32_t address, uint32_t segment)
{
return address - (segment << 4);
}
#endif /* KVM_SEGMENT_H */