#define INITRD_LOAD_ADDR 0x02800000
#define FDT_MAX_SIZE 0x10000
#define RTAS_MAX_SIZE 0x10000
+#define FW_MAX_SIZE 0x400000
+#define FW_FILE_NAME "slof.bin"
+
+#define MIN_RAM_SLOF 512UL
#define TIMEBASE_FREQ 512000000ULL
sPAPREnvironment *spapr,
target_phys_addr_t initrd_base,
target_phys_addr_t initrd_size,
+ const char *boot_device,
const char *kernel_cmdline,
target_phys_addr_t rtas_addr,
target_phys_addr_t rtas_size,
&start_prop, sizeof(start_prop))));
_FDT((fdt_property(fdt, "linux,initrd-end",
&end_prop, sizeof(end_prop))));
+ _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
_FDT((fdt_end_node(fdt)));
ram_addr_t ram_offset;
target_phys_addr_t fdt_addr, rtas_addr;
uint32_t kernel_base, initrd_base;
- long kernel_size, initrd_size, htab_size, rtas_size;
+ long kernel_size, initrd_size, htab_size, rtas_size, fw_size;
long pteg_shift = 17;
int fdt_size;
char *filename;
initrd_size = 0;
}
} else {
- fprintf(stderr, "pSeries machine needs -kernel for now");
- exit(1);
+ if (ram_size < (MIN_RAM_SLOF << 20)) {
+ fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
+ "%ldM guest RAM\n", MIN_RAM_SLOF);
+ exit(1);
+ }
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin");
+ fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
+ if (fw_size < 0) {
+ hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
+ exit(1);
+ }
+ qemu_free(filename);
+ kernel_base = 0x100;
+ initrd_base = 0;
+ initrd_size = 0;
+
+ /* SLOF will startup the secondary CPUs using RTAS,
+ rather than expecting a kexec() style entry */
+ for (i = 0; i < smp_cpus; i++) {
+ envs[i]->halted = 1;
+ }
}
/* Prepare the device tree */
fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, envs, spapr,
- initrd_base, initrd_size, kernel_cmdline,
+ initrd_base, initrd_size,
+ boot_device, kernel_cmdline,
rtas_addr, rtas_size, pteg_shift + 7);
assert(fdt != NULL);
envs[0]->gpr[3] = fdt_addr;
envs[0]->gpr[5] = 0;
envs[0]->hreset_vector = kernel_base;
+ envs[0]->halted = 0;
}
static QEMUMachine spapr_machine = {
rtas_st(rets, 0, 0);
}
+static void rtas_query_cpu_stopped_state(sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ target_ulong id;
+ CPUState *env;
+
+ if (nargs != 1 || nret != 2) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ id = rtas_ld(args, 0);
+ for (env = first_cpu; env; env = env->next_cpu) {
+ if (env->cpu_index != id) {
+ continue;
+ }
+
+ if (env->halted) {
+ rtas_st(rets, 1, 0);
+ } else {
+ rtas_st(rets, 1, 2);
+ }
+
+ rtas_st(rets, 0, 0);
+ return;
+ }
+
+ /* Didn't find a matching cpu */
+ rtas_st(rets, 0, -3);
+}
+
+static void rtas_start_cpu(sPAPREnvironment *spapr,
+ uint32_t token, uint32_t nargs,
+ target_ulong args,
+ uint32_t nret, target_ulong rets)
+{
+ target_ulong id, start, r3;
+ CPUState *env;
+
+ if (nargs != 3 || nret != 1) {
+ rtas_st(rets, 0, -3);
+ return;
+ }
+
+ id = rtas_ld(args, 0);
+ start = rtas_ld(args, 1);
+ r3 = rtas_ld(args, 2);
+
+ for (env = first_cpu; env; env = env->next_cpu) {
+ if (env->cpu_index != id) {
+ continue;
+ }
+
+ if (!env->halted) {
+ rtas_st(rets, 0, -1);
+ return;
+ }
+
+ env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME);
+ env->nip = start;
+ env->gpr[3] = r3;
+ env->halted = 0;
+
+ qemu_cpu_kick(env);
+
+ rtas_st(rets, 0, 0);
+ return;
+ }
+
+ /* Didn't find a matching cpu */
+ rtas_st(rets, 0, -3);
+}
+
static struct rtas_call {
const char *name;
spapr_rtas_fn fn;
spapr_rtas_register("display-character", rtas_display_character);
spapr_rtas_register("get-time-of-day", rtas_get_time_of_day);
spapr_rtas_register("power-off", rtas_power_off);
+ spapr_rtas_register("query-cpu-stopped-state",
+ rtas_query_cpu_stopped_state);
+ spapr_rtas_register("start-cpu", rtas_start_cpu);
}
device_init(register_core_rtas);