zfs: Add ZFS filesystem support
[platform/kernel/u-boot.git] / common / cmd_zfs.c
1 /*
2  *
3  * ZFS filesystem porting to Uboot by
4  * Jorgen Lundman <lundman at lundman.net>
5  *
6  * zfsfs support
7  * made from existing GRUB Sources by Sun, GNU and others.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  * MA 02111-1307 USA
23  *
24  */
25
26 #include <common.h>
27 #include <part.h>
28 #include <config.h>
29 #include <command.h>
30 #include <image.h>
31 #include <linux/ctype.h>
32 #include <asm/byteorder.h>
33 #include <zfs_common.h>
34 #include <linux/stat.h>
35 #include <malloc.h>
36
37 #if defined(CONFIG_CMD_USB) && defined(CONFIG_USB_STORAGE)
38 #include <usb.h>
39 #endif
40
41 #if !defined(CONFIG_DOS_PARTITION) && !defined(CONFIG_EFI_PARTITION)
42 #error DOS or EFI partition support must be selected
43 #endif
44
45 #define DOS_PART_MAGIC_OFFSET   0x1fe
46 #define DOS_FS_TYPE_OFFSET      0x36
47 #define DOS_FS32_TYPE_OFFSET    0x52
48
49 static int do_zfs_load(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
50 {
51         char *filename = NULL;
52         char *ep;
53         int dev;
54         unsigned long part = 1;
55         ulong addr = 0;
56         ulong part_length;
57         disk_partition_t info;
58         char buf[12];
59         unsigned long count;
60         const char *addr_str;
61         struct zfs_file zfile;
62         struct device_s vdev;
63
64         if (argc < 3)
65                 return CMD_RET_USAGE;
66
67         count = 0;
68         addr = simple_strtoul(argv[3], NULL, 16);
69         filename = getenv("bootfile");
70         switch (argc) {
71         case 3:
72                 addr_str = getenv("loadaddr");
73                 if (addr_str != NULL)
74                         addr = simple_strtoul(addr_str, NULL, 16);
75                 else
76                         addr = CONFIG_SYS_LOAD_ADDR;
77
78                 break;
79         case 4:
80                 break;
81         case 5:
82                 filename = argv[4];
83                 break;
84         case 6:
85                 filename = argv[4];
86                 count = simple_strtoul(argv[5], NULL, 16);
87                 break;
88
89         default:
90                 return cmd_usage(cmdtp);
91         }
92
93         if (!filename) {
94                 puts("** No boot file defined **\n");
95                 return 1;
96         }
97
98         dev = (int)simple_strtoul(argv[2], &ep, 16);
99         zfs_dev_desc = get_dev(argv[1], dev);
100         if (zfs_dev_desc == NULL) {
101                 printf("** Block device %s %d not supported\n", argv[1], dev);
102                 return 1;
103         }
104
105         if (*ep) {
106                 if (*ep != ':') {
107                         puts("** Invalid boot device, use `dev[:part]' **\n");
108                         return 1;
109                 }
110                 part = simple_strtoul(++ep, NULL, 16);
111         }
112
113         if (part != 0) {
114                 if (get_partition_info(zfs_dev_desc, part, &info)) {
115                         printf("** Bad partition %lu **\n", part);
116                         return 1;
117                 }
118
119                 if (strncmp((char *)info.type, BOOT_PART_TYPE,
120                                         strlen(BOOT_PART_TYPE)) != 0) {
121                         printf("** Invalid partition type \"%s\" (expect \"" BOOT_PART_TYPE "\")\n",
122                                    info.type);
123                         return 1;
124                 }
125                 printf("Loading file \"%s\" "
126                            "from %s device %d:%lu %s\n",
127                            filename, argv[1], dev, part, info.name);
128         } else {
129                 printf("Loading file \"%s\" from %s device %d\n",
130                            filename, argv[1], dev);
131         }
132
133         part_length = zfs_set_blk_dev(zfs_dev_desc, part);
134         if (part_length == 0) {
135                 printf("**Bad partition - %s %d:%lu **\n", argv[1], dev, part);
136                 return 1;
137         }
138
139         vdev.part_length = part_length;
140
141         memset(&zfile, 0, sizeof(zfile));
142         zfile.device = &vdev;
143         if (zfs_open(&zfile, filename)) {
144                 printf("** File not found %s\n", filename);
145                 return 1;
146         }
147
148         if ((count < zfile.size) && (count != 0))
149                 zfile.size = (uint64_t)count;
150
151         if (zfs_read(&zfile, (char *)addr, zfile.size) != zfile.size) {
152                 printf("** Unable to read \"%s\" from %s %d:%lu **\n",
153                            filename, argv[1], dev, part);
154                 zfs_close(&zfile);
155                 return 1;
156         }
157
158         zfs_close(&zfile);
159
160         /* Loading ok, update default load address */
161         load_addr = addr;
162
163         printf("%llu bytes read\n", zfile.size);
164         sprintf(buf, "%llX", zfile.size);
165         setenv("filesize", buf);
166
167         return 0;
168 }
169
170
171 int zfs_print(const char *entry, const struct zfs_dirhook_info *data)
172 {
173         printf("%s %s\n",
174                    data->dir ? "<DIR> " : "              ",
175                    entry);
176         return 0; /* 0 continue, 1 stop */
177 }
178
179
180
181 static int do_zfs_ls(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
182 {
183         const char *filename = "/";
184         int dev;
185         unsigned long part = 1;
186         char *ep;
187         int part_length;
188         struct device_s vdev;
189
190         if (argc < 3)
191                 return cmd_usage(cmdtp);
192
193         dev = (int)simple_strtoul(argv[2], &ep, 16);
194         zfs_dev_desc = get_dev(argv[1], dev);
195
196         if (zfs_dev_desc == NULL) {
197                 printf("\n** Block device %s %d not supported\n", argv[1], dev);
198                 return 1;
199         }
200
201         if (*ep) {
202                 if (*ep != ':') {
203                         puts("\n** Invalid boot device, use `dev[:part]' **\n");
204                         return 1;
205                 }
206                 part = simple_strtoul(++ep, NULL, 16);
207         }
208
209         if (argc == 4)
210                 filename = argv[3];
211
212         part_length = zfs_set_blk_dev(zfs_dev_desc, part);
213         if (part_length == 0) {
214                 printf("** Bad partition - %s %d:%lu **\n", argv[1], dev, part);
215                 return 1;
216         }
217
218         vdev.part_length = part_length;
219
220         zfs_ls(&vdev, filename,
221                    zfs_print);
222
223         return 0;
224 }
225
226
227 U_BOOT_CMD(zfsls, 4, 1, do_zfs_ls,
228                    "list files in a directory (default /)",
229                    "<interface> <dev[:part]> [directory]\n"
230                    "      - list files from 'dev' on 'interface' in a '/DATASET/@/$dir/'");
231
232 U_BOOT_CMD(zfsload, 6, 0, do_zfs_load,
233                    "load binary file from a ZFS filesystem",
234                    "<interface> <dev[:part]> [addr] [filename] [bytes]\n"
235                    "      - load binary file '/DATASET/@/$dir/$file' from 'dev' on 'interface'\n"
236                    "             to address 'addr' from ZFS filesystem");