Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
E
ezynq
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Elphel
ezynq
Commits
4cc35f88
Commit
4cc35f88
authored
Dec 26, 2018
by
Oleg Dzhimiev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
start over
parent
11a59314
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
0 additions
and
2794 deletions
+0
-2794
fdt_host.h
u-boot-tree/tools/fdt_host.h
+0
-33
fdtgrep.c
u-boot-tree/tools/fdtgrep.c
+0
-1231
fit_check_sign.c
u-boot-tree/tools/fit_check_sign.c
+0
-97
fit_common.c
u-boot-tree/tools/fit_common.c
+0
-107
fit_common.h
u-boot-tree/tools/fit_common.h
+0
-33
fit_image.c
u-boot-tree/tools/fit_image.c
+0
-829
fit_info.c
u-boot-tree/tools/fit_info.c
+0
-109
imagetool.h
u-boot-tree/tools/imagetool.h
+0
-307
mkimage.h
u-boot-tree/tools/mkimage.h
+0
-48
No files found.
u-boot-tree/tools/fdt_host.h
deleted
100644 → 0
View file @
11a59314
/*
* (C) Copyright 2008 Semihalf
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __FDT_HOST_H__
#define __FDT_HOST_H__
/* Make sure to include u-boot version of libfdt include files */
#include "../include/libfdt.h"
#include "../include/fdt_support.h"
/**
* fdt_remove_unused_strings() - Remove any unused strings from an FDT
*
* This creates a new device tree in @new with unused strings removed. The
* called can then use fdt_pack() to minimise the space consumed.
*
* @old: Old device tree blog
* @new: Place to put new device tree blob, which must be as large as
* @old
* @return
* 0, on success
* -FDT_ERR_BADOFFSET, corrupt device tree
* -FDT_ERR_NOSPACE, out of space, which should not happen unless there
* is something very wrong with the device tree input
*/
int
fdt_remove_unused_strings
(
const
void
*
old
,
void
*
new
);
int
fit_check_sign
(
const
void
*
working_fdt
,
const
void
*
key
);
#endif
/* __FDT_HOST_H__ */
u-boot-tree/tools/fdtgrep.c
deleted
100644 → 0
View file @
11a59314
/*
* Copyright (c) 2013, Google Inc.
* Written by Simon Glass <sjg@chromium.org>
*
* SPDX-License-Identifier: GPL-2.0+
*
* Perform a grep of an FDT either displaying the source subset or producing
* a new .dtb subset which can be used as required.
*/
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fdt_host.h"
#include "libfdt_internal.h"
/* Define DEBUG to get some debugging output on stderr */
#ifdef DEBUG
#define debug(a, b...) fprintf(stderr, a, ## b)
#else
#define debug(a, b...)
#endif
/* A linked list of values we are grepping for */
struct
value_node
{
int
type
;
/* Types this value matches (FDT_IS... mask) */
int
include
;
/* 1 to include matches, 0 to exclude */
const
char
*
string
;
/* String to match */
struct
value_node
*
next
;
/* Pointer to next node, or NULL */
};
/* Output formats we support */
enum
output_t
{
OUT_DTS
,
/* Device tree source */
OUT_DTB
,
/* Valid device tree binary */
OUT_BIN
,
/* Fragment of .dtb, for hashing */
};
/* Holds information which controls our output and options */
struct
display_info
{
enum
output_t
output
;
/* Output format */
int
add_aliases
;
/* Add aliases node to output */
int
all
;
/* Display all properties/nodes */
int
colour
;
/* Display output in ANSI colour */
int
region_list
;
/* Output a region list */
int
flags
;
/* Flags (FDT_REG_...) */
int
list_strings
;
/* List strings in string table */
int
show_offset
;
/* Show offset */
int
show_addr
;
/* Show address */
int
header
;
/* Output an FDT header */
int
diff
;
/* Show +/- diff markers */
int
include_root
;
/* Include the root node and all properties */
int
remove_strings
;
/* Remove unused strings */
int
show_dts_version
;
/* Put '/dts-v1/;' on the first line */
int
types_inc
;
/* Mask of types that we include (FDT_IS...) */
int
types_exc
;
/* Mask of types that we exclude (FDT_IS...) */
int
invert
;
/* Invert polarity of match */
struct
value_node
*
value_head
;
/* List of values to match */
const
char
*
output_fname
;
/* Output filename */
FILE
*
fout
;
/* File to write dts/dtb output */
};
static
void
report_error
(
const
char
*
where
,
int
err
)
{
fprintf
(
stderr
,
"Error at '%s': %s
\n
"
,
where
,
fdt_strerror
(
err
));
}
/* Supported ANSI colours */
enum
{
COL_BLACK
,
COL_RED
,
COL_GREEN
,
COL_YELLOW
,
COL_BLUE
,
COL_MAGENTA
,
COL_CYAN
,
COL_WHITE
,
COL_NONE
=
-
1
,
};
/**
* print_ansi_colour() - Print out the ANSI sequence for a colour
*
* @fout: Output file
* @col: Colour to output (COL_...), or COL_NONE to reset colour
*/
static
void
print_ansi_colour
(
FILE
*
fout
,
int
col
)
{
if
(
col
==
COL_NONE
)
fprintf
(
fout
,
"
\033
[0m"
);
else
fprintf
(
fout
,
"
\033
[1;%dm"
,
col
+
30
);
}
/**
* value_add() - Add a new value to our list of things to grep for
*
* @disp: Display structure, holding info about our options
* @headp: Pointer to header pointer of list
* @type: Type of this value (FDT_IS_...)
* @include: 1 if we want to include matches, 0 to exclude
* @str: String value to match
*/
static
int
value_add
(
struct
display_info
*
disp
,
struct
value_node
**
headp
,
int
type
,
int
include
,
const
char
*
str
)
{
struct
value_node
*
node
;
/*
* Keep track of which types we are excluding/including. We don't
* allow both including and excluding things, because it doesn't make
* sense. 'Including' means that everything not mentioned is
* excluded. 'Excluding' means that everything not mentioned is
* included. So using the two together would be meaningless.
*/
if
(
include
)
disp
->
types_inc
|=
type
;
else
disp
->
types_exc
|=
type
;
if
(
disp
->
types_inc
&
disp
->
types_exc
&
type
)
{
fprintf
(
stderr
,
"Cannot use both include and exclude for '%s'
\n
"
,
str
);
return
-
1
;
}
str
=
strdup
(
str
);
node
=
malloc
(
sizeof
(
*
node
));
if
(
!
str
||
!
node
)
{
fprintf
(
stderr
,
"Out of memory
\n
"
);
return
-
1
;
}
node
->
next
=
*
headp
;
node
->
type
=
type
;
node
->
include
=
include
;
node
->
string
=
str
;
*
headp
=
node
;
return
0
;
}
static
bool
util_is_printable_string
(
const
void
*
data
,
int
len
)
{
const
char
*
s
=
data
;
const
char
*
ss
,
*
se
;
/* zero length is not */
if
(
len
==
0
)
return
0
;
/* must terminate with zero */
if
(
s
[
len
-
1
]
!=
'\0'
)
return
0
;
se
=
s
+
len
;
while
(
s
<
se
)
{
ss
=
s
;
while
(
s
<
se
&&
*
s
&&
isprint
((
unsigned
char
)
*
s
))
s
++
;
/* not zero, or not done yet */
if
(
*
s
!=
'\0'
||
s
==
ss
)
return
0
;
s
++
;
}
return
1
;
}
static
void
utilfdt_print_data
(
const
char
*
data
,
int
len
)
{
int
i
;
const
char
*
p
=
data
;
const
char
*
s
;
/* no data, don't print */
if
(
len
==
0
)
return
;
if
(
util_is_printable_string
(
data
,
len
))
{
printf
(
" = "
);
s
=
data
;
do
{
printf
(
"
\"
%s
\"
"
,
s
);
s
+=
strlen
(
s
)
+
1
;
if
(
s
<
data
+
len
)
printf
(
", "
);
}
while
(
s
<
data
+
len
);
}
else
if
((
len
%
4
)
==
0
)
{
const
uint32_t
*
cell
=
(
const
uint32_t
*
)
data
;
printf
(
" = <"
);
for
(
i
=
0
,
len
/=
4
;
i
<
len
;
i
++
)
printf
(
"0x%08x%s"
,
fdt32_to_cpu
(
cell
[
i
]),
i
<
(
len
-
1
)
?
" "
:
""
);
printf
(
">"
);
}
else
{
printf
(
" = ["
);
for
(
i
=
0
;
i
<
len
;
i
++
)
printf
(
"%02x%s"
,
*
p
++
,
i
<
len
-
1
?
" "
:
""
);
printf
(
"]"
);
}
}
/**
* display_fdt_by_regions() - Display regions of an FDT source
*
* This dumps an FDT as source, but only certain regions of it. This is the
* final stage of the grep - we have a list of regions we want to display,
* and this function displays them.
*
* @disp: Display structure, holding info about our options
* @blob: FDT blob to display
* @region: List of regions to display
* @count: Number of regions
*/
static
int
display_fdt_by_regions
(
struct
display_info
*
disp
,
const
void
*
blob
,
struct
fdt_region
region
[],
int
count
)
{
struct
fdt_region
*
reg
=
region
,
*
reg_end
=
region
+
count
;
uint32_t
off_mem_rsvmap
=
fdt_off_mem_rsvmap
(
blob
);
int
base
=
fdt_off_dt_struct
(
blob
);
int
version
=
fdt_version
(
blob
);
int
offset
,
nextoffset
;
int
tag
,
depth
,
shift
;
FILE
*
f
=
disp
->
fout
;
uint64_t
addr
,
size
;
int
in_region
;
int
file_ofs
;
int
i
;
if
(
disp
->
show_dts_version
)
fprintf
(
f
,
"/dts-v1/;
\n
"
);
if
(
disp
->
header
)
{
fprintf
(
f
,
"// magic:
\t\t
0x%x
\n
"
,
fdt_magic
(
blob
));
fprintf
(
f
,
"// totalsize:
\t\t
0x%x (%d)
\n
"
,
fdt_totalsize
(
blob
),
fdt_totalsize
(
blob
));
fprintf
(
f
,
"// off_dt_struct:
\t
0x%x
\n
"
,
fdt_off_dt_struct
(
blob
));
fprintf
(
f
,
"// off_dt_strings:
\t
0x%x
\n
"
,
fdt_off_dt_strings
(
blob
));
fprintf
(
f
,
"// off_mem_rsvmap:
\t
0x%x
\n
"
,
off_mem_rsvmap
);
fprintf
(
f
,
"// version:
\t\t
%d
\n
"
,
version
);
fprintf
(
f
,
"// last_comp_version:
\t
%d
\n
"
,
fdt_last_comp_version
(
blob
));
if
(
version
>=
2
)
{
fprintf
(
f
,
"// boot_cpuid_phys:
\t
0x%x
\n
"
,
fdt_boot_cpuid_phys
(
blob
));
}
if
(
version
>=
3
)
{
fprintf
(
f
,
"// size_dt_strings:
\t
0x%x
\n
"
,
fdt_size_dt_strings
(
blob
));
}
if
(
version
>=
17
)
{
fprintf
(
f
,
"// size_dt_struct:
\t
0x%x
\n
"
,
fdt_size_dt_struct
(
blob
));
}
fprintf
(
f
,
"
\n
"
);
}
if
(
disp
->
flags
&
FDT_REG_ADD_MEM_RSVMAP
)
{
const
struct
fdt_reserve_entry
*
p_rsvmap
;
p_rsvmap
=
(
const
struct
fdt_reserve_entry
*
)
((
const
char
*
)
blob
+
off_mem_rsvmap
);
for
(
i
=
0
;
;
i
++
)
{
addr
=
fdt64_to_cpu
(
p_rsvmap
[
i
].
address
);
size
=
fdt64_to_cpu
(
p_rsvmap
[
i
].
size
);
if
(
addr
==
0
&&
size
==
0
)
break
;
fprintf
(
f
,
"/memreserve/ %llx %llx;
\n
"
,
(
unsigned
long
long
)
addr
,
(
unsigned
long
long
)
size
);
}
}
depth
=
0
;
nextoffset
=
0
;
shift
=
4
;
/* 4 spaces per indent */
do
{
const
struct
fdt_property
*
prop
;
const
char
*
name
;
int
show
;
int
len
;
offset
=
nextoffset
;
/*
* Work out the file offset of this offset, and decide
* whether it is in the region list or not
*/
file_ofs
=
base
+
offset
;
if
(
reg
<
reg_end
&&
file_ofs
>=
reg
->
offset
+
reg
->
size
)
reg
++
;
in_region
=
reg
<
reg_end
&&
file_ofs
>=
reg
->
offset
&&
file_ofs
<
reg
->
offset
+
reg
->
size
;
tag
=
fdt_next_tag
(
blob
,
offset
,
&
nextoffset
);
if
(
tag
==
FDT_END
)
break
;
show
=
in_region
||
disp
->
all
;
if
(
show
&&
disp
->
diff
)
fprintf
(
f
,
"%c"
,
in_region
?
'+'
:
'-'
);
if
(
!
show
)
{
/* Do this here to avoid 'if (show)' in every 'case' */
if
(
tag
==
FDT_BEGIN_NODE
)
depth
++
;
else
if
(
tag
==
FDT_END_NODE
)
depth
--
;
continue
;
}
if
(
tag
!=
FDT_END
)
{
if
(
disp
->
show_addr
)
fprintf
(
f
,
"%4x: "
,
file_ofs
);
if
(
disp
->
show_offset
)
fprintf
(
f
,
"%4x: "
,
file_ofs
-
base
);
}
/* Green means included, red means excluded */
if
(
disp
->
colour
)
print_ansi_colour
(
f
,
in_region
?
COL_GREEN
:
COL_RED
);
switch
(
tag
)
{
case
FDT_PROP
:
prop
=
fdt_get_property_by_offset
(
blob
,
offset
,
NULL
);
name
=
fdt_string
(
blob
,
fdt32_to_cpu
(
prop
->
nameoff
));
fprintf
(
f
,
"%*s%s"
,
depth
*
shift
,
""
,
name
);
utilfdt_print_data
(
prop
->
data
,
fdt32_to_cpu
(
prop
->
len
));
fprintf
(
f
,
";"
);
break
;
case
FDT_NOP
:
fprintf
(
f
,
"%*s// [NOP]"
,
depth
*
shift
,
""
);
break
;
case
FDT_BEGIN_NODE
:
name
=
fdt_get_name
(
blob
,
offset
,
&
len
);
fprintf
(
f
,
"%*s%s {"
,
depth
++
*
shift
,
""
,
*
name
?
name
:
"/"
);
break
;
case
FDT_END_NODE
:
fprintf
(
f
,
"%*s};"
,
--
depth
*
shift
,
""
);
break
;
}
/* Reset colour back to normal before end of line */
if
(
disp
->
colour
)
print_ansi_colour
(
f
,
COL_NONE
);
fprintf
(
f
,
"
\n
"
);
}
while
(
1
);
/* Print a list of strings if requested */
if
(
disp
->
list_strings
)
{
const
char
*
str
;
int
str_base
=
fdt_off_dt_strings
(
blob
);
for
(
offset
=
0
;
offset
<
fdt_size_dt_strings
(
blob
);
offset
+=
strlen
(
str
)
+
1
)
{
str
=
fdt_string
(
blob
,
offset
);
int
len
=
strlen
(
str
)
+
1
;
int
show
;
/* Only print strings that are in the region */
file_ofs
=
str_base
+
offset
;
in_region
=
reg
<
reg_end
&&
file_ofs
>=
reg
->
offset
&&
file_ofs
+
len
<
reg
->
offset
+
reg
->
size
;
show
=
in_region
||
disp
->
all
;
if
(
show
&&
disp
->
diff
)
printf
(
"%c"
,
in_region
?
'+'
:
'-'
);
if
(
disp
->
show_addr
)
printf
(
"%4x: "
,
file_ofs
);
if
(
disp
->
show_offset
)
printf
(
"%4x: "
,
offset
);
printf
(
"%s
\n
"
,
str
);
}
}
return
0
;
}
/**
* dump_fdt_regions() - Dump regions of an FDT as binary data
*
* This dumps an FDT as binary, but only certain regions of it. This is the
* final stage of the grep - we have a list of regions we want to dump,
* and this function dumps them.
*
* The output of this function may or may not be a valid FDT. To ensure it
* is, these disp->flags must be set:
*
* FDT_REG_SUPERNODES: ensures that subnodes are preceded by their
* parents. Without this option, fragments of subnode data may be
* output without the supernodes above them. This is useful for
* hashing but cannot produce a valid FDT.
* FDT_REG_ADD_STRING_TAB: Adds a string table to the end of the FDT.
* Without this none of the properties will have names
* FDT_REG_ADD_MEM_RSVMAP: Adds a mem_rsvmap table - an FDT is invalid
* without this.
*
* @disp: Display structure, holding info about our options
* @blob: FDT blob to display
* @region: List of regions to display
* @count: Number of regions
* @out: Output destination
*/
static
int
dump_fdt_regions
(
struct
display_info
*
disp
,
const
void
*
blob
,
struct
fdt_region
region
[],
int
count
,
char
*
out
)
{
struct
fdt_header
*
fdt
;
int
size
,
struct_start
;
int
ptr
;
int
i
;
/* Set up a basic header (even if we don't actually write it) */
fdt
=
(
struct
fdt_header
*
)
out
;
memset
(
fdt
,
'\0'
,
sizeof
(
*
fdt
));
fdt_set_magic
(
fdt
,
FDT_MAGIC
);
struct_start
=
FDT_ALIGN
(
sizeof
(
struct
fdt_header
),
sizeof
(
struct
fdt_reserve_entry
));
fdt_set_off_mem_rsvmap
(
fdt
,
struct_start
);
fdt_set_version
(
fdt
,
FDT_LAST_SUPPORTED_VERSION
);
fdt_set_last_comp_version
(
fdt
,
FDT_FIRST_SUPPORTED_VERSION
);
/*
* Calculate the total size of the regions we are writing out. The
* first will be the mem_rsvmap if the FDT_REG_ADD_MEM_RSVMAP flag
* is set. The last will be the string table if FDT_REG_ADD_STRING_TAB
* is set.
*/
for
(
i
=
size
=
0
;
i
<
count
;
i
++
)
size
+=
region
[
i
].
size
;
/* Bring in the mem_rsvmap section from the old file if requested */
if
(
count
>
0
&&
(
disp
->
flags
&
FDT_REG_ADD_MEM_RSVMAP
))
{
struct_start
+=
region
[
0
].
size
;
size
-=
region
[
0
].
size
;
}
fdt_set_off_dt_struct
(
fdt
,
struct_start
);
/* Update the header to have the correct offsets/sizes */
if
(
count
>=
2
&&
(
disp
->
flags
&
FDT_REG_ADD_STRING_TAB
))
{
int
str_size
;
str_size
=
region
[
count
-
1
].
size
;
fdt_set_size_dt_struct
(
fdt
,
size
-
str_size
);
fdt_set_off_dt_strings
(
fdt
,
struct_start
+
size
-
str_size
);
fdt_set_size_dt_strings
(
fdt
,
str_size
);
fdt_set_totalsize
(
fdt
,
struct_start
+
size
);
}
/* Write the header if required */
ptr
=
0
;
if
(
disp
->
header
)
{
ptr
=
sizeof
(
*
fdt
);
while
(
ptr
<
fdt_off_mem_rsvmap
(
fdt
))
out
[
ptr
++
]
=
'\0'
;
}
/* Output all the nodes including any mem_rsvmap/string table */
for
(
i
=
0
;
i
<
count
;
i
++
)
{
struct
fdt_region
*
reg
=
&
region
[
i
];
memcpy
(
out
+
ptr
,
(
const
char
*
)
blob
+
reg
->
offset
,
reg
->
size
);
ptr
+=
reg
->
size
;
}
return
ptr
;
}
/**
* show_region_list() - Print out a list of regions
*
* The list includes the region offset (absolute offset from start of FDT
* blob in bytes) and size
*
* @reg: List of regions to print
* @count: Number of regions
*/
static
void
show_region_list
(
struct
fdt_region
*
reg
,
int
count
)
{
int
i
;
printf
(
"Regions: %d
\n
"
,
count
);
for
(
i
=
0
;
i
<
count
;
i
++
,
reg
++
)
{
printf
(
"%d: %-10x %-10x
\n
"
,
i
,
reg
->
offset
,
reg
->
offset
+
reg
->
size
);
}
}
static
int
check_type_include
(
void
*
priv
,
int
type
,
const
char
*
data
,
int
size
)
{
struct
display_info
*
disp
=
priv
;
struct
value_node
*
val
;
int
match
,
none_match
=
FDT_IS_ANY
;
/* If none of our conditions mention this type, we know nothing */
debug
(
"type=%x, data=%s
\n
"
,
type
,
data
?
data
:
"(null)"
);
if
(
!
((
disp
->
types_inc
|
disp
->
types_exc
)
&
type
))
{
debug
(
" - not in any condition
\n
"
);
return
-
1
;
}
/*
* Go through the list of conditions. For inclusive conditions, we
* return 1 at the first match. For exclusive conditions, we must
* check that there are no matches.
*/
if
(
data
)
{
for
(
val
=
disp
->
value_head
;
val
;
val
=
val
->
next
)
{
if
(
!
(
type
&
val
->
type
))
continue
;
match
=
fdt_stringlist_contains
(
data
,
size
,
val
->
string
);
debug
(
" - val->type=%x, str='%s', match=%d
\n
"
,
val
->
type
,
val
->
string
,
match
);
if
(
match
&&
val
->
include
)
{
debug
(
" - match inc %s
\n
"
,
val
->
string
);
return
1
;
}
if
(
match
)
none_match
&=
~
val
->
type
;
}
}
/*
* If this is an exclusive condition, and nothing matches, then we
* should return 1.
*/
if
((
type
&
disp
->
types_exc
)
&&
(
none_match
&
type
))
{
debug
(
" - match exc
\n
"
);
/*
* Allow FDT_IS_COMPAT to make the final decision in the
* case where there is no specific type
*/
if
(
type
==
FDT_IS_NODE
&&
disp
->
types_exc
==
FDT_ANY_GLOBAL
)
{
debug
(
" - supressed exc node
\n
"
);
return
-
1
;
}
return
1
;
}
/*
* Allow FDT_IS_COMPAT to make the final decision in the
* case where there is no specific type (inclusive)
*/
if
(
type
==
FDT_IS_NODE
&&
disp
->
types_inc
==
FDT_ANY_GLOBAL
)
return
-
1
;
debug
(
" - no match, types_inc=%x, types_exc=%x, none_match=%x
\n
"
,
disp
->
types_inc
,
disp
->
types_exc
,
none_match
);
return
0
;
}
/**
* h_include() - Include handler function for fdt_find_regions()
*
* This function decides whether to include or exclude a node, property or
* compatible string. The function is defined by fdt_find_regions().
*
* The algorithm is documented in the code - disp->invert is 0 for normal
* operation, and 1 to invert the sense of all matches.
*
* See
*/
static
int
h_include
(
void
*
priv
,
const
void
*
fdt
,
int
offset
,
int
type
,
const
char
*
data
,
int
size
)
{
struct
display_info
*
disp
=
priv
;
int
inc
,
len
;
inc
=
check_type_include
(
priv
,
type
,
data
,
size
);
if
(
disp
->
include_root
&&
type
==
FDT_IS_PROP
&&
offset
==
0
&&
inc
)
return
1
;
/*
* If the node name does not tell us anything, check the
* compatible string
*/
if
(
inc
==
-
1
&&
type
==
FDT_IS_NODE
)
{
debug
(
" - checking compatible2
\n
"
);
data
=
fdt_getprop
(
fdt
,
offset
,
"compatible"
,
&
len
);
inc
=
check_type_include
(
priv
,
FDT_IS_COMPAT
,
data
,
len
);
}
/* If we still have no idea, check for properties in the node */
if
(
inc
!=
1
&&
type
==
FDT_IS_NODE
&&
(
disp
->
types_inc
&
FDT_NODE_HAS_PROP
))
{
debug
(
" - checking node '%s'
\n
"
,
fdt_get_name
(
fdt
,
offset
,
NULL
));
for
(
offset
=
fdt_first_property_offset
(
fdt
,
offset
);
offset
>
0
&&
inc
!=
1
;
offset
=
fdt_next_property_offset
(
fdt
,
offset
))
{
const
struct
fdt_property
*
prop
;
const
char
*
str
;
prop
=
fdt_get_property_by_offset
(
fdt
,
offset
,
NULL
);
if
(
!
prop
)
continue
;
str
=
fdt_string
(
fdt
,
fdt32_to_cpu
(
prop
->
nameoff
));
inc
=
check_type_include
(
priv
,
FDT_NODE_HAS_PROP
,
str
,
strlen
(
str
));
}
if
(
inc
==
-
1
)
inc
=
0
;
}
switch
(
inc
)
{
case
1
:
inc
=
!
disp
->
invert
;
break
;
case
0
:
inc
=
disp
->
invert
;
break
;
}
debug
(
" - returning %d
\n
"
,
inc
);
return
inc
;
}
static
int
h_cmp_region
(
const
void
*
v1
,
const
void
*
v2
)
{
const
struct
fdt_region
*
region1
=
v1
,
*
region2
=
v2
;
return
region1
->
offset
-
region2
->
offset
;
}
static
int
fdtgrep_find_regions
(
const
void
*
fdt
,
int
(
*
include_func
)(
void
*
priv
,
const
void
*
fdt
,
int
offset
,
int
type
,
const
char
*
data
,
int
size
),
struct
display_info
*
disp
,
struct
fdt_region
*
region
,
int
max_regions
,
char
*
path
,
int
path_len
,
int
flags
)
{
struct
fdt_region_state
state
;
int
count
;
int
ret
;
count
=
0
;
ret
=
fdt_first_region
(
fdt
,
include_func
,
disp
,
&
region
[
count
++
],
path
,
path_len
,
disp
->
flags
,
&
state
);
while
(
ret
==
0
)
{
ret
=
fdt_next_region
(
fdt
,
include_func
,
disp
,
count
<
max_regions
?
&
region
[
count
]
:
NULL
,
path
,
path_len
,
disp
->
flags
,
&
state
);
if
(
!
ret
)
count
++
;
}
if
(
ret
&&
ret
!=
-
FDT_ERR_NOTFOUND
)
return
ret
;
/* Find all the aliases and add those regions back in */
if
(
disp
->
add_aliases
&&
count
<
max_regions
)
{
int
new_count
;
new_count
=
fdt_add_alias_regions
(
fdt
,
region
,
count
,
max_regions
,
&
state
);
if
(
new_count
==
-
FDT_ERR_NOTFOUND
)
{
/* No alias node found */
}
else
if
(
new_count
<
0
)
{
return
new_count
;
}
else
if
(
new_count
<=
max_regions
)
{
/*
* The alias regions will now be at the end of the list.
* Sort the regions by offset to get things into the
* right order
*/
count
=
new_count
;
qsort
(
region
,
count
,
sizeof
(
struct
fdt_region
),
h_cmp_region
);
}
}
return
count
;
}
int
utilfdt_read_err_len
(
const
char
*
filename
,
char
**
buffp
,
off_t
*
len
)
{
int
fd
=
0
;
/* assume stdin */
char
*
buf
=
NULL
;
off_t
bufsize
=
1024
,
offset
=
0
;
int
ret
=
0
;
*
buffp
=
NULL
;
if
(
strcmp
(
filename
,
"-"
)
!=
0
)
{
fd
=
open
(
filename
,
O_RDONLY
);
if
(
fd
<
0
)
return
errno
;
}
/* Loop until we have read everything */
buf
=
malloc
(
bufsize
);
if
(
!
buf
)
return
-
ENOMEM
;
do
{
/* Expand the buffer to hold the next chunk */
if
(
offset
==
bufsize
)
{
bufsize
*=
2
;
buf
=
realloc
(
buf
,
bufsize
);
if
(
!
buf
)
return
-
ENOMEM
;
}
ret
=
read
(
fd
,
&
buf
[
offset
],
bufsize
-
offset
);
if
(
ret
<
0
)
{
ret
=
errno
;
break
;
}
offset
+=
ret
;
}
while
(
ret
!=
0
);
/* Clean up, including closing stdin; return errno on error */
close
(
fd
);
if
(
ret
)
free
(
buf
);
else
*
buffp
=
buf
;
*
len
=
bufsize
;
return
ret
;
}
int
utilfdt_read_err
(
const
char
*
filename
,
char
**
buffp
)
{
off_t
len
;
return
utilfdt_read_err_len
(
filename
,
buffp
,
&
len
);
}
char
*
utilfdt_read_len
(
const
char
*
filename
,
off_t
*
len
)
{
char
*
buff
;
int
ret
=
utilfdt_read_err_len
(
filename
,
&
buff
,
len
);
if
(
ret
)
{
fprintf
(
stderr
,
"Couldn't open blob from '%s': %s
\n
"
,
filename
,
strerror
(
ret
));
return
NULL
;
}
/* Successful read */
return
buff
;
}
char
*
utilfdt_read
(
const
char
*
filename
)
{
off_t
len
;
return
utilfdt_read_len
(
filename
,
&
len
);
}
/**
* Run the main fdtgrep operation, given a filename and valid arguments
*
* @param disp Display information / options
* @param filename Filename of blob file
* @param return 0 if ok, -ve on error
*/
static
int
do_fdtgrep
(
struct
display_info
*
disp
,
const
char
*
filename
)
{
struct
fdt_region
*
region
;
int
max_regions
;
int
count
=
100
;
char
path
[
1024
];
char
*
blob
;
int
i
,
ret
;
blob
=
utilfdt_read
(
filename
);
if
(
!
blob
)
return
-
1
;
ret
=
fdt_check_header
(
blob
);
if
(
ret
)
{
fprintf
(
stderr
,
"Error: %s
\n
"
,
fdt_strerror
(
ret
));
return
ret
;
}
/* Allow old files, but they are untested */
if
(
fdt_version
(
blob
)
<
17
&&
disp
->
value_head
)
{
fprintf
(
stderr
,
"Warning: fdtgrep does not fully support version %d files
\n
"
,
fdt_version
(
blob
));
}
/*
* We do two passes, since we don't know how many regions we need.
* The first pass will count the regions, but if it is too many,
* we do another pass to actually record them.
*/
for
(
i
=
0
;
i
<
3
;
i
++
)
{
region
=
malloc
(
count
*
sizeof
(
struct
fdt_region
));
if
(
!
region
)
{
fprintf
(
stderr
,
"Out of memory for %d regions
\n
"
,
count
);
return
-
1
;
}
max_regions
=
count
;
count
=
fdtgrep_find_regions
(
blob
,
h_include
,
disp
,
region
,
max_regions
,
path
,
sizeof
(
path
),
disp
->
flags
);
if
(
count
<
0
)
{
report_error
(
"fdt_find_regions"
,
count
);
return
-
1
;
}
if
(
count
<=
max_regions
)
break
;
free
(
region
);
}
/* Optionally print a list of regions */
if
(
disp
->
region_list
)
show_region_list
(
region
,
count
);
/* Output either source .dts or binary .dtb */
if
(
disp
->
output
==
OUT_DTS
)
{
ret
=
display_fdt_by_regions
(
disp
,
blob
,
region
,
count
);
}
else
{
void
*
fdt
;
/* Allow reserved memory section to expand slightly */
int
size
=
fdt_totalsize
(
blob
)
+
16
;
fdt
=
malloc
(
size
);
if
(
!
fdt
)
{
fprintf
(
stderr
,
"Out_of_memory
\n
"
);
ret
=
-
1
;
goto
err
;
}
size
=
dump_fdt_regions
(
disp
,
blob
,
region
,
count
,
fdt
);
if
(
disp
->
remove_strings
)
{
void
*
out
;
out
=
malloc
(
size
);
if
(
!
out
)
{
fprintf
(
stderr
,
"Out_of_memory
\n
"
);
ret
=
-
1
;
goto
err
;
}
ret
=
fdt_remove_unused_strings
(
fdt
,
out
);
if
(
ret
<
0
)
{
fprintf
(
stderr
,
"Failed to remove unused strings: err=%d
\n
"
,
ret
);
goto
err
;
}
free
(
fdt
);
fdt
=
out
;
ret
=
fdt_pack
(
fdt
);
if
(
ret
<
0
)
{
fprintf
(
stderr
,
"Failed to pack: err=%d
\n
"
,
ret
);
goto
err
;
}
size
=
fdt_totalsize
(
fdt
);
}
if
(
size
!=
fwrite
(
fdt
,
1
,
size
,
disp
->
fout
))
{
fprintf
(
stderr
,
"Write failure, %d bytes
\n
"
,
size
);
free
(
fdt
);
ret
=
1
;
goto
err
;
}
free
(
fdt
);
}
err:
free
(
blob
);
free
(
region
);
return
ret
;
}
static
const
char
usage_synopsis
[]
=
"fdtgrep - extract portions from device tree
\n
"
"
\n
"
"Usage:
\n
"
" fdtgrep <options> <dt file>|-
\n\n
"
"Output formats are:
\n
"
"
\t
dts - device tree soure text
\n
"
"
\t
dtb - device tree blob (sets -Hmt automatically)
\n
"
"
\t
bin - device tree fragment (may not be a valid .dtb)"
;
/* Helper for usage_short_opts string constant */
#define USAGE_COMMON_SHORT_OPTS "hV"
/* Helper for aligning long_opts array */
#define a_argument required_argument
/* Helper for usage_long_opts option array */
#define USAGE_COMMON_LONG_OPTS \
{"help", no_argument, NULL, 'h'}, \
{"version", no_argument, NULL, 'V'}, \
{NULL, no_argument, NULL, 0x0}
/* Helper for usage_opts_help array */
#define USAGE_COMMON_OPTS_HELP \
"Print this help and exit", \
"Print version and exit", \
NULL
/* Helper for getopt case statements */
#define case_USAGE_COMMON_FLAGS \
case 'h': usage(NULL); \
case 'V': util_version(); \
case '?': usage("unknown option");
static
const
char
usage_short_opts
[]
=
"haAc:b:C:defg:G:HIlLmn:N:o:O:p:P:rRsStTv"
USAGE_COMMON_SHORT_OPTS
;
static
struct
option
const
usage_long_opts
[]
=
{
{
"show-address"
,
no_argument
,
NULL
,
'a'
},
{
"colour"
,
no_argument
,
NULL
,
'A'
},
{
"include-node-with-prop"
,
a_argument
,
NULL
,
'b'
},
{
"include-compat"
,
a_argument
,
NULL
,
'c'
},
{
"exclude-compat"
,
a_argument
,
NULL
,
'C'
},
{
"diff"
,
no_argument
,
NULL
,
'd'
},
{
"enter-node"
,
no_argument
,
NULL
,
'e'
},
{
"show-offset"
,
no_argument
,
NULL
,
'f'
},
{
"include-match"
,
a_argument
,
NULL
,
'g'
},
{
"exclude-match"
,
a_argument
,
NULL
,
'G'
},
{
"show-header"
,
no_argument
,
NULL
,
'H'
},
{
"show-version"
,
no_argument
,
NULL
,
'I'
},
{
"list-regions"
,
no_argument
,
NULL
,
'l'
},
{
"list-strings"
,
no_argument
,
NULL
,
'L'
},
{
"include-mem"
,
no_argument
,
NULL
,
'm'
},
{
"include-node"
,
a_argument
,
NULL
,
'n'
},
{
"exclude-node"
,
a_argument
,
NULL
,
'N'
},
{
"include-prop"
,
a_argument
,
NULL
,
'p'
},
{
"exclude-prop"
,
a_argument
,
NULL
,
'P'
},
{
"remove-strings"
,
no_argument
,
NULL
,
'r'
},
{
"include-root"
,
no_argument
,
NULL
,
'R'
},
{
"show-subnodes"
,
no_argument
,
NULL
,
's'
},
{
"skip-supernodes"
,
no_argument
,
NULL
,
'S'
},
{
"show-stringtab"
,
no_argument
,
NULL
,
't'
},
{
"show-aliases"
,
no_argument
,
NULL
,
'T'
},
{
"out"
,
a_argument
,
NULL
,
'o'
},
{
"out-format"
,
a_argument
,
NULL
,
'O'
},
{
"invert-match"
,
no_argument
,
NULL
,
'v'
},
USAGE_COMMON_LONG_OPTS
,
};
static
const
char
*
const
usage_opts_help
[]
=
{
"Display address"
,
"Show all nodes/tags, colour those that match"
,
"Include contains containing property"
,
"Compatible nodes to include in grep"
,
"Compatible nodes to exclude in grep"
,
"Diff: Mark matching nodes with +, others with -"
,
"Enter direct subnode names of matching nodes"
,
"Display offset"
,
"Node/property/compatible string to include in grep"
,
"Node/property/compatible string to exclude in grep"
,
"Output a header"
,
"Put
\"
/dts-v1/;
\"
on first line of dts output"
,
"Output a region list"
,
"List strings in string table"
,
"Include mem_rsvmap section in binary output"
,
"Node to include in grep"
,
"Node to exclude in grep"
,
"Property to include in grep"
,
"Property to exclude in grep"
,
"Remove unused strings from string table"
,
"Include root node and all properties"
,
"Show all subnodes matching nodes"
,
"Don't include supernodes of matching nodes"
,
"Include string table in binary output"
,
"Include matching aliases in output"
,
"-o <output file>"
,
"-O <output format>"
,
"Invert the sense of matching (select non-matching lines)"
,
USAGE_COMMON_OPTS_HELP
};
/**
* Call getopt_long() with standard options
*
* Since all util code runs getopt in the same way, provide a helper.
*/
#define util_getopt_long() getopt_long(argc, argv, usage_short_opts, \
usage_long_opts, NULL)
void
util_usage
(
const
char
*
errmsg
,
const
char
*
synopsis
,
const
char
*
short_opts
,
struct
option
const
long_opts
[],
const
char
*
const
opts_help
[])
{
FILE
*
fp
=
errmsg
?
stderr
:
stdout
;
const
char
a_arg
[]
=
"<arg>"
;
size_t
a_arg_len
=
strlen
(
a_arg
)
+
1
;
size_t
i
;
int
optlen
;
fprintf
(
fp
,
"Usage: %s
\n
"
"
\n
"
"Options: -[%s]
\n
"
,
synopsis
,
short_opts
);
/* prescan the --long opt length to auto-align */
optlen
=
0
;
for
(
i
=
0
;
long_opts
[
i
].
name
;
++
i
)
{
/* +1 is for space between --opt and help text */
int
l
=
strlen
(
long_opts
[
i
].
name
)
+
1
;
if
(
long_opts
[
i
].
has_arg
==
a_argument
)
l
+=
a_arg_len
;
if
(
optlen
<
l
)
optlen
=
l
;
}
for
(
i
=
0
;
long_opts
[
i
].
name
;
++
i
)
{
/* helps when adding new applets or options */
assert
(
opts_help
[
i
]
!=
NULL
);
/* first output the short flag if it has one */
if
(
long_opts
[
i
].
val
>
'~'
)
fprintf
(
fp
,
" "
);
else
fprintf
(
fp
,
" -%c, "
,
long_opts
[
i
].
val
);
/* then the long flag */
if
(
long_opts
[
i
].
has_arg
==
no_argument
)
{
fprintf
(
fp
,
"--%-*s"
,
optlen
,
long_opts
[
i
].
name
);
}
else
{
fprintf
(
fp
,
"--%s %s%*s"
,
long_opts
[
i
].
name
,
a_arg
,
(
int
)(
optlen
-
strlen
(
long_opts
[
i
].
name
)
-
a_arg_len
),
""
);
}
/* finally the help text */
fprintf
(
fp
,
"%s
\n
"
,
opts_help
[
i
]);
}
if
(
errmsg
)
{
fprintf
(
fp
,
"
\n
Error: %s
\n
"
,
errmsg
);
exit
(
EXIT_FAILURE
);
}
else
{
exit
(
EXIT_SUCCESS
);
}
}
/**
* Show usage and exit
*
* If you name all your usage variables with usage_xxx, then you can call this
* help macro rather than expanding all arguments yourself.
*
* @param errmsg If non-NULL, an error message to display
*/
#define usage(errmsg) \
util_usage(errmsg, usage_synopsis, usage_short_opts, \
usage_long_opts, usage_opts_help)
void
util_version
(
void
)
{
printf
(
"Version: %s
\n
"
,
"(U-Boot)"
);
exit
(
0
);
}
static
void
scan_args
(
struct
display_info
*
disp
,
int
argc
,
char
*
argv
[])
{
int
opt
;
while
((
opt
=
util_getopt_long
())
!=
EOF
)
{
int
type
=
0
;
int
inc
=
1
;
switch
(
opt
)
{
case_USAGE_COMMON_FLAGS
case
'a'
:
disp
->
show_addr
=
1
;
break
;
case
'A'
:
disp
->
all
=
1
;
break
;
case
'b'
:
type
=
FDT_NODE_HAS_PROP
;
break
;
case
'C'
:
inc
=
0
;
/* no break */
case
'c'
:
type
=
FDT_IS_COMPAT
;
break
;
case
'd'
:
disp
->
diff
=
1
;
break
;
case
'e'
:
disp
->
flags
|=
FDT_REG_DIRECT_SUBNODES
;
break
;
case
'f'
:
disp
->
show_offset
=
1
;
break
;
case
'G'
:
inc
=
0
;
/* no break */
case
'g'
:
type
=
FDT_ANY_GLOBAL
;
break
;
case
'H'
:
disp
->
header
=
1
;
break
;
case
'l'
:
disp
->
region_list
=
1
;
break
;
case
'L'
:
disp
->
list_strings
=
1
;
break
;
case
'm'
:
disp
->
flags
|=
FDT_REG_ADD_MEM_RSVMAP
;
break
;
case
'N'
:
inc
=
0
;
/* no break */
case
'n'
:
type
=
FDT_IS_NODE
;
break
;
case
'o'
:
disp
->
output_fname
=
optarg
;
break
;
case
'O'
:
if
(
!
strcmp
(
optarg
,
"dtb"
))
disp
->
output
=
OUT_DTB
;
else
if
(
!
strcmp
(
optarg
,
"dts"
))
disp
->
output
=
OUT_DTS
;
else
if
(
!
strcmp
(
optarg
,
"bin"
))
disp
->
output
=
OUT_BIN
;
else
usage
(
"Unknown output format"
);
break
;
case
'P'
:
inc
=
0
;
/* no break */
case
'p'
:
type
=
FDT_IS_PROP
;
break
;
case
'r'
:
disp
->
remove_strings
=
1
;
break
;
case
'R'
:
disp
->
include_root
=
1
;
break
;
case
's'
:
disp
->
flags
|=
FDT_REG_ALL_SUBNODES
;
break
;
case
'S'
:
disp
->
flags
&=
~
FDT_REG_SUPERNODES
;
break
;
case
't'
:
disp
->
flags
|=
FDT_REG_ADD_STRING_TAB
;
break
;
case
'T'
:
disp
->
add_aliases
=
1
;
break
;
case
'v'
:
disp
->
invert
=
1
;
break
;
case
'I'
:
disp
->
show_dts_version
=
1
;
break
;
}
if
(
type
&&
value_add
(
disp
,
&
disp
->
value_head
,
type
,
inc
,
optarg
))
usage
(
"Cannot add value"
);
}
if
(
disp
->
invert
&&
disp
->
types_exc
)
usage
(
"-v has no meaning when used with 'exclude' conditions"
);
}
int
main
(
int
argc
,
char
*
argv
[])
{
char
*
filename
=
NULL
;
struct
display_info
disp
;
int
ret
;
/* set defaults */
memset
(
&
disp
,
'\0'
,
sizeof
(
disp
));
disp
.
flags
=
FDT_REG_SUPERNODES
;
/* Default flags */
scan_args
(
&
disp
,
argc
,
argv
);
/* Show matched lines in colour if we can */
disp
.
colour
=
disp
.
all
&&
isatty
(
0
);
/* Any additional arguments can match anything, just like -g */
while
(
optind
<
argc
-
1
)
{
if
(
value_add
(
&
disp
,
&
disp
.
value_head
,
FDT_IS_ANY
,
1
,
argv
[
optind
++
]))
usage
(
"Cannot add value"
);
}
if
(
optind
<
argc
)
filename
=
argv
[
optind
++
];
if
(
!
filename
)
usage
(
"Missing filename"
);
/* If a valid .dtb is required, set flags to ensure we get one */
if
(
disp
.
output
==
OUT_DTB
)
{
disp
.
header
=
1
;
disp
.
flags
|=
FDT_REG_ADD_MEM_RSVMAP
|
FDT_REG_ADD_STRING_TAB
;
}
if
(
disp
.
output_fname
)
{
disp
.
fout
=
fopen
(
disp
.
output_fname
,
"w"
);
if
(
!
disp
.
fout
)
usage
(
"Cannot open output file"
);
}
else
{
disp
.
fout
=
stdout
;
}
/* Run the grep and output the results */
ret
=
do_fdtgrep
(
&
disp
,
filename
);
if
(
disp
.
output_fname
)
fclose
(
disp
.
fout
);
if
(
ret
)
return
1
;
return
0
;
}
u-boot-tree/tools/fit_check_sign.c
deleted
100644 → 0
View file @
11a59314
/*
* (C) Copyright 2014
* DENX Software Engineering
* Heiko Schocher <hs@denx.de>
*
* Based on:
* (C) Copyright 2008 Semihalf
*
* (C) Copyright 2000-2004
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* Updated-by: Prafulla Wadaskar <prafulla@marvell.com>
* FIT image specific code abstracted from mkimage.c
* some functions added to address abstraction
*
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include "mkimage.h"
#include "fit_common.h"
#include <image.h>
#include <u-boot/crc.h>
void
usage
(
char
*
cmdname
)
{
fprintf
(
stderr
,
"Usage: %s -f fit file -k key file
\n
"
" -f ==> set fit file which should be checked'
\n
"
" -k ==> set key file which contains the key'
\n
"
,
cmdname
);
exit
(
EXIT_FAILURE
);
}
int
main
(
int
argc
,
char
**
argv
)
{
int
ffd
=
-
1
;
int
kfd
=
-
1
;
struct
stat
fsbuf
;
struct
stat
ksbuf
;
void
*
fit_blob
;
char
*
fdtfile
=
NULL
;
char
*
keyfile
=
NULL
;
char
cmdname
[
256
];
int
ret
;
void
*
key_blob
;
int
c
;
strncpy
(
cmdname
,
*
argv
,
sizeof
(
cmdname
)
-
1
);
cmdname
[
sizeof
(
cmdname
)
-
1
]
=
'\0'
;
while
((
c
=
getopt
(
argc
,
argv
,
"f:k:"
))
!=
-
1
)
switch
(
c
)
{
case
'f'
:
fdtfile
=
optarg
;
break
;
case
'k'
:
keyfile
=
optarg
;
break
;
default:
usage
(
cmdname
);
break
;
}
if
(
!
fdtfile
)
{
fprintf
(
stderr
,
"%s: Missing fdt file
\n
"
,
*
argv
);
usage
(
*
argv
);
}
if
(
!
keyfile
)
{
fprintf
(
stderr
,
"%s: Missing key file
\n
"
,
*
argv
);
usage
(
*
argv
);
}
ffd
=
mmap_fdt
(
cmdname
,
fdtfile
,
0
,
&
fit_blob
,
&
fsbuf
,
false
);
if
(
ffd
<
0
)
return
EXIT_FAILURE
;
kfd
=
mmap_fdt
(
cmdname
,
keyfile
,
0
,
&
key_blob
,
&
ksbuf
,
false
);
if
(
kfd
<
0
)
return
EXIT_FAILURE
;
image_set_host_blob
(
key_blob
);
ret
=
fit_check_sign
(
fit_blob
,
key_blob
);
if
(
!
ret
)
{
ret
=
EXIT_SUCCESS
;
fprintf
(
stderr
,
"Signature check OK
\n
"
);
}
else
{
ret
=
EXIT_FAILURE
;
fprintf
(
stderr
,
"Signature check Bad (error %d)
\n
"
,
ret
);
}
(
void
)
munmap
((
void
*
)
fit_blob
,
fsbuf
.
st_size
);
(
void
)
munmap
((
void
*
)
key_blob
,
ksbuf
.
st_size
);
close
(
ffd
);
close
(
kfd
);
exit
(
ret
);
}
u-boot-tree/tools/fit_common.c
deleted
100644 → 0
View file @
11a59314
/*
* (C) Copyright 2014
* DENX Software Engineering
* Heiko Schocher <hs@denx.de>
*
* (C) Copyright 2008 Semihalf
*
* (C) Copyright 2000-2004
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* Updated-by: Prafulla Wadaskar <prafulla@marvell.com>
* FIT image specific code abstracted from mkimage.c
* some functions added to address abstraction
*
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include "imagetool.h"
#include "mkimage.h"
#include "fit_common.h"
#include <image.h>
#include <u-boot/crc.h>
int
fit_verify_header
(
unsigned
char
*
ptr
,
int
image_size
,
struct
image_tool_params
*
params
)
{
return
fdt_check_header
(
ptr
);
}
int
fit_check_image_types
(
uint8_t
type
)
{
if
(
type
==
IH_TYPE_FLATDT
)
return
EXIT_SUCCESS
;
else
return
EXIT_FAILURE
;
}
int
mmap_fdt
(
const
char
*
cmdname
,
const
char
*
fname
,
size_t
size_inc
,
void
**
blobp
,
struct
stat
*
sbuf
,
bool
delete_on_error
)
{
void
*
ptr
;
int
fd
;
/* Load FIT blob into memory (we need to write hashes/signatures) */
fd
=
open
(
fname
,
O_RDWR
|
O_BINARY
);
if
(
fd
<
0
)
{
fprintf
(
stderr
,
"%s: Can't open %s: %s
\n
"
,
cmdname
,
fname
,
strerror
(
errno
));
goto
err
;
}
if
(
fstat
(
fd
,
sbuf
)
<
0
)
{
fprintf
(
stderr
,
"%s: Can't stat %s: %s
\n
"
,
cmdname
,
fname
,
strerror
(
errno
));
goto
err
;
}
if
(
size_inc
)
{
sbuf
->
st_size
+=
size_inc
;
if
(
ftruncate
(
fd
,
sbuf
->
st_size
))
{
fprintf
(
stderr
,
"%s: Can't expand %s: %s
\n
"
,
cmdname
,
fname
,
strerror
(
errno
));
goto
err
;
}
}
errno
=
0
;
ptr
=
mmap
(
0
,
sbuf
->
st_size
,
PROT_READ
|
PROT_WRITE
,
MAP_SHARED
,
fd
,
0
);
if
((
ptr
==
MAP_FAILED
)
||
(
errno
!=
0
))
{
fprintf
(
stderr
,
"%s: Can't read %s: %s
\n
"
,
cmdname
,
fname
,
strerror
(
errno
));
goto
err
;
}
/* check if ptr has a valid blob */
if
(
fdt_check_header
(
ptr
))
{
fprintf
(
stderr
,
"%s: Invalid FIT blob
\n
"
,
cmdname
);
goto
err
;
}
/* expand if needed */
if
(
size_inc
)
{
int
ret
;
ret
=
fdt_open_into
(
ptr
,
ptr
,
sbuf
->
st_size
);
if
(
ret
)
{
fprintf
(
stderr
,
"%s: Cannot expand FDT: %s
\n
"
,
cmdname
,
fdt_strerror
(
ret
));
goto
err
;
}
}
*
blobp
=
ptr
;
return
fd
;
err:
if
(
fd
>=
0
)
close
(
fd
);
if
(
delete_on_error
)
unlink
(
fname
);
return
-
1
;
}
u-boot-tree/tools/fit_common.h
deleted
100644 → 0
View file @
11a59314
/*
* (C) Copyright 2014
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _FIT_COMMON_H_
#define _FIT_COMMON_H_
#include "imagetool.h"
#include "mkimage.h"
#include <image.h>
int
fit_verify_header
(
unsigned
char
*
ptr
,
int
image_size
,
struct
image_tool_params
*
params
);
int
fit_check_image_types
(
uint8_t
type
);
/**
* Map an FDT into memory, optionally increasing its size
*
* @cmdname: Tool name (for displaying with error messages)
* @fname: Filename containing FDT
* @size_inc: Amount to increase size by (0 = leave it alone)
* @blobp: Returns pointer to FDT blob
* @sbuf: File status information is stored here
* @delete_on_error: true to delete the file if we get an error
* @return 0 if OK, -1 on error.
*/
int
mmap_fdt
(
const
char
*
cmdname
,
const
char
*
fname
,
size_t
size_inc
,
void
**
blobp
,
struct
stat
*
sbuf
,
bool
delete_on_error
);
#endif
/* _FIT_COMMON_H_ */
u-boot-tree/tools/fit_image.c
deleted
100644 → 0
View file @
11a59314
/*
* (C) Copyright 2008 Semihalf
*
* (C) Copyright 2000-2004
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* Updated-by: Prafulla Wadaskar <prafulla@marvell.com>
* FIT image specific code abstracted from mkimage.c
* some functions added to address abstraction
*
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include "imagetool.h"
#include "fit_common.h"
#include "mkimage.h"
#include <image.h>
#include <stdarg.h>
#include <version.h>
#include <u-boot/crc.h>
static
image_header_t
header
;
static
int
fit_add_file_data
(
struct
image_tool_params
*
params
,
size_t
size_inc
,
const
char
*
tmpfile
)
{
int
tfd
,
destfd
=
0
;
void
*
dest_blob
=
NULL
;
off_t
destfd_size
=
0
;
struct
stat
sbuf
;
void
*
ptr
;
int
ret
=
0
;
tfd
=
mmap_fdt
(
params
->
cmdname
,
tmpfile
,
size_inc
,
&
ptr
,
&
sbuf
,
true
);
if
(
tfd
<
0
)
return
-
EIO
;
if
(
params
->
keydest
)
{
struct
stat
dest_sbuf
;
destfd
=
mmap_fdt
(
params
->
cmdname
,
params
->
keydest
,
size_inc
,
&
dest_blob
,
&
dest_sbuf
,
false
);
if
(
destfd
<
0
)
{
ret
=
-
EIO
;
goto
err_keydest
;
}
destfd_size
=
dest_sbuf
.
st_size
;
}
/* for first image creation, add a timestamp at offset 0 i.e., root */
if
(
params
->
datafile
)
{
time_t
time
=
imagetool_get_source_date
(
params
,
sbuf
.
st_mtime
);
ret
=
fit_set_timestamp
(
ptr
,
0
,
time
);
}
if
(
!
ret
)
{
ret
=
fit_add_verification_data
(
params
->
keydir
,
dest_blob
,
ptr
,
params
->
comment
,
params
->
require_keys
,
params
->
engine_id
);
}
if
(
dest_blob
)
{
munmap
(
dest_blob
,
destfd_size
);
close
(
destfd
);
}
err_keydest:
munmap
(
ptr
,
sbuf
.
st_size
);
close
(
tfd
);
return
ret
;
}
/**
* fit_calc_size() - Calculate the approximate size of the FIT we will generate
*/
static
int
fit_calc_size
(
struct
image_tool_params
*
params
)
{
struct
content_info
*
cont
;
int
size
,
total_size
;
size
=
imagetool_get_filesize
(
params
,
params
->
datafile
);
if
(
size
<
0
)
return
-
1
;
total_size
=
size
;
if
(
params
->
fit_ramdisk
)
{
size
=
imagetool_get_filesize
(
params
,
params
->
fit_ramdisk
);
if
(
size
<
0
)
return
-
1
;
total_size
+=
size
;
}
for
(
cont
=
params
->
content_head
;
cont
;
cont
=
cont
->
next
)
{
size
=
imagetool_get_filesize
(
params
,
cont
->
fname
);
if
(
size
<
0
)
return
-
1
;
/* Add space for properties */
total_size
+=
size
+
300
;
}
/* Add plenty of space for headers, properties, nodes, etc. */
total_size
+=
4096
;
return
total_size
;
}
static
int
fdt_property_file
(
struct
image_tool_params
*
params
,
void
*
fdt
,
const
char
*
name
,
const
char
*
fname
)
{
struct
stat
sbuf
;
void
*
ptr
;
int
ret
;
int
fd
;
fd
=
open
(
fname
,
O_RDWR
|
O_BINARY
);
if
(
fd
<
0
)
{
fprintf
(
stderr
,
"%s: Can't open %s: %s
\n
"
,
params
->
cmdname
,
fname
,
strerror
(
errno
));
return
-
1
;
}
if
(
fstat
(
fd
,
&
sbuf
)
<
0
)
{
fprintf
(
stderr
,
"%s: Can't stat %s: %s
\n
"
,
params
->
cmdname
,
fname
,
strerror
(
errno
));
goto
err
;
}
ret
=
fdt_property_placeholder
(
fdt
,
"data"
,
sbuf
.
st_size
,
&
ptr
);
if
(
ret
)
goto
err
;
ret
=
read
(
fd
,
ptr
,
sbuf
.
st_size
);
if
(
ret
!=
sbuf
.
st_size
)
{
fprintf
(
stderr
,
"%s: Can't read %s: %s
\n
"
,
params
->
cmdname
,
fname
,
strerror
(
errno
));
goto
err
;
}
close
(
fd
);
return
0
;
err:
close
(
fd
);
return
-
1
;
}
static
int
fdt_property_strf
(
void
*
fdt
,
const
char
*
name
,
const
char
*
fmt
,
...)
{
char
str
[
100
];
va_list
ptr
;
va_start
(
ptr
,
fmt
);
vsnprintf
(
str
,
sizeof
(
str
),
fmt
,
ptr
);
va_end
(
ptr
);
return
fdt_property_string
(
fdt
,
name
,
str
);
}
static
void
get_basename
(
char
*
str
,
int
size
,
const
char
*
fname
)
{
const
char
*
p
,
*
start
,
*
end
;
int
len
;
/*
* Use the base name as the 'name' field. So for example:
*
* "arch/arm/dts/sun7i-a20-bananapro.dtb"
* becomes "sun7i-a20-bananapro"
*/
p
=
strrchr
(
fname
,
'/'
);
start
=
p
?
p
+
1
:
fname
;
p
=
strrchr
(
fname
,
'.'
);
end
=
p
?
p
:
fname
+
strlen
(
fname
);
len
=
end
-
start
;
if
(
len
>=
size
)
len
=
size
-
1
;
memcpy
(
str
,
start
,
len
);
str
[
len
]
=
'\0'
;
}
/**
* fit_write_images() - Write out a list of images to the FIT
*
* We always include the main image (params->datafile). If there are device
* tree files, we include an fdt- node for each of those too.
*/
static
int
fit_write_images
(
struct
image_tool_params
*
params
,
char
*
fdt
)
{
struct
content_info
*
cont
;
const
char
*
typename
;
char
str
[
100
];
int
upto
;
int
ret
;
fdt_begin_node
(
fdt
,
"images"
);
/* First the main image */
typename
=
genimg_get_type_short_name
(
params
->
fit_image_type
);
snprintf
(
str
,
sizeof
(
str
),
"%s-1"
,
typename
);
fdt_begin_node
(
fdt
,
str
);
fdt_property_string
(
fdt
,
"description"
,
params
->
imagename
);
fdt_property_string
(
fdt
,
"type"
,
typename
);
fdt_property_string
(
fdt
,
"arch"
,
genimg_get_arch_short_name
(
params
->
arch
));
fdt_property_string
(
fdt
,
"os"
,
genimg_get_os_short_name
(
params
->
os
));
fdt_property_string
(
fdt
,
"compression"
,
genimg_get_comp_short_name
(
params
->
comp
));
fdt_property_u32
(
fdt
,
"load"
,
params
->
addr
);
fdt_property_u32
(
fdt
,
"entry"
,
params
->
ep
);
/*
* Put data last since it is large. SPL may only load the first part
* of the DT, so this way it can access all the above fields.
*/
ret
=
fdt_property_file
(
params
,
fdt
,
"data"
,
params
->
datafile
);
if
(
ret
)
return
ret
;
fdt_end_node
(
fdt
);
/* Now the device tree files if available */
upto
=
0
;
for
(
cont
=
params
->
content_head
;
cont
;
cont
=
cont
->
next
)
{
if
(
cont
->
type
!=
IH_TYPE_FLATDT
)
continue
;
snprintf
(
str
,
sizeof
(
str
),
"%s-%d"
,
FIT_FDT_PROP
,
++
upto
);
fdt_begin_node
(
fdt
,
str
);
get_basename
(
str
,
sizeof
(
str
),
cont
->
fname
);
fdt_property_string
(
fdt
,
"description"
,
str
);
ret
=
fdt_property_file
(
params
,
fdt
,
"data"
,
cont
->
fname
);
if
(
ret
)
return
ret
;
fdt_property_string
(
fdt
,
"type"
,
typename
);
fdt_property_string
(
fdt
,
"arch"
,
genimg_get_arch_short_name
(
params
->
arch
));
fdt_property_string
(
fdt
,
"compression"
,
genimg_get_comp_short_name
(
IH_COMP_NONE
));
fdt_end_node
(
fdt
);
}
/* And a ramdisk file if available */
if
(
params
->
fit_ramdisk
)
{
fdt_begin_node
(
fdt
,
FIT_RAMDISK_PROP
"-1"
);
fdt_property_string
(
fdt
,
"type"
,
FIT_RAMDISK_PROP
);
fdt_property_string
(
fdt
,
"os"
,
genimg_get_os_short_name
(
params
->
os
));
ret
=
fdt_property_file
(
params
,
fdt
,
"data"
,
params
->
fit_ramdisk
);
if
(
ret
)
return
ret
;
fdt_end_node
(
fdt
);
}
fdt_end_node
(
fdt
);
return
0
;
}
/**
* fit_write_configs() - Write out a list of configurations to the FIT
*
* If there are device tree files, we include a configuration for each, which
* selects the main image (params->datafile) and its corresponding device
* tree file.
*
* Otherwise we just create a configuration with the main image in it.
*/
static
void
fit_write_configs
(
struct
image_tool_params
*
params
,
char
*
fdt
)
{
struct
content_info
*
cont
;
const
char
*
typename
;
char
str
[
100
];
int
upto
;
fdt_begin_node
(
fdt
,
"configurations"
);
fdt_property_string
(
fdt
,
"default"
,
"conf-1"
);
upto
=
0
;
for
(
cont
=
params
->
content_head
;
cont
;
cont
=
cont
->
next
)
{
if
(
cont
->
type
!=
IH_TYPE_FLATDT
)
continue
;
typename
=
genimg_get_type_short_name
(
cont
->
type
);
snprintf
(
str
,
sizeof
(
str
),
"conf-%d"
,
++
upto
);
fdt_begin_node
(
fdt
,
str
);
get_basename
(
str
,
sizeof
(
str
),
cont
->
fname
);
fdt_property_string
(
fdt
,
"description"
,
str
);
typename
=
genimg_get_type_short_name
(
params
->
fit_image_type
);
snprintf
(
str
,
sizeof
(
str
),
"%s-1"
,
typename
);
fdt_property_string
(
fdt
,
typename
,
str
);
if
(
params
->
fit_ramdisk
)
fdt_property_string
(
fdt
,
FIT_RAMDISK_PROP
,
FIT_RAMDISK_PROP
"-1"
);
snprintf
(
str
,
sizeof
(
str
),
FIT_FDT_PROP
"-%d"
,
upto
);
fdt_property_string
(
fdt
,
FIT_FDT_PROP
,
str
);
fdt_end_node
(
fdt
);
}
if
(
!
upto
)
{
fdt_begin_node
(
fdt
,
"conf-1"
);
typename
=
genimg_get_type_short_name
(
params
->
fit_image_type
);
snprintf
(
str
,
sizeof
(
str
),
"%s-1"
,
typename
);
fdt_property_string
(
fdt
,
typename
,
str
);
if
(
params
->
fit_ramdisk
)
fdt_property_string
(
fdt
,
FIT_RAMDISK_PROP
,
FIT_RAMDISK_PROP
"-1"
);
fdt_end_node
(
fdt
);
}
fdt_end_node
(
fdt
);
}
static
int
fit_build_fdt
(
struct
image_tool_params
*
params
,
char
*
fdt
,
int
size
)
{
int
ret
;
ret
=
fdt_create
(
fdt
,
size
);
if
(
ret
)
return
ret
;
fdt_finish_reservemap
(
fdt
);
fdt_begin_node
(
fdt
,
""
);
fdt_property_strf
(
fdt
,
"description"
,
"%s image with one or more FDT blobs"
,
genimg_get_type_name
(
params
->
fit_image_type
));
fdt_property_strf
(
fdt
,
"creator"
,
"U-Boot mkimage %s"
,
PLAIN_VERSION
);
fdt_property_u32
(
fdt
,
"#address-cells"
,
1
);
ret
=
fit_write_images
(
params
,
fdt
);
if
(
ret
)
return
ret
;
fit_write_configs
(
params
,
fdt
);
fdt_end_node
(
fdt
);
ret
=
fdt_finish
(
fdt
);
if
(
ret
)
return
ret
;
return
fdt_totalsize
(
fdt
);
}
static
int
fit_build
(
struct
image_tool_params
*
params
,
const
char
*
fname
)
{
char
*
buf
;
int
size
;
int
ret
;
int
fd
;
size
=
fit_calc_size
(
params
);
if
(
size
<
0
)
return
-
1
;
buf
=
malloc
(
size
);
if
(
!
buf
)
{
fprintf
(
stderr
,
"%s: Out of memory (%d bytes)
\n
"
,
params
->
cmdname
,
size
);
return
-
1
;
}
ret
=
fit_build_fdt
(
params
,
buf
,
size
);
if
(
ret
<
0
)
{
fprintf
(
stderr
,
"%s: Failed to build FIT image
\n
"
,
params
->
cmdname
);
goto
err_buf
;
}
size
=
ret
;
fd
=
open
(
fname
,
O_RDWR
|
O_CREAT
|
O_TRUNC
|
O_BINARY
,
0666
);
if
(
fd
<
0
)
{
fprintf
(
stderr
,
"%s: Can't open %s: %s
\n
"
,
params
->
cmdname
,
fname
,
strerror
(
errno
));
goto
err_buf
;
}
ret
=
write
(
fd
,
buf
,
size
);
if
(
ret
!=
size
)
{
fprintf
(
stderr
,
"%s: Can't write %s: %s
\n
"
,
params
->
cmdname
,
fname
,
strerror
(
errno
));
goto
err
;
}
close
(
fd
);
free
(
buf
);
return
0
;
err:
close
(
fd
);
err_buf:
free
(
buf
);
return
-
1
;
}
/**
* fit_extract_data() - Move all data outside the FIT
*
* This takes a normal FIT file and removes all the 'data' properties from it.
* The data is placed in an area after the FIT so that it can be accessed
* using an offset into that area. The 'data' properties turn into
* 'data-offset' properties.
*
* This function cannot cope with FITs with 'data-offset' properties. All
* data must be in 'data' properties on entry.
*/
static
int
fit_extract_data
(
struct
image_tool_params
*
params
,
const
char
*
fname
)
{
void
*
buf
;
int
buf_ptr
;
int
fit_size
,
new_size
;
int
fd
;
struct
stat
sbuf
;
void
*
fdt
;
int
ret
;
int
images
;
int
node
;
fd
=
mmap_fdt
(
params
->
cmdname
,
fname
,
0
,
&
fdt
,
&
sbuf
,
false
);
if
(
fd
<
0
)
return
-
EIO
;
fit_size
=
fdt_totalsize
(
fdt
);
/* Allocate space to hold the image data we will extract */
buf
=
malloc
(
fit_size
);
if
(
!
buf
)
{
ret
=
-
ENOMEM
;
goto
err_munmap
;
}
buf_ptr
=
0
;
images
=
fdt_path_offset
(
fdt
,
FIT_IMAGES_PATH
);
if
(
images
<
0
)
{
debug
(
"%s: Cannot find /images node: %d
\n
"
,
__func__
,
images
);
ret
=
-
EINVAL
;
goto
err_munmap
;
}
for
(
node
=
fdt_first_subnode
(
fdt
,
images
);
node
>=
0
;
node
=
fdt_next_subnode
(
fdt
,
node
))
{
const
char
*
data
;
int
len
;
data
=
fdt_getprop
(
fdt
,
node
,
"data"
,
&
len
);
if
(
!
data
)
continue
;
memcpy
(
buf
+
buf_ptr
,
data
,
len
);
debug
(
"Extracting data size %x
\n
"
,
len
);
ret
=
fdt_delprop
(
fdt
,
node
,
"data"
);
if
(
ret
)
{
ret
=
-
EPERM
;
goto
err_munmap
;
}
if
(
params
->
external_offset
>
0
)
{
/* An external offset positions the data absolutely. */
fdt_setprop_u32
(
fdt
,
node
,
"data-position"
,
params
->
external_offset
+
buf_ptr
);
}
else
{
fdt_setprop_u32
(
fdt
,
node
,
"data-offset"
,
buf_ptr
);
}
fdt_setprop_u32
(
fdt
,
node
,
"data-size"
,
len
);
buf_ptr
+=
(
len
+
3
)
&
~
3
;
}
/* Pack the FDT and place the data after it */
fdt_pack
(
fdt
);
debug
(
"Size reduced from %x to %x
\n
"
,
fit_size
,
fdt_totalsize
(
fdt
));
debug
(
"External data size %x
\n
"
,
buf_ptr
);
new_size
=
fdt_totalsize
(
fdt
);
new_size
=
(
new_size
+
3
)
&
~
3
;
munmap
(
fdt
,
sbuf
.
st_size
);
if
(
ftruncate
(
fd
,
new_size
))
{
debug
(
"%s: Failed to truncate file: %s
\n
"
,
__func__
,
strerror
(
errno
));
ret
=
-
EIO
;
goto
err
;
}
/* Check if an offset for the external data was set. */
if
(
params
->
external_offset
>
0
)
{
if
(
params
->
external_offset
<
new_size
)
{
debug
(
"External offset %x overlaps FIT length %x"
,
params
->
external_offset
,
new_size
);
ret
=
-
EINVAL
;
goto
err
;
}
new_size
=
params
->
external_offset
;
}
if
(
lseek
(
fd
,
new_size
,
SEEK_SET
)
<
0
)
{
debug
(
"%s: Failed to seek to end of file: %s
\n
"
,
__func__
,
strerror
(
errno
));
ret
=
-
EIO
;
goto
err
;
}
if
(
write
(
fd
,
buf
,
buf_ptr
)
!=
buf_ptr
)
{
debug
(
"%s: Failed to write external data to file %s
\n
"
,
__func__
,
strerror
(
errno
));
ret
=
-
EIO
;
goto
err
;
}
free
(
buf
);
close
(
fd
);
return
0
;
err_munmap:
munmap
(
fdt
,
sbuf
.
st_size
);
err:
if
(
buf
)
free
(
buf
);
close
(
fd
);
return
ret
;
}
static
int
fit_import_data
(
struct
image_tool_params
*
params
,
const
char
*
fname
)
{
void
*
fdt
,
*
old_fdt
;
int
fit_size
,
new_size
,
size
,
data_base
;
int
fd
;
struct
stat
sbuf
;
int
ret
;
int
images
;
int
node
;
fd
=
mmap_fdt
(
params
->
cmdname
,
fname
,
0
,
&
old_fdt
,
&
sbuf
,
false
);
if
(
fd
<
0
)
return
-
EIO
;
fit_size
=
fdt_totalsize
(
old_fdt
);
data_base
=
(
fit_size
+
3
)
&
~
3
;
/* Allocate space to hold the new FIT */
size
=
sbuf
.
st_size
+
16384
;
fdt
=
malloc
(
size
);
if
(
!
fdt
)
{
fprintf
(
stderr
,
"%s: Failed to allocate memory (%d bytes)
\n
"
,
__func__
,
size
);
ret
=
-
ENOMEM
;
goto
err_has_fd
;
}
ret
=
fdt_open_into
(
old_fdt
,
fdt
,
size
);
if
(
ret
)
{
debug
(
"%s: Failed to expand FIT: %s
\n
"
,
__func__
,
fdt_strerror
(
errno
));
ret
=
-
EINVAL
;
goto
err_has_fd
;
}
images
=
fdt_path_offset
(
fdt
,
FIT_IMAGES_PATH
);
if
(
images
<
0
)
{
debug
(
"%s: Cannot find /images node: %d
\n
"
,
__func__
,
images
);
ret
=
-
EINVAL
;
goto
err_has_fd
;
}
for
(
node
=
fdt_first_subnode
(
fdt
,
images
);
node
>=
0
;
node
=
fdt_next_subnode
(
fdt
,
node
))
{
int
buf_ptr
;
int
len
;
buf_ptr
=
fdtdec_get_int
(
fdt
,
node
,
"data-offset"
,
-
1
);
len
=
fdtdec_get_int
(
fdt
,
node
,
"data-size"
,
-
1
);
if
(
buf_ptr
==
-
1
||
len
==
-
1
)
continue
;
debug
(
"Importing data size %x
\n
"
,
len
);
ret
=
fdt_setprop
(
fdt
,
node
,
"data"
,
fdt
+
data_base
+
buf_ptr
,
len
);
if
(
ret
)
{
debug
(
"%s: Failed to write property: %s
\n
"
,
__func__
,
fdt_strerror
(
ret
));
ret
=
-
EINVAL
;
goto
err_has_fd
;
}
}
/* Close the old fd so we can re-use it. */
close
(
fd
);
/* Pack the FDT and place the data after it */
fdt_pack
(
fdt
);
new_size
=
fdt_totalsize
(
fdt
);
debug
(
"Size expanded from %x to %x
\n
"
,
fit_size
,
new_size
);
fd
=
open
(
fname
,
O_RDWR
|
O_CREAT
|
O_TRUNC
|
O_BINARY
,
0666
);
if
(
fd
<
0
)
{
fprintf
(
stderr
,
"%s: Can't open %s: %s
\n
"
,
params
->
cmdname
,
fname
,
strerror
(
errno
));
ret
=
-
EIO
;
goto
err_no_fd
;
}
if
(
write
(
fd
,
fdt
,
new_size
)
!=
new_size
)
{
debug
(
"%s: Failed to write external data to file %s
\n
"
,
__func__
,
strerror
(
errno
));
ret
=
-
EIO
;
goto
err_has_fd
;
}
ret
=
0
;
err_has_fd:
close
(
fd
);
err_no_fd:
munmap
(
old_fdt
,
sbuf
.
st_size
);
free
(
fdt
);
return
ret
;
}
/**
* fit_handle_file - main FIT file processing function
*
* fit_handle_file() runs dtc to convert .its to .itb, includes
* binary data, updates timestamp property and calculates hashes.
*
* datafile - .its file
* imagefile - .itb file
*
* returns:
* only on success, otherwise calls exit (EXIT_FAILURE);
*/
static
int
fit_handle_file
(
struct
image_tool_params
*
params
)
{
char
tmpfile
[
MKIMAGE_MAX_TMPFILE_LEN
];
char
cmd
[
MKIMAGE_MAX_DTC_CMDLINE_LEN
];
size_t
size_inc
;
int
ret
;
/* Flattened Image Tree (FIT) format handling */
debug
(
"FIT format handling
\n
"
);
/* call dtc to include binary properties into the tmp file */
if
(
strlen
(
params
->
imagefile
)
+
strlen
(
MKIMAGE_TMPFILE_SUFFIX
)
+
1
>
sizeof
(
tmpfile
))
{
fprintf
(
stderr
,
"%s: Image file name (%s) too long, "
"can't create tmpfile"
,
params
->
imagefile
,
params
->
cmdname
);
return
(
EXIT_FAILURE
);
}
sprintf
(
tmpfile
,
"%s%s"
,
params
->
imagefile
,
MKIMAGE_TMPFILE_SUFFIX
);
/* We either compile the source file, or use the existing FIT image */
if
(
params
->
auto_its
)
{
if
(
fit_build
(
params
,
tmpfile
))
{
fprintf
(
stderr
,
"%s: failed to build FIT
\n
"
,
params
->
cmdname
);
return
EXIT_FAILURE
;
}
*
cmd
=
'\0'
;
}
else
if
(
params
->
datafile
)
{
/* dtc -I dts -O dtb -p 500 datafile > tmpfile */
snprintf
(
cmd
,
sizeof
(
cmd
),
"%s %s
\"
%s
\"
>
\"
%s
\"
"
,
MKIMAGE_DTC
,
params
->
dtc
,
params
->
datafile
,
tmpfile
);
debug
(
"Trying to execute
\"
%s
\"\n
"
,
cmd
);
}
else
{
snprintf
(
cmd
,
sizeof
(
cmd
),
"cp
\"
%s
\"
\"
%s
\"
"
,
params
->
imagefile
,
tmpfile
);
}
if
(
*
cmd
&&
system
(
cmd
)
==
-
1
)
{
fprintf
(
stderr
,
"%s: system(%s) failed: %s
\n
"
,
params
->
cmdname
,
cmd
,
strerror
(
errno
));
goto
err_system
;
}
/* Move the data so it is internal to the FIT, if needed */
ret
=
fit_import_data
(
params
,
tmpfile
);
if
(
ret
)
goto
err_system
;
/*
* Set hashes for images in the blob. Unfortunately we may need more
* space in either FDT, so keep trying until we succeed.
*
* Note: this is pretty inefficient for signing, since we must
* calculate the signature every time. It would be better to calculate
* all the data and then store it in a separate step. However, this
* would be considerably more complex to implement. Generally a few
* steps of this loop is enough to sign with several keys.
*/
for
(
size_inc
=
0
;
size_inc
<
64
*
1024
;
size_inc
+=
1024
)
{
ret
=
fit_add_file_data
(
params
,
size_inc
,
tmpfile
);
if
(
!
ret
||
ret
!=
-
ENOSPC
)
break
;
}
if
(
ret
)
{
fprintf
(
stderr
,
"%s Can't add hashes to FIT blob: %d
\n
"
,
params
->
cmdname
,
ret
);
goto
err_system
;
}
/* Move the data so it is external to the FIT, if requested */
if
(
params
->
external_data
)
{
ret
=
fit_extract_data
(
params
,
tmpfile
);
if
(
ret
)
goto
err_system
;
}
if
(
rename
(
tmpfile
,
params
->
imagefile
)
==
-
1
)
{
fprintf
(
stderr
,
"%s: Can't rename %s to %s: %s
\n
"
,
params
->
cmdname
,
tmpfile
,
params
->
imagefile
,
strerror
(
errno
));
unlink
(
tmpfile
);
unlink
(
params
->
imagefile
);
return
EXIT_FAILURE
;
}
return
EXIT_SUCCESS
;
err_system:
unlink
(
tmpfile
);
return
-
1
;
}
/**
* fit_image_extract - extract a FIT component image
* @fit: pointer to the FIT format image header
* @image_noffset: offset of the component image node
* @file_name: name of the file to store the FIT sub-image
*
* returns:
* zero in case of success or a negative value if fail.
*/
static
int
fit_image_extract
(
const
void
*
fit
,
int
image_noffset
,
const
char
*
file_name
)
{
const
void
*
file_data
;
size_t
file_size
=
0
;
/* get the "data" property of component at offset "image_noffset" */
fit_image_get_data
(
fit
,
image_noffset
,
&
file_data
,
&
file_size
);
/* save the "file_data" into the file specified by "file_name" */
return
imagetool_save_subimage
(
file_name
,
(
ulong
)
file_data
,
file_size
);
}
/**
* fit_extract_contents - retrieve a sub-image component from the FIT image
* @ptr: pointer to the FIT format image header
* @params: command line parameters
*
* returns:
* zero in case of success or a negative value if fail.
*/
static
int
fit_extract_contents
(
void
*
ptr
,
struct
image_tool_params
*
params
)
{
int
images_noffset
;
int
noffset
;
int
ndepth
;
const
void
*
fit
=
ptr
;
int
count
=
0
;
const
char
*
p
;
/* Indent string is defined in header image.h */
p
=
IMAGE_INDENT_STRING
;
if
(
!
fit_check_format
(
fit
))
{
printf
(
"Bad FIT image format
\n
"
);
return
-
1
;
}
/* Find images parent node offset */
images_noffset
=
fdt_path_offset
(
fit
,
FIT_IMAGES_PATH
);
if
(
images_noffset
<
0
)
{
printf
(
"Can't find images parent node '%s' (%s)
\n
"
,
FIT_IMAGES_PATH
,
fdt_strerror
(
images_noffset
));
return
-
1
;
}
/* Avoid any overrun */
count
=
fit_get_subimage_count
(
fit
,
images_noffset
);
if
((
params
->
pflag
<
0
)
||
(
count
<=
params
->
pflag
))
{
printf
(
"No such component at '%d'
\n
"
,
params
->
pflag
);
return
-
1
;
}
/* Process its subnodes, extract the desired component from image */
for
(
ndepth
=
0
,
count
=
0
,
noffset
=
fdt_next_node
(
fit
,
images_noffset
,
&
ndepth
);
(
noffset
>=
0
)
&&
(
ndepth
>
0
);
noffset
=
fdt_next_node
(
fit
,
noffset
,
&
ndepth
))
{
if
(
ndepth
==
1
)
{
/*
* Direct child node of the images parent node,
* i.e. component image node.
*/
if
(
params
->
pflag
==
count
)
{
printf
(
"Extracted:
\n
%s Image %u (%s)
\n
"
,
p
,
count
,
fit_get_name
(
fit
,
noffset
,
NULL
));
fit_image_print
(
fit
,
noffset
,
p
);
return
fit_image_extract
(
fit
,
noffset
,
params
->
outfile
);
}
count
++
;
}
}
return
0
;
}
static
int
fit_check_params
(
struct
image_tool_params
*
params
)
{
if
(
params
->
auto_its
)
return
0
;
return
((
params
->
dflag
&&
(
params
->
fflag
||
params
->
lflag
))
||
(
params
->
fflag
&&
(
params
->
dflag
||
params
->
lflag
))
||
(
params
->
lflag
&&
(
params
->
dflag
||
params
->
fflag
)));
}
U_BOOT_IMAGE_TYPE
(
fitimage
,
"FIT Image support"
,
sizeof
(
image_header_t
),
(
void
*
)
&
header
,
fit_check_params
,
fit_verify_header
,
fit_print_contents
,
NULL
,
fit_extract_contents
,
fit_check_image_types
,
fit_handle_file
,
NULL
/* FIT images use DTB header */
);
u-boot-tree/tools/fit_info.c
deleted
100644 → 0
View file @
11a59314
/*
* (C) Copyright 2014
* DENX Software Engineering
* Heiko Schocher <hs@denx.de>
*
* fit_info: print the offset and the len of a property from
* node in a fit file.
*
* Based on:
* (C) Copyright 2008 Semihalf
*
* (C) Copyright 2000-2004
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* Updated-by: Prafulla Wadaskar <prafulla@marvell.com>
* FIT image specific code abstracted from mkimage.c
* some functions added to address abstraction
*
* All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include "mkimage.h"
#include "fit_common.h"
#include <image.h>
#include <u-boot/crc.h>
void
usage
(
char
*
cmdname
)
{
fprintf
(
stderr
,
"Usage: %s -f fit file -n node -p property
\n
"
" -f ==> set fit file which is used'
\n
"
" -n ==> set node name'
\n
"
" -p ==> set property name'
\n
"
,
cmdname
);
exit
(
EXIT_FAILURE
);
}
int
main
(
int
argc
,
char
**
argv
)
{
int
ffd
=
-
1
;
struct
stat
fsbuf
;
void
*
fit_blob
;
int
len
;
int
nodeoffset
;
/* node offset from libfdt */
const
void
*
nodep
;
/* property node pointer */
char
*
fdtfile
=
NULL
;
char
*
nodename
=
NULL
;
char
*
propertyname
=
NULL
;
char
cmdname
[
256
];
int
c
;
strncpy
(
cmdname
,
*
argv
,
sizeof
(
cmdname
)
-
1
);
cmdname
[
sizeof
(
cmdname
)
-
1
]
=
'\0'
;
while
((
c
=
getopt
(
argc
,
argv
,
"f:n:p:"
))
!=
-
1
)
switch
(
c
)
{
case
'f'
:
fdtfile
=
optarg
;
break
;
case
'n'
:
nodename
=
optarg
;
break
;
case
'p'
:
propertyname
=
optarg
;
break
;
default:
usage
(
cmdname
);
break
;
}
if
(
!
fdtfile
)
{
fprintf
(
stderr
,
"%s: Missing fdt file
\n
"
,
*
argv
);
usage
(
*
argv
);
}
if
(
!
nodename
)
{
fprintf
(
stderr
,
"%s: Missing node name
\n
"
,
*
argv
);
usage
(
*
argv
);
}
if
(
!
propertyname
)
{
fprintf
(
stderr
,
"%s: Missing property name
\n
"
,
*
argv
);
usage
(
*
argv
);
}
ffd
=
mmap_fdt
(
cmdname
,
fdtfile
,
0
,
&
fit_blob
,
&
fsbuf
,
false
);
if
(
ffd
<
0
)
{
printf
(
"Could not open %s
\n
"
,
fdtfile
);
exit
(
EXIT_FAILURE
);
}
nodeoffset
=
fdt_path_offset
(
fit_blob
,
nodename
);
if
(
nodeoffset
<
0
)
{
printf
(
"%s not found."
,
nodename
);
exit
(
EXIT_FAILURE
);
}
nodep
=
fdt_getprop
(
fit_blob
,
nodeoffset
,
propertyname
,
&
len
);
if
(
len
==
0
)
{
printf
(
"len == 0 %s
\n
"
,
propertyname
);
exit
(
EXIT_FAILURE
);
}
printf
(
"NAME: %s
\n
"
,
fit_get_name
(
fit_blob
,
nodeoffset
,
NULL
));
printf
(
"LEN: %d
\n
"
,
len
);
printf
(
"OFF: %d
\n
"
,
(
int
)(
nodep
-
fit_blob
));
(
void
)
munmap
((
void
*
)
fit_blob
,
fsbuf
.
st_size
);
close
(
ffd
);
exit
(
EXIT_SUCCESS
);
}
u-boot-tree/tools/imagetool.h
deleted
100644 → 0
View file @
11a59314
/*
* (C) Copyright 2013
*
* Written by Guilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _IMAGETOOL_H_
#define _IMAGETOOL_H_
#include "os_support.h"
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <u-boot/sha1.h>
#include "fdt_host.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define IH_ARCH_DEFAULT IH_ARCH_INVALID
/* Information about a file that needs to be placed into the FIT */
struct
content_info
{
struct
content_info
*
next
;
int
type
;
/* File type (IH_TYPE_...) */
const
char
*
fname
;
};
/*
* This structure defines all such variables those are initialized by
* mkimage and dumpimage main core and need to be referred by image
* type specific functions
*/
struct
image_tool_params
{
int
dflag
;
int
eflag
;
int
fflag
;
int
iflag
;
int
lflag
;
int
pflag
;
int
vflag
;
int
xflag
;
int
skipcpy
;
int
os
;
int
arch
;
int
type
;
int
comp
;
char
*
dtc
;
unsigned
int
addr
;
unsigned
int
ep
;
char
*
imagename
;
char
*
imagename2
;
char
*
datafile
;
char
*
imagefile
;
char
*
cmdname
;
const
char
*
outfile
;
/* Output filename */
const
char
*
keydir
;
/* Directory holding private keys */
const
char
*
keydest
;
/* Destination .dtb for public key */
const
char
*
comment
;
/* Comment to add to signature node */
int
require_keys
;
/* 1 to mark signing keys as 'required' */
int
file_size
;
/* Total size of output file */
int
orig_file_size
;
/* Original size for file before padding */
bool
auto_its
;
/* Automatically create the .its file */
int
fit_image_type
;
/* Image type to put into the FIT */
char
*
fit_ramdisk
;
/* Ramdisk file to include */
struct
content_info
*
content_head
;
/* List of files to include */
struct
content_info
*
content_tail
;
bool
external_data
;
/* Store data outside the FIT */
bool
quiet
;
/* Don't output text in normal operation */
unsigned
int
external_offset
;
/* Add padding to external data */
const
char
*
engine_id
;
/* Engine to use for signing */
};
/*
* image type specific variables and callback functions
*/
struct
image_type_params
{
/* name is an identification tag string for added support */
char
*
name
;
/*
* header size is local to the specific image type to be supported,
* mkimage core treats this as number of bytes
*/
uint32_t
header_size
;
/* Image type header pointer */
void
*
hdr
;
/*
* There are several arguments that are passed on the command line
* and are registered as flags in image_tool_params structure.
* This callback function can be used to check the passed arguments
* are in-lined with the image type to be supported
*
* Returns 1 if parameter check is successful
*/
int
(
*
check_params
)
(
struct
image_tool_params
*
);
/*
* This function is used by list command (i.e. mkimage -l <filename>)
* image type verification code must be put here
*
* Returns 0 if image header verification is successful
* otherwise, returns respective negative error codes
*/
int
(
*
verify_header
)
(
unsigned
char
*
,
int
,
struct
image_tool_params
*
);
/* Prints image information abstracting from image header */
void
(
*
print_header
)
(
const
void
*
);
/*
* The header or image contents need to be set as per image type to
* be generated using this callback function.
* further output file post processing (for ex. checksum calculation,
* padding bytes etc..) can also be done in this callback function.
*/
void
(
*
set_header
)
(
void
*
,
struct
stat
*
,
int
,
struct
image_tool_params
*
);
/*
* This function is used by the command to retrieve a component
* (sub-image) from the image (i.e. dumpimage -i <image> -p <position>
* <sub-image-name>).
* Thus the code to extract a file from an image must be put here.
*
* Returns 0 if the file was successfully retrieved from the image,
* or a negative value on error.
*/
int
(
*
extract_subimage
)(
void
*
,
struct
image_tool_params
*
);
/*
* Some image generation support for ex (default image type) supports
* more than one type_ids, this callback function is used to check
* whether input (-T <image_type>) is supported by registered image
* generation/list low level code
*/
int
(
*
check_image_type
)
(
uint8_t
);
/* This callback function will be executed if fflag is defined */
int
(
*
fflag_handle
)
(
struct
image_tool_params
*
);
/*
* This callback function will be executed for variable size record
* It is expected to build this header in memory and return its length
* and a pointer to it by using image_type_params.header_size and
* image_type_params.hdr. The return value shall indicate if an
* additional padding should be used when copying the data image
* by returning the padding length.
*/
int
(
*
vrec_header
)
(
struct
image_tool_params
*
,
struct
image_type_params
*
);
};
/**
* imagetool_get_type() - find the image type params for a given image type
*
* It scans all registers image type supports
* checks the input type for each supported image type
*
* if successful,
* returns respective image_type_params pointer if success
* if input type_id is not supported by any of image_type_support
* returns NULL
*/
struct
image_type_params
*
imagetool_get_type
(
int
type
);
/*
* imagetool_verify_print_header() - verifies the image header
*
* Scan registered image types and verify the image_header for each
* supported image type. If verification is successful, this prints
* the respective header.
*
* @return 0 on success, negative if input image format does not match with
* any of supported image types
*/
int
imagetool_verify_print_header
(
void
*
ptr
,
struct
stat
*
sbuf
,
struct
image_type_params
*
tparams
,
struct
image_tool_params
*
params
);
/**
* imagetool_save_subimage - store data into a file
* @file_name: name of the destination file
* @file_data: data to be written
* @file_len: the amount of data to store
*
* imagetool_save_subimage() store file_len bytes of data pointed by file_data
* into the file name by file_name.
*
* returns:
* zero in case of success or a negative value if fail.
*/
int
imagetool_save_subimage
(
const
char
*
file_name
,
ulong
file_data
,
ulong
file_len
);
/**
* imagetool_get_filesize() - Utility function to obtain the size of a file
*
* This function prints a message if an error occurs, showing the error that
* was obtained.
*
* @params: mkimage parameters
* @fname: filename to check
* @return size of file, or -ve value on error
*/
int
imagetool_get_filesize
(
struct
image_tool_params
*
params
,
const
char
*
fname
);
/**
* imagetool_get_source_date() - Get timestamp for build output.
*
* Gets a timestamp for embedding it in a build output. If set
* SOURCE_DATE_EPOCH is used. Else the given fallback value is returned. Prints
* an error message if SOURCE_DATE_EPOCH contains an invalid value and returns
* 0.
*
* @params: mkimage parameters
* @fallback: timestamp to use if SOURCE_DATE_EPOCH isn't set
* @return timestamp based on SOURCE_DATE_EPOCH
*/
time_t
imagetool_get_source_date
(
struct
image_tool_params
*
params
,
time_t
fallback
);
/*
* There is a c file associated with supported image type low level code
* for ex. default_image.c, fit_image.c
*/
void
pbl_load_uboot
(
int
fd
,
struct
image_tool_params
*
mparams
);
#define ___cat(a, b) a ## b
#define __cat(a, b) ___cat(a, b)
/* we need some special handling for this host tool running eventually on
* Darwin. The Mach-O section handling is a bit different than ELF section
* handling. The differnces in detail are:
* a) we have segments which have sections
* b) we need a API call to get the respective section symbols */
#if defined(__MACH__)
#include <mach-o/getsect.h>
#define INIT_SECTION(name) do { \
unsigned long name ## _len; \
char *__cat(pstart_, name) = getsectdata("__TEXT", \
#name, &__cat(name, _len)); \
char *__cat(pstop_, name) = __cat(pstart_, name) + \
__cat(name, _len); \
__cat(__start_, name) = (void *)__cat(pstart_, name); \
__cat(__stop_, name) = (void *)__cat(pstop_, name); \
} while (0)
#define SECTION(name) __attribute__((section("__TEXT, " #name)))
struct
image_type_params
**
__start_image_type
,
**
__stop_image_type
;
#else
#define INIT_SECTION(name)
/* no-op for ELF */
#define SECTION(name) __attribute__((section(#name)))
/* We construct a table of pointers in an ELF section (pointers generally
* go unpadded by gcc). ld creates boundary syms for us. */
extern
struct
image_type_params
*
__start_image_type
[],
*
__stop_image_type
[];
#endif
/* __MACH__ */
#if !defined(__used)
# if __GNUC__ == 3 && __GNUC_MINOR__ < 3
# define __used __attribute__((__unused__))
# else
# define __used __attribute__((__used__))
# endif
#endif
#define U_BOOT_IMAGE_TYPE( \
_id, \
_name, \
_header_size, \
_header, \
_check_params, \
_verify_header, \
_print_header, \
_set_header, \
_extract_subimage, \
_check_image_type, \
_fflag_handle, \
_vrec_header \
) \
static struct image_type_params __cat(image_type_, _id) = \
{ \
.name = _name, \
.header_size = _header_size, \
.hdr = _header, \
.check_params = _check_params, \
.verify_header = _verify_header, \
.print_header = _print_header, \
.set_header = _set_header, \
.extract_subimage = _extract_subimage, \
.check_image_type = _check_image_type, \
.fflag_handle = _fflag_handle, \
.vrec_header = _vrec_header \
}; \
static struct image_type_params *SECTION(image_type) __used \
__cat(image_type_ptr_, _id) = &__cat(image_type_, _id)
#endif
/* _IMAGETOOL_H_ */
u-boot-tree/tools/mkimage.h
deleted
100644 → 0
View file @
11a59314
/*
* (C) Copyright 2000-2004
* DENX Software Engineering
* Wolfgang Denk, wd@denx.de
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _MKIIMAGE_H_
#define _MKIIMAGE_H_
#include "os_support.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <u-boot/sha1.h>
#include "fdt_host.h"
#include "imagetool.h"
#undef MKIMAGE_DEBUG
#ifdef MKIMAGE_DEBUG
#define debug(fmt,args...) printf (fmt ,##args)
#else
#define debug(fmt,args...)
#endif
/* MKIMAGE_DEBUG */
static
inline
void
*
map_sysmem
(
ulong
paddr
,
unsigned
long
len
)
{
return
(
void
*
)(
uintptr_t
)
paddr
;
}
static
inline
ulong
map_to_sysmem
(
void
*
ptr
)
{
return
(
ulong
)(
uintptr_t
)
ptr
;
}
#define MKIMAGE_TMPFILE_SUFFIX ".tmp"
#define MKIMAGE_MAX_TMPFILE_LEN 256
#define MKIMAGE_DEFAULT_DTC_OPTIONS "-I dts -O dtb -p 500"
#define MKIMAGE_MAX_DTC_CMDLINE_LEN 512
#endif
/* _MKIIMAGE_H_ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment