1da0c48c4Sopenharmony_ci/* Linux kernel image support for libdwfl.
2da0c48c4Sopenharmony_ci   Copyright (C) 2009-2011 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   Copyright (C) 2022 Mark J. Wielaard <mark@klomp.org>
4da0c48c4Sopenharmony_ci   This file is part of elfutils.
5da0c48c4Sopenharmony_ci
6da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
7da0c48c4Sopenharmony_ci   it under the terms of either
8da0c48c4Sopenharmony_ci
9da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
10da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
11da0c48c4Sopenharmony_ci       your option) any later version
12da0c48c4Sopenharmony_ci
13da0c48c4Sopenharmony_ci   or
14da0c48c4Sopenharmony_ci
15da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
16da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
17da0c48c4Sopenharmony_ci       your option) any later version
18da0c48c4Sopenharmony_ci
19da0c48c4Sopenharmony_ci   or both in parallel, as here.
20da0c48c4Sopenharmony_ci
21da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
22da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
23da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24da0c48c4Sopenharmony_ci   General Public License for more details.
25da0c48c4Sopenharmony_ci
26da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
27da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
28da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
29da0c48c4Sopenharmony_ci
30da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
31da0c48c4Sopenharmony_ci# include <config.h>
32da0c48c4Sopenharmony_ci#endif
33da0c48c4Sopenharmony_ci
34da0c48c4Sopenharmony_ci#include "libdwflP.h"
35da0c48c4Sopenharmony_ci
36da0c48c4Sopenharmony_ci#if BYTE_ORDER == LITTLE_ENDIAN
37da0c48c4Sopenharmony_ci# define LE16(x)	(x)
38da0c48c4Sopenharmony_ci#else
39da0c48c4Sopenharmony_ci# define LE16(x)	bswap_16 (x)
40da0c48c4Sopenharmony_ci#endif
41da0c48c4Sopenharmony_ci
42da0c48c4Sopenharmony_ci/* See Documentation/x86/boot.txt in Linux kernel sources
43da0c48c4Sopenharmony_ci   for an explanation of these format details.  */
44da0c48c4Sopenharmony_ci
45da0c48c4Sopenharmony_ci#define MAGIC1			0xaa55
46da0c48c4Sopenharmony_ci#define MAGIC2			0x53726448 /* "HdrS" little-endian */
47da0c48c4Sopenharmony_ci#define MIN_VERSION		0x0208
48da0c48c4Sopenharmony_ci
49da0c48c4Sopenharmony_ci#define H_START			(H_SETUP_SECTS & -4)
50da0c48c4Sopenharmony_ci#define H_SETUP_SECTS		0x1f1
51da0c48c4Sopenharmony_ci#define H_MAGIC1		0x1fe
52da0c48c4Sopenharmony_ci#define H_MAGIC2		0x202
53da0c48c4Sopenharmony_ci#define H_VERSION		0x206
54da0c48c4Sopenharmony_ci#define H_PAYLOAD_OFFSET	0x248
55da0c48c4Sopenharmony_ci#define H_PAYLOAD_LENGTH	0x24c
56da0c48c4Sopenharmony_ci#define H_END			0x250
57da0c48c4Sopenharmony_ci#define H_READ_SIZE		(H_END - H_START)
58da0c48c4Sopenharmony_ci
59da0c48c4Sopenharmony_ciDwfl_Error
60da0c48c4Sopenharmony_ciinternal_function
61da0c48c4Sopenharmony_ci__libdw_image_header (int fd, off_t *start_offset,
62da0c48c4Sopenharmony_ci		      void *mapped, size_t mapped_size)
63da0c48c4Sopenharmony_ci{
64da0c48c4Sopenharmony_ci  if (likely (mapped_size > H_END))
65da0c48c4Sopenharmony_ci    {
66da0c48c4Sopenharmony_ci      const void *header = mapped;
67da0c48c4Sopenharmony_ci      char header_buffer[H_READ_SIZE];
68da0c48c4Sopenharmony_ci      if (header == NULL)
69da0c48c4Sopenharmony_ci	{
70da0c48c4Sopenharmony_ci	  ssize_t n = pread_retry (fd, header_buffer, H_READ_SIZE,
71da0c48c4Sopenharmony_ci				   *start_offset + H_START);
72da0c48c4Sopenharmony_ci	  if (n < 0)
73da0c48c4Sopenharmony_ci	    return DWFL_E_ERRNO;
74da0c48c4Sopenharmony_ci	  if (n < H_READ_SIZE)
75da0c48c4Sopenharmony_ci	    return DWFL_E_BADELF;
76da0c48c4Sopenharmony_ci
77da0c48c4Sopenharmony_ci	  header = header_buffer - H_START;
78da0c48c4Sopenharmony_ci	}
79da0c48c4Sopenharmony_ci
80da0c48c4Sopenharmony_ci      uint16_t magic1;
81da0c48c4Sopenharmony_ci      uint32_t magic2;
82da0c48c4Sopenharmony_ci      uint16_t version;
83da0c48c4Sopenharmony_ci      memcpy (&magic1, header + H_MAGIC1, sizeof (uint16_t));
84da0c48c4Sopenharmony_ci      memcpy (&magic2, header + H_MAGIC2, sizeof (uint32_t));
85da0c48c4Sopenharmony_ci      memcpy (&version, header + H_VERSION, sizeof (uint16_t));
86da0c48c4Sopenharmony_ci      if (magic1 == LE16 (MAGIC1) && magic2 == LE32 (MAGIC2)
87da0c48c4Sopenharmony_ci	  && LE16 (version) >= MIN_VERSION)
88da0c48c4Sopenharmony_ci	{
89da0c48c4Sopenharmony_ci	  /* The magic numbers match and the version field is sufficient.
90da0c48c4Sopenharmony_ci	     Extract the payload bounds.  */
91da0c48c4Sopenharmony_ci
92da0c48c4Sopenharmony_ci	  uint32_t offset;
93da0c48c4Sopenharmony_ci	  uint32_t length;
94da0c48c4Sopenharmony_ci	  uint8_t sects;
95da0c48c4Sopenharmony_ci	  memcpy (&offset, header + H_PAYLOAD_OFFSET, sizeof (uint32_t));
96da0c48c4Sopenharmony_ci	  memcpy (&length, header + H_PAYLOAD_LENGTH, sizeof (uint32_t));
97da0c48c4Sopenharmony_ci	  memcpy (&sects, header + H_SETUP_SECTS, sizeof (uint8_t));
98da0c48c4Sopenharmony_ci	  offset = LE32 (offset);
99da0c48c4Sopenharmony_ci	  length = LE32 (length);
100da0c48c4Sopenharmony_ci
101da0c48c4Sopenharmony_ci	  offset += ((sects ?: 4) + 1) * 512;
102da0c48c4Sopenharmony_ci
103da0c48c4Sopenharmony_ci	  if (offset > H_END && offset < mapped_size
104da0c48c4Sopenharmony_ci	      && mapped_size - offset >= length)
105da0c48c4Sopenharmony_ci	    {
106da0c48c4Sopenharmony_ci	      /* It looks kosher.  Use it!  */
107da0c48c4Sopenharmony_ci	      *start_offset += offset;
108da0c48c4Sopenharmony_ci	      return DWFL_E_NOERROR;
109da0c48c4Sopenharmony_ci	    }
110da0c48c4Sopenharmony_ci	}
111da0c48c4Sopenharmony_ci    }
112da0c48c4Sopenharmony_ci  return DWFL_E_BADELF;
113da0c48c4Sopenharmony_ci}
114