Annotation of qemu/hw/spapr.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
        !             3:  *
        !             4:  * Copyright (c) 2004-2007 Fabrice Bellard
        !             5:  * Copyright (c) 2007 Jocelyn Mayer
        !             6:  * Copyright (c) 2010 David Gibson, IBM Corporation.
        !             7:  *
        !             8:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !             9:  * of this software and associated documentation files (the "Software"), to deal
        !            10:  * in the Software without restriction, including without limitation the rights
        !            11:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            12:  * copies of the Software, and to permit persons to whom the Software is
        !            13:  * furnished to do so, subject to the following conditions:
        !            14:  *
        !            15:  * The above copyright notice and this permission notice shall be included in
        !            16:  * all copies or substantial portions of the Software.
        !            17:  *
        !            18:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            19:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            20:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            21:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            22:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            23:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            24:  * THE SOFTWARE.
        !            25:  *
        !            26:  */
        !            27: #include "sysemu.h"
        !            28: #include "hw.h"
        !            29: #include "elf.h"
        !            30: #include "net.h"
        !            31: #include "blockdev.h"
        !            32: 
        !            33: #include "hw/boards.h"
        !            34: #include "hw/ppc.h"
        !            35: #include "hw/loader.h"
        !            36: 
        !            37: #include "hw/spapr.h"
        !            38: #include "hw/spapr_vio.h"
        !            39: #include "hw/xics.h"
        !            40: 
        !            41: #include <libfdt.h>
        !            42: 
        !            43: #define KERNEL_LOAD_ADDR        0x00000000
        !            44: #define INITRD_LOAD_ADDR        0x02800000
        !            45: #define FDT_MAX_SIZE            0x10000
        !            46: #define RTAS_MAX_SIZE           0x10000
        !            47: #define FW_MAX_SIZE             0x400000
        !            48: #define FW_FILE_NAME            "slof.bin"
        !            49: 
        !            50: #define MIN_RAM_SLOF           512UL
        !            51: 
        !            52: #define TIMEBASE_FREQ           512000000ULL
        !            53: 
        !            54: #define MAX_CPUS                256
        !            55: #define XICS_IRQS              1024
        !            56: 
        !            57: sPAPREnvironment *spapr;
        !            58: 
        !            59: static void *spapr_create_fdt_skel(const char *cpu_model,
        !            60:                                    target_phys_addr_t initrd_base,
        !            61:                                    target_phys_addr_t initrd_size,
        !            62:                                    const char *boot_device,
        !            63:                                    const char *kernel_cmdline,
        !            64:                                    long hash_shift)
        !            65: {
        !            66:     void *fdt;
        !            67:     CPUState *env;
        !            68:     uint64_t mem_reg_property[] = { 0, cpu_to_be64(ram_size) };
        !            69:     uint32_t start_prop = cpu_to_be32(initrd_base);
        !            70:     uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
        !            71:     uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
        !            72:     char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
        !            73:         "\0hcall-tce\0hcall-vio\0hcall-splpar";
        !            74:     uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
        !            75:     int i;
        !            76:     char *modelname;
        !            77: 
        !            78: #define _FDT(exp) \
        !            79:     do { \
        !            80:         int ret = (exp);                                           \
        !            81:         if (ret < 0) {                                             \
        !            82:             fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
        !            83:                     #exp, fdt_strerror(ret));                      \
        !            84:             exit(1);                                               \
        !            85:         }                                                          \
        !            86:     } while (0)
        !            87: 
        !            88:     fdt = qemu_mallocz(FDT_MAX_SIZE);
        !            89:     _FDT((fdt_create(fdt, FDT_MAX_SIZE)));
        !            90: 
        !            91:     _FDT((fdt_finish_reservemap(fdt)));
        !            92: 
        !            93:     /* Root node */
        !            94:     _FDT((fdt_begin_node(fdt, "")));
        !            95:     _FDT((fdt_property_string(fdt, "device_type", "chrp")));
        !            96:     _FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)")));
        !            97: 
        !            98:     _FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
        !            99:     _FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
        !           100: 
        !           101:     /* /chosen */
        !           102:     _FDT((fdt_begin_node(fdt, "chosen")));
        !           103: 
        !           104:     _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline)));
        !           105:     _FDT((fdt_property(fdt, "linux,initrd-start",
        !           106:                        &start_prop, sizeof(start_prop))));
        !           107:     _FDT((fdt_property(fdt, "linux,initrd-end",
        !           108:                        &end_prop, sizeof(end_prop))));
        !           109:     _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
        !           110: 
        !           111:     _FDT((fdt_end_node(fdt)));
        !           112: 
        !           113:     /* memory node */
        !           114:     _FDT((fdt_begin_node(fdt, "memory@0")));
        !           115: 
        !           116:     _FDT((fdt_property_string(fdt, "device_type", "memory")));
        !           117:     _FDT((fdt_property(fdt, "reg",
        !           118:                        mem_reg_property, sizeof(mem_reg_property))));
        !           119: 
        !           120:     _FDT((fdt_end_node(fdt)));
        !           121: 
        !           122:     /* cpus */
        !           123:     _FDT((fdt_begin_node(fdt, "cpus")));
        !           124: 
        !           125:     _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
        !           126:     _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
        !           127: 
        !           128:     modelname = qemu_strdup(cpu_model);
        !           129: 
        !           130:     for (i = 0; i < strlen(modelname); i++) {
        !           131:         modelname[i] = toupper(modelname[i]);
        !           132:     }
        !           133: 
        !           134:     for (env = first_cpu; env != NULL; env = env->next_cpu) {
        !           135:         int index = env->cpu_index;
        !           136:         uint32_t gserver_prop[] = {cpu_to_be32(index), 0}; /* HACK! */
        !           137:         char *nodename;
        !           138:         uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
        !           139:                            0xffffffff, 0xffffffff};
        !           140: 
        !           141:         if (asprintf(&nodename, "%s@%x", modelname, index) < 0) {
        !           142:             fprintf(stderr, "Allocation failure\n");
        !           143:             exit(1);
        !           144:         }
        !           145: 
        !           146:         _FDT((fdt_begin_node(fdt, nodename)));
        !           147: 
        !           148:         free(nodename);
        !           149: 
        !           150:         _FDT((fdt_property_cell(fdt, "reg", index)));
        !           151:         _FDT((fdt_property_string(fdt, "device_type", "cpu")));
        !           152: 
        !           153:         _FDT((fdt_property_cell(fdt, "cpu-version", env->spr[SPR_PVR])));
        !           154:         _FDT((fdt_property_cell(fdt, "dcache-block-size",
        !           155:                                 env->dcache_line_size)));
        !           156:         _FDT((fdt_property_cell(fdt, "icache-block-size",
        !           157:                                 env->icache_line_size)));
        !           158:         _FDT((fdt_property_cell(fdt, "timebase-frequency", TIMEBASE_FREQ)));
        !           159:         /* Hardcode CPU frequency for now.  It's kind of arbitrary on
        !           160:          * full emu, for kvm we should copy it from the host */
        !           161:         _FDT((fdt_property_cell(fdt, "clock-frequency", 1000000000)));
        !           162:         _FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr)));
        !           163:         _FDT((fdt_property(fdt, "ibm,pft-size",
        !           164:                            pft_size_prop, sizeof(pft_size_prop))));
        !           165:         _FDT((fdt_property_string(fdt, "status", "okay")));
        !           166:         _FDT((fdt_property(fdt, "64-bit", NULL, 0)));
        !           167:         _FDT((fdt_property_cell(fdt, "ibm,ppc-interrupt-server#s", index)));
        !           168:         _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
        !           169:                            gserver_prop, sizeof(gserver_prop))));
        !           170: 
        !           171:         if (env->mmu_model & POWERPC_MMU_1TSEG) {
        !           172:             _FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
        !           173:                                segs, sizeof(segs))));
        !           174:         }
        !           175: 
        !           176:         _FDT((fdt_end_node(fdt)));
        !           177:     }
        !           178: 
        !           179:     qemu_free(modelname);
        !           180: 
        !           181:     _FDT((fdt_end_node(fdt)));
        !           182: 
        !           183:     /* RTAS */
        !           184:     _FDT((fdt_begin_node(fdt, "rtas")));
        !           185: 
        !           186:     _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop,
        !           187:                        sizeof(hypertas_prop))));
        !           188: 
        !           189:     _FDT((fdt_end_node(fdt)));
        !           190: 
        !           191:     /* interrupt controller */
        !           192:     _FDT((fdt_begin_node(fdt, "interrupt-controller@0")));
        !           193: 
        !           194:     _FDT((fdt_property_string(fdt, "device_type",
        !           195:                               "PowerPC-External-Interrupt-Presentation")));
        !           196:     _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp")));
        !           197:     _FDT((fdt_property_cell(fdt, "reg", 0)));
        !           198:     _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
        !           199:     _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges",
        !           200:                        interrupt_server_ranges_prop,
        !           201:                        sizeof(interrupt_server_ranges_prop))));
        !           202: 
        !           203:     _FDT((fdt_end_node(fdt)));
        !           204: 
        !           205:     /* vdevice */
        !           206:     _FDT((fdt_begin_node(fdt, "vdevice")));
        !           207: 
        !           208:     _FDT((fdt_property_string(fdt, "device_type", "vdevice")));
        !           209:     _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice")));
        !           210:     _FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
        !           211:     _FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
        !           212:     _FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2)));
        !           213:     _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
        !           214: 
        !           215:     _FDT((fdt_end_node(fdt)));
        !           216: 
        !           217:     _FDT((fdt_end_node(fdt))); /* close root node */
        !           218:     _FDT((fdt_finish(fdt)));
        !           219: 
        !           220:     return fdt;
        !           221: }
        !           222: 
        !           223: static void spapr_finalize_fdt(sPAPREnvironment *spapr,
        !           224:                                target_phys_addr_t fdt_addr,
        !           225:                                target_phys_addr_t rtas_addr,
        !           226:                                target_phys_addr_t rtas_size)
        !           227: {
        !           228:     int ret;
        !           229:     void *fdt;
        !           230: 
        !           231:     fdt = qemu_malloc(FDT_MAX_SIZE);
        !           232: 
        !           233:     /* open out the base tree into a temp buffer for the final tweaks */
        !           234:     _FDT((fdt_open_into(spapr->fdt_skel, fdt, FDT_MAX_SIZE)));
        !           235: 
        !           236:     ret = spapr_populate_vdevice(spapr->vio_bus, fdt);
        !           237:     if (ret < 0) {
        !           238:         fprintf(stderr, "couldn't setup vio devices in fdt\n");
        !           239:         exit(1);
        !           240:     }
        !           241: 
        !           242:     /* RTAS */
        !           243:     ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size);
        !           244:     if (ret < 0) {
        !           245:         fprintf(stderr, "Couldn't set up RTAS device tree properties\n");
        !           246:     }
        !           247: 
        !           248:     _FDT((fdt_pack(fdt)));
        !           249: 
        !           250:     cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));
        !           251: 
        !           252:     qemu_free(fdt);
        !           253: }
        !           254: 
        !           255: static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
        !           256: {
        !           257:     return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
        !           258: }
        !           259: 
        !           260: static void emulate_spapr_hypercall(CPUState *env)
        !           261: {
        !           262:     env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]);
        !           263: }
        !           264: 
        !           265: static void spapr_reset(void *opaque)
        !           266: {
        !           267:     sPAPREnvironment *spapr = (sPAPREnvironment *)opaque;
        !           268: 
        !           269:     fprintf(stderr, "sPAPR reset\n");
        !           270: 
        !           271:     /* flush out the hash table */
        !           272:     memset(spapr->htab, 0, spapr->htab_size);
        !           273: 
        !           274:     /* Load the fdt */
        !           275:     spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr,
        !           276:                        spapr->rtas_size);
        !           277: 
        !           278:     /* Set up the entry state */
        !           279:     first_cpu->gpr[3] = spapr->fdt_addr;
        !           280:     first_cpu->gpr[5] = 0;
        !           281:     first_cpu->halted = 0;
        !           282:     first_cpu->nip = spapr->entry_point;
        !           283: 
        !           284: }
        !           285: 
        !           286: /* pSeries LPAR / sPAPR hardware init */
        !           287: static void ppc_spapr_init(ram_addr_t ram_size,
        !           288:                            const char *boot_device,
        !           289:                            const char *kernel_filename,
        !           290:                            const char *kernel_cmdline,
        !           291:                            const char *initrd_filename,
        !           292:                            const char *cpu_model)
        !           293: {
        !           294:     CPUState *env;
        !           295:     int i;
        !           296:     ram_addr_t ram_offset;
        !           297:     uint32_t initrd_base;
        !           298:     long kernel_size, initrd_size, fw_size;
        !           299:     long pteg_shift = 17;
        !           300:     char *filename;
        !           301:     int irq = 16;
        !           302: 
        !           303:     spapr = qemu_malloc(sizeof(*spapr));
        !           304:     cpu_ppc_hypercall = emulate_spapr_hypercall;
        !           305: 
        !           306:     /* We place the device tree just below either the top of RAM, or
        !           307:      * 2GB, so that it can be processed with 32-bit code if
        !           308:      * necessary */
        !           309:     spapr->fdt_addr = MIN(ram_size, 0x80000000) - FDT_MAX_SIZE;
        !           310:     spapr->rtas_addr = spapr->fdt_addr - RTAS_MAX_SIZE;
        !           311: 
        !           312:     /* init CPUs */
        !           313:     if (cpu_model == NULL) {
        !           314:         cpu_model = "POWER7";
        !           315:     }
        !           316:     for (i = 0; i < smp_cpus; i++) {
        !           317:         env = cpu_init(cpu_model);
        !           318: 
        !           319:         if (!env) {
        !           320:             fprintf(stderr, "Unable to find PowerPC CPU definition\n");
        !           321:             exit(1);
        !           322:         }
        !           323:         /* Set time-base frequency to 512 MHz */
        !           324:         cpu_ppc_tb_init(env, TIMEBASE_FREQ);
        !           325:         qemu_register_reset((QEMUResetHandler *)&cpu_reset, env);
        !           326: 
        !           327:         env->hreset_vector = 0x60;
        !           328:         env->hreset_excp_prefix = 0;
        !           329:         env->gpr[3] = env->cpu_index;
        !           330:     }
        !           331: 
        !           332:     /* allocate RAM */
        !           333:     ram_offset = qemu_ram_alloc(NULL, "ppc_spapr.ram", ram_size);
        !           334:     cpu_register_physical_memory(0, ram_size, ram_offset);
        !           335: 
        !           336:     /* allocate hash page table.  For now we always make this 16mb,
        !           337:      * later we should probably make it scale to the size of guest
        !           338:      * RAM */
        !           339:     spapr->htab_size = 1ULL << (pteg_shift + 7);
        !           340:     spapr->htab = qemu_malloc(spapr->htab_size);
        !           341: 
        !           342:     for (env = first_cpu; env != NULL; env = env->next_cpu) {
        !           343:         env->external_htab = spapr->htab;
        !           344:         env->htab_base = -1;
        !           345:         env->htab_mask = spapr->htab_size - 1;
        !           346:     }
        !           347: 
        !           348:     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
        !           349:     spapr->rtas_size = load_image_targphys(filename, spapr->rtas_addr,
        !           350:                                            ram_size - spapr->rtas_addr);
        !           351:     if (spapr->rtas_size < 0) {
        !           352:         hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
        !           353:         exit(1);
        !           354:     }
        !           355:     qemu_free(filename);
        !           356: 
        !           357:     /* Set up Interrupt Controller */
        !           358:     spapr->icp = xics_system_init(XICS_IRQS);
        !           359: 
        !           360:     /* Set up VIO bus */
        !           361:     spapr->vio_bus = spapr_vio_bus_init();
        !           362: 
        !           363:     for (i = 0; i < MAX_SERIAL_PORTS; i++, irq++) {
        !           364:         if (serial_hds[i]) {
        !           365:             spapr_vty_create(spapr->vio_bus, SPAPR_VTY_BASE_ADDRESS + i,
        !           366:                              serial_hds[i], xics_find_qirq(spapr->icp, irq),
        !           367:                              irq);
        !           368:         }
        !           369:     }
        !           370: 
        !           371:     for (i = 0; i < nb_nics; i++, irq++) {
        !           372:         NICInfo *nd = &nd_table[i];
        !           373: 
        !           374:         if (!nd->model) {
        !           375:             nd->model = qemu_strdup("ibmveth");
        !           376:         }
        !           377: 
        !           378:         if (strcmp(nd->model, "ibmveth") == 0) {
        !           379:             spapr_vlan_create(spapr->vio_bus, 0x1000 + i, nd,
        !           380:                               xics_find_qirq(spapr->icp, irq), irq);
        !           381:         } else {
        !           382:             fprintf(stderr, "pSeries (sPAPR) platform does not support "
        !           383:                     "NIC model '%s' (only ibmveth is supported)\n",
        !           384:                     nd->model);
        !           385:             exit(1);
        !           386:         }
        !           387:     }
        !           388: 
        !           389:     for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) {
        !           390:         spapr_vscsi_create(spapr->vio_bus, 0x2000 + i,
        !           391:                            xics_find_qirq(spapr->icp, irq), irq);
        !           392:         irq++;
        !           393:     }
        !           394: 
        !           395:     if (kernel_filename) {
        !           396:         uint64_t lowaddr = 0;
        !           397: 
        !           398:         kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
        !           399:                                NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
        !           400:         if (kernel_size < 0) {
        !           401:             kernel_size = load_image_targphys(kernel_filename,
        !           402:                                               KERNEL_LOAD_ADDR,
        !           403:                                               ram_size - KERNEL_LOAD_ADDR);
        !           404:         }
        !           405:         if (kernel_size < 0) {
        !           406:             fprintf(stderr, "qemu: could not load kernel '%s'\n",
        !           407:                     kernel_filename);
        !           408:             exit(1);
        !           409:         }
        !           410: 
        !           411:         /* load initrd */
        !           412:         if (initrd_filename) {
        !           413:             initrd_base = INITRD_LOAD_ADDR;
        !           414:             initrd_size = load_image_targphys(initrd_filename, initrd_base,
        !           415:                                               ram_size - initrd_base);
        !           416:             if (initrd_size < 0) {
        !           417:                 fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
        !           418:                         initrd_filename);
        !           419:                 exit(1);
        !           420:             }
        !           421:         } else {
        !           422:             initrd_base = 0;
        !           423:             initrd_size = 0;
        !           424:         }
        !           425: 
        !           426:         spapr->entry_point = KERNEL_LOAD_ADDR;
        !           427:     } else {
        !           428:         if (ram_size < (MIN_RAM_SLOF << 20)) {
        !           429:             fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
        !           430:                     "%ldM guest RAM\n", MIN_RAM_SLOF);
        !           431:             exit(1);
        !           432:         }
        !           433:         filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "slof.bin");
        !           434:         fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
        !           435:         if (fw_size < 0) {
        !           436:             hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
        !           437:             exit(1);
        !           438:         }
        !           439:         qemu_free(filename);
        !           440:         spapr->entry_point = 0x100;
        !           441:         initrd_base = 0;
        !           442:         initrd_size = 0;
        !           443: 
        !           444:         /* SLOF will startup the secondary CPUs using RTAS,
        !           445:            rather than expecting a kexec() style entry */
        !           446:         for (env = first_cpu; env != NULL; env = env->next_cpu) {
        !           447:             env->halted = 1;
        !           448:         }
        !           449:     }
        !           450: 
        !           451:     /* Prepare the device tree */
        !           452:     spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
        !           453:                                             initrd_base, initrd_size,
        !           454:                                             boot_device, kernel_cmdline,
        !           455:                                             pteg_shift + 7);
        !           456:     assert(spapr->fdt_skel != NULL);
        !           457: 
        !           458:     qemu_register_reset(spapr_reset, spapr);
        !           459: }
        !           460: 
        !           461: static QEMUMachine spapr_machine = {
        !           462:     .name = "pseries",
        !           463:     .desc = "pSeries Logical Partition (PAPR compliant)",
        !           464:     .init = ppc_spapr_init,
        !           465:     .max_cpus = MAX_CPUS,
        !           466:     .no_vga = 1,
        !           467:     .no_parallel = 1,
        !           468:     .use_scsi = 1,
        !           469: };
        !           470: 
        !           471: static void spapr_machine_init(void)
        !           472: {
        !           473:     qemu_register_machine(&spapr_machine);
        !           474: }
        !           475: 
        !           476: machine_init(spapr_machine_init);

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.