summaryrefslogtreecommitdiff
path: root/kernel/source/load-app.cpp
blob: b4ffe039564286871a98bf4368ed62e0d8f12db0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <hilbert/kernel/load-app.hpp>
#include <hilbert/kernel/paging.hpp>

namespace hilbert::kernel {

  struct elf_header {
    uint8_t fixed[24];
    uint64_t entry_point;
    uint64_t program_header_offset;
    uint64_t section_header_offset;
    uint32_t flags;
    uint16_t elf_header_length;
    uint16_t program_header_pitch;
    uint16_t program_header_count;
  };

  struct program_header {
    uint32_t type;
    uint32_t flags;
    uint64_t foffset;
    uint64_t vaddr;
    uint64_t paddr;
    uint64_t flength;
    uint64_t vlength;
  };

  struct load_info {
    uint64_t vaddr;
    uint64_t vpages_start;
    uint64_t vpages_count;
    uint64_t foffset;
    uint64_t flength;
    bool writable;
    bool executable;
  };

  static uint8_t expected_fixed_header[24] = {
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00
  };

  load_app_result load_app(
    vfile::vfile &file, app_memory &into, uint64_t &entry_out) {

    if (file.dir_entry.type != storage::file_type::regular_file)
      return load_app_result::not_app;

    if (file.dir_entry.length < sizeof(elf_header))
      return load_app_result::not_app;

    elf_header eh;
    if (file.read_file(0, sizeof(elf_header), &eh)
        != storage::fs_result::success)
      return load_app_result::io_error;

    for (int i = 0; i < 24; ++i)
      if (eh.fixed[i] != expected_fixed_header[i])
        return load_app_result::not_app;

    if (eh.entry_point < 0x1000 || eh.entry_point >= 0x4000000000)
      return load_app_result::not_app;

    utility::vector<load_info> load_infos;

    for (int i = 0; i < eh.program_header_count; ++i) {

      uint64_t offset = eh.program_header_offset + eh.program_header_pitch * i;
      if (offset + sizeof(program_header) > file.dir_entry.length)
        return load_app_result::not_app;

      program_header ph;
      if (file.read_file(offset, sizeof(program_header), &ph)
          != storage::fs_result::success)
        return load_app_result::io_error;

      if (ph.type == 1) {

        uint64_t vpages_start = (ph.vaddr / 4096) * 4096;
        uint64_t vpages_end = ((ph.vaddr + ph.vlength - 1) / 4096 + 1) * 4096;

        if (vpages_start < 0x1000 || vpages_end >= 0x4000000000 ||
            ph.foffset + ph.flength > file.dir_entry.length)
          return load_app_result::not_app;

        load_infos.add_end((load_info){
          .vaddr = ph.vaddr,
          .vpages_start = vpages_start,
          .vpages_count = (vpages_end - vpages_start) / 4096,
          .foffset = ph.foffset,
          .flength = ph.flength,
          .writable = (ph.flags & 2) != 0,
          .executable = (ph.flags & 4) != 0 });

      }

    }

    for (unsigned i = 0; i < load_infos.count; ++i) {
      const auto &li = load_infos.buffer[i];

      for (uint64_t pi = 0; pi < li.vpages_count; ++pi) {

        uint64_t page_user_vaddr = li.vpages_start + pi * 4096;

        uint64_t page_kernel_vaddr;
        uint64_t page_paddr;
        paging::map_new_kernel_page(page_kernel_vaddr, page_paddr);

        uint8_t *ptr = (uint8_t *)page_kernel_vaddr;
        int bytes_left = 4096;
        int64_t foffset = page_user_vaddr - li.vaddr;

        if (foffset < 0) {
          int to_skip = -foffset;
          for (int i = 0; i < to_skip; ++i)
            ptr[i] = 0;
          ptr += to_skip;
          bytes_left -= to_skip;
          foffset = 0;
        }

        int64_t left_in_file = li.flength - foffset;
        if (left_in_file > 0) {
          int to_read = left_in_file < bytes_left ? left_in_file : bytes_left;
          if (file.read_file(li.foffset + foffset, to_read, ptr) !=
              storage::fs_result::success) {
            paging::unmap_kernel_page((uint64_t)page_kernel_vaddr);
            paging::free_pram_page(page_paddr);
            return load_app_result::io_error;
          }
          ptr += to_read;
          bytes_left -= to_read;
        }

        if (bytes_left > 0)
          for (int i = 0; i < bytes_left; ++i)
            ptr[i] = 0;

        paging::unmap_kernel_page((uint64_t)page_kernel_vaddr);
        into.map_page(
          page_user_vaddr, page_paddr, li.writable, li.executable, true);

      }

    }

    entry_out = eh.entry_point;
    return load_app_result::success;

  }

}