1*fcf5c041SMiao Yan /* 2*fcf5c041SMiao Yan * (C) Copyright 2015 Miao Yan <yanmiaobest@gmail.com> 3*fcf5c041SMiao Yan * 4*fcf5c041SMiao Yan * SPDX-License-Identifier: GPL-2.0+ 5*fcf5c041SMiao Yan */ 6*fcf5c041SMiao Yan 7*fcf5c041SMiao Yan #include <common.h> 8*fcf5c041SMiao Yan #include <command.h> 9*fcf5c041SMiao Yan #include <errno.h> 10*fcf5c041SMiao Yan #include <qemu_fw_cfg.h> 11*fcf5c041SMiao Yan 12*fcf5c041SMiao Yan /* 13*fcf5c041SMiao Yan * This function prepares kernel for zboot. It loads kernel data 14*fcf5c041SMiao Yan * to 'load_addr', initrd to 'initrd_addr' and kernel command 15*fcf5c041SMiao Yan * line using qemu fw_cfg interface. 16*fcf5c041SMiao Yan */ 17*fcf5c041SMiao Yan static int qemu_fwcfg_setup_kernel(void *load_addr, void *initrd_addr) 18*fcf5c041SMiao Yan { 19*fcf5c041SMiao Yan char *data_addr; 20*fcf5c041SMiao Yan uint32_t setup_size, kernel_size, cmdline_size, initrd_size; 21*fcf5c041SMiao Yan 22*fcf5c041SMiao Yan qemu_fwcfg_read_entry(FW_CFG_SETUP_SIZE, 4, &setup_size); 23*fcf5c041SMiao Yan qemu_fwcfg_read_entry(FW_CFG_KERNEL_SIZE, 4, &kernel_size); 24*fcf5c041SMiao Yan 25*fcf5c041SMiao Yan if (setup_size == 0 || kernel_size == 0) { 26*fcf5c041SMiao Yan printf("warning: no kernel available\n"); 27*fcf5c041SMiao Yan return -1; 28*fcf5c041SMiao Yan } 29*fcf5c041SMiao Yan 30*fcf5c041SMiao Yan data_addr = load_addr; 31*fcf5c041SMiao Yan qemu_fwcfg_read_entry(FW_CFG_SETUP_DATA, 32*fcf5c041SMiao Yan le32_to_cpu(setup_size), data_addr); 33*fcf5c041SMiao Yan data_addr += le32_to_cpu(setup_size); 34*fcf5c041SMiao Yan 35*fcf5c041SMiao Yan qemu_fwcfg_read_entry(FW_CFG_KERNEL_DATA, 36*fcf5c041SMiao Yan le32_to_cpu(kernel_size), data_addr); 37*fcf5c041SMiao Yan data_addr += le32_to_cpu(kernel_size); 38*fcf5c041SMiao Yan 39*fcf5c041SMiao Yan data_addr = initrd_addr; 40*fcf5c041SMiao Yan qemu_fwcfg_read_entry(FW_CFG_INITRD_SIZE, 4, &initrd_size); 41*fcf5c041SMiao Yan if (initrd_size == 0) { 42*fcf5c041SMiao Yan printf("warning: no initrd available\n"); 43*fcf5c041SMiao Yan } else { 44*fcf5c041SMiao Yan qemu_fwcfg_read_entry(FW_CFG_INITRD_DATA, 45*fcf5c041SMiao Yan le32_to_cpu(initrd_size), data_addr); 46*fcf5c041SMiao Yan data_addr += le32_to_cpu(initrd_size); 47*fcf5c041SMiao Yan } 48*fcf5c041SMiao Yan 49*fcf5c041SMiao Yan qemu_fwcfg_read_entry(FW_CFG_CMDLINE_SIZE, 4, &cmdline_size); 50*fcf5c041SMiao Yan if (cmdline_size) { 51*fcf5c041SMiao Yan qemu_fwcfg_read_entry(FW_CFG_CMDLINE_DATA, 52*fcf5c041SMiao Yan le32_to_cpu(cmdline_size), data_addr); 53*fcf5c041SMiao Yan /* 54*fcf5c041SMiao Yan * if kernel cmdline only contains '\0', (e.g. no -append 55*fcf5c041SMiao Yan * when invoking qemu), do not update bootargs 56*fcf5c041SMiao Yan */ 57*fcf5c041SMiao Yan if (*data_addr != '\0') { 58*fcf5c041SMiao Yan if (setenv("bootargs", data_addr) < 0) 59*fcf5c041SMiao Yan printf("warning: unable to change bootargs\n"); 60*fcf5c041SMiao Yan } 61*fcf5c041SMiao Yan } 62*fcf5c041SMiao Yan 63*fcf5c041SMiao Yan printf("loading kernel to address %p size %x", load_addr, 64*fcf5c041SMiao Yan le32_to_cpu(kernel_size)); 65*fcf5c041SMiao Yan if (initrd_size) 66*fcf5c041SMiao Yan printf(" initrd %p size %x\n", 67*fcf5c041SMiao Yan initrd_addr, 68*fcf5c041SMiao Yan le32_to_cpu(initrd_size)); 69*fcf5c041SMiao Yan else 70*fcf5c041SMiao Yan printf("\n"); 71*fcf5c041SMiao Yan 72*fcf5c041SMiao Yan return 0; 73*fcf5c041SMiao Yan } 74*fcf5c041SMiao Yan 75*fcf5c041SMiao Yan static int qemu_fwcfg_list_firmware(void) 76*fcf5c041SMiao Yan { 77*fcf5c041SMiao Yan int ret; 78*fcf5c041SMiao Yan struct fw_cfg_file_iter iter; 79*fcf5c041SMiao Yan struct fw_file *file; 80*fcf5c041SMiao Yan 81*fcf5c041SMiao Yan /* make sure fw_list is loaded */ 82*fcf5c041SMiao Yan ret = qemu_fwcfg_read_firmware_list(); 83*fcf5c041SMiao Yan if (ret) 84*fcf5c041SMiao Yan return ret; 85*fcf5c041SMiao Yan 86*fcf5c041SMiao Yan 87*fcf5c041SMiao Yan for (file = qemu_fwcfg_file_iter_init(&iter); 88*fcf5c041SMiao Yan !qemu_fwcfg_file_iter_end(&iter); 89*fcf5c041SMiao Yan file = qemu_fwcfg_file_iter_next(&iter)) { 90*fcf5c041SMiao Yan printf("%-56s\n", file->cfg.name); 91*fcf5c041SMiao Yan } 92*fcf5c041SMiao Yan 93*fcf5c041SMiao Yan return 0; 94*fcf5c041SMiao Yan } 95*fcf5c041SMiao Yan 96*fcf5c041SMiao Yan static int qemu_fwcfg_do_list(cmd_tbl_t *cmdtp, int flag, 97*fcf5c041SMiao Yan int argc, char * const argv[]) 98*fcf5c041SMiao Yan { 99*fcf5c041SMiao Yan if (qemu_fwcfg_list_firmware() < 0) 100*fcf5c041SMiao Yan return CMD_RET_FAILURE; 101*fcf5c041SMiao Yan 102*fcf5c041SMiao Yan return 0; 103*fcf5c041SMiao Yan } 104*fcf5c041SMiao Yan 105*fcf5c041SMiao Yan static int qemu_fwcfg_do_cpus(cmd_tbl_t *cmdtp, int flag, 106*fcf5c041SMiao Yan int argc, char * const argv[]) 107*fcf5c041SMiao Yan { 108*fcf5c041SMiao Yan int ret = qemu_fwcfg_online_cpus(); 109*fcf5c041SMiao Yan if (ret < 0) { 110*fcf5c041SMiao Yan printf("QEMU fw_cfg interface not found\n"); 111*fcf5c041SMiao Yan return CMD_RET_FAILURE; 112*fcf5c041SMiao Yan } 113*fcf5c041SMiao Yan 114*fcf5c041SMiao Yan printf("%d cpu(s) online\n", qemu_fwcfg_online_cpus()); 115*fcf5c041SMiao Yan 116*fcf5c041SMiao Yan return 0; 117*fcf5c041SMiao Yan } 118*fcf5c041SMiao Yan 119*fcf5c041SMiao Yan static int qemu_fwcfg_do_load(cmd_tbl_t *cmdtp, int flag, 120*fcf5c041SMiao Yan int argc, char * const argv[]) 121*fcf5c041SMiao Yan { 122*fcf5c041SMiao Yan char *env; 123*fcf5c041SMiao Yan void *load_addr; 124*fcf5c041SMiao Yan void *initrd_addr; 125*fcf5c041SMiao Yan 126*fcf5c041SMiao Yan env = getenv("loadaddr"); 127*fcf5c041SMiao Yan load_addr = env ? 128*fcf5c041SMiao Yan (void *)simple_strtoul(env, NULL, 16) : 129*fcf5c041SMiao Yan (void *)CONFIG_LOADADDR; 130*fcf5c041SMiao Yan 131*fcf5c041SMiao Yan env = getenv("ramdiskaddr"); 132*fcf5c041SMiao Yan initrd_addr = env ? 133*fcf5c041SMiao Yan (void *)simple_strtoul(env, NULL, 16) : 134*fcf5c041SMiao Yan (void *)CONFIG_RAMDISK_ADDR; 135*fcf5c041SMiao Yan 136*fcf5c041SMiao Yan if (argc == 2) { 137*fcf5c041SMiao Yan load_addr = (void *)simple_strtoul(argv[0], NULL, 16); 138*fcf5c041SMiao Yan initrd_addr = (void *)simple_strtoul(argv[1], NULL, 16); 139*fcf5c041SMiao Yan } else if (argc == 1) { 140*fcf5c041SMiao Yan load_addr = (void *)simple_strtoul(argv[0], NULL, 16); 141*fcf5c041SMiao Yan } 142*fcf5c041SMiao Yan 143*fcf5c041SMiao Yan return qemu_fwcfg_setup_kernel(load_addr, initrd_addr); 144*fcf5c041SMiao Yan } 145*fcf5c041SMiao Yan 146*fcf5c041SMiao Yan static cmd_tbl_t fwcfg_commands[] = { 147*fcf5c041SMiao Yan U_BOOT_CMD_MKENT(list, 0, 1, qemu_fwcfg_do_list, "", ""), 148*fcf5c041SMiao Yan U_BOOT_CMD_MKENT(cpus, 0, 1, qemu_fwcfg_do_cpus, "", ""), 149*fcf5c041SMiao Yan U_BOOT_CMD_MKENT(load, 2, 1, qemu_fwcfg_do_load, "", ""), 150*fcf5c041SMiao Yan }; 151*fcf5c041SMiao Yan 152*fcf5c041SMiao Yan static int do_qemu_fw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 153*fcf5c041SMiao Yan { 154*fcf5c041SMiao Yan int ret; 155*fcf5c041SMiao Yan cmd_tbl_t *fwcfg_cmd; 156*fcf5c041SMiao Yan 157*fcf5c041SMiao Yan if (!qemu_fwcfg_present()) { 158*fcf5c041SMiao Yan printf("QEMU fw_cfg interface not found\n"); 159*fcf5c041SMiao Yan return CMD_RET_USAGE; 160*fcf5c041SMiao Yan } 161*fcf5c041SMiao Yan 162*fcf5c041SMiao Yan fwcfg_cmd = find_cmd_tbl(argv[1], fwcfg_commands, 163*fcf5c041SMiao Yan ARRAY_SIZE(fwcfg_commands)); 164*fcf5c041SMiao Yan argc -= 2; 165*fcf5c041SMiao Yan argv += 2; 166*fcf5c041SMiao Yan if (!fwcfg_cmd || argc > fwcfg_cmd->maxargs) 167*fcf5c041SMiao Yan return CMD_RET_USAGE; 168*fcf5c041SMiao Yan 169*fcf5c041SMiao Yan ret = fwcfg_cmd->cmd(fwcfg_cmd, flag, argc, argv); 170*fcf5c041SMiao Yan 171*fcf5c041SMiao Yan return cmd_process_error(fwcfg_cmd, ret); 172*fcf5c041SMiao Yan } 173*fcf5c041SMiao Yan 174*fcf5c041SMiao Yan U_BOOT_CMD( 175*fcf5c041SMiao Yan qfw, 4, 1, do_qemu_fw, 176*fcf5c041SMiao Yan "QEMU firmware interface", 177*fcf5c041SMiao Yan "<command>\n" 178*fcf5c041SMiao Yan " - list : print firmware(s) currently loaded\n" 179*fcf5c041SMiao Yan " - cpus : print online cpu number\n" 180*fcf5c041SMiao Yan " - load <kernel addr> <initrd addr> : load kernel and initrd (if any), and setup for zboot\n" 181*fcf5c041SMiao Yan ) 182