1 /** @file 2 * 3 * Copyright (c) 2017, Andrey Warkentin <andrey.warkentin@gmail.com> 4 * Copyright (c) 2016, Linaro, Ltd. All rights reserved. 5 * 6 * SPDX-License-Identifier: BSD-2-Clause-Patent 7 * 8 **/ 9 #include <PiDxe.h> 10 11 #include <Library/BaseLib.h> 12 #include <Library/DebugLib.h> 13 #include <Library/DxeServicesLib.h> 14 #include <Library/MemoryAllocationLib.h> 15 #include <Library/UefiBootServicesTableLib.h> 16 #include <Library/UefiLib.h> 17 #include <libfdt.h> 18 #include <Protocol/RpiFirmware.h> 19 #include <Guid/Fdt.h> 20 #include <ConfigVars.h> 21 22 STATIC VOID *mFdtImage; 23 24 STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol; 25 26 STATIC 27 EFI_STATUS 28 FixEthernetAliases ( 29 VOID 30 ) 31 { 32 INTN Aliases; 33 CONST CHAR8 *Ethernet; 34 CONST CHAR8 *Ethernet0; 35 CONST CHAR8 *Alias; 36 UINTN CopySize; 37 CHAR8 *Copy; 38 INTN Retval; 39 EFI_STATUS Status; 40 41 // 42 // Look up the 'ethernet[0]' aliases 43 // 44 Aliases = fdt_path_offset (mFdtImage, "/aliases"); 45 if (Aliases < 0) { 46 DEBUG ((DEBUG_ERROR, "%a: failed to locate '/aliases'\n", __FUNCTION__)); 47 return EFI_NOT_FOUND; 48 } 49 Ethernet = fdt_getprop (mFdtImage, Aliases, "ethernet", NULL); 50 Ethernet0 = fdt_getprop (mFdtImage, Aliases, "ethernet0", NULL); 51 Alias = Ethernet ? Ethernet : Ethernet0; 52 if (!Alias) { 53 DEBUG ((DEBUG_ERROR, "%a: failed to locate 'ethernet[0]' alias\n", __FUNCTION__)); 54 return EFI_NOT_FOUND; 55 } 56 57 // 58 // Create copy for fdt_setprop 59 // 60 CopySize = AsciiStrSize (Alias); 61 Copy = AllocateCopyPool (CopySize, Alias); 62 if (!Copy) { 63 DEBUG ((DEBUG_ERROR, "%a: failed to copy '%a'\n", __FUNCTION__, Alias)); 64 return EFI_OUT_OF_RESOURCES; 65 } 66 67 // 68 // Create missing aliases 69 // 70 Status = EFI_SUCCESS; 71 if (!Ethernet) { 72 Retval = fdt_setprop (mFdtImage, Aliases, "ethernet", Copy, CopySize); 73 if (Retval != 0) { 74 Status = EFI_NOT_FOUND; 75 DEBUG ((DEBUG_ERROR, "%a: failed to create 'ethernet' alias (%d)\n", 76 __FUNCTION__, Retval)); 77 } 78 DEBUG ((DEBUG_INFO, "%a: created 'ethernet' alias '%a'\n", __FUNCTION__, Copy)); 79 } 80 if (!Ethernet0) { 81 Retval = fdt_setprop (mFdtImage, Aliases, "ethernet0", Copy, CopySize); 82 if (Retval != 0) { 83 Status = EFI_NOT_FOUND; 84 DEBUG ((DEBUG_ERROR, "%a: failed to create 'ethernet0' alias (%d)\n", 85 __FUNCTION__, Retval)); 86 } 87 DEBUG ((DEBUG_INFO, "%a: created 'ethernet0' alias '%a'\n", __FUNCTION__, Copy)); 88 } 89 90 FreePool (Copy); 91 return Status; 92 } 93 94 STATIC 95 EFI_STATUS 96 UpdateMacAddress ( 97 VOID 98 ) 99 { 100 INTN Node; 101 INTN Retval; 102 EFI_STATUS Status; 103 UINT8 MacAddress[6]; 104 105 // 106 // Locate the node that the 'ethernet' alias refers to 107 // 108 Node = fdt_path_offset (mFdtImage, "ethernet"); 109 if (Node < 0) { 110 DEBUG ((DEBUG_ERROR, "%a: failed to locate 'ethernet' alias\n", __FUNCTION__)); 111 return EFI_NOT_FOUND; 112 } 113 114 // 115 // Get the MAC address from the firmware 116 // 117 Status = mFwProtocol->GetMacAddress (MacAddress); 118 if (EFI_ERROR (Status)) { 119 DEBUG ((DEBUG_ERROR, "%a: failed to retrieve MAC address\n", __FUNCTION__)); 120 return Status; 121 } 122 123 Retval = fdt_setprop (mFdtImage, Node, "mac-address", MacAddress, 124 sizeof MacAddress); 125 if (Retval != 0) { 126 DEBUG ((DEBUG_ERROR, "%a: failed to create 'mac-address' property (%d)\n", 127 __FUNCTION__, Retval)); 128 return EFI_NOT_FOUND; 129 } 130 131 DEBUG ((DEBUG_INFO, "%a: setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", 132 __FUNCTION__, MacAddress[0], MacAddress[1], MacAddress[2], MacAddress[3], 133 MacAddress[4], MacAddress[5])); 134 return EFI_SUCCESS; 135 } 136 137 // 138 // Add "bcm2835-usb" to the USB compatible property list, if not present. 139 // Required because some Linux kernels can't handle USB devices otherwise. 140 // 141 STATIC 142 EFI_STATUS 143 AddUsbCompatibleProperty ( 144 VOID 145 ) 146 { 147 CONST CHAR8 Prop[] = "brcm,bcm2708-usb"; 148 CONST CHAR8 NewProp[] = "brcm,bcm2835-usb"; 149 CONST CHAR8 *List; 150 CHAR8 *NewList; 151 INT32 ListSize; 152 INTN Node; 153 INTN Retval; 154 155 // Locate the node that the 'usb' alias refers to 156 Node = fdt_path_offset (mFdtImage, "usb"); 157 if (Node < 0) { 158 DEBUG ((DEBUG_ERROR, "%a: failed to locate 'usb' alias\n", __FUNCTION__)); 159 return EFI_NOT_FOUND; 160 } 161 162 // Get the property list. This is a list of NUL terminated strings. 163 List = fdt_getprop (mFdtImage, Node, "compatible", &ListSize); 164 if (List == NULL) { 165 DEBUG ((DEBUG_ERROR, "%a: failed to locate properties\n", __FUNCTION__)); 166 return EFI_NOT_FOUND; 167 } 168 169 // Check if the compatible value we plan to add is already present 170 if (fdt_stringlist_contains (List, ListSize, NewProp)) { 171 DEBUG ((DEBUG_INFO, "%a: property '%a' is already set.\n", 172 __FUNCTION__, NewProp)); 173 return EFI_SUCCESS; 174 } 175 176 // Make sure the compatible device is what we expect 177 if (!fdt_stringlist_contains (List, ListSize, Prop)) { 178 DEBUG ((DEBUG_ERROR, "%a: property '%a' is missing!\n", 179 __FUNCTION__, Prop)); 180 return EFI_NOT_FOUND; 181 } 182 183 // Add the new NUL terminated entry to our list 184 DEBUG ((DEBUG_INFO, "%a: adding '%a' to the properties\n", 185 __FUNCTION__, NewProp)); 186 187 NewList = AllocatePool (ListSize + sizeof (NewProp)); 188 if (NewList == NULL) { 189 DEBUG ((DEBUG_ERROR, "%a: failed to allocate memory\n", __FUNCTION__)); 190 return EFI_OUT_OF_RESOURCES;; 191 } 192 CopyMem (NewList, List, ListSize); 193 CopyMem (&NewList[ListSize], NewProp, sizeof (NewProp)); 194 195 Retval = fdt_setprop (mFdtImage, Node, "compatible", NewList, 196 ListSize + sizeof (NewProp)); 197 FreePool (NewList); 198 if (Retval != 0) { 199 DEBUG ((DEBUG_ERROR, "%a: failed to update properties (%d)\n", 200 __FUNCTION__, Retval)); 201 return EFI_NOT_FOUND; 202 } 203 204 return EFI_SUCCESS; 205 } 206 207 STATIC 208 EFI_STATUS 209 CleanMemoryNodes ( 210 VOID 211 ) 212 { 213 INTN Node; 214 INT32 Retval; 215 216 Node = fdt_path_offset (mFdtImage, "/memory"); 217 if (Node < 0) { 218 return EFI_SUCCESS; 219 } 220 221 /* 222 * Remove bogus memory nodes which can make the booted 223 * OS go crazy and ignore the UEFI map. 224 */ 225 DEBUG ((DEBUG_INFO, "Removing bogus /memory\n")); 226 Retval = fdt_del_node (mFdtImage, Node); 227 if (Retval != 0) { 228 DEBUG ((DEBUG_ERROR, "Failed to remove /memory\n")); 229 return EFI_NOT_FOUND; 230 } 231 232 return EFI_SUCCESS; 233 } 234 235 STATIC 236 EFI_STATUS 237 SanitizePSCI ( 238 VOID 239 ) 240 { 241 INTN Node; 242 INTN Root; 243 INT32 Retval; 244 245 Root = fdt_path_offset (mFdtImage, "/"); 246 ASSERT (Root >= 0); 247 if (Root < 0) { 248 return EFI_NOT_FOUND; 249 } 250 251 Node = fdt_path_offset (mFdtImage, "/psci"); 252 if (Node < 0) { 253 Node = fdt_add_subnode (mFdtImage, Root, "psci"); 254 } 255 256 ASSERT (Node >= 0); 257 if (Node < 0) { 258 DEBUG ((DEBUG_ERROR, "Couldn't find/create /psci\n")); 259 return EFI_NOT_FOUND; 260 } 261 262 Retval = fdt_setprop_string (mFdtImage, Node, "compatible", "arm,psci-1.0"); 263 if (Retval != 0) { 264 DEBUG ((DEBUG_ERROR, "Couldn't set /psci compatible property\n")); 265 return EFI_NOT_FOUND; 266 } 267 268 Retval = fdt_setprop_string (mFdtImage, Node, "method", "smc"); 269 if (Retval != 0) { 270 DEBUG ((DEBUG_ERROR, "Couldn't set /psci method property\n")); 271 return EFI_NOT_FOUND; 272 } 273 274 Root = fdt_path_offset (mFdtImage, "/cpus"); 275 if (Root < 0) { 276 DEBUG ((DEBUG_ERROR, "No CPUs to update with PSCI enable-method?\n")); 277 return EFI_NOT_FOUND; 278 } 279 280 Node = fdt_first_subnode (mFdtImage, Root); 281 while (Node >= 0) { 282 if (fdt_setprop_string (mFdtImage, Node, "enable-method", "psci") != 0) { 283 DEBUG ((DEBUG_ERROR, "Failed to update enable-method for a CPU\n")); 284 return EFI_NOT_FOUND; 285 } 286 287 fdt_delprop (mFdtImage, Node, "cpu-release-addr"); 288 Node = fdt_next_subnode (mFdtImage, Node); 289 } 290 return EFI_SUCCESS; 291 } 292 293 STATIC 294 EFI_STATUS 295 CleanSimpleFramebuffer ( 296 VOID 297 ) 298 { 299 INTN Node; 300 INT32 Retval; 301 302 /* 303 * Should look for nodes by kind and remove aliases 304 * by matching against device. 305 */ 306 Node = fdt_path_offset (mFdtImage, "display0"); 307 if (Node < 0) { 308 return EFI_SUCCESS; 309 } 310 311 /* 312 * Remove bogus GPU-injected simple-framebuffer, which 313 * doesn't reflect the framebuffer built by UEFI. 314 */ 315 DEBUG ((DEBUG_INFO, "Removing bogus display0\n")); 316 Retval = fdt_del_node (mFdtImage, Node); 317 if (Retval != 0) { 318 DEBUG ((DEBUG_ERROR, "Failed to remove display0\n")); 319 return EFI_NOT_FOUND; 320 } 321 322 Node = fdt_path_offset (mFdtImage, "/aliases"); 323 if (Node < 0) { 324 DEBUG ((DEBUG_ERROR, "Couldn't find /aliases to remove display0\n")); 325 return EFI_NOT_FOUND; 326 } 327 328 Retval = fdt_delprop (mFdtImage, Node, "display0"); 329 if (Retval != 0) { 330 DEBUG ((DEBUG_ERROR, "Failed to remove display0 alias\n")); 331 return EFI_NOT_FOUND; 332 } 333 334 return EFI_SUCCESS; 335 } 336 337 /** 338 @param ImageHandle of the loaded driver 339 @param SystemTable Pointer to the System Table 340 341 @retval EFI_SUCCESS Protocol registered 342 @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure 343 @retval EFI_DEVICE_ERROR Hardware problems 344 345 **/ 346 EFI_STATUS 347 EFIAPI 348 FdtDxeInitialize ( 349 IN EFI_HANDLE ImageHandle, 350 IN EFI_SYSTEM_TABLE *SystemTable 351 ) 352 { 353 INT32 Retval; 354 EFI_STATUS Status; 355 UINTN FdtSize; 356 VOID *FdtImage = NULL; 357 358 if (PcdGet32 (PcdSystemTableMode) != SYSTEM_TABLE_MODE_BOTH && 359 PcdGet32 (PcdSystemTableMode) != SYSTEM_TABLE_MODE_DT) { 360 DEBUG ((DEBUG_INFO, "Device Tree disabled per user configuration\n")); 361 return EFI_SUCCESS; 362 } 363 364 Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL, 365 (VOID**)&mFwProtocol); 366 ASSERT_EFI_ERROR (Status); 367 368 FdtImage = (VOID*)(UINTN)PcdGet32 (PcdFdtBaseAddress); 369 Retval = fdt_check_header (FdtImage); 370 if (Retval != 0) { 371 /* 372 * Any one of: 373 * - Invalid config.txt device_tree_address (not PcdFdtBaseAddress) 374 * - Missing FDT for your Pi variant (if not overriding via device_tree=) 375 */ 376 DEBUG ((DEBUG_ERROR, "No devicetree passed via config.txt\n")); 377 return EFI_NOT_FOUND; 378 } 379 380 FdtSize = fdt_totalsize (FdtImage); 381 DEBUG ((DEBUG_INFO, "Devicetree passed via config.txt (0x%lx bytes)\n", FdtSize)); 382 383 /* 384 * Probably overkill. 385 */ 386 FdtSize += EFI_PAGE_SIZE * 2; 387 Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, 388 EFI_SIZE_TO_PAGES (FdtSize), (EFI_PHYSICAL_ADDRESS*)&mFdtImage); 389 if (EFI_ERROR (Status)) { 390 DEBUG ((DEBUG_ERROR, "Failed to allocate devicetree: %r\n", Status)); 391 goto out; 392 } 393 394 Retval = fdt_open_into (FdtImage, mFdtImage, FdtSize); 395 if (Retval != 0) { 396 DEBUG ((DEBUG_ERROR, "fdt_open_into failed: %d\n", Retval)); 397 goto out; 398 } 399 400 /* 401 * These are all best-effort. 402 */ 403 404 Status = SanitizePSCI (); 405 if (EFI_ERROR (Status)) { 406 Print (L"Failed to sanitize PSCI: %r\n", Status); 407 } 408 409 Status = CleanMemoryNodes (); 410 if (EFI_ERROR (Status)) { 411 Print (L"Failed to clean memory nodes: %r\n", Status); 412 } 413 414 Status = CleanSimpleFramebuffer (); 415 if (EFI_ERROR (Status)) { 416 Print (L"Failed to clean frame buffer: %r\n", Status); 417 } 418 419 Status = FixEthernetAliases (); 420 if (EFI_ERROR (Status)) { 421 Print (L"Failed to fix ethernet aliases: %r\n", Status); 422 } 423 424 Status = UpdateMacAddress (); 425 if (EFI_ERROR (Status)) { 426 Print (L"Failed to update MAC address: %r\n", Status); 427 } 428 429 Status = AddUsbCompatibleProperty (); 430 if (EFI_ERROR (Status)) { 431 Print (L"Failed to update USB compatible properties: %r\n", Status); 432 } 433 434 DEBUG ((DEBUG_INFO, "Installed devicetree at address %p\n", mFdtImage)); 435 Status = gBS->InstallConfigurationTable (&gFdtTableGuid, mFdtImage); 436 if (EFI_ERROR (Status)) { 437 DEBUG ((DEBUG_ERROR, "Couldn't register devicetree: %r\n", Status)); 438 goto out; 439 } 440 441 out: 442 if (EFI_ERROR(Status)) { 443 if (mFdtImage != NULL) { 444 gBS->FreePages ((EFI_PHYSICAL_ADDRESS) mFdtImage, EFI_SIZE_TO_PAGES (FdtSize)); 445 } 446 } 447 return Status; 448 } 449