diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c index 4442b6a83..400d85d28 100644 --- a/grub-core/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -123,6 +123,11 @@ grub_ieee1275_find_options (void) break; } } + +#if defined(__powerpc__) + if (grub_strncmp (tmp, "IBM,", 4) == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY); +#endif } if (is_smartfirmware) diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index 2adf4fdfc..0bc571e3e 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -200,11 +200,176 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, return 0; } +/* + * How much memory does OF believe it has? (regardless of whether + * it's accessible or not) + */ +static grub_err_t +grub_ieee1275_total_mem (grub_uint64_t *total) +{ + grub_ieee1275_phandle_t root; + grub_ieee1275_phandle_t memory; + grub_uint32_t reg[4]; + grub_ssize_t reg_size; + grub_uint32_t address_cells = 1; + grub_uint32_t size_cells = 1; + grub_uint64_t size; + + /* If we fail to get to the end, report 0. */ + *total = 0; + + /* Determine the format of each entry in `reg'. */ + if (grub_ieee1275_finddevice ("/", &root)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find / node"); + if (grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells, + sizeof (address_cells), 0)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine #address-cells"); + if (grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells, + sizeof (size_cells), 0)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine #size-cells"); + + if (size_cells > address_cells) + address_cells = size_cells; + + /* Load `/memory/reg'. */ + if (grub_ieee1275_finddevice ("/memory", &memory)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find /memory node"); + if (grub_ieee1275_get_integer_property (memory, "reg", reg, + sizeof (reg), ®_size)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine /memory/reg property"); + if (reg_size < 0 || (grub_size_t) reg_size > sizeof (reg)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "/memory response buffer exceeded"); + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS)) + { + address_cells = 1; + size_cells = 1; + } + + /* Decode only the size */ + size = reg[address_cells]; + if (size_cells == 2) + size = (size << 32) | reg[address_cells + 1]; + + *total = size; + + return grub_errno; +} + +#if defined(__powerpc__) + +/* See PAPR or arch/powerpc/kernel/prom_init.c */ +struct option_vector2 +{ + grub_uint8_t byte1; + grub_uint16_t reserved; + grub_uint32_t real_base; + grub_uint32_t real_size; + grub_uint32_t virt_base; + grub_uint32_t virt_size; + grub_uint32_t load_base; + grub_uint32_t min_rma; + grub_uint32_t min_load; + grub_uint8_t min_rma_percent; + grub_uint8_t max_pft_size; +} GRUB_PACKED; + +struct pvr_entry +{ + grub_uint32_t mask; + grub_uint32_t entry; +}; + +struct cas_vector +{ + struct + { + struct pvr_entry terminal; + } pvr_list; + grub_uint8_t num_vecs; + grub_uint8_t vec1_size; + grub_uint8_t vec1; + grub_uint8_t vec2_size; + struct option_vector2 vec2; + grub_uint8_t vec3_size; + grub_uint16_t vec3; + grub_uint8_t vec4_size; + grub_uint16_t vec4; +} GRUB_PACKED; + +/* + * Call ibm,client-architecture-support to try to get more RMA. + * We ask for 512MB which should be enough to verify a distro kernel. + * We ignore most errors: if we don't succeed we'll proceed with whatever + * memory we have. + */ +static void +grub_ieee1275_ibm_cas (void) +{ + int rc; + grub_ieee1275_ihandle_t root; + struct cas_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_ihandle_t ihandle; + grub_ieee1275_cell_t cas_addr; + grub_ieee1275_cell_t result; + } args; + struct cas_vector vector = + { + .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */ + .num_vecs = 4 - 1, + .vec1_size = 0, + .vec1 = 0x80, /* ignore */ + .vec2_size = 1 + sizeof (struct option_vector2) - 2, + .vec2 = { + 0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48 + }, + .vec3_size = 2 - 1, + .vec3 = 0x00e0, /* ask for FP + VMX + DFP but don't halt if unsatisfied */ + .vec4_size = 2 - 1, + .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ + }; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2); + args.method = (grub_ieee1275_cell_t) "ibm,client-architecture-support"; + rc = grub_ieee1275_open ("/", &root); + if (rc) + { + grub_error (GRUB_ERR_IO, "could not open root when trying to call CAS"); + return; + } + args.ihandle = root; + args.cas_addr = (grub_ieee1275_cell_t) &vector; + + grub_printf ("Calling ibm,client-architecture-support from grub..."); + IEEE1275_CALL_ENTRY_FN (&args); + grub_printf ("done\n"); + + grub_ieee1275_close (root); +} + +#endif /* __powerpc__ */ + static void grub_claim_heap (void) { unsigned long total = 0; +#if defined(__powerpc__) + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY)) + { + grub_uint64_t rma_size; + grub_err_t err; + + err = grub_ieee1275_total_mem (&rma_size); + /* if we have an error, don't call CAS, just hope for the best */ + if (err == GRUB_ERR_NONE && rma_size < (512 * 1024 * 1024)) + grub_ieee1275_ibm_cas (); + } +#endif + grub_machine_mmap_iterate (heap_init, &total); } #endif diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index f53228703..77e4e61e5 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -128,6 +128,16 @@ enum grub_ieee1275_flag GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN, GRUB_IEEE1275_FLAG_RAW_DEVNAMES, + +#if defined(__powerpc__) + /* + * On PFW, the first time we boot a Linux partition, we may only get 256MB of + * real memory area, even if the partition has more memory. Set this flag if + * we think we're running under PFW. Then, if this flag is set, and the RMA is + * only 256MB in size, try asking for more with CAS. + */ + GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY, +#endif }; extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag);