2004-03-14 20:20:30 +08:00
|
|
|
/*
|
|
|
|
* QEMU PC System Emulator
|
2007-09-17 05:08:06 +08:00
|
|
|
*
|
2004-03-14 20:20:30 +08:00
|
|
|
* Copyright (c) 2003-2004 Fabrice Bellard
|
2007-09-17 05:08:06 +08:00
|
|
|
*
|
2004-03-14 20:20:30 +08:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
2007-11-18 01:14:51 +08:00
|
|
|
#include "hw.h"
|
|
|
|
#include "pc.h"
|
2010-03-22 03:46:26 +08:00
|
|
|
#include "apic.h"
|
2007-11-18 01:14:51 +08:00
|
|
|
#include "fdc.h"
|
|
|
|
#include "pci.h"
|
2009-11-11 20:59:56 +08:00
|
|
|
#include "vmware_vga.h"
|
|
|
|
#include "usb-uhci.h"
|
|
|
|
#include "usb-ohci.h"
|
|
|
|
#include "prep_pci.h"
|
|
|
|
#include "apb_pci.h"
|
2007-11-18 01:14:51 +08:00
|
|
|
#include "block.h"
|
|
|
|
#include "sysemu.h"
|
|
|
|
#include "audio/audio.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "smbus.h"
|
|
|
|
#include "boards.h"
|
2009-03-06 07:01:23 +08:00
|
|
|
#include "monitor.h"
|
2008-09-19 02:27:29 +08:00
|
|
|
#include "fw_cfg.h"
|
2008-12-18 07:28:44 +08:00
|
|
|
#include "hpet_emul.h"
|
2009-04-25 20:56:19 +08:00
|
|
|
#include "watchdog.h"
|
qemu: Add support for SMBIOS command line otions (Alex Williamson)
Create a new -smbios option (x86-only) to allow binary SMBIOS entries
to be passed through to the BIOS or modify the default values of
individual fields of type 0 and 1 entries on the command line.
Binary SMBIOS entries can be generated as follows:
dmidecode -t 1 -u | grep $'^\t\t[^"]' | xargs -n1 | \
perl -lne 'printf "%c", hex($_)' > smbios_type_1.bin
These can then be passed to the BIOS using this switch:
-smbios file=smbios_type_1.bin
Command line generation supports the following syntax:
-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]
-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]
[,uuid=$(uuidgen)][,sku=str][,family=str]
For instance, to add a serial number to the type 1 table:
-smbios type=1,serial=0123456789
Interface is extensible to support more fields/tables as needed.
aliguori: remove texi formatting from help output
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7163 c046a42c-6fe2-441c-8c8c-71466251a162
2009-04-18 02:59:56 +08:00
|
|
|
#include "smbios.h"
|
2009-08-20 21:22:19 +08:00
|
|
|
#include "ide.h"
|
2009-09-20 22:58:02 +08:00
|
|
|
#include "loader.h"
|
|
|
|
#include "elf.h"
|
2009-12-26 21:13:46 +08:00
|
|
|
#include "multiboot.h"
|
2010-03-30 03:23:52 +08:00
|
|
|
#include "kvm.h"
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2004-03-15 05:46:48 +08:00
|
|
|
/* output Bochs bios info messages */
|
|
|
|
//#define DEBUG_BIOS
|
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
#define BIOS_FILENAME "bios.bin"
|
|
|
|
|
2008-04-25 01:59:27 +08:00
|
|
|
#define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
|
|
|
|
|
2007-04-01 03:41:22 +08:00
|
|
|
/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables. */
|
|
|
|
#define ACPI_DATA_SIZE 0x10000
|
2008-09-19 02:27:29 +08:00
|
|
|
#define BIOS_CFG_IOPORT 0x510
|
2009-02-28 04:12:36 +08:00
|
|
|
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
|
qemu: Add support for SMBIOS command line otions (Alex Williamson)
Create a new -smbios option (x86-only) to allow binary SMBIOS entries
to be passed through to the BIOS or modify the default values of
individual fields of type 0 and 1 entries on the command line.
Binary SMBIOS entries can be generated as follows:
dmidecode -t 1 -u | grep $'^\t\t[^"]' | xargs -n1 | \
perl -lne 'printf "%c", hex($_)' > smbios_type_1.bin
These can then be passed to the BIOS using this switch:
-smbios file=smbios_type_1.bin
Command line generation supports the following syntax:
-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]
-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]
[,uuid=$(uuidgen)][,sku=str][,family=str]
For instance, to add a serial number to the type 1 table:
-smbios type=1,serial=0123456789
Interface is extensible to support more fields/tables as needed.
aliguori: remove texi formatting from help output
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7163 c046a42c-6fe2-441c-8c8c-71466251a162
2009-04-18 02:59:56 +08:00
|
|
|
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
|
2009-08-06 22:25:50 +08:00
|
|
|
#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
|
2010-02-16 01:33:46 +08:00
|
|
|
#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3)
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2007-12-02 12:51:10 +08:00
|
|
|
#define MAX_IDE_BUS 2
|
|
|
|
|
2010-02-07 17:01:18 +08:00
|
|
|
static FDCtrl *floppy_controller;
|
2004-04-01 02:58:38 +08:00
|
|
|
static RTCState *rtc_state;
|
2004-05-04 07:18:25 +08:00
|
|
|
static PITState *pit;
|
2009-08-28 21:28:15 +08:00
|
|
|
static PCII440FXState *i440fx_state;
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2010-02-16 01:33:46 +08:00
|
|
|
#define E820_NR_ENTRIES 16
|
|
|
|
|
|
|
|
struct e820_entry {
|
|
|
|
uint64_t address;
|
|
|
|
uint64_t length;
|
|
|
|
uint32_t type;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct e820_table {
|
|
|
|
uint32_t count;
|
|
|
|
struct e820_entry entry[E820_NR_ENTRIES];
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct e820_table e820_table;
|
|
|
|
|
2009-08-10 00:44:55 +08:00
|
|
|
typedef struct isa_irq_state {
|
|
|
|
qemu_irq *i8259;
|
2009-08-10 00:44:56 +08:00
|
|
|
qemu_irq *ioapic;
|
2009-08-10 00:44:55 +08:00
|
|
|
} IsaIrqState;
|
|
|
|
|
|
|
|
static void isa_irq_handler(void *opaque, int n, int level)
|
|
|
|
{
|
|
|
|
IsaIrqState *isa = (IsaIrqState *)opaque;
|
|
|
|
|
2009-08-10 00:44:56 +08:00
|
|
|
if (n < 16) {
|
|
|
|
qemu_set_irq(isa->i8259[n], level);
|
|
|
|
}
|
2009-09-10 17:43:23 +08:00
|
|
|
if (isa->ioapic)
|
|
|
|
qemu_set_irq(isa->ioapic[n], level);
|
2009-08-10 00:44:56 +08:00
|
|
|
};
|
2009-08-10 00:44:55 +08:00
|
|
|
|
2004-03-15 05:46:48 +08:00
|
|
|
static void ioport80_write(void *opaque, uint32_t addr, uint32_t data)
|
2004-03-14 20:20:30 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2004-05-09 05:03:41 +08:00
|
|
|
/* MSDOS compatibility mode FPU exception support */
|
2007-04-08 02:14:41 +08:00
|
|
|
static qemu_irq ferr_irq;
|
2004-05-09 05:03:41 +08:00
|
|
|
/* XXX: add IGNNE support */
|
|
|
|
void cpu_set_ferr(CPUX86State *s)
|
|
|
|
{
|
2007-04-08 02:14:41 +08:00
|
|
|
qemu_irq_raise(ferr_irq);
|
2004-05-09 05:03:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ioportF0_write(void *opaque, uint32_t addr, uint32_t data)
|
|
|
|
{
|
2007-04-08 02:14:41 +08:00
|
|
|
qemu_irq_lower(ferr_irq);
|
2004-05-09 05:03:41 +08:00
|
|
|
}
|
|
|
|
|
2004-05-20 22:02:14 +08:00
|
|
|
/* TSC handling */
|
|
|
|
uint64_t cpu_get_tsc(CPUX86State *env)
|
|
|
|
{
|
2009-08-11 06:07:24 +08:00
|
|
|
return cpu_get_ticks();
|
2004-05-20 22:02:14 +08:00
|
|
|
}
|
|
|
|
|
2006-09-25 02:48:00 +08:00
|
|
|
/* SMM support */
|
|
|
|
void cpu_smm_update(CPUState *env)
|
|
|
|
{
|
|
|
|
if (i440fx_state && env == first_cpu)
|
|
|
|
i440fx_set_smm(i440fx_state, (env->hflags >> HF_SMM_SHIFT) & 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-07-03 02:11:44 +08:00
|
|
|
/* IRQ handling */
|
|
|
|
int cpu_get_pic_interrupt(CPUState *env)
|
|
|
|
{
|
|
|
|
int intno;
|
|
|
|
|
|
|
|
intno = apic_get_interrupt(env);
|
|
|
|
if (intno >= 0) {
|
|
|
|
/* set irq request if a PIC irq is still pending */
|
|
|
|
/* XXX: improve that */
|
2007-09-17 05:08:06 +08:00
|
|
|
pic_update_irq(isa_pic);
|
2005-07-03 02:11:44 +08:00
|
|
|
return intno;
|
|
|
|
}
|
|
|
|
/* read the irq from the PIC */
|
2007-10-09 11:08:56 +08:00
|
|
|
if (!apic_accept_pic_intr(env))
|
|
|
|
return -1;
|
|
|
|
|
2005-07-03 02:11:44 +08:00
|
|
|
intno = pic_read_irq(isa_pic);
|
|
|
|
return intno;
|
|
|
|
}
|
|
|
|
|
2007-04-08 02:14:41 +08:00
|
|
|
static void pic_irq_request(void *opaque, int irq, int level)
|
2005-07-03 02:11:44 +08:00
|
|
|
{
|
2008-04-14 00:08:30 +08:00
|
|
|
CPUState *env = first_cpu;
|
|
|
|
|
2008-08-19 20:55:20 +08:00
|
|
|
if (env->apic_state) {
|
|
|
|
while (env) {
|
|
|
|
if (apic_accept_pic_intr(env))
|
2008-08-21 11:14:52 +08:00
|
|
|
apic_deliver_pic_intr(env, level);
|
2008-08-19 20:55:20 +08:00
|
|
|
env = env->next_cpu;
|
|
|
|
}
|
|
|
|
} else {
|
2008-08-21 11:14:41 +08:00
|
|
|
if (level)
|
|
|
|
cpu_interrupt(env, CPU_INTERRUPT_HARD);
|
|
|
|
else
|
|
|
|
cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
|
2008-04-14 00:08:30 +08:00
|
|
|
}
|
2005-07-03 02:11:44 +08:00
|
|
|
}
|
|
|
|
|
2004-04-01 02:58:38 +08:00
|
|
|
/* PC cmos mappings */
|
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
#define REG_EQUIPMENT_BYTE 0x14
|
|
|
|
|
2004-05-24 00:26:20 +08:00
|
|
|
static int cmos_get_fd_drive_type(int fd0)
|
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
|
|
|
switch (fd0) {
|
|
|
|
case 0:
|
|
|
|
/* 1.44 Mb 3"5 drive */
|
|
|
|
val = 4;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
/* 2.88 Mb 3"5 drive */
|
|
|
|
val = 5;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
/* 1.2 Mb 5"5 drive */
|
|
|
|
val = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
val = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2007-09-17 05:08:06 +08:00
|
|
|
static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd)
|
2004-10-10 00:47:59 +08:00
|
|
|
{
|
|
|
|
RTCState *s = rtc_state;
|
|
|
|
int cylinders, heads, sectors;
|
|
|
|
bdrv_get_geometry_hint(hd, &cylinders, &heads, §ors);
|
|
|
|
rtc_set_memory(s, type_ofs, 47);
|
|
|
|
rtc_set_memory(s, info_ofs, cylinders);
|
|
|
|
rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
|
|
|
|
rtc_set_memory(s, info_ofs + 2, heads);
|
|
|
|
rtc_set_memory(s, info_ofs + 3, 0xff);
|
|
|
|
rtc_set_memory(s, info_ofs + 4, 0xff);
|
|
|
|
rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
|
|
|
|
rtc_set_memory(s, info_ofs + 6, cylinders);
|
|
|
|
rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
|
|
|
|
rtc_set_memory(s, info_ofs + 8, sectors);
|
|
|
|
}
|
|
|
|
|
2007-10-31 09:54:04 +08:00
|
|
|
/* convert boot_device letter to something recognizable by the bios */
|
|
|
|
static int boot_device2nibble(char boot_device)
|
|
|
|
{
|
|
|
|
switch(boot_device) {
|
|
|
|
case 'a':
|
|
|
|
case 'b':
|
|
|
|
return 0x01; /* floppy boot */
|
|
|
|
case 'c':
|
|
|
|
return 0x02; /* hard drive boot */
|
|
|
|
case 'd':
|
|
|
|
return 0x03; /* CD-ROM boot */
|
|
|
|
case 'n':
|
|
|
|
return 0x04; /* Network boot */
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-02-18 01:07:48 +08:00
|
|
|
static int set_boot_dev(RTCState *s, const char *boot_device, int fd_bootchk)
|
2008-05-05 04:11:34 +08:00
|
|
|
{
|
|
|
|
#define PC_MAX_BOOT_DEVICES 3
|
|
|
|
int nbds, bds[3] = { 0, };
|
|
|
|
int i;
|
|
|
|
|
|
|
|
nbds = strlen(boot_device);
|
|
|
|
if (nbds > PC_MAX_BOOT_DEVICES) {
|
2010-02-19 00:25:24 +08:00
|
|
|
error_report("Too many boot devices for PC");
|
2008-05-05 04:11:34 +08:00
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
for (i = 0; i < nbds; i++) {
|
|
|
|
bds[i] = boot_device2nibble(boot_device[i]);
|
|
|
|
if (bds[i] == 0) {
|
2010-02-19 00:25:24 +08:00
|
|
|
error_report("Invalid boot device for PC: '%c'",
|
|
|
|
boot_device[i]);
|
2008-05-05 04:11:34 +08:00
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
|
2010-02-18 01:07:48 +08:00
|
|
|
rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1));
|
2008-05-05 04:11:34 +08:00
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
2010-02-18 01:07:48 +08:00
|
|
|
static int pc_boot_set(void *opaque, const char *boot_device)
|
|
|
|
{
|
|
|
|
return set_boot_dev(opaque, boot_device, 0);
|
|
|
|
}
|
|
|
|
|
2004-10-10 00:47:59 +08:00
|
|
|
/* hd_table must contain 4 block drivers */
|
2009-10-02 05:12:16 +08:00
|
|
|
static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
|
2009-08-28 21:47:03 +08:00
|
|
|
const char *boot_device, DriveInfo **hd_table)
|
2004-03-14 20:20:30 +08:00
|
|
|
{
|
2004-04-01 02:58:38 +08:00
|
|
|
RTCState *s = rtc_state;
|
2004-03-14 20:20:30 +08:00
|
|
|
int val;
|
2004-03-15 05:46:48 +08:00
|
|
|
int fd0, fd1, nb;
|
2004-10-10 00:47:59 +08:00
|
|
|
int i;
|
2004-04-01 02:58:38 +08:00
|
|
|
|
|
|
|
/* various important CMOS locations needed by PC/Bochs bios */
|
2004-03-14 20:20:30 +08:00
|
|
|
|
|
|
|
/* memory size */
|
2004-04-08 04:51:30 +08:00
|
|
|
val = 640; /* base memory in K */
|
|
|
|
rtc_set_memory(s, 0x15, val);
|
|
|
|
rtc_set_memory(s, 0x16, val >> 8);
|
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
val = (ram_size / 1024) - 1024;
|
|
|
|
if (val > 65535)
|
|
|
|
val = 65535;
|
2004-04-01 02:58:38 +08:00
|
|
|
rtc_set_memory(s, 0x17, val);
|
|
|
|
rtc_set_memory(s, 0x18, val >> 8);
|
|
|
|
rtc_set_memory(s, 0x30, val);
|
|
|
|
rtc_set_memory(s, 0x31, val >> 8);
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2008-04-28 05:12:55 +08:00
|
|
|
if (above_4g_mem_size) {
|
|
|
|
rtc_set_memory(s, 0x5b, (unsigned int)above_4g_mem_size >> 16);
|
|
|
|
rtc_set_memory(s, 0x5c, (unsigned int)above_4g_mem_size >> 24);
|
|
|
|
rtc_set_memory(s, 0x5d, (uint64_t)above_4g_mem_size >> 32);
|
|
|
|
}
|
|
|
|
|
2004-06-26 23:53:17 +08:00
|
|
|
if (ram_size > (16 * 1024 * 1024))
|
|
|
|
val = (ram_size / 65536) - ((16 * 1024 * 1024) / 65536);
|
|
|
|
else
|
|
|
|
val = 0;
|
2004-03-14 20:20:30 +08:00
|
|
|
if (val > 65535)
|
|
|
|
val = 65535;
|
2004-04-01 02:58:38 +08:00
|
|
|
rtc_set_memory(s, 0x34, val);
|
|
|
|
rtc_set_memory(s, 0x35, val >> 8);
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2008-03-29 06:28:08 +08:00
|
|
|
/* set the number of CPU */
|
|
|
|
rtc_set_memory(s, 0x5f, smp_cpus - 1);
|
|
|
|
|
2007-10-31 09:54:04 +08:00
|
|
|
/* set boot devices, and disable floppy signature check if requested */
|
2010-02-18 01:07:48 +08:00
|
|
|
if (set_boot_dev(s, boot_device, fd_bootchk)) {
|
2007-11-11 09:50:45 +08:00
|
|
|
exit(1);
|
|
|
|
}
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2004-03-15 05:46:48 +08:00
|
|
|
/* floppy type */
|
|
|
|
|
2004-03-20 07:05:34 +08:00
|
|
|
fd0 = fdctrl_get_drive_type(floppy_controller, 0);
|
|
|
|
fd1 = fdctrl_get_drive_type(floppy_controller, 1);
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2004-05-24 00:26:20 +08:00
|
|
|
val = (cmos_get_fd_drive_type(fd0) << 4) | cmos_get_fd_drive_type(fd1);
|
2004-04-01 02:58:38 +08:00
|
|
|
rtc_set_memory(s, 0x10, val);
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2004-04-01 02:58:38 +08:00
|
|
|
val = 0;
|
2004-03-15 05:46:48 +08:00
|
|
|
nb = 0;
|
2004-03-14 20:20:30 +08:00
|
|
|
if (fd0 < 3)
|
|
|
|
nb++;
|
|
|
|
if (fd1 < 3)
|
|
|
|
nb++;
|
|
|
|
switch (nb) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
2004-04-01 02:58:38 +08:00
|
|
|
val |= 0x01; /* 1 drive, ready for boot */
|
2004-03-14 20:20:30 +08:00
|
|
|
break;
|
|
|
|
case 2:
|
2004-04-01 02:58:38 +08:00
|
|
|
val |= 0x41; /* 2 drives, ready for boot */
|
2004-03-14 20:20:30 +08:00
|
|
|
break;
|
|
|
|
}
|
2004-04-01 02:58:38 +08:00
|
|
|
val |= 0x02; /* FPU is there */
|
|
|
|
val |= 0x04; /* PS/2 mouse installed */
|
|
|
|
rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
|
|
|
|
|
2004-10-10 00:47:59 +08:00
|
|
|
/* hard drives */
|
|
|
|
|
|
|
|
rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
|
|
|
|
if (hd_table[0])
|
2009-08-28 21:47:03 +08:00
|
|
|
cmos_init_hd(0x19, 0x1b, hd_table[0]->bdrv);
|
2007-09-17 05:08:06 +08:00
|
|
|
if (hd_table[1])
|
2009-08-28 21:47:03 +08:00
|
|
|
cmos_init_hd(0x1a, 0x24, hd_table[1]->bdrv);
|
2004-10-10 00:47:59 +08:00
|
|
|
|
|
|
|
val = 0;
|
2004-10-13 05:50:05 +08:00
|
|
|
for (i = 0; i < 4; i++) {
|
2004-10-10 00:47:59 +08:00
|
|
|
if (hd_table[i]) {
|
2004-11-16 09:45:27 +08:00
|
|
|
int cylinders, heads, sectors, translation;
|
|
|
|
/* NOTE: bdrv_get_geometry_hint() returns the physical
|
|
|
|
geometry. It is always such that: 1 <= sects <= 63, 1
|
|
|
|
<= heads <= 16, 1 <= cylinders <= 16383. The BIOS
|
|
|
|
geometry can be different if a translation is done. */
|
2009-08-28 21:47:03 +08:00
|
|
|
translation = bdrv_get_translation_hint(hd_table[i]->bdrv);
|
2004-11-16 09:45:27 +08:00
|
|
|
if (translation == BIOS_ATA_TRANSLATION_AUTO) {
|
2009-08-28 21:47:03 +08:00
|
|
|
bdrv_get_geometry_hint(hd_table[i]->bdrv, &cylinders, &heads, §ors);
|
2004-11-16 09:45:27 +08:00
|
|
|
if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
|
|
|
|
/* No translation. */
|
|
|
|
translation = 0;
|
|
|
|
} else {
|
|
|
|
/* LBA translation. */
|
|
|
|
translation = 1;
|
|
|
|
}
|
2004-10-13 05:50:05 +08:00
|
|
|
} else {
|
2004-11-16 09:45:27 +08:00
|
|
|
translation--;
|
2004-10-10 00:47:59 +08:00
|
|
|
}
|
|
|
|
val |= translation << (i * 2);
|
|
|
|
}
|
2004-10-13 05:50:05 +08:00
|
|
|
}
|
2004-10-10 00:47:59 +08:00
|
|
|
rtc_set_memory(s, 0x39, val);
|
2004-03-14 20:20:30 +08:00
|
|
|
}
|
|
|
|
|
2005-11-22 07:34:32 +08:00
|
|
|
void ioport_set_a20(int enable)
|
|
|
|
{
|
|
|
|
/* XXX: send to all CPUs ? */
|
|
|
|
cpu_x86_set_a20(first_cpu, enable);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ioport_get_a20(void)
|
|
|
|
{
|
|
|
|
return ((first_cpu->a20_mask >> 20) & 1);
|
|
|
|
}
|
|
|
|
|
2004-04-06 04:26:03 +08:00
|
|
|
static void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
|
|
|
|
{
|
2005-11-22 07:34:32 +08:00
|
|
|
ioport_set_a20((val >> 1) & 1);
|
2004-04-06 04:26:03 +08:00
|
|
|
/* XXX: bit 0 is fast reset */
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t ioport92_read(void *opaque, uint32_t addr)
|
|
|
|
{
|
2005-11-22 07:34:32 +08:00
|
|
|
return ioport_get_a20() << 1;
|
2004-04-06 04:26:03 +08:00
|
|
|
}
|
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
/***********************************************************/
|
|
|
|
/* Bochs BIOS debug ports */
|
|
|
|
|
2007-11-18 09:44:38 +08:00
|
|
|
static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
|
2004-03-14 20:20:30 +08:00
|
|
|
{
|
2004-06-20 21:00:26 +08:00
|
|
|
static const char shutdown_str[8] = "Shutdown";
|
|
|
|
static int shutdown_index = 0;
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
switch(addr) {
|
|
|
|
/* Bochs BIOS messages */
|
|
|
|
case 0x400:
|
|
|
|
case 0x401:
|
|
|
|
fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val);
|
|
|
|
exit(1);
|
|
|
|
case 0x402:
|
|
|
|
case 0x403:
|
|
|
|
#ifdef DEBUG_BIOS
|
|
|
|
fprintf(stderr, "%c", val);
|
|
|
|
#endif
|
|
|
|
break;
|
2004-06-20 21:00:26 +08:00
|
|
|
case 0x8900:
|
|
|
|
/* same as Bochs power off */
|
|
|
|
if (val == shutdown_str[shutdown_index]) {
|
|
|
|
shutdown_index++;
|
|
|
|
if (shutdown_index == 8) {
|
|
|
|
shutdown_index = 0;
|
|
|
|
qemu_system_shutdown_request();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
shutdown_index = 0;
|
|
|
|
}
|
|
|
|
break;
|
2004-03-14 20:20:30 +08:00
|
|
|
|
|
|
|
/* LGPL'ed VGA BIOS messages */
|
|
|
|
case 0x501:
|
|
|
|
case 0x502:
|
|
|
|
fprintf(stderr, "VGA BIOS panic, line %d\n", val);
|
|
|
|
exit(1);
|
|
|
|
case 0x500:
|
|
|
|
case 0x503:
|
|
|
|
#ifdef DEBUG_BIOS
|
|
|
|
fprintf(stderr, "%c", val);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-16 01:33:46 +08:00
|
|
|
int e820_add_entry(uint64_t address, uint64_t length, uint32_t type)
|
|
|
|
{
|
|
|
|
int index = e820_table.count;
|
|
|
|
struct e820_entry *entry;
|
|
|
|
|
|
|
|
if (index >= E820_NR_ENTRIES)
|
|
|
|
return -EBUSY;
|
|
|
|
entry = &e820_table.entry[index];
|
|
|
|
|
|
|
|
entry->address = address;
|
|
|
|
entry->length = length;
|
|
|
|
entry->type = type;
|
|
|
|
|
|
|
|
e820_table.count++;
|
|
|
|
return e820_table.count;
|
|
|
|
}
|
|
|
|
|
2009-06-29 21:37:38 +08:00
|
|
|
static void *bochs_bios_init(void)
|
2004-03-14 20:20:30 +08:00
|
|
|
{
|
2008-09-19 02:27:29 +08:00
|
|
|
void *fw_cfg;
|
qemu: Add support for SMBIOS command line otions (Alex Williamson)
Create a new -smbios option (x86-only) to allow binary SMBIOS entries
to be passed through to the BIOS or modify the default values of
individual fields of type 0 and 1 entries on the command line.
Binary SMBIOS entries can be generated as follows:
dmidecode -t 1 -u | grep $'^\t\t[^"]' | xargs -n1 | \
perl -lne 'printf "%c", hex($_)' > smbios_type_1.bin
These can then be passed to the BIOS using this switch:
-smbios file=smbios_type_1.bin
Command line generation supports the following syntax:
-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]
-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]
[,uuid=$(uuidgen)][,sku=str][,family=str]
For instance, to add a serial number to the type 1 table:
-smbios type=1,serial=0123456789
Interface is extensible to support more fields/tables as needed.
aliguori: remove texi formatting from help output
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7163 c046a42c-6fe2-441c-8c8c-71466251a162
2009-04-18 02:59:56 +08:00
|
|
|
uint8_t *smbios_table;
|
|
|
|
size_t smbios_len;
|
2009-04-22 06:31:41 +08:00
|
|
|
uint64_t *numa_fw_cfg;
|
|
|
|
int i, j;
|
2008-09-19 02:27:29 +08:00
|
|
|
|
2004-03-15 05:46:48 +08:00
|
|
|
register_ioport_write(0x400, 1, 2, bochs_bios_write, NULL);
|
|
|
|
register_ioport_write(0x401, 1, 2, bochs_bios_write, NULL);
|
|
|
|
register_ioport_write(0x402, 1, 1, bochs_bios_write, NULL);
|
|
|
|
register_ioport_write(0x403, 1, 1, bochs_bios_write, NULL);
|
2004-06-20 21:00:26 +08:00
|
|
|
register_ioport_write(0x8900, 1, 1, bochs_bios_write, NULL);
|
2004-03-15 05:46:48 +08:00
|
|
|
|
|
|
|
register_ioport_write(0x501, 1, 2, bochs_bios_write, NULL);
|
|
|
|
register_ioport_write(0x502, 1, 2, bochs_bios_write, NULL);
|
|
|
|
register_ioport_write(0x500, 1, 1, bochs_bios_write, NULL);
|
|
|
|
register_ioport_write(0x503, 1, 1, bochs_bios_write, NULL);
|
2008-09-19 02:27:29 +08:00
|
|
|
|
|
|
|
fw_cfg = fw_cfg_init(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 1, 0, 0);
|
2009-06-29 21:37:38 +08:00
|
|
|
|
2008-09-19 02:27:29 +08:00
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
|
2008-09-19 02:33:18 +08:00
|
|
|
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
2009-03-07 23:50:18 +08:00
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, (uint8_t *)acpi_tables,
|
|
|
|
acpi_tables_len);
|
2009-08-06 22:25:50 +08:00
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_IRQ0_OVERRIDE, &irq0override, 1);
|
qemu: Add support for SMBIOS command line otions (Alex Williamson)
Create a new -smbios option (x86-only) to allow binary SMBIOS entries
to be passed through to the BIOS or modify the default values of
individual fields of type 0 and 1 entries on the command line.
Binary SMBIOS entries can be generated as follows:
dmidecode -t 1 -u | grep $'^\t\t[^"]' | xargs -n1 | \
perl -lne 'printf "%c", hex($_)' > smbios_type_1.bin
These can then be passed to the BIOS using this switch:
-smbios file=smbios_type_1.bin
Command line generation supports the following syntax:
-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]
-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]
[,uuid=$(uuidgen)][,sku=str][,family=str]
For instance, to add a serial number to the type 1 table:
-smbios type=1,serial=0123456789
Interface is extensible to support more fields/tables as needed.
aliguori: remove texi formatting from help output
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7163 c046a42c-6fe2-441c-8c8c-71466251a162
2009-04-18 02:59:56 +08:00
|
|
|
|
|
|
|
smbios_table = smbios_get_table(&smbios_len);
|
|
|
|
if (smbios_table)
|
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
|
|
|
|
smbios_table, smbios_len);
|
2010-02-16 01:33:46 +08:00
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, (uint8_t *)&e820_table,
|
|
|
|
sizeof(struct e820_table));
|
2009-04-22 06:31:41 +08:00
|
|
|
|
|
|
|
/* allocate memory for the NUMA channel: one (64bit) word for the number
|
|
|
|
* of nodes, one word for each VCPU->node and one word for each node to
|
|
|
|
* hold the amount of memory.
|
|
|
|
*/
|
|
|
|
numa_fw_cfg = qemu_mallocz((1 + smp_cpus + nb_numa_nodes) * 8);
|
|
|
|
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
|
|
|
|
for (i = 0; i < smp_cpus; i++) {
|
|
|
|
for (j = 0; j < nb_numa_nodes; j++) {
|
|
|
|
if (node_cpumask[j] & (1 << i)) {
|
|
|
|
numa_fw_cfg[i + 1] = cpu_to_le64(j);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < nb_numa_nodes; i++) {
|
|
|
|
numa_fw_cfg[smp_cpus + 1 + i] = cpu_to_le64(node_mem[i]);
|
|
|
|
}
|
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, (uint8_t *)numa_fw_cfg,
|
|
|
|
(1 + smp_cpus + nb_numa_nodes) * 8);
|
2009-06-29 21:37:38 +08:00
|
|
|
|
|
|
|
return fw_cfg;
|
2004-03-14 20:20:30 +08:00
|
|
|
}
|
|
|
|
|
2007-05-20 05:04:38 +08:00
|
|
|
static long get_file_size(FILE *f)
|
|
|
|
{
|
|
|
|
long where, size;
|
|
|
|
|
|
|
|
/* XXX: on Unix systems, using fstat() probably makes more sense */
|
|
|
|
|
|
|
|
where = ftell(f);
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
|
|
size = ftell(f);
|
|
|
|
fseek(f, where, SEEK_SET);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2009-06-29 21:37:39 +08:00
|
|
|
static void load_linux(void *fw_cfg,
|
2008-11-09 00:27:07 +08:00
|
|
|
const char *kernel_filename,
|
2007-05-20 05:04:38 +08:00
|
|
|
const char *initrd_filename,
|
2009-05-19 04:35:58 +08:00
|
|
|
const char *kernel_cmdline,
|
2009-10-01 22:42:33 +08:00
|
|
|
target_phys_addr_t max_ram_size)
|
2007-05-20 05:04:38 +08:00
|
|
|
{
|
|
|
|
uint16_t protocol;
|
2009-05-30 07:52:44 +08:00
|
|
|
int setup_size, kernel_size, initrd_size = 0, cmdline_size;
|
2007-05-20 05:04:38 +08:00
|
|
|
uint32_t initrd_max;
|
2009-11-13 04:53:14 +08:00
|
|
|
uint8_t header[8192], *setup, *kernel, *initrd_data;
|
2009-10-02 05:12:16 +08:00
|
|
|
target_phys_addr_t real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
|
2009-10-01 22:42:33 +08:00
|
|
|
FILE *f;
|
2009-07-13 23:46:42 +08:00
|
|
|
char *vmode;
|
2007-05-20 05:04:38 +08:00
|
|
|
|
|
|
|
/* Align to 16 bytes as a paranoia measure */
|
|
|
|
cmdline_size = (strlen(kernel_cmdline)+16) & ~15;
|
|
|
|
|
|
|
|
/* load the kernel header */
|
|
|
|
f = fopen(kernel_filename, "rb");
|
|
|
|
if (!f || !(kernel_size = get_file_size(f)) ||
|
2009-06-29 21:37:39 +08:00
|
|
|
fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) !=
|
|
|
|
MIN(ARRAY_SIZE(header), kernel_size)) {
|
2009-10-01 22:42:56 +08:00
|
|
|
fprintf(stderr, "qemu: could not load kernel '%s': %s\n",
|
|
|
|
kernel_filename, strerror(errno));
|
2007-05-20 05:04:38 +08:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* kernel protocol version */
|
2007-11-08 00:54:42 +08:00
|
|
|
#if 0
|
2007-05-20 05:04:38 +08:00
|
|
|
fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202));
|
2007-11-08 00:54:42 +08:00
|
|
|
#endif
|
2007-05-20 05:04:38 +08:00
|
|
|
if (ldl_p(header+0x202) == 0x53726448)
|
|
|
|
protocol = lduw_p(header+0x206);
|
2009-06-29 21:37:39 +08:00
|
|
|
else {
|
|
|
|
/* This looks like a multiboot kernel. If it is, let's stop
|
|
|
|
treating it like a Linux kernel. */
|
2009-12-26 21:13:46 +08:00
|
|
|
if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
|
|
|
|
kernel_cmdline, kernel_size, header))
|
2009-09-07 00:31:58 +08:00
|
|
|
return;
|
2007-05-20 05:04:38 +08:00
|
|
|
protocol = 0;
|
2009-06-29 21:37:39 +08:00
|
|
|
}
|
2007-05-20 05:04:38 +08:00
|
|
|
|
|
|
|
if (protocol < 0x200 || !(header[0x211] & 0x01)) {
|
|
|
|
/* Low kernel */
|
2008-05-14 02:50:56 +08:00
|
|
|
real_addr = 0x90000;
|
|
|
|
cmdline_addr = 0x9a000 - cmdline_size;
|
|
|
|
prot_addr = 0x10000;
|
2007-05-20 05:04:38 +08:00
|
|
|
} else if (protocol < 0x202) {
|
|
|
|
/* High but ancient kernel */
|
2008-05-14 02:50:56 +08:00
|
|
|
real_addr = 0x90000;
|
|
|
|
cmdline_addr = 0x9a000 - cmdline_size;
|
|
|
|
prot_addr = 0x100000;
|
2007-05-20 05:04:38 +08:00
|
|
|
} else {
|
|
|
|
/* High and recent kernel */
|
2008-05-14 02:50:56 +08:00
|
|
|
real_addr = 0x10000;
|
|
|
|
cmdline_addr = 0x20000;
|
|
|
|
prot_addr = 0x100000;
|
2007-05-20 05:04:38 +08:00
|
|
|
}
|
|
|
|
|
2007-11-08 00:54:42 +08:00
|
|
|
#if 0
|
2007-05-20 05:04:38 +08:00
|
|
|
fprintf(stderr,
|
2008-07-16 20:13:52 +08:00
|
|
|
"qemu: real_addr = 0x" TARGET_FMT_plx "\n"
|
|
|
|
"qemu: cmdline_addr = 0x" TARGET_FMT_plx "\n"
|
|
|
|
"qemu: prot_addr = 0x" TARGET_FMT_plx "\n",
|
2008-05-14 02:50:56 +08:00
|
|
|
real_addr,
|
|
|
|
cmdline_addr,
|
|
|
|
prot_addr);
|
2007-11-08 00:54:42 +08:00
|
|
|
#endif
|
2007-05-20 05:04:38 +08:00
|
|
|
|
|
|
|
/* highest address for loading the initrd */
|
|
|
|
if (protocol >= 0x203)
|
|
|
|
initrd_max = ldl_p(header+0x22c);
|
|
|
|
else
|
|
|
|
initrd_max = 0x37ffffff;
|
|
|
|
|
2009-05-19 04:35:58 +08:00
|
|
|
if (initrd_max >= max_ram_size-ACPI_DATA_SIZE)
|
|
|
|
initrd_max = max_ram_size-ACPI_DATA_SIZE-1;
|
2007-05-20 05:04:38 +08:00
|
|
|
|
2009-11-13 04:53:14 +08:00
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
|
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline)+1);
|
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA,
|
|
|
|
(uint8_t*)strdup(kernel_cmdline),
|
|
|
|
strlen(kernel_cmdline)+1);
|
2007-05-20 05:04:38 +08:00
|
|
|
|
|
|
|
if (protocol >= 0x202) {
|
2008-05-14 02:50:56 +08:00
|
|
|
stl_p(header+0x228, cmdline_addr);
|
2007-05-20 05:04:38 +08:00
|
|
|
} else {
|
|
|
|
stw_p(header+0x20, 0xA33F);
|
|
|
|
stw_p(header+0x22, cmdline_addr-real_addr);
|
|
|
|
}
|
|
|
|
|
2009-07-13 23:46:42 +08:00
|
|
|
/* handle vga= parameter */
|
|
|
|
vmode = strstr(kernel_cmdline, "vga=");
|
|
|
|
if (vmode) {
|
|
|
|
unsigned int video_mode;
|
|
|
|
/* skip "vga=" */
|
|
|
|
vmode += 4;
|
|
|
|
if (!strncmp(vmode, "normal", 6)) {
|
|
|
|
video_mode = 0xffff;
|
|
|
|
} else if (!strncmp(vmode, "ext", 3)) {
|
|
|
|
video_mode = 0xfffe;
|
|
|
|
} else if (!strncmp(vmode, "ask", 3)) {
|
|
|
|
video_mode = 0xfffd;
|
|
|
|
} else {
|
|
|
|
video_mode = strtol(vmode, NULL, 0);
|
|
|
|
}
|
|
|
|
stw_p(header+0x1fa, video_mode);
|
|
|
|
}
|
|
|
|
|
2007-05-20 05:04:38 +08:00
|
|
|
/* loader type */
|
|
|
|
/* High nybble = B reserved for Qemu; low nybble is revision number.
|
|
|
|
If this code is substantially changed, you may want to consider
|
|
|
|
incrementing the revision. */
|
|
|
|
if (protocol >= 0x200)
|
|
|
|
header[0x210] = 0xB0;
|
|
|
|
|
|
|
|
/* heap */
|
|
|
|
if (protocol >= 0x201) {
|
|
|
|
header[0x211] |= 0x80; /* CAN_USE_HEAP */
|
|
|
|
stw_p(header+0x224, cmdline_addr-real_addr-0x200);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* load initrd */
|
|
|
|
if (initrd_filename) {
|
|
|
|
if (protocol < 0x200) {
|
|
|
|
fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2009-10-01 22:42:33 +08:00
|
|
|
initrd_size = get_image_size(initrd_filename);
|
|
|
|
initrd_addr = (initrd_max-initrd_size) & ~4095;
|
2009-11-13 04:53:14 +08:00
|
|
|
|
|
|
|
initrd_data = qemu_malloc(initrd_size);
|
|
|
|
load_image(initrd_filename, initrd_data);
|
|
|
|
|
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
|
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
|
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size);
|
2007-05-20 05:04:38 +08:00
|
|
|
|
2008-05-14 02:50:56 +08:00
|
|
|
stl_p(header+0x218, initrd_addr);
|
2007-05-20 05:04:38 +08:00
|
|
|
stl_p(header+0x21c, initrd_size);
|
|
|
|
}
|
|
|
|
|
2009-10-01 22:42:33 +08:00
|
|
|
/* load kernel and setup */
|
2007-05-20 05:04:38 +08:00
|
|
|
setup_size = header[0x1f1];
|
|
|
|
if (setup_size == 0)
|
|
|
|
setup_size = 4;
|
|
|
|
setup_size = (setup_size+1)*512;
|
2009-10-01 22:42:33 +08:00
|
|
|
kernel_size -= setup_size;
|
2007-05-20 05:04:38 +08:00
|
|
|
|
2009-10-01 22:42:33 +08:00
|
|
|
setup = qemu_malloc(setup_size);
|
|
|
|
kernel = qemu_malloc(kernel_size);
|
|
|
|
fseek(f, 0, SEEK_SET);
|
2009-12-26 02:19:17 +08:00
|
|
|
if (fread(setup, 1, setup_size, f) != setup_size) {
|
|
|
|
fprintf(stderr, "fread() failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (fread(kernel, 1, kernel_size, f) != kernel_size) {
|
|
|
|
fprintf(stderr, "fread() failed\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2007-05-20 05:04:38 +08:00
|
|
|
fclose(f);
|
2009-10-01 22:42:33 +08:00
|
|
|
memcpy(setup, header, MIN(sizeof(header), setup_size));
|
2009-11-13 04:53:14 +08:00
|
|
|
|
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr);
|
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
|
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size);
|
|
|
|
|
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr);
|
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
|
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
|
|
|
|
|
|
|
|
option_rom[nb_option_roms] = "linuxboot.bin";
|
|
|
|
nb_option_roms++;
|
2007-05-20 05:04:38 +08:00
|
|
|
}
|
|
|
|
|
2004-03-15 05:46:48 +08:00
|
|
|
static const int ide_iobase[2] = { 0x1f0, 0x170 };
|
|
|
|
static const int ide_iobase2[2] = { 0x3f6, 0x376 };
|
|
|
|
static const int ide_irq[2] = { 14, 15 };
|
|
|
|
|
|
|
|
#define NE2000_NB_MAX 6
|
|
|
|
|
2009-09-13 16:32:37 +08:00
|
|
|
static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360,
|
|
|
|
0x280, 0x380 };
|
|
|
|
static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
|
2004-03-15 05:46:48 +08:00
|
|
|
|
2009-09-13 16:32:37 +08:00
|
|
|
static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
|
|
|
|
static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
|
2005-01-15 20:02:56 +08:00
|
|
|
|
2005-12-19 04:34:32 +08:00
|
|
|
#ifdef HAS_AUDIO
|
2007-04-08 02:14:41 +08:00
|
|
|
static void audio_init (PCIBus *pci_bus, qemu_irq *pic)
|
2005-12-19 04:34:32 +08:00
|
|
|
{
|
|
|
|
struct soundhw *c;
|
|
|
|
|
2009-05-20 07:25:22 +08:00
|
|
|
for (c = soundhw; c->name; ++c) {
|
|
|
|
if (c->enabled) {
|
|
|
|
if (c->isa) {
|
|
|
|
c->init.init_isa(pic);
|
|
|
|
} else {
|
|
|
|
if (pci_bus) {
|
|
|
|
c->init.init_pci(pci_bus);
|
2005-12-19 04:34:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-08-14 17:36:15 +08:00
|
|
|
static void pc_init_ne2k_isa(NICInfo *nd)
|
2006-02-05 12:14:41 +08:00
|
|
|
{
|
|
|
|
static int nb_ne2k = 0;
|
|
|
|
|
|
|
|
if (nb_ne2k == NE2000_NB_MAX)
|
|
|
|
return;
|
2009-08-14 17:36:15 +08:00
|
|
|
isa_ne2000_init(ne2000_io[nb_ne2k],
|
2009-09-10 17:43:33 +08:00
|
|
|
ne2000_irq[nb_ne2k], nd);
|
2006-02-05 12:14:41 +08:00
|
|
|
nb_ne2k++;
|
|
|
|
}
|
|
|
|
|
2009-06-10 20:40:48 +08:00
|
|
|
int cpu_is_bsp(CPUState *env)
|
|
|
|
{
|
2010-03-02 02:10:32 +08:00
|
|
|
/* We hard-wire the BSP to the first CPU. */
|
|
|
|
return env->cpu_index == 0;
|
2009-06-10 20:40:48 +08:00
|
|
|
}
|
|
|
|
|
2010-03-30 03:23:52 +08:00
|
|
|
/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
|
|
|
|
BIOS will read it and start S3 resume at POST Entry */
|
|
|
|
static void cmos_set_s3_resume(void *opaque, int irq, int level)
|
|
|
|
{
|
|
|
|
RTCState *s = opaque;
|
|
|
|
|
|
|
|
if (level) {
|
|
|
|
rtc_set_memory(s, 0xF, 0xFE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_smi_interrupt(void *opaque, int irq, int level)
|
|
|
|
{
|
|
|
|
CPUState *s = opaque;
|
|
|
|
|
|
|
|
if (level) {
|
|
|
|
cpu_interrupt(s, CPU_INTERRUPT_SMI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-25 14:23:39 +08:00
|
|
|
static CPUState *pc_new_cpu(const char *cpu_model)
|
|
|
|
{
|
|
|
|
CPUState *env;
|
|
|
|
|
|
|
|
env = cpu_init(cpu_model);
|
|
|
|
if (!env) {
|
|
|
|
fprintf(stderr, "Unable to find x86 CPU definition\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) {
|
|
|
|
env->cpuid_apic_id = env->cpu_index;
|
|
|
|
/* APIC reset callback resets cpu */
|
|
|
|
apic_init(env);
|
|
|
|
} else {
|
|
|
|
qemu_register_reset((QEMUResetHandler*)cpu_reset, env);
|
|
|
|
}
|
|
|
|
return env;
|
|
|
|
}
|
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
/* PC hardware initialisation */
|
2009-10-02 05:12:16 +08:00
|
|
|
static void pc_init1(ram_addr_t ram_size,
|
2009-01-17 03:04:14 +08:00
|
|
|
const char *boot_device,
|
2009-07-08 18:48:03 +08:00
|
|
|
const char *kernel_filename,
|
|
|
|
const char *kernel_cmdline,
|
2005-11-07 02:20:37 +08:00
|
|
|
const char *initrd_filename,
|
2009-07-08 18:48:03 +08:00
|
|
|
const char *cpu_model,
|
2009-07-30 18:29:12 +08:00
|
|
|
int pci_enabled)
|
2004-03-14 20:20:30 +08:00
|
|
|
{
|
2009-05-30 07:52:44 +08:00
|
|
|
char *filename;
|
2007-05-20 05:04:38 +08:00
|
|
|
int ret, linux_boot, i;
|
2009-10-02 05:12:16 +08:00
|
|
|
ram_addr_t ram_addr, bios_offset, option_rom_offset;
|
|
|
|
ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
|
2009-10-01 22:42:33 +08:00
|
|
|
int bios_size, isa_bios_size;
|
2004-06-22 03:43:00 +08:00
|
|
|
PCIBus *pci_bus;
|
2009-07-31 18:30:17 +08:00
|
|
|
ISADevice *isa_dev;
|
2006-05-14 21:44:07 +08:00
|
|
|
int piix3_devfn = -1;
|
2005-11-22 07:34:32 +08:00
|
|
|
CPUState *env;
|
2007-04-08 02:14:41 +08:00
|
|
|
qemu_irq *cpu_irq;
|
2009-08-10 00:44:55 +08:00
|
|
|
qemu_irq *isa_irq;
|
2007-04-08 02:14:41 +08:00
|
|
|
qemu_irq *i8259;
|
2010-03-30 03:23:52 +08:00
|
|
|
qemu_irq *cmos_s3;
|
|
|
|
qemu_irq *smi_irq;
|
2009-08-10 00:44:55 +08:00
|
|
|
IsaIrqState *isa_irq_state;
|
2009-08-28 21:47:03 +08:00
|
|
|
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
|
2009-09-22 19:53:18 +08:00
|
|
|
DriveInfo *fd[MAX_FD];
|
2009-06-29 21:37:38 +08:00
|
|
|
void *fw_cfg;
|
2005-07-24 03:05:37 +08:00
|
|
|
|
2008-04-28 05:12:55 +08:00
|
|
|
if (ram_size >= 0xe0000000 ) {
|
|
|
|
above_4g_mem_size = ram_size - 0xe0000000;
|
|
|
|
below_4g_mem_size = 0xe0000000;
|
|
|
|
} else {
|
|
|
|
below_4g_mem_size = ram_size;
|
|
|
|
}
|
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
linux_boot = (kernel_filename != NULL);
|
|
|
|
|
2005-11-22 07:34:32 +08:00
|
|
|
/* init CPUs */
|
2007-11-08 21:28:47 +08:00
|
|
|
if (cpu_model == NULL) {
|
|
|
|
#ifdef TARGET_X86_64
|
|
|
|
cpu_model = "qemu64";
|
|
|
|
#else
|
|
|
|
cpu_model = "qemu32";
|
|
|
|
#endif
|
|
|
|
}
|
2009-06-25 14:23:39 +08:00
|
|
|
|
|
|
|
for (i = 0; i < smp_cpus; i++) {
|
|
|
|
env = pc_new_cpu(cpu_model);
|
2005-11-22 07:34:32 +08:00
|
|
|
}
|
|
|
|
|
2008-04-08 03:47:25 +08:00
|
|
|
vmport_init();
|
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
/* allocate RAM */
|
2010-02-25 05:11:19 +08:00
|
|
|
ram_addr = qemu_ram_alloc(below_4g_mem_size);
|
2008-09-16 00:01:01 +08:00
|
|
|
cpu_register_physical_memory(0, 0xa0000, ram_addr);
|
|
|
|
cpu_register_physical_memory(0x100000,
|
|
|
|
below_4g_mem_size - 0x100000,
|
2010-02-25 05:11:19 +08:00
|
|
|
ram_addr + 0x100000);
|
2008-04-28 05:12:55 +08:00
|
|
|
|
|
|
|
/* above 4giga memory allocation */
|
|
|
|
if (above_4g_mem_size > 0) {
|
2009-05-19 23:05:00 +08:00
|
|
|
#if TARGET_PHYS_ADDR_BITS == 32
|
|
|
|
hw_error("To much RAM for 32-bit physical address");
|
|
|
|
#else
|
2008-09-16 00:01:01 +08:00
|
|
|
ram_addr = qemu_ram_alloc(above_4g_mem_size);
|
|
|
|
cpu_register_physical_memory(0x100000000ULL,
|
2008-07-16 20:13:52 +08:00
|
|
|
above_4g_mem_size,
|
2008-09-16 00:01:01 +08:00
|
|
|
ram_addr);
|
2009-05-19 23:05:00 +08:00
|
|
|
#endif
|
2008-04-28 05:12:55 +08:00
|
|
|
}
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2008-09-16 00:01:01 +08:00
|
|
|
|
2007-02-09 07:09:59 +08:00
|
|
|
/* BIOS load */
|
2007-10-05 21:08:35 +08:00
|
|
|
if (bios_name == NULL)
|
|
|
|
bios_name = BIOS_FILENAME;
|
2009-05-30 07:52:44 +08:00
|
|
|
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
|
|
|
if (filename) {
|
|
|
|
bios_size = get_image_size(filename);
|
|
|
|
} else {
|
|
|
|
bios_size = -1;
|
|
|
|
}
|
2007-09-17 05:08:06 +08:00
|
|
|
if (bios_size <= 0 ||
|
2007-02-09 07:09:59 +08:00
|
|
|
(bios_size % 65536) != 0) {
|
2004-06-20 21:43:27 +08:00
|
|
|
goto bios_error;
|
|
|
|
}
|
2007-02-09 07:09:59 +08:00
|
|
|
bios_offset = qemu_ram_alloc(bios_size);
|
2009-10-26 19:18:27 +08:00
|
|
|
ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size));
|
|
|
|
if (ret != 0) {
|
2004-06-20 21:43:27 +08:00
|
|
|
bios_error:
|
2009-05-30 07:52:44 +08:00
|
|
|
fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
|
2004-03-14 20:20:30 +08:00
|
|
|
exit(1);
|
|
|
|
}
|
2009-05-30 07:52:44 +08:00
|
|
|
if (filename) {
|
|
|
|
qemu_free(filename);
|
|
|
|
}
|
2004-06-20 21:43:27 +08:00
|
|
|
/* map the last 128KB of the BIOS in ISA space */
|
|
|
|
isa_bios_size = bios_size;
|
|
|
|
if (isa_bios_size > (128 * 1024))
|
|
|
|
isa_bios_size = 128 * 1024;
|
2007-09-17 05:08:06 +08:00
|
|
|
cpu_register_physical_memory(0x100000 - isa_bios_size,
|
|
|
|
isa_bios_size,
|
2004-06-20 21:43:27 +08:00
|
|
|
(bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
|
2007-01-06 01:39:04 +08:00
|
|
|
|
2009-10-01 22:42:33 +08:00
|
|
|
option_rom_offset = qemu_ram_alloc(PC_ROM_SIZE);
|
|
|
|
cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset);
|
2009-04-10 04:59:05 +08:00
|
|
|
|
2009-06-29 21:37:37 +08:00
|
|
|
/* map all the bios at the top of memory */
|
|
|
|
cpu_register_physical_memory((uint32_t)(-bios_size),
|
|
|
|
bios_size, bios_offset | IO_MEM_ROM);
|
|
|
|
|
2009-06-29 21:37:38 +08:00
|
|
|
fw_cfg = bochs_bios_init();
|
2010-01-08 22:25:40 +08:00
|
|
|
rom_set_fw(fw_cfg);
|
2009-06-29 21:37:37 +08:00
|
|
|
|
2009-04-10 04:59:05 +08:00
|
|
|
if (linux_boot) {
|
2009-10-01 22:42:33 +08:00
|
|
|
load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
|
2009-04-10 04:59:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < nb_option_roms; i++) {
|
2009-10-01 22:42:33 +08:00
|
|
|
rom_add_option(option_rom[i]);
|
2009-06-17 21:05:30 +08:00
|
|
|
}
|
|
|
|
|
2008-04-14 00:08:30 +08:00
|
|
|
cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1);
|
2007-04-08 02:14:41 +08:00
|
|
|
i8259 = i8259_init(cpu_irq[0]);
|
2009-08-10 00:44:55 +08:00
|
|
|
isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state));
|
|
|
|
isa_irq_state->i8259 = i8259;
|
2009-08-10 00:44:56 +08:00
|
|
|
isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24);
|
2007-04-08 02:14:41 +08:00
|
|
|
|
2004-05-19 07:05:28 +08:00
|
|
|
if (pci_enabled) {
|
2009-08-28 21:28:20 +08:00
|
|
|
pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq);
|
2004-06-22 03:43:00 +08:00
|
|
|
} else {
|
|
|
|
pci_bus = NULL;
|
2009-08-14 17:36:14 +08:00
|
|
|
isa_bus_new(NULL);
|
2004-05-19 07:05:28 +08:00
|
|
|
}
|
2009-08-14 17:36:14 +08:00
|
|
|
isa_bus_irqs(isa_irq);
|
2004-05-19 07:05:28 +08:00
|
|
|
|
2009-08-14 17:36:15 +08:00
|
|
|
ferr_irq = isa_reserve_irq(13);
|
|
|
|
|
2004-03-14 20:20:30 +08:00
|
|
|
/* init basic PC hardware */
|
2004-03-15 05:46:48 +08:00
|
|
|
register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2004-05-09 05:03:41 +08:00
|
|
|
register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
|
|
|
|
|
2004-06-05 21:46:47 +08:00
|
|
|
if (cirrus_vga_enabled) {
|
|
|
|
if (pci_enabled) {
|
2009-05-14 00:56:25 +08:00
|
|
|
pci_cirrus_vga_init(pci_bus);
|
2004-06-05 21:46:47 +08:00
|
|
|
} else {
|
2009-05-14 00:56:25 +08:00
|
|
|
isa_cirrus_vga_init();
|
2004-06-05 21:46:47 +08:00
|
|
|
}
|
2007-04-02 09:10:46 +08:00
|
|
|
} else if (vmsvga_enabled) {
|
|
|
|
if (pci_enabled)
|
2009-05-14 00:56:25 +08:00
|
|
|
pci_vmsvga_init(pci_bus);
|
2007-04-02 09:10:46 +08:00
|
|
|
else
|
|
|
|
fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
|
2009-01-16 04:37:28 +08:00
|
|
|
} else if (std_vga_enabled) {
|
2006-08-17 18:45:20 +08:00
|
|
|
if (pci_enabled) {
|
2009-05-14 00:56:25 +08:00
|
|
|
pci_vga_init(pci_bus, 0, 0);
|
2006-08-17 18:45:20 +08:00
|
|
|
} else {
|
2009-05-14 00:56:25 +08:00
|
|
|
isa_vga_init();
|
2006-08-17 18:45:20 +08:00
|
|
|
}
|
2004-06-05 21:46:47 +08:00
|
|
|
}
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2009-09-10 17:43:35 +08:00
|
|
|
rtc_state = rtc_init(2000);
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2008-06-21 00:25:06 +08:00
|
|
|
qemu_register_boot_set(pc_boot_set, rtc_state);
|
|
|
|
|
2004-04-06 04:26:03 +08:00
|
|
|
register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
|
|
|
|
register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
|
|
|
|
|
2005-07-24 03:05:37 +08:00
|
|
|
if (pci_enabled) {
|
2009-08-10 00:44:56 +08:00
|
|
|
isa_irq_state->ioapic = ioapic_init();
|
2005-07-24 03:05:37 +08:00
|
|
|
}
|
2009-08-14 17:36:15 +08:00
|
|
|
pit = pit_init(0x40, isa_reserve_irq(0));
|
2006-04-25 05:58:30 +08:00
|
|
|
pcspk_init(pit);
|
2008-12-18 07:28:44 +08:00
|
|
|
if (!no_hpet) {
|
2009-08-10 00:44:55 +08:00
|
|
|
hpet_init(isa_irq);
|
2008-12-18 07:28:44 +08:00
|
|
|
}
|
2004-03-15 05:46:48 +08:00
|
|
|
|
2004-08-25 05:13:40 +08:00
|
|
|
for(i = 0; i < MAX_SERIAL_PORTS; i++) {
|
|
|
|
if (serial_hds[i]) {
|
2009-09-22 19:53:21 +08:00
|
|
|
serial_isa_init(i, serial_hds[i]);
|
2004-08-25 05:13:40 +08:00
|
|
|
}
|
|
|
|
}
|
2004-03-15 05:46:48 +08:00
|
|
|
|
2005-01-15 20:02:56 +08:00
|
|
|
for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
|
|
|
|
if (parallel_hds[i]) {
|
2009-09-22 19:53:22 +08:00
|
|
|
parallel_init(i, parallel_hds[i]);
|
2005-01-15 20:02:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-05 12:14:41 +08:00
|
|
|
for(i = 0; i < nb_nics; i++) {
|
2009-01-14 03:47:10 +08:00
|
|
|
NICInfo *nd = &nd_table[i];
|
|
|
|
|
|
|
|
if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0))
|
2009-08-14 17:36:15 +08:00
|
|
|
pc_init_ne2k_isa(nd);
|
2009-01-14 03:47:10 +08:00
|
|
|
else
|
2009-09-25 09:53:51 +08:00
|
|
|
pci_nic_init_nofail(nd, "e1000", NULL);
|
2006-02-05 12:14:41 +08:00
|
|
|
}
|
2004-03-15 05:46:48 +08:00
|
|
|
|
2007-12-02 12:51:10 +08:00
|
|
|
if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
|
|
|
|
fprintf(stderr, "qemu: too many IDE bus\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) {
|
2009-08-28 21:47:03 +08:00
|
|
|
hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS);
|
2007-12-02 12:51:10 +08:00
|
|
|
}
|
|
|
|
|
2006-02-05 12:14:41 +08:00
|
|
|
if (pci_enabled) {
|
2009-08-29 01:37:00 +08:00
|
|
|
pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1);
|
2006-02-05 12:14:41 +08:00
|
|
|
} else {
|
2007-12-02 12:51:10 +08:00
|
|
|
for(i = 0; i < MAX_IDE_BUS; i++) {
|
2009-09-16 04:05:00 +08:00
|
|
|
isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i],
|
2007-12-02 12:51:10 +08:00
|
|
|
hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]);
|
2004-05-19 07:05:28 +08:00
|
|
|
}
|
2004-03-15 05:46:48 +08:00
|
|
|
}
|
2004-05-19 07:05:28 +08:00
|
|
|
|
2009-09-10 17:43:27 +08:00
|
|
|
isa_dev = isa_create_simple("i8042");
|
2004-06-22 00:58:50 +08:00
|
|
|
DMA_init(0);
|
2005-12-19 04:34:32 +08:00
|
|
|
#ifdef HAS_AUDIO
|
2009-08-10 00:44:55 +08:00
|
|
|
audio_init(pci_enabled ? pci_bus : NULL, isa_irq);
|
2004-11-10 07:09:44 +08:00
|
|
|
#endif
|
2004-03-14 20:20:30 +08:00
|
|
|
|
2007-12-02 12:51:10 +08:00
|
|
|
for(i = 0; i < MAX_FD; i++) {
|
2009-09-22 19:53:18 +08:00
|
|
|
fd[i] = drive_get(IF_FLOPPY, 0, i);
|
2007-12-02 12:51:10 +08:00
|
|
|
}
|
2009-09-10 17:43:26 +08:00
|
|
|
floppy_controller = fdctrl_init_isa(fd);
|
2004-03-15 05:46:48 +08:00
|
|
|
|
2008-04-28 05:12:55 +08:00
|
|
|
cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, hd);
|
2004-05-19 07:05:28 +08:00
|
|
|
|
2005-11-05 22:22:28 +08:00
|
|
|
if (pci_enabled && usb_enabled) {
|
2007-06-07 00:26:14 +08:00
|
|
|
usb_uhci_piix3_init(pci_bus, piix3_devfn + 2);
|
2005-11-05 22:22:28 +08:00
|
|
|
}
|
|
|
|
|
2006-05-04 06:02:44 +08:00
|
|
|
if (pci_enabled && acpi_enabled) {
|
2007-02-02 11:13:18 +08:00
|
|
|
uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */
|
2007-05-23 08:03:59 +08:00
|
|
|
i2c_bus *smbus;
|
|
|
|
|
2010-03-30 03:23:52 +08:00
|
|
|
cmos_s3 = qemu_allocate_irqs(cmos_set_s3_resume, rtc_state, 1);
|
|
|
|
smi_irq = qemu_allocate_irqs(acpi_smi_interrupt, first_cpu, 1);
|
2007-05-23 08:03:59 +08:00
|
|
|
/* TODO: Populate SPD eeprom data. */
|
2009-08-14 17:36:15 +08:00
|
|
|
smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100,
|
2010-03-30 03:23:52 +08:00
|
|
|
isa_reserve_irq(9), *cmos_s3, *smi_irq,
|
|
|
|
kvm_enabled());
|
2007-02-02 11:13:18 +08:00
|
|
|
for (i = 0; i < 8; i++) {
|
2009-05-15 05:35:08 +08:00
|
|
|
DeviceState *eeprom;
|
2009-05-23 07:05:19 +08:00
|
|
|
eeprom = qdev_create((BusState *)smbus, "smbus-eeprom");
|
2009-09-30 04:48:26 +08:00
|
|
|
qdev_prop_set_uint8(eeprom, "address", 0x50 + i);
|
2009-07-15 19:43:31 +08:00
|
|
|
qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256));
|
2009-10-07 07:15:58 +08:00
|
|
|
qdev_init_nofail(eeprom);
|
2007-02-02 11:13:18 +08:00
|
|
|
}
|
2009-09-26 03:42:45 +08:00
|
|
|
piix4_acpi_system_hot_add_init(pci_bus);
|
2006-05-04 06:02:44 +08:00
|
|
|
}
|
2007-09-17 16:09:54 +08:00
|
|
|
|
2006-09-25 02:48:00 +08:00
|
|
|
if (i440fx_state) {
|
|
|
|
i440fx_init_memory_mappings(i440fx_state);
|
|
|
|
}
|
2007-12-02 12:51:10 +08:00
|
|
|
|
2006-05-30 09:48:12 +08:00
|
|
|
if (pci_enabled) {
|
2007-12-02 12:51:10 +08:00
|
|
|
int max_bus;
|
2009-05-15 05:35:07 +08:00
|
|
|
int bus;
|
2007-01-08 04:42:14 +08:00
|
|
|
|
2007-12-02 12:51:10 +08:00
|
|
|
max_bus = drive_get_max_bus(IF_SCSI);
|
|
|
|
for (bus = 0; bus <= max_bus; bus++) {
|
2009-05-15 05:35:07 +08:00
|
|
|
pci_create_simple(pci_bus, -1, "lsi53c895a");
|
2007-12-02 12:51:10 +08:00
|
|
|
}
|
2006-05-30 09:48:12 +08:00
|
|
|
}
|
2004-03-14 20:20:30 +08:00
|
|
|
}
|
2005-06-05 22:51:11 +08:00
|
|
|
|
2009-10-02 05:12:16 +08:00
|
|
|
static void pc_init_pci(ram_addr_t ram_size,
|
2009-01-17 03:04:14 +08:00
|
|
|
const char *boot_device,
|
2007-09-17 05:08:06 +08:00
|
|
|
const char *kernel_filename,
|
2005-11-07 02:20:37 +08:00
|
|
|
const char *kernel_cmdline,
|
2007-03-06 03:44:02 +08:00
|
|
|
const char *initrd_filename,
|
|
|
|
const char *cpu_model)
|
2005-11-07 02:20:37 +08:00
|
|
|
{
|
2009-05-14 00:56:25 +08:00
|
|
|
pc_init1(ram_size, boot_device,
|
2005-11-07 02:20:37 +08:00
|
|
|
kernel_filename, kernel_cmdline,
|
2009-07-30 18:29:12 +08:00
|
|
|
initrd_filename, cpu_model, 1);
|
2005-11-07 02:20:37 +08:00
|
|
|
}
|
|
|
|
|
2009-10-02 05:12:16 +08:00
|
|
|
static void pc_init_isa(ram_addr_t ram_size,
|
2009-01-17 03:04:14 +08:00
|
|
|
const char *boot_device,
|
2007-09-17 05:08:06 +08:00
|
|
|
const char *kernel_filename,
|
2005-11-07 02:20:37 +08:00
|
|
|
const char *kernel_cmdline,
|
2007-03-06 03:44:02 +08:00
|
|
|
const char *initrd_filename,
|
|
|
|
const char *cpu_model)
|
2005-11-07 02:20:37 +08:00
|
|
|
{
|
2009-09-10 17:43:24 +08:00
|
|
|
if (cpu_model == NULL)
|
|
|
|
cpu_model = "486";
|
2009-05-14 00:56:25 +08:00
|
|
|
pc_init1(ram_size, boot_device,
|
2005-11-07 02:20:37 +08:00
|
|
|
kernel_filename, kernel_cmdline,
|
2009-07-30 18:29:12 +08:00
|
|
|
initrd_filename, cpu_model, 0);
|
2005-11-07 02:20:37 +08:00
|
|
|
}
|
|
|
|
|
2009-05-21 07:38:09 +08:00
|
|
|
static QEMUMachine pc_machine = {
|
2010-02-15 23:13:54 +08:00
|
|
|
.name = "pc-0.13",
|
2009-07-22 17:02:51 +08:00
|
|
|
.alias = "pc",
|
2008-08-16 05:30:28 +08:00
|
|
|
.desc = "Standard PC",
|
|
|
|
.init = pc_init_pci,
|
2008-10-08 04:39:39 +08:00
|
|
|
.max_cpus = 255,
|
2009-05-22 09:41:01 +08:00
|
|
|
.is_default = 1,
|
2005-11-07 02:20:37 +08:00
|
|
|
};
|
|
|
|
|
2010-02-15 23:13:54 +08:00
|
|
|
static QEMUMachine pc_machine_v0_12 = {
|
|
|
|
.name = "pc-0.12",
|
|
|
|
.desc = "Standard PC",
|
|
|
|
.init = pc_init_pci,
|
|
|
|
.max_cpus = 255,
|
2010-02-15 23:13:55 +08:00
|
|
|
.compat_props = (GlobalProperty[]) {
|
|
|
|
{
|
|
|
|
.driver = "virtio-serial-pci",
|
|
|
|
.property = "max_nr_ports",
|
|
|
|
.value = stringify(1),
|
|
|
|
},{
|
|
|
|
.driver = "virtio-serial-pci",
|
|
|
|
.property = "vectors",
|
|
|
|
.value = stringify(0),
|
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
}
|
2010-02-15 23:13:54 +08:00
|
|
|
};
|
|
|
|
|
2010-01-08 22:25:37 +08:00
|
|
|
static QEMUMachine pc_machine_v0_11 = {
|
|
|
|
.name = "pc-0.11",
|
|
|
|
.desc = "Standard PC, qemu 0.11",
|
|
|
|
.init = pc_init_pci,
|
|
|
|
.max_cpus = 255,
|
|
|
|
.compat_props = (GlobalProperty[]) {
|
|
|
|
{
|
|
|
|
.driver = "virtio-blk-pci",
|
|
|
|
.property = "vectors",
|
|
|
|
.value = stringify(0),
|
2010-02-15 23:13:55 +08:00
|
|
|
},{
|
|
|
|
.driver = "virtio-serial-pci",
|
|
|
|
.property = "max_nr_ports",
|
|
|
|
.value = stringify(1),
|
|
|
|
},{
|
|
|
|
.driver = "virtio-serial-pci",
|
|
|
|
.property = "vectors",
|
|
|
|
.value = stringify(0),
|
2010-01-14 21:44:14 +08:00
|
|
|
},{
|
|
|
|
.driver = "ide-drive",
|
|
|
|
.property = "ver",
|
|
|
|
.value = "0.11",
|
|
|
|
},{
|
|
|
|
.driver = "scsi-disk",
|
|
|
|
.property = "ver",
|
|
|
|
.value = "0.11",
|
2010-01-08 22:25:42 +08:00
|
|
|
},{
|
|
|
|
.driver = "PCI",
|
|
|
|
.property = "rombar",
|
|
|
|
.value = stringify(0),
|
2010-01-08 22:25:37 +08:00
|
|
|
},
|
|
|
|
{ /* end of list */ }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-07-15 19:48:22 +08:00
|
|
|
static QEMUMachine pc_machine_v0_10 = {
|
|
|
|
.name = "pc-0.10",
|
|
|
|
.desc = "Standard PC, qemu 0.10",
|
|
|
|
.init = pc_init_pci,
|
|
|
|
.max_cpus = 255,
|
2009-12-08 20:11:33 +08:00
|
|
|
.compat_props = (GlobalProperty[]) {
|
2009-07-15 19:48:23 +08:00
|
|
|
{
|
|
|
|
.driver = "virtio-blk-pci",
|
|
|
|
.property = "class",
|
|
|
|
.value = stringify(PCI_CLASS_STORAGE_OTHER),
|
2009-07-15 19:48:24 +08:00
|
|
|
},{
|
virtio-console: qdev conversion, new virtio-serial-bus
This commit converts the virtio-console device to create a new
virtio-serial bus that can host console and generic serial ports. The
file hosting this code is now called virtio-serial-bus.c.
The virtio console is now a very simple qdev device that sits on the
virtio-serial-bus and communicates between the bus and qemu's chardevs.
This commit also includes a few changes to the virtio backing code for
pci and s390 to spawn the virtio-serial bus.
As a result of the qdev conversion, we get rid of a lot of legacy code.
The old-style way of instantiating a virtio console using
-virtioconsole ...
is maintained, but the new, preferred way is to use
-device virtio-serial -device virtconsole,chardev=...
With this commit, multiple devices as well as multiple ports with a
single device can be supported.
For multiple ports support, each port gets an IO vq pair. Since the
guest needs to know in advance how many vqs a particular device will
need, we have to set this number as a property of the virtio-serial
device and also as a config option.
In addition, we also spawn a pair of control IO vqs. This is an internal
channel meant for guest-host communication for things like port
open/close, sending port properties over to the guest, etc.
This commit is a part of a series of other commits to get the full
implementation of multiport support. Future commits will add other
support as well as ride on the savevm version that we bump up here.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-01-20 03:06:52 +08:00
|
|
|
.driver = "virtio-serial-pci",
|
2009-07-15 19:48:24 +08:00
|
|
|
.property = "class",
|
|
|
|
.value = stringify(PCI_CLASS_DISPLAY_OTHER),
|
2010-02-15 23:13:55 +08:00
|
|
|
},{
|
|
|
|
.driver = "virtio-serial-pci",
|
|
|
|
.property = "max_nr_ports",
|
|
|
|
.value = stringify(1),
|
|
|
|
},{
|
|
|
|
.driver = "virtio-serial-pci",
|
|
|
|
.property = "vectors",
|
|
|
|
.value = stringify(0),
|
2009-07-15 19:48:25 +08:00
|
|
|
},{
|
|
|
|
.driver = "virtio-net-pci",
|
|
|
|
.property = "vectors",
|
|
|
|
.value = stringify(0),
|
2009-08-12 18:47:24 +08:00
|
|
|
},{
|
|
|
|
.driver = "virtio-blk-pci",
|
|
|
|
.property = "vectors",
|
|
|
|
.value = stringify(0),
|
2010-01-14 21:44:14 +08:00
|
|
|
},{
|
|
|
|
.driver = "ide-drive",
|
|
|
|
.property = "ver",
|
|
|
|
.value = "0.10",
|
|
|
|
},{
|
|
|
|
.driver = "scsi-disk",
|
|
|
|
.property = "ver",
|
|
|
|
.value = "0.10",
|
2010-01-08 22:25:42 +08:00
|
|
|
},{
|
|
|
|
.driver = "PCI",
|
|
|
|
.property = "rombar",
|
|
|
|
.value = stringify(0),
|
2009-07-15 19:48:23 +08:00
|
|
|
},
|
2009-07-15 19:48:22 +08:00
|
|
|
{ /* end of list */ }
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2009-05-21 07:38:09 +08:00
|
|
|
static QEMUMachine isapc_machine = {
|
2008-08-16 05:30:28 +08:00
|
|
|
.name = "isapc",
|
|
|
|
.desc = "ISA-only PC",
|
|
|
|
.init = pc_init_isa,
|
2008-10-08 04:39:39 +08:00
|
|
|
.max_cpus = 1,
|
2005-06-05 22:51:11 +08:00
|
|
|
};
|
2009-05-21 07:38:09 +08:00
|
|
|
|
|
|
|
static void pc_machine_init(void)
|
|
|
|
{
|
|
|
|
qemu_register_machine(&pc_machine);
|
2010-02-15 23:13:54 +08:00
|
|
|
qemu_register_machine(&pc_machine_v0_12);
|
2010-01-08 22:25:37 +08:00
|
|
|
qemu_register_machine(&pc_machine_v0_11);
|
2009-07-15 19:48:22 +08:00
|
|
|
qemu_register_machine(&pc_machine_v0_10);
|
2009-05-21 07:38:09 +08:00
|
|
|
qemu_register_machine(&isapc_machine);
|
|
|
|
}
|
|
|
|
|
|
|
|
machine_init(pc_machine_init);
|