blob: ca165f05f10e5268a0668814529b30a705530e70 [file] [log] [blame]
Simon Glass02741682013-05-26 07:07:58 -07001#!/usr/bin/python
2
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""crosfw - Chrome OS Firmware build/flash script.
8
9Builds a firmware image for any board and writes it to the board. The image
10can be pure upstream or include Chrome OS components (-V). Some device
11tree parameters can be provided, including silent console (-C) and secure
12boot (-S). Use -i for a faster incremental build. The image is written to
13the board by default using USB/em100 (or sdcard with -x). Use -b to specify
14the board to build. Options can be added to ~/.crosfwrc - see the script for
15details.
16
17It can also flash SPI by writing a 'magic flasher' U-Boot with a payload
18to the board.
19
20Usage: crosfw [options]
21
22The script is normally run from within the U-Boot directory which is
23.../src/third_party/u-boot/files
24
25Example 1: Build upstream image for coreboot and write to a 'link':
26
27 crosfw -b link
28
29Example 2: Build verified boot image (V) for daisy/snow and boot in secure
30 mode (S) so that breaking in on boot is not possible.
31
32 crosfw -b daisy -VS
33 crosfw -b daisy -VSC (no console output)
34
35Example 3: Build a magic flasher (F) with full verified boot for peach_pit,
36 but with console enabled, write to SD card (x)
37
38 crosfw -b peach_pit -VSFx
39
40This sript does not use an ebuild. It does a similar thing to the
41chromeos-u-boot ebuild, and runs cros_bundle_firmware to produce various
42types of image, a little like the chromeos-bootimage ebuild.
43
44The purpose of this script is to make it easier and faster to perform
45common firmware build tasks without changing boards, manually updating
46device tree files or lots of USE flags and complexity in the ebuilds.
47
48This script has been tested with snow, link and peach_pit. It builds for
49peach_pit by default. Note that it will also build any upstream ARM
50board - e.g. "-b snapper9260" will build an image for that board.
51
52Mostly you can use the script inside and outside the chroot. The main
53limitation is that dut-control doesn't really work outside the chroot,
54so writing the image to the board over USB is not possible, nor can the
55board be automatically reset on x86 platforms.
56
57For an incremental build (faster), run with -i
58
59To get faster clean builds, install ccache, and create ~/.crosfwrc with
60this line:
61
62 use_ccache = True
63
64(make sure ~/.ccache is not on NFS, or set CCACHE_DIR)
65
66Other options are the default board to build, and verbosity (0-4), e.g.:
67
68 default_board = 'daisy'
69 verbose = 1
70
71It is possible to use multiple servo boards, each on its own port. Add
72these lines to your ~/.crosfwrc to set the servo port to use for each
73board:
74
75 SERVO_PORT['link'] = 8888
76 SERVO_PORT['daisy'] = 9999
77 SERVO_PORT['peach_pit'] = 7777
78
79All builds appear in the b/<board> subdirectory and images are written
80to b/<uboard>/out, where <uboard> is the U-Boot name for the board (in the
81U-Boot boards.cfg file)
82
83For the -a option here are some useful options:
84
85--add-blob cros-splash /dev/null
86--gbb-flags -force-dev-switch-on
87--add-node-enable /spi@131b0000/cros-ecp@0 1
88--verify --full-erase
89--bootcmd "cros_test sha"
90--gbb-flags -force-dev-switch-on
91--bmpblk ~/trunk/src/third_party/u-boot/bmp.bin
92
93For example: -a "--gbb-flags -force-dev-switch-on"
94
95Note the standard bmpblk is at:
96 /home/$USER/trunk/src/third_party/chromiumos-overlay/sys-boot/
97 chromeos-bootimage/files/bmpblk.bin"
98"""
99
100import glob
101import logging
102import multiprocessing
103import os
104import re
105import sys
106
107from chromite.buildbot import constants
108from chromite.lib import commandline
109from chromite.lib import cros_build_lib
110from chromite.lib import osutils
111from chromite.lib import parallel
112
113
114arch = None
115board = None
116compiler = None
117default_board = None
118family = None
119in_chroot = True
120
121logger = logging.getLogger('chromite')
122logging.basicConfig(format='%(message)s')
123kwargs = {'print_cmd': False, 'error_code_ok': True,
124 'debug_level': logger.getEffectiveLevel()}
125
126outdir = ''
127
128 # If you have multiple boards connected on different servo ports, put lines
129# like 'SERVO_PORT{"peach_pit"} = 7777' in your ~/.crosfwrc
130SERVO_PORT = {}
131
132smdk = None
133src_root = os.path.join(constants.SOURCE_ROOT, 'src')
134in_chroot = cros_build_lib.IsInsideChroot()
135
136uboard = ''
137
138default_board = 'peach_pit'
139use_ccache = False
140vendor = None
141verbose = False
142
143# Special cases for the U-Boot board config, the SOCs and default device tree
144# since the naming is not always consistent.
145# x86 has a lot of boards, but to U-Boot they are all the same
146UBOARDS = {
147 'daisy': 'smdk5250',
148 'peach': 'smdk5420',
149}
150for b in ['alex', 'butterfly', 'emeraldlake2', 'link', 'lumpy', 'parrot',
151 'stout', 'stumpy']:
152 UBOARDS[b] = 'coreboot-x86'
153 UBOARDS['chromeos_%s' % b] = 'chromeos_coreboot'
154
155SOCS = {
156 'coreboot-x86': '',
157 'chromeos_coreboot': '',
158 'daisy': 'exynos5250-',
159 'peach': 'exynos5420-',
160}
161
162DEFAULT_DTS = {
163 'daisy': 'snow',
164 'daisy_spring': 'spring',
165 'peach_pit': 'peach-pit',
166}
167
168rc_file = os.path.expanduser('~/.crosfwrc')
169if os.path.exists(rc_file):
170 execfile(rc_file)
171
172
173def Log(msg):
174 """Print out a message if we are in verbose mode.
175
176 Args:
177 msg: Message to print
178 """
179 if verbose:
180 cros_build_lib.Info(msg)
181
182
183def Dumper(flag, infile, outfile):
184 """Run objdump on an input file.
185
186 Args:
187 flag: Flag to pass objdump (e.g. '-d').
188 infile: Input file to process.
189 outfile: Output file to write to.
190 """
191 result = cros_build_lib.RunCommand(
192 [CompilerTool('objdump'), flag, infile],
193 log_stdout_to_file=outfile, **kwargs)
194 if result.returncode:
195 sys.exit()
196
197
198def CompilerTool(tool):
199 """Returns the cross-compiler tool filename.
200
201 Args:
202 tool: Tool name to return, e.g. 'size'.
203
204 Returns:
205 Filename of requested tool.
206 """
207 return '%s%s' % (compiler, tool)
208
209
210def ParseCmdline(argv):
211 """Parse all command line options.
212
213 Args:
214 argv: Arguments to parse.
215
216 Returns:
217 Tuple containing:
218 options: Command line options from optpase
219 args: List of command line arguments
220 """
221 parser = commandline.OptionParser(__doc__)
222 parser.add_option('-a', '--cbfargs', action='append',
223 help='Pass extra arguments to cros_bundle_firmware')
224 parser.add_option('-b', '--board', type='string', default=default_board,
225 help='Select board to build (daisy/peach_pit/link)')
226 parser.add_option('-B', '--build', action='store_false', default=True,
227 help="Don't build U-Boot, just configure device tree")
228 parser.add_option('-C', '--console', action='store_false', default=True,
229 help='Permit console output')
230 parser.add_option('-d', '--dt', default='seaboard',
231 help='Select name of device tree file to use')
232 parser.add_option('-D', '--nodefaults', dest='use_defaults',
233 action='store_false', default=True,
234 help="Don't select default filenames for those not given")
235 parser.add_option('-F', '--flash', action='store_true', default=False,
236 help='Create magic flasher')
237 parser.add_option('-i', '--incremental', action='store_true', default=False,
238 help="Don't reconfigure and clean")
239 parser.add_option('-k', '--kernel', action='store_true', default=False,
240 help='Send kernel to board also')
241 parser.add_option('-O', '--objdump', action='store_true', default=False,
242 help='Write disassembly output')
243 parser.add_option('-r', '--run', action='store_false', default=True,
244 help='Run the boot command')
245 parser.add_option('--ro', action='store_true', default=False,
246 help='Create Chrome OS read-only image')
247 parser.add_option('--rw', action='store_true', default=False,
248 help='Create Chrome OS read-write image')
249 parser.add_option('-s', '--separate', action='store_false', default=True,
250 help='Link device tree into U-Boot, instead of separate')
251 parser.add_option('-S', '--secure', action='store_true', default=False,
252 help='Use vboot_twostop secure boot')
253 parser.add_option('--small', action='store_true', default=False,
254 help='Create Chrome OS small image')
255 parser.add_option('-t', '--trace', action='store_true', default=False,
256 help='Enable trace support')
257 parser.add_option('-v', '--verbose', type='int', default=0,
258 help='Make cros_bundle_firmware verbose')
259 parser.add_option('-V', '--verified', action='store_true', default=False,
260 help='Include Chrome OS verified boot components')
261 parser.add_option('-w', '--write', action='store_false', default=True,
262 help="Don't write image to board using usb/em100")
263 parser.add_option('-x', '--sdcard', action='store_true', default=False,
264 help='Write to SD card instead of USB/em100')
265 parser.add_option('-z', '--size', action='store_true', default=False,
266 help='Display U-Boot image size')
267 return parser.parse_args(argv)
268
269
270def SetupBuild(options):
271 """Set up parameters needed for the build.
272
273 This checks the current environment and options and sets up various things
274 needed for the build, including 'base' which holds the base flags for
275 passing to the U-Boot Makefile.
276
277 Args:
278 options: Command line options
279
280 Returns:
281 Base flags to use for U-Boot, as a list.
282 """
283 global arch, board, compiler, family, outdir, smdk, uboard, vendor, verbose
284
285 if not verbose:
286 verbose = options.verbose != 0
287
288 logger.setLevel(options.verbose)
289
290 Log('Building for %s' % options.board)
291
292 # Separate out board_variant string: "peach_pit" becomes "peach", "pit"
293 parts = options.board.split('_')
294 board = parts[0]
295
296 # To allow this to be run from 'cros_sdk'
297 if in_chroot:
298 os.chdir(os.path.join(src_root, 'third_party', 'u-boot', 'files'))
299
300 base_board = board
301
302 if options.verified:
303 base_board = 'chromeos_%s' % base_board
304
305 uboard = UBOARDS.get(base_board, base_board)
306 Log('U-Boot board is %s' % uboard)
307
308 # Pull out some information from the U-Boot boards config file
309 family = None
310 with open('boards.cfg') as f:
311 for line in f:
312 if uboard in line:
313 if line[0] == '#':
314 continue
315 fields = line.split()
316 if not fields:
317 continue
318 arch = fields[1]
319 fields += [None, None, None]
320 smdk = fields[3]
321 vendor = fields[4]
322 family = fields[5]
323 break
324 if not arch:
325 cros_build_lib.Die("Selected board '%s' not found in boards.cfg." % board)
326
327 vboot = os.path.join('build', board, 'usr')
328 if arch == 'x86':
329 family = 'em100'
330 if in_chroot:
331 compiler = 'i686-pc-linux-gnu-'
332 else:
333 compiler = '/opt/i686/bin/i686-unknown-elf-'
334 elif arch == 'arm':
335 if in_chroot:
336 # Use the Chrome OS toolchain
337 compiler = 'armv7a-cros-linux-gnueabi-'
338 else:
339 compiler = glob.glob('/opt/linaro/gcc-linaro-arm-linux-*/bin/*gcc')
340 if not compiler:
341 cros_build_lib.Die("""Please install an ARM toolchain for your machine.
342'Install a Linaro toolchain from:'
343'https://launchpad.net/linaro-toolchain-binaries'
344'or see cros/commands/cros_chrome_sdk.py.""")
345 compiler = compiler[0]
346 compiler = re.sub('gcc$', '', compiler)
347 elif arch == 'sandbox':
348 compiler = ''
349 else:
350 cros_build_lib.Die("Selected arch '%s' not supported." % arch)
351
352 if not options.build:
353 options.incremental = True
354
355 cpus = multiprocessing.cpu_count()
356
357 outdir = 'b/%s' % uboard
358 base = [
359 'make',
360 '-j%d' % cpus,
361 'O=%s' % outdir,
362 'ARCH=%s' % arch,
363 'CROSS_COMPILE=%s' % compiler,
364 '--no-print-directory',
365 'HOSTSTRIP=true',
366 'DEV_TREE_SRC=%s-%s' % (family, options.dt),
367 'QEMU_ARCH=']
368
369 if options.verbose < 2:
370 base.append('-s')
371
372 if options.ro and options.rw:
373 cros_build_lib.Die('Cannot specify both --ro and --rw options')
374 if options.ro:
375 base.append('CROS_RO=1')
376 options.small = True
377
378 if options.rw:
379 base.append('CROS_RW=1')
380 options.small = True
381
382 if options.small:
383 base.append('CROS_SMALL=1')
384 else:
385 base.append('CROS_FULL=1')
386
387 if options.verified:
388 base += [
389 'VBOOT=%s' % vboot,
390 'MAKEFLAGS_VBOOT=DEBUG=1',
391 'QUIET=1',
392 'CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS',
393 'VBOOT_SOURCE=%s/platform/vboot_reference' % src_root]
394 if not options.small:
395 base.append('VBOOT_DEBUG=1')
396
397 # Handle the Chrome OS USE_STDINT workaround. Vboot needs <stdint.h> due
398 # to a recent change, the need for which I didn't fully understand. But
399 # U-Boot doesn't normally use this. We have added an option to U-Boot to
400 # enable use of <stdint.h> and without it vboot will fail to build. So we
401 # need to enable it where ww can. We can't just enable it always since
402 # that would prevent this script from building other non-Chrome OS boards
403 # with a different (older) toolchain, or Chrome OS boards without vboot.
404 # So use USE_STDINT if the toolchain supports it, and not if not. This
405 # file was originally part of glibc but has recently migrated to the
406 # compiler so it is reasonable to use it with a stand-alone program like
407 # U-Boot. At this point the comment has got long enough that we may as
408 # well include some poetry which seems to be sorely lacking the code base,
409 # so this is from Ogden Nash:
410 # To keep your marriage brimming
411 # With love in the loving cup,
412 # Whenever you're wrong, admit it;
413 # Whenever you're right, shut up.
414 cmd = [CompilerTool('gcc'), '-ffreestanding', '-x', 'c', '-c', '-']
415 result = cros_build_lib.RunCommandCaptureOutput(cmd,
416 input='#include <stdint.h>',
417 **kwargs)
418 if result.returncode == 0:
419 base.append('USE_STDINT=1')
420
421 if options.trace:
422 base.append('FTRACE=1')
423 if options.separate:
424 base.append('DEV_TREE_SEPARATE=1')
425
426 if options.incremental:
427 # Get the correct board for cros_write_firmware
428 config_mk = '%s/include/config.mk' % outdir
429 if not os.path.exists(config_mk):
430 cros_build_lib.Warning('No build found for %s - dropping -i' % board)
431 options.incremental = False
432
433 config_mk = 'include/config.mk'
434 if os.path.exists(config_mk):
435 cros_build_lib.Warning("Warning: '%s' exists, try 'make distclean'"
436 % config_mk)
437
438 # For when U-Boot supports ccache
439 # See http://patchwork.ozlabs.org/patch/245079/
440 if use_ccache:
441 os.environ['CCACHE'] = 'ccache'
442
443 return base
444
445
446def RunBuild(options, base, target, queue):
447 """Run the U-Boot build.
448
449 Args:
450 options: Command line options.
451 base: Base U-Boot flags.
452 target: Target to build.
453 queue: A parallel queue to add jobs to.
454 """
455 Log('U-Boot build flags: %s' % ' '.join(base))
456
457 # Reconfigure U-Boot.
458 if not options.incremental:
459 # Ignore any error from this, some older U-Boots fail on this.
460 cros_build_lib.RunCommand(base + ['distclean'], **kwargs)
461 result = cros_build_lib.RunCommand(base + ['%s_config' % uboard], **kwargs)
462 if result.returncode:
463 sys.exit()
464
465 # Do the actual build.
466 if options.build:
467 result = cros_build_lib.RunCommand(base + [target], **kwargs)
468 if result.returncode:
469 sys.exit()
470
471 files = ['%s/u-boot' % outdir]
472 spl = glob.glob('%s/spl/u-boot-spl' % outdir)
473 if spl:
474 files += spl
475 if options.size:
476 cros_build_lib.RunCommand([CompilerTool('size')] + files, **kwargs)
477 if result.returncode:
478 sys.exit()
479
480 # Create disassembly files .dis and .Dis (full dump)
481 for f in files:
482 base = os.path.splitext(f)[0]
483 if options.objdump:
484 queue.put(('-d', f, base + '.dis'))
485 queue.put(('-D', f, base + '.Dis'))
486 else:
487 # Remove old files which otherwise might be confusing
488 osutils.SafeUnlink(base + '.dis')
489 osutils.SafeUnlink(base + '.Dis')
490
491 Log('Output directory %s' % outdir)
492
493
494def WriteFirmware(options):
495 """Write firmware to the board.
496
497 This uses cros_bundle_firmware to create a firmware image and write it to
498 the board.
499
500 Args:
501 options: Command line options
502 """
503 flash = []
504 kernel = []
505 run = []
506 secure = []
507 servo = []
508 silent = []
509 verbose_arg = []
510
511 bl2 = ['--bl2', '%s/spl/%s-spl.bin' % (outdir, smdk)]
512
513 if options.use_defaults:
514 bl1 = []
515 bmpblk = []
516 ecro = []
517 ecrw = []
518 defaults = []
519 else:
520 bl1 = ['--bl1', '##/build/%s/firmware/u-boot.bl1.bin' % options.board]
521 bmpblk = ['--bmpblk', '##/build/%s/firmware/bmpblk.bin' % options.board]
522 ecro = ['--ecro', '##/build/%s/firmware/ec.RO.bin' % options.board]
523 ecrw = ['--ec', '##/build/%s/firmware/ec.RW.bin' % options.board]
524 defaults = ['-D']
525
526 if arch == 'x86':
527 seabios = ['--seabios',
528 '##/build/%s/firmware/seabios.cbfs' % options.board]
529 else:
530 seabios = []
531
532 if options.sdcard:
533 dest = 'sd:.'
534 elif arch == 'x86':
535 dest = 'em100'
536 elif arch == 'sandbox':
537 dest = ''
538 else:
539 dest = 'usb'
540
541 port = SERVO_PORT.get(options.board, '')
542 if port:
543 servo = ['--servo', '%d' % port]
544
545 if options.flash:
546 flash = ['-F', 'spi']
547
548 if options.verbose:
549 verbose_arg = ['-v', '%s' % options.verbose]
550
551 if options.secure:
552 secure += ['--bootsecure', '--bootcmd', 'vboot_twostop']
553
554 if not options.verified:
555 # Make a small image, without GBB, etc.
556 secure.append('-s')
557
558 if options.kernel:
559 kernel = ['--kernel', '##/build/%s/boot/vmlinux.uimg' % options.board]
560
561 if not options.console:
562 silent = ['--add-config-int', 'silent-console', '1']
563
564 if not options.run:
565 run = ['--bootcmd', 'none']
566
567 if arch != 'sandbox' and not in_chroot and servo:
568 if dest == 'usb':
569 cros_build_lib.Warning('Image cannot be written to board')
570 dest = ''
571 servo = []
572 elif dest == 'em100':
573 cros_build_lib.Warning('Please reset the board manually to boot firmware')
574 servo = []
575
576 if not servo:
577 cros_build_lib.Warning('(sadly dut-control does not work outside chroot)')
578
579 if dest:
580 dest = ['-w', dest]
581 else:
582 dest = []
583
584 soc = SOCS.get(board)
585 if not soc:
586 soc = SOCS.get(uboard, '')
587 dt_name = DEFAULT_DTS.get(options.board, options.board)
588 dts_file = 'board/%s/dts/%s%s.dts' % (vendor, soc, dt_name)
589 Log('Device tree: %s' % dts_file)
590
591 if arch == 'sandbox':
592 uboot_fname = '%s/u-boot' % outdir
593 else:
594 uboot_fname = '%s/u-boot.bin' % outdir
595
596 cbf = ['%s/platform/dev/host/cros_bundle_firmware' % src_root,
597 '-b', options.board,
598 '-d', dts_file,
599 '-I', 'arch/%s/dts' % arch, '-I', 'cros/dts',
600 '-u', uboot_fname,
601 '-O', '%s/out' % outdir,
602 '-M', family]
603
604 for other in [bl1, bl2, bmpblk, defaults, dest, ecro, ecrw, flash, kernel,
605 run, seabios, secure, servo, silent, verbose_arg]:
606 if other:
607 cbf += other
608 if options.cbfargs:
609 for item in options.cbfargs:
610 cbf += item.split(' ')
611 os.environ['PYTHONPATH'] = ('%s/platform/dev/host/lib:%s/..' %
612 (src_root, src_root))
613 Log(' '.join(cbf))
614 result = cros_build_lib.RunCommand(cbf, **kwargs)
615 if result.returncode:
616 cros_build_lib.Die('cros_bundle_firmware failed')
617
618 if not dest or not result.returncode:
619 cros_build_lib.Info('Image is available at %s/out/image.bin' % outdir)
620 else:
621 if result.returncode:
622 cros_build_lib.Die('Failed to write image to board')
623 else:
624 cros_build_lib.Info('Image written to board with %s' %
625 ' '.join(dest + servo))
626
627
628def main(argv):
629 """Main function for script to build/write firmware.
630
631 Args:
632 argv: Program arguments.
633 """
634 options, args = ParseCmdline(argv)
635 base = SetupBuild(options)
636
637 with parallel.BackgroundTaskRunner(Dumper) as queue:
638 target = args[0] if args else 'all'
639 RunBuild(options, base, target, queue)
640
641 if options.write:
642 WriteFirmware(options)
643
644 if options.objdump:
645 Log('Writing diasssembly files')