backported more fixes from 256boss
[bootcensus] / src / part.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2019  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include "part.h"
20 #include "boot.h"
21 #include "bootdev.h"
22 #include "ptype.h"
23
24 struct part_record {
25         uint8_t stat;
26         uint8_t first_head, first_cyl, first_sect;
27         uint8_t type;
28         uint8_t last_head, last_cyl, last_sect;
29         uint32_t first_lba;
30         uint32_t nsect_lba;
31 } __attribute__((packed));
32
33 static int read_sector(int dev, uint64_t sidx);
34 static const char *ptype_name(int type);
35
36 static unsigned char sectdata[512];
37
38 #define BOOTSIG_OFFS    510
39 #define PTABLE_OFFS             0x1be
40
41 #define BOOTSIG                 0xaa55
42
43 #define IS_MBR                  (sidx == 0)
44 #define IS_FIRST_EBR    (!IS_MBR && (first_ebr_offs == 0))
45
46 int read_partitions(int dev, struct partition *ptab, int ptabsz)
47 {
48         int i, num_rec, nparts = 0;
49         int num_bootrec = 0;
50         struct partition *part = ptab;
51         struct part_record *prec;
52         uint64_t sidx = 0;
53         uint64_t first_ebr_offs = 0;
54
55         if(ptabsz <= 0) {
56                 ptab = 0;
57         }
58
59         do {
60                 if(IS_FIRST_EBR) {
61                         first_ebr_offs = sidx;
62                 }
63
64                 if(read_sector(dev, sidx) == -1) {
65                         printf("failed to read sector %llu\n", (unsigned long long)sidx);
66                         return -1;
67                 }
68                 if(*(uint16_t*)(sectdata + BOOTSIG_OFFS) != BOOTSIG) {
69                         printf("invalid partitionm table, sector %llu has no magic\n", (unsigned long long)sidx);
70                         return -1;
71                 }
72                 prec = (struct part_record*)(sectdata + PTABLE_OFFS);
73
74                 /* MBR has 4 records, EBRs have 2 */
75                 num_rec = IS_MBR ? 4 : 2;
76
77                 for(i=0; i<num_rec; i++) {
78                         /* ignore empty partitions in the MBR, stop on empty partitions in an EBR */
79                         if(prec[i].type == 0) {
80                                 if(num_bootrec > 0) {
81                                         sidx = 0;
82                                         break;
83                                 }
84                                 continue;
85                         }
86
87                         /* ignore extended partitions and setup sector index to read the
88                          * next logical partition.
89                          */
90                         if(prec[i].type == PTYPE_EXT || prec[i].type == PTYPE_EXT_LBA) {
91                                 /* all EBR start fields are relative to the first EBR offset */
92                                 sidx = first_ebr_offs + prec[i].first_lba;
93                                 continue;
94                         }
95
96                         /* found a proper partition */
97                         nparts++;
98
99                         if(ptab) {
100                                 part->attr = prec[i].type;
101
102                                 if(prec[i].stat & 0x80) {
103                                         part->attr |= PART_ACT_BIT;
104                                 }
105                                 if(IS_MBR) {
106                                         part->attr |= PART_PRIM_BIT;
107                                 }
108                                 part->start_sect = prec[i].first_lba + first_ebr_offs;
109                                 part->size_sect = prec[i].nsect_lba;
110                                 part++;
111                         }
112                 }
113
114                 num_bootrec++;
115         } while(sidx > 0 && (!ptab || nparts < ptabsz));
116
117         return nparts;
118 }
119
120 void print_partition_table(struct partition *ptab, int npart)
121 {
122         int i;
123         struct partition *p = ptab;
124
125         printf("Found %d partitions\n", npart);
126         for(i=0; i<npart; i++) {
127                 printf("%d%c ", i, PART_IS_ACT(p->attr) ? '*' : ' ');
128                 printf("(%s) %-20s ", PART_IS_PRIM(p->attr) ? "pri" : "log", ptype_name(PART_TYPE(p->attr)));
129                 printf("start: %-10llu ", (unsigned long long)p->start_sect);
130                 printf("size: %-10llu\n", (unsigned long long)p->size_sect);
131                 p++;
132         }
133 }
134
135 static int read_sector(int dev, uint64_t sidx)
136 {
137         if(dev == -1 || dev == boot_drive_number) {
138                 if(bdev_read_sect(sidx, sectdata) == -1) {
139                         return -1;
140                 }
141                 return 0;
142         }
143
144         printf("BUG: reading partitions of drives other than the boot drive not implemented yet\n");
145         return -1;
146 }
147
148 static const char *ptype_name(int type)
149 {
150         int i;
151
152         for(i=0; i<PTYPES_SIZE; i++) {
153                 if(partypes[i].type == type) {
154                         return partypes[i].name;
155                 }
156         }
157         return "unknown";
158 }