arm64/efi: move EFI_PAGE definitions to efi/memory.h
[grub.git] / grub-core / loader / efi / fdt.c
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 2013-2015  Free Software Foundation, Inc.
4  *
5  *  GRUB is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  GRUB is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <grub/fdt.h>
20 #include <grub/mm.h>
21 #include <grub/err.h>
22 #include <grub/dl.h>
23 #include <grub/command.h>
24 #include <grub/file.h>
25 #include <grub/efi/efi.h>
26 #include <grub/efi/fdtload.h>
27 #include <grub/efi/memory.h>
28
29 static void *loaded_fdt;
30 static void *fdt;
31
32 void *
33 grub_fdt_load (grub_size_t additional_size)
34 {
35   void *raw_fdt;
36   unsigned int size;
37
38   if (fdt)
39     {
40       size = GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (fdt));
41       grub_efi_free_pages ((grub_addr_t) fdt, size);
42     }
43
44   if (loaded_fdt)
45     raw_fdt = loaded_fdt;
46   else
47     raw_fdt = grub_efi_get_firmware_fdt();
48
49   size =
50     raw_fdt ? grub_fdt_get_totalsize (raw_fdt) : GRUB_FDT_EMPTY_TREE_SZ;
51   size += additional_size;
52
53   grub_dprintf ("linux", "allocating %d bytes for fdt\n", size);
54   fdt = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (size));
55   if (!fdt)
56     return NULL;
57
58   if (raw_fdt)
59     {
60       grub_memmove (fdt, raw_fdt, size);
61       grub_fdt_set_totalsize (fdt, size);
62     }
63   else
64     {
65       grub_fdt_create_empty_tree (fdt, size);
66     }
67   return fdt;
68 }
69
70 grub_err_t
71 grub_fdt_install (void)
72 {
73   grub_efi_boot_services_t *b;
74   grub_efi_guid_t fdt_guid = GRUB_EFI_DEVICE_TREE_GUID;
75   grub_efi_status_t status;
76
77   b = grub_efi_system_table->boot_services;
78   status = b->install_configuration_table (&fdt_guid, fdt);
79   if (status != GRUB_EFI_SUCCESS)
80     return grub_error (GRUB_ERR_IO, "failed to install FDT");
81
82   grub_dprintf ("fdt", "Installed/updated FDT configuration table @ %p\n",
83                 fdt);
84   return GRUB_ERR_NONE;
85 }
86
87 void
88 grub_fdt_unload (void) {
89   if (!fdt) {
90     return;
91   }
92   grub_efi_free_pages ((grub_addr_t) fdt,
93                        GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (fdt)));
94   fdt = NULL;
95 }
96
97 static grub_err_t
98 grub_cmd_devicetree (grub_command_t cmd __attribute__ ((unused)),
99                      int argc, char *argv[])
100 {
101   grub_file_t dtb;
102   void *blob = NULL;
103   int size;
104
105   if (loaded_fdt)
106     grub_free (loaded_fdt);
107   loaded_fdt = NULL;
108
109   /* No arguments means "use firmware FDT".  */
110   if (argc == 0)
111     {
112       return GRUB_ERR_NONE;
113     }
114
115   dtb = grub_file_open (argv[0]);
116   if (!dtb)
117     goto out;
118
119   size = grub_file_size (dtb);
120   blob = grub_malloc (size);
121   if (!blob)
122     goto out;
123
124   if (grub_file_read (dtb, blob, size) < size)
125     {
126       if (!grub_errno)
127         grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
128       goto out;
129     }
130
131   if (grub_fdt_check_header (blob, size) != 0)
132     {
133       grub_error (GRUB_ERR_BAD_OS, N_("invalid device tree"));
134       goto out;
135     }
136
137 out:
138   if (dtb)
139     grub_file_close (dtb);
140
141   if (blob)
142     {
143       if (grub_errno == GRUB_ERR_NONE)
144         loaded_fdt = blob;
145       else
146         grub_free (blob);
147     }
148
149   return grub_errno;
150 }
151
152 static grub_command_t cmd_devicetree;
153
154 GRUB_MOD_INIT (fdt)
155 {
156   cmd_devicetree =
157     grub_register_command ("devicetree", grub_cmd_devicetree, 0,
158                            N_("Load DTB file."));
159 }
160
161 GRUB_MOD_FINI (fdt)
162 {
163   grub_unregister_command (cmd_devicetree);
164 }